113

What is the most reliable way to find out CPU architecture when compiling C or C++ code? As far as I can tell, different compilers have their own set of non-standard preprocessor definitions (_M_X86 in MSVS, __i386__, __arm__ in GCC, etc).

Is there a standard way to detect the architecture I'm building for? If not, is there a source for a comprehensive list of such definitions for various compilers, such as a header with all the boilerplate #ifdefs?

phuclv
  • 37,963
  • 15
  • 156
  • 475
Alex B
  • 82,554
  • 44
  • 203
  • 280

7 Answers7

29

Enjoy, I was the original author of this.

extern "C" {
    const char *getBuild() { //Get current architecture, detectx nearly every architecture. Coded by Freak
        #if defined(__x86_64__) || defined(_M_X64)
        return "x86_64";
        #elif defined(i386) || defined(__i386__) || defined(__i386) || defined(_M_IX86)
        return "x86_32";
        #elif defined(__ARM_ARCH_2__)
        return "ARM2";
        #elif defined(__ARM_ARCH_3__) || defined(__ARM_ARCH_3M__)
        return "ARM3";
        #elif defined(__ARM_ARCH_4T__) || defined(__TARGET_ARM_4T)
        return "ARM4T";
        #elif defined(__ARM_ARCH_5_) || defined(__ARM_ARCH_5E_)
        return "ARM5"
        #elif defined(__ARM_ARCH_6T2_) || defined(__ARM_ARCH_6T2_)
        return "ARM6T2";
        #elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__)
        return "ARM6";
        #elif defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__)
        return "ARM7";
        #elif defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__)
        return "ARM7A";
        #elif defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__)
        return "ARM7R";
        #elif defined(__ARM_ARCH_7M__)
        return "ARM7M";
        #elif defined(__ARM_ARCH_7S__)
        return "ARM7S";
        #elif defined(__aarch64__) || defined(_M_ARM64)
        return "ARM64";
        #elif defined(mips) || defined(__mips__) || defined(__mips)
        return "MIPS";
        #elif defined(__sh__)
        return "SUPERH";
        #elif defined(__powerpc) || defined(__powerpc__) || defined(__powerpc64__) || defined(__POWERPC__) || defined(__ppc__) || defined(__PPC__) || defined(_ARCH_PPC)
        return "POWERPC";
        #elif defined(__PPC64__) || defined(__ppc64__) || defined(_ARCH_PPC64)
        return "POWERPC64";
        #elif defined(__sparc__) || defined(__sparc)
        return "SPARC";
        #elif defined(__m68k__)
        return "M68K";
        #else
        return "UNKNOWN";
        #endif
    }
}
Kotori0
  • 135
  • 2
  • 10
FreakAnon
  • 399
  • 3
  • 4
  • 2
    Your ARM strings are mixing up ARM CPU names like [ARM7](https://en.wikipedia.org/wiki/ARM7) with ARM ISA revisions like [ARMv7](https://en.wikipedia.org/wiki/Comparison_of_ARMv7-A_cores). – Peter Cordes Feb 17 '21 at 23:41
  • updated the post with correct arm7 versions. as well as powerpc64 detection proper – FreakAnon Feb 21 '21 at 10:10
  • 4
    I meant that you return `"ARM7"` when you should be returning `"ARMv7"`, or `"ARMv7A"` or whatever. The "v" is important in ARM in telling the difference between a specific ARM core model vs. an ISA version. Remember that someone looking at a program that prints this string will just the see one string on their machine, not the whole table that makes it clear it could have been "ARM6T2" (which is more clearly just a corruption of ARMv6T2, not a CPU model number.) – Peter Cordes Feb 21 '21 at 10:24
  • Is there a simpler way to detect 32-bit ARM? – Aaron Franke Jun 05 '22 at 19:03
  • @AaronFranke it can be simpler if you have only a limited number of compilers to support. This is likely for most of the common compilers out there – phuclv Jun 08 '22 at 01:13
  • Consider adding support for RISC-V. It seems that the correct macros are: `__riscv`, `__riscv32`, `__riscv__`, `_riscv` (found [here](https://groups.google.com/a/groups.riscv.org/g/sw-dev/c/r4cUgIhWLbY)). – pmor Jul 27 '23 at 11:17
21

There's no inter-compiler standard, but each compiler tends to be quite consistent. You can build a header for yourself that's something like this:

#if MSVC
#ifdef _M_X86
#define ARCH_X86
#endif
#endif

#if GCC
#ifdef __i386__
#define ARCH_X86
#endif
#endif

There's not much point to a comprehensive list, because there are thousands of compilers but only 3-4 in widespread use (Microsoft C++, GCC, Intel CC, maybe TenDRA?). Just decide which compilers your application will support, list their #defines, and update your header as needed.

John Millikin
  • 197,344
  • 39
  • 212
  • 226
15

If you would like to dump all available features on a particular platform, you could run GCC like:

gcc -march=native -dM -E - </dev/null

It would dump macros like #define __SSE3__ 1, #define __AES__ 1, etc.

phuclv
  • 37,963
  • 15
  • 156
  • 475
Wei Shen
  • 2,014
  • 19
  • 17
  • `-march=native` fails for ARM and MIPS for GCC 4.9 and below. – jww Jun 03 '16 at 22:04
  • 1
    The question was *at compile time* -- this doesn't answer that – Walter Jan 27 '22 at 09:45
  • 1
    @Walter if one has access to the architecture in question, it allows them to inspect which macros are actually defined, and then use them in the code so that it does work at compile time. – Hubert Kario Oct 04 '22 at 16:32
  • You should also use the `-xc++` switch to specify what you are compiling (C vs C++). – not2qubit Nov 27 '22 at 18:31
11

If you want a cross-compiler solution then just use Boost.Predef which contains

  • BOOST_ARCH_ for system/CPU architecture one is compiling for.
  • BOOST_COMP_ for the compiler one is using.
  • BOOST_LANG_ for language standards one is compiling against.
  • BOOST_LIB_C_ and BOOST_LIB_STD_ for the C and C++ standard library in use.
  • BOOST_OS_ for the operating system we are compiling to.
  • BOOST_PLAT_ for platforms on top of operating system or compilers.
  • BOOST_ENDIAN_ for endianness of the os and architecture combination.
  • BOOST_HW_ for hardware specific features.
  • BOOST_HW_SIMD for SIMD (Single Instruction Multiple Data) detection.

Note that although Boost is usually thought of as a C++ library, Boost.Predef is pure header-only and works for C

For example

#include <boost/predef.h>
// or just include the necessary headers
// #include <boost/predef/architecture.h>
// #include <boost/predef/other.h>

#if BOOST_ARCH_X86
    #if BOOST_ARCH_X86_64
            std::cout << "x86-64\n";
    #elif BOOST_ARCH_X86_32
            std::cout << "x86-32\n";
    #else
            std::cout << "x86-" << BOOST_ARCH_WORD_BITS << '\n'; // Probably x86-16
    #endif
#elif BOOST_ARCH_ARM
    #if BOOST_ARCH_ARM >= BOOST_VERSION_NUMBER(8, 0, 0)
        #if BOOST_ARCH_WORD_BITS == 64
            std::cout << "ARMv8+ Aarch64\n";
        #elif BOOST_ARCH_WORD_BITS == 32
            std::cout << "ARMv8+ Aarch32\n";
        #else
            std::cout << "Unexpected ARMv8+ " << BOOST_ARCH_WORD_BITS << "bit\n";
        #endif
    #elif BOOST_ARCH_ARM >= BOOST_VERSION_NUMBER(7, 0, 0)
            std::cout << "ARMv7 (ARM32)\n";
    #elif BOOST_ARCH_ARM >= BOOST_VERSION_NUMBER(6, 0, 0)
            std::cout << "ARMv6 (ARM32)\n";
    #else
            std::cout << "ARMv5 or older\n";
    #endif
#elif BOOST_ARCH_MIPS
    #if BOOST_ARCH_WORD_BITS == 64
            std::cout << "MIPS64\n";
    #else
            std::cout << "MIPS32\n";
    #endif
#elif BOOST_ARCH_PPC_64
            std::cout << "PPC64\n";
#elif BOOST_ARCH_PPC
            std::cout << "PPC32\n";
#else
            std::cout << "Unknown " << BOOST_ARCH_WORD_BITS << "-bit arch\n";
#endif

You can find out more on how to use it here

Demo on Godbolt

phuclv
  • 37,963
  • 15
  • 156
  • 475
  • Does this mean that `_M_ARM` is only defined on 32-bit ARM? – Aaron Franke Jun 05 '22 at 19:04
  • @AaronFranke no, that was my mistake because the documentation was unclear. Please see my update. You can check ARM32 easily with `BOOST_ARCH_WORD_BITS` – phuclv Jun 07 '22 at 16:35
3

There's nothing standard. Brian Hook documented a bunch of these in his "Portable Open Source Harness", and even tries to make them into something coherent and usable (ymmv regarding that). See the posh.h header on this site:

Note, the link above may require you to enter some bogus userid/password due to a DOS attack some time ago.

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • 2
    Jeez - sorry about the bogus link - it should be to http://hookatooka.com/poshlib/ that gives information about the userid/password. My browser must have 'auto logged in' from some previous visit to the page. – Michael Burr Apr 09 '09 at 21:20
  • 2
    Its also worth noting... The authors of the website stated why they did added a password: *"I apologize for the inconvenience, but due to an inexplicable DDoS attack on our earlier direct link, we've had to create this page to 'buffer' against the DDoS..."* I'm not sure its fair to penalize Michael for it. – jww Jun 03 '16 at 22:40
  • None of the links work. Could you please update it? Thanks! – Lluís Alemany-Puig Jun 05 '23 at 13:46
2

There's a list of the #defines here. There was a previous highly voted answer that included this link but it was deleted by a mod presumably due to SO's "answers must have code" rule. So here's a random sample. Follow the link for the full list.

AMD64

Type Macro Description
Identification __amd64__ __amd64 __x86_64__ __x86_64 Defined by GNU C and Sun Studio
Identification _M_X64 _M_AMD64 Defined by Visual Studio
Timmmm
  • 88,195
  • 71
  • 364
  • 509
  • What about for 32-bit? What's the shortest way I can detect both 32-bit and 64-bit x86 that works in MSVC, GCC, and Clang? – Aaron Franke Dec 08 '21 at 21:09
  • You can use a combination of the `AMD64` and `Intel x86` macros listed on that page. But you almost certainly shouldn't. Use `sizeof()`, `static_assert` and so on instead. Also you should be aware of the x32 ABI. Even on a 64-bit architecture pointers can be 32-bit. – Timmmm Dec 09 '21 at 11:59
-3

If you need a fine-grained detection of CPU features, the best approach is to ship also a CPUID program which outputs to stdout or some "cpu_config.h" file the set of features supported by the CPU. Then you integrate that program with your build process.

zvrba
  • 24,186
  • 3
  • 55
  • 65
  • 5
    Will not work for cross compiling. And how do you compile a cpuid program unless you know which machine it needs to run on? – jforberg Jan 12 '16 at 16:40