char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

Ruben Ortlamvulkan: use spec constant for matrix matrix multiplication A-type (#25773)91f6a6cf3

char/aliceai-t5-moe
79.6 KiB1425 linesraw
1#include <iostream>
2#include <fstream>
3#include <sstream>
4#include <string>
5#include <stdexcept>
6#include <array>
7#include <vector>
8#include <map>
9#include <thread>
10#include <mutex>
11#include <future>
12#include <queue>
13#include <condition_variable>
14#include <atomic>
15#include <cstdio>
16#include <cstring>
17#include <cstdlib>
18#include <cassert>
19#include <algorithm>
20#include <sys/stat.h>
21#include <sys/types.h>
22#include <filesystem>
23
24#ifdef _WIN32
25    #define NOMINMAX
26    #include <windows.h>
27    #include <direct.h> // For _mkdir on Windows
28#else
29    #include <unistd.h>
30    #include <sys/wait.h>
31    #include <fcntl.h>
32#endif
33
34#define ASYNCIO_CONCURRENCY 64
35
36std::mutex lock;
37std::vector<std::pair<std::string, std::string>> shader_fnames;
38// Set when any shader subprocess fails (non-zero exit / stderr / launch failure) so the
39// build is stopped instead of silently producing a broken libggml-vulkan. (issue #24393)
40static std::atomic<bool> compile_failed{false};
41std::locale c_locale("C");
42
43std::string GLSLC = "glslc";
44std::string input_filepath = "";
45std::string output_dir = "/tmp";
46std::string target_hpp = "";
47std::string target_cpp = "";
48
49const std::vector<std::string> type_names = {
50    "f32",
51    "f16",
52    "q1_0",
53    "q2_0",
54    "q4_0",
55    "q4_1",
56    "q5_0",
57    "q5_1",
58    "q8_0",
59    "q2_k",
60    "q3_k",
61    "q4_k",
62    "q5_k",
63    "q6_k",
64    "iq1_s",
65    "iq1_m",
66    "iq2_xxs",
67    "iq2_xs",
68    "iq2_s",
69    "iq3_xxs",
70    "iq3_s",
71    "iq4_xs",
72    "iq4_nl",
73    "mxfp4",
74    "nvfp4",
75    "tq1_0",
76    "tq2_0",
77    "bf16",
78};
79
80enum MatMulIdType {
81    NONE,
82    DEFAULT,
83    SUBGROUP,
84};
85
86namespace {
87
88int execute_command(std::vector<std::string>& command, std::string& stdout_str, std::string& stderr_str) {
89#ifdef _WIN32
90    HANDLE stdout_read, stdout_write;
91    HANDLE stderr_read, stderr_write;
92    SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
93
94    if (!CreatePipe(&stdout_read, &stdout_write, &sa, 0) ||
95        !SetHandleInformation(stdout_read, HANDLE_FLAG_INHERIT, 0)) {
96        throw std::runtime_error("Failed to create stdout pipe");
97    }
98
99    if (!CreatePipe(&stderr_read, &stderr_write, &sa, 0) ||
100        !SetHandleInformation(stderr_read, HANDLE_FLAG_INHERIT, 0)) {
101        throw std::runtime_error("Failed to create stderr pipe");
102    }
103
104    PROCESS_INFORMATION pi;
105    STARTUPINFOA si = {};
106    si.cb = sizeof(STARTUPINFOA);
107    si.dwFlags = STARTF_USESTDHANDLES;
108    si.hStdOutput = stdout_write;
109    si.hStdError = stderr_write;
110
111    std::string cmd;
112    for (const auto& part : command) {
113        cmd += part + " ";
114    }
115
116    if (!CreateProcessA(NULL, cmd.data(), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
117        throw std::runtime_error("Failed to create process");
118    }
119
120    CloseHandle(stdout_write);
121    CloseHandle(stderr_write);
122
123    std::array<char, 128> buffer;
124    DWORD bytes_read;
125
126    while (ReadFile(stdout_read, buffer.data(), (DWORD)buffer.size(), &bytes_read, NULL) && bytes_read > 0) {
127        stdout_str.append(buffer.data(), bytes_read);
128    }
129
130    while (ReadFile(stderr_read, buffer.data(), (DWORD)buffer.size(), &bytes_read, NULL) && bytes_read > 0) {
131        stderr_str.append(buffer.data(), bytes_read);
132    }
133
134    CloseHandle(stdout_read);
135    CloseHandle(stderr_read);
136    WaitForSingleObject(pi.hProcess, INFINITE);
137    DWORD exit_code = 1;
138    GetExitCodeProcess(pi.hProcess, &exit_code);
139    CloseHandle(pi.hProcess);
140    CloseHandle(pi.hThread);
141    return (int)exit_code;
142#else
143    int stdout_pipe[2];
144    int stderr_pipe[2];
145
146    if (pipe(stdout_pipe) != 0 || pipe(stderr_pipe) != 0) {
147        throw std::runtime_error("Failed to create pipes");
148    }
149
150    pid_t pid = fork();
151    if (pid < 0) {
152        std::cerr << strerror(errno) << "\n";
153        throw std::runtime_error("Failed to fork process");
154    }
155
156    std::vector<char*> argv;
157    for (std::string& part : command) {
158        argv.push_back(part.data());
159    }
160    argv.push_back(nullptr);
161
162    if (pid == 0) {
163        close(stdout_pipe[0]);
164        close(stderr_pipe[0]);
165        dup2(stdout_pipe[1], STDOUT_FILENO);
166        dup2(stderr_pipe[1], STDERR_FILENO);
167        close(stdout_pipe[1]);
168        close(stderr_pipe[1]);
169        execvp(argv[0], argv.data());
170        _exit(EXIT_FAILURE);
171    } else {
172        close(stdout_pipe[1]);
173        close(stderr_pipe[1]);
174
175        std::array<char, 128> buffer;
176        ssize_t bytes_read;
177
178        while ((bytes_read = read(stdout_pipe[0], buffer.data(), buffer.size())) > 0) {
179            stdout_str.append(buffer.data(), bytes_read);
180        }
181
182        while ((bytes_read = read(stderr_pipe[0], buffer.data(), buffer.size())) > 0) {
183            stderr_str.append(buffer.data(), bytes_read);
184        }
185
186        close(stdout_pipe[0]);
187        close(stderr_pipe[0]);
188        int status = 0;
189        waitpid(pid, &status, 0);
190        return WIFEXITED(status) ? WEXITSTATUS(status) : -1;
191    }
192#endif
193}
194
195bool directory_exists(const std::string& path) {
196    struct stat info;
197    if (stat(path.c_str(), &info) != 0) {
198        return false; // Path doesn't exist or can't be accessed
199    }
200    return (info.st_mode & S_IFDIR) != 0; // Check if it is a directory
201}
202
203bool create_directory(const std::string& path) {
204#ifdef _WIN32
205    return _mkdir(path.c_str()) == 0 || errno == EEXIST; // EEXIST means the directory already exists
206#else
207    return mkdir(path.c_str(), 0755) == 0 || errno == EEXIST; // 0755 is the directory permissions
208#endif
209}
210
211std::string to_uppercase(const std::string& input) {
212    std::string result = input;
213    for (char& c : result) {
214        c = std::toupper(c);
215    }
216    return result;
217}
218
219bool string_starts_with(const std::string& str, const std::string& prefix) {
220    if (prefix.size() > str.size()) {
221        return false;
222    }
223    return std::equal(prefix.begin(), prefix.end(), str.begin());
224}
225
226bool string_ends_with(const std::string& str, const std::string& suffix) {
227    if (suffix.size() > str.size()) {
228        return false;
229    }
230    return std::equal(suffix.rbegin(), suffix.rend(), str.rbegin());
231}
232
233bool is_quantized_type(const std::string& type_name) {
234    return type_name != "f32" && type_name != "f16" && type_name != "bf16";
235}
236
237bool is_legacy_quant(const std::string& type_name) {
238    return type_name == "q2_0" || type_name == "q4_0" || type_name == "q4_1" || type_name == "q5_0" || type_name == "q5_1" || type_name == "q8_0";
239}
240
241bool is_k_quant(const std::string& type_name) {
242    return string_ends_with(type_name, "_k");
243}
244
245bool is_iq_quant(const std::string& type_name) {
246    return string_starts_with(type_name, "iq");
247}
248
249bool is_lut_quant(const std::string& type_name) {
250    return is_iq_quant(type_name) || type_name == "mxfp4" || type_name == "nvfp4";
251}
252
253std::string lut_load_vec_a(const std::string& type_name) {
254    if (type_name == "iq1_s" || type_name == "iq1_m" || type_name == "iq2_xxs" || type_name == "iq2_xs" || type_name == "iq2_s") {
255        return "8";
256    }
257    return "4";
258}
259
260static const char path_separator = '/';
261
262std::string join_paths(const std::string& path1, const std::string& path2) {
263    return path1 + path_separator + path2;
264}
265
266std::string basename(const std::string &path) {
267    return path.substr(path.find_last_of("/\\") + 1);
268}
269
270std::stringstream make_generic_stringstream() {
271    std::stringstream ss;
272    ss.imbue(c_locale);
273    return ss;
274}
275
276std::string read_binary_file(const std::string& path, bool may_not_exist = false) {
277    FILE* f = fopen(path.c_str(), "rb");
278    if (!f) {
279        if (!may_not_exist) {
280            std::cerr << "Error opening file: " << path << " (" << strerror(errno) << ")\n";
281        }
282        return {};
283    }
284
285    fseek(f, 0, SEEK_END);
286    size_t size = ftell(f);
287    fseek(f, 0, SEEK_SET);
288
289    std::string data(size, '\0');
290    size_t read_size = fread(data.data(), 1, size, f);
291    fclose(f);
292    if (read_size != size) {
293        std::cerr << "Error reading file: " << path << " (" << strerror(errno) << ")\n";
294        return {};
295    }
296
297    return data;
298}
299
300void write_binary_file(const std::string& path, const std::string& content) {
301    FILE* f = fopen(path.c_str(), "wb");
302    if (!f) {
303        std::cerr << "Error opening file for writing: " << path << " (" << strerror(errno) << ")\n";
304        return;
305    }
306
307    size_t write_size = fwrite(content.data(), 1, content.size(), f);
308    fclose(f);
309    if (write_size != content.size()) {
310        std::cerr << "Error writing file: " << path << " (" << strerror(errno) << ")\n";
311        return;
312    }
313}
314
315void write_file_if_changed(const std::string& path, const std::string& content) {
316    std::string existing = read_binary_file(path, true);
317    if (existing != content) {
318        write_binary_file(path, content);
319    }
320}
321
322
323// variables to track number of compiles in progress
324static uint32_t compile_count = 0;
325static std::mutex compile_count_mutex;
326static std::condition_variable compile_count_cond;
327static bool generate_dep_file = true;
328
329void decrement_compile_count(uint32_t * count) {
330    if (count) {
331        std::lock_guard<std::mutex> guard(compile_count_mutex);
332        assert(compile_count > 0);
333        compile_count--;
334        compile_count_cond.notify_all();
335    }
336}
337
338using compile_count_guard = std::unique_ptr<uint32_t, decltype(&decrement_compile_count)>;
339
340compile_count_guard acquire_compile_slot() {
341    // wait until fewer than N compiles are in progress.
342    // 16 is an arbitrary limit, the goal is to avoid "failed to create pipe" errors.
343    uint32_t N = std::max(1u, std::min(16u, std::thread::hardware_concurrency()));
344    std::unique_lock<std::mutex> guard(compile_count_mutex);
345    compile_count_cond.wait(guard, [N] { return compile_count < N; });
346    compile_count++;
347    return compile_count_guard(&compile_count, &decrement_compile_count);
348}
349
350void string_to_spv_func(std::string name, std::string in_path, std::string out_path, std::map<std::string, std::string> defines, bool coopmat, bool dep_file, compile_count_guard slot) {
351    std::string target_env = (name.find("_cm2") != std::string::npos) ? "--target-env=vulkan1.3" : "--target-env=vulkan1.2";
352
353    #ifdef _WIN32
354        std::vector<std::string> cmd = {GLSLC, "-fshader-stage=compute", target_env, "\"" + in_path + "\"", "-o", "\"" + out_path + "\""};
355    #else
356        std::vector<std::string> cmd = {GLSLC, "-fshader-stage=compute", target_env, in_path, "-o", out_path};
357    #endif
358
359    // disable spirv-opt for coopmat shaders for https://github.com/ggml-org/llama.cpp/issues/10734
360    // disable spirv-opt for bf16 shaders for https://github.com/ggml-org/llama.cpp/issues/15344
361    // disable spirv-opt for rope shaders for https://github.com/ggml-org/llama.cpp/issues/16860
362    // disable spirv-opt for dot2 shaders (spirv-opt doesn't recognize SPV_VALVE_mixed_float_dot_product capability)
363    if (!coopmat && name.find("bf16") == std::string::npos && name.find("rope") == std::string::npos && name.find("_dot2") == std::string::npos) {
364        cmd.push_back("-O");
365    }
366
367    if (dep_file) {
368        cmd.push_back("-MD");
369        cmd.push_back("-MF");
370#ifdef _WIN32
371        cmd.push_back("\"" + target_cpp + ".d\"");
372#else
373        cmd.push_back(target_cpp + ".d");
374#endif
375    }
376
377    #ifdef GGML_VULKAN_SHADER_DEBUG_INFO
378        cmd.push_back("-g");
379    #endif
380
381    for (const auto& define : defines) {
382        cmd.push_back("-D" + define.first + "=" + define.second);
383    }
384
385    std::string command;
386    for (const auto& part : cmd) {
387        command += part + " ";
388    }
389
390    std::string stdout_str, stderr_str;
391    try {
392        // std::cout << "Executing command: ";
393        // for (const auto& part : cmd) {
394        //     std::cout << part << " ";
395        // }
396        // std::cout << std::endl;
397
398        int exit_code = execute_command(cmd, stdout_str, stderr_str);
399        if (exit_code != 0 || !stderr_str.empty()) {
400            std::cerr << "cannot compile " << name << " (exit code " << exit_code << ")\n\n";
401            for (const auto& part : cmd) {
402                std::cerr << part << " ";
403            }
404            std::cerr << "\n\n" << stderr_str << std::endl;
405            compile_failed = true;
406            return;
407        }
408
409        if (dep_file) {
410            // replace .spv output path with the embed .cpp path which is used as output in CMakeLists.txt
411            std::string dep = read_binary_file(target_cpp + ".d", true);
412            if (!dep.empty()) {
413                size_t pos = dep.find(out_path);
414                if (pos != std::string::npos) {
415                    dep.replace(pos, out_path.length(), target_cpp);
416                }
417                write_binary_file(target_cpp + ".d", dep);
418            }
419        }
420
421        std::lock_guard<std::mutex> guard(lock);
422        shader_fnames.push_back(std::make_pair(name, out_path));
423    } catch (const std::exception& e) {
424        std::cerr << "Error executing command for " << name << ": " << e.what() << std::endl;
425        compile_failed = true;
426    }
427}
428
429std::map<std::string, std::string> merge_maps(const std::map<std::string, std::string>& a, const std::map<std::string, std::string>& b) {
430    std::map<std::string, std::string> result = a;
431    result.insert(b.begin(), b.end());
432    return result;
433}
434
435static std::deque<std::future<void>> compiles;
436void string_to_spv(std::string name, const std::string& source, const std::map<std::string, std::string>& defines, bool fp16 = true, bool coopmat = false, bool coopmat2 = false, bool f16acc = false, const std::string& suffix = "") {
437    name = name + (f16acc ? "_f16acc" : "") + (coopmat ? "_cm1" : "") + (coopmat2 ? "_cm2" : (fp16 ? "" : "_fp32")) + suffix;
438    std::string out_path = join_paths(output_dir, name + ".spv");
439
440    if (input_filepath == "") {
441        // No input source to compile, only generate header for all shaders
442        shader_fnames.push_back(std::pair(name, out_path));
443        return;
444    } else if (basename(input_filepath) != source) {
445        // Only compile shader variants matching the input filename
446        return;
447    }
448
449    compile_count_guard slot = acquire_compile_slot();
450    compiles.push_back(std::async(
451        string_to_spv_func, name, input_filepath, out_path, defines, coopmat, generate_dep_file, std::move(slot)));
452    // Don't write the same dep file from multiple processes
453    generate_dep_file = false;
454
455    // Clean up completed futures - don't accumulate virtual memory for completed threads' stacks.
456    while (!compiles.empty() && compiles.front().wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
457        compiles.pop_front();
458    }
459}
460
461void matmul_shaders(bool fp16, MatMulIdType matmul_id_type, bool coopmat, bool coopmat2, bool f16acc, bool dot2 = false) {
462    std::string load_vec = coopmat2 ? "1" : fp16 ? "8" : "4";
463    std::string aligned_b_type_f32 = coopmat2 ? "float" : fp16 ? "mat2x4" : "vec4";
464    std::string aligned_b_type_f16 = coopmat2 ? "float16_t" : fp16 ? "f16mat2x4" : "f16vec4";
465    std::string dot2_sfx = dot2 ? "_dot2" : "";
466
467    std::map<std::string, std::string> base_dict;
468    std::string shader_name = "matmul";
469
470    if (matmul_id_type == MatMulIdType::DEFAULT) {
471        base_dict["MUL_MAT_ID"] = "1";
472        shader_name = "matmul_id";
473    } else if (matmul_id_type == MatMulIdType::SUBGROUP) {
474        base_dict["MUL_MAT_ID"] = "1";
475        base_dict["MUL_MAT_ID_USE_SUBGROUPS"] = "1";
476        shader_name = "matmul_id_subgroup";
477    }
478
479    if (fp16) {
480        base_dict["FLOAT16"] = "1";
481    }
482
483    base_dict["ACC_TYPE"  ] = f16acc ? "float16_t" : "float";
484    base_dict["ACC_TYPEV2"] = f16acc ? "f16vec2"   : "vec2";
485    if (f16acc) {
486        base_dict["ACC_TYPE_MAX"] = "float16_t(65504.0)";
487    }
488
489    if (coopmat) {
490        base_dict["COOPMAT"] = "1";
491    }
492#if defined(GGML_VULKAN_COOPMAT2_DECODE_VECTOR_GLSLC_SUPPORT)
493    if (coopmat2) {
494        base_dict["GGML_VULKAN_COOPMAT2_DECODE_VECTOR"] = "1";
495    }
496#endif
497
498    if (dot2) {
499        base_dict["DOT2_F16"] = "1";
500    }
501
502    const std::string source_name = coopmat2 ? "mul_mm_cm2.comp" : "mul_mm.comp";
503
504    auto const &FLOAT_TYPE = [&](int vec, const std::string &t) -> std::string {
505        switch (vec) {
506        case 1:
507            if (t == "bf16") {
508                // scalar path promotes to float
509                if (!coopmat && !coopmat2) {
510                    return "float";
511                }
512                return "bfloat16_t";
513            }
514            if (coopmat2 || fp16) {
515                return "float16_t";
516            }
517            return "float";
518        case 2:
519            if (t == "bf16") {
520                // scalar path promotes to float
521                if (!coopmat && !coopmat2) {
522                    return "vec2";
523                }
524                return "bf16vec2";
525            }
526            if (coopmat2 || fp16) {
527                return "f16vec2";
528            }
529            return "vec2";
530        case 4:
531            if (t == "bf16") {
532                // scalar path promotes to float
533                if (!coopmat && !coopmat2) {
534                    return "vec4";
535                }
536                return "bf16vec4";
537            }
538            if (coopmat2 || fp16) {
539                return "f16vec4";
540            }
541            return "vec4";
542        case 8:
543            if (t == "bf16") {
544                // scalar path promotes to float
545                if (!coopmat && !coopmat2) {
546                    return "mat2x4";
547                }
548                throw std::runtime_error("bf16 vec8 not supported");
549            }
550            if (coopmat2 || fp16) {
551                return "f16mat2x4";
552            }
553            return "mat2x4";
554        default:
555            throw std::runtime_error("invalid vector size");
556        }
557    };
558
559    const std::map<std::string, std::string> float_type_dict_f16 = {
560        {"FLOAT_TYPE",   FLOAT_TYPE(1, "f16")},
561        {"FLOAT_TYPEV2", FLOAT_TYPE(2, "f16")},
562        {"FLOAT_TYPEV4", FLOAT_TYPE(4, "f16")},
563        {"FLOAT_TYPEV8", FLOAT_TYPE(8, "f16")},
564    };
565
566    // Shaders with f16 B_TYPE
567    string_to_spv(shader_name + "_f32_f16" + dot2_sfx, source_name, merge_maps(merge_maps(base_dict, float_type_dict_f16), {{"DATA_A_F32", "1"}, {"LOAD_VEC_A", load_vec}, {"LOAD_VEC_B", load_vec}, {"B_TYPE", aligned_b_type_f16}, {"B_TYPE_SCALAR", "float16_t"}, {"B_TYPEV4", "f16vec4"}, {"D_TYPE", "float"}}), fp16, coopmat, coopmat2, f16acc);
568
569    string_to_spv(shader_name + "_f16" + dot2_sfx, source_name, merge_maps(merge_maps(base_dict, float_type_dict_f16), {{"DATA_A_F16", "1"}, {"LOAD_VEC_A", load_vec}, {"LOAD_VEC_B", load_vec}, {"B_TYPE", aligned_b_type_f16}, {"B_TYPE_SCALAR", "float16_t"}, {"B_TYPEV4", "f16vec4"}, {"D_TYPE", "float"}}), fp16, coopmat, coopmat2, f16acc);
570
571    // bf16
572    {
573        // For aligned matmul loads
574        std::string load_vec_a = coopmat2 ? "1" : "4";
575
576        // scalar path promotes to float
577        std::string to_float_type = (coopmat || coopmat2) ? "uintBitsToBFloat16EXT" : "bf16_to_fp32";
578
579        const std::map<std::string, std::string> float_type_dict_bf16 = {
580            {"FLOAT_TYPE",   FLOAT_TYPE(1, "bf16")},
581            {"FLOAT_TYPEV2", FLOAT_TYPE(2, "bf16")},
582            {"FLOAT_TYPEV4", FLOAT_TYPE(4, "bf16")},
583        };
584
585        // If bfloat16 is not supported, then only compile the scalar (promote to fp32) shader
586#if !defined(GGML_VULKAN_BFLOAT16_GLSLC_SUPPORT)
587        if (!(coopmat || coopmat2))
588#endif
589        {
590            if (!dot2) {
591                string_to_spv(shader_name + "_bf16", source_name, merge_maps(merge_maps(base_dict, float_type_dict_bf16), {{"TO_FLOAT_TYPE", to_float_type}, {"DATA_A_BF16", "1"}, {"LOAD_VEC_A", load_vec_a}, {"LOAD_VEC_B", "4"}, {"B_TYPE", coopmat2 ? "bfloat16_t" : "u16vec4"}, {"B_TYPE_SCALAR", coopmat2 ? "bfloat16_t" : "uint16_t"}, {"B_TYPEV4", "bf16vec4"}, {"D_TYPE", "float"}, {"B_IS_FLOAT", "1"}, {"DATA_B_BF16", "1"}}), fp16, coopmat, coopmat2, f16acc);
592            }
593        }
594    }
595
596    for (const auto& tname : type_names) {
597        if (tname == "bf16") {
598            continue;
599        }
600
601        // Float types keep per-type compilation (different accumulation loop structure)
602        if (tname == "f32" || tname == "f16") {
603            std::string data_a_key = "DATA_A_" + to_uppercase(tname);
604
605            const std::map<std::string, std::string> float_type_dict = {
606                {"FLOAT_TYPE",   FLOAT_TYPE(1, tname)},
607                {"FLOAT_TYPEV2", FLOAT_TYPE(2, tname)},
608                {"FLOAT_TYPEV4", FLOAT_TYPE(4, tname)},
609                {"FLOAT_TYPEV8", FLOAT_TYPE(8, tname)},
610            };
611
612            if (!coopmat2) {
613                string_to_spv(shader_name + "_" + tname + "_f32" + dot2_sfx, source_name, merge_maps(merge_maps(base_dict, float_type_dict), {{data_a_key, "1"}, {"LOAD_VEC_A", load_vec}, {"LOAD_VEC_B", load_vec}, {"B_TYPE", aligned_b_type_f32}, {"B_TYPE_SCALAR", "float"}, {"B_TYPEV4", "vec4"}, {"D_TYPE", "float"}}), fp16, coopmat, coopmat2, f16acc);
614            }
615            continue;
616        }
617
618        std::string data_a_key = "DATA_A_" + to_uppercase(tname);
619        const std::map<std::string, std::string> float_type_dict = {
620            {"FLOAT_TYPE",   FLOAT_TYPE(1, tname)},
621            {"FLOAT_TYPEV2", FLOAT_TYPE(2, tname)},
622            {"FLOAT_TYPEV4", FLOAT_TYPE(4, tname)},
623            {"FLOAT_TYPEV8", FLOAT_TYPE(8, tname)},
624        };
625
626#if defined(GGML_VULKAN_INTEGER_DOT_GLSLC_SUPPORT)
627        if (!f16acc && !coopmat && !coopmat2 && !dot2 && (is_legacy_quant(tname) || is_k_quant(tname) || tname == "mxfp4")) {
628            string_to_spv(shader_name + "_" + tname + "_q8_1", "mul_mmq.comp", merge_maps(merge_maps(base_dict, float_type_dict), {{data_a_key, "1"}, {"D_TYPE", "float"},}), fp16, coopmat, coopmat2, f16acc);
629        }
630#endif
631
632        if (is_lut_quant(tname)) {
633            std::string lva = lut_load_vec_a(tname);
634
635            string_to_spv(shader_name + "_" + tname + "_f16" + dot2_sfx, source_name, merge_maps(merge_maps(base_dict, float_type_dict), {{data_a_key, "1"}, {"LOAD_VEC_A", lva}, {"LOAD_VEC_B", load_vec}, {"B_TYPE", aligned_b_type_f16}, {"B_TYPE_SCALAR", "float16_t"}, {"B_TYPEV4", "f16vec4"}, {"D_TYPE", "float"}}), fp16, coopmat, coopmat2, f16acc);
636
637            if (!coopmat2) {
638                string_to_spv(shader_name + "_" + tname + "_f32" + dot2_sfx, source_name, merge_maps(merge_maps(base_dict, float_type_dict), {{data_a_key, "1"}, {"LOAD_VEC_A", lva}, {"LOAD_VEC_B", load_vec}, {"B_TYPE", aligned_b_type_f32}, {"B_TYPE_SCALAR", "float"}, {"B_TYPEV4", "vec4"}, {"D_TYPE", "float"}}), fp16, coopmat, coopmat2, f16acc);
639            }
640
641#if defined(GGML_VULKAN_FLOAT_E2M1_GLSLC_SUPPORT) && defined(GGML_VULKAN_FLOAT_E4M3_GLSLC_SUPPORT)
642            if ((tname == "mxfp4" || tname == "nvfp4") && (coopmat || coopmat2)) {
643                string_to_spv(shader_name + "_" + tname + "_f16_ocp" + dot2_sfx, source_name, merge_maps(merge_maps(base_dict, float_type_dict), {{data_a_key, "1"}, {"USE_OCP_FP4", "1"}, {"LOAD_VEC_A", lva}, {"LOAD_VEC_B", load_vec}, {"B_TYPE", aligned_b_type_f16}, {"B_TYPE_SCALAR", "float16_t"}, {"B_TYPEV4", "f16vec4"}, {"D_TYPE", "float"}}), fp16, coopmat, coopmat2, f16acc);
644                if (!coopmat2) {
645                    string_to_spv(shader_name + "_" + tname + "_f32_ocp" + dot2_sfx, source_name, merge_maps(merge_maps(base_dict, float_type_dict), {{data_a_key, "1"}, {"USE_OCP_FP4", "1"}, {"LOAD_VEC_A", lva}, {"LOAD_VEC_B", load_vec}, {"B_TYPE", aligned_b_type_f32}, {"B_TYPE_SCALAR", "float"}, {"B_TYPEV4", "vec4"}, {"D_TYPE", "float"}}), fp16, coopmat, coopmat2, f16acc);
646                }
647            }
648#endif
649            continue;
650        }
651
652        // dedicated shader needed due to regression on Ampere
653        if (coopmat2 && (tname == "q4_k" || tname == "q5_k")) {
654            string_to_spv(shader_name + "_" + tname + "_f16" + dot2_sfx, source_name, merge_maps(merge_maps(base_dict, float_type_dict), {{data_a_key, "1"}, {"LOAD_VEC_A", load_vec}, {"LOAD_VEC_B", load_vec}, {"B_TYPE", aligned_b_type_f16}, {"B_TYPE_SCALAR", "float16_t"}, {"B_TYPEV4", "f16vec4"}, {"D_TYPE", "float"}}), fp16, coopmat, coopmat2, f16acc);
655        }
656    }
657
658    // Quant shader: one SPIR-V for all quant types, selected via MmTypeA spec constant
659    {
660        const std::map<std::string, std::string> quant_float_type_dict = {
661            {"FLOAT_TYPE",   FLOAT_TYPE(1, "q4_0")},
662            {"FLOAT_TYPEV2", FLOAT_TYPE(2, "q4_0")},
663            {"FLOAT_TYPEV4", FLOAT_TYPE(4, "q4_0")},
664            {"FLOAT_TYPEV8", FLOAT_TYPE(8, "q4_0")},
665        };
666
667        string_to_spv(shader_name + "_quant_f16" + dot2_sfx, source_name, merge_maps(merge_maps(base_dict, quant_float_type_dict), {{"MULMAT_QUANT", "1"}, {"LOAD_VEC_B", load_vec}, {"B_TYPE", aligned_b_type_f16}, {"B_TYPE_SCALAR", "float16_t"}, {"B_TYPEV4", "f16vec4"}, {"D_TYPE", "float"}}), fp16, coopmat, coopmat2, f16acc);
668
669        if (!coopmat2) {
670            string_to_spv(shader_name + "_quant_f32" + dot2_sfx, source_name, merge_maps(merge_maps(base_dict, quant_float_type_dict), {{"MULMAT_QUANT", "1"}, {"LOAD_VEC_B", load_vec}, {"B_TYPE", aligned_b_type_f32}, {"B_TYPE_SCALAR", "float"}, {"B_TYPEV4", "vec4"}, {"D_TYPE", "float"}}), fp16, coopmat, coopmat2, f16acc);
671        }
672    }
673}
674
675void process_shaders() {
676    // matmul
677    for (const MatMulIdType& matmul_id_type : {MatMulIdType::NONE, MatMulIdType::DEFAULT, MatMulIdType::SUBGROUP}) {
678        // No coopmats
679        // fp32
680        matmul_shaders(false, matmul_id_type, false, false, false);
681
682        // fp16, fp32acc and fp16acc
683        matmul_shaders(true, matmul_id_type, false, false, false);
684        matmul_shaders(true, matmul_id_type, false, false, true);
685
686        // dot2 variants (scalar fp16 only)
687        matmul_shaders(true, matmul_id_type, false, false, false, true);
688        matmul_shaders(true, matmul_id_type, false, false, true,  true);
689
690        if (matmul_id_type != MatMulIdType::DEFAULT) {
691#if defined(GGML_VULKAN_COOPMAT_GLSLC_SUPPORT)
692            // Coopmat, fp32acc and fp16acc
693            matmul_shaders(true, matmul_id_type, true, false, false);
694            matmul_shaders(true, matmul_id_type, true, false, true);
695#endif
696
697#if defined(GGML_VULKAN_COOPMAT2_GLSLC_SUPPORT)
698            // Coopmat2, fp32acc and fp16acc
699            matmul_shaders(true, matmul_id_type, false, true, false);
700            matmul_shaders(true, matmul_id_type, false, true, true);
701#endif
702        }
703    }
704
705    for (const bool& fp16 : {false, true}) {
706        std::map<std::string, std::string> base_dict;
707        if (fp16) {
708            base_dict = {{"FLOAT_TYPE", "float16_t"}, {"FLOAT_TYPEV2", "f16vec2"}, {"FLOAT_TYPEV4", "f16vec4"}, {"FLOAT16", "1"}, {"FLOAT_TYPE_MAX", "float16_t(65504.0)"}};
709        } else {
710            base_dict = {{"FLOAT_TYPE", "float"}, {"FLOAT_TYPEV2", "vec2"}, {"FLOAT_TYPEV4", "vec4"}};
711        }
712
713        // flash attention
714        for (const bool& f16acc : {false, true}) {
715            std::map<std::string, std::string> fa_base_dict = base_dict;
716            fa_base_dict["ACC_TYPE"] = fp16 && f16acc ? "float16_t" : "float";
717            fa_base_dict["ACC_TYPEV2"] = fp16 && f16acc ? "f16vec2" : "vec2";
718            fa_base_dict["ACC_TYPEV4"] = fp16 && f16acc ? "f16vec4" : "vec4";
719            // Compile IQ4_NL support into all FA variants so its shared LUT is available when K or V uses it.
720            fa_base_dict["DATA_A_IQ4_NL"] = "1";
721            if (fp16 && f16acc) {
722                fa_base_dict["ACC_TYPE_MAX"] = "float16_t(65504.0)";
723            }
724
725            if (fp16) {
726#if defined(GGML_VULKAN_COOPMAT2_GLSLC_SUPPORT)
727                string_to_spv("flash_attn_f32_f16", "flash_attn_cm2.comp",
728                    merge_maps(fa_base_dict, {{"Q_TYPE", "float"}, {"D_TYPE", "float"}, {"D_TYPEV4", "vec4"}}), fp16, false, true, f16acc);
729#endif
730
731#if defined(GGML_VULKAN_COOPMAT_GLSLC_SUPPORT)
732                string_to_spv("flash_attn_f32_f16", "flash_attn_cm1.comp",
733                    merge_maps(fa_base_dict, {{"Q_TYPE", "float"}, {"D_TYPE", "float"}, {"D_TYPEV4", "vec4"}, {"COOPMAT", "1"}}), fp16, true, false, f16acc);
734#endif
735            }
736
737            string_to_spv("flash_attn_f32_f16", "flash_attn.comp",
738                merge_maps(fa_base_dict, {{"Q_TYPE", "float"}, {"D_TYPE", "float"}, {"D_TYPEV4", "vec4"}}), fp16, false, false, f16acc);
739
740            if (fp16) {
741                string_to_spv("flash_attn_f32_f16_dot2", "flash_attn.comp",
742                    merge_maps(fa_base_dict, {{"Q_TYPE", "float"}, {"D_TYPE", "float"}, {"D_TYPEV4", "vec4"}, {"DOT2_F16", "1"}}), fp16, false, false, f16acc);
743            }
744
745#if defined(GGML_VULKAN_INTEGER_DOT_GLSLC_SUPPORT)
746            string_to_spv("flash_attn_f32_f16", "flash_attn.comp",
747                merge_maps(fa_base_dict, {{"Q_TYPE", "float"}, {"D_TYPE", "float"}, {"D_TYPEV4", "vec4"}, {"MMQ", "1"}, {"FA_MMQ_MIXED", "1"}}), fp16, false, false, f16acc, "_int8");
748#endif
749        }
750    }
751
752    const std::map<std::string, std::string> fa_bf16_dict = {
753        {"FLOAT_TYPE",   "bfloat16_t"},
754        {"FLOAT_TYPEV2", "bf16vec2"},
755        {"FLOAT_TYPEV4", "bf16vec4"},
756        {"ACC_TYPE",     "float"},
757        {"ACC_TYPEV2",   "vec2"},
758        {"ACC_TYPEV4",   "vec4"},
759        {"BFLOAT16",     "1"},
760    };
761
762#if defined(GGML_VULKAN_BFLOAT16_GLSLC_SUPPORT) && defined(GGML_VULKAN_COOPMAT_GLSLC_SUPPORT)
763    string_to_spv("flash_attn_f32_f16_bf16", "flash_attn_cm1.comp",
764        merge_maps(fa_bf16_dict, {{"Q_TYPE", "float"}, {"D_TYPE", "float"}, {"D_TYPEV4", "vec4"}, {"COOPMAT", "1"}}),
765        true, true, false, false);
766#endif
767
768#if defined(GGML_VULKAN_BFLOAT16_GLSLC_SUPPORT) && defined(GGML_VULKAN_COOPMAT2_GLSLC_SUPPORT)
769    string_to_spv("flash_attn_f32_f16_bf16", "flash_attn_cm2.comp",
770        merge_maps(fa_bf16_dict, {{"Q_TYPE", "float"}, {"D_TYPE", "float"}, {"D_TYPEV4", "vec4"}}),
771        true, false, true, false);
772#endif
773
774    std::map<std::string, std::string> base_dict = {{"FLOAT_TYPE", "float"}, {"FLOAT_TYPEV2", "vec2"}};
775
776    for (const auto& tname : type_names) {
777        // mul mat vec
778        std::string data_a_key = "DATA_A_" + to_uppercase(tname);
779        std::string shader = (string_ends_with(tname, "_k") || string_starts_with(tname, "iq1_") || string_starts_with(tname, "iq2_") || string_starts_with(tname, "iq3_") || tname == "iq4_xs" || tname == "tq2_0" || tname == "tq1_0") ? "mul_mat_vec_" + tname + ".comp" : "mul_mat_vec.comp";
780
781        string_to_spv("mul_mat_vec_" + tname + "_f32_f32", shader, merge_maps(base_dict, {{data_a_key, "1"}, {"B_TYPE", "float"}, {"B_TYPEV2", "vec2"}, {"B_TYPEV4", "vec4"}, {"D_TYPE", "float"}}));
782        string_to_spv("mul_mat_vec_" + tname + "_f16_f32", shader, merge_maps(base_dict, {{data_a_key, "1"}, {"B_TYPE", "float16_t"}, {"B_TYPEV2", "f16vec2"}, {"B_TYPEV4", "f16vec4"}, {"D_TYPE", "float"}}));
783
784        string_to_spv("mul_mat_vec_" + tname + "_f32_f32_subgroup", shader, merge_maps(base_dict, {{data_a_key, "1"}, {"B_TYPE", "float"}, {"B_TYPEV2", "vec2"}, {"B_TYPEV4", "vec4"}, {"D_TYPE", "float"}, {"USE_SUBGROUP_ADD", "1"}}));
785        string_to_spv("mul_mat_vec_" + tname + "_f16_f32_subgroup", shader, merge_maps(base_dict, {{data_a_key, "1"}, {"B_TYPE", "float16_t"}, {"B_TYPEV2", "f16vec2"}, {"B_TYPEV4", "f16vec4"}, {"D_TYPE", "float"}, {"USE_SUBGROUP_ADD", "1"}}));
786
787        string_to_spv("mul_mat_vec_" + tname + "_f32_f32_subgroup_no_shmem", shader, merge_maps(base_dict, {{data_a_key, "1"}, {"B_TYPE", "float"}, {"B_TYPEV2", "vec2"}, {"B_TYPEV4", "vec4"}, {"D_TYPE", "float"}, {"USE_SUBGROUP_ADD_NO_SHMEM", "1"}}));
788        string_to_spv("mul_mat_vec_" + tname + "_f16_f32_subgroup_no_shmem", shader, merge_maps(base_dict, {{data_a_key, "1"}, {"B_TYPE", "float16_t"}, {"B_TYPEV2", "f16vec2"}, {"B_TYPEV4", "f16vec4"}, {"D_TYPE", "float"}, {"USE_SUBGROUP_ADD_NO_SHMEM", "1"}}));
789
790#if defined(GGML_VULKAN_FLOAT_E2M1_GLSLC_SUPPORT) && defined(GGML_VULKAN_FLOAT_E4M3_GLSLC_SUPPORT)
791        if (tname == "mxfp4" || tname == "nvfp4") {
792            string_to_spv("mul_mat_vec_" + tname + "_f32_f32_ocp", shader, merge_maps(base_dict, {{data_a_key, "1"}, {"USE_OCP_FP4", "1"}, {"B_TYPE", "float"}, {"B_TYPEV2", "vec2"}, {"B_TYPEV4", "vec4"}, {"D_TYPE", "float"}}));
793            string_to_spv("mul_mat_vec_" + tname + "_f16_f32_ocp", shader, merge_maps(base_dict, {{data_a_key, "1"}, {"USE_OCP_FP4", "1"}, {"B_TYPE", "float16_t"}, {"B_TYPEV2", "f16vec2"}, {"B_TYPEV4", "f16vec4"}, {"D_TYPE", "float"}}));
794            string_to_spv("mul_mat_vec_" + tname + "_f32_f32_ocp_subgroup", shader, merge_maps(base_dict, {{data_a_key, "1"}, {"USE_OCP_FP4", "1"}, {"B_TYPE", "float"}, {"B_TYPEV2", "vec2"}, {"B_TYPEV4", "vec4"}, {"D_TYPE", "float"}, {"USE_SUBGROUP_ADD", "1"}}));
795            string_to_spv("mul_mat_vec_" + tname + "_f16_f32_ocp_subgroup", shader, merge_maps(base_dict, {{data_a_key, "1"}, {"USE_OCP_FP4", "1"}, {"B_TYPE", "float16_t"}, {"B_TYPEV2", "f16vec2"}, {"B_TYPEV4", "f16vec4"}, {"D_TYPE", "float"}, {"USE_SUBGROUP_ADD", "1"}}));
796            string_to_spv("mul_mat_vec_" + tname + "_f32_f32_ocp_subgroup_no_shmem", shader, merge_maps(base_dict, {{data_a_key, "1"}, {"USE_OCP_FP4", "1"}, {"B_TYPE", "float"}, {"B_TYPEV2", "vec2"}, {"B_TYPEV4", "vec4"}, {"D_TYPE", "float"}, {"USE_SUBGROUP_ADD_NO_SHMEM", "1"}}));
797            string_to_spv("mul_mat_vec_" + tname + "_f16_f32_ocp_subgroup_no_shmem", shader, merge_maps(base_dict, {{data_a_key, "1"}, {"USE_OCP_FP4", "1"}, {"B_TYPE", "float16_t"}, {"B_TYPEV2", "f16vec2"}, {"B_TYPEV4", "f16vec4"}, {"D_TYPE", "float"}, {"USE_SUBGROUP_ADD_NO_SHMEM", "1"}}));
798            string_to_spv("mul_mat_vec_id_" + tname + "_f32_f32_ocp", shader, merge_maps(base_dict, {{"MUL_MAT_ID", "1"}, {data_a_key, "1"}, {"USE_OCP_FP4", "1"}, {"B_TYPE", "float"}, {"B_TYPEV2", "vec2"}, {"B_TYPEV4", "vec4"}, {"D_TYPE", "float"}}));
799            string_to_spv("mul_mat_vec_id_" + tname + "_f32_f32_ocp_subgroup", shader, merge_maps(base_dict, {{"MUL_MAT_ID", "1"}, {data_a_key, "1"}, {"USE_OCP_FP4", "1"}, {"B_TYPE", "float"}, {"B_TYPEV2", "vec2"}, {"B_TYPEV4", "vec4"}, {"D_TYPE", "float"}, {"USE_SUBGROUP_ADD", "1"}}));
800            string_to_spv("mul_mat_vec_id_" + tname + "_f32_f32_ocp_subgroup_no_shmem", shader, merge_maps(base_dict, {{"MUL_MAT_ID", "1"}, {data_a_key, "1"}, {"USE_OCP_FP4", "1"}, {"B_TYPE", "float"}, {"B_TYPEV2", "vec2"}, {"B_TYPEV4", "vec4"}, {"D_TYPE", "float"}, {"USE_SUBGROUP_ADD_NO_SHMEM", "1"}}));
801        }
802#endif
803
804        string_to_spv("mul_mat_vec_id_" + tname + "_f32_f32", shader, merge_maps(base_dict, {{"MUL_MAT_ID", "1"}, {data_a_key, "1"}, {"B_TYPE", "float"}, {"B_TYPEV2", "vec2"}, {"B_TYPEV4", "vec4"}, {"D_TYPE", "float"}}));
805        string_to_spv("mul_mat_vec_id_" + tname + "_f32_f32_subgroup", shader, merge_maps(base_dict, {{"MUL_MAT_ID", "1"}, {data_a_key, "1"}, {"B_TYPE", "float"}, {"B_TYPEV2", "vec2"}, {"B_TYPEV4", "vec4"}, {"D_TYPE", "float"}, {"USE_SUBGROUP_ADD", "1"}}));
806        string_to_spv("mul_mat_vec_id_" + tname + "_f32_f32_subgroup_no_shmem", shader, merge_maps(base_dict, {{"MUL_MAT_ID", "1"}, {data_a_key, "1"}, {"B_TYPE", "float"}, {"B_TYPEV2", "vec2"}, {"B_TYPEV4", "vec4"}, {"D_TYPE", "float"}, {"USE_SUBGROUP_ADD_NO_SHMEM", "1"}}));
807
808        // mul mat vec with integer dot product
809#if defined(GGML_VULKAN_INTEGER_DOT_GLSLC_SUPPORT)
810        if (is_legacy_quant(tname) || tname == "mxfp4" || is_k_quant(tname) || tname == "iq1_s" || tname == "iq1_m") {
811            string_to_spv("mul_mat_vec_" + tname + "_q8_1_f32", "mul_mat_vecq.comp", merge_maps(base_dict, {{data_a_key, "1"}, {"D_TYPE", "float"}, {"FLOAT_TYPE", "float"}, {"FLOAT_TYPEV2", "vec2"}, {"ACC_TYPE", "float"}}));
812            string_to_spv("mul_mat_vec_" + tname + "_q8_1_f32_subgroup", "mul_mat_vecq.comp", merge_maps(base_dict, {{data_a_key, "1"}, {"D_TYPE", "float"}, {"FLOAT_TYPE", "float"}, {"FLOAT_TYPEV2", "vec2"}, {"ACC_TYPE", "float"}, {"USE_SUBGROUP_ADD", "1"}}));
813            string_to_spv("mul_mat_vec_" + tname + "_q8_1_f32_subgroup_no_shmem", "mul_mat_vecq.comp", merge_maps(base_dict, {{data_a_key, "1"}, {"D_TYPE", "float"}, {"FLOAT_TYPE", "float"}, {"FLOAT_TYPEV2", "vec2"}, {"ACC_TYPE", "float"}, {"USE_SUBGROUP_ADD_NO_SHMEM", "1"}}));
814
815            string_to_spv("mul_mat_vec_id_" + tname + "_q8_1_f32", "mul_mat_vecq.comp", merge_maps(base_dict, {{"MUL_MAT_ID", "1"}, {data_a_key, "1"}, {"D_TYPE", "float"}, {"FLOAT_TYPE", "float"}, {"FLOAT_TYPEV2", "vec2"}, {"ACC_TYPE", "float"}}));
816            string_to_spv("mul_mat_vec_id_" + tname + "_q8_1_f32_subgroup", "mul_mat_vecq.comp", merge_maps(base_dict, {{"MUL_MAT_ID", "1"}, {data_a_key, "1"}, {"D_TYPE", "float"}, {"FLOAT_TYPE", "float"}, {"FLOAT_TYPEV2", "vec2"}, {"ACC_TYPE", "float"}, {"USE_SUBGROUP_ADD", "1"}}));
817            string_to_spv("mul_mat_vec_id_" + tname + "_q8_1_f32_subgroup_no_shmem", "mul_mat_vecq.comp", merge_maps(base_dict, {{"MUL_MAT_ID", "1"}, {data_a_key, "1"}, {"D_TYPE", "float"}, {"FLOAT_TYPE", "float"}, {"FLOAT_TYPEV2", "vec2"}, {"ACC_TYPE", "float"}, {"USE_SUBGROUP_ADD_NO_SHMEM", "1"}}));
818        }
819#endif
820
821        // Dequant shaders
822        if (tname != "f16" && tname != "bf16") {
823            string_to_spv("dequant_" + tname, "dequant_" + tname + ".comp", merge_maps(base_dict, {{data_a_key, "1"}, {"D_TYPE", "float16_t"}}));
824        }
825        // Fused dequant+transpose variant for FA quant-KV (per-head-contiguous f16 scratch).
826        if (tname == "q8_0") {
827            string_to_spv("dequant_" + tname + "_transpose", "dequant_" + tname + ".comp", merge_maps(base_dict, {{data_a_key, "1"}, {"D_TYPE", "float16_t"}, {"DEQUANT_TRANSPOSE", "1"}}));
828        }
829
830        shader = (tname == "f32" || tname == "f16" || tname == "bf16") ? "get_rows.comp" : "get_rows_quant.comp";
831
832        if (tname == "f16") {
833            string_to_spv("get_rows_" + tname, shader, merge_maps(base_dict, {{"TEMP_TYPE", "FLOAT_TYPE"}, {data_a_key, "1"}, {"B_TYPE", "int"}, {"D_TYPE", "float16_t"}, {"OPTIMIZATION_ERROR_WORKAROUND", "1"}}));
834        } else {
835            string_to_spv("get_rows_" + tname, shader, merge_maps(base_dict, {{"TEMP_TYPE", "FLOAT_TYPE"}, {data_a_key, "1"}, {"B_TYPE", "int"}, {"D_TYPE", "float16_t"}}));
836        }
837        string_to_spv("get_rows_" + tname + "_f32", shader, merge_maps(base_dict, {{"TEMP_TYPE", "FLOAT_TYPE"}, {data_a_key, "1"}, {"B_TYPE", "int"}, {"D_TYPE", "float"}}));
838    }
839
840    string_to_spv("get_rows_i32", "get_rows.comp", {{"TEMP_TYPE", "uint"}, {"A_TYPE", "uint"}, {"B_TYPE", "int"}, {"D_TYPE", "uint"}});
841
842    string_to_spv("mul_mat_vec_p021_f16_f32_subgroup_add", "mul_mat_vec_p021.comp", {{"A_TYPE", "float16_t"}, {"A_TYPEV4", "f16vec4"}, {"B_TYPE", "float"}, {"B_TYPEV4", "vec4"}, {"D_TYPE", "float"}, {"USE_SUBGROUP_ADD", "1"}});
843    string_to_spv("mul_mat_vec_p021_f16_f32",              "mul_mat_vec_p021.comp", {{"A_TYPE", "float16_t"}, {"A_TYPEV4", "f16vec4"}, {"B_TYPE", "float"}, {"B_TYPEV4", "vec4"}, {"D_TYPE", "float"}});
844    string_to_spv("mul_mat_vec_nc_f16_f32", "mul_mat_vec_nc.comp", {{"A_TYPE", "float16_t"}, {"A_TYPEV4", "f16vec4"}, {"B_TYPE", "float"}, {"B_TYPEV4", "vec4"}, {"D_TYPE", "float"}});
845
846    // Norms
847    string_to_spv("norm_f32", "norm.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"D_TYPE", "float"}}));
848    string_to_spv("group_norm_f32", "group_norm.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"D_TYPE", "float"}}));
849    string_to_spv("rms_norm_f32", "rms_norm.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}}));
850    string_to_spv("rms_norm_mul_add_f32", "rms_norm.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}, {"RMS_NORM_ADD_FUSION", "1"}}));
851    string_to_spv("rms_norm_mul_add_partials_f32", "rms_norm_partials.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}, {"RMS_NORM_ADD_FUSION", "1"}}));
852    string_to_spv("rms_norm_set_rows_f32_f32", "rms_norm.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}, {"RMS_NORM_SET_ROWS_FUSION", "1"}}));
853    string_to_spv("rms_norm_set_rows_f32_f16", "rms_norm.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float16_t"}, {"RMS_NORM_SET_ROWS_FUSION", "1"}}));
854    string_to_spv("rms_norm_partials_f32", "rms_norm_partials.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}}));
855    string_to_spv("rms_norm_mul_rope_f32_f32", "rms_norm.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}, {"ROPE_D_TYPE", "float"}, {"RMS_NORM_ROPE_FUSION", "1"}}));
856    string_to_spv("rms_norm_mul_rope_f32_f16", "rms_norm.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}, {"ROPE_D_TYPE", "float16_t"}, {"RMS_NORM_ROPE_FUSION", "1"}}));
857    string_to_spv("rms_norm_back_f32", "rms_norm_back.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}}));
858    string_to_spv("l2_norm_f32", "l2_norm.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"D_TYPE", "float"}}));
859
860    string_to_spv("cpy_f32_f32", "copy.comp", {{"A_TYPE", "float"}, {"D_TYPE", "float"}});
861    string_to_spv("cpy_f32_f16", "copy.comp", {{"A_TYPE", "float"}, {"D_TYPE", "float16_t"}});
862    string_to_spv("cpy_f16_f16", "copy.comp", {{"A_TYPE", "float16_t"}, {"D_TYPE", "float16_t"}, {"OPTIMIZATION_ERROR_WORKAROUND", "1"}});
863    string_to_spv("cpy_f16_f32", "copy.comp", {{"A_TYPE", "float16_t"}, {"D_TYPE", "float"}, {"OPTIMIZATION_ERROR_WORKAROUND", "1"}});
864    string_to_spv("cpy_f32_bf16","copy.comp", {{"A_TYPE", "float"}, {"D_TYPE", "uint16_t"}, {"DATA_D_BF16", "1"}});
865    string_to_spv("cpy_bf16_f32","copy.comp", {{"A_TYPE", "uint16_t"}, {"D_TYPE", "float"}, {"DATA_A_BF16", "1"}});
866    string_to_spv("contig_cpy_f32_f32", "contig_copy.comp", {{"A_TYPE", "float"}, {"D_TYPE", "float"}});
867    string_to_spv("contig_cpy_f32_i32", "contig_copy.comp", {{"A_TYPE", "float"}, {"D_TYPE", "int"}});
868    string_to_spv("contig_cpy_i32_f32", "contig_copy.comp", {{"A_TYPE", "int"}, {"D_TYPE", "float"}});
869    string_to_spv("contig_cpy_f32_f16", "contig_copy.comp", {{"A_TYPE", "float"}, {"D_TYPE", "float16_t"}});
870    string_to_spv("contig_cpy_f16_f16", "contig_copy.comp", {{"A_TYPE", "float16_t"}, {"D_TYPE", "float16_t"}, {"OPTIMIZATION_ERROR_WORKAROUND", "1"}});
871    string_to_spv("contig_cpy_f16_f32", "contig_copy.comp", {{"A_TYPE", "float16_t"}, {"D_TYPE", "float"}, {"OPTIMIZATION_ERROR_WORKAROUND", "1"}});
872    string_to_spv("contig_cpy_f32_bf16","contig_copy.comp",{{"A_TYPE", "float"}, {"D_TYPE", "uint16_t"}, {"DATA_D_BF16", "1"}});
873    string_to_spv("contig_cpy_bf16_f32","contig_copy.comp",{{"A_TYPE", "uint16_t"}, {"D_TYPE", "float"}, {"DATA_A_BF16", "1"}});
874    string_to_spv("cpy_f32_i32", "copy.comp", {{"A_TYPE", "float"}, {"D_TYPE", "int"}});
875    string_to_spv("cpy_i32_f32", "copy.comp", {{"A_TYPE", "int"}, {"D_TYPE", "float"}});
876
877    string_to_spv("cpy_transpose_16", "copy_transpose.comp", {{"A_TYPE", "uint16_t"}, {"D_TYPE", "uint16_t"}});
878    string_to_spv("cpy_transpose_32", "copy_transpose.comp", {{"A_TYPE", "uint"}, {"D_TYPE", "uint"}});
879    string_to_spv("cpy_transpose_02_16", "copy_transpose_02.comp", {{"A_TYPE", "uint16_t"}, {"D_TYPE", "uint16_t"}});
880    string_to_spv("cpy_transpose_02_32", "copy_transpose_02.comp", {{"A_TYPE", "uint"}, {"D_TYPE", "uint"}});
881
882    for (std::string t : {"q1_0", "q2_0", "q4_0", "q4_1", "q5_0", "q5_1", "q8_0", "iq4_nl"}) {
883        string_to_spv("cpy_f32_" + t, "copy_to_quant.comp", {{"DATA_A_" + to_uppercase(t), "1"}, {"S_TYPE", "float"}, {"D_TYPE", "float"}, {"FLOAT_TYPE", "float"}});
884        string_to_spv("cpy_" + t + "_f32", "copy_from_quant.comp", {{"DATA_A_" + to_uppercase(t), "1"}, {"D_TYPE", "float"}, {"FLOAT_TYPE", "float"}});
885    }
886
887    for (auto src : {std::pair{"f32", "float"}, std::pair{"f16", "float16_t"}}) {
888        for (std::string dst : {"f32", "f16", "bf16", "q1_0", "q2_0", "q4_0", "q4_1", "q5_0", "q5_1", "q8_0", "iq4_nl"}) {
889            string_to_spv("set_rows_" + std::string(src.first) + "_" + dst + "_i32", "copy_to_quant.comp", {{"SET_ROWS", "1"}, {"DATA_A_" + to_uppercase(dst), "1"}, {"B_TYPE", "uint"}, {"B_SIZE", "32"}, {"S_TYPE", src.second}, {"D_TYPE", "float"}, {"FLOAT_TYPE", "float"}});
890            string_to_spv("set_rows_" + std::string(src.first) + "_" + dst + "_i64", "copy_to_quant.comp", {{"SET_ROWS", "1"}, {"DATA_A_" + to_uppercase(dst), "1"}, {"B_TYPE", "uvec2"}, {"B_SIZE", "64"}, {"S_TYPE", src.second}, {"D_TYPE", "float"}, {"FLOAT_TYPE", "float"}});
891        }
892    }
893
894    auto get_type_str = [](bool f16) {
895        return f16 ? "float16_t" : "float";
896    };
897    auto get_suffix = [](bool src0_f16, bool src1_f16, bool dst_f16) {
898        std::string s;
899        s += std::string(src0_f16 ? "_f16" : "_f32");
900        s += std::string(src1_f16 ? "_f16" : "_f32");
901        s += std::string(dst_f16 ? "_f16" : "_f32");
902        return s;
903    };
904    for (std::string op : {"add", "sub", "mul", "div", "add_rms", }) {
905    for (auto src0_f16 : {false, true}) {
906    for (auto src1_f16 : {false, true}) {
907    for (auto dst_f16  : {false, true}) {
908        auto source = op == "add_rms" ? std::string("add") : op;
909        auto name = op + get_suffix(src0_f16, src1_f16, dst_f16);
910        auto add_rms = op == "add_rms" ? "1" : "0";
911        string_to_spv(name.c_str(), source + ".comp", {{"A_TYPE", get_type_str(src0_f16)}, {"B_TYPE", get_type_str(src1_f16)}, {"D_TYPE", get_type_str(dst_f16)}, {"FLOAT_TYPE", "float"}, {"ADD_RMS" , add_rms}});
912    }
913    }
914    }
915    }
916
917    string_to_spv("sub_f32", "sub.comp", {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}, {"FLOAT_TYPE", "float"}});
918
919    string_to_spv("acc_f32", "acc.comp", {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}, {"FLOAT_TYPE", "float"}});
920
921    string_to_spv("split_k_reduce", "mul_mat_split_k_reduce.comp", {});
922    string_to_spv("fa_split_k_reduce", "flash_attn_split_k_reduce.comp", {});
923
924    string_to_spv("fa_mask_opt", "flash_attn_mask_opt.comp", {});
925
926    string_to_spv("quantize_q8_1", "quantize_q8_1.comp", {});
927    string_to_spv("quantize_q8_1_subgroup", "quantize_q8_1.comp", {{"USE_SUBGROUPS", "1"}});
928
929    string_to_spv("quantize_q8_1_x4", "quantize_q8_1.comp", {{"QBLOCK_X4", "1"}});
930    string_to_spv("quantize_q8_1_x4_subgroup", "quantize_q8_1.comp", {{"QBLOCK_X4", "1"}, {"USE_SUBGROUPS", "1"}});
931
932    string_to_spv("mul_f32", "mul.comp", {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}, {"FLOAT_TYPE", "float"}});
933
934    string_to_spv("div_f32", "div.comp", {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}, {"FLOAT_TYPE", "float"}});
935
936    string_to_spv("repeat_i32", "repeat.comp", {{"A_TYPE", "int32_t"}, {"D_TYPE", "int32_t"}});
937    string_to_spv("repeat_back_f32", "repeat_back.comp", {{"A_TYPE", "float"}, {"D_TYPE", "float"}});
938    string_to_spv("get_rows_back_f32", "get_rows_back.comp", {{"A_TYPE", "float"}, {"B_TYPE", "int"}, {"D_TYPE", "float"}});
939
940    string_to_spv("repeat_i16", "repeat.comp", {{"A_TYPE", "int16_t"}, {"D_TYPE", "int16_t"}});
941
942    string_to_spv("scale_f32", "scale.comp", {{"A_TYPE", "float"}, {"D_TYPE", "float"}, {"FLOAT_TYPE", "float"}});
943
944    string_to_spv("pad_f32", "pad.comp", {{"A_TYPE", "float"}, {"D_TYPE", "float"}});
945    string_to_spv("pad_reflect_1d_f32", "pad_reflect_1d.comp", {{"A_TYPE", "float"}, {"D_TYPE", "float"}});
946
947    string_to_spv("concat_i8", "concat.comp", {{"A_TYPE", "uint8_t"}, {"B_TYPE", "uint8_t"}, {"D_TYPE", "uint8_t"}});
948    string_to_spv("concat_i16", "concat.comp", {{"A_TYPE", "uint16_t"}, {"B_TYPE", "uint16_t"}, {"D_TYPE", "uint16_t"}});
949    string_to_spv("concat_i32", "concat.comp", {{"A_TYPE", "uint"}, {"B_TYPE", "uint"}, {"D_TYPE", "uint"}});
950    string_to_spv("concat_i64", "concat.comp", {{"A_TYPE", "uvec2"}, {"B_TYPE", "uvec2"}, {"D_TYPE", "uvec2"}});
951
952    string_to_spv("upscale_f32", "upscale.comp", {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}});
953
954    string_to_spv("exp_f16",        "unary.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}, {"OP", "op_exp"}});
955    string_to_spv("exp_f32",        "unary.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"},     {"OP", "op_exp"}});
956    string_to_spv("expm1_f16",      "unary.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}, {"OP", "op_expm1"}});
957    string_to_spv("expm1_f32",      "unary.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"},     {"OP", "op_expm1"}});
958
959    string_to_spv("log_f16",        "log.comp",         {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}});
960    string_to_spv("log_f32",        "log.comp",         {{"A_TYPE", "float"},       {"D_TYPE", "float"}});
961    string_to_spv("gelu_f16",       "unary.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}, {"OP", "op_gelu"}});
962    string_to_spv("gelu_f32",       "unary.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"},     {"OP", "op_gelu"}});
963    string_to_spv("gelu_erf_f16",   "unary.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}, {"OP", "op_gelu_erf"}});
964    string_to_spv("gelu_erf_f32",   "unary.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"},     {"OP", "op_gelu_erf"}});
965    string_to_spv("gelu_quick_f16", "unary.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}, {"OP", "op_gelu_quick"}});
966    string_to_spv("gelu_quick_f32", "unary.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"},     {"OP", "op_gelu_quick"}});
967    string_to_spv("silu_f16",       "unary.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}, {"OP", "op_silu"}});
968    string_to_spv("silu_f32",       "unary.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"},     {"OP", "op_silu"}});
969    string_to_spv("relu_f16",       "unary.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}, {"OP", "op_relu"}});
970    string_to_spv("relu_f32",       "unary.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"},     {"OP", "op_relu"}});
971    string_to_spv("sqr_f16",        "unary.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}, {"OP", "op_sqr"}});
972    string_to_spv("sqr_f32",        "unary.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"},     {"OP", "op_sqr"}});
973    string_to_spv("sqrt_f16",       "unary.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}, {"OP", "op_sqrt"}});
974    string_to_spv("sqrt_f32",       "unary.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"},     {"OP", "op_sqrt"}});
975    string_to_spv("sin_f16",        "unary.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}, {"OP", "op_sin"}});
976    string_to_spv("sin_f32",        "unary.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"},     {"OP", "op_sin"}});
977    string_to_spv("cos_f16",        "unary.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}, {"OP", "op_cos"}});
978    string_to_spv("cos_f32",        "unary.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"},     {"OP", "op_cos"}});
979    string_to_spv("clamp_f16",      "unary.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}, {"OP", "op_clamp"}});
980    string_to_spv("clamp_f32",      "unary.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"},     {"OP", "op_clamp"}});
981    string_to_spv("leaky_relu_f16", "unary.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}, {"OP", "op_leaky_relu"}});
982    string_to_spv("leaky_relu_f32", "unary.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"},     {"OP", "op_leaky_relu"}});
983    string_to_spv("neg_f16",        "unary.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}, {"OP", "op_neg"}});
984    string_to_spv("neg_f32",        "unary.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"},     {"OP", "op_neg"}});
985    string_to_spv("tanh_f16",       "unary.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}, {"OP", "op_tanh"}});
986    string_to_spv("tanh_f32",       "unary.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"},     {"OP", "op_tanh"}});
987    string_to_spv("sigmoid_f16",    "unary.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}, {"OP", "op_sigmoid"}});
988    string_to_spv("sigmoid_f32",    "unary.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"},     {"OP", "op_sigmoid"}});
989    string_to_spv("hardsigmoid_f16","unary.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}, {"OP", "op_hardsigmoid"}});
990    string_to_spv("hardsigmoid_f32","unary.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"},     {"OP", "op_hardsigmoid"}});
991    string_to_spv("hardswish_f16",  "unary.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}, {"OP", "op_hardswish"}});
992    string_to_spv("hardswish_f32",  "unary.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"},     {"OP", "op_hardswish"}});
993    string_to_spv("abs_f16",        "unary.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}, {"OP", "op_abs"}});
994    string_to_spv("abs_f32",        "unary.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"},     {"OP", "op_abs"}});
995    string_to_spv("elu_f16",        "unary.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}, {"OP", "op_elu"}});
996    string_to_spv("elu_f32",        "unary.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"},     {"OP", "op_elu"}});
997    string_to_spv("xielu_f16",      "unary.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}, {"OP", "op_xielu"}});
998    string_to_spv("xielu_f32",      "unary.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"},     {"OP", "op_xielu"}});
999    string_to_spv("sgn_f16",        "unary.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}, {"OP", "op_sgn"}});
1000    string_to_spv("sgn_f32",        "unary.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"},     {"OP", "op_sgn"}});
1001
1002    string_to_spv("tri_f16",        "tri.comp",         {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}});
1003    string_to_spv("tri_f32",        "tri.comp",         {{"A_TYPE", "float"},       {"D_TYPE", "float"}});
1004    string_to_spv("diag_f16",       "diag.comp",        {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}});
1005    string_to_spv("diag_f32",       "diag.comp",        {{"A_TYPE", "float"},       {"D_TYPE", "float"}});
1006
1007    string_to_spv("softplus_f16",   "unary.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}, {"OP", "op_softplus"}});
1008    string_to_spv("softplus_f32",   "unary.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"},     {"OP", "op_softplus"}});
1009
1010    string_to_spv("gelu_mul_f32",    "unary.comp",      {{"A_TYPE", "float"},       {"B_TYPE", "float"},     {"D_TYPE", "float"},     {"FLOAT_TYPE", "float"}, {"OP", "op_gelu"},     {"UNARY_MUL_FUSION", "1"}});
1011    string_to_spv("gelu_mul_f16",    "unary.comp",      {{"A_TYPE", "float16_t"},   {"B_TYPE", "float16_t"}, {"D_TYPE", "float16_t"}, {"FLOAT_TYPE", "float"}, {"OP", "op_gelu"},     {"UNARY_MUL_FUSION", "1"}});
1012    string_to_spv("sigmoid_mul_f32", "unary.comp",      {{"A_TYPE", "float"},       {"B_TYPE", "float"},     {"D_TYPE", "float"},     {"FLOAT_TYPE", "float"}, {"OP", "op_sigmoid"},  {"UNARY_MUL_FUSION", "1"}});
1013    string_to_spv("sigmoid_mul_f16", "unary.comp",      {{"A_TYPE", "float16_t"},   {"B_TYPE", "float16_t"}, {"D_TYPE", "float16_t"}, {"FLOAT_TYPE", "float"}, {"OP", "op_sigmoid"},  {"UNARY_MUL_FUSION", "1"}});
1014    string_to_spv("silu_mul_f32",    "unary.comp",      {{"A_TYPE", "float"},       {"B_TYPE", "float"},     {"D_TYPE", "float"},     {"FLOAT_TYPE", "float"}, {"OP", "op_silu"},     {"UNARY_MUL_FUSION", "1"}});
1015    string_to_spv("silu_mul_f16",    "unary.comp",      {{"A_TYPE", "float16_t"},   {"B_TYPE", "float16_t"}, {"D_TYPE", "float16_t"}, {"FLOAT_TYPE", "float"}, {"OP", "op_silu"},     {"UNARY_MUL_FUSION", "1"}});
1016    string_to_spv("softplus_mul_f32","unary.comp",      {{"A_TYPE", "float"},       {"B_TYPE", "float"},     {"D_TYPE", "float"},     {"FLOAT_TYPE", "float"}, {"OP", "op_softplus"}, {"UNARY_MUL_FUSION", "1"}});
1017    string_to_spv("softplus_mul_f16","unary.comp",      {{"A_TYPE", "float16_t"},   {"B_TYPE", "float16_t"}, {"D_TYPE", "float16_t"}, {"FLOAT_TYPE", "float"}, {"OP", "op_softplus"}, {"UNARY_MUL_FUSION", "1"}});
1018
1019    string_to_spv("add1_f16_f16",   "add1.comp",        {{"A_TYPE", "float16_t"},   {"B_TYPE", "float16_t"}, {"D_TYPE", "float16_t"}, {"FLOAT_TYPE", "float"}});
1020    string_to_spv("add1_f16_f32",   "add1.comp",        {{"A_TYPE", "float16_t"},   {"B_TYPE", "float"}, {"D_TYPE", "float16_t"}, {"FLOAT_TYPE", "float"}});
1021    string_to_spv("add1_f32_f32",   "add1.comp",        {{"A_TYPE", "float"},       {"B_TYPE", "float"}, {"D_TYPE", "float"}, {"FLOAT_TYPE", "float"}});
1022    string_to_spv("arange_f32",     "arange.comp",      {{"A_TYPE", "float"},       {"D_TYPE", "float"}, {"FLOAT_TYPE", "float"}});
1023    string_to_spv("fill_f32",       "fill.comp",        {{"D_TYPE", "float"},       {"FLOAT_TYPE", "float"}});
1024    string_to_spv("fill_f16",       "fill.comp",        {{"D_TYPE", "float16_t"},   {"FLOAT_TYPE", "float"}});
1025    string_to_spv("step_f16",       "unary.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}, {"OP", "op_step"}});
1026    string_to_spv("step_f32",       "unary.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"},     {"OP", "op_step"}});
1027    string_to_spv("round_f16",      "unary.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}, {"OP", "op_round"}});
1028    string_to_spv("round_f32",      "unary.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"},     {"OP", "op_round"}});
1029    string_to_spv("ceil_f16",       "unary.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}, {"OP", "op_ceil"}});
1030    string_to_spv("ceil_f32",       "unary.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"},     {"OP", "op_ceil"}});
1031    string_to_spv("floor_f16",      "unary.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}, {"OP", "op_floor"}});
1032    string_to_spv("floor_f32",      "unary.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"},     {"OP", "op_floor"}});
1033    string_to_spv("trunc_f16",      "unary.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}, {"OP", "op_trunc"}});
1034    string_to_spv("trunc_f32",      "unary.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"},     {"OP", "op_trunc"}});
1035
1036    string_to_spv("geglu_f16",      "geglu.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}});
1037    string_to_spv("geglu_f32",      "geglu.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"}});
1038    string_to_spv("reglu_f16",      "reglu.comp",       {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}});
1039    string_to_spv("reglu_f32",      "reglu.comp",       {{"A_TYPE", "float"},       {"D_TYPE", "float"}});
1040    string_to_spv("swiglu_f16",     "swiglu.comp",      {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}});
1041    string_to_spv("swiglu_f32",     "swiglu.comp",      {{"A_TYPE", "float"},       {"D_TYPE", "float"}});
1042    string_to_spv("swiglu_oai_f16", "swiglu_oai.comp",  {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}});
1043    string_to_spv("swiglu_oai_f32", "swiglu_oai.comp",  {{"A_TYPE", "float"},       {"D_TYPE", "float"}});
1044    string_to_spv("swiglu_clamp_f16", "swiglu_clamp.comp", {{"A_TYPE", "float16_t"}, {"D_TYPE", "float16_t"}});
1045    string_to_spv("swiglu_clamp_f32", "swiglu_clamp.comp", {{"A_TYPE", "float"},     {"D_TYPE", "float"}});
1046    string_to_spv("geglu_erf_f16",  "geglu_erf.comp",   {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}});
1047    string_to_spv("geglu_erf_f32",  "geglu_erf.comp",   {{"A_TYPE", "float"},       {"D_TYPE", "float"}});
1048    string_to_spv("geglu_quick_f16","geglu_quick.comp", {{"A_TYPE", "float16_t"},   {"D_TYPE", "float16_t"}});
1049    string_to_spv("geglu_quick_f32","geglu_quick.comp", {{"A_TYPE", "float"},       {"D_TYPE", "float"}});
1050
1051    string_to_spv("silu_back_f32",  "silu_back.comp",   {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}});
1052
1053    string_to_spv("diag_mask_inf_f32", "diag_mask_inf.comp", {{"A_TYPE", "float"}, {"D_TYPE", "float"}});
1054
1055    string_to_spv("soft_max_f32", "soft_max.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}}));
1056    string_to_spv("soft_max_f32_f16", "soft_max.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float16_t"}, {"D_TYPE", "float"}}));
1057    string_to_spv("soft_max_back_f32", "soft_max_back.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}}));
1058
1059    string_to_spv("soft_max_large1_f32", "soft_max_large1.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}}));
1060    string_to_spv("soft_max_large2_f32", "soft_max_large2.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}}));
1061    string_to_spv("soft_max_large3_f32", "soft_max_large3.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}}));
1062    string_to_spv("soft_max_large1_f32_f16", "soft_max_large1.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float16_t"}, {"D_TYPE", "float"}}));
1063    string_to_spv("soft_max_large2_f32_f16", "soft_max_large2.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float16_t"}, {"D_TYPE", "float"}}));
1064    string_to_spv("soft_max_large3_f32_f16", "soft_max_large3.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float16_t"}, {"D_TYPE", "float"}}));
1065
1066    string_to_spv("rope_norm_f32", "rope_norm.comp", {{"A_TYPE", "float"}, {"ROPE_D_TYPE", "float"}});
1067    string_to_spv("rope_norm_f16", "rope_norm.comp", {{"A_TYPE", "float16_t"}, {"ROPE_D_TYPE", "float16_t"}});
1068    string_to_spv("rope_norm_f32_f16", "rope_norm.comp", {{"A_TYPE", "float"}, {"ROPE_D_TYPE", "float16_t"}});
1069
1070    string_to_spv("rope_neox_f32", "rope_neox.comp", {{"A_TYPE", "float"}, {"ROPE_D_TYPE", "float"}});
1071    string_to_spv("rope_neox_f16", "rope_neox.comp", {{"A_TYPE", "float16_t"}, {"ROPE_D_TYPE", "float16_t"}});
1072    string_to_spv("rope_neox_f32_f16", "rope_neox.comp", {{"A_TYPE", "float"}, {"ROPE_D_TYPE", "float16_t"}});
1073
1074    string_to_spv("rope_multi_f32", "rope_multi.comp", {{"A_TYPE", "float"}, {"ROPE_D_TYPE", "float"}});
1075    string_to_spv("rope_multi_f16", "rope_multi.comp", {{"A_TYPE", "float16_t"}, {"ROPE_D_TYPE", "float16_t"}});
1076    string_to_spv("rope_multi_f32_f16", "rope_multi.comp", {{"A_TYPE", "float"}, {"ROPE_D_TYPE", "float16_t"}});
1077
1078    string_to_spv("rope_vision_f32", "rope_vision.comp", {{"A_TYPE", "float"}, {"ROPE_D_TYPE", "float"}});
1079    string_to_spv("rope_vision_f16", "rope_vision.comp", {{"A_TYPE", "float16_t"}, {"ROPE_D_TYPE", "float16_t"}});
1080
1081    string_to_spv("argsort_f32", "argsort.comp", {{"A_TYPE", "float"}});
1082    string_to_spv("argsort_large_f32", "argsort_large.comp", {{"A_TYPE", "float"}});
1083
1084    string_to_spv("topk_argsort_f32", "topk_argsort.comp", {{"A_TYPE", "float"}});
1085    string_to_spv("topk_nary_search_f32", "topk_nary_search.comp", {{"A_TYPE", "float"}});
1086    string_to_spv("topk_radix_select_f32", "topk_radix_select.comp", {{"A_TYPE", "float"}});
1087
1088    string_to_spv("argmax_f32", "argmax.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"D_TYPE", "int"}}));
1089    string_to_spv("sum_rows_f32", "sum_rows.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"D_TYPE", "float"}}));
1090    string_to_spv("cross_entropy_loss_f32", "cross_entropy_loss.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}}));
1091    string_to_spv("cross_entropy_loss_back_f32", "cross_entropy_loss_back.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}}));
1092    string_to_spv("fwht_f32", "fwht.comp", {});
1093    string_to_spv("fwht_shmem_f32", "fwht.comp", {{"FWHT_SHMEM", "1"}});
1094    string_to_spv("count_equal_i32", "count_equal.comp", merge_maps(base_dict, {{"A_TYPE", "int"}, {"B_TYPE", "int"}, {"D_TYPE", "int"}}));
1095    string_to_spv("dsv4_hc_comb_f32", "dsv4_hc_comb.comp", {});
1096    string_to_spv("dsv4_hc_pre_f32",  "dsv4_hc_pre.comp",  {});
1097    string_to_spv("dsv4_hc_post_f32", "dsv4_hc_post.comp", {});
1098    string_to_spv("cumsum_f32", "cumsum.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"D_TYPE", "float"}}));
1099    string_to_spv("cumsum_multipass1_f32", "cumsum_multipass1.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"D_TYPE", "float"}}));
1100    string_to_spv("cumsum_multipass2_f32", "cumsum_multipass2.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"D_TYPE", "float"}}));
1101
1102    string_to_spv("count_experts", "count_experts.comp", merge_maps(base_dict, {{"A_TYPE", "uint"}, {"D_TYPE", "uint"}}));
1103    string_to_spv("count_experts_subgroup", "count_experts.comp", merge_maps(base_dict, {{"A_TYPE", "uint"}, {"D_TYPE", "uint"}, {"USE_SUBGROUPS", "1"}}));
1104
1105    for (std::string dim_str : {"", "_3d"}) {
1106        for (bool bda : {false, true}) {
1107            std::string bda_str = bda ? "_bda" : "";
1108            std::string bda_def = bda ? "1" : "0";
1109            string_to_spv("im2col" + dim_str + "_f32" + bda_str, "im2col" + dim_str + ".comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"D_TYPE", "float"}, {"D_SIZE", "4"}, {"BDA", bda_def}}));
1110            string_to_spv("im2col" + dim_str + "_f32_f16" + bda_str, "im2col" + dim_str + ".comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"D_TYPE", "float16_t"}, {"D_SIZE", "2"}, {"BDA", bda_def}}));
1111        }
1112    }
1113
1114    string_to_spv("out_prod_f32", "out_prod.comp", {});
1115
1116    string_to_spv("timestep_embedding_f32", "timestep_embedding.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"D_TYPE", "float"}}));
1117
1118    string_to_spv("conv_transpose_1d_f32", "conv_transpose_1d.comp", {{"A_TYPE", "float"},  {"B_TYPE", "float"}, {"D_TYPE", "float"}});
1119    string_to_spv("col2im_1d_f32",  "col2im_1d.comp", {{"DATA_A_F32", "1"},  {"A_TYPE", "float"},     {"D_TYPE", "float"}});
1120    string_to_spv("col2im_1d_f16",  "col2im_1d.comp", {{"DATA_A_F16", "1"},  {"A_TYPE", "float16_t"}, {"D_TYPE", "float16_t"}});
1121    string_to_spv("col2im_1d_bf16", "col2im_1d.comp", {{"DATA_A_BF16", "1"}, {"A_TYPE", "uint16_t"},  {"D_TYPE", "uint16_t"}});
1122
1123    string_to_spv("snake_f32",  "snake.comp", {{"DATA_A_F32", "1"},  {"A_TYPE", "float"},     {"D_TYPE", "float"}});
1124    string_to_spv("snake_f16",  "snake.comp", {{"DATA_A_F16", "1"},  {"A_TYPE", "float16_t"}, {"D_TYPE", "float16_t"}});
1125    string_to_spv("snake_bf16", "snake.comp", {{"DATA_A_BF16", "1"}, {"DATA_D_BF16", "1"}, {"A_TYPE", "uint16_t"},  {"D_TYPE", "uint16_t"}});
1126
1127    string_to_spv("pool1d_f32", "pool1d.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"D_TYPE", "float"}}));
1128    string_to_spv("pool2d_f32", "pool2d.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"D_TYPE", "float"}}));
1129
1130    string_to_spv("rwkv_wkv6_f32", "wkv6.comp", merge_maps(base_dict, {{"A_TYPE", "float"}}));
1131
1132    string_to_spv("gated_linear_attn_f32", "gla.comp", merge_maps(base_dict, {{"A_TYPE", "float"}}));
1133
1134    // Compile IQ4_NL support in so its shared LUT is available when K uses it.
1135    // K quant type is selected at runtime via the FaTypeK spec constant.
1136    std::map<std::string, std::string> li_dict = {{"FLOAT_TYPE", "float"}, {"FLOAT_TYPEV4", "vec4"}, {"DATA_A_IQ4_NL", "1"}};
1137    string_to_spv("lightning_indexer_f32", "lightning_indexer.comp", li_dict);
1138    string_to_spv("lightning_indexer_subgroup_f32", "lightning_indexer.comp", merge_maps(li_dict, {{"USE_SUBGROUP_ADD", "1"}}));
1139
1140    string_to_spv("rwkv_wkv7_f32", "wkv7.comp", merge_maps(base_dict, {{"A_TYPE", "float"}}));
1141
1142    string_to_spv("gated_delta_net_f32", "gated_delta_net.comp", merge_maps(base_dict, {{"FLOAT_TYPE", "float"}, {"USE_SUBGROUP_ADD", "1"}, {"USE_SUBGROUP_CLUSTERED", "1"}}));
1143    string_to_spv("gated_delta_net_f32_nocluster", "gated_delta_net.comp", merge_maps(base_dict, {{"FLOAT_TYPE", "float"}, {"USE_SUBGROUP_ADD", "1"}, {"USE_SUBGROUP_CLUSTERED", "0"}}));
1144    string_to_spv("gated_delta_net_f32_shmem", "gated_delta_net.comp", merge_maps(base_dict, {{"FLOAT_TYPE", "float"}, {"USE_SUBGROUP_ADD", "0"}, {"USE_SUBGROUP_CLUSTERED", "0"}}));
1145
1146    string_to_spv("opt_step_adamw_f32", "opt_step_adamw.comp", merge_maps(base_dict, {{"A_TYPE", "float"}}));
1147    string_to_spv("opt_step_sgd_f32", "opt_step_sgd.comp", merge_maps(base_dict, {{"A_TYPE", "float"}}));
1148
1149    string_to_spv("solve_tri_f32", "solve_tri.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}}));
1150
1151    for (auto transpose : {false, true}) {
1152        for (auto unroll : {false, true}) {
1153            for (auto a_f16 : {false, true}) {
1154                std::map<std::string, std::string> defines = {
1155                    {"A_TYPE", a_f16 ? "float16_t" : "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"},
1156                    {"USE_COLLECTIVES", "1"}, {"UNROLL", unroll ? "[[unroll]]" : ""},
1157                };
1158                if (transpose) defines["TRANSPOSE"] = "1";
1159                std::string name = std::string(transpose ? "conv_transpose_2d": "conv2d")
1160                    + (a_f16 ? "_f16" : "") + "_f32";
1161                string_to_spv(name + (unroll ? "_unroll" : ""), "conv2d_mm.comp", defines);
1162#if defined(GGML_VULKAN_COOPMAT2_GLSLC_SUPPORT)
1163                if (unroll) {
1164                    auto cm2_defines = defines;
1165                    cm2_defines["COOPMAT2"] = "1";
1166                    string_to_spv(name, "conv2d_mm.comp", cm2_defines, true, false, true);
1167                }
1168#endif
1169#if defined(GGML_VULKAN_COOPMAT_GLSLC_SUPPORT)
1170                if (unroll) {
1171                    auto cm1_defines = defines;
1172                    cm1_defines["COOPMAT"] = "1";
1173                    string_to_spv(name, "conv2d_mm.comp", cm1_defines, true, true, false);
1174                }
1175#endif
1176            }
1177        }
1178    }
1179
1180    for (auto unroll : {false, true}) {
1181        for (auto a_f16 : {false, true}) {
1182            std::map<std::string, std::string> defines = {
1183                {"A_TYPE", a_f16 ? "float16_t" : "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"},
1184                {"UNROLL", unroll ? "[[unroll]]" : ""},
1185            };
1186            std::string name = std::string("conv3d") + (a_f16 ? "_f16" : "") + "_f32";
1187            string_to_spv(name + (unroll ? "_unroll" : ""), "conv3d_mm.comp", defines);
1188#if defined(GGML_VULKAN_COOPMAT2_GLSLC_SUPPORT)
1189            if (unroll) {
1190                auto cm2_defines = defines;
1191                cm2_defines["COOPMAT2"] = "1";
1192                string_to_spv(name, "conv3d_mm.comp", cm2_defines, true, false, true);
1193            }
1194#endif
1195#if defined(GGML_VULKAN_COOPMAT_GLSLC_SUPPORT)
1196            if (unroll) {
1197                auto cm1_defines = defines;
1198                cm1_defines["COOPMAT"] = "1";
1199                string_to_spv(name, "conv3d_mm.comp", cm1_defines, true, true, false);
1200            }
1201#endif
1202        }
1203    }
1204
1205    string_to_spv("conv2d_dw_whcn_f32", "conv2d_dw.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}, {"WHCN", "1"}}));
1206    string_to_spv("conv2d_dw_cwhn_f32", "conv2d_dw.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}, {"CWHN", "1"}}));
1207    string_to_spv("conv2d_dw_whcn_f16_f32", "conv2d_dw.comp", merge_maps(base_dict, {{"A_TYPE", "float16_t"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}, {"WHCN", "1"}}));
1208    string_to_spv("conv2d_dw_cwhn_f16_f32", "conv2d_dw.comp", merge_maps(base_dict, {{"A_TYPE", "float16_t"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}, {"CWHN", "1"}}));
1209
1210    string_to_spv("roll_f32", "roll.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"D_TYPE", "float"}}));
1211
1212    string_to_spv("add_id_f32", "add_id.comp", merge_maps(base_dict, {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}}));
1213
1214    string_to_spv("multi_add_f32", "multi_add.comp", {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}, {"FLOAT_TYPE", "float"}, {"ADD_RMS" , "0"}});
1215    string_to_spv("multi_add_rms_f32", "multi_add.comp", {{"A_TYPE", "float"}, {"B_TYPE", "float"}, {"D_TYPE", "float"}, {"FLOAT_TYPE", "float"}, {"ADD_RMS" , "1"}});
1216
1217    string_to_spv("ssm_scan_f32",          "ssm_scan.comp", {{"A_TYPE", "float"}});
1218    string_to_spv("ssm_scan_subgroup_f32", "ssm_scan.comp", {{"A_TYPE", "float"}, {"USE_SUBGROUP_ADD", "1"}});
1219
1220    string_to_spv("ssm_conv_f32", "ssm_conv.comp", {{"A_TYPE", "float"}});
1221
1222    string_to_spv("topk_moe_f32", "topk_moe.comp", {});
1223
1224    for (auto &c : compiles) {
1225        c.wait();
1226    }
1227}
1228
1229void write_output_files() {
1230    std::stringstream hdr = make_generic_stringstream();
1231    std::stringstream src = make_generic_stringstream();
1232
1233    hdr << "#include <cstdint>\n\n";
1234    src << "#include \"" << basename(target_hpp) << "\"\n\n";
1235
1236    std::sort(shader_fnames.begin(), shader_fnames.end());
1237    for (const auto& pair : shader_fnames) {
1238        const std::string& name = pair.first;
1239        #ifdef _WIN32
1240            std::string path = pair.second;
1241            std::replace(path.begin(), path.end(), '/', '\\' );
1242        #else
1243            const std::string& path = pair.second;
1244        #endif
1245
1246        hdr << "extern const uint64_t " << name << "_len;\n";
1247        hdr << "extern const unsigned char " << name << "_data[];\n\n";
1248
1249        if (input_filepath != "") {
1250            std::string data = read_binary_file(path);
1251            if (data.empty()) {
1252                continue;
1253            }
1254
1255            src << "const uint64_t " << name << "_len = " << data.size() << ";\n";
1256            src << "const unsigned char " << name << "_data[" << data.size() << "] = {\n" << std::hex;
1257            auto bytes = reinterpret_cast<const uint8_t*>(data.data());
1258            for (size_t i = 0; i < data.size(); ++i) {
1259                src << "0x" << static_cast<int>(bytes[i]) << ",";
1260                if ((i + 1) % 12 == 0) src << "\n";
1261            }
1262            src << std::dec << "\n};\n\n";
1263        }
1264    }
1265
1266    std::string suffixes[2] = {"_f32", "_f16"};
1267    for (std::string op : {"add", "sub", "mul", "div", "add_rms"}) {
1268        hdr << "extern const void * " << op << "_data[2][2][2];\n";
1269        hdr << "extern const uint64_t " << op << "_len[2][2][2];\n";
1270
1271        std::string op_file = op == "add_rms" ? "add.comp" : std::string(op) + ".comp";
1272        if (basename(input_filepath) != op_file) {
1273            continue;
1274        }
1275        std::stringstream data = make_generic_stringstream();
1276        std::stringstream len  = make_generic_stringstream();
1277        data << "const void * " << op << "_data[2][2][2] = ";
1278        len  << "const uint64_t " << op << "_len[2][2][2] = ";
1279        for (uint32_t t0 = 0; t0 < 2; ++t0) {
1280            if (t0 == 0) {
1281                data << "{";
1282                len  << "{";
1283            }
1284            for (uint32_t t1 = 0; t1 < 2; ++t1) {
1285                if (t1 == 0) {
1286                    data << "{";
1287                    len  << "{";
1288                }
1289                for (uint32_t t2 = 0; t2 < 2; ++t2) {
1290                    if (t2 == 0) {
1291                        data << "{";
1292                        len  << "{";
1293                    }
1294                    data << op << suffixes[t0] << suffixes[t1] << suffixes[t2];
1295                    len  << op << suffixes[t0] << suffixes[t1] << suffixes[t2];
1296                    data << "_data,";
1297                    len  << "_len,";
1298                    if (t2 == 1) {
1299                        data << "}, ";
1300                        len  << "}, ";
1301                    }
1302                }
1303                if (t1 == 1) {
1304                    data << "}, ";
1305                    len  << "}, ";
1306                }
1307            }
1308            if (t0 == 1) {
1309                data << "};\n";
1310                len  << "};\n";
1311            }
1312        }
1313        src << data.str();
1314        src << len.str();
1315    }
1316
1317    std::vector<std::string> btypes = {"f16", "f32"};
1318
1319#if defined(GGML_VULKAN_INTEGER_DOT_GLSLC_SUPPORT)
1320    btypes.push_back("q8_1");
1321#endif
1322
1323    for (const std::string& btype : btypes) {
1324    for (const auto& tname : type_names) {
1325        if (btype == "q8_1" && !is_legacy_quant(tname) && tname != "mxfp4" && !is_k_quant(tname) && tname != "iq1_s" && tname != "iq1_m") {
1326            continue;
1327        }
1328        hdr << "extern const void * arr_dmmv_"   << tname << "_" << btype << "_f32_data[3];\n";
1329        hdr << "extern const uint64_t arr_dmmv_" << tname << "_" << btype << "_f32_len[3];\n";
1330        if (basename(input_filepath) == "mul_mat_vec.comp") {
1331            src << "const void * arr_dmmv_"   << tname << "_" << btype << "_f32_data[3] = {mul_mat_vec_" << tname << "_" << btype << "_f32_data, mul_mat_vec_" << tname << "_" << btype << "_f32_subgroup_data, mul_mat_vec_" << tname << "_" << btype << "_f32_subgroup_no_shmem_data};\n";
1332            src << "const uint64_t arr_dmmv_" << tname << "_" << btype << "_f32_len[3] =  {mul_mat_vec_" << tname << "_" << btype << "_f32_len,  mul_mat_vec_" << tname << "_" << btype << "_f32_subgroup_len, mul_mat_vec_"  << tname << "_" << btype << "_f32_subgroup_no_shmem_len};\n";
1333        }
1334
1335        if (btype == "f16") {
1336            continue;
1337        }
1338        hdr << "extern const void * arr_dmmv_id_"   << tname << "_" << btype << "_f32_data[3];\n";
1339        hdr << "extern const uint64_t arr_dmmv_id_" << tname << "_" << btype << "_f32_len[3];\n";
1340        if (basename(input_filepath) == "mul_mat_vec.comp") {
1341            src << "const void * arr_dmmv_id_"   << tname << "_" << btype << "_f32_data[3] = {mul_mat_vec_id_" << tname << "_" << btype << "_f32_data, mul_mat_vec_id_" << tname << "_" << btype << "_f32_subgroup_data, mul_mat_vec_id_" << tname << "_" << btype << "_f32_subgroup_no_shmem_data};\n";
1342            src << "const uint64_t arr_dmmv_id_" << tname << "_" << btype << "_f32_len[3] =  {mul_mat_vec_id_" << tname << "_" << btype << "_f32_len,  mul_mat_vec_id_" << tname << "_" << btype << "_f32_subgroup_len, mul_mat_vec_id_"  << tname << "_" << btype << "_f32_subgroup_no_shmem_len};\n";
1343        }
1344    }
1345    }
1346
1347#if defined(GGML_VULKAN_FLOAT_E2M1_GLSLC_SUPPORT) && defined(GGML_VULKAN_FLOAT_E4M3_GLSLC_SUPPORT)
1348    for (const std::string& btype : {"f16", "f32"}) {
1349    for (const std::string& tname : {"mxfp4", "nvfp4"}) {
1350        hdr << "extern const void * arr_dmmv_"   << tname << "_" << btype << "_f32_ocp_data[3];\n";
1351        hdr << "extern const uint64_t arr_dmmv_" << tname << "_" << btype << "_f32_ocp_len[3];\n";
1352        if (basename(input_filepath) == "mul_mat_vec.comp") {
1353            src << "const void * arr_dmmv_"   << tname << "_" << btype << "_f32_ocp_data[3] = {mul_mat_vec_" << tname << "_" << btype << "_f32_ocp_data, mul_mat_vec_" << tname << "_" << btype << "_f32_ocp_subgroup_data, mul_mat_vec_" << tname << "_" << btype << "_f32_ocp_subgroup_no_shmem_data};\n";
1354            src << "const uint64_t arr_dmmv_" << tname << "_" << btype << "_f32_ocp_len[3] =  {mul_mat_vec_" << tname << "_" << btype << "_f32_ocp_len,  mul_mat_vec_" << tname << "_" << btype << "_f32_ocp_subgroup_len, mul_mat_vec_"  << tname << "_" << btype << "_f32_ocp_subgroup_no_shmem_len};\n";
1355        }
1356    }
1357    }
1358    for (const std::string& tname : {"mxfp4", "nvfp4"}) {
1359        hdr << "extern const void * arr_dmmv_id_"   << tname << "_f32_f32_ocp_data[3];\n";
1360        hdr << "extern const uint64_t arr_dmmv_id_" << tname << "_f32_f32_ocp_len[3];\n";
1361        if (basename(input_filepath) == "mul_mat_vec.comp") {
1362            src << "const void * arr_dmmv_id_"   << tname << "_f32_f32_ocp_data[3] = {mul_mat_vec_id_" << tname << "_f32_f32_ocp_data, mul_mat_vec_id_" << tname << "_f32_f32_ocp_subgroup_data, mul_mat_vec_id_" << tname << "_f32_f32_ocp_subgroup_no_shmem_data};\n";
1363            src << "const uint64_t arr_dmmv_id_" << tname << "_f32_f32_ocp_len[3] =  {mul_mat_vec_id_" << tname << "_f32_f32_ocp_len,  mul_mat_vec_id_" << tname << "_f32_f32_ocp_subgroup_len, mul_mat_vec_id_"  << tname << "_f32_f32_ocp_subgroup_no_shmem_len};\n";
1364        }
1365    }
1366#endif
1367
1368    if (input_filepath == "") {
1369        write_file_if_changed(target_hpp, hdr.str());
1370    }
1371    if (target_cpp != "") {
1372        write_binary_file(target_cpp, src.str());
1373    }
1374}
1375
1376} // namespace
1377
1378int main(int argc, char** argv) {
1379    std::map<std::string, std::string> args;
1380    for (int i = 1; i < argc; ++i) {
1381        std::string arg = argv[i];
1382        if (arg.rfind("--", 0) == 0) {
1383            if (i + 1 < argc && argv[i + 1][0] != '-') {
1384                args[arg] = argv[i + 1];
1385                ++i;
1386            } else {
1387                args[arg] = "";
1388            }
1389        }
1390    }
1391
1392    if (args.find("--glslc") != args.end()) {
1393        GLSLC = args["--glslc"]; // Path to glslc
1394    }
1395    if (args.find("--source") != args.end()) {
1396        input_filepath = args["--source"]; // The shader source file to compile
1397    }
1398    if (args.find("--output-dir") != args.end()) {
1399        output_dir = args["--output-dir"]; // Directory for containing SPIR-V output
1400    }
1401    if (args.find("--target-hpp") != args.end()) {
1402        target_hpp = args["--target-hpp"]; // Path to generated header file
1403    }
1404    if (args.find("--target-cpp") != args.end()) {
1405        target_cpp = args["--target-cpp"]; // Path to generated cpp file
1406    }
1407
1408    if (!directory_exists(output_dir)) {
1409        if (!create_directory(output_dir)) {
1410            std::cerr << "Error creating output directory: " << output_dir << "\n";
1411            return EXIT_FAILURE;
1412        }
1413    }
1414
1415    process_shaders();
1416
1417    if (compile_failed) {
1418        std::cerr << "vulkan-shaders-gen: one or more shaders failed to compile" << std::endl;
1419        return EXIT_FAILURE;
1420    }
1421
1422    write_output_files();
1423
1424    return EXIT_SUCCESS;
1425}