Merge remote-tracking branch 'origin/master' into no_std

This commit is contained in:
Dan Gohman
2018-03-12 12:55:57 -07:00
138 changed files with 3795 additions and 1168 deletions

View File

@@ -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,
) {

View File

@@ -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> {

View File

@@ -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,

View File

@@ -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))]

View File

@@ -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")),
};
}

View File

@@ -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,

View File

@@ -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.

View File

@@ -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 {