WholeProgramDevirt.h 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. #pragma once
  2. #ifdef __GNUC__
  3. #pragma GCC diagnostic push
  4. #pragma GCC diagnostic ignored "-Wunused-parameter"
  5. #endif
  6. //===- WholeProgramDevirt.h - Whole-program devirt pass ---------*- C++ -*-===//
  7. //
  8. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  9. // See https://llvm.org/LICENSE.txt for license information.
  10. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  11. //
  12. //===----------------------------------------------------------------------===//
  13. //
  14. // This file defines parts of the whole-program devirtualization pass
  15. // implementation that may be usefully unit tested.
  16. //
  17. //===----------------------------------------------------------------------===//
  18. #ifndef LLVM_TRANSFORMS_IPO_WHOLEPROGRAMDEVIRT_H
  19. #define LLVM_TRANSFORMS_IPO_WHOLEPROGRAMDEVIRT_H
  20. #include "llvm/IR/GlobalValue.h"
  21. #include "llvm/IR/PassManager.h"
  22. #include <cassert>
  23. #include <cstdint>
  24. #include <map>
  25. #include <set>
  26. #include <utility>
  27. #include <vector>
  28. namespace llvm {
  29. class Module;
  30. template <typename T> class ArrayRef;
  31. template <typename T> class MutableArrayRef;
  32. class Function;
  33. class GlobalVariable;
  34. class ModuleSummaryIndex;
  35. struct ValueInfo;
  36. namespace wholeprogramdevirt {
  37. // A bit vector that keeps track of which bits are used. We use this to
  38. // pack constant values compactly before and after each virtual table.
  39. struct AccumBitVector {
  40. std::vector<uint8_t> Bytes;
  41. // Bits in BytesUsed[I] are 1 if matching bit in Bytes[I] is used, 0 if not.
  42. std::vector<uint8_t> BytesUsed;
  43. std::pair<uint8_t *, uint8_t *> getPtrToData(uint64_t Pos, uint8_t Size) {
  44. if (Bytes.size() < Pos + Size) {
  45. Bytes.resize(Pos + Size);
  46. BytesUsed.resize(Pos + Size);
  47. }
  48. return std::make_pair(Bytes.data() + Pos, BytesUsed.data() + Pos);
  49. }
  50. // Set little-endian value Val with size Size at bit position Pos,
  51. // and mark bytes as used.
  52. void setLE(uint64_t Pos, uint64_t Val, uint8_t Size) {
  53. assert(Pos % 8 == 0);
  54. auto DataUsed = getPtrToData(Pos / 8, Size);
  55. for (unsigned I = 0; I != Size; ++I) {
  56. DataUsed.first[I] = Val >> (I * 8);
  57. assert(!DataUsed.second[I]);
  58. DataUsed.second[I] = 0xff;
  59. }
  60. }
  61. // Set big-endian value Val with size Size at bit position Pos,
  62. // and mark bytes as used.
  63. void setBE(uint64_t Pos, uint64_t Val, uint8_t Size) {
  64. assert(Pos % 8 == 0);
  65. auto DataUsed = getPtrToData(Pos / 8, Size);
  66. for (unsigned I = 0; I != Size; ++I) {
  67. DataUsed.first[Size - I - 1] = Val >> (I * 8);
  68. assert(!DataUsed.second[Size - I - 1]);
  69. DataUsed.second[Size - I - 1] = 0xff;
  70. }
  71. }
  72. // Set bit at bit position Pos to b and mark bit as used.
  73. void setBit(uint64_t Pos, bool b) {
  74. auto DataUsed = getPtrToData(Pos / 8, 1);
  75. if (b)
  76. *DataUsed.first |= 1 << (Pos % 8);
  77. assert(!(*DataUsed.second & (1 << Pos % 8)));
  78. *DataUsed.second |= 1 << (Pos % 8);
  79. }
  80. };
  81. // The bits that will be stored before and after a particular vtable.
  82. struct VTableBits {
  83. // The vtable global.
  84. GlobalVariable *GV;
  85. // Cache of the vtable's size in bytes.
  86. uint64_t ObjectSize = 0;
  87. // The bit vector that will be laid out before the vtable. Note that these
  88. // bytes are stored in reverse order until the globals are rebuilt. This means
  89. // that any values in the array must be stored using the opposite endianness
  90. // from the target.
  91. AccumBitVector Before;
  92. // The bit vector that will be laid out after the vtable.
  93. AccumBitVector After;
  94. };
  95. // Information about a member of a particular type identifier.
  96. struct TypeMemberInfo {
  97. // The VTableBits for the vtable.
  98. VTableBits *Bits;
  99. // The offset in bytes from the start of the vtable (i.e. the address point).
  100. uint64_t Offset;
  101. bool operator<(const TypeMemberInfo &other) const {
  102. return Bits < other.Bits || (Bits == other.Bits && Offset < other.Offset);
  103. }
  104. };
  105. // A virtual call target, i.e. an entry in a particular vtable.
  106. struct VirtualCallTarget {
  107. VirtualCallTarget(Function *Fn, const TypeMemberInfo *TM);
  108. // For testing only.
  109. VirtualCallTarget(const TypeMemberInfo *TM, bool IsBigEndian)
  110. : Fn(nullptr), TM(TM), IsBigEndian(IsBigEndian), WasDevirt(false) {}
  111. // The function stored in the vtable.
  112. Function *Fn;
  113. // A pointer to the type identifier member through which the pointer to Fn is
  114. // accessed.
  115. const TypeMemberInfo *TM;
  116. // When doing virtual constant propagation, this stores the return value for
  117. // the function when passed the currently considered argument list.
  118. uint64_t RetVal;
  119. // Whether the target is big endian.
  120. bool IsBigEndian;
  121. // Whether at least one call site to the target was devirtualized.
  122. bool WasDevirt;
  123. // The minimum byte offset before the address point. This covers the bytes in
  124. // the vtable object before the address point (e.g. RTTI, access-to-top,
  125. // vtables for other base classes) and is equal to the offset from the start
  126. // of the vtable object to the address point.
  127. uint64_t minBeforeBytes() const { return TM->Offset; }
  128. // The minimum byte offset after the address point. This covers the bytes in
  129. // the vtable object after the address point (e.g. the vtable for the current
  130. // class and any later base classes) and is equal to the size of the vtable
  131. // object minus the offset from the start of the vtable object to the address
  132. // point.
  133. uint64_t minAfterBytes() const { return TM->Bits->ObjectSize - TM->Offset; }
  134. // The number of bytes allocated (for the vtable plus the byte array) before
  135. // the address point.
  136. uint64_t allocatedBeforeBytes() const {
  137. return minBeforeBytes() + TM->Bits->Before.Bytes.size();
  138. }
  139. // The number of bytes allocated (for the vtable plus the byte array) after
  140. // the address point.
  141. uint64_t allocatedAfterBytes() const {
  142. return minAfterBytes() + TM->Bits->After.Bytes.size();
  143. }
  144. // Set the bit at position Pos before the address point to RetVal.
  145. void setBeforeBit(uint64_t Pos) {
  146. assert(Pos >= 8 * minBeforeBytes());
  147. TM->Bits->Before.setBit(Pos - 8 * minBeforeBytes(), RetVal);
  148. }
  149. // Set the bit at position Pos after the address point to RetVal.
  150. void setAfterBit(uint64_t Pos) {
  151. assert(Pos >= 8 * minAfterBytes());
  152. TM->Bits->After.setBit(Pos - 8 * minAfterBytes(), RetVal);
  153. }
  154. // Set the bytes at position Pos before the address point to RetVal.
  155. // Because the bytes in Before are stored in reverse order, we use the
  156. // opposite endianness to the target.
  157. void setBeforeBytes(uint64_t Pos, uint8_t Size) {
  158. assert(Pos >= 8 * minBeforeBytes());
  159. if (IsBigEndian)
  160. TM->Bits->Before.setLE(Pos - 8 * minBeforeBytes(), RetVal, Size);
  161. else
  162. TM->Bits->Before.setBE(Pos - 8 * minBeforeBytes(), RetVal, Size);
  163. }
  164. // Set the bytes at position Pos after the address point to RetVal.
  165. void setAfterBytes(uint64_t Pos, uint8_t Size) {
  166. assert(Pos >= 8 * minAfterBytes());
  167. if (IsBigEndian)
  168. TM->Bits->After.setBE(Pos - 8 * minAfterBytes(), RetVal, Size);
  169. else
  170. TM->Bits->After.setLE(Pos - 8 * minAfterBytes(), RetVal, Size);
  171. }
  172. };
  173. // Find the minimum offset that we may store a value of size Size bits at. If
  174. // IsAfter is set, look for an offset before the object, otherwise look for an
  175. // offset after the object.
  176. uint64_t findLowestOffset(ArrayRef<VirtualCallTarget> Targets, bool IsAfter,
  177. uint64_t Size);
  178. // Set the stored value in each of Targets to VirtualCallTarget::RetVal at the
  179. // given allocation offset before the vtable address. Stores the computed
  180. // byte/bit offset to OffsetByte/OffsetBit.
  181. void setBeforeReturnValues(MutableArrayRef<VirtualCallTarget> Targets,
  182. uint64_t AllocBefore, unsigned BitWidth,
  183. int64_t &OffsetByte, uint64_t &OffsetBit);
  184. // Set the stored value in each of Targets to VirtualCallTarget::RetVal at the
  185. // given allocation offset after the vtable address. Stores the computed
  186. // byte/bit offset to OffsetByte/OffsetBit.
  187. void setAfterReturnValues(MutableArrayRef<VirtualCallTarget> Targets,
  188. uint64_t AllocAfter, unsigned BitWidth,
  189. int64_t &OffsetByte, uint64_t &OffsetBit);
  190. } // end namespace wholeprogramdevirt
  191. struct WholeProgramDevirtPass : public PassInfoMixin<WholeProgramDevirtPass> {
  192. ModuleSummaryIndex *ExportSummary;
  193. const ModuleSummaryIndex *ImportSummary;
  194. bool UseCommandLine = false;
  195. WholeProgramDevirtPass()
  196. : ExportSummary(nullptr), ImportSummary(nullptr), UseCommandLine(true) {}
  197. WholeProgramDevirtPass(ModuleSummaryIndex *ExportSummary,
  198. const ModuleSummaryIndex *ImportSummary)
  199. : ExportSummary(ExportSummary), ImportSummary(ImportSummary) {
  200. assert(!(ExportSummary && ImportSummary));
  201. }
  202. PreservedAnalyses run(Module &M, ModuleAnalysisManager &);
  203. };
  204. struct VTableSlotSummary {
  205. StringRef TypeID;
  206. uint64_t ByteOffset;
  207. };
  208. bool hasWholeProgramVisibility(bool WholeProgramVisibilityEnabledInLTO);
  209. void updatePublicTypeTestCalls(Module &M,
  210. bool WholeProgramVisibilityEnabledInLTO);
  211. void updateVCallVisibilityInModule(
  212. Module &M, bool WholeProgramVisibilityEnabledInLTO,
  213. const DenseSet<GlobalValue::GUID> &DynamicExportSymbols);
  214. void updateVCallVisibilityInIndex(
  215. ModuleSummaryIndex &Index, bool WholeProgramVisibilityEnabledInLTO,
  216. const DenseSet<GlobalValue::GUID> &DynamicExportSymbols);
  217. /// Perform index-based whole program devirtualization on the \p Summary
  218. /// index. Any devirtualized targets used by a type test in another module
  219. /// are added to the \p ExportedGUIDs set. For any local devirtualized targets
  220. /// only used within the defining module, the information necessary for
  221. /// locating the corresponding WPD resolution is recorded for the ValueInfo
  222. /// in case it is exported by cross module importing (in which case the
  223. /// devirtualized target name will need adjustment).
  224. void runWholeProgramDevirtOnIndex(
  225. ModuleSummaryIndex &Summary, std::set<GlobalValue::GUID> &ExportedGUIDs,
  226. std::map<ValueInfo, std::vector<VTableSlotSummary>> &LocalWPDTargetsMap);
  227. /// Call after cross-module importing to update the recorded single impl
  228. /// devirt target names for any locals that were exported.
  229. void updateIndexWPDForExports(
  230. ModuleSummaryIndex &Summary,
  231. function_ref<bool(StringRef, ValueInfo)> isExported,
  232. std::map<ValueInfo, std::vector<VTableSlotSummary>> &LocalWPDTargetsMap);
  233. } // end namespace llvm
  234. #endif // LLVM_TRANSFORMS_IPO_WHOLEPROGRAMDEVIRT_H
  235. #ifdef __GNUC__
  236. #pragma GCC diagnostic pop
  237. #endif