Rather than doing a standard swift runtime cast to an existential, explicitly check for the conforming instruction classes, which is much faster.
The new `isFullApplySite` and `isReturnInstruction` casting utilities are used in the (very few) time critical places in the optimizer.
After toolchain builders are upgraded to a compiler version which includes the fix for this problem (https://github.com/swiftlang/swift/pull/88270), we don't need this workaround anymore and the regular `as`/`is` casts can be used again.
Now the runtime casts doesn't show up prominently in compile-time profiling data anymore - even with a host compiler which doesn't implement fast type checks, yet.
rdar://173916206
Passing a C++ object to the TSanInOutAccess builtin resulted in an extra
temporary copy. This copy was not optimized out because the semantics of this
builtin was not understood by the optimizer. Teaching the utils that this
intrinsic does not actually modify the object, does not escape it,
and does not read it lets the optimizer eliminate this copy.
Strictly speaking, the test code that uses interop is not safe/correct,
this is why it had a lifetime issue.
rdar://173921363
We cannot use spare bits or other overlapping storage layout tricks with fundamentally
address-only enums, and we can take advantage of this to do borrowing switches or other
in-place projections without copying the value. However, for resilient enums, the
implementation may use spare bit packing, but the type must be handled address-only
outside of its defining module, and we didn't have a way to express that with
borrowing switch. Optimization passes have also been running into problems with the
complexity that we were using `unchecked_take_enum_data_addr` sometimes as a pure
operation. This patch splits the instruction into three:
- `unchecked_inplace_enum_data_addr` represents a nondestructive in-place enum
projection. It is only allowed for enums whose projection operation is
nondestructive.
- `unchecked_take_enum_data_addr` represents a destructive enum projection,
invalidating the enum and leaving the payload to be further consumed.
This matches the current instruction's semantics.
- `unchecked_borrow_enum_data_addr` represents a borrowing enum projection.
The instruction takes a second operand for "scratch" space, which the
enum representation may be copied into in order to avoid invalidating the
enum value, so the result is dependent on the lifetime of both the
original enum and the scratch buffer. This allows for borrowing switches
over resilient enums.
`unchecked_borrow_enum_data_addr` is implemented by taking advantage of the
"address-only enums can't do spare bit optimization" property at runtime.
We inspect the operand type's bitwise-borrowability from its metadata. If
the type is bitwise-borrowable, then we are allowed to bitwise-copy the
enum to the scratch space and apply the projection to the scratch space,
preserving the original value. If the type is not bitwise-borrowable, then
we cannot use spare bit optimization in its layout, so we apply the
projection in-place.
Fixes rdar://174952822.
The original implementation of `mayAccessPointer` did look through `address_to_pointer` - `pointer_to_address` pairs and therefore not detect such pointers.
Fixes a miscompile
rdar://174268466
Make sure an `end_borrow` is emitted on the `dereference_borrow` so that
SILGenCleanup recognizes this pattern and lowers it to a `return_borrow`
as it does for returned `load_borrow`s. Add support to the Swift implementation
of `BorrowingInstruction` for `DereferenceBorrow`.
This allows to move many SIL APIs and utilities, which require a context, to the SIL module.
The SIL-part of SwiftPassInvocation is extracted into a base class SILContext which now lives in SIL.
Also: simplify the begin/end-pass functions of the SwiftPassInvocation.
This is a workaround for a bug in the move-only checker: rdar://151841926.
The move-only checker sometimes inserts destroy_addr within read-only static access scopes.
Therefore don't consider static access scopes as immutable scopes.
This prevents simplification and SILCombine passes to remove (alive) `mark_dependence_addr`.
The instruction is conceptually equivalent to
```
%v = load %addr
%d = mark_dependence %v on %base
store %d to %addr
```
Therefore the address operand has to be defined as writing to the address.
The lifetime of yielded values always end at the end_apply.
This is required because a yielded address is non-aliasing inside the begin/end_apply scope, but might be aliasing after the end_apply.
For example, if the callee yields an `ref_element_addr` (which is encapsulated in a begin/end_access).
Therefore, even if the callee does not write anything, the effects must be "read" and "write".
Fixes a SIL verifier error
rdar://147601749
If the base value of a mark_dependence is an address, that memory location must be initialized at the mark_dependence.
This requires that the mark_dependence is considered to read from the base address.
Global let-variables are immutable, except in functions which initialize them.
This brings back handling of global let-variables in alias analysis, which was removed in the previous commit.
Although a let-field can never be mutated, a release or consume of the class must be considered as writing to such a field.
This change removes the special handling of let-fields in two places, where they don't belong.
Class fields are handled by ImmutableScope anyway.
Handling of global let-variable is temporarily removed by this commit.
Fixes a miscompile.
rdar://142996449
This encourages AccessPathWalker clients to handle enclosing mark_deps. In
some cases, it is necessary. The accessBaseWithScopes API now provides both
nested begin_access and mark_dependence.
Checking if an access base is derived from a begin-borrow was too optimistic.
We have to bail for instructions which are not handled by the walker utilities.
Fixes a verifier crash.
rdar://139788357
Add `Value.constantAccessPath`. It is like `accessPath`, but ensures that the projectionPath only contains "constant" elements.
This means: if the access contains an `index_addr` projection with a non-constant index, the `projectionPath` does _not_ contain the `index_addr`.
Instead, the `base` is an `AccessBase.index` which refers to the `index_addr`.
The main changes are:
*) Rewrite everything in swift. So far, parts of memory-behavior analysis were already implemented in swift. Now everything is done in swift and lives in `AliasAnalysis.swift`. This is a big code simplification.
*) Support many more instructions in the memory-behavior analysis - especially OSSA instructions, like `begin_borrow`, `end_borrow`, `store_borrow`, `load_borrow`. The computation of end_borrow effects is now much more precise. Also, partial_apply is now handled more precisely.
*) Simplify and reduce type-based alias analysis (TBAA). The complexity of the old TBAA comes from old days where the language and SIL didn't have strict aliasing and exclusivity rules (e.g. for inout arguments). Now TBAA is only needed for code using unsafe pointers. The new TBAA handles this - and not more. Note that TBAA for classes is already done in `AccessBase.isDistinct`.
*) Handle aliasing in `begin_access [modify]` scopes. We already supported truly immutable scopes like `begin_access [read]` or `ref_element_addr [immutable]`. For `begin_access [modify]` we know that there are no other reads or writes to the access-address within the scope.
*) Don't cache memory-behavior results. It turned out that the hit-miss rate was pretty bad (~ 1:7). The overhead of the cache lookup took as long as recomputing the memory behavior.
Mostly restore the behavior of getMemoryEffectsFn on builtins other than
`once` and `onceWithContext` to before
8a8a895239 but keeping the improvement to
return `Instruction.memoryEffects` in the face of escaping.
An instruction is a deinit barrier whenever one of three component
predicates is true for it. In the case of applies, it is true whenever
one of those three predicates is true for any of the instructions in any
of its callees; that fact is cached in the side-effect analysis of every
function.
If side-effect analysis or callee analysis is unavailable, in order to
define each of those three component predicates on a
`FullApplySite`/`EndApplyInst`/`AbortApplyInst`, it would be necessary
to define them to conservatively return true: it isn't known whether any
of the instructions in any of the callees were deinit barriers.
Refactored the two versions of the deinit barrier predicate (namely
`Instruction.isDeinitBarrier(_:) and
`swift::mayBeDeinitBarrierNotConsideringSideEffects`) to handle
`FullApplySite`/`EndApplyInst`/`AbortApplyInst`s specially first (to
look up the callees' side-effect and to conservatively bail,
respectively). Asserted that the three component predicates are not
called with `FullApplySite`/`EndApplyInst`/`AbortApplyInst`s. Callers
should instead use the `isDeinitBarrier` APIs.
An alternative would be to conservatively return true from the three
components. That seems more likely to result in direct calls to these
member predicates, however, and at the moment at least there is no
reason for such calls to exist. If some other caller besides the
deinit-barrier predicates needs to call this function, side-effect
analysis should be updated to cache these three properties separately at
that point.
Layers:
- FunctionConvention: AST FunctionType: results, parameters
- ArgumentConventions: SIL function arguments
- ApplyOperandConventions: applied operands
The meaning of an integer index is determined by the collection
type. All the mapping between the various indices (results,
parameters, SIL argument, applied arguments) is restricted to the
collection type that owns that mapping. Remove the concept of a
"caller argument index".
Introduce two modes of bridging:
* inline mode: this is basically how it worked so far. Using full C++ interop which allows bridging functions to be inlined.
* pure mode: bridging functions are not inlined but compiled in a cpp file. This allows to reduce the C++ interop requirements to a minimum. No std/llvm/swift headers are imported.
This change requires a major refactoring of bridging sources. The implementation of bridging functions go to two separate files: SILBridgingImpl.h and OptimizerBridgingImpl.h.
Depending on the mode, those files are either included in the corresponding header files (inline mode), or included in the c++ file (pure mode).
The mode can be selected with the BRIDGING_MODE cmake variable. By default it is set to the inline mode (= existing behavior). The pure mode is only selected in certain configurations to work around C++ interop issues:
* In debug builds, to workaround a problem with LLDB's `po` command (rdar://115770255).
* On windows to workaround a build problem.
We already use a complexity limit for other functions in AliasAnalysis.
This is a workaround for quadratic complexity in ARCSequenceOpts.
Fixes a compile time problem
rdar://114352817
* Check if the address in question is even visible from outside the function
* Return the memory effects of the called function
Also, add a new API `Instruction.memoryEffects`, which is internally used by `mayReadFromMemory` et al.