int.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  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. #ifndef CARBON_TOOLCHAIN_BASE_INT_H_
  5. #define CARBON_TOOLCHAIN_BASE_INT_H_
  6. #include "common/check.h"
  7. #include "llvm/ADT/APInt.h"
  8. #include "llvm/ADT/SmallVector.h"
  9. #include "toolchain/base/canonical_value_store.h"
  10. #include "toolchain/base/index_base.h"
  11. #include "toolchain/base/mem_usage.h"
  12. #include "toolchain/base/value_store.h"
  13. #include "toolchain/base/yaml.h"
  14. namespace Carbon {
  15. // Forward declare a testing peer so we can friend it.
  16. namespace Testing {
  17. struct IntStoreTestPeer;
  18. } // namespace Testing
  19. // Corresponds to a canonicalized integer value. This is used both for integer
  20. // literal tokens, and integer values in SemIR. These always represent the
  21. // abstract mathematical value -- signed and regardless of the needed precision.
  22. //
  23. // Small values are internalized into the ID itself. Large values are
  24. // represented as an index into an array of `APInt`s with a canonicalized bit
  25. // width. The ID itself can be queried for whether it is a value-embedded-ID or
  26. // an index ID. The ID also provides APIs for extracting either the value or an
  27. // index.
  28. //
  29. // ## Details of the encoding scheme ##
  30. //
  31. // We need all the values from a maximum to minimum, as well as a healthy range
  32. // of indices, to fit within the token ID bits.
  33. //
  34. // We represent this as a signed `TokenIdBits`-bit 2s compliment integer. The
  35. // sign extension from TokenIdBits to a register size can be folded into the
  36. // shift used to extract those bits from compressed bitfield storage.
  37. //
  38. // We then divide the smallest 1/4th of that bit width's space to represent
  39. // indices, and the larger 3/4ths to embedded values. For 23-bits total this
  40. // still gives us 2 million unique integers larger than the embedded ones, which
  41. // would be difficult to fill without exceeding the number of tokens we can lex
  42. // (8 million). For non-token based integers, the indices can continue downward
  43. // to the 32-bit signed integer minimum, supporting approximately 1.998 billion
  44. // unique larger integers.
  45. //
  46. // Note the `None` ID can't be used with a token. This is OK as we expect
  47. // invalid tokens to be *error* tokens and not need to represent an invalid
  48. // integer.
  49. class IntId : public Printable<IntId> {
  50. public:
  51. static constexpr llvm::StringLiteral Label = "int";
  52. // The encoding of integer IDs ensures that IDs associated with tokens during
  53. // lexing can fit into a compressed storage space. We arrange for
  54. // `TokenIdBits` to be the minimum number of bits of storage for token
  55. // associated IDs. The constant is public so the lexer can ensure it reserves
  56. // adequate space.
  57. //
  58. // Note that there may still be IDs either not associated with
  59. // tokens or computed after lexing outside of this range.
  60. static constexpr int TokenIdBits = 23;
  61. static const IntId None;
  62. static auto MakeFromTokenPayload(uint32_t payload) -> IntId {
  63. // Token-associated IDs are signed `TokenIdBits` integers, so force sign
  64. // extension from that bit.
  65. return IntId(static_cast<int32_t>(payload << TokenIdBitsShift) >>
  66. TokenIdBitsShift);
  67. }
  68. // Construct an ID from a raw 32-bit ID value.
  69. static constexpr auto MakeRaw(int32_t raw_id) -> IntId {
  70. return IntId(raw_id);
  71. }
  72. // Tests whether the ID is an embedded value ID.
  73. //
  74. // Note `None` is not an embedded value, so this implies `has_value()` is
  75. // true.
  76. constexpr auto is_embedded_value() const -> bool { return id_ > ZeroIndexId; }
  77. // Tests whether the ID is an index ID.
  78. //
  79. // Note `None` is represented as an index ID, so this is *not* sufficient to
  80. // test `has_value()`.
  81. constexpr auto is_index() const -> bool { return id_ <= ZeroIndexId; }
  82. // Test whether a value is present.
  83. //
  84. // This does not distinguish between embedded values and index IDs, only
  85. // whether some value is present.
  86. constexpr auto has_value() const -> bool { return id_ != NoneId; }
  87. // Converts an ID to the embedded value. Requires that `is_embedded_value()`
  88. // is true.
  89. constexpr auto AsValue() const -> int {
  90. CARBON_DCHECK(is_embedded_value());
  91. return id_;
  92. }
  93. // Converts an ID to an index. Requires that `is_index()` is true.
  94. //
  95. // Note `None` is represented as an index ID, and can be converted here.
  96. constexpr auto AsIndex() const -> int {
  97. CARBON_DCHECK(is_index());
  98. return ZeroIndexId - id_;
  99. }
  100. // Returns the ID formatted as a lex token payload.
  101. constexpr auto AsTokenPayload() const -> uint32_t {
  102. uint32_t payload = id_;
  103. // Ensure this ID round trips as the token payload.
  104. CARBON_DCHECK(*this == MakeFromTokenPayload(payload));
  105. return payload;
  106. }
  107. constexpr auto AsRaw() const -> int32_t { return id_; }
  108. auto Print(llvm::raw_ostream& out) const -> void {
  109. out << Label << "(";
  110. if (is_embedded_value()) {
  111. out << "value: " << AsValue();
  112. } else if (is_index()) {
  113. out << "index: " << AsIndex();
  114. } else {
  115. CARBON_CHECK(!has_value());
  116. out << "<none>";
  117. }
  118. out << ")";
  119. }
  120. friend constexpr auto operator==(IntId lhs, IntId rhs) -> bool {
  121. return lhs.id_ == rhs.id_;
  122. }
  123. friend constexpr auto operator<=>(IntId lhs, IntId rhs)
  124. -> std::strong_ordering {
  125. return lhs.id_ <=> rhs.id_;
  126. }
  127. private:
  128. friend class IntStore;
  129. friend Testing::IntStoreTestPeer;
  130. // The shift needed when adjusting a between a `TokenIdBits`-width integer and
  131. // a 32-bit integer.
  132. static constexpr int TokenIdBitsShift = 32 - TokenIdBits;
  133. // The maximum embedded value in an ID.
  134. static constexpr int32_t MaxValue =
  135. std::numeric_limits<int32_t>::max() >> TokenIdBitsShift;
  136. // The ID value that represents an index of `0`. This is the first ID value
  137. // representing an index, and all indices are `<=` to this.
  138. //
  139. // `ZeroIndexId` is the first index ID, and we encode indices as successive
  140. // negative numbers counting downwards. The setup allows us to both use a
  141. // comparison with this ID to distinguish value and index IDs, and to compute
  142. // the actual index from the ID.
  143. //
  144. // The computation of an index in fact is just a subtraction:
  145. // `ZeroIndexId - id_`. Subtraction is *also* how most CPUs implement the
  146. // comparison, and so all of this ends up carefully constructed to enable very
  147. // small code size when testing for an embedded value and when that test fails
  148. // computing and using the index.
  149. static constexpr int32_t ZeroIndexId =
  150. std::numeric_limits<int32_t>::min() >> (TokenIdBitsShift + 1);
  151. // The minimum embedded value in an ID.
  152. static constexpr int32_t MinValue = ZeroIndexId + 1;
  153. // The `None` ID, which needs to be placed after the largest index, which
  154. // count downwards as IDs so below the smallest index ID, in order to optimize
  155. // the code sequence needed to distinguish between integer and value IDs and
  156. // to convert index IDs into actual indices small.
  157. static constexpr int32_t NoneId = std::numeric_limits<int32_t>::min();
  158. // The `None` index. This is the result of converting a `None` ID into an
  159. // index. We ensure that conversion can be done so that we can simplify the
  160. // code that first tries to use an embedded value, then converts to an index
  161. // and checks that the index is still `None`.
  162. static const int32_t NoneIndex;
  163. // Document the specific values of some of these constants to help visualize
  164. // how the bit patterns map from the above computations.
  165. //
  166. // clang-format off: visualizing bit positions
  167. //
  168. // Each bit is either `T` for part of the token or `P` as part
  169. // of the available payload that we use for the ID:
  170. //
  171. // 0bTTTT'TTTT'TPPP'PPPP'PPPP'PPPP'PPPP'PPPP
  172. static_assert(MaxValue == 0b0000'0000'0011'1111'1111'1111'1111'1111);
  173. static_assert(ZeroIndexId == 0b1111'1111'1110'0000'0000'0000'0000'0000);
  174. static_assert(MinValue == 0b1111'1111'1110'0000'0000'0000'0000'0001);
  175. static_assert(NoneId == 0b1000'0000'0000'0000'0000'0000'0000'0000);
  176. // clang-format on
  177. constexpr explicit IntId(int32_t id) : id_(id) {}
  178. int32_t id_;
  179. };
  180. inline constexpr IntId IntId::None(IntId::NoneId);
  181. // Note that we initialize the `None` index in a constexpr context which
  182. // ensures there is no UB in forming it. This helps ensure all the ID -> index
  183. // conversions are correct because the `None` ID is at the limit of that range.
  184. inline constexpr int32_t IntId::NoneIndex = None.AsIndex();
  185. // A canonicalizing value store with deep optimizations for integers.
  186. //
  187. // This stores integers as abstract, signed mathematical integers. The bit width
  188. // of specific `APInt` values, either as inputs or outputs, is disregarded for
  189. // the purpose of canonicalization and the returned integer may use a very
  190. // different bit width `APInt` than was used when adding. There are also
  191. // optimized paths for adding integer values representable using native integer
  192. // types.
  193. //
  194. // Because the integers in the store are canonicalized with only a minimum bit
  195. // width, there are helper functions to coerce them to a specific desired bit
  196. // width for use.
  197. //
  198. // This leverages a significant optimization for small integer values -- rather
  199. // than canonicalizing and making them unique in a `ValueStore`, they are
  200. // directly embedded in the `IntId` itself. Only larger integers are stored in
  201. // an array of `APInt` values and represented as an index in the ID.
  202. class IntStore {
  203. public:
  204. // We rely on `APInt` values having a minimum bit width.
  205. static constexpr int MinAPWidth = 64;
  206. // The maximum supported bit width of an integer type.
  207. // TODO: Pick a maximum size and document it in the design. For now
  208. // we use 2^^23, because that's the largest size that LLVM supports.
  209. static constexpr int MaxIntWidth = 1 << 23;
  210. // Pick a canonical bit width for the provided number of significant bits.
  211. static auto CanonicalBitWidth(int significant_bits) -> int;
  212. // Accepts a signed `int64_t` and uses the mathematical signed integer value
  213. // of it as the added integer value.
  214. //
  215. // Returns the ID corresponding to this integer value, storing an `APInt` if
  216. // necessary to represent it.
  217. auto Add(int64_t value) -> IntId {
  218. // First try directly making this into an ID.
  219. if (IntId id = TryMakeValue(value); id.has_value()) [[likely]] {
  220. return id;
  221. }
  222. // Fallback for larger values.
  223. return AddLarge(value);
  224. }
  225. // Returns the ID corresponding to this integer value.
  226. auto Add(llvm::APSInt value) -> IntId {
  227. return value.isSigned() ? AddSigned(value) : AddUnsigned(value);
  228. }
  229. // Returns the ID corresponding to this signed integer value, storing an
  230. // `APInt` if necessary to represent it.
  231. auto AddSigned(llvm::APInt value) -> IntId {
  232. // First try directly making this into an ID.
  233. if (IntId id = TryMakeSignedValue(value); id.has_value()) [[likely]] {
  234. return id;
  235. }
  236. // Fallback for larger values.
  237. return AddSignedLarge(std::move(value));
  238. }
  239. // Returns the ID corresponding to an equivalent signed integer value for the
  240. // provided unsigned integer value, storing an `APInt` if necessary to
  241. // represent it.
  242. auto AddUnsigned(llvm::APInt value) -> IntId {
  243. // First try directly making this into an ID.
  244. if (IntId id = TryMakeUnsignedValue(value); id.has_value()) [[likely]] {
  245. return id;
  246. }
  247. // Fallback for larger values.
  248. return AddUnsignedLarge(std::move(value));
  249. }
  250. // Returns the value for an ID.
  251. //
  252. // This will always be a signed `APInt` with a canonical bit width for the
  253. // specific integer value in question.
  254. auto Get(IntId id) const -> llvm::APInt {
  255. if (id.is_embedded_value()) [[likely]] {
  256. return llvm::APInt(MinAPWidth, id.AsValue(), /*isSigned=*/true);
  257. }
  258. return values_.Get(APIntId(id.AsIndex()));
  259. }
  260. // Returns the value for an ID adjusted to a specific bit width.
  261. //
  262. // Note that because we store canonical mathematical integers as signed
  263. // integers, this always sign extends or truncates to the target width. The
  264. // caller can then use that as a signed or unsigned integer as needed.
  265. auto GetAtWidth(IntId id, int bit_width) const -> llvm::APInt {
  266. llvm::APInt value = Get(id);
  267. if (static_cast<int>(value.getBitWidth()) != bit_width) {
  268. value = value.sextOrTrunc(bit_width);
  269. }
  270. return value;
  271. }
  272. // Returns the value for an ID adjusted to the bit width specified with
  273. // another integer ID.
  274. //
  275. // This simply looks up the width integer ID, and then calls the above
  276. // `GetAtWidth` overload using the value found for it. See that overload for
  277. // more details.
  278. auto GetAtWidth(IntId id, IntId bit_width_id) const -> llvm::APInt {
  279. const llvm::APInt bit_width = Get(bit_width_id);
  280. CARBON_CHECK(
  281. bit_width.isStrictlyPositive() && bit_width.isSignedIntN(MinAPWidth),
  282. "Invalid bit width value: {0}", bit_width);
  283. return GetAtWidth(id, bit_width.getSExtValue());
  284. }
  285. // Accepts a signed `int64_t` and uses the mathematical signed integer value
  286. // of it as the integer value to lookup. Returns the canonical ID for that
  287. // value or returns `None` if not in the store.
  288. auto Lookup(int64_t value) const -> IntId {
  289. if (IntId id = TryMakeValue(value); id.has_value()) [[likely]] {
  290. return id;
  291. }
  292. // Fallback for larger values.
  293. return LookupLarge(value);
  294. }
  295. // Looks up the canonical ID for this signed integer value, or returns `None`
  296. // if not in the store.
  297. auto LookupSigned(llvm::APInt value) const -> IntId {
  298. if (IntId id = TryMakeSignedValue(value); id.has_value()) [[likely]] {
  299. return id;
  300. }
  301. // Fallback for larger values.
  302. return LookupSignedLarge(std::move(value));
  303. }
  304. // Output a YAML description of this data structure. Note that this will only
  305. // include the integers that required storing, not those successfully embedded
  306. // into the ID space.
  307. auto OutputYaml() const -> Yaml::OutputMapping;
  308. auto values() const [[clang::lifetimebound]] -> auto {
  309. return values_.values();
  310. }
  311. auto size() const -> size_t { return values_.size(); }
  312. // Collects the memory usage of the separately stored integers.
  313. auto CollectMemUsage(MemUsage& mem_usage, llvm::StringRef label) const
  314. -> void;
  315. private:
  316. friend struct Testing::IntStoreTestPeer;
  317. // Used for `values_`; tracked using `IntId`'s index range.
  318. struct APIntId : IdBase<APIntId> {
  319. static constexpr llvm::StringLiteral Label = "ap_int";
  320. static const APIntId None;
  321. using IdBase::IdBase;
  322. };
  323. static auto MakeIndexOrNone(int index) -> IntId {
  324. CARBON_DCHECK(index >= 0 && index <= IntId::NoneIndex);
  325. return IntId(IntId::ZeroIndexId - index);
  326. }
  327. // Tries to make a signed 64-bit integer into an embedded value in the ID, and
  328. // if unable to do that returns the `None` ID.
  329. static auto TryMakeValue(int64_t value) -> IntId {
  330. if (IntId::MinValue <= value && value <= IntId::MaxValue) {
  331. return IntId(value);
  332. }
  333. return IntId::None;
  334. }
  335. // Tries to make a signed APInt into an embedded value in the ID, and if
  336. // unable to do that returns the `None` ID.
  337. static auto TryMakeSignedValue(llvm::APInt value) -> IntId {
  338. if (value.sge(IntId::MinValue) && value.sle(IntId::MaxValue)) {
  339. return IntId(value.getSExtValue());
  340. }
  341. return IntId::None;
  342. }
  343. // Tries to make an unsigned APInt into an embedded value in the ID, and if
  344. // unable to do that returns the `None` ID.
  345. static auto TryMakeUnsignedValue(llvm::APInt value) -> IntId {
  346. if (value.ule(IntId::MaxValue)) {
  347. return IntId(value.getZExtValue());
  348. }
  349. return IntId::None;
  350. }
  351. // Canonicalize an incoming signed APInt to the correct bit width.
  352. static auto CanonicalizeSigned(llvm::APInt value) -> llvm::APInt;
  353. // Canonicalize an incoming unsigned APInt to the correct bit width.
  354. static auto CanonicalizeUnsigned(llvm::APInt value) -> llvm::APInt;
  355. // Helper functions for handling values that are large enough to require an
  356. // allocated `APInt` for storage. Creating or manipulating that storage is
  357. // only a few lines of code, but we move these out-of-line because the
  358. // generated code is big and harms performance for the non-`Large` common
  359. // case.
  360. auto AddLarge(int64_t value) -> IntId;
  361. auto AddSignedLarge(llvm::APInt value) -> IntId;
  362. auto AddUnsignedLarge(llvm::APInt value) -> IntId;
  363. auto LookupLarge(int64_t value) const -> IntId;
  364. auto LookupSignedLarge(llvm::APInt value) const -> IntId;
  365. // Stores values which don't fit in an IntId. These are always signed.
  366. CanonicalValueStore<APIntId, llvm::APInt> values_;
  367. };
  368. inline constexpr IntStore::APIntId IntStore::APIntId::None(
  369. IntId::None.AsIndex());
  370. } // namespace Carbon
  371. #endif // CARBON_TOOLCHAIN_BASE_INT_H_