InfinitelyManic
InfinitelyManic

Reputation: 822

Trying to figure out how to using printf without PUSH & POP; which do not exist in ARMv8. Seems like a lot of extra work

Trying to figure out how to using printf without PUSH & POP; which do not exist in ARMv8. Seems like a lot of extra work.

What am I missing?

armv8 #printf

 12 main:
 13         nop
 14         mov x12, 0xff
 15         mov x1, x12
 16         1:
 17         mov x2, x12
 18         2:
 19
 20         mul x3, x1, x2
 21
 22         bl write                // triggers save of next location in x30
 23
 24         subs x2, x2, 1
 25         bpl 2b
 26         subs x1, x1, 1
 27         bpl 1b
 28
 29 exit:
 30         mov x8, #93             // exit see /usr/include/asm-generic/unistd.h
 31         svc 0
 32
 33 write:
 34         stp x29,x30,[sp]        // save FP and LR
 35         stp x1,x2,[sp,(8*3)]    // store pair of regs on stack
 36         str x12,[sp,(8*5)]      // store single reg on stack
 37         ldr w0,=fmt
 38         bl printf
 39         ldr x12,[sp,(8*5)]
 40         ldp x1,x2,[sp,(8*3)]
 41         ldp x29,x30,[sp]
 42         ret                     // return from function

....

sample out:
255 * 255 =  65025
255 * 254 =  64770
255 * 253 =  64515
255 * 252 =  64260
255 * 251 =  64005
255 * 250 =  63750
255 * 249 =  63495

Upvotes: 1

Views: 672

Answers (1)

Notlikethat
Notlikethat

Reputation: 20934

stp x29,x30,[sp]        // save FP and LR
stp x1,x2,[sp,(8*3)]    // store pair of regs on stack
str x12,[sp,(8*5)]      // store single reg on stack

Since the AArch64 ABI uses a full-descending stack - i.e. it grows downwards, and SP points at the last entry rather than below it - what this code does is trash 5 of the bottom 6 entries of your current stack contents (sparing a gap in the middle, for some reason). You happen to have got lucky via the combination of being at zero call depth in main(), then invoking the exit syscall directly, so you never notice the damage.

You need to adjust SP accordingly to make room for things you're pushing, then move it back up again when you pop them off, which is easily done with standard addressing modes*:

stp x29, x30, [sp, #-16]!  // pre-indexed with writeback 'push'
ldp x29, x30, [sp], #16    // post-indexed 'pop'

Furthermore, SP has to remain 16-byte aligned, so if you've got an odd number of registers to preserve before a call, you can simply push/pop xzr to make up the difference.

* hey, you could even write some macros if you really want to pretend.

Upvotes: 2

Related Questions