c - GCC 不优化未初始化静态常量的结构副本

标签 c gcc optimization struct

首先,我正在为微 Controller 开发,因此 RAM 和 ROM 的使用是优先事项。

我意识到这可能被解读为错误报告或不够具体。如果我在这里没有得到任何答案,我将按原样归档。

我喜欢使用 static const 结构将堆栈结构初始化为默认值。在大多数情况下,默认结构全为零。我更喜欢使用 static const 结构而不是 memset(memset or struct assignmentstatic const assignment)

我当前的工具链是 arm-none-eabi-gcc-4_7_​​3,使用优化 -Os 编译 Cortex M4 目标。

我注意到以下几点;如果我明确地将我的 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;
}

编译后,生成以下汇编程序 list (为简洁起见重新排列和修剪):

 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_foo2bar2 = init_bar2,编译器优化了副本,直接将零存储到 foo2 或为 bar2 调用 memset

我们可以看到,对于 foo1 = init_foo1bar1 = init_bar1,编译器正在执行显式复制,加载和保存 foo1 的寄存器> 并为 foo2 调用 memcpy

我有几个问题:

  1. 这是预期的 GCC 操作吗?我希望未初始化的 static const 结构在 GCC 中遵循与初始化的 static const 结构相同的路径,因此产生相同的输出。
  2. 其他版本的 ARM GCC 是否会出现这种情况?我手头没有其他版本,所有在线 C 汇编编译器实际上都是 C++ 编译器。
  3. GCC 的其他目标架构是否会出现这种情况?同样,我手头没有其他版本。

最佳答案

我已经在 amd64 上进行了测试,令我惊讶的是它看起来像一个一致的行为(但我不知道这是否是一个错误)。 gcc 将 foo1_init 和 bar1_init 放在公共(public)数据段中,或操作系统 (.bss) 的零初始化值段。 foo2_init 和 bar2_init 被放在只读段 (.rodata) 中,就好像它们是非零初始化值一样。使用-O0 可以看到这一点。因为您没有使用操作系统,所以操作系统初始化部分由 gcc 和/或链接器手动初始化,然后被复制。 gcc 通过直接 memset 和消除死的 *2_init 变量来优化 rodata 值。不过,clang 对这两种情况都进行了同样的优化。

以下是 gcc 输出 (-O0):

    .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

关于c - GCC 不优化未初始化静态常量的结构副本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35224943/

相关文章:

c - 陷休眠眠状态(1.)

MySQL 子查询

c - 如何创建内存泄漏?

java - JNI : Can not get array length

c - 使用 GCC 编译时在定义静态数组之前声明它

javascript - Jquery - 对多个 id 使用一个函数以节省重复代码

c - 如何编写更好的 strlen 函数?

c++ - 如何知道C++中 `shm_open`使用的共享内存的可用大小?

gcc - 配置需要 C++14 支持,但 gcc -v = 4.9.3

c - Scanf 在 C 中跳过每隔一个 while 循环