In C++, we always expected to invoke the dtor for moved-from objects.
This is not the case for swift. Fortunately, @inCxx calling convention
is already expressing that the caller supposed to destroy the object.
This fixes the missing dtor calls when calling C++ functions taking
rvalue references. Fixes#77894.
rdar://140786022
Although I don't plan to bring over new assertions wholesale
into the current qualification branch, it's entirely possible
that various minor changes in main will use the new assertions;
having this basic support in the release branch will simplify that.
(This is why I'm adding the includes as a separate pass from
rewriting the individual assertions)
This allows to run the NamedReturnValueOptimization only late in the pipeline.
The optimization shouldn't be done before serialization, because it might prevent predictable memory optimizations in the caller after inlining.
This invalidation kind is used when a compute-effects pass changes function effects.
Also, let optimization passes which don't change effects only invalidate the `FunctionBody` and not `Everything`.
This patch replace all in-memory objects of DebugValueAddrInst with
DebugValueInst + op_deref, and duplicates logics that handles
DebugValueAddrInst with the latter. All related check in the tests
have been updated as well.
Note that this patch neither remove the DebugValueAddrInst class nor
remove `debug_value_addr` syntax in the test inputs.
After hoisting the destroy of the copy src, debug_value_addr users of the
copy src become dead, this PR cleans them so that MemoryLifetimeVerifier does not
complain later.
The premise of CopyForwarding was that memory locations have their own
ownership lifetime. We knew this was unmaintainable at the time, and
that was the original incentive for SIL opaque values, aided by
OSSA. In the meantime, we've been relying on SILGen producing
reasonable SIL patterns. Unfortunately, the CopyForwarding pass is
still with us while we make major changes to SIL ownership patterns
and agressively optimize ownership. That introduces risk.
Ultimately, the entire CopyForwarding pass should be redesigned for
OSSA-only and destroy hoisting should be a simple OSSA utility where
most of the work is done by AccessPath::collectUses.
But in the meantime, we should remove the source of risk by limiting
the CopyForwarding pass to OSSA. Any performance regressions will be
recovered as OSSA moves later in the pipeline. After that, opaque
values will improve even more over the current state by handling
generic SIL using the more powerful CopyPropagation pass.
Fixes rdar://71584600 (miscompile in CopyForwarding's release hoisting)
Here's an example of the kind of SIL the CopyForwarding does not
anticipate (although it only actually miscompiles in much more obscure
scenarios, which is why it's so dangerous):
bb0(%0 : $AnyObject):
%alloc1 = alloc_stack $AnyObject
store %0 to %objaddr : $*AnyObject
%ref = load %objaddr : $*AnyObject
%alloc2 = alloc_stack $ObjWrapper
# The in-memory reference is destroyed before retaining the loaded ref.
copy_addr [take] %alloc1 to [initialization] %alloc2 : $*ObjWrapper
retain_value %ref : $AnyObject
LLVM, as of 77e0e9e17daf0865620abcd41f692ab0642367c4, now builds with
-Wsuggest-override. Let's clean up the swift sources rather than disable
the warning locally.
CopyForwarding attempts to enforce "normal" SIL address patterns using
asserts. This isn't a good strategy because it results in strange
crash diagnostics in release builds. Eventually, we should replace
this logic with a SIL address lifetime utility based on OSSA form and
enforced in the verifier.
Loosen one of these restrictions where we assume that a value
initialized with "copy_addr [initialization]" will be properly
destroyed. This assumption is violated when lowering
.int_fma_FPIEEE32, which knows that the type is trivial, so avoids
deinitialization.
The original SIL looks like this:
copy_addr %src to [initialization] %dest : $*Float
%fma = builtin "int_fma_FPIEEE32"(% : $Builtin.FPIEEE32, % : $Builtin.FPIEEE32, % : $Builtin.FPIEEE32) : $Builtin.FPIEEE32
%result = struct $Float (%fma : $Builtin.FPIEEE32)
store %result to %dest : $*Float
Fixes rdar://64671864.
This avoids use-after-free bugs that can be introduced by removing a
copy without replacing all corresponding destroys.
Add more descriptive comments since multiple bugs have been introduced
in this pass.
None of this will be relevant once the pass is converted to OSSA.
1. WitnessMethodInst doesn't need to be handled as use because it's operand is a typedependent operand, which we exclude anyway when looking at the uses.
2. Replace a list of handled instructions with a default value in the switch. It's easy to forget to add an instruction here. So it's safer to have a default behavior.
The XXOptUtils.h convention is already established and parallels
the SIL/XXUtils convention.
New:
- InstOptUtils.h
- CFGOptUtils.h
- BasicBlockOptUtils.h
- ValueLifetime.h
Removed:
- Local.h
- Two conflicting CFG.h files
This reorganization is helpful before I introduce more
utilities for block cloning similar to SinkAddressProjections.
Move the control flow utilies out of Local.h, which was an
unreadable, unprincipled mess. Rename it to InstOptUtils.h, and
confine it to small APIs for working with individual instructions.
These are the optimizer's additions to /SIL/InstUtils.h.
Rename CFG.h to CFGOptUtils.h and remove the one in /Analysis. Now
there is only SIL/CFG.h, resolving the naming conflict within the
swift project (this has always been a problem for source tools). Limit
this header to low-level APIs for working with branches and CFG edges.
Add BasicBlockOptUtils.h for block level transforms (it makes me sad
that I can't use BBOptUtils.h, but SIL already has
BasicBlockUtils.h). These are larger APIs for cloning or removing
whole blocks.
Now we handle this case:
%stack = alloc_stack $Protocol
copy_addr %var to [initialization] %stack
open_existential_addr immutable_access %stack
...
destroy_addr %stack
dealloc_stack %stack
We only do this if we have immutable_access. To be conservative I still only let
the normal whitelist of other instructions through.
I am adding this optimization since I am going to be eliminating a SILGen
peephole so I can enable SIL ownership verification everywhere.
A recent fix added some code that is inconsistent with the design of
the pass, creates an implicit coupling between separate parts of the
optimization, and relies on incredibly subtle reasoning to ensure
correctness.
This sort of thing needs a big fat comment.
This disables a bunch of passes when ownership is enabled. This will allow me to
keep transparent functions in ossa and skip most of the performance pipeline without
being touched by passes that have not been updated for ownership.
This is important so that we can in -Onone code import transparent functions and
inline them into other ossa functions (you can't inline from ossa => non-ossa).
The problematic scenario is that a function receives an @in_guaranteed and @inout parameter where one is a copy of the other value. For example, when appending a container to itself.
In this case the optimization removed the copy_addr, resulting in passing the same stack location to both parameters.
rdar://problem/47632890
Once the algorithm has begun hoisting destroys globally, there's no
way to cleanly bailout. The previous attempt to bailout could result
in an assert or lost destroy in release mode.
This is continued fall-out from changes in the previous release to
upstream SILGen or mandatory passes, such as PredictableMemOps, that
no longer preserve natural variable lifetimes.
In this case, we end up with SIL like this before CopyForwarding:
bb(%arg)
%local_addr = alloc_stack
store %arg to %local_addr
%payload = switch_enum(%arg)
retain %arg
store %arg to %some_addr
destroy_addr %local_addr
release_value %arg
We're attempting to hoist the destroy_addr to its last use, but can't
because the lifetimes of the alloc_stack (%local_addr) and the value
being stored on the stack (%arg) have become mixed up by an upstream
pass. We actually detect this situation now in order to bail-out of
destroy hoisting. Sadly, the bailout might only partially recover in
the case of interesting control flow, as happens in the test case's
Graph.init function. This triggers an assert, but in release mode it
simply drops the destroy.
Fixed <rdar://problem/43888666> [SR-8526]: Memory leak after switch in
release configuration.
At least most of these were latent bugs since the code was
unreachable in the PartialApply case. But that's no excuse to misuse
the API.
Also, whenever referring to an integer index, be explicit about
whether it is an applied argument or callee argument.
Fixes <rdar://problem/39209102> [SR-7354]: Swift 4.1 Regression: EXC_BAD_ACCESS for Optimized Builds in Xcode 9.3
Commentary:
The underlying problem is that CopyForwarding is an inherently
dangerous pass by design that has been waiting for the SIL
representation to evolve to the point where it can be rewritten.
The regressions was caused by PredictableMemOps failing to preserve
normal patterns of ownership. When it forwards loads, it implicitly
extends the lifetime of stored value
store %val to %addr
...
retain %val
...
destroy_addr %addr
CopyForwarding already tried to detect such violations of ownership
rules and normally bypasses destroy hoisting in those cases. In this
case, it fails to detect the problem because PredictableMemOps has
already erased the load, so there's no evidence of the value's
lifetime being extended.
It might have been nice if PredictableMemOps had transformed the
retain %val into a retain %addr. However, for the immediate fix, we
don't want to change any existing behavior other than suppressing
optimization. In the long term, CopyForwarding does not really make
sense without SemanticSIL+SILOwnership and should be totally rewritten
and greatly simplified at that point.
Reviewing the code with Arnold revealed a corner case where forward propagating
a copy into multiple operands of the same instruction wasn't properly detected.
I don't think this case was possible given the language rules, but nonetheless
it is valid SIL and needs to be handled.
This is confusing because in some cases we optimize that situation correctly,
and in other cases we try to assert that it doesn't happen. So, I simplified the
code to bailout anywhere that we see multiple operands of the same value. This
isn't an expected situation that needs to be optimized.
Copy forwarding was designed with some assumptions about symmetry of
operations. If copy_value/destroy_value are expanded somewhere for a given
value, then they should be expanded everywhere. The pass took a conservative
approach to SIL patterns that were guaranteed safe, and bailed out on unknown
patterns. However, due to some over-aggressive code factoring, the assumption
wasn't being checked in one corner case.
This redesign makes a clear distinction between the requirements for forward
vs. backward propagation.
Fixes <rdar://35646292> Swift CI: resilience bot seg faults in
stdlib/RangeReplaceable.swift.gyb
introduce a common superclass, SILNode.
This is in preparation for allowing instructions to have multiple
results. It is also a somewhat more elegant representation for
instructions that have zero results. Instructions that are known
to have exactly one result inherit from a class, SingleValueInstruction,
that subclasses both ValueBase and SILInstruction. Some care must be
taken when working with SILNode pointers and testing for equality;
please see the comment on SILNode for more information.
A number of SIL passes needed to be updated in order to handle this
new distinction between SIL values and SIL instructions.
Note that the SIL parser is now stricter about not trying to assign
a result value from an instruction (like 'return' or 'strong_retain')
that does not produce any.
The etymology of these terms isn't about race, but "black" = "blocked"
and "white" = "allowed" isn't really a good look these days. In most
cases we weren't using these terms particularly precisely anyway, so
the rephrasing is actually an improvement.
This is a separate optimization that detects short-lived temporaries that can be eliminated.
This is necessary now that SILGen no longer performs basic RValue forwarding in some cases.
SR-5508: Performance regression in benchmarks caused by removing SILGen peephole for LoadExpr in +0 context
At some point, pass definitions were heavily macro-ized. Pass
descriptive names were added in two places. This is not only redundant
but a source of confusion. You could waste a lot of time grepping for
the wrong string. I removed all the getName() overrides which, at
around 90 passes, was a fairly significant amount of code bloat.
Any pass that we want to be able to invoke by name from a tool
(sil-opt) or pipeline plan *should* have unique type name, enum value,
commend-line string, and name string. I removed a comment about the
various inliner passes that contradicted that.
Side note: We should be consistent with the policy that a pass is
identified by its type. We have a couple passes, LICM and CSE, which
currently violate that convention.