pub mod error;
pub mod hart2enc;
pub mod state;
pub mod step;
use crate::config::{self, AddressMode, Features};
use crate::packet::{payload, sync, unit};
use crate::types::Privilege;
use error::Error;
use hart2enc::CType;
use payload::InstructionTrace;
#[derive(Clone, Debug)]
pub struct Generator<S, I = unit::ReferenceIOptions, D = unit::ReferenceDOptions>
where
S: step::Step,
I: unit::IOptions,
{
state: state::State,
features: Features,
options: Option<(I, D)>,
current: Option<S>,
previous: Option<(step::Kind, Privilege)>,
reported_exception: bool,
event: Option<Event>,
}
impl<S: step::Step + Clone, I: unit::IOptions + Clone, D: Clone> Generator<S, I, D> {
pub fn begin_qualification(
&mut self,
ioptions: I,
doptions: D,
) -> Result<sync::Support<I, D>, Error> {
ioptions
.update_features(&mut self.features)
.map_err(Error::UnsupportedFeature)?;
if self.features.implicit_returns {
return Err(Error::UnsupportedFeature("implicit return"));
}
if let Some(mode) = ioptions.address_mode() {
self.state.set_address_mode(mode);
}
self.options = Some((ioptions.clone(), doptions.clone()));
Ok(sync::Support {
ienable: true,
encoder_mode: sync::EncoderMode::BranchTrace,
qual_status: sync::QualStatus::NoChange,
ioptions,
denable: false,
dloss: false,
doptions,
})
}
pub fn end_qualification(&mut self, ienable: bool) -> Output<'_, S, I, D> {
self.do_step(OutputKind::Draining { ienable }, None)
}
pub fn process_step(&mut self, step: S, event: Option<Event>) -> Output<'_, S, I, D> {
if let Some(current) = self.current.as_mut() {
current.refine(&step);
}
let kind = OutputKind::Regular {
next_kind: step.kind(),
next_ctype: step.ctype(),
next_priv: step.context().privilege,
next_event: event,
};
self.do_step(kind, Some(step))
}
fn do_step(&mut self, kind: OutputKind, next: Option<S>) -> Output<'_, S, I, D> {
let current = self.current.take();
let event = self.event.take();
let previous = self.previous.take();
self.current = next;
if let OutputKind::Regular { next_event, .. } = &kind {
self.event = *next_event;
}
let state = if let Some(current) = current {
self.previous = Some((current.kind(), current.context().privilege));
Some(OutputState::First {
previous,
current,
event,
})
} else {
self.reported_exception = false;
None
};
Output {
generator: self,
kind,
state,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Event {
ReSync,
Notify,
}
pub fn builder() -> Builder {
Default::default()
}
#[derive(Copy, Clone, Default)]
pub struct Builder {
features: Features,
address_mode: AddressMode,
}
impl Builder {
pub fn with_params(self, config: &config::Parameters) -> Self {
Self {
features: Features {
sequentially_inferred_jumps: config.sijump_p,
..self.features
},
..self
}
}
pub fn with_address_mode(self, mode: AddressMode) -> Self {
Self {
address_mode: mode,
..self
}
}
pub fn with_implicit_return(self, implicit_returns: bool) -> Self {
Self {
features: Features {
implicit_returns,
..self.features
},
..self
}
}
pub fn build<S, I, D>(&self) -> Result<Generator<S, I, D>, Error>
where
S: step::Step,
I: unit::IOptions,
{
let state = state::State::new(self.address_mode);
Ok(Generator {
state,
features: self.features,
options: None,
current: None,
previous: None,
reported_exception: false,
event: None,
})
}
}
#[derive(Debug)]
pub struct Output<'g, S, I = unit::ReferenceIOptions, D = unit::ReferenceDOptions>
where
S: step::Step,
I: unit::IOptions,
{
generator: &'g mut Generator<S, I, D>,
kind: OutputKind,
state: Option<OutputState<S>>,
}
impl<S: step::Step, I: unit::IOptions, D> Output<'_, S, I, D> {
fn do_block_start(
&mut self,
previous: Option<(step::Kind, Privilege)>,
current: &S,
event: Option<Event>,
) -> Result<Option<InstructionTrace<I, D>>, Error> {
let kind = current.kind();
let reported_exception = self.generator.reported_exception;
self.generator.reported_exception = false;
let mut builder = self.generator.state.payload_builder(
current.address(),
current.context(),
current.timestamp(),
);
if let step::Kind::Branch { taken, .. } = kind {
builder.add_branch(taken, current.is_single())?;
}
if let Some((step::Kind::Trap { info, .. }, _)) = previous {
let payload = if kind.is_exc_only() {
builder.report_trap(false, info).into()
} else if reported_exception {
builder.report_sync().into()
} else {
builder.report_trap(true, info).into()
};
return Ok(Some(payload));
}
if event == Some(Event::ReSync)
|| matches!(current.ctype(), CType::Precisely | CType::AsyncDiscon)
|| previous.map(|(_, p)| p) != Some(current.context().privilege)
{
return Ok(Some(builder.report_sync().into()));
}
let sijumps = self.generator.features.sequentially_inferred_jumps;
if previous.map(|(k, _)| k.is_updiscon(sijumps)) == Some(true) {
return if let step::Kind::Trap {
insn_size: None,
info,
} = kind
{
self.generator.reported_exception = true;
Ok(builder.report_trap(false, info).into())
} else {
let reason = if self
.kind
.is_updiscon_cause(current.context().privilege)
.unwrap_or(self.generator.options.is_some())
{
state::Reason::Updiscon
} else {
state::Reason::Other
};
builder.report_address(reason)
}
.map(Some);
}
Ok(None)
}
fn do_block_end(
&mut self,
current: &S,
event: Option<Event>,
) -> Result<Option<InstructionTrace<I, D>>, Error> {
let mut builder = self.generator.state.payload_builder(
current.last_address(),
current.context(),
current.timestamp(),
);
if event == Some(Event::Notify) {
return builder.report_address(state::Reason::Notify).map(Some);
}
if let step::Kind::Trap { insn_size, .. } = current.kind() {
return if insn_size.is_some() {
builder.report_address(state::Reason::Other).map(Some)
} else {
Ok(None)
};
}
let OutputKind::Regular {
next_kind,
next_ctype,
next_priv,
next_event,
} = self.kind
else {
return Ok(None);
};
let have_branches = builder.branches() != 0;
if next_event == Some(Event::ReSync) && have_branches {
return builder.report_address(state::Reason::Other).map(Some);
}
let ppccd = next_priv != current.context().privilege
|| matches!(next_ctype, CType::Precisely | CType::AsyncDiscon);
if next_kind.is_exc_only() || (ppccd && have_branches) {
return builder.report_address(state::Reason::Other).map(Some);
}
if let Some(branches) = builder.report_full_branchmap() {
return Ok(Some(branches.into()));
}
match current.ctype() {
CType::Imprecisely => Ok(Some(builder.context().into())),
_ => Ok(None),
}
}
}
impl<S: step::Step, I: unit::IOptions + Clone, D: Clone> Iterator for Output<'_, S, I, D> {
type Item = Result<InstructionTrace<I, D>, Error>;
fn next(&mut self) -> Option<Self::Item> {
match self.state.take()? {
OutputState::First {
previous,
current,
event,
} => {
let res = self.do_block_start(previous, ¤t, event).transpose();
if res.is_none() {
self.state = Some(OutputState::Last { current, event });
return self.next();
}
if !current.is_single() {
self.state = Some(OutputState::Last { current, event });
} else if let OutputKind::Draining { ienable } = &self.kind {
self.state = Some(OutputState::End {
ienable: *ienable,
qual_status: sync::QualStatus::EndedNtr,
});
}
res
}
OutputState::Last { current, event } => {
let res = self.do_block_end(¤t, event).transpose();
let OutputKind::Draining { ienable } = &self.kind else {
return res;
};
let qual_status = match res.is_some() {
true => sync::QualStatus::EndedNtr,
false => sync::QualStatus::EndedRep,
};
self.state = Some(OutputState::End {
ienable: *ienable,
qual_status,
});
res.or_else(|| {
let res = self
.generator
.state
.payload_builder(
current.last_address(),
current.context(),
current.timestamp(),
)
.report_address(state::Reason::Other);
Some(res)
})
}
OutputState::End {
ienable,
qual_status,
} => {
let (ioptions, doptions) = self.generator.options.clone()?;
Some(Ok(sync::Support {
ienable,
encoder_mode: sync::EncoderMode::BranchTrace,
qual_status,
ioptions,
denable: false,
dloss: false,
doptions,
}
.into()))
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self.state {
Some(OutputState::First { .. }) => (1, Some(3)),
Some(OutputState::Last { .. }) => (1, Some(2)),
Some(OutputState::End { .. }) => (1, Some(1)),
None => (0, Some(0)),
}
}
}
#[derive(Copy, Clone, Debug)]
enum OutputState<S> {
First {
previous: Option<(step::Kind, Privilege)>,
current: S,
event: Option<Event>,
},
Last { current: S, event: Option<Event> },
End {
ienable: bool,
qual_status: sync::QualStatus,
},
}
#[derive(Copy, Clone, Debug)]
enum OutputKind {
Regular {
next_kind: step::Kind,
next_ctype: CType,
next_priv: Privilege,
next_event: Option<Event>,
},
Draining { ienable: bool },
}
impl OutputKind {
pub fn is_updiscon_cause(&self, privilege: Privilege) -> Option<bool> {
let Self::Regular {
next_kind,
next_ctype,
next_priv,
next_event,
} = self
else {
return None;
};
let res = *next_event == Some(Event::ReSync)
|| matches!(next_kind, step::Kind::Trap { .. })
|| !matches!(next_ctype, CType::Unreported)
|| privilege != *next_priv;
Some(res)
}
}