@@ -62,6 +62,7 @@ struct ValueToDeclInferrer {
62
62
63
63
RCIdentityFunctionInfo &rcfi;
64
64
SmallVector<std::pair<SILType, Projection>, 32 > accessPath;
65
+ SmallVector<Operand *, 32 > rcIdenticalSecondaryUseSearch;
65
66
66
67
ValueToDeclInferrer (RCIdentityFunctionInfo &rcfi) : rcfi(rcfi) {}
67
68
@@ -70,17 +71,74 @@ struct ValueToDeclInferrer {
70
71
bool infer (ArgumentKeyKind keyKind, SILValue value,
71
72
SmallVectorImpl<Argument> &resultingInferredDecls);
72
73
73
- // / Print out a note to \p stream that beings at decl and then consumes the
74
- // / accessPath we computed for decl producing a segmented access path, e.x.:
75
- // / "of 'x.lhs.ivar'".
76
- void printNote (llvm::raw_string_ostream &stream, const ValueDecl *decl);
74
+ // / Print out a note to \p stream that beings at decl and then if
75
+ // / useProjectionPath is set to true iterates the accessPath we computed for
76
+ // / decl producing a segmented access path, e.x.: "of 'x.lhs.ivar'".
77
+ // /
78
+ // / The reason why one may not want to emit a projection path note here is if
79
+ // / one found an debug_value on a value that is rc-identical to the actual
80
+ // / value associated with the current projection path. Consider the following
81
+ // / SIL:
82
+ // /
83
+ // / struct KlassPair {
84
+ // / var lhs: Klass
85
+ // / var rhs: Klass
86
+ // / }
87
+ // /
88
+ // / struct StateWithOwningPointer {
89
+ // / var state: TrivialState
90
+ // / var owningPtr: Klass
91
+ // / }
92
+ // /
93
+ // / sil @theFunction : $@convention(thin) () -> () {
94
+ // / bb0:
95
+ // / %0 = apply %getKlassPair() : $@convention(thin) () -> @owned KlassPair
96
+ // / // This debug_value's name can be combined...
97
+ // / debug_value %0 : $KlassPair, name "myPair"
98
+ // / // ... with the access path from the struct_extract here...
99
+ // / %1 = struct_extract %0 : $KlassPair, #KlassPair.lhs
100
+ // / // ... to emit a nice diagnostic that 'myPair.lhs' is being retained.
101
+ // / strong_retain %1 : $Klass
102
+ // /
103
+ // / // In contrast in this case, we rely on looking through rc-identity
104
+ // / // uses to find the debug_value. In this case, the source info
105
+ // / // associated with the debug_value (%2) is no longer associated with
106
+ // / // the underlying access path we have been tracking upwards (%1 is in
107
+ // / // our access path list). Instead, we know that the debug_value is
108
+ // / // rc-identical to whatever value we were originally tracking up (%1)
109
+ // / // and thus the correct identifier to use is the direct name of the
110
+ // / // identifier alone since that source identifier must be some value
111
+ // / // in the source that by itself is rc-identical to whatever is being
112
+ // / // manipulated.
113
+ // / //
114
+ // / // The reason why we must do this is due to the behavior of the late
115
+ // / // optimizer and how it forms these patterns in the code.
116
+ // / %0a = apply %getStateWithOwningPointer() : $@convention(thin) () -> @owned StateWithOwningPointer
117
+ // / %1 = struct_extract %0a : $StateWithOwningPointer, #StateWithOwningPointer.owningPtr
118
+ // / strong_retain %1 : $Klass
119
+ // / %2 = struct $Array(%0 : $Builtin.NativeObject, ...)
120
+ // / debug_value %2 : $Array, ...
121
+ // / }
122
+ void printNote (llvm::raw_string_ostream &stream, StringRef name,
123
+ bool shouldPrintAccessPath = true );
124
+
125
+ // / Convenience overload that calls:
126
+ // /
127
+ // / printNote(stream, decl->getBaseName().userFacingName(), shouldPrintAccessPath).
128
+ void printNote (llvm::raw_string_ostream &stream, const ValueDecl *decl,
129
+ bool shouldPrintAccessPath = true ) {
130
+ printNote (stream, decl->getBaseName ().userFacingName (),
131
+ shouldPrintAccessPath);
132
+ }
77
133
78
- void printProjectionPath (llvm::raw_string_ostream &stream);
134
+ // / Print out non-destructively the current access path we have found to
135
+ // / stream.
136
+ void printAccessPath (llvm::raw_string_ostream &stream);
79
137
};
80
138
81
139
} // anonymous namespace
82
140
83
- void ValueToDeclInferrer::printProjectionPath (llvm::raw_string_ostream &stream) {
141
+ void ValueToDeclInferrer::printAccessPath (llvm::raw_string_ostream &stream) {
84
142
for (auto &pair : accessPath) {
85
143
auto baseType = pair.first ;
86
144
auto &proj = pair.second ;
@@ -120,11 +178,11 @@ void ValueToDeclInferrer::printProjectionPath(llvm::raw_string_ostream &stream)
120
178
}
121
179
122
180
void ValueToDeclInferrer::printNote (llvm::raw_string_ostream &stream,
123
- const ValueDecl *decl) {
124
- assert (decl &&
125
- " We assume for now that this is always called with a non-null decl " ) ;
126
- stream << " of ' " << decl-> getBaseName ();
127
- printProjectionPath (stream);
181
+ StringRef name,
182
+ bool shouldPrintAccessPath) {
183
+ stream << " of ' " << name ;
184
+ if (shouldPrintAccessPath)
185
+ printAccessPath (stream);
128
186
stream << " '" ;
129
187
}
130
188
@@ -161,6 +219,7 @@ bool ValueToDeclInferrer::infer(
161
219
SWIFT_DEFER {
162
220
accessPath.clear ();
163
221
};
222
+ SmallPtrSet<SILInstruction *, 8 > visitedDebugValueInsts;
164
223
165
224
// This is a linear IR traversal using a 'falling while loop'. That means
166
225
// every time through the loop we are trying to handle a case before we hit
@@ -233,62 +292,77 @@ bool ValueToDeclInferrer::infer(
233
292
}
234
293
}
235
294
236
- // Then visit our users and see if we can find a debug_value that provides
237
- // us with a decl we can use to construct an argument.
295
+ // Then visit our users (ignoring rc identical transformations) and see if
296
+ // we can find a debug_value that provides us with a decl we can use to
297
+ // construct an argument.
298
+ //
299
+ // The reason why we do this is that sometimes we reform a struct from its
300
+ // constituant parts and then construct the debug_value from that. For
301
+ // instance, if we FSOed.
238
302
bool foundDeclFromUse = false ;
239
- for ( auto *use : value-> getUses () ) {
303
+ rcfi. visitRCUses (value, [&](Operand *use) {
240
304
// Skip type dependent uses.
241
305
if (use->isTypeDependent ())
242
- continue ;
306
+ return ;
307
+
308
+ // Then see if we have a debug_value that is associated with a non-inlined
309
+ // debug scope. Such an instruction is an instruction that is from the
310
+ // current function.
311
+ auto *dvi = dyn_cast<DebugValueInst>(use->getUser ());
312
+ if (!dvi || !hasNonInlinedDebugScope (dvi))
313
+ return ;
243
314
244
- if (auto *dvi = dyn_cast<DebugValueInst>(use->getUser ())) {
245
- // Check if our debug_value has a decl and was not inlined into the
246
- // current function.
247
- if (hasNonInlinedDebugScope (dvi)) {
248
- if (auto *decl = dvi->getDecl ()) {
249
- std::string msg;
250
- {
251
- llvm::raw_string_ostream stream (msg);
252
- printNote (stream, decl);
253
- }
254
- resultingInferredDecls.emplace_back (
255
- OptRemark::ArgumentKey{keyKind, " InferredValue" },
256
- std::move (msg), decl);
257
- foundDeclFromUse = true ;
258
- } else {
259
- // If we did not have a decl, see if we were asked for testing
260
- // purposes to use SILDebugInfo to create a placeholder inferred
261
- // value.
262
- if (DecllessDebugValueUseSILDebugInfo) {
263
- if (auto varInfo = dvi->getVarInfo ()) {
264
- auto name = varInfo->Name ;
265
- if (!name.empty ()) {
266
- std::string msg;
267
- {
268
- llvm::raw_string_ostream stream (msg);
269
- stream << " of '" << name;
270
- printProjectionPath (stream);
271
- stream << " '" ;
272
- }
273
- resultingInferredDecls.push_back (
274
- Argument ({keyKind, " InferredValue" },
275
- std::move (msg),
276
- dvi->getLoc ()));
277
- foundDeclFromUse = true ;
278
- }
279
- }
280
- }
281
- }
315
+ // See if we have already inferred this debug_value as a potential source
316
+ // for this instruction. In such a case, just return.
317
+ if (!visitedDebugValueInsts.insert (dvi).second )
318
+ return ;
319
+
320
+ if (auto *decl = dvi->getDecl ()) {
321
+ std::string msg;
322
+ {
323
+ llvm::raw_string_ostream stream (msg);
324
+ // If we are not a top level use, we must be a rc-identical transitive
325
+ // use. In such a case, we just print out the rc identical value
326
+ // without a projection path. This is because we now have a better
327
+ // name and the name is rc-identical to whatever was at the end of the
328
+ // projection path but is not at the end of that projection path.
329
+ printNote (stream, decl,
330
+ use->get () == value /* print projection path*/ );
282
331
}
332
+ resultingInferredDecls.emplace_back (
333
+ OptRemark::ArgumentKey{keyKind, " InferredValue" }, std::move (msg),
334
+ decl);
335
+ foundDeclFromUse = true ;
336
+ return ;
283
337
}
284
- }
285
338
286
- // At this point, we could not infer any argument. See if we can look
287
- // through loads.
288
- //
289
- // TODO: Add GEPs to construct a ProjectionPath.
339
+ // If we did not have a decl, see if we were asked for testing
340
+ // purposes to use SILDebugInfo to create a placeholder inferred
341
+ // value.
342
+ if (!DecllessDebugValueUseSILDebugInfo)
343
+ return ;
344
+
345
+ auto varInfo = dvi->getVarInfo ();
346
+ if (!varInfo)
347
+ return ;
348
+
349
+ auto name = varInfo->Name ;
350
+ if (name.empty ())
351
+ return ;
352
+
353
+ std::string msg;
354
+ {
355
+ llvm::raw_string_ostream stream (msg);
356
+ printNote (stream, name, use->get () == value /* print projection path*/ );
357
+ }
358
+ resultingInferredDecls.push_back (
359
+ Argument ({keyKind, " InferredValue" }, std::move (msg), dvi->getLoc ()));
360
+ foundDeclFromUse = true ;
361
+ });
290
362
291
- // Finally, see if we can look through a load...
363
+ // At this point, we could not infer any argument. See if we can look up the
364
+ // def-use graph and come up with a good location after looking through
365
+ // loads and projections.
292
366
if (auto *li = dyn_cast<LoadInst>(value)) {
293
367
value = stripAccessMarkers (li->getOperand ());
294
368
continue ;
@@ -305,7 +379,8 @@ bool ValueToDeclInferrer::infer(
305
379
// TODO: We could emit at this point a msg for temporary allocations.
306
380
307
381
// If we reached this point, we finished falling through the loop and return
308
- // true.
382
+ // if we found any decls from uses. We always process everything so we /can/
383
+ // potentially emit multiple diagnostics.
309
384
return foundDeclFromUse;
310
385
}
311
386
}
0 commit comments