// SDD-3 Layer B5 — aux→encoder gradient accumulator.
//
// Single-purpose kernel that performs an element-wise += of a source
// vector into a destination vector. Used by `PerceptionTrainer` to fold
// the auxiliary trunk's `grad_x` (computed at each K-step of the
// reverse-order aux backward) into the main `grad_h_enriched_seq_t_d`
// slot when the asymmetric stop-grad is LIFTED (E3 design memo).
//
// Why a dedicated kernel rather than reusing an existing add primitive:
//   * The other ml-alpha kernels that mutate `grad_h_enriched_seq` write
//     OVERWRITE-style (cfc_step_bwd produces the slot's gradient by
//     itself). Adding aux on top requires an independent += launch.
//   * Per `feedback_no_atomicadd.md` — this is purely position-local
//     (one thread per (b, c)) so no atomics; the destination slot is
//     write-once per launch.
//
// Buffer contract:
//   * `dst`  — `[n]` destination, accumulated in-place: `dst[i] += src[i]`
//   * `src`  — `[n]` source (read-only)
//   * `n`    — element count
//
// Launch: grid=(ceil(n/256), 1, 1), block=(256, 1, 1), shared=0.
//
// Constraints honoured:
//   * `feedback_no_atomicadd.md` — single-thread-per-element write.
//   * `pearl_no_host_branches_in_captured_graph` — the captured graph's
//     scalar `n` is bound at capture time; replay reads the same value.
//   * `feedback_no_nvrtc.md` — pre-compiled cubin via build.rs.

extern "C" __global__ void aux_vec_add_inplace(
    float* __restrict__ dst,
    const float* __restrict__ src,
    int n
) {
    const int i = blockIdx.x * blockDim.x + threadIdx.x;
    if (i >= n) return;
    dst[i] += src[i];
}
