// Copyright (c) 2011, David J. Pearce (djp@ecs.vuw.ac.nz)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the <organization> nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL DAVID J. PEARCE BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package jasm.util;
import jasm.attributes.*;
import jasm.lang.*;
import java.util.*;
/**
* The purpose of this pass is to eliminate any statically determinable
* dead-code. This is required to generate correct java bytecode. For example,
* consider the following Java code:
*
* <pre>
* void f(...) {
* if(...) {
* return ...;
* else {
* return ...;
* }
* }
* </pre>
*
* Then, the front-end will produce the following (pseudo) bytecode:
*
* <pre>
* void f(...) {
* if(...) goto iftrue0;
* return ...;
* goto ifexit0;
* iftrue0:
* return ...;
* ifexit0:
* }
* </pre>
*
* Here, we can see quite clearly that the code after the first return
* statement, as well as the ifexit label, is dead.
*
* @author David J. Pearce
*
*/
public class DeadCodeElimination {
public void apply(ClassFile cf) {
for(ClassFile.Method m : cf.methods()) {
apply(m);
}
}
public void apply(ClassFile.Method method) {
for (BytecodeAttribute a : method.attributes()) {
if(a instanceof Code) {
apply((Code)a);
}
}
}
public void apply(Code code) {
List<Bytecode> bytecodes = code.bytecodes();
// First, perform depth-first search of method starting from first
// bytecode. This identifies those bytecodes which are reachable from
// the method's entry point.
HashSet<Integer> visited = new HashSet<Integer>();
HashMap<String,Integer> labelMap = buildLabelMap(bytecodes);
visit(0,visited,labelMap,bytecodes);
// now, visit handlers as well
for(Code.Handler handler : code.handlers()) {
int target = labelMap.get(handler.label);
visit(target,visited,labelMap,bytecodes);
}
// Second, for any unreachable bytecode, add a rewrite which simply
// deletes it.
ArrayList<Code.Rewrite> rewrites = new ArrayList<Code.Rewrite>();
for(int i=0;i!=bytecodes.size();++i) {
if(!visited.contains(i)) {
rewrites.add(new Code.Rewrite(i,1));
}
}
code.apply(rewrites);
}
protected void visit(int index, HashSet<Integer> visited,
HashMap<String,Integer> labels,
List<Bytecode> bytecodes) {
while(index < bytecodes.size() && !visited.contains(index)) {
visited.add(index);
Bytecode b = bytecodes.get(index);
if(b instanceof Bytecode.Goto) {
Bytecode.Goto g = (Bytecode.Goto) b;
int i = labels.get(g.label);
if(!visited.contains(i)) {
visit(i,visited,labels,bytecodes);
}
return;
} else if(b instanceof Bytecode.Branch) {
Bytecode.Branch g = (Bytecode.Branch) b;
int i = labels.get(g.label);
if(!visited.contains(i)) {
visit(i,visited,labels,bytecodes);
}
} else if(b instanceof Bytecode.Switch) {
Bytecode.Switch sw = (Bytecode.Switch) b;
if (!visited.contains(labels.get(sw.defaultLabel))) {
visit(labels.get(sw.defaultLabel), visited, labels,
bytecodes);
}
for (Pair<Integer, String> p : sw.cases) {
int i = labels.get(p.second());
visit(i, visited, labels, bytecodes);
}
} else if (b instanceof Bytecode.Return
|| b instanceof Bytecode.Throw) {
// return + throw statements are simply dead-ends
return;
}
index = index + 1;
}
}
protected HashMap<String,Integer> buildLabelMap(List<Bytecode> bytecodes) {
HashMap<String,Integer> map = new HashMap<String,Integer>();
for(int i=0;i!=bytecodes.size();++i) {
Bytecode b = bytecodes.get(i);
if(b instanceof Bytecode.Label) {
Bytecode.Label lab = (Bytecode.Label) b;
map.put(lab.name, i);
}
}
return map;
}
}