ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
https://ziglang.org
MIT License
35.09k stars 2.56k forks source link

C Backend Miscompilation #21724

Open ikskuh opened 1 month ago

ikskuh commented 1 month ago

Zig Version

0.13.0

Steps to Reproduce and Observed Behavior

Compile the following code with zig build-exe -lc -ofmt=c /tmp/magic.zig -O ReleaseFast and check out the generated C file:

pub fn read() u8 {
    return 1;
}
pub fn write(_: u8) void {}

pub export fn main() void {
    var buf: [1 << 31]u8 = undefined;

    for (0..255) |i| buf[i] = read();
    for (0..255) |i| write(buf[i]);
}

This code generates

void main(void) {
 uintptr_t t2;
 uintptr_t t1;
 uintptr_t t7;
 uint64_t t3;
 uint8_t *t5;
 bool t4;
 uint8_t t6;
 uint8_t t8[2147483648];
 uint8_t t0[2147483648];
 /* file:2:5 */
 /* var:buf */
 t1 = (uintptr_t)0ul;
 /* file:4:10 */
 for (;;) {
  t2 = t1;
  t3 = t2;
  t4 = t3 < UINT64_C(255);
  if (t4) {
   /* var:i */
   /* file:4:25 */
   t5 = (uint8_t *)&t0[t2];
   /* file:4:35 */
   t6 = magic_read__249();
   (*t5) = t6;
   goto zig_block_1;
  }
  goto zig_block_0;

  zig_block_1:;
  t2 = t2 + (uintptr_t)1ul;
  t1 = t2;
 }

 zig_block_0:;
 t7 = (uintptr_t)0ul;
 /* file:5:10 */
 for (;;) {
  t2 = t7;
  t3 = t2;
  t4 = t3 < UINT64_C(255);
  if (t4) {
   /* var:i */
   /* file:5:27 */
   memcpy(t8, (const char *)&t0, sizeof(uint8_t[2147483648]));
   /* file:5:31 */
   t6 = t8[t2];
   /* file:5:27 */
   magic_write__250(t6);
   goto zig_block_3;
  }
  goto zig_block_2;

  zig_block_3:;
  t2 = t2 + (uintptr_t)1ul;
  t7 = t2;
 }

 zig_block_2:;
 return;
}

Both GCC 14.2 and Clang emit the memcpy and are not able to optimize t8 away.

Expected Behavior

The array t8 is unnecessarily created copied before accessing it.

I don't expect Zig to emit the memcpy(t8, (const char *)&t0, sizeof(uint8_t[2147483648]));

EnronEvolved commented 1 month ago

Is this something #19466 should track?