Skip to content

Commit 259d2bb

Browse files
committed
[ownership] Commit a generic replaceAllUsesAndEraseFixingOwnership api and enable SimplifyInstruction on OSSA.
This is a generic API that when ownership is enabled allows one to replace all uses of a value with a value with a differing ownership by transforming/lifetime extending as appropriate. This API supports all pairings of ownership /except/ replacing a value with OwnershipKind::None with a value without OwnershipKind::None. This is a more complex optimization that we do not support today. As a result, we include on our state struct a helper routine that callers can use to know if the two values that they want to process can be handled by the algorithm. My moticiation is to use this to to update InstSimplify and SILCombiner in a less bug prone way rather than just turn stuff off. Noting that this transformation inserts ownership instructions, I have made sure to test this API in two ways: 1. With Mandatory Combiner alone (to make sure it works period). 2. With Mandatory Combiner + Semantic ARC Opts to make sure that we can eliminate the extra ownership instructions it inserts. As one can see from the tests, the optimizer today is able to handle all of these transforms except one conditional case where I need to eliminate a dead phi arg. I have a separate branch that hits that today but I have exposed unsafe behavior in ClosureLifetimeFixup that I need to fix first before I can land that. I don't want that to stop this PR since I think the current low level ARC optimizer may be able to help me here since this is a simple transform it does all of the time.
1 parent 6bd4a8e commit 259d2bb

19 files changed

+1733
-86
lines changed

include/swift/SIL/BasicBlockUtils.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ class DeadEndBlocks {
8989
}
9090
return ReachableBlocks.empty();
9191
}
92+
93+
const SILFunction *getFunction() const { return F; }
9294
};
9395

9496
/// A struct that contains the intermediate state used in computing

include/swift/SIL/OwnershipUtils.h

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,15 @@ struct BorrowingOperand {
170170
return *this;
171171
}
172172

173+
// A set of operators so that a BorrowingOperand can be used like a normal
174+
// operand in a light weight way.
175+
operator const Operand *() const { return op; }
176+
operator Operand *() { return op; }
177+
const Operand *operator*() const { return op; }
178+
Operand *operator*() { return op; }
179+
const Operand *operator->() const { return op; }
180+
Operand *operator->() { return op; }
181+
173182
/// If \p op is a borrow introducing operand return it after doing some
174183
/// checks.
175184
static Optional<BorrowingOperand> get(Operand *op) {
@@ -425,7 +434,7 @@ struct BorrowedValue {
425434
///
426435
/// NOTE: Scratch space is used internally to this method to store the end
427436
/// borrow scopes if needed.
428-
bool areUsesWithinScope(ArrayRef<Operand *> instructions,
437+
bool areUsesWithinScope(ArrayRef<Operand *> uses,
429438
SmallVectorImpl<Operand *> &scratchSpace,
430439
SmallPtrSetImpl<SILBasicBlock *> &visitedBlocks,
431440
DeadEndBlocks &deadEndBlocks) const;
@@ -447,6 +456,24 @@ struct BorrowedValue {
447456
bool visitInteriorPointerOperands(
448457
function_ref<void(const InteriorPointerOperand &)> func) const;
449458

459+
/// Visit all immediate uses of this borrowed value and if any of them are
460+
/// reborrows, place them in BorrowingOperand form into \p
461+
/// foundReborrows. Returns true if we appended any such reborrows to
462+
/// foundReborrows... false otherwise.
463+
bool
464+
gatherReborrows(SmallVectorImpl<BorrowingOperand> &foundReborrows) const {
465+
bool foundAnyReborrows = false;
466+
for (auto *op : value->getUses()) {
467+
if (auto borrowingOperand = BorrowingOperand::get(op)) {
468+
if (borrowingOperand->isReborrow()) {
469+
foundReborrows.push_back(*borrowingOperand);
470+
foundAnyReborrows = true;
471+
}
472+
}
473+
}
474+
return foundAnyReborrows;
475+
}
476+
450477
private:
451478
/// Internal constructor for failable static constructor. Please do not expand
452479
/// its usage since it assumes the code passed in is well formed.

include/swift/SILOptimizer/Analysis/SimplifyInstruction.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ class SILInstruction;
3030
/// analysis of the operands of the instruction, without looking at its uses
3131
/// (e.g. constant folding). If a simpler result can be found, it is
3232
/// returned, otherwise a null SILValue is returned.
33+
///
34+
/// This is assumed to implement read-none transformations.
3335
SILValue simplifyInstruction(SILInstruction *I);
3436

3537
/// Replace an instruction with a simplified result and erase it. If the
@@ -38,9 +40,17 @@ SILValue simplifyInstruction(SILInstruction *I);
3840
///
3941
/// If it is nonnull, eraseNotify will be called before each instruction is
4042
/// deleted.
43+
///
44+
/// If it is nonnull and inst is in OSSA, newInstNotify will be called with each
45+
/// new instruction inserted to compensate for ownership.
46+
///
47+
/// NOTE: When OSSA is enabled this API assumes OSSA is properly formed and will
48+
/// insert compensating instructions.
4149
SILBasicBlock::iterator replaceAllSimplifiedUsesAndErase(
4250
SILInstruction *I, SILValue result,
43-
std::function<void(SILInstruction *)> eraseNotify = nullptr);
51+
std::function<void(SILInstruction *)> eraseNotify = nullptr,
52+
std::function<void(SILInstruction *)> newInstNotify = nullptr,
53+
DeadEndBlocks *deadEndBlocks = nullptr);
4454

4555
/// Simplify invocations of builtin operations that may overflow.
4656
/// All such operations return a tuple (result, overflow_flag).

include/swift/SILOptimizer/Utils/CanonicalizeInstruction.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#ifndef SWIFT_SILOPTIMIZER_UTILS_CANONICALIZEINSTRUCTION_H
2727
#define SWIFT_SILOPTIMIZER_UTILS_CANONICALIZEINSTRUCTION_H
2828

29+
#include "swift/SIL/BasicBlockUtils.h"
2930
#include "swift/SIL/SILBasicBlock.h"
3031
#include "swift/SIL/SILInstruction.h"
3132
#include "llvm/Support/Debug.h"
@@ -38,8 +39,11 @@ struct CanonicalizeInstruction {
3839
// May be overriden by passes.
3940
static constexpr const char *defaultDebugType = "sil-canonicalize";
4041
const char *debugType = defaultDebugType;
42+
DeadEndBlocks &deadEndBlocks;
4143

42-
CanonicalizeInstruction(const char *passDebugType) {
44+
CanonicalizeInstruction(const char *passDebugType,
45+
DeadEndBlocks &deadEndBlocks)
46+
: deadEndBlocks(deadEndBlocks) {
4347
#ifndef NDEBUG
4448
if (llvm::DebugFlag && !llvm::isCurrentDebugType(debugType))
4549
debugType = passDebugType;
@@ -48,6 +52,8 @@ struct CanonicalizeInstruction {
4852

4953
virtual ~CanonicalizeInstruction();
5054

55+
const SILFunction *getFunction() const { return deadEndBlocks.getFunction(); }
56+
5157
/// Rewrite this instruction, based on its operands and uses, into a more
5258
/// canonical representation.
5359
///

include/swift/SILOptimizer/Utils/InstOptUtils.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,13 @@ namespace swift {
3535
class DominanceInfo;
3636
template <class T> class NullablePtr;
3737

38-
/// Transform a Use Range (Operand*) into a User Range (SILInstruction*)
38+
/// Transform a Use Range (Operand*) into a User Range (SILInstruction *)
3939
using UserTransform = std::function<SILInstruction *(Operand *)>;
4040
using ValueBaseUserRange =
4141
TransformRange<iterator_range<ValueBase::use_iterator>, UserTransform>;
4242

43-
inline ValueBaseUserRange
44-
makeUserRange(iterator_range<ValueBase::use_iterator> range) {
43+
template <typename Range>
44+
inline TransformRange<Range, UserTransform> makeUserRange(Range range) {
4545
auto toUser = [](Operand *operand) { return operand->getUser(); };
4646
return makeTransformRange(range, UserTransform(toUser));
4747
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//===--- OwnershipOptUtils.h ----------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2020 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+
///
13+
/// \file
14+
///
15+
/// Ownership Utilities that rely on SILOptimizer functionality.
16+
///
17+
//===----------------------------------------------------------------------===//
18+
19+
#ifndef SWIFT_SILOPTIMIZER_UTILS_OWNERSHIPOPTUTILS_H
20+
#define SWIFT_SILOPTIMIZER_UTILS_OWNERSHIPOPTUTILS_H
21+
22+
#include "swift/SIL/OwnershipUtils.h"
23+
#include "swift/SIL/SILModule.h"
24+
25+
namespace swift {
26+
27+
// Defined in BasicBlockUtils.h
28+
struct JointPostDominanceSetComputer;
29+
30+
struct OwnershipFixupContext {
31+
std::function<void(SILInstruction *)> eraseNotify;
32+
std::function<void(SILInstruction *)> newInstNotify;
33+
DeadEndBlocks &deBlocks;
34+
JointPostDominanceSetComputer &jointPostDomSetComputer;
35+
36+
SILBasicBlock::iterator
37+
replaceAllUsesAndEraseFixingOwnership(SingleValueInstruction *oldValue,
38+
SILValue newValue);
39+
40+
/// We can not RAUW all old values with new values.
41+
///
42+
/// Namely, we do not support RAUWing values with ValueOwnershipKind::None
43+
/// that have uses that do not require ValueOwnershipKind::None or
44+
/// ValueOwnershipKind::Any.
45+
static bool canFixUpOwnershipForRAUW(SingleValueInstruction *oldValue,
46+
SILValue newValue);
47+
};
48+
49+
} // namespace swift
50+
51+
#endif

lib/SIL/Utils/BasicBlockUtils.cpp

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -387,20 +387,22 @@ void DeadEndBlocks::compute() {
387387

388388
void JointPostDominanceSetComputer::findJointPostDominatingSet(
389389
SILBasicBlock *dominatingBlock, ArrayRef<SILBasicBlock *> dominatedBlockSet,
390-
function_ref<void(SILBasicBlock *)> foundInputBlocksNotInJointPostDomSet,
390+
function_ref<void(SILBasicBlock *)> inputBlocksFoundDuringWalk,
391391
function_ref<void(SILBasicBlock *)> foundJointPostDomSetCompletionBlocks,
392-
function_ref<void(SILBasicBlock *)> foundInputBlocksInJointPostDomSet) {
392+
function_ref<void(SILBasicBlock *)> inputBlocksInJointPostDomSet) {
393393
// If our reachable block set is empty, assert. This is most likely programmer
394394
// error.
395395
assert(dominatedBlockSet.size() != 0);
396396

397397
// If we have a reachable block set with a single block and that block is
398398
// dominatingBlock, then we return success since a block post-doms its self so
399399
// it is already complete.
400+
//
401+
// NOTE: We do not consider this a visiteed
400402
if (dominatedBlockSet.size() == 1) {
401403
if (dominatingBlock == dominatedBlockSet[0]) {
402-
if (foundInputBlocksInJointPostDomSet)
403-
foundInputBlocksInJointPostDomSet(dominatingBlock);
404+
if (inputBlocksInJointPostDomSet)
405+
inputBlocksInJointPostDomSet(dominatingBlock);
404406
return;
405407
}
406408
}
@@ -475,16 +477,16 @@ void JointPostDominanceSetComputer::findJointPostDominatingSet(
475477
// callback.
476478
sortUnique(reachableInputBlocks);
477479
for (auto *block : reachableInputBlocks)
478-
foundInputBlocksNotInJointPostDomSet(block);
480+
inputBlocksFoundDuringWalk(block);
479481

480482
// Then if were asked to find the subset of our input blocks that are in the
481483
// joint-postdominance set, compute that.
482-
if (!foundInputBlocksInJointPostDomSet)
484+
if (!inputBlocksInJointPostDomSet)
483485
return;
484486

485487
// Pass back the reachable input blocks that were not reachable from other
486488
// input blocks to.
487489
for (auto *block : dominatedBlockSet)
488490
if (lower_bound(reachableInputBlocks, block) == reachableInputBlocks.end())
489-
foundInputBlocksInJointPostDomSet(block);
491+
inputBlocksInJointPostDomSet(block);
490492
}

lib/SIL/Utils/OwnershipUtils.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,9 +221,17 @@ bool BorrowingOperand::visitLocalEndScopeUses(
221221
case BorrowingOperandKind::TryApply:
222222
case BorrowingOperandKind::Yield:
223223
return true;
224-
case BorrowingOperandKind::Branch:
224+
case BorrowingOperandKind::Branch: {
225+
auto *br = cast<BranchInst>(op->getUser());
226+
for (auto *use : br->getArgForOperand(op)->getUses())
227+
if (use->isLifetimeEnding())
228+
if (!func(use))
229+
return false;
225230
return true;
226231
}
232+
}
233+
234+
llvm_unreachable("Covered switch isn't covered");
227235
}
228236

229237
void BorrowingOperand::visitBorrowIntroducingUserResults(

0 commit comments

Comments
 (0)