STP: Store Pair of Registers

The STP (Store Pair) instruction in ARM64 is used to store two registers into memory in a single operation. It is commonly used to save registers onto the stack or to store data in memory efficiently.

Syntax

STP , , [, #]

Parameters

Example


    // Store x0 and x1 into memory at the address in x2
    STP x0, x1, [x2]

    // Store x0 and x1 into memory at the address in x2 with an offset of 16 bytes
    STP x0, x1, [x2, #16]
    

C-Code Example

The following C-code demonstrates a scenario that could translate to the use of the STP instruction:


    void save_registers(uint64_t *memory, uint64_t reg1, uint64_t reg2) {
        memory[0] = reg1; // Corresponds to storing the first register
        memory[1] = reg2; // Corresponds to storing the second register
    }
    

In assembly, this could translate to:


    // Assuming x0 = memory, x1 = reg1, x2 = reg2
    STP x1, x2, [x0]
    

Use Cases

Function Call Context

This is common to see when calling functions. For example, when a function is called, the caller may need to save certain registers onto the stack to preserve their values. This is often done using the STP instruction to save multiple registers efficiently:


    // Save x29 (frame pointer) and x30 (link register) onto the stack
    STP x29, x30, [sp, #-16]!
    
    // Restore x29 and x30 from the stack
    LDP x29, x30, [sp], #16
    

In this example, the STP instruction is used to save the frame pointer and link register at the beginning of a function, and the LDP (Load Pair) instruction is used to restore them at the end of the function.

LDR: Load Register

The LDR (Load Register) instruction in ARM64 is used to load a value from memory into a register. It is commonly used to retrieve data from memory for processing.

Syntax

LDR , [, #]

Parameters

Example


    // Load the value at the address in x2 into x0
    LDR x0, [x2]

    // Load the value at the address in x2 with an offset of 16 bytes into x0
    LDR x0, [x2, #16]
    

C-Code Example

The following C-code demonstrates a scenario that could translate to the use of the LDR instruction:


    uint64_t load_register(uint64_t *memory) {
        return memory[0]; // Corresponds to loading the value from memory
    }
    

In assembly, this could translate to:


    // Assuming x0 = memory, x1 = destination register
    LDR x1, [x0]
    

Use Cases

Function Call Context

This is common to see when accessing function arguments or local variables stored in memory. For example, when a function accesses a variable stored on the stack, the LDR instruction is often used:


    // Load a variable from the stack into x0
    LDR x0, [sp, #8]
    

In this example, the LDR instruction is used to load a value from the stack into a register for further processing.

STR: Store Register

The STR (Store Register) instruction in ARM64 is used to store the value of a register into memory. It is commonly used to save data from a register into a specific memory location.

Syntax

STR , [, #]

Parameters

Example


    // Store the value of x0 into memory at the address in x2
    STR x0, [x2]

    // Store the value of x0 into memory at the address in x2 with an offset of 16 bytes
    STR x0, [x2, #16]
    

C-Code Example

The following C-code demonstrates a scenario that could translate to the use of the STR instruction:


    void store_register(uint64_t *memory, uint64_t value) {
        memory[0] = value; // Corresponds to storing the value into memory
    }
    

In assembly, this could translate to:


    // Assuming x0 = memory, x1 = value
    STR x1, [x0]
    

Use Cases

CSET: Conditional Set

The CSET (Conditional Set) instruction in ARM64 is used to set a register to 1 or 0 based on the condition flags. It is commonly used for conditional operations where a boolean value is needed.

Syntax

CSET , 

Parameters

Example


// Set x0 to 1 if the "equal" condition is true, otherwise set it to 0
CSET x0, EQ

// Set x1 to 1 if the "greater than" condition is true, otherwise set it to 0
CSET x1, GT

C-Code Example

The following C-code demonstrates a scenario that could translate to the use of the CSET instruction:


uint64_t conditional_set(bool condition) {
    return condition ? 1 : 0; // Corresponds to setting the register based on a condition
}

In assembly, this could translate to:


// Assuming the condition flags are set appropriately
CSET x0, EQ

Use Cases

Function Call Context

This is common to see when evaluating conditions in low-level code. For example, when a comparison is made, the CSET instruction can be used to store the result of the comparison as a boolean value:


// Compare x0 and x1, and set x2 to 1 if x0 is less than x1, otherwise set it to 0
CMP x0, x1
CSET x2, LT

In this example, the CMP instruction sets the condition flags based on the comparison, and the CSET instruction uses those flags to set the value of the destination register.

ADRP: Adjust PC-relative Address

The ADRP (Adjust PC-relative Address) instruction in ARM64 is used to calculate a page-aligned address relative to the program counter (PC). It is commonly used for accessing global variables or constants in a position-independent manner.

Syntax

ADRP , 

Parameters

Example


// Calculate the page-aligned address of a label and store it in x0
ADRP x0, my_label

C-Code Example

The following C-code demonstrates a scenario that could translate to the use of the ADRP instruction:


extern uint64_t global_variable;

uint64_t *get_global_address() {
    return &global_variable; // Corresponds to calculating the address of a global variable
}

In assembly, this could translate to:


// Assuming my_label corresponds to the address of global_variable
ADRP x0, my_label

Use Cases

Function Call Context

This is common to see when accessing global variables or constants in position-independent code. For example, when a global variable is accessed, the ADRP instruction is often used to calculate the base address:


// Calculate the page-aligned address of a global variable
ADRP x0, global_variable

// Add the offset within the page to get the exact address
ADD x0, x0, :lo12:global_variable

In this example, the ADRP instruction calculates the page-aligned address, and the ADD instruction adds the offset within the page to get the exact address of the global variable.

o

Data Sizes in ARM64

In ARM64, data sizes are often referred to using specific terms. Here are the common data sizes and their corresponding examples:

General Purpose Registers in AArch64

AArch64, the 64-bit execution state of the ARMv8 architecture, provides a set of 31 general-purpose registers (GPRs) for use in various operations. These registers are versatile and can be used for data storage, arithmetic operations, addressing, and more.

Register Overview

Special Registers

Zero and Stack Pointer Register

The x31 register has a dual role:

C-Code and Assembly Examples for All Registers

Example 1: Function Arguments and Return Values (x0 - x7)

C-Code:


uint64_t sum_of_args(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e, uint64_t f, uint64_t g, uint64_t h) {
    return a + b + c + d + e + f + g + h;
}

Assembly:


// x0 = a, x1 = b, x2 = c, x3 = d, x4 = e, x5 = f, x6 = g, x7 = h
ADD x0, x0, x1  // x0 = a + b
ADD x0, x0, x2  // x0 = (a + b) + c
ADD x0, x0, x3  // x0 = ((a + b) + c) + d
ADD x0, x0, x4  // x0 = (((a + b) + c) + d) + e
ADD x0, x0, x5  // x0 = ((((a + b) + c) + d) + e) + f
ADD x0, x0, x6  // x0 = (((((a + b) + c) + d) + e) + f) + g
ADD x0, x0, x7  // x0 = ((((((a + b) + c) + d) + e) + f) + g) + h
RET

Example 2: Temporary Registers (x9 - x15)

C-Code:


uint64_t complex_operation(uint64_t a, uint64_t b) {
    uint64_t temp1 = a + b;
    uint64_t temp2 = a * b;
    return temp1 ^ temp2;
}

Assembly:


// x0 = a, x1 = b
ADD x9, x0, x1  // x9 = a + b (temporary register)
MUL x10, x0, x1 // x10 = a * b (temporary register)
EOR x0, x9, x10 // x0 = (a + b) ^ (a * b)
RET

Example 3: Callee-Saved Registers (x19 - x28)

C-Code:


void preserve_registers(uint64_t *memory, uint64_t value) {
    memory[0] = value;
}

Assembly:


// x0 = memory, x1 = value
STP x19, x20, [sp, #-16]!  // Save x19 and x20 on the stack
MOV x19, x1                // Preserve value in x19
STR x19, [x0]              // Store value into memory
LDP x19, x20, [sp], #16    // Restore x19 and x20 from the stack
RET

Example 4: Frame Pointer and Link Register (x29, x30)

C-Code:


void function_prologue_and_epilogue() {
    // Function body
}

Assembly:


// Function prologue
STP x29, x30, [sp, #-16]!  // Save frame pointer and link register
MOV x29, sp                // Set frame pointer

// Function body
NOP                        // Placeholder for function logic

// Function epilogue
LDP x29, x30, [sp], #16    // Restore frame pointer and link register
RET

Example 5: Zero Register and Stack Pointer (x31)

C-Code:


void zero_register_example(uint64_t *memory) {
    memory[0] = 0; // Store zero into memory
}

Assembly:


// x0 = memory
STR xzr, [x0]  // Store zero (xzr) into memory
RET

Explanation: The zero register (xzr) always reads as 0 and writes to it are ignored.

Example Usage


// Add the values in x0 and x1, store the result in x2
ADD x2, x0, x1

// Store the value in x2 to memory at the address in x3
// Think: Put the value in x2 into the location pointed to by x3. After this, the memory at address x3 now contains whatever was in x2.
STR x2, [x3]

// Load a value from memory at the address in x3 into x4
// Think: Take the value at the location pointed to br x3 and put it into x4. After this, x4 contains whatever was in memory at address x3. This is the opposite of STR.
// So if you did STR x2, [x3] before this, x4 will now contain the value that was in x2.
LDR x4, [x3]

// Call a function, saving the return address in x30 (LR)
BL my_function

Use Cases

The general-purpose registers in AArch64 provide flexibility and efficiency for a wide range of programming tasks, making them a cornerstone of the architecture.

Program Counter (PC) and Stack Pointer (SP)

The Program Counter (PC) and Stack Pointer (SP) are special-purpose registers in ARM64 that play crucial roles in program execution and memory management.

Program Counter (PC)

Stack Pointer (SP)

Difference Between Stack Pointer (SP) and Frame Pointer (FP)

While both the Stack Pointer (SP) and Frame Pointer (FP) are used in stack management, they serve different purposes:

Example


// Function prologue: Save the old frame pointer and link register, set up a new frame
STP x29, x30, [sp, #-16]!
MOV x29, sp

// Function epilogue: Restore the old frame pointer and link register
LDP x29, x30, [sp], #16

In this example, the SP is used to manage the stack, while the FP (x29) provides a stable reference for the current function's stack frame.

Main Uses of x0 in AArch64