Skip to content

Commit ddf2c58

Browse files
committed
[WIP][RISCV] Support __builtin_cpu_init and __builtin_cpu_supports
This implements the __builtin_cpu_init and __builtin_cpu_supports builtin routines based on the compiler runtime changes in llvm#85790. This is inspired by llvm#85786. Major changes are a) a restriction in scope to only the builtins (which have a much narrower user interface), and the avoidance of false generality. This change deliberately only handles group 0 extensions (which happen to be all defined ones today), and avoids the tblgen changes from that review. This is still a WIP. It is posted for initial feedback on whether this makes sense to try to get into 19.x release. Major items left undone: * Updating clang tests to exercise this logic. * Actually running it at all. I did not build compiler-rt, and thus all my checking was of generated asm/IR. * Investigate claims from gcc docs that __builtin_cpu_init is called early in process lifetime with high priority constructor. I did not find this with some quick searching.
1 parent 5e8cd29 commit ddf2c58

File tree

6 files changed

+133
-0
lines changed

6 files changed

+133
-0
lines changed

clang/lib/Basic/Targets/RISCV.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,3 +479,9 @@ RISCVTargetInfo::checkCallingConvention(CallingConv CC) const {
479479
return CCCR_OK;
480480
}
481481
}
482+
483+
bool RISCVTargetInfo::validateCpuSupports(StringRef Feature) const {
484+
// Only allow extensions we have a known bit position for in the
485+
// __riscv_feature_bits structure.
486+
return -1 != llvm::RISCVISAInfo::getRISCVFeaturesBitPosition(Feature);
487+
}

clang/lib/Basic/Targets/RISCV.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ class RISCVTargetInfo : public TargetInfo {
126126
std::pair<unsigned, unsigned> hardwareInterferenceSizes() const override {
127127
return std::make_pair(32, 32);
128128
}
129+
130+
bool supportsCpuSupports() const override { return getTriple().isOSLinux(); }
131+
bool supportsCpuInit() const override { return getTriple().isOSLinux(); }
132+
bool validateCpuSupports(StringRef Feature) const override;
129133
};
130134
class LLVM_LIBRARY_VISIBILITY RISCV32TargetInfo : public RISCVTargetInfo {
131135
public:

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@
6262
#include "llvm/Support/MathExtras.h"
6363
#include "llvm/Support/ScopedPrinter.h"
6464
#include "llvm/TargetParser/AArch64TargetParser.h"
65+
#include "llvm/TargetParser/RISCVISAInfo.h"
66+
#include "llvm/TargetParser/RISCVTargetParser.h"
6567
#include "llvm/TargetParser/X86TargetParser.h"
6668
#include <optional>
6769
#include <sstream>
@@ -14215,6 +14217,16 @@ Value *CodeGenFunction::EmitAArch64CpuInit() {
1421514217
return Builder.CreateCall(Func);
1421614218
}
1421714219

14220+
Value *CodeGenFunction::EmitRISCVCpuInit() {
14221+
llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false);
14222+
llvm::FunctionCallee Func =
14223+
CGM.CreateRuntimeFunction(FTy, "__init_riscv_feature_bits");
14224+
auto *CalleeGV = cast<llvm::GlobalValue>(Func.getCallee());
14225+
CalleeGV->setDSOLocal(true);
14226+
CalleeGV->setDLLStorageClass(llvm::GlobalValue::DefaultStorageClass);
14227+
return Builder.CreateCall(Func);
14228+
}
14229+
1421814230
Value *CodeGenFunction::EmitX86CpuInit() {
1421914231
llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy,
1422014232
/*Variadic*/ false);
@@ -14267,6 +14279,43 @@ CodeGenFunction::EmitAArch64CpuSupports(ArrayRef<StringRef> FeaturesStrs) {
1426714279
return Result;
1426814280
}
1426914281

14282+
Value *CodeGenFunction::EmitRISCVCpuSupports(const CallExpr *E) {
14283+
14284+
const Expr *FeatureExpr = E->getArg(0)->IgnoreParenCasts();
14285+
StringRef FeatureStr = cast<StringLiteral>(FeatureExpr)->getString();
14286+
if (!getContext().getTargetInfo().validateCpuSupports(FeatureStr))
14287+
return Builder.getFalse();
14288+
14289+
// Note: We are making an unchecked assumption that the size of the
14290+
// feature array is >= 1. This holds for any version of compiler-rt
14291+
// which defines this interface.
14292+
llvm::ArrayType *ArrayOfInt64Ty =
14293+
llvm::ArrayType::get(Int64Ty, 1);
14294+
llvm::Type *StructTy = llvm::StructType::get(Int32Ty, ArrayOfInt64Ty);
14295+
llvm::Constant *RISCVFeaturesBits =
14296+
CGM.CreateRuntimeVariable(StructTy, "__riscv_feature_bits");
14297+
auto *GV = cast<llvm::GlobalValue>(RISCVFeaturesBits);
14298+
GV->setDSOLocal(true);
14299+
14300+
auto LoadFeatureBit = [&](unsigned Index) {
14301+
// Create GEP then load.
14302+
Value *IndexVal = llvm::ConstantInt::get(Int32Ty, Index);
14303+
llvm::Value *GEPIndices[] = {Builder.getInt32(0), Builder.getInt32(1),
14304+
IndexVal};
14305+
Value *Ptr =
14306+
Builder.CreateInBoundsGEP(StructTy, RISCVFeaturesBits, GEPIndices);
14307+
Value *FeaturesBit =
14308+
Builder.CreateAlignedLoad(Int64Ty, Ptr, CharUnits::fromQuantity(8));
14309+
return FeaturesBit;
14310+
};
14311+
14312+
int BitPos = RISCVISAInfo::getRISCVFeaturesBitPosition(FeatureStr);
14313+
assert(BitPos != -1 && "validation should have rejected this feature");
14314+
Value *MaskV = Builder.getInt64(1ULL << BitPos);
14315+
Value *Bitset = Builder.CreateAnd(LoadFeatureBit(0), MaskV);
14316+
return Builder.CreateICmpEQ(Bitset, MaskV);
14317+
}
14318+
1427014319
Value *CodeGenFunction::EmitX86BuiltinExpr(unsigned BuiltinID,
1427114320
const CallExpr *E) {
1427214321
if (BuiltinID == Builtin::BI__builtin_cpu_is)
@@ -21728,6 +21777,12 @@ Value *CodeGenFunction::EmitHexagonBuiltinExpr(unsigned BuiltinID,
2172821777
Value *CodeGenFunction::EmitRISCVBuiltinExpr(unsigned BuiltinID,
2172921778
const CallExpr *E,
2173021779
ReturnValueSlot ReturnValue) {
21780+
21781+
if (BuiltinID == Builtin::BI__builtin_cpu_supports)
21782+
return EmitRISCVCpuSupports(E);
21783+
if (BuiltinID == Builtin::BI__builtin_cpu_init)
21784+
return EmitRISCVCpuInit();
21785+
2173121786
SmallVector<Value *, 4> Ops;
2173221787
llvm::Type *ResultType = ConvertType(E->getType());
2173321788

clang/lib/CodeGen/CodeGenFunction.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4695,6 +4695,9 @@ class CodeGenFunction : public CodeGenTypeCache {
46954695
llvm::Value *EmitRISCVBuiltinExpr(unsigned BuiltinID, const CallExpr *E,
46964696
ReturnValueSlot ReturnValue);
46974697

4698+
llvm::Value *EmitRISCVCpuSupports(const CallExpr *E);
4699+
llvm::Value *EmitRISCVCpuInit();
4700+
46984701
void AddAMDGPUFenceAddressSpaceMMRA(llvm::Instruction *Inst,
46994702
const CallExpr *E);
47004703
void ProcessOrderScopeAMDGCN(llvm::Value *Order, llvm::Value *Scope,

llvm/include/llvm/TargetParser/RISCVISAInfo.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ class RISCVISAInfo {
8080
std::set<StringRef> &EnabledFeatureNames,
8181
StringMap<StringRef> &DescMap);
8282

83+
/// Return the bit position (in group 0) of __riscv_feature_bits. Returns
84+
/// -1 if not supported.
85+
static int getRISCVFeaturesBitPosition(StringRef Ext);
86+
8387
private:
8488
RISCVISAInfo(unsigned XLen) : XLen(XLen) {}
8589

llvm/lib/TargetParser/RISCVISAInfo.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,3 +1020,64 @@ std::string RISCVISAInfo::getTargetFeatureForExtension(StringRef Ext) {
10201020
return isExperimentalExtension(Name) ? "experimental-" + Name.str()
10211021
: Name.str();
10221022
}
1023+
1024+
struct RISCVExtBit {
1025+
const StringRef ext;
1026+
uint64_t bitpos;
1027+
};
1028+
1029+
/// Maps extensions with assigned bit positions within group 0 of
1030+
/// __riscv_features_bits to their respective bit position. At the
1031+
/// moment all extensions are within group 0.
1032+
static RISCVExtBit RISCVGroup0BitPositions[] = {
1033+
{"a", 0},
1034+
{"c", 2},
1035+
{"d", 3},
1036+
{"f", 5},
1037+
{"i", 8},
1038+
{"m", 12},
1039+
{"v", 21},
1040+
{"zacas", 26},
1041+
{"zba", 27},
1042+
{"zbb", 28},
1043+
{"zbc", 29},
1044+
{"zbkb", 30},
1045+
{"zbkc", 31},
1046+
{"zbkx", 32},
1047+
{"zbs", 33},
1048+
{"zfa", 34},
1049+
{"zfh", 35},
1050+
{"zfhmin", 36},
1051+
{"zicboz", 37},
1052+
{"zicond", 38},
1053+
{"zihintntl", 39},
1054+
{"zihintpause", 40},
1055+
{"zknd", 41},
1056+
{"zkne", 42},
1057+
{"zknh", 43},
1058+
{"zksed", 44},
1059+
{"zksh", 45},
1060+
{"zkt", 46},
1061+
{"ztso", 47},
1062+
{"zvbb", 48},
1063+
{"zvbc", 49},
1064+
{"zvfh", 50},
1065+
{"zvfhmin", 51},
1066+
{"zvkb", 52},
1067+
{"zvkg", 53},
1068+
{"zvkned", 54},
1069+
{"zvknha", 55},
1070+
{"zvknhb", 56},
1071+
{"zvksed", 57},
1072+
{"zvksh", 58},
1073+
{"zvkt", 59}
1074+
};
1075+
int RISCVISAInfo::getRISCVFeaturesBitPosition(StringRef Ext) {
1076+
// Note that this code currently accepts mixed case extension names, but
1077+
// does not handle extension versions at all. That's probably fine because
1078+
// there's only one extension version in the __riscv_feature_bits vector.
1079+
for (auto E : RISCVGroup0BitPositions)
1080+
if (E.ext.equals_insensitive(Ext))
1081+
return E.bitpos;
1082+
return -1;
1083+
}

0 commit comments

Comments
 (0)