diff --git a/clang/lib/Headers/intrin.h b/clang/lib/Headers/intrin.h --- a/clang/lib/Headers/intrin.h +++ b/clang/lib/Headers/intrin.h @@ -507,16 +507,26 @@ |* Misc \*----------------------------------------------------------------------------*/ #if defined(__i386__) || defined(__x86_64__) +#if defined(__i386__) +#define __cpuid_count(__leaf, __count, __eax, __ebx, __ecx, __edx) \ + __asm("cpuid" \ + : "=a"(__eax), "=b"(__ebx), "=c"(__ecx), "=d"(__edx) \ + : "0"(__leaf), "2"(__count)) +#else +/* x86-64 uses %rbx as the base register, so preserve it. */ +#define __cpuid_count(__leaf, __count, __eax, __ebx, __ecx, __edx) \ + __asm("xchgq %%rbx,%q1\n" \ + "cpuid\n" \ + "xchgq %%rbx,%q1" \ + : "=a"(__eax), "=r"(__ebx), "=c"(__ecx), "=d"(__edx) \ + : "0"(__leaf), "2"(__count)) +#endif static __inline__ void __DEFAULT_FN_ATTRS __cpuid(int __info[4], int __level) { - __asm__("cpuid" - : "=a"(__info[0]), "=b"(__info[1]), "=c"(__info[2]), "=d"(__info[3]) - : "a"(__level), "c"(0)); + __cpuid_count(__level, 0, __info[0], __info[1], __info[2], __info[3]); } static __inline__ void __DEFAULT_FN_ATTRS __cpuidex(int __info[4], int __level, int __ecx) { - __asm__("cpuid" - : "=a"(__info[0]), "=b"(__info[1]), "=c"(__info[2]), "=d"(__info[3]) - : "a"(__level), "c"(__ecx)); + __cpuid_count(__level, __ecx, __info[0], __info[1], __info[2], __info[3]); } static __inline__ void __DEFAULT_FN_ATTRS __halt(void) { __asm__ volatile("hlt"); diff --git a/clang/test/CodeGen/ms-intrinsics-cpuid.c b/clang/test/CodeGen/ms-intrinsics-cpuid.c --- a/clang/test/CodeGen/ms-intrinsics-cpuid.c +++ b/clang/test/CodeGen/ms-intrinsics-cpuid.c @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -ffreestanding -fms-extensions -fms-compatibility -fms-compatibility-version=17.00 \ -// RUN: -triple i686-windows-msvc -emit-llvm %s -o - | FileCheck %s +// RUN: -triple i686-windows-msvc -emit-llvm %s -o - | FileCheck %s --check-prefix=X86 // RUN: %clang_cc1 -ffreestanding -fms-extensions -fms-compatibility -fms-compatibility-version=17.00 \ -// RUN: -triple x86_64-windows-msvc -emit-llvm %s -o - | FileCheck %s +// RUN: -triple x86_64-windows-msvc -emit-llvm %s -o - | FileCheck %s --check-prefix=X64 // intrin.h needs size_t, but -ffreestanding prevents us from getting it from // stddef.h. Work around it with this typedef. @@ -12,7 +12,12 @@ void test__cpuid(int *info, int level) { __cpuid(info, level); } -// CHECK-LABEL: define {{.*}} @test__cpuid(i32* %{{.*}}, i32 %{{.*}}) -// CHECK: call { i32, i32, i32, i32 } asm "cpuid", -// CHECK-SAME: "={ax},={bx},={cx},={dx},{ax},{cx},~{dirflag},~{fpsr},~{flags}" -// CHECK-SAME: (i32 %{{.*}}, i32 0) +// X86-LABEL: define {{.*}} @test__cpuid(i32* %{{.*}}, i32 %{{.*}}) +// X86: call { i32, i32, i32, i32 } asm "cpuid", +// X86-SAME: "={ax},={bx},={cx},={dx},0,2,~{dirflag},~{fpsr},~{flags}" +// X86-SAME: (i32 %{{.*}}, i32 0) + +// X64-LABEL: define {{.*}} @test__cpuid(i32* %{{.*}}, i32 %{{.*}}) +// X64: call { i32, i32, i32, i32 } asm "xchgq %rbx{{.*}}cpuid{{.*}}xchgq %rbx{{.*}}", +// X64-SAME: "={ax},=r,={cx},={dx},0,2,~{dirflag},~{fpsr},~{flags}" +// X64-SAME: (i32 %{{.*}}, i32 0)