Skip to content

Commit

Permalink
[RISCV][FMV] Support target_clones
Browse files Browse the repository at this point in the history
  • Loading branch information
BeMg committed Jun 28, 2024
1 parent 937fecd commit aaa7017
Show file tree
Hide file tree
Showing 17 changed files with 741 additions and 7 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 @@ -374,4 +374,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_target_clones : Error<
"target_clones 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 @@ -1481,7 +1481,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
10 changes: 10 additions & 0 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13744,6 +13744,16 @@ void ASTContext::getFunctionFeatureMap(llvm::StringMap<bool> &FeatureMap,
Features.insert(Features.begin(),
Target->getTargetOpts().FeaturesAsWritten.begin(),
Target->getTargetOpts().FeaturesAsWritten.end());
} else if (Target->getTriple().isRISCV()) {
StringRef VersionStr = TC->getFeatureStr(GD.getMultiVersionIndex());
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());
} else {
StringRef VersionStr = TC->getFeatureStr(GD.getMultiVersionIndex());
if (VersionStr.starts_with("arch="))
Expand Down
10 changes: 8 additions & 2 deletions clang/lib/Basic/Targets/RISCV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ bool RISCVTargetInfo::initFeatureMap(

// If a target attribute specified a full arch string, override all the ISA
// extension target features.
const auto I = llvm::find(FeaturesVec, "__RISCV_TargetAttrNeedOverride");
const auto I = llvm::find(FeaturesVec, "+__RISCV_TargetAttrNeedOverride");
if (I != FeaturesVec.end()) {
std::vector<std::string> OverrideFeatures(std::next(I), FeaturesVec.end());

Expand Down Expand Up @@ -367,6 +367,12 @@ bool RISCVTargetInfo::handleTargetFeatures(std::vector<std::string> &Features,
return true;
}

bool RISCVTargetInfo::isValidFeatureName(StringRef Feature) const {
if (Feature.starts_with("__RISCV_TargetAttrNeedOverride"))
return true;
return llvm::RISCVISAInfo::isSupportedExtensionFeature(Feature);
}

bool RISCVTargetInfo::isValidCPUName(StringRef Name) const {
bool Is64Bit = getTriple().isArch64Bit();
return llvm::RISCV::parseCPU(Name, Is64Bit);
Expand All @@ -391,7 +397,7 @@ void RISCVTargetInfo::fillValidTuneCPUList(

static void handleFullArchString(StringRef FullArchStr,
std::vector<std::string> &Features) {
Features.push_back("__RISCV_TargetAttrNeedOverride");
Features.push_back("+__RISCV_TargetAttrNeedOverride");
auto RII = llvm::RISCVISAInfo::parseArchString(
FullArchStr, /* EnableExperimentalExtension */ true);
if (llvm::errorToBool(RII.takeError())) {
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Basic/Targets/RISCV.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ class RISCVTargetInfo : public TargetInfo {
bool handleTargetFeatures(std::vector<std::string> &Features,
DiagnosticsEngine &Diags) override;

bool isValidFeatureName(StringRef Feature) const override;

bool hasBitIntType() const override { return true; }

bool hasBFloat16Type() const override { return true; }
Expand Down
78 changes: 78 additions & 0 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/TargetParser/AArch64TargetParser.h"
#include "llvm/TargetParser/RISCVTargetParser.h"
#include "llvm/TargetParser/X86TargetParser.h"
#include <optional>
#include <sstream>
Expand Down Expand Up @@ -14174,6 +14175,16 @@ Value *CodeGenFunction::EmitAArch64CpuInit() {
return Builder.CreateCall(Func);
}

Value *CodeGenFunction::EmitRISCVCpuInit() {
llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false);
llvm::FunctionCallee Func =
CGM.CreateRuntimeFunction(FTy, "__init_riscv_features_bit");
cast<llvm::GlobalValue>(Func.getCallee())->setDSOLocal(true);
cast<llvm::GlobalValue>(Func.getCallee())
->setDLLStorageClass(llvm::GlobalValue::DefaultStorageClass);
return Builder.CreateCall(Func);
}

Value *CodeGenFunction::EmitX86CpuInit() {
llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy,
/*Variadic*/ false);
Expand Down Expand Up @@ -14226,6 +14237,73 @@ CodeGenFunction::EmitAArch64CpuSupports(ArrayRef<StringRef> FeaturesStrs) {
return Result;
}

Value *CodeGenFunction::EmitRISCVCpuSupports(ArrayRef<StringRef> FeaturesStrs,
unsigned &MaxGroupIDUsed) {

const unsigned FeatureBitSize = 2;
llvm::ArrayType *ArrayOfInt64Ty =
llvm::ArrayType::get(Int64Ty, FeatureBitSize);
llvm::Type *StructTy = llvm::StructType::get(Int32Ty, ArrayOfInt64Ty);
llvm::Constant *RISCVFeaturesBits =
CGM.CreateRuntimeVariable(StructTy, "__riscv_feature_bits");
cast<llvm::GlobalValue>(RISCVFeaturesBits)->setDSOLocal(true);

auto LoadFeatureBit = [&](unsigned Index) {
// Create GEP then load.
Value *IndexVal = llvm::ConstantInt::get(Int32Ty, Index);
std::vector<llvm::Value *> GEPIndices = {llvm::ConstantInt::get(Int32Ty, 0),
llvm::ConstantInt::get(Int32Ty, 1),
IndexVal};
Value *Ptr =
Builder.CreateInBoundsGEP(StructTy, RISCVFeaturesBits, GEPIndices);
Value *FeaturesBit =
Builder.CreateAlignedLoad(Int64Ty, Ptr, CharUnits::fromQuantity(8));
return FeaturesBit;
};

SmallVector<unsigned long long> RequireFeatureBits =
llvm::RISCV::getRequireFeatureBitMask(FeaturesStrs);
Value *Result = Builder.getTrue();
for (unsigned i = 0; i < RequireFeatureBits.size(); i++) {
if (!RequireFeatureBits[i])
continue;
MaxGroupIDUsed = i;
Value *Mask = Builder.getInt64(RequireFeatureBits[i]);
Value *Bitset = Builder.CreateAnd(LoadFeatureBit(i), Mask);
Value *Cmp = Builder.CreateICmpEQ(Bitset, Mask);
Result = Builder.CreateAnd(Result, Cmp);
}

return Result;
}

Value *CodeGenFunction::EmitRISCVFeatureBitsLength(unsigned MaxGroupIDUsed) {

const unsigned FeatureBitSize = 2;
llvm::ArrayType *ArrayOfInt64Ty =
llvm::ArrayType::get(Int64Ty, FeatureBitSize);
llvm::Type *StructTy = llvm::StructType::get(Int32Ty, ArrayOfInt64Ty);
llvm::Constant *RISCVFeaturesBits =
CGM.CreateRuntimeVariable(StructTy, "__riscv_feature_bits");
cast<llvm::GlobalValue>(RISCVFeaturesBits)->setDSOLocal(true);

auto LoadMaxGroupID = [&]() {
std::vector<llvm::Value *> GEPIndices = {
llvm::ConstantInt::get(Int32Ty, 0), llvm::ConstantInt::get(Int32Ty, 0)};
llvm::Value *Ptr =
Builder.CreateInBoundsGEP(StructTy, RISCVFeaturesBits, GEPIndices);
Value *Length =
Builder.CreateAlignedLoad(Int64Ty, Ptr, CharUnits::fromQuantity(8));
return Length;
};

Value *UsedMaxGroupID = Builder.getInt64(MaxGroupIDUsed);
Value *GroupIDResult =
Builder.CreateICmpULT(UsedMaxGroupID, LoadMaxGroupID());

return GroupIDResult;
}

Value *CodeGenFunction::EmitX86BuiltinExpr(unsigned BuiltinID,
const CallExpr *E) {
if (BuiltinID == Builtin::BI__builtin_cpu_is)
Expand Down
105 changes: 104 additions & 1 deletion clang/lib/CodeGen/CodeGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2850,10 +2850,113 @@ 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");
}
}

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_target_clones);
return;
}

llvm::BasicBlock *CurBlock = createBasicBlock("resolver_entry", Resolver);
Builder.SetInsertPoint(CurBlock);
EmitRISCVCpuInit();

bool SupportsIFunc = getContext().getTargetInfo().supportsIFunc();
bool HasDefault = false;
int DefaultIndex = 0;
// Check the each candidate function.
for (unsigned Index = 0; Index < Options.size(); Index++) {

if (Options[Index].Conditions.Features[0].starts_with("default")) {
HasDefault = true;
DefaultIndex = Index;
continue;
}

Builder.SetInsertPoint(CurBlock);

std::vector<std::string> TargetAttrFeats =
getContext()
.getTargetInfo()
.parseTargetAttr(Options[Index].Conditions.Features[0])
.Features;

if (!TargetAttrFeats.empty()) {
// If this function doens't need override, then merge with module level
// target features. Otherwise, remain the current target features.
auto I = llvm::find(TargetAttrFeats, "+__RISCV_TargetAttrNeedOverride");
if (I == TargetAttrFeats.end())
TargetAttrFeats.insert(TargetAttrFeats.begin(),
Target.getTargetOpts().FeaturesAsWritten.begin(),
Target.getTargetOpts().FeaturesAsWritten.end());
else
TargetAttrFeats.erase(I);

// Only consider +<extension-feature>.
llvm::SmallVector<StringRef, 8> PlusTargetAttrFeats;
for (StringRef Feat : TargetAttrFeats) {
if (!getContext().getTargetInfo().isValidFeatureName(
Feat.substr(1).str()))
continue;
if (Feat.starts_with("+"))
PlusTargetAttrFeats.push_back(Feat.substr(1));
}

llvm::BasicBlock *SecondCond =
createBasicBlock("resovler_cond", Resolver);

Builder.SetInsertPoint(SecondCond);
unsigned MaxGroupIDUsed = 0;
llvm::Value *SecondCondition =
EmitRISCVCpuSupports(PlusTargetAttrFeats, MaxGroupIDUsed);

Builder.SetInsertPoint(CurBlock);
llvm::Value *FirstCondition = EmitRISCVFeatureBitsLength(MaxGroupIDUsed);

llvm::BasicBlock *RetBlock =
createBasicBlock("resolver_return", Resolver);
CGBuilderTy RetBuilder(*this, RetBlock);
CreateMultiVersionResolverReturn(CGM, Resolver, RetBuilder,
Options[Index].Function, SupportsIFunc);
llvm::BasicBlock *ElseBlock = createBasicBlock("resolver_else", Resolver);

Builder.SetInsertPoint(CurBlock);
Builder.CreateCondBr(FirstCondition, SecondCond, ElseBlock);

Builder.SetInsertPoint(SecondCond);
Builder.CreateCondBr(SecondCondition, RetBlock, ElseBlock);

CurBlock = ElseBlock;
}
}

// Finally, emit the default one.
if (HasDefault) {
Builder.SetInsertPoint(CurBlock);
CreateMultiVersionResolverReturn(
CGM, Resolver, Builder, Options[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
7 changes: 7 additions & 0 deletions clang/lib/CodeGen/CodeGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -5254,6 +5254,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 All @@ -5278,6 +5281,10 @@ class CodeGenFunction : public CodeGenTypeCache {
FormAArch64ResolverCondition(const MultiVersionResolverOption &RO);
llvm::Value *EmitAArch64CpuSupports(const CallExpr *E);
llvm::Value *EmitAArch64CpuSupports(ArrayRef<StringRef> FeatureStrs);
llvm::Value *EmitRISCVCpuInit();
llvm::Value *EmitRISCVCpuSupports(ArrayRef<StringRef> FeatureStrs,
unsigned &MaxGroupIDUsed);
llvm::Value *EmitRISCVFeatureBitsLength(unsigned MaxGroupIDUsed);
};

inline DominatingLLVMValue::saved_type
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 @@ -4233,7 +4233,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
23 changes: 23 additions & 0 deletions clang/lib/CodeGen/Targets/RISCV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,32 @@ 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 << '.';
Out << AttrStr;
}

void RISCVABIInfo::computeInfo(CGFunctionInfo &FI) const {
QualType RetTy = FI.getReturnType();
if (!getCXXABI().classifyReturnType(FI))
Expand Down
25 changes: 25 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3152,6 +3152,31 @@ bool Sema::checkTargetClonesAttrString(
/*IncludeLocallyStreaming=*/false))
return Diag(LiteralLoc,
diag::err_sme_streaming_cannot_be_multiversioned);
} else if (TInfo.getTriple().isRISCV()) {
// Suppress warn_target_clone_mixed_values
HasCommas = false;

if (Str.starts_with("arch=")) {
// parseTargetAttr will parse full version string,
// the following split Cur string is no longer interesting.
if ((!Cur.starts_with("arch=")))
continue;

ParsedTargetAttr TargetAttr =
Context.getTargetInfo().parseTargetAttr(Str);
if (TargetAttr.Features.empty())
return Diag(CurLoc, diag::warn_unsupported_target_attribute)
<< Unsupported << None << Str << TargetClones;
} else if (Str == "default") {
DefaultIsDupe = HasDefault;
HasDefault = true;
} else {
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
8 changes: 8 additions & 0 deletions clang/test/CodeGen/attr-target-clones-riscv-invaild.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: target_clones is currently only supported on Linux
__attribute__((target_clones("default", "arch=+c"))) int foo2(void) {
return 2;
}

int bar() { return foo1()+foo2(); }
Loading

0 comments on commit aaa7017

Please sign in to comment.