As long as begin/end access scopes are respected, enforce-exclusivity does not
actually care about address phis.
Eventually, we will disable address phis completely, but that requires adding a
"address legalization" transform to various CFG passes, such as jump threading
and loop rotate.
Meanwhile, we will continue to suppress these CFG transformations when access
markers are involved. This means that optimizations will work differently, and
some will be suppressed in the presence of access markers. That means that
optimizing the access markers themselves will be a prerequisite to enabling them
by default, even if the overhead of the checks is tolerable.
As a structural SIL property, we disallow address-type block
arguments. Supporting them would prevent reliably reasoning about the
underlying storage of memory access. This reasoning is important for
diagnosing violations of memory access rules and supporting future
optimizations such as bitfield packing. Address-type block arguments
also create unnecessary complexity for SIL optimization passes that
need to reason about memory aliasing.
This must be enforced in RAW SIL for diagnosing exclusive memory
access. The optimizer currently breaks the invariant in three places:
1. Normal Simplify CFG during conditional branch simplification
(sneaky jump threading).
2. Simplify CFG via Jump Threading.
3. Loop Rotation.
This way we'll link against the key path component the other module provides instead of making fragile assumptions about its current implementation. Since external keypath lowering isn't fully implemented elsewhere in the compiler, this is enabled behind a staging flag.
external keypath staging
This patch both makes debug variable information it optional on
alloc_stack and alloc_box instructions, and forced variable
information on debug_value and debug_value_addr instructions. The
change of the interface uncovered a plethora of bugs in SILGen,
SILTransform, and IRGen's LoadableByAddress pass.
Most importantly this fixes the previously commented part of the
DebugInfo/local-vars.swift.gyb testcase.
rdar://problem/37720555
When working with tuples, if the tuple does not contain a large loadable type but does contain a function signature, we currently do nothing.
We should convert the function signature inside the tuple type instead.
This will allow key paths to resiliently reference public properties from other binaries by referencing a descriptor vended by the originating binary. NFC yet, this just provides printing/parsing/verification of the new component.
- @noescape functions are trivial types
That makes @noescape functions incompatible with escaping functions.
- Forward ownership of mark_dependence %3 : callee_guaranteed () -> () on %0 : noescape @callee_guaranteed () -> ()
- SIL: Add ABIEscapeToNoEscapeConversion to SILFunctionType::ABICompatibilityCheckResult
- SIL: A thin_to_thick_function with a @noescape result type has trivial ownership
- SIL: thin_to_thick_function can create noescape function types
Part of:
SR-5441
rdar://36116691
@noescape function types will eventually be trivial. A
convert_escape_to_noescape instruction does not take ownership of its
operand. It is a projection to the trivial value carried by the closure
-- both context and implementation function viewed as a trivial value.
A safe SIL program must ensure that the object that the project value is based
on is live beyond the last use of the trivial value. This will be
achieve by means of making the lifetimes dependent.
For example:
%e = partial_apply [callee_guaranteed] %f(%z) : $@convention(thin) (Builtin.Int64) -> ()
%n = convert_escape_to_noescape %e : $@callee_guaranteed () -> () to $@noescape @callee_guaranteed () -> ()
%n2 = mark_dependence %n : $@noescape @callee_guaranteed () -> () on %e : $@callee_guaranteed () -> ()
%f2 = function_ref @use : $@convention(thin) (@noescape @callee_guaranteed () -> ()) -> ()
apply %f2(%n2) : $@convention(thin) (@noescape @callee_guaranteed () -> ()) -> ()
release_value %e : $@callee_guaranteed () -> ()
Note: This is not yet actually used.
Part of:
SR-5441
rdar://36116691
This found a bunch of issues where we didn't properly preserve
debug infos which have been fixed over the course of the past
two weeks.
The basic idea is we want to make sure that within a BB we never
have `holes` in debug scopes, i.e. if we enter a scope, one of
the two holds
1) We never visited that scope before
2) We're re-entering a scope that is an ancestor of the scope
currently leaving. This is needed to correctly model nested
scopes, e.g.
{
let i = 0; // scope 0
if (cond) {
let j = i // scope 1
}
let k = i + 1 // scope 0 and okay.
}
I also checked in a `cl::opt`, so that if this breaks something,
it can be conveniently disabled. This is currently not enabled
as we flesh out the last violations.
<rdar://problem/35816212>
Another cleanup before I add a new SILLinkage kind.
This operation is difficult to reason about and was only used by
the SIL verifier. Replace the SIL verifier checks with simpler
operations.
I still need to revisit and uncomment the default witness table
visibility check. Right now we serialize default witness tables,
but default witness thunks have private linkage, which is
clearly wrong.
This has three principal advantages:
- It gives some additional type-safety when working
with known accessors.
- It makes it significantly easier to test whether a declaration
is an accessor and encourages the use of a common idiom.
- It saves a small amount of memory in both FuncDecl and its
serialized form.
Extend the SIL verifier to check for the assumptions that the static exclusivity
diagnostics make about conversions from noescape closures to noescape blocks.
Soundness of the static diagnostics requires that the analysis can locally
determine when a noescape closure is converted to a noescape and then
passed to a function.
We can just !SILFunction::hasQualifiedOwnership(). Plus as Andy pointed out,
even ignoring the functional aspects, having APIs with names this close can
create confusion.
Right now if convert_function is used incorrectly by SILGen, we get an error via
the SILVerifier after we have finished building the entire function. This is a
pain in the butt to track down. Using this refactor, I am able to just assert in
the SILBuilder itself.
On another note, down the line we should just separate the dataflow checker part
of the verifier from specific SILInstruction verification. This would allow for
SILBuilder to perform the SILInstruction verification upon creation giving the
same effect. Such a change is a much larger change though and this is just a
commit chopped off a larger commit.
rdar://34222540
The goal is to make it more composable to add trailing-objects fields in
a subclass.
While I was doing this, I noticed that the apply instructions provided
redundant getNumArguments() and getNumCallArguments() accessors, so I
went ahead and unified them.
This matches what LLVM does, and makes testing/fuzzing easier.
It's also slightly more friendly in case the user violates an
assumption. It can't be turned on by default (yet) because it's
important to crash on verification failures, when running swiftc
in a debugger.
This replaces the '[volatile]' flag. Now, class_method and
super_method are only used for vtable dispatch.
The witness_method instruction is still overloaded for use
with both ObjC protocol requirements and Swift protocol
requirements; the next step is to make it only mean the
latter, also using objc_method for ObjC protocol calls.
Pre-specializations need some special handling when it comes to the Serialized attribute. Their bodies should not be SIL serialized. Instead, only their declarations should be serialized.
And since their bodies are not serialized and cannot be imported by the client code, it is OK if pre-specializations reference non-fragile functions inside their bodies. Due to the same reason, it is fine if pre-specializations are referenced from fragile functions, even though these pre-specializations are not fragile in a usual sense.
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.