Files
hakorune/docs/development/current/main/phase171-completion-report.md
nyash-codex 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

8.8 KiB

Phase 171: JoinIR → MIR ValueId Boundary Mapping Fix - Completion Report

Date: 2025-12-07 Status: Infrastructure Complete (Testing in Progress)


Executive Summary

Successfully implemented the infrastructure to map condition-only input variables across the JoinIR → MIR boundary. This fixes the root cause of "undefined ValueId" errors for variables like start, end, len that appear only in loop conditions.

Problem Solved: Variables used only in loop conditions (not loop parameters) were using HOST ValueIds directly in JoinIR, causing undefined value errors when merged into MIR.

Solution Implemented: Extended JoinInlineBoundary with condition_inputs field and updated the entire merge pipeline to collect, remap, and inject Copy instructions for these variables.


Implementation Phases Completed

Phase 171-1: Boundary Coverage Analysis

Deliverable: phase171-1-boundary-analysis.md (documented current state)

Key Findings:

  • Loop variables (i): Properly mapped via join_inputs
  • Carriers (sum, count): Properly mapped via exit_bindings
  • Condition inputs (start, end): NOT MAPPED → ROOT CAUSE

Phase 171-2: Design Decision

Deliverable: phase171-2-condition-inputs-design.md

Decision: Option A - Extend JoinInlineBoundary

Added field:

pub condition_inputs: Vec<(String, ValueId)>

Rationale:

  1. Minimal invasiveness
  2. Clear semantics
  3. Reuses existing Copy injection mechanism
  4. Future-proof design

Phase 171-3: Infrastructure Implementation

Deliverable: phase171-3-implementation-report.md

Files Modified:

  1. inline_boundary.rs (+93 lines)

    • Added condition_inputs field
    • Added 2 new constructors
    • Updated 4 existing constructors
  2. condition_to_joinir.rs (+180 lines)

    • Implemented extract_condition_variables()
    • Added recursive AST traversal
    • Added 3 comprehensive tests
  3. exit_binding.rs (+2 lines)

    • Fixed test boundary initialization

Build Status: Successful (0 errors, 57 warnings)

Phase 171-4: Merge Logic Integration

Files Modified:

  1. pattern2_with_break.rs (+19 lines)

    • Extract condition variables from AST
    • Look up HOST ValueIds
    • Pass to boundary constructor
  2. merge/mod.rs (+13 lines)

    • Add condition_inputs to used_values for remapping
    • Debug logging for condition inputs
  3. merge/instruction_rewriter.rs (+17 lines)

    • Add condition_inputs to value_map_for_injector
    • Enable remapping of HOST ValueIds
  4. joinir_inline_boundary_injector.rs (+35 lines)

    • Inject Copy instructions for condition inputs
    • Handle both join_inputs and condition_inputs

Build Status: Successful (0 errors, 57 warnings)


Technical Changes Summary

Data Flow: Before vs After

Before (Broken)

HOST: start = ValueId(33)
  ↓ condition_to_joinir reads directly
JoinIR: uses ValueId(33) in Compare
  ↓ NO BOUNDARY, NO REMAP
MIR: ValueId(33) undefined → ERROR ❌

After (Fixed)

HOST: start = ValueId(33)
  ↓ pattern2_with_break extracts "start"
  ↓ boundary.condition_inputs = [("start", ValueId(33))]
  ↓ merge adds to used_values
  ↓ remap_values: ValueId(33) → ValueId(100)
  ↓ BoundaryInjector: Copy ValueId(100) = Copy ValueId(33)
JoinIR: uses ValueId(33) in Compare
  ↓ instruction_rewriter remaps to ValueId(100)
MIR: ValueId(100) = Copy ValueId(33) → SUCCESS ✅

Key Implementation Points

1. Condition Variable Extraction (condition_to_joinir.rs:326-361)

pub fn extract_condition_variables(
    cond_ast: &ASTNode,
    exclude_vars: &[String],  // Loop parameters to exclude
) -> Vec<String>
  • Recursively traverses condition AST
  • Collects unique variable names
  • Filters out loop parameters
  • Returns sorted list (BTreeSet for determinism)

2. Boundary Registration (pattern2_with_break.rs:73-92)

let condition_var_names = extract_condition_variables(condition, &[loop_var_name.clone()]);
let mut condition_inputs = Vec::new();
for var_name in &condition_var_names {
    let host_value_id = self.variable_map.get(var_name)
        .copied()
        .ok_or_else(|| ...)?;
    condition_inputs.push((var_name.clone(), host_value_id));
}

3. Value Collection (merge/mod.rs:80-91)

// Add condition_inputs to used_values for remapping
if let Some(boundary) = boundary {
    for (var_name, host_value_id) in &boundary.condition_inputs {
        used_values.insert(*host_value_id);
    }
}

4. Value Remapping (merge/instruction_rewriter.rs:402-415)

// Add condition_inputs to value_map
for (var_name, host_value_id) in &boundary.condition_inputs {
    if let Some(remapped) = remapper.get_value(*host_value_id) {
        value_map_for_injector.insert(*host_value_id, remapped);
    }
}

5. Copy Injection (joinir_inline_boundary_injector.rs:86-116)

// Inject Copy instructions for condition_inputs
for (var_name, host_value_id) in &boundary.condition_inputs {
    if let Some(&remapped_value) = value_map.get(host_value_id) {
        let copy_inst = MirInstruction::Copy {
            dst: remapped_value,
            src: *host_value_id,
        };
        copy_instructions.push(copy_inst);
    }
}

Test Status

Unit Tests Pass

Extraction function tests (condition_to_joinir.rs):

  • test_extract_condition_variables_simple
  • test_extract_condition_variables_with_exclude
  • test_extract_condition_variables_complex

🔄 Integration Tests (In Progress)

Test File: local_tests/test_trim_main_pattern.hako

Current Status: Still shows undefined ValueId errors:

[ssa-undef-debug] fn=TrimTest.trim/1 bb=BasicBlockId(8) inst_idx=0 used=ValueId(33)
[ssa-undef-debug] fn=TrimTest.trim/1 bb=BasicBlockId(17) inst_idx=0 used=ValueId(49)

Next Debugging Steps:

  1. Add debug output to Pattern 2 lowerer to confirm condition variable extraction
  2. Verify boundary condition_inputs are populated
  3. Check value remapping in merge logic
  4. Verify Copy instruction injection

Remaining Work

Phase 171-5: Verification & Documentation

Tasks:

  1. Unit tests complete
  2. 🔄 Integration tests (debugging in progress)
  3. Update CURRENT_TASK.md
  4. Final documentation

Blocker: Need to debug why ValueId(33) is still undefined despite infrastructure being in place.

Hypothesis: The issue might be:

  • Condition variable extraction not running (check debug output)
  • Boundary not being passed correctly
  • Value remapping not applying to all instruction types
  • Copy instructions not being injected

Files Modified (Summary)

File Lines Changed Purpose
inline_boundary.rs +93 Add condition_inputs field + constructors
condition_to_joinir.rs +180 Extract condition variables from AST
exit_binding.rs +2 Fix test
pattern2_with_break.rs +19 Register condition inputs
merge/mod.rs +13 Add to used_values
merge/instruction_rewriter.rs +17 Add to value_map
joinir_inline_boundary_injector.rs +35 Inject Copy instructions
Total +359 lines Complete infrastructure

Design Principles Applied

  1. Box-First Philosophy

    • Clean separation: JoinIR frontier, boundary metadata, merge logic
    • Each component has single responsibility
  2. Fail-Fast

    • Explicit error when condition variable not in variable_map
    • No silent fallbacks
  3. Determinism

    • BTreeSet for sorted variable names
    • Consistent iteration order
  4. 80/20 Rule

    • Infrastructure first (80%)
    • Debugging and edge cases (20% - in progress)

Known Limitations

  1. Pattern 1/3/4 Not Yet Updated

    • Only Pattern 2 (loop_with_break) currently implements condition input extraction
    • Other patterns will need similar updates
  2. No Condition-Only Outputs

    • Current design handles inputs only
    • Outputs would need similar treatment (future extension point)
  3. Manual Debugging Required

    • Integration tests not yet passing
    • Need to trace execution to find where mapping breaks down

Next Steps

Immediate:

  1. Add debug output to confirm condition variable extraction runs
  2. Verify boundary.condition_inputs is populated
  3. Check if Copy instructions are actually injected
  4. Trace ValueId remapping through merge pipeline

Follow-up:

  1. Update Pattern 1/3/4 lowerers with same infrastructure
  2. Add integration tests for each pattern
  3. Document edge cases and limitations
  4. Update CURRENT_TASK.md with completion status

References

  • Phase 171-1 Analysis: phase171-1-boundary-analysis.md
  • Phase 171-2 Design: phase171-2-condition-inputs-design.md
  • Phase 171-3 Implementation: phase171-3-implementation-report.md
  • Phase 170 Background: phase170-completion-report.md