/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.compilers.opt.lir2mir;
import java.util.Enumeration;
import org.jikesrvm.VM;
import org.jikesrvm.compilers.opt.OptimizingCompilerException;
import org.jikesrvm.compilers.opt.depgraph.DepGraph;
import org.jikesrvm.compilers.opt.depgraph.DepGraphEdge;
import org.jikesrvm.compilers.opt.depgraph.DepGraphNode;
import org.jikesrvm.compilers.opt.ir.IR;
import org.jikesrvm.compilers.opt.ir.Instruction;
import static org.jikesrvm.compilers.opt.ir.Operators.CALL_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.GUARD_COMBINE;
import static org.jikesrvm.compilers.opt.ir.Operators.GUARD_COND_MOVE;
import static org.jikesrvm.compilers.opt.ir.Operators.GUARD_MOVE;
import static org.jikesrvm.compilers.opt.ir.Operators.IR_PROLOGUE;
import static org.jikesrvm.compilers.opt.ir.Operators.OTHER_OPERAND_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.RETURN_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.SYSCALL_opcode;
import static org.jikesrvm.compilers.opt.ir.Operators.YIELDPOINT_OSR_opcode;
import org.jikesrvm.compilers.opt.ir.ResultCarrier;
import org.jikesrvm.compilers.opt.ir.operand.AddressConstantOperand;
import org.jikesrvm.compilers.opt.ir.operand.BranchOperand;
import org.jikesrvm.compilers.opt.ir.operand.InlinedOsrTypeInfoOperand;
import org.jikesrvm.compilers.opt.ir.operand.IntConstantOperand;
import org.jikesrvm.compilers.opt.ir.operand.LongConstantOperand;
import org.jikesrvm.compilers.opt.ir.operand.Operand;
import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand;
import org.jikesrvm.compilers.opt.util.SpaceEffGraphEdge;
import org.jikesrvm.compilers.opt.util.SpaceEffGraphNode;
/**
* This class contains methods for invoking BURS tree-pattern matching
* from the OPT Compiler. BURS is invoked on trees obtained from the
* dependence graph of a basic block.
*
* @see DepGraph
* @see BURS_StateCoder
* @see AbstractBURS_TreeNode
*/
final class NormalBURS extends BURS {
private int numTreeRoots;
/**
* track problem nodes (nodes with outgoing non-reg-true edges)
*/
private SpaceEffGraphEdge[] problemEdges;
/** Number of problem edges */
private int numProblemEdges = 0;
/**
* Create a BURS object for the given IR.
*
* @param ir the IR to translate from LIR to MIR.
*/
NormalBURS(IR ir) {
super(ir);
}
/**
* Build BURS trees for dependence graph dg, label the trees, and
* then generate MIR instructions based on the labelling.
* @param dg the dependence graph.
*/
public void invoke(NormalBURS_DepGraph dg) {
if (DEBUG) dg.printDepGraph();
buildTrees(dg);
if (haveProblemEdges()) {
problemEdgePrep();
handleProblemEdges();
}
orderTrees(dg);
labelTrees();
generateTrees(makeCoder());
}
////////////////////////////////
// Implementation
////////////////////////////////
private NormalBURS_DepGraphNode castNode(SpaceEffGraphNode node) {
return (NormalBURS_DepGraphNode) node;
}
/**
* Stage 1: Complete the expression trees and identify tree roots.
* Complete BURS trees by adding leaf nodes as needed, and
* creating tree edges by calling insertChild1() or insertChild2()
* This step is also where we introduce intermediate tree nodes for
* any LIR instruction that has > 2 "real" operands e.g., a CALL.
* We also mark nodes that must be tree roots.
*
* @param dg The dependence graph.
*/
private void buildTrees(DepGraph dg) {
DepGraphNode bbNodes = (DepGraphNode) dg.firstNode();
for (DepGraphNode n = bbNodes; n != null; n = (DepGraphNode) n.getNext()) {
// Initialize n.treeNode
AbstractBURS_TreeNode cur_parent = AbstractBURS_TreeNode.create(n);
castNode(n).setCurrentParent(cur_parent);
Instruction instr = n.instruction();
// cur_parent = current parent node for var length IR instructions
// loop for USES of an instruction
for (Enumeration<Operand> uses = instr.getUses(); uses.hasMoreElements();) {
// Create tree edge for next use.
Operand op = uses.nextElement();
if (op == null) continue;
// Set child = AbstractBURS_TreeNode for operand op
AbstractBURS_TreeNode child;
if (op instanceof RegisterOperand) {
RegisterOperand regOp = (RegisterOperand) op;
// ignore validation registers
if (regOp.getRegister().isValidation()) continue;
DepGraphEdge e = DepGraphEdge.findInputEdge(n, op);
if (e == null) { // operand is leaf
child = Register;
} else {
child = castNode(e.fromNode()).getCurrentParent();
}
} else if (op instanceof IntConstantOperand) {
child = new BURS_IntConstantTreeNode(((IntConstantOperand) op).value);
} else if (op instanceof LongConstantOperand) {
child = LongConstant;
} else if (op instanceof AddressConstantOperand) {
child = AddressConstant;
} else if (op instanceof BranchOperand && instr.isCall()) {
child = BranchTarget;
} else if (op instanceof InlinedOsrTypeInfoOperand && instr.isYieldPoint()) {
child = NullTreeNode;
} else {
continue;
}
// Attach child as child of cur_parent in correct position
if (cur_parent.getChild1() == null) {
cur_parent.setChild1(child);
} else if (cur_parent.getChild2() == null) {
cur_parent.setChild2(child);
} else {
// Create auxiliary node so as to represent
// a instruction with arity > 2 in a binary tree.
AbstractBURS_TreeNode child1 = cur_parent.getChild2();
AbstractBURS_TreeNode aux = AbstractBURS_TreeNode.create(OTHER_OPERAND_opcode);
cur_parent.setChild2(aux);
cur_parent = aux;
cur_parent.setChild1(child1);
cur_parent.setChild2(child);
}
} // for (uses = ...)
// patch for calls & return
switch (instr.getOpcode()) {
case CALL_opcode:
case SYSCALL_opcode:
case YIELDPOINT_OSR_opcode:
if (cur_parent.getChild2() == null) {
cur_parent.setChild2(NullTreeNode);
}
// fall through
case RETURN_opcode:
if (cur_parent.getChild1() == null) {
cur_parent.setChild1(NullTreeNode);
}
}
if (mustBeTreeRoot(n)) {
makeTreeRoot(castNode(n).getCurrentParent());
}
}
}
/**
* Stage 1b: Do bookkeeping to make it easier to identify
* harmless problem edges.
*/
private void problemEdgePrep() {
for (int i = 0; i < numTreeRoots; i++) {
AbstractBURS_TreeNode n = treeRoots[i];
problemEdgePrep(n, n.dg_node);
}
}
private void problemEdgePrep(AbstractBURS_TreeNode n, SpaceEffGraphNode root) {
AbstractBURS_TreeNode child1 = n.child1;
AbstractBURS_TreeNode child2 = n.child2;
if (child1 != null && !child1.isTreeRoot()) {
problemEdgePrep(child1, root);
}
if (child2 != null && !child2.isTreeRoot()) {
problemEdgePrep(child2, root);
}
if (n.dg_node != null) {
n.dg_node.nextSorted = root;
castNode(n.dg_node).setPredecessorCount(0);
}
}
/**
* Stage 1c: Mark src node of some problem edges as tree roots to avoid
* cyclic dependencies.
*/
private void handleProblemEdges() {
// Stage 1: Remove problem edges whose destination
// is the root of their own tree; these edges
// are trivially redundant with reg-true edges.
int remaining = 0;
for (int i = 0; i < numProblemEdges; i++) {
SpaceEffGraphEdge e = problemEdges[i];
SpaceEffGraphNode src = e.fromNode();
SpaceEffGraphNode dst = e.toNode();
SpaceEffGraphNode srcRoot = src.nextSorted;
if (srcRoot != dst) {
// might still be trouble
problemEdges[remaining++] = e;
}
}
numProblemEdges = remaining;
if (numProblemEdges == 0) return;
// Still some edges that might introduce cycles.
int searchnum = 0;
for (int i = 0; i < numProblemEdges; i++) {
SpaceEffGraphEdge e = problemEdges[i];
SpaceEffGraphNode src = e.fromNode();
SpaceEffGraphNode dst = e.toNode();
AbstractBURS_TreeNode n = castNode(src).getCurrentParent();
if (n.isTreeRoot()) continue; // some other problem edge already forced it
SpaceEffGraphNode srcRoot = src.nextSorted;
SpaceEffGraphNode dstRoot = dst.nextSorted;
if (srcRoot == dstRoot && srcRoot != dst) {
// potential for intra-tree cycle
if (!trueEdgeRedundant(src, dst, srcRoot)) {
if (DEBUG) {
VM.sysWriteln("Potential intra-tree cycle with edge " + e + " forcing " + n + " to be a tree root");
}
makeTreeRoot(n);
problemEdgePrep(n, n.dg_node);
}
} else {
// potential for inter-tree cycle
if (reachableRoot(dstRoot, srcRoot, ++searchnum)) {
if (DEBUG) {
VM.sysWriteln("Potential inter-tree cycle with edge " + e + " forcing " + n + " to be a tree root");
}
makeTreeRoot(n);
problemEdgePrep(n, n.dg_node);
}
}
}
}
// routine to identify harmless intra-tree edges.
// if the edge is redundant wrt regTrue edges, then it
// can be ignored.
private boolean trueEdgeRedundant(SpaceEffGraphNode current, SpaceEffGraphNode goal,
SpaceEffGraphNode root) {
if (current == goal) return true;
if (current.nextSorted != root) return false; // don't cross tree boundaries
for (SpaceEffGraphEdge out = current.firstOutEdge(); out != null; out = out.getNextOut()) {
if (DepGraphEdge.isRegTrue(out) && trueEdgeRedundant(out.toNode(), goal, root)) {
return true;
}
}
return false;
}
// routine to identify harmless inter-tree edges.
// Is goal reachable via any edge in the current tree?
private boolean reachableRoot(SpaceEffGraphNode current, SpaceEffGraphNode goal, int searchnum) {
if (current == goal) return true;
if (castNode(current).getPredecessorCount() == searchnum) return false;
castNode(current).setPredecessorCount(searchnum);
AbstractBURS_TreeNode root = castNode(current).getCurrentParent();
return reachableChild(root, goal, searchnum);
}
private boolean reachableChild(AbstractBURS_TreeNode n, SpaceEffGraphNode goal, int searchnum) {
SpaceEffGraphNode dgn = n.dg_node;
if (dgn != null) {
for (SpaceEffGraphEdge out = dgn.firstOutEdge(); out != null; out = out.getNextOut()) {
if (reachableRoot(out.toNode().nextSorted, goal, searchnum)) return true;
}
}
if (n.getChild1() != null && !n.getChild1().isTreeRoot() && reachableChild(n.getChild1(), goal, searchnum)) {
return true;
}
if (n.getChild2() != null && !n.getChild2().isTreeRoot() && reachableChild(n.getChild2(), goal, searchnum)) {
return true;
}
return false;
}
/**
* Stage 2: Construct topological ordering of tree roots based on the
* dependencies between nodes in the tree.
*
* @param dg The dependence graph.
*/
private void orderTrees(DepGraph dg) {
// Initialize tree root field for all nodes
for (int i = 0; i < numTreeRoots; i++) {
AbstractBURS_TreeNode n = treeRoots[i];
castNode(n.dg_node).setPredecessorCount(0);
initTreeRootNode(n, n.dg_node);
}
// Initialize predCount[*]
for (SpaceEffGraphNode node = dg.firstNode(); node != null; node = node.getNext()) {
SpaceEffGraphNode n_treeRoot = node.nextSorted;
for (SpaceEffGraphEdge in = node.firstInEdge(); in != null; in = in.getNextIn()) {
SpaceEffGraphNode source_treeRoot = in.fromNode().nextSorted;
if (source_treeRoot != n_treeRoot) {
castNode(n_treeRoot).incPredecessorCount();
}
}
}
if (DEBUG) {
for (int i = 0; i < numTreeRoots; i++) {
AbstractBURS_TreeNode n = treeRoots[i];
VM.sysWriteln(castNode(n.dg_node).getPredecessorCount() + ":" + n);
}
}
}
/**
* Stage 3: Label the trees with their min cost cover.
*/
private void labelTrees() {
for (int i = 0; i < numTreeRoots; i++) {
AbstractBURS_TreeNode n = treeRoots[i];
label(n);
mark(n, /* goalnt -stm_NT */ (byte)1);
}
}
/**
* Stage 4: Visit the tree roots in topological order and
* emit MIR instructions by calling BURS_StateCoder.code on each
* supernode in the tree.
*
* @param burs the BURS_StateCoder object.
*/
private void generateTrees(BURS_StateCoder burs) {
// Append tree roots with predCount = 0 to readySet
for (int i = 0; i < numTreeRoots; i++) {
AbstractBURS_TreeNode n = treeRoots[i];
if (castNode(n.dg_node).getPredecessorCount() == 0) {
readySetInsert(n);
}
}
// Emit code for each tree root in readySet
while (readySetNotEmpty()) {
AbstractBURS_TreeNode k = readySetRemove();
// Invoke burs.code on the supernodes of k in a post order walk of the
// tree (thus code for children is emited before code for the parent).
if (DEBUG) {
VM.sysWriteln("PROCESSING TREE ROOTED AT " + k.dg_node);
dumpTree(k);
}
numTreeRoots--;
generateTree(k, burs);
}
if (numTreeRoots != 0) {
throw new OptimizingCompilerException("BURS", "Not all tree roots were processed");
}
}
// Generate code for a single tree root.
// Also process inter-tree dependencies from this tree to other trees.
private void generateTree(AbstractBURS_TreeNode k, BURS_StateCoder burs) {
AbstractBURS_TreeNode child1 = k.child1;
AbstractBURS_TreeNode child2 = k.child2;
if (child1 != null) {
if (child2 != null) {
// k has two children; use register labeling to
// determine order that minimizes register pressure
if (k.isSuperNodeRoot()) {
byte act = action(k.rule(k.getNonTerminal()));
if ((act & BURS_StateCoder.LEFT_CHILD_FIRST) != 0) {
// rule selected forces order of evaluation
generateTree(child1, burs);
generateTree(child2, burs);
} else if ((act & BURS_StateCoder.RIGHT_CHILD_FIRST) != 0) {
// rule selected forces order of evaluation
generateTree(child2, burs);
generateTree(child1, burs);
} else if (child2.numRegisters() > child1.numRegisters()) {
generateTree(child2, burs);
generateTree(child1, burs);
} else {
generateTree(child1, burs);
generateTree(child2, burs);
}
} else {
if (child2.numRegisters() > child1.numRegisters()) {
generateTree(child2, burs);
generateTree(child1, burs);
} else {
generateTree(child1, burs);
generateTree(child2, burs);
}
}
} else {
generateTree(child1, burs);
}
} else if (child2 != null) {
generateTree(child2, burs);
}
if (k.isSuperNodeRoot()) {
int nonterminal = k.getNonTerminal();
int rule = k.rule(nonterminal);
burs.code(k, nonterminal, rule);
if (DEBUG) VM.sysWriteln(k + " " + debug(rule));
}
DepGraphNode dgnode = k.dg_node;
if (dgnode != null) {
SpaceEffGraphNode source = dgnode.nextSorted;
for (SpaceEffGraphEdge out = dgnode.firstOutEdge(); out != null; out = out.getNextOut()) {
SpaceEffGraphNode dest = out.toNode().nextSorted;
if (source != dest) {
castNode(dest).decPredecessorCount();
int count = castNode(dest).getPredecessorCount();
if (DEBUG) VM.sysWriteln(count + ": edge " + source + " to " + dest);
if (count == 0) {
readySetInsert(castNode(dest).getCurrentParent());
}
}
}
}
}
/**
* Checks if the given node needs to be a tree rode.
* If the node does not need to be a tree root right now
* but might later have to be marked as a tree
* root, then include in a set of problem nodes.
*
* @param n the dep graph node in question.
* @return {@code true} if node n must be a root of a BURS tree
* based only on its register true dependencies.
*/
private boolean mustBeTreeRoot(DepGraphNode n) {
// A "fan-out" node must be a root of a BURS tree.
// (A fan-out node is a node with > 1 outgoing register-true dependences)
SpaceEffGraphEdge trueDepEdge = null;
for (SpaceEffGraphEdge out = n.firstOutEdge(); out != null; out = out.getNextOut()) {
if (DepGraphEdge.isRegTrue(out)) {
if (trueDepEdge != null) return true;
trueDepEdge = out;
}
}
if (trueDepEdge == null) {
return true; // 0 outgoing register-true dependencies
} else {
// Exactly 1 true edge, since we would have bailed out of above
// loop if we'd found a second one.
// If the node produces a register value that is live on exit
// from the basic block then it must be the root of a BURS tree.
Instruction instr = n.instruction();
if (instr.operator() == IR_PROLOGUE) return true;
RegisterOperand rop = ResultCarrier.getResult(instr);
if (rop.getRegister().spansBasicBlock()) return true;
SpaceEffGraphNode parent = trueDepEdge.toNode();
// If our parent has a superset of our
// other out edges (ignoring trueDepEdge)
// then we don't have to worry about creating cycles
// by not forcing n to be a tree root.
for (SpaceEffGraphEdge out = n.firstOutEdge(); out != null; out = out.getNextOut()) {
if (out != trueDepEdge) {
boolean match = false;
for (SpaceEffGraphEdge out2 = parent.firstOutEdge(); out2 != null; out2 = out2.getNextOut()) {
if (out2.toNode() == out.toNode()) {
match = true;
break;
}
}
if (!match) {
// could be trouble. Remember for later processing.
rememberAsProblemEdge(out);
}
}
}
return false;
}
}
// NOTE: assumes n has exactly 1 reg true parent (ie it is in
// an expression tree and is not the tree root).
@SuppressWarnings("unused")
private SpaceEffGraphNode regTrueParent(SpaceEffGraphNode n) {
for (SpaceEffGraphEdge out = n.firstOutEdge(); out != null; out = out.getNextOut()) {
if (DepGraphEdge.isRegTrue(out)) {
return out.toNode();
}
}
if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED);
return null;
}
/**
* Initialize nextSorted for nodes in tree rooted at t i.e.
* for all register true descendants of t up to but not including
* any new tree roots.
*
* @param t the BURS node
* @param treeRoot the dependence graph node that belongs to the BURS node
*/
private void initTreeRootNode(AbstractBURS_TreeNode t, SpaceEffGraphNode treeRoot) {
// Recurse
if (t.getChild1() != null) {
if (t.getChild1().isTreeRoot()) {
t.setChild1(Register);
} else {
initTreeRootNode(t.getChild1(), treeRoot);
}
}
if (t.getChild2() != null) {
if (t.getChild2().isTreeRoot()) {
t.setChild2(Register);
} else {
initTreeRootNode(t.getChild2(), treeRoot);
}
}
if (t.dg_node != null) {
t.dg_node.nextSorted = treeRoot;
if (DEBUG) VM.sysWriteln(t.dg_node + " --> " + treeRoot);
}
if (t.getChild1() != null || t.getChild2() != null) {
// label t as in section 9.10 of the dragon book
int lchild = (t.getChild1() != null) ? t.getChild1().numRegisters() : 0;
int rchild = (t.getChild2() != null) ? t.getChild2().numRegisters() : 0;
if (lchild == rchild) {
t.setNumRegisters(lchild + 1);
} else {
t.setNumRegisters(Math.max(lchild, rchild));
}
if (DEBUG) VM.sysWrite("\tnum registers = " + t.numRegisters());
}
}
/**
* Set of all tree roots.
*/
private AbstractBURS_TreeNode[] treeRoots = new AbstractBURS_TreeNode[32];
private void makeTreeRoot(AbstractBURS_TreeNode n) {
if (numTreeRoots == treeRoots.length) {
AbstractBURS_TreeNode[] tmp = new AbstractBURS_TreeNode[treeRoots.length * 2];
for (int i = 0; i < treeRoots.length; i++) {
tmp[i] = treeRoots[i];
}
treeRoots = tmp;
}
n.setTreeRoot();
treeRoots[numTreeRoots++] = n;
}
/**
* A priority queue of ready tree nodes.
* Used to keep track of the tree roots that are ready to be
* emitted during code generation (since all of their
* predecessors have been emitted already).
* readySetRemove returns the node that uses the maximum
* number of registers. This is a heuristic that tends to
* reduce register pressure and enable coalescing by the
* register allocator. (Goal is to end live ranges 'early').
*/
private AbstractBURS_TreeNode[] heap = new AbstractBURS_TreeNode[16];
private int numElements = 0;
private void readySetInsert(AbstractBURS_TreeNode node) {
Instruction s = node.getInstruction();
if (s.operator() == GUARD_COMBINE ||
s.operator() == GUARD_COND_MOVE ||
s.operator() == GUARD_MOVE ||
!ResultCarrier.conforms(s)) {
// Adjust numRegisters to bias away from picking trees that
// are rooted in result carriers, since they start a new live
// range. We don't count guard operations as result carriers, since
// guard operations get wiped out before register allocation anyways.
node.setNumRegisters(node.numRegisters() + 2);
}
numElements++;
if (numElements == heap.length) {
AbstractBURS_TreeNode[] tmp = new AbstractBURS_TreeNode[heap.length * 2];
for (int i = 0; i < heap.length; i++) {
tmp[i] = heap[i];
}
heap = tmp;
}
// restore heap property
int current = numElements;
heap[current] = node;
for (int parent = current / 2; current > 1 && heap[current].numRegisters() > heap[parent].numRegisters(); current =
parent, parent = current / 2) {
swap(current, parent);
}
}
private boolean readySetNotEmpty() {
return numElements > 0;
}
private AbstractBURS_TreeNode readySetRemove() {
AbstractBURS_TreeNode ans = heap[1];
heap[1] = heap[numElements--];
heapify(1);
return ans;
}
private void heapify(int p) {
int l = p * 2;
int r = l + 1;
int max = p;
if (l <= numElements && heap[l].numRegisters() > heap[max].numRegisters()) {
max = l;
}
if (r <= numElements && heap[r].numRegisters() > heap[max].numRegisters()) {
max = r;
}
if (max != p) {
swap(p, max);
heapify(max);
}
}
private void swap(int x, int y) {
AbstractBURS_TreeNode t = heap[x];
heap[x] = heap[y];
heap[y] = t;
}
void rememberAsProblemEdge(SpaceEffGraphEdge e) {
if (problemEdges == null) {
problemEdges = new SpaceEffGraphEdge[8];
}
if (numProblemEdges == problemEdges.length) {
SpaceEffGraphEdge[] tmp = new SpaceEffGraphEdge[problemEdges.length * 2];
for (int i = 0; i < problemEdges.length; i++) {
tmp[i] = problemEdges[i];
}
problemEdges = tmp;
}
problemEdges[numProblemEdges++] = e;
}
private boolean haveProblemEdges() {
return numProblemEdges > 0;
}
}