tags:

views:

981

answers:

6

How can I detect which CPU is being used at runtime ? The c++ code needs to differentiate between AMD / Intel architectures ? Using gcc 4.2.

thanks in advance, vivekian

+10  A: 

If you're on Linux (or on Windows running under Cygwin), you can figure that out by reading the special file /proc/cpuinfo and looking for the line beginning with vendor_id. If the string is GenuineIntel, you're running on an Intel chip. If you get AuthenticAMD, you're running on an AMD chip.

void get_vendor_id(char *vendor_id)  // must be at least 13 bytes
{
    FILE *cpuinfo = fopen("/proc/cpuinfo", "r");
    if(cpuinfo == NULL)
        ;  // handle error
    char line[256];
    while(fgets(line, 256, cpuinfo))
    {
        if(strncmp(line, "vendor_id", 9) == 0)
        {
            char *colon = strchr(line, ':');
            if(colon == NULL || colon[1] == 0)
                ;  // handle error
            strncpy(vendor_id, 12, colon + 2);
            fclose(cpuinfo);
            return;
        }
    }

    // if we got here, handle error
    fclose(cpuinfo);
}

If you know you're running on an x86 architecture, a less portable method would be to use the CPUID instruction:

void get_vendor_id(char *vendor_id)  // must be at least 13 bytes
{
    // GCC inline assembler
    __asm__ __volatile__
        ("movl $0, %%eax\n\t"
         "cpuid\n\t"
         "movl %%ebx, %0\n\t"
         "movl %%edx, %1\n\t"
         "movl %%ecx, %2\n\t"
         : "=m"(vendor_id), "=m"(vendor_id + 4), "=m"(vendor_id + 8)  // outputs
         : // no inputs
         : "%eax", "%ebx", "%edx", "%ecx", "memory");  // clobbered registers
    vendor_id[12] = 0;
}

int main(void)
{
    char vendor_id[13];
    get_vendor_id(vendor_id);

    if(strcmp(vendor_id, "GenuineIntel") == 0)
        ; // it's Intel
    else if(strcmp(vendor_id, "AuthenticAMD") == 0)
        ; // it's AMD
    else
        ; // other
    return 0;
}
Adam Rosenfield
I am quite sure it will be x86 architecture. Just need to differentiate between AMD and Intel. Although I am using gcc, dont think have access to /proc/cpuinfo.
vivekian2
/proc/cpuinfo is world readable, so why do you think you can't access it?
phihag
The program is to run on DOS using DJGPP. So no access to /proc/cpuinfo
vivekian2
This is the right way to do it if you want failover code when dealing with other brands - if you know your code will only ever run on AMD and Intel processors, I think mine is a bit clearer.
Branan
Reading /proc/cpuinfo feels like heavy artillery. I mean, it's like, a couple of lines of ASM code really, what's the point of reading /proc/cpuinfo to do that?
DrJokepu
here the cpudetection code used by mplayer: http://svn.mplayerhq.hu/mplayer/trunk/cpudetect.c?view=markup also look into cpudetect.h that has defines used.
Johannes Schaub - litb
Thanks everyone. This is very helpful.
vivekian2
@DrJokepu: readability/portability issues - not eveyrone's particularly brave to trek into asm territory, and maybe there's a need to port it to other arch.
Calyth
If you're running DOS, you're likely talking embedded. How would you not know? Your PCB design will spec Intel or AMD, or possibly another vendor.
MSalters
+2  A: 

On Windows, you can use the GetNativeSystemInfo function

On Linux, try sysinfo

Kevin
+10  A: 

The cpuid instruction, used with EAX=0 will return a 12-character vendor string in EBX, EDX, ECX, in that order.

For Intel, this string is "GenuineIntel". For AMD, it's "AuthenticAMD". Other companies that have created x86 chips have their own strings.The Wikipedia page for cpuid has many (all?) of the strings listed, as well as an example ASM listing for retrieving the details.

You really only need to check if ECX matches the last four characters. You can't use the first four, because some Transmeta CPUs also start with "Genuine"

  • For Intel, this is 0x6c65746e
  • For AMD, this is 0x444d4163

If you convert each byte in those to a character, they'll appear to be backwards. This is just a result of the little endian design of x86. If you copied the register to memory and looked at it as a string, it would work just fine.

Example Code:

bool IsIntel() // returns true on an Intel processor, false on anything else
{
  int id_str; // The first four characters of the vendor ID string

  __asm__ ("cpuid":\    // run the cpuid instruction with...
  "=c" (id_str) :       // id_str set to the value of EBX after cpuid runs...
  "a" (0) :             // and EAX set to 0 to run the proper cpuid function.
  "eax", "ebx", "edx"); // cpuid clobbers EAX, ECX, and EDX, in addition to EBX.

  if(id_str==0x6c65746e) // letn. little endian clobbering of GenuineI[ntel]
    return true;
  else
    return false;
}

EDIT: One other thing - this can easily be changed into an IsAMD function, IsVIA function, IsTransmeta function, etc. just by changing the magic number in the if.

Branan
You could even make a function `int Last4()` that returns the last four characters as an int using the inline assembly, and then make `bool IsIntel() { return Last4() == 0x6c65746e; }` and `bool IsAMD() { return Last4() == 0x444d4163; }` quite easily. It also avoids code duplication and reduces the overall amount of inline assembly required.
Chris Lutz
A: 

You have to define it in your Makefile arch=uname -p 2>&1 , then use #ifdef i386 some #endif for diferent architectures.

Jorge Niedbalski R.
This detects what machine it was _compiled_ on, not what machine it's _running_ on.
Adam Rosenfield
True. I misunderstand the answer.
Jorge Niedbalski R.
A: 

You probably should not check at all. Instead, check whether the CPU supports the features you need, e.g. SSE3. The differences between two Intel chips might be greater than between AMD and Intel chips.

MSalters
A: 

Trying to compile the example under AMD X86_64. But maybe i'm unlucky; all i get from GCC is an error message:

"
support_functions.cpp: In function ‘void get_vendor_id(char*)’:
support_functions.cpp:16: error: lvalue required in asm statement
support_functions.cpp:16: error: lvalue required in asm statement
support_functions.cpp:16: error: invalid lvalue in asm output 1
support_functions.cpp:16: error: invalid lvalue in asm output 2
"

Note that support_functions.cpp is simply:

"
#ifndef _SUPPORT_FUNCTIONS_CPP
#define _SUPPORT_FUNCTIONS_CPP


void get_vendor_id(char *vendor_id)  // must be at least 13 bytes
{
    // GCC inline assembler
    __asm__ __volatile__
        ("movl $0, %%eax\n\t"
         "cpuid\n\t"
         "movl %%ebx, %0\n\t"
         "movl %%edx, %1\n\t"
         "movl %%ecx, %2\n\t"
         : "=m"(vendor_id), "=m"(vendor_id + 4), "=m"(vendor_id + 8)  // outputs
         : // no inputs
         : "%eax", "%ebx", "%edx", "%ecx", "memory"   // clobbered registers
         );
    vendor_id[12] = 0;
}

#endif
"

I'm not very used to assembly... in fact, apart from some MIPS assembly at college, i've never ever touched it anymore. Someone can drive me in the correct direction?

Thanks a lot in advance,

Gabriel.

Gabriel