- code simplification critical for comprehension
- substantially improves the overhead of AccessedStorage comparison
- as a side effect improves precision of analysis in some cases
AccessedStorage is meant to be an immutable value type that identifies
a storage location with minimal representation. It is used in many global
interprocedural data structures.
The RefElementAddress instruction that it was derived from does not
contribute to the uniqueness of the storage location. It doesn't
belong here. It was being used to create a ProjectionPath, which is an
extremely inneficient way to compare access paths.
Just delete all the code related to that extra field.
The field's ordinal value is used by the Projection abstraction, which is
the basis of efficiently comparing and sorting access paths in SIL. It must
be cached before it is used by any SIL passes, including the verifier, or it
causes widespread quadratic complexity.
Fixes <rdar://problem/50353228> Swift compile time regression with optimizations enabled
In production code, a file that was taking 40 minutes to compile now
takes 1 minute, with more than half of the time in LLVM.
Here's a short script that reproduces the problem. It used to take 30s
and now takes 0.06s:
// swift genlazyinit.swift > lazyinit.sil
// sil-opt ./lazyinit.sil --access-enforcement-opts
var NumProperties = 300
print("""
sil_stage canonical
import Builtin
import Swift
import SwiftShims
public class LazyProperties {
""")
for i in 0..<NumProperties {
print("""
// public lazy var i\(i): Int { get set }
@_hasStorage @_hasInitialValue final var __lazy_storage__i\(i): Int? { get set }
""")
}
print("""
}
// LazyProperties.init()
sil @$s4lazy14LazyPropertiesCACycfc : $@convention(method) (@owned LazyProperties) -> @owned LazyProperties {
bb0(%0 : $LazyProperties):
%enum = enum $Optional<Int>, #Optional.none!enumelt
""")
for i in 0..<NumProperties {
let adr = (i*4) + 2
let access = adr + 1
print("""
%\(adr) = ref_element_addr %0 : $LazyProperties, #LazyProperties.__lazy_storage__i\(i)
%\(access) = begin_access [modify] [dynamic] %\(adr) : $*Optional<Int>
store %enum to %\(access) : $*Optional<Int>
end_access %\(access) : $*Optional<Int>
""")
}
print("""
return %0 : $LazyProperties
} // end sil function '$s4lazy14LazyPropertiesCACycfc'
""")
We want to eventually remove address phi arguments from SIL. This will enable
all sorts of nice IRGen optimizations and in general make life better. We are
not there yet, but given that is the direction we are going in, I don't think
there is much use in having to implement this sort of checking for SIL phi
arguments.
rdar://50676315
Instructions that start a scope should have a (discoverable) method
that retrieves the end of scope. This is a basic structural property
of the instruction.
I removed the makeEndBorrowRange helper because it adds overall
complexity and doesn't provide any value. If some code wants to be
generic over BeginBorrow/LoadBorrow, then that code should have it's
own trivial generic helper:
EndBorrowRange getEndBorrows<T>(T *beginBorrow) {
return beginBorrow->getEndBorrows()
}
This adds support to the load->struct_extract canonicalization for
nontrivial element types which look like:
load [copy]
borrow
struct_extract
...uses...
end_borrow
destroy
Cleaning up in preparation for making changes that improve
compile-time issues in AccessEnforcementOpts.
This is a simple but important enum with a handful of cases. The cases
need to be easily referenced from the header. Don't define them in a
separate .def. Remove the visitor biolerplate because it doesn't serve
any purpose.
This enum is meant to be used with covered switches. The enum cases do
not have their own types, so there's no performance reason to use a
Visitor pattern.
It should not be possible to add a case to this enum without carefully
considering the impact on the encoding of this class and the impact on
each and every one of the uses. We always want covered switches at the
use sites.
This is a major improvement in readability and usability both in the
definition of the class and in the one place where a visitor was used.
My intention is to use this checker to also verify that in_guaranteed arguments
are used in the same manner immutably. Beyond improving that in_guaranteed
parameters are properly used immutably (which is just goodness), by using the
same check it ensures that we can always inline a callee into a caller with an
open_existential_addr without violating any check on the oea instruction.
I am doing this separately from adding more checks/applying it to
in_guaranteed/fixing some exposed bugs for ease of review. This change is
completely mechanical.
rdar://50212579
We do not consider inout_aliasable to be "truly mutating" since today it is just
used as a way to mark a captured argument and not that something truly has
mutating semantics. The reason why this is safe is that the typechecker
guarantees that if our value was immutable, then the use in the closure must be
immutable as well.
In a future SIL, we want to remove Inout_Aliasable in favor of just using
inout/in_guaranteed using the capture info from the type checker.
rdar://50212579
This is a large patch; I couldn't split it up further while still
keeping things working. There are four things being changed at
once here:
- Places that call SILType::isAddressOnly()/isLoadable() now call
the SILFunction overload and not the SILModule one.
- SILFunction's overloads of getTypeLowering() and getLoweredType()
now pass the function's resilience expansion down, instead of
hardcoding ResilienceExpansion::Minimal.
- Various other places with '// FIXME: Expansion' now use a better
resilience expansion.
- A few tests were updated to reflect SILGen's improved code
generation, and some new tests are added to cover more code paths
that previously were uncovered and only manifested themselves as
standard library build failures while I was working on this change.
Currently Sema completes all _ObjectiveCBridgeable conformances it thinks
are needed in SILGen and runtime dynamic casts, but that's about to change.
Make sure SIL passes a LazyResolver down so that if a conformance doesn't
have a type witness for _ObjectiveCType yet, one can be resolved.
Note that in practice, ClangImporter-synthesized conformances use a special
LazyMemberLoader implementation to fill in type witnesses without any
intervention from the type checker. However, that might go away now that
we have a long-lived type checker instance. Furthermore, various tests use
-enable-source-import and start to fail once Sema no longer completes
_ObjectiveCBridgeable conformances.
This allows the conversion of the Windows `BOOL` type to be converted to
`Bool` implicitly. The implicit bridging allows for a more ergonomic
use of the native Windows APIs in Swift.
Due to the ambiguity between the Objective C `BOOL` and the Windows
`BOOL`, we must manually map the `BOOL` type to the appropriate type.
This required lifting the mapping entry for `ObjCBool` from the mapped
types XMACRO definition into the inline definition in the importer.
Take the opportunity to simplify the mapping code.
Adjust the standard library usage of the `BOOL` type which is now
eclipsed by the new `WindowsBool` type, preferring to use `Bool`
whenever possible.
Thanks to Jordan Rose for the suggestion to do this and a couple of
hints along the way.
The initialization of an instance property that has an attached
property delegate involves the initial value written on the property
declaration, the implicit memberwise initializer, and the default
arguments to the implicit memberwise initializer. Implement SILGen
support for each of these cases.
There is a small semantic change to the creation of the implicit
memberwise initializer due to SE-0242 (default arguments for the
memberwise initializer). Specifically, the memberwise initializer will
use the original property type for the parameter to memberwise
initializer when either of the following is true:
- The corresponding property has an initial value specified with the
`=` syntax, e.g., `@Lazy var i = 17`, or
- The corresponding property has no initial value, but the property
delegate type has an `init(initialValue:)`.
The specific case that changed is when a property has an initial value
specified as a direct initialization of the delegate *and* the
property delegate type has an `init(initialValue:)`, e.g.,
```swift
struct X {
@Lazy(closure: { ... })
var i: Int
}
```
Previously, this would have synthesized an initializer:
```swift
init(i: Int = ???) { ... }
```
However, there is no way for the initialization specified within the
declaration of i to be expressed via the default argument. Now, it
synthesizes an initializer:
```swift
init(i: Lazy<Int> = Lazy(closure: { ... }))
```
The initializer associated with a lazy property should not be executed
directly, because it is subsumed by code synthesized into the
getter. Generalize the terminology here so we can re-use this path for
property delegate initialization.
This is to support dynamic function replacement of functions with opaque
result type.
This approach requires that all state is thrown away (that could contain the
old returned type for an opaque type) between replacements.
rdar://48887938
unknown symbolic values by renaming some diagnostics and
creating new unknown reasons for each type of failure that
can happen during constant evaluation.
Tear out the hacks to pre-substitute opaque types before they enter the SIL type system.
Implement UnderlyingToOpaqueExpr as bitcasting the result of the underlying expression from the
underlying type to the opaque type.