2016-02-05 6 views
7

Po pierwsze pracuję nad mikrokontrolerem, więc użycie pamięci RAM i ROM jest priorytetem.GCC nie optymalizuje strukturalnej kopii niezainicjowanej statycznej konstulacji

Zdaję sobie sprawę, że może to być traktowane jako raport o błędzie lub nie wystarczająco szczegółowy. Jeśli nie otrzymam tutaj żadnych odpowiedzi, zagram to jako taki.

Uwielbiam używać struktur static const do inicjowania struktur stosów do wartości domyślnych. W większości przypadków domyślną strukturą są wszystkie zera. Wolę to zrobić z static const elemencie raczej niż memset (memset or struct assignment, static const assignment)

Mój obecny toolchain jest arm-none-eabi-gcc-4_7_3, kompilacja na cel Cortex M4 z optymalizacją -Os.

Zauważyłem następujące; GCC generuje inny kod, jeśli jawnie zainicjuję moją strukturę static const na zero, niż gdybym tego nie zrobił (static const struct foo; vs static const struct foo = {0};). W szczególności przydziela niezainicjowane struktury static const do pamięci i wykonuje operacje kopiowania.

Oto przykładowy kod:

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; 
} 

Zestawione ta produkuje następujące asembler nieruchomosci (przegrupowanego i przycięte na zwięzłość):

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 

Widzimy, że dla foo2 = init_foo2 i bar2 = init_bar2 kompilator zoptymalizował kopie do zapisywania zer do foo2 bezpośrednio lub wywołując memset dla .

Widzimy, że dla foo1 = init_foo1 i bar1 = init_bar1 kompilator wykonuje wyraźne kopie, ładuje się i oszczędność z rejestrów dla foo1 i nazywając memcpy dla foo2.

mam kilka pytań:

  1. Czy to spodziewane działanie GCC? Spodziewam się, że niezainicjowane struktury będą podążały tą samą ścieżką wewnątrz GCC, co zainicjowane struktury static const, a więc wytwarzają to samo wyjście.
  2. Czy dzieje się to w przypadku innych wersji ARM GCC? Nie mam innych wersji pod ręką, a wszystkie online C do kompilatorów zespołów są w rzeczywistości kompilatorami C++.
  3. Czy dzieje się to w przypadku innych docelowych architektur GCC? Ponownie nie mam do dyspozycji innych wersji.
+0

można edytować kod dla spójności? Obecnie odnosi się do 'foo1_init' itd., Które nie są zdefiniowane w twoim kodzie (zamiast tego definiuje' init_foo1'). Przypuszczam, że to tylko literówka, ponieważ masz 'init_foo1' jako zmienną i funkcję w tym samym zasięgu. –

+0

Wywołanie 'memcpy()' jest dość tanie pod względem przestrzeni, czy porównałeś to z kosztem wstawiania kopii? Być może istnieje heurystyka, która emituje połączenie, gdy liczba bajtów jest wystarczająco duża. – unwind

+0

@Ian Rzeczywiście literówka. Pierwotnie miałem jedną funkcję o nazwie coś innego, ale to sprawiło, że wynik montażu był trudny do zrozumienia. –

Odpowiedz

-1

Testowałem na amd64 i ku mojemu zdziwieniu wygląda to na spójne zachowanie (ale nie wiem, czy to błąd). gcc umieszcza foo1_init i bar1_init we wspólnym segmencie danych lub w segmencie zerowych wartości inicjowanych przez system operacyjny (.bss). foo2_init i bar2_init są umieszczane w segmencie tylko do odczytu (.rodata) tak, jakby były niezerowymi wartościami inicjalizowanymi. Można to zobaczyć za pomocą -O0. Ponieważ nie używasz systemu operacyjnego, zainicjowana sekcja systemu operacyjnego jest ręcznie inicjowana przez gcc i/lub linker, a następnie kopiowana. gcc optymalizuje wartości rodata, tworząc bezpośredni zbiór i eliminując martwe zmienne * 2_init. clang optymalizuje oba przypadki jednakowo.

Tu następuje wyjście 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 
Powiązane problemy