如何制作跨平台的C++内联汇编语言?

5
我翻译了以下代码:

我黑掉了以下代码:

unsigned long long get_cc_time () volatile {
  uint64 ret;
  __asm__ __volatile__("rdtsc" : "=A" (ret) : :);
  return ret;
}

这段代码在g++上可以运行,但在Visual Studio上无法运行。 如何进行移植? 应该使用哪些宏来检测VS / g++?

4个回答

5
#if defined(_MSC_VER)
// visual c
#elif defined(__GCCE__)
// gcce
#else
// unknown
#endif

我内嵌汇编的技能有些生疏,但它的工作原理如下:

__asm
{
// some assembler code
}

但是只要使用rdtsc,您就可以使用内置函数:

unsigned __int64 counter;
counter = __rdtsc();

http://msdn.microsoft.com/en-us/library/twchhe95.aspx


有没有适用于intrinsics rdtsc的Linux变体?谢谢! - Łukasz Lew

5
除了OP提出的具体问题之外,我找到了一种方法来定义一个宏,适用于两个语法版本:
#ifdef _MSC_VER
#   define ASM(asm_literal) \
        __asm { \
            asm_literal \
        };
#elif __GNUC__ || __clang__
#   define ASM(asm_literal) \
        "__asm__(\"" \
            #asm_literal \
        "\" : : );"
#endif

很不幸,由于预处理器在宏扩展之前剥离换行符,因此您需要使用此宏来包围每个汇编语句。

float abs(float x) {
    ASM( fld     dword ptr[x] );
    ASM( fabs                 );
    ASM( fstp    dword ptr[x] );

    return x;
}

请注意,GCC和clang 使用AT&T/UNIX汇编语法,但MSVC使用Intel汇编语法(虽然找不到任何官方来源)。但幸运的是,GCC/clang也可以配置为使用Intel语法。要么使用__asm__(".intel_syntax noprefix");/ __asm__(".att_syntax prefix");(一定要重置更改,因为它会影响从那一点开始生成的所有汇编代码,甚至是由编译器从C源代码生成的汇编代码)。这将使我们拥有以下宏:

#ifdef _MSC_VER
#   define ASM(asm_literal) \
        __asm { \
            asm_literal \
        };
#elif __GNUC__ || __clang__
#   define ASM(asm_literal) \
        "__asm__(\".intel_syntax noprefix\");" \
        "__asm__(\"" \
            #asm_literal \
        "\" : : );" \
        "__asm__(\".att_syntax prefix\");"
#endif

或者您也可以使用-masm=intel标志,使用GCC/clang进行编译,这会全局切换语法。


2

VC++中有一个名为"_MSC_VER"的宏,MSDN上描述其为“Microsoft特定”,并且在其他编译器编译代码时可能未定义。您可以使用#ifdef来确定编译器类型,并针对gcc和VC++编译不同的代码。

#ifdef _MSC_VER
    //VC++ version
#else
    //gcc version
#endif

2
直接使用RDTSC指令存在一些严重的缺点:
  • TSC不能保证在所有CPU上同步,因此如果您的线程/进程从一个CPU核心迁移到另一个核心,则TSC可能会出现“时间扭曲”,除非您使用线程/进程亲和性来防止迁移。
  • TSC不能保证以恒定速率增加,特别是在启用电源管理或“C1时钟斜坡”功能的PC上。对于具有多个CPU的计算机,这可能会增加偏差(例如,如果您有一个正在自旋的线程和一个正在睡眠的线程,则一个TSC可能比另一个TSC快)。
  • 直接访问TSC不允许您利用HPET
使用操作系统计时器接口更好,但根据实现方式仍可能存在一些相同的缺点: 还要注意,Microsoft Visual C++在针对64位处理器时不支持内联汇编,因此需要使用Virne提到的__rdtsc()内部函数。

1
甚至更好的是使用一个平台无关的库组件,如http://www.dre.vanderbilt.edu/Doxygen/Stable/ace/classACE__High__Res__Timer.html。 - lothar
TSC 也有其缺点,正如所描述的那样,但它也有其优点。它非常快(20-30 个时钟周期),而所有其他机制(如 HPET)都需要进入 ring 0,因此成本为 1000 个时钟周期或更多。它是精确的,而标准操作系统工具通常只提供 10 毫秒的粒度。HPET 在许多系统上不可用,即使可用,也可能只能由超级用户访问。别问我为什么 - 只需找到最近的 Linux 盒子并检查 /dev/hpet 上的权限即可。 - Eugene Smith
关于同步,桌面英特尔处理器通常在多个核心之间同步(不确定移动版英特尔是否也是如此),而在AMD处理器上,您可以通过修改线程的处理器亲和性来限制跨核心迁移。 - Eugene Smith

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