/*
* 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.ssa;
import java.util.Enumeration;
import org.jikesrvm.VM;
import org.jikesrvm.classloader.TypeReference;
import org.jikesrvm.compilers.opt.ir.BBend;
import org.jikesrvm.compilers.opt.ir.Move;
import org.jikesrvm.compilers.opt.ir.BasicBlock;
import org.jikesrvm.compilers.opt.ir.IR;
import org.jikesrvm.compilers.opt.ir.IRTools;
import org.jikesrvm.compilers.opt.ir.Instruction;
import org.jikesrvm.compilers.opt.ir.Operator;
import static org.jikesrvm.compilers.opt.driver.OptConstants.SSA_SYNTH_BCI;
import static org.jikesrvm.compilers.opt.ir.Operators.PHI;
import org.jikesrvm.compilers.opt.ir.Register;
import org.jikesrvm.compilers.opt.ir.Phi;
import org.jikesrvm.compilers.opt.ir.operand.BasicBlockOperand;
import org.jikesrvm.compilers.opt.ir.operand.ConstantOperand;
import org.jikesrvm.compilers.opt.ir.operand.HeapOperand;
import org.jikesrvm.compilers.opt.ir.operand.Operand;
import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand;
/**
* This module holds utility functions for SSA form.
*
* <p> Our SSA form is <em> Heap Array SSA Form </em>, an extension of
* SSA that allows analysis of scalars, arrays, and object fields
* in a unified framework. See our SAS 2000 paper
* <a href="http://www.research.ibm.com/jalapeno/publication.html#sas00">
* Unified Analysis of Arrays and Object References in Strongly Typed
* Languages </a>
* <p> Details about our current implementation include:
* <ul>
* <li> We explicitly place phi-functions as instructions in the IR.
* <li> Scalar registers are explicitly renamed in the IR.
* <li> Heap variables are represented implicitly. Each instruction
* that reads or writes from the heap implicitly uses a Heap variable.
* The particular heap variable for each instruction is cached
* in {@link org.jikesrvm.compilers.opt.ir.HIRInfo#dictionary} which
* is reachable via the IR object.
*
* dphi functions do <em> not </em>
* explicitly appear in the IR.
* <p>
* For example, consider the code:
* <pre>
* a.x = z;
* b[100] = 5;
* y = a.x;
* </pre>
*
* <p>Logically, we translate to Array SSA form (before renumbering) as:
* <pre>
* HEAP_x[a] = z
* HEAP_x = dphi(HEAP_x,HEAP_x)
* HEAP_I[] = { < b,100,5 > }
* HEAP_I[] = dphi(HEAP_I[], HEAP_I[])
* y = HEAP_x[a]
* </pre>
*
* <p> However, the implementation does not actually modify the instruction
* stream. Instead, we keep track of the following information with
* <code> ir.HIRInfo.dictionary </code>:
* <pre>
* a.x = z (implicit: reads HEAP_x, writes HEAP_x)
* b[100] =5 (implicit: reads HEAP_I[], writes HEAP_I[])
* y = a.x (implicit: reads HEAP_x)
* </pre>
*
* <p>Similarly, phi functions for the implicit heap
* variables <em> will not </em>
* appear explicitly in the instruction stream. Instead, the
* SSADictionary data structure keeps the heap control phi
* functions for each basic block in a lookaside table.
* </ul>
*
* @see EnterSSA
* @see LeaveSSA
* @see SSADictionary
* @see org.jikesrvm.compilers.opt.ir.HIRInfo
*/
class SSA {
/**
* Add a move instruction at the end of a basic block, renaming
* with a temporary register if needed to protect conditional branches
* at the end of the block.
*
* @param ir governing IR
* @param bb the basic block
* @param c the move instruction to insert
* @param exp whether or not to respect exception control flow at the
* end of the block
*/
static void addAtEnd(IR ir, BasicBlock bb, Instruction c, boolean exp) {
if (exp) {
bb.appendInstructionRespectingTerminalBranchOrPEI(c);
} else {
bb.appendInstructionRespectingTerminalBranch(c);
}
RegisterOperand aux = null;
if (VM.VerifyAssertions) {
VM._assert(Move.conforms(c));
}
RegisterOperand lhs = Move.getResult(c);
Instruction i = c.nextInstructionInCodeOrder();
while (!BBend.conforms(i)) {
Enumeration<Operand> os = i.getUses();
while (os.hasMoreElements()) {
Operand op = os.nextElement();
if (lhs.similar(op)) {
if (aux == null) {
aux = ir.regpool.makeTemp(lhs);
c.insertBefore(makeMoveInstruction(ir, aux.getRegister(), lhs.getRegister(), lhs.getType()));
}
op.asRegister().setRegister(aux.getRegister());
}
}
i = i.nextInstructionInCodeOrder();
}
}
/**
* Print the instructions in SSA form.
*
* @param ir the IR, assumed to be in SSA form
*/
public static void printInstructions(IR ir) {
SSADictionary dictionary = ir.HIRInfo.dictionary;
System.out.println("********* START OF IR DUMP in SSA FOR " + ir.method);
for (Enumeration<BasicBlock> be = ir.forwardBlockEnumerator(); be.hasMoreElements();) {
BasicBlock bb = be.nextElement();
// print the explicit instructions for basic block bb
for (Enumeration<Instruction> e = dictionary.getAllInstructions(bb); e.hasMoreElements();) {
Instruction s = e.nextElement();
System.out.print(s.getBytecodeIndex() + "\t" + s);
if (dictionary.defsHeapVariable(s) && s.operator() != PHI) {
System.out.print(" (Implicit Defs: ");
HeapOperand<?>[] defs = dictionary.getHeapDefs(s);
if (defs != null) {
for (HeapOperand<?> def : defs) System.out.print(def + " ");
}
System.out.print(" )");
}
if (dictionary.usesHeapVariable(s) && s.operator() != PHI) {
System.out.print(" (Implicit Uses: ");
HeapOperand<?>[] uses = dictionary.getHeapUses(s);
if (uses != null) {
for (HeapOperand<?> use : uses) System.out.print(use + " ");
}
System.out.print(" )");
}
System.out.println();
}
}
System.out.println("********* END OF IR DUMP in SSA FOR " + ir.method);
}
/**
* Create a move instruction r1 := r2.<p>
*
* TODO: This utility function should be moved elsewhere.
*
* @param ir the governing ir
* @param r1 the destination
* @param r2 the source
* @param t the type of r1 and r2.
* @return the created move instruction
*/
static Instruction makeMoveInstruction(IR ir, Register r1, Register r2, TypeReference t) {
Operator mv = IRTools.getMoveOp(t);
RegisterOperand o1 = new RegisterOperand(r1, t);
RegisterOperand o2 = new RegisterOperand(r2, t);
Instruction s = Move.create(mv, o1, o2);
s.setSourcePosition(SSA_SYNTH_BCI, ir.getGc().getInlineSequence());
return s;
}
/**
* Create a move instruction r1 := c.<p>
*
* !!TODO: put this functionality elsewhere.
*
* @param ir the governing ir
* @param r1 the destination
* @param c the source
* @return the created move instruction
*/
static Instruction makeMoveInstruction(IR ir, Register r1, ConstantOperand c) {
Operator mv = IRTools.getMoveOp(c.getType());
RegisterOperand o1 = new RegisterOperand(r1, c.getType());
Operand o2 = c.copy();
Instruction s = Move.create(mv, o1, o2);
s.setSourcePosition(SSA_SYNTH_BCI, ir.getGc().getInlineSequence());
return s;
}
/**
* Fix up any PHI instructions in the given target block to reflect that
* the given source block is no longer a predecessor of target.
* The basic algorithm is to erase the PHI operands related to the edge
* from source to target by sliding the other PHI operands down as required.
*
* @param source the source block to remove from PHIs in target
* @param target the target block that may contain PHIs to update.
*/
static void purgeBlockFromPHIs(BasicBlock source, BasicBlock target) {
for (Enumeration<Instruction> e = target.forwardRealInstrEnumerator(); e.hasMoreElements();) {
Instruction s = e.nextElement();
if (s.operator() != PHI) return; // all done (assume PHIs are first!)
int numPairs = Phi.getNumberOfPreds(s);
int dst = 0;
for (int src = 0; src < numPairs; src++) {
BasicBlockOperand bbop = Phi.getPred(s, src);
if (bbop.block == source) {
Phi.setValue(s, src, null);
Phi.setPred(s, src, null);
} else {
if (src != dst) {
Phi.setValue(s, dst, Phi.getClearValue(s, src));
Phi.setPred(s, dst, Phi.getClearPred(s, src));
}
dst++;
}
}
for (int i = dst; i < numPairs; i++) {
Phi.setValue(s, i, null);
Phi.setPred(s, i, null);
}
}
}
/**
* Update PHI instructions in the target block so that any PHIs that
* come from basic block B1, now come from basic block B2.
*
* @param target the target block that may contain PHIs to update.
* @param B1 the block to replace in the phi instructions
* @param B2 the replacement block for B1
*/
static void replaceBlockInPhis(BasicBlock target, BasicBlock B1, BasicBlock B2) {
for (Enumeration<Instruction> e = target.forwardRealInstrEnumerator(); e.hasMoreElements();) {
Instruction s = e.nextElement();
if (s.operator() != PHI) return; // all done (assume PHIs are first!)
int numPairs = Phi.getNumberOfPreds(s);
for (int src = 0; src < numPairs; src++) {
BasicBlockOperand bbop = Phi.getPred(s, src);
if (bbop.block == B1) {
Phi.setPred(s, src, new BasicBlockOperand(B2));
}
}
}
}
}