[英]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.