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