use std::{
marker, mem,
ops::{Add, AddAssign, Sub},
};
use derive_more::Display;
use gazebo::dupe::Dupe;
use crate::eval::bc::{
if_debug::IfDebug,
instr::BcInstr,
opcode::BcOpcode,
repr::{BcInstrHeader, BcInstrRepr},
};
#[derive(
Eq, PartialEq, Copy, Clone, Dupe, Debug, Ord, PartialOrd, Display, Hash
)]
#[display(fmt = "@{}", _0)]
pub(crate) struct BcAddr(pub(crate) u32);
impl BcAddr {
pub(crate) fn offset_from(self, start: BcAddr) -> BcAddrOffset {
debug_assert!(self >= start);
BcAddrOffset(self.0 - start.0)
}
pub(crate) fn offset(self, offset: BcAddrOffset) -> BcAddr {
BcAddr(self.0 + offset.0)
}
}
impl Sub<u32> for BcAddr {
type Output = BcAddr;
fn sub(self, rhs: u32) -> BcAddr {
BcAddr(self.0.checked_sub(rhs).unwrap())
}
}
impl Add<u32> for BcAddr {
type Output = BcAddr;
fn add(self, rhs: u32) -> BcAddr {
BcAddr(self.0 + rhs)
}
}
impl AddAssign<u32> for BcAddr {
fn add_assign(&mut self, rhs: u32) {
self.0 += rhs;
}
}
#[derive(Copy, Clone, Dupe, Debug, PartialEq)]
pub(crate) struct BcPtrRange {
start: *const u8,
len: usize,
}
impl BcPtrRange {
pub(crate) fn for_slice(slice: &[usize]) -> BcPtrRange {
BcPtrRange {
start: slice.as_ptr() as *const u8,
len: slice.len() * mem::size_of::<usize>(),
}
}
pub(crate) fn assert_in_range(&self, ptr: *const u8) {
let offset = unsafe { ptr.offset_from(self.start) };
assert!(offset >= 0);
assert!(offset as usize <= self.len);
}
fn end(&self) -> *const u8 {
unsafe { self.start.add(self.len) }
}
}
#[derive(Copy, Clone, Dupe, PartialOrd, PartialEq, Debug)]
pub(crate) struct BcPtrAddr<'b> {
ptr: *const u8,
range: IfDebug<BcPtrRange>,
_marker: marker::PhantomData<&'b u8>,
}
impl<'b> BcPtrAddr<'b> {
unsafe fn new(ptr: *const u8, range: IfDebug<BcPtrRange>) -> BcPtrAddr<'b> {
range.if_debug(|range| range.assert_in_range(ptr));
BcPtrAddr {
ptr,
range,
_marker: marker::PhantomData,
}
}
pub(crate) fn for_slice_start(slice: &'b [usize]) -> BcPtrAddr<'b> {
unsafe {
BcPtrAddr::new(
slice.as_ptr() as *const u8,
IfDebug::new(BcPtrRange::for_slice(slice)),
)
}
}
pub(crate) fn for_slice_end(slice: &'b [usize]) -> BcPtrAddr<'b> {
unsafe {
BcPtrAddr::new(
slice.as_ptr().add(slice.len()) as *const u8,
IfDebug::new(BcPtrRange::for_slice(slice)),
)
}
}
fn remaining_if_debug(self) -> usize {
unsafe { self.range.get_ref_if_debug().end().offset_from(self.ptr) as usize }
}
pub(crate) fn get_instr<I: BcInstr>(self) -> &'b BcInstrRepr<I> {
debug_assert!(self.remaining_if_debug() >= mem::size_of::<BcInstrRepr<I>>());
let ptr = self.ptr as *const BcInstrRepr<I>;
let repr = unsafe { &*ptr };
debug_assert_eq!(repr.header.opcode, BcOpcode::for_instr::<I>());
repr
}
pub(crate) fn get_instr_mut<I: BcInstr>(self) -> *mut BcInstrRepr<I> {
debug_assert!(self.remaining_if_debug() >= mem::size_of::<BcInstrRepr<I>>());
self.ptr as *mut BcInstrRepr<I>
}
pub(crate) fn get_opcode(self) -> BcOpcode {
debug_assert!(self.remaining_if_debug() >= mem::size_of::<BcInstrHeader>());
let ptr = self.ptr as *const BcInstrHeader;
unsafe { (*ptr).opcode }
}
pub(crate) fn offset_from(self, start: BcPtrAddr) -> BcAddr {
debug_assert!(self.range.get_ref_if_debug() == start.range.get_ref_if_debug());
unsafe {
let offset = self.ptr.offset_from(start.ptr);
debug_assert!(offset >= 0);
debug_assert!(offset <= i32::MAX as isize);
BcAddr(offset as u32)
}
}
pub(crate) fn sub(self, start: BcAddr) -> BcPtrAddr<'b> {
unsafe { BcPtrAddr::new(self.ptr.sub(start.0 as usize), self.range) }
}
pub(crate) fn offset(self, addr: BcAddr) -> BcPtrAddr<'b> {
self.add(addr.0 as usize)
}
pub(crate) fn add_rel(self, rel: BcAddrOffset) -> BcPtrAddr<'b> {
self.add(rel.0 as usize)
}
pub(crate) fn add(self, offset: usize) -> BcPtrAddr<'b> {
unsafe { BcPtrAddr::new(self.ptr.add(offset), self.range) }
}
pub(crate) fn add_instr<I: BcInstr>(self) -> BcPtrAddr<'b> {
self.add_rel(BcAddrOffset::for_instr::<I>())
}
}
#[derive(Eq, PartialEq, Copy, Clone, Dupe, Debug, Ord, PartialOrd, Display)]
#[display(fmt = "{}", _0)]
pub(crate) struct BcAddrOffset(pub(crate) u32);
impl BcAddrOffset {
pub(crate) const FORWARD: BcAddrOffset = BcAddrOffset(0xdeadbeef);
fn for_instr<I: BcInstr>() -> BcAddrOffset {
<BcInstrRepr<I>>::assert_align();
BcAddrOffset(mem::size_of::<BcInstrRepr<I>>() as u32)
}
}