在C ++中使用asm代码操作寄存器时会发生什么变化? [英] What happens to registers when you manipulate them using asm code in C++?

查看:64
本文介绍了在C ++中使用asm代码操作寄存器时会发生什么变化?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

一些代码:

int x = 1;
for(int i = 1; i < 10; i++)
{
    x *= i;
    __asm {
        mov eax, x 
    };
}

如果此程序使用 eax 来增加 i 的值,那么当我操作 eax 时会发生什么?

If this program uses eax in order to increase the value of i, what will happen when I manipulate eax?

编译器将在 __ asm 调用之前保存寄存器,并在执行asm代码之后使用它们,否则它将忽略对 eax 进行操作并继续产生某种形式的奇怪的行为?

Will the compiler save registers from before the __asm call and use them after the asm code was executed or will it ignore that eax was manipulated and continue producing some sort of strange behavior?

内部传真会怎样?

编辑:即使我的代码仅适用于Visual C ++,我也想知道一般情况以及不同的编译器将如何处理此问题.

Even if my code only works with Visual C++ I want to know what happens in general and how different compilers will handle this.

推荐答案

内部传真会怎样?

What happens to eax internally?

内联汇编不是魔术也不是特殊的.C ++编译器已经将C ++转换为asm.

Inline asm isn't magic or special. C++ compilers already translate C++ into asm.

您的内联汇编仅包含在编译器生成的汇编中.如果您想了解实际情况,请查看编译器的asm输出,以了解您的代码如何适应.

Your inline asm just gets included in with the compiler-generated asm. If you want to understand what's really happening, look at the asm output from the compiler to see how your code fits in.

即问题的这一部分的答案是,它取决于编译器,上下文和优化选项,因此您应该只查看生成的asm以便自己查看.

i.e. the answer to this part of the question is that it depends on the compiler, the context, and the optimization options, so you should just look at the generated asm to see for yourself.

您的问题使用MSVC样式的内联汇编,它可以在内联汇编(除了 ebp ,当然还有 esp 之外)周围保存/恢复寄存器.因此,我认为您的榜样永远不会有效果.MSVC风格没有语法可以向编译器传达有关寄存器使用情况的任何信息,也不能用于在寄存器而不是内存中获取/输入值.

Your question uses MSVC-style inline asm, which saves/restores registers around inline asm (other than ebp, and of course esp). So I think your example could would always have no effect. MSVC-style has no syntax to communicate anything to the compiler about register usage, or for getting values in/out in registers instead of memory.

您有时会看到MSVC内联汇编,它在 int 函数的末尾在 eax 中留下一个值,而没有 return 语句,从而使在有限的情况下最安全的假设是,在inline-asm的末尾与函数的末尾之间,编译器不会对eax进行任何操作.

You sometimes see MSVC inline asm that leaves a value in eax at the end of an int function with no return statement, making the mostly-safe assumption under limited circumstances that the compiler won't do anything with eax between the end of the inline-asm and the end of the function.

您在评论中说,您想要g ++的答案,它甚至无法编译您的示例,但是无论如何,我会为您编写一个.

You said in comments you'd like an answer for g++, which couldn't even compile your example, but whatever, I'll write one for you.

GNU C内联asm 使用不同的语法,这要求您告诉编译器哪些寄存器是输入(而不是修改的),哪些是读写或仅写的.还有哪些寄存器是破坏性的临时寄存器.

GNU C inline asm uses different syntax, which requires you to tell the compiler which registers are inputs (and not modified), and which are read-write or write-only. Also which registers are clobbered scratch regs.

由程序员决定如何使用约束条件向编译器正确描述asm,否则您将踩到它的脚趾.使用GNU C嵌入式asm就像跳舞一样,您可以在其中 取得良好的效果,但前提是您不小心会踩到编译器的脚步.(此外,通常编译器可以自行创建好的asm,并且内联asm是优化的一种非常脆弱的方法;许多主要问题之一是内联后不可能通过内联asm进行持续传播.)

It's up to the programmer to correctly describe the asm to the compiler using constraints, otherwise you will step on its toes. Using GNU C inline asm is like a dance, where you can potentially achieve good results, but only if you're not careful you'll step on the compiler's toes. (Also, usually the compiler can make good asm on its own, and inline asm is a very brittle way to optimize; one of the many major problems is that constant propagation after inlining isn't possible through inline asm.)

int foo_badasm(int n) {
  int factorial = 1;
  for (int i=1 ; i < n ; i++ ) {
    // compile with -masm=intel, since I'm using Intel syntax here
    asm volatile ("mov   eax, %[x]   # THIS LINE FROM INLINE ASM\n"
                  "# more lines\n"
                  // "xor  eax,eax\n"
        : // no outputs, making the volatile implicit even if we didn't specify it
        : [x] "rmi" (factorial)   // input can be reg, memory, or immediate
        : // "eax"   // uncomment this to tell the compiler we clobber eax, so our asm doesn't break step on the compiler's toes.
    );
    factorial *= i;
  }
  return factorial;
}

See the code with asm output on the Godbolt compiler explorer, and for the same function with no asm statement.

内部循环编译为此(gcc 6.1 -O3 -fverbose-asm -masm = intel ,并使用 -fno-tree-vectorize 保持简单).您也可以尝试使用clang.

The inner loop compiles to this (gcc 6.1 -O3 -fverbose-asm -masm=intel, with -fno-tree-vectorize to keep it simple). You can also try it with clang.

.L10:
    mov   eax, eax   # THIS LINE FROM INLINE ASM    # <retval>

    imul    eax, edx        # <retval>, i
    add     edx, 1    # i,
    cmp     edi, edx  # n, i
    jne     .L10      #,
    ret

如您所见,在这种情况下,内联asm语句产生了no-op.( mov eax,eax rax 截断为32位,将高32位清零,但在此函数中它们已经为零.)

As you can see, in this case the inline asm statement produced a no-op. (mov eax,eax truncates rax to 32 bits, zeroing the upper 32 bits, but they were already zero in this function.)

如果我们什么也不想做,例如将寄存器清零,或者从其他来源获取 mov ,我们将破坏该功能.编译器生成的asm仅取决于 asm 语句中列出的约束,而不取决于代码的文本(与MSVC不同).

If we'd don't anything else, like zero the register, or mov from a different source, we would have broken the function. The asm generated by the compiler depends only on the constraints listed in the asm statement, not on the text of the code (unlike MSVC).

请参见标记Wiki了解更多信息,尤其是MSVC和GNU C内联汇编之间的区别.

See the inline-assembly tag wiki for more info, especially this answer on the difference between MSVC and GNU C inline asm.

更重要的是,阅读 https://gcc.gnu.org/wiki/DontUseInlineAsm 在实际将内联汇编用于任何东西之前.

More importantly, read https://gcc.gnu.org/wiki/DontUseInlineAsm before actually using inline asm for anything.

这篇关于在C ++中使用asm代码操作寄存器时会发生什么变化?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆