Get control flow working (fingers crossed)

This commit is contained in:
Lachlan Sneff 2019-02-12 18:02:00 -08:00
parent 5ee19e55a5
commit 2572a0259b
5 changed files with 377 additions and 52 deletions

View File

@ -2,21 +2,22 @@ use inkwell::{
builder::Builder,
context::Context,
module::Module,
passes::PassManager,
types::{BasicType, BasicTypeEnum, FunctionType},
values::{BasicValue, FunctionValue},
values::{BasicValue, FunctionValue, PhiValue},
FloatPredicate, IntPredicate,
};
use smallvec::SmallVec;
use wasmer_runtime_core::{
module::ModuleInfo,
structures::{Map, SliceMap, TypedIndex},
types::{FuncIndex, FuncSig, LocalFuncIndex, LocalOrImport, SigIndex, Type},
types::{FuncIndex, MemoryIndex, FuncSig, LocalFuncIndex, LocalOrImport, SigIndex, Type, MemoryType},
};
use wasmparser::{BinaryReaderError, CodeSectionReader, LocalsReader, Operator, OperatorsReader};
use crate::intrinsics::Intrinsics;
use crate::read_info::type_to_type;
use crate::state::State;
use crate::state::{ControlFrame, IfElseState, State};
fn func_sig_to_llvm(context: &Context, intrinsics: &Intrinsics, sig: &FuncSig) -> FunctionType {
let user_param_types = sig.params().iter().map(|&ty| type_to_llvm(intrinsics, ty));
@ -116,10 +117,37 @@ fn parse_function(
let llvm_sig = &signatures[sig_index];
let function = functions[func_index];
let entry_block = context.append_basic_block(&function, "entry");
builder.position_at_end(&entry_block);
let mut state = State::new();
let entry_block = context.append_basic_block(&function, "entry");
let return_block = context.append_basic_block(&function, "return");
builder.position_at_end(&return_block);
let phis: SmallVec<[PhiValue; 1]> = func_sig
.returns()
.iter()
.map(|&wasmer_ty| type_to_llvm(intrinsics, wasmer_ty))
.map(|ty| builder.build_phi(ty, &state.var_name()))
.collect();
match phis.as_slice() {
// No returns.
&[] => {
builder.build_return(None);
}
&[one_value] => {
let value = one_value.as_basic_value();
builder.build_return(Some(&value));
}
returns @ _ => {
// let struct_ty = llvm_sig.get_return_type().as_struct_type();
// let ret_struct = struct_ty.const_zero();
unimplemented!("multi-value returns not yet implemented")
}
}
state.push_block(return_block, phis);
builder.position_at_end(&entry_block);
let mut locals = Vec::with_capacity(locals_reader.get_count() as usize);
locals.extend(function.get_param_iter().enumerate().map(|(index, param)| {
@ -163,7 +191,7 @@ fn parse_function(
offset: -1isize as usize,
})?;
let end_block = context.append_basic_block(&function, &state.block_name());
let end_block = context.append_basic_block(&function, "end");
builder.position_at_end(&end_block);
let phis = if let Ok(wasmer_ty) = type_to_type(ty) {
@ -180,12 +208,24 @@ fn parse_function(
builder.position_at_end(&current_block);
}
Operator::Loop { ty } => {
let loop_body = context.append_basic_block(&function, "loop_body");
let loop_next = context.append_basic_block(&function, "loop_outer");
// let loop_body = context.append_basic_block(&function, &state.block_name());
// let next = context.append_basic_block(&function, &state.block_name());
// builder.build_unconditional_branch(&body);
// let num_return_values = if ty == wasmparser::Type::EmptyBlockType { 0 } else { 1 };
// state.push_loop(loop_body, next, num_return_values);
builder.build_unconditional_branch(&loop_body);
builder.position_at_end(&loop_next);
let phis = if let Ok(wasmer_ty) = type_to_type(ty) {
let llvm_ty = type_to_llvm(intrinsics, wasmer_ty);
[llvm_ty]
.iter()
.map(|&ty| builder.build_phi(ty, &state.var_name()))
.collect()
} else {
SmallVec::new()
};
builder.position_at_end(&loop_body);
state.push_loop(loop_body, loop_next, phis);
}
Operator::Br { relative_depth } => {
let frame = state.frame_at_depth(relative_depth)?;
@ -195,7 +235,13 @@ fn parse_function(
offset: -1isize as usize,
})?;
let values = state.peekn(frame.phis().len())?;
let value_len = if frame.is_loop() {
0
} else {
frame.phis().len()
};
let values = state.peekn(value_len)?;
// For each result of the block we're branching to,
// pop a value off the value stack and load it into
@ -204,11 +250,10 @@ fn parse_function(
phi.add_incoming(&[(value, &current_block)]);
}
builder.build_unconditional_branch(frame.dest());
builder.build_unconditional_branch(frame.br_dest());
state.popn(frame.phis().len())?;
builder.build_unreachable();
state.popn(value_len)?;
state.reachable = false;
}
Operator::BrIf { relative_depth } => {
let cond = state.pop1()?;
@ -219,13 +264,19 @@ fn parse_function(
offset: -1isize as usize,
})?;
let param_stack = state.peekn(frame.phis().len())?;
let value_len = if frame.is_loop() {
0
} else {
frame.phis().len()
};
let param_stack = state.peekn(value_len)?;
for (phi, value) in frame.phis().iter().zip(param_stack.iter()) {
phi.add_incoming(&[(value, &current_block)]);
}
let false_block = context.append_basic_block(&function, &state.block_name());
let else_block = context.append_basic_block(&function, "else");
let cond_value = builder.build_int_compare(
IntPredicate::NE,
@ -233,8 +284,8 @@ fn parse_function(
intrinsics.i32_zero,
&state.var_name(),
);
builder.build_conditional_branch(cond_value, frame.dest(), &false_block);
builder.position_at_end(&false_block);
builder.build_conditional_branch(cond_value, frame.br_dest(), &else_block);
builder.position_at_end(&else_block);
}
Operator::BrTable { ref table } => {
let current_block = builder.get_insert_block().ok_or(BinaryReaderError {
@ -268,25 +319,131 @@ fn parse_function(
phi.add_incoming(&[(value, &current_block)]);
}
Ok((case_index_literal, frame.dest()))
Ok((case_index_literal, frame.br_dest()))
})
.collect::<Result<_, _>>()?;
builder.build_switch(index.into_int_value(), default_frame.dest(), &cases[..]);
builder.build_switch(index.into_int_value(), default_frame.br_dest(), &cases[..]);
state.popn(res_len)?;
builder.build_unreachable();
}
Operator::If { ty } => {
let current_block = builder.get_insert_block().ok_or(BinaryReaderError {
message: "not currently in a block",
offset: -1isize as usize,
})?;
let if_then_block = context.append_basic_block(&function, "if_then");
let if_else_block = context.append_basic_block(&function, "if_else");
let end_block = context.append_basic_block(&function, "if_end");
let end_phis = {
builder.position_at_end(&end_block);
let phis = if let Ok(wasmer_ty) = type_to_type(ty) {
let llvm_ty = type_to_llvm(intrinsics, wasmer_ty);
[llvm_ty]
.iter()
.map(|&ty| builder.build_phi(ty, &state.var_name()))
.collect()
} else {
SmallVec::new()
};
builder.position_at_end(&current_block);
phis
};
let cond = state.pop1()?;
let cond_value = builder.build_int_compare(
IntPredicate::NE,
cond.into_int_value(),
intrinsics.i32_zero,
&state.var_name(),
);
builder.build_conditional_branch(cond_value, &if_then_block, &if_else_block);
builder.position_at_end(&if_then_block);
state.push_if(if_then_block, if_else_block, end_block, end_phis);
}
Operator::Else => {
if state.reachable {
let frame = state.frame_at_depth(0)?;
builder.build_unconditional_branch(frame.code_after());
let current_block = builder.get_insert_block().ok_or(BinaryReaderError {
message: "not currently in a block",
offset: -1isize as usize,
})?;
for phi in frame.phis().to_vec().iter().rev() {
let value = state.pop1()?;
phi.add_incoming(&[(&value, &current_block)])
}
}
let (if_else_block, if_else_state) = if let ControlFrame::IfElse {
if_else,
if_else_state,
..
} = state.frame_at_depth_mut(0)?
{
(if_else, if_else_state)
} else {
unreachable!()
};
*if_else_state = IfElseState::Else;
builder.position_at_end(if_else_block);
state.reachable = true;
}
Operator::End => {
let frame = state.pop_frame()?;
let current_block = builder.get_insert_block().ok_or(BinaryReaderError {
message: "not currently in a block",
offset: -1isize as usize,
})?;
if state.reachable {
builder.build_unconditional_branch(frame.code_after());
for phi in frame.phis().iter().rev() {
let value = state.pop1()?;
phi.add_incoming(&[(&value, &current_block)])
}
}
if let ControlFrame::IfElse {
if_else,
next,
phis,
if_else_state,
..
} = &frame
{
if let IfElseState::If = if_else_state {
builder.position_at_end(if_else);
builder.build_unconditional_branch(next);
}
}
builder.position_at_end(frame.code_after());
state.reset_stack(&frame);
state.reachable = true;
// Push each phi value to the value stack.
for phi in frame.phis() {
state.push1(phi.as_basic_value());
}
}
Operator::Return => {
let frame = state.outermost_frame()?;
state.reset_stack(&frame);
builder.build_unconditional_branch(frame.br_dest());
state.reachable = false;
}
Operator::Unreachable => {
@ -294,6 +451,7 @@ fn parse_function(
// If llvm cannot prove that this is never touched,
// it will emit a `ud2` instruction on x86_64 arches.
builder.build_unreachable();
state.reachable = false;
}
/***************************
@ -1021,6 +1179,26 @@ fn parse_function(
unimplemented!("waiting on better bitcasting support in inkwell")
}
Operator::MemoryGrow { reserved } => {
let memory_grow_const = intrinsics.i32_ty.const_int(reserved as u64, false);
let memory_index = MemoryIndex::new(reserved);
match memory_index.local_or_import(info) {
LocalOrImport::Local(local_mem_index) => {
let mem_desc = &info.memories[local_mem_index];
match mem_desc.memory_type() {
MemoryType::Dynamic => {
}
}
},
LocalOrImport::Import(import_mem_index) => {
},
}
}
op @ _ => {
println!("{}", module.print_to_string().to_string());
unimplemented!("{:?}", op);
@ -1028,5 +1206,12 @@ fn parse_function(
}
}
let pass_manager = PassManager::create_for_module();
pass_manager.add_promote_memory_to_register_pass();
pass_manager.add_cfg_simplification_pass();
pass_manager.run_on_module(module);
println!("{}", module.print_to_string().to_string());
Ok(())
}

View File

@ -54,6 +54,22 @@ pub struct Intrinsics {
pub i64_zero: IntValue,
pub f32_zero: FloatValue,
pub f64_zero: FloatValue,
// VM intrinsics.
pub memory_grow_dynamic_local: FunctionValue,
pub memory_grow_static_local: FunctionValue,
pub memory_grow_shared_local: FunctionValue,
pub memory_grow_dynamic_import: FunctionValue,
pub memory_grow_static_import: FunctionValue,
pub memory_grow_shared_import: FunctionValue,
pub memory_size_dynamic_local: FunctionValue,
pub memory_size_static_local: FunctionValue,
pub memory_size_shared_local: FunctionValue,
pub memory_size_dynamic_import: FunctionValue,
pub memory_size_static_import: FunctionValue,
pub memory_size_shared_import: FunctionValue,
// pub ctx_ty: StructType,
}
impl Intrinsics {
@ -64,6 +80,7 @@ impl Intrinsics {
let i64_ty = context.i64_type();
let f32_ty = context.f32_type();
let f64_ty = context.f64_type();
// let ctx_ty = context.struct_type(&[], false);
let i1_zero = i1_ty.const_int(0, false);
let i32_zero = i32_ty.const_int(0, false);
@ -89,6 +106,9 @@ impl Intrinsics {
let ret_f32_take_f32_f32 = f32_ty.fn_type(&[f32_ty_basic, f32_ty_basic], false);
let ret_f64_take_f64_f64 = f64_ty.fn_type(&[f64_ty_basic, f64_ty_basic], false);
let ret_i32_take_i64_i32_i32 = i32_ty.fn_type(&[i64_ty, i32_ty, i32_ty], false);
let ret_i32_take_i64_i32 = i32_ty.fn_type(&[i64_ty, i32_ty], false);
Self {
ctlz_i32: module.add_function("llvm.ctlz.i32", ret_i32_take_i32_i1, None),
ctlz_i64: module.add_function("llvm.ctlz.i64", ret_i64_take_i64_i1, None),
@ -138,6 +158,25 @@ impl Intrinsics {
i64_zero,
f32_zero,
f64_zero,
// VM intrinsics.
memory_grow_dynamic_local: module.add_function("vm.memory.grow.dynamic.local", ret_i32_take_i64_i32_i32, None),
memory_grow_static_local: module.add_function("vm.memory.grow.static.local", ret_i32_take_i64_i32_i32, None),
memory_grow_shared_local: module.add_function("vm.memory.grow.shared.local", ret_i32_take_i64_i32_i32, None),
memory_grow_dynamic_import: module.add_function("vm.memory.grow.dynamic.import", ret_i32_take_i64_i32_i32, None),
memory_grow_static_import: module.add_function("vm.memory.grow.static.import", ret_i32_take_i64_i32_i32, None),
memory_grow_shared_import: module.add_function("vm.memory.grow.shared.import", ret_i32_take_i64_i32_i32, None),
memory_size_dynamic_local: module.add_function("vm.memory.size.dynamic.local", ret_i32_take_i64_i32, None),
memory_size_static_local: module.add_function("vm.memory.size.static.local", ret_i32_take_i64_i32, None),
memory_size_shared_local: module.add_function("vm.memory.size.shared.local", ret_i32_take_i64_i32, None),
memory_size_dynamic_import: module.add_function("vm.memory.size.dynamic.import", ret_i32_take_i64_i32, None),
memory_size_static_import: module.add_function("vm.memory.size.static.import", ret_i32_take_i64_i32, None),
memory_size_shared_import: module.add_function("vm.memory.size.shared.import", ret_i32_take_i64_i32, None),
}
}
}
// pub struct CtxType {
// ctx_ty: StructType,
// }

View File

@ -30,21 +30,26 @@ impl Compiler for LLVMCompiler {
#[test]
fn test_read_module() {
use wabt::wat2wasm;
let WAT: &'static str = r#"
let wat = r#"
(module
(type $t0 (func (param i32) (result i32)))
(import "env" "memory" (memory 1 1))
(import "env" "table" (table 10 anyfunc))
(import "env" "global" (global i32))
(import "env" "print_i32" (func $print_i32 (type $t0)))
(func $identity (type $t0) (param $p0 i32) (result i32)
get_local $p0)
(func $print_num (export "print_num") (type $t0) (param $p0 i32) (result i32)
get_global 0
call $identity
call $print_i32))
(type $t1 (func (result i32)))
(func $foo (type $t0) (param i32) (result i32)
get_local 0
(if
(then
i32.const 42
set_local 0
)
(else
i32.const 24
set_local 0
)
)
get_local 0
))
"#;
let wasm = wat2wasm(WAT).unwrap();
let wasm = wat2wasm(wat).unwrap();
let (info, code_reader) = read_info::read_module(&wasm).unwrap();

View File

@ -285,7 +285,12 @@ pub fn type_to_type(ty: WpType) -> Result<Type, BinaryReaderError> {
offset: -1isize as usize,
});
}
_ => panic!("broken invariant, invalid type"),
_ => {
return Err(BinaryReaderError {
message: "that type is not supported as a wasmer type",
offset: -1isize as usize,
});
}
})
}

View File

@ -6,33 +6,73 @@ use smallvec::SmallVec;
use std::cell::Cell;
use wasmparser::BinaryReaderError;
#[derive(Debug)]
pub enum ControlFrame {
Block {
end_label: BasicBlock,
next: BasicBlock,
phis: SmallVec<[PhiValue; 1]>,
stack_size_snapshot: usize,
},
Loop {
body: BasicBlock,
next: BasicBlock,
phis: SmallVec<[PhiValue; 1]>,
stack_size_snapshot: usize,
},
IfElse {
if_then: BasicBlock,
if_else: BasicBlock,
next: BasicBlock,
phis: SmallVec<[PhiValue; 1]>,
stack_size_snapshot: usize,
if_else_state: IfElseState,
},
}
#[derive(Debug)]
pub enum IfElseState {
If,
Else,
}
impl ControlFrame {
pub fn dest(&self) -> &BasicBlock {
pub fn code_after(&self) -> &BasicBlock {
match self {
ControlFrame::Block { ref end_label, .. } => end_label,
ControlFrame::Block { ref next, .. }
| ControlFrame::Loop { ref next, .. }
| ControlFrame::IfElse { ref next, .. } => next,
}
}
pub fn br_dest(&self) -> &BasicBlock {
match self {
ControlFrame::Block { ref next, .. } | ControlFrame::IfElse { ref next, .. } => next,
ControlFrame::Loop { ref body, .. } => body,
}
}
pub fn phis(&self) -> &[PhiValue] {
match self {
ControlFrame::Block { ref phis, .. } => phis.as_slice(),
ControlFrame::Block { ref phis, .. }
| ControlFrame::Loop { ref phis, .. }
| ControlFrame::IfElse { ref phis, .. } => phis.as_slice(),
}
}
pub fn is_loop(&self) -> bool {
match self {
ControlFrame::Loop { .. } => true,
_ => false,
}
}
}
#[derive(Debug)]
pub struct State {
stack: Vec<BasicValueEnum>,
control_stack: Vec<ControlFrame>,
value_counter: Cell<usize>,
block_counter: Cell<usize>,
pub reachable: bool,
}
impl State {
@ -41,7 +81,7 @@ impl State {
stack: vec![],
control_stack: vec![],
value_counter: Cell::new(0),
block_counter: Cell::new(0),
reachable: true,
}
}
@ -50,11 +90,26 @@ impl State {
ControlFrame::Block {
stack_size_snapshot,
..
}
| ControlFrame::Loop {
stack_size_snapshot,
..
}
| ControlFrame::IfElse {
stack_size_snapshot,
..
} => *stack_size_snapshot,
};
self.stack.truncate(stack_size_snapshot);
}
pub fn outermost_frame(&self) -> Result<&ControlFrame, BinaryReaderError> {
self.control_stack.get(0).ok_or(BinaryReaderError {
message: "invalid control stack depth",
offset: -1isize as usize,
})
}
pub fn frame_at_depth(&self, depth: u32) -> Result<&ControlFrame, BinaryReaderError> {
let index = self.control_stack.len() - 1 - (depth as usize);
self.control_stack.get(index).ok_or(BinaryReaderError {
@ -63,6 +118,17 @@ impl State {
})
}
pub fn frame_at_depth_mut(
&mut self,
depth: u32,
) -> Result<&mut ControlFrame, BinaryReaderError> {
let index = self.control_stack.len() - 1 - (depth as usize);
self.control_stack.get_mut(index).ok_or(BinaryReaderError {
message: "invalid control stack depth",
offset: -1isize as usize,
})
}
pub fn pop_frame(&mut self) -> Result<ControlFrame, BinaryReaderError> {
self.control_stack.pop().ok_or(BinaryReaderError {
message: "cannot pop from control stack",
@ -70,13 +136,6 @@ impl State {
})
}
pub fn block_name(&self) -> String {
let counter = self.block_counter.get();
let s = format!("block{}", counter);
self.block_counter.set(counter + 1);
s
}
pub fn var_name(&self) -> String {
let counter = self.value_counter.get();
let s = format!("s{}", counter);
@ -129,6 +188,12 @@ impl State {
})
}
pub fn popn_save(&mut self, n: usize) -> Result<Vec<BasicValueEnum>, BinaryReaderError> {
let v = self.peekn(n)?.to_vec();
self.popn(n)?;
Ok(v)
}
pub fn popn(&mut self, n: usize) -> Result<(), BinaryReaderError> {
if self.stack.len() < n {
return Err(BinaryReaderError {
@ -142,11 +207,37 @@ impl State {
Ok(())
}
pub fn push_block(&mut self, end_label: BasicBlock, phis: SmallVec<[PhiValue; 1]>) {
pub fn push_block(&mut self, next: BasicBlock, phis: SmallVec<[PhiValue; 1]>) {
self.control_stack.push(ControlFrame::Block {
end_label,
next,
phis,
stack_size_snapshot: self.stack.len(),
});
}
pub fn push_loop(&mut self, body: BasicBlock, next: BasicBlock, phis: SmallVec<[PhiValue; 1]>) {
self.control_stack.push(ControlFrame::Loop {
body,
next,
phis,
stack_size_snapshot: self.stack.len(),
});
}
pub fn push_if(
&mut self,
if_then: BasicBlock,
if_else: BasicBlock,
next: BasicBlock,
phis: SmallVec<[PhiValue; 1]>,
) {
self.control_stack.push(ControlFrame::IfElse {
if_then,
if_else,
next,
phis,
stack_size_snapshot: self.stack.len(),
if_else_state: IfElseState::If,
});
}
}