==> 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}