Different assembly when initializing an array with a string literal

Viewed 231

According to this thread, initializing an array with a shorter string literal pads the array with zeros.

So, is there any reason why these two functions (test1 and test2) would produce different results when compiled for 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);
}

I am getting equivalent assemblies for x86-64, but on ARM gcc I get different output:

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 Answers

First of, the code is equivalent in the sense that the contents of memory constituting the objects named buf will be the same.

That said, the compiler clearly produces worse code for the second function. Consequently, since there is a way to produce more optimized code that has equivalent semantics, it would be reasonable to consider this an optimization failure in the compiler and file a bug for it.

If the compiler wanted to emit the same code, it would have to recognize that the string literal representation in memory can be zero-padded without changing the semantics of the program (though the string literal itself cannot be padded, since sizeof "test" cannot be equal to sizeof "test\0\0\0").

However, since this would only be advantageous if the string literal is used to initialize an explicit-length array (usually a bad idea) that is longer than the literal and where normal string semantics are not sufficient (bytes past the null-terminator are relevant), the value of better optimization for this case seems limited.

Addendum: If you set godbolt to not remove assembler directives, you can see how the literals are created:

.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

Interestingly, the compiler does not deduplicate, and it leaves the padding after the string literal to the assembler (.space 3), rather than explicitly zeroing it.

Related