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 
32 namespace wholeprogramdevirt {
33 
34 // A bit vector that keeps track of which bits are used. We use this to
35 // pack constant values compactly before and after each virtual table.
36 struct AccumBitVector {
37   std::vector<uint8_t> Bytes;
38 
39   // Bits in BytesUsed[I] are 1 if matching bit in Bytes[I] is used, 0 if not.
40   std::vector<uint8_t> BytesUsed;
41 
getPtrToDataAccumBitVector42   std::pair<uint8_t *, uint8_t *> getPtrToData(uint64_t Pos, uint8_t Size) {
43     if (Bytes.size() < Pos + Size) {
44       Bytes.resize(Pos + Size);
45       BytesUsed.resize(Pos + Size);
46     }
47     return std::make_pair(Bytes.data() + Pos, BytesUsed.data() + Pos);
48   }
49 
50   // Set little-endian value Val with size Size at bit position Pos,
51   // and mark bytes as used.
setLEAccumBitVector52   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 
62   // Set big-endian value Val with size Size at bit position Pos,
63   // and mark bytes as used.
setBEAccumBitVector64   void setBE(uint64_t Pos, uint64_t Val, uint8_t Size) {
65     assert(Pos % 8 == 0);
66     auto DataUsed = getPtrToData(Pos / 8, Size);
67     for (unsigned I = 0; I != Size; ++I) {
68       DataUsed.first[Size - I - 1] = Val >> (I * 8);
69       assert(!DataUsed.second[Size - I - 1]);
70       DataUsed.second[Size - I - 1] = 0xff;
71     }
72   }
73 
74   // Set bit at bit position Pos to b and mark bit as used.
setBitAccumBitVector75   void setBit(uint64_t Pos, bool b) {
76     auto DataUsed = getPtrToData(Pos / 8, 1);
77     if (b)
78       *DataUsed.first |= 1 << (Pos % 8);
79     assert(!(*DataUsed.second & (1 << Pos % 8)));
80     *DataUsed.second |= 1 << (Pos % 8);
81   }
82 };
83 
84 // The bits that will be stored before and after a particular vtable.
85 struct VTableBits {
86   // The vtable global.
87   GlobalVariable *GV;
88 
89   // Cache of the vtable's size in bytes.
90   uint64_t ObjectSize = 0;
91 
92   // The bit vector that will be laid out before the vtable. Note that these
93   // bytes are stored in reverse order until the globals are rebuilt. This means
94   // that any values in the array must be stored using the opposite endianness
95   // from the target.
96   AccumBitVector Before;
97 
98   // The bit vector that will be laid out after the vtable.
99   AccumBitVector After;
100 };
101 
102 // Information about a member of a particular type identifier.
103 struct TypeMemberInfo {
104   // The VTableBits for the vtable.
105   VTableBits *Bits;
106 
107   // The offset in bytes from the start of the vtable (i.e. the address point).
108   uint64_t Offset;
109 
110   bool operator<(const TypeMemberInfo &other) const {
111     return Bits < other.Bits || (Bits == other.Bits && Offset < other.Offset);
112   }
113 };
114 
115 // A virtual call target, i.e. an entry in a particular vtable.
116 struct VirtualCallTarget {
117   VirtualCallTarget(Function *Fn, const TypeMemberInfo *TM);
118 
119   // For testing only.
VirtualCallTargetVirtualCallTarget120   VirtualCallTarget(const TypeMemberInfo *TM, bool IsBigEndian)
121       : Fn(nullptr), TM(TM), IsBigEndian(IsBigEndian) {}
122 
123   // The function stored in the vtable.
124   Function *Fn;
125 
126   // A pointer to the type identifier member through which the pointer to Fn is
127   // accessed.
128   const TypeMemberInfo *TM;
129 
130   // When doing virtual constant propagation, this stores the return value for
131   // the function when passed the currently considered argument list.
132   uint64_t RetVal;
133 
134   // Whether the target is big endian.
135   bool IsBigEndian;
136 
137   // The minimum byte offset before the address point. This covers the bytes in
138   // the vtable object before the address point (e.g. RTTI, access-to-top,
139   // vtables for other base classes) and is equal to the offset from the start
140   // of the vtable object to the address point.
minBeforeBytesVirtualCallTarget141   uint64_t minBeforeBytes() const { return TM->Offset; }
142 
143   // The minimum byte offset after the address point. This covers the bytes in
144   // the vtable object after the address point (e.g. the vtable for the current
145   // class and any later base classes) and is equal to the size of the vtable
146   // object minus the offset from the start of the vtable object to the address
147   // point.
minAfterBytesVirtualCallTarget148   uint64_t minAfterBytes() const { return TM->Bits->ObjectSize - TM->Offset; }
149 
150   // The number of bytes allocated (for the vtable plus the byte array) before
151   // the address point.
allocatedBeforeBytesVirtualCallTarget152   uint64_t allocatedBeforeBytes() const {
153     return minBeforeBytes() + TM->Bits->Before.Bytes.size();
154   }
155 
156   // The number of bytes allocated (for the vtable plus the byte array) after
157   // the address point.
allocatedAfterBytesVirtualCallTarget158   uint64_t allocatedAfterBytes() const {
159     return minAfterBytes() + TM->Bits->After.Bytes.size();
160   }
161 
162   // Set the bit at position Pos before the address point to RetVal.
setBeforeBitVirtualCallTarget163   void setBeforeBit(uint64_t Pos) {
164     assert(Pos >= 8 * minBeforeBytes());
165     TM->Bits->Before.setBit(Pos - 8 * minBeforeBytes(), RetVal);
166   }
167 
168   // Set the bit at position Pos after the address point to RetVal.
setAfterBitVirtualCallTarget169   void setAfterBit(uint64_t Pos) {
170     assert(Pos >= 8 * minAfterBytes());
171     TM->Bits->After.setBit(Pos - 8 * minAfterBytes(), RetVal);
172   }
173 
174   // Set the bytes at position Pos before the address point to RetVal.
175   // Because the bytes in Before are stored in reverse order, we use the
176   // opposite endianness to the target.
setBeforeBytesVirtualCallTarget177   void setBeforeBytes(uint64_t Pos, uint8_t Size) {
178     assert(Pos >= 8 * minBeforeBytes());
179     if (IsBigEndian)
180       TM->Bits->Before.setLE(Pos - 8 * minBeforeBytes(), RetVal, Size);
181     else
182       TM->Bits->Before.setBE(Pos - 8 * minBeforeBytes(), RetVal, Size);
183   }
184 
185   // Set the bytes at position Pos after the address point to RetVal.
setAfterBytesVirtualCallTarget186   void setAfterBytes(uint64_t Pos, uint8_t Size) {
187     assert(Pos >= 8 * minAfterBytes());
188     if (IsBigEndian)
189       TM->Bits->After.setBE(Pos - 8 * minAfterBytes(), RetVal, Size);
190     else
191       TM->Bits->After.setLE(Pos - 8 * minAfterBytes(), RetVal, Size);
192   }
193 };
194 
195 // Find the minimum offset that we may store a value of size Size bits at. If
196 // IsAfter is set, look for an offset before the object, otherwise look for an
197 // offset after the object.
198 uint64_t findLowestOffset(ArrayRef<VirtualCallTarget> Targets, bool IsAfter,
199                           uint64_t Size);
200 
201 // Set the stored value in each of Targets to VirtualCallTarget::RetVal at the
202 // given allocation offset before the vtable address. Stores the computed
203 // byte/bit offset to OffsetByte/OffsetBit.
204 void setBeforeReturnValues(MutableArrayRef<VirtualCallTarget> Targets,
205                            uint64_t AllocBefore, unsigned BitWidth,
206                            int64_t &OffsetByte, uint64_t &OffsetBit);
207 
208 // Set the stored value in each of Targets to VirtualCallTarget::RetVal at the
209 // given allocation offset after the vtable address. Stores the computed
210 // byte/bit offset to OffsetByte/OffsetBit.
211 void setAfterReturnValues(MutableArrayRef<VirtualCallTarget> Targets,
212                           uint64_t AllocAfter, unsigned BitWidth,
213                           int64_t &OffsetByte, uint64_t &OffsetBit);
214 
215 } // end namespace wholeprogramdevirt
216 
217 struct WholeProgramDevirtPass : public PassInfoMixin<WholeProgramDevirtPass> {
218   PreservedAnalyses run(Module &M, ModuleAnalysisManager &);
219 };
220 
221 } // end namespace llvm
222 
223 #endif // LLVM_TRANSFORMS_IPO_WHOLEPROGRAMDEVIRT_H
224