Skip to content

Commit

Permalink
Add support for the x86 builtin __builtin_cpu_supports.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
echristo committed Jun 29, 2015
1 parent 63cacd7 commit d983270
Show file tree
Hide file tree
Showing 9 changed files with 174 additions and 0 deletions.
5 changes: 5 additions & 0 deletions clang/include/clang/Basic/BuiltinsX86.def
Original file line number Diff line number Diff line change
Expand Up @@ -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", "")
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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<
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/TargetInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,9 @@ class TargetInfo : public RefCountedBase<TargetInfo> {
}
};

// 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.
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -8734,6 +8734,7 @@ class Sema {
bool SemaBuiltinARMSpecialReg(unsigned BuiltinID, CallExpr *TheCall,
int ArgNum, unsigned ExpectedFieldNum,
bool AllowName);
bool SemaBuiltinCpuSupports(CallExpr *TheCall);
public:
enum FormatStringType {
FST_Scanf,
Expand Down
28 changes: 28 additions & 0 deletions clang/lib/Basic/Targets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2215,6 +2215,7 @@ class X86TargetInfo : public TargetInfo {
Names = AddlRegNames;
NumNames = llvm::array_lengthof(AddlRegNames);
}
bool validateCpuSupports(StringRef Name) const override;
bool validateAsmConstraint(const char *&Name,
TargetInfo::ConstraintInfo &info) const override;

Expand Down Expand Up @@ -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 {
Expand Down
77 changes: 77 additions & 0 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
22 changes: 22 additions & 0 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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.
Expand Down
16 changes: 16 additions & 0 deletions clang/test/CodeGen/builtin-cpu-supports.c
Original file line number Diff line number Diff line change
@@ -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;
}
21 changes: 21 additions & 0 deletions clang/test/Sema/builtin-cpu-supports.c
Original file line number Diff line number Diff line change
@@ -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;
}

0 comments on commit d983270

Please sign in to comment.