GCC不会优化未初始化静态常量的结构副本
首先,我正在开发一个微控制器,所以RAM和ROM的使用是优先考虑的 我意识到这可能是一个错误报告或不够具体。如果我在这里没有得到任何答案,我会将其归档 我喜欢使用GCC不会优化未初始化静态常量的结构副本,c,gcc,optimization,struct,C,Gcc,Optimization,Struct,首先,我正在开发一个微控制器,所以RAM和ROM的使用是优先考虑的 我意识到这可能是一个错误报告或不够具体。如果我在这里没有得到任何答案,我会将其归档 我喜欢使用static conststructs将堆栈结构初始化为默认值。在大多数情况下,默认结构为全零。我更喜欢使用static conststructs而不是memset(,) 我当前的工具链是arm-none-eabi-gcc-4_7_3,为Cortex M4目标编译,并进行优化-Os 我注意到以下几点:;如果我显式地将我的static c
static const
structs将堆栈结构初始化为默认值。在大多数情况下,默认结构为全零。我更喜欢使用static const
structs而不是memset
(,)
我当前的工具链是arm-none-eabi-gcc-4_7_3
,为Cortex M4目标编译,并进行优化-Os
我注意到以下几点:;如果我显式地将我的static const
struct初始化为零,GCC会生成不同的代码,如果我没有(static const struct foo;
vsstatic const struct foo={0};
)。特别是,它将未初始化的静态常量分配给内存并执行复制操作
下面是一个代码示例:
struct foo {int foo; int bar;};
struct bar {int bar[20];};
static const struct foo foo1_init, foo2_init = {0};
static const struct bar bar1_init, bar2_init = {0};
extern struct foo foo1, foo2;
extern struct bar bar1, bar2;
void init_foo1(void)
{
foo1 = foo1_init;
}
void init_foo2(void)
{
foo2 = foo2_init;
}
void init_bar1(void)
{
bar1 = bar1_init;
}
void init_bar2(void)
{
bar2 = bar2_init;
}
编译后,将生成以下汇编程序列表(为了简洁起见,重新排列和修剪):
我们可以看到,对于foo2=init\u foo2
和bar2=init\u bar2
而言,编译器优化了副本,将零直接存储到foo2
,或者调用memset
以bar2
我们可以看到,对于foo1=init\u foo1
和bar1=init\u bar1
编译器正在执行显式复制,加载到foo1
的寄存器并从寄存器中保存,并为foo2
调用memcpy
我有几个问题:
静态常量
结构与初始化的静态常量
结构在GCC中遵循相同的路径,从而产生相同的输出C
到汇编编译器实际上都是C++
编译器我已经在amd64上进行了测试,令我惊讶的是,它看起来像是一个一致的行为(但我不知道它是否是一个bug)。gcc将foo1_init和bar1_init放在公共数据段中,或由操作系统(.bss)初始化的零值段中。foo2_init和bar2_init放在只读段(.rodata)中,就像它们是非零初始化值一样。使用-O0可以看到这一点。因为您没有使用操作系统,所以操作系统初始化部分由gcc和/或链接器手动初始化,然后复制。gcc通过生成直接memset并消除dead*2_init变量来优化rodata值。不过,clang同样优化了这两种情况 以下是gcc输出(-O0):
您可以编辑代码以保持一致性吗?它当前指的是
foo1\u init
等,这些未在代码中定义(它定义的是init\u foo1
)。我想这只是一个输入错误,因为init\u foo1
既是一个变量又是一个函数,在同一个范围内。调用memcpy()
在空间上非常便宜,你有没有将其与内联副本的成本进行比较?也许有一种启发式方法,当字节数足够大时发出调用。1。我怀疑这是因为未初始化的变量只是暂时定义的,而编译器生成的代码并不关心变量是否已完全定义。(也就是说,它不检查变量是否在以后的翻译单元中使用初始值设定项完全定义。)我同意@IanAbbott,如果是这种情况,编译器的行为是正确的,因为您将foo2_init
和bar2_init
明确定义为常量,并且始终==0。因此,正确的复制优化是将目标数组归零(使用memset
)。另一方面,foo1_init
和bar1_init
是常量,但其内容未知,因此编译器会尝试保留将其复制到目标的内容。另外,编译器只知道已经完成的翻译,不关心在使用点之后定义或初始化了什么。@IanAbbott我的理解是未初始化的变量在BSS中,由.section.BSS.foo1_init,“aw”、%nobits
和.section.BSS.bar1_init,“aw”、%nobits
行表示。
396 .section .bss.foo1_init,"aw",%nobits
397 .align 2
398 .set .LANCHOR0,. + 0
401 foo1_init:
402 0000 00000000 .space 8
402 00000000
40 .L2:
41 0010 00000000 .word .LANCHOR0
42 0014 00000000 .word foo1
55: **** foo1 = foo1_init;
32 .loc 1 55 0
33 0000 034A ldr r2, .L2
34 0002 044B ldr r3, .L2+4
35 0004 92E80300 ldmia r2, {r0, r1}
36 0008 83E80300 stmia r3, {r0, r1}
67 .L5:
68 000c 00000000 .word foo2
60: **** foo2 = foo2_init;
60 0000 024B ldr r3, .L5
61 0002 0022 movs r2, #0
62 0004 1A60 str r2, [r3, #0]
63 0006 5A60 str r2, [r3, #4]
389 .section .bss.bar1_init,"aw",%nobits
390 .align 2
391 .set .LANCHOR1,. + 0
394 bar1_init:
395 0000 00000000 .space 80
395 00000000
395 00000000
395 00000000
395 00000000
98 .L8:
99 0010 00000000 .word .LANCHOR1
100 0014 00000000 .word bar1
65: **** bar1 = bar1_init;
89 .loc 1 65 0
90 0002 0349 ldr r1, .L8
91 0004 0348 ldr r0, .L8+4
92 0006 5022 movs r2, #80
93 0008 FFF7FEFF bl memcpy
130 .L11:
131 0010 00000000 .word bar2
70: **** bar2 = bar2_init;
121 .loc 1 70 0
122 0002 0021 movs r1, #0
123 0004 5022 movs r2, #80
124 0006 0248 ldr r0, .L11
125 0008 FFF7FEFF bl memset
.file "defs.c"
.local foo1_init
.comm foo1_init,8,8
.section .rodata
.align 8
.type foo2_init, @object
.size foo2_init, 8
foo2_init:
.zero 8
.local bar1_init
.comm bar1_init,80,32
.align 32
.type bar2_init, @object
.size bar2_init, 80
bar2_init:
.zero 80
.text
.globl init_foo1
.type init_foo1, @function
init_foo1:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq foo1_init(%rip), %rax
movq %rax, foo1(%rip)
nop
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size init_foo1, .-init_foo1
.globl init_foo2
.type init_foo2, @function
init_foo2:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq $0, foo2(%rip)
nop
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size init_foo2, .-init_foo2
.globl init_bar1
.type init_bar1, @function
init_bar1:
.LFB2:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq bar1_init(%rip), %rax
movq %rax, bar1(%rip)
movq bar1_init+8(%rip), %rax
movq %rax, bar1+8(%rip)
movq bar1_init+16(%rip), %rax
movq %rax, bar1+16(%rip)
movq bar1_init+24(%rip), %rax
movq %rax, bar1+24(%rip)
movq bar1_init+32(%rip), %rax
movq %rax, bar1+32(%rip)
movq bar1_init+40(%rip), %rax
movq %rax, bar1+40(%rip)
movq bar1_init+48(%rip), %rax
movq %rax, bar1+48(%rip)
movq bar1_init+56(%rip), %rax
movq %rax, bar1+56(%rip)
movq bar1_init+64(%rip), %rax
movq %rax, bar1+64(%rip)
movq bar1_init+72(%rip), %rax
movq %rax, bar1+72(%rip)
nop
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2:
.size init_bar1, .-init_bar1
.globl init_bar2
.type init_bar2, @function
init_bar2:
.LFB3:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $bar2, %eax
movl $80, %ecx
movl $0, %esi
movq %rsi, (%rax)
movl %ecx, %edx
addq %rax, %rdx
addq $8, %rdx
movq %rsi, -16(%rdx)
leaq 8(%rax), %rdx
andq $-8, %rdx
subq %rdx, %rax
addl %eax, %ecx
andl $-8, %ecx
movl %ecx, %eax
shrl $3, %eax
movl %eax, %ecx
movq %rdx, %rdi
movq %rsi, %rax
rep stosq
nop
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE3:
.size init_bar2, .-init_bar2
.ident "GCC: (GNU) 6.3.1 20170306"
.section .note.GNU-stack,"",@progbits