1 //! WebAssembly function translation state.
2 //!
3 //! The `TranslationState` struct defined in this module is used to keep track of the WebAssembly
4 //! value and control stacks during the translation of a single function.
5
6 use super::{HashMap, Occupied, Vacant};
7 use crate::environ::{FuncEnvironment, GlobalVariable, WasmResult};
8 use crate::translation_utils::{FuncIndex, GlobalIndex, MemoryIndex, SignatureIndex, TableIndex};
9 use cranelift_codegen::ir::{self, Ebb, Inst, Value};
10 use std::vec::Vec;
11
12 /// A control stack frame can be an `if`, a `block` or a `loop`, each one having the following
13 /// fields:
14 ///
15 /// - `destination`: reference to the `Ebb` that will hold the code after the control block;
16 /// - `num_return_values`: number of values returned by the control block;
17 /// - `original_stack_size`: size of the value stack at the beginning of the control block.
18 ///
19 /// Moreover, the `if` frame has the `branch_inst` field that points to the `brz` instruction
20 /// separating the `true` and `false` branch. The `loop` frame has a `header` field that references
21 /// the `Ebb` that contains the beginning of the body of the loop.
22 #[derive(Debug)]
23 pub enum ControlStackFrame {
24 If {
25 destination: Ebb,
26 branch_inst: Inst,
27 num_return_values: usize,
28 original_stack_size: usize,
29 exit_is_branched_to: bool,
30 reachable_from_top: bool,
31 },
32 Block {
33 destination: Ebb,
34 num_return_values: usize,
35 original_stack_size: usize,
36 exit_is_branched_to: bool,
37 },
38 Loop {
39 destination: Ebb,
40 header: Ebb,
41 num_return_values: usize,
42 original_stack_size: usize,
43 },
44 }
45
46 /// Helper methods for the control stack objects.
47 impl ControlStackFrame {
num_return_values(&self) -> usize48 pub fn num_return_values(&self) -> usize {
49 match *self {
50 ControlStackFrame::If {
51 num_return_values, ..
52 }
53 | ControlStackFrame::Block {
54 num_return_values, ..
55 }
56 | ControlStackFrame::Loop {
57 num_return_values, ..
58 } => num_return_values,
59 }
60 }
following_code(&self) -> Ebb61 pub fn following_code(&self) -> Ebb {
62 match *self {
63 ControlStackFrame::If { destination, .. }
64 | ControlStackFrame::Block { destination, .. }
65 | ControlStackFrame::Loop { destination, .. } => destination,
66 }
67 }
br_destination(&self) -> Ebb68 pub fn br_destination(&self) -> Ebb {
69 match *self {
70 ControlStackFrame::If { destination, .. }
71 | ControlStackFrame::Block { destination, .. } => destination,
72 ControlStackFrame::Loop { header, .. } => header,
73 }
74 }
original_stack_size(&self) -> usize75 pub fn original_stack_size(&self) -> usize {
76 match *self {
77 ControlStackFrame::If {
78 original_stack_size,
79 ..
80 }
81 | ControlStackFrame::Block {
82 original_stack_size,
83 ..
84 }
85 | ControlStackFrame::Loop {
86 original_stack_size,
87 ..
88 } => original_stack_size,
89 }
90 }
is_loop(&self) -> bool91 pub fn is_loop(&self) -> bool {
92 match *self {
93 ControlStackFrame::If { .. } | ControlStackFrame::Block { .. } => false,
94 ControlStackFrame::Loop { .. } => true,
95 }
96 }
97
exit_is_branched_to(&self) -> bool98 pub fn exit_is_branched_to(&self) -> bool {
99 match *self {
100 ControlStackFrame::If {
101 exit_is_branched_to,
102 ..
103 }
104 | ControlStackFrame::Block {
105 exit_is_branched_to,
106 ..
107 } => exit_is_branched_to,
108 ControlStackFrame::Loop { .. } => false,
109 }
110 }
111
set_branched_to_exit(&mut self)112 pub fn set_branched_to_exit(&mut self) {
113 match *self {
114 ControlStackFrame::If {
115 ref mut exit_is_branched_to,
116 ..
117 }
118 | ControlStackFrame::Block {
119 ref mut exit_is_branched_to,
120 ..
121 } => *exit_is_branched_to = true,
122 ControlStackFrame::Loop { .. } => {}
123 }
124 }
125 }
126
127 /// VisibleTranslationState wraps a TranslationState with an interface appropriate for users
128 /// outside this `cranelift-wasm`.
129 ///
130 /// VisibleTranslationState is currently very minimal (only exposing reachability information), but
131 /// is anticipated to grow in the future, with functions to inspect or modify the wasm operand
132 /// stack for example.
133 pub struct VisibleTranslationState<'a> {
134 state: &'a TranslationState,
135 }
136
137 impl<'a> VisibleTranslationState<'a> {
138 /// Build a VisibleTranslationState from an existing TranslationState
new(state: &'a TranslationState) -> Self139 pub fn new(state: &'a TranslationState) -> Self {
140 VisibleTranslationState { state }
141 }
142
143 /// True if the current translation state expresses reachable code, false if it is unreachable
reachable(&self) -> bool144 pub fn reachable(&self) -> bool {
145 self.state.reachable
146 }
147 }
148
149 /// Contains information passed along during the translation and that records:
150 ///
151 /// - The current value and control stacks.
152 /// - The depth of the two unreachable control blocks stacks, that are manipulated when translating
153 /// unreachable code;
154 pub struct TranslationState {
155 /// A stack of values corresponding to the active values in the input wasm function at this
156 /// point.
157 pub stack: Vec<Value>,
158 /// A stack of active control flow operations at this point in the input wasm function.
159 pub control_stack: Vec<ControlStackFrame>,
160 /// Is the current translation state still reachable? This is false when translating operators
161 /// like End, Return, or Unreachable.
162 pub reachable: bool,
163
164 // Map of global variables that have already been created by `FuncEnvironment::make_global`.
165 globals: HashMap<GlobalIndex, GlobalVariable>,
166
167 // Map of heaps that have been created by `FuncEnvironment::make_heap`.
168 heaps: HashMap<MemoryIndex, ir::Heap>,
169
170 // Map of tables that have been created by `FuncEnvironment::make_table`.
171 tables: HashMap<TableIndex, ir::Table>,
172
173 // Map of indirect call signatures that have been created by
174 // `FuncEnvironment::make_indirect_sig()`.
175 // Stores both the signature reference and the number of WebAssembly arguments
176 signatures: HashMap<SignatureIndex, (ir::SigRef, usize)>,
177
178 // Imported and local functions that have been created by
179 // `FuncEnvironment::make_direct_func()`.
180 // Stores both the function reference and the number of WebAssembly arguments
181 functions: HashMap<FuncIndex, (ir::FuncRef, usize)>,
182 }
183
184 impl TranslationState {
185 /// Construct a new, empty, `TranslationState`
new() -> Self186 pub fn new() -> Self {
187 Self {
188 stack: Vec::new(),
189 control_stack: Vec::new(),
190 reachable: true,
191 globals: HashMap::new(),
192 heaps: HashMap::new(),
193 tables: HashMap::new(),
194 signatures: HashMap::new(),
195 functions: HashMap::new(),
196 }
197 }
198
clear(&mut self)199 fn clear(&mut self) {
200 debug_assert!(self.stack.is_empty());
201 debug_assert!(self.control_stack.is_empty());
202 self.reachable = true;
203 self.globals.clear();
204 self.heaps.clear();
205 self.tables.clear();
206 self.signatures.clear();
207 self.functions.clear();
208 }
209
210 /// Initialize the state for compiling a function with the given signature.
211 ///
212 /// This resets the state to containing only a single block representing the whole function.
213 /// The exit block is the last block in the function which will contain the return instruction.
initialize(&mut self, sig: &ir::Signature, exit_block: Ebb)214 pub fn initialize(&mut self, sig: &ir::Signature, exit_block: Ebb) {
215 self.clear();
216 self.push_block(
217 exit_block,
218 sig.returns
219 .iter()
220 .filter(|arg| arg.purpose == ir::ArgumentPurpose::Normal)
221 .count(),
222 );
223 }
224
225 /// Push a value.
push1(&mut self, val: Value)226 pub fn push1(&mut self, val: Value) {
227 self.stack.push(val);
228 }
229
230 /// Push multiple values.
pushn(&mut self, vals: &[Value])231 pub fn pushn(&mut self, vals: &[Value]) {
232 self.stack.extend_from_slice(vals);
233 }
234
235 /// Pop one value.
pop1(&mut self) -> Value236 pub fn pop1(&mut self) -> Value {
237 self.stack.pop().unwrap()
238 }
239
240 /// Peek at the top of the stack without popping it.
peek1(&self) -> Value241 pub fn peek1(&self) -> Value {
242 *self.stack.last().unwrap()
243 }
244
245 /// Pop two values. Return them in the order they were pushed.
pop2(&mut self) -> (Value, Value)246 pub fn pop2(&mut self) -> (Value, Value) {
247 let v2 = self.stack.pop().unwrap();
248 let v1 = self.stack.pop().unwrap();
249 (v1, v2)
250 }
251
252 /// Pop three values. Return them in the order they were pushed.
pop3(&mut self) -> (Value, Value, Value)253 pub fn pop3(&mut self) -> (Value, Value, Value) {
254 let v3 = self.stack.pop().unwrap();
255 let v2 = self.stack.pop().unwrap();
256 let v1 = self.stack.pop().unwrap();
257 (v1, v2, v3)
258 }
259
260 /// Pop the top `n` values on the stack.
261 ///
262 /// The popped values are not returned. Use `peekn` to look at them before popping.
popn(&mut self, n: usize)263 pub fn popn(&mut self, n: usize) {
264 let new_len = self.stack.len() - n;
265 self.stack.truncate(new_len);
266 }
267
268 /// Peek at the top `n` values on the stack in the order they were pushed.
peekn(&self, n: usize) -> &[Value]269 pub fn peekn(&self, n: usize) -> &[Value] {
270 &self.stack[self.stack.len() - n..]
271 }
272
273 /// Push a block on the control stack.
push_block(&mut self, following_code: Ebb, num_result_types: usize)274 pub fn push_block(&mut self, following_code: Ebb, num_result_types: usize) {
275 self.control_stack.push(ControlStackFrame::Block {
276 destination: following_code,
277 original_stack_size: self.stack.len(),
278 num_return_values: num_result_types,
279 exit_is_branched_to: false,
280 });
281 }
282
283 /// Push a loop on the control stack.
push_loop(&mut self, header: Ebb, following_code: Ebb, num_result_types: usize)284 pub fn push_loop(&mut self, header: Ebb, following_code: Ebb, num_result_types: usize) {
285 self.control_stack.push(ControlStackFrame::Loop {
286 header,
287 destination: following_code,
288 original_stack_size: self.stack.len(),
289 num_return_values: num_result_types,
290 });
291 }
292
293 /// Push an if on the control stack.
push_if(&mut self, branch_inst: Inst, following_code: Ebb, num_result_types: usize)294 pub fn push_if(&mut self, branch_inst: Inst, following_code: Ebb, num_result_types: usize) {
295 self.control_stack.push(ControlStackFrame::If {
296 branch_inst,
297 destination: following_code,
298 original_stack_size: self.stack.len(),
299 num_return_values: num_result_types,
300 exit_is_branched_to: false,
301 reachable_from_top: self.reachable,
302 });
303 }
304 }
305
306 /// Methods for handling entity references.
307 impl TranslationState {
308 /// Get the `GlobalVariable` reference that should be used to access the global variable
309 /// `index`. Create the reference if necessary.
310 /// Also return the WebAssembly type of the global.
get_global<FE: FuncEnvironment + ?Sized>( &mut self, func: &mut ir::Function, index: u32, environ: &mut FE, ) -> WasmResult<GlobalVariable>311 pub fn get_global<FE: FuncEnvironment + ?Sized>(
312 &mut self,
313 func: &mut ir::Function,
314 index: u32,
315 environ: &mut FE,
316 ) -> WasmResult<GlobalVariable> {
317 let index = GlobalIndex::from_u32(index);
318 match self.globals.entry(index) {
319 Occupied(entry) => Ok(*entry.get()),
320 Vacant(entry) => Ok(*entry.insert(environ.make_global(func, index)?)),
321 }
322 }
323
324 /// Get the `Heap` reference that should be used to access linear memory `index`.
325 /// Create the reference if necessary.
get_heap<FE: FuncEnvironment + ?Sized>( &mut self, func: &mut ir::Function, index: u32, environ: &mut FE, ) -> WasmResult<ir::Heap>326 pub fn get_heap<FE: FuncEnvironment + ?Sized>(
327 &mut self,
328 func: &mut ir::Function,
329 index: u32,
330 environ: &mut FE,
331 ) -> WasmResult<ir::Heap> {
332 let index = MemoryIndex::from_u32(index);
333 match self.heaps.entry(index) {
334 Occupied(entry) => Ok(*entry.get()),
335 Vacant(entry) => Ok(*entry.insert(environ.make_heap(func, index)?)),
336 }
337 }
338
339 /// Get the `Table` reference that should be used to access table `index`.
340 /// Create the reference if necessary.
get_table<FE: FuncEnvironment + ?Sized>( &mut self, func: &mut ir::Function, index: u32, environ: &mut FE, ) -> WasmResult<ir::Table>341 pub fn get_table<FE: FuncEnvironment + ?Sized>(
342 &mut self,
343 func: &mut ir::Function,
344 index: u32,
345 environ: &mut FE,
346 ) -> WasmResult<ir::Table> {
347 let index = TableIndex::from_u32(index);
348 match self.tables.entry(index) {
349 Occupied(entry) => Ok(*entry.get()),
350 Vacant(entry) => Ok(*entry.insert(environ.make_table(func, index)?)),
351 }
352 }
353
354 /// Get the `SigRef` reference that should be used to make an indirect call with signature
355 /// `index`. Also return the number of WebAssembly arguments in the signature.
356 ///
357 /// Create the signature if necessary.
get_indirect_sig<FE: FuncEnvironment + ?Sized>( &mut self, func: &mut ir::Function, index: u32, environ: &mut FE, ) -> WasmResult<(ir::SigRef, usize)>358 pub fn get_indirect_sig<FE: FuncEnvironment + ?Sized>(
359 &mut self,
360 func: &mut ir::Function,
361 index: u32,
362 environ: &mut FE,
363 ) -> WasmResult<(ir::SigRef, usize)> {
364 let index = SignatureIndex::from_u32(index);
365 match self.signatures.entry(index) {
366 Occupied(entry) => Ok(*entry.get()),
367 Vacant(entry) => {
368 let sig = environ.make_indirect_sig(func, index)?;
369 Ok(*entry.insert((sig, normal_args(&func.dfg.signatures[sig]))))
370 }
371 }
372 }
373
374 /// Get the `FuncRef` reference that should be used to make a direct call to function
375 /// `index`. Also return the number of WebAssembly arguments in the signature.
376 ///
377 /// Create the function reference if necessary.
get_direct_func<FE: FuncEnvironment + ?Sized>( &mut self, func: &mut ir::Function, index: u32, environ: &mut FE, ) -> WasmResult<(ir::FuncRef, usize)>378 pub fn get_direct_func<FE: FuncEnvironment + ?Sized>(
379 &mut self,
380 func: &mut ir::Function,
381 index: u32,
382 environ: &mut FE,
383 ) -> WasmResult<(ir::FuncRef, usize)> {
384 let index = FuncIndex::from_u32(index);
385 match self.functions.entry(index) {
386 Occupied(entry) => Ok(*entry.get()),
387 Vacant(entry) => {
388 let fref = environ.make_direct_func(func, index)?;
389 let sig = func.dfg.ext_funcs[fref].signature;
390 Ok(*entry.insert((fref, normal_args(&func.dfg.signatures[sig]))))
391 }
392 }
393 }
394 }
395
396 /// Count the number of normal parameters in a signature.
397 /// Exclude special-purpose parameters that represent runtime stuff and not WebAssembly arguments.
normal_args(sig: &ir::Signature) -> usize398 fn normal_args(sig: &ir::Signature) -> usize {
399 sig.params
400 .iter()
401 .filter(|arg| arg.purpose == ir::ArgumentPurpose::Normal)
402 .count()
403 }
404