Skip to main content

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