尝试读取x86 EFLAGS寄存器时出现不可预知的行为

时间:2016-05-01 16:49:58

标签: c++ c x86 intrinsics

我在尝试读取x86 EFLAGS寄存器时遇到不可预测的行为,以便捕获整数操作溢出。

出现问题的功能如下:

inline int64_t multiply(const int64_t op1, const int64_t op2, bool& overflow)
{
    int64_t result = op1 * op2;
    overflow = (0 != (__readeflags() & 0x801));
    return result;
}

当为x64 / Debug目标编译时,该函数按预期运行,即:它计算操作数op1op2的整数乘法,然后将overflow布尔值设置为{{ 1}}如果操作溢出或true否则。

但是,当为x64 / Release目标编译时,该函数以false布尔值的不可预测的方式运行,该布尔值随机设置为overflowtrue是否乘法溢出是否。

我可以通过在函数原型中将false限定符添加到volatile声明来修复此问题,即:

op1

有人可以解释一下为什么我在x64 / Release中获得这种随机行为,并且除了让op1 inline int64_t multiply(volatile const int64_t op1, const int64_t op2, bool& overflow) { ... } op1解决此问题之外还有其他方法吗?

PS:使用MS vc14 编译,使用volatile标头中声明的内部__readeflags();我想在intrin.h中声明的 gcc 存在同样的内在因素。

编辑:添加了汇编代码:

当指定x86intrin.h限定符时,为内联函数multiply()生成的代码如下:

volatile

在上面的程序集中,操作数乘法(; File int_overflow.h ; Line 71 imul rsi, rdx ; Line 72 pushfq pop rax test rax, 2049 ; 00000801H ; File test_int_overflow.cpp ; Line 109 mov rax, rdx ; File int_overflow.h ; Line 72 setne r8b )在EFLAGS寄存器(imul rsi, rdx)的测试之前发生,正如预期的那样。

然而,当test rax, 2049限定符未被指定时,为内联函数multiply()生成的代码如下:

volatile

在上面的程序集中,操作数乘法(; File int_overflow.h ; Line 72 pushfq pop rax test rax, 2049 ; 00000801H mov rsi, rcx ; File test_int_overflow.cpp ; Line 109 mov rax, rdx mov r12, rdx ; File int_overflow.h ; Line 72 setne r8b ; File test_int_overflow.cpp ; Line 103 mov r13, rcx ; File int_overflow.h ; Line 71 imul rsi, rdx )在EFLAGS寄存器(imul rsi, rdx)的测试之后很久就被推迟了;因此,这就是测试与乘法状态无关的原因。

我认为这是编译器优化的结果。所以,我现在的问题是:

有没有一种安全的方法可以强制编译器在EFLAGS寄存器测试之前进行乘法?

0 个答案:

没有答案