wasmer_compiler_cranelift/translator/
func_translator.rs

1// This file contains code from external sources.
2// Attributions: https://github.com/wasmerio/wasmer/blob/main/docs/ATTRIBUTIONS.md
3
4//! Standalone WebAssembly to Cranelift IR translator.
5//!
6//! This module defines the `FuncTranslator` type which can translate a single WebAssembly
7//! function to Cranelift IR guided by a `FuncEnvironment` which provides information about the
8//! WebAssembly module and the runtime environment.
9
10use super::code_translator::translate_operator;
11use super::func_state::FuncTranslationState;
12use super::translation_utils::get_vmctx_value_label;
13use crate::func_environ::FuncEnvironment;
14use crate::translator::EXN_REF_TYPE;
15use crate::translator::code_translator::bitcast_wasm_returns;
16use core::convert::TryFrom;
17use cranelift_codegen::entity::EntityRef;
18use cranelift_codegen::ir::{self, Block, InstBuilder, ValueLabel};
19use cranelift_codegen::timing;
20use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
21use wasmer_compiler::wasmparser::RefType;
22use wasmer_compiler::{FunctionBinaryReader, ModuleTranslationState, wptype_to_type};
23use wasmer_compiler::{wasm_unsupported, wasmparser};
24use wasmer_types::{LocalFunctionIndex, WasmResult};
25
26/// WebAssembly to Cranelift IR function translator.
27///
28/// A `FuncTranslator` is used to translate a binary WebAssembly function into Cranelift IR guided
29/// by a `FuncEnvironment` object. A single translator instance can be reused to translate multiple
30/// functions which will reduce heap allocation traffic.
31pub struct FuncTranslator {
32    func_ctx: FunctionBuilderContext,
33    state: FuncTranslationState,
34    allow_unaligned_memory_accesses: bool,
35}
36
37impl wasmer_compiler::FuncTranslator for FuncTranslator {}
38
39impl FuncTranslator {
40    /// Create a new translator.
41    pub fn new(allow_unaligned_memory_accesses: bool) -> Self {
42        Self {
43            func_ctx: FunctionBuilderContext::new(),
44            state: FuncTranslationState::new(),
45            allow_unaligned_memory_accesses,
46        }
47    }
48
49    /// Translate a binary WebAssembly function.
50    ///
51    /// The `code` slice contains the binary WebAssembly *function code* as it appears in the code
52    /// section of a WebAssembly module, not including the initial size of the function code. The
53    /// slice is expected to contain two parts:
54    ///
55    /// - The declaration of *locals*, and
56    /// - The function *body* as an expression.
57    ///
58    /// See [the WebAssembly specification][wasm].
59    ///
60    /// [wasm]: https://webassembly.github.io/spec/core/binary/modules.html#code-section
61    ///
62    /// The Cranelift IR function `func` should be completely empty except for the `func.signature`
63    /// and `func.name` fields. The signature may contain special-purpose arguments which are not
64    /// regarded as WebAssembly local variables. Any signature arguments marked as
65    /// `ArgumentPurpose::Normal` are made accessible as WebAssembly local variables.
66    ///
67    pub fn translate(
68        &mut self,
69        module_translation_state: &ModuleTranslationState,
70        reader: &mut dyn FunctionBinaryReader,
71        func: &mut ir::Function,
72        environ: &mut FuncEnvironment<'_>,
73        local_function_index: LocalFunctionIndex,
74    ) -> WasmResult<()> {
75        environ.push_params_on_stack(local_function_index);
76        self.translate_from_reader(module_translation_state, reader, func, environ)
77    }
78
79    /// Translate a binary WebAssembly function from a `FunctionBinaryReader`.
80    pub fn translate_from_reader(
81        &mut self,
82        module_translation_state: &ModuleTranslationState,
83        reader: &mut dyn FunctionBinaryReader,
84        func: &mut ir::Function,
85        environ: &mut FuncEnvironment<'_>,
86    ) -> WasmResult<()> {
87        let _tt = timing::wasm_translate_function();
88        tracing::trace!(
89            "translate({} bytes, {}{})",
90            reader.bytes_remaining(),
91            func.name,
92            func.signature
93        );
94        debug_assert_eq!(func.dfg.num_blocks(), 0, "Function must be empty");
95        debug_assert_eq!(func.dfg.num_insts(), 0, "Function must be empty");
96
97        // This clears the `FunctionBuilderContext`.
98        let mut builder = FunctionBuilder::new(func, &mut self.func_ctx);
99        builder.set_srcloc(cur_srcloc(reader));
100        let entry_block = builder.create_block();
101        builder.append_block_params_for_function_params(entry_block);
102        builder.switch_to_block(entry_block); // This also creates values for the arguments.
103        builder.seal_block(entry_block); // Declare all predecessors known.
104
105        // Make sure the entry block is inserted in the layout before we make any callbacks to
106        // `environ`. The callback functions may need to insert things in the entry block.
107        builder.ensure_inserted_block();
108
109        let num_params = declare_wasm_parameters(&mut builder, entry_block, environ);
110
111        // Set up the translation state with a single pushed control block representing the whole
112        // function and its return values.
113        let exit_block = builder.create_block();
114        builder.append_block_params_for_function_returns(exit_block);
115        self.state.initialize(&builder.func.signature, exit_block);
116
117        parse_local_decls(reader, &mut builder, num_params, environ)?;
118        parse_function_body(
119            module_translation_state,
120            reader,
121            &mut builder,
122            &mut self.state,
123            environ,
124            self.allow_unaligned_memory_accesses,
125        )?;
126
127        builder.finalize();
128        Ok(())
129    }
130}
131
132/// Declare local variables for the signature parameters that correspond to WebAssembly locals.
133///
134/// Return the number of local variables declared.
135fn declare_wasm_parameters(
136    builder: &mut FunctionBuilder,
137    entry_block: Block,
138    environ: &FuncEnvironment<'_>,
139) -> usize {
140    let sig_len = builder.func.signature.params.len();
141    let mut next_local = 0;
142    for i in 0..sig_len {
143        let param_type = builder.func.signature.params[i];
144        // There may be additional special-purpose parameters in addition to the normal WebAssembly
145        // signature parameters. For example, a `vmctx` pointer.
146        if environ.is_wasm_parameter(&builder.func.signature, i) {
147            // This is a normal WebAssembly signature parameter, so create a local for it.
148            let local = builder.declare_var(param_type.value_type);
149            let local_index = local.index();
150            debug_assert_eq!(local_index, next_local);
151            debug_assert!(u32::try_from(local_index).is_ok());
152            next_local += 1;
153
154            let param_value = builder.block_params(entry_block)[i];
155            builder.def_var(local, param_value);
156        }
157        if param_type.purpose == ir::ArgumentPurpose::VMContext {
158            let param_value = builder.block_params(entry_block)[i];
159            builder.set_val_label(param_value, get_vmctx_value_label());
160        }
161    }
162
163    next_local
164}
165
166/// Parse the local variable declarations that precede the function body.
167///
168/// Declare local variables, starting from `num_params`.
169fn parse_local_decls(
170    reader: &mut dyn FunctionBinaryReader,
171    builder: &mut FunctionBuilder,
172    num_params: usize,
173    environ: &mut FuncEnvironment<'_>,
174) -> WasmResult<()> {
175    let mut next_local = num_params;
176    let local_count = reader.read_local_count()?;
177
178    for _ in 0..local_count {
179        builder.set_srcloc(cur_srcloc(reader));
180        let (count, ty) = reader.read_local_decl()?;
181        declare_locals(builder, count, ty, &mut next_local, environ)?;
182    }
183
184    Ok(())
185}
186
187/// Declare `count` local variables of the same type, starting from `next_local`.
188///
189/// Fail if the type is not valid for a local.
190fn declare_locals(
191    builder: &mut FunctionBuilder,
192    count: u32,
193    wasm_type: wasmparser::ValType,
194    next_local: &mut usize,
195    environ: &mut FuncEnvironment<'_>,
196) -> WasmResult<()> {
197    // All locals are initialized to 0.
198    use wasmparser::ValType::*;
199    let zeroval = match wasm_type {
200        I32 => builder.ins().iconst(ir::types::I32, 0),
201        I64 => builder.ins().iconst(ir::types::I64, 0),
202        F32 => builder.ins().f32const(ir::immediates::Ieee32::with_bits(0)),
203        F64 => builder.ins().f64const(ir::immediates::Ieee64::with_bits(0)),
204        V128 => {
205            let constant_handle = builder.func.dfg.constants.insert([0; 16].to_vec().into());
206            builder.ins().vconst(ir::types::I8X16, constant_handle)
207        }
208        Ref(ty) => {
209            if ty.is_func_ref() || ty.is_extern_ref() {
210                builder.ins().iconst(environ.reference_type(), 0)
211            } else if ty == RefType::EXNREF || ty == RefType::EXN {
212                // no `.is_exnref` yet
213                builder.ins().iconst(EXN_REF_TYPE, 0)
214            } else {
215                return Err(wasm_unsupported!("unsupported reference type: {:?}", ty));
216            }
217        }
218    };
219
220    let wasmer_ty = wptype_to_type(wasm_type).unwrap();
221    let ty = builder.func.dfg.value_type(zeroval);
222    for _ in 0..count {
223        let local = builder.declare_var(ty);
224        let local_index = local.index();
225        debug_assert_eq!(local_index, *next_local);
226        debug_assert!(u32::try_from(local_index).is_ok());
227        builder.def_var(local, zeroval);
228        builder.set_val_label(zeroval, ValueLabel::new(*next_local));
229        environ.push_local_decl_on_stack(wasmer_ty);
230        *next_local += 1;
231    }
232    Ok(())
233}
234
235/// Parse the function body in `reader`.
236///
237/// This assumes that the local variable declarations have already been parsed and function
238/// arguments and locals are declared in the builder.
239fn parse_function_body(
240    module_translation_state: &ModuleTranslationState,
241    reader: &mut dyn FunctionBinaryReader,
242    builder: &mut FunctionBuilder,
243    state: &mut FuncTranslationState,
244    environ: &mut FuncEnvironment<'_>,
245    allow_unaligned_memory_accesses: bool,
246) -> WasmResult<()> {
247    // The control stack is initialized with a single block representing the whole function.
248    debug_assert_eq!(state.control_stack.len(), 1, "State not initialized");
249
250    // Keep going until the final `End` operator which pops the outermost block.
251    while !state.control_stack.is_empty() {
252        builder.set_srcloc(cur_srcloc(reader));
253        let op = reader.read_operator()?;
254        translate_operator(
255            module_translation_state,
256            &op,
257            builder,
258            state,
259            environ,
260            allow_unaligned_memory_accesses,
261        )?;
262    }
263
264    // The final `End` operator left us in the exit block where we need to manually add a return
265    // instruction.
266    //
267    // If the exit block is unreachable, it may not have the correct arguments, so we would
268    // generate a return instruction that doesn't match the signature.
269    if state.reachable {
270        //debug_assert!(builder.is_pristine());
271        if !builder.is_unreachable() {
272            {
273                bitcast_wasm_returns(environ, &mut state.stack, builder);
274                builder.ins().return_(&state.stack)
275            };
276        }
277    }
278
279    // Discard any remaining values on the stack. Either we just returned them,
280    // or the end of the function is unreachable.
281    state.stack.clear();
282    //state.metadata_stack.clear();
283
284    debug_assert!(reader.eof());
285
286    Ok(())
287}
288
289/// Get the current source location from a reader.
290fn cur_srcloc(reader: &dyn FunctionBinaryReader) -> ir::SourceLoc {
291    // We record source locations as byte code offsets relative to the beginning of the file.
292    // This will wrap around if byte code is larger than 4 GB.
293    ir::SourceLoc::new(reader.original_position() as u32)
294}