## Problem
Pattern 4 (loop with continue) was producing SSA-undef errors due to function_params
key mismatch. function_params uses 'join_func_0'/'join_func_1' format (from join_func_name()),
but code was looking for 'main'/'loop_step'.
## Solution
- Fix mod.rs: Use correct function keys 'join_func_0' and 'join_func_1'
- Add warning logs to detect future key mismatches immediately
- Update pattern4_with_continue.rs: Mark as fully implemented (not stub)
- Remove unused imports: MergeResult, TailCallKind, classify_tail_call, etc.
## Testing
- Pattern 3 (If PHI): sum=9 ✅
- Pattern 4 (Continue): 25 ✅ (1+3+5+7+9)
- No SSA-undef errors
- No new warnings on successful execution
## Debug Improvements
Added pre-flight checks in merge/mod.rs Phase 33-21:
```
[cf_loop/joinir] WARNING: function_params.get('join_func_0') returned None.
Available keys: [...]
```
This will save significant debugging time for future parameter mapping issues.
🤖 Generated with Claude Code
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Wire LoopUpdateSummary box into real code paths:
- Add `update_summary: Option<LoopUpdateSummary>` field to LoopFeatures
- Add `detect_with_updates()` method to CaseALoweringShape
- Uses UpdateKind (CounterLike/AccumulationLike) for classification
- Single CounterLike carrier → StringExamination
- AccumulationLike present → ArrayAccumulation or IterationWithAccumulation
- Update loop_to_join.rs to use detect_with_updates()
- Update deprecated detect() to use detect_with_updates() internally
- Set update_summary: None in AST-based feature extraction
Carrier name heuristics now encapsulated in LoopUpdateSummary box.
No regression: 15/16 loop smoke tests pass (1 failure is pre-existing).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add UpdateKind/LoopUpdateSummary types for loop update pattern analysis.
Currently uses carrier name heuristics internally (same as 170-C-1),
but provides clean interface for future AST/MIR-based analysis.
New types:
- UpdateKind: CounterLike | AccumulationLike | Other
- CarrierUpdateInfo: name + kind pair
- LoopUpdateSummary: collection with helper methods
Helper methods:
- has_single_counter(): for StringExamination detection
- has_accumulation(): for ArrayAccumulation detection
Design doc: docs/development/current/main/phase170-c2-update-summary-design.md
- Describes LoopFeatures.update_summary integration plan
- Migration path to AST/MIR analysis
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add detect_with_carrier_name() to improve CaseALoweringShape detection
accuracy by using carrier variable names as a heuristic.
Since LoopUpdateAnalyzer operates at AST level (not accessible from MIR),
use carrier naming conventions instead:
- Typical index names (i, e, idx, pos, start, end) → StringExamination
- Other names (result, items, defs) → ArrayAccumulation
This reduces Generic fallback cases for single-carrier loops.
Changes:
- case_a_lowering_shape.rs: Add detect_with_carrier_name(), is_typical_index_name()
- loop_to_join.rs: Extract progress_carrier name, call new detection function
Phase 170-C-2 can add MIR-based update pattern analysis for higher accuracy.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace hardcoded function name routing with CaseALoweringShape-based
dispatch in LoopToJoinLowerer::lower_with_scope().
Design: Hybrid approach with backward compatibility
1. Detect shape from LoopScopeShape (structure-based, name-agnostic)
2. If shape is recognized → use corresponding lowerer
3. If shape is Generic/NotCaseA → fallback to name-based whitelist
Shape to lowerer mapping:
- StringExamination → skip_ws lowerer (Main.skip/1, trim patterns)
- ArrayAccumulation → append_defs lowerer
- IterationWithAccumulation → stage1 lowerer
This enables gradual migration: existing name-based code continues to
work, while new patterns can be added purely by structure detection.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Clarify design principle: CaseALoweringShape does NOT look at function names.
Input must be LoopFeatures/LoopPatternKind only (structure-based detection).
Changes:
- Add detect_from_features(LoopFeatures, carrier_count, has_progress_carrier)
- Deprecate detect(LoopScopeShape) with backward-compat wrapper
- Case-A now rejects loops with `has_continue` (only break is allowed)
- Document Phase 170-B future work (loop body AST analysis)
Design Principle:
> "CaseALoweringShape は 関数名を見ない。LoopFeatures/LoopPatternKind だけを入力にする"
This ensures generic routing works for ANY structurally matching loop.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add NYASH_JOINIR_STRUCTURE_ONLY=1 environment variable to bypass
function name whitelist and route purely based on loop structure.
When enabled:
- Skips hardcoded function name whitelist (13 entries)
- Routes directly to pattern detection (LoopPatternContext)
- Falls back to legacy LoopBuilder if no pattern matches
This is Phase 1 of structure-based routing migration:
- Phase 1: Opt-in via env flag (this commit) ✅
- Phase 2: Make structure-based default (future)
- Phase 3: Remove whitelist entirely (future)
Verified:
- test_loop_return.hako → RC: 2 ✅
- test_trim_loop.hako → RC: 3 ✅🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Box theory refactoring: Extract exit_bindings construction logic from
pattern lowerers into focused, reusable ExitMetaCollector Box.
**Changes**:
1. Created meta_collector.rs (+102 lines):
- ExitMetaCollector::collect() builds exit_bindings from ExitMeta
- Pure function (no side effects)
- Reusable by all pattern lowerers
2. Updated pattern2_with_break.rs (-20 lines):
- Use ExitMetaCollector::collect() instead of inline filter_map
- Removed manual binding construction loop
- Cleaner caller code
3. Made exit_line module public:
- Allows pattern lowerers to use ExitMetaCollector
- Clear module visibility boundaries
**Box Design**:
- Single responsibility: Convert ExitMeta + variable_map → exit_bindings
- Pure function: No side effects, testable independently
- Reusable: Can be used by Pattern 3, Pattern 4, etc.
**Testing**:
- Build: ✅ Success (1m 04s)
- Execution: ✅ RC: 0 (Pattern 2 verified)
- Regression: ✅ No issues
**Metrics**:
- New lines: +102 (meta_collector.rs)
- Removed lines: -20 (pattern2_with_break.rs)
- Net change: +82 lines
- Code clarity: Significantly improved
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
Previously, PHI incoming values were not being remapped from JoinIR-local
ValueIds to Host ValueIds. Only the block IDs were remapped.
This fix ensures both block ID and value ID are remapped for PHI
instructions during JoinIR merge.
Note: trim still fails because condition variable exit values are not
being reflected in variable_map (Phase 172-4/5 work needed).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
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>
**Phase 193-2**: Add flexible builder methods to CarrierInfo and ExitMeta
## Summary
Enhanced CarrierInfo and ExitMeta with convenient builder methods that support
multiple construction patterns. This reduces boilerplate and makes carrier info
generation more flexible for different lowering scenarios.
## Changes
### CarrierInfo New Methods
- **from_variable_map()**: Automatically extract carriers from variable_map
- Eliminates manual carrier listing for simple cases
- Auto-discovers all non-loop-control variables
- Deterministic sorting for reproducibility
- **with_explicit_carriers()**: Selective carrier extraction
- Choose which variables to treat as carriers
- Useful for Pattern 5+ with complex variable sets
- Validates all carriers exist in variable_map
- **with_carriers()**: Direct CarrierVar construction
- Most explicit method for advanced use cases
- Use when CarrierVar structs already exist
- Auto-sorts for determinism
### CarrierInfo Query Methods
- **carrier_count()**: Get number of carriers
- **is_multi_carrier()**: Check if multi-carrier loop
- **find_carrier()**: Lookup specific carrier by name
### ExitMeta New Methods
- **binding_count()**: Get number of exit bindings
- **is_empty()**: Check if any exit values exist
- **find_binding()**: Lookup exit value by carrier name
- **with_binding()**: Chainable binding addition
## Design Benefits
| Aspect | Benefit |
|--------|---------|
| **Flexibility** | 3 construction patterns for different scenarios |
| **Clarity** | Explicit method names document intent |
| **Ergonomics** | Reduced boilerplate in lowerers |
| **Validation** | Error handling for missing variables |
| **Determinism** | Automatic sorting in all methods |
## Usage Examples
```rust
// Pattern 1: Auto-discover from variable_map
let info = CarrierInfo::from_variable_map("i", &variable_map)?;
// Pattern 2: Selective carriers
let info = CarrierInfo::with_explicit_carriers(
"i", loop_id,
vec!["sum".into(), "count".into()],
&variable_map
)?;
// Pattern 3: Manual construction
let info = CarrierInfo::with_carriers("i", loop_id, carriers);
// Query methods
if info.is_multi_carrier() {
println!("Multi-carrier loop with {} carriers", info.carrier_count());
}
// ExitMeta chaining
let meta = ExitMeta::empty()
.with_binding("sum".into(), ValueId(15))
.with_binding("count".into(), ValueId(16));
```
## Metrics
- CarrierInfo: +3 construction methods, +3 query methods
- ExitMeta: +4 new methods (existing 3 methods unchanged)
- Total lines added: ~150 (including docs)
- Build time: 1m 05s ✅
- Zero regressions ✅
## Next Steps
- Phase 193-3: Pattern Classification Improvement
- Phase 194: Further optimization opportunities
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Two root causes for Pattern 4 outputting 0 instead of 25:
1. instruction_rewriter.rs:92 - BasicBlock::new() was overwriting
existing blocks that already contained PHI instructions from
JoinIR Select lowering. Fixed by removing and reusing existing
blocks instead of creating new ones.
2. pattern4_with_continue.rs - JoinIR exit PHI (ValueId 15) was not
connected to host's sum variable, causing DCE to eliminate the PHI
as "unused". Fixed by using new_with_exit_bindings() with explicit
LoopExitBinding to connect k_exit's sum_exit to host's sum slot.
Test result: loop_continue_pattern4.hako now correctly outputs 25.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Change from dual-path (continue Jump + normal Call) to single-path
using Select to merge sum values before tail call.
- Replace conditional Jump with Select instruction
- sum_merged = Select(continue_cond, sum_param, sum_next)
- Single tail call: Call(loop_step, [i_next, sum_merged])
Known issue: PHI generated at JoinIR level but lost in MIR merge.
Need to investigate JoinIR→MIR merge layer.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Fix: Call with Callee::Method now includes receiver in used_values()
- Prevents DCE from eliminating Copy instructions that define receivers
- Pattern 3 (loop_if_phi.hako) now works correctly (sum=9)
- Add: NYASH_DCE_TRACE=1 for debugging eliminated instructions
- Shows which pure instructions DCE removes and from which block
- Cleanup: Consolidate Call used_values to single source of truth
- Early return in methods.rs handles all Call variants
- Removed duplicate match arm (now unreachable!())
- ChatGPT's suggestion for cleaner architecture
- Docs: Phase 166 analysis of inst_meta layer architecture
- Identified CSE pass callee bug (to be fixed next)
- Improvement proposals for CallLikeInst
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Document the new JoinLoopTrace unified tracing system:
- NYASH_JOINIR_DEBUG for routing and block allocation traces
- Example output format with [trace:pattern], [trace:joinir], [trace:blocks]
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Updated mod.rs with comprehensive module organization documentation
- Documented before/after line counts and size reductions
- Removed old generic_case_a_old.rs monolith file
- Verified all imports are clean (no unused warnings)
- Verified all visibility is correct (pub(crate) for public API)
Final statistics:
- Before: 1,056 lines in single file
- After: 1,634 lines across 7 focused modules
- Main coordinator: 93 lines (91% reduction)
- Average module size: 233 lines (manageable, focused)
- Largest module: trim.rs at 537 lines (still maintainable)
All success criteria met:
✅ All 7 modules created
✅ cargo build --release succeeds
✅ Zero breaking changes (all APIs compatible)
✅ Module documentation comprehensive
✅ All visibility correct
Ref: Phase 192 modularization effort
- Created src/mir/join_ir/lowering/generic_case_a/ directory
- Moved entry_builder.rs and whitespace_check.rs into new directory
- Created mod.rs with public API exports and comprehensive documentation
- Renamed old generic_case_a.rs to generic_case_a_old.rs temporarily
- Updated parent mod.rs to import from new structure
Ref: Phase 192 modularization effort
- Document 17-module structure with complete directory tree
- Record 50% line reduction achievement (312 → 187 lines in mod.rs)
- List all 7 modularization phases
- Add navigation link to architecture section
- Total module count: 2,129 lines across 17 files