我在尝试读取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目标编译时,该函数按预期运行,即:它计算操作数op1
和op2
的整数乘法,然后将overflow
布尔值设置为{{ 1}}如果操作溢出或true
否则。
但是,当为x64 / Release目标编译时,该函数以false
布尔值的不可预测的方式运行,该布尔值随机设置为overflow
或true
是否乘法溢出是否。
我可以通过在函数原型中将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寄存器测试之前进行乘法?