Merge remote-tracking branch 'origin/master' into no_std
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
//! This module contains the bulk of the interesting code performing the translation between
|
||||
//! WebAssembly and Cretonne IL.
|
||||
//! WebAssembly and Cretonne IR.
|
||||
//!
|
||||
//! The translation is done in one pass, opcode by opcode. Two main data structures are used during
|
||||
//! code translations: the value stack and the control stack. The value stack mimics the execution
|
||||
@@ -22,21 +22,23 @@
|
||||
//!
|
||||
//! That is why `translate_function_body` takes an object having the `WasmRuntime` trait as
|
||||
//! argument.
|
||||
use cretonne::ir::{self, InstBuilder, MemFlags, JumpTableData};
|
||||
use cretonne::ir::condcodes::{FloatCC, IntCC};
|
||||
use cretonne::ir::types::*;
|
||||
use cretonne::ir::condcodes::{IntCC, FloatCC};
|
||||
use cretonne::ir::{self, InstBuilder, JumpTableData, MemFlags};
|
||||
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};
|
||||
use translation_utils::{TableIndex, SignatureIndex, FunctionIndex, MemoryIndex};
|
||||
use state::{TranslationState, ControlStackFrame};
|
||||
use std::collections::{HashMap, hash_map};
|
||||
use environ::{FuncEnvironment, GlobalValue};
|
||||
use std::{i32, u32};
|
||||
use state::{ControlStackFrame, TranslationState};
|
||||
use std::collections::{hash_map, HashMap};
|
||||
use std::vec::Vec;
|
||||
use std::{i32, u32};
|
||||
use translation_utils::{FunctionIndex, MemoryIndex, SignatureIndex, TableIndex};
|
||||
use translation_utils::{num_return_values, type_to_type, f32_translation, f64_translation};
|
||||
use wasmparser::{MemoryImmediate, Operator};
|
||||
|
||||
/// Translates wasm operators into Cretonne IL instructions. Returns `true` if it inserted
|
||||
// Clippy warns about "flags: _" but its important to document that the flags field is ignored
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(unneeded_field_pattern))]
|
||||
/// Translates wasm operators into Cretonne IR instructions. Returns `true` if it inserted
|
||||
/// a return.
|
||||
pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
op: Operator,
|
||||
@@ -45,7 +47,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
environ: &mut FE,
|
||||
) {
|
||||
if !state.reachable {
|
||||
return translate_unreachable_operator(op, builder, state);
|
||||
return translate_unreachable_operator(&op, builder, state);
|
||||
}
|
||||
|
||||
// This big match treats all Wasm code operators.
|
||||
@@ -73,8 +75,9 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
GlobalValue::Const(val) => val,
|
||||
GlobalValue::Memory { gv, ty } => {
|
||||
let addr = builder.ins().global_addr(environ.native_pointer(), gv);
|
||||
// TODO: It is likely safe to set `aligned notrap` flags on a global load.
|
||||
let flags = ir::MemFlags::new();
|
||||
let mut flags = ir::MemFlags::new();
|
||||
flags.set_notrap();
|
||||
flags.set_aligned();
|
||||
builder.ins().load(ty, flags, addr, 0)
|
||||
}
|
||||
};
|
||||
@@ -85,8 +88,9 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
GlobalValue::Const(_) => panic!("global #{} is a constant", global_index),
|
||||
GlobalValue::Memory { gv, .. } => {
|
||||
let addr = builder.ins().global_addr(environ.native_pointer(), gv);
|
||||
// TODO: It is likely safe to set `aligned notrap` flags on a global store.
|
||||
let flags = ir::MemFlags::new();
|
||||
let mut flags = ir::MemFlags::new();
|
||||
flags.set_notrap();
|
||||
flags.set_aligned();
|
||||
let val = state.pop1();
|
||||
builder.ins().store(flags, val, addr, 0);
|
||||
}
|
||||
@@ -138,6 +142,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
builder.ins().jump(loop_body, &[]);
|
||||
state.push_loop(loop_body, next, num_return_values(ty));
|
||||
builder.switch_to_block(loop_body);
|
||||
environ.translate_loop_header(builder.cursor());
|
||||
}
|
||||
Operator::If { ty } => {
|
||||
let val = state.pop1();
|
||||
@@ -187,8 +192,8 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
}
|
||||
Operator::End => {
|
||||
let frame = state.control_stack.pop().unwrap();
|
||||
let return_count = frame.num_return_values();
|
||||
if !builder.is_unreachable() || !builder.is_pristine() {
|
||||
let return_count = frame.num_return_values();
|
||||
builder.ins().jump(
|
||||
frame.following_code(),
|
||||
state.peekn(return_count),
|
||||
@@ -197,9 +202,8 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
builder.switch_to_block(frame.following_code());
|
||||
builder.seal_block(frame.following_code());
|
||||
// If it is a loop we also have to seal the body loop block
|
||||
match frame {
|
||||
ControlStackFrame::Loop { header, .. } => builder.seal_block(header),
|
||||
_ => {}
|
||||
if let ControlStackFrame::Loop { header, .. } = frame {
|
||||
builder.seal_block(header)
|
||||
}
|
||||
state.stack.truncate(frame.original_stack_size());
|
||||
state.stack.extend_from_slice(
|
||||
@@ -247,27 +251,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
state.popn(return_count);
|
||||
state.reachable = false;
|
||||
}
|
||||
Operator::BrIf { relative_depth } => {
|
||||
let val = state.pop1();
|
||||
let i = state.control_stack.len() - 1 - (relative_depth as usize);
|
||||
let (return_count, br_destination) = {
|
||||
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_branched_to_exit();
|
||||
let return_count = if frame.is_loop() {
|
||||
0
|
||||
} else {
|
||||
frame.num_return_values()
|
||||
};
|
||||
(return_count, frame.br_destination())
|
||||
};
|
||||
builder.ins().brnz(
|
||||
val,
|
||||
br_destination,
|
||||
state.peekn(return_count),
|
||||
);
|
||||
}
|
||||
Operator::BrIf { relative_depth } => translate_br_if(relative_depth, builder, state),
|
||||
Operator::BrTable { table } => {
|
||||
let (depths, default) = table.read_table();
|
||||
let mut min_depth = default;
|
||||
@@ -765,101 +749,45 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
}
|
||||
/**************************** Comparison Operators **********************************/
|
||||
Operator::I32LtS | Operator::I64LtS => {
|
||||
let (arg1, arg2) = state.pop2();
|
||||
let val = builder.ins().icmp(IntCC::SignedLessThan, arg1, arg2);
|
||||
state.push1(builder.ins().bint(I32, val));
|
||||
translate_icmp(IntCC::SignedLessThan, builder, state)
|
||||
}
|
||||
Operator::I32LtU | Operator::I64LtU => {
|
||||
let (arg1, arg2) = state.pop2();
|
||||
let val = builder.ins().icmp(IntCC::UnsignedLessThan, arg1, arg2);
|
||||
state.push1(builder.ins().bint(I32, val));
|
||||
translate_icmp(IntCC::UnsignedLessThan, builder, state)
|
||||
}
|
||||
Operator::I32LeS | Operator::I64LeS => {
|
||||
let (arg1, arg2) = state.pop2();
|
||||
let val = builder.ins().icmp(IntCC::SignedLessThanOrEqual, arg1, arg2);
|
||||
state.push1(builder.ins().bint(I32, val));
|
||||
translate_icmp(IntCC::SignedLessThanOrEqual, builder, state)
|
||||
}
|
||||
Operator::I32LeU | Operator::I64LeU => {
|
||||
let (arg1, arg2) = state.pop2();
|
||||
let val = builder.ins().icmp(
|
||||
IntCC::UnsignedLessThanOrEqual,
|
||||
arg1,
|
||||
arg2,
|
||||
);
|
||||
state.push1(builder.ins().bint(I32, val));
|
||||
translate_icmp(IntCC::UnsignedLessThanOrEqual, builder, state)
|
||||
}
|
||||
Operator::I32GtS | Operator::I64GtS => {
|
||||
let (arg1, arg2) = state.pop2();
|
||||
let val = builder.ins().icmp(IntCC::SignedGreaterThan, arg1, arg2);
|
||||
state.push1(builder.ins().bint(I32, val));
|
||||
translate_icmp(IntCC::SignedGreaterThan, builder, state)
|
||||
}
|
||||
Operator::I32GtU | Operator::I64GtU => {
|
||||
let (arg1, arg2) = state.pop2();
|
||||
let val = builder.ins().icmp(IntCC::UnsignedGreaterThan, arg1, arg2);
|
||||
state.push1(builder.ins().bint(I32, val));
|
||||
translate_icmp(IntCC::UnsignedGreaterThan, builder, state)
|
||||
}
|
||||
Operator::I32GeS | Operator::I64GeS => {
|
||||
let (arg1, arg2) = state.pop2();
|
||||
let val = builder.ins().icmp(
|
||||
IntCC::SignedGreaterThanOrEqual,
|
||||
arg1,
|
||||
arg2,
|
||||
);
|
||||
state.push1(builder.ins().bint(I32, val));
|
||||
translate_icmp(IntCC::SignedGreaterThanOrEqual, builder, state)
|
||||
}
|
||||
Operator::I32GeU | Operator::I64GeU => {
|
||||
let (arg1, arg2) = state.pop2();
|
||||
let val = builder.ins().icmp(
|
||||
IntCC::UnsignedGreaterThanOrEqual,
|
||||
arg1,
|
||||
arg2,
|
||||
);
|
||||
state.push1(builder.ins().bint(I32, val));
|
||||
translate_icmp(IntCC::UnsignedGreaterThanOrEqual, builder, state)
|
||||
}
|
||||
Operator::I32Eqz | Operator::I64Eqz => {
|
||||
let arg = state.pop1();
|
||||
let val = builder.ins().icmp_imm(IntCC::Equal, arg, 0);
|
||||
state.push1(builder.ins().bint(I32, val));
|
||||
}
|
||||
Operator::I32Eq | Operator::I64Eq => {
|
||||
let (arg1, arg2) = state.pop2();
|
||||
let val = builder.ins().icmp(IntCC::Equal, arg1, arg2);
|
||||
state.push1(builder.ins().bint(I32, val));
|
||||
}
|
||||
Operator::F32Eq | Operator::F64Eq => {
|
||||
let (arg1, arg2) = state.pop2();
|
||||
let val = builder.ins().fcmp(FloatCC::Equal, arg1, arg2);
|
||||
state.push1(builder.ins().bint(I32, val));
|
||||
}
|
||||
Operator::I32Ne | Operator::I64Ne => {
|
||||
let (arg1, arg2) = state.pop2();
|
||||
let val = builder.ins().icmp(IntCC::NotEqual, arg1, arg2);
|
||||
state.push1(builder.ins().bint(I32, val));
|
||||
}
|
||||
Operator::F32Ne | Operator::F64Ne => {
|
||||
let (arg1, arg2) = state.pop2();
|
||||
let val = builder.ins().fcmp(FloatCC::NotEqual, arg1, arg2);
|
||||
state.push1(builder.ins().bint(I32, val));
|
||||
}
|
||||
Operator::F32Gt | Operator::F64Gt => {
|
||||
let (arg1, arg2) = state.pop2();
|
||||
let val = builder.ins().fcmp(FloatCC::GreaterThan, arg1, arg2);
|
||||
state.push1(builder.ins().bint(I32, val));
|
||||
}
|
||||
Operator::I32Eq | Operator::I64Eq => translate_icmp(IntCC::Equal, builder, state),
|
||||
Operator::F32Eq | Operator::F64Eq => translate_fcmp(FloatCC::Equal, builder, state),
|
||||
Operator::I32Ne | Operator::I64Ne => translate_icmp(IntCC::NotEqual, builder, state),
|
||||
Operator::F32Ne | Operator::F64Ne => translate_fcmp(FloatCC::NotEqual, builder, state),
|
||||
Operator::F32Gt | Operator::F64Gt => translate_fcmp(FloatCC::GreaterThan, builder, state),
|
||||
Operator::F32Ge | Operator::F64Ge => {
|
||||
let (arg1, arg2) = state.pop2();
|
||||
let val = builder.ins().fcmp(FloatCC::GreaterThanOrEqual, arg1, arg2);
|
||||
state.push1(builder.ins().bint(I32, val));
|
||||
}
|
||||
Operator::F32Lt | Operator::F64Lt => {
|
||||
let (arg1, arg2) = state.pop2();
|
||||
let val = builder.ins().fcmp(FloatCC::LessThan, arg1, arg2);
|
||||
state.push1(builder.ins().bint(I32, val));
|
||||
translate_fcmp(FloatCC::GreaterThanOrEqual, builder, state)
|
||||
}
|
||||
Operator::F32Lt | Operator::F64Lt => translate_fcmp(FloatCC::LessThan, builder, state),
|
||||
Operator::F32Le | Operator::F64Le => {
|
||||
let (arg1, arg2) = state.pop2();
|
||||
let val = builder.ins().fcmp(FloatCC::LessThanOrEqual, arg1, arg2);
|
||||
state.push1(builder.ins().bint(I32, val));
|
||||
translate_fcmp(FloatCC::LessThanOrEqual, builder, state)
|
||||
}
|
||||
Operator::Wake { .. } |
|
||||
Operator::I32Wait { .. } |
|
||||
@@ -932,15 +860,17 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
}
|
||||
}
|
||||
|
||||
// Clippy warns us of some fields we are deliberately ignoring
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(unneeded_field_pattern))]
|
||||
/// Deals with a Wasm instruction located in an unreachable portion of the code. Most of them
|
||||
/// 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,
|
||||
op: &Operator,
|
||||
builder: &mut FunctionBuilder<Variable>,
|
||||
state: &mut TranslationState,
|
||||
) {
|
||||
match op {
|
||||
match *op {
|
||||
Operator::If { ty: _ } => {
|
||||
// Push a placeholder control stack entry. The if isn't reachable,
|
||||
// so we don't have any branches anywhere.
|
||||
@@ -952,27 +882,25 @@ fn translate_unreachable_operator(
|
||||
}
|
||||
Operator::Else => {
|
||||
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;
|
||||
if let ControlStackFrame::If {
|
||||
branch_inst,
|
||||
ref mut reachable_from_top,
|
||||
..
|
||||
} = state.control_stack[i]
|
||||
{
|
||||
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);
|
||||
}
|
||||
// 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 => {
|
||||
@@ -1016,7 +944,7 @@ fn translate_unreachable_operator(
|
||||
}
|
||||
}
|
||||
|
||||
// Get the address+offset to use for a heap access.
|
||||
/// Get the address+offset to use for a heap access.
|
||||
fn get_heap_addr(
|
||||
heap: ir::Heap,
|
||||
addr32: ir::Value,
|
||||
@@ -1053,7 +981,7 @@ fn get_heap_addr(
|
||||
}
|
||||
}
|
||||
|
||||
// Translate a load instruction.
|
||||
/// Translate a load instruction.
|
||||
fn translate_load<FE: FuncEnvironment + ?Sized>(
|
||||
offset: u32,
|
||||
opcode: ir::Opcode,
|
||||
@@ -1066,6 +994,9 @@ fn translate_load<FE: FuncEnvironment + ?Sized>(
|
||||
// We don't yet support multiple linear memories.
|
||||
let heap = state.get_heap(builder.func, 0, environ);
|
||||
let (base, offset) = get_heap_addr(heap, addr32, offset, environ.native_pointer(), builder);
|
||||
// Note that we don't set `is_aligned` here, even if the load instruction's
|
||||
// alignment immediate says it's aligned, because WebAssembly's immediate
|
||||
// field is just a hint, while Cretonne's aligned flag needs a guarantee.
|
||||
let flags = MemFlags::new();
|
||||
let (load, dfg) = builder.ins().Load(
|
||||
opcode,
|
||||
@@ -1077,7 +1008,7 @@ fn translate_load<FE: FuncEnvironment + ?Sized>(
|
||||
state.push1(dfg.first_result(load));
|
||||
}
|
||||
|
||||
// Translate a store instruction.
|
||||
/// Translate a store instruction.
|
||||
fn translate_store<FE: FuncEnvironment + ?Sized>(
|
||||
offset: u32,
|
||||
opcode: ir::Opcode,
|
||||
@@ -1091,6 +1022,7 @@ fn translate_store<FE: FuncEnvironment + ?Sized>(
|
||||
// We don't yet support multiple linear memories.
|
||||
let heap = state.get_heap(builder.func, 0, environ);
|
||||
let (base, offset) = get_heap_addr(heap, addr32, offset, environ.native_pointer(), builder);
|
||||
// See the comments in `translate_load` about the flags.
|
||||
let flags = MemFlags::new();
|
||||
builder.ins().Store(
|
||||
opcode,
|
||||
@@ -1101,3 +1033,54 @@ fn translate_store<FE: FuncEnvironment + ?Sized>(
|
||||
base,
|
||||
);
|
||||
}
|
||||
|
||||
fn translate_icmp(
|
||||
cc: IntCC,
|
||||
builder: &mut FunctionBuilder<Variable>,
|
||||
state: &mut TranslationState,
|
||||
) {
|
||||
let (arg0, arg1) = state.pop2();
|
||||
let val = builder.ins().icmp(cc, arg0, arg1);
|
||||
state.push1(builder.ins().bint(I32, val));
|
||||
}
|
||||
|
||||
fn translate_fcmp(
|
||||
cc: FloatCC,
|
||||
builder: &mut FunctionBuilder<Variable>,
|
||||
state: &mut TranslationState,
|
||||
) {
|
||||
let (arg0, arg1) = state.pop2();
|
||||
let val = builder.ins().fcmp(cc, arg0, arg1);
|
||||
state.push1(builder.ins().bint(I32, val));
|
||||
}
|
||||
|
||||
fn translate_br_if(
|
||||
relative_depth: u32,
|
||||
builder: &mut FunctionBuilder<Variable>,
|
||||
state: &mut TranslationState,
|
||||
) {
|
||||
let val = state.pop1();
|
||||
let (br_destination, inputs) = translate_br_if_args(relative_depth, state);
|
||||
builder.ins().brnz(val, br_destination, inputs);
|
||||
}
|
||||
|
||||
fn translate_br_if_args(
|
||||
relative_depth: u32,
|
||||
state: &mut TranslationState,
|
||||
) -> (ir::Ebb, &[ir::Value]) {
|
||||
let i = state.control_stack.len() - 1 - (relative_depth as usize);
|
||||
let (return_count, br_destination) = {
|
||||
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_branched_to_exit();
|
||||
let return_count = if frame.is_loop() {
|
||||
0
|
||||
} else {
|
||||
frame.num_return_values()
|
||||
};
|
||||
(return_count, frame.br_destination())
|
||||
};
|
||||
let inputs = state.peekn(return_count);
|
||||
(br_destination, inputs)
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
//! "Dummy" environment for testing wasm translation.
|
||||
|
||||
use environ::{FuncEnvironment, GlobalValue, ModuleEnvironment};
|
||||
use translation_utils::{Global, Memory, Table, GlobalIndex, TableIndex, SignatureIndex,
|
||||
FunctionIndex, MemoryIndex};
|
||||
use func_translator::FuncTranslator;
|
||||
use cretonne::ir::{self, InstBuilder};
|
||||
use cretonne::ir::types::*;
|
||||
use cretonne::cursor::FuncCursor;
|
||||
use cretonne::ir::types::*;
|
||||
use cretonne::ir::{self, InstBuilder};
|
||||
use cretonne::settings;
|
||||
use wasmparser;
|
||||
use std::vec::Vec;
|
||||
use environ::{FuncEnvironment, GlobalValue, ModuleEnvironment};
|
||||
use func_translator::FuncTranslator;
|
||||
use std::string::String;
|
||||
use std::vec::Vec;
|
||||
use translation_utils::{FunctionIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex,
|
||||
Table, TableIndex};
|
||||
use wasmparser;
|
||||
|
||||
/// Compute a `ir::ExternalName` for a given wasm function index.
|
||||
fn get_func_name(func_index: FunctionIndex) -> ir::ExternalName {
|
||||
@@ -120,7 +120,7 @@ impl DummyEnvironment {
|
||||
}
|
||||
}
|
||||
|
||||
/// The FuncEnvironment implementation for use by the `DummyEnvironment`.
|
||||
/// The `FuncEnvironment` implementation for use by the `DummyEnvironment`.
|
||||
pub struct DummyFuncEnvironment<'dummy_environment> {
|
||||
pub mod_info: &'dummy_environment DummyModuleInfo,
|
||||
}
|
||||
@@ -208,7 +208,10 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
|
||||
let ext = pos.ins().uextend(I64, callee);
|
||||
pos.ins().imul_imm(ext, 4)
|
||||
};
|
||||
let func_ptr = pos.ins().load(ptr, ir::MemFlags::new(), callee_offset, 0);
|
||||
let mut mflags = ir::MemFlags::new();
|
||||
mflags.set_notrap();
|
||||
mflags.set_aligned();
|
||||
let func_ptr = pos.ins().load(ptr, mflags, callee_offset, 0);
|
||||
|
||||
// Build a value list for the indirect call instruction containing the callee, call_args,
|
||||
// and the vmctx parameter.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Support for configurable wasm translation.
|
||||
|
||||
mod spec;
|
||||
mod dummy;
|
||||
mod spec;
|
||||
|
||||
pub use environ::spec::{ModuleEnvironment, FuncEnvironment, GlobalValue};
|
||||
pub use environ::dummy::DummyEnvironment;
|
||||
pub use environ::spec::{FuncEnvironment, GlobalValue, ModuleEnvironment};
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
//! All the runtime support necessary for the wasm to cretonne translation is formalized by the
|
||||
//! traits `FunctionEnvironment` and `ModuleEnvironment`.
|
||||
use cretonne::ir::{self, InstBuilder};
|
||||
use cretonne::cursor::FuncCursor;
|
||||
use cretonne::ir::{self, InstBuilder};
|
||||
use cretonne::settings::Flags;
|
||||
use translation_utils::{SignatureIndex, FunctionIndex, TableIndex, GlobalIndex, MemoryIndex,
|
||||
Global, Table, Memory};
|
||||
use std::vec::Vec;
|
||||
use std::string::String;
|
||||
use std::vec::Vec;
|
||||
use translation_utils::{FunctionIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex,
|
||||
Table, TableIndex};
|
||||
|
||||
/// The value of a WebAssembly global variable.
|
||||
#[derive(Clone, Copy)]
|
||||
@@ -26,7 +26,7 @@ pub enum GlobalValue {
|
||||
/// Environment affecting the translation of a single WebAssembly function.
|
||||
///
|
||||
/// A `FuncEnvironment` trait object is required to translate a WebAssembly function to Cretonne
|
||||
/// IL. The function environment provides information about the WebAssembly module as well as the
|
||||
/// IR. The function environment provides information about the WebAssembly module as well as the
|
||||
/// runtime environment.
|
||||
pub trait FuncEnvironment {
|
||||
/// Get the flags for the current compilation.
|
||||
@@ -146,6 +146,14 @@ pub trait FuncEnvironment {
|
||||
index: MemoryIndex,
|
||||
heap: ir::Heap,
|
||||
) -> ir::Value;
|
||||
|
||||
/// Emit code at the beginning of every wasm loop.
|
||||
///
|
||||
/// This can be used to insert explicit interrupt or safepoint checking at
|
||||
/// the beginnings of loops.
|
||||
fn translate_loop_header(&mut self, _pos: FuncCursor) {
|
||||
// By default, don't emit anything.
|
||||
}
|
||||
}
|
||||
|
||||
/// An object satisfying the `ModuleEnvironment` trait can be passed as argument to the
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
//! Stand-alone WebAssembly to Cretonne IL translator.
|
||||
//! Stand-alone WebAssembly to Cretonne IR translator.
|
||||
//!
|
||||
//! This module defines the `FuncTranslator` type which can translate a single WebAssembly
|
||||
//! function to Cretonne IL guided by a `FuncEnvironment` which provides information about the
|
||||
//! function to Cretonne IR guided by a `FuncEnvironment` which provides information about the
|
||||
//! WebAssembly module and the runtime environment.
|
||||
|
||||
use code_translator::translate_operator;
|
||||
use cretonne::entity::EntityRef;
|
||||
use cretonne::ir::{self, InstBuilder, Ebb};
|
||||
use cretonne::result::{CtonResult, CtonError};
|
||||
use cretonne::ir::{self, Ebb, InstBuilder};
|
||||
use cretonne::result::{CtonError, CtonResult};
|
||||
use cretonne::timing;
|
||||
use cton_frontend::{ILBuilder, FunctionBuilder, Variable};
|
||||
use cton_frontend::{FunctionBuilder, FunctionBuilderContext, Variable};
|
||||
use environ::FuncEnvironment;
|
||||
use state::TranslationState;
|
||||
use wasmparser::{self, BinaryReader};
|
||||
|
||||
/// WebAssembly to Cretonne IL function translator.
|
||||
/// WebAssembly to Cretonne IR function translator.
|
||||
///
|
||||
/// A `FuncTranslator` is used to translate a binary WebAssembly function into Cretonne IL guided
|
||||
/// A `FuncTranslator` is used to translate a binary WebAssembly function into Cretonne IR guided
|
||||
/// 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<Variable>,
|
||||
func_ctx: FunctionBuilderContext<Variable>,
|
||||
state: TranslationState,
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ impl FuncTranslator {
|
||||
/// Create a new translator.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
il_builder: ILBuilder::new(),
|
||||
func_ctx: FunctionBuilderContext::new(),
|
||||
state: TranslationState::new(),
|
||||
}
|
||||
}
|
||||
@@ -77,8 +77,8 @@ impl FuncTranslator {
|
||||
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);
|
||||
// This clears the `FunctionBuilderContext`.
|
||||
let mut builder = FunctionBuilder::new(func, &mut self.func_ctx);
|
||||
let entry_block = builder.create_ebb();
|
||||
builder.append_ebb_params_for_function_params(entry_block);
|
||||
builder.switch_to_block(entry_block); // This also creates values for the arguments.
|
||||
@@ -232,10 +232,10 @@ fn cur_srcloc(reader: &BinaryReader) -> ir::SourceLoc {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use cretonne::{ir, Context};
|
||||
use cretonne::ir::types::I32;
|
||||
use environ::{DummyEnvironment, FuncEnvironment};
|
||||
use super::FuncTranslator;
|
||||
use cretonne::ir::types::I32;
|
||||
use cretonne::{ir, Context};
|
||||
use environ::{DummyEnvironment, FuncEnvironment};
|
||||
|
||||
#[test]
|
||||
fn small1() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//! Performs the translation from a wasm module in binary format to the in-memory representation
|
||||
//! of the Cretonne IL. More particularly, it translates the code of all the functions bodies and
|
||||
//! Performs translation from a wasm module in binary format to the in-memory form
|
||||
//! of Cretonne IR. More particularly, it translates the code of all the functions bodies and
|
||||
//! interacts with an environment implementing the
|
||||
//! [`ModuleEnvironment`](trait.ModuleEnvironment.html)
|
||||
//! trait to deal with tables, globals and linear memory.
|
||||
@@ -9,9 +9,9 @@
|
||||
//!
|
||||
//! The main function of this module is [`translate_module`](fn.translate_module.html).
|
||||
|
||||
#![deny(missing_docs,
|
||||
trivial_numeric_casts,
|
||||
unused_extern_crates)]
|
||||
#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)]
|
||||
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(new_without_default, redundant_field_names))]
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![cfg_attr(not(feature = "std"), feature(alloc))]
|
||||
@@ -24,24 +24,24 @@ extern crate alloc;
|
||||
#[cfg(not(feature = "std"))]
|
||||
extern crate hashmap_core;
|
||||
|
||||
extern crate wasmparser;
|
||||
extern crate cton_frontend;
|
||||
#[macro_use(dbg)]
|
||||
extern crate cretonne;
|
||||
extern crate cton_frontend;
|
||||
extern crate wasmparser;
|
||||
|
||||
mod code_translator;
|
||||
mod environ;
|
||||
mod func_translator;
|
||||
mod module_translator;
|
||||
mod environ;
|
||||
mod sections_translator;
|
||||
mod state;
|
||||
mod translation_utils;
|
||||
|
||||
pub use environ::{DummyEnvironment, FuncEnvironment, GlobalValue, ModuleEnvironment};
|
||||
pub use func_translator::FuncTranslator;
|
||||
pub use module_translator::translate_module;
|
||||
pub use environ::{FuncEnvironment, ModuleEnvironment, DummyEnvironment, GlobalValue};
|
||||
pub use translation_utils::{FunctionIndex, GlobalIndex, TableIndex, MemoryIndex, SignatureIndex,
|
||||
Global, GlobalInit, Table, Memory};
|
||||
pub use translation_utils::{FunctionIndex, Global, GlobalIndex, GlobalInit, Memory, MemoryIndex,
|
||||
SignatureIndex, Table, TableIndex};
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
mod std {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
//! 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};
|
||||
use sections_translator::{SectionParsingError, parse_function_signatures, parse_import_section,
|
||||
parse_function_section, parse_export_section, parse_start_section,
|
||||
parse_memory_section, parse_global_section, parse_table_section,
|
||||
parse_elements_section, parse_data_section};
|
||||
use environ::ModuleEnvironment;
|
||||
use sections_translator::{parse_data_section, parse_elements_section, parse_export_section,
|
||||
parse_function_section, parse_function_signatures, parse_global_section,
|
||||
parse_import_section, parse_memory_section, parse_start_section,
|
||||
parse_table_section, SectionParsingError};
|
||||
use wasmparser::{BinaryReaderError, Parser, ParserInput, ParserState, SectionCode, WasmDecoder};
|
||||
|
||||
use std::string::String;
|
||||
|
||||
/// Translate a sequence of bytes forming a valid Wasm binary into a list of valid Cretonne IL
|
||||
/// Translate a sequence of bytes forming a valid Wasm binary into a list of valid Cretonne IR
|
||||
/// [`Function`](../cretonne/ir/function/struct.Function.html).
|
||||
/// Returns the functions and also the mappings for imported functions and signature between the
|
||||
/// indexes in the wasm module and the indexes inside each functions.
|
||||
|
||||
@@ -7,17 +7,17 @@
|
||||
//! The special case of the initialize expressions for table elements offsets or global variables
|
||||
//! is handled, according to the semantics of WebAssembly, to only specific expressions that are
|
||||
//! interpreted on the fly.
|
||||
use translation_utils::{type_to_type, TableIndex, FunctionIndex, GlobalIndex, SignatureIndex,
|
||||
MemoryIndex, Global, GlobalInit, Table, TableElementType, Memory};
|
||||
use cretonne::ir::{Signature, AbiParam, CallConv};
|
||||
use cretonne;
|
||||
use wasmparser::{Parser, ParserState, FuncType, ImportSectionEntryType, ExternalKind, WasmDecoder,
|
||||
MemoryType, Operator};
|
||||
use wasmparser;
|
||||
use std::str::from_utf8;
|
||||
use cretonne::ir::{AbiParam, CallConv, Signature};
|
||||
use environ::ModuleEnvironment;
|
||||
use std::vec::Vec;
|
||||
use std::str::from_utf8;
|
||||
use std::string::String;
|
||||
use std::vec::Vec;
|
||||
use translation_utils::{type_to_type, FunctionIndex, Global, GlobalIndex, GlobalInit, Memory,
|
||||
MemoryIndex, SignatureIndex, Table, TableElementType, TableIndex};
|
||||
use wasmparser;
|
||||
use wasmparser::{ExternalKind, FuncType, ImportSectionEntryType, MemoryType, Operator, Parser,
|
||||
ParserState, WasmDecoder};
|
||||
|
||||
pub enum SectionParsingError {
|
||||
WrongSectionContent(String),
|
||||
@@ -36,7 +36,7 @@ pub fn parse_function_signatures(
|
||||
ref params,
|
||||
ref returns,
|
||||
}) => {
|
||||
let mut sig = Signature::new(CallConv::Native);
|
||||
let mut sig = Signature::new(CallConv::SystemV);
|
||||
sig.params.extend(params.iter().map(|ty| {
|
||||
let cret_arg: cretonne::ir::Type = type_to_type(ty).expect(
|
||||
"only numeric types are supported in function signatures",
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
use cretonne::ir::{self, Ebb, Inst, Value};
|
||||
use environ::{FuncEnvironment, GlobalValue};
|
||||
use std::collections::HashMap;
|
||||
use translation_utils::{GlobalIndex, MemoryIndex, SignatureIndex, FunctionIndex};
|
||||
use std::vec::Vec;
|
||||
use translation_utils::{FunctionIndex, GlobalIndex, MemoryIndex, SignatureIndex};
|
||||
|
||||
/// A control stack frame can be an `if`, a `block` or a `loop`, each one having the following
|
||||
/// fields:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Helper functions and structures for the translation.
|
||||
use wasmparser;
|
||||
use cretonne;
|
||||
use std::u32;
|
||||
use wasmparser;
|
||||
|
||||
/// Index of a function (imported or defined) inside the WebAssembly module.
|
||||
pub type FunctionIndex = usize;
|
||||
|
||||
Reference in New Issue
Block a user