opencl: add Adreno xmem SDPA path (#26331)

* opencl: add Adreno xmem SDPA path

Assisted-by: Codex

* Removed the Adreno-specific queue profiling override

* Clean up formatting

* 修复数值误差优化gqa/mask attn

Assisted-by: Codex

* add env GGML_OPENCL_XMEM_SDPA

Assisted-by: OpenAI Codex

---------

Co-authored-by: happyyzy <happyyzy@users.noreply.github.com>
This commit is contained in:
yzyyzyhhh
2026-09-04 10:12:05 -07:00
committed by GitHub
co-authored by happyyzy
parent 8b4b3558f1
commit 4acf4a4cb8
3 changed files with 1555 additions and 1 deletions
+1 -1
View File
@@ -238,7 +238,7 @@ set(GGML_OPENCL_KERNELS
)
if (GGML_OPENCL_USE_ADRENO_KERNELS)
list(APPEND GGML_OPENCL_KERNELS gemm_xmem_f16_f32_os8)
list(APPEND GGML_OPENCL_KERNELS gemm_xmem_f16_f32_os8 sdpa_xmem_f32_f16_os8)
endif ()
foreach (K ${GGML_OPENCL_KERNELS})
+683
View File
@@ -417,6 +417,10 @@ static void populateProfilingInfo(
struct ggml_backend_opencl_context;
#ifdef GGML_OPENCL_USE_ADRENO_KERNELS
static void ggml_cl_adreno_xmem_attn_release_scratch(ggml_backend_opencl_context * backend_ctx);
#endif
// backend device context
struct ggml_backend_opencl_device_context {
cl_platform_id platform;
@@ -537,6 +541,54 @@ struct ggml_opencl_fa_kernels {
std::set<std::pair<int, std::pair<int, int>>> variant_attempted;
};
#ifdef GGML_OPENCL_USE_ADRENO_KERNELS
struct ggml_cl_adreno_xmem_attn_scratch {
cl_mem q_img = nullptr;
cl_mem k_img = nullptr;
cl_mem v_img = nullptr;
cl_mem out_img = nullptr;
cl_mem k_transpose_buf = nullptr;
cl_mem k_transpose_img1d = nullptr;
cl_mem k_packed_buf = nullptr;
cl_mem v_packed_buf = nullptr;
cl_mem score_buf = nullptr;
cl_mem prob_buf = nullptr;
cl_mem score_img1d = nullptr;
cl_mem prob_img1d = nullptr;
cl_mem softmax_stats_img2d = nullptr;
cl_mem xmem_qk = nullptr;
cl_mem xmem_pv = nullptr;
int n_q = 0;
int n_kv = 0;
int n_kv_padded = 0;
int d_head_q = 0;
int d_head_v = 0;
int q_width = 0;
int kv_heads_total = 0;
};
struct ggml_cl_adreno_xmem_attn_state {
bool compiled = false;
bool logged = false;
cl_kernel kernel_q_f32_to_img_scaled = nullptr;
cl_kernel kernel_kv_f32_to_img_gqa = nullptr;
cl_kernel kernel_kv_f16_to_img_gqa = nullptr;
cl_kernel kernel_img_to_f32 = nullptr;
cl_kernel kernel_k_gather = nullptr;
cl_kernel kernel_pack_k = nullptr;
cl_kernel kernel_qk_gemm = nullptr;
cl_kernel kernel_softmax_reduce_basic = nullptr;
cl_kernel kernel_softmax_apply_basic = nullptr;
cl_kernel kernel_mask_scores = nullptr;
cl_kernel kernel_pack_v = nullptr;
cl_kernel kernel_pv_gemm = nullptr;
ggml_cl_adreno_xmem_attn_scratch scratch;
};
#endif
// backend context
struct ggml_backend_opencl_context {
int ref_count;
@@ -762,6 +814,9 @@ struct ggml_backend_opencl_context {
cl_kernel kernel_soft_max, kernel_soft_max_4;
cl_kernel kernel_soft_max_f16, kernel_soft_max_4_f16;
ggml_opencl_fa_kernels fa;
#ifdef GGML_OPENCL_USE_ADRENO_KERNELS
ggml_cl_adreno_xmem_attn_state adreno_xmem_attn;
#endif
cl_kernel kernel_get_rows_f32, kernel_get_rows_f16, kernel_get_rows_q4_0;
cl_kernel kernel_set_rows_f32_i64, kernel_set_rows_f32_i32, kernel_set_rows_f16_i64, kernel_set_rows_f16_i32;
cl_kernel kernel_set_rows_q8_0_i64, kernel_set_rows_q8_0_i32;
@@ -1176,6 +1231,9 @@ struct ggml_backend_opencl_context {
if (kv.second.image) { CL_CHECK(clReleaseMemObject(kv.second.image)); }
}
dequant_f16_pool.clear();
#ifdef GGML_OPENCL_USE_ADRENO_KERNELS
ggml_cl_adreno_xmem_attn_release_scratch(this);
#endif
}
}
};
@@ -2343,6 +2401,49 @@ static void load_cl_kernels(ggml_backend_opencl_context *backend_ctx) {
}
#endif // GGML_OPENCL_USE_ADRENO_KERNELS
#ifdef GGML_OPENCL_USE_ADRENO_KERNELS
// Adreno xmem SDPA
if (backend_ctx->gpu_family == GPU_FAMILY::ADRENO) {
#ifdef GGML_OPENCL_EMBED_KERNELS
const std::string kernel_src {
#include "sdpa_xmem_f32_f16_os8.cl.h"
};
#else
const std::string kernel_src = read_file("sdpa_xmem_f32_f16_os8.cl");
#endif
cl_program program = build_program_from_source(backend_ctx, kernel_src.c_str(), compile_opts);
auto & xmem_attn = backend_ctx->adreno_xmem_attn;
CL_CHECK((xmem_attn.kernel_q_f32_to_img_scaled =
clCreateKernel(program, "adreno_xmem_attn_q_f32_to_img_scaled", &err), err));
CL_CHECK((xmem_attn.kernel_kv_f32_to_img_gqa =
clCreateKernel(program, "adreno_xmem_attn_kv_f32_to_img_gqa", &err), err));
CL_CHECK((xmem_attn.kernel_kv_f16_to_img_gqa =
clCreateKernel(program, "adreno_xmem_attn_kv_f16_to_img_gqa", &err), err));
CL_CHECK((xmem_attn.kernel_img_to_f32 =
clCreateKernel(program, "adreno_xmem_attn_img_to_f32", &err), err));
CL_CHECK((xmem_attn.kernel_k_gather =
clCreateKernel(program, "adreno_xmem_attn_k_gather", &err), err));
CL_CHECK((xmem_attn.kernel_pack_k =
clCreateKernel(program, "adreno_xmem_attn_pack_k", &err), err));
CL_CHECK((xmem_attn.kernel_qk_gemm =
clCreateKernel(program, "adreno_xmem_attn_qk_gemm", &err), err));
CL_CHECK((xmem_attn.kernel_softmax_reduce_basic =
clCreateKernel(program, "adreno_xmem_attn_softmax_reduce_basic", &err), err));
CL_CHECK((xmem_attn.kernel_softmax_apply_basic =
clCreateKernel(program, "adreno_xmem_attn_softmax_apply_basic", &err), err));
CL_CHECK((xmem_attn.kernel_mask_scores =
clCreateKernel(program, "adreno_xmem_attn_mask_scores", &err), err));
CL_CHECK((xmem_attn.kernel_pack_v =
clCreateKernel(program, "adreno_xmem_attn_pack_v", &err), err));
CL_CHECK((xmem_attn.kernel_pv_gemm =
clCreateKernel(program, "adreno_xmem_attn_pv_gemm", &err), err));
CL_CHECK(clReleaseProgram(program));
xmem_attn.compiled = true;
GGML_LOG_CONT(".");
}
#endif // GGML_OPENCL_USE_ADRENO_KERNELS
// mul_mm_f32_f32_l4_lm
{
#ifdef GGML_OPENCL_EMBED_KERNELS
@@ -15927,6 +16028,581 @@ static constexpr int FD_MAX_N_Q_MULTI = 8;
static constexpr int FD_MQ_KV_PER_SPLIT = 256;
static constexpr int FD_MQ_MAX_SPLITS = 128;
#ifdef GGML_OPENCL_USE_ADRENO_KERNELS
struct ggml_cl_adreno_xmem_attn_schedule {
int qk_lws0 = 256;
int qk_lws2 = 1;
int softmax_reduce_lws0 = 256;
int softmax_apply_lws0 = 64;
int softmax_apply_lws2 = 4;
int pv_lws0 = 64;
int pv_lws2 = 4;
};
static inline size_t ggml_cl_round_up(size_t x, size_t a) {
return ((x + a - 1) / a) * a;
}
static inline int ggml_cl_round_up_div(int x, int y) {
return (x + y - 1) / y;
}
static inline void ggml_cl_set_arg_int4(cl_kernel kernel, cl_uint index, int x, int y, int z, int w) {
struct { int x, y, z, w; } value { x, y, z, w };
CL_CHECK(clSetKernelArg(kernel, index, sizeof(value), &value));
}
static cl_mem ggml_cl_make_image2d_half4(cl_context context, cl_mem_flags flags, size_t width, size_t height) {
cl_int err = CL_SUCCESS;
cl_image_format format = { CL_RGBA, CL_HALF_FLOAT };
cl_image_desc desc = {};
desc.image_type = CL_MEM_OBJECT_IMAGE2D;
desc.image_width = width;
desc.image_height = height;
cl_mem image = clCreateImage(context, flags, &format, &desc, nullptr, &err);
CL_CHECK(err);
return image;
}
static cl_mem ggml_cl_make_image1d_buffer_half4(cl_context context, cl_mem_flags flags, size_t width, cl_mem backing_buffer) {
cl_int err = CL_SUCCESS;
cl_image_format format = { CL_RGBA, CL_HALF_FLOAT };
cl_image_desc desc = {};
desc.image_type = CL_MEM_OBJECT_IMAGE1D_BUFFER;
desc.image_width = width;
desc.buffer = backing_buffer;
cl_mem image = clCreateImage(context, flags, &format, &desc, nullptr, &err);
CL_CHECK(err);
return image;
}
static void ggml_cl_release_mem(cl_mem & mem) {
if (mem != nullptr) {
CL_CHECK(clReleaseMemObject(mem));
mem = nullptr;
}
}
static void ggml_cl_adreno_xmem_attn_release_scratch(ggml_backend_opencl_context * backend_ctx) {
auto & s = backend_ctx->adreno_xmem_attn.scratch;
ggml_cl_release_mem(s.q_img);
ggml_cl_release_mem(s.k_img);
ggml_cl_release_mem(s.v_img);
ggml_cl_release_mem(s.out_img);
ggml_cl_release_mem(s.k_transpose_img1d);
ggml_cl_release_mem(s.k_transpose_buf);
ggml_cl_release_mem(s.k_packed_buf);
ggml_cl_release_mem(s.v_packed_buf);
ggml_cl_release_mem(s.score_img1d);
ggml_cl_release_mem(s.prob_img1d);
ggml_cl_release_mem(s.score_buf);
ggml_cl_release_mem(s.prob_buf);
ggml_cl_release_mem(s.softmax_stats_img2d);
ggml_cl_release_mem(s.xmem_qk);
ggml_cl_release_mem(s.xmem_pv);
s = {};
}
static ggml_cl_adreno_xmem_attn_schedule ggml_cl_adreno_xmem_attn_select_schedule(
const ggml_backend_opencl_context * backend_ctx,
int n_q,
int n_kv,
int heads_total,
int q_width,
int gqa_ratio) {
const bool big_h = heads_total >= 8;
ggml_cl_adreno_xmem_attn_schedule sched;
if (gqa_ratio == 1) {
if (n_q >= 512) { sched.qk_lws0 = 512; }
else if (n_q >= 256) { sched.qk_lws0 = 128; }
else { sched.qk_lws0 = 64; }
sched.qk_lws2 = (big_h && n_q >= 512) ? 2 : 1;
} else {
if (q_width >= 2048) { sched.qk_lws0 = 512; }
else if (q_width >= 256) { sched.qk_lws0 = 128; }
else { sched.qk_lws0 = 64; }
sched.qk_lws2 = MIN(8, (int) backend_ctx->max_workgroup_size / sched.qk_lws0);
}
if (n_kv >= 2048) { sched.softmax_reduce_lws0 = 1024; }
else if (n_kv >= 512) { sched.softmax_reduce_lws0 = big_h ? 256 : 512; }
else { sched.softmax_reduce_lws0 = 256; }
if (n_kv < 256) { sched.softmax_apply_lws0 = 64; }
else { sched.softmax_apply_lws0 = big_h ? 128 : 64; }
sched.softmax_apply_lws2 = n_kv >= 512 ? 8 : 4;
if (n_q < 256) { sched.pv_lws0 = 64; }
else { sched.pv_lws0 = big_h ? 128 : 64; }
sched.pv_lws2 = big_h ? 8 : (n_q <= 256 ? 8 : 4);
const int max_wg = (int) backend_ctx->max_workgroup_size;
auto fix = [&](int & l0, int & l2) {
while (l0 * l2 > max_wg) {
if (l2 > 1) { l2 /= 2; }
else if (l0 > 32) { l0 /= 2; }
else { break; }
}
};
fix(sched.qk_lws0, sched.qk_lws2);
fix(sched.softmax_apply_lws0, sched.softmax_apply_lws2);
fix(sched.pv_lws0, sched.pv_lws2);
while (sched.softmax_reduce_lws0 > max_wg) {
sched.softmax_reduce_lws0 /= 2;
}
return sched;
}
static bool ggml_cl_adreno_xmem_attn_prepare(
ggml_backend_opencl_context * backend_ctx,
int n_q,
int n_kv,
int d_head_q,
int d_head_v,
int n_head,
int n_head_kv,
int n_batch) {
auto & s = backend_ctx->adreno_xmem_attn.scratch;
const int gqa_ratio = n_head / n_head_kv;
const int q_width = n_q * gqa_ratio;
const int kv_heads_total = n_head_kv * n_batch;
const int n_kv_padded = (int) ggml_cl_round_up((size_t) n_kv, 32);
if (s.q_img != nullptr &&
s.n_q == n_q &&
s.n_kv == n_kv &&
s.n_kv_padded == n_kv_padded &&
s.d_head_q == d_head_q &&
s.d_head_v == d_head_v &&
s.q_width == q_width &&
s.kv_heads_total == kv_heads_total) {
return true;
}
ggml_cl_adreno_xmem_attn_release_scratch(backend_ctx);
const int qpack = d_head_q / 4;
const int vpack = d_head_v / 4;
const int npack = n_kv_padded / 4;
const size_t q_img_h = (size_t) kv_heads_total * qpack;
const size_t v_img_h = (size_t) kv_heads_total * vpack;
s.q_img = ggml_cl_make_image2d_half4(backend_ctx->context, CL_MEM_READ_WRITE, (size_t) q_width, q_img_h);
s.k_img = ggml_cl_make_image2d_half4(backend_ctx->context, CL_MEM_READ_WRITE, (size_t) n_kv_padded, q_img_h);
s.v_img = ggml_cl_make_image2d_half4(backend_ctx->context, CL_MEM_READ_WRITE, (size_t) n_kv_padded, v_img_h);
s.out_img = ggml_cl_make_image2d_half4(backend_ctx->context, CL_MEM_READ_WRITE, (size_t) q_width, v_img_h);
const size_t k_transpose_half4_elems = (size_t) npack * kv_heads_total * d_head_q;
s.k_transpose_buf = clCreateBuffer(backend_ctx->context, CL_MEM_READ_WRITE, k_transpose_half4_elems * sizeof(uint16_t) * 4, nullptr, nullptr);
GGML_ASSERT(s.k_transpose_buf != nullptr);
s.k_transpose_img1d = ggml_cl_make_image1d_buffer_half4(backend_ctx->context, CL_MEM_READ_ONLY, k_transpose_half4_elems, s.k_transpose_buf);
const size_t k_groups16 = (size_t) ggml_cl_round_up_div(kv_heads_total * d_head_q, 16);
const size_t v_groups16 = (size_t) ggml_cl_round_up_div(kv_heads_total * d_head_v, 16);
const size_t k_packed_half4_elems = (size_t) n_kv_padded * k_groups16 * 4;
const size_t v_packed_half4_elems = (size_t) n_kv_padded * v_groups16 * 4;
s.k_packed_buf = clCreateBuffer(backend_ctx->context, CL_MEM_READ_WRITE, k_packed_half4_elems * sizeof(uint16_t) * 4, nullptr, nullptr);
s.v_packed_buf = clCreateBuffer(backend_ctx->context, CL_MEM_READ_WRITE, v_packed_half4_elems * sizeof(uint16_t) * 4, nullptr, nullptr);
GGML_ASSERT(s.k_packed_buf != nullptr && s.v_packed_buf != nullptr);
const size_t score_half4_elems = (size_t) npack * kv_heads_total * q_width;
const size_t score_bytes = score_half4_elems * sizeof(uint16_t) * 4;
s.score_buf = clCreateBuffer(backend_ctx->context, CL_MEM_READ_WRITE, score_bytes, nullptr, nullptr);
s.prob_buf = clCreateBuffer(backend_ctx->context, CL_MEM_READ_WRITE, score_bytes, nullptr, nullptr);
GGML_ASSERT(s.score_buf != nullptr && s.prob_buf != nullptr);
s.score_img1d = ggml_cl_make_image1d_buffer_half4(backend_ctx->context, CL_MEM_READ_ONLY, score_half4_elems, s.score_buf);
s.prob_img1d = ggml_cl_make_image1d_buffer_half4(backend_ctx->context, CL_MEM_READ_ONLY, score_half4_elems, s.prob_buf);
s.softmax_stats_img2d = ggml_cl_make_image2d_half4(backend_ctx->context, CL_MEM_READ_WRITE,
(size_t) q_width, (size_t) kv_heads_total);
s.xmem_qk = clCreateBuffer(backend_ctx->context, CL_MEM_READ_ONLY, 6144, nullptr, nullptr);
s.xmem_pv = clCreateBuffer(backend_ctx->context, CL_MEM_READ_ONLY, 6144, nullptr, nullptr);
GGML_ASSERT(s.softmax_stats_img2d != nullptr && s.xmem_qk != nullptr && s.xmem_pv != nullptr);
s.n_q = n_q;
s.n_kv = n_kv;
s.n_kv_padded = n_kv_padded;
s.d_head_q = d_head_q;
s.d_head_v = d_head_v;
s.q_width = q_width;
s.kv_heads_total = kv_heads_total;
return true;
}
static bool ggml_cl_adreno_xmem_attn_can_use(
const ggml_backend_opencl_context * backend_ctx,
const ggml_tensor * q,
const ggml_tensor * k,
const ggml_tensor * dst) {
static const char * xmem_sdpa_env = getenv("GGML_OPENCL_XMEM_SDPA");
if (xmem_sdpa_env == nullptr || xmem_sdpa_env[0] == '0') {
return false;
}
const ggml_tensor * v = dst->src[2];
const ggml_tensor * mask = dst->src[3];
const ggml_tensor * sinks = dst->src[4];
if (!backend_ctx->adreno_xmem_attn.compiled || backend_ctx->gpu_family != GPU_FAMILY::ADRENO) {
return false;
}
if (q->type != GGML_TYPE_F32 || dst->type != GGML_TYPE_F32 ||
(k->type != GGML_TYPE_F16 && k->type != GGML_TYPE_F32) ||
(v->type != GGML_TYPE_F16 && v->type != GGML_TYPE_F32)) {
return false;
}
if (sinks != nullptr) {
return false;
}
if (q->nb[0] != ggml_type_size(q->type) || k->nb[0] != ggml_type_size(k->type) ||
v->nb[0] != ggml_type_size(v->type) || dst->nb[0] != ggml_type_size(dst->type)) {
return false;
}
if (mask != nullptr && (mask->type != GGML_TYPE_F16 || mask->nb[0] != sizeof(ggml_fp16_t))) {
return false;
}
const int n_q = q->ne[1];
const int n_kv = k->ne[1];
const int d_head_q = q->ne[0];
const int d_head_v = v->ne[0];
const int n_head = q->ne[2];
const int n_head_kv = k->ne[2];
const int n_batch = q->ne[3];
if (n_q <= 1 || n_kv <= 0 || n_kv > 8192) {
return false;
}
if (d_head_q != k->ne[0] || d_head_v != v->ne[0] || k->ne[1] != v->ne[1] || k->ne[3] != v->ne[3]) {
return false;
}
if (q->ne[3] != k->ne[3]) {
return false;
}
if (n_head_kv <= 0 || n_head % n_head_kv != 0 || k->ne[2] != v->ne[2]) {
return false;
}
if (dst->ne[0] != d_head_v || dst->ne[1] != n_head || dst->ne[2] != n_q || dst->ne[3] != n_batch) {
return false;
}
if ((d_head_q % 8) != 0 || (d_head_v % 32) != 0) {
return false;
}
if (mask != nullptr &&
(mask->ne[0] < n_kv || mask->ne[1] < n_q || mask->ne[2] <= 0 || mask->ne[3] <= 0)) {
return false;
}
float params[3];
memcpy(params, dst->op_params, sizeof(params));
if (params[1] != 0.0f || params[2] != 0.0f) {
return false;
}
const int gqa_ratio = n_head / n_head_kv;
const int q_width = n_q * gqa_ratio;
const int kv_heads_total = n_head_kv * n_batch;
const int n_kv_padded = (int) ggml_cl_round_up((size_t) n_kv, 32);
const int qpack = d_head_q / 4;
const int vpack = d_head_v / 4;
const int npack = n_kv_padded / 4;
if ((size_t) q_width > backend_ctx->image2d_max_width ||
(size_t) n_kv_padded > backend_ctx->image2d_max_width) {
return false;
}
if ((size_t) kv_heads_total * (size_t) qpack > backend_ctx->image2d_max_height ||
(size_t) kv_heads_total * (size_t) vpack > backend_ctx->image2d_max_height) {
return false;
}
if ((size_t) npack * (size_t) kv_heads_total * (size_t) d_head_q > backend_ctx->image_max_buffer_size ||
(size_t) npack * (size_t) kv_heads_total * (size_t) q_width > backend_ctx->image_max_buffer_size) {
return false;
}
return true;
}
static void ggml_cl_adreno_xmem_attn_run(
ggml_backend_t backend,
const ggml_tensor * q,
const ggml_tensor * k,
ggml_tensor * dst) {
ggml_backend_opencl_context * backend_ctx = (ggml_backend_opencl_context *) backend->context;
auto & xstate = backend_ctx->adreno_xmem_attn;
auto & s = xstate.scratch;
if (!xstate.logged) {
GGML_LOG_INFO("ggml_opencl: using Adreno xmem attention path\n");
xstate.logged = true;
}
const ggml_tensor * v = dst->src[2];
const ggml_tensor * mask = dst->src[3];
ggml_tensor_extra_cl * extra_q = (ggml_tensor_extra_cl *) q->extra;
ggml_tensor_extra_cl * extra_k = (ggml_tensor_extra_cl *) k->extra;
ggml_tensor_extra_cl * extra_v = (ggml_tensor_extra_cl *) v->extra;
ggml_tensor_extra_cl * extra_o = (ggml_tensor_extra_cl *) dst->extra;
ggml_tensor_extra_cl * extra_mask = mask ? (ggml_tensor_extra_cl *) mask->extra : nullptr;
const cl_ulong offset_q = extra_q->offset + q->view_offs;
const cl_ulong offset_k = extra_k->offset + k->view_offs;
const cl_ulong offset_v = extra_v->offset + v->view_offs;
const cl_ulong offset_o = extra_o->offset + dst->view_offs;
const cl_ulong offset_mask = extra_mask ? extra_mask->offset + mask->view_offs : 0;
const int n_q = q->ne[1];
const int n_kv = k->ne[1];
const int d_head_q = q->ne[0];
const int d_head_v = v->ne[0];
const int n_head = q->ne[2];
const int n_head_kv = k->ne[2];
const int n_batch = q->ne[3];
const int heads_total = n_head * n_batch;
const int gqa_ratio = n_head / n_head_kv;
const int q_width = n_q * gqa_ratio;
const int kv_heads_total = n_head_kv * n_batch;
const int n_kv_padded = (int) ggml_cl_round_up((size_t) n_kv, 32);
const int qpack = d_head_q / 4;
const int opack = d_head_v / 4;
const int npack = n_kv_padded / 4;
const float scale = ((const float *) dst->op_params)[0];
GGML_ASSERT(ggml_cl_adreno_xmem_attn_prepare(
backend_ctx, n_q, n_kv, d_head_q, d_head_v, n_head, n_head_kv, n_batch));
const ggml_cl_adreno_xmem_attn_schedule sched =
ggml_cl_adreno_xmem_attn_select_schedule(
backend_ctx, n_q, n_kv_padded, heads_total, q_width, gqa_ratio);
{
size_t gws[3] = {ggml_cl_round_up((size_t) n_q, 8), (size_t) heads_total, (size_t) qpack};
size_t lws[3] = {8, 1, (size_t) ((qpack <= 32) ? qpack : 1)};
cl_kernel kernel = xstate.kernel_q_f32_to_img_scaled;
CL_CHECK(clSetKernelArg(kernel, 0, sizeof(cl_mem), &extra_q->data_device));
CL_CHECK(clSetKernelArg(kernel, 1, sizeof(cl_ulong), &offset_q));
CL_CHECK(clSetKernelArg(kernel, 2, sizeof(cl_mem), &s.q_img));
CL_CHECK(clSetKernelArg(kernel, 3, sizeof(float), &scale));
CL_CHECK(clSetKernelArg(kernel, 4, sizeof(int), &d_head_q));
CL_CHECK(clSetKernelArg(kernel, 5, sizeof(int), &n_q));
CL_CHECK(clSetKernelArg(kernel, 6, sizeof(int), &n_head));
CL_CHECK(clSetKernelArg(kernel, 7, sizeof(int), &n_head_kv));
CL_CHECK(clSetKernelArg(kernel, 8, sizeof(int), &n_batch));
CL_CHECK(clSetKernelArg(kernel, 9, sizeof(cl_ulong), &q->nb[1]));
CL_CHECK(clSetKernelArg(kernel, 10, sizeof(cl_ulong), &q->nb[2]));
CL_CHECK(clSetKernelArg(kernel, 11, sizeof(cl_ulong), &q->nb[3]));
backend_ctx->enqueue_ndrange_kernel(kernel, 3, gws, lws, dst);
}
{
size_t gws[3] = {(size_t) n_kv_padded, (size_t) kv_heads_total, (size_t) qpack};
size_t lws[3] = {8, 1, (size_t) ((qpack <= 32) ? qpack : 1)};
cl_kernel kernel = k->type == GGML_TYPE_F16 ?
xstate.kernel_kv_f16_to_img_gqa : xstate.kernel_kv_f32_to_img_gqa;
CL_CHECK(clSetKernelArg(kernel, 0, sizeof(cl_mem), &extra_k->data_device));
CL_CHECK(clSetKernelArg(kernel, 1, sizeof(cl_ulong), &offset_k));
CL_CHECK(clSetKernelArg(kernel, 2, sizeof(cl_mem), &s.k_img));
CL_CHECK(clSetKernelArg(kernel, 3, sizeof(int), &d_head_q));
CL_CHECK(clSetKernelArg(kernel, 4, sizeof(int), &n_kv));
CL_CHECK(clSetKernelArg(kernel, 5, sizeof(int), &n_kv_padded));
CL_CHECK(clSetKernelArg(kernel, 6, sizeof(int), &n_head_kv));
CL_CHECK(clSetKernelArg(kernel, 7, sizeof(int), &n_batch));
CL_CHECK(clSetKernelArg(kernel, 8, sizeof(cl_ulong), &k->nb[1]));
CL_CHECK(clSetKernelArg(kernel, 9, sizeof(cl_ulong), &k->nb[2]));
CL_CHECK(clSetKernelArg(kernel, 10, sizeof(cl_ulong), &k->nb[3]));
backend_ctx->enqueue_ndrange_kernel(kernel, 3, gws, lws, dst);
}
{
size_t gws[3] = {(size_t) n_kv_padded, (size_t) kv_heads_total, (size_t) opack};
size_t lws[3] = {8, 1, (size_t) ((opack <= 32) ? opack : 1)};
cl_kernel kernel = v->type == GGML_TYPE_F16 ?
xstate.kernel_kv_f16_to_img_gqa : xstate.kernel_kv_f32_to_img_gqa;
CL_CHECK(clSetKernelArg(kernel, 0, sizeof(cl_mem), &extra_v->data_device));
CL_CHECK(clSetKernelArg(kernel, 1, sizeof(cl_ulong), &offset_v));
CL_CHECK(clSetKernelArg(kernel, 2, sizeof(cl_mem), &s.v_img));
CL_CHECK(clSetKernelArg(kernel, 3, sizeof(int), &d_head_v));
CL_CHECK(clSetKernelArg(kernel, 4, sizeof(int), &n_kv));
CL_CHECK(clSetKernelArg(kernel, 5, sizeof(int), &n_kv_padded));
CL_CHECK(clSetKernelArg(kernel, 6, sizeof(int), &n_head_kv));
CL_CHECK(clSetKernelArg(kernel, 7, sizeof(int), &n_batch));
CL_CHECK(clSetKernelArg(kernel, 8, sizeof(cl_ulong), &v->nb[1]));
CL_CHECK(clSetKernelArg(kernel, 9, sizeof(cl_ulong), &v->nb[2]));
CL_CHECK(clSetKernelArg(kernel, 10, sizeof(cl_ulong), &v->nb[3]));
backend_ctx->enqueue_ndrange_kernel(kernel, 3, gws, lws, dst);
}
{
size_t gws[3] = {(size_t) d_head_q, (size_t) kv_heads_total, (size_t) npack};
size_t lws[3] = {(size_t) MIN(64, d_head_q), (size_t) (kv_heads_total >= 2 ? 2 : 1), (size_t) MIN(8, npack)};
if (lws[0] * lws[1] * lws[2] > backend_ctx->max_workgroup_size) {
lws[1] = 1;
}
cl_kernel kernel = xstate.kernel_k_gather;
CL_CHECK(clSetKernelArg(kernel, 0, sizeof(cl_mem), &s.k_transpose_buf));
CL_CHECK(clSetKernelArg(kernel, 1, sizeof(cl_mem), &s.k_img));
ggml_cl_set_arg_int4(kernel, 2, n_kv_padded, kv_heads_total, npack, d_head_q);
ggml_cl_set_arg_int4(kernel, 3, qpack, 0, 0, 0);
backend_ctx->enqueue_ndrange_kernel(kernel, 3, gws, lws, dst);
}
{
const size_t groups16 = (size_t) ggml_cl_round_up_div(kv_heads_total * d_head_q, 16);
const size_t packed_linear = (size_t) n_kv_padded * groups16;
const size_t lws0 = MIN((size_t) 1024, backend_ctx->max_workgroup_size);
size_t gws[3] = {ggml_cl_round_up(packed_linear, lws0), 1, 1};
size_t lws[3] = {lws0, 1, 1};
cl_kernel kernel = xstate.kernel_pack_k;
CL_CHECK(clSetKernelArg(kernel, 0, sizeof(cl_mem), &s.k_packed_buf));
CL_CHECK(clSetKernelArg(kernel, 1, sizeof(cl_mem), &s.k_transpose_img1d));
ggml_cl_set_arg_int4(kernel, 2, 8, (int) packed_linear, qpack, d_head_q);
ggml_cl_set_arg_int4(kernel, 3, kv_heads_total, kv_heads_total, kv_heads_total, npack);
ggml_cl_set_arg_int4(kernel, 4, d_head_q, 0, 0, 0);
backend_ctx->enqueue_ndrange_kernel(kernel, 3, gws, lws, dst);
}
{
size_t lws[3] = {(size_t) sched.qk_lws0, 1, (size_t) sched.qk_lws2};
const int slices_per_group = sched.qk_lws2 * 8;
const size_t groups_z = (size_t) ggml_cl_round_up_div(npack, slices_per_group);
const size_t groups_x = (size_t) ggml_cl_round_up_div(q_width, sched.qk_lws0);
size_t gws[3] = {
lws[0] * groups_z,
groups_x,
(size_t) kv_heads_total * lws[2],
};
cl_kernel kernel = xstate.kernel_qk_gemm;
CL_CHECK(clSetKernelArg(kernel, 0, sizeof(cl_mem), &s.score_buf));
CL_CHECK(clSetKernelArg(kernel, 1, sizeof(cl_mem), &s.k_packed_buf));
CL_CHECK(clSetKernelArg(kernel, 2, sizeof(cl_mem), &s.xmem_qk));
CL_CHECK(clSetKernelArg(kernel, 3, sizeof(cl_mem), &s.q_img));
ggml_cl_set_arg_int4(kernel, 4, kv_heads_total, npack, q_width, 32);
ggml_cl_set_arg_int4(kernel, 5, qpack, 0, 0, kv_heads_total);
ggml_cl_set_arg_int4(kernel, 6, qpack, 1, 1, 0);
backend_ctx->enqueue_ndrange_kernel(kernel, 3, gws, lws, dst);
}
cl_mem softmax_input_img = s.score_img1d;
cl_mem softmax_output_buf = s.prob_buf;
cl_mem pv_prob_img = s.prob_img1d;
if (mask != nullptr) {
const cl_ulong mask_nb1 = mask->nb[1];
const cl_ulong mask_nb2 = mask->nb[2];
const cl_ulong mask_nb3 = mask->nb[3];
const int mask_ne2 = mask->ne[2];
const int mask_ne3 = mask->ne[3];
size_t lws[3] = {(size_t) sched.softmax_apply_lws0, 1, (size_t) sched.softmax_apply_lws2};
size_t gws[3] = {
ggml_cl_round_up((size_t) q_width, lws[0]),
(size_t) kv_heads_total,
ggml_cl_round_up((size_t) npack, lws[2]),
};
cl_kernel kernel = xstate.kernel_mask_scores;
CL_CHECK(clSetKernelArg(kernel, 0, sizeof(cl_mem), &s.prob_buf));
CL_CHECK(clSetKernelArg(kernel, 1, sizeof(cl_mem), &s.score_img1d));
CL_CHECK(clSetKernelArg(kernel, 2, sizeof(cl_mem), &extra_mask->data_device));
CL_CHECK(clSetKernelArg(kernel, 3, sizeof(cl_ulong), &offset_mask));
CL_CHECK(clSetKernelArg(kernel, 4, sizeof(int), &q_width));
CL_CHECK(clSetKernelArg(kernel, 5, sizeof(int), &n_q));
CL_CHECK(clSetKernelArg(kernel, 6, sizeof(int), &n_kv));
CL_CHECK(clSetKernelArg(kernel, 7, sizeof(int), &n_kv_padded));
CL_CHECK(clSetKernelArg(kernel, 8, sizeof(int), &kv_heads_total));
CL_CHECK(clSetKernelArg(kernel, 9, sizeof(int), &n_head));
CL_CHECK(clSetKernelArg(kernel, 10, sizeof(int), &n_head_kv));
CL_CHECK(clSetKernelArg(kernel, 11, sizeof(cl_ulong), &mask_nb1));
CL_CHECK(clSetKernelArg(kernel, 12, sizeof(cl_ulong), &mask_nb2));
CL_CHECK(clSetKernelArg(kernel, 13, sizeof(cl_ulong), &mask_nb3));
CL_CHECK(clSetKernelArg(kernel, 14, sizeof(int), &mask_ne2));
CL_CHECK(clSetKernelArg(kernel, 15, sizeof(int), &mask_ne3));
backend_ctx->enqueue_ndrange_kernel(kernel, 3, gws, lws, dst);
softmax_input_img = s.prob_img1d;
softmax_output_buf = s.score_buf;
pv_prob_img = s.score_img1d;
}
{
size_t lws[3] = {(size_t) sched.softmax_reduce_lws0, 1, 1};
size_t gws[3] = {ggml_cl_round_up((size_t) q_width, lws[0]), (size_t) kv_heads_total, 1};
cl_kernel kernel = xstate.kernel_softmax_reduce_basic;
CL_CHECK(clSetKernelArg(kernel, 0, sizeof(cl_mem), &softmax_input_img));
CL_CHECK(clSetKernelArg(kernel, 1, sizeof(cl_mem), &s.softmax_stats_img2d));
ggml_cl_set_arg_int4(kernel, 2, kv_heads_total, 1, q_width, n_kv);
ggml_cl_set_arg_int4(kernel, 3, kv_heads_total, q_width, 0, 0);
backend_ctx->enqueue_ndrange_kernel(kernel, 3, gws, lws, dst);
}
{
size_t lws[3] = {(size_t) sched.softmax_apply_lws0, 1, (size_t) sched.softmax_apply_lws2};
size_t gws[3] = {
ggml_cl_round_up((size_t) q_width, lws[0]),
(size_t) kv_heads_total,
ggml_cl_round_up((size_t) npack, lws[2]),
};
cl_kernel kernel = xstate.kernel_softmax_apply_basic;
CL_CHECK(clSetKernelArg(kernel, 0, sizeof(cl_mem), &softmax_output_buf));
CL_CHECK(clSetKernelArg(kernel, 1, sizeof(cl_mem), &softmax_input_img));
CL_CHECK(clSetKernelArg(kernel, 2, sizeof(cl_mem), &s.softmax_stats_img2d));
ggml_cl_set_arg_int4(kernel, 3, kv_heads_total, npack, q_width, 1);
ggml_cl_set_arg_int4(kernel, 4, kv_heads_total, q_width, n_kv, 0);
backend_ctx->enqueue_ndrange_kernel(kernel, 3, gws, lws, dst);
}
{
const size_t groups16 = (size_t) ggml_cl_round_up_div(kv_heads_total * d_head_v, 16);
const size_t packed_linear = (size_t) n_kv_padded * groups16;
const size_t lws0 = MIN((size_t) 1024, backend_ctx->max_workgroup_size);
size_t gws[3] = {ggml_cl_round_up(packed_linear, lws0), 1, 1};
size_t lws[3] = {lws0, 1, 1};
cl_kernel kernel = xstate.kernel_pack_v;
CL_CHECK(clSetKernelArg(kernel, 0, sizeof(cl_mem), &s.v_packed_buf));
CL_CHECK(clSetKernelArg(kernel, 1, sizeof(cl_mem), &s.v_img));
ggml_cl_set_arg_int4(kernel, 2, 8, (int) packed_linear, npack, n_kv_padded);
ggml_cl_set_arg_int4(kernel, 3, kv_heads_total, kv_heads_total, opack, 0);
backend_ctx->enqueue_ndrange_kernel(kernel, 3, gws, lws, dst);
}
{
size_t lws[3] = {(size_t) sched.pv_lws0, 1, (size_t) sched.pv_lws2};
const int blocks = ggml_cl_round_up_div(opack, 8);
const size_t groups_z = (size_t) ggml_cl_round_up_div(blocks, sched.pv_lws2);
const size_t groups_x = (size_t) ggml_cl_round_up_div(q_width, sched.pv_lws0);
size_t gws[3] = {
lws[0] * groups_z,
groups_x,
(size_t) kv_heads_total * lws[2],
};
cl_kernel kernel = xstate.kernel_pv_gemm;
CL_CHECK(clSetKernelArg(kernel, 0, sizeof(cl_mem), &s.v_packed_buf));
CL_CHECK(clSetKernelArg(kernel, 1, sizeof(cl_mem), &s.xmem_pv));
CL_CHECK(clSetKernelArg(kernel, 2, sizeof(cl_mem), &pv_prob_img));
CL_CHECK(clSetKernelArg(kernel, 3, sizeof(cl_mem), &s.out_img));
ggml_cl_set_arg_int4(kernel, 4, kv_heads_total, opack, q_width, 32);
ggml_cl_set_arg_int4(kernel, 5, npack, 0, 0, kv_heads_total);
ggml_cl_set_arg_int4(kernel, 6, kv_heads_total * q_width, npack, q_width, 1);
ggml_cl_set_arg_int4(kernel, 7, 1, 0, 0, 0);
backend_ctx->enqueue_ndrange_kernel(kernel, 3, gws, lws, dst);
}
{
size_t gws[3] = {ggml_cl_round_up((size_t) n_q, 8), (size_t) heads_total, (size_t) opack};
size_t lws[3] = {8, 1, (size_t) ((opack <= 32) ? opack : 1)};
cl_kernel kernel = xstate.kernel_img_to_f32;
CL_CHECK(clSetKernelArg(kernel, 0, sizeof(cl_mem), &extra_o->data_device));
CL_CHECK(clSetKernelArg(kernel, 1, sizeof(cl_ulong), &offset_o));
CL_CHECK(clSetKernelArg(kernel, 2, sizeof(cl_mem), &s.out_img));
CL_CHECK(clSetKernelArg(kernel, 3, sizeof(int), &d_head_v));
CL_CHECK(clSetKernelArg(kernel, 4, sizeof(int), &n_q));
CL_CHECK(clSetKernelArg(kernel, 5, sizeof(int), &n_head));
CL_CHECK(clSetKernelArg(kernel, 6, sizeof(int), &n_head_kv));
CL_CHECK(clSetKernelArg(kernel, 7, sizeof(int), &n_batch));
CL_CHECK(clSetKernelArg(kernel, 8, sizeof(cl_ulong), &dst->nb[1]));
CL_CHECK(clSetKernelArg(kernel, 9, sizeof(cl_ulong), &dst->nb[2]));
CL_CHECK(clSetKernelArg(kernel, 10, sizeof(cl_ulong), &dst->nb[3]));
backend_ctx->enqueue_ndrange_kernel(kernel, 3, gws, lws, dst);
}
}
#endif // GGML_OPENCL_USE_ADRENO_KERNELS
static void ggml_cl_flash_attn(ggml_backend_t backend, const ggml_tensor * q, const ggml_tensor * k, ggml_tensor * dst) {
const ggml_tensor * v = dst->src[2];
const ggml_tensor * mask = dst->src[3];
@@ -15954,6 +16630,13 @@ static void ggml_cl_flash_attn(ggml_backend_t backend, const ggml_tensor * q, co
const int n_head_kv = k->ne[2];
const int n_batch = q->ne[3];
#ifdef GGML_OPENCL_USE_ADRENO_KERNELS
if (ggml_cl_adreno_xmem_attn_can_use(backend_ctx, q, k, dst)) {
ggml_cl_adreno_xmem_attn_run(backend, q, k, dst);
return;
}
#endif
// DK=512 (Gemma-4 global layers) runs decode-only (q1 / q1_split) on
// Adreno - it never uses the BM-tile path, and the prepass + split-tile
// programs OOM the compiler at DK=512; supports_op only admits
@@ -0,0 +1,871 @@
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
#pragma OPENCL EXTENSION cl_qcom_subgroup_uniform_load : enable
#pragma OPENCL EXTENSION cl_qcom_subgroup_constant_load : enable
#define bool2 uchar2
#define bool3 uchar3
#define bool4 uchar4
__constant sampler_t smp_none = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_NONE | CLK_FILTER_NEAREST;
__constant sampler_t smp_zero = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP | CLK_FILTER_NEAREST;
__kernel void adreno_xmem_attn_q_f32_to_img_scaled(const global void * src_void,
ulong src_offset,
write_only image2d_t dst_image2d,
const float scale,
const int d_head,
const int n_q,
const int n_head,
const int n_head_kv,
const int n_batch,
const ulong src_nb1,
const ulong src_nb2,
const ulong src_nb3) {
const int x = get_global_id(0);
const int flat_h = get_global_id(1);
const int d = get_global_id(2);
const int heads_total = n_head * n_batch;
const int kpack = d_head / 4;
if (x >= n_q || flat_h >= heads_total || d >= kpack) {
return;
}
const int batch = flat_h / n_head;
const int head = flat_h % n_head;
const int gqa = n_head / n_head_kv;
const int head_kv = head / gqa;
const int head_group = head - head_kv * gqa;
const int compact_h = batch * n_head_kv + head_kv;
const int compact_x = head_group * n_q + x;
const int c = d * 4;
const global char * src_base = (const global char *) src_void + src_offset;
const global float * row_ptr = (const global float *) (src_base + batch * src_nb3 + head * src_nb2 + x * src_nb1);
half4 out = (half4) (0.0h);
out.x = convert_half(row_ptr[c + 0] * scale);
if (c + 1 < d_head) {
out.y = convert_half(row_ptr[c + 1] * scale);
}
if (c + 2 < d_head) {
out.z = convert_half(row_ptr[c + 2] * scale);
}
if (c + 3 < d_head) {
out.w = convert_half(row_ptr[c + 3] * scale);
}
write_imageh(dst_image2d, (int2) (compact_x, compact_h * kpack + d), out);
}
__kernel void adreno_xmem_attn_kv_f32_to_img_gqa(const global void * src_void,
ulong src_offset,
write_only image2d_t dst_image2d,
const int d_head,
const int n_kv,
const int n_kv_padded,
const int n_head_kv,
const int n_batch,
const ulong src_nb1,
const ulong src_nb2,
const ulong src_nb3) {
const int x = get_global_id(0);
const int flat_h = get_global_id(1);
const int d = get_global_id(2);
const int kv_heads_total = n_head_kv * n_batch;
const int kpack = d_head / 4;
if (x >= n_kv_padded || flat_h >= kv_heads_total || d >= kpack) {
return;
}
const int batch = flat_h / n_head_kv;
const int head_kv = flat_h % n_head_kv;
const int c = d * 4;
half4 out = (half4) (0.0h);
if (x < n_kv) {
const global char * src_base = (const global char *) src_void + src_offset;
const global float * row_ptr =
(const global float *) (src_base + batch * src_nb3 + head_kv * src_nb2 + x * src_nb1);
out.x = convert_half(row_ptr[c + 0]);
if (c + 1 < d_head) {
out.y = convert_half(row_ptr[c + 1]);
}
if (c + 2 < d_head) {
out.z = convert_half(row_ptr[c + 2]);
}
if (c + 3 < d_head) {
out.w = convert_half(row_ptr[c + 3]);
}
}
write_imageh(dst_image2d, (int2) (x, flat_h * kpack + d), out);
}
__kernel void adreno_xmem_attn_kv_f16_to_img_gqa(const global void * src_void,
ulong src_offset,
write_only image2d_t dst_image2d,
const int d_head,
const int n_kv,
const int n_kv_padded,
const int n_head_kv,
const int n_batch,
const ulong src_nb1,
const ulong src_nb2,
const ulong src_nb3) {
const int x = get_global_id(0);
const int flat_h = get_global_id(1);
const int d = get_global_id(2);
const int kv_heads_total = n_head_kv * n_batch;
const int kpack = d_head / 4;
if (x >= n_kv_padded || flat_h >= kv_heads_total || d >= kpack) {
return;
}
const int batch = flat_h / n_head_kv;
const int head_kv = flat_h % n_head_kv;
const int c = d * 4;
half4 out = (half4) (0.0h);
if (x < n_kv) {
const global char * src_base = (const global char *) src_void + src_offset;
const global half * row_ptr =
(const global half *) (src_base + batch * src_nb3 + head_kv * src_nb2 + x * src_nb1);
out.x = row_ptr[c + 0];
if (c + 1 < d_head) {
out.y = row_ptr[c + 1];
}
if (c + 2 < d_head) {
out.z = row_ptr[c + 2];
}
if (c + 3 < d_head) {
out.w = row_ptr[c + 3];
}
}
write_imageh(dst_image2d, (int2) (x, flat_h * kpack + d), out);
}
__kernel void adreno_xmem_attn_img_to_f32(global void * dst_void,
ulong dst_offset,
read_only image2d_t src_image2d,
const int d_head,
const int n_q,
const int n_head,
const int n_head_kv,
const int n_batch,
const ulong dst_nb1,
const ulong dst_nb2,
const ulong dst_nb3) {
const int x = get_global_id(0);
const int flat_h = get_global_id(1);
const int d = get_global_id(2);
const int heads_total = n_head * n_batch;
const int kpack = d_head / 4;
if (x >= n_q || flat_h >= heads_total || d >= kpack) {
return;
}
const int batch = flat_h / n_head;
const int head = flat_h % n_head;
const int gqa = n_head / n_head_kv;
const int head_kv = head / gqa;
const int head_group = head - head_kv * gqa;
const int compact_h = batch * n_head_kv + head_kv;
const int compact_x = head_group * n_q + x;
const int c = d * 4;
global char * dst_base = (global char *) dst_void + dst_offset;
global float * row_ptr = (global float *) (dst_base + batch * dst_nb3 + x * dst_nb2 + head * dst_nb1);
const half4 in_value = read_imageh(src_image2d, smp_zero, (int2) (compact_x, compact_h * kpack + d));
row_ptr[c + 0] = convert_float(in_value.x);
if (c + 1 < d_head) {
row_ptr[c + 1] = convert_float(in_value.y);
}
if (c + 2 < d_head) {
row_ptr[c + 2] = convert_float(in_value.z);
}
if (c + 3 < d_head) {
row_ptr[c + 3] = convert_float(in_value.w);
}
}
__kernel void adreno_xmem_attn_k_gather(global half4 * dst_tensor_buffer,
read_only image2d_t src_tensor_image2d,
const int4 shared_int4_0,
const int4 shared_int4_1) {
int X = get_global_id(0);
int Y = get_global_id(1);
int S = get_global_id(2);
if (X >= shared_int4_0.w || Y >= shared_int4_0.y || S >= shared_int4_0.z) {
return;
}
half temps[4];
temps[0] = (half) (0.f);
temps[1] = (half) (0.f);
temps[2] = (half) (0.f);
temps[3] = (half) (0.f);
for (int i = 0; i < 4; ++i) {
int dst_channel = S * 4 + i;
if (dst_channel < shared_int4_0.x) {
int s_y = Y;
int s_x = dst_channel;
int s_c = X;
{
int slice_coord_TMP = (s_c) / 4;
int sub_ch_coord_TMP = (s_c) % 4;
half4 src_TMP = read_imageh(src_tensor_image2d, smp_zero,
(int2) ((s_x), ((s_y) *shared_int4_1.x + (slice_coord_TMP))));
temps[i] = (half[4]){ src_TMP.x, src_TMP.y, src_TMP.z, src_TMP.w }[sub_ch_coord_TMP];
};
}
}
half4 result;
result.x = temps[0];
result.y = temps[1];
result.z = temps[2];
result.w = temps[3];
dst_tensor_buffer[(((S) *shared_int4_0.y + (Y)) * shared_int4_0.w + (X))] = result;
}
__kernel void adreno_xmem_attn_pack_k(global half4 * dst_tensor_buffer,
read_only image1d_buffer_t src_image_buffer,
const int4 shared_int4_0,
const int4 shared_int4_1,
const int4 shared_int4_2) {
int linear_index = get_global_id(0);
if (linear_index >= shared_int4_0.y) {
return;
}
if (get_global_id(1) != 0) {
return;
}
if (get_global_id(2) != 0) {
return;
}
int dst_o_sp_i_ogroup = linear_index;
int dst_ogroup = dst_o_sp_i_ogroup % shared_int4_0.x;
int dst_o_sp_i = dst_o_sp_i_ogroup / shared_int4_0.x;
int dst_i = dst_o_sp_i % shared_int4_0.z;
int dst_o_sp = dst_o_sp_i / shared_int4_0.z;
int dst_sp = dst_o_sp % shared_int4_1.x;
int dst_o = dst_o_sp / shared_int4_1.x;
int i_slice = dst_i;
int o_slice = dst_o * shared_int4_0.x + dst_ogroup;
int spatial_linear = dst_sp;
int W = spatial_linear % shared_int4_1.y;
int H = spatial_linear / shared_int4_1.y;
half4 w0 = (half4) (0);
half4 w1 = (half4) (0);
half4 w2 = (half4) (0);
half4 w3 = (half4) (0);
if (i_slice * 4 < shared_int4_0.w && o_slice < shared_int4_1.w) {
w0 = read_imageh(src_image_buffer, (((o_slice) *shared_int4_1.z + (W)) * shared_int4_2.x + (i_slice * 4)));
}
if (i_slice * 4 + 1 < shared_int4_0.w && o_slice < shared_int4_1.w) {
w1 = read_imageh(src_image_buffer, (((o_slice) *shared_int4_1.z + (W)) * shared_int4_2.x + (i_slice * 4 + 1)));
}
if (i_slice * 4 + 2 < shared_int4_0.w && o_slice < shared_int4_1.w) {
w2 = read_imageh(src_image_buffer, (((o_slice) *shared_int4_1.z + (W)) * shared_int4_2.x + (i_slice * 4 + 2)));
}
if (i_slice * 4 + 3 < shared_int4_0.w && o_slice < shared_int4_1.w) {
w3 = read_imageh(src_image_buffer, (((o_slice) *shared_int4_1.z + (W)) * shared_int4_2.x + (i_slice * 4 + 3)));
}
half4 r0 = w0;
half4 r1 = w1;
half4 r2 = w2;
half4 r3 = w3;
dst_tensor_buffer[linear_index * 4 + 0] = r0;
dst_tensor_buffer[linear_index * 4 + 1] = r1;
dst_tensor_buffer[linear_index * 4 + 2] = r2;
dst_tensor_buffer[linear_index * 4 + 3] = r3;
}
__attribute__((qcom_max_concurrent_subgroups(12))) __kernel void adreno_xmem_attn_qk_gemm(
global half4 * dst_tensor_buffer,
constant half8 * weights_buffer __attribute__((sub_group_uniform)),
constant half8 * xmem_buffer __attribute__((max_constant_size((6144)))),
read_only image2d_t src_tensor_image2d,
const int4 shared_int4_0,
const int4 shared_int4_1,
const int4 shared_int4_2) {
int X = get_group_id(1) * get_local_size(0) + get_local_id(0);
int Y = get_group_id(2) * get_local_size(1) + get_local_id(1);
int Z = get_group_id(0) * get_local_size(2) + get_local_id(2);
if (X >= shared_int4_0.z || Y >= shared_int4_0.x) {
return;
}
if (Z * 8 >= shared_int4_0.y) {
return;
}
half4 r0 = (half4) (0.f);
half4 r1 = (half4) (0.f);
half4 r2 = (half4) (0.f);
half4 r3 = (half4) (0.f);
half4 r4 = (half4) (0.f);
half4 r5 = (half4) (0.f);
half4 r6 = (half4) (0.f);
half4 r7 = (half4) (0.f);
int x_coord = mad24(X, shared_int4_2.y, shared_int4_1.y);
int y_coord = mad24(Y, shared_int4_2.z, shared_int4_1.z);
int coord_x, coord_y, coord_s;
int f_offset = (Z * shared_int4_1.w + Y) * shared_int4_1.x * 32;
int subgroup_id = (int) ((0x1F & qcom_get_physical_sub_group_id()));
subgroup_id = subgroup_id % 12;
int c_offset = mul24(subgroup_id, shared_int4_0.w);
__constant half16 * weights_cache = (__constant half16 *) &xmem_buffer[c_offset];
coord_y = Y;
coord_x = X;
coord_s = 0;
do {
half4 src0 =
read_imageh(src_tensor_image2d, smp_zero, (int2) ((coord_x), ((coord_y) *shared_int4_2.x + (coord_s))));
coord_s++;
half4 src1 =
read_imageh(src_tensor_image2d, smp_zero, (int2) ((coord_x), ((coord_y) *shared_int4_2.x + (coord_s))));
coord_s++;
qcom_sub_group_constant_load8(xmem_buffer, weights_buffer, c_offset, f_offset >> 1, 32);
f_offset += 64;
qcom_sub_group_sync(QCOM_CLK_CONST_LOAD_SYNC);
r0 += src0.x * weights_cache[0].s0123;
r0 += src0.y * weights_cache[0].s4567;
r0 += src0.z * weights_cache[0].s89ab;
r0 += src0.w * weights_cache[0].scdef;
r1 += src0.x * weights_cache[1].s0123;
r1 += src0.y * weights_cache[1].s4567;
r1 += src0.z * weights_cache[1].s89ab;
r1 += src0.w * weights_cache[1].scdef;
r2 += src0.x * weights_cache[2].s0123;
r2 += src0.y * weights_cache[2].s4567;
r2 += src0.z * weights_cache[2].s89ab;
r2 += src0.w * weights_cache[2].scdef;
r3 += src0.x * weights_cache[3].s0123;
r3 += src0.y * weights_cache[3].s4567;
r3 += src0.z * weights_cache[3].s89ab;
r3 += src0.w * weights_cache[3].scdef;
r4 += src0.x * weights_cache[4].s0123;
r4 += src0.y * weights_cache[4].s4567;
r4 += src0.z * weights_cache[4].s89ab;
r4 += src0.w * weights_cache[4].scdef;
r5 += src0.x * weights_cache[5].s0123;
r5 += src0.y * weights_cache[5].s4567;
r5 += src0.z * weights_cache[5].s89ab;
r5 += src0.w * weights_cache[5].scdef;
r6 += src0.x * weights_cache[6].s0123;
r6 += src0.y * weights_cache[6].s4567;
r6 += src0.z * weights_cache[6].s89ab;
r6 += src0.w * weights_cache[6].scdef;
r7 += src0.x * weights_cache[7].s0123;
r7 += src0.y * weights_cache[7].s4567;
r7 += src0.z * weights_cache[7].s89ab;
r7 += src0.w * weights_cache[7].scdef;
r0 += src1.x * weights_cache[8].s0123;
r0 += src1.y * weights_cache[8].s4567;
r0 += src1.z * weights_cache[8].s89ab;
r0 += src1.w * weights_cache[8].scdef;
r1 += src1.x * weights_cache[9].s0123;
r1 += src1.y * weights_cache[9].s4567;
r1 += src1.z * weights_cache[9].s89ab;
r1 += src1.w * weights_cache[9].scdef;
r2 += src1.x * weights_cache[10].s0123;
r2 += src1.y * weights_cache[10].s4567;
r2 += src1.z * weights_cache[10].s89ab;
r2 += src1.w * weights_cache[10].scdef;
r3 += src1.x * weights_cache[11].s0123;
r3 += src1.y * weights_cache[11].s4567;
r3 += src1.z * weights_cache[11].s89ab;
r3 += src1.w * weights_cache[11].scdef;
r4 += src1.x * weights_cache[12].s0123;
r4 += src1.y * weights_cache[12].s4567;
r4 += src1.z * weights_cache[12].s89ab;
r4 += src1.w * weights_cache[12].scdef;
r5 += src1.x * weights_cache[13].s0123;
r5 += src1.y * weights_cache[13].s4567;
r5 += src1.z * weights_cache[13].s89ab;
r5 += src1.w * weights_cache[13].scdef;
r6 += src1.x * weights_cache[14].s0123;
r6 += src1.y * weights_cache[14].s4567;
r6 += src1.z * weights_cache[14].s89ab;
r6 += src1.w * weights_cache[14].scdef;
r7 += src1.x * weights_cache[15].s0123;
r7 += src1.y * weights_cache[15].s4567;
r7 += src1.z * weights_cache[15].s89ab;
r7 += src1.w * weights_cache[15].scdef;
} while (coord_s < shared_int4_2.x);
coord_s = mul24(Z, 8);
coord_x = X;
coord_y = Y;
if (coord_s < shared_int4_0.y) {
half4 res = convert_half4(r0);
if (coord_s < 0) {
res += read_imageh(src_tensor_image2d, smp_zero, (int2) ((0), ((0) * shared_int4_2.x + (0))));
}
dst_tensor_buffer[(((coord_s) *shared_int4_0.x + (coord_y)) * shared_int4_0.z + (coord_x))] = res;
coord_s++;
}
if (coord_s < shared_int4_0.y) {
half4 res = convert_half4(r1);
if (coord_s < 0) {
res += read_imageh(src_tensor_image2d, smp_zero, (int2) ((0), ((0) * shared_int4_2.x + (0))));
}
dst_tensor_buffer[(((coord_s) *shared_int4_0.x + (coord_y)) * shared_int4_0.z + (coord_x))] = res;
coord_s++;
}
if (coord_s < shared_int4_0.y) {
half4 res = convert_half4(r2);
if (coord_s < 0) {
res += read_imageh(src_tensor_image2d, smp_zero, (int2) ((0), ((0) * shared_int4_2.x + (0))));
}
dst_tensor_buffer[(((coord_s) *shared_int4_0.x + (coord_y)) * shared_int4_0.z + (coord_x))] = res;
coord_s++;
}
if (coord_s < shared_int4_0.y) {
half4 res = convert_half4(r3);
if (coord_s < 0) {
res += read_imageh(src_tensor_image2d, smp_zero, (int2) ((0), ((0) * shared_int4_2.x + (0))));
}
dst_tensor_buffer[(((coord_s) *shared_int4_0.x + (coord_y)) * shared_int4_0.z + (coord_x))] = res;
coord_s++;
}
if (coord_s < shared_int4_0.y) {
half4 res = convert_half4(r4);
if (coord_s < 0) {
res += read_imageh(src_tensor_image2d, smp_zero, (int2) ((0), ((0) * shared_int4_2.x + (0))));
}
dst_tensor_buffer[(((coord_s) *shared_int4_0.x + (coord_y)) * shared_int4_0.z + (coord_x))] = res;
coord_s++;
}
if (coord_s < shared_int4_0.y) {
half4 res = convert_half4(r5);
if (coord_s < 0) {
res += read_imageh(src_tensor_image2d, smp_zero, (int2) ((0), ((0) * shared_int4_2.x + (0))));
}
dst_tensor_buffer[(((coord_s) *shared_int4_0.x + (coord_y)) * shared_int4_0.z + (coord_x))] = res;
coord_s++;
}
if (coord_s < shared_int4_0.y) {
half4 res = convert_half4(r6);
if (coord_s < 0) {
res += read_imageh(src_tensor_image2d, smp_zero, (int2) ((0), ((0) * shared_int4_2.x + (0))));
}
dst_tensor_buffer[(((coord_s) *shared_int4_0.x + (coord_y)) * shared_int4_0.z + (coord_x))] = res;
coord_s++;
}
if (coord_s < shared_int4_0.y) {
half4 res = convert_half4(r7);
if (coord_s < 0) {
res += read_imageh(src_tensor_image2d, smp_zero, (int2) ((0), ((0) * shared_int4_2.x + (0))));
}
dst_tensor_buffer[(((coord_s) *shared_int4_0.x + (coord_y)) * shared_int4_0.z + (coord_x))] = res;
coord_s++;
}
}
__kernel void adreno_xmem_attn_softmax_reduce_basic(read_only image1d_buffer_t src_tensor_image_buffer,
write_only image2d_t dst_tensor_image2d,
const int4 shared_int4_0,
const int4 shared_int4_1) {
int X = get_global_id(0);
int Y = get_global_id(1);
if (X >= shared_int4_0.z || Y >= shared_int4_0.x) {
return;
}
float sum = 0.0f;
int end_channel = shared_int4_0.w;
int end_slice = (end_channel + 3) / 4;
int start_channel = 0;
int start_slice = start_channel / 4;
bool need_per_channels_check = start_channel % 4 != 0 || end_channel % 4 != 0;
float maximum;
{
int slice_coord_TMP = (start_channel) / 4;
int sub_ch_coord_TMP = (start_channel) % 4;
float4 src_TMP = convert_float4(
read_imageh(src_tensor_image_buffer, ((slice_coord_TMP) *shared_int4_1.x + (Y)) * shared_int4_1.y + (X)));
maximum = (float[4]){ src_TMP.x, src_TMP.y, src_TMP.z, src_TMP.w }[sub_ch_coord_TMP];
};
for (int d = start_slice; d < end_slice; d += 1) {
float4 mask_dot = (float4) (1.f);
float4 src =
convert_float4(read_imageh(src_tensor_image_buffer, ((d) *shared_int4_1.x + (Y)) * shared_int4_1.y + (X)));
if (need_per_channels_check && (d == start_slice || d == end_slice - 1)) {
if (d * 4 + 0 < start_channel || d * 4 + 0 >= end_channel) {
mask_dot.x = 0.f;
src.x = maximum;
}
if (d * 4 + 1 < start_channel || d * 4 + 1 >= end_channel) {
mask_dot.y = 0.f;
src.y = maximum;
}
if (d * 4 + 2 < start_channel || d * 4 + 2 >= end_channel) {
mask_dot.z = 0.f;
src.z = maximum;
}
if (d * 4 + 3 < start_channel || d * 4 + 3 >= end_channel) {
mask_dot.w = 0.f;
src.w = maximum;
}
}
float new_max = max(src.x, src.y);
new_max = max(new_max, src.z);
new_max = max(new_max, src.w);
new_max = max(new_max, maximum);
float scale = native_exp(maximum - new_max);
maximum = new_max;
sum *= scale;
float4 exp_res = native_exp(src - maximum);
sum += dot(mask_dot, exp_res);
}
if (!isfinite(maximum) || sum == 0.0f) {
write_imageh(dst_tensor_image2d, (int2) (X, Y), (half4) (0.0h));
return;
}
write_imageh(dst_tensor_image2d, (int2) (X, Y),
(half4) (convert_half(1.0f / sum), convert_half(maximum), 0.0h, 0.0h));
}
__kernel void adreno_xmem_attn_softmax_apply_basic(global half4 * dst_tensor_buffer,
read_only image1d_buffer_t src_tensor_image_buffer,
read_only image2d_t src_tensor_1_image2d,
const int4 shared_int4_0,
const int4 shared_int4_1) {
int X = get_global_id(0);
int Y = get_global_id(1);
int Z = get_global_id(2);
if (X >= shared_int4_0.z || Y >= shared_int4_0.x || Z >= shared_int4_0.y) {
return;
}
half4 src = read_imageh(src_tensor_image_buffer, ((Z) *shared_int4_1.x + (Y)) * shared_int4_1.y + (X));
{
half4 src_final;
{
{
half4 exp_val = read_imageh(src_tensor_1_image2d, smp_zero, (int2) (X, Y));
src_final = exp(src - exp_val.y) * exp_val.x;
const int k = Z * 4;
const int n_kv = shared_int4_1.z;
if (k + 0 >= n_kv) {
src_final.x = 0.0h;
}
if (k + 1 >= n_kv) {
src_final.y = 0.0h;
}
if (k + 2 >= n_kv) {
src_final.z = 0.0h;
}
if (k + 3 >= n_kv) {
src_final.w = 0.0h;
}
}
}
dst_tensor_buffer[(((Z) *shared_int4_0.x + (Y)) * shared_int4_0.z + (X))] = src_final;
};
}
__kernel void adreno_xmem_attn_mask_scores(global half4 * dst_score_tensor_buffer,
read_only image1d_buffer_t src_score_image_buffer,
const global half * mask,
const ulong mask_offset,
const int q_width,
const int n_q,
const int n_kv,
const int n_kv_padded,
const int kv_heads_total,
const int n_head,
const int n_head_kv,
const ulong mask_nb1,
const ulong mask_nb2,
const ulong mask_nb3,
const int mask_ne2,
const int mask_ne3) {
const int X = get_global_id(0);
const int Y = get_global_id(1);
const int Z = get_global_id(2);
const int npack = n_kv_padded / 4;
if (X >= q_width || Y >= kv_heads_total || Z >= npack) {
return;
}
const int gqa = n_head / n_head_kv;
const int head_kv = Y % n_head_kv;
const int batch = Y / n_head_kv;
const int head_group = X / n_q;
const int q = X - head_group * n_q;
const int head = head_kv * gqa + head_group;
const int mask_head_idx = head % mask_ne2;
const int mask_batch_idx = batch % mask_ne3;
const global char * mask_base = (const global char *) mask + mask_offset;
const global half * mask_row = (const global half *) (mask_base + mask_batch_idx * mask_nb3 +
mask_head_idx * mask_nb2 + q * mask_nb1);
const half4 score = read_imageh(src_score_image_buffer, ((Z * kv_heads_total + Y) * q_width + X));
float vals[4] = {
convert_float(score.x),
convert_float(score.y),
convert_float(score.z),
convert_float(score.w),
};
for (int lane = 0; lane < 4; ++lane) {
const int k_idx = Z * 4 + lane;
if (k_idx >= n_kv) {
vals[lane] = -INFINITY;
} else {
vals[lane] += convert_float(mask_row[k_idx]);
}
}
dst_score_tensor_buffer[((Z * kv_heads_total + Y) * q_width + X)] =
(half4) (convert_half(vals[0]), convert_half(vals[1]), convert_half(vals[2]), convert_half(vals[3]));
}
__kernel void adreno_xmem_attn_pack_v(global half4 * dst_tensor_buffer,
read_only image2d_t src_image2d,
const int4 shared_int4_0,
const int4 shared_int4_1) {
int linear_index = get_global_id(0);
if (linear_index >= shared_int4_0.y) {
return;
}
if (get_global_id(1) != 0) {
return;
}
if (get_global_id(2) != 0) {
return;
}
int dst_o_sp_i_ogroup = linear_index;
int dst_ogroup = dst_o_sp_i_ogroup % shared_int4_0.x;
int dst_o_sp_i = dst_o_sp_i_ogroup / shared_int4_0.x;
int dst_i = dst_o_sp_i % shared_int4_0.z;
int dst_o_sp = dst_o_sp_i / shared_int4_0.z;
int dst_sp = dst_o_sp % shared_int4_1.x;
int dst_o = dst_o_sp / shared_int4_1.x;
int i_slice = dst_i;
int o_slice = dst_o * shared_int4_0.x + dst_ogroup;
int spatial_linear = dst_sp;
int W = spatial_linear % shared_int4_1.y;
int H = spatial_linear / shared_int4_1.y;
half4 w0 = (half4) (0);
half4 w1 = (half4) (0);
half4 w2 = (half4) (0);
half4 w3 = (half4) (0);
if (i_slice * 4 < shared_int4_0.w && o_slice < shared_int4_1.z) {
w0 = read_imageh(src_image2d, smp_zero, (int2) ((i_slice * 4), ((W) *shared_int4_1.z + (o_slice))));
}
if (i_slice * 4 + 1 < shared_int4_0.w && o_slice < shared_int4_1.z) {
w1 = read_imageh(src_image2d, smp_zero, (int2) ((i_slice * 4 + 1), ((W) *shared_int4_1.z + (o_slice))));
}
if (i_slice * 4 + 2 < shared_int4_0.w && o_slice < shared_int4_1.z) {
w2 = read_imageh(src_image2d, smp_zero, (int2) ((i_slice * 4 + 2), ((W) *shared_int4_1.z + (o_slice))));
}
if (i_slice * 4 + 3 < shared_int4_0.w && o_slice < shared_int4_1.z) {
w3 = read_imageh(src_image2d, smp_zero, (int2) ((i_slice * 4 + 3), ((W) *shared_int4_1.z + (o_slice))));
}
half4 r0 = w0;
half4 r1 = w1;
half4 r2 = w2;
half4 r3 = w3;
dst_tensor_buffer[linear_index * 4 + 0] = r0;
dst_tensor_buffer[linear_index * 4 + 1] = r1;
dst_tensor_buffer[linear_index * 4 + 2] = r2;
dst_tensor_buffer[linear_index * 4 + 3] = r3;
}
__attribute__((qcom_max_concurrent_subgroups(12))) __kernel void adreno_xmem_attn_pv_gemm(
constant half8 * weights_buffer __attribute__((sub_group_uniform)),
constant half8 * xmem_buffer __attribute__((max_constant_size((6144)))),
read_only image1d_buffer_t src_tensor_image_buffer,
write_only image2d_t dst_tensor_image2d,
const int4 shared_int4_0,
const int4 shared_int4_1,
const int4 shared_int4_2,
const int4 shared_int4_3) {
int X = get_group_id(1) * get_local_size(0) + get_local_id(0);
int Y = get_group_id(2) * get_local_size(1) + get_local_id(1);
int Z = get_group_id(0) * get_local_size(2) + get_local_id(2);
if (X >= shared_int4_0.z || Y >= shared_int4_0.x) {
return;
}
if (Z * 8 >= shared_int4_0.y) {
return;
}
half4 r0 = (half4) (0.f);
half4 r1 = (half4) (0.f);
half4 r2 = (half4) (0.f);
half4 r3 = (half4) (0.f);
half4 r4 = (half4) (0.f);
half4 r5 = (half4) (0.f);
half4 r6 = (half4) (0.f);
half4 r7 = (half4) (0.f);
int x_coord = mad24(X, shared_int4_2.w, shared_int4_1.y);
int y_coord = mad24(Y, shared_int4_3.x, shared_int4_1.z);
int coord_x, coord_y, coord_s;
int f_offset = (Z * shared_int4_1.w + Y) * shared_int4_1.x * 32;
int subgroup_id = (int) ((0x1F & qcom_get_physical_sub_group_id()));
subgroup_id = subgroup_id % 12;
int c_offset = mul24(subgroup_id, shared_int4_0.w);
__constant half16 * weights_cache = (__constant half16 *) &xmem_buffer[c_offset];
coord_y = Y;
coord_x = X;
int addr = (((0) * shared_int4_1.w + (coord_y)) * shared_int4_2.z + (coord_x));
int dz = shared_int4_2.x;
coord_s = 0;
do {
half4 src0 = read_imageh(src_tensor_image_buffer, addr);
addr += dz;
coord_s++;
half4 src1 = read_imageh(src_tensor_image_buffer, addr);
addr += dz;
coord_s++;
qcom_sub_group_constant_load8(xmem_buffer, weights_buffer, c_offset, f_offset >> 1, 32);
f_offset += 64;
qcom_sub_group_sync(QCOM_CLK_CONST_LOAD_SYNC);
r0 += src0.x * weights_cache[0].s0123;
r0 += src0.y * weights_cache[0].s4567;
r0 += src0.z * weights_cache[0].s89ab;
r0 += src0.w * weights_cache[0].scdef;
r1 += src0.x * weights_cache[1].s0123;
r1 += src0.y * weights_cache[1].s4567;
r1 += src0.z * weights_cache[1].s89ab;
r1 += src0.w * weights_cache[1].scdef;
r2 += src0.x * weights_cache[2].s0123;
r2 += src0.y * weights_cache[2].s4567;
r2 += src0.z * weights_cache[2].s89ab;
r2 += src0.w * weights_cache[2].scdef;
r3 += src0.x * weights_cache[3].s0123;
r3 += src0.y * weights_cache[3].s4567;
r3 += src0.z * weights_cache[3].s89ab;
r3 += src0.w * weights_cache[3].scdef;
r4 += src0.x * weights_cache[4].s0123;
r4 += src0.y * weights_cache[4].s4567;
r4 += src0.z * weights_cache[4].s89ab;
r4 += src0.w * weights_cache[4].scdef;
r5 += src0.x * weights_cache[5].s0123;
r5 += src0.y * weights_cache[5].s4567;
r5 += src0.z * weights_cache[5].s89ab;
r5 += src0.w * weights_cache[5].scdef;
r6 += src0.x * weights_cache[6].s0123;
r6 += src0.y * weights_cache[6].s4567;
r6 += src0.z * weights_cache[6].s89ab;
r6 += src0.w * weights_cache[6].scdef;
r7 += src0.x * weights_cache[7].s0123;
r7 += src0.y * weights_cache[7].s4567;
r7 += src0.z * weights_cache[7].s89ab;
r7 += src0.w * weights_cache[7].scdef;
r0 += src1.x * weights_cache[8].s0123;
r0 += src1.y * weights_cache[8].s4567;
r0 += src1.z * weights_cache[8].s89ab;
r0 += src1.w * weights_cache[8].scdef;
r1 += src1.x * weights_cache[9].s0123;
r1 += src1.y * weights_cache[9].s4567;
r1 += src1.z * weights_cache[9].s89ab;
r1 += src1.w * weights_cache[9].scdef;
r2 += src1.x * weights_cache[10].s0123;
r2 += src1.y * weights_cache[10].s4567;
r2 += src1.z * weights_cache[10].s89ab;
r2 += src1.w * weights_cache[10].scdef;
r3 += src1.x * weights_cache[11].s0123;
r3 += src1.y * weights_cache[11].s4567;
r3 += src1.z * weights_cache[11].s89ab;
r3 += src1.w * weights_cache[11].scdef;
r4 += src1.x * weights_cache[12].s0123;
r4 += src1.y * weights_cache[12].s4567;
r4 += src1.z * weights_cache[12].s89ab;
r4 += src1.w * weights_cache[12].scdef;
r5 += src1.x * weights_cache[13].s0123;
r5 += src1.y * weights_cache[13].s4567;
r5 += src1.z * weights_cache[13].s89ab;
r5 += src1.w * weights_cache[13].scdef;
r6 += src1.x * weights_cache[14].s0123;
r6 += src1.y * weights_cache[14].s4567;
r6 += src1.z * weights_cache[14].s89ab;
r6 += src1.w * weights_cache[14].scdef;
r7 += src1.x * weights_cache[15].s0123;
r7 += src1.y * weights_cache[15].s4567;
r7 += src1.z * weights_cache[15].s89ab;
r7 += src1.w * weights_cache[15].scdef;
} while (coord_s < shared_int4_2.y);
coord_s = mul24(Z, 8);
coord_x = X;
coord_y = Y;
if (coord_s < shared_int4_0.y) {
half4 res = convert_half4(r0);
if (coord_s < 0) {
res += read_imageh(src_tensor_image_buffer, ((0) * shared_int4_1.w + (0)) * shared_int4_2.z + (0));
}
write_imageh(dst_tensor_image2d, (int2) ((coord_x), ((coord_y) *shared_int4_0.y + (coord_s))), res);
coord_s++;
}
if (coord_s < shared_int4_0.y) {
half4 res = convert_half4(r1);
if (coord_s < 0) {
res += read_imageh(src_tensor_image_buffer, ((0) * shared_int4_1.w + (0)) * shared_int4_2.z + (0));
}
write_imageh(dst_tensor_image2d, (int2) ((coord_x), ((coord_y) *shared_int4_0.y + (coord_s))), res);
coord_s++;
}
if (coord_s < shared_int4_0.y) {
half4 res = convert_half4(r2);
if (coord_s < 0) {
res += read_imageh(src_tensor_image_buffer, ((0) * shared_int4_1.w + (0)) * shared_int4_2.z + (0));
}
write_imageh(dst_tensor_image2d, (int2) ((coord_x), ((coord_y) *shared_int4_0.y + (coord_s))), res);
coord_s++;
}
if (coord_s < shared_int4_0.y) {
half4 res = convert_half4(r3);
if (coord_s < 0) {
res += read_imageh(src_tensor_image_buffer, ((0) * shared_int4_1.w + (0)) * shared_int4_2.z + (0));
}
write_imageh(dst_tensor_image2d, (int2) ((coord_x), ((coord_y) *shared_int4_0.y + (coord_s))), res);
coord_s++;
}
if (coord_s < shared_int4_0.y) {
half4 res = convert_half4(r4);
if (coord_s < 0) {
res += read_imageh(src_tensor_image_buffer, ((0) * shared_int4_1.w + (0)) * shared_int4_2.z + (0));
}
write_imageh(dst_tensor_image2d, (int2) ((coord_x), ((coord_y) *shared_int4_0.y + (coord_s))), res);
coord_s++;
}
if (coord_s < shared_int4_0.y) {
half4 res = convert_half4(r5);
if (coord_s < 0) {
res += read_imageh(src_tensor_image_buffer, ((0) * shared_int4_1.w + (0)) * shared_int4_2.z + (0));
}
write_imageh(dst_tensor_image2d, (int2) ((coord_x), ((coord_y) *shared_int4_0.y + (coord_s))), res);
coord_s++;
}
if (coord_s < shared_int4_0.y) {
half4 res = convert_half4(r6);
if (coord_s < 0) {
res += read_imageh(src_tensor_image_buffer, ((0) * shared_int4_1.w + (0)) * shared_int4_2.z + (0));
}
write_imageh(dst_tensor_image2d, (int2) ((coord_x), ((coord_y) *shared_int4_0.y + (coord_s))), res);
coord_s++;
}
if (coord_s < shared_int4_0.y) {
half4 res = convert_half4(r7);
if (coord_s < 0) {
res += read_imageh(src_tensor_image_buffer, ((0) * shared_int4_1.w + (0)) * shared_int4_2.z + (0));
}
write_imageh(dst_tensor_image2d, (int2) ((coord_x), ((coord_y) *shared_int4_0.y + (coord_s))), res);
coord_s++;
}
}