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 exception object
224    pub wasmer_exc_phi: Option<PhiValue<'ctx>>,
225    // The PHI node to receive the libunwind exception pointer,
226    // which stands in as the exnref for now
227    pub uw_exc_ptr_phi: Option<PhiValue<'ctx>>,
228}
229
230#[derive(Debug)]
231pub struct Landingpad<'ctx> {
232    // The block that has the landingpad instruction.
233    // Will be None for catch-less try_table instructions
234    // with no outer landingpads.
235    pub lpad_block: Option<BasicBlock<'ctx>>,
236    // The tags that this landingpad can catch
237    pub tags: Vec<TagCatchInfo<'ctx>>,
238}
239
240#[derive(Debug)]
241pub struct State<'ctx> {
242    pub stack: Vec<(BasicValueEnum<'ctx>, ExtraInfo)>,
243    pub control_stack: Vec<ControlFrame<'ctx>>,
244    pub landingpads: VecDeque<Landingpad<'ctx>>,
245    pub reachable: bool,
246}
247
248impl<'ctx> State<'ctx> {
249    pub fn new() -> Self {
250        Self {
251            stack: vec![],
252            control_stack: vec![],
253            reachable: true,
254            landingpads: VecDeque::new(),
255        }
256    }
257
258    pub fn has_control_frames(&self) -> bool {
259        !self.control_stack.is_empty()
260    }
261
262    pub fn reset_stack(&mut self, frame: &ControlFrame<'ctx>) {
263        let stack_size_snapshot = match frame {
264            ControlFrame::Block {
265                stack_size_snapshot,
266                ..
267            }
268            | ControlFrame::Loop {
269                stack_size_snapshot,
270                ..
271            }
272            | ControlFrame::IfElse {
273                stack_size_snapshot,
274                ..
275            }
276            | ControlFrame::Landingpad {
277                stack_size_snapshot,
278                ..
279            } => *stack_size_snapshot,
280        };
281        self.stack.truncate(stack_size_snapshot);
282    }
283
284    pub fn outermost_frame(&self) -> Result<&ControlFrame<'ctx>, CompileError> {
285        self.control_stack.first().ok_or_else(|| {
286            CompileError::Codegen("outermost_frame: invalid control stack depth".to_string())
287        })
288    }
289
290    pub fn frame_at_depth(&self, depth: u32) -> Result<&ControlFrame<'ctx>, CompileError> {
291        let index = self
292            .control_stack
293            .len()
294            .checked_sub(1 + (depth as usize))
295            .ok_or_else(|| {
296                CompileError::Codegen("frame_at_depth: invalid control stack depth".to_string())
297            })?;
298        Ok(&self.control_stack[index])
299    }
300
301    pub fn frame_at_depth_mut(
302        &mut self,
303        depth: u32,
304    ) -> Result<&mut ControlFrame<'ctx>, CompileError> {
305        let index = self
306            .control_stack
307            .len()
308            .checked_sub(1 + (depth as usize))
309            .ok_or_else(|| {
310                CompileError::Codegen("frame_at_depth_mut: invalid control stack depth".to_string())
311            })?;
312        Ok(&mut self.control_stack[index])
313    }
314
315    pub fn pop_frame(&mut self) -> Result<ControlFrame<'ctx>, CompileError> {
316        self.control_stack.pop().ok_or_else(|| {
317            CompileError::Codegen("pop_frame: cannot pop from control stack".to_string())
318        })
319    }
320
321    pub fn push1<T: BasicValue<'ctx>>(&mut self, value: T) {
322        self.push1_extra(value, Default::default());
323    }
324
325    pub fn push1_extra<T: BasicValue<'ctx>>(&mut self, value: T, info: ExtraInfo) {
326        self.stack.push((value.as_basic_value_enum(), info));
327    }
328
329    pub fn pop1(&mut self) -> Result<BasicValueEnum<'ctx>, CompileError> {
330        Ok(self.pop1_extra()?.0)
331    }
332
333    pub fn pop1_extra(&mut self) -> Result<(BasicValueEnum<'ctx>, ExtraInfo), CompileError> {
334        self.stack
335            .pop()
336            .ok_or_else(|| CompileError::Codegen("pop1_extra: invalid value stack".to_string()))
337    }
338
339    pub fn pop2(&mut self) -> Result<(BasicValueEnum<'ctx>, BasicValueEnum<'ctx>), CompileError> {
340        let v2 = self.pop1()?;
341        let v1 = self.pop1()?;
342        Ok((v1, v2))
343    }
344
345    #[allow(clippy::type_complexity)]
346    pub fn pop2_extra(
347        &mut self,
348    ) -> Result<
349        (
350            (BasicValueEnum<'ctx>, ExtraInfo),
351            (BasicValueEnum<'ctx>, ExtraInfo),
352        ),
353        CompileError,
354    > {
355        let v2 = self.pop1_extra()?;
356        let v1 = self.pop1_extra()?;
357        Ok((v1, v2))
358    }
359
360    pub fn pop3(
361        &mut self,
362    ) -> Result<
363        (
364            BasicValueEnum<'ctx>,
365            BasicValueEnum<'ctx>,
366            BasicValueEnum<'ctx>,
367        ),
368        CompileError,
369    > {
370        let v3 = self.pop1()?;
371        let v2 = self.pop1()?;
372        let v1 = self.pop1()?;
373        Ok((v1, v2, v3))
374    }
375
376    #[allow(clippy::type_complexity)]
377    pub fn pop3_extra(
378        &mut self,
379    ) -> Result<
380        (
381            (BasicValueEnum<'ctx>, ExtraInfo),
382            (BasicValueEnum<'ctx>, ExtraInfo),
383            (BasicValueEnum<'ctx>, ExtraInfo),
384        ),
385        CompileError,
386    > {
387        let v3 = self.pop1_extra()?;
388        let v2 = self.pop1_extra()?;
389        let v1 = self.pop1_extra()?;
390        Ok((v1, v2, v3))
391    }
392
393    pub fn peek1_extra(&self) -> Result<(BasicValueEnum<'ctx>, ExtraInfo), CompileError> {
394        let index =
395            self.stack.len().checked_sub(1).ok_or_else(|| {
396                CompileError::Codegen("peek1_extra: invalid value stack".to_string())
397            })?;
398        Ok(self.stack[index])
399    }
400
401    pub fn peekn(&self, n: usize) -> Result<Vec<BasicValueEnum<'ctx>>, CompileError> {
402        Ok(self.peekn_extra(n)?.iter().map(|x| x.0).collect())
403    }
404
405    pub fn peekn_extra(
406        &self,
407        n: usize,
408    ) -> Result<&[(BasicValueEnum<'ctx>, ExtraInfo)], CompileError> {
409        let index =
410            self.stack.len().checked_sub(n).ok_or_else(|| {
411                CompileError::Codegen("peekn_extra: invalid value stack".to_string())
412            })?;
413        Ok(&self.stack[index..])
414    }
415
416    pub fn popn_save_extra(
417        &mut self,
418        n: usize,
419    ) -> Result<Vec<(BasicValueEnum<'ctx>, ExtraInfo)>, CompileError> {
420        let v = self.peekn_extra(n)?.to_vec();
421        self.popn(n)?;
422        Ok(v)
423    }
424
425    pub fn popn(&mut self, n: usize) -> Result<(), CompileError> {
426        let index = self
427            .stack
428            .len()
429            .checked_sub(n)
430            .ok_or_else(|| CompileError::Codegen("popn: invalid value stack".to_string()))?;
431
432        self.stack.truncate(index);
433        Ok(())
434    }
435
436    pub fn push_block(
437        &mut self,
438        next: BasicBlock<'ctx>,
439        phis: SmallVec<[PhiValue<'ctx>; 1]>,
440        num_inputs: usize,
441    ) {
442        self.control_stack.push(ControlFrame::Block {
443            next,
444            phis,
445            stack_size_snapshot: self
446                .stack
447                .len()
448                .checked_sub(num_inputs)
449                .expect("Internal codegen error: not enough inputs on stack"),
450        });
451    }
452
453    pub fn push_loop(
454        &mut self,
455        body: BasicBlock<'ctx>,
456        next: BasicBlock<'ctx>,
457        loop_body_phis: SmallVec<[PhiValue<'ctx>; 1]>,
458        phis: SmallVec<[PhiValue<'ctx>; 1]>,
459        num_inputs: usize,
460    ) {
461        self.control_stack.push(ControlFrame::Loop {
462            body,
463            next,
464            loop_body_phis,
465            phis,
466            stack_size_snapshot: self
467                .stack
468                .len()
469                .checked_sub(num_inputs)
470                .expect("Internal codegen error: not enough inputs on stack"),
471        });
472    }
473
474    #[allow(clippy::too_many_arguments)]
475    pub fn push_if(
476        &mut self,
477        if_then: BasicBlock<'ctx>,
478        if_else: BasicBlock<'ctx>,
479        next: BasicBlock<'ctx>,
480        then_phis: SmallVec<[PhiValue<'ctx>; 1]>,
481        else_phis: SmallVec<[PhiValue<'ctx>; 1]>,
482        next_phis: SmallVec<[PhiValue<'ctx>; 1]>,
483        num_inputs: usize,
484    ) {
485        self.control_stack.push(ControlFrame::IfElse {
486            if_then,
487            if_else,
488            next,
489            then_phis,
490            else_phis,
491            next_phis,
492            if_else_state: IfElseState::If,
493            stack_size_snapshot: self
494                .stack
495                .len()
496                .checked_sub(num_inputs)
497                .expect("Internal codegen error: not enough inputs on stack"),
498        });
499    }
500
501    pub fn push_landingpad(
502        &mut self,
503        lpad_block: Option<BasicBlock<'ctx>>,
504        next: BasicBlock<'ctx>,
505        next_phis: SmallVec<[PhiValue<'ctx>; 1]>,
506        tags: &[TagCatchInfo<'ctx>],
507        num_inputs: usize,
508    ) {
509        self.control_stack.push(ControlFrame::Landingpad {
510            next,
511            next_phis,
512            stack_size_snapshot: self
513                .stack
514                .len()
515                .checked_sub(num_inputs)
516                .expect("Internal codegen error: not enough inputs on stack"),
517        });
518
519        self.landingpads.push_back(Landingpad {
520            lpad_block,
521            tags: tags.to_vec(),
522        })
523    }
524
525    // Throws and function calls need to be turned into invokes targeting this
526    // landingpad if it exists; otherwise, there is no landingpad within this
527    // frame. Note that the innermost landing pad has catch clauses for *all*
528    // the tags that are active in this frame, including the ones from outer
529    // landingpads, so there's never a reason to target any other landingpad.
530    pub(crate) fn get_innermost_landingpad(&mut self) -> Option<BasicBlock<'ctx>> {
531        self.landingpads
532            .iter()
533            .rev()
534            .filter_map(|v| v.lpad_block)
535            .next()
536    }
537
538    pub(crate) fn pop_landingpad(&mut self) -> bool {
539        self.landingpads.pop_back().is_some()
540    }
541}