diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -1409,7 +1409,7 @@ /// 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. diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -4886,6 +4886,10 @@ void EmitAArch64MultiVersionResolver(llvm::Function *Resolver, ArrayRef Options); + void + EmitRISCVMultiVersionResolver(llvm::Function *Resolver, + ArrayRef Options); + llvm::Value *EmitRISCVCheckFeatureFunc(std::string currFeats); private: QualType getVarArgType(const Expr *Arg); diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -2728,7 +2728,10 @@ 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"); } @@ -2820,6 +2823,86 @@ Builder.ClearInsertionPoint(); } +static std::string +mergeRISCVTargetFeature(const std::vector &Features) { + if (Features.empty()) + return std::string(); + + std::string Rst = ""; + for (unsigned i = 0; i < Features.size(); i++) { + Rst += Features[i].substr(1); // Remove prefix +/- + if (i != Features.size() - 1) + Rst += ";"; + } + return Rst; +} + +llvm::Value * +CodeGenFunction::EmitRISCVCheckFeatureFunc(std::string CurrFeatStr) { + llvm::Constant *FeatStr = Builder.CreateGlobalString(CurrFeatStr); + llvm::Type *Args[] = {Int8PtrTy}; + llvm::FunctionType *FTy = + llvm::FunctionType::get(Builder.getInt1Ty(), Args, /*Variadic*/ false); + llvm::FunctionCallee Func = + CGM.CreateRuntimeFunction(FTy, "__riscv_ifunc_select"); + cast(Func.getCallee())->setDSOLocal(true); + cast(Func.getCallee()) + ->setDLLStorageClass(llvm::GlobalValue::DefaultStorageClass); + return Builder.CreateCall(Func, FeatStr); +} + +void CodeGenFunction::EmitRISCVMultiVersionResolver( + llvm::Function *Resolver, ArrayRef Options) { + bool SupportsIFunc = getContext().getTargetInfo().supportsIFunc(); + + std::vector CurrOptions(Options); + + llvm::BasicBlock *CurBlock = createBasicBlock("resolver_entry", Resolver); + Builder.SetInsertPoint(CurBlock); + + bool HasDefault = false; + int DefaultIndex = 0; + // Check the each candidate function. + for (unsigned Index = 0; Index < CurrOptions.size(); Index++) { + Builder.SetInsertPoint(CurBlock); + + std::string CurrFeatStr = mergeRISCVTargetFeature( + getContext() + .getTargetInfo() + .parseTargetAttr(CurrOptions[Index].Conditions.Features[0]) + .Features); + + if (!CurrFeatStr.empty()) { + llvm::Value *Condition = EmitRISCVCheckFeatureFunc(CurrFeatStr); + llvm::BasicBlock *RetBlock = createBasicBlock("resolver_return", Resolver); + CGBuilderTy RetBuilder(*this, RetBlock); + CreateMultiVersionResolverReturn(CGM, Resolver, RetBuilder, CurrOptions[Index].Function, + SupportsIFunc); + CurBlock = createBasicBlock("resolver_else", Resolver); + Builder.CreateCondBr(Condition, RetBlock, CurBlock); + } else { + HasDefault = true; + DefaultIndex = Index; + } + } + + // 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(); +} + // Loc - where the diagnostic will point, where in the source code this // alignment has failed. // SecondaryLoc - if present (will be present if sufficiently different from diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -3992,7 +3992,10 @@ if (CurFD->getMultiVersionKind() == MultiVersionKind::Target) { const auto *TA = CurFD->getAttr(); llvm::SmallVector Feats; - TA->getAddedFeatures(Feats); + if (getTarget().getTriple().isRISCV()) + Feats.push_back(TA->getFeaturesStr()); + else + TA->getAddedFeatures(Feats); Options.emplace_back(cast(Func), TA->getArchitecture(), Feats); } else { diff --git a/clang/test/CodeGen/RISCV/riscv-fmv.c b/clang/test/CodeGen/RISCV/riscv-fmv.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/RISCV/riscv-fmv.c @@ -0,0 +1,106 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --include-generated-funcs --version 2 +// REQUIRES: riscv-registered-target +// RUN: %clang_cc1 -triple riscv64 -target-feature +zifencei \ +// RUN: -target-feature +m -target-feature +a \ +// RUN: -target-feature +f -target-feature +d \ +// RUN: -emit-llvm %s -o - | FileCheck %s \ +// RUN: --check-prefix=CHECK-IR + +__attribute__((target("arch=+v"))) void test1(void) {} +__attribute__((target("arch=+v,+zbb"))) void test1(void) {} +__attribute__((target("arch=+v,+zbb,+zicond1p0"))) void test1(void) {} +__attribute__((target("arch=rv64gc"))) void test1(void) {} +__attribute__((target("default"))) void test1(void) {} + +__attribute__((target("default"))) void test2(void) {} + +void test3() { test1(); test2(); } + +// CHECK-IR: $test1.resolver = comdat any +// CHECK-IR: $test2.resolver = comdat any + +// CHECK-IR: @0 = private unnamed_addr constant [2 x i8] c"v\00", align 1 +// CHECK-IR: @1 = private unnamed_addr constant [6 x i8] c"v;zbb\00", align 1 +// CHECK-IR: @2 = private unnamed_addr constant [26 x i8] c"v;zbb;experimental-zicond\00", align 1 +// CHECK-IR: @3 = private unnamed_addr constant [25 x i8] c"m;a;f;d;c;zicsr;zifencei\00", align 1 + + +// CHECK-IR-LABEL: define dso_local void @test1.v +// CHECK-IR-SAME: () #[[ATTR0:[0-9]+]] { +// CHECK-IR-NEXT: entry: +// CHECK-IR-NEXT: ret void +// +// +// CHECK-IR-LABEL: define dso_local void @test1.zbb_v +// CHECK-IR-SAME: () #[[ATTR1:[0-9]+]] { +// CHECK-IR-NEXT: entry: +// CHECK-IR-NEXT: ret void +// +// +// CHECK-IR-LABEL: define dso_local void @test1.experimental-zicond_zbb_v +// CHECK-IR-SAME: () #[[ATTR2:[0-9]+]] { +// CHECK-IR-NEXT: entry: +// CHECK-IR-NEXT: ret void +// +// +// CHECK-IR-LABEL: define dso_local void @test1.zifencei_zicsr_m_f_d_c_a +// CHECK-IR-SAME: () #[[ATTR3:[0-9]+]] { +// CHECK-IR-NEXT: entry: +// CHECK-IR-NEXT: ret void +// +// +// CHECK-IR-LABEL: define dso_local void @test1 +// CHECK-IR-SAME: () #[[ATTR4:[0-9]+]] { +// CHECK-IR-NEXT: entry: +// CHECK-IR-NEXT: ret void +// +// +// CHECK-IR-LABEL: define dso_local void @test2 +// CHECK-IR-SAME: () #[[ATTR4]] { +// CHECK-IR-NEXT: entry: +// CHECK-IR-NEXT: ret void +// +// +// CHECK-IR-LABEL: define dso_local void @test3 +// CHECK-IR-SAME: () #[[ATTR4]] { +// CHECK-IR-NEXT: entry: +// CHECK-IR-NEXT: call void @test1.resolver() +// CHECK-IR-NEXT: call void @test2.resolver() +// CHECK-IR-NEXT: ret void +// +// +// CHECK-IR-LABEL: define weak_odr void @test1.resolver() comdat { +// CHECK-IR-NEXT: resolver_entry: +// CHECK-IR-NEXT: [[TMP0:%.*]] = call i1 @__riscv_ifunc_select(ptr @[[GLOB0:[0-9]+]]) +// CHECK-IR-NEXT: br i1 [[TMP0]], label [[RESOLVER_RETURN:%.*]], label [[RESOLVER_ELSE:%.*]] +// CHECK-IR: resolver_return: +// CHECK-IR-NEXT: musttail call void @test1.v() +// CHECK-IR-NEXT: ret void +// CHECK-IR: resolver_else: +// CHECK-IR-NEXT: [[TMP1:%.*]] = call i1 @__riscv_ifunc_select(ptr @[[GLOB1:[0-9]+]]) +// CHECK-IR-NEXT: br i1 [[TMP1]], label [[RESOLVER_RETURN1:%.*]], label [[RESOLVER_ELSE2:%.*]] +// CHECK-IR: resolver_return1: +// CHECK-IR-NEXT: musttail call void @test1.zbb_v() +// CHECK-IR-NEXT: ret void +// CHECK-IR: resolver_else2: +// CHECK-IR-NEXT: [[TMP2:%.*]] = call i1 @__riscv_ifunc_select(ptr @[[GLOB2:[0-9]+]]) +// CHECK-IR-NEXT: br i1 [[TMP2]], label [[RESOLVER_RETURN3:%.*]], label [[RESOLVER_ELSE4:%.*]] +// CHECK-IR: resolver_return3: +// CHECK-IR-NEXT: musttail call void @test1.experimental-zicond_zbb_v() +// CHECK-IR-NEXT: ret void +// CHECK-IR: resolver_else4: +// CHECK-IR-NEXT: [[TMP3:%.*]] = call i1 @__riscv_ifunc_select(ptr @[[GLOB3:[0-9]+]]) +// CHECK-IR-NEXT: br i1 [[TMP3]], label [[RESOLVER_RETURN5:%.*]], label [[RESOLVER_ELSE6:%.*]] +// CHECK-IR: resolver_return5: +// CHECK-IR-NEXT: musttail call void @test1.zifencei_zicsr_m_f_d_c_a() +// CHECK-IR-NEXT: ret void +// CHECK-IR: resolver_else6: +// CHECK-IR-NEXT: musttail call void @test1() +// CHECK-IR-NEXT: ret void +// +// +// CHECK-IR-LABEL: define weak_odr void @test2.resolver() comdat { +// CHECK-IR-NEXT: resolver_entry: +// CHECK-IR-NEXT: musttail call void @test2() +// CHECK-IR-NEXT: ret void +//