[RISCV] Support target attribute for function
The proposal of target attribute is https://github.com/riscv-non-isa/riscv-c-api-doc/pull/35 This patch implements it by emitting .option arch during codegen. Reviewed By: craig.topper Differential Revision: https://reviews.llvm.org/D151730
This commit is contained in:
@@ -3177,6 +3177,10 @@ def warn_unsupported_target_attribute
|
||||
def err_attribute_unsupported
|
||||
: Error<"%0 attribute is not supported on targets missing %1;"
|
||||
" specify an appropriate -march= or -mcpu=">;
|
||||
def err_duplicate_target_attribute
|
||||
: Error<"%select{unsupported|duplicate|unknown}0%select{| CPU|"
|
||||
" tune CPU}1 '%2' in the '%select{target|target_clones|target_version}3' "
|
||||
"attribute string; ">;
|
||||
// The err_*_attribute_argument_not_int are separate because they're used by
|
||||
// VerifyIntegerConstantExpression.
|
||||
def err_aligned_attribute_argument_not_int : Error<
|
||||
|
||||
@@ -235,6 +235,47 @@ ArrayRef<Builtin::Info> RISCVTargetInfo::getTargetBuiltins() const {
|
||||
clang::RISCV::LastTSBuiltin - Builtin::FirstTSBuiltin);
|
||||
}
|
||||
|
||||
static std::vector<std::string>
|
||||
collectNonISAExtFeature(const std::vector<std::string> &FeaturesNeedOverride,
|
||||
int XLen) {
|
||||
auto ParseResult =
|
||||
llvm::RISCVISAInfo::parseFeatures(XLen, FeaturesNeedOverride);
|
||||
|
||||
if (!ParseResult) {
|
||||
consumeError(ParseResult.takeError());
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
|
||||
std::vector<std::string> ImpliedFeatures = (*ParseResult)->toFeatureVector();
|
||||
|
||||
std::vector<std::string> NonISAExtFeatureVec;
|
||||
|
||||
llvm::copy_if(FeaturesNeedOverride, std::back_inserter(NonISAExtFeatureVec),
|
||||
[&](const std::string &Feat) {
|
||||
return !llvm::is_contained(ImpliedFeatures, Feat);
|
||||
});
|
||||
|
||||
return NonISAExtFeatureVec;
|
||||
}
|
||||
|
||||
static std::vector<std::string>
|
||||
resolveTargetAttrOverride(const std::vector<std::string> &FeaturesVec,
|
||||
int XLen) {
|
||||
auto I = llvm::find(FeaturesVec, "__RISCV_TargetAttrNeedOverride");
|
||||
if (I == FeaturesVec.end())
|
||||
return FeaturesVec;
|
||||
|
||||
const std::vector<std::string> FeaturesNeedOverride(FeaturesVec.begin(), I);
|
||||
std::vector<std::string> NonISAExtFeature =
|
||||
collectNonISAExtFeature(FeaturesNeedOverride, XLen);
|
||||
|
||||
auto ResolvedFeature = std::vector(++I, FeaturesVec.end());
|
||||
ResolvedFeature.insert(ResolvedFeature.end(), NonISAExtFeature.begin(),
|
||||
NonISAExtFeature.end());
|
||||
|
||||
return ResolvedFeature;
|
||||
}
|
||||
|
||||
bool RISCVTargetInfo::initFeatureMap(
|
||||
llvm::StringMap<bool> &Features, DiagnosticsEngine &Diags, StringRef CPU,
|
||||
const std::vector<std::string> &FeaturesVec) const {
|
||||
@@ -248,7 +289,10 @@ bool RISCVTargetInfo::initFeatureMap(
|
||||
Features["32bit"] = true;
|
||||
}
|
||||
|
||||
auto ParseResult = llvm::RISCVISAInfo::parseFeatures(XLen, FeaturesVec);
|
||||
std::vector<std::string> NewFeaturesVec =
|
||||
resolveTargetAttrOverride(FeaturesVec, XLen);
|
||||
|
||||
auto ParseResult = llvm::RISCVISAInfo::parseFeatures(XLen, NewFeaturesVec);
|
||||
if (!ParseResult) {
|
||||
std::string Buffer;
|
||||
llvm::raw_string_ostream OutputErrMsg(Buffer);
|
||||
@@ -262,7 +306,7 @@ bool RISCVTargetInfo::initFeatureMap(
|
||||
// RISCVISAInfo makes implications for ISA features
|
||||
std::vector<std::string> ImpliedFeatures = (*ParseResult)->toFeatureVector();
|
||||
// Add non-ISA features like `relax` and `save-restore` back
|
||||
for (const std::string &Feature : FeaturesVec)
|
||||
for (const std::string &Feature : NewFeaturesVec)
|
||||
if (!llvm::is_contained(ImpliedFeatures, Feature))
|
||||
ImpliedFeatures.push_back(Feature);
|
||||
|
||||
@@ -359,3 +403,82 @@ void RISCVTargetInfo::fillValidTuneCPUList(
|
||||
bool Is64Bit = getTriple().isArch64Bit();
|
||||
llvm::RISCV::fillValidTuneCPUArchList(Values, Is64Bit);
|
||||
}
|
||||
|
||||
static void handleFullArchString(StringRef FullArchStr,
|
||||
std::vector<std::string> &Features) {
|
||||
Features.push_back("__RISCV_TargetAttrNeedOverride");
|
||||
auto RII = llvm::RISCVISAInfo::parseArchString(
|
||||
FullArchStr, /* EnableExperimentalExtension */ true);
|
||||
if (!RII) {
|
||||
consumeError(RII.takeError());
|
||||
// Forward the invalid FullArchStr.
|
||||
Features.push_back("+" + FullArchStr.str());
|
||||
} else {
|
||||
std::vector<std::string> FeatStrings = (*RII)->toFeatureVector();
|
||||
for (auto FeatString : FeatStrings)
|
||||
Features.push_back(FeatString);
|
||||
}
|
||||
}
|
||||
|
||||
ParsedTargetAttr RISCVTargetInfo::parseTargetAttr(StringRef Features) const {
|
||||
ParsedTargetAttr Ret;
|
||||
if (Features == "default")
|
||||
return Ret;
|
||||
SmallVector<StringRef, 1> AttrFeatures;
|
||||
Features.split(AttrFeatures, ";");
|
||||
bool FoundArch = false;
|
||||
|
||||
for (auto &Feature : AttrFeatures) {
|
||||
Feature = Feature.trim();
|
||||
StringRef AttrString = Feature.split("=").second.trim();
|
||||
|
||||
if (Feature.startswith("arch=")) {
|
||||
// Override last features
|
||||
Ret.Features.clear();
|
||||
if (FoundArch)
|
||||
Ret.Duplicate = "arch=";
|
||||
FoundArch = true;
|
||||
|
||||
if (AttrString.startswith("+")) {
|
||||
// EXTENSION like arch=+v,+zbb
|
||||
SmallVector<StringRef, 1> Exts;
|
||||
AttrString.split(Exts, ",");
|
||||
for (auto Ext : Exts) {
|
||||
if (Ext.empty())
|
||||
continue;
|
||||
|
||||
StringRef ExtName = Ext.substr(1);
|
||||
std::string TargetFeature =
|
||||
llvm::RISCVISAInfo::getTargetFeatureForExtension(ExtName);
|
||||
if (!TargetFeature.empty())
|
||||
Ret.Features.push_back(Ext.front() + TargetFeature);
|
||||
else
|
||||
Ret.Features.push_back(Ext.str());
|
||||
}
|
||||
} else {
|
||||
// full-arch-string like arch=rv64gcv
|
||||
handleFullArchString(AttrString, Ret.Features);
|
||||
}
|
||||
} else if (Feature.startswith("cpu=")) {
|
||||
if (!Ret.CPU.empty())
|
||||
Ret.Duplicate = "cpu=";
|
||||
|
||||
Ret.CPU = AttrString;
|
||||
|
||||
if (!FoundArch) {
|
||||
// Update Features with CPU's features
|
||||
StringRef MarchFromCPU = llvm::RISCV::getMArchFromMcpu(Ret.CPU);
|
||||
if (MarchFromCPU != "") {
|
||||
Ret.Features.clear();
|
||||
handleFullArchString(MarchFromCPU, Ret.Features);
|
||||
}
|
||||
}
|
||||
} else if (Feature.startswith("tune=")) {
|
||||
if (!Ret.Tune.empty())
|
||||
Ret.Duplicate = "tune=";
|
||||
|
||||
Ret.Tune = AttrString;
|
||||
}
|
||||
}
|
||||
return Ret;
|
||||
}
|
||||
|
||||
@@ -117,6 +117,8 @@ public:
|
||||
void fillValidCPUList(SmallVectorImpl<StringRef> &Values) const override;
|
||||
bool isValidTuneCPUName(StringRef Name) const override;
|
||||
void fillValidTuneCPUList(SmallVectorImpl<StringRef> &Values) const override;
|
||||
bool supportsTargetAttributeTune() const override { return true; }
|
||||
ParsedTargetAttr parseTargetAttr(StringRef Str) const override;
|
||||
};
|
||||
class LLVM_LIBRARY_VISIBILITY RISCV32TargetInfo : public RISCVTargetInfo {
|
||||
public:
|
||||
|
||||
@@ -3451,6 +3451,11 @@ bool Sema::checkTargetAttr(SourceLocation LiteralLoc, StringRef AttrStr) {
|
||||
return Diag(LiteralLoc, diag::warn_unsupported_target_attribute)
|
||||
<< Unknown << Tune << ParsedAttrs.Tune << Target;
|
||||
|
||||
if (Context.getTargetInfo().getTriple().isRISCV() &&
|
||||
ParsedAttrs.Duplicate != "")
|
||||
return Diag(LiteralLoc, diag::err_duplicate_target_attribute)
|
||||
<< Duplicate << None << ParsedAttrs.Duplicate << Target;
|
||||
|
||||
if (ParsedAttrs.Duplicate != "")
|
||||
return Diag(LiteralLoc, diag::warn_unsupported_target_attribute)
|
||||
<< Duplicate << None << ParsedAttrs.Duplicate << Target;
|
||||
|
||||
10
clang/test/CodeGen/RISCV/riscv-func-attr-target-err.c
Normal file
10
clang/test/CodeGen/RISCV/riscv-func-attr-target-err.c
Normal file
@@ -0,0 +1,10 @@
|
||||
// REQUIRES: riscv-registered-target
|
||||
// RUN: not %clang_cc1 -triple riscv64 -target-feature +zifencei -target-feature +m -target-feature +a \
|
||||
// RUN: -emit-llvm %s 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: error: duplicate 'arch=' in the 'target' attribute string;
|
||||
__attribute__((target("arch=rv64gc;arch=rv64gc_zbb"))) void testMultiArchSelectLast() {}
|
||||
// CHECK: error: duplicate 'cpu=' in the 'target' attribute string;
|
||||
__attribute__((target("cpu=sifive-u74;cpu=sifive-u54"))) void testMultiCpuSelectLast() {}
|
||||
// CHECK: error: duplicate 'tune=' in the 'target' attribute string;
|
||||
__attribute__((target("tune=sifive-u74;tune=sifive-u54"))) void testMultiTuneSelectLast() {}
|
||||
46
clang/test/CodeGen/RISCV/riscv-func-attr-target.c
Normal file
46
clang/test/CodeGen/RISCV/riscv-func-attr-target.c
Normal file
@@ -0,0 +1,46 @@
|
||||
// REQUIRES: riscv-registered-target
|
||||
// RUN: %clang_cc1 -triple riscv64 -target-feature +zifencei -target-feature +m \
|
||||
// RUN: -target-feature +a -target-feature +save-restore \
|
||||
// RUN: -emit-llvm %s -o - | FileCheck %s
|
||||
|
||||
// CHECK-LABEL: define dso_local void @testDefault
|
||||
// CHECK-SAME: () #0 {
|
||||
void testDefault() {}
|
||||
// CHECK-LABEL: define dso_local void @testMultiAttrStr
|
||||
// CHECK-SAME: () #1 {
|
||||
__attribute__((target("cpu=rocket-rv64;tune=generic-rv64;arch=+v"))) void
|
||||
testMultiAttrStr() {}
|
||||
// CHECK-LABEL: define dso_local void @testSingleExtension
|
||||
// CHECK-SAME: () #2 {
|
||||
__attribute__((target("arch=+zbb"))) void testSingleExtension() {}
|
||||
// CHECK-LABEL: define dso_local void @testMultiExtension
|
||||
// CHECK-SAME: () #3 {
|
||||
__attribute__((target("arch=+zbb,+v,+zicond"))) void testMultiExtension() {}
|
||||
// CHECK-LABEL: define dso_local void @testFullArch
|
||||
// CHECK-SAME: () #4 {
|
||||
__attribute__((target("arch=rv64gc_zbb"))) void testFullArch() {}
|
||||
// CHECK-LABEL: define dso_local void @testFullArchButSmallThanCmdArch
|
||||
// CHECK-SAME: () #5 {
|
||||
__attribute__((target("arch=rv64im"))) void testFullArchButSmallThanCmdArch() {}
|
||||
// CHECK-LABEL: define dso_local void @testAttrArchAndAttrCpu
|
||||
// CHECK-SAME: () #6 {
|
||||
__attribute__((target("cpu=sifive-u54;arch=+zbb"))) void
|
||||
testAttrArchAndAttrCpu() {}
|
||||
// CHECK-LABEL: define dso_local void @testAttrFullArchAndAttrCpu
|
||||
// CHECK-SAME: () #7 {
|
||||
__attribute__((target("cpu=sifive-u54;arch=rv64im"))) void
|
||||
testAttrFullArchAndAttrCpu() {}
|
||||
// CHECK-LABEL: define dso_local void @testAttrCpuOnly
|
||||
// CHECK-SAME: () #8 {
|
||||
__attribute__((target("cpu=sifive-u54"))) void testAttrCpuOnly() {}
|
||||
|
||||
//.
|
||||
// CHECK: attributes #0 = { {{.*}}"target-features"="+64bit,+a,+m,+save-restore,+zifencei" }
|
||||
// CHECK: attributes #1 = { {{.*}}"target-cpu"="rocket-rv64" "target-features"="+64bit,+a,+d,+f,+m,+save-restore,+v,+zicsr,+zifencei,+zve32f,+zve32x,+zve64d,+zve64f,+zve64x,+zvl128b,+zvl32b,+zvl64b" "tune-cpu"="generic-rv64" }
|
||||
// CHECK: attributes #2 = { {{.*}}"target-features"="+64bit,+a,+m,+save-restore,+zbb,+zifencei" }
|
||||
// CHECK: attributes #3 = { {{.*}}"target-features"="+64bit,+a,+d,+experimental-zicond,+f,+m,+save-restore,+v,+zbb,+zicsr,+zifencei,+zve32f,+zve32x,+zve64d,+zve64f,+zve64x,+zvl128b,+zvl32b,+zvl64b" }
|
||||
// CHECK: attributes #4 = { {{.*}}"target-features"="+64bit,+a,+c,+d,+f,+m,+save-restore,+zbb,+zicsr,+zifencei" }
|
||||
// CHECK: attributes #5 = { {{.*}}"target-features"="+64bit,+m,+save-restore" }
|
||||
// CHECK: attributes #6 = { {{.*}}"target-cpu"="sifive-u54" "target-features"="+64bit,+a,+m,+save-restore,+zbb,+zifencei" }
|
||||
// CHECK: attributes #7 = { {{.*}}"target-cpu"="sifive-u54" "target-features"="+64bit,+m,+save-restore" }
|
||||
// CHECK: attributes #8 = { {{.*}}"target-cpu"="sifive-u54" "target-features"="+64bit,+a,+c,+d,+f,+m,+save-restore,+zicsr,+zifencei" }
|
||||
Reference in New Issue
Block a user