12constexpr int kLocalSize = 256;
14std::string header(
int localX,
int localY = 1) {
15 std::ostringstream
os;
16 os <<
"#version 450\n";
17 os <<
"layout(local_size_x = " << localX;
18 if (localY > 1)
os <<
", local_size_y = " << localY;
23std::string bufferDecl(
int binding,
const char *
name) {
24 std::ostringstream
os;
25 os <<
"layout(set = 0, binding = " << binding <<
") buffer B" << binding <<
" { float "
26 <<
name <<
"[]; };\n";
30std::string bufferDeclUint(
int binding,
const char *
name) {
31 std::ostringstream
os;
32 os <<
"layout(set = 0, binding = " << binding <<
") buffer B" << binding <<
" { uint "
33 <<
name <<
"[]; };\n";
42std::string emitQuantizedBVal(
DType dt,
int group,
const char *bufName =
"b") {
43 std::ostringstream
os;
47 os <<
"float bval(uint idx) {\n"
48 <<
" uint word = " << bufName <<
"[idx >> 2u];\n"
49 <<
" uint byte = (word >> ((idx & 3u) * 8u)) & 0xFFu;\n"
50 <<
" int v = int(byte);\n"
51 <<
" if (v >= 128) v -= 256;\n"
52 <<
" return float(v) * bs[idx / " << g <<
"u];\n"
56 os <<
"float bval(uint idx) {\n"
57 <<
" uint word = " << bufName <<
"[idx >> 3u];\n"
58 <<
" uint byte = (word >> (((idx >> 1u) & 3u) * 8u)) & 0xFFu;\n"
59 <<
" uint nib = (idx & 1u) == 0u ? (byte & 0xFu) : (byte >> 4u);\n"
60 <<
" int v = int(nib);\n"
61 <<
" if (v >= 8) v -= 16;\n"
62 <<
" return float(v) * bs[idx / " << g <<
"u];\n"
66 os <<
"float bval(uint idx) {\n"
67 <<
" uint word = " << bufName <<
"[idx >> 1u];\n"
68 <<
" uint hb = (word >> ((idx & 1u) * 16u)) & 0xFFFFu;\n"
69 <<
" return unpackHalf2x16(hb).x;\n"
73 os <<
"float bval(uint idx) {\n"
74 <<
" uint word = " << bufName <<
"[idx >> 2u];\n"
75 <<
" uint byte = (word >> ((idx & 3u) * 8u)) & 0xFFu;\n"
76 <<
" int s = (int(byte & 0x80u) != 0) ? -1 : 1;\n"
77 <<
" int e = int((byte >> 3u) & 0xFu);\n"
78 <<
" int m = int(byte & 0x7u);\n"
79 <<
" float v = (e == 0) ? exp2(-6.0) * float(m) / 8.0\n"
80 <<
" : exp2(float(e - 7)) * (1.0 + float(m) / 8.0);\n"
81 <<
" return float(s) * v * bs[idx / " << g <<
"u];\n"
85 os <<
"float bval(uint idx) {\n"
86 <<
" uint word = " << bufName <<
"[idx >> 3u];\n"
87 <<
" uint byte = (word >> (((idx >> 1u) & 3u) * 8u)) & 0xFFu;\n"
88 <<
" uint nib = (idx & 1u) == 0u ? (byte & 0xFu) : (byte >> 4u);\n"
89 <<
" int s = (int(nib & 8u) != 0) ? -1 : 1;\n"
90 <<
" int e = int((nib >> 1u) & 3u);\n"
91 <<
" int m = int(nib & 1u);\n"
92 <<
" float v = (e == 0) ? 0.5 * float(m)\n"
93 <<
" : exp2(float(e - 1)) * (1.0 + 0.5 * float(m));\n"
94 <<
" return float(s) * v * bs[idx / " << g <<
"u];\n"
102std::string pushConstant() {
103 return "layout(push_constant) uniform PC { float data[32]; } pc;\n";
106int groupsFor(
int count) {
return (count + kLocalSize - 1) / kLocalSize; }
108std::string scalarStr(
float v) {
109 if (
v ==
int(
v) && std::fabs(
v) < 1e9f)
return std::to_string(
int(
v)) +
".0";
110 std::ostringstream
os;
125 std::ostringstream &
os;
129 std::string operand(
int nodeId) {
132 const auto it = std::find(
group.inputs.begin(),
group.inputs.end(), nodeId);
133 if (it !=
group.inputs.end()) {
134 const size_t idx =
static_cast<size_t>(it -
group.inputs.begin());
137 return vars[
static_cast<size_t>(nodeId)];
142 std::string emit(
int nodeId) {
143 const GraphNode &nd =
graph.node(nodeId);
145 : (nd.in0 >= 0 ? operand(nd.in0) :
rootVar);
148 case OpType::Add: expr =
"(" +
x +
" + " + operand(nd.in1) +
")";
break;
149 case OpType::Sub: expr =
"(" +
x +
" - " + operand(nd.in1) +
")";
break;
151 case OpType::Divide: expr =
"(" +
x +
" / " + operand(nd.in1) +
")";
break;
168 expr =
"(0.5 * " +
x +
169 " * (1.0 + tanh(0.7978845608028654 * (" +
x +
170 " + 0.044715 * " +
x +
" * " +
x +
" * " +
x +
"))))";
172 case OpType::Silu: expr =
"(" +
x +
" / (1.0 + exp(-" +
x +
")))";
break;
174 float lo = nd.s0, hi = nd.s1;
175 if (lo > hi) std::swap(lo, hi);
176 expr =
"clamp(" +
x +
", " + scalarStr(lo) +
", " + scalarStr(hi) +
")";
182 expr =
"(" + operand(nd.in0) +
" > 0.5 ? " + operand(nd.in1) +
" : " +
183 operand(nd.in2) +
")";
187 const std::string var =
"t" + std::to_string(
temp++);
188 os <<
" float " << var <<
" = " << expr <<
";\n";
189 if (
vars.size() <=
static_cast<size_t>(nodeId))
vars.resize(
static_cast<size_t>(nodeId) + 1);
190 vars[
static_cast<size_t>(nodeId)] = var;
200void emitInputIndexExprs(std::ostringstream &
os,
const Graph &g,
const FusedGroup &grp,
202 const GraphNode &on = g.node(grp.outputNode);
203 const int rank = on.rank;
204 std::vector<int> S(
static_cast<size_t>(rank), 1);
206 S[
static_cast<size_t>(rank - 1)] = 1;
207 for (
int k = rank - 2; k >= 0; --k) S[
static_cast<size_t>(k)] =
208 S[
static_cast<size_t>(k + 1)] * on.dims[k + 1];
211 for (
size_t k = 0; k < grp.inputs.size(); ++k) {
212 const GraphNode &inN = g.node(grp.inputs[k]);
213 bool identical = inN.rank == rank;
215 for (
int d = 0;
d < rank; ++
d)
216 if (inN.dims[
d] != on.dims[
d]) {
225 const int pad = rank - inN.rank;
226 std::vector<int> stride(
static_cast<size_t>(rank), 0);
227 for (
int d = 0;
d < rank; ++
d) {
228 const int dim =
d < pad ? 1 : inN.dims[
d - pad];
229 if (dim == 1)
continue;
231 for (
int t =
d + 1; t < rank; ++t) {
232 const int dt = t < pad ? 1 : inN.dims[t - pad];
233 if (dt != 1)
s *= dt;
235 stride[
static_cast<size_t>(
d)] =
s;
237 const std::string var =
"idx" + std::to_string(k);
238 os <<
" uint " << var <<
" = 0u;\n";
239 for (
int d = 0;
d < rank; ++
d) {
240 if (stride[
static_cast<size_t>(
d)] == 0)
continue;
241 os <<
" " << var <<
" += ((i_ / " << S[
static_cast<size_t>(
d)] <<
"u) % "
242 << on.dims[
d] <<
"u) * " << stride[
static_cast<size_t>(
d)] <<
"u;\n";
248bool genElementwise(
const Graph &g,
const FusedGroup &grp, KernelSpec &out) {
250 const GraphNode &on = g.node(grp.outputNode);
251 std::ostringstream
os;
252 os << header(kLocalSize);
253 for (
size_t k = 0; k < grp.inputs.size(); ++k)
254 os << bufferDecl(
int(k), (
"a" + std::to_string(k)).c_str());
255 os << bufferDecl(
int(grp.inputs.size()),
"o");
256 os << pushConstant();
257 os <<
"void main() {\n";
258 os <<
" uint i_ = gl_GlobalInvocationID.x;\n";
259 os <<
" if (i_ >= " << on.size <<
"u) return;\n";
263 for (
int u : grp.nodes) ctx.emit(
u);
264 os <<
" o[i_] = " << ctx.vars[
static_cast<size_t>(grp.outputNode)] <<
";\n";
267 out.pass2 =
os.str();
268 out.groupsX2 = groupsFor(on.size);
269 out.inputCount = int(grp.inputs.size());
274bool genMatMul(
const Graph &g,
const FusedGroup &grp,
bool tiled, KernelSpec &out) {
275 const GraphNode &mm = g.node(grp.nodes.front());
276 const GraphNode &A = g.node(mm.in0);
277 const GraphNode &B = g.node(mm.in1);
278 const bool batched = mm.rank == 3;
279 const int batch = batched ? A.dims[0] : 1;
280 const int m = A.dims[batched ? 1 : 0];
281 const int k = A.dims[batched ? 2 : 1];
282 const int n = B.dims[batched ? 2 : 1];
283 const bool hasBias = grp.biasNode >= 0;
285 if (bQuant && tiled)
return false;
286 const int scalesBinding = hasBias ? 3 : 2;
287 const int bindingOut = bQuant ? (hasBias ? 4 : 3) : (hasBias ? 3 : 2);
289 std::ostringstream
os;
291 os << header(kLocalSize);
293 os << header(16, 16);
295 os << bufferDecl(0,
"a");
296 if (bQuant)
os << bufferDeclUint(1,
"b");
297 else os << bufferDecl(1,
"b");
298 if (hasBias)
os << bufferDecl(2,
"bias");
299 if (bQuant && B.dtype !=
static_cast<int>(
DType::Fp16))
300 os << bufferDecl(scalesBinding,
"bs");
301 os << bufferDecl(bindingOut,
"o");
302 os << pushConstant();
303 if (bQuant) os << emitQuantizedBVal(static_cast<DType>(B.dtype), B.qGroup);
305 std::vector<std::string>
indexExprs(grp.inputs.size(),
"i_");
307 ctx.biasIndexExpr =
"jj";
310 os <<
"void main() {\n";
311 os <<
" uint i_ = gl_GlobalInvocationID.x;\n";
312 os <<
" if (i_ >= " << batch *
m *
n <<
"u) return;\n";
314 os <<
" uint bb = i_ / " <<
m *
n <<
"u;\n";
315 os <<
" uint rem = i_ % " <<
m *
n <<
"u;\n";
316 os <<
" uint ii = rem / " <<
n <<
"u;\n";
317 os <<
" uint jj = rem % " <<
n <<
"u;\n";
319 os <<
" uint ii = i_ / " <<
n <<
"u;\n";
320 os <<
" uint jj = i_ % " <<
n <<
"u;\n";
322 os <<
" float r = 0.0;\n";
323 os <<
" for (uint t = 0u; t < " << k <<
"u; ++t) {\n";
325 os <<
" r += a[bb * " <<
m * k <<
"u + ii * " << k <<
"u + t] * "
326 << (bQuant ?
"bval(bb * " + std::to_string(k *
n) +
"u + t * " +
327 std::to_string(
n) +
"u + jj)"
328 :
"b[bb * " + std::to_string(k *
n) +
"u + t * " +
329 std::to_string(
n) +
"u + jj]")
332 os <<
" r += a[ii * " << k <<
"u + t] * "
333 << (bQuant ?
"bval(t * " + std::to_string(
n) +
"u + jj)"
334 :
"b[t * " + std::to_string(
n) +
"u + jj]")
338 for (
int u : grp.epilogue) ctx.emit(
u);
339 const std::string finalExpr =
340 grp.epilogue.empty() ? std::string(
"r") : ctx.
vars[static_cast<size_t>(grp.outputNode)];
341 os <<
" o[i_] = " << finalExpr <<
";\n";
343 out.groupsX2 = groupsFor(batch *
m *
n);
345 ctx.biasIndexExpr =
"col";
346 const int gx = (
n + 15) / 16;
347 const int gy = (
m + 15) / 16;
348 os <<
"shared float As[16][17];\n";
349 os <<
"shared float Bs[16][17];\n";
350 os <<
"void main() {\n";
351 os <<
" uint tx = gl_LocalInvocationID.x;\n";
352 os <<
" uint ty = gl_LocalInvocationID.y;\n";
353 os <<
" uint row = gl_GlobalInvocationID.y;\n";
354 os <<
" uint col = gl_GlobalInvocationID.x;\n";
355 os <<
" float acc = 0.0;\n";
356 os <<
" for (uint tile = 0u; tile < " << (k + 15) / 16 <<
"u; ++tile) {\n";
357 os <<
" uint aCol = tile * 16u + tx;\n";
358 os <<
" uint bRow = tile * 16u + ty;\n";
359 os <<
" As[ty][tx] = (aCol < " << k <<
"u && row < " <<
m <<
"u) ? a[row * "
360 << k <<
"u + aCol] : 0.0;\n";
361 os <<
" Bs[ty][tx] = (bRow < " << k <<
"u && col < " <<
n <<
"u) ? b[bRow * "
362 <<
n <<
"u + col] : 0.0;\n";
363 os <<
" barrier();\n";
364 os <<
" for (uint t = 0u; t < 16u; ++t) acc += As[ty][t] * Bs[t][tx];\n";
365 os <<
" barrier();\n";
367 os <<
" float r = acc;\n";
368 for (
int u : grp.epilogue) ctx.emit(
u);
369 const std::string finalExpr =
370 grp.epilogue.empty() ? std::string(
"r") : ctx.
vars[static_cast<size_t>(grp.outputNode)];
371 os <<
" if (row < " <<
m <<
"u && col < " <<
n <<
"u) o[row * " <<
n <<
"u + col] = "
372 << finalExpr <<
";\n";
378 out.pass2 =
os.str();
379 out.inputCount = hasBias ? 3 : 2;
380 out.qDtype = bQuant ? B.dtype : 0;
381 out.qGroup = B.qGroup;
382 out.scalesBinding = bQuant && B.dtype !=
static_cast<int>(
DType::Fp16) ? scalesBinding : -1;
383 out.outputBinding = bQuant ? bindingOut : -1;
384 if (tiled && batched)
return false;
388bool genConv(
const Graph &g,
const FusedGroup &grp, KernelSpec &out) {
389 const GraphNode &cn = g.node(grp.nodes.front());
390 const GraphNode &
X = g.node(cn.in0);
391 const GraphNode &Wt = g.node(cn.in1);
393 const int stride = cn.i0, pad = cn.i1;
394 const bool hasBias = grp.biasNode >= 0;
395 const int bindingOut = hasBias ? 3 : 2;
397 std::ostringstream
os;
398 os << header(kLocalSize);
399 os << bufferDecl(0,
"x");
400 os << bufferDecl(1,
"w");
401 if (hasBias)
os << bufferDecl(2,
"bias");
402 os << bufferDecl(bindingOut,
"o");
403 os << pushConstant();
404 std::vector<std::string>
indexExprs(grp.inputs.size(),
"i_");
406 ctx.biasIndexExpr =
"f";
408 os <<
"void main() {\n";
409 os <<
" uint i_ = gl_GlobalInvocationID.x;\n";
410 os <<
" if (i_ >= " << g.node(grp.outputNode).size <<
"u) return;\n";
412 const int N =
X.dims[0], C =
X.dims[1], L =
X.dims[2];
413 const int F = Wt.dims[0], K = Wt.dims[2];
414 os <<
" uint n_ = i_ / (" << F <<
"u * " << cn.dims[2] <<
"u);\n";
415 os <<
" uint rem = i_ % (" << F <<
"u * " << cn.dims[2] <<
"u);\n";
416 os <<
" uint f = rem / " << cn.dims[2] <<
"u;\n";
417 os <<
" uint ol = rem % " << cn.dims[2] <<
"u;\n";
418 os <<
" float r = " << (hasBias ?
"bias[f]" :
"0.0") <<
";\n";
419 os <<
" for (uint c = 0u; c < " << C <<
"u; ++c) {\n";
420 os <<
" for (uint kk = 0u; kk < " << K <<
"u; ++kk) {\n";
421 os <<
" int il = int(ol) * " << stride <<
" + int(kk) - " << pad <<
";\n";
422 os <<
" if (il < 0 || il >= " << L <<
") continue;\n";
423 os <<
" r += x[(n_ * " << C <<
"u + c) * " << L <<
"u + uint(il)] * w[(f * "
424 << C <<
"u + c) * " << K <<
"u + kk];\n";
428 const int N =
X.dims[0], C =
X.dims[1], H =
X.dims[2], W =
X.dims[3];
429 const int F = Wt.dims[0], KH = Wt.dims[2], KW = Wt.dims[3];
430 os <<
" uint n_ = i_ / (" << F <<
"u * " << cn.dims[2] <<
"u * " << cn.dims[3] <<
"u);\n";
431 os <<
" uint rem = i_ % (" << F <<
"u * " << cn.dims[2] <<
"u * " << cn.dims[3] <<
"u);\n";
432 os <<
" uint f = rem / (" << cn.dims[2] <<
"u * " << cn.dims[3] <<
"u);\n";
433 os <<
" uint rem2 = rem % (" << cn.dims[2] <<
"u * " << cn.dims[3] <<
"u);\n";
434 os <<
" uint oh = rem2 / " << cn.dims[3] <<
"u;\n";
435 os <<
" uint ow = rem2 % " << cn.dims[3] <<
"u;\n";
436 os <<
" float r = " << (hasBias ?
"bias[f]" :
"0.0") <<
";\n";
437 os <<
" for (uint c = 0u; c < " << C <<
"u; ++c) {\n";
438 os <<
" for (uint kh = 0u; kh < " << KH <<
"u; ++kh) {\n";
439 os <<
" int ih = int(oh) * " << stride <<
" + int(kh) - " << pad <<
";\n";
440 os <<
" if (ih < 0 || ih >= " << H <<
") continue;\n";
441 os <<
" for (uint kw = 0u; kw < " << KW <<
"u; ++kw) {\n";
442 os <<
" int iw = int(ow) * " << stride <<
" + int(kw) - " << pad <<
";\n";
443 os <<
" if (iw < 0 || iw >= " << W <<
") continue;\n";
444 os <<
" r += x[((n_ * " << C <<
"u + c) * " << H <<
"u + uint(ih)) * " << W
445 <<
"u + uint(iw)] * w[((f * " << C <<
"u + c) * " << KH <<
"u + kh) * " << KW
451 for (
int u : grp.epilogue) ctx.emit(
u);
452 const std::string finalExpr =
453 grp.epilogue.empty() ? std::string(
"r") : ctx.
vars[static_cast<size_t>(grp.outputNode)];
454 os <<
" o[i_] = " << finalExpr <<
";\n";
457 out.pass2 =
os.str();
458 out.groupsX2 = groupsFor(g.node(grp.outputNode).size);
459 out.inputCount = hasBias ? 3 : 2;
463bool genPool(
const Graph &g,
const FusedGroup &grp, KernelSpec &out) {
464 const GraphNode &pn = g.node(grp.outputNode);
465 const GraphNode &
X = g.node(pn.in0);
466 const int ksize = pn.i0, stride = pn.i1, pad = pn.i2;
467 const int N =
X.dims[0], C =
X.dims[1], H =
X.dims[2], W =
X.dims[3];
468 const int OH = pn.dims[2], OW = pn.dims[3];
470 std::ostringstream
os;
471 os << header(kLocalSize);
472 os << bufferDecl(0,
"in_");
473 os << bufferDecl(1,
"o");
474 os << pushConstant();
475 os <<
"void main() {\n";
476 os <<
" uint i_ = gl_GlobalInvocationID.x;\n";
477 os <<
" if (i_ >= " << pn.size <<
"u) return;\n";
478 os <<
" uint n_ = i_ / (" << C <<
"u * " << OH <<
"u * " << OW <<
"u);\n";
479 os <<
" uint rem = i_ % (" << C <<
"u * " << OH <<
"u * " << OW <<
"u);\n";
480 os <<
" uint c = rem / (" << OH <<
"u * " << OW <<
"u);\n";
481 os <<
" uint rem2 = rem % (" << OH <<
"u * " << OW <<
"u);\n";
482 os <<
" uint oh = rem2 / " << OW <<
"u;\n";
483 os <<
" uint ow = rem2 % " << OW <<
"u;\n";
484 os <<
" float acc = " << (maxPool ?
"-3.402823e38" :
"0.0") <<
";\n";
485 os <<
" int valid = 0;\n";
486 os <<
" for (int kh = 0; kh < " << ksize <<
"; ++kh) {\n";
487 os <<
" int ih = int(oh) * " << stride <<
" + kh - " << pad <<
";\n";
488 os <<
" if (ih < 0 || ih >= " << H <<
") continue;\n";
489 os <<
" for (int kw = 0; kw < " << ksize <<
"; ++kw) {\n";
490 os <<
" int iw = int(ow) * " << stride <<
" + kw - " << pad <<
";\n";
491 os <<
" if (iw < 0 || iw >= " << W <<
") continue;\n";
492 os <<
" float v = in_[((n_ * " << C <<
"u + c) * " << H <<
"u + uint(ih)) * " << W
493 <<
"u + uint(iw)];\n";
495 os <<
" acc = max(acc, v);\n";
497 os <<
" acc += v; ++valid;\n";
501 if (!maxPool)
os <<
" acc = valid > 0 ? acc / float(valid) : 0.0;\n";
502 os <<
" o[i_] = acc;\n";
505 out.pass2 =
os.str();
506 out.groupsX2 = groupsFor(pn.size);
511bool genSoftmax(
const Graph &g,
const FusedGroup &grp, KernelSpec &out) {
512 const GraphNode &sn = g.node(grp.outputNode);
513 const GraphNode &
X = g.node(sn.in0);
514 const int axis = sn.i0;
515 int outer = 1,
reduce = 1, inner = 1;
516 for (
int k = 0; k < axis; ++k) outer *=
X.dims[k];
518 for (
int k = axis + 1; k <
X.rank; ++k) inner *=
X.dims[k];
519 const int rows = outer * inner;
520 const bool logMode = grp.logMode;
522 std::ostringstream os1, os2;
523 os1 << header(kLocalSize);
524 os1 << bufferDecl(0,
"in_");
525 os1 << bufferDecl(1,
"mx");
526 os1 << bufferDecl(2,
"sm");
527 os1 << pushConstant();
528 os1 <<
"void main() {\n";
529 os1 <<
" uint i_ = gl_GlobalInvocationID.x;\n";
530 os1 <<
" if (i_ >= " << rows <<
"u) return;\n";
531 os1 <<
" uint o_ = i_ / " << inner <<
"u;\n";
532 os1 <<
" uint ii = i_ % " << inner <<
"u;\n";
533 os1 <<
" float m = -3.402823e38;\n";
534 os1 <<
" for (uint j = 0u; j < " <<
reduce <<
"u; ++j) {\n";
535 os1 <<
" m = max(m, in_[(o_ * " <<
reduce <<
"u + j) * " << inner <<
"u + ii]);\n";
537 os1 <<
" float s = 0.0;\n";
538 os1 <<
" for (uint j = 0u; j < " <<
reduce <<
"u; ++j) {\n";
539 os1 <<
" s += exp(in_[(o_ * " <<
reduce <<
"u + j) * " << inner <<
"u + ii] - m);\n";
541 os1 <<
" mx[i_] = m;\n";
542 os1 <<
" sm[i_] = s;\n";
545 os2 << header(kLocalSize);
546 os2 << bufferDecl(0,
"in_");
547 os2 << bufferDecl(1,
"mx");
548 os2 << bufferDecl(2,
"sm");
549 os2 << bufferDecl(3,
"o");
550 os2 << pushConstant();
551 os2 <<
"void main() {\n";
552 os2 <<
" uint i_ = gl_GlobalInvocationID.x;\n";
553 os2 <<
" if (i_ >= " << sn.size <<
"u) return;\n";
554 os2 <<
" uint o_ = (i_ / (" <<
reduce * inner <<
"u)) * " << inner <<
"u + (i_ % "
556 os2 <<
" float m = mx[o_];\n";
557 os2 <<
" float s = sm[o_];\n";
558 os2 <<
" float x = in_[i_];\n";
560 os2 <<
" o[i_] = (x - m) - log(s);\n";
562 os2 <<
" o[i_] = exp(x - m) / s;\n";
565 out.pass1 = os1.str();
566 out.pass2 = os2.str();
567 out.groupsX1 = groupsFor(rows);
568 out.groupsX2 = groupsFor(sn.size);
570 out.inputsReadPass1 = 1;
572 out.statsSize = rows;
577bool genNorm(
const Graph &g,
const FusedGroup &grp,
bool rms, KernelSpec &out) {
578 const GraphNode &nn = g.node(grp.outputNode);
579 const GraphNode &
X = g.node(nn.in0);
580 const int cols =
X.dims[
X.rank - 1];
581 const int rows =
X.size / cols;
582 const float eps = nn.s0;
583 const bool hasScale = grp.hasScale;
584 const bool hasBias = !rms && grp.hasBias;
585 const int inputCount = 1 + (hasScale ? 1 : 0) + (hasBias ? 1 : 0);
586 const int statsCount = rms ? 1 : 2;
587 const int outBinding = inputCount + statsCount;
589 std::ostringstream os1, os2;
590 os1 << header(kLocalSize);
591 os1 << bufferDecl(0,
"in_");
592 os1 << bufferDecl(1,
"st0");
593 if (statsCount > 1) os1 << bufferDecl(2,
"st1");
594 os1 << pushConstant();
595 os1 <<
"void main() {\n";
596 os1 <<
" uint i_ = gl_GlobalInvocationID.x;\n";
597 os1 <<
" if (i_ >= " << rows <<
"u) return;\n";
598 os1 <<
" float s0 = 0.0;\n";
599 if (statsCount > 1) os1 <<
" float s1 = 0.0;\n";
600 os1 <<
" for (uint j = 0u; j < " << cols <<
"u; ++j) {\n";
601 os1 <<
" float v = in_[i_ * " << cols <<
"u + j];\n";
603 os1 <<
" s0 += v * v;\n";
605 os1 <<
" s0 += v; s1 += v * v;\n";
608 os1 <<
" st0[i_] = s0;\n";
609 if (statsCount > 1) os1 <<
" st1[i_] = s1;\n";
612 os2 << header(kLocalSize);
613 for (
int k = 0; k < inputCount; ++k)
614 os2 << bufferDecl(k, (
"a" + std::to_string(k)).c_str());
615 os2 << bufferDecl(inputCount,
"st0");
616 if (statsCount > 1) os2 << bufferDecl(inputCount + 1,
"st1");
617 os2 << bufferDecl(outBinding,
"o");
618 os2 << pushConstant();
619 os2 <<
"void main() {\n";
620 os2 <<
" uint i_ = gl_GlobalInvocationID.x;\n";
621 os2 <<
" if (i_ >= " << nn.size <<
"u) return;\n";
622 os2 <<
" uint r = i_ / " << cols <<
"u;\n";
623 os2 <<
" uint c = i_ % " << cols <<
"u;\n";
625 os2 <<
" float inv = 1.0 / sqrt(st0[r] / " << cols <<
".0 + " << scalarStr(eps)
627 os2 <<
" float y = a0[i_] * inv;\n";
628 if (hasScale) os2 <<
" y *= a1[c];\n";
630 os2 <<
" float mean = st0[r] / " << cols <<
".0;\n";
631 os2 <<
" float var = st1[r] / " << cols <<
".0 - mean * mean;\n";
632 os2 <<
" var = max(var, 0.0);\n";
633 os2 <<
" float inv = 1.0 / sqrt(var + " << scalarStr(eps) <<
");\n";
634 os2 <<
" float y = (a0[i_] - mean) * inv;\n";
635 if (hasScale) os2 <<
" y *= a1[c];\n";
636 if (hasBias) os2 <<
" y += a" << (hasScale ? 2 : 1) <<
"[c];\n";
638 os2 <<
" o[i_] = y;\n";
640 out.pass1 = os1.str();
641 out.pass2 = os2.str();
642 out.groupsX1 = groupsFor(rows);
643 out.groupsX2 = groupsFor(nn.size);
644 out.inputCount = inputCount;
645 out.inputsReadPass1 = 1;
646 out.statsCount = statsCount;
647 out.statsSize = rows;
652bool genReduceOrArgmax(
const Graph &g,
const FusedGroup &grp,
bool argmax, KernelSpec &out) {
653 const GraphNode &rn = g.node(grp.outputNode);
654 const GraphNode &
X = g.node(rn.in0);
655 const int axis = rn.i0;
656 int outer = 1,
reduce = 1, inner = 1;
657 for (
int k = 0; k < axis; ++k) outer *=
X.dims[k];
659 for (
int k = axis + 1; k <
X.rank; ++k) inner *=
X.dims[k];
660 const int outSize = outer * inner;
661 std::ostringstream
os;
662 os << header(kLocalSize);
663 os << bufferDecl(0,
"in_");
664 os << bufferDecl(1,
"o");
665 os << pushConstant();
666 os <<
"void main() {\n";
667 os <<
" uint i_ = gl_GlobalInvocationID.x;\n";
668 os <<
" if (i_ >= " << outSize <<
"u) return;\n";
669 os <<
" uint o_ = i_ / " << inner <<
"u;\n";
670 os <<
" uint ii = i_ % " << inner <<
"u;\n";
672 os <<
" float best = -3.402823e38;\n";
673 os <<
" float bestJ = 0.0;\n";
674 os <<
" for (uint j = 0u; j < " <<
reduce <<
"u; ++j) {\n";
675 os <<
" float v = in_[(o_ * " <<
reduce <<
"u + j) * " << inner <<
"u + ii];\n";
676 os <<
" if (v > best) { best = v; bestJ = float(j); }\n";
678 os <<
" o[i_] = bestJ;\n";
683 os <<
" float acc = 0.0;\n";
684 os <<
" for (uint j = 0u; j < " <<
reduce <<
"u; ++j) acc += in_[(o_ * "
685 <<
reduce <<
"u + j) * " << inner <<
"u + ii];\n";
687 os <<
" acc /= " << scalarStr(
float(
reduce)) <<
";\n";
690 os <<
" float acc = 3.402823e38;\n";
691 os <<
" for (uint j = 0u; j < " <<
reduce <<
"u; ++j) acc = min(acc, in_[(o_ * "
692 <<
reduce <<
"u + j) * " << inner <<
"u + ii]);\n";
695 os <<
" float acc = -3.402823e38;\n";
696 os <<
" for (uint j = 0u; j < " <<
reduce <<
"u; ++j) acc = max(acc, in_[(o_ * "
697 <<
reduce <<
"u + j) * " << inner <<
"u + ii]);\n";
699 default:
return false;
701 os <<
" o[i_] = acc;\n";
705 out.pass2 =
os.str();
706 out.groupsX2 = groupsFor(outSize);
711bool genEmbedding(
const Graph &g,
const FusedGroup &grp, KernelSpec &out) {
712 const GraphNode &en = g.node(grp.outputNode);
713 const GraphNode &T = g.node(en.in0);
714 const GraphNode &I = g.node(en.in1);
716 const bool tInt = T.dtype !=
static_cast<int>(
DType::Fp16);
717 const int vocab = T.dims[0], dim = T.dims[1];
718 std::ostringstream
os;
719 os << header(kLocalSize);
720 if (tQuant)
os << bufferDeclUint(0,
"table");
721 else os << bufferDecl(0,
"table");
722 os << bufferDecl(1,
"idx");
723 if (tQuant && tInt)
os << bufferDecl(2,
"bs");
724 os << bufferDecl(tQuant ? 3 : 2,
"o");
725 os << pushConstant();
726 if (tQuant) os << emitQuantizedBVal(static_cast<DType>(T.dtype), T.qGroup,
"table");
727 os <<
"void main() {\n";
728 os <<
" uint i_ = gl_GlobalInvocationID.x;\n";
729 os <<
" if (i_ >= " << en.size <<
"u) return;\n";
730 os <<
" uint r = i_ / " << dim <<
"u;\n";
731 os <<
" uint d = i_ % " << dim <<
"u;\n";
732 os <<
" int ii = int(idx[r]);\n";
733 os <<
" ii = clamp(ii, 0, " << (vocab - 1) <<
");\n";
734 os <<
" o[i_] = " << (tQuant ?
"bval(uint(ii) * " + std::to_string(dim) +
"u + d)"
735 :
"table[uint(ii) * " + std::to_string(dim) +
"u + d]")
739 out.pass2 =
os.str();
740 out.groupsX2 = groupsFor(en.size);
742 out.qDtype = tQuant ? T.dtype : 0;
743 out.qGroup = T.qGroup;
744 out.scalesBinding = tQuant && tInt ? 2 : -1;
745 out.outputBinding = tQuant ? 3 : -1;
749bool genConcat(
const Graph &g,
const FusedGroup &grp, KernelSpec &out) {
750 const GraphNode &cn = g.node(grp.outputNode);
751 const int axis = cn.i0;
752 const int n = int(grp.inputs.size());
753 if (n < 2 || n > 4)
return false;
756 for (
int k = 0; k <
n; ++k) {
757 starts[k] = axisTotal;
758 axisTotal += g.node(grp.inputs[
static_cast<size_t>(k)]).dims[axis];
761 for (
int k = axis + 1; k < cn.rank; ++k) inner *= cn.dims[k];
762 std::ostringstream
os;
763 os << header(kLocalSize);
764 for (
int k = 0; k <
n; ++k)
os << bufferDecl(k, (
"a" + std::to_string(k)).c_str());
765 os << bufferDecl(
n,
"o");
766 os << pushConstant();
767 os <<
"void main() {\n";
768 os <<
" uint i_ = gl_GlobalInvocationID.x;\n";
769 os <<
" if (i_ >= " << cn.size <<
"u) return;\n";
770 os <<
" uint ax = (i_ / " << inner <<
"u) % " << axisTotal <<
"u;\n";
771 os <<
" uint op = i_ / (" << axisTotal <<
"u * " << inner <<
"u);\n";
772 os <<
" uint ip = i_ % " << inner <<
"u;\n";
773 os <<
" float v = 0.0;\n";
774 for (
int k = 0; k <
n; ++k) {
775 const int sz = g.node(grp.inputs[
static_cast<size_t>(k)]).dims[axis];
776 const char *cond = k == 0 ?
"if" :
"else if";
777 os <<
" " << cond <<
" (ax >= " << starts[k] <<
"u && ax < " << starts[k] + sz
779 os <<
" v = a" << k <<
"[op * " << sz <<
"u * " << inner <<
"u + (ax - " << starts[k]
780 <<
"u) * " << inner <<
"u + ip];\n";
783 os <<
" o[i_] = v;\n";
786 out.pass2 =
os.str();
787 out.groupsX2 = groupsFor(cn.size);
792bool genSlice(
const Graph &g,
const FusedGroup &grp, KernelSpec &out) {
793 const GraphNode &sn = g.node(grp.outputNode);
794 const GraphNode &
X = g.node(sn.in0);
795 const int axis = sn.i0, begin = sn.i1, end = sn.i2;
796 const int axisSize = end - begin;
798 for (
int k = axis + 1; k < sn.rank; ++k) inner *= sn.dims[k];
799 std::ostringstream
os;
800 os << header(kLocalSize);
801 os << bufferDecl(0,
"in_");
802 os << bufferDecl(1,
"o");
803 os << pushConstant();
804 os <<
"void main() {\n";
805 os <<
" uint i_ = gl_GlobalInvocationID.x;\n";
806 os <<
" if (i_ >= " << sn.size <<
"u) return;\n";
807 os <<
" uint ax = (i_ / " << inner <<
"u) % " << axisSize <<
"u;\n";
808 os <<
" uint op = i_ / (" << axisSize <<
"u * " << inner <<
"u);\n";
809 os <<
" uint ip = i_ % " << inner <<
"u;\n";
810 os <<
" o[i_] = in_[op * " <<
X.dims[axis] <<
"u * " << inner <<
"u + (ax + " << begin
811 <<
"u) * " << inner <<
"u + ip];\n";
814 out.pass2 =
os.str();
815 out.groupsX2 = groupsFor(sn.size);
820bool genPermute(
const Graph &g,
const FusedGroup &grp, KernelSpec &out) {
821 const GraphNode &pn = g.node(grp.outputNode);
822 const GraphNode &
X = g.node(pn.in0);
823 const int rank = pn.rank;
826 for (
int k = rank - 2; k >= 0; --k) S[k] = S[k + 1] * pn.dims[k + 1];
828 inStride[rank - 1] = 1;
829 for (
int k = rank - 2; k >= 0; --k) inStride[k] = inStride[k + 1] *
X.dims[k + 1];
830 std::ostringstream
os;
831 os << header(kLocalSize);
832 os << bufferDecl(0,
"in_");
833 os << bufferDecl(1,
"o");
834 os << pushConstant();
835 os <<
"void main() {\n";
836 os <<
" uint i_ = gl_GlobalInvocationID.x;\n";
837 os <<
" if (i_ >= " << pn.size <<
"u) return;\n";
838 os <<
" uint idx = 0u;\n";
839 for (
int k = 0; k < rank; ++k) {
840 const int inAxis = pn.perm[k];
841 os <<
" idx += ((i_ / " << S[k] <<
"u) % " << pn.dims[k] <<
"u) * "
842 << inStride[inAxis] <<
"u;\n";
844 os <<
" o[i_] = in_[idx];\n";
847 out.pass2 =
os.str();
848 out.groupsX2 = groupsFor(pn.size);
853bool genResize2d(
const Graph &g,
const FusedGroup &grp, KernelSpec &out) {
854 const GraphNode &rn = g.node(grp.outputNode);
855 const GraphNode &
X = g.node(rn.in0);
856 const int H =
X.dims[2], W =
X.dims[3];
857 const int OH = rn.dims[2], OW = rn.dims[3];
858 const bool nearest = rn.i0 == 0;
859 std::ostringstream
os;
860 os << header(kLocalSize);
861 os << bufferDecl(0,
"in_");
862 os << bufferDecl(1,
"o");
863 os << pushConstant();
864 os <<
"void main() {\n";
865 os <<
" uint i_ = gl_GlobalInvocationID.x;\n";
866 os <<
" if (i_ >= " << rn.size <<
"u) return;\n";
867 os <<
" uint ow = i_ % " << OW <<
"u;\n";
868 os <<
" uint rem = i_ / " << OW <<
"u;\n";
869 os <<
" uint oh = rem % " << OH <<
"u;\n";
870 os <<
" uint rem2 = rem / " << OH <<
"u;\n";
871 os <<
" uint c = rem2 % " <<
X.dims[1] <<
"u;\n";
872 os <<
" uint n_ = rem2 / " <<
X.dims[1] <<
"u;\n";
873 os <<
" uint base = (n_ * " <<
X.dims[1] <<
"u + c) * " << H <<
"u * " << W <<
"u;\n";
875 os <<
" uint ih = uint(float(oh) * " << scalarStr(
float(H) / OH) <<
") ;\n";
876 os <<
" uint iw = uint(float(ow) * " << scalarStr(
float(W) / OW) <<
") ;\n";
877 os <<
" ih = min(ih, " << H - 1 <<
"u); iw = min(iw, " << W - 1 <<
"u);\n";
878 os <<
" o[i_] = in_[base + ih * " << W <<
"u + iw];\n";
880 os <<
" float fx = float(ow) * " << scalarStr(
float(W) / OW) <<
" - 0.5;\n";
881 os <<
" float fy = float(oh) * " << scalarStr(
float(H) / OH) <<
" - 0.5;\n";
882 os <<
" fx = clamp(fx, 0.0, " << scalarStr(
float(W - 1)) <<
");\n";
883 os <<
" fy = clamp(fy, 0.0, " << scalarStr(
float(H - 1)) <<
");\n";
884 os <<
" uint x0 = uint(floor(fx)); uint y0 = uint(floor(fy));\n";
885 os <<
" uint x1 = min(x0 + 1u, " << W - 1 <<
"u); uint y1 = min(y0 + 1u, " << H - 1
887 os <<
" float w00 = in_[base + y0 * " << W <<
"u + x0];\n";
888 os <<
" float w10 = in_[base + y0 * " << W <<
"u + x1];\n";
889 os <<
" float w01 = in_[base + y1 * " << W <<
"u + x0];\n";
890 os <<
" float w11 = in_[base + y1 * " << W <<
"u + x1];\n";
891 os <<
" float top = w00 + (w10 - w00) * (fx - float(x0));\n";
892 os <<
" float bot = w01 + (w11 - w01) * (fx - float(x0));\n";
893 os <<
" o[i_] = top + (bot - top) * (fy - float(y0));\n";
897 out.pass2 =
os.str();
898 out.groupsX2 = groupsFor(rn.size);
903bool genSdpa(
const Graph &g,
const FusedGroup &grp, KernelSpec &out) {
904 const GraphNode &qn = g.node(grp.outputNode);
905 const GraphNode &Q = g.node(qn.in0);
906 const GraphNode &K = g.node(qn.in1);
907 const int B = Q.dims[0], H = Q.dims[1], T = Q.dims[2], D = Q.dims[3];
908 const int S = K.dims[2];
909 if (S > 2048 || D > 512)
return false;
910 const float scale = qn.s0;
911 const bool masked = grp.masked;
912 const int bindingMask = masked ? 3 : -1;
913 const int bindingOut = masked ? 4 : 3;
914 std::ostringstream
os;
916 os << bufferDecl(0,
"q");
917 os << bufferDecl(1,
"k");
918 os << bufferDecl(2,
"v");
919 if (masked)
os << bufferDecl(3,
"mask");
920 os << bufferDecl(bindingOut,
"o");
921 os <<
"shared float scores[" << S <<
"];\n";
922 os <<
"shared float maxv;\n";
923 os <<
"shared float sumv;\n";
924 os << pushConstant();
925 os <<
"void main() {\n";
926 os <<
" uint tid = gl_LocalInvocationID.x;\n";
927 os <<
" uint bh = gl_WorkGroupID.x;\n";
928 os <<
" uint t = gl_WorkGroupID.y;\n";
929 os <<
" uint b = bh / " << H <<
"u;\n";
930 os <<
" uint h = bh % " << H <<
"u;\n";
931 os <<
" uint qbase = (b * " << H <<
"u + h) * " << T <<
"u * " << D <<
"u + t * " << D
933 os <<
" uint kbase = (b * " << H <<
"u + h) * " << S <<
"u * " << D <<
"u;\n";
934 os <<
" uint vbase = kbase;\n";
935 os <<
" for (uint s = tid; s < " << S <<
"u; s += 128u) {\n";
936 os <<
" float acc = 0.0;\n";
937 os <<
" for (uint d = 0u; d < " << D <<
"u; ++d) acc += q[qbase + d] * k[kbase + s * "
939 os <<
" acc *= " << scalarStr(
scale) <<
";\n";
941 os <<
" acc += mask[(b * " << H <<
"u + h) * " << T <<
"u * " << S <<
"u + t * " << S
944 os <<
" scores[s] = acc;\n";
946 os <<
" barrier();\n";
947 os <<
" if (tid == 0u) {\n";
948 os <<
" float m = -3.402823e38;\n";
949 os <<
" for (uint s = 0u; s < " << S <<
"u; ++s) m = max(m, scores[s]);\n";
950 os <<
" float sm = 0.0;\n";
951 os <<
" for (uint s = 0u; s < " << S <<
"u; ++s) sm += exp(scores[s] - m);\n";
952 os <<
" maxv = m; sumv = sm;\n";
954 os <<
" barrier();\n";
955 os <<
" for (uint d = tid; d < " << D <<
"u; d += 128u) {\n";
956 os <<
" float acc = 0.0;\n";
957 os <<
" for (uint s = 0u; s < " << S <<
"u; ++s) acc += exp(scores[s] - maxv) * v[vbase + s * "
959 os <<
" o[qbase + d] = acc / sumv;\n";
963 out.pass2 =
os.str();
964 out.groupsX2 = B * H;
966 out.inputCount = masked ? 4 : 3;
gpgpu::ComputeShader * reduce
const std::vector< std::string > & indexExprs
std::vector< std::string > vars
std::string biasIndexExpr
static constexpr int kMaxRank
bool isQuantDType(DType dt)
bool generateKernel(const Graph &graph, const FusedGroup &group, KernelSpec &out)
DType
Tensor element types.
constexpr int kMaxKernelBindings
bool generateMatMulVariant(const Graph &graph, const FusedGroup &group, bool tiled, KernelSpec &out)