Skip to content

Commit d983270

Browse files
committed
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
1 parent 63cacd7 commit d983270

File tree

9 files changed

+174
-0
lines changed

9 files changed

+174
-0
lines changed

clang/include/clang/Basic/BuiltinsX86.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@
2424

2525
// FIXME: Are these nothrow/const?
2626

27+
// Miscellaneous builtin for checking x86 cpu features.
28+
// TODO: Make this somewhat generic so that other backends
29+
// can use it?
30+
BUILTIN(__builtin_cpu_supports, "bcC*", "nc")
31+
2732
// 3DNow!
2833
//
2934
BUILTIN(__builtin_ia32_femms, "v", "")

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,7 @@ def warn_redecl_library_builtin : Warning<
426426
InGroup<DiagGroup<"incompatible-library-redeclaration">>;
427427
def err_builtin_definition : Error<"definition of builtin function %0">;
428428
def err_arm_invalid_specialreg : Error<"invalid special register for builtin">;
429+
def err_invalid_cpu_supports : Error<"invalid cpu feature string for builtin">;
429430
def warn_builtin_unknown : Warning<"use of unknown builtin %0">,
430431
InGroup<ImplicitFunctionDeclare>, DefaultError;
431432
def warn_dyn_class_memaccess : Warning<

clang/include/clang/Basic/TargetInfo.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,9 @@ class TargetInfo : public RefCountedBase<TargetInfo> {
611611
}
612612
};
613613

614+
// Validate the contents of the __builtin_cpu_supports(const char*) argument.
615+
virtual bool validateCpuSupports(StringRef Name) const { return false; }
616+
614617
// validateOutputConstraint, validateInputConstraint - Checks that
615618
// a constraint is valid and provides information about it.
616619
// FIXME: These should return a real error instead of just true/false.

clang/include/clang/Sema/Sema.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8734,6 +8734,7 @@ class Sema {
87348734
bool SemaBuiltinARMSpecialReg(unsigned BuiltinID, CallExpr *TheCall,
87358735
int ArgNum, unsigned ExpectedFieldNum,
87368736
bool AllowName);
8737+
bool SemaBuiltinCpuSupports(CallExpr *TheCall);
87378738
public:
87388739
enum FormatStringType {
87398740
FST_Scanf,

clang/lib/Basic/Targets.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2215,6 +2215,7 @@ class X86TargetInfo : public TargetInfo {
22152215
Names = AddlRegNames;
22162216
NumNames = llvm::array_lengthof(AddlRegNames);
22172217
}
2218+
bool validateCpuSupports(StringRef Name) const override;
22182219
bool validateAsmConstraint(const char *&Name,
22192220
TargetInfo::ConstraintInfo &info) const override;
22202221

@@ -3338,6 +3339,33 @@ bool X86TargetInfo::hasFeature(StringRef Feature) const {
33383339
.Default(false);
33393340
}
33403341

3342+
// We can't use a generic validation scheme for the features accepted here
3343+
// versus subtarget features accepted in the target attribute because the
3344+
// bitfield structure that's initialized in the runtime only supports the
3345+
// below currently rather than the full range of subtarget features. (See
3346+
// X86TargetInfo::hasFeature for a somewhat comprehensive list).
3347+
bool X86TargetInfo::validateCpuSupports(StringRef FeatureStr) const {
3348+
return llvm::StringSwitch<bool>(FeatureStr)
3349+
.Case("cmov", true)
3350+
.Case("mmx", true)
3351+
.Case("popcnt", true)
3352+
.Case("sse", true)
3353+
.Case("sse2", true)
3354+
.Case("sse3", true)
3355+
.Case("sse4.1", true)
3356+
.Case("sse4.2", true)
3357+
.Case("avx", true)
3358+
.Case("avx2", true)
3359+
.Case("sse4a", true)
3360+
.Case("fma4", true)
3361+
.Case("xop", true)
3362+
.Case("fma", true)
3363+
.Case("avx512f", true)
3364+
.Case("bmi", true)
3365+
.Case("bmi2", true)
3366+
.Default(false);
3367+
}
3368+
33413369
bool
33423370
X86TargetInfo::validateAsmConstraint(const char *&Name,
33433371
TargetInfo::ConstraintInfo &Info) const {

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6101,6 +6101,83 @@ Value *CodeGenFunction::EmitX86BuiltinExpr(unsigned BuiltinID,
61016101

61026102
switch (BuiltinID) {
61036103
default: return nullptr;
6104+
case X86::BI__builtin_cpu_supports: {
6105+
const Expr *FeatureExpr = E->getArg(0)->IgnoreParenCasts();
6106+
StringRef FeatureStr = cast<StringLiteral>(FeatureExpr)->getString();
6107+
6108+
// TODO: When/if this becomes more than x86 specific then use a TargetInfo
6109+
// based mapping.
6110+
// Processor features and mapping to processor feature value.
6111+
enum X86Features {
6112+
CMOV = 0,
6113+
MMX,
6114+
POPCNT,
6115+
SSE,
6116+
SSE2,
6117+
SSE3,
6118+
SSSE3,
6119+
SSE4_1,
6120+
SSE4_2,
6121+
AVX,
6122+
AVX2,
6123+
SSE4_A,
6124+
FMA4,
6125+
XOP,
6126+
FMA,
6127+
AVX512F,
6128+
BMI,
6129+
BMI2,
6130+
MAX
6131+
};
6132+
6133+
X86Features Feature = StringSwitch<X86Features>(FeatureStr)
6134+
.Case("cmov", X86Features::CMOV)
6135+
.Case("mmx", X86Features::MMX)
6136+
.Case("popcnt", X86Features::POPCNT)
6137+
.Case("sse", X86Features::SSE)
6138+
.Case("sse2", X86Features::SSE2)
6139+
.Case("sse3", X86Features::SSE3)
6140+
.Case("sse4.1", X86Features::SSE4_1)
6141+
.Case("sse4.2", X86Features::SSE4_2)
6142+
.Case("avx", X86Features::AVX)
6143+
.Case("avx2", X86Features::AVX2)
6144+
.Case("sse4a", X86Features::SSE4_A)
6145+
.Case("fma4", X86Features::FMA4)
6146+
.Case("xop", X86Features::XOP)
6147+
.Case("fma", X86Features::FMA)
6148+
.Case("avx512f", X86Features::AVX512F)
6149+
.Case("bmi", X86Features::BMI)
6150+
.Case("bmi2", X86Features::BMI2)
6151+
.Default(X86Features::MAX);
6152+
assert(Feature != X86Features::MAX && "Invalid feature!");
6153+
6154+
// Matching the struct layout from the compiler-rt/libgcc structure that is
6155+
// filled in:
6156+
// unsigned int __cpu_vendor;
6157+
// unsigned int __cpu_type;
6158+
// unsigned int __cpu_subtype;
6159+
// unsigned int __cpu_features[1];
6160+
llvm::Type *STy = llvm::StructType::get(
6161+
Int32Ty, Int32Ty, Int32Ty, llvm::ArrayType::get(Int32Ty, 1), nullptr);
6162+
6163+
// Grab the global __cpu_model.
6164+
llvm::Constant *CpuModel = CGM.CreateRuntimeVariable(STy, "__cpu_model");
6165+
6166+
// Grab the first (0th) element from the field __cpu_features off of the
6167+
// global in the struct STy.
6168+
Value *Idxs[] = {
6169+
ConstantInt::get(Int32Ty, 0),
6170+
ConstantInt::get(Int32Ty, 3),
6171+
ConstantInt::get(Int32Ty, 0)
6172+
};
6173+
Value *CpuFeatures = Builder.CreateGEP(STy, CpuModel, Idxs);
6174+
Value *Features = Builder.CreateLoad(CpuFeatures);
6175+
6176+
// Check the value of the bit corresponding to the feature requested.
6177+
Value *Bitset = Builder.CreateAnd(
6178+
Features, llvm::ConstantInt::get(Int32Ty, 1 << Feature));
6179+
return Builder.CreateICmpNE(Bitset, llvm::ConstantInt::get(Int32Ty, 0));
6180+
}
61046181
case X86::BI_mm_prefetch: {
61056182
Value *Address = EmitScalarExpr(E->getArg(0));
61066183
Value *RW = ConstantInt::get(Int32Ty, 0);

clang/lib/Sema/SemaChecking.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,6 +1031,8 @@ bool Sema::CheckX86BuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
10311031
unsigned i = 0, l = 0, u = 0;
10321032
switch (BuiltinID) {
10331033
default: return false;
1034+
case X86::BI__builtin_cpu_supports:
1035+
return SemaBuiltinCpuSupports(TheCall);
10341036
case X86::BI_mm_prefetch: i = 1; l = 0; u = 3; break;
10351037
case X86::BI__builtin_ia32_sha1rnds4: i = 2, l = 0; u = 3; break;
10361038
case X86::BI__builtin_ia32_vpermil2pd:
@@ -2782,6 +2784,26 @@ bool Sema::SemaBuiltinARMSpecialReg(unsigned BuiltinID, CallExpr *TheCall,
27822784
return false;
27832785
}
27842786

2787+
/// SemaBuiltinCpuSupports - Handle __builtin_cpu_supports(char *).
2788+
/// This checks that the target supports __builtin_cpu_supports and
2789+
/// that the string argument is constant and valid.
2790+
bool Sema::SemaBuiltinCpuSupports(CallExpr *TheCall) {
2791+
Expr *Arg = TheCall->getArg(0);
2792+
2793+
// Check if the argument is a string literal.
2794+
if (!isa<StringLiteral>(Arg->IgnoreParenImpCasts()))
2795+
return Diag(TheCall->getLocStart(), diag::err_expr_not_string_literal)
2796+
<< Arg->getSourceRange();
2797+
2798+
// Check the contents of the string.
2799+
StringRef Feature =
2800+
cast<StringLiteral>(Arg->IgnoreParenImpCasts())->getString();
2801+
if (!Context.getTargetInfo().validateCpuSupports(Feature))
2802+
return Diag(TheCall->getLocStart(), diag::err_invalid_cpu_supports)
2803+
<< Arg->getSourceRange();
2804+
return false;
2805+
}
2806+
27852807
/// SemaBuiltinLongjmp - Handle __builtin_longjmp(void *env[5], int val).
27862808
/// This checks that the target supports __builtin_longjmp and
27872809
/// that val is a constant 1.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm < %s| FileCheck %s
2+
3+
// Test that we have the structure definition, the gep offsets, the name of the
4+
// global, the bit grab, and the icmp correct.
5+
extern void a(const char *);
6+
7+
int main() {
8+
if (__builtin_cpu_supports("sse4.2"))
9+
a("sse4.2");
10+
11+
// 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)
12+
// CHECK: [[AND:%[^ ]+]] = and i32 [[LOAD]], 256
13+
// CHECK = icmp ne i32 [[AND]], 0
14+
15+
return 0;
16+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %clang_cc1 -fsyntax-only -triple x86_64-pc-linux-gnu -verify %s
2+
// RUN: %clang_cc1 -fsyntax-only -triple powerpc64le-linux-gnu -verify %s
3+
4+
extern void a(const char *);
5+
6+
extern const char *str;
7+
8+
int main() {
9+
#ifdef __x86_64__
10+
if (__builtin_cpu_supports("ss")) // expected-error {{invalid cpu feature string}}
11+
a("sse4.2");
12+
13+
if (__builtin_cpu_supports(str)) // expected-error {{expression is not a string literal}}
14+
a(str);
15+
#else
16+
if (__builtin_cpu_supports("vsx")) // expected-error {{use of unknown builtin}}
17+
a("vsx");
18+
#endif
19+
20+
return 0;
21+
}

0 commit comments

Comments
 (0)