Skip to content

Commit 20e262b

Browse files
Merge pull request #64190 from nate-chandler/redundant-move-elim
[SILOptimizer] Add RedundantMoveValueElimination.
2 parents b7a2fa2 + 5f561ed commit 20e262b

File tree

10 files changed

+401
-2
lines changed

10 files changed

+401
-2
lines changed

include/swift/SIL/OwnershipUtils.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,12 @@ inline bool isForwardingConsume(SILValue value) {
122122

123123
bool hasPointerEscape(BorrowedValue value);
124124

125+
/// Whether the specified OSSA-lifetime introducer has a pointer escape.
126+
///
127+
/// precondition: \p value introduces an OSSA-lifetime, either a BorrowedValue
128+
/// can be constructed from it or it's an owned value
129+
bool hasPointerEscape(SILValue value);
130+
125131
/// Find leaf "use points" of \p guaranteedValue that determine its lifetime
126132
/// requirement. Return true if no PointerEscape use was found.
127133
///

lib/SIL/Utils/OwnershipUtils.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,44 @@ bool swift::hasPointerEscape(BorrowedValue value) {
8383
return false;
8484
}
8585

86+
bool swift::hasPointerEscape(SILValue original) {
87+
if (auto borrowedValue = BorrowedValue(original)) {
88+
return hasPointerEscape(borrowedValue);
89+
}
90+
assert(original->getOwnershipKind() == OwnershipKind::Owned);
91+
92+
ValueWorklist worklist(original->getFunction());
93+
worklist.push(original);
94+
if (auto *phi = SILArgument::asPhi(original)) {
95+
phi->visitTransitiveIncomingPhiOperands([&](auto *phi, auto *operand) {
96+
worklist.pushIfNotVisited(operand->get());
97+
return true;
98+
});
99+
}
100+
while (auto value = worklist.popAndForget()) {
101+
for (auto use : value->getUses()) {
102+
switch (use->getOperandOwnership()) {
103+
case OperandOwnership::PointerEscape:
104+
case OperandOwnership::ForwardingUnowned:
105+
return true;
106+
case OperandOwnership::ForwardingConsume: {
107+
auto *branch = dyn_cast<BranchInst>(use->getUser());
108+
if (!branch) {
109+
// Non-phi forwarding consumes end the lifetime of an owned value.
110+
break;
111+
}
112+
auto *phi = branch->getDestBB()->getArgument(use->getOperandNumber());
113+
worklist.pushIfNotVisited(phi);
114+
break;
115+
}
116+
default:
117+
break;
118+
}
119+
}
120+
}
121+
return false;
122+
}
123+
86124
bool swift::canOpcodeForwardInnerGuaranteedValues(SILValue value) {
87125
// If we have an argument from a transforming terminator, we can forward
88126
// guaranteed.

lib/SILOptimizer/SemanticARC/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ target_sources(swiftSILOptimizer PRIVATE
88
Context.cpp
99
SemanticARCOptVisitor.cpp
1010
OwnershipConversionElimination.cpp
11+
RedundantMoveValueElimination.cpp
1112
)
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//===--- RedundantMoveValueElimination.cpp - Delete spurious move_values --===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
// A move_value ends an owned lifetime and begins an owned lifetime.
13+
//
14+
// The new lifetime may have the same characteristics as the original lifetime
15+
// with regards to
16+
// - lexicality
17+
// - escaping
18+
//
19+
// If it has the same characteristics, there is no reason to have two separate
20+
// lifetimes--they are redundant. This optimization deletes such redundant
21+
// move_values.
22+
//===----------------------------------------------------------------------===//
23+
24+
#include "SemanticARC/SemanticARCOpts.h"
25+
#include "SemanticARCOptVisitor.h"
26+
#include "swift/SIL/LinearLifetimeChecker.h"
27+
28+
using namespace swift;
29+
using namespace semanticarc;
30+
31+
//===----------------------------------------------------------------------===//
32+
// Top Level Entrypoint
33+
//===----------------------------------------------------------------------===//
34+
35+
bool SemanticARCOptVisitor::visitMoveValueInst(MoveValueInst *mvi) {
36+
if (ctx.onlyMandatoryOpts)
37+
return false;
38+
39+
if (!ctx.shouldPerform(ARCTransformKind::RedundantMoveValueElim))
40+
return false;
41+
42+
auto original = mvi->getOperand();
43+
44+
// If the moved-from value has none ownership, hasPointerEscape can't handle
45+
// it, so it can't be used to determine whether escaping matches.
46+
if (original->getOwnershipKind() != OwnershipKind::Owned) {
47+
return false;
48+
}
49+
50+
// First, check whether lexicality matches, the cheaper check.
51+
if (mvi->isLexical() != original->isLexical()) {
52+
return false;
53+
}
54+
55+
// Then, check whether escaping matches, the more expensive check.
56+
if (hasPointerEscape(mvi) != hasPointerEscape(original)) {
57+
return false;
58+
}
59+
60+
// Both characteristics match.
61+
eraseAndRAUWSingleValueInstruction(mvi, original);
62+
return true;
63+
}

lib/SILOptimizer/SemanticARC/SemanticARCOptVisitor.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ struct LLVM_LIBRARY_VISIBILITY SemanticARCOptVisitor
143143
bool visitCopyValueInst(CopyValueInst *cvi);
144144
bool visitBeginBorrowInst(BeginBorrowInst *bbi);
145145
bool visitLoadInst(LoadInst *li);
146+
bool visitMoveValueInst(MoveValueInst *mvi);
146147
bool
147148
visitUncheckedOwnershipConversionInst(UncheckedOwnershipConversionInst *uoci);
148149

@@ -153,6 +154,7 @@ struct LLVM_LIBRARY_VISIBILITY SemanticARCOptVisitor
153154
case SILInstructionKind::CopyValueInst:
154155
case SILInstructionKind::BeginBorrowInst:
155156
case SILInstructionKind::LoadInst:
157+
case SILInstructionKind::MoveValueInst:
156158
case SILInstructionKind::UncheckedOwnershipConversionInst:
157159
return true;
158160
}

lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,11 @@ static llvm::cl::list<ARCTransformKind> TransformsToPerform(
5151
"sil-semantic-arc-owned-to-guaranteed-phi",
5252
"Perform Owned To Guaranteed Phi. NOTE: Seeded by peephole "
5353
"optimizer for compile time saving purposes, so run this "
54-
"after running peepholes)")),
54+
"after running peepholes)"),
55+
clEnumValN(ARCTransformKind::RedundantMoveValueElim,
56+
"sil-semantic-arc-redundant-move-value-elim",
57+
"Eliminate move_value which don't change owned lifetime "
58+
"characteristics. (Escaping, Lexical).")),
5559
llvm::cl::desc(
5660
"For testing purposes only run the specified list of semantic arc "
5761
"optimization. If the list is empty, we run all transforms"));
@@ -86,6 +90,7 @@ struct SemanticARCOpts : SILFunctionTransform {
8690
case ARCTransformKind::LoadCopyToLoadBorrowPeephole:
8791
case ARCTransformKind::AllPeepholes:
8892
case ARCTransformKind::OwnershipConversionElimPeephole:
93+
case ARCTransformKind::RedundantMoveValueElim:
8994
// We never assume we are at fixed point when running these transforms.
9095
if (performPeepholesWithoutFixedPoint(visitor)) {
9196
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);

lib/SILOptimizer/SemanticARC/SemanticARCOpts.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,13 @@ enum class ARCTransformKind : uint64_t {
3131
RedundantCopyValueElimPeephole = 0x8,
3232
LifetimeJoiningPeephole = 0x10,
3333
OwnershipConversionElimPeephole = 0x20,
34+
RedundantMoveValueElim = 0x40,
3435

3536
AllPeepholes = LoadCopyToLoadBorrowPeephole |
3637
RedundantBorrowScopeElimPeephole |
3738
RedundantCopyValueElimPeephole | LifetimeJoiningPeephole |
3839
OwnershipConversionElimPeephole,
39-
All = AllPeepholes | OwnedToGuaranteedPhi,
40+
All = AllPeepholes | OwnedToGuaranteedPhi | RedundantMoveValueElim,
4041
};
4142

4243
inline ARCTransformKind operator&(ARCTransformKind lhs, ARCTransformKind rhs) {

lib/SILOptimizer/UtilityPasses/UnitTestRunner.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
#include "swift/SIL/MemAccessUtils.h"
7272
#include "swift/SIL/OSSALifetimeCompletion.h"
7373
#include "swift/SIL/OwnershipLiveness.h"
74+
#include "swift/SIL/OwnershipUtils.h"
7475
#include "swift/SIL/PrunedLiveness.h"
7576
#include "swift/SIL/SILArgumentArrayRef.h"
7677
#include "swift/SIL/SILBasicBlock.h"
@@ -267,6 +268,22 @@ struct TestSpecificationTest : UnitTest {
267268
}
268269
};
269270

271+
// Arguments:
272+
// - value: the value to check for escaping
273+
// Dumps:
274+
// - the value
275+
// - whether it has a pointer escape
276+
struct OwnershipUtilsHasPointerEscape : UnitTest {
277+
OwnershipUtilsHasPointerEscape(UnitTestRunner *pass) : UnitTest(pass) {}
278+
void invoke(Arguments &arguments) override {
279+
auto value = arguments.takeValue();
280+
auto has = hasPointerEscape(value);
281+
value->print(llvm::errs());
282+
auto *boolString = has ? "true" : "false";
283+
llvm::errs() << boolString << "\n";
284+
}
285+
};
286+
270287
//===----------------------------------------------------------------------===//
271288
// MARK: OSSA Lifetime Unit Tests
272289
//===----------------------------------------------------------------------===//
@@ -789,6 +806,7 @@ void UnitTestRunner::withTest(StringRef name, Doit doit) {
789806
ADD_UNIT_TEST_SUBCLASS("find-borrow-introducers", FindBorrowIntroducers)
790807
ADD_UNIT_TEST_SUBCLASS("find-enclosing-defs", FindEnclosingDefsTest)
791808
ADD_UNIT_TEST_SUBCLASS("function-get-self-argument-index", FunctionGetSelfArgumentIndex)
809+
ADD_UNIT_TEST_SUBCLASS("has-pointer-escape", OwnershipUtilsHasPointerEscape)
792810
ADD_UNIT_TEST_SUBCLASS("interior-liveness", InteriorLivenessTest)
793811
ADD_UNIT_TEST_SUBCLASS("is-deinit-barrier", IsDeinitBarrierTest)
794812
ADD_UNIT_TEST_SUBCLASS("is-lexical", IsLexicalTest)
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// RUN: %target-sil-opt -unit-test-runner %s -o /dev/null 2>&1 | %FileCheck %s
2+
3+
sil_stage canonical
4+
5+
import Builtin
6+
7+
class C {}
8+
9+
sil @getOwned : $@convention(thin) () -> (@owned C)
10+
11+
sil @borrow : $@convention(thin) (@guaranteed C) -> ()
12+
13+
sil @useUnmanaged : $@convention(thin) (@sil_unmanaged C) -> ()
14+
15+
sil [ossa] @test_escape_of_phi_transitive_incoming_value : $@convention(thin) () -> () {
16+
entry:
17+
%getOwned = function_ref @getOwned : $@convention(thin) () -> (@owned C)
18+
%useUnmanaged = function_ref @useUnmanaged : $@convention(thin) (@sil_unmanaged C) -> ()
19+
cond_br undef, left, right
20+
left:
21+
cond_br undef, left_left, left_right
22+
left_left:
23+
%llinstance = apply %getOwned() : $@convention(thin) () -> (@owned C)
24+
br left_bottom(%llinstance : $C)
25+
left_right:
26+
%lrinstance = apply %getOwned() : $@convention(thin) () -> (@owned C)
27+
%escape = ref_to_unmanaged %lrinstance : $C to $@sil_unmanaged C
28+
apply %useUnmanaged(%escape) : $@convention(thin) (@sil_unmanaged C) -> ()
29+
br left_bottom(%lrinstance : $C)
30+
left_bottom(%linstance : @owned $C):
31+
br exit(%linstance : $C)
32+
right:
33+
%rinstance = apply %getOwned() : $@convention(thin) () -> (@owned C)
34+
br exit(%rinstance : $C)
35+
exit(%instance : @owned $C):
36+
// CHECK-LABEL: begin running test {{[0-9]+}} of {{[0-9]+}} on test_escape_of_phi_transitive_incoming_value: has-pointer-escape
37+
// CHECK: %14 = argument of bb6 : $C
38+
// CHECK: true
39+
// CHECK-LABEL: end running test {{[0-9]+}} of {{[0-9]+}} on test_escape_of_phi_transitive_incoming_value: has-pointer-escape with
40+
test_specification "has-pointer-escape @block.argument"
41+
destroy_value %instance : $C
42+
%retval = tuple ()
43+
return %retval : $()
44+
}

0 commit comments

Comments
 (0)