Files
hakorune/docs/development/current/main/phases/phase-129/README.md
nyash-codex 083be99214 test(joinir): Phase 129 P2 - add post-if return var fixture + VM smoke
- Add phase129_if_only_post_if_return_var_min.hako
  - Pattern: x=1; if flag==1 { x=2 }; print(x)
  - Tests join_k continuation env merge

- Add phase129_if_only_post_if_return_var_vm.sh
  - Expected output: 2 (x updated in then branch)
  - Dev-only: NYASH_JOINIR_DEV=1 HAKO_JOINIR_STRICT=1

Note: Currently passes via fallback path (non-Normalized)
P1 implementation (join_k materialization) is next step
2025-12-18 07:18:00 +09:00

5.5 KiB

Phase 129: Materialize join_k Continuation + LLVM Parity

Goal

  • Materialize join_k as a real JoinFunction (not just a concept)
  • Achieve VM+LLVM EXE parity for Phase 128 if-only partial assign pattern
  • Verify structural properties: join_k exists, PHI禁止 (no PHI in Normalized)

Background

Phase 128 added basic Assign(int literal) support to Normalized builder, but:

  • join_k was mentioned in comments but not actually materialized
  • The If lowering doesn't generate a join continuation
  • post-if statements (e.g., return x after if) aren't supported in Normalized path

Phase 129 fixes this by actually generating join_k as a JoinFunction.

Design

join_k Continuation Pattern

if cond {
  x = 2  // then: env_then = env with x=2
} else {
  // else: env_else = env (unchanged)
}
print(x)  // post-if: needs env from both branches

Normalized Lowering:

then:
  x = Const(2)
  env_then[x] = new_vid
  TailCall(join_k, env_then)

else:
  // no assignment
  env_else = env (original)
  TailCall(join_k, env_else)

join_k(env_phi):
  // env_phi is the merged environment
  // post-if statements use env_phi
  print(env_phi[x])
  Ret

Key Properties (SSOT)

  1. join_k is a JoinFunction: Not a concept, but actual function in JoinModule
  2. then/else end with TailCall(join_k): Not Ret, not direct post-if
  3. env merging is explicit: env_phi parameter receives merged environment
  4. PHI禁止: No PHI instructions in Normalized (env update + continuation only)

Structural Verification

verify_normalized_structure() enforces:

  • If node exists → join_k exists (as JoinFunction)
  • then/else don't end with Ret (they tail-call join_k)
  • join_k has env parameter (for merged environment)
  • No PHI instructions (structural check)

Scope

In-Scope (Phase 129)

  • If-only patterns (no loops)
  • Assign(int literal) RHS only (Phase 128 baseline)
  • post-if statements: print/return (minimal set)
  • VM + LLVM EXE parity (both must work)

Out-of-Scope

  • Loop patterns (Phase 130+)
  • Assign(complex expr) RHS (Phase 130+)
  • Nested if (Phase 130+)

Implementation Plan

P0: LLVM EXE smoke for Phase 128

  • Add phase128_if_only_partial_assign_normalized_llvm_exe.sh
  • Verify VM+LLVM parity for Phase 128 baseline
  • Regression: phase103, phase118

Status: DONE (commit e7ad3d31b)

P1: Materialize join_k Continuation

Target: src/mir/control_tree/normalized_shadow/builder.rs

Changes:

  1. lower_if_node: Generate join_k function

    • Create JoinFunction with env parameter
    • then: TailCall(join_k, env_then)
    • else: TailCall(join_k, env_else)
    • join_k body: process post-if statements
  2. verify_normalized_structure: Add join_k checks

    • If exists → join_k exists (by name or structure)
    • then/else end with TailCall (not Ret)
    • join_k has params (env fields)
    • No PHI instructions anywhere

Acceptance:

  • cargo test --lib PASS
  • Phase 128 VM smoke PASS
  • Phase 129 LLVM EXE smoke PASS (new fixture)

P2: Post-If Return Var Fixture

New fixture: apps/tests/phase129_if_only_post_if_return_var_min.hako

x=1; flag=1; if flag==1 { x=2 }; print(x); return "OK"

Expected: 2 (x updated in then branch)

VM smoke: phase129_if_only_post_if_return_var_vm.sh

  • NYASH_JOINIR_DEV=1 HAKO_JOINIR_STRICT=1
  • Verify join_k continuation works

Acceptance:

  • VM smoke PASS
  • join_k actually used (not fallback path)

P3: Documentation

  • This README (DONE)
  • Update 10-Now.md / 01-JoinIR-Selfhost-INDEX.md / 30-Backlog.md

Acceptance Criteria

  • P0: Phase 128 LLVM EXE smoke passes
  • P1: join_k materialized in builder.rs
  • P1: verify_normalized_structure enforces join_k properties
  • P2: Phase 129 fixture + VM smoke passes
  • P3: Documentation updated
  • Regression: phase103, phase118, phase128 all PASS
  • cargo test --lib PASS

Verification Commands

# Unit tests
cargo test --lib

# Smoke tests
bash tools/smokes/v2/profiles/integration/apps/phase128_if_only_partial_assign_normalized_vm.sh
bash tools/smokes/v2/profiles/integration/apps/phase128_if_only_partial_assign_normalized_llvm_exe.sh
bash tools/smokes/v2/profiles/integration/apps/phase129_if_only_post_if_return_var_vm.sh

# Regression
bash tools/smokes/v2/profiles/integration/apps/phase103_if_only_llvm_exe.sh
bash tools/smokes/v2/profiles/integration/apps/phase118_loop_nested_if_merge_vm.sh

Feedback Points

Box-First Modularization

  • lower_if_node: Currently ~70 lines, could be split into:
    • generate_join_k_function (create JoinFunction)
    • lower_if_branches (then/else tail calls)
    • Single responsibility principle

Fail-Fast Opportunities

  • verify_normalized_structure should fail early if:
    • If exists but no join_k found
    • then/else don't tail-call join_k
    • join_k has no env parameter

Legacy Findings

  • Current verify_branch_is_return_literal: Too restrictive for Phase 129
    • Should allow Assign statements in branches (Phase 128 already supports)
    • Should verify tail-call to join_k (not just Return)
  • Phase 128: If-only partial assign (baseline)
  • Phase 113: If-only partial assign parity (StepTree path)
  • Phase 121-127: StepTree→Normalized foundation
  • Phase 130+: Loop patterns, complex RHS

Notes

  • Dev-only: joinir_dev_enabled() + HAKO_JOINIR_STRICT=1 required
  • PHI禁止: Normalized path doesn't use PHI (env update + continuation instead)
  • join_k naming: Could use join_k, k_join, or auto-generate unique ID