wasmtime_internal_cranelift/translate/
func_translator.rs

1//! Stand-alone WebAssembly to Cranelift IR translator.
2//!
3//! This module defines the `FuncTranslator` type which can translate a single WebAssembly
4//! function to Cranelift IR guided by a `FuncEnvironment` which provides information about the
5//! WebAssembly module and the runtime environment.
6
7use crate::func_environ::FuncEnvironment;
8use crate::translate::TargetEnvironment;
9use crate::translate::code_translator::{bitcast_wasm_returns, translate_operator};
10use crate::translate::stack::FuncTranslationStacks;
11use crate::translate::translation_utils::get_vmctx_value_label;
12use cranelift_codegen::entity::EntityRef;
13use cranelift_codegen::ir::{self, Block, InstBuilder, ValueLabel};
14use cranelift_codegen::timing;
15use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
16use wasmparser::{BinaryReader, FuncValidator, FunctionBody, OperatorsReader, WasmModuleResources};
17use wasmtime_environ::{TypeConvert, WasmResult};
18
19/// WebAssembly to Cranelift IR function translator.
20///
21/// A `FuncTranslator` is used to translate a binary WebAssembly function into Cranelift IR guided
22/// by a `FuncEnvironment` object. A single translator instance can be reused to translate multiple
23/// functions which will reduce heap allocation traffic.
24pub struct FuncTranslator {
25    func_ctx: FunctionBuilderContext,
26    state: FuncTranslationStacks,
27}
28
29impl FuncTranslator {
30    /// Create a new translator.
31    pub fn new() -> Self {
32        Self {
33            func_ctx: FunctionBuilderContext::new(),
34            state: FuncTranslationStacks::new(),
35        }
36    }
37
38    /// Returns the underlying `FunctionBuilderContext` that this translator
39    /// uses.
40    pub fn context(&mut self) -> &mut FunctionBuilderContext {
41        &mut self.func_ctx
42    }
43
44    /// Translate a binary WebAssembly function from a `FunctionBody`.
45    ///
46    /// See [the WebAssembly specification][wasm].
47    ///
48    /// [wasm]: https://webassembly.github.io/spec/core/binary/modules.html#code-section
49    ///
50    /// The Cranelift IR function `func` should be completely empty except for the `func.signature`
51    /// and `func.name` fields. The signature may contain special-purpose arguments which are not
52    /// regarded as WebAssembly local variables. Any signature arguments marked as
53    /// `ArgumentPurpose::Normal` are made accessible as WebAssembly local variables.
54    pub fn translate_body(
55        &mut self,
56        validator: &mut FuncValidator<impl WasmModuleResources>,
57        body: FunctionBody<'_>,
58        func: &mut ir::Function,
59        environ: &mut FuncEnvironment<'_>,
60    ) -> WasmResult<()> {
61        let _tt = timing::wasm_translate_function();
62        let mut reader = body.get_binary_reader();
63        log::trace!(
64            "translate({} bytes, {}{})",
65            reader.bytes_remaining(),
66            func.name,
67            func.signature
68        );
69        debug_assert_eq!(func.dfg.num_blocks(), 0, "Function must be empty");
70        debug_assert_eq!(func.dfg.num_insts(), 0, "Function must be empty");
71
72        let mut builder = FunctionBuilder::new(func, &mut self.func_ctx);
73        builder.set_srcloc(cur_srcloc(&reader));
74        let entry_block = builder.create_block();
75        builder.append_block_params_for_function_params(entry_block);
76        builder.switch_to_block(entry_block);
77        builder.seal_block(entry_block); // Declare all predecessors known.
78
79        // Make sure the entry block is inserted in the layout before we make any callbacks to
80        // `environ`. The callback functions may need to insert things in the entry block.
81        builder.ensure_inserted_block();
82
83        let num_params = declare_wasm_parameters(&mut builder, entry_block, environ);
84
85        // Set up the translation state with a single pushed control block representing the whole
86        // function and its return values.
87        let exit_block = builder.create_block();
88        builder.append_block_params_for_function_returns(exit_block);
89        self.state.initialize(&builder.func.signature, exit_block);
90
91        parse_local_decls(&mut reader, &mut builder, num_params, environ, validator)?;
92        parse_function_body(validator, reader, &mut builder, &mut self.state, environ)?;
93
94        builder.finalize();
95        log::trace!("translated Wasm to CLIF:\n{}", func.display());
96        Ok(())
97    }
98}
99
100/// Declare local variables for the signature parameters that correspond to WebAssembly locals.
101///
102/// Return the number of local variables declared.
103fn declare_wasm_parameters(
104    builder: &mut FunctionBuilder,
105    entry_block: Block,
106    environ: &FuncEnvironment<'_>,
107) -> usize {
108    let sig_len = builder.func.signature.params.len();
109    let mut next_local = 0;
110    for i in 0..sig_len {
111        let param_type = builder.func.signature.params[i];
112        // There may be additional special-purpose parameters in addition to the normal WebAssembly
113        // signature parameters. For example, a `vmctx` pointer.
114        if environ.is_wasm_parameter(&builder.func.signature, i) {
115            // This is a normal WebAssembly signature parameter, so create a local for it.
116            let local = builder.declare_var(param_type.value_type);
117            debug_assert_eq!(local.index(), next_local);
118            next_local += 1;
119
120            if environ.param_needs_stack_map(&builder.func.signature, i) {
121                builder.declare_var_needs_stack_map(local);
122            }
123
124            let param_value = builder.block_params(entry_block)[i];
125            builder.def_var(local, param_value);
126        }
127        if param_type.purpose == ir::ArgumentPurpose::VMContext {
128            let param_value = builder.block_params(entry_block)[i];
129            builder.set_val_label(param_value, get_vmctx_value_label());
130        }
131    }
132
133    next_local
134}
135
136/// Parse the local variable declarations that precede the function body.
137///
138/// Declare local variables, starting from `num_params`.
139fn parse_local_decls(
140    reader: &mut BinaryReader,
141    builder: &mut FunctionBuilder,
142    num_params: usize,
143    environ: &mut FuncEnvironment<'_>,
144    validator: &mut FuncValidator<impl WasmModuleResources>,
145) -> WasmResult<()> {
146    let mut next_local = num_params;
147    let local_count = reader.read_var_u32()?;
148
149    for _ in 0..local_count {
150        builder.set_srcloc(cur_srcloc(reader));
151        let pos = reader.original_position();
152        let count = reader.read_var_u32()?;
153        let ty = reader.read()?;
154        validator.define_locals(pos, count, ty)?;
155        declare_locals(builder, count, ty, &mut next_local, environ)?;
156    }
157
158    Ok(())
159}
160
161/// Declare `count` local variables of the same type, starting from `next_local`.
162///
163/// Fail if too many locals are declared in the function, or if the type is not valid for a local.
164fn declare_locals(
165    builder: &mut FunctionBuilder,
166    count: u32,
167    wasm_type: wasmparser::ValType,
168    next_local: &mut usize,
169    environ: &mut FuncEnvironment<'_>,
170) -> WasmResult<()> {
171    // All locals are initialized to 0.
172    use wasmparser::ValType::*;
173    let (ty, init, needs_stack_map) = match wasm_type {
174        I32 => (
175            ir::types::I32,
176            Some(builder.ins().iconst(ir::types::I32, 0)),
177            false,
178        ),
179        I64 => (
180            ir::types::I64,
181            Some(builder.ins().iconst(ir::types::I64, 0)),
182            false,
183        ),
184        F32 => (
185            ir::types::F32,
186            Some(builder.ins().f32const(ir::immediates::Ieee32::with_bits(0))),
187            false,
188        ),
189        F64 => (
190            ir::types::F64,
191            Some(builder.ins().f64const(ir::immediates::Ieee64::with_bits(0))),
192            false,
193        ),
194        V128 => {
195            let constant_handle = builder.func.dfg.constants.insert([0; 16].to_vec().into());
196            (
197                ir::types::I8X16,
198                Some(builder.ins().vconst(ir::types::I8X16, constant_handle)),
199                false,
200            )
201        }
202        Ref(rt) => {
203            let hty = environ.convert_heap_type(rt.heap_type())?;
204            let (ty, needs_stack_map) = environ.reference_type(hty);
205            let init = if rt.is_nullable() {
206                Some(environ.translate_ref_null(builder.cursor(), hty)?)
207            } else {
208                None
209            };
210            (ty, init, needs_stack_map)
211        }
212    };
213
214    for _ in 0..count {
215        let local = builder.declare_var(ty);
216        debug_assert_eq!(local.index(), *next_local);
217        if needs_stack_map {
218            builder.declare_var_needs_stack_map(local);
219        }
220        if let Some(init) = init {
221            builder.def_var(local, init);
222            builder.set_val_label(init, ValueLabel::new(*next_local));
223        }
224        *next_local += 1;
225    }
226    Ok(())
227}
228
229/// Parse the function body in `reader`.
230///
231/// This assumes that the local variable declarations have already been parsed and function
232/// arguments and locals are declared in the builder.
233fn parse_function_body(
234    validator: &mut FuncValidator<impl WasmModuleResources>,
235    reader: BinaryReader,
236    builder: &mut FunctionBuilder,
237    stack: &mut FuncTranslationStacks,
238    environ: &mut FuncEnvironment<'_>,
239) -> WasmResult<()> {
240    // The control stack is initialized with a single block representing the whole function.
241    debug_assert_eq!(stack.control_stack.len(), 1, "State not initialized");
242
243    environ.before_translate_function(builder, stack)?;
244
245    let mut reader = OperatorsReader::new(reader);
246    let mut operand_types = vec![];
247
248    while !reader.eof() {
249        let pos = reader.original_position();
250        builder.set_srcloc(cur_srcloc(&reader.get_binary_reader()));
251
252        let op = reader.read()?;
253        let operand_types =
254            validate_op_and_get_operand_types(validator, environ, &mut operand_types, &op, pos)?;
255
256        environ.before_translate_operator(&op, operand_types, builder, stack)?;
257        translate_operator(validator, &op, operand_types, builder, stack, environ)?;
258        environ.after_translate_operator(&op, operand_types, builder, stack)?;
259    }
260    environ.after_translate_function(builder, stack)?;
261    reader.finish()?;
262
263    // The final `End` operator left us in the exit block where we need to manually add a return
264    // instruction.
265    //
266    // If the exit block is unreachable, it may not have the correct arguments, so we would
267    // generate a return instruction that doesn't match the signature.
268    if stack.reachable {
269        if !builder.is_unreachable() {
270            environ.handle_before_return(&stack.stack, builder);
271            bitcast_wasm_returns(&mut stack.stack, builder);
272            builder.ins().return_(&stack.stack);
273        }
274    }
275
276    // Discard any remaining values on the stack. Either we just returned them,
277    // or the end of the function is unreachable.
278    stack.stack.clear();
279
280    Ok(())
281}
282
283fn validate_op_and_get_operand_types<'a>(
284    validator: &mut FuncValidator<impl WasmModuleResources>,
285    environ: &mut FuncEnvironment<'_>,
286    operand_types: &'a mut Vec<wasmtime_environ::WasmValType>,
287    op: &wasmparser::Operator<'_>,
288    pos: usize,
289) -> WasmResult<Option<&'a [wasmtime_environ::WasmValType]>> {
290    // Get the operand types for this operator.
291    //
292    // Note that we don't know if the `op` is valid yet, but only valid ops will
293    // definitely have arity. However, we also must check the arity before
294    // validating the op so that the validator has the right state to correctly
295    // report the arity. Furthermore, even if the op is valid, if it is in
296    // unreachable code, the op might want to pop more values from the stack
297    // than actually exist on the stack (which is allowed in unreachable code)
298    // so even if we can get arity, we are only guaranteed to have operand types
299    // for ops that are not only valid but also reachable.
300    let arity = op.operator_arity(&*validator);
301    operand_types.clear();
302    let operand_types = arity.and_then(|(operand_arity, _result_arity)| {
303        for i in (0..operand_arity).rev() {
304            let i = usize::try_from(i).unwrap();
305            let ty = validator.get_operand_type(i)??;
306            let ty = environ.convert_valtype(ty).ok()?;
307            operand_types.push(ty);
308        }
309        Some(&operand_types[..])
310    });
311
312    validator.op(pos, &op)?;
313
314    Ok(operand_types)
315}
316
317/// Get the current source location from a reader.
318fn cur_srcloc(reader: &BinaryReader) -> ir::SourceLoc {
319    // We record source locations as byte code offsets relative to the beginning of the file.
320    // This will panic if bytecode is larger than 4 GB.
321    ir::SourceLoc::new(reader.original_position().try_into().unwrap())
322}