feat(phase143): Step 4 - Branch instruction emission with JoinModule

Phase 143 P0 Step 4: Build JoinModule with 6 functions and Jump/Call instructions

- Allocate 6 JoinFuncIds (main, loop_step, loop_cond_check, k_then, k_else, k_exit)
- Create JoinFunction skeletons with correct env parameter passing
- Lower condition in loop_cond_check using NormalizedExprLowererBox
- Emit Jump instruction with conditional (cond=Some(vid))
  - Jump to k_exit if condition is true (break)
  - Fall through to Call(loop_step) if condition is false (continue loop)
- Build complete JoinModule with 6 functions and mark as normalized
- Return Ok(None) skeleton (Step 6 will add Return statement to k_exit)

Key design decisions:
- Jump semantics: jump to cont if cond is true, else fall through
- Fallthrough behavior: implicit continue via Call(loop_step)
- k_then and k_else: unused in P0 scope (kept for structural clarity)
- cargo check passes with no errors

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

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-19 06:00:21 +09:00
parent f55f6cc653
commit 434c891a1b

View File

@ -104,11 +104,181 @@ impl LoopTrueIfBreakContinueBuilderBox {
}
}
// Phase 143 P0: Skeleton continues to return Ok(None)
// Steps 4-6 will build the actual JoinModule with Jump instructions
if crate::config::env::joinir_dev_enabled() {
eprintln!("[phase143/debug] Step 3 validation passed, skeleton still returns Ok(None)");
// Step 4 (P0): Build JoinModule with 6 functions and Branch instructions
// === Helper closures ===
fn alloc_value_id(next_value_id: &mut u32) -> ValueId {
let vid = ValueId(*next_value_id);
*next_value_id += 1;
vid
}
let alloc_env_params =
|fields: &[String], next_value_id: &mut u32| -> Vec<ValueId> {
fields
.iter()
.map(|_| alloc_value_id(next_value_id))
.collect()
};
let build_env_map = |fields: &[String], params: &[ValueId]| -> BTreeMap<String, ValueId> {
let mut env = BTreeMap::new();
for (name, vid) in fields.iter().zip(params.iter()) {
env.insert(name.clone(), *vid);
}
env
};
let collect_env_args = |fields: &[String], env: &BTreeMap<String, ValueId>| -> Result<Vec<ValueId>, String> {
let mut args = Vec::with_capacity(fields.len());
for name in fields {
let vid = env.get(name).copied().ok_or_else(|| {
format!("phase143/branch_emission: env missing required field '{name}'")
})?;
args.push(vid);
}
Ok(args)
};
// === Phase 4: Allocate IDs and build env ===
let mut next_value_id: u32 = 1;
let env_fields = env_layout.env_fields();
// Allocate 6 JoinFuncIds
let main_id = JoinFuncId::new(0);
let loop_step_id = JoinFuncId::new(1);
let loop_cond_check_id = JoinFuncId::new(2);
let k_then_id = JoinFuncId::new(3);
let k_else_id = JoinFuncId::new(4);
let k_exit_id = JoinFuncId::new(5);
// === main(env) ===
let main_params = alloc_env_params(&env_fields, &mut next_value_id);
let env_main = build_env_map(&env_fields, &main_params);
let main_func = {
let mut f = JoinFunction::new(main_id, "main".to_string(), main_params);
// main: Call(loop_step, env)
let loop_step_args = collect_env_args(&env_fields, &env_main)?;
f.body.push(JoinInst::Call {
func: loop_step_id,
args: loop_step_args,
k_next: None,
dst: None,
});
f
};
// === loop_step(env) ===
let loop_step_params = alloc_env_params(&env_fields, &mut next_value_id);
let env_loop_step = build_env_map(&env_fields, &loop_step_params);
let loop_step_func = {
let mut f = JoinFunction::new(loop_step_id, "loop_step".to_string(), loop_step_params);
// loop_step: Call(loop_cond_check, env)
let loop_cond_check_args = collect_env_args(&env_fields, &env_loop_step)?;
f.body.push(JoinInst::Call {
func: loop_cond_check_id,
args: loop_cond_check_args,
k_next: None,
dst: None,
});
f
};
// === loop_cond_check(env): Lower condition and emit Jump ===
let loop_cond_check_params = alloc_env_params(&env_fields, &mut next_value_id);
let env_loop_cond_check = build_env_map(&env_fields, &loop_cond_check_params);
let loop_cond_check_func = {
let mut f = JoinFunction::new(
loop_cond_check_id,
"loop_cond_check".to_string(),
loop_cond_check_params,
);
// Reuse the validated condition lowering from Step 3
let cond_vid = match NormalizedExprLowererBox::lower_expr_with_scope(
ExprLoweringScope::PureOnly,
&cond_ast,
&env_loop_cond_check,
&mut f.body,
&mut next_value_id,
) {
Ok(Some(vid)) => vid,
_ => {
return Err("phase143/branch_emission: condition lowering failed unexpectedly".to_string());
}
};
// Emit conditional Jump instruction
// Jump { cond: Some(cond_vid), cont: k_exit }
// If cond_vid is true: jump to k_exit (break)
// If cond_vid is false: fall through to next instruction (continue)
let k_exit_args = collect_env_args(&env_fields, &env_loop_cond_check)?;
f.body.push(JoinInst::Jump {
cont: k_exit_id.as_cont(),
args: k_exit_args,
cond: Some(cond_vid),
});
// If we don't jump (condition is false), continue the loop
// Call loop_step() to iterate again
let loop_step_args = collect_env_args(&env_fields, &env_loop_cond_check)?;
f.body.push(JoinInst::Call {
func: loop_step_id,
args: loop_step_args,
k_next: None,
dst: None,
});
f
};
// === k_then(env): Not used in P0 (direct Jump from loop_cond_check) ===
// Kept for structural clarity in case future extensions need it
let k_then_params = alloc_env_params(&env_fields, &mut next_value_id);
let _env_k_then = build_env_map(&env_fields, &k_then_params);
let k_then_func = {
let f = JoinFunction::new(k_then_id, "k_then".to_string(), k_then_params);
// Empty placeholder: P0 doesn't use this function
f
};
// === k_else(env): Not used in P0 (direct Call from loop_cond_check fallthrough) ===
// Kept for structural clarity in case future extensions need it
let k_else_params = alloc_env_params(&env_fields, &mut next_value_id);
let _env_k_else = build_env_map(&env_fields, &k_else_params);
let k_else_func = {
let f = JoinFunction::new(k_else_id, "k_else".to_string(), k_else_params);
// Empty placeholder: P0 doesn't use this function
f
};
// === k_exit(env): Placeholder for return ===
let k_exit_params = alloc_env_params(&env_fields, &mut next_value_id);
let _env_k_exit = build_env_map(&env_fields, &k_exit_params);
let k_exit_func = {
let f = JoinFunction::new(k_exit_id, "k_exit".to_string(), k_exit_params);
// Step 6 will add: Return with exit_values
// For now, placeholder empty (will be filled in Step 6)
f
};
// === Build JoinModule ===
let mut module = JoinModule::new();
module.add_function(main_func);
module.add_function(loop_step_func);
module.add_function(loop_cond_check_func);
module.add_function(k_then_func);
module.add_function(k_else_func);
module.add_function(k_exit_func);
module.entry = Some(main_id);
module.mark_normalized();
if crate::config::env::joinir_dev_enabled() {
eprintln!("[phase143/debug] Step 4: JoinModule built with 6 functions and Jump instructions");
}
// Step 5-6 will complete TailCalls and exit handling
// For now, return Ok(None) skeleton
Ok(None)
}