diff --git a/llvm/include/llvm/TargetParser/RISCVTargetParser.h b/llvm/include/llvm/TargetParser/RISCVTargetParser.h index 2ac58a539d5ee..5cb339ff58a75 100644 --- a/llvm/include/llvm/TargetParser/RISCVTargetParser.h +++ b/llvm/include/llvm/TargetParser/RISCVTargetParser.h @@ -16,6 +16,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/Compiler.h" +#include "llvm/Support/Error.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" @@ -47,6 +48,22 @@ struct CPUInfo { bool is64Bit() const { return DefaultMarch.starts_with("rv64"); } }; +/// Fatal errors encountered during parsing. +struct ParserError : public ErrorInfo { + using ErrorInfo::ErrorInfo; + explicit ParserError(const Twine &S) + : ErrorInfo(S, inconvertibleErrorCode()) {} + static char ID; +}; + +/// Warnings encountered during parsing. +struct ParserWarning : public ErrorInfo { + using ErrorInfo::ErrorInfo; + explicit ParserWarning(const Twine &S) + : ErrorInfo(S, inconvertibleErrorCode()) {} + static char ID; +}; + // We use 64 bits as the known part in the scalable vector types. static constexpr unsigned RVVBitsPerBlock = 64; static constexpr unsigned RVVBytesPerBlock = RVVBitsPerBlock / 8; @@ -54,6 +71,9 @@ static constexpr unsigned RVVBytesPerBlock = RVVBitsPerBlock / 8; LLVM_ABI void getFeaturesForCPU(StringRef CPU, SmallVectorImpl &EnabledFeatures, bool NeedPlus = false); +LLVM_ABI void getAllTuneFeatures(SmallVectorImpl &TuneFeatures); +LLVM_ABI Error parseTuneFeatureString( + StringRef TFString, SmallVectorImpl &TuneFeatures); LLVM_ABI bool parseCPU(StringRef CPU, bool IsRV64); LLVM_ABI bool parseTuneCPU(StringRef CPU, bool IsRV64); LLVM_ABI StringRef getMArchFromMcpu(StringRef CPU); diff --git a/llvm/lib/Target/RISCV/RISCVFeatures.td b/llvm/lib/Target/RISCV/RISCVFeatures.td index 0b964c4808d8a..65594a672c0b4 100644 --- a/llvm/lib/Target/RISCV/RISCVFeatures.td +++ b/llvm/lib/Target/RISCV/RISCVFeatures.td @@ -1740,6 +1740,13 @@ def HasVendorXSMTVDot // LLVM specific features and extensions //===----------------------------------------------------------------------===// +class RISCVTuneFeatureBase; +class RISCVSimpleTuneFeature : RISCVTuneFeatureBase; +class RISCVTuneFeature : RISCVTuneFeatureBase { + string PositiveDirectiveName = pos_directive; + string NegativeDirectiveName = neg_directive; +} + // Feature32Bit exists to mark CPUs that support RV32 to distinguish them from // tuning CPU names. def Feature32Bit @@ -1789,47 +1796,58 @@ def FeatureUnalignedVectorMem def TuneNLogNVRGather : SubtargetFeature<"log-vrgather", "RISCVVRGatherCostModel", "NLog2N", - "Has vrgather.vv with LMUL*log2(LMUL) latency">; + "Has vrgather.vv with LMUL*log2(LMUL) latency">, + RISCVSimpleTuneFeature; def TunePostRAScheduler : SubtargetFeature<"use-postra-scheduler", - "UsePostRAScheduler", "true", "Schedule again after register allocation">; + "UsePostRAScheduler", "true", "Schedule again after register allocation">, + RISCVSimpleTuneFeature; def TuneDisableMISchedLoadClustering : SubtargetFeature<"disable-misched-load-clustering", - "EnableMISchedLoadClustering", "false", "Disable load clustering in the machine scheduler">; + "EnableMISchedLoadClustering", "false", "Disable load clustering in the machine scheduler">, + RISCVTuneFeature<"disable-misched-load-clustering", "enable-misched-load-clustering">; def TuneDisableMISchedStoreClustering : SubtargetFeature<"disable-misched-store-clustering", - "EnableMISchedStoreClustering", "false", "Disable store clustering in the machine scheduler">; + "EnableMISchedStoreClustering", "false", "Disable store clustering in the machine scheduler">, + RISCVTuneFeature<"disable-misched-store-clustering", "enable-misched-store-clustering">; def TuneDisablePostMISchedLoadClustering : SubtargetFeature<"disable-postmisched-load-clustering", - "EnablePostMISchedLoadClustering", "false", "Disable PostRA load clustering in the machine scheduler">; + "EnablePostMISchedLoadClustering", "false", "Disable PostRA load clustering in the machine scheduler">, + RISCVTuneFeature<"disable-postmisched-load-clustering", "enable-postmisched-load-clustering">; def TuneDisablePostMISchedStoreClustering : SubtargetFeature<"disable-postmisched-store-clustering", - "EnablePostMISchedStoreClustering", "false", "Disable PostRA store clustering in the machine scheduler">; + "EnablePostMISchedStoreClustering", "false", "Disable PostRA store clustering in the machine scheduler">, + RISCVTuneFeature<"disable-postmisched-store-clustering", "enable-postmisched-store-clustering">; def TuneDisableLatencySchedHeuristic : SubtargetFeature<"disable-latency-sched-heuristic", "DisableLatencySchedHeuristic", "true", - "Disable latency scheduling heuristic">; + "Disable latency scheduling heuristic">, + RISCVTuneFeature<"disable-latency-sched-heuristic", "enable-latency-sched-heuristic">; def TunePredictableSelectIsExpensive : SubtargetFeature<"predictable-select-expensive", "PredictableSelectIsExpensive", "true", - "Prefer likely predicted branches over selects">; + "Prefer likely predicted branches over selects">, + RISCVSimpleTuneFeature; def TuneOptimizedZeroStrideLoad : SubtargetFeature<"optimized-zero-stride-load", "HasOptimizedZeroStrideLoad", "true", "Optimized (perform fewer memory operations)" - "zero-stride vector load">; + "zero-stride vector load">, + RISCVSimpleTuneFeature; foreach nf = {2-8} in def TuneOptimizedNF#nf#SegmentLoadStore : SubtargetFeature<"optimized-nf"#nf#"-segment-load-store", "HasOptimizedNF"#nf#"SegmentLoadStore", "true", "vlseg"#nf#"eN.v and vsseg"#nf#"eN.v are " - "implemented as a wide memory op and shuffle">; + "implemented as a wide memory op and shuffle">, + RISCVSimpleTuneFeature; def TuneVLDependentLatency : SubtargetFeature<"vl-dependent-latency", "HasVLDependentLatency", "true", "Latency of vector instructions is dependent on the " - "dynamic value of vl">; + "dynamic value of vl">, + RISCVSimpleTuneFeature; def Experimental : SubtargetFeature<"experimental", "HasExperimental", @@ -1840,51 +1858,60 @@ def Experimental // that can be done in parallel. def TuneDLenFactor2 : SubtargetFeature<"dlen-factor-2", "DLenFactor2", "true", - "Vector unit DLEN(data path width) is half of VLEN">; + "Vector unit DLEN(data path width) is half of VLEN">, + RISCVSimpleTuneFeature; def TuneNoDefaultUnroll : SubtargetFeature<"no-default-unroll", "EnableDefaultUnroll", "false", - "Disable default unroll preference.">; + "Disable default unroll preference.">, + RISCVTuneFeature<"no-default-unroll", "enable-default-unroll">; // SiFive 7 is able to fuse integer ALU operations with a preceding branch // instruction. def TuneShortForwardBranchOpt : SubtargetFeature<"short-forward-branch-opt", "HasShortForwardBranchOpt", - "true", "Enable short forward branch optimization">; + "true", "Enable short forward branch optimization">, + RISCVSimpleTuneFeature; def HasShortForwardBranchOpt : Predicate<"Subtarget->hasShortForwardBranchOpt()">; def NoShortForwardBranchOpt : Predicate<"!Subtarget->hasShortForwardBranchOpt()">; def TuneShortForwardBranchIMinMax : SubtargetFeature<"short-forward-branch-i-minmax", "HasShortForwardBranchIMinMax", "true", "Enable short forward branch optimization for min,max instructions in Zbb", - [TuneShortForwardBranchOpt]>; + [TuneShortForwardBranchOpt]>, + RISCVSimpleTuneFeature; def TuneShortForwardBranchIMul : SubtargetFeature<"short-forward-branch-i-mul", "HasShortForwardBranchIMul", "true", "Enable short forward branch optimization for mul instruction", - [TuneShortForwardBranchOpt]>; + [TuneShortForwardBranchOpt]>, + RISCVSimpleTuneFeature; // Some subtargets require a S2V transfer buffer to move scalars into vectors. // FIXME: Forming .vx/.vf/.wx/.wf can reduce register pressure. def TuneNoSinkSplatOperands : SubtargetFeature<"no-sink-splat-operands", "SinkSplatOperands", "false", "Disable sink splat operands to enable .vx, .vf," - ".wx, and .wf instructions">; + ".wx, and .wf instructions">, + RISCVTuneFeature<"no-sink-splat-operands", "sink-splat-operands">; def TunePreferWInst : SubtargetFeature<"prefer-w-inst", "PreferWInst", "true", - "Prefer instructions with W suffix">; + "Prefer instructions with W suffix">, + RISCVSimpleTuneFeature; def TuneConditionalCompressedMoveFusion : SubtargetFeature<"conditional-cmv-fusion", "HasConditionalCompressedMoveFusion", - "true", "Enable branch+c.mv fusion">; + "true", "Enable branch+c.mv fusion">, + RISCVSimpleTuneFeature; def HasConditionalMoveFusion : Predicate<"Subtarget->hasConditionalMoveFusion()">; def NoConditionalMoveFusion : Predicate<"!Subtarget->hasConditionalMoveFusion()">; def TuneHasSingleElementVecFP64 : SubtargetFeature<"single-element-vec-fp64", "HasSingleElementVectorFP64", "true", "Certain vector FP64 operations produce a single result " - "element per cycle">; + "element per cycle">, + RISCVSimpleTuneFeature; def TuneMIPSP8700 : SubtargetFeature<"mips-p8700", "RISCVProcFamily", "MIPSP8700", @@ -1900,13 +1927,15 @@ def TuneAndes45 : SubtargetFeature<"andes45", "RISCVProcFamily", "Andes45", "Andes 45-Series processors">; def TuneVXRMPipelineFlush : SubtargetFeature<"vxrm-pipeline-flush", "HasVXRMPipelineFlush", - "true", "VXRM writes causes pipeline flush">; + "true", "VXRM writes causes pipeline flush">, + RISCVSimpleTuneFeature; def TunePreferVsetvliOverReadVLENB : SubtargetFeature<"prefer-vsetvli-over-read-vlenb", "PreferVsetvliOverReadVLENB", "true", - "Prefer vsetvli over read vlenb CSR to calculate VLEN">; + "Prefer vsetvli over read vlenb CSR to calculate VLEN">, + RISCVSimpleTuneFeature; // Assume that lock-free native-width atomics are available, even if the target // and operating system combination would not usually provide them. The user diff --git a/llvm/lib/TargetParser/RISCVTargetParser.cpp b/llvm/lib/TargetParser/RISCVTargetParser.cpp index 5ea63a973ea37..e06aa7f068a34 100644 --- a/llvm/lib/TargetParser/RISCVTargetParser.cpp +++ b/llvm/lib/TargetParser/RISCVTargetParser.cpp @@ -12,13 +12,20 @@ //===----------------------------------------------------------------------===// #include "llvm/TargetParser/RISCVTargetParser.h" +#include "llvm/ADT/SetOperations.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/StringTable.h" #include "llvm/TargetParser/RISCVISAInfo.h" namespace llvm { namespace RISCV { +char ParserError::ID = 0; +char ParserWarning::ID = 0; + enum CPUKind : unsigned { #define PROC(ENUM, NAME, DEFAULT_MARCH, FAST_SCALAR_UNALIGN, \ FAST_VECTOR_UNALIGN, MVENDORID, MARCHID, MIMPID) \ @@ -145,6 +152,157 @@ void getFeaturesForCPU(StringRef CPU, EnabledFeatures.push_back(F.substr(1)); } +namespace { +class RISCVTuneFeatureLookupTable { + struct RISCVTuneFeature { + unsigned PosIdx; + unsigned NegIdx; + unsigned FeatureIdx; + }; + + struct RISCVImpliedTuneFeature { + unsigned FeatureIdx; + unsigned ImpliedFeatureIdx; + }; + +#define GET_TUNE_FEATURES +#include "llvm/TargetParser/RISCVTargetParserDef.inc" + + // Positive directive name -> Feature name + StringMap PositiveMap; + // Negative directive name -> Feature name + StringMap NegativeMap; + + StringMap> ImpliedFeatureMap; + StringMap> InvImpliedFeatureMap; + +public: + static void getAllTuneFeatures(SmallVectorImpl &Features) { + for (const auto &TuneFeature : TuneFeatures) + Features.push_back(TuneFeatureStrings[TuneFeature.FeatureIdx]); + } + + RISCVTuneFeatureLookupTable() { + for (const auto &TuneFeature : TuneFeatures) { + StringRef PosDirective = TuneFeatureStrings[TuneFeature.PosIdx]; + StringRef NegDirective = TuneFeatureStrings[TuneFeature.NegIdx]; + StringRef FeatureName = TuneFeatureStrings[TuneFeature.FeatureIdx]; + PositiveMap[PosDirective] = FeatureName; + NegativeMap[NegDirective] = FeatureName; + } + + for (const auto &Imp : ImpliedTuneFeatures) { + StringRef Feature = TuneFeatureStrings[Imp.FeatureIdx]; + StringRef ImpliedFeature = TuneFeatureStrings[Imp.ImpliedFeatureIdx]; + ImpliedFeatureMap[Feature].push_back(ImpliedFeature); + InvImpliedFeatureMap[ImpliedFeature].push_back(Feature); + } + } + + /// Returns {Feature name, Is positive or not}, or empty feature name + /// if not found. + std::pair getFeature(StringRef DirectiveName) const { + auto It = PositiveMap.find(DirectiveName); + if (It != PositiveMap.end()) + return {It->getValue(), /*IsPositive=*/true}; + + return {NegativeMap.lookup(DirectiveName), /*IsPositive=*/false}; + } + + /// Returns the implied features, or empty ArrayRef if not found. Note: + /// ImpliedFeatureMap / InvImpliedFeatureMap are the owners of these implied + /// feature lists, so we can just return the ArrayRef. + ArrayRef featureImplies(StringRef FeatureName, + bool Inverse = false) const { + const auto &Map = Inverse ? InvImpliedFeatureMap : ImpliedFeatureMap; + auto It = Map.find(FeatureName); + if (It == Map.end()) + return {}; + return It->second; + } +}; +} // namespace + +void getAllTuneFeatures(SmallVectorImpl &Features) { + RISCVTuneFeatureLookupTable::getAllTuneFeatures(Features); +} + +Error parseTuneFeatureString(StringRef TFString, + SmallVectorImpl &ResFeatures) { + using SmallStringSet = SmallSet; + RISCVTuneFeatureLookupTable TFLookup; + + // Do not create ParserWarning right away. Instead, we store the warning + // message until the last moment. + std::string WarningMsg; + + TFString = TFString.trim(); + // Note: StringSet is not really ergonomic to use in this case here. + SmallStringSet PositiveFeatures; + SmallStringSet NegativeFeatures; + // Phase 1: Collect explicit features. + StringRef DirectiveStr; + do { + std::tie(DirectiveStr, TFString) = TFString.split(","); + auto [FeatureName, IsPositive] = TFLookup.getFeature(DirectiveStr); + if (FeatureName.empty()) { + raw_string_ostream SS(WarningMsg); + SS << "unrecognized tune feature directive '" << DirectiveStr << "'"; + continue; + } + + auto &Features = IsPositive ? PositiveFeatures : NegativeFeatures; + if (!Features.insert(FeatureName).second) + return make_error( + "cannot specify more than one instance of '" + Twine(DirectiveStr) + + "'"); + } while (!TFString.empty()); + + auto Intersection = + llvm::set_intersection(PositiveFeatures, NegativeFeatures); + if (!Intersection.empty()) { + std::string IntersectedStr = join(Intersection, "', '"); + return make_error("Feature(s) '" + Twine(IntersectedStr) + + "' cannot appear in both " + "positive and negative directives"); + } + + // Phase 2: Derive implied features. + SmallStringSet DerivedPosFeatures; + SmallStringSet DerivedNegFeatures; + for (StringRef PF : PositiveFeatures) { + if (auto FeatureList = TFLookup.featureImplies(PF); !FeatureList.empty()) + DerivedPosFeatures.insert_range(FeatureList); + } + for (StringRef NF : NegativeFeatures) { + if (auto FeatureList = TFLookup.featureImplies(NF, /*Inverse=*/true); + !FeatureList.empty()) + DerivedNegFeatures.insert_range(FeatureList); + } + PositiveFeatures.insert_range(DerivedPosFeatures); + NegativeFeatures.insert_range(DerivedNegFeatures); + + Intersection = llvm::set_intersection(PositiveFeatures, NegativeFeatures); + if (!Intersection.empty()) { + std::string IntersectedStr = join(Intersection, "', '"); + return make_error("Feature(s) '" + Twine(IntersectedStr) + + "' were implied by both " + "positive and negative directives"); + } + + // Export the result. + const std::string PosPrefix("+"); + const std::string NegPrefix("-"); + for (StringRef PF : PositiveFeatures) + ResFeatures.emplace_back(PosPrefix + PF.str()); + for (StringRef NF : NegativeFeatures) + ResFeatures.emplace_back(NegPrefix + NF.str()); + + if (WarningMsg.empty()) + return Error::success(); + else + return make_error(WarningMsg); +} } // namespace RISCV namespace RISCVVType { diff --git a/llvm/test/TableGen/riscv-target-def.td b/llvm/test/TableGen/riscv-target-def.td index 79178731f12a7..d12fe0a1681bb 100644 --- a/llvm/test/TableGen/riscv-target-def.td +++ b/llvm/test/TableGen/riscv-target-def.td @@ -1,4 +1,6 @@ // RUN: llvm-tblgen -gen-riscv-target-def -I %p/../../include %s | FileCheck %s +// RUN: not llvm-tblgen -gen-riscv-target-def -DINVALID_IMPLY -I %p/../../include %s 2>&1 | FileCheck %s --check-prefix=INVALID-IMPLY +// RUN: not llvm-tblgen -gen-riscv-target-def -DDUPLICATE -I %p/../../include %s 2>&1 | FileCheck %s --check-prefix=DUPLICATE include "llvm/Target/Target.td" @@ -26,6 +28,13 @@ class RISCVExperimentalExtension : RISCVTuneFeatureBase { + string PositiveDirectiveName = pos_directive; + string NegativeDirectiveName = neg_directive; +} + def FeatureStdExtI : RISCVExtension<"i", 2, 1, "'I' (Base Integer Instruction Set)">, @@ -188,3 +197,29 @@ def ROCKET : RISCVTuneProcessorModel<"rocket", // CHECK-NEXT: {"i", 0, 8ULL}, // CHECK-NEXT: }; // CHECK-NEXT: #endif + +#ifdef INVALID_IMPLY +def TuneFeatureBar : SubtargetFeature<"bar", "HasBar", "true", "TBA">; + +def TuneFeatureFoo : SubtargetFeature<"foo", "HasFoo", "true", "TBA", + [TuneFeatureBar]>, + RISCVSimpleTuneFeature; + +// INVALID-IMPLY: error: A RISC-V tune feature can only imply another tune feature +// INVALID-IMPLY-NEXT: def TuneFeatureBar +// INVALID-IMPLY: note: implied by this tune feature +// INVALID-IMPLY-NEXT: def TuneFeatureFoo +#endif + +#ifdef DUPLICATE +def TuneFeatureBar : SubtargetFeature<"bar", "HasBar", "true", "TBA">, + RISCVTuneFeature<"abc", "def">; + +def TuneFeatureFoo : SubtargetFeature<"foo", "HasFoo", "true", "TBA">, + RISCVTuneFeature<"xyz", "abc">; + +// DUPLICATE: error: RISC-V tune feature negative directive 'abc' was already defined +// DUPLICATE-NEXT: def TuneFeatureFoo +// DUPLICATE: note: Previously defined here +// DUPLICATE-NEXT: def TuneFeatureBar +#endif diff --git a/llvm/unittests/TargetParser/RISCVTargetParserTest.cpp b/llvm/unittests/TargetParser/RISCVTargetParserTest.cpp index 63ac8f993ecdc..d294fee88557f 100644 --- a/llvm/unittests/TargetParser/RISCVTargetParserTest.cpp +++ b/llvm/unittests/TargetParser/RISCVTargetParserTest.cpp @@ -7,6 +7,8 @@ //===----------------------------------------------------------------------===// #include "llvm/TargetParser/RISCVTargetParser.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" #include "gtest/gtest.h" using namespace llvm; @@ -30,4 +32,110 @@ TEST(RISCVVType, CheckSameRatioLMUL) { EXPECT_EQ(RISCVVType::LMUL_F2, RISCVVType::getSameRatioLMUL(8, RISCVVType::LMUL_F4, 16)); } + +TEST(RISCVTuneFeature, AllTuneFeatures) { + SmallVector AllTuneFeatures; + RISCV::getAllTuneFeatures(AllTuneFeatures); + // Only allowed subtarget features that are explicitly marked by + // special TableGen class. + EXPECT_EQ(AllTuneFeatures.size(), 28U); + for (auto F : {"conditional-cmv-fusion", + "dlen-factor-2", + "disable-latency-sched-heuristic", + "disable-misched-load-clustering", + "disable-misched-store-clustering", + "disable-postmisched-load-clustering", + "disable-postmisched-store-clustering", + "single-element-vec-fp64", + "log-vrgather", + "no-default-unroll", + "no-sink-splat-operands", + "optimized-nf2-segment-load-store", + "optimized-nf3-segment-load-store", + "optimized-nf4-segment-load-store", + "optimized-nf5-segment-load-store", + "optimized-nf6-segment-load-store", + "optimized-nf7-segment-load-store", + "optimized-nf8-segment-load-store", + "optimized-zero-stride-load", + "use-postra-scheduler", + "predictable-select-expensive", + "prefer-vsetvli-over-read-vlenb", + "prefer-w-inst", + "short-forward-branch-i-minmax", + "short-forward-branch-i-mul", + "short-forward-branch-opt", + "vl-dependent-latency", + "vxrm-pipeline-flush"}) + EXPECT_TRUE(is_contained(AllTuneFeatures, F)); +} + +TEST(RISCVTuneFeature, LegalTuneFeatureStrings) { + SmallVector Result; + EXPECT_FALSE(errorToBool(RISCV::parseTuneFeatureString( + "log-vrgather,no-short-forward-branch-opt,vl-dependent-latency", + Result))); + EXPECT_TRUE(is_contained(Result, "+log-vrgather")); + EXPECT_TRUE(is_contained(Result, "+vl-dependent-latency")); + EXPECT_TRUE(is_contained(Result, "-short-forward-branch-opt")); + EXPECT_TRUE(is_contained(Result, "-short-forward-branch-i-minmax")); + EXPECT_TRUE(is_contained(Result, "-short-forward-branch-i-mul")); + + Result.clear(); + // Test inverse implied features. + EXPECT_FALSE(errorToBool(RISCV::parseTuneFeatureString( + "no-short-forward-branch-i-mul,short-forward-branch-i-minmax", Result))); + EXPECT_TRUE(is_contained(Result, "+short-forward-branch-i-minmax")); + EXPECT_TRUE(is_contained(Result, "+short-forward-branch-opt")); + EXPECT_TRUE(is_contained(Result, "-short-forward-branch-i-mul")); + + Result.clear(); + // Test custom directive names. + EXPECT_FALSE(errorToBool( + RISCV::parseTuneFeatureString("enable-default-unroll,no-sink-splat-" + "operands,enable-latency-sched-heuristic", + Result))); + EXPECT_TRUE(is_contained(Result, "+no-sink-splat-operands")); + EXPECT_TRUE(is_contained(Result, "-no-default-unroll")); + EXPECT_TRUE(is_contained(Result, "-disable-latency-sched-heuristic")); +} + +TEST(RISCVTuneFeature, IgnoreUnrecognizedTuneFeature) { + SmallVector Result; + auto Err = RISCV::parseTuneFeatureString("32bit,log-vrgather", Result); + // This should be an warning. + EXPECT_TRUE(Err.isA()); + EXPECT_EQ(toString(std::move(Err)), + "unrecognized tune feature directive '32bit'"); + EXPECT_TRUE(is_contained(Result, "+log-vrgather")); +} + +TEST(RISCVTuneFeature, DuplicatedFeatures) { + SmallVector Result; + EXPECT_EQ(toString(RISCV::parseTuneFeatureString("log-vrgather,log-vrgather", + Result)), + "cannot specify more than one instance of 'log-vrgather'"); + + EXPECT_EQ(toString(RISCV::parseTuneFeatureString( + "log-vrgather,no-log-vrgather,short-forward-branch-i-mul,no-" + "short-forward-branch-i-mul", + Result)), + "Feature(s) 'log-vrgather', 'short-forward-branch-i-mul' cannot " + "appear in both positive and negative directives"); + + // The error message should show the feature name for those using custom + // directives. + EXPECT_EQ( + toString(RISCV::parseTuneFeatureString( + "disable-latency-sched-heuristic,enable-latency-sched-heuristic", + Result)), + "Feature(s) 'disable-latency-sched-heuristic' cannot appear in both " + "positive and negative directives"); + + EXPECT_EQ( + toString(RISCV::parseTuneFeatureString( + "short-forward-branch-i-mul,no-short-forward-branch-opt", Result)), + "Feature(s) 'short-forward-branch-i-mul', 'short-forward-branch-opt' " + "were implied by both positive and negative directives"); +} } // namespace diff --git a/llvm/utils/TableGen/Basic/RISCVTargetDefEmitter.cpp b/llvm/utils/TableGen/Basic/RISCVTargetDefEmitter.cpp index f7959376adc4a..cbebd2577e81f 100644 --- a/llvm/utils/TableGen/Basic/RISCVTargetDefEmitter.cpp +++ b/llvm/utils/TableGen/Basic/RISCVTargetDefEmitter.cpp @@ -13,8 +13,11 @@ #include "llvm/ADT/DenseSet.h" #include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/RISCVISAUtils.h" +#include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" +#include "llvm/TableGen/StringToOffsetTable.h" #include "llvm/TableGen/TableGenBackend.h" using namespace llvm; @@ -259,7 +262,107 @@ static void emitRISCVExtensionBitmask(const RecordKeeper &RK, raw_ostream &OS) { << "},\n"; } OS << "};\n"; - OS << "#endif\n"; + OS << "#endif\n\n"; +} + +static void emitRISCVTuneFeatures(const RecordKeeper &RK, raw_ostream &OS) { + std::vector TuneFeatureRecords = + RK.getAllDerivedDefinitionsIfDefined("RISCVTuneFeatureBase"); + + // {Post Directive Idx, Neg Directive Idx, TuneFeature Record} + SmallVector> + TuneFeatureDirectives; + // {Directive Idx -> Original Record} + // This is primarily for diagnosing purposes -- when there is a duplication, + // we are able to pointed out the previous definition. + DenseMap DirectiveToRecord; + // A list of {Feature Name, Implied Feature Name} + SmallVector> ImpliedFeatureList; + StringToOffsetTable StrTable; + + auto tryInsertDirectives = [&](StringRef PosName, StringRef NegName, + const Record *R) { + unsigned PosIdx = StrTable.GetOrAddStringOffset(PosName); + if (auto [ItEntry, Inserted] = DirectiveToRecord.try_emplace(PosIdx, R); + !Inserted) { + PrintError(R, "RISC-V tune feature positive directive '" + + Twine(PosName) + "' was already defined"); + PrintFatalNote(ItEntry->second, "Previously defined here"); + } + unsigned NegIdx = StrTable.GetOrAddStringOffset(NegName); + if (auto [ItEntry, Inserted] = DirectiveToRecord.try_emplace(NegIdx, R); + !Inserted) { + PrintError(R, "RISC-V tune feature negative directive '" + + Twine(NegName) + "' was already defined"); + PrintFatalNote(ItEntry->second, "Previously defined here"); + } + + TuneFeatureDirectives.emplace_back(PosIdx, NegIdx, R); + }; + + const std::string SimpleNegPrefix("no-"); + for (const auto *R : TuneFeatureRecords) { + if (!R->isSubClassOf("SubtargetFeature")) + PrintFatalError( + R, "A RISC-V tune feature should also be a SubtargetFeature"); + // Preemptively insert feature name into the string table because we know + // it will be used later. + StringRef FeatureName = R->getValueAsString("Name"); + StrTable.GetOrAddStringOffset(FeatureName); + if (R->isSubClassOf("RISCVSimpleTuneFeature")) { + // The positive directve will be the feature name, and the negative + // directive will be "no-" + feature name. + std::string NegName = SimpleNegPrefix + FeatureName.str(); + tryInsertDirectives(FeatureName, NegName, R); + } else if (R->isSubClassOf("RISCVTuneFeature")) { + StringRef PosName = R->getValueAsString("PositiveDirectiveName"); + StringRef NegName = R->getValueAsString("NegativeDirectiveName"); + tryInsertDirectives(PosName, NegName, R); + } else { + llvm_unreachable("unrecognized RISCVTuneFeatureBase"); + } + } + + for (const auto *R : TuneFeatureRecords) { + std::vector Implies = R->getValueAsListOfDefs("Implies"); + for (const auto *ImpliedRecord : Implies) { + if (!ImpliedRecord->isSubClassOf("RISCVTuneFeatureBase") || + ImpliedRecord == R) { + PrintError(ImpliedRecord, + "A RISC-V tune feature can only imply another tune feature"); + PrintFatalNote(R, "implied by this tune feature"); + } + StringRef CurrFeatureName = R->getValueAsString("Name"); + StringRef ImpliedFeatureName = ImpliedRecord->getValueAsString("Name"); + + ImpliedFeatureList.emplace_back(CurrFeatureName, ImpliedFeatureName); + } + } + + OS << "#ifdef GET_TUNE_FEATURES\n"; + OS << "#undef GET_TUNE_FEATURES\n\n"; + + StrTable.EmitStringTableDef(OS, "TuneFeatureStrings"); + OS << "\n"; + + OS << "static constexpr RISCVTuneFeature TuneFeatures[] = {\n"; + for (const auto &[PosIdx, NegIdx, R] : TuneFeatureDirectives) { + StringRef FeatureName = R->getValueAsString("Name"); + OS.indent(4) << formatv("{{ {0}, {1}, {2} },\t// '{3}'\n", PosIdx, NegIdx, + *StrTable.GetStringOffset(FeatureName), + FeatureName); + } + OS << "};\n\n"; + + OS << "static constexpr RISCVImpliedTuneFeature ImpliedTuneFeatures[] = {\n"; + for (auto [Feature, ImpliedFeature] : ImpliedFeatureList) + OS.indent(4) << formatv("{{ {0}, {1} }, // '{2}' -> '{3}'\n", + *StrTable.GetStringOffset(Feature), + *StrTable.GetStringOffset(ImpliedFeature), Feature, + ImpliedFeature); + OS << "};\n\n"; + + OS << "#endif // GET_TUNE_FEATURES\n"; } static void emitRiscvTargetDef(const RecordKeeper &RK, raw_ostream &OS) { @@ -267,6 +370,7 @@ static void emitRiscvTargetDef(const RecordKeeper &RK, raw_ostream &OS) { emitRISCVProfiles(RK, OS); emitRISCVProcs(RK, OS); emitRISCVExtensionBitmask(RK, OS); + emitRISCVTuneFeatures(RK, OS); } static TableGen::Emitter::Opt X("gen-riscv-target-def", emitRiscvTargetDef,