mod aliasing;
mod range;
mod stack;
use core::{any::Any, fmt};
pub use self::{
aliasing::ValueOrAlias,
range::{AsValueRange, ValueRange},
stack::StackOperand,
};
use super::*;
use crate::{DynHash, DynPartialEq, PartialEqable, any::AsAny, interner};
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct ValueId(u32);
impl ValueId {
const ID_SHIFT: u32 = 6;
const MAX_VALUE_ID: u32 = (u8::MAX as u32) << 24;
const OP_RESULT_INDEX_MASK: u32 = (u8::MAX as u32) >> 2;
const OP_RESULT_TAG: u32 = 1u32 << 30;
const USER_DEFINED_TAG: u32 = 1u32 << 31;
pub const fn from_symbol(sym: interner::Symbol) -> Self {
let sym = sym.as_u32();
debug_assert!(
sym < Self::MAX_VALUE_ID,
"cannot convert symbol id to value id: bits set in reserved range"
);
Self((sym << Self::ID_SHIFT) | Self::USER_DEFINED_TAG)
}
pub const fn is_user_defined(self) -> bool {
self.0 & Self::USER_DEFINED_TAG == Self::USER_DEFINED_TAG
}
pub const fn is_op_result(self) -> bool {
self.0 & Self::OP_RESULT_TAG == Self::OP_RESULT_TAG
}
pub const fn result_index(self) -> Option<u8> {
if self.is_op_result() {
Some((self.0 & Self::OP_RESULT_INDEX_MASK) as u8)
} else {
None
}
}
pub const fn with_result_index(self, index: u8) -> Self {
assert!(
index as u32 <= Self::OP_RESULT_INDEX_MASK,
"invalid op result index: must be less than 64",
);
Self((self.0 & !Self::OP_RESULT_INDEX_MASK) | index as u32)
}
pub(super) const fn without_result_index(self) -> Self {
Self(self.0 & !(Self::OP_RESULT_INDEX_MASK | Self::OP_RESULT_TAG))
}
pub const fn from_u32(id: u32) -> Self {
assert!(id < Self::MAX_VALUE_ID, "invalid value id: bits set in reserved range");
Self(id << Self::ID_SHIFT)
}
pub const fn as_u32(&self) -> u32 {
let untagged = self.0 & !(Self::USER_DEFINED_TAG | Self::OP_RESULT_TAG);
untagged >> Self::ID_SHIFT
}
pub const fn as_symbol_id(self) -> Option<interner::Symbol> {
if self.is_user_defined() {
Some(unsafe { core::mem::transmute::<u32, interner::Symbol>(self.as_u32()) })
} else {
None
}
}
}
impl EntityId for ValueId {
#[inline(always)]
fn as_usize(&self) -> usize {
self.0 as usize
}
}
impl fmt::Debug for ValueId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
f.debug_struct("ValueId")
.field("is_user_defined", &self.is_user_defined())
.field("is_op_result", &self.is_op_result())
.field("op_result_index", &self.result_index())
.field("id", &self.as_u32())
.finish()
} else {
fmt::Display::fmt(self, f)
}
}
}
impl fmt::Display for ValueId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(sym) = self.as_symbol_id() {
write!(f, "%{sym}")
} else if let Some(index) = self.result_index() {
write!(f, "%{}#{index}", self.as_u32())
} else {
write!(f, "%{}", self.as_u32())
}
}
}
pub trait Value:
AsAny
+ EntityWithId<Id = ValueId>
+ Spanned
+ Usable<Use = OpOperandImpl>
+ fmt::Debug
+ fmt::Display
+ PartialEqable
+ DynPartialEq
+ DynHash
{
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
fn set_span(&mut self, span: SourceSpan);
fn ty(&self) -> &Type;
fn set_type(&mut self, ty: Type);
fn get_defining_op(&self) -> Option<OperationRef>;
fn parent_region(&self) -> Option<RegionRef> {
self.parent_block().and_then(|block| block.parent())
}
fn parent_block(&self) -> Option<BlockRef>;
fn is_used_outside_of_block(&self, block: &BlockRef) -> bool {
self.iter_uses()
.any(|user| user.owner.parent().is_some_and(|blk| !BlockRef::ptr_eq(&blk, block)))
}
fn replace_all_uses_with(&mut self, mut replacement: ValueRef) {
let mut cursor = self.uses_mut().front_mut();
while let Some(mut user) = cursor.as_pointer() {
{
let mut user = user.borrow_mut();
user.value = Some(replacement);
}
cursor.remove();
replacement.borrow_mut().insert_use(user);
}
}
fn replace_all_uses_except(&mut self, mut replacement: ValueRef, exceptions: &[OperationRef]) {
let mut cursor = self.uses_mut().front_mut();
while let Some(mut user) = cursor.as_pointer() {
{
let mut user = user.borrow_mut();
if exceptions.contains(&user.owner) {
cursor.move_next();
continue;
}
user.value = Some(replacement);
}
cursor.remove();
replacement.borrow_mut().insert_use(user);
}
}
}
impl dyn Value {
#[inline]
pub fn is<T: Value>(&self) -> bool {
Value::as_any(self).is::<T>()
}
#[inline]
pub fn downcast_ref<T: Value>(&self) -> Option<&T> {
Value::as_any(self).downcast_ref::<T>()
}
#[inline]
pub fn downcast_mut<T: Value>(&mut self) -> Option<&mut T> {
Value::as_any_mut(self).downcast_mut::<T>()
}
pub fn replace_uses_with_if<F>(&mut self, mut replacement: ValueRef, should_replace: F)
where
F: Fn(&OpOperandImpl) -> bool,
{
let mut cursor = self.uses_mut().front_mut();
while let Some(mut user) = cursor.as_pointer() {
{
let mut user = user.borrow_mut();
if !should_replace(&user) {
cursor.move_next();
continue;
}
user.value = Some(replacement);
}
cursor.remove();
replacement.borrow_mut().insert_use(user);
}
}
}
impl ValueRef {
pub fn try_downcast_value<T: Value>(self) -> Result<UnsafeEntityRef<T>, Self> {
if self.borrow().is::<T>() {
Ok(unsafe { self.cast_unchecked::<T>() })
} else {
Err(self)
}
}
#[track_caller]
pub fn downcast_value<T: Value>(self) -> UnsafeEntityRef<T> {
match self.try_downcast_value::<T>() {
Ok(value) => value,
Err(_) => panic!("invalid cast"),
}
}
}
macro_rules! value_impl {
(
$(#[$outer:meta])*
$vis:vis struct $ValueKind:ident {
$(#[doc $($owner_doc_args:tt)*])*
owner: $OwnerTy:ty,
$(#[doc $($index_doc_args:tt)*])*
index: u8,
$(
$(#[$inner:ident $($args:tt)*])*
$Field:ident: $FieldTy:ty,
)*
}
fn get_defining_op(&$GetDefiningOpSelf:ident) -> Option<OperationRef> $GetDefiningOp:block
fn parent_block(&$ParentBlockSelf:ident) -> Option<BlockRef> $ParentBlock:block
$($t:tt)*
) => {
$(#[$outer])*
#[derive(Spanned)]
$vis struct $ValueKind {
id: ValueId,
#[span]
span: SourceSpan,
ty: Type,
uses: OpOperandList,
owner: $OwnerTy,
index: u8,
$(
$(#[$inner $($args)*])*
$Field: $FieldTy
),*
}
impl $ValueKind {
pub fn new(
span: SourceSpan,
id: ValueId,
ty: Type,
owner: $OwnerTy,
index: u8,
$(
$Field: $FieldTy
),*
) -> Self {
Self {
id,
ty,
span,
uses: Default::default(),
owner,
index,
$(
$Field
),*
}
}
$(#[doc $($owner_doc_args)*])*
pub fn owner(&self) -> $OwnerTy {
self.owner.clone()
}
$(#[doc $($index_doc_args)*])*
pub fn index(&self) -> usize {
self.index as usize
}
}
impl Value for $ValueKind {
#[inline(always)]
fn as_any(&self) -> &dyn Any {
self
}
#[inline(always)]
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn ty(&self) -> &Type {
&self.ty
}
fn set_span(&mut self, span: SourceSpan) {
self.span = span;
}
fn set_type(&mut self, ty: Type) {
self.ty = ty;
}
fn get_defining_op(&$GetDefiningOpSelf) -> Option<OperationRef> $GetDefiningOp
fn parent_block(&$ParentBlockSelf) -> Option<BlockRef> $ParentBlock
}
impl Entity for $ValueKind {}
impl EntityWithId for $ValueKind {
type Id = ValueId;
#[inline(always)]
fn id(&self) -> Self::Id {
self.id
}
}
impl EntityParent<OpOperandImpl> for $ValueKind {
fn offset() -> usize {
core::mem::offset_of!($ValueKind, uses)
}
}
impl Usable for $ValueKind {
type Use = OpOperandImpl;
#[inline(always)]
fn uses(&self) -> &OpOperandList {
&self.uses
}
#[inline(always)]
fn uses_mut(&mut self) -> &mut OpOperandList {
&mut self.uses
}
}
impl Eq for $ValueKind {}
impl PartialEq for $ValueKind {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Ord for $ValueKind {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.id.cmp(&other.id)
}
}
impl PartialOrd for $ValueKind {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl core::hash::Hash for $ValueKind {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
impl fmt::Display for $ValueKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use crate::formatter::PrettyPrint;
self.pretty_print(f)
}
}
impl fmt::Debug for $ValueKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut builder = f.debug_struct(stringify!($ValueKind));
builder
.field("id", &self.id)
.field("ty", &self.ty)
.field("index", &self.index)
.field("is_used", &(!self.uses.is_empty()));
$(
builder.field(stringify!($Field), &self.$Field);
)*
builder.finish_non_exhaustive()
}
}
$($t)*
}
}
pub type ValueRef = UnsafeEntityRef<dyn Value>;
pub type BlockArgumentRef = UnsafeEntityRef<BlockArgument>;
pub type OpResultRef = UnsafeEntityRef<OpResult>;
value_impl!(
pub struct BlockArgument {
owner: BlockRef,
index: u8,
}
fn get_defining_op(&self) -> Option<OperationRef> {
None
}
fn parent_block(&self) -> Option<BlockRef> {
Some(self.owner)
}
);
impl BlockArgument {
#[inline]
pub fn as_value_ref(&self) -> ValueRef {
self.as_block_argument_ref() as ValueRef
}
#[inline]
pub fn as_block_argument_ref(&self) -> BlockArgumentRef {
unsafe { BlockArgumentRef::from_raw(self) }
}
}
impl crate::formatter::PrettyPrint for BlockArgument {
fn render(&self) -> crate::formatter::Document {
use crate::formatter::*;
display(self.id) + const_text(": ") + self.ty.render()
}
}
impl StorableEntity for BlockArgument {
#[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 block arguments");
}
}
pub type BlockArgumentRange<'a> = crate::EntityRange<'a, BlockArgumentRef>;
pub type BlockArgumentRangeMut<'a> = crate::EntityRangeMut<'a, BlockArgumentRef, 1>;
value_impl!(
pub struct OpResult {
owner: OperationRef,
index: u8,
}
fn get_defining_op(&self) -> Option<OperationRef> {
Some(self.owner)
}
fn parent_block(&self) -> Option<BlockRef> {
self.owner.parent()
}
);
impl OpResult {
#[inline]
pub fn as_value_ref(&self) -> ValueRef {
unsafe { ValueRef::from_raw(self as &dyn Value) }
}
#[inline]
pub fn as_op_result_ref(&self) -> OpResultRef {
unsafe { OpResultRef::from_raw(self) }
}
}
impl crate::formatter::PrettyPrint for OpResult {
#[inline]
fn render(&self) -> crate::formatter::Document {
use crate::formatter::*;
display(self.id)
}
}
impl StorableEntity for OpResult {
#[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 op results");
}
fn unlink(&mut self) {
let uses = self.uses_mut();
uses.clear();
}
}
pub type OpResultStorage = crate::EntityStorage<OpResultRef, 1>;
pub type OpResultRange<'a> = crate::EntityRange<'a, OpResultRef>;
pub type OpResultRangeMut<'a> = crate::EntityRangeMut<'a, OpResultRef, 1>;