How do I write the following inline Assembly Code in C using GCC

匿名 (未验证) 提交于 2019-12-03 01:10:02

问题:

I was reading some answers and questions on here and kept coming up with this suggestion but I noticed no one ever actually explained "exactly" what you need to do to do it, On Windows using Intel and GCC compiler. Commented below is exactly what I am trying to do.

#include   int main() {     int x = 1;     int y = 2;     //assembly code begin     /*       push x into stack; 

回答1:

Let the compiler choose the registers, using the k prefix to denote a 32-bit register for the int type (so it works as expected on x86-64) :

__asm__ ("pushl %k0\n\t"          "movl %k1, %k0\n\t"          "popl %k1"          : "+r" (x), "+r" (y)); 

It's not necessary (and actually wrong) to specify any clobbered operands in this case.


It's also apparent that x,y are commutative here, i.e., swapping the operands should still yield the same result using: : "+%r" (x), "+r" (y) where % denotes that this operand and the next operand may commute.



回答2:

You can't just push/pop safely from inline asm, if it's going to be portable to systems with a red-zone. That includes every non-Windows x86-64 platform. (There's no way to tell gcc you want to clobber it). Well, you could add rsp, -128 first to skip past the red-zone before pushing/popping anything, then restore it later. But then you can't use an "m" constraints, because the compiler might use RSP-relative addressing with offsets that assume RSP hasn't been modified.

But really this is a ridiculous thing to be doing in inline asm.

Here's how you use inline-asm to swap two C variables:

#include   int main() {     int x = 1;     int y = 2;      asm(""                  // no actual instructions.         : "=r"(y), "=r"(x)   // request both outputs in the compiler's choice of register         :  "0"(x),  "1"(y)   // matching constraints: request each input in the same register as the other output         );     // apparently "=m" doesn't compile: you can't use a matching constraint on a memory operand      printf("x=%d,y=%d\n",x,y);     // getchar();  // Set up your terminal not to close after the program exits if you want similar behaviour: don't embed it into your programs     return 0; } 

gcc -O3 output (targeting the x86-64 System V ABI, not Windows) from the Godbolt compiler explorer:

.section .rodata .LC0:     .string "x=%d,y=%d" .section .text main:     sub     rsp, 8     mov     edi, OFFSET FLAT:.LC0     xor     eax, eax     mov     edx, 1     mov     esi, 2 #APP # 8 "/tmp/gcc-explorer-compiler116814-16347-5i3lz1/example.cpp" 1             # I used "\n" instead of just "" so we could see exactly where our inline-asm code ended up.  # 0 "" 2 #NO_APP     call    printf     xor     eax, eax     add     rsp, 8     ret 

C variables are a high level concept; it doesn't cost anything to decide that the same registers now logically hold different named variables, instead of swapping the register contents without changing the varname->register mapping.

When hand-writing asm, use comments to keep track of the current logical meaning of different registers, or parts of a vector register.


The inline-asm didn't lead to any extra instructions outside the inline-asm block either, so it's perfectly efficient in this case. Still, the compiler can't see through it, and doesn't know that the values are still 1 and 2, so further constant-propagation would be defeated. https://gcc.gnu.org/wiki/DontUseInlineAsm



回答3:

#include   int main() {     int x=1;     int y=2;     printf("x::%d,y::%d\n",x,y);     __asm__( "movl %1, %%eax;"              "movl %%eax, %0;"              :"=r"(y)              :"r"(x)              :"%eax"             );     printf("x::%d,y::%d\n",x,y);     return 0; }  /* Load x to eax Load eax to y */ 

If you want to exchange the values, it can also be done using this way. Please note that this instructs GCC to take care of the clobbered EAX register. For educational purposes, it is okay, but I find it more suitable to leave micro-optimizations to the compiler.



回答4:

You can use extended inline assembly. It is a compiler feature whicg allows you to write assembly instructions within your C code. A good reference for inline gcc assembly is available here.

The following code copies the value of x into y using pop and push instructions.
( compiled and tested using gcc on x86_64 )

  #include       int main()     {         int x = 1;         int y = 2;     asm volatile (          "pushq  %%rax\n"          /* Push x into the stack */          "movq   %%rbx, %%rax\n"   /* Copy y into x         */          "popq   %%rbx\n"          /* Pop  x into y         */       : "=b"(y), "=a"(x)          /* OUTPUT values         */        : "a"(x),  "b"(y)           /* INPUT  values         */       :    /*No need for the clobber list, since the compiler knows              which registers have been modified            */     );            printf("x=%d,y=%d",x,y);         getchar();         return 0;     } 

Result x=2 y=1, as you expected.

The intel compiler works in a similar way, I think you have just to change the keyword asm to __asm__. You can find info about inline assembly for the INTEL compiler here.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!