Skip to content

Commit

Permalink
8259984: IGV: Crash when drawing control flow before GCM
Browse files Browse the repository at this point in the history
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
robcasloz committed Feb 19, 2021
1 parent 7e2c909 commit 61820b7
Showing 1 changed file with 154 additions and 77 deletions.
@@ -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
Expand Down Expand Up @@ -45,6 +45,7 @@ private static class Node {
public InputBlock block;
public boolean isBlockProjection;
public boolean isBlockStart;
public boolean isCFG;
}
private InputGraph graph;
private Collection<Node> nodes;
Expand All @@ -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) {
Expand Down Expand Up @@ -203,6 +214,8 @@ public Collection<InputBlock> schedule(InputGraph graph) {

this.graph = graph;
buildUpGraph();
markCFGNodes();
connectOrphansAndWidows();
buildBlocks();
buildDominators();
buildCommonDominators();
Expand Down Expand Up @@ -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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.