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".
Even if the destroyed value doesn't have a deinit.
This fixes a false alarm when a non-copyable value ends its lifetime in a function with performance annotations.
rdar://117002721
Make it clear that drop_deinit cannot be used to prevent a deinit called from a destroy_addr.
This is more a refactoring and clarification than a bug fix, because a destroy_addr cannot have a drop_deinit as operand, anyway.
In regular swift this is a nice optimization. In embedded swift it's a requirement, because the compiler needs to be able to specialize generic deinits of non-copyable types.
The new de-virtualization utilities are called from two places:
* from the new DeinitDevirtualizer pass. It replaces the old MoveOnlyDeinitDevirtualization, which is very basic and does not fulfill the needs for embedded swift.
* from MandatoryPerformanceOptimizations for embedded swift
* add `NominalTypeDecl.isResilient`
* make the return type of `Type.getNominalFields` optional and return nil in case the nominal type is resilient.
This forces users of this API to think about what to do in case the nominal type is resilient.
Make filter APIs for UseList chainable by adding them to Sequence where Element == Operand
For example, it allows to write:
```
let singleUse = value.uses.ignoreDebugUses.ignoreUsers(ofType: EndAccessInst.self).singleUse
```
Also, add `UseList.getSingleUser(notOfType:)`
In the C++ sources it is slightly more convenient to dump to stderr than
to print to stdout, but it is rather more unsightly to print to stderr
from the Swift sources. Switch to stdout. Also allows the dump
functions to be marked debug only.
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.
This could be combined with ValueUseDefWalker if the latter is
refactored to classsify instructions by projections and aggegation
(which always forward) vs. other arbitrary hard-coded instruction
types. It would also need to limit the walk to real operands (which
are always forwarded). Then this walker can call into the default walk
for projections and track the projection path. The current
implementation is however simpler and more efficient.
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.
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
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.
For example:
```
var p = Point(x: 10, y: 20)
let o = UnsafePointer(&p)
```
Also support outlined arrays with pointers to other globals. For example:
```
var g1 = 1
var g2 = 2
func f() -> [UnsafePointer<Int>] {
return [UnsafePointer(&g1), UnsafePointer(&g2)]
}
```
We inline a function (e.g. a struct initializer) into a global init function if the result is part of the initialized global.
Now, also handle functions with indirect return values. Such function can result from not-reabstracted generic specializations.
Handle cases where the result is stored into a temporary alloc_stack or directly stored to (a part) of the global variable.
To make it available in other optimizations as well.
Also, a few problems:
* Use destructre instructions when in OSSA
* Don't split the store if it's nominal type has unreferenceable stoarge
* rename it to `trySplit` because it's not guaranteed to work
Also, add the counterpart for load instructions: `LoadInst.trySplit()`
A begin_apply can yield multiple addresses. We need to store the result of the apply in order to distinguish between two AccessBases with different results from the same begin_apply.
`ownership` is a bad name in `LoadInst`, because it hides `Value.ownership`.
Therefore rename it to `loadOwnership`.
Do the same for ownership in StoreInst to be consistent.