Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
20 changes: 20 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 @@ -47,13 +48,32 @@ 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;

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
73 changes: 51 additions & 22 deletions llvm/lib/Target/RISCV/RISCVFeatures.td
Original file line number Diff line number Diff line change
Expand Up @@ -1740,6 +1740,13 @@ def HasVendorXSMTVDot
// LLVM specific features and extensions
//===----------------------------------------------------------------------===//

class RISCVTuneFeatureBase;
class RISCVSimpleTuneFeature : RISCVTuneFeatureBase;
class RISCVTuneFeature<string pos_directive, string neg_directive> : 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
Expand Down Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -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
Expand Down
158 changes: 158 additions & 0 deletions llvm/lib/TargetParser/RISCVTargetParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down Expand Up @@ -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<StringRef> PositiveMap;
// Negative directive name -> Feature name
StringMap<StringRef> NegativeMap;

StringMap<SmallVector<StringRef>> ImpliedFeatureMap;
StringMap<SmallVector<StringRef>> InvImpliedFeatureMap;

public:
static void getAllTuneFeatures(SmallVectorImpl<StringRef> &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<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 TFString,
SmallVectorImpl<std::string> &ResFeatures) {
using SmallStringSet = SmallSet<StringRef, 4>;
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<ParserError>(
"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<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();
else
return make_error<ParserWarning>(WarningMsg);
}
} // namespace RISCV

namespace RISCVVType {
Expand Down
Loading