Files
hakorune/src/mir/builder/exprs_peek.rs

138 lines
5.3 KiB
Rust
Raw Normal View History

use super::{BasicBlockId, ValueId};
use crate::ast::{ASTNode, LiteralValue};
impl super::MirBuilder {
// Peek expression lowering
pub(super) fn build_peek_expression(
&mut self,
scrutinee: ASTNode,
arms: Vec<(LiteralValue, ASTNode)>,
else_expr: ASTNode,
) -> Result<ValueId, String> {
// Evaluate scrutinee in the current block
let scr_val = self.build_expression_impl(scrutinee)?;
// Prepare merge and result
let merge_block: BasicBlockId = self.block_gen.next();
let result_val = self.next_value_id();
let mut phi_inputs: Vec<(BasicBlockId, ValueId)> = Vec::new();
// Create dispatch block where we start comparing arms
let dispatch_block = self.block_gen.next();
// Jump from current block to dispatch (ensure terminator exists)
let need_jump = {
let cur = self.current_block;
if let (Some(cb), Some(ref func)) = (cur, &self.current_function) {
if let Some(bb) = func.blocks.get(&cb) {
!bb.is_terminated()
} else {
true
}
} else {
true
}
};
if need_jump {
crate::mir::builder::emission::branch::emit_jump(self, dispatch_block)?;
}
self.start_new_block(dispatch_block)?;
// If there are no arms, fall through to else directly
if arms.is_empty() {
let else_block = self.block_gen.next();
crate::mir::builder::emission::branch::emit_jump(self, else_block)?;
self.start_new_block(else_block)?;
let else_val = self.build_expression_impl(else_expr)?;
phi_inputs.push((else_block, else_val));
crate::mir::builder::emission::branch::emit_jump(self, merge_block)?;
self.start_new_block(merge_block)?;
// フェーズM: PHI はブロック先頭に配置cf_common 統一)
refactor: unify PHI insertion patterns (Phase 4) - Add PHI insertion helper utilities in mir/utils/phi_helpers.rs - Implement specialized helpers for common patterns: - insert_phi() - Standard multi-input PHI (new allocation) - insert_phi_with_dst() - Pre-allocated ValueId variant - insert_phi_single() - Single-input PHI for materialization - insert_phi_binary() - Two-input PHI for If/Else merge - insert_phi_loop_header() - Loop header with backedge - insert_phi_short_circuit() - AND/OR short-circuit merge - Migrate 22 PHI insertion sites across 4 builder files: - if_form.rs: 2 sites (-12 lines, 86% reduction) - ops.rs: 5 sites (-32 lines, 86% reduction) - phi.rs: 4 sites (-13 lines, 81% reduction) - exprs_peek.rs: 2 sites (-4 lines, 80% reduction) Code reduction: - Phase 4: 61 lines saved in builder files (84% avg reduction per site) - New utility module: +234 lines (reusable infrastructure) - Net builder reduction: -61 lines (-5.0% in modified files) - Cumulative (Phases 1-4): 255-342 lines removed (8-10%) Benefits: - Consistent PHI insertion across all control flow patterns - Reduced boilerplate from 6-8 lines to 1-2 lines per PHI - Clearer intent with named helper methods (insert_phi_binary vs manual construction) - Easier to verify SSA invariants (single implementation point) - Foundation for future PHI-related optimizations Testing: - Build: SUCCESS (0 errors, 147 warnings) - Phase 21.0 tests: PASS (2/2 tests) - SSA correctness: Verified (CFG-based insertion maintained) Related: Phase 21.0 refactoring, MIR SSA construction Risk: Low (wraps existing insert_phi_at_head, fully tested)
2025-11-06 23:57:24 +09:00
self.insert_phi_with_dst(result_val, phi_inputs)?;
return Ok(result_val);
}
// Else block to handle default case
let else_block = self.block_gen.next();
// Chain dispatch blocks for each arm
let mut cur_dispatch = dispatch_block;
for (i, (label, arm_expr)) in arms.iter().cloned().enumerate() {
let then_block = self.block_gen.next();
// Next dispatch (only for non-last arm)
let next_dispatch = if i + 1 < arms.len() {
Some(self.block_gen.next())
} else {
None
};
let else_target = next_dispatch.unwrap_or(else_block);
// In current dispatch block, compare and branch
self.start_new_block(cur_dispatch)?;
let lit_id = match label {
LiteralValue::String(s) => {
crate::mir::builder::emission::constant::emit_string(self, s)
}
LiteralValue::Integer(i) => {
crate::mir::builder::emission::constant::emit_integer(self, i)
}
LiteralValue::Bool(b) => {
crate::mir::builder::emission::constant::emit_bool(self, b)
}
LiteralValue::Float(f) => {
crate::mir::builder::emission::constant::emit_float(self, f)
}
LiteralValue::Null => crate::mir::builder::emission::constant::emit_null(self),
LiteralValue::Void => crate::mir::builder::emission::constant::emit_void(self),
};
let cond_id = self.next_value_id();
crate::mir::builder::emission::compare::emit_to(
self,
cond_id,
super::CompareOp::Eq,
scr_val,
lit_id,
)?;
crate::mir::builder::emission::branch::emit_conditional(
self,
cond_id,
then_block,
else_target,
)?;
// then arm
self.start_new_block(then_block)?;
let then_val = self.build_expression_impl(arm_expr)?;
phi_inputs.push((then_block, then_val));
crate::mir::builder::emission::branch::emit_jump(self, merge_block)?;
// Move to next dispatch or else block
cur_dispatch = else_target;
}
// Lower else expression in else_block
self.start_new_block(else_block)?;
let else_val = self.build_expression_impl(else_expr)?;
phi_inputs.push((else_block, else_val));
crate::mir::builder::emission::branch::emit_jump(self, merge_block)?;
// Merge and yield result
self.start_new_block(merge_block)?;
// フェーズM: PHI はブロック先頭に配置cf_common 統一)
if let (Some(func), Some(cur_bb)) = (self.current_function.as_mut(), self.current_block) {
crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
func,
cur_bb,
result_val,
phi_inputs,
self.current_span,
);
} else {
self.emit_instruction(super::MirInstruction::Phi {
dst: result_val,
inputs: phi_inputs,
})?;
}
Ok(result_val)
}
}