// @generated by tool/gen/fn/generate.py; DO NOT EDIT.
// schema=matrix_blas.json schema_version=1 generator_version=64
// schema_sha256=31a29676780c696e7f6a77f290398587a9e29e612903c5816503e35d3dda8687
// Semantic operation: matrix.mat_mul_nt; stable kernel ID: 21.
// FP32 specialization of OA's established 64x64x16 tiled GEMM arithmetic.
import storage;
import attributes;
struct PushConstants {
uint left_index;
uint right_index;
uint output_index;
uint m;
uint n;
uint k;
};
[[vk::push_constant]] PushConstants push;
[[vk::binding(0, 0)]] RWByteAddressBuffer storage_buffers[];
static const uint BM = 64;
static const uint BN = 64;
static const uint BK = 16;
static const uint TM = 4;
static const uint TN = 4;
static const uint THREADS_COL = BN / TN;
static const uint LOAD_WIDTH = 4;
static const uint BK_PAD = BK + 1;
groupshared float shared_left[BM * BK_PAD];
groupshared float shared_right[BN * BK_PAD];
void load_slice(
uint row,
uint k_begin,
uint buffer_index,
uint row_limit,
out float values[LOAD_WIDTH]
) {
if (row < row_limit && k_begin + LOAD_WIDTH <= push.k) {
float4 packed = storage_buffers[buffer_index].Load<float4>(
(row * push.k + k_begin) * 4u
);
values[0] = packed.x;
values[1] = packed.y;
values[2] = packed.z;
values[3] = packed.w;
} else {
[[unroll]] for (uint element = 0; element < LOAD_WIDTH; ++element) {
values[element] = row < row_limit && k_begin + element < push.k
? load_f32(storage_buffers[buffer_index], row * push.k + k_begin + element)
: 0.0f;
}
}
}
[kernel_name("mat_mul_nt")]
[domain("matrix")]
[variant("tiled")]
[dtype("f32")]
[status("experimental")]
[shader("compute")]
[numthreads(256, 1, 1)]
void main(uint3 group_id : SV_GroupID, uint3 group_thread_id : SV_GroupThreadID) {
uint thread = group_thread_id.x;
uint thread_row = thread / THREADS_COL;
uint thread_column = thread % THREADS_COL;
uint row_base = group_id.x * BM;
uint column_base = group_id.y * BN;
uint shared_row = (thread * LOAD_WIDTH) / BK;
uint shared_column = (thread * LOAD_WIDTH) % BK;
uint shared_offset = shared_row * BK_PAD + shared_column;
float accumulators[TM * TN];
[[unroll]] for (uint index = 0; index < TM * TN; ++index) {
accumulators[index] = 0.0f;
}
for (uint k_base = 0; k_base < push.k; k_base += BK) {
float left_values[LOAD_WIDTH];
float right_values[LOAD_WIDTH];
load_slice(
row_base + shared_row,
k_base + shared_column,
push.left_index,
push.m,
left_values
);
load_slice(
column_base + shared_row,
k_base + shared_column,
push.right_index,
push.n,
right_values
);
[[unroll]] for (uint element = 0; element < LOAD_WIDTH; ++element) {
shared_left[shared_offset + element] = left_values[element];
shared_right[shared_offset + element] = right_values[element];
}
GroupMemoryBarrierWithGroupSync();
[[unroll]] for (uint inner = 0; inner < BK; ++inner) {
float left_fragment[TM];
float right_fragment[TN];
[[unroll]] for (uint row = 0; row < TM; ++row) {
left_fragment[row] = shared_left[(thread_row * TM + row) * BK_PAD + inner];
}
[[unroll]] for (uint column = 0; column < TN; ++column) {
right_fragment[column] = shared_right[(thread_column * TN + column) * BK_PAD + inner];
}
[[unroll]] for (uint row = 0; row < TM; ++row) {
[[unroll]] for (uint column = 0; column < TN; ++column) {
accumulators[row * TN + column] += left_fragment[row] * right_fragment[column];
}
}
}
GroupMemoryBarrierWithGroupSync();
}
[[unroll]] for (uint row = 0; row < TM; ++row) {
[[unroll]] for (uint column = 0; column < TN; ++column) {
uint output_row = row_base + thread_row * TM + row;
uint output_column = column_base + thread_column * TN + column;
if (output_row < push.m && output_column < push.n) {
store_f32(
storage_buffers[push.output_index],
output_row * push.n + output_column,
accumulators[row * TN + column]
);
}
}
}
}