Skip to content
Permalink
Browse files
8259984: IGV: Crash when drawing control flow before GCM
Replace backward traversal in the IGV block formation algorithm by forward
traversal guided by node category information. This change addresses the
reported assertion failures, places block projection nodes together with their
predecessors, and gives a more natural block numbering.

Reviewed-by: chagedorn, neliasso
  • Loading branch information
Roberto Castañeda Lozano committed Feb 19, 2021
1 parent 7e2c909 commit 61820b74dd804899f44aa21813d94c9bd2661d6a
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -45,6 +45,7 @@
public InputBlock block;
public boolean isBlockProjection;
public boolean isBlockStart;
public boolean isCFG;
}
private InputGraph graph;
private Collection<Node> nodes;
@@ -63,112 +64,122 @@ public int compare(InputEdge o1, InputEdge o2) {

public void buildBlocks() {

// Initialize data structures.
blocks = new Vector<>();
Node root = findRoot();
if (root == null) {
return;
}
Stack<Node> stack = new Stack<>();
Set<Node> visited = new HashSet<>();
Map<InputBlock, Set<Node>> terminators = new HashMap<>();
// Pre-compute control successors of each node, excluding self edges.
Map<Node, Set<Node>> controlSuccs = new HashMap<>();
for (Node n : nodes) {
if (n.isCFG) {
Set<Node> nControlSuccs = new HashSet<>();
for (Node s : n.succs) {
if (s.isCFG && s != n) {
nControlSuccs.add(s);
}
}
controlSuccs.put(n, nControlSuccs);
}
}
stack.add(root);
int blockCount = 0;
// Start from 1 to follow the style of compiler-generated CFGs.
int blockCount = 1;
InputBlock rootBlock = null;


// Traverse the control-flow subgraph forwards, starting from the root.
while (!stack.isEmpty()) {
Node proj = stack.pop();
Node parent = proj;
if (proj.isBlockProjection && proj.preds.size() > 0) {
parent = proj.preds.get(0);
}

if (!visited.contains(parent)) {
visited.add(parent);
InputBlock block = graph.addBlock(Integer.toString(blockCount));
blocks.add(block);
if (parent == root) {
rootBlock = block;
}
blockCount++;
parent.block = block;
if (proj != parent && proj.succs.size() == 1 && proj.succs.contains(root)) {
// Special treatment of Halt-nodes
proj.block = block;
}

Node p = proj;
do {
if (p.preds.size() == 0 || p.preds.get(0) == null) {
p = parent;
break;
}

p = p.preds.get(0);
if (p == proj) {
// Cycle, stop
// Pop a node, mark it as visited, and create a new block.
Node n = stack.pop();
if (visited.contains(n)) {
continue;
}
visited.add(n);
InputBlock block = graph.addBlock(Integer.toString(blockCount));
blocks.add(block);
if (n == root) {
rootBlock = block;
}
blockCount++;
Set<Node> blockTerminators = new HashSet<Node>();
// Move forwards until a terminator node is found, assigning all
// visited nodes to the current block.
while (true) {
// Assign n to current block.
n.block = block;
if (controlSuccs.get(n).size() == 0) {
// No successors: end the block.
blockTerminators.add(n);
break;
} else if (controlSuccs.get(n).size() == 1) {
// One successor: end the block if it is a block start node.
Node s = controlSuccs.get(n).iterator().next();
if (s.isBlockStart) {
// Block start: end the block.
blockTerminators.add(n);
stack.push(s);
break;
} else {
// Not a block start: keep filling the current block.
n = s;
}

if (p.block == null) {
p.block = block;
}
} while (!p.isBlockProjection && !p.isBlockStart);

if (block != rootBlock) {
for (Node n : p.preds) {
if (n != null && n != p) {
if (n.isBlockProjection) {
n = n.preds.get(0);
}
if (n.block != null) {
graph.addBlockEdge(n.block, block);
}
}
}
}

for (Node n : parent.succs) {
if (n != root && n.isBlockProjection) {
for (Node n2 : n.succs) {

if (n2 != parent && n2.block != null && n2.block != rootBlock) {
graph.addBlockEdge(block, n2.block);
} else {
// Multiple successors: end the block.
for (Node s : controlSuccs.get(n)) {
if (s.isBlockProjection && s != root) {
// Assign block projections to the current block,
// and push their successors to the stack. In the
// normal case, we would expect control projections
// to have only one successor, but there are some
// intermediate graphs (e.g. 'Before RemoveUseless')
// where 'IfX' nodes flow both to 'Region' and
// (dead) 'Safepoint' nodes.
s.block = block;
blockTerminators.add(s);
for (Node ps : controlSuccs.get(s)) {
stack.push(ps);
}
}
} else {
if (n != parent && n.block != null && n.block != rootBlock) {
graph.addBlockEdge(block, n.block);
} else {
blockTerminators.add(n);
stack.push(s);
}
}
break;
}
}
terminators.put(block, blockTerminators);
}

int num_preds = p.preds.size();
int bottom = -1;
if (isRegion(p) || isPhi(p)) {
bottom = 0;
}

int pushed = 0;
for (int i = num_preds - 1; i > bottom; i--) {
if (p.preds.get(i) != null && p.preds.get(i) != p) {
stack.push(p.preds.get(i));
pushed++;
// Add block edges based on terminator successors. Note that a block
// might have multiple terminators preceding the same successor block.
for (Map.Entry<InputBlock, Set<Node>> terms : terminators.entrySet()) {
// Unique set of terminator successors.
Set<Node> uniqueSuccs = new HashSet<>();
for (Node t : terms.getValue()) {
for (Node s : controlSuccs.get(t)) {
if (s.block != rootBlock) {
uniqueSuccs.add(s);
}
}

if (pushed == 0 && p == root) {
// TODO: special handling when root backedges are not built yet
}
}
for (Node s : uniqueSuccs) {
graph.addBlockEdge(terms.getKey(), s.block);
}
}

// Fill the blocks.
for (Node n : nodes) {
InputBlock block = n.block;
if (block != null) {
block.addNode(n.inputNode.getId());
}
}

// Compute block index map for dominator computation.
int z = 0;
blockIndex = new HashMap<>(blocks.size());
for (InputBlock b : blocks) {
@@ -203,6 +214,8 @@ private String getBlockName(InputNode n) {

this.graph = graph;
buildUpGraph();
markCFGNodes();
connectOrphansAndWidows();
buildBlocks();
buildDominators();
buildCommonDominators();
@@ -621,4 +634,68 @@ public void buildUpGraph() {
}
}
}

// Mark nodes that form the CFG (same as shown by the 'Show control flow
// only' filter, plus the Root node).
public void markCFGNodes() {
for (Node n : nodes) {
String category = n.inputNode.getProperties().get("category");
assert category != null :
"Node category not found, please use input from a compatible " +
"compiler version";
if (category.equals("control") || category.equals("mixed")) {
// Example: If, IfTrue, CallStaticJava.
n.isCFG = true;
} else if (n.inputNode.getProperties().get("type").equals("bottom")
&& n.preds.size() > 0 &&
n.preds.get(0) != null &&
n.preds.get(0).inputNode.getProperties()
.get("category").equals("control")) {
// Example: Halt, Return, Rethrow.
n.isCFG = true;
} else if (n.isBlockStart || n.isBlockProjection) {
// Example: Root.
n.isCFG = true;
} else {
n.isCFG = false;
}
}
}

// Fix ill-formed graphs with orphan/widow control-flow nodes by adding
// edges from/to the Root node. Such edges are assumed by different parts of
// the scheduling algorithm, but are not always present, e.g. for certain
// 'Safepoint' nodes in the 'Before RemoveUseless' phase.
public void connectOrphansAndWidows() {
Node root = findRoot();
if (root == null) {
return;
}
for (Node n : nodes) {
if (n.isCFG) {
boolean orphan = true;
for (Node p : n.preds) {
if (p != n && p.isCFG) {
orphan = false;
}
}
if (orphan) {
// Add edge from root to this node.
root.succs.add(n);
n.preds.add(0, root);
}
boolean widow = true;
for (Node s : n.succs) {
if (s != n && s.isCFG) {
widow = false;
}
}
if (widow) {
// Add edge from this node to root.
root.preds.add(n);
n.succs.add(root);
}
}
}
}
}

1 comment on commit 61820b7

@openjdk-notifier

This comment has been minimized.

Copy link

@openjdk-notifier openjdk-notifier bot commented on 61820b7 Feb 19, 2021

Please sign in to comment.