wasmer_clif_fork_wasm/environ/
dummy.rs

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
8use crate::environ::{
9    FuncEnvironment, GlobalVariable, ModuleEnvironment, ReturnMode, TargetEnvironment, WasmResult,
10};
11use crate::func_translator::FuncTranslator;
12use crate::state::ModuleTranslationState;
13use crate::translation_utils::{
14    DefinedFuncIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, PassiveDataIndex,
15    PassiveElemIndex, SignatureIndex, Table, TableIndex,
16};
17use core::convert::TryFrom;
18use cranelift_codegen::cursor::FuncCursor;
19use cranelift_codegen::ir::immediates::{Offset32, Uimm64};
20use cranelift_codegen::ir::types::*;
21use cranelift_codegen::ir::{self, InstBuilder};
22use cranelift_codegen::isa::TargetFrontendConfig;
23use cranelift_entity::{EntityRef, PrimaryMap, SecondaryMap};
24use std::boxed::Box;
25use std::string::String;
26use std::vec::Vec;
27
28/// Compute a `ir::ExternalName` for a given wasm function index.
29fn get_func_name(func_index: FuncIndex) -> ir::ExternalName {
30    ir::ExternalName::user(0, func_index.as_u32())
31}
32
33/// A collection of names under which a given entity is exported.
34pub struct Exportable<T> {
35    /// A wasm entity.
36    pub entity: T,
37
38    /// Names under which the entity is exported.
39    pub export_names: Vec<String>,
40}
41
42impl<T> Exportable<T> {
43    pub fn new(entity: T) -> Self {
44        Self {
45            entity,
46            export_names: Vec::new(),
47        }
48    }
49}
50
51/// The main state belonging to a `DummyEnvironment`. This is split out from
52/// `DummyEnvironment` to allow it to be borrowed separately from the
53/// `FuncTranslator` field.
54pub struct DummyModuleInfo {
55    /// Target description relevant to frontends producing Cranelift IR.
56    config: TargetFrontendConfig,
57
58    /// Signatures as provided by `declare_signature`.
59    pub signatures: PrimaryMap<SignatureIndex, ir::Signature>,
60
61    /// Module and field names of imported functions as provided by `declare_func_import`.
62    pub imported_funcs: Vec<(String, String)>,
63
64    /// Module and field names of imported globals as provided by `declare_global_import`.
65    pub imported_globals: Vec<(String, String)>,
66
67    /// Module and field names of imported tables as provided by `declare_table_import`.
68    pub imported_tables: Vec<(String, String)>,
69
70    /// Module and field names of imported memories as provided by `declare_memory_import`.
71    pub imported_memories: Vec<(String, String)>,
72
73    /// Functions, imported and local.
74    pub functions: PrimaryMap<FuncIndex, Exportable<SignatureIndex>>,
75
76    /// Function bodies.
77    pub function_bodies: PrimaryMap<DefinedFuncIndex, ir::Function>,
78
79    /// Tables as provided by `declare_table`.
80    pub tables: PrimaryMap<TableIndex, Exportable<Table>>,
81
82    /// Memories as provided by `declare_memory`.
83    pub memories: PrimaryMap<MemoryIndex, Exportable<Memory>>,
84
85    /// Globals as provided by `declare_global`.
86    pub globals: PrimaryMap<GlobalIndex, Exportable<Global>>,
87
88    /// The start function.
89    pub start_func: Option<FuncIndex>,
90}
91
92impl DummyModuleInfo {
93    /// Creates a new `DummyModuleInfo` instance.
94    pub fn new(config: TargetFrontendConfig) -> Self {
95        Self {
96            config,
97            signatures: PrimaryMap::new(),
98            imported_funcs: Vec::new(),
99            imported_globals: Vec::new(),
100            imported_tables: Vec::new(),
101            imported_memories: Vec::new(),
102            functions: PrimaryMap::new(),
103            function_bodies: PrimaryMap::new(),
104            tables: PrimaryMap::new(),
105            memories: PrimaryMap::new(),
106            globals: PrimaryMap::new(),
107            start_func: None,
108        }
109    }
110}
111
112/// This `ModuleEnvironment` implementation is a "naïve" one, doing essentially nothing and
113/// emitting placeholders when forced to. Don't try to execute code translated for this
114/// environment, essentially here for translation debug purposes.
115pub struct DummyEnvironment {
116    /// Module information.
117    pub info: DummyModuleInfo,
118
119    /// Function translation.
120    trans: FuncTranslator,
121
122    /// Vector of wasm bytecode size for each function.
123    pub func_bytecode_sizes: Vec<usize>,
124
125    /// How to return from functions.
126    return_mode: ReturnMode,
127
128    /// Instructs to collect debug data during translation.
129    debug_info: bool,
130
131    /// Function names.
132    function_names: SecondaryMap<FuncIndex, String>,
133}
134
135impl DummyEnvironment {
136    /// Creates a new `DummyEnvironment` instance.
137    pub fn new(config: TargetFrontendConfig, return_mode: ReturnMode, debug_info: bool) -> Self {
138        Self {
139            info: DummyModuleInfo::new(config),
140            trans: FuncTranslator::new(),
141            func_bytecode_sizes: Vec::new(),
142            return_mode,
143            debug_info,
144            function_names: SecondaryMap::new(),
145        }
146    }
147
148    /// Return a `DummyFuncEnvironment` for translating functions within this
149    /// `DummyEnvironment`.
150    pub fn func_env(&self) -> DummyFuncEnvironment {
151        DummyFuncEnvironment::new(&self.info, self.return_mode)
152    }
153
154    fn get_func_type(&self, func_index: FuncIndex) -> SignatureIndex {
155        self.info.functions[func_index].entity
156    }
157
158    /// Return the number of imported functions within this `DummyEnvironment`.
159    pub fn get_num_func_imports(&self) -> usize {
160        self.info.imported_funcs.len()
161    }
162
163    /// Return the name of the function, if a name for the function with
164    /// the corresponding index exists.
165    pub fn get_func_name(&self, func_index: FuncIndex) -> Option<&str> {
166        self.function_names.get(func_index).map(String::as_ref)
167    }
168}
169
170/// The `FuncEnvironment` implementation for use by the `DummyEnvironment`.
171pub struct DummyFuncEnvironment<'dummy_environment> {
172    pub mod_info: &'dummy_environment DummyModuleInfo,
173
174    return_mode: ReturnMode,
175}
176
177impl<'dummy_environment> DummyFuncEnvironment<'dummy_environment> {
178    pub fn new(mod_info: &'dummy_environment DummyModuleInfo, return_mode: ReturnMode) -> Self {
179        Self {
180            mod_info,
181            return_mode,
182        }
183    }
184
185    // Create a signature for `sigidx` amended with a `vmctx` argument after the standard wasm
186    // arguments.
187    fn vmctx_sig(&self, sigidx: SignatureIndex) -> ir::Signature {
188        let mut sig = self.mod_info.signatures[sigidx].clone();
189        sig.params.push(ir::AbiParam::special(
190            self.pointer_type(),
191            ir::ArgumentPurpose::VMContext,
192        ));
193        sig
194    }
195}
196
197impl<'dummy_environment> TargetEnvironment for DummyFuncEnvironment<'dummy_environment> {
198    fn target_config(&self) -> TargetFrontendConfig {
199        self.mod_info.config
200    }
201}
202
203impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environment> {
204    fn return_mode(&self) -> ReturnMode {
205        self.return_mode
206    }
207
208    fn make_global(
209        &mut self,
210        func: &mut ir::Function,
211        index: GlobalIndex,
212    ) -> WasmResult<GlobalVariable> {
213        // Just create a dummy `vmctx` global.
214        let offset = i32::try_from((index.index() * 8) + 8).unwrap().into();
215        let vmctx = func.create_global_value(ir::GlobalValueData::VMContext {});
216        Ok(GlobalVariable::Memory {
217            gv: vmctx,
218            offset,
219            ty: self.mod_info.globals[index].entity.ty,
220        })
221    }
222
223    fn make_heap(&mut self, func: &mut ir::Function, _index: MemoryIndex) -> WasmResult<ir::Heap> {
224        // Create a static heap whose base address is stored at `vmctx+0`.
225        let addr = func.create_global_value(ir::GlobalValueData::VMContext);
226        let gv = func.create_global_value(ir::GlobalValueData::Load {
227            base: addr,
228            offset: Offset32::new(0),
229            global_type: self.pointer_type(),
230            readonly: true,
231        });
232
233        Ok(func.create_heap(ir::HeapData {
234            base: gv,
235            min_size: 0.into(),
236            offset_guard_size: 0x8000_0000.into(),
237            style: ir::HeapStyle::Static {
238                bound: 0x1_0000_0000.into(),
239            },
240            index_type: I32,
241        }))
242    }
243
244    fn make_table(&mut self, func: &mut ir::Function, _index: TableIndex) -> WasmResult<ir::Table> {
245        // Create a table whose base address is stored at `vmctx+0`.
246        let vmctx = func.create_global_value(ir::GlobalValueData::VMContext);
247        let base_gv = func.create_global_value(ir::GlobalValueData::Load {
248            base: vmctx,
249            offset: Offset32::new(0),
250            global_type: self.pointer_type(),
251            readonly: true, // when tables in wasm become "growable", revisit whether this can be readonly or not.
252        });
253        let bound_gv = func.create_global_value(ir::GlobalValueData::Load {
254            base: vmctx,
255            offset: Offset32::new(0),
256            global_type: I32,
257            readonly: true,
258        });
259
260        Ok(func.create_table(ir::TableData {
261            base_gv,
262            min_size: Uimm64::new(0),
263            bound_gv,
264            element_size: Uimm64::from(u64::from(self.pointer_bytes()) * 2),
265            index_type: I32,
266        }))
267    }
268
269    fn make_indirect_sig(
270        &mut self,
271        func: &mut ir::Function,
272        index: SignatureIndex,
273    ) -> WasmResult<ir::SigRef> {
274        // A real implementation would probably change the calling convention and add `vmctx` and
275        // signature index arguments.
276        Ok(func.import_signature(self.vmctx_sig(index)))
277    }
278
279    fn make_direct_func(
280        &mut self,
281        func: &mut ir::Function,
282        index: FuncIndex,
283    ) -> WasmResult<ir::FuncRef> {
284        let sigidx = self.mod_info.functions[index].entity;
285        // A real implementation would probably add a `vmctx` argument.
286        // And maybe attempt some signature de-duplication.
287        let signature = func.import_signature(self.vmctx_sig(sigidx));
288        let name = get_func_name(index);
289        Ok(func.import_function(ir::ExtFuncData {
290            name,
291            signature,
292            colocated: false,
293        }))
294    }
295
296    fn translate_call_indirect(
297        &mut self,
298        mut pos: FuncCursor,
299        _table_index: TableIndex,
300        _table: ir::Table,
301        _sig_index: SignatureIndex,
302        sig_ref: ir::SigRef,
303        callee: ir::Value,
304        call_args: &[ir::Value],
305    ) -> WasmResult<ir::Inst> {
306        // Pass the current function's vmctx parameter on to the callee.
307        let vmctx = pos
308            .func
309            .special_param(ir::ArgumentPurpose::VMContext)
310            .expect("Missing vmctx parameter");
311
312        // The `callee` value is an index into a table of function pointers.
313        // Apparently, that table is stored at absolute address 0 in this dummy environment.
314        // TODO: Generate bounds checking code.
315        let ptr = self.pointer_type();
316        let callee_offset = if ptr == I32 {
317            pos.ins().imul_imm(callee, 4)
318        } else {
319            let ext = pos.ins().uextend(I64, callee);
320            pos.ins().imul_imm(ext, 4)
321        };
322        let mflags = ir::MemFlags::trusted();
323        let func_ptr = pos.ins().load(ptr, mflags, callee_offset, 0);
324
325        // Build a value list for the indirect call instruction containing the callee, call_args,
326        // and the vmctx parameter.
327        let mut args = ir::ValueList::default();
328        args.push(func_ptr, &mut pos.func.dfg.value_lists);
329        args.extend(call_args.iter().cloned(), &mut pos.func.dfg.value_lists);
330        args.push(vmctx, &mut pos.func.dfg.value_lists);
331
332        Ok(pos
333            .ins()
334            .CallIndirect(ir::Opcode::CallIndirect, INVALID, sig_ref, args)
335            .0)
336    }
337
338    fn translate_call(
339        &mut self,
340        mut pos: FuncCursor,
341        _callee_index: FuncIndex,
342        callee: ir::FuncRef,
343        call_args: &[ir::Value],
344    ) -> WasmResult<ir::Inst> {
345        // Pass the current function's vmctx parameter on to the callee.
346        let vmctx = pos
347            .func
348            .special_param(ir::ArgumentPurpose::VMContext)
349            .expect("Missing vmctx parameter");
350
351        // Build a value list for the call instruction containing the call_args and the vmctx
352        // parameter.
353        let mut args = ir::ValueList::default();
354        args.extend(call_args.iter().cloned(), &mut pos.func.dfg.value_lists);
355        args.push(vmctx, &mut pos.func.dfg.value_lists);
356
357        Ok(pos.ins().Call(ir::Opcode::Call, INVALID, callee, args).0)
358    }
359
360    fn translate_memory_grow(
361        &mut self,
362        mut pos: FuncCursor,
363        _index: MemoryIndex,
364        _heap: ir::Heap,
365        _val: ir::Value,
366    ) -> WasmResult<ir::Value> {
367        Ok(pos.ins().iconst(I32, -1))
368    }
369
370    fn translate_memory_size(
371        &mut self,
372        mut pos: FuncCursor,
373        _index: MemoryIndex,
374        _heap: ir::Heap,
375    ) -> WasmResult<ir::Value> {
376        Ok(pos.ins().iconst(I32, -1))
377    }
378
379    fn translate_memory_copy(
380        &mut self,
381        _pos: FuncCursor,
382        _index: MemoryIndex,
383        _heap: ir::Heap,
384        _dst: ir::Value,
385        _src: ir::Value,
386        _len: ir::Value,
387    ) -> WasmResult<()> {
388        Ok(())
389    }
390
391    fn translate_memory_fill(
392        &mut self,
393        _pos: FuncCursor,
394        _index: MemoryIndex,
395        _heap: ir::Heap,
396        _dst: ir::Value,
397        _val: ir::Value,
398        _len: ir::Value,
399    ) -> WasmResult<()> {
400        Ok(())
401    }
402
403    fn translate_memory_init(
404        &mut self,
405        _pos: FuncCursor,
406        _index: MemoryIndex,
407        _heap: ir::Heap,
408        _seg_index: u32,
409        _dst: ir::Value,
410        _src: ir::Value,
411        _len: ir::Value,
412    ) -> WasmResult<()> {
413        Ok(())
414    }
415
416    fn translate_data_drop(&mut self, _pos: FuncCursor, _seg_index: u32) -> WasmResult<()> {
417        Ok(())
418    }
419
420    fn translate_table_size(
421        &mut self,
422        mut pos: FuncCursor,
423        _index: TableIndex,
424        _table: ir::Table,
425    ) -> WasmResult<ir::Value> {
426        Ok(pos.ins().iconst(I32, -1))
427    }
428
429    fn translate_table_grow(
430        &mut self,
431        mut pos: FuncCursor,
432        _table_index: u32,
433        _delta: ir::Value,
434        _init_value: ir::Value,
435    ) -> WasmResult<ir::Value> {
436        Ok(pos.ins().iconst(I32, -1))
437    }
438
439    fn translate_table_get(
440        &mut self,
441        mut pos: FuncCursor,
442        _table_index: u32,
443        _index: ir::Value,
444    ) -> WasmResult<ir::Value> {
445        Ok(pos.ins().null(self.reference_type()))
446    }
447
448    fn translate_table_set(
449        &mut self,
450        _pos: FuncCursor,
451        _table_index: u32,
452        _value: ir::Value,
453        _index: ir::Value,
454    ) -> WasmResult<()> {
455        Ok(())
456    }
457
458    fn translate_table_copy(
459        &mut self,
460        _pos: FuncCursor,
461        _dst_index: TableIndex,
462        _dst_table: ir::Table,
463        _src_index: TableIndex,
464        _src_table: ir::Table,
465        _dst: ir::Value,
466        _src: ir::Value,
467        _len: ir::Value,
468    ) -> WasmResult<()> {
469        Ok(())
470    }
471
472    fn translate_table_fill(
473        &mut self,
474        _pos: FuncCursor,
475        _table_index: u32,
476        _dst: ir::Value,
477        _val: ir::Value,
478        _len: ir::Value,
479    ) -> WasmResult<()> {
480        Ok(())
481    }
482
483    fn translate_table_init(
484        &mut self,
485        _pos: FuncCursor,
486        _seg_index: u32,
487        _table_index: TableIndex,
488        _table: ir::Table,
489        _dst: ir::Value,
490        _src: ir::Value,
491        _len: ir::Value,
492    ) -> WasmResult<()> {
493        Ok(())
494    }
495
496    fn translate_elem_drop(&mut self, _pos: FuncCursor, _seg_index: u32) -> WasmResult<()> {
497        Ok(())
498    }
499
500    fn translate_ref_func(
501        &mut self,
502        mut pos: FuncCursor,
503        _func_index: u32,
504    ) -> WasmResult<ir::Value> {
505        Ok(pos.ins().null(self.reference_type()))
506    }
507
508    fn translate_custom_global_get(
509        &mut self,
510        mut pos: FuncCursor,
511        _global_index: GlobalIndex,
512    ) -> WasmResult<ir::Value> {
513        Ok(pos.ins().iconst(I32, -1))
514    }
515
516    fn translate_custom_global_set(
517        &mut self,
518        _pos: FuncCursor,
519        _global_index: GlobalIndex,
520        _val: ir::Value,
521    ) -> WasmResult<()> {
522        Ok(())
523    }
524}
525
526impl TargetEnvironment for DummyEnvironment {
527    fn target_config(&self) -> TargetFrontendConfig {
528        self.info.config
529    }
530}
531
532impl<'data> ModuleEnvironment<'data> for DummyEnvironment {
533    fn declare_signature(&mut self, sig: ir::Signature) -> WasmResult<()> {
534        self.info.signatures.push(sig);
535        Ok(())
536    }
537
538    fn declare_func_import(
539        &mut self,
540        sig_index: SignatureIndex,
541        module: &'data str,
542        field: &'data str,
543    ) -> WasmResult<()> {
544        assert_eq!(
545            self.info.functions.len(),
546            self.info.imported_funcs.len(),
547            "Imported functions must be declared first"
548        );
549        self.info.functions.push(Exportable::new(sig_index));
550        self.info
551            .imported_funcs
552            .push((String::from(module), String::from(field)));
553        Ok(())
554    }
555
556    fn declare_func_type(&mut self, sig_index: SignatureIndex) -> WasmResult<()> {
557        self.info.functions.push(Exportable::new(sig_index));
558        Ok(())
559    }
560
561    fn declare_global(&mut self, global: Global) -> WasmResult<()> {
562        self.info.globals.push(Exportable::new(global));
563        Ok(())
564    }
565
566    fn declare_global_import(
567        &mut self,
568        global: Global,
569        module: &'data str,
570        field: &'data str,
571    ) -> WasmResult<()> {
572        self.info.globals.push(Exportable::new(global));
573        self.info
574            .imported_globals
575            .push((String::from(module), String::from(field)));
576        Ok(())
577    }
578
579    fn declare_table(&mut self, table: Table) -> WasmResult<()> {
580        self.info.tables.push(Exportable::new(table));
581        Ok(())
582    }
583
584    fn declare_table_import(
585        &mut self,
586        table: Table,
587        module: &'data str,
588        field: &'data str,
589    ) -> WasmResult<()> {
590        self.info.tables.push(Exportable::new(table));
591        self.info
592            .imported_tables
593            .push((String::from(module), String::from(field)));
594        Ok(())
595    }
596
597    fn declare_table_elements(
598        &mut self,
599        _table_index: TableIndex,
600        _base: Option<GlobalIndex>,
601        _offset: usize,
602        _elements: Box<[FuncIndex]>,
603    ) -> WasmResult<()> {
604        // We do nothing
605        Ok(())
606    }
607
608    fn declare_passive_element(
609        &mut self,
610        _elem_index: PassiveElemIndex,
611        _segments: Box<[FuncIndex]>,
612    ) -> WasmResult<()> {
613        Ok(())
614    }
615
616    fn declare_passive_data(
617        &mut self,
618        _elem_index: PassiveDataIndex,
619        _segments: &'data [u8],
620    ) -> WasmResult<()> {
621        Ok(())
622    }
623
624    fn declare_memory(&mut self, memory: Memory) -> WasmResult<()> {
625        self.info.memories.push(Exportable::new(memory));
626        Ok(())
627    }
628
629    fn declare_memory_import(
630        &mut self,
631        memory: Memory,
632        module: &'data str,
633        field: &'data str,
634    ) -> WasmResult<()> {
635        self.info.memories.push(Exportable::new(memory));
636        self.info
637            .imported_memories
638            .push((String::from(module), String::from(field)));
639        Ok(())
640    }
641
642    fn declare_data_initialization(
643        &mut self,
644        _memory_index: MemoryIndex,
645        _base: Option<GlobalIndex>,
646        _offset: usize,
647        _data: &'data [u8],
648    ) -> WasmResult<()> {
649        // We do nothing
650        Ok(())
651    }
652
653    fn declare_func_export(&mut self, func_index: FuncIndex, name: &'data str) -> WasmResult<()> {
654        self.info.functions[func_index]
655            .export_names
656            .push(String::from(name));
657        Ok(())
658    }
659
660    fn declare_table_export(
661        &mut self,
662        table_index: TableIndex,
663        name: &'data str,
664    ) -> WasmResult<()> {
665        self.info.tables[table_index]
666            .export_names
667            .push(String::from(name));
668        Ok(())
669    }
670
671    fn declare_memory_export(
672        &mut self,
673        memory_index: MemoryIndex,
674        name: &'data str,
675    ) -> WasmResult<()> {
676        self.info.memories[memory_index]
677            .export_names
678            .push(String::from(name));
679        Ok(())
680    }
681
682    fn declare_global_export(
683        &mut self,
684        global_index: GlobalIndex,
685        name: &'data str,
686    ) -> WasmResult<()> {
687        self.info.globals[global_index]
688            .export_names
689            .push(String::from(name));
690        Ok(())
691    }
692
693    fn declare_start_func(&mut self, func_index: FuncIndex) -> WasmResult<()> {
694        debug_assert!(self.info.start_func.is_none());
695        self.info.start_func = Some(func_index);
696        Ok(())
697    }
698
699    fn define_function_body(
700        &mut self,
701        module_translation_state: &ModuleTranslationState,
702        body_bytes: &'data [u8],
703        body_offset: usize,
704    ) -> WasmResult<()> {
705        let func = {
706            let mut func_environ = DummyFuncEnvironment::new(&self.info, self.return_mode);
707            let func_index =
708                FuncIndex::new(self.get_num_func_imports() + self.info.function_bodies.len());
709            let name = get_func_name(func_index);
710            let sig = func_environ.vmctx_sig(self.get_func_type(func_index));
711            let mut func = ir::Function::with_name_signature(name, sig);
712            if self.debug_info {
713                func.collect_debug_info();
714            }
715            self.trans.translate(
716                module_translation_state,
717                body_bytes,
718                body_offset,
719                &mut func,
720                &mut func_environ,
721            )?;
722            func
723        };
724        self.func_bytecode_sizes.push(body_bytes.len());
725        self.info.function_bodies.push(func);
726        Ok(())
727    }
728
729    fn declare_func_name(&mut self, func_index: FuncIndex, name: &'data str) -> WasmResult<()> {
730        self.function_names[func_index] = String::from(name);
731        Ok(())
732    }
733}