Merge remote-tracking branch 'origin/master' into no_std
This commit is contained in:
@@ -1,20 +1,20 @@
|
||||
[package]
|
||||
name = "cretonne-wasm"
|
||||
version = "0.3.4"
|
||||
version = "0.4.1"
|
||||
authors = ["The Cretonne Project Developers"]
|
||||
description = "Translator from WebAssembly to Cretonne IL"
|
||||
description = "Translator from WebAssembly to Cretonne IR"
|
||||
repository = "https://github.com/Cretonne/cretonne"
|
||||
license = "Apache-2.0"
|
||||
readme = "README.md"
|
||||
keywords = [ "webassembly", "wasm" ]
|
||||
keywords = ["webassembly", "wasm"]
|
||||
|
||||
[lib]
|
||||
name = "cton_wasm"
|
||||
|
||||
[dependencies]
|
||||
wasmparser = "0.14.1"
|
||||
cretonne = { path = "../cretonne", version = "0.3.4", default_features = false }
|
||||
cretonne-frontend = { path = "../frontend", version = "0.3.4", default_features = false }
|
||||
wasmparser = "0.15.1"
|
||||
cretonne = { path = "../cretonne", version = "0.4.1", default_features = false }
|
||||
cretonne-frontend = { path = "../frontend", version = "0.4.1", default_features = false }
|
||||
|
||||
[dependencies.hashmap_core]
|
||||
version = "0.1.1"
|
||||
@@ -27,3 +27,7 @@ tempdir = "0.3.5"
|
||||
default = ["std"]
|
||||
std = ["cretonne/std", "cretonne-frontend/std"]
|
||||
core = ["hashmap_core", "cretonne/core", "cretonne-frontend/core"]
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "experimental" }
|
||||
travis-ci = { repository = "Cretonne/cretonne" }
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
This crate performs the translation from a wasm module in binary format to the
|
||||
in-memory representation of the [Cretonne](https://crates.io/crates/cretonne)
|
||||
IL.
|
||||
in-memory form of the [Cretonne](https://crates.io/crates/cretonne) IR.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
extern crate cton_wasm;
|
||||
extern crate cretonne;
|
||||
extern crate cton_wasm;
|
||||
extern crate tempdir;
|
||||
|
||||
use cton_wasm::{translate_module, DummyEnvironment};
|
||||
use std::path::PathBuf;
|
||||
use std::fs::File;
|
||||
use std::error::Error;
|
||||
use std::io;
|
||||
use std::str;
|
||||
use std::io::prelude::*;
|
||||
use std::process::Command;
|
||||
use std::fs;
|
||||
use cretonne::ir;
|
||||
use cretonne::ir::entities::AnyEntity;
|
||||
use cretonne::isa::TargetIsa;
|
||||
use cretonne::print_errors::pretty_verifier_error;
|
||||
use cretonne::settings::{self, Configurable, Flags};
|
||||
use cretonne::verifier;
|
||||
use cton_wasm::{translate_module, DummyEnvironment};
|
||||
use std::error::Error;
|
||||
use std::fs;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::str;
|
||||
use tempdir::TempDir;
|
||||
|
||||
#[test]
|
||||
@@ -27,7 +25,7 @@ fn testsuite() {
|
||||
// Ignore files starting with `.`, which could be editor temporary files
|
||||
if let Some(stem) = p.path().file_stem() {
|
||||
if let Some(stemstr) = stem.to_str() {
|
||||
return !stemstr.starts_with(".");
|
||||
return !stemstr.starts_with('.');
|
||||
}
|
||||
}
|
||||
false
|
||||
@@ -37,7 +35,7 @@ fn testsuite() {
|
||||
let flags = Flags::new(&settings::builder());
|
||||
for path in paths {
|
||||
let path = path.path();
|
||||
handle_module(path, &flags);
|
||||
handle_module(&path, &flags);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +44,7 @@ fn return_at_end() {
|
||||
let mut flag_builder = settings::builder();
|
||||
flag_builder.enable("return_at_end").unwrap();
|
||||
let flags = Flags::new(&flag_builder);
|
||||
handle_module(PathBuf::from("../../wasmtests/return_at_end.wat"), &flags);
|
||||
handle_module(&PathBuf::from("../../wasmtests/return_at_end.wat"), &flags);
|
||||
}
|
||||
|
||||
fn read_wasm_file(path: PathBuf) -> Result<Vec<u8>, io::Error> {
|
||||
@@ -56,7 +54,7 @@ fn read_wasm_file(path: PathBuf) -> Result<Vec<u8>, io::Error> {
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
fn handle_module(path: PathBuf, flags: &Flags) {
|
||||
fn handle_module(path: &PathBuf, flags: &Flags) {
|
||||
let data = match path.extension() {
|
||||
None => {
|
||||
panic!("the file extension is not wasm or wat");
|
||||
@@ -105,29 +103,7 @@ fn handle_module(path: PathBuf, flags: &Flags) {
|
||||
translate_module(&data, &mut dummy_environ).unwrap();
|
||||
for func in &dummy_environ.info.function_bodies {
|
||||
verifier::verify_function(func, flags)
|
||||
.map_err(|err| panic!(pretty_verifier_error(func, None, err)))
|
||||
.map_err(|err| panic!(pretty_verifier_error(func, None, &err)))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Pretty-print a verifier error.
|
||||
pub fn pretty_verifier_error(
|
||||
func: &ir::Function,
|
||||
isa: Option<&TargetIsa>,
|
||||
err: verifier::Error,
|
||||
) -> String {
|
||||
let msg = err.to_string();
|
||||
let str1 = match err.location {
|
||||
AnyEntity::Inst(inst) => {
|
||||
format!(
|
||||
"{}\n{}: {}\n\n",
|
||||
msg,
|
||||
inst,
|
||||
func.dfg.display_inst(inst, isa)
|
||||
)
|
||||
}
|
||||
_ => String::from(format!("{}\n", msg)),
|
||||
};
|
||||
format!("{}{}", str1, func.display(isa))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user