forked from revng/revng
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathreachingdefinitions.h
398 lines (329 loc) · 13.6 KB
/
reachingdefinitions.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
#ifndef _REACHINGDEFINITIONS_H
#define _REACHINGDEFINITIONS_H
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
// Standard includes
#include <unordered_set>
#include <vector>
// LLVM includes
#include "llvm/Pass.h"
#include "llvm/ADT/SmallBitVector.h"
#include "llvm/ADT/SmallSet.h"
// Local includes
#include "datastructures.h"
#include "debug.h"
#include "memoryaccess.h"
#define BitVector SmallBitVector
namespace llvm {
class Instruction;
class StoreInst;
class LoadInst;
class Value;
class BranchInst;
class TerminatorInst;
};
// TODO: [speedup] Use LoadStorePtr
// TODO: store in definitions/reaching the MemoryAccess
enum class ReachingDefinitionsResult {
ReachingDefinitions,
ReachedLoads
};
template<class BBI, ReachingDefinitionsResult R>
class ReachingDefinitionsImplPass;
enum LoadDefinitionType {
NoReachingDefinitions, ///< No one can reach it
SelfReaching, ///< Can see it self
HasReachingDefinitions
};
struct MemoryInstruction {
MemoryInstruction(llvm::Instruction *I,
TypeSizeProvider &TSP) : I(I), MA(I, TSP) { }
MemoryInstruction(llvm::StoreInst *I,
TypeSizeProvider &TSP) : I(I), MA(I, TSP) { }
MemoryInstruction(llvm::LoadInst *I,
TypeSizeProvider &TSP) : I(I), MA(I, TSP) { }
bool operator<(const MemoryInstruction Other) const {
return I < Other.I;
}
bool operator==(const MemoryInstruction Other) const {
return I == Other.I;
}
llvm::Instruction *I;
MemoryAccess MA;
};
template<class Container, class UnaryPredicate>
static inline void erase_if(Container &C, UnaryPredicate P) {
C.erase(std::remove_if(C.begin(), C.end(), P), C.end());
}
namespace std {
template <> struct hash<MemoryInstruction>
{
size_t operator()(const MemoryInstruction & MI) const {
return std::hash<llvm::Instruction *>()(MI.I);
}
};
}
class BasicBlockInfo {
public:
unsigned addCondition(int32_t ConditionIndex) { assert(false); }
void resetDefinitions(TypeSizeProvider &TSP) {
Definitions.clear();
// for (llvm::Instruction *I : Reaching)
// Definitions.push_back(MemoryInstruction(I, TSP));
std::copy(Reaching.begin(),
Reaching.end(),
std::back_inserter(Definitions));
}
unsigned size() const { return Reaching.size(); }
void clearDefinitions() {
Definitions.clear();
}
void newDefinition(llvm::StoreInst *Store, TypeSizeProvider &TSP);
LoadDefinitionType newDefinition(llvm::LoadInst *Load,
TypeSizeProvider &TSP);
bool propagateTo(BasicBlockInfo &Target,
TypeSizeProvider &TSP,
const llvm::SmallVector<int32_t, 2> &DefinedIndexes,
int32_t NewConditionIndex);
std::vector<std::pair<llvm::Instruction *, MemoryAccess>>
getReachingDefinitions(std::set<llvm::LoadInst *> &WhiteList,
TypeSizeProvider &TSP);
void dump(std::ostream &Output);
private:
template<class UnaryPredicate>
void removeDefinitions(UnaryPredicate P) {
erase_if(Definitions, P);
}
private:
// llvm::SmallSet<llvm::Instruction *, 3> Reaching;
std::unordered_set<MemoryInstruction> Reaching;
std::vector<MemoryInstruction> Definitions;
};
class ConditionalBasicBlockInfo {
public:
unsigned addCondition(int32_t ConditionIndex) {
unsigned Result = getConditionIndex(ConditionIndex);
Conditions.set(Result);
return Result;
}
bool hasCondition(int32_t ConditionIndex) {
unsigned Result = getConditionIndex(ConditionIndex);
return Conditions[Result];
}
void resetDefinitions(TypeSizeProvider &TSP) {
for (auto &P : Reaching)
Definitions.push_back({ P.second, P.first });
}
unsigned size() const { return Reaching.size(); }
void clearDefinitions() {
Definitions.clear();
}
void newDefinition(llvm::StoreInst *Store, TypeSizeProvider &TSP);
LoadDefinitionType newDefinition(llvm::LoadInst *Load,
TypeSizeProvider &TSP);
bool propagateTo(ConditionalBasicBlockInfo &Target,
TypeSizeProvider &TSP,
const llvm::SmallVector<int32_t, 2> &DefinedIndexes,
int32_t NewConditionIndex);
std::vector<std::pair<llvm::Instruction *, MemoryAccess>>
getReachingDefinitions(std::set<llvm::LoadInst *> &WhiteList,
TypeSizeProvider &TSP);
void dump(std::ostream& Output);
private:
using CondDefPair = std::pair<llvm::BitVector, MemoryInstruction>;
using ReachingType = std::unordered_map<MemoryInstruction, llvm::BitVector>;
enum ConditionsComparison {
Identical,
Different,
Complementary
};
private:
/// \brief Set the bit corresponding to \p Index in \p Target, if present in
/// SeenCondtions.
bool setIndexIfSeen(llvm::BitVector &Target, int32_t Index) const;
template<class UnaryPredicate>
void removeDefinitions(UnaryPredicate P) {
erase_if(Definitions, P);
}
unsigned getConditionIndex(uint32_t ConditionIndex) {
auto It = std::find(SeenConditions.begin(),
SeenConditions.end(),
ConditionIndex);
if (It != SeenConditions.end()) {
return It - SeenConditions.begin();
} else {
SeenConditions.push_back(ConditionIndex);
auto NewSize = SeenConditions.size();
Conditions.resize(NewSize);
for (auto &P : Reaching)
P.second.resize(NewSize);
for (CondDefPair &Definition : Definitions)
Definition.first.resize(NewSize);
return NewSize - 1;
}
}
bool mergeDefinition(CondDefPair NewDefinition,
std::vector<CondDefPair> &Targets,
TypeSizeProvider &TSP) const;
bool mergeDefinition(CondDefPair NewDefinition,
ReachingType &Targets,
TypeSizeProvider &TSP) const;
private:
// Seen conditions
std::vector<int32_t> SeenConditions;
// TODO: switch to list?
ReachingType Reaching;
std::vector<CondDefPair> Definitions;
llvm::BitVector Conditions;
};
using ReachingDefinitionsPass = ReachingDefinitionsImplPass<BasicBlockInfo,
ReachingDefinitionsResult::ReachingDefinitions>;
using ConditionalReachingDefinitionsPass =
ReachingDefinitionsImplPass<ConditionalBasicBlockInfo,
ReachingDefinitionsResult::ReachingDefinitions>;
using ReachedLoadsPass =
ReachingDefinitionsImplPass<BasicBlockInfo,
ReachingDefinitionsResult::ReachedLoads>;
using ConditionalReachedLoadsPass =
ReachingDefinitionsImplPass<ConditionalBasicBlockInfo,
ReachingDefinitionsResult::ReachedLoads>;
template<class BBI, ReachingDefinitionsResult R>
class ReachingDefinitionsImplPass : public llvm::FunctionPass {
public:
static char ID;
ReachingDefinitionsImplPass() : llvm::FunctionPass(ID) { };
bool runOnFunction(llvm::Function &F) override;
void getAnalysisUsage(llvm::AnalysisUsage &AU) const override;
const std::vector<llvm::LoadInst *> &
getReachedLoads(const llvm::Instruction *I);
const std::vector<llvm::Instruction *> &
getReachingDefinitions(const llvm::LoadInst *Load);
unsigned getReachingDefinitionsCount(const llvm::LoadInst *Load);
virtual void releaseMemory() override {
DBG("release", {
dbg << "ReachingDefinitionsImplPass is releasing memory\n";
});
freeContainer(ReachedLoads);
freeContainer(ReachingDefinitions);
freeContainer(ReachingDefinitionsCount);
}
private:
int32_t getConditionIndex(llvm::TerminatorInst *T);
const llvm::SmallVector<int32_t, 2> &getDefinedConditions(llvm::BasicBlock *BB);
private:
using BasicBlock = llvm::BasicBlock;
using LoadInst = llvm::LoadInst;
using Instruction = llvm::Instruction;
std::map<BasicBlock *, BBI> DefinitionsMap;
std::set<BasicBlock *> BasicBlockBlackList;
std::set<LoadInst *> NRDLoads;
std::set<LoadInst *> SelfReachingLoads;
std::map<const Instruction *, std::vector<LoadInst *>> ReachedLoads;
std::map<const LoadInst *, std::vector<Instruction *>> ReachingDefinitions;
std::map<const LoadInst *, unsigned> ReachingDefinitionsCount;
};
/// The ConditionNumberingPass loops over all the conditional branch
/// instructions in the program and tries to identify those that are based on
/// exactly the same condition, i.e., the pair for which can be sure that, if
/// the first branch is taken, then also the second branch will be taken. This
/// is particularly useful to handle consecutive predeicate instructions.
///
/// Two conditions are considered the same, if they actually are the same or if
/// they compute exactly the same operations on the same operands. To
/// efficiently identify which branch instructions use the same conditions we
/// populate an hashmap with a custom hash function. At the end, we will discard
/// all the entries of the hashmap with a single entry, since we're not
/// interested in considering a condition if it doesn't have at least a
/// companion branch instruction. Each condition with at least two branches
/// using it is assigned a unique identifier, the condition index.
///
/// The ConditionNumberingPass also provides, for each condition index, a list
/// of "reset" basic blocks, i.e., a list of basic blocks which define at least
/// one of the values involved in the computation of the condition. Such a list
/// can be used to understand when it doesn't make sense for an analysis to
/// consider that a certain condition is still holding.
///
/// "reset" basic blocks also include the last basic block that might be
/// affected by the associated condition index. This is useful to prevent an
/// analysis from keeping track of a condition index which we can be sure will
/// never be used again. The last basic block that might be affected by a
/// condition index is the immediate post-dominator of the set of basic blocks
/// containing the branches associated to that condition index.
///
/// The following figures examplifies the situation: BB1 and BB2 share the same
/// condition, BB3 is their immediate post-dominator. To easily identify it as
/// such we introduce a temporary basic block BB0 and make it a predecessor of
/// both BB1 and BB2. Then, we compute the post-dominator tree and ask for the
/// immediate post-domiantor of BB0, obtaining BB3.
///
/// +-----------+
/// | |
/// +- - - - - -+ BB0 +- - - - -+
/// | | | |
/// +-----------+
/// | |
///
/// +-----v-----+ +-----v-----+
/// | | | |
/// +---+ BB1 +---+ +---+ BB2 +---+
/// | | | | | | | |
/// | +-----------+ | | +-----------+ |
/// | | | |
/// | | | |
/// +-----v-----+ +-----v-----+ +-----v-----+ +-----v-----+
/// | | | | | | | |
/// | | | | | | | |
/// | | | | | | | |
/// +-----+-----+ +-----+-----+ +-----+-----+ +-----+-----+
/// | | | |
/// | | | |
/// | +-----v-----+ | +-----v-----+
/// | | | | | |
/// +-------------> <-------+ | |
/// | | | |
/// +-----+-----+ +-----+-----+
/// | |
/// | |
/// | +-----------+ |
/// | | | |
/// +----------> BB3 <----------+
/// | |
/// +-----+-----+
/// |
/// |
/// v
class ConditionNumberingPass : public llvm::FunctionPass {
public:
static char ID;
static const llvm::SmallVector<int32_t, 2> NoDefinedConditions;
ConditionNumberingPass() : llvm::FunctionPass(ID) { };
bool runOnFunction(llvm::Function &F) override;
void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
AU.addRequired<ReachingDefinitionsPass>();
AU.setPreservesAll();
}
int32_t getConditionIndex(llvm::TerminatorInst *T) {
return BranchConditionNumberMap[T];
}
const llvm::SmallVector<int32_t, 2> &getDefinedConditions(llvm::BasicBlock *BB) const {
auto It = DefinedConditions.find(BB);
if (It == DefinedConditions.end())
return NoDefinedConditions;
else
return It->second;
}
virtual void releaseMemory() override {
DBG("release", { dbg << "ConditionNumberingPass is releasing memory\n"; });
freeContainer(DefinedConditions);
freeContainer(BranchConditionNumberMap);
}
private:
std::map<llvm::BasicBlock *, llvm::SmallVector<int32_t, 2>> DefinedConditions;
std::map<llvm::TerminatorInst *, int32_t> BranchConditionNumberMap;
};
template<>
char ReachingDefinitionsImplPass<BasicBlockInfo, ReachingDefinitionsResult::ReachingDefinitions>::ID;
template<>
char ReachingDefinitionsImplPass<ConditionalBasicBlockInfo, ReachingDefinitionsResult::ReachedLoads>::ID;
#endif // _REACHINGDEFINITIONS_H