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