==> src/float_guard.c <==1/* float_check, as in mesh-host/src/wasm.rs: validate twice (every proposal, then without what the
2 * executor never runs), then walk imports, exports and every operator. */
3#include "wasm/float_guard.h"
4
5#include "mesh/abi/wasm.h"
6#include "wasm/decode.h"
7#include "wasm/instr.h"
8#include "wasm/validate.h"
9
10#include <stdarg.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14
15bool wasm_flushing_f32(uint32_t op) {
16 switch (op) {
17 case 0x8b: case 0x8c: case 0x8d: case 0x8e: case 0x8f: case 0x90: case 0x91: /* abs neg ceil floor trunc nearest sqrt */
18 case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97: case 0x98: /* add sub mul div min max copysign */
19 case 0x5b: case 0x5c: case 0x5d: case 0x5e: case 0x5f: case 0x60: /* eq ne lt gt le ge */
20 case 0xb6: case 0xbb: /* demote, promote */
21 return true;
22 default: return false;
23 }
24}
25
26static bool say(char *err, size_t errlen, const char *fmt, ...) {
27 if (err && errlen) {
28 va_list ap;
29 va_start(ap, fmt);
30 vsnprintf(err, errlen, fmt, ap);
31 va_end(ap);
32 }
33 return false;
34}
35
36static bool starts_with(const uint8_t *s, uint32_t n, const char *prefix) {
37 size_t k = strlen(prefix);
38 return n >= k && memcmp(s, prefix, k) == 0;
39}
40
41bool wasm_float_check(const uint8_t *wasm, size_t len, bool ftz, wasm_float_report *rep, char *err, size_t errlen) {
42 char msg[384];
43 wasm_module m;
44 wasm_float_report r = {0};
45 /* Well-formed under every proposal we know (else it is the compiler's refusal, `jit`) ... */
46 if (!wasm_decode(wasm, len, &m, msg, sizeof msg)) return say(err, errlen, "jit: invalid wasm: %s", msg);
47 bool ok = false;
48 bool *helpers = NULL;
49 if (!wasm_validate(&m, WASM_F_ALL, NULL, msg, sizeof msg)) {
50 say(err, errlen, "jit: invalid wasm: %s", msg);
51 goto out;
52 }
53 /* ... and without what this executor never runs: threads, shared memory and relaxed SIMD
54 * anywhere; any SIMD in an FTZ image (its f32x4 ops have no flush). */
55 wasm_features f = WASM_F_ALL & ~(wasm_features)(WASM_F_THREADS | WASM_F_RELAXED_SIMD);
56 if (ftz) f &= ~(wasm_features)WASM_F_SIMD;
57 if (!wasm_validate(&m, f, NULL, msg, sizeof msg)) {
58 say(err, errlen, "float: %s (a CPU program uses no threads, shared memory or relaxed SIMD%s)", msg,
59 ftz ? "; an FTZ image no SIMD" : "");
60 goto out;
61 }
62 for (uint32_t i = 0; i < m.nimports; i++) {
63 const wasm_import *im = &m.imports[i];
64 if (!(im->module_len == strlen(MESH_WASM_MODULE) && memcmp(im->module, MESH_WASM_MODULE, im->module_len) == 0) ||
65 im->kind != WASM_EXTERN_FUNC) {
66 say(err, errlen, "load: import %.*s::%.*s: a CPU program imports only `%s` functions (mesh-abi v2::wasm)",
67 (int)im->module_len, im->module, (int)im->name_len, im->name, MESH_WASM_MODULE);
68 goto out;
69 }
70 }
71 if (!(helpers = calloc(m.nfuncs ? m.nfuncs : 1, sizeof *helpers))) {
72 say(err, errlen, "load: out of memory");
73 goto out;
74 }
75 for (uint32_t i = 0; i < m.nexports; i++) {
76 const wasm_export *e = &m.exports[i];
77 if (e->kind == WASM_EXTERN_FUNC && starts_with(e->name, e->name_len, MESH_WASM_FTZ_PREFIX) && !helpers[e->index]) {
78 helpers[e->index] = true;
79 r.helpers++;
80 }
81 }
82 for (uint32_t b = 0; b < m.ncodes; b++) {
83 uint32_t func = m.nfunc_imports + b;
84 const wasm_expr *body = &m.codes[b].body;
85 wasm_reader rd = wasm_reader_at(m.bytes, body->start, body->start + body->len, msg, sizeof msg);
86 wasm_instr in;
87 while (!wasm_rd_eof(&rd)) {
88 if (!wasm_rd_instr(&rd, &in)) {
89 say(err, errlen, "jit: %s", msg);
90 goto out;
91 }
92 if (!wasm_flushing_f32(in.op)) continue;
93 r.f32_ops++;
94 if (ftz && !helpers[func]) {
95 say(err, errlen,
96 "float: %s in function %u, outside the image's %s* helpers: an FTZ image (manifest float = \"ftz\", "
97 "the default) computes every f32 op through mesh-ref's Z (crates/mesh-rt-wasm); declare float = "
98 "\"ieee\" for plain f32 (not lockstep)",
99 in.info.name, func, MESH_WASM_FTZ_PREFIX);
100 goto out;
101 }
102 }
103 }
104 r.funcs = m.ncodes;
105 if (rep) *rep = r;
106 ok = true;
107out:
108 free(helpers);
109 wasm_module_free(&m);
110 return ok;
111}