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