Previously, we would put a destroy_value directly on the value that we tried to
cast. Since checked_cast_br is consuming, this would cause the destroy_value on
the failure path to be flagged as a double consume.
This commit causes SILGen to emit the value consumed by checked_cast_br as an
@owned argument to the failure BB, allowing semantic arc rules to be respected.
As an additional benefit, I also upgraded the ownership_model_eliminator test to
use semantic sil verification.
One issue that did come up though is that I was unable to use the new code in
all locations in the compiler. Specifically, there is one location in
SILGenPattern that uses argument unforwarding. I am going to need to undo
argument unforwarding in SILGenPattern in order to completely eliminate the old
code path.
First, use the correct generic environment to compute the substituted
storage type. Substitutions derived from 'self' are not enough,
because we also want the archetypes of the generic subscript's
innermost generic parameters.
Also, use the method and witness_method calling conventions for the
materializeForSet callback, depending on if we have a protocol
witness or concrete implementation.
Since the materializeForSet callback is called with a more
abstract type at the call site than the actual function type
of the callback, we used to rely on these two SIL types being
ABI compatible:
@convention(thin) <Self : P, T, U) (..., Self.Type) -> ()
@convention(thin) <T, U> (..., Foo<T, U>.Type) -> ()
The IRGen lowering is roughly the following -- the call site
passes two unused parameters, but that's fine:
(..., Self.Type*, Self.Type*, Self.P*)
(..., Foo<T, U>.Type*)
However if the callback has its own generic parameters because
the subscript is generic, we might have SIL types like so,
@convention(thin) <Self : P, T, U, V) (..., Self.Type) -> ()
@convention(thin) <T, U, V> (..., Foo<T, U>.Type) -> ()
And the IRGen lowering is the following:
(..., Self.Type*, Self.Type*, Self.P*, V.Type*)
(..., Foo<T, U>.Type*, V.Type*)
The parameters no longer line up, because the caller still passes
the two discarded arguments, and type metadata for V cannot be
derived from the Self metadata so must be passed separately.
The witness_method calling convention is designed to solve this
problem; it puts the Self metadata and protocol conformance last,
so if you have these SIL types:
@convention(witness_method) <Self : P, T, U, V) (..., swiftself Self.Type) -> ()
@convention(witness_method) <T, U, V> (..., swiftself Foo<T, U>.Type) -> ()
The IRGen lowering is the following:
(..., Self.Type*, V.Type*, Self.Type*, Self.P*)
(..., Foo<T, U>.Type*, V.Type*, Self.Type*, unused i8*)
However, the problem is now that witness_method and thin functions
are not ABI compatible, because thin functions don't have a
distinguished 'self', which is passed differently in LLVM's swiftcc
calling convention:
@convention(witness_method) <Self : P, T, U, V) (..., Self.Type) -> ()
@convention(thin) <T, U, V> (..., Foo<T, U>.Type) -> ()
So instead of using 'thin' representation for the concrete callback
case, use 'method', which is essentially the same as 'thin' except if
the last parameter is pointer-size, it is passed as the 'self' value.
This makes everything work out.
This is a small corner case that simplifies the ownership verifier.
Specifically, today the ownership verifier has problems with the control
dependent nature of a cond_br's condition operand on the arguments of the
cond_br. By eliminating the possibility of values with ownership being
propagated along critical edges, the verifier can associate the arguments of the
cond_br with the destination blocks safely.
*NOTE* I ran a full testing run with sil-verify-all and this check did not
trigger once after SILGen. Thus I think it is safe to say that there is no real
effect of this change today. It is change ensuring that we maintain the current
behavior. As part of teaching the optimizer how to handle ownership, this
property will need to be pushed back there as well.
rdar://29791263
There are a few different use cases here:
1. In Raw SIL, no return folding may not have been run yet implying that a call
to a no-return function /can/ have arbitrary control flow after it (consider
mandatory inlined functions). We need to recognize that the region of code that
is strictly post dominated by the no-return function is "transitively
unreachable" and thus leaking is ok from that point. *Footnote 1*.
2. In Canonical and Raw SIL, we must recognize that unreachables and no-return
functions constitute places where we are allowed to leak.
rdar://29791263
----
*Footnote 1*: The reason why this is done is since we want to emit unreachable
code diagnostics when we run no-return folding. By leaving in the relevant code,
we have preserved all of the SILLocations on that code allowing us to create
really nice diagnostics.
SubstitutionList is going to be a more compact representation of
a SubstitutionMap, suitable for inline allocation inside another
object.
For now, it's just a typedef for ArrayRef<Substitution>.
Introduce an algorithm to canonicalize and minimize same-type
constraints. The algorithm itself computes the equivalence classes
that would exist if all explicitly-provided same-type constraints are
ignored, and then forms a minimal, canonical set of explicit same-type
constraints to reform the actual equivalence class known to the type
checker. This should eliminate a number of problems we've seen with
inconsistently-chosen same-type constraints affecting
canonicalization.
Storing this separately is unnecessary since we already
serialize the enum element's interface type. Also, this
eliminates one of the few remaining cases where we serialize
archetypes during AST serialization.
[NFC] Add -enable-sil-opaque-values frontend option.
This will be used to change the SIL-level calling convention for opaque values,
such as generics and resilient structs, to pass-by-value. Under this flag,
opaque values have SSA lifetimes, managed by copy_value and destroy_value.
This will make it easier to optimize copies and verify ownership.
* [SILGen] type lowering support for opaque values.
Add OpaqueValueTypeLowering.
Under EnableSILOpaqueValues, lower address-only types as opaque values.
* [SIL] Fix ValueOwnershipKind to support opaque SIL values.
* Test case: SILGen opaque value support for Parameter/ResultConvention.
* [SILGen] opaque value support for function arguments.
* Future Test case: SILGen opaque value specialDest arguments.
* Future Test case: SILGen opaque values: emitOpenExistential.
* Test case: SIL parsing support for EnableSILOpaqueValues.
* SILGen opaque values: prepareArchetypeCallee.
* [SIL Verify] allow copy_value for EnableSILOpaqueValues.
* Test cast: SIL serializer support for opaque values.
* Add a static_assert for ParameterConvention layout.
* Test case: Mandatory SILOpt support for EnableSILOpaqueValues.
* Test case: SILOpt support for EnableSILOpaqueValues.
* SILGen opaque values: TypeLowering emitCopyValue.
* SILBuilder createLoad. Allow loading opaque values.
* SIL Verifier. Allow loading and storing opaque values.
* SILGen emitSemanticStore support for opaque values.
* Test case for SILGen emitSemanticStore.
* Test case for SIL mandatory support for inout assignment.
* Fix SILGen opaque values test case after rebasing.
Formal types are defined by the language's type system. SIL types are
lowered. They are no longer part of that type system.
The important distinction here is between the SIL storage type and the SIL value
type. To make this distinction clear, I refer to the SILFunctionTypes "formal"
conventions. These conventions dictate the SIL storage type but *not* the SIL
value type. I call them "formal" conventions because they are an immutable
characteristic of the function's type and made explicit via qualifiers on the
function type's parameters and results. This is in contrast to to SIL
conventions which depend on the SIL stage, and in the short term whether the
opaque values flag is enabled.
Separate formal lowered types from SIL types.
The SIL type of an argument will depend on the SIL module's conventions.
The module conventions are determined by the SIL stage and LangOpts.
Almost NFC, but specialized manglings are broken incidentally as a result of
fixes to the way passes handle book-keeping of aruments. The mangler is fixed in
the subsequent commit.
Otherwise, NFC is intended, but quite possible do to rewriting the logic in many
places.
The typedef `swift::Module` was a temporary solution that allowed
`swift::Module` to be renamed to `swift::ModuleDecl` without requiring
every single callsite to be modified.
Modify all the callsites, and get rid of the typedef.
For this we need to store the linkage of the “original” method implementation in the vtable.
Otherwise DeadFunctionElimination thinks that the method implementation is not public but private (which is the linkage of the thunk).
The big part of this change is to extend SILVTable to store the linkage (+ serialization, printing, etc.).
fixes rdar://problem/29841635
Fixes assertion failures in SILGen and the optimizer with this
exotic setup:
protocol P {
associatedtype T : Q
}
protocol Q {
func requirement<U : P>(u: U) where U.T == Self
}
Here, we only have a U : P conformance, and not Self : Q,
because Self : Q is available as U.T : Q.
There were three problems here:
- The SIL verifier was too strict in verifying the generic signature.
All that matters is we can get the Self parameter conformance, not
that it's the first requirement, etc.
- GenericSignature::getSubstitutionMap() had a TODO concerning handling
of same-type constraints -- this is the first test-case I've found
that triggered the problem.
- GenericEnvironment::getSubstitutionMap() incorrectly ignored
same-type constraints where one of the two types was a generic
parameter.
Fixes <https://bugs.swift.org/browse/SR-3321>.
Not sure why but this was another "toxic utility method".
Most of the usages fell into one of three categories:
- The base value was always non-null, so we could just call
getCanonicalType() instead, making intent more explicit
- The result was being compared for equality, so we could
skip canonicalization and call isEqual() instead, removing
some boilerplate
- Utterly insane code that made no sense
There were only a couple of legitimate uses, and even there
open-coding the conditional null check made the code clearer.
Also while I'm at it, make the SIL open archetypes tracker
more typesafe by passing around ArchetypeType * instead of
Type and CanType.
This commit includes the dataflow verifier and plugs in the use checker into the
dataflow verifier.
Some specific checks in the use checker need revision, but I for today
this is good enough. As I go through SILGen I am going to fix them.
rdar://29671437
This is the first verifier for SemanticSIL. The verifier is very simple and
verifies that given a SILValue V, V->getOwnershipKind() returns an ownership
kind compatible with all of V's user instructions.
This is implemented by adding a new method to SILInstruction:
SILInstruction::verifyOperandOwnership()
This method creates an instance of the visitor OwnershipCompatibilityUseChecker
and then has the instance visit this.
The OwnershipCompatibilityUseChecker is a SILInstructionVisitor that for a given
instruction verifies that the given SILInstruction's operand SILValue's produce
ValueOwnershipKind that are compatible with the SILInstruction. The reason why
it is implemented as a visitor is to ensure that a warning is produced if a new
instruction is added and a method on the OwnershipCompatibleUseChecker isn't
added.
Keep in mind that this is just the first verifier and the full verifier (that
also verifies dataflow) is built on top of it. The reason why this separate API
to the use verifier is exposed is that exposing the checker enables us to place
an assert in SILBuilder to diagnose any places where SIL ownership is violated
immediately when the violation occurs allowing for an easy debugging experience
for compiler writers. This assert is a key tool that I am going to be using to
make SILGen conform to the SIL Ownership Model.
Again, this will be behind the -enable-semantic-sil flag, so normal development
will be unaffected by this change.
rdar://29671437
This was in the first high level ARC instruction proposal, but I have not needed
it until now. The use case for this is to ahandle strong_retain_unowned (which
takes in an unowned value, asserts it is still alive, performs a strong_retain,
and returns the @owned value). This @owned value needs a destroy_value.
rdar://29671437
This simplifies the SILType substitution APIs and brings them in line with Doug and Slava's refactorings to improve AST-level type substitution. NFC intended.
Applying nontrivial generic arguments to a nontrivial SIL layout requires lowered SILType substitution, which requires a SILModule. NFC yet, just an API change.
There's no longer a single element type to speak of. Update uses to either iterate all box fields or to assert that they're working with a single-field box.
This was made redundant by typed boxes, and the type operand was already removed from textual SIL, but the field was never removed from the instruction's in memory representation. It becomes wrong in the face of compound boxes with layout.
This was already done for getSuccessorBlocks() to distinguish getting successor
blocks from getting the full list of SILSuccessors via getSuccessors(). This
commit just makes all of the successor/predecessor code follow that naming
convention.
Some examples:
getSingleSuccessor() => getSingleSuccessorBlock().
isSuccessor() => isSuccessorBlock().
getPreds() => getPredecessorBlocks().
Really, IMO, we should consider renaming SILSuccessor to a more verbose name so
that it is clear that it is more of an internal detail of SILBasicBlock's
implementation rather than something that one should consider as apart of one's
mental model of the IR when one really wants to be thinking about predecessor
and successor blocks. But that is not what this commit is trying to change, it
is just trying to eliminate a bit of technical debt by making the naming
conventions here consistent.
Before this commit all code relating to handling arguments in SILBasicBlock had
somewhere in the name BB. This is redundant given that the class's name is
already SILBasicBlock. This commit drops those names.
Some examples:
getBBArg() => getArgument()
BBArgList => ArgumentList
bbarg_begin() => args_begin()
Optional now lowers its payload type, and we lower away the Optional/IUO distinction in SIL's type system, but the verifier was not updated to account for this.