diff options
| author | Eric Christopher <echristo@gmail.com> | 2015-06-29 21:00:05 +0000 |
|---|---|---|
| committer | Eric Christopher <echristo@gmail.com> | 2015-06-29 21:00:05 +0000 |
| commit | d983270976d24bd30737262f6aafbf362f9077c8 (patch) | |
| tree | 1afbc51f1b85cce678baaf84a0453986162ed339 | |
| parent | 63cacd7b7901d2ecf7541f62113c1c2252abbd8c (diff) | |
| download | bcm5719-llvm-d983270976d24bd30737262f6aafbf362f9077c8.tar.gz bcm5719-llvm-d983270976d24bd30737262f6aafbf362f9077c8.zip | |
Add support for the x86 builtin __builtin_cpu_supports.
This matches the implementation of the gcc support for the same
feature, including checking the values set up by libgcc at runtime.
The structure looks like this:
unsigned int __cpu_vendor;
unsigned int __cpu_type;
unsigned int __cpu_subtype;
unsigned int __cpu_features[1];
with a set of enums to match various fields that are field out after
parsing the output of the cpuid instruction.
This also adds a set of errors checking for valid input (and cpu).
compiler-rt support for this and the other builtins in this family
(__builtin_cpu_init and __builtin_cpu_is) are forthcoming.
llvm-svn: 240994
| -rw-r--r-- | clang/include/clang/Basic/BuiltinsX86.def | 5 | ||||
| -rw-r--r-- | clang/include/clang/Basic/DiagnosticSemaKinds.td | 1 | ||||
| -rw-r--r-- | clang/include/clang/Basic/TargetInfo.h | 3 | ||||
| -rw-r--r-- | clang/include/clang/Sema/Sema.h | 1 | ||||
| -rw-r--r-- | clang/lib/Basic/Targets.cpp | 28 | ||||
| -rw-r--r-- | clang/lib/CodeGen/CGBuiltin.cpp | 77 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaChecking.cpp | 22 | ||||
| -rw-r--r-- | clang/test/CodeGen/builtin-cpu-supports.c | 16 | ||||
| -rw-r--r-- | clang/test/Sema/builtin-cpu-supports.c | 21 |
9 files changed, 174 insertions, 0 deletions
diff --git a/clang/include/clang/Basic/BuiltinsX86.def b/clang/include/clang/Basic/BuiltinsX86.def index a0b5218d78c..aed206f4a52 100644 --- a/clang/include/clang/Basic/BuiltinsX86.def +++ b/clang/include/clang/Basic/BuiltinsX86.def @@ -24,6 +24,11 @@ // FIXME: Are these nothrow/const? +// Miscellaneous builtin for checking x86 cpu features. +// TODO: Make this somewhat generic so that other backends +// can use it? +BUILTIN(__builtin_cpu_supports, "bcC*", "nc") + // 3DNow! // BUILTIN(__builtin_ia32_femms, "v", "") diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 4448b3d7484..98d300ece7b 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -426,6 +426,7 @@ def warn_redecl_library_builtin : Warning< InGroup<DiagGroup<"incompatible-library-redeclaration">>; 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 warn_builtin_unknown : Warning<"use of unknown builtin %0">, InGroup<ImplicitFunctionDeclare>, DefaultError; def warn_dyn_class_memaccess : Warning< diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h index e415733189a..f8f710c4abe 100644 --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -611,6 +611,9 @@ public: } }; + // Validate the contents of the __builtin_cpu_supports(const char*) argument. + virtual bool validateCpuSupports(StringRef Name) const { return false; } + // validateOutputConstraint, validateInputConstraint - Checks that // a constraint is valid and provides information about it. // FIXME: These should return a real error instead of just true/false. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 85409117d9e..8794664afbd 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -8734,6 +8734,7 @@ private: bool SemaBuiltinARMSpecialReg(unsigned BuiltinID, CallExpr *TheCall, int ArgNum, unsigned ExpectedFieldNum, bool AllowName); + bool SemaBuiltinCpuSupports(CallExpr *TheCall); public: enum FormatStringType { FST_Scanf, diff --git a/clang/lib/Basic/Targets.cpp b/clang/lib/Basic/Targets.cpp index 16c678e9588..e496a1f6cd7 100644 --- a/clang/lib/Basic/Targets.cpp +++ b/clang/lib/Basic/Targets.cpp @@ -2215,6 +2215,7 @@ public: Names = AddlRegNames; NumNames = llvm::array_lengthof(AddlRegNames); } + bool validateCpuSupports(StringRef Name) const override; bool validateAsmConstraint(const char *&Name, TargetInfo::ConstraintInfo &info) const override; @@ -3338,6 +3339,33 @@ bool X86TargetInfo::hasFeature(StringRef Feature) const { .Default(false); } +// We can't use a generic validation scheme for the features accepted here +// versus subtarget features accepted in the target attribute because the +// bitfield structure that's initialized in the runtime only supports the +// below currently rather than the full range of subtarget features. (See +// X86TargetInfo::hasFeature for a somewhat comprehensive list). +bool X86TargetInfo::validateCpuSupports(StringRef FeatureStr) const { + return llvm::StringSwitch<bool>(FeatureStr) + .Case("cmov", true) + .Case("mmx", true) + .Case("popcnt", true) + .Case("sse", true) + .Case("sse2", true) + .Case("sse3", true) + .Case("sse4.1", true) + .Case("sse4.2", true) + .Case("avx", true) + .Case("avx2", true) + .Case("sse4a", true) + .Case("fma4", true) + .Case("xop", true) + .Case("fma", true) + .Case("avx512f", true) + .Case("bmi", true) + .Case("bmi2", true) + .Default(false); +} + bool X86TargetInfo::validateAsmConstraint(const char *&Name, TargetInfo::ConstraintInfo &Info) const { diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 5e58bcc2ce9..8bd80085e65 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -6101,6 +6101,83 @@ Value *CodeGenFunction::EmitX86BuiltinExpr(unsigned BuiltinID, switch (BuiltinID) { default: return nullptr; + case X86::BI__builtin_cpu_supports: { + const Expr *FeatureExpr = E->getArg(0)->IgnoreParenCasts(); + StringRef FeatureStr = cast<StringLiteral>(FeatureExpr)->getString(); + + // TODO: When/if this becomes more than x86 specific then use a TargetInfo + // based mapping. + // Processor features and mapping to processor feature value. + enum X86Features { + CMOV = 0, + MMX, + POPCNT, + SSE, + SSE2, + SSE3, + SSSE3, + SSE4_1, + SSE4_2, + AVX, + AVX2, + SSE4_A, + FMA4, + XOP, + FMA, + AVX512F, + BMI, + BMI2, + MAX + }; + + X86Features Feature = StringSwitch<X86Features>(FeatureStr) + .Case("cmov", X86Features::CMOV) + .Case("mmx", X86Features::MMX) + .Case("popcnt", X86Features::POPCNT) + .Case("sse", X86Features::SSE) + .Case("sse2", X86Features::SSE2) + .Case("sse3", X86Features::SSE3) + .Case("sse4.1", X86Features::SSE4_1) + .Case("sse4.2", X86Features::SSE4_2) + .Case("avx", X86Features::AVX) + .Case("avx2", X86Features::AVX2) + .Case("sse4a", X86Features::SSE4_A) + .Case("fma4", X86Features::FMA4) + .Case("xop", X86Features::XOP) + .Case("fma", X86Features::FMA) + .Case("avx512f", X86Features::AVX512F) + .Case("bmi", X86Features::BMI) + .Case("bmi2", X86Features::BMI2) + .Default(X86Features::MAX); + assert(Feature != X86Features::MAX && "Invalid feature!"); + + // 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), nullptr); + + // Grab the global __cpu_model. + llvm::Constant *CpuModel = CGM.CreateRuntimeVariable(STy, "__cpu_model"); + + // Grab the first (0th) element from the field __cpu_features off of the + // global in the struct STy. + Value *Idxs[] = { + ConstantInt::get(Int32Ty, 0), + ConstantInt::get(Int32Ty, 3), + ConstantInt::get(Int32Ty, 0) + }; + Value *CpuFeatures = Builder.CreateGEP(STy, CpuModel, Idxs); + Value *Features = Builder.CreateLoad(CpuFeatures); + + // Check the value of the bit corresponding to the feature requested. + Value *Bitset = Builder.CreateAnd( + Features, llvm::ConstantInt::get(Int32Ty, 1 << Feature)); + return Builder.CreateICmpNE(Bitset, llvm::ConstantInt::get(Int32Ty, 0)); + } case X86::BI_mm_prefetch: { Value *Address = EmitScalarExpr(E->getArg(0)); Value *RW = ConstantInt::get(Int32Ty, 0); diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index f76727cad88..870da782b97 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -1031,6 +1031,8 @@ bool Sema::CheckX86BuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { unsigned i = 0, l = 0, u = 0; switch (BuiltinID) { default: return false; + case X86::BI__builtin_cpu_supports: + return SemaBuiltinCpuSupports(TheCall); case X86::BI_mm_prefetch: i = 1; l = 0; u = 3; break; case X86::BI__builtin_ia32_sha1rnds4: i = 2, l = 0; u = 3; break; case X86::BI__builtin_ia32_vpermil2pd: @@ -2782,6 +2784,26 @@ bool Sema::SemaBuiltinARMSpecialReg(unsigned BuiltinID, CallExpr *TheCall, return false; } +/// SemaBuiltinCpuSupports - Handle __builtin_cpu_supports(char *). +/// This checks that the target supports __builtin_cpu_supports and +/// that the string argument is constant and valid. +bool Sema::SemaBuiltinCpuSupports(CallExpr *TheCall) { + Expr *Arg = TheCall->getArg(0); + + // Check if the argument is a string literal. + if (!isa<StringLiteral>(Arg->IgnoreParenImpCasts())) + return 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 (!Context.getTargetInfo().validateCpuSupports(Feature)) + return Diag(TheCall->getLocStart(), diag::err_invalid_cpu_supports) + << Arg->getSourceRange(); + return false; +} + /// SemaBuiltinLongjmp - Handle __builtin_longjmp(void *env[5], int val). /// This checks that the target supports __builtin_longjmp and /// that val is a constant 1. diff --git a/clang/test/CodeGen/builtin-cpu-supports.c b/clang/test/CodeGen/builtin-cpu-supports.c new file mode 100644 index 00000000000..2252b3e2b8c --- /dev/null +++ b/clang/test/CodeGen/builtin-cpu-supports.c @@ -0,0 +1,16 @@ +// 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 *); + +int main() { + if (__builtin_cpu_supports("sse4.2")) + a("sse4.2"); + + // CHECK: [[LOAD:%[^ ]+]] = load i32, i32* getelementptr inbounds ({ i32, i32, i32, [1 x i32] }, { i32, i32, i32, [1 x i32] }* @__cpu_model, i32 0, i32 3, i32 0) + // CHECK: [[AND:%[^ ]+]] = and i32 [[LOAD]], 256 + // CHECK = icmp ne i32 [[AND]], 0 + + return 0; +} diff --git a/clang/test/Sema/builtin-cpu-supports.c b/clang/test/Sema/builtin-cpu-supports.c new file mode 100644 index 00000000000..c537b4140b2 --- /dev/null +++ b/clang/test/Sema/builtin-cpu-supports.c @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -fsyntax-only -triple x86_64-pc-linux-gnu -verify %s +// RUN: %clang_cc1 -fsyntax-only -triple powerpc64le-linux-gnu -verify %s + +extern void a(const char *); + +extern const char *str; + +int main() { +#ifdef __x86_64__ + if (__builtin_cpu_supports("ss")) // expected-error {{invalid cpu feature string}} + a("sse4.2"); + + if (__builtin_cpu_supports(str)) // expected-error {{expression is not a string literal}} + a(str); +#else + if (__builtin_cpu_supports("vsx")) // expected-error {{use of unknown builtin}} + a("vsx"); +#endif + + return 0; +} |

