diff options
author | Piyou Chen <piyou.chen@sifive.com> | 2024-09-13 18:04:53 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-13 18:04:53 +0800 |
commit | 9cd93774098c861c260090a690f428b7ae031c65 (patch) | |
tree | 4d85118ee70cab7ef772e1dc45fe27ad7f2ad109 /clang/lib | |
parent | 4ca8fb18129e6465c3594a8681f1cca0e2aff724 (diff) | |
download | llvm-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.cpp | 12 | ||||
-rw-r--r-- | clang/lib/Basic/Targets/RISCV.cpp | 2 | ||||
-rw-r--r-- | clang/lib/CodeGen/CodeGenFunction.cpp | 134 | ||||
-rw-r--r-- | clang/lib/CodeGen/CodeGenFunction.h | 3 | ||||
-rw-r--r-- | clang/lib/CodeGen/CodeGenModule.cpp | 5 | ||||
-rw-r--r-- | clang/lib/CodeGen/Targets/RISCV.cpp | 44 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclAttr.cpp | 49 | ||||
-rw-r--r-- | clang/lib/Sema/SemaRISCV.cpp | 11 |
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 |