[X86] Implement __builtin_cpu_is

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

Differential Revision: https://reviews.llvm.org/D35449

llvm-svn: 310657
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 1a9ff26..2c8a6e0 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -7287,8 +7287,125 @@
   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_cpu_is)
+    return EmitX86CpuIs(*this, E);
+
   SmallVector<Value*, 4> Ops;
 
   // Find out if any arguments are required to be integer constant expressions.