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