4

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
4
  • 2
    As far as the C standard is concerned, these are equivalent. Though a compiler will be allowed to skip writing zeroes after the null terminator, if it can deduct that the memory after \0 isn't used (which it can't here, due to external linkage). So it rather seems like this is some manner of bug. If I change test2 to char buff[8] = "test\0\0\0"; (4 trailing zeroes) I get identical machine code for both cases on ARM too. Commented Dec 21, 2018 at 9:05
  • BTW: buff gets filled up with 0 if you use {'t', 'e', 's', 't', 0 }, too. I'd expect that this produces the same assembler output as the string literal version. Commented Dec 21, 2018 at 9:24
  • @StephanLechner: it does, except when compiling with arm gcc. Commented Dec 21, 2018 at 9:26
  • 1
    x86-64 is a 64-bit architecture (and zero-extends 32-bit immediates to 64-bit for free), so it's unsurprising for multiple reasons that both versions use a single qword store. If you make the array 9 bytes long, and add a 5th non-zero byte (godbolt.org/z/Xk-vLP), you get significantly different code from gcc (with some silly test1 code), but clang still makes identical good code for both. Commented Dec 21, 2018 at 18:40

1 Answer 1

3

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.