2

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 <stdio.h>

int main()
{
    int x = 1;
    int y = 2;
    //assembly code begin
    /*
      push x into stack; < Need Help
      x=y;               < With This
      pop stack into y;  < Please
    */
    //assembly code end
    printf("x=%d,y=%d",x,y);
    getchar();
    return 0;
}
4
  • 2
    Why would you want to do that? I see no I see no advantage against the normal tmp = x; x = y; y = tmp;, especially since what you want is the same thing (but using push/pop for the temporary variable). Commented Jul 29, 2013 at 10:29
  • 2
    @JoachimPileborg I want to measure the difference, and also for educational/experimental purposes. Commented Jul 29, 2013 at 10:41
  • 1
    "Measuring the difference" in the given example is meaningless, because the compiler would completely optimize out the "pure C" swap code, and treat it as if you'd written printf("x=%d,y=%d",2,1) directly. Commented Jul 30, 2013 at 22:20
  • Also see GCC inline assembly with stack operation for a discussion of stack maintenance on x86_64. It is too bad no one took the time to explain it in your context or find a duplicate for you. Commented Jun 22, 2018 at 3:28

3 Answers 3

3

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 <stdio.h>

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

Sign up to request clarification or add additional context in comments.

2 Comments

I'd be interested to see the push/pop version. I suspect there's a little more to it because of x86_64 and stack maintenance.
@jww: Use @GiuseppePes's currently-unsafe answer, but put add $-128, %%rsp before it, and sub $-128, %%rsp after it, for the x86-64 SysV ABI with a red-zone. (-128 fits in an imm8, but +128 doesn't, hence the reversal). You don't have to keep the stack 16-byte aligned or anything inside inline asm, you just have to preserve it (because you can't declare a clobber on it). Or compile with -mno-red-zone. What are you really interested in? The only good reason to use the stack from inline asm is making a function call, which you should avoid doing. Otherwise use dummy tmp outputs.
1
#include <stdio.h>

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.

Comments

1

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 )

This is only safe if compiled with -mno-red-zone, or if you subtract 128 from RSP before pushing anything. It will happen to work without problems in some functions: testing with one set of surrounding code is not sufficient to verify the correctness of something you did with GNU C inline asm.

  #include <stdio.h>

    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.

11 Comments

The op intends to swap contents of x and y using the stack as the temporary location. Can you modify your code-snippet to reflect the same?...
Yes, sure! Sorry I forget that operation.
@TheCodeArtist I've updated the code as specified in the question. Thanks for your notification!
Thank you for the code! I am working on fixing some errors changed the asm to asm, it says for me bad register name rax and rbx. Also I am running on a 64 bit machine and my gcc settings I am running are as follows -march=native -O3 -std=iso9899:2011 , did some research native should be working as I am using an i7 processor not sure why it would give that error %rax bad register name...
I ran the code on 64 bit machine as well. I compiled with -std=gnu99 and no optimizations! Why did you change asm to __ asm __ ? in gcc should leave the code unchanged. That is the reason because you are getting %rax error. __ asm __ does not require double %% as asm does.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.