WIP: added running generators to settle loop

This commit is contained in:
Jacob Lifshay 2025-03-20 03:50:23 -07:00
parent b4650f1bff
commit f378b9a1d2
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ

View file

@ -55,13 +55,17 @@ use petgraph::{
use std::{
any::Any,
borrow::Cow,
cell::RefCell,
collections::BTreeSet,
fmt,
future::{Future, IntoFuture},
marker::PhantomData,
mem,
ops::IndexMut,
pin::Pin,
rc::Rc,
sync::Arc,
task::Poll,
};
mod interpreter;
@ -6755,10 +6759,35 @@ impl SimulationModuleState {
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum WaitTarget {
/// Settle is less than Instant
Settle,
Instant(SimInstant),
}
impl PartialOrd for WaitTarget {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for WaitTarget {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
match (self, other) {
(WaitTarget::Settle, WaitTarget::Settle) => std::cmp::Ordering::Equal,
(WaitTarget::Settle, WaitTarget::Instant(_)) => std::cmp::Ordering::Less,
(WaitTarget::Instant(_), WaitTarget::Settle) => std::cmp::Ordering::Greater,
(WaitTarget::Instant(l), WaitTarget::Instant(r)) => l.cmp(r),
}
}
}
struct SimulationExternModuleState {
module_state: SimulationModuleState,
sim: ExternModuleSimulation<Bundle>,
running_generator: Option<Box<dyn Future<Output = ()> + 'static>>,
running_generator: Option<Pin<Box<dyn Future<Output = ()> + 'static>>>,
wait_target: Option<WaitTarget>,
}
impl fmt::Debug for SimulationExternModuleState {
@ -6767,6 +6796,7 @@ impl fmt::Debug for SimulationExternModuleState {
module_state,
sim,
running_generator,
wait_target,
} = self;
f.debug_struct("SimulationExternModuleState")
.field("module_state", module_state)
@ -6775,6 +6805,7 @@ impl fmt::Debug for SimulationExternModuleState {
"running_generator",
&running_generator.as_ref().map(|_| DebugAsDisplay("...")),
)
.field("wait_target", wait_target)
.finish()
}
}
@ -6782,7 +6813,7 @@ impl fmt::Debug for SimulationExternModuleState {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
enum WhichModule {
Main,
Extern { index: usize },
Extern { module_index: usize },
}
struct ReadBitFn {
@ -6855,11 +6886,19 @@ impl MaybeNeedsSettleFn<&'_ mut interpreter::State> for ReadFn {
}
}
struct GeneratorWaker;
impl std::task::Wake for GeneratorWaker {
fn wake(self: Arc<Self>) {
panic!("can't await other kinds of futures in function passed to ExternalModuleSimulation");
}
}
struct SimulationImpl {
state: interpreter::State,
io: Expr<Bundle>,
main_module: SimulationModuleState,
extern_modules: Vec<SimulationExternModuleState>,
extern_modules: Box<[SimulationExternModuleState]>,
needs_settle: bool,
trace_decls: TraceModule,
traces: SimTraces<Box<[SimTrace<SimTraceKind, BitVec>]>>,
@ -6868,6 +6907,7 @@ struct SimulationImpl {
instant: SimInstant,
clocks_triggered: Interned<[StatePartIndex<StatePartKindSmallSlots>]>,
breakpoints: Option<BreakpointsSet>,
generator_waker: std::task::Waker,
}
impl fmt::Debug for SimulationImpl {
@ -6891,6 +6931,7 @@ impl SimulationImpl {
instant,
clocks_triggered,
breakpoints: _,
generator_waker: _,
} = self;
f.debug_struct("Simulation")
.field("state", state)
@ -6908,6 +6949,8 @@ impl SimulationImpl {
}
fn new(compiled: Compiled<Bundle>) -> Self {
let io_target = Target::from(compiled.io);
// TODO: add extern_modules
let extern_modules: Box<[SimulationExternModuleState]> = Box::new([]);
Self {
state: State::new(compiled.insns),
io: compiled.io.to_expr(),
@ -6928,8 +6971,7 @@ impl SimulationImpl {
)
}),
),
// TODO: add extern_modules
extern_modules: vec![],
extern_modules,
needs_settle: true,
trace_decls: compiled.base_module.trace_decls,
traces: SimTraces(Box::from_iter(compiled.traces.0.iter().map(
@ -6948,6 +6990,7 @@ impl SimulationImpl {
instant: SimInstant::START,
clocks_triggered: compiled.clocks_triggered,
breakpoints: None,
generator_waker: Arc::new(GeneratorWaker).into(),
}
}
fn write_traces<const ONLY_IF_CHANGED: bool>(
@ -7086,10 +7129,11 @@ impl SimulationImpl {
}
}
#[track_caller]
fn advance_time(&mut self, duration: SimDuration) {
self.settle();
self.instant += duration;
self.for_each_trace_writer_storing_error(|this, mut trace_writer_state| {
fn advance_time(this: &Rc<RefCell<Self>>, duration: SimDuration) {
Self::settle(this);
let mut this = this.borrow_mut();
this.instant += duration;
this.for_each_trace_writer_storing_error(|this, mut trace_writer_state| {
match &mut trace_writer_state {
TraceWriterState::Decls(_) | TraceWriterState::Init(_) => unreachable!(),
TraceWriterState::Running(trace_writer) => {
@ -7100,31 +7144,130 @@ impl SimulationImpl {
Ok(trace_writer_state)
});
}
#[must_use]
fn yield_advance_time_or_settle(
this: Rc<RefCell<Self>>,
module_index: usize,
duration: Option<SimDuration>,
) -> impl Future<Output = ()> + 'static {
struct MyGenerator {
sim: Rc<RefCell<SimulationImpl>>,
yielded_at_all: bool,
module_index: usize,
target: WaitTarget,
}
impl Future for MyGenerator {
type Output = ();
fn poll(
mut self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> Poll<Self::Output> {
let this = &mut *self;
let yielded_at_all = mem::replace(&mut this.yielded_at_all, true);
let mut sim = this.sim.borrow_mut();
let sim = &mut *sim;
assert!(cx.waker().will_wake(&sim.generator_waker), "can't use ExternModuleSimulationState's methods outside of ExternModuleSimulation");
if let WaitTarget::Instant(target) = this.target {
if target < sim.instant {
this.target = WaitTarget::Settle;
} else if yielded_at_all && target == sim.instant {
this.target = WaitTarget::Settle;
}
}
if let WaitTarget::Settle = this.target {
if yielded_at_all {
return Poll::Ready(());
}
}
let wait_target = sim.extern_modules[this.module_index]
.wait_target
.get_or_insert(this.target);
*wait_target = (*wait_target).min(this.target);
Poll::Pending
}
}
let target = duration.map_or(WaitTarget::Settle, |duration| {
WaitTarget::Instant(this.borrow().instant + duration)
});
MyGenerator {
sim: this,
yielded_at_all: false,
module_index,
target,
}
}
fn get_ready_external_modules(&mut self, run_list: &mut Vec<usize>) -> Option<WaitTarget> {
run_list.clear();
let mut iter = self.extern_modules.iter_mut().enumerate().filter_map(
|(module_index, extern_module)| Some((module_index, extern_module.wait_target?)),
);
let (first_module_index, mut wait_target) = iter.next()?;
run_list.push(first_module_index);
for (next_module_index, next_wait_target) in iter {
match next_wait_target.cmp(&wait_target) {
std::cmp::Ordering::Less => run_list.clear(),
std::cmp::Ordering::Equal => {}
std::cmp::Ordering::Greater => continue,
}
run_list.push(next_module_index);
wait_target = next_wait_target;
}
Some(wait_target)
}
#[track_caller]
fn settle(&mut self) {
fn settle(this_ref: &Rc<RefCell<Self>>) {
let mut this = this_ref.borrow_mut();
assert!(
self.main_module.uninitialized_ios.is_empty(),
this.main_module.uninitialized_ios.is_empty(),
"didn't initialize all inputs",
);
let mut run_list = Vec::new();
let generator_waker = this.generator_waker.clone();
for _ in 0..100000 {
if !self.needs_settle {
if !this.needs_settle {
if let Some(wait_target) = this.get_ready_external_modules(&mut run_list) {
if wait_target > WaitTarget::Instant(this.instant) {
return;
}
for module_index in run_list.drain(..) {
this.extern_modules[module_index].wait_target = None;
let Some(mut generator) =
this.extern_modules[module_index].running_generator.take()
else {
continue;
};
drop(this);
let generator = match generator
.as_mut()
.poll(&mut std::task::Context::from_waker(&generator_waker))
{
Poll::Ready(()) => None,
Poll::Pending => Some(generator),
};
this = this_ref.borrow_mut();
this.extern_modules[module_index].running_generator = generator;
}
continue;
}
return;
}
self.state.setup_call(0);
if self.breakpoints.is_some() {
this.state.setup_call(0);
if this.breakpoints.is_some() {
loop {
match self
let this = &mut *this;
match this
.state
.run(self.breakpoints.as_mut().expect("just checked"))
.run(this.breakpoints.as_mut().expect("just checked"))
{
RunResult::Break(break_action) => {
println!(
"hit breakpoint at:\n{:?}",
self.state.debug_insn_at(self.state.pc),
this.state.debug_insn_at(this.state.pc),
);
match break_action {
BreakAction::DumpStateAndContinue => {
println!("{self:#?}");
println!("{this:#?}");
}
BreakAction::Continue => {}
}
@ -7133,21 +7276,21 @@ impl SimulationImpl {
}
}
} else {
let RunResult::Return(()) = self.state.run(());
let RunResult::Return(()) = this.state.run(());
}
if self.main_module.did_initial_settle {
self.read_traces::<false>();
if this.main_module.did_initial_settle {
this.read_traces::<false>();
} else {
self.read_traces::<true>();
this.read_traces::<true>();
}
self.state.memory_write_log.sort_unstable();
self.state.memory_write_log.dedup();
self.main_module.did_initial_settle = true;
self.needs_settle = self
this.state.memory_write_log.sort_unstable();
this.state.memory_write_log.dedup();
this.main_module.did_initial_settle = true;
this.needs_settle = this
.clocks_triggered
.iter()
.any(|i| self.state.small_slots[*i] != 0);
self.for_each_trace_writer_storing_error(|this, trace_writer_state| {
.any(|i| this.state.small_slots[*i] != 0);
this.for_each_trace_writer_storing_error(|this, trace_writer_state| {
Ok(match trace_writer_state {
TraceWriterState::Decls(trace_writer_decls) => TraceWriterState::Running(
this.init_trace_writer(trace_writer_decls.write_decls(
@ -7165,20 +7308,22 @@ impl SimulationImpl {
TraceWriterState::Errored(e) => TraceWriterState::Errored(e),
})
});
self.state.memory_write_log.clear();
this.state.memory_write_log.clear();
}
panic!("settle(): took too many steps");
}
fn get_module(&self, which_module: WhichModule) -> &SimulationModuleState {
match which_module {
WhichModule::Main => &self.main_module,
WhichModule::Extern { index } => &self.extern_modules[index].module_state,
WhichModule::Extern { module_index } => &self.extern_modules[module_index].module_state,
}
}
fn get_module_mut(&mut self, which_module: WhichModule) -> &mut SimulationModuleState {
match which_module {
WhichModule::Main => &mut self.main_module,
WhichModule::Extern { index } => &mut self.extern_modules[index].module_state,
WhichModule::Extern { module_index } => {
&mut self.extern_modules[module_index].module_state
}
}
}
#[track_caller]
@ -7374,14 +7519,31 @@ impl SimulationImpl {
);
}
#[track_caller]
fn settle_if_needed<F, O>(&mut self, v: MaybeNeedsSettle<F, O>) -> O
fn settle_if_needed<F, O>(this_ref: &Rc<RefCell<Self>>, v: MaybeNeedsSettle<F, O>) -> O
where
for<'a> F: MaybeNeedsSettleFn<&'a mut interpreter::State, Output = O>,
{
match v {
MaybeNeedsSettle::NeedsSettle(v) => {
self.settle();
v.call(&mut self.state)
Self::settle(this_ref);
v.call(&mut this_ref.borrow_mut().state)
}
MaybeNeedsSettle::NoSettleNeeded(v) => v,
}
}
#[track_caller]
async fn yield_settle_if_needed<F, O>(
this_ref: &Rc<RefCell<Self>>,
module_index: usize,
v: MaybeNeedsSettle<F, O>,
) -> O
where
for<'a> F: MaybeNeedsSettleFn<&'a mut interpreter::State, Output = O>,
{
match v {
MaybeNeedsSettle::NeedsSettle(v) => {
Self::yield_advance_time_or_settle(this_ref.clone(), module_index, None).await;
v.call(&mut this_ref.borrow_mut().state)
}
MaybeNeedsSettle::NoSettleNeeded(v) => v,
}
@ -7455,17 +7617,17 @@ impl SimulationImpl {
self.trace_writers = trace_writers;
retval
}
fn close(mut self) -> std::io::Result<()> {
if self.main_module.did_initial_settle {
self.settle();
fn close(this: Rc<RefCell<Self>>) -> std::io::Result<()> {
if this.borrow().main_module.did_initial_settle {
Self::settle(&this);
}
self.close_all_trace_writers()
this.borrow_mut().close_all_trace_writers()
}
fn flush_traces(&mut self) -> std::io::Result<()> {
if self.main_module.did_initial_settle {
self.settle();
fn flush_traces(this_ref: &Rc<RefCell<Self>>) -> std::io::Result<()> {
if this_ref.borrow().main_module.did_initial_settle {
Self::settle(this_ref);
}
self.for_each_trace_writer_getting_error(
this_ref.borrow_mut().for_each_trace_writer_getting_error(
|this, trace_writer: TraceWriterState<DynTraceWriterDecls>| match trace_writer {
TraceWriterState::Decls(v) => {
let mut v = v.write_decls(
@ -7499,7 +7661,7 @@ impl Drop for SimulationImpl {
}
pub struct Simulation<T: BundleType> {
sim_impl: SimulationImpl,
sim_impl: Rc<RefCell<SimulationImpl>>,
io: Expr<T>,
}
@ -7546,24 +7708,113 @@ impl<K: fmt::Debug, V: fmt::Debug> fmt::Debug for SortedMapDebug<'_, K, V> {
impl<T: BundleType> fmt::Debug for Simulation<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { sim_impl, io } = self;
sim_impl.debug_fmt(Some(io), f)
sim_impl.borrow().debug_fmt(Some(io), f)
}
}
macro_rules! impl_simulation_methods {
(async_await = ($($async:tt, $await:tt)?)) => {
#[track_caller]
pub $($async)? fn read_bool_or_int<I: BoolOrIntType>(&mut self, io: Expr<I>) -> I::Value {
let retval = self
.sim_impl
.borrow_mut()
.read_bool_or_int(io, WhichModule::Main);
self.settle_if_needed(retval)$(.$await)?
}
#[track_caller]
pub $($async)? fn write_bool_or_int<I: BoolOrIntType>(
&mut self,
io: Expr<I>,
value: impl ToExpr<Type = I>,
) {
let value = value.to_expr();
assert_eq!(Expr::ty(io), Expr::ty(value), "type mismatch");
let value = value
.to_literal_bits()
.expect("the value that is being written to an input must be a literal");
self.sim_impl.borrow_mut().write_bool_or_int(
io,
I::bits_to_value(Cow::Borrowed(&value)),
WhichModule::Main,
);
}
#[track_caller]
pub $($async)? fn write_clock(&mut self, io: Expr<Clock>, value: bool) {
self.sim_impl
.borrow_mut()
.write_bit(Expr::canonical(io), value, WhichModule::Main);
}
#[track_caller]
pub $($async)? fn read_clock(&mut self, io: Expr<Clock>) -> bool {
let retval = self
.sim_impl
.borrow_mut()
.read_bit(Expr::canonical(io), WhichModule::Main);
self.settle_if_needed(retval)$(.$await)?
}
#[track_caller]
pub $($async)? fn write_bool(&mut self, io: Expr<Bool>, value: bool) {
self.sim_impl
.borrow_mut()
.write_bit(Expr::canonical(io), value, WhichModule::Main);
}
#[track_caller]
pub $($async)? fn read_bool(&mut self, io: Expr<Bool>) -> bool {
let retval = self
.sim_impl
.borrow_mut()
.read_bit(Expr::canonical(io), WhichModule::Main);
self.settle_if_needed(retval)$(.$await)?
}
#[track_caller]
pub $($async)? fn write_reset<R: ResetType>(&mut self, io: Expr<R>, value: bool) {
self.sim_impl
.borrow_mut()
.write_bit(Expr::canonical(io), value, WhichModule::Main);
}
#[track_caller]
pub $($async)? fn read_reset<R: ResetType>(&mut self, io: Expr<R>) -> bool {
let retval = self
.sim_impl
.borrow_mut()
.read_bit(Expr::canonical(io), WhichModule::Main);
self.settle_if_needed(retval)$(.$await)?
}
#[track_caller]
pub $($async)? fn read<IO: Type>(&mut self, io: Expr<IO>) -> SimValue<IO> {
let retval = self
.sim_impl
.borrow_mut()
.read(Expr::canonical(io), WhichModule::Main);
SimValue::from_canonical(self.settle_if_needed(retval)$(.$await)?)
}
#[track_caller]
pub $($async)? fn write<IO: Type, V: ToSimValue<IO>>(&mut self, io: Expr<IO>, value: V) {
self.sim_impl.borrow_mut().write(
Expr::canonical(io),
value.into_sim_value(Expr::ty(io)).into_canonical(),
WhichModule::Main,
);
}
};
}
impl<T: BundleType> Simulation<T> {
pub fn new(module: Interned<Module<T>>) -> Self {
Self::from_compiled(Compiled::new(module))
}
pub fn add_trace_writer<W: TraceWriterDecls>(&mut self, writer: W) {
self.sim_impl
.borrow_mut()
.trace_writers
.push(TraceWriterState::Decls(DynTraceWriterDecls::new(writer)));
}
pub fn flush_traces(&mut self) -> std::io::Result<()> {
self.sim_impl.flush_traces()
SimulationImpl::flush_traces(&self.sim_impl)
}
pub fn close(self) -> std::io::Result<()> {
self.sim_impl.close()
SimulationImpl::close(self.sim_impl)
}
pub fn canonical(self) -> Simulation<Bundle> {
let Self { sim_impl, io } = self;
@ -7586,92 +7837,29 @@ impl<T: BundleType> Simulation<T> {
let sim_impl = SimulationImpl::new(compiled.canonical());
Self {
io: Expr::from_bundle(sim_impl.io),
sim_impl,
sim_impl: Rc::new(RefCell::new(sim_impl)),
}
}
#[track_caller]
pub fn settle(&mut self) {
self.sim_impl.settle();
SimulationImpl::settle(&self.sim_impl);
}
#[track_caller]
pub fn advance_time(&mut self, duration: SimDuration) {
self.sim_impl.advance_time(duration);
SimulationImpl::advance_time(&self.sim_impl, duration);
}
#[track_caller]
pub fn read_bool_or_int<I: BoolOrIntType>(&mut self, io: Expr<I>) -> I::Value {
let retval = self.sim_impl.read_bool_or_int(io, WhichModule::Main);
self.sim_impl.settle_if_needed(retval)
}
#[track_caller]
pub fn write_bool_or_int<I: BoolOrIntType>(
&mut self,
io: Expr<I>,
value: impl ToExpr<Type = I>,
) {
let value = value.to_expr();
assert_eq!(Expr::ty(io), Expr::ty(value), "type mismatch");
let value = value
.to_literal_bits()
.expect("the value that is being written to an input must be a literal");
self.sim_impl.write_bool_or_int(
io,
I::bits_to_value(Cow::Borrowed(&value)),
WhichModule::Main,
);
}
#[track_caller]
pub fn write_clock(&mut self, io: Expr<Clock>, value: bool) {
self.sim_impl
.write_bit(Expr::canonical(io), value, WhichModule::Main);
}
#[track_caller]
pub fn read_clock(&mut self, io: Expr<Clock>) -> bool {
let retval = self
.sim_impl
.read_bit(Expr::canonical(io), WhichModule::Main);
self.sim_impl.settle_if_needed(retval)
}
#[track_caller]
pub fn write_bool(&mut self, io: Expr<Bool>, value: bool) {
self.sim_impl
.write_bit(Expr::canonical(io), value, WhichModule::Main);
}
#[track_caller]
pub fn read_bool(&mut self, io: Expr<Bool>) -> bool {
let retval = self
.sim_impl
.read_bit(Expr::canonical(io), WhichModule::Main);
self.sim_impl.settle_if_needed(retval)
}
#[track_caller]
pub fn write_reset<R: ResetType>(&mut self, io: Expr<R>, value: bool) {
self.sim_impl
.write_bit(Expr::canonical(io), value, WhichModule::Main);
}
#[track_caller]
pub fn read_reset<R: ResetType>(&mut self, io: Expr<R>) -> bool {
let retval = self
.sim_impl
.read_bit(Expr::canonical(io), WhichModule::Main);
self.sim_impl.settle_if_needed(retval)
}
#[track_caller]
pub fn read<IO: Type>(&mut self, io: Expr<IO>) -> SimValue<IO> {
let retval = self.sim_impl.read(Expr::canonical(io), WhichModule::Main);
SimValue::from_canonical(self.sim_impl.settle_if_needed(retval))
}
#[track_caller]
pub fn write<IO: Type, V: ToSimValue<IO>>(&mut self, io: Expr<IO>, value: V) {
self.sim_impl.write(
Expr::canonical(io),
value.into_sim_value(Expr::ty(io)).into_canonical(),
WhichModule::Main,
);
fn settle_if_needed<F, O>(&mut self, v: MaybeNeedsSettle<F, O>) -> O
where
for<'a> F: MaybeNeedsSettleFn<&'a mut interpreter::State, Output = O>,
{
SimulationImpl::settle_if_needed(&self.sim_impl, v)
}
impl_simulation_methods!(async_await = ());
#[doc(hidden)]
/// This is explicitly unstable and may be changed/removed at any time
pub fn set_breakpoints_unstable(&mut self, pcs: HashSet<usize>, trace: bool) {
self.sim_impl.breakpoints = Some(BreakpointsSet {
self.sim_impl.borrow_mut().breakpoints = Some(BreakpointsSet {
last_was_break: false,
set: pcs,
trace,
@ -7679,22 +7867,74 @@ impl<T: BundleType> Simulation<T> {
}
}
#[derive(Debug)]
pub struct ExternModuleSimulationState<T: BundleType> {
sim_impl: Rc<RefCell<SimulationImpl>>,
module_index: usize,
io_ty: T,
}
impl<T: BundleType> fmt::Debug for ExternModuleSimulationState<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
sim_impl: _,
module_index,
io_ty,
} = self;
f.debug_struct("ExternModuleSimulationState")
.field("sim_impl", &DebugAsDisplay("..."))
.field("module_index", module_index)
.field("io_ty", io_ty)
.finish()
}
}
impl<T: BundleType> ExternModuleSimulationState<T> {
pub fn canonical(self) -> ExternModuleSimulationState<Bundle> {
let Self {
sim_impl,
module_index,
io_ty,
} = self;
ExternModuleSimulationState {
io_ty: Bundle::from_canonical(self.io_ty.canonical()),
sim_impl,
module_index,
io_ty: Bundle::from_canonical(io_ty.canonical()),
}
}
pub fn from_canonical(sim: ExternModuleSimulationState<Bundle>) -> Self {
let ExternModuleSimulationState {
sim_impl,
module_index,
io_ty,
} = sim;
Self {
io_ty: T::from_canonical(sim.io_ty.canonical()),
sim_impl,
module_index,
io_ty: T::from_canonical(io_ty.canonical()),
}
}
#[track_caller]
pub async fn settle(&mut self) {
SimulationImpl::yield_advance_time_or_settle(self.sim_impl.clone(), self.module_index, None)
.await
}
#[track_caller]
pub async fn advance_time(&mut self, duration: SimDuration) {
SimulationImpl::yield_advance_time_or_settle(
self.sim_impl.clone(),
self.module_index,
Some(duration),
)
.await
}
#[track_caller]
async fn settle_if_needed<F, O>(&mut self, v: MaybeNeedsSettle<F, O>) -> O
where
for<'a> F: MaybeNeedsSettleFn<&'a mut interpreter::State, Output = O>,
{
SimulationImpl::yield_settle_if_needed(&self.sim_impl, self.module_index, v).await
}
impl_simulation_methods!(async_await = (async, await));
}
pub trait ExternModuleSimGenerator: