==> include/wasm/numeric.h <==1/* @ajax/wasm-numeric: WebAssembly's numeric instructions (spec 4.3), deterministic.
2 *
3 * Floats follow IEEE 754 in the default rounding mode, with no fused multiply-add (build with
4 * -ffp-contract=off, on a target with FLT_EVAL_METHOD 0) and, as wasmtime's engine in meshd
5 * (`cranelift_nan_canonicalization(true)`), every NaN an arithmetic operation produces is the
6 * canonical one: add, sub, mul, div, sqrt, min, max, ceil, floor, trunc, nearest, promote, demote.
7 * neg, abs and copysign are bit operations and keep a NaN's payload, as do loads, stores, selects
8 * and reinterprets.
9 *
10 * Operations that can trap return a trap code (WASM_NUM_OK when they didn't) and write the result
11 * through a pointer. Header-only. */
12#ifndef WASM_NUMERIC_H
13#define WASM_NUMERIC_H
14
15#include <float.h>
16#include <math.h>
17#include <stdbool.h>
18#include <stdint.h>
19#include <string.h>
20
21_Static_assert(FLT_EVAL_METHOD == 0, "wasm floats need float and double evaluated in their own precision");
22
23/* Trap codes, numbered as wasmtime's `Trap` (what meshd's FAULT_TRAP word carries). */
24enum {
25 WASM_NUM_OK = -1,
26 WASM_TRAP_STACK_OVERFLOW = 0,
27 WASM_TRAP_MEMORY_OUT_OF_BOUNDS = 1,
28 WASM_TRAP_HEAP_MISALIGNED = 2,
29 WASM_TRAP_TABLE_OUT_OF_BOUNDS = 3,
30 WASM_TRAP_INDIRECT_CALL_TO_NULL = 4,
31 WASM_TRAP_BAD_SIGNATURE = 5,
32 WASM_TRAP_INTEGER_OVERFLOW = 6,
33 WASM_TRAP_INTEGER_DIVISION_BY_ZERO = 7,
34 WASM_TRAP_BAD_CONVERSION_TO_INTEGER = 8,
35 WASM_TRAP_UNREACHABLE = 9,
36 WASM_TRAP_INTERRUPT = 10,
37 WASM_TRAP_OUT_OF_FUEL = 11,
38 WASM_TRAP_NULL_REFERENCE = 13,
39};
40
41/* wasmtime's message for a trap ("wasm trap: " + this). */
42static inline const char *wasm_trap_message(int trap) {
43 switch (trap) {
44 case WASM_TRAP_STACK_OVERFLOW: return "call stack exhausted";
45 case WASM_TRAP_MEMORY_OUT_OF_BOUNDS: return "out of bounds memory access";
46 case WASM_TRAP_HEAP_MISALIGNED: return "unaligned atomic";
47 case WASM_TRAP_TABLE_OUT_OF_BOUNDS: return "undefined element: out of bounds table access";
48 case WASM_TRAP_INDIRECT_CALL_TO_NULL: return "uninitialized element";
49 case WASM_TRAP_BAD_SIGNATURE: return "indirect call type mismatch";
50 case WASM_TRAP_INTEGER_OVERFLOW: return "integer overflow";
51 case WASM_TRAP_INTEGER_DIVISION_BY_ZERO: return "integer divide by zero";
52 case WASM_TRAP_BAD_CONVERSION_TO_INTEGER: return "invalid conversion to integer";
53 case WASM_TRAP_UNREACHABLE: return "wasm `unreachable` instruction executed";
54 case WASM_TRAP_INTERRUPT: return "interrupt";
55 case WASM_TRAP_OUT_OF_FUEL: return "all fuel consumed by WebAssembly";
56 case WASM_TRAP_NULL_REFERENCE: return "null reference";
57 default: return "trap";
58 }
59}
60
61#define WASM_CANON_F32 0x7fc00000u
62#define WASM_CANON_F64 0x7ff8000000000000ull
63
64static inline uint32_t wasm_f32_bits(float f) {
65 uint32_t u;
66 memcpy(&u, &f, 4);
67 return u;
68}
69static inline uint64_t wasm_f64_bits(double d) {
70 uint64_t u;
71 memcpy(&u, &d, 8);
72 return u;
73}
74static inline float wasm_f32_from(uint32_t u) {
75 float f;
76 memcpy(&f, &u, 4);
77 return f;
78}
79static inline double wasm_f64_from(uint64_t u) {
80 double d;
81 memcpy(&d, &u, 8);
82 return d;
83}
84
85static inline float wasm_canon32(float f) { return f != f ? wasm_f32_from(WASM_CANON_F32) : f; }
86static inline double wasm_canon64(double d) { return d != d ? wasm_f64_from(WASM_CANON_F64) : d; }
87
88/* ---- integers ---- */
89
90static inline uint32_t wasm_i32_clz(uint32_t x) { return x ? (uint32_t)__builtin_clz(x) : 32; }
91static inline uint32_t wasm_i32_ctz(uint32_t x) { return x ? (uint32_t)__builtin_ctz(x) : 32; }
92static inline uint32_t wasm_i32_popcnt(uint32_t x) { return (uint32_t)__builtin_popcount(x); }
93static inline uint64_t wasm_i64_clz(uint64_t x) { return x ? (uint64_t)__builtin_clzll(x) : 64; }
94static inline uint64_t wasm_i64_ctz(uint64_t x) { return x ? (uint64_t)__builtin_ctzll(x) : 64; }
95static inline uint64_t wasm_i64_popcnt(uint64_t x) { return (uint64_t)__builtin_popcountll(x); }
96static inline uint32_t wasm_i32_rotl(uint32_t x, uint32_t k) { k &= 31; return k ? x << k | x >> (32 - k) : x; }
97static inline uint32_t wasm_i32_rotr(uint32_t x, uint32_t k) { k &= 31; return k ? x >> k | x << (32 - k) : x; }
98static inline uint64_t wasm_i64_rotl(uint64_t x, uint64_t k) { k &= 63; return k ? x << k | x >> (64 - k) : x; }
99static inline uint64_t wasm_i64_rotr(uint64_t x, uint64_t k) { k &= 63; return k ? x >> k | x << (64 - k) : x; }
100static inline uint32_t wasm_i32_shr_s(uint32_t x, uint32_t k) {
101 k &= 31;
102 return (x & 0x80000000u) ? ~(~x >> k) : x >> k;
103}
104static inline uint64_t wasm_i64_shr_s(uint64_t x, uint64_t k) {
105 k &= 63;
106 return (x & 0x8000000000000000ull) ? ~(~x >> k) : x >> k;
107}
108
109static inline int wasm_i32_div_s(uint32_t a, uint32_t b, uint32_t *r) {
110 if (b == 0) return WASM_TRAP_INTEGER_DIVISION_BY_ZERO;
111 if (a == 0x80000000u && b == 0xffffffffu) return WASM_TRAP_INTEGER_OVERFLOW;
112 *r = (uint32_t)((int32_t)a / (int32_t)b);
113 return WASM_NUM_OK;
114}
115static inline int wasm_i32_div_u(uint32_t a, uint32_t b, uint32_t *r) {
116 if (b == 0) return WASM_TRAP_INTEGER_DIVISION_BY_ZERO;
117 *r = a / b;
118 return WASM_NUM_OK;
119}
120static inline int wasm_i32_rem_s(uint32_t a, uint32_t b, uint32_t *r) {
121 if (b == 0) return WASM_TRAP_INTEGER_DIVISION_BY_ZERO;
122 *r = b == 0xffffffffu ? 0 : (uint32_t)((int32_t)a % (int32_t)b);
123 return WASM_NUM_OK;
124}
125static inline int wasm_i32_rem_u(uint32_t a, uint32_t b, uint32_t *r) {
126 if (b == 0) return WASM_TRAP_INTEGER_DIVISION_BY_ZERO;
127 *r = a % b;
128 return WASM_NUM_OK;
129}
130static inline int wasm_i64_div_s(uint64_t a, uint64_t b, uint64_t *r) {
131 if (b == 0) return WASM_TRAP_INTEGER_DIVISION_BY_ZERO;
132 if (a == 0x8000000000000000ull && b == ~0ull) return WASM_TRAP_INTEGER_OVERFLOW;
133 *r = (uint64_t)((int64_t)a / (int64_t)b);
134 return WASM_NUM_OK;
135}
136static inline int wasm_i64_div_u(uint64_t a, uint64_t b, uint64_t *r) {
137 if (b == 0) return WASM_TRAP_INTEGER_DIVISION_BY_ZERO;
138 *r = a / b;
139 return WASM_NUM_OK;
140}
141static inline int wasm_i64_rem_s(uint64_t a, uint64_t b, uint64_t *r) {
142 if (b == 0) return WASM_TRAP_INTEGER_DIVISION_BY_ZERO;
143 *r = b == ~0ull ? 0 : (uint64_t)((int64_t)a % (int64_t)b);
144 return WASM_NUM_OK;
145}
146static inline int wasm_i64_rem_u(uint64_t a, uint64_t b, uint64_t *r) {
147 if (b == 0) return WASM_TRAP_INTEGER_DIVISION_BY_ZERO;
148 *r = a % b;
149 return WASM_NUM_OK;
150}
151
152/* ---- floats ---- */
153
154static inline float wasm_f32_min(float a, float b) {
155 if (a != a || b != b) return wasm_f32_from(WASM_CANON_F32);
156 if (a == b) return wasm_f32_from(wasm_f32_bits(a) | wasm_f32_bits(b)); /* min(-0, +0) = -0 */
157 return a < b ? a : b;
158}
159static inline float wasm_f32_max(float a, float b) {
160 if (a != a || b != b) return wasm_f32_from(WASM_CANON_F32);
161 if (a == b) return wasm_f32_from(wasm_f32_bits(a) & wasm_f32_bits(b)); /* max(-0, +0) = +0 */
162 return a > b ? a : b;
163}
164static inline double wasm_f64_min(double a, double b) {
165 if (a != a || b != b) return wasm_f64_from(WASM_CANON_F64);
166 if (a == b) return wasm_f64_from(wasm_f64_bits(a) | wasm_f64_bits(b));
167 return a < b ? a : b;
168}
169static inline double wasm_f64_max(double a, double b) {
170 if (a != a || b != b) return wasm_f64_from(WASM_CANON_F64);
171 if (a == b) return wasm_f64_from(wasm_f64_bits(a) & wasm_f64_bits(b));
172 return a > b ? a : b;
173}
174
175static inline float wasm_f32_add(float a, float b) { return wasm_canon32(a + b); }
176static inline float wasm_f32_sub(float a, float b) { return wasm_canon32(a - b); }
177static inline float wasm_f32_mul(float a, float b) { return wasm_canon32(a * b); }
178static inline float wasm_f32_div(float a, float b) { return wasm_canon32(a / b); }
179static inline float wasm_f32_sqrt(float a) { return wasm_canon32(sqrtf(a)); }
180static inline float wasm_f32_ceil(float a) { return wasm_canon32(ceilf(a)); }
181static inline float wasm_f32_floor(float a) { return wasm_canon32(floorf(a)); }
182static inline float wasm_f32_trunc(float a) { return wasm_canon32(truncf(a)); }
183/* Round to nearest, ties to even (the default rounding mode, which meshd never changes). */
184static inline float wasm_f32_nearest(float a) { return wasm_canon32(nearbyintf(a)); }
185static inline double wasm_f64_add(double a, double b) { return wasm_canon64(a + b); }
186static inline double wasm_f64_sub(double a, double b) { return wasm_canon64(a - b); }
187static inline double wasm_f64_mul(double a, double b) { return wasm_canon64(a * b); }
188static inline double wasm_f64_div(double a, double b) { return wasm_canon64(a / b); }
189static inline double wasm_f64_sqrt(double a) { return wasm_canon64(sqrt(a)); }
190static inline double wasm_f64_ceil(double a) { return wasm_canon64(ceil(a)); }
191static inline double wasm_f64_floor(double a) { return wasm_canon64(floor(a)); }
192static inline double wasm_f64_trunc(double a) { return wasm_canon64(trunc(a)); }
193static inline double wasm_f64_nearest(double a) { return wasm_canon64(nearbyint(a)); }
194static inline float wasm_f32_demote(double a) { return wasm_canon32((float)a); }
195static inline double wasm_f64_promote(float a) { return wasm_canon64((double)a); }
196
197static inline uint32_t wasm_f32_neg(uint32_t a) { return a ^ 0x80000000u; }
198static inline uint32_t wasm_f32_abs(uint32_t a) { return a & 0x7fffffffu; }
199static inline uint32_t wasm_f32_copysign(uint32_t a, uint32_t b) { return (a & 0x7fffffffu) | (b & 0x80000000u); }
200static inline uint64_t wasm_f64_neg(uint64_t a) { return a ^ 0x8000000000000000ull; }
201static inline uint64_t wasm_f64_abs(uint64_t a) { return a & 0x7fffffffffffffffull; }
202static inline uint64_t wasm_f64_copysign(uint64_t a, uint64_t b) {
203 return (a & 0x7fffffffffffffffull) | (b & 0x8000000000000000ull);
204}
205
206/* ---- float to integer: trapping (NaN: bad conversion; out of range: overflow) ---- */
207
208#define WASM_TRUNC(name, F, T, lo_ok, hi_ok) \
209 static inline int name(F x, T *r) { \
210 if (x != x) return WASM_TRAP_BAD_CONVERSION_TO_INTEGER; \
211 if (!(lo_ok) || !(hi_ok)) return WASM_TRAP_INTEGER_OVERFLOW; \
212 *r = (T)x; \
213 return WASM_NUM_OK; \
214 }
215WASM_TRUNC(wasm_i32_trunc_f32_s_, float, int32_t, x >= -2147483648.0f, x < 2147483648.0f)
216WASM_TRUNC(wasm_i32_trunc_f32_u_, float, uint32_t, x > -1.0f, x < 4294967296.0f)
217WASM_TRUNC(wasm_i32_trunc_f64_s_, double, int32_t, x > -2147483649.0, x < 2147483648.0)
218WASM_TRUNC(wasm_i32_trunc_f64_u_, double, uint32_t, x > -1.0, x < 4294967296.0)
219WASM_TRUNC(wasm_i64_trunc_f32_s_, float, int64_t, x >= -9223372036854775808.0f, x < 9223372036854775808.0f)
220WASM_TRUNC(wasm_i64_trunc_f32_u_, float, uint64_t, x > -1.0f, x < 18446744073709551616.0f)
221WASM_TRUNC(wasm_i64_trunc_f64_s_, double, int64_t, x >= -9223372036854775808.0, x < 9223372036854775808.0)
222WASM_TRUNC(wasm_i64_trunc_f64_u_, double, uint64_t, x > -1.0, x < 18446744073709551616.0)
223#undef WASM_TRUNC
224
225static inline int wasm_i32_trunc_f32_s(float x, uint32_t *r) {
226 int32_t v = 0;
227 int t = wasm_i32_trunc_f32_s_(x, &v);
228 *r = (uint32_t)v;
229 return t;
230}
231static inline int wasm_i32_trunc_f64_s(double x, uint32_t *r) {
232 int32_t v = 0;
233 int t = wasm_i32_trunc_f64_s_(x, &v);
234 *r = (uint32_t)v;
235 return t;
236}
237static inline int wasm_i64_trunc_f32_s(float x, uint64_t *r) {
238 int64_t v = 0;
239 int t = wasm_i64_trunc_f32_s_(x, &v);
240 *r = (uint64_t)v;
241 return t;
242}
243static inline int wasm_i64_trunc_f64_s(double x, uint64_t *r) {
244 int64_t v = 0;
245 int t = wasm_i64_trunc_f64_s_(x, &v);
246 *r = (uint64_t)v;
247 return t;
248}
249#define wasm_i32_trunc_f32_u wasm_i32_trunc_f32_u_
250#define wasm_i32_trunc_f64_u wasm_i32_trunc_f64_u_
251#define wasm_i64_trunc_f32_u wasm_i64_trunc_f32_u_
252#define wasm_i64_trunc_f64_u wasm_i64_trunc_f64_u_
253
254/* ---- float to integer: saturating (NaN: 0; out of range: the nearest bound) ---- */
255
256static inline uint32_t wasm_i32_trunc_sat_f32_s(float x) {
257 if (x != x) return 0;
258 if (x < -2147483648.0f) return 0x80000000u;
259 if (x >= 2147483648.0f) return 0x7fffffffu;
260 return (uint32_t)(int32_t)x;
261}
262static inline uint32_t wasm_i32_trunc_sat_f32_u(float x) {
263 if (x != x || x <= -1.0f) return 0;
264 if (x >= 4294967296.0f) return 0xffffffffu;
265 return (uint32_t)x;
266}
267static inline uint32_t wasm_i32_trunc_sat_f64_s(double x) {
268 if (x != x) return 0;
269 if (x <= -2147483649.0) return 0x80000000u;
270 if (x >= 2147483648.0) return 0x7fffffffu;
271 return (uint32_t)(int32_t)x;
272}
273static inline uint32_t wasm_i32_trunc_sat_f64_u(double x) {
274 if (x != x || x <= -1.0) return 0;
275 if (x >= 4294967296.0) return 0xffffffffu;
276 return (uint32_t)x;
277}
278static inline uint64_t wasm_i64_trunc_sat_f32_s(float x) {
279 if (x != x) return 0;
280 if (x < -9223372036854775808.0f) return 0x8000000000000000ull;
281 if (x >= 9223372036854775808.0f) return 0x7fffffffffffffffull;
282 return (uint64_t)(int64_t)x;
283}
284static inline uint64_t wasm_i64_trunc_sat_f32_u(float x) {
285 if (x != x || x <= -1.0f) return 0;
286 if (x >= 18446744073709551616.0f) return ~0ull;
287 return (uint64_t)x;
288}
289static inline uint64_t wasm_i64_trunc_sat_f64_s(double x) {
290 if (x != x) return 0;
291 if (x < -9223372036854775808.0) return 0x8000000000000000ull;
292 if (x >= 9223372036854775808.0) return 0x7fffffffffffffffull;
293 return (uint64_t)(int64_t)x;
294}
295static inline uint64_t wasm_i64_trunc_sat_f64_u(double x) {
296 if (x != x || x <= -1.0) return 0;
297 if (x >= 18446744073709551616.0) return ~0ull;
298 return (uint64_t)x;
299}
300
301#endif