diff --git a/lib/frontend/src/frontend.rs b/lib/frontend/src/frontend.rs index beb5dfdcfe..8eb90dd1d6 100644 --- a/lib/frontend/src/frontend.rs +++ b/lib/frontend/src/frontend.rs @@ -26,7 +26,10 @@ pub struct FunctionBuilder<'a, Variable: 'a> where 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, position: Position, pristine: bool, diff --git a/lib/wasm/src/code_translator.rs b/lib/wasm/src/code_translator.rs index 1e96b02c4d..d7e895cd9a 100644 --- a/lib/wasm/src/code_translator.rs +++ b/lib/wasm/src/code_translator.rs @@ -21,18 +21,18 @@ //! //! That is why `translate_function_body` takes an object having the `WasmRuntime` trait as //! argument. -use cretonne::ir::{Function, Signature, Type, InstBuilder, FunctionName, Ebb, FuncRef, SigRef, - ExtFuncData, MemFlags}; +use cretonne::ir::{self, Function, Signature, Type, InstBuilder, FunctionName, Ebb, FuncRef, + SigRef, ExtFuncData, MemFlags}; use cretonne::ir::types::*; -use cretonne::ir::immediates::{Ieee32, Ieee64, Offset32}; +use cretonne::ir::immediates::{Ieee32, Ieee64}; use cretonne::ir::condcodes::{IntCC, FloatCC}; use cton_frontend::{ILBuilder, FunctionBuilder}; use wasmparser::{Parser, ParserState, Operator, WasmDecoder, MemoryImmediate}; use translation_utils::{f32_translation, f64_translation, type_to_type, translate_type, Local, - GlobalIndex, FunctionIndex, SignatureIndex}; + FunctionIndex, SignatureIndex}; use state::{TranslationState, ControlStackFrame}; use std::collections::HashMap; -use runtime::WasmRuntime; +use runtime::{FuncEnvironment, GlobalValue, WasmRuntime}; use std::u32; @@ -194,12 +194,28 @@ fn translate_operator( * `get_global` and `set_global` are handled by the runtime. ***********************************************************************************/ 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); } Operator::SetGlobal { global_index } => { - let val = state.pop1(); - runtime.translate_set_global(builder, global_index as GlobalIndex, val); + match state.get_global(builder.func, global_index, runtime) { + 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 *************************************** * `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 ************************************************************************************/ Operator::I32Load8U { memory_immediate: MemoryImmediate { flags: _, offset } } => { - 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); - state.push1(builder.ins().uload8(I32, memflags, addr, memoffset)) + translate_load(offset, ir::Opcode::Uload8, I32, builder, state, runtime); } Operator::I32Load16U { memory_immediate: MemoryImmediate { flags: _, offset } } => { - 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); - state.push1(builder.ins().uload8(I32, memflags, addr, memoffset)) + translate_load(offset, ir::Opcode::Uload16, I32, builder, state, runtime); } Operator::I32Load8S { memory_immediate: MemoryImmediate { flags: _, offset } } => { - 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); - state.push1(builder.ins().sload8(I32, memflags, addr, memoffset)) + translate_load(offset, ir::Opcode::Sload8, I32, builder, state, runtime); } Operator::I32Load16S { memory_immediate: MemoryImmediate { flags: _, offset } } => { - 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); - state.push1(builder.ins().sload8(I32, memflags, addr, memoffset)) + translate_load(offset, ir::Opcode::Sload16, I32, builder, state, runtime); } Operator::I64Load8U { memory_immediate: MemoryImmediate { flags: _, offset } } => { - 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); - state.push1(builder.ins().uload8(I64, memflags, addr, memoffset)) + translate_load(offset, ir::Opcode::Uload8, I64, builder, state, runtime); } Operator::I64Load16U { memory_immediate: MemoryImmediate { flags: _, offset } } => { - 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); - state.push1(builder.ins().uload16(I64, memflags, addr, memoffset)) + translate_load(offset, ir::Opcode::Uload16, I64, builder, state, runtime); } Operator::I64Load8S { memory_immediate: MemoryImmediate { flags: _, offset } } => { - 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); - state.push1(builder.ins().sload8(I64, memflags, addr, memoffset)) + translate_load(offset, ir::Opcode::Sload8, I64, builder, state, runtime); } Operator::I64Load16S { memory_immediate: MemoryImmediate { flags: _, offset } } => { - 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); - state.push1(builder.ins().sload16(I64, memflags, addr, memoffset)) + translate_load(offset, ir::Opcode::Sload16, I64, builder, state, runtime); } Operator::I64Load32S { memory_immediate: MemoryImmediate { flags: _, offset } } => { - 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); - state.push1(builder.ins().sload32(memflags, addr, memoffset)) + translate_load(offset, ir::Opcode::Sload32, I64, builder, state, runtime); } Operator::I64Load32U { memory_immediate: MemoryImmediate { flags: _, offset } } => { - 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); - state.push1(builder.ins().uload32(memflags, addr, memoffset)) + translate_load(offset, ir::Opcode::Uload32, I64, builder, state, runtime); } Operator::I32Load { memory_immediate: MemoryImmediate { flags: _, offset } } => { - 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); - state.push1(builder.ins().load(I32, memflags, addr, memoffset)) + translate_load(offset, ir::Opcode::Load, I32, builder, state, runtime); } Operator::F32Load { memory_immediate: MemoryImmediate { flags: _, offset } } => { - 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); - state.push1(builder.ins().load(F32, memflags, addr, memoffset)) + translate_load(offset, ir::Opcode::Load, F32, builder, state, runtime); } Operator::I64Load { memory_immediate: MemoryImmediate { flags: _, offset } } => { - 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); - state.push1(builder.ins().load(I64, memflags, addr, memoffset)) + translate_load(offset, ir::Opcode::Load, I64, builder, state, runtime); } Operator::F64Load { memory_immediate: MemoryImmediate { flags: _, offset } } => { - 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); - state.push1(builder.ins().load(F64, memflags, addr, memoffset)) + translate_load(offset, ir::Opcode::Load, F64, builder, state, runtime); } /****************************** Store instructions *********************************** * 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::F32Store { memory_immediate: MemoryImmediate { flags: _, offset } } | Operator::F64Store { memory_immediate: MemoryImmediate { flags: _, offset } } => { - let val = state.pop1(); - 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); + translate_store(offset, ir::Opcode::Store, builder, state, runtime); } Operator::I32Store8 { memory_immediate: MemoryImmediate { flags: _, offset } } | Operator::I64Store8 { memory_immediate: MemoryImmediate { flags: _, offset } } => { - let val = state.pop1(); - 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); + translate_store(offset, ir::Opcode::Istore8, builder, state, runtime); } Operator::I32Store16 { memory_immediate: MemoryImmediate { flags: _, offset } } | Operator::I64Store16 { memory_immediate: MemoryImmediate { flags: _, offset } } => { - let val = state.pop1(); - 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); + translate_store(offset, ir::Opcode::Istore16, builder, state, runtime); } Operator::I64Store32 { memory_immediate: MemoryImmediate { flags: _, offset } } => { - let val = state.pop1(); - 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); + translate_store(offset, ir::Opcode::Istore32, builder, state, runtime); } /****************************** Nullary Operators ************************************/ 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, +) -> (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( + offset: u32, + opcode: ir::Opcode, + result_ty: ir::Type, + builder: &mut FunctionBuilder, + 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( + offset: u32, + opcode: ir::Opcode, + builder: &mut FunctionBuilder, + 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( index: FunctionIndex, functions: &[SignatureIndex], diff --git a/lib/wasm/src/runtime/dummy.rs b/lib/wasm/src/runtime/dummy.rs index 05d7cbfaa3..9b0df58174 100644 --- a/lib/wasm/src/runtime/dummy.rs +++ b/lib/wasm/src/runtime/dummy.rs @@ -1,9 +1,8 @@ -use runtime::WasmRuntime; +use runtime::{FuncEnvironment, GlobalValue, WasmRuntime}; use translation_utils::{Local, Global, Memory, Table, GlobalIndex, TableIndex, FunctionIndex, MemoryIndex}; use cton_frontend::FunctionBuilder; -use cretonne::ir::{Value, InstBuilder, SigRef}; -use cretonne::ir::immediates::{Ieee32, Ieee64}; +use cretonne::ir::{self, Value, InstBuilder, SigRef}; use cretonne::ir::types::*; /// This runtime implementation is a "naïve" one, doing essentially nothing and emitting @@ -20,29 +19,32 @@ impl DummyRuntime { } } -impl WasmRuntime for DummyRuntime { - fn translate_get_global( - &self, - builder: &mut FunctionBuilder, - global_index: GlobalIndex, - ) -> Value { - let glob = self.globals[global_index]; - match glob.ty { - I32 => builder.ins().iconst(glob.ty, -1), - I64 => builder.ins().iconst(glob.ty, -1), - F32 => builder.ins().f32const(Ieee32::with_bits(0xbf800000)), // -1.0 - F64 => { - builder.ins().f64const( - Ieee64::with_bits(0xbff0000000000000), - ) - } // -1.0 - _ => panic!("should not happen"), +impl FuncEnvironment for DummyRuntime { + fn native_pointer(&self) -> ir::Type { + ir::types::I64 + } + + fn make_global(&self, func: &mut ir::Function, index: GlobalIndex) -> GlobalValue { + // Just create a dummy `vmctx` global. + let offset = ((index * 8) as i32 + 8).into(); + let gv = func.global_vars.push(ir::GlobalVarData::VmCtx { offset }); + GlobalValue::Memory { + gv, + ty: self.globals[index].ty, } } - fn translate_set_global(&self, _: &mut FunctionBuilder, _: GlobalIndex, _: Value) { - // We do nothing + fn make_heap(&self, func: &mut ir::Function, _index: MemoryIndex) -> ir::Heap { + 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, _: Value) -> Value { 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); builder.inst_results(call_inst) } - fn translate_memory_base_address( - &self, - builder: &mut FunctionBuilder, - _: MemoryIndex, - ) -> Value { - builder.ins().iconst(I64, 0) - } fn declare_global(&mut self, global: Global) { self.globals.push(global); } diff --git a/lib/wasm/src/runtime/mod.rs b/lib/wasm/src/runtime/mod.rs index 9f2a41d4f9..548ad6bb6b 100644 --- a/lib/wasm/src/runtime/mod.rs +++ b/lib/wasm/src/runtime/mod.rs @@ -1,5 +1,5 @@ mod spec; mod dummy; -pub use runtime::spec::WasmRuntime; +pub use runtime::spec::{WasmRuntime, FuncEnvironment, GlobalValue}; pub use runtime::dummy::DummyRuntime; diff --git a/lib/wasm/src/runtime/spec.rs b/lib/wasm/src/runtime/spec.rs index 9f346337ab..5333d84c95 100644 --- a/lib/wasm/src/runtime/spec.rs +++ b/lib/wasm/src/runtime/spec.rs @@ -1,14 +1,51 @@ //! All the runtime support necessary for the wasm to cretonne translation is formalized by the //! trait `WasmRuntime`. 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, 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 /// [`translate_module`](fn.translate_module.html) function. These methods should not be called /// by the user, they are only for the `wasm2cretonne` internal use. -pub trait WasmRuntime { +pub trait WasmRuntime: FuncEnvironment { /// Declares a global to the runtime. fn declare_global(&mut self, global: Global); /// Declares a table to the runtime. @@ -34,29 +71,10 @@ pub trait WasmRuntime { fn begin_translation(&mut self); /// Call this function between each function body translation. fn next_function(&mut self); - /// Translates a `get_global` wasm instruction. - fn translate_get_global( - &self, - builder: &mut FunctionBuilder, - global_index: GlobalIndex, - ) -> Value; - /// Translates a `set_global` wasm instruction. - fn translate_set_global( - &self, - builder: &mut FunctionBuilder, - global_index: GlobalIndex, - val: Value, - ); /// Translates a `grow_memory` wasm instruction. Returns the old size (in pages) of the memory. fn translate_grow_memory(&mut self, builder: &mut FunctionBuilder, val: Value) -> Value; /// Translates a `current_memory` wasm instruction. Returns the size in pages of the memory. fn translate_current_memory(&mut self, builder: &mut FunctionBuilder) -> Value; - /// Returns the base address of a wasm memory as a Cretonne `Value`. - fn translate_memory_base_address( - &self, - builder: &mut FunctionBuilder, - index: MemoryIndex, - ) -> Value; /// 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. fn translate_call_indirect<'a>( diff --git a/lib/wasm/src/state.rs b/lib/wasm/src/state.rs index d9fabb1194..8479b5cc8a 100644 --- a/lib/wasm/src/state.rs +++ b/lib/wasm/src/state.rs @@ -4,6 +4,9 @@ //! value and control stacks during the translation of a single function. 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 /// fields: @@ -104,6 +107,12 @@ pub struct TranslationState { pub control_stack: Vec, pub phantom_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, + + // Map of heaps that have been created by `FuncEnvironment::make_heap`. + heaps: HashMap, } impl TranslationState { @@ -113,6 +122,8 @@ impl TranslationState { control_stack: Vec::new(), phantom_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.phantom_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. @@ -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( + &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( + &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), + ) + } +}