Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
8 changes: 8 additions & 0 deletions clang/lib/Basic/Targets/RISCV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -508,3 +508,11 @@ bool RISCVTargetInfo::validateGlobalRegisterVariable(
}
return false;
}

bool RISCVTargetInfo::validateCpuIs(StringRef CPUName) const {
llvm::Triple Triple = getTriple();
assert(Triple.isOSLinux() &&
"__builtin_cpu_is() is only supported for Linux.");

return llvm::RISCV::hasValidCPUModel(CPUName);
}
2 changes: 2 additions & 0 deletions clang/lib/Basic/Targets/RISCV.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,10 @@ class RISCVTargetInfo : public TargetInfo {
}

bool supportsCpuSupports() const override { return getTriple().isOSLinux(); }
bool supportsCpuIs() const override { return getTriple().isOSLinux(); }
bool supportsCpuInit() const override { return getTriple().isOSLinux(); }
bool validateCpuSupports(StringRef Feature) const override;
bool validateCpuIs(StringRef CPUName) const override;
bool isValidFeatureName(StringRef Name) const override;

bool validateGlobalRegisterVariable(StringRef RegName, unsigned RegSize,
Expand Down
54 changes: 54 additions & 0 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/TargetParser/AArch64TargetParser.h"
#include "llvm/TargetParser/RISCVISAInfo.h"
#include "llvm/TargetParser/RISCVTargetParser.h"
#include "llvm/TargetParser/X86TargetParser.h"
#include <optional>
#include <sstream>
Expand Down Expand Up @@ -22505,6 +22506,57 @@ Value *CodeGenFunction::EmitHexagonBuiltinExpr(unsigned BuiltinID,
return nullptr;
}

Value *CodeGenFunction::EmitRISCVCpuIs(const CallExpr *E) {
const Expr *CPUExpr = E->getArg(0)->IgnoreParenCasts();
StringRef CPUStr = cast<clang::StringLiteral>(CPUExpr)->getString();
return EmitRISCVCpuIs(CPUStr);
}

Value *CodeGenFunction::EmitRISCVCpuIs(StringRef CPUStr) {
llvm::Type *Int32Ty = Builder.getInt32Ty();
llvm::Type *Int64Ty = Builder.getInt64Ty();
llvm::Type *MXLenType =
CGM.getTarget().getTriple().isArch32Bit() ? Int32Ty : Int64Ty;

llvm::Type *StructTy = llvm::StructType::get(Int32Ty, MXLenType, MXLenType);
llvm::Constant *RISCVCPUModel =
CGM.CreateRuntimeVariable(StructTy, "__riscv_cpu_model");
cast<llvm::GlobalValue>(RISCVCPUModel)->setDSOLocal(true);

auto loadRISCVCPUID = [&](unsigned Index, llvm::Type *ValueTy,
CGBuilderTy &Builder, CodeGenModule &CGM) {
llvm::Value *GEPIndices[] = {Builder.getInt32(0),
llvm::ConstantInt::get(Int32Ty, Index)};
Value *Ptr = Builder.CreateInBoundsGEP(StructTy, RISCVCPUModel, GEPIndices);
Value *CPUID = Builder.CreateAlignedLoad(
ValueTy, Ptr,
CharUnits::fromQuantity(ValueTy->getScalarSizeInBits() / 8));
return CPUID;
};

// Compare mvendorid.
Value *VendorID = loadRISCVCPUID(0, Int32Ty, Builder, CGM);
Value *Result = Builder.CreateICmpEQ(
VendorID,
llvm::ConstantInt::get(Int32Ty, llvm::RISCV::getVendorID(CPUStr)));

// Compare marchid.
Value *ArchID = loadRISCVCPUID(1, MXLenType, Builder, CGM);
Result = Builder.CreateAnd(
Result, Builder.CreateICmpEQ(
ArchID, llvm::ConstantInt::get(
MXLenType, llvm::RISCV::getArchID(CPUStr))));

// Compare mimplid.
Value *ImplID = loadRISCVCPUID(2, MXLenType, Builder, CGM);
Result = Builder.CreateAnd(
Result, Builder.CreateICmpEQ(
ImplID, llvm::ConstantInt::get(
MXLenType, llvm::RISCV::getImplID(CPUStr))));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently we are using getVendorID, getArchID or getImplID returning 0 to mean we have no IDs in LLVM for that CPU. On the CPU Core's side, the CSRs will also report 0 if they are not implemented (though I'm not sure what e.g. Linux reports to userspace in this case).

When we have no IDs, what comparisons should we be doing?

Maybe we should only support __builtin_cpu_is(...) for CPUs where we have non-zero values for any of the three items? Otherwise either emit a compile-time error, or return false because we don't have a way to actually know if we're on the CPU being asked for?

I don't know the right answer here, but I think this implementation isn't handling these cases right.

Copy link
Contributor Author

@wangpc-pp wangpc-pp Nov 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently this implementation just reports an error when there is no non-zero values in CPU definitions (done in validateCpuIs). This should match PPC/X86. PPC supports CPUs with valid Linux_SUPPORT_METHOD or AIX_SUPPORT_METHOD value; X86 supports a set of defined CPUs.


return Result;
}

Value *CodeGenFunction::EmitRISCVBuiltinExpr(unsigned BuiltinID,
const CallExpr *E,
ReturnValueSlot ReturnValue) {
Expand All @@ -22513,6 +22565,8 @@ Value *CodeGenFunction::EmitRISCVBuiltinExpr(unsigned BuiltinID,
return EmitRISCVCpuSupports(E);
if (BuiltinID == Builtin::BI__builtin_cpu_init)
return EmitRISCVCpuInit();
if (BuiltinID == Builtin::BI__builtin_cpu_is)
return EmitRISCVCpuIs(E);

SmallVector<Value *, 4> Ops;
llvm::Type *ResultType = ConvertType(E->getType());
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CodeGen/CodeGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -4730,6 +4730,8 @@ class CodeGenFunction : public CodeGenTypeCache {
llvm::Value *EmitRISCVCpuSupports(const CallExpr *E);
llvm::Value *EmitRISCVCpuSupports(ArrayRef<StringRef> FeaturesStrs);
llvm::Value *EmitRISCVCpuInit();
llvm::Value *EmitRISCVCpuIs(const CallExpr *E);
llvm::Value *EmitRISCVCpuIs(StringRef CPUStr);

void AddAMDGPUFenceAddressSpaceMMRA(llvm::Instruction *Inst,
const CallExpr *E);
Expand Down
116 changes: 96 additions & 20 deletions clang/test/CodeGen/builtin-cpu-is.c
Original file line number Diff line number Diff line change
@@ -1,55 +1,131 @@
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm < %s| FileCheck %s
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-X86
// RUN: %clang_cc1 -triple riscv64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s --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.
extern void a(const char *);

// CHECK: @__cpu_model = external dso_local global { i32, i32, i32, [1 x i32] }

// CHECK-X86-LABEL: define dso_local void @intel(
// CHECK-X86-SAME: ) #[[ATTR0:[0-9]+]] {
// CHECK-X86-NEXT: [[ENTRY:.*:]]
// CHECK-X86-NEXT: [[TMP0:%.*]] = load i32, ptr @__cpu_model, align 4
// CHECK-X86-NEXT: [[TMP1:%.*]] = icmp eq i32 [[TMP0]], 1
// CHECK-X86-NEXT: br i1 [[TMP1]], label %[[IF_THEN:.*]], label %[[IF_END:.*]]
// CHECK-X86: [[IF_THEN]]:
// CHECK-X86-NEXT: call void @a(ptr noundef @.str)
// CHECK-X86-NEXT: br label %[[IF_END]]
// CHECK-X86: [[IF_END]]:
// CHECK-X86-NEXT: ret void
//
void intel(void) {
if (__builtin_cpu_is("intel"))
a("intel");

// CHECK: [[LOAD:%[^ ]+]] = load i32, ptr @__cpu_model
// CHECK: = icmp eq i32 [[LOAD]], 1
}

// CHECK-X86-LABEL: define dso_local void @amd(
// CHECK-X86-SAME: ) #[[ATTR0]] {
// CHECK-X86-NEXT: [[ENTRY:.*:]]
// CHECK-X86-NEXT: [[TMP0:%.*]] = load i32, ptr @__cpu_model, align 4
// CHECK-X86-NEXT: [[TMP1:%.*]] = icmp eq i32 [[TMP0]], 2
// CHECK-X86-NEXT: br i1 [[TMP1]], label %[[IF_THEN:.*]], label %[[IF_END:.*]]
// CHECK-X86: [[IF_THEN]]:
// CHECK-X86-NEXT: call void @a(ptr noundef @.str.1)
// CHECK-X86-NEXT: br label %[[IF_END]]
// CHECK-X86: [[IF_END]]:
// CHECK-X86-NEXT: ret void
//
void amd(void) {
if (__builtin_cpu_is("amd"))
a("amd");

// CHECK: [[LOAD:%[^ ]+]] = load i32, ptr @__cpu_model
// CHECK: = icmp eq i32 [[LOAD]], 2
}

// CHECK-X86-LABEL: define dso_local void @atom(
// CHECK-X86-SAME: ) #[[ATTR0]] {
// CHECK-X86-NEXT: [[ENTRY:.*:]]
// CHECK-X86-NEXT: [[TMP0:%.*]] = load i32, ptr getelementptr inbounds ({ i32, i32, i32, [1 x i32] }, ptr @__cpu_model, i32 0, i32 1), align 4
// CHECK-X86-NEXT: [[TMP1:%.*]] = icmp eq i32 [[TMP0]], 1
// CHECK-X86-NEXT: br i1 [[TMP1]], label %[[IF_THEN:.*]], label %[[IF_END:.*]]
// CHECK-X86: [[IF_THEN]]:
// CHECK-X86-NEXT: call void @a(ptr noundef @.str.2)
// CHECK-X86-NEXT: br label %[[IF_END]]
// CHECK-X86: [[IF_END]]:
// CHECK-X86-NEXT: ret void
//
void atom(void) {
if (__builtin_cpu_is("atom"))
a("atom");

// CHECK: [[LOAD:%[^ ]+]] = load i32, ptr getelementptr inbounds ({ i32, i32, i32, [1 x i32] }, ptr @__cpu_model, i32 0, i32 1)
// CHECK: = icmp eq i32 [[LOAD]], 1
}

// CHECK-X86-LABEL: define dso_local void @amdfam10h(
// CHECK-X86-SAME: ) #[[ATTR0]] {
// CHECK-X86-NEXT: [[ENTRY:.*:]]
// CHECK-X86-NEXT: [[TMP0:%.*]] = load i32, ptr getelementptr inbounds ({ i32, i32, i32, [1 x i32] }, ptr @__cpu_model, i32 0, i32 1), align 4
// CHECK-X86-NEXT: [[TMP1:%.*]] = icmp eq i32 [[TMP0]], 4
// CHECK-X86-NEXT: br i1 [[TMP1]], label %[[IF_THEN:.*]], label %[[IF_END:.*]]
// CHECK-X86: [[IF_THEN]]:
// CHECK-X86-NEXT: call void @a(ptr noundef @.str.3)
// CHECK-X86-NEXT: br label %[[IF_END]]
// CHECK-X86: [[IF_END]]:
// CHECK-X86-NEXT: ret void
//
void amdfam10h(void) {
if (__builtin_cpu_is("amdfam10h"))
a("amdfam10h");

// CHECK: [[LOAD:%[^ ]+]] = load i32, ptr getelementptr inbounds ({ i32, i32, i32, [1 x i32] }, ptr @__cpu_model, i32 0, i32 1)
// CHECK: = icmp eq i32 [[LOAD]], 4
}

// CHECK-X86-LABEL: define dso_local void @barcelona(
// CHECK-X86-SAME: ) #[[ATTR0]] {
// CHECK-X86-NEXT: [[ENTRY:.*:]]
// CHECK-X86-NEXT: [[TMP0:%.*]] = load i32, ptr getelementptr inbounds ({ i32, i32, i32, [1 x i32] }, ptr @__cpu_model, i32 0, i32 2), align 4
// CHECK-X86-NEXT: [[TMP1:%.*]] = icmp eq i32 [[TMP0]], 4
// CHECK-X86-NEXT: br i1 [[TMP1]], label %[[IF_THEN:.*]], label %[[IF_END:.*]]
// CHECK-X86: [[IF_THEN]]:
// CHECK-X86-NEXT: call void @a(ptr noundef @.str.4)
// CHECK-X86-NEXT: br label %[[IF_END]]
// CHECK-X86: [[IF_END]]:
// CHECK-X86-NEXT: ret void
//
void barcelona(void) {
if (__builtin_cpu_is("barcelona"))
a("barcelona");

// CHECK: [[LOAD:%[^ ]+]] = load i32, ptr getelementptr inbounds ({ i32, i32, i32, [1 x i32] }, ptr @__cpu_model, i32 0, i32 2)
// CHECK: = icmp eq i32 [[LOAD]], 4
}

// CHECK-X86-LABEL: define dso_local void @nehalem(
// CHECK-X86-SAME: ) #[[ATTR0]] {
// CHECK-X86-NEXT: [[ENTRY:.*:]]
// CHECK-X86-NEXT: [[TMP0:%.*]] = load i32, ptr getelementptr inbounds ({ i32, i32, i32, [1 x i32] }, ptr @__cpu_model, i32 0, i32 2), align 4
// CHECK-X86-NEXT: [[TMP1:%.*]] = icmp eq i32 [[TMP0]], 1
// CHECK-X86-NEXT: br i1 [[TMP1]], label %[[IF_THEN:.*]], label %[[IF_END:.*]]
// CHECK-X86: [[IF_THEN]]:
// CHECK-X86-NEXT: call void @a(ptr noundef @.str.5)
// CHECK-X86-NEXT: br label %[[IF_END]]
// CHECK-X86: [[IF_END]]:
// CHECK-X86-NEXT: ret void
//
void nehalem(void) {
if (__builtin_cpu_is("nehalem"))
a("nehalem");
}
#endif

// CHECK: [[LOAD:%[^ ]+]] = load i32, ptr getelementptr inbounds ({ i32, i32, i32, [1 x i32] }, ptr @__cpu_model, i32 0, i32 2)
// CHECK: = icmp eq i32 [[LOAD]], 1
#ifdef __riscv
// CHECK-RV64-LABEL: define dso_local signext i32 @test_cpu_is_veyron_v1(
// CHECK-RV64-SAME: ) #[[ATTR0:[0-9]+]] {
// CHECK-RV64-NEXT: [[ENTRY:.*:]]
// CHECK-RV64-NEXT: [[TMP0:%.*]] = load i32, ptr @__riscv_cpu_model, align 4
// CHECK-RV64-NEXT: [[TMP1:%.*]] = icmp eq i32 [[TMP0]], 1567
// CHECK-RV64-NEXT: [[TMP2:%.*]] = load i64, ptr getelementptr inbounds ({ i32, i64, i64 }, ptr @__riscv_cpu_model, i32 0, i32 1), align 8
// CHECK-RV64-NEXT: [[TMP3:%.*]] = icmp eq i64 [[TMP2]], -9223372036854710272
// CHECK-RV64-NEXT: [[TMP4:%.*]] = and i1 [[TMP1]], [[TMP3]]
// CHECK-RV64-NEXT: [[TMP5:%.*]] = load i64, ptr getelementptr inbounds ({ i32, i64, i64 }, ptr @__riscv_cpu_model, i32 0, i32 2), align 8
// CHECK-RV64-NEXT: [[TMP6:%.*]] = icmp eq i64 [[TMP5]], 273
// CHECK-RV64-NEXT: [[TMP7:%.*]] = and i1 [[TMP4]], [[TMP6]]
// CHECK-RV64-NEXT: [[CONV:%.*]] = zext i1 [[TMP7]] to i32
// CHECK-RV64-NEXT: ret i32 [[CONV]]
//
int test_cpu_is_veyron_v1() {
return __builtin_cpu_is("veyron-v1");
}
#endif
4 changes: 4 additions & 0 deletions llvm/include/llvm/TargetParser/RISCVTargetParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ void fillValidCPUArchList(SmallVectorImpl<StringRef> &Values, bool IsRV64);
void fillValidTuneCPUArchList(SmallVectorImpl<StringRef> &Values, bool IsRV64);
bool hasFastScalarUnalignedAccess(StringRef CPU);
bool hasFastVectorUnalignedAccess(StringRef CPU);
bool hasValidCPUModel(StringRef CPU);
uint32_t getVendorID(StringRef CPU);
uint64_t getArchID(StringRef CPU);
uint64_t getImplID(StringRef CPU);

} // namespace RISCV

Expand Down
9 changes: 8 additions & 1 deletion llvm/lib/Target/RISCV/RISCVProcessors.td
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ class RISCVProcessorModel<string n,
string default_march = "">
: ProcessorModel<n, m, f, tunef> {
string DefaultMarch = default_march;
int MVendorID = 0;
int MArchID = 0;
int MImpID = 0;
}

class RISCVTuneProcessorModel<string n,
Expand Down Expand Up @@ -435,7 +438,11 @@ def VENTANA_VEYRON_V1 : RISCVProcessorModel<"veyron-v1",
TuneZExtHFusion,
TuneZExtWFusion,
TuneShiftedZExtWFusion,
TuneLDADDFusion]>;
TuneLDADDFusion]> {
let MVendorID = 0x61f;
let MArchID = 0x8000000000010000;
let MImpID = 0x111;
}

def XIANGSHAN_NANHU : RISCVProcessorModel<"xiangshan-nanhu",
XiangShanNanHuModel,
Expand Down
43 changes: 40 additions & 3 deletions llvm/lib/TargetParser/RISCVTargetParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ namespace RISCV {

enum CPUKind : unsigned {
#define PROC(ENUM, NAME, DEFAULT_MARCH, FAST_SCALAR_UNALIGN, \
FAST_VECTOR_UNALIGN) \
FAST_VECTOR_UNALIGN, MVENDORID, MARCHID, MIMPID) \
CK_##ENUM,
#define TUNE_PROC(ENUM, NAME) CK_##ENUM,
#include "llvm/TargetParser/RISCVTargetParserDef.inc"
Expand All @@ -33,13 +33,24 @@ struct CPUInfo {
StringLiteral DefaultMarch;
bool FastScalarUnalignedAccess;
bool FastVectorUnalignedAccess;
uint32_t MVendorID;
uint64_t MArchID;
uint64_t MImpID;
bool is64Bit() const { return DefaultMarch.starts_with("rv64"); }
};

constexpr CPUInfo RISCVCPUInfo[] = {
#define PROC(ENUM, NAME, DEFAULT_MARCH, FAST_SCALAR_UNALIGN, \
FAST_VECTOR_UNALIGN) \
{NAME, DEFAULT_MARCH, FAST_SCALAR_UNALIGN, FAST_VECTOR_UNALIGN},
FAST_VECTOR_UNALIGN, MVENDORID, MARCHID, MIMPID) \
{ \
NAME, \
DEFAULT_MARCH, \
FAST_SCALAR_UNALIGN, \
FAST_VECTOR_UNALIGN, \
MVENDORID, \
MARCHID, \
MIMPID, \
},
#include "llvm/TargetParser/RISCVTargetParserDef.inc"
};

Expand All @@ -60,6 +71,32 @@ bool hasFastVectorUnalignedAccess(StringRef CPU) {
return Info && Info->FastVectorUnalignedAccess;
}

bool hasValidCPUModel(StringRef CPU) {
const CPUInfo *Info = getCPUInfoByName(CPU);
return Info && Info->MVendorID && Info->MArchID && Info->MImpID;
}

uint32_t getVendorID(StringRef CPU) {
const CPUInfo *Info = getCPUInfoByName(CPU);
if (!Info)
return false;
return Info->MVendorID;
}

uint64_t getArchID(StringRef CPU) {
const CPUInfo *Info = getCPUInfoByName(CPU);
if (!Info)
return false;
return Info->MArchID;
}

uint64_t getImplID(StringRef CPU) {
const CPUInfo *Info = getCPUInfoByName(CPU);
if (!Info)
return false;
return Info->MImpID;
}

bool parseCPU(StringRef CPU, bool IsRV64) {
const CPUInfo *Info = getCPUInfoByName(CPU);

Expand Down
13 changes: 8 additions & 5 deletions llvm/test/TableGen/riscv-target-def.td
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ class RISCVProcessorModel<string n,
string default_march = "">
: ProcessorModel<n, m, f, tunef> {
string DefaultMarch = default_march;
int MVendorID = 0;
int MArchID = 0;
int MImpID = 0;
}

class RISCVTuneProcessorModel<string n,
Expand Down Expand Up @@ -160,13 +163,13 @@ def ROCKET : RISCVTuneProcessorModel<"rocket",
// CHECK: #endif // GET_SUPPORTED_PROFILES

// CHECK: #ifndef PROC
// CHECK-NEXT: #define PROC(ENUM, NAME, DEFAULT_MARCH, FAST_SCALAR_UNALIGN, FAST_VECTOR_UNALIGN)
// CHECK-NEXT: #define PROC(ENUM, NAME, DEFAULT_MARCH, FAST_SCALAR_UNALIGN, FAST_VECTOR_UNALIGN, MVENDORID, MARCHID, MIMPID)
// CHECK-NEXT: #endif

// CHECK: PROC(GENERIC_RV32, {"generic-rv32"}, {"rv32i2p1"}, 0, 0)
// CHECK-NEXT: PROC(GENERIC_RV64, {"generic-rv64"}, {"rv64i2p1"}, 0, 0)
// CHECK-NEXT: PROC(ROCKET_RV32, {"rocket-rv32"}, {"rv32i2p1_zicsr2p0_zidummy0p1_zifencei2p0"}, 0, 0)
// CHECK-NEXT: PROC(ROCKET_RV64, {"rocket-rv64"}, {"rv64i2p1_zicsr2p0_zidummy0p1_zifencei2p0"}, 0, 0)
// CHECK: PROC(GENERIC_RV32, {"generic-rv32"}, {"rv32i2p1"}, 0, 0, 0x00000000, 0x00000000, 0x00000000)
// CHECK-NEXT: PROC(GENERIC_RV64, {"generic-rv64"}, {"rv64i2p1"}, 0, 0, 0x00000000, 0x0000000000000000, 0x0000000000000000)
// CHECK-NEXT: PROC(ROCKET_RV32, {"rocket-rv32"}, {"rv32i2p1_zicsr2p0_zidummy0p1_zifencei2p0"}, 0, 0, 0x00000000, 0x00000000, 0x00000000)
// CHECK-NEXT: PROC(ROCKET_RV64, {"rocket-rv64"}, {"rv64i2p1_zicsr2p0_zidummy0p1_zifencei2p0"}, 0, 0, 0x00000000, 0x0000000000000000, 0x0000000000000000)

// CHECK: #undef PROC

Expand Down
Loading
Loading