In AArch64 (ARMv8-A), the main difference between LDR and LDUR is how they handle the immediate offset from the base address.
LDR(Load Register): Uses a scaled positive immediate offset. It is the standard instruction for loading data from validly aligned structures and arrays.LDUR(Load Register Unscaled): Uses an unscaled signed immediate offset. It is used for accessing data at negative offsets or unaligned addresses thatLDRcannot reach.
Here is a detailed breakdown of the differences:
1. Offset Scaling
LDR(Scaled): The immediate value you provide is automatically multiplied (scaled) by the size of the data you are loading (4 for 32-bit, 8 for 64-bit).- Example:
LDR X0, [X1, #8]— The encoded immediate is actually1. The CPU calculatesBase + (1 * 8). - Constraint: The offset must be a multiple of the data size (e.g., you cannot use an offset of
#3for a 64-bit load).
- Example:
LDUR(Unscaled): The immediate value is treated exactly as written, in bytes.- Example:
LDUR X0, [X1, #3]— The encoded immediate is3. The CPU calculatesBase + 3. - Benefit: The offset does not need to be a multiple of the data size, allowing access to unaligned data.
- Example:
2. Offset Range & Sign
LDR: Supports a large, unsigned (positive only) range.- 32-bit load: 0 to 16,380 bytes (must be multiple of 4).
- 64-bit load: 0 to 32,760 bytes (must be multiple of 8).
LDUR: Supports a small, signed range.- Range: -256 to +255 bytes.
- Benefit: This allows you to load data stored before the base register address (negative offsets).
3. Assembler “Magic” (Aliasing)
In practice, you rarely need to type LDUR manually. Most AArch64 assemblers are smart enough to choose the correct opcode for you based on the offset you write.
If you write:
LDR X0, [X1, #-8] ; Negative offset
The assembler sees the negative sign, realizes the standard LDR encoding cannot handle it, and automatically compiles it into the machine code for LDUR.
Similarly, if you write:
LDR X0, [X1, #3] ; Unaligned offset
The assembler detects that #3 is not a multiple of 8 (for a 64-bit load) and emits an LDUR instruction.
Summary Table
| Feature | LDR (Immediate) | LDUR |
|---|---|---|
| Full Name | Load Register | Load Register (Unscaled) |
| Offset Type | Scaled (Multiplied by transfer size) | Unscaled (Raw byte count) |
| Offset Sign | Positive (Unsigned) | Signed (Positive or Negative) |
| Offset Range | Large (0 to ~32KB) | Small (-256 to +255 bytes) |
| Alignment | Offset must be aligned to data size | Offset can be unaligned |
| Primary Use | Array/Struct access | Negative offsets, unaligned fields |
Note: Do not confuse LDUR (Unscaled) with LDTR, which stands for “Load Register Unprivileged.” LDTR is used to perform a load as if the CPU were in a lower exception level (e.g., User mode), usually for permission checking in OS kernels.
STUR (Store Register Unscaled)
It is the direct counterpart to LDUR.
- Purpose: Stores a register to memory using an unscaled, signed immediate offset.
- When to use: It is used when you need to store data at an address offset that is not a multiple of the data size (unaligned) or is negative.
- Assembler behavior: Just like with
LDR/LDUR, you rarely typeSTURmanually. If you writeSTR X0, [X1, #-5], the assembler automatically converts it toSTURbecause standardSTRcannot handle unaligned or negative immediate offsets in that specific way.