Files
wasmtime/src/function_body.rs

241 lines
8.5 KiB
Rust

use backend::*;
use error::Error;
use module::TranslationContext;
use wasmparser::{FunctionBody, Operator, Type};
// TODO: Use own declared `Type` enum.
/// Type of a control frame.
#[derive(Debug, Copy, Clone, PartialEq)]
enum ControlFrameKind {
/// A regular block frame.
///
/// Can be used for an implicit function block.
Block { end_label: Label },
/// Loop frame (branching to the beginning of block).
#[allow(unused)]
Loop { header: Label },
/// True-subblock of if expression.
IfTrue {
/// If jump happens inside the if-true block then control will
/// land on this label.
end_label: Label,
/// If the condition of the `if` statement is unsatisfied, control
/// will land on this label. This label might point to `else` block if it
/// exists. Otherwise it equal to `end_label`.
if_not: Label,
},
/// False-subblock of if expression.
IfFalse { end_label: Label },
}
impl ControlFrameKind {
/// Returns a label which should be used as a branch destination.
fn br_destination(&self) -> Label {
match *self {
ControlFrameKind::Block { end_label } => end_label,
ControlFrameKind::Loop { header } => header,
ControlFrameKind::IfTrue { end_label, .. } => end_label,
ControlFrameKind::IfFalse { end_label } => end_label,
}
}
/// Returns `true` if this block of a loop kind.
fn is_loop(&self) -> bool {
match *self {
ControlFrameKind::Loop { .. } => true,
_ => false,
}
}
}
struct ControlFrame {
kind: ControlFrameKind,
/// Boolean which signals whether value stack became polymorphic. Value stack starts in non-polymorphic state and
/// becomes polymorphic only after an instruction that never passes control further is executed,
/// i.e. `unreachable`, `br` (but not `br_if`!), etc.
stack_polymorphic: bool,
/// Relative stack depth at the beginning of the frame.
stack_depth: StackDepth,
ty: Type,
}
impl ControlFrame {
pub fn new(kind: ControlFrameKind, stack_depth: StackDepth, ty: Type) -> ControlFrame {
ControlFrame {
kind,
stack_depth,
ty,
stack_polymorphic: false,
}
}
pub fn outgoing_stack_depth(&self) -> StackDepth {
let mut outgoing_stack_depth = self.stack_depth;
if self.ty != Type::EmptyBlockType {
// If there a return value then reserve expected outgoing stack depth value
// to account for the result value.
outgoing_stack_depth.reserve(1);
}
outgoing_stack_depth
}
/// Marks this control frame as reached stack-polymorphic state.
pub fn mark_stack_polymorphic(&mut self) {
self.stack_polymorphic = true;
}
}
pub fn translate(
session: &mut CodeGenSession,
translation_ctx: &TranslationContext,
func_idx: u32,
body: &FunctionBody,
) -> Result<(), Error> {
let locals = body.get_locals_reader()?;
let func_type = translation_ctx.func_type(func_idx);
let arg_count = func_type.params.len() as u32;
let return_ty = if func_type.returns.len() > 0 {
func_type.returns[0]
} else {
Type::EmptyBlockType
};
let mut framesize = arg_count;
for local in locals {
let (count, _ty) = local?;
framesize += count;
}
let mut ctx = session.new_context(func_idx);
let operators = body.get_operators_reader()?;
prologue(&mut ctx, framesize);
for arg_pos in 0..arg_count {
copy_incoming_arg(&mut ctx, framesize, arg_pos);
}
let mut control_frames = Vec::new();
// Upon entering the function implicit frame for function body is pushed. It has the same
// result type as the function itself. Branching to it is equivalent to returning from the function.
let epilogue_label = create_label(&mut ctx);
control_frames.push(ControlFrame::new(
ControlFrameKind::Block {
end_label: epilogue_label,
},
current_stack_depth(&ctx),
return_ty,
));
for op in operators {
match op? {
Operator::Unreachable => {
control_frames
.last_mut()
.expect("control stack is never empty")
.mark_stack_polymorphic();
trap(&mut ctx);
}
Operator::If { ty } => {
let end_label = create_label(&mut ctx);
let if_not = create_label(&mut ctx);
pop_and_breq(&mut ctx, if_not);
control_frames.push(ControlFrame::new(
ControlFrameKind::IfTrue { end_label, if_not },
current_stack_depth(&ctx),
ty,
));
}
Operator::Else => {
match control_frames.pop() {
Some(ControlFrame {
kind: ControlFrameKind::IfTrue { if_not, end_label },
ty,
stack_depth,
..
}) => {
// Finalize if..else block by jumping to the `end_label`.
br(&mut ctx, end_label);
// Define `if_not` label here, so if the corresponding `if` block receives
// 0 it will branch here.
// After that reset stack depth to the value before entering `if` block.
define_label(&mut ctx, if_not);
restore_stack_depth(&mut ctx, stack_depth);
// Carry over the `end_label`, so it will be resolved when the corresponding `end`
// is encountered.
//
// Also note that we reset `stack_depth` to the value before entering `if` block.
let mut frame = ControlFrame::new(
ControlFrameKind::IfFalse { end_label },
stack_depth,
ty,
);
control_frames.push(frame);
}
Some(_) => panic!("else expects if block"),
None => panic!("control stack is never empty"),
};
}
Operator::End => {
let control_frame = control_frames.pop().expect("control stack is never empty");
if !control_frame.kind.is_loop() {
// Branches to a control frame with block type directs control flow to the header of the loop
// and we don't need to resolve it here. Branching to other control frames always lead
// control flow to the corresponding `end`.
define_label(&mut ctx, control_frame.kind.br_destination());
}
if let ControlFrameKind::IfTrue { if_not, .. } = control_frame.kind {
// this is `if .. end` construction. Define the `if_not` label here.
define_label(&mut ctx, if_not);
}
restore_stack_depth(&mut ctx, control_frame.outgoing_stack_depth());
if control_frames.len() == 0 {
// This is the last control frame. Perform the implicit return here.
if return_ty != Type::EmptyBlockType {
prepare_return_value(&mut ctx);
}
}
}
Operator::I32Eq => relop_eq_i32(&mut ctx),
Operator::I32Add => i32_add(&mut ctx),
Operator::I32Sub => i32_sub(&mut ctx),
Operator::I32And => i32_and(&mut ctx),
Operator::I32Or => i32_or(&mut ctx),
Operator::I32Xor => i32_xor(&mut ctx),
Operator::I32Mul => i32_mul(&mut ctx),
Operator::GetLocal { local_index } => get_local_i32(&mut ctx, local_index),
Operator::I32Const { value } => literal_i32(&mut ctx, value),
Operator::Call { function_index } => {
let callee_ty = translation_ctx.func_type(function_index);
// TODO: this implementation assumes that this function is locally defined.
call_direct(
&mut ctx,
function_index,
callee_ty.params.len() as u32,
callee_ty.returns.len() as u32,
);
}
_ => {
trap(&mut ctx);
}
}
}
epilogue(&mut ctx);
Ok(())
}