SemanticARCOpts keeps on growing with various optimizations attached to a single
"optimization" manager. Move it to its own folder in prepation for splitting it
into multiple different optimizations and utility files.
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.
LLVM, as of 77e0e9e17daf0865620abcd41f692ab0642367c4, now builds with
-Wsuggest-override. Let's clean up the swift sources rather than disable
the warning locally.
TLDR: This fixes an ownership verifier assert caused by not placing end_borrows
along paths where an enum is provable to have a trivial case. It only happens if
all non-trivial cases in a switch_enum are "dead end blocks" where the program
will end and we leak objects.
The Problem
-----------
The actual bug here only occurs in cases where we have a switch_enum on an enum
with mixed trivial, non-trivial cases and all of the non-trivial payloaded cases
are "dead end blocks". As an example, lets look at a simple switch_enum over an
optional where the .some case is a dead end block and we leak the Klass object
into program termination:
```
%0 = load [copy] %mem : $Klass
switch_enum %0 : $Optional<Klass>, case #Optional.some: bbDeadEnd, case #Optional.none: bbContinue
bbDeadEnd(%0a : @owned $Klass): // %0 is leaked into program end!
unreachable
bbContinue:
... // program continue.
```
In this case, if we were only looking at final destroying uses, we would pass a
def without any uses to the ValueLifetimeChecker causing us to not have a
frontier at all causing us to not insert any end_borrows, yielding:
```
%0 = load_borrow %mem : $Klass
switch_enum %0 : $Optional<Klass>, case #Optional.some: bbDeadEnd, case #Optional.none: bbContinue
bbDeadEnd(%0a : @guaranteed $Klass): // %0 is leaked into program end and
// doesnt need an end_borrow!
unreachable
bbContinue:
... // program continue... we need an end_borrow here though!
```
This then trips the ownership verifier since switch_enum is a transforming
terminator that acts like a forwarding instruction implying we need an
end_borrow on the base value along all non-dead end paths through the program.
Importantly this is not actually a leak of a value or unsafe behavior since the
only time that we enter into unsafe territory is along paths where the enum was
actually trivial. So the load_borrow is actually just loaded the trivial enum
value.
The Fix
-------
In order to work around this, I realized that the right solution is to also
include the forwarding consuming uses (in this case the switch_enum use) when
determining the lifetime and that this solves the problem.
That being said, after I made that change, I noticed that I needed to remove my
previous manner of computing the insertion point to use for arguments when
finding the lifetime using ValueLifetimeAnalysis. Previously since I was using
only the destroying uses I knew that the destroy_value could not be the first
instruction in the block of my argument since I handled that case individually
before using the ValueLifetimeAnalysis. That invariant is no longer true as can
be seen in the case above if %0 was from a SILArgument itself instead of a load
[copy] and we were converting that argument to be a guaranteed argument.
To fix this, I taught ValueLifetimeAnalysis how to handle defs from
Arguments. The key thing is I noticed while reading the code that the analysis
only generally cared about the instruction's parent block. Beyond that, the def
being from an instruction was only needed to determine if a user is earlier in
the same block as the def instruction. Those concerns not apply to SILArgument
which dominate all instructions in the same block, so in this patch, we just
skip those conditional checks when we have a SILArgument. The rest of the code
that uses the parent block is the same for both SILArgument/SILInstructions.
rdar://65244617
Specifically:
1. I made methods, variables camelCase.
2. I expanded out variable names (e.x.: bb -> block, predBB -> predBlocks, U -> wrappedUse).
3. I changed typedef -> using.
4. I changed a few c style for loops into for each loops using llvm::enumerate.
NOTE: I left the parts needed for syncing to LLVM in the old style since LLVM
needs these to exist for CRTP to work correctly for the SILSSAUpdater.
For now I am trying out /not/ expanding when ownership is enabled. I still fixed
the problems in it though. I also put in a force expand everything pass to make
sure that in ossa we can still successfully go down this code path if we want
to.
The reason why I think this is the right thing to do is that the original reason
why lower aggregate instrs was written was to help the low level arc optimizer
(which only runs on non-ossa code). To the high level arc optimizer this is just
noise and will keep the IR simpler.
Another thing to note is that I updated the code so it should work in both ossa
and non-ossa. In order to not perturb the code too much, I had to add a small
optimization to TypeLowering where when ownership is disabled, we do not reform
aggregates when copying. Instead, we just return the passed in value. This makes
the pass the same when optimizing in both modes.
An empty tuple doesn't need to be assigned before it is passed to destroy_addr.
I found this by experimenting with the pass pipeline. Not sure if this crash can happen with the current pipeline.
Since the two ExtInfos share a common ClangTypeInfo, and C++ doesn't let us
forward declare nested classes, we need to hoist out AnyFunctionType::ExtInfo
and SILFunctionType::ExtInfo to the top-level.
We also add some convenience APIs on (AST|SIL)ExtInfo for frequently used
withXYZ methods. Note that all non-default construction still goes through the
builder's build() method.
We do not add any checks for invariants here; those will be added later.
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'}}
}
```
Optimizes String operations with constant operands.
Specifically:
* Replaces x.append(y) with x = y if x is empty.
* Removes x.append("")
* Replaces x.append(y) with x = x + y if x and y are constant strings.
* Replaces _typeName(T.self) with a constant string if T is statically known.
With this optimization it's possible to constant fold string interpolations, like "the \(Int.self) type" -> "the Int type"
This new pass runs on high-level SIL, where semantic calls are still in place.
rdar://problem/65642843
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.