| 1 | //===- ARMTargetDefEmitter.cpp - Generate data about ARM Architectures ----===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // This tablegen backend exports information about CPUs, FPUs, architectures, |
| 10 | // and features into a common format that can be used by both TargetParser and |
| 11 | // the ARM and AArch64 backends. |
| 12 | // |
| 13 | //===----------------------------------------------------------------------===// |
| 14 | |
| 15 | #include "llvm/ADT/DenseMap.h" |
| 16 | #include "llvm/ADT/StringSet.h" |
| 17 | #include "llvm/Support/Format.h" |
| 18 | #include "llvm/Support/FormatVariadic.h" |
| 19 | #include "llvm/TableGen/Error.h" |
| 20 | #include "llvm/TableGen/Record.h" |
| 21 | #include "llvm/TableGen/StringToOffsetTable.h" |
| 22 | #include "llvm/TableGen/TableGenBackend.h" |
| 23 | #include <set> |
| 24 | #include <string> |
| 25 | |
| 26 | using namespace llvm; |
| 27 | |
| 28 | /// Collect the full set of implied features for a SubtargetFeature. |
| 29 | static void collectImpliedFeatures(std::set<const Record *> &SeenFeats, |
| 30 | const Record *Rec) { |
| 31 | assert(Rec->isSubClassOf("SubtargetFeature" ) && |
| 32 | "Rec is not a SubtargetFeature" ); |
| 33 | |
| 34 | SeenFeats.insert(x: Rec); |
| 35 | for (const Record *Implied : Rec->getValueAsListOfDefs(FieldName: "Implies" )) |
| 36 | collectImpliedFeatures(SeenFeats, Rec: Implied); |
| 37 | } |
| 38 | |
| 39 | static void checkFeatureTree(const Record *Root) { |
| 40 | std::set<const Record *> SeenFeats; |
| 41 | collectImpliedFeatures(SeenFeats, Rec: Root); |
| 42 | |
| 43 | // Check that each of the mandatory (implied) features which is an |
| 44 | // ExtensionWithMArch is also enabled by default. |
| 45 | auto DefaultExtsVec = Root->getValueAsListOfDefs(FieldName: "DefaultExts" ); |
| 46 | std::set<const Record *> DefaultExts{DefaultExtsVec.begin(), |
| 47 | DefaultExtsVec.end()}; |
| 48 | for (const Record *Feat : SeenFeats) { |
| 49 | if (Feat->isSubClassOf(Name: "ExtensionWithMArch" ) && !DefaultExts.count(x: Feat)) |
| 50 | PrintFatalError(ErrorLoc: Root->getLoc(), |
| 51 | Msg: "ExtensionWithMArch " + Feat->getName() + |
| 52 | " is implied (mandatory) as a SubtargetFeature, but " |
| 53 | "is not present in DefaultExts" ); |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | static void emitARMTargetDef(const RecordKeeper &RK, raw_ostream &OS) { |
| 58 | OS << "// Autogenerated by ARMTargetDefEmitter.cpp\n\n" ; |
| 59 | |
| 60 | // Look through all SubtargetFeature defs with the given FieldName, and |
| 61 | // collect the set of all Values that that FieldName is set to. |
| 62 | auto GatherSubtargetFeatureFieldValues = [&RK](StringRef FieldName) { |
| 63 | llvm::StringSet<> Set; |
| 64 | for (const Record *Rec : RK.getAllDerivedDefinitions(ClassName: "SubtargetFeature" )) { |
| 65 | if (Rec->getValueAsString(FieldName: "FieldName" ) == FieldName) { |
| 66 | Set.insert(key: Rec->getValueAsString(FieldName: "Value" )); |
| 67 | } |
| 68 | } |
| 69 | return Set; |
| 70 | }; |
| 71 | |
| 72 | // Sort the extensions alphabetically, so they don't appear in tablegen order. |
| 73 | std::vector<const Record *> SortedExtensions = |
| 74 | RK.getAllDerivedDefinitions(ClassName: "Extension" ); |
| 75 | auto Alphabetical = [](const Record *A, const Record *B) -> bool { |
| 76 | const auto NameA = A->getValueAsString(FieldName: "Name" ); |
| 77 | const auto NameB = B->getValueAsString(FieldName: "Name" ); |
| 78 | return NameA.compare(RHS: NameB) < 0; // A lexographically less than B |
| 79 | }; |
| 80 | sort(C&: SortedExtensions, Comp: Alphabetical); |
| 81 | |
| 82 | // Cache Extension records for quick lookup. |
| 83 | DenseMap<StringRef, const Record *> ExtensionMap; |
| 84 | for (const Record *Rec : SortedExtensions) { |
| 85 | auto Name = Rec->getValueAsString(FieldName: "UserVisibleName" ); |
| 86 | if (Name.empty()) |
| 87 | Name = Rec->getValueAsString(FieldName: "Name" ); |
| 88 | ExtensionMap[Name] = Rec; |
| 89 | } |
| 90 | |
| 91 | // The ARMProcFamilyEnum values are initialised by SubtargetFeature defs |
| 92 | // which set the ARMProcFamily field. We can generate the enum from these defs |
| 93 | // which look like this: |
| 94 | // |
| 95 | // def ProcA5 : SubtargetFeature<"a5", "ARMProcFamily", "CortexA5", |
| 96 | // "Cortex-A5 ARM processors", []>; |
| 97 | OS << "#ifndef ARM_PROCESSOR_FAMILY\n" |
| 98 | << "#define ARM_PROCESSOR_FAMILY(ENUM)\n" |
| 99 | << "#endif\n\n" ; |
| 100 | const StringSet<> ARMProcFamilyVals = |
| 101 | GatherSubtargetFeatureFieldValues("ARMProcFamily" ); |
| 102 | for (const StringRef &Family : ARMProcFamilyVals.keys()) |
| 103 | OS << "ARM_PROCESSOR_FAMILY(" << Family << ")\n" ; |
| 104 | OS << "\n#undef ARM_PROCESSOR_FAMILY\n\n" ; |
| 105 | |
| 106 | OS << "#ifndef ARM_ARCHITECTURE\n" |
| 107 | << "#define ARM_ARCHITECTURE(ENUM)\n" |
| 108 | << "#endif\n\n" ; |
| 109 | // This should correspond to instances of the Architecture tablegen class. |
| 110 | const StringSet<> ARMArchVals = GatherSubtargetFeatureFieldValues("ARMArch" ); |
| 111 | for (const StringRef &Arch : ARMArchVals.keys()) |
| 112 | OS << "ARM_ARCHITECTURE(" << Arch << ")\n" ; |
| 113 | OS << "\n#undef ARM_ARCHITECTURE\n\n" ; |
| 114 | |
| 115 | // Currently only AArch64 (not ARM) is handled beyond this point. |
| 116 | if (!RK.getClass(Name: "Architecture64" )) |
| 117 | return; |
| 118 | |
| 119 | StringToOffsetTable StrTab; |
| 120 | |
| 121 | // Emit the ArchExtKind enum |
| 122 | OS << "#ifdef EMIT_ARCHEXTKIND_ENUM\n" |
| 123 | << "enum ArchExtKind : unsigned {\n" ; |
| 124 | for (const Record *Rec : SortedExtensions) { |
| 125 | auto AEK = Rec->getValueAsString(FieldName: "ArchExtKindSpelling" ).upper(); |
| 126 | OS << " " << AEK << ",\n" ; |
| 127 | } |
| 128 | OS << " AEK_NUM_EXTENSIONS\n" |
| 129 | << "};\n" |
| 130 | << "#undef EMIT_ARCHEXTKIND_ENUM\n" |
| 131 | << "#endif // EMIT_ARCHEXTKIND_ENUM\n" ; |
| 132 | |
| 133 | // Emit information for each defined Extension; used to build ArmExtKind. |
| 134 | OS << "#ifdef EMIT_EXTENSIONS\n" |
| 135 | << "inline constexpr ExtensionInfo Extensions[] = {\n" ; |
| 136 | for (const Record *Rec : SortedExtensions) { |
| 137 | auto AEK = Rec->getValueAsString(FieldName: "ArchExtKindSpelling" ).upper(); |
| 138 | OS << " " ; |
| 139 | OS << "{" |
| 140 | << StrTab.GetOrAddStringOffset(Str: Rec->getValueAsString(FieldName: "UserVisibleName" )); |
| 141 | // Empty string implies no alias. |
| 142 | OS << ", " |
| 143 | << StrTab.GetOrAddStringOffset( |
| 144 | Str: Rec->getValueAsString(FieldName: "UserVisibleAlias" )); |
| 145 | OS << ", AArch64::" << AEK; |
| 146 | OS << ", " |
| 147 | << StrTab.GetOrAddStringOffset(Str: Rec->getValueAsString(FieldName: "ArchFeatureName" )); |
| 148 | OS << ", " << StrTab.GetOrAddStringOffset(Str: Rec->getValueAsString(FieldName: "Desc" )); |
| 149 | OS << ", " |
| 150 | << StrTab.GetOrAddStringOffset( |
| 151 | Str: std::string("+" ) + |
| 152 | Rec->getValueAsString(FieldName: "Name" ).str()); // posfeature |
| 153 | OS << ", " |
| 154 | << StrTab.GetOrAddStringOffset( |
| 155 | Str: std::string("-" ) + |
| 156 | Rec->getValueAsString(FieldName: "Name" ).str()); // negfeature |
| 157 | OS << "},\n" ; |
| 158 | }; |
| 159 | OS << "};\n" ; |
| 160 | OS << "#undef EMIT_EXTENSIONS\n" |
| 161 | << "#endif // EMIT_EXTENSIONS\n" |
| 162 | << "\n" ; |
| 163 | |
| 164 | // Emit FMV information |
| 165 | auto FMVExts = RK.getAllDerivedDefinitionsIfDefined(ClassName: "FMVExtension" ); |
| 166 | OS << "#ifdef EMIT_FMV_INFO\n" |
| 167 | << "const std::vector<llvm::AArch64::FMVInfo>& " |
| 168 | "llvm::AArch64::getFMVInfo() {\n" |
| 169 | << " static std::vector<FMVInfo> I;\n" |
| 170 | << " if(I.size()) return I;\n" |
| 171 | << " I.reserve(" << FMVExts.size() << ");\n" ; |
| 172 | for (const Record *Rec : FMVExts) { |
| 173 | auto FeatName = Rec->getValueAsString(FieldName: "BackendFeature" ); |
| 174 | const Record *FeatRec = ExtensionMap[FeatName]; |
| 175 | OS << " I.emplace_back(" ; |
| 176 | OS << "\"" << Rec->getValueAsString(FieldName: "Name" ) << "\"" ; |
| 177 | if (FeatRec) |
| 178 | OS << ", " << Rec->getValueAsString(FieldName: "FeatureBit" ); |
| 179 | else |
| 180 | OS << ", std::nullopt" ; |
| 181 | OS << ", " << Rec->getValueAsString(FieldName: "PriorityBit" ); |
| 182 | if (FeatRec) |
| 183 | OS << ", " << FeatRec->getValueAsString(FieldName: "ArchExtKindSpelling" ).upper(); |
| 184 | else |
| 185 | OS << ", std::nullopt" ; |
| 186 | OS << ");\n" ; |
| 187 | }; |
| 188 | OS << " return I;\n" |
| 189 | << "}\n" |
| 190 | << "#undef EMIT_FMV_INFO\n" |
| 191 | << "#endif // EMIT_FMV_INFO\n" |
| 192 | << "\n" ; |
| 193 | |
| 194 | // Emit extension dependencies |
| 195 | OS << "#ifdef EMIT_EXTENSION_DEPENDENCIES\n" |
| 196 | << "inline constexpr ExtensionDependency ExtensionDependencies[] = {\n" ; |
| 197 | for (const Record *Rec : SortedExtensions) { |
| 198 | auto LaterAEK = Rec->getValueAsString(FieldName: "ArchExtKindSpelling" ).upper(); |
| 199 | for (const Record *I : Rec->getValueAsListOfDefs(FieldName: "Implies" )) |
| 200 | if (auto EarlierAEK = I->getValueAsOptionalString(FieldName: "ArchExtKindSpelling" )) |
| 201 | OS << " {" << EarlierAEK->upper() << ", " << LaterAEK << "},\n" ; |
| 202 | } |
| 203 | // FIXME: Tablegen has the Subtarget Feature FeatureRCPC_IMMO which is implied |
| 204 | // by FeatureRCPC3 and in turn implies FeatureRCPC. The proper fix is to make |
| 205 | // FeatureRCPC_IMMO an Extension but that will expose it to the command line. |
| 206 | OS << " {AEK_RCPC, AEK_RCPC3},\n" ; |
| 207 | OS << "};\n" |
| 208 | << "#undef EMIT_EXTENSION_DEPENDENCIES\n" |
| 209 | << "#endif // EMIT_EXTENSION_DEPENDENCIES\n" |
| 210 | << "\n" ; |
| 211 | |
| 212 | // Emit architecture information |
| 213 | OS << "#ifdef EMIT_ARCHITECTURES\n" ; |
| 214 | |
| 215 | // Return the C++ name of the of an ArchInfo object |
| 216 | auto ArchInfoName = [](int Major, int Minor, |
| 217 | StringRef Profile) -> std::string { |
| 218 | return Minor == 0 ? "ARMV" + std::to_string(val: Major) + Profile.upper() |
| 219 | : "ARMV" + std::to_string(val: Major) + "_" + |
| 220 | std::to_string(val: Minor) + Profile.upper(); |
| 221 | }; |
| 222 | |
| 223 | auto Architectures = RK.getAllDerivedDefinitionsIfDefined(ClassName: "Architecture64" ); |
| 224 | OS << "\n" |
| 225 | << "/// The set of all architectures\n" |
| 226 | << "inline constexpr std::array<ArchInfo, " << Architectures.size() |
| 227 | << "> ArchInfos = {{\n" ; |
| 228 | std::vector<std::string> CppSpellings; |
| 229 | for (const Record *Rec : Architectures) { |
| 230 | const int Major = Rec->getValueAsInt(FieldName: "Major" ); |
| 231 | const int Minor = Rec->getValueAsInt(FieldName: "Minor" ); |
| 232 | const std::string ProfileLower = Rec->getValueAsString(FieldName: "Profile" ).str(); |
| 233 | const std::string ProfileUpper = Rec->getValueAsString(FieldName: "Profile" ).upper(); |
| 234 | |
| 235 | if (ProfileLower != "a" && ProfileLower != "r" ) |
| 236 | PrintFatalError(ErrorLoc: Rec->getLoc(), |
| 237 | Msg: "error: Profile must be one of 'a' or 'r', got '" + |
| 238 | ProfileLower + "'" ); |
| 239 | |
| 240 | // Name of the object in C++ |
| 241 | std::string CppSpelling = ArchInfoName(Major, Minor, ProfileUpper); |
| 242 | OS << " {\n" ; |
| 243 | CppSpellings.push_back(x: std::move(CppSpelling)); |
| 244 | |
| 245 | OS << llvm::format(Fmt: " VersionTuple{%d, %d},\n" , Vals: Major, Vals: Minor); |
| 246 | OS << llvm::format(Fmt: " %sProfile,\n" , Vals: ProfileUpper.c_str()); |
| 247 | |
| 248 | // Name as spelled for -march. |
| 249 | std::string ArchStr; |
| 250 | llvm::raw_string_ostream AS(ArchStr); |
| 251 | if (Minor == 0) |
| 252 | AS << llvm::format(Fmt: "armv%d-%s" , Vals: Major, Vals: ProfileLower.c_str()); |
| 253 | else |
| 254 | AS << llvm::format(Fmt: "armv%d.%d-%s" , Vals: Major, Vals: Minor, Vals: ProfileLower.c_str()); |
| 255 | OS << " " << StrTab.GetOrAddStringOffset(Str: ArchStr) << ",\n" ; |
| 256 | |
| 257 | // SubtargetFeature::Name, used for -target-feature. Here the "+" is added. |
| 258 | std::string TargetFeatureName = |
| 259 | (Twine("+" ) + Rec->getValueAsString(FieldName: "Name" )).str(); |
| 260 | OS << " " << StrTab.GetOrAddStringOffset(Str: TargetFeatureName) << ",\n" ; |
| 261 | |
| 262 | // Construct the list of default extensions |
| 263 | OS << " (AArch64::ExtensionBitset({" ; |
| 264 | for (auto *E : Rec->getValueAsListOfDefs(FieldName: "DefaultExts" )) { |
| 265 | OS << "AArch64::" << E->getValueAsString(FieldName: "ArchExtKindSpelling" ).upper() |
| 266 | << ", " ; |
| 267 | } |
| 268 | OS << "}))\n" ; |
| 269 | |
| 270 | OS << " },\n" ; |
| 271 | } |
| 272 | OS << "}};\n" ; |
| 273 | |
| 274 | for (auto [Idx, CppSpelling] : enumerate(First&: CppSpellings)) |
| 275 | OS << "static constexpr const ArchInfo &" << CppSpelling << " = ArchInfos[" |
| 276 | << Idx << "];\n" ; |
| 277 | |
| 278 | OS << "#undef EMIT_ARCHITECTURES\n" |
| 279 | << "#endif // EMIT_ARCHITECTURES\n" |
| 280 | << "\n" ; |
| 281 | |
| 282 | // Emit CPU Aliases |
| 283 | OS << "#ifdef EMIT_CPU_ALIAS\n" |
| 284 | << "inline constexpr Alias CpuAliases[] = {\n" ; |
| 285 | |
| 286 | llvm::StringSet<> Processors; |
| 287 | for (const Record *Rec : RK.getAllDerivedDefinitions(ClassName: "ProcessorModel" )) |
| 288 | Processors.insert(key: Rec->getValueAsString(FieldName: "Name" )); |
| 289 | |
| 290 | llvm::StringSet<> Aliases; |
| 291 | for (const Record *Rec : RK.getAllDerivedDefinitions(ClassName: "ProcessorAlias" )) { |
| 292 | auto Name = Rec->getValueAsString(FieldName: "Name" ); |
| 293 | auto Alias = Rec->getValueAsString(FieldName: "Alias" ); |
| 294 | if (!Processors.contains(key: Alias)) |
| 295 | PrintFatalError( |
| 296 | Rec, Msg: "Alias '" + Name + "' references a non-existent ProcessorModel '" + Alias + "'" ); |
| 297 | if (Processors.contains(key: Name)) |
| 298 | PrintFatalError( |
| 299 | Rec, Msg: "Alias '" + Name + "' duplicates an existing ProcessorModel" ); |
| 300 | if (!Aliases.insert(key: Name).second) |
| 301 | PrintFatalError( |
| 302 | Rec, Msg: "Alias '" + Name + "' duplicates an existing ProcessorAlias" ); |
| 303 | |
| 304 | OS << " {" << StrTab.GetOrAddStringOffset(Str: Name) << ", " |
| 305 | << StrTab.GetOrAddStringOffset(Str: Alias) << "},\n" ; |
| 306 | } |
| 307 | |
| 308 | OS << "};\n" |
| 309 | << "#undef EMIT_CPU_ALIAS\n" |
| 310 | << "#endif // EMIT_CPU_ALIAS\n" |
| 311 | << "\n" ; |
| 312 | |
| 313 | // Emit CPU information |
| 314 | OS << "#ifdef EMIT_CPU_INFO\n" |
| 315 | << "inline constexpr CpuInfo CpuInfos[] = {\n" ; |
| 316 | |
| 317 | for (const Record *Rec : RK.getAllDerivedDefinitions(ClassName: "ProcessorModel" )) { |
| 318 | auto Name = Rec->getValueAsString(FieldName: "Name" ); |
| 319 | auto Features = Rec->getValueAsListOfDefs(FieldName: "Features" ); |
| 320 | |
| 321 | // "apple-latest" is backend-only, should not be accepted by TargetParser. |
| 322 | if (Name == "apple-latest" ) |
| 323 | continue; |
| 324 | |
| 325 | const Record *Arch; |
| 326 | if (Name == "generic" ) { |
| 327 | // "generic" is an exception. It does not have an architecture, and there |
| 328 | // are tests that depend on e.g. -mattr=-v8.4a meaning HasV8_0aOps==false. |
| 329 | // However, in TargetParser CPUInfo, it is written as 8.0-A. |
| 330 | Arch = RK.getDef(Name: "HasV8_0aOps" ); |
| 331 | } else { |
| 332 | // Search for an Architecture64 in the list of features. |
| 333 | auto IsArch = [](const Record *F) { |
| 334 | return F->isSubClassOf(Name: "Architecture64" ); |
| 335 | }; |
| 336 | auto ArchIter = llvm::find_if(Range&: Features, P: IsArch); |
| 337 | if (ArchIter == Features.end()) |
| 338 | PrintFatalError(Rec, Msg: "Features must include an Architecture64." ); |
| 339 | Arch = *ArchIter; |
| 340 | |
| 341 | // Check there is only one Architecture in the list. |
| 342 | if (llvm::count_if(Range&: Features, P: IsArch) > 1) |
| 343 | PrintFatalError(Rec, Msg: "Features has multiple Architecture64 entries" ); |
| 344 | } |
| 345 | |
| 346 | auto Major = Arch->getValueAsInt(FieldName: "Major" ); |
| 347 | auto Minor = Arch->getValueAsInt(FieldName: "Minor" ); |
| 348 | auto Profile = Arch->getValueAsString(FieldName: "Profile" ); |
| 349 | auto ArchInfo = ArchInfoName(Major, Minor, Profile); |
| 350 | unsigned ArchIdx = |
| 351 | llvm::find(Range&: CppSpellings, Val: ArchInfo) - CppSpellings.begin(); |
| 352 | |
| 353 | checkFeatureTree(Root: Arch); |
| 354 | |
| 355 | OS << " {\n" |
| 356 | << " " << StrTab.GetOrAddStringOffset(Str: Name) << ",\n" |
| 357 | << " " << ArchIdx << " /* " << ArchInfo << " */,\n" |
| 358 | << " AArch64::ExtensionBitset({\n" ; |
| 359 | |
| 360 | // Keep track of extensions we have seen |
| 361 | StringSet<> SeenExts; |
| 362 | for (const Record *E : Rec->getValueAsListOfDefs(FieldName: "Features" )) |
| 363 | // Only process subclasses of Extension |
| 364 | if (E->isSubClassOf(Name: "Extension" )) { |
| 365 | const auto AEK = E->getValueAsString(FieldName: "ArchExtKindSpelling" ).upper(); |
| 366 | if (!SeenExts.insert(key: AEK).second) |
| 367 | PrintFatalError(Rec, Msg: "feature already added: " + E->getName()); |
| 368 | OS << " AArch64::" << AEK << ",\n" ; |
| 369 | } |
| 370 | OS << " })\n" |
| 371 | << " },\n" ; |
| 372 | } |
| 373 | OS << "};\n" ; |
| 374 | |
| 375 | OS << "#undef EMIT_CPU_INFO\n" |
| 376 | << "#endif // EMIT_CPU_INFO\n" |
| 377 | << "\n" ; |
| 378 | |
| 379 | // Emit string table. |
| 380 | OS << "#ifdef EMIT_STRTAB\n" ; |
| 381 | StrTab.EmitStringTableDef(OS, Name: "StrTab" ); |
| 382 | OS << "#undef EMIT_STRTAB\n" |
| 383 | << "#endif // EMIT_STRTAB\n" |
| 384 | << "\n" ; |
| 385 | } |
| 386 | |
| 387 | static TableGen::Emitter::Opt |
| 388 | X("gen-arm-target-def" , emitARMTargetDef, |
| 389 | "Generate the ARM or AArch64 Architecture information header." ); |
| 390 | |