Skip to content

Miscompilation with array inside of anonymous struct #7525

@LiterallyVoid

Description

@LiterallyVoid

This code:

const std = @import("std");

pub noinline fn outer() u32 {
    var a: u32 = 42;
    return inner(.{
        .unused = a,
        .value = [1]u32{0},
    });
}

pub noinline fn inner(args: anytype) u32 {
    return args.value[0];
}

test "foo" {
    std.debug.warn("value: {}\n", .{outer()});
}

prints this:

1/1 test "foo"... value: 32767
OK
All 1 tests passed.

Relevant LLVM IR:

define internal fastcc i32 @outer() unnamed_addr #7 !dbg !2866 {
Entry:
  %result = alloca i32, align 4
  %a = alloca i32, align 4
  %0 = alloca %"struct:5:19", align 4
  store i32 42, i32* %a, align 4, !dbg !2872
  call void @llvm.dbg.declare(metadata i32* %a, metadata !2870, metadata !DIExpression()), !dbg !2872
  %1 = load i32, i32* %a, align 4, !dbg !2873
  %2 = getelementptr inbounds %"struct:5:19", %"struct:5:19"* %0, i32 0, i32 0, !dbg !2873
  store i32 %1, i32* %2, align 4, !dbg !2873
  %3 = call fastcc i32 @inner(%"struct:5:19"* %0), !dbg !2874
  store i32 %3, i32* %result, align 4, !dbg !2874
  %4 = load i32, i32* %result, align 4, !dbg !2875
  ret i32 %4, !dbg !2875
}

define internal fastcc i32 @inner(%"struct:5:19"* nonnull readonly align 4 %0) unnamed_addr #7 !dbg !3325 {
Entry:
  %result = alloca i32, align 4
  call void @llvm.dbg.declare(metadata %"struct:5:19"* %0, metadata !3337, metadata !DIExpression()), !dbg !3338
  %1 = getelementptr inbounds %"struct:5:19", %"struct:5:19"* %0, i32 0, i32 1, !dbg !3339
  %2 = getelementptr inbounds [1 x i32], [1 x i32]* %1, i64 0, i64 0, !dbg !3341
  %3 = load i32, i32* %2, align 4, !dbg !3341
  store i32 %3, i32* %result, align 4, !dbg !3341
  %4 = load i32, i32* %result, align 4, !dbg !3342
  ret i32 %4, !dbg !3342
}

Note how in @outer, the only store is into element 0 of the structure (which is unused.)

Workarounds

Put the array value into a constant.

pub noinline fn outer() u32 {
    var a: u32 = 42;
    const value = [1]u32{0};
    return inner(.{
        .unused = a,
        .value = value,
    });
}

Don't use an anonymous struct.

const S = struct {
    unused: u32,
    value: [1]u32,
};

pub noinline fn outer() u32 {
    var a: u32 = 42;
    return inner(S{
        .unused = a,
        .value = [1]u32{0},
    });
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugObserved behavior contradicts documented or intended behaviormiscompilationThe compiler reports success but produces semantically incorrect code.stage1The process of building from source via WebAssembly and the C backend.

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions