• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===- WholeProgramDevirt.h - Whole-program devirt pass ---------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file defines parts of the whole-program devirtualization pass
11 // implementation that may be usefully unit tested.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #ifndef LLVM_TRANSFORMS_IPO_WHOLEPROGRAMDEVIRT_H
16 #define LLVM_TRANSFORMS_IPO_WHOLEPROGRAMDEVIRT_H
17 
18 #include "llvm/IR/Module.h"
19 #include "llvm/IR/PassManager.h"
20 #include <cassert>
21 #include <cstdint>
22 #include <utility>
23 #include <vector>
24 
25 namespace llvm {
26 
27 template <typename T> class ArrayRef;
28 template <typename T> class MutableArrayRef;
29 class Function;
30 class GlobalVariable;
31 class ModuleSummaryIndex;
32 
33 namespace wholeprogramdevirt {
34 
35 // A bit vector that keeps track of which bits are used. We use this to
36 // pack constant values compactly before and after each virtual table.
37 struct AccumBitVector {
38   std::vector<uint8_t> Bytes;
39 
40   // Bits in BytesUsed[I] are 1 if matching bit in Bytes[I] is used, 0 if not.
41   std::vector<uint8_t> BytesUsed;
42 
getPtrToDataAccumBitVector43   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 
51   // Set little-endian value Val with size Size at bit position Pos,
52   // and mark bytes as used.
setLEAccumBitVector53   void setLE(uint64_t Pos, uint64_t Val, uint8_t Size) {
54     assert(Pos % 8 == 0);
55     auto DataUsed = getPtrToData(Pos / 8, Size);
56     for (unsigned I = 0; I != Size; ++I) {
57       DataUsed.first[I] = Val >> (I * 8);
58       assert(!DataUsed.second[I]);
59       DataUsed.second[I] = 0xff;
60     }
61   }
62 
63   // Set big-endian value Val with size Size at bit position Pos,
64   // and mark bytes as used.
setBEAccumBitVector65   void setBE(uint64_t Pos, uint64_t Val, uint8_t Size) {
66     assert(Pos % 8 == 0);
67     auto DataUsed = getPtrToData(Pos / 8, Size);
68     for (unsigned I = 0; I != Size; ++I) {
69       DataUsed.first[Size - I - 1] = Val >> (I * 8);
70       assert(!DataUsed.second[Size - I - 1]);
71       DataUsed.second[Size - I - 1] = 0xff;
72     }
73   }
74 
75   // Set bit at bit position Pos to b and mark bit as used.
setBitAccumBitVector76   void setBit(uint64_t Pos, bool b) {
77     auto DataUsed = getPtrToData(Pos / 8, 1);
78     if (b)
79       *DataUsed.first |= 1 << (Pos % 8);
80     assert(!(*DataUsed.second & (1 << Pos % 8)));
81     *DataUsed.second |= 1 << (Pos % 8);
82   }
83 };
84 
85 // The bits that will be stored before and after a particular vtable.
86 struct VTableBits {
87   // The vtable global.
88   GlobalVariable *GV;
89 
90   // Cache of the vtable's size in bytes.
91   uint64_t ObjectSize = 0;
92 
93   // The bit vector that will be laid out before the vtable. Note that these
94   // bytes are stored in reverse order until the globals are rebuilt. This means
95   // that any values in the array must be stored using the opposite endianness
96   // from the target.
97   AccumBitVector Before;
98 
99   // The bit vector that will be laid out after the vtable.
100   AccumBitVector After;
101 };
102 
103 // Information about a member of a particular type identifier.
104 struct TypeMemberInfo {
105   // The VTableBits for the vtable.
106   VTableBits *Bits;
107 
108   // The offset in bytes from the start of the vtable (i.e. the address point).
109   uint64_t Offset;
110 
111   bool operator<(const TypeMemberInfo &other) const {
112     return Bits < other.Bits || (Bits == other.Bits && Offset < other.Offset);
113   }
114 };
115 
116 // A virtual call target, i.e. an entry in a particular vtable.
117 struct VirtualCallTarget {
118   VirtualCallTarget(Function *Fn, const TypeMemberInfo *TM);
119 
120   // For testing only.
VirtualCallTargetVirtualCallTarget121   VirtualCallTarget(const TypeMemberInfo *TM, bool IsBigEndian)
122       : Fn(nullptr), TM(TM), IsBigEndian(IsBigEndian), WasDevirt(false) {}
123 
124   // The function stored in the vtable.
125   Function *Fn;
126 
127   // A pointer to the type identifier member through which the pointer to Fn is
128   // accessed.
129   const TypeMemberInfo *TM;
130 
131   // When doing virtual constant propagation, this stores the return value for
132   // the function when passed the currently considered argument list.
133   uint64_t RetVal;
134 
135   // Whether the target is big endian.
136   bool IsBigEndian;
137 
138   // Whether at least one call site to the target was devirtualized.
139   bool WasDevirt;
140 
141   // The minimum byte offset before the address point. This covers the bytes in
142   // the vtable object before the address point (e.g. RTTI, access-to-top,
143   // vtables for other base classes) and is equal to the offset from the start
144   // of the vtable object to the address point.
minBeforeBytesVirtualCallTarget145   uint64_t minBeforeBytes() const { return TM->Offset; }
146 
147   // The minimum byte offset after the address point. This covers the bytes in
148   // the vtable object after the address point (e.g. the vtable for the current
149   // class and any later base classes) and is equal to the size of the vtable
150   // object minus the offset from the start of the vtable object to the address
151   // point.
minAfterBytesVirtualCallTarget152   uint64_t minAfterBytes() const { return TM->Bits->ObjectSize - TM->Offset; }
153 
154   // The number of bytes allocated (for the vtable plus the byte array) before
155   // the address point.
allocatedBeforeBytesVirtualCallTarget156   uint64_t allocatedBeforeBytes() const {
157     return minBeforeBytes() + TM->Bits->Before.Bytes.size();
158   }
159 
160   // The number of bytes allocated (for the vtable plus the byte array) after
161   // the address point.
allocatedAfterBytesVirtualCallTarget162   uint64_t allocatedAfterBytes() const {
163     return minAfterBytes() + TM->Bits->After.Bytes.size();
164   }
165 
166   // Set the bit at position Pos before the address point to RetVal.
setBeforeBitVirtualCallTarget167   void setBeforeBit(uint64_t Pos) {
168     assert(Pos >= 8 * minBeforeBytes());
169     TM->Bits->Before.setBit(Pos - 8 * minBeforeBytes(), RetVal);
170   }
171 
172   // Set the bit at position Pos after the address point to RetVal.
setAfterBitVirtualCallTarget173   void setAfterBit(uint64_t Pos) {
174     assert(Pos >= 8 * minAfterBytes());
175     TM->Bits->After.setBit(Pos - 8 * minAfterBytes(), RetVal);
176   }
177 
178   // Set the bytes at position Pos before the address point to RetVal.
179   // Because the bytes in Before are stored in reverse order, we use the
180   // opposite endianness to the target.
setBeforeBytesVirtualCallTarget181   void setBeforeBytes(uint64_t Pos, uint8_t Size) {
182     assert(Pos >= 8 * minBeforeBytes());
183     if (IsBigEndian)
184       TM->Bits->Before.setLE(Pos - 8 * minBeforeBytes(), RetVal, Size);
185     else
186       TM->Bits->Before.setBE(Pos - 8 * minBeforeBytes(), RetVal, Size);
187   }
188 
189   // Set the bytes at position Pos after the address point to RetVal.
setAfterBytesVirtualCallTarget190   void setAfterBytes(uint64_t Pos, uint8_t Size) {
191     assert(Pos >= 8 * minAfterBytes());
192     if (IsBigEndian)
193       TM->Bits->After.setBE(Pos - 8 * minAfterBytes(), RetVal, Size);
194     else
195       TM->Bits->After.setLE(Pos - 8 * minAfterBytes(), RetVal, Size);
196   }
197 };
198 
199 // Find the minimum offset that we may store a value of size Size bits at. If
200 // IsAfter is set, look for an offset before the object, otherwise look for an
201 // offset after the object.
202 uint64_t findLowestOffset(ArrayRef<VirtualCallTarget> Targets, bool IsAfter,
203                           uint64_t Size);
204 
205 // Set the stored value in each of Targets to VirtualCallTarget::RetVal at the
206 // given allocation offset before the vtable address. Stores the computed
207 // byte/bit offset to OffsetByte/OffsetBit.
208 void setBeforeReturnValues(MutableArrayRef<VirtualCallTarget> Targets,
209                            uint64_t AllocBefore, unsigned BitWidth,
210                            int64_t &OffsetByte, uint64_t &OffsetBit);
211 
212 // Set the stored value in each of Targets to VirtualCallTarget::RetVal at the
213 // given allocation offset after the vtable address. Stores the computed
214 // byte/bit offset to OffsetByte/OffsetBit.
215 void setAfterReturnValues(MutableArrayRef<VirtualCallTarget> Targets,
216                           uint64_t AllocAfter, unsigned BitWidth,
217                           int64_t &OffsetByte, uint64_t &OffsetBit);
218 
219 } // end namespace wholeprogramdevirt
220 
221 struct WholeProgramDevirtPass : public PassInfoMixin<WholeProgramDevirtPass> {
222   ModuleSummaryIndex *ExportSummary;
223   const ModuleSummaryIndex *ImportSummary;
WholeProgramDevirtPassWholeProgramDevirtPass224   WholeProgramDevirtPass(ModuleSummaryIndex *ExportSummary,
225                          const ModuleSummaryIndex *ImportSummary)
226       : ExportSummary(ExportSummary), ImportSummary(ImportSummary) {
227     assert(!(ExportSummary && ImportSummary));
228   }
229   PreservedAnalyses run(Module &M, ModuleAnalysisManager &);
230 };
231 
232 } // end namespace llvm
233 
234 #endif // LLVM_TRANSFORMS_IPO_WHOLEPROGRAMDEVIRT_H
235