Path: blob/main/cranelift/codegen/src/cursor.rs
1693 views
//! Cursor library.1//!2//! This module defines cursor data types that can be used for inserting instructions.34use crate::ir;56/// The possible positions of a cursor.7#[derive(Clone, Copy, PartialEq, Eq, Debug)]8pub enum CursorPosition {9/// Cursor is not pointing anywhere. No instructions can be inserted.10Nowhere,11/// Cursor is pointing at an existing instruction.12/// New instructions will be inserted *before* the current instruction.13At(ir::Inst),14/// Cursor is before the beginning of a block. No instructions can be inserted. Calling15/// `next_inst()` will move to the first instruction in the block.16Before(ir::Block),17/// Cursor is pointing after the end of a block.18/// New instructions will be appended to the block.19After(ir::Block),20}2122/// All cursor types implement the `Cursor` which provides common navigation operations.23pub trait Cursor {24/// Get the current cursor position.25fn position(&self) -> CursorPosition;2627/// Set the current position.28fn set_position(&mut self, pos: CursorPosition);2930/// Get the source location that should be assigned to new instructions.31fn srcloc(&self) -> ir::SourceLoc;3233/// Set the source location that should be assigned to new instructions.34fn set_srcloc(&mut self, srcloc: ir::SourceLoc);3536/// Borrow a reference to the function layout that this cursor is navigating.37fn layout(&self) -> &ir::Layout;3839/// Borrow a mutable reference to the function layout that this cursor is navigating.40fn layout_mut(&mut self) -> &mut ir::Layout;4142/// Exchange this cursor for one with a set source location.43///44/// This is intended to be used as a builder method:45///46/// ```47/// # use cranelift_codegen::ir::{Function, Block, SourceLoc};48/// # use cranelift_codegen::cursor::{Cursor, FuncCursor};49/// fn edit_func(func: &mut Function, srcloc: SourceLoc) {50/// let mut pos = FuncCursor::new(func).with_srcloc(srcloc);51///52/// // Use `pos`...53/// }54/// ```55fn with_srcloc(mut self, srcloc: ir::SourceLoc) -> Self56where57Self: Sized,58{59self.set_srcloc(srcloc);60self61}6263/// Rebuild this cursor positioned at `pos`.64fn at_position(mut self, pos: CursorPosition) -> Self65where66Self: Sized,67{68self.set_position(pos);69self70}7172/// Rebuild this cursor positioned at `inst`.73///74/// This is intended to be used as a builder method:75///76/// ```77/// # use cranelift_codegen::ir::{Function, Block, Inst};78/// # use cranelift_codegen::cursor::{Cursor, FuncCursor};79/// fn edit_func(func: &mut Function, inst: Inst) {80/// let mut pos = FuncCursor::new(func).at_inst(inst);81///82/// // Use `pos`...83/// }84/// ```85fn at_inst(mut self, inst: ir::Inst) -> Self86where87Self: Sized,88{89self.goto_inst(inst);90self91}9293/// Rebuild this cursor positioned at the first insertion point for `block`.94/// This differs from `at_first_inst` in that it doesn't assume that any95/// instructions have been inserted into `block` yet.96///97/// This is intended to be used as a builder method:98///99/// ```100/// # use cranelift_codegen::ir::{Function, Block, Inst};101/// # use cranelift_codegen::cursor::{Cursor, FuncCursor};102/// fn edit_func(func: &mut Function, block: Block) {103/// let mut pos = FuncCursor::new(func).at_first_insertion_point(block);104///105/// // Use `pos`...106/// }107/// ```108fn at_first_insertion_point(mut self, block: ir::Block) -> Self109where110Self: Sized,111{112self.goto_first_insertion_point(block);113self114}115116/// Rebuild this cursor positioned at the first instruction in `block`.117///118/// This is intended to be used as a builder method:119///120/// ```121/// # use cranelift_codegen::ir::{Function, Block, Inst};122/// # use cranelift_codegen::cursor::{Cursor, FuncCursor};123/// fn edit_func(func: &mut Function, block: Block) {124/// let mut pos = FuncCursor::new(func).at_first_inst(block);125///126/// // Use `pos`...127/// }128/// ```129fn at_first_inst(mut self, block: ir::Block) -> Self130where131Self: Sized,132{133self.goto_first_inst(block);134self135}136137/// Rebuild this cursor positioned at the last instruction in `block`.138///139/// This is intended to be used as a builder method:140///141/// ```142/// # use cranelift_codegen::ir::{Function, Block, Inst};143/// # use cranelift_codegen::cursor::{Cursor, FuncCursor};144/// fn edit_func(func: &mut Function, block: Block) {145/// let mut pos = FuncCursor::new(func).at_last_inst(block);146///147/// // Use `pos`...148/// }149/// ```150fn at_last_inst(mut self, block: ir::Block) -> Self151where152Self: Sized,153{154self.goto_last_inst(block);155self156}157158/// Rebuild this cursor positioned after `inst`.159///160/// This is intended to be used as a builder method:161///162/// ```163/// # use cranelift_codegen::ir::{Function, Block, Inst};164/// # use cranelift_codegen::cursor::{Cursor, FuncCursor};165/// fn edit_func(func: &mut Function, inst: Inst) {166/// let mut pos = FuncCursor::new(func).after_inst(inst);167///168/// // Use `pos`...169/// }170/// ```171fn after_inst(mut self, inst: ir::Inst) -> Self172where173Self: Sized,174{175self.goto_after_inst(inst);176self177}178179/// Rebuild this cursor positioned at the top of `block`.180///181/// This is intended to be used as a builder method:182///183/// ```184/// # use cranelift_codegen::ir::{Function, Block, Inst};185/// # use cranelift_codegen::cursor::{Cursor, FuncCursor};186/// fn edit_func(func: &mut Function, block: Block) {187/// let mut pos = FuncCursor::new(func).at_top(block);188///189/// // Use `pos`...190/// }191/// ```192fn at_top(mut self, block: ir::Block) -> Self193where194Self: Sized,195{196self.goto_top(block);197self198}199200/// Rebuild this cursor positioned at the bottom of `block`.201///202/// This is intended to be used as a builder method:203///204/// ```205/// # use cranelift_codegen::ir::{Function, Block, Inst};206/// # use cranelift_codegen::cursor::{Cursor, FuncCursor};207/// fn edit_func(func: &mut Function, block: Block) {208/// let mut pos = FuncCursor::new(func).at_bottom(block);209///210/// // Use `pos`...211/// }212/// ```213fn at_bottom(mut self, block: ir::Block) -> Self214where215Self: Sized,216{217self.goto_bottom(block);218self219}220221/// Get the block corresponding to the current position.222fn current_block(&self) -> Option<ir::Block> {223use self::CursorPosition::*;224match self.position() {225Nowhere => None,226At(inst) => self.layout().inst_block(inst),227Before(block) | After(block) => Some(block),228}229}230231/// Get the instruction corresponding to the current position, if any.232fn current_inst(&self) -> Option<ir::Inst> {233use self::CursorPosition::*;234match self.position() {235At(inst) => Some(inst),236_ => None,237}238}239240/// Go to the position after a specific instruction, which must be inserted241/// in the layout. New instructions will be inserted after `inst`.242fn goto_after_inst(&mut self, inst: ir::Inst) {243debug_assert!(self.layout().inst_block(inst).is_some());244let new_pos = if let Some(next) = self.layout().next_inst(inst) {245CursorPosition::At(next)246} else {247CursorPosition::After(248self.layout()249.inst_block(inst)250.expect("current instruction removed?"),251)252};253self.set_position(new_pos);254}255256/// Go to a specific instruction which must be inserted in the layout.257/// New instructions will be inserted before `inst`.258fn goto_inst(&mut self, inst: ir::Inst) {259debug_assert!(self.layout().inst_block(inst).is_some());260self.set_position(CursorPosition::At(inst));261}262263/// Go to the position for inserting instructions at the beginning of `block`,264/// which unlike `goto_first_inst` doesn't assume that any instructions have265/// been inserted into `block` yet.266fn goto_first_insertion_point(&mut self, block: ir::Block) {267if let Some(inst) = self.layout().first_inst(block) {268self.goto_inst(inst);269} else {270self.goto_bottom(block);271}272}273274/// Go to the first instruction in `block`.275fn goto_first_inst(&mut self, block: ir::Block) {276let inst = self.layout().first_inst(block).expect("Empty block");277self.goto_inst(inst);278}279280/// Go to the last instruction in `block`.281fn goto_last_inst(&mut self, block: ir::Block) {282let inst = self.layout().last_inst(block).expect("Empty block");283self.goto_inst(inst);284}285286/// Go to the top of `block` which must be inserted into the layout.287/// At this position, instructions cannot be inserted, but `next_inst()` will move to the first288/// instruction in `block`.289fn goto_top(&mut self, block: ir::Block) {290debug_assert!(self.layout().is_block_inserted(block));291self.set_position(CursorPosition::Before(block));292}293294/// Go to the bottom of `block` which must be inserted into the layout.295/// At this position, inserted instructions will be appended to `block`.296fn goto_bottom(&mut self, block: ir::Block) {297debug_assert!(self.layout().is_block_inserted(block));298self.set_position(CursorPosition::After(block));299}300301/// Get the next position that a forwards traversal will move to, but do not302/// move this cursor.303fn next_position(&self) -> CursorPosition {304self.next_inst_position()305.unwrap_or_else(|| self.next_block_position())306}307308/// Get the next position that a backwards traversal will move to, but do309/// not move this cursor.310fn prev_position(&self) -> CursorPosition {311self.prev_inst_position()312.unwrap_or_else(|| self.prev_block_position())313}314315/// Get the position that a `cursor.next_block()` call would move this316/// cursor to, but do not update this cursor's position.317fn next_block_position(&self) -> CursorPosition {318let next = if let Some(block) = self.current_block() {319self.layout().next_block(block)320} else {321self.layout().entry_block()322};323match next {324Some(block) => CursorPosition::Before(block),325None => CursorPosition::Nowhere,326}327}328329/// Go to the top of the next block in layout order and return it.330///331/// - If the cursor wasn't pointing at anything, go to the top of the first block in the332/// function.333/// - If there are no more blocks, leave the cursor pointing at nothing and return `None`.334///335/// # Examples336///337/// The `next_block()` method is intended for iterating over the blocks in layout order:338///339/// ```340/// # use cranelift_codegen::ir::{Function, Block};341/// # use cranelift_codegen::cursor::{Cursor, FuncCursor};342/// fn edit_func(func: &mut Function) {343/// let mut cursor = FuncCursor::new(func);344/// while let Some(block) = cursor.next_block() {345/// // Edit block.346/// }347/// }348/// ```349fn next_block(&mut self) -> Option<ir::Block> {350let pos = self.next_block_position();351self.set_position(pos);352self.current_block()353}354355/// Get the position that a `cursor.prev_block()` call would move this356/// cursor to, but do not update this cursor's position.357fn prev_block_position(&self) -> CursorPosition {358let prev = if let Some(block) = self.current_block() {359self.layout().prev_block(block)360} else {361self.layout().last_block()362};363match prev {364Some(block) => CursorPosition::After(block),365None => CursorPosition::Nowhere,366}367}368369/// Go to the bottom of the previous block in layout order and return it.370///371/// - If the cursor wasn't pointing at anything, go to the bottom of the last block in the372/// function.373/// - If there are no more blocks, leave the cursor pointing at nothing and return `None`.374///375/// # Examples376///377/// The `prev_block()` method is intended for iterating over the blocks in backwards layout order:378///379/// ```380/// # use cranelift_codegen::ir::{Function, Block};381/// # use cranelift_codegen::cursor::{Cursor, FuncCursor};382/// fn edit_func(func: &mut Function) {383/// let mut cursor = FuncCursor::new(func);384/// while let Some(block) = cursor.prev_block() {385/// // Edit block.386/// }387/// }388/// ```389fn prev_block(&mut self) -> Option<ir::Block> {390let pos = self.prev_block_position();391self.set_position(pos);392self.current_block()393}394395/// Get the position that a `cursor.next_inst()` call would move this cursor396/// to, but do not update this cursor's position.397fn next_inst_position(&self) -> Option<CursorPosition> {398use self::CursorPosition::*;399match self.position() {400Nowhere | After(..) => None,401At(inst) => {402if let Some(next) = self.layout().next_inst(inst) {403Some(At(next))404} else {405Some(After(406self.layout()407.inst_block(inst)408.expect("current instruction removed?"),409))410}411}412Before(block) => {413if let Some(next) = self.layout().first_inst(block) {414Some(At(next))415} else {416Some(After(block))417}418}419}420}421422/// Move to the next instruction in the same block and return it.423///424/// - If the cursor was positioned before a block, go to the first instruction in that block.425/// - If there are no more instructions in the block, go to the `After(block)` position and return426/// `None`.427/// - If the cursor wasn't pointing anywhere, keep doing that.428///429/// This method will never move the cursor to a different block.430///431/// # Examples432///433/// The `next_inst()` method is intended for iterating over the instructions in a block like434/// this:435///436/// ```437/// # use cranelift_codegen::ir::{Function, Block};438/// # use cranelift_codegen::cursor::{Cursor, FuncCursor};439/// fn edit_block(func: &mut Function, block: Block) {440/// let mut cursor = FuncCursor::new(func).at_top(block);441/// while let Some(inst) = cursor.next_inst() {442/// // Edit instructions...443/// }444/// }445/// ```446/// The loop body can insert and remove instructions via the cursor.447///448/// Iterating over all the instructions in a function looks like this:449///450/// ```451/// # use cranelift_codegen::ir::{Function, Block};452/// # use cranelift_codegen::cursor::{Cursor, FuncCursor};453/// fn edit_func(func: &mut Function) {454/// let mut cursor = FuncCursor::new(func);455/// while let Some(block) = cursor.next_block() {456/// while let Some(inst) = cursor.next_inst() {457/// // Edit instructions...458/// }459/// }460/// }461/// ```462fn next_inst(&mut self) -> Option<ir::Inst> {463let pos = self.next_inst_position()?;464self.set_position(pos);465self.current_inst()466}467468/// Get the position that a `cursor.prev_inst()` call would move this cursor469/// to, but do not update this cursor's position.470fn prev_inst_position(&self) -> Option<CursorPosition> {471use self::CursorPosition::*;472match self.position() {473Nowhere | Before(..) => None,474At(inst) => {475if let Some(prev) = self.layout().prev_inst(inst) {476Some(At(prev))477} else {478Some(Before(479self.layout()480.inst_block(inst)481.expect("current instruction removed?"),482))483}484}485After(block) => {486if let Some(prev) = self.layout().last_inst(block) {487Some(At(prev))488} else {489Some(Before(block))490}491}492}493}494495/// Move to the previous instruction in the same block and return it.496///497/// - If the cursor was positioned after a block, go to the last instruction in that block.498/// - If there are no more instructions in the block, go to the `Before(block)` position and return499/// `None`.500/// - If the cursor wasn't pointing anywhere, keep doing that.501///502/// This method will never move the cursor to a different block.503///504/// # Examples505///506/// The `prev_inst()` method is intended for iterating backwards over the instructions in an507/// block like this:508///509/// ```510/// # use cranelift_codegen::ir::{Function, Block};511/// # use cranelift_codegen::cursor::{Cursor, FuncCursor};512/// fn edit_block(func: &mut Function, block: Block) {513/// let mut cursor = FuncCursor::new(func).at_bottom(block);514/// while let Some(inst) = cursor.prev_inst() {515/// // Edit instructions...516/// }517/// }518/// ```519fn prev_inst(&mut self) -> Option<ir::Inst> {520let pos = self.prev_inst_position()?;521self.set_position(pos);522self.current_inst()523}524525/// Insert an instruction at the current position.526///527/// - If pointing at an instruction, the new instruction is inserted before the current528/// instruction.529/// - If pointing at the bottom of a block, the new instruction is appended to the block.530/// - Otherwise panic.531///532/// In either case, the cursor is not moved, such that repeated calls to `insert_inst()` causes533/// instructions to appear in insertion order in the block.534fn insert_inst(&mut self, inst: ir::Inst) {535use self::CursorPosition::*;536match self.position() {537Nowhere | Before(..) => panic!("Invalid insert_inst position"),538At(cur) => self.layout_mut().insert_inst(inst, cur),539After(block) => self.layout_mut().append_inst(inst, block),540}541}542543/// Remove the instruction under the cursor.544///545/// The cursor is left pointing at the position following the current instruction.546///547/// Return the instruction that was removed.548fn remove_inst(&mut self) -> ir::Inst {549let inst = self.current_inst().expect("No instruction to remove");550self.next_inst();551self.layout_mut().remove_inst(inst);552inst553}554555/// Remove the instruction under the cursor.556///557/// The cursor is left pointing at the position preceding the current instruction.558///559/// Return the instruction that was removed.560fn remove_inst_and_step_back(&mut self) -> ir::Inst {561let inst = self.current_inst().expect("No instruction to remove");562self.prev_inst();563self.layout_mut().remove_inst(inst);564inst565}566567/// Replace the instruction under the cursor with `new_inst`.568///569/// The cursor is left pointing at the new instruction.570///571/// The old instruction that was replaced is returned.572fn replace_inst(&mut self, new_inst: ir::Inst) -> ir::Inst {573let prev_inst = self.remove_inst();574self.insert_inst(new_inst);575prev_inst576}577578/// Insert a block at the current position and switch to it.579///580/// As far as possible, this method behaves as if the block header were an instruction inserted581/// at the current position.582///583/// - If the cursor is pointing at an existing instruction, *the current block is split in two*584/// and the current instruction becomes the first instruction in the inserted block.585/// - If the cursor points at the bottom of a block, the new block is inserted after the current586/// one, and moved to the bottom of the new block where instructions can be appended.587/// - If the cursor points to the top of a block, the new block is inserted above the current one.588/// - If the cursor is not pointing at anything, the new block is placed last in the layout.589///590/// This means that it is always valid to call this method, and it always leaves the cursor in591/// a state that will insert instructions into the new block.592fn insert_block(&mut self, new_block: ir::Block) {593use self::CursorPosition::*;594match self.position() {595At(inst) => {596self.layout_mut().split_block(new_block, inst);597// All other cases move to `After(block)`, but in this case we'll stay `At(inst)`.598return;599}600Nowhere => self.layout_mut().append_block(new_block),601Before(block) => self.layout_mut().insert_block(new_block, block),602After(block) => self.layout_mut().insert_block_after(new_block, block),603}604// For everything but `At(inst)` we end up appending to the new block.605self.set_position(After(new_block));606}607}608609/// Function cursor.610///611/// A `FuncCursor` holds a mutable reference to a whole `ir::Function` while keeping a position612/// too. The function can be re-borrowed by accessing the public `cur.func` member.613///614/// This cursor is for use before legalization. The inserted instructions are not given an615/// encoding.616pub struct FuncCursor<'f> {617pos: CursorPosition,618srcloc: ir::SourceLoc,619620/// The referenced function.621pub func: &'f mut ir::Function,622}623624impl<'f> FuncCursor<'f> {625/// Create a new `FuncCursor` pointing nowhere.626pub fn new(func: &'f mut ir::Function) -> Self {627Self {628pos: CursorPosition::Nowhere,629srcloc: Default::default(),630func,631}632}633634/// Use the source location of `inst` for future instructions.635pub fn use_srcloc(&mut self, inst: ir::Inst) {636self.srcloc = self.func.srcloc(inst);637}638639/// Create an instruction builder that inserts an instruction at the current position.640pub fn ins(&mut self) -> ir::InsertBuilder<'_, &mut FuncCursor<'f>> {641ir::InsertBuilder::new(self)642}643}644645impl<'f> Cursor for FuncCursor<'f> {646fn position(&self) -> CursorPosition {647self.pos648}649650fn set_position(&mut self, pos: CursorPosition) {651self.pos = pos652}653654fn srcloc(&self) -> ir::SourceLoc {655self.srcloc656}657658fn set_srcloc(&mut self, srcloc: ir::SourceLoc) {659self.func.params.ensure_base_srcloc(srcloc);660self.srcloc = srcloc;661}662663fn layout(&self) -> &ir::Layout {664&self.func.layout665}666667fn layout_mut(&mut self) -> &mut ir::Layout {668&mut self.func.layout669}670}671672impl<'c, 'f> ir::InstInserterBase<'c> for &'c mut FuncCursor<'f> {673fn data_flow_graph(&self) -> &ir::DataFlowGraph {674&self.func.dfg675}676677fn data_flow_graph_mut(&mut self) -> &mut ir::DataFlowGraph {678&mut self.func.dfg679}680681fn insert_built_inst(self, inst: ir::Inst) -> &'c mut ir::DataFlowGraph {682self.insert_inst(inst);683if !self.srcloc.is_default() {684self.func.set_srcloc(inst, self.srcloc);685}686&mut self.func.dfg687}688}689690691