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:
@ -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)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user