-
Notifications
You must be signed in to change notification settings - Fork 856
Expand file tree
/
Copy pathint128.cpp
More file actions
120 lines (95 loc) · 3.96 KB
/
int128.cpp
File metadata and controls
120 lines (95 loc) · 3.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// Copyright 2026 WebAssembly Community Group participants
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "support/int128.h"
namespace wasm {
#ifdef __SIZEOF_INT128__
Int128 mul_wide_s(uint64_t lhs, uint64_t rhs) {
__int128 result = static_cast<__int128>(static_cast<int64_t>(lhs)) *
static_cast<__int128>(static_cast<int64_t>(rhs));
return {static_cast<uint64_t>(result >> 64), static_cast<uint64_t>(result)};
}
Int128 mul_wide_u(uint64_t lhs, uint64_t rhs) {
unsigned __int128 result =
static_cast<unsigned __int128>(lhs) * static_cast<unsigned __int128>(rhs);
return {static_cast<uint64_t>(result >> 64), static_cast<uint64_t>(result)};
}
#else
Int128 mul_wide_s(uint64_t lhs, uint64_t rhs) {
return detail::mul_wide_s_fallback(lhs, rhs);
}
Int128 mul_wide_u(uint64_t lhs, uint64_t rhs) {
return detail::mul_wide_u_fallback(lhs, rhs);
}
#endif
namespace detail {
Int128 mul_wide_s_fallback(uint64_t lhs, uint64_t rhs) {
auto [high, low] = mul_wide_u_fallback(lhs, rhs);
// If lhs is negative, then it looks like 2**64 is added to its unsigned value
// so we computed
// (lhs + 2**64) * rhs
// = lhs * rhs + 2**64 * rhs
// We need to subtract 2**64 * rhs, so just subtract rhs directly from the
// high bits.
//
// If lhs AND rhs are negative then we computed
// (lhs + 2**64) * (rhs + 2**64)
// = lhs * rhs + 2**64 * rhs + 2**64 * lhs + 2**128
// The last term overflowed and had no effect, so it's enough to do the two
// subtractions in the two different branches.
if (static_cast<int64_t>(lhs) < 0) {
high -= rhs;
}
if (static_cast<int64_t>(rhs) < 0) {
high -= lhs;
}
return {high, low};
}
Int128 mul_wide_u_fallback(uint64_t lhs, uint64_t rhs) {
// Decompose lhs and rhs into 4 32-bit numbers and distribute to compute:
// (lhsHigh * 2^32 + lhsLow) * (rhsHigh * 2^32 + rhsLow)
//
// (lhsHigh * rhsHigh) * 2^64 [Upper 64 bits]
// (lhsHigh * rhsLow + lhsLow * rhsHigh) * 2^32 [Middle 64 bits]
// (lhsLow * rhsLow) [Lower 64 bits]
uint64_t lhsLow = lhs & 0xffffffff;
uint64_t lhsHigh = lhs >> 32;
uint64_t rhsLow = rhs & 0xffffffff;
uint64_t rhsHigh = rhs >> 32;
uint64_t lowLow = lhsLow * rhsLow;
uint64_t lowHigh = lhsLow * rhsHigh;
uint64_t highLow = lhsHigh * rhsLow;
uint64_t highHigh = lhsHigh * rhsHigh;
// The lowest 32 bits consist only of lowLow (without its carry)
//
// The next 32 bits consist of `lowHigh + highLow + the carry of lowlow`
// (again this may carry to the next 32) Start by adding the carry which is
// guaranteed to not overflow 64 bits. Overflow can't happen because lowHigh
// is max (2**32 - 1)**2 and `lowLow >> 32` is no more than 32 bits, (2**32 -
// 1)**2 + (2**32 -1) < 2**64 - 1
uint64_t highOfLow = (lowLow >> 32) + lowHigh;
// We might have a carry into the next 32 (the low of the high), mask it out
// now so we can add highLow.
uint64_t carry = highOfLow >> 32;
// This is also guaranteed to not overflow by the same logic.
highOfLow = (highOfLow & 0xffffffff) + highLow;
// highOfLow might have exceeded 32 bits again, carry it again
uint64_t carry2 = highOfLow >> 32;
uint64_t lower = (lowLow & 0xffffffff) | (highOfLow << 32);
// No need to worry about overflow here, since 128 bits is always enough to
// store the product of two 64-bit ints.
uint64_t higher = carry + carry2 + highHigh;
return {higher, lower};
}
} // namespace detail
} // namespace wasm