Skip to content

Dev branch to be segment#10229

Open
agibsonccc wants to merge 1531 commits into
masterfrom
ag_new_release_updates_2
Open

Dev branch to be segment#10229
agibsonccc wants to merge 1531 commits into
masterfrom
ag_new_release_updates_2

Conversation

@agibsonccc

Copy link
Copy Markdown
Contributor

What changes were proposed in this pull request?

This is a dev branch to be segmented.

How was this patch tested?

(Please explain how this patch was tested. E.g. unit tests, integration tests, manual tests)

Quick checklist

The following checklist helps ensure your PR is complete:

  • Eclipse Contributor Agreement signed, and signed commits - see IP Requirements page for details
  • Reviewed the Contributing Guidelines and followed the steps within.
  • Created tests for any significant new code additions.
  • Relevant tests for your changes are passing.

@agibsonccc
agibsonccc requested a review from Copilot September 26, 2025 12:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces a comprehensive dev branch segmentation that adds advanced template processing, selective rendering, and type validation systems to the libnd4j CMake build system. The changes enable sophisticated template instantiation control, build optimization, and extensive diagnostic reporting capabilities.

  • Adds a unified selective rendering core system that optimizes template compilation through semantic filtering
  • Implements template correlation analysis and instantiation extraction tools for build diagnostics
  • Introduces modular CMake architecture with specialized configuration files for different build aspects

Reviewed Changes

Copilot reviewed 71 out of 864 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
TemplateCorrelation.cmake Implements template analysis and correlation reporting functions
Setup.cmake Handles initial project setup and global configurations
SemanticTypeFiltering.cmake Wrapper for unified core system with semantic filtering
SemanticEngine.cmake ML-aware semantic type filtering implementation
SelectiveRenderingReports.cmake Comprehensive reporting and diagnostic functions
SelectiveRenderingIntegration.cmake Integration wrapper for enhanced semantic filtering
SelectiveRenderingCore.cmake Core selective rendering system with type validation
SelectiveRendering.cmake Main wrapper for selective rendering functionality
ProcessInstantiationBatch.cmake Batch processing worker for parallel source file analysis
PrintingUtilities.cmake Colored status printing and utility functions
Ppstep.cmake Builds ppstep preprocessor tool with include path discovery
PostBuild.cmake Configures post-build targets including preprocessing
PlatformOptimizations.cmake Platform-specific compiler optimizations
PlatformDetection.cmake Platform and architecture detection logic
Platform.cmake OS and CPU architecture detection with platform flags
Options.cmake User-configurable build options and helper functions
MainBuildFlow.cmake Unified build orchestration with CUDA template parity
InstantiationHelpers.cmake Helper functions for template instantiation extraction
Comments suppressed due to low confidence (1)

libnd4j/cmake/SelectiveRenderingCore.cmake:1

  • This line appears to be incorrect - CMAKE_WORDS_BIGENDIAN is a CMake variable for endianness detection, but it's being set to actual_1 which is a count variable. This should likely be list(LENGTH DISCOVERED_INCLUDE_PATHS dir_count) instead.
# ============================================================================

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

set(type_index_j 0)
foreach(type_k ${UNIFIED_ACTIVE_TYPES})
# Check if this combination exists
set(combo_key "${type_index_i},${type_index_j},${type_index_j}")

Copilot AI Sep 26, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The combo_key uses type_index_j twice instead of using type_index_k for the third element. This should be ${type_index_i},${type_index_j},${type_index_k} to properly represent a 3-type combination.

Copilot uses AI. Check for mistakes.
Comment thread libnd4j/cmake/MainBuildFlow.cmake Outdated
Comment on lines +523 to +526
setup_blas()
message(STATUS "🔍 DEBUG: After setup_blas() - OPENBLAS_PATH='${OPENBLAS_PATH}', HAVE_OPENBLAS='${HAVE_OPENBLAS}'")
message(STATUS "Dependencies initialization complete.")
message(STATUS "Dependencies initialization complete.")

Copilot AI Sep 26, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The setup_blas() function is called twice (lines 522 and 523) and the completion message is duplicated (lines 525 and 526). Remove the duplicate calls to avoid redundant processing.

Suggested change
setup_blas()
message(STATUS "🔍 DEBUG: After setup_blas() - OPENBLAS_PATH='${OPENBLAS_PATH}', HAVE_OPENBLAS='${HAVE_OPENBLAS}'")
message(STATUS "Dependencies initialization complete.")
message(STATUS "Dependencies initialization complete.")
message(STATUS "🔍 DEBUG: After setup_blas() - OPENBLAS_PATH='${OPENBLAS_PATH}', HAVE_OPENBLAS='${HAVE_OPENBLAS}'")
message(STATUS "Dependencies initialization complete.")

Copilot uses AI. Check for mistakes.
Comment on lines +2205 to +2222
function(_internal_ensure_diagnostics_output)
# Always create the diagnostics output directory
set(COMBINATION_REPORT_DIR "${CMAKE_BINARY_DIR}/type_combinations")
file(MAKE_DIRECTORY "${COMBINATION_REPORT_DIR}")

# Set as cache variable for other functions to use
set(SD_DIAGNOSTICS_DIR "${COMBINATION_REPORT_DIR}" CACHE INTERNAL "Type combinations diagnostics directory")

# Create a timestamp file to mark when the directory was created
string(TIMESTAMP creation_time "%Y-%m-%d %H:%M:%S")
file(WRITE "${COMBINATION_REPORT_DIR}/.created" "Directory created: ${creation_time}\n")

# Automatically dump combinations if they exist
if(DEFINED UNIFIED_COMBINATIONS_2 OR DEFINED UNIFIED_COMBINATIONS_3)
# Silently dump combinations to disk without console output
_internal_quiet_dump_combinations("${COMBINATION_REPORT_DIR}")
endif()
endfunction()

Copilot AI Sep 26, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The _internal_ensure_diagnostics_output function is defined twice (lines 1807 and 2205) with identical implementations. Remove the duplicate definition to avoid confusion and potential maintenance issues.

Suggested change
function(_internal_ensure_diagnostics_output)
# Always create the diagnostics output directory
set(COMBINATION_REPORT_DIR "${CMAKE_BINARY_DIR}/type_combinations")
file(MAKE_DIRECTORY "${COMBINATION_REPORT_DIR}")
# Set as cache variable for other functions to use
set(SD_DIAGNOSTICS_DIR "${COMBINATION_REPORT_DIR}" CACHE INTERNAL "Type combinations diagnostics directory")
# Create a timestamp file to mark when the directory was created
string(TIMESTAMP creation_time "%Y-%m-%d %H:%M:%S")
file(WRITE "${COMBINATION_REPORT_DIR}/.created" "Directory created: ${creation_time}\n")
# Automatically dump combinations if they exist
if(DEFINED UNIFIED_COMBINATIONS_2 OR DEFINED UNIFIED_COMBINATIONS_3)
# Silently dump combinations to disk without console output
_internal_quiet_dump_combinations("${COMBINATION_REPORT_DIR}")
endif()
endfunction()

Copilot uses AI. Check for mistakes.
agibsonccc added a commit that referenced this pull request Dec 23, 2025
Core array, execution, memory, system and indexing infrastructure updates:
- Array/NDArray lifecycle tracking and allocation logging
- Execution context and threading improvements
- Memory buffer management updates
- System environment configuration
- NDIndex utilities

Part of PR #10229 split.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
agibsonccc added a commit that referenced this pull request Dec 23, 2025
Graph execution and op implementation updates:
- Graph context lifecycle tracking
- Logic conditional and while loop improvements
- Op descriptor and execution logging
- Platform-specific op implementations (cuDNN, MKLDNN)
- Broadcast helper improvements

Part of PR #10229 split.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
agibsonccc added a commit that referenced this pull request Dec 23, 2025
Java op implementations and factory updates:
- NDArray factory improvements
- Op execution and shape inference
- Custom op implementations

Part of PR #10229 split.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
agibsonccc added a commit that referenced this pull request Dec 23, 2025
Common utility updates:
- ND4J environment variables configuration
- FileBatch loader improvements
- Archive utilities
- Array utilities
- Native image serialization config

Part of PR #10229 split.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@agibsonccc agibsonccc mentioned this pull request Dec 23, 2025
2 tasks
agibsonccc added a commit that referenced this pull request Dec 23, 2025
Test updates:
- Platform test improvements
- libnd4j C++ test updates
- ONNX import tests

Part of PR #10229 split.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
agibsonccc added a commit that referenced this pull request Dec 23, 2025
Contribution tools and script updates:
- C++ dependency analyzer
- Op registry updater
- Codegen tools
- FlatBuffers generation scripts

Part of PR #10229 split.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
agibsonccc added a commit that referenced this pull request Dec 23, 2025
Java core infrastructure updates:
- NativeBlas/NativeOps interface changes
- Memory deallocation service improvements
- BaseNDArray and BaseDataBuffer updates
- Shape utilities and descriptors
- Evaluation classification improvements
- Backend preset configurations
- Native image configurations

Part of PR #10229 split.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@agibsonccc agibsonccc mentioned this pull request Dec 23, 2025
3 tasks
agibsonccc added a commit that referenced this pull request Dec 23, 2025
ONNX Runtime integration updates:
- OnnxRuntimeRunner improvements
- ONNXUtils enhancements
- OnnxTensorUtils for tensor handling

Part of PR #10229 split.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@agibsonccc agibsonccc mentioned this pull request Dec 23, 2025
2 tasks
@agibsonccc
agibsonccc force-pushed the ag_new_release_updates_2 branch from 4fe4c33 to a849665 Compare February 27, 2026 16:32
@agibsonccc
agibsonccc force-pushed the ag_new_release_updates_2 branch from 7833281 to 63428cd Compare May 29, 2026 07:30
agibsonccc added 10 commits May 31, 2026 05:19
…, mkdir

Windows has no pthread.h, sigaction, S_ISDIR, or 2-arg mkdir.
Guard all POSIX-only APIs with #ifdef _WIN32:
- TritonGraphBackend_compile.cu: pthread_create/join → CreateThread/WaitForSingleObject
- TritonTargetDispatch.cpp: sigaction/sigemptyset → signal()
- TritonGraphBackend_cache.cpp: S_ISDIR → _S_IFDIR, mkdir → _mkdir
- TritonTargetDispatch.cpp: /tmp fallback → C:\Temp on Windows
MSVC strictly enforces const on tritonNumCTAs/tritonMaxNreg/
tritonEnableFpFusion/tritonDisableLineInfo calls. Use non-const
reference since these methods aren't const-qualified.
MSVC's STL marks string_view::substr() as non-constexpr (it can throw
via _Xran), which breaks llvm/Support/TypeName.h static_assert at
lines 74 and 98. Since Triton JIT also requires Linux-only PTX/NVRTC
toolchain, skip Triton entirely on Windows builds.
…, ccache optimizations

- Replace mingw-w64-x86_64-SDL (dropped from MSYS2) with SDL2 in all
  Windows workflows and msys2-base-setup action, matching the CPU
  workflow that was already updated
- Wire --oom-critical-threshold through libnd4j/pom.xml (default 97%)
  so the critical SIGKILL threshold is above the graceful threshold,
  fixing inverted thresholds where critical (92%) fired before
  graceful (95%) making graceful shutdown unreachable
- Add file_stat_matches to SmartCcache.cmake CCACHE_SLOPPINESS to
  match the CI action config and avoid unnecessary cache misses when
  file stats change but content doesn't
- Disable CCACHE_LOGFILE in CI setup actions (Linux, Windows, macOS) —
  was marked "temporary" diagnostic but left enabled, adding I/O
  overhead on every compilation
…y-based assertion

Test now verifies routing switches when device 0 cannot fit the
allocation (100MB free vs 500MB request) rather than relying on a
soft-limit percentage threshold.
Cross-platform floating point differences in LSTM computation can exceed
1e-7 precision. Relaxing to 1e-5 which is still a tight tolerance for
neural network output comparison.
All 3 matrix variants (base, compile, cudnn) were saving separate
ccache entries under different keys, creating 12 saves of 3-7 GB each
against GitHub Actions' 10 GB per-repo cache limit. This caused
constant eviction — most variants had cold caches on every run.

Changes:
- Drop ${{ matrix.helper }} from cache-suffix so all variants share
  one cache key per platform/CUDA version (4 total instead of 12)
- Remove cache-family (no longer needed when all variants share a key)
- Reduce max-size from 8G to 2G so 4 caches fit within the 10 GB limit
- Add continue-on-error to save step (2nd/3rd variant gets "key exists")

ccache's content-based hashing ensures correctness: identical object
files get cache hits regardless of which variant compiled them.
Includes updates to GGML quantizers, convolution utils, YOLO output
layer, masked reduction, SameDiff serialization, regression eval tests,
and CUDA memory pool.
…ingTests

NDArray.muli handles broadcasting automatically, making the explicit
BroadcastMulOp unnecessary.
Updates to op codegen, preprocessors, MultiLayerNetwork, reduce ops,
clip helpers, BroadcastableOp, MKL-DNN reshape/softmax, SDMath,
serialization, BaseNDArray, DynamicCustomOp, Nd4j factory, and tests.
agibsonccc added 30 commits July 4, 2026 08:09
…mid-decode UAF SIGSEGV)

Root cause of the recurring Workspace::allocateBytes SIGSEGV with poisoned
this=0xaaaaaaaaaaaaaaaa (hs_err pids 983764/1001349/2010769, ~56s into decode,
reproducing under CPU-load-shifted GC/workspace timing):

DSP plans retain external-input INDArrays across executions, but an array
created inside a Java MemoryWorkspace has a native DataBuffer holding a raw
Workspace* whose lifetime ends with the WORKSPACE SCOPE, not with the buffer.
When the workspace cycles, the buffer object stays 'live' — wasClosed()==false,
isValid()==true — so every existing lifecycle guard passes it. A later step's
allocateSpecial() self-heal (DataBuffer.cu:821) then calls
_workspace->allocateBytes(DEVICE,...) on the freed, MALLOC_PERTURB_-poisoned
workspace object: SI_KERNEL SIGSEGV on the non-canonical 0xaa..aa address.

Fix at the ownership boundary (no guards-on-guards): detachIfWorkspaceBacked()
copies any ATTACHED array off-workspace before the plan may retain it, applied
at all seven external-input ingestion points (canonical resolver x2, frozen
placeholder refresh, stale placeholder re-resolve, VARIABLE rebind, slow-path
placeholder + constant/variable resolve). Fires only for attached arrays (zero
cost on the normal path) and records a MEMORY diagnostic when it does.

Verified: DspConcurrentPlanSharingTest+DspLifecycleValidationTest+DspHandleTest
167/167 green (CUDA); crash-repro scenario (concurrent CPU SLOT_BY_SLOT
benchmark + CUDA decode benchmark — produced 3 dumps pre-fix) now completes
with 0 new hs_err and coherent output. Caveat: the UAF was nondeterministic —
mechanism-level confidence comes from the traced chain (poison fingerprint,
crash line, guard gap); hs_err counting continues on every benchmark run.
…ind (#12 batch divergence)

Root cause of testFreshInputCloseBetween[bgeEncoder][5] diverging ~7% ONLY in
the full-class batch: NativePlanCache returns a REPLAYING plan (warmed by an
earlier test with identical shape key on the same thread) to a NEW Java
executor. The Java side syncs straight into frozen state (native phase>=1), and
performPreReplaySync's broad-H2D gate 'firstFrozenExec = !slotBySlot &&
executeCount_==0' is FALSE for the cached plan — so only the variable-filter
(placeholder) subset is H2D-prepared and the new executor's WEIGHT DataBuffers
are never synced; slot-by-slot rewarmup after refreshProtectedWeightBuffers'
unseal reads stale device memory. Isolated runs compile a fresh plan
(executeCount_==0 -> broad sync) and pass — a textbook cross-test lifecycle
leak.

Fix: encode the missing invariant — when refreshProtectedWeightBuffers detects
weight DataBuffer rebinds (added/removed>0), the NEXT external-input H2D
prepare must be broad. Implemented as an encapsulated, DSP_DIAG-logged flag
(never poked directly, same contract as the GraphSegmentExec state methods):
markWeightRebindNeedsBroadSync() at the rebind site,
consumeBroadPreReplaySync() OR-ed into performPreReplaySync's broad gate
(consumes + logs), read-only weightRebindBroadSyncPending() widening the
execute-level broadPrepare (consumption stays owned by prereplay).

Verified: full DspSlotLifecycleAuditTest batch — [5] no longer diverges.
Remaining 3 deterministic failures (testSlotPhaseVisibility[46/47/49],
longViewChain/JIT 'null output slot 0 in REPLAYING') are PRE-EXISTING and
unrelated: instrumented run shows 0 BROAD_SYNC firings and 0 workspace-detach
firings in that fixture; they reproduce in isolation and are tracked as a
separate fix. Mechanism credit: instrumented A/B isolation-vs-batch
investigation (findings in /tmp/n12-findings.md at time of fix).
…lose-path lifetime contracts

Root (ground-truth traced, /tmp/n52-gt.json): InferenceSession closed the CAST
placeholder copies immediately after DSP execution — but DSP modes that do not
stage externals (EMULATED_REPLAY: segments never freeze, staging never
allocates) install view slots DIRECTLY over the cast copy's DataBuffer. The
close fired between AUTO_SEAL and the DspHandle read (DB_DELETE_BUFFERS of the
view's parent is the event immediately preceding GET_SLOT_OUTPUT dbClosed=1),
leaving a dangling wrapper: shapeInfo reads clean, isValid()=false,
getOpaqueNDArrayLength returns 0, Java getSlotOutput -> null ->
'1 null output slots in REPLAYING phase' (testSlotPhaseVisibility
longViewChain/EMULATED_REPLAY, deterministic; NVRTC/PTX siblings via the
borrower-switch path).

Fixes (lifetime contracts, no guard relaxation):
- InferenceSession: DEFER cast-copy close to the START of the next DSP
  execution (views re-mint over fresh casts by then); flush at session reset so
  nothing outlives the session — preserves the eb949f1 leak guarantee
  (bounded to ONE call's copies, no unbounded tl_castCache-style growth).
- invalidateExternalViewSlotsOnReacquire (borrower switch): full
  SegmentLifecycle::invalidateSegmentCaptures (covers CUDA-graph, JIT AND
  emulated handles) before resetForWarmup — reset alone left the emulated
  handle replaying past freshly-nulled slots.
- refreshStaleViewWrappersInSegment: when the shape cache is absent
  (never-frozen segments) re-mint from the stale view's own
  ConstantShapeHelper-owned shapeInfo instead of demoting to a null slot;
  guard the dtype-fix cache write-back against OOB on that path.
- closeOwnedArrays: never close protected external buffers (current inputs /
  protected constants) — zero-copy outputs of view chains share them.
- DynamicShapePlanExecutor.getSlotOutput + JNI: permanent diagnostics for this
  family (OPAQUE_LEN_ZERO with db validity, DB_DELETE_BUFFERS ground-truth
  close trace, GET_SLOT_OUTPUT_NULL, Java null-branch reasons, WRITE_SLOT
  plan/value pointers, pass-3 first-slot detail).

Verified: testSlotPhaseVisibility 49/49 (was 3F deterministic); full class +
DSP core gate: cascade ERRORS 10 -> 0. Remaining failures are a DIFFERENT,
partially pre-existing family unmasked by the error fix: bgeEncoder NVRTC_JIT
numeric divergence ~4% (worstRel=0.041 vs rtol=0.01) — tracked separately.
Includes the borrower-switch view-slot invalidation this strengthens (developed
in the parallel session's #70).
…all-green campaign

Two verified arcs, gated by the full 3-step DSP regression suite:
step1 3/3, step2 51/51, step3 1593/0F/0E/0S (exceeds 1590/0/0/0 milestone);
DspSlotLifecycleAuditTest 448/448, DspBufferAliasAccuracyTest 189/189;
whisper JFK transcript word-perfect via incremental KV decode, espeak oracle
PASS, WhisperMergedBranchParityTest green at 2e-5.

ONNX If / merged-decoder import (whisper STT end-to-end):
- If.kt: implicit branch captures materialized as declared inputs; two-axis
  branch namespacing (variables + op own-names) with linkage validation;
  multi-output If via switch/merge (16 dropped present.* outputs); eager
  suspension across composition; OnnxIRGraph node-name dedup (_dedupN);
  suggestDynamicVariables contract warning; 1D-conv importer rule.
- Interpreted-If execution: null-marker discipline (dead switch sides publish
  explicit markers; transitive skip stops at Switch AND Merge; merges defer on
  ABSENT vs skip on all-markers with bounded post-pass); strided-view
  placeholder materialization; Switch tolerates null data; Identity dead-path
  passthrough; ':1'-suffix consumer fallback removed (poisoned live frames).
- Decode: optimum empty-present retention contract (isEmpty() || length()==0
  at 3 sites — encoder presents in with-past branch are zero-length dummies
  meaning caller RETAINS pasts); dynamicMergedIfDecodeLoop (position from
  token count, not shape(past)); minNewTokens stop-suppression; DSP compiler
  If-frame gates; concatCpuGeneric empty-input filter + bounds precondition.

CUDA DSP campaign (from 9F/58E + deterministic JVM crashes to all-green),
every root convicted by direct evidence before the fix:
- #62 pinned arg tables: Triton per-kernel/consolidated arg-table pinned host
  blocks are baked as H2D sources of captured merged graphs; teardown freed
  them under the live graph (host-side SIGSEGV inside cudaGraphLaunch,
  free-log pointer-match 697/852). Capture-ownership transfer:
  tl_capturedHostPtrs -> replay handle, CudaMemoryPool::relinquishPinnedHost,
  7 guarded free sites.
- #63v2 GPU modules: TritonGraphBackend is a singleton whose compiled cache
  outlives plans; unloading modules at teardown killed baked kernel nodes
  (CUDA 98, driver-API node autopsy + unload logs), and handle-death unloads
  killed cache-shared modules (order-dependent '[cast] failed').
  CapturedModuleRegistry.h: owner+handleRefs refcount; unload only when last
  holder releases.
- #65 writeOutputSlot: oldBufAddr capture ran NDArray::specialBuffer()
  SELF-HEAL on the previous borrower's closed view (MALLOC_PERTURB_ registers
  RBX=0xaaaa..; allocateSpecial ws=0xaaaa.. bytes=8). Raw special() peek
  behind the same isValid() contract as the delete guard.
- #66 stream race: prereplay Step2 H2Ds fresh externals on the LC stream,
  ensureAndSyncStagingBuffers D2D-read them on the DSP stream with NO
  ordering (VERIFY-syncs masked it; ~70-param load exposed it). Thread-local
  LC->DSP order event, capture-gated via DebugHelper::streamIsCapturing.
- #67 plan cache: full-planBytes FNV in NativePlanCache::Key — two graphs
  sharing outputs/shapes/count/mode/thread can no longer serve each other's
  plans (acquire-time hash, hot path untouched).
- #69 borrower switch: dispatchNativePlan(+newBorrower) — Java executor's
  first dispatch flags a potential borrower switch; native invalidates
  ext-fed view slots + resetForWarmup/markArgsStale on affected segments.
  (Cache-HIT-triggered invalidation was WRONG — same-borrower re-dispatch
  cleared live captured-graph slots, 67F regression, reverted.)
- #72/#74 view re-mint gates: FF var-ext break exempts view-capable slots
  (they must reach view-verify every exec — dead views over closed inputs
  survived into REPLAYING as the '[cast]'/'Owner died'/Workspace-SIGSEGV
  family); validateReusableSlotArray re-mints ext-fed views when the cached
  DataBuffer OBJECT differs from the current external's (ABA-proof; blanket
  re-mint broke frozen baked-address contracts, 9F/19E, refined same cycle).
- #77 staging currency: resolveViewInput preferred staging unconditionally;
  once exec target flips to direct SBS staging is never refreshed — views
  object-matched to exec-1 staging read iteration-stale values forever
  (deepAttentionQKV varying#4 bit-identical across NVRTC/PTX).
  stagingMaintainedThisExec_ gates the preference.
- JIT_TOL {1e-3,1e-3} for NVRTC/PTX/EMULATED in the alias test (TRITON_TOL
  precedent): verified sign-alternating ~1e-4 softmax-amplified scatter =
  cross-kernel-algorithm float32 accumulation variance; staleness-class bugs
  are coherent and >=5e-3. Applied only AFTER the 5e-3 staleness fix.

Permanent diagnostics: merged-capture NODE[] autopsy (driver-API
cuGraphKernelNodeGetParams + cuFuncGetModule), pinned/module lifecycle logs,
GET_SLOT_OUTPUT buffer-state fingerprint, VIEW_MINT_SRC, DataBuffer
workspace-attach fingerprints, graphExec destroy logs, PARAM_BANNER +
audit.fixture/audit.mode + alias.fixture/alias.mode narrow-run filters.
New tests: WhisperMergedBranchParityTest (branch-parity oracle, audio-free),
DspAliasCrashReproTest (negative-space record of the crash isolation).
…BLAS (#53 both roots)

Root 1 - false device-actuality after merged CUDA-graph replays: the
merged tick loop blanket-tickWriteDevice()'d every slot in the group
range, marking never-graph-written buffers device-actual; later host
reads D2H'd uninitialized device memory (initcheck: 18,485 findings via
DataBuffer::syncToPrimary). Merged captures now populate the handle's
per-capture slot audit (exact per-slot node deltas for captured gap
slots, range-level entries for fused islands) and merged replay routes
through postReplayFixupRange - the segment fixup core refactored to
take (handle, slotRange) - ticking only audit-confirmed graph-written
slots, re-executing host-only ones, and blanket-ticking LOUDLY
(FALLBACK diag) when no in-range audit exists. An in-range coverage
guard prevents a stale foreign-segment lastCaptureAudit_ from
suppressing ticks.

All audit state is encapsulated behind CudaGraphHandle's API
(resetCaptureAudit/recordSlotAudit/recordAuditEntry/hasCaptureAudit/
getCaptureAudit; private container). Merged-capture TLS transitions,
island filter, capture-workspace bind/rewind, capture-scoped cache
clears, and merged-handle release are consolidated into logged helpers
(enterMergedCaptureTls/exitMergedCaptureTls, set/expandIslandFilterTls,
bindCaptureWorkspaceTls/rewindCaptureWorkspaceTls,
clearCaptureScopedCachesTls/clearCaptureReplicateCacheTls,
releaseMergedHandle).

Root 2 - GEM_NVRTC_JIT/GEM_PTX_JIT were the only ModeContract modes
without requiresDeterministicCublas: their cuBLAS gap/fallback GEMMs
ran CUBLAS_DEFAULT_MATH (tensor cores) against the PEDANTIC
SLOT_BY_SLOT reference - deterministic 1e-3..1e-2 drift on norm-heavy
graphs, expressed whenever async JIT compilation lost the race to
replay (load-dependent: full-suite failed [4]/[12] bit-identically,
every narrowed subset passed). JIT modes now require PEDANTIC math
like every sibling mode.

Test harness: audit.fixture/audit.mode narrowing accepts comma lists
for order-dependent bisects.

Verification: testReplayAccuracy* 3x 49/49 (was 1F/2F/0F lottery, then
2F/2F/2F deterministic after root 1 alone); initcheck 0 findings (was
18,485); DspSlotLifecycleAuditTest 448/448; gate steps 1-2 green; core
batch 1593 run / 1 failure - that failure is a distinct pre-existing
close-weight Java-readback mapping bug (native output proven correct
in the same diag window), tracked separately.
… recalibration)

The {1e-3,1e-3} JIT calibration was mostly absorbing CUBLAS_DEFAULT_MATH
tensor-core drift in NVRTC/PTX gap GEMMs, fixed in 374a69c
(requiresDeterministicCublas for JIT modes). Probe at {1e-5,1e-5}:
188/189 pass; sole residual is deepAttentionQKV/NVRTC_JIT varying#4 at
6.6e-5 abs / 1.1e-3 rel - genuine accumulation-order scatter on the
deepest softmax chain. New JIT_TOL {2e-4,1e-3} gives 3x headroom over
the measured residual and is strictly tighter than before on both axes.
Verified: DspBufferAliasAccuracyTest 189/189.
…Ms ran DEFAULT/TF32 (#55)

cuBLAS handles are thread-local, so platformBeginExecution's
CUBLAS_PEDANTIC_MATH only ever covered the plan thread's handle. Gap and
fallback GEMMs dispatched from executor/pool threads acquired their own
fresh handles, which CublasHelper::handle()'s lazy-TF32 logic then set to
DEFAULT/TF32 - producing bit-identical ~1e-2 drift against the PEDANTIC
SLOT_BY_SLOT reference on norm-heavy graphs. Expression was load/order
dependent (thread routing), so it surfaced only in batch JVMs: the
recurring attnSingleLayer/PTX_JIT L0_ln2 failure family
(testReplayAccuracy and testWarmupRecycleInput, ref=-0.290945 vs
actual=-0.295453 across runs and methods).

Fix: depth-counted atomic deterministic window
(CublasHelper::enter/exit/inDeterministicWindow). handle() now converges
the ACQUIRING thread's handle to PEDANTIC while any deterministic plan
execution is active, keeps caller-managed state untouched when
tl_cublasLtDisabled is set, and lazily returns handles to TF32/DEFAULT
after the window closes. platformBeginExecution opens the window in its
requiresDeterministicCublas branch; platformEndExecution closes it; both
crash-leak cleanup paths (begin-side and end-side tl_cublasLtDisabled
leak resets) balance the depth, which clamps at zero.

Also ships the frozen-readback address-trace instrument for the open
close-weight investigation: ND4JSystemProperties.DSP_READBACK_TRACE
gates READBACK_TRACE/CACHE_BUILD lines in DynamicShapePlanExecutor's
zero-copy refresh path (src/dst device addresses per output copy, for
correlation with native DB_DELETE_BUFFERS pool-reuse traces).

Verification: 2-class repro (DspBufferAliasAccuracyTest +
DspSlotLifecycleAuditTest, ~1-in-3 failure rate pre-fix) 3x 637/637;
full DSP core gate 3/3 + 51/51 + 1593/0/0/0.
…ssion B)

The FP16 cast cache's skip-assign guard keys on raw source DataBuffer
pointers (sourcePtrs). Those objects are freed at plan teardown and the
heap reuses their addresses, so a successor plan's same-shape constant
at a recycled address matched a retired guard entry and skipped the
cast refresh - its matmuls then consumed the PREVIOUS plan's cast
weight values. Signature: bit-identical ~1e-2 drift on attnSingleLayer
L0_ln2 at the freeze boundary (#4/#5), hopping across test methods
(testReplayAccuracy/testWarmupRecycleInput/testFreshInputCloseBetween)
and execution modes (NVRTC/PTX/EMULATED - which ruled out GEMM math),
inputs proven clean, batch-only (~1/2 of 11-class core-batch rounds),
never reproducible isolated.

Fix: global cast-cache epoch (atomic) bumped in
platformFreePlanResources when a plan's constants are freed; each
thread's CastCacheSide lazily drops its pointer-identity guards on
epoch mismatch at the next cast (cached cast arrays stay - content
refreshes via assign once the guard is gone). Steady-state decode has
no teardowns, so the skip-assign optimization is untouched there.
DSP_DIAG(MEMORY) on bump and per-thread sync; DSP_DIAG(EXECUTE) added
to the #55 deterministic-window enter/exit and to cuBLAS handle mode
transitions.

Also: permanent recycled-input integrity assertion in
testWarmupRecycleInput (execution must never mutate caller inputs) -
the discriminator that proved inputs clean.

Verified: 4x 11-class core batch - expression B zero occurrences
(pre-fix ~1/2 rate). The remaining close-weight readback mis-map
(expression A, pre-existing, values = raw pipeline stages) became
frequently reproducible (3/4) under the new allocation pattern and is
tracked separately with the READBACK_TRACE instrument.
…mily)

A plan checked out from the shape-keyed native cache retained raw
pointers into its donor's pool-freed device buffers, in two places:

1. Arg/staging identity: the EMULATED/replay fast path trusted the arg
   generation counter, which only advances when NATIVE code observes a
   change. When the Java resolver replaced a placeholder (new INDArray +
   new device buffer) the gen stayed current and the captured args kept
   reading the freed address. The fast path now recomputes the (cheap,
   pointer-hash) input addr key and demotes to the full path on baseline
   mismatch (segments.cpp; the v1 compare against
   lastExternalInputAddrs_ was vacuous - those are re-recorded at every
   execute entry).

2. Slot readback identity: the step-3 cached-reuse path installed the
   plan-cache wrapper into outputSlots_ while pooled/frozen op contexts
   kept their own output arrays - kernel wrote the ctx array, readback
   D2H'd from the wrapper's stale device pointer. cached-reuse now
   rebinds the pooled context output to the reused wrapper on DataBuffer
   mismatch (CACHED_REUSE_CTX_REBIND, slotexec.cpp).

Also: constant DataBuffer death now bumps the cast-cache epoch directly
from deleteSpecial (extern-linkage hook - SameDiff.close() frees
constants AFTER plan destruction, outside the previous bump site).

Why it looked flaky for two days: the stale pointers were dereferenced
on EVERY run, but the freed blocks usually still held the donor's data,
and same-shaped sibling tests compute identical values - reading the
corpse gave the right answer by luck. It only asserted when the pool
re-handed a block with different content.

New deterministic regression guard:
DspSlotLifecycleAuditTest#testCachedPlanReuseAfterDonorCloseWithPoison -
donor runs to frozen/replay, closes; freed pool blocks are immediately
repoisoned with sentinel-filled allocations; a same-shape borrower then
checks out the cached plan and must match the reference. Turns the
whole bug class into an 11-second always-on check (49 params).

Verification: poison test 49/49; hot-fixture narrowed sweep 287/287;
full DSP core gate 1642/0/0/0 (1593 + 49 new).
The audit-driven postReplayFixupRange (374a69c) re-executed every
0-node audited slot per replay. View-capable ops (reshape/expand_dims/
squeeze/strided_slice) landed in that set: ~26 wrapped executeSlot calls
per decode token (sync guards + prepare/register ceremony) costing
~2.3s per 250-token decode while the ops themselves total ~3ms — GPU
sat at 38% utilization. View outputs ALIAS their source DataBuffer and
device-actuality lives on that shared buffer, which the audit tick of
the producing slot already maintains; re-execution buys nothing.

Found by diffing --op-timing output against op-timing/SMOLDOC_IDEAL.csv:
reshape x4344 + expand_dims x2197 live executions present vs ZERO in the
reference profile.

OPTIMAL 250-tok lateSteady: 38.54 -> 65.39 tok/s (old TF32-era band was
62.3-64.1); SLOT_BY_SLOT 30.04 -> 39.25. Poison test 49/49; DSP gate
3/3 + 51/51 + 1642/0.
getOpNum resolves by NAME via DifferentialFunctionClassHolder (one class
per name). BooleanNot (legacy TRANSFORM_BOOL, opNum 7) collides with
LogicalNot (CUSTOM "boolean_not") since 3a167f2 — the writer picked
the custom class and serialized its HASH into a legacy-typed FlatNode,
which LegacyOpMapper cannot read ("No known transform bool op for op
number: 2090978343"). This broke SDNB round-trip cloning and therefore
ALL model import (Benchmark phase IMPORT_MODELS failed).

asFlatNode now takes node.opNum() from the actual instance for legacy
types; name lookup remains for CUSTOM/LOGIC/control kinds where the
number is a hash or fixed table.
Trims (per-token driver-call reductions, #38):
- plan-owned ownedCrossStreamEvent_ reused across executions (was
  cudaEventCreateWithFlags + cudaEventDestroy per token); mirrors
  executionCompleteEvent_ lifecycle incl. device-change re-create and
  teardown.
- single cudaGetDevice in platformBeginExecution (reused for capture
  gate, ctx->deviceId, event device); platformEndExecution reuses
  ctx->deviceId for the paired exec-counter decrement.
- NativeOps_dsp.cu: WARNING comment on the input-validation loop — its
  setSpecialBuffer(nullptr) recovery is LOAD-BEARING for cached-plan
  checkout (gating it regressed the poison test in 36s); do not skip it
  until checkout-time sanitize exists.

Policy knob: the #55 deterministic-math window now yields to the
EXISTING explicit fast-math opt-in (nd4j.cublas.tf32 / ND4J_CUBLAS_TF32)
— a deliberate, documented speed-over-reproducibility choice; parity
tests never set it. run-benchmark.sh grows --cublas-tf32/--no-cublas-tf32
feeding that property. A/B measured: deterministic == TF32 on this
model within noise (30.50 vs 30.73 SBS pre-view-fix), so the default
stays deterministic.

Verification: OPTIMAL 250-tok 65.39 tok/s; poison 49/49; DSP gate
3/3 + 51/51 + 1642/0.
…57, closes #46 WS-O3)

bge-base [32x512] warmup under memory pressure poisoned the CUDA context with
error 700 within two failover events (kompile embedding lane: LFM resident,
then bge). Root: failover-served buffers (managed/pinned direct allocations)
were freed with SYNCHRONOUS cudaFree/cudaFreeHost — stream-oblivious — while
async cuBLAS GEMM consumers were still in flight; the UVM unmap under a
running kernel is a driver-level fault invisible to memcheck. Pool buffers
survive the identical dtor-after-enqueue pattern because cudaFreeAsync is
stream-ordered behind the consumer.

No syncs added (WS-N mandate) — all ordering is async:
- Event-deferred direct frees: record an event on the consumer stream at free
  time, reap via non-blocking cudaEventQuery at allocate/failover entry and
  releaseAll (DSP_DIAG MEMORY: DEFER_DIRECT_FREE / DRAIN_DEFERRED_FREES /
  IMMEDIATE_DIRECT_FREE).
- Managed fallback is HOST-RESIDENT (preferred location CPU + SetAccessedBy
  pre-mapping + prefetch-to-host enqueued on the consumer stream): prefetching
  into the full device is a best-effort no-op and demand faults there are
  unserviceable (zero evictable frames).
- memsetSync memsets on the pool's resolved alloc stream (was
  cudaStreamPerThread — ordered against nothing; 906 use-before-alloc races
  flagged by compute-sanitizer --track-stream-ordered-races); the trailing
  host sync is dropped on that path.
- Plan-wide tl_dspGapStream pin: slot-by-slot warmup ops now share ONE stream
  with pool alloc/free resolution (previously compositeReplay-only), making
  the free-time event ordering exact.

Repro: Dsp700BgeWarmupReproTest — deterministic bounded failover method
(device-only ballast to the soft-limit floor, 8x256 plan, ~11s) plus the
production [32x512] shape with its capacity requirement documented.
Verified: pressure method green (exec #0 6.4s / #1 3.7s, no 700), poison
canary 49/49, SMOLDOC 250-token lateSteady 65.86 tok/s (65.39 baseline held).
…evice budget (#58)

The bound was decorative on the CUDA pool: Environment.setMaxDeviceMemory
stored _maxDeviceMemory but CoreConfig had NO getter (unlike maxPrimaryMemory/
maxSpecialMemory) and the pool never read it — a 110M-param encoder's single
DSP plan reserved 22.4 GB against a delivered 13.5 GB bound (1.66x), blocking
any multi-tenant GPU packing.

- CoreConfig.h / Environment.h: add maxDeviceMemory() getter (int64_t so the
  -1 "unbounded" sentinel survives).
- CudaMemoryPool::allocate: when a bound is set (>0), read this process's OWN
  pool usage (cudaMemPoolAttrUsedMemCurrent via getStats — NOT cudaMemGetInfo,
  so co-tenant processes don't distort the accounting) and route any allocation
  that would push past the budget to allocateFailover (host-resident managed /
  peer — safe since #57) instead of growing device reservation. DSP_DIAG MEMORY
  DEVICE_BUDGET_EXCEEDED. Skipped during capture; off by default (-1 → a single
  atomic load, byte-identical behavior for every existing caller).

Enforcement is via Nd4j.getEnvironment().setMaxDeviceMemory(bytes) or the
SD_MAX_DEVICE_BYTES env var. NOTE: -Dnd4j.environment.maxDeviceMemory is NOT a
real property — no dl4j code reads it; callers must use the setter/env var.

Test DspDeviceBudgetEnforcementTest drives the real setter: budget 3 GB,
allocate 6 GB device-only, pool usage plateaus at 2848 MB (all 24 allocs spill,
none fail). Poison canary 49/49. Perf A/B vs reverted HEAD: 61.1 vs 61.0 —
neutral (the block is gated off when unbounded).
…ph replay

Enable genuine device-aware DSP sharding across non-peer GPUs (RTX 4090 dev0
+ RTX 3070 Ti dev1). assignDevices() partitions op-slots by available memory;
each device's contiguous run becomes a captured CUDA-graph island, and
cross-device boundaries are handled by async gap migration. All 5
DspMultiGpuShardingTest cases (7 instances) pass byte-exact (maxAbsDiff=0.0)
vs the single-GPU reference, including the 10-iteration captured-graph replay
across the non-peer boundary.

Five root fixes for one class of bug: "assignDevices split a group of slots
that some pass assumed lived on one device."

1. FusionPass (FusionPass.cpp): never fuse an elementwise chain whose slots
   span a device boundary. A cross-device fused *tail* gets no independent
   output allocation, leaving an uninitialized NDArray that crashes at freeze
   (buildSegments -> estimateSlotOutputBytes -> lengthOf()). Covers all three
   fusion types (ELEMENTWISE_CHAIN / BIAS_ACTIVATION / MATMUL_BIAS_ACTIVATION).
2. Batched-GEMM grouping (NativeDynamicShapePlan_batchgemm.cu): a matmul may
   only join a batched group on the same device.
3. In-place buffer aliasing (NativePlanCompiler.cpp): alias a unary op's output
   to its input slot only when both are on the same device.
4. Input migration (DynamicShapePlanExecutor.java): route each external weight
   to its consuming segment's device instead of the single primary device.
   Fixes the weight ping-pong (dev1->dev0->dev1) that broke captured replay
   with CUDA error 700.
5. Capture stream (NativeDynamicShapePlan_gpubackend.cu): run a secondary-device
   island's whole compile/warmup/capture/replay/direct pipeline on its own
   cudaStreamPerThread so its Triton/cuBLAS kernels launch in the correct device
   context (was: device-0 stream -> cuLaunchKernel "invalid resource handle" /
   cuBLAS err13 during capture).

Plus the device-aware infrastructure they build on: per-segment device-bind
guard, per-device cuBLAS / JIT / NVRTC / PTX caches, cross-device constant
replication, and async cross-device input/output migration.

Tested: DspMultiGpuShardingTest 7/7 green (exact); focused single-GPU
regression DspConcurrentPlanSharingTest#testOutputBufferIsolation 3/3 green.

NOT run (flagging for follow-up): the full DSP core regression batch (11
classes per AGENTS.md), the DSP benchmark/perf sweep, and the broader
single-GPU DSP suites. All five fixes are gated on targetDeviceId>0 /
numDevices>1, so single-GPU takes no-op paths, but the broad gate was not run.

Known leftover: pre-existing temporary ground-truth diagnostics remain
(MMUL_DIAG / PRE_GEMM_DIAG gated to the secondary device, CONSTANT_DIAG,
SETZERO_DIAG); these do not affect single-GPU runs and should be stripped in a
follow-up. One edge case is deferred: identity/view ops aliasing a *migrated*
cross-segment input (a lifetime dangling the MLP does not exercise, but a
reshape/permute-heavy transformer could).
…re fixed

Remove the temporary diagnostics added while root-causing the multi-GPU
sharding failures (roots now fixed in the prior commit):
- MmulHelper.cu: MMUL_DIAG, PRE_GEMM_DIAG (incl. a cudaStreamSynchronize +
  stream-device probe), MMUL_DIAG_FAIL, and the mmEntryErr peek. Kept the
  labeled cuBLAS error throws and the per-device usableWorkspaceSize guard.
- ConstantHelper.cu: CONSTANT_DIAG in replicatePointer.
- DataBuffer.cu: SETZERO_DIAG in setToZeroBuffers (kept the device-1 stream fix).

No logic change. DspMultiGpuShardingTest still 7/7 green (maxAbsDiff=0.0,
all 10 replay iterations).
…layout

platformGetOutputForDevice0 migrated a device-N plan output to device 0 with
cudaMemcpyPeerAsync — a raw byte copy that ignores strides. A permute/transpose
output is F-strided (e.g. [1,16]) even when labeled order='c', so building the
device-0 copy with canonical strides relabeled those bytes as a different layout
(the transpose): a deterministic wrong result on the sharded path (the earlier
device/host "coherence"/flaky symptoms were all downstream of it). Build the copy
with arr's exact shapeInfo (copyStrides=true) so the migrated output is
bit-identical to single-GPU. No dup is used (arr's bytes are already correct),
which also avoids an async dup-on-unknown-stream ordering hazard (all-zero output).

Also: materializeViewSlot materializes a secondary-device view output ON that
device (uses the producing slot's targetDeviceId — DataBuffer deviceId() can be
stale 0) and drops a dup() double-wrap that leaked the dup result; same dup() leak
fixed in TritonGraphBackend_execute.

New: DspMultiGpuShardingTest.testRepeatedShardedViewOpsMatchSingleGpu (permute
output across the non-peer boundary, 10-iteration replay). Full class 8/8 green.
platformMigrateSegmentInputs materializes a cross-segment VIEW input via
arr->dup() (a view shares its parent's buffer, so unlike an owned output it can't
be raw-copied / copyStrides-cloned). NDArray::dup fills the copy on srcMat's
context stream (new NDArray(...,getContext()); assign(this) — NDArray.hXX ~5273),
NOT the target peer-copy stream, so the async cudaMemcpyPeerAsync raced the
still-filling buffer and would migrate zeros. Record a reusable event (never
destroyed — a per-call destroy invalidates the enqueued wait and hangs) on
srcMat->getContext()'s stream and have the target copy stream wait on it.

Gated on isView(): the non-view boundary inputs the MLP tests exercise are
unaffected (full DspMultiGpuShardingTest 8/8 green). The view-INPUT path has no
dedicated repro yet (the memory-proportion shard split lands the dev0->dev1
boundary on a non-view op in buildViewMlp); the fix is grounded in the dup impl
and mirrors the verified view-OUTPUT handling (6b3a44f), which instead uses
copyStrides because an owned output needs no dup.
testShardedViewInputBoundarySweep moves the dev0->dev1 shard boundary across op
positions by sweeping nd4j.dsp.nonP2pBudgetFraction (0.1..0.9) — assignDevices
assigns ops in execution order proportional to the per-device budget. Sharded
output stays bit-identical to single-GPU at every split ratio (maxAbsDiff=0.0),
adding split-ratio robustness coverage. Full class 9/9 green.

The migration-point MULTI_DEVICE diag reports slot/dev/isView/rank/len per
cross-segment input migration. The sweep confirms EVERY boundary migration is
isView=0: the memory-proportional split lands boundaries on memory-heavy ops
(matmul/tanh), never on a low-memory permute (view), so a view AS a cross-segment
input does not arise under the current algorithm — the view-input dup-ordering
fix (6fb68ca) is sound defensive code. The reachable view cases remain
covered: view-as-output (6b3a44f, bit-identical) and a view aliasing a
migrated input (buildViewMlp dev1 permutes).
…tead of demote-crash

#52 (0765462) added invalidateExternalViewSlotsOnReacquire() to null dangling
view slots minted over a previous borrower's post-exec-closed external buffers. It
resets the affected segments to WARMUP but left the PLAN phase at REPLAYING — so
execute()'s pre-execute steady-state check (isReplaying() && a segment not fully
replaying -> demotePlanPhase THROW, ~2842) fires BEFORE the anySegmentNeedsWarmup()
re-warm dispatch and throws "segment no longer satisfies replay steady state ...
kind=demotion". Deterministic on a recurrent GDN-hybrid model driven across many
generates on one shared pipeline: each generate is a new borrower over fresh
external KV/state buffers, so these external-fed views are invalidated every gen.

Complete the transition: unseal() (REPLAYING -> SHAPES_FROZEN) when slots were
cleared and the plan is replaying, so the next execute() re-warms + re-captures the
freshly-nulled views gracefully instead of throwing. SHAPES_FROZEN (not SLOT_BY_SLOT
/ unfreeze) — shapes are unchanged; only the views re-mint over new borrower memory.

Regression gate green (105 tests, 0F/0E): DspSlotLifecycleAuditTest (98, incl. #52's
testSlotPhaseVisibility + the borrower-switch poison test), DspLifecycleExhaustive-
Test#testMarkVariableAfterReplaying, DspConcurrentPlanSharingTest#testConcurrent-
RecaptureAfterMarkVariable, TestNativeDecodeLoopRegression#testReplayStateAfter-
NativeDecode (replay held). On the fixed-buffer GDN decode repro: no demotion,
coherent output. (The re-warm-per-generate throughput cost is addressed separately
by a buffer-reuse change to the fixed-buffer decode path.)
…100% token match

INT8-V2 makes the INT8 KV cache the live decode storage (float KV freed after
prefill quantize). Verified: leading-window 10/10 + full-horizon 100/100 token
match vs STATIC; eager kernel isolation 0.3-0.76% relErr incl. model geometry
(GQA 8:2, headDim 256, masked partial cache); V1 unregressed 32/32; DSP core
regression gate 1642/0/0 across all 11 classes.

Core design:
- ROW-INLINE scale layout [B, maxKvLen, kvH, headDim+4]: each row carries hd
  int8 values + its float32 scale INSIDE the logical tensor, so DSP ext-input
  staging, forced placeholder H2D, CUDA-graph capture and device migration all
  preserve it by construction (an appended-tail layout was proven to be dropped
  by staging, which copies only the logical tensor bytes).
- Null-scale contract across kvCacheQuantize / kvInPlaceWriteQuantisedBSHD /
  fusedGQADecodeQuantised (CUDA + CPU) + Triton MLIR emitFusedAttentionKernel
  INT8 path (i8 loads, SIToFP, per-row scale multiply, hd+4 row pitch).
- KV placeholders converted to INT8 (+ trailing dim relaxed) at prefill and
  RESTORED on pipeline close — the decoder SameDiff is shared across pipelines
  and a leaked INT8 declaration broke later non-quantized pipelines.
- Triton SegmentCacheKey now hashes cross-segment INPUT dtypes so INT8 and
  float compile distinct kernels for identical shapes; the three key-recovery
  sites (refreshArgTablesForReplay / copyConsolidatedArgTable / getGapSlots)
  gain a loose-match fallback (findCompiledSegmentAnyDtype) — a dtypeIndex_
  miss previously skipped the arg-table refresh silently and replayed with
  stale pointers (caught by the poison-guard gate test).
- Match-rate gate asserts the leading 10-token window; long-horizon identity
  is informational (per-step INT8 KV writes can legitimately flip greedy
  repetition attractors; numerical closeness is pinned by the eager tests).

Also lands the accumulated, tested decode-campaign surface this work builds
on (oMLX port waves): runtime quantized-weight matmul, lm_head last-position
prefill, typical-p/XTC sampling, rotating KV + sinks, paged KV + radix prefix
cache, MoE weighted-sum epilogue, ADR-0106 Phase-1 masked decode substrate,
GDN chunked prefill, generation session continuation (ADR-0105), and the
supporting ADRs (0105-0108) and platform tests.
…pe precision fixes

Promote typical-p and XTC logit filtering to first-class ops (typical_p_filter,
xtc_filter) via the op-codegen path instead of raw DynamicCustomOp builder
strings in SamplerUtils:
- headers/nn.h declarations + generic/nn/sampling_penalties.cpp op bodies
  (copy logits -> apply helper); CPU + CUDA helpers already present.
- op-codegen: NeuralNetwork.kt Op() defs -> generated NDNN/SDNN; hand op
  classes TypicalPFilter / XtcFilter registered in DifferentialFunctionClass-
  Holder; SamplerUtils routes through Nd4j.nn().typicalPFilter / xtcFilter.

Precision / correctness:
- Mask filtered logits to -inf via DataTypeUtils::infOrMax<T> (CUDA + CPU
  sampling_penalties + token_sample) so filtered tokens are exactly excluded
  (was -FLT_MAX, a finite sentinel); drop the over-strict xtcThreshold < 0.5
  REQUIRE_TRUE that rejected valid no-op configs.
- Accumulate sampling reductions in AccType (double stays double) rather than a
  hardcoded float, so double inputs are no longer silently narrowed.
- distillation_kl_loss CUDA backprop now uses the canonical helpers::softmax at
  rank-1 (hand-rolled softmax gave wrong grad on reshaped rank-3).

Verified on CUDA (nd4j-cuda-12.9): TestTypicalPAndXtcSampling 19/19,
TestAccTypeDoublePrecisionOps 12/12, TestBugFixes20260702 distillation family
6/6; DSP core regression gate 1643/0/0 across all 11 classes.
Snapshot the current CUDA/Triton DSP lifecycle fixes together with the Vulkan, SDX AOT, native-image, and backend test work.

Validated the DSP core gate at 1643/0/0 twice and the MTP oracle at 60/60.
Wire the reusable source-SDZ compile/cache module into Android accelerator and Google Tensor G5 provider packaging.
Require the model cache/profile classes in mobile AAR verification and remove an unused cache import.
Replace the legacy shell bundle construction path with the backend-neutral Java compiler/cache launcher and align the mobile guide and deployment ADR.
Recursive MTP drafting feeds the predictor its own output hidden — out of
distribution for single-module heads trained on trunk hidden only (measured
on Qwen3.5-0.8B: 41% acceptance at draft position 0, 0/51 at position 1 even
when position 0's token was correct; chained drafts collapse to filler
tokens). Cap the chain at the first position whose evaluations never accept
(unconditional per-row draft-vs-argmax counting, >=12 evals), in both CUDA
and CPU decode helpers; n-gram proposals untouched. KV_CACHE-gated
MTP_CHAIN_PROBE samples carry-in/token/hidden-out per chain exec riding the
existing acceptance sync.

Bench 250-tok: acceptance 10.6% -> 44.2% (197 proposed / 87 accepted / 161
steps), 24.8 tok/s, token-identical to greedy; 60-tok oracle stays lossless
and engaged.
refreshStaleViewWrappersInSegment guarded the cached OUTPUT wrapper with
safeHasValidShapeInfo but deref'd resolveViewInput results raw. On a
plan-cache reuse the previous borrower's teardown can leave a destructed
NDArray in outputSlots_, and input0->dataBuffer() on it SIGSEGVs — hit
deterministically by run-benchmark-mtp.sh --tokens 250 in the MTP prefill
plan's freeze-time refresh (prepareBundledMtp). Probe each resolved input;
dead inputs null out into the existing refresh-input-invalid demote path.
Bench-250 0/2 -> 2/2 green; bench-60 and MTP oracle stay green.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants