Skip to content

Commit

Permalink
Merge pull request swiftlang#3286 from eeckstein/escape-analysis
Browse files Browse the repository at this point in the history
  • Loading branch information
swift-ci authored Jul 1, 2016
2 parents 8048a13 + dda1749 commit 55b8555
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 7 deletions.
56 changes: 49 additions & 7 deletions lib/SILOptimizer/Analysis/EscapeAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "swift/SILOptimizer/Analysis/ValueTracking.h"
#include "swift/SILOptimizer/PassManager/PassManager.h"
#include "swift/SIL/SILArgument.h"
#include "swift/SIL/DebugUtils.h"
#include "llvm/Support/GraphWriter.h"
#include "llvm/Support/raw_ostream.h"

Expand All @@ -30,13 +31,23 @@ static bool isProjection(ValueBase *V) {
case ValueKind::TupleElementAddrInst:
case ValueKind::UncheckedTakeEnumDataAddrInst:
case ValueKind::StructExtractInst:
case ValueKind::TupleExtractInst:
case ValueKind::UncheckedEnumDataInst:
case ValueKind::MarkDependenceInst:
case ValueKind::PointerToAddressInst:
case ValueKind::AddressToPointerInst:
case ValueKind::InitEnumDataAddrInst:
return true;
case ValueKind::TupleExtractInst: {
auto *TEI = cast<TupleExtractInst>(V);
// Special handling for extracting the pointer-result from an
// array construction. We handle this like a ref_element_addr
// rather than a projection. See the handling of tuple_extract
// in analyzeInstruction().
if (TEI->getFieldNo() == 1 &&
ArraySemanticsCall(TEI->getOperand(), "array.uninitialized", false))
return false;
return true;
}
default:
return false;
}
Expand Down Expand Up @@ -1095,6 +1106,15 @@ void EscapeAnalysis::buildConnectionGraph(FunctionInfo *FInfo,
FInfo->Graph.F->getName() << '\n');
}

/// Returns true if all uses of \p I are tuple_extract instructions.
static bool onlyUsedInTupleExtract(SILInstruction *I) {
for (Operand *Use : getNonDebugUses(I)) {
if (!isa<TupleExtractInst>(Use->getUser()))
return false;
}
return true;
}

void EscapeAnalysis::analyzeInstruction(SILInstruction *I,
FunctionInfo *FInfo,
FunctionOrder &BottomUpOrder,
Expand All @@ -1113,13 +1133,20 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I,
// These array semantics calls do not capture anything.
return;
case ArrayCallKind::kArrayUninitialized:
// array.uninitialized may have a first argument which is the
// allocated array buffer. The call is like a struct(buffer)
// instruction.
if (CGNode *BufferNode = ConGraph->getNode(FAS.getArgument(0), this)) {
ConGraph->defer(ConGraph->getNode(I, this), BufferNode);
// Check if the result is used in the usual way: extracting the
// array and the element pointer with tuple_extract.
if (onlyUsedInTupleExtract(I)) {
// array.uninitialized may have a first argument which is the
// allocated array buffer. The call is like a struct(buffer)
// instruction.
if (CGNode *BufferNode = ConGraph->getNode(FAS.getArgument(0), this)) {
CGNode *ArrayNode = ConGraph->getNode(I, this);
CGNode *ArrayContent = ConGraph->getContentNode(ArrayNode);
ConGraph->defer(ArrayContent, BufferNode);
}
return;
}
return;
break;
case ArrayCallKind::kGetArrayOwner:
if (CGNode *BufferNode = ConGraph->getNode(ASC.getSelf(), this)) {
ConGraph->defer(ConGraph->getNode(I, this), BufferNode);
Expand Down Expand Up @@ -1388,6 +1415,21 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I,
}
return;
}
case ValueKind::TupleExtractInst: {
// This is a tuple_extract which extracts the second result of an
// array.uninitialized call. The first result is the array itself.
// The second result (which is a pointer to the array elements) must be
// the content node of the first result. It's just like a ref_element_addr
// instruction.
auto *TEI = cast<TupleExtractInst>(I);
assert(TEI->getFieldNo() == 1 &&
ArraySemanticsCall(TEI->getOperand(), "array.uninitialized", false)
&& "tuple_extract should be handled as projection");
CGNode *ArrayNode = ConGraph->getNode(TEI->getOperand(), this);
CGNode *ArrayElements = ConGraph->getContentNode(ArrayNode);
ConGraph->setNode(I, ArrayElements);
return;
}
case ValueKind::UncheckedRefCastInst:
case ValueKind::ConvertFunctionInst:
case ValueKind::UpcastInst:
Expand Down
26 changes: 26 additions & 0 deletions test/SILOptimizer/escape_analysis.sil
Original file line number Diff line number Diff line change
Expand Up @@ -1239,6 +1239,28 @@ bb(%0 : $*Array<X>, %1 : $@callee_owned (@inout X) -> (@out (), @error ErrorType
return %r : $()
}

// CHECK-LABEL: CG of arraysemantics_createUninitialized
// CHECK-NEXT: Arg %0 Esc: A, Succ:
// CHECK-NEXT: Val %2 Esc: R, Succ: (%4.2)
// CHECK-NEXT: Val %4 Esc: R, Succ: (%4.1)
// CHECK-NEXT: Con %4.1 Esc: R, Succ: %2
// CHECK-NEXT: Con %4.2 Esc: R, Succ: %0
// CHECK-NEXT: Ret Esc: R, Succ: %4
// CHECK-NEXT: End
sil @arraysemantics_createUninitialized : $@convention(thin) (@owned X) -> @owned Array<X> {
bb0(%0 : $X):
%1 = function_ref @swift_bufferAllocate : $@convention(thin) () -> @owned AnyObject
%2 = apply %1() : $@convention(thin) () -> @owned AnyObject
%3 = function_ref @createUninitialized : $@convention(method) (@owned AnyObject) -> (@owned Array<X>, UnsafeMutablePointer<X>)
%4 = apply %3(%2) : $@convention(method) (@owned AnyObject) -> (@owned Array<X>, UnsafeMutablePointer<X>)
%5 = tuple_extract %4 : $(Array<X>, UnsafeMutablePointer<X>), 0
%6 = tuple_extract %4 : $(Array<X>, UnsafeMutablePointer<X>), 1
%7 = struct_extract %6 : $UnsafeMutablePointer<X>, #UnsafeMutablePointer._rawValue
%8 = pointer_to_address %7 : $Builtin.RawPointer to $*X
store %0 to %8 : $*X
return %5 : $Array<X>
}

sil [_semantics "array.withUnsafeMutableBufferPointer"] @withUnsafeMutableBufferPointer : $@convention(method) (@owned @callee_owned (@inout X) -> (@out (), @error ErrorType), @inout Array<X>) -> (@out (), @error ErrorType)
sil [_semantics "array.props.isNativeTypeChecked"] @is_native_type_checked : $@convention(method) (@guaranteed Array<X>) -> Bool
sil [_semantics "array.check_subscript"] @check_subscript : $@convention(method) (Int32, Bool, @guaranteed Array<X>) -> ()
Expand All @@ -1251,6 +1273,10 @@ sil [_semantics "array.get_capacity"] @get_capacity : $@convention(method) (@gua

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)
sil [_semantics "self_no_escaping_closure"] @unsafeWithNotEscapedSelfPointer: $@convention(method) (@owned @callee_owned (X, X) -> (@out X, @error ErrorType), @guaranteed X) -> (@out X, @error ErrorType)
sil [_semantics "array.uninitialized"] @createUninitialized : $@convention(method) (@owned AnyObject) -> (@owned Array<X>, UnsafeMutablePointer<X>)

// A simplified version of swift_bufferAllocate
sil @swift_bufferAllocate : $@convention(thin) () -> @owned AnyObject

// CHECK-LABEL: CG of semantics_pair_no_escaping_closure
// CHECK-NEXT: Arg %0 Esc: A, Succ:
Expand Down
25 changes: 25 additions & 0 deletions test/SILOptimizer/stack_promotion_escaping.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// RUN: %target-swift-frontend -parse-as-library -O -module-name=test %s -emit-sil | FileCheck %s

final class Item {}

final public class Escaper {
var myItem: Item = Item()

@inline(never)
func update(items: [Item]) {
myItem = items[0]
}

// CHECK-LABEL: sil [noinline] @_TFC4test7Escaper15badStuffHappensfT_T_ : $@convention(method) (@guaranteed Escaper) -> () {
// CHECK: %2 = alloc_ref $Item
// CHECK: function_ref @swift_bufferAllocateOnStack
// CHECK: return
@inline(never)
public func badStuffHappens() {
// Check that 'item' is not stack promoted, because it escapes to myItem.
let item = Item()
// On the other hand, the array buffer of the array literal should be stack promoted.
update(items:[item])
}
}

0 comments on commit 55b8555

Please sign in to comment.