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