mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 22:02:23 +01:00
This switches from `int_store*` to `int*` as this file contains both the ID and the store for integers. This was supposed to be added to #4487 before merging, apologies for missing that.
159 lines
5.2 KiB
C++
159 lines
5.2 KiB
C++
// 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 <gmock/gmock.h>
|
|
#include <gtest/gtest.h>
|
|
|
|
#include <limits>
|
|
|
|
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<int64_t>::max());
|
|
IntId id_min64 = ints.Add(std::numeric_limits<int64_t>::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.is_valid());
|
|
}
|
|
|
|
// 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_value());
|
|
EXPECT_TRUE(id_nines.is_index());
|
|
EXPECT_TRUE(!id_max64.is_value());
|
|
EXPECT_TRUE(id_max64.is_index());
|
|
EXPECT_TRUE(!id_min64.is_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<int64_t>::max()));
|
|
EXPECT_THAT(ints.Get(id_min64), Eq(std::numeric_limits<int64_t>::min()));
|
|
}
|
|
|
|
// Helper struct to hold test values and the resulting IDs.
|
|
struct APAndId {
|
|
llvm::APInt ap;
|
|
IntId id = IntId::Invalid;
|
|
};
|
|
|
|
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.is_valid()) << 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<uint64_t>::max())},
|
|
{.ap = llvm::APInt(MinAPWidth + 1, std::numeric_limits<uint64_t>::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.is_valid()) << 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
|