diff --git a/llvm/include/llvm/MC/MCSubtargetInfo.h b/llvm/include/llvm/MC/MCSubtargetInfo.h index 2d1053b2113c0..e8de24cee5831 100644 --- a/llvm/include/llvm/MC/MCSubtargetInfo.h +++ b/llvm/include/llvm/MC/MCSubtargetInfo.h @@ -274,7 +274,7 @@ class LLVM_ABI MCSubtargetInfo { } /// Check whether the CPU string is valid. - virtual bool isCPUStringValid(StringRef CPU) const { + bool isCPUStringValid(StringRef CPU) const { auto Found = llvm::lower_bound(ProcDesc, CPU); return Found != ProcDesc.end() && StringRef(Found->key()) == CPU; } diff --git a/llvm/include/llvm/Target/Target.td b/llvm/include/llvm/Target/Target.td index cb97ab5d1bae4..c652af023c720 100644 --- a/llvm/include/llvm/Target/Target.td +++ b/llvm/include/llvm/Target/Target.td @@ -2136,6 +2136,13 @@ class ProcessorModel f, let SchedModel = m; } +// Defines an alternate name 'n' for the processor 'alias', resolving +// to the same subtarget features and scheduling model. +class ProcessorAlias { + string Name = n; + string Alias = alias; +} + //===----------------------------------------------------------------------===// // InstrMapping - This class is used to create mapping tables to relate // instructions with each other based on the values specified in RowFields, diff --git a/llvm/lib/Target/AArch64/AArch64Processors.td b/llvm/lib/Target/AArch64/AArch64Processors.td index e3ee97defb37d..e72d0b35fdd87 100644 --- a/llvm/lib/Target/AArch64/AArch64Processors.td +++ b/llvm/lib/Target/AArch64/AArch64Processors.td @@ -1276,12 +1276,6 @@ def ProcessorFeatures { list Generic = [FeatureFPARMv8, FeatureNEON, FeatureETE]; } -// Define an alternative name for a given Processor. -class ProcessorAlias { - string Name = n; - string Alias = alias; -} - // A note on scheduling models - we do not have exact models for each core and // attempt to use the "best equivalent" that we have for those that are // missing. -mcpu=generic uses a cortex-a510 model to get good scheduling diff --git a/llvm/test/TableGen/ProcessorAlias.td b/llvm/test/TableGen/ProcessorAlias.td new file mode 100644 index 0000000000000..c70c7dd58f8e4 --- /dev/null +++ b/llvm/test/TableGen/ProcessorAlias.td @@ -0,0 +1,24 @@ +// RUN: llvm-tblgen -gen-subtarget -I %p/../../include %s 2>&1 | FileCheck %s +// Verify that ProcessorAlias entries are emitted into the CPU subtype table +// alongside real processors, resolving to their canonical processor's +// features and scheduling model, and that the table stays sorted by key. + +include "llvm/Target/Target.td" + +def MyTarget : Target; + +def FeatureA : SubtargetFeature<"feature-a", "HasA", "true", "">; + +def ProcA : ProcessorModel<"cpu-a", NoSchedModel, [FeatureA]>; +def ProcB : ProcessorModel<"cpu-b", NoSchedModel, []>; + +// An alias resolves to an existing processor and is emitted as its own entry. +def : ProcessorAlias<"alias-of-a", "cpu-a">; + +// The subtype table has 3 entries (2 processors + 1 alias) sorted by key: +// alias-of-a, cpu-a, cpu-b. The alias carries cpu-a's feature mask (bit 0 set). +// CHECK: extern const llvm::SubtargetSubTypeKVStorage< 3, +// CHECK: { sizeof(SubtargetSubTypeKV) * 3 + {{[0-9]+}}, { { { 0x1ULL,{{.*}} }, {{.*}}, 0 }, +// CHECK-NEXT: { sizeof(SubtargetSubTypeKV) * 2 + {{[0-9]+}}, { { { 0x1ULL,{{.*}} }, {{.*}}, 0 }, +// CHECK-NEXT: { sizeof(SubtargetSubTypeKV) * 1 + {{[0-9]+}}, { { { 0x0ULL,{{.*}} }, {{.*}}, 0 }, +// CHECK: "\000alias-of-a\000cpu-a\000cpu-b\000" diff --git a/llvm/test/TableGen/ProcessorAliasErrors.td b/llvm/test/TableGen/ProcessorAliasErrors.td new file mode 100644 index 0000000000000..356c1419640a4 --- /dev/null +++ b/llvm/test/TableGen/ProcessorAliasErrors.td @@ -0,0 +1,34 @@ +// RUN: rm -rf %t && split-file %s %t +// RUN: not llvm-tblgen -gen-subtarget -I %p/../../include %t/non-existent.td 2>&1 \ +// RUN: | FileCheck %t/non-existent.td -DFILE=%t/non-existent.td --implicit-check-not="error:" +// RUN: not llvm-tblgen -gen-subtarget -I %p/../../include %t/dup-processor.td 2>&1 \ +// RUN: | FileCheck %t/dup-processor.td -DFILE=%t/dup-processor.td --implicit-check-not="error:" +// RUN: not llvm-tblgen -gen-subtarget -I %p/../../include %t/dup-alias.td 2>&1 \ +// RUN: | FileCheck %t/dup-alias.td -DFILE=%t/dup-alias.td --implicit-check-not="error:" + +// Verify the ProcessorAlias validation performed by the subtarget emitter. + +//--- non-existent.td +include "llvm/Target/Target.td" +def MyTarget : Target; +def ProcA : ProcessorModel<"cpu-a", NoSchedModel, []>; +// An alias must resolve to an existing processor. +// CHECK: [[FILE]]:[[#@LINE+1]]:1: error: Alias 'bad-alias' references a non-existent Processor 'cpu-missing' +def : ProcessorAlias<"bad-alias", "cpu-missing">; + +//--- dup-processor.td +include "llvm/Target/Target.td" +def MyTarget : Target; +def ProcA : ProcessorModel<"cpu-a", NoSchedModel, []>; +// An alias name must not collide with a real processor. +// CHECK: [[FILE]]:[[#@LINE+1]]:1: error: Alias 'cpu-a' duplicates an existing Processor +def : ProcessorAlias<"cpu-a", "cpu-a">; + +//--- dup-alias.td +include "llvm/Target/Target.td" +def MyTarget : Target; +def ProcA : ProcessorModel<"cpu-a", NoSchedModel, []>; +def DupA : ProcessorAlias<"dup", "cpu-a">; +// Two aliases must not share the same name. +// CHECK: [[FILE]]:[[#@LINE+1]]:5: error: Alias 'dup' duplicates an existing alias +def DupB : ProcessorAlias<"dup", "cpu-a">; diff --git a/llvm/utils/TableGen/SubtargetEmitter.cpp b/llvm/utils/TableGen/SubtargetEmitter.cpp index ae41feda46d65..ff7eaa1c55b4b 100644 --- a/llvm/utils/TableGen/SubtargetEmitter.cpp +++ b/llvm/utils/TableGen/SubtargetEmitter.cpp @@ -286,24 +286,44 @@ static void checkDuplicateCPUFeatures(StringRef CPUName, std::pair SubtargetEmitter::cpuKeyValues(raw_ostream &OS, const FeatureMapTy &FeatureMap) { - // Gather and sort processor information std::vector ProcessorList = Records.getAllDerivedDefinitions("Processor"); - llvm::sort(ProcessorList, LessRecordFieldName()); - // In the string table, include the aliases as well. + StringMap ProcessorMap; + for (const Record *Processor : ProcessorList) + ProcessorMap[Processor->getValueAsString("Name")] = Processor; + + // Maps each emitted CPU name (processor or alias) to the processor record it + // resolves to. Keying by name detects duplicates on insertion. + StringMap SubTypeEntries; + for (const Record *Processor : ProcessorList) + SubTypeEntries[Processor->getValueAsString("Name")] = Processor; + std::vector ProcessorAliasList = Records.getAllDerivedDefinitionsIfDefined("ProcessorAlias"); - SmallVector Names; - Names.reserve(ProcessorList.size() + ProcessorAliasList.size()); - for (const Record *Processor : ProcessorList) - Names.push_back(Processor->getValueAsString("Name")); - for (const Record *Rec : ProcessorAliasList) - Names.push_back(Rec->getValueAsString("Name")); - llvm::sort(Names); + for (const Record *Rec : ProcessorAliasList) { + StringRef Name = Rec->getValueAsString("Name"); + StringRef Alias = Rec->getValueAsString("Alias"); + auto It = ProcessorMap.find(Alias); + if (It == ProcessorMap.end()) + PrintFatalError(Rec, "Alias '" + Name + + "' references a non-existent Processor '" + + Alias + "'"); + if (!SubTypeEntries.try_emplace(Name, It->second).second) + PrintFatalError( + Rec, "Alias '" + Name + "' duplicates an existing " + + (ProcessorMap.contains(Name) ? "Processor" : "alias")); + } + + // The table must stay sorted by key for the binary search in the lookups. + std::vector> SortedEntries; + SortedEntries.reserve(SubTypeEntries.size()); + for (const auto &Entry : SubTypeEntries) + SortedEntries.emplace_back(Entry.getKey(), Entry.getValue()); + llvm::sort(SortedEntries, llvm::less_first()); StringToOffsetTable StrTab; - for (StringRef Name : Names) + for (const auto &[Name, Proc] : SortedEntries) StrTab.GetOrAddStringOffset(Name); // Note that unlike `FeatureKeyValues`, here we do not need to check for @@ -311,24 +331,25 @@ SubtargetEmitter::cpuKeyValues(raw_ostream &OS, // constructor calls `getSchedModels` to build a `CodeGenSchedModels` object, // which does the duplicate processor check. + unsigned Total = SortedEntries.size(); + // Begin processor table. OS << "// Sorted (by key) array of values for CPU subtype.\n" - << "extern const llvm::SubtargetSubTypeKVStorage< " << ProcessorList.size() - << ", " << (StrTab.size() + 1) << "> " << Target - << "SubTypeKVStorage = {\n {\n"; + << "extern const llvm::SubtargetSubTypeKVStorage< " << Total << ", " + << (StrTab.size() + 1) << "> " << Target << "SubTypeKVStorage = {\n {\n"; - for (const auto &[Idx, Processor] : enumerate(ProcessorList)) { - StringRef Name = Processor->getValueAsString("Name"); + for (const auto &[Idx, Entry] : enumerate(SortedEntries)) { + const auto &[Name, Processor] = Entry; ConstRecVec FeatureList = Processor->getValueAsListOfDefs("Features"); ConstRecVec TuneFeatureList = Processor->getValueAsListOfDefs("TuneFeatures"); - // Warn the user if there are duplicate processor features or tune - // features. - checkDuplicateCPUFeatures(Name, FeatureList, TuneFeatureList); + // Aliases share the canonical processor's already-checked feature lists. + if (Name == Processor->getValueAsString("Name")) + checkDuplicateCPUFeatures(Name, FeatureList, TuneFeatureList); - OS << " { sizeof(SubtargetSubTypeKV) * " << (ProcessorList.size() - Idx) - << " + " << StrTab.GetOrAddStringOffset(Name) << ", "; + OS << " { sizeof(SubtargetSubTypeKV) * " << (Total - Idx) << " + " + << StrTab.GetOrAddStringOffset(Name) << ", "; printFeatureMask(OS, FeatureList, FeatureMap); OS << ", "; @@ -344,7 +365,7 @@ SubtargetEmitter::cpuKeyValues(raw_ostream &OS, // End processor table. OS << "};\n"; - return {ProcessorList.size(), StrTab.size() + 1}; + return {Total, StrTab.size() + 1}; } // @@ -1990,10 +2011,6 @@ void SubtargetEmitter::parseFeaturesFunction(raw_ostream &OS) { return; } - if (Target == "AArch64") - OS << " CPU = AArch64::resolveCPUAlias(CPU);\n" - << " TuneCPU = AArch64::resolveCPUAlias(TuneCPU);\n"; - OS << " InitMCProcessorInfo(CPU, TuneCPU, FS);\n" << " const FeatureBitset &Bits = getFeatureBits();\n"; @@ -2068,11 +2085,6 @@ void SubtargetEmitter::emitGenMCSubtargetInfo(raw_ostream &OS) { OS << " unsigned getHwMode(enum HwModeType type = HwMode_Default) const " "final;\n"; } - if (Target == "AArch64") - OS << " bool isCPUStringValid(StringRef CPU) const final {\n" - << " CPU = AArch64::resolveCPUAlias(CPU);\n" - << " return MCSubtargetInfo::isCPUStringValid(CPU);\n" - << " }\n"; OS << "};\n"; emitHwModeCheck(Target + "GenMCSubtargetInfo", OS, /*IsMC=*/true); } @@ -2105,8 +2117,6 @@ FeatureMapTy SubtargetEmitter::emitEnums(raw_ostream &OS) { SubtargetEmitter::MCDescInfo SubtargetEmitter::emitMCDesc(raw_ostream &OS, const FeatureMapTy &FeatureMap) { IfDefEmitter IfDef(OS, "GET_SUBTARGETINFO_MC_DESC"); - if (Target == "AArch64") - OS << "#include \"llvm/TargetParser/AArch64TargetParser.h\"\n\n"; NamespaceEmitter LlvmNS(OS, "llvm"); MCDescInfo Res; @@ -2127,9 +2137,6 @@ SubtargetEmitter::emitMCDesc(raw_ostream &OS, const FeatureMapTy &FeatureMap) { OS << "\nstatic inline MCSubtargetInfo *create" << Target << "MCSubtargetInfoImpl(" << "const Triple &TT, StringRef CPU, StringRef TuneCPU, StringRef FS) {\n"; - if (Target == "AArch64") - OS << " CPU = AArch64::resolveCPUAlias(CPU);\n" - << " TuneCPU = AArch64::resolveCPUAlias(TuneCPU);\n"; OS << " return new " << Target << "GenMCSubtargetInfo(TT, CPU, TuneCPU, FS, "; OS << "StringTable(" << Target << "SubTypeKVStorage.Strings), "; @@ -2163,8 +2170,6 @@ void SubtargetEmitter::emitTargetDesc(raw_ostream &OS) { OS << "#include \"llvm/ADT/BitmaskEnum.h\"\n"; OS << "#include \"llvm/Support/Debug.h\"\n"; OS << "#include \"llvm/Support/raw_ostream.h\"\n\n"; - if (Target == "AArch64") - OS << "#include \"llvm/TargetParser/AArch64TargetParser.h\"\n\n"; parseFeaturesFunction(OS); } @@ -2264,11 +2269,7 @@ void SubtargetEmitter::emitCtor(raw_ostream &OS, MCDescInfo DescInfo) { OS << ClassName << "::" << ClassName << "(const Triple &TT, StringRef CPU, " << "StringRef TuneCPU, StringRef FS)\n"; - if (Target == "AArch64") - OS << " : TargetSubtargetInfo(TT, AArch64::resolveCPUAlias(CPU),\n" - << " AArch64::resolveCPUAlias(TuneCPU), FS, "; - else - OS << " : TargetSubtargetInfo(TT, CPU, TuneCPU, FS, "; + OS << " : TargetSubtargetInfo(TT, CPU, TuneCPU, FS, "; OS << "StringTable(" << Target << "SubTypeKVStorage.Strings), "; if (DescInfo.NumFeatures) OS << "ArrayRef(" << Target << "FeatureKVStorage.Features), ";