Struct FunctionBuilder
pub struct FunctionBuilder<'a> {
pub func: &'a mut Function,
/* private fields */
}
Expand description
Temporary object used to build a single Cranelift IR [Function
].
Fields§
§func: &'a mut Function
The function currently being built. This field is public so the function can be re-borrowed.
Implementations§
§impl<'a> FunctionBuilder<'a>
impl<'a> FunctionBuilder<'a>
This module allows you to create a function in Cranelift IR in a straightforward way, hiding all the complexity of its internal representation.
The module is parametrized by one type which is the representation of variables in your
origin language. It offers a way to conveniently append instruction to your program flow.
You are responsible to split your instruction flow into extended blocks (declared with
create_block
) whose properties are:
- branch and jump instructions can only point at the top of extended blocks;
- the last instruction of each block is a terminator instruction which has no natural successor, and those instructions can only appear at the end of extended blocks.
The parameters of Cranelift IR instructions are Cranelift IR values, which can only be created
as results of other Cranelift IR instructions. To be able to create variables redefined multiple
times in your program, use the def_var
and use_var
command,
that will maintain the correspondence between your variables and Cranelift IR SSA values.
The first block for which you call switch_to_block
will be assumed to
be the beginning of the function.
At creation, a FunctionBuilder
instance borrows an already allocated Function
which it
modifies with the information stored in the mutable borrowed
FunctionBuilderContext
. The function passed in argument should be newly created with
[Function::with_name_signature()
], whereas the FunctionBuilderContext
can be kept as is
between two function translations.
§Errors
The functions below will panic in debug mode whenever you try to modify the Cranelift IR
function in a way that violate the coherence of the code. For instance: switching to a new
Block
when you haven’t filled the current one with a terminator instruction, inserting a
return instruction with arguments that don’t match the function’s signature.
pub fn new(
func: &'a mut Function,
func_ctx: &'a mut FunctionBuilderContext,
) -> FunctionBuilder<'a>
pub fn new( func: &'a mut Function, func_ctx: &'a mut FunctionBuilderContext, ) -> FunctionBuilder<'a>
Creates a new FunctionBuilder
structure that will operate on a [Function
] using a
FunctionBuilderContext
.
pub fn current_block(&self) -> Option<Block>
pub fn current_block(&self) -> Option<Block>
Get the block that this builder is currently at.
pub fn set_srcloc(&mut self, srcloc: SourceLoc)
pub fn set_srcloc(&mut self, srcloc: SourceLoc)
Set the source location that should be assigned to all new instructions.
pub fn create_block(&mut self) -> Block
pub fn create_block(&mut self) -> Block
Creates a new Block
and returns its reference.
pub fn set_cold_block(&mut self, block: Block)
pub fn set_cold_block(&mut self, block: Block)
Mark a block as “cold”.
This will try to move it out of the ordinary path of execution when lowered to machine code.
pub fn insert_block_after(&mut self, block: Block, after: Block)
pub fn insert_block_after(&mut self, block: Block, after: Block)
Insert block
in the layout after the existing block after
.
pub fn switch_to_block(&mut self, block: Block)
pub fn switch_to_block(&mut self, block: Block)
After the call to this function, new instructions will be inserted into the designated
block, in the order they are declared. You must declare the types of the Block
arguments
you will use here.
When inserting the terminator instruction (which doesn’t have a fallthrough to its immediate successor), the block will be declared filled and it will not be possible to append instructions to it.
pub fn seal_block(&mut self, block: Block)
pub fn seal_block(&mut self, block: Block)
Declares that all the predecessors of this block are known.
Function to call with block
as soon as the last branch instruction to block
has been
created. Forgetting to call this method on every block will cause inconsistencies in the
produced functions.
pub fn seal_all_blocks(&mut self)
pub fn seal_all_blocks(&mut self)
Effectively calls seal_block on all unsealed blocks in the function.
It’s more efficient to seal Block
s as soon as possible, during
translation, but for frontends where this is impractical to do, this
function can be used at the end of translating all blocks to ensure
that everything is sealed.
pub fn try_declare_var(
&mut self,
var: Variable,
ty: Type,
) -> Result<(), DeclareVariableError>
pub fn try_declare_var( &mut self, var: Variable, ty: Type, ) -> Result<(), DeclareVariableError>
Declares the type of a variable.
This allows the variable to be used later (by calling
FunctionBuilder::use_var
).
§Errors
This function will return an error if the variable has been previously declared.
pub fn declare_var(&mut self, var: Variable, ty: Type)
pub fn declare_var(&mut self, var: Variable, ty: Type)
Declares the type of a variable, panicking if it is already declared.
§Panics
Panics if the variable has already been declared.
pub fn declare_var_needs_stack_map(&mut self, var: Variable)
pub fn declare_var_needs_stack_map(&mut self, var: Variable)
Declare that all uses of the given variable must be included in stack map metadata.
All values that are uses of this variable will be spilled to the stack before each safepoint and their location on the stack included in stack maps. Stack maps allow the garbage collector to identify the on-stack GC roots.
This does not affect any pre-existing uses of the variable.
§Panics
Panics if the variable’s type is larger than 16 bytes or if this variable has not been declared yet.
pub fn try_use_var(&mut self, var: Variable) -> Result<Value, UseVariableError>
pub fn try_use_var(&mut self, var: Variable) -> Result<Value, UseVariableError>
Returns the Cranelift IR necessary to use a previously defined user variable, returning an error if this is not possible.
pub fn use_var(&mut self, var: Variable) -> Value
pub fn use_var(&mut self, var: Variable) -> Value
Returns the Cranelift IR value corresponding to the utilization at the current program position of a previously defined user variable.
pub fn try_def_var(
&mut self,
var: Variable,
val: Value,
) -> Result<(), DefVariableError>
pub fn try_def_var( &mut self, var: Variable, val: Value, ) -> Result<(), DefVariableError>
Registers a new definition of a user variable. This function will return an error if the value supplied does not match the type the variable was declared to have.
pub fn def_var(&mut self, var: Variable, val: Value)
pub fn def_var(&mut self, var: Variable, val: Value)
Register a new definition of a user variable. The type of the value must be the same as the type registered for the variable.
pub fn set_val_label(&mut self, val: Value, label: ValueLabel)
pub fn set_val_label(&mut self, val: Value, label: ValueLabel)
Set label for Value
This will not do anything unless
func.dfg.collect_debug_info
is called first.
pub fn declare_value_needs_stack_map(&mut self, val: Value)
pub fn declare_value_needs_stack_map(&mut self, val: Value)
Declare that the given value is a GC reference that requires inclusion in a stack map when it is live across GC safepoints.
At the current moment, values that need inclusion in stack maps are spilled before safepoints, but they are not reloaded afterwards. This means that moving GCs are not yet supported, however the intention is to add this support in the near future.
§Panics
Panics if val
is larger than 16 bytes.
pub fn create_jump_table(&mut self, data: JumpTableData) -> JumpTable
pub fn create_jump_table(&mut self, data: JumpTableData) -> JumpTable
Creates a jump table in the function, to be used by br_table
instructions.
pub fn create_sized_stack_slot(&mut self, data: StackSlotData) -> StackSlot
pub fn create_sized_stack_slot(&mut self, data: StackSlotData) -> StackSlot
Creates a sized stack slot in the function, to be used by stack_load
,
stack_store
and stack_addr
instructions.
pub fn create_dynamic_stack_slot(
&mut self,
data: DynamicStackSlotData,
) -> DynamicStackSlot
pub fn create_dynamic_stack_slot( &mut self, data: DynamicStackSlotData, ) -> DynamicStackSlot
Creates a dynamic stack slot in the function, to be used by
dynamic_stack_load
,
dynamic_stack_store
and
dynamic_stack_addr
instructions.
pub fn import_signature(&mut self, signature: Signature) -> SigRef
pub fn import_signature(&mut self, signature: Signature) -> SigRef
Adds a signature which can later be used to declare an external function import.
pub fn import_function(&mut self, data: ExtFuncData) -> FuncRef
pub fn import_function(&mut self, data: ExtFuncData) -> FuncRef
Declare an external function import.
pub fn create_global_value(&mut self, data: GlobalValueData) -> GlobalValue
pub fn create_global_value(&mut self, data: GlobalValueData) -> GlobalValue
Declares a global value accessible to the function.
pub fn ins<'short>(&'short mut self) -> FuncInstBuilder<'short, 'a>
pub fn ins<'short>(&'short mut self) -> FuncInstBuilder<'short, 'a>
Returns an object with the InstBuilder
trait that allows to conveniently append an instruction to the current Block
being built.
pub fn ensure_inserted_block(&mut self)
pub fn ensure_inserted_block(&mut self)
Make sure that the current block is inserted in the layout.
pub fn cursor(&mut self) -> FuncCursor<'_>
pub fn cursor(&mut self) -> FuncCursor<'_>
Returns a [FuncCursor
] pointed at the current position ready for inserting instructions.
This can be used to insert SSA code that doesn’t need to access locals and that doesn’t
need to know about FunctionBuilder
at all.
pub fn append_block_params_for_function_params(&mut self, block: Block)
pub fn append_block_params_for_function_params(&mut self, block: Block)
Append parameters to the given Block
corresponding to the function
parameters. This can be used to set up the block parameters for the
entry block.
pub fn append_block_params_for_function_returns(&mut self, block: Block)
pub fn append_block_params_for_function_returns(&mut self, block: Block)
Append parameters to the given Block
corresponding to the function
return values. This can be used to set up the block parameters for a
function exit block.
pub fn finalize(self)
pub fn finalize(self)
Declare that translation of the current function is complete.
This resets the state of the FunctionBuilderContext
in preparation to
be used for another function.
§impl<'a> FunctionBuilder<'a>
impl<'a> FunctionBuilder<'a>
All the functions documented in the previous block are write-only and help you build a valid Cranelift IR functions via multiple debug asserts. However, you might need to improve the performance of your translation perform more complex transformations to your Cranelift IR function. The functions below help you inspect the function you’re creating and modify it in ways that can be unsafe if used incorrectly.
pub fn block_params(&self, block: Block) -> &[Value]
pub fn block_params(&self, block: Block) -> &[Value]
Retrieves all the parameters for a Block
currently inferred from the jump instructions
inserted that target it and the SSA construction.
pub fn signature(&self, sigref: SigRef) -> Option<&Signature>
pub fn signature(&self, sigref: SigRef) -> Option<&Signature>
Retrieves the signature with reference sigref
previously added with
import_signature
.
pub fn append_block_param(&mut self, block: Block, ty: Type) -> Value
pub fn append_block_param(&mut self, block: Block, ty: Type) -> Value
Creates a parameter for a specific Block
by appending it to the list of already existing
parameters.
Note: this function has to be called at the creation of the Block
before adding
instructions to it, otherwise this could interfere with SSA construction.
pub fn inst_results(&self, inst: Inst) -> &[Value]
pub fn inst_results(&self, inst: Inst) -> &[Value]
Returns the result values of an instruction.
pub fn change_jump_destination(
&mut self,
inst: Inst,
old_block: Block,
new_block: Block,
)
pub fn change_jump_destination( &mut self, inst: Inst, old_block: Block, new_block: Block, )
Changes the destination of a jump instruction after creation.
Note: You are responsible for maintaining the coherence with the arguments of other jump instructions.
pub fn is_unreachable(&self) -> bool
pub fn is_unreachable(&self) -> bool
Returns true
if and only if the current Block
is sealed and has no predecessors declared.
The entry block of a function is never unreachable.
§impl<'a> FunctionBuilder<'a>
impl<'a> FunctionBuilder<'a>
Helper functions
pub fn call_memcpy(
&mut self,
config: TargetFrontendConfig,
dest: Value,
src: Value,
size: Value,
)
pub fn call_memcpy( &mut self, config: TargetFrontendConfig, dest: Value, src: Value, size: Value, )
Calls libc.memcpy
Copies the size
bytes from src
to dest
, assumes that src + size
won’t overlap onto dest
. If dest
and src
overlap, the behavior is
undefined. Applications in which dest
and src
might overlap should
use call_memmove
instead.
pub fn emit_small_memory_copy(
&mut self,
config: TargetFrontendConfig,
dest: Value,
src: Value,
size: u64,
dest_align: u8,
src_align: u8,
non_overlapping: bool,
flags: MemFlags,
)
pub fn emit_small_memory_copy( &mut self, config: TargetFrontendConfig, dest: Value, src: Value, size: u64, dest_align: u8, src_align: u8, non_overlapping: bool, flags: MemFlags, )
Optimised memcpy or memmove for small copies.
§Codegen safety
The following properties must hold to prevent UB:
src_align
anddest_align
are an upper-bound on the alignment ofsrc
respectivelydest
.- If
non_overlapping
is true, then this must be correct.
pub fn call_memset(
&mut self,
config: TargetFrontendConfig,
buffer: Value,
ch: Value,
size: Value,
)
pub fn call_memset( &mut self, config: TargetFrontendConfig, buffer: Value, ch: Value, size: Value, )
Calls libc.memset
Writes size
bytes of i8 value ch
to memory starting at buffer
.
pub fn emit_small_memset(
&mut self,
config: TargetFrontendConfig,
buffer: Value,
ch: u8,
size: u64,
buffer_align: u8,
flags: MemFlags,
)
pub fn emit_small_memset( &mut self, config: TargetFrontendConfig, buffer: Value, ch: u8, size: u64, buffer_align: u8, flags: MemFlags, )
Calls libc.memset
Writes size
bytes of value ch
to memory starting at buffer
.
pub fn call_memmove(
&mut self,
config: TargetFrontendConfig,
dest: Value,
source: Value,
size: Value,
)
pub fn call_memmove( &mut self, config: TargetFrontendConfig, dest: Value, source: Value, size: Value, )
Calls libc.memmove
Copies size
bytes from memory starting at source
to memory starting
at dest
. source
is always read before writing to dest
.
pub fn call_memcmp(
&mut self,
config: TargetFrontendConfig,
left: Value,
right: Value,
size: Value,
) -> Value
pub fn call_memcmp( &mut self, config: TargetFrontendConfig, left: Value, right: Value, size: Value, ) -> Value
Calls libc.memcmp
Compares size
bytes from memory starting at left
to memory starting
at right
. Returns 0
if all n
bytes are equal. If the first difference
is at offset i
, returns a positive integer if ugt(left[i], right[i])
and a negative integer if ult(left[i], right[i])
.
Returns a C int
, which is currently always types::I32
.
pub fn emit_small_memory_compare(
&mut self,
config: TargetFrontendConfig,
int_cc: IntCC,
left: Value,
right: Value,
size: u64,
left_align: NonZero<u8>,
right_align: NonZero<u8>,
flags: MemFlags,
) -> Value
pub fn emit_small_memory_compare( &mut self, config: TargetFrontendConfig, int_cc: IntCC, left: Value, right: Value, size: u64, left_align: NonZero<u8>, right_align: NonZero<u8>, flags: MemFlags, ) -> Value
Optimised Self::call_memcmp
for small copies.
This implements the byte slice comparison int_cc(left[..size], right[..size])
.
left_align
and right_align
are the statically-known alignments of the
left
and right
pointers respectively. These are used to know whether
to mark load
s as aligned. It’s always fine to pass 1
for these, but
passing something higher than the true alignment may trap or otherwise
misbehave as described in MemFlags::aligned
.
Note that memcmp
is a big-endian and unsigned comparison.
As such, this panics when called with IntCC::Signed*
.