wip(joinir): Phase 188-Impl-2 Pattern1Context host variable integration
Work in progress - ValueId mismatch issue needs resolution. Changes: - Add Pattern1Context struct with loop_var and value_allocator - Extract loop variable from condition in cf_loop_pattern1_minimal - Pass host's ValueId to Pattern 1 lowerer Problem discovered: - merge_joinir_mir_blocks() remaps ALL ValueIds - Host's i (ValueId(2)) gets remapped to ValueId(5) - ValueId(5) has no initialization → undefined value error Options under consideration: - Option A: Pinned Values (don't remap host ValueIds) - Option B: Inline Block Generation (no JoinModule) - Option C: Continuation Passing - Option D: SSA Copy Injection 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -375,18 +375,28 @@ impl super::MirBuilder {
|
|||||||
/// This bypasses the LoopFrontendBinding JSON path and directly calls
|
/// This bypasses the LoopFrontendBinding JSON path and directly calls
|
||||||
/// the Pattern 1 minimal lowerer for apps/tests/loop_min_while.hako
|
/// the Pattern 1 minimal lowerer for apps/tests/loop_min_while.hako
|
||||||
///
|
///
|
||||||
|
/// # Phase 188-Impl-2: Host Variable Integration
|
||||||
|
///
|
||||||
|
/// Extracts the loop variable from the host function (e.g., `i` from `i < 3`)
|
||||||
|
/// and passes it to the Pattern 1 lowerer along with a ValueId allocator.
|
||||||
|
///
|
||||||
/// # Pipeline
|
/// # Pipeline
|
||||||
/// 1. Call simple_while_minimal::lower_simple_while_minimal() → JoinModule
|
/// 1. Extract loop variable name from condition
|
||||||
/// 2. convert_join_module_to_mir_with_meta() → MirModule
|
/// 2. Look up ValueId in host variable_map
|
||||||
/// 3. Merge MIR blocks into current_function
|
/// 3. Create Pattern1Context with host variable + allocator
|
||||||
|
/// 4. Call simple_while_minimal::lower_simple_while_minimal() → JoinModule
|
||||||
|
/// 5. convert_join_module_to_mir_with_meta() → MirModule
|
||||||
|
/// 6. Merge MIR blocks into current_function
|
||||||
fn cf_loop_pattern1_minimal(
|
fn cf_loop_pattern1_minimal(
|
||||||
&mut self,
|
&mut self,
|
||||||
_condition: &ASTNode,
|
condition: &ASTNode,
|
||||||
_body: &[ASTNode],
|
_body: &[ASTNode],
|
||||||
_func_name: &str,
|
_func_name: &str,
|
||||||
debug: bool,
|
debug: bool,
|
||||||
) -> Result<Option<ValueId>, String> {
|
) -> Result<Option<ValueId>, String> {
|
||||||
use crate::mir::join_ir::lowering::simple_while_minimal::lower_simple_while_minimal;
|
use crate::mir::join_ir::lowering::simple_while_minimal::{
|
||||||
|
lower_simple_while_minimal, Pattern1Context,
|
||||||
|
};
|
||||||
use crate::mir::join_ir_vm_bridge::convert_join_module_to_mir_with_meta;
|
use crate::mir::join_ir_vm_bridge::convert_join_module_to_mir_with_meta;
|
||||||
use crate::mir::BasicBlockId;
|
use crate::mir::BasicBlockId;
|
||||||
use std::collections::{BTreeMap, BTreeSet};
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
@ -395,6 +405,27 @@ impl super::MirBuilder {
|
|||||||
eprintln!("[cf_loop/joinir/pattern1] Calling Pattern 1 minimal lowerer");
|
eprintln!("[cf_loop/joinir/pattern1] Calling Pattern 1 minimal lowerer");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Phase 188-Impl-2: Extract loop variable from condition
|
||||||
|
// For `i < 3`, extract `i` and look up its ValueId in variable_map
|
||||||
|
let loop_var_name = self.extract_loop_variable_from_condition(condition)?;
|
||||||
|
let loop_var_id = self
|
||||||
|
.variable_map
|
||||||
|
.get(&loop_var_name)
|
||||||
|
.copied()
|
||||||
|
.ok_or_else(|| {
|
||||||
|
format!(
|
||||||
|
"[cf_loop/pattern1] Loop variable '{}' not found in variable_map",
|
||||||
|
loop_var_name
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
eprintln!(
|
||||||
|
"[cf_loop/joinir/pattern1] Loop variable '{}' → {:?}",
|
||||||
|
loop_var_name, loop_var_id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Create a minimal LoopScopeShape (Phase 188: hardcoded for loop_min_while.hako)
|
// Create a minimal LoopScopeShape (Phase 188: hardcoded for loop_min_while.hako)
|
||||||
// Pattern 1 lowerer ignores the scope anyway, so this is just a placeholder
|
// Pattern 1 lowerer ignores the scope anyway, so this is just a placeholder
|
||||||
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
|
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
|
||||||
@ -411,8 +442,16 @@ impl super::MirBuilder {
|
|||||||
variable_definitions: BTreeMap::new(),
|
variable_definitions: BTreeMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Call Pattern 1 lowerer
|
// Phase 188-Impl-2: Create Pattern1Context with host variable + allocator
|
||||||
let join_module = match lower_simple_while_minimal(scope) {
|
// Clone value_gen to move into closure
|
||||||
|
let mut value_gen_clone = self.value_gen.clone();
|
||||||
|
let ctx = Pattern1Context {
|
||||||
|
loop_var: loop_var_id,
|
||||||
|
value_allocator: Box::new(move || value_gen_clone.next()),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Call Pattern 1 lowerer with host context
|
||||||
|
let join_module = match lower_simple_while_minimal(scope, Some(ctx)) {
|
||||||
Some(module) => module,
|
Some(module) => module,
|
||||||
None => {
|
None => {
|
||||||
if debug {
|
if debug {
|
||||||
@ -1174,6 +1213,42 @@ impl super::MirBuilder {
|
|||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Phase 188-Impl-2: Extract loop variable name from condition
|
||||||
|
///
|
||||||
|
/// For `i < 3`, extracts `i`.
|
||||||
|
/// For `arr.length() > 0`, extracts `arr`.
|
||||||
|
///
|
||||||
|
/// This is a minimal implementation that handles simple comparison patterns.
|
||||||
|
fn extract_loop_variable_from_condition(&self, condition: &ASTNode) -> Result<String, String> {
|
||||||
|
use crate::ast::BinaryOperator;
|
||||||
|
|
||||||
|
match condition {
|
||||||
|
ASTNode::BinaryOp {
|
||||||
|
operator, left, ..
|
||||||
|
} if matches!(
|
||||||
|
operator,
|
||||||
|
BinaryOperator::Less
|
||||||
|
| BinaryOperator::Greater
|
||||||
|
| BinaryOperator::LessEqual
|
||||||
|
| BinaryOperator::GreaterEqual
|
||||||
|
) =>
|
||||||
|
{
|
||||||
|
// Binary comparison: extract variable from left side
|
||||||
|
match &**left {
|
||||||
|
ASTNode::Variable { name, .. } => Ok(name.clone()),
|
||||||
|
_ => Err(format!(
|
||||||
|
"[cf_loop/pattern1] Cannot extract loop variable from condition: {:?}",
|
||||||
|
condition
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Err(format!(
|
||||||
|
"[cf_loop/pattern1] Unsupported loop condition pattern: {:?}",
|
||||||
|
condition
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Control-flow: throw
|
/// Control-flow: throw
|
||||||
pub(super) fn cf_throw(&mut self, expression: ASTNode) -> Result<ValueId, String> {
|
pub(super) fn cf_throw(&mut self, expression: ASTNode) -> Result<ValueId, String> {
|
||||||
if self.in_cleanup_block && !self.cleanup_allow_throw {
|
if self.in_cleanup_block && !self.cleanup_allow_throw {
|
||||||
|
|||||||
@ -359,7 +359,8 @@ impl LoopToJoinLowerer {
|
|||||||
eprintln!("[LoopToJoinLowerer] Trying Pattern 1 lowering for {:?}", name);
|
eprintln!("[LoopToJoinLowerer] Trying Pattern 1 lowering for {:?}", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(result) = super::simple_while_minimal::lower_simple_while_minimal(scope.clone()) {
|
// Phase 188-Impl-2: Use standalone mode (None context) for backward compatibility
|
||||||
|
if let Some(result) = super::simple_while_minimal::lower_simple_while_minimal(scope.clone(), None) {
|
||||||
if self.debug {
|
if self.debug {
|
||||||
eprintln!("[LoopToJoinLowerer] Pattern 1 lowering succeeded for {:?}", name);
|
eprintln!("[LoopToJoinLowerer] Pattern 1 lowering succeeded for {:?}", name);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,20 +49,56 @@ use crate::mir::join_ir::{
|
|||||||
};
|
};
|
||||||
use crate::mir::ValueId;
|
use crate::mir::ValueId;
|
||||||
|
|
||||||
|
/// Context passed from the host function to the Pattern 1 lowerer
|
||||||
|
pub struct Pattern1Context {
|
||||||
|
/// The loop variable ValueId from the host function (e.g., ValueId(6) for `i`)
|
||||||
|
pub loop_var: ValueId,
|
||||||
|
/// ValueId allocator function
|
||||||
|
pub value_allocator: Box<dyn FnMut() -> ValueId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pattern1Context {
|
||||||
|
/// Create a standalone context with hardcoded ValueIds (for backward compatibility)
|
||||||
|
pub fn standalone() -> Self {
|
||||||
|
let mut counter = 1000u32;
|
||||||
|
Self {
|
||||||
|
loop_var: ValueId(counter),
|
||||||
|
value_allocator: Box::new(move || {
|
||||||
|
counter += 1;
|
||||||
|
ValueId(counter)
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Lower Pattern 1 (Simple While Loop) to JoinIR
|
/// Lower Pattern 1 (Simple While Loop) to JoinIR
|
||||||
///
|
///
|
||||||
/// This is a minimal implementation for loop_min_while.hako.
|
/// This is a minimal implementation for loop_min_while.hako.
|
||||||
/// It generates hardcoded JoinIR for the specific pattern.
|
/// It generates JoinIR that integrates with the host function's variable bindings.
|
||||||
|
///
|
||||||
|
/// # Phase 188-Impl-2: Host Variable Integration
|
||||||
|
///
|
||||||
|
/// This version accepts the host's loop variable ValueId and allocates fresh IDs
|
||||||
|
/// for intermediate values. This ensures the generated JoinIR connects properly
|
||||||
|
/// to the host function's variable bindings.
|
||||||
|
///
|
||||||
|
/// If called without a context (from legacy code), it uses standalone mode with
|
||||||
|
/// hardcoded ValueIds for backward compatibility.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `_scope` - LoopScopeShape (reserved for future generic implementation)
|
/// * `_scope` - LoopScopeShape (reserved for future generic implementation)
|
||||||
|
/// * `ctx` - Pattern1Context containing host variable bindings (or None for standalone)
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// * `Some(JoinModule)` - Successfully lowered to JoinIR
|
/// * `Some(JoinModule)` - Successfully lowered to JoinIR
|
||||||
/// * `None` - Pattern not matched (fallback to other lowerers)
|
/// * `None` - Pattern not matched (fallback to other lowerers)
|
||||||
pub fn lower_simple_while_minimal(_scope: LoopScopeShape) -> Option<JoinModule> {
|
pub fn lower_simple_while_minimal(
|
||||||
|
_scope: LoopScopeShape,
|
||||||
|
ctx: Option<Pattern1Context>,
|
||||||
|
) -> Option<JoinModule> {
|
||||||
|
let mut ctx = ctx.unwrap_or_else(Pattern1Context::standalone);
|
||||||
// Phase 188-Impl-1: Hardcoded JoinIR for loop_min_while.hako
|
// Phase 188-Impl-1: Hardcoded JoinIR for loop_min_while.hako
|
||||||
// This establishes the infrastructure. Generic implementation in Phase 188-Impl-2+.
|
// This establishes the infrastructure. Generic implementation in Phase 188-Impl-2+.
|
||||||
|
|
||||||
@ -76,32 +112,32 @@ pub fn lower_simple_while_minimal(_scope: LoopScopeShape) -> Option<JoinModule>
|
|||||||
let k_exit_id = JoinFuncId::new(2);
|
let k_exit_id = JoinFuncId::new(2);
|
||||||
|
|
||||||
// ==================================================================
|
// ==================================================================
|
||||||
// ValueId allocation (hardcoded for minimal implementation)
|
// ValueId allocation (Phase 188-Impl-2: Use host variable + allocator)
|
||||||
// ==================================================================
|
// ==================================================================
|
||||||
let i_init = ValueId(1000);
|
// Host's loop variable (e.g., ValueId(6) for `i`)
|
||||||
let loop_result = ValueId(1001);
|
let i_init = ctx.loop_var;
|
||||||
let const_0_main = ValueId(1002);
|
|
||||||
|
// Allocate fresh IDs for local values
|
||||||
|
let loop_result = (ctx.value_allocator)();
|
||||||
|
let const_0_main = (ctx.value_allocator)();
|
||||||
|
|
||||||
// loop_step locals
|
// loop_step locals
|
||||||
let i_param = ValueId(2000);
|
let i_param = (ctx.value_allocator)();
|
||||||
let const_3 = ValueId(2001);
|
let const_3 = (ctx.value_allocator)();
|
||||||
let cmp_lt = ValueId(2002);
|
let cmp_lt = (ctx.value_allocator)();
|
||||||
let exit_cond = ValueId(2003);
|
let exit_cond = (ctx.value_allocator)();
|
||||||
let const_1 = ValueId(2004);
|
let const_1 = (ctx.value_allocator)();
|
||||||
let i_next = ValueId(2005);
|
let i_next = (ctx.value_allocator)();
|
||||||
|
|
||||||
// ==================================================================
|
// ==================================================================
|
||||||
// main() function
|
// main() function
|
||||||
// ==================================================================
|
// ==================================================================
|
||||||
let mut main_func = JoinFunction::new(main_id, "main".to_string(), vec![]);
|
let mut main_func = JoinFunction::new(main_id, "main".to_string(), vec![]);
|
||||||
|
|
||||||
// i_init = 0
|
// Phase 188-Impl-2: Skip i_init = 0 (host already initialized the variable)
|
||||||
main_func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
// The host's ValueId (i_init) is already bound to 0 in the host function
|
||||||
dst: i_init,
|
|
||||||
value: ConstValue::Integer(0),
|
|
||||||
}));
|
|
||||||
|
|
||||||
// result = loop_step(i_init)
|
// result = loop_step(i_init) ← Use host's i directly
|
||||||
main_func.body.push(JoinInst::Call {
|
main_func.body.push(JoinInst::Call {
|
||||||
func: loop_step_id,
|
func: loop_step_id,
|
||||||
args: vec![i_init],
|
args: vec![i_init],
|
||||||
|
|||||||
Reference in New Issue
Block a user