Much like in 32-bit*, SP is a valid base register for any load/store instruction, so the mechanics aren't all that different. What is different is that SP is no longer a general-purpose register you can do whatever you want with, you don't have variable load/store-multiple operations any more, and there are no programmer-friendly aliases. Thus a typical "push" would be:
stp xn, xm, [sp, #-16]!
Note that you should generally use stp
/ldp
in favour of str
/ldr
in order to maintain alignment when operating on the stack (and especially when you have the hardware alignment checking turned on) - if you only have one register you care about, push/pop xzr
as the other to fill the gap.
* Remember that e.g. "push" in the ARM instruction set is just an assembler alias for stmdb r13!, {rn, ..., rm}
or str rn, [sp, #-4]!
as appropriate, and such aliases were only introduced with the unified assembly language as part of Thumb-2. Specific push/pop instructions did appear in original 16-bit Thumb, as SP can't be encoded in the normal load/store ops there which only operate on low registers.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…