Skip to content

Commit 673ee65

Browse files
committed
merge: pr/345 -- GDN norm-gate, fused preamble and the compressed-state capability, family 5 (#345)
Completes the gfx1100 GDN slice: rocm_gdn_fused.hip (norm-gate, sigmoid gate, the fused AttnQkNormRopeGate preamble) plus a test at the real Qwen3.5-0.8B dims (Dh=256, rot=64 partial) rather than toy shapes. The one shared-seam change in the slice comes in here and is the right shape: CheckGdnCommon's compressed-state clause used to read `q.device.type == kCUDA`, and now asks Backend::SupportsCompressedGdnState(). CUDA answers true, so CUDA takes exactly the branch it took before; the op layer stops naming a device. It mirrors the SupportsCompressedConvState seam that already existed next to it. Fifth of the stacked slice. Conflicts resolved by union (additive vs additive). Refs #345, #41. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: Claude:claude-opus-5 [ClaudeCode]
2 parents 772cf98 + 07e9cc3 commit 673ee65

10 files changed

Lines changed: 586 additions & 7 deletions

File tree

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,6 +1329,7 @@ if(VLLM_CPP_HIP)
13291329
src/vt/rocm/rocm_gdn_conv.hip
13301330
src/vt/rocm/rocm_gdn_postconv.hip
13311331
src/vt/rocm/rocm_gdn_scan.hip
1332+
src/vt/rocm/rocm_gdn_fused.hip
13321333
src/vt/rocm/rocm_ops.hip)
13331334
if(VLLM_CPP_HIP_ARCHITECTURES)
13341335
set_source_files_properties(
@@ -1348,6 +1349,7 @@ if(VLLM_CPP_HIP)
13481349
src/vt/rocm/rocm_gdn_conv.hip
13491350
src/vt/rocm/rocm_gdn_postconv.hip
13501351
src/vt/rocm/rocm_gdn_scan.hip
1352+
src/vt/rocm/rocm_gdn_fused.hip
13511353
src/vt/rocm/rocm_ops.hip
13521354
PROPERTIES HIP_ARCHITECTURES "${VLLM_CPP_HIP_ARCHITECTURES}")
13531355
endif()

docs/FEATURES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ HTTP are not started.
219219
| CPU (x86, Arm i8mm; A76 assembly correct/default, llama speed gate open) |||||
220220
| Metal (Apple Silicon) |||||
221221
| Vulkan |||||
222-
| ROCm | ◐ (W0 community-verified on 4 gfx archs, #41; APU unified-memory fix landed, unverified; gfx1200 M0-M4 MET on one model, #269) | |||
222+
| ROCm | W0-W1 verified on 5 gfx archs; classic-dense AND GDN-hybrid e2e run all-native (strict CPU parity not met: near-tie regime, #269; GDN divergence characterization open) | Backend + platform + #140 ops + full GDN op set; ctest-green gfx1151/1103/1100/1201/1200 ([#41](https://github.com/mudler/vllm.cpp/issues/41)). APU UnifiedMemory fix verified. [ROCM.md](ROCM.md) |||
223223
| XPU / TPU |||||
224224
| Tenstorrent Blackhole |`ACTIVE`, OPT-125m STRICT 6/6 e2e; Qwen3-0.6B gate wired with device goldens, full 16x16 rerun pending ([spec](../.agents/specs/tenstorrent-backend.md), `BACKEND-TENSTORRENT`) ||||
225225

docs/USAGE.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,14 @@ The ROCm backend registers native ops family by family
4444
the indexed state I/O pair (`kGdnStateGather`/`kGdnStateScatter`), the causal
4545
conv1d pair (`kCausalConv1dFwd`/`kCausalConv1dUpdate`, incl. the exact-chunks
4646
descriptor form Qwen3.5 prefill passes), the fused post-conv glue
47-
(`kGdnPostConv`), and the gated-delta recurrence (`kGdnPrefill`/`kGdnDecode`,
48-
portable scan). On a
47+
(`kGdnPostConv`), the gated-delta recurrence (`kGdnPrefill`/`kGdnDecode`,
48+
portable scan), and the norm-gate/preamble ops (`kRmsNormGated`,
49+
`kSigmoidGateBf16`, `kAttnQkNormRopeGate`) — the full set Qwen3.5-class
50+
GDN-hybrid models call. Compressed conv/SSM state (bf16, the vLLM
51+
`mamba_cache_dtype` default) is advertised via the
52+
`SupportsCompressedConvState`/`SupportsCompressedGdnState` backend probes.
53+
Known limit on a separate path: the ROCm `MoeRouterTopK` kernel takes f32
54+
logits only, so MoE-bearing models still throw there. On a
4955
discrete card there is no CPU fallback tier, so a model whose layers call an op
5056
that is not registered yet fails loudly with `vt: no kernel for op N on device
5157
type 5` — that is the memory-safety design working, not a crash. Run with

include/vt/backend.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,13 @@ class Backend {
178178
// device, exactly as SupportsAsyncSampledTokenReadback did for the runner.
179179
virtual bool SupportsCompressedConvState() const { return false; }
180180

181+
// The GDN recurrent (SSM) state twin of the conv clause above: f16/bf16
182+
// [N,Hv,Dv,Dk] state addressed in place by the GdnPrefill/GdnDecode kernels,
183+
// read/written in f32 registers (vLLM's mamba_cache_dtype default is bf16).
184+
// CheckGdnCommon used to spell this as `device == kCUDA`; asking the backend
185+
// keeps the shared op layer device-agnostic.
186+
virtual bool SupportsCompressedGdnState() const { return false; }
187+
181188
// Optional graph/command capture (CUDA Graphs / Metal ICB / Vulkan CB).
182189
virtual bool SupportsGraphCapture() const { return false; }
183190
virtual void BeginCapture(Queue& q);

src/vt/cuda/cuda_backend.cu

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ class CudaBackend final : public Backend {
116116
// the branch it took before.
117117
bool SupportsCompressedConvState() const override { return true; }
118118

119+
// CheckGdnCommon's compressed-state clause used to name kCUDA directly; the
120+
// capability query keeps CUDA on exactly the branch it took before.
121+
bool SupportsCompressedGdnState() const override { return true; }
122+
119123
// --- Async-output primitives (ENG-ASYNC-SCHED W3, async_utils.py:12-70) ------
120124
// Page-locked host memory the copy engine DMAs into without a staging bounce
121125
// (a pageable destination would force cudaMemcpyAsync to block), plus real

src/vt/ops.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1642,12 +1642,17 @@ void CheckGdnCommon(const Queue& q, const Tensor& out, const Tensor& q_in, const
16421642
VT_CHECK(g.dtype == DType::kF32 && beta.dtype == DType::kF32,
16431643
std::string(name) + ": g/beta must be f32 (upstream keeps them f32)");
16441644
if (allow_compressed_state) {
1645+
// Asking the backend (Backend::SupportsCompressedGdnState) rather than
1646+
// naming a device — the same device-agnostic pattern CheckConvCommon
1647+
// already uses for the conv state. CUDA answers for its existing kernels;
1648+
// ROCm answers for the portable scan's f16/bf16 state arms.
1649+
const Backend* gdn_backend = TryGetBackend(q.device.type);
16451650
VT_CHECK(state.dtype == DType::kF32 ||
1646-
((state.dtype == DType::kF16 ||
1647-
state.dtype == DType::kBF16) &&
1648-
q.device.type == DeviceType::kCUDA),
1651+
((state.dtype == DType::kF16 || state.dtype == DType::kBF16) &&
1652+
gdn_backend != nullptr && gdn_backend->SupportsCompressedGdnState()),
16491653
std::string(name) +
1650-
": state must be f32, or fp16/bf16 on CUDA (in/out, in place; "
1654+
": state must be f32, or fp16/bf16 on a backend whose GDN kernels "
1655+
"support a compressed state in place (in/out, in place; "
16511656
"read/written in f32 registers)");
16521657
} else {
16531658
VT_CHECK(state.dtype == DType::kF32,

src/vt/rocm/rocm_backend.hip

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,14 @@ class RocmBackend final : public Backend {
238238
// host access is guaranteed by construction. This is the route
239239
// XNACK-less RDNA3 APUs take, where ground 1 reads false (issue #41 F6).
240240
// Probed, never inferred from the gfx name.
241+
// The GDN conv-update and scan kernels registered for this backend carry
242+
// bf16/f16 state arms (f32 register math, boundary conversion), so both
243+
// compressed-state capabilities answer true. Validated on gfx1100 against
244+
// the f32 oracle before advertisement (the M2 e2e run exercises the bf16
245+
// mamba_cache_dtype path end to end).
246+
bool SupportsCompressedConvState() const override { return true; }
247+
bool SupportsCompressedGdnState() const override { return true; }
248+
241249
bool UnifiedMemory() const override { return unified_memory_; }
242250

243251
private:

src/vt/rocm/rocm_gdn_fused.hip

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
// ROCm GDN norm/gate + fused attention preamble (BACKEND-ROCM-GDN-KERNELS
2+
// family 5, the final slice; issue #41). kRmsNormGated (gdn-semantics §5),
3+
// kSigmoidGateBf16 (ops.cpp contract: out bf16, attn f32/bf16, gate f32 — the
4+
// sigmoid input stays unrounded), kAttnQkNormRopeGate (fused full-attention
5+
// preamble: split q|gate + gemma/plain qk-RMSNorm + partial NeoX RoPE-from-
6+
// cache + gate passthrough). Donors: cuda_gdn.cu RmsNormGatedRowKernel and
7+
// cuda_ops.cu:1429 area; CPU oracles cpu_ops.cpp:1210/:2273/:956.
8+
// kSigmoidGateBf16 has NO CUDA registration — the CPU composite semantics are
9+
// the donor of record (the Vulkan lane's only native sibling).
10+
//
11+
// Gate: the norm-gate and preamble arms of test_backend_cross_device.cpp —
12+
// NMSE <= 5e-4 for the norm/preamble arithmetic; SigmoidGateBf16 bit-exact
13+
// (single multiply, same RNE store on both sides).
14+
15+
#include <hip/hip_bf16.h>
16+
#include <hip/hip_fp16.h>
17+
#include <hip/hip_runtime.h>
18+
19+
#include <cstdint>
20+
#include <stdexcept>
21+
#include <string>
22+
23+
#include "vt/ops.h"
24+
25+
namespace vt::rocm {
26+
namespace {
27+
28+
constexpr int kBlock = 256;
29+
30+
inline void Check(hipError_t err, const char* what) {
31+
if (err != hipSuccess) {
32+
throw std::runtime_error(std::string("vt rocm gdn: ") + what + ": " +
33+
hipGetErrorString(err));
34+
}
35+
}
36+
inline hipStream_t AsStream(const Queue& q) {
37+
return static_cast<hipStream_t>(q.handle);
38+
}
39+
inline unsigned GridFor(int64_t n) {
40+
if (n <= 0) return 1;
41+
const int64_t g = (n + kBlock - 1) / kBlock;
42+
return static_cast<unsigned>(g > 65535 ? 65535 : g);
43+
}
44+
45+
__device__ inline float Ld(const float* p, int64_t i) { return p[i]; }
46+
__device__ inline float Ld(const __half* p, int64_t i) { return __half2float(p[i]); }
47+
__device__ inline float Ld(const __hip_bfloat16* p, int64_t i) {
48+
return __bfloat162float(p[i]);
49+
}
50+
__device__ inline void St(float* p, int64_t i, float v) { p[i] = v; }
51+
__device__ inline void St(__half* p, int64_t i, float v) { p[i] = __float2half_rn(v); }
52+
__device__ inline void St(__hip_bfloat16* p, int64_t i, float v) {
53+
p[i] = __float2bfloat16(v);
54+
}
55+
__device__ inline float Silu(float x) { return x / (1.0f + expf(-x)); }
56+
__device__ inline float Sigmoid(float x) { return 1.0f / (1.0f + expf(-x)); }
57+
__device__ inline float GemmaNormElem(float v, float inv, float w, bool gemma) {
58+
float wj = w;
59+
if (gemma) wj += 1.0f;
60+
return v * inv * wj;
61+
}
62+
63+
// ── Norm/gate (donor cuda_gdn.cu RmsNormGatedRowKernel; CPU :1210/:2273) ─────
64+
template <typename Tx, typename Tout>
65+
__global__ void RmsNormGatedK(Tout* out, const Tx* x, const Tx* gate,
66+
const Tx* w, int64_t rows, int64_t d,
67+
int64_t gate_group, int64_t gate_outer, float eps,
68+
bool sigmoid_gate) {
69+
const int64_t i = static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x;
70+
if (i >= rows) return;
71+
float sumsq = 0.0f;
72+
for (int64_t j = 0; j < d; ++j) {
73+
const float v = Ld(x, i * d + j);
74+
sumsq += v * v;
75+
}
76+
const float inv = 1.0f / sqrtf(sumsq / static_cast<float>(d) + eps);
77+
const int64_t gbase = (i / gate_group) * gate_outer + (i % gate_group) * d;
78+
for (int64_t j = 0; j < d; ++j) {
79+
const float z = Ld(gate, gbase + j);
80+
const float act = sigmoid_gate ? Sigmoid(z) : Silu(z);
81+
St(out, i * d + j, Ld(x, i * d + j) * inv * Ld(w, j) * act);
82+
}
83+
}
84+
85+
// ops.cpp contract: out bf16, attn f32-or-bf16, gate f32 (unrounded sigmoid
86+
// input). Tattn only.
87+
template <typename Tattn>
88+
__global__ void SigmoidGateK(__hip_bfloat16* out, const Tattn* attn,
89+
const float* gate, int64_t n) {
90+
const int64_t step = static_cast<int64_t>(gridDim.x) * blockDim.x;
91+
for (int64_t i = static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x;
92+
i < n; i += step) {
93+
St(out, i, Ld(attn, i) * Sigmoid(gate[i]));
94+
}
95+
}
96+
97+
template <typename Tsrc, typename Tqk, typename Tgate>
98+
__global__ void AttnQkNormRopeGateK(Tqk* q_out, Tqk* k_out, Tgate* gate_out,
99+
const Tsrc* qgate, const Tsrc* kf,
100+
const float* q_norm, const float* k_norm,
101+
const float* cos_sin, int64_t t, int64_t hq,
102+
int64_t hkv, int64_t dh, int64_t qgate_stride,
103+
int64_t kf_stride, int rot, float eps,
104+
bool gemma) {
105+
const int64_t half = rot / 2;
106+
const int64_t items = t * (hq + hkv);
107+
const int64_t step = static_cast<int64_t>(gridDim.x) * blockDim.x;
108+
for (int64_t item = static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x;
109+
item < items; item += step) {
110+
const int64_t tok = item / (hq + hkv);
111+
const int64_t h = item % (hq + hkv);
112+
const float* cs = cos_sin + tok * rot;
113+
int64_t src_off, out_off;
114+
if (h < hq) {
115+
src_off = tok * qgate_stride + h * 2 * dh;
116+
out_off = (tok * hq + h) * dh;
117+
for (int64_t j = 0; j < dh; ++j)
118+
St(gate_out, out_off + j, Ld(qgate, src_off + dh + j));
119+
float ss = 0.0f;
120+
for (int64_t j = 0; j < dh; ++j) {
121+
const float v = Ld(qgate, src_off + j);
122+
ss += v * v;
123+
}
124+
const float inv = 1.0f / sqrtf(ss / static_cast<float>(dh) + eps);
125+
for (int64_t j = 0; j < dh; ++j) {
126+
if (j < half) {
127+
const float ni = GemmaNormElem(Ld(qgate, src_off + j), inv, q_norm[j], gemma);
128+
const float nih =
129+
GemmaNormElem(Ld(qgate, src_off + j + half), inv, q_norm[j + half], gemma);
130+
St(q_out, out_off + j, ni * cs[j] - nih * cs[half + j]);
131+
} else if (j < rot) {
132+
const int64_t i = j - half;
133+
const float ni = GemmaNormElem(Ld(qgate, src_off + i), inv, q_norm[i], gemma);
134+
const float nih =
135+
GemmaNormElem(Ld(qgate, src_off + i + half), inv, q_norm[i + half], gemma);
136+
St(q_out, out_off + j, ni * cs[half + i] + nih * cs[i]);
137+
} else {
138+
St(q_out, out_off + j,
139+
GemmaNormElem(Ld(qgate, src_off + j), inv, q_norm[j], gemma));
140+
}
141+
}
142+
} else {
143+
const int64_t hk_i = h - hq;
144+
src_off = tok * kf_stride + hk_i * dh;
145+
out_off = (tok * hkv + hk_i) * dh;
146+
float ss = 0.0f;
147+
for (int64_t j = 0; j < dh; ++j) {
148+
const float v = Ld(kf, src_off + j);
149+
ss += v * v;
150+
}
151+
const float inv = 1.0f / sqrtf(ss / static_cast<float>(dh) + eps);
152+
for (int64_t j = 0; j < dh; ++j) {
153+
if (j < half) {
154+
const float ni = GemmaNormElem(Ld(kf, src_off + j), inv, k_norm[j], gemma);
155+
const float nih =
156+
GemmaNormElem(Ld(kf, src_off + j + half), inv, k_norm[j + half], gemma);
157+
St(k_out, out_off + j, ni * cs[j] - nih * cs[half + j]);
158+
} else if (j < rot) {
159+
const int64_t i = j - half;
160+
const float ni = GemmaNormElem(Ld(kf, src_off + i), inv, k_norm[i], gemma);
161+
const float nih =
162+
GemmaNormElem(Ld(kf, src_off + i + half), inv, k_norm[i + half], gemma);
163+
St(k_out, out_off + j, ni * cs[half + i] + nih * cs[i]);
164+
} else {
165+
St(k_out, out_off + j,
166+
GemmaNormElem(Ld(kf, src_off + j), inv, k_norm[j], gemma));
167+
}
168+
}
169+
}
170+
}
171+
}
172+
173+
} // namespace
174+
175+
void RmsNormGatedKernelRocm(Queue& q, Tensor& out, const Tensor& x, const Tensor& gate,
176+
const Tensor& w, const RmsNormGatedArgs& args) {
177+
VT_CHECK(x.dtype == DType::kF32 || x.dtype == DType::kBF16,
178+
"rocm rmsnorm_gated: unsupported input dtype (f32/bf16 only)");
179+
VT_CHECK(gate.dtype == x.dtype && w.dtype == x.dtype,
180+
"rocm rmsnorm_gated: gate/weight dtype must match x");
181+
const int64_t d = x.shape[x.rank - 1];
182+
const int64_t t = d == 0 ? 0 : x.Numel() / d;
183+
if (t == 0 || d == 0) return;
184+
const int64_t gate_group = gate.rank == 3 ? gate.shape[1] : 1;
185+
const int64_t gate_outer = gate.stride[0];
186+
hipStream_t s = AsStream(q);
187+
const unsigned grid = static_cast<unsigned>(t);
188+
if (x.dtype == DType::kF32) {
189+
if (out.dtype == DType::kF32) {
190+
RmsNormGatedK<float, float><<<grid, 1, 0, s>>>(
191+
out.Ptr<float>(), x.Ptr<float>(), gate.Ptr<float>(), w.Ptr<float>(), t, d,
192+
gate_group, gate_outer, args.eps, args.sigmoid_gate);
193+
} else {
194+
RmsNormGatedK<float, __hip_bfloat16><<<grid, 1, 0, s>>>(
195+
out.Ptr<__hip_bfloat16>(), x.Ptr<float>(), gate.Ptr<float>(), w.Ptr<float>(),
196+
t, d, gate_group, gate_outer, args.eps, args.sigmoid_gate);
197+
}
198+
} else {
199+
if (out.dtype == DType::kF32) {
200+
RmsNormGatedK<__hip_bfloat16, float><<<grid, 1, 0, s>>>(
201+
out.Ptr<float>(), x.Ptr<__hip_bfloat16>(), gate.Ptr<__hip_bfloat16>(),
202+
w.Ptr<__hip_bfloat16>(), t, d, gate_group, gate_outer, args.eps,
203+
args.sigmoid_gate);
204+
} else {
205+
RmsNormGatedK<__hip_bfloat16, __hip_bfloat16><<<grid, 1, 0, s>>>(
206+
out.Ptr<__hip_bfloat16>(), x.Ptr<__hip_bfloat16>(), gate.Ptr<__hip_bfloat16>(),
207+
w.Ptr<__hip_bfloat16>(), t, d, gate_group, gate_outer, args.eps,
208+
args.sigmoid_gate);
209+
}
210+
}
211+
Check(hipGetLastError(), "rmsnorm_gated launch");
212+
}
213+
214+
void SigmoidGateBf16KernelRocm(Queue& q, Tensor& out, const Tensor& attn,
215+
const Tensor& gate) {
216+
// Contract (ops.cpp:3350): out bf16, attn f32/bf16, gate f32.
217+
VT_CHECK(out.dtype == DType::kBF16, "rocm sigmoid_gate_bf16: out must be bf16");
218+
VT_CHECK(gate.dtype == DType::kF32, "rocm sigmoid_gate_bf16: gate must be f32");
219+
const int64_t n = out.Numel();
220+
if (n == 0) return;
221+
hipStream_t s = AsStream(q);
222+
if (attn.dtype == DType::kBF16) {
223+
SigmoidGateK<<<GridFor(n), kBlock, 0, s>>>(out.Ptr<__hip_bfloat16>(),
224+
attn.Ptr<__hip_bfloat16>(),
225+
gate.Ptr<float>(), n);
226+
} else {
227+
SigmoidGateK<<<GridFor(n), kBlock, 0, s>>>(out.Ptr<__hip_bfloat16>(),
228+
attn.Ptr<float>(),
229+
gate.Ptr<float>(), n);
230+
}
231+
Check(hipGetLastError(), "sigmoid_gate launch");
232+
}
233+
234+
void AttnQkNormRopeGateKernelRocm(Queue& q, Tensor& q_out, Tensor& k_out, Tensor& gate_out,
235+
const Tensor& qgate, const Tensor& kf,
236+
const Tensor& q_norm, const Tensor& k_norm,
237+
const Tensor& cos_sin, const RmsNormArgs& na,
238+
const RopeArgs& ra) {
239+
const int64_t t = q_out.shape[0], hq = q_out.shape[1], dh = q_out.shape[2];
240+
const int64_t hkv = k_out.shape[1];
241+
if (t == 0) return;
242+
const int64_t items = t * (hq + hkv);
243+
hipStream_t s = AsStream(q);
244+
auto launch = [&](auto src_tag, auto qk_tag, auto gate_tag) {
245+
using Tsrc = decltype(src_tag);
246+
using Tqk = decltype(qk_tag);
247+
using Tgate = decltype(gate_tag);
248+
AttnQkNormRopeGateK<Tsrc, Tqk, Tgate><<<GridFor(items), kBlock, 0, s>>>(
249+
q_out.Ptr<Tqk>(), k_out.Ptr<Tqk>(), gate_out.Ptr<Tgate>(), qgate.Ptr<Tsrc>(),
250+
kf.Ptr<Tsrc>(), q_norm.Ptr<float>(), k_norm.Ptr<float>(), cos_sin.Ptr<float>(),
251+
t, hq, hkv, dh, qgate.stride[0], kf.stride[0], ra.rotary_dim, na.eps, na.gemma);
252+
};
253+
// Validated combos (mirror the CUDA lane): (f32 src, f32 out); (bf16 src,
254+
// bf16 out + bf16 gate); (bf16 src, bf16 out + f32 gate).
255+
if (qgate.dtype == DType::kBF16) {
256+
if (gate_out.dtype == DType::kF32) {
257+
launch(__hip_bfloat16{}, __hip_bfloat16{}, float{});
258+
} else {
259+
launch(__hip_bfloat16{}, __hip_bfloat16{}, __hip_bfloat16{});
260+
}
261+
} else {
262+
launch(float{}, float{}, float{});
263+
}
264+
Check(hipGetLastError(), "attn_qk_norm_rope_gate launch");
265+
}
266+
267+
} // namespace vt::rocm

0 commit comments

Comments
 (0)