Skip to content

[semantic-arc] Change StorageGuaranteesLoadVisitor::visitClassAccess to find borrow scope introducers using utilities from OwnershipUtils.h. #30069

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

gottesmm
Copy link
Contributor

I added a new API into OwnershipUtils called
getSingleBorrowIntroducingValue. This API returns a single
BorrowScopeIntroducingValue for a passed in guaranteed value. If we can not find
such a BorrowScopeIntroducingValue or we find multiple such, we return None.

Using that, I refactored StorageGuaranteesLoadVisitor::visitClassAccess(...) to
use this new API which should make it significantly more robust since the
routine uses the definitions of "guaranteed forwarding" that the ownership
verifier uses when it verifies meaning that we can rely on the routine to be
exhaustive and correct. This means that we now promote load [copy] ->
load_borrow even if our borrow scope introducer feeds through a switch_enum or
checked_cast_br result (the main reason I looked into this change).

To create getSingleBorrowIntroducingValue, I refactored
getUnderlyingBorrowIntroucingValues to use a generator to find all of its
underlying values. Then in getSingleBorrowIntroducingValue, I just made it so
that we call the generator 1-2 times (as appropriate) to implement this query.

@gottesmm gottesmm requested a review from atrick February 26, 2020 03:59
@gottesmm
Copy link
Contributor Author

gottesmm commented Feb 26, 2020

@atrick the biggest change in the patterns we support is not we can promote load [copy] -> load_borrow if our guaranteed base is switched or casted upon via switch_enums, checked_cast_br.

I.e. we can transform this:

switch_enum %0, bb1, bb2

bb1(%1 : @guaranteed $Klass):
   %2 = ref_element_addr %1 : $Klass, #Klass.aLetField
   %3 = load [copy] %2
   apply %f(%3)
   destroy_value %3
   ...

Into this:

switch_enum %0, bb1, bb2

bb1(%1 : @guaranteed $Klass):
   %2 = ref_element_addr %1 : $Klass, #Klass.aLetField
   %3 = load_borrow %2
   apply %f(%3)
   end_borrow %3
   ...

@gottesmm
Copy link
Contributor Author

@swift-ci test

@gottesmm
Copy link
Contributor Author

@swift-ci test source compatibility

@gottesmm
Copy link
Contributor Author

@swift-ci benchmark

Copy link
Contributor

@atrick atrick left a comment

Choose a reason for hiding this comment

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

The changes to semantic ARC look correct to me. The new utility just doesn't make sense... because it's reason for existing is based entirely on some nonsense in the original implementation...


BorrowScopeIntroducingValueGenerator::Result
BorrowScopeIntroducingValueGenerator::next() {
while (!worklist.empty()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't like the idea of a worklist here because it contradicts the basic design of a single borrow introducer for each scope.

Copy link
Contributor Author

@gottesmm gottesmm Feb 26, 2020

Choose a reason for hiding this comment

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

What if I have a struct literal that has multiple guaranteed parameters lets say from different sil function arguments.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In such a case, I think we would need to return both borrow introducers.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, if we allow a borrowed tuple or struct to be composed from borrowed elements, then this utility makes sense. The only thing I don't like is the general code that places all operands on the worklist. I feel like forwarding instructions (casts) should have an API that returns the single forwarded operand. And tuple/struct should be handled explicitly as the only thing that can "merge" borrow scopes.

There should be no reason to allow cond_br to handle borrowed values. No passes require critical edges in the mandatory pipeline. If it's done temporarily until we get around to enforcing that, then it should be a FIXME.

// instruction
if (isGuaranteedForwardingValue(v)) {
if (auto *i = v->getDefiningInstruction()) {
llvm::transform(i->getAllOperands(), std::back_inserter(worklist),
Copy link
Contributor

Choose a reason for hiding this comment

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

Any forwarding instruction should identify the single operand that it forwards. We shouldn't just arbitrarily dump all the operands on a worklist.

Copy link
Contributor

Choose a reason for hiding this comment

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

It is nonsense to forward multiple guaranteed values into a single scope.

auto *arg = cast<SILPhiArgument>(v);
auto *termInst = arg->getSingleTerminator();
assert(termInst && termInst->isTransformationTerminator());
llvm::transform(termInst->getAllOperands(), std::back_inserter(worklist),
Copy link
Contributor

Choose a reason for hiding this comment

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

Some here, a forwarding terminator should identify the single operand that it forwards

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am fine with doing that.

namespace {

/// A generator of borrow scope introducing values.
struct BorrowScopeIntroducingValueGenerator {
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we want this abstraction at all.... see comments below.

@swift-ci
Copy link
Contributor

Performance: -O

Code size: -O

Performance: -Osize

Regression OLD NEW DELTA RATIO
DataAppendDataSmallToSmall 3880 4340 +11.9% 0.89x (?)
 
Improvement OLD NEW DELTA RATIO
DictionaryBridgeToObjC_Access 952 837 -12.1% 1.14x (?)

Code size: -Osize

Performance: -Onone

Regression OLD NEW DELTA RATIO
DataAppendDataSmallToSmall 3940 4420 +12.2% 0.89x (?)

Code size: -swiftlibs

How to read the data The tables contain differences in performance which are larger than 8% and differences in code size which are larger than 1%.

If you see any unexpected regressions, you should consider fixing the
regressions before you merge the PR.

Noise: Sometimes the performance results (not code size!) contain false
alarms. Unexpected regressions which are marked with '(?)' are probably noise.
If you see regressions which you cannot explain you can try to run the
benchmarks again. If regressions still show up, please consult with the
performance team (@eeckstein).

Hardware Overview
  Model Name: Mac Pro
  Model Identifier: MacPro6,1
  Processor Name: 12-Core Intel Xeon E5
  Processor Speed: 2.7 GHz
  Number of Processors: 1
  Total Number of Cores: 12
  L2 Cache (per Core): 256 KB
  L3 Cache: 30 MB
  Memory: 64 GB

@gottesmm
Copy link
Contributor Author

I am going to swing back around and clean this up a little bit/add some more comments/etc before this lands.

…to find borrow scope introducers using utilities from OwnershipUtils.h.

I added a new API into OwnershipUtils called
getSingleBorrowIntroducingValue. This API returns a single
BorrowScopeIntroducingValue for a passed in guaranteed value. If we can not find
such a BorrowScopeIntroducingValue or we find multiple such, we return None.

Using that, I refactored StorageGuaranteesLoadVisitor::visitClassAccess(...) to
use this new API which should make it significantly more robust since the
routine uses the definitions of "guaranteed forwarding" that the ownership
verifier uses when it verifies meaning that we can rely on the routine to be
exhaustive and correct. This means that we now promote load [copy] ->
load_borrow even if our borrow scope introducer feeds through a switch_enum or
checked_cast_br result (the main reason I looked into this change).

To create getSingleBorrowIntroducingValue, I refactored
getUnderlyingBorrowIntroucingValues to use a generator to find all of its
underlying values. Then in getSingleBorrowIntroducingValue, I just made it so
that we call the generator 1-2 times (as appropriate) to implement this query.
@gottesmm gottesmm force-pushed the pr-cdc5d32f1a5317b8716c016a0e72f9ee40eae4dd branch from 46efc9a to 962b4ed Compare March 2, 2020 08:56
@gottesmm
Copy link
Contributor Author

gottesmm commented Mar 2, 2020

@swift-ci smoke test

@gottesmm
Copy link
Contributor Author

gottesmm commented Mar 2, 2020

@swift-ci benchmark

1 similar comment
@gottesmm
Copy link
Contributor Author

gottesmm commented Mar 2, 2020

@swift-ci benchmark

@swift-ci
Copy link
Contributor

swift-ci commented Mar 2, 2020

Performance: -O

Regression OLD NEW DELTA RATIO
MapReduce 207 224 +8.2% 0.92x (?)
 
Improvement OLD NEW DELTA RATIO
PrefixArray 14 13 -7.1% 1.08x (?)

Code size: -O

Performance: -Osize

Regression OLD NEW DELTA RATIO
DropFirstCountableRange 16 18 +12.5% 0.89x (?)

Code size: -Osize

Performance: -Onone

Improvement OLD NEW DELTA RATIO
DataAppendDataSmallToSmall 3180 2960 -6.9% 1.07x (?)

Code size: -swiftlibs

How to read the data The tables contain differences in performance which are larger than 8% and differences in code size which are larger than 1%.

If you see any unexpected regressions, you should consider fixing the
regressions before you merge the PR.

Noise: Sometimes the performance results (not code size!) contain false
alarms. Unexpected regressions which are marked with '(?)' are probably noise.
If you see regressions which you cannot explain you can try to run the
benchmarks again. If regressions still show up, please consult with the
performance team (@eeckstein).

Hardware Overview
  Model Name: Mac mini
  Model Identifier: Macmini8,1
  Processor Name: Intel Core i7
  Processor Speed: 3.2 GHz
  Number of Processors: 1
  Total Number of Cores: 6
  L2 Cache (per Core): 256 KB
  L3 Cache: 12 MB
  Memory: 64 GB

@gottesmm gottesmm merged commit ae2bef9 into swiftlang:master Mar 3, 2020
@gottesmm gottesmm deleted the pr-cdc5d32f1a5317b8716c016a0e72f9ee40eae4dd branch March 3, 2020 06:58
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.

3 participants