/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Common Public License (CPL);
* 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/cpl1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.compilers.opt;
import java.util.Enumeration;
import org.jikesrvm.VM;
import org.jikesrvm.classloader.VM_TypeReference;
import static org.jikesrvm.compilers.opt.OPT_Constants.SSA_SYNTH_BCI;
import org.jikesrvm.compilers.opt.ir.BBend;
import org.jikesrvm.compilers.opt.ir.Move;
import org.jikesrvm.compilers.opt.ir.OPT_BasicBlock;
import org.jikesrvm.compilers.opt.ir.OPT_BasicBlockEnumeration;
import org.jikesrvm.compilers.opt.ir.OPT_BasicBlockOperand;
import org.jikesrvm.compilers.opt.ir.OPT_ConstantOperand;
import org.jikesrvm.compilers.opt.ir.OPT_HeapOperand;
import org.jikesrvm.compilers.opt.ir.OPT_IR;
import org.jikesrvm.compilers.opt.ir.OPT_IRTools;
import org.jikesrvm.compilers.opt.ir.OPT_Instruction;
import org.jikesrvm.compilers.opt.ir.OPT_InstructionEnumeration;
import org.jikesrvm.compilers.opt.ir.OPT_Operand;
import org.jikesrvm.compilers.opt.ir.OPT_OperandEnumeration;
import org.jikesrvm.compilers.opt.ir.OPT_Operator;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.PHI;
import org.jikesrvm.compilers.opt.ir.OPT_Register;
import org.jikesrvm.compilers.opt.ir.OPT_RegisterOperand;
import org.jikesrvm.compilers.opt.ir.Phi;
/**
* This module holds utility functions for SSA form.
*
* 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 OPT_SSADictionary <code> ir.HIRInfo.SSADictionary </code>}.
* dphi functions do <em> not </em>
* explicitly appear in the IR.
* <p>
* For example, consider the code:
* <p>
* <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.SSADictionary </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
* OPT_SSADictionary data structure keeps the heap control phi
* functions for each basic block in a lookaside table.
* </ul>
*
* @see OPT_EnterSSA
* @see OPT_LeaveSSA
* @see OPT_SSADictionary
* @see org.jikesrvm.compilers.opt.ir.OPT_HIRInfo
*/
class OPT_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(OPT_IR ir, OPT_BasicBlock bb, OPT_Instruction c, boolean exp) {
if (exp) {
bb.appendInstructionRespectingTerminalBranchOrPEI(c);
} else {
bb.appendInstructionRespectingTerminalBranch(c);
}
OPT_RegisterOperand aux = null;
if (VM.VerifyAssertions) {
VM._assert(Move.conforms(c));
}
OPT_RegisterOperand lhs = Move.getResult(c);
OPT_Instruction i = c.nextInstructionInCodeOrder();
while (!BBend.conforms(i)) {
OPT_OperandEnumeration os = i.getUses();
while (os.hasMoreElements()) {
OPT_Operand op = os.next();
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(OPT_IR ir) {
OPT_SSADictionary dictionary = ir.HIRInfo.SSADictionary;
System.out.println("********* START OF IR DUMP in SSA FOR " + ir.method);
for (OPT_BasicBlockEnumeration be = ir.forwardBlockEnumerator(); be.hasMoreElements();) {
OPT_BasicBlock bb = be.next();
// print the explicit instructions for basic block bb
for (Enumeration<OPT_Instruction> e = dictionary.getAllInstructions(bb); e.hasMoreElements();) {
OPT_Instruction s = e.nextElement();
System.out.print(s.bcIndex + "\t" + s);
if (dictionary.defsHeapVariable(s) && s.operator != PHI) {
System.out.print(" (Implicit Defs: ");
OPT_HeapOperand<?>[] defs = dictionary.getHeapDefs(s);
if (defs != null) {
for (OPT_HeapOperand<?> def : defs) System.out.print(def + " ");
}
System.out.print(" )");
}
if (dictionary.usesHeapVariable(s) && s.operator != PHI) {
System.out.print(" (Implicit Uses: ");
OPT_HeapOperand<?>[] uses = dictionary.getHeapUses(s);
if (uses != null) {
for (OPT_HeapOperand<?> use : uses) System.out.print(use + " ");
}
System.out.print(" )");
}
System.out.print("\n");
}
}
System.out.println("********* END OF IR DUMP in SSA FOR " + ir.method);
}
/**
* Create a move instruction r1 := r2.
*
* 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.
*/
static OPT_Instruction makeMoveInstruction(OPT_IR ir, OPT_Register r1, OPT_Register r2, VM_TypeReference t) {
OPT_Operator mv = OPT_IRTools.getMoveOp(t);
OPT_RegisterOperand o1 = new OPT_RegisterOperand(r1, t);
OPT_RegisterOperand o2 = new OPT_RegisterOperand(r2, t);
OPT_Instruction s = Move.create(mv, o1, o2);
s.position = ir.gc.inlineSequence;
s.bcIndex = SSA_SYNTH_BCI;
return s;
}
/**
* Create a move instruction r1 := c.
*
* !!TODO: put this functionality elsewhere.
*
* @param ir the governing ir
* @param r1 the destination
* @param c the source
*/
static OPT_Instruction makeMoveInstruction(OPT_IR ir, OPT_Register r1, OPT_ConstantOperand c) {
OPT_Operator mv = OPT_IRTools.getMoveOp(c.getType());
OPT_RegisterOperand o1 = new OPT_RegisterOperand(r1, c.getType());
OPT_Operand o2 = c.copy();
OPT_Instruction s = Move.create(mv, o1, o2);
s.position = ir.gc.inlineSequence;
s.bcIndex = SSA_SYNTH_BCI;
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(OPT_BasicBlock source, OPT_BasicBlock target) {
for (OPT_InstructionEnumeration e = target.forwardRealInstrEnumerator(); e.hasMoreElements();) {
OPT_Instruction s = e.next();
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++) {
OPT_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(OPT_BasicBlock target, OPT_BasicBlock B1, OPT_BasicBlock B2) {
for (OPT_InstructionEnumeration e = target.forwardRealInstrEnumerator(); e.hasMoreElements();) {
OPT_Instruction s = e.next();
if (s.operator() != PHI) return; // all done (assume PHIs are first!)
int numPairs = Phi.getNumberOfPreds(s);
for (int src = 0; src < numPairs; src++) {
OPT_BasicBlockOperand bbop = Phi.getPred(s, src);
if (bbop.block == B1) {
Phi.setPred(s, src, new OPT_BasicBlockOperand(B2));
}
}
}
}
}