Add FuncEnvironment trait.
This trait is used to provide the environment necessary to translate a single WebAssembly function without having other global data structures for the WebAssembly module. The WasmRuntime trait extends the FuncEnvironment trait for those uses that want to parse a whole WebAssembly module. - Change the handling of WebAssembly globals to use the FuncEnvironment trait as well as the new GlobalVar infrastructure in Cretonne. The runtime is not consulted on the translation of each get_global/get_global instruction. Instead it gets to create the GlobalVar declaration in the function preamble the first time the global is used. - Change the handling of heap load/store instructions to use the new Heap infrastructure in Cretonne. The runtime is called to create the Heap declaration in the preamble. It is not involved in individual load/store instructions.
This commit is contained in:
@@ -26,7 +26,10 @@ pub struct FunctionBuilder<'a, Variable: 'a>
|
|||||||
where
|
where
|
||||||
Variable: EntityRef + Hash + Default,
|
Variable: EntityRef + Hash + Default,
|
||||||
{
|
{
|
||||||
func: &'a mut Function,
|
/// The function currently being built.
|
||||||
|
/// This field is public so the function can be re-borrowed.
|
||||||
|
pub func: &'a mut Function,
|
||||||
|
|
||||||
builder: &'a mut ILBuilder<Variable>,
|
builder: &'a mut ILBuilder<Variable>,
|
||||||
position: Position,
|
position: Position,
|
||||||
pristine: bool,
|
pristine: bool,
|
||||||
|
|||||||
@@ -21,18 +21,18 @@
|
|||||||
//!
|
//!
|
||||||
//! That is why `translate_function_body` takes an object having the `WasmRuntime` trait as
|
//! That is why `translate_function_body` takes an object having the `WasmRuntime` trait as
|
||||||
//! argument.
|
//! argument.
|
||||||
use cretonne::ir::{Function, Signature, Type, InstBuilder, FunctionName, Ebb, FuncRef, SigRef,
|
use cretonne::ir::{self, Function, Signature, Type, InstBuilder, FunctionName, Ebb, FuncRef,
|
||||||
ExtFuncData, MemFlags};
|
SigRef, ExtFuncData, MemFlags};
|
||||||
use cretonne::ir::types::*;
|
use cretonne::ir::types::*;
|
||||||
use cretonne::ir::immediates::{Ieee32, Ieee64, Offset32};
|
use cretonne::ir::immediates::{Ieee32, Ieee64};
|
||||||
use cretonne::ir::condcodes::{IntCC, FloatCC};
|
use cretonne::ir::condcodes::{IntCC, FloatCC};
|
||||||
use cton_frontend::{ILBuilder, FunctionBuilder};
|
use cton_frontend::{ILBuilder, FunctionBuilder};
|
||||||
use wasmparser::{Parser, ParserState, Operator, WasmDecoder, MemoryImmediate};
|
use wasmparser::{Parser, ParserState, Operator, WasmDecoder, MemoryImmediate};
|
||||||
use translation_utils::{f32_translation, f64_translation, type_to_type, translate_type, Local,
|
use translation_utils::{f32_translation, f64_translation, type_to_type, translate_type, Local,
|
||||||
GlobalIndex, FunctionIndex, SignatureIndex};
|
FunctionIndex, SignatureIndex};
|
||||||
use state::{TranslationState, ControlStackFrame};
|
use state::{TranslationState, ControlStackFrame};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use runtime::WasmRuntime;
|
use runtime::{FuncEnvironment, GlobalValue, WasmRuntime};
|
||||||
use std::u32;
|
use std::u32;
|
||||||
|
|
||||||
|
|
||||||
@@ -194,12 +194,28 @@ fn translate_operator(
|
|||||||
* `get_global` and `set_global` are handled by the runtime.
|
* `get_global` and `set_global` are handled by the runtime.
|
||||||
***********************************************************************************/
|
***********************************************************************************/
|
||||||
Operator::GetGlobal { global_index } => {
|
Operator::GetGlobal { global_index } => {
|
||||||
let val = runtime.translate_get_global(builder, global_index as GlobalIndex);
|
let val = match state.get_global(builder.func, global_index, runtime) {
|
||||||
|
GlobalValue::Const(val) => val,
|
||||||
|
GlobalValue::Memory { gv, ty } => {
|
||||||
|
let addr = builder.ins().global_addr(runtime.native_pointer(), gv);
|
||||||
|
// TODO: It is likely safe to set `aligned notrap` flags on a global load.
|
||||||
|
let flags = ir::MemFlags::new();
|
||||||
|
builder.ins().load(ty, flags, addr, 0)
|
||||||
|
}
|
||||||
|
};
|
||||||
state.push1(val);
|
state.push1(val);
|
||||||
}
|
}
|
||||||
Operator::SetGlobal { global_index } => {
|
Operator::SetGlobal { global_index } => {
|
||||||
let val = state.pop1();
|
match state.get_global(builder.func, global_index, runtime) {
|
||||||
runtime.translate_set_global(builder, global_index as GlobalIndex, val);
|
GlobalValue::Const(_) => panic!("global #{} is a constant", global_index),
|
||||||
|
GlobalValue::Memory { gv, .. } => {
|
||||||
|
let addr = builder.ins().global_addr(runtime.native_pointer(), gv);
|
||||||
|
// TODO: It is likely safe to set `aligned notrap` flags on a global store.
|
||||||
|
let flags = ir::MemFlags::new();
|
||||||
|
let val = state.pop1();
|
||||||
|
builder.ins().store(flags, val, addr, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/********************************* Stack misc ***************************************
|
/********************************* Stack misc ***************************************
|
||||||
* `drop`, `nop`, `unreachable` and `select`.
|
* `drop`, `nop`, `unreachable` and `select`.
|
||||||
@@ -508,130 +524,46 @@ fn translate_operator(
|
|||||||
* TODO: differentiate between 32 bit and 64 bit architecture, to put the uextend or not
|
* TODO: differentiate between 32 bit and 64 bit architecture, to put the uextend or not
|
||||||
************************************************************************************/
|
************************************************************************************/
|
||||||
Operator::I32Load8U { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
Operator::I32Load8U { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
||||||
let address_i32 = state.pop1();
|
translate_load(offset, ir::Opcode::Uload8, I32, builder, state, runtime);
|
||||||
let base = runtime.translate_memory_base_address(builder, 0);
|
|
||||||
let address_i64 = builder.ins().uextend(I64, address_i32);
|
|
||||||
let addr = builder.ins().iadd(base, address_i64);
|
|
||||||
let memflags = MemFlags::new();
|
|
||||||
let memoffset = Offset32::new(offset as i32);
|
|
||||||
state.push1(builder.ins().uload8(I32, memflags, addr, memoffset))
|
|
||||||
}
|
}
|
||||||
Operator::I32Load16U { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
Operator::I32Load16U { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
||||||
let address_i32 = state.pop1();
|
translate_load(offset, ir::Opcode::Uload16, I32, builder, state, runtime);
|
||||||
let base = runtime.translate_memory_base_address(builder, 0);
|
|
||||||
let address_i64 = builder.ins().uextend(I64, address_i32);
|
|
||||||
let addr = builder.ins().iadd(base, address_i64);
|
|
||||||
let memflags = MemFlags::new();
|
|
||||||
let memoffset = Offset32::new(offset as i32);
|
|
||||||
state.push1(builder.ins().uload8(I32, memflags, addr, memoffset))
|
|
||||||
}
|
}
|
||||||
Operator::I32Load8S { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
Operator::I32Load8S { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
||||||
let address_i32 = state.pop1();
|
translate_load(offset, ir::Opcode::Sload8, I32, builder, state, runtime);
|
||||||
let base = runtime.translate_memory_base_address(builder, 0);
|
|
||||||
let address_i64 = builder.ins().uextend(I64, address_i32);
|
|
||||||
let addr = builder.ins().iadd(base, address_i64);
|
|
||||||
let memflags = MemFlags::new();
|
|
||||||
let memoffset = Offset32::new(offset as i32);
|
|
||||||
state.push1(builder.ins().sload8(I32, memflags, addr, memoffset))
|
|
||||||
}
|
}
|
||||||
Operator::I32Load16S { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
Operator::I32Load16S { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
||||||
let address_i32 = state.pop1();
|
translate_load(offset, ir::Opcode::Sload16, I32, builder, state, runtime);
|
||||||
let base = runtime.translate_memory_base_address(builder, 0);
|
|
||||||
let address_i64 = builder.ins().uextend(I64, address_i32);
|
|
||||||
let addr = builder.ins().iadd(base, address_i64);
|
|
||||||
let memflags = MemFlags::new();
|
|
||||||
let memoffset = Offset32::new(offset as i32);
|
|
||||||
state.push1(builder.ins().sload8(I32, memflags, addr, memoffset))
|
|
||||||
}
|
}
|
||||||
Operator::I64Load8U { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
Operator::I64Load8U { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
||||||
let address_i32 = state.pop1();
|
translate_load(offset, ir::Opcode::Uload8, I64, builder, state, runtime);
|
||||||
let base = runtime.translate_memory_base_address(builder, 0);
|
|
||||||
let address_i64 = builder.ins().uextend(I64, address_i32);
|
|
||||||
let addr = builder.ins().iadd(base, address_i64);
|
|
||||||
let memflags = MemFlags::new();
|
|
||||||
let memoffset = Offset32::new(offset as i32);
|
|
||||||
state.push1(builder.ins().uload8(I64, memflags, addr, memoffset))
|
|
||||||
}
|
}
|
||||||
Operator::I64Load16U { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
Operator::I64Load16U { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
||||||
let address_i32 = state.pop1();
|
translate_load(offset, ir::Opcode::Uload16, I64, builder, state, runtime);
|
||||||
let base = runtime.translate_memory_base_address(builder, 0);
|
|
||||||
let address_i64 = builder.ins().uextend(I64, address_i32);
|
|
||||||
let addr = builder.ins().iadd(base, address_i64);
|
|
||||||
let memflags = MemFlags::new();
|
|
||||||
let memoffset = Offset32::new(offset as i32);
|
|
||||||
state.push1(builder.ins().uload16(I64, memflags, addr, memoffset))
|
|
||||||
}
|
}
|
||||||
Operator::I64Load8S { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
Operator::I64Load8S { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
||||||
let address_i32 = state.pop1();
|
translate_load(offset, ir::Opcode::Sload8, I64, builder, state, runtime);
|
||||||
let base = runtime.translate_memory_base_address(builder, 0);
|
|
||||||
let address_i64 = builder.ins().uextend(I64, address_i32);
|
|
||||||
let addr = builder.ins().iadd(base, address_i64);
|
|
||||||
let memflags = MemFlags::new();
|
|
||||||
let memoffset = Offset32::new(offset as i32);
|
|
||||||
state.push1(builder.ins().sload8(I64, memflags, addr, memoffset))
|
|
||||||
}
|
}
|
||||||
Operator::I64Load16S { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
Operator::I64Load16S { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
||||||
let address_i32 = state.pop1();
|
translate_load(offset, ir::Opcode::Sload16, I64, builder, state, runtime);
|
||||||
let base = runtime.translate_memory_base_address(builder, 0);
|
|
||||||
let address_i64 = builder.ins().uextend(I64, address_i32);
|
|
||||||
let addr = builder.ins().iadd(base, address_i64);
|
|
||||||
let memflags = MemFlags::new();
|
|
||||||
let memoffset = Offset32::new(offset as i32);
|
|
||||||
state.push1(builder.ins().sload16(I64, memflags, addr, memoffset))
|
|
||||||
}
|
}
|
||||||
Operator::I64Load32S { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
Operator::I64Load32S { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
||||||
let address_i32 = state.pop1();
|
translate_load(offset, ir::Opcode::Sload32, I64, builder, state, runtime);
|
||||||
let base = runtime.translate_memory_base_address(builder, 0);
|
|
||||||
let address_i64 = builder.ins().uextend(I64, address_i32);
|
|
||||||
let addr = builder.ins().iadd(base, address_i64);
|
|
||||||
let memflags = MemFlags::new();
|
|
||||||
let memoffset = Offset32::new(offset as i32);
|
|
||||||
state.push1(builder.ins().sload32(memflags, addr, memoffset))
|
|
||||||
}
|
}
|
||||||
Operator::I64Load32U { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
Operator::I64Load32U { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
||||||
let address_i32 = state.pop1();
|
translate_load(offset, ir::Opcode::Uload32, I64, builder, state, runtime);
|
||||||
let base = runtime.translate_memory_base_address(builder, 0);
|
|
||||||
let address_i64 = builder.ins().uextend(I64, address_i32);
|
|
||||||
let addr = builder.ins().iadd(base, address_i64);
|
|
||||||
let memflags = MemFlags::new();
|
|
||||||
let memoffset = Offset32::new(offset as i32);
|
|
||||||
state.push1(builder.ins().uload32(memflags, addr, memoffset))
|
|
||||||
}
|
}
|
||||||
Operator::I32Load { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
Operator::I32Load { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
||||||
let address_i32 = state.pop1();
|
translate_load(offset, ir::Opcode::Load, I32, builder, state, runtime);
|
||||||
let base = runtime.translate_memory_base_address(builder, 0);
|
|
||||||
let address_i64 = builder.ins().uextend(I64, address_i32);
|
|
||||||
let addr = builder.ins().iadd(base, address_i64);
|
|
||||||
let memflags = MemFlags::new();
|
|
||||||
let memoffset = Offset32::new(offset as i32);
|
|
||||||
state.push1(builder.ins().load(I32, memflags, addr, memoffset))
|
|
||||||
}
|
}
|
||||||
Operator::F32Load { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
Operator::F32Load { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
||||||
let address_i32 = state.pop1();
|
translate_load(offset, ir::Opcode::Load, F32, builder, state, runtime);
|
||||||
let base = runtime.translate_memory_base_address(builder, 0);
|
|
||||||
let address_i64 = builder.ins().uextend(I64, address_i32);
|
|
||||||
let addr = builder.ins().iadd(base, address_i64);
|
|
||||||
let memflags = MemFlags::new();
|
|
||||||
let memoffset = Offset32::new(offset as i32);
|
|
||||||
state.push1(builder.ins().load(F32, memflags, addr, memoffset))
|
|
||||||
}
|
}
|
||||||
Operator::I64Load { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
Operator::I64Load { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
||||||
let address_i32 = state.pop1();
|
translate_load(offset, ir::Opcode::Load, I64, builder, state, runtime);
|
||||||
let base = runtime.translate_memory_base_address(builder, 0);
|
|
||||||
let address_i64 = builder.ins().uextend(I64, address_i32);
|
|
||||||
let addr = builder.ins().iadd(base, address_i64);
|
|
||||||
let memflags = MemFlags::new();
|
|
||||||
let memoffset = Offset32::new(offset as i32);
|
|
||||||
state.push1(builder.ins().load(I64, memflags, addr, memoffset))
|
|
||||||
}
|
}
|
||||||
Operator::F64Load { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
Operator::F64Load { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
||||||
let address_i32 = state.pop1();
|
translate_load(offset, ir::Opcode::Load, F64, builder, state, runtime);
|
||||||
let base = runtime.translate_memory_base_address(builder, 0);
|
|
||||||
let address_i64 = builder.ins().uextend(I64, address_i32);
|
|
||||||
let addr = builder.ins().iadd(base, address_i64);
|
|
||||||
let memflags = MemFlags::new();
|
|
||||||
let memoffset = Offset32::new(offset as i32);
|
|
||||||
state.push1(builder.ins().load(F64, memflags, addr, memoffset))
|
|
||||||
}
|
}
|
||||||
/****************************** Store instructions ***********************************
|
/****************************** Store instructions ***********************************
|
||||||
* Wasm specifies an integer alignment flag but we drop it in Cretonne.
|
* Wasm specifies an integer alignment flag but we drop it in Cretonne.
|
||||||
@@ -642,46 +574,18 @@ fn translate_operator(
|
|||||||
Operator::I64Store { memory_immediate: MemoryImmediate { flags: _, offset } } |
|
Operator::I64Store { memory_immediate: MemoryImmediate { flags: _, offset } } |
|
||||||
Operator::F32Store { memory_immediate: MemoryImmediate { flags: _, offset } } |
|
Operator::F32Store { memory_immediate: MemoryImmediate { flags: _, offset } } |
|
||||||
Operator::F64Store { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
Operator::F64Store { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
||||||
let val = state.pop1();
|
translate_store(offset, ir::Opcode::Store, builder, state, runtime);
|
||||||
let address_i32 = state.pop1();
|
|
||||||
let base = runtime.translate_memory_base_address(builder, 0);
|
|
||||||
let address_i64 = builder.ins().uextend(I64, address_i32);
|
|
||||||
let addr = builder.ins().iadd(base, address_i64);
|
|
||||||
let memflags = MemFlags::new();
|
|
||||||
let memoffset = Offset32::new(offset as i32);
|
|
||||||
builder.ins().store(memflags, val, addr, memoffset);
|
|
||||||
}
|
}
|
||||||
Operator::I32Store8 { memory_immediate: MemoryImmediate { flags: _, offset } } |
|
Operator::I32Store8 { memory_immediate: MemoryImmediate { flags: _, offset } } |
|
||||||
Operator::I64Store8 { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
Operator::I64Store8 { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
||||||
let val = state.pop1();
|
translate_store(offset, ir::Opcode::Istore8, builder, state, runtime);
|
||||||
let address_i32 = state.pop1();
|
|
||||||
let base = runtime.translate_memory_base_address(builder, 0);
|
|
||||||
let address_i64 = builder.ins().uextend(I64, address_i32);
|
|
||||||
let addr = builder.ins().iadd(base, address_i64);
|
|
||||||
let memflags = MemFlags::new();
|
|
||||||
let memoffset = Offset32::new(offset as i32);
|
|
||||||
builder.ins().istore8(memflags, val, addr, memoffset);
|
|
||||||
}
|
}
|
||||||
Operator::I32Store16 { memory_immediate: MemoryImmediate { flags: _, offset } } |
|
Operator::I32Store16 { memory_immediate: MemoryImmediate { flags: _, offset } } |
|
||||||
Operator::I64Store16 { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
Operator::I64Store16 { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
||||||
let val = state.pop1();
|
translate_store(offset, ir::Opcode::Istore16, builder, state, runtime);
|
||||||
let address_i32 = state.pop1();
|
|
||||||
let base = runtime.translate_memory_base_address(builder, 0);
|
|
||||||
let address_i64 = builder.ins().uextend(I64, address_i32);
|
|
||||||
let addr = builder.ins().iadd(base, address_i64);
|
|
||||||
let memflags = MemFlags::new();
|
|
||||||
let memoffset = Offset32::new(offset as i32);
|
|
||||||
builder.ins().istore16(memflags, val, addr, memoffset);
|
|
||||||
}
|
}
|
||||||
Operator::I64Store32 { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
Operator::I64Store32 { memory_immediate: MemoryImmediate { flags: _, offset } } => {
|
||||||
let val = state.pop1();
|
translate_store(offset, ir::Opcode::Istore32, builder, state, runtime);
|
||||||
let address_i32 = state.pop1();
|
|
||||||
let base = runtime.translate_memory_base_address(builder, 0);
|
|
||||||
let address_i64 = builder.ins().uextend(I64, address_i32);
|
|
||||||
let addr = builder.ins().iadd(base, address_i64);
|
|
||||||
let memflags = MemFlags::new();
|
|
||||||
let memoffset = Offset32::new(offset as i32);
|
|
||||||
builder.ins().istore32(memflags, val, addr, memoffset);
|
|
||||||
}
|
}
|
||||||
/****************************** Nullary Operators ************************************/
|
/****************************** Nullary Operators ************************************/
|
||||||
Operator::I32Const { value } => state.push1(builder.ins().iconst(I32, value as i64)),
|
Operator::I32Const { value } => state.push1(builder.ins().iconst(I32, value as i64)),
|
||||||
@@ -1117,6 +1021,91 @@ fn translate_unreachable_operator(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the address+offset to use for a heap access.
|
||||||
|
fn get_heap_addr(
|
||||||
|
heap: ir::Heap,
|
||||||
|
addr32: ir::Value,
|
||||||
|
offset: u32,
|
||||||
|
addr_ty: ir::Type,
|
||||||
|
builder: &mut FunctionBuilder<Local>,
|
||||||
|
) -> (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");
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// bounds.
|
||||||
|
//
|
||||||
|
// For accesses on the outer skirts of the guard pages, we expect that we get a trap
|
||||||
|
// even if the access goes beyond the guard pages. This is because the first byte pointed to is
|
||||||
|
// inside the guard pages.
|
||||||
|
let check_size = min(
|
||||||
|
u32::max_value() as i64,
|
||||||
|
1 + (offset as i64 / guard_size) * guard_size,
|
||||||
|
) as u32;
|
||||||
|
let base = builder.ins().heap_addr(addr_ty, heap, addr32, check_size);
|
||||||
|
|
||||||
|
// Native load/store instructions take a signed `Offset32` immediate, so adjust the base
|
||||||
|
// pointer if necessary.
|
||||||
|
if offset > i32::max_value() as u32 {
|
||||||
|
// Offset doesn't fit in the load/store instruction.
|
||||||
|
let adj = builder.ins().iadd_imm(base, i32::max_value() as i64 + 1);
|
||||||
|
(adj, (offset - (i32::max_value() as u32 + 1)) as i32)
|
||||||
|
} else {
|
||||||
|
(base, offset as i32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translate a load instruction.
|
||||||
|
fn translate_load<FE: FuncEnvironment + ?Sized>(
|
||||||
|
offset: u32,
|
||||||
|
opcode: ir::Opcode,
|
||||||
|
result_ty: ir::Type,
|
||||||
|
builder: &mut FunctionBuilder<Local>,
|
||||||
|
state: &mut TranslationState,
|
||||||
|
environ: &FE,
|
||||||
|
) {
|
||||||
|
let addr32 = state.pop1();
|
||||||
|
// 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);
|
||||||
|
let flags = MemFlags::new();
|
||||||
|
let (load, dfg) = builder.ins().Load(
|
||||||
|
opcode,
|
||||||
|
result_ty,
|
||||||
|
flags,
|
||||||
|
offset.into(),
|
||||||
|
base,
|
||||||
|
);
|
||||||
|
state.push1(dfg.first_result(load));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translate a store instruction.
|
||||||
|
fn translate_store<FE: FuncEnvironment + ?Sized>(
|
||||||
|
offset: u32,
|
||||||
|
opcode: ir::Opcode,
|
||||||
|
builder: &mut FunctionBuilder<Local>,
|
||||||
|
state: &mut TranslationState,
|
||||||
|
environ: &FE,
|
||||||
|
) {
|
||||||
|
let (addr32, val) = state.pop2();
|
||||||
|
let val_ty = builder.func.dfg.value_type(val);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
let flags = MemFlags::new();
|
||||||
|
builder.ins().Store(
|
||||||
|
opcode,
|
||||||
|
val_ty,
|
||||||
|
flags,
|
||||||
|
offset.into(),
|
||||||
|
val,
|
||||||
|
base,
|
||||||
|
);
|
||||||
|
}
|
||||||
fn args_count(
|
fn args_count(
|
||||||
index: FunctionIndex,
|
index: FunctionIndex,
|
||||||
functions: &[SignatureIndex],
|
functions: &[SignatureIndex],
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
use runtime::WasmRuntime;
|
use runtime::{FuncEnvironment, GlobalValue, WasmRuntime};
|
||||||
use translation_utils::{Local, Global, Memory, Table, GlobalIndex, TableIndex, FunctionIndex,
|
use translation_utils::{Local, Global, Memory, Table, GlobalIndex, TableIndex, FunctionIndex,
|
||||||
MemoryIndex};
|
MemoryIndex};
|
||||||
use cton_frontend::FunctionBuilder;
|
use cton_frontend::FunctionBuilder;
|
||||||
use cretonne::ir::{Value, InstBuilder, SigRef};
|
use cretonne::ir::{self, Value, InstBuilder, SigRef};
|
||||||
use cretonne::ir::immediates::{Ieee32, Ieee64};
|
|
||||||
use cretonne::ir::types::*;
|
use cretonne::ir::types::*;
|
||||||
|
|
||||||
/// This runtime implementation is a "naïve" one, doing essentially nothing and emitting
|
/// This runtime implementation is a "naïve" one, doing essentially nothing and emitting
|
||||||
@@ -20,29 +19,32 @@ impl DummyRuntime {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WasmRuntime for DummyRuntime {
|
impl FuncEnvironment for DummyRuntime {
|
||||||
fn translate_get_global(
|
fn native_pointer(&self) -> ir::Type {
|
||||||
&self,
|
ir::types::I64
|
||||||
builder: &mut FunctionBuilder<Local>,
|
}
|
||||||
global_index: GlobalIndex,
|
|
||||||
) -> Value {
|
fn make_global(&self, func: &mut ir::Function, index: GlobalIndex) -> GlobalValue {
|
||||||
let glob = self.globals[global_index];
|
// Just create a dummy `vmctx` global.
|
||||||
match glob.ty {
|
let offset = ((index * 8) as i32 + 8).into();
|
||||||
I32 => builder.ins().iconst(glob.ty, -1),
|
let gv = func.global_vars.push(ir::GlobalVarData::VmCtx { offset });
|
||||||
I64 => builder.ins().iconst(glob.ty, -1),
|
GlobalValue::Memory {
|
||||||
F32 => builder.ins().f32const(Ieee32::with_bits(0xbf800000)), // -1.0
|
gv,
|
||||||
F64 => {
|
ty: self.globals[index].ty,
|
||||||
builder.ins().f64const(
|
|
||||||
Ieee64::with_bits(0xbff0000000000000),
|
|
||||||
)
|
|
||||||
} // -1.0
|
|
||||||
_ => panic!("should not happen"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn translate_set_global(&self, _: &mut FunctionBuilder<Local>, _: GlobalIndex, _: Value) {
|
fn make_heap(&self, func: &mut ir::Function, _index: MemoryIndex) -> ir::Heap {
|
||||||
// We do nothing
|
func.heaps.push(ir::HeapData {
|
||||||
|
base: ir::HeapBase::ReservedReg,
|
||||||
|
min_size: 0.into(),
|
||||||
|
guard_size: 0x8000_0000.into(),
|
||||||
|
style: ir::HeapStyle::Static { bound: 0x1_0000_0000.into() },
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WasmRuntime for DummyRuntime {
|
||||||
fn translate_grow_memory(&mut self, builder: &mut FunctionBuilder<Local>, _: Value) -> Value {
|
fn translate_grow_memory(&mut self, builder: &mut FunctionBuilder<Local>, _: Value) -> Value {
|
||||||
builder.ins().iconst(I32, -1)
|
builder.ins().iconst(I32, -1)
|
||||||
}
|
}
|
||||||
@@ -59,13 +61,6 @@ impl WasmRuntime for DummyRuntime {
|
|||||||
let call_inst = builder.ins().call_indirect(sig_ref, index_val, call_args);
|
let call_inst = builder.ins().call_indirect(sig_ref, index_val, call_args);
|
||||||
builder.inst_results(call_inst)
|
builder.inst_results(call_inst)
|
||||||
}
|
}
|
||||||
fn translate_memory_base_address(
|
|
||||||
&self,
|
|
||||||
builder: &mut FunctionBuilder<Local>,
|
|
||||||
_: MemoryIndex,
|
|
||||||
) -> Value {
|
|
||||||
builder.ins().iconst(I64, 0)
|
|
||||||
}
|
|
||||||
fn declare_global(&mut self, global: Global) {
|
fn declare_global(&mut self, global: Global) {
|
||||||
self.globals.push(global);
|
self.globals.push(global);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
mod spec;
|
mod spec;
|
||||||
mod dummy;
|
mod dummy;
|
||||||
|
|
||||||
pub use runtime::spec::WasmRuntime;
|
pub use runtime::spec::{WasmRuntime, FuncEnvironment, GlobalValue};
|
||||||
pub use runtime::dummy::DummyRuntime;
|
pub use runtime::dummy::DummyRuntime;
|
||||||
|
|||||||
@@ -1,14 +1,51 @@
|
|||||||
//! All the runtime support necessary for the wasm to cretonne translation is formalized by the
|
//! All the runtime support necessary for the wasm to cretonne translation is formalized by the
|
||||||
//! trait `WasmRuntime`.
|
//! trait `WasmRuntime`.
|
||||||
use cton_frontend::FunctionBuilder;
|
use cton_frontend::FunctionBuilder;
|
||||||
use cretonne::ir::{Value, SigRef};
|
use cretonne::ir::{self, Value, SigRef};
|
||||||
use translation_utils::{Local, FunctionIndex, TableIndex, GlobalIndex, MemoryIndex, Global, Table,
|
use translation_utils::{Local, FunctionIndex, TableIndex, GlobalIndex, MemoryIndex, Global, Table,
|
||||||
Memory};
|
Memory};
|
||||||
|
|
||||||
|
/// The value of a WebAssembly global variable.
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum GlobalValue {
|
||||||
|
/// This is a constant global with a value known at compile time.
|
||||||
|
Const(ir::Value),
|
||||||
|
|
||||||
|
/// This is a variable in memory that should be referenced as a `GlobalVar`.
|
||||||
|
Memory { gv: ir::GlobalVar, ty: ir::Type },
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
/// runtime environment.
|
||||||
|
pub trait FuncEnvironment {
|
||||||
|
/// Get the Cretonne integer type to use for native pointers.
|
||||||
|
///
|
||||||
|
/// This should be `I64` for 64-bit architectures and `I32` for 32-bit architectures.
|
||||||
|
fn native_pointer(&self) -> ir::Type;
|
||||||
|
|
||||||
|
/// Set up the necessary preamble definitions in `func` to access the global variable
|
||||||
|
/// identified by `index`.
|
||||||
|
///
|
||||||
|
/// The index space covers both imported globals and globals defined by the module.
|
||||||
|
///
|
||||||
|
/// Return the global variable reference that should be used to access the global and the
|
||||||
|
/// WebAssembly type of the global.
|
||||||
|
fn make_global(&self, func: &mut ir::Function, index: GlobalIndex) -> GlobalValue;
|
||||||
|
|
||||||
|
/// Set up the necessary preamble definitions in `func` to access the linear memory identified
|
||||||
|
/// by `index`.
|
||||||
|
///
|
||||||
|
/// The index space covers both imported and locally declared memories.
|
||||||
|
fn make_heap(&self, func: &mut ir::Function, index: MemoryIndex) -> ir::Heap;
|
||||||
|
}
|
||||||
|
|
||||||
/// An object satisfyng the `WasmRuntime` trait can be passed as argument to the
|
/// An object satisfyng the `WasmRuntime` trait can be passed as argument to the
|
||||||
/// [`translate_module`](fn.translate_module.html) function. These methods should not be called
|
/// [`translate_module`](fn.translate_module.html) function. These methods should not be called
|
||||||
/// by the user, they are only for the `wasm2cretonne` internal use.
|
/// by the user, they are only for the `wasm2cretonne` internal use.
|
||||||
pub trait WasmRuntime {
|
pub trait WasmRuntime: FuncEnvironment {
|
||||||
/// Declares a global to the runtime.
|
/// Declares a global to the runtime.
|
||||||
fn declare_global(&mut self, global: Global);
|
fn declare_global(&mut self, global: Global);
|
||||||
/// Declares a table to the runtime.
|
/// Declares a table to the runtime.
|
||||||
@@ -34,29 +71,10 @@ pub trait WasmRuntime {
|
|||||||
fn begin_translation(&mut self);
|
fn begin_translation(&mut self);
|
||||||
/// Call this function between each function body translation.
|
/// Call this function between each function body translation.
|
||||||
fn next_function(&mut self);
|
fn next_function(&mut self);
|
||||||
/// Translates a `get_global` wasm instruction.
|
|
||||||
fn translate_get_global(
|
|
||||||
&self,
|
|
||||||
builder: &mut FunctionBuilder<Local>,
|
|
||||||
global_index: GlobalIndex,
|
|
||||||
) -> Value;
|
|
||||||
/// Translates a `set_global` wasm instruction.
|
|
||||||
fn translate_set_global(
|
|
||||||
&self,
|
|
||||||
builder: &mut FunctionBuilder<Local>,
|
|
||||||
global_index: GlobalIndex,
|
|
||||||
val: Value,
|
|
||||||
);
|
|
||||||
/// Translates a `grow_memory` wasm instruction. Returns the old size (in pages) of the memory.
|
/// Translates a `grow_memory` wasm instruction. Returns the old size (in pages) of the memory.
|
||||||
fn translate_grow_memory(&mut self, builder: &mut FunctionBuilder<Local>, val: Value) -> Value;
|
fn translate_grow_memory(&mut self, builder: &mut FunctionBuilder<Local>, val: Value) -> Value;
|
||||||
/// Translates a `current_memory` wasm instruction. Returns the size in pages of the memory.
|
/// Translates a `current_memory` wasm instruction. Returns the size in pages of the memory.
|
||||||
fn translate_current_memory(&mut self, builder: &mut FunctionBuilder<Local>) -> Value;
|
fn translate_current_memory(&mut self, builder: &mut FunctionBuilder<Local>) -> Value;
|
||||||
/// Returns the base address of a wasm memory as a Cretonne `Value`.
|
|
||||||
fn translate_memory_base_address(
|
|
||||||
&self,
|
|
||||||
builder: &mut FunctionBuilder<Local>,
|
|
||||||
index: MemoryIndex,
|
|
||||||
) -> Value;
|
|
||||||
/// Translates a `call_indirect` wasm instruction. It involves looking up the value contained
|
/// Translates a `call_indirect` wasm instruction. It involves looking up the value contained
|
||||||
/// it the table at location `index_val` and calling the corresponding function.
|
/// it the table at location `index_val` and calling the corresponding function.
|
||||||
fn translate_call_indirect<'a>(
|
fn translate_call_indirect<'a>(
|
||||||
|
|||||||
@@ -4,6 +4,9 @@
|
|||||||
//! value and control stacks during the translation of a single function.
|
//! value and control stacks during the translation of a single function.
|
||||||
|
|
||||||
use cretonne::ir::{self, Ebb, Inst, Type, Value};
|
use cretonne::ir::{self, Ebb, Inst, Type, Value};
|
||||||
|
use runtime::{FuncEnvironment, GlobalValue};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use translation_utils::{GlobalIndex, MemoryIndex};
|
||||||
|
|
||||||
/// A control stack frame can be an `if`, a `block` or a `loop`, each one having the following
|
/// A control stack frame can be an `if`, a `block` or a `loop`, each one having the following
|
||||||
/// fields:
|
/// fields:
|
||||||
@@ -104,6 +107,12 @@ pub struct TranslationState {
|
|||||||
pub control_stack: Vec<ControlStackFrame>,
|
pub control_stack: Vec<ControlStackFrame>,
|
||||||
pub phantom_unreachable_stack_depth: usize,
|
pub phantom_unreachable_stack_depth: usize,
|
||||||
pub real_unreachable_stack_depth: usize,
|
pub real_unreachable_stack_depth: usize,
|
||||||
|
|
||||||
|
// Map of global variables that have already been created by `FuncEnvironment::make_global`.
|
||||||
|
globals: HashMap<GlobalIndex, GlobalValue>,
|
||||||
|
|
||||||
|
// Map of heaps that have been created by `FuncEnvironment::make_heap`.
|
||||||
|
heaps: HashMap<MemoryIndex, ir::Heap>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TranslationState {
|
impl TranslationState {
|
||||||
@@ -113,6 +122,8 @@ impl TranslationState {
|
|||||||
control_stack: Vec::new(),
|
control_stack: Vec::new(),
|
||||||
phantom_unreachable_stack_depth: 0,
|
phantom_unreachable_stack_depth: 0,
|
||||||
real_unreachable_stack_depth: 0,
|
real_unreachable_stack_depth: 0,
|
||||||
|
globals: HashMap::new(),
|
||||||
|
heaps: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,6 +132,8 @@ impl TranslationState {
|
|||||||
self.control_stack.clear();
|
self.control_stack.clear();
|
||||||
self.phantom_unreachable_stack_depth = 0;
|
self.phantom_unreachable_stack_depth = 0;
|
||||||
self.real_unreachable_stack_depth = 0;
|
self.real_unreachable_stack_depth = 0;
|
||||||
|
self.globals.clear();
|
||||||
|
self.heaps.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize the state for compiling a function with the given signature.
|
/// Initialize the state for compiling a function with the given signature.
|
||||||
@@ -211,3 +224,35 @@ impl TranslationState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Methods for handling entity references.
|
||||||
|
impl TranslationState {
|
||||||
|
/// Get the `GlobalVar` reference that should be used to access the global variable `index`.
|
||||||
|
/// Create the reference if necessary.
|
||||||
|
/// Also return the WebAssembly type of the global.
|
||||||
|
pub fn get_global<FE: FuncEnvironment + ?Sized>(
|
||||||
|
&mut self,
|
||||||
|
func: &mut ir::Function,
|
||||||
|
index: u32,
|
||||||
|
environ: &FE,
|
||||||
|
) -> GlobalValue {
|
||||||
|
let index = index as GlobalIndex;
|
||||||
|
*self.globals.entry(index).or_insert_with(
|
||||||
|
|| environ.make_global(func, index),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the `Heap` reference that should be used to access linear memory `index`.
|
||||||
|
/// Create the reference if necessary.
|
||||||
|
pub fn get_heap<FE: FuncEnvironment + ?Sized>(
|
||||||
|
&mut self,
|
||||||
|
func: &mut ir::Function,
|
||||||
|
index: u32,
|
||||||
|
environ: &FE,
|
||||||
|
) -> ir::Heap {
|
||||||
|
let index = index as MemoryIndex;
|
||||||
|
*self.heaps.entry(index).or_insert_with(
|
||||||
|
|| environ.make_heap(func, index),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user