载入中...
搜索中...
未找到
KernelGen.cpp
浏览该文件的文档.
1#include "tensor/KernelGen.h"
2#include "tensor/CpuKernels.h"
3#include "tensor/Quant.h"
4
5#include <algorithm>
6#include <cmath>
7#include <sstream>
8
9namespace eve::tensor {
10namespace {
11
12constexpr int kLocalSize = 256;
13
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;
19 os << ") in;\n";
20 return os.str();
21}
22
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";
27 return os.str();
28}
29
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";
34 return os.str();
35}
36
42std::string emitQuantizedBVal(DType dt, int group, const char *bufName = "b") {
43 std::ostringstream os;
44 const int g = group > 0 ? group : 1;
45 switch (dt) {
46 case DType::Int8:
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"
53 << "}\n";
54 break;
55 case DType::Int4:
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"
63 << "}\n";
64 break;
65 case DType::Fp16:
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"
70 << "}\n";
71 break;
72 case DType::Fp8E4M3:
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"
82 << "}\n";
83 break;
84 case DType::Fp4E2M1:
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"
95 << "}\n";
96 break;
97 default: break;
98 }
99 return os.str();
100}
101
102std::string pushConstant() {
103 return "layout(push_constant) uniform PC { float data[32]; } pc;\n";
104}
105
106int groupsFor(int count) { return (count + kLocalSize - 1) / kLocalSize; }
107
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;
111 os << v << "f";
112 return os.str();
113}
114
120struct ChainContext {
121 const Graph &graph;
122 const FusedGroup &group;
123 const std::vector<std::string> &indexExprs; // per group input
124 std::string rootVar;
125 std::ostringstream &os;
126 int temp = 0;
127 std::string biasIndexExpr; // e.g. "jj" (matmul) / "f" (conv), empty = none
128
129 std::string operand(int nodeId) {
130 if (nodeId == group.biasNode && !biasIndexExpr.empty())
131 return "bias[" + biasIndexExpr + "]";
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());
135 return "a" + std::to_string(idx) + "[" + indexExprs[idx] + "]";
136 }
137 return vars[static_cast<size_t>(nodeId)];
138 }
139
140 std::vector<std::string> vars;
141
142 std::string emit(int nodeId) {
143 const GraphNode &nd = graph.node(nodeId);
144 const std::string x = (nd.in0 == group.nodes.front() && rootVar != "") ? rootVar
145 : (nd.in0 >= 0 ? operand(nd.in0) : rootVar);
146 std::string expr;
147 switch (nd.type) {
148 case OpType::Add: expr = "(" + x + " + " + operand(nd.in1) + ")"; break;
149 case OpType::Sub: expr = "(" + x + " - " + operand(nd.in1) + ")"; break;
150 case OpType::Multiply: expr = "(" + x + " * " + operand(nd.in1) + ")"; break;
151 case OpType::Divide: expr = "(" + x + " / " + operand(nd.in1) + ")"; break;
152 case OpType::AddScalar: expr = "(" + x + " + " + scalarStr(nd.s0) + ")"; break;
153 case OpType::SubScalar: expr = "(" + x + " - " + scalarStr(nd.s0) + ")"; break;
154 case OpType::MulScalar: expr = "(" + x + " * " + scalarStr(nd.s0) + ")"; break;
155 case OpType::DivScalar: expr = "(" + x + " / " + scalarStr(nd.s0) + ")"; break;
156 case OpType::PowScalar: expr = "pow(" + x + ", " + scalarStr(nd.s0) + ")"; break;
157 case OpType::Neg: expr = "(-" + x + ")"; break;
158 case OpType::Abs: expr = "abs(" + x + ")"; break;
159 case OpType::Sqrt: expr = "sqrt(" + x + ")"; break;
160 case OpType::Exp: expr = "exp(" + x + ")"; break;
161 case OpType::Log: expr = "log(" + x + ")"; break;
162 case OpType::Sin: expr = "sin(" + x + ")"; break;
163 case OpType::Cos: expr = "cos(" + x + ")"; break;
164 case OpType::Tanh: expr = "tanh(" + x + ")"; break;
165 case OpType::Relu: expr = "max(" + x + ", 0.0)"; break;
166 case OpType::Sigmoid: expr = "(1.0 / (1.0 + exp(-" + x + ")))"; break;
167 case OpType::Gelu:
168 expr = "(0.5 * " + x +
169 " * (1.0 + tanh(0.7978845608028654 * (" + x +
170 " + 0.044715 * " + x + " * " + x + " * " + x + "))))";
171 break;
172 case OpType::Silu: expr = "(" + x + " / (1.0 + exp(-" + x + ")))"; break;
173 case OpType::Clamp: {
174 float lo = nd.s0, hi = nd.s1;
175 if (lo > hi) std::swap(lo, hi);
176 expr = "clamp(" + x + ", " + scalarStr(lo) + ", " + scalarStr(hi) + ")";
177 break;
178 }
179 case OpType::MaximumScalar: expr = "max(" + x + ", " + scalarStr(nd.s0) + ")"; break;
180 case OpType::MinimumScalar: expr = "min(" + x + ", " + scalarStr(nd.s0) + ")"; break;
181 case OpType::Where:
182 expr = "(" + operand(nd.in0) + " > 0.5 ? " + operand(nd.in1) + " : " +
183 operand(nd.in2) + ")";
184 break;
185 default: return "";
186 }
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;
191 return var;
192 }
193};
194
200void emitInputIndexExprs(std::ostringstream &os, const Graph &g, const FusedGroup &grp,
201 std::vector<std::string> &indexExprs) {
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);
205 if (rank > 0) {
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];
209 }
210 indexExprs.resize(grp.inputs.size());
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;
214 if (identical) {
215 for (int d = 0; d < rank; ++d)
216 if (inN.dims[d] != on.dims[d]) {
217 identical = false;
218 break;
219 }
220 }
221 if (identical) {
222 indexExprs[k] = "i_";
223 continue;
224 }
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;
230 int s = 1;
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;
234 }
235 stride[static_cast<size_t>(d)] = s;
236 }
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";
243 }
244 indexExprs[k] = var;
245 }
246}
247
248bool genElementwise(const Graph &g, const FusedGroup &grp, KernelSpec &out) {
249 if (grp.inputs.size() > size_t(kMaxKernelBindings - 1)) return false;
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";
260 std::vector<std::string> indexExprs;
261 emitInputIndexExprs(os, g, grp, indexExprs);
262 ChainContext ctx{g, grp, indexExprs, "", os, 0, ""};
263 for (int u : grp.nodes) ctx.emit(u);
264 os << " o[i_] = " << ctx.vars[static_cast<size_t>(grp.outputNode)] << ";\n";
265 os << "}\n";
266 out.pass1.clear();
267 out.pass2 = os.str();
268 out.groupsX2 = groupsFor(on.size);
269 out.inputCount = int(grp.inputs.size());
270 return true;
271}
272
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;
284 const bool bQuant = q::isQuantDType(static_cast<DType>(B.dtype)) && !B.constBytes.empty();
285 if (bQuant && tiled) return false; // quantized weights use the naive variant only
286 const int scalesBinding = hasBias ? 3 : 2;
287 const int bindingOut = bQuant ? (hasBias ? 4 : 3) : (hasBias ? 3 : 2);
288
289 std::ostringstream os;
290 if (!tiled) {
291 os << header(kLocalSize);
292 } else {
293 os << header(16, 16);
294 }
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);
304
305 std::vector<std::string> indexExprs(grp.inputs.size(), "i_");
306 ChainContext ctx{g, grp, indexExprs, "r", os, 0, ""};
307 ctx.biasIndexExpr = "jj";
308
309 if (!tiled) {
310 os << "void main() {\n";
311 os << " uint i_ = gl_GlobalInvocationID.x;\n";
312 os << " if (i_ >= " << batch * m * n << "u) return;\n";
313 if (batched) {
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";
318 } else {
319 os << " uint ii = i_ / " << n << "u;\n";
320 os << " uint jj = i_ % " << n << "u;\n";
321 }
322 os << " float r = 0.0;\n";
323 os << " for (uint t = 0u; t < " << k << "u; ++t) {\n";
324 if (batched) {
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]")
330 << ";\n";
331 } else {
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]")
335 << ";\n";
336 }
337 os << " }\n";
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";
342 os << "}\n";
343 out.groupsX2 = groupsFor(batch * m * n);
344 } else {
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";
366 os << " }\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";
373 os << "}\n";
374 out.groupsX2 = gx;
375 out.groupsY2 = gy;
376 }
377 out.pass1.clear();
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; // batched matmul uses the naive variant
385 return true;
386}
387
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);
392 const bool is1d = cn.type == OpType::Conv1d;
393 const int stride = cn.i0, pad = cn.i1;
394 const bool hasBias = grp.biasNode >= 0;
395 const int bindingOut = hasBias ? 3 : 2;
396
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_");
405 ChainContext ctx{g, grp, indexExprs, "r", os, 0, ""};
406 ctx.biasIndexExpr = "f";
407
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";
411 if (is1d) {
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";
425 os << " }\n";
426 os << " }\n";
427 } else {
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
446 << "u + kw];\n";
447 os << " }\n";
448 os << " }\n";
449 os << " }\n";
450 }
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";
455 os << "}\n";
456 out.pass1.clear();
457 out.pass2 = os.str();
458 out.groupsX2 = groupsFor(g.node(grp.outputNode).size);
459 out.inputCount = hasBias ? 3 : 2;
460 return true;
461}
462
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];
469 const bool maxPool = pn.type == OpType::MaxPool2d;
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";
494 if (maxPool) {
495 os << " acc = max(acc, v);\n";
496 } else {
497 os << " acc += v; ++valid;\n";
498 }
499 os << " }\n";
500 os << " }\n";
501 if (!maxPool) os << " acc = valid > 0 ? acc / float(valid) : 0.0;\n";
502 os << " o[i_] = acc;\n";
503 os << "}\n";
504 out.pass1.clear();
505 out.pass2 = os.str();
506 out.groupsX2 = groupsFor(pn.size);
507 out.inputCount = 1;
508 return true;
509}
510
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];
517 reduce = X.dims[axis];
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;
521
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";
536 os1 << " }\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";
540 os1 << " }\n";
541 os1 << " mx[i_] = m;\n";
542 os1 << " sm[i_] = s;\n";
543 os1 << "}\n";
544
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_ % "
555 << inner << "u);\n";
556 os2 << " float m = mx[o_];\n";
557 os2 << " float s = sm[o_];\n";
558 os2 << " float x = in_[i_];\n";
559 if (logMode) {
560 os2 << " o[i_] = (x - m) - log(s);\n";
561 } else {
562 os2 << " o[i_] = exp(x - m) / s;\n";
563 }
564 os2 << "}\n";
565 out.pass1 = os1.str();
566 out.pass2 = os2.str();
567 out.groupsX1 = groupsFor(rows);
568 out.groupsX2 = groupsFor(sn.size);
569 out.inputCount = 1;
570 out.inputsReadPass1 = 1;
571 out.statsCount = 2;
572 out.statsSize = rows;
573 out.twoPass = true;
574 return true;
575}
576
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;
588
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";
602 if (rms) {
603 os1 << " s0 += v * v;\n";
604 } else {
605 os1 << " s0 += v; s1 += v * v;\n";
606 }
607 os1 << " }\n";
608 os1 << " st0[i_] = s0;\n";
609 if (statsCount > 1) os1 << " st1[i_] = s1;\n";
610 os1 << "}\n";
611
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";
624 if (rms) {
625 os2 << " float inv = 1.0 / sqrt(st0[r] / " << cols << ".0 + " << scalarStr(eps)
626 << ");\n";
627 os2 << " float y = a0[i_] * inv;\n";
628 if (hasScale) os2 << " y *= a1[c];\n";
629 } else {
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";
637 }
638 os2 << " o[i_] = y;\n";
639 os2 << "}\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;
648 out.twoPass = true;
649 return true;
650}
651
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];
658 reduce = X.dims[axis];
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";
671 if (argmax) {
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";
677 os << " }\n";
678 os << " o[i_] = bestJ;\n";
679 } else {
680 switch (grp.op) {
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";
686 if (grp.op == OpType::ReduceMean)
687 os << " acc /= " << scalarStr(float(reduce)) << ";\n";
688 break;
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";
693 break;
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";
698 break;
699 default: return false;
700 }
701 os << " o[i_] = acc;\n";
702 }
703 os << "}\n";
704 out.pass1.clear();
705 out.pass2 = os.str();
706 out.groupsX2 = groupsFor(outSize);
707 out.inputCount = 1;
708 return true;
709}
710
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);
715 const bool tQuant = q::isQuantDType(static_cast<DType>(T.dtype)) && !T.constBytes.empty();
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]")
736 << ";\n";
737 os << "}\n";
738 out.pass1.clear();
739 out.pass2 = os.str();
740 out.groupsX2 = groupsFor(en.size);
741 out.inputCount = 2;
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;
746 return true;
747}
748
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;
754 int starts[4] = {};
755 int axisTotal = 0;
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];
759 }
760 int inner = 1;
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
778 << "u) {\n";
779 os << " v = a" << k << "[op * " << sz << "u * " << inner << "u + (ax - " << starts[k]
780 << "u) * " << inner << "u + ip];\n";
781 os << " }\n";
782 }
783 os << " o[i_] = v;\n";
784 os << "}\n";
785 out.pass1.clear();
786 out.pass2 = os.str();
787 out.groupsX2 = groupsFor(cn.size);
788 out.inputCount = n;
789 return true;
790}
791
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;
797 int inner = 1;
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";
812 os << "}\n";
813 out.pass1.clear();
814 out.pass2 = os.str();
815 out.groupsX2 = groupsFor(sn.size);
816 out.inputCount = 1;
817 return true;
818}
819
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;
824 int S[Tensor::kMaxRank] = {};
825 S[rank - 1] = 1;
826 for (int k = rank - 2; k >= 0; --k) S[k] = S[k + 1] * pn.dims[k + 1];
827 int inStride[Tensor::kMaxRank] = {};
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";
843 }
844 os << " o[i_] = in_[idx];\n";
845 os << "}\n";
846 out.pass1.clear();
847 out.pass2 = os.str();
848 out.groupsX2 = groupsFor(pn.size);
849 out.inputCount = 1;
850 return true;
851}
852
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";
874 if (nearest) {
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";
879 } else {
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
886 << "u);\n";
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";
894 }
895 os << "}\n";
896 out.pass1.clear();
897 out.pass2 = os.str();
898 out.groupsX2 = groupsFor(rn.size);
899 out.inputCount = 1;
900 return true;
901}
902
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; // shared-memory limits -> CPU fallback
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;
915 os << header(128);
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
932 << "u;\n";
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 * "
938 << D << "u + d];\n";
939 os << " acc *= " << scalarStr(scale) << ";\n";
940 if (masked) {
941 os << " acc += mask[(b * " << H << "u + h) * " << T << "u * " << S << "u + t * " << S
942 << "u + s];\n";
943 }
944 os << " scores[s] = acc;\n";
945 os << " }\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";
953 os << " }\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 * "
958 << D << "u + d];\n";
959 os << " o[qbase + d] = acc / sumv;\n";
960 os << " }\n";
961 os << "}\n";
962 out.pass1.clear();
963 out.pass2 = os.str();
964 out.groupsX2 = B * H;
965 out.groupsY2 = T;
966 out.inputCount = masked ? 4 : 3;
967 return true;
968}
969
970} // namespace
971
973 out = KernelSpec{};
974 switch (group.kind) {
975 case GroupKind::Elementwise: return genElementwise(graph, group, out);
977 // naive variant first; the runtime autotunes between naive and tiled
978 return genMatMul(graph, group, false, out);
980 case GroupKind::Conv2d: return genConv(graph, group, out);
982 case GroupKind::AvgPool2d: return genPool(graph, group, out);
983 case GroupKind::Softmax: return genSoftmax(graph, group, out);
984 case GroupKind::LayerNorm: return genNorm(graph, group, false, out);
985 case GroupKind::RMSNorm: return genNorm(graph, group, true, out);
988 return genReduceOrArgmax(graph, group, group.kind == GroupKind::ArgMax, out);
989 case GroupKind::Embedding: return genEmbedding(graph, group, out);
990 case GroupKind::Concat: return genConcat(graph, group, out);
991 case GroupKind::Slice: return genSlice(graph, group, out);
992 case GroupKind::Permute: return genPermute(graph, group, out);
993 case GroupKind::Resize2d: return genResize2d(graph, group, out);
994 case GroupKind::Sdpa: return genSdpa(graph, group, out);
995 case GroupKind::Alias: return true; // no kernel; pure buffer alias
996 }
997 return false;
998}
999
1000bool generateMatMulVariant(const Graph &graph, const FusedGroup &group, bool tiled,
1001 KernelSpec &out) {
1002 out = KernelSpec{};
1003 if (group.kind != GroupKind::MatMul) return false;
1004 return genMatMul(graph, group, tiled, out);
1005}
1006
1007} // namespace eve::tensor
gpgpu::ComputeShader * reduce
float u
Definition Grass.cpp:234
int x
Definition Grass.cpp:135
glm::vec3 n
Definition Grass.cpp:64
std::string rootVar
const std::vector< std::string > & indexExprs
std::vector< std::string > vars
std::ostringstream & os
int temp
std::string biasIndexExpr
const FusedGroup & group
const Graph & graph
int idx
const char * name
Definition RockMesh.cpp:21
int d
int v
float scale
Definition TreeMesh.cpp:122
float m[16]
uint32_t s
Definition Weather.cpp:28
static constexpr int kMaxRank
Definition Tensor.h:45
bool isQuantDType(DType dt)
Definition Quant.h:16
bool generateKernel(const Graph &graph, const FusedGroup &group, KernelSpec &out)
DType
Tensor element types.
Definition Tensor.h:22
constexpr int kMaxKernelBindings
Definition KernelGen.h:59
bool generateMatMulVariant(const Graph &graph, const FusedGroup &group, bool tiled, KernelSpec &out)