繁体   English   中英

为什么 GCC 在 LDR 之后会产生额外的 ADDS 指令以在 ARM thumb 指令集上加载 .rodata 指针?

[英]Why does GCC produce extra ADDS instruction after LDR for loading an .rodata pointer on ARM thumb instruction set?

这段代码:

const char padding[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };

const char myTable[] = { 1, 2, 3, 4 };

int keepPadding() {
  return (int)(&padding);
}

int foo() {
  return (int)(&myTable);  // <-- this is the part I'm looking at
}

编译为 thumb 指令集的以下程序集(为清楚起见而缩写)。 特别注意adds作为foo的第二条指令:

...
foo:
    @ args = 0, pretend = 0, frame = 0
    @ frame_needed = 0, uses_anonymous_args = 0
    @ link register save eliminated.
    ldr r0, .L5
    @ sp needed
    adds    r0, r0, #10
    bx  lr
.L6:
    .align  2
.L5:
    .word   .LANCHOR0
    .size   foo, .-foo
    .align  1
    .global bar
    .syntax unified
    .code   16
    .thumb_func
    .type   bar, %function

...
myTable:
    .ascii  "\001\002\003\004"

看起来它正在将指针( ldr )加载到myTable的顶部,然后以编程方式偏移到 myTable 的位置( adds )。 但是为什么不直接加载表本身的地址呢?

注意:当我删除const时,它似乎没有ADDS指令(在.data中使用myTable

问题的上下文是我正在尝试手动优化一些 C 固件,并注意到这adds了似乎是多余的指令,所以我想知道是否有办法重组我的代码以摆脱它。

注意:这都是为 ARM thumb 指令集编译的,如下所示(使用 arm-none-eabi-gcc 版本 11.2.1):

arm-none-eabi-gcc -Os -c -mcpu=cortex-m0 -mthumb temp.c -S

另请注意:此处的示例代码旨在表示更大代码库的片段。 如果myTable是唯一编译的东西,那么它会在.rodata中的偏移量 0 处着陆,并且adds指令消失,但这不是典型的真实场景。 为了表示生成此程序集的典型真实场景,我在表格前添加了填充。

另请参阅此处它在 Godbolt 上复制

这个问题最初只包含这个:

const char myTable[] = { 1, 2, 3, 4 };
int foo() {
  return (int)(&myTable);
}


arm-none-eabi-gcc -Os -c -mthumb so.c -o so.o
arm-none-eabi-objdump -D so.o

但它没有产生添加:

Disassembly of section .text:

00000000 <foo>:
   0:   4800        ldr r0, [pc, #0]    ; (4 <foo+0x4>)
   2:   4770        bx  lr
   4:   00000000    andeq   r0, r0, r0

Disassembly of section .rodata:

00000000 <myTable>:
   0:   04030201    streq   r0, [r3], #-513 ; 0xfffffdff

该问题已被编辑以显示可重复的示例,因此该答案已被编辑。 但我只会留下答案,朝着相同的解决方案努力。 可能有趣的是,到达锚点需要一些组件以避免问题被优化。

所以从你的问题和这个:

const char padding[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
const char myTable[] = { 1, 2, 3, 4 };
int foo() {
  return (int)(&myTable);
}

很明显为什么 myTable 的偏移量为 10。

但是填充被优化了,所以你仍然会得到相同的结果。

所以:

const char padding[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
const char myTable[] = { 1, 2, 3, 4 };
int keepPadding() {
  return (int)(&padding);
}
int foo() {
  return (int)(&myTable);
}

该函数的名称意味着您已经知道所有这些,并且知道如何制作一个最小示例等。

arm-none-eabi-gcc -Os -c -mthumb so.c -S


foo:
    @ Function supports interworking.
    @ args = 0, pretend = 0, frame = 0
    @ frame_needed = 0, uses_anonymous_args = 0
    @ link register save eliminated.
    ldr r0, .L5
    @ sp needed
    adds    r0, r0, #10
    bx  lr
.L6:
    .align  2
.L5:
    .word   .LANCHOR0
    .size   foo, .-foo
    .global myTable
    .global padding
    .section    .rodata
    .set    .LANCHOR0,. + 0
    .type   padding, %object
    .size   padding, 10
padding:
    .space  10
    .type   myTable, %object
    .size   myTable, 4
myTable:
    .ascii  "\001\002\003\004"
    .ident  "GCC: (GNU) 11.2.0"

它正在生成一个锚点,然后从锚点引用而不是直接引用标签。

我怀疑这是为了优化 ldr。 我们试试看:

 arm-none-eabi-gcc -Os -c -mthumb -mcpu=cortex-m4 so.c -S

foo:
    @ args = 0, pretend = 0, frame = 0
    @ frame_needed = 0, uses_anonymous_args = 0
    @ link register save eliminated.
    ldr r0, .L5
    bx  lr
.L6:
    .align  2
.L5:
    .word   .LANCHOR0+10
    .size   foo, .-foo

00000008 <foo>:
   8:   4800        ldr r0, [pc, #0]    ; (c <foo+0x4>)
   a:   4770        bx  lr
   c:   0000000a    .word   0x0000000a

是的,所以修复了它,但是如何链接它

Disassembly of section .rodata:

00000000 <padding>:
    ...

0000000a <myTable>:
   a:   04030201    streq   r0, [r3], #-513 ; 0xfffffdff

Disassembly of section .text:

00000010 <keepPadding>:
  10:   4800        ldr r0, [pc, #0]    ; (14 <keepPadding+0x4>)
  12:   4770        bx  lr
  14:   00000000    andeq   r0, r0, r0

00000018 <foo>:
  18:   4801        ldr r0, [pc, #4]    ; (20 <foo+0x8>)
  1a:   300a        adds    r0, #10
  1c:   4770        bx  lr
  1e:   46c0        nop         ; (mov r8, r8)
  20:   00000000    andeq   r0, r0, r0

不,希望链接器将替换 pc-relative 负载并将其转换为 mov r0,#0...保存负载,这(可能)是对非 cortex-m(甚至是 cortex)系统的优化-m)。

注意:这也有效

arm-none-eabi-gcc -Os -c -mthumb -fno-section-anchors so.c -o so.o

00000008 <foo>:
   8:   4800        ldr r0, [pc, #0]    ; (c <foo+0x4>)
   a:   4770        bx  lr
   c:   00000000    andeq   r0, r0, r0
foo:
    @ Function supports interworking.
    @ args = 0, pretend = 0, frame = 0
    @ frame_needed = 0, uses_anonymous_args = 0
    @ link register save eliminated.
    ldr r0, .L5
    @ sp needed
    bx  lr
.L6:
    .align  2
.L5:
    .word   myTable
    .size   foo, .-foo
    .global myTable
    .section    .rodata
    .type   myTable, %object
    .size   myTable, 4
myTable:
    .ascii  "\001\002\003\004"
    .global padding
    .type   padding, %object
    .size   padding, 10

没有使用锚点,所以直接使用了 myTable 的地址。

从我的角度来看,“为什么”是因为使用了锚点并且前面的填充导致 myTable 与锚点发生偏移。 因此,负载加载锚地址,然后添加将您从锚点添加到表中。

为什么是锚? 为读者或其他人练习。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM