【发布时间】:2016-05-15 11:26:57
【问题描述】:
首先,我正在为微控制器开发,因此 RAM 和 ROM 的使用是优先事项。
我意识到这可能被视为错误报告或不够具体。如果我在这里没有得到任何答案,我会照此归档。
我喜欢使用static const 结构将堆栈结构初始化为默认值。在大多数情况下,默认结构全为零。我更喜欢使用 static const 结构而不是 memset (memset or struct assignment, static const assignment)
我当前的工具链是 arm-none-eabi-gcc-4_7_3,为 Cortex M4 目标编译并优化 -Os。
我注意到以下内容;如果我明确地将我的static const 结构初始化为零,则 GCC 会产生不同的代码,而不是如果我不这样做(static const struct foo; vs static const struct foo = {0};)。特别是,它将未初始化的static const 结构分配到内存并执行复制操作。
这是一个代码示例:
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;
}
已编译,这将产生以下汇编程序列表(为简洁起见重新排列和修剪):
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
我们可以看到,对于foo2 = init_foo2 和bar2 = init_bar2,编译器已将副本优化为直接将零存储到foo2 或为bar2 调用memset。
我们可以看到,对于foo1 = init_foo1 和bar1 = init_bar1,编译器正在执行显式复制,为foo1 加载和保存寄存器,并为foo2 调用memcpy。
我有几个问题:
- 这是预期的 GCC 操作吗?我希望未初始化的
static const结构在 GCC 中遵循与初始化的static const结构相同的路径,因此产生相同的输出。 - 其他版本的 ARM GCC 是否会发生这种情况?我手头没有其他版本,所有在线的
C到汇编编译器实际上都是C++编译器。 - GCC 的其他目标体系结构是否会发生这种情况?同样,我手头没有其他版本。
【问题讨论】:
-
你能编辑你的代码以保持一致性吗?它目前指的是
foo1_init等,它们没有在你的代码中定义(它定义了init_foo1代替)。我想这只是一个错字,因为您将init_foo1作为同一范围内的变量和函数。 -
对
memcpy()的调用在空间方面非常便宜,您是否将其与内联副本的成本进行了比较?当字节数足够大时,也许有一种启发式方法会发出调用。 -
1.我怀疑这是因为未初始化的变量只是暂时定义的,编译器生成的代码并不关心变量是否完全定义。 (即,它不会检查变量是否在稍后在翻译单元中使用初始化程序完全定义。)
-
我同意@IanAbbott 的观点,如果是这种情况,编译器会正确运行,因为您将
foo2_init和bar2_init明确定义为constant并且始终==0。因此,对复制的正确优化是将目标数组归零(使用memset)。另一方面,foo1_init和bar1_init是constant,但内容未知,因此编译器尝试保留该内容并将其复制到目标。附言编译器只知道已经完成的翻译,不关心在使用点之后定义或初始化的内容。 -
@IanAbbott 我的理解是未初始化的变量在 BSS 中,由
.section .bss.foo1_init,"aw",%nobits和.section .bss.bar1_init,"aw",%nobits行表示。
标签: c gcc optimization struct