Skip to content

Commit ef9637b

Browse files
mshockwavelenary
andcommitted
[RISCV] Introduce a new syntax for specifying tuning feature strings
Co-Authored-By: Sam Elliott <[email protected]>
1 parent e8cc0d2 commit ef9637b

File tree

5 files changed

+265
-23
lines changed

5 files changed

+265
-23
lines changed

llvm/include/llvm/TargetParser/RISCVTargetParser.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "llvm/ADT/StringRef.h"
1818
#include "llvm/Support/Compiler.h"
19+
#include "llvm/Support/Error.h"
1920
#include "llvm/Support/MathExtras.h"
2021
#include "llvm/Support/raw_ostream.h"
2122

@@ -54,6 +55,9 @@ static constexpr unsigned RVVBytesPerBlock = RVVBitsPerBlock / 8;
5455
LLVM_ABI void getFeaturesForCPU(StringRef CPU,
5556
SmallVectorImpl<std::string> &EnabledFeatures,
5657
bool NeedPlus = false);
58+
LLVM_ABI void getAllTuneFeatures(SmallVectorImpl<StringRef> &TuneFeatures);
59+
LLVM_ABI Error parseTuneFeatureString(
60+
StringRef TFString, SmallVectorImpl<std::string> &TuneFeatures);
5761
LLVM_ABI bool parseCPU(StringRef CPU, bool IsRV64);
5862
LLVM_ABI bool parseTuneCPU(StringRef CPU, bool IsRV64);
5963
LLVM_ABI StringRef getMArchFromMcpu(StringRef CPU);

llvm/lib/Target/RISCV/RISCVFeatures.td

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1740,6 +1740,10 @@ def HasVendorXSMTVDot
17401740
// LLVM specific features and extensions
17411741
//===----------------------------------------------------------------------===//
17421742

1743+
class RISCVTuneFeature<string name, string field_name, string value,
1744+
string description, list<SubtargetFeature> implied = []>
1745+
: SubtargetFeature<name, field_name, value, description, implied>;
1746+
17431747
// Feature32Bit exists to mark CPUs that support RV32 to distinguish them from
17441748
// tuning CPU names.
17451749
def Feature32Bit
@@ -1788,46 +1792,46 @@ def FeatureUnalignedVectorMem
17881792
"loads and stores">;
17891793

17901794
def TuneNLogNVRGather
1791-
: SubtargetFeature<"log-vrgather", "RISCVVRGatherCostModel", "NLog2N",
1795+
: RISCVTuneFeature<"log-vrgather", "RISCVVRGatherCostModel", "NLog2N",
17921796
"Has vrgather.vv with LMUL*log2(LMUL) latency">;
17931797

1794-
def TunePostRAScheduler : SubtargetFeature<"use-postra-scheduler",
1798+
def TunePostRAScheduler : RISCVTuneFeature<"use-postra-scheduler",
17951799
"UsePostRAScheduler", "true", "Schedule again after register allocation">;
17961800

1797-
def TuneDisableMISchedLoadClustering : SubtargetFeature<"disable-misched-load-clustering",
1801+
def TuneDisableMISchedLoadClustering : RISCVTuneFeature<"disable-misched-load-clustering",
17981802
"EnableMISchedLoadClustering", "false", "Disable load clustering in the machine scheduler">;
17991803

1800-
def TuneDisableMISchedStoreClustering : SubtargetFeature<"disable-misched-store-clustering",
1804+
def TuneDisableMISchedStoreClustering : RISCVTuneFeature<"disable-misched-store-clustering",
18011805
"EnableMISchedStoreClustering", "false", "Disable store clustering in the machine scheduler">;
18021806

1803-
def TuneDisablePostMISchedLoadClustering : SubtargetFeature<"disable-postmisched-load-clustering",
1807+
def TuneDisablePostMISchedLoadClustering : RISCVTuneFeature<"disable-postmisched-load-clustering",
18041808
"EnablePostMISchedLoadClustering", "false", "Disable PostRA load clustering in the machine scheduler">;
18051809

1806-
def TuneDisablePostMISchedStoreClustering : SubtargetFeature<"disable-postmisched-store-clustering",
1810+
def TuneDisablePostMISchedStoreClustering : RISCVTuneFeature<"disable-postmisched-store-clustering",
18071811
"EnablePostMISchedStoreClustering", "false", "Disable PostRA store clustering in the machine scheduler">;
18081812

18091813
def TuneDisableLatencySchedHeuristic
1810-
: SubtargetFeature<"disable-latency-sched-heuristic", "DisableLatencySchedHeuristic", "true",
1814+
: RISCVTuneFeature<"disable-latency-sched-heuristic", "DisableLatencySchedHeuristic", "true",
18111815
"Disable latency scheduling heuristic">;
18121816

18131817
def TunePredictableSelectIsExpensive
1814-
: SubtargetFeature<"predictable-select-expensive", "PredictableSelectIsExpensive", "true",
1818+
: RISCVTuneFeature<"predictable-select-expensive", "PredictableSelectIsExpensive", "true",
18151819
"Prefer likely predicted branches over selects">;
18161820

18171821
def TuneOptimizedZeroStrideLoad
1818-
: SubtargetFeature<"optimized-zero-stride-load", "HasOptimizedZeroStrideLoad",
1822+
: RISCVTuneFeature<"optimized-zero-stride-load", "HasOptimizedZeroStrideLoad",
18191823
"true", "Optimized (perform fewer memory operations)"
18201824
"zero-stride vector load">;
18211825

18221826
foreach nf = {2-8} in
18231827
def TuneOptimizedNF#nf#SegmentLoadStore :
1824-
SubtargetFeature<"optimized-nf"#nf#"-segment-load-store",
1828+
RISCVTuneFeature<"optimized-nf"#nf#"-segment-load-store",
18251829
"HasOptimizedNF"#nf#"SegmentLoadStore",
18261830
"true", "vlseg"#nf#"eN.v and vsseg"#nf#"eN.v are "
18271831
"implemented as a wide memory op and shuffle">;
18281832

18291833
def TuneVLDependentLatency
1830-
: SubtargetFeature<"vl-dependent-latency", "HasVLDependentLatency", "true",
1834+
: RISCVTuneFeature<"vl-dependent-latency", "HasVLDependentLatency", "true",
18311835
"Latency of vector instructions is dependent on the "
18321836
"dynamic value of vl">;
18331837

@@ -1839,50 +1843,50 @@ def Experimental
18391843
// and instead split over multiple cycles. DLEN refers to the datapath width
18401844
// that can be done in parallel.
18411845
def TuneDLenFactor2
1842-
: SubtargetFeature<"dlen-factor-2", "DLenFactor2", "true",
1846+
: RISCVTuneFeature<"dlen-factor-2", "DLenFactor2", "true",
18431847
"Vector unit DLEN(data path width) is half of VLEN">;
18441848

18451849
def TuneNoDefaultUnroll
1846-
: SubtargetFeature<"no-default-unroll", "EnableDefaultUnroll", "false",
1850+
: RISCVTuneFeature<"no-default-unroll", "EnableDefaultUnroll", "false",
18471851
"Disable default unroll preference.">;
18481852

18491853
// SiFive 7 is able to fuse integer ALU operations with a preceding branch
18501854
// instruction.
18511855
def TuneShortForwardBranchOpt
1852-
: SubtargetFeature<"short-forward-branch-opt", "HasShortForwardBranchOpt",
1856+
: RISCVTuneFeature<"short-forward-branch-opt", "HasShortForwardBranchOpt",
18531857
"true", "Enable short forward branch optimization">;
18541858
def HasShortForwardBranchOpt : Predicate<"Subtarget->hasShortForwardBranchOpt()">;
18551859
def NoShortForwardBranchOpt : Predicate<"!Subtarget->hasShortForwardBranchOpt()">;
18561860

18571861
def TuneShortForwardBranchIMinMax
1858-
: SubtargetFeature<"short-forward-branch-i-minmax", "HasShortForwardBranchIMinMax",
1862+
: RISCVTuneFeature<"short-forward-branch-i-minmax", "HasShortForwardBranchIMinMax",
18591863
"true", "Enable short forward branch optimization for min,max instructions in Zbb",
18601864
[TuneShortForwardBranchOpt]>;
18611865

18621866
def TuneShortForwardBranchIMul
1863-
: SubtargetFeature<"short-forward-branch-i-mul", "HasShortForwardBranchIMul",
1867+
: RISCVTuneFeature<"short-forward-branch-i-mul", "HasShortForwardBranchIMul",
18641868
"true", "Enable short forward branch optimization for mul instruction",
18651869
[TuneShortForwardBranchOpt]>;
18661870

18671871
// Some subtargets require a S2V transfer buffer to move scalars into vectors.
18681872
// FIXME: Forming .vx/.vf/.wx/.wf can reduce register pressure.
18691873
def TuneNoSinkSplatOperands
1870-
: SubtargetFeature<"no-sink-splat-operands", "SinkSplatOperands",
1874+
: RISCVTuneFeature<"no-sink-splat-operands", "SinkSplatOperands",
18711875
"false", "Disable sink splat operands to enable .vx, .vf,"
18721876
".wx, and .wf instructions">;
18731877

18741878
def TunePreferWInst
1875-
: SubtargetFeature<"prefer-w-inst", "PreferWInst", "true",
1879+
: RISCVTuneFeature<"prefer-w-inst", "PreferWInst", "true",
18761880
"Prefer instructions with W suffix">;
18771881

18781882
def TuneConditionalCompressedMoveFusion
1879-
: SubtargetFeature<"conditional-cmv-fusion", "HasConditionalCompressedMoveFusion",
1883+
: RISCVTuneFeature<"conditional-cmv-fusion", "HasConditionalCompressedMoveFusion",
18801884
"true", "Enable branch+c.mv fusion">;
18811885
def HasConditionalMoveFusion : Predicate<"Subtarget->hasConditionalMoveFusion()">;
18821886
def NoConditionalMoveFusion : Predicate<"!Subtarget->hasConditionalMoveFusion()">;
18831887

18841888
def TuneHasSingleElementVecFP64
1885-
: SubtargetFeature<"single-element-vec-fp64", "HasSingleElementVectorFP64", "true",
1889+
: RISCVTuneFeature<"single-element-vec-fp64", "HasSingleElementVectorFP64", "true",
18861890
"Certain vector FP64 operations produce a single result "
18871891
"element per cycle">;
18881892

@@ -1899,11 +1903,11 @@ def TuneVentanaVeyron : SubtargetFeature<"ventana-veyron", "RISCVProcFamily", "V
18991903
def TuneAndes45 : SubtargetFeature<"andes45", "RISCVProcFamily", "Andes45",
19001904
"Andes 45-Series processors">;
19011905

1902-
def TuneVXRMPipelineFlush : SubtargetFeature<"vxrm-pipeline-flush", "HasVXRMPipelineFlush",
1906+
def TuneVXRMPipelineFlush : RISCVTuneFeature<"vxrm-pipeline-flush", "HasVXRMPipelineFlush",
19031907
"true", "VXRM writes causes pipeline flush">;
19041908

19051909
def TunePreferVsetvliOverReadVLENB
1906-
: SubtargetFeature<"prefer-vsetvli-over-read-vlenb",
1910+
: RISCVTuneFeature<"prefer-vsetvli-over-read-vlenb",
19071911
"PreferVsetvliOverReadVLENB",
19081912
"true",
19091913
"Prefer vsetvli over read vlenb CSR to calculate VLEN">;

llvm/lib/TargetParser/RISCVTargetParser.cpp

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@
1212
//===----------------------------------------------------------------------===//
1313

1414
#include "llvm/TargetParser/RISCVTargetParser.h"
15+
#include "llvm/ADT/SetOperations.h"
16+
#include "llvm/ADT/SmallSet.h"
1517
#include "llvm/ADT/SmallVector.h"
18+
#include "llvm/ADT/StringExtras.h"
19+
#include "llvm/ADT/StringSet.h"
1620
#include "llvm/ADT/StringSwitch.h"
1721
#include "llvm/TargetParser/RISCVISAInfo.h"
1822

@@ -145,6 +149,101 @@ void getFeaturesForCPU(StringRef CPU,
145149
EnabledFeatures.push_back(F.substr(1));
146150
}
147151

152+
using RISCVImpliedTuneFeature = std::pair<const char *, const char *>;
153+
154+
#define GET_TUNE_FEATURES
155+
#define GET_IMPLIED_TUNE_FEATURES
156+
#include "llvm/TargetParser/RISCVTargetParserDef.inc"
157+
158+
void getAllTuneFeatures(SmallVectorImpl<StringRef> &Features) {
159+
Features.assign(std::begin(TuneFeatures), std::end(TuneFeatures));
160+
}
161+
162+
Error parseTuneFeatureString(StringRef TFString,
163+
SmallVectorImpl<std::string> &ResFeatures) {
164+
const StringSet<> AllTuneFeatureSet(llvm::from_range, TuneFeatures);
165+
using SmallStringSet = SmallSet<StringRef, 4>;
166+
167+
TFString = TFString.trim();
168+
// Note: StringSet is not really ergnomic to use in this case here.
169+
SmallStringSet PositiveFeatures;
170+
SmallStringSet NegativeFeatures;
171+
// Phase 1: Collect explicit features.
172+
StringRef FeatureStr;
173+
do {
174+
std::tie(FeatureStr, TFString) = TFString.split(",");
175+
if (AllTuneFeatureSet.count(FeatureStr)) {
176+
if (!PositiveFeatures.insert(FeatureStr).second)
177+
return createStringError(inconvertibleErrorCode(),
178+
"cannot specify more than one instance of '" +
179+
Twine(FeatureStr) + "'");
180+
} else if (FeatureStr.starts_with("no-")) {
181+
// Check if this is a negative feature, like `no-foo` for `foo`.
182+
StringRef ActualFeature = FeatureStr.drop_front(3);
183+
if (AllTuneFeatureSet.count(ActualFeature)) {
184+
if (!NegativeFeatures.insert(ActualFeature).second)
185+
return createStringError(
186+
inconvertibleErrorCode(),
187+
"cannot specify more than one instance of '" + Twine(FeatureStr) +
188+
"'");
189+
}
190+
} else {
191+
return createStringError(inconvertibleErrorCode(),
192+
"unrecognized tune feature directive '" +
193+
Twine(FeatureStr) + "'");
194+
}
195+
} while (!TFString.empty());
196+
197+
auto Intersection =
198+
llvm::set_intersection(PositiveFeatures, NegativeFeatures);
199+
if (!Intersection.empty()) {
200+
std::string IntersectedStr = join(Intersection, "', '");
201+
return createStringError(inconvertibleErrorCode(),
202+
"Feature(s) '" + Twine(IntersectedStr) +
203+
"' cannot appear in both "
204+
"positive and negative directives");
205+
}
206+
207+
// Phase 2: Derive implied features.
208+
StringMap<SmallVector<StringRef, 2>> ImpliedFeatureMap;
209+
StringMap<SmallVector<StringRef, 2>> InverseImpliedFeatureMap;
210+
for (auto [Feature, ImpliedFeature] : ImpliedTuneFeatures) {
211+
ImpliedFeatureMap[Feature].push_back(ImpliedFeature);
212+
InverseImpliedFeatureMap[ImpliedFeature].push_back(Feature);
213+
}
214+
215+
for (StringRef PF : PositiveFeatures) {
216+
auto ItFeatures = ImpliedFeatureMap.find(PF);
217+
if (ItFeatures != ImpliedFeatureMap.end())
218+
PositiveFeatures.insert(ItFeatures->second.begin(),
219+
ItFeatures->second.end());
220+
}
221+
for (StringRef NF : NegativeFeatures) {
222+
auto ItFeatures = InverseImpliedFeatureMap.find(NF);
223+
if (ItFeatures != InverseImpliedFeatureMap.end())
224+
NegativeFeatures.insert(ItFeatures->second.begin(),
225+
ItFeatures->second.end());
226+
}
227+
228+
Intersection = llvm::set_intersection(PositiveFeatures, NegativeFeatures);
229+
if (!Intersection.empty()) {
230+
std::string IntersectedStr = join(Intersection, "', '");
231+
return createStringError(inconvertibleErrorCode(),
232+
"Feature(s) '" + Twine(IntersectedStr) +
233+
"' were implied by both "
234+
"positive and negative directives");
235+
}
236+
237+
// Export the result.
238+
const std::string PosPrefix("+");
239+
const std::string NegPrefix("-");
240+
for (StringRef PF : PositiveFeatures)
241+
ResFeatures.emplace_back(PosPrefix + PF.str());
242+
for (StringRef NF : NegativeFeatures)
243+
ResFeatures.emplace_back(NegPrefix + NF.str());
244+
245+
return Error::success();
246+
}
148247
} // namespace RISCV
149248

150249
namespace RISCVVType {

llvm/unittests/TargetParser/RISCVTargetParserTest.cpp

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "llvm/TargetParser/RISCVTargetParser.h"
10+
#include "llvm/ADT/STLExtras.h"
11+
#include "llvm/ADT/SmallVector.h"
1012
#include "gtest/gtest.h"
1113

1214
using namespace llvm;
@@ -30,4 +32,90 @@ TEST(RISCVVType, CheckSameRatioLMUL) {
3032
EXPECT_EQ(RISCVVType::LMUL_F2,
3133
RISCVVType::getSameRatioLMUL(8, RISCVVType::LMUL_F4, 16));
3234
}
35+
36+
TEST(RISCVTuneFeature, AllTuneFeatures) {
37+
SmallVector<StringRef> AllTuneFeatures;
38+
RISCV::getAllTuneFeatures(AllTuneFeatures);
39+
EXPECT_EQ(AllTuneFeatures.size(), 28U);
40+
for (auto F : {"conditional-cmv-fusion",
41+
"dlen-factor-2",
42+
"disable-latency-sched-heuristic",
43+
"disable-misched-load-clustering",
44+
"disable-misched-store-clustering",
45+
"disable-postmisched-load-clustering",
46+
"disable-postmisched-store-clustering",
47+
"single-element-vec-fp64",
48+
"log-vrgather",
49+
"no-default-unroll",
50+
"no-sink-splat-operands",
51+
"optimized-nf2-segment-load-store",
52+
"optimized-nf3-segment-load-store",
53+
"optimized-nf4-segment-load-store",
54+
"optimized-nf5-segment-load-store",
55+
"optimized-nf6-segment-load-store",
56+
"optimized-nf7-segment-load-store",
57+
"optimized-nf8-segment-load-store",
58+
"optimized-zero-stride-load",
59+
"use-postra-scheduler",
60+
"predictable-select-expensive",
61+
"prefer-vsetvli-over-read-vlenb",
62+
"prefer-w-inst",
63+
"short-forward-branch-i-minmax",
64+
"short-forward-branch-i-mul",
65+
"short-forward-branch-opt",
66+
"vl-dependent-latency",
67+
"vxrm-pipeline-flush"})
68+
EXPECT_TRUE(is_contained(AllTuneFeatures, F));
69+
}
70+
71+
TEST(RISCVTuneFeature, LegalTuneFeatureStrings) {
72+
SmallVector<std::string> Result;
73+
EXPECT_FALSE(errorToBool(RISCV::parseTuneFeatureString(
74+
"log-vrgather,no-short-forward-branch-opt,vl-dependent-latency",
75+
Result)));
76+
EXPECT_TRUE(is_contained(Result, "+log-vrgather"));
77+
EXPECT_TRUE(is_contained(Result, "+vl-dependent-latency"));
78+
EXPECT_TRUE(is_contained(Result, "-short-forward-branch-opt"));
79+
EXPECT_TRUE(is_contained(Result, "-short-forward-branch-i-minmax"));
80+
EXPECT_TRUE(is_contained(Result, "-short-forward-branch-i-mul"));
81+
82+
Result.clear();
83+
EXPECT_FALSE(errorToBool(RISCV::parseTuneFeatureString(
84+
"no-short-forward-branch-i-mul,short-forward-branch-i-minmax", Result)));
85+
EXPECT_TRUE(is_contained(Result, "+short-forward-branch-i-minmax"));
86+
EXPECT_TRUE(is_contained(Result, "+short-forward-branch-opt"));
87+
EXPECT_TRUE(is_contained(Result, "-short-forward-branch-i-mul"));
88+
89+
Result.clear();
90+
EXPECT_FALSE(errorToBool(RISCV::parseTuneFeatureString(
91+
"no-no-default-unroll,no-sink-splat-operands", Result)));
92+
EXPECT_TRUE(is_contained(Result, "+no-sink-splat-operands"));
93+
EXPECT_TRUE(is_contained(Result, "-no-default-unroll"));
94+
}
95+
96+
TEST(RISCVTuneFeature, UnrecognizedTuneFeature) {
97+
SmallVector<std::string> Result;
98+
EXPECT_EQ(toString(RISCV::parseTuneFeatureString("32bit", Result)),
99+
"unrecognized tune feature directive '32bit'");
100+
}
101+
102+
TEST(RISCVTuneFeature, DuplicatedFeatures) {
103+
SmallVector<std::string> Result;
104+
EXPECT_EQ(toString(RISCV::parseTuneFeatureString("log-vrgather,log-vrgather",
105+
Result)),
106+
"cannot specify more than one instance of 'log-vrgather'");
107+
108+
EXPECT_EQ(toString(RISCV::parseTuneFeatureString(
109+
"log-vrgather,no-log-vrgather,short-forward-branch-i-mul,no-"
110+
"short-forward-branch-i-mul",
111+
Result)),
112+
"Feature(s) 'log-vrgather', 'short-forward-branch-i-mul' cannot "
113+
"appear in both positive and negative directives");
114+
115+
EXPECT_EQ(
116+
toString(RISCV::parseTuneFeatureString(
117+
"short-forward-branch-i-mul,no-short-forward-branch-opt", Result)),
118+
"Feature(s) 'short-forward-branch-i-mul', 'short-forward-branch-opt' "
119+
"were implied by both positive and negative directives");
120+
}
33121
} // namespace

0 commit comments

Comments
 (0)