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

Nay*_*osh 6 c++ assembly

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

__asm {
        mov ebx, xx              ;xx address to registers
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

谁能帮我解决这个警告?

谢谢!

Gri*_*tov 1

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

为了帮助保存/恢复受汇编块影响的所有寄存器,建议使用所谓的破坏列表的扩展语法。您的示例使用 MSVC 样式__asm{...}语法。在 MSVC 风格的语法中,编译器会检测您触摸的寄存器并为您保存/恢复它们。ICC 还支持带有 clobber 列表的扩展 asm 的类似于 GCC 的表示法:asm("...":::)。它还支持更简单的 GCC asm("..."),无需破坏列表部分。有关更多详细信息,请参阅此问题(感谢 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

没有破坏列表的简单内联汇编块只能在以下情况下安全使用:

  1. 该块的指令不会修改 ABI 中定义的寄存器。因此 GPR、堆栈计数器、标志应该保持不变;如果函数中有浮点计算,FPU/向量寄存器也不受限制。即使内存写入也可能导致错误,因为编译器依赖于驻留在内存中的已知值。相反,可以发出INT3、等指令HLTWRMSR这些指令要么不接触任何寄存器,要么只影响编译器不使用的系统寄存器。然而,大多数此类指令都是特权指令,不能在用户应用程序中使用。人们还可以读取所有可用的寄存器,前提是此类读取没有副作用。
  2. 汇编程序块是函数体中的唯一语句。在这种情况下,它必须遵守所选平台的调用约定:如何传递函数的参数、应将其退出代码放置在何处等。该块还需要处理编译器生成的序言和尾声代码块,这些代码块具有自己的功能。自己关于寄存器的假设。他们的代码不是严格稳定的,也不是可移植的,也不能保证在不同的优化级别下是相同的。对于 x86 上的 GCC,我无法禁用序言/结尾生成,因此仍然存在违反编译器假设的风险。
  3. 您自己保存所有被破坏的寄存器并在之后恢复它们。这相对容易,因为您可以看到自己的汇编代码,并可以判断寄存器是否被它修改。然而,犯了一个错误,编译器不会在这里为你指出它。ICC 2018 确实给出了警告,这非常好,尽管它可能只是将 asm 块视为黑匣子。
  4. 您从编译器“窃取”了一个寄存器。GCC 允许使用 withregister asm语句执行此操作(不记得相同的技巧是否适用于其他编译器)。因此,您可以声明变量绑定到某个寄存器。请注意,这种技术会减少编译器在寄存器分配阶段可用的寄存器数量,这会降低其生成的代码质量。要求太多的寄存器,编译器将束手无策,拒绝工作。同样,不能要求编译器取消具有专用作用的寄存器,例如堆栈指针或程序计数器。

也就是说,asm带有破坏列表的扩展语法提供了一个很好的替代方案。它将一个asm部分从一个黑盒子变成了一个内联内部“函数”,它声明了自己的输入、输出和它覆盖的资源,这些资源与外部函数共享。

  • OP 使用 MSVC 风格的 `__asm { }` 语法,*不是* GNU C Basic Asm `asm( "" )` 语法。在 MSVC 风格的语法中,编译器会检测您触摸的寄存器并保存/恢复它们(或让它们被破坏)。你不应该在asm语句中使用`push`/`pop`,编译器仍然会在你的asm块周围发出自己的保存/恢复,即使你的块包含push/pop:https://godbolt.org/g/ Cebj1b. (所以你的建议只适用于 GNU C 扩展 asm 语法,是的,你可以推送/弹出 ebx,而不是在这种情况下声明一个 clobber。) 如果 OP 关心与 MSVC 的兼容性,我不知道。 (2认同)