Skip to content

Conversation

@eeckstein
Copy link
Contributor

@eeckstein eeckstein commented Dec 17, 2025

  • handle debug_value in begin_borrow simplification
  • ignore type-dependent operands when converting borrowed -> owned
  • remove borrow scopes which are borrowing an already guaranteed value
  • allow optimizing lexical begin_borrows outside the mandatory pipeline
  • SimplifyLoad/SimplifyLoadBorrow: replace address casts of heap objects
  • SimplifyLoadBorrow: replace a load_borrow of a store_borrow with a begin_borrow
  • convert destructure_tuple/destructure_struct with guaranteed ownership to to tuple_extract/struct_extract

The improvements for begin_borrow simplification allows to remove the now obsolete BorrowScope optimization from SemanticArcOpts.

This PR also fixes a few small bugs which I run into while working on the optimizations:

  • fix operand ownership for taskAddCancellationHandler and taskAddPriorityEscalationHandler builtins
  • LoadableByAddress: handle the unchecked_bitwise_cast instruction
  • don't remove begin_borrow [lexical] of a thin_to_thick_function in the mandatory pipeline
  • OwnershipOptUtils: fix a wrong debug location when creating destroy_value
  • CopyToBorrowOptimization: need to update borrowed-from instructions when changes are made
  • SILCombine: disable unchecked_bitwise_cast -> unchekced_ref_cast conversion in OSSA
  • SILVerifier: print a readable error message when referencing an undefined SIL function
  • SILVerifier: don't crash on verifier errors in the function header

For details see the commit messages

@eeckstein
Copy link
Contributor Author

@swift-ci test

@eeckstein
Copy link
Contributor Author

@swift-ci apple silicon benchmark

@eeckstein eeckstein requested review from aidan-hall, atrick, elsakeirouz and meg-gupta and removed request for jckarter December 17, 2025 18:11
return true
}

private func tryForwardStoreBorrow(_ context: SimplifyContext) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a unit test for store borrow forwarding?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch! I added a test and also a source comment

Copy link
Contributor

@meg-gupta meg-gupta Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. I think this maybe the first transform assuming store_borrow result immutability within its scope. It maybe good to verify this like we verify load_borrow source immutability via verifyNoMutatingUsesInLiverange in the future..

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It maybe good to verify this like we verify load_borrow source immutability via verifyNoMutatingUsesInLiverange in the future..

good idea!

/// ```
/// %1 = load [copy] %0
/// %2 = unchecked_ref_cast %1 : $SomeClass to $OtherClass
/// ```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you comment why this transform is useful?

@eeckstein
Copy link
Contributor Author

@swift-ci test

@eeckstein
Copy link
Contributor Author

@swift-ci test

/// bb0(%0 : @guaranteed $T):
/// // ... uses of %0
/// ```
private func tryReplaceInnerBorrowScope(beginBorrow: BeginBorrowInst, _ context: SimplifyContext) -> Bool {
Copy link
Contributor

@meg-gupta meg-gupta Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need a lexical check here? Can we remove an inner lexical borrow with an outer non lexical borrow?

Copy link
Contributor Author

@eeckstein eeckstein Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lexical check is only done in the mandatory pipeline. So, when running in the optimization pipeline, lexical begin_borrrows will be removed, anyway.

Can we remove an inner lexical borrow with an outer non lexical borrow?

I don't think so (in the mandatory pipeline) because then the lexical flag gets lost and the outer, non-lexical, borrow scope could be removed as well

@meg-gupta
Copy link
Contributor

meg-gupta commented Dec 18, 2025

I have a another comment - #86113 (comment)

Otherwise LGTM

@eeckstein
Copy link
Contributor Author

@swift-ci test linux

@eeckstein
Copy link
Contributor Author

@swift-ci test linux

* rename `lookThroughSingleForwardingUses` -> `lookThroughOwnedConvertibaleForwardingChain`
* fix the comment of `replaceGuaranteed`
When trying to remove a borrow scope by replacing all guaranteed uses with owned uses, don't bail for values which have debug_value uses.
Also make sure that the debug_value instructions are located within the owned value's lifetime.
* remove borrow scopes which are borrowing an already guaranteed value
* allow optimizing lexical `begin_borrows` outside the mandatory pipeline
* fix: don't remove `begin_borrow [lexical]` of a `thin_to_thick_function` in the mandatory pipeline
…alue

When taking the location from the builder's insertion point, we must make sure it's not a return location.
Fixes an assertion failure.
I found this during my work on simplifying borrow scopes
* Replace address casts of heap objects
```
  %1 = unchecked_addr_cast %0 : $*SomeClass to $*OtherClass
  %2 = load [copy] %1
```
with ref-casts of the loaded value
```
  %1 = load [copy] %0
  %2 = unchecked_ref_cast %1 : $SomeClass to $OtherClass
```

* Replace a `load_borrow` of a `store_borrow` with a `begin_borrow`:
```
  %1 = alloc_stack $T
  %2 = store_borrow %0 to %1
  ...
  %3 = load_borrow %2
  // ... uses of %3
  end_borrow %3
```
->
```
  %1 = alloc_stack $T
  %2 = store_borrow %0 to %1
  ...
  %3 = begin_borrow %0
  // ... uses of %3
  end_borrow %3
```
… with guaranteed ownership to to `tuple_extract`/`struct_extract`
By defining its own array structs, which are independent from objc interop.
Also, the requires-line was wrong anyway. The test didn't run on macos either.
…hen changes are made

When ownership is changed from owned to guaranteed, the enclosing values of a guaranteed value can change.
Fixes a SIL verifier error.
…AddPriorityEscalationHandler` builtins

The operand is a closure which escapes to the return value to the builtin.
Therefore the operand ownership must be `BitwiseEscape`.
Otherwise the closure might be destroyed too early.

Fixes a mis-compile in the Concurrency library.
Fixes a compiler crash in LoadableByAddress.
Unfortunately I don't have a test case for this.
…ined SIL function

This error was not printed because the verifier complained before the error handling was done.
The thing with `optional<VerifierErrorEmitterGuard>` didn't work as expected.
This resulted in a null-pointer de-reference when printing a verifier error.
…conversion in OSSA

In ownership converting an unowned bitwise cast to a "real" ownership forwarding instruction can cause various troubles.

Fixes a SIL verifier crash.
@eeckstein
Copy link
Contributor Author

@swift-ci test

@eeckstein
Copy link
Contributor Author

@swift-ci smoke test macos

@eeckstein eeckstein merged commit cd3ebfb into swiftlang:main Dec 20, 2025
4 of 5 checks passed
@eeckstein eeckstein deleted the simplify-load branch December 20, 2025 06:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants