Skip to content

Commit 1ea21db

Browse files
authored
Merge pull request #40287 from gottesmm/pr-8bb19fcfe173f5e7b33ddd95dcbc5566be2262b8
[sil] Ban casting AnyObject with a guaranteed ownership forwarding checked_cast_br and fix up semantic-arc-opts given that.
2 parents 95a4ea4 + 8e5fb21 commit 1ea21db

File tree

8 files changed

+187
-106
lines changed

8 files changed

+187
-106
lines changed

include/swift/SIL/SILInstruction.h

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,16 +1057,40 @@ class CopyLikeInstruction {
10571057
/// initializes the kind field on our object is run before our constructor runs.
10581058
class OwnershipForwardingMixin {
10591059
ValueOwnershipKind ownershipKind;
1060+
bool directlyForwards;
10601061

10611062
protected:
10621063
OwnershipForwardingMixin(SILInstructionKind kind,
1063-
ValueOwnershipKind ownershipKind)
1064-
: ownershipKind(ownershipKind) {
1064+
ValueOwnershipKind ownershipKind,
1065+
bool isDirectlyForwarding = true)
1066+
: ownershipKind(ownershipKind), directlyForwards(isDirectlyForwarding) {
10651067
assert(isa(kind) && "Invalid subclass?!");
10661068
assert(ownershipKind && "invalid forwarding ownership");
1069+
assert((directlyForwards || ownershipKind != OwnershipKind::Guaranteed) &&
1070+
"Non directly forwarding instructions can not forward guaranteed "
1071+
"ownership");
10671072
}
10681073

10691074
public:
1075+
/// If an instruction is directly forwarding, then any operand op whose
1076+
/// ownership it forwards into a result r must have the property that op and r
1077+
/// are "rc identical". This means that they are representing the same set of
1078+
/// underlying lifetimes (plural b/c of aggregates).
1079+
///
1080+
/// An instruction that is not directly forwarding, can not have guaranteed
1081+
/// ownership since without direct forwarding, there isn't necessarily any
1082+
/// connection in between the operand's lifetime and the value's lifetime.
1083+
///
1084+
/// An example of this is checked_cast_br where when performing the following:
1085+
///
1086+
/// __SwiftValue(AnyHashable(Klass())) to OtherKlass()
1087+
///
1088+
/// we will look through the __SwiftValue(AnyHashable(X)) any just cast Klass
1089+
/// to OtherKlass. This means that the result argument would no longer be
1090+
/// rc-identical to the operand and default case and thus we can not propagate
1091+
/// forward any form of guaranteed ownership.
1092+
bool isDirectlyForwarding() const { return directlyForwards; }
1093+
10701094
/// Forwarding ownership is determined by the forwarding instruction's
10711095
/// constant ownership attribute. If forwarding ownership is owned, then the
10721096
/// instruction moves an owned operand to its result, ending its lifetime. If
@@ -1081,6 +1105,9 @@ class OwnershipForwardingMixin {
10811105
return ownershipKind;
10821106
}
10831107
void setForwardingOwnershipKind(ValueOwnershipKind newKind) {
1108+
assert((isDirectlyForwarding() || newKind != OwnershipKind::Guaranteed) &&
1109+
"Non directly forwarding instructions can not forward guaranteed "
1110+
"ownership");
10841111
ownershipKind = newKind;
10851112
}
10861113

@@ -7927,9 +7954,10 @@ class OwnershipForwardingTermInst : public TermInst,
79277954
protected:
79287955
OwnershipForwardingTermInst(SILInstructionKind kind,
79297956
SILDebugLocation debugLoc,
7930-
ValueOwnershipKind ownershipKind)
7957+
ValueOwnershipKind ownershipKind,
7958+
bool isDirectlyForwarding = true)
79317959
: TermInst(kind, debugLoc),
7932-
OwnershipForwardingMixin(kind, ownershipKind) {
7960+
OwnershipForwardingMixin(kind, ownershipKind, isDirectlyForwarding) {
79337961
assert(classof(kind));
79347962
}
79357963

@@ -8909,7 +8937,13 @@ class CheckedCastBranchInst final
89098937
ValueOwnershipKind forwardingOwnershipKind)
89108938
: UnaryInstructionWithTypeDependentOperandsBase(
89118939
DebugLoc, Operand, TypeDependentOperands, SuccessBB, FailureBB,
8912-
Target1Count, Target2Count, forwardingOwnershipKind),
8940+
Target1Count, Target2Count, forwardingOwnershipKind,
8941+
// We are always directly forwarding unless we are casting an
8942+
// AnyObject. This is b/c an AnyObject could contain a boxed
8943+
// AnyObject(Class()) that we unwrap as part of the cast. In such a
8944+
// case, we would return a different value and potentially end the
8945+
// lifetime of the operand value.
8946+
!Operand->getType().isAnyObject()),
89138947
DestLoweredTy(DestLoweredTy), DestFormalTy(DestFormalTy),
89148948
IsExact(IsExact) {}
89158949

lib/SIL/Utils/MemAccessUtils.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -877,9 +877,11 @@ SILValue swift::findOwnershipReferenceAggregate(SILValue ref) {
877877
if (auto *arg = dyn_cast<SILArgument>(root)) {
878878
if (auto *term = arg->getSingleTerminator()) {
879879
if (term->isTransformationTerminator()) {
880-
assert(OwnershipForwardingTermInst::isa(term));
881-
root = term->getOperand(0);
882-
continue;
880+
auto *ti = cast<OwnershipForwardingTermInst>(term);
881+
if (ti->isDirectlyForwarding()) {
882+
root = term->getOperand(0);
883+
continue;
884+
}
883885
}
884886
}
885887
}

lib/SIL/Utils/OwnershipUtils.cpp

Lines changed: 32 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -29,121 +29,51 @@ bool swift::isValueAddressOrTrivial(SILValue v) {
2929
v.getOwnershipKind() == OwnershipKind::None;
3030
}
3131

32-
// These operations forward both owned and guaranteed ownership.
33-
static bool isOwnershipForwardingInstructionKind(SILInstructionKind kind) {
34-
switch (kind) {
35-
case SILInstructionKind::TupleInst:
36-
case SILInstructionKind::StructInst:
37-
case SILInstructionKind::EnumInst:
38-
case SILInstructionKind::DifferentiableFunctionInst:
39-
case SILInstructionKind::LinearFunctionInst:
40-
case SILInstructionKind::OpenExistentialRefInst:
41-
case SILInstructionKind::UpcastInst:
42-
case SILInstructionKind::UncheckedValueCastInst:
43-
case SILInstructionKind::UncheckedRefCastInst:
44-
case SILInstructionKind::ConvertFunctionInst:
45-
case SILInstructionKind::RefToBridgeObjectInst:
46-
case SILInstructionKind::BridgeObjectToRefInst:
47-
case SILInstructionKind::UnconditionalCheckedCastInst:
48-
case SILInstructionKind::UncheckedEnumDataInst:
49-
case SILInstructionKind::SelectEnumInst:
50-
case SILInstructionKind::SwitchEnumInst:
51-
case SILInstructionKind::CheckedCastBranchInst:
52-
case SILInstructionKind::DestructureStructInst:
53-
case SILInstructionKind::DestructureTupleInst:
54-
case SILInstructionKind::MarkDependenceInst:
55-
case SILInstructionKind::InitExistentialRefInst:
56-
return true;
57-
default:
58-
return false;
59-
}
60-
}
61-
62-
// These operations forward guaranteed ownership, but don't necessarily forward
63-
// owned values.
64-
static bool isGuaranteedForwardingInstructionKind(SILInstructionKind kind) {
65-
switch (kind) {
66-
case SILInstructionKind::TupleExtractInst:
67-
case SILInstructionKind::StructExtractInst:
68-
case SILInstructionKind::DifferentiableFunctionExtractInst:
69-
case SILInstructionKind::LinearFunctionExtractInst:
70-
case SILInstructionKind::OpenExistentialValueInst:
71-
case SILInstructionKind::OpenExistentialBoxValueInst:
72-
return true;
73-
default:
74-
return isOwnershipForwardingInstructionKind(kind);
75-
}
76-
}
77-
7832
bool swift::canOpcodeForwardGuaranteedValues(SILValue value) {
7933
// If we have an argument from a transforming terminator, we can forward
8034
// guaranteed.
8135
if (auto *arg = dyn_cast<SILArgument>(value))
8236
if (auto *ti = arg->getSingleTerminator())
83-
if (ti->isTransformationTerminator()) {
84-
assert(OwnershipForwardingMixin::isa(ti));
85-
return true;
86-
}
37+
if (ti->isTransformationTerminator())
38+
return OwnershipForwardingMixin::get(ti)->isDirectlyForwarding();
8739

88-
auto *inst = value->getDefiningInstruction();
89-
if (!inst)
90-
return false;
40+
if (auto *inst = value->getDefiningInstruction())
41+
if (auto *mixin = OwnershipForwardingMixin::get(inst))
42+
return mixin->isDirectlyForwarding() &&
43+
!isa<OwnedFirstArgForwardingSingleValueInst>(inst);
9144

92-
bool result = isGuaranteedForwardingInstructionKind(inst->getKind());
93-
if (result) {
94-
assert(!isa<OwnedFirstArgForwardingSingleValueInst>(inst));
95-
assert(OwnershipForwardingMixin::isa(inst));
96-
}
97-
return result;
45+
return false;
9846
}
9947

10048
bool swift::canOpcodeForwardGuaranteedValues(Operand *use) {
101-
auto *user = use->getUser();
102-
bool result = isOwnershipForwardingInstructionKind(user->getKind());
103-
if (result) {
104-
assert(!isa<GuaranteedFirstArgForwardingSingleValueInst>(user));
105-
assert(OwnershipForwardingMixin::isa(user));
106-
}
107-
return result;
108-
}
109-
110-
static bool isOwnedForwardingValueKind(SILInstructionKind kind) {
111-
switch (kind) {
112-
case SILInstructionKind::MarkUninitializedInst:
113-
return true;
114-
default:
115-
return isOwnershipForwardingInstructionKind(kind);
116-
}
49+
if (auto *mixin = OwnershipForwardingMixin::get(use->getUser()))
50+
return mixin->isDirectlyForwarding() &&
51+
!isa<OwnedFirstArgForwardingSingleValueInst>(use->getUser());
52+
return false;
11753
}
11854

11955
bool swift::canOpcodeForwardOwnedValues(SILValue value) {
12056
// If we have a SILArgument and we are the successor block of a transforming
12157
// terminator, we are fine.
12258
if (auto *arg = dyn_cast<SILPhiArgument>(value))
12359
if (auto *predTerm = arg->getSingleTerminator())
124-
if (predTerm->isTransformationTerminator()) {
125-
assert(OwnershipForwardingMixin::isa(predTerm));
126-
return true;
127-
}
128-
auto *inst = value->getDefiningInstruction();
129-
if (!inst)
130-
return false;
60+
if (predTerm->isTransformationTerminator())
61+
return OwnershipForwardingMixin::get(predTerm)->isDirectlyForwarding();
13162

132-
bool result = isOwnedForwardingValueKind(inst->getKind());
133-
if (result) {
134-
assert(!isa<GuaranteedFirstArgForwardingSingleValueInst>(inst));
135-
assert(OwnershipForwardingMixin::isa(inst));
136-
}
137-
return result;
63+
if (auto *inst = value->getDefiningInstruction())
64+
if (auto *mixin = OwnershipForwardingMixin::get(inst))
65+
return mixin->isDirectlyForwarding() &&
66+
!isa<GuaranteedFirstArgForwardingSingleValueInst>(inst);
67+
68+
return false;
13869
}
13970

14071
bool swift::canOpcodeForwardOwnedValues(Operand *use) {
14172
auto *user = use->getUser();
142-
bool result = isOwnershipForwardingInstructionKind(user->getKind());
143-
if (result) {
144-
assert(OwnershipForwardingMixin::isa(user));
145-
}
146-
return result;
73+
if (auto *mixin = OwnershipForwardingMixin::get(user))
74+
return mixin->isDirectlyForwarding() &&
75+
!isa<GuaranteedFirstArgForwardingSingleValueInst>(user);
76+
return false;
14777
}
14878

14979
//===----------------------------------------------------------------------===//
@@ -980,7 +910,8 @@ bool swift::getAllBorrowIntroducingValues(SILValue inputValue,
980910
// predecessor terminator.
981911
auto *arg = cast<SILPhiArgument>(value);
982912
auto *termInst = arg->getSingleTerminator();
983-
assert(termInst && termInst->isTransformationTerminator());
913+
assert(termInst && termInst->isTransformationTerminator() &&
914+
OwnershipForwardingMixin::get(termInst)->isDirectlyForwarding());
984915
assert(termInst->getNumOperands() == 1 &&
985916
"Transforming terminators should always have a single operand");
986917
worklist.push_back(termInst->getAllOperands()[0].get());
@@ -1031,7 +962,8 @@ BorrowedValue swift::getSingleBorrowIntroducingValue(SILValue inputValue) {
1031962
// predecessor terminator.
1032963
auto *arg = cast<SILPhiArgument>(currentValue);
1033964
auto *termInst = arg->getSingleTerminator();
1034-
assert(termInst && termInst->isTransformationTerminator());
965+
assert(termInst && termInst->isTransformationTerminator() &&
966+
OwnershipForwardingMixin::get(termInst)->isDirectlyForwarding());
1035967
assert(termInst->getNumOperands() == 1 &&
1036968
"Transformation terminators should only have single operands");
1037969
currentValue = termInst->getAllOperands()[0].get();
@@ -1083,7 +1015,8 @@ bool swift::getAllOwnedValueIntroducers(
10831015
// predecessor terminator.
10841016
auto *arg = cast<SILPhiArgument>(value);
10851017
auto *termInst = arg->getSingleTerminator();
1086-
assert(termInst && termInst->isTransformationTerminator());
1018+
assert(termInst && termInst->isTransformationTerminator() &&
1019+
OwnershipForwardingMixin::get(termInst)->isDirectlyForwarding());
10871020
assert(termInst->getNumOperands() == 1 &&
10881021
"Transforming terminators should always have a single operand");
10891022
worklist.push_back(termInst->getAllOperands()[0].get());
@@ -1127,10 +1060,11 @@ OwnedValueIntroducer swift::getSingleOwnedValueIntroducer(SILValue inputValue) {
11271060
}
11281061

11291062
// Otherwise, we should have a block argument that is defined by a single
1130-
// predecessor terminator.
1063+
// predecessor terminator and is directly forwarding.
11311064
auto *arg = cast<SILPhiArgument>(currentValue);
11321065
auto *termInst = arg->getSingleTerminator();
1133-
assert(termInst && termInst->isTransformationTerminator());
1066+
assert(termInst && termInst->isTransformationTerminator() &&
1067+
OwnershipForwardingMixin::get(termInst)->isDirectlyForwarding());
11341068
assert(termInst->getNumOperands()
11351069
- termInst->getNumTypeDependentOperands() == 1 &&
11361070
"Transformation terminators should only have single operands");

lib/SIL/Verifier/SILVerifier.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4048,6 +4048,24 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
40484048
CBI->getOperand().getOwnershipKind()),
40494049
"failure dest block argument must have ownership compatible with "
40504050
"the checked_cast_br operand");
4051+
4052+
// Do not allow for checked_cast_br to forward guaranteed ownership if the
4053+
// source type is an AnyObject.
4054+
//
4055+
// EXPLANATION: A checked_cast_br from an AnyObject may return a different
4056+
// object. This occurs for instance if one has an AnyObject that is a
4057+
// boxed AnyHashable (ClassType). This breaks with the guarantees of
4058+
// checked_cast_br guaranteed, so we ban it.
4059+
require(!CBI->getOperand()->getType().isAnyObject() ||
4060+
CBI->getOperand().getOwnershipKind() !=
4061+
OwnershipKind::Guaranteed,
4062+
"checked_cast_br with an AnyObject typed source cannot forward "
4063+
"guaranteed ownership");
4064+
require(CBI->isDirectlyForwarding() ||
4065+
CBI->getOperand().getOwnershipKind() !=
4066+
OwnershipKind::Guaranteed,
4067+
"If checked_cast_br is not directly forwarding, it can not have "
4068+
"guaranteed ownership");
40514069
} else {
40524070
require(CBI->getFailureBB()->args_empty(),
40534071
"Failure dest of checked_cast_br must not take any argument in "

lib/SILGen/SILGenBuilder.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,12 @@ void SILGenBuilder::createCheckedCastBranch(SILLocation loc, bool isExact,
536536
SILBasicBlock *falseBlock,
537537
ProfileCounter Target1Count,
538538
ProfileCounter Target2Count) {
539+
// Check if our source type is AnyObject. In such a case, we need to ensure
540+
// plus one our operand since SIL does not support guaranteed casts from an
541+
// AnyObject.
542+
if (op.getType().isAnyObject()) {
543+
op = op.ensurePlusOne(SGF, loc);
544+
}
539545
createCheckedCastBranch(loc, isExact, op.forward(SGF),
540546
destLoweredTy, destFormalTy,
541547
trueBlock, falseBlock,

lib/SILOptimizer/SemanticARC/OwnershipLiveRange.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "OwnershipPhiOperand.h"
1515

1616
#include "swift/SIL/BasicBlockUtils.h"
17+
#include "swift/SIL/OwnershipUtils.h"
1718

1819
using namespace swift;
1920
using namespace swift::semanticarc;
@@ -83,6 +84,20 @@ OwnershipLiveRange::OwnershipLiveRange(SILValue value)
8384
continue;
8485
}
8586

87+
// If we have a subclass of OwnershipForwardingMixin that doesnt directly
88+
// forward its operand to the result, treat the use as an unknown consuming
89+
// use.
90+
//
91+
// If we do not directly forward and we have an owned value (which we do
92+
// here), we could get back a different value. Thus we can not transform
93+
// such a thing from owned to guaranteed.
94+
if (auto *i = OwnershipForwardingMixin::get(op->getUser())) {
95+
if (!i->isDirectlyForwarding()) {
96+
tmpUnknownConsumingUses.push_back(op);
97+
continue;
98+
}
99+
}
100+
86101
// Ok, this is a forwarding instruction whose ownership we can flip from
87102
// owned -> guaranteed.
88103
tmpForwardingConsumingUses.push_back(op);
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// RUN: %target-swift-emit-silgen -module-name checked_cast_br_anyobject -parse-as-library -Xllvm -sil-full-demangle -enforce-exclusivity=checked %s
2+
3+
public struct BridgedSwiftObject {
4+
var swiftMetatype : UnsafeRawPointer
5+
var refCounts : Int64
6+
}
7+
8+
public typealias SwiftObject = UnsafeMutablePointer<BridgedSwiftObject>
9+
10+
extension UnsafeMutablePointer where Pointee == BridgedSwiftObject {
11+
init<T: AnyObject>(_ object: T) {
12+
let ptr = Unmanaged.passUnretained(object).toOpaque()
13+
self = ptr.bindMemory(to: BridgedSwiftObject.self, capacity: 1)
14+
}
15+
16+
func getAs<T: AnyObject>(_ objectType: T.Type) -> T {
17+
return Unmanaged<T>.fromOpaque(self).takeUnretainedValue()
18+
}
19+
}
20+
21+
extension Optional where Wrapped == UnsafeMutablePointer<BridgedSwiftObject> {
22+
func getAs<T: AnyObject>(_ objectType: T.Type) -> T? {
23+
if let pointer = self {
24+
return Unmanaged<T>.fromOpaque(pointer).takeUnretainedValue()
25+
}
26+
return nil
27+
}
28+
}
29+
30+
public class Klass {}
31+
public class Klass2 {}
32+
33+
// Make sure that we do not crash when emitting this code!
34+
public func getValue(_ obj: UnsafeMutablePointer<BridgedSwiftObject>) -> AnyObject {
35+
let v = obj.getAs(AnyObject.self)
36+
switch v {
37+
case let k as Klass:
38+
return k
39+
case let k as Klass2:
40+
return k
41+
default:
42+
fatalError("unknown type")
43+
}
44+
}

0 commit comments

Comments
 (0)