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