int_test.cpp 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #include "toolchain/base/int.h"
  5. #include <gmock/gmock.h>
  6. #include <gtest/gtest.h>
  7. #include <limits>
  8. namespace Carbon::Testing {
  9. struct IntStoreTestPeer {
  10. static constexpr int MinAPWidth = IntStore::MinAPWidth;
  11. static constexpr int32_t MaxIdEmbeddedValue = IntId::MaxValue;
  12. static constexpr int32_t MinIdEmbeddedValue = IntId::MinValue;
  13. };
  14. namespace {
  15. using ::testing::Eq;
  16. static constexpr int MinAPWidth = IntStoreTestPeer::MinAPWidth;
  17. static constexpr int32_t MaxIdEmbeddedValue =
  18. IntStoreTestPeer::MaxIdEmbeddedValue;
  19. static constexpr int32_t MinIdEmbeddedValue =
  20. IntStoreTestPeer::MinIdEmbeddedValue;
  21. TEST(IntStore, Basic) {
  22. IntStore ints;
  23. IntId id_0 = ints.Add(0);
  24. IntId id_1 = ints.Add(1);
  25. IntId id_2 = ints.Add(2);
  26. IntId id_42 = ints.Add(42);
  27. IntId id_n1 = ints.Add(-1);
  28. IntId id_n42 = ints.Add(-42);
  29. IntId id_nines = ints.Add(999'999'999'999);
  30. IntId id_max64 = ints.Add(std::numeric_limits<int64_t>::max());
  31. IntId id_min64 = ints.Add(std::numeric_limits<int64_t>::min());
  32. for (IntId id :
  33. {id_0, id_1, id_2, id_42, id_n1, id_n42, id_nines, id_max64, id_min64}) {
  34. ASSERT_TRUE(id.has_value());
  35. }
  36. // Small values should be embedded.
  37. EXPECT_THAT(id_0.AsValue(), Eq(0));
  38. EXPECT_THAT(id_1.AsValue(), Eq(1));
  39. EXPECT_THAT(id_2.AsValue(), Eq(2));
  40. EXPECT_THAT(id_42.AsValue(), Eq(42));
  41. EXPECT_THAT(id_n1.AsValue(), Eq(-1));
  42. EXPECT_THAT(id_n42.AsValue(), Eq(-42));
  43. // Rest should be indices as they don't fit as embedded values.
  44. EXPECT_TRUE(!id_nines.is_embedded_value());
  45. EXPECT_TRUE(id_nines.is_index());
  46. EXPECT_TRUE(!id_max64.is_embedded_value());
  47. EXPECT_TRUE(id_max64.is_index());
  48. EXPECT_TRUE(!id_min64.is_embedded_value());
  49. EXPECT_TRUE(id_min64.is_index());
  50. // And round tripping all the way through the store should work.
  51. EXPECT_THAT(ints.Get(id_0), Eq(0));
  52. EXPECT_THAT(ints.Get(id_1), Eq(1));
  53. EXPECT_THAT(ints.Get(id_2), Eq(2));
  54. EXPECT_THAT(ints.Get(id_42), Eq(42));
  55. EXPECT_THAT(ints.Get(id_n1), Eq(-1));
  56. EXPECT_THAT(ints.Get(id_n42), Eq(-42));
  57. EXPECT_THAT(ints.Get(id_nines), Eq(999'999'999'999));
  58. EXPECT_THAT(ints.Get(id_max64), Eq(std::numeric_limits<int64_t>::max()));
  59. EXPECT_THAT(ints.Get(id_min64), Eq(std::numeric_limits<int64_t>::min()));
  60. }
  61. // Helper struct to hold test values and the resulting IDs.
  62. struct APAndId {
  63. llvm::APInt ap;
  64. IntId id = IntId::None;
  65. };
  66. TEST(IntStore, APSigned) {
  67. IntStore ints;
  68. llvm::APInt big_128_ap =
  69. llvm::APInt(128, 0x1234'abcd'1234'abcd, /*isSigned=*/true) * 0xabcd'0000;
  70. llvm::APInt max_embedded_ap(MinAPWidth, MaxIdEmbeddedValue,
  71. /*isSigned=*/true);
  72. llvm::APInt min_embedded_ap(MinAPWidth, MinIdEmbeddedValue,
  73. /*isSigned=*/true);
  74. APAndId ap_and_ids[] = {
  75. {.ap = llvm::APInt(MinAPWidth, 1, /*isSigned=*/true)},
  76. {.ap = llvm::APInt(MinAPWidth, 2, /*isSigned=*/true)},
  77. {.ap = llvm::APInt(MinAPWidth, 999'999'999'999, /*isSigned=*/true)},
  78. {.ap = big_128_ap},
  79. {.ap = -big_128_ap},
  80. {.ap =
  81. big_128_ap.sext(512) * big_128_ap.sext(512) * big_128_ap.sext(512)},
  82. {.ap =
  83. -big_128_ap.sext(512) * big_128_ap.sext(512) * big_128_ap.sext(512)},
  84. {.ap = max_embedded_ap},
  85. {.ap = max_embedded_ap + 1},
  86. {.ap = min_embedded_ap},
  87. {.ap = min_embedded_ap - 1},
  88. };
  89. for (auto& [ap, id] : ap_and_ids) {
  90. id = ints.AddSigned(ap);
  91. ASSERT_TRUE(id.has_value()) << ap;
  92. }
  93. for (const auto& [ap, id] : ap_and_ids) {
  94. // The sign extend here may be a no-op, but the original bit width is a
  95. // reliable one at which to do the comparison.
  96. EXPECT_THAT(ints.Get(id).sext(ap.getBitWidth()), Eq(ap));
  97. }
  98. }
  99. TEST(IntStore, APUnsigned) {
  100. IntStore ints;
  101. llvm::APInt big_128_ap =
  102. llvm::APInt(128, 0xabcd'abcd'abcd'abcd) * 0xabcd'0000'abcd'0000;
  103. llvm::APInt max_embedded_ap(MinAPWidth, MaxIdEmbeddedValue);
  104. APAndId ap_and_ids[] = {
  105. {.ap = llvm::APInt(MinAPWidth, 1)},
  106. {.ap = llvm::APInt(MinAPWidth, 2)},
  107. {.ap = llvm::APInt(MinAPWidth, 999'999'999'999)},
  108. {.ap = llvm::APInt(MinAPWidth, std::numeric_limits<uint64_t>::max())},
  109. {.ap = llvm::APInt(MinAPWidth + 1, std::numeric_limits<uint64_t>::max()) +
  110. 1},
  111. {.ap = big_128_ap},
  112. {.ap =
  113. big_128_ap.zext(512) * big_128_ap.zext(512) * big_128_ap.zext(512)},
  114. {.ap = max_embedded_ap},
  115. {.ap = max_embedded_ap + 1},
  116. };
  117. for (auto& [ap, id] : ap_and_ids) {
  118. id = ints.AddUnsigned(ap);
  119. ASSERT_TRUE(id.has_value()) << ap;
  120. }
  121. for (const auto& [ap, id] : ap_and_ids) {
  122. auto stored_ap = ints.Get(id);
  123. // Pick a bit width wide enough to represent both whatever is returned and
  124. // the original value as a *signed* integer without any truncation.
  125. int width = std::max(stored_ap.getBitWidth(), ap.getBitWidth() + 1);
  126. // We sign extend the stored value and zero extend the original number. This
  127. // ensures that anything added as unsigned ends up stored as a positive
  128. // number even when sign extended.
  129. EXPECT_THAT(stored_ap.sext(width), Eq(ap.zext(width)));
  130. }
  131. }
  132. } // namespace
  133. } // namespace Carbon::Testing