Understanding ARM64 Registers and Memory Management
ARM64 Registers
- X0 – X7: Arguments/Results (no preserved call)
- X8: Indirect result location register (no preserved call)
- X9 – X15: Temporary registers (no preserved call)
- X16: Linker as a scratch register, otherwise, temporary (no preserved call)
- X17: Linker as a scratch register, otherwise, temporary (no preserved call)
- X18: Platform register for platform independent code, otherwise, temporary (no preserved call)
- X19 – X27: Saved (preserved call)
- X28: Stack pointer (preserved call)
- X29: Frame pointer (preserved call)
- X30: Link register/return address (current instruction +4) (preserved call)
- XZR: Constant value of 0
Memory Management
- The stack is not a region in the set of registers
- The BL instruction doesn’t copy registers to the stack
- FALSE: A procedure should copy all of registers X9-X17 and X19-X28 to the stack before executing the procedure’s computations.
- FALSE: If a procedure will update registers X19, X20, X21, and X22, the procedure should make room on the stack by adding 32 to SP.
- If P is not a leaf procedure, P should always save LR on the stack
- If P will write to X0 to pass a parameter to Q, P might first need to save X0 to the stack.
- FALSE: When P calls Q, P should expect that Q might pop less from the stack than Q pushed to the stack.
- All of the saved registers and local variables for a procedure call are referred to as an activation record.
- If a running program should create a new array, the array is normally stored on the heap.
- The heap typically grows upwards in memory, while the stack grows downwards.
- If a program allocated huge arrays, the program may cause the heap to run into the stack, causing an error.
Addressing Modes
- Immediate addressing: The operand is a constant within the instruction itself
- Register addressing: The operand is a register
- Base addressing / displacement addressing: The operand is at the memory location whose address is the sum of a register and a constant in the instruction
- PC-relative addressing: The branch address is the sum of the PC and a constant in the instruction
Example Functions
- long long int leaf_example (long long int g, long long int h, long long int i, long long int j) {
- long long int f;
- f = (g+h) – (i+j);
- return f;
ASM
- leaf_example:
- SUBI SP, SP, #24 // adjust stack to make room for 3 items
- STUR X10, [SP,#16] // save register X10 for use afterwards
- STUR X9, [SP,#8] // save register X9 for use afterwards
- STUR X19, [SP,#0] // save register X19 for use afterwards
- ADD X9,X0,X1 // register X9 contains g + h
- ADD X10,X2,X3 // register X10 contains i + j
- SUB X19,X9,X10 // f = X9 – X10, which is (g + h) – (i + j)
- ADD X0,X19,XZR // returns f (X0 = X19 + 0)
- LDUR X19, [SP,#0] // restore register X19 for caller
- LDUR X9, [SP,#8] // restore register X9 for caller
- LDUR X10, [SP,#16] // restore register X10 for caller
- ADDI SP,SP,#24 // adjust stack to delete 3 items
- BR LR // branch back to calling routine
ASM
- long long int fact (long long int n)
- {
- if (nelse return (n * fact(n – 1));
- }
ASM
- fact:
- SUBI SP, SP, #16 // adjust stack for 2 items
- STUR LR, [SP,#8] // save the return address
- STUR X0, [SP,#0] // save the argument n
- SUBIS ZXR,X0, #1 // test for nB.GE L1 // if n >= 1, go to L1
- ADDI X1,XZR, #1 // return 1
- ADDI SP,SP,#16 // pop 2 items off stack
- BR LR // return to caller
- L1: SUBI X0,X0,#1 // n >= 1: argument gets (n – 1)
- BL fact // call fact with (n – 1)
- LDUR X0, [SP,#0] // return from BL: restore argument n
- LDUR LR, [SP,#8] // restore the return address
- ADDI SP, SP, #16 // adjust stack pointer to pop 2 items
- MUL X1,X0,X1 // return n * fact (n – 1)
- BR LR // return to the caller
ASM
- MOVZ X19, 7, LSL 16
- Output:
- 00000000 00000000 00000000 00000000 00000000 00000111 00000000 00000000
ASM
- MOVK X19, 8, LSL 0
- Output:
- 00000000 00000000 00000000 00000000 00000000 00000111 00000000 00001000
Arithmetic Operations
- LDURB, LDURH are load instructions used for byte and halfword arithmetic operations.
- ADD, SUB, MULT, DIV, are arithmetic instructions or byte and halfword arithmetic operations.
- STURB, STURH are store instructions used for byte and halfword arithmetic operations.
- If we ignore the sign bits, the length of the multiplication of an n-bit multiplicand and an m-bit multiplier is a product that is n + m bits long.
Multiplication Process
OLD
- The multiplication process involves shifting the multiplicand and multiplier registers to obtain the product.
- Each step of the multiplication shifts the multiplier 1 bit to the right.
- The multiplier register is 64 bits long.
- Each step of the multiplication shifts the multiplicand register 1 bit to the left.
- The multiplicand register is 128 bits long.
- The product register is 128 bits long.
- Each iteration of the multiplication consists of 3 basic steps.
NEW
- The speed up comes from performing the operations in parallel: the multiplier and multiplicand are shifted while the multiplicand is added to the product if the multiplier bit is a 1.
- The product register is 128 bits long.
- Signed multiplication involves converting the multiplier and multiplicand to positive numbers and then remembering their original signs.
- The algorithms should next be run for 31 iterations, leaving the signs out of the calculation.
- When the algorithm completes, the lower doubleword would have the 64-bit product.
- To produce a properly signed or unsigned 128-bit product, LEGv8 has three instructions:
- To get the integer 64-bit product, the programmer uses MUL.
- To get the upper 64 bits of the 128-bit product, the programmer uses either SMULH (signed) or UMULH (unsigned), depending on the types of multiplier and multiplicand.
