1 //! "Dummy" implementations of `ModuleEnvironment` and `FuncEnvironment` for testing
2 //! wasm translation. For complete implementations of `ModuleEnvironment` and
3 //! `FuncEnvironment`, see [wasmtime-environ] in [Wasmtime].
4 //!
5 //! [wasmtime-environ]: https://crates.io/crates/wasmtime-environ
6 //! [Wasmtime]: https://github.com/bytecodealliance/wasmtime
7
8 use crate::environ::{
9 FuncEnvironment, GlobalVariable, ModuleEnvironment, ReturnMode, TargetEnvironment,
10 WasmFuncType, WasmResult,
11 };
12 use crate::func_translator::FuncTranslator;
13 use crate::state::FuncTranslationState;
14 use crate::translation_utils::{
15 DataIndex, DefinedFuncIndex, ElemIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex,
16 Table, TableIndex, TypeIndex,
17 };
18 use crate::WasmType;
19 use core::convert::TryFrom;
20 use cranelift_codegen::cursor::FuncCursor;
21 use cranelift_codegen::ir::immediates::{Offset32, Uimm64};
22 use cranelift_codegen::ir::types::*;
23 use cranelift_codegen::ir::{self, InstBuilder};
24 use cranelift_codegen::isa::{CallConv, TargetFrontendConfig};
25 use cranelift_entity::{EntityRef, PrimaryMap, SecondaryMap};
26 use cranelift_frontend::FunctionBuilder;
27 use std::boxed::Box;
28 use std::string::String;
29 use std::vec::Vec;
30 use wasmparser::{FuncValidator, FunctionBody, Operator, ValidatorResources, WasmFeatures};
31
32 /// Compute a `ir::ExternalName` for a given wasm function index.
get_func_name(func_index: FuncIndex) -> ir::ExternalName33 fn get_func_name(func_index: FuncIndex) -> ir::ExternalName {
34 ir::ExternalName::user(0, func_index.as_u32())
35 }
36
37 /// A collection of names under which a given entity is exported.
38 pub struct Exportable<T> {
39 /// A wasm entity.
40 pub entity: T,
41
42 /// Names under which the entity is exported.
43 pub export_names: Vec<String>,
44 }
45
46 impl<T> Exportable<T> {
new(entity: T) -> Self47 pub fn new(entity: T) -> Self {
48 Self {
49 entity,
50 export_names: Vec::new(),
51 }
52 }
53 }
54
55 /// The main state belonging to a `DummyEnvironment`. This is split out from
56 /// `DummyEnvironment` to allow it to be borrowed separately from the
57 /// `FuncTranslator` field.
58 pub struct DummyModuleInfo {
59 /// Target description relevant to frontends producing Cranelift IR.
60 config: TargetFrontendConfig,
61
62 /// Signatures as provided by `declare_signature`.
63 pub signatures: PrimaryMap<TypeIndex, ir::Signature>,
64
65 /// Module and field names of imported functions as provided by `declare_func_import`.
66 pub imported_funcs: Vec<(String, String)>,
67
68 /// Module and field names of imported globals as provided by `declare_global_import`.
69 pub imported_globals: Vec<(String, String)>,
70
71 /// Module and field names of imported tables as provided by `declare_table_import`.
72 pub imported_tables: Vec<(String, String)>,
73
74 /// Module and field names of imported memories as provided by `declare_memory_import`.
75 pub imported_memories: Vec<(String, String)>,
76
77 /// Functions, imported and local.
78 pub functions: PrimaryMap<FuncIndex, Exportable<TypeIndex>>,
79
80 /// Function bodies.
81 pub function_bodies: PrimaryMap<DefinedFuncIndex, ir::Function>,
82
83 /// Tables as provided by `declare_table`.
84 pub tables: PrimaryMap<TableIndex, Exportable<Table>>,
85
86 /// Memories as provided by `declare_memory`.
87 pub memories: PrimaryMap<MemoryIndex, Exportable<Memory>>,
88
89 /// Globals as provided by `declare_global`.
90 pub globals: PrimaryMap<GlobalIndex, Exportable<Global>>,
91
92 /// The start function.
93 pub start_func: Option<FuncIndex>,
94 }
95
96 impl DummyModuleInfo {
97 /// Creates a new `DummyModuleInfo` instance.
new(config: TargetFrontendConfig) -> Self98 pub fn new(config: TargetFrontendConfig) -> Self {
99 Self {
100 config,
101 signatures: PrimaryMap::new(),
102 imported_funcs: Vec::new(),
103 imported_globals: Vec::new(),
104 imported_tables: Vec::new(),
105 imported_memories: Vec::new(),
106 functions: PrimaryMap::new(),
107 function_bodies: PrimaryMap::new(),
108 tables: PrimaryMap::new(),
109 memories: PrimaryMap::new(),
110 globals: PrimaryMap::new(),
111 start_func: None,
112 }
113 }
114 }
115
116 /// State for tracking and checking reachability at each operator. Used for unit testing with the
117 /// `DummyEnvironment`.
118 #[derive(Clone)]
119 pub struct ExpectedReachability {
120 /// Before- and after-reachability
121 reachability: Vec<(bool, bool)>,
122 before_idx: usize,
123 after_idx: usize,
124 }
125
126 impl ExpectedReachability {
check_before(&mut self, reachable: bool)127 fn check_before(&mut self, reachable: bool) {
128 assert_eq!(reachable, self.reachability[self.before_idx].0);
129 self.before_idx += 1;
130 }
check_after(&mut self, reachable: bool)131 fn check_after(&mut self, reachable: bool) {
132 assert_eq!(reachable, self.reachability[self.after_idx].1);
133 self.after_idx += 1;
134 }
check_end(&self)135 fn check_end(&self) {
136 assert_eq!(self.before_idx, self.reachability.len());
137 assert_eq!(self.after_idx, self.reachability.len());
138 }
139 }
140
141 /// This `ModuleEnvironment` implementation is a "naïve" one, doing essentially nothing and
142 /// emitting placeholders when forced to. Don't try to execute code translated for this
143 /// environment, essentially here for translation debug purposes.
144 pub struct DummyEnvironment {
145 /// Module information.
146 pub info: DummyModuleInfo,
147
148 /// Function translation.
149 trans: FuncTranslator,
150
151 /// Vector of wasm bytecode size for each function.
152 pub func_bytecode_sizes: Vec<usize>,
153
154 /// How to return from functions.
155 return_mode: ReturnMode,
156
157 /// Instructs to collect debug data during translation.
158 debug_info: bool,
159
160 /// Name of the module from the wasm file.
161 pub module_name: Option<String>,
162
163 /// Function names.
164 function_names: SecondaryMap<FuncIndex, String>,
165
166 /// Expected reachability data (before/after for each op) to assert. This is used for testing.
167 expected_reachability: Option<ExpectedReachability>,
168 }
169
170 impl DummyEnvironment {
171 /// Creates a new `DummyEnvironment` instance.
new(config: TargetFrontendConfig, return_mode: ReturnMode, debug_info: bool) -> Self172 pub fn new(config: TargetFrontendConfig, return_mode: ReturnMode, debug_info: bool) -> Self {
173 Self {
174 info: DummyModuleInfo::new(config),
175 trans: FuncTranslator::new(),
176 func_bytecode_sizes: Vec::new(),
177 return_mode,
178 debug_info,
179 module_name: None,
180 function_names: SecondaryMap::new(),
181 expected_reachability: None,
182 }
183 }
184
185 /// Return a `DummyFuncEnvironment` for translating functions within this
186 /// `DummyEnvironment`.
func_env(&self) -> DummyFuncEnvironment187 pub fn func_env(&self) -> DummyFuncEnvironment {
188 DummyFuncEnvironment::new(
189 &self.info,
190 self.return_mode,
191 self.expected_reachability.clone(),
192 )
193 }
194
get_func_type(&self, func_index: FuncIndex) -> TypeIndex195 fn get_func_type(&self, func_index: FuncIndex) -> TypeIndex {
196 self.info.functions[func_index].entity
197 }
198
199 /// Return the number of imported functions within this `DummyEnvironment`.
get_num_func_imports(&self) -> usize200 pub fn get_num_func_imports(&self) -> usize {
201 self.info.imported_funcs.len()
202 }
203
204 /// Return the name of the function, if a name for the function with
205 /// the corresponding index exists.
get_func_name(&self, func_index: FuncIndex) -> Option<&str>206 pub fn get_func_name(&self, func_index: FuncIndex) -> Option<&str> {
207 self.function_names.get(func_index).map(String::as_ref)
208 }
209
210 /// Test reachability bits before and after every opcode during translation, as provided by the
211 /// `FuncTranslationState`. This is generally used only for unit tests. This is applied to
212 /// every function in the module (so is likely only useful for test modules with one function).
test_expected_reachability(&mut self, reachability: Vec<(bool, bool)>)213 pub fn test_expected_reachability(&mut self, reachability: Vec<(bool, bool)>) {
214 self.expected_reachability = Some(ExpectedReachability {
215 reachability,
216 before_idx: 0,
217 after_idx: 0,
218 });
219 }
220 }
221
222 /// The `FuncEnvironment` implementation for use by the `DummyEnvironment`.
223 pub struct DummyFuncEnvironment<'dummy_environment> {
224 pub mod_info: &'dummy_environment DummyModuleInfo,
225
226 return_mode: ReturnMode,
227
228 /// Expected reachability data (before/after for each op) to assert. This is used for testing.
229 expected_reachability: Option<ExpectedReachability>,
230 }
231
232 impl<'dummy_environment> DummyFuncEnvironment<'dummy_environment> {
new( mod_info: &'dummy_environment DummyModuleInfo, return_mode: ReturnMode, expected_reachability: Option<ExpectedReachability>, ) -> Self233 pub fn new(
234 mod_info: &'dummy_environment DummyModuleInfo,
235 return_mode: ReturnMode,
236 expected_reachability: Option<ExpectedReachability>,
237 ) -> Self {
238 Self {
239 mod_info,
240 return_mode,
241 expected_reachability,
242 }
243 }
244
245 // Create a signature for `sigidx` amended with a `vmctx` argument after the standard wasm
246 // arguments.
vmctx_sig(&self, sigidx: TypeIndex) -> ir::Signature247 fn vmctx_sig(&self, sigidx: TypeIndex) -> ir::Signature {
248 let mut sig = self.mod_info.signatures[sigidx].clone();
249 sig.params.push(ir::AbiParam::special(
250 self.pointer_type(),
251 ir::ArgumentPurpose::VMContext,
252 ));
253 sig
254 }
255
reference_type(&self) -> ir::Type256 fn reference_type(&self) -> ir::Type {
257 match self.pointer_type() {
258 ir::types::I32 => ir::types::R32,
259 ir::types::I64 => ir::types::R64,
260 _ => panic!("unsupported pointer type"),
261 }
262 }
263 }
264
265 impl<'dummy_environment> TargetEnvironment for DummyFuncEnvironment<'dummy_environment> {
target_config(&self) -> TargetFrontendConfig266 fn target_config(&self) -> TargetFrontendConfig {
267 self.mod_info.config
268 }
269 }
270
271 impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environment> {
return_mode(&self) -> ReturnMode272 fn return_mode(&self) -> ReturnMode {
273 self.return_mode
274 }
275
make_global( &mut self, func: &mut ir::Function, index: GlobalIndex, ) -> WasmResult<GlobalVariable>276 fn make_global(
277 &mut self,
278 func: &mut ir::Function,
279 index: GlobalIndex,
280 ) -> WasmResult<GlobalVariable> {
281 // Just create a dummy `vmctx` global.
282 let offset = i32::try_from((index.index() * 8) + 8).unwrap().into();
283 let vmctx = func.create_global_value(ir::GlobalValueData::VMContext {});
284 Ok(GlobalVariable::Memory {
285 gv: vmctx,
286 offset,
287 ty: self.mod_info.globals[index].entity.ty,
288 })
289 }
290
make_heap(&mut self, func: &mut ir::Function, _index: MemoryIndex) -> WasmResult<ir::Heap>291 fn make_heap(&mut self, func: &mut ir::Function, _index: MemoryIndex) -> WasmResult<ir::Heap> {
292 // Create a static heap whose base address is stored at `vmctx+0`.
293 let addr = func.create_global_value(ir::GlobalValueData::VMContext);
294 let gv = func.create_global_value(ir::GlobalValueData::Load {
295 base: addr,
296 offset: Offset32::new(0),
297 global_type: self.pointer_type(),
298 readonly: true,
299 });
300
301 Ok(func.create_heap(ir::HeapData {
302 base: gv,
303 min_size: 0.into(),
304 offset_guard_size: 0x8000_0000.into(),
305 style: ir::HeapStyle::Static {
306 bound: 0x1_0000_0000.into(),
307 },
308 index_type: I32,
309 }))
310 }
311
make_table(&mut self, func: &mut ir::Function, _index: TableIndex) -> WasmResult<ir::Table>312 fn make_table(&mut self, func: &mut ir::Function, _index: TableIndex) -> WasmResult<ir::Table> {
313 // Create a table whose base address is stored at `vmctx+0`.
314 let vmctx = func.create_global_value(ir::GlobalValueData::VMContext);
315 let base_gv = func.create_global_value(ir::GlobalValueData::Load {
316 base: vmctx,
317 offset: Offset32::new(0),
318 global_type: self.pointer_type(),
319 readonly: true, // when tables in wasm become "growable", revisit whether this can be readonly or not.
320 });
321 let bound_gv = func.create_global_value(ir::GlobalValueData::Load {
322 base: vmctx,
323 offset: Offset32::new(0),
324 global_type: I32,
325 readonly: true,
326 });
327
328 Ok(func.create_table(ir::TableData {
329 base_gv,
330 min_size: Uimm64::new(0),
331 bound_gv,
332 element_size: Uimm64::from(u64::from(self.pointer_bytes()) * 2),
333 index_type: I32,
334 }))
335 }
336
make_indirect_sig( &mut self, func: &mut ir::Function, index: TypeIndex, ) -> WasmResult<ir::SigRef>337 fn make_indirect_sig(
338 &mut self,
339 func: &mut ir::Function,
340 index: TypeIndex,
341 ) -> WasmResult<ir::SigRef> {
342 // A real implementation would probably change the calling convention and add `vmctx` and
343 // signature index arguments.
344 Ok(func.import_signature(self.vmctx_sig(index)))
345 }
346
make_direct_func( &mut self, func: &mut ir::Function, index: FuncIndex, ) -> WasmResult<ir::FuncRef>347 fn make_direct_func(
348 &mut self,
349 func: &mut ir::Function,
350 index: FuncIndex,
351 ) -> WasmResult<ir::FuncRef> {
352 let sigidx = self.mod_info.functions[index].entity;
353 // A real implementation would probably add a `vmctx` argument.
354 // And maybe attempt some signature de-duplication.
355 let signature = func.import_signature(self.vmctx_sig(sigidx));
356 let name = get_func_name(index);
357 Ok(func.import_function(ir::ExtFuncData {
358 name,
359 signature,
360 colocated: false,
361 }))
362 }
363
before_translate_operator( &mut self, _op: &Operator, _builder: &mut FunctionBuilder, state: &FuncTranslationState, ) -> WasmResult<()>364 fn before_translate_operator(
365 &mut self,
366 _op: &Operator,
367 _builder: &mut FunctionBuilder,
368 state: &FuncTranslationState,
369 ) -> WasmResult<()> {
370 if let Some(ref mut r) = &mut self.expected_reachability {
371 r.check_before(state.reachable());
372 }
373 Ok(())
374 }
375
after_translate_operator( &mut self, _op: &Operator, _builder: &mut FunctionBuilder, state: &FuncTranslationState, ) -> WasmResult<()>376 fn after_translate_operator(
377 &mut self,
378 _op: &Operator,
379 _builder: &mut FunctionBuilder,
380 state: &FuncTranslationState,
381 ) -> WasmResult<()> {
382 if let Some(ref mut r) = &mut self.expected_reachability {
383 r.check_after(state.reachable());
384 }
385 Ok(())
386 }
387
after_translate_function( &mut self, _builder: &mut FunctionBuilder, _state: &FuncTranslationState, ) -> WasmResult<()>388 fn after_translate_function(
389 &mut self,
390 _builder: &mut FunctionBuilder,
391 _state: &FuncTranslationState,
392 ) -> WasmResult<()> {
393 if let Some(ref mut r) = &mut self.expected_reachability {
394 r.check_end();
395 }
396 Ok(())
397 }
398
translate_call_indirect( &mut self, mut pos: FuncCursor, _table_index: TableIndex, _table: ir::Table, _sig_index: TypeIndex, sig_ref: ir::SigRef, callee: ir::Value, call_args: &[ir::Value], ) -> WasmResult<ir::Inst>399 fn translate_call_indirect(
400 &mut self,
401 mut pos: FuncCursor,
402 _table_index: TableIndex,
403 _table: ir::Table,
404 _sig_index: TypeIndex,
405 sig_ref: ir::SigRef,
406 callee: ir::Value,
407 call_args: &[ir::Value],
408 ) -> WasmResult<ir::Inst> {
409 // Pass the current function's vmctx parameter on to the callee.
410 let vmctx = pos
411 .func
412 .special_param(ir::ArgumentPurpose::VMContext)
413 .expect("Missing vmctx parameter");
414
415 // The `callee` value is an index into a table of function pointers.
416 // Apparently, that table is stored at absolute address 0 in this dummy environment.
417 // TODO: Generate bounds checking code.
418 let ptr = self.pointer_type();
419 let callee_offset = if ptr == I32 {
420 pos.ins().imul_imm(callee, 4)
421 } else {
422 let ext = pos.ins().uextend(I64, callee);
423 pos.ins().imul_imm(ext, 4)
424 };
425 let mflags = ir::MemFlags::trusted();
426 let func_ptr = pos.ins().load(ptr, mflags, callee_offset, 0);
427
428 // Build a value list for the indirect call instruction containing the callee, call_args,
429 // and the vmctx parameter.
430 let mut args = ir::ValueList::default();
431 args.push(func_ptr, &mut pos.func.dfg.value_lists);
432 args.extend(call_args.iter().cloned(), &mut pos.func.dfg.value_lists);
433 args.push(vmctx, &mut pos.func.dfg.value_lists);
434
435 Ok(pos
436 .ins()
437 .CallIndirect(ir::Opcode::CallIndirect, INVALID, sig_ref, args)
438 .0)
439 }
440
translate_call( &mut self, mut pos: FuncCursor, _callee_index: FuncIndex, callee: ir::FuncRef, call_args: &[ir::Value], ) -> WasmResult<ir::Inst>441 fn translate_call(
442 &mut self,
443 mut pos: FuncCursor,
444 _callee_index: FuncIndex,
445 callee: ir::FuncRef,
446 call_args: &[ir::Value],
447 ) -> WasmResult<ir::Inst> {
448 // Pass the current function's vmctx parameter on to the callee.
449 let vmctx = pos
450 .func
451 .special_param(ir::ArgumentPurpose::VMContext)
452 .expect("Missing vmctx parameter");
453
454 // Build a value list for the call instruction containing the call_args and the vmctx
455 // parameter.
456 let mut args = ir::ValueList::default();
457 args.extend(call_args.iter().cloned(), &mut pos.func.dfg.value_lists);
458 args.push(vmctx, &mut pos.func.dfg.value_lists);
459
460 Ok(pos.ins().Call(ir::Opcode::Call, INVALID, callee, args).0)
461 }
462
translate_memory_grow( &mut self, mut pos: FuncCursor, _index: MemoryIndex, _heap: ir::Heap, _val: ir::Value, ) -> WasmResult<ir::Value>463 fn translate_memory_grow(
464 &mut self,
465 mut pos: FuncCursor,
466 _index: MemoryIndex,
467 _heap: ir::Heap,
468 _val: ir::Value,
469 ) -> WasmResult<ir::Value> {
470 Ok(pos.ins().iconst(I32, -1))
471 }
472
translate_memory_size( &mut self, mut pos: FuncCursor, _index: MemoryIndex, _heap: ir::Heap, ) -> WasmResult<ir::Value>473 fn translate_memory_size(
474 &mut self,
475 mut pos: FuncCursor,
476 _index: MemoryIndex,
477 _heap: ir::Heap,
478 ) -> WasmResult<ir::Value> {
479 Ok(pos.ins().iconst(I32, -1))
480 }
481
translate_memory_copy( &mut self, _pos: FuncCursor, _src_index: MemoryIndex, _src_heap: ir::Heap, _dst_index: MemoryIndex, _dst_heap: ir::Heap, _dst: ir::Value, _src: ir::Value, _len: ir::Value, ) -> WasmResult<()>482 fn translate_memory_copy(
483 &mut self,
484 _pos: FuncCursor,
485 _src_index: MemoryIndex,
486 _src_heap: ir::Heap,
487 _dst_index: MemoryIndex,
488 _dst_heap: ir::Heap,
489 _dst: ir::Value,
490 _src: ir::Value,
491 _len: ir::Value,
492 ) -> WasmResult<()> {
493 Ok(())
494 }
495
translate_memory_fill( &mut self, _pos: FuncCursor, _index: MemoryIndex, _heap: ir::Heap, _dst: ir::Value, _val: ir::Value, _len: ir::Value, ) -> WasmResult<()>496 fn translate_memory_fill(
497 &mut self,
498 _pos: FuncCursor,
499 _index: MemoryIndex,
500 _heap: ir::Heap,
501 _dst: ir::Value,
502 _val: ir::Value,
503 _len: ir::Value,
504 ) -> WasmResult<()> {
505 Ok(())
506 }
507
translate_memory_init( &mut self, _pos: FuncCursor, _index: MemoryIndex, _heap: ir::Heap, _seg_index: u32, _dst: ir::Value, _src: ir::Value, _len: ir::Value, ) -> WasmResult<()>508 fn translate_memory_init(
509 &mut self,
510 _pos: FuncCursor,
511 _index: MemoryIndex,
512 _heap: ir::Heap,
513 _seg_index: u32,
514 _dst: ir::Value,
515 _src: ir::Value,
516 _len: ir::Value,
517 ) -> WasmResult<()> {
518 Ok(())
519 }
520
translate_data_drop(&mut self, _pos: FuncCursor, _seg_index: u32) -> WasmResult<()>521 fn translate_data_drop(&mut self, _pos: FuncCursor, _seg_index: u32) -> WasmResult<()> {
522 Ok(())
523 }
524
translate_table_size( &mut self, mut pos: FuncCursor, _index: TableIndex, _table: ir::Table, ) -> WasmResult<ir::Value>525 fn translate_table_size(
526 &mut self,
527 mut pos: FuncCursor,
528 _index: TableIndex,
529 _table: ir::Table,
530 ) -> WasmResult<ir::Value> {
531 Ok(pos.ins().iconst(I32, -1))
532 }
533
translate_table_grow( &mut self, mut pos: FuncCursor, _table_index: TableIndex, _table: ir::Table, _delta: ir::Value, _init_value: ir::Value, ) -> WasmResult<ir::Value>534 fn translate_table_grow(
535 &mut self,
536 mut pos: FuncCursor,
537 _table_index: TableIndex,
538 _table: ir::Table,
539 _delta: ir::Value,
540 _init_value: ir::Value,
541 ) -> WasmResult<ir::Value> {
542 Ok(pos.ins().iconst(I32, -1))
543 }
544
translate_table_get( &mut self, builder: &mut FunctionBuilder, _table_index: TableIndex, _table: ir::Table, _index: ir::Value, ) -> WasmResult<ir::Value>545 fn translate_table_get(
546 &mut self,
547 builder: &mut FunctionBuilder,
548 _table_index: TableIndex,
549 _table: ir::Table,
550 _index: ir::Value,
551 ) -> WasmResult<ir::Value> {
552 Ok(builder.ins().null(self.reference_type()))
553 }
554
translate_table_set( &mut self, _builder: &mut FunctionBuilder, _table_index: TableIndex, _table: ir::Table, _value: ir::Value, _index: ir::Value, ) -> WasmResult<()>555 fn translate_table_set(
556 &mut self,
557 _builder: &mut FunctionBuilder,
558 _table_index: TableIndex,
559 _table: ir::Table,
560 _value: ir::Value,
561 _index: ir::Value,
562 ) -> WasmResult<()> {
563 Ok(())
564 }
565
translate_table_copy( &mut self, _pos: FuncCursor, _dst_index: TableIndex, _dst_table: ir::Table, _src_index: TableIndex, _src_table: ir::Table, _dst: ir::Value, _src: ir::Value, _len: ir::Value, ) -> WasmResult<()>566 fn translate_table_copy(
567 &mut self,
568 _pos: FuncCursor,
569 _dst_index: TableIndex,
570 _dst_table: ir::Table,
571 _src_index: TableIndex,
572 _src_table: ir::Table,
573 _dst: ir::Value,
574 _src: ir::Value,
575 _len: ir::Value,
576 ) -> WasmResult<()> {
577 Ok(())
578 }
579
translate_table_fill( &mut self, _pos: FuncCursor, _table_index: TableIndex, _dst: ir::Value, _val: ir::Value, _len: ir::Value, ) -> WasmResult<()>580 fn translate_table_fill(
581 &mut self,
582 _pos: FuncCursor,
583 _table_index: TableIndex,
584 _dst: ir::Value,
585 _val: ir::Value,
586 _len: ir::Value,
587 ) -> WasmResult<()> {
588 Ok(())
589 }
590
translate_table_init( &mut self, _pos: FuncCursor, _seg_index: u32, _table_index: TableIndex, _table: ir::Table, _dst: ir::Value, _src: ir::Value, _len: ir::Value, ) -> WasmResult<()>591 fn translate_table_init(
592 &mut self,
593 _pos: FuncCursor,
594 _seg_index: u32,
595 _table_index: TableIndex,
596 _table: ir::Table,
597 _dst: ir::Value,
598 _src: ir::Value,
599 _len: ir::Value,
600 ) -> WasmResult<()> {
601 Ok(())
602 }
603
translate_elem_drop(&mut self, _pos: FuncCursor, _seg_index: u32) -> WasmResult<()>604 fn translate_elem_drop(&mut self, _pos: FuncCursor, _seg_index: u32) -> WasmResult<()> {
605 Ok(())
606 }
607
translate_ref_func( &mut self, mut pos: FuncCursor, _func_index: FuncIndex, ) -> WasmResult<ir::Value>608 fn translate_ref_func(
609 &mut self,
610 mut pos: FuncCursor,
611 _func_index: FuncIndex,
612 ) -> WasmResult<ir::Value> {
613 Ok(pos.ins().null(self.reference_type()))
614 }
615
translate_custom_global_get( &mut self, mut pos: FuncCursor, _global_index: GlobalIndex, ) -> WasmResult<ir::Value>616 fn translate_custom_global_get(
617 &mut self,
618 mut pos: FuncCursor,
619 _global_index: GlobalIndex,
620 ) -> WasmResult<ir::Value> {
621 Ok(pos.ins().iconst(I32, -1))
622 }
623
translate_custom_global_set( &mut self, _pos: FuncCursor, _global_index: GlobalIndex, _val: ir::Value, ) -> WasmResult<()>624 fn translate_custom_global_set(
625 &mut self,
626 _pos: FuncCursor,
627 _global_index: GlobalIndex,
628 _val: ir::Value,
629 ) -> WasmResult<()> {
630 Ok(())
631 }
632
translate_atomic_wait( &mut self, mut pos: FuncCursor, _index: MemoryIndex, _heap: ir::Heap, _addr: ir::Value, _expected: ir::Value, _timeout: ir::Value, ) -> WasmResult<ir::Value>633 fn translate_atomic_wait(
634 &mut self,
635 mut pos: FuncCursor,
636 _index: MemoryIndex,
637 _heap: ir::Heap,
638 _addr: ir::Value,
639 _expected: ir::Value,
640 _timeout: ir::Value,
641 ) -> WasmResult<ir::Value> {
642 Ok(pos.ins().iconst(I32, -1))
643 }
644
translate_atomic_notify( &mut self, mut pos: FuncCursor, _index: MemoryIndex, _heap: ir::Heap, _addr: ir::Value, _count: ir::Value, ) -> WasmResult<ir::Value>645 fn translate_atomic_notify(
646 &mut self,
647 mut pos: FuncCursor,
648 _index: MemoryIndex,
649 _heap: ir::Heap,
650 _addr: ir::Value,
651 _count: ir::Value,
652 ) -> WasmResult<ir::Value> {
653 Ok(pos.ins().iconst(I32, 0))
654 }
655 }
656
657 impl TargetEnvironment for DummyEnvironment {
target_config(&self) -> TargetFrontendConfig658 fn target_config(&self) -> TargetFrontendConfig {
659 self.info.config
660 }
661 }
662
663 impl<'data> ModuleEnvironment<'data> for DummyEnvironment {
declare_type_func(&mut self, wasm: WasmFuncType) -> WasmResult<()>664 fn declare_type_func(&mut self, wasm: WasmFuncType) -> WasmResult<()> {
665 let mut sig = ir::Signature::new(CallConv::Fast);
666 let mut cvt = |ty: &WasmType| {
667 let reference_type = match self.pointer_type() {
668 ir::types::I32 => ir::types::R32,
669 ir::types::I64 => ir::types::R64,
670 _ => panic!("unsupported pointer type"),
671 };
672 ir::AbiParam::new(match ty {
673 WasmType::I32 => ir::types::I32,
674 WasmType::I64 => ir::types::I64,
675 WasmType::F32 => ir::types::F32,
676 WasmType::F64 => ir::types::F64,
677 WasmType::V128 => ir::types::I8X16,
678 WasmType::FuncRef | WasmType::ExternRef | WasmType::ExnRef => reference_type,
679 })
680 };
681 sig.params.extend(wasm.params.iter().map(&mut cvt));
682 sig.returns.extend(wasm.returns.iter().map(&mut cvt));
683 self.info.signatures.push(sig);
684 Ok(())
685 }
686
declare_func_import( &mut self, index: TypeIndex, module: &'data str, field: Option<&'data str>, ) -> WasmResult<()>687 fn declare_func_import(
688 &mut self,
689 index: TypeIndex,
690 module: &'data str,
691 field: Option<&'data str>,
692 ) -> WasmResult<()> {
693 assert_eq!(
694 self.info.functions.len(),
695 self.info.imported_funcs.len(),
696 "Imported functions must be declared first"
697 );
698 self.info.functions.push(Exportable::new(index));
699 self.info
700 .imported_funcs
701 .push((String::from(module), String::from(field.unwrap())));
702 Ok(())
703 }
704
declare_func_type(&mut self, index: TypeIndex) -> WasmResult<()>705 fn declare_func_type(&mut self, index: TypeIndex) -> WasmResult<()> {
706 self.info.functions.push(Exportable::new(index));
707 Ok(())
708 }
709
declare_global(&mut self, global: Global) -> WasmResult<()>710 fn declare_global(&mut self, global: Global) -> WasmResult<()> {
711 self.info.globals.push(Exportable::new(global));
712 Ok(())
713 }
714
declare_global_import( &mut self, global: Global, module: &'data str, field: Option<&'data str>, ) -> WasmResult<()>715 fn declare_global_import(
716 &mut self,
717 global: Global,
718 module: &'data str,
719 field: Option<&'data str>,
720 ) -> WasmResult<()> {
721 self.info.globals.push(Exportable::new(global));
722 self.info
723 .imported_globals
724 .push((String::from(module), String::from(field.unwrap())));
725 Ok(())
726 }
727
declare_table(&mut self, table: Table) -> WasmResult<()>728 fn declare_table(&mut self, table: Table) -> WasmResult<()> {
729 self.info.tables.push(Exportable::new(table));
730 Ok(())
731 }
732
declare_table_import( &mut self, table: Table, module: &'data str, field: Option<&'data str>, ) -> WasmResult<()>733 fn declare_table_import(
734 &mut self,
735 table: Table,
736 module: &'data str,
737 field: Option<&'data str>,
738 ) -> WasmResult<()> {
739 self.info.tables.push(Exportable::new(table));
740 self.info
741 .imported_tables
742 .push((String::from(module), String::from(field.unwrap())));
743 Ok(())
744 }
745
declare_table_elements( &mut self, _table_index: TableIndex, _base: Option<GlobalIndex>, _offset: u32, _elements: Box<[FuncIndex]>, ) -> WasmResult<()>746 fn declare_table_elements(
747 &mut self,
748 _table_index: TableIndex,
749 _base: Option<GlobalIndex>,
750 _offset: u32,
751 _elements: Box<[FuncIndex]>,
752 ) -> WasmResult<()> {
753 // We do nothing
754 Ok(())
755 }
756
declare_passive_element( &mut self, _elem_index: ElemIndex, _segments: Box<[FuncIndex]>, ) -> WasmResult<()>757 fn declare_passive_element(
758 &mut self,
759 _elem_index: ElemIndex,
760 _segments: Box<[FuncIndex]>,
761 ) -> WasmResult<()> {
762 Ok(())
763 }
764
declare_passive_data( &mut self, _elem_index: DataIndex, _segments: &'data [u8], ) -> WasmResult<()>765 fn declare_passive_data(
766 &mut self,
767 _elem_index: DataIndex,
768 _segments: &'data [u8],
769 ) -> WasmResult<()> {
770 Ok(())
771 }
772
declare_memory(&mut self, memory: Memory) -> WasmResult<()>773 fn declare_memory(&mut self, memory: Memory) -> WasmResult<()> {
774 self.info.memories.push(Exportable::new(memory));
775 Ok(())
776 }
777
declare_memory_import( &mut self, memory: Memory, module: &'data str, field: Option<&'data str>, ) -> WasmResult<()>778 fn declare_memory_import(
779 &mut self,
780 memory: Memory,
781 module: &'data str,
782 field: Option<&'data str>,
783 ) -> WasmResult<()> {
784 self.info.memories.push(Exportable::new(memory));
785 self.info
786 .imported_memories
787 .push((String::from(module), String::from(field.unwrap())));
788 Ok(())
789 }
790
declare_data_initialization( &mut self, _memory_index: MemoryIndex, _base: Option<GlobalIndex>, _offset: u32, _data: &'data [u8], ) -> WasmResult<()>791 fn declare_data_initialization(
792 &mut self,
793 _memory_index: MemoryIndex,
794 _base: Option<GlobalIndex>,
795 _offset: u32,
796 _data: &'data [u8],
797 ) -> WasmResult<()> {
798 // We do nothing
799 Ok(())
800 }
801
declare_func_export(&mut self, func_index: FuncIndex, name: &'data str) -> WasmResult<()>802 fn declare_func_export(&mut self, func_index: FuncIndex, name: &'data str) -> WasmResult<()> {
803 self.info.functions[func_index]
804 .export_names
805 .push(String::from(name));
806 Ok(())
807 }
808
declare_table_export( &mut self, table_index: TableIndex, name: &'data str, ) -> WasmResult<()>809 fn declare_table_export(
810 &mut self,
811 table_index: TableIndex,
812 name: &'data str,
813 ) -> WasmResult<()> {
814 self.info.tables[table_index]
815 .export_names
816 .push(String::from(name));
817 Ok(())
818 }
819
declare_memory_export( &mut self, memory_index: MemoryIndex, name: &'data str, ) -> WasmResult<()>820 fn declare_memory_export(
821 &mut self,
822 memory_index: MemoryIndex,
823 name: &'data str,
824 ) -> WasmResult<()> {
825 self.info.memories[memory_index]
826 .export_names
827 .push(String::from(name));
828 Ok(())
829 }
830
declare_global_export( &mut self, global_index: GlobalIndex, name: &'data str, ) -> WasmResult<()>831 fn declare_global_export(
832 &mut self,
833 global_index: GlobalIndex,
834 name: &'data str,
835 ) -> WasmResult<()> {
836 self.info.globals[global_index]
837 .export_names
838 .push(String::from(name));
839 Ok(())
840 }
841
declare_start_func(&mut self, func_index: FuncIndex) -> WasmResult<()>842 fn declare_start_func(&mut self, func_index: FuncIndex) -> WasmResult<()> {
843 debug_assert!(self.info.start_func.is_none());
844 self.info.start_func = Some(func_index);
845 Ok(())
846 }
847
define_function_body( &mut self, mut validator: FuncValidator<ValidatorResources>, body: FunctionBody<'data>, ) -> WasmResult<()>848 fn define_function_body(
849 &mut self,
850 mut validator: FuncValidator<ValidatorResources>,
851 body: FunctionBody<'data>,
852 ) -> WasmResult<()> {
853 self.func_bytecode_sizes
854 .push(body.get_binary_reader().bytes_remaining());
855 let func = {
856 let mut func_environ = DummyFuncEnvironment::new(
857 &self.info,
858 self.return_mode,
859 self.expected_reachability.clone(),
860 );
861 let func_index =
862 FuncIndex::new(self.get_num_func_imports() + self.info.function_bodies.len());
863 let name = get_func_name(func_index);
864 let sig = func_environ.vmctx_sig(self.get_func_type(func_index));
865 let mut func = ir::Function::with_name_signature(name, sig);
866 if self.debug_info {
867 func.collect_debug_info();
868 }
869 self.trans
870 .translate_body(&mut validator, body, &mut func, &mut func_environ)?;
871 func
872 };
873 self.info.function_bodies.push(func);
874 Ok(())
875 }
876
declare_module_name(&mut self, name: &'data str)877 fn declare_module_name(&mut self, name: &'data str) {
878 self.module_name = Some(String::from(name));
879 }
880
declare_func_name(&mut self, func_index: FuncIndex, name: &'data str)881 fn declare_func_name(&mut self, func_index: FuncIndex, name: &'data str) {
882 self.function_names[func_index] = String::from(name);
883 }
884
wasm_features(&self) -> WasmFeatures885 fn wasm_features(&self) -> WasmFeatures {
886 WasmFeatures {
887 multi_value: true,
888 simd: true,
889 reference_types: true,
890 bulk_memory: true,
891 ..WasmFeatures::default()
892 }
893 }
894 }
895