wasmer_compiler_cranelift/translator/func_state.rs
1// This file contains code from external sources.
2// Attributions: https://github.com/wasmerio/wasmer/blob/main/docs/ATTRIBUTIONS.md
3
4//! WebAssembly module and function translation state.
5//!
6//! The `ModuleTranslationState` struct defined in this module is used to keep track of data about
7//! the whole WebAssembly module, such as the decoded type signatures.
8//!
9//! The `FuncTranslationState` struct defined in this module is used to keep track of the WebAssembly
10//! value and control stacks during the translation of a single function.
11
12use super::func_environ::{FuncEnvironment, GlobalVariable};
13use crate::heap::Heap;
14use crate::{HashMap, Occupied, Vacant};
15use cranelift_codegen::ir::{self, Block, Inst, Value};
16use std::vec::Vec;
17use wasmer_types::{FunctionIndex, GlobalIndex, MemoryIndex, SignatureIndex, WasmResult};
18
19/// Information about the presence of an associated `else` for an `if`, or the
20/// lack thereof.
21#[derive(Debug)]
22pub enum ElseData {
23 /// The `if` does not already have an `else` block.
24 ///
25 /// This doesn't mean that it will never have an `else`, just that we
26 /// haven't seen it yet.
27 NoElse {
28 /// If we discover that we need an `else` block, this is the jump
29 /// instruction that needs to be fixed up to point to the new `else`
30 /// block rather than the destination block after the `if...end`.
31 branch_inst: Inst,
32
33 /// The placeholder block we're replacing.
34 placeholder: Block,
35 },
36
37 /// We have already allocated an `else` block.
38 ///
39 /// Usually we don't know whether we will hit an `if .. end` or an `if
40 /// .. else .. end`, but sometimes we can tell based on the block's type
41 /// signature that the signature is not valid if there isn't an `else`. In
42 /// these cases, we pre-allocate the `else` block.
43 WithElse {
44 /// This is the `else` block.
45 else_block: Block,
46 },
47}
48
49/// A control stack frame can be an `if`, a `block` or a `loop`, each one having the following
50/// fields:
51///
52/// - `destination`: reference to the `Block` that will hold the code after the control block;
53/// - `num_return_values`: number of values returned by the control block;
54/// - `original_stack_size`: size of the value stack at the beginning of the control block.
55///
56/// Moreover, the `if` frame has the `branch_inst` field that points to the `brz` instruction
57/// separating the `true` and `false` branch. The `loop` frame has a `header` field that references
58/// the `Block` that contains the beginning of the body of the loop.
59#[derive(Debug)]
60pub enum ControlStackFrame {
61 If {
62 destination: Block,
63 else_data: ElseData,
64 num_param_values: usize,
65 num_return_values: usize,
66 original_stack_size: usize,
67 exit_is_branched_to: bool,
68 blocktype: wasmer_compiler::wasmparser::BlockType,
69 /// Was the head of the `if` reachable?
70 head_is_reachable: bool,
71 /// What was the reachability at the end of the consequent?
72 ///
73 /// This is `None` until we're finished translating the consequent, and
74 /// is set to `Some` either by hitting an `else` when we will begin
75 /// translating the alternative, or by hitting an `end` in which case
76 /// there is no alternative.
77 consequent_ends_reachable: Option<bool>,
78 // Note: no need for `alternative_ends_reachable` because that is just
79 // `state.reachable` when we hit the `end` in the `if .. else .. end`.
80 },
81 Block {
82 destination: Block,
83 num_param_values: usize,
84 num_return_values: usize,
85 original_stack_size: usize,
86 exit_is_branched_to: bool,
87 },
88 Loop {
89 destination: Block,
90 header: Block,
91 num_param_values: usize,
92 num_return_values: usize,
93 original_stack_size: usize,
94 },
95}
96
97/// Helper methods for the control stack objects.
98impl ControlStackFrame {
99 pub fn num_return_values(&self) -> usize {
100 match *self {
101 Self::If {
102 num_return_values, ..
103 }
104 | Self::Block {
105 num_return_values, ..
106 }
107 | Self::Loop {
108 num_return_values, ..
109 } => num_return_values,
110 }
111 }
112 pub fn num_param_values(&self) -> usize {
113 match *self {
114 Self::If {
115 num_param_values, ..
116 }
117 | Self::Block {
118 num_param_values, ..
119 }
120 | Self::Loop {
121 num_param_values, ..
122 } => num_param_values,
123 }
124 }
125 pub fn following_code(&self) -> Block {
126 match *self {
127 Self::If { destination, .. }
128 | Self::Block { destination, .. }
129 | Self::Loop { destination, .. } => destination,
130 }
131 }
132 pub fn br_destination(&self) -> Block {
133 match *self {
134 Self::If { destination, .. } | Self::Block { destination, .. } => destination,
135 Self::Loop { header, .. } => header,
136 }
137 }
138 /// Private helper. Use `truncate_value_stack_to_else_params()` or
139 /// `truncate_value_stack_to_original_size()` to restore value-stack state.
140 fn original_stack_size(&self) -> usize {
141 match *self {
142 Self::If {
143 original_stack_size,
144 ..
145 }
146 | Self::Block {
147 original_stack_size,
148 ..
149 }
150 | Self::Loop {
151 original_stack_size,
152 ..
153 } => original_stack_size,
154 }
155 }
156 pub fn is_loop(&self) -> bool {
157 match *self {
158 Self::If { .. } | Self::Block { .. } => false,
159 Self::Loop { .. } => true,
160 }
161 }
162
163 pub fn exit_is_branched_to(&self) -> bool {
164 match *self {
165 Self::If {
166 exit_is_branched_to,
167 ..
168 }
169 | Self::Block {
170 exit_is_branched_to,
171 ..
172 } => exit_is_branched_to,
173 Self::Loop { .. } => false,
174 }
175 }
176
177 pub fn set_branched_to_exit(&mut self) {
178 match *self {
179 Self::If {
180 ref mut exit_is_branched_to,
181 ..
182 }
183 | Self::Block {
184 ref mut exit_is_branched_to,
185 ..
186 } => *exit_is_branched_to = true,
187 Self::Loop { .. } => {}
188 }
189 }
190
191 /// Pop values from the value stack so that it is left at the
192 /// input-parameters to an else-block.
193 pub fn truncate_value_stack_to_else_params(&self, stack: &mut Vec<Value>) {
194 debug_assert!(matches!(self, &Self::If { .. }));
195 stack.truncate(self.original_stack_size());
196 }
197
198 /// Pop values from the value stack so that it is left at the state it was
199 /// before this control-flow frame.
200 pub fn truncate_value_stack_to_original_size(&self, stack: &mut Vec<Value>) {
201 // The "If" frame pushes its parameters twice, so they're available to the else block
202 // (see also `FuncTranslationState::push_if`).
203 // Yet, the original_stack_size member accounts for them only once, so that the else
204 // block can see the same number of parameters as the consequent block. As a matter of
205 // fact, we need to substract an extra number of parameter values for if blocks.
206 let num_duplicated_params = match self {
207 &Self::If {
208 num_param_values, ..
209 } => {
210 debug_assert!(num_param_values <= self.original_stack_size());
211 num_param_values
212 }
213 _ => 0,
214 };
215 stack.truncate(self.original_stack_size() - num_duplicated_params);
216 }
217}
218
219/// Contains information passed along during a function's translation and that records:
220///
221/// - The current value and control stacks.
222/// - The depth of the two unreachable control blocks stacks, that are manipulated when translating
223/// unreachable code;
224pub struct FuncTranslationState {
225 /// A stack of values corresponding to the active values in the input wasm function at this
226 /// point.
227 pub(crate) stack: Vec<Value>,
228 /// A stack of active control flow operations at this point in the input wasm function.
229 pub(crate) control_stack: Vec<ControlStackFrame>,
230 /// Is the current translation state still reachable? This is false when translating operators
231 /// like End, Return, or Unreachable.
232 pub(crate) reachable: bool,
233
234 // Map of global variables that have already been created by `FuncEnvironment::make_global`.
235 globals: HashMap<GlobalIndex, GlobalVariable>,
236
237 // Map of heaps that have been created by `FuncEnvironment::make_heap`.
238 heaps: HashMap<MemoryIndex, Heap>,
239
240 // Map of indirect call signatures that have been created by
241 // `FuncEnvironment::make_indirect_sig()`.
242 // Stores both the signature reference and the number of WebAssembly arguments
243 signatures: HashMap<SignatureIndex, (ir::SigRef, usize)>,
244
245 // Imported and local functions that have been created by
246 // `FuncEnvironment::make_direct_func()`.
247 // Stores both the function reference and the number of WebAssembly arguments
248 functions: HashMap<FunctionIndex, (ir::FuncRef, usize)>,
249}
250
251// Public methods that are exposed to non-`cranelift_wasm` API consumers.
252impl FuncTranslationState {
253 /// True if the current translation state expresses reachable code, false if it is unreachable.
254 #[inline]
255 #[allow(dead_code)]
256 pub fn reachable(&self) -> bool {
257 self.reachable
258 }
259}
260
261impl FuncTranslationState {
262 /// Construct a new, empty, `FuncTranslationState`
263 pub(crate) fn new() -> Self {
264 Self {
265 stack: Vec::new(),
266 // TODO(reftypes):
267 //metadata_stack: Vec::new(),
268 control_stack: Vec::new(),
269 reachable: true,
270 globals: HashMap::new(),
271 heaps: HashMap::new(),
272 signatures: HashMap::new(),
273 functions: HashMap::new(),
274 }
275 }
276
277 fn clear(&mut self) {
278 debug_assert!(self.stack.is_empty());
279 debug_assert!(self.control_stack.is_empty());
280 self.reachable = true;
281 self.globals.clear();
282 self.heaps.clear();
283 self.signatures.clear();
284 self.functions.clear();
285 }
286
287 /// Initialize the state for compiling a function with the given signature.
288 ///
289 /// This resets the state to containing only a single block representing the whole function.
290 /// The exit block is the last block in the function which will contain the return instruction.
291 pub(crate) fn initialize(&mut self, sig: &ir::Signature, exit_block: Block) {
292 self.clear();
293 self.push_block(
294 exit_block,
295 0,
296 sig.returns
297 .iter()
298 .filter(|arg| arg.purpose == ir::ArgumentPurpose::Normal)
299 .count(),
300 );
301 }
302
303 /// Push a value.
304 pub(crate) fn push1(&mut self, val: Value) {
305 self.stack.push(val);
306 }
307
308 /// Push multiple values.
309 pub(crate) fn pushn(&mut self, vals: &[Value]) {
310 self.stack.extend_from_slice(vals);
311 }
312
313 /// Pop one value.
314 pub(crate) fn pop1(&mut self) -> Value {
315 self.stack
316 .pop()
317 .expect("attempted to pop a value from an empty stack")
318 }
319
320 /// Peek at the top of the stack without popping it.
321 pub(crate) fn peek1(&self) -> Value {
322 *self
323 .stack
324 .last()
325 .expect("attempted to peek at a value on an empty stack")
326 }
327
328 /// Pop two values. Return them in the order they were pushed.
329 pub(crate) fn pop2(&mut self) -> (Value, Value) {
330 let v2 = self.pop1();
331 let v1 = self.pop1();
332 (v1, v2)
333 }
334
335 /// Pop three values. Return them in the order they were pushed.
336 pub(crate) fn pop3(&mut self) -> (Value, Value, Value) {
337 let v3 = self.pop1();
338 let v2 = self.pop1();
339 let v1 = self.pop1();
340 (v1, v2, v3)
341 }
342
343 /// Helper to ensure the stack size is at least as big as `n`; note that due to
344 /// `debug_assert` this will not execute in non-optimized builds.
345 #[inline]
346 fn ensure_length_is_at_least(&self, n: usize) {
347 debug_assert!(
348 n <= self.stack.len(),
349 "attempted to access {} values but stack only has {} values",
350 n,
351 self.stack.len()
352 );
353 }
354
355 /// Pop the top `n` values on the stack.
356 ///
357 /// The popped values are not returned. Use `peekn` to look at them before popping.
358 pub(crate) fn popn(&mut self, n: usize) {
359 self.ensure_length_is_at_least(n);
360 let new_len = self.stack.len() - n;
361 self.stack.truncate(new_len);
362 }
363
364 /// Peek at the top `n` values on the stack in the order they were pushed.
365 pub(crate) fn peekn(&self, n: usize) -> &[Value] {
366 self.ensure_length_is_at_least(n);
367 &self.stack[self.stack.len() - n..]
368 }
369
370 /// Peek at the top `n` values on the stack in the order they were pushed.
371 pub(crate) fn peekn_mut(&mut self, n: usize) -> &mut [Value] {
372 self.ensure_length_is_at_least(n);
373 let len = self.stack.len();
374 &mut self.stack[len - n..]
375 }
376
377 /// Push a block on the control stack.
378 pub(crate) fn push_block(
379 &mut self,
380 following_code: Block,
381 num_param_types: usize,
382 num_result_types: usize,
383 ) {
384 debug_assert!(num_param_types <= self.stack.len());
385 self.control_stack.push(ControlStackFrame::Block {
386 destination: following_code,
387 original_stack_size: self.stack.len() - num_param_types,
388 num_param_values: num_param_types,
389 num_return_values: num_result_types,
390 exit_is_branched_to: false,
391 });
392 }
393
394 /// Push a loop on the control stack.
395 pub(crate) fn push_loop(
396 &mut self,
397 header: Block,
398 following_code: Block,
399 num_param_types: usize,
400 num_result_types: usize,
401 ) {
402 debug_assert!(num_param_types <= self.stack.len());
403 self.control_stack.push(ControlStackFrame::Loop {
404 header,
405 destination: following_code,
406 original_stack_size: self.stack.len() - num_param_types,
407 num_param_values: num_param_types,
408 num_return_values: num_result_types,
409 });
410 }
411
412 /// Push an if on the control stack.
413 pub(crate) fn push_if(
414 &mut self,
415 destination: Block,
416 else_data: ElseData,
417 num_param_types: usize,
418 num_result_types: usize,
419 blocktype: wasmer_compiler::wasmparser::BlockType,
420 ) {
421 debug_assert!(num_param_types <= self.stack.len());
422
423 // Push a second copy of our `if`'s parameters on the stack. This lets
424 // us avoid saving them on the side in the `ControlStackFrame` for our
425 // `else` block (if it exists), which would require a second heap
426 // allocation. See also the comment in `translate_operator` for
427 // `Operator::Else`.
428 self.stack.reserve(num_param_types);
429 for i in (self.stack.len() - num_param_types)..self.stack.len() {
430 let val = self.stack[i];
431 self.stack.push(val);
432 }
433
434 self.control_stack.push(ControlStackFrame::If {
435 destination,
436 else_data,
437 original_stack_size: self.stack.len() - num_param_types,
438 num_param_values: num_param_types,
439 num_return_values: num_result_types,
440 exit_is_branched_to: false,
441 head_is_reachable: self.reachable,
442 consequent_ends_reachable: None,
443 blocktype,
444 });
445 }
446}
447
448/// Methods for handling entity references.
449impl FuncTranslationState {
450 /// Get the `GlobalVariable` reference that should be used to access the global variable
451 /// `index`. Create the reference if necessary.
452 /// Also return the WebAssembly type of the global.
453 pub(crate) fn get_global<FE: FuncEnvironment + ?Sized>(
454 &mut self,
455 func: &mut ir::Function,
456 index: u32,
457 environ: &mut FE,
458 ) -> WasmResult<GlobalVariable> {
459 let index = GlobalIndex::from_u32(index);
460 match self.globals.entry(index) {
461 Occupied(entry) => Ok(*entry.get()),
462 Vacant(entry) => Ok(*entry.insert(environ.make_global(func, index)?)),
463 }
464 }
465
466 /// Get the `Heap` reference that should be used to access linear memory `index`.
467 /// Create the reference if necessary.
468 pub(crate) fn get_heap<FE: FuncEnvironment + ?Sized>(
469 &mut self,
470 func: &mut ir::Function,
471 index: u32,
472 environ: &mut FE,
473 ) -> WasmResult<Heap> {
474 let index = MemoryIndex::from_u32(index);
475 match self.heaps.entry(index) {
476 Occupied(entry) => Ok(*entry.get()),
477 Vacant(entry) => Ok(*entry.insert(environ.make_heap(func, index)?)),
478 }
479 }
480
481 /// Get the `SigRef` reference that should be used to make an indirect call with signature
482 /// `index`. Also return the number of WebAssembly arguments in the signature.
483 ///
484 /// Create the signature if necessary.
485 pub(crate) fn get_indirect_sig<FE: FuncEnvironment + ?Sized>(
486 &mut self,
487 func: &mut ir::Function,
488 index: u32,
489 environ: &mut FE,
490 ) -> WasmResult<(ir::SigRef, usize)> {
491 let index = SignatureIndex::from_u32(index);
492 match self.signatures.entry(index) {
493 Occupied(entry) => Ok(*entry.get()),
494 Vacant(entry) => {
495 let sig = environ.make_indirect_sig(func, index)?;
496 Ok(*entry.insert((sig, num_wasm_parameters(environ, &func.dfg.signatures[sig]))))
497 }
498 }
499 }
500
501 /// Get the `FuncRef` reference that should be used to make a direct call to function
502 /// `index`. Also return the number of WebAssembly arguments in the signature.
503 ///
504 /// Create the function reference if necessary.
505 pub(crate) fn get_direct_func<FE: FuncEnvironment + ?Sized>(
506 &mut self,
507 func: &mut ir::Function,
508 index: u32,
509 environ: &mut FE,
510 ) -> WasmResult<(ir::FuncRef, usize)> {
511 let index = FunctionIndex::from_u32(index);
512 match self.functions.entry(index) {
513 Occupied(entry) => Ok(*entry.get()),
514 Vacant(entry) => {
515 let fref = environ.make_direct_func(func, index)?;
516 let sig = func.dfg.ext_funcs[fref].signature;
517 Ok(*entry.insert((
518 fref,
519 num_wasm_parameters(environ, &func.dfg.signatures[sig]),
520 )))
521 }
522 }
523 }
524}
525
526fn num_wasm_parameters<FE: FuncEnvironment + ?Sized>(
527 environ: &FE,
528 signature: &ir::Signature,
529) -> usize {
530 (0..signature.params.len())
531 .filter(|index| environ.is_wasm_parameter(signature, *index))
532 .count()
533}