Specifically, now we properly identify the SILLocation for the final inlined
call site of a Source Location and put the remark there instead of in the
callee.
This eliminates opt-remarks on initializers/friends, e.x.:
```
struct KlassPair {
var lhs: Klass // expected-remark {{retain of type 'Klass'}}
// expected-note @-1 {{of 'self.lhs'}}
// expected-remark @-2 {{release of type 'Klass'}}
// expected-note @-3 {{of 'self.lhs'}}
var rhs: Klass // expected-remark {{retain of type 'Klass'}}
// expected-note @-1 {{of 'self.rhs'}}
// expected-remark @-2 {{release of type 'Klass'}}
// expected-note @-3 {{of 'self.rhs'}}
}
```
becomes:
```
struct KlassPair {
var lhs: Klass
var rhs: Klass
}
```
I also added an -Xllvm option (-optremarkgen-visit-implicit-autogen-funcs) that
will force these to be emitted as a compiler knob for compiler developers. There
is a test that validates the behavior.
This ensures that we are able to properly look through struct_extract,
tuple_extract in cases where we have an aggregate with multiple non-trivial
subtypes (implying it is not-rc identical with those sub-types).
The end result is that we now emit good diagnostics for things like this:
```
func returnStructWithOwnerOwner(x: StructWithOwner) -> Klass {
return x.owner // expected-remark {{retain}}
// expected-note @-7:33 {{of 'x.owner'}}
}
```
More specifically, if one wants to force emit /all/ opt-remarks on a function, mark it with:
```
@_semantics("optremark")
```
If one wants to emit opt-remarks only for a specific SIL pass (like lets say
sil-opt-remark-gen), one can write:
```
@_semantics("optremark.sil-opt-remark-gen")
```
I made the pattern matching strict so if you just put in a '.' or add additional
suffixes, it will not pattern match. I think that this is sufficient for a
prototyping tool.
This is useful if one wants to play around with opt-remarks when optimizing code
in Xcode or any IDE that can use serialized diagnostics.
So now if one looks at remarks in an IDE, you will see in the NOTE itself the
decl to look for. I am going to expand this to include handling of projection
paths.
I noticed that I kept on needing to hack on this as I added stuff to
OptRemarkGenerator and felt the need to tie it even closer to
OptRemarkGenerator. As I began to think about it, this is really something
specific to that algorithm, so it really doesn't make sense to have it as part
of the general SIL library.
This is a NFC refactoring.
In all of these cases, we already had a SILFunction and were just grabbing its
SILModule instead of passing it in. So this is just an NFC change.
The reason why I am doing this is so that I can force emit opt-remarks on
functions with the semantics attribute "optremark", so I need to be able to
access the SILFunction in the optimization remark infrastructure.
This is just an initial implementation and doesn't handle all cases. This works
by grabbing the rc-identity root of a retain/release operation and then seeing:
1. If it is an argument, we use the ValueDecl of the argument.
2. If it is an instruction result, we try to infer the ValueDecl by looking for debug_value uses.
3. In the future, we should add support for globals and looking at addresses.
I may do 3 since it is important to have support for that due to inout objects.
In order to test this, I implemented a small source loc inference routine for
instructions without valid SILLocations. This is an optional nob that the
opt-remark writer can optionally enable on a per remark basis. The current
behaviors are just forward/backward scans in the same basic block. If we scan
forwards, if we find a valid SourceLoc, we just use ethat. If we are scanning
backwards, instead we grab the SourceRange and if it is valid use the end source
range of the given instruction. This seems to give a good result for retain
(forward scan) and release (backward scan).
The specific reason that I did that is that my test case for this are
retain/release operations. Often times these operations due to code-motion are
moved around (and rightly to prevent user confusion) given by optimizations auto
generated SIL locations. Since that is the test case I am using, to test this I
needed said engine.
We do allow for limited def-use traversal to do things like find debug_value.
But nothing like a full data flow pass. As a proof of concept I just emit
opt-remarks when we see retains, releases. Eventually I would like to also print
out which lvalue the retain is from but that is more than I want to do with this
proof of concept.