/src/WasmEdge/include/common/int128.h
Line | Count | Source |
1 | | // SPDX-License-Identifier: Apache-2.0 |
2 | | // SPDX-FileCopyrightText: Copyright The WasmEdge Authors |
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 those environments that 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 | 0 | constexpr uint128(uint64_t H, uint64_t L) noexcept : Low(L), High(H) {} |
132 | | |
133 | | #if defined(__x86_64__) || defined(__aarch64__) || \ |
134 | | (defined(__riscv) && __riscv_xlen == 64) || defined(__s390x__) |
135 | | constexpr uint128(unsigned __int128 V) noexcept |
136 | 0 | : Low(static_cast<uint64_t>(V)), High(static_cast<uint64_t>(V >> 64)) {} |
137 | | #endif |
138 | | |
139 | 0 | constexpr operator bool() const noexcept { |
140 | 0 | return static_cast<bool>(Low) || static_cast<bool>(High); |
141 | 0 | } |
142 | 0 | constexpr operator uint8_t() const noexcept { |
143 | 0 | return static_cast<uint8_t>(Low); |
144 | 0 | } |
145 | 0 | constexpr operator uint16_t() const noexcept { |
146 | 0 | return static_cast<uint16_t>(Low); |
147 | 0 | } |
148 | 0 | constexpr operator uint32_t() const noexcept { |
149 | 0 | return static_cast<uint32_t>(Low); |
150 | 0 | } |
151 | 0 | constexpr operator uint64_t() const noexcept { |
152 | 0 | return static_cast<uint64_t>(Low); |
153 | 0 | } |
154 | | |
155 | 0 | constexpr uint128 &operator=(unsigned int V) noexcept { |
156 | 0 | return *this = uint128(V); |
157 | 0 | } |
158 | 0 | constexpr uint128 &operator=(unsigned long V) noexcept { |
159 | 0 | return *this = uint128(V); |
160 | 0 | } |
161 | 0 | constexpr uint128 &operator=(unsigned long long V) noexcept { |
162 | 0 | return *this = uint128(V); |
163 | 0 | } |
164 | 0 | constexpr uint128 &operator+=(uint128 Other) noexcept { |
165 | 0 | return *this = *this + Other; |
166 | 0 | } |
167 | 0 | constexpr uint128 &operator-=(uint128 Other) noexcept { |
168 | 0 | return *this = *this - Other; |
169 | 0 | } |
170 | 0 | constexpr uint128 &operator*=(uint128 Other) noexcept { |
171 | 0 | return *this = *this * Other; |
172 | 0 | } |
173 | 0 | constexpr uint128 &operator/=(uint128 Other) noexcept { |
174 | 0 | return *this = *this / Other; |
175 | 0 | } |
176 | 0 | constexpr uint128 &operator%=(uint128 Other) noexcept { |
177 | 0 | return *this = *this % Other; |
178 | 0 | } |
179 | 0 | constexpr uint128 &operator&=(uint128 Other) noexcept { |
180 | 0 | return *this = *this & Other; |
181 | 0 | } |
182 | 0 | constexpr uint128 &operator|=(uint128 Other) noexcept { |
183 | 0 | return *this = *this | Other; |
184 | 0 | } |
185 | 0 | constexpr uint128 &operator^=(uint128 Other) noexcept { |
186 | 0 | return *this = *this ^ Other; |
187 | 0 | } |
188 | 0 | constexpr uint128 &operator<<=(unsigned int Other) noexcept { |
189 | 0 | return *this = *this << Other; |
190 | 0 | } |
191 | 0 | constexpr uint128 &operator>>=(unsigned int Other) noexcept { |
192 | 0 | return *this = *this >> Other; |
193 | 0 | } |
194 | 0 | constexpr uint128 &operator<<=(int Other) noexcept { |
195 | 0 | return *this = *this << Other; |
196 | 0 | } |
197 | 0 | constexpr uint128 &operator>>=(int Other) noexcept { |
198 | 0 | return *this = *this >> Other; |
199 | 0 | } |
200 | | |
201 | | constexpr uint128 &operator=(int128 V) noexcept; |
202 | | |
203 | 0 | friend constexpr bool operator==(uint128 LHS, uint128 RHS) noexcept { |
204 | 0 | return LHS.Low == RHS.Low && LHS.High == RHS.High; |
205 | 0 | } |
206 | 0 | friend constexpr bool operator<(uint128 LHS, uint128 RHS) noexcept { |
207 | 0 | return LHS.High == RHS.High ? LHS.Low < RHS.Low : LHS.High < RHS.High; |
208 | 0 | } |
209 | 0 | friend constexpr bool operator>(uint128 LHS, uint128 RHS) noexcept { |
210 | 0 | return RHS < LHS; |
211 | 0 | } |
212 | 0 | friend constexpr bool operator!=(uint128 LHS, uint128 RHS) noexcept { |
213 | 0 | return !(LHS == RHS); |
214 | 0 | } |
215 | 0 | friend constexpr bool operator<=(uint128 LHS, uint128 RHS) noexcept { |
216 | 0 | return !(LHS > RHS); |
217 | 0 | } |
218 | 0 | friend constexpr bool operator>=(uint128 LHS, uint128 RHS) noexcept { |
219 | 0 | return !(LHS < RHS); |
220 | 0 | } |
221 | | |
222 | 0 | friend constexpr uint128 operator+(uint128 LHS, uint128 RHS) noexcept { |
223 | 0 | uint64_t Carry = |
224 | 0 | (std::numeric_limits<uint64_t>::max() - LHS.Low) < RHS.Low ? 1 : 0; |
225 | 0 | return uint128(LHS.High + RHS.High + Carry, LHS.Low + RHS.Low); |
226 | 0 | } |
227 | 0 | friend constexpr uint128 operator-(uint128 LHS, uint128 RHS) noexcept { |
228 | 0 | uint64_t Carry = LHS.Low < RHS.Low ? 1 : 0; |
229 | 0 | return uint128(LHS.High - RHS.High - Carry, LHS.Low - RHS.Low); |
230 | 0 | } |
231 | 0 | friend constexpr uint128 operator*(uint128 LHS, uint128 RHS) noexcept { |
232 | 0 | uint64_t A32 = LHS.Low >> 32; |
233 | 0 | uint64_t A00 = LHS.Low & UINT64_C(0xffffffff); |
234 | 0 | uint64_t B32 = RHS.Low >> 32; |
235 | 0 | uint64_t B00 = RHS.Low & UINT64_C(0xffffffff); |
236 | 0 | uint128 Result = |
237 | 0 | uint128(LHS.High * RHS.Low + LHS.Low * RHS.High + A32 * B32, A00 * B00); |
238 | 0 | Result += uint128(A32 * B00) << 32U; |
239 | 0 | Result += uint128(A00 * B32) << 32U; |
240 | 0 | return Result; |
241 | 0 | } |
242 | 0 | friend uint128 operator/(uint128 LHS, uint64_t RHS) noexcept { |
243 | 0 | if (LHS.High == 0 && LHS.Low < RHS) { |
244 | 0 | LHS.Low = 0; |
245 | 0 | return LHS; |
246 | 0 | } |
247 | 0 | if (LHS.High < RHS) { |
248 | 0 | uint64_t Rem = 0; |
249 | 0 | uint64_t QLow = udiv128by64to64(LHS.High, LHS.Low, RHS, Rem); |
250 | 0 | LHS.Low = QLow; |
251 | 0 | LHS.High = 0; |
252 | 0 | return LHS; |
253 | 0 | } |
254 | 0 | uint64_t QHigh = LHS.High / RHS; |
255 | 0 | uint64_t Rem = 0; |
256 | 0 | uint64_t QLow = udiv128by64to64(LHS.High % RHS, LHS.Low, RHS, Rem); |
257 | 0 | LHS.Low = QLow; |
258 | 0 | LHS.High = QHigh; |
259 | 0 | return LHS; |
260 | 0 | } |
261 | 0 | friend constexpr uint128 operator/(uint128 LHS, uint128 RHS) noexcept { |
262 | 0 | if (RHS > LHS) { |
263 | 0 | return 0U; |
264 | 0 | } |
265 | 0 | if (RHS == LHS) { |
266 | 0 | return 1U; |
267 | 0 | } |
268 | 0 | if (RHS.High == 0) { |
269 | 0 | return LHS / RHS.Low; |
270 | 0 | } |
271 | 0 | uint128 Denominator = RHS; |
272 | 0 | uint128 Quotient = 0U; |
273 | 0 | const unsigned int Shift = RHS.clz() - LHS.clz(); |
274 | 0 | Denominator <<= Shift; |
275 | 0 | for (unsigned int I = 0U; I <= Shift; ++I) { |
276 | 0 | Quotient <<= 1U; |
277 | 0 | if (LHS >= Denominator) { |
278 | 0 | LHS -= Denominator; |
279 | 0 | Quotient |= 1U; |
280 | 0 | } |
281 | 0 | Denominator >>= 1U; |
282 | 0 | } |
283 | 0 | return Quotient; |
284 | 0 | } |
285 | 0 | friend constexpr uint128 operator%(uint128 LHS, uint128 RHS) noexcept { |
286 | 0 | if (RHS > LHS) { |
287 | 0 | return LHS; |
288 | 0 | } |
289 | 0 | if (RHS == LHS) { |
290 | 0 | return 0U; |
291 | 0 | } |
292 | 0 | uint128 Denominator = RHS; |
293 | 0 | const unsigned int Shift = RHS.clz() - LHS.clz(); |
294 | 0 | Denominator <<= Shift; |
295 | 0 | for (unsigned int I = 0; I <= Shift; ++I) { |
296 | 0 | if (LHS >= Denominator) { |
297 | 0 | LHS -= Denominator; |
298 | 0 | } |
299 | 0 | Denominator >>= 1U; |
300 | 0 | } |
301 | 0 | return LHS; |
302 | 0 | } |
303 | 0 | friend constexpr uint128 operator&(uint128 LHS, uint128 RHS) noexcept { |
304 | 0 | return uint128(LHS.High & RHS.High, LHS.Low & RHS.Low); |
305 | 0 | } |
306 | 0 | friend constexpr uint128 operator|(uint128 LHS, uint128 RHS) noexcept { |
307 | 0 | return uint128(LHS.High | RHS.High, LHS.Low | RHS.Low); |
308 | 0 | } |
309 | 0 | friend constexpr uint128 operator^(uint128 LHS, uint128 RHS) noexcept { |
310 | 0 | return uint128(LHS.High ^ RHS.High, LHS.Low ^ RHS.Low); |
311 | 0 | } |
312 | 0 | friend constexpr uint128 operator~(uint128 Value) noexcept { |
313 | 0 | return uint128(~Value.High, ~Value.Low); |
314 | 0 | } |
315 | | friend constexpr uint128 operator<<(uint128 Value, |
316 | 0 | unsigned int Shift) noexcept { |
317 | 0 | if (Shift < 64) { |
318 | 0 | if (Shift != 0) { |
319 | 0 | return uint128((Value.High << Shift) | (Value.Low >> (64 - Shift)), |
320 | 0 | Value.Low << Shift); |
321 | 0 | } |
322 | 0 | return Value; |
323 | 0 | } |
324 | 0 | return uint128(Value.Low << (Shift - 64), 0); |
325 | 0 | } |
326 | | friend constexpr uint128 operator>>(uint128 Value, |
327 | 0 | unsigned int Shift) noexcept { |
328 | 0 | if (Shift < 64) { |
329 | 0 | if (Shift != 0) { |
330 | 0 | return uint128((Value.High >> Shift), |
331 | 0 | Value.Low >> Shift | (Value.High << (64 - Shift))); |
332 | 0 | } |
333 | 0 | return Value; |
334 | 0 | } |
335 | 0 | return uint128(0, Value.High >> (Shift - 64)); |
336 | 0 | } |
337 | 0 | friend constexpr uint128 operator<<(uint128 Value, int Shift) noexcept { |
338 | 0 | return Value << static_cast<unsigned int>(Shift); |
339 | 0 | } |
340 | 0 | friend constexpr uint128 operator>>(uint128 Value, int Shift) noexcept { |
341 | 0 | return Value >> static_cast<unsigned int>(Shift); |
342 | 0 | } |
343 | | friend constexpr uint128 operator<<(uint128 Value, |
344 | 0 | unsigned long long Shift) noexcept { |
345 | 0 | return Value << static_cast<unsigned int>(Shift); |
346 | 0 | } |
347 | | friend constexpr uint128 operator>>(uint128 Value, |
348 | 0 | unsigned long long Shift) noexcept { |
349 | 0 | return Value >> static_cast<unsigned int>(Shift); |
350 | 0 | } |
351 | | |
352 | 0 | static constexpr uint128 numericMin() noexcept { |
353 | 0 | return uint128(std::numeric_limits<uint64_t>::min(), |
354 | 0 | std::numeric_limits<uint64_t>::min()); |
355 | 0 | } |
356 | 0 | static constexpr uint128 numericMax() noexcept { |
357 | 0 | return uint128(std::numeric_limits<uint64_t>::max(), |
358 | 0 | std::numeric_limits<uint64_t>::max()); |
359 | 0 | } |
360 | | |
361 | 0 | constexpr uint64_t low() const noexcept { return Low; } |
362 | 0 | constexpr uint64_t high() const noexcept { return High; } |
363 | 0 | constexpr unsigned int clz() const noexcept { |
364 | 0 | if (High) { |
365 | 0 | return static_cast<unsigned int>(WasmEdge::clz(High)); |
366 | 0 | } |
367 | 0 | if (Low) { |
368 | 0 | return static_cast<unsigned int>(WasmEdge::clz(Low)) + 64U; |
369 | 0 | } |
370 | 0 | return 128U; |
371 | 0 | } |
372 | | |
373 | | private: |
374 | | uint64_t Low; |
375 | | uint64_t High; |
376 | | }; |
377 | | |
378 | | class int128 { |
379 | | public: |
380 | | int128() noexcept = default; |
381 | | constexpr int128(const int128 &) noexcept = default; |
382 | | constexpr int128(int128 &&) noexcept = default; |
383 | | constexpr int128 &operator=(const int128 &V) noexcept = default; |
384 | | constexpr int128 &operator=(int128 &&V) noexcept = default; |
385 | | |
386 | | constexpr int128(int V) noexcept |
387 | 0 | : Low(static_cast<uint64_t>(V)), High(V < 0 ? INT64_C(-1) : INT64_C(0)) {} |
388 | | constexpr int128(long V) noexcept |
389 | 0 | : Low(static_cast<uint64_t>(V)), High(V < 0 ? INT64_C(-1) : INT64_C(0)) {} |
390 | | constexpr int128(long long V) noexcept |
391 | 0 | : Low(static_cast<uint64_t>(V)), High(V < 0 ? INT64_C(-1) : INT64_C(0)) {} |
392 | 0 | constexpr int128(unsigned int V) noexcept : Low(V), High(INT64_C(0)) {} |
393 | 0 | constexpr int128(unsigned long V) noexcept : Low(V), High(INT64_C(0)) {} |
394 | 0 | constexpr int128(unsigned long long V) noexcept : Low(V), High(INT64_C(0)) {} |
395 | | constexpr int128(uint128 V) noexcept; |
396 | 0 | constexpr int128(int64_t H, uint64_t L) noexcept : Low(L), High(H) {} |
397 | | #if defined(__x86_64__) || defined(__aarch64__) || \ |
398 | | (defined(__riscv) && __riscv_xlen == 64) || defined(__s390x__) |
399 | | constexpr int128(__int128 V) noexcept |
400 | 0 | : Low(static_cast<uint64_t>(V)), High(V >> 64) {} |
401 | | #endif |
402 | | |
403 | 0 | constexpr int128 &operator=(int V) noexcept { return *this = int128(V); } |
404 | 0 | constexpr int128 &operator=(long V) noexcept { return *this = int128(V); } |
405 | 0 | constexpr int128 &operator=(long long V) noexcept { |
406 | 0 | return *this = int128(V); |
407 | 0 | } |
408 | 0 | constexpr int128 &operator=(unsigned int V) noexcept { |
409 | 0 | return *this = int128(V); |
410 | 0 | } |
411 | 0 | constexpr int128 &operator=(unsigned long V) noexcept { |
412 | 0 | return *this = int128(V); |
413 | 0 | } |
414 | 0 | constexpr int128 &operator=(unsigned long long V) noexcept { |
415 | 0 | return *this = int128(V); |
416 | 0 | } |
417 | | |
418 | | constexpr int128 &operator=(uint128 V) noexcept; |
419 | | |
420 | 0 | static constexpr int128 numericMin() noexcept { |
421 | 0 | return int128(std::numeric_limits<int64_t>::min(), 0); |
422 | 0 | } |
423 | 0 | static constexpr int128 numericMax() noexcept { |
424 | 0 | return int128(std::numeric_limits<int64_t>::max(), |
425 | 0 | std::numeric_limits<uint64_t>::max()); |
426 | 0 | } |
427 | | |
428 | 0 | constexpr uint64_t low() const noexcept { return Low; } |
429 | 0 | constexpr int64_t high() const noexcept { return High; } |
430 | | |
431 | | private: |
432 | | uint64_t Low; |
433 | | int64_t High; |
434 | | }; |
435 | | |
436 | | inline constexpr uint128::uint128(int128 V) noexcept |
437 | | : Low(V.low()), High(static_cast<uint64_t>(V.high())) {} |
438 | | |
439 | 0 | inline constexpr uint128 &uint128::operator=(int128 V) noexcept { |
440 | 0 | return *this = uint128(V); |
441 | 0 | } |
442 | | |
443 | | inline constexpr int128::int128(uint128 V) noexcept |
444 | | : Low(V.low()), High(static_cast<int64_t>(V.high())) {} |
445 | | |
446 | 0 | inline constexpr int128 &int128::operator=(uint128 V) noexcept { |
447 | 0 | return *this = int128(V); |
448 | 0 | } |
449 | | |
450 | | } // namespace WasmEdge |
451 | | |
452 | | namespace std { |
453 | | template <> class numeric_limits<WasmEdge::uint128> { |
454 | | public: |
455 | | static constexpr bool is_specialized = true; |
456 | | static constexpr bool is_signed = false; |
457 | | static constexpr bool is_integer = true; |
458 | | static constexpr bool is_exact = true; |
459 | | static constexpr bool has_infinity = false; |
460 | | static constexpr bool has_quiet_NaN = false; |
461 | | static constexpr bool has_signaling_NaN = false; |
462 | | static constexpr float_denorm_style has_denorm = denorm_absent; |
463 | | static constexpr bool has_denorm_loss = false; |
464 | | static constexpr float_round_style round_style = round_toward_zero; |
465 | | static constexpr bool is_iec559 = false; |
466 | | static constexpr bool is_bounded = true; |
467 | | static constexpr bool is_modulo = false; |
468 | | static constexpr int digits = 127; |
469 | | static constexpr int digits10 = 38; |
470 | | static constexpr int max_digits10 = 0; |
471 | | static constexpr int radix = 2; |
472 | | static constexpr int min_exponent = 0; |
473 | | static constexpr int min_exponent10 = 0; |
474 | | static constexpr int max_exponent = 0; |
475 | | static constexpr int max_exponent10 = 0; |
476 | | static constexpr bool traps = numeric_limits<uint64_t>::traps; |
477 | | static constexpr bool tinyness_before = false; |
478 | | |
479 | 0 | static constexpr WasmEdge::uint128 min() { |
480 | 0 | return WasmEdge::uint128::numericMin(); |
481 | 0 | } |
482 | 0 | static constexpr WasmEdge::uint128 lowest() { |
483 | 0 | return WasmEdge::uint128::numericMin(); |
484 | 0 | } |
485 | 0 | static constexpr WasmEdge::uint128 max() { |
486 | 0 | return WasmEdge::uint128::numericMax(); |
487 | 0 | } |
488 | 0 | static constexpr WasmEdge::uint128 epsilon() { return 0U; } |
489 | 0 | static constexpr WasmEdge::uint128 round_error() { return 0U; } |
490 | 0 | static constexpr WasmEdge::uint128 infinity() { return 0U; } |
491 | 0 | static constexpr WasmEdge::uint128 quiet_NaN() { return 0U; } |
492 | 0 | static constexpr WasmEdge::uint128 signaling_NaN() { return 0U; } |
493 | 0 | static constexpr WasmEdge::uint128 denorm_min() { return 0U; } |
494 | | }; |
495 | | template <> class numeric_limits<WasmEdge::int128> { |
496 | | public: |
497 | | static constexpr bool is_specialized = true; |
498 | | static constexpr bool is_signed = true; |
499 | | static constexpr bool is_integer = true; |
500 | | static constexpr bool is_exact = true; |
501 | | static constexpr bool has_infinity = false; |
502 | | static constexpr bool has_quiet_NaN = false; |
503 | | static constexpr bool has_signaling_NaN = false; |
504 | | static constexpr float_denorm_style has_denorm = denorm_absent; |
505 | | static constexpr bool has_denorm_loss = false; |
506 | | static constexpr float_round_style round_style = round_toward_zero; |
507 | | static constexpr bool is_iec559 = false; |
508 | | static constexpr bool is_bounded = true; |
509 | | static constexpr bool is_modulo = false; |
510 | | static constexpr int digits = 127; |
511 | | static constexpr int digits10 = 38; |
512 | | static constexpr int max_digits10 = 0; |
513 | | static constexpr int radix = 2; |
514 | | static constexpr int min_exponent = 0; |
515 | | static constexpr int min_exponent10 = 0; |
516 | | static constexpr int max_exponent = 0; |
517 | | static constexpr int max_exponent10 = 0; |
518 | | static constexpr bool traps = numeric_limits<uint64_t>::traps; |
519 | | static constexpr bool tinyness_before = false; |
520 | | |
521 | 0 | static constexpr WasmEdge::int128 min() { |
522 | 0 | return WasmEdge::int128::numericMin(); |
523 | 0 | } |
524 | 0 | static constexpr WasmEdge::int128 lowest() { |
525 | 0 | return WasmEdge::int128::numericMin(); |
526 | 0 | } |
527 | 0 | static constexpr WasmEdge::int128 max() { |
528 | 0 | return WasmEdge::int128::numericMax(); |
529 | 0 | } |
530 | 0 | static constexpr WasmEdge::int128 epsilon() { return 0; } |
531 | 0 | static constexpr WasmEdge::int128 round_error() { return 0; } |
532 | 0 | static constexpr WasmEdge::int128 infinity() { return 0; } |
533 | 0 | static constexpr WasmEdge::int128 quiet_NaN() { return 0; } |
534 | 0 | static constexpr WasmEdge::int128 signaling_NaN() { return 0; } |
535 | 0 | static constexpr WasmEdge::int128 denorm_min() { return 0; } |
536 | | }; |
537 | | } // namespace std |
538 | | |
539 | | namespace WasmEdge { |
540 | | // If there is a built-in type __int128, then use it directly |
541 | | #if defined(__x86_64__) || defined(__aarch64__) || \ |
542 | | (defined(__riscv) && __riscv_xlen == 64) || defined(__s390x__) |
543 | | using int128_t = __int128; |
544 | | using uint128_t = unsigned __int128; |
545 | | #else |
546 | | using int128_t = int128; |
547 | | using uint128_t = uint128; |
548 | | #endif |
549 | | } // namespace WasmEdge |
550 | | |
551 | | #include <fmt/format.h> |
552 | | |
553 | | FMT_BEGIN_NAMESPACE |
554 | | #if FMT_VERSION >= 90000 |
555 | | namespace detail { |
556 | | #if FMT_VERSION >= 120200 |
557 | | using uint128_fallback = uint128; |
558 | | #endif |
559 | | inline constexpr bool operator>=(detail::uint128_fallback LHS, |
560 | 0 | unsigned int RHS) { |
561 | 0 | return LHS.high() != 0 || LHS.low() >= static_cast<uint64_t>(RHS); |
562 | 0 | } |
563 | | |
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 | detail::uint128_fallback RHS) { |
571 | 0 | return LHS.high() < RHS.high() || |
572 | 0 | (LHS.high() == RHS.high() && LHS.low() < RHS.low()); |
573 | 0 | } |
574 | | |
575 | | inline constexpr detail::uint128_fallback |
576 | 0 | operator+(detail::uint128_fallback LHS, unsigned int RHS) { |
577 | 0 | uint64_t NewLow = LHS.low() + RHS; |
578 | 0 | uint64_t NewHigh = LHS.high() + (NewLow < LHS.low()); |
579 | 0 | return {NewHigh, NewLow}; |
580 | 0 | } |
581 | | |
582 | | inline constexpr detail::uint128_fallback |
583 | 0 | operator-(unsigned int LHS, detail::uint128_fallback RHS) { |
584 | 0 | detail::uint128_fallback Negated = {~RHS.high(), ~RHS.low()}; |
585 | 0 | return Negated + 1 + LHS; |
586 | 0 | } |
587 | | |
588 | | inline constexpr detail::uint128_fallback & |
589 | 0 | operator/=(detail::uint128_fallback &LHS, unsigned int RHSi) { |
590 | 0 | const uint64_t RHS = static_cast<uint64_t>(RHSi); |
591 | 0 | if (LHS.high() == 0 && LHS.low() < RHS) { |
592 | 0 | LHS = 0; |
593 | 0 | return LHS; |
594 | 0 | } |
595 | 0 | if (LHS.high() < RHS) { |
596 | 0 | uint64_t Rem = 0; |
597 | 0 | uint64_t QLo = WasmEdge::udiv128by64to64(LHS.high(), LHS.low(), RHS, Rem); |
598 | 0 | LHS = QLo; |
599 | 0 | return LHS; |
600 | 0 | } |
601 | 0 | uint64_t QHi = LHS.high() / RHS; |
602 | 0 | uint64_t Rem = 0; |
603 | 0 | uint64_t QLo = |
604 | 0 | WasmEdge::udiv128by64to64(LHS.high() % RHS, LHS.low(), RHS, Rem); |
605 | 0 | LHS = (detail::uint128_t{QHi} << 64u) | QLo; |
606 | 0 | return LHS; |
607 | 0 | } |
608 | | |
609 | | inline constexpr detail::uint128_fallback |
610 | 0 | operator%(detail::uint128_fallback LHS, unsigned int RHSi) { |
611 | 0 | const uint64_t RHS = static_cast<uint64_t>(RHSi); |
612 | 0 | if (LHS.high() == 0 && LHS.low() < RHS) { |
613 | 0 | return LHS; |
614 | 0 | } |
615 | 0 | uint64_t Rem = 0; |
616 | 0 | WasmEdge::udiv128by64to64(LHS.high() % RHS, LHS.low(), RHS, Rem); |
617 | 0 | return Rem; |
618 | 0 | } |
619 | | |
620 | 0 | inline int do_count_digits(detail::uint128_fallback N) { |
621 | 0 | const uint64_t Low = static_cast<uint64_t>(N); |
622 | 0 | const uint64_t High = static_cast<uint64_t>(N >> 64); |
623 | 0 | if (High == 0) { |
624 | 0 | return detail::count_digits(Low); |
625 | 0 | } |
626 | 0 | // Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)). |
627 | 0 | static constexpr uint8_t Bsr2Log10[] = { |
628 | 0 | 20, 20, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 24, 24, 24, 25, |
629 | 0 | 25, 25, 25, 26, 26, 26, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, |
630 | 0 | 30, 30, 30, 31, 31, 31, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, |
631 | 0 | 35, 35, 35, 35, 36, 36, 36, 37, 37, 37, 38, 38, 38, 38, 39, 39}; |
632 | 0 | auto C = WasmEdge::clz(High); |
633 | 0 | auto T = Bsr2Log10[C ^ 63]; |
634 | 0 | static constexpr const uint128_t PowersOf10[] = { |
635 | 0 | 0, |
636 | 0 | (uint128_t(5ULL) << 64) | uint128_t(7766279631452241920ULL), |
637 | 0 | (uint128_t(54ULL) << 64) | uint128_t(3875820019684212736ULL), |
638 | 0 | (uint128_t(542ULL) << 64) | uint128_t(1864712049423024128ULL), |
639 | 0 | (uint128_t(5421ULL) << 64) | uint128_t(200376420520689664ULL), |
640 | 0 | (uint128_t(54210ULL) << 64) | uint128_t(2003764205206896640ULL), |
641 | 0 | (uint128_t(542101ULL) << 64) | uint128_t(1590897978359414784ULL), |
642 | 0 | (uint128_t(5421010ULL) << 64) | uint128_t(15908979783594147840ULL), |
643 | 0 | (uint128_t(54210108ULL) << 64) | uint128_t(11515845246265065472ULL), |
644 | 0 | (uint128_t(542101086ULL) << 64) | uint128_t(4477988020393345024ULL), |
645 | 0 | (uint128_t(5421010862ULL) << 64) | uint128_t(7886392056514347008ULL), |
646 | 0 | (uint128_t(54210108624ULL) << 64) | uint128_t(5076944270305263616ULL), |
647 | 0 | (uint128_t(542101086242ULL) << 64) | uint128_t(13875954555633532928ULL), |
648 | 0 | (uint128_t(5421010862427ULL) << 64) | uint128_t(9632337040368467968ULL), |
649 | 0 | (uint128_t(54210108624275ULL) << 64) | uint128_t(4089650035136921600ULL), |
650 | 0 | (uint128_t(542101086242752ULL) << 64) | uint128_t(4003012203950112768ULL), |
651 | 0 | (uint128_t(5421010862427522ULL) << 64) | |
652 | 0 | uint128_t(3136633892082024448ULL), |
653 | 0 | (uint128_t(54210108624275221ULL) << 64) | |
654 | 0 | uint128_t(12919594847110692864ULL), |
655 | 0 | (uint128_t(542101086242752217ULL) << 64) | |
656 | 0 | uint128_t(68739955140067328ULL), |
657 | 0 | (uint128_t(5421010862427522170ULL) << 64) | |
658 | 0 | uint128_t(687399551400673280ULL), |
659 | 0 | }; |
660 | 0 | return T - (N < PowersOf10[T - 20]); |
661 | 0 | } |
662 | | |
663 | 0 | FMT_CONSTEXPR20 inline int count_digits(detail::uint128_fallback N) { |
664 | 0 | if (!is_constant_evaluated()) { |
665 | 0 | return do_count_digits(N); |
666 | 0 | } |
667 | 0 | return count_digits_fallback(N); |
668 | 0 | } |
669 | | |
670 | | } // namespace detail |
671 | | #endif |
672 | | |
673 | | #if FMT_VERSION >= 80000 |
674 | | template <typename Char> struct formatter<WasmEdge::uint128, Char> { |
675 | | private: |
676 | | detail::dynamic_format_specs<Char> Specs; |
677 | | |
678 | | public: |
679 | 0 | template <typename ParseContext> constexpr auto parse(ParseContext &Ctx) { |
680 | 0 | #if FMT_VERSION >= 100000 |
681 | 0 | return parse_format_specs(Ctx.begin(), Ctx.end(), Specs, Ctx, |
682 | 0 | detail::type::uint_type); |
683 | | #else |
684 | | using HandlerType = detail::dynamic_specs_handler<ParseContext>; |
685 | | detail::specs_checker<HandlerType> Handler(HandlerType(Specs, Ctx), |
686 | | detail::type::uint_type); |
687 | | return parse_format_specs(Ctx.begin(), Ctx.end(), Handler); |
688 | | #endif |
689 | 0 | } |
690 | | |
691 | | template <typename FormatContext> |
692 | 0 | auto format(WasmEdge::uint128 V, FormatContext &Ctx) const { |
693 | 0 | auto Out = Ctx.out(); |
694 | 0 | auto S = Specs; |
695 | | #if FMT_VERSION >= 110100 |
696 | | detail::handle_dynamic_spec(S.dynamic_width(), S.width, S.width_ref, Ctx); |
697 | | detail::handle_dynamic_spec(S.dynamic_precision(), S.precision, |
698 | | S.precision_ref, Ctx); |
699 | | #else |
700 | 0 | detail::handle_dynamic_spec<detail::width_checker>(S.width, S.width_ref, |
701 | 0 | Ctx); |
702 | 0 | detail::handle_dynamic_spec<detail::precision_checker>( |
703 | 0 | S.precision, S.precision_ref, Ctx); |
704 | 0 | #endif |
705 | |
|
706 | 0 | const detail::uint128_t U = |
707 | 0 | (detail::uint128_t{static_cast<uint64_t>(V >> 64)} << 64) | |
708 | 0 | detail::uint128_t{static_cast<uint64_t>(V)}; |
709 | | #if FMT_VERSION >= 110100 |
710 | | return detail::write_int<Char>(Out, detail::make_write_int_arg(U, S.sign()), |
711 | | S); |
712 | | #else |
713 | 0 | return detail::write_int<Char>(Out, detail::make_write_int_arg(U, S.sign), |
714 | 0 | S, Ctx.locale()); |
715 | 0 | #endif |
716 | 0 | } |
717 | | }; |
718 | | #else |
719 | | template <typename Char> struct formatter<WasmEdge::uint128, Char> { |
720 | | private: |
721 | | Char Fill = static_cast<Char>(' '); |
722 | | char Align = '\0'; |
723 | | bool Alt = false; |
724 | | bool ZeroPad = false; |
725 | | unsigned int Width = 0U; |
726 | | char Type = '\0'; |
727 | | |
728 | | public: |
729 | | template <typename ParseContext> |
730 | | constexpr auto parse(ParseContext &Ctx) -> decltype(Ctx.begin()) { |
731 | | auto It = Ctx.begin(); |
732 | | const auto End = Ctx.end(); |
733 | | if (It == End || *It == static_cast<Char>('}')) { |
734 | | return It; |
735 | | } |
736 | | const auto IsAlign = [](Char C) { |
737 | | return C == static_cast<Char>('<') || C == static_cast<Char>('>') || |
738 | | C == static_cast<Char>('^'); |
739 | | }; |
740 | | auto Next = It; |
741 | | ++Next; |
742 | | if (Next != End && IsAlign(*Next)) { |
743 | | Fill = *It; |
744 | | Align = static_cast<char>(*Next); |
745 | | It = Next; |
746 | | ++It; |
747 | | } else if (IsAlign(*It)) { |
748 | | Align = static_cast<char>(*It); |
749 | | ++It; |
750 | | } |
751 | | if (It != End && *It == static_cast<Char>('#')) { |
752 | | Alt = true; |
753 | | ++It; |
754 | | } |
755 | | if (It != End && *It == static_cast<Char>('0')) { |
756 | | ZeroPad = true; |
757 | | ++It; |
758 | | } |
759 | | while (It != End && *It >= static_cast<Char>('0') && |
760 | | *It <= static_cast<Char>('9')) { |
761 | | Width = |
762 | | Width * 10U + static_cast<unsigned int>(*It - static_cast<Char>('0')); |
763 | | ++It; |
764 | | } |
765 | | if (It != End && *It != static_cast<Char>('}')) { |
766 | | Type = static_cast<char>(*It); |
767 | | ++It; |
768 | | } |
769 | | return It; |
770 | | } |
771 | | |
772 | | template <typename FormatContext> |
773 | | auto format(WasmEdge::uint128 V, FormatContext &Ctx) -> decltype(Ctx.out()) { |
774 | | const bool IsZeroValue = (V == WasmEdge::uint128(0U)); |
775 | | unsigned int Base = 10U; |
776 | | const char *DigitChars = "0123456789abcdef"; |
777 | | char Prefix[2] = {'\0', '\0'}; |
778 | | switch (Type) { |
779 | | case 'X': |
780 | | DigitChars = "0123456789ABCDEF"; |
781 | | [[fallthrough]]; |
782 | | case 'x': |
783 | | Base = 16U; |
784 | | if (Alt) { |
785 | | Prefix[0] = '0'; |
786 | | Prefix[1] = (Type == 'X') ? 'X' : 'x'; |
787 | | } |
788 | | break; |
789 | | case 'B': |
790 | | case 'b': |
791 | | Base = 2U; |
792 | | if (Alt) { |
793 | | Prefix[0] = '0'; |
794 | | Prefix[1] = (Type == 'B') ? 'B' : 'b'; |
795 | | } |
796 | | break; |
797 | | case 'o': |
798 | | Base = 8U; |
799 | | if (Alt && !IsZeroValue) { |
800 | | Prefix[0] = '0'; |
801 | | } |
802 | | break; |
803 | | default: |
804 | | break; |
805 | | } |
806 | | |
807 | | char Buf[130]; |
808 | | char *Pos = Buf + sizeof(Buf); |
809 | | const WasmEdge::uint128 BaseV(Base); |
810 | | do { |
811 | | *--Pos = DigitChars[static_cast<unsigned int>((V % BaseV).low())]; |
812 | | V /= BaseV; |
813 | | } while (V != WasmEdge::uint128(0U)); |
814 | | |
815 | | const unsigned int NumDigits = |
816 | | static_cast<unsigned int>(Buf + sizeof(Buf) - Pos); |
817 | | const unsigned int PrefixLen = |
818 | | (Prefix[0] != '\0') ? (Prefix[1] != '\0' ? 2U : 1U) : 0U; |
819 | | const unsigned int CoreLen = PrefixLen + NumDigits; |
820 | | |
821 | | auto Out = Ctx.out(); |
822 | | const auto WritePrefix = [&]() { |
823 | | for (unsigned int I = 0U; I < PrefixLen; ++I) { |
824 | | *Out++ = Prefix[I]; |
825 | | } |
826 | | }; |
827 | | const auto WriteDigits = [&]() { |
828 | | for (const char *It = Pos; It != Buf + sizeof(Buf); ++It) { |
829 | | *Out++ = *It; |
830 | | } |
831 | | }; |
832 | | const auto WriteFill = [&](unsigned int Count) { |
833 | | for (unsigned int I = 0U; I < Count; ++I) { |
834 | | *Out++ = Fill; |
835 | | } |
836 | | }; |
837 | | |
838 | | if (Width <= CoreLen) { |
839 | | WritePrefix(); |
840 | | WriteDigits(); |
841 | | return Out; |
842 | | } |
843 | | |
844 | | const unsigned int Pad = Width - CoreLen; |
845 | | if (ZeroPad && Align == '\0') { |
846 | | WritePrefix(); |
847 | | for (unsigned int I = 0U; I < Pad; ++I) { |
848 | | *Out++ = '0'; |
849 | | } |
850 | | WriteDigits(); |
851 | | return Out; |
852 | | } |
853 | | |
854 | | const char Alignment = (Align == '\0') ? '>' : Align; |
855 | | if (Alignment == '<') { |
856 | | WritePrefix(); |
857 | | WriteDigits(); |
858 | | WriteFill(Pad); |
859 | | } else if (Alignment == '^') { |
860 | | const unsigned int Left = Pad / 2U; |
861 | | WriteFill(Left); |
862 | | WritePrefix(); |
863 | | WriteDigits(); |
864 | | WriteFill(Pad - Left); |
865 | | } else { |
866 | | WriteFill(Pad); |
867 | | WritePrefix(); |
868 | | WriteDigits(); |
869 | | } |
870 | | return Out; |
871 | | } |
872 | | }; |
873 | | #endif |
874 | | FMT_END_NAMESPACE |