Stream: beginners

Topic: Extra copies in generated code for a equivalent rewrite


view this post on Zulip zhen (Nov 03 2026 at 05:25):

Hi there, I am working on an emulator rn, and trying to optimize the backend for better frame rate.

In this process, I nuked one intermediate var and did a direct return. However, somehow this worsen the performance(in terms of frame rate).
I (and a LLM) observed that this should-have-been-equivalent tiny changes resulted extra on-stack allocation in the generated binary.

image.png

The compiler I used is the latest nightly released on Nov.2nd, and the platform is X86-64 Linux, with flag --opt=speed --debug, nothing special.

I have a snippet below, it was written by an LLM based on my codebase as a concise PoC.
Am I doing smth wrong here?

app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.26.0/GddgvsGjiNXrHFW6au5i5dUDbTs8FinQdrgE2Gqmw53t.tar.zst" }
import pf.OsStr
import pf.Stdout

# Pad is 800 inline bytes; Bus is 1016 bytes on x86-64 (nine List descriptors).
Quad : { a : U64, b : U64, c : U64, d : U64 }
Block : { a : Quad, b : Quad, c : Quad, d : Quad }
Regs : { a : Block, b : Block, c : Block, d : Block }
Pad : { a : Regs, b : Block, c : Block, d : Quad }
Bus := { bytes : List(U8), l1 : List(U8), l2 : List(U8), l3 : List(U8), l4 : List(U8), l5 : List(U8), l6 : List(U8), l7 : List(U8), l8 : List(U8), regs : Pad }
Machine :: { bus : Bus, now : U64, retained : List(U8) }
ReadResult(a) : { val : a, state : [Unchanged, Updated(Box(Bus))] }

advance : Machine, U64 -> Try(Machine, [Backwards, OutOfBounds])
advance = |m, target| {
    if target < m.now { Err(Backwards) }
    else if target - m.now > 2 {
        changed = m.bus.bytes.set(0, m.bus.bytes.get(0)?.plus_wrap(1))?
        advance({ ..m, now: m.now + 2, bus: { ..m.bus, bytes: changed } }, target)
    } else { Ok({ ..m, now: target }) }
}

read16 : Bus, U32 -> Try(ReadResult(U16), [OutOfBounds, ReadError])
read16 = |bus, addr| {
    # Keep a non-inline read shape. The exercised addresses are 0..20.
    if addr >= 32 {
        changed = bus.bytes.set(0, bus.bytes.get(0)?.plus_wrap(1))?
        return read16({ ..bus, bytes: changed }, addr - 32)
    }
    val = bus.bytes.get(addr.to_u64())?
    match addr.mod_by(4) {
        0 => Ok({ val: val.to_u16(), state: Unchanged })
        1 => Ok({ val: val.to_u16(), state: Updated(Box.box({ ..bus, bytes: bus.bytes.set(addr.to_u64(), val.plus_wrap(1))? })) })
        2 => Ok({ val: val.to_u16(), state: Unchanged })
        _ => Err(ReadError)
    }
}
read32 : Bus, U32 -> Try(ReadResult(U32), [OutOfBounds, ReadError])
read32 = |bus, addr| {
    first = read16(bus, addr)?
    Ok({ val: first.val.to_u32(), state: first.state })
}

candidate : Machine, [W16, W32], U32 -> Try((Machine, U32), _)
candidate = |m, width, addr| {
    advanced = advance(m, m.now.plus_wrap(addr.to_u64()))?
    intermediate = match width {
        W16 => {
            inner = read16(advanced.bus, addr)?
            { val: inner.val.to_u32(), state: inner.state }
        }
        W32 => read32(advanced.bus, addr)?
    }
# substitute here
    new_bus = match intermediate.state {
        Unchanged => advanced.bus
        Updated(x) => Box.unbox(x)
    }
    Ok(({ ..advanced, bus: new_bus }, intermediate.val))
# match intermediate.state {
#      Unchanged => Ok((advanced, intermediate.val))
#       Updated(x) => Ok(({ ..advanced, bus: Box.unbox(x) }, intermediate.val))
#    }
}

view = |m| {
    bus: { bytes: m.bus.bytes, l1: m.bus.l1, l2: m.bus.l2, l3: m.bus.l3, l4: m.bus.l4,
        l5: m.bus.l5, l6: m.bus.l6, l7: m.bus.l7, l8: m.bus.l8, regs: m.bus.regs },
    now: m.now, retained: m.retained,
}
result_view = |r| match r { Ok((m, val)) => Ok((view(m), val)) Err(e) => Err(e) }

exercise! : Machine, U32 => Try({}, _)
exercise! = |m, addr| {
    if addr == 20 { return Ok({}) }
    width = if addr.mod_by(2) == 0 W16 else W32
    a = candidate(m, width, addr)
    b = candidate(m, width, addr + 1)
    Stdout.line!(Str.inspect((result_view(a), result_view(b))))?
    exercise!(m, addr + 1)
}

main! : List(OsStr) => Try({}, _)
main! = |args| {
    seed = U64.from_str(OsStr.to_str_try(args.get(0)?)?)?
    q = { a: seed, b: seed.plus_wrap(1), c: seed.plus_wrap(2), d: seed.plus_wrap(3) }
    block = { a: q, b: q, c: q, d: q }
    regs = { a: { a: block, b: block, c: block, d: block }, b: block, c: block, d: q }
    bytes = List.repeat(seed.to_u8_wrap(), 16)
    m = advance({ bus: { bytes, l1: bytes, l2: bytes, l3: bytes, l4: bytes, l5: bytes, l6: bytes, l7: bytes, l8: bytes, regs }, now: seed, retained: bytes }, seed)?
    exercise!(m, 0)?
    for saved in [m.bus.bytes, m.bus.l1, m.bus.l2, m.bus.l3, m.bus.l4, m.bus.l5, m.bus.l6, m.bus.l7, m.bus.l8, m.retained] {
        if saved.len() != 16 { return Err(SnapshotChanged) }
        for byte in saved { if byte != seed.to_u8_wrap() { return Err(SnapshotChanged) } }
    }
    if m.now != seed or m.bus.regs != regs { return Err(SnapshotChanged) }
    Stdout.line!(Str.inspect(view(m)))?
    Ok({})
}

view this post on Zulip Luke Boswell (Nov 03 2026 at 08:21):

Looks like a bug -- I had Opus 5.5 dig in to find the root cause and made this issue
https://github.com/roc-lang/roc/issues/12819

view this post on Zulip Notification Bot (Nov 04 2026 at 07:37):

3 messages were moved from this topic to #beginners > HTML templating by Anton.


Last updated: Nov 04 2026 at 18:54 UTC