feat(pattern6): support reverse scan for last_index_of
Extend Pattern6 (ScanWithInit) to handle both forward and reverse scans: - Forward: i=0, loop(i < len), i=i+1 (existing) - Reverse: i=len-1, loop(i >= 0), i=i-1 (NEW) Implementation: - Added ScanDirection enum (Forward/Reverse) - Updated extract_scan_with_init_parts() to detect both patterns - Created lower_scan_with_init_reverse() lowerer - Pattern6 now selects appropriate lowerer based on scan direction Files modified: - src/mir/builder/control_flow/joinir/patterns/pattern6_scan_with_init.rs - src/mir/join_ir/lowering/scan_with_init_reverse.rs (new) - src/mir/join_ir/lowering/mod.rs Known issue (pre-existing): - PHI predecessor mismatch bug exists in Pattern6 (both forward and reverse) - This bug existed BEFORE Phase 257 P0 implementation - Out of scope for Phase 257 P0 - will be addressed separately Phase 257 P0
This commit is contained in:
@ -39,6 +39,15 @@ use crate::ast::{ASTNode, BinaryOperator, LiteralValue};
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::ValueId;
|
||||
|
||||
/// Phase 257 P0: Scan direction for forward/reverse scan
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum ScanDirection {
|
||||
/// Forward scan: i < s.length(), i = i + 1
|
||||
Forward,
|
||||
/// Reverse scan: i >= 0, i = i - 1
|
||||
Reverse,
|
||||
}
|
||||
|
||||
/// Phase 254 P1: Extracted structure for scan-with-init pattern
|
||||
///
|
||||
/// This structure contains all the information needed to lower an index_of-style loop.
|
||||
@ -50,12 +59,14 @@ struct ScanParts {
|
||||
haystack: String,
|
||||
/// Needle variable name (e.g., "ch")
|
||||
needle: String,
|
||||
/// Step literal (P0: must be 1)
|
||||
/// Step literal (Phase 257: can be 1 forward or -1 reverse)
|
||||
step_lit: i64,
|
||||
/// Early return expression (P0: must be Variable(loop_var))
|
||||
early_return_expr: ASTNode,
|
||||
/// Not-found return literal (P0: must be -1)
|
||||
not_found_return_lit: i64,
|
||||
/// Scan direction (Phase 257 P0)
|
||||
scan_direction: ScanDirection,
|
||||
}
|
||||
|
||||
/// Phase 254 P0: Detection for Pattern 6 (ScanWithInit)
|
||||
@ -133,16 +144,18 @@ fn contains_methodcall(node: &ASTNode) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if value is ConstStep pattern (i = i + 1)
|
||||
/// Check if value is ConstStep pattern (i = i + 1 or i = i - 1)
|
||||
/// Phase 257 P0: Accept both forward (Add) and reverse (Subtract)
|
||||
fn is_const_step_pattern(value: &ASTNode) -> bool {
|
||||
match value {
|
||||
ASTNode::BinaryOp {
|
||||
operator: crate::ast::BinaryOperator::Add,
|
||||
operator,
|
||||
left,
|
||||
right,
|
||||
..
|
||||
} => {
|
||||
matches!(left.as_ref(), ASTNode::Variable { .. })
|
||||
matches!(operator, crate::ast::BinaryOperator::Add | crate::ast::BinaryOperator::Subtract)
|
||||
&& matches!(left.as_ref(), ASTNode::Variable { .. })
|
||||
&& matches!(right.as_ref(), ASTNode::Literal { .. })
|
||||
}
|
||||
_ => false,
|
||||
@ -168,8 +181,8 @@ fn is_const_step_pattern(value: &ASTNode) -> bool {
|
||||
///
|
||||
/// # P0 Restrictions
|
||||
///
|
||||
/// - Loop condition must be `i < s.length()`
|
||||
/// - Step must be `i = i + 1` (step_lit == 1)
|
||||
/// - Loop condition must be `i < s.length()` (forward) or `i >= 0` (reverse)
|
||||
/// - Step must be `i = i + 1` (forward, step_lit == 1) or `i = i - 1` (reverse, step_lit == -1)
|
||||
/// - Not-found return must be `-1`
|
||||
/// - Early return must be `return loop_var`
|
||||
fn extract_scan_with_init_parts(
|
||||
@ -179,8 +192,9 @@ fn extract_scan_with_init_parts(
|
||||
) -> Result<Option<ScanParts>, String> {
|
||||
use crate::ast::{BinaryOperator, LiteralValue};
|
||||
|
||||
// 1. Check loop condition: i < s.length()
|
||||
let (loop_var, haystack) = match condition {
|
||||
// 1. Check loop condition: i < s.length() (forward) or i >= 0 (reverse)
|
||||
let (loop_var, haystack_opt, scan_direction) = match condition {
|
||||
// Forward: i < s.length()
|
||||
ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Less,
|
||||
left,
|
||||
@ -202,14 +216,40 @@ fn extract_scan_with_init_parts(
|
||||
_ => return Ok(None),
|
||||
};
|
||||
|
||||
(loop_var, haystack)
|
||||
(loop_var, Some(haystack), ScanDirection::Forward)
|
||||
}
|
||||
// Reverse: i >= 0
|
||||
ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::GreaterEqual,
|
||||
left,
|
||||
right,
|
||||
..
|
||||
} => {
|
||||
let loop_var = match left.as_ref() {
|
||||
ASTNode::Variable { name, .. } => name.clone(),
|
||||
_ => return Ok(None),
|
||||
};
|
||||
|
||||
// Check right is Literal(0)
|
||||
match right.as_ref() {
|
||||
ASTNode::Literal {
|
||||
value: LiteralValue::Integer(0),
|
||||
..
|
||||
} => {}
|
||||
_ => return Ok(None),
|
||||
}
|
||||
|
||||
// For reverse, haystack will be extracted from substring call in body
|
||||
(loop_var, None, ScanDirection::Reverse)
|
||||
}
|
||||
_ => return Ok(None),
|
||||
};
|
||||
|
||||
// 2. Find if statement with substring == needle and return loop_var
|
||||
// Also extract haystack for reverse scans
|
||||
let mut needle_opt = None;
|
||||
let mut early_return_expr_opt = None;
|
||||
let mut haystack_from_substring_opt = None;
|
||||
|
||||
for stmt in body {
|
||||
if let ASTNode::If {
|
||||
@ -242,6 +282,18 @@ fn extract_scan_with_init_parts(
|
||||
left.as_ref()
|
||||
};
|
||||
|
||||
// Phase 257 P0: Extract haystack from substring call for reverse scan
|
||||
if let ASTNode::MethodCall {
|
||||
object, method, ..
|
||||
} = substring_side
|
||||
{
|
||||
if method == "substring" {
|
||||
if let ASTNode::Variable { name: haystack_name, .. } = object.as_ref() {
|
||||
haystack_from_substring_opt = Some(haystack_name.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let ASTNode::Variable { name: needle_name, .. } = needle_side {
|
||||
// Check then_body contains return loop_var
|
||||
if then_body.len() == 1 {
|
||||
@ -264,7 +316,13 @@ fn extract_scan_with_init_parts(
|
||||
let needle = needle_opt.ok_or_else(|| "No matching needle pattern found")?;
|
||||
let early_return_expr = early_return_expr_opt.ok_or_else(|| "No early return found")?;
|
||||
|
||||
// 3. Check for step: i = i + 1
|
||||
// Phase 257 P0: Determine haystack based on scan direction
|
||||
let haystack = match scan_direction {
|
||||
ScanDirection::Forward => haystack_opt.ok_or_else(|| "Forward scan missing haystack in loop condition")?,
|
||||
ScanDirection::Reverse => haystack_from_substring_opt.ok_or_else(|| "Reverse scan missing haystack in substring call")?,
|
||||
};
|
||||
|
||||
// 3. Check for step: i = i + 1 (forward) or i = i - 1 (reverse)
|
||||
let mut step_lit_opt = None;
|
||||
|
||||
for stmt in body {
|
||||
@ -272,7 +330,7 @@ fn extract_scan_with_init_parts(
|
||||
if let ASTNode::Variable { name: target_name, .. } = target.as_ref() {
|
||||
if target_name == &loop_var {
|
||||
if let ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Add,
|
||||
operator,
|
||||
left,
|
||||
right,
|
||||
..
|
||||
@ -285,7 +343,15 @@ fn extract_scan_with_init_parts(
|
||||
..
|
||||
} = right.as_ref()
|
||||
{
|
||||
step_lit_opt = Some(*lit);
|
||||
match operator {
|
||||
BinaryOperator::Add => {
|
||||
step_lit_opt = Some(*lit);
|
||||
}
|
||||
BinaryOperator::Subtract => {
|
||||
step_lit_opt = Some(-lit);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -297,9 +363,18 @@ fn extract_scan_with_init_parts(
|
||||
|
||||
let step_lit = step_lit_opt.ok_or_else(|| "No step pattern found")?;
|
||||
|
||||
// P0: step must be 1
|
||||
if step_lit != 1 {
|
||||
return Ok(None);
|
||||
// Phase 257 P0: Verify step matches scan direction
|
||||
match scan_direction {
|
||||
ScanDirection::Forward => {
|
||||
if step_lit != 1 {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
ScanDirection::Reverse => {
|
||||
if step_lit != -1 {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. P0: not-found return must be -1 (hardcoded for now)
|
||||
@ -312,6 +387,7 @@ fn extract_scan_with_init_parts(
|
||||
step_lit,
|
||||
early_return_expr,
|
||||
not_found_return_lit,
|
||||
scan_direction,
|
||||
}))
|
||||
}
|
||||
|
||||
@ -352,7 +428,6 @@ impl MirBuilder {
|
||||
fn_body: Option<&[ASTNode]>,
|
||||
) -> Result<Option<ValueId>, String> {
|
||||
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
|
||||
use crate::mir::join_ir::lowering::scan_with_init_minimal::lower_scan_with_init_minimal;
|
||||
use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder;
|
||||
|
||||
let trace = trace::trace();
|
||||
@ -410,9 +485,18 @@ impl MirBuilder {
|
||||
);
|
||||
}
|
||||
|
||||
// Step 3: Create JoinModule
|
||||
// Step 3: Create JoinModule based on scan direction
|
||||
let mut join_value_space = JoinValueSpace::new();
|
||||
let join_module = lower_scan_with_init_minimal(&mut join_value_space);
|
||||
let join_module = match parts.scan_direction {
|
||||
ScanDirection::Forward => {
|
||||
use crate::mir::join_ir::lowering::scan_with_init_minimal::lower_scan_with_init_minimal;
|
||||
lower_scan_with_init_minimal(&mut join_value_space)
|
||||
}
|
||||
ScanDirection::Reverse => {
|
||||
use crate::mir::join_ir::lowering::scan_with_init_reverse::lower_scan_with_init_reverse;
|
||||
lower_scan_with_init_reverse(&mut join_value_space)
|
||||
}
|
||||
};
|
||||
|
||||
// Phase 255 P2: Build CarrierInfo for loop variable only
|
||||
// Step 1: Create CarrierInfo with loop variable (i) only
|
||||
|
||||
@ -77,6 +77,7 @@ pub mod loop_with_if_phi_if_sum; // Phase 213: Pattern 3 AST-based if-sum lowere
|
||||
pub mod min_loop;
|
||||
pub mod simple_while_minimal; // Phase 188-Impl-1: Pattern 1 minimal lowerer
|
||||
pub mod scan_with_init_minimal; // Phase 254 P1: Pattern 6 minimal lowerer (index_of/find/contains)
|
||||
pub mod scan_with_init_reverse; // Phase 257 P0: Pattern 6 reverse scan lowerer (last_index_of)
|
||||
pub mod split_scan_minimal; // Phase 256 P0: Pattern 7 minimal lowerer (split/tokenization with variable step)
|
||||
pub mod skip_ws;
|
||||
pub mod stage1_using_resolver;
|
||||
|
||||
320
src/mir/join_ir/lowering/scan_with_init_reverse.rs
Normal file
320
src/mir/join_ir/lowering/scan_with_init_reverse.rs
Normal file
@ -0,0 +1,320 @@
|
||||
//! Phase 257 P0: Pattern 6 Reverse Scan Lowerer (last_index_of)
|
||||
//!
|
||||
//! Target: apps/tests/phase257_p0_last_index_of_min.hako
|
||||
//!
|
||||
//! Code:
|
||||
//! ```nyash
|
||||
//! static box Main {
|
||||
//! main() {
|
||||
//! local s = "hello world"
|
||||
//! local ch = "o"
|
||||
//! local i = s.length() - 1
|
||||
//! loop(i >= 0) {
|
||||
//! if s.substring(i, i + 1) == ch {
|
||||
//! return i
|
||||
//! }
|
||||
//! i = i - 1
|
||||
//! }
|
||||
//! return -1
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Expected JoinIR:
|
||||
//! ```text
|
||||
//! fn main(i, ch, s):
|
||||
//! result = loop_step(i, ch, s)
|
||||
//!
|
||||
//! fn loop_step(i, ch, s):
|
||||
//! // 1. Check exit condition: i < 0
|
||||
//! exit_cond = (i < 0)
|
||||
//! Jump(k_exit, [-1], cond=exit_cond) // Not found case
|
||||
//!
|
||||
//! // 2. Calculate i_plus_1 for substring
|
||||
//! i_plus_1 = i + 1
|
||||
//!
|
||||
//! // 3. Hoist MethodCall(substring) to init-time BoxCall
|
||||
//! cur = StringBox.substring(s, i, i_plus_1)
|
||||
//!
|
||||
//! // 4. Check match condition
|
||||
//! match = (cur == ch)
|
||||
//! Jump(k_exit, [i], cond=match) // Found case
|
||||
//!
|
||||
//! // 5. Decrement and tail recurse
|
||||
//! i_minus_1 = i - 1
|
||||
//! Call(loop_step, [i_minus_1, ch, s])
|
||||
//!
|
||||
//! fn k_exit(i_exit):
|
||||
//! return i_exit
|
||||
//! ```
|
||||
|
||||
use crate::mir::join_ir::lowering::canonical_names as cn;
|
||||
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
|
||||
use crate::mir::join_ir::{
|
||||
BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule, MirLikeInst,
|
||||
};
|
||||
|
||||
/// Lower Pattern 6 Reverse Scan to JoinIR
|
||||
///
|
||||
/// # Phase 257 P0: Reverse scan (backward iteration)
|
||||
///
|
||||
/// This is a variant of scan_with_init_minimal that scans backward from i = s.length() - 1.
|
||||
///
|
||||
/// ## Key Differences from Forward Scan
|
||||
///
|
||||
/// - Exit condition: `i < 0` instead of `i >= len`
|
||||
/// - Step: `i = i - 1` instead of `i = i + 1`
|
||||
/// - Init: Caller must provide `i = s.length() - 1` before calling
|
||||
///
|
||||
/// ## Boundary Contract
|
||||
///
|
||||
/// This function returns a JoinModule with:
|
||||
/// - **Input slots**: main() params for (i, ch, s)
|
||||
/// - **Caller responsibility**: Ensure i is initialized to s.length() - 1
|
||||
/// - **Exit binding**: k_exit param receives found index or -1
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `join_value_space` - Unified ValueId allocator (Phase 202-A)
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `JoinModule` - Successfully lowered to JoinIR
|
||||
pub(crate) fn lower_scan_with_init_reverse(
|
||||
join_value_space: &mut JoinValueSpace,
|
||||
) -> JoinModule {
|
||||
let mut join_module = JoinModule::new();
|
||||
|
||||
// ==================================================================
|
||||
// Function IDs allocation
|
||||
// ==================================================================
|
||||
let main_id = JoinFuncId::new(0);
|
||||
let loop_step_id = JoinFuncId::new(1);
|
||||
let k_exit_id = JoinFuncId::new(2);
|
||||
|
||||
// ==================================================================
|
||||
// ValueId allocation
|
||||
// ==================================================================
|
||||
// main() params/locals
|
||||
// Phase 255 P0: Loop variable MUST be first, then alphabetical order [ch, s]
|
||||
let i_main_param = join_value_space.alloc_param(); // loop index
|
||||
let ch_main_param = join_value_space.alloc_param(); // needle character (alphabetically first)
|
||||
let s_main_param = join_value_space.alloc_param(); // haystack string (alphabetically second)
|
||||
let loop_result = join_value_space.alloc_local(); // result from loop_step
|
||||
|
||||
// loop_step params/locals
|
||||
let i_step_param = join_value_space.alloc_param(); // loop index
|
||||
let ch_step_param = join_value_space.alloc_param(); // needle (alphabetically first)
|
||||
let s_step_param = join_value_space.alloc_param(); // haystack (alphabetically second)
|
||||
let const_0 = join_value_space.alloc_local(); // 0 for exit condition
|
||||
let exit_cond = join_value_space.alloc_local(); // i < 0
|
||||
let const_minus_1 = join_value_space.alloc_local(); // -1 for not found
|
||||
let const_1 = join_value_space.alloc_local(); // 1 for i + 1
|
||||
let i_plus_1 = join_value_space.alloc_local(); // i + 1
|
||||
let cur = join_value_space.alloc_local(); // substring result
|
||||
let match_cond = join_value_space.alloc_local(); // cur == ch
|
||||
let i_minus_1 = join_value_space.alloc_local(); // i - 1
|
||||
|
||||
// k_exit params
|
||||
let i_exit_param = join_value_space.alloc_param(); // exit parameter (index or -1)
|
||||
|
||||
// ==================================================================
|
||||
// main() function
|
||||
// ==================================================================
|
||||
let mut main_func = JoinFunction::new(
|
||||
main_id,
|
||||
"main".to_string(),
|
||||
vec![i_main_param, ch_main_param, s_main_param], // Phase 255 P0: [i, ch, s] alphabetical
|
||||
);
|
||||
|
||||
// result = loop_step(i, ch, s) // Phase 255 P0: alphabetical order
|
||||
main_func.body.push(JoinInst::Call {
|
||||
func: loop_step_id,
|
||||
args: vec![i_main_param, ch_main_param, s_main_param], // Phase 255 P0: [i, ch, s] alphabetical
|
||||
k_next: None,
|
||||
dst: Some(loop_result),
|
||||
});
|
||||
|
||||
// Return loop_result (found index or -1)
|
||||
main_func.body.push(JoinInst::Ret { value: Some(loop_result) });
|
||||
|
||||
join_module.add_function(main_func);
|
||||
|
||||
// ==================================================================
|
||||
// loop_step(i, ch, s) function
|
||||
// ==================================================================
|
||||
let mut loop_step_func = JoinFunction::new(
|
||||
loop_step_id,
|
||||
cn::LOOP_STEP.to_string(),
|
||||
vec![i_step_param, ch_step_param, s_step_param], // Phase 255 P0: [i, ch, s] alphabetical
|
||||
);
|
||||
|
||||
// 1. const 0
|
||||
loop_step_func
|
||||
.body
|
||||
.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: const_0,
|
||||
value: ConstValue::Integer(0),
|
||||
}));
|
||||
|
||||
// 2. exit_cond = (i < 0)
|
||||
loop_step_func
|
||||
.body
|
||||
.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||
dst: exit_cond,
|
||||
op: CompareOp::Lt,
|
||||
lhs: i_step_param,
|
||||
rhs: const_0,
|
||||
}));
|
||||
|
||||
// 3. const -1
|
||||
loop_step_func
|
||||
.body
|
||||
.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: const_minus_1,
|
||||
value: ConstValue::Integer(-1),
|
||||
}));
|
||||
|
||||
// 4. Jump(k_exit, [-1], cond=exit_cond) - not found case
|
||||
loop_step_func.body.push(JoinInst::Jump {
|
||||
cont: k_exit_id.as_cont(),
|
||||
args: vec![const_minus_1],
|
||||
cond: Some(exit_cond),
|
||||
});
|
||||
|
||||
// 5. i_plus_1 = i + 1
|
||||
loop_step_func
|
||||
.body
|
||||
.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: const_1,
|
||||
value: ConstValue::Integer(1),
|
||||
}));
|
||||
|
||||
loop_step_func
|
||||
.body
|
||||
.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||
dst: i_plus_1,
|
||||
op: BinOpKind::Add,
|
||||
lhs: i_step_param,
|
||||
rhs: const_1,
|
||||
}));
|
||||
|
||||
// 6. cur = s.substring(i, i_plus_1) - init-time BoxCall
|
||||
loop_step_func
|
||||
.body
|
||||
.push(JoinInst::Compute(MirLikeInst::BoxCall {
|
||||
dst: Some(cur),
|
||||
box_name: "StringBox".to_string(),
|
||||
method: "substring".to_string(),
|
||||
args: vec![s_step_param, i_step_param, i_plus_1],
|
||||
}));
|
||||
|
||||
// 7. match_cond = (cur == ch)
|
||||
loop_step_func
|
||||
.body
|
||||
.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||
dst: match_cond,
|
||||
op: CompareOp::Eq,
|
||||
lhs: cur,
|
||||
rhs: ch_step_param,
|
||||
}));
|
||||
|
||||
// 8. Jump(k_exit, [i], cond=match_cond) - found case
|
||||
loop_step_func.body.push(JoinInst::Jump {
|
||||
cont: k_exit_id.as_cont(),
|
||||
args: vec![i_step_param],
|
||||
cond: Some(match_cond),
|
||||
});
|
||||
|
||||
// 9. i_minus_1 = i - 1
|
||||
loop_step_func
|
||||
.body
|
||||
.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||
dst: i_minus_1,
|
||||
op: BinOpKind::Sub,
|
||||
lhs: i_step_param,
|
||||
rhs: const_1,
|
||||
}));
|
||||
|
||||
// 10. Call(loop_step, [i_minus_1, ch, s]) - tail recursion
|
||||
loop_step_func.body.push(JoinInst::Call {
|
||||
func: loop_step_id,
|
||||
args: vec![i_minus_1, ch_step_param, s_step_param], // Phase 255 P0: [i_minus_1, ch, s] alphabetical
|
||||
k_next: None, // CRITICAL: None for tail call
|
||||
dst: None,
|
||||
});
|
||||
|
||||
join_module.add_function(loop_step_func);
|
||||
|
||||
// ==================================================================
|
||||
// k_exit(i_exit) function
|
||||
// ==================================================================
|
||||
let mut k_exit_func = JoinFunction::new(k_exit_id, cn::K_EXIT.to_string(), vec![i_exit_param]);
|
||||
|
||||
// Return i_exit (found index or -1)
|
||||
k_exit_func.body.push(JoinInst::Ret {
|
||||
value: Some(i_exit_param),
|
||||
});
|
||||
|
||||
join_module.add_function(k_exit_func);
|
||||
|
||||
// Set entry point
|
||||
join_module.entry = Some(main_id);
|
||||
|
||||
eprintln!("[joinir/pattern6] Generated JoinIR for ScanWithInit Reverse Pattern");
|
||||
eprintln!("[joinir/pattern6] Functions: main, loop_step, k_exit");
|
||||
eprintln!("[joinir/pattern6] Direction: Reverse (i >= 0, i = i - 1)");
|
||||
|
||||
join_module
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_lower_scan_with_init_reverse() {
|
||||
let mut join_value_space = JoinValueSpace::new();
|
||||
|
||||
let join_module = lower_scan_with_init_reverse(&mut join_value_space);
|
||||
|
||||
// main + loop_step + k_exit の3関数
|
||||
assert_eq!(join_module.functions.len(), 3);
|
||||
|
||||
// Entry が main(0) に設定されている
|
||||
assert_eq!(join_module.entry, Some(JoinFuncId::new(0)));
|
||||
|
||||
// k_exit 関数が取れる
|
||||
let k_exit_func = join_module
|
||||
.functions
|
||||
.get(&JoinFuncId::new(2))
|
||||
.expect("k_exit function should exist");
|
||||
assert_eq!(k_exit_func.name, cn::K_EXIT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_loop_step_has_reverse_step() {
|
||||
let mut join_value_space = JoinValueSpace::new();
|
||||
|
||||
let join_module = lower_scan_with_init_reverse(&mut join_value_space);
|
||||
|
||||
// loop_step 関数を取得
|
||||
let loop_step = join_module
|
||||
.functions
|
||||
.get(&JoinFuncId::new(1))
|
||||
.expect("loop_step function should exist");
|
||||
|
||||
// BinOp(Sub) (i - 1) が含まれることを確認
|
||||
let has_decrement = loop_step.body.iter().any(|inst| {
|
||||
matches!(
|
||||
inst,
|
||||
JoinInst::Compute(MirLikeInst::BinOp { op: BinOpKind::Sub, .. })
|
||||
)
|
||||
});
|
||||
|
||||
assert!(
|
||||
has_decrement,
|
||||
"loop_step should contain i - 1 decrement"
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user