93

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
  • 32,499
  • 12
  • 130
  • 417
Alex B
  • 79,216
  • 40
  • 196
  • 275

7 Answers7

18

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
  • 191,086
  • 39
  • 207
  • 222
10

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
  • 32,499
  • 12
  • 130
  • 417
Wei Shen
  • 1,816
  • 16
  • 16
9

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
  • 103
  • 2
  • 7
FreakAnon
  • 91
  • 1
  • 3
  • 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
  • 1
    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
4

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.

For example

#if defined(BOOST_ARCH_X86)
    #if BOOST_ARCH_X86_64
        std::cout << "x86_64 " << BOOST_ARCH_X86_64 << " \n";
    #elif BOOST_ARCH_X86_32
        std::cout << "x86 " << BOOST_ARCH_X86_32 << " \n";
    #endif
#elif defined(BOOST_ARCH_ARM)
    #if _M_ARM
        std::cout << "ARM " << _M_ARM << " \n";
    #elif _M_ARM64
        std::cout << "ARM64 " << _M_ARM64 << " \n";
    #endif
#endif

You can find out more on how to use it here

phuclv
  • 32,499
  • 12
  • 130
  • 417
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
  • 321,763
  • 49
  • 514
  • 739
  • 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
1

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
  • 78,116
  • 55
  • 321
  • 425
  • 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
-2

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
  • 23,738
  • 3
  • 53
  • 65
  • 4
    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