为 arm64 生成代码时,为什么 gcc + clang 使用 adrp+add 而不是 adr 来寻址附近的变量?

When generating code for arm64, why do gcc + clang use adrp+add rather than adr for addressing nearby variables?

对于 arm64,可以使用 adr 指令将附近地址的文字加载到寄存器中。根据 ARM-V8 Architecture Reference Manual adr 指令:

ADR <Xd>, <label>   Address of label at a PC-relative offset

可以引用 +/-1MB 以内的标签。有一个设置了 bit-31 的页面版本,adrp,用于构造更大的偏移量。

我不明白的是为什么 ARM64 的 gcc 8.2 nor clang 7.0 都不使用 adr 而不是 adrpadd 对附近的变量。优化级别不会改变这一点。

int write(int fd, const void *buf, int count);

void xyz(void)
{
    write(2, "abc", 4);
}

xyz(): // @xyz()
  adrp x1, .L.str
  add x1, x1, :lo12:.L.str
  orr w0, wzr, #0x2
  orr w2, wzr, #0x4
  b write(int, void const*, int)
.L.str:
  .asciz "abc"

他们不能推断出这个字符串文字在 +/-1MB 以内吗?有编译器 attribute/switch 告诉他们这个吗?

GCC 将生成这样的代码 -mcmodel=tiny:

    .global xyz
    .type   xyz, %function
xyz:
.LFB0:
    .cfi_startproc
    mov w2, 4
    adr x1, .LC0
    mov w0, 2
    b   write
    .cfi_endproc
.LFE0:
    .size   xyz, .-xyz
    .section    .rodata.str1.8,"aMS",@progbits,1
    .align  3
.LC0:
    .string "abc"