Path: blob/main/cranelift/frontend/src/frontend.rs
3079 views
//! A frontend for building Cranelift IR from other languages.1use crate::ssa::{SSABuilder, SideEffects};2use crate::variable::Variable;3use alloc::vec::Vec;4use core::fmt::{self, Debug};5use cranelift_codegen::cursor::{Cursor, CursorPosition, FuncCursor};6use cranelift_codegen::entity::{EntityRef, EntitySet, PrimaryMap, SecondaryMap};7use cranelift_codegen::ir;8use cranelift_codegen::ir::condcodes::IntCC;9use cranelift_codegen::ir::{10AbiParam, Block, DataFlowGraph, DynamicStackSlot, DynamicStackSlotData, ExtFuncData,11ExternalName, FuncRef, Function, GlobalValue, GlobalValueData, Inst, InstBuilder,12InstBuilderBase, InstructionData, JumpTable, JumpTableData, LibCall, MemFlags, RelSourceLoc,13SigRef, Signature, StackSlot, StackSlotData, Type, Value, ValueLabel, ValueLabelAssignments,14ValueLabelStart, types,15};16use cranelift_codegen::isa::TargetFrontendConfig;17use cranelift_codegen::packed_option::PackedOption;18use cranelift_codegen::traversals::Dfs;19use smallvec::SmallVec;2021mod safepoints;2223/// Structure used for translating a series of functions into Cranelift IR.24///25/// In order to reduce memory reallocations when compiling multiple functions,26/// [`FunctionBuilderContext`] holds various data structures which are cleared between27/// functions, rather than dropped, preserving the underlying allocations.28#[derive(Default)]29pub struct FunctionBuilderContext {30ssa: SSABuilder,31status: SecondaryMap<Block, BlockStatus>,32variables: PrimaryMap<Variable, Type>,33stack_map_vars: EntitySet<Variable>,34stack_map_values: EntitySet<Value>,35safepoints: safepoints::SafepointSpiller,36}3738/// Temporary object used to build a single Cranelift IR [`Function`].39pub struct FunctionBuilder<'a> {40/// The function currently being built.41/// This field is public so the function can be re-borrowed.42pub func: &'a mut Function,4344/// Source location to assign to all new instructions.45srcloc: ir::SourceLoc,4647func_ctx: &'a mut FunctionBuilderContext,48position: PackedOption<Block>,49}5051#[derive(Clone, Default, Eq, PartialEq)]52enum BlockStatus {53/// No instructions have been added.54#[default]55Empty,56/// Some instructions have been added, but no terminator.57Partial,58/// A terminator has been added; no further instructions may be added.59Filled,60}6162impl FunctionBuilderContext {63/// Creates a [`FunctionBuilderContext`] structure. The structure is automatically cleared after64/// each [`FunctionBuilder`] completes translating a function.65pub fn new() -> Self {66Self::default()67}6869fn clear(&mut self) {70let FunctionBuilderContext {71ssa,72status,73variables,74stack_map_vars,75stack_map_values,76safepoints,77} = self;78ssa.clear();79status.clear();80variables.clear();81stack_map_values.clear();82stack_map_vars.clear();83safepoints.clear();84}8586fn is_empty(&self) -> bool {87self.ssa.is_empty() && self.status.is_empty() && self.variables.is_empty()88}89}9091/// Implementation of the [`InstBuilder`] that has92/// one convenience method per Cranelift IR instruction.93pub struct FuncInstBuilder<'short, 'long: 'short> {94builder: &'short mut FunctionBuilder<'long>,95block: Block,96}9798impl<'short, 'long> FuncInstBuilder<'short, 'long> {99fn new(builder: &'short mut FunctionBuilder<'long>, block: Block) -> Self {100Self { builder, block }101}102}103104impl<'short, 'long> InstBuilderBase<'short> for FuncInstBuilder<'short, 'long> {105fn data_flow_graph(&self) -> &DataFlowGraph {106&self.builder.func.dfg107}108109fn data_flow_graph_mut(&mut self) -> &mut DataFlowGraph {110&mut self.builder.func.dfg111}112113// This implementation is richer than `InsertBuilder` because we use the data of the114// instruction being inserted to add related info to the DFG and the SSA building system,115// and perform debug sanity checks.116fn build(self, data: InstructionData, ctrl_typevar: Type) -> (Inst, &'short mut DataFlowGraph) {117// We only insert the Block in the layout when an instruction is added to it118self.builder.ensure_inserted_block();119120let inst = self.builder.func.dfg.make_inst(data);121self.builder.func.dfg.make_inst_results(inst, ctrl_typevar);122self.builder.func.layout.append_inst(inst, self.block);123if !self.builder.srcloc.is_default() {124self.builder.func.set_srcloc(inst, self.builder.srcloc);125}126127match &self.builder.func.dfg.insts[inst] {128ir::InstructionData::Jump {129destination: dest, ..130} => {131// If the user has supplied jump arguments we must adapt the arguments of132// the destination block133let block = dest.block(&self.builder.func.dfg.value_lists);134self.builder.declare_successor(block, inst);135}136137ir::InstructionData::Brif {138blocks: [branch_then, branch_else],139..140} => {141let block_then = branch_then.block(&self.builder.func.dfg.value_lists);142let block_else = branch_else.block(&self.builder.func.dfg.value_lists);143144self.builder.declare_successor(block_then, inst);145if block_then != block_else {146self.builder.declare_successor(block_else, inst);147}148}149150ir::InstructionData::BranchTable { table, .. } => {151let pool = &self.builder.func.dfg.value_lists;152153// Unlike most other jumps/branches and like try_call,154// jump tables are capable of having the same successor appear155// multiple times, so we must deduplicate.156let mut unique = EntitySet::<Block>::new();157for dest_block in self158.builder159.func160.stencil161.dfg162.jump_tables163.get(*table)164.expect("you are referencing an undeclared jump table")165.all_branches()166{167let block = dest_block.block(pool);168if !unique.insert(block) {169continue;170}171172// Call `declare_block_predecessor` instead of `declare_successor` for173// avoiding the borrow checker.174self.builder175.func_ctx176.ssa177.declare_block_predecessor(block, inst);178}179}180181ir::InstructionData::TryCall { exception, .. }182| ir::InstructionData::TryCallIndirect { exception, .. } => {183let pool = &self.builder.func.dfg.value_lists;184185// Unlike most other jumps/branches and like br_table,186// exception tables are capable of having the same successor187// appear multiple times, so we must deduplicate.188let mut unique = EntitySet::<Block>::new();189for dest_block in self190.builder191.func192.stencil193.dfg194.exception_tables195.get(*exception)196.expect("you are referencing an undeclared exception table")197.all_branches()198{199let block = dest_block.block(pool);200if !unique.insert(block) {201continue;202}203204// Call `declare_block_predecessor` instead of `declare_successor` for205// avoiding the borrow checker.206self.builder207.func_ctx208.ssa209.declare_block_predecessor(block, inst);210}211}212213inst => assert!(!inst.opcode().is_branch()),214}215216if data.opcode().is_terminator() {217self.builder.fill_current_block()218}219(inst, &mut self.builder.func.dfg)220}221}222223#[derive(Debug, Copy, Clone, PartialEq, Eq)]224/// An error encountered when calling [`FunctionBuilder::try_use_var`].225pub enum UseVariableError {226UsedBeforeDeclared(Variable),227}228229impl fmt::Display for UseVariableError {230fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {231match self {232UseVariableError::UsedBeforeDeclared(variable) => {233write!(234f,235"variable {} was used before it was defined",236variable.index()237)?;238}239}240Ok(())241}242}243244impl std::error::Error for UseVariableError {}245246#[derive(Debug, Copy, Clone, Eq, PartialEq)]247/// An error encountered when defining the initial value of a variable.248pub enum DefVariableError {249/// The variable was instantiated with a value of the wrong type.250///251/// note: to obtain the type of the value, you can call252/// [`cranelift_codegen::ir::dfg::DataFlowGraph::value_type`] (using the253/// `FunctionBuilder.func.dfg` field)254TypeMismatch(Variable, Value),255/// The value was defined (in a call to [`FunctionBuilder::def_var`]) before256/// it was declared (in a call to [`FunctionBuilder::declare_var`]).257DefinedBeforeDeclared(Variable),258}259260impl fmt::Display for DefVariableError {261fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {262match self {263DefVariableError::TypeMismatch(variable, value) => {264write!(265f,266"the types of variable {} and value {} are not the same.267The `Value` supplied to `def_var` must be of the same type as268the variable was declared to be of in `declare_var`.",269variable.index(),270value.as_u32()271)?;272}273DefVariableError::DefinedBeforeDeclared(variable) => {274write!(275f,276"the value of variable {} was declared before it was defined",277variable.index()278)?;279}280}281Ok(())282}283}284285/// This module allows you to create a function in Cranelift IR in a straightforward way, hiding286/// all the complexity of its internal representation.287///288/// The module is parametrized by one type which is the representation of variables in your289/// origin language. It offers a way to conveniently append instruction to your program flow.290/// You are responsible to split your instruction flow into extended blocks (declared with291/// [`create_block`](Self::create_block)) whose properties are:292///293/// - branch and jump instructions can only point at the top of extended blocks;294/// - the last instruction of each block is a terminator instruction which has no natural successor,295/// and those instructions can only appear at the end of extended blocks.296///297/// The parameters of Cranelift IR instructions are Cranelift IR values, which can only be created298/// as results of other Cranelift IR instructions. To be able to create variables redefined multiple299/// times in your program, use the [`def_var`](Self::def_var) and [`use_var`](Self::use_var) command,300/// that will maintain the correspondence between your variables and Cranelift IR SSA values.301///302/// The first block for which you call [`switch_to_block`](Self::switch_to_block) will be assumed to303/// be the beginning of the function.304///305/// At creation, a [`FunctionBuilder`] instance borrows an already allocated `Function` which it306/// modifies with the information stored in the mutable borrowed307/// [`FunctionBuilderContext`]. The function passed in argument should be newly created with308/// [`Function::with_name_signature()`], whereas the [`FunctionBuilderContext`] can be kept as is309/// between two function translations.310///311/// # Errors312///313/// The functions below will panic in debug mode whenever you try to modify the Cranelift IR314/// function in a way that violate the coherence of the code. For instance: switching to a new315/// [`Block`] when you haven't filled the current one with a terminator instruction, inserting a316/// return instruction with arguments that don't match the function's signature.317impl<'a> FunctionBuilder<'a> {318/// Creates a new [`FunctionBuilder`] structure that will operate on a [`Function`] using a319/// [`FunctionBuilderContext`].320pub fn new(func: &'a mut Function, func_ctx: &'a mut FunctionBuilderContext) -> Self {321debug_assert!(func_ctx.is_empty());322Self {323func,324srcloc: Default::default(),325func_ctx,326position: Default::default(),327}328}329330/// Get the block that this builder is currently at.331pub fn current_block(&self) -> Option<Block> {332self.position.expand()333}334335/// Set the source location that should be assigned to all new instructions.336pub fn set_srcloc(&mut self, srcloc: ir::SourceLoc) {337self.srcloc = srcloc;338}339340/// Get the current source location that this builder is using.341pub fn srcloc(&self) -> ir::SourceLoc {342self.srcloc343}344345/// Creates a new [`Block`] and returns its reference.346pub fn create_block(&mut self) -> Block {347let block = self.func.dfg.make_block();348self.func_ctx.ssa.declare_block(block);349block350}351352/// Mark a block as "cold".353///354/// This will try to move it out of the ordinary path of execution355/// when lowered to machine code.356pub fn set_cold_block(&mut self, block: Block) {357self.func.layout.set_cold(block);358}359360/// Insert `block` in the layout *after* the existing block `after`.361pub fn insert_block_after(&mut self, block: Block, after: Block) {362self.func.layout.insert_block_after(block, after);363}364365/// After the call to this function, new instructions will be inserted into the designated366/// block, in the order they are declared. You must declare the types of the [`Block`] arguments367/// you will use here.368///369/// When inserting the terminator instruction (which doesn't have a fallthrough to its immediate370/// successor), the block will be declared filled and it will not be possible to append371/// instructions to it.372pub fn switch_to_block(&mut self, block: Block) {373log::trace!("switch to {block:?}");374375// First we check that the previous block has been filled.376debug_assert!(377self.position.is_none()378|| self.is_unreachable()379|| self.is_pristine(self.position.unwrap())380|| self.is_filled(self.position.unwrap()),381"you have to fill your block before switching"382);383// We cannot switch to a filled block384debug_assert!(385!self.is_filled(block),386"you cannot switch to a block which is already filled"387);388389// Then we change the cursor position.390self.position = PackedOption::from(block);391}392393/// Declares that all the predecessors of this block are known.394///395/// Function to call with `block` as soon as the last branch instruction to `block` has been396/// created. Forgetting to call this method on every block will cause inconsistencies in the397/// produced functions.398pub fn seal_block(&mut self, block: Block) {399let side_effects = self.func_ctx.ssa.seal_block(block, self.func);400self.handle_ssa_side_effects(side_effects);401}402403/// Effectively calls [seal_block](Self::seal_block) on all unsealed blocks in the function.404///405/// It's more efficient to seal [`Block`]s as soon as possible, during406/// translation, but for frontends where this is impractical to do, this407/// function can be used at the end of translating all blocks to ensure408/// that everything is sealed.409pub fn seal_all_blocks(&mut self) {410let side_effects = self.func_ctx.ssa.seal_all_blocks(self.func);411self.handle_ssa_side_effects(side_effects);412}413414/// Declares the type of a variable.415///416/// This allows the variable to be defined and used later (by calling417/// [`FunctionBuilder::def_var`] and [`FunctionBuilder::use_var`]418/// respectively).419pub fn declare_var(&mut self, ty: Type) -> Variable {420self.func_ctx.variables.push(ty)421}422423/// Declare that all uses of the given variable must be included in stack424/// map metadata.425///426/// All values that are uses of this variable will be spilled to the stack427/// before each safepoint and reloaded afterwards. Stack maps allow the428/// garbage collector to identify the on-stack GC roots. Between spilling429/// the stack and it being reloading again, the stack can be updated to430/// facilitate moving GCs.431///432/// This does not affect any pre-existing uses of the variable.433///434/// # Panics435///436/// Panics if the variable's type is larger than 16 bytes or if this437/// variable has not been declared yet.438pub fn declare_var_needs_stack_map(&mut self, var: Variable) {439log::trace!("declare_var_needs_stack_map({var:?})");440let ty = self.func_ctx.variables[var];441assert!(ty != types::INVALID);442assert!(ty.bytes() <= 16);443self.func_ctx.stack_map_vars.insert(var);444}445446/// Returns the Cranelift IR necessary to use a previously defined user447/// variable, returning an error if this is not possible.448pub fn try_use_var(&mut self, var: Variable) -> Result<Value, UseVariableError> {449// Assert that we're about to add instructions to this block using the definition of the450// given variable. ssa.use_var is the only part of this crate which can add block parameters451// behind the caller's back. If we disallow calling append_block_param as soon as use_var is452// called, then we enforce a strict separation between user parameters and SSA parameters.453self.ensure_inserted_block();454455let (val, side_effects) = {456let ty = *self457.func_ctx458.variables459.get(var)460.ok_or(UseVariableError::UsedBeforeDeclared(var))?;461debug_assert_ne!(462ty,463types::INVALID,464"variable {var:?} is used but its type has not been declared"465);466self.func_ctx467.ssa468.use_var(self.func, var, ty, self.position.unwrap())469};470self.handle_ssa_side_effects(side_effects);471472Ok(val)473}474475/// Returns the Cranelift IR value corresponding to the utilization at the current program476/// position of a previously defined user variable.477pub fn use_var(&mut self, var: Variable) -> Value {478self.try_use_var(var).unwrap_or_else(|_| {479panic!("variable {var:?} is used but its type has not been declared")480})481}482483/// Registers a new definition of a user variable. This function will return484/// an error if the value supplied does not match the type the variable was485/// declared to have.486pub fn try_def_var(&mut self, var: Variable, val: Value) -> Result<(), DefVariableError> {487log::trace!("try_def_var: {var:?} = {val:?}");488489let var_ty = *self490.func_ctx491.variables492.get(var)493.ok_or(DefVariableError::DefinedBeforeDeclared(var))?;494if var_ty != self.func.dfg.value_type(val) {495return Err(DefVariableError::TypeMismatch(var, val));496}497498self.func_ctx.ssa.def_var(var, val, self.position.unwrap());499Ok(())500}501502/// Register a new definition of a user variable. The type of the value must be503/// the same as the type registered for the variable.504pub fn def_var(&mut self, var: Variable, val: Value) {505self.try_def_var(var, val)506.unwrap_or_else(|error| match error {507DefVariableError::TypeMismatch(var, val) => {508panic!("declared type of variable {var:?} doesn't match type of value {val}");509}510DefVariableError::DefinedBeforeDeclared(var) => {511panic!("variable {var:?} is used but its type has not been declared");512}513})514}515516/// Set label for [`Value`]517///518/// This will not do anything unless519/// [`func.dfg.collect_debug_info`](DataFlowGraph::collect_debug_info) is called first.520pub fn set_val_label(&mut self, val: Value, label: ValueLabel) {521if let Some(values_labels) = self.func.stencil.dfg.values_labels.as_mut() {522use alloc::collections::btree_map::Entry;523524let start = ValueLabelStart {525from: RelSourceLoc::from_base_offset(self.func.params.base_srcloc(), self.srcloc),526label,527};528529match values_labels.entry(val) {530Entry::Occupied(mut e) => match e.get_mut() {531ValueLabelAssignments::Starts(starts) => starts.push(start),532_ => panic!("Unexpected ValueLabelAssignments at this stage"),533},534Entry::Vacant(e) => {535e.insert(ValueLabelAssignments::Starts(vec![start]));536}537}538}539}540541/// Declare that the given value is a GC reference that requires inclusion542/// in a stack map when it is live across GC safepoints.543///544/// All values that are uses of this variable will be spilled to the stack545/// before each safepoint and reloaded afterwards. Stack maps allow the546/// garbage collector to identify the on-stack GC roots. Between spilling547/// the stack and it being reloading again, the stack can be updated to548/// facilitate moving GCs.549///550/// # Panics551///552/// Panics if `val` is larger than 16 bytes.553pub fn declare_value_needs_stack_map(&mut self, val: Value) {554log::trace!("declare_value_needs_stack_map({val:?})");555556// We rely on these properties in `insert_safepoint_spills`.557let size = self.func.dfg.value_type(val).bytes();558assert!(size <= 16);559assert!(size.is_power_of_two());560561self.func_ctx.stack_map_values.insert(val);562}563564/// Creates a jump table in the function, to be used by [`br_table`](InstBuilder::br_table) instructions.565pub fn create_jump_table(&mut self, data: JumpTableData) -> JumpTable {566self.func.create_jump_table(data)567}568569/// Creates a sized stack slot in the function, to be used by [`stack_load`](InstBuilder::stack_load),570/// [`stack_store`](InstBuilder::stack_store) and [`stack_addr`](InstBuilder::stack_addr) instructions.571pub fn create_sized_stack_slot(&mut self, data: StackSlotData) -> StackSlot {572self.func.create_sized_stack_slot(data)573}574575/// Creates a dynamic stack slot in the function, to be used by576/// [`dynamic_stack_load`](InstBuilder::dynamic_stack_load),577/// [`dynamic_stack_store`](InstBuilder::dynamic_stack_store) and578/// [`dynamic_stack_addr`](InstBuilder::dynamic_stack_addr) instructions.579pub fn create_dynamic_stack_slot(&mut self, data: DynamicStackSlotData) -> DynamicStackSlot {580self.func.create_dynamic_stack_slot(data)581}582583/// Adds a signature which can later be used to declare an external function import.584pub fn import_signature(&mut self, signature: Signature) -> SigRef {585self.func.import_signature(signature)586}587588/// Declare an external function import.589pub fn import_function(&mut self, data: ExtFuncData) -> FuncRef {590self.func.import_function(data)591}592593/// Declares a global value accessible to the function.594pub fn create_global_value(&mut self, data: GlobalValueData) -> GlobalValue {595self.func.create_global_value(data)596}597598/// Returns an object with the [`InstBuilder`]599/// trait that allows to conveniently append an instruction to the current [`Block`] being built.600pub fn ins<'short>(&'short mut self) -> FuncInstBuilder<'short, 'a> {601let block = self602.position603.expect("Please call switch_to_block before inserting instructions");604FuncInstBuilder::new(self, block)605}606607/// Make sure that the current block is inserted in the layout.608pub fn ensure_inserted_block(&mut self) {609let block = self.position.unwrap();610if self.is_pristine(block) {611if !self.func.layout.is_block_inserted(block) {612self.func.layout.append_block(block);613}614self.func_ctx.status[block] = BlockStatus::Partial;615} else {616debug_assert!(617!self.is_filled(block),618"you cannot add an instruction to a block already filled"619);620}621}622623/// Returns a [`FuncCursor`] pointed at the current position ready for inserting instructions.624///625/// This can be used to insert SSA code that doesn't need to access locals and that doesn't626/// need to know about [`FunctionBuilder`] at all.627pub fn cursor(&mut self) -> FuncCursor<'_> {628self.ensure_inserted_block();629FuncCursor::new(self.func)630.with_srcloc(self.srcloc)631.at_bottom(self.position.unwrap())632}633634/// Append parameters to the given [`Block`] corresponding to the function635/// parameters. This can be used to set up the block parameters for the636/// entry block.637pub fn append_block_params_for_function_params(&mut self, block: Block) {638debug_assert!(639!self.func_ctx.ssa.has_any_predecessors(block),640"block parameters for function parameters should only be added to the entry block"641);642643// These parameters count as "user" parameters here because they aren't644// inserted by the SSABuilder.645debug_assert!(646self.is_pristine(block),647"You can't add block parameters after adding any instruction"648);649650for argtyp in &self.func.stencil.signature.params {651self.func652.stencil653.dfg654.append_block_param(block, argtyp.value_type);655}656}657658/// Append parameters to the given [`Block`] corresponding to the function659/// return values. This can be used to set up the block parameters for a660/// function exit block.661pub fn append_block_params_for_function_returns(&mut self, block: Block) {662// These parameters count as "user" parameters here because they aren't663// inserted by the SSABuilder.664debug_assert!(665self.is_pristine(block),666"You can't add block parameters after adding any instruction"667);668669for argtyp in &self.func.stencil.signature.returns {670self.func671.stencil672.dfg673.append_block_param(block, argtyp.value_type);674}675}676677/// Declare that translation of the current function is complete.678///679/// This resets the state of the [`FunctionBuilderContext`] in preparation to680/// be used for another function.681pub fn finalize(mut self) {682// Check that all the `Block`s are filled and sealed.683#[cfg(debug_assertions)]684{685for block in self.func_ctx.status.keys() {686if !self.is_pristine(block) {687assert!(688self.func_ctx.ssa.is_sealed(block),689"FunctionBuilder finalized, but block {block} is not sealed",690);691assert!(692self.is_filled(block),693"FunctionBuilder finalized, but block {block} is not filled",694);695}696}697}698699// In debug mode, check that all blocks are valid basic blocks.700#[cfg(debug_assertions)]701{702// Iterate manually to provide more helpful error messages.703for block in self.func_ctx.status.keys() {704if let Err((inst, msg)) = self.func.is_block_basic(block) {705let inst_str = self.func.dfg.display_inst(inst);706panic!("{block} failed basic block invariants on {inst_str}: {msg}");707}708}709}710711// Propagate the needs-stack-map bit from variables to each of their712// associated values.713for var in self.func_ctx.stack_map_vars.iter() {714for val in self.func_ctx.ssa.values_for_var(var) {715log::trace!("propagating needs-stack-map from {var:?} to {val:?}");716debug_assert_eq!(self.func.dfg.value_type(val), self.func_ctx.variables[var]);717self.func_ctx.stack_map_values.insert(val);718}719}720721// If we have any values that need inclusion in stack maps, then we need722// to run our pass to spill those values to the stack at safepoints and723// generate stack maps.724if !self.func_ctx.stack_map_values.is_empty() {725self.func_ctx726.safepoints727.run(&mut self.func, &self.func_ctx.stack_map_values);728}729730// Clear the state (but preserve the allocated buffers) in preparation731// for translation another function.732self.func_ctx.clear();733}734}735736/// All the functions documented in the previous block are write-only and help you build a valid737/// Cranelift IR functions via multiple debug asserts. However, you might need to improve the738/// performance of your translation perform more complex transformations to your Cranelift IR739/// function. The functions below help you inspect the function you're creating and modify it740/// in ways that can be unsafe if used incorrectly.741impl<'a> FunctionBuilder<'a> {742/// Retrieves all the parameters for a [`Block`] currently inferred from the jump instructions743/// inserted that target it and the SSA construction.744pub fn block_params(&self, block: Block) -> &[Value] {745self.func.dfg.block_params(block)746}747748/// Retrieves the signature with reference `sigref` previously added with749/// [`import_signature`](Self::import_signature).750pub fn signature(&self, sigref: SigRef) -> Option<&Signature> {751self.func.dfg.signatures.get(sigref)752}753754/// Creates a parameter for a specific [`Block`] by appending it to the list of already existing755/// parameters.756///757/// **Note:** this function has to be called at the creation of the `Block` before adding758/// instructions to it, otherwise this could interfere with SSA construction.759pub fn append_block_param(&mut self, block: Block, ty: Type) -> Value {760debug_assert!(761self.is_pristine(block),762"You can't add block parameters after adding any instruction"763);764self.func.dfg.append_block_param(block, ty)765}766767/// Returns the result values of an instruction.768pub fn inst_results(&self, inst: Inst) -> &[Value] {769self.func.dfg.inst_results(inst)770}771772/// Changes the destination of a jump instruction after creation.773///774/// **Note:** You are responsible for maintaining the coherence with the arguments of775/// other jump instructions.776pub fn change_jump_destination(&mut self, inst: Inst, old_block: Block, new_block: Block) {777let dfg = &mut self.func.dfg;778for block in779dfg.insts[inst].branch_destination_mut(&mut dfg.jump_tables, &mut dfg.exception_tables)780{781if block.block(&dfg.value_lists) == old_block {782self.func_ctx.ssa.remove_block_predecessor(old_block, inst);783block.set_block(new_block, &mut dfg.value_lists);784self.func_ctx.ssa.declare_block_predecessor(new_block, inst);785}786}787}788789/// Returns `true` if and only if the current [`Block`] is sealed and has no predecessors declared.790///791/// The entry block of a function is never unreachable.792pub fn is_unreachable(&self) -> bool {793let is_entry = match self.func.layout.entry_block() {794None => false,795Some(entry) => self.position.unwrap() == entry,796};797!is_entry798&& self.func_ctx.ssa.is_sealed(self.position.unwrap())799&& !self800.func_ctx801.ssa802.has_any_predecessors(self.position.unwrap())803}804805/// Returns `true` if and only if no instructions have been added since the last call to806/// [`switch_to_block`](Self::switch_to_block).807fn is_pristine(&self, block: Block) -> bool {808self.func_ctx.status[block] == BlockStatus::Empty809}810811/// Returns `true` if and only if a terminator instruction has been inserted since the812/// last call to [`switch_to_block`](Self::switch_to_block).813fn is_filled(&self, block: Block) -> bool {814self.func_ctx.status[block] == BlockStatus::Filled815}816}817818/// Helper functions819impl<'a> FunctionBuilder<'a> {820/// Calls libc.memcpy821///822/// Copies the `size` bytes from `src` to `dest`, assumes that `src + size`823/// won't overlap onto `dest`. If `dest` and `src` overlap, the behavior is824/// undefined. Applications in which `dest` and `src` might overlap should825/// use `call_memmove` instead.826pub fn call_memcpy(827&mut self,828config: TargetFrontendConfig,829dest: Value,830src: Value,831size: Value,832) {833let pointer_type = config.pointer_type();834let signature = {835let mut s = Signature::new(config.default_call_conv);836s.params.push(AbiParam::new(pointer_type));837s.params.push(AbiParam::new(pointer_type));838s.params.push(AbiParam::new(pointer_type));839s.returns.push(AbiParam::new(pointer_type));840self.import_signature(s)841};842843let libc_memcpy = self.import_function(ExtFuncData {844name: ExternalName::LibCall(LibCall::Memcpy),845signature,846colocated: false,847patchable: false,848});849850self.ins().call(libc_memcpy, &[dest, src, size]);851}852853/// Optimised memcpy or memmove for small copies.854///855/// # Codegen safety856///857/// The following properties must hold to prevent UB:858///859/// * `src_align` and `dest_align` are an upper-bound on the alignment of `src` respectively `dest`.860/// * If `non_overlapping` is true, then this must be correct.861pub fn emit_small_memory_copy(862&mut self,863config: TargetFrontendConfig,864dest: Value,865src: Value,866size: u64,867dest_align: u8,868src_align: u8,869non_overlapping: bool,870mut flags: MemFlags,871) {872// Currently the result of guess work, not actual profiling.873const THRESHOLD: u64 = 4;874875if size == 0 {876return;877}878879let access_size = greatest_divisible_power_of_two(size);880assert!(881access_size.is_power_of_two(),882"`size` is not a power of two"883);884assert!(885access_size >= u64::from(::core::cmp::min(src_align, dest_align)),886"`size` is smaller than `dest` and `src`'s alignment value."887);888889let (access_size, int_type) = if access_size <= 8 {890(access_size, Type::int((access_size * 8) as u16).unwrap())891} else {892(8, types::I64)893};894895let load_and_store_amount = size / access_size;896897if load_and_store_amount > THRESHOLD {898let size_value = self.ins().iconst(config.pointer_type(), size as i64);899if non_overlapping {900self.call_memcpy(config, dest, src, size_value);901} else {902self.call_memmove(config, dest, src, size_value);903}904return;905}906907if u64::from(src_align) >= access_size && u64::from(dest_align) >= access_size {908flags.set_aligned();909}910911// Load all of the memory first. This is necessary in case `dest` overlaps.912// It can also improve performance a bit.913let registers: smallvec::SmallVec<[_; THRESHOLD as usize]> = (0..load_and_store_amount)914.map(|i| {915let offset = (access_size * i) as i32;916(self.ins().load(int_type, flags, src, offset), offset)917})918.collect();919920for (value, offset) in registers {921self.ins().store(flags, value, dest, offset);922}923}924925/// Calls libc.memset926///927/// Writes `size` bytes of i8 value `ch` to memory starting at `buffer`.928pub fn call_memset(929&mut self,930config: TargetFrontendConfig,931buffer: Value,932ch: Value,933size: Value,934) {935let pointer_type = config.pointer_type();936let signature = {937let mut s = Signature::new(config.default_call_conv);938s.params.push(AbiParam::new(pointer_type));939s.params.push(AbiParam::new(types::I32));940s.params.push(AbiParam::new(pointer_type));941s.returns.push(AbiParam::new(pointer_type));942self.import_signature(s)943};944945let libc_memset = self.import_function(ExtFuncData {946name: ExternalName::LibCall(LibCall::Memset),947signature,948colocated: false,949patchable: false,950});951952let ch = self.ins().uextend(types::I32, ch);953self.ins().call(libc_memset, &[buffer, ch, size]);954}955956/// Calls libc.memset957///958/// Writes `size` bytes of value `ch` to memory starting at `buffer`.959pub fn emit_small_memset(960&mut self,961config: TargetFrontendConfig,962buffer: Value,963ch: u8,964size: u64,965buffer_align: u8,966mut flags: MemFlags,967) {968// Currently the result of guess work, not actual profiling.969const THRESHOLD: u64 = 4;970971if size == 0 {972return;973}974975let access_size = greatest_divisible_power_of_two(size);976assert!(977access_size.is_power_of_two(),978"`size` is not a power of two"979);980assert!(981access_size >= u64::from(buffer_align),982"`size` is smaller than `dest` and `src`'s alignment value."983);984985let (access_size, int_type) = if access_size <= 8 {986(access_size, Type::int((access_size * 8) as u16).unwrap())987} else {988(8, types::I64)989};990991let load_and_store_amount = size / access_size;992993if load_and_store_amount > THRESHOLD {994let ch = self.ins().iconst(types::I8, i64::from(ch));995let size = self.ins().iconst(config.pointer_type(), size as i64);996self.call_memset(config, buffer, ch, size);997} else {998if u64::from(buffer_align) >= access_size {999flags.set_aligned();1000}10011002let ch = u64::from(ch);1003let raw_value = if int_type == types::I64 {1004ch * 0x0101010101010101_u641005} else if int_type == types::I32 {1006ch * 0x01010101_u641007} else if int_type == types::I16 {1008(ch << 8) | ch1009} else {1010assert_eq!(int_type, types::I8);1011ch1012};10131014let value = self.ins().iconst(int_type, raw_value as i64);1015for i in 0..load_and_store_amount {1016let offset = (access_size * i) as i32;1017self.ins().store(flags, value, buffer, offset);1018}1019}1020}10211022/// Calls libc.memmove1023///1024/// Copies `size` bytes from memory starting at `source` to memory starting1025/// at `dest`. `source` is always read before writing to `dest`.1026pub fn call_memmove(1027&mut self,1028config: TargetFrontendConfig,1029dest: Value,1030source: Value,1031size: Value,1032) {1033let pointer_type = config.pointer_type();1034let signature = {1035let mut s = Signature::new(config.default_call_conv);1036s.params.push(AbiParam::new(pointer_type));1037s.params.push(AbiParam::new(pointer_type));1038s.params.push(AbiParam::new(pointer_type));1039s.returns.push(AbiParam::new(pointer_type));1040self.import_signature(s)1041};10421043let libc_memmove = self.import_function(ExtFuncData {1044name: ExternalName::LibCall(LibCall::Memmove),1045signature,1046colocated: false,1047patchable: false,1048});10491050self.ins().call(libc_memmove, &[dest, source, size]);1051}10521053/// Calls libc.memcmp1054///1055/// Compares `size` bytes from memory starting at `left` to memory starting1056/// at `right`. Returns `0` if all `n` bytes are equal. If the first difference1057/// is at offset `i`, returns a positive integer if `ugt(left[i], right[i])`1058/// and a negative integer if `ult(left[i], right[i])`.1059///1060/// Returns a C `int`, which is currently always [`types::I32`].1061pub fn call_memcmp(1062&mut self,1063config: TargetFrontendConfig,1064left: Value,1065right: Value,1066size: Value,1067) -> Value {1068let pointer_type = config.pointer_type();1069let signature = {1070let mut s = Signature::new(config.default_call_conv);1071s.params.reserve(3);1072s.params.push(AbiParam::new(pointer_type));1073s.params.push(AbiParam::new(pointer_type));1074s.params.push(AbiParam::new(pointer_type));1075s.returns.push(AbiParam::new(types::I32));1076self.import_signature(s)1077};10781079let libc_memcmp = self.import_function(ExtFuncData {1080name: ExternalName::LibCall(LibCall::Memcmp),1081signature,1082colocated: false,1083patchable: false,1084});10851086let call = self.ins().call(libc_memcmp, &[left, right, size]);1087self.func.dfg.first_result(call)1088}10891090/// Optimised [`Self::call_memcmp`] for small copies.1091///1092/// This implements the byte slice comparison `int_cc(left[..size], right[..size])`.1093///1094/// `left_align` and `right_align` are the statically-known alignments of the1095/// `left` and `right` pointers respectively. These are used to know whether1096/// to mark `load`s as aligned. It's always fine to pass `1` for these, but1097/// passing something higher than the true alignment may trap or otherwise1098/// misbehave as described in [`MemFlags::aligned`].1099///1100/// Note that `memcmp` is a *big-endian* and *unsigned* comparison.1101/// As such, this panics when called with `IntCC::Signed*`.1102pub fn emit_small_memory_compare(1103&mut self,1104config: TargetFrontendConfig,1105int_cc: IntCC,1106left: Value,1107right: Value,1108size: u64,1109left_align: std::num::NonZeroU8,1110right_align: std::num::NonZeroU8,1111flags: MemFlags,1112) -> Value {1113use IntCC::*;1114let (zero_cc, empty_imm) = match int_cc {1115//1116Equal => (Equal, 1),1117NotEqual => (NotEqual, 0),11181119UnsignedLessThan => (SignedLessThan, 0),1120UnsignedGreaterThanOrEqual => (SignedGreaterThanOrEqual, 1),1121UnsignedGreaterThan => (SignedGreaterThan, 0),1122UnsignedLessThanOrEqual => (SignedLessThanOrEqual, 1),11231124SignedLessThan1125| SignedGreaterThanOrEqual1126| SignedGreaterThan1127| SignedLessThanOrEqual => {1128panic!("Signed comparison {int_cc} not supported by memcmp")1129}1130};11311132if size == 0 {1133return self.ins().iconst(types::I8, empty_imm);1134}11351136// Future work could consider expanding this to handle more-complex scenarios.1137if let Some(small_type) = size.try_into().ok().and_then(Type::int_with_byte_size) {1138if let Equal | NotEqual = zero_cc {1139let mut left_flags = flags;1140if size == left_align.get() as u64 {1141left_flags.set_aligned();1142}1143let mut right_flags = flags;1144if size == right_align.get() as u64 {1145right_flags.set_aligned();1146}1147let left_val = self.ins().load(small_type, left_flags, left, 0);1148let right_val = self.ins().load(small_type, right_flags, right, 0);1149return self.ins().icmp(int_cc, left_val, right_val);1150} else if small_type == types::I8 {1151// Once the big-endian loads from wasmtime#2492 are implemented in1152// the backends, we could easily handle comparisons for more sizes here.1153// But for now, just handle single bytes where we don't need to worry.11541155let mut aligned_flags = flags;1156aligned_flags.set_aligned();1157let left_val = self.ins().load(small_type, aligned_flags, left, 0);1158let right_val = self.ins().load(small_type, aligned_flags, right, 0);1159return self.ins().icmp(int_cc, left_val, right_val);1160}1161}11621163let pointer_type = config.pointer_type();1164let size = self.ins().iconst(pointer_type, size as i64);1165let cmp = self.call_memcmp(config, left, right, size);1166self.ins().icmp_imm(zero_cc, cmp, 0)1167}1168}11691170fn greatest_divisible_power_of_two(size: u64) -> u64 {1171(size as i64 & -(size as i64)) as u641172}11731174// Helper functions1175impl<'a> FunctionBuilder<'a> {1176/// A Block is 'filled' when a terminator instruction is present.1177fn fill_current_block(&mut self) {1178self.func_ctx.status[self.position.unwrap()] = BlockStatus::Filled;1179}11801181fn declare_successor(&mut self, dest_block: Block, jump_inst: Inst) {1182self.func_ctx1183.ssa1184.declare_block_predecessor(dest_block, jump_inst);1185}11861187fn handle_ssa_side_effects(&mut self, side_effects: SideEffects) {1188let SideEffects {1189instructions_added_to_blocks,1190} = side_effects;11911192for modified_block in instructions_added_to_blocks {1193if self.is_pristine(modified_block) {1194self.func_ctx.status[modified_block] = BlockStatus::Partial;1195}1196}1197}1198}11991200#[cfg(test)]1201mod tests {1202use super::greatest_divisible_power_of_two;1203use crate::Variable;1204use crate::frontend::{1205DefVariableError, FunctionBuilder, FunctionBuilderContext, UseVariableError,1206};1207use alloc::string::ToString;1208use cranelift_codegen::ir::condcodes::IntCC;1209use cranelift_codegen::ir::{1210AbiParam, BlockCall, ExceptionTableData, ExtFuncData, ExternalName, Function, InstBuilder,1211MemFlags, Signature, UserExternalName, UserFuncName, Value, types::*,1212};1213use cranelift_codegen::isa::{CallConv, TargetFrontendConfig, TargetIsa};1214use cranelift_codegen::settings;1215use cranelift_codegen::verifier::verify_function;1216use target_lexicon::PointerWidth;12171218fn sample_function(lazy_seal: bool) {1219let mut sig = Signature::new(CallConv::SystemV);1220sig.returns.push(AbiParam::new(I32));1221sig.params.push(AbiParam::new(I32));12221223let mut fn_ctx = FunctionBuilderContext::new();1224let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);1225{1226let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);12271228let block0 = builder.create_block();1229let block1 = builder.create_block();1230let block2 = builder.create_block();1231let block3 = builder.create_block();1232let x = builder.declare_var(I32);1233let y = builder.declare_var(I32);1234let z = builder.declare_var(I32);12351236builder.append_block_params_for_function_params(block0);12371238builder.switch_to_block(block0);1239if !lazy_seal {1240builder.seal_block(block0);1241}1242{1243let tmp = builder.block_params(block0)[0]; // the first function parameter1244builder.def_var(x, tmp);1245}1246{1247let tmp = builder.ins().iconst(I32, 2);1248builder.def_var(y, tmp);1249}1250{1251let arg1 = builder.use_var(x);1252let arg2 = builder.use_var(y);1253let tmp = builder.ins().iadd(arg1, arg2);1254builder.def_var(z, tmp);1255}1256builder.ins().jump(block1, &[]);12571258builder.switch_to_block(block1);1259{1260let arg1 = builder.use_var(y);1261let arg2 = builder.use_var(z);1262let tmp = builder.ins().iadd(arg1, arg2);1263builder.def_var(z, tmp);1264}1265{1266let arg = builder.use_var(y);1267builder.ins().brif(arg, block3, &[], block2, &[]);1268}12691270builder.switch_to_block(block2);1271if !lazy_seal {1272builder.seal_block(block2);1273}1274{1275let arg1 = builder.use_var(z);1276let arg2 = builder.use_var(x);1277let tmp = builder.ins().isub(arg1, arg2);1278builder.def_var(z, tmp);1279}1280{1281let arg = builder.use_var(y);1282builder.ins().return_(&[arg]);1283}12841285builder.switch_to_block(block3);1286if !lazy_seal {1287builder.seal_block(block3);1288}12891290{1291let arg1 = builder.use_var(y);1292let arg2 = builder.use_var(x);1293let tmp = builder.ins().isub(arg1, arg2);1294builder.def_var(y, tmp);1295}1296builder.ins().jump(block1, &[]);1297if !lazy_seal {1298builder.seal_block(block1);1299}13001301if lazy_seal {1302builder.seal_all_blocks();1303}13041305builder.finalize();1306}13071308let flags = settings::Flags::new(settings::builder());1309// println!("{}", func.display(None));1310if let Err(errors) = verify_function(&func, &flags) {1311panic!("{}\n{}", func.display(), errors)1312}1313}13141315#[test]1316fn sample() {1317sample_function(false)1318}13191320#[test]1321fn sample_with_lazy_seal() {1322sample_function(true)1323}13241325#[track_caller]1326fn check(func: &Function, expected_ir: &str) {1327let expected_ir = expected_ir.trim();1328let actual_ir = func.display().to_string();1329let actual_ir = actual_ir.trim();1330assert!(1331expected_ir == actual_ir,1332"Expected:\n{expected_ir}\nGot:\n{actual_ir}"1333);1334}13351336/// Helper function to construct a fixed frontend configuration.1337fn systemv_frontend_config() -> TargetFrontendConfig {1338TargetFrontendConfig {1339default_call_conv: CallConv::SystemV,1340pointer_width: PointerWidth::U64,1341page_size_align_log2: 12,1342}1343}13441345#[test]1346fn memcpy() {1347let frontend_config = systemv_frontend_config();1348let mut sig = Signature::new(frontend_config.default_call_conv);1349sig.returns.push(AbiParam::new(I32));13501351let mut fn_ctx = FunctionBuilderContext::new();1352let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);1353{1354let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);13551356let block0 = builder.create_block();1357let x = builder.declare_var(frontend_config.pointer_type());1358let y = builder.declare_var(frontend_config.pointer_type());1359let _z = builder.declare_var(I32);13601361builder.append_block_params_for_function_params(block0);1362builder.switch_to_block(block0);13631364let src = builder.use_var(x);1365let dest = builder.use_var(y);1366let size = builder.use_var(y);1367builder.call_memcpy(frontend_config, dest, src, size);1368builder.ins().return_(&[size]);13691370builder.seal_all_blocks();1371builder.finalize();1372}13731374check(1375&func,1376"function %sample() -> i32 system_v {1377sig0 = (i64, i64, i64) -> i64 system_v1378fn0 = %Memcpy sig013791380block0:1381v4 = iconst.i64 01382v1 -> v41383v3 = iconst.i64 01384v0 -> v31385v2 = call fn0(v1, v0, v1) ; v1 = 0, v0 = 0, v1 = 01386return v1 ; v1 = 01387}1388",1389);1390}13911392#[test]1393fn small_memcpy() {1394let frontend_config = systemv_frontend_config();1395let mut sig = Signature::new(frontend_config.default_call_conv);1396sig.returns.push(AbiParam::new(I32));13971398let mut fn_ctx = FunctionBuilderContext::new();1399let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);1400{1401let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);14021403let block0 = builder.create_block();1404let x = builder.declare_var(frontend_config.pointer_type());1405let y = builder.declare_var(frontend_config.pointer_type());14061407builder.append_block_params_for_function_params(block0);1408builder.switch_to_block(block0);14091410let src = builder.use_var(x);1411let dest = builder.use_var(y);1412let size = 8;1413builder.emit_small_memory_copy(1414frontend_config,1415dest,1416src,1417size,14188,14198,1420true,1421MemFlags::new(),1422);1423builder.ins().return_(&[dest]);14241425builder.seal_all_blocks();1426builder.finalize();1427}14281429check(1430&func,1431"function %sample() -> i32 system_v {1432block0:1433v4 = iconst.i64 01434v1 -> v41435v3 = iconst.i64 01436v0 -> v31437v2 = load.i64 aligned v0 ; v0 = 01438store aligned v2, v1 ; v1 = 01439return v1 ; v1 = 01440}1441",1442);1443}14441445#[test]1446fn not_so_small_memcpy() {1447let frontend_config = systemv_frontend_config();1448let mut sig = Signature::new(frontend_config.default_call_conv);1449sig.returns.push(AbiParam::new(I32));14501451let mut fn_ctx = FunctionBuilderContext::new();1452let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);1453{1454let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);14551456let block0 = builder.create_block();1457let x = builder.declare_var(frontend_config.pointer_type());1458let y = builder.declare_var(frontend_config.pointer_type());1459builder.append_block_params_for_function_params(block0);1460builder.switch_to_block(block0);14611462let src = builder.use_var(x);1463let dest = builder.use_var(y);1464let size = 8192;1465builder.emit_small_memory_copy(1466frontend_config,1467dest,1468src,1469size,14708,14718,1472true,1473MemFlags::new(),1474);1475builder.ins().return_(&[dest]);14761477builder.seal_all_blocks();1478builder.finalize();1479}14801481check(1482&func,1483"function %sample() -> i32 system_v {1484sig0 = (i64, i64, i64) -> i64 system_v1485fn0 = %Memcpy sig014861487block0:1488v5 = iconst.i64 01489v1 -> v51490v4 = iconst.i64 01491v0 -> v41492v2 = iconst.i64 81921493v3 = call fn0(v1, v0, v2) ; v1 = 0, v0 = 0, v2 = 81921494return v1 ; v1 = 01495}1496",1497);1498}14991500#[test]1501fn small_memset() {1502let frontend_config = systemv_frontend_config();1503let mut sig = Signature::new(frontend_config.default_call_conv);1504sig.returns.push(AbiParam::new(I32));15051506let mut fn_ctx = FunctionBuilderContext::new();1507let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);1508{1509let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);15101511let block0 = builder.create_block();1512let y = builder.declare_var(frontend_config.pointer_type());1513builder.append_block_params_for_function_params(block0);1514builder.switch_to_block(block0);15151516let dest = builder.use_var(y);1517let size = 8;1518builder.emit_small_memset(frontend_config, dest, 1, size, 8, MemFlags::new());1519builder.ins().return_(&[dest]);15201521builder.seal_all_blocks();1522builder.finalize();1523}15241525check(1526&func,1527"function %sample() -> i32 system_v {1528block0:1529v2 = iconst.i64 01530v0 -> v21531v1 = iconst.i64 0x0101_0101_0101_01011532store aligned v1, v0 ; v1 = 0x0101_0101_0101_0101, v0 = 01533return v0 ; v0 = 01534}1535",1536);1537}15381539#[test]1540fn not_so_small_memset() {1541let frontend_config = systemv_frontend_config();1542let mut sig = Signature::new(frontend_config.default_call_conv);1543sig.returns.push(AbiParam::new(I32));15441545let mut fn_ctx = FunctionBuilderContext::new();1546let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);1547{1548let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);15491550let block0 = builder.create_block();1551let y = builder.declare_var(frontend_config.pointer_type());1552builder.append_block_params_for_function_params(block0);1553builder.switch_to_block(block0);15541555let dest = builder.use_var(y);1556let size = 8192;1557builder.emit_small_memset(frontend_config, dest, 1, size, 8, MemFlags::new());1558builder.ins().return_(&[dest]);15591560builder.seal_all_blocks();1561builder.finalize();1562}15631564check(1565&func,1566"function %sample() -> i32 system_v {1567sig0 = (i64, i32, i64) -> i64 system_v1568fn0 = %Memset sig015691570block0:1571v5 = iconst.i64 01572v0 -> v51573v1 = iconst.i8 11574v2 = iconst.i64 81921575v3 = uextend.i32 v1 ; v1 = 11576v4 = call fn0(v0, v3, v2) ; v0 = 0, v2 = 81921577return v0 ; v0 = 01578}1579",1580);1581}15821583#[test]1584fn memcmp() {1585use core::str::FromStr;1586use cranelift_codegen::isa;15871588let shared_builder = settings::builder();1589let shared_flags = settings::Flags::new(shared_builder);15901591let triple =1592::target_lexicon::Triple::from_str("x86_64").expect("Couldn't create x86_64 triple");15931594let target = isa::lookup(triple)1595.ok()1596.map(|b| b.finish(shared_flags))1597.expect("This test requires x86_64 support.")1598.expect("Should be able to create backend with default flags");15991600let mut sig = Signature::new(target.default_call_conv());1601sig.returns.push(AbiParam::new(I32));16021603let mut fn_ctx = FunctionBuilderContext::new();1604let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);1605{1606let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);16071608let block0 = builder.create_block();1609let x = builder.declare_var(target.pointer_type());1610let y = builder.declare_var(target.pointer_type());1611let z = builder.declare_var(target.pointer_type());1612builder.append_block_params_for_function_params(block0);1613builder.switch_to_block(block0);16141615let left = builder.use_var(x);1616let right = builder.use_var(y);1617let size = builder.use_var(z);1618let cmp = builder.call_memcmp(target.frontend_config(), left, right, size);1619builder.ins().return_(&[cmp]);16201621builder.seal_all_blocks();1622builder.finalize();1623}16241625check(1626&func,1627"function %sample() -> i32 system_v {1628sig0 = (i64, i64, i64) -> i32 system_v1629fn0 = %Memcmp sig016301631block0:1632v6 = iconst.i64 01633v2 -> v61634v5 = iconst.i64 01635v1 -> v51636v4 = iconst.i64 01637v0 -> v41638v3 = call fn0(v0, v1, v2) ; v0 = 0, v1 = 0, v2 = 01639return v31640}1641",1642);1643}16441645#[test]1646fn small_memcmp_zero_size() {1647let align_eight = std::num::NonZeroU8::new(8).unwrap();1648small_memcmp_helper(1649"1650block0:1651v4 = iconst.i64 01652v1 -> v41653v3 = iconst.i64 01654v0 -> v31655v2 = iconst.i8 11656return v2 ; v2 = 1",1657|builder, target, x, y| {1658builder.emit_small_memory_compare(1659target.frontend_config(),1660IntCC::UnsignedGreaterThanOrEqual,1661x,1662y,16630,1664align_eight,1665align_eight,1666MemFlags::new(),1667)1668},1669);1670}16711672#[test]1673fn small_memcmp_byte_ugt() {1674let align_one = std::num::NonZeroU8::new(1).unwrap();1675small_memcmp_helper(1676"1677block0:1678v6 = iconst.i64 01679v1 -> v61680v5 = iconst.i64 01681v0 -> v51682v2 = load.i8 aligned v0 ; v0 = 01683v3 = load.i8 aligned v1 ; v1 = 01684v4 = icmp ugt v2, v31685return v4",1686|builder, target, x, y| {1687builder.emit_small_memory_compare(1688target.frontend_config(),1689IntCC::UnsignedGreaterThan,1690x,1691y,16921,1693align_one,1694align_one,1695MemFlags::new(),1696)1697},1698);1699}17001701#[test]1702fn small_memcmp_aligned_eq() {1703let align_four = std::num::NonZeroU8::new(4).unwrap();1704small_memcmp_helper(1705"1706block0:1707v6 = iconst.i64 01708v1 -> v61709v5 = iconst.i64 01710v0 -> v51711v2 = load.i32 aligned v0 ; v0 = 01712v3 = load.i32 aligned v1 ; v1 = 01713v4 = icmp eq v2, v31714return v4",1715|builder, target, x, y| {1716builder.emit_small_memory_compare(1717target.frontend_config(),1718IntCC::Equal,1719x,1720y,17214,1722align_four,1723align_four,1724MemFlags::new(),1725)1726},1727);1728}17291730#[test]1731fn small_memcmp_ipv6_ne() {1732let align_two = std::num::NonZeroU8::new(2).unwrap();1733small_memcmp_helper(1734"1735block0:1736v6 = iconst.i64 01737v1 -> v61738v5 = iconst.i64 01739v0 -> v51740v2 = load.i128 v0 ; v0 = 01741v3 = load.i128 v1 ; v1 = 01742v4 = icmp ne v2, v31743return v4",1744|builder, target, x, y| {1745builder.emit_small_memory_compare(1746target.frontend_config(),1747IntCC::NotEqual,1748x,1749y,175016,1751align_two,1752align_two,1753MemFlags::new(),1754)1755},1756);1757}17581759#[test]1760fn small_memcmp_odd_size_uge() {1761let one = std::num::NonZeroU8::new(1).unwrap();1762small_memcmp_helper(1763"1764sig0 = (i64, i64, i64) -> i32 system_v1765fn0 = %Memcmp sig017661767block0:1768v6 = iconst.i64 01769v1 -> v61770v5 = iconst.i64 01771v0 -> v51772v2 = iconst.i64 31773v3 = call fn0(v0, v1, v2) ; v0 = 0, v1 = 0, v2 = 31774v4 = icmp_imm sge v3, 01775return v4",1776|builder, target, x, y| {1777builder.emit_small_memory_compare(1778target.frontend_config(),1779IntCC::UnsignedGreaterThanOrEqual,1780x,1781y,17823,1783one,1784one,1785MemFlags::new(),1786)1787},1788);1789}17901791fn small_memcmp_helper(1792expected: &str,1793f: impl FnOnce(&mut FunctionBuilder, &dyn TargetIsa, Value, Value) -> Value,1794) {1795use core::str::FromStr;1796use cranelift_codegen::isa;17971798let shared_builder = settings::builder();1799let shared_flags = settings::Flags::new(shared_builder);18001801let triple =1802::target_lexicon::Triple::from_str("x86_64").expect("Couldn't create x86_64 triple");18031804let target = isa::lookup(triple)1805.ok()1806.map(|b| b.finish(shared_flags))1807.expect("This test requires x86_64 support.")1808.expect("Should be able to create backend with default flags");18091810let mut sig = Signature::new(target.default_call_conv());1811sig.returns.push(AbiParam::new(I8));18121813let mut fn_ctx = FunctionBuilderContext::new();1814let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);1815{1816let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);18171818let block0 = builder.create_block();1819let x = builder.declare_var(target.pointer_type());1820let y = builder.declare_var(target.pointer_type());1821builder.append_block_params_for_function_params(block0);1822builder.switch_to_block(block0);18231824let left = builder.use_var(x);1825let right = builder.use_var(y);1826let ret = f(&mut builder, &*target, left, right);1827builder.ins().return_(&[ret]);18281829builder.seal_all_blocks();1830builder.finalize();1831}18321833check(1834&func,1835&format!("function %sample() -> i8 system_v {{{expected}\n}}\n"),1836);1837}18381839#[test]1840fn undef_vector_vars() {1841let mut sig = Signature::new(CallConv::SystemV);1842sig.returns.push(AbiParam::new(I8X16));1843sig.returns.push(AbiParam::new(I8X16));1844sig.returns.push(AbiParam::new(F32X4));18451846let mut fn_ctx = FunctionBuilderContext::new();1847let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);1848{1849let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);18501851let block0 = builder.create_block();1852let a = builder.declare_var(I8X16);1853let b = builder.declare_var(I8X16);1854let c = builder.declare_var(F32X4);1855builder.switch_to_block(block0);18561857let a = builder.use_var(a);1858let b = builder.use_var(b);1859let c = builder.use_var(c);1860builder.ins().return_(&[a, b, c]);18611862builder.seal_all_blocks();1863builder.finalize();1864}18651866check(1867&func,1868"function %sample() -> i8x16, i8x16, f32x4 system_v {1869const0 = 0x0000000000000000000000000000000018701871block0:1872v5 = f32const 0.01873v6 = splat.f32x4 v5 ; v5 = 0.01874v2 -> v61875v4 = vconst.i8x16 const01876v1 -> v41877v3 = vconst.i8x16 const01878v0 -> v31879return v0, v1, v2 ; v0 = const0, v1 = const01880}1881",1882);1883}18841885#[test]1886fn test_greatest_divisible_power_of_two() {1887assert_eq!(64, greatest_divisible_power_of_two(64));1888assert_eq!(16, greatest_divisible_power_of_two(48));1889assert_eq!(8, greatest_divisible_power_of_two(24));1890assert_eq!(1, greatest_divisible_power_of_two(25));1891}18921893#[test]1894fn try_use_var() {1895let sig = Signature::new(CallConv::SystemV);18961897let mut fn_ctx = FunctionBuilderContext::new();1898let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);1899{1900let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);19011902let block0 = builder.create_block();1903builder.append_block_params_for_function_params(block0);1904builder.switch_to_block(block0);19051906assert_eq!(1907builder.try_use_var(Variable::from_u32(0)),1908Err(UseVariableError::UsedBeforeDeclared(Variable::from_u32(0)))1909);19101911let value = builder.ins().iconst(cranelift_codegen::ir::types::I32, 0);19121913assert_eq!(1914builder.try_def_var(Variable::from_u32(0), value),1915Err(DefVariableError::DefinedBeforeDeclared(Variable::from_u32(191601917)))1918);1919}1920}19211922#[test]1923fn test_builder_with_iconst_and_negative_constant() {1924let sig = Signature::new(CallConv::SystemV);1925let mut fn_ctx = FunctionBuilderContext::new();1926let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);19271928let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);19291930let block0 = builder.create_block();1931builder.switch_to_block(block0);1932builder.ins().iconst(I32, -1);1933builder.ins().return_(&[]);19341935builder.seal_all_blocks();1936builder.finalize();19371938let flags = cranelift_codegen::settings::Flags::new(cranelift_codegen::settings::builder());1939let ctx = cranelift_codegen::Context::for_function(func);1940ctx.verify(&flags).expect("should be valid");19411942check(1943&ctx.func,1944"function %sample() system_v {1945block0:1946v0 = iconst.i32 -11947return1948}",1949);1950}19511952#[test]1953fn try_call() {1954let mut sig = Signature::new(CallConv::SystemV);1955sig.params.push(AbiParam::new(I8));1956sig.returns.push(AbiParam::new(I32));1957let mut fn_ctx = FunctionBuilderContext::new();1958let mut func = Function::with_name_signature(UserFuncName::testcase("sample"), sig);19591960let sig0 = func.import_signature(Signature::new(CallConv::SystemV));1961let name = func.declare_imported_user_function(UserExternalName::new(0, 0));1962let fn0 = func.import_function(ExtFuncData {1963name: ExternalName::User(name),1964signature: sig0,1965colocated: false,1966patchable: false,1967});19681969let mut builder = FunctionBuilder::new(&mut func, &mut fn_ctx);19701971let block0 = builder.create_block();1972let block1 = builder.create_block();1973let block2 = builder.create_block();1974let block3 = builder.create_block();19751976let my_var = builder.declare_var(I32);19771978builder.switch_to_block(block0);1979let branch_val = builder.append_block_param(block0, I8);1980builder.ins().brif(branch_val, block1, &[], block2, &[]);19811982builder.switch_to_block(block1);1983let one = builder.ins().iconst(I32, 1);1984builder.def_var(my_var, one);19851986let normal_return = BlockCall::new(block3, [], &mut builder.func.dfg.value_lists);1987let exception_table = builder1988.func1989.dfg1990.exception_tables1991.push(ExceptionTableData::new(sig0, normal_return, []));1992builder.ins().try_call(fn0, &[], exception_table);19931994builder.switch_to_block(block2);1995let two = builder.ins().iconst(I32, 2);1996builder.def_var(my_var, two);19971998let normal_return = BlockCall::new(block3, [], &mut builder.func.dfg.value_lists);1999let exception_table = builder2000.func2001.dfg2002.exception_tables2003.push(ExceptionTableData::new(sig0, normal_return, []));2004builder.ins().try_call(fn0, &[], exception_table);20052006builder.switch_to_block(block3);2007let ret_val = builder.use_var(my_var);2008builder.ins().return_(&[ret_val]);20092010builder.seal_all_blocks();2011builder.finalize();20122013let flags = cranelift_codegen::settings::Flags::new(cranelift_codegen::settings::builder());2014let ctx = cranelift_codegen::Context::for_function(func);2015ctx.verify(&flags).expect("should be valid");20162017check(2018&ctx.func,2019"function %sample(i8) -> i32 system_v {2020sig0 = () system_v2021fn0 = u0:0 sig020222023block0(v0: i8):2024brif v0, block1, block220252026block1:2027v1 = iconst.i32 12028try_call fn0(), sig0, block3(v1), [] ; v1 = 120292030block2:2031v2 = iconst.i32 22032try_call fn0(), sig0, block3(v2), [] ; v2 = 220332034block3(v3: i32):2035return v32036}",2037);2038}2039}204020412042