Coverage Report

Created: 2026-11-12 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wasm3/source/m3_math_utils.h
Line
Count
Source
1
//
2
//  m3_math_utils.h
3
//
4
//  Created by Volodymyr Shymanksyy on 8/10/19.
5
//  Copyright © 2019 Volodymyr Shymanskyy. All rights reserved.
6
//
7
8
#ifndef m3_math_utils_h
9
#define m3_math_utils_h
10
11
#include "m3_core.h"
12
13
#include <limits.h>
14
15
#if defined(M3_COMPILER_MSVC)
16
17
#  include <intrin.h>
18
19
#  define __builtin_popcount    __popcnt
20
21
static inline
22
int __builtin_ctz (uint32_t x)
23
{
24
    unsigned long ret;
25
    _BitScanForward(&ret, x);
26
    return (int)ret;
27
}
28
29
static inline
30
int __builtin_clz (uint32_t x)
31
{
32
    unsigned long ret;
33
    _BitScanReverse(&ret, x);
34
    return (int)(31 ^ ret);
35
}
36
37
38
#  ifdef _WIN64
39
40
#    define __builtin_popcountll  __popcnt64
41
42
static inline
43
int __builtin_ctzll (uint64_t value)
44
{
45
    unsigned long ret;
46
    _BitScanForward64(&ret, value);
47
    return (int)ret;
48
}
49
50
static inline
51
int __builtin_clzll (uint64_t value)
52
{
53
    unsigned long ret;
54
    _BitScanReverse64(&ret, value);
55
    return (int)(63 ^ ret);
56
}
57
58
#  else // _WIN64
59
60
#    define __builtin_popcountll(x)  (__popcnt((x) & 0xFFFFFFFF) + __popcnt((x) >> 32))
61
62
static inline
63
int __builtin_ctzll (uint64_t value)
64
{
65
    //if (value == 0) return 64; // Note: ctz(0) result is undefined anyway
66
    uint32_t msh = (uint32_t)(value >> 32);
67
    uint32_t lsh = (uint32_t)(value & 0xFFFFFFFF);
68
    if (lsh != 0) {
69
        return __builtin_ctz(lsh);
70
    }
71
    return 32 + __builtin_ctz(msh);
72
}
73
74
static inline
75
int __builtin_clzll (uint64_t value)
76
{
77
    //if (value == 0) return 64; // Note: clz(0) result is undefined anyway
78
    uint32_t msh = (uint32_t)(value >> 32);
79
    uint32_t lsh = (uint32_t)(value & 0xFFFFFFFF);
80
    if (msh != 0) {
81
        return __builtin_clz(msh);
82
    }
83
    return 32 + __builtin_clz(lsh);
84
}
85
86
#  endif // _WIN64
87
88
#endif // defined(M3_COMPILER_MSVC)
89
90
#if defined(M3_COMPILER_TCC)
91
92
// TinyCC provides none of the bit-counting builtins the interpreter uses
93
94
static inline
95
int __builtin_popcount (uint32_t x)
96
{
97
    x = x - ((x >> 1) & 0x55555555u);
98
    x = (x & 0x33333333u) + ((x >> 2) & 0x33333333u);
99
    x = (x + (x >> 4)) & 0x0f0f0f0fu;
100
    return (int)((x * 0x01010101u) >> 24);
101
}
102
103
static inline
104
int __builtin_ctz (uint32_t x)
105
{
106
    //if (x == 0) return 32; // Note: ctz(0) result is undefined anyway
107
    return __builtin_popcount((x & (~x + 1u)) - 1);
108
}
109
110
static inline
111
int __builtin_clz (uint32_t x)
112
{
113
    //if (x == 0) return 32; // Note: clz(0) result is undefined anyway
114
    x |= x >> 1;
115
    x |= x >> 2;
116
    x |= x >> 4;
117
    x |= x >> 8;
118
    x |= x >> 16;
119
    return 32 - __builtin_popcount(x);
120
}
121
122
static inline
123
int __builtin_popcountll (uint64_t value)
124
{
125
    return __builtin_popcount((uint32_t)(value & 0xFFFFFFFF)) +
126
           __builtin_popcount((uint32_t)(value >> 32));
127
}
128
129
static inline
130
int __builtin_ctzll (uint64_t value)
131
{
132
    uint32_t msh = (uint32_t)(value >> 32);
133
    uint32_t lsh = (uint32_t)(value & 0xFFFFFFFF);
134
    if (lsh != 0) {
135
        return __builtin_ctz(lsh);
136
    }
137
    return 32 + __builtin_ctz(msh);
138
}
139
140
static inline
141
int __builtin_clzll (uint64_t value)
142
{
143
    uint32_t msh = (uint32_t)(value >> 32);
144
    uint32_t lsh = (uint32_t)(value & 0xFFFFFFFF);
145
    if (msh != 0) {
146
        return __builtin_clz(msh);
147
    }
148
    return 32 + __builtin_clz(lsh);
149
}
150
151
152
// TinyCC compiles -x on floats as an arithmetic negation, which quiets signaling
153
// NaNs; f32.neg/f64.neg must only flip the sign bit and leave the payload alone
154
155
static inline
156
f32 m3_negf (f32 x)
157
{
158
    u32 bits;
159
    memcpy(&bits, &x, sizeof(bits));
160
    bits ^= 0x80000000u;
161
    memcpy(&x, &bits, sizeof(x));
162
    return x;
163
}
164
165
static inline
166
f64 m3_neg (f64 x)
167
{
168
    u64 bits;
169
    memcpy(&bits, &x, sizeof(bits));
170
    bits ^= 0x8000000000000000ull;
171
    memcpy(&x, &bits, sizeof(x));
172
    return x;
173
}
174
175
#endif // defined(M3_COMPILER_TCC)
176
177
#if defined(__AVR__)
178
179
static inline
180
float rintf (float arg)
181
{
182
    union {
183
        float    f;
184
        uint32_t i;
185
    } u;
186
    u.f = arg;
187
    uint32_t ux = u.i & 0x7FFFFFFF;
188
    if (M3_UNLIKELY(ux == 0 || ux > 0x5A000000)) {
189
        return arg;
190
    }
191
    return (float)lrint(arg);
192
}
193
194
static inline
195
double rint (double arg)
196
{
197
    union {
198
        double   f;
199
        uint32_t i[2];
200
    } u;
201
    u.f = arg;
202
    uint32_t ux = u.i[1] & 0x7FFFFFFF;
203
    if (M3_UNLIKELY((ux == 0 && u.i[0] == 0) || ux > 0x433FFFFF)) {
204
        return arg;
205
    }
206
    return (double)lrint(arg);
207
}
208
209
static inline
210
uint64_t strtoull (const char* str, char** endptr, int base)
211
{
212
    uint64_t    result = 0;
213
    const char* p = str;
214
215
    while (*p == ' ' || *p == '\t') {
216
        p++;
217
    }
218
219
    if (base == 0) {
220
        if (*p == '0' && (*(p + 1) == 'x' || *(p + 1) == 'X')) {
221
            base = 16;
222
            p += 2;
223
        } else if (*p == '0') {
224
            base = 8;
225
            p++;
226
        } else {
227
            base = 10;
228
        }
229
    } else if (base == 16 && *p == '0' && (*(p + 1) == 'x' || *(p + 1) == 'X')) {
230
        p += 2;
231
    }
232
233
    while (*p) {
234
        int digit;
235
        if (*p >= '0' && *p <= '9') {
236
            digit = *p - '0';
237
        } else if (*p >= 'a' && *p <= 'f') {
238
            digit = *p - 'a' + 10;
239
        } else if (*p >= 'A' && *p <= 'F') {
240
            digit = *p - 'A' + 10;
241
        } else {
242
            break;
243
        }
244
        if (digit >= base) {
245
            break;
246
        }
247
        result = result * base + digit;
248
        p++;
249
    }
250
251
    if (endptr) {
252
        *endptr = (char*)p;
253
    }
254
    return result;
255
}
256
257
#endif
258
259
/*
260
 * Rotr, Rotl
261
 */
262
263
static inline
264
u32 rotl32 (u32 n, unsigned c)
265
3.01M
{
266
3.01M
    const unsigned mask = CHAR_BIT * sizeof(n) - 1;
267
3.01M
    c &= mask & 31;
268
3.01M
    return (n << c) | (n >> ((32 - c) & mask));
269
3.01M
}
270
271
static inline
272
u32 rotr32 (u32 n, unsigned c)
273
5.12M
{
274
5.12M
    const unsigned mask = CHAR_BIT * sizeof(n) - 1;
275
5.12M
    c &= mask & 31;
276
5.12M
    return (n >> c) | (n << ((32 - c) & mask));
277
5.12M
}
278
279
static inline
280
u64 rotl64 (u64 n, u64 c)
281
3.23M
{
282
3.23M
    const unsigned mask = CHAR_BIT * sizeof(n) - 1;
283
3.23M
    c &= mask & 63;
284
3.23M
    return (n << c) | (n >> ((64 - c) & mask));
285
3.23M
}
286
287
static inline
288
u64 rotr64 (u64 n, u64 c)
289
2.28M
{
290
2.28M
    const unsigned mask = CHAR_BIT * sizeof(n) - 1;
291
2.28M
    c &= mask & 63;
292
2.28M
    return (n >> c) | (n << ((64 - c) & mask));
293
2.28M
}
294
295
/*
296
 * Wide multiply: the full 128-bit product of two 64-bit integers, as its low
297
 * half returned and its high half written through o_high
298
 */
299
300
static inline
301
u64 mul_wide_u64 (u64 a, u64 b, u64* o_high)
302
2.31M
{
303
2.31M
#if defined(__SIZEOF_INT128__) && (UINTPTR_MAX > UINT32_MAX || defined(__wasm_wide_arithmetic__))
304
    // a 64-bit target multiplies into a register pair in one instruction. A 32-bit
305
    // one that has the type anyway, wasm32 among them, would make a library call,
306
    // unless the target itself provides wide arithmetic instructions.
307
2.31M
    unsigned __int128 product = (unsigned __int128)a * b;
308
309
2.31M
    *o_high = (u64)(product >> 64);
310
2.31M
    return (u64)product;
311
#else
312
    // four 32 x 32 partial products of the halves, a1:a0 times b1:b0. The middle
313
    // column collects the carry out of the low one and the low halves of the two
314
    // cross terms, at most 3 * (2^32 - 1).
315
    u64 a0 = (u32)a, a1 = a >> 32;
316
    u64 b0 = (u32)b, b1 = b >> 32;
317
318
    u64 ll = a0 * b0;
319
    u64 lh = a0 * b1;
320
    u64 hl = a1 * b0;
321
    u64 hh = a1 * b1;
322
323
    u64 middle = (ll >> 32) + (u32)lh + (u32)hl;
324
325
    *o_high = hh + (lh >> 32) + (hl >> 32) + (middle >> 32);
326
    return (middle << 32) | (u32)ll;
327
#endif
328
2.31M
}
329
330
// A negative factor reads 2^64 too large as unsigned, which puts 2^64 times the
331
// other factor too much into the product - one other factor too much in the high
332
// half, which is where it is taken back out
333
static inline
334
u64 mul_wide_i64 (i64 a, i64 b, u64* o_high)
335
1.67M
{
336
1.67M
    u64 low = mul_wide_u64((u64)a, (u64)b, o_high);
337
338
1.67M
    if (a < 0) {
339
908k
        *o_high -= (u64)b;
340
908k
    }
341
1.67M
    if (b < 0) {
342
948k
        *o_high -= (u64)a;
343
948k
    }
344
345
1.67M
    return low;
346
1.67M
}
347
348
/*
349
 * Integer Div, Rem
350
 */
351
352
#define OP_DIV_U(RES, A, B)                                         \
353
80.9k
    if (M3_UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero);    \
354
80.9k
    RES = A / B;
355
356
#define OP_REM_U(RES, A, B)                                         \
357
87.7k
    if (M3_UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero);    \
358
87.7k
    RES = A % B;
359
360
// 2's complement detection
361
#if (INT_MIN != -INT_MAX)
362
363
#  define OP_DIV_S(RES, A, B, TYPE_MIN)                             \
364
108k
      if (M3_UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero);  \
365
108k
      if (M3_UNLIKELY(B == -1 and A == TYPE_MIN)) {                 \
366
8
          newTrap (m3Err_trapIntegerOverflow);                      \
367
8
      }                                                             \
368
107k
      RES = A / B;
369
370
#  define OP_REM_S(RES, A, B, TYPE_MIN)                             \
371
106k
      if (M3_UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero);  \
372
106k
      if (M3_UNLIKELY(B == -1 and A == TYPE_MIN)) RES = 0;          \
373
106k
      else RES = A % B;
374
375
#else
376
377
#  define OP_DIV_S(RES, A, B, TYPE_MIN) OP_DIV_U(RES, A, B)
378
#  define OP_REM_S(RES, A, B, TYPE_MIN) OP_REM_U(RES, A, B)
379
380
#endif
381
382
/*
383
 * Trunc
384
 */
385
386
#define OP_TRUNC(RES, A, TYPE, RMIN, RMAX)                  \
387
20.9M
    if (M3_UNLIKELY(isnan(A))) {                            \
388
100
        newTrap (m3Err_trapIntegerConversion);              \
389
100
    }                                                       \
390
20.9M
    if (M3_UNLIKELY(A <= RMIN or A >= RMAX)) {              \
391
187
        newTrap (m3Err_trapIntegerOverflow);                \
392
187
    }                                                       \
393
20.9M
    RES = (TYPE)A;
394
395
396
3.01M
#define OP_I32_TRUNC_F32(RES, A)    OP_TRUNC(RES, A, i32, -2147483904.0f, 2147483648.0f)
397
3.19M
#define OP_U32_TRUNC_F32(RES, A)    OP_TRUNC(RES, A, u32,          -1.0f, 4294967296.0f)
398
2.11M
#define OP_I32_TRUNC_F64(RES, A)    OP_TRUNC(RES, A, i32, -2147483649.0 , 2147483648.0 )
399
2.40M
#define OP_U32_TRUNC_F64(RES, A)    OP_TRUNC(RES, A, u32,          -1.0 , 4294967296.0 )
400
401
3.61M
#define OP_I64_TRUNC_F32(RES, A)    OP_TRUNC(RES, A, i64, -9223373136366403584.0f,  9223372036854775808.0f)
402
2.66M
#define OP_U64_TRUNC_F32(RES, A)    OP_TRUNC(RES, A, u64,                   -1.0f, 18446744073709551616.0f)
403
1.92M
#define OP_I64_TRUNC_F64(RES, A)    OP_TRUNC(RES, A, i64, -9223372036854777856.0 ,  9223372036854775808.0 )
404
2.00M
#define OP_U64_TRUNC_F64(RES, A)    OP_TRUNC(RES, A, u64,                   -1.0 , 18446744073709551616.0 )
405
406
#define OP_TRUNC_SAT(RES, A, TYPE, RMIN, RMAX, IMIN, IMAX)  \
407
45.3M
    if (M3_UNLIKELY(isnan(A))) {                            \
408
9.37M
        RES = 0;                                            \
409
36.0M
    } else if (M3_UNLIKELY(A <= RMIN)) {                    \
410
11.1M
        RES = IMIN;                                         \
411
24.8M
    } else if (M3_UNLIKELY(A >= RMAX)) {                    \
412
10.6M
        RES = IMAX;                                         \
413
14.2M
    } else {                                                \
414
14.2M
        RES = (TYPE)A;                                      \
415
14.2M
    }
416
417
6.93M
#define OP_I32_TRUNC_SAT_F32(RES, A)    OP_TRUNC_SAT(RES, A, i32, -2147483904.0f, 2147483648.0f,   INT32_MIN,  INT32_MAX)
418
7.22M
#define OP_U32_TRUNC_SAT_F32(RES, A)    OP_TRUNC_SAT(RES, A, u32,          -1.0f, 4294967296.0f,         0UL, UINT32_MAX)
419
4.49M
#define OP_I32_TRUNC_SAT_F64(RES, A)    OP_TRUNC_SAT(RES, A, i32, -2147483649.0 , 2147483648.0,    INT32_MIN,  INT32_MAX)
420
4.55M
#define OP_U32_TRUNC_SAT_F64(RES, A)    OP_TRUNC_SAT(RES, A, u32,          -1.0 , 4294967296.0,          0UL, UINT32_MAX)
421
422
6.65M
#define OP_I64_TRUNC_SAT_F32(RES, A)    OP_TRUNC_SAT(RES, A, i64, -9223373136366403584.0f,  9223372036854775808.0f, INT64_MIN,  INT64_MAX)
423
6.51M
#define OP_U64_TRUNC_SAT_F32(RES, A)    OP_TRUNC_SAT(RES, A, u64,                   -1.0f, 18446744073709551616.0f,      0ULL, UINT64_MAX)
424
4.57M
#define OP_I64_TRUNC_SAT_F64(RES, A)    OP_TRUNC_SAT(RES, A, i64, -9223372036854777856.0 ,  9223372036854775808.0,  INT64_MIN,  INT64_MAX)
425
4.41M
#define OP_U64_TRUNC_SAT_F64(RES, A)    OP_TRUNC_SAT(RES, A, u64,                   -1.0 , 18446744073709551616.0,       0ULL, UINT64_MAX)
426
427
/*
428
 * Min, Max
429
 */
430
431
#if d_m3HasFloat
432
433
#  include <math.h>
434
435
// Propagate a NaN operand the way the arithmetic ops do.
436
// keep its sign and payload, but force the quiet bit, since the spec requires
437
// min/max to produce an arithmetic NaN.
438
439
static inline
440
f32 quiet_nan_f32 (f32 arg)
441
2.33M
{
442
2.33M
    union {
443
2.33M
        f32 f;
444
2.33M
        u32 i;
445
2.33M
    } u;
446
2.33M
    u.f = arg;
447
2.33M
    u.i |= 0x00400000;
448
2.33M
    return u.f;
449
2.33M
}
450
451
static inline
452
f64 quiet_nan_f64 (f64 arg)
453
860k
{
454
860k
    union {
455
860k
        f64 f;
456
860k
        u64 i;
457
860k
    } u;
458
860k
    u.f = arg;
459
860k
    u.i |= 0x0008000000000000ULL;
460
860k
    return u.f;
461
860k
}
462
463
static inline
464
f32 min_f32 (f32 a, f32 b)
465
3.80M
{
466
3.80M
    if (M3_UNLIKELY(isnan(a) or isnan(b))) {
467
1.12M
        return quiet_nan_f32(isnan(a) ? a : b);
468
1.12M
    }
469
2.67M
    if (M3_UNLIKELY(a == 0 and a == b)) {
470
491k
        return signbit(a) ? a : b;
471
491k
    }
472
2.18M
    return a > b ? b : a;
473
2.67M
}
474
475
static inline
476
f32 max_f32 (f32 a, f32 b)
477
3.99M
{
478
3.99M
    if (M3_UNLIKELY(isnan(a) or isnan(b))) {
479
1.21M
        return quiet_nan_f32(isnan(a) ? a : b);
480
1.21M
    }
481
2.78M
    if (M3_UNLIKELY(a == 0 and a == b)) {
482
573k
        return signbit(a) ? b : a;
483
573k
    }
484
2.20M
    return a > b ? a : b;
485
2.78M
}
486
487
static inline
488
f64 min_f64 (f64 a, f64 b)
489
1.72M
{
490
1.72M
    if (M3_UNLIKELY(isnan(a) or isnan(b))) {
491
420k
        return quiet_nan_f64(isnan(a) ? a : b);
492
420k
    }
493
1.30M
    if (M3_UNLIKELY(a == 0 and a == b)) {
494
623k
        return signbit(a) ? a : b;
495
623k
    }
496
676k
    return a > b ? b : a;
497
1.30M
}
498
499
static inline
500
f64 max_f64 (f64 a, f64 b)
501
1.64M
{
502
1.64M
    if (M3_UNLIKELY(isnan(a) or isnan(b))) {
503
440k
        return quiet_nan_f64(isnan(a) ? a : b);
504
440k
    }
505
1.20M
    if (M3_UNLIKELY(a == 0 and a == b)) {
506
649k
        return signbit(a) ? b : a;
507
649k
    }
508
554k
    return a > b ? a : b;
509
1.20M
}
510
511
// The NaN the spec calls canonical: quiet, positive, and with nothing in its
512
// payload. Where an operation's result is a NaN, the spec allows any payload and any
513
// sign, and the hardware picks - differently on different architectures. These pin
514
// that down, so that a module's results are the same wherever it runs.
515
//
516
// Only operations the spec says produce an *arithmetic* NaN go through this. abs,
517
// neg and copysign are defined bitwise and carry their operand's payload through by
518
// design; canonicalizing them would be wrong, not merely stricter.
519
#  if d_m3CanonicalNaN
520
521
static inline
522
f32 canon_nan_f32 (f32 arg)
523
{
524
    // arg != arg is the NaN test that does not need <math.h> to have been told to
525
    // keep it, and it is the whole of the cost when the value is not a NaN
526
    if (M3_UNLIKELY(arg != arg)) {
527
        union {
528
            f32 f;
529
            u32 i;
530
        } u;
531
        u.i = 0x7FC00000u;
532
        return u.f;
533
    }
534
    return arg;
535
}
536
537
static inline
538
f64 canon_nan_f64 (f64 arg)
539
{
540
    if (M3_UNLIKELY(arg != arg)) {
541
        union {
542
            f64 f;
543
            u64 i;
544
        } u;
545
        u.i = 0x7FF8000000000000ULL;
546
        return u.f;
547
    }
548
    return arg;
549
}
550
551
#  else
552
553
30.1M
#    define canon_nan_f32(ARG)  (ARG)
554
20.5M
#    define canon_nan_f64(ARG)  (ARG)
555
556
#  endif // d_m3CanonicalNaN
557
558
#endif
559
560
#endif // m3_math_utils_h