Skip to content

Commit 55b8555

Browse files
authored
Merge pull request #3286 from eeckstein/escape-analysis
2 parents 8048a13 + dda1749 commit 55b8555

File tree

3 files changed

+100
-7
lines changed

3 files changed

+100
-7
lines changed

lib/SILOptimizer/Analysis/EscapeAnalysis.cpp

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "swift/SILOptimizer/Analysis/ValueTracking.h"
1818
#include "swift/SILOptimizer/PassManager/PassManager.h"
1919
#include "swift/SIL/SILArgument.h"
20+
#include "swift/SIL/DebugUtils.h"
2021
#include "llvm/Support/GraphWriter.h"
2122
#include "llvm/Support/raw_ostream.h"
2223

@@ -30,13 +31,23 @@ static bool isProjection(ValueBase *V) {
3031
case ValueKind::TupleElementAddrInst:
3132
case ValueKind::UncheckedTakeEnumDataAddrInst:
3233
case ValueKind::StructExtractInst:
33-
case ValueKind::TupleExtractInst:
3434
case ValueKind::UncheckedEnumDataInst:
3535
case ValueKind::MarkDependenceInst:
3636
case ValueKind::PointerToAddressInst:
3737
case ValueKind::AddressToPointerInst:
3838
case ValueKind::InitEnumDataAddrInst:
3939
return true;
40+
case ValueKind::TupleExtractInst: {
41+
auto *TEI = cast<TupleExtractInst>(V);
42+
// Special handling for extracting the pointer-result from an
43+
// array construction. We handle this like a ref_element_addr
44+
// rather than a projection. See the handling of tuple_extract
45+
// in analyzeInstruction().
46+
if (TEI->getFieldNo() == 1 &&
47+
ArraySemanticsCall(TEI->getOperand(), "array.uninitialized", false))
48+
return false;
49+
return true;
50+
}
4051
default:
4152
return false;
4253
}
@@ -1095,6 +1106,15 @@ void EscapeAnalysis::buildConnectionGraph(FunctionInfo *FInfo,
10951106
FInfo->Graph.F->getName() << '\n');
10961107
}
10971108

1109+
/// Returns true if all uses of \p I are tuple_extract instructions.
1110+
static bool onlyUsedInTupleExtract(SILInstruction *I) {
1111+
for (Operand *Use : getNonDebugUses(I)) {
1112+
if (!isa<TupleExtractInst>(Use->getUser()))
1113+
return false;
1114+
}
1115+
return true;
1116+
}
1117+
10981118
void EscapeAnalysis::analyzeInstruction(SILInstruction *I,
10991119
FunctionInfo *FInfo,
11001120
FunctionOrder &BottomUpOrder,
@@ -1113,13 +1133,20 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I,
11131133
// These array semantics calls do not capture anything.
11141134
return;
11151135
case ArrayCallKind::kArrayUninitialized:
1116-
// array.uninitialized may have a first argument which is the
1117-
// allocated array buffer. The call is like a struct(buffer)
1118-
// instruction.
1119-
if (CGNode *BufferNode = ConGraph->getNode(FAS.getArgument(0), this)) {
1120-
ConGraph->defer(ConGraph->getNode(I, this), BufferNode);
1136+
// Check if the result is used in the usual way: extracting the
1137+
// array and the element pointer with tuple_extract.
1138+
if (onlyUsedInTupleExtract(I)) {
1139+
// array.uninitialized may have a first argument which is the
1140+
// allocated array buffer. The call is like a struct(buffer)
1141+
// instruction.
1142+
if (CGNode *BufferNode = ConGraph->getNode(FAS.getArgument(0), this)) {
1143+
CGNode *ArrayNode = ConGraph->getNode(I, this);
1144+
CGNode *ArrayContent = ConGraph->getContentNode(ArrayNode);
1145+
ConGraph->defer(ArrayContent, BufferNode);
1146+
}
1147+
return;
11211148
}
1122-
return;
1149+
break;
11231150
case ArrayCallKind::kGetArrayOwner:
11241151
if (CGNode *BufferNode = ConGraph->getNode(ASC.getSelf(), this)) {
11251152
ConGraph->defer(ConGraph->getNode(I, this), BufferNode);
@@ -1388,6 +1415,21 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I,
13881415
}
13891416
return;
13901417
}
1418+
case ValueKind::TupleExtractInst: {
1419+
// This is a tuple_extract which extracts the second result of an
1420+
// array.uninitialized call. The first result is the array itself.
1421+
// The second result (which is a pointer to the array elements) must be
1422+
// the content node of the first result. It's just like a ref_element_addr
1423+
// instruction.
1424+
auto *TEI = cast<TupleExtractInst>(I);
1425+
assert(TEI->getFieldNo() == 1 &&
1426+
ArraySemanticsCall(TEI->getOperand(), "array.uninitialized", false)
1427+
&& "tuple_extract should be handled as projection");
1428+
CGNode *ArrayNode = ConGraph->getNode(TEI->getOperand(), this);
1429+
CGNode *ArrayElements = ConGraph->getContentNode(ArrayNode);
1430+
ConGraph->setNode(I, ArrayElements);
1431+
return;
1432+
}
13911433
case ValueKind::UncheckedRefCastInst:
13921434
case ValueKind::ConvertFunctionInst:
13931435
case ValueKind::UpcastInst:

test/SILOptimizer/escape_analysis.sil

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1239,6 +1239,28 @@ bb(%0 : $*Array<X>, %1 : $@callee_owned (@inout X) -> (@out (), @error ErrorType
12391239
return %r : $()
12401240
}
12411241

1242+
// CHECK-LABEL: CG of arraysemantics_createUninitialized
1243+
// CHECK-NEXT: Arg %0 Esc: A, Succ:
1244+
// CHECK-NEXT: Val %2 Esc: R, Succ: (%4.2)
1245+
// CHECK-NEXT: Val %4 Esc: R, Succ: (%4.1)
1246+
// CHECK-NEXT: Con %4.1 Esc: R, Succ: %2
1247+
// CHECK-NEXT: Con %4.2 Esc: R, Succ: %0
1248+
// CHECK-NEXT: Ret Esc: R, Succ: %4
1249+
// CHECK-NEXT: End
1250+
sil @arraysemantics_createUninitialized : $@convention(thin) (@owned X) -> @owned Array<X> {
1251+
bb0(%0 : $X):
1252+
%1 = function_ref @swift_bufferAllocate : $@convention(thin) () -> @owned AnyObject
1253+
%2 = apply %1() : $@convention(thin) () -> @owned AnyObject
1254+
%3 = function_ref @createUninitialized : $@convention(method) (@owned AnyObject) -> (@owned Array<X>, UnsafeMutablePointer<X>)
1255+
%4 = apply %3(%2) : $@convention(method) (@owned AnyObject) -> (@owned Array<X>, UnsafeMutablePointer<X>)
1256+
%5 = tuple_extract %4 : $(Array<X>, UnsafeMutablePointer<X>), 0
1257+
%6 = tuple_extract %4 : $(Array<X>, UnsafeMutablePointer<X>), 1
1258+
%7 = struct_extract %6 : $UnsafeMutablePointer<X>, #UnsafeMutablePointer._rawValue
1259+
%8 = pointer_to_address %7 : $Builtin.RawPointer to $*X
1260+
store %0 to %8 : $*X
1261+
return %5 : $Array<X>
1262+
}
1263+
12421264
sil [_semantics "array.withUnsafeMutableBufferPointer"] @withUnsafeMutableBufferPointer : $@convention(method) (@owned @callee_owned (@inout X) -> (@out (), @error ErrorType), @inout Array<X>) -> (@out (), @error ErrorType)
12431265
sil [_semantics "array.props.isNativeTypeChecked"] @is_native_type_checked : $@convention(method) (@guaranteed Array<X>) -> Bool
12441266
sil [_semantics "array.check_subscript"] @check_subscript : $@convention(method) (Int32, Bool, @guaranteed Array<X>) -> ()
@@ -1251,6 +1273,10 @@ sil [_semantics "array.get_capacity"] @get_capacity : $@convention(method) (@gua
12511273

12521274
sil [_semantics "pair_no_escaping_closure"] @unsafeWithNotEscapedSelfPointerPair : $@convention(method) (@owned X, @owned @callee_owned (X, X) -> (@out X, @error ErrorType), @guaranteed X) -> (@out X, @error ErrorType)
12531275
sil [_semantics "self_no_escaping_closure"] @unsafeWithNotEscapedSelfPointer: $@convention(method) (@owned @callee_owned (X, X) -> (@out X, @error ErrorType), @guaranteed X) -> (@out X, @error ErrorType)
1276+
sil [_semantics "array.uninitialized"] @createUninitialized : $@convention(method) (@owned AnyObject) -> (@owned Array<X>, UnsafeMutablePointer<X>)
1277+
1278+
// A simplified version of swift_bufferAllocate
1279+
sil @swift_bufferAllocate : $@convention(thin) () -> @owned AnyObject
12541280

12551281
// CHECK-LABEL: CG of semantics_pair_no_escaping_closure
12561282
// CHECK-NEXT: Arg %0 Esc: A, Succ:
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %target-swift-frontend -parse-as-library -O -module-name=test %s -emit-sil | FileCheck %s
2+
3+
final class Item {}
4+
5+
final public class Escaper {
6+
var myItem: Item = Item()
7+
8+
@inline(never)
9+
func update(items: [Item]) {
10+
myItem = items[0]
11+
}
12+
13+
// CHECK-LABEL: sil [noinline] @_TFC4test7Escaper15badStuffHappensfT_T_ : $@convention(method) (@guaranteed Escaper) -> () {
14+
// CHECK: %2 = alloc_ref $Item
15+
// CHECK: function_ref @swift_bufferAllocateOnStack
16+
// CHECK: return
17+
@inline(never)
18+
public func badStuffHappens() {
19+
// Check that 'item' is not stack promoted, because it escapes to myItem.
20+
let item = Item()
21+
// On the other hand, the array buffer of the array literal should be stack promoted.
22+
update(items:[item])
23+
}
24+
}
25+

0 commit comments

Comments
 (0)