@@ -62,6 +62,7 @@ struct ValueToDeclInferrer {
6262
6363 RCIdentityFunctionInfo &rcfi;
6464 SmallVector<std::pair<SILType, Projection>, 32 > accessPath;
65+ SmallVector<Operand *, 32 > rcIdenticalSecondaryUseSearch;
6566
6667 ValueToDeclInferrer (RCIdentityFunctionInfo &rcfi) : rcfi(rcfi) {}
6768
@@ -70,17 +71,74 @@ struct ValueToDeclInferrer {
7071 bool infer (ArgumentKeyKind keyKind, SILValue value,
7172 SmallVectorImpl<Argument> &resultingInferredDecls);
7273
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+ }
77133
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);
79137};
80138
81139} // anonymous namespace
82140
83- void ValueToDeclInferrer::printProjectionPath (llvm::raw_string_ostream &stream) {
141+ void ValueToDeclInferrer::printAccessPath (llvm::raw_string_ostream &stream) {
84142 for (auto &pair : accessPath) {
85143 auto baseType = pair.first ;
86144 auto &proj = pair.second ;
@@ -120,11 +178,11 @@ void ValueToDeclInferrer::printProjectionPath(llvm::raw_string_ostream &stream)
120178}
121179
122180void 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);
128186 stream << " '" ;
129187}
130188
@@ -161,6 +219,7 @@ bool ValueToDeclInferrer::infer(
161219 SWIFT_DEFER {
162220 accessPath.clear ();
163221 };
222+ SmallPtrSet<SILInstruction *, 8 > visitedDebugValueInsts;
164223
165224 // This is a linear IR traversal using a 'falling while loop'. That means
166225 // every time through the loop we are trying to handle a case before we hit
@@ -233,62 +292,77 @@ bool ValueToDeclInferrer::infer(
233292 }
234293 }
235294
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.
238302 bool foundDeclFromUse = false ;
239- for ( auto *use : value-> getUses () ) {
303+ rcfi. visitRCUses (value, [&](Operand *use) {
240304 // Skip type dependent uses.
241305 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 ;
243314
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*/ );
282331 }
332+ resultingInferredDecls.emplace_back (
333+ OptRemark::ArgumentKey{keyKind, " InferredValue" }, std::move (msg),
334+ decl);
335+ foundDeclFromUse = true ;
336+ return ;
283337 }
284- }
285338
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+ });
290362
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.
292366 if (auto *li = dyn_cast<LoadInst>(value)) {
293367 value = stripAccessMarkers (li->getOperand ());
294368 continue ;
@@ -305,7 +379,8 @@ bool ValueToDeclInferrer::infer(
305379 // TODO: We could emit at this point a msg for temporary allocations.
306380
307381 // 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.
309384 return foundDeclFromUse;
310385 }
311386}
0 commit comments