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)
`std::optional` doesn't have `hasValue` or `getPointer`. Using the
implicit decay to boolean, and grabbing the pointer to the element by
expanding the optional and grabbing the reference address.
Reformatting everything now that we have `llvm` namespaces. I've
separated this from the main commit to help manage merge-conflicts and
for making it a bit easier to read the mega-patch.
This is phase-1 of switching from llvm::Optional to std::optional in the
next rebranch. llvm::Optional was removed from upstream LLVM, so we need
to migrate off rather soon. On Darwin, std::optional, and llvm::Optional
have the same layout, so we don't need to be as concerned about ABI
beyond the name mangling. `llvm::Optional` is only returned from one
function in
```
getStandardTypeSubst(StringRef TypeName,
bool allowConcurrencyManglings);
```
It's the return value, so it should not impact the mangling of the
function, and the layout is the same as `std::optional`, so it should be
mostly okay. This function doesn't appear to have users, and the ABI was
already broken 2 years ago for concurrency and no one seemed to notice
so this should be "okay".
I'm doing the migration incrementally so that folks working on main can
cherry-pick back to the release/5.9 branch. Once 5.9 is done and locked
away, then we can go through and finish the replacement. Since `None`
and `Optional` show up in contexts where they are not `llvm::None` and
`llvm::Optional`, I'm preparing the work now by going through and
removing the namespace unwrapping and making the `llvm` namespace
explicit. This should make it fairly mechanical to go through and
replace llvm::Optional with std::optional, and llvm::None with
std::nullopt. It's also a change that can be brought onto the
release/5.9 with minimal impact. This should be an NFC change.
Although nonescaping closures are representationally trivial pointers to their
on-stack context, it is useful to model them as borrowing their captures, which
allows for checking correct use of move-only values across the closure, and
lets us model the lifetime dependence between a closure and its captures without
an ad-hoc web of `mark_dependence` instructions.
During ownership elimination, We eliminate copy/destroy_value instructions and
end the partial_apply's lifetime with an explicit dealloc_stack as before,
for compatibility with existing IRGen and non-OSSA aware passes.
`getValue` -> `value`
`getValueOr` -> `value_or`
`hasValue` -> `has_value`
`map` -> `transform`
The old API will be deprecated in the rebranch.
To avoid merge conflicts, use the new API already in the main branch.
rdar://102362022
An error found in DiagnoseInvalidEscapingCaptures can indicate invalid
SIL, which will cause DiagnoseStaticExclusivity to assert during SIL
verification. When the source-level closure captures an inout
argument, it appears in SIL to be a non-escaping closure. The SIL
verification then fails because the "nonescaping" closure actually
escapes.
Ensure that capture diagnostics run on closures before exclusivity
enforcement runs on the parent function. Bypass the SIL verification
assert if a diagnostic error was found.
Fixes rdar://75364904 (Crash with assertion `noescape partial_apply
has unexpected use`)
Add AccesssedStorage::compute and computeInScope to mirror AccessPath.
Allow recovering the begin_access for Nested storage.
Adds AccessedStorage.visitRoots().
Things that have come up recently but are somewhat blocked on this:
- Moving AccessMarkerElimination down in the pipeline
- SemanticARCOpts correctness and improvements
- AliasAnalysis improvements
- LICM performance regressions
- RLE/DSE improvements
Begin to formalize the model for valid memory access in SIL. Ignoring
ownership, every access is a def-use chain in three parts:
object root -> formal access base -> memory operation address
AccessPath abstracts over this path and standardizes the identity of a
memory access throughout the optimizer. This abstraction is the basis
for a new AccessPathVerification.
With that verification, we now have all the properties we need for the
type of analysis requires for exclusivity enforcement, but now
generalized for any memory analysis. This is suitable for an extremely
lightweight analysis with no side data structures. We currently have a
massive amount of ad-hoc memory analysis throughout SIL, which is
incredibly unmaintainable, bug-prone, and not performance-robust. We
can begin taking advantage of this verifably complete model to solve
that problem.
The properties this gives us are:
Access analysis must be complete over memory operations: every memory
operation needs a recognizable valid access. An access can be
unidentified only to the extent that it is rooted in some non-address
type and we can prove that it is at least *not* part of an access to a
nominal class or global property. Pointer provenance is also required
for future IRGen-level bitfield optimizations.
Access analysis must be complete over address users: for an identified
object root all memory accesses including subobjects must be
discoverable.
Access analysis must be symmetric: use-def and def-use analysis must
be consistent.
AccessPath is merely a wrapper around the existing accessed-storage
utilities and IndexTrieNode. Existing passes already very succesfully
use this approach, but in an ad-hoc way. With a general utility we
can:
- update passes to use this approach to identify memory access,
reducing the space and time complexity of those algorithms.
- implement an inexpensive on-the-fly, debug mode address lifetime analysis
- implement a lightweight debug mode alias analysis
- ultimately improve the power, efficiency, and maintainability of
full alias analysis
- make our type-based alias analysis sensistive to the access path
...and avoid reallocation.
This is immediately necessary for LICM, in addition to its current
uses. I suspect this could be used by many passes that work with
addresses. RLE/DSE should absolutely migrate to it.
For use outside access enforcement passes.
Add isUniquelyIdentifiedAfterEnforcement.
Rename functions for clarity and generality.
Rename isUniquelyIdentifiedOrClass to isFormalAccessBase.
Rename findAccessedStorage to identifyFormalAccess.
Rename findAccessedStorageNonNested to findAccessedStorage.
Part of generalizing the utility for use outside the access
enforcement passes.
Potentially source breaking: SR-11700 Diagnose exclusivity violations
with Dictionary.subscript._modify:
Exclusivity violations within code that computes the `default`
argument during Dictionary access are now diagnosed.
```swift
struct Container {
static let defaultKey = 0
var dictionary = [defaultKey:0]
mutating func incrementValue(at key: Int) {
dictionary[key, default: dictionary[Container.defaultKey]!] += 1
}
}
error: overlapping accesses to 'self.dictionary', but modification requires exclusive access; consider copying to a local variable
dictionary[key, default: dictionary[Container.defaultKey]!] += 1
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
note: conflicting access is here
dictionary[key, default: dictionary[Container.defaultKey]!] += 1
~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~
```
This reworks the logic so that four problems end up being fixed:
Fixes three problems related to coroutines:
(1) DiagnoseStaticExclusivity must consider begin_apply as a user of accessed variables. This was an undefined behavior hole in the diagnostics.
(2) AccessedSummaryAnalysis should consider begin_apply as a user of accessed arguments. This does not show up in practice because coroutines don't capture things.
(3) AccessedSummaryAnalysis must consider begin_apply a valid user of
noescape closures.
And fixes one problem related to resilience:
(4) AccessedSummaryAnalysis must conservatively consider arguments to external functions.
Fixes <rdar://problem/56378713> Investigate why AccessSummaryAnalysis is crashing
With the advent of dynamic_function_ref the actual callee of such a ref
my vary. Optimizations should not assume to know the content of a
function referenced by dynamic_function_ref. Introduce
getReferencedFunctionOrNull which will return null for such function
refs. And getInitialReferencedFunction to return the referenced
function.
Use as appropriate.
rdar://50959798
Previously, any function marked [dynamically_replaceable] that was
partially applied and captured by address would not be diagnosed.
This is a rare thing. For example:
struct S {
var x = 0
public mutating func testCallDynamic() {
dynamic func bar(_ i: inout Int) {
i = 1
x = 2
}
bar(&x)
}
}
Fixes <rdar://problem/50972786> Fix exclusivity diagnostics to be
aware of [dynamically_replaceable].
The new pass is based on existing asserts in DiagnoseStaticExclusivity.
They were compiled out in release builds and only checked for captures of
inout parameters. This patch converts the assertions into diagnostics and
adds checks for captures of non-escaping function values.
Unlike the Sema-based checks that this replaces, the new code handles
transitive captures from recursive local functions, which means certain
invalid code that used to compile will now be rejected with an error.
The new analysis also looks at the ultimate usages of a local function
instead of just assuming all local functions are escaping, which fixes
issues where the compiler would reject valid code.
Fixes a bunch of related issues, including:
- <rdar://problem/29403178>
- <https://bugs.swift.org/browse/SR-8546> / <rdar://problem/43355341>
- <https://bugs.swift.org/browse/SR-9043> / <rdar://problem/45511834>
Remove the compiler support for exclusivity warnings.
Leave runtime support for exclusivity warnings in non-release builds
only for unit testing convenience.
Remove a test case that checked the warning log output.
Modify test cases that relied on successful compilation in the
presence of exclusivity violations.
Fixes: <rdar://problem/45146046> Remaining -swift-version 3 tests for exclusivity
This silences the instances of the warning from Visual Studio about not all
codepaths returning a value. This makes the output more readable and less
likely to lose useful warnings. NFC.
- getAsDeclOrDeclExtensionContext -> getAsDecl
This is basically the same as a dyn_cast, so it should use a 'getAs'
name like TypeBase does.
- getAsNominalTypeOrNominalTypeExtensionContext -> getSelfNominalTypeDecl
- getAsClassOrClassExtensionContext -> getSelfClassDecl
- getAsEnumOrEnumExtensionContext -> getSelfEnumDecl
- getAsStructOrStructExtensionContext -> getSelfStructDecl
- getAsProtocolOrProtocolExtensionContext -> getSelfProtocolDecl
- getAsTypeOrTypeExtensionContext -> getSelfTypeDecl (private)
These do /not/ return some form of 'this'; instead, they get the
extended types when 'this' is an extension. They started off life with
'is' names, which makes sense, but changed to this at some point. The
names I went with match up with getSelfInterfaceType and
getSelfTypeInContext, even though strictly speaking they're closer to
what getDeclaredInterfaceType does. But it didn't seem right to claim
that an extension "declares" the ClassDecl here.
- getAsProtocolExtensionContext -> getExtendedProtocolDecl
Like the above, this didn't return the ExtensionDecl; it returned its
extended type.
This entire commit is a mechanical change: find-and-replace, followed
by manual reformatted but no code changes.
DiagnoseStaticExclusivity no longer asserts as follows when
non-escaping closures are passed to withoutActuallyEscaping:
Applied argument must be @noescape function type: ...
A partial_apply with @inout_aliasable may only be used as a @noescape
function type argument.
Subsequent commits will improve diagnostics to detect actual conflicts
in these situations.
Fixes <rdar://problem/43059088> Assertion in DiagnoseStaticExclusivity.
Add support to static diagnostics for tracking noescape closures through block
arguments.
Improve the noescape closure SIL verification logic to match. Cleanup noescape
closure handling in both diagnostics and SIL verification to be more
robust--they must perfectly match each other.
Fixes <rdar://problem/42560459> [Exclusivity] Failure to statically diagnose a
conflict when passing conditional noescape closures.
Initially reported in [SR-8266] Compiler crash when checking exclusivity of
inout alias.
Example:
struct S {
var x: Int
mutating func takeNoescapeClosure(_ f: ()->()) { f() }
mutating func testNoescapePartialApplyPhiUse(z : Bool) {
func f1() {
x = 1 // expected-note {{conflicting access is here}}
}
func f2() {
x = 1 // expected-note {{conflicting access is here}}
}
takeNoescapeClosure(z ? f1 : f2)
// expected-error@-1 2 {{overlapping accesses to 'self', but modification requires exclusive access; consider copying to a local variable}}
}
}
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.
Closures with @inout_aliasable argument convention may only be used as
nonescaping closures. However, In some cases, they are passed to reabstraction
thunks as escaping closures. The verification in DiagnoseStaticExclusivity,
which is necessary to ensure that the exclusivity model is complete, was
asserting on this case.
We can relax verification here without strengthening the diagnostic because the
diagnostic already had special handing for reabstraction thunks. The fix is to
use the same special handling during verification but in the reverse direction.
It would be preferable to disallow this SIL pattern, but changing the compiler
in 4.2 is too risky. Teaching the verifier about this does not actually weaken
verification, so that's the better approach.
Fixes <rdar://problem/41976355> [SR-8201]: Swift 4.2 Crash in DiagnoseStaticExclusivity (llvm_unreachable)
Now that access marker verification is strict and exhaustive, adjust some code
to handle the extra markers and extra checks produced by -enable-verify-exclusivity.
Now that SILGen change adds Unsafe access markers to addressors and
materializeForSet, we can use that as a sentinel to enable strict
verification everywhere.
* Teach findAccessedStorage about global addressors.
AccessedStorage now properly represents access to global variables, even if they
haven't been fully optimized down to global_addr instructions.
This is essential for optimizing dynamic exclusivity checks. As a
verified SIL property, all access to globals and class properties
needs to be identifiable.
* Add stronger SILVerifier support for formal access.
Ensure that all formal access follows recognizable patterns
at all points in the SIL pipeline.
This is important to run acccess enforcement optimization late in the pipeline.
The file is more approachable now. The data flow algorithm is self-contained in
about 250 lines, split into manageable routines, with separate data flow state.
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.