Rather than recognizing specific patterns of closures that we've run
across, handle a broad set of possible SIL patterns. This includes
directly applied closures and reapplied closures.
I'm not sure how to expose these SIL patterns with source level
tests. However, unlike optimization passes, the exclusivity
diagnostics cannot conservatively ignore or bail out on unrecognized
SIL, and we cannot allow future changes to SILGen and mandatory passes
to create new holes in the exclusivity model.
Use begin_unpaired_access [no_nested_conflict] for
Builtin.performInstantaneousReadAccess. This can't be optimized away
and is the proper marker to use when the access scope is unknown.
Drop the requirement that
_semantics("optimize.sil.preserve_exclusivity") be @inline(never). We
actually want theses inlined into user code. Verify that the
@_semantic functions are not inlined or otherwise tampered with prior
to serialization.
Make *no* change to propagate @inline(__always) into LLVM. This no longer has
any relationship to this PR and can be investigated seperately.
This is a special @_semantics attribute that preserves exclusivity even if we
are eliminating exclusivity in other parts of a module in release mode. This is
done by:
1. Teaching the Access Marker Elimination pass to skip any function with the
semantics tag.
2. Requiring all functions with the semantics tag to be noinline. This ensures
that the SIL level inliner will not inline these functions into any callers
without the protection of the semantics tag. This is enforced in IRGenPrepare
and ensures that our access markers will live to IRGen time.
3. In IRGenPrepare, we convert these functions from noinline to always
inline. After IRGen this then allows for the LLVM inliner to inline these
trivial functions that just perform the exclusivity checks ensuring that we do
not have extra calls in the fast path.
This ensures that we can fix the keypaths exclusivity issue without having to
enable exclusivity across the entire stdlib and deal with any of the potential
performance issues therein.
rdar://39335800
I am going to be adding logic here to enable apple/swift#1550 to be completed.
The rename makes sense due to precedent from LLVM's codegen prepare and also
since I am going to be expanding what the pass is doing beyond just "cleaning
up". It is really a grab bag pass for performing simple transformations that we
do not want to pollute IRGen's logic with.
https://github.com/apple/swift/pull/15502
rdar://39335800
We only need to deserialize the function itself, not its transitive
dependencies. Also, only deserialize a function after we've checked
that its transparent.
For now, this doesn't reduce the volume of SIL linking, because the
mandatory linker pass still links everything. But we're almost
there.
Added new files, MemAccessUtils.h and MemAccessUtils.cpp to house the
new utility. Three functions previously in InstructionUtils.h move
here:
- findAccessedAddressBase
- isPossibleFormalAccessBase
- visitAccessedAddress
Rather than working with SILValues, these routines now work with the
AccessedStorage abstraction. This allows enforcement logic and SIL
pattern recognition to be shared across diagnostics and
optimization. (It's very important for this to be consistent).
The new AccessedStorage utility is a superset of the class that was
local to DiagnoseStaticExclusivity. It exposes the full set of all
recognized kinds of storage. It also represents function arguments as
an index rather that a SILValue. This allows an analysis pass to
compare/merge AccessedStorage results from multiple callee functions,
or more naturally propagate from callee to caller context.
We only need to deserialize the function itself, not its transitive
dependencies. Also, only deserialize a function after we've checked
that its transparent.
For now, this doesn't reduce the volume of SIL linking, because the
mandatory linker pass still links everything. But we're almost
there.
closure lifetimes.
SILGen will now unconditionally emit
%cvt = convert_escape_to_noescape [guaranteed] %op
instructions. The mandatory ClosureLifetimeFixup pass ensures that %op's
lifetime spans %cvt's uses.
The code in DefiniteInitialization that handled a subset of cases is
removed.
To replace the code in DI and eventually remove PostponedCleanup in a
follow-up.
When SILGen emits ``convert_escape_to_noescape [not_guaranteed]
%operand`` instructions it assumes that a later SIL pass (this pass)
comes along and inserts retain_value/release_value instructions such that
the lifetime of the operand for the duration of the trivial closure
result.
This commit introduces the pass but does not yet use it.
To mark when a user of it is known to escape the value. This happens
with materializeForSet arguments which are captured and used in the
write-back. This means we need to keep the context alive until after
the write-back.
Follow-up patches to fully replace the PostponedCleanup hack in SILGen
by a mandatory SIL transformation pass to guarantee the proper lifetime
will use this flag to be more conservative when extending the lifetime.
The problem:
%pa = partial_apply %f(%some_context)
%cvt = convert_escape_to_noescape [not_guaranteed] [escaped] %pa
%ptr = %materialize_for_set(..., %cvt)
... write_back
... // <-- %pa needs to be alive until after write_back
Once the dust settled, this logic was only protecting a single occurrence of
memory access in SILGenLValue.cpp. It's simpler just to emit the begin/end
access markers in that case. The logic to work around it was nasty.
In Swift 4.1 we fixed a false negative and started looking through
reabstraction thunks to statically find exclusivity violations. To lessen
source impact, we treated these violations as warnings. This commit upgrades
those warnings to errors.
rdar://problem/34669400
If we have an apply of a partial_apply, all the substitutions
are performed at the partial_apply. We don't have substitutions
on the apply itself. This is unlikely to change in the future,
and it's not valid to 'concatenate' two lists of substitutions
like this anyway.
-enforce-exclusivity=checked now does what it says. It emits checks at -O, not
just -Onone.
This does not change the default.
-Onone default: -enforce-exclusivity=checked
-O default: -enforce-exclusivity=unchecked
This is a module pass, so was processing a combination of raw and deserialized
canonical SIL. The analysis makes structural assumptions that only hold prior to
mandatory inlining.
Add more verification of the structural properties.
Teach the pass to skip over canonical SIL, which only works because closures
must be serialized/deserialized in the same module as their parent.
Fixes <rdar://38042781> [Repl] crash while running SILOptimizer
Until other bugs are fixed, this is still required in the short term after
mandatory inlining of debug libraries into an optimized executable.
Also, enable rerunning access marker elimination on deserialized functions,
because theoretically, the same problem could occur if we emit an external
function without inlining it.
Add a new warning that detects when a function will call itself
recursively on all code paths. Attempts to invoke functions like this
may cause unbounded stack growth at least or undefined behavior in the
worst cases.
The detection code is implemented as DFS for a reachable exit path in
a given SILFunction.