内部循环中的内联汇编

时间:2010-01-13 10:14:19

标签: c++ inline-assembly

我在一个项目中大量使用内联汇编,我需要在编译时调用具有未知数量参数的函数,同时我自己管理它,有时候,在linux中(在Windows中我不记得有那个问题)这样的奇怪事情发生了:

如果我有类似

的话
for(int i = 1; i >= 0; i--)
   asm("push %0"::"m"(someArray[i]));

有效。

如果我有

for(int i = this->someVar; i >= 0; i--)
   asm("push %0"::"m"(someArray[i]));

我保证我的生活中someVar保持值1它会引发分段错误。

如果我有

int x = 1;
for(int i = x; i >= 0; i--)
   asm("push %0"::"m"(someArray[i]));

它有效但

int x = this->someVar;
for(int i = x; i >= 0; i--)
    asm("push %0"::"m"(someArray[i]));

没有。

另外,同样奇怪的是,我可以说,在某些功能中,我在其他人中没有遇到任何问题,都在同一个对象中。

如果有人能指出一些可以解决问题的信息,我将不胜感激。

请注意,我必须在for循环中推送参数,以避免它不是一个选项。

我也尝试使用内联汇编字“volatile”,但没有改变。

5 个答案:

答案 0 :(得分:4)

我无法理解问题是什么,但尝试使用与

相同的清晰asm代码编写代码
asm{
   loop1:
     mov ax, this->var
     ...
     dec ax
     cmp ax, 0
     je exit
     jmp loop1
}

...

出口:

同时尝试将“var”值设为静态也可能有所帮助。

答案 1 :(得分:4)

检查反汇编。最可能的原因是i和/或保持结束值的变量在for循环的每次迭代时从堆栈上的固定偏移量中被重新获取,并且push偏移量堆栈指针从编译器预期的位置开始,因此导致获取错误的值。

您可以尝试各种变通方法(例如声明局部变量register),但遗憾的是,在这种情况下,保证在C / C ++中的正确​​行为没有好办法。为避免这个问题,请自行实施循环,如oivoodoo所暗示的那样。

答案 2 :(得分:3)

这是我的心理调试工作:

ithis很可能存储在堆栈中,而在386及更高版本上,机器代码可以直接引用esp - 相对内存位置,因此编译器很可能制作像

这样的说明
mov eax,[esp+8]

this的值输入eax寄存器。问题是你的push操作混乱了堆栈指针,因此这些硬编码访问将在第一次迭代后访问(越来越多)错误的内存位置。

最有可能的是,没有this->someVar的更简单的循环形式被编译器更彻底地优化,并导致机器代码仅使用寄存器而没有esp - 相对访问,这意味着它们继续正常工作。

曾几何时,所有对局部变量和参数的内存访问都是通过ebp寄存器完成的,该寄存器不会被内联汇编代码更改。如果你能找到一个强制使用ebp代替esp的编译器开关,这可能会解决你的问题。

警告:编译器不希望你弄乱堆栈 - 它希望它始终知道栈顶的位置。如果你真的想在堆栈上动态地推送东西,我建议用汇编语言完全编写循环本身oivoodoo

答案 3 :(得分:1)

首先,可能发生的是linux下的gcc使用堆栈指针索引局部变量而不是使用堆栈帧指针。这是一个优化,允许gcc使用帧指针(x86下的BP)作为另一个通用寄存器,并避免大量设置帧的代码。帧基本上只是属于本地函数的SP和BP之间的区域。我敢打赌,如果你包含一个调用alloca的大小,你传入这个函数它会变得更好,因为它会迫使编译器不进行这种优化。

话虽如此,该错误确实存在于您的代码中。除非你真的知道你在做什么,否则你永远不应该退出内联asm,其堆栈指针与你输入内联asm时的不同。编译器几乎总是认为它们只拥有堆栈指针。它们依赖于它保持不变,以便它们可以使用它来查找存储变量的位置。 你也应该远离帧指针(BP)。

可以捣乱的时间很少见,通常用于上下文切换代码(从一个线程或进程更改为另一个)。

答案 4 :(得分:0)

如果您知道args数量的限制,您可以使用具有该数量的参数的单个函数调用来调用它,将您的实际参数对齐到最后。

警告:x86_64 abi使用寄存器来处理某些参数,这会打破这个和你的代码。