Merge remote-tracking branch 'origin/master' into no_std
This commit is contained in:
@@ -25,9 +25,10 @@
|
||||
use cretonne::ir::{self, InstBuilder, MemFlags, JumpTableData};
|
||||
use cretonne::ir::types::*;
|
||||
use cretonne::ir::condcodes::{IntCC, FloatCC};
|
||||
use cton_frontend::FunctionBuilder;
|
||||
use cretonne::packed_option::ReservedValue;
|
||||
use cton_frontend::{FunctionBuilder, Variable};
|
||||
use wasmparser::{Operator, MemoryImmediate};
|
||||
use translation_utils::{f32_translation, f64_translation, type_to_type, num_return_values, Local};
|
||||
use translation_utils::{f32_translation, f64_translation, type_to_type, num_return_values};
|
||||
use translation_utils::{TableIndex, SignatureIndex, FunctionIndex, MemoryIndex};
|
||||
use state::{TranslationState, ControlStackFrame};
|
||||
use std::collections::{HashMap, hash_map};
|
||||
@@ -38,29 +39,31 @@ use std::vec::Vec;
|
||||
/// Translates wasm operators into Cretonne IL instructions. Returns `true` if it inserted
|
||||
/// a return.
|
||||
pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
op: &Operator,
|
||||
builder: &mut FunctionBuilder<Local>,
|
||||
op: Operator,
|
||||
builder: &mut FunctionBuilder<Variable>,
|
||||
state: &mut TranslationState,
|
||||
environ: &mut FE,
|
||||
) {
|
||||
if state.in_unreachable_code() {
|
||||
if !state.reachable {
|
||||
return translate_unreachable_operator(op, builder, state);
|
||||
}
|
||||
|
||||
// This big match treats all Wasm code operators.
|
||||
match *op {
|
||||
match op {
|
||||
/********************************** Locals ****************************************
|
||||
* `get_local` and `set_local` are treated as non-SSA variables and will completely
|
||||
* diseappear in the Cretonne Code
|
||||
* disappear in the Cretonne Code
|
||||
***********************************************************************************/
|
||||
Operator::GetLocal { local_index } => state.push1(builder.use_var(Local(local_index))),
|
||||
Operator::GetLocal { local_index } => {
|
||||
state.push1(builder.use_var(Variable::with_u32(local_index)))
|
||||
}
|
||||
Operator::SetLocal { local_index } => {
|
||||
let val = state.pop1();
|
||||
builder.def_var(Local(local_index), val);
|
||||
builder.def_var(Variable::with_u32(local_index), val);
|
||||
}
|
||||
Operator::TeeLocal { local_index } => {
|
||||
let val = state.peek1();
|
||||
builder.def_var(Local(local_index), val);
|
||||
builder.def_var(Variable::with_u32(local_index), val);
|
||||
}
|
||||
/********************************** Globals ****************************************
|
||||
* `get_global` and `set_global` are handled by the environment.
|
||||
@@ -90,7 +93,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
}
|
||||
}
|
||||
/********************************* Stack misc ***************************************
|
||||
* `drop`, `nop`, `unreachable` and `select`.
|
||||
* `drop`, `nop`, `unreachable` and `select`.
|
||||
***********************************************************************************/
|
||||
Operator::Drop => {
|
||||
state.pop1();
|
||||
@@ -106,7 +109,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
// We use `trap user0` to indicate a user-generated trap.
|
||||
// We could make the trap code configurable if need be.
|
||||
builder.ins().trap(ir::TrapCode::User(0));
|
||||
state.real_unreachable_stack_depth = 1;
|
||||
state.reachable = false;
|
||||
}
|
||||
/***************************** Control flow blocks **********************************
|
||||
* When starting a control flow block, we create a new `Ebb` that will hold the code
|
||||
@@ -156,15 +159,24 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
// and push a new control frame with a new ebb for the code after the if/then/else
|
||||
// At the end of the then clause we jump to the destination
|
||||
let i = state.control_stack.len() - 1;
|
||||
let (destination, return_count, branch_inst) = match state.control_stack[i] {
|
||||
ControlStackFrame::If {
|
||||
destination,
|
||||
num_return_values,
|
||||
branch_inst,
|
||||
..
|
||||
} => (destination, num_return_values, branch_inst),
|
||||
_ => panic!("should not happen"),
|
||||
};
|
||||
let (destination, return_count, branch_inst, ref mut reachable_from_top) =
|
||||
match state.control_stack[i] {
|
||||
ControlStackFrame::If {
|
||||
destination,
|
||||
num_return_values,
|
||||
branch_inst,
|
||||
reachable_from_top,
|
||||
..
|
||||
} => (
|
||||
destination,
|
||||
num_return_values,
|
||||
branch_inst,
|
||||
reachable_from_top,
|
||||
),
|
||||
_ => panic!("should not happen"),
|
||||
};
|
||||
// The if has an else, so there's no branch to the end from the top.
|
||||
*reachable_from_top = false;
|
||||
builder.ins().jump(destination, state.peekn(return_count));
|
||||
state.popn(return_count);
|
||||
// We change the target of the branch instruction
|
||||
@@ -220,7 +232,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
let (return_count, br_destination) = {
|
||||
let frame = &mut state.control_stack[i];
|
||||
// We signal that all the code that follows until the next End is unreachable
|
||||
frame.set_reachable();
|
||||
frame.set_branched_to_exit();
|
||||
let return_count = if frame.is_loop() {
|
||||
0
|
||||
} else {
|
||||
@@ -233,7 +245,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
state.peekn(return_count),
|
||||
);
|
||||
state.popn(return_count);
|
||||
state.real_unreachable_stack_depth = 1 + relative_depth as usize;
|
||||
state.reachable = false;
|
||||
}
|
||||
Operator::BrIf { relative_depth } => {
|
||||
let val = state.pop1();
|
||||
@@ -242,7 +254,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
let frame = &mut state.control_stack[i];
|
||||
// The values returned by the branch are still available for the reachable
|
||||
// code that comes after it
|
||||
frame.set_reachable();
|
||||
frame.set_branched_to_exit();
|
||||
let return_count = if frame.is_loop() {
|
||||
0
|
||||
} else {
|
||||
@@ -256,7 +268,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
state.peekn(return_count),
|
||||
);
|
||||
}
|
||||
Operator::BrTable { ref table } => {
|
||||
Operator::BrTable { table } => {
|
||||
let (depths, default) = table.read_table();
|
||||
let mut min_depth = default;
|
||||
for depth in &depths {
|
||||
@@ -273,31 +285,32 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
min_depth_frame.num_return_values()
|
||||
}
|
||||
};
|
||||
let val = state.pop1();
|
||||
let mut data = JumpTableData::with_capacity(depths.len());
|
||||
if jump_args_count == 0 {
|
||||
// No jump arguments
|
||||
let val = state.pop1();
|
||||
let mut data = JumpTableData::with_capacity(depths.len());
|
||||
for depth in depths {
|
||||
let i = state.control_stack.len() - 1 - (depth as usize);
|
||||
let frame = &mut state.control_stack[i];
|
||||
let ebb = frame.br_destination();
|
||||
let ebb = {
|
||||
let i = state.control_stack.len() - 1 - (depth as usize);
|
||||
let frame = &mut state.control_stack[i];
|
||||
frame.set_branched_to_exit();
|
||||
frame.br_destination()
|
||||
};
|
||||
data.push_entry(ebb);
|
||||
frame.set_reachable();
|
||||
}
|
||||
let jt = builder.create_jump_table(data);
|
||||
builder.ins().br_table(val, jt);
|
||||
let i = state.control_stack.len() - 1 - (default as usize);
|
||||
let frame = &mut state.control_stack[i];
|
||||
let ebb = frame.br_destination();
|
||||
let ebb = {
|
||||
let i = state.control_stack.len() - 1 - (default as usize);
|
||||
let frame = &mut state.control_stack[i];
|
||||
frame.set_branched_to_exit();
|
||||
frame.br_destination()
|
||||
};
|
||||
builder.ins().jump(ebb, &[]);
|
||||
state.real_unreachable_stack_depth = 1 + min_depth as usize;
|
||||
frame.set_reachable();
|
||||
} else {
|
||||
// Here we have jump arguments, but Cretonne's br_table doesn't support them
|
||||
// We then proceed to split the edges going out of the br_table
|
||||
let val = state.pop1();
|
||||
let return_count = jump_args_count;
|
||||
let mut data = JumpTableData::with_capacity(depths.len());
|
||||
let mut dest_ebb_sequence = Vec::new();
|
||||
let mut dest_ebb_map = HashMap::new();
|
||||
for depth in depths {
|
||||
@@ -313,29 +326,32 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
}
|
||||
let jt = builder.create_jump_table(data);
|
||||
builder.ins().br_table(val, jt);
|
||||
let default_ebb = state.control_stack[state.control_stack.len() - 1 -
|
||||
(default as usize)]
|
||||
.br_destination();
|
||||
let default_ebb = {
|
||||
let i = state.control_stack.len() - 1 - (default as usize);
|
||||
let frame = &mut state.control_stack[i];
|
||||
frame.set_branched_to_exit();
|
||||
frame.br_destination()
|
||||
};
|
||||
builder.ins().jump(default_ebb, state.peekn(return_count));
|
||||
for (depth, dest_ebb) in dest_ebb_sequence {
|
||||
builder.switch_to_block(dest_ebb);
|
||||
builder.seal_block(dest_ebb);
|
||||
let i = state.control_stack.len() - 1 - depth;
|
||||
let real_dest_ebb = {
|
||||
let i = state.control_stack.len() - 1 - depth;
|
||||
let frame = &mut state.control_stack[i];
|
||||
frame.set_reachable();
|
||||
frame.set_branched_to_exit();
|
||||
frame.br_destination()
|
||||
};
|
||||
builder.ins().jump(real_dest_ebb, state.peekn(return_count));
|
||||
}
|
||||
state.popn(return_count);
|
||||
state.real_unreachable_stack_depth = 1 + min_depth as usize;
|
||||
}
|
||||
state.reachable = false;
|
||||
}
|
||||
Operator::Return => {
|
||||
let (return_count, br_destination) = {
|
||||
let frame = &mut state.control_stack[0];
|
||||
frame.set_reachable();
|
||||
frame.set_branched_to_exit();
|
||||
let return_count = frame.num_return_values();
|
||||
(return_count, frame.br_destination())
|
||||
};
|
||||
@@ -348,7 +364,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
}
|
||||
}
|
||||
state.popn(return_count);
|
||||
state.real_unreachable_stack_depth = 1;
|
||||
state.reachable = false;
|
||||
}
|
||||
/************************************ Calls ****************************************
|
||||
* The call instructions pop off their arguments from the stack and append their
|
||||
@@ -619,12 +635,35 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
let val = state.pop1();
|
||||
state.push1(builder.ins().bitcast(I64, val));
|
||||
}
|
||||
Operator::I32Extend8S |
|
||||
Operator::I32Extend16S |
|
||||
Operator::I64Extend8S |
|
||||
Operator::I64Extend16S |
|
||||
Operator::I32Extend8S => {
|
||||
let val = state.pop1();
|
||||
state.push1(builder.ins().ireduce(I8, val));
|
||||
let val = state.pop1();
|
||||
state.push1(builder.ins().sextend(I32, val));
|
||||
}
|
||||
Operator::I32Extend16S => {
|
||||
let val = state.pop1();
|
||||
state.push1(builder.ins().ireduce(I16, val));
|
||||
let val = state.pop1();
|
||||
state.push1(builder.ins().sextend(I32, val));
|
||||
}
|
||||
Operator::I64Extend8S => {
|
||||
let val = state.pop1();
|
||||
state.push1(builder.ins().ireduce(I8, val));
|
||||
let val = state.pop1();
|
||||
state.push1(builder.ins().sextend(I64, val));
|
||||
}
|
||||
Operator::I64Extend16S => {
|
||||
let val = state.pop1();
|
||||
state.push1(builder.ins().ireduce(I16, val));
|
||||
let val = state.pop1();
|
||||
state.push1(builder.ins().sextend(I64, val));
|
||||
}
|
||||
Operator::I64Extend32S => {
|
||||
panic!("proposed sign-extend operators not yet supported");
|
||||
let val = state.pop1();
|
||||
state.push1(builder.ins().ireduce(I32, val));
|
||||
let val = state.pop1();
|
||||
state.push1(builder.ins().sextend(I64, val));
|
||||
}
|
||||
/****************************** Binary Operators ************************************/
|
||||
Operator::I32Add | Operator::I64Add => {
|
||||
@@ -897,80 +936,78 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
/// are dropped but special ones like `End` or `Else` signal the potential end of the unreachable
|
||||
/// portion so the translation state muts be updated accordingly.
|
||||
fn translate_unreachable_operator(
|
||||
op: &Operator,
|
||||
builder: &mut FunctionBuilder<Local>,
|
||||
op: Operator,
|
||||
builder: &mut FunctionBuilder<Variable>,
|
||||
state: &mut TranslationState,
|
||||
) {
|
||||
let stack = &mut state.stack;
|
||||
let control_stack = &mut state.control_stack;
|
||||
|
||||
// We don't translate because the code is unreachable
|
||||
// Nevertheless we have to record a phantom stack for this code
|
||||
// to know when the unreachable code ends
|
||||
match *op {
|
||||
Operator::If { ty: _ } |
|
||||
match op {
|
||||
Operator::If { ty: _ } => {
|
||||
// Push a placeholder control stack entry. The if isn't reachable,
|
||||
// so we don't have any branches anywhere.
|
||||
state.push_if(ir::Inst::reserved_value(), ir::Ebb::reserved_value(), 0);
|
||||
}
|
||||
Operator::Loop { ty: _ } |
|
||||
Operator::Block { ty: _ } => {
|
||||
state.phantom_unreachable_stack_depth += 1;
|
||||
}
|
||||
Operator::End => {
|
||||
if state.phantom_unreachable_stack_depth > 0 {
|
||||
state.phantom_unreachable_stack_depth -= 1;
|
||||
} else {
|
||||
// This End corresponds to a real control stack frame
|
||||
// We switch to the destination block but we don't insert
|
||||
// a jump instruction since the code is still unreachable
|
||||
let frame = control_stack.pop().unwrap();
|
||||
|
||||
builder.switch_to_block(frame.following_code());
|
||||
builder.seal_block(frame.following_code());
|
||||
match frame {
|
||||
// If it is a loop we also have to seal the body loop block
|
||||
ControlStackFrame::Loop { header, .. } => builder.seal_block(header),
|
||||
// If it is an if then the code after is reachable again
|
||||
ControlStackFrame::If { .. } => {
|
||||
state.real_unreachable_stack_depth = 1;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if frame.is_reachable() {
|
||||
state.real_unreachable_stack_depth = 1;
|
||||
}
|
||||
// Now we have to split off the stack the values not used
|
||||
// by unreachable code that hasn't been translated
|
||||
stack.truncate(frame.original_stack_size());
|
||||
// And add the return values of the block but only if the next block is reachble
|
||||
// (which corresponds to testing if the stack depth is 1)
|
||||
if state.real_unreachable_stack_depth == 1 {
|
||||
stack.extend_from_slice(builder.ebb_params(frame.following_code()));
|
||||
}
|
||||
state.real_unreachable_stack_depth -= 1;
|
||||
}
|
||||
state.push_block(ir::Ebb::reserved_value(), 0);
|
||||
}
|
||||
Operator::Else => {
|
||||
if state.phantom_unreachable_stack_depth > 0 {
|
||||
// This is part of a phantom if-then-else, we do nothing
|
||||
} else {
|
||||
// Encountering an real else means that the code in the else
|
||||
// clause is reachable again
|
||||
let (branch_inst, original_stack_size) = match control_stack[control_stack.len() -
|
||||
1] {
|
||||
ControlStackFrame::If {
|
||||
branch_inst,
|
||||
original_stack_size,
|
||||
..
|
||||
} => (branch_inst, original_stack_size),
|
||||
_ => panic!("should not happen"),
|
||||
};
|
||||
// We change the target of the branch instruction
|
||||
let else_ebb = builder.create_ebb();
|
||||
builder.change_jump_destination(branch_inst, else_ebb);
|
||||
builder.seal_block(else_ebb);
|
||||
builder.switch_to_block(else_ebb);
|
||||
// Now we have to split off the stack the values not used
|
||||
// by unreachable code that hasn't been translated
|
||||
stack.truncate(original_stack_size);
|
||||
state.real_unreachable_stack_depth = 0;
|
||||
let i = state.control_stack.len() - 1;
|
||||
match state.control_stack[i] {
|
||||
ControlStackFrame::If {
|
||||
branch_inst,
|
||||
ref mut reachable_from_top,
|
||||
..
|
||||
} => {
|
||||
if *reachable_from_top {
|
||||
// We have a branch from the top of the if to the else.
|
||||
state.reachable = true;
|
||||
// And because there's an else, there can no longer be a
|
||||
// branch from the top directly to the end.
|
||||
*reachable_from_top = false;
|
||||
|
||||
// We change the target of the branch instruction
|
||||
let else_ebb = builder.create_ebb();
|
||||
builder.change_jump_destination(branch_inst, else_ebb);
|
||||
builder.seal_block(else_ebb);
|
||||
builder.switch_to_block(else_ebb);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Operator::End => {
|
||||
let stack = &mut state.stack;
|
||||
let control_stack = &mut state.control_stack;
|
||||
let frame = control_stack.pop().unwrap();
|
||||
|
||||
// Now we have to split off the stack the values not used
|
||||
// by unreachable code that hasn't been translated
|
||||
stack.truncate(frame.original_stack_size());
|
||||
|
||||
let reachable_anyway = match frame {
|
||||
// If it is a loop we also have to seal the body loop block
|
||||
ControlStackFrame::Loop { header, .. } => {
|
||||
builder.seal_block(header);
|
||||
// And loops can't have branches to the end.
|
||||
false
|
||||
}
|
||||
ControlStackFrame::If { reachable_from_top, .. } => {
|
||||
// A reachable if without an else has a branch from the top
|
||||
// directly to the bottom.
|
||||
reachable_from_top
|
||||
}
|
||||
// All other control constructs are already handled.
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if frame.exit_is_branched_to() || reachable_anyway {
|
||||
builder.switch_to_block(frame.following_code());
|
||||
builder.seal_block(frame.following_code());
|
||||
|
||||
// And add the return values of the block but only if the next block is reachable
|
||||
// (which corresponds to testing if the stack depth is 1)
|
||||
stack.extend_from_slice(builder.ebb_params(frame.following_code()));
|
||||
state.reachable = true;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
@@ -985,12 +1022,12 @@ fn get_heap_addr(
|
||||
addr32: ir::Value,
|
||||
offset: u32,
|
||||
addr_ty: ir::Type,
|
||||
builder: &mut FunctionBuilder<Local>,
|
||||
builder: &mut FunctionBuilder<Variable>,
|
||||
) -> (ir::Value, i32) {
|
||||
use std::cmp::min;
|
||||
|
||||
let guard_size: i64 = builder.func.heaps[heap].guard_size.into();
|
||||
assert!(guard_size > 0, "Heap guard pages currently required");
|
||||
debug_assert!(guard_size > 0, "Heap guard pages currently required");
|
||||
|
||||
// Generate `heap_addr` instructions that are friendly to CSE by checking offsets that are
|
||||
// multiples of the guard size. Add one to make sure that we check the pointer itself is in
|
||||
@@ -1021,7 +1058,7 @@ fn translate_load<FE: FuncEnvironment + ?Sized>(
|
||||
offset: u32,
|
||||
opcode: ir::Opcode,
|
||||
result_ty: ir::Type,
|
||||
builder: &mut FunctionBuilder<Local>,
|
||||
builder: &mut FunctionBuilder<Variable>,
|
||||
state: &mut TranslationState,
|
||||
environ: &mut FE,
|
||||
) {
|
||||
@@ -1044,7 +1081,7 @@ fn translate_load<FE: FuncEnvironment + ?Sized>(
|
||||
fn translate_store<FE: FuncEnvironment + ?Sized>(
|
||||
offset: u32,
|
||||
opcode: ir::Opcode,
|
||||
builder: &mut FunctionBuilder<Local>,
|
||||
builder: &mut FunctionBuilder<Variable>,
|
||||
state: &mut TranslationState,
|
||||
environ: &mut FE,
|
||||
) {
|
||||
|
||||
@@ -148,7 +148,7 @@ pub trait FuncEnvironment {
|
||||
) -> ir::Value;
|
||||
}
|
||||
|
||||
/// An object satisfyng the `ModuleEnvironment` trait can be passed as argument to the
|
||||
/// An object satisfying the `ModuleEnvironment` trait can be passed as argument to the
|
||||
/// [`translate_module`](fn.translate_module.html) function. These methods should not be called
|
||||
/// by the user, they are only for `cretonne-wasm` internal use.
|
||||
pub trait ModuleEnvironment<'data> {
|
||||
|
||||
@@ -9,10 +9,9 @@ use cretonne::entity::EntityRef;
|
||||
use cretonne::ir::{self, InstBuilder, Ebb};
|
||||
use cretonne::result::{CtonResult, CtonError};
|
||||
use cretonne::timing;
|
||||
use cton_frontend::{ILBuilder, FunctionBuilder};
|
||||
use cton_frontend::{ILBuilder, FunctionBuilder, Variable};
|
||||
use environ::FuncEnvironment;
|
||||
use state::TranslationState;
|
||||
use translation_utils::Local;
|
||||
use wasmparser::{self, BinaryReader};
|
||||
|
||||
/// WebAssembly to Cretonne IL function translator.
|
||||
@@ -21,7 +20,7 @@ use wasmparser::{self, BinaryReader};
|
||||
/// by a `FuncEnvironment` object. A single translator instance can be reused to translate multiple
|
||||
/// functions which will reduce heap allocation traffic.
|
||||
pub struct FuncTranslator {
|
||||
il_builder: ILBuilder<Local>,
|
||||
il_builder: ILBuilder<Variable>,
|
||||
state: TranslationState,
|
||||
}
|
||||
|
||||
@@ -45,7 +44,7 @@ impl FuncTranslator {
|
||||
///
|
||||
/// See [the WebAssembly specification][wasm].
|
||||
///
|
||||
/// [wasm]: http://webassembly.github.io/spec/binary/modules.html#code-section
|
||||
/// [wasm]: https://webassembly.github.io/spec/binary/modules.html#code-section
|
||||
///
|
||||
/// The Cretonne IR function `func` should be completely empty except for the `func.signature`
|
||||
/// and `func.name` fields. The signature may contain special-purpose arguments which are not
|
||||
@@ -75,8 +74,8 @@ impl FuncTranslator {
|
||||
func.name,
|
||||
func.signature
|
||||
);
|
||||
assert_eq!(func.dfg.num_ebbs(), 0, "Function must be empty");
|
||||
assert_eq!(func.dfg.num_insts(), 0, "Function must be empty");
|
||||
debug_assert_eq!(func.dfg.num_ebbs(), 0, "Function must be empty");
|
||||
debug_assert_eq!(func.dfg.num_insts(), 0, "Function must be empty");
|
||||
|
||||
// This clears the `ILBuilder`.
|
||||
let mut builder = FunctionBuilder::new(func, &mut self.il_builder);
|
||||
@@ -107,7 +106,7 @@ impl FuncTranslator {
|
||||
/// Declare local variables for the signature parameters that correspond to WebAssembly locals.
|
||||
///
|
||||
/// Return the number of local variables declared.
|
||||
fn declare_wasm_parameters(builder: &mut FunctionBuilder<Local>, entry_block: Ebb) -> usize {
|
||||
fn declare_wasm_parameters(builder: &mut FunctionBuilder<Variable>, entry_block: Ebb) -> usize {
|
||||
let sig_len = builder.func.signature.params.len();
|
||||
let mut next_local = 0;
|
||||
for i in 0..sig_len {
|
||||
@@ -116,7 +115,7 @@ fn declare_wasm_parameters(builder: &mut FunctionBuilder<Local>, entry_block: Eb
|
||||
// signature parameters. For example, a `vmctx` pointer.
|
||||
if param_type.purpose == ir::ArgumentPurpose::Normal {
|
||||
// This is a normal WebAssembly signature parameter, so create a local for it.
|
||||
let local = Local::new(next_local);
|
||||
let local = Variable::new(next_local);
|
||||
builder.declare_var(local, param_type.value_type);
|
||||
next_local += 1;
|
||||
|
||||
@@ -133,7 +132,7 @@ fn declare_wasm_parameters(builder: &mut FunctionBuilder<Local>, entry_block: Eb
|
||||
/// Declare local variables, starting from `num_params`.
|
||||
fn parse_local_decls(
|
||||
reader: &mut BinaryReader,
|
||||
builder: &mut FunctionBuilder<Local>,
|
||||
builder: &mut FunctionBuilder<Variable>,
|
||||
num_params: usize,
|
||||
) -> CtonResult {
|
||||
let mut next_local = num_params;
|
||||
@@ -157,7 +156,7 @@ fn parse_local_decls(
|
||||
///
|
||||
/// Fail of too many locals are declared in the function, or if the type is not valid for a local.
|
||||
fn declare_locals(
|
||||
builder: &mut FunctionBuilder<Local>,
|
||||
builder: &mut FunctionBuilder<Variable>,
|
||||
count: u32,
|
||||
wasm_type: wasmparser::Type,
|
||||
next_local: &mut usize,
|
||||
@@ -174,7 +173,7 @@ fn declare_locals(
|
||||
|
||||
let ty = builder.func.dfg.value_type(zeroval);
|
||||
for _ in 0..count {
|
||||
let local = Local::new(*next_local);
|
||||
let local = Variable::new(*next_local);
|
||||
builder.declare_var(local, ty);
|
||||
builder.def_var(local, zeroval);
|
||||
*next_local += 1;
|
||||
@@ -187,18 +186,18 @@ fn declare_locals(
|
||||
/// arguments and locals are declared in the builder.
|
||||
fn parse_function_body<FE: FuncEnvironment + ?Sized>(
|
||||
mut reader: BinaryReader,
|
||||
builder: &mut FunctionBuilder<Local>,
|
||||
builder: &mut FunctionBuilder<Variable>,
|
||||
state: &mut TranslationState,
|
||||
environ: &mut FE,
|
||||
) -> CtonResult {
|
||||
// The control stack is initialized with a single block representing the whole function.
|
||||
assert_eq!(state.control_stack.len(), 1, "State not initialized");
|
||||
debug_assert_eq!(state.control_stack.len(), 1, "State not initialized");
|
||||
|
||||
// Keep going until the final `End` operator which pops the outermost block.
|
||||
while !state.control_stack.is_empty() {
|
||||
builder.set_srcloc(cur_srcloc(&reader));
|
||||
let op = reader.read_operator().map_err(|_| CtonError::InvalidInput)?;
|
||||
translate_operator(&op, builder, state, environ);
|
||||
translate_operator(op, builder, state, environ);
|
||||
}
|
||||
|
||||
// The final `End` operator left us in the exit block where we need to manually add a return
|
||||
@@ -206,9 +205,11 @@ fn parse_function_body<FE: FuncEnvironment + ?Sized>(
|
||||
//
|
||||
// If the exit block is unreachable, it may not have the correct arguments, so we would
|
||||
// generate a return instruction that doesn't match the signature.
|
||||
debug_assert!(builder.is_pristine());
|
||||
if !builder.is_unreachable() {
|
||||
builder.ins().return_(&state.stack);
|
||||
if state.reachable {
|
||||
debug_assert!(builder.is_pristine());
|
||||
if !builder.is_unreachable() {
|
||||
builder.ins().return_(&state.stack);
|
||||
}
|
||||
}
|
||||
|
||||
// Discard any remaining values on the stack. Either we just returned them,
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
//!
|
||||
//! The main function of this module is [`translate_module`](fn.translate_module.html).
|
||||
|
||||
#![deny(missing_docs)]
|
||||
#![deny(missing_docs,
|
||||
trivial_numeric_casts,
|
||||
unused_extern_crates)]
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![cfg_attr(not(feature = "std"), feature(alloc))]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Translation skeletton that traverses the whole WebAssembly module and call helper functions
|
||||
//! Translation skeleton that traverses the whole WebAssembly module and call helper functions
|
||||
//! to deal with each part of it.
|
||||
use cretonne::timing;
|
||||
use wasmparser::{ParserState, SectionCode, ParserInput, Parser, WasmDecoder, BinaryReaderError};
|
||||
@@ -126,6 +126,10 @@ pub fn translate_module<'data>(
|
||||
}
|
||||
}
|
||||
}
|
||||
ParserState::BeginSection { code: SectionCode::Custom { .. }, .. } => {
|
||||
// Ignore unknown custom sections.
|
||||
next_input = ParserInput::SkipSection;
|
||||
}
|
||||
_ => return Err(String::from("wrong content in the preamble")),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ pub fn parse_import_section<'data>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Retrieves the correspondances between functions and signatures from the function section
|
||||
/// Retrieves the correspondences between functions and signatures from the function section
|
||||
pub fn parse_function_section(
|
||||
parser: &mut Parser,
|
||||
environ: &mut ModuleEnvironment,
|
||||
|
||||
@@ -26,20 +26,20 @@ pub enum ControlStackFrame {
|
||||
branch_inst: Inst,
|
||||
num_return_values: usize,
|
||||
original_stack_size: usize,
|
||||
reachable: bool,
|
||||
exit_is_branched_to: bool,
|
||||
reachable_from_top: bool,
|
||||
},
|
||||
Block {
|
||||
destination: Ebb,
|
||||
num_return_values: usize,
|
||||
original_stack_size: usize,
|
||||
reachable: bool,
|
||||
exit_is_branched_to: bool,
|
||||
},
|
||||
Loop {
|
||||
destination: Ebb,
|
||||
header: Ebb,
|
||||
num_return_values: usize,
|
||||
original_stack_size: usize,
|
||||
reachable: bool,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -81,19 +81,21 @@ impl ControlStackFrame {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_reachable(&self) -> bool {
|
||||
pub fn exit_is_branched_to(&self) -> bool {
|
||||
match *self {
|
||||
ControlStackFrame::If { reachable, .. } |
|
||||
ControlStackFrame::Block { reachable, .. } |
|
||||
ControlStackFrame::Loop { reachable, .. } => reachable,
|
||||
ControlStackFrame::If { exit_is_branched_to, .. } |
|
||||
ControlStackFrame::Block { exit_is_branched_to, .. } => exit_is_branched_to,
|
||||
ControlStackFrame::Loop { .. } => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_reachable(&mut self) {
|
||||
pub fn set_branched_to_exit(&mut self) {
|
||||
match *self {
|
||||
ControlStackFrame::If { ref mut reachable, .. } |
|
||||
ControlStackFrame::Block { ref mut reachable, .. } |
|
||||
ControlStackFrame::Loop { ref mut reachable, .. } => *reachable = true,
|
||||
ControlStackFrame::If { ref mut exit_is_branched_to, .. } |
|
||||
ControlStackFrame::Block { ref mut exit_is_branched_to, .. } => {
|
||||
*exit_is_branched_to = true
|
||||
}
|
||||
ControlStackFrame::Loop { .. } => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,8 +108,7 @@ impl ControlStackFrame {
|
||||
pub struct TranslationState {
|
||||
pub stack: Vec<Value>,
|
||||
pub control_stack: Vec<ControlStackFrame>,
|
||||
pub phantom_unreachable_stack_depth: usize,
|
||||
pub real_unreachable_stack_depth: usize,
|
||||
pub reachable: bool,
|
||||
|
||||
// Map of global variables that have already been created by `FuncEnvironment::make_global`.
|
||||
globals: HashMap<GlobalIndex, GlobalValue>,
|
||||
@@ -131,8 +132,7 @@ impl TranslationState {
|
||||
Self {
|
||||
stack: Vec::new(),
|
||||
control_stack: Vec::new(),
|
||||
phantom_unreachable_stack_depth: 0,
|
||||
real_unreachable_stack_depth: 0,
|
||||
reachable: true,
|
||||
globals: HashMap::new(),
|
||||
heaps: HashMap::new(),
|
||||
signatures: HashMap::new(),
|
||||
@@ -143,8 +143,7 @@ impl TranslationState {
|
||||
fn clear(&mut self) {
|
||||
debug_assert!(self.stack.is_empty());
|
||||
debug_assert!(self.control_stack.is_empty());
|
||||
debug_assert_eq!(self.phantom_unreachable_stack_depth, 0);
|
||||
debug_assert_eq!(self.real_unreachable_stack_depth, 0);
|
||||
self.reachable = true;
|
||||
self.globals.clear();
|
||||
self.heaps.clear();
|
||||
self.signatures.clear();
|
||||
@@ -220,7 +219,7 @@ impl TranslationState {
|
||||
destination: following_code,
|
||||
original_stack_size: self.stack.len(),
|
||||
num_return_values: num_result_types,
|
||||
reachable: false,
|
||||
exit_is_branched_to: false,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -231,7 +230,6 @@ impl TranslationState {
|
||||
destination: following_code,
|
||||
original_stack_size: self.stack.len(),
|
||||
num_return_values: num_result_types,
|
||||
reachable: false,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -242,19 +240,10 @@ impl TranslationState {
|
||||
destination: following_code,
|
||||
original_stack_size: self.stack.len(),
|
||||
num_return_values: num_result_types,
|
||||
reachable: false,
|
||||
exit_is_branched_to: false,
|
||||
reachable_from_top: self.reachable,
|
||||
});
|
||||
}
|
||||
|
||||
/// Test if the translation state is currently in unreachable code.
|
||||
pub fn in_unreachable_code(&self) -> bool {
|
||||
if self.real_unreachable_stack_depth > 0 {
|
||||
true
|
||||
} else {
|
||||
debug_assert_eq!(self.phantom_unreachable_stack_depth, 0, "in reachable code");
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Methods for handling entity references.
|
||||
|
||||
@@ -71,25 +71,6 @@ pub struct Memory {
|
||||
pub shared: bool,
|
||||
}
|
||||
|
||||
/// Wrapper to a `get_local` and `set_local` index. They are WebAssembly's non-SSA variables.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct Local(pub u32);
|
||||
impl cretonne::entity::EntityRef for Local {
|
||||
fn new(index: usize) -> Self {
|
||||
debug_assert!(index < (u32::MAX as usize));
|
||||
Local(index as u32)
|
||||
}
|
||||
|
||||
fn index(self) -> usize {
|
||||
self.0 as usize
|
||||
}
|
||||
}
|
||||
impl Default for Local {
|
||||
fn default() -> Self {
|
||||
Local(u32::MAX)
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function translating wasmparser types to Cretonne types when possible.
|
||||
pub fn type_to_type(ty: &wasmparser::Type) -> Result<cretonne::ir::Type, ()> {
|
||||
match *ty {
|
||||
|
||||
Reference in New Issue
Block a user