Search code examples
gccarm

arm gcc aarch32 compile longlong constants param why skip r1 register?


using toolchains:

"gcc-arm-none-eabi-9-2020-q2-update"

build cmd:

"arm-none-eabi-gcc -MMD -g -Wno-discarded-qualifiers -O0 -mcpu=cortex-r52 -c -DGCC -mthumb -mfloat-abi=hard -mfpu=fp-armv8 -nostartfiles -ffreestanding -falign-functions=16 -falign-jumps=8 -falign-loops=8 -fomit-frame-pointer -funroll-loops printf.c -o printf.o"

Found that the code:

printf("test hex long number = 0x%lx\n",  0x123456789abcdef0ul);

Compiled as:

  401372:   a315        add r3, pc, #84 ; (adr r3, 4013c8 <printf_test+0xd8>)
  401374:   e9d3 2300   ldrd    r2, r3, [r3]
  401378:   f245 201c   movw    r0, #21020  ; 0x521c
  40137c:   f2c0 0040   movt    r0, #64 ; 0x40
  401380:   f7ff ff76   bl  401270 <_printf>

Why not use "r1" register as params delivery? That make "_printf" print unexpected.

test hex long number = 0x9abcdef000000000

How to fix or workaround? Let "_printf" print as expected "0x123456789abcdef0"


Solution

  • The use of r2/r3 is correct. The AAPCS ABI specifies that 8-byte objects (or more precisely, objects needing 8-byte alignment) shall be passed in an even/odd register pair. See Section 6.3 stage C3. This is most likely so that ldrd/strd can be used, as they have this same restriction.

    The bug in your program is that 0x123456789abcdef0ul is of type unsigned long long despite the ul suffix, since it is too large for the 32-bit unsigned long. As such you need to use the %llx format specifier with it. If you do, then printf will correctly find the argument in r2/r3 and everything works fine.

    With the code as it is, you ought to get a compiler warning about the format specifier not matching the argument type.