@@ -366,8 +366,8 @@ pub fn alloc_wide_slot(builder: &mut FunctionBuilder, nb: usize) -> Value {
366366 builder. ins ( ) . stack_addr ( I64 , slot, 0 )
367367}
368368
369- /// Compile a chunk into a native function. Returns `(func, mmap)`;
370- /// the caller must keep `mmap` alive for as long as `func` is callable
369+ /// Compile a chunk into a native function. Returns `(func, mmap)`; the
370+ /// caller must keep `mmap` alive for as long as `func` is callable
371371/// (typically by wrapping both in `ChunkArtifact`).
372372pub fn build_binary (
373373 config : & Config ,
@@ -527,6 +527,42 @@ fn build_binary_inner(
527527 let zero_hi = builder. ins ( ) . iconst ( I64 , 0 ) ;
528528 let zero_128 = builder. ins ( ) . iconcat ( zero_lo, zero_hi) ;
529529
530+ // Sub-block guard (incremental settle): split the chunk into the same
531+ // groups as the plan's per-sub dependency sets and gate each group on a
532+ // bit of the caller-provided mask. The mask travels through a slot in
533+ // the write-log header: loaded once in the prologue and immediately
534+ // cleared, so a nested CompiledBlock (compiled with its own guards)
535+ // sees 0 — "run whole" — instead of consuming the outer chunk's mask,
536+ // and so every non-incremental caller runs the chunk whole too.
537+ let sub_len = if crate :: ir:: incremental:: enabled ( ) {
538+ crate :: ir:: incremental:: sub_split_len ( proto. len ( ) )
539+ } else {
540+ None
541+ } ;
542+ // Store elimination forwards a skipped store through the load cache;
543+ // a guarded (skippable) region boundary breaks that forwarding, so
544+ // keep every store when guards are present.
545+ let store_elim = if sub_len. is_some ( ) {
546+ HashSet :: default ( )
547+ } else {
548+ store_elim
549+ } ;
550+ let sub_mask_eff = sub_len. map ( |_| {
551+ use crate :: ir:: write_log:: WRITE_LOG_OFFSET_INCR_SUB_MASK ;
552+ let flags = MemFlagsData :: trusted ( ) ;
553+ let raw = builder
554+ . ins ( )
555+ . load ( I32 , flags, log_buf, WRITE_LOG_OFFSET_INCR_SUB_MASK ) ;
556+ let zero_i32 = builder. ins ( ) . iconst ( I32 , 0 ) ;
557+ builder
558+ . ins ( )
559+ . store ( flags, zero_i32, log_buf, WRITE_LOG_OFFSET_INCR_SUB_MASK ) ;
560+ // 0 = no request recorded: run every sub-block.
561+ let is_zero = builder. ins ( ) . icmp_imm_s ( IntCC :: Equal , raw, 0 ) ;
562+ let all = builder. ins ( ) . iconst ( I32 , 0xff ) ;
563+ builder. ins ( ) . select ( is_zero, all, raw)
564+ } ) ;
565+
530566 let mut cranelift_context = Context {
531567 use_4state : config. use_4state ,
532568 ff_values,
@@ -559,8 +595,32 @@ fn build_binary_inner(
559595 }
560596
561597 let len = proto. len ( ) ;
598+ // Merge target of the currently open guarded region (jump destination
599+ // for both the skip branch and the region's fall-through).
600+ let mut pending_merge: Option < cranelift:: prelude:: Block > = None ;
562601 for ( i, x) in proto. iter ( ) . enumerate ( ) {
563- let is_last = ( i + 1 ) == len;
602+ if let ( Some ( s) , Some ( m_eff) ) = ( sub_len, sub_mask_eff)
603+ && i % s == 0
604+ {
605+ if let Some ( mb) = pending_merge. take ( ) {
606+ builder. ins ( ) . jump ( mb, & [ ] ) ;
607+ builder. switch_to_block ( mb) ;
608+ }
609+ let si = ( i / s) as i64 ;
610+ let body = builder. create_block ( ) ;
611+ let merge = builder. create_block ( ) ;
612+ let shifted = builder. ins ( ) . ushr_imm_s ( m_eff, si) ;
613+ let bit = builder. ins ( ) . band_imm_s ( shifted, 1 ) ;
614+ builder. ins ( ) . brif ( bit, body, & [ ] , merge, & [ ] ) ;
615+ builder. switch_to_block ( body) ;
616+ pending_merge = Some ( merge) ;
617+ // Values cached in earlier (possibly skipped) regions must not
618+ // be forwarded across the guard.
619+ cranelift_context. load_cache . clear ( ) ;
620+ }
621+ // `is_last` lets the final statement return directly, bypassing any
622+ // merge block — incompatible with the guard's region merge.
623+ let is_last = ( i + 1 ) == len && sub_len. is_none ( ) ;
564624 x. build_binary ( & mut cranelift_context, & mut builder, is_last) ?;
565625
566626 // Belady: while over capacity, evict the entry whose next read
@@ -589,6 +649,10 @@ fn build_binary_inner(
589649 }
590650 }
591651
652+ if let Some ( mb) = pending_merge. take ( ) {
653+ builder. ins ( ) . jump ( mb, & [ ] ) ;
654+ builder. switch_to_block ( mb) ;
655+ }
592656 builder. ins ( ) . return_ ( & [ ] ) ;
593657 builder. seal_all_blocks ( ) ;
594658 builder. finalize ( isa. frontend_config ( ) ) ;
0 commit comments