==> tests/float_guard.c <==1/* The Rust's `float_guard` test (mesh-host/src/wasm.rs), case for case. */
2#include "mesh/check.h"
3#include "wasm/builder.h"
4#include "wasm/float_guard.h"
5
6#include <string.h>
7
8/* A module of one function of type (f32, f32) -> f32 (or () -> () with simd), exported as export,
9 * whose body is body; optionally one function import first (the Rust test's `module`). */
10static wasm_buf module(const char *imod, const char *iname, const char *export, const uint8_t *body, size_t n, bool simd) {
11 wasm_builder w = {0};
12 uint32_t t = wasm_builder_type(&w, simd ? ">" : "ff>f");
13 if (imod) wasm_builder_import_func(&w, imod, iname, t);
14 uint32_t f;
15 wb_bytes(wasm_builder_func_add(&w, t, "", &f), body, n);
16 wasm_builder_add_export(&w, export, WASM_EXTERN_FUNC, f);
17 wasm_buf out;
18 wasm_builder_finish(&w, &out);
19 wasm_builder_free(&w);
20 return out;
21}
22
23static const uint8_t ADD[] = {0x20, 0, 0x20, 1, 0x92}; /* local.get 0, local.get 1, f32.add */
24static const uint8_t MOVE[] = {0x20, 0}; /* local.get 0 (no f32 op) */
25
26static bool prefix(const char *s, const char *p) { return strncmp(s, p, strlen(p)) == 0; }
27
28int main(void) {
29 char err[512];
30 wasm_float_report r;
31 /* f32.add in a non-helper: refused in an FTZ image, fine in an IEEE one. */
32 wasm_buf raw = module(NULL, NULL, "f", ADD, sizeof ADD, false);
33 CHECK(!wasm_float_check(raw.data, raw.len, true, &r, err, sizeof err));
34 CHECK(prefix(err, "float: F32Add in function 0"));
35 CHECK(wasm_float_check(raw.data, raw.len, false, &r, err, sizeof err));
36 CHECK_U(r.funcs, 1);
37 CHECK_U(r.helpers, 0);
38 CHECK_U(r.f32_ops, 1);
39 wasm_buf_free(&raw);
40 /* The same op inside an exported mesh_ftz_* helper: allowed. */
41 wasm_buf helper = module(NULL, NULL, "mesh_ftz_add", ADD, sizeof ADD, false);
42 CHECK(wasm_float_check(helper.data, helper.len, true, &r, err, sizeof err));
43 CHECK_U(r.funcs, 1);
44 CHECK_U(r.helpers, 1);
45 CHECK_U(r.f32_ops, 1);
46 wasm_buf_free(&helper);
47 /* Moves are not flushing ops. */
48 wasm_buf mv = module(NULL, NULL, "f", MOVE, sizeof MOVE, false);
49 CHECK(wasm_float_check(mv.data, mv.len, true, &r, err, sizeof err));
50 CHECK_U(r.f32_ops, 0);
51 wasm_buf_free(&mv);
52 /* Imports: only `mesh` functions (the helper index counts the import). */
53 wasm_buf env = module("env", "x", "mesh_ftz_add", ADD, sizeof ADD, false);
54 CHECK(!wasm_float_check(env.data, env.len, true, &r, err, sizeof err));
55 CHECK(prefix(err, "load: import env::x"));
56 wasm_buf_free(&env);
57 wasm_buf mesh = module("mesh", "info", "mesh_ftz_add", ADD, sizeof ADD, false);
58 CHECK(wasm_float_check(mesh.data, mesh.len, true, &r, err, sizeof err));
59 CHECK_U(r.helpers, 1);
60 wasm_buf_free(&mesh);
61 /* SIMD (v128.const; drop): refused in an FTZ image only. */
62 uint8_t simd[19] = {0xfd, 0x0c};
63 simd[18] = 0x1a;
64 wasm_buf v = module(NULL, NULL, "f", simd, sizeof simd, true);
65 CHECK(!wasm_float_check(v.data, v.len, true, &r, err, sizeof err));
66 CHECK(prefix(err, "float: "));
67 CHECK(wasm_float_check(v.data, v.len, false, &r, err, sizeof err));
68 wasm_buf_free(&v);
69 /* Not wasm at all: the compiler's refusal. */
70 CHECK(!wasm_float_check((const uint8_t *)"\0asm\x01\0\0\0\x01\x05", 10, true, &r, err, sizeof err));
71 CHECK(prefix(err, "jit: "));
72 /* Threads and relaxed SIMD are refused even in an IEEE image. */
73 static const uint8_t fence[] = {0xfe, 0x03, 0x00};
74 wasm_buf th = module(NULL, NULL, "f", fence, sizeof fence, true);
75 CHECK(!wasm_float_check(th.data, th.len, false, &r, err, sizeof err));
76 CHECK(prefix(err, "float: threads support is not enabled"));
77 wasm_buf_free(&th);
78 /* Every flushing op is one of the 22 the Rust lists. */
79 int n = 0;
80 for (uint32_t op = 0; op < 256; op++) n += wasm_flushing_f32(op);
81 CHECK_I(n, 22);
82 return mesh_check_done();
83}