use super::{CodeGenContext, MacroAssembler, OperandSize};
use crate::{
abi::{ABIResult, ABI},
masm::CmpKind,
CallingConvention,
};
use cranelift_codegen::MachLabel;
use wasmtime_environ::WasmType;
#[derive(Debug)]
pub(crate) enum ControlStackFrame {
If {
cont: MachLabel,
exit: MachLabel,
result: ABIResult,
original_stack_len: usize,
original_sp_offset: u32,
reachable: bool,
},
Else {
exit: MachLabel,
result: ABIResult,
original_stack_len: usize,
original_sp_offset: u32,
reachable: bool,
},
Block {
exit: MachLabel,
original_stack_len: usize,
result: ABIResult,
original_sp_offset: u32,
is_branch_target: bool,
},
Loop {
head: MachLabel,
original_stack_len: usize,
original_sp_offset: u32,
result: ABIResult,
},
}
impl ControlStackFrame {
pub fn if_<M: MacroAssembler>(
returns: &[WasmType],
masm: &mut M,
context: &mut CodeGenContext,
) -> Self {
let result = <M::ABI as ABI>::result(&returns, &CallingConvention::Default);
let mut control = Self::If {
cont: masm.get_label(),
exit: masm.get_label(),
result,
reachable: context.reachable,
original_stack_len: 0,
original_sp_offset: 0,
};
control.emit(masm, context);
control
}
pub fn function_body_block<M: MacroAssembler>(
result: ABIResult,
masm: &mut M,
context: &mut CodeGenContext,
) -> Self {
Self::Block {
original_stack_len: context.stack.len(),
result,
is_branch_target: false,
exit: masm.get_label(),
original_sp_offset: masm.sp_offset(),
}
}
pub fn block<M: MacroAssembler>(
returns: &[WasmType],
masm: &mut M,
context: &mut CodeGenContext,
) -> Self {
let result = <M::ABI as ABI>::result(&returns, &CallingConvention::Default);
let mut control = Self::Block {
original_stack_len: 0,
result,
is_branch_target: false,
exit: masm.get_label(),
original_sp_offset: 0,
};
control.emit(masm, context);
control
}
pub fn loop_<M: MacroAssembler>(
returns: &[WasmType],
masm: &mut M,
context: &mut CodeGenContext,
) -> Self {
let result = <M::ABI as ABI>::result(&returns, &CallingConvention::Default);
let mut control = Self::Loop {
original_stack_len: 0,
result,
head: masm.get_label(),
original_sp_offset: 0,
};
control.emit(masm, context);
control
}
fn emit<M: MacroAssembler>(&mut self, masm: &mut M, context: &mut CodeGenContext) {
use ControlStackFrame::*;
if !context.reachable {
return;
}
match self {
If {
cont,
original_stack_len,
original_sp_offset,
..
} => {
let top = context.pop_to_reg(masm, None, OperandSize::S32);
context.spill(masm);
*original_stack_len = context.stack.len();
*original_sp_offset = masm.sp_offset();
masm.branch(CmpKind::Eq, top.into(), top.into(), *cont, OperandSize::S32);
context.free_gpr(top);
}
Block {
original_stack_len,
original_sp_offset,
..
} => {
context.spill(masm);
*original_stack_len = context.stack.len();
*original_sp_offset = masm.sp_offset();
}
Loop {
original_stack_len,
original_sp_offset,
head,
..
} => {
context.spill(masm);
*original_stack_len = context.stack.len();
*original_sp_offset = masm.sp_offset();
masm.bind(*head);
}
_ => unreachable!(),
}
}
pub fn emit_else<M: MacroAssembler>(&mut self, masm: &mut M, context: &mut CodeGenContext) {
use ControlStackFrame::*;
match self {
If {
result,
original_stack_len,
exit,
..
} => {
assert!((*original_stack_len + result.len()) == context.stack.len());
context.pop_abi_results(&result, 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,
result,
original_stack_len,
original_sp_offset,
exit,
..
} => {
masm.bind(*cont);
*self = ControlStackFrame::Else {
exit: *exit,
original_stack_len: *original_stack_len,
result: *result,
reachable,
original_sp_offset: *original_sp_offset,
};
}
_ => unreachable!(),
}
}
pub fn emit_end<M: MacroAssembler>(&mut self, masm: &mut M, context: &mut CodeGenContext) {
use ControlStackFrame::*;
match self {
If {
result,
original_stack_len,
..
}
| Else {
result,
original_stack_len,
..
} => {
assert!((*original_stack_len + result.len()) == context.stack.len());
context.pop_abi_results(&result, masm);
self.bind_end(masm, context);
}
Block {
original_stack_len,
result,
..
} => {
assert!((*original_stack_len + result.len()) == context.stack.len());
context.pop_abi_results(&result, masm);
self.bind_end(masm, context);
}
Loop {
result,
original_stack_len,
..
} => {
assert!((*original_stack_len + result.len()) == context.stack.len());
}
}
}
pub fn bind_end<M: MacroAssembler>(&self, masm: &mut M, context: &mut CodeGenContext) {
context.push_abi_results(self.result(), 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 result(&self) -> &ABIResult {
use ControlStackFrame::*;
match self {
If { result, .. }
| Else { result, .. }
| Block { result, .. }
| Loop { result, .. } => result,
}
}
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 original_stack_len_and_sp_offset(&self) -> (usize, u32) {
use ControlStackFrame::*;
match self {
If {
original_stack_len,
original_sp_offset,
..
}
| Else {
original_stack_len,
original_sp_offset,
..
}
| Block {
original_stack_len,
original_sp_offset,
..
}
| Loop {
original_stack_len,
original_sp_offset,
..
} => (*original_stack_len, *original_sp_offset),
}
}
}