FIR CFA: clean up some utility functions

This commit is contained in:
pyos
2022-11-27 18:29:06 +01:00
committed by Dmitriy Novozhilov
parent faf0129a5d
commit a079010fad
@@ -319,7 +319,7 @@ class ControlFlowGraphBuilder {
if (invocationKind?.canBeVisited() == true) { if (invocationKind?.canBeVisited() == true) {
addEdge(exitNode, postponedExitNode, propagateDeadness = isDefinitelyVisited) addEdge(exitNode, postponedExitNode, propagateDeadness = isDefinitelyVisited)
if (invocationKind.canBeRevisited()) { if (invocationKind.canBeRevisited()) {
addBackEdge(postponedExitNode, splitNode, label = LoopBackPath) addBackEdge(postponedExitNode, splitNode)
} }
} }
return Triple(exitNode, postponedExitNode, graph) return Triple(exitNode, postponedExitNode, graph)
@@ -698,7 +698,7 @@ class ControlFlowGraphBuilder {
postponedLambdaExits.pop() postponedLambdaExits.pop()
} }
val labelForFinallyBLock = when (jump) { val labelForFinallyBlock = when (jump) {
is FirReturnExpression -> ReturnPath(jump.target.labeledElement.symbol) is FirReturnExpression -> ReturnPath(jump.target.labeledElement.symbol)
is FirLoopJump -> LoopPath(jump) is FirLoopJump -> LoopPath(jump)
else -> NormalPath else -> NormalPath
@@ -709,14 +709,19 @@ class ControlFlowGraphBuilder {
// do ... while (continue) // <- jump back (to itself), believe it or not // do ... while (continue) // <- jump back (to itself), believe it or not
val isBack = nextNode is LoopConditionEnterNode && val isBack = nextNode is LoopConditionEnterNode &&
(nextNode.loop !is FirDoWhileLoop || nextNode.previousNodes.any { it is LoopBlockExitNode }) (nextNode.loop !is FirDoWhileLoop || nextNode.previousNodes.any { it is LoopBlockExitNode })
addNodeWithJump( popAndAddEdge(node)
node, if (nextNode != null) {
nextNode, if (isBack) {
isBack = isBack, addBackEdge(node, nextNode) // TODO: shouldn't this go through finally too?
trackJump = jump is FirReturnExpression, } else {
label = if (isBack) LoopBackPath else NormalPath, val hadFinally = addEdgeThroughFinally(node, nextNode, labelForFinallyBlock = labelForFinallyBlock)
labelForFinallyBLock = labelForFinallyBLock if (hadFinally && jump is FirReturnExpression) {
) //actually we can store all returns like this, but not sure if it makes anything better
nonDirectJumps.put(nextNode, node)
}
}
}
addWithStub(node)
return node return node
} }
@@ -818,7 +823,7 @@ class ControlFlowGraphBuilder {
val loopBlockExitNode = createLoopBlockExitNode(loop) val loopBlockExitNode = createLoopBlockExitNode(loop)
popAndAddEdge(loopBlockExitNode) popAndAddEdge(loopBlockExitNode)
val conditionEnterNode = loopConditionEnterNodes.pop() val conditionEnterNode = loopConditionEnterNodes.pop()
addBackEdge(loopBlockExitNode, conditionEnterNode, label = LoopBackPath) addBackEdge(loopBlockExitNode, conditionEnterNode)
val loopExitNode = loopExitNodes.pop() val loopExitNode = loopExitNodes.pop()
loopExitNode.updateDeadStatus() loopExitNode.updateDeadStatus()
lastNodes.push(loopExitNode) lastNodes.push(loopExitNode)
@@ -860,7 +865,7 @@ class ControlFlowGraphBuilder {
popAndAddEdge(conditionExitNode) popAndAddEdge(conditionExitNode)
val blockEnterNode = lastNodes.pop() val blockEnterNode = lastNodes.pop()
require(blockEnterNode is LoopBlockEnterNode) require(blockEnterNode is LoopBlockEnterNode)
addBackEdge(conditionExitNode, blockEnterNode, isDead = conditionBooleanValue == false, label = LoopBackPath) addBackEdge(conditionExitNode, blockEnterNode, isDead = conditionBooleanValue == false)
val loopExit = loopExitNodes.pop() val loopExit = loopExitNodes.pop()
addEdge(conditionExitNode, loopExit, propagateDeadness = false, isDead = conditionBooleanValue == true) addEdge(conditionExitNode, loopExit, propagateDeadness = false, isDead = conditionBooleanValue == true)
loopExit.updateDeadStatus() loopExit.updateDeadStatus()
@@ -1068,15 +1073,11 @@ class ControlFlowGraphBuilder {
val stub = withLevelOfNode(node) { createStubNode() } val stub = withLevelOfNode(node) { createStubNode() }
val edges = node.followingNodes.map { it to node.outgoingEdges.getValue(it) } val edges = node.followingNodes.map { it to node.outgoingEdges.getValue(it) }
CFGNode.removeAllOutgoingEdges(node) CFGNode.removeAllOutgoingEdges(node)
addEdge(node, stub) CFGNode.addEdge(node, stub, EdgeKind.DeadForward, propagateDeadness = false)
for ((to, edge) in edges) { for ((to, edge) in edges) {
addEdge( val kind = if (edge.kind.isBack) EdgeKind.DeadBackward else EdgeKind.DeadForward
from = stub, // TODO: `propagateDeadness` should be redundant if `propagateDeadnessForward` was correct, but it's not.
to = to, CFGNode.addEdge(stub, to, kind, propagateDeadness = true, label = edge.label)
isBack = edge.kind.isBack,
preferredKind = edge.kind,
label = edge.label
)
} }
stub.followingNodes.forEach { propagateDeadnessForward(it, deep = true) } stub.followingNodes.forEach { propagateDeadnessForward(it, deep = true) }
} }
@@ -1384,15 +1385,20 @@ class ControlFlowGraphBuilder {
// ----------------------------------- Edge utils ----------------------------------- // ----------------------------------- Edge utils -----------------------------------
private fun addNewSimpleNode( private fun addNewSimpleNode(node: CFGNode<*>, isDead: Boolean = false) {
node: CFGNode<*>, addEdge(lastNodes.pop(), node, preferredKind = if (isDead) EdgeKind.DeadForward else EdgeKind.Forward)
isDead: Boolean = false,
preferredKind: EdgeKind = EdgeKind.Forward
): CFGNode<*> {
val lastNode = lastNodes.pop()
addEdge(lastNode, node, isDead = isDead, preferredKind = preferredKind)
lastNodes.push(node) lastNodes.push(node)
return lastNode }
private fun addNewSimpleNodeIfPossible(newNode: CFGNode<*>) {
if (lastNodes.isEmpty) return
addNewSimpleNode(newNode)
}
private fun addWithStub(node: CFGNode<*>) {
val stub = createStubNode()
addEdge(node, stub)
lastNodes.push(stub)
} }
private fun addNodeThatReturnsNothing(node: CFGNode<*>, preferredKind: EdgeKind = EdgeKind.Forward) { private fun addNodeThatReturnsNothing(node: CFGNode<*>, preferredKind: EdgeKind = EdgeKind.Forward) {
@@ -1406,6 +1412,24 @@ class ControlFlowGraphBuilder {
finallyEnterNodes.topOrNull()?.level == levelCounter -> { finallyEnterNodes.topOrNull()?.level == levelCounter -> {
// (2)... with finally // (2)... with finally
// Either in try-main or catch. Route to `finally` // Either in try-main or catch. Route to `finally`
// TODO: that doesn't seem right?
// fun bar(): Nothing = throw RuntimeException()
// fun main() {
// val x: Int
// try {
// try {
// x = 1
// println(x)
// bar() // addNodeThatReturnsNothing
// } finally {} // to this finally?
// } finally { // but what about this one?
// x = 2
// println(x)
// }
// }
// -> compiles and prints 1 then 2
// Guessing that there should already be an UncaughtExceptionPath edge from finally 1 to finally 2
// regardless of `bar()`, but there isn't.
finallyEnterNodes.top() finallyEnterNodes.top()
} }
// (2)... without finally or within finally ...(3) // (2)... without finally or within finally ...(3)
@@ -1426,77 +1450,38 @@ class ControlFlowGraphBuilder {
// (3)... within finally. // (3)... within finally.
else -> exitTargetsForTry.top() else -> exitTargetsForTry.top()
} }
popAndAddEdge(node, preferredKind)
if (targetNode is TryMainBlockExitNode) { if (targetNode is TryMainBlockExitNode) {
val catches = targetNode.fir.catches val catches = targetNode.fir.catches
if (catches.isEmpty()) { if (catches.isEmpty()) {
addNodeWithJump(node, exitTargetsForTry.top(), preferredKind, label = UncaughtExceptionPath) addEdgeThroughFinally(node, exitTargetsForTry.top(), label = UncaughtExceptionPath)
} else { } else {
//edges to all the catches for (catch in catches) {
addNodeWithJumpToCatches(node, catches.map { catchNodeStorage[it]!! }, preferredKind = preferredKind) addEdge(node, catchNodeStorage[catch]!!, propagateDeadness = false, label = UncaughtExceptionPath)
}
} }
} else { } else {
addNodeWithJump(node, targetNode, preferredKind, label = UncaughtExceptionPath) addEdgeThroughFinally(node, targetNode, label = UncaughtExceptionPath)
} }
addWithStub(node)
} }
private fun addNodeWithJump( private fun addEdgeThroughFinally(
node: CFGNode<*>, node: CFGNode<*>,
targetNode: CFGNode<*>?, targetNode: CFGNode<*>,
preferredKind: EdgeKind = EdgeKind.Forward,
isBack: Boolean = false,
label: EdgeLabel = NormalPath, label: EdgeLabel = NormalPath,
labelForFinallyBLock: EdgeLabel = label, labelForFinallyBlock: EdgeLabel = label
trackJump: Boolean = false ): Boolean {
) { var next = node
popAndAddEdge(node, preferredKind) for (finallyEnter in finallyEnterNodes.all()) {
if (targetNode != null) { if (finallyEnter.level <= targetNode.level) {
if (isBack) { break
addBackEdge(node, targetNode, label = label)
} else {
// go through all final nodes between node and target
val finallyNodes = finallyBefore(targetNode)
val finalFrom = finallyNodes.fold(node) { from, (finallyEnter, tryExit) ->
addEdgeIfNotExist(from, finallyEnter, propagateDeadness = false, label = labelForFinallyBLock)
tryExit
}
addEdgeIfNotExist(
finalFrom,
targetNode,
propagateDeadness = false,
label = if (finallyNodes.isEmpty()) label else labelForFinallyBLock
)
if (trackJump && finallyNodes.isNotEmpty()) {
//actually we can store all returns like this, but not sure if it makes anything better
nonDirectJumps.put(targetNode, node)
}
} }
addEdgeIfNotExist(next, finallyEnter, propagateDeadness = false, label = labelForFinallyBlock)
next = finallyExitNodes[finallyEnter.fir]!!
} }
val stub = createStubNode() addEdgeIfNotExist(next, targetNode, propagateDeadness = false, label = if (next == node) label else labelForFinallyBlock)
addEdge(node, stub) return next != node
lastNodes.push(stub)
}
private fun addNodeWithJumpToCatches(
node: CFGNode<*>,
targets: List<CatchClauseEnterNode>,
label: EdgeLabel = UncaughtExceptionPath,
preferredKind: EdgeKind = EdgeKind.Forward
) {
require(targets.isNotEmpty())
popAndAddEdge(node, preferredKind)
targets.forEach { target ->
addEdge(node, target, propagateDeadness = false, label = label)
}
val stub = createStubNode()
addEdge(node, stub)
lastNodes.push(stub)
}
private fun finallyBefore(target: CFGNode<*>): List<Pair<FinallyBlockEnterNode, FinallyBlockExitNode>> {
return finallyEnterNodes.all().takeWhile { it.level > target.level }.map { finallyEnter ->
val finallyExit = finallyExitNodes[finallyEnter.fir]!!
finallyEnter to finallyExit
}
} }
private fun popAndAddEdge(to: CFGNode<*>, preferredKind: EdgeKind = EdgeKind.Forward) { private fun popAndAddEdge(to: CFGNode<*>, preferredKind: EdgeKind = EdgeKind.Forward) {
@@ -1507,12 +1492,11 @@ class ControlFlowGraphBuilder {
from: CFGNode<*>, from: CFGNode<*>,
to: CFGNode<*>, to: CFGNode<*>,
propagateDeadness: Boolean = true, propagateDeadness: Boolean = true,
isDead: Boolean = false,
preferredKind: EdgeKind = EdgeKind.Forward, preferredKind: EdgeKind = EdgeKind.Forward,
label: EdgeLabel = NormalPath label: EdgeLabel = NormalPath
) { ) {
if (!from.followingNodes.contains(to)) { if (!from.followingNodes.contains(to)) {
addEdge(from, to, propagateDeadness, isDead, preferredKind = preferredKind, label = label) addEdge(from, to, propagateDeadness, preferredKind = preferredKind, label = label)
} }
} }
@@ -1521,23 +1505,18 @@ class ControlFlowGraphBuilder {
to: CFGNode<*>, to: CFGNode<*>,
propagateDeadness: Boolean = true, propagateDeadness: Boolean = true,
isDead: Boolean = false, isDead: Boolean = false,
isBack: Boolean = false,
preferredKind: EdgeKind = EdgeKind.Forward, preferredKind: EdgeKind = EdgeKind.Forward,
label: EdgeLabel = NormalPath label: EdgeLabel = NormalPath
) { ) {
val kind = if (isDead || from.isDead || to.isDead) { val kind = if (isDead || from.isDead || to.isDead) {
if (isBack) EdgeKind.DeadBackward else EdgeKind.DeadForward if (preferredKind.isBack) EdgeKind.DeadBackward else EdgeKind.DeadForward
} else preferredKind } else preferredKind
CFGNode.addEdge(from, to, kind, propagateDeadness, label) CFGNode.addEdge(from, to, kind, propagateDeadness, label)
} }
private fun addBackEdge( private fun addBackEdge(from: CFGNode<*>, to: CFGNode<*>, isDead: Boolean = false) {
from: CFGNode<*>, val kind = if (isDead || from.isDead || to.isDead) EdgeKind.DeadBackward else EdgeKind.CfgBackward
to: CFGNode<*>, CFGNode.addEdge(from, to, kind, propagateDeadness = false, label = LoopBackPath)
isDead: Boolean = false,
label: EdgeLabel = NormalPath
) {
addEdge(from, to, propagateDeadness = false, isDead = isDead, isBack = true, preferredKind = EdgeKind.CfgBackward, label = label)
} }
private fun propagateDeadnessForward(node: CFGNode<*>, deep: Boolean = false) { private fun propagateDeadnessForward(node: CFGNode<*>, deep: Boolean = false) {
@@ -1570,11 +1549,6 @@ class ControlFlowGraphBuilder {
else -> emptyList() else -> emptyList()
} }
private fun addNewSimpleNodeIfPossible(newNode: CFGNode<*>, isDead: Boolean = false): CFGNode<*>? {
if (lastNodes.isEmpty) return null
return addNewSimpleNode(newNode, isDead)
}
private fun <R> withLevelOfNode(node: CFGNode<*>, f: () -> R): R { private fun <R> withLevelOfNode(node: CFGNode<*>, f: () -> R): R {
val last = levelCounter val last = levelCounter
levelCounter = node.level levelCounter = node.level