Skip to content

Fluff UP001: stop wrap at bare scope containing nested *= - #87

Open
manz wants to merge 2 commits into
masterfrom
fix/up001-bare-scope-boundary
Open

Fluff UP001: stop wrap at bare scope containing nested *=#87
manz wants to merge 2 commits into
masterfrom
fix/up001-bare-scope-boundary

Conversation

@manz

@manz manz commented May 24, 2026

Copy link
Copy Markdown
Owner

Summary

UP001 autofix over-extended through bare { ... } scopes when
the scope contained a nested *=. The outer wrap engulfed the
scope, producing an oversized alloc that the allocator refused.

ff4 #31 friction report:

UP001 brace-tracker over-extends when a scope block ({ ... })
appears in body of a *= section. [...] Allocator then sees
section spanning $00:B335..$14:F6XX, fails: alloc size 672657 does not fit in any free chunk.

Before

*=0x00B335
    jmp.w _animation_wait_route

{
    *=0x14F656
    .dw 0x2016
}

UP001 produced:

.alloc at 0x00B335 {
    jmp.w _animation_wait_route
    {
        *=0x14F656
        .dw 0x2016
    }
}

The bare scope got dragged inside. Allocator size = $14F656 - $00B335.

After

.alloc at 0x00B335 {
    jmp.w _animation_wait_route
}
{
    *=0x14F656
    .dw 0x2016
}

Wrap closes before the bare brace. Scope keeps its independent
placement context.

Fix

_next_placement_or_end now treats CompoundAstNode (bare
{ ... }) as a wrap boundary when it contains a nested *= or
.alloc. Recurses into further bare scopes via
_contains_placement so multi-level nesting is caught too.

Bare scopes without placement directives stay non-terminating
since they don't reset the cursor.

Out of scope

UP001 doesn't recurse INTO the bare scope to flag the inner
*= (separate follow-up). The pre-flight check that refuses
the rewrite when it would produce a nested-*= alloc is also
deferred — the boundary fix now produces a correct wrap on its
own, so the pre-flight is belt-and-braces.

ff4 #31 friction: `*= ADDR / opcodes / { ... *= INNER ... }` had
the bare scope swallowed inside the new `.alloc at ADDR { ... }`
wrap. Resulting alloc spanned ADDR..INNER (672657 bytes for the
real ff4 case), allocator refused.

Add `CompoundAstNode` containing nested placement directives
(`*=` / `.alloc`) to the wrap boundary set. Bare scopes without
nested placements stay non-terminating since they don't change
the cursor.

`_contains_placement` recurses into further bare scopes to catch
multi-level nesting.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented May 24, 2026

Copy link
Copy Markdown

Deploying a816 with  Cloudflare Pages  Cloudflare Pages

Latest commit: 16beb8b
Status: ✅  Deploy successful!
Preview URL: https://fa37d022.a816.pages.dev
Branch Preview URL: https://fix-up001-bare-scope-boundar.a816.pages.dev

View logs

ff4 #32 surfaced that the previous stop-list approach was too
narrow:  followed by 30 `.import`s engulfed the
entire tail of the file because IfAstNode / ImportAstNode /
IncludeAstNode were not in the boundary set.

Switch to an allow-list. Wrap body extends only across pure
emit-style nodes (opcodes, data, text, ascii, labels, constants,
register-size, macro-apply, comments, docstrings). First node
outside that set terminates the wrap.

Separately: when the body contains `.incbin` / `.import` /
`.include`, skip the conversion entirely. These rely on
direct-mode chain semantics (cross-bank silent overflow for
incbin; cursor-drives-module-placement for import/include)
that don't translate mechanically to `.alloc at`. User
migrates by hand:
- `.incbin` blocks bigger than a bank: split per bank.
- `.import` chains: lift imports to top of file, move
  placement into the modules themselves.

Resolves the 672657-byte mega-wrap from ff4 #31 + the
chain-engulf bug from ff4 #32. Per-PR build now safe for ff4.
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant