use super::{CodeGenContext, MacroAssembler, OperandSize};
use crate::{
abi::ABIResultsData,
codegen::env::BlockTypeInfo,
masm::{IntCmpKind, SPOffset},
};
use cranelift_codegen::MachLabel;
#[derive(Debug)]
pub(crate) enum ControlStackFrame {
If {
cont: MachLabel,
exit: MachLabel,
results_data: ABIResultsData,
block_type_info: BlockTypeInfo,
base_stack_len: usize,
base_sp: SPOffset,
reachable: bool,
},
Else {
exit: MachLabel,
results_data: ABIResultsData,
block_type_info: BlockTypeInfo,
base_stack_len: usize,
base_sp: SPOffset,
reachable: bool,
},
Block {
exit: MachLabel,
base_stack_len: usize,
results_data: ABIResultsData,
block_type_info: BlockTypeInfo,
base_sp: SPOffset,
is_branch_target: bool,
},
Loop {
head: MachLabel,
base_stack_len: usize,
base_sp: SPOffset,
block_type_info: BlockTypeInfo,
},
}
impl ControlStackFrame {
pub fn r#if<M: MacroAssembler>(
results_data: ABIResultsData,
block_type_info: BlockTypeInfo,
masm: &mut M,
context: &mut CodeGenContext,
) -> Self {
let mut control = Self::If {
cont: masm.get_label(),
exit: masm.get_label(),
results_data,
block_type_info,
reachable: context.reachable,
base_stack_len: 0,
base_sp: SPOffset::from_u32(0),
};
control.emit(masm, context);
control
}
pub fn function_body_block<M: MacroAssembler>(
results_data: ABIResultsData,
block_type_info: BlockTypeInfo,
masm: &mut M,
context: &mut CodeGenContext,
) -> Self {
Self::Block {
base_stack_len: context.stack.len(),
results_data,
block_type_info,
is_branch_target: false,
exit: masm.get_label(),
base_sp: masm.sp_offset(),
}
}
pub fn block<M: MacroAssembler>(
results_data: ABIResultsData,
block_type_info: BlockTypeInfo,
masm: &mut M,
context: &mut CodeGenContext,
) -> Self {
let mut control = Self::Block {
base_stack_len: 0,
results_data,
block_type_info,
is_branch_target: false,
exit: masm.get_label(),
base_sp: SPOffset::from_u32(0),
};
control.emit(masm, context);
control
}
pub fn r#loop<M: MacroAssembler>(
block_type_info: BlockTypeInfo,
masm: &mut M,
context: &mut CodeGenContext,
) -> Self {
let mut control = Self::Loop {
base_stack_len: 0,
block_type_info,
head: masm.get_label(),
base_sp: SPOffset::from_u32(0),
};
control.emit(masm, context);
control
}
fn init<M: MacroAssembler>(&mut self, masm: &mut M, context: &mut CodeGenContext) {
assert!(self.block_type_info().param_count == 0);
assert!(self.block_type_info().result_count < 2);
context.spill(masm);
self.set_base_stack_len(context.stack.len());
self.set_base_sp(masm.sp_offset());
}
fn set_base_stack_len(&mut self, len: usize) {
use ControlStackFrame::*;
match self {
If { base_stack_len, .. }
| Block { base_stack_len, .. }
| Loop { base_stack_len, .. } => *base_stack_len = len,
_ => {}
}
}
fn set_base_sp(&mut self, base: SPOffset) {
use ControlStackFrame::*;
match self {
If { base_sp, .. } | Block { base_sp, .. } | Loop { base_sp, .. } => *base_sp = base,
_ => {}
}
}
fn block_type_info(&mut self) -> &BlockTypeInfo {
use ControlStackFrame::*;
match self {
If {
block_type_info, ..
}
| Else {
block_type_info, ..
}
| Loop {
block_type_info, ..
}
| Block {
block_type_info, ..
} => block_type_info,
}
}
fn emit<M: MacroAssembler>(&mut self, masm: &mut M, context: &mut CodeGenContext) {
use ControlStackFrame::*;
if !context.reachable {
return;
}
match *self {
If { cont, .. } => {
let top = context.pop_to_reg(masm, None);
self.init(masm, context);
masm.branch(
IntCmpKind::Eq,
top.reg.into(),
top.reg.into(),
cont,
OperandSize::S32,
);
context.free_reg(top);
}
Block { .. } => self.init(masm, context),
Loop { head, .. } => {
self.init(masm, context);
masm.bind(head);
}
_ => unreachable!(),
}
}
pub fn emit_else<M: MacroAssembler>(&mut self, masm: &mut M, context: &mut CodeGenContext) {
use ControlStackFrame::*;
match self {
If {
results_data,
base_stack_len,
exit,
block_type_info,
..
} => {
assert!(
(*base_stack_len + block_type_info.result_count - block_type_info.param_count)
== context.stack.len()
);
context.pop_abi_results(results_data, masm);
masm.jmp(*exit);
self.bind_else(masm, context.reachable);
}
_ => unreachable!(),
}
}
pub fn bind_else<M: MacroAssembler>(&mut self, masm: &mut M, reachable: bool) {
use ControlStackFrame::*;
match self {
If {
cont,
results_data,
block_type_info,
base_stack_len,
base_sp,
exit,
..
} => {
masm.bind(*cont);
*self = ControlStackFrame::Else {
exit: *exit,
base_stack_len: *base_stack_len,
reachable,
base_sp: *base_sp,
results_data: results_data.clone(),
block_type_info: *block_type_info,
};
}
_ => unreachable!(),
}
}
pub fn emit_end<M: MacroAssembler>(&mut self, masm: &mut M, context: &mut CodeGenContext) {
use ControlStackFrame::*;
match self {
If {
results_data,
base_stack_len,
block_type_info,
..
}
| Else {
results_data,
base_stack_len,
block_type_info,
..
}
| Block {
results_data,
base_stack_len,
block_type_info,
..
} => {
assert!(
(*base_stack_len + block_type_info.result_count - block_type_info.param_count)
== context.stack.len()
);
context.pop_abi_results(results_data, masm);
self.bind_end(masm, context);
}
Loop {
block_type_info,
base_stack_len,
..
} => {
assert!(
(*base_stack_len + block_type_info.result_count - block_type_info.param_count)
== context.stack.len()
);
}
}
}
pub fn bind_end<M: MacroAssembler>(&self, masm: &mut M, context: &mut CodeGenContext) {
if let Some(data) = self.results() {
context.push_abi_results(data, masm);
}
self.bind_exit_label(masm);
}
pub fn bind_exit_label<M: MacroAssembler>(&self, masm: &mut M) {
use ControlStackFrame::*;
match self {
If { cont, .. } => masm.bind(*cont),
_ => {}
}
if let Some(label) = self.exit_label() {
masm.bind(*label);
}
}
pub fn label(&self) -> &MachLabel {
use ControlStackFrame::*;
match self {
If { exit, .. } | Else { exit, .. } | Block { exit, .. } => exit,
Loop { head, .. } => head,
}
}
pub fn exit_label(&self) -> Option<&MachLabel> {
use ControlStackFrame::*;
match self {
If { exit, .. } | Else { exit, .. } | Block { exit, .. } => Some(exit),
Loop { .. } => None,
}
}
pub fn set_as_target(&mut self) {
match self {
ControlStackFrame::Block {
is_branch_target, ..
} => {
*is_branch_target = true;
}
_ => {}
}
}
pub fn results(&self) -> Option<&ABIResultsData> {
use ControlStackFrame::*;
match self {
If { results_data, .. } | Else { results_data, .. } | Block { results_data, .. } => {
Some(results_data)
}
Loop { .. } => None,
}
}
pub fn is_next_sequence_reachable(&self) -> bool {
use ControlStackFrame::*;
match self {
If { reachable, .. } | Else { reachable, .. } => *reachable,
Block {
is_branch_target, ..
} => *is_branch_target,
Loop { .. } => false,
}
}
pub fn base_stack_len_and_sp(&self) -> (usize, SPOffset) {
use ControlStackFrame::*;
match self {
If {
base_sp,
base_stack_len,
..
}
| Else {
base_sp,
base_stack_len,
..
}
| Block {
base_sp,
base_stack_len,
..
}
| Loop {
base_sp,
base_stack_len,
..
} => (*base_stack_len, *base_sp),
}
}
pub fn as_target_results(&self) -> Option<&ABIResultsData> {
self.results()
}
pub fn is_if(&self) -> bool {
match self {
Self::If { .. } => true,
_ => false,
}
}
}