[llvm-branch-commits] [RISCV] Support __builtin_cpu_is (PR #116231)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Nov 14 06:02:29 PST 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Pengcheng Wang (wangpc-pp)

<details>
<summary>Changes</summary>

We have defined `__riscv_cpu_model` variable in #<!-- -->101449. It contains
`mvendorid`, `marchid` and `mimpid` fields which are read via system
call `sys_riscv_hwprobe`.

We can support `__builtin_cpu_is` via comparing values in compiler's
CPU definitions and `__riscv_cpu_model`.

This depends on #<!-- -->116202.


---
Full diff: https://github.com/llvm/llvm-project/pull/116231.diff


7 Files Affected:

- (modified) clang/lib/Basic/Targets/RISCV.cpp (+8) 
- (modified) clang/lib/Basic/Targets/RISCV.h (+2) 
- (modified) clang/lib/CodeGen/CGBuiltin.cpp (+54) 
- (modified) clang/lib/CodeGen/CodeGenFunction.h (+2) 
- (modified) clang/test/CodeGen/builtin-cpu-is.c (+110-1) 
- (modified) llvm/include/llvm/TargetParser/RISCVTargetParser.h (+4) 
- (modified) llvm/lib/TargetParser/RISCVTargetParser.cpp (+26) 


``````````diff
diff --git a/clang/lib/Basic/Targets/RISCV.cpp b/clang/lib/Basic/Targets/RISCV.cpp
index eaaba7642bd7b2..15e489a6a1288f 100644
--- a/clang/lib/Basic/Targets/RISCV.cpp
+++ b/clang/lib/Basic/Targets/RISCV.cpp
@@ -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);
+}
diff --git a/clang/lib/Basic/Targets/RISCV.h b/clang/lib/Basic/Targets/RISCV.h
index 3b418585ab4a39..3544ea64cb5e77 100644
--- a/clang/lib/Basic/Targets/RISCV.h
+++ b/clang/lib/Basic/Targets/RISCV.h
@@ -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,
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 9e0c0bff0125c0..82e21d105fcc0e 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -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>
@@ -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))));
+
+  return Result;
+}
+
 Value *CodeGenFunction::EmitRISCVBuiltinExpr(unsigned BuiltinID,
                                              const CallExpr *E,
                                              ReturnValueSlot ReturnValue) {
@@ -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());
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index fcc1013d7361ec..5c4d76c2267a77 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -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);
diff --git a/clang/test/CodeGen/builtin-cpu-is.c b/clang/test/CodeGen/builtin-cpu-is.c
index dab6d2f9d11aec..e4a2071cf46795 100644
--- a/clang/test/CodeGen/builtin-cpu-is.c
+++ b/clang/test/CodeGen/builtin-cpu-is.c
@@ -1,11 +1,26 @@
-// 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");
@@ -14,6 +29,18 @@ void intel(void) {
   // 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");
@@ -22,6 +49,18 @@ void amd(void) {
   // 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");
@@ -30,6 +69,18 @@ void atom(void) {
   // 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");
@@ -38,6 +89,18 @@ void amdfam10h(void) {
   // 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");
@@ -46,6 +109,18 @@ void barcelona(void) {
   // 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");
@@ -53,3 +128,37 @@ void nehalem(void) {
   // 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
 }
+#endif
+
+#ifdef __riscv
+// 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:    [[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:    br i1 [[TMP7]], label %[[IF_THEN:.*]], label %[[IF_END:.*]]
+// CHECK-RV64:       [[IF_THEN]]:
+// CHECK-RV64-NEXT:    store i32 3, ptr [[RETVAL]], align 4
+// CHECK-RV64-NEXT:    br label %[[RETURN:.*]]
+// CHECK-RV64:       [[IF_END]]:
+// CHECK-RV64-NEXT:    store i32 0, ptr [[RETVAL]], align 4
+// CHECK-RV64-NEXT:    br label %[[RETURN]]
+// CHECK-RV64:       [[RETURN]]:
+// CHECK-RV64-NEXT:    [[TMP8:%.*]] = load i32, ptr [[RETVAL]], align 4
+// CHECK-RV64-NEXT:    ret i32 [[TMP8]]
+//
+int test_riscv(int a) {
+  if (__builtin_cpu_is("veyron-v1"))
+    return 3;
+  return 0;
+}
+#endif
diff --git a/llvm/include/llvm/TargetParser/RISCVTargetParser.h b/llvm/include/llvm/TargetParser/RISCVTargetParser.h
index c75778952e0f51..23a30425e95616 100644
--- a/llvm/include/llvm/TargetParser/RISCVTargetParser.h
+++ b/llvm/include/llvm/TargetParser/RISCVTargetParser.h
@@ -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
 
diff --git a/llvm/lib/TargetParser/RISCVTargetParser.cpp b/llvm/lib/TargetParser/RISCVTargetParser.cpp
index 62fbd3458112e2..d62e1ba25cd94d 100644
--- a/llvm/lib/TargetParser/RISCVTargetParser.cpp
+++ b/llvm/lib/TargetParser/RISCVTargetParser.cpp
@@ -71,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);
 

``````````

</details>


https://github.com/llvm/llvm-project/pull/116231


More information about the llvm-branch-commits mailing list