Skip to content

Commit 8cd8bd4

Browse files
alanzhao1zmodem
authored andcommitted
Implement __cpuid and __cpuidex as Clang builtins
https://reviews.llvm.org/D23944 implemented the #pragma intrinsic from MSVC. This causes the statement #pragma intrinsic(cpuid) to fail [0] on Clang because cpuid is currently implemented in intrin.h instead of a Clang builtin. Reimplementing cpuid (as well as it's releated function, cpuidex) should resolve this. [0]: https://crbug.com/1279344 Differential revision: https://reviews.llvm.org/D121653
1 parent a014cb8 commit 8cd8bd4

File tree

4 files changed

+110
-31
lines changed

4 files changed

+110
-31
lines changed

clang/include/clang/Basic/BuiltinsX86.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2071,6 +2071,9 @@ TARGET_HEADER_BUILTIN(_ReadWriteBarrier, "v", "nh", "intrin.h", ALL_MS_LANGUAGES
20712071
TARGET_HEADER_BUILTIN(_ReadBarrier, "v", "nh", "intrin.h", ALL_MS_LANGUAGES, "")
20722072
TARGET_HEADER_BUILTIN(_WriteBarrier, "v", "nh", "intrin.h", ALL_MS_LANGUAGES, "")
20732073

2074+
TARGET_HEADER_BUILTIN(__cpuid, "vi*i", "nh", "intrin.h", ALL_MS_LANGUAGES, "")
2075+
TARGET_HEADER_BUILTIN(__cpuidex, "vi*ii", "nh", "intrin.h", ALL_MS_LANGUAGES, "")
2076+
20742077
TARGET_HEADER_BUILTIN(__emul, "LLiii", "nch", "intrin.h", ALL_MS_LANGUAGES, "")
20752078
TARGET_HEADER_BUILTIN(__emulu, "ULLiUiUi", "nch", "intrin.h", ALL_MS_LANGUAGES, "")
20762079

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14922,6 +14922,46 @@ Value *CodeGenFunction::EmitX86BuiltinExpr(unsigned BuiltinID,
1492214922
return EmitX86Select(*this, Ops[2], Res, Ops[1]);
1492314923
}
1492414924

14925+
case X86::BI__cpuid:
14926+
case X86::BI__cpuidex: {
14927+
Value *FuncId = EmitScalarExpr(E->getArg(1));
14928+
Value *SubFuncId = BuiltinID == X86::BI__cpuidex
14929+
? EmitScalarExpr(E->getArg(2))
14930+
: llvm::ConstantInt::get(Int32Ty, 0);
14931+
14932+
llvm::StructType *CpuidRetTy =
14933+
llvm::StructType::get(Int32Ty, Int32Ty, Int32Ty, Int32Ty);
14934+
llvm::FunctionType *FTy =
14935+
llvm::FunctionType::get(CpuidRetTy, {Int32Ty, Int32Ty}, false);
14936+
14937+
StringRef Asm, Constraints;
14938+
if (getTarget().getTriple().getArch() == llvm::Triple::x86) {
14939+
Asm = "cpuid";
14940+
Constraints = "={ax},={bx},={cx},={dx},{ax},{cx}";
14941+
} else {
14942+
// x86-64 uses %rbx as the base register, so preserve it.
14943+
Asm = "xchgq %rbx, ${1:q}\n"
14944+
"cpuid\n"
14945+
"xchgq %rbx, ${1:q}";
14946+
Constraints = "={ax},=r,={cx},={dx},0,2";
14947+
}
14948+
14949+
llvm::InlineAsm *IA = llvm::InlineAsm::get(FTy, Asm, Constraints,
14950+
/*hasSideEffects=*/false);
14951+
Value *IACall = Builder.CreateCall(IA, {FuncId, SubFuncId});
14952+
Value *BasePtr = EmitScalarExpr(E->getArg(0));
14953+
Value *Store = nullptr;
14954+
for (unsigned i = 0; i < 4; i++) {
14955+
Value *Extracted = Builder.CreateExtractValue(IACall, i);
14956+
Value *StorePtr = Builder.CreateConstInBoundsGEP1_32(Int32Ty, BasePtr, i);
14957+
Store = Builder.CreateAlignedStore(Extracted, StorePtr, getIntAlign());
14958+
}
14959+
14960+
// Return the last store instruction to signal that we have emitted the
14961+
// the intrinsic.
14962+
return Store;
14963+
}
14964+
1492514965
case X86::BI__emul:
1492614966
case X86::BI__emulu: {
1492714967
llvm::Type *Int64Ty = llvm::IntegerType::get(getLLVMContext(), 64);

clang/lib/Headers/intrin.h

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -534,27 +534,6 @@ static __inline__ void __DEFAULT_FN_ATTRS __stosq(unsigned __int64 *__dst,
534534
|* Misc
535535
\*----------------------------------------------------------------------------*/
536536
#if defined(__i386__) || defined(__x86_64__)
537-
#if defined(__i386__)
538-
#define __cpuid_count(__leaf, __count, __eax, __ebx, __ecx, __edx) \
539-
__asm("cpuid" \
540-
: "=a"(__eax), "=b"(__ebx), "=c"(__ecx), "=d"(__edx) \
541-
: "0"(__leaf), "2"(__count))
542-
#else
543-
/* x86-64 uses %rbx as the base register, so preserve it. */
544-
#define __cpuid_count(__leaf, __count, __eax, __ebx, __ecx, __edx) \
545-
__asm("xchg{q} {%%rbx, %q1|%q1, rbx}\n" \
546-
"cpuid\n" \
547-
"xchg{q} {%%rbx, %q1|%q1, rbx}" \
548-
: "=a"(__eax), "=r"(__ebx), "=c"(__ecx), "=d"(__edx) \
549-
: "0"(__leaf), "2"(__count))
550-
#endif
551-
static __inline__ void __DEFAULT_FN_ATTRS __cpuid(int __info[4], int __level) {
552-
__cpuid_count(__level, 0, __info[0], __info[1], __info[2], __info[3]);
553-
}
554-
static __inline__ void __DEFAULT_FN_ATTRS __cpuidex(int __info[4], int __level,
555-
int __ecx) {
556-
__cpuid_count(__level, __ecx, __info[0], __info[1], __info[2], __info[3]);
557-
}
558537
static __inline__ void __DEFAULT_FN_ATTRS __halt(void) {
559538
__asm__ volatile("hlt");
560539
}
Lines changed: 67 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,80 @@
11
// RUN: %clang_cc1 -ffreestanding -fms-extensions -fms-compatibility -fms-compatibility-version=17.00 \
2-
// RUN: -triple i686-windows-msvc -emit-llvm %s -o - | FileCheck %s --check-prefix=X86
2+
// RUN: -Werror -triple i686-windows-msvc -emit-llvm %s -o - | FileCheck %s --check-prefix=X86
33
// RUN: %clang_cc1 -ffreestanding -fms-extensions -fms-compatibility -fms-compatibility-version=17.00 \
4-
// RUN: -triple x86_64-windows-msvc -emit-llvm %s -o - | FileCheck %s --check-prefix=X64
4+
// RUN: -Werror -triple x86_64-windows-msvc -emit-llvm %s -o - | FileCheck %s --check-prefix=X64
55

66
// intrin.h needs size_t, but -ffreestanding prevents us from getting it from
77
// stddef.h. Work around it with this typedef.
88
typedef __SIZE_TYPE__ size_t;
99

1010
#include <intrin.h>
1111

12-
void test__cpuid(int *info, int level) {
13-
__cpuid(info, level);
12+
#pragma intrinsic(__cpuid)
13+
14+
void test__cpuid(int cpuInfo[4], int function_id) {
15+
__cpuid(cpuInfo, function_id);
1416
}
1517
// X86-LABEL: define {{.*}} @test__cpuid(i32* noundef %{{.*}}, i32 noundef %{{.*}})
16-
// X86: call { i32, i32, i32, i32 } asm "cpuid",
17-
// X86-SAME: "={ax},={bx},={cx},={dx},0,2,~{dirflag},~{fpsr},~{flags}"
18-
// X86-SAME: (i32 %{{.*}}, i32 0)
18+
// X86-DAG: [[ASMRESULTS:%[0-9]+]] = call { i32, i32, i32, i32 } asm "cpuid", "={ax},={bx},={cx},={dx},{ax},{cx}"
19+
// X86-DAG: [[ADDRPTR0:%[0-9]+]] = getelementptr inbounds i32, i32* %{{.*}}, i32 0
20+
// X86-DAG: [[ADDRPTR1:%[0-9]+]] = getelementptr inbounds i32, i32* %{{.*}}, i32 1
21+
// X86-DAG: [[ADDRPTR2:%[0-9]+]] = getelementptr inbounds i32, i32* %{{.*}}, i32 2
22+
// X86-DAG: [[ADDRPTR3:%[0-9]+]] = getelementptr inbounds i32, i32* %{{.*}}, i32 3
23+
// X86-DAG: [[RESULT0:%[0-9]+]] = extractvalue { i32, i32, i32, i32 } [[ASMRESULTS]], 0
24+
// X86-DAG: [[RESULT1:%[0-9]+]] = extractvalue { i32, i32, i32, i32 } [[ASMRESULTS]], 1
25+
// X86-DAG: [[RESULT2:%[0-9]+]] = extractvalue { i32, i32, i32, i32 } [[ASMRESULTS]], 2
26+
// X86-DAG: [[RESULT3:%[0-9]+]] = extractvalue { i32, i32, i32, i32 } [[ASMRESULTS]], 3
27+
// X86-DAG: store i32 [[RESULT0]], i32* [[ADDRPTR0]], align 4
28+
// X86-DAG: store i32 [[RESULT1]], i32* [[ADDRPTR1]], align 4
29+
// X86-DAG: store i32 [[RESULT2]], i32* [[ADDRPTR2]], align 4
30+
// X86-DAG: store i32 [[RESULT3]], i32* [[ADDRPTR3]], align 4
1931

2032
// X64-LABEL: define {{.*}} @test__cpuid(i32* noundef %{{.*}}, i32 noundef %{{.*}})
21-
// X64: call { i32, i32, i32, i32 } asm "xchg$(q$) $(%rbx{{.*}}$){{.*}}cpuid{{.*}}xchg$(q$) $(%rbx{{.*}}$)",
22-
// X64-SAME: "={ax},=r,={cx},={dx},0,2,~{dirflag},~{fpsr},~{flags}"
23-
// X64-SAME: (i32 %{{.*}}, i32 0)
33+
// X64-DAG: [[ASMRESULTS:%[0-9]+]] = call { i32, i32, i32, i32 } asm "xchgq %rbx, ${1:q}\0Acpuid\0Axchgq %rbx, ${1:q}", "={ax},=r,={cx},={dx},0,2"
34+
// X64-DAG: [[ADDRPTR0:%[0-9]+]] = getelementptr inbounds i32, i32* %{{.*}}, i32 0
35+
// X64-DAG: [[ADDRPTR1:%[0-9]+]] = getelementptr inbounds i32, i32* %{{.*}}, i32 1
36+
// X64-DAG: [[ADDRPTR2:%[0-9]+]] = getelementptr inbounds i32, i32* %{{.*}}, i32 2
37+
// X64-DAG: [[ADDRPTR3:%[0-9]+]] = getelementptr inbounds i32, i32* %{{.*}}, i32 3
38+
// X64-DAG: [[RESULT0:%[0-9]+]] = extractvalue { i32, i32, i32, i32 } [[ASMRESULTS]], 0
39+
// X64-DAG: [[RESULT1:%[0-9]+]] = extractvalue { i32, i32, i32, i32 } [[ASMRESULTS]], 1
40+
// X64-DAG: [[RESULT2:%[0-9]+]] = extractvalue { i32, i32, i32, i32 } [[ASMRESULTS]], 2
41+
// X64-DAG: [[RESULT3:%[0-9]+]] = extractvalue { i32, i32, i32, i32 } [[ASMRESULTS]], 3
42+
// X64-DAG: store i32 [[RESULT0]], i32* [[ADDRPTR0]], align 4
43+
// X64-DAG: store i32 [[RESULT1]], i32* [[ADDRPTR1]], align 4
44+
// X64-DAG: store i32 [[RESULT2]], i32* [[ADDRPTR2]], align 4
45+
// X64-DAG: store i32 [[RESULT3]], i32* [[ADDRPTR3]], align 4
46+
47+
#pragma intrinsic(__cpuidex)
48+
49+
void test__cpuidex(int cpuInfo[4], int function_id, int subfunction_id) {
50+
__cpuidex(cpuInfo, function_id, subfunction_id);
51+
}
52+
// X86-LABEL: define {{.*}} @test__cpuidex(i32* noundef %{{.*}}, i32 noundef %{{.*}}, i32 noundef %{{.*}})
53+
// X86-DAG: [[ASMRESULTS:%[0-9]+]] = call { i32, i32, i32, i32 } asm "cpuid", "={ax},={bx},={cx},={dx},{ax},{cx}"
54+
// X86-DAG: [[ADDRPTR0:%[0-9]+]] = getelementptr inbounds i32, i32* %{{.*}}, i32 0
55+
// X86-DAG: [[ADDRPTR1:%[0-9]+]] = getelementptr inbounds i32, i32* %{{.*}}, i32 1
56+
// X86-DAG: [[ADDRPTR2:%[0-9]+]] = getelementptr inbounds i32, i32* %{{.*}}, i32 2
57+
// X86-DAG: [[ADDRPTR3:%[0-9]+]] = getelementptr inbounds i32, i32* %{{.*}}, i32 3
58+
// X86-DAG: [[RESULT0:%[0-9]+]] = extractvalue { i32, i32, i32, i32 } [[ASMRESULTS]], 0
59+
// X86-DAG: [[RESULT1:%[0-9]+]] = extractvalue { i32, i32, i32, i32 } [[ASMRESULTS]], 1
60+
// X86-DAG: [[RESULT2:%[0-9]+]] = extractvalue { i32, i32, i32, i32 } [[ASMRESULTS]], 2
61+
// X86-DAG: [[RESULT3:%[0-9]+]] = extractvalue { i32, i32, i32, i32 } [[ASMRESULTS]], 3
62+
// X86-DAG: store i32 [[RESULT0]], i32* [[ADDRPTR0]], align 4
63+
// X86-DAG: store i32 [[RESULT1]], i32* [[ADDRPTR1]], align 4
64+
// X86-DAG: store i32 [[RESULT2]], i32* [[ADDRPTR2]], align 4
65+
// X86-DAG: store i32 [[RESULT3]], i32* [[ADDRPTR3]], align 4
66+
67+
// X64-LABEL: define {{.*}} @test__cpuidex(i32* noundef %{{.*}}, i32 noundef %{{.*}}, i32 noundef %{{.*}})
68+
// X64-DAG: [[ASMRESULTS:%[0-9]+]] = call { i32, i32, i32, i32 } asm "xchgq %rbx, ${1:q}\0Acpuid\0Axchgq %rbx, ${1:q}", "={ax},=r,={cx},={dx},0,2"
69+
// X64-DAG: [[ADDRPTR0:%[0-9]+]] = getelementptr inbounds i32, i32* %{{.*}}, i32 0
70+
// X64-DAG: [[ADDRPTR1:%[0-9]+]] = getelementptr inbounds i32, i32* %{{.*}}, i32 1
71+
// X64-DAG: [[ADDRPTR2:%[0-9]+]] = getelementptr inbounds i32, i32* %{{.*}}, i32 2
72+
// X64-DAG: [[ADDRPTR3:%[0-9]+]] = getelementptr inbounds i32, i32* %{{.*}}, i32 3
73+
// X64-DAG: [[RESULT0:%[0-9]+]] = extractvalue { i32, i32, i32, i32 } [[ASMRESULTS]], 0
74+
// X64-DAG: [[RESULT1:%[0-9]+]] = extractvalue { i32, i32, i32, i32 } [[ASMRESULTS]], 1
75+
// X64-DAG: [[RESULT2:%[0-9]+]] = extractvalue { i32, i32, i32, i32 } [[ASMRESULTS]], 2
76+
// X64-DAG: [[RESULT3:%[0-9]+]] = extractvalue { i32, i32, i32, i32 } [[ASMRESULTS]], 3
77+
// X64-DAG: store i32 [[RESULT0]], i32* [[ADDRPTR0]], align 4
78+
// X64-DAG: store i32 [[RESULT1]], i32* [[ADDRPTR1]], align 4
79+
// X64-DAG: store i32 [[RESULT2]], i32* [[ADDRPTR2]], align 4
80+
// X64-DAG: store i32 [[RESULT3]], i32* [[ADDRPTR3]], align 4

0 commit comments

Comments
 (0)