craig.topper created this revision.

This patch adds support for __builtin_cpu_is. I've tried to match the strings 
supported to the latest version of gcc.

I've only tested this on my Macbook so far so I'd appreciate if others would 
test it. An AMD system would be great.


https://reviews.llvm.org/D35449

Files:
  include/clang/Basic/BuiltinsX86.def
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Basic/TargetInfo.h
  lib/Basic/Targets.cpp
  lib/CodeGen/CGBuiltin.cpp
  lib/Sema/SemaChecking.cpp
  test/CodeGen/builtin-cpu-is.c
  test/CodeGen/target-builtin-noerror.c
  test/Sema/builtin-cpu-supports.c

Index: test/Sema/builtin-cpu-supports.c
===================================================================
--- test/Sema/builtin-cpu-supports.c
+++ test/Sema/builtin-cpu-supports.c
@@ -12,9 +12,15 @@
 
   if (__builtin_cpu_supports(str)) // expected-error {{expression is not a string literal}}
     a(str);
+
+  if (__builtin_cpu_is("int")) // expected-error {{invalid cpu name for builtin}}
+    a("intel");
 #else
   if (__builtin_cpu_supports("vsx")) // expected-error {{use of unknown builtin}}
     a("vsx");
+
+  if (__builtin_cpu_is("pwr9")) // expected-error {{use of unknown builtin}}
+    a("pwr9");
 #endif
 
   return 0;
Index: test/CodeGen/target-builtin-noerror.c
===================================================================
--- test/CodeGen/target-builtin-noerror.c
+++ test/CodeGen/target-builtin-noerror.c
@@ -73,3 +73,35 @@
   (void)__builtin_cpu_supports("avx512vbmi");
   (void)__builtin_cpu_supports("avx512ifma");
 }
+
+void verifycpustrings() {
+  (void)__builtin_cpu_is("amd");
+  (void)__builtin_cpu_is("amdfam10h");
+  (void)__builtin_cpu_is("amdfam15h");
+  (void)__builtin_cpu_is("atom");
+  (void)__builtin_cpu_is("barcelona");
+  (void)__builtin_cpu_is("bdver1");
+  (void)__builtin_cpu_is("bdver2");
+  (void)__builtin_cpu_is("bdver3");
+  (void)__builtin_cpu_is("bdver4");
+  (void)__builtin_cpu_is("bonnell");
+  (void)__builtin_cpu_is("broadwell");
+  (void)__builtin_cpu_is("btver1");
+  (void)__builtin_cpu_is("btver2");
+  (void)__builtin_cpu_is("core2");
+  (void)__builtin_cpu_is("corei7");
+  (void)__builtin_cpu_is("haswell");
+  (void)__builtin_cpu_is("intel");
+  (void)__builtin_cpu_is("istanbul");
+  (void)__builtin_cpu_is("ivybridge");
+  (void)__builtin_cpu_is("knl");
+  (void)__builtin_cpu_is("nehalem");
+  (void)__builtin_cpu_is("sandybridge");
+  (void)__builtin_cpu_is("shanghai");
+  (void)__builtin_cpu_is("silvermont");
+  (void)__builtin_cpu_is("skylake");
+  (void)__builtin_cpu_is("skylake-avx512");
+  (void)__builtin_cpu_is("slm");
+  (void)__builtin_cpu_is("westmere");
+  (void)__builtin_cpu_is("znver1");
+}
Index: test/CodeGen/builtin-cpu-is.c
===================================================================
--- /dev/null
+++ test/CodeGen/builtin-cpu-is.c
@@ -0,0 +1,53 @@
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm < %s| FileCheck %s
+
+// 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 *);
+
+void intel() {
+  if (__builtin_cpu_is("intel"))
+    a("intel");
+
+  // CHECK: [[LOAD:%[^ ]+]] = load i32, i32* getelementptr inbounds ({ i32, i32, i32, [1 x i32] }, { i32, i32, i32, [1 x i32] }* @__cpu_model, i32 0, i32 0)
+  // CHECK: = icmp eq i32 [[LOAD]], 1
+}
+
+void amd() {
+  if (__builtin_cpu_is("amd"))
+    a("amd");
+
+  // CHECK: [[LOAD:%[^ ]+]] = load i32, i32* getelementptr inbounds ({ i32, i32, i32, [1 x i32] }, { i32, i32, i32, [1 x i32] }* @__cpu_model, i32 0, i32 0)
+  // CHECK: = icmp eq i32 [[LOAD]], 2
+}
+
+void atom() {
+  if (__builtin_cpu_is("atom"))
+    a("atom");
+
+  // CHECK: [[LOAD:%[^ ]+]] = load i32, i32* getelementptr inbounds ({ i32, i32, i32, [1 x i32] }, { i32, i32, i32, [1 x i32] }* @__cpu_model, i32 0, i32 1)
+  // CHECK: = icmp eq i32 [[LOAD]], 1
+}
+
+void amdfam10h() {
+  if (__builtin_cpu_is("amdfam10h"))
+    a("amdfam10h");
+
+  // CHECK: [[LOAD:%[^ ]+]] = load i32, i32* getelementptr inbounds ({ i32, i32, i32, [1 x i32] }, { i32, i32, i32, [1 x i32] }* @__cpu_model, i32 0, i32 1)
+  // CHECK: = icmp eq i32 [[LOAD]], 4
+}
+
+void barcelona() {
+  if (__builtin_cpu_is("barcelona"))
+    a("barcelona");
+
+  // CHECK: [[LOAD:%[^ ]+]] = load i32, i32* getelementptr inbounds ({ i32, i32, i32, [1 x i32] }, { i32, i32, i32, [1 x i32] }* @__cpu_model, i32 0, i32 2)
+  // CHECK: = icmp eq i32 [[LOAD]], 4
+}
+
+void nehalem() {
+  if (__builtin_cpu_is("nehalem"))
+    a("nehalem");
+
+  // CHECK: [[LOAD:%[^ ]+]] = load i32, i32* getelementptr inbounds ({ i32, i32, i32, [1 x i32] }, { i32, i32, i32, [1 x i32] }* @__cpu_model, i32 0, i32 2)
+  // CHECK: = icmp eq i32 [[LOAD]], 1
+}
Index: lib/Sema/SemaChecking.cpp
===================================================================
--- lib/Sema/SemaChecking.cpp
+++ lib/Sema/SemaChecking.cpp
@@ -1782,6 +1782,26 @@
   return false;
 }
 
+/// SemaBuiltinCpuIs - Handle __builtin_cpu_is(char *).
+/// This checks that the target supports __builtin_cpu_is and
+/// that the string argument is constant and valid.
+static bool SemaBuiltinCpuIs(Sema &S, CallExpr *TheCall) {
+  Expr *Arg = TheCall->getArg(0);
+
+  // Check if the argument is a string literal.
+  if (!isa<StringLiteral>(Arg->IgnoreParenImpCasts()))
+    return S.Diag(TheCall->getLocStart(), diag::err_expr_not_string_literal)
+           << Arg->getSourceRange();
+
+  // Check the contents of the string.
+  StringRef Feature =
+      cast<StringLiteral>(Arg->IgnoreParenImpCasts())->getString();
+  if (!S.Context.getTargetInfo().validateCpuIs(Feature))
+    return S.Diag(TheCall->getLocStart(), diag::err_invalid_cpu_is)
+           << Arg->getSourceRange();
+  return false;
+}
+
 // Check if the rounding mode is legal.
 bool Sema::CheckX86BuiltinRoundingOrSAE(unsigned BuiltinID, CallExpr *TheCall) {
   // Indicates if this instruction has rounding control or just SAE.
@@ -2095,6 +2115,9 @@
   if (BuiltinID == X86::BI__builtin_cpu_supports)
     return SemaBuiltinCpuSupports(*this, TheCall);
 
+  if (BuiltinID == X86::BI__builtin_cpu_is)
+    return SemaBuiltinCpuIs(*this, TheCall);
+
   if (BuiltinID == X86::BI__builtin_ms_va_start)
     return SemaBuiltinVAStart(BuiltinID, TheCall);
 
Index: lib/CodeGen/CGBuiltin.cpp
===================================================================
--- lib/CodeGen/CGBuiltin.cpp
+++ lib/CodeGen/CGBuiltin.cpp
@@ -7221,6 +7221,120 @@
   return CGF.Builder.CreateSExt(Mask, DstTy, "vpmovm2");
 }
 
+static Value *EmitX86CpuIs(CodeGenFunction &CGF, const CallExpr *E) {
+  const Expr *CPUExpr = E->getArg(0)->IgnoreParenCasts();
+  StringRef CPUStr = cast<clang::StringLiteral>(CPUExpr)->getString();
+
+  // This enum contains the vendor, type, and subtype enums from the
+  // runtime library concatenated together. The _START labels mark
+  // the start and are used to adjust the value into the correct
+  // encoding space.
+  enum X86CPUs {
+    INTEL = 1,
+    AMD,
+    CPU_TYPE_START,
+    INTEL_BONNELL,
+    INTEL_CORE2,
+    INTEL_COREI7,
+    AMDFAM10H,
+    AMDFAM15H,
+    INTEL_SILVERMONT,
+    INTEL_KNL,
+    AMD_BTVER1,
+    AMD_BTVER2,
+    CPU_SUBTYPE_START,
+    INTEL_COREI7_NEHALEM,
+    INTEL_COREI7_WESTMERE,
+    INTEL_COREI7_SANDYBRIDGE,
+    AMDFAM10H_BARCELONA,
+    AMDFAM10H_SHANGHAI,
+    AMDFAM10H_ISTANBUL,
+    AMDFAM15H_BDVER1,
+    AMDFAM15H_BDVER2,
+    AMDFAM15H_BDVER3,
+    AMDFAM15H_BDVER4,
+    AMDFAM17H_ZNVER1,
+    INTEL_COREI7_IVYBRIDGE,
+    INTEL_COREI7_HASWELL,
+    INTEL_COREI7_BROADWELL,
+    INTEL_COREI7_SKYLAKE,
+    INTEL_COREI7_SKYLAKE_AVX512,
+  };
+
+  X86CPUs CPU =
+    StringSwitch<X86CPUs>(CPUStr)
+      .Case("amd", AMD)
+      .Case("amdfam10h", AMDFAM10H)
+      .Case("amdfam15h", AMDFAM15H)
+      .Case("atom", INTEL_BONNELL)
+      .Case("barcelona", AMDFAM10H_BARCELONA)
+      .Case("bdver1", AMDFAM15H_BDVER1)
+      .Case("bdver2", AMDFAM15H_BDVER2)
+      .Case("bdver3", AMDFAM15H_BDVER3)
+      .Case("bdver4", AMDFAM15H_BDVER4)
+      .Case("bonnell", INTEL_BONNELL)
+      .Case("broadwell", INTEL_COREI7_BROADWELL)
+      .Case("btver1", AMD_BTVER1)
+      .Case("btver2", AMD_BTVER2)
+      .Case("core2", INTEL_CORE2)
+      .Case("corei7", INTEL_COREI7)
+      .Case("haswell", INTEL_COREI7_HASWELL)
+      .Case("intel", INTEL)
+      .Case("istanbul", AMDFAM10H_ISTANBUL)
+      .Case("ivybridge", INTEL_COREI7_IVYBRIDGE)
+      .Case("knl", INTEL_KNL)
+      .Case("nehalem", INTEL_COREI7_NEHALEM)
+      .Case("sandybridge", INTEL_COREI7_SANDYBRIDGE)
+      .Case("shanghai", AMDFAM10H_SHANGHAI)
+      .Case("silvermont", INTEL_SILVERMONT)
+      .Case("skylake", INTEL_COREI7_SKYLAKE)
+      .Case("skylake-avx512", INTEL_COREI7_SKYLAKE_AVX512)
+      .Case("slm", INTEL_SILVERMONT)
+      .Case("westmere", INTEL_COREI7_WESTMERE)
+      .Case("znver1", AMDFAM17H_ZNVER1);
+
+  llvm::Type *Int32Ty = CGF.Builder.getInt32Ty();
+
+  // Matching the struct layout from the compiler-rt/libgcc structure that is
+  // filled in:
+  // unsigned int __cpu_vendor;
+  // unsigned int __cpu_type;
+  // unsigned int __cpu_subtype;
+  // unsigned int __cpu_features[1];
+  llvm::Type *STy = llvm::StructType::get(Int32Ty, Int32Ty, Int32Ty,
+                                          llvm::ArrayType::get(Int32Ty, 1));
+
+  // Grab the global __cpu_model.
+  llvm::Constant *CpuModel = CGF.CGM.CreateRuntimeVariable(STy, "__cpu_model");
+
+  // Calculate the index needed to access the correct field based on the
+  // range. Also adjust the expected value.
+  unsigned Index;
+  unsigned Value;
+  if (CPU > CPU_SUBTYPE_START) {
+    Index = 2;
+    Value = CPU - CPU_SUBTYPE_START;
+  } else if (CPU > CPU_TYPE_START) {
+    Index = 1;
+    Value = CPU - CPU_TYPE_START;
+  } else {
+    Index = 0;
+    Value = CPU;
+  }
+
+  // Grab the appropriate field from __cpu_model.
+  llvm::Value *Idxs[] = {
+    ConstantInt::get(Int32Ty, 0),
+    ConstantInt::get(Int32Ty, Index)
+  };
+  llvm::Value *CpuValue = CGF.Builder.CreateGEP(STy, CpuModel, Idxs);
+  CpuValue = CGF.Builder.CreateAlignedLoad(CpuValue, CharUnits::fromQuantity(4));
+
+  // Check the value of the field against the requested value.
+  return CGF.Builder.CreateICmpEQ(CpuValue,
+                                  llvm::ConstantInt::get(Int32Ty, Value));
+}
+
 Value *CodeGenFunction::EmitX86BuiltinExpr(unsigned BuiltinID,
                                            const CallExpr *E) {
   if (BuiltinID == X86::BI__builtin_ms_va_start ||
@@ -7247,6 +7361,8 @@
     Value *ArgPtr = Builder.CreateLoad(SrcAddr, "ap.val");
     return Builder.CreateStore(ArgPtr, DestAddr);
   }
+  if (BuiltinID == X86::BI__builtin_cpu_is)
+    return EmitX86CpuIs(*this, E);
 
   SmallVector<Value*, 4> Ops;
 
Index: lib/Basic/Targets.cpp
===================================================================
--- lib/Basic/Targets.cpp
+++ lib/Basic/Targets.cpp
@@ -2974,6 +2974,7 @@
     return llvm::makeArrayRef(AddlRegNames);
   }
   bool validateCpuSupports(StringRef Name) const override;
+  bool validateCpuIs(StringRef Name) const override;
   bool validateAsmConstraint(const char *&Name,
                              TargetInfo::ConstraintInfo &info) const override;
 
@@ -4342,6 +4343,44 @@
       .Default(false);
 }
 
+// We can't use a generic validation scheme for the cpus accepted here
+// versus subtarget cpus accepted in the target attribute because the
+// variables intitialized by the runtime only support the below currently
+// rather than the full range of cpus.
+bool X86TargetInfo::validateCpuIs(StringRef FeatureStr) const {
+  return llvm::StringSwitch<bool>(FeatureStr)
+      .Case("amd", true)
+      .Case("amdfam10h", true)
+      .Case("amdfam15h", true)
+      .Case("atom", true)
+      .Case("barcelona", true)
+      .Case("bdver1", true)
+      .Case("bdver2", true)
+      .Case("bdver3", true)
+      .Case("bdver4", true)
+      .Case("bonnell", true)
+      .Case("broadwell", true)
+      .Case("btver1", true)
+      .Case("btver2", true)
+      .Case("core2", true)
+      .Case("corei7", true)
+      .Case("haswell", true)
+      .Case("intel", true)
+      .Case("istanbul", true)
+      .Case("ivybridge", true)
+      .Case("knl", true)
+      .Case("nehalem", true)
+      .Case("sandybridge", true)
+      .Case("shanghai", true)
+      .Case("silvermont", true)
+      .Case("skylake", true)
+      .Case("skylake-avx512", true)
+      .Case("slm", true)
+      .Case("westmere", true)
+      .Case("znver1", true)
+      .Default(false);
+}
+
 bool
 X86TargetInfo::validateAsmConstraint(const char *&Name,
                                      TargetInfo::ConstraintInfo &Info) const {
Index: include/clang/Basic/TargetInfo.h
===================================================================
--- include/clang/Basic/TargetInfo.h
+++ include/clang/Basic/TargetInfo.h
@@ -902,6 +902,10 @@
   // argument.
   virtual bool validateCpuSupports(StringRef Name) const { return false; }
 
+  // \brief Validate the contents of the __builtin_cpu_is(const char*)
+  // argument.
+  virtual bool validateCpuIs(StringRef Name) const { return false; }
+
   // \brief Returns maximal number of args passed in registers.
   unsigned getRegParmMax() const {
     assert(RegParmMax < 7 && "RegParmMax value is larger than AST can handle");
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -581,6 +581,7 @@
 def err_builtin_definition : Error<"definition of builtin function %0">;
 def err_arm_invalid_specialreg : Error<"invalid special register for builtin">;
 def err_invalid_cpu_supports : Error<"invalid cpu feature string for builtin">;
+def err_invalid_cpu_is : Error<"invalid cpu name for builtin">;
 def err_builtin_needs_feature : Error<"%0 needs target feature %1">;
 def err_function_needs_feature
     : Error<"always_inline function %1 requires target feature '%2', but would "
Index: include/clang/Basic/BuiltinsX86.def
===================================================================
--- include/clang/Basic/BuiltinsX86.def
+++ include/clang/Basic/BuiltinsX86.def
@@ -33,6 +33,7 @@
 // TODO: Make this somewhat generic so that other backends
 // can use it?
 BUILTIN(__builtin_cpu_supports, "bcC*", "nc")
+BUILTIN(__builtin_cpu_is, "bcC*", "nc")
 
 // Win64-compatible va_list functions
 BUILTIN(__builtin_ms_va_start, "vc*&.", "nt")
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to