Skip to content

Commit f0a1fb5

Browse files
authored
Merge pull request #29003 from slavapestov/fix-speculative-devirt
Fix speculative devirtualization to correctly use casted value
2 parents b133b7e + 64971e7 commit f0a1fb5

File tree

9 files changed

+100
-81
lines changed

9 files changed

+100
-81
lines changed

include/swift/SIL/SILBuilder.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1783,12 +1783,6 @@ class SILBuilder {
17831783
// Unchecked cast helpers
17841784
//===--------------------------------------------------------------------===//
17851785

1786-
// Create an UncheckedRefCast if the source and dest types are legal,
1787-
// otherwise return null.
1788-
// Unwrap or wrap optional types as needed.
1789-
SingleValueInstruction *tryCreateUncheckedRefCast(SILLocation Loc, SILValue Op,
1790-
SILType ResultTy);
1791-
17921786
// Create the appropriate cast instruction based on result type.
17931787
SingleValueInstruction *createUncheckedBitCast(SILLocation Loc, SILValue Op,
17941788
SILType Ty);

lib/SIL/SILBuilder.cpp

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -110,19 +110,6 @@ ProjectBoxInst *SILBuilder::createProjectBox(SILLocation Loc,
110110
getSILDebugLocation(Loc), boxOperand, index, fieldTy));
111111
}
112112

113-
// If legal, create an unchecked_ref_cast from the given operand and result
114-
// type, otherwise return null.
115-
SingleValueInstruction *
116-
SILBuilder::tryCreateUncheckedRefCast(SILLocation Loc, SILValue Op,
117-
SILType ResultTy) {
118-
if (!SILType::canRefCast(Op->getType(), ResultTy, getModule()))
119-
return nullptr;
120-
121-
return insert(UncheckedRefCastInst::create(getSILDebugLocation(Loc), Op,
122-
ResultTy, getFunction(),
123-
C.OpenedArchetypes));
124-
}
125-
126113
ClassifyBridgeObjectInst *
127114
SILBuilder::createClassifyBridgeObject(SILLocation Loc, SILValue value) {
128115
auto &ctx = getASTContext();
@@ -142,8 +129,8 @@ SILBuilder::createUncheckedBitCast(SILLocation Loc, SILValue Op, SILType Ty) {
142129
return insert(UncheckedTrivialBitCastInst::create(
143130
getSILDebugLocation(Loc), Op, Ty, getFunction(), C.OpenedArchetypes));
144131

145-
if (auto refCast = tryCreateUncheckedRefCast(Loc, Op, Ty))
146-
return refCast;
132+
if (SILType::canRefCast(Op->getType(), Ty, getModule()))
133+
return createUncheckedRefCast(Loc, Op, Ty);
147134

148135
// The destination type is nontrivial, and may be smaller than the source
149136
// type, so RC identity cannot be assumed.

lib/SILGen/SILGenBuilder.cpp

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -804,17 +804,6 @@ ManagedValue SILGenBuilder::createUncheckedAddrCast(SILLocation loc, ManagedValu
804804
return cloner.clone(cast);
805805
}
806806

807-
ManagedValue SILGenBuilder::tryCreateUncheckedRefCast(SILLocation loc,
808-
ManagedValue original,
809-
SILType type) {
810-
CleanupCloner cloner(*this, original);
811-
SILValue result = tryCreateUncheckedRefCast(loc, original.getValue(), type);
812-
if (!result)
813-
return ManagedValue();
814-
original.forward(SGF);
815-
return cloner.clone(result);
816-
}
817-
818807
ManagedValue SILGenBuilder::createUncheckedTrivialBitCast(SILLocation loc,
819808
ManagedValue original,
820809
SILType type) {

lib/SILGen/SILGenBuilder.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -256,11 +256,6 @@ class SILGenBuilder : public SILBuilder {
256256
ManagedValue createUpcast(SILLocation loc, ManagedValue original,
257257
SILType type);
258258

259-
using SILBuilder::tryCreateUncheckedRefCast;
260-
ManagedValue tryCreateUncheckedRefCast(SILLocation loc,
261-
ManagedValue op,
262-
SILType type);
263-
264259
using SILBuilder::createUncheckedTrivialBitCast;
265260
ManagedValue createUncheckedTrivialBitCast(SILLocation loc,
266261
ManagedValue original,

lib/SILGen/SILGenBuiltin.cpp

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -649,13 +649,14 @@ emitBuiltinCastReference(SILGenFunction &SGF,
649649
auto &toTL = SGF.getTypeLowering(toTy);
650650
assert(!fromTL.isTrivial() && !toTL.isTrivial() && "expected ref type");
651651

652+
auto arg = args[0];
653+
652654
// TODO: Fix this API.
653655
if (!fromTL.isAddress() || !toTL.isAddress()) {
654-
if (auto refCast = SGF.B.tryCreateUncheckedRefCast(loc, args[0],
655-
toTL.getLoweredType())) {
656+
if (SILType::canRefCast(arg.getType(), toTL.getLoweredType(), SGF.SGM.M)) {
656657
// Create a reference cast, forwarding the cleanup.
657658
// The cast takes the source reference.
658-
return refCast;
659+
return SGF.B.createUncheckedRefCast(loc, arg, toTL.getLoweredType());
659660
}
660661
}
661662

@@ -670,7 +671,7 @@ emitBuiltinCastReference(SILGenFunction &SGF,
670671
// TODO: For now, we leave invalid casts in address form so that the runtime
671672
// will trap. We could emit a noreturn call here instead which would provide
672673
// more information to the optimizer.
673-
SILValue srcVal = args[0].ensurePlusOne(SGF, loc).forward(SGF);
674+
SILValue srcVal = arg.ensurePlusOne(SGF, loc).forward(SGF);
674675
SILValue fromAddr;
675676
if (!fromTL.isAddress()) {
676677
// Move the loadable value into a "source temp". Since the source and
@@ -745,17 +746,9 @@ static ManagedValue emitBuiltinReinterpretCast(SILGenFunction &SGF,
745746
}
746747
// Create the appropriate bitcast based on the source and dest types.
747748
ManagedValue in = args[0];
748-
SILType resultTy = toTL.getLoweredType();
749-
if (resultTy.isTrivial(SGF.F))
750-
return SGF.B.createUncheckedTrivialBitCast(loc, in, resultTy);
751749

752-
// If we can perform a ref cast, just return.
753-
if (auto refCast = SGF.B.tryCreateUncheckedRefCast(loc, in, resultTy))
754-
return refCast;
755-
756-
// Otherwise leave the original cleanup and retain the cast value.
757-
SILValue out = SGF.B.createUncheckedBitwiseCast(loc, in.getValue(), resultTy);
758-
return SGF.emitManagedRetain(loc, out, toTL);
750+
SILType resultTy = toTL.getLoweredType();
751+
return SGF.B.createUncheckedBitCast(loc, in, resultTy);
759752
}
760753

761754
/// Specialized emitter for Builtin.castToBridgeObject.

lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -286,9 +286,12 @@ SILCombiner::visitUncheckedRefCastAddrInst(UncheckedRefCastAddrInst *URCI) {
286286
Builder.setCurrentDebugScope(URCI->getDebugScope());
287287
LoadInst *load = Builder.createLoad(Loc, URCI->getSrc(),
288288
LoadOwnershipQualifier::Unqualified);
289-
auto *cast = Builder.tryCreateUncheckedRefCast(Loc, load,
290-
DestTy.getObjectType());
291-
assert(cast && "SILBuilder cannot handle reference-castable types");
289+
290+
assert(SILType::canRefCast(load->getType(), DestTy.getObjectType(),
291+
Builder.getModule()) &&
292+
"SILBuilder cannot handle reference-castable types");
293+
auto *cast = Builder.createUncheckedRefCast(Loc, load,
294+
DestTy.getObjectType());
292295
Builder.createStore(Loc, cast, URCI->getDest(),
293296
StoreOwnershipQualifier::Unqualified);
294297

@@ -393,11 +396,12 @@ visitUncheckedBitwiseCastInst(UncheckedBitwiseCastInst *UBCI) {
393396
UBCI->getOperand(),
394397
UBCI->getType());
395398

396-
if (auto refCast = Builder.tryCreateUncheckedRefCast(
397-
UBCI->getLoc(), UBCI->getOperand(), UBCI->getType()))
398-
return refCast;
399+
if (!SILType::canRefCast(UBCI->getOperand()->getType(), UBCI->getType(),
400+
Builder.getModule()))
401+
return nullptr;
399402

400-
return nullptr;
403+
return Builder.createUncheckedRefCast(UBCI->getLoc(), UBCI->getOperand(),
404+
UBCI->getType());
401405
}
402406

403407
SILInstruction *

lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,20 @@ static SILBasicBlock *cloneEdge(TermInst *TI, unsigned SuccIndex) {
8181
}
8282

8383
// A utility function for cloning the apply instruction.
84-
static FullApplySite CloneApply(FullApplySite AI, SILBuilder &Builder) {
84+
static FullApplySite CloneApply(FullApplySite AI, SILValue SelfArg,
85+
SILBuilder &Builder) {
8586
// Clone the Apply.
8687
Builder.setCurrentDebugScope(AI.getDebugScope());
8788
Builder.addOpenedArchetypeOperands(AI.getInstruction());
8889
auto Args = AI.getArguments();
8990
SmallVector<SILValue, 8> Ret(Args.size());
90-
for (unsigned i = 0, e = Args.size(); i != e; ++i)
91-
Ret[i] = Args[i];
91+
for (unsigned i = 0, e = Args.size(); i != e; ++i) {
92+
if (i == e - 1 && SelfArg) {
93+
Ret[i] = SelfArg;
94+
} else {
95+
Ret[i] = Args[i];
96+
}
97+
}
9298

9399
FullApplySite NAI;
94100

@@ -169,8 +175,8 @@ static FullApplySite speculateMonomorphicTarget(FullApplySite AI,
169175
SILValue DownCastedClassInstance = Iden->getArgument(0);
170176

171177
// Copy the two apply instructions into the two blocks.
172-
FullApplySite IdenAI = CloneApply(AI, IdenBuilder);
173-
FullApplySite VirtAI = CloneApply(AI, VirtBuilder);
178+
FullApplySite IdenAI = CloneApply(AI, DownCastedClassInstance, IdenBuilder);
179+
FullApplySite VirtAI = CloneApply(AI, SILValue(), VirtBuilder);
174180

175181
// See if Continue has a release on self as the instruction right after the
176182
// apply. If it exists, move it into position in the diamond.

test/SILOptimizer/devirt_speculate.swift

Lines changed: 69 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,44 +10,96 @@ public class Base {
1010
public init() {}
1111
public func foo() {}
1212
}
13+
14+
@_optimize(none)
15+
func blackHole<T : AnyObject>(_: T) {}
16+
1317
class Sub1 : Base {
14-
override func foo() {}
18+
override func foo() { blackHole(self) }
1519
}
1620
class Sub2 : Base {
17-
override func foo() {}
21+
override func foo() { blackHole(self) }
1822
}
1923
class Sub3 : Base {
20-
override func foo() {}
24+
override func foo() { blackHole(self) }
2125
}
2226
class Sub4 : Base {
23-
override func foo() {}
27+
override func foo() { blackHole(self) }
2428
}
2529
class Sub5 : Base {
26-
override func foo() {}
30+
override func foo() { blackHole(self) }
2731
}
2832
class Sub6 : Base {
29-
override func foo() {}
33+
override func foo() { blackHole(self) }
3034
}
3135
class Sub7 : Base {
32-
override func foo() {}
36+
override func foo() { blackHole(self) }
3337
}
3438
// CHECK: @$s16devirt_speculate28testMaxNumSpeculativeTargetsyyAA4BaseCF
35-
// CHECK: checked_cast_br [exact] %0 : $Base to Base
36-
// CHECK: checked_cast_br [exact] %0 : $Base to Sub1
37-
// CHECK: checked_cast_br [exact] %0 : $Base to Sub2
38-
// CHECK: checked_cast_br [exact] %0 : $Base to Sub3
39-
// CHECK: checked_cast_br [exact] %0 : $Base to Sub4
40-
// CHECK: checked_cast_br [exact] %0 : $Base to Sub5
41-
// CHECK: checked_cast_br [exact] %0 : $Base to Sub6
39+
// CHECK: bb0(%0 : $Base):
40+
// CHECK: checked_cast_br [exact] %0 : $Base to Base, bb2, bb3
41+
42+
// CHECK: bb2([[CASTED:%.*]]):
43+
// CHECK: br bb1
44+
45+
// CHECK: bb3:
46+
// CHECK: checked_cast_br [exact] %0 : $Base to Sub1, bb4, bb5
47+
48+
// CHECK: bb4([[CASTED:%.*]] : $Sub1):
49+
// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> ()
50+
// CHECK: apply [[FN]]<Sub1>([[CASTED]])
51+
// CHECK: br bb1
52+
53+
// CHECK: bb5:
54+
// CHECK: checked_cast_br [exact] %0 : $Base to Sub2, bb6, bb7
55+
56+
// CHECK: bb6([[CASTED:%.*]] : $Sub2):
57+
// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> ()
58+
// CHECK: apply [[FN]]<Sub2>([[CASTED]])
59+
// CHECK: br bb1
60+
61+
// CHECK: bb7:
62+
// CHECK: checked_cast_br [exact] %0 : $Base to Sub3, bb8, bb9
63+
64+
// CHECK: bb8([[CASTED:%.*]] : $Sub3):
65+
// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> ()
66+
// CHECK: apply [[FN]]<Sub3>([[CASTED]])
67+
// CHECK: br bb1
68+
69+
// CHECK: bb9:
70+
// CHECK: checked_cast_br [exact] %0 : $Base to Sub4, bb10, bb11
71+
72+
// CHECK: bb10([[CASTED:%.*]] : $Sub4):
73+
// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> ()
74+
// CHECK: apply [[FN]]<Sub4>([[CASTED]])
75+
// CHECK: br bb1
76+
77+
// CHECK: bb11:
78+
// CHECK: checked_cast_br [exact] %0 : $Base to Sub5, bb12, bb13
79+
80+
// CHECK: bb12([[CASTED:%.*]] : $Sub5):
81+
// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> ()
82+
// CHECK: apply [[FN]]<Sub5>([[CASTED]])
83+
// CHECK: br bb1
84+
85+
// CHECK: bb13:
86+
// CHECK: checked_cast_br [exact] %0 : $Base to Sub6, bb14, bb15
87+
88+
// CHECK: bb14([[CASTED:%.*]] : $Sub6):
89+
// CHECK: [[FN:%.*]] = function_ref @$s16devirt_speculate9blackHoleyyxRlzClF : $@convention(thin) <τ_0_0 where τ_0_0 : AnyObject> (@guaranteed τ_0_0) -> ()
90+
// CHECK: apply [[FN]]<Sub6>([[CASTED]])
91+
// CHECK: br bb1
92+
93+
// CHECK: bb15:
4294
// CHECK-NOT: checked_cast_br
43-
// CHECK: %[[CM:[0-9]+]] = class_method %0 : $Base, #Base.foo!1 : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
44-
// CHECK: apply %[[CM]](%0) : $@convention(method) (@guaranteed Base) -> ()
95+
// CHECK: %[[CM:[0-9]+]] = class_method %0 : $Base, #Base.foo!1 : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
96+
// CHECK: apply %[[CM]](%0) : $@convention(method) (@guaranteed Base) -> ()
4597

4698
// YAML: Pass: sil-speculative-devirtualizer
4799
// YAML-NEXT: Name: sil.PartialSpecDevirt
48100
// YAML-NEXT: DebugLoc:
49101
// YAML-NEXT: File: {{.*}}/devirt_speculate.swift
50-
// YAML-NEXT: Line: 66
102+
// YAML-NEXT: Line: 118
51103
// YAML-NEXT: Column: 5
52104
// YAML-NEXT: Function: 'testMaxNumSpeculativeTargets(_:)'
53105
// YAML-NEXT: Args:

test/SILOptimizer/devirt_speculative.sil

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,6 @@ bb0(%0: $Base2):
145145
// CHECK-NEXT: unreachable
146146
// CHECK: checked_cast_br
147147
// CHECK: function_ref @Sub_exit
148-
// CHECK-NEXT: unchecked_ref_cast
149148
// CHECK-NEXT: apply
150149
// CHECK-NEXT: unreachable
151150
sil hidden [noinline] @test_devirt_of_noreturn_function : $@convention(thin) (@owned Base) -> () {

0 commit comments

Comments
 (0)