Skip to content

Commit 9cd9377

Browse files
authored
[RISCV][FMV] Support target_clones (llvm#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 riscv-non-isa/riscv-c-api-doc#48 (which has landed), as modified by the proposed riscv-non-isa/riscv-c-api-doc#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; } } } ```
1 parent 4ca8fb1 commit 9cd9377

15 files changed

+1200
-3
lines changed

clang/include/clang/Basic/DiagnosticFrontendKinds.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,4 +378,8 @@ def warn_missing_symbol_graph_dir : Warning<
378378
def err_ast_action_on_llvm_ir : Error<
379379
"cannot apply AST actions to LLVM IR file '%0'">,
380380
DefaultFatal;
381+
382+
def err_os_unsupport_riscv_fmv : Error<
383+
"function multiversioning is currently only supported on Linux">;
384+
381385
}

clang/include/clang/Basic/TargetInfo.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1496,7 +1496,8 @@ class TargetInfo : public TransferrableTargetInfo,
14961496
/// Identify whether this target supports multiversioning of functions,
14971497
/// which requires support for cpu_supports and cpu_is functionality.
14981498
bool supportsMultiVersioning() const {
1499-
return getTriple().isX86() || getTriple().isAArch64();
1499+
return getTriple().isX86() || getTriple().isAArch64() ||
1500+
getTriple().isRISCV();
15001501
}
15011502

15021503
/// Identify whether this target supports IFuncs.

clang/include/clang/Sema/SemaRISCV.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class SemaRISCV : public SemaBase {
4343

4444
void handleInterruptAttr(Decl *D, const ParsedAttr &AL);
4545
bool isAliasValid(unsigned BuiltinID, llvm::StringRef AliasName);
46+
bool isValidFMVExtension(StringRef Ext);
4647

4748
/// Indicate RISC-V vector builtin functions enabled or not.
4849
bool DeclareRVVBuiltins = false;

clang/lib/AST/ASTContext.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14248,6 +14248,18 @@ void ASTContext::getFunctionFeatureMap(llvm::StringMap<bool> &FeatureMap,
1424814248
Target->getTargetOpts().FeaturesAsWritten.begin(),
1424914249
Target->getTargetOpts().FeaturesAsWritten.end());
1425014250
Target->initFeatureMap(FeatureMap, getDiagnostics(), TargetCPU, Features);
14251+
} else if (Target->getTriple().isRISCV()) {
14252+
StringRef VersionStr = TC->getFeatureStr(GD.getMultiVersionIndex());
14253+
std::vector<std::string> Features;
14254+
if (VersionStr != "default") {
14255+
ParsedTargetAttr ParsedAttr = Target->parseTargetAttr(VersionStr);
14256+
Features.insert(Features.begin(), ParsedAttr.Features.begin(),
14257+
ParsedAttr.Features.end());
14258+
}
14259+
Features.insert(Features.begin(),
14260+
Target->getTargetOpts().FeaturesAsWritten.begin(),
14261+
Target->getTargetOpts().FeaturesAsWritten.end());
14262+
Target->initFeatureMap(FeatureMap, getDiagnostics(), TargetCPU, Features);
1425114263
} else {
1425214264
std::vector<std::string> Features;
1425314265
StringRef VersionStr = TC->getFeatureStr(GD.getMultiVersionIndex());

clang/lib/Basic/Targets/RISCV.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,8 @@ ParsedTargetAttr RISCVTargetInfo::parseTargetAttr(StringRef Features) const {
459459
Ret.Duplicate = "tune=";
460460

461461
Ret.Tune = AttrString;
462+
} else if (Feature.starts_with("priority")) {
463+
// Skip because it only use for FMV.
462464
}
463465
}
464466
return Ret;

clang/lib/CodeGen/CodeGenFunction.cpp

Lines changed: 133 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2889,10 +2889,142 @@ void CodeGenFunction::EmitMultiVersionResolver(
28892889
case llvm::Triple::aarch64:
28902890
EmitAArch64MultiVersionResolver(Resolver, Options);
28912891
return;
2892+
case llvm::Triple::riscv32:
2893+
case llvm::Triple::riscv64:
2894+
EmitRISCVMultiVersionResolver(Resolver, Options);
2895+
return;
28922896

28932897
default:
2894-
assert(false && "Only implemented for x86 and AArch64 targets");
2898+
assert(false && "Only implemented for x86, AArch64 and RISC-V targets");
2899+
}
2900+
}
2901+
2902+
static int getPriorityFromAttrString(StringRef AttrStr) {
2903+
SmallVector<StringRef, 8> Attrs;
2904+
2905+
AttrStr.split(Attrs, ';');
2906+
2907+
// Default Priority is zero.
2908+
int Priority = 0;
2909+
for (auto Attr : Attrs) {
2910+
if (Attr.consume_front("priority=")) {
2911+
int Result;
2912+
if (!Attr.getAsInteger(0, Result)) {
2913+
Priority = Result;
2914+
}
2915+
}
2916+
}
2917+
2918+
return Priority;
2919+
}
2920+
2921+
void CodeGenFunction::EmitRISCVMultiVersionResolver(
2922+
llvm::Function *Resolver, ArrayRef<MultiVersionResolverOption> Options) {
2923+
2924+
if (getContext().getTargetInfo().getTriple().getOS() !=
2925+
llvm::Triple::OSType::Linux) {
2926+
CGM.getDiags().Report(diag::err_os_unsupport_riscv_fmv);
2927+
return;
28952928
}
2929+
2930+
llvm::BasicBlock *CurBlock = createBasicBlock("resolver_entry", Resolver);
2931+
Builder.SetInsertPoint(CurBlock);
2932+
EmitRISCVCpuInit();
2933+
2934+
bool SupportsIFunc = getContext().getTargetInfo().supportsIFunc();
2935+
bool HasDefault = false;
2936+
unsigned DefaultIndex = 0;
2937+
2938+
SmallVector<CodeGenFunction::MultiVersionResolverOption, 10> CurrOptions(
2939+
Options);
2940+
2941+
llvm::stable_sort(
2942+
CurrOptions, [](const CodeGenFunction::MultiVersionResolverOption &LHS,
2943+
const CodeGenFunction::MultiVersionResolverOption &RHS) {
2944+
return getPriorityFromAttrString(LHS.Conditions.Features[0]) >
2945+
getPriorityFromAttrString(RHS.Conditions.Features[0]);
2946+
});
2947+
2948+
// Check the each candidate function.
2949+
for (unsigned Index = 0; Index < CurrOptions.size(); Index++) {
2950+
2951+
if (CurrOptions[Index].Conditions.Features[0].starts_with("default")) {
2952+
HasDefault = true;
2953+
DefaultIndex = Index;
2954+
continue;
2955+
}
2956+
2957+
Builder.SetInsertPoint(CurBlock);
2958+
2959+
std::vector<std::string> TargetAttrFeats =
2960+
getContext()
2961+
.getTargetInfo()
2962+
.parseTargetAttr(CurrOptions[Index].Conditions.Features[0])
2963+
.Features;
2964+
2965+
if (TargetAttrFeats.empty())
2966+
continue;
2967+
2968+
// FeaturesCondition: The bitmask of the required extension has been
2969+
// enabled by the runtime object.
2970+
// (__riscv_feature_bits.features[i] & REQUIRED_BITMASK) ==
2971+
// REQUIRED_BITMASK
2972+
//
2973+
// When condition is met, return this version of the function.
2974+
// Otherwise, try the next version.
2975+
//
2976+
// if (FeaturesConditionVersion1)
2977+
// return Version1;
2978+
// else if (FeaturesConditionVersion2)
2979+
// return Version2;
2980+
// else if (FeaturesConditionVersion3)
2981+
// return Version3;
2982+
// ...
2983+
// else
2984+
// return DefaultVersion;
2985+
2986+
// TODO: Add a condition to check the length before accessing elements.
2987+
// Without checking the length first, we may access an incorrect memory
2988+
// address when using different versions.
2989+
llvm::SmallVector<StringRef, 8> CurrTargetAttrFeats;
2990+
2991+
for (auto &Feat : TargetAttrFeats) {
2992+
StringRef CurrFeat = Feat;
2993+
if (CurrFeat.starts_with('+'))
2994+
CurrTargetAttrFeats.push_back(CurrFeat.substr(1));
2995+
}
2996+
2997+
Builder.SetInsertPoint(CurBlock);
2998+
llvm::Value *FeatsCondition = EmitRISCVCpuSupports(CurrTargetAttrFeats);
2999+
3000+
llvm::BasicBlock *RetBlock = createBasicBlock("resolver_return", Resolver);
3001+
CGBuilderTy RetBuilder(*this, RetBlock);
3002+
CreateMultiVersionResolverReturn(
3003+
CGM, Resolver, RetBuilder, CurrOptions[Index].Function, SupportsIFunc);
3004+
llvm::BasicBlock *ElseBlock = createBasicBlock("resolver_else", Resolver);
3005+
3006+
Builder.SetInsertPoint(CurBlock);
3007+
Builder.CreateCondBr(FeatsCondition, RetBlock, ElseBlock);
3008+
3009+
CurBlock = ElseBlock;
3010+
}
3011+
3012+
// Finally, emit the default one.
3013+
if (HasDefault) {
3014+
Builder.SetInsertPoint(CurBlock);
3015+
CreateMultiVersionResolverReturn(CGM, Resolver, Builder,
3016+
CurrOptions[DefaultIndex].Function,
3017+
SupportsIFunc);
3018+
return;
3019+
}
3020+
3021+
// If no generic/default, emit an unreachable.
3022+
Builder.SetInsertPoint(CurBlock);
3023+
llvm::CallInst *TrapCall = EmitTrapCall(llvm::Intrinsic::trap);
3024+
TrapCall->setDoesNotReturn();
3025+
TrapCall->setDoesNotThrow();
3026+
Builder.CreateUnreachable();
3027+
Builder.ClearInsertionPoint();
28963028
}
28973029

28983030
void CodeGenFunction::EmitAArch64MultiVersionResolver(

clang/lib/CodeGen/CodeGenFunction.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5339,6 +5339,9 @@ class CodeGenFunction : public CodeGenTypeCache {
53395339
void
53405340
EmitAArch64MultiVersionResolver(llvm::Function *Resolver,
53415341
ArrayRef<MultiVersionResolverOption> Options);
5342+
void
5343+
EmitRISCVMultiVersionResolver(llvm::Function *Resolver,
5344+
ArrayRef<MultiVersionResolverOption> Options);
53425345

53435346
private:
53445347
QualType getVarArgType(const Expr *Arg);

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4283,7 +4283,10 @@ void CodeGenModule::emitMultiVersionFunctions() {
42834283
Feats.clear();
42844284
if (getTarget().getTriple().isAArch64())
42854285
TC->getFeatures(Feats, I);
4286-
else {
4286+
else if (getTarget().getTriple().isRISCV()) {
4287+
StringRef Version = TC->getFeatureStr(I);
4288+
Feats.push_back(Version);
4289+
} else {
42874290
StringRef Version = TC->getFeatureStr(I);
42884291
if (Version.starts_with("arch="))
42894292
Architecture = Version.drop_front(sizeof("arch=") - 1);

clang/lib/CodeGen/Targets/RISCV.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,53 @@ class RISCVABIInfo : public DefaultABIInfo {
6363
CharUnits Field2Off) const;
6464

6565
ABIArgInfo coerceVLSVector(QualType Ty) const;
66+
67+
using ABIInfo::appendAttributeMangling;
68+
void appendAttributeMangling(TargetClonesAttr *Attr, unsigned Index,
69+
raw_ostream &Out) const override;
70+
void appendAttributeMangling(StringRef AttrStr,
71+
raw_ostream &Out) const override;
6672
};
6773
} // end anonymous namespace
6874

75+
void RISCVABIInfo::appendAttributeMangling(TargetClonesAttr *Attr,
76+
unsigned Index,
77+
raw_ostream &Out) const {
78+
appendAttributeMangling(Attr->getFeatureStr(Index), Out);
79+
}
80+
81+
void RISCVABIInfo::appendAttributeMangling(StringRef AttrStr,
82+
raw_ostream &Out) const {
83+
if (AttrStr == "default") {
84+
Out << ".default";
85+
return;
86+
}
87+
88+
Out << '.';
89+
90+
SmallVector<StringRef, 8> Attrs;
91+
AttrStr.split(Attrs, ';');
92+
93+
// Only consider the arch string.
94+
StringRef ArchStr;
95+
for (auto &Attr : Attrs) {
96+
if (Attr.starts_with("arch="))
97+
ArchStr = Attr;
98+
}
99+
100+
// Extract features string.
101+
SmallVector<StringRef, 8> Features;
102+
ArchStr.consume_front("arch=");
103+
ArchStr.split(Features, ',');
104+
105+
llvm::stable_sort(Features);
106+
107+
for (auto Feat : Features) {
108+
Feat.consume_front("+");
109+
Out << "_" << Feat;
110+
}
111+
}
112+
69113
void RISCVABIInfo::computeInfo(CGFunctionInfo &FI) const {
70114
QualType RetTy = FI.getReturnType();
71115
if (!getCXXABI().classifyReturnType(FI))

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3161,6 +3161,55 @@ bool Sema::checkTargetClonesAttrString(
31613161
HasNotDefault = true;
31623162
}
31633163
}
3164+
} else if (TInfo.getTriple().isRISCV()) {
3165+
// Suppress warn_target_clone_mixed_values
3166+
HasCommas = false;
3167+
3168+
// Cur is split's parts of Str. RISC-V uses Str directly,
3169+
// so skip when encountered more than once.
3170+
if (!Str.starts_with(Cur))
3171+
continue;
3172+
3173+
llvm::SmallVector<StringRef, 8> AttrStrs;
3174+
Str.split(AttrStrs, ";");
3175+
3176+
bool IsPriority = false;
3177+
bool IsDefault = false;
3178+
for (auto &AttrStr : AttrStrs) {
3179+
// Only support arch=+ext,... syntax.
3180+
if (AttrStr.starts_with("arch=+")) {
3181+
ParsedTargetAttr TargetAttr =
3182+
Context.getTargetInfo().parseTargetAttr(AttrStr);
3183+
3184+
if (TargetAttr.Features.empty() ||
3185+
llvm::any_of(TargetAttr.Features, [&](const StringRef Ext) {
3186+
return !RISCV().isValidFMVExtension(Ext);
3187+
}))
3188+
return Diag(CurLoc, diag::warn_unsupported_target_attribute)
3189+
<< Unsupported << None << Str << TargetClones;
3190+
} else if (AttrStr.starts_with("default")) {
3191+
IsDefault = true;
3192+
DefaultIsDupe = HasDefault;
3193+
HasDefault = true;
3194+
} else if (AttrStr.consume_front("priority=")) {
3195+
IsPriority = true;
3196+
int Digit;
3197+
if (AttrStr.getAsInteger(0, Digit))
3198+
return Diag(CurLoc, diag::warn_unsupported_target_attribute)
3199+
<< Unsupported << None << Str << TargetClones;
3200+
} else {
3201+
return Diag(CurLoc, diag::warn_unsupported_target_attribute)
3202+
<< Unsupported << None << Str << TargetClones;
3203+
}
3204+
}
3205+
3206+
if (IsPriority && IsDefault)
3207+
return Diag(CurLoc, diag::warn_unsupported_target_attribute)
3208+
<< Unsupported << None << Str << TargetClones;
3209+
3210+
if (llvm::is_contained(StringsBuffer, Str) || DefaultIsDupe)
3211+
Diag(CurLoc, diag::warn_target_clone_duplicate_options);
3212+
StringsBuffer.push_back(Str);
31643213
} else {
31653214
// Other targets ( currently X86 )
31663215
if (Cur.starts_with("arch=")) {

clang/lib/Sema/SemaRISCV.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "clang/Sema/Sema.h"
2626
#include "clang/Support/RISCVVIntrinsicUtils.h"
2727
#include "llvm/ADT/SmallVector.h"
28+
#include "llvm/TargetParser/RISCVISAInfo.h"
2829
#include "llvm/TargetParser/RISCVTargetParser.h"
2930
#include <optional>
3031
#include <string>
@@ -1492,6 +1493,16 @@ bool SemaRISCV::isAliasValid(unsigned BuiltinID, StringRef AliasName) {
14921493
BuiltinID <= RISCV::LastRVVBuiltin;
14931494
}
14941495

1496+
bool SemaRISCV::isValidFMVExtension(StringRef Ext) {
1497+
if (Ext.empty())
1498+
return false;
1499+
1500+
if (!Ext.consume_front("+"))
1501+
return false;
1502+
1503+
return -1 != RISCVISAInfo::getRISCVFeaturesBitsInfo(Ext).second;
1504+
}
1505+
14951506
SemaRISCV::SemaRISCV(Sema &S) : SemaBase(S) {}
14961507

14971508
} // namespace clang
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// RUN: not %clang_cc1 -triple riscv64 -target-feature +i -emit-llvm -o - %s 2>&1 | FileCheck %s --check-prefix=CHECK-UNSUPPORT-OS
2+
3+
// CHECK-UNSUPPORT-OS: error: function multiversioning is currently only supported on Linux
4+
__attribute__((target_clones("default", "arch=+c"))) int foo(void) {
5+
return 2;
6+
}
7+
8+
int bar() { return foo(); }

0 commit comments

Comments
 (0)