From 177ac85db53b1f4e5ecc58c23fd3c346ef02e0fb Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Thu, 13 Oct 2016 14:36:34 -0700 Subject: [PATCH] Add a LAyout Cursor data structure. A layout cursor can be used instead of an iterator to keep track of a position in a basic block *and* permitting instructions to be manipulated. --- cranelift/src/libcretonne/ir/layout.rs | 319 ++++++++++++++++++++++++- cranelift/src/libcretonne/ir/mod.rs | 2 +- 2 files changed, 319 insertions(+), 2 deletions(-) diff --git a/cranelift/src/libcretonne/ir/layout.rs b/cranelift/src/libcretonne/ir/layout.rs index a872efa5d8..1b78386100 100644 --- a/cranelift/src/libcretonne/ir/layout.rs +++ b/cranelift/src/libcretonne/ir/layout.rs @@ -250,9 +250,273 @@ impl<'a> Iterator for Insts<'a> { } } + +/// Layout Cursor. +/// +/// A `Cursor` represents a position in a function layout where instructions can be inserted and +/// removed. It can be used to iterate through the instructions of a function while editing them at +/// the same time. A normal instruction iterator can't do this since it holds an immutable refernce +/// to the Layout. +/// +/// When new instructions are added, the cursor can either apend them to an EBB or insert them +/// before the current instruction. +pub struct Cursor<'a> { + layout: &'a mut Layout, + pos: CursorPosition, +} + +/// The possible positions of a cursor. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum CursorPosition { + /// Cursor is not pointing anywhere. No instructions can be inserted. + Nowhere, + /// Cursor is pointing at an existing instruction. + /// New instructions will be inserted *before* the current instruction. + At(Inst), + /// Cursor is before the beginning of an EBB. No instructions can be inserted. Calling + /// `next_inst()` wil move to the first instruction in the EBB. + Before(Ebb), + /// Cursor is pointing after the end of an EBB. + /// New instructions will be appended to the EBB. + After(Ebb), +} + +impl<'a> Cursor<'a> { + /// Create a new `Cursor` for `layout`. + /// The cursor holds a mutable reference to `layout` for its entire lifetime. + pub fn new(layout: &'a mut Layout) -> Cursor { + Cursor { + layout: layout, + pos: CursorPosition::Nowhere, + } + } + + /// Get the current position. + pub fn position(&self) -> CursorPosition { + self.pos + } + + /// Get the EBB corresponding to the current position. + pub fn current_ebb(&self) -> Option { + use self::CursorPosition::*; + match self.pos { + Nowhere => None, + At(inst) => self.layout.inst_ebb(inst), + Before(ebb) | After(ebb) => Some(ebb), + } + } + + /// Go to a specific instruction which must be inserted in the layout. + /// New instructions will be inserted before `inst`. + pub fn goto_inst(&mut self, inst: Inst) { + assert!(self.layout.inst_ebb(inst).is_some()); + self.pos = CursorPosition::At(inst); + } + + /// Go to the top of `ebb` which must be inserted into the layout. + /// At this position, instructions cannot be inserted, but `next_inst()` will move to the first + /// instruction in `ebb`. + pub fn goto_top(&mut self, ebb: Ebb) { + assert!(self.layout.is_ebb_inserted(ebb)); + self.pos = CursorPosition::Before(ebb); + } + + /// Go to the bottom of `ebb` which must be inserted into the layout. + /// At this position, inserted instructions will be appended to `ebb`. + pub fn goto_bottom(&mut self, ebb: Ebb) { + assert!(self.layout.is_ebb_inserted(ebb)); + self.pos = CursorPosition::After(ebb); + } + + /// Go to the top of the next EBB in layout order and return it. + /// + /// - If the cursor wasn't pointing at anything, go to the top of the first EBB in the + /// function. + /// - If there are no more EBBs, leave the cursor pointing at nothing and return `None`. + /// + /// # Examples + /// + /// The `next_ebb()` method is intended for iterating over the EBBs in layout order: + /// + /// ``` + /// # use cretonne::ir::{Function, Ebb}; + /// # use cretonne::ir::layout::Cursor; + /// fn edit_func(func: &mut Function) { + /// let mut cursor = Cursor::new(&mut func.layout); + /// while let Some(ebb) = cursor.next_ebb() { + /// // Edit ebb. + /// } + /// } + /// ``` + pub fn next_ebb(&mut self) -> Option { + let next = if let Some(ebb) = self.current_ebb() { + self.layout.ebbs[ebb].next.wrap() + } else { + self.layout.first_ebb + }; + self.pos = match next { + Some(ebb) => CursorPosition::Before(ebb), + None => CursorPosition::Nowhere, + }; + next + } + + /// Go to the bottom of the previous EBB in layout order and return it. + /// + /// - If the cursor wasn't pointing at anything, go to the bottom of the last EBB in the + /// function. + /// - If there are no more EBBs, leave the cursor pointing at nothing and return `None`. + /// + /// # Examples + /// + /// The `prev_ebb()` method is intended for iterating over the EBBs in backwards layout order: + /// + /// ``` + /// # use cretonne::ir::{Function, Ebb}; + /// # use cretonne::ir::layout::Cursor; + /// fn edit_func(func: &mut Function) { + /// let mut cursor = Cursor::new(&mut func.layout); + /// while let Some(ebb) = cursor.prev_ebb() { + /// // Edit ebb. + /// } + /// } + /// ``` + pub fn prev_ebb(&mut self) -> Option { + let prev = if let Some(ebb) = self.current_ebb() { + self.layout.ebbs[ebb].prev.wrap() + } else { + self.layout.last_ebb + }; + self.pos = match prev { + Some(ebb) => CursorPosition::After(ebb), + None => CursorPosition::Nowhere, + }; + prev + } + + /// Move to the next instruction in the same EBB and return it. + /// + /// - If the cursor was positioned before an EBB, go to the first instruction in that EBB. + /// - If there are no more instructions in the EBB, go to the `After(ebb)` position and return + /// `None`. + /// - If the cursor wasn't pointing anywhere, keep doing that. + /// + /// This method will never move the cursor to a different EBB. + /// + /// # Examples + /// + /// The `next_inst()` method is intended for iterating over the instructions in an EBB like + /// this: + /// + /// ``` + /// # use cretonne::ir::{Function, Ebb}; + /// # use cretonne::ir::layout::Cursor; + /// fn edit_ebb(func: &mut Function, ebb: Ebb) { + /// let mut cursor = Cursor::new(&mut func.layout); + /// cursor.goto_top(ebb); + /// while let Some(inst) = cursor.next_inst() { + /// // Edit instructions... + /// } + /// } + /// ``` + /// The loop body can insert and remove instructions via the cursor. + /// + /// Iterating over all the instructions in a function looks like this: + /// + /// ``` + /// # use cretonne::ir::{Function, Ebb}; + /// # use cretonne::ir::layout::Cursor; + /// fn edit_func(func: &mut Function) { + /// let mut cursor = Cursor::new(&mut func.layout); + /// while let Some(ebb) = cursor.next_ebb() { + /// while let Some(inst) = cursor.next_inst() { + /// // Edit instructions... + /// } + /// } + /// } + /// ``` + pub fn next_inst(&mut self) -> Option { + use self::CursorPosition::*; + match self.pos { + Nowhere | After(..) => None, + At(inst) => { + if let Some(next) = self.layout.insts[inst].next.wrap() { + self.pos = At(next); + Some(next) + } else { + self.pos = + After(self.layout.inst_ebb(inst).expect("current instruction removed?")); + None + } + } + Before(ebb) => { + if let Some(next) = self.layout.ebbs[ebb].first_inst.wrap() { + self.pos = At(next); + Some(next) + } else { + self.pos = After(ebb); + None + } + } + } + } + + /// Move to the previous instruction in the same EBB and return it. + /// + /// - If the cursor was positioned after an EBB, go to the last instruction in that EBB. + /// - If there are no more instructions in the EBB, go to the `Before(ebb)` position and return + /// `None`. + /// - If the cursor wasn't pointing anywhere, keep doing that. + /// + /// This method will never move the cursor to a different EBB. + /// + /// # Examples + /// + /// The `prev_inst()` method is intended for iterating backwards over the instructions in an + /// EBB like this: + /// + /// ``` + /// # use cretonne::ir::{Function, Ebb}; + /// # use cretonne::ir::layout::Cursor; + /// fn edit_ebb(func: &mut Function, ebb: Ebb) { + /// let mut cursor = Cursor::new(&mut func.layout); + /// cursor.goto_bottom(ebb); + /// while let Some(inst) = cursor.prev_inst() { + /// // Edit instructions... + /// } + /// } + /// ``` + pub fn prev_inst(&mut self) -> Option { + use self::CursorPosition::*; + match self.pos { + Nowhere | Before(..) => None, + At(inst) => { + if let Some(prev) = self.layout.insts[inst].prev.wrap() { + self.pos = At(prev); + Some(prev) + } else { + self.pos = + Before(self.layout.inst_ebb(inst).expect("current instruction removed?")); + None + } + } + After(ebb) => { + if let Some(prev) = self.layout.ebbs[ebb].last_inst.wrap() { + self.pos = At(prev); + Some(prev) + } else { + self.pos = Before(ebb); + None + } + } + } + } +} + + #[cfg(test)] mod tests { - use super::Layout; + use super::{Layout, Cursor, CursorPosition}; use entity_map::EntityRef; use ir::{Ebb, Inst}; @@ -301,6 +565,35 @@ mod tests { } assert_eq!(v, [e1, e2, e0]); } + + // Test cursor positioning. + let mut cur = Cursor::new(&mut layout); + assert_eq!(cur.position(), CursorPosition::Nowhere); + assert_eq!(cur.next_inst(), None); + assert_eq!(cur.position(), CursorPosition::Nowhere); + assert_eq!(cur.prev_inst(), None); + assert_eq!(cur.position(), CursorPosition::Nowhere); + + assert_eq!(cur.next_ebb(), Some(e1)); + assert_eq!(cur.position(), CursorPosition::Before(e1)); + assert_eq!(cur.next_inst(), None); + assert_eq!(cur.position(), CursorPosition::After(e1)); + assert_eq!(cur.next_inst(), None); + assert_eq!(cur.position(), CursorPosition::After(e1)); + assert_eq!(cur.next_ebb(), Some(e2)); + assert_eq!(cur.prev_inst(), None); + assert_eq!(cur.position(), CursorPosition::Before(e2)); + assert_eq!(cur.next_ebb(), Some(e0)); + assert_eq!(cur.next_ebb(), None); + assert_eq!(cur.position(), CursorPosition::Nowhere); + + // Backwards through the EBBs. + assert_eq!(cur.prev_ebb(), Some(e0)); + assert_eq!(cur.position(), CursorPosition::After(e0)); + assert_eq!(cur.prev_ebb(), Some(e2)); + assert_eq!(cur.prev_ebb(), Some(e1)); + assert_eq!(cur.prev_ebb(), None); + assert_eq!(cur.position(), CursorPosition::Nowhere); } #[test] @@ -378,6 +671,30 @@ mod tests { assert_eq!(layout.inst_ebb(i2), Some(e1)); let v: Vec = layout.ebb_insts(e1).collect(); assert_eq!(v, [i1, i2, i0]); + + // Test cursor positioning. + let mut cur = Cursor::new(&mut layout); + cur.goto_top(e1); + assert_eq!(cur.position(), CursorPosition::Before(e1)); + assert_eq!(cur.prev_inst(), None); + assert_eq!(cur.position(), CursorPosition::Before(e1)); + assert_eq!(cur.next_inst(), Some(i1)); + assert_eq!(cur.position(), CursorPosition::At(i1)); + assert_eq!(cur.next_inst(), Some(i2)); + assert_eq!(cur.next_inst(), Some(i0)); + assert_eq!(cur.prev_inst(), Some(i2)); + assert_eq!(cur.position(), CursorPosition::At(i2)); + assert_eq!(cur.next_inst(), Some(i0)); + assert_eq!(cur.position(), CursorPosition::At(i0)); + assert_eq!(cur.next_inst(), None); + assert_eq!(cur.position(), CursorPosition::After(e1)); + assert_eq!(cur.next_inst(), None); + assert_eq!(cur.position(), CursorPosition::After(e1)); + assert_eq!(cur.prev_inst(), Some(i0)); + assert_eq!(cur.prev_inst(), Some(i2)); + assert_eq!(cur.prev_inst(), Some(i1)); + assert_eq!(cur.prev_inst(), None); + assert_eq!(cur.position(), CursorPosition::Before(e1)); } #[test] diff --git a/cranelift/src/libcretonne/ir/mod.rs b/cranelift/src/libcretonne/ir/mod.rs index 36d17c29aa..bbbc6d26ab 100644 --- a/cranelift/src/libcretonne/ir/mod.rs +++ b/cranelift/src/libcretonne/ir/mod.rs @@ -22,6 +22,6 @@ pub use ir::instructions::{Opcode, InstructionData, VariableArgs}; pub use ir::stackslot::StackSlotData; pub use ir::jumptable::JumpTableData; pub use ir::dfg::{DataFlowGraph, ValueDef}; -pub use ir::layout::Layout; +pub use ir::layout::{Layout, Cursor}; pub use ir::function::Function; pub use ir::builder::Builder;