Add a simple boilerplate.
This commit is contained in:
committed by
Dan Gohman
parent
08240761d5
commit
b42696f207
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use error::Error;
|
use error::Error;
|
||||||
use dynasmrt::x64::Assembler;
|
use dynasmrt::x64::Assembler;
|
||||||
use dynasmrt::{DynasmApi, AssemblyOffset, ExecutableBuffer};
|
use dynasmrt::{DynasmApi, DynasmLabelApi, AssemblyOffset, ExecutableBuffer, DynamicLabel};
|
||||||
|
|
||||||
/// Size of a pointer on the target in bytes.
|
/// Size of a pointer on the target in bytes.
|
||||||
const WORD_SIZE: u32 = 8;
|
const WORD_SIZE: u32 = 8;
|
||||||
@@ -82,6 +82,10 @@ impl Registers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Label in code.
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub struct Label(DynamicLabel);
|
||||||
|
|
||||||
/// Describes location of a argument.
|
/// Describes location of a argument.
|
||||||
enum ArgLocation {
|
enum ArgLocation {
|
||||||
/// Argument is passed via some register.
|
/// Argument is passed via some register.
|
||||||
@@ -175,6 +179,19 @@ impl<'a> Context<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new undefined label.
|
||||||
|
pub fn create_label(ctx: &mut Context) -> Label {
|
||||||
|
Label(ctx.asm.new_dynamic_label())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Define the given label at the current position.
|
||||||
|
///
|
||||||
|
/// Multiple labels can be defined at the same position. However, a label
|
||||||
|
/// can be defined only once.
|
||||||
|
pub fn define_label(ctx: &mut Context, label: Label) {
|
||||||
|
ctx.asm.dynamic_label(label.0);
|
||||||
|
}
|
||||||
|
|
||||||
fn push_i32(ctx: &mut Context, gpr: GPR) {
|
fn push_i32(ctx: &mut Context, gpr: GPR) {
|
||||||
// For now, do an actual push (and pop below). In the future, we could
|
// For now, do an actual push (and pop below). In the future, we could
|
||||||
// do on-the-fly register allocation here.
|
// do on-the-fly register allocation here.
|
||||||
@@ -284,7 +301,7 @@ pub fn epilogue(ctx: &mut Context) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unsupported_opcode(ctx: &mut Context) {
|
pub fn trap(ctx: &mut Context) {
|
||||||
dynasm!(ctx.asm
|
dynasm!(ctx.asm
|
||||||
; ud2
|
; ud2
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,6 +2,73 @@ use backend::*;
|
|||||||
use error::Error;
|
use error::Error;
|
||||||
use wasmparser::{FunctionBody, Operator};
|
use wasmparser::{FunctionBody, Operator};
|
||||||
|
|
||||||
|
/// 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).
|
||||||
|
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,
|
||||||
|
// TODO: type, stack height, etc
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ControlFrame {
|
||||||
|
pub fn new(kind: ControlFrameKind) -> ControlFrame {
|
||||||
|
ControlFrame {
|
||||||
|
kind,
|
||||||
|
stack_polymorphic: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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, body: &FunctionBody) -> Result<(), Error> {
|
pub fn translate(session: &mut CodeGenSession, body: &FunctionBody) -> Result<(), Error> {
|
||||||
let locals = body.get_locals_reader()?;
|
let locals = body.get_locals_reader()?;
|
||||||
|
|
||||||
@@ -24,24 +91,60 @@ pub fn translate(session: &mut CodeGenSession, body: &FunctionBody) -> Result<()
|
|||||||
copy_incoming_arg(&mut ctx, arg_pos);
|
copy_incoming_arg(&mut ctx, 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,
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
for op in operators {
|
for op in operators {
|
||||||
match op? {
|
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);
|
||||||
|
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.
|
||||||
|
}
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
Operator::I32Add => {
|
Operator::I32Add => {
|
||||||
add_i32(&mut ctx);
|
add_i32(&mut ctx);
|
||||||
}
|
}
|
||||||
Operator::GetLocal { local_index } => {
|
Operator::GetLocal { local_index } => {
|
||||||
get_local_i32(&mut ctx, local_index);
|
get_local_i32(&mut ctx, local_index);
|
||||||
}
|
}
|
||||||
Operator::End => {
|
|
||||||
// TODO: This is super naive and makes a lot of unfounded assumptions
|
|
||||||
// but will do for the start.
|
|
||||||
prepare_return_value(&mut ctx);
|
|
||||||
}
|
|
||||||
_ => {
|
_ => {
|
||||||
unsupported_opcode(&mut ctx);
|
trap(&mut ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
prepare_return_value(&mut ctx);
|
||||||
epilogue(&mut ctx);
|
epilogue(&mut ctx);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
Reference in New Issue
Block a user