char-fork/llama.cpp

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

Reese Levineggml-webgpu: FlashAttention refactor + standardize quantization support (#23834)e8c54893f

char/aliceai-t5-moe
25.8 KiB808 linesraw
1#ifndef PRE_WGSL_HPP
2#define PRE_WGSL_HPP
3
4#include <cctype>
5#include <fstream>
6#include <sstream>
7#include <stdexcept>
8#include <string>
9#include <string_view>
10#include <unordered_map>
11#include <unordered_set>
12#include <vector>
13
14namespace pre_wgsl {
15
16//==============================================================
17// Options
18//==============================================================
19struct Options {
20    std::string              include_path = ".";
21    std::vector<std::string> macros;
22};
23
24//==============================================================
25// Utility: trim
26//==============================================================
27static std::string trim(const std::string & s) {
28    size_t a = 0;
29    while (a < s.size() && std::isspace((unsigned char) s[a])) {
30        a++;
31    }
32    size_t b = s.size();
33    while (b > a && std::isspace((unsigned char) s[b - 1])) {
34        b--;
35    }
36    return s.substr(a, b - a);
37}
38
39static std::string trim_value(std::istream & is) {
40    std::ostringstream ss;
41    ss << is.rdbuf();
42    return trim(ss.str());
43}
44
45static bool isIdentChar(char c) {
46    return std::isalnum(static_cast<unsigned char>(c)) || c == '_';
47}
48
49static bool endsWithContinuation(const std::string & line) {
50    size_t i = line.size();
51    while (i > 0 && std::isspace((unsigned char) line[i - 1])) {
52        i--;
53    }
54    return i > 0 && line[i - 1] == '\\';
55}
56
57static void stripContinuation(std::string & line) {
58    size_t i = line.size();
59    while (i > 0 && std::isspace((unsigned char) line[i - 1])) {
60        i--;
61    }
62    if (i > 0 && line[i - 1] == '\\') {
63        line.erase(i - 1);
64    }
65}
66
67static std::string expandMacrosRecursiveInternal(const std::string &                                  line,
68                                                 const std::unordered_map<std::string, std::string> & macros,
69                                                 std::unordered_set<std::string> &                    visiting);
70
71static std::string expandMacroValue(const std::string &                                  name,
72                                    const std::unordered_map<std::string, std::string> & macros,
73                                    std::unordered_set<std::string> &                    visiting) {
74    if (visiting.count(name)) {
75        throw std::runtime_error("Recursive macro: " + name);
76    }
77    visiting.insert(name);
78
79    auto it = macros.find(name);
80    if (it == macros.end()) {
81        visiting.erase(name);
82        return name;
83    }
84
85    const std::string & value = it->second;
86    if (value.empty()) {
87        visiting.erase(name);
88        return "";
89    }
90
91    std::string expanded = expandMacrosRecursiveInternal(value, macros, visiting);
92    visiting.erase(name);
93    return expanded;
94}
95
96static std::string expandMacrosRecursiveInternal(const std::string &                                  line,
97                                                 const std::unordered_map<std::string, std::string> & macros,
98                                                 std::unordered_set<std::string> &                    visiting) {
99    std::string result;
100    result.reserve(line.size());
101
102    size_t i = 0;
103    while (i < line.size()) {
104        if (isIdentChar(line[i])) {
105            size_t start = i;
106            while (i < line.size() && isIdentChar(line[i])) {
107                i++;
108            }
109            std::string token = line.substr(start, i - start);
110
111            auto it = macros.find(token);
112            if (it != macros.end()) {
113                result += expandMacroValue(token, macros, visiting);
114            } else {
115                result += token;
116            }
117        } else {
118            result += line[i];
119            i++;
120        }
121    }
122
123    return result;
124}
125
126static std::string expandMacrosRecursive(const std::string &                                  line,
127                                         const std::unordered_map<std::string, std::string> & macros) {
128    std::unordered_set<std::string> visiting;
129    return expandMacrosRecursiveInternal(line, macros, visiting);
130}
131
132//==============================================================
133// Tokenizer for expressions in #if/#elif
134//==============================================================
135class ExprLexer {
136  public:
137    enum Kind { END, IDENT, NUMBER, OP, LPAREN, RPAREN };
138
139    struct Tok {
140        Kind        kind;
141        std::string text;
142    };
143
144    explicit ExprLexer(std::string_view sv) : src(sv), pos(0) {}
145
146    Tok next() {
147        skipWS();
148        if (pos >= src.size()) {
149            return { END, "" };
150        }
151
152        char c = src[pos];
153
154        // number
155        if (std::isdigit((unsigned char) c)) {
156            size_t start = pos;
157            while (pos < src.size() && std::isdigit((unsigned char) src[pos])) {
158                pos++;
159            }
160            return { NUMBER, std::string(src.substr(start, pos - start)) };
161        }
162
163        // identifier
164        if (std::isalpha((unsigned char) c) || c == '_') {
165            size_t start = pos;
166            while (pos < src.size() && (std::isalnum((unsigned char) src[pos]) || src[pos] == '_')) {
167                pos++;
168            }
169            return { IDENT, std::string(src.substr(start, pos - start)) };
170        }
171
172        if (c == '(') {
173            pos++;
174            return { LPAREN, "(" };
175        }
176        if (c == ')') {
177            pos++;
178            return { RPAREN, ")" };
179        }
180
181        // multi-char operators
182        static const char * two_ops[] = { "==", "!=", "<=", ">=", "&&", "||", "<<", ">>" };
183        for (auto op : two_ops) {
184            if (src.substr(pos, 2) == op) {
185                pos += 2;
186                return { OP, std::string(op) };
187            }
188        }
189
190        // single-char operators
191        if (std::string("+-*/%<>!").find(c) != std::string::npos) {
192            pos++;
193            return { OP, std::string(1, c) };
194        }
195
196        // unexpected
197        pos++;
198        return { END, "" };
199    }
200
201  private:
202    std::string_view src;
203    size_t           pos;
204
205    void skipWS() {
206        while (pos < src.size() && std::isspace((unsigned char) src[pos])) {
207            pos++;
208        }
209    }
210};
211
212//==============================================================
213// Expression Parser (recursive descent)
214//==============================================================
215class ExprParser {
216  public:
217    ExprParser(std::string_view                                     expr,
218               const std::unordered_map<std::string, std::string> & macros,
219               std::unordered_set<std::string> &                    visiting) :
220        lex(expr),
221        macros(macros),
222        visiting(visiting) {
223        advance();
224    }
225
226    int parse() { return parseLogicalOr(); }
227
228  private:
229    ExprLexer                                            lex;
230    ExprLexer::Tok                                       tok;
231    const std::unordered_map<std::string, std::string> & macros;
232    std::unordered_set<std::string> &                    visiting;
233
234    void advance() { tok = lex.next(); }
235
236    bool acceptOp(const std::string & s) {
237        if (tok.kind == ExprLexer::OP && tok.text == s) {
238            advance();
239            return true;
240        }
241        return false;
242    }
243
244    bool acceptKind(ExprLexer::Kind k) {
245        if (tok.kind == k) {
246            advance();
247            return true;
248        }
249        return false;
250    }
251
252    int parseLogicalOr() {
253        int v = parseLogicalAnd();
254        while (acceptOp("||")) {
255            int rhs = parseLogicalAnd();
256            v       = (v || rhs);
257        }
258        return v;
259    }
260
261    int parseLogicalAnd() {
262        int v = parseEquality();
263        while (acceptOp("&&")) {
264            int rhs = parseEquality();
265            v       = (v && rhs);
266        }
267        return v;
268    }
269
270    int parseEquality() {
271        int v = parseRelational();
272        for (;;) {
273            if (acceptOp("==")) {
274                int rhs = parseRelational();
275                v       = (v == rhs);
276            } else if (acceptOp("!=")) {
277                int rhs = parseRelational();
278                v       = (v != rhs);
279            } else {
280                break;
281            }
282        }
283        return v;
284    }
285
286    int parseRelational() {
287        int v = parseShift();
288        for (;;) {
289            if (acceptOp("<")) {
290                int rhs = parseShift();
291                v       = (v < rhs);
292            } else if (acceptOp(">")) {
293                int rhs = parseShift();
294                v       = (v > rhs);
295            } else if (acceptOp("<=")) {
296                int rhs = parseShift();
297                v       = (v <= rhs);
298            } else if (acceptOp(">=")) {
299                int rhs = parseShift();
300                v       = (v >= rhs);
301            } else {
302                break;
303            }
304        }
305        return v;
306    }
307
308    int parseShift() {
309        int v = parseAdd();
310        for (;;) {
311            if (acceptOp("<<")) {
312                int rhs = parseAdd();
313                v       = (v << rhs);
314            } else if (acceptOp(">>")) {
315                int rhs = parseAdd();
316                v       = (v >> rhs);
317            } else {
318                break;
319            }
320        }
321        return v;
322    }
323
324    int parseAdd() {
325        int v = parseMult();
326        for (;;) {
327            if (acceptOp("+")) {
328                int rhs = parseMult();
329                v       = (v + rhs);
330            } else if (acceptOp("-")) {
331                int rhs = parseMult();
332                v       = (v - rhs);
333            } else {
334                break;
335            }
336        }
337        return v;
338    }
339
340    int parseMult() {
341        int v = parseUnary();
342        for (;;) {
343            if (acceptOp("*")) {
344                int rhs = parseUnary();
345                v       = (v * rhs);
346            } else if (acceptOp("/")) {
347                int rhs = parseUnary();
348                v       = (rhs == 0 ? 0 : v / rhs);
349            } else if (acceptOp("%")) {
350                int rhs = parseUnary();
351                v       = (rhs == 0 ? 0 : v % rhs);
352            } else {
353                break;
354            }
355        }
356        return v;
357    }
358
359    int parseUnary() {
360        if (acceptOp("!")) {
361            return !parseUnary();
362        }
363        if (acceptOp("-")) {
364            return -parseUnary();
365        }
366        if (acceptOp("+")) {
367            return +parseUnary();
368        }
369        return parsePrimary();
370    }
371
372    int parsePrimary() {
373        // '(' expr ')'
374        if (acceptKind(ExprLexer::LPAREN)) {
375            int v = parse();
376            if (!acceptKind(ExprLexer::RPAREN)) {
377                throw std::runtime_error("missing ')'");
378            }
379            return v;
380        }
381
382        // number
383        if (tok.kind == ExprLexer::NUMBER) {
384            int v = std::stoi(tok.text);
385            advance();
386            return v;
387        }
388
389        // defined(identifier)
390        if (tok.kind == ExprLexer::IDENT && tok.text == "defined") {
391            advance();
392            if (acceptKind(ExprLexer::LPAREN)) {
393                if (tok.kind != ExprLexer::IDENT) {
394                    throw std::runtime_error("expected identifier in defined()");
395                }
396                std::string name = tok.text;
397                advance();
398                if (!acceptKind(ExprLexer::RPAREN)) {
399                    throw std::runtime_error("missing ) in defined()");
400                }
401                return macros.count(name) ? 1 : 0;
402            } else {
403                // defined NAME
404                if (tok.kind != ExprLexer::IDENT) {
405                    throw std::runtime_error("expected identifier in defined NAME");
406                }
407                std::string name = tok.text;
408                advance();
409                return macros.count(name) ? 1 : 0;
410            }
411        }
412
413        // identifier -> treat as integer, if defined use its value else 0
414        if (tok.kind == ExprLexer::IDENT) {
415            std::string name = tok.text;
416            advance();
417            auto it = macros.find(name);
418            if (it == macros.end()) {
419                return 0;
420            }
421            if (it->second.empty()) {
422                return 1;
423            }
424            return evalMacroExpression(name, it->second);
425        }
426
427        // unexpected
428        return 0;
429    }
430
431    int evalMacroExpression(const std::string & name, const std::string & value) {
432        if (visiting.count(name)) {
433            throw std::runtime_error("Recursive macro: " + name);
434        }
435
436        visiting.insert(name);
437        ExprParser ep(value, macros, visiting);
438        int        v = ep.parse();
439        visiting.erase(name);
440        return v;
441    }
442};
443
444//==============================================================
445// Preprocessor
446//==============================================================
447class Preprocessor {
448  public:
449    explicit Preprocessor(Options opts = {}) : opts_(std::move(opts)) {
450        // Treat empty include path as current directory
451        if (opts_.include_path.empty()) {
452            opts_.include_path = ".";
453        }
454        parseMacroDefinitions(opts_.macros);
455    }
456
457    std::string preprocess_file(const std::string & filename, const std::vector<std::string> & additional_macros = {}) {
458        std::unordered_map<std::string, std::string> macros;
459        std::unordered_set<std::string>              predefined;
460        std::unordered_set<std::string>              include_stack;
461        buildMacros(additional_macros, macros, predefined);
462
463        std::string result = processFile(filename, macros, predefined, include_stack, DirectiveMode::All);
464        return result;
465    }
466
467    std::string preprocess(const std::string & contents, const std::vector<std::string> & additional_macros = {}) {
468        std::unordered_map<std::string, std::string> macros;
469        std::unordered_set<std::string>              predefined;
470        std::unordered_set<std::string>              include_stack;
471        buildMacros(additional_macros, macros, predefined);
472
473        std::string result = processString(contents, macros, predefined, include_stack, DirectiveMode::All);
474        return result;
475    }
476
477    std::string preprocess_includes_file(const std::string & filename) {
478        std::unordered_map<std::string, std::string> macros;
479        std::unordered_set<std::string>              predefined;
480        std::unordered_set<std::string>              include_stack;
481        std::string result = processFile(filename, macros, predefined, include_stack, DirectiveMode::IncludesOnly);
482        return result;
483    }
484
485    std::string preprocess_includes(const std::string & contents) {
486        std::unordered_map<std::string, std::string> macros;
487        std::unordered_set<std::string>              predefined;
488        std::unordered_set<std::string>              include_stack;
489        std::string result = processString(contents, macros, predefined, include_stack, DirectiveMode::IncludesOnly);
490        return result;
491    }
492
493  private:
494    Options                                      opts_;
495    std::unordered_map<std::string, std::string> global_macros;
496
497    enum class DirectiveMode { All, IncludesOnly };
498
499    struct Cond {
500        bool parent_active;
501        bool active;
502        bool taken;
503    };
504
505    //----------------------------------------------------------
506    // Parse macro definitions into global_macros
507    //----------------------------------------------------------
508    void parseMacroDefinitions(const std::vector<std::string> & macro_defs) {
509        for (const auto & def : macro_defs) {
510            size_t eq_pos = def.find('=');
511            if (eq_pos != std::string::npos) {
512                // Format: NAME=VALUE
513                std::string name    = trim(def.substr(0, eq_pos));
514                std::string value   = trim(def.substr(eq_pos + 1));
515                global_macros[name] = value;
516            } else {
517                // Format: NAME
518                std::string name    = trim(def);
519                global_macros[name] = "";
520            }
521        }
522    }
523
524    //----------------------------------------------------------
525    // Build combined macro map and predefined set for a preprocessing operation
526    //----------------------------------------------------------
527    void buildMacros(const std::vector<std::string> &               additional_macros,
528                     std::unordered_map<std::string, std::string> & macros,
529                     std::unordered_set<std::string> &              predefined) {
530        macros = global_macros;
531        predefined.clear();
532
533        for (const auto & [name, value] : global_macros) {
534            predefined.insert(name);
535        }
536
537        for (const auto & def : additional_macros) {
538            size_t      eq_pos = def.find('=');
539            std::string name, value;
540            if (eq_pos != std::string::npos) {
541                name  = trim(def.substr(0, eq_pos));
542                value = trim(def.substr(eq_pos + 1));
543            } else {
544                name  = trim(def);
545                value = "";
546            }
547
548            // Add to macros map (will override global if same name)
549            macros[name] = value;
550            predefined.insert(name);
551        }
552    }
553
554    //----------------------------------------------------------
555    // Helpers
556    //----------------------------------------------------------
557    std::string loadFile(const std::string & fname) {
558        std::ifstream f(fname);
559        if (!f.is_open()) {
560            throw std::runtime_error("Could not open file: " + fname);
561        }
562        std::stringstream ss;
563        ss << f.rdbuf();
564        return ss.str();
565    }
566
567    bool condActive(const std::vector<Cond> & cond) const {
568        if (cond.empty()) {
569            return true;
570        }
571        return cond.back().active;
572    }
573
574    //----------------------------------------------------------
575    // Process a file
576    //----------------------------------------------------------
577    std::string processFile(const std::string &                            name,
578                            std::unordered_map<std::string, std::string> & macros,
579                            const std::unordered_set<std::string> &        predefined_macros,
580                            std::unordered_set<std::string> &              include_stack,
581                            DirectiveMode                                  mode) {
582        if (include_stack.count(name)) {
583            throw std::runtime_error("Recursive include: " + name);
584        }
585
586        include_stack.insert(name);
587        std::string shader_code = loadFile(name);
588        std::string out         = processString(shader_code, macros, predefined_macros, include_stack, mode);
589        include_stack.erase(name);
590        return out;
591    }
592
593    std::string processIncludeFile(const std::string &                            fname,
594                                   std::unordered_map<std::string, std::string> & macros,
595                                   const std::unordered_set<std::string> &        predefined_macros,
596                                   std::unordered_set<std::string> &              include_stack,
597                                   DirectiveMode                                  mode) {
598        std::string full_path = opts_.include_path + "/" + fname;
599        return processFile(full_path, macros, predefined_macros, include_stack, mode);
600    }
601
602    //----------------------------------------------------------
603    // Process text
604    //----------------------------------------------------------
605    std::string processString(const std::string &                            shader_code,
606                              std::unordered_map<std::string, std::string> & macros,
607                              const std::unordered_set<std::string> &        predefined_macros,
608                              std::unordered_set<std::string> &              include_stack,
609                              DirectiveMode                                  mode) {
610        std::vector<Cond>  cond;  // Conditional stack for this shader
611        std::stringstream  out;
612        std::istringstream in(shader_code);
613        std::string        line;
614
615        while (std::getline(in, line)) {
616            std::string logical = line;
617            std::string t       = trim(logical);
618            if (!t.empty() && t[0] == '#') {
619                while (endsWithContinuation(logical)) {
620                    stripContinuation(logical);
621                    if (!std::getline(in, line)) {
622                        break;
623                    }
624                    logical += "\n";
625                    logical += line;
626                }
627                t = trim(logical);
628            }
629
630            if (!t.empty() && t[0] == '#') {
631                bool handled = handleDirective(t, out, macros, predefined_macros, cond, include_stack, mode);
632                if (mode == DirectiveMode::IncludesOnly && !handled) {
633                    out << logical << "\n";
634                }
635            } else {
636                if (mode == DirectiveMode::IncludesOnly) {
637                    out << logical << "\n";
638                } else if (condActive(cond)) {
639                    // Expand macros in the line before outputting
640                    std::string expanded = expandMacrosRecursive(logical, macros);
641                    out << expanded << "\n";
642                }
643            }
644        }
645
646        if (mode == DirectiveMode::All && !cond.empty()) {
647            throw std::runtime_error("Unclosed #if directive");
648        }
649
650        return out.str();
651    }
652
653    //----------------------------------------------------------
654    // Directive handler
655    //----------------------------------------------------------
656    bool handleDirective(const std::string &                            t,
657                         std::stringstream &                            out,
658                         std::unordered_map<std::string, std::string> & macros,
659                         const std::unordered_set<std::string> &        predefined_macros,
660                         std::vector<Cond> &                            cond,
661                         std::unordered_set<std::string> &              include_stack,
662                         DirectiveMode                                  mode) {
663        // split into tokens
664        std::string        body = t.substr(1);
665        std::istringstream iss(body);
666        std::string        cmd;
667        iss >> cmd;
668
669        if (cmd == "include") {
670            if (mode == DirectiveMode::All && !condActive(cond)) {
671                return true;
672            }
673            std::string file;
674            iss >> file;
675            if (file.size() >= 2 && file.front() == '"' && file.back() == '"') {
676                file = file.substr(1, file.size() - 2);
677            }
678            out << processIncludeFile(file, macros, predefined_macros, include_stack, mode);
679            return true;
680        }
681
682        if (mode == DirectiveMode::IncludesOnly) {
683            return false;
684        }
685
686        if (cmd == "define") {
687            if (!condActive(cond)) {
688                return true;
689            }
690            std::string name;
691            iss >> name;
692            // Don't override predefined macros from options
693            if (predefined_macros.count(name)) {
694                return true;
695            }
696            std::string value = trim_value(iss);
697            macros[name]      = value;
698            return true;
699        }
700
701        if (cmd == "undef") {
702            if (!condActive(cond)) {
703                return true;
704            }
705            std::string name;
706            iss >> name;
707            // Don't undef predefined macros from options
708            if (predefined_macros.count(name)) {
709                return true;
710            }
711            macros.erase(name);
712            return true;
713        }
714
715        if (cmd == "ifdef") {
716            std::string name;
717            iss >> name;
718            bool p = condActive(cond);
719            bool v = macros.count(name);
720            cond.push_back({ p, p && v, p && v });
721            return true;
722        }
723
724        if (cmd == "ifndef") {
725            std::string name;
726            iss >> name;
727            bool p = condActive(cond);
728            bool v = !macros.count(name);
729            cond.push_back({ p, p && v, p && v });
730            return true;
731        }
732
733        if (cmd == "if") {
734            std::string expr = trim_value(iss);
735            bool        p    = condActive(cond);
736            bool        v    = false;
737            if (p) {
738                std::unordered_set<std::string> visiting;
739                ExprParser                      ep(expr, macros, visiting);
740                v = ep.parse() != 0;
741            }
742            cond.push_back({ p, p && v, p && v });
743            return true;
744        }
745
746        if (cmd == "elif") {
747            std::string expr = trim_value(iss);
748
749            if (cond.empty()) {
750                throw std::runtime_error("#elif without #if");
751            }
752
753            Cond & c = cond.back();
754            if (!c.parent_active) {
755                c.active = false;
756                return true;
757            }
758
759            if (c.taken) {
760                c.active = false;
761                return true;
762            }
763
764            std::unordered_set<std::string> visiting;
765            ExprParser                      ep(expr, macros, visiting);
766            bool                            v = ep.parse() != 0;
767            c.active                          = v;
768            if (v) {
769                c.taken = true;
770            }
771            return true;
772        }
773
774        if (cmd == "else") {
775            if (cond.empty()) {
776                throw std::runtime_error("#else without #if");
777            }
778
779            Cond & c = cond.back();
780            if (!c.parent_active) {
781                c.active = false;
782                return true;
783            }
784            if (c.taken) {
785                c.active = false;
786            } else {
787                c.active = true;
788                c.taken  = true;
789            }
790            return true;
791        }
792
793        if (cmd == "endif") {
794            if (cond.empty()) {
795                throw std::runtime_error("#endif without #if");
796            }
797            cond.pop_back();
798            return true;
799        }
800
801        // Unknown directive
802        throw std::runtime_error("Unknown directive: #" + cmd);
803    }
804};
805
806}  // namespace pre_wgsl
807
808#endif  // PRE_WGSL_HPP