#[cfg(feature = "alloc")]
use alloc::boxed::Box;
use core::fmt;
use crate::config;
use super::decoder::{Decode, Decoder};
use super::encoder::{Encode, Encoder};
use super::error::Error;
use config::AddressMode;
pub trait Unit<U = Self> {
type IOptions: IOptions + 'static;
type DOptions: DOptions + 'static;
fn encoder_mode_width(&self) -> u8;
fn decode_ioptions(decoder: &mut Decoder<U>) -> Result<Self::IOptions, Error>;
fn decode_doptions(decoder: &mut Decoder<U>) -> Result<Self::DOptions, Error>;
#[cfg(feature = "alloc")]
fn as_plug(&self) -> Plug
where
Self: Unit<Plug> + Sized,
<Self as Unit<Plug>>::IOptions: fmt::Debug,
<Self as Unit<Plug>>::DOptions: fmt::Debug,
{
Plug::new(self)
}
}
pub trait IOptions: Send + Sync {
fn address_mode(&self) -> Option<AddressMode> {
None
}
fn sequentially_inferred_jumps(&self) -> Option<bool> {
None
}
fn implicit_return(&self) -> Option<bool> {
None
}
fn implicit_exception(&self) -> Option<bool> {
None
}
fn branch_prediction(&self) -> Option<bool> {
None
}
fn jump_target_cache(&self) -> Option<bool> {
None
}
fn update_features(&self, features: &mut config::Features) -> Result<(), &'static str> {
if self.implicit_exception() == Some(true) {
return Err("implicit exceptions");
}
if self.branch_prediction() == Some(true) {
return Err("branch prediction");
}
if self.jump_target_cache() == Some(true) {
return Err("jump target cache");
}
if let Some(jumps) = self.sequentially_inferred_jumps() {
features.sequentially_inferred_jumps = jumps;
}
if let Some(returns) = self.implicit_return() {
features.implicit_returns = returns;
}
Ok(())
}
}
#[cfg(feature = "alloc")]
impl<T: IOptions + ?Sized> IOptions for Box<T> {
fn address_mode(&self) -> Option<AddressMode> {
T::address_mode(self.as_ref())
}
fn sequentially_inferred_jumps(&self) -> Option<bool> {
T::sequentially_inferred_jumps(self.as_ref())
}
fn implicit_return(&self) -> Option<bool> {
T::implicit_return(self.as_ref())
}
fn implicit_exception(&self) -> Option<bool> {
T::implicit_exception(self.as_ref())
}
fn branch_prediction(&self) -> Option<bool> {
T::branch_prediction(self.as_ref())
}
fn jump_target_cache(&self) -> Option<bool> {
T::jump_target_cache(self.as_ref())
}
}
#[cfg(feature = "either")]
impl<L: IOptions, R: IOptions> IOptions for either::Either<L, R> {
fn address_mode(&self) -> Option<AddressMode> {
either::for_both!(self, o => o.address_mode())
}
fn sequentially_inferred_jumps(&self) -> Option<bool> {
either::for_both!(self, o => o.sequentially_inferred_jumps())
}
fn implicit_return(&self) -> Option<bool> {
either::for_both!(self, o => o.implicit_return())
}
fn implicit_exception(&self) -> Option<bool> {
either::for_both!(self, o => o.implicit_exception())
}
fn branch_prediction(&self) -> Option<bool> {
either::for_both!(self, o => o.branch_prediction())
}
fn jump_target_cache(&self) -> Option<bool> {
either::for_both!(self, o => o.jump_target_cache())
}
}
pub trait DebugIOptions: IOptions + fmt::Debug {}
impl<T: IOptions + fmt::Debug> DebugIOptions for T {}
pub trait DOptions: Send + Sync {}
#[cfg(feature = "alloc")]
impl<T: DOptions + ?Sized> DOptions for Box<T> {}
#[cfg(feature = "either")]
impl<L: DOptions, R: DOptions> DOptions for either::Either<L, R> {}
pub trait DebugDOptions: DOptions + fmt::Debug {}
impl<T: DOptions + fmt::Debug> DebugDOptions for T {}
#[derive(Copy, Clone, Debug, Default, PartialEq)]
pub struct Reference;
impl<U> Unit<U> for Reference {
type IOptions = ReferenceIOptions;
type DOptions = ReferenceDOptions;
fn encoder_mode_width(&self) -> u8 {
1
}
fn decode_ioptions(decoder: &mut Decoder<U>) -> Result<Self::IOptions, Error> {
Decode::decode(decoder)
}
fn decode_doptions(decoder: &mut Decoder<U>) -> Result<Self::DOptions, Error> {
Decode::decode(decoder)
}
}
#[derive(Copy, Clone, Default, Debug, PartialEq)]
pub struct ReferenceIOptions {
pub implicit_return: bool,
pub implicit_exception: bool,
pub full_address: bool,
pub jump_target_cache: bool,
pub branch_prediction: bool,
}
impl<U> Decode<'_, U> for ReferenceIOptions {
fn decode(decoder: &mut Decoder<U>) -> Result<Self, Error> {
let implicit_return = decoder.read_bit()?;
let implicit_exception = decoder.read_bit()?;
let full_address = decoder.read_bit()?;
let jump_target_cache = decoder.read_bit()?;
let branch_prediction = decoder.read_bit()?;
Ok(Self {
implicit_return,
implicit_exception,
full_address,
jump_target_cache,
branch_prediction,
})
}
}
impl<U> Encode<'_, U> for ReferenceIOptions {
fn encode(&self, encoder: &mut Encoder<U>) -> Result<(), Error> {
encoder.write_bit(self.implicit_return)?;
encoder.write_bit(self.implicit_exception)?;
encoder.write_bit(self.full_address)?;
encoder.write_bit(self.jump_target_cache)?;
encoder.write_bit(self.branch_prediction)
}
}
impl IOptions for ReferenceIOptions {
fn address_mode(&self) -> Option<AddressMode> {
Some(AddressMode::from_full(self.full_address))
}
fn implicit_return(&self) -> Option<bool> {
Some(self.implicit_return)
}
fn implicit_exception(&self) -> Option<bool> {
Some(self.implicit_exception)
}
fn branch_prediction(&self) -> Option<bool> {
Some(self.branch_prediction)
}
fn jump_target_cache(&self) -> Option<bool> {
Some(self.jump_target_cache)
}
}
#[derive(Copy, Clone, Default, Debug, PartialEq)]
pub struct ReferenceDOptions {
pub no_address: bool,
pub no_data: bool,
pub full_address: bool,
pub full_data: bool,
}
impl<U> Decode<'_, U> for ReferenceDOptions {
fn decode(decoder: &mut Decoder<U>) -> Result<Self, Error> {
let no_address = decoder.read_bit()?;
let no_data = decoder.read_bit()?;
let full_address = decoder.read_bit()?;
let full_data = decoder.read_bit()?;
Ok(Self {
no_address,
no_data,
full_address,
full_data,
})
}
}
impl<U> Encode<'_, U> for ReferenceDOptions {
fn encode(&self, encoder: &mut Encoder<U>) -> Result<(), Error> {
encoder.write_bit(self.no_address)?;
encoder.write_bit(self.no_data)?;
encoder.write_bit(self.full_address)?;
encoder.write_bit(self.full_data)
}
}
impl DOptions for ReferenceDOptions {}
#[derive(Copy, Clone, Debug, Default, PartialEq)]
pub struct PULP;
impl<U> Unit<U> for PULP {
type IOptions = PULPIOptions;
type DOptions = NoOptions;
fn encoder_mode_width(&self) -> u8 {
1
}
fn decode_ioptions(decoder: &mut Decoder<U>) -> Result<Self::IOptions, Error> {
Decode::decode(decoder)
}
fn decode_doptions(decoder: &mut Decoder<U>) -> Result<Self::DOptions, Error> {
Decode::decode(decoder)
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct PULPIOptions {
pub delta_address: bool,
pub full_address: bool,
pub implicit_exception: bool,
pub sijump: bool,
pub implicit_return: bool,
pub branch_prediction: bool,
pub jump_target_cache: bool,
}
impl<U> Decode<'_, U> for PULPIOptions {
fn decode(decoder: &mut Decoder<U>) -> Result<Self, Error> {
let jump_target_cache = decoder.read_bit()?;
let branch_prediction = decoder.read_bit()?;
let implicit_return = decoder.read_bit()?;
let sijump = decoder.read_bit()?;
let implicit_exception = decoder.read_bit()?;
let full_address = decoder.read_bit()?;
let delta_address = decoder.read_bit()?;
Ok(Self {
delta_address,
full_address,
implicit_exception,
sijump,
implicit_return,
branch_prediction,
jump_target_cache,
})
}
}
impl<U> Encode<'_, U> for PULPIOptions {
fn encode(&self, encoder: &mut Encoder<U>) -> Result<(), Error> {
encoder.write_bit(self.jump_target_cache)?;
encoder.write_bit(self.branch_prediction)?;
encoder.write_bit(self.implicit_return)?;
encoder.write_bit(self.sijump)?;
encoder.write_bit(self.implicit_exception)?;
encoder.write_bit(self.full_address)?;
encoder.write_bit(self.delta_address)
}
}
impl IOptions for PULPIOptions {
fn address_mode(&self) -> Option<AddressMode> {
match (self.delta_address, self.full_address) {
(true, false) => Some(AddressMode::Delta),
(false, true) => Some(AddressMode::Full),
_ => None,
}
}
fn sequentially_inferred_jumps(&self) -> Option<bool> {
Some(self.sijump)
}
fn implicit_return(&self) -> Option<bool> {
Some(self.implicit_return)
}
fn implicit_exception(&self) -> Option<bool> {
Some(self.implicit_exception)
}
fn branch_prediction(&self) -> Option<bool> {
Some(self.branch_prediction)
}
fn jump_target_cache(&self) -> Option<bool> {
Some(self.jump_target_cache)
}
}
#[cfg(feature = "alloc")]
#[allow(clippy::type_complexity)]
#[derive(Copy, Clone, Debug)]
pub struct Plug {
encoder_mode_width: u8,
decode_ioptions: fn(&mut Decoder<Self>) -> Result<Box<dyn DebugIOptions>, Error>,
decode_doptions: fn(&mut Decoder<Self>) -> Result<Box<dyn DebugDOptions>, Error>,
}
#[cfg(feature = "alloc")]
impl Plug {
pub fn new<U>(inner: &U) -> Self
where
U: Unit<Self>,
U::IOptions: fmt::Debug,
U::DOptions: fmt::Debug,
{
fn decode_ioptions<U>(decoder: &mut Decoder<Plug>) -> Result<Box<dyn DebugIOptions>, Error>
where
U: Unit<Plug>,
U::IOptions: fmt::Debug,
{
U::decode_ioptions(decoder).map(|r| -> Box<dyn DebugIOptions> { Box::new(r) })
}
fn decode_doptions<U>(decoder: &mut Decoder<Plug>) -> Result<Box<dyn DebugDOptions>, Error>
where
U: Unit<Plug>,
U::DOptions: fmt::Debug,
{
U::decode_doptions(decoder).map(|r| -> Box<dyn DebugDOptions> { Box::new(r) })
}
Self {
encoder_mode_width: inner.encoder_mode_width(),
decode_ioptions: decode_ioptions::<U>,
decode_doptions: decode_doptions::<U>,
}
}
}
#[cfg(feature = "alloc")]
impl Default for Plug {
fn default() -> Self {
Self::new(&Reference)
}
}
#[cfg(feature = "alloc")]
impl Unit for Plug {
type IOptions = Box<dyn DebugIOptions>;
type DOptions = Box<dyn DebugDOptions>;
fn encoder_mode_width(&self) -> u8 {
self.encoder_mode_width
}
fn decode_ioptions(decoder: &mut Decoder<Self>) -> Result<Self::IOptions, Error> {
(decoder.unit().decode_ioptions)(decoder)
}
fn decode_doptions(decoder: &mut Decoder<Self>) -> Result<Self::DOptions, Error> {
(decoder.unit().decode_doptions)(decoder)
}
}
#[cfg(feature = "alloc")]
pub const PLUGS: &[PlugsEntry<'static>] = &[
PlugsEntry::new(
"reference",
"Reference flow's original encoder model",
|| Plug::new(&Reference),
),
PlugsEntry::new("pulp", "PULP plattform's rv_tracer", || Plug::new(&PULP)),
];
#[cfg(feature = "alloc")]
#[derive(Copy, Clone, Debug)]
pub struct PlugsEntry<'a> {
name: &'a str,
description: &'a str,
ctor: fn() -> Plug,
}
#[cfg(feature = "alloc")]
impl<'a> PlugsEntry<'a> {
pub const fn new(name: &'a str, description: &'a str, ctor: fn() -> Plug) -> Self {
Self {
name,
description,
ctor,
}
}
pub fn name(&self) -> &'a str {
self.name
}
pub fn description(&self) -> &'a str {
self.description
}
pub fn plug(&self) -> Plug {
(self.ctor)()
}
}
#[cfg(feature = "alloc")]
impl Default for PlugsEntry<'_> {
fn default() -> Self {
PLUGS[0]
}
}
#[derive(Copy, Clone, Debug, Default)]
pub struct NoOptions;
impl<U> Decode<'_, U> for NoOptions {
fn decode(_decoder: &mut Decoder<U>) -> Result<Self, Error> {
Ok(Self)
}
}
impl<U> Encode<'_, U> for NoOptions {
fn encode(&self, _encoder: &mut Encoder<U>) -> Result<(), Error> {
Ok(())
}
}
impl IOptions for NoOptions {}
impl DOptions for NoOptions {}