Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RISCV][FMV] Support target_clones #85786

Merged
merged 18 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -14181,6 +14181,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 @@ -464,6 +464,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 @@ -2877,10 +2877,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 @@ -5326,6 +5326,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 @@ -4281,7 +4281,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 @@ -3138,6 +3138,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