skip to content
[readonly]
==> src/ptx_float_rules.c <==
1/* The rules, one awk program's worth: strip the comment, the leading blanks
2 * and braces, and a predicate guard; take the opcode up to the first blank or
3 * ';'; split it at '.'; then the rules by its first token. */
4#include "mesh/ptx_float_rules.h"
5
6#include <stdbool.h>
7#include <string.h>
8
9#define MAX_TOKENS 32
10
11static bool is_lower(char c) { return c >= 'a' && c <= 'z'; }
12static bool is_digit(char c) { return c >= '0' && c <= '9'; }
13static bool is_ident(char c) { return is_lower(c) || is_digit(c) || c == '_'; }
14
15static bool tok_is(const char *t, size_t n, const char *w) { return strlen(w) == n && memcmp(t, w, n) == 0; }
16static bool tok_in(const char *t, size_t n, const char *const *ws) {
17    for (; *ws; ws++)
18        if (tok_is(t, n, *ws)) return true;
19    return false;
20}
21
22static const char *const FLOATS[] = {"f16", "f16x2", "bf16", "bf16x2", "tf32", "e4m3", "e5m2", "e4m3x2", "e5m2x2", "f32", "f64", NULL};
23static const char *const NARROW[] = {"f16", "f16x2", "bf16", "bf16x2", "tf32", "e4m3", "e5m2", "e4m3x2", "e5m2x2", NULL};
24static const char *const ROUNDED[] = {"add", "sub", "mul", "div", "rcp", "sqrt", NULL};
25static const char *const FLUSHED[] = {"abs", "neg", "min", "max", "setp", "set", "slct", NULL};
26static const char *const PLAIN[] = {"selp", "copysign", "testp", "mov", "ld", "ldu", "st", NULL};
27
28const char *mesh_ptx_float_line(const char *line, size_t len) {
29    /* sub(/\/\/.*\/, "", line) */
30    for (size_t i = 0; i + 1 < len; i++)
31        if (line[i] == '/' && line[i + 1] == '/') { len = i; break; }
32    /* gsub(/^[ \t{]+/, "", line) */
33    size_t a = 0;
34    while (a < len && (line[a] == ' ' || line[a] == '\t' || line[a] == '{')) a++;
35    const char *s = line + a;
36    len -= a;
37    /* A predicate guard: @p, @!p, @%p1 ... followed by blanks. */
38    if (len >= 2 && s[0] == '@') {
39        size_t i = 1;
40        if (i < len && s[i] == '!') i++;
41        if (i < len && s[i] == '%') {
42            i++;
43            size_t id = i;
44            while (i < len && (is_lower(s[i]) || is_digit(s[i]) || s[i] == '_')) i++;
45            if (i > id && i < len && (s[i] == ' ' || s[i] == '\t')) {
46                while (i < len && (s[i] == ' ' || s[i] == '\t')) i++;
47                s += i;
48                len -= i;
49            }
50        }
51    }
52    /* line !~ /^[a-z][a-z0-9_]*\./ { next } */
53    if (len == 0 || !is_lower(s[0])) return NULL;
54    size_t i = 1;
55    while (i < len && is_ident(s[i])) i++;
56    if (i >= len || s[i] != '.') return NULL;
57    /* op = line up to the first blank or ';' */
58    size_t oplen = 0;
59    while (oplen < len && s[oplen] != ' ' && s[oplen] != '\t' && s[oplen] != ';') oplen++;
60    const char *tok[MAX_TOKENS];
61    size_t tl[MAX_TOKENS];
62    int n = 0;
63    for (size_t p = 0, start = 0; p <= oplen; p++) {
64        if (p == oplen || s[p] == '.') {
65            if (n < MAX_TOKENS) { tok[n] = s + start; tl[n] = p - start; n++; }
66            start = p + 1;
67        }
68    }
69    bool fl = false, f32 = false, narrow = false, approx = false, full = false, ftz = false, rn = false;
70    for (int k = 1; k < n; k++) {
71        if (tok_in(tok[k], tl[k], FLOATS)) fl = true;
72        if (tok_is(tok[k], tl[k], "f32")) f32 = true;
73    }
74    if (!fl) return NULL;
75    /* m = "." op ".": the tokens, the first one included. */
76    for (int k = 0; k < n; k++) {
77        if (tok_in(tok[k], tl[k], NARROW)) narrow = true;
78        if (tok_is(tok[k], tl[k], "approx")) approx = true;
79        if (tok_is(tok[k], tl[k], "full")) full = true;
80        if (tok_is(tok[k], tl[k], "ftz")) ftz = true;
81        if (tok_is(tok[k], tl[k], "rn")) rn = true;
82    }
83    const char *t1 = tok[0];
84    size_t l1 = tl[0];
85    if (narrow) return "16-bit / 8-bit float";
86    if (approx) return ".approx";
87    if (full) return "div.full";
88    if (tok_is(t1, l1, "fma") || tok_is(t1, l1, "mad")) return "fused multiply-add";
89    if (tok_in(t1, l1, ROUNDED)) {
90        if (!rn) return "no .rn (ptxas may fuse it, per arch)";
91        if (f32 && !ftz) return "f32 without .ftz";
92        return NULL;
93    }
94    if (tok_in(t1, l1, FLUSHED)) return f32 && !ftz ? "f32 without .ftz" : NULL;
95    if (tok_is(t1, l1, "cvt")) {
96        bool src32 = tok_is(tok[n - 1], tl[n - 1], "f32");
97        bool narrowing = n >= 2 && tok_is(tok[n - 2], tl[n - 2], "f32") && tok_is(tok[n - 1], tl[n - 1], "f64");
98        return (src32 || narrowing) && !ftz ? "f32 convert without .ftz" : NULL;
99    }
100    if (!tok_in(t1, l1, PLAIN)) return "float instruction with no rule (approximate or not covered)";
101    return NULL;
102}
103
104int mesh_ptx_float_check(const char *path, const char *text, size_t len, FILE *out) {
105    int bad = 0;
106    size_t lineno = 0;
107    for (size_t at = 0; at < len && bad < 5;) {
108        size_t end = at;
109        while (end < len && text[end] != '\n') end++;
110        lineno++;
111        const char *why = mesh_ptx_float_line(text + at, end - at);
112        if (why) {
113            bad++;
114            if (out) fprintf(out, "%s:%zu: %s: %.*s\n", path, lineno, why, (int)(end - at), text + at);
115        }
116        at = end + 1;
117    }
118    return bad;
119}