use alloc::{rc::Rc, vec::Vec};
use core::{fmt, ptr::NonNull, sync::atomic::AtomicBool};
use smallvec::SmallVec;
use super::{entity::EntityParent, *};
use crate::{Context, interner};
pub type BlockRef = UnsafeIntrusiveEntityRef<Block>;
pub type BlockList = EntityList<Block>;
pub type BlockCursor<'a> = EntityListCursor<'a, Block>;
pub type BlockCursorMut<'a> = EntityListCursorMut<'a, Block>;
pub type PreOrderBlockIter = cfg::PreOrderIter<BlockRef>;
pub type PostOrderBlockIter = cfg::PostOrderIter<BlockRef>;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct BlockId(u32);
impl BlockId {
const USER_DEFINED_TAG: u32 = 1u32 << 31;
pub const fn from_symbol(sym: interner::Symbol) -> Self {
assert!(
sym.as_u32() & Self::USER_DEFINED_TAG == 0,
"cannot convert symbol id to block id: out of range"
);
Self(sym.as_u32())
}
pub const fn is_user_defined(self) -> bool {
self.0 & Self::USER_DEFINED_TAG == Self::USER_DEFINED_TAG
}
pub const fn from_u32(id: u32) -> Self {
assert!(
id & Self::USER_DEFINED_TAG == 0,
"invalid block id: value must be less than 2^31"
);
Self(id)
}
pub const fn as_u32(&self) -> u32 {
self.0 & !Self::USER_DEFINED_TAG
}
}
impl EntityId for BlockId {
#[inline(always)]
fn as_usize(&self) -> usize {
self.0 as usize
}
}
impl fmt::Debug for BlockId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
f.debug_struct("BlockId")
.field("is_user_defined", &self.is_user_defined())
.field("id", &self.as_u32())
.finish()
} else {
fmt::Display::fmt(self, f)
}
}
}
impl fmt::Display for BlockId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_user_defined() {
write!(f, "^{}", unsafe {
core::mem::transmute::<u32, interner::Symbol>(self.as_u32())
})
} else {
write!(f, "^block{}", self.as_u32())
}
}
}
pub struct Block {
context: NonNull<Context>,
id: BlockId,
valid_op_ordering: AtomicBool,
uses: BlockOperandList,
body: OpList,
arguments: Vec<BlockArgumentRef>,
}
impl Eq for Block {}
impl PartialEq for Block {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl core::hash::Hash for Block {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
impl fmt::Debug for Block {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Block")
.field("id", &self.id)
.field_with("region", |f| match self.parent() {
None => f.write_str("None"),
Some(r) => write!(f, "Some({r:p})"),
})
.field_with("arguments", |f| {
let mut list = f.debug_list();
for arg in self.arguments.iter() {
list.entry_with(|f| f.write_fmt(format_args!("{}", &arg.borrow())));
}
list.finish()
})
.finish_non_exhaustive()
}
}
impl fmt::Display for Block {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.id)
}
}
impl crate::formatter::PrettyPrint for Block {
fn render(&self) -> crate::formatter::Document {
let flags = OpPrintingFlags::default();
let mut printer = crate::print::AsmPrinter::new(self.context_rc(), &flags);
printer.print_block(self);
printer.finish()
}
}
impl Spanned for Block {
fn span(&self) -> SourceSpan {
self.body.front().get().map(|op| op.span()).unwrap_or_default()
}
}
impl Entity for Block {}
impl EntityWithId for Block {
type Id = BlockId;
fn id(&self) -> Self::Id {
self.id
}
}
impl EntityWithParent for Block {
type Parent = Region;
}
impl EntityListItem for Block {}
impl EntityParent<Operation> for Block {
fn offset() -> usize {
core::mem::offset_of!(Block, body)
}
}
impl EntityParent<BlockOperand> for Block {
fn offset() -> usize {
core::mem::offset_of!(Block, uses)
}
}
impl Usable for Block {
type Use = BlockOperand;
#[inline(always)]
fn uses(&self) -> &BlockOperandList {
&self.uses
}
#[inline(always)]
fn uses_mut(&mut self) -> &mut BlockOperandList {
&mut self.uses
}
}
impl cfg::Graph for Block {
type ChildEdgeIter = BlockSuccessorEdgesIter;
type ChildIter = BlockSuccessorIter;
type Edge = BlockOperandRef;
type Node = BlockRef;
fn size(&self) -> usize {
if let Some(term) = self.terminator() {
term.borrow().num_successors()
} else {
0
}
}
fn entry_node(&self) -> Self::Node {
self.as_block_ref()
}
fn children(parent: Self::Node) -> Self::ChildIter {
BlockSuccessorIter::new(parent)
}
fn children_edges(parent: Self::Node) -> Self::ChildEdgeIter {
BlockSuccessorEdgesIter::new(parent)
}
fn edge_dest(edge: Self::Edge) -> Self::Node {
edge.borrow().successor()
}
}
impl<'a> cfg::InvertibleGraph for &'a Block {
type Inverse = cfg::Inverse<&'a Block>;
type InvertibleChildEdgeIter = BlockPredecessorEdgesIter;
type InvertibleChildIter = BlockPredecessorIter;
fn inverse(self) -> Self::Inverse {
cfg::Inverse::new(self)
}
fn inverse_children(parent: Self::Node) -> Self::InvertibleChildIter {
BlockPredecessorIter::new(parent)
}
fn inverse_children_edges(parent: Self::Node) -> Self::InvertibleChildEdgeIter {
BlockPredecessorEdgesIter::new(parent)
}
}
impl cfg::Graph for BlockRef {
type ChildEdgeIter = BlockSuccessorEdgesIter;
type ChildIter = BlockSuccessorIter;
type Edge = BlockOperandRef;
type Node = BlockRef;
fn size(&self) -> usize {
if let Some(term) = self.borrow().terminator() {
term.borrow().num_successors()
} else {
0
}
}
fn entry_node(&self) -> Self::Node {
*self
}
fn children(parent: Self::Node) -> Self::ChildIter {
BlockSuccessorIter::new(parent)
}
fn children_edges(parent: Self::Node) -> Self::ChildEdgeIter {
BlockSuccessorEdgesIter::new(parent)
}
fn edge_dest(edge: Self::Edge) -> Self::Node {
edge.borrow().successor()
}
}
impl cfg::InvertibleGraph for BlockRef {
type Inverse = cfg::Inverse<Self>;
type InvertibleChildEdgeIter = BlockPredecessorEdgesIter;
type InvertibleChildIter = BlockPredecessorIter;
fn inverse(self) -> Self::Inverse {
cfg::Inverse::new(self)
}
fn inverse_children(parent: Self::Node) -> Self::InvertibleChildIter {
BlockPredecessorIter::new(parent)
}
fn inverse_children_edges(parent: Self::Node) -> Self::InvertibleChildEdgeIter {
BlockPredecessorEdgesIter::new(parent)
}
}
#[doc(hidden)]
pub struct BlockSuccessorIter {
iter: BlockSuccessorEdgesIter,
}
impl BlockSuccessorIter {
pub fn new(parent: BlockRef) -> Self {
Self {
iter: BlockSuccessorEdgesIter::new(parent),
}
}
}
impl ExactSizeIterator for BlockSuccessorIter {
#[inline]
fn len(&self) -> usize {
self.iter.len()
}
#[inline]
fn is_empty(&self) -> bool {
self.iter.is_empty()
}
}
impl Iterator for BlockSuccessorIter {
type Item = BlockRef;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|bo| bo.borrow().successor())
}
#[inline]
fn collect<B: FromIterator<Self::Item>>(self) -> B
where
Self: Sized,
{
let Some(terminator) = self.iter.terminator.as_ref() else {
return B::from_iter([]);
};
let terminator = terminator.borrow();
let successors = terminator.successors();
B::from_iter(
successors.all().as_slice()[self.iter.index..self.iter.num_successors]
.iter()
.map(|succ| succ.block.borrow().successor()),
)
}
fn collect_into<E: Extend<Self::Item>>(self, collection: &mut E) -> &mut E
where
Self: Sized,
{
let Some(terminator) = self.iter.terminator.as_ref() else {
return collection;
};
let terminator = terminator.borrow();
let successors = terminator.successors();
collection.extend(
successors.all().as_slice()[self.iter.index..self.iter.num_successors]
.iter()
.map(|succ| succ.block.borrow().successor()),
);
collection
}
}
#[doc(hidden)]
pub struct BlockSuccessorEdgesIter {
terminator: Option<OperationRef>,
num_successors: usize,
index: usize,
}
impl BlockSuccessorEdgesIter {
pub fn new(parent: BlockRef) -> Self {
let terminator = parent.borrow().terminator();
let num_successors = terminator.as_ref().map(|t| t.borrow().num_successors()).unwrap_or(0);
Self {
terminator,
num_successors,
index: 0,
}
}
}
impl ExactSizeIterator for BlockSuccessorEdgesIter {
#[inline]
fn len(&self) -> usize {
self.num_successors.saturating_sub(self.index)
}
#[inline]
fn is_empty(&self) -> bool {
self.index >= self.num_successors
}
}
impl Iterator for BlockSuccessorEdgesIter {
type Item = BlockOperandRef;
fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.num_successors {
return None;
}
let terminator = unsafe { self.terminator.as_ref().unwrap_unchecked() };
let index = self.index;
self.index += 1;
Some(terminator.borrow().successor(index).dest)
}
fn collect<B: FromIterator<Self::Item>>(self) -> B
where
Self: Sized,
{
let Some(terminator) = self.terminator.as_ref() else {
return B::from_iter([]);
};
let terminator = terminator.borrow();
let successors = terminator.successors();
B::from_iter(
successors.all().as_slice()[self.index..self.num_successors]
.iter()
.map(|succ| succ.block),
)
}
fn collect_into<E: Extend<Self::Item>>(self, collection: &mut E) -> &mut E
where
Self: Sized,
{
let Some(terminator) = self.terminator.as_ref() else {
return collection;
};
let terminator = terminator.borrow();
let successors = terminator.successors();
collection.extend(
successors.all().as_slice()[self.index..self.num_successors]
.iter()
.map(|succ| succ.block),
);
collection
}
}
#[doc(hidden)]
pub struct BlockPredecessorIter {
preds: SmallVec<[BlockRef; 4]>,
index: usize,
}
impl BlockPredecessorIter {
pub fn new(child: BlockRef) -> Self {
let preds = child.borrow().predecessors().map(|bo| bo.predecessor()).collect();
Self { preds, index: 0 }
}
#[inline(always)]
pub fn into_inner(self) -> SmallVec<[BlockRef; 4]> {
self.preds
}
}
impl ExactSizeIterator for BlockPredecessorIter {
#[inline]
fn len(&self) -> usize {
self.preds.len().saturating_sub(self.index)
}
#[inline]
fn is_empty(&self) -> bool {
self.index >= self.preds.len()
}
}
impl Iterator for BlockPredecessorIter {
type Item = BlockRef;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.is_empty() {
return None;
}
let index = self.index;
self.index += 1;
Some(self.preds[index])
}
fn collect<B: FromIterator<Self::Item>>(self) -> B
where
Self: Sized,
{
B::from_iter(self.preds)
}
fn collect_into<E: Extend<Self::Item>>(self, collection: &mut E) -> &mut E
where
Self: Sized,
{
collection.extend(self.preds);
collection
}
}
#[doc(hidden)]
pub struct BlockPredecessorEdgesIter {
preds: SmallVec<[BlockOperandRef; 4]>,
index: usize,
}
impl BlockPredecessorEdgesIter {
pub fn new(child: BlockRef) -> Self {
let preds = child
.borrow()
.predecessors()
.map(|bo| unsafe { BlockOperandRef::from_raw(&*bo) })
.collect();
Self { preds, index: 0 }
}
}
impl ExactSizeIterator for BlockPredecessorEdgesIter {
#[inline]
fn len(&self) -> usize {
self.preds.len().saturating_sub(self.index)
}
#[inline]
fn is_empty(&self) -> bool {
self.index >= self.preds.len()
}
}
impl Iterator for BlockPredecessorEdgesIter {
type Item = BlockOperandRef;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.is_empty() {
return None;
}
let index = self.index;
self.index += 1;
Some(self.preds[index])
}
fn collect<B: FromIterator<Self::Item>>(self) -> B
where
Self: Sized,
{
B::from_iter(self.preds)
}
fn collect_into<E: Extend<Self::Item>>(self, collection: &mut E) -> &mut E
where
Self: Sized,
{
collection.extend(self.preds);
collection
}
}
impl Block {
pub fn new(context: Rc<Context>, id: BlockId) -> Self {
Self {
context: unsafe { NonNull::new_unchecked(Rc::as_ptr(&context).cast_mut()) },
id,
valid_op_ordering: AtomicBool::new(true),
uses: Default::default(),
body: Default::default(),
arguments: Default::default(),
}
}
#[inline(always)]
pub fn context(&self) -> &Context {
unsafe { self.context.as_ref() }
}
pub fn context_rc(&self) -> Rc<Context> {
unsafe {
let ptr = self.context.as_ptr().cast_const();
Rc::increment_strong_count(ptr);
Rc::from_raw(ptr)
}
}
#[inline]
pub fn as_block_ref(&self) -> BlockRef {
unsafe { BlockRef::from_raw(self) }
}
pub fn parent(&self) -> Option<RegionRef> {
self.as_block_ref().parent()
}
pub fn parent_op(&self) -> Option<OperationRef> {
self.parent().and_then(|region| region.parent())
}
pub fn parent_block(&self) -> Option<BlockRef> {
self.parent_op().and_then(|op| op.parent())
}
pub fn is_entry_block(&self) -> bool {
if let Some(parent) = self.parent().map(|r| r.borrow()) {
parent.entry_block_ref().is_some_and(|entry| entry == self.as_block_ref())
} else {
false
}
}
#[inline]
pub fn front(&self) -> Option<OperationRef> {
self.body.front().as_pointer()
}
#[inline]
pub fn back(&self) -> Option<OperationRef> {
self.body.back().as_pointer()
}
#[inline(always)]
pub fn body(&self) -> &OpList {
&self.body
}
#[inline(always)]
pub fn body_mut(&mut self) -> &mut OpList {
&mut self.body
}
}
impl Block {
#[inline]
pub fn has_arguments(&self) -> bool {
!self.arguments.is_empty()
}
#[inline]
pub fn num_arguments(&self) -> usize {
self.arguments.len()
}
#[inline(always)]
pub fn arguments(&self) -> &[BlockArgumentRef] {
self.arguments.as_slice()
}
#[inline(always)]
pub fn arguments_mut(&mut self) -> &mut Vec<BlockArgumentRef> {
&mut self.arguments
}
pub fn argument_values(&self) -> impl ExactSizeIterator<Item = ValueRef> + '_ {
self.arguments.iter().copied().map(|arg| arg as ValueRef)
}
pub fn argument_types(&self) -> impl ExactSizeIterator<Item = Type> + '_ {
self.arguments.iter().copied().map(|arg| arg.borrow().ty().clone())
}
#[inline]
pub fn get_argument(&self, index: usize) -> BlockArgumentRef {
self.arguments[index]
}
pub fn erase_argument(&mut self, index: usize) {
assert!(
!self.arguments[index].borrow().is_used(),
"cannot erase block arguments with uses"
);
self.arguments.remove(index);
}
pub fn erase_arguments<F>(&mut self, should_erase: F)
where
F: Fn(&BlockArgument) -> bool,
{
self.arguments.retain(|arg| {
let arg = arg.borrow();
let keep = !should_erase(&arg);
assert!(keep || !arg.is_used(), "cannot erase block arguments with uses");
keep
});
}
pub fn erase(&mut self) {
if let Some(mut region) = self.parent() {
let mut region = region.borrow_mut();
let body = region.body_mut();
let mut cursor = unsafe { body.cursor_mut_from_ptr(self.as_block_ref()) };
cursor.remove();
}
}
}
impl Block {
#[track_caller]
pub fn insert_after(&mut self, after: BlockRef) {
assert!(
self.parent().is_none(),
"cannot insert block that is already attached to another region"
);
let mut region = after.parent().expect("'after' block is not attached to a region");
{
let mut region = region.borrow_mut();
let region_body = region.body_mut();
let mut cursor = unsafe { region_body.cursor_mut_from_ptr(after) };
cursor.insert_after(self.as_block_ref());
}
}
#[track_caller]
pub fn insert_before(&mut self, before: BlockRef) {
assert!(
self.parent().is_none(),
"cannot insert block that is already attached to another region"
);
let mut region = before.parent().expect("'before' block is not attached to a region");
{
let mut region = region.borrow_mut();
let region_body = region.body_mut();
let mut cursor = unsafe { region_body.cursor_mut_from_ptr(before) };
cursor.insert_before(self.as_block_ref());
}
}
#[track_caller]
pub fn insert_at_end(&mut self, mut region: RegionRef) {
assert!(
self.parent().is_none(),
"cannot insert block that is already attached to another region"
);
region.borrow_mut().body_mut().push_back(self.as_block_ref());
}
#[track_caller]
pub fn move_before(&mut self, before: BlockRef) {
self.unlink();
self.insert_before(before);
}
fn unlink(&mut self) {
if let Some(mut region) = self.parent() {
let mut region = region.borrow_mut();
unsafe {
let mut cursor = region.body_mut().cursor_mut_from_ptr(self.as_block_ref());
cursor.remove();
}
}
}
pub fn splice_block(&mut self, block: &mut Self) {
let ops = block.body_mut().take();
self.body.back_mut().splice_after(ops);
}
pub fn splice_block_before(&mut self, block: &mut Self, ip: OperationRef) {
assert_eq!(ip.parent().unwrap(), block.as_block_ref());
let ops = block.body_mut().take();
let mut cursor = unsafe { self.body.cursor_mut_from_ptr(ip) };
cursor.splice_before(ops);
}
pub fn splice_block_after(&mut self, block: &mut Self, ip: OperationRef) {
assert_eq!(ip.parent().unwrap(), block.as_block_ref());
let ops = block.body_mut().take();
let mut cursor = unsafe { self.body.cursor_mut_from_ptr(ip) };
cursor.splice_after(ops);
}
pub fn split_block(&mut self, before: OperationRef) -> BlockRef {
let this = self.as_block_ref();
assert!(
BlockRef::ptr_eq(
&this,
&before.parent().expect("'before' op is not attached to a block")
),
"cannot split block using an operation that does not belong to the block being split"
);
let mut region = self.parent().expect("block is not attached to a region");
let parent = region.parent().expect("parent region is not attached to an operation");
let mut new_block = parent.borrow().context_rc().create_block();
{
let mut region_mut = region.borrow_mut();
let blocks = region_mut.body_mut();
let mut cursor = unsafe { blocks.cursor_mut_from_ptr(this) };
cursor.insert_after(new_block);
}
let ops = {
let mut cursor = unsafe { self.body.cursor_mut_from_ptr(before) };
cursor.move_prev();
cursor.split_after()
};
new_block.borrow_mut().body_mut().back_mut().splice_after(ops);
new_block
}
pub fn clear(&mut self) {
self.drop_all_references();
self.body_mut().clear();
}
}
impl Block {
pub fn is_legal_to_hoist_into(&self) -> bool {
use crate::traits::ReturnLike;
let Some(terminator) = self.terminator() else {
return true;
};
if self.num_successors() == 0 {
return false;
}
let terminator = terminator.borrow();
!terminator.is_memory_effect_free() || terminator.implements::<dyn ReturnLike>()
}
pub fn has_ssa_dominance(&self) -> bool {
self.parent_op()
.and_then(|op| {
op.borrow()
.as_trait::<dyn RegionKindInterface>()
.map(|rki| rki.has_ssa_dominance())
})
.unwrap_or(true)
}
pub fn traverse_ancestors<F>(block: BlockRef, mut f: F) -> Option<BlockRef>
where
F: FnMut(BlockRef) -> bool,
{
let mut block = Some(block);
while let Some(current) = block.take() {
if f(current) {
return Some(current);
}
block = current.borrow().parent_block();
}
None
}
pub fn get_blocks_in_same_region(a: BlockRef, b: BlockRef) -> Option<(BlockRef, BlockRef)> {
let a_region = a.parent().unwrap();
let b_region = b.parent().unwrap();
if a_region == b_region {
return Some((a, b));
}
let mut a_depth = 0;
let result = Self::traverse_ancestors(a, |block| {
a_depth += 1;
block.parent().is_some_and(|r| r == b_region)
});
if let Some(a) = result {
return Some((a, b));
}
let mut b_depth = 0;
let result = Self::traverse_ancestors(b, |block| {
b_depth += 1;
block.parent().is_some_and(|r| r == a_region)
});
if let Some(b) = result {
return Some((a, b));
}
let mut a = Some(a);
let mut b = Some(b);
loop {
use core::cmp::Ordering;
match a_depth.cmp(&b_depth) {
Ordering::Greater => {
a = a.and_then(|a| a.grandparent().and_then(|gp| gp.parent()));
a_depth -= 1;
}
Ordering::Less => {
b = b.and_then(|b| b.grandparent().and_then(|gp| gp.parent()));
b_depth -= 1;
}
Ordering::Equal => break,
}
}
while let Some(next_a) = a.take() {
let next_a_parent = next_a.parent();
let b_parent = b.as_ref().and_then(|b| b.parent());
if next_a_parent == b_parent {
return Some((next_a, b.unwrap()));
}
a = next_a_parent.and_then(|r| r.grandparent());
b = b_parent.and_then(|r| r.grandparent());
}
None
}
}
impl Block {
#[inline(always)]
pub fn has_predecessors(&self) -> bool {
self.is_used()
}
#[inline(always)]
pub fn predecessors(&self) -> BlockOperandIter<'_> {
self.iter_uses()
}
pub fn get_single_predecessor(&self) -> Option<BlockRef> {
let front = self.uses.front().as_pointer()?;
let back = self.uses.back().as_pointer().unwrap();
if BlockOperandRef::ptr_eq(&front, &back) {
Some(front.borrow().predecessor())
} else {
None
}
}
pub fn get_unique_predecessor(&self) -> Option<BlockRef> {
let mut front = self.uses.front();
let block_operand = front.get()?;
let block = block_operand.predecessor();
loop {
front.move_next();
if let Some(bo) = front.get() {
if !BlockRef::ptr_eq(&block, &bo.predecessor()) {
break None;
}
} else {
break Some(block);
}
}
}
#[inline]
pub fn has_successors(&self) -> bool {
self.num_successors() > 0
}
pub fn num_successors(&self) -> usize {
self.terminator().map(|op| op.borrow().num_successors()).unwrap_or(0)
}
#[track_caller]
pub fn get_successor(&self, index: usize) -> BlockRef {
let op = self.terminator().expect("this block has no terminator");
op.borrow().successor(index).dest.borrow().successor()
}
pub fn drop_all_references(&mut self) {
let mut cursor = self.body.front_mut();
while let Some(mut op) = cursor.as_pointer() {
op.borrow_mut().drop_all_references();
cursor.move_next();
}
}
pub fn drop_all_defined_value_uses(&mut self) {
let mut cursor = self.body.back_mut();
while let Some(mut op) = cursor.as_pointer() {
op.borrow_mut().drop_all_defined_value_uses();
cursor.move_prev();
}
for arg in self.arguments.iter_mut() {
let mut arg = arg.borrow_mut();
arg.uses_mut().clear();
}
self.drop_all_uses();
}
#[inline]
pub fn drop_all_uses(&mut self) {
self.uses_mut().clear();
}
pub fn replace_all_uses_with(&mut self, mut replacement: BlockRef) {
if BlockRef::ptr_eq(&self.as_block_ref(), &replacement) {
return;
}
let mut replacement_block = replacement.borrow_mut();
let uses = self.uses_mut().take();
replacement_block.uses_mut().back_mut().splice_after(uses);
}
#[inline(always)]
pub(super) fn is_op_order_valid(&self) -> bool {
use core::sync::atomic::Ordering;
self.valid_op_ordering.load(Ordering::Acquire)
}
#[inline(always)]
pub(super) fn mark_op_order_valid(&self) {
use core::sync::atomic::Ordering;
self.valid_op_ordering.store(true, Ordering::Release);
}
pub(super) fn invalidate_op_order(&mut self) {
use core::sync::atomic::Ordering;
assert!(self.verify_op_order());
self.valid_op_ordering.store(false, Ordering::Release);
}
pub(super) fn verify_op_order(&self) -> bool {
if !self.is_op_order_valid() {
return false;
}
if self.body.is_empty()
|| OperationRef::ptr_eq(
&self.body.front().as_pointer().unwrap(),
&self.body.back().as_pointer().unwrap(),
)
{
return true;
}
let mut cursor = self.body.front();
let mut prev = None;
while let Some(op) = cursor.as_pointer() {
cursor.move_next();
if prev.is_none() {
prev = Some(op);
continue;
}
let prev_order = prev.take().unwrap().borrow().order();
let current_order = op.borrow().order().unwrap_or(u32::MAX);
if prev_order.is_some_and(|o| o >= current_order) {
return false;
}
prev = Some(op);
}
true
}
pub fn terminator(&self) -> Option<OperationRef> {
if !self.has_terminator() {
None
} else {
self.body.back().as_pointer()
}
}
pub fn has_terminator(&self) -> bool {
use crate::traits::Terminator;
!self.body.is_empty()
&& self.body.back().get().is_some_and(|op| op.implements::<dyn Terminator>())
}
}
pub type BlockOperandRef = UnsafeIntrusiveEntityRef<BlockOperand>;
pub type BlockOperandList = EntityList<BlockOperand>;
#[allow(unused)]
pub type BlockOperandCursor<'a> = EntityListCursor<'a, BlockOperand>;
#[allow(unused)]
pub type BlockOperandCursorMut<'a> = EntityListCursorMut<'a, BlockOperand>;
pub type BlockOperandIter<'a> = EntityListIter<'a, BlockOperand>;
pub struct BlockOperand {
pub owner: OperationRef,
pub index: u8,
}
impl Entity for BlockOperand {}
impl EntityWithParent for BlockOperand {
type Parent = Block;
}
impl EntityListItem for BlockOperand {
#[track_caller]
fn on_inserted(
this: UnsafeIntrusiveEntityRef<Self>,
_cursor: &mut EntityListCursorMut<'_, Self>,
) {
assert!(this.parent().is_some());
}
}
impl BlockOperand {
#[inline]
pub fn new(owner: OperationRef, index: u8) -> Self {
Self { owner, index }
}
pub fn as_block_operand_ref(&self) -> BlockOperandRef {
unsafe { BlockOperandRef::from_raw(self) }
}
pub fn block_id(&self) -> BlockId {
self.successor().borrow().id
}
pub fn predecessor(&self) -> BlockRef {
self.owner.parent().expect("owning operation is not attached to a block")
}
#[inline]
pub fn successor(&self) -> BlockRef {
self.as_block_operand_ref().parent().unwrap_or_else(|| {
panic!(
"block operand is dead at index {} in {}",
self.index,
&self.owner.borrow().name()
)
})
}
pub fn set(&mut self, mut block: BlockRef) {
self.unlink();
block.borrow_mut().insert_use(self.as_block_operand_ref());
}
}
impl fmt::Debug for BlockOperand {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BlockOperand")
.field("block", &self.successor())
.field_with("owner", |f| write!(f, "{:p}", &self.owner))
.field("index", &self.index)
.finish()
}
}
impl fmt::Display for BlockOperand {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", &self.block_id())
}
}
impl StorableEntity for BlockOperand {
#[inline(always)]
fn index(&self) -> usize {
self.index as usize
}
unsafe fn set_index(&mut self, index: usize) {
self.index = index.try_into().expect("too many successors");
}
fn unlink(&mut self) {
let this = self.as_block_operand_ref();
if !this.is_linked() {
return;
}
let Some(mut parent) = this.parent() else {
return;
};
let mut block = parent.borrow_mut();
let uses = block.uses_mut();
unsafe {
let mut cursor = uses.cursor_mut_from_ptr(this);
cursor.remove();
}
}
}