| // 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" |
| #include "gtest/gtest.h" |
| #include <limits> |
| |
| using namespace wasm; |
| |
| class Int128MulWideSTest |
| : public ::testing::TestWithParam<Int128 (*)(uint64_t, uint64_t)> {}; |
| |
| TEST_P(Int128MulWideSTest, Basic) { |
| auto mul_s = GetParam(); |
| |
| // Simple cases |
| EXPECT_EQ(mul_s(0, 0), (Int128{0, 0})); |
| EXPECT_EQ(mul_s(1, 1), (Int128{0, 1})); |
| |
| // Mixed sign |
| EXPECT_EQ(mul_s(-1, 1), |
| (Int128{0xffffffffffffffffULL, 0xffffffffffffffffULL})); |
| EXPECT_EQ(mul_s(1, -1), |
| (Int128{0xffffffffffffffffULL, 0xffffffffffffffffULL})); |
| |
| // Double negative |
| EXPECT_EQ(mul_s(-1, -1), (Int128{0, 1})); |
| |
| int64_t maxInt = std::numeric_limits<int64_t>::max(); |
| // Fits in the lower half because the signed bit now goes in the upper half. |
| EXPECT_EQ(mul_s(maxInt, 2), (Int128{0, 0xfffffffffffffffeULL})); |
| EXPECT_EQ(mul_s(maxInt, maxInt), (Int128{0x3fffffffffffffffULL, 1})); |
| |
| // Min Ints (0x8000000000000000) |
| int64_t minInt = std::numeric_limits<int64_t>::min(); |
| EXPECT_EQ(mul_s(minInt, 2), (Int128{0xffffffffffffffffULL, 0})); |
| EXPECT_EQ(mul_s(minInt, minInt), (Int128{0x4000000000000000ULL, 0})); |
| } |
| |
| // Test that our fallback implementation is commutative |
| Int128 mul_wide_s_fallback_reversed(uint64_t lhs, uint64_t rhs) { |
| return detail::mul_wide_s_fallback(rhs, lhs); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(Int128, |
| Int128MulWideSTest, |
| ::testing::Values(mul_wide_s, |
| detail::mul_wide_s_fallback, |
| mul_wide_s_fallback_reversed)); |
| |
| class Int128MulWideUTest |
| : public ::testing::TestWithParam<Int128 (*)(uint64_t, uint64_t)> {}; |
| |
| TEST_P(Int128MulWideUTest, Basic) { |
| auto mul_u = GetParam(); |
| |
| // Simple cases |
| EXPECT_EQ(mul_u(0, 0), (Int128{0, 0})); |
| EXPECT_EQ(mul_u(1, 0), (Int128{0, 0})); |
| EXPECT_EQ(mul_u(1, 1), (Int128{0, 1})); |
| |
| // Max Uint (0xffffffffffffffff) |
| uint64_t maxUint = std::numeric_limits<uint64_t>::max(); |
| EXPECT_EQ(mul_u(maxUint, 2), (Int128{1, 0xfffffffffffffffeULL})); |
| EXPECT_EQ(mul_u(maxUint, maxUint), (Int128{0xfffffffffffffffeULL, 1})); |
| |
| // Max 32-bit uint (0xffffffff) |
| EXPECT_EQ(mul_u(0xffffffffULL, 0xffffffffULL), |
| (Int128{0, 0xfffffffe00000001ULL})); |
| |
| // Exactly 2^32 (0x100000000) - Tests a 1 in the lowest bit of the high half |
| EXPECT_EQ(mul_u(0x100000000ULL, 0x100000000ULL), (Int128{1, 0})); |
| |
| // Mixed boundaries |
| EXPECT_EQ(mul_u(0xffffffffULL, 0x100000000ULL), |
| (Int128{0, 0xffffffff00000000ULL})); |
| |
| // Upper half filled, lower half empty |
| uint64_t highOnly = 0xffffffff00000000ULL; |
| EXPECT_EQ(mul_u(highOnly, 2), (Int128{1, 0xfffffffe00000000ULL})); |
| |
| // Lower half filled, upper half empty |
| uint64_t lowOnly = 0x00000000ffffffffULL; |
| EXPECT_EQ(mul_u(lowOnly, lowOnly), (Int128{0, 0xfffffffe00000001ULL})); |
| } |
| |
| // Test that our fallback implementation is commutative |
| Int128 mul_wide_u_fallback_reversed(uint64_t lhs, uint64_t rhs) { |
| return detail::mul_wide_u_fallback(rhs, lhs); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(Int128, |
| Int128MulWideUTest, |
| ::testing::Values(mul_wide_u, |
| detail::mul_wide_u_fallback, |
| mul_wide_u_fallback_reversed)); |