Skip to content

SuperSLM 1.8.0

Choose a tag to compare

@dansupergameprogrammer dansupergameprogrammer released this 25 Sep 11:51
· 428 commits to main since this release

SuperSLM 1.8.0

Released 2026-09-25.

SuperSLM 1.8.0 makes the GPU path report memory exhaustion as memory exhaustion. An allocation
failure during a GPU call, on a device that is not removed, returns SSLM_GPU_ALLOCATION_FAILED,
leaves the process's GPU command list closed, and leaves the context, the device and every other
handle usable. No token, save format, SslmGpuStatus ordinal or ABI struct changes. Several
statuses change for the same fault; read "Compatibility and required action" before upgrading a
host that maps GPU statuses.

The status contract a caller relies on

Stated once in include/superslm/gpu_1p0.h, beside SSLM_GPU_ALLOCATION_FAILED:

  • When. One of these fails inside a GPU call, on any heap, while the device is not reported
    removed at the moment the call classifies the failure:
    • a host allocation (std::bad_alloc, std::length_error);
    • a D3D12 call returning E_OUTOFMEMORY -- resource creation, Map, Reset, pipeline creation,
      and a Close or Signal the engine confirmed recovered;
    • the first-time setup of the process's submission device, at the first call that submits GPU
      work.
  • What stays valid. The context and the device, with the submission command list closed; every
    other sequence, model and adapter handle. An entry point with an output handle leaves it null. In
    sslm_decode_step_batch_gpu the status is that sequence's own out_statuses[i], and the other
    sequences are recorded and complete as usual.
  • What the caller may do next. Retry the call; it succeeds once memory is available. Retrying
    smaller is also valid: a smaller context_cap, or a model mapped without
    SSLM_GPU_RESIDENCY_HEAD_ON_DEVICE. A sequence the failed call was advancing must be reset
    (sslm_gpu_seq_reset) or restored before its next generation call: a prompt or schema prefill
    may have committed earlier sub-chunks; its prefill snapshot is empty, so
    sslm_gpu_seq_read_prefill_final_hidden returns SSLM_PREFILL_HIDDEN_UNAVAILABLE; a decode
    step's finish may have written K/V the host mirror does not hold.
  • Named residuals, both reported as SSLM_DEVICE_LOST, the conservative direction:
    • an allocation failure racing an unrelated device removal;
    • a stranded submission: memory exhausted during command recording where the command list
      cannot be confirmed Closed after Close, a retry and a confirm step, or a fence Signal or
      wait that cannot be confirmed. That is the terminal case gpu_1p0.h already documents: every
      later call on that list's context fails, and for the process's submission list, which every
      decode and prefill uses, every later submitting call in the process.
  • SSLM_DEVICE_LOST now means one of: the device is reported removed; a GPU operation failed
    for a reason other than memory (a missing shader, a D3D12 call failing with another HRESULT); a
    stranded submission; the submission device's first-time setup failed for a reason other than
    memory; or, as before, a saturated context cap in a prefill. It never means that memory ran out
    on a live device with the submission clean.
  • A failed first-time setup of the process's submission device returns one status per cause,
    the same from every entry point: SSLM_DEVICE_LOST, on every call for the life of the process,
    when setup failed for a reason other than memory (no adapter, device creation refused, a
    non-memory D3D12 failure); SSLM_GPU_ALLOCATION_FAILED when it ran out of memory, and the next
    call sets up again.

What changed

1.7.1 got the status wrong in three ways when memory ran out during a GPU call (TE-419, TE-421,
TE-423):

  • At many sites -- every D3D12 upload and readback heap allocation during an encode, and the host
    allocations of a submission's finish -- it reported the GPU as lost, although the engine had
    recovered.
  • At every host allocation inside a command-recording window it reported the allocation failure
    correctly but left the process's shared command list recording, so the next GPU call in the
    process, on whichever handle ran next, failed and reported the GPU as lost.
  • If memory ran out while the process set up its GPU submission device, the failure was cached for
    the life of the process, with a different permanent status depending on which call touched the
    device first (SSLM_DEVICE_LOST, SSLM_SEQUENCE_REJECTED or SSLM_SEQUENCE_KV_BUFFER_MISMATCH).

The fix, in src/gpu/:

  • Classified at the throw. SSLM_GPU_HR throws harness::GpuAllocationError for
    E_OUTOFMEMORY from any D3D12 call, and a plain std::runtime_error for every other failure.
  • One classifier at every site that turns an exception into a status. Rules, in order: a
    stranded submission is SSLM_DEVICE_LOST; a removed device is SSLM_DEVICE_LOST; an
    allocation failure is SSLM_GPU_ALLOCATION_FAILED; anything else keeps the site's
    non-allocation status.
  • The status is no longer discarded. The prompt and schema prefill twins, per-token decode,
    sslm_gpu_ready, the decode-step bridge and batch decode return the classified status.
  • Every command-recording window closes its list on every exception type, including a
    catch-all that closes the list and rethrows, and each submission tail contains every exception
    type, waiting its submitted work out before the buffers that work uses are released. Every
    Close on the 1.0 path goes through
    CloseListConfirmed: Close, a retry, then a confirm (Reset then Close), so a Close that
    fails with E_OUTOFMEMORY but did close the list is recoverable, and one that did not is
    reported as the stranded terminal case.
  • Every other try on the GPU path contains every exception type too. A decode's finish did
    not: a non-standard exception type from one of its host allocations left the sequence
    Submitted with its in-flight token freed, so reset, release and model unmap refused it as busy
    and the next sslm_gpu_ready on it read freed memory (also true of 1.7.1). Sequence restore, the
    batched prefill's chunk submission and every handle-creating site now end in the same clause.
    tools/ci/check_gpu_catch_totality.py fails CI on an outermost try under src/gpu/ whose
    handlers do not end in catch (...).
  • The internal forward status splits. SslmForwardStatus::GpuAllocationFailed now means only
    an allocation failure on a live device with the submission clean. A new, appended
    SslmForwardStatus::GpuOperationFailed carries every non-allocation GPU failure on a live device,
    so a missing .cso during recording, or finishing a call with no in-flight token, is never
    reported as out-of-memory.
  • harness::GetDevice() never throws, and a first-time setup that ran out of memory is retried
    by the next submission, at most one attempt per submission (a batch submits each sequence in
    turn, so a later sequence in the same batch may set the device up again).

No shader, dispatch, barrier, recording order, readback or host arithmetic on data changed: every
changed statement in src/gpu/ is inside a catch clause or a helper only catch clauses call, at a
throw site, in a status mapping or
status-carrying signature, in the GetDevice() holder and Device::Init()'s setup-failure record,
in the close and fence helpers, or the one flag set after the command list's Reset.

Compatibility and required action

Statuses change for the same fault.

  • SSLM_DEVICE_LOST becomes SSLM_GPU_ALLOCATION_FAILED for memory exhaustion on a live device,
    host or GPU, from sslm_gpu_context_create, sslm_gpu_model_map (with or without
    SSLM_GPU_RESIDENCY_HEAD_ON_DEVICE), sslm_gpu_adapter_map, sslm_gpu_seq_create,
    sslm_gpu_seq_restore, both prefill twins, sslm_decode_step_gpu, sslm_gpu_ready's
    out_status, sslm_decode_step_batch_gpu's out_statuses, the decode-step bridge and
    SslmGpuSeqFinishTokenForG5Bridge. A host that treated SSLM_DEVICE_LOST as the only GPU
    memory signal must handle SSLM_GPU_ALLOCATION_FAILED on these entry points as a recoverable,
    per-call failure.
  • A batch no longer marks the rest of its sequences SSLM_DEVICE_LOST when one sequence runs out
    of memory; only a device loss does that.
  • A failed first-time device setup returns SSLM_DEVICE_LOST from every entry point, on every
    call; one that ran out of memory returns SSLM_GPU_ALLOCATION_FAILED and is retried by the next
    call. In 1.7.1 decode-first read SSLM_SEQUENCE_REJECTED and restore-first
    SSLM_SEQUENCE_KV_BUFFER_MISMATCH, permanently.
  • SslmGpuSeqDecodeStepForG5Bridge returns the status its underlying decode calls reported --
    SSLM_GPU_ALLOCATION_FAILED, SSLM_SEQUENCE_REJECTED for a guard refusal,
    SSLM_DISPATCH_BUDGET_TOO_SMALL for a budget below one layer, SSLM_DEVICE_LOST for a device
    failure -- where 1.7.1 reported every such failure as SSLM_DEVICE_LOST.

No new SslmGpuStatus value, and no ordinal moves. A host whose build fails on an unmapped
SslmGpuStatus (the SuperSLM-Unreal plugin's G5 guard) needs no new arm; it needs to map the
changed meanings above deliberately.

SslmForwardStatus gains one appended enumerator, GpuOperationFailed (installed header
include/superslm/checked_chain_funnel.h). No existing ordinal moves. It never crosses the C ABI
(MapForwardStatus maps it to SSLM_ARTIFACT_REJECTED; the CPU path never produces it), but a
consumer that switches exhaustively over SslmForwardStatus must add an arm.

superslm_gpu::RestoreGpuSequenceState (installed header include/superslm/gpu_port.h) gains
a trailing, defaulted out-parameter, out_setup_status. Source-compatible; a caller compiled
against 1.7.1's header must be recompiled.

For consumers that include the internal harness header (src/gpu/d3d12_harness.h;
SuperSLM-Unreal does):

  • harness::GetDevice() still never throws, and is now declared noexcept.
  • Device::available is now std::atomic<bool>; reads of it compile unchanged. Device gains
    setup_failure (std::atomic<harness::SetupFailure>) and is no longer copyable.
  • An unavailable submission device may become available on a later GetDevice() call, after a
    first-time setup that ran out of memory.
  • Device::Init() no longer throws: it records a failed setup in available, init_error and
    setup_failure.
  • Device::CloseListWithRetry() is replaced by Device::CloseListConfirmed() (reports whether the
    list ended Closed) and Device::CloseListOrThrow().
  • SSLM_GPU_HR throws harness::GpuAllocationError, derived from std::runtime_error, for
    E_OUTOFMEMORY; an existing catch (const std::runtime_error&) still catches it.

GPU sequence saves are unchanged (SLM5), and no model-artifact reconversion is required.

Files changed

  • src/gpu/d3d12_harness.h -- GpuAllocationError, GpuSubmissionStrandedError and the fault
    classifier; SSLM_GPU_HR's typed failure; Device::Init()'s setup-failure record;
    CloseListConfirmed, CloseListOrThrow and the fence helpers; the GetDevice() holder; the
    classifier's removed-device query.
  • src/gpu/superslm_gpu.cpp -- the recording windows' cleanup (AbandonRecording) and catch-all;
    both submission tails; the finish; the chunk-open state's setup-failure status and list_opened
    flag; the non-final sub-chunk exclusion list; restore's round trip; the O11 seam's thrown type.
  • src/gpu/gpu_1p0.cpp -- the classifier at every handle-creating site; the device logits bundle
    and its run; context create; the decode mappings; the chunk submission's recorded fault and the
    twins' use of it; the decode-step drive; the API boundary's GpuAllocationError clause.
  • include/superslm/gpu_1p0.h -- the status contract, every paragraph that promised
    SSLM_DEVICE_LOST for an allocation failure, and the schema twin's list of its causes.
  • include/superslm/checked_chain_funnel.h, src/forward/checked_chain_funnel.cpp,
    src/sslm_abi.cpp -- GpuOperationFailed, its name, and its MapForwardStatus arm.
  • include/superslm/gpu_port.h -- RestoreGpuSequenceState's out-status and comments.
  • tests/ci/check_gpu_guard_status_parity.py -- its citations of the GPU statuses' return shape.
  • tools/ci/check_gpu_catch_totality.py, .github/workflows/tests.yml -- the gate that every
    outermost try under src/gpu/ ends in catch (...), and its CI step.
  • CMakeLists.txt, README.md, CHANGELOG.md, tests/install-consumer/CMakeLists.txt -- release
    bookkeeping for 1.8.0.