[gcc r17-3004] i386: Fix __get_cpuid() and __get_cpuid_count() for Centaur/Zhaoxin CPUID range

"H.J. Lu via Gcc-cvs" <[email protected]> Thu, 6 Aug 2026 05:31:17 +0000 (GMT)
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:44cc8e5718efd0cc1ef57c68052dbe33b047f158

commit r17-3004-g44cc8e5718efd0cc1ef57c68052dbe33b047f158
Author: Ewan <[email protected]>
Date:   Tue Mar 17 16:12:15 2026 +0800

    i386: Fix __get_cpuid() and __get_cpuid_count() for Centaur/Zhaoxin CPUID range
    
    The __get_cpuid() and __get_cpuid_count() functions use
    "__leaf & 0x80000000" to determine the CPUID range base, which only
    distinguishes between Basic (0x0) and Extended (0x80000000) ranges.
    This causes queries to the Centaur/Zhaoxin range (0xC0000000) to be
    incorrectly mapped to the Extended range, and always return 0 (failure)
    on Centaur/Zhaoxin processors that support CPUID 0xC000xxxx
    leaves (e.g. PadLock feature detection via leaf 0xC0000001).
    
    Replace the bitmask with explicit range checks that correctly identify
    all four CPUID ranges: Basic (0x0), Hypervisor (0x40000000), Extended
    (0x80000000), and Centaur/Zhaoxin (0xC0000000).
    
    Additionally, the Hypervisor range (0x40000000) is also handled as an
    extra improvement, since the original bitmask incorrectly mapped it to
    the Basic range (0x0).
    
    Also update the __get_cpuid_max() comment to document the newly
    supported range base values.
    
    gcc/ChangeLog:
    
            * config/i386/cpuid.h (__get_cpuid_max): Update comment to
            document 0x40000000 and 0xC0000000 as valid ext values.
            (__get_cpuid): Replace "__leaf & 0x80000000" with explicit
            range checks for Basic, Hypervisor, Extended, and
            Centaur/Zhaoxin CPUID ranges.
            (__get_cpuid_count): Likewise.
    
    Signed-off-by: Ewanhaioc <[email protected]>

Diff:
---
 gcc/config/i386/cpuid.h | 29 +++++++++++++++++++++++++----
 1 file changed, 25 insertions(+), 4 deletions(-)

diff --git a/gcc/config/i386/cpuid.h b/gcc/config/i386/cpuid.h
index c56ad7cce80e..129531dcc382 100644
--- a/gcc/config/i386/cpuid.h
+++ b/gcc/config/i386/cpuid.h
@@ -271,8 +271,9 @@
 
 
 /* Return highest supported input value for cpuid instruction.  ext can
-   be either 0x0 or 0x80000000 to return highest supported value for
-   basic or extended cpuid information.  Function returns 0 if cpuid
+   be either 0x0, 0x40000000, 0x80000000, or 0xC0000000 to return
+   highest supported value for basic, hypervisor, extended, or
+   Centaur/Zhaoxin cpuid information.  Function returns 0 if cpuid
    is not supported or whatever cpuid returns in eax register.  If sig
    pointer is non-null, then first four bytes of the signature
    (as found in ebx register) are returned in location pointed by sig.  */
@@ -337,7 +338,17 @@ __get_cpuid (unsigned int __leaf,
 	     unsigned int *__eax, unsigned int *__ebx,
 	     unsigned int *__ecx, unsigned int *__edx)
 {
-  unsigned int __ext = __leaf & 0x80000000;
+  unsigned int __ext;
+
+  if (__leaf >= 0xC0000000)
+    __ext = 0xC0000000;
+  else if (__leaf >= 0x80000000)
+    __ext = 0x80000000;
+  else if (__leaf >= 0x40000000)
+    __ext = 0x40000000;
+  else
+    __ext = 0x00000000;
+
   unsigned int __maxlevel = __get_cpuid_max (__ext, 0);
 
   if (__maxlevel == 0 || __maxlevel < __leaf)
@@ -354,7 +365,17 @@ __get_cpuid_count (unsigned int __leaf, unsigned int __subleaf,
 		   unsigned int *__eax, unsigned int *__ebx,
 		   unsigned int *__ecx, unsigned int *__edx)
 {
-  unsigned int __ext = __leaf & 0x80000000;
+  unsigned int __ext;
+
+  if (__leaf >= 0xC0000000)
+    __ext = 0xC0000000;
+  else if (__leaf >= 0x80000000)
+    __ext = 0x80000000;
+  else if (__leaf >= 0x40000000)
+    __ext = 0x40000000;
+  else
+    __ext = 0x00000000;
+
   unsigned int __maxlevel = __get_cpuid_max (__ext, 0);
 
   if (__builtin_expect (__maxlevel == 0, 0) || __maxlevel < __leaf)