// Part of the Carbon Language project, under the Apache License v2.0 with LLVM // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/base/int.h" #include #include #include namespace Carbon::Testing { struct IntStoreTestPeer { static constexpr int MinAPWidth = IntStore::MinAPWidth; static constexpr int32_t MaxIdEmbeddedValue = IntId::MaxValue; static constexpr int32_t MinIdEmbeddedValue = IntId::MinValue; }; namespace { using ::testing::Eq; static constexpr int MinAPWidth = IntStoreTestPeer::MinAPWidth; static constexpr int32_t MaxIdEmbeddedValue = IntStoreTestPeer::MaxIdEmbeddedValue; static constexpr int32_t MinIdEmbeddedValue = IntStoreTestPeer::MinIdEmbeddedValue; TEST(IntStore, Basic) { IntStore ints; IntId id_0 = ints.Add(0); IntId id_1 = ints.Add(1); IntId id_2 = ints.Add(2); IntId id_42 = ints.Add(42); IntId id_n1 = ints.Add(-1); IntId id_n42 = ints.Add(-42); IntId id_nines = ints.Add(999'999'999'999); IntId id_max64 = ints.Add(std::numeric_limits::max()); IntId id_min64 = ints.Add(std::numeric_limits::min()); for (IntId id : {id_0, id_1, id_2, id_42, id_n1, id_n42, id_nines, id_max64, id_min64}) { ASSERT_TRUE(id.has_value()); } // Small values should be embedded. EXPECT_THAT(id_0.AsValue(), Eq(0)); EXPECT_THAT(id_1.AsValue(), Eq(1)); EXPECT_THAT(id_2.AsValue(), Eq(2)); EXPECT_THAT(id_42.AsValue(), Eq(42)); EXPECT_THAT(id_n1.AsValue(), Eq(-1)); EXPECT_THAT(id_n42.AsValue(), Eq(-42)); // Rest should be indices as they don't fit as embedded values. EXPECT_TRUE(!id_nines.is_embedded_value()); EXPECT_TRUE(id_nines.is_index()); EXPECT_TRUE(!id_max64.is_embedded_value()); EXPECT_TRUE(id_max64.is_index()); EXPECT_TRUE(!id_min64.is_embedded_value()); EXPECT_TRUE(id_min64.is_index()); // And round tripping all the way through the store should work. EXPECT_THAT(ints.Get(id_0), Eq(0)); EXPECT_THAT(ints.Get(id_1), Eq(1)); EXPECT_THAT(ints.Get(id_2), Eq(2)); EXPECT_THAT(ints.Get(id_42), Eq(42)); EXPECT_THAT(ints.Get(id_n1), Eq(-1)); EXPECT_THAT(ints.Get(id_n42), Eq(-42)); EXPECT_THAT(ints.Get(id_nines), Eq(999'999'999'999)); EXPECT_THAT(ints.Get(id_max64), Eq(std::numeric_limits::max())); EXPECT_THAT(ints.Get(id_min64), Eq(std::numeric_limits::min())); } // Helper struct to hold test values and the resulting IDs. struct APAndId { llvm::APInt ap; IntId id = IntId::None; }; TEST(IntStore, APSigned) { IntStore ints; llvm::APInt big_128_ap = llvm::APInt(128, 0x1234'abcd'1234'abcd, /*isSigned=*/true) * 0xabcd'0000; llvm::APInt max_embedded_ap(MinAPWidth, MaxIdEmbeddedValue, /*isSigned=*/true); llvm::APInt min_embedded_ap(MinAPWidth, MinIdEmbeddedValue, /*isSigned=*/true); APAndId ap_and_ids[] = { {.ap = llvm::APInt(MinAPWidth, 1, /*isSigned=*/true)}, {.ap = llvm::APInt(MinAPWidth, 2, /*isSigned=*/true)}, {.ap = llvm::APInt(MinAPWidth, 999'999'999'999, /*isSigned=*/true)}, {.ap = big_128_ap}, {.ap = -big_128_ap}, {.ap = big_128_ap.sext(512) * big_128_ap.sext(512) * big_128_ap.sext(512)}, {.ap = -big_128_ap.sext(512) * big_128_ap.sext(512) * big_128_ap.sext(512)}, {.ap = max_embedded_ap}, {.ap = max_embedded_ap + 1}, {.ap = min_embedded_ap}, {.ap = min_embedded_ap - 1}, }; for (auto& [ap, id] : ap_and_ids) { id = ints.AddSigned(ap); ASSERT_TRUE(id.has_value()) << ap; } for (const auto& [ap, id] : ap_and_ids) { // The sign extend here may be a no-op, but the original bit width is a // reliable one at which to do the comparison. EXPECT_THAT(ints.Get(id).sext(ap.getBitWidth()), Eq(ap)); } } TEST(IntStore, APUnsigned) { IntStore ints; llvm::APInt big_128_ap = llvm::APInt(128, 0xabcd'abcd'abcd'abcd) * 0xabcd'0000'abcd'0000; llvm::APInt max_embedded_ap(MinAPWidth, MaxIdEmbeddedValue); APAndId ap_and_ids[] = { {.ap = llvm::APInt(MinAPWidth, 1)}, {.ap = llvm::APInt(MinAPWidth, 2)}, {.ap = llvm::APInt(MinAPWidth, 999'999'999'999)}, {.ap = llvm::APInt(MinAPWidth, std::numeric_limits::max())}, {.ap = llvm::APInt(MinAPWidth + 1, std::numeric_limits::max()) + 1}, {.ap = big_128_ap}, {.ap = big_128_ap.zext(512) * big_128_ap.zext(512) * big_128_ap.zext(512)}, {.ap = max_embedded_ap}, {.ap = max_embedded_ap + 1}, }; for (auto& [ap, id] : ap_and_ids) { id = ints.AddUnsigned(ap); ASSERT_TRUE(id.has_value()) << ap; } for (const auto& [ap, id] : ap_and_ids) { auto stored_ap = ints.Get(id); // Pick a bit width wide enough to represent both whatever is returned and // the original value as a *signed* integer without any truncation. int width = std::max(stored_ap.getBitWidth(), ap.getBitWidth() + 1); // We sign extend the stored value and zero extend the original number. This // ensures that anything added as unsigned ends up stored as a positive // number even when sign extended. EXPECT_THAT(stored_ap.sext(width), Eq(ap.zext(width))); } } } // namespace } // namespace Carbon::Testing