InterpStack.h 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. //===--- InterpStack.h - Stack implementation for the VM --------*- C++ -*-===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. //
  9. // Defines the upwards-growing stack used by the interpreter.
  10. //
  11. //===----------------------------------------------------------------------===//
  12. #ifndef LLVM_CLANG_AST_INTERP_INTERPSTACK_H
  13. #define LLVM_CLANG_AST_INTERP_INTERPSTACK_H
  14. #include "PrimType.h"
  15. #include <memory>
  16. #include <vector>
  17. namespace clang {
  18. namespace interp {
  19. /// Stack frame storing temporaries and parameters.
  20. class InterpStack final {
  21. public:
  22. InterpStack() {}
  23. /// Destroys the stack, freeing up storage.
  24. ~InterpStack();
  25. /// Constructs a value in place on the top of the stack.
  26. template <typename T, typename... Tys> void push(Tys &&... Args) {
  27. new (grow(aligned_size<T>())) T(std::forward<Tys>(Args)...);
  28. #ifndef NDEBUG
  29. ItemTypes.push_back(toPrimType<T>());
  30. #endif
  31. }
  32. /// Returns the value from the top of the stack and removes it.
  33. template <typename T> T pop() {
  34. #ifndef NDEBUG
  35. assert(!ItemTypes.empty());
  36. assert(ItemTypes.back() == toPrimType<T>());
  37. ItemTypes.pop_back();
  38. #endif
  39. auto *Ptr = &peek<T>();
  40. auto Value = std::move(*Ptr);
  41. Ptr->~T();
  42. shrink(aligned_size<T>());
  43. return Value;
  44. }
  45. /// Discards the top value from the stack.
  46. template <typename T> void discard() {
  47. #ifndef NDEBUG
  48. assert(ItemTypes.back() == toPrimType<T>());
  49. ItemTypes.pop_back();
  50. #endif
  51. auto *Ptr = &peek<T>();
  52. Ptr->~T();
  53. shrink(aligned_size<T>());
  54. }
  55. /// Returns a reference to the value on the top of the stack.
  56. template <typename T> T &peek() const {
  57. return *reinterpret_cast<T *>(peek(aligned_size<T>()));
  58. }
  59. /// Returns a pointer to the top object.
  60. void *top() const { return Chunk ? peek(0) : nullptr; }
  61. /// Returns the size of the stack in bytes.
  62. size_t size() const { return StackSize; }
  63. /// Clears the stack without calling any destructors.
  64. void clear();
  65. // Returns whether the stack is empty.
  66. bool empty() const { return StackSize == 0; }
  67. private:
  68. /// All stack slots are aligned to the native pointer alignment for storage.
  69. /// The size of an object is rounded up to a pointer alignment multiple.
  70. template <typename T> constexpr size_t aligned_size() const {
  71. constexpr size_t PtrAlign = alignof(void *);
  72. return ((sizeof(T) + PtrAlign - 1) / PtrAlign) * PtrAlign;
  73. }
  74. /// Grows the stack to accommodate a value and returns a pointer to it.
  75. void *grow(size_t Size);
  76. /// Returns a pointer from the top of the stack.
  77. void *peek(size_t Size) const;
  78. /// Shrinks the stack.
  79. void shrink(size_t Size);
  80. /// Allocate stack space in 1Mb chunks.
  81. static constexpr size_t ChunkSize = 1024 * 1024;
  82. /// Metadata for each stack chunk.
  83. ///
  84. /// The stack is composed of a linked list of chunks. Whenever an allocation
  85. /// is out of bounds, a new chunk is linked. When a chunk becomes empty,
  86. /// it is not immediately freed: a chunk is deallocated only when the
  87. /// predecessor becomes empty.
  88. struct StackChunk {
  89. StackChunk *Next;
  90. StackChunk *Prev;
  91. char *End;
  92. StackChunk(StackChunk *Prev = nullptr)
  93. : Next(nullptr), Prev(Prev), End(reinterpret_cast<char *>(this + 1)) {}
  94. /// Returns the size of the chunk, minus the header.
  95. size_t size() const { return End - start(); }
  96. /// Returns a pointer to the start of the data region.
  97. char *start() { return reinterpret_cast<char *>(this + 1); }
  98. const char *start() const {
  99. return reinterpret_cast<const char *>(this + 1);
  100. }
  101. };
  102. static_assert(sizeof(StackChunk) < ChunkSize, "Invalid chunk size");
  103. /// First chunk on the stack.
  104. StackChunk *Chunk = nullptr;
  105. /// Total size of the stack.
  106. size_t StackSize = 0;
  107. #ifndef NDEBUG
  108. /// vector recording the type of data we pushed into the stack.
  109. std::vector<PrimType> ItemTypes;
  110. template <typename T> static constexpr PrimType toPrimType() {
  111. if constexpr (std::is_same_v<T, Pointer>)
  112. return PT_Ptr;
  113. else if constexpr (std::is_same_v<T, bool> ||
  114. std::is_same_v<T, Boolean>)
  115. return PT_Bool;
  116. else if constexpr (std::is_same_v<T, int8_t> ||
  117. std::is_same_v<T, Integral<8, true>>)
  118. return PT_Sint8;
  119. else if constexpr (std::is_same_v<T, uint8_t> ||
  120. std::is_same_v<T, Integral<8, false>>)
  121. return PT_Uint8;
  122. else if constexpr (std::is_same_v<T, int16_t> ||
  123. std::is_same_v<T, Integral<16, true>>)
  124. return PT_Sint16;
  125. else if constexpr (std::is_same_v<T, uint16_t> ||
  126. std::is_same_v<T, Integral<16, false>>)
  127. return PT_Uint16;
  128. else if constexpr (std::is_same_v<T, int32_t> ||
  129. std::is_same_v<T, Integral<32, true>>)
  130. return PT_Sint32;
  131. else if constexpr (std::is_same_v<T, uint32_t> ||
  132. std::is_same_v<T, Integral<32, false>>)
  133. return PT_Uint32;
  134. else if constexpr (std::is_same_v<T, int64_t> ||
  135. std::is_same_v<T, Integral<64, true>>)
  136. return PT_Sint64;
  137. else if constexpr (std::is_same_v<T, uint64_t> ||
  138. std::is_same_v<T, Integral<64, false>>)
  139. return PT_Uint64;
  140. llvm_unreachable("unknown type push()'ed into InterpStack");
  141. }
  142. #endif
  143. };
  144. } // namespace interp
  145. } // namespace clang
  146. #endif