Implement if then else
This commit is contained in:
committed by
Dan Gohman
parent
ba216b2e8a
commit
aa5643b9b5
@@ -1,6 +1,8 @@
|
||||
use backend::*;
|
||||
use error::Error;
|
||||
use wasmparser::{FunctionBody, Operator};
|
||||
use wasmparser::{FunctionBody, Operator, Type};
|
||||
|
||||
// TODO: Use own declared `Type` enum.
|
||||
|
||||
/// Type of a control frame.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
@@ -10,6 +12,7 @@ enum ControlFrameKind {
|
||||
/// 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 {
|
||||
@@ -49,20 +52,34 @@ impl ControlFrameKind {
|
||||
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.
|
||||
/// 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,
|
||||
// TODO: type, stack height, etc
|
||||
/// Relative stack depth at the beginning of the frame.
|
||||
stack_depth: StackDepth,
|
||||
ty: Type,
|
||||
}
|
||||
|
||||
impl ControlFrame {
|
||||
pub fn new(kind: ControlFrameKind) -> 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 increment expected outgoing stack depth value
|
||||
// to account for the result value.
|
||||
outgoing_stack_depth.increment();
|
||||
}
|
||||
outgoing_stack_depth
|
||||
}
|
||||
|
||||
/// Marks this control frame as reached stack-polymorphic state.
|
||||
pub fn mark_stack_polymorphic(&mut self) {
|
||||
self.stack_polymorphic = true;
|
||||
@@ -75,6 +92,7 @@ pub fn translate(session: &mut CodeGenSession, body: &FunctionBody) -> Result<()
|
||||
// Assume signature is (i32, i32) -> i32 for now.
|
||||
// TODO: Use a real signature
|
||||
const ARG_COUNT: u32 = 2;
|
||||
let return_ty = Type::I32;
|
||||
|
||||
let mut framesize = ARG_COUNT;
|
||||
for local in locals {
|
||||
@@ -100,6 +118,8 @@ pub fn translate(session: &mut CodeGenSession, body: &FunctionBody) -> Result<()
|
||||
ControlFrameKind::Block {
|
||||
end_label: epilogue_label,
|
||||
},
|
||||
current_stack_depth(&ctx),
|
||||
return_ty,
|
||||
));
|
||||
|
||||
for op in operators {
|
||||
@@ -114,24 +134,63 @@ pub fn translate(session: &mut CodeGenSession, body: &FunctionBody) -> Result<()
|
||||
Operator::If { ty } => {
|
||||
let end_label = create_label(&mut ctx);
|
||||
let if_not = create_label(&mut ctx);
|
||||
control_frames.push(ControlFrame::new(
|
||||
ControlFrameKind::IfTrue {
|
||||
end_label,
|
||||
if_not,
|
||||
},
|
||||
));
|
||||
|
||||
// TODO: Generate code that pops a value and executes the if_true part if the value
|
||||
// is not equal to zero.
|
||||
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
|
||||
// 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());
|
||||
}
|
||||
Operator::I32Eq => {
|
||||
relop_eq_i32(&mut ctx);
|
||||
|
||||
Reference in New Issue
Block a user