Skip to content

Global write optimized-out even though side effect is possible #20452

@cdemirer

Description

@cdemirer

Zig Version

0.12.0 (also newer versions)

Steps to Reproduce and Observed Behavior

I'm not 100% confident with atomics and memory orderings, but this might be a bug.

const std = @import("std");

var FLAG = std.atomic.Value(bool).init(false);
var SHARED_VALUE: usize = 0;

pub fn worker_fn() void {
    while (true) {
        if (!FLAG.load(.seq_cst)) continue;
        std.debug.print("Shared value is: {}\n", .{SHARED_VALUE});
        FLAG.store(false, .seq_cst);
    }
}

pub fn main() !void {
    _ = try std.Thread.spawn(.{}, worker_fn, .{});

    SHARED_VALUE = 1;
    magic();
    SHARED_VALUE = 2;
    magic();
}

pub fn magic() void {
    FLAG.store(true, .seq_cst);
    while (FLAG.load(.seq_cst)) {}
}

When compiled with -O ReleaseSmall, it outputs:

Shared value is: 0
Shared value is: 2

If the magic() function is inlined (either manually or using inline), the problem disappears.


I also have this modified version that triggers this behavior in -O ReleaseFast as well:

Click to expand
const std = @import("std");

const MAGIC_NUM_1 = 6; // Try 5 :)
const MAGIC_NUM_2 = 7; // Try 6 :)

var FLAG = std.atomic.Value(bool).init(false);
var SHARED_VALUE: usize = 0;

pub fn worker_fn() void {
    while (true) {
        if (!FLAG.load(.seq_cst)) continue;
        std.debug.print("Shared value is: {}\n", .{SHARED_VALUE});
        FLAG.store(false, .seq_cst);
    }
}

pub fn main() !void {
    _ = try std.Thread.spawn(.{}, worker_fn, .{});

    SHARED_VALUE = 1; // Optimized-out in ReleaseFast, if the magic values are just right.
    magic();
    SHARED_VALUE = 2;
    magic();
}

pub fn magic() void {
    for (0..MAGIC_NUM_1) |_| {
        if (!FLAG.load(.seq_cst)) {
            FLAG.store(true, .seq_cst);
        }
    }

    for (0..MAGIC_NUM_2) |_| {
        while (FLAG.load(.seq_cst)) {}
    }
}

Output:

Shared value is: 0
Shared value is: 2

Expected Behavior

Maybe

Shared value is: 1
Shared value is: 2

but I'm not sure.

Metadata

Metadata

Assignees

No one assigned

    Labels

    backend-llvmThe LLVM backend outputs an LLVM IR Module.bugObserved behavior contradicts documented or intended behaviormiscompilationThe compiler reports success but produces semantically incorrect code.upstreamAn issue with a third party project that Zig uses.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions