@@ -3340,3 +3340,241 @@ test "compiler: multi named-return with mixed erased and boxed types constructs
33403340 try std .testing .expectEqual (@as (usize , 3 ), countOp (c , .call ));
33413341 try std .testing .expect (countOp (c , .validate_named_range ) >= 1 );
33423342}
3343+
3344+ // ── #204 native-lane backfill: fusion pass trigger decisions ──────────────
3345+ //
3346+ // See lang/fusion_pass.zig's module doc and dev-docs/design/vm-architecture.md
3347+ // §6: every fusion is a legality-checked rewrite of adjacent core ops into a
3348+ // VM-private fused op, run by `compile()`/`compileWithSession()` (both call
3349+ // fusion_pass.fuse() already, so every test in this file already observes
3350+ // post-fusion bytecode). These tests assert the *specific* trigger shape for
3351+ // each fused op that had zero direct native coverage before this pass, plus
3352+ // one legality-boundary case (same_slot). Had zero native coverage (#204).
3353+
3354+ test "fusion: global read + constant binop fuses to get_global_const_X" {
3355+ var rt = try setup ();
3356+ defer rt .deinit ();
3357+ try compile (& rt ,
3358+ \\g := 10
3359+ \\func geqc() bool { return g == 5 }
3360+ \\func gaddc() int { return g + 5 }
3361+ \\func gltc() bool { return g < 5 }
3362+ \\func gsubc() int { return g - 5 }
3363+ );
3364+ const c = rt .chunk_state ;
3365+ try std .testing .expectEqual (@as (usize , 1 ), countOp (c , .get_global_const_eq ));
3366+ try std .testing .expectEqual (@as (usize , 1 ), countOp (c , .get_global_const_add ));
3367+ try std .testing .expectEqual (@as (usize , 1 ), countOp (c , .get_global_const_lt ));
3368+ try std .testing .expectEqual (@as (usize , 1 ), countOp (c , .get_global_const_sub ));
3369+ }
3370+
3371+ test "fusion: add immediately before ret fuses to add_ret" {
3372+ // Two different locals summed and returned directly: neither get_local
3373+ // pairs with the other (no pairFusion rule joins two get_locals), so the
3374+ // only fusable pair left is the trailing add+ret.
3375+ var rt = try setup ();
3376+ defer rt .deinit ();
3377+ try compile (& rt ,
3378+ \\func addTwo(a int, b int) int { return a + b }
3379+ );
3380+ const c = rt .chunk_state ;
3381+ try std .testing .expectEqual (@as (usize , 1 ), countOp (c , .add_ret ));
3382+ try std .testing .expectEqual (@as (usize , 0 ), countOp (c , .add ));
3383+ }
3384+
3385+ test "fusion: local struct field read fuses to get_local_get_field" {
3386+ var rt = try setup ();
3387+ defer rt .deinit ();
3388+ try compile (& rt ,
3389+ \\type Point struct { x int, y int }
3390+ \\func fieldRead(p Point) int { return p.x }
3391+ );
3392+ const c = rt .chunk_state ;
3393+ try std .testing .expectEqual (@as (usize , 1 ), countOp (c , .get_local_get_field ));
3394+ }
3395+
3396+ test "fusion: bare local return fuses to get_local_ret" {
3397+ var rt = try setup ();
3398+ defer rt .deinit ();
3399+ try compile (& rt ,
3400+ \\func identity(x int) int { return x }
3401+ );
3402+ const c = rt .chunk_state ;
3403+ try std .testing .expectEqual (@as (usize , 1 ), countOp (c , .get_local_ret ));
3404+ }
3405+
3406+ test "fusion: while-loop condition on a global with a constant fuses to get_global_const_lt_jif_pop" {
3407+ var rt = try setup ();
3408+ defer rt .deinit ();
3409+ try compile (& rt ,
3410+ \\g := 10
3411+ \\func gforlt() int {
3412+ \\ i := 0
3413+ \\ for g < 100 {
3414+ \\ i = i + 1
3415+ \\ }
3416+ \\ return i
3417+ \\}
3418+ );
3419+ const c = rt .chunk_state ;
3420+ try std .testing .expectEqual (@as (usize , 1 ), countOp (c , .get_global_const_lt_jif_pop ));
3421+ }
3422+
3423+ test "fusion: while-loop condition on a local with > and a constant fuses to get_local_const_gt_jif_pop" {
3424+ var rt = try setup ();
3425+ defer rt .deinit ();
3426+ try compile (& rt ,
3427+ \\func countdown() int {
3428+ \\ i := 5
3429+ \\ for i > 0 {
3430+ \\ i = i - 1
3431+ \\ }
3432+ \\ return i
3433+ \\}
3434+ );
3435+ const c = rt .chunk_state ;
3436+ try std .testing .expectEqual (@as (usize , 1 ), countOp (c , .get_local_const_gt_jif_pop ));
3437+ }
3438+
3439+ test "fusion: C-style for-loop header (local < constant) fuses to the quint get_local_const_lt_jif_pop_jump" {
3440+ // The jump-to-body-first layout a C-for uses (skip the post-statement on
3441+ // the loop's first iteration) is the one shape that places an
3442+ // unconditional jump directly after the get_local_const_lt_jif_pop quad,
3443+ // letting it grow into the 13-byte quint.
3444+ var rt = try setup ();
3445+ defer rt .deinit ();
3446+ try compile (& rt ,
3447+ \\func cfor() int {
3448+ \\ x := 0
3449+ \\ for i := 0; i < 5; i++ {
3450+ \\ x = x + i
3451+ \\ }
3452+ \\ return x
3453+ \\}
3454+ );
3455+ const c = rt .chunk_state ;
3456+ try std .testing .expectEqual (@as (usize , 1 ), countOp (c , .get_local_const_lt_jif_pop_jump ));
3457+ }
3458+
3459+ test "fusion: C-style for-loop's per-iteration capture close fuses to close_upvalue_loop" {
3460+ // Every iteration of a C-for rebinds the loop variable (for capture
3461+ // correctness), closing it right before the loop's back-edge — this is
3462+ // what supplies close_upvalue_loop's trigger, independent of whether the
3463+ // loop body actually captures the variable in a closure.
3464+ var rt = try setup ();
3465+ defer rt .deinit ();
3466+ try compile (& rt ,
3467+ \\func cfor() int {
3468+ \\ x := 0
3469+ \\ for i := 0; i < 5; i++ {
3470+ \\ x = x + i
3471+ \\ }
3472+ \\ return x
3473+ \\}
3474+ );
3475+ const c = rt .chunk_state ;
3476+ try std .testing .expectEqual (@as (usize , 1 ), countOp (c , .close_upvalue_loop ));
3477+ }
3478+
3479+ test "fusion: top-level global compound-add fuses to inc_global_const" {
3480+ var rt = try setup ();
3481+ defer rt .deinit ();
3482+ try compile (& rt ,
3483+ \\g := 10
3484+ \\func incGlobal() { g += 5 }
3485+ );
3486+ const c = rt .chunk_state ;
3487+ try std .testing .expectEqual (@as (usize , 1 ), countOp (c , .inc_global_const ));
3488+ }
3489+
3490+ test "fusion: loop-body compound-add on a local fuses to local_add_const, and to local_add_const_loop when it directly precedes the back-edge" {
3491+ var rt = try setup ();
3492+ defer rt .deinit ();
3493+ try compile (& rt ,
3494+ \\func loopLocalAdd(n int) int {
3495+ \\ x := 0
3496+ \\ i := 0
3497+ \\ for i < n {
3498+ \\ x += 3
3499+ \\ i = i + 1
3500+ \\ }
3501+ \\ return x
3502+ \\}
3503+ );
3504+ const c = rt .chunk_state ;
3505+ // x += 3 isn't the loop's last statement (i = i + 1 follows it), so it
3506+ // fuses only as far as local_add_const; the trailing i = i + 1 IS last,
3507+ // so it grows the extra step to local_add_const_loop.
3508+ try std .testing .expectEqual (@as (usize , 1 ), countOp (c , .local_add_const ));
3509+ try std .testing .expectEqual (@as (usize , 1 ), countOp (c , .local_add_const_loop ));
3510+ }
3511+
3512+ test "fusion: global assignment as a loop's last statement fuses to set_global_loop" {
3513+ var rt = try setup ();
3514+ defer rt .deinit ();
3515+ try compile (& rt ,
3516+ \\g := 0
3517+ \\func loopGlobalAssign(n int) int {
3518+ \\ i := 0
3519+ \\ for i < n {
3520+ \\ i = i + 1
3521+ \\ g = i
3522+ \\ }
3523+ \\ return g
3524+ \\}
3525+ );
3526+ const c = rt .chunk_state ;
3527+ try std .testing .expectEqual (@as (usize , 1 ), countOp (c , .set_global_loop ));
3528+ }
3529+
3530+ test "fusion: local = local + local fuses to the 4-window local_add_local" {
3531+ var rt = try setup ();
3532+ defer rt .deinit ();
3533+ try compile (& rt ,
3534+ \\func addLocal() int {
3535+ \\ a := 1
3536+ \\ b := 2
3537+ \\ a += b
3538+ \\ return a
3539+ \\}
3540+ );
3541+ const c = rt .chunk_state ;
3542+ try std .testing .expectEqual (@as (usize , 1 ), countOp (c , .local_add_local ));
3543+ }
3544+
3545+ test "fusion: local += struct field fuses to the 4-window local_add_field" {
3546+ var rt = try setup ();
3547+ defer rt .deinit ();
3548+ try compile (& rt ,
3549+ \\type Point struct { x int, y int }
3550+ \\func addField() int {
3551+ \\ x := 1
3552+ \\ p := Point{x: 1, y: 2}
3553+ \\ x += p.y
3554+ \\ return x
3555+ \\}
3556+ );
3557+ const c = rt .chunk_state ;
3558+ try std .testing .expectEqual (@as (usize , 1 ), countOp (c , .local_add_field ));
3559+ }
3560+
3561+ test "fusion: get_local_const_add does not fuse into local_add_const across mismatched slots" {
3562+ // Legality boundary: local_add_const additionally requires the read slot
3563+ // and the write slot to match (same_slot in pairFusionFull) — a plain
3564+ // reassignment into a *different* local must leave get_local_const_add
3565+ // and set_local as two separate instructions, not one fused op.
3566+ var rt = try setup ();
3567+ defer rt .deinit ();
3568+ try compile (& rt ,
3569+ \\func f() int {
3570+ \\ x := 10
3571+ \\ y := 0
3572+ \\ y = x + 5
3573+ \\ return y
3574+ \\}
3575+ );
3576+ const c = rt .chunk_state ;
3577+ try std .testing .expectEqual (@as (usize , 1 ), countOp (c , .get_local_const_add ));
3578+ try std .testing .expectEqual (@as (usize , 0 ), countOp (c , .local_add_const ));
3579+ try std .testing .expect (try hasOp (c , .set_local ));
3580+ }
0 commit comments