0

I am new to learning about assembly and find it very interesting. I am trying to dissect what the purpose of setting registers x8 and x9 are in this example. Specifically, I am unsure as to why x8 and x9 are set at all if the printf function relies on registers x0 - x7 for the input it needs, and that we don't need to access the values saved in x8 and x9 later on anyway. I am guessing it maybe has to do with the fact that printf might manipulate the values in the input registers in some way that we would need to preserve the values in x8 / 9 if we were to need them later on (which we don't, but...). Printf just seems sort of like a black-box to me and results in assembly code that I don't see when I call user-defined functions (ie, I have yet to see x8 and x9 used when I call other functions that I wrote in my C program). Any help would be greatly appreciated, thank you!


output: file format mach-o arm64

Disassembly of section __TEXT,__text:

0000000100003f48 <_main>:
100003f48: ff c3 00 d1  sub sp, sp, #48
100003f4c: fd 7b 02 a9  stp x29, x30, [sp, #32]
100003f50: fd 83 00 91  add x29, sp, #32
100003f54: 08 00 80 52  mov w8, #0
100003f58: a8 43 1f b8  stur    w8, [x29, #-12]
100003f5c: bf c3 1f b8  stur    wzr, [x29, #-4]
100003f60: a8 00 80 52  mov w8, #5
100003f64: a8 83 1f b8  stur    w8, [x29, #-8]
100003f68: a9 83 5f b8  ldur    w9, [x29, #-8]
100003f6c: e8 03 09 aa  mov x8, x9
100003f70: e9 03 00 91  mov x9, sp
100003f74: 28 01 00 f9  str x8, [x9]
100003f78: 00 00 00 90  adrp    x0, 0x100003000 <_main+0x30>
100003f7c: 00 b0 3e 91  add x0, x0, #4012
100003f80: 08 00 00 94  bl  0x100003fa0 <_printf+0x100003fa0>
100003f84: a0 43 5f b8  ldur    w0, [x29, #-12]
100003f88: a8 83 5f b8  ldur    w8, [x29, #-8]
100003f8c: 08 05 00 11  add w8, w8, #1
100003f90: a8 83 1f b8  stur    w8, [x29, #-8]
100003f94: fd 7b 42 a9  ldp x29, x30, [sp, #32]
100003f98: ff c3 00 91  add sp, sp, #48
100003f9c: c0 03 5f d6  ret

Disassembly of section __TEXT,__stubs:

0000000100003fa0 <__stubs>:
100003fa0: 10 00 00 b0  adrp    x16, 0x100004000 <__stubs+0x4>
100003fa4: 10 02 40 f9  ldr x16, [x16]
100003fa8: 00 02 1f d6  br  x16

This is the C code that was used to generate the above snippet:

#include <stdio.h>

int main()
{
    int x = 5;
    printf("x is %d\n", x);
    return 0;
}
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • You compiled without optimization so a lot of that code is just noise, not surprising it uses multiple temporary registers. This is Apple clang compiling for MacOS, I assume; so it's storing/reloading a `0` for the return value (because [that's what clang does in `main` if you tell it not to optimize](https://stackoverflow.com/questions/49300094/unoptimized-clang-code-generates-unneeded-movl-0-4rbp-in-a-trivial-mai), regardless of execution unconditionally reaching a `return` statement with a constant, with a type that fits in a register.) – Peter Cordes Feb 05 '23 at 23:30
  • Even at `-O3`, `clang -target arm64-macos` stores a `5` to the stack before calling printf, https://godbolt.org/z/KT353h7TK . Oh right, Apple's calling convention for ARM64 passes variadic args on the stack, not registers, and it's easier to see that in optimized code. See also [Calling printf from aarch64 asm code on Apple M1 / MacOS](https://stackoverflow.com/q/69454175) – Peter Cordes Feb 05 '23 at 23:32
  • IDK why clang picks x8 and x9 specifically as temporaries, if that was your question. Probably it has a list of call-clobbered registers to choose from when it needs a temporary, and picks in preference order. As for why the devs set up the list that way, perhaps *because* those are free but not used for arg-passing, so can be used while calculating other args, or without disturbing incoming args if something else needs to happen first. – Peter Cordes Feb 05 '23 at 23:45
  • Thank you for the responses, I found them very helpful. My question was not as much why those registers are chosen as temporaries, but why values are stored there at all in this example? When I call a function of my own that I define within a c file, variables in main are not stored in x8 and x9 before passing control over to the function, but are in the case of printf. – Griffin Baker Feb 06 '23 at 00:15
  • 1
    Ok yeah, then [Calling printf from aarch64 asm code on Apple M1 / MacOS](https://stackoverflow.com/q/69454175) is the key duplicate; you probably didn't declare your functions as variadic, like `int foo(int x, ...)`, so the calling convention is different, no stack args. – Peter Cordes Feb 06 '23 at 00:52

0 Answers0