Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/cranelift/src/translate/func_translator.rs
1692 views
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
7
use crate::func_environ::FuncEnvironment;
8
use crate::translate::TargetEnvironment;
9
use crate::translate::code_translator::{bitcast_wasm_returns, translate_operator};
10
use crate::translate::stack::FuncTranslationStacks;
11
use crate::translate::translation_utils::get_vmctx_value_label;
12
use cranelift_codegen::entity::EntityRef;
13
use cranelift_codegen::ir::{self, Block, InstBuilder, ValueLabel};
14
use cranelift_codegen::timing;
15
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
16
use wasmparser::{BinaryReader, FuncValidator, FunctionBody, OperatorsReader, WasmModuleResources};
17
use 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.
24
pub struct FuncTranslator {
25
func_ctx: FunctionBuilderContext,
26
state: FuncTranslationStacks,
27
}
28
29
impl 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.
103
fn 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`.
139
fn 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.
164
fn 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.
233
fn 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
283
fn 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.
318
fn 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
}
323
324