fayalite/crates/fayalite/src/sim/interpreter.rs
Jacob Lifshay d4d9706798
reimplement fayalite::formal and add support to the simulator
Add support to the simulator for running hdl asserts/assumes and being
able to write to the formal global clock/reset and all any/all_const/seq that are used.
This allows you to use the exact same HDL code for running a simulation and for running a formal proof.
2026-06-05 00:56:24 -07:00

2135 lines
68 KiB
Rust

// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
expr::ValueType,
int::{BoolOrIntType, SInt, UInt},
intern::{Intern, Interned, Memoize},
sim::interpreter::parts::{
StateLayout, StatePartIndex, StatePartIndexRange, StatePartKind, StatePartKindBigSlots,
StatePartKindMemories, StatePartKindSimOnlySlots, StatePartKindSmallSlots, StatePartLen,
TypeIndexRange, TypeLayout, get_state_part_kinds,
},
source_location::SourceLocation,
util::{HashMap, HashSet},
};
use bitvec::slice::BitSlice;
use num_bigint::BigInt;
use num_traits::{One, Signed, ToPrimitive, Zero};
use std::{
convert::Infallible,
fmt::{self, Write},
hash::Hash,
marker::PhantomData,
ops::{ControlFlow, Deref, Index, IndexMut},
};
use vec_map::VecMap;
pub(crate) mod parts;
pub(crate) type SmallUInt = u64;
pub(crate) type SmallSInt = i64;
pub(crate) const MIN_BITS_FOR_NEEDING_BIG: usize = SmallUInt::BITS as usize + 1;
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub(crate) enum InsnFieldKind {
Input,
Output,
Memory,
Immediate,
BranchTarget,
}
pub(crate) trait InsnFieldTypeTransform: Eq + Hash + fmt::Debug + Send + Sync {
type Type<FieldType: InsnFieldTrait>: Eq + Hash + fmt::Debug + Send + Sync;
fn empty_type() -> Self::Type<[(); 0]>;
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
pub(crate) struct InsnFieldTypeTransformUnit;
impl InsnFieldTypeTransform for InsnFieldTypeTransformUnit {
type Type<FieldType: InsnFieldTrait> = ();
fn empty_type() -> Self::Type<[(); 0]> {
()
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub(crate) struct InsnFieldTypeTransformRef<'a>(PhantomData<&'a ()>);
impl<'a> InsnFieldTypeTransform for InsnFieldTypeTransformRef<'a> {
type Type<FieldType: InsnFieldTrait> = &'a FieldType;
fn empty_type() -> Self::Type<[(); 0]> {
&[]
}
}
#[derive(PartialEq, Eq, Hash, Debug)]
pub(crate) struct InsnFieldTypeTransformRefMut<'a>(PhantomData<&'a mut ()>);
impl<'a> InsnFieldTypeTransform for InsnFieldTypeTransformRefMut<'a> {
type Type<FieldType: InsnFieldTrait> = &'a mut FieldType;
fn empty_type() -> Self::Type<[(); 0]> {
&mut []
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub(crate) struct InsnFieldTypeTransformValue;
impl InsnFieldTypeTransform for InsnFieldTypeTransformValue {
type Type<FieldType: InsnFieldTrait> = FieldType;
fn empty_type() -> Self::Type<[(); 0]> {
[]
}
}
pub trait InsnFieldTrait: Send + Sync + 'static + Copy + Eq + Hash + fmt::Debug {
const UNIT: InsnFieldType<InsnFieldTypeTransformUnit>;
fn variant<Transform: InsnFieldTypeTransform>(
v: Transform::Type<Self>,
) -> InsnFieldType<Transform>;
}
macro_rules! insn_field_enum {
(
$enum_vis:vis enum $InsnFieldType:ident<$Transform:ident: $InsnFieldTypeTransform:ident> {
$($Variant:ident($Transform2:ident::$Type:ident<$variant_ty:ty>),)*
}
) => {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
$enum_vis enum $InsnFieldType<$Transform: $InsnFieldTypeTransform> {
$($Variant($Transform2::$Type<$variant_ty>),)*
}
$(impl InsnFieldTrait for $variant_ty {
const UNIT: $InsnFieldType<InsnFieldTypeTransformUnit> = $InsnFieldType::$Variant(());
fn variant<$Transform2: $InsnFieldTypeTransform>(
v: $Transform2::$Type<Self>,
) -> $InsnFieldType<$Transform2> {
$InsnFieldType::$Variant(v)
}
})*
};
}
macro_rules! insn_field_enum2 {
(
type_singular_variants = [$($type_singular_variant:ident,)*];
array_indexed_variants = [$($array_indexed_variant:ident,)*];
type_kinds = [$($type_kind:ident,)*];
) => {
insn_field_enum! {
pub(crate) enum InsnFieldType<Transform: InsnFieldTypeTransform> {
Memory(Transform::Type<StatePartIndex<StatePartKindMemories>>),
$($type_singular_variant(Transform::Type<StatePartIndex<$type_kind>>),)*
$($array_indexed_variant(Transform::Type<StatePartArrayIndexed<$type_kind>>),)*
SmallUInt(Transform::Type<SmallUInt>),
SmallSInt(Transform::Type<SmallSInt>),
InternedBigInt(Transform::Type<Interned<BigInt>>),
U8(Transform::Type<u8>),
USize(Transform::Type<usize>),
Empty(Transform::Type<[(); 0]>),
}
}
};
}
get_state_part_kinds! {
insn_field_enum2! {
type_singular_variants;
array_indexed_variants;
type_kinds;
}
}
impl<Transform: InsnFieldTypeTransform> InsnFieldType<Transform> {
pub(crate) fn empty() -> Self {
Self::Empty(Transform::empty_type())
}
}
#[derive(PartialEq, Eq, Hash, Debug)]
pub(crate) struct InsnField<Transform: InsnFieldTypeTransform> {
pub(crate) ty: InsnFieldType<Transform>,
pub(crate) kind: InsnFieldKind,
}
impl<Transform: InsnFieldTypeTransform> Clone for InsnField<Transform>
where
InsnFieldType<Transform>: Clone,
{
fn clone(&self) -> Self {
Self {
ty: self.ty.clone(),
kind: self.kind.clone(),
}
}
}
impl<Transform: InsnFieldTypeTransform> Copy for InsnField<Transform> where
InsnFieldType<Transform>: Copy
{
}
fn make_array_into_iter<T, const I: usize, const N: usize>(
input: [T; I],
mut default: impl FnMut() -> T,
) -> std::array::IntoIter<T, N> {
const {
assert!(I <= N);
};
let mut input = input.into_iter();
let array = std::array::from_fn(|_| input.next().unwrap_or_else(&mut default));
let mut retval = array.into_iter();
// remove unneeded trailing elements
if I < N {
retval.nth_back(N - I - 1);
}
retval
}
impl fmt::Debug for Insn {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.debug_fmt::<InsnsBuildingDone>(f, None, None, None)
}
}
pub(crate) struct PrefixLinesWrapper<'a, W> {
writer: W,
at_beginning_of_line: bool,
blank_line_prefix: &'a str,
line_prefix: &'a str,
}
impl<'a, W> PrefixLinesWrapper<'a, W> {
pub(crate) fn new(writer: W, at_beginning_of_line: bool, line_prefix: &'a str) -> Self {
Self {
writer,
at_beginning_of_line,
blank_line_prefix: line_prefix.trim_end(),
line_prefix,
}
}
pub(crate) fn into_inner(self) -> W {
self.writer
}
}
impl<T: fmt::Write> fmt::Write for PrefixLinesWrapper<'_, T> {
fn write_str(&mut self, input: &str) -> fmt::Result {
for part in input.split_inclusive('\n') {
if part.is_empty() {
continue;
}
if self.at_beginning_of_line {
let prefix = match part {
"\n" => self.blank_line_prefix,
_ => self.line_prefix,
};
if !prefix.is_empty() {
self.writer.write_str(prefix)?;
}
self.at_beginning_of_line = false;
}
self.writer.write_str(part)?;
self.at_beginning_of_line = part.ends_with('\n');
}
Ok(())
}
}
impl Insn {
fn debug_fmt<BK: InsnsBuildingKind>(
&self,
f: &mut fmt::Formatter<'_>,
labels: Option<&Labels>,
state_layout: Option<&StateLayout<BK>>,
state: Option<&State>,
) -> fmt::Result {
let (insn_name, fields) = self.fields_with_names();
write!(f, "{insn_name}")?;
if fields.len() == 0 {
return Ok(());
}
let mut f = PrefixLinesWrapper::new(f, false, " ");
writeln!(f, " {{")?;
for (field_name, field) in fields {
write!(f, "{field_name}: ")?;
match field.kind {
InsnFieldKind::BranchTarget => match field.ty {
InsnFieldType::USize(&label_index) => {
if let Some(labels) = labels {
write!(f, "L{label_index}")?;
if let Some(label) = labels.labels.get(label_index) {
if let Some(address) = label.address {
write!(f, " (at {address})")?;
} else {
write!(f, " (not yet defined)")?;
}
} else {
write!(f, " (invalid)")?;
}
writeln!(f, ",")?;
continue;
}
}
InsnFieldType::Memory(_)
| InsnFieldType::SmallSlot(_)
| InsnFieldType::BigSlot(_)
| InsnFieldType::SimOnlySlot(_)
| InsnFieldType::SmallSlotArrayIndexed(_)
| InsnFieldType::BigSlotArrayIndexed(_)
| InsnFieldType::SimOnlySlotArrayIndexed(_)
| InsnFieldType::SmallUInt(_)
| InsnFieldType::SmallSInt(_)
| InsnFieldType::InternedBigInt(_)
| InsnFieldType::U8(_)
| InsnFieldType::Empty(_) => {}
},
InsnFieldKind::Input
| InsnFieldKind::Memory
| InsnFieldKind::Output
| InsnFieldKind::Immediate => {}
}
macro_rules! debug_fmt_state_part {
($v:expr) => {
$v.debug_fmt(&mut f, ",", " // ", " // ", "", state_layout, state)
};
}
match field.ty {
InsnFieldType::Memory(v) => {
debug_fmt_state_part!(v)?;
}
InsnFieldType::SmallSlot(v) => {
debug_fmt_state_part!(v)?;
}
InsnFieldType::BigSlot(v) => {
debug_fmt_state_part!(v)?;
}
InsnFieldType::SimOnlySlot(v) => {
debug_fmt_state_part!(v)?;
}
InsnFieldType::SmallSlotArrayIndexed(v) => {
debug_fmt_state_part!(v)?;
}
InsnFieldType::BigSlotArrayIndexed(v) => {
debug_fmt_state_part!(v)?;
}
InsnFieldType::SimOnlySlotArrayIndexed(v) => {
debug_fmt_state_part!(v)?;
}
InsnFieldType::SmallUInt(v) => write!(f, "{v:#x}")?,
InsnFieldType::SmallSInt(v) => write!(f, "{v:#x}")?,
InsnFieldType::InternedBigInt(v) => write!(f, "{v:#x}")?,
InsnFieldType::U8(v) => write!(f, "{v:#x}")?,
InsnFieldType::USize(v) => write!(f, "{v}")?,
InsnFieldType::Empty(v) => write!(f, "{v:?}")?,
}
writeln!(f, ",")?;
}
write!(f.into_inner(), "}}")
}
}
pub(crate) trait Breakpoints {
type Break;
fn check_for_breakpoint(&mut self, pc: usize) -> ControlFlow<Self::Break>;
}
impl<T: ?Sized + Breakpoints> Breakpoints for &'_ mut T {
type Break = T::Break;
fn check_for_breakpoint(&mut self, pc: usize) -> ControlFlow<Self::Break> {
T::check_for_breakpoint(self, pc)
}
}
impl<T: ?Sized + Breakpoints> Breakpoints for Box<T> {
type Break = T::Break;
fn check_for_breakpoint(&mut self, pc: usize) -> ControlFlow<Self::Break> {
T::check_for_breakpoint(self, pc)
}
}
impl Breakpoints for () {
type Break = Infallible;
fn check_for_breakpoint(&mut self, _pc: usize) -> ControlFlow<Self::Break> {
ControlFlow::Continue(())
}
}
pub(crate) struct BreakpointsSet {
pub(crate) last_was_break: bool,
pub(crate) set: HashSet<usize>,
pub(crate) trace: bool,
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub(crate) enum BreakAction {
DumpStateAndContinue,
Continue,
}
impl Breakpoints for BreakpointsSet {
type Break = BreakAction;
fn check_for_breakpoint(&mut self, pc: usize) -> ControlFlow<Self::Break> {
let retval = if self.last_was_break {
ControlFlow::Continue(())
} else if self.set.contains(&pc) {
ControlFlow::Break(BreakAction::DumpStateAndContinue)
} else if self.trace {
ControlFlow::Break(BreakAction::Continue)
} else {
ControlFlow::Continue(())
};
self.last_was_break = retval.is_break();
retval
}
}
pub(crate) enum RunResult<Break, Return> {
Break(Break),
Return(Return),
}
macro_rules! impl_insns {
(
#[insn = $Insn:ident, next_macro = $next_macro:ident, branch_macro = $branch_macro:ident]
$vis:vis fn $State:ident::$run:ident(&mut $self:ident) -> $run_ret_ty:ty {
#[state]
let mut $state:ident = $state_init:expr;
setup! {
$($setup:tt)*
}
main_loop!();
cleanup! {
$($cleanup:tt)*
}
}
$(
$(#[$insn_meta:meta])*
$insn_name:ident $({
$(
#[kind = $field_kind:ident]
$(#[$field_meta:meta])*
$field_name:ident: $field_ty:ty,
)*
})? => $block:block
)*
) => {
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
$vis enum $Insn {
$(
$(#[$insn_meta])*
$insn_name $({
$(
$(#[$field_meta])*
$field_name: $field_ty,
)*
})?,
)*
}
impl $Insn {
$vis const MAX_FIELDS: usize = {
let mut retval = 0;
$($(
let fields = [$(stringify!($field_name),)*].len();
if retval < fields {
retval = fields;
}
)?)*
retval
};
}
impl $Insn {
$vis const fn fields_unit(&self) -> &'static [InsnField<InsnFieldTypeTransformUnit>] {
match self {
$(
$Insn::$insn_name {..} => &[
$($(InsnField {
ty: <$field_ty as InsnFieldTrait>::UNIT,
kind: InsnFieldKind::$field_kind,
},)*)?
],
)*
}
}
$vis fn fields<'a>(&'a self) -> std::array::IntoIter<InsnField<InsnFieldTypeTransformRef<'a>>, { $Insn::MAX_FIELDS }> {
match self {
$(
$Insn::$insn_name $({
$($field_name,)*
})? => make_array_into_iter([
$($(InsnField {
ty: <$field_ty as InsnFieldTrait>::variant($field_name),
kind: InsnFieldKind::$field_kind,
},)*)?
],
|| InsnField {
ty: InsnFieldType::empty(),
kind: InsnFieldKind::Immediate,
},
),
)*
}
}
$vis fn fields_with_names<'a>(&'a self) -> (&'static str, std::array::IntoIter<(&'static str, InsnField<InsnFieldTypeTransformRef<'a>>), { $Insn::MAX_FIELDS }>) {
match self {
$(
$Insn::$insn_name $({
$($field_name,)*
})? => (
stringify!($insn_name),
make_array_into_iter([
$($((stringify!($field_name), InsnField {
ty: <$field_ty as InsnFieldTrait>::variant($field_name),
kind: InsnFieldKind::$field_kind,
}),)*)?
],
|| ("", InsnField {
ty: InsnFieldType::empty(),
kind: InsnFieldKind::Immediate,
}),
),
),
)*
}
}
$vis fn fields_mut<'a>(&'a mut self) -> std::array::IntoIter<InsnField<InsnFieldTypeTransformRefMut<'a>>, { $Insn::MAX_FIELDS }> {
match self {
$(
$Insn::$insn_name $({
$($field_name,)*
})? => make_array_into_iter([
$($(InsnField {
ty: <$field_ty as InsnFieldTrait>::variant($field_name),
kind: InsnFieldKind::$field_kind,
},)*)?
],
|| InsnField {
ty: InsnFieldType::empty(),
kind: InsnFieldKind::Immediate,
},
),
)*
}
}
$vis fn into_fields(self) -> std::array::IntoIter<InsnField<InsnFieldTypeTransformValue>, { $Insn::MAX_FIELDS }> {
match self {
$(
$Insn::$insn_name $({
$($field_name,)*
})? => make_array_into_iter([
$($(InsnField {
ty: <$field_ty as InsnFieldTrait>::variant($field_name),
kind: InsnFieldKind::$field_kind,
},)*)?
],
|| InsnField {
ty: InsnFieldType::empty(),
kind: InsnFieldKind::Immediate,
},
),
)*
}
}
}
impl $State {
$vis fn $run<B: Breakpoints>(&mut $self, mut breakpoints: B) -> RunResult<B::Break, $run_ret_ty> {
let mut $state = $state_init;
$($setup)*
let mut insn = $state.insns[$state.pc];
let retval = 'main_loop: loop {
if let ControlFlow::Break(b) = breakpoints.check_for_breakpoint($state.pc) {
break RunResult::Break(b);
}
macro_rules! $next_macro {
() => {
$state.pc += 1;
insn = $state.insns[$state.pc];
continue 'main_loop;
};
}
macro_rules! $branch_macro {
($next_pc:expr) => {
$state.pc = $next_pc;
insn = $state.insns[$state.pc];
continue 'main_loop;
};
}
let _: Infallible = match insn {
$(
$Insn::$insn_name $({
$(
$field_name,
)*
})? => {
$block
}
)*
};
};
$($cleanup)*
retval
}
}
};
}
pub(crate) trait InsnsBuildingKind: Copy + Eq + fmt::Debug + Hash + Default {
type Vec<T: fmt::Debug + Clone + Eq + Hash + Send + Sync + 'static>: fmt::Debug
+ Clone
+ Eq
+ Hash
+ Send
+ Sync
+ 'static
+ Default
+ Deref<Target = [T]>
+ FromIterator<T>;
type Labels: Default;
fn labels(labels: &Self::Labels) -> Option<&Labels>;
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
pub(crate) struct InsnsBuildingDone;
impl InsnsBuildingKind for InsnsBuildingDone {
type Vec<T: fmt::Debug + Clone + Eq + Hash + Send + Sync + 'static> = Interned<[T]>;
type Labels = ();
fn labels(_labels: &Self::Labels) -> Option<&Labels> {
None
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
pub(crate) struct InsnsBuilding;
impl InsnsBuildingKind for InsnsBuilding {
type Vec<T: fmt::Debug + Clone + Eq + Hash + Send + Sync + 'static> = Vec<T>;
type Labels = Labels;
fn labels(labels: &Self::Labels) -> Option<&Labels> {
Some(labels)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub(crate) struct Label(pub(crate) usize);
impl fmt::Debug for Label {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "L{}:", self.0)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub(crate) enum InsnOrLabel {
Insn(Insn),
Label(Label),
}
impl From<Insn> for InsnOrLabel {
fn from(value: Insn) -> Self {
Self::Insn(value)
}
}
impl From<Label> for InsnOrLabel {
fn from(value: Label) -> Self {
Self::Label(value)
}
}
impl fmt::Debug for InsnOrLabel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Insn(v) => v.fmt(f),
Self::Label(v) => v.fmt(f),
}
}
}
struct LabelData {
address: Option<usize>,
}
#[derive(Default)]
pub(crate) struct Labels {
labels: Vec<LabelData>,
address_to_label_map: HashMap<usize, Vec<Label>>,
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub(crate) struct Insns<BK: InsnsBuildingKind> {
pub(crate) insns: BK::Vec<Insn>,
pub(crate) insn_source_locations: BK::Vec<SourceLocation>,
pub(crate) labels: BK::Labels,
pub(crate) state_layout: StateLayout<BK>,
}
impl<BK: InsnsBuildingKind> Insns<BK> {
pub(crate) fn state_layout(&self) -> &StateLayout<BK> {
&self.state_layout
}
}
struct InsnsDebug<'a, BK: InsnsBuildingKind> {
insns: &'a [Insn],
insn_source_locations: &'a [SourceLocation],
labels: &'a BK::Labels,
state_layout: &'a StateLayout<BK>,
state: Option<&'a State>,
}
struct InsnDebug<'a, BK: InsnsBuildingKind> {
address: usize,
source_location: Option<SourceLocation>,
insn: &'a Insn,
labels: Option<&'a Labels>,
state_layout: &'a StateLayout<BK>,
state: Option<&'a State>,
}
impl<BK: InsnsBuildingKind> fmt::Debug for InsnDebug<'_, BK> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(labels) = self
.labels
.and_then(|labels| labels.address_to_label_map.get(&self.address))
{
for &label in labels {
writeln!(f, "{label:?}")?;
}
}
if let Some(source_location) = self.source_location {
writeln!(f, "// at: {source_location}")?;
}
write!(f, "{}: ", self.address)?;
self.insn
.debug_fmt(f, self.labels, Some(self.state_layout), self.state)
}
}
impl<'a, BK: InsnsBuildingKind> fmt::Debug for InsnsDebug<'a, BK> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let labels = BK::labels(&self.labels);
let mut debug_list = f.debug_list();
let mut last_source_location = None;
for (address, (insn, &source_location)) in self
.insns
.iter()
.zip(self.insn_source_locations)
.enumerate()
{
debug_list.entry(&InsnDebug {
address,
source_location: if Some(source_location) != last_source_location {
Some(source_location)
} else {
None
},
insn,
labels,
state_layout: self.state_layout,
state: self.state,
});
last_source_location = Some(source_location);
}
debug_list.finish()
}
}
impl<BK: InsnsBuildingKind> Insns<BK> {
pub(crate) fn debug_insn_at<'a>(
&'a self,
address: usize,
state: Option<&'a State>,
) -> impl fmt::Debug + 'a {
let Self {
insns,
insn_source_locations,
labels,
state_layout,
} = self;
InsnDebug {
address,
source_location: Some(insn_source_locations[address]),
insn: &insns[address],
labels: BK::labels(labels),
state_layout,
state,
}
}
}
impl State {
pub(crate) fn debug_insn_at(&self, address: usize) -> impl fmt::Debug + '_ {
self.insns.debug_insn_at(address, Some(self))
}
}
struct InsnsOfState<'a>(&'a State);
impl fmt::Debug for InsnsOfState<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.insns.debug_fmt(Some(self.0), f)
}
}
impl<BK: InsnsBuildingKind> Insns<BK> {
fn debug_fmt(&self, state: Option<&State>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
insns,
insn_source_locations,
labels,
state_layout,
} = self;
f.debug_struct("Insns")
.field("state_layout", state_layout)
.field(
"insns",
&InsnsDebug::<BK> {
insns,
insn_source_locations,
labels,
state_layout,
state,
},
)
.finish_non_exhaustive()
}
}
impl<BK: InsnsBuildingKind> fmt::Debug for Insns<BK> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.debug_fmt(None, f)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub(crate) struct StatePartArrayIndex<K: StatePartKind> {
pub(crate) index: StatePartIndex<StatePartKindSmallSlots>,
pub(crate) len: usize,
pub(crate) stride: StatePartLen<K>,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub(crate) struct StatePartArrayIndexed<K: StatePartKind> {
pub(crate) base: StatePartIndex<K>,
pub(crate) indexes: Interned<[StatePartArrayIndex<K>]>,
}
impl<K: StatePartKind> StatePartArrayIndexed<K> {
pub(crate) fn debug_fmt<BK: InsnsBuildingKind>(
&self,
f: &mut impl fmt::Write,
before_debug_info_text: &str,
comment_start: &str,
comment_line_start: &str,
comment_end: &str,
state_layout: Option<&StateLayout<BK>>,
state: Option<&State>,
) -> fmt::Result {
let Self { base, indexes } = *self;
if indexes.is_empty() {
base.debug_fmt(
f,
before_debug_info_text,
comment_start,
comment_line_start,
comment_end,
state_layout,
state,
)
} else {
base.debug_fmt(f, "", " /* ", "", " */ ", state_layout, state)?;
for StatePartArrayIndex { index, len, stride } in indexes {
f.write_str("[")?;
index.debug_fmt(f, "", " /* ", "", " */ ", state_layout, state)?;
write!(f, ", len={len}, stride={}]", stride.value)?;
}
f.write_str(before_debug_info_text)
}
}
}
impl<K: StatePartKind + fmt::Debug> fmt::Debug for StatePartArrayIndexed<K> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.debug_fmt::<InsnsBuildingDone>(f, "", "", "", "", None, None)
}
}
impl<K: StatePartKind> From<StatePartIndex<K>> for StatePartArrayIndexed<K> {
fn from(base: StatePartIndex<K>) -> Self {
Self {
base,
indexes: Interned::default(),
}
}
}
impl<K: StatePartKind> StatePartArrayIndexed<K> {
pub(crate) fn for_each_target2(
base: StatePartIndex<K>,
indexes: &[StatePartArrayIndex<K>],
f: &mut (impl FnMut(StatePartIndex<K>) + ?Sized),
) {
if let [next, rest @ ..] = indexes {
for i in 0..next.len.try_into().expect("array too big") {
Self::for_each_target2(
StatePartIndex::new(
base.value
.checked_add(next.stride.value.checked_mul(i).expect("array too big"))
.expect("array too big"),
),
rest,
f,
);
}
} else {
f(base);
}
}
pub(crate) fn for_each_target(self, mut f: impl FnMut(StatePartIndex<K>)) {
Self::for_each_target2(self.base, &self.indexes, &mut f);
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub(crate) struct StatePart<K: StatePartKind> {
pub(crate) value: K::State,
}
impl<K: StatePartKind> StatePart<K> {
pub(crate) fn new(layout_data: &[K::LayoutData]) -> Self {
Self {
value: K::new_state(layout_data),
}
}
pub(crate) fn borrow<'a>(&'a mut self) -> BorrowedStatePart<'a, K> {
BorrowedStatePart {
value: K::borrow_state(&mut self.value),
}
}
pub(crate) fn state_index_fetch_maybe_modified_flag(
&self,
part_index: StatePartIndex<K>,
) -> bool {
K::state_index_fetch_maybe_modified_flag(&self.value, part_index)
}
pub(crate) fn state_index_range_fetch_maybe_modified_flags(
&self,
part_index_range: StatePartIndexRange<K>,
) -> bool {
K::state_index_range_fetch_maybe_modified_flags(&self.value, part_index_range)
}
pub(crate) fn clear_all_maybe_modified_flags(&mut self) {
K::clear_all_maybe_modified_flags(&mut self.value)
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub(crate) struct BorrowedStatePart<'a, K: StatePartKind> {
pub(crate) value: K::BorrowedState<'a>,
}
impl<K: StatePartKind> BorrowedStatePart<'_, K> {
pub(crate) fn get_disjoint_mut<const N: usize>(
&mut self,
indexes: [StatePartIndex<K>; N],
) -> [&mut K::StateElement; N] {
K::borrowed_state_get_disjoint_mut(&mut self.value, indexes)
}
}
impl<K: StatePartKind> Index<StatePartIndex<K>> for StatePart<K> {
type Output = K::StateElement;
fn index(&self, index: StatePartIndex<K>) -> &Self::Output {
K::state_index(&self.value, index)
}
}
impl<K: StatePartKind> IndexMut<StatePartIndex<K>> for StatePart<K> {
fn index_mut(&mut self, index: StatePartIndex<K>) -> &mut Self::Output {
K::state_index_mut(&mut self.value, index)
}
}
impl<K: StatePartKind> Index<StatePartIndex<K>> for BorrowedStatePart<'_, K> {
type Output = K::StateElement;
fn index(&self, index: StatePartIndex<K>) -> &Self::Output {
K::borrowed_state_index(&self.value, index)
}
}
impl<K: StatePartKind> IndexMut<StatePartIndex<K>> for BorrowedStatePart<'_, K> {
fn index_mut(&mut self, index: StatePartIndex<K>) -> &mut Self::Output {
K::borrowed_state_index_mut(&mut self.value, index)
}
}
macro_rules! make_state {
(
state_plural_fields = [$(#[state] $state_plural_field:ident,)* $(#[type] $type_plural_field:ident,)*];
state_kinds = [$(#[state] $state_kind:ident,)* $(#[type] $type_kind:ident,)*];
) => {
pub(crate) struct State {
pub(crate) insns: Interned<Insns<InsnsBuildingDone>>,
pub(crate) pc: usize,
pub(crate) memory_write_log: Vec<(StatePartIndex<StatePartKindMemories>, usize)>,
pub(crate) assert_failed_log: Vec<usize>,
$(pub(crate) $state_plural_field: StatePart<$state_kind>,)*
$(pub(crate) $type_plural_field: StatePart<$type_kind>,)*
}
impl fmt::Debug for State {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
insns: _,
pc,
memory_write_log,
assert_failed_log,
$($state_plural_field,)*
$($type_plural_field,)*
} = self;
f.debug_struct("State")
.field("insns", &InsnsOfState(self))
.field("pc", pc)
.field("memory_write_log", memory_write_log)
.field("assert_failed_log", assert_failed_log)
$(.field(stringify!($state_plural_field), $state_plural_field))*
$(.field(stringify!($type_plural_field), $type_plural_field))*
.finish()
}
}
impl State {
pub(crate) fn new(insns: Interned<Insns<InsnsBuildingDone>>) -> Self {
Self {
insns,
pc: 0,
memory_write_log: Vec::with_capacity(32),
assert_failed_log: Vec::new(),
$($state_plural_field: StatePart::new(&insns.state_layout.$state_plural_field.layout_data),)*
$($type_plural_field: StatePart::new(&insns.state_layout.ty.$type_plural_field.layout_data),)*
}
}
pub(crate) fn borrow(&mut self) -> BorrowedState<'_> {
BorrowedState {
orig_insns: self.insns,
insns: &self.insns.insns,
pc: self.pc,
orig_pc: &mut self.pc,
memory_write_log: &mut self.memory_write_log,
assert_failed_log: &mut self.assert_failed_log,
$($state_plural_field: self.$state_plural_field.borrow(),)*
$($type_plural_field: self.$type_plural_field.borrow(),)*
}
}
pub(crate) fn type_index_range_fetch_maybe_modified_flags(&self, range: TypeIndexRange) -> bool {
$(self.$type_plural_field.state_index_range_fetch_maybe_modified_flags(
range.$type_plural_field,
))||*
}
pub(crate) fn clear_all_maybe_modified_flags(&mut self) {
$(self.$state_plural_field.clear_all_maybe_modified_flags();)*
$(self.$type_plural_field.clear_all_maybe_modified_flags();)*
}
}
#[derive(Debug)]
pub(crate) struct BorrowedState<'a> {
pub(crate) orig_insns: Interned<Insns<InsnsBuildingDone>>,
pub(crate) insns: &'a [Insn],
pub(crate) orig_pc: &'a mut usize,
pub(crate) pc: usize,
pub(crate) memory_write_log: &'a mut Vec<(StatePartIndex<StatePartKindMemories>, usize)>,
pub(crate) assert_failed_log: &'a mut Vec<usize>,
$(pub(crate) $state_plural_field: BorrowedStatePart<'a, $state_kind>,)*
$(pub(crate) $type_plural_field: BorrowedStatePart<'a, $type_kind>,)*
}
impl Drop for BorrowedState<'_> {
fn drop(&mut self) {
*self.orig_pc = self.pc;
}
}
};
}
get_state_part_kinds! {
make_state! {
state_plural_fields;
state_kinds;
}
}
#[derive(PartialEq, Eq, Hash, Clone)]
pub(crate) struct StatePartIndexMap<K: StatePartKind, V> {
pub(crate) map: VecMap<V>,
_phantom: PhantomData<K>,
}
impl<K: StatePartKind, V: fmt::Debug> fmt::Debug for StatePartIndexMap<K, V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_map().entries(self.iter()).finish()
}
}
impl<K: StatePartKind, V> Extend<(StatePartIndex<K>, V)> for StatePartIndexMap<K, V> {
fn extend<T: IntoIterator<Item = (StatePartIndex<K>, V)>>(&mut self, iter: T) {
self.map
.extend(iter.into_iter().map(|(k, v)| (k.as_usize(), v)));
}
}
impl<K: StatePartKind, V> StatePartIndexMap<K, V> {
pub(crate) fn new() -> Self {
Self {
map: VecMap::new(),
_phantom: PhantomData,
}
}
pub(crate) fn get(&self, key: StatePartIndex<K>) -> Option<&V> {
self.map.get(key.as_usize())
}
pub(crate) fn get_mut(&mut self, key: StatePartIndex<K>) -> Option<&mut V> {
self.map.get_mut(key.as_usize())
}
pub(crate) fn keys(&self) -> impl Iterator<Item = StatePartIndex<K>> + '_ {
self.map.keys().map(|v| StatePartIndex::new(v as _))
}
pub(crate) fn values(&self) -> vec_map::Values<'_, V> {
self.map.values()
}
pub(crate) fn values_mut(&mut self) -> vec_map::ValuesMut<'_, V> {
self.map.values_mut()
}
pub(crate) fn iter(&self) -> impl Iterator<Item = (StatePartIndex<K>, &V)> + '_ {
self.map
.iter()
.map(|(k, v)| (StatePartIndex::new(k as u32), v))
}
pub(crate) fn iter_mut(&mut self) -> impl Iterator<Item = (StatePartIndex<K>, &mut V)> + '_ {
self.map
.iter_mut()
.map(|(k, v)| (StatePartIndex::new(k as u32), v))
}
pub(crate) fn len(&self) -> usize {
self.map.len()
}
pub(crate) fn is_empty(&self) -> bool {
self.map.is_empty()
}
pub(crate) fn insert(&mut self, key: StatePartIndex<K>, value: V) -> Option<V> {
self.map.insert(key.as_usize(), value)
}
pub(crate) fn remove(&mut self, key: StatePartIndex<K>) -> Option<V> {
self.map.remove(key.as_usize())
}
pub(crate) fn entry(&mut self, key: StatePartIndex<K>) -> StatePartIndexMapEntry<'_, K, V> {
match self.map.entry(key.as_usize()) {
vec_map::Entry::Vacant(v) => {
StatePartIndexMapEntry::Vacant(StatePartIndexMapVacantEntry(v, PhantomData))
}
vec_map::Entry::Occupied(v) => {
StatePartIndexMapEntry::Occupied(StatePartIndexMapOccupiedEntry(v, PhantomData))
}
}
}
}
impl<K: StatePartKind, V> Default for StatePartIndexMap<K, V> {
fn default() -> Self {
Self::new()
}
}
impl<K: StatePartKind, V> Index<StatePartIndex<K>> for StatePartIndexMap<K, V> {
type Output = V;
fn index(&self, index: StatePartIndex<K>) -> &Self::Output {
&self.map[index.as_usize()]
}
}
impl<K: StatePartKind, V> IndexMut<StatePartIndex<K>> for StatePartIndexMap<K, V> {
fn index_mut(&mut self, index: StatePartIndex<K>) -> &mut Self::Output {
&mut self.map[index.as_usize()]
}
}
pub(crate) struct StatePartIndexMapVacantEntry<'a, K: StatePartKind, V>(
vec_map::VacantEntry<'a, V>,
PhantomData<K>,
);
pub(crate) struct StatePartIndexMapOccupiedEntry<'a, K: StatePartKind, V>(
vec_map::OccupiedEntry<'a, V>,
PhantomData<K>,
);
pub(crate) enum StatePartIndexMapEntry<'a, K: StatePartKind, V> {
Vacant(StatePartIndexMapVacantEntry<'a, K, V>),
Occupied(StatePartIndexMapOccupiedEntry<'a, K, V>),
}
impl<'a, K: StatePartKind, V> StatePartIndexMapEntry<'a, K, V> {
pub(crate) fn or_insert(self, default: V) -> &'a mut V {
match self {
StatePartIndexMapEntry::Vacant(v) => v.0.insert(default),
StatePartIndexMapEntry::Occupied(v) => v.0.into_mut(),
}
}
pub(crate) fn or_insert_with(self, f: impl FnOnce() -> V) -> &'a mut V {
match self {
StatePartIndexMapEntry::Vacant(v) => v.0.insert(f()),
StatePartIndexMapEntry::Occupied(v) => v.0.into_mut(),
}
}
}
impl Insns<InsnsBuilding> {
pub(crate) fn new() -> Self {
Self {
insns: Vec::new(),
insn_source_locations: Vec::new(),
labels: Labels::default(),
state_layout: StateLayout::empty(),
}
}
pub(crate) fn last_insn_pc(&self) -> usize {
self.insns
.len()
.checked_sub(1)
.expect("no last instruction")
}
pub(crate) fn next_insn_pc(&self) -> usize {
self.insns.len()
}
pub(crate) fn push(&mut self, insn: Insn, source_location: SourceLocation) {
self.insns.push(insn);
self.insn_source_locations.push(source_location);
}
pub(crate) fn extend<T: Into<InsnOrLabel>>(
&mut self,
insns_or_labels: impl IntoIterator<Item = T>,
source_location: SourceLocation,
) {
for insn_or_label in insns_or_labels {
match insn_or_label.into() {
InsnOrLabel::Insn(insn) => self.push(insn, source_location),
InsnOrLabel::Label(label) => self.define_label_at_next_insn(label),
}
}
}
pub(crate) fn allocate_variable<BK: InsnsBuildingKind>(
&mut self,
layout: &TypeLayout<BK>,
) -> TypeIndexRange {
self.state_layout.ty.allocate(layout)
}
pub(crate) fn new_label(&mut self) -> Label {
let retval = Label(self.labels.labels.len());
self.labels.labels.push(LabelData { address: None });
retval
}
#[track_caller]
pub(crate) fn define_label_at_next_insn(&mut self, label: Label) {
let address = self.next_insn_pc();
let label_data = &mut self.labels.labels[label.0];
assert!(label_data.address.is_none(), "label already defined");
label_data.address = Some(address);
self.labels
.address_to_label_map
.entry(address)
.or_default()
.push(label);
}
}
impl From<Insns<InsnsBuilding>> for Insns<InsnsBuildingDone> {
fn from(input: Insns<InsnsBuilding>) -> Self {
let Insns {
mut insns,
insn_source_locations,
labels,
state_layout,
} = input;
for insn in &mut insns {
for field in insn.fields_mut() {
match field.kind {
InsnFieldKind::BranchTarget => match field.ty {
InsnFieldType::USize(value) => {
*value = labels.labels[*value]
.address
.expect("label address not set");
}
InsnFieldType::Memory(_)
| InsnFieldType::SmallSlot(_)
| InsnFieldType::BigSlot(_)
| InsnFieldType::SimOnlySlot(_)
| InsnFieldType::SmallSlotArrayIndexed(_)
| InsnFieldType::BigSlotArrayIndexed(_)
| InsnFieldType::SimOnlySlotArrayIndexed(_)
| InsnFieldType::SmallUInt(_)
| InsnFieldType::SmallSInt(_)
| InsnFieldType::InternedBigInt(_)
| InsnFieldType::U8(_)
| InsnFieldType::Empty(_) => {
unreachable!()
}
},
InsnFieldKind::Input
| InsnFieldKind::Memory
| InsnFieldKind::Output
| InsnFieldKind::Immediate => {}
}
}
}
Insns {
insns: Intern::intern_owned(insns),
insn_source_locations: Intern::intern_owned(insn_source_locations),
labels: (),
state_layout: state_layout.into(),
}
}
}
impl State {
pub(crate) fn setup_call(&mut self, entry_pc: usize) {
let Self {
insns: _,
pc,
memory_write_log: _,
assert_failed_log: _,
memories: _,
small_slots: _,
big_slots: _,
sim_only_slots: _,
} = self;
*pc = entry_pc;
}
}
impl BorrowedState<'_> {
fn eval_array_indexed<K: StatePartKind>(
&self,
array_indexed: StatePartArrayIndexed<K>,
) -> Option<StatePartIndex<K>> {
let StatePartArrayIndexed {
base: mut retval,
indexes,
} = array_indexed;
for StatePartArrayIndex { index, len, stride } in indexes {
let index = self.small_slots[index];
if index >= len as SmallUInt {
return None;
}
retval.value += stride.value * index as u32;
}
Some(retval)
}
fn log_memory_write(&mut self, memory: StatePartIndex<StatePartKindMemories>, addr: SmallUInt) {
let Ok(addr) = usize::try_from(addr) else {
return;
};
if addr < self.memories[memory].array_type.len() {
let log_entry = (memory, addr);
if self.memory_write_log.last().copied() == Some(log_entry) {
return;
}
self.memory_write_log.push(log_entry);
}
}
#[cold]
fn assert_failed(&mut self, assert_index: usize) {
self.assert_failed_log.push(assert_index);
}
}
fn bigint_pow2(width: usize) -> Interned<BigInt> {
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
struct MyMemoize;
impl Memoize for MyMemoize {
type Input = usize;
type InputOwned = usize;
type Output = Interned<BigInt>;
fn inner(self, input: &Self::Input) -> Self::Output {
(BigInt::one() << *input).intern_sized()
}
}
MyMemoize.get(&width)
}
fn bigint_mask(width: usize) -> Interned<BigInt> {
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
struct MyMemoize;
impl Memoize for MyMemoize {
type Input = usize;
type InputOwned = usize;
type Output = Interned<BigInt>;
fn inner(self, input: &Self::Input) -> Self::Output {
((BigInt::one() << *input) - BigInt::one()).intern_sized()
}
}
MyMemoize.get(&width)
}
fn bigint_not_mask(width: usize) -> Interned<BigInt> {
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
struct MyMemoize;
impl Memoize for MyMemoize {
type Input = usize;
type InputOwned = usize;
type Output = Interned<BigInt>;
fn inner(self, input: &Self::Input) -> Self::Output {
(-BigInt::one() << *input).intern_sized()
}
}
MyMemoize.get(&width)
}
fn cast_bigint_to_sint(src: &BigInt, dest_width: usize) -> BigInt {
if dest_width == 0 {
BigInt::ZERO
} else if src.bit((dest_width - 1) as u64) {
src | &*bigint_not_mask(dest_width)
} else {
src & &*bigint_mask(dest_width)
}
}
fn cast_bigint_to_uint(src: &BigInt, dest_width: usize) -> BigInt {
src & &*bigint_mask(dest_width)
}
fn memory_get_mut(
memory: &mut BitSlice,
addr: SmallUInt,
stride: usize,
start: usize,
width: usize,
) -> Option<&mut BitSlice> {
let start = usize::try_from(addr)
.ok()?
.checked_mul(stride)?
.checked_add(start)?;
memory.get_mut(start..start.checked_add(width)?)
}
fn memory_read_big<T: BoolOrIntType>(
memory: &mut BitSlice,
addr: SmallUInt,
stride: usize,
start: usize,
width: usize,
) -> Option<BigInt> {
let bits = memory_get_mut(memory, addr, stride, start, width)?;
Some(T::bits_to_bigint(bits))
}
fn memory_write_big<T: BoolOrIntType>(
memory: &mut BitSlice,
addr: SmallUInt,
stride: usize,
start: usize,
width: usize,
value: &BigInt,
) -> Option<()> {
let bits = memory_get_mut(memory, addr, stride, start, width)?;
T::copy_bits_from_bigint_wrapping(value, bits);
Some(())
}
impl_insns! {
#[insn = Insn, next_macro = next, branch_macro = branch]
pub(crate) fn State::run(&mut self) -> () {
#[state]
let mut state = self.borrow();
setup! {}
main_loop!();
cleanup! {}
}
CopySmall {
#[kind = Output]
dest: StatePartIndex<StatePartKindSmallSlots>,
#[kind = Input]
src: StatePartIndex<StatePartKindSmallSlots>,
} => {
state.small_slots[dest] = state.small_slots[src];
next!();
}
Copy {
#[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
src: StatePartIndex<StatePartKindBigSlots>,
} => {
if dest != src {
let [dest, src] = state.big_slots.get_disjoint_mut([dest, src]);
dest.clone_from(src);
}
next!();
}
CloneSimOnly {
#[kind = Output]
dest: StatePartIndex<StatePartKindSimOnlySlots>,
#[kind = Input]
src: StatePartIndex<StatePartKindSimOnlySlots>,
} => {
if dest != src {
let [dest, src] = state.sim_only_slots.get_disjoint_mut([dest, src]);
dest.clone_from(src);
}
next!();
}
ReadSmallIndexed {
#[kind = Output]
dest: StatePartIndex<StatePartKindSmallSlots>,
#[kind = Input]
src: StatePartArrayIndexed<StatePartKindSmallSlots>,
} => {
if let Some(src) = state.eval_array_indexed(src) {
state.small_slots[dest] = state.small_slots[src];
} else {
state.small_slots[dest] = 0;
}
next!();
}
ReadIndexed {
#[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
src: StatePartArrayIndexed<StatePartKindBigSlots>,
} => {
if let Some(src) = state.eval_array_indexed(src) {
if dest != src {
let [dest, src] = state.big_slots.get_disjoint_mut([dest, src]);
dest.clone_from(src);
}
} else {
state.big_slots[dest] = BigInt::ZERO;
}
next!();
}
ReadSimOnlyIndexed {
#[kind = Output]
dest: StatePartIndex<StatePartKindSimOnlySlots>,
#[kind = Input]
src: StatePartArrayIndexed<StatePartKindSimOnlySlots>,
} => {
if let Some(src) = state.eval_array_indexed(src) {
if dest != src {
let [dest, src] = state.sim_only_slots.get_disjoint_mut([dest, src]);
dest.clone_from(src);
}
} else {
state.sim_only_slots[dest] = state.sim_only_slots[dest].ty().default_value();
}
next!();
}
WriteSmallIndexed {
#[kind = Output]
dest: StatePartArrayIndexed<StatePartKindSmallSlots>,
#[kind = Input]
src: StatePartIndex<StatePartKindSmallSlots>,
} => {
if let Some(dest) = state.eval_array_indexed(dest) {
state.small_slots[dest] = state.small_slots[src];
}
next!();
}
WriteIndexed {
#[kind = Output]
dest: StatePartArrayIndexed<StatePartKindBigSlots>,
#[kind = Input]
src: StatePartIndex<StatePartKindBigSlots>,
} => {
if let Some(dest) = state.eval_array_indexed(dest) {
if dest != src {
let [dest, src] = state.big_slots.get_disjoint_mut([dest, src]);
dest.clone_from(src);
}
}
next!();
}
WriteSimOnlyIndexed {
#[kind = Output]
dest: StatePartArrayIndexed<StatePartKindSimOnlySlots>,
#[kind = Input]
src: StatePartIndex<StatePartKindSimOnlySlots>,
} => {
if let Some(dest) = state.eval_array_indexed(dest) {
if dest != src {
let [dest, src] = state.sim_only_slots.get_disjoint_mut([dest, src]);
dest.clone_from(src);
}
}
next!();
}
CastBigToArrayIndex {
#[kind = Output]
dest: StatePartIndex<StatePartKindSmallSlots>,
#[kind = Input]
src: StatePartIndex<StatePartKindBigSlots>,
} => {
let src = &state.big_slots[src];
let value = src.try_into().unwrap_or(SmallUInt::MAX);
state.small_slots[dest] = value;
next!();
}
IsNonZeroDestIsSmall {
#[kind = Output]
dest: StatePartIndex<StatePartKindSmallSlots>,
#[kind = Input]
src: StatePartIndex<StatePartKindBigSlots>,
} => {
let src = &state.big_slots[src];
let value = !src.is_zero();
state.small_slots[dest] = value as SmallUInt;
next!();
}
CastToSInt {
#[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
src: StatePartIndex<StatePartKindBigSlots>,
#[kind = Immediate]
dest_width: usize,
} => {
let value = cast_bigint_to_sint(&state.big_slots[src], dest_width);
state.big_slots[dest] = value;
next!();
}
CastToUInt {
#[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
src: StatePartIndex<StatePartKindBigSlots>,
#[kind = Immediate]
dest_width: usize,
} => {
let value = cast_bigint_to_uint(&state.big_slots[src], dest_width);
state.big_slots[dest] = value;
next!();
}
And {
#[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
lhs: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
rhs: StatePartIndex<StatePartKindBigSlots>,
} => {
let value = &state.big_slots[lhs] & &state.big_slots[rhs];
state.big_slots[dest] = value;
next!();
}
AndSmall {
#[kind = Output]
dest: StatePartIndex<StatePartKindSmallSlots>,
#[kind = Input]
lhs: StatePartIndex<StatePartKindSmallSlots>,
#[kind = Input]
rhs: StatePartIndex<StatePartKindSmallSlots>,
} => {
let value = state.small_slots[lhs] & state.small_slots[rhs];
state.small_slots[dest] = value;
next!();
}
AndSmallImmediate {
#[kind = Output]
dest: StatePartIndex<StatePartKindSmallSlots>,
#[kind = Input]
lhs: StatePartIndex<StatePartKindSmallSlots>,
#[kind = Immediate]
rhs: SmallUInt,
} => {
let value = state.small_slots[lhs] & rhs;
state.small_slots[dest] = value;
next!();
}
AndBigWithSmallImmediate {
#[kind = Output]
dest: StatePartIndex<StatePartKindSmallSlots>,
#[kind = Input]
lhs: StatePartIndex<StatePartKindBigSlots>,
#[kind = Immediate]
rhs: SmallUInt,
} => {
let lhs = &state.big_slots[lhs];
let mut lhs_lsb64 = lhs.iter_u64_digits().next().unwrap_or(0);
if lhs.is_negative() {
lhs_lsb64 = lhs_lsb64.wrapping_neg();
}
let value = lhs_lsb64 & rhs;
state.small_slots[dest] = value;
next!();
}
Or {
#[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
lhs: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
rhs: StatePartIndex<StatePartKindBigSlots>,
} => {
let value = &state.big_slots[lhs] | &state.big_slots[rhs];
state.big_slots[dest] = value;
next!();
}
Xor {
#[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
lhs: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
rhs: StatePartIndex<StatePartKindBigSlots>,
} => {
let value = &state.big_slots[lhs] ^ &state.big_slots[rhs];
state.big_slots[dest] = value;
next!();
}
XorSmallImmediate {
#[kind = Output]
dest: StatePartIndex<StatePartKindSmallSlots>,
#[kind = Input]
lhs: StatePartIndex<StatePartKindSmallSlots>,
#[kind = Immediate]
rhs: SmallUInt,
} => {
let value = state.small_slots[lhs] ^ rhs;
state.small_slots[dest] = value;
next!();
}
NotS {
#[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
src: StatePartIndex<StatePartKindBigSlots>,
} => {
let value = !&state.big_slots[src];
state.big_slots[dest] = value;
next!();
}
NotU {
#[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
src: StatePartIndex<StatePartKindBigSlots>,
#[kind = Immediate]
width: usize,
} => {
let value = &state.big_slots[src] ^ &*bigint_mask(width);
state.big_slots[dest] = value;
next!();
}
Neg {
#[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
src: StatePartIndex<StatePartKindBigSlots>,
} => {
let value = -&state.big_slots[src];
state.big_slots[dest] = value;
next!();
}
Add {
#[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
lhs: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
rhs: StatePartIndex<StatePartKindBigSlots>,
} => {
let value = &state.big_slots[lhs] + &state.big_slots[rhs];
state.big_slots[dest] = value;
next!();
}
SubS {
#[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
lhs: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
rhs: StatePartIndex<StatePartKindBigSlots>,
} => {
let value = &state.big_slots[lhs] - &state.big_slots[rhs];
state.big_slots[dest] = value;
next!();
}
SubU {
#[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
lhs: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
rhs: StatePartIndex<StatePartKindBigSlots>,
#[kind = Immediate]
dest_width: usize,
} => {
let mut value = &state.big_slots[lhs] - &state.big_slots[rhs];
value &= &*bigint_mask(dest_width);
state.big_slots[dest] = value;
next!();
}
Mul {
#[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
lhs: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
rhs: StatePartIndex<StatePartKindBigSlots>,
} => {
let value = &state.big_slots[lhs] * &state.big_slots[rhs];
state.big_slots[dest] = value;
next!();
}
Div {
#[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
lhs: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
rhs: StatePartIndex<StatePartKindBigSlots>,
} => {
let value = state.big_slots[lhs].checked_div(&state.big_slots[rhs]).unwrap_or(BigInt::ZERO);
state.big_slots[dest] = value;
next!();
}
Rem {
#[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
lhs: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
rhs: StatePartIndex<StatePartKindBigSlots>,
} => {
// no checked_rem?!
let value = if state.big_slots[rhs].is_zero() {
BigInt::ZERO
} else {
&state.big_slots[lhs] % &state.big_slots[rhs]
};
state.big_slots[dest] = value;
next!();
}
DynShl {
#[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
lhs: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
rhs: StatePartIndex<StatePartKindBigSlots>,
} => {
let value = &state.big_slots[lhs] << state.big_slots[rhs].to_usize().expect("shl by invalid value");
state.big_slots[dest] = value;
next!();
}
DynShr {
#[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
lhs: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
rhs: StatePartIndex<StatePartKindBigSlots>,
} => {
assert!(!state.big_slots[rhs].is_negative(), "shr by invalid value");
let value = state.big_slots[rhs].to_usize().map_or(BigInt::ZERO, |rhs| &state.big_slots[lhs] >> rhs);
state.big_slots[dest] = value;
next!();
}
Shl {
#[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
lhs: StatePartIndex<StatePartKindBigSlots>,
#[kind = Immediate]
rhs: usize,
} => {
let value = &state.big_slots[lhs] << rhs;
state.big_slots[dest] = value;
next!();
}
Shr {
#[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
lhs: StatePartIndex<StatePartKindBigSlots>,
#[kind = Immediate]
rhs: usize,
} => {
let value = &state.big_slots[lhs] >> rhs;
state.big_slots[dest] = value;
next!();
}
SliceInt {
#[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
src: StatePartIndex<StatePartKindBigSlots>,
#[kind = Immediate]
start: usize,
#[kind = Immediate]
len: usize,
} => {
let value = &state.big_slots[src] >> start;
state.big_slots[dest] = value & &*bigint_mask(len);
next!();
}
CmpEq {
#[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
lhs: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
rhs: StatePartIndex<StatePartKindBigSlots>,
} => {
let lhs = &state.big_slots[lhs];
let rhs = &state.big_slots[rhs];
let value = BigInt::from(lhs == rhs);
state.big_slots[dest] = value;
next!();
}
CmpNe {
#[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
lhs: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
rhs: StatePartIndex<StatePartKindBigSlots>,
} => {
let lhs = &state.big_slots[lhs];
let rhs = &state.big_slots[rhs];
let value = BigInt::from(lhs != rhs);
state.big_slots[dest] = value;
next!();
}
CmpLt {
#[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
lhs: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
rhs: StatePartIndex<StatePartKindBigSlots>,
} => {
let lhs = &state.big_slots[lhs];
let rhs = &state.big_slots[rhs];
let value = BigInt::from(lhs < rhs);
state.big_slots[dest] = value;
next!();
}
CmpLe {
#[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
lhs: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
rhs: StatePartIndex<StatePartKindBigSlots>,
} => {
let lhs = &state.big_slots[lhs];
let rhs = &state.big_slots[rhs];
let value = BigInt::from(lhs <= rhs);
state.big_slots[dest] = value;
next!();
}
ReduceBitXor {
#[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>,
#[kind = Input]
src: StatePartIndex<StatePartKindBigSlots>,
#[kind = Immediate]
input_width: usize,
} => {
let src = &state.big_slots[src];
let src = src & &*bigint_mask(input_width);
let value = BigInt::from(
src.to_biguint().expect("known to be non-negative").count_ones() % 2,
);
state.big_slots[dest] = value;
next!();
}
Const {
#[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>,
#[kind = Immediate]
value: Interned<BigInt>,
} => {
state.big_slots[dest].clone_from(&value);
next!();
}
ConstSmall {
#[kind = Output]
dest: StatePartIndex<StatePartKindSmallSlots>,
#[kind = Immediate]
value: SmallUInt,
} => {
state.small_slots[dest] = value;
next!();
}
Branch {
#[kind = BranchTarget]
target: usize,
} => {
branch!(target);
}
BranchIfZero {
#[kind = BranchTarget]
target: usize,
#[kind = Input]
value: StatePartIndex<StatePartKindBigSlots>,
} => {
if state.big_slots[value].is_zero() {
branch!(target);
} else {
next!();
}
}
BranchIfNonZero {
#[kind = BranchTarget]
target: usize,
#[kind = Input]
value: StatePartIndex<StatePartKindBigSlots>,
} => {
if state.big_slots[value].is_zero() {
next!();
} else {
branch!(target);
}
}
BranchIfSmallZero {
#[kind = BranchTarget]
target: usize,
#[kind = Input]
value: StatePartIndex<StatePartKindSmallSlots>,
} => {
if state.small_slots[value] == 0 {
branch!(target);
} else {
next!();
}
}
BranchIfSmallNonZero {
#[kind = BranchTarget]
target: usize,
#[kind = Input]
value: StatePartIndex<StatePartKindSmallSlots>,
} => {
if state.small_slots[value] == 0 {
next!();
} else {
branch!(target);
}
}
BranchIfSmallNeImmediate {
#[kind = BranchTarget]
target: usize,
#[kind = Input]
lhs: StatePartIndex<StatePartKindSmallSlots>,
#[kind = Immediate]
rhs: SmallUInt,
} => {
if state.small_slots[lhs] != rhs {
branch!(target);
} else {
next!();
}
}
MemoryReadUInt {
#[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>,
#[kind = Memory]
memory: StatePartIndex<StatePartKindMemories>,
#[kind = Input]
addr: StatePartIndex<StatePartKindSmallSlots>,
#[kind = Immediate]
stride: usize,
#[kind = Immediate]
start: usize,
#[kind = Immediate]
width: usize,
} => {
let addr = state.small_slots[addr];
state.big_slots[dest] = memory_read_big::<UInt>(&mut state.memories[memory].data, addr, stride, start, width).unwrap_or_default();
next!();
}
MemoryReadSInt {
#[kind = Output]
dest: StatePartIndex<StatePartKindBigSlots>,
#[kind = Memory]
memory: StatePartIndex<StatePartKindMemories>,
#[kind = Input]
addr: StatePartIndex<StatePartKindSmallSlots>,
#[kind = Immediate]
stride: usize,
#[kind = Immediate]
start: usize,
#[kind = Immediate]
width: usize,
} => {
let addr = state.small_slots[addr];
state.big_slots[dest] = memory_read_big::<SInt>(&mut state.memories[memory].data, addr, stride, start, width).unwrap_or_default();
next!();
}
MemoryWriteUInt {
#[kind = Input]
value: StatePartIndex<StatePartKindBigSlots>,
#[kind = Memory]
memory: StatePartIndex<StatePartKindMemories>,
#[kind = Input]
addr: StatePartIndex<StatePartKindSmallSlots>,
#[kind = Immediate]
stride: usize,
#[kind = Immediate]
start: usize,
#[kind = Immediate]
width: usize,
} => {
let addr = state.small_slots[addr];
memory_write_big::<UInt>(&mut state.memories[memory].data, addr, stride, start, width, &mut state.big_slots[value]);
state.log_memory_write(memory, addr);
next!();
}
MemoryWriteSInt {
#[kind = Input]
value: StatePartIndex<StatePartKindBigSlots>,
#[kind = Memory]
memory: StatePartIndex<StatePartKindMemories>,
#[kind = Input]
addr: StatePartIndex<StatePartKindSmallSlots>,
#[kind = Immediate]
stride: usize,
#[kind = Immediate]
start: usize,
#[kind = Immediate]
width: usize,
} => {
let addr = state.small_slots[addr];
memory_write_big::<SInt>(&mut state.memories[memory].data, addr, stride, start, width, &mut state.big_slots[value]);
state.log_memory_write(memory, addr);
next!();
}
Assert {
#[kind = Input]
clk_triggered: StatePartIndex<StatePartKindSmallSlots>,
#[kind = Input]
pred: StatePartIndex<StatePartKindSmallSlots>,
#[kind = Immediate]
assert_index: usize,
} => {
if state.small_slots[clk_triggered] != 0 && state.small_slots[pred] == 0 {
state.assert_failed(assert_index);
}
next!();
}
Return => {
break RunResult::Return(());
}
}