Skip to content

[sil] Ban casting AnyObject with a guaranteed ownership forwarding checked_cast_br and fix up semantic-arc-opts given that. #40287

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

Merged

Conversation

gottesmm
Copy link
Contributor

The reason why I am doing this is that currently checked_cast_br of an AnyObject
may return a different value due to boxing. As an example, this can occur when
performing a checked_cast_br of a __SwiftValue(AnyHashable(Klass())).

To model this in SIL, I added to OwnershipForwardingMixin a bit of information
that states if the instruction directly forwards ownership with a default value
of true. In checked_cast_br's constructor though I specialize this behavior if
the source type is AnyObject and thus mark the checked_cast_br as not directly
forwarding its operand. If an OwnershipForwardingMixin directly forwards
ownership, one can assume that if it forwards ownership, it will always do so in
a way that ensures that each forwarded value is rc-identical to whatever result
it algebraically forwards ownership to. So in the context of checked_cast_br, it
means that the source value is rc-identical to the argument of the success and
default blocks.

I added a verifier check to CheckedCastBr that makes sure that it can never
forward guaranteed ownership and have a source type of AnyObject.

In SemanticARCOpts, I modified the code that builds extended live ranges of
owned values (*) to check if a OwnershipForwardingMixin is directly
forwarding. If it is not directly forwarding, then we treat the use just as an
unknown consuming use. This will then prevent us from converting such an owned
value to guaranteed appropriately in such a case.

(*) For those unaware, SemanticARCOpts contains a model of an owned value and
all forwarding uses of it called an OwnershipLiveRange.

rdar://85710101

Replace this paragraph with a description of your changes and rationale. Provide links to external references/discussions if appropriate.

Resolves SR-NNNN.

@gottesmm gottesmm requested review from atrick and tbkka November 23, 2021 20:14
@gottesmm
Copy link
Contributor Author

@swift-ci smoke test

@gottesmm
Copy link
Contributor Author

Turns out libswift hit the verification. I put a fix in SILGenBuilder to ensure that if we create a checked_cast_br, we ensurePlusOne if the source type is AnyObject.

@gottesmm gottesmm force-pushed the pr-8bb19fcfe173f5e7b33ddd95dcbc5566be2262b8 branch from 4c9ab1c to 9862ae6 Compare November 25, 2021 07:00
@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

@swift-ci
Copy link
Contributor

Performance (x86_64): -O

Regression OLD NEW DELTA RATIO
DataAppendDataSmallToSmall 3700 4140 +11.9% 0.89x (?)
 
Improvement OLD NEW DELTA RATIO
NSStringConversion.MutableCopy.Rebridge.UTF8 862 750 -13.0% 1.15x (?)
String.data.Medium 108 100 -7.4% 1.08x (?)
EqualSubstringSubstring 42 39 -7.1% 1.08x (?)
EqualStringSubstring 42 39 -7.1% 1.08x (?)
EqualSubstringString 42 39 -7.1% 1.08x (?)
LessSubstringSubstring 43 40 -7.0% 1.07x (?)
EqualSubstringSubstringGenericEquatable 43 40 -7.0% 1.07x (?)
LessSubstringSubstringGenericComparable 43 40 -7.0% 1.07x (?)

Code size: -O

Improvement OLD NEW DELTA RATIO
AngryPhonebook.o 8983 8843 -1.6% 1.02x

Performance (x86_64): -Osize

Improvement OLD NEW DELTA RATIO
DictionaryBridgeToObjC_Access 991 882 -11.0% 1.12x (?)
LessSubstringSubstring 44 40 -9.1% 1.10x (?)
ObjectiveCBridgeStringIsEqualAllSwift 105 97 -7.6% 1.08x (?)
EqualSubstringSubstring 43 40 -7.0% 1.07x (?)
EqualStringSubstring 43 40 -7.0% 1.07x (?)
EqualSubstringSubstringGenericEquatable 43 40 -7.0% 1.07x (?)
EqualSubstringString 43 40 -7.0% 1.07x (?)
LessSubstringSubstringGenericComparable 43 40 -7.0% 1.07x (?)
StringBuilderWithLongSubstring 1580 1470 -7.0% 1.07x (?)
FlattenListFlatMap 6926 6457 -6.8% 1.07x (?)

Code size: -Osize

Performance (x86_64): -Onone

Regression OLD NEW DELTA RATIO
FloatingPointPrinting_Float_interpolated 65000 70800 +8.9% 0.92x (?)
 
Improvement OLD NEW DELTA RATIO
StringBuilderWithLongSubstring 4470 3740 -16.3% 1.20x (?)
DataToStringSmall 5050 4350 -13.9% 1.16x (?)
String.data.Medium 185 170 -8.1% 1.09x (?)

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

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.

Thanks. Look great except...

swift::findOwnershipReferenceAggregate needs to be fixed to check for direct forwarding. Maybe there are other places?

The canOpcodeForwardGuaranteedValues APIs are the problem. They're completely misleading. Can they be fixed to be canDirectlyForwardValue and canDirectlyForwardOperand? That would be much better than adding a check to OwnershipLiveRange

I probably would have opted for custom logic in isDirectlyForwarding that checks for checked_cast_br, then calls CheckedCastBranch::isDirectlyForwarding, but the flag is cleaner so that's up to your judgement.

…ecked_cast_br and fix up semantic-arc-opts given that.

The reason why I am doing this is that currently checked_cast_br of an AnyObject
may return a different value due to boxing. As an example, this can occur when
performing a checked_cast_br of a __SwiftValue(AnyHashable(Klass())).

To model this in SIL, I added to OwnershipForwardingMixin a bit of information
that states if the instruction directly forwards ownership with a default value
of true. In checked_cast_br's constructor though I specialize this behavior if
the source type is AnyObject and thus mark the checked_cast_br as not directly
forwarding its operand. If an OwnershipForwardingMixin directly forwards
ownership, one can assume that if it forwards ownership, it will always do so in
a way that ensures that each forwarded value is rc-identical to whatever result
it algebraically forwards ownership to. So in the context of checked_cast_br, it
means that the source value is rc-identical to the argument of the success and
default blocks.

I added a verifier check to CheckedCastBr that makes sure that it can never
forward guaranteed ownership and have a source type of AnyObject.

In SemanticARCOpts, I modified the code that builds extended live ranges of
owned values (*) to check if a OwnershipForwardingMixin is directly
forwarding. If it is not directly forwarding, then we treat the use just as an
unknown consuming use. This will then prevent us from converting such an owned
value to guaranteed appropriately in such a case.

I also in SILGen needed to change checked_cast_br emission in SILGenBuilder to
perform an ensurePlusOne on the input operand (converting it to +1 with an
appropriate cleanup) if the source type is an AnyObject. I found this via the
verifier check catching this behavior from SILGen when compiling libswift. This
just shows how important IR verification of invariants can be.

(*) For those unaware, SemanticARCOpts contains a model of an owned value and
all forwarding uses of it called an OwnershipLiveRange.

rdar://85710101
@gottesmm gottesmm force-pushed the pr-8bb19fcfe173f5e7b33ddd95dcbc5566be2262b8 branch from 9862ae6 to 8e5fb21 Compare November 30, 2021 00:08
@gottesmm
Copy link
Contributor Author

@swift-ci smoke test

@gottesmm
Copy link
Contributor Author

@swift-ci test

@tbkka
Copy link
Contributor

tbkka commented Nov 30, 2021

I believe the concept here is correct: The optimization that assumes a successful cast of a reference type returns the same pointer does need to be avoided if the source is AnyObject, since AnyObject can hold references to various box types that can be unboxed to get at a contained class object reference.

Copy link
Contributor

@tbkka tbkka left a comment

Choose a reason for hiding this comment

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

I believe the concept is sound; I'll let @atrick weigh in on the implementation.

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 8e5fb21

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 8e5fb21

@gottesmm
Copy link
Contributor Author

I asked Mishal to kill the linux/macOS job since I am already running the smoke test.

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.

Looks great!

if (ti->isDirectlyForwarding()) {
root = term->getOperand(0);
continue;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Not critical in this PR, but it seems that 6 lines above should now have an assert:

  assert(cast<OwnershipForwardingTermInst>(root)->isDirectlyForwarding());
   root = root->getDefiningInstruction()->getOperand(0);

@gottesmm gottesmm merged commit 1ea21db into swiftlang:main Nov 30, 2021
@gottesmm gottesmm deleted the pr-8bb19fcfe173f5e7b33ddd95dcbc5566be2262b8 branch November 30, 2021 05:26
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.

4 participants