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