警告#13212:在需要堆栈对齐的函数中引用ebx

时间:2018-02-28 07:10:34

标签: c++ assembly

我正在尝试使用ICC 2018编译以下代码:

__asm {
        mov ebx, xx              ;xx address to registers
}

其中xx的类型为int16。这是我函数中的第一条指令。

我使用上面的汇编代码得到以下警告: 警告#13212:在需要堆栈对齐的函数中引用ebx

令人惊讶的是,当我用eax或esi替换ebx时,我看到警告消失了。我无法理解为什么我只看到ebx的问题,据我所知,ebx和eax都有相同的架构(32位寄存器)。

另外,当我使用ICC 2013编译相同的代码时,我没有看到警告。

有人可以帮我解决此警告吗?

谢谢!

1 个答案:

答案 0 :(得分:1)

选择平台上的编译器(ICC因为它模仿MSVC的行为)如果需要额外的对齐,则使用EBX保存原始堆栈指针值。因此,您无法安全地覆盖它。 程序的行为将变得不确定。编译器警告只会告诉你这一点。

为了帮助保存/恢复受程序集块影响的所有寄存器,建议使用带有所谓clobber列表的扩展语法。您的示例使用MSVC样式的__asm{...}语法。在MSVC样式语法中,编译器会检测您触摸的寄存器并为您保存/恢复它们。 ICC还支持类似GCC的符号,用于扩展asm和clobber列表:asm("...":::)。它还支持更简单的GCC asm("...")而没有clobber列表部分。有关详细信息,请参阅此question(感谢Peter Cordes的链接和说明。)

我在学习使用clobber列表时发现有用的文档(我实际上一直使用它,因为它不可能记住它相当人性化的语法):

  1. https://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html#s5
  2. https://software.intel.com/en-us/node/694235
  3. 只有以下情况才能安全地使用没有clobber列表的简单内联汇编块:

    1. 块的指令不会修改ABI中定义的寄存器。因此,GPRs,堆栈计数器,标志应该不受影响;如果函数中有浮点计算,FPU /向量寄存器也是禁止的。即使是内存写入也可能导致错误,因为编译器依赖已知值驻留在内存中。相反,可以发出INT3HLTWRMSR等指令,这些指令既不接触寄存器也不影响编译器不使用的系统寄存器。但是,大多数此类指令都是特权的,不能在用户应用程序中使用。如果没有这种读取的副作用,也可以读取所有可用的寄存器。
    2. 汇编程序块是函数体中的唯一语句。在这种情况下,它必须遵守所选平台的调用约定:如何传递函数的参数,应该放置退出代码等等。块还需要处理编译器生成的序言和结尾代码对寄存器有自己假设的块。它们的代码不是严格稳定的,也不是可移植的,也不保证在不同的优化级别下是相同的。在x86上使用GCC,我无法禁用序言/结尾生成,因此仍存在违反编译器假设的风险。
    3. 您自己保存所有破坏的寄存器并在之后恢复它们。这相对简单,因为您可以看到自己的汇编代码,并可以判断寄存器是否被修改。但是,犯了一个错误,编译器不会在这里指出它。尽管它可以将asm块视为黑盒子,但实际发出警告是非常好的ICC 2018。
    4. 你"偷走"来自编译器的寄存器。 GCC允许使用register asm语句执行此操作(不记得相同的技巧是否适用于其他编译器)。因此,您可以声明变量绑定到某个寄存器。请注意,此类技术会减少编译器可用于其寄存器分配阶段的寄存器数量,这会降低其生成的代码质量。要求太多的寄存器,编译器将无助并拒绝工作。类似地,不能要求具有专用角色的寄存器从编译器中取出,例如堆栈指针或程序计数器。
    5. 也就是说,带有clobber列表的扩展asm语法提供了一个不错的选择。它将asm部分从黑盒子转换为内联内部"功能"声明它自己的输入,输出和资源,它覆盖与外部函数共享。