Coverage Report

Created: 2025-08-29 06:29

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