In your example,
LDUR R3, [R1, #8]
this instruction will load to R3
the value pointed by R1
plus 8
bytes. This is what the ARM Reference Manual means by byte offset
.So if R1
holds the value 0x50
, this will load the value stored at address 0x58
. The value of R1
will not be modified.
The instruction LDR R3, [R1, #8]
(LDR (immediate) the Unsigned offset variant) produces the same operation, however, the prototype is different:
-- loads a 32-bit valueLDR <Wt>, [<Xn|SP>{, #<pimm>}]-- loads a 64-bit valueLDR <Xt>, [<Xn|SP>{, #<pimm>}]
The immediate offset pimm is different, LDUR uses a simm. This means that the offset is interpreted in a different way. The first (pimm) is a positive offset and its range is different for the 32-bit variant and the 64-bit variant.
In the 32 bit version:
In the 64 bit version:
This means that some of the offsets combinations of LDUR and LDR (immediate) are going to produce the same operation.
LDUR R3, [R1, #8]
if:
R1=50
then:
[R1, #8]
= value of address for 58 (=50
+ #8
)LDUR R3, [R1, #8]
= Load value of address for 58 to R3
registerbut what am I taking from R1 and how does the offset operate?
offset #8
operate normally, just: R1
+ #8
= 50 + 8
= 58
Is it like a logical shift?
no any logical shift.
The ARM manual describes it as "byte offset" but then doesn't describe how that offset functions on R1.
ARM manual is here: LDUR, the full description is
LDUR Wt, [Xn|SP{, #simm}] ; 32-bit general registers...simmIs the optional signed immediate byte offset, in the range -256 to 255, defaulting to 0.
I have same confusion same with you before. now clear:
Function: Load Register (immediate)
syntax
LDR Wt, [Xn|SP], #simm ; 32-bit general registers, Post-indexLDR Xt, [Xn|SP], #simm ; 64-bit general registers, Post-indexLDR Wt, [Xn|SP, #simm]! ; 32-bit general registers, Pre-indexLDR Xt, [Xn|SP, #simm]! ; 64-bit general registers, Pre-indexLDR Wt, [Xn|SP{, #pimm}] ; 32-bit general registersLDR Xt, [Xn|SP{, #pimm}] ; 64-bit general registers
->
-》
Note:
-256 ~ 255
0 ~ 16380
pimm % 4 == 0
0 ~ 32760
pimm % 8 == 0
Function: Load register (unscaled offset)
syntax:
LDUR Wt, [Xn|SP{, #simm}] ; 32-bit general registersLDUR Xt, [Xn|SP{, #simm}] ; 64-bit general registers
->
Note:
-256 ~ 255
Let's talk about LDR first:
It supports 3 ways to get value
Note:
The commonly used writing method here is: the third:
ldur q0, [x19, #0xa8]
which is:
The last part is:
[register name, #immediate data]
And: LDUR also supports the third type (the first and second types are not supported)
Then comes the difference:
LDR Wt/Xt, [Xn|SP{, #pimm}]
0 ~ 16380
, and pimm % 4 == 0
(is a multiple of 4)* 64-bit: 0 ~ 32760
, and pimm % 8 == 0
(is a multiple of 8)-256 ~ 255
-> must be a multiple of 4 (or 8), which is called:
offset (i.e. imm here) is scaled by 4 (or 8)
-> Here:
-》
-> which is:
Syntax of LDR and LDUR:
LDR Wt/Xt, [Xn|SP{, #imm}]
the core differences are:
The difference is found, that:
From Arm® A64 Instruction Set Architecture: Armv8, for Armv8-A architecture profile
if HaveMTEExt() thenboolean is_load_store = MemOp_LOAD IN {MemOp_STORE, MemOp_LOAD};SetNotTagCheckedInstruction(is_load_store && n == 31);bits(64) address;bits(datasize) data;if n == 31 thenCheckSPAlignment();address = SP[];elseaddress = X[n];address = address + offset;data = Mem[address, datasize DIV 8, AccType_NORMAL];X[t] = ZeroExtend(data, regsize);
this pseudo-code shows how does the offset operates is applied.
In unsigned offset mode, LDR's imm should keep 4 byte or 8 byte align.
If imm % 4 == 0, stur Wt, [Xn|SP, #imm] is equal to str Wt, [Xn|SP, #imm] in A32.
If imm % 8 == 0, stur Xt, [Xn|SP, #imm] is equal to str Xt, [Xn|SP, #imm] in A64.
LDR can not addressing from -256 to 255 byte by byte while keeping the base register unchanged. That's what LDUR does.