opencl: avoid the vec path in GEMV for unaligned row stride (#25671)

The f16 GEMV kernels take a vectorized path for ne00 >= 128 that casts the row
pointers to half4 or float4. When the row stride is not aligned, the wide load
becomes misaligned. On devices that require natural alignment for vector loads,
the kernel reads garbage. This is the case Intel GPUs and the kernels produce
incorrect results there. Adreno happpens to be byte addressable and the kernels
happen to work.
This commit is contained in:
Hongqiang Wang
2026-07-14 12:27:56 -07:00
committed by GitHub
parent c71854292f
commit a4ce2595c5
3 changed files with 24 additions and 3 deletions
@@ -64,7 +64,14 @@ kernel void kernel_mul_mat_f16_f16(
global half * x = (global half *) (src0 + offset_src0);
if (ne00 < 128) {
// The vector path below casts the row pointers to half4, which must be 8-byte aligned.
// A row address is r0*nb01 + ..., and a permuted or strided src leaves nb01/nb11
// unconstrained -- an odd ne00, say, gives a row that is only 2-byte aligned. Every
// src1 row this work-item walks is src1_base + r1*nb11, so require both.
const ulong src1_base = (ulong) (src1 + (i12)*nb12 + (i13)*nb13);
const bool row_aligned = (((ulong) x) & 7) == 0 && (src1_base & 7) == 0 && (nb11 & 7) == 0;
if (ne00 < 128 || !row_aligned) {
for (int row = 0; row < N_F16_F16; ++row) {
int r1 = rb + row;
if (r1 >= ne11) {
@@ -64,7 +64,14 @@ kernel void kernel_mul_mat_f16_f32(
global half * x = (global half *) (src0 + offset_src0);
if (ne00 < 128) {
// The vector path below casts the row pointers to half4/float4, which must be 8- and
// 16-byte aligned. A row address is r0*nb01 + ..., and a permuted or strided src leaves
// nb01/nb11 unconstrained -- an odd ne00, say, gives a row that is only 2-byte aligned.
// Every src1 row this work-item walks is src1_base + r1*nb11, so require both.
const ulong src1_base = (ulong) (src1 + (i12)*nb12 + (i13)*nb13);
const bool row_aligned = (((ulong) x) & 7) == 0 && (src1_base & 15) == 0 && (nb11 & 15) == 0;
if (ne00 < 128 || !row_aligned) {
for (int row = 0; row < N_F16_F32; ++row) {
int r1 = rb + row;
if (r1 >= ne11) {
@@ -64,8 +64,15 @@ kernel void kernel_mul_mat_f16_f32_1row(
global half * x = (global half *) (src0 + offset_src0);
global float * y = (global float *) (src1 + offset_src1);
// The vector path below casts the row pointers to half4/float4, which must be 8- and
// 16-byte aligned. A row address is r0*nb01 + ..., and a permuted or strided src leaves
// nb01/nb11 unconstrained -- an odd ne00, say, gives a row that is only 2-byte aligned.
// Take the vector path only when the rows this work-item touches are actually aligned;
// the scalar loop has no such requirement.
const bool row_aligned = (((ulong) x) & 7) == 0 && (((ulong) y) & 15) == 0;
float sumf = 0;
if (ne00 < 128) {
if (ne00 < 128 || !row_aligned) {
for (int i = get_sub_group_local_id(); i < ne00; i += get_max_sub_group_size()) {
sumf += (float) x[i] * (float) y[i];
}