ldc-developers / ldc

The LLVM-based D Compiler.
http://wiki.dlang.org/LDC
Other
1.18k stars 255 forks source link

In optimized WASM build, recursive function mutates an immutable variable #4404

Open andy-hanson opened 1 year ago

andy-hanson commented 1 year ago

a.d:

immutable struct S {
    immutable int x;
}

void countTo2(immutable S a) {
    if (a.x < 2)
        countTo2(S(a.x + 1));
}

extern(C) void _start() {}

extern(C) int test() {
    immutable S a = S(0);
    immutable S b = a;
    countTo2(a);
    return a.x - b.x;
}

test.js:

const fs = require("fs")

const main = async () => {
    const debug = await WebAssembly.instantiate(fs.readFileSync("./a-debug.wasm"))
    console.log("DEBUG:", debug.instance.exports.test())

    const opt = await WebAssembly.instantiate(fs.readFileSync("./a-O2.wasm"))
    console.log("OPTIMIZED:", opt.instance.exports.test())
}

main().catch(e => { console.error("ERROR", e) })

Now run:

ldc2 -ofa-debug.wasm -w -betterC -mtriple=wasm32-unknown-unknown-wasm a.d 
ldc2 -of a-O2.wasm -w -betterC -mtriple=wasm32-unknown-unknown-wasm -O2 a.d
node test.js

The output is:

DEBUG: 0
OPTIMIZED: 2

a and b are immutable and b = a, so the difference should be 0. But in an optimized WASM build, apparently a changes after countTo2(a). (And it was passed by value too.) Tested with LDC 1.33.0-git-aa64fad.

kinke commented 11 months ago

I can't see any obvious bug in our unoptimized IR. I suspect a wasm-specific LLVM issue wrt. the byval IR attribute; the optimizer 'optimizes' the function to a return 2.