Другая сборка при инициализации массива строковым литералом - PullRequest
0 голосов
/ 21 декабря 2018

Согласно этому потоку , инициализация массива с более короткой литеральной строкой дополняет массив нулями.

Итак, есть ли причина, почему эти две функции (test1 и test2) будет давать разные результаты при компиляции для ARM Cortex M4?

extern void write(char * buff);

void test1(void)
{
    char buff[8] = {'t', 'e', 's', 't', 0, 0, 0, 0 };
    write(buff);
}

void test2(void)
{
    char buff[8] = "test";
    write(buff);
}

Я получаю эквивалентные сборки для x86-64 , но на ARM GCC я получаю другой вывод:

test1:
        str     lr, [sp, #-4]!
        sub     sp, sp, #12
        mov     r3, sp
        ldr     r2, .L4
        ldm     r2, {r0, r1}
        stm     r3, {r0, r1}
        mov     r0, r3
        bl      write
        add     sp, sp, #12
        ldr     pc, [sp], #4
.L4:
        .word   .LANCHOR0
test2:
        mov     r3, #0
        str     lr, [sp, #-4]!
        ldr     r2, .L8
        sub     sp, sp, #12
        ldm     r2, {r0, r1}
        str     r0, [sp]
        mov     r0, sp
        strb    r1, [sp, #4]
        strb    r3, [sp, #5]
        strb    r3, [sp, #6]
        strb    r3, [sp, #7]
        bl      write
        add     sp, sp, #12
        ldr     pc, [sp], #4
.L8:
        .word   .LANCHOR0+8

1 Ответ

0 голосов
/ 21 декабря 2018

Во-первых, код эквивалентен в том смысле, что содержимое памяти, составляющей объекты с именем buf, будет одинаковым.

При этом компилятор явно генерирует худший код для второй функции.Следовательно, поскольку существует способ создания более оптимизированного кода с эквивалентной семантикой, было бы разумно считать это ошибкой оптимизации в компиляторе и выдавать за нее ошибку.

Если компилятор хотел выдатьтот же код, он должен был бы признать, что представление строкового литерала в памяти может быть дополнено нулями без изменения семантики программы (хотя сам строковый литерал не может быть дополнен, так как sizeof "test" не может быть равен sizeof "test\0\0\0").

Однако, поскольку это только будет выгодно, если строковый литерал используется для инициализации массива с явной длиной (обычно это плохая идея), который длиннее литерала и где нормальная семантика строкинедостаточно (байты после нулевого терминатора релевантны), значение лучшей оптимизации для этого случая кажется ограниченным.

Добавление: если вы установили godbolt, чтобы не удалять директивы ассемблера, вы можете увидеть, как литералысоздано:

.section        .rodata
.align  2
.set    .LANCHOR0,. + 0
.byte   116
.byte   101
.byte   115
.byte   116
.byte   0
.byte   0
.byte   0
.byte   0
.ascii  "test\000"
.space  3

Интересно,компилятор не выполняет дедупликацию и оставляет отступ после строкового литерала ассемблеру (.space 3), а не явно обнуляет его.

...