mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
EscapeAnalysis: some new and changed utility functions to be used by alias analysis and ARC analysis.
This commit is contained in:
@@ -286,8 +286,16 @@ private:
|
|||||||
/// the node's value.
|
/// the node's value.
|
||||||
/// Note that in the false-case the node's value can still escape via
|
/// Note that in the false-case the node's value can still escape via
|
||||||
/// the return instruction.
|
/// the return instruction.
|
||||||
bool escapesInsideFunction() const {
|
bool escapesInsideFunction(bool isNotAliasingArgument) const {
|
||||||
return getEscapeState() > EscapeState::Return;
|
switch (getEscapeState()) {
|
||||||
|
case EscapeState::None:
|
||||||
|
case EscapeState::Return:
|
||||||
|
return false;
|
||||||
|
case EscapeState::Arguments:
|
||||||
|
return !isNotAliasingArgument;
|
||||||
|
case EscapeState::Global:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -423,7 +431,12 @@ public:
|
|||||||
/// taken. This means the node is always created for the "outermost" value
|
/// taken. This means the node is always created for the "outermost" value
|
||||||
/// where V is contained.
|
/// where V is contained.
|
||||||
/// Returns null, if V is not a "pointer".
|
/// Returns null, if V is not a "pointer".
|
||||||
CGNode *getOrCreateNode(ValueBase *V);
|
CGNode *getNode(ValueBase *V, EscapeAnalysis *EA, bool createIfNeeded = true);
|
||||||
|
|
||||||
|
/// Gets or creates a node for a SILValue (same as above).
|
||||||
|
CGNode *getNode(SILValue V, EscapeAnalysis *EA) {
|
||||||
|
return getNode(V.getDef(), EA, true);
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets or creates a content node to which \a AddrNode points to.
|
/// Gets or creates a content node to which \a AddrNode points to.
|
||||||
CGNode *getContentNode(CGNode *AddrNode);
|
CGNode *getContentNode(CGNode *AddrNode);
|
||||||
@@ -444,7 +457,7 @@ public:
|
|||||||
|
|
||||||
/// Returns the node of the "exact" value \p V (no projections are skipped)
|
/// Returns the node of the "exact" value \p V (no projections are skipped)
|
||||||
/// if one exists.
|
/// if one exists.
|
||||||
CGNode *getNodeOrNull(ValueBase *V) {
|
CGNode *lookupNode(ValueBase *V) {
|
||||||
CGNode *Node = Values2Nodes.lookup(V);
|
CGNode *Node = Values2Nodes.lookup(V);
|
||||||
if (Node)
|
if (Node)
|
||||||
return Node->getMergeTarget();
|
return Node->getMergeTarget();
|
||||||
@@ -502,6 +515,9 @@ public:
|
|||||||
/// lookup-up with getNode() anymore.
|
/// lookup-up with getNode() anymore.
|
||||||
void removeFromGraph(ValueBase *V) { Values2Nodes.erase(V); }
|
void removeFromGraph(ValueBase *V) { Values2Nodes.erase(V); }
|
||||||
|
|
||||||
|
/// Returns true if there is a path from \p From to \p To.
|
||||||
|
bool isReachable(CGNode *From, CGNode *To);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// Gets or creates a node for a value \p V.
|
/// Gets or creates a node for a value \p V.
|
||||||
@@ -509,11 +525,13 @@ public:
|
|||||||
/// taken. This means the node is always created for the "outermost" value
|
/// taken. This means the node is always created for the "outermost" value
|
||||||
/// where V is contained.
|
/// where V is contained.
|
||||||
/// Returns null, if V is not a "pointer".
|
/// Returns null, if V is not a "pointer".
|
||||||
CGNode *getNode(ValueBase *V, EscapeAnalysis *EA);
|
CGNode *getNodeOrNull(ValueBase *V, EscapeAnalysis *EA) {
|
||||||
|
return getNode(V, EA, false);
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets or creates a node for a SILValue (same as above).
|
/// Gets or creates a node for a SILValue (same as above).
|
||||||
CGNode *getNode(SILValue V, EscapeAnalysis *EA) {
|
CGNode *getNodeOrNull(SILValue V, EscapeAnalysis *EA) {
|
||||||
return getNode(V.getDef(), EA);
|
return getNode(V.getDef(), EA, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of use-points of a node.
|
/// Returns the number of use-points of a node.
|
||||||
@@ -529,9 +547,6 @@ public:
|
|||||||
/// e.g. release or apply instructions.
|
/// e.g. release or apply instructions.
|
||||||
bool isUsePoint(ValueBase *V, CGNode *Node);
|
bool isUsePoint(ValueBase *V, CGNode *Node);
|
||||||
|
|
||||||
/// Returns true if there is a path from \p From to \p To.
|
|
||||||
bool canEscapeTo(CGNode *From, CGNode *To);
|
|
||||||
|
|
||||||
/// Computes the use point information.
|
/// Computes the use point information.
|
||||||
void computeUsePoints();
|
void computeUsePoints();
|
||||||
|
|
||||||
@@ -627,7 +642,7 @@ private:
|
|||||||
bool isPointer(ValueBase *V);
|
bool isPointer(ValueBase *V);
|
||||||
|
|
||||||
/// If V is a pointer, set it to global escaping.
|
/// If V is a pointer, set it to global escaping.
|
||||||
void setEscapesGlobal(ConnectionGraph *ConGraph, SILValue V) {
|
void setEscapesGlobal(ConnectionGraph *ConGraph, ValueBase *V) {
|
||||||
if (CGNode *Node = ConGraph->getNode(V, this))
|
if (CGNode *Node = ConGraph->getNode(V, this))
|
||||||
ConGraph->setEscapesGlobal(Node);
|
ConGraph->setEscapesGlobal(Node);
|
||||||
}
|
}
|
||||||
@@ -704,19 +719,30 @@ public:
|
|||||||
|
|
||||||
/// Returns true if the value \p V can escape to the function call \p FAS.
|
/// Returns true if the value \p V can escape to the function call \p FAS.
|
||||||
/// This means that the called function may access the value \p V.
|
/// This means that the called function may access the value \p V.
|
||||||
bool canEscapeTo(SILValue V, FullApplySite FAS, ConnectionGraph *ConGraph) {
|
/// If \p V has reference semantics, this function returns false if only the
|
||||||
return canEscapeToUsePoint(V, FAS.getInstruction(), ConGraph);
|
/// address of a contained property escapes, but not the object itself.
|
||||||
}
|
bool canEscapeTo(SILValue V, FullApplySite FAS);
|
||||||
|
|
||||||
|
/// Returns true if the value \p V or its content can escape to the
|
||||||
|
/// function call \p FAS.
|
||||||
|
/// This is the same as above, execpt that it returns true if an address of
|
||||||
|
/// a contained property escapes.
|
||||||
|
bool canObjectOrContentEscapeTo(SILValue V, FullApplySite FAS);
|
||||||
|
|
||||||
/// Returns true if the value \p V can escape to the release-instruction \p
|
/// Returns true if the value \p V can escape to the release-instruction \p
|
||||||
/// RI. This means that \p RI may release \p V or any called destructor may
|
/// RI. This means that \p RI may release \p V or any called destructor may
|
||||||
/// access (or release) \p V.
|
/// access (or release) \p V.
|
||||||
/// Note that if \p RI is a retain-instruction always false is returned.
|
/// Note that if \p RI is a retain-instruction always false is returned.
|
||||||
bool canEscapeTo(SILValue V, RefCountingInst *RI, ConnectionGraph *ConGraph) {
|
bool canEscapeTo(SILValue V, RefCountingInst *RI);
|
||||||
return canEscapeToUsePoint(V, RI, ConGraph);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool canPointToSameMemory(SILValue V1, SILValue V2, ConnectionGraph *ConGraph);
|
/// Returns true if the value \p V can escape to any other pointer \p To.
|
||||||
|
/// This means that either \p To is the same as \p V or containes a reference
|
||||||
|
/// to \p V.
|
||||||
|
bool canEscapeToValue(SILValue V, SILValue To);
|
||||||
|
|
||||||
|
/// Returns true if the pointers \p V1 and \p V2 can possibly point to the
|
||||||
|
/// same memory.
|
||||||
|
bool canPointToSameMemory(SILValue V1, SILValue V2);
|
||||||
|
|
||||||
virtual void invalidate(InvalidationKind K) override;
|
virtual void invalidate(InvalidationKind K) override;
|
||||||
|
|
||||||
|
|||||||
@@ -80,7 +80,21 @@ void EscapeAnalysis::ConnectionGraph::clear() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
EscapeAnalysis::CGNode *EscapeAnalysis::ConnectionGraph::
|
EscapeAnalysis::CGNode *EscapeAnalysis::ConnectionGraph::
|
||||||
getOrCreateNode(ValueBase *V) {
|
getNode(ValueBase *V, EscapeAnalysis *EA, bool createIfNeeded) {
|
||||||
|
if (isa<FunctionRefInst>(V))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (!V->hasValue())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (!EA->isPointer(V))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
V = skipProjections(V);
|
||||||
|
|
||||||
|
if (!createIfNeeded)
|
||||||
|
return lookupNode(V);
|
||||||
|
|
||||||
CGNode * &Node = Values2Nodes[V];
|
CGNode * &Node = Values2Nodes[V];
|
||||||
if (!Node) {
|
if (!Node) {
|
||||||
if (SILArgument *Arg = dyn_cast<SILArgument>(V)) {
|
if (SILArgument *Arg = dyn_cast<SILArgument>(V)) {
|
||||||
@@ -296,7 +310,7 @@ void EscapeAnalysis::ConnectionGraph::computeUsePoints() {
|
|||||||
/// In addition to releasing instructions (see below) we also add block
|
/// In addition to releasing instructions (see below) we also add block
|
||||||
/// arguments as use points. In case of loops, block arguments can
|
/// arguments as use points. In case of loops, block arguments can
|
||||||
/// "extend" the liferange of a reference in upward direction.
|
/// "extend" the liferange of a reference in upward direction.
|
||||||
if (CGNode *ArgNode = getNodeOrNull(BBArg)) {
|
if (CGNode *ArgNode = lookupNode(BBArg)) {
|
||||||
addUsePoint(ArgNode, BBArg);
|
addUsePoint(ArgNode, BBArg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -314,7 +328,7 @@ void EscapeAnalysis::ConnectionGraph::computeUsePoints() {
|
|||||||
int ValueIdx = -1;
|
int ValueIdx = -1;
|
||||||
for (const Operand &Op : I.getAllOperands()) {
|
for (const Operand &Op : I.getAllOperands()) {
|
||||||
ValueBase *OpV = Op.get().getDef();
|
ValueBase *OpV = Op.get().getDef();
|
||||||
if (CGNode *OpNd = getNodeOrNull(skipProjections(OpV))) {
|
if (CGNode *OpNd = lookupNode(skipProjections(OpV))) {
|
||||||
if (ValueIdx < 0) {
|
if (ValueIdx < 0) {
|
||||||
ValueIdx = addUsePoint(OpNd, &I);
|
ValueIdx = addUsePoint(OpNd, &I);
|
||||||
} else {
|
} else {
|
||||||
@@ -438,22 +452,6 @@ bool EscapeAnalysis::ConnectionGraph::mergeFrom(ConnectionGraph *SourceGraph,
|
|||||||
return Changed;
|
return Changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
EscapeAnalysis::CGNode *EscapeAnalysis::ConnectionGraph::
|
|
||||||
getNode(ValueBase *V, EscapeAnalysis *EA) {
|
|
||||||
if (isa<FunctionRefInst>(V))
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
if (!V->hasValue())
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
if (!EA->isPointer(V))
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
V = skipProjections(V);
|
|
||||||
|
|
||||||
return getOrCreateNode(V);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if \p V is a use of \p Node, i.e. V may (indirectly)
|
/// Returns true if \p V is a use of \p Node, i.e. V may (indirectly)
|
||||||
/// somehow refer to the Node's value.
|
/// somehow refer to the Node's value.
|
||||||
/// Use-points are only values which are relevant for lifeness computation,
|
/// Use-points are only values which are relevant for lifeness computation,
|
||||||
@@ -470,7 +468,7 @@ bool EscapeAnalysis::ConnectionGraph::isUsePoint(ValueBase *V, CGNode *Node) {
|
|||||||
return Node->UsePoints.test(Idx);
|
return Node->UsePoints.test(Idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EscapeAnalysis::ConnectionGraph::canEscapeTo(CGNode *From, CGNode *To) {
|
bool EscapeAnalysis::ConnectionGraph::isReachable(CGNode *From, CGNode *To) {
|
||||||
// See if we can reach the From-node by transitively visiting the
|
// See if we can reach the From-node by transitively visiting the
|
||||||
// predecessor nodes of the To-node.
|
// predecessor nodes of the To-node.
|
||||||
// Usually nodes have few predecessor nodes and the graph depth is small.
|
// Usually nodes have few predecessor nodes and the graph depth is small.
|
||||||
@@ -1121,6 +1119,9 @@ void EscapeAnalysis::analyzeInstruction(SILInstruction *I,
|
|||||||
case ValueKind::AllocStackInst:
|
case ValueKind::AllocStackInst:
|
||||||
case ValueKind::AllocRefInst:
|
case ValueKind::AllocRefInst:
|
||||||
case ValueKind::AllocBoxInst:
|
case ValueKind::AllocBoxInst:
|
||||||
|
ConGraph->getNode(I, this);
|
||||||
|
return;
|
||||||
|
|
||||||
case ValueKind::DeallocStackInst:
|
case ValueKind::DeallocStackInst:
|
||||||
case ValueKind::StrongRetainInst:
|
case ValueKind::StrongRetainInst:
|
||||||
case ValueKind::StrongRetainUnownedInst:
|
case ValueKind::StrongRetainUnownedInst:
|
||||||
@@ -1318,7 +1319,7 @@ void EscapeAnalysis::setAllEscaping(SILInstruction *I,
|
|||||||
for (const Operand &Op : I->getAllOperands()) {
|
for (const Operand &Op : I->getAllOperands()) {
|
||||||
SILValue OpVal = Op.get();
|
SILValue OpVal = Op.get();
|
||||||
if (!isNonWritableMemoryAddress(OpVal.getDef()))
|
if (!isNonWritableMemoryAddress(OpVal.getDef()))
|
||||||
setEscapesGlobal(ConGraph, OpVal);
|
setEscapesGlobal(ConGraph, OpVal.getDef());
|
||||||
}
|
}
|
||||||
// Even if the instruction does not write memory it could e.g. return the
|
// Even if the instruction does not write memory it could e.g. return the
|
||||||
// address of global memory. Therefore we have to define it as escaping.
|
// address of global memory. Therefore we have to define it as escaping.
|
||||||
@@ -1469,42 +1470,134 @@ bool EscapeAnalysis::mergeSummaryGraph(ConnectionGraph *SummaryGraph,
|
|||||||
return SummaryGraph->mergeFrom(Graph, Mapping);
|
return SummaryGraph->mergeFrom(Graph, Mapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool EscapeAnalysis::canEscapeToUsePoint(SILValue V, ValueBase *UsePoint,
|
bool EscapeAnalysis::canEscapeToUsePoint(SILValue V, ValueBase *UsePoint,
|
||||||
ConnectionGraph *ConGraph) {
|
ConnectionGraph *ConGraph) {
|
||||||
CGNode *Node = ConGraph->getNode(V, this);
|
|
||||||
|
assert((FullApplySite::isa(UsePoint) || isa<RefCountingInst>(UsePoint)) &&
|
||||||
|
"use points are only created for calls and refcount instructions");
|
||||||
|
|
||||||
|
CGNode *Node = ConGraph->getNodeOrNull(V, this);
|
||||||
if (!Node)
|
if (!Node)
|
||||||
return false;
|
return true;
|
||||||
|
|
||||||
// First check if there are escape pathes which we don't explicitly see
|
// First check if there are escape pathes which we don't explicitly see
|
||||||
// in the graph.
|
// in the graph.
|
||||||
switch (Node->getEscapeState()) {
|
if (Node->escapesInsideFunction(isNotAliasingArgument(V)))
|
||||||
case EscapeState::None:
|
|
||||||
case EscapeState::Return:
|
|
||||||
break;
|
|
||||||
case EscapeState::Arguments:
|
|
||||||
if (!isNotAliasingArgument(V))
|
|
||||||
return true;
|
return true;
|
||||||
break;
|
|
||||||
case EscapeState::Global:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// No hidden escapes: check if the Node is reachable from the UsePoint.
|
// No hidden escapes: check if the Node is reachable from the UsePoint.
|
||||||
return ConGraph->isUsePoint(UsePoint, Node);
|
return ConGraph->isUsePoint(UsePoint, Node);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EscapeAnalysis::canPointToSameMemory(SILValue V1, SILValue V2,
|
bool EscapeAnalysis::canEscapeTo(SILValue V, FullApplySite FAS) {
|
||||||
ConnectionGraph *ConGraph) {
|
// If it's not a local object we don't know anything about the value.
|
||||||
CGNode *Node1 = ConGraph->getNode(V1, this);
|
if (!pointsToLocalObject(V))
|
||||||
assert(Node1 && "value is not a pointer");
|
return true;
|
||||||
CGNode *Node2 = ConGraph->getNode(V2, this);
|
auto *ConGraph = getConnectionGraph(FAS.getFunction());
|
||||||
assert(Node2 && "value is not a pointer");
|
return canEscapeToUsePoint(V, FAS.getInstruction(), ConGraph);
|
||||||
|
}
|
||||||
|
|
||||||
// If both nodes escape, the relation of the nodes may not be explicitly
|
bool EscapeAnalysis::canObjectOrContentEscapeTo(SILValue V, FullApplySite FAS) {
|
||||||
// represented in the graph.
|
// If it's not a local object we don't know anything about the value.
|
||||||
if (Node1->escapesInsideFunction() && Node2->escapesInsideFunction())
|
if (!pointsToLocalObject(V))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
auto *ConGraph = getConnectionGraph(FAS.getFunction());
|
||||||
|
CGNode *Node = ConGraph->getNodeOrNull(V, this);
|
||||||
|
if (!Node)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// First check if there are escape pathes which we don't explicitly see
|
||||||
|
// in the graph.
|
||||||
|
if (Node->escapesInsideFunction(isNotAliasingArgument(V)))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Check if the object itself can escape to the called function.
|
||||||
|
SILInstruction *UsePoint = FAS.getInstruction();
|
||||||
|
if (ConGraph->isUsePoint(UsePoint, Node))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (V.getType().hasReferenceSemantics()) {
|
||||||
|
// Check if the object "content", i.e. a pointer to one of its stored
|
||||||
|
// properties, can escape to the called function.
|
||||||
|
CGNode *ContentNode = ConGraph->getContentNode(Node);
|
||||||
|
if (ContentNode->escapesInsideFunction(false))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (ConGraph->isUsePoint(UsePoint, ContentNode))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EscapeAnalysis::canEscapeTo(SILValue V, RefCountingInst *RI) {
|
||||||
|
// If it's not a local object we don't know anything about the value.
|
||||||
|
if (!pointsToLocalObject(V))
|
||||||
|
return true;
|
||||||
|
auto *ConGraph = getConnectionGraph(RI->getFunction());
|
||||||
|
return canEscapeToUsePoint(V, RI, ConGraph);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Utility to get the function which contains both values \p V1 and \p V2.
|
||||||
|
static SILFunction *getCommonFunction(SILValue V1, SILValue V2) {
|
||||||
|
SILBasicBlock *BB1 = V1->getParentBB();
|
||||||
|
SILBasicBlock *BB2 = V2->getParentBB();
|
||||||
|
if (!BB1 || !BB2)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
SILFunction *F = BB1->getParent();
|
||||||
|
assert(BB2->getParent() == F && "values not in same function");
|
||||||
|
return F;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EscapeAnalysis::canEscapeToValue(SILValue V, SILValue To) {
|
||||||
|
if (!pointsToLocalObject(V))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
SILFunction *F = getCommonFunction(V, To);
|
||||||
|
if (!F)
|
||||||
|
return true;
|
||||||
|
auto *ConGraph = getConnectionGraph(F);
|
||||||
|
|
||||||
|
CGNode *Node = ConGraph->getNodeOrNull(V, this);
|
||||||
|
if (!Node)
|
||||||
|
return true;
|
||||||
|
CGNode *ToNode = ConGraph->getNodeOrNull(To, this);
|
||||||
|
if (!ToNode)
|
||||||
|
return true;
|
||||||
|
return ConGraph->isReachable(Node, ToNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EscapeAnalysis::canPointToSameMemory(SILValue V1, SILValue V2) {
|
||||||
|
// At least one of the values must be a non-escaping local object.
|
||||||
|
bool isLocal1 = pointsToLocalObject(V1);
|
||||||
|
bool isLocal2 = pointsToLocalObject(V2);
|
||||||
|
if (!isLocal1 && !isLocal2)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
SILFunction *F = getCommonFunction(V1, V2);
|
||||||
|
if (!F)
|
||||||
|
return true;
|
||||||
|
auto *ConGraph = getConnectionGraph(F);
|
||||||
|
|
||||||
|
CGNode *Node1 = ConGraph->getNodeOrNull(V1, this);
|
||||||
|
if (!Node1)
|
||||||
|
return true;
|
||||||
|
CGNode *Node2 = ConGraph->getNodeOrNull(V2, this);
|
||||||
|
if (!Node2)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Finish the check for one value being a non-escaping local object.
|
||||||
|
if (isLocal1 && Node1->escapesInsideFunction(isNotAliasingArgument(V1)))
|
||||||
|
isLocal1 = false;
|
||||||
|
|
||||||
|
if (isLocal2 && Node2->escapesInsideFunction(isNotAliasingArgument(V2)))
|
||||||
|
isLocal2 = false;
|
||||||
|
|
||||||
|
if (!isLocal1 && !isLocal2)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Check if both nodes may point to the same content.
|
||||||
CGNode *Content1 = ConGraph->getContentNode(Node1);
|
CGNode *Content1 = ConGraph->getContentNode(Node1);
|
||||||
CGNode *Content2 = ConGraph->getContentNode(Node2);
|
CGNode *Content2 = ConGraph->getContentNode(Node2);
|
||||||
return Content1 == Content2;
|
return Content1 == Content2;
|
||||||
|
|||||||
@@ -290,7 +290,7 @@ bool StackPromoter::canPromoteAlloc(SILInstruction *AI,
|
|||||||
SILInstruction *&DeallocInsertionPoint) {
|
SILInstruction *&DeallocInsertionPoint) {
|
||||||
AllocInsertionPoint = nullptr;
|
AllocInsertionPoint = nullptr;
|
||||||
DeallocInsertionPoint = nullptr;
|
DeallocInsertionPoint = nullptr;
|
||||||
auto *Node = ConGraph->getNode(AI, EA);
|
auto *Node = ConGraph->getNodeOrNull(AI, EA);
|
||||||
if (!Node)
|
if (!Node)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user