zig icon indicating copy to clipboard operation
zig copied to clipboard

Setting a global enum on WASM fails silently

Open knexator opened this issue 1 year ago • 2 comments

Zig Version

0.14.0-dev.1694+3b465ebec

Steps to Reproduce and Observed Behavior

A global variable, of type an enum with 4 fields, sometimes updates incorrectly on WASM :

src/main.zig:

const std = @import("std");

pub fn main() !void {
    buggy_fn(2);
}

var my_global_value: Direction = undefined;

export fn buggy_fn(new_value: u32) void {
    std.debug.print("input to fn was: {d}\n", .{new_value});
    my_global_value = Direction.num2dir(new_value);
    std.debug.print("global is now: {d}\n", .{Direction.dir2num(my_global_value)});
}

const Direction = enum {
    Left,
    Right,
    Up,
    Down,

    fn dir2num(d: Direction) u32 {
        return switch (d) {
            .Up => 0,
            .Down => 1,
            .Left => 2,
            .Right => 3,
        };
    }

    fn num2dir(n: u32) Direction {
        return switch (n) {
            0 => .Up,
            1 => .Down,
            2 => .Left,
            3 => .Right,
            else => unreachable,
        };
    }
};

build.zig:

const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.resolveTargetQuery(.{
        .cpu_arch = .wasm32,
        .os_tag = .wasi,
    });

    const optimize: std.builtin.OptimizeMode = .ReleaseSmall;

    const exe = b.addExecutable(.{
        .name = "main",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });

    b.installArtifact(exe);

    const run_exe = b.addRunArtifact(exe);
    const run_step = b.step("run", "Run");
    run_step.dependOn(&run_exe.step);
}

zig build run -fwasmtime outputs, on my machine:

input to fn was: 2 global is now: 0

Expected Behavior

The output should be:

input to fn was: 2 global is now: 2

The issue only happens in WASM, with optimize mode set to ReleaseSmall. It also works correctly if Direction is an enum(u3), but not if it's an enum(u2) (despite having 4 values).

knexator avatar Sep 30 '24 17:09 knexator

As a workaround, add a type to the enum:

const Direction = enum(u2) {

jedisct1 avatar Sep 30 '24 19:09 jedisct1

It fails too. Surprisingly, it works with

const Direction = enum(u3) {

knexator avatar Sep 30 '24 19:09 knexator

I think it is fixed in LLVM 20. From IR triangulation problem lies in SimplifyCFG pass, which was probably solved in https://github.com/llvm/llvm-project/commit/6f194a6dea4b4067336431e699ea3588417d4b96

pavelverigo avatar Nov 23 '24 03:11 pavelverigo

@alexrp I could not reproduce this on 0.14.0, I think llvm 20 did the job.

pavelverigo avatar May 07 '25 00:05 pavelverigo