Coverage Report

Created: 2025-07-01 06:18

/src/WasmEdge/include/common/int128.h
Line
Count
Source (jump to first uncovered line)
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: 2019-2024 Second State INC
3
4
//===-- wasmedge/common/int128.h - 128-bit integer type -------------------===//
5
//
6
// Part of the WasmEdge Project.
7
//
8
//===----------------------------------------------------------------------===//
9
///
10
/// \file
11
/// This file contains the 128-bit integer type.
12
///
13
//===----------------------------------------------------------------------===//
14
#pragma once
15
16
#if defined(_MSC_VER) && !defined(__clang__)
17
#pragma intrinsic(_BitScanReverse)
18
#pragma intrinsic(_BitScanReverse64)
19
#include <immintrin.h>
20
#endif
21
// We have to detect for those environments who don't support __int128 type
22
// natively.
23
#include "endian.h"
24
25
#include <cstdint>
26
#include <limits>
27
#include <stdexcept>
28
29
// Currently, only byte-swapped little endian is handled.
30
#if !WASMEDGE_ENDIAN_LITTLE_BYTE
31
#error unsupported endian!
32
#endif
33
34
namespace WasmEdge {
35
36
0
inline constexpr int clz(uint32_t V) noexcept {
37
0
#if defined(_MSC_VER) && !defined(__clang__)
38
0
  if (V) {
39
0
    unsigned long LeadingZero = 0;
40
0
    _BitScanReverse(&LeadingZero, V);
41
0
    return 31 ^ static_cast<int>(LeadingZero);
42
0
  }
43
0
  return 32;
44
0
#else
45
0
  return __builtin_clz(V);
46
0
#endif
47
0
}
48
49
0
inline constexpr int clz(uint64_t V) noexcept {
50
0
#if defined(_MSC_VER) && !defined(__clang__)
51
0
  if (V) {
52
0
    unsigned long LeadingZero = 0;
53
0
    _BitScanReverse64(&LeadingZero, V);
54
0
    return 63 ^ static_cast<int>(LeadingZero);
55
0
  }
56
0
  return 64;
57
0
#else
58
0
  return __builtin_clzll(V);
59
0
#endif
60
0
}
61
62
inline auto udiv128by64to64(uint64_t U1, uint64_t U0, uint64_t V,
63
0
                            uint64_t &R) noexcept {
64
0
  {
65
0
#if defined(_M_X64) && defined(_MSC_VER) && !defined(__clang__)
66
0
    return _udiv128(U1, U0, V, &R);
67
0
#elif defined(__x86_64__)
68
0
    uint64_t Result = 0;
69
0
    __asm__("divq %[v]" : "=a"(Result), "=d"(R) : [v] "r"(V), "a"(U0), "d"(U1));
70
0
    return Result;
71
0
#endif
72
0
  }
73
0
  const uint32_t V0 = static_cast<uint32_t>(V);
74
0
  const uint32_t V1 = static_cast<uint32_t>(V >> 32);
75
0
  if (V1 == 0) {
76
0
    auto Rem = (U1 << 32) | (U0 >> 32);
77
0
    auto Result = Rem / V0;
78
0
    Rem = ((Rem % V0) << 32) | static_cast<uint32_t>(U0);
79
0
    Result = (Result << 32) | (Rem / V0);
80
0
    R = Rem % V0;
81
0
    return Result;
82
0
  }
83
0
  uint64_t Un64 = 0, Un10 = 0;
84
0
  const auto s = clz(V1);
85
0
  if (s > 0) {
86
0
    V <<= s;
87
0
    Un64 = (U1 << s) | (U0 >> (64 - s));
88
0
    Un10 = U0 << s;
89
0
  } else {
90
0
    Un64 = U1;
91
0
    Un10 = U0;
92
0
  }
93
0
  uint64_t Vn1 = static_cast<uint32_t>(V >> 32);
94
0
  uint64_t Vn0 = static_cast<uint32_t>(V);
95
0
  uint64_t Un1 = static_cast<uint32_t>(Un10 >> 32);
96
0
  uint64_t Un0 = static_cast<uint32_t>(Un10);
97
0
  uint64_t Q1 = Un64 / Vn1;
98
0
  uint64_t Rhat = Un64 - Q1 * Vn1;
99
0
  while ((Q1 >> 32) >= 1 || Q1 * Vn0 > (Rhat << 32) + Un1) {
100
0
    --Q1;
101
0
    Rhat += Vn1;
102
0
    if ((Rhat >> 32) >= 1) {
103
0
      break;
104
0
    }
105
0
  }
106
0
107
0
  uint64_t Un21 = (Un64 << 32) + Un1 - Q1 * V;
108
0
  uint64_t Q0 = Un21 / Vn1;
109
0
  Rhat = Un21 - Q0 * Vn1;
110
0
  while ((Q0 >> 32) >= 1 || Q0 * Vn0 > (Rhat << 32) + Un0) {
111
0
    --Q0;
112
0
    Rhat += Vn1;
113
0
    if ((Rhat >> 32) >= 1) {
114
0
      break;
115
0
    }
116
0
  }
117
0
  R = ((Un21 << 32) + Un0 - Q0 * V) >> s;
118
0
  return (Q1 << 32) + Q0;
119
0
}
120
121
class int128;
122
class uint128;
123
124
class uint128 {
125
public:
126
  uint128() noexcept = default;
127
  constexpr uint128(const uint128 &) noexcept = default;
128
  constexpr uint128(uint128 &&) noexcept = default;
129
  constexpr uint128 &operator=(const uint128 &V) noexcept = default;
130
  constexpr uint128 &operator=(uint128 &&V) noexcept = default;
131
132
0
  constexpr uint128(unsigned int V) noexcept : Low(V), High(0) {}
133
0
  constexpr uint128(unsigned long V) noexcept : Low(V), High(0) {}
134
0
  constexpr uint128(unsigned long long V) noexcept : Low(V), High(0) {}
135
  constexpr uint128(int128 V) noexcept;
136
  constexpr uint128(uint64_t H, uint64_t L) noexcept
137
0
      : Low(L), High(H){}
138
139
#if defined(__x86_64__) || defined(__aarch64__) ||                             \
140
    (defined(__riscv) && __riscv_xlen == 64)
141
        constexpr uint128(unsigned __int128 V) noexcept
142
0
      : Low(static_cast<uint64_t>(V)), High(static_cast<uint64_t>(V >> 64)) {
143
0
  }
144
#endif
145
146
0
  constexpr operator bool() const noexcept {
147
0
    return static_cast<bool>(Low) || static_cast<bool>(High);
148
0
  }
149
0
  constexpr operator uint8_t() const noexcept {
150
0
    return static_cast<uint8_t>(Low);
151
0
  }
152
0
  constexpr operator uint16_t() const noexcept {
153
0
    return static_cast<uint16_t>(Low);
154
0
  }
155
0
  constexpr operator uint32_t() const noexcept {
156
0
    return static_cast<uint32_t>(Low);
157
0
  }
158
0
  constexpr operator uint64_t() const noexcept {
159
0
    return static_cast<uint64_t>(Low);
160
0
  }
161
162
0
  constexpr uint128 &operator=(unsigned int V) noexcept {
163
0
    return *this = uint128(V);
164
0
  }
165
0
  constexpr uint128 &operator=(unsigned long V) noexcept {
166
0
    return *this = uint128(V);
167
0
  }
168
0
  constexpr uint128 &operator=(unsigned long long V) noexcept {
169
0
    return *this = uint128(V);
170
0
  }
171
0
  constexpr uint128 &operator+=(uint128 Other) noexcept {
172
0
    return *this = *this + Other;
173
0
  }
174
0
  constexpr uint128 &operator-=(uint128 Other) noexcept {
175
0
    return *this = *this - Other;
176
0
  }
177
0
  constexpr uint128 &operator*=(uint128 Other) noexcept {
178
0
    return *this = *this * Other;
179
0
  }
180
0
  constexpr uint128 &operator/=(uint128 Other) noexcept {
181
0
    return *this = *this / Other;
182
0
  }
183
0
  constexpr uint128 &operator%=(uint128 Other) noexcept {
184
0
    return *this = *this % Other;
185
0
  }
186
0
  constexpr uint128 &operator&=(uint128 Other) noexcept {
187
0
    return *this = *this & Other;
188
0
  }
189
0
  constexpr uint128 &operator|=(uint128 Other) noexcept {
190
0
    return *this = *this | Other;
191
0
  }
192
0
  constexpr uint128 &operator^=(uint128 Other) noexcept {
193
0
    return *this = *this ^ Other;
194
0
  }
195
0
  constexpr uint128 &operator<<=(unsigned int Other) noexcept {
196
0
    return *this = *this << Other;
197
0
  }
198
0
  constexpr uint128 &operator>>=(unsigned int Other) noexcept {
199
0
    return *this = *this >> Other;
200
0
  }
201
0
  constexpr uint128 &operator<<=(int Other) noexcept {
202
0
    return *this = *this << Other;
203
0
  }
204
0
  constexpr uint128 &operator>>=(int Other) noexcept {
205
0
    return *this = *this >> Other;
206
0
  }
207
208
  constexpr uint128 &operator=(int128 V) noexcept;
209
210
0
  friend constexpr bool operator==(uint128 LHS, uint128 RHS) noexcept {
211
0
    return LHS.Low == RHS.Low && LHS.High == RHS.High;
212
0
  }
213
0
  friend constexpr bool operator<(uint128 LHS, uint128 RHS) noexcept {
214
0
    return LHS.High == RHS.High ? LHS.Low < RHS.Low : LHS.High < RHS.High;
215
0
  }
216
0
  friend constexpr bool operator>(uint128 LHS, uint128 RHS) noexcept {
217
0
    return RHS < LHS;
218
0
  }
219
0
  friend constexpr bool operator!=(uint128 LHS, uint128 RHS) noexcept {
220
0
    return !(LHS == RHS);
221
0
  }
222
0
  friend constexpr bool operator<=(uint128 LHS, uint128 RHS) noexcept {
223
0
    return !(LHS > RHS);
224
0
  }
225
0
  friend constexpr bool operator>=(uint128 LHS, uint128 RHS) noexcept {
226
0
    return !(LHS < RHS);
227
0
  }
228
229
0
  friend constexpr uint128 operator+(uint128 LHS, uint128 RHS) noexcept {
230
0
    uint64_t Carry =
231
0
        (std::numeric_limits<uint64_t>::max() - LHS.Low) < RHS.Low ? 1 : 0;
232
0
    return uint128(LHS.High + RHS.High + Carry, LHS.Low + RHS.Low);
233
0
  }
234
0
  friend constexpr uint128 operator-(uint128 LHS, uint128 RHS) noexcept {
235
0
    uint64_t Carry = LHS.Low < RHS.Low ? 1 : 0;
236
0
    return uint128(LHS.High - RHS.High - Carry, LHS.Low - RHS.Low);
237
0
  }
238
0
  friend constexpr uint128 operator*(uint128 LHS, uint128 RHS) noexcept {
239
0
    uint64_t A32 = LHS.Low >> 32;
240
0
    uint64_t A00 = LHS.Low & UINT64_C(0xffffffff);
241
0
    uint64_t B32 = RHS.Low >> 32;
242
0
    uint64_t B00 = RHS.Low & UINT64_C(0xffffffff);
243
0
    uint128 Result =
244
0
        uint128(LHS.High * RHS.Low + LHS.Low * RHS.High + A32 * B32, A00 * B00);
245
0
    Result += uint128(A32 * B00) << 32U;
246
0
    Result += uint128(A00 * B32) << 32U;
247
0
    return Result;
248
0
  }
249
0
  friend uint128 operator/(uint128 LHS, uint64_t RHS) noexcept {
250
0
    if (LHS.High == 0 && LHS.Low < RHS) {
251
0
      LHS.Low = 0;
252
0
      return LHS;
253
0
    }
254
0
    if (LHS.High < RHS) {
255
0
      uint64_t Rem = 0;
256
0
      uint64_t QLow = udiv128by64to64(LHS.High, LHS.Low, RHS, Rem);
257
0
      LHS.Low = QLow;
258
0
      LHS.High = 0;
259
0
      return LHS;
260
0
    }
261
0
    uint64_t QHigh = LHS.High / RHS;
262
0
    uint64_t Rem = 0;
263
0
    uint64_t QLow = udiv128by64to64(LHS.High % RHS, LHS.Low, RHS, Rem);
264
0
    LHS.Low = QLow;
265
0
    LHS.High = QHigh;
266
0
    return LHS;
267
0
  }
268
0
  friend constexpr uint128 operator/(uint128 LHS, uint128 RHS) noexcept {
269
0
    if (RHS > LHS) {
270
0
      return 0U;
271
0
    }
272
0
    if (RHS == LHS) {
273
0
      return 1U;
274
0
    }
275
0
    if (RHS.High == 0) {
276
0
      return LHS / RHS.Low;
277
0
    }
278
0
    uint128 Denominator = RHS;
279
0
    uint128 Quotient = 0U;
280
0
    const unsigned int Shift = RHS.clz() - LHS.clz();
281
0
    Denominator <<= Shift;
282
0
    for (unsigned int I = 0U; I <= Shift; ++I) {
283
0
      Quotient <<= 1U;
284
0
      if (LHS >= Denominator) {
285
0
        LHS -= Denominator;
286
0
        Quotient |= 1U;
287
0
      }
288
0
      Denominator >>= 1U;
289
0
    }
290
0
    return Quotient;
291
0
  }
292
0
  friend constexpr uint128 operator%(uint128 LHS, uint128 RHS) noexcept {
293
0
    if (RHS > LHS) {
294
0
      return LHS;
295
0
    }
296
0
    if (RHS == LHS) {
297
0
      return 0U;
298
0
    }
299
0
    uint128 Denominator = RHS;
300
0
    const unsigned int Shift = RHS.clz() - LHS.clz();
301
0
    Denominator <<= Shift;
302
0
    for (unsigned int I = 0; I <= Shift; ++I) {
303
0
      if (LHS >= Denominator) {
304
0
        LHS -= Denominator;
305
0
      }
306
0
      Denominator >>= 1U;
307
0
    }
308
0
    return LHS;
309
0
  }
310
0
  friend constexpr uint128 operator&(uint128 LHS, uint128 RHS) noexcept {
311
0
    return uint128(LHS.High & RHS.High, LHS.Low & RHS.Low);
312
0
  }
313
0
  friend constexpr uint128 operator|(uint128 LHS, uint128 RHS) noexcept {
314
0
    return uint128(LHS.High | RHS.High, LHS.Low | RHS.Low);
315
0
  }
316
0
  friend constexpr uint128 operator^(uint128 LHS, uint128 RHS) noexcept {
317
0
    return uint128(LHS.High ^ RHS.High, LHS.Low ^ RHS.Low);
318
0
  }
319
0
  friend constexpr uint128 operator~(uint128 Value) noexcept {
320
0
    return uint128(~Value.High, ~Value.Low);
321
0
  }
322
  friend constexpr uint128 operator<<(uint128 Value,
323
0
                                      unsigned int Shift) noexcept {
324
0
    if (Shift < 64) {
325
0
      if (Shift != 0) {
326
0
        return uint128((Value.High << Shift) | (Value.Low >> (64 - Shift)),
327
0
                       Value.Low << Shift);
328
0
      }
329
0
      return Value;
330
0
    }
331
0
    return uint128(Value.Low << (Shift - 64), 0);
332
0
  }
333
  friend constexpr uint128 operator>>(uint128 Value,
334
0
                                      unsigned int Shift) noexcept {
335
0
    if (Shift < 64) {
336
0
      if (Shift != 0) {
337
0
        return uint128((Value.High >> Shift),
338
0
                       Value.Low >> Shift | (Value.High << (64 - Shift)));
339
0
      }
340
0
      return Value;
341
0
    }
342
0
    return uint128(0, Value.High >> (Shift - 64));
343
0
  }
344
0
  friend constexpr uint128 operator<<(uint128 Value, int Shift) noexcept {
345
0
    return Value << static_cast<unsigned int>(Shift);
346
0
  }
347
0
  friend constexpr uint128 operator>>(uint128 Value, int Shift) noexcept {
348
0
    return Value >> static_cast<unsigned int>(Shift);
349
0
  }
350
  friend constexpr uint128 operator<<(uint128 Value,
351
0
                                      unsigned long long Shift) noexcept {
352
0
    return Value << static_cast<unsigned int>(Shift);
353
0
  }
354
  friend constexpr uint128 operator>>(uint128 Value,
355
0
                                      unsigned long long Shift) noexcept {
356
0
    return Value >> static_cast<unsigned int>(Shift);
357
0
  }
358
359
0
  static constexpr uint128 numericMin() noexcept {
360
0
    return uint128(std::numeric_limits<uint64_t>::min(),
361
0
                   std::numeric_limits<uint64_t>::min());
362
0
  }
363
0
  static constexpr uint128 numericMax() noexcept {
364
0
    return uint128(std::numeric_limits<uint64_t>::max(),
365
0
                   std::numeric_limits<uint64_t>::max());
366
0
  }
367
368
0
  constexpr uint64_t low() const noexcept { return Low; }
369
0
  constexpr uint64_t high() const noexcept { return High; }
370
0
  constexpr unsigned int clz() const noexcept {
371
0
    if (High) {
372
0
      return static_cast<unsigned int>(WasmEdge::clz(High));
373
0
    }
374
0
    if (Low) {
375
0
      return static_cast<unsigned int>(WasmEdge::clz(Low)) + 64U;
376
0
    }
377
0
    return 128U;
378
0
  }
379
380
private:
381
  uint64_t Low;
382
  uint64_t High;
383
};
384
385
class int128 {
386
public:
387
  int128() noexcept = default;
388
  constexpr int128(const int128 &) noexcept = default;
389
  constexpr int128(int128 &&) noexcept = default;
390
  constexpr int128 &operator=(const int128 &V) noexcept = default;
391
  constexpr int128 &operator=(int128 &&V) noexcept = default;
392
393
  constexpr int128(int V) noexcept
394
0
      : Low(static_cast<uint64_t>(V)), High(V < 0 ? INT64_C(-1) : INT64_C(0)) {}
395
  constexpr int128(long V) noexcept
396
0
      : Low(static_cast<uint64_t>(V)), High(V < 0 ? INT64_C(-1) : INT64_C(0)) {}
397
  constexpr int128(long long V) noexcept
398
0
      : Low(static_cast<uint64_t>(V)), High(V < 0 ? INT64_C(-1) : INT64_C(0)) {}
399
0
  constexpr int128(unsigned int V) noexcept : Low(V), High(INT64_C(0)) {}
400
0
  constexpr int128(unsigned long V) noexcept : Low(V), High(INT64_C(0)) {}
401
0
  constexpr int128(unsigned long long V) noexcept : Low(V), High(INT64_C(0)) {}
402
  constexpr int128(uint128 V) noexcept;
403
  constexpr int128(int64_t H, uint64_t L) noexcept
404
0
      : Low(L), High(H){}
405
#if defined(__x86_64__) || defined(__aarch64__) ||                             \
406
    (defined(__riscv) && __riscv_xlen == 64)
407
        constexpr int128(__int128 V) noexcept
408
0
      : Low(static_cast<uint64_t>(V)), High(V >> 64) {
409
0
  }
410
#endif
411
412
0
  constexpr int128 &operator=(int V) noexcept { return *this = int128(V); }
413
0
  constexpr int128 &operator=(long V) noexcept { return *this = int128(V); }
414
0
  constexpr int128 &operator=(long long V) noexcept {
415
0
    return *this = int128(V);
416
0
  }
417
0
  constexpr int128 &operator=(unsigned int V) noexcept {
418
0
    return *this = int128(V);
419
0
  }
420
0
  constexpr int128 &operator=(unsigned long V) noexcept {
421
0
    return *this = int128(V);
422
0
  }
423
0
  constexpr int128 &operator=(unsigned long long V) noexcept {
424
0
    return *this = int128(V);
425
0
  }
426
427
  constexpr int128 &operator=(uint128 V) noexcept;
428
429
0
  static constexpr int128 numericMin() noexcept {
430
0
    return int128(std::numeric_limits<int64_t>::min(), 0);
431
0
  }
432
0
  static constexpr int128 numericMax() noexcept {
433
0
    return int128(std::numeric_limits<int64_t>::max(),
434
0
                  std::numeric_limits<uint64_t>::max());
435
0
  }
436
437
0
  constexpr uint64_t low() const noexcept { return Low; }
438
0
  constexpr int64_t high() const noexcept { return High; }
439
440
private:
441
  uint64_t Low;
442
  int64_t High;
443
};
444
445
inline constexpr uint128::uint128(int128 V) noexcept
446
    : Low(V.low()), High(static_cast<uint64_t>(V.high())) {}
447
448
0
inline constexpr uint128 &uint128::operator=(int128 V) noexcept {
449
0
  return *this = uint128(V);
450
0
}
451
452
inline constexpr int128::int128(uint128 V) noexcept
453
    : Low(V.low()), High(static_cast<int64_t>(V.high())) {}
454
455
0
inline constexpr int128 &int128::operator=(uint128 V) noexcept {
456
0
  return *this = int128(V);
457
0
}
458
459
} // namespace WasmEdge
460
461
namespace std {
462
template <> class numeric_limits<WasmEdge::uint128> {
463
public:
464
  static constexpr bool is_specialized = true;
465
  static constexpr bool is_signed = false;
466
  static constexpr bool is_integer = true;
467
  static constexpr bool is_exact = true;
468
  static constexpr bool has_infinity = false;
469
  static constexpr bool has_quiet_NaN = false;
470
  static constexpr bool has_signaling_NaN = false;
471
  static constexpr float_denorm_style has_denorm = denorm_absent;
472
  static constexpr bool has_denorm_loss = false;
473
  static constexpr float_round_style round_style = round_toward_zero;
474
  static constexpr bool is_iec559 = false;
475
  static constexpr bool is_bounded = true;
476
  static constexpr bool is_modulo = false;
477
  static constexpr int digits = 127;
478
  static constexpr int digits10 = 38;
479
  static constexpr int max_digits10 = 0;
480
  static constexpr int radix = 2;
481
  static constexpr int min_exponent = 0;
482
  static constexpr int min_exponent10 = 0;
483
  static constexpr int max_exponent = 0;
484
  static constexpr int max_exponent10 = 0;
485
  static constexpr bool traps = numeric_limits<uint64_t>::traps;
486
  static constexpr bool tinyness_before = false;
487
488
0
  static constexpr WasmEdge::uint128 min() {
489
0
    return WasmEdge::uint128::numericMin();
490
0
  }
491
0
  static constexpr WasmEdge::uint128 lowest() {
492
0
    return WasmEdge::uint128::numericMin();
493
0
  }
494
0
  static constexpr WasmEdge::uint128 max() {
495
0
    return WasmEdge::uint128::numericMax();
496
0
  }
497
0
  static constexpr WasmEdge::uint128 epsilon() { return 0U; }
498
0
  static constexpr WasmEdge::uint128 round_error() { return 0U; }
499
0
  static constexpr WasmEdge::uint128 infinity() { return 0U; }
500
0
  static constexpr WasmEdge::uint128 quiet_NaN() { return 0U; }
501
0
  static constexpr WasmEdge::uint128 signaling_NaN() { return 0U; }
502
0
  static constexpr WasmEdge::uint128 denorm_min() { return 0U; }
503
};
504
template <> class numeric_limits<WasmEdge::int128> {
505
public:
506
  static constexpr bool is_specialized = true;
507
  static constexpr bool is_signed = true;
508
  static constexpr bool is_integer = true;
509
  static constexpr bool is_exact = true;
510
  static constexpr bool has_infinity = false;
511
  static constexpr bool has_quiet_NaN = false;
512
  static constexpr bool has_signaling_NaN = false;
513
  static constexpr float_denorm_style has_denorm = denorm_absent;
514
  static constexpr bool has_denorm_loss = false;
515
  static constexpr float_round_style round_style = round_toward_zero;
516
  static constexpr bool is_iec559 = false;
517
  static constexpr bool is_bounded = true;
518
  static constexpr bool is_modulo = false;
519
  static constexpr int digits = 127;
520
  static constexpr int digits10 = 38;
521
  static constexpr int max_digits10 = 0;
522
  static constexpr int radix = 2;
523
  static constexpr int min_exponent = 0;
524
  static constexpr int min_exponent10 = 0;
525
  static constexpr int max_exponent = 0;
526
  static constexpr int max_exponent10 = 0;
527
  static constexpr bool traps = numeric_limits<uint64_t>::traps;
528
  static constexpr bool tinyness_before = false;
529
530
0
  static constexpr WasmEdge::int128 min() {
531
0
    return WasmEdge::int128::numericMin();
532
0
  }
533
0
  static constexpr WasmEdge::int128 lowest() {
534
0
    return WasmEdge::int128::numericMin();
535
0
  }
536
0
  static constexpr WasmEdge::int128 max() {
537
0
    return WasmEdge::int128::numericMax();
538
0
  }
539
0
  static constexpr WasmEdge::int128 epsilon() { return 0; }
540
0
  static constexpr WasmEdge::int128 round_error() { return 0; }
541
0
  static constexpr WasmEdge::int128 infinity() { return 0; }
542
0
  static constexpr WasmEdge::int128 quiet_NaN() { return 0; }
543
0
  static constexpr WasmEdge::int128 signaling_NaN() { return 0; }
544
0
  static constexpr WasmEdge::int128 denorm_min() { return 0; }
545
};
546
} // namespace std
547
548
#include <type_traits>
549
namespace std {
550
template <> struct is_class<WasmEdge::uint128> : std::true_type {};
551
} // namespace std
552
553
namespace WasmEdge {
554
// If there is a built-in type __int128, then use it directly
555
#if defined(__x86_64__) || defined(__aarch64__) ||                             \
556
    (defined(__riscv) && __riscv_xlen == 64)
557
using int128_t = __int128;
558
using uint128_t = unsigned __int128;
559
#else
560
using int128_t = int128;
561
using uint128_t = uint128;
562
#endif
563
} // namespace WasmEdge
564
565
#include <fmt/format.h>
566
567
FMT_BEGIN_NAMESPACE
568
namespace detail {
569
inline constexpr bool operator>=(detail::uint128_fallback LHS,
570
0
                                 unsigned int RHS) {
571
0
  return LHS.high() != 0 || LHS.low() >= static_cast<uint64_t>(RHS);
572
0
}
573
574
inline constexpr bool operator<(detail::uint128_fallback LHS,
575
0
                                unsigned int RHS) {
576
0
  return LHS.high() == 0 && LHS.low() < static_cast<uint64_t>(RHS);
577
0
}
578
579
inline constexpr bool operator<(detail::uint128_fallback LHS,
580
0
                                detail::uint128_fallback RHS) {
581
0
  return LHS.high() < RHS.high() ||
582
0
         (LHS.high() == RHS.high() && LHS.low() < RHS.low());
583
0
}
584
585
inline constexpr detail::uint128_fallback
586
0
operator+(detail::uint128_fallback LHS, unsigned int RHS) {
587
0
  uint64_t NewLow = LHS.low() + RHS;
588
0
  uint64_t NewHigh = LHS.high() + (NewLow < LHS.low());
589
0
  return {NewHigh, NewLow};
590
0
}
591
592
inline constexpr detail::uint128_fallback
593
0
operator-(unsigned int LHS, detail::uint128_fallback RHS) {
594
0
  uint128_fallback Result = RHS;
595
0
  return (~Result) + 1 + LHS;
596
0
}
597
598
inline constexpr detail::uint128_fallback &
599
0
operator/=(detail::uint128_fallback &LHS, unsigned int RHSi) {
600
0
  const uint64_t RHS = static_cast<uint64_t>(RHSi);
601
0
  if (LHS.high() == 0 && LHS.low() < RHS) {
602
0
    LHS = 0;
603
0
    return LHS;
604
0
  }
605
0
  if (LHS.high() < RHS) {
606
0
    uint64_t Rem = 0;
607
0
    uint64_t QLo = WasmEdge::udiv128by64to64(LHS.high(), LHS.low(), RHS, Rem);
608
0
    LHS = QLo;
609
0
    return LHS;
610
0
  }
611
0
  uint64_t QHi = LHS.high() / RHS;
612
0
  uint64_t Rem = 0;
613
0
  uint64_t QLo =
614
0
      WasmEdge::udiv128by64to64(LHS.high() % RHS, LHS.low(), RHS, Rem);
615
0
  LHS = (detail::uint128_t{QHi} << 64u) | QLo;
616
0
  return LHS;
617
0
}
618
619
inline constexpr detail::uint128_fallback
620
0
operator%(detail::uint128_fallback LHS, unsigned int RHSi) {
621
0
  const uint64_t RHS = static_cast<uint64_t>(RHSi);
622
0
  if (LHS.high() == 0 && LHS.low() < RHS) {
623
0
    return LHS;
624
0
  }
625
0
  uint64_t Rem = 0;
626
0
  WasmEdge::udiv128by64to64(LHS.high() % RHS, LHS.low(), RHS, Rem);
627
0
  return Rem;
628
0
}
629
630
0
inline int do_count_digits(detail::uint128_fallback N) {
631
0
  const uint64_t Low = static_cast<uint64_t>(N);
632
0
  const uint64_t High = static_cast<uint64_t>(N >> 64);
633
0
  if (High == 0) {
634
0
    return detail::count_digits(Low);
635
0
  }
636
0
  // Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)).
637
0
  static constexpr uint8_t Bsr2Log10[] = {
638
0
      20, 20, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 24, 24, 24, 25,
639
0
      25, 25, 25, 26, 26, 26, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29,
640
0
      30, 30, 30, 31, 31, 31, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34,
641
0
      35, 35, 35, 35, 36, 36, 36, 37, 37, 37, 38, 38, 38, 38, 39, 39};
642
0
  auto C = WasmEdge::clz(High);
643
0
  auto T = Bsr2Log10[C ^ 63];
644
0
  static constexpr const uint128_t PowersOf10[] = {
645
0
      0,
646
0
      (uint128_t(5ULL) << 64) | uint128_t(7766279631452241920ULL),
647
0
      (uint128_t(54ULL) << 64) | uint128_t(3875820019684212736ULL),
648
0
      (uint128_t(542ULL) << 64) | uint128_t(1864712049423024128ULL),
649
0
      (uint128_t(5421ULL) << 64) | uint128_t(200376420520689664ULL),
650
0
      (uint128_t(54210ULL) << 64) | uint128_t(2003764205206896640ULL),
651
0
      (uint128_t(542101ULL) << 64) | uint128_t(1590897978359414784ULL),
652
0
      (uint128_t(5421010ULL) << 64) | uint128_t(15908979783594147840ULL),
653
0
      (uint128_t(54210108ULL) << 64) | uint128_t(11515845246265065472ULL),
654
0
      (uint128_t(542101086ULL) << 64) | uint128_t(4477988020393345024ULL),
655
0
      (uint128_t(5421010862ULL) << 64) | uint128_t(7886392056514347008ULL),
656
0
      (uint128_t(54210108624ULL) << 64) | uint128_t(5076944270305263616ULL),
657
0
      (uint128_t(542101086242ULL) << 64) | uint128_t(13875954555633532928ULL),
658
0
      (uint128_t(5421010862427ULL) << 64) | uint128_t(9632337040368467968ULL),
659
0
      (uint128_t(54210108624275ULL) << 64) | uint128_t(4089650035136921600ULL),
660
0
      (uint128_t(542101086242752ULL) << 64) | uint128_t(4003012203950112768ULL),
661
0
      (uint128_t(5421010862427522ULL) << 64) |
662
0
          uint128_t(3136633892082024448ULL),
663
0
      (uint128_t(54210108624275221ULL) << 64) |
664
0
          uint128_t(12919594847110692864ULL),
665
0
      (uint128_t(542101086242752217ULL) << 64) |
666
0
          uint128_t(68739955140067328ULL),
667
0
      (uint128_t(5421010862427522170ULL) << 64) |
668
0
          uint128_t(687399551400673280ULL),
669
0
  };
670
0
  return T - (N < PowersOf10[T - 20]);
671
0
}
672
673
0
FMT_CONSTEXPR20 inline int count_digits(detail::uint128_fallback N) {
674
0
  if (!is_constant_evaluated()) {
675
0
    return do_count_digits(N);
676
0
  }
677
0
  return count_digits_fallback(N);
678
0
}
679
680
} // namespace detail
681
682
template <typename Char> struct formatter<WasmEdge::uint128, Char> {
683
private:
684
  detail::dynamic_format_specs<Char> Specs;
685
686
public:
687
0
  template <typename ParseContext> constexpr auto parse(ParseContext &Ctx) {
688
0
#if FMT_VERSION >= 100000
689
0
    return parse_format_specs(Ctx.begin(), Ctx.end(), Specs, Ctx,
690
0
                              detail::type::uint_type);
691
#else
692
    using HandlerType = detail::dynamic_specs_handler<ParseContext>;
693
    detail::specs_checker<HandlerType> Handler(HandlerType(Specs, Ctx),
694
                                               detail::type::uint_type);
695
    return parse_format_specs(Ctx.begin(), Ctx.end(), Handler);
696
#endif
697
0
  }
698
699
  template <typename FormatContext>
700
0
  auto format(WasmEdge::uint128 V, FormatContext &Ctx) const {
701
0
    auto Out = Ctx.out();
702
0
    auto S = Specs;
703
#if FMT_VERSION >= 110100
704
    detail::handle_dynamic_spec(S.dynamic_width(), S.width, S.width_ref, Ctx);
705
    detail::handle_dynamic_spec(S.dynamic_precision(), S.precision,
706
                                S.precision_ref, Ctx);
707
#else
708
0
    detail::handle_dynamic_spec<detail::width_checker>(S.width, S.width_ref,
709
0
                                                       Ctx);
710
0
    detail::handle_dynamic_spec<detail::precision_checker>(
711
0
        S.precision, S.precision_ref, Ctx);
712
0
#endif
713
714
0
    const detail::uint128_t U =
715
0
        (detail::uint128_t{static_cast<uint64_t>(V >> 64)} << 64) |
716
0
        detail::uint128_t{static_cast<uint64_t>(V)};
717
#if FMT_VERSION >= 110100
718
    return detail::write_int<Char>(Out, detail::make_write_int_arg(U, S.sign()),
719
                                   S);
720
#else
721
0
    return detail::write_int<Char>(Out, detail::make_write_int_arg(U, S.sign),
722
0
                                   S, Ctx.locale());
723
0
#endif
724
0
  }
725
};
726
FMT_END_NAMESPACE