/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 |