检测CPU架构的编译时方法

115

在编译C或C++代码时,最可靠的查找CPU架构的方法是什么?据我所知,不同的编译器有自己的一套非标准预处理宏定义(如MSVS中的_M_X86,GCC中的__i386____arm__等)。

是否有一个标准的方法来检测我正在构建的架构?如果没有,是否有一个包含各种编译器的所有这些定义的头文件,例如所有样板#ifdef的综合列表的来源?


1
操作系统检测的基本答案如下:https://dev59.com/wHVC5IYBdhLWcg3w7V33 - Ciro Santilli OurBigBook.com
https://sourceforge.net/p/predef/wiki/Architectures/ - Timmmm
7个回答

35

请享用,我是这篇文章的原作者。

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
    }
}

2
你的 ARM 字符串混淆了 ARM CPU 名称,例如 ARM7 和 ARM ISA 修订版本,如 ARMv7 - Peter Cordes
已更新帖子,包含正确的ARM7版本以及适当的PowerPC64检测。 - FreakAnon
5
我的意思是当你应该返回“ARMv7”、“ARMv7A”或其他版本时,你返回了“ARM7”。在ARM中,“v”很重要,可以区分特定的ARM核心型号和ISA版本。请记住,查看打印此字符串的程序的人只会在他们的机器上看到一个字符串,而不是整个表格,因此需要明确它可能是“ARM6T2”(更清楚地表示为ARMv6T2,而不是CPU型号号码的错误)。 - Peter Cordes
有没有更简单的方法来检测32位ARM? - Aaron Franke
@AaronFranke 如果你只需要支持有限数量的编译器,那么这可能会更简单。对于大多数常见的编译器来说,这是很可能的。 - phuclv
显示剩余2条评论

22

每个编译器都没有标准,但每个编译器往往都非常一致。您可以为自己构建一个标题,例如:

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

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

因为有成千上万种编译器,但只有3-4个广泛使用(Microsoft C++、GCC、Intel CC,或许还有TenDRA?),所以全面的列表意义不大。只需决定哪些编译器适合您的应用程序,列出它们的#define,并根据需要更新头文件即可。


5
在Visual Studio 2010上,这对我没有起作用。 _M_X86未被定义为正数(32位版本)。正确的是_M_IX86(感谢上面Serge的链接)。 - Thomas
这些能在32位和64位的x86上都运行吗? - Aaron Franke

15

如果您想在特定平台上卸载所有可用的功能,您可以运行以下命令:

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

它会导出像 #define __SSE3__ 1#define __AES__ 1 等宏定义。


对于GCC 4.9及以下版本,-march=native在ARM和MIPS上失败。 - jww
1
问题是“在编译时”——这并没有回答那个问题。 - Walter
1
@Walter 如果一个人可以访问所涉及的架构,那么他们就可以检查实际定义了哪些宏,然后在代码中使用它们,以便在编译时起作用。 - Hubert Kario
你还应该使用-xc++开关来指定你正在编译的语言类型(C还是C++)。 - not2qubit

12
如果您需要跨编译器解决方案,只需使用Boost.Predef,其中包含以下内容:
  • BOOST_ARCH_:正在编译的系统/CPU架构。
  • BOOST_COMP_:正在使用的编译器。
  • BOOST_LANG_:正在编译的语言标准。
  • BOOST_LIB_C_BOOST_LIB_STD_:正在使用的C和C++标准库。
  • BOOST_OS_:我们正在编译的操作系统。
  • BOOST_PLAT_:基于操作系统或编译器的平台。
  • BOOST_ENDIAN_:操作系统和架构组合的字节序。
  • BOOST_HW_:硬件特定功能。
  • BOOST_HW_SIMD:检测SIMD(单指令多数据)。
请注意,尽管Boost通常被认为是一个C++库,但Boost.Predef是纯头文件,也适用于C。
例如:
#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

你可以在这里了解如何使用它

Demo on Godbolt


这是否意味着 _M_ARM 只在32位ARM上定义? - Aaron Franke
@AaronFranke 不好意思,那是我的错误,因为文档不够清晰。请看我的更新。你可以使用 BOOST_ARCH_WORD_BITS 轻松检查 ARM32。 - phuclv

3

没有标准。Brian Hook在他的“可携带开源工具”中记录了其中许多内容,并尝试将它们变成一些连贯和可用的东西(关于这一点可能因人而异)。请参见此站点上的posh.h标头:

请注意,由于以前的DOS攻击,上面的链接可能需要您输入一些虚假的用户ID /密码。


2
真是抱歉,之前的链接有误,正确的链接应该是http://hookatooka.com/poshlib/,该链接提供了有关用户ID/密码的信息。可能是我的浏览器从之前访问该页面时自动登录了。 - Michael Burr
2
值得注意的是...网站的作者解释了为什么他们添加了密码:“我为给您带来的不便道歉,但由于我们之前的直接链接遭受了难以解释的DDoS攻击,我们不得不创建这个页面来缓冲DDoS…” 我不确定因此惩罚迈克尔是否公平。 - jww
没有一个链接有效。你能更新一下吗?谢谢! - Lluís Alemany-Puig
没有一个链接有效。你能更新一下吗?谢谢! - undefined

2

这里有一个 #define 列表 在这里。之前有一个高票回答包含了这个链接,但是被版主删除了,可能是因为 SO 的“回答必须有代码”的规定。所以这里提供一个随机样本。点击链接查看完整列表。

AMD64

类型 宏定义 描述
识别 __amd64__ __amd64 __x86_64__ __x86_64 由 GNU C 和 Sun Studio 定义
识别 _M_X64 _M_AMD64 由 Visual Studio 定义

32位的情况怎么办?我能用什么最简单的方法来检测在MSVC、GCC和Clang中都能工作的32位和64位x86呢? - Aaron Franke
你可以使用该页面上列出的 AMD64Intel x86 宏的组合。但你几乎肯定不应该这样做。相反,应该使用 sizeof()static_assert 等等。此外,你应该了解 x32 ABI。即使在 64 位架构上,指针也可以是 32 位的。 - Timmmm

-3
如果您需要对CPU功能进行细粒度检测,最好的方法是同时提供一个CPUID程序,该程序将支持的功能集输出到stdout或某个“cpu_config.h”文件中。然后将该程序与您的构建过程集成。

6
不适用于交叉编译。如果你不知道程序需要在哪台机器上运行,那么你怎么编译cpuid程序呢? - jforberg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接