[ARM] Accessing a static global object fails to optimize based on relative member positions
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
When accessing members of a static global object, the pointer to each member is calculated independently, and that prevents any optimizations based on relative positions within the object.
Interestingly, if a pointer to the static global object is allowed to escape the translation unit, the behavior changes and each member is accessed relative to the start of the object. This matches external global behavior, except that it doesn't use the GOT to resolve the object address.
Using `-mglobal-merge` mitigates the issue by merging all static global objects into known relative positions, but that seems heavy-handed when positions within the same object should already be known (and this flag is not enabled by default below `-O3`).
Example on compiler explorer: https://godbolt.org/z/8rbG8hdf5
```c
STATIC struct test {
int a, b, c, d;
} g;
void test(void) {
g.a++;
g.b++;
g.c++;
g.d++;
}
#ifdef ESCAPE
int* escape(void) {
return &g.d;
}
#endif
```
Non-static global, accessed through GOT: `-DSTATIC= -Os -fpic`
```asm
test:
ldr r0, .LCPI0_0
.LPC0_0:
ldr r0, [pc, r0]
ldm r0, {r1, r2, r3, r12}
add r1, r1, #1
add r2, r2, #1
add r3, r3, #1
add r12, r12, #1
stm r0, {r1, r2, r3, r12}
bx lr
.LCPI0_0:
.Ltmp2:
.long g(GOT_PREL)-(.LPC0_0+8-.Ltmp2)
g:
.zero 16
```
Static global: `-DSTATIC=static -Os -fpic`
```asm
test:
ldr r0, .LCPI0_0
ldr r1, .LCPI0_1
ldr r12, .LCPI0_2
ldr r3, .LCPI0_3
ldr r2, .LCPI0_4
.LPC0_0:
add r0, pc, r0
.LPC0_3:
add r3, pc, r3
.LPC0_1:
add r1, pc, r1
.LPC0_4:
ldr r2, [pc, r2]
.LPC0_2:
add r12, pc, r12
add r2, r2, #1
str r2, [r3]
ldr r2, .LCPI0_5
.LPC0_5:
ldr r2, [pc, r2]
add r2, r2, #1
str r2, [r0]
ldr r0, .LCPI0_6
.LPC0_6:
ldr r0, [pc, r0]
add r0, r0, #1
str r0, [r1]
ldr r0, .LCPI0_7
.LPC0_7:
ldr r0, [pc, r0]
add r0, r0, #1
str r0, [r12]
bx lr
.LCPI0_0:
.long g.1-(.LPC0_0+8)
.LCPI0_1:
.long g.2-(.LPC0_1+8)
.LCPI0_2:
.long g.3-(.LPC0_2+8)
.LCPI0_3:
.long g.0-(.LPC0_3+8)
.LCPI0_4:
.long g.0-(.LPC0_4+8)
.LCPI0_5:
.long g.1-(.LPC0_5+8)
.LCPI0_6:
.long g.2-(.LPC0_6+8)
.LCPI0_7:
.long g.3-(.LPC0_7+8)
```
Static global, with pointer escaped from the TU: `-DESCAPE -DSTATIC=static -Os -fpic`
```asm
test:
ldr r0, .LCPI0_0
ldr r1, .LCPI0_1
.LPC0_0:
add r0, pc, r0
.LPC0_1:
ldr r1, [pc, r1]
ldmib r0, {r2, r3, r12}
add r1, r1, #1
add r2, r2, #1
add r3, r3, #1
add r12, r12, #1
stm r0, {r1, r2, r3, r12}
bx lr
.LCPI0_0:
.long g-(.LPC0_0+8)
.LCPI0_1:
.long g-(.LPC0_1+8)
escape:
ldr r0, .LCPI1_0
.LPC1_0:
add r0, pc, r0
add r0, r0, #12
bx lr
.LCPI1_0:
.long g-(.LPC1_0+8)
```
Static global, with merged globals: `-DSTATIC=static -mglobal-merge -Os -fpic`
```asm
test:
ldr r0, .LCPI0_0
ldr r1, .LCPI0_1
.LPC0_0:
add r0, pc, r0
.LPC0_1:
ldr r1, [pc, r1]
ldmib r0, {r2, r3, r12}
add r1, r1, #1
add r2, r2, #1
add r3, r3, #1
add r12, r12, #1
stm r0, {r1, r2, r3, r12}
bx lr
.LCPI0_0:
.long .L_MergedGlobals-(.LPC0_0+8)
.LCPI0_1:
.long .L_MergedGlobals-(.LPC0_1+8)
g.0 = .L_MergedGlobals
g.1 = .L_MergedGlobals+4
g.2 = .L_MergedGlobals+8
g.3 = .L_MergedGlobals+12
```
Contributor guide
Research direction
Reproduce the ARM outputs from the Compiler Explorer example with -fpic, comparing static, escaped, non-static, and -mglobal-merge cases. Start by tracing the ARM compiler path that lowers accesses to static global members, then add a regression test showing that relative member positions are used without requiring global merging; done means the generated code matches the intended optimized pattern.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c
- Domain
- compilers, performance
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 43/100