Skip to content

Commit 19cf8de

Browse files
[compiler-rt][X86] Use functions in cpuid.h instead of inline assembly (llvm#97877)
This patch makes the host/feature detection in compiler-rt and LLVM use the functions provided in cpuid.h(__get_cpuid, __get_cpuid_count) instead of inline assembly. This simplifies the implementation and moves any inline assembly away to a more common place. A while ago, some similar cleanup was attempted, but this ended up resulting in some compilation errors due to toolchain minimum version issues (https://bugs.llvm.org/show_bug.cgi?id=30384). After the reversion landed, there have been no attempts since then to clean up the code, even though the minimum supported compilers now support the relevant functions (https://godbolt.org/z/o1Mjz8ndv).
1 parent 5ef4e6d commit 19cf8de

File tree

2 files changed

+28
-150
lines changed
  • compiler-rt/lib/builtins/cpu_model
  • llvm/lib/TargetParser

2 files changed

+28
-150
lines changed

compiler-rt/lib/builtins/cpu_model/x86.c

Lines changed: 14 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323

2424
#include <assert.h>
2525

26+
#if defined(__GNUC__) || defined(__clang__)
27+
#include <cpuid.h>
28+
#endif
29+
2630
#ifdef _MSC_VER
2731
#include <intrin.h>
2832
#endif
@@ -224,38 +228,6 @@ enum ProcessorFeatures {
224228
CPU_FEATURE_MAX
225229
};
226230

227-
// The check below for i386 was copied from clang's cpuid.h (__get_cpuid_max).
228-
// Check motivated by bug reports for OpenSSL crashing on CPUs without CPUID
229-
// support. Consequently, for i386, the presence of CPUID is checked first
230-
// via the corresponding eflags bit.
231-
static bool isCpuIdSupported(void) {
232-
#if defined(__GNUC__) || defined(__clang__)
233-
#if defined(__i386__)
234-
int __cpuid_supported;
235-
__asm__(" pushfl\n"
236-
" popl %%eax\n"
237-
" movl %%eax,%%ecx\n"
238-
" xorl $0x00200000,%%eax\n"
239-
" pushl %%eax\n"
240-
" popfl\n"
241-
" pushfl\n"
242-
" popl %%eax\n"
243-
" movl $0,%0\n"
244-
" cmpl %%eax,%%ecx\n"
245-
" je 1f\n"
246-
" movl $1,%0\n"
247-
"1:"
248-
: "=r"(__cpuid_supported)
249-
:
250-
: "eax", "ecx");
251-
if (!__cpuid_supported)
252-
return false;
253-
#endif
254-
return true;
255-
#endif
256-
return true;
257-
}
258-
259231
// This code is copied from lib/Support/Host.cpp.
260232
// Changes to either file should be mirrored in the other.
261233

@@ -264,25 +236,7 @@ static bool isCpuIdSupported(void) {
264236
static bool getX86CpuIDAndInfo(unsigned value, unsigned *rEAX, unsigned *rEBX,
265237
unsigned *rECX, unsigned *rEDX) {
266238
#if defined(__GNUC__) || defined(__clang__)
267-
#if defined(__x86_64__)
268-
// gcc doesn't know cpuid would clobber ebx/rbx. Preserve it manually.
269-
// FIXME: should we save this for Clang?
270-
__asm__("movq\t%%rbx, %%rsi\n\t"
271-
"cpuid\n\t"
272-
"xchgq\t%%rbx, %%rsi\n\t"
273-
: "=a"(*rEAX), "=S"(*rEBX), "=c"(*rECX), "=d"(*rEDX)
274-
: "a"(value));
275-
return false;
276-
#elif defined(__i386__)
277-
__asm__("movl\t%%ebx, %%esi\n\t"
278-
"cpuid\n\t"
279-
"xchgl\t%%ebx, %%esi\n\t"
280-
: "=a"(*rEAX), "=S"(*rEBX), "=c"(*rECX), "=d"(*rEDX)
281-
: "a"(value));
282-
return false;
283-
#else
284-
return true;
285-
#endif
239+
return !__get_cpuid(value, rEAX, rEBX, rECX, rEDX);
286240
#elif defined(_MSC_VER)
287241
// The MSVC intrinsic is portable across x86 and x64.
288242
int registers[4];
@@ -303,26 +257,12 @@ static bool getX86CpuIDAndInfo(unsigned value, unsigned *rEAX, unsigned *rEBX,
303257
static bool getX86CpuIDAndInfoEx(unsigned value, unsigned subleaf,
304258
unsigned *rEAX, unsigned *rEBX, unsigned *rECX,
305259
unsigned *rEDX) {
260+
// TODO(boomanaiden154): When the minimum toolchain versions for gcc and clang
261+
// are such that __cpuidex is defined within cpuid.h for both, we can remove
262+
// the __get_cpuid_count function and share the MSVC implementation between
263+
// all three.
306264
#if defined(__GNUC__) || defined(__clang__)
307-
#if defined(__x86_64__)
308-
// gcc doesn't know cpuid would clobber ebx/rbx. Preserve it manually.
309-
// FIXME: should we save this for Clang?
310-
__asm__("movq\t%%rbx, %%rsi\n\t"
311-
"cpuid\n\t"
312-
"xchgq\t%%rbx, %%rsi\n\t"
313-
: "=a"(*rEAX), "=S"(*rEBX), "=c"(*rECX), "=d"(*rEDX)
314-
: "a"(value), "c"(subleaf));
315-
return false;
316-
#elif defined(__i386__)
317-
__asm__("movl\t%%ebx, %%esi\n\t"
318-
"cpuid\n\t"
319-
"xchgl\t%%ebx, %%esi\n\t"
320-
: "=a"(*rEAX), "=S"(*rEBX), "=c"(*rECX), "=d"(*rEDX)
321-
: "a"(value), "c"(subleaf));
322-
return false;
323-
#else
324-
return true;
325-
#endif
265+
return !__get_cpuid_count(value, subleaf, rEAX, rEBX, rECX, rEDX);
326266
#elif defined(_MSC_VER)
327267
int registers[4];
328268
__cpuidex(registers, value, subleaf);
@@ -338,6 +278,9 @@ static bool getX86CpuIDAndInfoEx(unsigned value, unsigned subleaf,
338278

339279
// Read control register 0 (XCR0). Used to detect features such as AVX.
340280
static bool getX86XCR0(unsigned *rEAX, unsigned *rEDX) {
281+
// TODO(boomanaiden154): When the minimum toolchain versions for gcc and clang
282+
// are such that _xgetbv is supported by both, we can unify the implementation
283+
// with MSVC and remove all inline assembly.
341284
#if defined(__GNUC__) || defined(__clang__)
342285
// Check xgetbv; this uses a .byte sequence instead of the instruction
343286
// directly because older assemblers do not include support for xgetbv and
@@ -1108,8 +1051,7 @@ int CONSTRUCTOR_ATTRIBUTE __cpu_indicator_init(void) {
11081051
if (__cpu_model.__cpu_vendor)
11091052
return 0;
11101053

1111-
if (!isCpuIdSupported() ||
1112-
getX86CpuIDAndInfo(0, &MaxLeaf, &Vendor, &ECX, &EDX) || MaxLeaf < 1) {
1054+
if (getX86CpuIDAndInfo(0, &MaxLeaf, &Vendor, &ECX, &EDX) || MaxLeaf < 1) {
11131055
__cpu_model.__cpu_vendor = VENDOR_OTHER;
11141056
return -1;
11151057
}

llvm/lib/TargetParser/Host.cpp

Lines changed: 14 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@
5050
#if defined(__sun__) && defined(__svr4__)
5151
#include <kstat.h>
5252
#endif
53+
#if defined(__GNUC__) || defined(__clang__)
54+
#include <cpuid.h>
55+
#endif
5356

5457
#define DEBUG_TYPE "host-detection"
5558

@@ -521,68 +524,15 @@ StringRef sys::detail::getHostCPUNameForBPF() {
521524
#endif
522525
}
523526

524-
#if defined(__i386__) || defined(_M_IX86) || \
525-
defined(__x86_64__) || defined(_M_X64)
526-
527-
// The check below for i386 was copied from clang's cpuid.h (__get_cpuid_max).
528-
// Check motivated by bug reports for OpenSSL crashing on CPUs without CPUID
529-
// support. Consequently, for i386, the presence of CPUID is checked first
530-
// via the corresponding eflags bit.
531-
// Removal of cpuid.h header motivated by PR30384
532-
// Header cpuid.h and method __get_cpuid_max are not used in llvm, clang, openmp
533-
// or test-suite, but are used in external projects e.g. libstdcxx
534-
static bool isCpuIdSupported() {
535-
#if defined(__GNUC__) || defined(__clang__)
536-
#if defined(__i386__)
537-
int __cpuid_supported;
538-
__asm__(" pushfl\n"
539-
" popl %%eax\n"
540-
" movl %%eax,%%ecx\n"
541-
" xorl $0x00200000,%%eax\n"
542-
" pushl %%eax\n"
543-
" popfl\n"
544-
" pushfl\n"
545-
" popl %%eax\n"
546-
" movl $0,%0\n"
547-
" cmpl %%eax,%%ecx\n"
548-
" je 1f\n"
549-
" movl $1,%0\n"
550-
"1:"
551-
: "=r"(__cpuid_supported)
552-
:
553-
: "eax", "ecx");
554-
if (!__cpuid_supported)
555-
return false;
556-
#endif
557-
return true;
558-
#endif
559-
return true;
560-
}
527+
#if defined(__i386__) || defined(_M_IX86) || defined(__x86_64__) || \
528+
defined(_M_X64)
561529

562530
/// getX86CpuIDAndInfo - Execute the specified cpuid and return the 4 values in
563531
/// the specified arguments. If we can't run cpuid on the host, return true.
564532
static bool getX86CpuIDAndInfo(unsigned value, unsigned *rEAX, unsigned *rEBX,
565533
unsigned *rECX, unsigned *rEDX) {
566534
#if defined(__GNUC__) || defined(__clang__)
567-
#if defined(__x86_64__)
568-
// gcc doesn't know cpuid would clobber ebx/rbx. Preserve it manually.
569-
// FIXME: should we save this for Clang?
570-
__asm__("movq\t%%rbx, %%rsi\n\t"
571-
"cpuid\n\t"
572-
"xchgq\t%%rbx, %%rsi\n\t"
573-
: "=a"(*rEAX), "=S"(*rEBX), "=c"(*rECX), "=d"(*rEDX)
574-
: "a"(value));
575-
return false;
576-
#elif defined(__i386__)
577-
__asm__("movl\t%%ebx, %%esi\n\t"
578-
"cpuid\n\t"
579-
"xchgl\t%%ebx, %%esi\n\t"
580-
: "=a"(*rEAX), "=S"(*rEBX), "=c"(*rECX), "=d"(*rEDX)
581-
: "a"(value));
582-
return false;
583-
#else
584-
return true;
585-
#endif
535+
return !__get_cpuid(value, rEAX, rEBX, rECX, rEDX);
586536
#elif defined(_MSC_VER)
587537
// The MSVC intrinsic is portable across x86 and x64.
588538
int registers[4];
@@ -609,9 +559,6 @@ VendorSignatures getVendorSignature(unsigned *MaxLeaf) {
609559
else
610560
*MaxLeaf = 0;
611561

612-
if (!isCpuIdSupported())
613-
return VendorSignatures::UNKNOWN;
614-
615562
if (getX86CpuIDAndInfo(0, MaxLeaf, &EBX, &ECX, &EDX) || *MaxLeaf < 1)
616563
return VendorSignatures::UNKNOWN;
617564

@@ -639,26 +586,12 @@ using namespace llvm::sys::detail::x86;
639586
static bool getX86CpuIDAndInfoEx(unsigned value, unsigned subleaf,
640587
unsigned *rEAX, unsigned *rEBX, unsigned *rECX,
641588
unsigned *rEDX) {
589+
// TODO(boomanaiden154): When the minimum toolchain versions for gcc and clang
590+
// are such that __cpuidex is defined within cpuid.h for both, we can remove
591+
// the __get_cpuid_count function and share the MSVC implementation between
592+
// all three.
642593
#if defined(__GNUC__) || defined(__clang__)
643-
#if defined(__x86_64__)
644-
// gcc doesn't know cpuid would clobber ebx/rbx. Preserve it manually.
645-
// FIXME: should we save this for Clang?
646-
__asm__("movq\t%%rbx, %%rsi\n\t"
647-
"cpuid\n\t"
648-
"xchgq\t%%rbx, %%rsi\n\t"
649-
: "=a"(*rEAX), "=S"(*rEBX), "=c"(*rECX), "=d"(*rEDX)
650-
: "a"(value), "c"(subleaf));
651-
return false;
652-
#elif defined(__i386__)
653-
__asm__("movl\t%%ebx, %%esi\n\t"
654-
"cpuid\n\t"
655-
"xchgl\t%%ebx, %%esi\n\t"
656-
: "=a"(*rEAX), "=S"(*rEBX), "=c"(*rECX), "=d"(*rEDX)
657-
: "a"(value), "c"(subleaf));
658-
return false;
659-
#else
660-
return true;
661-
#endif
594+
return !__get_cpuid_count(value, subleaf, rEAX, rEBX, rECX, rEDX);
662595
#elif defined(_MSC_VER)
663596
int registers[4];
664597
__cpuidex(registers, value, subleaf);
@@ -674,6 +607,9 @@ static bool getX86CpuIDAndInfoEx(unsigned value, unsigned subleaf,
674607

675608
// Read control register 0 (XCR0). Used to detect features such as AVX.
676609
static bool getX86XCR0(unsigned *rEAX, unsigned *rEDX) {
610+
// TODO(boomanaiden154): When the minimum toolchain versions for gcc and clang
611+
// are such that _xgetbv is supported by both, we can unify the implementation
612+
// with MSVC and remove all inline assembly.
677613
#if defined(__GNUC__) || defined(__clang__)
678614
// Check xgetbv; this uses a .byte sequence instead of the instruction
679615
// directly because older assemblers do not include support for xgetbv and

0 commit comments

Comments
 (0)