use alloc::{collections::VecDeque, sync::Arc};
use core::fmt;
use crate::{
Alignable, OpPrinter, UnsafeIntrusiveEntityRef,
constants::ConstantData,
derive::{OpParser, OpPrinter, operation},
diagnostics::{Diagnostic, miette},
dialects::builtin::{
BuiltinDialect,
attributes::{BoolAttr, BytesAttr, U32Attr},
},
traits::*,
};
pub type SegmentRef = UnsafeIntrusiveEntityRef<Segment>;
#[derive(OpPrinter, OpParser)]
#[operation(
dialect = BuiltinDialect,
traits(
SingleBlock,
NoRegionArguments,
IsolatedFromAbove,
),
implements(OpPrinter)
)]
pub struct Segment {
#[attr]
offset: U32Attr,
#[attr]
data: BytesAttr,
#[attr]
#[default]
readonly: BoolAttr,
}
impl Segment {
pub fn size_in_bytes(&self) -> usize {
self.get_data().len()
}
pub fn initializer(&self) -> Arc<ConstantData> {
self.get_data().clone()
}
}
impl fmt::Debug for Segment {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let data = self.initializer();
f.debug_struct("Segment")
.field("offset", &self.get_offset())
.field("size", &data.len())
.field("init", &format_args!("{data}"))
.field("readonly", &self.get_readonly())
.finish()
}
}
#[derive(Debug, thiserror::Error, Diagnostic)]
pub enum DataSegmentError {
#[error(
"invalid data segment: segment of {size1} bytes at {offset1:#x} overlaps with segment of \
{size2} bytes at {offset2:#x}"
)]
#[diagnostic()]
OverlappingSegments {
offset1: u32,
size1: u32,
offset2: u32,
size2: u32,
},
#[error(
"invalid data segment: segment at {0:#x} conflicts with a previous segment declaration at \
this address"
)]
#[diagnostic()]
Mismatch(u32),
#[error(
"invalid data segment: segment of {size} bytes at {offset:#x} would extend beyond the end \
of the usable heap"
)]
#[diagnostic()]
OutOfBounds { offset: u32, size: u32 },
#[error(
"invalid data segment: segment at {0:#x} was declared with an initializer larger than \
2^32 bytes"
)]
#[diagnostic()]
InitTooLarge(u32),
#[error(
"invalid data segment: segment of {size} bytes at {offset:#x} has an initializer of \
{actual} bytes"
)]
#[diagnostic()]
InitOutOfBounds { offset: u32, size: u32, actual: u32 },
}
#[derive(Default, Clone)]
pub struct DataSegmentLayout {
segments: VecDeque<SegmentRef>,
}
impl DataSegmentLayout {
pub fn is_empty(&self) -> bool {
self.segments.is_empty()
}
pub fn len(&self) -> usize {
self.segments.len()
}
pub fn next_available_offset(&self) -> u32 {
if let Some(last_segment) = self.segments.back() {
let last_segment = last_segment.borrow();
let next_offset = *last_segment.get_offset() + last_segment.size_in_bytes() as u32;
next_offset.align_up(32)
} else {
0
}
}
pub fn insert(&mut self, segment_ref: SegmentRef) -> Result<(), DataSegmentError> {
if self.is_empty() {
self.segments.push_back(segment_ref);
return Ok(());
}
let segment = segment_ref.borrow();
let offset = *segment.get_offset();
let size = u32::try_from(segment.size_in_bytes())
.map_err(|_| DataSegmentError::InitTooLarge(offset))?;
let end = offset + size;
for (index, current_segment_ref) in self.segments.iter().enumerate() {
let current_segment = current_segment_ref.borrow();
let current_offset = *current_segment.get_offset();
let current_size = current_segment.size_in_bytes() as u32;
let segment_end = current_offset + current_size;
if current_offset >= end {
self.segments.insert(index, segment_ref);
return Ok(());
}
if current_offset == offset {
if current_size == size
&& current_segment.initializer() == segment.initializer()
&& current_segment.readonly() == segment.readonly()
{
return Ok(());
}
return Err(DataSegmentError::Mismatch(offset));
}
if segment_end > offset {
return Err(DataSegmentError::OverlappingSegments {
offset1: offset,
size1: size,
offset2: current_offset,
size2: current_size,
});
}
}
self.segments.push_back(segment_ref);
Ok(())
}
pub fn iter(&self) -> impl Iterator<Item = SegmentRef> + '_ {
self.segments.iter().copied()
}
#[inline]
pub fn pop_front(&mut self) -> Option<SegmentRef> {
self.segments.pop_front()
}
}
impl fmt::Debug for DataSegmentLayout {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut builder = f.debug_list();
for segment in self.segments.iter() {
let segment = segment.borrow();
builder.entry(&segment);
}
builder.finish()
}
}