cranelift_frontend/
frontend.rs

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