问题
我怎么能
自然地
没有
RaiseException
)生成
EXCEPTION_FLT_DENORMAL_OPERAND
Windows上的异常(最好是x64)?
上下文
我们有一个程序,可以捕捉崩溃的程序,回放错误并分析错误的原因。它比简单的调试器复杂得多,因为它可以回放崩溃,污染参数传播的内存,执行一些一致性和其他复杂的事情。
为此,我们需要在各种崩溃的情况下测试程序,即生成
Windows exceptions
.
我知道我可以打电话
RaiseException
具有所需的
dwExceptionCode
但在我们的案例中,这与“自然”提出的相同例外非常不同。在我们的案例中,生成
EXCEPTION_fillt_deormal_operator
同时操作浮点数。
EXCEPTION_fillt_deormal_operator
根据文件,如果出现以下情况,则提出例外情况:
浮点运算中的一个操作数是非规格化的。非正规值是指太小而无法表示为标准浮点值的值。
解释了非正规(也称为亚正规)值
thoroughly on Wikipedia
; 使用一些“古怪”的C(C++)代码生成它们也很容易:
const unsigned ux = 0xf; // denormal as float.
const unsigned uy = 0x7; // denormal as float.
//auto x = (float)ux; // this obviouly doesn't work as you end up with 15.f.
auto px = (float*)&ux;
auto py = (float*)&uy;
auto x = *px; // denormal
auto y = *py; // denormal
注意,我可以在调试器下看到
x
和
y
是非正规的。
该代码是使用为x64体系结构编译的
cl
编译器和此函数:
DWORD ExceptionFltDenormalOperand(LPVOID lpParam)
{
UNREFERENCED_PARAMETER(lpParam);
_control87(_EM_INVALID, _EM_DENORMAL);
const unsigned ux = 0xf; // denormal as float.
const unsigned uy = 0x7; // denormal as float.
auto px = (float*)&ux;
auto py = (float*)&uy;
auto x = *px;
auto y = *py;
//auto y = 1.f;
float sum = 0.0f;
for (int i = 0; i < 1000; i++) {
sum += x / y;
}
return static_cast<DWORD>(sum); // avoid optimizing away the above computation.
}
相关编译器标志:
请注意,分解后的代码显示
XMM
使用(SIMD)寄存器,而不是x87 FPU(
ST(x)
寄存器);下面是分部
x / y
:
00007FF7B46F1AF5 | F3:0F1085 C4000000 | movss xmm0,dword ptr ss:[rbp+C4]
00007FF7B46F1AFD | F3:0F5E85 E4000000 | divss xmm0,dword ptr ss:[rbp+E4]
控制异常
这个
_control87
函数用于控制浮点控制字的行为,尤其是用于非正规操作数。
_control87和_controlfp之间的区别在于它们如何处理DENORMAL值。对于x86、x64、ARM和ARM64平台,_control87可以设置和清除DENORMAL OPERAND异常掩码_controlfp不修改DENORMAL OPERAND异常掩码。
该函数的用途如下:
_control87(_EM_INVALID, _EM_DENORMAL);
调试时:
// Before: MxCsr = 0x1F80
00007FF7B46F1A6A | FF15 E0190100 | call qword ptr ds:[<&_control87>]
// After: MxCsr = 0x1E80
根据《英特尔手册》,关于
MxCsr
登记
位7到12为SIMD浮点异常提供单独的掩码位。如果设置了相应的掩码位,则会屏蔽异常类型,如果清除了该位,则不屏蔽异常类型。这些掩码位是在加电或复位时设置的。这会导致最初屏蔽所有SIMD浮点异常。
默认情况下,该值为0x1F80(所有遮罩都设置为1)。值0x1E80表示比特8为0(其中比特8为DM或
Denormal Operation Mask
):
0x1E80 in binary.
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|3|3|2|2|2|2|2|2|2|2|2|2|1|1|1|1|1|1|1|1|1|1|0|0|0|0|0|0|0|0|0|0|
|1|0|9|8|7|6|5|4|3|2|1|0|9|8|7|6|5|4|3|2|1|0|9|8|7|6|5|4|3|2|1|0|
|â¾|â¾|â¾|â¾|â¾|â¾|â¾|â¾|â¾|â¾|â¾|â¾|â¾|â¾|â¾|â¾|â¾|â¾|â¾|â¾|â¾|â¾|â¾|â¾|â¾|â¾|â¾|â¾|â¾|â¾|â¾|â¾|
|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|1|1|1|1|0|1|0|0|0|0|0|0|0|
â¾ â¾ â¾ â¾ â¾ â¾ â¾ â¾ â¾ â¾ â¾ â¾ â¾ â¾ â¾ â¾ â¾ â¾ â¾ â¾ â¾ â¾ â¾ â¾ â¾ â¾ â¾ â¾ â¾ â¾ â¾ â¾
后果
(执行上述代码的)结果异常
总是
导致
EXCEPTION_FLT_INVALID_OPERATION
(而不是等待
EXCEPTION_fillt_deormal_operator
).
[edit]在异常站点
MxCsr
寄存器值为0x1E82;这表明比特1(DE:Denormal Flag)被切换。
其他测试