aboutsummaryrefslogtreecommitdiff
path: root/llvm/utils/TableGen/Basic/ARMTargetDefEmitter.cpp
blob: 3f284ee1b10325ff9580c40bce598ce7eeb3ddf9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
//===- ARMTargetDefEmitter.cpp - Generate data about ARM Architectures ----===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This tablegen backend exports information about CPUs, FPUs, architectures,
// and features into a common format that can be used by both TargetParser and
// the ARM and AArch64 backends.
//
//===----------------------------------------------------------------------===//

#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/TableGen/Error.h"
#include "llvm/TableGen/Record.h"
#include "llvm/TableGen/TableGenBackend.h"
#include <set>
#include <string>

using namespace llvm;

/// Collect the full set of implied features for a SubtargetFeature.
static void collectImpliedFeatures(std::set<const Record *> &SeenFeats,
                                   const Record *Rec) {
  assert(Rec->isSubClassOf("SubtargetFeature") &&
         "Rec is not a SubtargetFeature");

  SeenFeats.insert(Rec);
  for (const Record *Implied : Rec->getValueAsListOfDefs("Implies"))
    collectImpliedFeatures(SeenFeats, Implied);
}

static void checkFeatureTree(const Record *Root) {
  std::set<const Record *> SeenFeats;
  collectImpliedFeatures(SeenFeats, Root);

  // Check that each of the mandatory (implied) features which is an
  // ExtensionWithMArch is also enabled by default.
  auto DefaultExtsVec = Root->getValueAsListOfDefs("DefaultExts");
  std::set<const Record *> DefaultExts{DefaultExtsVec.begin(),
                                       DefaultExtsVec.end()};
  for (const Record *Feat : SeenFeats) {
    if (Feat->isSubClassOf("ExtensionWithMArch") && !DefaultExts.count(Feat))
      PrintFatalError(Root->getLoc(),
                      "ExtensionWithMArch " + Feat->getName() +
                          " is implied (mandatory) as a SubtargetFeature, but "
                          "is not present in DefaultExts");
  }
}

static void emitARMTargetDef(const RecordKeeper &RK, raw_ostream &OS) {
  OS << "// Autogenerated by ARMTargetDefEmitter.cpp\n\n";

  // Look through all SubtargetFeature defs with the given FieldName, and
  // collect the set of all Values that that FieldName is set to.
  auto GatherSubtargetFeatureFieldValues = [&RK](StringRef FieldName) {
    llvm::StringSet<> Set;
    for (const Record *Rec : RK.getAllDerivedDefinitions("SubtargetFeature")) {
      if (Rec->getValueAsString("FieldName") == FieldName) {
        Set.insert(Rec->getValueAsString("Value"));
      }
    }
    return Set;
  };

  // Sort the extensions alphabetically, so they don't appear in tablegen order.
  std::vector<const Record *> SortedExtensions =
      RK.getAllDerivedDefinitions("Extension");
  auto Alphabetical = [](const Record *A, const Record *B) -> bool {
    const auto NameA = A->getValueAsString("Name");
    const auto NameB = B->getValueAsString("Name");
    return NameA.compare(NameB) < 0; // A lexographically less than B
  };
  sort(SortedExtensions, Alphabetical);

  // Cache Extension records for quick lookup.
  DenseMap<StringRef, const Record *> ExtensionMap;
  for (const Record *Rec : SortedExtensions) {
    auto Name = Rec->getValueAsString("UserVisibleName");
    if (Name.empty())
      Name = Rec->getValueAsString("Name");
    ExtensionMap[Name] = Rec;
  }

  // The ARMProcFamilyEnum values are initialised by SubtargetFeature defs
  // which set the ARMProcFamily field. We can generate the enum from these defs
  // which look like this:
  //
  // def ProcA5      : SubtargetFeature<"a5", "ARMProcFamily", "CortexA5",
  //                                    "Cortex-A5 ARM processors", []>;
  OS << "#ifndef ARM_PROCESSOR_FAMILY\n"
     << "#define ARM_PROCESSOR_FAMILY(ENUM)\n"
     << "#endif\n\n";
  const StringSet<> ARMProcFamilyVals =
      GatherSubtargetFeatureFieldValues("ARMProcFamily");
  for (const StringRef &Family : ARMProcFamilyVals.keys())
    OS << "ARM_PROCESSOR_FAMILY(" << Family << ")\n";
  OS << "\n#undef ARM_PROCESSOR_FAMILY\n\n";

  OS << "#ifndef ARM_ARCHITECTURE\n"
     << "#define ARM_ARCHITECTURE(ENUM)\n"
     << "#endif\n\n";
  // This should correspond to instances of the Architecture tablegen class.
  const StringSet<> ARMArchVals = GatherSubtargetFeatureFieldValues("ARMArch");
  for (const StringRef &Arch : ARMArchVals.keys())
    OS << "ARM_ARCHITECTURE(" << Arch << ")\n";
  OS << "\n#undef ARM_ARCHITECTURE\n\n";

  // Currently only AArch64 (not ARM) is handled beyond this point.
  if (!RK.getClass("Architecture64"))
    return;

  // Emit the ArchExtKind enum
  OS << "#ifdef EMIT_ARCHEXTKIND_ENUM\n"
     << "enum ArchExtKind : unsigned {\n";
  for (const Record *Rec : SortedExtensions) {
    auto AEK = Rec->getValueAsString("ArchExtKindSpelling").upper();
    OS << "  " << AEK << ",\n";
  }
  OS << "  AEK_NUM_EXTENSIONS\n"
     << "};\n"
     << "#undef EMIT_ARCHEXTKIND_ENUM\n"
     << "#endif // EMIT_ARCHEXTKIND_ENUM\n";

  // Emit information for each defined Extension; used to build ArmExtKind.
  OS << "#ifdef EMIT_EXTENSIONS\n"
     << "inline constexpr ExtensionInfo Extensions[] = {\n";
  for (const Record *Rec : SortedExtensions) {
    auto AEK = Rec->getValueAsString("ArchExtKindSpelling").upper();
    OS << "  ";
    OS << "{\"" << Rec->getValueAsString("UserVisibleName") << "\"";
    if (auto Alias = Rec->getValueAsString("UserVisibleAlias"); Alias.empty())
      OS << ", {}";
    else
      OS << ", \"" << Alias << "\"";
    OS << ", AArch64::" << AEK;
    OS << ", \"" << Rec->getValueAsString("ArchFeatureName") << "\"";
    OS << ", \"" << Rec->getValueAsString("Desc") << "\"";
    OS << ", \"+" << Rec->getValueAsString("Name") << "\""; // posfeature
    OS << ", \"-" << Rec->getValueAsString("Name") << "\""; // negfeature
    OS << "},\n";
  };
  OS << "};\n"
     << "#undef EMIT_EXTENSIONS\n"
     << "#endif // EMIT_EXTENSIONS\n"
     << "\n";

  // Emit FMV information
  auto FMVExts = RK.getAllDerivedDefinitionsIfDefined("FMVExtension");
  OS << "#ifdef EMIT_FMV_INFO\n"
     << "const std::vector<llvm::AArch64::FMVInfo>& "
        "llvm::AArch64::getFMVInfo() {\n"
     << "  static std::vector<FMVInfo> I;\n"
     << "  if(I.size()) return I;\n"
     << "  I.reserve(" << FMVExts.size() << ");\n";
  for (const Record *Rec : FMVExts) {
    OS << "  I.emplace_back(";
    OS << "\"" << Rec->getValueAsString("Name") << "\"";
    OS << ", " << Rec->getValueAsString("FeatureBit");
    OS << ", " << Rec->getValueAsString("PriorityBit");
    auto FeatName = Rec->getValueAsString("BackendFeature");
    const Record *FeatRec = ExtensionMap[FeatName];
    if (FeatRec)
      OS << ", " << FeatRec->getValueAsString("ArchExtKindSpelling").upper();
    else
      OS << ", std::nullopt";
    OS << ");\n";
  };
  OS << "  return I;\n"
     << "}\n"
     << "#undef EMIT_FMV_INFO\n"
     << "#endif // EMIT_FMV_INFO\n"
     << "\n";

  // Emit extension dependencies
  OS << "#ifdef EMIT_EXTENSION_DEPENDENCIES\n"
     << "inline constexpr ExtensionDependency ExtensionDependencies[] = {\n";
  for (const Record *Rec : SortedExtensions) {
    auto LaterAEK = Rec->getValueAsString("ArchExtKindSpelling").upper();
    for (const Record *I : Rec->getValueAsListOfDefs("Implies"))
      if (auto EarlierAEK = I->getValueAsOptionalString("ArchExtKindSpelling"))
        OS << "  {" << EarlierAEK->upper() << ", " << LaterAEK << "},\n";
  }
  // FIXME: Tablegen has the Subtarget Feature FeatureRCPC_IMMO which is implied
  // by FeatureRCPC3 and in turn implies FeatureRCPC. The proper fix is to make
  // FeatureRCPC_IMMO an Extension but that will expose it to the command line.
  OS << "  {AEK_RCPC, AEK_RCPC3},\n";
  OS << "};\n"
     << "#undef EMIT_EXTENSION_DEPENDENCIES\n"
     << "#endif // EMIT_EXTENSION_DEPENDENCIES\n"
     << "\n";

  // Emit architecture information
  OS << "#ifdef EMIT_ARCHITECTURES\n";

  // Return the C++ name of the of an ArchInfo object
  auto ArchInfoName = [](int Major, int Minor,
                         StringRef Profile) -> std::string {
    return Minor == 0 ? "ARMV" + std::to_string(Major) + Profile.upper()
                      : "ARMV" + std::to_string(Major) + "_" +
                            std::to_string(Minor) + Profile.upper();
  };

  auto Architectures = RK.getAllDerivedDefinitionsIfDefined("Architecture64");
  std::vector<std::string> CppSpellings;
  for (const Record *Rec : Architectures) {
    const int Major = Rec->getValueAsInt("Major");
    const int Minor = Rec->getValueAsInt("Minor");
    const std::string ProfileLower = Rec->getValueAsString("Profile").str();
    const std::string ProfileUpper = Rec->getValueAsString("Profile").upper();

    if (ProfileLower != "a" && ProfileLower != "r")
      PrintFatalError(Rec->getLoc(),
                      "error: Profile must be one of 'a' or 'r', got '" +
                          ProfileLower + "'");

    // Name of the object in C++
    const std::string CppSpelling = ArchInfoName(Major, Minor, ProfileUpper);
    OS << "inline constexpr ArchInfo " << CppSpelling << " = {\n";
    CppSpellings.push_back(std::move(CppSpelling));

    OS << llvm::format("  VersionTuple{%d, %d},\n", Major, Minor);
    OS << llvm::format("  %sProfile,\n", ProfileUpper.c_str());

    // Name as spelled for -march.
    if (Minor == 0)
      OS << llvm::format("  \"armv%d-%s\",\n", Major, ProfileLower.c_str());
    else
      OS << llvm::format("  \"armv%d.%d-%s\",\n", Major, Minor,
                         ProfileLower.c_str());

    // SubtargetFeature::Name, used for -target-feature. Here the "+" is added.
    const auto TargetFeatureName = Rec->getValueAsString("Name");
    OS << "  \"+" << TargetFeatureName << "\",\n";

    // Construct the list of default extensions
    OS << "  (AArch64::ExtensionBitset({";
    for (auto *E : Rec->getValueAsListOfDefs("DefaultExts")) {
      OS << "AArch64::" << E->getValueAsString("ArchExtKindSpelling").upper()
         << ", ";
    }
    OS << "}))\n";

    OS << "};\n";
  }

  OS << "\n"
     << "/// The set of all architectures\n"
     << "static constexpr std::array<const ArchInfo *, " << CppSpellings.size()
     << "> ArchInfos = {\n";
  for (StringRef CppSpelling : CppSpellings)
    OS << "  &" << CppSpelling << ",\n";
  OS << "};\n";

  OS << "#undef EMIT_ARCHITECTURES\n"
     << "#endif // EMIT_ARCHITECTURES\n"
     << "\n";

  // Emit CPU Aliases
  OS << "#ifdef EMIT_CPU_ALIAS\n"
     << "inline constexpr Alias CpuAliases[] = {\n";

  llvm::StringSet<> Processors;
  for (const Record *Rec : RK.getAllDerivedDefinitions("ProcessorModel"))
    Processors.insert(Rec->getValueAsString("Name"));

  llvm::StringSet<> Aliases;
  for (const Record *Rec : RK.getAllDerivedDefinitions("ProcessorAlias")) {
    auto Name = Rec->getValueAsString("Name");
    auto Alias = Rec->getValueAsString("Alias");
    if (!Processors.contains(Alias))
      PrintFatalError(
          Rec, "Alias '" + Name + "' references a non-existent ProcessorModel '" + Alias + "'");
    if (Processors.contains(Name))
      PrintFatalError(
          Rec, "Alias '" + Name + "' duplicates an existing ProcessorModel");
    if (!Aliases.insert(Name).second)
      PrintFatalError(
          Rec, "Alias '" + Name + "' duplicates an existing ProcessorAlias");

    OS << llvm::formatv(R"(  { "{0}", "{1}" },)", Name, Alias) << '\n';
  }

  OS << "};\n"
     << "#undef EMIT_CPU_ALIAS\n"
     << "#endif // EMIT_CPU_ALIAS\n"
     << "\n";

  // Emit CPU information
  OS << "#ifdef EMIT_CPU_INFO\n"
     << "inline constexpr CpuInfo CpuInfos[] = {\n";

  for (const Record *Rec : RK.getAllDerivedDefinitions("ProcessorModel")) {
    auto Name = Rec->getValueAsString("Name");
    auto Features = Rec->getValueAsListOfDefs("Features");

    // "apple-latest" is backend-only, should not be accepted by TargetParser.
    if (Name == "apple-latest")
      continue;

    const Record *Arch;
    if (Name == "generic") {
      // "generic" is an exception. It does not have an architecture, and there
      // are tests that depend on e.g. -mattr=-v8.4a meaning HasV8_0aOps==false.
      // However, in TargetParser CPUInfo, it is written as 8.0-A.
      Arch = RK.getDef("HasV8_0aOps");
    } else {
      // Search for an Architecture64 in the list of features.
      auto IsArch = [](const Record *F) {
        return F->isSubClassOf("Architecture64");
      };
      auto ArchIter = llvm::find_if(Features, IsArch);
      if (ArchIter == Features.end())
        PrintFatalError(Rec, "Features must include an Architecture64.");
      Arch = *ArchIter;

      // Check there is only one Architecture in the list.
      if (llvm::count_if(Features, IsArch) > 1)
        PrintFatalError(Rec, "Features has multiple Architecture64 entries");
    }

    auto Major = Arch->getValueAsInt("Major");
    auto Minor = Arch->getValueAsInt("Minor");
    auto Profile = Arch->getValueAsString("Profile");
    auto ArchInfo = ArchInfoName(Major, Minor, Profile);

    checkFeatureTree(Arch);

    OS << "  {\n"
       << "    \"" << Name << "\",\n"
       << "    " << ArchInfo << ",\n"
       << "    AArch64::ExtensionBitset({\n";

    // Keep track of extensions we have seen
    StringSet<> SeenExts;
    for (const Record *E : Rec->getValueAsListOfDefs("Features"))
      // Only process subclasses of Extension
      if (E->isSubClassOf("Extension")) {
        const auto AEK = E->getValueAsString("ArchExtKindSpelling").upper();
        if (!SeenExts.insert(AEK).second)
          PrintFatalError(Rec, "feature already added: " + E->getName());
        OS << "      AArch64::" << AEK << ",\n";
      }
    OS << "    })\n"
       << "  },\n";
  }
  OS << "};\n";

  OS << "#undef EMIT_CPU_INFO\n"
     << "#endif // EMIT_CPU_INFO\n"
     << "\n";
}

static TableGen::Emitter::Opt
    X("gen-arm-target-def", emitARMTargetDef,
      "Generate the ARM or AArch64 Architecture information header.");