use crate::instruction::{Instruction, Operand};
use super::super::super::{HighLevelEmitter, LoopContext};
impl HighLevelEmitter {
pub(in super::super::super) fn emit_comparison_if_block(
&mut self,
instruction: &Instruction,
symbol: &str,
) {
let delta = match instruction.operand {
Some(Operand::Jump(value)) => value as isize,
Some(Operand::Jump32(value)) => value as isize,
_ => {
self.emit_relative(instruction, &format!("jump-if-{symbol}"));
return;
}
};
let target = instruction.offset as isize + delta;
if target <= instruction.offset as isize {
self.emit_relative(instruction, &format!("jump-if-{symbol}"));
return;
}
if self.stack.len() < 2 {
self.push_comment(instruction);
self.stack_underflow(instruction, 2);
return;
}
let (Some(right), Some(left)) = (self.stack.pop(), self.stack.pop()) else {
return;
};
let condition = format!("{left} {symbol} {right}");
let false_target = target as usize;
if self.has_crossing_closer(false_target)
|| self.has_internal_crossing_branch(instruction.offset, false_target)
{
self.emit_conditional_goto(instruction, &format!("!({condition})"), false_target);
return;
}
self.begin_branch_body(instruction, &format!("if {condition} {{"), false_target);
self.register_else_for_branch(instruction.offset, false_target);
}
pub(in super::super::super) fn emit_if_block(&mut self, instruction: &Instruction) {
self.emit_unary_if_block(instruction, false, "jump-ifnot");
}
pub(in super::super::super) fn emit_jmpif_block(&mut self, instruction: &Instruction) {
self.emit_unary_if_block(instruction, true, "jump-if");
}
fn emit_unary_if_block(
&mut self,
instruction: &Instruction,
negate_condition: bool,
fallback_label: &str,
) {
let delta = match instruction.operand {
Some(Operand::Jump(value)) => value as isize,
Some(Operand::Jump32(value)) => value as isize,
_ => {
self.emit_relative(instruction, fallback_label);
return;
}
};
let target = instruction.offset as isize + delta;
if target <= instruction.offset as isize {
self.emit_relative(instruction, fallback_label);
return;
}
let raw_condition = match self.stack.pop() {
Some(value) => value,
None => {
self.push_comment(instruction);
self.stack_underflow(instruction, 1);
return;
}
};
let condition = if negate_condition {
format!("!{raw_condition}")
} else {
raw_condition.clone()
};
let false_target = target as usize;
if self.has_crossing_closer(false_target)
|| self.has_internal_crossing_branch(instruction.offset, false_target)
{
let jump_condition = if negate_condition {
raw_condition
} else {
format!("!{raw_condition}")
};
self.emit_conditional_goto(instruction, &jump_condition, false_target);
return;
}
let loop_jump = self.detect_loop_back(false_target, instruction.offset);
if let Some(loop_jump) = loop_jump.as_ref() {
self.begin_branch_body(instruction, &format!("while {condition} {{"), false_target);
self.skip_jumps.insert(loop_jump.jump_offset);
self.loop_stack.push(LoopContext {
break_offset: false_target,
continue_offset: loop_jump.target,
});
} else {
self.begin_branch_body(instruction, &format!("if {condition} {{"), false_target);
self.register_else_for_branch(instruction.offset, false_target);
}
}
fn begin_branch_body(&mut self, instruction: &Instruction, header: &str, false_target: usize) {
self.push_comment(instruction);
self.statements.push(header.to_string());
self.branch_saved_stacks
.entry(false_target)
.or_insert_with(|| self.stack.clone());
let closer_entry = self.pending_closers.entry(false_target).or_insert(0);
*closer_entry += 1;
}
fn emit_conditional_goto(&mut self, instruction: &Instruction, condition: &str, target: usize) {
self.push_comment(instruction);
if self.index_by_offset.contains_key(&target) {
self.transfer_labels.insert(target);
}
self.statements.push(format!(
"if {condition} {{ goto {}; }}",
Self::transfer_label_name(target)
));
}
fn register_else_for_branch(&mut self, branch_offset: usize, false_target: usize) {
if let Some((jump_offset, jump_target)) = self.detect_else(branch_offset, false_target) {
if !self.is_loop_control_target(jump_target)
&& !self.else_targets.contains_key(&false_target)
{
self.skip_jumps.insert(jump_offset);
let else_entry = self.else_targets.entry(false_target).or_insert(0);
*else_entry += 1;
let closer = self.pending_closers.entry(jump_target).or_insert(0);
*closer += 1;
self.pre_branch_stack_depth
.entry(jump_target)
.or_insert(self.stack.len());
}
} else if let Some(else_end) = self.detect_implicit_else(branch_offset, false_target) {
if !self.else_targets.contains_key(&false_target) {
let else_entry = self.else_targets.entry(false_target).or_insert(0);
*else_entry += 1;
let closer = self.pending_closers.entry(else_end).or_insert(0);
*closer += 1;
}
}
}
}