Subversion Repositories QNX 8.QNX8 LLVM/Clang compiler suite

Rev

Details | Last modification | View Log | RSS feed

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