Enum, struct and tuple instructions should not have a forwarding
ownership within a debug reconstruction block.
Enum instructions (via salvageUnaryInst) and struct salvaging now reset
the forwarding ownership to None. Tuple instructions don't force the
forwarding ownership to owned for move-only types, so no fix was needed
for those, but tests have been added for all cases.
- Do not set the scope, which should already be correct.
- Assert that the `debug_value` instruction has no attached reconstruction block, to avoid silently dropping it.
- Note that this will eventually have to transition from using tuple fragments to reconstruction blocks.
When a debug reconstruction block exists, adding a fragment is invalid.
Instead, salvage the struct instruction into the existing block. As
these blocks don't support multiple inputs, only one element is
salvaged, but this works well for single-element structs.
Additionally, to avoid crashes, a special case is added for empty
structs, which don't require any input.
Assisted-by: Claude
When a store was being salvaged, the reconstruction block was dropped,
which could cause invalid debug values to be created. Reconstruction
blocks are now cloned with the debug values, and the uses of the address
removed by stripDeref.
Assisted-by: Claude
The salvage for float literals works the same way as the one for integer
literals: clone the instruction into debug reconstruction block.
Assisted-by: Claude (for tests)
Replace the use of createDebugValueAddr in salvageLoadDebugInfo, that
was used to rewrite debug value instructions with a prepended deref, to
use the prependDeref function to prepend it in place, and move it to the
correct place.
This function is used by passes and salvages to facilitate adding
instructions to reconstruction blocks, even if no block exists on the
debug value yet.
Instead of using an op_constu/consts in the DIExpr, the integer literal
salvage now uses a debug basic block using a typed integer_literal.
This also has the effect of supporting integer literals bigger than
64 bits (such as an Int128 being salvaged) correctly.
Assisted-by: Claude
The salvage for this instruction was wrong as it didn't multiply the
index by the element stride.
As this salvage is rare, only was correct for byte sized elements, and
will be rewritten in the future, remove it rather than fix it.
The VarInfo for an alloc_stack will always have an op_deref, so that they can
get copied along when the VarInfo is moved to a debug_value. This op_deref is
not printed by the SILPrinter.
This commit also updates uses of AllocStackInst's getVarInfo to strip this
op_deref at places where it is not needed, and uses of createDebugValueAddr
where the extra op_deref is no longer needed.
The only change at the IR level is for undef values that now have a DW_OP_deref
for move-only values.
This fixes most of the inconsitencies with op_deref at the SIL level. All
debug_values with addresses should always have an op_deref.
Assisted-by: Claude Opus 4.6 (Updated tests)
usePoints is an artifact of the time when we didn't have guaranteed forwarding phis.
It is no longer used and triggered an unnecessary assert for borrow accessors.
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.
There is some logic to figure out if a type is unreferencable but that
did not handle tuple fields. C arrays are imported as Swift tuples, so
in case a C array had unreferenceable element type we can end up taking
the wrong path in the optimizer and trigger a crash.
rdar://175037688
A dead forwarding operation (e.g. `unchecked_enum_data`,
`unchecked_value_cast`, `destructure_struct`) with an owned move-only operand
must not be deleted because it ends the lifetime of its operand.
Previously, only `destructure_struct` was guarded against deletion. This
change uses `ForwardingOperation` and `getSingleForwardingOperand()` to
cover all single-operand forwarding instructions uniformly.
Resolves rdar://175150849
Mainly:
* look through ownership instructions in more places
* support `load_borrow` and `store_borrow` where `load` and `store`s are expected
* support `destructure_struct` where `struct_extract` is expected
* enable inout-keypath optimization for OSSA
* OSSA support in StringOptimization
Inserts an unreachable after an unconditional fail:
```
%0 = integer_literal 1
cond_fail %0, "message"
// following instructions
```
->
```
%0 = integer_literal 1
cond_fail %0, "message"
unreachable
deadblock:
// following instructions
```
Remove the old SILCombine implementation because it's not working well with OSSA lifetime completion.
This also required to move the `shouldRemoveCondFail` utility function from SILCombine to InstOptUtils.
We may see undef closure captures in ClosureLifetimeFixup since
it is a mandatory pass that runs on invalid code as well.
This could sometimes hang the compiler while running `insertDeallocOfCapturedArguments`
in release builds.
This change adds a bailout when we see an undef closure capture to avoid running into this issue.
This peephole optimization didn't consider that an alloc_stack of an enum can be overridden by another value.
The fix is to remove this peephole optimization at all because it is already covered by `optimizeEnum` in alloc_stack simplification.
Fixes a miscompile
https://github.com/swiftlang/swift/issues/85687
rdar://165374568
A dead `destructure_struct` with an owned argument can appear for a non-copyable or non-escapable struct which has only trivial elements.
The instruction is not trivially dead because it ends the lifetime of its operand.
Fixes an ownership verification error.
executing unknown code
This means we have to claw back some performance by recognizing harmless
releases.
Such as releases on types we known don't call a deinit with unknown
side-effects.
rdar://143497196
rdar://143141695
Make sure that an enum is only initialized once before it is taken. This implies that the initialization must dominate the take.
Fixes a verifier crash: rdar://139381701