I recently needed to call the cpuid instruction from some C++ code. This is well documented on wikipedia and everything went fine until I hit a snag moving the code from x86_64 to i386.

It seems that %ebx is the PIC register for i386, and so you must take care to not clobber it when calling cpuid. This is accompolished by explictly saving and restoring the register before and after the call to cpuid. An additional step is required to tell gcc that %ebx will not be clobbered by the call to cpuid. The last lines of assembly define which registers were potentially clobbered so gcc can generate appropriate code. By manually saving and restoring %ebx, it can be saftely omitted from that list. I did not find any information documenting this specifically, so here it is! The following code returns the result of cpuid and runs on both x86_64 and i386.

void
cpuid(int regs[4], int cpuid_leaf)
{
        int eax, ebx, ecx, edx;
        asm volatile (
#if defined(__i386__)
        "pushl %%ebx;\n\t"
#endif
        "movl %4, %%eax;\n\t"
        "cpuid;\n\t"
        "movl %%eax, %0;\n\t"
        "movl %%ebx, %1;\n\t"
        "movl %%ecx, %2;\n\t"
        "movl %%edx, %3;\n\t"
#if defined(__i386__)
        "popl %%ebx;\n\t"
#endif
        :"=m" (eax), "=m" (ebx), "=m" (ecx), "=m" (edx)
        :"r" (cpuid_leaf)
        :"%eax",
#if !defined(__i386__)
        "%ebx",
#endif
        "%ecx", "%edx");

        regs[0] = eax;
        regs[1] = ebx;
        regs[2] = ecx;
        regs[3] = edx;
}

For additional information, I found Sam Hocovar’s post to be quite informative.