Skip to content

Commit

Permalink
[RISCV][FMV] Support target_clones (llvm#85786)
Browse files Browse the repository at this point in the history
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;
        }
    }
}
```
  • Loading branch information
BeMg authored Sep 13, 2024
1 parent 4ca8fb1 commit 9cd9377
Show file tree
Hide file tree
Showing 15 changed files with 1,200 additions and 3 deletions.
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/DiagnosticFrontendKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -378,4 +378,8 @@ def warn_missing_symbol_graph_dir : Warning<
def err_ast_action_on_llvm_ir : Error<
"cannot apply AST actions to LLVM IR file '%0'">,
DefaultFatal;

def err_os_unsupport_riscv_fmv : Error<
"function multiversioning is currently only supported on Linux">;

}
3 changes: 2 additions & 1 deletion clang/include/clang/Basic/TargetInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -1496,7 +1496,8 @@ class TargetInfo : public TransferrableTargetInfo,
/// Identify whether this target supports multiversioning of functions,
/// which requires support for cpu_supports and cpu_is functionality.
bool supportsMultiVersioning() const {
return getTriple().isX86() || getTriple().isAArch64();
return getTriple().isX86() || getTriple().isAArch64() ||
getTriple().isRISCV();
}

/// Identify whether this target supports IFuncs.
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Sema/SemaRISCV.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class SemaRISCV : public SemaBase {

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

/// Indicate RISC-V vector builtin functions enabled or not.
bool DeclareRVVBuiltins = false;
Expand Down
12 changes: 12 additions & 0 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Basic/Targets/RISCV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
134 changes: 133 additions & 1 deletion clang/lib/CodeGen/CodeGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CodeGen/CodeGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -5339,6 +5339,9 @@ class CodeGenFunction : public CodeGenTypeCache {
void
EmitAArch64MultiVersionResolver(llvm::Function *Resolver,
ArrayRef<MultiVersionResolverOption> Options);
void
EmitRISCVMultiVersionResolver(llvm::Function *Resolver,
ArrayRef<MultiVersionResolverOption> Options);

private:
QualType getVarArgType(const Expr *Arg);
Expand Down
5 changes: 4 additions & 1 deletion clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
44 changes: 44 additions & 0 deletions clang/lib/CodeGen/Targets/RISCV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,53 @@ class RISCVABIInfo : public DefaultABIInfo {
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))
Expand Down
49 changes: 49 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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=")) {
Expand Down
11 changes: 11 additions & 0 deletions clang/lib/Sema/SemaRISCV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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
8 changes: 8 additions & 0 deletions clang/test/CodeGen/attr-target-clones-riscv-invalid.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// RUN: not %clang_cc1 -triple riscv64 -target-feature +i -emit-llvm -o - %s 2>&1 | FileCheck %s --check-prefix=CHECK-UNSUPPORT-OS

// CHECK-UNSUPPORT-OS: error: function multiversioning is currently only supported on Linux
__attribute__((target_clones("default", "arch=+c"))) int foo(void) {
return 2;
}

int bar() { return foo(); }
Loading

0 comments on commit 9cd9377

Please sign in to comment.