cranelift_frontend/
frontend.rs

1//! A frontend for building Cranelift IR from other languages.
2use crate::ssa::{SSABuilder, SideEffects};
3use crate::variable::Variable;
4use alloc::vec::Vec;
5use core::fmt::{self, Debug};
6use cranelift_codegen::cursor::{Cursor, CursorPosition, FuncCursor};
7use cranelift_codegen::entity::{EntityRef, EntitySet, SecondaryMap};
8use cranelift_codegen::ir;
9use cranelift_codegen::ir::condcodes::IntCC;
10use cranelift_codegen::ir::{
11    types, AbiParam, Block, DataFlowGraph, DynamicStackSlot, DynamicStackSlotData, ExtFuncData,
12    ExternalName, FuncRef, Function, GlobalValue, GlobalValueData, Inst, InstBuilder,
13    InstBuilderBase, InstructionData, JumpTable, JumpTableData, LibCall, MemFlags, RelSourceLoc,
14    SigRef, Signature, StackSlot, StackSlotData, Type, Value, ValueLabel, ValueLabelAssignments,
15    ValueLabelStart,
16};
17use cranelift_codegen::isa::TargetFrontendConfig;
18use cranelift_codegen::packed_option::PackedOption;
19use cranelift_codegen::traversals::Dfs;
20use smallvec::SmallVec;
21
22mod safepoints;
23
24/// Structure used for translating a series of functions into Cranelift IR.
25///
26/// In order to reduce memory reallocations when compiling multiple functions,
27/// [`FunctionBuilderContext`] holds various data structures which are cleared between
28/// functions, rather than dropped, preserving the underlying allocations.
29#[derive(Default)]
30pub struct FunctionBuilderContext {
31    ssa: SSABuilder,
32    status: SecondaryMap<Block, BlockStatus>,
33    types: SecondaryMap<Variable, Type>,
34    stack_map_vars: EntitySet<Variable>,
35    stack_map_values: EntitySet<Value>,
36    safepoints: safepoints::SafepointSpiller,
37}
38
39/// Temporary object used to build a single Cranelift IR [`Function`].
40pub struct FunctionBuilder<'a> {
41    /// The function currently being built.
42    /// This field is public so the function can be re-borrowed.
43    pub func: &'a mut Function,
44
45    /// Source location to assign to all new instructions.
46    srcloc: ir::SourceLoc,
47
48    func_ctx: &'a mut FunctionBuilderContext,
49    position: PackedOption<Block>,
50}
51
52#[derive(Clone, Default, Eq, PartialEq)]
53enum BlockStatus {
54    /// No instructions have been added.
55    #[default]
56    Empty,
57    /// Some instructions have been added, but no terminator.
58    Partial,
59    /// A terminator has been added; no further instructions may be added.
60    Filled,
61}
62
63impl FunctionBuilderContext {
64    /// Creates a [`FunctionBuilderContext`] structure. The structure is automatically cleared after
65    /// each [`FunctionBuilder`] completes translating a function.
66    pub fn new() -> Self {
67        Self::default()
68    }
69
70    fn clear(&mut self) {
71        let FunctionBuilderContext {
72            ssa,
73            status,
74            types,
75            stack_map_vars,
76            stack_map_values,
77            safepoints,
78        } = self;
79        ssa.clear();
80        status.clear();
81        types.clear();
82        stack_map_values.clear();
83        stack_map_vars.clear();
84        safepoints.clear();
85    }
86
87    fn is_empty(&self) -> bool {
88        self.ssa.is_empty() && self.status.is_empty() && self.types.is_empty()
89    }
90}
91
92/// Implementation of the [`InstBuilder`] that has
93/// one convenience method per Cranelift IR instruction.
94pub struct FuncInstBuilder<'short, 'long: 'short> {
95    builder: &'short mut FunctionBuilder<'long>,
96    block: Block,
97}
98
99impl<'short, 'long> FuncInstBuilder<'short, 'long> {
100    fn new(builder: &'short mut FunctionBuilder<'long>, block: Block) -> Self {
101        Self { builder, block }
102    }
103}
104
105impl<'short, 'long> InstBuilderBase<'short> for FuncInstBuilder<'short, 'long> {
106    fn data_flow_graph(&self) -> &DataFlowGraph {
107        &self.builder.func.dfg
108    }
109
110    fn data_flow_graph_mut(&mut self) -> &mut DataFlowGraph {
111        &mut self.builder.func.dfg
112    }
113
114    // This implementation is richer than `InsertBuilder` because we use the data of the
115    // instruction being inserted to add related info to the DFG and the SSA building system,
116    // and perform debug sanity checks.
117    fn build(self, data: InstructionData, ctrl_typevar: Type) -> (Inst, &'short mut DataFlowGraph) {
118        // We only insert the Block in the layout when an instruction is added to it
119        self.builder.ensure_inserted_block();
120
121        let inst = self.builder.func.dfg.make_inst(data);
122        self.builder.func.dfg.make_inst_results(inst, ctrl_typevar);
123        self.builder.func.layout.append_inst(inst, self.block);
124        if !self.builder.srcloc.is_default() {
125            self.builder.func.set_srcloc(inst, self.builder.srcloc);
126        }
127
128        match &self.builder.func.dfg.insts[inst] {
129            ir::InstructionData::Jump {
130                destination: dest, ..
131            } => {
132                // If the user has supplied jump arguments we must adapt the arguments of
133                // the destination block
134                let block = dest.block(&self.builder.func.dfg.value_lists);
135                self.builder.declare_successor(block, inst);
136            }
137
138            ir::InstructionData::Brif {
139                blocks: [branch_then, branch_else],
140                ..
141            } => {
142                let block_then = branch_then.block(&self.builder.func.dfg.value_lists);
143                let block_else = branch_else.block(&self.builder.func.dfg.value_lists);
144
145                self.builder.declare_successor(block_then, inst);
146                if block_then != block_else {
147                    self.builder.declare_successor(block_else, inst);
148                }
149            }
150
151            ir::InstructionData::BranchTable { table, .. } => {
152                let pool = &self.builder.func.dfg.value_lists;
153
154                // Unlike most other jumps/branches and like try_call,
155                // jump tables are capable of having the same successor appear
156                // multiple times, so we must deduplicate.
157                let mut unique = EntitySet::<Block>::new();
158                for dest_block in self
159                    .builder
160                    .func
161                    .stencil
162                    .dfg
163                    .jump_tables
164                    .get(*table)
165                    .expect("you are referencing an undeclared jump table")
166                    .all_branches()
167                {
168                    let block = dest_block.block(pool);
169                    if !unique.insert(block) {
170                        continue;
171                    }
172
173                    // Call `declare_block_predecessor` instead of `declare_successor` for
174                    // avoiding the borrow checker.
175                    self.builder
176                        .func_ctx
177                        .ssa
178                        .declare_block_predecessor(block, inst);
179                }
180            }
181
182            ir::InstructionData::TryCall { exception, .. }
183            | ir::InstructionData::TryCallIndirect { exception, .. } => {
184                let pool = &self.builder.func.dfg.value_lists;
185
186                // Unlike most other jumps/branches and like br_table,
187                // exception tables are capable of having the same successor
188                // appear multiple times, so we must deduplicate.
189                let mut unique = EntitySet::<Block>::new();
190                for dest_block in self
191                    .builder
192                    .func
193                    .stencil
194                    .dfg
195                    .exception_tables
196                    .get(*exception)
197                    .expect("you are referencing an undeclared exception table")
198                    .all_branches()
199                {
200                    let block = dest_block.block(pool);
201                    if !unique.insert(block) {
202                        continue;
203                    }
204
205                    // Call `declare_block_predecessor` instead of `declare_successor` for
206                    // avoiding the borrow checker.
207                    self.builder
208                        .func_ctx
209                        .ssa
210                        .declare_block_predecessor(block, inst);
211                }
212            }
213
214            inst => assert!(!inst.opcode().is_branch()),
215        }
216
217        if data.opcode().is_terminator() {
218            self.builder.fill_current_block()
219        }
220        (inst, &mut self.builder.func.dfg)
221    }
222}
223
224#[derive(Debug, Copy, Clone, PartialEq, Eq)]
225/// An error encountered when calling [`FunctionBuilder::try_use_var`].
226pub enum UseVariableError {
227    UsedBeforeDeclared(Variable),
228}
229
230impl fmt::Display for UseVariableError {
231    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
232        match self {
233            UseVariableError::UsedBeforeDeclared(variable) => {
234                write!(
235                    f,
236                    "variable {} was used before it was defined",
237                    variable.index()
238                )?;
239            }
240        }
241        Ok(())
242    }
243}
244
245impl std::error::Error for UseVariableError {}
246
247#[derive(Debug, Copy, Clone, Eq, PartialEq)]
248/// An error encountered when calling [`FunctionBuilder::try_declare_var`].
249pub enum DeclareVariableError {
250    DeclaredMultipleTimes(Variable),
251}
252
253impl std::error::Error for DeclareVariableError {}
254
255impl fmt::Display for DeclareVariableError {
256    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
257        match self {
258            DeclareVariableError::DeclaredMultipleTimes(variable) => {
259                write!(
260                    f,
261                    "variable {} was declared multiple times",
262                    variable.index()
263                )?;
264            }
265        }
266        Ok(())
267    }
268}
269
270#[derive(Debug, Copy, Clone, Eq, PartialEq)]
271/// An error encountered when defining the initial value of a variable.
272pub enum DefVariableError {
273    /// The variable was instantiated with a value of the wrong type.
274    ///
275    /// note: to obtain the type of the value, you can call
276    /// [`cranelift_codegen::ir::dfg::DataFlowGraph::value_type`] (using the
277    /// `FunctionBuilder.func.dfg` field)
278    TypeMismatch(Variable, Value),
279    /// The value was defined (in a call to [`FunctionBuilder::def_var`]) before
280    /// it was declared (in a call to [`FunctionBuilder::declare_var`]).
281    DefinedBeforeDeclared(Variable),
282}
283
284impl fmt::Display for DefVariableError {
285    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
286        match self {
287            DefVariableError::TypeMismatch(variable, value) => {
288                write!(
289                    f,
290                    "the types of variable {} and value {} are not the same.
291                    The `Value` supplied to `def_var` must be of the same type as
292                    the variable was declared to be of in `declare_var`.",
293                    variable.index(),
294                    value.as_u32()
295                )?;
296            }
297            DefVariableError::DefinedBeforeDeclared(variable) => {
298                write!(
299                    f,
300                    "the value of variable {} was declared before it was defined",
301                    variable.index()
302                )?;
303            }
304        }
305        Ok(())
306    }
307}
308
309/// This module allows you to create a function in Cranelift IR in a straightforward way, hiding
310/// all the complexity of its internal representation.
311///
312/// The module is parametrized by one type which is the representation of variables in your
313/// origin language. It offers a way to conveniently append instruction to your program flow.
314/// You are responsible to split your instruction flow into extended blocks (declared with
315/// [`create_block`](Self::create_block)) whose properties are:
316///
317/// - branch and jump instructions can only point at the top of extended blocks;
318/// - the last instruction of each block is a terminator instruction which has no natural successor,
319///   and those instructions can only appear at the end of extended blocks.
320///
321/// The parameters of Cranelift IR instructions are Cranelift IR values, which can only be created
322/// as results of other Cranelift IR instructions. To be able to create variables redefined multiple
323/// times in your program, use the [`def_var`](Self::def_var) and [`use_var`](Self::use_var) command,
324/// that will maintain the correspondence between your variables and Cranelift IR SSA values.
325///
326/// The first block for which you call [`switch_to_block`](Self::switch_to_block) will be assumed to
327/// be the beginning of the function.
328///
329/// At creation, a [`FunctionBuilder`] instance borrows an already allocated `Function` which it
330/// modifies with the information stored in the mutable borrowed
331/// [`FunctionBuilderContext`]. The function passed in argument should be newly created with
332/// [`Function::with_name_signature()`], whereas the [`FunctionBuilderContext`] can be kept as is
333/// between two function translations.
334///
335/// # Errors
336///
337/// The functions below will panic in debug mode whenever you try to modify the Cranelift IR
338/// function in a way that violate the coherence of the code. For instance: switching to a new
339/// [`Block`] when you haven't filled the current one with a terminator instruction, inserting a
340/// return instruction with arguments that don't match the function's signature.
341impl<'a> FunctionBuilder<'a> {
342    /// Creates a new [`FunctionBuilder`] structure that will operate on a [`Function`] using a
343    /// [`FunctionBuilderContext`].
344    pub fn new(func: &'a mut Function, func_ctx: &'a mut FunctionBuilderContext) -> Self {
345        debug_assert!(func_ctx.is_empty());
346        Self {
347            func,
348            srcloc: Default::default(),
349            func_ctx,
350            position: Default::default(),
351        }
352    }
353
354    /// Get the block that this builder is currently at.
355    pub fn current_block(&self) -> Option<Block> {
356        self.position.expand()
357    }
358
359    /// Set the source location that should be assigned to all new instructions.
360    pub fn set_srcloc(&mut self, srcloc: ir::SourceLoc) {
361        self.srcloc = srcloc;
362    }
363
364    /// Creates a new [`Block`] and returns its reference.
365    pub fn create_block(&mut self) -> Block {
366        let block = self.func.dfg.make_block();
367        self.func_ctx.ssa.declare_block(block);
368        block
369    }
370
371    /// Mark a block as "cold".
372    ///
373    /// This will try to move it out of the ordinary path of execution
374    /// when lowered to machine code.
375    pub fn set_cold_block(&mut self, block: Block) {
376        self.func.layout.set_cold(block);
377    }
378
379    /// Insert `block` in the layout *after* the existing block `after`.
380    pub fn insert_block_after(&mut self, block: Block, after: Block) {
381        self.func.layout.insert_block_after(block, after);
382    }
383
384    /// After the call to this function, new instructions will be inserted into the designated
385    /// block, in the order they are declared. You must declare the types of the [`Block`] arguments
386    /// you will use here.
387    ///
388    /// When inserting the terminator instruction (which doesn't have a fallthrough to its immediate
389    /// successor), the block will be declared filled and it will not be possible to append
390    /// instructions to it.
391    pub fn switch_to_block(&mut self, block: Block) {
392        log::trace!("switch to {block:?}");
393
394        // First we check that the previous block has been filled.
395        debug_assert!(
396            self.position.is_none()
397                || self.is_unreachable()
398                || self.is_pristine(self.position.unwrap())
399                || self.is_filled(self.position.unwrap()),
400            "you have to fill your block before switching"
401        );
402        // We cannot switch to a filled block
403        debug_assert!(
404            !self.is_filled(block),
405            "you cannot switch to a block which is already filled"
406        );
407
408        // Then we change the cursor position.
409        self.position = PackedOption::from(block);
410    }
411
412    /// Declares that all the predecessors of this block are known.
413    ///
414    /// Function to call with `block` as soon as the last branch instruction to `block` has been
415    /// created. Forgetting to call this method on every block will cause inconsistencies in the
416    /// produced functions.
417    pub fn seal_block(&mut self, block: Block) {
418        let side_effects = self.func_ctx.ssa.seal_block(block, self.func);
419        self.handle_ssa_side_effects(side_effects);
420    }
421
422    /// Effectively calls [seal_block](Self::seal_block) on all unsealed blocks in the function.
423    ///
424    /// It's more efficient to seal [`Block`]s as soon as possible, during
425    /// translation, but for frontends where this is impractical to do, this
426    /// function can be used at the end of translating all blocks to ensure
427    /// that everything is sealed.
428    pub fn seal_all_blocks(&mut self) {
429        let side_effects = self.func_ctx.ssa.seal_all_blocks(self.func);
430        self.handle_ssa_side_effects(side_effects);
431    }
432
433    /// Declares the type of a variable.
434    ///
435    /// This allows the variable to be used later (by calling
436    /// [`FunctionBuilder::use_var`]).
437    ///
438    /// # Errors
439    ///
440    /// This function will return an error if the variable has been previously
441    /// declared.
442    pub fn try_declare_var(&mut self, var: Variable, ty: Type) -> Result<(), DeclareVariableError> {
443        if self.func_ctx.types[var] != types::INVALID {
444            return Err(DeclareVariableError::DeclaredMultipleTimes(var));
445        }
446        self.func_ctx.types[var] = ty;
447        Ok(())
448    }
449
450    /// Declares the type of a variable, panicking if it is already declared.
451    ///
452    /// # Panics
453    ///
454    /// Panics if the variable has already been declared.
455    pub fn declare_var(&mut self, var: Variable, ty: Type) {
456        self.try_declare_var(var, ty)
457            .unwrap_or_else(|_| panic!("the variable {var:?} has been declared multiple times"))
458    }
459
460    /// Declare that all uses of the given variable must be included in stack
461    /// map metadata.
462    ///
463    /// All values that are uses of this variable will be spilled to the stack
464    /// before each safepoint and their location on the stack included in stack
465    /// maps. Stack maps allow the garbage collector to identify the on-stack GC
466    /// roots.
467    ///
468    /// This does not affect any pre-existing uses of the variable.
469    ///
470    /// # Panics
471    ///
472    /// Panics if the variable's type is larger than 16 bytes or if this
473    /// variable has not been declared yet.
474    pub fn declare_var_needs_stack_map(&mut self, var: Variable) {
475        log::trace!("declare_var_needs_stack_map({var:?})");
476        let ty = self.func_ctx.types[var];
477        assert!(ty != types::INVALID);
478        assert!(ty.bytes() <= 16);
479        self.func_ctx.stack_map_vars.insert(var);
480    }
481
482    /// Returns the Cranelift IR necessary to use a previously defined user
483    /// variable, returning an error if this is not possible.
484    pub fn try_use_var(&mut self, var: Variable) -> Result<Value, UseVariableError> {
485        // Assert that we're about to add instructions to this block using the definition of the
486        // given variable. ssa.use_var is the only part of this crate which can add block parameters
487        // behind the caller's back. If we disallow calling append_block_param as soon as use_var is
488        // called, then we enforce a strict separation between user parameters and SSA parameters.
489        self.ensure_inserted_block();
490
491        let (val, side_effects) = {
492            let ty = *self
493                .func_ctx
494                .types
495                .get(var)
496                .ok_or(UseVariableError::UsedBeforeDeclared(var))?;
497            debug_assert_ne!(
498                ty,
499                types::INVALID,
500                "variable {var:?} is used but its type has not been declared"
501            );
502            self.func_ctx
503                .ssa
504                .use_var(self.func, var, ty, self.position.unwrap())
505        };
506        self.handle_ssa_side_effects(side_effects);
507
508        Ok(val)
509    }
510
511    /// Returns the Cranelift IR value corresponding to the utilization at the current program
512    /// position of a previously defined user variable.
513    pub fn use_var(&mut self, var: Variable) -> Value {
514        self.try_use_var(var).unwrap_or_else(|_| {
515            panic!("variable {var:?} is used but its type has not been declared")
516        })
517    }
518
519    /// Registers a new definition of a user variable. This function will return
520    /// an error if the value supplied does not match the type the variable was
521    /// declared to have.
522    pub fn try_def_var(&mut self, var: Variable, val: Value) -> Result<(), DefVariableError> {
523        log::trace!("try_def_var: {var:?} = {val:?}");
524
525        let var_ty = *self
526            .func_ctx
527            .types
528            .get(var)
529            .ok_or(DefVariableError::DefinedBeforeDeclared(var))?;
530        if var_ty != self.func.dfg.value_type(val) {
531            return Err(DefVariableError::TypeMismatch(var, val));
532        }
533
534        self.func_ctx.ssa.def_var(var, val, self.position.unwrap());
535        Ok(())
536    }
537
538    /// Register a new definition of a user variable. The type of the value must be
539    /// the same as the type registered for the variable.
540    pub fn def_var(&mut self, var: Variable, val: Value) {
541        self.try_def_var(var, val)
542            .unwrap_or_else(|error| match error {
543                DefVariableError::TypeMismatch(var, val) => {
544                    panic!("declared type of variable {var:?} doesn't match type of value {val}");
545                }
546                DefVariableError::DefinedBeforeDeclared(var) => {
547                    panic!("variable {var:?} is used but its type has not been declared");
548                }
549            })
550    }
551
552    /// Set label for [`Value`]
553    ///
554    /// This will not do anything unless
555    /// [`func.dfg.collect_debug_info`](DataFlowGraph::collect_debug_info) is called first.
556    pub fn set_val_label(&mut self, val: Value, label: ValueLabel) {
557        if let Some(values_labels) = self.func.stencil.dfg.values_labels.as_mut() {
558            use alloc::collections::btree_map::Entry;
559
560            let start = ValueLabelStart {
561                from: RelSourceLoc::from_base_offset(self.func.params.base_srcloc(), self.srcloc),
562                label,
563            };
564
565            match values_labels.entry(val) {
566                Entry::Occupied(mut e) => match e.get_mut() {
567                    ValueLabelAssignments::Starts(starts) => starts.push(start),
568                    _ => panic!("Unexpected ValueLabelAssignments at this stage"),
569                },
570                Entry::Vacant(e) => {
571                    e.insert(ValueLabelAssignments::Starts(vec![start]));
572                }
573            }
574        }
575    }
576
577    /// Declare that the given value is a GC reference that requires inclusion
578    /// in a stack map when it is live across GC safepoints.
579    ///
580    /// At the current moment, values that need inclusion in stack maps are
581    /// spilled before safepoints, but they are not reloaded afterwards. This
582    /// means that moving GCs are not yet supported, however the intention is to
583    /// add this support in the near future.
584    ///
585    /// # Panics
586    ///
587    /// Panics if `val` is larger than 16 bytes.
588    pub fn declare_value_needs_stack_map(&mut self, val: Value) {
589        log::trace!("declare_value_needs_stack_map({val:?})");
590
591        // We rely on these properties in `insert_safepoint_spills`.
592        let size = self.func.dfg.value_type(val).bytes();
593        assert!(size <= 16);
594        assert!(size.is_power_of_two());
595
596        self.func_ctx.stack_map_values.insert(val);
597    }
598
599    /// Creates a jump table in the function, to be used by [`br_table`](InstBuilder::br_table) instructions.
600    pub fn create_jump_table(&mut self, data: JumpTableData) -> JumpTable {
601        self.func.create_jump_table(data)
602    }
603
604    /// Creates a sized stack slot in the function, to be used by [`stack_load`](InstBuilder::stack_load),
605    /// [`stack_store`](InstBuilder::stack_store) and [`stack_addr`](InstBuilder::stack_addr) instructions.
606    pub fn create_sized_stack_slot(&mut self, data: StackSlotData) -> StackSlot {
607        self.func.create_sized_stack_slot(data)
608    }
609
610    /// Creates a dynamic stack slot in the function, to be used by
611    /// [`dynamic_stack_load`](InstBuilder::dynamic_stack_load),
612    /// [`dynamic_stack_store`](InstBuilder::dynamic_stack_store) and
613    /// [`dynamic_stack_addr`](InstBuilder::dynamic_stack_addr) instructions.
614    pub fn create_dynamic_stack_slot(&mut self, data: DynamicStackSlotData) -> DynamicStackSlot {
615        self.func.create_dynamic_stack_slot(data)
616    }
617
618    /// Adds a signature which can later be used to declare an external function import.
619    pub fn import_signature(&mut self, signature: Signature) -> SigRef {
620        self.func.import_signature(signature)
621    }
622
623    /// Declare an external function import.
624    pub fn import_function(&mut self, data: ExtFuncData) -> FuncRef {
625        self.func.import_function(data)
626    }
627
628    /// Declares a global value accessible to the function.
629    pub fn create_global_value(&mut self, data: GlobalValueData) -> GlobalValue {
630        self.func.create_global_value(data)
631    }
632
633    /// Returns an object with the [`InstBuilder`]
634    /// trait that allows to conveniently append an instruction to the current [`Block`] being built.
635    pub fn ins<'short>(&'short mut self) -> FuncInstBuilder<'short, 'a> {
636        let block = self
637            .position
638            .expect("Please call switch_to_block before inserting instructions");
639        FuncInstBuilder::new(self, block)
640    }
641
642    /// Make sure that the current block is inserted in the layout.
643    pub fn ensure_inserted_block(&mut self) {
644        let block = self.position.unwrap();
645        if self.is_pristine(block) {
646            if !self.func.layout.is_block_inserted(block) {
647                self.func.layout.append_block(block);
648            }
649            self.func_ctx.status[block] = BlockStatus::Partial;
650        } else {
651            debug_assert!(
652                !self.is_filled(block),
653                "you cannot add an instruction to a block already filled"
654            );
655        }
656    }
657
658    /// Returns a [`FuncCursor`] pointed at the current position ready for inserting instructions.
659    ///
660    /// This can be used to insert SSA code that doesn't need to access locals and that doesn't
661    /// need to know about [`FunctionBuilder`] at all.
662    pub fn cursor(&mut self) -> FuncCursor {
663        self.ensure_inserted_block();
664        FuncCursor::new(self.func)
665            .with_srcloc(self.srcloc)
666            .at_bottom(self.position.unwrap())
667    }
668
669    /// Append parameters to the given [`Block`] corresponding to the function
670    /// parameters. This can be used to set up the block parameters for the
671    /// entry block.
672    pub fn append_block_params_for_function_params(&mut self, block: Block) {
673        debug_assert!(
674            !self.func_ctx.ssa.has_any_predecessors(block),
675            "block parameters for function parameters should only be added to the entry block"
676        );
677
678        // These parameters count as "user" parameters here because they aren't
679        // inserted by the SSABuilder.
680        debug_assert!(
681            self.is_pristine(block),
682            "You can't add block parameters after adding any instruction"
683        );
684
685        for argtyp in &self.func.stencil.signature.params {
686            self.func
687                .stencil
688                .dfg
689                .append_block_param(block, argtyp.value_type);
690        }
691    }
692
693    /// Append parameters to the given [`Block`] corresponding to the function
694    /// return values. This can be used to set up the block parameters for a
695    /// function exit block.
696    pub fn append_block_params_for_function_returns(&mut self, block: Block) {
697        // These parameters count as "user" parameters here because they aren't
698        // inserted by the SSABuilder.
699        debug_assert!(
700            self.is_pristine(block),
701            "You can't add block parameters after adding any instruction"
702        );
703
704        for argtyp in &self.func.stencil.signature.returns {
705            self.func
706                .stencil
707                .dfg
708                .append_block_param(block, argtyp.value_type);
709        }
710    }
711
712    /// Declare that translation of the current function is complete.
713    ///
714    /// This resets the state of the [`FunctionBuilderContext`] in preparation to
715    /// be used for another function.
716    pub fn finalize(mut self) {
717        // Check that all the `Block`s are filled and sealed.
718        #[cfg(debug_assertions)]
719        {
720            for block in self.func_ctx.status.keys() {
721                if !self.is_pristine(block) {
722                    assert!(
723                        self.func_ctx.ssa.is_sealed(block),
724                        "FunctionBuilder finalized, but block {block} is not sealed",
725                    );
726                    assert!(
727                        self.is_filled(block),
728                        "FunctionBuilder finalized, but block {block} is not filled",
729                    );
730                }
731            }
732        }
733
734        // In debug mode, check that all blocks are valid basic blocks.
735        #[cfg(debug_assertions)]
736        {
737            // Iterate manually to provide more helpful error messages.
738            for block in self.func_ctx.status.keys() {
739                if let Err((inst, msg)) = self.func.is_block_basic(block) {
740                    let inst_str = self.func.dfg.display_inst(inst);
741                    panic!("{block} failed basic block invariants on {inst_str}: {msg}");
742                }
743            }
744        }
745
746        // Propagate the needs-stack-map bit from variables to each of their
747        // associated values.
748        for var in self.func_ctx.stack_map_vars.iter() {
749            for val in self.func_ctx.ssa.values_for_var(var) {
750                log::trace!("propagating needs-stack-map from {var:?} to {val:?}");
751                debug_assert_eq!(self.func.dfg.value_type(val), self.func_ctx.types[var]);
752                self.func_ctx.stack_map_values.insert(val);
753            }
754        }
755
756        // If we have any values that need inclusion in stack maps, then we need
757        // to run our pass to spill those values to the stack at safepoints and
758        // generate stack maps.
759        if !self.func_ctx.stack_map_values.is_empty() {
760            self.func_ctx
761                .safepoints
762                .run(&mut self.func, &self.func_ctx.stack_map_values);
763        }
764
765        // Clear the state (but preserve the allocated buffers) in preparation
766        // for translation another function.
767        self.func_ctx.clear();
768    }
769}
770
771/// All the functions documented in the previous block are write-only and help you build a valid
772/// Cranelift IR functions via multiple debug asserts. However, you might need to improve the
773/// performance of your translation perform more complex transformations to your Cranelift IR
774/// function. The functions below help you inspect the function you're creating and modify it
775/// in ways that can be unsafe if used incorrectly.
776impl<'a> FunctionBuilder<'a> {
777    /// Retrieves all the parameters for a [`Block`] currently inferred from the jump instructions
778    /// inserted that target it and the SSA construction.
779    pub fn block_params(&self, block: Block) -> &[Value] {
780        self.func.dfg.block_params(block)
781    }
782
783    /// Retrieves the signature with reference `sigref` previously added with
784    /// [`import_signature`](Self::import_signature).
785    pub fn signature(&self, sigref: SigRef) -> Option<&Signature> {
786        self.func.dfg.signatures.get(sigref)
787    }
788
789    /// Creates a parameter for a specific [`Block`] by appending it to the list of already existing
790    /// parameters.
791    ///
792    /// **Note:** this function has to be called at the creation of the `Block` before adding
793    /// instructions to it, otherwise this could interfere with SSA construction.
794    pub fn append_block_param(&mut self, block: Block, ty: Type) -> Value {
795        debug_assert!(
796            self.is_pristine(block),
797            "You can't add block parameters after adding any instruction"
798        );
799        self.func.dfg.append_block_param(block, ty)
800    }
801
802    /// Returns the result values of an instruction.
803    pub fn inst_results(&self, inst: Inst) -> &[Value] {
804        self.func.dfg.inst_results(inst)
805    }
806
807    /// Changes the destination of a jump instruction after creation.
808    ///
809    /// **Note:** You are responsible for maintaining the coherence with the arguments of
810    /// other jump instructions.
811    pub fn change_jump_destination(&mut self, inst: Inst, old_block: Block, new_block: Block) {
812        let dfg = &mut self.func.dfg;
813        for block in
814            dfg.insts[inst].branch_destination_mut(&mut dfg.jump_tables, &mut dfg.exception_tables)
815        {
816            if block.block(&dfg.value_lists) == old_block {
817                self.func_ctx.ssa.remove_block_predecessor(old_block, inst);
818                block.set_block(new_block, &mut dfg.value_lists);
819                self.func_ctx.ssa.declare_block_predecessor(new_block, inst);
820            }
821        }
822    }
823
824    /// Returns `true` if and only if the current [`Block`] is sealed and has no predecessors declared.
825    ///
826    /// The entry block of a function is never unreachable.
827    pub fn is_unreachable(&self) -> bool {
828        let is_entry = match self.func.layout.entry_block() {
829            None => false,
830            Some(entry) => self.position.unwrap() == entry,
831        };
832        !is_entry
833            && self.func_ctx.ssa.is_sealed(self.position.unwrap())
834            && !self
835                .func_ctx
836                .ssa
837                .has_any_predecessors(self.position.unwrap())
838    }
839
840    /// Returns `true` if and only if no instructions have been added since the last call to
841    /// [`switch_to_block`](Self::switch_to_block).
842    fn is_pristine(&self, block: Block) -> bool {
843        self.func_ctx.status[block] == BlockStatus::Empty
844    }
845
846    /// Returns `true` if and only if a terminator instruction has been inserted since the
847    /// last call to [`switch_to_block`](Self::switch_to_block).
848    fn is_filled(&self, block: Block) -> bool {
849        self.func_ctx.status[block] == BlockStatus::Filled
850    }
851}
852
853/// Helper functions
854impl<'a> FunctionBuilder<'a> {
855    /// Calls libc.memcpy
856    ///
857    /// Copies the `size` bytes from `src` to `dest`, assumes that `src + size`
858    /// won't overlap onto `dest`. If `dest` and `src` overlap, the behavior is
859    /// undefined. Applications in which `dest` and `src` might overlap should
860    /// use `call_memmove` instead.
861    pub fn call_memcpy(
862        &mut self,
863        config: TargetFrontendConfig,
864        dest: Value,
865        src: Value,
866        size: Value,
867    ) {
868        let pointer_type = config.pointer_type();
869        let signature = {
870            let mut s = Signature::new(config.default_call_conv);
871            s.params.push(AbiParam::new(pointer_type));
872            s.params.push(AbiParam::new(pointer_type));
873            s.params.push(AbiParam::new(pointer_type));
874            s.returns.push(AbiParam::new(pointer_type));
875            self.import_signature(s)
876        };
877
878        let libc_memcpy = self.import_function(ExtFuncData {
879            name: ExternalName::LibCall(LibCall::Memcpy),
880            signature,
881            colocated: false,
882        });
883
884        self.ins().call(libc_memcpy, &[dest, src, size]);
885    }
886
887    /// Optimised memcpy or memmove for small copies.
888    ///
889    /// # Codegen safety
890    ///
891    /// The following properties must hold to prevent UB:
892    ///
893    /// * `src_align` and `dest_align` are an upper-bound on the alignment of `src` respectively `dest`.
894    /// * If `non_overlapping` is true, then this must be correct.
895    pub fn emit_small_memory_copy(
896        &mut self,
897        config: TargetFrontendConfig,
898        dest: Value,
899        src: Value,
900        size: u64,
901        dest_align: u8,
902        src_align: u8,
903        non_overlapping: bool,
904        mut flags: MemFlags,
905    ) {
906        // Currently the result of guess work, not actual profiling.
907        const THRESHOLD: u64 = 4;
908
909        if size == 0 {
910            return;
911        }
912
913        let access_size = greatest_divisible_power_of_two(size);
914        assert!(
915            access_size.is_power_of_two(),
916            "`size` is not a power of two"
917        );
918        assert!(
919            access_size >= u64::from(::core::cmp::min(src_align, dest_align)),
920            "`size` is smaller than `dest` and `src`'s alignment value."
921        );
922
923        let (access_size, int_type) = if access_size <= 8 {
924            (access_size, Type::int((access_size * 8) as u16).unwrap())
925        } else {
926            (8, types::I64)
927        };
928
929        let load_and_store_amount = size / access_size;
930
931        if load_and_store_amount > THRESHOLD {
932            let size_value = self.ins().iconst(config.pointer_type(), size as i64);
933            if non_overlapping {
934                self.call_memcpy(config, dest, src, size_value);
935            } else {
936                self.call_memmove(config, dest, src, size_value);
937            }
938            return;
939        }
940
941        if u64::from(src_align) >= access_size && u64::from(dest_align) >= access_size {
942            flags.set_aligned();
943        }
944
945        // Load all of the memory first. This is necessary in case `dest` overlaps.
946        // It can also improve performance a bit.
947        let registers: smallvec::SmallVec<[_; THRESHOLD as usize]> = (0..load_and_store_amount)
948            .map(|i| {
949                let offset = (access_size * i) as i32;
950                (self.ins().load(int_type, flags, src, offset), offset)
951            })
952            .collect();
953
954        for (value, offset) in registers {
955            self.ins().store(flags, value, dest, offset);
956        }
957    }
958
959    /// Calls libc.memset
960    ///
961    /// Writes `size` bytes of i8 value `ch` to memory starting at `buffer`.
962    pub fn call_memset(
963        &mut self,
964        config: TargetFrontendConfig,
965        buffer: Value,
966        ch: Value,
967        size: Value,
968    ) {
969        let pointer_type = config.pointer_type();
970        let signature = {
971            let mut s = Signature::new(config.default_call_conv);
972            s.params.push(AbiParam::new(pointer_type));
973            s.params.push(AbiParam::new(types::I32));
974            s.params.push(AbiParam::new(pointer_type));
975            s.returns.push(AbiParam::new(pointer_type));
976            self.import_signature(s)
977        };
978
979        let libc_memset = self.import_function(ExtFuncData {
980            name: ExternalName::LibCall(LibCall::Memset),
981            signature,
982            colocated: false,
983        });
984
985        let ch = self.ins().uextend(types::I32, ch);
986        self.ins().call(libc_memset, &[buffer, ch, size]);
987    }
988
989    /// Calls libc.memset
990    ///
991    /// Writes `size` bytes of value `ch` to memory starting at `buffer`.
992    pub fn emit_small_memset(
993        &mut self,
994        config: TargetFrontendConfig,
995        buffer: Value,
996        ch: u8,
997        size: u64,
998        buffer_align: u8,
999        mut flags: MemFlags,
1000    ) {
1001        // Currently the result of guess work, not actual profiling.
1002        const THRESHOLD: u64 = 4;
1003
1004        if size == 0 {
1005            return;
1006        }
1007
1008        let access_size = greatest_divisible_power_of_two(size);
1009        assert!(
1010            access_size.is_power_of_two(),
1011            "`size` is not a power of two"
1012        );
1013        assert!(
1014            access_size >= u64::from(buffer_align),
1015            "`size` is smaller than `dest` and `src`'s alignment value."
1016        );
1017
1018        let (access_size, int_type) = if access_size <= 8 {
1019            (access_size, Type::int((access_size * 8) as u16).unwrap())
1020        } else {
1021            (8, types::I64)
1022        };
1023
1024        let load_and_store_amount = size / access_size;
1025
1026        if load_and_store_amount > THRESHOLD {
1027            let ch = self.ins().iconst(types::I8, i64::from(ch));
1028            let size = self.ins().iconst(config.pointer_type(), size as i64);
1029            self.call_memset(config, buffer, ch, size);
1030        } else {
1031            if u64::from(buffer_align) >= access_size {
1032                flags.set_aligned();
1033            }
1034
1035            let ch = u64::from(ch);
1036            let raw_value = if int_type == types::I64 {
1037                ch * 0x0101010101010101_u64
1038            } else if int_type == types::I32 {
1039                ch * 0x01010101_u64
1040            } else if int_type == types::I16 {
1041                (ch << 8) | ch
1042            } else {
1043                assert_eq!(int_type, types::I8);
1044                ch
1045            };
1046
1047            let value = self.ins().iconst(int_type, raw_value as i64);
1048            for i in 0..load_and_store_amount {
1049                let offset = (access_size * i) as i32;
1050                self.ins().store(flags, value, buffer, offset);
1051            }
1052        }
1053    }
1054
1055    /// Calls libc.memmove
1056    ///
1057    /// Copies `size` bytes from memory starting at `source` to memory starting
1058    /// at `dest`. `source` is always read before writing to `dest`.
1059    pub fn call_memmove(
1060        &mut self,
1061        config: TargetFrontendConfig,
1062        dest: Value,
1063        source: Value,
1064        size: Value,
1065    ) {
1066        let pointer_type = config.pointer_type();
1067        let signature = {
1068            let mut s = Signature::new(config.default_call_conv);
1069            s.params.push(AbiParam::new(pointer_type));
1070            s.params.push(AbiParam::new(pointer_type));
1071            s.params.push(AbiParam::new(pointer_type));
1072            s.returns.push(AbiParam::new(pointer_type));
1073            self.import_signature(s)
1074        };
1075
1076        let libc_memmove = self.import_function(ExtFuncData {
1077            name: ExternalName::LibCall(LibCall::Memmove),
1078            signature,
1079            colocated: false,
1080        });
1081
1082        self.ins().call(libc_memmove, &[dest, source, size]);
1083    }
1084
1085    /// Calls libc.memcmp
1086    ///
1087    /// Compares `size` bytes from memory starting at `left` to memory starting
1088    /// at `right`. Returns `0` if all `n` bytes are equal.  If the first difference
1089    /// is at offset `i`, returns a positive integer if `ugt(left[i], right[i])`
1090    /// and a negative integer if `ult(left[i], right[i])`.
1091    ///
1092    /// Returns a C `int`, which is currently always [`types::I32`].
1093    pub fn call_memcmp(
1094        &mut self,
1095        config: TargetFrontendConfig,
1096        left: Value,
1097        right: Value,
1098        size: Value,
1099    ) -> Value {
1100        let pointer_type = config.pointer_type();
1101        let signature = {
1102            let mut s = Signature::new(config.default_call_conv);
1103            s.params.reserve(3);
1104            s.params.push(AbiParam::new(pointer_type));
1105            s.params.push(AbiParam::new(pointer_type));
1106            s.params.push(AbiParam::new(pointer_type));
1107            s.returns.push(AbiParam::new(types::I32));
1108            self.import_signature(s)
1109        };
1110
1111        let libc_memcmp = self.import_function(ExtFuncData {
1112            name: ExternalName::LibCall(LibCall::Memcmp),
1113            signature,
1114            colocated: false,
1115        });
1116
1117        let call = self.ins().call(libc_memcmp, &[left, right, size]);
1118        self.func.dfg.first_result(call)
1119    }
1120
1121    /// Optimised [`Self::call_memcmp`] for small copies.
1122    ///
1123    /// This implements the byte slice comparison `int_cc(left[..size], right[..size])`.
1124    ///
1125    /// `left_align` and `right_align` are the statically-known alignments of the
1126    /// `left` and `right` pointers respectively.  These are used to know whether
1127    /// to mark `load`s as aligned.  It's always fine to pass `1` for these, but
1128    /// passing something higher than the true alignment may trap or otherwise
1129    /// misbehave as described in [`MemFlags::aligned`].
1130    ///
1131    /// Note that `memcmp` is a *big-endian* and *unsigned* comparison.
1132    /// As such, this panics when called with `IntCC::Signed*`.
1133    pub fn emit_small_memory_compare(
1134        &mut self,
1135        config: TargetFrontendConfig,
1136        int_cc: IntCC,
1137        left: Value,
1138        right: Value,
1139        size: u64,
1140        left_align: std::num::NonZeroU8,
1141        right_align: std::num::NonZeroU8,
1142        flags: MemFlags,
1143    ) -> Value {
1144        use IntCC::*;
1145        let (zero_cc, empty_imm) = match int_cc {
1146            //
1147            Equal => (Equal, 1),
1148            NotEqual => (NotEqual, 0),
1149
1150            UnsignedLessThan => (SignedLessThan, 0),
1151            UnsignedGreaterThanOrEqual => (SignedGreaterThanOrEqual, 1),
1152            UnsignedGreaterThan => (SignedGreaterThan, 0),
1153            UnsignedLessThanOrEqual => (SignedLessThanOrEqual, 1),
1154
1155            SignedLessThan
1156            | SignedGreaterThanOrEqual
1157            | SignedGreaterThan
1158            | SignedLessThanOrEqual => {
1159                panic!("Signed comparison {int_cc} not supported by memcmp")
1160            }
1161        };
1162
1163        if size == 0 {
1164            return self.ins().iconst(types::I8, empty_imm);
1165        }
1166
1167        // Future work could consider expanding this to handle more-complex scenarios.
1168        if let Some(small_type) = size.try_into().ok().and_then(Type::int_with_byte_size) {
1169            if let Equal | NotEqual = zero_cc {
1170                let mut left_flags = flags;
1171                if size == left_align.get() as u64 {
1172                    left_flags.set_aligned();
1173                }
1174                let mut right_flags = flags;
1175                if size == right_align.get() as u64 {
1176                    right_flags.set_aligned();
1177                }
1178                let left_val = self.ins().load(small_type, left_flags, left, 0);
1179                let right_val = self.ins().load(small_type, right_flags, right, 0);
1180                return self.ins().icmp(int_cc, left_val, right_val);
1181            } else if small_type == types::I8 {
1182                // Once the big-endian loads from wasmtime#2492 are implemented in
1183                // the backends, we could easily handle comparisons for more sizes here.
1184                // But for now, just handle single bytes where we don't need to worry.
1185
1186                let mut aligned_flags = flags;
1187                aligned_flags.set_aligned();
1188                let left_val = self.ins().load(small_type, aligned_flags, left, 0);
1189                let right_val = self.ins().load(small_type, aligned_flags, right, 0);
1190                return self.ins().icmp(int_cc, left_val, right_val);
1191            }
1192        }
1193
1194        let pointer_type = config.pointer_type();
1195        let size = self.ins().iconst(pointer_type, size as i64);
1196        let cmp = self.call_memcmp(config, left, right, size);
1197        self.ins().icmp_imm(zero_cc, cmp, 0)
1198    }
1199}
1200
1201fn greatest_divisible_power_of_two(size: u64) -> u64 {
1202    (size as i64 & -(size as i64)) as u64
1203}
1204
1205// Helper functions
1206impl<'a> FunctionBuilder<'a> {
1207    /// A Block is 'filled' when a terminator instruction is present.
1208    fn fill_current_block(&mut self) {
1209        self.func_ctx.status[self.position.unwrap()] = BlockStatus::Filled;
1210    }
1211
1212    fn declare_successor(&mut self, dest_block: Block, jump_inst: Inst) {
1213        self.func_ctx
1214            .ssa
1215            .declare_block_predecessor(dest_block, jump_inst);
1216    }
1217
1218    fn handle_ssa_side_effects(&mut self, side_effects: SideEffects) {
1219        let SideEffects {
1220            instructions_added_to_blocks,
1221        } = side_effects;
1222
1223        for modified_block in instructions_added_to_blocks {
1224            if self.is_pristine(modified_block) {
1225                self.func_ctx.status[modified_block] = BlockStatus::Partial;
1226            }
1227        }
1228    }
1229}
1230
1231#[cfg(test)]
1232mod tests {
1233    use super::greatest_divisible_power_of_two;
1234    use crate::frontend::{
1235        DeclareVariableError, DefVariableError, FunctionBuilder, FunctionBuilderContext,
1236        UseVariableError,
1237    };
1238    use crate::Variable;
1239    use alloc::string::ToString;
1240    use cranelift_codegen::entity::EntityRef;
1241    use cranelift_codegen::ir::condcodes::IntCC;
1242    use cranelift_codegen::ir::{
1243        types::*, AbiParam, BlockCall, ExceptionTableData, ExtFuncData, ExternalName, Function,
1244        InstBuilder, MemFlags, Signature, UserExternalName, UserFuncName, Value,
1245    };
1246    use cranelift_codegen::isa::{CallConv, TargetFrontendConfig, TargetIsa};
1247    use cranelift_codegen::settings;
1248    use cranelift_codegen::verifier::verify_function;
1249    use target_lexicon::PointerWidth;
1250
1251    fn sample_function(lazy_seal: bool) {
1252        let mut sig = Signature::new(CallConv::SystemV);
1253        sig.returns.push(AbiParam::new(I32));
1254        sig.params.push(AbiParam::new(I32));
1255
1256        let mut fn_ctx = FunctionBuilderContext::new();
1257        let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1258        {
1259            let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1260
1261            let block0 = builder.create_block();
1262            let block1 = builder.create_block();
1263            let block2 = builder.create_block();
1264            let block3 = builder.create_block();
1265            let x = Variable::new(0);
1266            let y = Variable::new(1);
1267            let z = Variable::new(2);
1268            builder.declare_var(x, I32);
1269            builder.declare_var(y, I32);
1270            builder.declare_var(z, I32);
1271            builder.append_block_params_for_function_params(block0);
1272
1273            builder.switch_to_block(block0);
1274            if !lazy_seal {
1275                builder.seal_block(block0);
1276            }
1277            {
1278                let tmp = builder.block_params(block0)[0]; // the first function parameter
1279                builder.def_var(x, tmp);
1280            }
1281            {
1282                let tmp = builder.ins().iconst(I32, 2);
1283                builder.def_var(y, tmp);
1284            }
1285            {
1286                let arg1 = builder.use_var(x);
1287                let arg2 = builder.use_var(y);
1288                let tmp = builder.ins().iadd(arg1, arg2);
1289                builder.def_var(z, tmp);
1290            }
1291            builder.ins().jump(block1, &[]);
1292
1293            builder.switch_to_block(block1);
1294            {
1295                let arg1 = builder.use_var(y);
1296                let arg2 = builder.use_var(z);
1297                let tmp = builder.ins().iadd(arg1, arg2);
1298                builder.def_var(z, tmp);
1299            }
1300            {
1301                let arg = builder.use_var(y);
1302                builder.ins().brif(arg, block3, &[], block2, &[]);
1303            }
1304
1305            builder.switch_to_block(block2);
1306            if !lazy_seal {
1307                builder.seal_block(block2);
1308            }
1309            {
1310                let arg1 = builder.use_var(z);
1311                let arg2 = builder.use_var(x);
1312                let tmp = builder.ins().isub(arg1, arg2);
1313                builder.def_var(z, tmp);
1314            }
1315            {
1316                let arg = builder.use_var(y);
1317                builder.ins().return_(&[arg]);
1318            }
1319
1320            builder.switch_to_block(block3);
1321            if !lazy_seal {
1322                builder.seal_block(block3);
1323            }
1324
1325            {
1326                let arg1 = builder.use_var(y);
1327                let arg2 = builder.use_var(x);
1328                let tmp = builder.ins().isub(arg1, arg2);
1329                builder.def_var(y, tmp);
1330            }
1331            builder.ins().jump(block1, &[]);
1332            if !lazy_seal {
1333                builder.seal_block(block1);
1334            }
1335
1336            if lazy_seal {
1337                builder.seal_all_blocks();
1338            }
1339
1340            builder.finalize();
1341        }
1342
1343        let flags = settings::Flags::new(settings::builder());
1344        // println!("{}", func.display(None));
1345        if let Err(errors) = verify_function(&func, &flags) {
1346            panic!("{}\n{}", func.display(), errors)
1347        }
1348    }
1349
1350    #[test]
1351    fn sample() {
1352        sample_function(false)
1353    }
1354
1355    #[test]
1356    fn sample_with_lazy_seal() {
1357        sample_function(true)
1358    }
1359
1360    #[track_caller]
1361    fn check(func: &Function, expected_ir: &str) {
1362        let expected_ir = expected_ir.trim();
1363        let actual_ir = func.display().to_string();
1364        let actual_ir = actual_ir.trim();
1365        assert!(
1366            expected_ir == actual_ir,
1367            "Expected:\n{expected_ir}\nGot:\n{actual_ir}"
1368        );
1369    }
1370
1371    /// Helper function to construct a fixed frontend configuration.
1372    fn systemv_frontend_config() -> TargetFrontendConfig {
1373        TargetFrontendConfig {
1374            default_call_conv: CallConv::SystemV,
1375            pointer_width: PointerWidth::U64,
1376            page_size_align_log2: 12,
1377        }
1378    }
1379
1380    #[test]
1381    fn memcpy() {
1382        let frontend_config = systemv_frontend_config();
1383        let mut sig = Signature::new(frontend_config.default_call_conv);
1384        sig.returns.push(AbiParam::new(I32));
1385
1386        let mut fn_ctx = FunctionBuilderContext::new();
1387        let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1388        {
1389            let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1390
1391            let block0 = builder.create_block();
1392            let x = Variable::new(0);
1393            let y = Variable::new(1);
1394            let z = Variable::new(2);
1395            builder.declare_var(x, frontend_config.pointer_type());
1396            builder.declare_var(y, frontend_config.pointer_type());
1397            builder.declare_var(z, I32);
1398            builder.append_block_params_for_function_params(block0);
1399            builder.switch_to_block(block0);
1400
1401            let src = builder.use_var(x);
1402            let dest = builder.use_var(y);
1403            let size = builder.use_var(y);
1404            builder.call_memcpy(frontend_config, dest, src, size);
1405            builder.ins().return_(&[size]);
1406
1407            builder.seal_all_blocks();
1408            builder.finalize();
1409        }
1410
1411        check(
1412            &func,
1413            "function %sample() -> i32 system_v {
1414    sig0 = (i64, i64, i64) -> i64 system_v
1415    fn0 = %Memcpy sig0
1416
1417block0:
1418    v4 = iconst.i64 0
1419    v1 -> v4
1420    v3 = iconst.i64 0
1421    v0 -> v3
1422    v2 = call fn0(v1, v0, v1)  ; v1 = 0, v0 = 0, v1 = 0
1423    return v1  ; v1 = 0
1424}
1425",
1426        );
1427    }
1428
1429    #[test]
1430    fn small_memcpy() {
1431        let frontend_config = systemv_frontend_config();
1432        let mut sig = Signature::new(frontend_config.default_call_conv);
1433        sig.returns.push(AbiParam::new(I32));
1434
1435        let mut fn_ctx = FunctionBuilderContext::new();
1436        let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1437        {
1438            let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1439
1440            let block0 = builder.create_block();
1441            let x = Variable::new(0);
1442            let y = Variable::new(16);
1443            builder.declare_var(x, frontend_config.pointer_type());
1444            builder.declare_var(y, frontend_config.pointer_type());
1445            builder.append_block_params_for_function_params(block0);
1446            builder.switch_to_block(block0);
1447
1448            let src = builder.use_var(x);
1449            let dest = builder.use_var(y);
1450            let size = 8;
1451            builder.emit_small_memory_copy(
1452                frontend_config,
1453                dest,
1454                src,
1455                size,
1456                8,
1457                8,
1458                true,
1459                MemFlags::new(),
1460            );
1461            builder.ins().return_(&[dest]);
1462
1463            builder.seal_all_blocks();
1464            builder.finalize();
1465        }
1466
1467        check(
1468            &func,
1469            "function %sample() -> i32 system_v {
1470block0:
1471    v4 = iconst.i64 0
1472    v1 -> v4
1473    v3 = iconst.i64 0
1474    v0 -> v3
1475    v2 = load.i64 aligned v0  ; v0 = 0
1476    store aligned v2, v1  ; v1 = 0
1477    return v1  ; v1 = 0
1478}
1479",
1480        );
1481    }
1482
1483    #[test]
1484    fn not_so_small_memcpy() {
1485        let frontend_config = systemv_frontend_config();
1486        let mut sig = Signature::new(frontend_config.default_call_conv);
1487        sig.returns.push(AbiParam::new(I32));
1488
1489        let mut fn_ctx = FunctionBuilderContext::new();
1490        let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1491        {
1492            let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1493
1494            let block0 = builder.create_block();
1495            let x = Variable::new(0);
1496            let y = Variable::new(16);
1497            builder.declare_var(x, frontend_config.pointer_type());
1498            builder.declare_var(y, frontend_config.pointer_type());
1499            builder.append_block_params_for_function_params(block0);
1500            builder.switch_to_block(block0);
1501
1502            let src = builder.use_var(x);
1503            let dest = builder.use_var(y);
1504            let size = 8192;
1505            builder.emit_small_memory_copy(
1506                frontend_config,
1507                dest,
1508                src,
1509                size,
1510                8,
1511                8,
1512                true,
1513                MemFlags::new(),
1514            );
1515            builder.ins().return_(&[dest]);
1516
1517            builder.seal_all_blocks();
1518            builder.finalize();
1519        }
1520
1521        check(
1522            &func,
1523            "function %sample() -> i32 system_v {
1524    sig0 = (i64, i64, i64) -> i64 system_v
1525    fn0 = %Memcpy sig0
1526
1527block0:
1528    v5 = iconst.i64 0
1529    v1 -> v5
1530    v4 = iconst.i64 0
1531    v0 -> v4
1532    v2 = iconst.i64 8192
1533    v3 = call fn0(v1, v0, v2)  ; v1 = 0, v0 = 0, v2 = 8192
1534    return v1  ; v1 = 0
1535}
1536",
1537        );
1538    }
1539
1540    #[test]
1541    fn small_memset() {
1542        let frontend_config = systemv_frontend_config();
1543        let mut sig = Signature::new(frontend_config.default_call_conv);
1544        sig.returns.push(AbiParam::new(I32));
1545
1546        let mut fn_ctx = FunctionBuilderContext::new();
1547        let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1548        {
1549            let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1550
1551            let block0 = builder.create_block();
1552            let y = Variable::new(16);
1553            builder.declare_var(y, frontend_config.pointer_type());
1554            builder.append_block_params_for_function_params(block0);
1555            builder.switch_to_block(block0);
1556
1557            let dest = builder.use_var(y);
1558            let size = 8;
1559            builder.emit_small_memset(frontend_config, dest, 1, size, 8, MemFlags::new());
1560            builder.ins().return_(&[dest]);
1561
1562            builder.seal_all_blocks();
1563            builder.finalize();
1564        }
1565
1566        check(
1567            &func,
1568            "function %sample() -> i32 system_v {
1569block0:
1570    v2 = iconst.i64 0
1571    v0 -> v2
1572    v1 = iconst.i64 0x0101_0101_0101_0101
1573    store aligned v1, v0  ; v1 = 0x0101_0101_0101_0101, v0 = 0
1574    return v0  ; v0 = 0
1575}
1576",
1577        );
1578    }
1579
1580    #[test]
1581    fn not_so_small_memset() {
1582        let frontend_config = systemv_frontend_config();
1583        let mut sig = Signature::new(frontend_config.default_call_conv);
1584        sig.returns.push(AbiParam::new(I32));
1585
1586        let mut fn_ctx = FunctionBuilderContext::new();
1587        let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1588        {
1589            let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1590
1591            let block0 = builder.create_block();
1592            let y = Variable::new(16);
1593            builder.declare_var(y, frontend_config.pointer_type());
1594            builder.append_block_params_for_function_params(block0);
1595            builder.switch_to_block(block0);
1596
1597            let dest = builder.use_var(y);
1598            let size = 8192;
1599            builder.emit_small_memset(frontend_config, dest, 1, size, 8, MemFlags::new());
1600            builder.ins().return_(&[dest]);
1601
1602            builder.seal_all_blocks();
1603            builder.finalize();
1604        }
1605
1606        check(
1607            &func,
1608            "function %sample() -> i32 system_v {
1609    sig0 = (i64, i32, i64) -> i64 system_v
1610    fn0 = %Memset sig0
1611
1612block0:
1613    v5 = iconst.i64 0
1614    v0 -> v5
1615    v1 = iconst.i8 1
1616    v2 = iconst.i64 8192
1617    v3 = uextend.i32 v1  ; v1 = 1
1618    v4 = call fn0(v0, v3, v2)  ; v0 = 0, v2 = 8192
1619    return v0  ; v0 = 0
1620}
1621",
1622        );
1623    }
1624
1625    #[test]
1626    fn memcmp() {
1627        use core::str::FromStr;
1628        use cranelift_codegen::isa;
1629
1630        let shared_builder = settings::builder();
1631        let shared_flags = settings::Flags::new(shared_builder);
1632
1633        let triple =
1634            ::target_lexicon::Triple::from_str("x86_64").expect("Couldn't create x86_64 triple");
1635
1636        let target = isa::lookup(triple)
1637            .ok()
1638            .map(|b| b.finish(shared_flags))
1639            .expect("This test requires x86_64 support.")
1640            .expect("Should be able to create backend with default flags");
1641
1642        let mut sig = Signature::new(target.default_call_conv());
1643        sig.returns.push(AbiParam::new(I32));
1644
1645        let mut fn_ctx = FunctionBuilderContext::new();
1646        let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1647        {
1648            let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1649
1650            let block0 = builder.create_block();
1651            let x = Variable::new(0);
1652            let y = Variable::new(1);
1653            let z = Variable::new(2);
1654            builder.declare_var(x, target.pointer_type());
1655            builder.declare_var(y, target.pointer_type());
1656            builder.declare_var(z, target.pointer_type());
1657            builder.append_block_params_for_function_params(block0);
1658            builder.switch_to_block(block0);
1659
1660            let left = builder.use_var(x);
1661            let right = builder.use_var(y);
1662            let size = builder.use_var(z);
1663            let cmp = builder.call_memcmp(target.frontend_config(), left, right, size);
1664            builder.ins().return_(&[cmp]);
1665
1666            builder.seal_all_blocks();
1667            builder.finalize();
1668        }
1669
1670        check(
1671            &func,
1672            "function %sample() -> i32 system_v {
1673    sig0 = (i64, i64, i64) -> i32 system_v
1674    fn0 = %Memcmp sig0
1675
1676block0:
1677    v6 = iconst.i64 0
1678    v2 -> v6
1679    v5 = iconst.i64 0
1680    v1 -> v5
1681    v4 = iconst.i64 0
1682    v0 -> v4
1683    v3 = call fn0(v0, v1, v2)  ; v0 = 0, v1 = 0, v2 = 0
1684    return v3
1685}
1686",
1687        );
1688    }
1689
1690    #[test]
1691    fn small_memcmp_zero_size() {
1692        let align_eight = std::num::NonZeroU8::new(8).unwrap();
1693        small_memcmp_helper(
1694            "
1695block0:
1696    v4 = iconst.i64 0
1697    v1 -> v4
1698    v3 = iconst.i64 0
1699    v0 -> v3
1700    v2 = iconst.i8 1
1701    return v2  ; v2 = 1",
1702            |builder, target, x, y| {
1703                builder.emit_small_memory_compare(
1704                    target.frontend_config(),
1705                    IntCC::UnsignedGreaterThanOrEqual,
1706                    x,
1707                    y,
1708                    0,
1709                    align_eight,
1710                    align_eight,
1711                    MemFlags::new(),
1712                )
1713            },
1714        );
1715    }
1716
1717    #[test]
1718    fn small_memcmp_byte_ugt() {
1719        let align_one = std::num::NonZeroU8::new(1).unwrap();
1720        small_memcmp_helper(
1721            "
1722block0:
1723    v6 = iconst.i64 0
1724    v1 -> v6
1725    v5 = iconst.i64 0
1726    v0 -> v5
1727    v2 = load.i8 aligned v0  ; v0 = 0
1728    v3 = load.i8 aligned v1  ; v1 = 0
1729    v4 = icmp ugt v2, v3
1730    return v4",
1731            |builder, target, x, y| {
1732                builder.emit_small_memory_compare(
1733                    target.frontend_config(),
1734                    IntCC::UnsignedGreaterThan,
1735                    x,
1736                    y,
1737                    1,
1738                    align_one,
1739                    align_one,
1740                    MemFlags::new(),
1741                )
1742            },
1743        );
1744    }
1745
1746    #[test]
1747    fn small_memcmp_aligned_eq() {
1748        let align_four = std::num::NonZeroU8::new(4).unwrap();
1749        small_memcmp_helper(
1750            "
1751block0:
1752    v6 = iconst.i64 0
1753    v1 -> v6
1754    v5 = iconst.i64 0
1755    v0 -> v5
1756    v2 = load.i32 aligned v0  ; v0 = 0
1757    v3 = load.i32 aligned v1  ; v1 = 0
1758    v4 = icmp eq v2, v3
1759    return v4",
1760            |builder, target, x, y| {
1761                builder.emit_small_memory_compare(
1762                    target.frontend_config(),
1763                    IntCC::Equal,
1764                    x,
1765                    y,
1766                    4,
1767                    align_four,
1768                    align_four,
1769                    MemFlags::new(),
1770                )
1771            },
1772        );
1773    }
1774
1775    #[test]
1776    fn small_memcmp_ipv6_ne() {
1777        let align_two = std::num::NonZeroU8::new(2).unwrap();
1778        small_memcmp_helper(
1779            "
1780block0:
1781    v6 = iconst.i64 0
1782    v1 -> v6
1783    v5 = iconst.i64 0
1784    v0 -> v5
1785    v2 = load.i128 v0  ; v0 = 0
1786    v3 = load.i128 v1  ; v1 = 0
1787    v4 = icmp ne v2, v3
1788    return v4",
1789            |builder, target, x, y| {
1790                builder.emit_small_memory_compare(
1791                    target.frontend_config(),
1792                    IntCC::NotEqual,
1793                    x,
1794                    y,
1795                    16,
1796                    align_two,
1797                    align_two,
1798                    MemFlags::new(),
1799                )
1800            },
1801        );
1802    }
1803
1804    #[test]
1805    fn small_memcmp_odd_size_uge() {
1806        let one = std::num::NonZeroU8::new(1).unwrap();
1807        small_memcmp_helper(
1808            "
1809    sig0 = (i64, i64, i64) -> i32 system_v
1810    fn0 = %Memcmp sig0
1811
1812block0:
1813    v6 = iconst.i64 0
1814    v1 -> v6
1815    v5 = iconst.i64 0
1816    v0 -> v5
1817    v2 = iconst.i64 3
1818    v3 = call fn0(v0, v1, v2)  ; v0 = 0, v1 = 0, v2 = 3
1819    v4 = icmp_imm sge v3, 0
1820    return v4",
1821            |builder, target, x, y| {
1822                builder.emit_small_memory_compare(
1823                    target.frontend_config(),
1824                    IntCC::UnsignedGreaterThanOrEqual,
1825                    x,
1826                    y,
1827                    3,
1828                    one,
1829                    one,
1830                    MemFlags::new(),
1831                )
1832            },
1833        );
1834    }
1835
1836    fn small_memcmp_helper(
1837        expected: &str,
1838        f: impl FnOnce(&mut FunctionBuilder, &dyn TargetIsa, Value, Value) -> Value,
1839    ) {
1840        use core::str::FromStr;
1841        use cranelift_codegen::isa;
1842
1843        let shared_builder = settings::builder();
1844        let shared_flags = settings::Flags::new(shared_builder);
1845
1846        let triple =
1847            ::target_lexicon::Triple::from_str("x86_64").expect("Couldn't create x86_64 triple");
1848
1849        let target = isa::lookup(triple)
1850            .ok()
1851            .map(|b| b.finish(shared_flags))
1852            .expect("This test requires x86_64 support.")
1853            .expect("Should be able to create backend with default flags");
1854
1855        let mut sig = Signature::new(target.default_call_conv());
1856        sig.returns.push(AbiParam::new(I8));
1857
1858        let mut fn_ctx = FunctionBuilderContext::new();
1859        let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1860        {
1861            let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1862
1863            let block0 = builder.create_block();
1864            let x = Variable::new(0);
1865            let y = Variable::new(1);
1866            builder.declare_var(x, target.pointer_type());
1867            builder.declare_var(y, target.pointer_type());
1868            builder.append_block_params_for_function_params(block0);
1869            builder.switch_to_block(block0);
1870
1871            let left = builder.use_var(x);
1872            let right = builder.use_var(y);
1873            let ret = f(&mut builder, &*target, left, right);
1874            builder.ins().return_(&[ret]);
1875
1876            builder.seal_all_blocks();
1877            builder.finalize();
1878        }
1879
1880        check(
1881            &func,
1882            &format!("function %sample() -> i8 system_v {{{expected}\n}}\n"),
1883        );
1884    }
1885
1886    #[test]
1887    fn undef_vector_vars() {
1888        let mut sig = Signature::new(CallConv::SystemV);
1889        sig.returns.push(AbiParam::new(I8X16));
1890        sig.returns.push(AbiParam::new(I8X16));
1891        sig.returns.push(AbiParam::new(F32X4));
1892
1893        let mut fn_ctx = FunctionBuilderContext::new();
1894        let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1895        {
1896            let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1897
1898            let block0 = builder.create_block();
1899            let a = Variable::new(0);
1900            let b = Variable::new(1);
1901            let c = Variable::new(2);
1902            builder.declare_var(a, I8X16);
1903            builder.declare_var(b, I8X16);
1904            builder.declare_var(c, F32X4);
1905            builder.switch_to_block(block0);
1906
1907            let a = builder.use_var(a);
1908            let b = builder.use_var(b);
1909            let c = builder.use_var(c);
1910            builder.ins().return_(&[a, b, c]);
1911
1912            builder.seal_all_blocks();
1913            builder.finalize();
1914        }
1915
1916        check(
1917            &func,
1918            "function %sample() -> i8x16, i8x16, f32x4 system_v {
1919    const0 = 0x00000000000000000000000000000000
1920
1921block0:
1922    v5 = f32const 0.0
1923    v6 = splat.f32x4 v5  ; v5 = 0.0
1924    v2 -> v6
1925    v4 = vconst.i8x16 const0
1926    v1 -> v4
1927    v3 = vconst.i8x16 const0
1928    v0 -> v3
1929    return v0, v1, v2  ; v0 = const0, v1 = const0
1930}
1931",
1932        );
1933    }
1934
1935    #[test]
1936    fn test_greatest_divisible_power_of_two() {
1937        assert_eq!(64, greatest_divisible_power_of_two(64));
1938        assert_eq!(16, greatest_divisible_power_of_two(48));
1939        assert_eq!(8, greatest_divisible_power_of_two(24));
1940        assert_eq!(1, greatest_divisible_power_of_two(25));
1941    }
1942
1943    #[test]
1944    fn try_use_var() {
1945        let sig = Signature::new(CallConv::SystemV);
1946
1947        let mut fn_ctx = FunctionBuilderContext::new();
1948        let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1949        {
1950            let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1951
1952            let block0 = builder.create_block();
1953            builder.append_block_params_for_function_params(block0);
1954            builder.switch_to_block(block0);
1955
1956            assert_eq!(
1957                builder.try_use_var(Variable::from_u32(0)),
1958                Err(UseVariableError::UsedBeforeDeclared(Variable::from_u32(0)))
1959            );
1960
1961            let value = builder.ins().iconst(cranelift_codegen::ir::types::I32, 0);
1962
1963            assert_eq!(
1964                builder.try_def_var(Variable::from_u32(0), value),
1965                Err(DefVariableError::DefinedBeforeDeclared(Variable::from_u32(
1966                    0
1967                )))
1968            );
1969
1970            builder.declare_var(Variable::from_u32(0), cranelift_codegen::ir::types::I32);
1971            assert_eq!(
1972                builder.try_declare_var(Variable::from_u32(0), cranelift_codegen::ir::types::I32),
1973                Err(DeclareVariableError::DeclaredMultipleTimes(
1974                    Variable::from_u32(0)
1975                ))
1976            );
1977        }
1978    }
1979
1980    #[test]
1981    fn test_builder_with_iconst_and_negative_constant() {
1982        let sig = Signature::new(CallConv::SystemV);
1983        let mut fn_ctx = FunctionBuilderContext::new();
1984        let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
1985
1986        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
1987
1988        let block0 = builder.create_block();
1989        builder.switch_to_block(block0);
1990        builder.ins().iconst(I32, -1);
1991        builder.ins().return_(&[]);
1992
1993        builder.seal_all_blocks();
1994        builder.finalize();
1995
1996        let flags = cranelift_codegen::settings::Flags::new(cranelift_codegen::settings::builder());
1997        let ctx = cranelift_codegen::Context::for_function(func);
1998        ctx.verify(&flags).expect("should be valid");
1999
2000        check(
2001            &ctx.func,
2002            "function %sample() system_v {
2003block0:
2004    v0 = iconst.i32 -1
2005    return
2006}",
2007        );
2008    }
2009
2010    #[test]
2011    fn try_call() {
2012        let mut sig = Signature::new(CallConv::SystemV);
2013        sig.params.push(AbiParam::new(I8));
2014        sig.returns.push(AbiParam::new(I32));
2015        let mut fn_ctx = FunctionBuilderContext::new();
2016        let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);
2017
2018        let sig0 = func.import_signature(Signature::new(CallConv::SystemV));
2019        let name = func.declare_imported_user_function(UserExternalName::new(0, 0));
2020        let fn0 = func.import_function(ExtFuncData {
2021            name: ExternalName::User(name),
2022            signature: sig0,
2023            colocated: false,
2024        });
2025
2026        let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);
2027
2028        let block0 = builder.create_block();
2029        let block1 = builder.create_block();
2030        let block2 = builder.create_block();
2031        let block3 = builder.create_block();
2032
2033        let my_var = Variable::from_u32(0);
2034        builder.declare_var(my_var, I32);
2035
2036        builder.switch_to_block(block0);
2037        let branch_val = builder.append_block_param(block0, I8);
2038        builder.ins().brif(branch_val, block1, &[], block2, &[]);
2039
2040        builder.switch_to_block(block1);
2041        let one = builder.ins().iconst(I32, 1);
2042        builder.def_var(my_var, one);
2043
2044        let normal_return =
2045            BlockCall::new(block3, [].into_iter(), &mut builder.func.dfg.value_lists);
2046        let exception_table = builder
2047            .func
2048            .dfg
2049            .exception_tables
2050            .push(ExceptionTableData::new(sig0, normal_return, []));
2051        builder.ins().try_call(fn0, &[], exception_table);
2052
2053        builder.switch_to_block(block2);
2054        let two = builder.ins().iconst(I32, 2);
2055        builder.def_var(my_var, two);
2056
2057        let normal_return =
2058            BlockCall::new(block3, [].into_iter(), &mut builder.func.dfg.value_lists);
2059        let exception_table = builder
2060            .func
2061            .dfg
2062            .exception_tables
2063            .push(ExceptionTableData::new(sig0, normal_return, []));
2064        builder.ins().try_call(fn0, &[], exception_table);
2065
2066        builder.switch_to_block(block3);
2067        let ret_val = builder.use_var(my_var);
2068        builder.ins().return_(&[ret_val]);
2069
2070        builder.seal_all_blocks();
2071        builder.finalize();
2072
2073        let flags = cranelift_codegen::settings::Flags::new(cranelift_codegen::settings::builder());
2074        let ctx = cranelift_codegen::Context::for_function(func);
2075        ctx.verify(&flags).expect("should be valid");
2076
2077        check(
2078            &ctx.func,
2079            "function %sample(i8) -> i32 system_v {
2080    sig0 = () system_v
2081    fn0 = u0:0 sig0
2082
2083block0(v0: i8):
2084    brif v0, block1, block2
2085
2086block1:
2087    v1 = iconst.i32 1
2088    try_call fn0(), sig0, block3(v1), []  ; v1 = 1
2089
2090block2:
2091    v2 = iconst.i32 2
2092    try_call fn0(), sig0, block3(v2), []  ; v2 = 2
2093
2094block3(v3: i32):
2095    return v3
2096}",
2097        );
2098    }
2099}