Commit Graph

11 Commits

Author SHA1 Message Date
3f58f34592 feat(llvm): Phase 132-P0 - block_end_values tuple-key fix for cross-function isolation
## Problem
`block_end_values` used block ID only as key, causing collisions when
multiple functions share the same block IDs (e.g., bb0 in both
condition_fn and main).

## Root Cause
- condition_fn's bb0 → block_end_values[0]
- main's bb0 → block_end_values[0] (OVERWRITES!)
- PHI resolution gets wrong snapshot → dominance error

## Solution (Box-First principle)
Change key from `int` to `Tuple[str, int]` (func_name, block_id):

```python
# Before
block_end_values: Dict[int, Dict[int, ir.Value]]

# After
block_end_values: Dict[Tuple[str, int], Dict[int, ir.Value]]
```

## Files Modified (Python - 6 files)

1. `llvm_builder.py` - Type annotation update
2. `function_lower.py` - Pass func_name to lower_blocks
3. `block_lower.py` - Use tuple keys for snapshot save/load
4. `resolver.py` - Add func_name parameter to resolve_incoming
5. `wiring.py` - Thread func_name through PHI wiring
6. `phi_manager.py` - Debug traces

## Files Modified (Rust - cleanup)

- Removed deprecated `loop_to_join.rs` (297 lines deleted)
- Updated pattern lowerers for cleaner exit handling
- Added lifecycle management improvements

## Verification

-  Pattern 1: VM RC: 3, LLVM Result: 3 (no regression)
- ⚠️ Case C: Still has dominance error (separate root cause)
  - Needs additional scope fixes (phi_manager, resolver caches)

## Design Principles

- **Box-First**: Each function is an isolated Box with scoped state
- **SSOT**: (func_name, block_id) uniquely identifies block snapshots
- **Fail-Fast**: No cross-function state contamination

## Known Issues (Phase 132-P1)

Other function-local state needs same treatment:
- phi_manager.predeclared
- resolver caches (i64_cache, ptr_cache, etc.)
- builder._jump_only_blocks

## Documentation

- docs/development/current/main/investigations/phase132-p0-case-c-root-cause.md
- docs/development/current/main/investigations/phase132-p0-tuple-key-implementation.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 05:36:50 +09:00
af6f95cd4b Phase 33 NORM canon test: enforce normalized dev route for P1/P2/JP mini 2025-12-11 20:54:33 +09:00
192620f842 feat(joinir): Phase 228 - CarrierInit for ConditionOnly header PHI initialization
- Add CarrierInit enum (FromHost/BoolConst) for explicit initialization policy
- LoopHeaderPhiBuilder generates Const instruction for BoolConst carriers
- merge/mod.rs uses carrier_info to include ALL carriers in header PHI
- Pattern 2/4 pass carrier_info to boundary builder
- ConditionOnly carriers (is_digit_pos) now included in header PHI

Remaining: latch incoming for ConditionOnly carriers (Phase 228-8)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-10 21:10:28 +09:00
478cc0012e feat(joinir): Phase 227 - CarrierRole separation (LoopState vs ConditionOnly)
- Add CarrierRole enum to distinguish state carriers from condition-only carriers
- ConditionOnly carriers (is_digit_pos) skip exit PHI but keep header PHI
- Update all test struct literals with role field
- 877/884 tests PASS (7 pre-existing failures unrelated)

Remaining: ValueId(0) undefined - header PHI initialization for ConditionOnly

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-10 20:07:30 +09:00
149c343ace refactor: Quick Win cleanup - 102 lines deleted, zero regressions
Completed three high-efficiency refactoring tasks:

## Task 1: Pattern1Context deletion (40 lines)
- Removed deprecated Pattern1Context struct from simple_while_minimal.rs
- Removed context parameter from lowering pipeline
- Simplified API surface (one less unused type)
- Files: simple_while_minimal.rs, pattern1_minimal.rs, loop_to_join.rs

## Task 2: #[allow(dead_code)] cleanup (62 lines)
- Deleted new_with_outputs() from inline_boundary.rs (truly dead, deprecated)
- Deleted extract_type_hint() from generic_type_resolver.rs (future placeholder)
- Removed 3 incorrect annotations (code IS actually used)
- Added clear comments for future-work items

## Task 3: Test organization (0 lines, +26 doc lines)
- Added 4-section navigation to loop_scope_shape/tests.rs
- Improved test discoverability (17 tests → organized by module)
- Low-risk organization improvement
- Easy path for future per-module test file splitting

## Summary
- Total deletion: 102 net lines
- Files modified: 9
- Build status:  Clean (0 errors, 0 warnings)
- Test status:  21/21 PASS
- Regressions: 0

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 23:59:28 +09:00
287ceca18d feat(joinir): Phase 33-16 Loop Header PHI SSOT with TailCallKind refactoring
## Problem
- joinir_min_loop.hako returned 0 instead of expected 2
- Entry block's tail call was redirected to itself (bb4 → bb4 self-loop)
- JoinIR function parameters lack SSA definition when inlined

## Solution
- Distinguish entry calls from back edges using TailCallKind enum
- Entry block (LoopEntry) stays at target, back edges redirect to header PHI
- Added explicit classification: LoopEntry, BackEdge, ExitJump

## Key Changes
- instruction_rewriter.rs: TailCallKind enum + classify_tail_call()
- Renamed is_entry_func_entry_block → is_loop_entry_point (clearer intent)
- loop_header_phi_builder.rs: New module for header PHI generation
- joinir_inline_boundary_injector.rs: Skip loop var Copy when header PHI handles it

## Verified
- joinir_min_loop.hako: returns 2 
- NYASH_SSA_UNDEF_DEBUG: no undefined errors 

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-07 12:01:54 +09:00
a09ce0cbff feat(joinir): Phase 33-14 JoinFragmentMeta for expr/carrier separation
Introduces JoinFragmentMeta to distinguish between loop expression results
and carrier variable updates, fixing SSA correctness issues.

## Changes

### New: JoinFragmentMeta struct (carrier_info.rs)
- `expr_result: Option<ValueId>` - Loop as expression (return loop(...))
- `exit_meta: ExitMeta` - Carrier updates for variable_map
- Helper methods: with_expr_result(), carrier_only(), empty()

### Pattern 2 Lowerer Updates
- loop_with_break_minimal.rs: Returns (JoinModule, JoinFragmentMeta)
- pattern2_with_break.rs: Sets boundary.expr_result from fragment_meta

### instruction_rewriter.rs
- Phase 33-14: Only add to exit_phi_inputs when boundary.expr_result is Some
- Phase 33-13: MergeResult struct with carrier_inputs map

### JoinInlineBoundary (inline_boundary.rs)
- New field: expr_result: Option<ValueId>
- All constructors updated with expr_result: None default

## Design Philosophy

Previously, exit_phi_inputs mixed expr results with carrier updates, causing:
- PHI inputs referencing undefined remapped values
- SSA-undef errors in VM execution

With JoinFragmentMeta:
- expr_result → exit_phi_inputs (generates PHI for expr value)
- exit_meta → carrier_inputs (updates variable_map via carrier PHIs)

## Test Results
- Pattern 1 (carrier-only): Works correctly (no exit_phi_inputs)
- Pattern 2 (expr result): Design complete, SSA-undef fix deferred to Phase 33-15

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-07 05:07:28 +09:00
e30116f53d feat(joinir): Phase 171-fix ConditionEnv/ConditionBinding architecture
Proper HOST↔JoinIR ValueId separation for condition variables:

- Add ConditionEnv struct (name → JoinIR-local ValueId mapping)
- Add ConditionBinding struct (HOST/JoinIR ValueId pairs)
- Modify condition_to_joinir to use ConditionEnv instead of builder.variable_map
- Update Pattern2 lowerer to build ConditionEnv and ConditionBindings
- Extend JoinInlineBoundary with condition_bindings field
- Update BoundaryInjector to inject Copy instructions for condition variables

This fixes the undefined ValueId errors where HOST ValueIds were being
used directly in JoinIR instructions. Programs now execute (RC: 0),
though loop variable exit values still need Phase 172 work.

Key invariants established:
1. JoinIR uses ONLY JoinIR-local ValueIds
2. HOST↔JoinIR bridging is ONLY through JoinInlineBoundary
3. condition_to_joinir NEVER accesses builder.variable_map

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-07 01:45:03 +09:00
caf38dba19 feat(joinir): Phase 190 - LoopExitBinding boxification
Formalize exit PHI → variable_map reconnection with explicit LoopExitBinding
structure. Eliminates hardcoded variable names and prepares for Pattern 4+
multi-carrier support.

Key changes:

1. **New LoopExitBinding struct**:
   - carrier_name: String (e.g., "sum", "count")
   - join_exit_value: ValueId (JoinIR exit value)
   - host_slot: ValueId (variable_map destination)
   Makes it explicit: WHICH variable, FROM where, TO where.

2. **Updated JoinInlineBoundary**:
   - Replaced implicit host_outputs: Vec<ValueId>
   - With explicit exit_bindings: Vec<LoopExitBinding>
   - Old APIs marked #[deprecated] for backward compatibility

3. **Pattern 3 now uses explicit bindings**:
   Before: boundary.host_outputs = vec![sum_var_id]  // implicit
   After:  boundary.exit_bindings = vec![LoopExitBinding {
       carrier_name: "sum".to_string(),
       join_exit_value: ValueId(18),
       host_slot: sum_var_id,
   }]

4. **merge_joinir_mir_blocks() updated**:
   - Consumes exit_bindings instead of bare ValueIds
   - Enhanced debug output shows carrier names
   - Validates carrier name matches variable_map expectations

Benefits:
- Self-documenting code: bindings explain themselves
- Multi-carrier ready: Pattern 4+ just extend the vec![]
- Type-safe: No implicit semantics
- Debuggable: Explicit carrier name in logs

Test status:
- Build:  SUCCESS (0 errors, 47 warnings)
- Pattern 3:  PASS (no regressions)
- Backward compatibility:  Maintained via #[deprecated]

Prepare for Phase 191: Pattern Router Table and Phase 192: JoinLoopTrace

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: ChatGPT <noreply@openai.com>
Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 19:59:40 +09:00
0397df5240 refactor(joinir): Phase 189 - Remove hardcoded 'sum' variable, generalize exit PHI connection
Key improvements:
1. **Eliminate hardcoded variable name**: Replace hardcoded "sum" with generic
   ValueId-based variable_map updates. Add new JoinInlineBoundary constructor
   `new_with_input_and_host_outputs()` for Pattern 3.

2. **Generalize output slot mapping**: Exit PHI result now updates all host_outputs
   entries in variable_map, not just hardcoded "sum". Prepares for future
   multi-carrier patterns.

3. **PHI preservation in blocks**: Fix block finalization to preserve existing
   PHI instructions (from handle_select) instead of overwriting them.

4. **Stable function name→ValueId mapping**: Ensure consistent ValueId
   assignment for tail call detection across multi-function merges.

5. **Enhanced debugging**: Add detailed logging in block converter and meta
   analysis for PHI verification.

Files modified:
- src/mir/builder/control_flow.rs: Remove hardcoded "sum", use boundary outputs
- src/mir/join_ir/lowering/inline_boundary.rs: Add new constructor
- src/mir/join_ir_vm_bridge/joinir_block_converter.rs: Stable mappings, PHI preservation
- src/mir/join_ir_vm_bridge/meta.rs: Debug output for PHI tracking
- src/mir/builder/joinir_id_remapper.rs: PHI value remapping
- src/mir/builder/joinir_inline_boundary_injector.rs: Span preservation

Test status: Pattern 3 (loop_if_phi.hako) still produces correct result (sum=9, RC=9)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: ChatGPT <noreply@openai.com>
Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 19:39:54 +09:00
d303d24b43 feat(joinir): Phase 188 JoinInlineBoundary + Pattern 1 working! 🎉
Major milestone: loop_min_while.hako outputs "0 1 2" correctly!

## JoinInlineBoundary (Option D from ChatGPT Pro design review)
- New struct for clean SSA boundary between JoinIR and host function
- JoinIR uses local ValueIds (0,1,2...) - no host ValueId dependency
- Copy injection at entry block connects host → JoinIR values

## Pattern 1 Simple While Loop
- Refactored to use pure local ValueIds
- Removed Pattern1Context dependency on host ValueIds
- Clean separation: lowerer generates, merger connects

## Key Design Principles (Box Theory)
- Box A: JoinIR Frontend (host-agnostic)
- Box B: Join→MIR Bridge (independent functions)
- Box C: JoinInlineBoundary (boundary info only)
- Box D: JoinMirInlineMerger (Copy injection)

## Files Changed
- NEW: inline_boundary.rs - JoinInlineBoundary struct
- control_flow.rs - merge with boundary, void return fix
- simple_while_minimal.rs - pure local ValueIds
- mod.rs - module export

Test: NYASH_DISABLE_PLUGINS=1 ./target/release/hakorune apps/tests/loop_min_while.hako
Output: 0\n1\n2 

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 13:46:44 +09:00