/* This file is part of the db4o object database http://www.db4o.com
Copyright (C) 2004 - 2011 Versant Corporation http://www.versant.com
db4o is free software; you can redistribute it and/or modify it under
the terms of version 3 of the GNU General Public License as published
by the Free Software Foundation.
db4o is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see http://www.gnu.org/licenses/. */
package EDU.purdue.cs.bloat.trans;
import java.util.*;
import EDU.purdue.cs.bloat.cfg.*;
import EDU.purdue.cs.bloat.editor.*;
import EDU.purdue.cs.bloat.tree.*;
import EDU.purdue.cs.bloat.util.*;
/**
* Attempts to remove residency checks an update checks from a control flow
* graph.
*/
public class PersistentCheckElimination {
public static boolean DEBUG = false;
private static final int RC = 0;
private static final int AUPDATE = 1;
private static final int SUPDATE = 2;
private static final int SIZE = 3; // Number of persistent opcodes
private EditorContext context;
/**
* Examines each residency check (<Tt>RCExpr</tt>) and update check (<tt>UCExpr</tt>)
* and determines whether or it is redundent. If a residency check checks
* something that we know is resident (i.e. the this pointer or the result
* of an object creation), then the check is redundent. Once an update check
* has been performed on a value, all subsequent checks are redundent.
* Redundent checks are removed from the control flow graph.
*
* @see RCExpr
* @see UCExpr
*/
public void transform(final FlowGraph cfg) {
context = cfg.method().declaringClass().context();
final BitSet[] seen = new BitSet[PersistentCheckElimination.SIZE];
for (int i = 0; i < PersistentCheckElimination.SIZE; i++) {
seen[i] = new BitSet();
}
search(cfg, cfg.source(), seen);
}
/**
* Recursively searches the tree for residency statements that can be
* removed. The value numbers of expressions that create new objects are
* noted. The value number of the "this" pointer is also noted. If a
* residency check (RCExpr) is performed on any of these expressions, it is
* redundent and can be removed.
*
* <p>
*
* When an update check (UCExpr) is encountered each of its children is
* visited. The value numbers of expressions that are checked (i.e. wrapped
* in UCExpr) are noted. If an UCExpr is encountered that checks an
* expression with one of these value numbers, the check is redundent and
* can be removed.
*
* @param seen
* An array containing a BitSet for each type of
* residency-related instruction (RC, AUPDATE, SUPDATE). Each bit
* in the BitSet corresponds to an expression involving residency
* being visited. That is, its value number is "seen".
*/
private void search(final FlowGraph cfg, final Block block,
final BitSet[] seen) {
// Accumulate the information stored in seen...
// Save a copy of the contens of seen
final BitSet[] save = new BitSet[PersistentCheckElimination.SIZE];
for (int i = 0; i < PersistentCheckElimination.SIZE; i++) {
save[i] = new BitSet(seen[i].size());
save[i].or(seen[i]);
}
// Visit each expression in the tree.
block.visit(new TreeVisitor() {
// When we reach an expression that creates a new object
// (NewArrayExpr, NewMultiArrayExpr, or NewExpr), set the bit in
// the RC bit vector corresponding to the expression's value
// number.
public void visitNewArrayExpr(final NewArrayExpr expr) {
expr.visitChildren(this);
final int v = expr.valueNumber();
Assert.isTrue(v != -1);
seen[PersistentCheckElimination.RC].set(v);
}
public void visitNewMultiArrayExpr(final NewMultiArrayExpr expr) {
expr.visitChildren(this);
final int v = expr.valueNumber();
Assert.isTrue(v != -1);
seen[PersistentCheckElimination.RC].set(v);
}
public void visitNewExpr(final NewExpr expr) {
expr.visitChildren(this);
final int v = expr.valueNumber();
Assert.isTrue(v != -1);
seen[PersistentCheckElimination.RC].set(v);
}
// Find the value number of the LocalExpr corresponding to the
// "this" variable. Set the bit in the RC bit vector
// corresponding to the "this" pointer's value number.
public void visitInitStmt(final InitStmt stmt) {
stmt.visitChildren(this);
final MethodEditor method = stmt.block().graph().method();
if (!method.isStatic()) {
Assert.isTrue(stmt.targets().length > 0);
final int v = stmt.targets()[0].valueNumber();
Assert.isTrue(v != -1);
seen[PersistentCheckElimination.RC].set(v);
}
}
// If the expression being checked by the RCExpr is either the
// result of a "new" expression or it is the "this" pointer, we
// know that it is resident and thus does not need to be
// checked. All occurrences of the RCExpr are replaced with the
// expression being checked. All of this is contingent on the
// fact that the check does not have an ALIAS side effect.
public void visitRCExpr(final RCExpr expr) {
expr.visitChildren(this);
final int v = expr.expr().valueNumber();
Assert.isTrue(v != -1);
final SideEffectChecker sideEffects = new SideEffectChecker(
context);
expr.expr().visit(sideEffects);
final int flag = sideEffects.sideEffects();
if (seen[PersistentCheckElimination.RC].get(v)
&& ((flag & SideEffectChecker.ALIAS) == 0)) {
// rc(x) --> x
expr.expr().setParent(null);
expr.replaceWith(expr.expr(), false);
expr.cleanupOnly();
}
seen[PersistentCheckElimination.RC].set(v);
}
// If an object has already been updated, it does not need to be
// updated again. Note that the children of the UCExpr are
// visited first. There is a seen bit vector for both AUPDATE
// and SUPDATE UCExpr. If the value number of the expression
// being checked has already been encountered in a UCExpr, then
// the UCExpr is redundent and can be replaced with the
// expression being checked provided that it has no ALIAS side
// effects.
public void visitUCExpr(final UCExpr expr) {
expr.visitChildren(this);
final int v = expr.expr().valueNumber();
Assert.isTrue(v != -1);
final SideEffectChecker sideEffects = new SideEffectChecker(
context);
expr.expr().visit(sideEffects);
final int flag = sideEffects.sideEffects();
if (expr.kind() == UCExpr.POINTER) {
if (seen[PersistentCheckElimination.AUPDATE].get(v)
&& ((flag & SideEffectChecker.ALIAS) == 0)) {
// aupdate(x) --> x
expr.expr().setParent(null);
expr.replaceWith(expr.expr(), false);
expr.cleanupOnly();
}
seen[PersistentCheckElimination.AUPDATE].set(v);
} else {
if (seen[PersistentCheckElimination.SUPDATE].get(v)
&& ((flag & SideEffectChecker.ALIAS) == 0)) {
// supdate(x) --> x
expr.expr().setParent(null);
expr.replaceWith(expr.expr(), false);
expr.cleanupOnly();
}
seen[PersistentCheckElimination.SUPDATE].set(v);
}
}
});
// Visit the blocks in the dominator tree in pre-order
final Iterator children = cfg.domChildren(block).iterator();
while (children.hasNext()) {
final Block child = (Block) children.next();
search(cfg, child, seen);
}
for (int i = 0; i < PersistentCheckElimination.SIZE; i++) {
seen[i] = save[i];
}
}
}