Skip to content

Enable ArrayElementValuePropagation on ownership SIL #34549

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/swift/SILOptimizer/Utils/InstOptUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ NullablePtr<SILInstruction> createIncrementBefore(SILValue ptr,
NullablePtr<SILInstruction> createDecrementBefore(SILValue ptr,
SILInstruction *insertpt);

/// Get the insertion point after \p val.
Optional<SILBasicBlock::iterator> getInsertAfterPoint(SILValue val);

/// A utility for deleting one or more instructions belonging to a function, and
/// cleaning up any dead code resulting from deleting those instructions. Use
/// this utility instead of
Expand Down
43 changes: 24 additions & 19 deletions lib/SILOptimizer/Analysis/ArraySemantic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -701,30 +701,29 @@ bool swift::ArraySemanticsCall::replaceByValue(SILValue V) {
if (!V->getType().isLoadable(*SemanticsCall->getFunction()))
return false;

if (!hasGetElementDirectResult())
return false;

// Expect a check_subscript call or the empty dependence.
auto SubscriptCheck = getSubscriptCheckArgument();
ArraySemanticsCall Check(SubscriptCheck, "array.check_subscript");
auto *EmptyDep = dyn_cast<StructInst>(SubscriptCheck);
if (!Check && (!EmptyDep || !EmptyDep->getElements().empty()))
return false;

SILBuilderWithScope Builder(SemanticsCall);
auto &ValLowering = Builder.getTypeLowering(V->getType());
if (hasGetElementDirectResult()) {
ValLowering.emitCopyValue(Builder, SemanticsCall->getLoc(), V);
SemanticsCall->replaceAllUsesWith(V);
} else {
auto Dest = SemanticsCall->getArgument(0);
// In OSSA, the InsertPt is after V's definition and not before SemanticsCall
// Because we are creating copy_value in ossa, and the source may have been
// taken previously. So our insert point for copy_value is immediately after
// V, where we can be sure it is live.
auto InsertPt = V->getFunction()->hasOwnership()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just this to always do this? Then we have one implementation? I haven't thought through it maybe there is a reason not to.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could, but the retain would be much earlier than when we need, and I was thinking it may effect arc optimizations.

? getInsertAfterPoint(V)
: SemanticsCall->getIterator();
assert(InsertPt.hasValue());

// Expect an alloc_stack initialization.
auto *ASI = dyn_cast<AllocStackInst>(Dest);
if (!ASI)
return false;
SILValue CopiedVal = SILBuilderWithScope(InsertPt.getValue())
.emitCopyValueOperation(SemanticsCall->getLoc(), V);
SemanticsCall->replaceAllUsesWith(CopiedVal);

ValLowering.emitCopyValue(Builder, SemanticsCall->getLoc(), V);
ValLowering.emitStoreOfCopy(Builder, SemanticsCall->getLoc(), V, Dest,
IsInitialization_t::IsInitialization);
}
removeCall();
return true;
}
Expand Down Expand Up @@ -777,9 +776,16 @@ bool swift::ArraySemanticsCall::replaceByAppendingValues(
for (SILValue V : Vals) {
auto SubTy = V->getType();
auto &ValLowering = Builder.getTypeLowering(SubTy);
auto CopiedVal = ValLowering.emitCopyValue(Builder, Loc, V);
// In OSSA, the InsertPt is after V's definition and not before
// SemanticsCall. Because we are creating copy_value in ossa, and the source
// may have been taken previously. So our insert point for copy_value is
// immediately after V, where we can be sure it is live.
auto InsertPt = F->hasOwnership() ? getInsertAfterPoint(V)
: SemanticsCall->getIterator();
assert(InsertPt.hasValue());
SILValue CopiedVal = SILBuilderWithScope(InsertPt.getValue())
.emitCopyValueOperation(V.getLoc(), V);
auto *AllocStackInst = Builder.createAllocStack(Loc, SubTy);

ValLowering.emitStoreOfCopy(Builder, Loc, CopiedVal, AllocStackInst,
IsInitialization_t::IsInitialization);

Expand All @@ -795,8 +801,7 @@ bool swift::ArraySemanticsCall::replaceByAppendingValues(
if (AppendContentsOfFnTy->getParameters()[0].getConvention() ==
ParameterConvention::Direct_Owned) {
SILValue SrcArray = SemanticsCall->getArgument(0);
Builder.createReleaseValue(SemanticsCall->getLoc(), SrcArray,
Builder.getDefaultAtomicity());
Builder.emitDestroyValueOperation(SemanticsCall->getLoc(), SrcArray);
}

removeCall();
Expand Down
23 changes: 15 additions & 8 deletions lib/SILOptimizer/Transforms/ArrayElementValuePropagation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,22 @@ bool ArrayAllocation::recursivelyCollectUses(ValueBase *Def) {
for (auto *Opd : Def->getUses()) {
auto *User = Opd->getUser();
// Ignore reference counting and debug instructions.
if (isa<RefCountingInst>(User) ||
isa<DebugValueInst>(User))
if (isa<RefCountingInst>(User) || isa<DebugValueInst>(User) ||
isa<DestroyValueInst>(User) || isa<EndBorrowInst>(User))
continue;

if (auto *CVI = dyn_cast<CopyValueInst>(User)) {
if (!recursivelyCollectUses(CVI))
return false;
continue;
}

if (auto *BBI = dyn_cast<BeginBorrowInst>(User)) {
if (!recursivelyCollectUses(BBI))
return false;
continue;
}

// Array value projection.
if (auto *SEI = dyn_cast<StructExtractInst>(User)) {
if (!recursivelyCollectUses(SEI))
Expand Down Expand Up @@ -312,11 +324,6 @@ class ArrayElementPropagation : public SILFunctionTransform {

void run() override {
auto &Fn = *getFunction();

// FIXME: Update for ownership.
if (Fn.hasOwnership())
return;

bool Changed = false;

for (auto &BB :Fn) {
Expand All @@ -326,7 +333,7 @@ class ArrayElementPropagation : public SILFunctionTransform {
if (!ALit.analyze(Apply))
continue;

// First optimization: replace getElemente calls.
// First optimization: replace getElement calls.
if (ALit.replaceGetElements()) {
Changed = true;
// Re-do the analysis if the SIL changed.
Expand Down
10 changes: 10 additions & 0 deletions lib/SILOptimizer/Utils/InstOptUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,16 @@ const std::function<void(SingleValueInstruction *, SILValue)>
i->eraseFromParent();
};

Optional<SILBasicBlock::iterator> swift::getInsertAfterPoint(SILValue val) {
if (isa<SingleValueInstruction>(val)) {
return std::next(cast<SingleValueInstruction>(val)->getIterator());
}
if (isa<SILArgument>(val)) {
return cast<SILArgument>(val)->getParentBlock()->begin();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just a quick drive by, but there is an API that already supports this: getDefiningInsertionPt.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm... I see why this is a little different.

}
return None;
}

/// Creates an increment on \p Ptr before insertion point \p InsertPt that
/// creates a strong_retain if \p Ptr has reference semantics itself or a
/// retain_value if \p Ptr is a non-trivial value without reference-semantics.
Expand Down
110 changes: 10 additions & 100 deletions test/SILOptimizer/array_element_propagation.sil
Original file line number Diff line number Diff line change
Expand Up @@ -29,106 +29,13 @@ sil @swift_bufferAllocate : $@convention(thin)() -> @owned AnyObject
sil [_semantics "array.uninitialized"] @adoptStorage : $@convention(thin) (@owned AnyObject, MyInt, @thin MyArray<MyInt>.Type) -> @owned (MyArray<MyInt>, UnsafeMutablePointer<MyInt>)
sil [_semantics "array.props.isNativeTypeChecked"] @hoistableIsNativeTypeChecked : $@convention(method) (@guaranteed MyArray<MyInt>) -> MyBool
sil [_semantics "array.check_subscript"] @checkSubscript : $@convention(method) (MyInt, MyBool, @guaranteed MyArray<MyInt>) -> _MyDependenceToken
sil [_semantics "array.get_element"] @getElement : $@convention(method) (MyInt, MyBool, _MyDependenceToken, @guaranteed MyArray<MyInt>) -> @out MyInt
sil [_semantics "array.get_element"] @getElement2 : $@convention(method) (MyInt, MyBool, _MyDependenceToken, @guaranteed MyArray<MyInt>) -> MyInt
sil [_semantics "array.get_element"] @getElement : $@convention(method) (MyInt, MyBool, _MyDependenceToken, @guaranteed MyArray<MyInt>) -> MyInt
sil @unknown_array_use : $@convention(method) (@guaranteed MyArray<MyInt>) -> MyBool
sil [_semantics "array.uninitialized"] @arrayAdoptStorage : $@convention(thin) (@owned AnyObject, MyInt, @thin Array<MyInt>.Type) -> @owned (Array<MyInt>, UnsafeMutablePointer<MyInt>)
sil @arrayInit : $@convention(method) (@thin Array<MyInt>.Type) -> @owned Array<MyInt>
sil [_semantics "array.finalize_intrinsic"] @finalize : $@convention(thin) (@owned MyArray<MyInt>) -> @owned MyArray<MyInt>
sil [_semantics "array.append_contentsOf"] @arrayAppendContentsOf : $@convention(method) (@owned Array<MyInt>, @inout Array<MyInt>) -> ()

// CHECK-LABEL: sil @propagate01
// CHECK: struct $MyInt
// CHECK: [[V0:%.*]] = integer_literal $Builtin.Int64, 0
// CHECK: [[I0:%.*]] = struct $MyInt ([[V0]] : $Builtin.Int64)
// CHECK: [[V1:%.*]] = integer_literal $Builtin.Int64, 1
// CHECK: [[I1:%.*]] = struct $MyInt ([[V1]] : $Builtin.Int64)
// CHECK: [[V2:%.*]] = integer_literal $Builtin.Int64, 2
// CHECK: [[I2:%.*]] = struct $MyInt ([[V2]] : $Builtin.Int64)
// CHECK: [[S0:%.*]] = alloc_stack $MyInt
// CHECK: [[HFUN:%.*]] = function_ref @hoistableIsNativeTypeChecked
// CHECK-NOT: apply [[HFUN]]
// CHECK: [[CFUN:%.*]] = function_ref @checkSubscript
// CHECK-NOT: apply [[CFUN]]
// CHECK: [[GFUN:%.*]] = function_ref @getElement
// CHECK-NOT: apply [[GFUN]]
// CHECK-NOT: apply [[HFUN]]
// CHECK-NOT: apply [[CFUN]]
// CHECK-NOT: apply [[GFUN]]
// CHECK: store [[I0]] to [[S0]]
// CHECK: [[S1:%.*]] = alloc_stack $MyInt
// CHECK: store [[I1]] to [[S1]]
// CHECK: [[S2:%.*]] = alloc_stack $MyInt
// CHECK: store [[I2]] to [[S2]]
// CHECK: return

sil @propagate01 : $@convention(thin) () -> () {
%0 = function_ref @swift_bufferAllocate : $@convention(thin) () -> @owned AnyObject
%1 = integer_literal $Builtin.Int64, 3
%2 = struct $MyInt (%1 : $Builtin.Int64)
%3 = apply %0() : $@convention(thin) () -> @owned AnyObject
%4 = metatype $@thin MyArray<MyInt>.Type
%5 = function_ref @adoptStorage : $@convention(thin) (@owned AnyObject, MyInt, @thin MyArray<MyInt>.Type) -> @owned (MyArray<MyInt>, UnsafeMutablePointer<MyInt>)
%6 = apply %5(%3, %2, %4) : $@convention(thin) (@owned AnyObject, MyInt, @thin MyArray<MyInt>.Type) -> @owned (MyArray<MyInt>, UnsafeMutablePointer<MyInt>)
%7 = tuple_extract %6 : $(MyArray<MyInt>, UnsafeMutablePointer<MyInt>), 0
%8 = tuple_extract %6 : $(MyArray<MyInt>, UnsafeMutablePointer<MyInt>), 1
debug_value %7 : $MyArray<MyInt>
debug_value %8 : $UnsafeMutablePointer<MyInt>
%9 = struct_extract %8 : $UnsafeMutablePointer<MyInt>, #UnsafeMutablePointer._rawValue
%10 = pointer_to_address %9 : $Builtin.RawPointer to [strict] $*MyInt
%11 = integer_literal $Builtin.Int64, 0
%12 = struct $MyInt (%11 : $Builtin.Int64)
store %12 to %10 : $*MyInt
%13 = integer_literal $Builtin.Word, 1
%14 = index_addr %10 : $*MyInt, %13 : $Builtin.Word
%15 = integer_literal $Builtin.Int64, 1
%16 = struct $MyInt (%15 : $Builtin.Int64)
store %16 to %14 : $*MyInt
%17 = integer_literal $Builtin.Word, 2
%18 = index_addr %10 : $*MyInt, %17 : $Builtin.Word
%19 = integer_literal $Builtin.Int64, 2
%20 = struct $MyInt (%19 : $Builtin.Int64)
store %20 to %18 : $*MyInt
%f = function_ref @finalize : $@convention(thin) (@owned MyArray<MyInt>) -> @owned MyArray<MyInt>
%a = apply %f(%7) : $@convention(thin) (@owned MyArray<MyInt>) -> @owned MyArray<MyInt>
%23 = struct_extract %a : $MyArray<MyInt>, #MyArray._buffer
%24 = struct_extract %23 : $_MyArrayBuffer<MyInt>, #_MyArrayBuffer._storage
%25 = struct_extract %24 : $_MyBridgeStorage, #_MyBridgeStorage.rawValue
%26 = alloc_stack $MyInt
debug_value %a : $MyArray<MyInt>
%27 = function_ref @hoistableIsNativeTypeChecked : $@convention(method) (@guaranteed MyArray<MyInt>) -> MyBool
%28 = apply %27(%a) : $@convention(method) (@guaranteed MyArray<MyInt>) -> MyBool
debug_value %28 : $MyBool // id: %104
%29 = function_ref @checkSubscript : $@convention(method) (MyInt, MyBool, @guaranteed MyArray<MyInt>) -> _MyDependenceToken
%30 = apply %29(%12, %28, %a) : $@convention(method) (MyInt, MyBool, @guaranteed MyArray<MyInt>) -> _MyDependenceToken
debug_value %30 : $_MyDependenceToken
%31 = function_ref @getElement : $@convention(method) (MyInt, MyBool, _MyDependenceToken, @guaranteed MyArray<MyInt>) -> @out MyInt
%32 = apply %31(%26, %12, %28, %30, %a) : $@convention(method) (MyInt, MyBool, _MyDependenceToken, @guaranteed MyArray<MyInt>) -> @out MyInt
%35 = alloc_stack $MyInt
debug_value %16 : $MyInt
debug_value %a : $MyArray<MyInt>
debug_value %28 : $MyBool
strong_retain %25 : $Builtin.BridgeObject
%36 = apply %29(%16, %28, %a) : $@convention(method) (MyInt, MyBool, @guaranteed MyArray<MyInt>) -> _MyDependenceToken
debug_value %36 : $_MyDependenceToken
%37 = apply %31(%35, %16, %28, %36, %a) : $@convention(method) (MyInt, MyBool, _MyDependenceToken, @guaranteed MyArray<MyInt>) -> @out MyInt
strong_release %25 : $Builtin.BridgeObject
%44 = alloc_stack $MyInt
debug_value %a : $MyArray<MyInt>
debug_value %28 : $MyBool
strong_retain %25 : $Builtin.BridgeObject
%45 = apply %29(%20, %28, %a) : $@convention(method) (MyInt, MyBool, @guaranteed MyArray<MyInt>) -> _MyDependenceToken
debug_value %45 : $_MyDependenceToken
%46 = apply %31(%44, %20, %28, %45, %a) : $@convention(method) (MyInt, MyBool, _MyDependenceToken, @guaranteed MyArray<MyInt>) -> @out MyInt
strong_release %25 : $Builtin.BridgeObject
%52 = tuple ()
dealloc_stack %44 : $*MyInt
dealloc_stack %35 : $*MyInt
dealloc_stack %26 : $*MyInt
strong_release %25 : $Builtin.BridgeObject
return %52 : $()
}

// CHECK-LABEL: sil @propagate_with_get_element_returning_direct_result
// CHECK: struct $MyInt
// CHECK: [[V0:%.*]] = integer_literal $Builtin.Int64, 0
Expand Down Expand Up @@ -192,7 +99,7 @@ sil @propagate_with_get_element_returning_direct_result : $@convention(thin) ()
%29 = function_ref @checkSubscript : $@convention(method) (MyInt, MyBool, @guaranteed MyArray<MyInt>) -> _MyDependenceToken
%30 = apply %29(%12, %28, %7) : $@convention(method) (MyInt, MyBool, @guaranteed MyArray<MyInt>) -> _MyDependenceToken
debug_value %30 : $_MyDependenceToken
%31 = function_ref @getElement2 : $@convention(method) (MyInt, MyBool, _MyDependenceToken, @guaranteed MyArray<MyInt>) -> MyInt
%31 = function_ref @getElement : $@convention(method) (MyInt, MyBool, _MyDependenceToken, @guaranteed MyArray<MyInt>) -> MyInt
%32 = apply %31(%12, %28, %30, %7) : $@convention(method) (MyInt, MyBool, _MyDependenceToken, @guaranteed MyArray<MyInt>) -> MyInt
store %32 to %26 : $*MyInt
%35 = alloc_stack $MyInt
Expand Down Expand Up @@ -256,12 +163,14 @@ sil @repeated_initialization : $@convention(thin) () -> () {
%28 = apply %27(%7) : $@convention(method) (@guaranteed MyArray<MyInt>) -> MyBool
%29 = function_ref @checkSubscript : $@convention(method) (MyInt, MyBool, @guaranteed MyArray<MyInt>) -> _MyDependenceToken
%30 = apply %29(%12, %28, %7) : $@convention(method) (MyInt, MyBool, @guaranteed MyArray<MyInt>) -> _MyDependenceToken
%31 = function_ref @getElement : $@convention(method) (MyInt, MyBool, _MyDependenceToken, @guaranteed MyArray<MyInt>) -> @out MyInt
%32 = apply %31(%26, %12, %28, %30, %7) : $@convention(method) (MyInt, MyBool, _MyDependenceToken, @guaranteed MyArray<MyInt>) -> @out MyInt
%31 = function_ref @getElement : $@convention(method) (MyInt, MyBool, _MyDependenceToken, @guaranteed MyArray<MyInt>) -> MyInt
%32 = apply %31(%12, %28, %30, %7) : $@convention(method) (MyInt, MyBool, _MyDependenceToken, @guaranteed MyArray<MyInt>) -> MyInt
store %32 to %26 : $*MyInt
%35 = alloc_stack $MyInt
strong_retain %25 : $Builtin.BridgeObject
%36 = apply %29(%16, %28, %7) : $@convention(method) (MyInt, MyBool, @guaranteed MyArray<MyInt>) -> _MyDependenceToken
%37 = apply %31(%35, %16, %28, %36, %7) : $@convention(method) (MyInt, MyBool, _MyDependenceToken, @guaranteed MyArray<MyInt>) -> @out MyInt
%37 = apply %31(%16, %28, %36, %7) : $@convention(method) (MyInt, MyBool, _MyDependenceToken, @guaranteed MyArray<MyInt>) -> MyInt
store %37 to %35 : $*MyInt
strong_release %25 : $Builtin.BridgeObject
%52 = tuple ()
dealloc_stack %35 : $*MyInt
Expand Down Expand Up @@ -298,8 +207,9 @@ sil @unknown_use : $@convention(thin) () -> () {
%28 = apply %27(%7) : $@convention(method) (@guaranteed MyArray<MyInt>) -> MyBool
%29 = function_ref @checkSubscript : $@convention(method) (MyInt, MyBool, @guaranteed MyArray<MyInt>) -> _MyDependenceToken
%30 = apply %29(%12, %28, %7) : $@convention(method) (MyInt, MyBool, @guaranteed MyArray<MyInt>) -> _MyDependenceToken
%31 = function_ref @getElement : $@convention(method) (MyInt, MyBool, _MyDependenceToken, @guaranteed MyArray<MyInt>) -> @out MyInt
%32 = apply %31(%26, %12, %28, %30, %7) : $@convention(method) (MyInt, MyBool, _MyDependenceToken, @guaranteed MyArray<MyInt>) -> @out MyInt
%31 = function_ref @getElement : $@convention(method) (MyInt, MyBool, _MyDependenceToken, @guaranteed MyArray<MyInt>) -> MyInt
%32 = apply %31(%12, %28, %30, %7) : $@convention(method) (MyInt, MyBool, _MyDependenceToken, @guaranteed MyArray<MyInt>) -> MyInt
store %32 to %26 : $*MyInt
%33 = function_ref @unknown_array_use : $@convention(method) (@guaranteed MyArray<MyInt>) -> MyBool
%34 = apply %33(%7) : $@convention(method) (@guaranteed MyArray<MyInt>) -> MyBool
%52 = tuple ()
Expand Down
Loading