Skip to content

Commit 8e5fb21

Browse files
committed
[sil] Ban casting AnyObject with a guaranteed ownership forwarding checked_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
1 parent 17f1cf9 commit 8e5fb21

File tree

8 files changed

+187
-106
lines changed

8 files changed

+187
-106
lines changed

include/swift/SIL/SILInstruction.h

+39-5
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

+5-3
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

+32-98
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

+18
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

+6
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

+15
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);
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)