Add loops

This commit is contained in:
Jef
2018-12-17 12:50:29 +01:00
parent 23b5a56a7d
commit 53841cdb07

View File

@@ -13,8 +13,7 @@ enum ControlFrameKind {
/// Can be used for an implicit function block. /// Can be used for an implicit function block.
Block { end_label: Label }, Block { end_label: Label },
/// Loop frame (branching to the beginning of block). /// Loop frame (branching to the beginning of block).
#[allow(unused)] Loop { header: Label, break_: Label },
Loop { header: Label },
/// True-subblock of if expression. /// True-subblock of if expression.
IfTrue { IfTrue {
/// If jump happens inside the if-true block then control will /// If jump happens inside the if-true block then control will
@@ -32,10 +31,10 @@ enum ControlFrameKind {
impl ControlFrameKind { impl ControlFrameKind {
/// Returns a label which should be used as a branch destination. /// Returns a label which should be used as a branch destination.
fn br_destination(&self) -> Label { fn block_end(&self) -> Label {
match *self { match *self {
ControlFrameKind::Block { end_label } => end_label, ControlFrameKind::Block { end_label } => end_label,
ControlFrameKind::Loop { header } => header, ControlFrameKind::Loop { break_, .. } => break_,
ControlFrameKind::IfTrue { end_label, .. } => end_label, ControlFrameKind::IfTrue { end_label, .. } => end_label,
ControlFrameKind::IfFalse { end_label } => end_label, ControlFrameKind::IfFalse { end_label } => end_label,
} }
@@ -100,16 +99,16 @@ pub fn translate(
num_locals += count; num_locals += count;
} }
let mut ctx = session.new_context(func_idx); let ctx = &mut session.new_context(func_idx);
let operators = body.get_operators_reader()?; let operators = body.get_operators_reader()?;
start_function(&mut ctx, arg_count, num_locals); start_function(ctx, arg_count, num_locals);
let mut control_frames = Vec::new(); let mut control_frames = Vec::new();
// Upon entering the function implicit frame for function body is pushed. It has the same // 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. // result type as the function itself. Branching to it is equivalent to returning from the function.
let epilogue_label = create_label(&mut ctx); let epilogue_label = create_label(ctx);
control_frames.push(ControlFrame::new( control_frames.push(ControlFrame::new(
ControlFrameKind::Block { ControlFrameKind::Block {
end_label: epilogue_label, end_label: epilogue_label,
@@ -125,13 +124,13 @@ pub fn translate(
.last_mut() .last_mut()
.expect("control stack is never empty") .expect("control stack is never empty")
.mark_stack_polymorphic(); .mark_stack_polymorphic();
trap(&mut ctx); trap(ctx);
} }
Operator::If { ty } => { Operator::If { ty } => {
let end_label = create_label(&mut ctx); let end_label = create_label(ctx);
let if_not = create_label(&mut ctx); let if_not = create_label(ctx);
pop_and_breq(&mut ctx, if_not); pop_and_breq(ctx, if_not);
control_frames.push(ControlFrame::new( control_frames.push(ControlFrame::new(
ControlFrameKind::IfTrue { end_label, if_not }, ControlFrameKind::IfTrue { end_label, if_not },
@@ -139,6 +138,19 @@ pub fn translate(
ty, ty,
)); ));
} }
Operator::Loop { ty } => {
let header = create_label(ctx);
let break_ = create_label(ctx);
define_label(ctx, header);
pop_and_breq(ctx, break_);
control_frames.push(ControlFrame::new(
ControlFrameKind::Loop { header, break_ },
current_block_state(&ctx),
ty,
));
}
Operator::Else => { Operator::Else => {
match control_frames.pop() { match control_frames.pop() {
Some(ControlFrame { Some(ControlFrame {
@@ -148,17 +160,17 @@ pub fn translate(
.. ..
}) => { }) => {
if ty != Type::EmptyBlockType { if ty != Type::EmptyBlockType {
return_from_block(&mut ctx); return_from_block(ctx);
} }
// Finalize if..else block by jumping to the `end_label`. // Finalize if..else block by jumping to the `end_label`.
br(&mut ctx, end_label); br(ctx, end_label);
// Define `if_not` label here, so if the corresponding `if` block receives // Define `if_not` label here, so if the corresponding `if` block receives
// 0 it will branch here. // 0 it will branch here.
// After that reset stack depth to the value before entering `if` block. // After that reset stack depth to the value before entering `if` block.
define_label(&mut ctx, if_not); define_label(ctx, if_not);
end_block(&mut ctx, block_state.clone()); end_block(ctx, block_state.clone());
// Carry over the `end_label`, so it will be resolved when the corresponding `end` // Carry over the `end_label`, so it will be resolved when the corresponding `end`
// is encountered. // is encountered.
@@ -179,57 +191,56 @@ pub fn translate(
let control_frame = control_frames.pop().expect("control stack is never empty"); let control_frame = control_frames.pop().expect("control stack is never empty");
if control_frame.ty != Type::EmptyBlockType && !control_frames.is_empty() { if control_frame.ty != Type::EmptyBlockType && !control_frames.is_empty() {
return_from_block(&mut ctx); return_from_block(ctx);
} }
if !control_frame.kind.is_loop() { if let ControlFrameKind::Loop { header, .. } = control_frame.kind {
// Branches to a control frame with block type directs control flow to the header of the loop br(ctx, header);
// 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());
} }
define_label(ctx, control_frame.kind.block_end());
if let ControlFrameKind::IfTrue { if_not, .. } = control_frame.kind { if let ControlFrameKind::IfTrue { if_not, .. } = control_frame.kind {
// this is `if .. end` construction. Define the `if_not` label here. // this is `if .. end` construction. Define the `if_not` label here.
define_label(&mut ctx, if_not); define_label(ctx, if_not);
} }
// This is the last control frame. Perform the implicit return here. // This is the last control frame. Perform the implicit return here.
if control_frames.len() == 0 && return_ty != Type::EmptyBlockType { if control_frames.len() == 0 && return_ty != Type::EmptyBlockType {
prepare_return_value(&mut ctx); prepare_return_value(ctx);
} }
end_block(&mut ctx, control_frame.block_state); end_block(ctx, control_frame.block_state);
push_block_return_value(&mut ctx); push_block_return_value(ctx);
} }
Operator::I32Eq => relop_eq_i32(&mut ctx), Operator::I32Eq => relop_eq_i32(ctx),
Operator::I32Add => i32_add(&mut ctx), Operator::I32Add => i32_add(ctx),
Operator::I32Sub => i32_sub(&mut ctx), Operator::I32Sub => i32_sub(ctx),
Operator::I32And => i32_and(&mut ctx), Operator::I32And => i32_and(ctx),
Operator::I32Or => i32_or(&mut ctx), Operator::I32Or => i32_or(ctx),
Operator::I32Xor => i32_xor(&mut ctx), Operator::I32Xor => i32_xor(ctx),
Operator::I32Mul => i32_mul(&mut ctx), Operator::I32Mul => i32_mul(ctx),
Operator::GetLocal { local_index } => get_local_i32(&mut ctx, local_index), Operator::GetLocal { local_index } => get_local_i32(ctx, local_index),
Operator::I32Const { value } => literal_i32(&mut ctx, value), Operator::I32Const { value } => literal_i32(ctx, value),
Operator::Call { function_index } => { Operator::Call { function_index } => {
let callee_ty = translation_ctx.func_type(function_index); let callee_ty = translation_ctx.func_type(function_index);
// TODO: this implementation assumes that this function is locally defined. // TODO: this implementation assumes that this function is locally defined.
call_direct( call_direct(
&mut ctx, ctx,
function_index, function_index,
callee_ty.params.len() as u32, callee_ty.params.len() as u32,
callee_ty.returns.len() as u32, callee_ty.returns.len() as u32,
); );
push_return_value(&mut ctx); push_return_value(ctx);
} }
_ => { _ => {
trap(&mut ctx); trap(ctx);
} }
} }
} }
epilogue(&mut ctx); epilogue(ctx);
Ok(()) Ok(())
} }