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.
What am I missing?
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
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