Skip to content

Commit 5dbd34d

Browse files
committed
[semantic-arc] Prevent future pointer invalidation issues in the OwnedToGuaranteedPhiOp transform.
The specific problem here is that I am going to be adding some code to SemanticARCOpts that eliminates reborrows and may need to create new phi arguments and thus add arguments to edges. The weird thing about this is that doing so actually requires us to create a new terminator! This means that subtle pointer invalidation issues can occur here. To work around that we store our terminators as SILBasicBlock, operand number since we can always immediately find a terminator from its basic block. If we do not have a terminator, we keep on just storing the SILInstruction itself. NOTE: This only saves us from additive changes. Deletions are still an issue.
1 parent b13a8e9 commit 5dbd34d

File tree

4 files changed

+63
-7
lines changed

4 files changed

+63
-7
lines changed

lib/SILOptimizer/SemanticARC/Context.h

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,44 @@ struct LLVM_LIBRARY_VISIBILITY Context {
6464
/// our LiveRange can not see through joined live ranges, we know that we
6565
/// should only be able to have a single owned value introducer for each
6666
/// consumed operand.
67-
FrozenMultiMap<SILValue, Operand *> joinedOwnedIntroducerToConsumedOperands;
67+
///
68+
/// NOTE: To work around potential invalidation of our consuming operands when
69+
/// adding values to edges on the CFG, we store our Operands as a
70+
/// SILBasicBlock and an operand number. We only add values to edges and never
71+
/// remove/modify edges so the operand number should be safe.
72+
struct ConsumingOperandState {
73+
PointerUnion<SILBasicBlock *, SILInstruction *> parent;
74+
unsigned operandNumber;
75+
76+
ConsumingOperandState() : parent(nullptr), operandNumber(UINT_MAX) {}
77+
78+
ConsumingOperandState(Operand *op)
79+
: parent(), operandNumber(op->getOperandNumber()) {
80+
if (auto *ti = dyn_cast<TermInst>(op->getUser())) {
81+
parent = ti->getParent();
82+
} else {
83+
parent = op->getUser();
84+
}
85+
}
86+
87+
ConsumingOperandState(const ConsumingOperandState &other) :
88+
parent(other.parent), operandNumber(other.operandNumber) {}
89+
90+
ConsumingOperandState &operator=(const ConsumingOperandState &other) {
91+
parent = other.parent;
92+
operandNumber = other.operandNumber;
93+
return *this;
94+
}
95+
96+
~ConsumingOperandState() = default;
97+
98+
operator bool() const {
99+
return bool(parent) && operandNumber != UINT_MAX;
100+
}
101+
};
102+
103+
FrozenMultiMap<SILValue, ConsumingOperandState>
104+
joinedOwnedIntroducerToConsumedOperands;
68105

69106
/// If set to true, then we should only run cheap optimizations that do not
70107
/// build up data structures or analyze code in depth.

lib/SILOptimizer/SemanticARC/CopyValueOpts.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,9 +224,9 @@ bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization(
224224
});
225225

226226
if (canOptimizePhi) {
227+
Context::ConsumingOperandState state(opPhi);
227228
opPhi.visitResults([&](SILValue value) {
228-
ctx.joinedOwnedIntroducerToConsumedOperands.insert(value,
229-
opPhi.getOperand());
229+
ctx.joinedOwnedIntroducerToConsumedOperands.insert(value, state);
230230
return true;
231231
});
232232
}

lib/SILOptimizer/SemanticARC/OwnedToGuaranteedPhiOpt.cpp

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,18 @@
1919
#include "Context.h"
2020
#include "OwnershipPhiOperand.h"
2121
#include "Transforms.h"
22+
#include "swift/Basic/STLExtras.h"
2223

2324
using namespace swift;
2425
using namespace swift::semanticarc;
2526

27+
namespace {
28+
using ConsumingOperandState = Context::ConsumingOperandState;
29+
} // anonymous namespace
30+
31+
template <typename OperandRangeTy>
2632
static bool canEliminatePhi(
27-
Context::FrozenMultiMapRange optimizableIntroducerRange,
33+
OperandRangeTy optimizableIntroducerRange,
2834
ArrayRef<OwnershipPhiOperand> incomingValueOperandList,
2935
SmallVectorImpl<OwnedValueIntroducer> &ownedValueIntroducerAccumulator) {
3036
for (auto incomingValueOperand : incomingValueOperandList) {
@@ -161,9 +167,19 @@ bool swift::semanticarc::tryConvertOwnedPhisToGuaranteedPhis(Context &ctx) {
161167
// eliminated if it was not for the given phi. If all of them are, we can
162168
// optimize!
163169
{
164-
auto rawFoundOptimizableIntroducerArray = pair.second;
165-
if (!canEliminatePhi(rawFoundOptimizableIntroducerArray,
166-
incomingValueOperandList, ownedValueIntroducers)) {
170+
std::function<Operand *(const Context::ConsumingOperandState)> lambda =
171+
[&](const Context::ConsumingOperandState &state) -> Operand * {
172+
unsigned opNum = state.operandNumber;
173+
if (state.parent.is<SILBasicBlock *>()) {
174+
SILBasicBlock *block = state.parent.get<SILBasicBlock *>();
175+
return &block->getTerminator()->getAllOperands()[opNum];
176+
}
177+
SILInstruction *inst = state.parent.get<SILInstruction *>();
178+
return &inst->getAllOperands()[opNum];
179+
};
180+
auto operandsTransformed = makeTransformRange(pair.second, lambda);
181+
if (!canEliminatePhi(operandsTransformed, incomingValueOperandList,
182+
ownedValueIntroducers)) {
167183
continue;
168184
}
169185
}

lib/SILOptimizer/SemanticARC/OwnershipPhiOperand.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ class LLVM_LIBRARY_VISIBILITY OwnershipPhiOperand {
6464
}
6565
}
6666

67+
operator const Operand *() const { return op; }
68+
operator Operand *() { return op; }
69+
6770
Operand *getOperand() const { return op; }
6871
SILValue getValue() const { return op->get(); }
6972
SILType getType() const { return op->get()->getType(); }

0 commit comments

Comments
 (0)