[llvm] [RISCV] Introduce a new syntax for processor-specific tuning feature strings (Take 2) (PR #175063)
Min-Yih Hsu via llvm-commits
llvm-commits at lists.llvm.org
Fri Jan 30 10:11:51 PST 2026
https://github.com/mshockwave updated https://github.com/llvm/llvm-project/pull/175063
>From c34cb3478b5d8bbaaee25284ba8763c150204250 Mon Sep 17 00:00:00 2001
From: Min-Yih Hsu <min.hsu at sifive.com>
Date: Wed, 7 Jan 2026 15:35:01 -0800
Subject: [PATCH 1/4] [RISCV] Introduce a new syntax for specifying processor
tuning feature strings
Co-Authored-By: Sam Elliott <aelliott at qti.qualcomm.com>
---
llvm/docs/RISCVUsage.rst | 35 +++
.../llvm/TargetParser/RISCVTargetParser.h | 26 +++
llvm/lib/Target/RISCV/RISCVFeatures.td | 145 ++++++++-----
llvm/lib/Target/RISCV/RISCVProcessors.td | 17 +-
llvm/lib/TargetParser/RISCVTargetParser.cpp | 205 ++++++++++++++++++
llvm/test/TableGen/riscv-target-def.td | 33 ++-
.../TargetParser/RISCVTargetParserTest.cpp | 131 +++++++++++
.../TableGen/Basic/RISCVTargetDefEmitter.cpp | 120 +++++++++-
8 files changed, 656 insertions(+), 56 deletions(-)
diff --git a/llvm/docs/RISCVUsage.rst b/llvm/docs/RISCVUsage.rst
index 3452ad86cf21e..46fb5cd914ddf 100644
--- a/llvm/docs/RISCVUsage.rst
+++ b/llvm/docs/RISCVUsage.rst
@@ -632,3 +632,38 @@ Sanitizers
Some intrinsics will be handled correctly anyway, if the RISC-V intrinsic is auto-upgraded into cross-platform LLVM intrinsics. Some others will be "heuristically" handled (possibly incorrectly). The rest will default to the "strict" handler, which checks that all the parameters are fully initialized.
MSan intrinsics support is only required if code (including dependencies) manually calls the intrinsic.
+
+Processor-Specific Tuning Feature String
+========================================
+Due to RISC-V's highly configurable nature, it is often desirable to share a single scheduling model across multiple similar RISC-V processors that only differ in a small number of (uArch) tuning features. An example of such tuning feature could be whether the latency of vector operations depend on VL or not. This could be extended to tuning features that are not directly connected to scheduling model but other parts of the RISC-V backend, like the cost of ``vrgather.vv`` instruction.
+
+To that end, RISC-V LLVM supports a tuning feature string format that helps users to build a performance model by "configuring" an existing tune CPU, along with its scheduling model. For example, this string
+
+::
+ "sifive-x280:single-element-vec-fp64"
+
+takes ``sifive-x280`` as the "base" tune CPU and configured it with ``single-element-vec-fp64``. This gives us a performance model that looks exactly like that of ``sifive-x280``, except some of the 64-bit vector floating point instructions now produce only a single element per cycle due to ``single-element-vec-fp64``. This string could eventually be used in places like ``-mtune`` at the frontend.
+
+More formally speaking, each tuning feature string has the following format:
+
+::
+ tune-cpu ::= 'tuning CPU name in lower case'
+ directive ::= "[a-zA-Z0-9\_-]+"
+ tune-features ::= directive ["," directive]*
+
+A *directive* can and can only _enable_ or _disable_ a certain tuning feature from the tuning CPU. A **positive directive**, like the ``single-element-vec-fp64`` we just saw, enables an additional tuning feature in the associated tuning model. A **negative directive**, on the other hand, removes a certain tuning feature. For example, ``sifive-x390`` already has the ``single-element-vec-fp64`` feature, and we can use
+
+::
+ "sifive-x390:no-single-element-vec-fp64"
+
+to create a new performance model that looks nearly the same as ``sifive-x390`` except ``single-element-vec-fp64`` being cut out. In this case, ``no-single-element-vec-fp64`` is a negative directive.
+
+There are some rules for the list of directives, though:
+
+1. The same directive cannot appear more than once.
+
+2. The positive and negative directives that belong to the same feature cannot appear at the same time.
+
+3. If a feature implies other features -- for example, ``short-forward-branch-imul`` implies ``short-forward-branch-ialu`` -- then the _implied_ features are subject to the previous two rules, too. For example, we cannot write _"short-forward-branch-imul,no-short-forward-branch-ialu"_, because the feature implied by ``short-forward-branch-imul`` violates rule 2.
+
+In addition to the rules listed above, right now, this string only accepts directives that are explicitly supported by the tune CPU. For example, _"sifive-x280:prefer-w-inst"_ is not a valide string as ``prefer-w-inst`` is not supported by ``sifive-x280`` at this moment. Vendors of these processors are expected to maintain the compatibility of their supported directives across different versions. There have been lots of discussions on having "generic" features that are universally supported by all RISC-V CPUs, yet many concerns -- including the difficulty to maintain compatibility across _all_ CPU targets and versions -- make us decide to table this issue until we find a reliable process to select such features.
diff --git a/llvm/include/llvm/TargetParser/RISCVTargetParser.h b/llvm/include/llvm/TargetParser/RISCVTargetParser.h
index 0b4c45e445bb6..b0ed9efcb9d82 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<ParserError, StringError> {
+ using ErrorInfo<ParserError, StringError>::ErrorInfo;
+ explicit ParserError(const Twine &S)
+ : ErrorInfo(S, inconvertibleErrorCode()) {}
+ static char ID;
+};
+
+/// Warnings encountered during parsing.
+struct ParserWarning : public ErrorInfo<ParserWarning, StringError> {
+ using ErrorInfo<ParserWarning, StringError>::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,15 @@ static constexpr unsigned RVVBytesPerBlock = RVVBitsPerBlock / 8;
LLVM_ABI void getFeaturesForCPU(StringRef CPU,
SmallVectorImpl<std::string> &EnabledFeatures,
bool NeedPlus = false);
+LLVM_ABI void getAllTuneFeatures(SmallVectorImpl<StringRef> &TuneFeatures);
+LLVM_ABI void
+getCPUConfigurableTuneFeatures(StringRef CPU,
+ SmallVectorImpl<StringRef> &Directives);
+/// Parse the tune feature string with the respective processor. If \p ProcName
+/// is empty, directives are not filtered by processor.
+LLVM_ABI Error
+parseTuneFeatureString(StringRef ProcName, StringRef TFString,
+ SmallVectorImpl<std::string> &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 64cd9f8184d0d..2ed98c1d49c98 100644
--- a/llvm/lib/Target/RISCV/RISCVFeatures.td
+++ b/llvm/lib/Target/RISCV/RISCVFeatures.td
@@ -1749,6 +1749,21 @@ def HasXAIFET : Predicate<"Subtarget->hasXAIFET()">,
// LLVM specific features and extensions
//===----------------------------------------------------------------------===//
+class RISCVTuneFeature<string name, string pos_directive, string neg_directive,
+ string field_name, string value, string description,
+ list<RISCVTuneFeature> implied_features = []>
+ : SubtargetFeature<name, field_name, value, description> {
+ let Implies = implied_features;
+
+ string PositiveDirectiveName = pos_directive;
+ string NegativeDirectiveName = neg_directive;
+}
+class RISCVSimpleTuneFeature<string name, string field_name, string value,
+ string description,
+ list<RISCVTuneFeature> implied_features = []>
+ : RISCVTuneFeature<name, name, "no-"#name, field_name, value, description,
+ implied_features>;
+
// Feature32Bit exists to mark CPUs that support RV32 to distinguish them from
// tuning CPU names.
def Feature32Bit
@@ -1839,32 +1854,54 @@ def TuneNLogNVRGather
: SubtargetFeature<"log-vrgather", "RISCVVRGatherCostModel", "NLog2N",
"Has vrgather.vv with LMUL*log2(LMUL) latency">;
-def TunePostRAScheduler : SubtargetFeature<"use-postra-scheduler",
- "UsePostRAScheduler", "true", "Schedule again after register allocation">;
-
-def TuneDisableMISchedLoadClustering : SubtargetFeature<"disable-misched-load-clustering",
- "EnableMISchedLoadClustering", "false", "Disable load clustering in the machine scheduler">;
-
-def TuneDisableMISchedStoreClustering : SubtargetFeature<"disable-misched-store-clustering",
- "EnableMISchedStoreClustering", "false", "Disable store clustering in the machine scheduler">;
-
-def TuneDisablePostMISchedLoadClustering : SubtargetFeature<"disable-postmisched-load-clustering",
- "EnablePostMISchedLoadClustering", "false", "Disable PostRA load clustering in the machine scheduler">;
-
-def TuneDisablePostMISchedStoreClustering : SubtargetFeature<"disable-postmisched-store-clustering",
- "EnablePostMISchedStoreClustering", "false", "Disable PostRA store clustering in the machine scheduler">;
+def TunePostRAScheduler
+ : RISCVSimpleTuneFeature<"use-postra-scheduler", "UsePostRAScheduler",
+ "true",
+ "Schedule again after register allocation">;
+
+def TuneDisableMISchedLoadClustering
+ : RISCVTuneFeature<
+ "disable-misched-load-clustering", "disable-misched-load-clustering",
+ "enable-misched-load-clustering", "EnableMISchedLoadClustering",
+ "false", "Disable load clustering in the machine scheduler">;
+
+def TuneDisableMISchedStoreClustering
+ : RISCVTuneFeature<"disable-misched-store-clustering",
+ "disable-misched-store-clustering",
+ "enable-misched-store-clustering",
+ "EnableMISchedStoreClustering", "false",
+ "Disable store clustering in the machine scheduler">;
+
+def TuneDisablePostMISchedLoadClustering
+ : RISCVTuneFeature<
+ "disable-postmisched-load-clustering",
+ "disable-postmisched-load-clustering",
+ "enable-postmisched-load-clustering",
+ "EnablePostMISchedLoadClustering", "false",
+ "Disable PostRA load clustering in the machine scheduler">;
+
+def TuneDisablePostMISchedStoreClustering
+ : RISCVTuneFeature<
+ "disable-postmisched-store-clustering",
+ "disable-postmisched-store-clustering",
+ "enable-postmisched-store-clustering",
+ "EnablePostMISchedStoreClustering", "false",
+ "Disable PostRA store clustering in the machine scheduler">;
def TuneDisableLatencySchedHeuristic
- : SubtargetFeature<"disable-latency-sched-heuristic", "DisableLatencySchedHeuristic", "true",
- "Disable latency scheduling heuristic">;
+ : RISCVTuneFeature<
+ "disable-latency-sched-heuristic", "disable-latency-sched-heuristic",
+ "enable-latency-sched-heuristic", "DisableLatencySchedHeuristic",
+ "true", "Disable latency scheduling heuristic">;
def TuneEnableVsetvliSchedHeuristic
: SubtargetFeature<"enable-vsetvli-sched-heuristic", "EnableVsetvliSchedHeuristic", "true",
"Enable vsetvli-based scheduling heuristic">;
def TunePredictableSelectIsExpensive
- : SubtargetFeature<"predictable-select-expensive", "PredictableSelectIsExpensive", "true",
- "Prefer likely predicted branches over selects">;
+ : RISCVSimpleTuneFeature<"predictable-select-expensive",
+ "PredictableSelectIsExpensive", "true",
+ "Prefer likely predicted branches over selects">;
def TuneOptimizedZeroStrideLoad
: SubtargetFeature<"optimized-zero-stride-load", "HasOptimizedZeroStrideLoad",
@@ -1883,9 +1920,10 @@ foreach nf = {2-8} in
"implemented as a wide memory op and shuffle">;
def TuneVLDependentLatency
- : SubtargetFeature<"vl-dependent-latency", "HasVLDependentLatency", "true",
- "Latency of vector instructions is dependent on the "
- "dynamic value of vl">;
+ : RISCVSimpleTuneFeature<
+ "vl-dependent-latency", "HasVLDependentLatency", "true",
+ "Latency of vector instructions is dependent on the "
+ "dynamic value of vl">;
def Experimental
: SubtargetFeature<"experimental", "HasExperimental",
@@ -1899,7 +1937,8 @@ def TuneDLenFactor2
"Vector unit DLEN(data path width) is half of VLEN">;
def TuneNoDefaultUnroll
- : SubtargetFeature<"no-default-unroll", "EnableDefaultUnroll", "false",
+ : RISCVTuneFeature<"no-default-unroll", "no-default-unroll",
+ "enable-default-unroll", "EnableDefaultUnroll", "false",
"Disable default unroll preference.">;
// Many Microarchitectures are able to fuse a branch over a single instruction
@@ -1924,59 +1963,67 @@ def TuneNoDefaultUnroll
// the vendor-specific instructions should only be enabled for vendor
// cores.
def TuneShortForwardBranchIALU
- : SubtargetFeature<"short-forward-branch-ialu", "HasShortForwardBranchIALU",
- "true", "Enable short forward branch optimization for RVI base instructions">;
+ : RISCVSimpleTuneFeature<
+ "short-forward-branch-ialu", "HasShortForwardBranchIALU", "true",
+ "Enable short forward branch optimization for RVI base instructions">;
def HasShortForwardBranchIALU : Predicate<"Subtarget->hasShortForwardBranchIALU()">;
def NoShortForwardBranch : Predicate<"!Subtarget->hasShortForwardBranchIALU()">;
def TuneShortForwardBranchIMinMax
- : SubtargetFeature<"short-forward-branch-iminmax", "HasShortForwardBranchIMinMax",
- "true", "Enable short forward branch optimization for MIN,MAX instructions in Zbb",
- [TuneShortForwardBranchIALU]>;
+ : RISCVSimpleTuneFeature<"short-forward-branch-iminmax",
+ "HasShortForwardBranchIMinMax", "true",
+ "Enable short forward branch optimization for "
+ "MIN,MAX instructions in Zbb",
+ [TuneShortForwardBranchIALU]>;
def HasShortForwardBranchIMinMax : Predicate<"Subtarget->hasShortForwardBranchIMinMax()">;
def TuneShortForwardBranchIMul
- : SubtargetFeature<"short-forward-branch-imul", "HasShortForwardBranchIMul",
- "true", "Enable short forward branch optimization for MUL instruction",
- [TuneShortForwardBranchIALU]>;
+ : RISCVSimpleTuneFeature<
+ "short-forward-branch-imul", "HasShortForwardBranchIMul", "true",
+ "Enable short forward branch optimization for MUL instruction",
+ [TuneShortForwardBranchIALU]>;
def HasShortForwardBranchIMul : Predicate<"Subtarget->hasShortForwardBranchIMul()">;
def TuneShortForwardBranchILoad
- : SubtargetFeature<"short-forward-branch-iload", "HasShortForwardBranchILoad",
- "true", "Enable short forward branch optimization for load instructions",
- [TuneShortForwardBranchIALU]>;
+ : RISCVSimpleTuneFeature<
+ "short-forward-branch-iload", "HasShortForwardBranchILoad", "true",
+ "Enable short forward branch optimization for load instructions",
+ [TuneShortForwardBranchIALU]>;
def HasShortForwardBranchILoad : Predicate<"Subtarget->hasShortForwardBranchILoad()">;
// 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">;
+ : RISCVTuneFeature<"no-sink-splat-operands", "no-sink-splat-operands",
+ "sink-splat-operands", "SinkSplatOperands", "false",
+ "Disable sink splat operands to enable .vx, .vf, .wx, "
+ "and .wf instructions">;
def TunePreferWInst
- : SubtargetFeature<"prefer-w-inst", "PreferWInst", "true",
- "Prefer instructions with W suffix">;
+ : RISCVSimpleTuneFeature<"prefer-w-inst", "PreferWInst", "true",
+ "Prefer instructions with W suffix">;
def TuneConditionalCompressedMoveFusion
- : SubtargetFeature<"conditional-cmv-fusion", "HasConditionalCompressedMoveFusion",
- "true", "Enable branch+c.mv fusion">;
+ : RISCVSimpleTuneFeature<"conditional-cmv-fusion",
+ "HasConditionalCompressedMoveFusion", "true",
+ "Enable branch+c.mv fusion">;
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">;
+ : RISCVSimpleTuneFeature<
+ "single-element-vec-fp64", "HasSingleElementVectorFP64", "true",
+ "Certain vector FP64 operations produce a single result "
+ "element per cycle">;
-def TuneVXRMPipelineFlush : SubtargetFeature<"vxrm-pipeline-flush", "HasVXRMPipelineFlush",
- "true", "VXRM writes causes pipeline flush">;
+def TuneVXRMPipelineFlush
+ : RISCVSimpleTuneFeature<"vxrm-pipeline-flush", "HasVXRMPipelineFlush",
+ "true", "VXRM writes causes pipeline flush">;
def TunePreferVsetvliOverReadVLENB
- : SubtargetFeature<"prefer-vsetvli-over-read-vlenb",
- "PreferVsetvliOverReadVLENB",
- "true",
- "Prefer vsetvli over read vlenb CSR to calculate VLEN">;
+ : RISCVSimpleTuneFeature<
+ "prefer-vsetvli-over-read-vlenb", "PreferVsetvliOverReadVLENB",
+ "true", "Prefer vsetvli over read vlenb CSR to calculate VLEN">;
//===----------------------------------------------------------------------===//
// CPU Families (alphabetized by vendor).
diff --git a/llvm/lib/Target/RISCV/RISCVProcessors.td b/llvm/lib/Target/RISCV/RISCVProcessors.td
index f7bc3b28fb3a4..c6e5b9eeec56c 100644
--- a/llvm/lib/Target/RISCV/RISCVProcessors.td
+++ b/llvm/lib/Target/RISCV/RISCVProcessors.td
@@ -78,13 +78,16 @@ class RISCVProcessorModel<string n,
int MVendorID = 0;
int MArchID = 0;
int MImpID = 0;
+
+ list<RISCVTuneFeature> ConfigurableTuneFeatures = [];
}
-class RISCVTuneProcessorModel<string n,
- SchedMachineModel m,
+class RISCVTuneProcessorModel<string n, SchedMachineModel m,
list<SubtargetFeature> tunef = [],
list<SubtargetFeature> f = []>
- : ProcessorModel<n, m, f,tunef>;
+ : ProcessorModel<n, m, f, tunef> {
+ list<RISCVTuneFeature> ConfigurableTuneFeatures = [];
+}
defvar GenericTuneFeatures = [TuneOptimizedNF2SegmentLoadStore];
@@ -294,7 +297,9 @@ def SIFIVE_X280 : RISCVProcessorModel<"sifive-x280", SiFive7Model,
FeatureStdExtZvfh,
FeatureStdExtZba,
FeatureStdExtZbb],
- SiFiveIntelligenceTuneFeatures>;
+ SiFiveIntelligenceTuneFeatures> {
+ let ConfigurableTuneFeatures = [TuneHasSingleElementVecFP64];
+}
def SIFIVE_X390 : RISCVProcessorModel<"sifive-x390",
SiFiveX390Model,
@@ -340,7 +345,9 @@ def SIFIVE_X390 : RISCVProcessorModel<"sifive-x390",
FeatureVendorXSiFivecdiscarddlone,
FeatureVendorXSiFivecflushdlone],
!listconcat(SiFiveIntelligenceTuneFeatures,
- [TuneHasSingleElementVecFP64])>;
+ [TuneHasSingleElementVecFP64])> {
+ let ConfigurableTuneFeatures = [TuneHasSingleElementVecFP64];
+}
defvar SiFiveP400TuneFeatures = [TuneNoDefaultUnroll,
TuneConditionalCompressedMoveFusion,
diff --git a/llvm/lib/TargetParser/RISCVTargetParser.cpp b/llvm/lib/TargetParser/RISCVTargetParser.cpp
index d0bd39f15afc4..eef426d7c9aee 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,204 @@ 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;
+ };
+
+ struct RISCVConfigurableTuneFeatures {
+ StringRef Processor;
+ unsigned DirectiveIdx;
+
+ bool operator<(const RISCVConfigurableTuneFeatures &RHS) const {
+ return Processor < RHS.Processor;
+ }
+ };
+
+#define GET_TUNE_FEATURES
+#define GET_CONFIGURABLE_TUNE_FEATURES
+#include "llvm/TargetParser/RISCVTargetParserDef.inc"
+
+ // Positive directive name -> Feature name
+ StringMap<StringRef> PositiveMap;
+ // Negative directive name -> Feature name
+ StringMap<StringRef> NegativeMap;
+
+ StringMap<SmallVector<StringRef>> ImpliedFeatureMap;
+ StringMap<SmallVector<StringRef>> InvImpliedFeatureMap;
+
+public:
+ using SmallStringSet = SmallSet<StringRef, 4>;
+
+ static void getAllTuneFeatures(SmallVectorImpl<StringRef> &Features) {
+ for (const auto &TuneFeature : TuneFeatures)
+ Features.push_back(TuneFeatureStrings[TuneFeature.FeatureIdx]);
+ }
+
+ static void getConfigurableFeatures(StringRef ProcName,
+ SmallStringSet &Directives) {
+ // Entries for the same processor are always put together.
+ auto [ItFirst, ItEnd] =
+ std::equal_range(std::begin(ConfigurableTuneFeatures),
+ std::end(ConfigurableTuneFeatures),
+ RISCVConfigurableTuneFeatures{ProcName, 0});
+ for (; ItFirst != ItEnd; ++ItFirst)
+ Directives.insert(TuneFeatureStrings[ItFirst->DirectiveIdx]);
+ }
+
+ 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<StringRef, bool> 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<StringRef> 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<StringRef> &Features) {
+ RISCVTuneFeatureLookupTable::getAllTuneFeatures(Features);
+}
+
+Error parseTuneFeatureString(StringRef ProcName, StringRef TFString,
+ SmallVectorImpl<std::string> &ResFeatures) {
+ RISCVTuneFeatureLookupTable TFLookup;
+ using SmallStringSet = RISCVTuneFeatureLookupTable::SmallStringSet;
+
+ // Do not create ParserWarning right away. Instead, we store the warning
+ // message until the last moment.
+ std::string WarningMsg;
+
+ TFString = TFString.trim();
+ if (TFString.empty())
+ return Error::success();
+
+ // Note: StringSet is not really ergonomic to use in this case here.
+ SmallStringSet PositiveFeatures;
+ SmallStringSet NegativeFeatures;
+ SmallStringSet PerProcDirectives;
+ RISCVTuneFeatureLookupTable::getConfigurableFeatures(ProcName,
+ PerProcDirectives);
+ if (PerProcDirectives.empty() && !ProcName.empty())
+ return make_error<ParserError>("Processor '" + Twine(ProcName) +
+ "' has no "
+ "configurable tuning features");
+
+ // 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<ParserError>(
+ "cannot specify more than one instance of '" + Twine(DirectiveStr) +
+ "'");
+
+ if (!PerProcDirectives.count(DirectiveStr) && !ProcName.empty())
+ return make_error<ParserError>("Directive '" + Twine(DirectiveStr) +
+ "' is not "
+ "allowed to be used with processor '" +
+ Twine(ProcName) + "'");
+ } while (!TFString.empty());
+
+ auto Intersection =
+ llvm::set_intersection(PositiveFeatures, NegativeFeatures);
+ if (!Intersection.empty()) {
+ std::string IntersectedStr = join(Intersection, "', '");
+ return make_error<ParserError>("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<ParserError>("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();
+
+ return make_error<ParserWarning>(WarningMsg);
+}
+
+void getCPUConfigurableTuneFeatures(StringRef CPU,
+ SmallVectorImpl<StringRef> &Directives) {
+ RISCVTuneFeatureLookupTable::SmallStringSet DirectiveSet;
+ RISCVTuneFeatureLookupTable::getConfigurableFeatures(CPU, DirectiveSet);
+ Directives.assign(DirectiveSet.begin(), DirectiveSet.end());
+}
} // namespace RISCV
namespace RISCVVType {
diff --git a/llvm/test/TableGen/riscv-target-def.td b/llvm/test/TableGen/riscv-target-def.td
index 79178731f12a7..7e97a7926a2d4 100644
--- a/llvm/test/TableGen/riscv-target-def.td
+++ b/llvm/test/TableGen/riscv-target-def.td
@@ -1,4 +1,5 @@
// RUN: llvm-tblgen -gen-riscv-target-def -I %p/../../include %s | FileCheck %s
+// 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 +27,21 @@ class RISCVExperimentalExtension<string name, int major, int minor, string desc,
let Experimental = true;
}
+class RISCVTuneFeature<string name, string pos_directive, string neg_directive,
+ string field_name, string value, string description,
+ list<RISCVTuneFeature> implied_features = []>
+ : SubtargetFeature<name, field_name, value, description> {
+ let Implies = implied_features;
+
+ string PositiveDirectiveName = pos_directive;
+ string NegativeDirectiveName = neg_directive;
+}
+class RISCVSimpleTuneFeature<string name, string field_name, string value,
+ string description,
+ list<RISCVTuneFeature> implied_features = []>
+ : RISCVTuneFeature<name, name, "no-"#name, field_name, value, description,
+ implied_features>;
+
def FeatureStdExtI
: RISCVExtension<"i", 2, 1,
"'I' (Base Integer Instruction Set)">,
@@ -84,13 +100,17 @@ class RISCVProcessorModel<string n,
int MVendorID = 0;
int MArchID = 0;
int MImpID = 0;
+
+ list<RISCVTuneFeature> ConfigurableTuneFeatures = [];
}
class RISCVTuneProcessorModel<string n,
SchedMachineModel m,
list<SubtargetFeature> tunef = [],
list<SubtargetFeature> f = []>
- : ProcessorModel<n, m, f,tunef>;
+ : ProcessorModel<n, m, f, tunef> {
+ list<RISCVTuneFeature> ConfigurableTuneFeatures = [];
+}
def GENERIC_RV32 : RISCVProcessorModel<"generic-rv32",
NoSchedModel,
@@ -188,3 +208,14 @@ def ROCKET : RISCVTuneProcessorModel<"rocket",
// CHECK-NEXT: {"i", 0, 8ULL},
// CHECK-NEXT: };
// CHECK-NEXT: #endif
+
+#ifdef DUPLICATE
+def TuneFeatureBar : RISCVTuneFeature<"bar", "abc", "def", "HasBar", "true", "TBA">;
+
+def TuneFeatureFoo : RISCVTuneFeature<"foo", "xyz", "abc", "HasFoo", "true", "TBA">;
+
+// 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 f778568dd373a..5710aff6f5bb6 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;
@@ -36,4 +38,133 @@ TEST(RISCVVType, CheckSameRatioLMUL) {
RISCVVType::getSameRatioLMUL(
RISCVVType::getSEWLMULRatio(8, RISCVVType::LMUL_F4), 16));
}
+
+TEST(RISCVTuneFeature, AllTuneFeatures) {
+ SmallVector<StringRef> AllTuneFeatures;
+ RISCV::getAllTuneFeatures(AllTuneFeatures);
+ // Only allowed subtarget features that are explicitly marked by
+ // special TableGen class.
+ EXPECT_EQ(AllTuneFeatures.size(), 19U);
+ for (auto F :
+ {"conditional-cmv-fusion", "disable-latency-sched-heuristic",
+ "disable-misched-load-clustering", "disable-misched-store-clustering",
+ "disable-postmisched-load-clustering",
+ "disable-postmisched-store-clustering", "single-element-vec-fp64",
+ "no-default-unroll", "no-sink-splat-operands", "use-postra-scheduler",
+ "predictable-select-expensive", "prefer-vsetvli-over-read-vlenb",
+ "prefer-w-inst", "short-forward-branch-ialu",
+ "short-forward-branch-iminmax", "short-forward-branch-imul",
+ "short-forward-branch-iload", "vl-dependent-latency",
+ "vxrm-pipeline-flush"})
+ EXPECT_TRUE(is_contained(AllTuneFeatures, F));
+}
+
+TEST(RISCVTuneFeature, LegalTuneFeatureStrings) {
+ SmallVector<std::string> Result;
+ EXPECT_FALSE(errorToBool(RISCV::parseTuneFeatureString(
+ /*ProcName=*/"",
+ "prefer-w-inst,no-short-forward-branch-ialu,vl-dependent-latency",
+ Result)));
+ EXPECT_TRUE(is_contained(Result, "+prefer-w-inst"));
+ EXPECT_TRUE(is_contained(Result, "+vl-dependent-latency"));
+ EXPECT_TRUE(is_contained(Result, "-short-forward-branch-ialu"));
+ EXPECT_TRUE(is_contained(Result, "-short-forward-branch-iminmax"));
+ EXPECT_TRUE(is_contained(Result, "-short-forward-branch-imul"));
+ EXPECT_TRUE(is_contained(Result, "-short-forward-branch-iload"));
+
+ Result.clear();
+ // Test inverse implied features.
+ EXPECT_FALSE(errorToBool(RISCV::parseTuneFeatureString(
+ /*ProcName=*/"",
+ "no-short-forward-branch-imul,short-forward-branch-iminmax", Result)));
+ EXPECT_TRUE(is_contained(Result, "+short-forward-branch-iminmax"));
+ EXPECT_TRUE(is_contained(Result, "+short-forward-branch-ialu"));
+ EXPECT_TRUE(is_contained(Result, "-short-forward-branch-imul"));
+
+ Result.clear();
+ // Test custom directive names.
+ EXPECT_FALSE(errorToBool(
+ RISCV::parseTuneFeatureString(/*ProcName=*/"",
+ "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<std::string> Result;
+ auto Err = RISCV::parseTuneFeatureString(/*ProcName=*/"",
+ "32bit,prefer-w-inst", Result);
+ // This should be an warning.
+ EXPECT_TRUE(Err.isA<RISCV::ParserWarning>());
+ EXPECT_EQ(toString(std::move(Err)),
+ "unrecognized tune feature directive '32bit'");
+ EXPECT_TRUE(is_contained(Result, "+prefer-w-inst"));
+}
+
+TEST(RISCVTuneFeature, DuplicatedFeatures) {
+ SmallVector<std::string> Result;
+ EXPECT_EQ(toString(RISCV::parseTuneFeatureString(
+ /*ProcName=*/"", "prefer-w-inst,prefer-w-inst", Result)),
+ "cannot specify more than one instance of 'prefer-w-inst'");
+
+ EXPECT_EQ(toString(RISCV::parseTuneFeatureString(
+ /*ProcName=*/"",
+ "prefer-w-inst,no-prefer-w-inst,short-forward-branch-imul,no-"
+ "short-forward-branch-imul",
+ Result)),
+ "Feature(s) 'prefer-w-inst', 'short-forward-branch-imul' 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(
+ /*ProcName=*/"",
+ "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(
+ /*ProcName=*/"",
+ "short-forward-branch-imul,no-short-forward-branch-ialu", Result)),
+ "Feature(s) 'short-forward-branch-imul', 'short-forward-branch-ialu' "
+ "were implied by both positive and negative directives");
+}
+
+TEST(RISCVTuneFeature, ProcConfigurableFeatures) {
+ SmallVector<std::string> Result;
+ EXPECT_FALSE(errorToBool(RISCV::parseTuneFeatureString(
+ "sifive-x280", "single-element-vec-fp64", Result)));
+ EXPECT_TRUE(is_contained(Result, "+single-element-vec-fp64"));
+
+ Result.clear();
+ EXPECT_EQ(
+ toString(RISCV::parseTuneFeatureString(
+ "sifive-x280", "single-element-vec-fp64,prefer-w-inst", Result)),
+ "Directive 'prefer-w-inst' is not allowed to be used with processor "
+ "'sifive-x280'");
+}
+
+TEST(RISCVTuneFeature, AllProcConfigurableFeatures) {
+ SmallVector<StringRef> Result;
+ RISCV::getCPUConfigurableTuneFeatures("sifive-x280", Result);
+ EXPECT_TRUE(is_contained(Result, "single-element-vec-fp64"));
+ EXPECT_TRUE(is_contained(Result, "no-single-element-vec-fp64"));
+ EXPECT_EQ(Result.size(), 2U);
+
+ Result.clear();
+ RISCV::getCPUConfigurableTuneFeatures("sifive-x390", Result);
+ EXPECT_TRUE(is_contained(Result, "single-element-vec-fp64"));
+ EXPECT_TRUE(is_contained(Result, "no-single-element-vec-fp64"));
+ EXPECT_EQ(Result.size(), 2U);
+
+ Result.clear();
+ RISCV::getCPUConfigurableTuneFeatures("rocket", Result);
+ EXPECT_TRUE(Result.empty());
+}
} // namespace
diff --git a/llvm/utils/TableGen/Basic/RISCVTargetDefEmitter.cpp b/llvm/utils/TableGen/Basic/RISCVTargetDefEmitter.cpp
index f7959376adc4a..24f122b4cb178 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,118 @@ 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,
+ StringToOffsetTable &StrTable,
+ raw_ostream &OS) {
+ std::vector<const Record *> TuneFeatureRecords =
+ RK.getAllDerivedDefinitionsIfDefined("RISCVTuneFeature");
+
+ // {Post Directive Idx, Neg Directive Idx, TuneFeature Record}
+ SmallVector<std::tuple<unsigned, unsigned, const Record *>>
+ 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<unsigned, const Record *> DirectiveToRecord;
+ // A list of {Feature Name, Implied Feature Name}
+ SmallVector<std::pair<StringRef, StringRef>> ImpliedFeatureList;
+
+ for (const auto *R : TuneFeatureRecords) {
+ // Preemptively insert feature name into the string table because we know
+ // it will be used later.
+ StringRef FeatureName = R->getValueAsString("Name");
+ StrTable.GetOrAddStringOffset(FeatureName);
+
+ StringRef PosName = R->getValueAsString("PositiveDirectiveName");
+ StringRef NegName = R->getValueAsString("NegativeDirectiveName");
+ 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);
+ }
+
+ for (const auto *R : TuneFeatureRecords) {
+ std::vector<const Record *> Implies = R->getValueAsListOfDefs("Implies");
+ for (const auto *ImpliedRecord : Implies) {
+ 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\n";
+}
+
+static void
+emitRISCVConfigurableTuneFeatures(const RecordKeeper &RK,
+ const StringToOffsetTable &StrTable,
+ raw_ostream &OS) {
+ std::vector<const Record *> AllProcModels =
+ RK.getAllDerivedDefinitionsIfDefined("ProcessorModel");
+
+ OS << "#ifdef GET_CONFIGURABLE_TUNE_FEATURES\n";
+ OS << "#undef GET_CONFIGURABLE_TUNE_FEATURES\n\n";
+
+ OS << "static constexpr RISCVConfigurableTuneFeatures "
+ "ConfigurableTuneFeatures[] = {\n";
+
+ for (const Record *Proc : AllProcModels) {
+ StringRef ProcName = Proc->getValueAsString("Name");
+ std::vector<const Record *> TuneFeatures =
+ Proc->getValueAsListOfDefs("ConfigurableTuneFeatures");
+ for (const Record *TF : TuneFeatures) {
+ unsigned PosDirectiveIdx = *StrTable.GetStringOffset(
+ TF->getValueAsString("PositiveDirectiveName"));
+ unsigned NegDirectiveIdx = *StrTable.GetStringOffset(
+ TF->getValueAsString("NegativeDirectiveName"));
+ OS.indent(4) << formatv("{{ {{ \"{0}\" }, {1} },\n", ProcName,
+ PosDirectiveIdx);
+ OS.indent(4) << formatv("{{ {{ \"{0}\" }, {1} },\n", ProcName,
+ NegDirectiveIdx);
+ }
+ }
+
+ OS << "};\n\n";
+ OS << "#endif // GET_CONFIGURABLE_TUNE_FEATURES\n";
}
static void emitRiscvTargetDef(const RecordKeeper &RK, raw_ostream &OS) {
@@ -267,6 +381,10 @@ static void emitRiscvTargetDef(const RecordKeeper &RK, raw_ostream &OS) {
emitRISCVProfiles(RK, OS);
emitRISCVProcs(RK, OS);
emitRISCVExtensionBitmask(RK, OS);
+
+ StringToOffsetTable TuneFeatureStrTable;
+ emitRISCVTuneFeatures(RK, TuneFeatureStrTable, OS);
+ emitRISCVConfigurableTuneFeatures(RK, TuneFeatureStrTable, OS);
}
static TableGen::Emitter::Opt X("gen-riscv-target-def", emitRiscvTargetDef,
>From cf76296c368693b706a0fa4503d11c9b5535bbb0 Mon Sep 17 00:00:00 2001
From: Min-Yih Hsu <min.hsu at sifive.com>
Date: Thu, 8 Jan 2026 13:30:04 -0800
Subject: [PATCH 2/4] fixup! [RISCV] Introduce a new syntax for specifying
processor tuning feature strings
---
llvm/lib/Target/RISCV/RISCVFeatures.td | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Target/RISCV/RISCVFeatures.td b/llvm/lib/Target/RISCV/RISCVFeatures.td
index 2ed98c1d49c98..b253007cfac44 100644
--- a/llvm/lib/Target/RISCV/RISCVFeatures.td
+++ b/llvm/lib/Target/RISCV/RISCVFeatures.td
@@ -1996,7 +1996,7 @@ def HasShortForwardBranchILoad : Predicate<"Subtarget->hasShortForwardBranchILoa
def TuneNoSinkSplatOperands
: RISCVTuneFeature<"no-sink-splat-operands", "no-sink-splat-operands",
"sink-splat-operands", "SinkSplatOperands", "false",
- "Disable sink splat operands to enable .vx, .vf, .wx, "
+ "Disable sink splat operands to enable .vx, .vf,.wx, "
"and .wf instructions">;
def TunePreferWInst
>From d5a79e22d83756c0d2a0fc24bc8497dcf60942d7 Mon Sep 17 00:00:00 2001
From: Min-Yih Hsu <min.hsu at sifive.com>
Date: Tue, 27 Jan 2026 11:14:25 -0800
Subject: [PATCH 3/4] fixup! Typo
---
llvm/docs/RISCVUsage.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/docs/RISCVUsage.rst b/llvm/docs/RISCVUsage.rst
index 46fb5cd914ddf..1de05d3983d64 100644
--- a/llvm/docs/RISCVUsage.rst
+++ b/llvm/docs/RISCVUsage.rst
@@ -666,4 +666,4 @@ There are some rules for the list of directives, though:
3. If a feature implies other features -- for example, ``short-forward-branch-imul`` implies ``short-forward-branch-ialu`` -- then the _implied_ features are subject to the previous two rules, too. For example, we cannot write _"short-forward-branch-imul,no-short-forward-branch-ialu"_, because the feature implied by ``short-forward-branch-imul`` violates rule 2.
-In addition to the rules listed above, right now, this string only accepts directives that are explicitly supported by the tune CPU. For example, _"sifive-x280:prefer-w-inst"_ is not a valide string as ``prefer-w-inst`` is not supported by ``sifive-x280`` at this moment. Vendors of these processors are expected to maintain the compatibility of their supported directives across different versions. There have been lots of discussions on having "generic" features that are universally supported by all RISC-V CPUs, yet many concerns -- including the difficulty to maintain compatibility across _all_ CPU targets and versions -- make us decide to table this issue until we find a reliable process to select such features.
+In addition to the rules listed above, right now, this string only accepts directives that are explicitly supported by the tune CPU. For example, _"sifive-x280:prefer-w-inst"_ is not a valid string as ``prefer-w-inst`` is not supported by ``sifive-x280`` at this moment. Vendors of these processors are expected to maintain the compatibility of their supported directives across different versions. There have been lots of discussions on having "generic" features that are universally supported by all RISC-V CPUs, yet many concerns -- including the difficulty to maintain compatibility across _all_ CPU targets and versions -- make us decide to table this issue until we find a reliable process to select such features.
>From caeedb305fb3e448a65a57fa1624724aecf39310 Mon Sep 17 00:00:00 2001
From: Min-Yih Hsu <min.hsu at sifive.com>
Date: Fri, 30 Jan 2026 10:11:32 -0800
Subject: [PATCH 4/4] fixup! Rename single-element-vec-fp64 directive
---
llvm/docs/RISCVUsage.rst | 9 +++++++--
llvm/lib/Target/RISCV/RISCVFeatures.td | 5 +++--
llvm/unittests/TargetParser/RISCVTargetParserTest.cpp | 4 ++--
3 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/llvm/docs/RISCVUsage.rst b/llvm/docs/RISCVUsage.rst
index 1de05d3983d64..3f420743b35b4 100644
--- a/llvm/docs/RISCVUsage.rst
+++ b/llvm/docs/RISCVUsage.rst
@@ -646,6 +646,11 @@ takes ``sifive-x280`` as the "base" tune CPU and configured it with ``single-ele
More formally speaking, each tuning feature string has the following format:
+::
+ <tune-cpu>[":"<tune-features>]?
+
+where
+
::
tune-cpu ::= 'tuning CPU name in lower case'
directive ::= "[a-zA-Z0-9\_-]+"
@@ -654,9 +659,9 @@ More formally speaking, each tuning feature string has the following format:
A *directive* can and can only _enable_ or _disable_ a certain tuning feature from the tuning CPU. A **positive directive**, like the ``single-element-vec-fp64`` we just saw, enables an additional tuning feature in the associated tuning model. A **negative directive**, on the other hand, removes a certain tuning feature. For example, ``sifive-x390`` already has the ``single-element-vec-fp64`` feature, and we can use
::
- "sifive-x390:no-single-element-vec-fp64"
+ "sifive-x390:full-vec-fp64"
-to create a new performance model that looks nearly the same as ``sifive-x390`` except ``single-element-vec-fp64`` being cut out. In this case, ``no-single-element-vec-fp64`` is a negative directive.
+to create a new performance model that looks nearly the same as ``sifive-x390`` except ``single-element-vec-fp64`` being cut out. In this case, ``full-vec-fp64`` is a negative directive.
There are some rules for the list of directives, though:
diff --git a/llvm/lib/Target/RISCV/RISCVFeatures.td b/llvm/lib/Target/RISCV/RISCVFeatures.td
index b253007cfac44..250bf58ca74ab 100644
--- a/llvm/lib/Target/RISCV/RISCVFeatures.td
+++ b/llvm/lib/Target/RISCV/RISCVFeatures.td
@@ -2011,8 +2011,9 @@ def HasConditionalMoveFusion : Predicate<"Subtarget->hasConditionalMoveFusion()"
def NoConditionalMoveFusion : Predicate<"!Subtarget->hasConditionalMoveFusion()">;
def TuneHasSingleElementVecFP64
- : RISCVSimpleTuneFeature<
- "single-element-vec-fp64", "HasSingleElementVectorFP64", "true",
+ : RISCVTuneFeature<"single-element-vec-fp64",
+ "single-element-vec-fp64", "full-vec-fp64",
+ "HasSingleElementVectorFP64", "true",
"Certain vector FP64 operations produce a single result "
"element per cycle">;
diff --git a/llvm/unittests/TargetParser/RISCVTargetParserTest.cpp b/llvm/unittests/TargetParser/RISCVTargetParserTest.cpp
index 5710aff6f5bb6..5778c67ab5bf9 100644
--- a/llvm/unittests/TargetParser/RISCVTargetParserTest.cpp
+++ b/llvm/unittests/TargetParser/RISCVTargetParserTest.cpp
@@ -154,13 +154,13 @@ TEST(RISCVTuneFeature, AllProcConfigurableFeatures) {
SmallVector<StringRef> Result;
RISCV::getCPUConfigurableTuneFeatures("sifive-x280", Result);
EXPECT_TRUE(is_contained(Result, "single-element-vec-fp64"));
- EXPECT_TRUE(is_contained(Result, "no-single-element-vec-fp64"));
+ EXPECT_TRUE(is_contained(Result, "full-vec-fp64"));
EXPECT_EQ(Result.size(), 2U);
Result.clear();
RISCV::getCPUConfigurableTuneFeatures("sifive-x390", Result);
EXPECT_TRUE(is_contained(Result, "single-element-vec-fp64"));
- EXPECT_TRUE(is_contained(Result, "no-single-element-vec-fp64"));
+ EXPECT_TRUE(is_contained(Result, "full-vec-fp64"));
EXPECT_EQ(Result.size(), 2U);
Result.clear();
More information about the llvm-commits
mailing list