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.
All SILArgument types are "block arguments". There are three kinds:
1. Function arguments
2. Phis
3. Terminator results
In every situation where the source of the block argument matters, we
need to distinguish between these three. Accidentally failing to
handle one of the cases is an perpetual source of compiler
bugs. Attempting to handle both phis and terminator results uniformly
is *always* a bug, especially once OSSA has phi flags. Even when all
cases are handled correctly, the code that deals with data flow across
blocks is incomprehensible without giving each case a type. This
continues to be a massive waste of time literally every time I review
code that involves cross-block control flow.
Unfortunately, we don't have these C++ types yet (nothing big is
blocking that, it just wasn't done). That's manageable because we can
use wrapper types on the Swift side for now. Wrapper types don't
create any more complexity than protocols, but they do sacrifice some
usability in switch cases.
There is no reason for a BlockArgument type. First, a function
argument is a block argument just as much as any other. BlockArgument
provides no useful information beyond Argument. And it is nearly
always a mistake to care about whether a value is a function argument
and not care whether it is a phi or terminator result.
The walker was not treating an EnumInst with zero payload, such as `Optional.none` as a root.
It seems the best way to fix that is to implement the handling of .anyValueFields for enums, as
they're documented in a comment to mean "follow anything", unlike .enumCase which expects
to find a specific case (though perhaps if it matches and there's no payload, it should still be a root?)
This instructions marks the point where all let-fields of a class are initialized.
This is important to ensure the correctness of ``ref_element_addr [immutable]`` for let-fields,
because in the initializer of a class, its let-fields are not immutable, yet.
Codegen is the same, but `begin_dealloc_ref` consumes the operand and produces a new SSA value.
This cleanly splits the liferange to the region before and within the destructor of a class.
I was originally hoping to reuse mark_must_check for multiple types of checkers.
In practice, this is not what happened... so giving it a name specifically to do
with non copyable types makes more sense and makes the code clearer.
Just a pure rename.
The path components may not be related to the current value in case mismatching types are visited, e.g. different concrete types of an existential.
This can lead to mismatching operand numbers for struct and tuple instructions.
rdar://104435056
Replace the `struct EscapeInfo` with a simpler API, just consisting of methods of `ProjectedValue` and `Value`:
* `isEscaping()`
* `isAddressEscaping()`
* `visit()`
* `visitAddress()`
If there are no more than 2 elements in the cache, we can avoid using the `cache` Dictionary, which avoids memory allocations.
Fortunately this is the common case by far (about 97% of all walker invocations).
* "merge" the `Path` and `State` in WalkUtils into a single `WalkingPath`. This makes it simpler for clients to configure a path and additional state variables. EscapeInfo now defines `EscapePath` which includes the projection path and EscapeInfo's specific state variables.
* Make the `WalkerCache` part of the WalkUtils, so that not all clients have to re-implement it.
* Rename `walkDownResults` -> `walkDownAllResults` and `walkUpOperands` -> `walkUpAllOperands` and make these functions client configurable.
Introduces a set of protocols useful to perform def-use and use-def
traversals to find uses and definitions of values.
This logic was originally baked into `EscapeInfo` directly.
Here we extract it into general utilities, namely:
- `ValueDefUseWalker`: visit uses of a value walking down value-value projections/constructions.
- `AddressDefUseWalker`: visit uses of an address walking down addr-addr projections/constructions.
- `ValueUseDefWalker`: visit definitions of a value walking up value-value projections/constructions.
- `AddressUseDefWalker`: visit definitions of an address walking up addr-addr projections/constructions.
These utilities can then be used in other passes or to create
new utilities by composing them. For example to find a definition
passing through both address projections and value extractions,
it's enough to implement a visitor conforming to both
`AddressUseDefWalker` and `ValueUseDefWalker`.