Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions crates/simulator/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub mod aot_c;
#[cfg(not(target_family = "wasm"))]
pub mod cranelift;
pub mod inst;
pub mod late;
pub mod registry;
pub mod validate;

Expand Down Expand Up @@ -116,6 +117,17 @@ pub struct ChunkArtifact {
/// still distinguishes chunks with different code. `None` before a stamp
/// (non-`dut_reuse` path); `Debug` then falls back to the address.
pub content_fp: Option<u128>,
/// Full-coverage read/write dependency sets of the compiled statements,
/// captured at compile time for the incremental (change-driven) settle
/// plan. `None` unless `VERYL_INCR=1` (see `ir::incremental`). Excluded
/// from `Debug`/`Hash`: derived deterministically from the statements the
/// fingerprint already identifies.
pub deps: Option<Arc<crate::ir::incremental::ChunkDeps>>,
/// The compiled code carries per-sub-block guards reading the mask slot
/// in the write-log header (see `ir::incremental::sub_split_len`); the
/// incremental settle then passes a requested-sub mask before the call.
/// Excluded from `Debug`/`Hash` like `deps` (derived from env + stmts).
pub sub_guarded: bool,
}

impl std::fmt::Debug for ChunkArtifact {
Expand Down
2 changes: 2 additions & 0 deletions crates/simulator/src/backend/aot_c/emit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8167,6 +8167,8 @@ mod tests {
func: stub,
keepalive: None,
content_fp: None,
deps: None,
sub_guarded: false,
})
}

Expand Down
4 changes: 4 additions & 0 deletions crates/simulator/src/backend/cranelift.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ impl Backend for CraneliftBackend {
// unmapped); only the fallback private mapping does.
keepalive: mmap.map(|m| Box::new(m) as Box<dyn Send + Sync>),
content_fp: None,
deps: None,
// Mirrors the guard condition in `build_binary_inner`.
sub_guarded: crate::ir::incremental::enabled()
&& crate::ir::incremental::sub_split_len(stmts.len()).is_some(),
}))
}
}
70 changes: 67 additions & 3 deletions crates/simulator/src/backend/cranelift/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,8 +366,8 @@ pub fn alloc_wide_slot(builder: &mut FunctionBuilder, nb: usize) -> Value {
builder.ins().stack_addr(I64, slot, 0)
}

/// Compile a chunk into a native function. Returns `(func, mmap)`;
/// the caller must keep `mmap` alive for as long as `func` is callable
/// Compile a chunk into a native function. Returns `(func, mmap)`; the
/// caller must keep `mmap` alive for as long as `func` is callable
/// (typically by wrapping both in `ChunkArtifact`).
pub fn build_binary(
config: &Config,
Expand Down Expand Up @@ -527,6 +527,42 @@ fn build_binary_inner(
let zero_hi = builder.ins().iconst(I64, 0);
let zero_128 = builder.ins().iconcat(zero_lo, zero_hi);

// Sub-block guard (incremental settle): split the chunk into the same
// groups as the plan's per-sub dependency sets and gate each group on a
// bit of the caller-provided mask. The mask travels through a slot in
// the write-log header: loaded once in the prologue and immediately
// cleared, so a nested CompiledBlock (compiled with its own guards)
// sees 0 — "run whole" — instead of consuming the outer chunk's mask,
// and so every non-incremental caller runs the chunk whole too.
let sub_len = if crate::ir::incremental::enabled() {
crate::ir::incremental::sub_split_len(proto.len())
} else {
None
};
// Store elimination forwards a skipped store through the load cache;
// a guarded (skippable) region boundary breaks that forwarding, so
// keep every store when guards are present.
let store_elim = if sub_len.is_some() {
HashSet::default()
} else {
store_elim
};
let sub_mask_eff = sub_len.map(|_| {
use crate::ir::write_log::WRITE_LOG_OFFSET_INCR_SUB_MASK;
let flags = MemFlagsData::trusted();
let raw = builder
.ins()
.load(I32, flags, log_buf, WRITE_LOG_OFFSET_INCR_SUB_MASK);
let zero_i32 = builder.ins().iconst(I32, 0);
builder
.ins()
.store(flags, zero_i32, log_buf, WRITE_LOG_OFFSET_INCR_SUB_MASK);
// 0 = no request recorded: run every sub-block.
let is_zero = builder.ins().icmp_imm_s(IntCC::Equal, raw, 0);
let all = builder.ins().iconst(I32, 0xff);
builder.ins().select(is_zero, all, raw)
});

let mut cranelift_context = Context {
use_4state: config.use_4state,
ff_values,
Expand Down Expand Up @@ -559,8 +595,32 @@ fn build_binary_inner(
}

let len = proto.len();
// Merge target of the currently open guarded region (jump destination
// for both the skip branch and the region's fall-through).
let mut pending_merge: Option<cranelift::prelude::Block> = None;
for (i, x) in proto.iter().enumerate() {
let is_last = (i + 1) == len;
if let (Some(s), Some(m_eff)) = (sub_len, sub_mask_eff)
&& i % s == 0
{
if let Some(mb) = pending_merge.take() {
builder.ins().jump(mb, &[]);
builder.switch_to_block(mb);
}
let si = (i / s) as i64;
let body = builder.create_block();
let merge = builder.create_block();
let shifted = builder.ins().ushr_imm_s(m_eff, si);
let bit = builder.ins().band_imm_s(shifted, 1);
builder.ins().brif(bit, body, &[], merge, &[]);
builder.switch_to_block(body);
pending_merge = Some(merge);
// Values cached in earlier (possibly skipped) regions must not
// be forwarded across the guard.
cranelift_context.load_cache.clear();
}
// `is_last` lets the final statement return directly, bypassing any
// merge block — incompatible with the guard's region merge.
let is_last = (i + 1) == len && sub_len.is_none();
x.build_binary(&mut cranelift_context, &mut builder, is_last)?;

// Belady: while over capacity, evict the entry whose next read
Expand Down Expand Up @@ -589,6 +649,10 @@ fn build_binary_inner(
}
}

if let Some(mb) = pending_merge.take() {
builder.ins().jump(mb, &[]);
builder.switch_to_block(mb);
}
builder.ins().return_(&[]);
builder.seal_all_blocks();
builder.finalize(isa.frontend_config());
Expand Down
Loading
Loading