com.googlecode.dex2jar.ir.ts.SSATransformer Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2009-2012 Panxiaobo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.googlecode.dex2jar.ir.ts;
import com.googlecode.dex2jar.ir.IrMethod;
import com.googlecode.dex2jar.ir.expr.Exprs;
import com.googlecode.dex2jar.ir.expr.Local;
import com.googlecode.dex2jar.ir.expr.Value;
import com.googlecode.dex2jar.ir.stmt.*;
import com.googlecode.dex2jar.ir.stmt.Stmt.ST;
import com.googlecode.dex2jar.ir.ts.Cfg.TravelCallBack;
import com.googlecode.dex2jar.ir.ts.an.AnalyzeValue;
import com.googlecode.dex2jar.ir.ts.an.BaseAnalyze;
import java.util.*;
/**
* Transform Stmt to SSA form and count local read
*
* @author Panxiaobo
* @version $Rev$
*/
public class SSATransformer implements Transformer {
private void cleanTagsAndReIndex(IrMethod method) {
int i = 0;
for (Local local : method.locals) {
local.tag = null;
local._ls_index = i++;
}
}
private void deleteDeadCode(IrMethod method) {
for (Iterator it = method.stmts.iterator(); it.hasNext(); ) {
Stmt stmt = it.next();
if (!stmt.visited && stmt.st != ST.LABEL) {
it.remove();
}
}
}
private void replaceLocalsWithSSA(final IrMethod method) {
final List locals = method.locals;
locals.clear();
StmtList stmts = method.stmts;
TravelCallBack tcb = new TravelCallBack() {
@Override
public Value onAssign(Local a, AssignStmt as) {
if (a._ls_index < 0) {
locals.add(a);
return a;
}
SSAValue lsv = (SSAValue) a.tag;
Local b = lsv.local;
locals.add(b);
return b;
}
@Override
public Value onUse(Local a) {
if (a._ls_index < 0) {
return a;
}
SSAValue lsv = (SSAValue) a.tag;
Local b = lsv.local;
return b;
}
};
Set froms = new HashSet<>();
List phiLabels = new ArrayList<>();
// 2. we are looking for Phis and insert Phi node to the code
for (Stmt p = stmts.getFirst(); p != null; p = p.getNext()) {
if (p.st == ST.LABEL) {
LabelStmt labelStmt = (LabelStmt) p;
List phis = null;
SSAValue[] frame = (SSAValue[]) p.frame;
if (frame != null) {
for (SSAValue v : frame) {
if (v == null || !v.used) {
continue;
}
if (v.parent != null) {
froms.add(v.parent.local);
}
if (v.otherParents != null) {
for (SSAValue parent : v.otherParents) {
froms.add(parent.local);
}
}
froms.remove(v.local);
if (phis == null) {
phis = new ArrayList<>();
}
locals.add(v.local);
phis.add(Stmts.nAssign(v.local, Exprs.nPhi(froms.toArray(new Value[froms.size()]))));
froms.clear();
}
}
labelStmt.phis = phis;
if (phis != null) {
phiLabels.add(labelStmt);
}
} else {
Cfg.travelMod(p, tcb, true);
}
p.frame = null;
}
if (phiLabels.size() > 0) {
method.phiLabels = phiLabels;
}
}
@Override
public void transform(final IrMethod method) {
boolean needSSA = prepare(method);
if (needSSA) {
// 1. analyze and build value graph
new SSAAnalyze(method).analyze();
// 2. delete dead code
deleteDeadCode(method);
// 3. replace locals with SSA-locals
replaceLocalsWithSSA(method);
}
// 4. clean tags on Local
cleanTagsAndReIndex(method);
}
private boolean prepare(final IrMethod method) {
int index = Cfg.reIndexLocal(method);
final int readCounts[] = new int[index];
final int writeCounts[] = new int[index];
Cfg.travel(method.stmts, new TravelCallBack() {
@Override
public Value onAssign(Local v, AssignStmt as) {
writeCounts[v._ls_index]++;
return v;
}
@Override
public Value onUse(Local v) {
readCounts[v._ls_index]++;
return v;
}
}, true);
boolean needTravel = false;
boolean needSSAAnalyze = false;
index = 0;
List oldLocals = method.locals;
List locals = new ArrayList<>(oldLocals);
oldLocals.clear();
for (Local local : locals) {
int idx = local._ls_index;
int read = readCounts[idx];
int write = writeCounts[idx];
if (read > 0 && write == 0) {
// TODO if we need throw exception ?
// or the code is dead?
}
if (read == 0 && write == 0) {
// ignore the local
} else {
if (write <= 1) {
// no phi require
local._ls_index = -1;
oldLocals.add(local);
} else if (read == 0) {
local._ls_index = -2;
needTravel = true;
// we are going to duplicate each usage of the local and add to method.locals,
// so not add the original local to method.locals
} else {
needSSAAnalyze = true;
local._ls_index = index++;
oldLocals.add(local);
}
}
}
if (needSSAAnalyze || needTravel) {
Cfg.travelMod(method.stmts, new TravelCallBack() {
@Override
public Value onAssign(Local v, AssignStmt as) {
if (v._ls_index == -1) {
return v;
} else if (v._ls_index == -2) {
Local n = (Local) v.clone();
method.locals.add(n);
return n;
}
// others
return v.clone();
}
@Override
public Value onUse(Local v) {
if (v._ls_index == -1) {
return v;
}
return v.clone();
}
}, true);
}
return needSSAAnalyze;
}
static class SSAAnalyze extends BaseAnalyze {
public int nextIndex;
public SSAAnalyze(IrMethod method) {
super(method, false);
}
@Override
protected void afterExec(SSAValue[] frame, Stmt stmt) {
if (!DEBUG) {
// remove frame to save memory
if (stmt._cfg_froms.size() < 2) {
// we only care stmt only has one or less parent,
// the parent must be visited already.
// if more than 1 parent, the other may not been visited at
// the moment
setFrame(stmt, null);
}
}
}
@Override
public Local onUse(Local local) {
if (local._ls_index < 0) {
return local;
}
return super.onUse(local);
}
@Override
public Local onAssign(Local local, AssignStmt as) {
if (local._ls_index < 0) {
return local;
}
return super.onAssign(local, as);
}
@Override
protected void analyzeValue() {
Set set = markUsed();
aValues.clear();
aValues = null;
if (DEBUG) {
clearLsEmptyValueFromFrame();
}
for (SSAValue v0 : set) {
SSAValue v = v0;
if (v.used && v.local == null) {
v.local = new Local(nextIndex++);
}
}
}
protected void clearLsEmptyValueFromFrame() {
for (Stmt p = method.stmts.getFirst(); p != null; p = p.getNext()) {
SSAValue[] frame = (SSAValue[]) p.frame;
if (frame != null) {
for (int i = 0; i < frame.length; i++) {
SSAValue r = frame[i];
if (r != null && !r.used) {
frame[i] = null;
}
}
}
}
}
@Override
protected void init() {
super.init();
nextIndex = method.locals.size();
}
@Override
protected void initCFG() {
Cfg.createCFG(this.method);
}
protected Set markUsed() {
Set used = new HashSet(aValues.size() / 2);
Queue q = new UniqueQueue<>();
q.addAll(aValues);
while (!q.isEmpty()) {
SSAValue v = q.poll();
if (v.used) {
used.add(v);
{
SSAValue p = v.parent;
if (p != null) {
if (!p.used) {
p.used = true;
q.add(p);
}
}
}
if (v.otherParents != null) {
for (SSAValue p : v.otherParents) {
if (!p.used) {
p.used = true;
q.add(p);
}
}
}
}
}
return used;
}
@Override
public SSAValue[] merge(SSAValue[] frame, SSAValue[] distFrame, Stmt src, Stmt dist) {
if (distFrame != null) {
relationMerge(frame, dist, distFrame);
} else {
if (dist._cfg_froms.size() > 1) {// detail mode
distFrame = newFrame();
relationMerge(frame, dist, distFrame);
} else if (needCopyFrame(src)) {
distFrame = newFrame();
System.arraycopy(frame, 0, distFrame, 0, distFrame.length);
} else {
distFrame = frame;
}
}
return distFrame;
}
private static boolean needCopyFrame(Stmt src) {
int c = 0;
if (src.exceptionHandlers != null) {
c += src.exceptionHandlers.size();
if (c > 1) {
return true;
}
}
if (src.st.canContinue()) {
c += 1;
if (c > 1) {
return true;
}
}
if (src.st.canBranch()) {
c += 1;
if (c > 1) {
return true;
}
}
if (src.st.canSwitch()) {
c += 1;
BaseSwitchStmt bss = (BaseSwitchStmt) src;
c += bss.targets.length;
}
return c > 1;
}
@Override
protected SSAValue[] newFrame(int size) {
return new SSAValue[size];
}
@Override
protected SSAValue newValue() {
return new SSAValue();
}
@Override
protected SSAValue onAssignLocal(Local local, Value value) {
SSAValue aValue = newValue();
aValue.local = local;
local.tag = aValue;
return aValue;
}
@Override
protected void onUseLocal(SSAValue aValue, Local local) {
local.tag = aValue;
aValue.used = true;
}
protected void relationMerge(SSAValue[] frame, Stmt dist, SSAValue[] distFrame) {
for (int i = 0; i < localSize; i++) {
SSAValue srcValue = (SSAValue) frame[i];
if (srcValue != null) {
SSAValue distValue = (SSAValue) distFrame[i];
if (distValue == null) {
if (!dist.visited) {
distValue = newValue();
aValues.add(distValue);
distFrame[i] = distValue;
linkParentChildren(srcValue, distValue);
}
} else {
linkParentChildren(srcValue, distValue);
}
}
}
}
private void linkParentChildren(SSAValue p, SSAValue c) {
if (c.parent == null) {
c.parent = p;
} else if (c.parent == p) {
return;
} else {
Set ps = c.otherParents;
if (ps == null) {
c.otherParents = ps = new HashSet<>(3);
}
ps.add(p);
}
}
}
private static class SSAValue implements AnalyzeValue {
public Local local;
public Set otherParents;
public boolean used = false;
public SSAValue parent;
@Override
public char toRsp() {
return used ? 'x' : '.';
}
@Override
public String toString() {
if (local != null) {
return local.toString();
} else {
return "N";
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy