aboutsummaryrefslogtreecommitdiff
path: root/clang/lib
diff options
context:
space:
mode:
authorPiyou Chen <piyou.chen@sifive.com>2024-09-13 18:04:53 +0800
committerGitHub <noreply@github.com>2024-09-13 18:04:53 +0800
commit9cd93774098c861c260090a690f428b7ae031c65 (patch)
tree4d85118ee70cab7ef772e1dc45fe27ad7f2ad109 /clang/lib
parent4ca8fb18129e6465c3594a8681f1cca0e2aff724 (diff)
downloadllvm-9cd93774098c861c260090a690f428b7ae031c65.zip
llvm-9cd93774098c861c260090a690f428b7ae031c65.tar.gz
llvm-9cd93774098c861c260090a690f428b7ae031c65.tar.bz2
[RISCV][FMV] Support target_clones (#85786)
This patch enable the function multiversion(FMV) and `target_clones` attribute for RISC-V target. The proposal of `target_clones` syntax can be found at the https://github.com/riscv-non-isa/riscv-c-api-doc/pull/48 (which has landed), as modified by the proposed https://github.com/riscv-non-isa/riscv-c-api-doc/pull/85 (which adds the priority syntax). It supports the `target_clones` function attribute and function multiversioning feature for RISC-V target. It will generate the ifunc resolver function for the function that declared with target_clones attribute. The resolver function will check the version support by runtime object `__riscv_feature_bits`. For example: ``` __attribute__((target_clones("default", "arch=+ver1", "arch=+ver2"))) int bar() { return 1; } ``` the corresponding resolver will be like: ``` bar.resolver() { __init_riscv_feature_bits(); // Check arch=+ver1 if ((__riscv_feature_bits.features[0] & BITMASK_OF_VERSION1) == BITMASK_OF_VERSION1) { return bar.arch=+ver1; } else { // Check arch=+ver2 if ((__riscv_feature_bits.features[0] & BITMASK_OF_VERSION2) == BITMASK_OF_VERSION2) { return bar.arch=+ver2; } else { // Default return bar.default; } } } ```
Diffstat (limited to 'clang/lib')
-rw-r--r--clang/lib/AST/ASTContext.cpp12
-rw-r--r--clang/lib/Basic/Targets/RISCV.cpp2
-rw-r--r--clang/lib/CodeGen/CodeGenFunction.cpp134
-rw-r--r--clang/lib/CodeGen/CodeGenFunction.h3
-rw-r--r--clang/lib/CodeGen/CodeGenModule.cpp5
-rw-r--r--clang/lib/CodeGen/Targets/RISCV.cpp44
-rw-r--r--clang/lib/Sema/SemaDeclAttr.cpp49
-rw-r--r--clang/lib/Sema/SemaRISCV.cpp11
8 files changed, 258 insertions, 2 deletions
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 8ece39a..7cc69ca 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -14248,6 +14248,18 @@ void ASTContext::getFunctionFeatureMap(llvm::StringMap<bool> &FeatureMap,
Target->getTargetOpts().FeaturesAsWritten.begin(),
Target->getTargetOpts().FeaturesAsWritten.end());
Target->initFeatureMap(FeatureMap, getDiagnostics(), TargetCPU, Features);
+ } else if (Target->getTriple().isRISCV()) {
+ StringRef VersionStr = TC->getFeatureStr(GD.getMultiVersionIndex());
+ std::vector<std::string> Features;
+ if (VersionStr != "default") {
+ ParsedTargetAttr ParsedAttr = Target->parseTargetAttr(VersionStr);
+ Features.insert(Features.begin(), ParsedAttr.Features.begin(),
+ ParsedAttr.Features.end());
+ }
+ Features.insert(Features.begin(),
+ Target->getTargetOpts().FeaturesAsWritten.begin(),
+ Target->getTargetOpts().FeaturesAsWritten.end());
+ Target->initFeatureMap(FeatureMap, getDiagnostics(), TargetCPU, Features);
} else {
std::vector<std::string> Features;
StringRef VersionStr = TC->getFeatureStr(GD.getMultiVersionIndex());
diff --git a/clang/lib/Basic/Targets/RISCV.cpp b/clang/lib/Basic/Targets/RISCV.cpp
index 223ac66..a4925e8 100644
--- a/clang/lib/Basic/Targets/RISCV.cpp
+++ b/clang/lib/Basic/Targets/RISCV.cpp
@@ -459,6 +459,8 @@ ParsedTargetAttr RISCVTargetInfo::parseTargetAttr(StringRef Features) const {
Ret.Duplicate = "tune=";
Ret.Tune = AttrString;
+ } else if (Feature.starts_with("priority")) {
+ // Skip because it only use for FMV.
}
}
return Ret;
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index fae26d1..eda96f3 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -2889,10 +2889,142 @@ void CodeGenFunction::EmitMultiVersionResolver(
case llvm::Triple::aarch64:
EmitAArch64MultiVersionResolver(Resolver, Options);
return;
+ case llvm::Triple::riscv32:
+ case llvm::Triple::riscv64:
+ EmitRISCVMultiVersionResolver(Resolver, Options);
+ return;
default:
- assert(false && "Only implemented for x86 and AArch64 targets");
+ assert(false && "Only implemented for x86, AArch64 and RISC-V targets");
+ }
+}
+
+static int getPriorityFromAttrString(StringRef AttrStr) {
+ SmallVector<StringRef, 8> Attrs;
+
+ AttrStr.split(Attrs, ';');
+
+ // Default Priority is zero.
+ int Priority = 0;
+ for (auto Attr : Attrs) {
+ if (Attr.consume_front("priority=")) {
+ int Result;
+ if (!Attr.getAsInteger(0, Result)) {
+ Priority = Result;
+ }
+ }
+ }
+
+ return Priority;
+}
+
+void CodeGenFunction::EmitRISCVMultiVersionResolver(
+ llvm::Function *Resolver, ArrayRef<MultiVersionResolverOption> Options) {
+
+ if (getContext().getTargetInfo().getTriple().getOS() !=
+ llvm::Triple::OSType::Linux) {
+ CGM.getDiags().Report(diag::err_os_unsupport_riscv_fmv);
+ return;
}
+
+ llvm::BasicBlock *CurBlock = createBasicBlock("resolver_entry", Resolver);
+ Builder.SetInsertPoint(CurBlock);
+ EmitRISCVCpuInit();
+
+ bool SupportsIFunc = getContext().getTargetInfo().supportsIFunc();
+ bool HasDefault = false;
+ unsigned DefaultIndex = 0;
+
+ SmallVector<CodeGenFunction::MultiVersionResolverOption, 10> CurrOptions(
+ Options);
+
+ llvm::stable_sort(
+ CurrOptions, [](const CodeGenFunction::MultiVersionResolverOption &LHS,
+ const CodeGenFunction::MultiVersionResolverOption &RHS) {
+ return getPriorityFromAttrString(LHS.Conditions.Features[0]) >
+ getPriorityFromAttrString(RHS.Conditions.Features[0]);
+ });
+
+ // Check the each candidate function.
+ for (unsigned Index = 0; Index < CurrOptions.size(); Index++) {
+
+ if (CurrOptions[Index].Conditions.Features[0].starts_with("default")) {
+ HasDefault = true;
+ DefaultIndex = Index;
+ continue;
+ }
+
+ Builder.SetInsertPoint(CurBlock);
+
+ std::vector<std::string> TargetAttrFeats =
+ getContext()
+ .getTargetInfo()
+ .parseTargetAttr(CurrOptions[Index].Conditions.Features[0])
+ .Features;
+
+ if (TargetAttrFeats.empty())
+ continue;
+
+ // FeaturesCondition: The bitmask of the required extension has been
+ // enabled by the runtime object.
+ // (__riscv_feature_bits.features[i] & REQUIRED_BITMASK) ==
+ // REQUIRED_BITMASK
+ //
+ // When condition is met, return this version of the function.
+ // Otherwise, try the next version.
+ //
+ // if (FeaturesConditionVersion1)
+ // return Version1;
+ // else if (FeaturesConditionVersion2)
+ // return Version2;
+ // else if (FeaturesConditionVersion3)
+ // return Version3;
+ // ...
+ // else
+ // return DefaultVersion;
+
+ // TODO: Add a condition to check the length before accessing elements.
+ // Without checking the length first, we may access an incorrect memory
+ // address when using different versions.
+ llvm::SmallVector<StringRef, 8> CurrTargetAttrFeats;
+
+ for (auto &Feat : TargetAttrFeats) {
+ StringRef CurrFeat = Feat;
+ if (CurrFeat.starts_with('+'))
+ CurrTargetAttrFeats.push_back(CurrFeat.substr(1));
+ }
+
+ Builder.SetInsertPoint(CurBlock);
+ llvm::Value *FeatsCondition = EmitRISCVCpuSupports(CurrTargetAttrFeats);
+
+ llvm::BasicBlock *RetBlock = createBasicBlock("resolver_return", Resolver);
+ CGBuilderTy RetBuilder(*this, RetBlock);
+ CreateMultiVersionResolverReturn(
+ CGM, Resolver, RetBuilder, CurrOptions[Index].Function, SupportsIFunc);
+ llvm::BasicBlock *ElseBlock = createBasicBlock("resolver_else", Resolver);
+
+ Builder.SetInsertPoint(CurBlock);
+ Builder.CreateCondBr(FeatsCondition, RetBlock, ElseBlock);
+
+ CurBlock = ElseBlock;
+ }
+
+ // Finally, emit the default one.
+ if (HasDefault) {
+ Builder.SetInsertPoint(CurBlock);
+ CreateMultiVersionResolverReturn(CGM, Resolver, Builder,
+ CurrOptions[DefaultIndex].Function,
+ SupportsIFunc);
+ return;
+ }
+
+ // If no generic/default, emit an unreachable.
+ Builder.SetInsertPoint(CurBlock);
+ llvm::CallInst *TrapCall = EmitTrapCall(llvm::Intrinsic::trap);
+ TrapCall->setDoesNotReturn();
+ TrapCall->setDoesNotThrow();
+ Builder.CreateUnreachable();
+ Builder.ClearInsertionPoint();
}
void CodeGenFunction::EmitAArch64MultiVersionResolver(
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 4eca770..9f868c9 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -5339,6 +5339,9 @@ public:
void
EmitAArch64MultiVersionResolver(llvm::Function *Resolver,
ArrayRef<MultiVersionResolverOption> Options);
+ void
+ EmitRISCVMultiVersionResolver(llvm::Function *Resolver,
+ ArrayRef<MultiVersionResolverOption> Options);
private:
QualType getVarArgType(const Expr *Arg);
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 50fa486..ba2d658 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -4283,7 +4283,10 @@ void CodeGenModule::emitMultiVersionFunctions() {
Feats.clear();
if (getTarget().getTriple().isAArch64())
TC->getFeatures(Feats, I);
- else {
+ else if (getTarget().getTriple().isRISCV()) {
+ StringRef Version = TC->getFeatureStr(I);
+ Feats.push_back(Version);
+ } else {
StringRef Version = TC->getFeatureStr(I);
if (Version.starts_with("arch="))
Architecture = Version.drop_front(sizeof("arch=") - 1);
diff --git a/clang/lib/CodeGen/Targets/RISCV.cpp b/clang/lib/CodeGen/Targets/RISCV.cpp
index 57b09f1..fd72fe6 100644
--- a/clang/lib/CodeGen/Targets/RISCV.cpp
+++ b/clang/lib/CodeGen/Targets/RISCV.cpp
@@ -63,9 +63,53 @@ public:
CharUnits Field2Off) const;
ABIArgInfo coerceVLSVector(QualType Ty) const;
+
+ using ABIInfo::appendAttributeMangling;
+ void appendAttributeMangling(TargetClonesAttr *Attr, unsigned Index,
+ raw_ostream &Out) const override;
+ void appendAttributeMangling(StringRef AttrStr,
+ raw_ostream &Out) const override;
};
} // end anonymous namespace
+void RISCVABIInfo::appendAttributeMangling(TargetClonesAttr *Attr,
+ unsigned Index,
+ raw_ostream &Out) const {
+ appendAttributeMangling(Attr->getFeatureStr(Index), Out);
+}
+
+void RISCVABIInfo::appendAttributeMangling(StringRef AttrStr,
+ raw_ostream &Out) const {
+ if (AttrStr == "default") {
+ Out << ".default";
+ return;
+ }
+
+ Out << '.';
+
+ SmallVector<StringRef, 8> Attrs;
+ AttrStr.split(Attrs, ';');
+
+ // Only consider the arch string.
+ StringRef ArchStr;
+ for (auto &Attr : Attrs) {
+ if (Attr.starts_with("arch="))
+ ArchStr = Attr;
+ }
+
+ // Extract features string.
+ SmallVector<StringRef, 8> Features;
+ ArchStr.consume_front("arch=");
+ ArchStr.split(Features, ',');
+
+ llvm::stable_sort(Features);
+
+ for (auto Feat : Features) {
+ Feat.consume_front("+");
+ Out << "_" << Feat;
+ }
+}
+
void RISCVABIInfo::computeInfo(CGFunctionInfo &FI) const {
QualType RetTy = FI.getReturnType();
if (!getCXXABI().classifyReturnType(FI))
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index fb2c2fb..14cc51c 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -3161,6 +3161,55 @@ bool Sema::checkTargetClonesAttrString(
HasNotDefault = true;
}
}
+ } else if (TInfo.getTriple().isRISCV()) {
+ // Suppress warn_target_clone_mixed_values
+ HasCommas = false;
+
+ // Cur is split's parts of Str. RISC-V uses Str directly,
+ // so skip when encountered more than once.
+ if (!Str.starts_with(Cur))
+ continue;
+
+ llvm::SmallVector<StringRef, 8> AttrStrs;
+ Str.split(AttrStrs, ";");
+
+ bool IsPriority = false;
+ bool IsDefault = false;
+ for (auto &AttrStr : AttrStrs) {
+ // Only support arch=+ext,... syntax.
+ if (AttrStr.starts_with("arch=+")) {
+ ParsedTargetAttr TargetAttr =
+ Context.getTargetInfo().parseTargetAttr(AttrStr);
+
+ if (TargetAttr.Features.empty() ||
+ llvm::any_of(TargetAttr.Features, [&](const StringRef Ext) {
+ return !RISCV().isValidFMVExtension(Ext);
+ }))
+ return Diag(CurLoc, diag::warn_unsupported_target_attribute)
+ << Unsupported << None << Str << TargetClones;
+ } else if (AttrStr.starts_with("default")) {
+ IsDefault = true;
+ DefaultIsDupe = HasDefault;
+ HasDefault = true;
+ } else if (AttrStr.consume_front("priority=")) {
+ IsPriority = true;
+ int Digit;
+ if (AttrStr.getAsInteger(0, Digit))
+ return Diag(CurLoc, diag::warn_unsupported_target_attribute)
+ << Unsupported << None << Str << TargetClones;
+ } else {
+ return Diag(CurLoc, diag::warn_unsupported_target_attribute)
+ << Unsupported << None << Str << TargetClones;
+ }
+ }
+
+ if (IsPriority && IsDefault)
+ return Diag(CurLoc, diag::warn_unsupported_target_attribute)
+ << Unsupported << None << Str << TargetClones;
+
+ if (llvm::is_contained(StringsBuffer, Str) || DefaultIsDupe)
+ Diag(CurLoc, diag::warn_target_clone_duplicate_options);
+ StringsBuffer.push_back(Str);
} else {
// Other targets ( currently X86 )
if (Cur.starts_with("arch=")) {
diff --git a/clang/lib/Sema/SemaRISCV.cpp b/clang/lib/Sema/SemaRISCV.cpp
index 56d6f12..3da4b51 100644
--- a/clang/lib/Sema/SemaRISCV.cpp
+++ b/clang/lib/Sema/SemaRISCV.cpp
@@ -25,6 +25,7 @@
#include "clang/Sema/Sema.h"
#include "clang/Support/RISCVVIntrinsicUtils.h"
#include "llvm/ADT/SmallVector.h"
+#include "llvm/TargetParser/RISCVISAInfo.h"
#include "llvm/TargetParser/RISCVTargetParser.h"
#include <optional>
#include <string>
@@ -1492,6 +1493,16 @@ bool SemaRISCV::isAliasValid(unsigned BuiltinID, StringRef AliasName) {
BuiltinID <= RISCV::LastRVVBuiltin;
}
+bool SemaRISCV::isValidFMVExtension(StringRef Ext) {
+ if (Ext.empty())
+ return false;
+
+ if (!Ext.consume_front("+"))
+ return false;
+
+ return -1 != RISCVISAInfo::getRISCVFeaturesBitsInfo(Ext).second;
+}
+
SemaRISCV::SemaRISCV(Sema &S) : SemaBase(S) {}
} // namespace clang