Skip to content

Commit 7705393

Browse files
committed
SILGen: Ease off +0 peepholes for load exprs.
Now that we more tightly close formal accesses on lvalues, having LoadExpr and friends try to return a +0 loaded value is unsafe without deeper analysis, since the access will be closed immediately after the load and potentially free temporary memory that might be the only thing keeping the borrowed object alive. Fixes rdar://problem/32730865.
1 parent 879e8e5 commit 7705393

File tree

7 files changed

+56
-16
lines changed

7 files changed

+56
-16
lines changed

lib/SILGen/SGFContext.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,9 @@ class SGFContext {
152152
/// a value. The Initialization is not okay to propagate down, but
153153
/// the +0/+1-ness is.
154154
SGFContext withFollowingProjection() const {
155-
SGFContext copy;
156-
copy.state.setInt(state.getInt());
157-
return copy;
155+
SGFContext result;
156+
result.state.setInt(state.getInt());
157+
return result;
158158
}
159159
};
160160

lib/SILGen/SILGenExpr.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1299,7 +1299,10 @@ RValue RValueEmitter::visitLoadExpr(LoadExpr *E, SGFContext C) {
12991299
// Any writebacks here are tightly scoped.
13001300
FormalEvaluationScope writeback(SGF);
13011301
LValue lv = SGF.emitLValue(E->getSubExpr(), AccessKind::Read);
1302-
return SGF.emitLoadOfLValue(E, std::move(lv), C);
1302+
// We can't load at immediate +0 from the lvalue without deeper analysis,
1303+
// since the access will be immediately ended and might invalidate the value
1304+
// we loaded.
1305+
return SGF.emitLoadOfLValue(E, std::move(lv), C.withFollowingSideEffects());
13031306
}
13041307

13051308
SILValue SILGenFunction::emitTemporaryAllocation(SILLocation loc,
@@ -2367,7 +2370,9 @@ RValue RValueEmitter::visitMemberRefExpr(MemberRefExpr *E, SGFContext C) {
23672370
FormalEvaluationScope scope(SGF);
23682371

23692372
LValue lv = SGF.emitLValue(E, AccessKind::Read);
2370-
return SGF.emitLoadOfLValue(E, std::move(lv), C);
2373+
// We can't load at +0 without further analysis, since the formal access into
2374+
// the lvalue will end immediately.
2375+
return SGF.emitLoadOfLValue(E, std::move(lv), C.withFollowingSideEffects());
23712376
}
23722377

23732378
RValue RValueEmitter::visitDynamicMemberRefExpr(DynamicMemberRefExpr *E,
@@ -2386,7 +2391,9 @@ RValue RValueEmitter::visitSubscriptExpr(SubscriptExpr *E, SGFContext C) {
23862391
FormalEvaluationScope scope(SGF);
23872392

23882393
LValue lv = SGF.emitLValue(E, AccessKind::Read);
2389-
return SGF.emitLoadOfLValue(E, std::move(lv), C);
2394+
// We can't load at +0 without further analysis, since the formal access into
2395+
// the lvalue will end immediately.
2396+
return SGF.emitLoadOfLValue(E, std::move(lv), C.withFollowingSideEffects());
23902397
}
23912398

23922399
RValue RValueEmitter::visitDynamicSubscriptExpr(

lib/SILGen/SILGenLValue.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3127,7 +3127,7 @@ RValue SILGenFunction::emitLoadOfLValue(SILLocation loc, LValue &&src,
31273127
PathComponent &&component =
31283128
drillToLastComponent(*this, loc, std::move(src), addr, AccessKind::Read);
31293129

3130-
// If the last component is physical, just drill down and load from it.
3130+
// If the last component is physical, drill down and load from it.
31313131
if (component.isPhysical()) {
31323132
addr = std::move(component.asPhysical())
31333133
.offset(*this, loc, addr, AccessKind::Read);
@@ -3137,7 +3137,7 @@ RValue SILGenFunction::emitLoadOfLValue(SILLocation loc, LValue &&src,
31373137
isGuaranteedValid));
31383138
}
31393139

3140-
// If the last component is logical, just emit a get.
3140+
// If the last component is logical, emit a get.
31413141
return std::move(component.asLogical()).get(*this, loc, addr, C);
31423142
}
31433143

test/SILGen/dynamic_lookup.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func direct_to_static_method(_ obj: AnyObject) {
6262
// CHECK: store [[ARG_COPY]] to [init] [[PBOBJ]] : $*AnyObject
6363
// CHECK: end_borrow [[BORROWED_ARG]] from [[ARG]]
6464
// CHECK: [[READ:%.*]] = begin_access [read] [unknown] [[PBOBJ]]
65-
// CHECK-NEXT: [[OBJCOPY:%[0-9]+]] = load_borrow [[READ]] : $*AnyObject
65+
// CHECK-NEXT: [[OBJCOPY:%[0-9]+]] = load [copy] [[READ]] : $*AnyObject
6666
// CHECK: end_access [[READ]]
6767
// CHECK-NEXT: [[OBJMETA:%[0-9]+]] = existential_metatype $@thick AnyObject.Type, [[OBJCOPY]] : $AnyObject
6868
// CHECK-NEXT: [[OPENMETA:%[0-9]+]] = open_existential_metatype [[OBJMETA]] : $@thick AnyObject.Type to $@thick (@opened([[UUID:".*"]]) AnyObject).Type
@@ -141,7 +141,7 @@ func opt_to_static_method(_ obj: AnyObject) {
141141
// CHECK: [[OPTBOX:%[0-9]+]] = alloc_box ${ var Optional<@callee_owned () -> ()> }
142142
// CHECK: [[PBO:%.*]] = project_box [[OPTBOX]]
143143
// CHECK: [[READ:%.*]] = begin_access [read] [unknown] [[PBOBJ]]
144-
// CHECK: [[OBJCOPY:%[0-9]+]] = load_borrow [[READ]] : $*AnyObject
144+
// CHECK: [[OBJCOPY:%[0-9]+]] = load [copy] [[READ]] : $*AnyObject
145145
// CHECK: [[OBJMETA:%[0-9]+]] = existential_metatype $@thick AnyObject.Type, [[OBJCOPY]] : $AnyObject
146146
// CHECK: [[OPENMETA:%[0-9]+]] = open_existential_metatype [[OBJMETA]] : $@thick AnyObject.Type to $@thick (@opened
147147
// CHECK: [[OBJCMETA:%[0-9]+]] = thick_to_objc_metatype [[OPENMETA]]

test/SILGen/functions.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ func calls(_ i:Int, j:Int, k:Int) {
176176
// -- Curry the Type onto static method argument lists.
177177

178178
// CHECK: [[READC:%.*]] = begin_access [read] [unknown] [[CADDR]]
179-
// CHECK: [[C:%[0-9]+]] = load_borrow [[READC]]
179+
// CHECK: [[C:%[0-9]+]] = load [copy] [[READC]]
180180
// CHECK: [[META:%.*]] = value_metatype $@thick SomeClass.Type, [[C]]
181181
// CHECK: [[METHOD:%[0-9]+]] = class_method [[META]] : {{.*}}, #SomeClass.static_method!1
182182
// CHECK: [[READI:%.*]] = begin_access [read] [unknown] [[IADDR]]
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// RUN: %target-swift-frontend -emit-silgen %s | %FileCheck %s
2+
3+
class A {
4+
lazy var b: B = B()
5+
}
6+
7+
final class B {
8+
var c: C? {
9+
get { return nil }
10+
set {}
11+
}
12+
}
13+
14+
struct C {
15+
let d: String
16+
}
17+
18+
// CHECK-LABEL: sil hidden @{{.*}}test
19+
func test(a: A) {
20+
let s: String?
21+
// CHECK: [[C_TEMP:%.*]] = alloc_stack $Optional<C>
22+
// CHECK: [[TAG:%.*]] = select_enum_addr [[C_TEMP]]
23+
// CHECK: cond_br [[TAG]], [[SOME:bb[0-9]+]], [[NONE:bb[0-9]+]]
24+
// CHECK: [[SOME]]:
25+
// CHECK: [[C_PAYLOAD:%.*]] = unchecked_take_enum_data_addr [[C_TEMP]]
26+
// -- This must be a copy, since we'll immediately destroy the value in the
27+
// temp buffer
28+
// CHECK: [[LOAD:%.*]] = load [copy] [[C_PAYLOAD]]
29+
// CHECK: destroy_addr [[C_TEMP]]
30+
s = a.b.c?.d
31+
print(s)
32+
}

test/SILGen/opaque_values_silgen.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,20 +1022,21 @@ func s480_________getError(someError: Error) -> Any {
10221022
// CHECK-LABEL: sil private @_T020opaque_values_silgen21s490_______loadBorrowyyF3FooL_V3foo7ElementQzSg5IndexQz3pos_tF : $@convention(method) <Elements where Elements : Collection> (@in Elements.Index, @inout Foo<Elements>) -> @out Optional<Elements.Element> {
10231023
// CHECK: bb0(%0 : $Elements.Index, %1 : $*Foo<Elements>):
10241024
// CHECK: [[READ:%.*]] = begin_access [read] [unknown] %1 : $*Foo<Elements>
1025-
// CHECK: [[LOAD:%.*]] = load_borrow [[READ]] : $*Foo<Elements>
1025+
// CHECK: [[LOAD:%.*]] = load [copy] [[READ]] : $*Foo<Elements>
10261026
// CHECK: end_access [[READ]] : $*Foo<Elements>
1027-
// CHECK: [[EXTRACT:%.*]] = struct_extract [[LOAD]] : $Foo<Elements>, #<abstract function>Foo._elements
1027+
// CHECK: [[BORROW_LOAD:%.*]] = begin_borrow [[LOAD]]
1028+
// CHECK: [[EXTRACT:%.*]] = struct_extract [[BORROW_LOAD]] : $Foo<Elements>, #<abstract function>Foo._elements
10281029
// CHECK: [[COPYELT:%.*]] = copy_value [[EXTRACT]] : $Elements
10291030
// CHECK: [[BORROW:%.*]] = begin_borrow %0 : $Elements.Index
10301031
// CHECK: [[COPYIDX:%.*]] = copy_value [[BORROW]] : $Elements.Index
10311032
// CHECK: [[WT:%.*]] = witness_method $Elements, #Collection.subscript!getter.1 : <Self where Self : Collection> (Self) -> (Self.Index) -> Self.Element : $@convention(witness_method) <τ_0_0 where τ_0_0 : Collection> (@in τ_0_0.Index, @in_guaranteed τ_0_0) -> @out τ_0_0.Element
10321033
// CHECK: %{{.*}} = apply [[WT]]<Elements>([[COPYIDX]], [[COPYELT]]) : $@convention(witness_method) <τ_0_0 where τ_0_0 : Collection> (@in τ_0_0.Index, @in_guaranteed τ_0_0) -> @out τ_0_0.Element
10331034
// CHECK: destroy_value [[COPYELT]] : $Elements
1034-
// CHECK: [[ENUM:%.*]] = enum $Optional<Elements.Element>, #Optional.some!enumelt.1, %12 : $Elements.Element
1035+
// CHECK: [[ENUM:%.*]] = enum $Optional<Elements.Element>, #Optional.some!enumelt.1, %13 : $Elements.Element
10351036
// CHECK: end_borrow [[BORROW]] from %0 : $Elements.Index, $Elements.Index
1036-
// CHECK: end_borrow [[LOAD]] from [[READ]] : $Foo<Elements>, $*Foo<Elements>
1037+
// CHECK: destroy_value [[LOAD]]
10371038
// CHECK: destroy_value %0 : $Elements.Index
1038-
// CHECK: return %14 : $Optional<Elements.Element>
1039+
// CHECK: return %15 : $Optional<Elements.Element>
10391040
// CHECK-LABEL: } // end sil function '_T020opaque_values_silgen21s490_______loadBorrowyyF3FooL_V3foo7ElementQzSg5IndexQz3pos_tF'
10401041

10411042
func s490_______loadBorrow() {

0 commit comments

Comments
 (0)