wasmer_compiler_llvm/translator/
state.rs

1use inkwell::{
2    basic_block::BasicBlock,
3    values::{BasicValue, BasicValueEnum, PhiValue},
4};
5use smallvec::SmallVec;
6use std::{
7    collections::VecDeque,
8    ops::{BitAnd, BitOr, BitOrAssign},
9};
10use wasmer_types::CompileError;
11
12#[derive(Debug)]
13#[allow(dead_code)]
14pub enum ControlFrame<'ctx> {
15    Block {
16        next: BasicBlock<'ctx>,
17        phis: SmallVec<[PhiValue<'ctx>; 1]>,
18        stack_size_snapshot: usize,
19    },
20    Loop {
21        body: BasicBlock<'ctx>,
22        next: BasicBlock<'ctx>,
23        phis: SmallVec<[PhiValue<'ctx>; 1]>,
24        loop_body_phis: SmallVec<[PhiValue<'ctx>; 1]>,
25        stack_size_snapshot: usize,
26    },
27    IfElse {
28        if_then: BasicBlock<'ctx>,
29        if_else: BasicBlock<'ctx>,
30        next: BasicBlock<'ctx>,
31        then_phis: SmallVec<[PhiValue<'ctx>; 1]>,
32        else_phis: SmallVec<[PhiValue<'ctx>; 1]>,
33        next_phis: SmallVec<[PhiValue<'ctx>; 1]>,
34        stack_size_snapshot: usize,
35        if_else_state: IfElseState,
36    },
37    Landingpad {
38        next: BasicBlock<'ctx>,
39        next_phis: SmallVec<[PhiValue<'ctx>; 1]>,
40        stack_size_snapshot: usize,
41    },
42}
43
44#[derive(Debug)]
45pub enum IfElseState {
46    If,
47    Else,
48}
49
50impl<'ctx> ControlFrame<'ctx> {
51    pub fn code_after(&self) -> &BasicBlock<'ctx> {
52        match self {
53            ControlFrame::Block { next, .. }
54            | ControlFrame::Loop { next, .. }
55            | ControlFrame::Landingpad { next, .. }
56            | ControlFrame::IfElse { next, .. } => next,
57        }
58    }
59
60    pub fn br_dest(&self) -> &BasicBlock<'ctx> {
61        match self {
62            ControlFrame::Block { next, .. }
63            | ControlFrame::IfElse { next, .. }
64            | ControlFrame::Landingpad { next, .. } => next,
65            ControlFrame::Loop { body, .. } => body,
66        }
67    }
68
69    pub fn phis(&self) -> &[PhiValue<'ctx>] {
70        match self {
71            ControlFrame::Block { phis, .. } | ControlFrame::Loop { phis, .. } => phis.as_slice(),
72            ControlFrame::IfElse { next_phis, .. } | ControlFrame::Landingpad { next_phis, .. } => {
73                next_phis.as_slice()
74            }
75        }
76    }
77
78    /// PHI nodes for stack values in the loop body.
79    pub fn loop_body_phis(&self) -> &[PhiValue<'ctx>] {
80        match self {
81            ControlFrame::Loop { loop_body_phis, .. } => loop_body_phis.as_slice(),
82            _ => &[],
83        }
84    }
85
86    pub fn is_loop(&self) -> bool {
87        matches!(self, ControlFrame::Loop { .. })
88    }
89}
90
91#[derive(Debug, Default, Eq, PartialEq, Copy, Clone, Hash)]
92pub struct ExtraInfo {
93    state: u8,
94}
95impl ExtraInfo {
96    // This value is required to be arithmetic 32-bit NaN (or 32x4) by the WAsm
97    // machine, but which might not be in the LLVM value. The conversion to
98    // arithmetic NaN is pending. It is required for correctness.
99    //
100    // When applied to a 64-bit value, this flag has no meaning and must be
101    // ignored. It may be set in such cases to allow for common handling of
102    // 32 and 64-bit operations.
103    pub const fn pending_f32_nan() -> ExtraInfo {
104        ExtraInfo { state: 1 }
105    }
106
107    // This value is required to be arithmetic 64-bit NaN (or 64x2) by the WAsm
108    // machine, but which might not be in the LLVM value. The conversion to
109    // arithmetic NaN is pending. It is required for correctness.
110    //
111    // When applied to a 32-bit value, this flag has no meaning and must be
112    // ignored. It may be set in such cases to allow for common handling of
113    // 32 and 64-bit operations.
114    pub const fn pending_f64_nan() -> ExtraInfo {
115        ExtraInfo { state: 2 }
116    }
117
118    // This value either does not contain a 32-bit NaN, or it contains an
119    // arithmetic NaN. In SIMD, applies to all 4 lanes.
120    pub const fn arithmetic_f32() -> ExtraInfo {
121        ExtraInfo { state: 4 }
122    }
123
124    // This value either does not contain a 64-bit NaN, or it contains an
125    // arithmetic NaN. In SIMD, applies to both lanes.
126    pub const fn arithmetic_f64() -> ExtraInfo {
127        ExtraInfo { state: 8 }
128    }
129
130    pub const fn has_pending_f32_nan(&self) -> bool {
131        self.state & ExtraInfo::pending_f32_nan().state != 0
132    }
133    pub const fn has_pending_f64_nan(&self) -> bool {
134        self.state & ExtraInfo::pending_f64_nan().state != 0
135    }
136    pub const fn is_arithmetic_f32(&self) -> bool {
137        self.state & ExtraInfo::arithmetic_f32().state != 0
138    }
139    pub const fn is_arithmetic_f64(&self) -> bool {
140        self.state & ExtraInfo::arithmetic_f64().state != 0
141    }
142
143    pub const fn strip_pending(&self) -> ExtraInfo {
144        ExtraInfo {
145            state: self.state
146                & !(ExtraInfo::pending_f32_nan().state | ExtraInfo::pending_f64_nan().state),
147        }
148    }
149}
150
151// Union two ExtraInfos.
152impl BitOr for ExtraInfo {
153    type Output = Result<Self, CompileError>;
154
155    fn bitor(self, other: Self) -> Self::Output {
156        if (self.has_pending_f32_nan() && other.has_pending_f64_nan())
157            || (self.has_pending_f64_nan() && other.has_pending_f32_nan())
158        {
159            return Err(CompileError::Codegen("Can't produce bitwise or of two different states if there are two different kinds of nan canonicalizations at the same time".to_string()));
160        }
161
162        Ok(ExtraInfo {
163            state: if self.is_arithmetic_f32() || other.is_arithmetic_f32() {
164                ExtraInfo::arithmetic_f32().state
165            } else if self.has_pending_f32_nan() || other.has_pending_f32_nan() {
166                ExtraInfo::pending_f32_nan().state
167            } else {
168                0
169            } + if self.is_arithmetic_f64() || other.is_arithmetic_f64() {
170                ExtraInfo::arithmetic_f64().state
171            } else if self.has_pending_f64_nan() || other.has_pending_f64_nan() {
172                ExtraInfo::pending_f64_nan().state
173            } else {
174                0
175            },
176        })
177    }
178}
179impl BitOrAssign for ExtraInfo {
180    fn bitor_assign(&mut self, other: Self) {
181        *self = (*self | other).unwrap();
182    }
183}
184
185// Intersection for ExtraInfo.
186impl BitAnd for ExtraInfo {
187    type Output = Result<Self, CompileError>;
188    fn bitand(self, other: Self) -> Self::Output {
189        // Pending canonicalizations are not safe to discard, or even reorder.
190        debug_assert!(
191            self.has_pending_f32_nan() == other.has_pending_f32_nan()
192                || self.is_arithmetic_f32()
193                || other.is_arithmetic_f32()
194        );
195        debug_assert!(
196            self.has_pending_f64_nan() == other.has_pending_f64_nan()
197                || self.is_arithmetic_f64()
198                || other.is_arithmetic_f64()
199        );
200        let info = match (
201            self.is_arithmetic_f32() && other.is_arithmetic_f32(),
202            self.is_arithmetic_f64() && other.is_arithmetic_f64(),
203        ) {
204            (false, false) => Default::default(),
205            (true, false) => ExtraInfo::arithmetic_f32(),
206            (false, true) => ExtraInfo::arithmetic_f64(),
207            (true, true) => (ExtraInfo::arithmetic_f32() | ExtraInfo::arithmetic_f64())?,
208        };
209        match (self.has_pending_f32_nan(), self.has_pending_f64_nan()) {
210            (false, false) => Ok(info),
211            (true, false) => info | ExtraInfo::pending_f32_nan(),
212            (false, true) => info | ExtraInfo::pending_f64_nan(),
213            (true, true) => unreachable!("Can't form ExtraInfo with two pending canonicalizations"),
214        }
215    }
216}
217
218#[derive(Debug, Clone, Copy)]
219pub struct TagCatchInfo<'ctx> {
220    pub tag: u32,
221    // The catch block
222    pub catch_block: BasicBlock<'ctx>,
223    // The PHI node to receive the exnref, if needed; catch_all
224    // blocks don't need the exnref.
225    pub exnref_phi: Option<PhiValue<'ctx>>,
226}
227
228#[derive(Debug)]
229pub struct Landingpad<'ctx> {
230    // The block that has the landingpad instruction.
231    // Will be None for catch-less try_table instructions
232    // with no outer landingpads.
233    pub lpad_block: Option<BasicBlock<'ctx>>,
234    // The tags that this landingpad can catch
235    pub tags: Vec<TagCatchInfo<'ctx>>,
236}
237
238#[derive(Debug)]
239pub struct State<'ctx> {
240    pub stack: Vec<(BasicValueEnum<'ctx>, ExtraInfo)>,
241    pub control_stack: Vec<ControlFrame<'ctx>>,
242    pub landingpads: VecDeque<Landingpad<'ctx>>,
243    pub reachable: bool,
244}
245
246impl<'ctx> State<'ctx> {
247    pub fn new() -> Self {
248        Self {
249            stack: vec![],
250            control_stack: vec![],
251            reachable: true,
252            landingpads: VecDeque::new(),
253        }
254    }
255
256    pub fn has_control_frames(&self) -> bool {
257        !self.control_stack.is_empty()
258    }
259
260    pub fn reset_stack(&mut self, frame: &ControlFrame<'ctx>) {
261        let stack_size_snapshot = match frame {
262            ControlFrame::Block {
263                stack_size_snapshot,
264                ..
265            }
266            | ControlFrame::Loop {
267                stack_size_snapshot,
268                ..
269            }
270            | ControlFrame::IfElse {
271                stack_size_snapshot,
272                ..
273            }
274            | ControlFrame::Landingpad {
275                stack_size_snapshot,
276                ..
277            } => *stack_size_snapshot,
278        };
279        self.stack.truncate(stack_size_snapshot);
280    }
281
282    pub fn outermost_frame(&self) -> Result<&ControlFrame<'ctx>, CompileError> {
283        self.control_stack.first().ok_or_else(|| {
284            CompileError::Codegen("outermost_frame: invalid control stack depth".to_string())
285        })
286    }
287
288    pub fn frame_at_depth(&self, depth: u32) -> Result<&ControlFrame<'ctx>, CompileError> {
289        let index = self
290            .control_stack
291            .len()
292            .checked_sub(1 + (depth as usize))
293            .ok_or_else(|| {
294                CompileError::Codegen("frame_at_depth: invalid control stack depth".to_string())
295            })?;
296        Ok(&self.control_stack[index])
297    }
298
299    pub fn frame_at_depth_mut(
300        &mut self,
301        depth: u32,
302    ) -> Result<&mut ControlFrame<'ctx>, CompileError> {
303        let index = self
304            .control_stack
305            .len()
306            .checked_sub(1 + (depth as usize))
307            .ok_or_else(|| {
308                CompileError::Codegen("frame_at_depth_mut: invalid control stack depth".to_string())
309            })?;
310        Ok(&mut self.control_stack[index])
311    }
312
313    pub fn pop_frame(&mut self) -> Result<ControlFrame<'ctx>, CompileError> {
314        self.control_stack.pop().ok_or_else(|| {
315            CompileError::Codegen("pop_frame: cannot pop from control stack".to_string())
316        })
317    }
318
319    pub fn push1<T: BasicValue<'ctx>>(&mut self, value: T) {
320        self.push1_extra(value, Default::default());
321    }
322
323    pub fn push1_extra<T: BasicValue<'ctx>>(&mut self, value: T, info: ExtraInfo) {
324        self.stack.push((value.as_basic_value_enum(), info));
325    }
326
327    pub fn pop1(&mut self) -> Result<BasicValueEnum<'ctx>, CompileError> {
328        Ok(self.pop1_extra()?.0)
329    }
330
331    pub fn pop1_extra(&mut self) -> Result<(BasicValueEnum<'ctx>, ExtraInfo), CompileError> {
332        self.stack
333            .pop()
334            .ok_or_else(|| CompileError::Codegen("pop1_extra: invalid value stack".to_string()))
335    }
336
337    pub fn pop2(&mut self) -> Result<(BasicValueEnum<'ctx>, BasicValueEnum<'ctx>), CompileError> {
338        let v2 = self.pop1()?;
339        let v1 = self.pop1()?;
340        Ok((v1, v2))
341    }
342
343    #[allow(clippy::type_complexity)]
344    pub fn pop2_extra(
345        &mut self,
346    ) -> Result<
347        (
348            (BasicValueEnum<'ctx>, ExtraInfo),
349            (BasicValueEnum<'ctx>, ExtraInfo),
350        ),
351        CompileError,
352    > {
353        let v2 = self.pop1_extra()?;
354        let v1 = self.pop1_extra()?;
355        Ok((v1, v2))
356    }
357
358    pub fn pop3(
359        &mut self,
360    ) -> Result<
361        (
362            BasicValueEnum<'ctx>,
363            BasicValueEnum<'ctx>,
364            BasicValueEnum<'ctx>,
365        ),
366        CompileError,
367    > {
368        let v3 = self.pop1()?;
369        let v2 = self.pop1()?;
370        let v1 = self.pop1()?;
371        Ok((v1, v2, v3))
372    }
373
374    #[allow(clippy::type_complexity)]
375    pub fn pop3_extra(
376        &mut self,
377    ) -> Result<
378        (
379            (BasicValueEnum<'ctx>, ExtraInfo),
380            (BasicValueEnum<'ctx>, ExtraInfo),
381            (BasicValueEnum<'ctx>, ExtraInfo),
382        ),
383        CompileError,
384    > {
385        let v3 = self.pop1_extra()?;
386        let v2 = self.pop1_extra()?;
387        let v1 = self.pop1_extra()?;
388        Ok((v1, v2, v3))
389    }
390
391    pub fn peek1_extra(&self) -> Result<(BasicValueEnum<'ctx>, ExtraInfo), CompileError> {
392        let index =
393            self.stack.len().checked_sub(1).ok_or_else(|| {
394                CompileError::Codegen("peek1_extra: invalid value stack".to_string())
395            })?;
396        Ok(self.stack[index])
397    }
398
399    pub fn peekn(&self, n: usize) -> Result<Vec<BasicValueEnum<'ctx>>, CompileError> {
400        Ok(self.peekn_extra(n)?.iter().map(|x| x.0).collect())
401    }
402
403    pub fn peekn_extra(
404        &self,
405        n: usize,
406    ) -> Result<&[(BasicValueEnum<'ctx>, ExtraInfo)], CompileError> {
407        let index =
408            self.stack.len().checked_sub(n).ok_or_else(|| {
409                CompileError::Codegen("peekn_extra: invalid value stack".to_string())
410            })?;
411        Ok(&self.stack[index..])
412    }
413
414    pub fn popn_save_extra(
415        &mut self,
416        n: usize,
417    ) -> Result<Vec<(BasicValueEnum<'ctx>, ExtraInfo)>, CompileError> {
418        let v = self.peekn_extra(n)?.to_vec();
419        self.popn(n)?;
420        Ok(v)
421    }
422
423    pub fn popn(&mut self, n: usize) -> Result<(), CompileError> {
424        let index = self
425            .stack
426            .len()
427            .checked_sub(n)
428            .ok_or_else(|| CompileError::Codegen("popn: invalid value stack".to_string()))?;
429
430        self.stack.truncate(index);
431        Ok(())
432    }
433
434    pub fn push_block(
435        &mut self,
436        next: BasicBlock<'ctx>,
437        phis: SmallVec<[PhiValue<'ctx>; 1]>,
438        num_inputs: usize,
439    ) {
440        self.control_stack.push(ControlFrame::Block {
441            next,
442            phis,
443            stack_size_snapshot: self
444                .stack
445                .len()
446                .checked_sub(num_inputs)
447                .expect("Internal codegen error: not enough inputs on stack"),
448        });
449    }
450
451    pub fn push_loop(
452        &mut self,
453        body: BasicBlock<'ctx>,
454        next: BasicBlock<'ctx>,
455        loop_body_phis: SmallVec<[PhiValue<'ctx>; 1]>,
456        phis: SmallVec<[PhiValue<'ctx>; 1]>,
457        num_inputs: usize,
458    ) {
459        self.control_stack.push(ControlFrame::Loop {
460            body,
461            next,
462            loop_body_phis,
463            phis,
464            stack_size_snapshot: self
465                .stack
466                .len()
467                .checked_sub(num_inputs)
468                .expect("Internal codegen error: not enough inputs on stack"),
469        });
470    }
471
472    #[allow(clippy::too_many_arguments)]
473    pub fn push_if(
474        &mut self,
475        if_then: BasicBlock<'ctx>,
476        if_else: BasicBlock<'ctx>,
477        next: BasicBlock<'ctx>,
478        then_phis: SmallVec<[PhiValue<'ctx>; 1]>,
479        else_phis: SmallVec<[PhiValue<'ctx>; 1]>,
480        next_phis: SmallVec<[PhiValue<'ctx>; 1]>,
481        num_inputs: usize,
482    ) {
483        self.control_stack.push(ControlFrame::IfElse {
484            if_then,
485            if_else,
486            next,
487            then_phis,
488            else_phis,
489            next_phis,
490            if_else_state: IfElseState::If,
491            stack_size_snapshot: self
492                .stack
493                .len()
494                .checked_sub(num_inputs)
495                .expect("Internal codegen error: not enough inputs on stack"),
496        });
497    }
498
499    pub fn push_landingpad(
500        &mut self,
501        lpad_block: Option<BasicBlock<'ctx>>,
502        next: BasicBlock<'ctx>,
503        next_phis: SmallVec<[PhiValue<'ctx>; 1]>,
504        tags: &[TagCatchInfo<'ctx>],
505        num_inputs: usize,
506    ) {
507        self.control_stack.push(ControlFrame::Landingpad {
508            next,
509            next_phis,
510            stack_size_snapshot: self
511                .stack
512                .len()
513                .checked_sub(num_inputs)
514                .expect("Internal codegen error: not enough inputs on stack"),
515        });
516
517        self.landingpads.push_back(Landingpad {
518            lpad_block,
519            tags: tags.to_vec(),
520        })
521    }
522
523    // Throws and function calls need to be turned into invokes targeting this
524    // landingpad if it exists; otherwise, there is no landingpad within this
525    // frame. Note that the innermost landing pad has catch clauses for *all*
526    // the tags that are active in this frame, including the ones from outer
527    // landingpads, so there's never a reason to target any other landingpad.
528    pub(crate) fn get_innermost_landingpad(&mut self) -> Option<BasicBlock<'ctx>> {
529        self.landingpads
530            .iter()
531            .rev()
532            .filter_map(|v| v.lpad_block)
533            .next()
534    }
535
536    pub(crate) fn pop_landingpad(&mut self) -> bool {
537        self.landingpads.pop_back().is_some()
538    }
539}