Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions llvm/include/llvm/TargetParser/RISCVTargetParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -54,6 +55,9 @@ 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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to hook this up to a clang option, eventually. Right now, clang --target=riscv32 -mtune=help just prints a list of known CPUs. This can be a follow-up.

We might also want something akin to clang's clang --target=riscv64 --print-enabled-extensions, which is focussed on architecture extensions rather than tuning parameters.

LLVM_ABI Error parseTuneFeatureString(
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);
Expand Down
48 changes: 26 additions & 22 deletions llvm/lib/Target/RISCV/RISCVFeatures.td
Original file line number Diff line number Diff line change
Expand Up @@ -1740,6 +1740,10 @@ def HasVendorXSMTVDot
// LLVM specific features and extensions
//===----------------------------------------------------------------------===//

class RISCVTuneFeature<string name, string field_name, string value,
string description, list<SubtargetFeature> implied = []>
: SubtargetFeature<name, field_name, value, description, implied>;

// Feature32Bit exists to mark CPUs that support RV32 to distinguish them from
// tuning CPU names.
def Feature32Bit
Expand Down Expand Up @@ -1788,46 +1792,46 @@ def FeatureUnalignedVectorMem
"loads and stores">;

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

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

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

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

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

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

def TuneDisableLatencySchedHeuristic
: SubtargetFeature<"disable-latency-sched-heuristic", "DisableLatencySchedHeuristic", "true",
: RISCVTuneFeature<"disable-latency-sched-heuristic", "DisableLatencySchedHeuristic", "true",
"Disable latency scheduling heuristic">;

def TunePredictableSelectIsExpensive
: SubtargetFeature<"predictable-select-expensive", "PredictableSelectIsExpensive", "true",
: RISCVTuneFeature<"predictable-select-expensive", "PredictableSelectIsExpensive", "true",
"Prefer likely predicted branches over selects">;

def TuneOptimizedZeroStrideLoad
: SubtargetFeature<"optimized-zero-stride-load", "HasOptimizedZeroStrideLoad",
: RISCVTuneFeature<"optimized-zero-stride-load", "HasOptimizedZeroStrideLoad",
"true", "Optimized (perform fewer memory operations)"
"zero-stride vector load">;

foreach nf = {2-8} in
def TuneOptimizedNF#nf#SegmentLoadStore :
SubtargetFeature<"optimized-nf"#nf#"-segment-load-store",
RISCVTuneFeature<"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">;

def TuneVLDependentLatency
: SubtargetFeature<"vl-dependent-latency", "HasVLDependentLatency", "true",
: RISCVTuneFeature<"vl-dependent-latency", "HasVLDependentLatency", "true",
"Latency of vector instructions is dependent on the "
"dynamic value of vl">;

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

def TuneNoDefaultUnroll
: SubtargetFeature<"no-default-unroll", "EnableDefaultUnroll", "false",
: RISCVTuneFeature<"no-default-unroll", "EnableDefaultUnroll", "false",
"Disable default unroll preference.">;

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

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

def TuneShortForwardBranchIMul
: SubtargetFeature<"short-forward-branch-i-mul", "HasShortForwardBranchIMul",
: RISCVTuneFeature<"short-forward-branch-i-mul", "HasShortForwardBranchIMul",
"true", "Enable short forward branch optimization for mul instruction",
[TuneShortForwardBranchOpt]>;

// 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",
: RISCVTuneFeature<"no-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",
: RISCVTuneFeature<"prefer-w-inst", "PreferWInst", "true",
"Prefer instructions with W suffix">;

def TuneConditionalCompressedMoveFusion
: SubtargetFeature<"conditional-cmv-fusion", "HasConditionalCompressedMoveFusion",
: RISCVTuneFeature<"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",
: RISCVTuneFeature<"single-element-vec-fp64", "HasSingleElementVectorFP64", "true",
"Certain vector FP64 operations produce a single result "
"element per cycle">;

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

def TuneVXRMPipelineFlush : SubtargetFeature<"vxrm-pipeline-flush", "HasVXRMPipelineFlush",
def TuneVXRMPipelineFlush : RISCVTuneFeature<"vxrm-pipeline-flush", "HasVXRMPipelineFlush",
"true", "VXRM writes causes pipeline flush">;

def TunePreferVsetvliOverReadVLENB
: SubtargetFeature<"prefer-vsetvli-over-read-vlenb",
: RISCVTuneFeature<"prefer-vsetvli-over-read-vlenb",
"PreferVsetvliOverReadVLENB",
"true",
"Prefer vsetvli over read vlenb CSR to calculate VLEN">;
Expand Down
99 changes: 99 additions & 0 deletions llvm/lib/TargetParser/RISCVTargetParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@
//===----------------------------------------------------------------------===//

#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/StringSet.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/TargetParser/RISCVISAInfo.h"

Expand Down Expand Up @@ -145,6 +149,101 @@ void getFeaturesForCPU(StringRef CPU,
EnabledFeatures.push_back(F.substr(1));
}

using RISCVImpliedTuneFeature = std::pair<const char *, const char *>;

#define GET_TUNE_FEATURES
#define GET_IMPLIED_TUNE_FEATURES
#include "llvm/TargetParser/RISCVTargetParserDef.inc"

void getAllTuneFeatures(SmallVectorImpl<StringRef> &Features) {
Features.assign(std::begin(TuneFeatures), std::end(TuneFeatures));
}

Error parseTuneFeatureString(StringRef TFString,
SmallVectorImpl<std::string> &ResFeatures) {
const StringSet<> AllTuneFeatureSet(llvm::from_range, TuneFeatures);
using SmallStringSet = SmallSet<StringRef, 4>;

TFString = TFString.trim();
// Note: StringSet is not really ergnomic to use in this case here.
SmallStringSet PositiveFeatures;
SmallStringSet NegativeFeatures;
// Phase 1: Collect explicit features.
StringRef FeatureStr;
do {
std::tie(FeatureStr, TFString) = TFString.split(",");
if (AllTuneFeatureSet.count(FeatureStr)) {
if (!PositiveFeatures.insert(FeatureStr).second)
return createStringError(inconvertibleErrorCode(),
"cannot specify more than one instance of '" +
Twine(FeatureStr) + "'");
} else if (FeatureStr.starts_with("no-")) {
// Check if this is a negative feature, like `no-foo` for `foo`.
StringRef ActualFeature = FeatureStr.drop_front(3);
if (AllTuneFeatureSet.count(ActualFeature)) {
if (!NegativeFeatures.insert(ActualFeature).second)
return createStringError(
inconvertibleErrorCode(),
"cannot specify more than one instance of '" + Twine(FeatureStr) +
"'");
}
} else {
return createStringError(inconvertibleErrorCode(),
"unrecognized tune feature directive '" +
Twine(FeatureStr) + "'");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I incline emit warning and then ignore, so that we have better compatibility between different versions and compilers.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I decided to not printing out the warning message right away but instead creating a custom llvm::Error to represent warning messages. So that the users (e.g. Clang) can print the messages however they want.

}
} while (!TFString.empty());

auto Intersection =
llvm::set_intersection(PositiveFeatures, NegativeFeatures);
if (!Intersection.empty()) {
std::string IntersectedStr = join(Intersection, "', '");
return createStringError(inconvertibleErrorCode(),
"Feature(s) '" + Twine(IntersectedStr) +
"' cannot appear in both "
"positive and negative directives");
}

// Phase 2: Derive implied features.
StringMap<SmallVector<StringRef, 2>> ImpliedFeatureMap;
StringMap<SmallVector<StringRef, 2>> InverseImpliedFeatureMap;
for (auto [Feature, ImpliedFeature] : ImpliedTuneFeatures) {
ImpliedFeatureMap[Feature].push_back(ImpliedFeature);
InverseImpliedFeatureMap[ImpliedFeature].push_back(Feature);
}

for (StringRef PF : PositiveFeatures) {
auto ItFeatures = ImpliedFeatureMap.find(PF);
if (ItFeatures != ImpliedFeatureMap.end())
PositiveFeatures.insert(ItFeatures->second.begin(),
ItFeatures->second.end());
}
for (StringRef NF : NegativeFeatures) {
auto ItFeatures = InverseImpliedFeatureMap.find(NF);
if (ItFeatures != InverseImpliedFeatureMap.end())
NegativeFeatures.insert(ItFeatures->second.begin(),
ItFeatures->second.end());
}

Intersection = llvm::set_intersection(PositiveFeatures, NegativeFeatures);
if (!Intersection.empty()) {
std::string IntersectedStr = join(Intersection, "', '");
return createStringError(inconvertibleErrorCode(),
"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());

return Error::success();
}
} // namespace RISCV

namespace RISCVVType {
Expand Down
88 changes: 88 additions & 0 deletions llvm/unittests/TargetParser/RISCVTargetParserTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -30,4 +32,90 @@ TEST(RISCVVType, CheckSameRatioLMUL) {
EXPECT_EQ(RISCVVType::LMUL_F2,
RISCVVType::getSameRatioLMUL(8, RISCVVType::LMUL_F4, 16));
}

TEST(RISCVTuneFeature, AllTuneFeatures) {
SmallVector<StringRef> AllTuneFeatures;
RISCV::getAllTuneFeatures(AllTuneFeatures);
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<std::string> 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();
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();
EXPECT_FALSE(errorToBool(RISCV::parseTuneFeatureString(
"no-no-default-unroll,no-sink-splat-operands", Result)));
EXPECT_TRUE(is_contained(Result, "+no-sink-splat-operands"));
EXPECT_TRUE(is_contained(Result, "-no-default-unroll"));
}

TEST(RISCVTuneFeature, UnrecognizedTuneFeature) {
SmallVector<std::string> Result;
EXPECT_EQ(toString(RISCV::parseTuneFeatureString("32bit", Result)),
"unrecognized tune feature directive '32bit'");
}

TEST(RISCVTuneFeature, DuplicatedFeatures) {
SmallVector<std::string> 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");

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
Loading
Loading