Skip to content
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
6 changes: 6 additions & 0 deletions clang/lib/Basic/Targets/RISCV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -479,3 +479,9 @@ RISCVTargetInfo::checkCallingConvention(CallingConv CC) const {
return CCCR_OK;
}
}

bool RISCVTargetInfo::validateCpuSupports(StringRef Feature) const {
// Only allow extensions we have a known bit position for in the
// __riscv_feature_bits structure.
return -1 != llvm::RISCVISAInfo::getRISCVFeaturesBitPosition(Feature);
}
4 changes: 4 additions & 0 deletions clang/lib/Basic/Targets/RISCV.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ class RISCVTargetInfo : public TargetInfo {
std::pair<unsigned, unsigned> hardwareInterferenceSizes() const override {
return std::make_pair(32, 32);
}

bool supportsCpuSupports() const override { return getTriple().isOSLinux(); }
bool supportsCpuInit() const override { return getTriple().isOSLinux(); }
bool validateCpuSupports(StringRef Feature) const override;
};
class LLVM_LIBRARY_VISIBILITY RISCV32TargetInfo : public RISCVTargetInfo {
public:
Expand Down
53 changes: 53 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/RISCVISAInfo.h"
#include "llvm/TargetParser/X86TargetParser.h"
#include <optional>
#include <sstream>
Expand Down Expand Up @@ -14215,6 +14216,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_feature_bits");
auto *CalleeGV = cast<llvm::GlobalValue>(Func.getCallee());
CalleeGV->setDSOLocal(true);
CalleeGV->setDLLStorageClass(llvm::GlobalValue::DefaultStorageClass);
return Builder.CreateCall(Func);
}

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

Value *CodeGenFunction::EmitRISCVCpuSupports(const CallExpr *E) {

const Expr *FeatureExpr = E->getArg(0)->IgnoreParenCasts();
StringRef FeatureStr = cast<StringLiteral>(FeatureExpr)->getString();
if (!getContext().getTargetInfo().validateCpuSupports(FeatureStr))
return Builder.getFalse();

// Note: We are making an unchecked assumption that the size of the
// feature array is >= 1. This holds for any version of compiler-rt
// which defines this interface.
llvm::ArrayType *ArrayOfInt64Ty = llvm::ArrayType::get(Int64Ty, 1);
llvm::Type *StructTy = llvm::StructType::get(Int32Ty, ArrayOfInt64Ty);
llvm::Constant *RISCVFeaturesBits =
CGM.CreateRuntimeVariable(StructTy, "__riscv_feature_bits");
auto *GV = cast<llvm::GlobalValue>(RISCVFeaturesBits);
GV->setDSOLocal(true);

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

int BitPos = RISCVISAInfo::getRISCVFeaturesBitPosition(FeatureStr);
assert(BitPos != -1 && "validation should have rejected this feature");
Value *MaskV = Builder.getInt64(1ULL << BitPos);
Value *Bitset = Builder.CreateAnd(LoadFeatureBit(0), MaskV);
return Builder.CreateICmpEQ(Bitset, MaskV);
}

Value *CodeGenFunction::EmitX86BuiltinExpr(unsigned BuiltinID,
const CallExpr *E) {
if (BuiltinID == Builtin::BI__builtin_cpu_is)
Expand Down Expand Up @@ -21728,6 +21775,12 @@ Value *CodeGenFunction::EmitHexagonBuiltinExpr(unsigned BuiltinID,
Value *CodeGenFunction::EmitRISCVBuiltinExpr(unsigned BuiltinID,
const CallExpr *E,
ReturnValueSlot ReturnValue) {

if (BuiltinID == Builtin::BI__builtin_cpu_supports)
return EmitRISCVCpuSupports(E);
if (BuiltinID == Builtin::BI__builtin_cpu_init)
return EmitRISCVCpuInit();

SmallVector<Value *, 4> Ops;
llvm::Type *ResultType = ConvertType(E->getType());

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 @@ -4695,6 +4695,9 @@ class CodeGenFunction : public CodeGenTypeCache {
llvm::Value *EmitRISCVBuiltinExpr(unsigned BuiltinID, const CallExpr *E,
ReturnValueSlot ReturnValue);

llvm::Value *EmitRISCVCpuSupports(const CallExpr *E);
llvm::Value *EmitRISCVCpuInit();

void AddAMDGPUFenceAddressSpaceMMRA(llvm::Instruction *Inst,
const CallExpr *E);
void ProcessOrderScopeAMDGCN(llvm::Value *Order, llvm::Value *Scope,
Expand Down
110 changes: 105 additions & 5 deletions clang/test/CodeGen/builtin-cpu-supports.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
// RUN: FileCheck %s --check-prefix=CHECK-X86
// RUN: %clang_cc1 -triple ppc64le-linux-gnu -emit-llvm -o - %s | FileCheck %s \
// RUN: --check-prefix=CHECK-PPC

#ifndef __PPC__
// RUN: %clang_cc1 -triple riscv32-linux-gnu -emit-llvm -o - %s | FileCheck %s \
// RUN: --check-prefix=CHECK-RV32
// RUN: %clang_cc1 -triple riscv64-linux-gnu -emit-llvm -o - %s | FileCheck %s \
// RUN: --check-prefix=CHECK-RV64
#ifdef __x86_64__

// Test that we have the structure definition, the gep offsets, the name of the
// global, the bit grab, and the icmp correct.
Expand Down Expand Up @@ -101,8 +104,10 @@ int v3() { return __builtin_cpu_supports("x86-64-v3"); }
// CHECK-X86-NEXT: ret i32 [[CONV]]
//
int v4() { return __builtin_cpu_supports("x86-64-v4"); }
#else
// CHECK-PPC-LABEL: define dso_local signext i32 @test(
#endif

#ifdef __PPC__
// CHECK-PPC-LABEL: define dso_local signext i32 @test_ppc(
// CHECK-PPC-SAME: i32 noundef signext [[A:%.*]]) #[[ATTR0:[0-9]+]] {
// CHECK-PPC-NEXT: entry:
// CHECK-PPC-NEXT: [[RETVAL:%.*]] = alloca i32, align 4
Expand Down Expand Up @@ -149,7 +154,7 @@ int v4() { return __builtin_cpu_supports("x86-64-v4"); }
// CHECK-PPC-NEXT: [[TMP10:%.*]] = load i32, ptr [[RETVAL]], align 4
// CHECK-PPC-NEXT: ret i32 [[TMP10]]
//
int test(int a) {
int test_ppc(int a) {
if (__builtin_cpu_supports("arch_3_00")) // HWCAP2
return a;
else if (__builtin_cpu_supports("mmu")) // HWCAP
Expand All @@ -159,3 +164,98 @@ int test(int a) {
return a + 5;
}
#endif

#ifdef __riscv
// CHECK-RV32-LABEL: define dso_local i32 @test_riscv(
// CHECK-RV32-SAME: i32 noundef [[A:%.*]]) #[[ATTR0:[0-9]+]] {
// CHECK-RV32-NEXT: entry:
// CHECK-RV32-NEXT: [[RETVAL:%.*]] = alloca i32, align 4
// CHECK-RV32-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4
// CHECK-RV32-NEXT: store i32 [[A]], ptr [[A_ADDR]], align 4
// CHECK-RV32-NEXT: call void @__init_riscv_feature_bits()
// CHECK-RV32-NEXT: [[TMP0:%.*]] = load i64, ptr getelementptr inbounds ({ i32, [1 x i64] }, ptr @__riscv_feature_bits, i32 0, i32 1, i32 0), align 8
// CHECK-RV32-NEXT: [[TMP1:%.*]] = and i64 [[TMP0]], 1
// CHECK-RV32-NEXT: [[TMP2:%.*]] = icmp eq i64 [[TMP1]], 1
// CHECK-RV32-NEXT: br i1 [[TMP2]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
// CHECK-RV32: if.then:
// CHECK-RV32-NEXT: store i32 3, ptr [[RETVAL]], align 4
// CHECK-RV32-NEXT: br label [[RETURN:%.*]]
// CHECK-RV32: if.else:
// CHECK-RV32-NEXT: [[TMP3:%.*]] = load i64, ptr getelementptr inbounds ({ i32, [1 x i64] }, ptr @__riscv_feature_bits, i32 0, i32 1, i32 0), align 8
// CHECK-RV32-NEXT: [[TMP4:%.*]] = and i64 [[TMP3]], 4
// CHECK-RV32-NEXT: [[TMP5:%.*]] = icmp eq i64 [[TMP4]], 4
// CHECK-RV32-NEXT: br i1 [[TMP5]], label [[IF_THEN1:%.*]], label [[IF_ELSE2:%.*]]
// CHECK-RV32: if.then1:
// CHECK-RV32-NEXT: store i32 7, ptr [[RETVAL]], align 4
// CHECK-RV32-NEXT: br label [[RETURN]]
// CHECK-RV32: if.else2:
// CHECK-RV32-NEXT: [[TMP6:%.*]] = load i64, ptr getelementptr inbounds ({ i32, [1 x i64] }, ptr @__riscv_feature_bits, i32 0, i32 1, i32 0), align 8
// CHECK-RV32-NEXT: [[TMP7:%.*]] = and i64 [[TMP6]], 2097152
// CHECK-RV32-NEXT: [[TMP8:%.*]] = icmp eq i64 [[TMP7]], 2097152
// CHECK-RV32-NEXT: br i1 [[TMP8]], label [[IF_THEN3:%.*]], label [[IF_END:%.*]]
// CHECK-RV32: if.then3:
// CHECK-RV32-NEXT: store i32 11, ptr [[RETVAL]], align 4
// CHECK-RV32-NEXT: br label [[RETURN]]
// CHECK-RV32: if.end:
// CHECK-RV32-NEXT: br label [[IF_END4:%.*]]
// CHECK-RV32: if.end4:
// CHECK-RV32-NEXT: br label [[IF_END5:%.*]]
// CHECK-RV32: if.end5:
// CHECK-RV32-NEXT: store i32 0, ptr [[RETVAL]], align 4
// CHECK-RV32-NEXT: br label [[RETURN]]
// CHECK-RV32: return:
// CHECK-RV32-NEXT: [[TMP9:%.*]] = load i32, ptr [[RETVAL]], align 4
// CHECK-RV32-NEXT: ret i32 [[TMP9]]
//
// CHECK-RV64-LABEL: define dso_local signext i32 @test_riscv(
// CHECK-RV64-SAME: i32 noundef signext [[A:%.*]]) #[[ATTR0:[0-9]+]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[RETVAL:%.*]] = alloca i32, align 4
// CHECK-RV64-NEXT: [[A_ADDR:%.*]] = alloca i32, align 4
// CHECK-RV64-NEXT: store i32 [[A]], ptr [[A_ADDR]], align 4
// CHECK-RV64-NEXT: call void @__init_riscv_feature_bits()
// CHECK-RV64-NEXT: [[TMP0:%.*]] = load i64, ptr getelementptr inbounds ({ i32, [1 x i64] }, ptr @__riscv_feature_bits, i32 0, i32 1, i32 0), align 8
// CHECK-RV64-NEXT: [[TMP1:%.*]] = and i64 [[TMP0]], 1
// CHECK-RV64-NEXT: [[TMP2:%.*]] = icmp eq i64 [[TMP1]], 1
// CHECK-RV64-NEXT: br i1 [[TMP2]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
// CHECK-RV64: if.then:
// CHECK-RV64-NEXT: store i32 3, ptr [[RETVAL]], align 4
// CHECK-RV64-NEXT: br label [[RETURN:%.*]]
// CHECK-RV64: if.else:
// CHECK-RV64-NEXT: [[TMP3:%.*]] = load i64, ptr getelementptr inbounds ({ i32, [1 x i64] }, ptr @__riscv_feature_bits, i32 0, i32 1, i32 0), align 8
// CHECK-RV64-NEXT: [[TMP4:%.*]] = and i64 [[TMP3]], 4
// CHECK-RV64-NEXT: [[TMP5:%.*]] = icmp eq i64 [[TMP4]], 4
// CHECK-RV64-NEXT: br i1 [[TMP5]], label [[IF_THEN1:%.*]], label [[IF_ELSE2:%.*]]
// CHECK-RV64: if.then1:
// CHECK-RV64-NEXT: store i32 7, ptr [[RETVAL]], align 4
// CHECK-RV64-NEXT: br label [[RETURN]]
// CHECK-RV64: if.else2:
// CHECK-RV64-NEXT: [[TMP6:%.*]] = load i64, ptr getelementptr inbounds ({ i32, [1 x i64] }, ptr @__riscv_feature_bits, i32 0, i32 1, i32 0), align 8
// CHECK-RV64-NEXT: [[TMP7:%.*]] = and i64 [[TMP6]], 2097152
// CHECK-RV64-NEXT: [[TMP8:%.*]] = icmp eq i64 [[TMP7]], 2097152
// CHECK-RV64-NEXT: br i1 [[TMP8]], label [[IF_THEN3:%.*]], label [[IF_END:%.*]]
// CHECK-RV64: if.then3:
// CHECK-RV64-NEXT: store i32 11, ptr [[RETVAL]], align 4
// CHECK-RV64-NEXT: br label [[RETURN]]
// CHECK-RV64: if.end:
// CHECK-RV64-NEXT: br label [[IF_END4:%.*]]
// CHECK-RV64: if.end4:
// CHECK-RV64-NEXT: br label [[IF_END5:%.*]]
// CHECK-RV64: if.end5:
// CHECK-RV64-NEXT: store i32 0, ptr [[RETVAL]], align 4
// CHECK-RV64-NEXT: br label [[RETURN]]
// CHECK-RV64: return:
// CHECK-RV64-NEXT: [[TMP9:%.*]] = load i32, ptr [[RETVAL]], align 4
// CHECK-RV64-NEXT: ret i32 [[TMP9]]
//
int test_riscv(int a) {
__builtin_cpu_init();
if (__builtin_cpu_supports("a"))
return 3;
else if (__builtin_cpu_supports("c"))
return 7;
else if (__builtin_cpu_supports("v"))
return 11;
return 0;
}
#endif
19 changes: 17 additions & 2 deletions clang/test/Preprocessor/has_builtin_cpuid.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,29 @@
// RUN: %clang_cc1 -fsyntax-only -triple x86_64-- -DX86 -verify %s
// RUN: %clang_cc1 -fsyntax-only -triple powerpc64-unknown-linux-gnu -DPPC \
// RUN: -verify %s
// RUN: %clang_cc1 -fsyntax-only -triple riscv32-unknown-linux-gnu -DRISCV \
// RUN: -verify %s
// RUN: %clang_cc1 -fsyntax-only -triple riscv64-unknown-linux-gnu -DRISCV \
// RUN: -verify %s
// expected-no-diagnostics
#if __has_builtin(__builtin_cpu_is)
# ifdef ARM
# error "ARM shouldn't have __builtin_cpu_is"
# if defined(ARM) || defined(RISCV)
# error "ARM/RISCV shouldn't have __builtin_cpu_is"
# endif
#endif

#if __has_builtin(__builtin_cpu_init)
# if defined(ARM) || defined(PPC)
# error "ARM/PPC shouldn't have __builtin_cpu_init"
# endif
#else
# ifdef RISCV
# error "RISCV should have __builtin_cpu_init"
# endif
#endif

#if !__has_builtin(__builtin_cpu_supports)
# if defined(ARM) || defined(X86) || defined(RISCV)
# error "ARM/X86/RISCV should have __builtin_cpu_supports"
# endif
#endif
11 changes: 10 additions & 1 deletion clang/test/Sema/builtin-cpu-supports.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// RUN: %clang_cc1 -fsyntax-only -triple x86_64-pc-linux-gnu -verify %s
// RUN: %clang_cc1 -fsyntax-only -triple aarch64-linux-gnu -verify %s
// RUN: %clang_cc1 -fsyntax-only -triple riscv32-linux-gnu -verify %s
// RUN: %clang_cc1 -fsyntax-only -triple riscv64-linux-gnu -verify %s

extern void a(const char *);

Expand All @@ -26,7 +28,9 @@ int main(void) {
(void)__builtin_cpu_supports("x86-64-v3");
(void)__builtin_cpu_supports("x86-64-v4");
(void)__builtin_cpu_supports("x86-64-v5"); // expected-warning {{invalid cpu feature string for builtin}}
#else
#endif

#ifdef __aarch64__
if (__builtin_cpu_supports("neon")) // expected-warning {{invalid cpu feature string for builtin}}
a("vsx");

Expand All @@ -36,5 +40,10 @@ int main(void) {
__builtin_cpu_init(); // expected-error {{builtin is not supported on this target}}
#endif

#ifdef __riscv
if (__builtin_cpu_supports("garbage")) // expected-warning {{invalid cpu feature string for builtin}}
a("vsx");
#endif

return 0;
}
4 changes: 4 additions & 0 deletions llvm/include/llvm/TargetParser/RISCVISAInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ class RISCVISAInfo {
std::set<StringRef> &EnabledFeatureNames,
StringMap<StringRef> &DescMap);

/// Return the bit position (in group 0) of __riscv_feature_bits. Returns
/// -1 if not supported.
static int getRISCVFeaturesBitPosition(StringRef Ext);

private:
RISCVISAInfo(unsigned XLen) : XLen(XLen) {}

Expand Down
40 changes: 40 additions & 0 deletions llvm/lib/TargetParser/RISCVISAInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1020,3 +1020,43 @@ std::string RISCVISAInfo::getTargetFeatureForExtension(StringRef Ext) {
return isExperimentalExtension(Name) ? "experimental-" + Name.str()
: Name.str();
}

struct RISCVExtBit {
const StringLiteral ext;
uint8_t bitpos;
};

/// Maps extensions with assigned bit positions within group 0 of
/// __riscv_features_bits to their respective bit position. At the
/// moment all extensions are within group 0.
constexpr static RISCVExtBit RISCVGroup0BitPositions[] = {
{"a", 0}, {"c", 2},
{"d", 3}, {"f", 5},
{"i", 8}, {"m", 12},
{"v", 21}, {"zacas", 26},
{"zba", 27}, {"zbb", 28},
{"zbc", 29}, {"zbkb", 30},
{"zbkc", 31}, {"zbkx", 32},
{"zbs", 33}, {"zfa", 34},
{"zfh", 35}, {"zfhmin", 36},
{"zicboz", 37}, {"zicond", 38},
{"zihintntl", 39}, {"zihintpause", 40},
{"zknd", 41}, {"zkne", 42},
{"zknh", 43}, {"zksed", 44},
{"zksh", 45}, {"zkt", 46},
{"ztso", 47}, {"zvbb", 48},
{"zvbc", 49}, {"zvfh", 50},
{"zvfhmin", 51}, {"zvkb", 52},
{"zvkg", 53}, {"zvkned", 54},
{"zvknha", 55}, {"zvknhb", 56},
{"zvksed", 57}, {"zvksh", 58},
{"zvkt", 59}};
int RISCVISAInfo::getRISCVFeaturesBitPosition(StringRef Ext) {
// Note that this code currently accepts mixed case extension names, but
// does not handle extension versions at all. That's probably fine because
// there's only one extension version in the __riscv_feature_bits vector.
for (auto E : RISCVGroup0BitPositions)
if (E.ext.equals_insensitive(Ext))
return E.bitpos;
return -1;
}