The address had to be split in two parts because this specific constant cannot be loaded into a register with a single instruction.
The ARM documentation specifies limitations for the immediate constants allowed in some instructions (such as MOV
):
In ARM instructions, constant can have any value that can be produced
by rotating an 8-bit value right by any even number of bits within a
32-bit word.
In 32-bit Thumb-2 instructions, constant can be:
Any constant that can be produced by shifting an 8-bit value left by
any number of bits within a 32-bit word.
Any constant of the form 0x00XY00XY.
Any constant of the form 0xXY00XY00.
Any constant of the form 0xXYXYXYXY.
The value 111111
(1B207
in hex) can't be represented as any of the above, so the compiler had to split it.
110592
is 1B000
so it fulfills the first condition (an 8-bit value 0x1B rotated left by 12 bits) and can be loaded using MOV
instruction.
The STR
instruction, on the other hand, has a different set of limitations for the offsets used. In particular, 519 (0x207) falls into the -4095 to 4095 range allowed for the word store/load in ARM mode.
In this specific case the compiler managed to split the constant in only two parts. If your immediate has more bits, it may have to generate even more instructions, or use a literal pool load. For example, if I use 0xABCDEF78
, I get this (for ARMv7):
movw r3, #61439
movt r3, 43981
mov r2, #4096
str r2, [r3, #-135]
mov r0, #0
bx lr
For architectures without MOVW/MOVT (e.g. ARMv4), GCC seems to fall back to literal pool:
mov r2, #4096
ldr r3, .L2
str r2, [r3, #-135]
mov r0, #0
bx lr
.L3:
.align 2
.L2:
.word -1412567041