diff --git a/xrcf/src/convert/mlir_to_llvmir.rs b/xrcf/src/convert/mlir_to_llvmir.rs index b303c013..e9e5e7e6 100644 --- a/xrcf/src/convert/mlir_to_llvmir.rs +++ b/xrcf/src/convert/mlir_to_llvmir.rs @@ -148,8 +148,7 @@ impl Rewrite for BlockLowering { let region = op.operation().region(); if let Some(region) = region { let blocks = region.blocks(); - let blocks = blocks.try_read().unwrap(); - for block in blocks.iter() { + for block in blocks.into_iter() { let label_prefix = block.label_prefix(); if label_prefix == "^" { return Ok(true); @@ -540,8 +539,7 @@ impl Rewrite for MergeLowering { } fn rewrite(&self, op: Arc>) -> Result { let blocks = op.operation().region().unwrap().blocks(); - let blocks = blocks.try_read().unwrap(); - for block in blocks.iter() { + for block in blocks.into_iter() { let block_read = block.try_read().unwrap(); let has_argument = !block_read.arguments().vec().try_read().unwrap().is_empty(); if has_argument { diff --git a/xrcf/src/convert/scf_to_cf.rs b/xrcf/src/convert/scf_to_cf.rs index 92bb35ef..4b2949a8 100644 --- a/xrcf/src/convert/scf_to_cf.rs +++ b/xrcf/src/convert/scf_to_cf.rs @@ -8,7 +8,6 @@ use crate::ir::Block; use crate::ir::BlockArgument; use crate::ir::BlockArgumentName; use crate::ir::BlockName; -use crate::ir::BlockPtr; use crate::ir::GuardedBlock; use crate::ir::GuardedOp; use crate::ir::GuardedOperation; @@ -80,42 +79,23 @@ fn branch_op(after: Arc>) -> Arc> { new_op } -fn add_block_from_region( - label: String, - after: Arc>, - region: Arc>, - parent_region: Arc>, -) -> Result>> { - let mut ops = region.ops(); +/// Add a `cf.br` to the end of `block` with destination `after`. +fn add_branch_to_after(block: Arc>, after: Arc>) { + let ops = block.ops(); + let mut ops = ops.try_write().unwrap(); let ops_clone = ops.clone(); let last_op = ops_clone.last().unwrap(); let last_op = last_op.try_read().unwrap(); let yield_op = last_op.as_any().downcast_ref::(); if let Some(yield_op) = yield_op { - let new_op = lower_yield_op(&yield_op, after.clone())?; + let new_op = lower_yield_op(&yield_op, after.clone()).unwrap(); ops.pop(); ops.push(new_op.clone()); } else { let new_op = branch_op(after.clone()); + new_op.set_parent(block.clone()); ops.push(new_op.clone()); }; - - let unset_block = parent_region.add_empty_block_before(after); - let block = unset_block.set_parent(Some(parent_region.clone())); - block.set_ops(Arc::new(RwLock::new(ops.clone()))); - block.set_label(BlockName::Unset); - for op in ops.iter() { - let op = op.try_read().unwrap(); - op.set_parent(block.clone()); - } - let block_label = BlockName::Name(label.clone()); - block.set_label(block_label); - - let label = Value::BlockPtr(BlockPtr::new(block.clone())); - let label = Arc::new(RwLock::new(label)); - let operand = OpOperand::new(label); - let operand = Arc::new(RwLock::new(operand)); - Ok(operand) } /// Move all successors of `scf.if` to the return block. @@ -258,20 +238,21 @@ fn results_users(results: Values) -> Vec { fn add_blocks( op: &dialect::scf::IfOp, parent_region: Arc>, -) -> Result<(Arc>, Arc>)> { +) -> Result<(Arc>, Arc>)> { let results = op.operation().results(); let results_users = results_users(results.clone()); let exit = add_exit_block(op, parent_region.clone())?; - let then_label = format!("{}", parent_region.unique_block_name()); - let then_label_index = then_label - .trim_start_matches("^bb") - .parse::() - .unwrap(); let has_results = !results.is_empty(); - let else_label = format!("^bb{}", then_label_index + 1); - let then = op.then().expect("Expected `then` region"); - let els = op.els().expect("Expected `else` region"); + let then_region = op.then().expect("Expected `then` region"); + let then = then_region.blocks().into_iter().next().unwrap(); + then.set_label(BlockName::Unset); + exit.inline_region_before(then_region.clone()); + + let else_region = op.els().expect("Expected `else` region"); + let els = else_region.blocks().into_iter().next().unwrap(); + els.set_label(BlockName::Unset); + exit.inline_region_before(else_region.clone()); let after = if has_results { let (merge, merge_block_arguments) = @@ -298,11 +279,10 @@ fn add_blocks( exit.clone() }; - let then_operand = - add_block_from_region(then_label, after.clone(), then, parent_region.clone())?; - let else_operand = add_block_from_region(else_label, after, els, parent_region.clone())?; + add_branch_to_after(then.clone(), after.clone()); + add_branch_to_after(els.clone(), after.clone()); - Ok((then_operand, else_operand)) + Ok((then, els)) } impl Rewrite for IfLowering { @@ -318,13 +298,15 @@ impl Rewrite for IfLowering { let parent_region = parent.parent().expect("Expected parent region"); let op = op.as_any().downcast_ref::().unwrap(); - let (then_operand, else_operand) = add_blocks(&op, parent_region.clone())?; + let (then, els) = add_blocks(&op, parent_region.clone())?; let mut operation = Operation::default(); operation.set_parent(Some(parent.clone())); operation.set_operand(0, op.operation().operand(0).clone().unwrap()); - operation.set_operand(1, then_operand.clone()); - operation.set_operand(2, else_operand.clone()); + let then_operand = Arc::new(RwLock::new(OpOperand::from_block(then))); + operation.set_operand(1, then_operand); + let els_operand = Arc::new(RwLock::new(OpOperand::from_block(els))); + operation.set_operand(2, els_operand); let new = dialect::cf::CondBranchOp::from_operation(operation); let new: Arc> = Arc::new(RwLock::new(new)); op.replace(new.clone()); diff --git a/xrcf/src/dialect/func/op.rs b/xrcf/src/dialect/func/op.rs index d4906b33..ffc4d7ba 100644 --- a/xrcf/src/dialect/func/op.rs +++ b/xrcf/src/dialect/func/op.rs @@ -413,11 +413,8 @@ impl Parser { { let blocks = region.blocks(); - let blocks = blocks.try_read().unwrap(); - let block = blocks.first().unwrap(); - let arguments = arguments.vec(); - let arguments = arguments.try_read().unwrap(); - for argument in arguments.iter() { + let block = blocks.into_iter().next().unwrap(); + for argument in arguments.into_iter() { argument.set_parent(Some(block.clone())); } } diff --git a/xrcf/src/ir/block.rs b/xrcf/src/ir/block.rs index b14b05ef..ec247704 100644 --- a/xrcf/src/ir/block.rs +++ b/xrcf/src/ir/block.rs @@ -2,6 +2,7 @@ use crate::ir::BlockArgumentName; use crate::ir::GuardedOp; use crate::ir::GuardedOpOperand; use crate::ir::GuardedOperation; +use crate::ir::GuardedRegion; use crate::ir::Op; use crate::ir::Operation; use crate::ir::Region; @@ -164,10 +165,9 @@ impl Block { let region = region.try_read().unwrap(); let index = region.index_of(self); let blocks = region.blocks(); - let predecessors = blocks.try_read().unwrap(); let predecessors = match index { - Some(index) => predecessors[..index].to_vec(), - None => predecessors.clone(), + Some(index) => blocks.into_iter().take(index).collect(), + None => blocks.into_iter().collect(), }; Some(predecessors) } @@ -180,9 +180,8 @@ impl Block { let region = region.try_read().unwrap(); let index = region.index_of(self); let blocks = region.blocks(); - let successors = blocks.try_read().unwrap(); let successors = match index { - Some(index) => successors[index + 1..].to_vec(), + Some(index) => blocks.into_iter().skip(index + 1).collect(), None => panic!("Expected block to be in region"), }; Some(successors) @@ -335,24 +334,31 @@ impl Block { } None } + /// Return index of `op` in `self`. + /// + /// Returns `None` if `op` is not found in `self`. pub fn index_of(&self, op: &Operation) -> Option { let ops = self.ops(); let ops = ops.try_read().unwrap(); - for (i, current) in (&ops).iter().enumerate() { + ops.iter().position(|current| { let current = current.try_read().unwrap(); let current = current.operation(); - let current = current.try_read().unwrap(); - if *current == *op { - return Some(i); - } - } - None + let current = &*current.try_read().unwrap(); + std::ptr::eq(current, op) + }) } pub fn index_of_arc(&self, op: Arc>) -> Option { self.index_of(&*op.try_read().unwrap()) } - pub fn inline_region_before(&self, _region: Arc>) { - todo!() + /// Move the blocks that belong to `region` before `self`. + /// + /// The caller is in charge of transferring the control flow to the region + /// and pass it the correct block arguments. + pub fn inline_region_before(&self, region: Arc>) { + let parent = self.parent(); + let parent = parent.expect("no parent"); + let blocks = parent.blocks(); + blocks.splice(self, region.blocks()); } pub fn insert_op(&self, op: Arc>, index: usize) { let ops = self.ops(); @@ -588,3 +594,69 @@ impl GuardedBlock for Arc> { self.try_read().unwrap().unique_value_name(prefix) } } + +#[derive(Clone)] +pub struct Blocks { + vec: Arc>>>>, +} + +impl IntoIterator for Blocks { + type Item = Arc>; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + let vec = self.vec.try_read().unwrap(); + vec.clone().into_iter() + } +} + +impl Blocks { + pub fn new(vec: Arc>>>>) -> Self { + Self { vec } + } + pub fn vec(&self) -> Arc>>>> { + self.vec.clone() + } + /// Return the index of `block` in `self`. + /// + /// Returns `None` if `block` is not found in `self`. + pub fn index_of(&self, block: &Block) -> Option { + let vec = self.vec(); + let vec = vec.try_read().unwrap(); + if vec.is_empty() { + panic!("Trying to find block in empty set of blocks"); + } + vec.iter().position(|b| { + let b = &*b.try_read().unwrap(); + std::ptr::eq(b, block) + }) + } + fn transfer(&self, before: &Block, blocks: Blocks) { + let index = self.index_of(before); + let index = match index { + Some(index) => index, + None => { + panic!("Could not find block in blocks during transfer"); + } + }; + let vec = self.vec(); + let mut vec = vec.try_write().unwrap(); + let blocks = blocks.vec(); + let mut blocks = blocks.try_write().unwrap(); + vec.splice(index..index, blocks.iter().cloned()); + { + let parent = before.parent(); + for block in blocks.iter() { + let mut block = block.try_write().unwrap(); + block.set_parent(parent.clone()); + } + } + blocks.clear(); + } + /// Move `blocks` before `before` in `self`. + /// + /// This also handles side-effects like updating parents. + pub fn splice(&self, before: &Block, blocks: Blocks) { + self.transfer(before, blocks); + } +} diff --git a/xrcf/src/ir/mod.rs b/xrcf/src/ir/mod.rs index 29d178ae..e3985023 100644 --- a/xrcf/src/ir/mod.rs +++ b/xrcf/src/ir/mod.rs @@ -21,6 +21,7 @@ pub use attribute::IntegerAttr; pub use attribute::StringAttr; pub use block::Block; pub use block::BlockName; +pub use block::Blocks; pub use block::GuardedBlock; pub use block::UnsetBlock; pub use module::ModuleOp; diff --git a/xrcf/src/ir/module.rs b/xrcf/src/ir/module.rs index 7ca10cfb..a0db0dc8 100644 --- a/xrcf/src/ir/module.rs +++ b/xrcf/src/ir/module.rs @@ -58,8 +58,7 @@ impl ModuleOp { None => return Err(anyhow::anyhow!("Expected 1 region in module, got 0")), }; let blocks = region.blocks(); - let blocks = blocks.try_read().unwrap(); - let block = match blocks.first() { + let block = match blocks.into_iter().next() { Some(block) => block, None => return Err(anyhow::anyhow!("Expected 1 block in module, got 0")), }; diff --git a/xrcf/src/ir/op.rs b/xrcf/src/ir/op.rs index 290c30f2..c93bfa5d 100644 --- a/xrcf/src/ir/op.rs +++ b/xrcf/src/ir/op.rs @@ -244,6 +244,7 @@ pub trait GuardedOp { fn remove(&self); fn replace(&self, new: Arc>); fn result(&self, index: usize) -> Arc>; + fn set_parent(&self, parent: Arc>); } impl GuardedOp for Arc> { @@ -277,4 +278,7 @@ impl GuardedOp for Arc> { fn result(&self, index: usize) -> Arc> { self.try_read().unwrap().result(index) } + fn set_parent(&self, parent: Arc>) { + self.try_read().unwrap().set_parent(parent); + } } diff --git a/xrcf/src/ir/operation.rs b/xrcf/src/ir/operation.rs index f952798e..dd1a788f 100644 --- a/xrcf/src/ir/operation.rs +++ b/xrcf/src/ir/operation.rs @@ -125,8 +125,7 @@ pub fn display_region_inside_func( if let Some(region) = region { let region = region.try_read().unwrap(); let blocks = region.blocks(); - let blocks = blocks.try_read().unwrap(); - if blocks.is_empty() { + if blocks.into_iter().next().is_none() { write!(f, "\n") } else { region.display(f, indent) @@ -203,9 +202,7 @@ impl Operation { let region = self.region(); let region = region.expect("expected region"); let region = region.try_read().unwrap(); - let blocks = region.blocks(); - let blocks = blocks.try_read().unwrap(); - blocks.to_vec() + region.blocks().into_iter().collect::>() } pub fn operand_types(&self) -> Types { let operands = self.operands.vec(); diff --git a/xrcf/src/ir/region.rs b/xrcf/src/ir/region.rs index dbc80a34..7ba2b4ea 100644 --- a/xrcf/src/ir/region.rs +++ b/xrcf/src/ir/region.rs @@ -1,5 +1,6 @@ use crate::ir::block::Block; use crate::ir::BlockName; +use crate::ir::Blocks; use crate::ir::GuardedBlock; use crate::ir::Op; use crate::ir::UnsetBlock; @@ -14,7 +15,7 @@ pub struct Region { /// /// This field is an `Arc>` because the parser may read the /// blocks before the region is fully constructed. - blocks: Arc>>>>, + blocks: Blocks, // This field does not have to be an `Arc>` because the `Region` // is shared via `Arc>`. parent: Option>>, @@ -54,17 +55,14 @@ fn set_fresh_block_labels(blocks: &Vec>>) { } impl Region { - pub fn new( - blocks: Arc>>>>, - parent: Option>>, - ) -> Self { + pub fn new(blocks: Blocks, parent: Option>>) -> Self { Self { blocks, parent } } - pub fn blocks(&self) -> Arc>>>> { + pub fn blocks(&self) -> Blocks { self.blocks.clone() } pub fn block(&self, index: usize) -> Arc> { - self.blocks.read().unwrap()[index].clone() + self.blocks().into_iter().nth(index).unwrap() } pub fn parent(&self) -> Option>> { self.parent.clone() @@ -72,8 +70,7 @@ impl Region { pub fn ops(&self) -> Vec>> { let mut result = Vec::new(); let blocks = self.blocks(); - let blocks = blocks.try_read().unwrap(); - for block in blocks.iter() { + for block in blocks.into_iter() { let ops = block.ops(); let ops = ops.read().unwrap(); for op in ops.iter() { @@ -82,32 +79,20 @@ impl Region { } result } + /// Return the index of `block` in `self`. + /// + /// Returns `None` if `block` is not found in `self`. pub fn index_of(&self, block: &Block) -> Option { - let blocks = self.blocks(); - let blocks = blocks.try_read().unwrap(); - if blocks.is_empty() { - panic!("Region {self} has no blocks"); - } - for (i, current) in (&blocks).iter().enumerate() { - let current = current.try_read().unwrap(); - if *current == *block { - return Some(i); - } - } - None - } - pub fn is_empty(&self) -> bool { - let blocks = self.blocks(); - let blocks = blocks.try_read().unwrap(); - blocks.is_empty() + self.blocks().index_of(block) } - pub fn set_blocks(&mut self, blocks: Arc>>>>) { + pub fn set_blocks(&mut self, blocks: Blocks) { self.blocks = blocks; } pub fn add_empty_block(&self) -> UnsetBlock { let block = Block::default(); let block = Arc::new(RwLock::new(block)); let blocks = self.blocks(); + let blocks = blocks.vec(); let mut blocks = blocks.try_write().unwrap(); blocks.push(block.clone()); UnsetBlock::new(block) @@ -118,6 +103,7 @@ impl Region { let new = Block::default(); let new = Arc::new(RwLock::new(new)); let blocks = self.blocks(); + let blocks = blocks.vec(); let mut blocks = blocks.try_write().unwrap(); blocks.insert(index, new.clone()); UnsetBlock::new(new) @@ -128,6 +114,7 @@ impl Region { pub fn display(&self, f: &mut Formatter<'_>, indent: i32) -> std::fmt::Result { write!(f, " {{\n")?; let blocks = self.blocks(); + let blocks = blocks.vec(); let blocks = blocks.try_read().unwrap(); set_fresh_block_labels(&blocks); for block in blocks.iter() { @@ -139,10 +126,8 @@ impl Region { } /// Find a unique name for a block (for example, `bb2`). pub fn unique_block_name(&self) -> String { - let blocks = self.blocks(); - let blocks = blocks.try_read().unwrap(); let mut new_name: i32 = 0; - for block in blocks.iter() { + for block in self.blocks().into_iter() { let block = block.try_read().unwrap(); let label = block.label(); let label = label.try_read().unwrap(); @@ -173,7 +158,7 @@ impl Display for Region { impl Default for Region { fn default() -> Self { Self { - blocks: Arc::new(RwLock::new(vec![])), + blocks: Blocks::new(Arc::new(RwLock::new(vec![]))), parent: None, } } @@ -182,10 +167,10 @@ impl Default for Region { pub trait GuardedRegion { fn add_empty_block(&self) -> UnsetBlock; fn add_empty_block_before(&self, block: Arc>) -> UnsetBlock; - fn blocks(&self) -> Arc>>>>; + fn blocks(&self) -> Blocks; fn display(&self, f: &mut Formatter<'_>, indent: i32) -> std::fmt::Result; fn ops(&self) -> Vec>>; - fn set_blocks(&self, blocks: Arc>>>>); + fn set_blocks(&self, blocks: Blocks); fn set_parent(&self, parent: Option>>); fn unique_block_name(&self) -> String; } @@ -197,7 +182,7 @@ impl GuardedRegion for Arc> { fn add_empty_block_before(&self, block: Arc>) -> UnsetBlock { self.try_read().unwrap().add_empty_block_before(block) } - fn blocks(&self) -> Arc>>>> { + fn blocks(&self) -> Blocks { self.try_read().unwrap().blocks() } fn display(&self, f: &mut Formatter<'_>, indent: i32) -> std::fmt::Result { @@ -206,7 +191,7 @@ impl GuardedRegion for Arc> { fn ops(&self) -> Vec>> { self.try_read().unwrap().ops() } - fn set_blocks(&self, blocks: Arc>>>>) { + fn set_blocks(&self, blocks: Blocks) { self.try_write().unwrap().set_blocks(blocks); } fn set_parent(&self, parent: Option>>) { diff --git a/xrcf/src/ir/value.rs b/xrcf/src/ir/value.rs index 70681b3e..9adb0d76 100644 --- a/xrcf/src/ir/value.rs +++ b/xrcf/src/ir/value.rs @@ -683,6 +683,16 @@ pub struct Values { values: Arc>>>>, } +impl IntoIterator for Values { + type Item = Arc>; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + let vec = self.values.try_read().unwrap(); + vec.clone().into_iter() + } +} + impl Values { pub fn from_vec(values: Vec>>) -> Self { Values { diff --git a/xrcf/src/parser/parser.rs b/xrcf/src/parser/parser.rs index 5e85eee8..129aa8c0 100644 --- a/xrcf/src/parser/parser.rs +++ b/xrcf/src/parser/parser.rs @@ -9,6 +9,7 @@ use crate::ir::Attribute; use crate::ir::Block; use crate::ir::BlockName; use crate::ir::BlockPtr; +use crate::ir::Blocks; use crate::ir::BooleanAttr; use crate::ir::GuardedBlock; use crate::ir::GuardedOp; @@ -171,8 +172,7 @@ enum Dialects { /// Replace block labels in operands by pointers. /// -/// Replaces operands that use block labels and point to `block` by pointers -/// after the operands have been parsed earlier. +/// More specifically, replaces [Value::BlockLabel] by [Value::BlockPtr]. /// /// Assumes it is only called during the parsing of a block. fn replace_block_labels(block: Arc>) { @@ -186,8 +186,7 @@ fn replace_block_labels(block: Arc>) { let parent = block.parent().expect("No parent"); // Assumes the current block was not yet added to the parent region. let predecessors = parent.blocks(); - let predecessors = predecessors.try_read().unwrap(); - for predecessor in predecessors.iter() { + for predecessor in predecessors.into_iter() { let predecessor = predecessor.try_read().unwrap(); let ops = predecessor.ops(); let ops = ops.try_read().unwrap(); @@ -340,7 +339,7 @@ impl Parser { self.expect(TokenKind::LBrace)?; let blocks = vec![]; let blocks = Arc::new(RwLock::new(blocks)); - region.set_blocks(blocks.clone()); + region.set_blocks(Blocks::new(blocks.clone())); while !self.is_region_end() { let block = self.parse_block(Some(region.clone()))?; let mut blocks = blocks.try_write().unwrap(); @@ -418,6 +417,7 @@ impl Parser { .write() .unwrap() .blocks() + .vec() .try_write() .unwrap() .push(block.clone()); diff --git a/xrcf/src/targ3t/llvmir/op.rs b/xrcf/src/targ3t/llvmir/op.rs index 1b2207c8..e3c0ec39 100644 --- a/xrcf/src/targ3t/llvmir/op.rs +++ b/xrcf/src/targ3t/llvmir/op.rs @@ -408,9 +408,7 @@ impl Op for ModuleOp { write!(f, "\n\n")?; let region = self.operation().region(); if let Some(region) = region { - let blocks = region.blocks(); - let blocks = blocks.try_read().unwrap(); - for block in blocks.iter() { + for block in region.blocks().into_iter() { block.display(f, indent)?; } } diff --git a/xrcf/tests/arith.rs b/xrcf/tests/arith.rs index bd069a35..e3e66306 100644 --- a/xrcf/tests/arith.rs +++ b/xrcf/tests/arith.rs @@ -35,7 +35,7 @@ fn parse_addi() { "}; let expected = indoc! {" module { - func.func @test_addi(%arg0 : i64) -> i64 { + func.func @test_addi(%arg0 : i64) -> i64 { %0 = arith.constant 1 : i64 %1 = arith.constant 2 : i64 %2 = arith.addi %0, %1 : i64