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 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 pub const fn pending_f32_nan() -> ExtraInfo {
104 ExtraInfo { state: 1 }
105 }
106
107 pub const fn pending_f64_nan() -> ExtraInfo {
115 ExtraInfo { state: 2 }
116 }
117
118 pub const fn arithmetic_f32() -> ExtraInfo {
121 ExtraInfo { state: 4 }
122 }
123
124 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
151impl 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
185impl BitAnd for ExtraInfo {
187 type Output = Result<Self, CompileError>;
188 fn bitand(self, other: Self) -> Self::Output {
189 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 pub catch_block: BasicBlock<'ctx>,
223 pub exnref_phi: Option<PhiValue<'ctx>>,
226}
227
228#[derive(Debug)]
229pub struct Landingpad<'ctx> {
230 pub lpad_block: Option<BasicBlock<'ctx>>,
234 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 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}