@@ -28,6 +28,19 @@ class StructuralHashImpl {
28
28
29
29
bool DetailedHash;
30
30
31
+ // / IgnoreOp is a function that returns true if the operand should be ignored.
32
+ IgnoreOperandFunc IgnoreOp = nullptr ;
33
+ // / A mapping from instruction indices to instruction pointers.
34
+ // / The index represents the position of an instruction based on the order in
35
+ // / which it is first encountered.
36
+ std::unique_ptr<IndexInstrMap> IndexInstruction = nullptr ;
37
+ // / A mapping from pairs of instruction indices and operand indices
38
+ // / to the hashes of the operands.
39
+ std::unique_ptr<IndexOperandHashMapType> IndexOperandHashMap = nullptr ;
40
+
41
+ // / Assign a unique ID to each Value in the order they are first seen.
42
+ DenseMap<const Value *, int > ValueToId;
43
+
31
44
// This will produce different values on 32-bit and 64-bit systens as
32
45
// hash_combine returns a size_t. However, this is only used for
33
46
// detailed hashing which, in-tree, only needs to distinguish between
@@ -47,24 +60,140 @@ class StructuralHashImpl {
47
60
48
61
public:
49
62
StructuralHashImpl () = delete ;
50
- explicit StructuralHashImpl (bool DetailedHash) : DetailedHash(DetailedHash) {}
63
+ explicit StructuralHashImpl (bool DetailedHash,
64
+ IgnoreOperandFunc IgnoreOp = nullptr )
65
+ : DetailedHash(DetailedHash), IgnoreOp(IgnoreOp) {
66
+ if (IgnoreOp) {
67
+ IndexInstruction = std::make_unique<IndexInstrMap>();
68
+ IndexOperandHashMap = std::make_unique<IndexOperandHashMapType>();
69
+ }
70
+ }
51
71
52
- stable_hash hashConstant (Constant *C ) {
72
+ stable_hash hashAPInt ( const APInt &I ) {
53
73
SmallVector<stable_hash> Hashes;
54
- // TODO: hashArbitaryType() is not stable.
55
- if (ConstantInt *ConstInt = dyn_cast<ConstantInt>(C)) {
56
- Hashes.emplace_back (hashArbitaryType (ConstInt->getValue ()));
57
- } else if (ConstantFP *ConstFP = dyn_cast<ConstantFP>(C)) {
58
- Hashes.emplace_back (hashArbitaryType (ConstFP->getValue ()));
59
- } else if (Function *Func = dyn_cast<Function>(C))
60
- // Hashing the name will be deterministic as LLVM's hashing infrastructure
61
- // has explicit support for hashing strings and will not simply hash
62
- // the pointer.
63
- Hashes.emplace_back (hashArbitaryType (Func->getName ()));
74
+ Hashes.emplace_back (I.getBitWidth ());
75
+ for (unsigned J = 0 ; J < I.getNumWords (); ++J)
76
+ Hashes.emplace_back ((I.getRawData ())[J]);
77
+ return stable_hash_combine (Hashes);
78
+ }
64
79
80
+ stable_hash hashAPFloat (const APFloat &F) {
81
+ SmallVector<stable_hash> Hashes;
82
+ const fltSemantics &S = F.getSemantics ();
83
+ Hashes.emplace_back (APFloat::semanticsPrecision (S));
84
+ Hashes.emplace_back (APFloat::semanticsMaxExponent (S));
85
+ Hashes.emplace_back (APFloat::semanticsMinExponent (S));
86
+ Hashes.emplace_back (APFloat::semanticsSizeInBits (S));
87
+ Hashes.emplace_back (hashAPInt (F.bitcastToAPInt ()));
65
88
return stable_hash_combine (Hashes);
66
89
}
67
90
91
+ stable_hash hashGlobalValue (const GlobalValue *GV) {
92
+ if (!GV->hasName ())
93
+ return 0 ;
94
+ return stable_hash_name (GV->getName ());
95
+ }
96
+
97
+ // Compute a hash for a Constant. This function is logically similar to
98
+ // FunctionComparator::cmpConstants() in FunctionComparator.cpp, but here
99
+ // we're interested in computing a hash rather than comparing two Constants.
100
+ // Some of the logic is simplified, e.g, we don't expand GEPOperator.
101
+ stable_hash hashConstant (Constant *C) {
102
+ SmallVector<stable_hash> Hashes;
103
+
104
+ Type *Ty = C->getType ();
105
+ Hashes.emplace_back (hashType (Ty));
106
+
107
+ if (C->isNullValue ()) {
108
+ Hashes.emplace_back (static_cast <stable_hash>(' N' ));
109
+ return stable_hash_combine (Hashes);
110
+ }
111
+
112
+ auto *G = dyn_cast<GlobalValue>(C);
113
+ if (G) {
114
+ Hashes.emplace_back (hashGlobalValue (G));
115
+ return stable_hash_combine (Hashes);
116
+ }
117
+
118
+ if (const auto *Seq = dyn_cast<ConstantDataSequential>(C)) {
119
+ Hashes.emplace_back (xxh3_64bits (Seq->getRawDataValues ()));
120
+ return stable_hash_combine (Hashes);
121
+ }
122
+
123
+ switch (C->getValueID ()) {
124
+ case Value::UndefValueVal:
125
+ case Value::PoisonValueVal:
126
+ case Value::ConstantTokenNoneVal: {
127
+ return stable_hash_combine (Hashes);
128
+ }
129
+ case Value::ConstantIntVal: {
130
+ const APInt &Int = cast<ConstantInt>(C)->getValue ();
131
+ Hashes.emplace_back (hashAPInt (Int));
132
+ return stable_hash_combine (Hashes);
133
+ }
134
+ case Value::ConstantFPVal: {
135
+ const APFloat &APF = cast<ConstantFP>(C)->getValueAPF ();
136
+ Hashes.emplace_back (hashAPFloat (APF));
137
+ return stable_hash_combine (Hashes);
138
+ }
139
+ case Value::ConstantArrayVal: {
140
+ const ConstantArray *A = cast<ConstantArray>(C);
141
+ uint64_t NumElements = cast<ArrayType>(Ty)->getNumElements ();
142
+ Hashes.emplace_back (NumElements);
143
+ for (auto &Op : A->operands ()) {
144
+ auto H = hashConstant (cast<Constant>(Op));
145
+ Hashes.emplace_back (H);
146
+ }
147
+ return stable_hash_combine (Hashes);
148
+ }
149
+ case Value::ConstantStructVal: {
150
+ const ConstantStruct *S = cast<ConstantStruct>(C);
151
+ unsigned NumElements = cast<StructType>(Ty)->getNumElements ();
152
+ Hashes.emplace_back (NumElements);
153
+ for (auto &Op : S->operands ()) {
154
+ auto H = hashConstant (cast<Constant>(Op));
155
+ Hashes.emplace_back (H);
156
+ }
157
+ return stable_hash_combine (Hashes);
158
+ }
159
+ case Value::ConstantVectorVal: {
160
+ const ConstantVector *V = cast<ConstantVector>(C);
161
+ unsigned NumElements = cast<FixedVectorType>(Ty)->getNumElements ();
162
+ Hashes.emplace_back (NumElements);
163
+ for (auto &Op : V->operands ()) {
164
+ auto H = hashConstant (cast<Constant>(Op));
165
+ Hashes.emplace_back (H);
166
+ }
167
+ return stable_hash_combine (Hashes);
168
+ }
169
+ case Value::ConstantExprVal: {
170
+ const ConstantExpr *E = cast<ConstantExpr>(C);
171
+ unsigned NumOperands = E->getNumOperands ();
172
+ Hashes.emplace_back (NumOperands);
173
+ for (auto &Op : E->operands ()) {
174
+ auto H = hashConstant (cast<Constant>(Op));
175
+ Hashes.emplace_back (H);
176
+ }
177
+ return stable_hash_combine (Hashes);
178
+ }
179
+ case Value::BlockAddressVal: {
180
+ const BlockAddress *BA = cast<BlockAddress>(C);
181
+ auto H = hashGlobalValue (BA->getFunction ());
182
+ Hashes.emplace_back (H);
183
+ return stable_hash_combine (Hashes);
184
+ }
185
+ case Value::DSOLocalEquivalentVal: {
186
+ const auto *Equiv = cast<DSOLocalEquivalent>(C);
187
+ auto H = hashGlobalValue (Equiv->getGlobalValue ());
188
+ Hashes.emplace_back (H);
189
+ return stable_hash_combine (Hashes);
190
+ }
191
+ default : // Unknown constant, abort.
192
+ llvm_unreachable (" Constant ValueID not recognized." );
193
+ }
194
+ return Hash;
195
+ }
196
+
68
197
stable_hash hashValue (Value *V) {
69
198
// Check constant and return its hash.
70
199
Constant *C = dyn_cast<Constant>(V);
@@ -76,6 +205,10 @@ class StructuralHashImpl {
76
205
if (Argument *Arg = dyn_cast<Argument>(V))
77
206
Hashes.emplace_back (Arg->getArgNo ());
78
207
208
+ // Get an index (an insertion order) for the non-constant value.
209
+ auto I = ValueToId.insert ({V, ValueToId.size ()});
210
+ Hashes.emplace_back (I.first ->second );
211
+
79
212
return stable_hash_combine (Hashes);
80
213
}
81
214
@@ -100,8 +233,20 @@ class StructuralHashImpl {
100
233
if (const auto *ComparisonInstruction = dyn_cast<CmpInst>(&Inst))
101
234
Hashes.emplace_back (ComparisonInstruction->getPredicate ());
102
235
103
- for (const auto &Op : Inst.operands ())
104
- Hashes.emplace_back (hashOperand (Op));
236
+ unsigned InstIdx = 0 ;
237
+ if (IndexInstruction) {
238
+ InstIdx = IndexInstruction->size ();
239
+ IndexInstruction->insert ({InstIdx, const_cast <Instruction *>(&Inst)});
240
+ }
241
+
242
+ for (const auto [OpndIdx, Op] : enumerate(Inst.operands ())) {
243
+ auto OpndHash = hashOperand (Op);
244
+ if (IgnoreOp && IgnoreOp (&Inst, OpndIdx)) {
245
+ assert (IndexOperandHashMap);
246
+ IndexOperandHashMap->insert ({{InstIdx, OpndIdx}, OpndHash});
247
+ } else
248
+ Hashes.emplace_back (OpndHash);
249
+ }
105
250
106
251
return stable_hash_combine (Hashes);
107
252
}
@@ -184,6 +329,12 @@ class StructuralHashImpl {
184
329
}
185
330
186
331
uint64_t getHash () const { return Hash; }
332
+ std::unique_ptr<IndexInstrMap> getIndexInstrMap () {
333
+ return std::move (IndexInstruction);
334
+ }
335
+ std::unique_ptr<IndexOperandHashMapType> getIndexPairOpndHashMap () {
336
+ return std::move (IndexOperandHashMap);
337
+ }
187
338
};
188
339
189
340
} // namespace
@@ -199,3 +350,12 @@ IRHash llvm::StructuralHash(const Module &M, bool DetailedHash) {
199
350
H.update (M);
200
351
return H.getHash ();
201
352
}
353
+
354
+ FunctionHashInfo
355
+ llvm::StructuralHashWithDifferences (const Function &F,
356
+ IgnoreOperandFunc IgnoreOp) {
357
+ StructuralHashImpl H (/* DetailedHash=*/ true , IgnoreOp);
358
+ H.update (F);
359
+ return FunctionHashInfo (H.getHash (), H.getIndexInstrMap (),
360
+ H.getIndexPairOpndHashMap ());
361
+ }
0 commit comments