summaryrefslogtreecommitdiffstats
path: root/dx/src/com/android/jack/dx/ssa
diff options
context:
space:
mode:
Diffstat (limited to 'dx/src/com/android/jack/dx/ssa')
-rw-r--r--dx/src/com/android/jack/dx/ssa/BasicRegisterMapper.java129
-rw-r--r--dx/src/com/android/jack/dx/ssa/ConstCollector.java400
-rw-r--r--dx/src/com/android/jack/dx/ssa/DeadCodeRemover.java272
-rw-r--r--dx/src/com/android/jack/dx/ssa/DomFront.java204
-rw-r--r--dx/src/com/android/jack/dx/ssa/Dominators.java285
-rw-r--r--dx/src/com/android/jack/dx/ssa/EscapeAnalysis.java843
-rw-r--r--dx/src/com/android/jack/dx/ssa/InterferenceRegisterMapper.java164
-rw-r--r--dx/src/com/android/jack/dx/ssa/LiteralOpUpgrader.java207
-rw-r--r--dx/src/com/android/jack/dx/ssa/LocalVariableExtractor.java208
-rw-r--r--dx/src/com/android/jack/dx/ssa/LocalVariableInfo.java251
-rw-r--r--dx/src/com/android/jack/dx/ssa/MoveParamCombiner.java156
-rw-r--r--dx/src/com/android/jack/dx/ssa/NormalSsaInsn.java236
-rw-r--r--dx/src/com/android/jack/dx/ssa/Optimizer.java257
-rw-r--r--dx/src/com/android/jack/dx/ssa/PhiInsn.java398
-rw-r--r--dx/src/com/android/jack/dx/ssa/PhiTypeResolver.java263
-rw-r--r--dx/src/com/android/jack/dx/ssa/RegisterMapper.java61
-rw-r--r--dx/src/com/android/jack/dx/ssa/SCCP.java697
-rw-r--r--dx/src/com/android/jack/dx/ssa/SetFactory.java95
-rw-r--r--dx/src/com/android/jack/dx/ssa/SsaBasicBlock.java1032
-rw-r--r--dx/src/com/android/jack/dx/ssa/SsaConverter.java391
-rw-r--r--dx/src/com/android/jack/dx/ssa/SsaInsn.java289
-rw-r--r--dx/src/com/android/jack/dx/ssa/SsaMethod.java873
-rw-r--r--dx/src/com/android/jack/dx/ssa/SsaRenamer.java662
-rw-r--r--dx/src/com/android/jack/dx/ssa/back/FirstFitAllocator.java151
-rw-r--r--dx/src/com/android/jack/dx/ssa/back/FirstFitLocalCombiningAllocator.java1139
-rw-r--r--dx/src/com/android/jack/dx/ssa/back/IdenticalBlockCombiner.java182
-rw-r--r--dx/src/com/android/jack/dx/ssa/back/InterferenceGraph.java113
-rw-r--r--dx/src/com/android/jack/dx/ssa/back/LivenessAnalyzer.java287
-rw-r--r--dx/src/com/android/jack/dx/ssa/back/NullRegisterAllocator.java58
-rw-r--r--dx/src/com/android/jack/dx/ssa/back/RegisterAllocator.java197
-rw-r--r--dx/src/com/android/jack/dx/ssa/back/SsaToRop.java380
-rw-r--r--dx/src/com/android/jack/dx/ssa/package-info.java103
32 files changed, 10983 insertions, 0 deletions
diff --git a/dx/src/com/android/jack/dx/ssa/BasicRegisterMapper.java b/dx/src/com/android/jack/dx/ssa/BasicRegisterMapper.java
new file mode 100644
index 00000000..f9bdb2fa
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/BasicRegisterMapper.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa;
+
+import com.android.jack.dx.rop.code.RegisterSpec;
+import com.android.jack.dx.rop.code.RegisterSpecList;
+import com.android.jack.dx.util.IntList;
+
+/**
+ * This class maps one register space into another, with
+ * each mapping built up individually and added via addMapping()
+ */
+public class BasicRegisterMapper extends RegisterMapper {
+ /** indexed by old register, containing new name */
+ private IntList oldToNew;
+
+ /** running count of used registers in new namespace */
+ private int runningCountNewRegisters;
+
+ /**
+ * Creates a new OneToOneRegisterMapper.
+ *
+ * @param countOldRegisters the number of registers in the old name space
+ */
+ public BasicRegisterMapper(int countOldRegisters) {
+ oldToNew = new IntList(countOldRegisters);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getNewRegisterCount() {
+ return runningCountNewRegisters;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RegisterSpec map(RegisterSpec registerSpec) {
+ if (registerSpec == null) {
+ return null;
+ }
+
+ int newReg;
+ try {
+ newReg = oldToNew.get(registerSpec.getReg());
+ } catch (IndexOutOfBoundsException ex) {
+ newReg = -1;
+ }
+
+ if (newReg < 0) {
+ throw new RuntimeException("no mapping specified for register");
+ }
+
+ return registerSpec.withReg(newReg);
+ }
+
+ /**
+ * Returns the new-namespace mapping for the specified
+ * old-namespace register, or -1 if one exists.
+ *
+ * @param oldReg {@code >= 0;} old-namespace register
+ * @return new-namespace register or -1 if none
+ */
+ public int oldToNew(int oldReg) {
+ if (oldReg >= oldToNew.size()) {
+ return -1;
+ }
+
+ return oldToNew.get(oldReg);
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("Old\tNew\n");
+ int sz = oldToNew.size();
+
+ for (int i = 0; i < sz; i++) {
+ sb.append(i);
+ sb.append('\t');
+ sb.append(oldToNew.get(i));
+ sb.append('\n');
+ }
+
+ sb.append("new reg count:");
+
+ sb.append(runningCountNewRegisters);
+ sb.append('\n');
+
+ return sb.toString();
+ }
+
+ /**
+ * Adds a mapping to the mapper. If oldReg has already been mapped,
+ * overwrites previous mapping with new mapping.
+ *
+ * @param oldReg {@code >= 0;} old register
+ * @param newReg {@code >= 0;} new register
+ * @param category {@code 1..2;} width of reg
+ */
+ public void addMapping(int oldReg, int newReg, int category) {
+ if (oldReg >= oldToNew.size()) {
+ // expand the array as necessary
+ for (int i = oldReg - oldToNew.size(); i >= 0; i--) {
+ oldToNew.add(-1);
+ }
+ }
+
+ oldToNew.set(oldReg, newReg);
+
+ if (runningCountNewRegisters < (newReg + category)) {
+ runningCountNewRegisters = newReg + category;
+ }
+ }
+}
diff --git a/dx/src/com/android/jack/dx/ssa/ConstCollector.java b/dx/src/com/android/jack/dx/ssa/ConstCollector.java
new file mode 100644
index 00000000..ee12cb44
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/ConstCollector.java
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa;
+
+import com.android.jack.dx.rop.code.LocalItem;
+import com.android.jack.dx.rop.code.PlainCstInsn;
+import com.android.jack.dx.rop.code.PlainInsn;
+import com.android.jack.dx.rop.code.RegOps;
+import com.android.jack.dx.rop.code.RegisterSpec;
+import com.android.jack.dx.rop.code.RegisterSpecList;
+import com.android.jack.dx.rop.code.Rop;
+import com.android.jack.dx.rop.code.Rops;
+import com.android.jack.dx.rop.code.SourcePosition;
+import com.android.jack.dx.rop.code.ThrowingCstInsn;
+import com.android.jack.dx.rop.cst.Constant;
+import com.android.jack.dx.rop.cst.CstString;
+import com.android.jack.dx.rop.cst.TypedConstant;
+import com.android.jack.dx.rop.type.StdTypeList;
+import com.android.jack.dx.rop.type.TypeBearer;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+/**
+ * Collects constants that are used more than once at the top of the
+ * method block. This increases register usage by about 5% but decreases
+ * insn size by about 3%.
+ */
+public class ConstCollector {
+ /** Maximum constants to collect per method. Puts cap on reg use */
+ private static final int MAX_COLLECTED_CONSTANTS = 5;
+
+ /**
+ * Also collect string consts, although they can throw exceptions.
+ * This is off now because it just doesn't seem to gain a whole lot.
+ * TODO if you turn this on, you must change SsaInsn.hasSideEffect()
+ * to return false for const-string insns whose exceptions are not
+ * caught in the current method.
+ */
+ private static boolean COLLECT_STRINGS = false;
+
+ /**
+ * If true, allow one local var to be involved with a collected const.
+ * Turned off because it mostly just inserts more moves.
+ */
+ private static boolean COLLECT_ONE_LOCAL = false;
+
+ /** method we're processing */
+ private final SsaMethod ssaMeth;
+
+ /**
+ * Processes a method.
+ *
+ * @param ssaMethod {@code non-null;} method to process
+ */
+ public static void process(SsaMethod ssaMethod) {
+ ConstCollector cc = new ConstCollector(ssaMethod);
+ cc.run();
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param ssaMethod {@code non-null;} method to process
+ */
+ private ConstCollector(SsaMethod ssaMethod) {
+ this.ssaMeth = ssaMethod;
+ }
+
+ /**
+ * Applies the optimization.
+ */
+ private void run() {
+ int regSz = ssaMeth.getRegCount();
+
+ ArrayList<TypedConstant> constantList
+ = getConstsSortedByCountUse();
+
+ int toCollect = Math.min(constantList.size(), MAX_COLLECTED_CONSTANTS);
+
+ SsaBasicBlock start = ssaMeth.getEntryBlock();
+
+ // Constant to new register containing the constant
+ HashMap<TypedConstant, RegisterSpec> newRegs
+ = new HashMap<TypedConstant, RegisterSpec> (toCollect);
+
+ for (int i = 0; i < toCollect; i++) {
+ TypedConstant cst = constantList.get(i);
+ RegisterSpec result
+ = RegisterSpec.make(ssaMeth.makeNewSsaReg(), cst);
+
+ Rop constRop = Rops.opConst(cst);
+
+ if (constRop.getBranchingness() == Rop.BRANCH_NONE) {
+ start.addInsnToHead(
+ new PlainCstInsn(Rops.opConst(cst),
+ SourcePosition.NO_INFO, result,
+ RegisterSpecList.EMPTY, cst));
+ } else {
+ // We need two new basic blocks along with the new insn
+ SsaBasicBlock entryBlock = ssaMeth.getEntryBlock();
+ SsaBasicBlock successorBlock
+ = entryBlock.getPrimarySuccessor();
+
+ // Insert a block containing the const insn.
+ SsaBasicBlock constBlock
+ = entryBlock.insertNewSuccessor(successorBlock);
+
+ constBlock.replaceLastInsn(
+ new ThrowingCstInsn(constRop, SourcePosition.NO_INFO,
+ RegisterSpecList.EMPTY,
+ StdTypeList.EMPTY, cst));
+
+ // Insert a block containing the move-result-pseudo insn.
+
+ SsaBasicBlock resultBlock
+ = constBlock.insertNewSuccessor(successorBlock);
+ PlainInsn insn
+ = new PlainInsn(
+ Rops.opMoveResultPseudo(result.getTypeBearer()),
+ SourcePosition.NO_INFO,
+ result, RegisterSpecList.EMPTY);
+
+ resultBlock.addInsnToHead(insn);
+ }
+
+ newRegs.put(cst, result);
+ }
+
+ updateConstUses(newRegs, regSz);
+ }
+
+ /**
+ * Gets all of the collectable constant values used in this method,
+ * sorted by most used first. Skips non-collectable consts, such as
+ * non-string object constants
+ *
+ * @return {@code non-null;} list of constants in most-to-least used order
+ */
+ private ArrayList<TypedConstant> getConstsSortedByCountUse() {
+ int regSz = ssaMeth.getRegCount();
+
+ final HashMap<TypedConstant, Integer> countUses
+ = new HashMap<TypedConstant, Integer>();
+
+ /*
+ * Each collected constant can be used by just one local
+ * (used only if COLLECT_ONE_LOCAL is true).
+ */
+ final HashSet<TypedConstant> usedByLocal
+ = new HashSet<TypedConstant>();
+
+ // Count how many times each const value is used.
+ for (int i = 0; i < regSz; i++) {
+ SsaInsn insn = ssaMeth.getDefinitionForRegister(i);
+
+ if (insn == null || insn.getOpcode() == null) continue;
+
+ RegisterSpec result = insn.getResult();
+ TypeBearer typeBearer = result.getTypeBearer();
+
+ if (!typeBearer.isConstant()) continue;
+
+ TypedConstant cst = (TypedConstant) typeBearer;
+
+ // Find defining instruction for move-result-pseudo instructions
+ if (insn.getOpcode().getOpcode() == RegOps.MOVE_RESULT_PSEUDO) {
+ int pred = insn.getBlock().getPredecessors().nextSetBit(0);
+ ArrayList<SsaInsn> predInsns;
+ predInsns = ssaMeth.getBlocks().get(pred).getInsns();
+ insn = predInsns.get(predInsns.size()-1);
+ }
+
+ if (insn.canThrow()) {
+ /*
+ * Don't move anything other than strings -- the risk
+ * of changing where an exception is thrown is too high.
+ */
+ if (!(cst instanceof CstString) || !COLLECT_STRINGS) {
+ continue;
+ }
+ /*
+ * We can't move any throwable const whose throw will be
+ * caught, so don't count them.
+ */
+ if (insn.getBlock().getSuccessors().cardinality() > 1) {
+ continue;
+ }
+ }
+
+ /*
+ * TODO: Might be nice to try and figure out which local
+ * wins most when collected.
+ */
+ if (ssaMeth.isRegALocal(result)) {
+ if (!COLLECT_ONE_LOCAL) {
+ continue;
+ } else {
+ if (usedByLocal.contains(cst)) {
+ // Count one local usage only.
+ continue;
+ } else {
+ usedByLocal.add(cst);
+ }
+ }
+ }
+
+ Integer has = countUses.get(cst);
+ if (has == null) {
+ countUses.put(cst, 1);
+ } else {
+ countUses.put(cst, has + 1);
+ }
+ }
+
+ // Collect constants that have been reused.
+ ArrayList<TypedConstant> constantList = new ArrayList<TypedConstant>();
+ for (Map.Entry<TypedConstant, Integer> entry : countUses.entrySet()) {
+ if (entry.getValue() > 1) {
+ constantList.add(entry.getKey());
+ }
+ }
+
+ // Sort by use, with most used at the beginning of the list.
+ Collections.sort(constantList, new Comparator<Constant>() {
+ public int compare(Constant a, Constant b) {
+ int ret;
+ ret = countUses.get(b) - countUses.get(a);
+
+ if (ret == 0) {
+ /*
+ * Provide sorting determinisim for constants with same
+ * usage count.
+ */
+ ret = a.compareTo(b);
+ }
+
+ return ret;
+ }
+
+ @Override
+ public boolean equals (Object obj) {
+ return obj == this;
+ }
+ });
+
+ return constantList;
+ }
+
+ /**
+ * Inserts mark-locals if necessary when changing a register. If
+ * the definition of {@code origReg} is associated with a local
+ * variable, then insert a mark-local for {@code newReg} just below
+ * it. We expect the definition of {@code origReg} to ultimately
+ * be removed by the dead code eliminator
+ *
+ * @param origReg {@code non-null;} original register
+ * @param newReg {@code non-null;} new register that will replace
+ * {@code origReg}
+ */
+ private void fixLocalAssignment(RegisterSpec origReg,
+ RegisterSpec newReg) {
+ for (SsaInsn use : ssaMeth.getUseListForRegister(origReg.getReg())) {
+ RegisterSpec localAssignment = use.getLocalAssignment();
+ if (localAssignment == null) {
+ continue;
+ }
+
+ if (use.getResult() == null) {
+ /*
+ * This is a mark-local. it will be updated when all uses
+ * are updated.
+ */
+ continue;
+ }
+
+ LocalItem local = localAssignment.getLocalItem();
+
+ // Un-associate original use.
+ use.setResultLocal(null);
+
+ // Now add a mark-local to the new reg immediately after.
+ newReg = newReg.withLocalItem(local);
+
+ SsaInsn newInsn
+ = SsaInsn.makeFromRop(
+ new PlainInsn(Rops.opMarkLocal(newReg),
+ SourcePosition.NO_INFO, null,
+ RegisterSpecList.make(newReg)),
+ use.getBlock());
+
+ ArrayList<SsaInsn> insns = use.getBlock().getInsns();
+
+ insns.add(insns.indexOf(use) + 1, newInsn);
+ }
+ }
+
+ /**
+ * Updates all uses of various consts to use the values in the newly
+ * assigned registers.
+ *
+ * @param newRegs {@code non-null;} mapping between constant and new reg
+ * @param origRegCount {@code >=0;} original SSA reg count, not including
+ * newly added constant regs
+ */
+ private void updateConstUses(HashMap<TypedConstant, RegisterSpec> newRegs,
+ int origRegCount) {
+
+ /*
+ * set of constants associated with a local variable; used
+ * only if COLLECT_ONE_LOCAL is true.
+ */
+ final HashSet<TypedConstant> usedByLocal
+ = new HashSet<TypedConstant>();
+
+ final ArrayList<SsaInsn>[] useList = ssaMeth.getUseListCopy();
+
+ for (int i = 0; i < origRegCount; i++) {
+ SsaInsn insn = ssaMeth.getDefinitionForRegister(i);
+
+ if (insn == null) {
+ continue;
+ }
+
+ final RegisterSpec origReg = insn.getResult();
+ TypeBearer typeBearer = insn.getResult().getTypeBearer();
+
+ if (!typeBearer.isConstant()) continue;
+
+ TypedConstant cst = (TypedConstant) typeBearer;
+ final RegisterSpec newReg = newRegs.get(cst);
+
+ if (newReg == null) {
+ continue;
+ }
+
+ if (ssaMeth.isRegALocal(origReg)) {
+ if (!COLLECT_ONE_LOCAL) {
+ continue;
+ } else {
+ /*
+ * TODO: If the same local gets the same cst
+ * multiple times, it would be nice to reuse the
+ * register.
+ */
+ if (usedByLocal.contains(cst)) {
+ continue;
+ } else {
+ usedByLocal.add(cst);
+ fixLocalAssignment(origReg, newRegs.get(cst));
+ }
+ }
+ }
+
+ // maps an original const register to the new collected register
+ RegisterMapper mapper = new RegisterMapper() {
+ @Override
+ public int getNewRegisterCount() {
+ return ssaMeth.getRegCount();
+ }
+
+ @Override
+ public RegisterSpec map(RegisterSpec registerSpec) {
+ if (registerSpec.getReg() == origReg.getReg()) {
+ return newReg.withLocalItem(
+ registerSpec.getLocalItem());
+ }
+
+ return registerSpec;
+ }
+ };
+
+ for (SsaInsn use : useList[origReg.getReg()]) {
+ if (use.canThrow()
+ && use.getBlock().getSuccessors().cardinality() > 1) {
+ continue;
+ }
+ use.mapSourceRegisters(mapper);
+ }
+ }
+ }
+}
diff --git a/dx/src/com/android/jack/dx/ssa/DeadCodeRemover.java b/dx/src/com/android/jack/dx/ssa/DeadCodeRemover.java
new file mode 100644
index 00000000..70c00054
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/DeadCodeRemover.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa;
+
+import com.android.jack.dx.rop.code.Insn;
+import com.android.jack.dx.rop.code.PlainInsn;
+import com.android.jack.dx.rop.code.RegOps;
+import com.android.jack.dx.rop.code.RegisterSpec;
+import com.android.jack.dx.rop.code.RegisterSpecList;
+import com.android.jack.dx.rop.code.Rop;
+import com.android.jack.dx.rop.code.Rops;
+import com.android.jack.dx.rop.code.SourcePosition;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashSet;
+
+/**
+ * A variation on Appel Algorithm 19.12 "Dead code elimination in SSA form".
+ *
+ * TODO this algorithm is more efficient if run in reverse from exit
+ * block to entry block.
+ */
+public class DeadCodeRemover {
+ /** method we're processing */
+ private final SsaMethod ssaMeth;
+
+ /** ssaMeth.getRegCount() */
+ private final int regCount;
+
+ /**
+ * indexed by register: whether reg should be examined
+ * (does it correspond to a no-side-effect insn?)
+ */
+ private final BitSet worklist;
+
+ /** use list indexed by register; modified during operation */
+ private final ArrayList<SsaInsn>[] useList;
+
+ /**
+ * Process a method with the dead-code remver
+ *
+ * @param ssaMethod method to process
+ */
+ public static void process(SsaMethod ssaMethod) {
+ DeadCodeRemover dc = new DeadCodeRemover(ssaMethod);
+ dc.run();
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param ssaMethod method to process
+ */
+ private DeadCodeRemover(SsaMethod ssaMethod) {
+ this.ssaMeth = ssaMethod;
+
+ regCount = ssaMethod.getRegCount();
+ worklist = new BitSet(regCount);
+ useList = ssaMeth.getUseListCopy();
+ }
+
+ /**
+ * Runs the dead code remover.
+ */
+ private void run() {
+ pruneDeadInstructions();
+
+ HashSet<SsaInsn> deletedInsns = new HashSet<SsaInsn>();
+
+ ssaMeth.forEachInsn(new NoSideEffectVisitor(worklist));
+
+ int regV;
+
+ while ( 0 <= (regV = worklist.nextSetBit(0)) ) {
+ worklist.clear(regV);
+
+ if (useList[regV].size() == 0
+ || isCircularNoSideEffect(regV, null)) {
+
+ SsaInsn insnS = ssaMeth.getDefinitionForRegister(regV);
+
+ // This insn has already been deleted.
+ if (deletedInsns.contains(insnS)) {
+ continue;
+ }
+
+ RegisterSpecList sources = insnS.getSources();
+
+ int sz = sources.size();
+ for (int i = 0; i < sz; i++) {
+ // Delete this insn from all usage lists.
+ RegisterSpec source = sources.get(i);
+ useList[source.getReg()].remove(insnS);
+
+ if (!hasSideEffect(
+ ssaMeth.getDefinitionForRegister(
+ source.getReg()))) {
+ /*
+ * Only registers whose definition has no side effect
+ * should be added back to the worklist.
+ */
+ worklist.set(source.getReg());
+ }
+ }
+
+ // Schedule this insn for later deletion.
+ deletedInsns.add(insnS);
+ }
+ }
+
+ ssaMeth.deleteInsns(deletedInsns);
+ }
+
+ /**
+ * Removes all instructions from every unreachable block.
+ */
+ private void pruneDeadInstructions() {
+ HashSet<SsaInsn> deletedInsns = new HashSet<SsaInsn>();
+
+ ssaMeth.computeReachability();
+
+ for (SsaBasicBlock block : ssaMeth.getBlocks()) {
+ if (block.isReachable()) continue;
+
+ // Prune instructions from unreachable blocks
+ for (int i = 0; i < block.getInsns().size(); i++) {
+ SsaInsn insn = block.getInsns().get(i);
+ RegisterSpecList sources = insn.getSources();
+ int sourcesSize = sources.size();
+
+ // Delete this instruction completely if it has sources
+ if (sourcesSize != 0) {
+ deletedInsns.add(insn);
+ }
+
+ // Delete this instruction from all usage lists.
+ for (int j = 0; j < sourcesSize; j++) {
+ RegisterSpec source = sources.get(j);
+ useList[source.getReg()].remove(insn);
+ }
+
+ // Remove this instruction result from the sources of any phis
+ RegisterSpec result = insn.getResult();
+ if (result == null) continue;
+ for (SsaInsn use : useList[result.getReg()]) {
+ if (use instanceof PhiInsn) {
+ PhiInsn phiUse = (PhiInsn) use;
+ phiUse.removePhiRegister(result);
+ }
+ }
+ }
+ }
+
+ ssaMeth.deleteInsns(deletedInsns);
+ }
+
+ /**
+ * Returns true if the only uses of this register form a circle of
+ * operations with no side effects.
+ *
+ * @param regV register to examine
+ * @param set a set of registers that we've already determined
+ * are only used as sources in operations with no side effect or null
+ * if this is the first recursion
+ * @return true if usage is circular without side effect
+ */
+ private boolean isCircularNoSideEffect(int regV, BitSet set) {
+ if ((set != null) && set.get(regV)) {
+ return true;
+ }
+
+ for (SsaInsn use : useList[regV]) {
+ if (hasSideEffect(use)) {
+ return false;
+ }
+ }
+
+ if (set == null) {
+ set = new BitSet(regCount);
+ }
+
+ // This register is only used in operations that have no side effect.
+ set.set(regV);
+
+ for (SsaInsn use : useList[regV]) {
+ RegisterSpec result = use.getResult();
+
+ if (result == null
+ || !isCircularNoSideEffect(result.getReg(), set)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns true if this insn has a side-effect. Returns true
+ * if the insn is null for reasons stated in the code block.
+ *
+ * @param insn {@code null-ok;} instruction in question
+ * @return true if it has a side-effect
+ */
+ private static boolean hasSideEffect(SsaInsn insn) {
+ if (insn == null) {
+ /* While false would seem to make more sense here, true
+ * prevents us from adding this back to a worklist unnecessarally.
+ */
+ return true;
+ }
+
+ return insn.hasSideEffect();
+ }
+
+ /**
+ * A callback class used to build up the initial worklist of
+ * registers defined by an instruction with no side effect.
+ */
+ static private class NoSideEffectVisitor implements SsaInsn.Visitor {
+ BitSet noSideEffectRegs;
+
+ /**
+ * Passes in data structures that will be filled out after
+ * ssaMeth.forEachInsn() is called with this instance.
+ *
+ * @param noSideEffectRegs to-build bitset of regs that are
+ * results of regs with no side effects
+ */
+ public NoSideEffectVisitor(BitSet noSideEffectRegs) {
+ this.noSideEffectRegs = noSideEffectRegs;
+ }
+
+ /** {@inheritDoc} */
+ public void visitMoveInsn (NormalSsaInsn insn) {
+ // If we're tracking local vars, some moves have side effects.
+ if (!hasSideEffect(insn)) {
+ noSideEffectRegs.set(insn.getResult().getReg());
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void visitPhiInsn (PhiInsn phi) {
+ // If we're tracking local vars, then some phis have side effects.
+ if (!hasSideEffect(phi)) {
+ noSideEffectRegs.set(phi.getResult().getReg());
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void visitNonMoveInsn (NormalSsaInsn insn) {
+ RegisterSpec result = insn.getResult();
+ if (!hasSideEffect(insn) && result != null) {
+ noSideEffectRegs.set(result.getReg());
+ }
+ }
+ }
+}
diff --git a/dx/src/com/android/jack/dx/ssa/DomFront.java b/dx/src/com/android/jack/dx/ssa/DomFront.java
new file mode 100644
index 00000000..9e67f464
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/DomFront.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa;
+
+import com.android.jack.dx.util.BitIntSet;
+import com.android.jack.dx.util.IntSet;
+import com.android.jack.dx.util.ListIntSet;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+
+/**
+ * Calculates the dominance-frontiers of a methot's basic blocks.
+ * Algorithm from "A Simple, Fast Dominance Algorithm" by Cooper,
+ * Harvey, and Kennedy; transliterated to Java.
+ */
+public class DomFront {
+ /** local debug flag */
+ private static boolean DEBUG = false;
+
+ /** {@code non-null;} method being processed */
+ private final SsaMethod meth;
+
+ private final ArrayList<SsaBasicBlock> nodes;
+
+ private final DomInfo[] domInfos;
+
+ /**
+ * Dominance-frontier information for a single basic block.
+ */
+ public static class DomInfo {
+ /**
+ * {@code null-ok;} the dominance frontier set indexed by
+ * block index
+ */
+ public IntSet dominanceFrontiers;
+
+ /** {@code >= 0 after run();} the index of the immediate dominator */
+ public int idom = -1;
+ }
+
+ /**
+ * Constructs instance. Call {@link DomFront#run} to process.
+ *
+ * @param meth {@code non-null;} method to process
+ */
+ public DomFront(SsaMethod meth) {
+ this.meth = meth;
+ nodes = meth.getBlocks();
+
+ int szNodes = nodes.size();
+ domInfos = new DomInfo[szNodes];
+
+ for (int i = 0; i < szNodes; i++) {
+ domInfos[i] = new DomInfo();
+ }
+ }
+
+ /**
+ * Calculates the dominance frontier information for the method.
+ *
+ * @return {@code non-null;} an array of DomInfo structures
+ */
+ public DomInfo[] run() {
+ int szNodes = nodes.size();
+
+ if (DEBUG) {
+ for (int i = 0; i < szNodes; i++) {
+ SsaBasicBlock node = nodes.get(i);
+ System.out.println("pred[" + i + "]: "
+ + node.getPredecessors());
+ }
+ }
+
+ Dominators methDom = Dominators.make(meth, domInfos, false);
+
+ if (DEBUG) {
+ for (int i = 0; i < szNodes; i++) {
+ DomInfo info = domInfos[i];
+ System.out.println("idom[" + i + "]: "
+ + info.idom);
+ }
+ }
+
+ buildDomTree();
+
+ if (DEBUG) {
+ debugPrintDomChildren();
+ }
+
+ for (int i = 0; i < szNodes; i++) {
+ domInfos[i].dominanceFrontiers
+ = SetFactory.makeDomFrontSet(szNodes);
+ }
+
+ calcDomFronts();
+
+ if (DEBUG) {
+ for (int i = 0; i < szNodes; i++) {
+ System.out.println("df[" + i + "]: "
+ + domInfos[i].dominanceFrontiers);
+ }
+ }
+
+ return domInfos;
+ }
+
+ private void debugPrintDomChildren() {
+ int szNodes = nodes.size();
+
+ for (int i = 0; i < szNodes; i++) {
+ SsaBasicBlock node = nodes.get(i);
+ StringBuffer sb = new StringBuffer();
+
+ sb.append('{');
+ boolean comma = false;
+ for (SsaBasicBlock child : node.getDomChildren()) {
+ if (comma) {
+ sb.append(',');
+ }
+ sb.append(child);
+ comma = true;
+ }
+ sb.append('}');
+
+ System.out.println("domChildren[" + node + "]: "
+ + sb);
+ }
+ }
+
+ /**
+ * The dominators algorithm leaves us knowing who the immediate dominator
+ * is for each node. This sweeps the node list and builds the proper
+ * dominance tree.
+ */
+ private void buildDomTree() {
+ int szNodes = nodes.size();
+
+ for (int i = 0; i < szNodes; i++) {
+ DomInfo info = domInfos[i];
+
+ if (info.idom == -1) continue;
+
+ SsaBasicBlock domParent = nodes.get(info.idom);
+ domParent.addDomChild(nodes.get(i));
+ }
+ }
+
+ /**
+ * Calculates the dominance-frontier set.
+ * from "A Simple, Fast Dominance Algorithm" by Cooper,
+ * Harvey, and Kennedy; transliterated to Java.
+ */
+ private void calcDomFronts() {
+ int szNodes = nodes.size();
+
+ for (int b = 0; b < szNodes; b++) {
+ SsaBasicBlock nb = nodes.get(b);
+ DomInfo nbInfo = domInfos[b];
+ BitSet pred = nb.getPredecessors();
+
+ if (pred.cardinality() > 1) {
+ for (int i = pred.nextSetBit(0); i >= 0;
+ i = pred.nextSetBit(i + 1)) {
+
+ for (int runnerIndex = i;
+ runnerIndex != nbInfo.idom; /* empty */) {
+ /*
+ * We can stop if we hit a block we already
+ * added label to, since we must be at a part
+ * of the dom tree we have seen before.
+ */
+ if (runnerIndex == -1) break;
+
+ DomInfo runnerInfo = domInfos[runnerIndex];
+
+ if (runnerInfo.dominanceFrontiers.has(b)) {
+ break;
+ }
+
+ // Add b to runner's dominance frontier set.
+ runnerInfo.dominanceFrontiers.add(b);
+ runnerIndex = runnerInfo.idom;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/dx/src/com/android/jack/dx/ssa/Dominators.java b/dx/src/com/android/jack/dx/ssa/Dominators.java
new file mode 100644
index 00000000..b3b4c3fd
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/Dominators.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashSet;
+
+/**
+ * This class computes dominator and post-dominator information using the
+ * Lengauer-Tarjan method.
+ *
+ * See A Fast Algorithm for Finding Dominators in a Flowgraph
+ * T. Lengauer & R. Tarjan, ACM TOPLAS July 1979, pgs 121-141.
+ *
+ * This implementation runs in time O(n log n). The time bound
+ * could be changed to O(n * ack(n)) with a small change to the link and eval,
+ * and an addition of a child field to the DFS info. In reality, the constant
+ * overheads are high enough that the current method is faster in all but the
+ * strangest artificially constructed examples.
+ *
+ * The basic idea behind this algorithm is to perform a DFS walk, keeping track
+ * of various info about parents. We then use this info to calculate the
+ * dominators, using union-find structures to link together the DFS info,
+ * then finally evaluate the union-find results to get the dominators.
+ * This implementation is m log n because it does not perform union by
+ * rank to keep the union-find tree balanced.
+ */
+public final class Dominators {
+ /* postdom is true if we want post dominators */
+ private final boolean postdom;
+
+ /* {@code non-null;} method being processed */
+ private final SsaMethod meth;
+
+ /* Method's basic blocks. */
+ private final ArrayList<SsaBasicBlock> blocks;
+
+ /** indexed by basic block index */
+ private final DFSInfo[] info;
+
+ private final ArrayList<SsaBasicBlock> vertex;
+
+ /** {@code non-null;} the raw dominator info */
+ private final DomFront.DomInfo domInfos[];
+
+ /**
+ * Constructs an instance.
+ *
+ * @param meth {@code non-null;} method to process
+ * @param domInfos {@code non-null;} the raw dominator info
+ * @param postdom true for postdom information, false for normal dom info
+ */
+ private Dominators(SsaMethod meth, DomFront.DomInfo[] domInfos,
+ boolean postdom) {
+ this.meth = meth;
+ this.domInfos = domInfos;
+ this.postdom = postdom;
+ this.blocks = meth.getBlocks();
+ this.info = new DFSInfo[blocks.size() + 2];
+ this.vertex = new ArrayList<SsaBasicBlock>();
+ }
+
+ /**
+ * Constructs a fully-initialized instance. (This method exists so as
+ * to avoid calling a large amount of code in the constructor.)
+ *
+ * @param meth {@code non-null;} method to process
+ * @param domInfos {@code non-null;} the raw dominator info
+ * @param postdom true for postdom information, false for normal dom info
+ */
+ public static Dominators make(SsaMethod meth, DomFront.DomInfo[] domInfos,
+ boolean postdom) {
+ Dominators result = new Dominators(meth, domInfos, postdom);
+
+ result.run();
+ return result;
+ }
+
+ private BitSet getSuccs(SsaBasicBlock block) {
+ if (postdom) {
+ return block.getPredecessors();
+ } else {
+ return block.getSuccessors();
+ }
+ }
+
+ private BitSet getPreds(SsaBasicBlock block) {
+ if (postdom) {
+ return block.getSuccessors();
+ } else {
+ return block.getPredecessors();
+ }
+ }
+
+ /**
+ * Performs path compress on the DFS info.
+ *
+ * @param in Basic block whose DFS info we are path compressing.
+ */
+ private void compress(SsaBasicBlock in) {
+ DFSInfo bbInfo = info[in.getIndex()];
+ DFSInfo ancestorbbInfo = info[bbInfo.ancestor.getIndex()];
+
+ if (ancestorbbInfo.ancestor != null) {
+ ArrayList<SsaBasicBlock> worklist = new ArrayList<SsaBasicBlock>();
+ HashSet<SsaBasicBlock> visited = new HashSet<SsaBasicBlock>();
+ worklist.add(in);
+
+ while (!worklist.isEmpty()) {
+ int wsize = worklist.size();
+ SsaBasicBlock v = worklist.get(wsize - 1);
+ DFSInfo vbbInfo = info[v.getIndex()];
+ SsaBasicBlock vAncestor = vbbInfo.ancestor;
+ DFSInfo vabbInfo = info[vAncestor.getIndex()];
+
+ // Make sure we process our ancestor before ourselves.
+ if (visited.add(vAncestor) && vabbInfo.ancestor != null) {
+ worklist.add(vAncestor);
+ continue;
+ }
+ worklist.remove(wsize - 1);
+
+ // Update based on ancestor info.
+ if (vabbInfo.ancestor == null) {
+ continue;
+ }
+ SsaBasicBlock vAncestorRep = vabbInfo.rep;
+ SsaBasicBlock vRep = vbbInfo.rep;
+ if (info[vAncestorRep.getIndex()].semidom
+ < info[vRep.getIndex()].semidom) {
+ vbbInfo.rep = vAncestorRep;
+ }
+ vbbInfo.ancestor = vabbInfo.ancestor;
+ }
+ }
+ }
+
+ private SsaBasicBlock eval(SsaBasicBlock v) {
+ DFSInfo bbInfo = info[v.getIndex()];
+
+ if (bbInfo.ancestor == null) {
+ return v;
+ }
+
+ compress(v);
+ return bbInfo.rep;
+ }
+
+ /**
+ * Performs dominator/post-dominator calculation for the control
+ * flow graph.
+ *
+ * @param meth {@code non-null;} method to analyze
+ */
+ private void run() {
+ SsaBasicBlock root = postdom
+ ? meth.getExitBlock() : meth.getEntryBlock();
+
+ if (root != null) {
+ vertex.add(root);
+ domInfos[root.getIndex()].idom = root.getIndex();
+ }
+
+ /*
+ * First we perform a DFS numbering of the blocks, by
+ * numbering the dfs tree roots.
+ */
+
+ DfsWalker walker = new DfsWalker();
+ meth.forEachBlockDepthFirst(postdom, walker);
+
+ // the largest semidom number assigned
+ int dfsMax = vertex.size() - 1;
+
+ // Now calculate semidominators.
+ for (int i = dfsMax; i >= 2; --i) {
+ SsaBasicBlock w = vertex.get(i);
+ DFSInfo wInfo = info[w.getIndex()];
+
+ BitSet preds = getPreds(w);
+ for (int j = preds.nextSetBit(0);
+ j >= 0;
+ j = preds.nextSetBit(j + 1)) {
+ SsaBasicBlock predBlock = blocks.get(j);
+ DFSInfo predInfo = info[predBlock.getIndex()];
+
+ /*
+ * PredInfo may not exist in case the predecessor is
+ * not reachable.
+ */
+ if (predInfo != null) {
+ int predSemidom = info[eval(predBlock).getIndex()].semidom;
+ if (predSemidom < wInfo.semidom) {
+ wInfo.semidom = predSemidom;
+ }
+ }
+ }
+ info[vertex.get(wInfo.semidom).getIndex()].bucket.add(w);
+
+ /*
+ * Normally we would call link here, but in our O(m log n)
+ * implementation this is equivalent to the following
+ * single line.
+ */
+ wInfo.ancestor = wInfo.parent;
+
+ // Implicity define idom for each vertex.
+ ArrayList<SsaBasicBlock> wParentBucket;
+ wParentBucket = info[wInfo.parent.getIndex()].bucket;
+
+ while (!wParentBucket.isEmpty()) {
+ int lastItem = wParentBucket.size() - 1;
+ SsaBasicBlock last = wParentBucket.remove(lastItem);
+ SsaBasicBlock U = eval(last);
+ if (info[U.getIndex()].semidom
+ < info[last.getIndex()].semidom) {
+ domInfos[last.getIndex()].idom = U.getIndex();
+ } else {
+ domInfos[last.getIndex()].idom = wInfo.parent.getIndex();
+ }
+ }
+ }
+
+ // Now explicitly define the immediate dominator of each vertex
+ for (int i = 2; i <= dfsMax; ++i) {
+ SsaBasicBlock w = vertex.get(i);
+ if (domInfos[w.getIndex()].idom
+ != vertex.get(info[w.getIndex()].semidom).getIndex()) {
+ domInfos[w.getIndex()].idom
+ = domInfos[domInfos[w.getIndex()].idom].idom;
+ }
+ }
+ }
+
+ /**
+ * Callback for depth-first walk through control flow graph (either
+ * from the entry block or the exit block). Records the traversal order
+ * in the {@code info}list.
+ */
+ private class DfsWalker implements SsaBasicBlock.Visitor {
+ private int dfsNum = 0;
+
+ public void visitBlock(SsaBasicBlock v, SsaBasicBlock parent) {
+ DFSInfo bbInfo = new DFSInfo();
+ bbInfo.semidom = ++dfsNum;
+ bbInfo.rep = v;
+ bbInfo.parent = parent;
+ vertex.add(v);
+ info[v.getIndex()] = bbInfo;
+ }
+ }
+
+ private static final class DFSInfo {
+ public int semidom;
+ public SsaBasicBlock parent;
+
+ /**
+ * rep(resentative) is known as "label" in the paper. It is the node
+ * that our block's DFS info has been unioned to.
+ */
+ public SsaBasicBlock rep;
+
+ public SsaBasicBlock ancestor;
+ public ArrayList<SsaBasicBlock> bucket;
+
+ public DFSInfo() {
+ bucket = new ArrayList<SsaBasicBlock>();
+ }
+ }
+}
diff --git a/dx/src/com/android/jack/dx/ssa/EscapeAnalysis.java b/dx/src/com/android/jack/dx/ssa/EscapeAnalysis.java
new file mode 100644
index 00000000..791a3b52
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/EscapeAnalysis.java
@@ -0,0 +1,843 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa;
+
+import com.android.jack.dx.rop.code.Exceptions;
+import com.android.jack.dx.rop.code.FillArrayDataInsn;
+import com.android.jack.dx.rop.code.Insn;
+import com.android.jack.dx.rop.code.PlainCstInsn;
+import com.android.jack.dx.rop.code.PlainInsn;
+import com.android.jack.dx.rop.code.RegOps;
+import com.android.jack.dx.rop.code.RegisterSpec;
+import com.android.jack.dx.rop.code.RegisterSpecList;
+import com.android.jack.dx.rop.code.Rop;
+import com.android.jack.dx.rop.code.Rops;
+import com.android.jack.dx.rop.code.ThrowingCstInsn;
+import com.android.jack.dx.rop.code.ThrowingInsn;
+import com.android.jack.dx.rop.cst.Constant;
+import com.android.jack.dx.rop.cst.CstLiteralBits;
+import com.android.jack.dx.rop.cst.CstMethodRef;
+import com.android.jack.dx.rop.cst.CstNat;
+import com.android.jack.dx.rop.cst.CstString;
+import com.android.jack.dx.rop.cst.CstType;
+import com.android.jack.dx.rop.cst.TypedConstant;
+import com.android.jack.dx.rop.cst.Zeroes;
+import com.android.jack.dx.rop.type.StdTypeList;
+import com.android.jack.dx.rop.type.Type;
+import com.android.jack.dx.rop.type.TypeBearer;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * Simple intraprocedural escape analysis. Finds new arrays that don't escape
+ * the method they are created in and replaces the array values with registers.
+ */
+public class EscapeAnalysis {
+ /**
+ * Struct used to generate and maintain escape analysis results.
+ */
+ static class EscapeSet {
+ /** set containing all registers related to an object */
+ BitSet regSet;
+ /** escape state of the object */
+ EscapeState escape;
+ /** list of objects that are put into this object */
+ ArrayList<EscapeSet> childSets;
+ /** list of objects that this object is put into */
+ ArrayList<EscapeSet> parentSets;
+ /** flag to indicate this object is a scalar replaceable array */
+ boolean replaceableArray;
+
+ /**
+ * Constructs an instance of an EscapeSet
+ *
+ * @param reg the SSA register that defines the object
+ * @param size the number of registers in the method
+ * @param escState the lattice value to initially set this to
+ */
+ EscapeSet(int reg, int size, EscapeState escState) {
+ regSet = new BitSet(size);
+ regSet.set(reg);
+ escape = escState;
+ childSets = new ArrayList<EscapeSet>();
+ parentSets = new ArrayList<EscapeSet>();
+ replaceableArray = false;
+ }
+ }
+
+ /**
+ * Lattice values used to indicate escape state for an object. Analysis can
+ * only raise escape state values, not lower them.
+ *
+ * TOP - Used for objects that haven't been analyzed yet
+ * NONE - Object does not escape, and is eligible for scalar replacement.
+ * METHOD - Object remains local to method, but can't be scalar replaced.
+ * INTER - Object is passed between methods. (treated as globally escaping
+ * since this is an intraprocedural analysis)
+ * GLOBAL - Object escapes globally.
+ */
+ public enum EscapeState {
+ TOP, NONE, METHOD, INTER, GLOBAL
+ }
+
+ /** method we're processing */
+ private SsaMethod ssaMeth;
+ /** ssaMeth.getRegCount() */
+ private int regCount;
+ /** Lattice values for each object register group */
+ private ArrayList<EscapeSet> latticeValues;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param ssaMeth method to process
+ */
+ private EscapeAnalysis(SsaMethod ssaMeth) {
+ this.ssaMeth = ssaMeth;
+ this.regCount = ssaMeth.getRegCount();
+ this.latticeValues = new ArrayList<EscapeSet>();
+ }
+
+ /**
+ * Finds the index in the lattice for a particular register.
+ * Returns the size of the lattice if the register wasn't found.
+ *
+ * @param reg {@code non-null;} register being looked up
+ * @return index of the register or size of the lattice if it wasn't found.
+ */
+ private int findSetIndex(RegisterSpec reg) {
+ int i;
+ for (i = 0; i < latticeValues.size(); i++) {
+ EscapeSet e = latticeValues.get(i);
+ if (e.regSet.get(reg.getReg())) {
+ return i;
+ }
+ }
+ return i;
+ }
+
+ /**
+ * Finds the corresponding instruction for a given move result
+ *
+ * @param moveInsn {@code non-null;} a move result instruction
+ * @return {@code non-null;} the instruction that produces the result for
+ * the move
+ */
+ private SsaInsn getInsnForMove(SsaInsn moveInsn) {
+ int pred = moveInsn.getBlock().getPredecessors().nextSetBit(0);
+ ArrayList<SsaInsn> predInsns = ssaMeth.getBlocks().get(pred).getInsns();
+ return predInsns.get(predInsns.size()-1);
+ }
+
+ /**
+ * Finds the corresponding move result for a given instruction
+ *
+ * @param insn {@code non-null;} an instruction that must always be
+ * followed by a move result
+ * @return {@code non-null;} the move result for the given instruction
+ */
+ private SsaInsn getMoveForInsn(SsaInsn insn) {
+ int succ = insn.getBlock().getSuccessors().nextSetBit(0);
+ ArrayList<SsaInsn> succInsns = ssaMeth.getBlocks().get(succ).getInsns();
+ return succInsns.get(0);
+ }
+
+ /**
+ * Creates a link in the lattice between two EscapeSets due to a put
+ * instruction. The object being put is the child and the object being put
+ * into is the parent. A child set must always have an escape state at
+ * least as high as its parent.
+ *
+ * @param parentSet {@code non-null;} the EscapeSet for the object being put
+ * into
+ * @param childSet {@code non-null;} the EscapeSet for the object being put
+ */
+ private void addEdge(EscapeSet parentSet, EscapeSet childSet) {
+ if (!childSet.parentSets.contains(parentSet)) {
+ childSet.parentSets.add(parentSet);
+ }
+ if (!parentSet.childSets.contains(childSet)) {
+ parentSet.childSets.add(childSet);
+ }
+ }
+
+ /**
+ * Merges all links in the lattice among two EscapeSets. On return, the
+ * newNode will have its old links as well as all links from the oldNode.
+ * The oldNode has all its links removed.
+ *
+ * @param newNode {@code non-null;} the EscapeSet to merge all links into
+ * @param oldNode {@code non-null;} the EscapeSet to remove all links from
+ */
+ private void replaceNode(EscapeSet newNode, EscapeSet oldNode) {
+ for (EscapeSet e : oldNode.parentSets) {
+ e.childSets.remove(oldNode);
+ e.childSets.add(newNode);
+ newNode.parentSets.add(e);
+ }
+ for (EscapeSet e : oldNode.childSets) {
+ e.parentSets.remove(oldNode);
+ e.parentSets.add(newNode);
+ newNode.childSets.add(e);
+ }
+ }
+
+ /**
+ * Performs escape analysis on a method. Finds scalar replaceable arrays and
+ * replaces them with equivalent registers.
+ *
+ * @param ssaMethod {@code non-null;} method to process
+ */
+ public static void process(SsaMethod ssaMethod) {
+ new EscapeAnalysis(ssaMethod).run();
+ }
+
+ /**
+ * Process a single instruction, looking for new objects resulting from
+ * move result or move param.
+ *
+ * @param insn {@code non-null;} instruction to process
+ */
+ private void processInsn(SsaInsn insn) {
+ int op = insn.getOpcode().getOpcode();
+ RegisterSpec result = insn.getResult();
+ EscapeSet escSet;
+
+ // Identify new objects
+ if (op == RegOps.MOVE_RESULT_PSEUDO &&
+ result.getTypeBearer().getBasicType() == Type.BT_OBJECT) {
+ // Handle objects generated through move_result_pseudo
+ escSet = processMoveResultPseudoInsn(insn);
+ processRegister(result, escSet);
+ } else if (op == RegOps.MOVE_PARAM &&
+ result.getTypeBearer().getBasicType() == Type.BT_OBJECT) {
+ // Track method arguments that are objects
+ escSet = new EscapeSet(result.getReg(), regCount, EscapeState.NONE);
+ latticeValues.add(escSet);
+ processRegister(result, escSet);
+ } else if (op == RegOps.MOVE_RESULT &&
+ result.getTypeBearer().getBasicType() == Type.BT_OBJECT) {
+ // Track method return values that are objects
+ escSet = new EscapeSet(result.getReg(), regCount, EscapeState.NONE);
+ latticeValues.add(escSet);
+ processRegister(result, escSet);
+ }
+ }
+
+ /**
+ * Determine the origin of a move result pseudo instruction that generates
+ * an object. Creates a new EscapeSet for the new object accordingly.
+ *
+ * @param insn {@code non-null;} move result pseudo instruction to process
+ * @return {@code non-null;} an EscapeSet for the object referred to by the
+ * move result pseudo instruction
+ */
+ private EscapeSet processMoveResultPseudoInsn(SsaInsn insn) {
+ RegisterSpec result = insn.getResult();
+ SsaInsn prevSsaInsn = getInsnForMove(insn);
+ int prevOpcode = prevSsaInsn.getOpcode().getOpcode();
+ EscapeSet escSet;
+ RegisterSpec prevSource;
+
+ switch(prevOpcode) {
+ // New instance / Constant
+ case RegOps.NEW_INSTANCE:
+ case RegOps.CONST:
+ escSet = new EscapeSet(result.getReg(), regCount,
+ EscapeState.NONE);
+ break;
+ // New array
+ case RegOps.NEW_ARRAY:
+ case RegOps.FILLED_NEW_ARRAY:
+ prevSource = prevSsaInsn.getSources().get(0);
+ if (prevSource.getTypeBearer().isConstant()) {
+ // New fixed array
+ escSet = new EscapeSet(result.getReg(), regCount,
+ EscapeState.NONE);
+ escSet.replaceableArray = true;
+ } else {
+ // New variable array
+ escSet = new EscapeSet(result.getReg(), regCount,
+ EscapeState.GLOBAL);
+ }
+ break;
+ // Loading a static object
+ case RegOps.GET_STATIC:
+ escSet = new EscapeSet(result.getReg(), regCount,
+ EscapeState.GLOBAL);
+ break;
+ // Type cast / load an object from a field or array
+ case RegOps.CHECK_CAST:
+ case RegOps.GET_FIELD:
+ case RegOps.AGET:
+ prevSource = prevSsaInsn.getSources().get(0);
+ int setIndex = findSetIndex(prevSource);
+
+ // Set should already exist, try to find it
+ if (setIndex != latticeValues.size()) {
+ escSet = latticeValues.get(setIndex);
+ escSet.regSet.set(result.getReg());
+ return escSet;
+ }
+
+ // Set not found, must be either null or unknown
+ if (prevSource.getType() == Type.KNOWN_NULL) {
+ escSet = new EscapeSet(result.getReg(), regCount,
+ EscapeState.NONE);
+ } else {
+ escSet = new EscapeSet(result.getReg(), regCount,
+ EscapeState.GLOBAL);
+ }
+ break;
+ default:
+ return null;
+ }
+
+ // Add the newly created escSet to the lattice and return it
+ latticeValues.add(escSet);
+ return escSet;
+ }
+
+ /**
+ * Iterate through all the uses of a new object.
+ *
+ * @param result {@code non-null;} register where new object is stored
+ * @param escSet {@code non-null;} EscapeSet for the new object
+ */
+ private void processRegister(RegisterSpec result, EscapeSet escSet) {
+ ArrayList<RegisterSpec> regWorklist = new ArrayList<RegisterSpec>();
+ regWorklist.add(result);
+
+ // Go through the worklist
+ while (!regWorklist.isEmpty()) {
+ int listSize = regWorklist.size() - 1;
+ RegisterSpec def = regWorklist.remove(listSize);
+ List<SsaInsn> useList = ssaMeth.getUseListForRegister(def.getReg());
+
+ // Handle all the uses of this register
+ for (SsaInsn use : useList) {
+ Rop useOpcode = use.getOpcode();
+
+ if (useOpcode == null) {
+ // Handle phis
+ processPhiUse(use, escSet, regWorklist);
+ } else {
+ // Handle other opcodes
+ processUse(def, use, escSet, regWorklist);
+ }
+ }
+ }
+ }
+
+ /**
+ * Handles phi uses of new objects. Will merge together the sources of a phi
+ * into a single EscapeSet. Adds the result of the phi to the worklist so
+ * its uses can be followed.
+ *
+ * @param use {@code non-null;} phi use being processed
+ * @param escSet {@code non-null;} EscapeSet for the object
+ * @param regWorklist {@code non-null;} worklist of instructions left to
+ * process for this object
+ */
+ private void processPhiUse(SsaInsn use, EscapeSet escSet,
+ ArrayList<RegisterSpec> regWorklist) {
+ int setIndex = findSetIndex(use.getResult());
+ if (setIndex != latticeValues.size()) {
+ // Check if result is in a set already
+ EscapeSet mergeSet = latticeValues.get(setIndex);
+ if (mergeSet != escSet) {
+ // If it is, merge the sets and states, then delete the copy
+ escSet.replaceableArray = false;
+ escSet.regSet.or(mergeSet.regSet);
+ if (escSet.escape.compareTo(mergeSet.escape) < 0) {
+ escSet.escape = mergeSet.escape;
+ }
+ replaceNode(escSet, mergeSet);
+ latticeValues.remove(setIndex);
+ }
+ } else {
+ // If no set is found, add it to this escSet and the worklist
+ escSet.regSet.set(use.getResult().getReg());
+ regWorklist.add(use.getResult());
+ }
+ }
+
+ /**
+ * Handles non-phi uses of new objects. Checks to see how instruction is
+ * used and updates the escape state accordingly.
+ *
+ * @param def {@code non-null;} register holding definition of new object
+ * @param use {@code non-null;} use of object being processed
+ * @param escSet {@code non-null;} EscapeSet for the object
+ * @param regWorklist {@code non-null;} worklist of instructions left to
+ * process for this object
+ */
+ private void processUse(RegisterSpec def, SsaInsn use, EscapeSet escSet,
+ ArrayList<RegisterSpec> regWorklist) {
+ int useOpcode = use.getOpcode().getOpcode();
+ switch (useOpcode) {
+ case RegOps.MOVE:
+ // Follow uses of the move by adding it to the worklist
+ escSet.regSet.set(use.getResult().getReg());
+ regWorklist.add(use.getResult());
+ break;
+ case RegOps.IF_EQ:
+ case RegOps.IF_NE:
+ case RegOps.CHECK_CAST:
+ // Compared objects can't be replaced, so promote if necessary
+ if (escSet.escape.compareTo(EscapeState.METHOD) < 0) {
+ escSet.escape = EscapeState.METHOD;
+ }
+ break;
+ case RegOps.APUT:
+ // For array puts, check for a constant array index
+ RegisterSpec putIndex = use.getSources().get(2);
+ if (!putIndex.getTypeBearer().isConstant()) {
+ // If not constant, array can't be replaced
+ escSet.replaceableArray = false;
+ }
+ // Intentional fallthrough
+ case RegOps.PUT_FIELD:
+ // Skip non-object puts
+ RegisterSpec putValue = use.getSources().get(0);
+ if (putValue.getTypeBearer().getBasicType() != Type.BT_OBJECT) {
+ break;
+ }
+ escSet.replaceableArray = false;
+
+ // Raise 1st object's escape state to 2nd if 2nd is higher
+ RegisterSpecList sources = use.getSources();
+ if (sources.get(0).getReg() == def.getReg()) {
+ int setIndex = findSetIndex(sources.get(1));
+ if (setIndex != latticeValues.size()) {
+ EscapeSet parentSet = latticeValues.get(setIndex);
+ addEdge(parentSet, escSet);
+ if (escSet.escape.compareTo(parentSet.escape) < 0) {
+ escSet.escape = parentSet.escape;
+ }
+ }
+ } else {
+ int setIndex = findSetIndex(sources.get(0));
+ if (setIndex != latticeValues.size()) {
+ EscapeSet childSet = latticeValues.get(setIndex);
+ addEdge(escSet, childSet);
+ if (childSet.escape.compareTo(escSet.escape) < 0) {
+ childSet.escape = escSet.escape;
+ }
+ }
+ }
+ break;
+ case RegOps.AGET:
+ // For array gets, check for a constant array index
+ RegisterSpec getIndex = use.getSources().get(1);
+ if (!getIndex.getTypeBearer().isConstant()) {
+ // If not constant, array can't be replaced
+ escSet.replaceableArray = false;
+ }
+ break;
+ case RegOps.PUT_STATIC:
+ // Static puts cause an object to escape globally
+ escSet.escape = EscapeState.GLOBAL;
+ break;
+ case RegOps.INVOKE_STATIC:
+ case RegOps.INVOKE_VIRTUAL:
+ case RegOps.INVOKE_SUPER:
+ case RegOps.INVOKE_DIRECT:
+ case RegOps.INVOKE_INTERFACE:
+ case RegOps.RETURN:
+ case RegOps.THROW:
+ // These operations cause an object to escape interprocedurally
+ escSet.escape = EscapeState.INTER;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Performs scalar replacement on all eligible arrays.
+ */
+ private void scalarReplacement() {
+ // Iterate through lattice, looking for non-escaping replaceable arrays
+ for (EscapeSet escSet : latticeValues) {
+ if (!escSet.replaceableArray || escSet.escape != EscapeState.NONE) {
+ continue;
+ }
+
+ // Get the instructions for the definition and move of the array
+ int e = escSet.regSet.nextSetBit(0);
+ SsaInsn def = ssaMeth.getDefinitionForRegister(e);
+ SsaInsn prev = getInsnForMove(def);
+
+ // Create a map for the new registers that will be created
+ TypeBearer lengthReg = prev.getSources().get(0).getTypeBearer();
+ int length = ((CstLiteralBits) lengthReg).getIntBits();
+ ArrayList<RegisterSpec> newRegs =
+ new ArrayList<RegisterSpec>(length);
+ HashSet<SsaInsn> deletedInsns = new HashSet<SsaInsn>();
+
+ // Replace the definition of the array with registers
+ replaceDef(def, prev, length, newRegs);
+
+ // Mark definition instructions for deletion
+ deletedInsns.add(prev);
+ deletedInsns.add(def);
+
+ // Go through all uses of the array
+ List<SsaInsn> useList = ssaMeth.getUseListForRegister(e);
+ for (SsaInsn use : useList) {
+ // Replace the use with scalars and then mark it for deletion
+ replaceUse(use, prev, newRegs, deletedInsns);
+ deletedInsns.add(use);
+ }
+
+ // Delete all marked instructions
+ ssaMeth.deleteInsns(deletedInsns);
+ ssaMeth.onInsnsChanged();
+
+ // Convert the method back to SSA form
+ SsaConverter.updateSsaMethod(ssaMeth, regCount);
+
+ // Propagate and remove extra moves added by scalar replacement
+ movePropagate();
+ }
+ }
+
+ /**
+ * Replaces the instructions that define an array with equivalent registers.
+ * For each entry in the array, a register is created, initialized to zero.
+ * A mapping between this register and the corresponding array index is
+ * added.
+ *
+ * @param def {@code non-null;} move result instruction for array
+ * @param prev {@code non-null;} instruction for instantiating new array
+ * @param length size of the new array
+ * @param newRegs {@code non-null;} mapping of array indices to new
+ * registers to be populated
+ */
+ private void replaceDef(SsaInsn def, SsaInsn prev, int length,
+ ArrayList<RegisterSpec> newRegs) {
+ Type resultType = def.getResult().getType();
+
+ // Create new zeroed out registers for each element in the array
+ for (int i = 0; i < length; i++) {
+ Constant newZero = Zeroes.zeroFor(resultType.getComponentType());
+ TypedConstant typedZero = (TypedConstant) newZero;
+ RegisterSpec newReg =
+ RegisterSpec.make(ssaMeth.makeNewSsaReg(), typedZero);
+ newRegs.add(newReg);
+ insertPlainInsnBefore(def, RegisterSpecList.EMPTY, newReg,
+ RegOps.CONST, newZero);
+ }
+ }
+
+ /**
+ * Replaces the use for a scalar replaceable array. Gets and puts become
+ * move instructions, and array lengths and fills are handled. Can also
+ * identify ArrayIndexOutOfBounds exceptions and throw them if detected.
+ *
+ * @param use {@code non-null;} move result instruction for array
+ * @param prev {@code non-null;} instruction for instantiating new array
+ * @param newRegs {@code non-null;} mapping of array indices to new
+ * registers
+ * @param deletedInsns {@code non-null;} set of instructions marked for
+ * deletion
+ */
+ private void replaceUse(SsaInsn use, SsaInsn prev,
+ ArrayList<RegisterSpec> newRegs,
+ HashSet<SsaInsn> deletedInsns) {
+ int index;
+ int length = newRegs.size();
+ SsaInsn next;
+ RegisterSpecList sources;
+ RegisterSpec source, result;
+ CstLiteralBits indexReg;
+
+ switch (use.getOpcode().getOpcode()) {
+ case RegOps.AGET:
+ // Replace array gets with moves
+ next = getMoveForInsn(use);
+ sources = use.getSources();
+ indexReg = ((CstLiteralBits) sources.get(1).getTypeBearer());
+ index = indexReg.getIntBits();
+ if (index < length) {
+ source = newRegs.get(index);
+ result = source.withReg(next.getResult().getReg());
+ insertPlainInsnBefore(next, RegisterSpecList.make(source),
+ result, RegOps.MOVE, null);
+ } else {
+ // Throw an exception if the index is out of bounds
+ insertExceptionThrow(next, sources.get(1), deletedInsns);
+ deletedInsns.add(next.getBlock().getInsns().get(2));
+ }
+ deletedInsns.add(next);
+ break;
+ case RegOps.APUT:
+ // Replace array puts with moves
+ sources = use.getSources();
+ indexReg = ((CstLiteralBits) sources.get(2).getTypeBearer());
+ index = indexReg.getIntBits();
+ if (index < length) {
+ source = sources.get(0);
+ result = source.withReg(newRegs.get(index).getReg());
+ insertPlainInsnBefore(use, RegisterSpecList.make(source),
+ result, RegOps.MOVE, null);
+ // Update the newReg entry to mark value as unknown now
+ newRegs.set(index, result.withSimpleType());
+ } else {
+ // Throw an exception if the index is out of bounds
+ insertExceptionThrow(use, sources.get(2), deletedInsns);
+ }
+ break;
+ case RegOps.ARRAY_LENGTH:
+ // Replace array lengths with const instructions
+ TypeBearer lengthReg = prev.getSources().get(0).getTypeBearer();
+ //CstInteger lengthReg = CstInteger.make(length);
+ next = getMoveForInsn(use);
+ insertPlainInsnBefore(next, RegisterSpecList.EMPTY,
+ next.getResult(), RegOps.CONST,
+ (Constant) lengthReg);
+ deletedInsns.add(next);
+ break;
+ case RegOps.MARK_LOCAL:
+ // Remove mark local instructions
+ break;
+ case RegOps.FILL_ARRAY_DATA:
+ // Create const instructions for each fill value
+ Insn ropUse = use.getOriginalRopInsn();
+ FillArrayDataInsn fill = (FillArrayDataInsn) ropUse;
+ ArrayList<Constant> constList = fill.getInitValues();
+ for (int i = 0; i < length; i++) {
+ RegisterSpec newFill =
+ RegisterSpec.make(newRegs.get(i).getReg(),
+ (TypeBearer) constList.get(i));
+ insertPlainInsnBefore(use, RegisterSpecList.EMPTY, newFill,
+ RegOps.CONST, constList.get(i));
+ // Update the newRegs to hold the new const value
+ newRegs.set(i, newFill);
+ }
+ break;
+ default:
+ }
+ }
+
+ /**
+ * Identifies extra moves added by scalar replacement and propagates the
+ * source of the move to any users of the result.
+ */
+ private void movePropagate() {
+ for (int i = 0; i < ssaMeth.getRegCount(); i++) {
+ SsaInsn insn = ssaMeth.getDefinitionForRegister(i);
+
+ // Look for move instructions only
+ if (insn == null || insn.getOpcode() == null ||
+ insn.getOpcode().getOpcode() != RegOps.MOVE) {
+ continue;
+ }
+
+ final ArrayList<SsaInsn>[] useList = ssaMeth.getUseListCopy();
+ final RegisterSpec source = insn.getSources().get(0);
+ final RegisterSpec result = insn.getResult();
+
+ // Ignore moves that weren't added due to scalar replacement
+ if (source.getReg() < regCount && result.getReg() < regCount) {
+ continue;
+ }
+
+ // Create a mapping from source to result
+ RegisterMapper mapper = new RegisterMapper() {
+ @Override
+ public int getNewRegisterCount() {
+ return ssaMeth.getRegCount();
+ }
+
+ @Override
+ public RegisterSpec map(RegisterSpec registerSpec) {
+ if (registerSpec.getReg() == result.getReg()) {
+ return source;
+ }
+
+ return registerSpec;
+ }
+ };
+
+ // Modify all uses of the move to use the source of the move instead
+ for (SsaInsn use : useList[result.getReg()]) {
+ use.mapSourceRegisters(mapper);
+ }
+ }
+ }
+
+ /**
+ * Runs escape analysis and scalar replacement of arrays.
+ */
+ private void run() {
+ ssaMeth.forEachBlockDepthFirstDom(new SsaBasicBlock.Visitor() {
+ public void visitBlock (SsaBasicBlock block,
+ SsaBasicBlock unused) {
+ block.forEachInsn(new SsaInsn.Visitor() {
+ public void visitMoveInsn(NormalSsaInsn insn) {
+ // do nothing
+ }
+
+ public void visitPhiInsn(PhiInsn insn) {
+ // do nothing
+ }
+
+ public void visitNonMoveInsn(NormalSsaInsn insn) {
+ processInsn(insn);
+ }
+ });
+ }
+ });
+
+ // Go through lattice and promote fieldSets as necessary
+ for (EscapeSet e : latticeValues) {
+ if (e.escape != EscapeState.NONE) {
+ for (EscapeSet field : e.childSets) {
+ if (e.escape.compareTo(field.escape) > 0) {
+ field.escape = e.escape;
+ }
+ }
+ }
+ }
+
+ // Perform scalar replacement for arrays
+ scalarReplacement();
+ }
+
+ /**
+ * Replaces instructions that trigger an ArrayIndexOutofBounds exception
+ * with an actual throw of the exception.
+ *
+ * @param insn {@code non-null;} instruction causing the exception
+ * @param index {@code non-null;} index value that is out of bounds
+ * @param deletedInsns {@code non-null;} set of instructions marked for
+ * deletion
+ */
+ private void insertExceptionThrow(SsaInsn insn, RegisterSpec index,
+ HashSet<SsaInsn> deletedInsns) {
+ // Create a new ArrayIndexOutOfBoundsException
+ CstType exception =
+ new CstType(Exceptions.TYPE_ArrayIndexOutOfBoundsException);
+ insertThrowingInsnBefore(insn, RegisterSpecList.EMPTY, null,
+ RegOps.NEW_INSTANCE, exception);
+
+ // Add a successor block with a move result pseudo for the exception
+ SsaBasicBlock currBlock = insn.getBlock();
+ SsaBasicBlock newBlock =
+ currBlock.insertNewSuccessor(currBlock.getPrimarySuccessor());
+ SsaInsn newInsn = newBlock.getInsns().get(0);
+ RegisterSpec newReg =
+ RegisterSpec.make(ssaMeth.makeNewSsaReg(), exception);
+ insertPlainInsnBefore(newInsn, RegisterSpecList.EMPTY, newReg,
+ RegOps.MOVE_RESULT_PSEUDO, null);
+
+ // Add another successor block to initialize the exception
+ SsaBasicBlock newBlock2 =
+ newBlock.insertNewSuccessor(newBlock.getPrimarySuccessor());
+ SsaInsn newInsn2 = newBlock2.getInsns().get(0);
+ CstNat newNat = new CstNat(new CstString("<init>"), new CstString("(I)V"));
+ CstMethodRef newRef = new CstMethodRef(exception, newNat);
+ insertThrowingInsnBefore(newInsn2, RegisterSpecList.make(newReg, index),
+ null, RegOps.INVOKE_DIRECT, newRef);
+ deletedInsns.add(newInsn2);
+
+ // Add another successor block to throw the new exception
+ SsaBasicBlock newBlock3 =
+ newBlock2.insertNewSuccessor(newBlock2.getPrimarySuccessor());
+ SsaInsn newInsn3 = newBlock3.getInsns().get(0);
+ insertThrowingInsnBefore(newInsn3, RegisterSpecList.make(newReg), null,
+ RegOps.THROW, null);
+ newBlock3.replaceSuccessor(newBlock3.getPrimarySuccessorIndex(),
+ ssaMeth.getExitBlock().getIndex());
+ deletedInsns.add(newInsn3);
+ }
+
+ /**
+ * Inserts a new PlainInsn before the given instruction.
+ * TODO: move this somewhere more appropriate
+ *
+ * @param insn {@code non-null;} instruction to insert before
+ * @param newSources {@code non-null;} sources of new instruction
+ * @param newResult {@code non-null;} result of new instruction
+ * @param newOpcode opcode of new instruction
+ * @param cst {@code null-ok;} constant for new instruction, if any
+ */
+ private void insertPlainInsnBefore(SsaInsn insn,
+ RegisterSpecList newSources, RegisterSpec newResult, int newOpcode,
+ Constant cst) {
+
+ Insn originalRopInsn = insn.getOriginalRopInsn();
+ Rop newRop;
+ if (newOpcode == RegOps.MOVE_RESULT_PSEUDO) {
+ newRop = Rops.opMoveResultPseudo(newResult.getType());
+ } else {
+ newRop = Rops.ropFor(newOpcode, newResult, newSources, cst);
+ }
+
+ Insn newRopInsn;
+ if (cst == null) {
+ newRopInsn = new PlainInsn(newRop,
+ originalRopInsn.getPosition(), newResult, newSources);
+ } else {
+ newRopInsn = new PlainCstInsn(newRop,
+ originalRopInsn.getPosition(), newResult, newSources, cst);
+ }
+
+ NormalSsaInsn newInsn = new NormalSsaInsn(newRopInsn, insn.getBlock());
+ List<SsaInsn> insns = insn.getBlock().getInsns();
+
+ insns.add(insns.lastIndexOf(insn), newInsn);
+ ssaMeth.onInsnAdded(newInsn);
+ }
+
+ /**
+ * Inserts a new ThrowingInsn before the given instruction.
+ * TODO: move this somewhere more appropriate
+ *
+ * @param insn {@code non-null;} instruction to insert before
+ * @param newSources {@code non-null;} sources of new instruction
+ * @param newResult {@code non-null;} result of new instruction
+ * @param newOpcode opcode of new instruction
+ * @param cst {@code null-ok;} constant for new instruction, if any
+ */
+ private void insertThrowingInsnBefore(SsaInsn insn,
+ RegisterSpecList newSources, RegisterSpec newResult, int newOpcode,
+ Constant cst) {
+
+ Insn origRopInsn = insn.getOriginalRopInsn();
+ Rop newRop = Rops.ropFor(newOpcode, newResult, newSources, cst);
+ Insn newRopInsn;
+ if (cst == null) {
+ newRopInsn = new ThrowingInsn(newRop,
+ origRopInsn.getPosition(), newSources, StdTypeList.EMPTY);
+ } else {
+ newRopInsn = new ThrowingCstInsn(newRop,
+ origRopInsn.getPosition(), newSources, StdTypeList.EMPTY, cst);
+ }
+
+ NormalSsaInsn newInsn = new NormalSsaInsn(newRopInsn, insn.getBlock());
+ List<SsaInsn> insns = insn.getBlock().getInsns();
+
+ insns.add(insns.lastIndexOf(insn), newInsn);
+ ssaMeth.onInsnAdded(newInsn);
+ }
+}
diff --git a/dx/src/com/android/jack/dx/ssa/InterferenceRegisterMapper.java b/dx/src/com/android/jack/dx/ssa/InterferenceRegisterMapper.java
new file mode 100644
index 00000000..589e2f28
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/InterferenceRegisterMapper.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa;
+
+import com.android.jack.dx.rop.code.RegisterSpec;
+import com.android.jack.dx.rop.code.RegisterSpecList;
+import com.android.jack.dx.ssa.back.InterferenceGraph;
+import com.android.jack.dx.util.BitIntSet;
+import com.android.jack.dx.util.IntSet;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+
+/**
+ * A register mapper that keeps track of the accumulated interference
+ * information for the registers in the new namespace.
+ *
+ * Please note that this mapper requires that the old namespace does not
+ * have variable register widths/categories, and the new namespace does.
+ */
+public class InterferenceRegisterMapper extends BasicRegisterMapper {
+ /**
+ * Array of interference sets. ArrayList is indexed by new namespace
+ * and BitIntSet's are indexed by old namespace. The list expands
+ * as needed and missing items are assumed to interfere with nothing.
+ *
+ * Bit sets are always used here, unlike elsewhere, because the max
+ * size of this matrix will be (countSsaRegs * countRopRegs), which may
+ * grow to hundreds of K but not megabytes.
+ */
+ private final ArrayList<BitIntSet> newRegInterference;
+
+ /** the interference graph for the old namespace */
+ private final InterferenceGraph oldRegInterference;
+
+ /**
+ * Constructs an instance
+ *
+ * @param countOldRegisters number of registers in old namespace
+ */
+ public InterferenceRegisterMapper(InterferenceGraph oldRegInterference,
+ int countOldRegisters) {
+ super(countOldRegisters);
+
+ newRegInterference = new ArrayList<BitIntSet>();
+ this.oldRegInterference = oldRegInterference;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addMapping(int oldReg, int newReg, int category) {
+ super.addMapping(oldReg, newReg, category);
+
+ addInterfence(newReg, oldReg);
+
+ if (category == 2) {
+ addInterfence(newReg + 1, oldReg);
+ }
+ }
+
+ /**
+ * Checks to see if old namespace reg {@code oldReg} interferes
+ * with what currently maps to {@code newReg}.
+ *
+ * @param oldReg old namespace register
+ * @param newReg new namespace register
+ * @param category category of old namespace register
+ * @return true if oldReg will interfere with newReg
+ */
+ public boolean interferes(int oldReg, int newReg, int category) {
+ if (newReg >= newRegInterference.size()) {
+ return false;
+ } else {
+ IntSet existing = newRegInterference.get(newReg);
+
+ if (existing == null) {
+ return false;
+ } else if (category == 1) {
+ return existing.has(oldReg);
+ } else {
+ return existing.has(oldReg)
+ || (interferes(oldReg, newReg+1, category-1));
+ }
+ }
+ }
+
+ /**
+ * Checks to see if old namespace reg {@code oldReg} interferes
+ * with what currently maps to {@code newReg}.
+ *
+ * @param oldSpec {@code non-null;} old namespace register
+ * @param newReg new namespace register
+ * @return true if oldReg will interfere with newReg
+ */
+ public boolean interferes(RegisterSpec oldSpec, int newReg) {
+ return interferes(oldSpec.getReg(), newReg, oldSpec.getCategory());
+ }
+
+ /**
+ * Adds a register's interference set to the interference list,
+ * growing it if necessary.
+ *
+ * @param newReg register in new namespace
+ * @param oldReg register in old namespace
+ */
+ private void addInterfence(int newReg, int oldReg) {
+ newRegInterference.ensureCapacity(newReg + 1);
+
+ while (newReg >= newRegInterference.size()) {
+ newRegInterference.add(new BitIntSet(newReg +1));
+ }
+
+ oldRegInterference.mergeInterferenceSet(
+ oldReg, newRegInterference.get(newReg));
+ }
+
+ /**
+ * Checks to see if any of a set of old-namespace registers are
+ * pinned to the specified new-namespace reg + category. Takes into
+ * account the category of the old-namespace registers.
+ *
+ * @param oldSpecs {@code non-null;} set of old-namespace regs
+ * @param newReg {@code >= 0;} new-namespace register
+ * @param targetCategory {@code 1..2;} the number of adjacent new-namespace
+ * registers (starting at ropReg) to consider
+ * @return true if any of the old-namespace register have been mapped
+ * to the new-namespace register + category
+ */
+ public boolean areAnyPinned(RegisterSpecList oldSpecs,
+ int newReg, int targetCategory) {
+ int sz = oldSpecs.size();
+
+ for (int i = 0; i < sz; i++) {
+ RegisterSpec oldSpec = oldSpecs.get(i);
+ int r = oldToNew(oldSpec.getReg());
+
+ /*
+ * If oldSpec is a category-2 register, then check both newReg
+ * and newReg - 1.
+ */
+ if (r == newReg
+ || (oldSpec.getCategory() == 2 && (r + 1) == newReg)
+ || (targetCategory == 2 && (r == newReg + 1))) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/dx/src/com/android/jack/dx/ssa/LiteralOpUpgrader.java b/dx/src/com/android/jack/dx/ssa/LiteralOpUpgrader.java
new file mode 100644
index 00000000..ebf94019
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/LiteralOpUpgrader.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa;
+
+import com.android.jack.dx.rop.code.Insn;
+import com.android.jack.dx.rop.code.PlainCstInsn;
+import com.android.jack.dx.rop.code.PlainInsn;
+import com.android.jack.dx.rop.code.RegOps;
+import com.android.jack.dx.rop.code.RegisterSpec;
+import com.android.jack.dx.rop.code.RegisterSpecList;
+import com.android.jack.dx.rop.code.Rop;
+import com.android.jack.dx.rop.code.Rops;
+import com.android.jack.dx.rop.code.TranslationAdvice;
+import com.android.jack.dx.rop.cst.Constant;
+import com.android.jack.dx.rop.cst.CstLiteralBits;
+import com.android.jack.dx.rop.type.Type;
+import com.android.jack.dx.rop.type.TypeBearer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Upgrades insn to their literal (constant-immediate) equivalent if possible.
+ * Also switches IF instructions that compare with a constant zero or null
+ * to be their IF_*Z equivalents.
+ */
+public class LiteralOpUpgrader {
+
+ /** method we're processing */
+ private final SsaMethod ssaMeth;
+
+ /**
+ * Process a method.
+ *
+ * @param ssaMethod {@code non-null;} method to process
+ */
+ public static void process(SsaMethod ssaMethod) {
+ LiteralOpUpgrader dc;
+
+ dc = new LiteralOpUpgrader(ssaMethod);
+
+ dc.run();
+ }
+
+ private LiteralOpUpgrader(SsaMethod ssaMethod) {
+ this.ssaMeth = ssaMethod;
+ }
+
+ /**
+ * Returns true if the register contains an integer 0 or a known-null
+ * object reference
+ *
+ * @param spec non-null spec
+ * @return true for 0 or null type bearers
+ */
+ private static boolean isConstIntZeroOrKnownNull(RegisterSpec spec) {
+ TypeBearer tb = spec.getTypeBearer();
+ if (tb instanceof CstLiteralBits) {
+ CstLiteralBits clb = (CstLiteralBits) tb;
+ return (clb.getLongBits() == 0);
+ }
+ return false;
+ }
+
+ /**
+ * Run the literal op upgrader
+ */
+ private void run() {
+ final TranslationAdvice advice = Optimizer.getAdvice();
+
+ ssaMeth.forEachInsn(new SsaInsn.Visitor() {
+ public void visitMoveInsn(NormalSsaInsn insn) {
+ // do nothing
+ }
+
+ public void visitPhiInsn(PhiInsn insn) {
+ // do nothing
+ }
+
+ public void visitNonMoveInsn(NormalSsaInsn insn) {
+
+ Insn originalRopInsn = insn.getOriginalRopInsn();
+ Rop opcode = originalRopInsn.getOpcode();
+ RegisterSpecList sources = insn.getSources();
+
+ // Replace insns with constant results with const insns
+ if (tryReplacingWithConstant(insn)) return;
+
+ if (sources.size() != 2 ) {
+ // We're only dealing with two-source insns here.
+ return;
+ }
+
+ if (opcode.getBranchingness() == Rop.BRANCH_IF) {
+ /*
+ * An if instruction can become an if-*z instruction.
+ */
+ if (isConstIntZeroOrKnownNull(sources.get(0))) {
+ replacePlainInsn(insn, sources.withoutFirst(),
+ RegOps.flippedIfOpcode(opcode.getOpcode()), null);
+ } else if (isConstIntZeroOrKnownNull(sources.get(1))) {
+ replacePlainInsn(insn, sources.withoutLast(),
+ opcode.getOpcode(), null);
+ }
+ } else if (advice.hasConstantOperation(
+ opcode, sources.get(0), sources.get(1))) {
+ insn.upgradeToLiteral();
+ } else if (opcode.isCommutative()
+ && advice.hasConstantOperation(
+ opcode, sources.get(1), sources.get(0))) {
+ /*
+ * An instruction can be commuted to a literal operation
+ */
+
+ insn.setNewSources(
+ RegisterSpecList.make(
+ sources.get(1), sources.get(0)));
+
+ insn.upgradeToLiteral();
+ }
+ }
+ });
+ }
+
+ /**
+ * Tries to replace an instruction with a const instruction. The given
+ * instruction must have a constant result for it to be replaced.
+ *
+ * @param insn {@code non-null;} instruction to try to replace
+ * @return true if the instruction was replaced
+ */
+ private boolean tryReplacingWithConstant(NormalSsaInsn insn) {
+ Insn originalRopInsn = insn.getOriginalRopInsn();
+ Rop opcode = originalRopInsn.getOpcode();
+ RegisterSpec result = insn.getResult();
+
+ if (result != null && !ssaMeth.isRegALocal(result) &&
+ opcode.getOpcode() != RegOps.CONST) {
+ TypeBearer type = insn.getResult().getTypeBearer();
+ if (type.isConstant() && type.getBasicType() == Type.BT_INT) {
+ // Replace the instruction with a constant
+ replacePlainInsn(insn, RegisterSpecList.EMPTY,
+ RegOps.CONST, (Constant) type);
+
+ // Remove the source as well if this is a move-result-pseudo
+ if (opcode.getOpcode() == RegOps.MOVE_RESULT_PSEUDO) {
+ int pred = insn.getBlock().getPredecessors().nextSetBit(0);
+ ArrayList<SsaInsn> predInsns =
+ ssaMeth.getBlocks().get(pred).getInsns();
+ NormalSsaInsn sourceInsn =
+ (NormalSsaInsn) predInsns.get(predInsns.size()-1);
+ replacePlainInsn(sourceInsn, RegisterSpecList.EMPTY,
+ RegOps.GOTO, null);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Replaces an SsaInsn containing a PlainInsn with a new PlainInsn. The
+ * new PlainInsn is constructed with a new RegOp and new sources.
+ *
+ * TODO move this somewhere else.
+ *
+ * @param insn {@code non-null;} an SsaInsn containing a PlainInsn
+ * @param newSources {@code non-null;} new sources list for new insn
+ * @param newOpcode A RegOp from {@link RegOps}
+ * @param cst {@code null-ok;} constant for new instruction, if any
+ */
+ private void replacePlainInsn(NormalSsaInsn insn,
+ RegisterSpecList newSources, int newOpcode, Constant cst) {
+
+ Insn originalRopInsn = insn.getOriginalRopInsn();
+ Rop newRop = Rops.ropFor(newOpcode, insn.getResult(), newSources, cst);
+ Insn newRopInsn;
+ if (cst == null) {
+ newRopInsn = new PlainInsn(newRop, originalRopInsn.getPosition(),
+ insn.getResult(), newSources);
+ } else {
+ newRopInsn = new PlainCstInsn(newRop, originalRopInsn.getPosition(),
+ insn.getResult(), newSources, cst);
+ }
+ NormalSsaInsn newInsn = new NormalSsaInsn(newRopInsn, insn.getBlock());
+
+ List<SsaInsn> insns = insn.getBlock().getInsns();
+
+ ssaMeth.onInsnRemoved(insn);
+ insns.set(insns.lastIndexOf(insn), newInsn);
+ ssaMeth.onInsnAdded(newInsn);
+ }
+}
diff --git a/dx/src/com/android/jack/dx/ssa/LocalVariableExtractor.java b/dx/src/com/android/jack/dx/ssa/LocalVariableExtractor.java
new file mode 100644
index 00000000..a16eeb19
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/LocalVariableExtractor.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa;
+
+import com.android.jack.dx.rop.code.RegisterSpec;
+import com.android.jack.dx.rop.code.RegisterSpecSet;
+import com.android.jack.dx.util.IntList;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.List;
+
+/**
+ * Code to figure out which local variables are active at which points in
+ * a method. Stolen and retrofitted from
+ * com.android.jack.dx.rop.code.LocalVariableExtractor
+ *
+ * TODO remove this. Allow Rop-form LocalVariableInfo to be passed in,
+ * converted, and adapted through edge-splitting.
+ */
+public class LocalVariableExtractor {
+ /** {@code non-null;} method being extracted from */
+ private final SsaMethod method;
+
+ /** {@code non-null;} block list for the method */
+ private final ArrayList<SsaBasicBlock> blocks;
+
+ /** {@code non-null;} result in-progress */
+ private final LocalVariableInfo resultInfo;
+
+ /** {@code non-null;} work set indicating blocks needing to be processed */
+ private final BitSet workSet;
+
+ /**
+ * Extracts out all the local variable information from the given method.
+ *
+ * @param method {@code non-null;} the method to extract from
+ * @return {@code non-null;} the extracted information
+ */
+ public static LocalVariableInfo extract(SsaMethod method) {
+ LocalVariableExtractor lve = new LocalVariableExtractor(method);
+ return lve.doit();
+ }
+
+ /**
+ * Constructs an instance. This method is private. Use {@link #extract}.
+ *
+ * @param method {@code non-null;} the method to extract from
+ */
+ private LocalVariableExtractor(SsaMethod method) {
+ if (method == null) {
+ throw new NullPointerException("method == null");
+ }
+
+ ArrayList<SsaBasicBlock> blocks = method.getBlocks();
+
+ this.method = method;
+ this.blocks = blocks;
+ this.resultInfo = new LocalVariableInfo(method);
+ this.workSet = new BitSet(blocks.size());
+ }
+
+ /**
+ * Does the extraction.
+ *
+ * @return {@code non-null;} the extracted information
+ */
+ private LocalVariableInfo doit() {
+
+ //FIXME why is this needed here?
+ if (method.getRegCount() > 0 ) {
+ for (int bi = method.getEntryBlockIndex();
+ bi >= 0;
+ bi = workSet.nextSetBit(0)) {
+ workSet.clear(bi);
+ processBlock(bi);
+ }
+ }
+
+ resultInfo.setImmutable();
+ return resultInfo;
+ }
+
+ /**
+ * Processes a single block.
+ *
+ * @param blockIndex {@code >= 0;} block index of the block to process
+ */
+ private void processBlock(int blockIndex) {
+ RegisterSpecSet primaryState
+ = resultInfo.mutableCopyOfStarts(blockIndex);
+ SsaBasicBlock block = blocks.get(blockIndex);
+ List<SsaInsn> insns = block.getInsns();
+ int insnSz = insns.size();
+
+ // The exit block has no insns and no successors
+ if (blockIndex == method.getExitBlockIndex()) {
+ return;
+ }
+
+ /*
+ * We may have to treat the last instruction specially: If it
+ * can (but doesn't always) throw, and the exception can be
+ * caught within the same method, then we need to use the
+ * state *before* executing it to be what is merged into
+ * exception targets.
+ */
+ SsaInsn lastInsn = insns.get(insnSz - 1);
+ boolean hasExceptionHandlers
+ = lastInsn.getOriginalRopInsn().getCatches().size() !=0 ;
+ boolean canThrowDuringLastInsn = hasExceptionHandlers
+ && (lastInsn.getResult() != null);
+ int freezeSecondaryStateAt = insnSz - 1;
+ RegisterSpecSet secondaryState = primaryState;
+
+ /*
+ * Iterate over the instructions, adding information for each place
+ * that the active variable set changes.
+ */
+
+ for (int i = 0; i < insnSz; i++) {
+ if (canThrowDuringLastInsn && (i == freezeSecondaryStateAt)) {
+ // Until this point, primaryState == secondaryState.
+ primaryState.setImmutable();
+ primaryState = primaryState.mutableCopy();
+ }
+
+ SsaInsn insn = insns.get(i);
+ RegisterSpec result;
+
+ result = insn.getLocalAssignment();
+
+ if (result == null) {
+ // We may be nuking an existing local
+
+ result = insn.getResult();
+
+ if (result != null && primaryState.get(result.getReg()) != null) {
+ primaryState.remove(primaryState.get(result.getReg()));
+ }
+ continue;
+ }
+
+ result = result.withSimpleType();
+
+ RegisterSpec already = primaryState.get(result);
+ /*
+ * The equals() check ensures we only add new info if
+ * the instruction causes a change to the set of
+ * active variables.
+ */
+ if (!result.equals(already)) {
+ /*
+ * If this insn represents a local moving from one register
+ * to another, remove the association between the old register
+ * and the local.
+ */
+ RegisterSpec previous
+ = primaryState.localItemToSpec(result.getLocalItem());
+
+ if (previous != null
+ && (previous.getReg() != result.getReg())) {
+
+ primaryState.remove(previous);
+ }
+
+ resultInfo.addAssignment(insn, result);
+ primaryState.put(result);
+ }
+ }
+
+ primaryState.setImmutable();
+
+ /*
+ * Merge this state into the start state for each successor,
+ * and update the work set where required (that is, in cases
+ * where the start state for a block changes).
+ */
+
+ IntList successors = block.getSuccessorList();
+ int succSz = successors.size();
+ int primarySuccessor = block.getPrimarySuccessorIndex();
+
+ for (int i = 0; i < succSz; i++) {
+ int succ = successors.get(i);
+ RegisterSpecSet state = (succ == primarySuccessor) ?
+ primaryState : secondaryState;
+
+ if (resultInfo.mergeStarts(succ, state)) {
+ workSet.set(succ);
+ }
+ }
+ }
+}
diff --git a/dx/src/com/android/jack/dx/ssa/LocalVariableInfo.java b/dx/src/com/android/jack/dx/ssa/LocalVariableInfo.java
new file mode 100644
index 00000000..0ff957c0
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/LocalVariableInfo.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa;
+
+import com.android.jack.dx.rop.code.RegisterSpec;
+import com.android.jack.dx.rop.code.RegisterSpecSet;
+import com.android.jack.dx.util.MutabilityControl;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Container for local variable information for a particular {@link
+ * com.android.jack.dx.ssa.SsaMethod}.
+ * Stolen from {@link com.android.jack.dx.rop.code.LocalVariableInfo}.
+ */
+public class LocalVariableInfo extends MutabilityControl {
+ /** {@code >= 0;} the register count for the method */
+ private final int regCount;
+
+ /**
+ * {@code non-null;} {@link com.android.jack.dx.rop.code.RegisterSpecSet} to use when indicating a block
+ * that has no locals; it is empty and immutable but has an appropriate
+ * max size for the method
+ */
+ private final RegisterSpecSet emptySet;
+
+ /**
+ * {@code non-null;} array consisting of register sets representing the
+ * sets of variables already assigned upon entry to each block,
+ * where array indices correspond to block indices
+ */
+ private final RegisterSpecSet[] blockStarts;
+
+ /** {@code non-null;} map from instructions to the variable each assigns */
+ private final HashMap<SsaInsn, RegisterSpec> insnAssignments;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param method {@code non-null;} the method being represented by this instance
+ */
+ public LocalVariableInfo(SsaMethod method) {
+ if (method == null) {
+ throw new NullPointerException("method == null");
+ }
+
+ List<SsaBasicBlock> blocks = method.getBlocks();
+
+ this.regCount = method.getRegCount();
+ this.emptySet = new RegisterSpecSet(regCount);
+ this.blockStarts = new RegisterSpecSet[blocks.size()];
+ this.insnAssignments =
+ new HashMap<SsaInsn, RegisterSpec>(/*hint here*/);
+
+ emptySet.setImmutable();
+ }
+
+ /**
+ * Sets the register set associated with the start of the block with
+ * the given index.
+ *
+ * @param index {@code >= 0;} the block index
+ * @param specs {@code non-null;} the register set to associate with the block
+ */
+ public void setStarts(int index, RegisterSpecSet specs) {
+ throwIfImmutable();
+
+ if (specs == null) {
+ throw new NullPointerException("specs == null");
+ }
+
+ try {
+ blockStarts[index] = specs;
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ // Translate the exception.
+ throw new IllegalArgumentException("bogus index");
+ }
+ }
+
+ /**
+ * Merges the given register set into the set for the block with the
+ * given index. If there was not already an associated set, then this
+ * is the same as calling {@link #setStarts}. Otherwise, this will
+ * merge the two sets and call {@link #setStarts} on the result of the
+ * merge.
+ *
+ * @param index {@code >= 0;} the block index
+ * @param specs {@code non-null;} the register set to merge into the start set
+ * for the block
+ * @return {@code true} if the merge resulted in an actual change
+ * to the associated set (including storing one for the first time) or
+ * {@code false} if there was no change
+ */
+ public boolean mergeStarts(int index, RegisterSpecSet specs) {
+ RegisterSpecSet start = getStarts0(index);
+ boolean changed = false;
+
+ if (start == null) {
+ setStarts(index, specs);
+ return true;
+ }
+
+ RegisterSpecSet newStart = start.mutableCopy();
+ newStart.intersect(specs, true);
+
+ if (start.equals(newStart)) {
+ return false;
+ }
+
+ newStart.setImmutable();
+ setStarts(index, newStart);
+
+ return true;
+ }
+
+ /**
+ * Gets the register set associated with the start of the block
+ * with the given index. This returns an empty set with the appropriate
+ * max size if no set was associated with the block in question.
+ *
+ * @param index {@code >= 0;} the block index
+ * @return {@code non-null;} the associated register set
+ */
+ public RegisterSpecSet getStarts(int index) {
+ RegisterSpecSet result = getStarts0(index);
+
+ return (result != null) ? result : emptySet;
+ }
+
+ /**
+ * Gets the register set associated with the start of the given
+ * block. This is just convenient shorthand for
+ * {@code getStarts(block.getLabel())}.
+ *
+ * @param block {@code non-null;} the block in question
+ * @return {@code non-null;} the associated register set
+ */
+ public RegisterSpecSet getStarts(SsaBasicBlock block) {
+ return getStarts(block.getIndex());
+ }
+
+ /**
+ * Gets a mutable copy of the register set associated with the
+ * start of the block with the given index. This returns a
+ * newly-allocated empty {@link RegisterSpecSet} of appropriate
+ * max size if there is not yet any set associated with the block.
+ *
+ * @param index {@code >= 0;} the block index
+ * @return {@code non-null;} the associated register set
+ */
+ public RegisterSpecSet mutableCopyOfStarts(int index) {
+ RegisterSpecSet result = getStarts0(index);
+
+ return (result != null) ?
+ result.mutableCopy() : new RegisterSpecSet(regCount);
+ }
+
+ /**
+ * Adds an assignment association for the given instruction and
+ * register spec. This throws an exception if the instruction
+ * doesn't actually perform a named variable assignment.
+ *
+ * <b>Note:</b> Although the instruction contains its own spec for
+ * the result, it still needs to be passed in explicitly to this
+ * method, since the spec that is stored here should always have a
+ * simple type and the one in the instruction can be an arbitrary
+ * {@link com.android.jack.dx.rop.type.TypeBearer} (such as a constant value).
+ *
+ * @param insn {@code non-null;} the instruction in question
+ * @param spec {@code non-null;} the associated register spec
+ */
+ public void addAssignment(SsaInsn insn, RegisterSpec spec) {
+ throwIfImmutable();
+
+ if (insn == null) {
+ throw new NullPointerException("insn == null");
+ }
+
+ if (spec == null) {
+ throw new NullPointerException("spec == null");
+ }
+
+ insnAssignments.put(insn, spec);
+ }
+
+ /**
+ * Gets the named register being assigned by the given instruction, if
+ * previously stored in this instance.
+ *
+ * @param insn {@code non-null;} instruction in question
+ * @return {@code null-ok;} the named register being assigned, if any
+ */
+ public RegisterSpec getAssignment(SsaInsn insn) {
+ return insnAssignments.get(insn);
+ }
+
+ /**
+ * Gets the number of assignments recorded by this instance.
+ *
+ * @return {@code >= 0;} the number of assignments
+ */
+ public int getAssignmentCount() {
+ return insnAssignments.size();
+ }
+
+ public void debugDump() {
+ for (int index = 0 ; index < blockStarts.length; index++) {
+ if (blockStarts[index] == null) {
+ continue;
+ }
+
+ if (blockStarts[index] == emptySet) {
+ System.out.printf("%04x: empty set\n", index);
+ } else {
+ System.out.printf("%04x: %s\n", index, blockStarts[index]);
+ }
+ }
+ }
+
+ /**
+ * Helper method, to get the starts for a index, throwing the
+ * right exception for range problems.
+ *
+ * @param index {@code >= 0;} the block index
+ * @return {@code null-ok;} associated register set or {@code null} if there
+ * is none
+ */
+ private RegisterSpecSet getStarts0(int index) {
+ try {
+ return blockStarts[index];
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ // Translate the exception.
+ throw new IllegalArgumentException("bogus index");
+ }
+ }
+}
diff --git a/dx/src/com/android/jack/dx/ssa/MoveParamCombiner.java b/dx/src/com/android/jack/dx/ssa/MoveParamCombiner.java
new file mode 100644
index 00000000..dc05a6e4
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/MoveParamCombiner.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa;
+
+import com.android.jack.dx.rop.code.CstInsn;
+import com.android.jack.dx.rop.code.LocalItem;
+import com.android.jack.dx.rop.code.RegOps;
+import com.android.jack.dx.rop.code.RegisterSpec;
+import com.android.jack.dx.rop.cst.CstInteger;
+
+import java.util.HashSet;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Combine identical move-param insns, which may result from Ropper's
+ * handling of synchronized methods.
+ */
+public class MoveParamCombiner {
+
+ /** method to process */
+ private final SsaMethod ssaMeth;
+
+ /**
+ * Processes a method with this optimization step.
+ *
+ * @param ssaMethod method to process
+ */
+ public static void process(SsaMethod ssaMethod) {
+ new MoveParamCombiner(ssaMethod).run();
+ }
+
+ private MoveParamCombiner(SsaMethod ssaMeth) {
+ this.ssaMeth = ssaMeth;
+ }
+
+ /**
+ * Runs this optimization step.
+ */
+ private void run() {
+ // This will contain the definition specs for each parameter
+ final RegisterSpec[] paramSpecs
+ = new RegisterSpec[ssaMeth.getParamWidth()];
+
+ // Insns to delete when all done
+ final HashSet<SsaInsn> deletedInsns = new HashSet();
+
+ ssaMeth.forEachInsn(new SsaInsn.Visitor() {
+ public void visitMoveInsn (NormalSsaInsn insn) {
+ }
+ public void visitPhiInsn (PhiInsn phi) {
+ }
+ public void visitNonMoveInsn (NormalSsaInsn insn) {
+ if (insn.getOpcode().getOpcode() != RegOps.MOVE_PARAM) {
+ return;
+ }
+
+ int param = getParamIndex(insn);
+
+ if (paramSpecs[param] == null) {
+ paramSpecs[param] = insn.getResult();
+ } else {
+ final RegisterSpec specA = paramSpecs[param];
+ final RegisterSpec specB = insn.getResult();
+ LocalItem localA = specA.getLocalItem();
+ LocalItem localB = specB.getLocalItem();
+ LocalItem newLocal;
+
+ /*
+ * Is there local information to preserve?
+ */
+
+ if (localA == null) {
+ newLocal = localB;
+ } else if (localB == null) {
+ newLocal = localA;
+ } else if (localA.equals(localB)) {
+ newLocal = localA;
+ } else {
+ /*
+ * Oddly, these two identical move-params have distinct
+ * debug info. We'll just keep them distinct.
+ */
+ return;
+ }
+
+ ssaMeth.getDefinitionForRegister(specA.getReg())
+ .setResultLocal(newLocal);
+
+ /*
+ * Map all uses of specB to specA
+ */
+
+ RegisterMapper mapper = new RegisterMapper() {
+ /** @inheritDoc */
+ public int getNewRegisterCount() {
+ return ssaMeth.getRegCount();
+ }
+
+ /** @inheritDoc */
+ public RegisterSpec map(RegisterSpec registerSpec) {
+ if (registerSpec.getReg() == specB.getReg()) {
+ return specA;
+ }
+
+ return registerSpec;
+ }
+ };
+
+ List<SsaInsn> uses
+ = ssaMeth.getUseListForRegister(specB.getReg());
+
+ // Use list is modified by mapSourceRegisters
+ for (int i = uses.size() - 1; i >= 0; i--) {
+ SsaInsn use = uses.get(i);
+ use.mapSourceRegisters(mapper);
+ }
+
+ deletedInsns.add(insn);
+ }
+
+ }
+ });
+
+ ssaMeth.deleteInsns(deletedInsns);
+ }
+
+ /**
+ * Returns the parameter index associated with a move-param insn. Does
+ * not verify that the insn is a move-param insn.
+ *
+ * @param insn {@code non-null;} a move-param insn
+ * @return {@code >=0;} parameter index
+ */
+ private int getParamIndex(NormalSsaInsn insn) {
+ CstInsn cstInsn = (CstInsn)(insn.getOriginalRopInsn());
+
+ int param = ((CstInteger)cstInsn.getConstant()).getValue();
+ return param;
+ }
+
+}
diff --git a/dx/src/com/android/jack/dx/ssa/NormalSsaInsn.java b/dx/src/com/android/jack/dx/ssa/NormalSsaInsn.java
new file mode 100644
index 00000000..c6695664
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/NormalSsaInsn.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa;
+
+import com.android.jack.dx.rop.code.*;
+
+/**
+ * A "normal" (non-phi) instruction in SSA form. Always wraps a rop insn.
+ */
+public final class NormalSsaInsn extends SsaInsn implements Cloneable {
+ /** {@code non-null;} rop insn that we're wrapping */
+ private Insn insn;
+
+ /**
+ * Creates an instance.
+ *
+ * @param insn Rop insn to wrap
+ * @param block block that contains this insn
+ */
+ NormalSsaInsn(final Insn insn, final SsaBasicBlock block) {
+ super(insn.getResult(), block);
+ this.insn = insn;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final void mapSourceRegisters(RegisterMapper mapper) {
+ RegisterSpecList oldSources = insn.getSources();
+ RegisterSpecList newSources = mapper.map(oldSources);
+
+ if (newSources != oldSources) {
+ insn = insn.withNewRegisters(getResult(), newSources);
+ getBlock().getParent().onSourcesChanged(this, oldSources);
+ }
+ }
+
+ /**
+ * Changes one of the insn's sources. New source should be of same type
+ * and category.
+ *
+ * @param index {@code >=0;} index of source to change
+ * @param newSpec spec for new source
+ */
+ public final void changeOneSource(int index, RegisterSpec newSpec) {
+ RegisterSpecList origSources = insn.getSources();
+ int sz = origSources.size();
+ RegisterSpecList newSources = new RegisterSpecList(sz);
+
+ for (int i = 0; i < sz; i++) {
+ newSources.set(i, i == index ? newSpec : origSources.get(i));
+ }
+
+ newSources.setImmutable();
+
+ RegisterSpec origSpec = origSources.get(index);
+ if (origSpec.getReg() != newSpec.getReg()) {
+ /*
+ * If the register remains unchanged, we're only changing
+ * the type or local var name so don't update use list
+ */
+ getBlock().getParent().onSourceChanged(this, origSpec, newSpec);
+ }
+
+ insn = insn.withNewRegisters(getResult(), newSources);
+ }
+
+ /**
+ * Changes the source list of the insn. New source list should be the
+ * same size and consist of sources of identical types.
+ *
+ * @param newSources non-null new sources list.
+ */
+ public final void setNewSources (RegisterSpecList newSources) {
+ RegisterSpecList origSources = insn.getSources();
+
+ if (origSources.size() != newSources.size()) {
+ throw new RuntimeException("Sources counts don't match");
+ }
+
+ insn = insn.withNewRegisters(getResult(), newSources);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public NormalSsaInsn clone() {
+ return (NormalSsaInsn) super.clone();
+ }
+
+ /**
+ * Like rop.Insn.getSources().
+ *
+ * @return {@code null-ok;} sources list
+ */
+ @Override
+ public RegisterSpecList getSources() {
+ return insn.getSources();
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ return toRopInsn().toHuman();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Insn toRopInsn() {
+ return insn.withNewRegisters(getResult(), insn.getSources());
+ }
+
+ /**
+ * @return the Rop opcode for this insn
+ */
+ @Override
+ public Rop getOpcode() {
+ return insn.getOpcode();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Insn getOriginalRopInsn() {
+ return insn;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RegisterSpec getLocalAssignment() {
+ RegisterSpec assignment;
+
+ if (insn.getOpcode().getOpcode() == RegOps.MARK_LOCAL) {
+ assignment = insn.getSources().get(0);
+ } else {
+ assignment = getResult();
+ }
+
+ if (assignment == null) {
+ return null;
+ }
+
+ LocalItem local = assignment.getLocalItem();
+
+ if (local == null) {
+ return null;
+ }
+
+ return assignment;
+ }
+
+ /**
+ * Upgrades this insn to a version that represents the constant source
+ * literally. If the upgrade is not possible, this does nothing.
+ *
+ * @see Insn#withSourceLiteral
+ */
+ public void upgradeToLiteral() {
+ RegisterSpecList oldSources = insn.getSources();
+
+ insn = insn.withSourceLiteral();
+ getBlock().getParent().onSourcesChanged(this, oldSources);
+ }
+
+ /**
+ * @return true if this is a move (but not a move-operand) instruction
+ */
+ @Override
+ public boolean isNormalMoveInsn() {
+ return insn.getOpcode().getOpcode() == RegOps.MOVE;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isMoveException() {
+ return insn.getOpcode().getOpcode() == RegOps.MOVE_EXCEPTION;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean canThrow() {
+ return insn.canThrow();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void accept(Visitor v) {
+ if (isNormalMoveInsn()) {
+ v.visitMoveInsn(this);
+ } else {
+ v.visitNonMoveInsn(this);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isPhiOrMove() {
+ return isNormalMoveInsn();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * TODO: Increase the scope of this.
+ */
+ @Override
+ public boolean hasSideEffect() {
+ Rop opcode = getOpcode();
+
+ if (opcode.getBranchingness() != Rop.BRANCH_NONE) {
+ return true;
+ }
+
+ boolean hasLocalSideEffect
+ = Optimizer.getPreserveLocals() && getLocalAssignment() != null;
+
+ switch (opcode.getOpcode()) {
+ case RegOps.MOVE_RESULT:
+ case RegOps.MOVE:
+ case RegOps.CONST:
+ return hasLocalSideEffect;
+ default:
+ return true;
+ }
+ }
+}
diff --git a/dx/src/com/android/jack/dx/ssa/Optimizer.java b/dx/src/com/android/jack/dx/ssa/Optimizer.java
new file mode 100644
index 00000000..799b7042
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/Optimizer.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa;
+
+import com.android.jack.dx.rop.code.RopMethod;
+import com.android.jack.dx.rop.code.TranslationAdvice;
+import com.android.jack.dx.ssa.back.LivenessAnalyzer;
+import com.android.jack.dx.ssa.back.SsaToRop;
+
+import java.util.EnumSet;
+
+/**
+ * Runs a method through the SSA form conversion, any optimization algorithms,
+ * and returns it to rop form.
+ */
+public class Optimizer {
+ private static boolean preserveLocals = true;
+
+ private static TranslationAdvice advice;
+
+ /** optional optimizer steps */
+ public enum OptionalStep {
+ MOVE_PARAM_COMBINER, SCCP, LITERAL_UPGRADE, CONST_COLLECTOR,
+ ESCAPE_ANALYSIS
+ }
+
+ /**
+ * @return true if local variable information should be preserved, even
+ * at code size/register size cost
+ */
+ public static boolean getPreserveLocals() {
+ return preserveLocals;
+ }
+
+ /**
+ * @return {@code non-null;} translation advice
+ */
+ public static TranslationAdvice getAdvice() {
+ return advice;
+ }
+
+ /**
+ * Runs optimization algorthims over this method, and returns a new
+ * instance of RopMethod with the changes.
+ *
+ * @param rmeth method to process
+ * @param paramWidth the total width, in register-units, of this method's
+ * parameters
+ * @param isStatic true if this method has no 'this' pointer argument.
+ * @param inPreserveLocals true if local variable info should be preserved,
+ * at the cost of some registers and insns
+ * @param inAdvice {@code non-null;} translation advice
+ * @return optimized method
+ */
+ public static RopMethod optimize(RopMethod rmeth, int paramWidth,
+ boolean isStatic, boolean inPreserveLocals,
+ TranslationAdvice inAdvice) {
+
+ return optimize(rmeth, paramWidth, isStatic, inPreserveLocals, inAdvice,
+ EnumSet.allOf(OptionalStep.class));
+ }
+
+ /**
+ * Runs optimization algorthims over this method, and returns a new
+ * instance of RopMethod with the changes.
+ *
+ * @param rmeth method to process
+ * @param paramWidth the total width, in register-units, of this method's
+ * parameters
+ * @param isStatic true if this method has no 'this' pointer argument.
+ * @param inPreserveLocals true if local variable info should be preserved,
+ * at the cost of some registers and insns
+ * @param inAdvice {@code non-null;} translation advice
+ * @param steps set of optional optimization steps to run
+ * @return optimized method
+ */
+ public static RopMethod optimize(RopMethod rmeth, int paramWidth,
+ boolean isStatic, boolean inPreserveLocals,
+ TranslationAdvice inAdvice, EnumSet<OptionalStep> steps) {
+ SsaMethod ssaMeth = null;
+
+ preserveLocals = inPreserveLocals;
+ advice = inAdvice;
+
+ ssaMeth = SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic);
+ runSsaFormSteps(ssaMeth, steps);
+
+ RopMethod resultMeth = SsaToRop.convertToRopMethod(ssaMeth, false);
+
+ if (resultMeth.getBlocks().getRegCount()
+ > advice.getMaxOptimalRegisterCount()) {
+ // Try to see if we can squeeze it under the register count bar
+ resultMeth = optimizeMinimizeRegisters(rmeth, paramWidth, isStatic,
+ steps);
+ }
+ return resultMeth;
+ }
+
+ /**
+ * Runs the optimizer with a strategy to minimize the number of rop-form
+ * registers used by the end result. Dex bytecode does not have instruction
+ * forms that take register numbers larger than 15 for all instructions.
+ * If we've produced a method that uses more than 16 registers, try again
+ * with a different strategy to see if we can get under the bar. The end
+ * result will be much more efficient.
+ *
+ * @param rmeth method to process
+ * @param paramWidth the total width, in register-units, of this method's
+ * parameters
+ * @param isStatic true if this method has no 'this' pointer argument.
+ * @param steps set of optional optimization steps to run
+ * @return optimized method
+ */
+ private static RopMethod optimizeMinimizeRegisters(RopMethod rmeth,
+ int paramWidth, boolean isStatic,
+ EnumSet<OptionalStep> steps) {
+ SsaMethod ssaMeth;
+ RopMethod resultMeth;
+
+ ssaMeth = SsaConverter.convertToSsaMethod(
+ rmeth, paramWidth, isStatic);
+
+ EnumSet<OptionalStep> newSteps = steps.clone();
+
+ /*
+ * CONST_COLLECTOR trades insns for registers, which is not an
+ * appropriate strategy here.
+ */
+ newSteps.remove(OptionalStep.CONST_COLLECTOR);
+
+ runSsaFormSteps(ssaMeth, newSteps);
+
+ resultMeth = SsaToRop.convertToRopMethod(ssaMeth, true);
+ return resultMeth;
+ }
+
+ private static void runSsaFormSteps(SsaMethod ssaMeth,
+ EnumSet<OptionalStep> steps) {
+ boolean needsDeadCodeRemover = true;
+
+ if (steps.contains(OptionalStep.MOVE_PARAM_COMBINER)) {
+ MoveParamCombiner.process(ssaMeth);
+ }
+
+ if (steps.contains(OptionalStep.SCCP)) {
+ SCCP.process(ssaMeth);
+ DeadCodeRemover.process(ssaMeth);
+ needsDeadCodeRemover = false;
+ }
+
+ if (steps.contains(OptionalStep.LITERAL_UPGRADE)) {
+ LiteralOpUpgrader.process(ssaMeth);
+ DeadCodeRemover.process(ssaMeth);
+ needsDeadCodeRemover = false;
+ }
+
+ /*
+ * ESCAPE_ANALYSIS impacts debuggability, so left off by default
+ */
+ steps.remove(OptionalStep.ESCAPE_ANALYSIS);
+ if (steps.contains(OptionalStep.ESCAPE_ANALYSIS)) {
+ EscapeAnalysis.process(ssaMeth);
+ DeadCodeRemover.process(ssaMeth);
+ needsDeadCodeRemover = false;
+ }
+
+ if (steps.contains(OptionalStep.CONST_COLLECTOR)) {
+ ConstCollector.process(ssaMeth);
+ DeadCodeRemover.process(ssaMeth);
+ needsDeadCodeRemover = false;
+ }
+
+ // dead code remover must be run before phi type resolver
+ if (needsDeadCodeRemover) {
+ DeadCodeRemover.process(ssaMeth);
+ }
+
+ PhiTypeResolver.process(ssaMeth);
+ }
+
+ public static SsaMethod debugEdgeSplit(RopMethod rmeth, int paramWidth,
+ boolean isStatic, boolean inPreserveLocals,
+ TranslationAdvice inAdvice) {
+
+ preserveLocals = inPreserveLocals;
+ advice = inAdvice;
+
+ return SsaConverter.testEdgeSplit(rmeth, paramWidth, isStatic);
+ }
+
+ public static SsaMethod debugPhiPlacement(RopMethod rmeth, int paramWidth,
+ boolean isStatic, boolean inPreserveLocals,
+ TranslationAdvice inAdvice) {
+
+ preserveLocals = inPreserveLocals;
+ advice = inAdvice;
+
+ return SsaConverter.testPhiPlacement(rmeth, paramWidth, isStatic);
+ }
+
+ public static SsaMethod debugRenaming(RopMethod rmeth, int paramWidth,
+ boolean isStatic, boolean inPreserveLocals,
+ TranslationAdvice inAdvice) {
+
+ preserveLocals = inPreserveLocals;
+ advice = inAdvice;
+
+ return SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic);
+ }
+
+ public static SsaMethod debugDeadCodeRemover(RopMethod rmeth,
+ int paramWidth, boolean isStatic, boolean inPreserveLocals,
+ TranslationAdvice inAdvice) {
+
+ SsaMethod ssaMeth;
+
+ preserveLocals = inPreserveLocals;
+ advice = inAdvice;
+
+ ssaMeth = SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic);
+ DeadCodeRemover.process(ssaMeth);
+
+ return ssaMeth;
+ }
+
+ public static SsaMethod debugNoRegisterAllocation(RopMethod rmeth,
+ int paramWidth, boolean isStatic, boolean inPreserveLocals,
+ TranslationAdvice inAdvice, EnumSet<OptionalStep> steps) {
+
+ SsaMethod ssaMeth;
+
+ preserveLocals = inPreserveLocals;
+ advice = inAdvice;
+
+ ssaMeth = SsaConverter.convertToSsaMethod(rmeth, paramWidth, isStatic);
+
+ runSsaFormSteps(ssaMeth, steps);
+
+ LivenessAnalyzer.constructInterferenceGraph(ssaMeth);
+
+ return ssaMeth;
+ }
+}
diff --git a/dx/src/com/android/jack/dx/ssa/PhiInsn.java b/dx/src/com/android/jack/dx/ssa/PhiInsn.java
new file mode 100644
index 00000000..9372305d
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/PhiInsn.java
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa;
+
+import com.android.jack.dx.rop.code.*;
+import com.android.jack.dx.rop.type.Type;
+import com.android.jack.dx.rop.type.TypeBearer;
+import com.android.jack.dx.util.Hex;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A Phi instruction (magical post-control-flow-merge) instruction
+ * in SSA form. Will be converted to moves in predecessor blocks before
+ * conversion back to ROP form.
+ */
+public final class PhiInsn extends SsaInsn {
+ /**
+ * result register. The original result register of the phi insn
+ * is needed during the renaming process after the new result
+ * register has already been chosen.
+ */
+ private final int ropResultReg;
+
+ /**
+ * {@code non-null;} operands of the instruction; built up by
+ * {@link #addPhiOperand}
+ */
+ private final ArrayList<Operand> operands = new ArrayList<Operand>();
+
+ /** {@code null-ok;} source registers; constructed lazily */
+ private RegisterSpecList sources;
+
+ /**
+ * Constructs a new phi insn with no operands.
+ *
+ * @param resultReg the result reg for this phi insn
+ * @param block block containing this insn.
+ */
+ public PhiInsn(RegisterSpec resultReg, SsaBasicBlock block) {
+ super(resultReg, block);
+ ropResultReg = resultReg.getReg();
+ }
+
+ /**
+ * Makes a phi insn with a void result type.
+ *
+ * @param resultReg the result register for this phi insn.
+ * @param block block containing this insn.
+ */
+ public PhiInsn(final int resultReg, final SsaBasicBlock block) {
+ /*
+ * The result type here is bogus: The type depends on the
+ * operand and will be derived later.
+ */
+ super(RegisterSpec.make(resultReg, Type.VOID), block);
+ ropResultReg = resultReg;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public PhiInsn clone() {
+ throw new UnsupportedOperationException("can't clone phi");
+ }
+
+ /**
+ * Updates the TypeBearers of all the sources (phi operands) to be
+ * the current TypeBearer of the register-defining instruction's result.
+ * This is used during phi-type resolution.<p>
+ *
+ * Note that local association of operands are preserved in this step.
+ *
+ * @param ssaMeth method that contains this insn
+ */
+ public void updateSourcesToDefinitions(SsaMethod ssaMeth) {
+ for (Operand o : operands) {
+ RegisterSpec def
+ = ssaMeth.getDefinitionForRegister(
+ o.regSpec.getReg()).getResult();
+
+ o.regSpec = o.regSpec.withType(def.getType());
+ }
+
+ sources = null;
+ }
+
+ /**
+ * Changes the result type. Used during phi type resolution
+ *
+ * @param type {@code non-null;} new TypeBearer
+ * @param local {@code null-ok;} new local info, if available
+ */
+ public void changeResultType(TypeBearer type, LocalItem local) {
+ setResult(RegisterSpec.makeLocalOptional(
+ getResult().getReg(), type, local));
+ }
+
+ /**
+ * Gets the original rop-form result reg. This is useful during renaming.
+ *
+ * @return the original rop-form result reg
+ */
+ public int getRopResultReg() {
+ return ropResultReg;
+ }
+
+ /**
+ * Adds an operand to this phi instruction.
+ *
+ * @param registerSpec register spec, including type and reg of operand
+ * @param predBlock predecessor block to be associated with this operand
+ */
+ public void addPhiOperand(RegisterSpec registerSpec,
+ SsaBasicBlock predBlock) {
+ operands.add(new Operand(registerSpec, predBlock.getIndex(),
+ predBlock.getRopLabel()));
+
+ // Un-cache sources, in case someone has already called getSources().
+ sources = null;
+ }
+
+ /**
+ * Removes all operand uses of a register from this phi instruction.
+ *
+ * @param registerSpec register spec, including type and reg of operand
+ */
+ public void removePhiRegister(RegisterSpec registerSpec) {
+ ArrayList<Operand> operandsToRemove = new ArrayList<Operand>();
+ for (Operand o : operands) {
+ if (o.regSpec.getReg() == registerSpec.getReg()) {
+ operandsToRemove.add(o);
+ }
+ }
+
+ operands.removeAll(operandsToRemove);
+
+ // Un-cache sources, in case someone has already called getSources().
+ sources = null;
+ }
+
+ /**
+ * Gets the index of the pred block associated with the RegisterSpec
+ * at the particular getSources() index.
+ *
+ * @param sourcesIndex index of source in getSources()
+ * @return block index
+ */
+ public int predBlockIndexForSourcesIndex(int sourcesIndex) {
+ return operands.get(sourcesIndex).blockIndex;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Always returns null for {@code PhiInsn}s.
+ */
+ @Override
+ public Rop getOpcode() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Always returns null for {@code PhiInsn}s.
+ */
+ @Override
+ public Insn getOriginalRopInsn() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Always returns false for {@code PhiInsn}s.
+ */
+ @Override
+ public boolean canThrow() {
+ return false;
+ }
+
+ /**
+ * Gets sources. Constructed lazily from phi operand data structures and
+ * then cached.
+ *
+ * @return {@code non-null;} sources list
+ */
+ @Override
+ public RegisterSpecList getSources() {
+ if (sources != null) {
+ return sources;
+ }
+
+ if (operands.size() == 0) {
+ // How'd this happen? A phi insn with no operand?
+ return RegisterSpecList.EMPTY;
+ }
+
+ int szSources = operands.size();
+ sources = new RegisterSpecList(szSources);
+
+ for (int i = 0; i < szSources; i++) {
+ Operand o = operands.get(i);
+
+ sources.set(i, o.regSpec);
+ }
+
+ sources.setImmutable();
+ return sources;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isRegASource(int reg) {
+ /*
+ * Avoid creating a sources list in case it has not already been
+ * created.
+ */
+
+ for (Operand o : operands) {
+ if (o.regSpec.getReg() == reg) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @return true if all operands use the same register
+ */
+ public boolean areAllOperandsEqual() {
+ if (operands.size() == 0 ) {
+ // This should never happen.
+ return true;
+ }
+
+ int firstReg = operands.get(0).regSpec.getReg();
+ for (Operand o : operands) {
+ if (firstReg != o.regSpec.getReg()) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public final void mapSourceRegisters(RegisterMapper mapper) {
+ for (Operand o : operands) {
+ RegisterSpec old = o.regSpec;
+ o.regSpec = mapper.map(old);
+ if (old != o.regSpec) {
+ getBlock().getParent().onSourceChanged(this, old, o.regSpec);
+ }
+ }
+ sources = null;
+ }
+
+ /**
+ * Always throws an exeption, since a phi insn may not be
+ * converted back to rop form.
+ *
+ * @return always throws exception
+ */
+ @Override
+ public Insn toRopInsn() {
+ throw new IllegalArgumentException(
+ "Cannot convert phi insns to rop form");
+ }
+
+ /**
+ * Returns the list of predecessor blocks associated with all operands
+ * that have {@code reg} as an operand register.
+ *
+ * @param reg register to look up
+ * @param ssaMeth method we're operating on
+ * @return list of predecessor blocks, empty if none
+ */
+ public List<SsaBasicBlock> predBlocksForReg(int reg, SsaMethod ssaMeth) {
+ ArrayList<SsaBasicBlock> ret = new ArrayList<SsaBasicBlock>();
+
+ for (Operand o : operands) {
+ if (o.regSpec.getReg() == reg) {
+ ret.add(ssaMeth.getBlocks().get(o.blockIndex));
+ }
+ }
+
+ return ret;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean isPhiOrMove() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean hasSideEffect() {
+ return Optimizer.getPreserveLocals() && getLocalAssignment() != null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void accept(SsaInsn.Visitor v) {
+ v.visitPhiInsn(this);
+ }
+
+ /** {@inheritDoc} */
+ public String toHuman() {
+ return toHumanWithInline(null);
+ }
+
+ /**
+ * Returns human-readable string for listing dumps. This method
+ * allows sub-classes to specify extra text.
+ *
+ * @param extra {@code null-ok;} the argument to print after the opcode
+ * @return human-readable string for listing dumps
+ */
+ protected final String toHumanWithInline(String extra) {
+ StringBuffer sb = new StringBuffer(80);
+
+ sb.append(SourcePosition.NO_INFO);
+ sb.append(": phi");
+
+ if (extra != null) {
+ sb.append("(");
+ sb.append(extra);
+ sb.append(")");
+ }
+
+ RegisterSpec result = getResult();
+
+ if (result == null) {
+ sb.append(" .");
+ } else {
+ sb.append(" ");
+ sb.append(result.toHuman());
+ }
+
+ sb.append(" <-");
+
+ int sz = getSources().size();
+ if (sz == 0) {
+ sb.append(" .");
+ } else {
+ for (int i = 0; i < sz; i++) {
+ sb.append(" ");
+ sb.append(sources.get(i).toHuman()
+ + "[b="
+ + Hex.u2(operands.get(i).ropLabel) + "]");
+ }
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * A single phi operand, consiting of source register and block index
+ * for move.
+ */
+ private static class Operand {
+ public RegisterSpec regSpec;
+ public final int blockIndex;
+ public final int ropLabel; // only used for debugging
+
+ public Operand(RegisterSpec regSpec, int blockIndex, int ropLabel) {
+ this.regSpec = regSpec;
+ this.blockIndex = blockIndex;
+ this.ropLabel = ropLabel;
+ }
+ }
+
+ /**
+ * Visitor interface for instances of this (outer) class.
+ */
+ public static interface Visitor {
+ public void visitPhiInsn(PhiInsn insn);
+ }
+}
diff --git a/dx/src/com/android/jack/dx/ssa/PhiTypeResolver.java b/dx/src/com/android/jack/dx/ssa/PhiTypeResolver.java
new file mode 100644
index 00000000..abc12534
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/PhiTypeResolver.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa;
+
+import com.android.jack.dx.rop.code.LocalItem;
+import com.android.jack.dx.rop.code.RegisterSpec;
+import com.android.jack.dx.rop.code.RegisterSpecList;
+import com.android.jack.dx.rop.type.Type;
+import com.android.jack.dx.rop.type.TypeBearer;
+
+import java.util.BitSet;
+import java.util.List;
+
+/**
+ * Resolves the result types of phi instructions. When phi instructions
+ * are inserted, their result types are set to BT_VOID (which is a nonsensical
+ * type for a register) but must be resolve to a real type before converting
+ * out of SSA form.<p>
+ *
+ * The resolve is done as an iterative merge of each phi's operand types.
+ * Phi operands may be themselves be the result of unresolved phis,
+ * and the algorithm tries to find the most-fit type (for example, if every
+ * operand is the same constant value or the same local variable info, we want
+ * that to be reflected).<p>
+ *
+ * This algorithm assumes a dead-code remover has already removed all
+ * circular-only phis that may have been inserted.
+ */
+public class PhiTypeResolver {
+
+ SsaMethod ssaMeth;
+ /** indexed by register; all registers still defined by unresolved phis */
+ private final BitSet worklist;
+
+ /**
+ * Resolves all phi types in the method
+ * @param ssaMeth method to process
+ */
+ public static void process (SsaMethod ssaMeth) {
+ new PhiTypeResolver(ssaMeth).run();
+ }
+
+ private PhiTypeResolver(SsaMethod ssaMeth) {
+ this.ssaMeth = ssaMeth;
+ worklist = new BitSet(ssaMeth.getRegCount());
+ }
+
+ /**
+ * Runs the phi-type resolver.
+ */
+ private void run() {
+
+ int regCount = ssaMeth.getRegCount();
+
+ for (int reg = 0; reg < regCount; reg++) {
+ SsaInsn definsn = ssaMeth.getDefinitionForRegister(reg);
+
+ if (definsn != null
+ && (definsn.getResult().getBasicType() == Type.BT_VOID)) {
+ worklist.set(reg);
+ }
+ }
+
+ int reg;
+ while ( 0 <= (reg = worklist.nextSetBit(0))) {
+ worklist.clear(reg);
+
+ /*
+ * definitions on the worklist have a type of BT_VOID, which
+ * must have originated from a PhiInsn.
+ */
+ PhiInsn definsn = (PhiInsn)ssaMeth.getDefinitionForRegister(reg);
+
+ if (resolveResultType(definsn)) {
+ /*
+ * If the result type has changed, re-resolve all phis
+ * that use this.
+ */
+
+ List<SsaInsn> useList = ssaMeth.getUseListForRegister(reg);
+
+ int sz = useList.size();
+ for (int i = 0; i < sz; i++ ) {
+ SsaInsn useInsn = useList.get(i);
+ RegisterSpec resultReg = useInsn.getResult();
+ if (resultReg != null && useInsn instanceof PhiInsn) {
+ worklist.set(resultReg.getReg());
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns true if a and b are equal, whether
+ * or not either of them are null.
+ * @param a
+ * @param b
+ * @return true if equal
+ */
+ private static boolean equalsHandlesNulls(LocalItem a, LocalItem b) {
+ return (a == b) || ((a != null) && a.equals(b));
+ }
+
+ /**
+ * Resolves the result of a phi insn based on its operands. The "void"
+ * type, which is a nonsensical type for a register, is used for
+ * registers defined by as-of-yet-unresolved phi operations.
+ *
+ * @return true if the result type changed, false if no change
+ */
+ boolean resolveResultType(PhiInsn insn) {
+ insn.updateSourcesToDefinitions(ssaMeth);
+
+ RegisterSpecList sources = insn.getSources();
+
+ // Start by finding the first non-void operand
+ RegisterSpec first = null;
+ int firstIndex = -1;
+
+ int szSources = sources.size();
+ for (int i = 0 ; i <szSources ; i++) {
+ RegisterSpec rs = sources.get(i);
+
+ if (rs.getBasicType() != Type.BT_VOID) {
+ first = rs;
+ firstIndex = i;
+ }
+ }
+
+ if (first == null) {
+ // All operands are void -- we're not ready to resolve yet
+ return false;
+ }
+
+ LocalItem firstLocal = first.getLocalItem();
+ TypeBearer mergedType = first.getType();
+ boolean sameLocals = true;
+ for (int i = 0 ; i < szSources ; i++) {
+ if (i == firstIndex) {
+ continue;
+ }
+
+ RegisterSpec rs = sources.get(i);
+
+ // Just skip void (unresolved phi results) for now
+ if (rs.getBasicType() == Type.BT_VOID){
+ continue;
+ }
+
+ sameLocals = sameLocals
+ && equalsHandlesNulls(firstLocal, rs.getLocalItem());
+
+ mergedType = mergeType(mergedType, rs.getType());
+ }
+
+ TypeBearer newResultType;
+
+ if (mergedType != null) {
+ newResultType = mergedType;
+ } else {
+ StringBuilder sb = new StringBuilder();
+
+ for (int i = 0; i < szSources; i++) {
+ sb.append(sources.get(i).toString());
+ sb.append(' ');
+ }
+
+ throw new RuntimeException ("Couldn't map types in phi insn:" + sb);
+ }
+
+ LocalItem newLocal = sameLocals ? firstLocal : null;
+
+ RegisterSpec result = insn.getResult();
+
+ if ((result.getTypeBearer() == newResultType)
+ && equalsHandlesNulls(newLocal, result.getLocalItem())) {
+ return false;
+ }
+
+ insn.changeResultType(newResultType, newLocal);
+
+ return true;
+ }
+
+ /**
+ * Merges two frame types.
+ *
+ * @param ft1 {@code non-null;} a frame type
+ * @param ft2 {@code non-null;} another frame type
+ * @return {@code non-null;} the result of merging the two types
+ */
+ private static TypeBearer mergeType(TypeBearer ft1, TypeBearer ft2) {
+ if ((ft1 == null) || ft1.equals(ft2)) {
+ return ft1;
+ } else if (ft2 == null) {
+ return null;
+ } else {
+ Type type1 = ft1.getType();
+ Type type2 = ft2.getType();
+
+ if (type1 == type2) {
+ return type1;
+ } else if (type1.isReference() && type2.isReference()) {
+ if (type1 == Type.KNOWN_NULL) {
+ /*
+ * A known-null merges with any other reference type to
+ * be that reference type.
+ */
+ return type2;
+ } else if (type2 == Type.KNOWN_NULL) {
+ /*
+ * The same as above, but this time it's type2 that's
+ * the known-null.
+ */
+ return type1;
+ } else if (type1.isArray() && type2.isArray()) {
+ TypeBearer componentUnion =
+ mergeType(type1.getComponentType(),
+ type2.getComponentType());
+ if (componentUnion == null) {
+ /*
+ * At least one of the types is a primitive type,
+ * so the merged result is just Object.
+ */
+ return Type.OBJECT;
+ }
+ return ((Type) componentUnion).getArrayType();
+ } else {
+ /*
+ * All other unequal reference types get merged to be
+ * Object in this phase. This is fine here, but it
+ * won't be the right thing to do in the verifier.
+ */
+ return Type.OBJECT;
+ }
+ } else if (type1.isIntlike() && type2.isIntlike()) {
+ /*
+ * Merging two non-identical int-like types results in
+ * the type int.
+ */
+ return Type.INT;
+ } else {
+ return null;
+ }
+ }
+ }
+
+}
diff --git a/dx/src/com/android/jack/dx/ssa/RegisterMapper.java b/dx/src/com/android/jack/dx/ssa/RegisterMapper.java
new file mode 100644
index 00000000..300d956b
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/RegisterMapper.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa;
+
+import com.android.jack.dx.rop.code.RegisterSpec;
+import com.android.jack.dx.rop.code.RegisterSpecList;
+import com.android.jack.dx.util.ToHuman;
+
+/**
+ * Represents a mapping between two register numbering schemes.
+ * Subclasses of this may be mutable, and as such the mapping provided
+ * is only valid for the lifetime of the method call in which
+ * instances of this class are passed.
+ */
+public abstract class RegisterMapper {
+ /**
+ * Gets the count of registers (really, the total register width, since
+ * category width is counted) in the new namespace.
+ * @return >= 0 width of new namespace.
+ */
+ public abstract int getNewRegisterCount();
+
+ /**
+ * @param registerSpec old register
+ * @return register in new space
+ */
+ public abstract RegisterSpec map(RegisterSpec registerSpec);
+
+ /**
+ *
+ * @param sources old register list
+ * @return new mapped register list, or old if nothing has changed.
+ */
+ public final RegisterSpecList map(RegisterSpecList sources) {
+ int sz = sources.size();
+ RegisterSpecList newSources = new RegisterSpecList(sz);
+
+ for (int i = 0; i < sz; i++) {
+ newSources.set(i, map(sources.get(i)));
+ }
+
+ newSources.setImmutable();
+
+ // Return the old sources if nothing has changed.
+ return newSources.equals(sources) ? sources : newSources;
+ }
+}
diff --git a/dx/src/com/android/jack/dx/ssa/SCCP.java b/dx/src/com/android/jack/dx/ssa/SCCP.java
new file mode 100644
index 00000000..95753535
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/SCCP.java
@@ -0,0 +1,697 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa;
+
+import com.android.jack.dx.rop.code.CstInsn;
+import com.android.jack.dx.rop.code.Insn;
+import com.android.jack.dx.rop.code.PlainInsn;
+import com.android.jack.dx.rop.code.RegOps;
+import com.android.jack.dx.rop.code.RegisterSpec;
+import com.android.jack.dx.rop.code.RegisterSpecList;
+import com.android.jack.dx.rop.code.Rop;
+import com.android.jack.dx.rop.code.Rops;
+import com.android.jack.dx.rop.cst.Constant;
+import com.android.jack.dx.rop.cst.CstBoolean;
+import com.android.jack.dx.rop.cst.CstInteger;
+import com.android.jack.dx.rop.cst.TypedConstant;
+import com.android.jack.dx.rop.type.Type;
+import com.android.jack.dx.rop.type.TypeBearer;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+
+/**
+ * A small variant of Wegman and Zadeck's Sparse Conditional Constant
+ * Propagation algorithm.
+ */
+public class SCCP {
+ /** Lattice values */
+ private static final int TOP = 0;
+ private static final int CONSTANT = 1;
+ private static final int VARYING = 2;
+ /** method we're processing */
+ private SsaMethod ssaMeth;
+ /** ssaMeth.getRegCount() */
+ private int regCount;
+ /** Lattice values for each SSA register */
+ private int[] latticeValues;
+ /** For those registers that are constant, this is the constant value */
+ private Constant[] latticeConstants;
+ /** Worklist of basic blocks to be processed */
+ private ArrayList<SsaBasicBlock> cfgWorklist;
+ /** Worklist of executed basic blocks with phis to be processed */
+ private ArrayList<SsaBasicBlock> cfgPhiWorklist;
+ /** Bitset containing bits for each block that has been found executable */
+ private BitSet executableBlocks;
+ /** Worklist for SSA edges. This is a list of registers to process */
+ private ArrayList<SsaInsn> ssaWorklist;
+ /**
+ * Worklist for SSA edges that represent varying values. It makes the
+ * algorithm much faster if you move all values to VARYING as fast as
+ * possible.
+ */
+ private ArrayList<SsaInsn> varyingWorklist;
+ /** Worklist of potential branches to convert to gotos */
+ private ArrayList<SsaInsn> branchWorklist;
+
+ private SCCP(SsaMethod ssaMeth) {
+ this.ssaMeth = ssaMeth;
+ this.regCount = ssaMeth.getRegCount();
+ this.latticeValues = new int[this.regCount];
+ this.latticeConstants = new Constant[this.regCount];
+ this.cfgWorklist = new ArrayList<SsaBasicBlock>();
+ this.cfgPhiWorklist = new ArrayList<SsaBasicBlock>();
+ this.executableBlocks = new BitSet(ssaMeth.getBlocks().size());
+ this.ssaWorklist = new ArrayList<SsaInsn>();
+ this.varyingWorklist = new ArrayList<SsaInsn>();
+ this.branchWorklist = new ArrayList<SsaInsn>();
+ for (int i = 0; i < this.regCount; i++) {
+ latticeValues[i] = TOP;
+ latticeConstants[i] = null;
+ }
+ }
+
+ /**
+ * Performs sparse conditional constant propagation on a method.
+ * @param ssaMethod Method to process
+ */
+ public static void process (SsaMethod ssaMethod) {
+ new SCCP(ssaMethod).run();
+ }
+
+ /**
+ * Adds a SSA basic block to the CFG worklist if it's unexecuted, or
+ * to the CFG phi worklist if it's already executed.
+ * @param ssaBlock Block to add
+ */
+ private void addBlockToWorklist(SsaBasicBlock ssaBlock) {
+ if (!executableBlocks.get(ssaBlock.getIndex())) {
+ cfgWorklist.add(ssaBlock);
+ executableBlocks.set(ssaBlock.getIndex());
+ } else {
+ cfgPhiWorklist.add(ssaBlock);
+ }
+ }
+
+ /**
+ * Adds an SSA register's uses to the SSA worklist.
+ * @param reg SSA register
+ * @param latticeValue new lattice value for @param reg.
+ */
+ private void addUsersToWorklist(int reg, int latticeValue) {
+ if (latticeValue == VARYING) {
+ for (SsaInsn insn : ssaMeth.getUseListForRegister(reg)) {
+ varyingWorklist.add(insn);
+ }
+ } else {
+ for (SsaInsn insn : ssaMeth.getUseListForRegister(reg)) {
+ ssaWorklist.add(insn);
+ }
+ }
+ }
+
+ /**
+ * Sets a lattice value for a register to value.
+ * @param reg SSA register
+ * @param value Lattice value
+ * @param cst Constant value (may be null)
+ * @return true if the lattice value changed.
+ */
+ private boolean setLatticeValueTo(int reg, int value, Constant cst) {
+ if (value != CONSTANT) {
+ if (latticeValues[reg] != value) {
+ latticeValues[reg] = value;
+ return true;
+ }
+ return false;
+ } else {
+ if (latticeValues[reg] != value
+ || !latticeConstants[reg].equals(cst)) {
+ latticeValues[reg] = value;
+ latticeConstants[reg] = cst;
+ return true;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Simulates a PHI node and set the lattice for the result
+ * to the appropriate value.
+ * Meet values:
+ * TOP x anything = TOP
+ * VARYING x anything = VARYING
+ * CONSTANT x CONSTANT = CONSTANT if equal constants, VARYING otherwise
+ * @param insn PHI to simulate.
+ */
+ private void simulatePhi(PhiInsn insn) {
+ int phiResultReg = insn.getResult().getReg();
+
+ if (latticeValues[phiResultReg] == VARYING) {
+ return;
+ }
+
+ RegisterSpecList sources = insn.getSources();
+ int phiResultValue = TOP;
+ Constant phiConstant = null;
+ int sourceSize = sources.size();
+
+ for (int i = 0; i < sourceSize; i++) {
+ int predBlockIndex = insn.predBlockIndexForSourcesIndex(i);
+ int sourceReg = sources.get(i).getReg();
+ int sourceRegValue = latticeValues[sourceReg];
+
+ if (!executableBlocks.get(predBlockIndex)) {
+ continue;
+ }
+
+ if (sourceRegValue == CONSTANT) {
+ if (phiConstant == null) {
+ phiConstant = latticeConstants[sourceReg];
+ phiResultValue = CONSTANT;
+ } else if (!latticeConstants[sourceReg].equals(phiConstant)){
+ phiResultValue = VARYING;
+ break;
+ }
+ } else {
+ phiResultValue = sourceRegValue;
+ break;
+ }
+ }
+ if (setLatticeValueTo(phiResultReg, phiResultValue, phiConstant)) {
+ addUsersToWorklist(phiResultReg, phiResultValue);
+ }
+ }
+
+ /**
+ * Simulate a block and note the results in the lattice.
+ * @param block Block to visit
+ */
+ private void simulateBlock(SsaBasicBlock block) {
+ for (SsaInsn insn : block.getInsns()) {
+ if (insn instanceof PhiInsn) {
+ simulatePhi((PhiInsn) insn);
+ } else {
+ simulateStmt(insn);
+ }
+ }
+ }
+
+ /**
+ * Simulate the phis in a block and note the results in the lattice.
+ * @param block Block to visit
+ */
+ private void simulatePhiBlock(SsaBasicBlock block) {
+ for (SsaInsn insn : block.getInsns()) {
+ if (insn instanceof PhiInsn) {
+ simulatePhi((PhiInsn) insn);
+ } else {
+ return;
+ }
+ }
+ }
+
+ private static String latticeValName(int latticeVal) {
+ switch (latticeVal) {
+ case TOP: return "TOP";
+ case CONSTANT: return "CONSTANT";
+ case VARYING: return "VARYING";
+ default: return "UNKNOWN";
+ }
+ }
+
+ /**
+ * Simulates branch insns, if possible. Adds reachable successor blocks
+ * to the CFG worklists.
+ * @param insn branch to simulate
+ */
+ private void simulateBranch(SsaInsn insn) {
+ Rop opcode = insn.getOpcode();
+ RegisterSpecList sources = insn.getSources();
+
+ boolean constantBranch = false;
+ boolean constantSuccessor = false;
+
+ // Check if the insn is a branch with a constant condition
+ if (opcode.getBranchingness() == Rop.BRANCH_IF) {
+ Constant cA = null;
+ Constant cB = null;
+
+ RegisterSpec specA = sources.get(0);
+ int regA = specA.getReg();
+ if (!ssaMeth.isRegALocal(specA) &&
+ latticeValues[regA] == CONSTANT) {
+ cA = latticeConstants[regA];
+ }
+
+ if (sources.size() == 2) {
+ RegisterSpec specB = sources.get(1);
+ int regB = specB.getReg();
+ if (!ssaMeth.isRegALocal(specB) &&
+ latticeValues[regB] == CONSTANT) {
+ cB = latticeConstants[regB];
+ }
+ }
+
+ // Calculate the result of the condition
+ if (cA != null && sources.size() == 1) {
+ switch (((TypedConstant) cA).getBasicType()) {
+ case Type.BT_BOOLEAN: {
+ constantBranch = true;
+ boolean vA = ((CstBoolean) cA).getValue();
+ switch (opcode.getOpcode()) {
+ case RegOps.IF_EQ:
+ constantSuccessor = !vA;
+ break;
+ case RegOps.IF_NE:
+ constantSuccessor = vA;
+ break;
+ default:
+ throw new RuntimeException("Unexpected op");
+ }
+ break;
+ }
+ case Type.BT_INT:
+ constantBranch = true;
+ int vA = ((CstInteger) cA).getValue();
+ switch (opcode.getOpcode()) {
+ case RegOps.IF_EQ:
+ constantSuccessor = (vA == 0);
+ break;
+ case RegOps.IF_NE:
+ constantSuccessor = (vA != 0);
+ break;
+ case RegOps.IF_LT:
+ constantSuccessor = (vA < 0);
+ break;
+ case RegOps.IF_GE:
+ constantSuccessor = (vA >= 0);
+ break;
+ case RegOps.IF_LE:
+ constantSuccessor = (vA <= 0);
+ break;
+ case RegOps.IF_GT:
+ constantSuccessor = (vA > 0);
+ break;
+ default:
+ throw new RuntimeException("Unexpected op");
+ }
+ break;
+ default:
+ // not yet supported
+ }
+ } else if (cA != null && cB != null) {
+ switch (((TypedConstant) cA).getBasicType()) {
+ case Type.BT_INT:
+ constantBranch = true;
+ int vA = ((CstInteger) cA).getValue();
+ int vB = ((CstInteger) cB).getValue();
+ switch (opcode.getOpcode()) {
+ case RegOps.IF_EQ:
+ constantSuccessor = (vA == vB);
+ break;
+ case RegOps.IF_NE:
+ constantSuccessor = (vA != vB);
+ break;
+ case RegOps.IF_LT:
+ constantSuccessor = (vA < vB);
+ break;
+ case RegOps.IF_GE:
+ constantSuccessor = (vA >= vB);
+ break;
+ case RegOps.IF_LE:
+ constantSuccessor = (vA <= vB);
+ break;
+ case RegOps.IF_GT:
+ constantSuccessor = (vA > vB);
+ break;
+ default:
+ throw new RuntimeException("Unexpected op");
+ }
+ break;
+ default:
+ // not yet supported
+ }
+ }
+ }
+
+ /*
+ * If condition is constant, add only the target block to the
+ * worklist. Otherwise, add all successors to the worklist.
+ */
+ SsaBasicBlock block = insn.getBlock();
+
+ if (constantBranch) {
+ int successorBlock;
+ if (constantSuccessor) {
+ successorBlock = block.getSuccessorList().get(1);
+ } else {
+ successorBlock = block.getSuccessorList().get(0);
+ }
+ addBlockToWorklist(ssaMeth.getBlocks().get(successorBlock));
+ branchWorklist.add(insn);
+ } else {
+ for (int i = 0; i < block.getSuccessorList().size(); i++) {
+ int successorBlock = block.getSuccessorList().get(i);
+ addBlockToWorklist(ssaMeth.getBlocks().get(successorBlock));
+ }
+ }
+ }
+
+ /**
+ * Simulates math insns, if possible.
+ *
+ * @param insn non-null insn to simulate
+ * @param resultType basic type of the result
+ * @return constant result or null if not simulatable.
+ */
+ private Constant simulateMath(SsaInsn insn, int resultType) {
+ Insn ropInsn = insn.getOriginalRopInsn();
+ int opcode = insn.getOpcode().getOpcode();
+ RegisterSpecList sources = insn.getSources();
+ int regA = sources.get(0).getReg();
+ Constant cA;
+ Constant cB;
+
+ if (latticeValues[regA] != CONSTANT) {
+ cA = null;
+ } else {
+ cA = latticeConstants[regA];
+ }
+
+ if (sources.size() == 1) {
+ CstInsn cstInsn = (CstInsn) ropInsn;
+ cB = cstInsn.getConstant();
+ } else { /* sources.size() == 2 */
+ int regB = sources.get(1).getReg();
+ if (latticeValues[regB] != CONSTANT) {
+ cB = null;
+ } else {
+ cB = latticeConstants[regB];
+ }
+ }
+
+ if (cA == null || cB == null) {
+ //TODO handle a constant of 0 with MUL or AND
+ return null;
+ }
+
+ switch (resultType) {
+ case Type.BT_INT:
+ int vR;
+ boolean skip=false;
+
+ int vA = ((CstInteger) cA).getValue();
+ int vB = ((CstInteger) cB).getValue();
+
+ switch (opcode) {
+ case RegOps.ADD:
+ vR = vA + vB;
+ break;
+ case RegOps.SUB:
+ // 1 source for reverse sub, 2 sources for regular sub
+ if (sources.size() == 1) {
+ vR = vB - vA;
+ } else {
+ vR = vA - vB;
+ }
+ break;
+ case RegOps.MUL:
+ vR = vA * vB;
+ break;
+ case RegOps.DIV:
+ if (vB == 0) {
+ skip = true;
+ vR = 0; // just to hide a warning
+ } else {
+ vR = vA / vB;
+ }
+ break;
+ case RegOps.AND:
+ vR = vA & vB;
+ break;
+ case RegOps.OR:
+ vR = vA | vB;
+ break;
+ case RegOps.XOR:
+ vR = vA ^ vB;
+ break;
+ case RegOps.SHL:
+ vR = vA << vB;
+ break;
+ case RegOps.SHR:
+ vR = vA >> vB;
+ break;
+ case RegOps.USHR:
+ vR = vA >>> vB;
+ break;
+ case RegOps.REM:
+ if (vB == 0) {
+ skip = true;
+ vR = 0; // just to hide a warning
+ } else {
+ vR = vA % vB;
+ }
+ break;
+ default:
+ throw new RuntimeException("Unexpected op");
+ }
+
+ return skip ? null : CstInteger.make(vR);
+
+ default:
+ // not yet supported
+ return null;
+ }
+ }
+
+ /**
+ * Simulates a statement and set the result lattice value.
+ * @param insn instruction to simulate
+ */
+ private void simulateStmt(SsaInsn insn) {
+ Insn ropInsn = insn.getOriginalRopInsn();
+ if (ropInsn.getOpcode().getBranchingness() != Rop.BRANCH_NONE
+ || ropInsn.getOpcode().isCallLike()) {
+ simulateBranch(insn);
+ }
+
+ int opcode = insn.getOpcode().getOpcode();
+ RegisterSpec result = insn.getResult();
+
+ if (result == null) {
+ // Find move-result-pseudo result for int div and int rem
+ if (opcode == RegOps.DIV || opcode == RegOps.REM) {
+ SsaBasicBlock succ = insn.getBlock().getPrimarySuccessor();
+ result = succ.getInsns().get(0).getResult();
+ } else {
+ return;
+ }
+ }
+
+ int resultReg = result.getReg();
+ int resultValue = VARYING;
+ Constant resultConstant = null;
+
+ switch (opcode) {
+ case RegOps.CONST: {
+ CstInsn cstInsn = (CstInsn)ropInsn;
+ resultValue = CONSTANT;
+ resultConstant = cstInsn.getConstant();
+ break;
+ }
+ case RegOps.MOVE: {
+ if (insn.getSources().size() == 1) {
+ int sourceReg = insn.getSources().get(0).getReg();
+ resultValue = latticeValues[sourceReg];
+ resultConstant = latticeConstants[sourceReg];
+ }
+ break;
+ }
+ case RegOps.ADD:
+ case RegOps.SUB:
+ case RegOps.MUL:
+ case RegOps.DIV:
+ case RegOps.AND:
+ case RegOps.OR:
+ case RegOps.XOR:
+ case RegOps.SHL:
+ case RegOps.SHR:
+ case RegOps.USHR:
+ case RegOps.REM: {
+ resultConstant = simulateMath(insn, result.getBasicType());
+ if (resultConstant != null) {
+ resultValue = CONSTANT;
+ }
+ break;
+ }
+ case RegOps.MOVE_RESULT_PSEUDO: {
+ if (latticeValues[resultReg] == CONSTANT) {
+ resultValue = latticeValues[resultReg];
+ resultConstant = latticeConstants[resultReg];
+ }
+ break;
+ }
+ // TODO: Handle non-int arithmetic.
+ // TODO: Eliminate check casts that we can prove the type of.
+ default: {}
+ }
+ if (setLatticeValueTo(resultReg, resultValue, resultConstant)) {
+ addUsersToWorklist(resultReg, resultValue);
+ }
+ }
+
+ private void run() {
+ SsaBasicBlock firstBlock = ssaMeth.getEntryBlock();
+ addBlockToWorklist(firstBlock);
+
+ /* Empty all the worklists by propagating our values */
+ while (!cfgWorklist.isEmpty()
+ || !cfgPhiWorklist.isEmpty()
+ || !ssaWorklist.isEmpty()
+ || !varyingWorklist.isEmpty()) {
+ while (!cfgWorklist.isEmpty()) {
+ int listSize = cfgWorklist.size() - 1;
+ SsaBasicBlock block = cfgWorklist.remove(listSize);
+ simulateBlock(block);
+ }
+
+ while (!cfgPhiWorklist.isEmpty()) {
+ int listSize = cfgPhiWorklist.size() - 1;
+ SsaBasicBlock block = cfgPhiWorklist.remove(listSize);
+ simulatePhiBlock(block);
+ }
+
+ while (!varyingWorklist.isEmpty()) {
+ int listSize = varyingWorklist.size() - 1;
+ SsaInsn insn = varyingWorklist.remove(listSize);
+
+ if (!executableBlocks.get(insn.getBlock().getIndex())) {
+ continue;
+ }
+
+ if (insn instanceof PhiInsn) {
+ simulatePhi((PhiInsn)insn);
+ } else {
+ simulateStmt(insn);
+ }
+ }
+ while (!ssaWorklist.isEmpty()) {
+ int listSize = ssaWorklist.size() - 1;
+ SsaInsn insn = ssaWorklist.remove(listSize);
+
+ if (!executableBlocks.get(insn.getBlock().getIndex())) {
+ continue;
+ }
+
+ if (insn instanceof PhiInsn) {
+ simulatePhi((PhiInsn)insn);
+ } else {
+ simulateStmt(insn);
+ }
+ }
+ }
+
+ replaceConstants();
+ replaceBranches();
+ }
+
+ /**
+ * Replaces TypeBearers in source register specs with constant type
+ * bearers if possible. These are then referenced in later optimization
+ * steps.
+ */
+ private void replaceConstants() {
+ for (int reg = 0; reg < regCount; reg++) {
+ if (latticeValues[reg] != CONSTANT) {
+ continue;
+ }
+ if (!(latticeConstants[reg] instanceof TypedConstant)) {
+ // We can't do much with these
+ continue;
+ }
+
+ SsaInsn defn = ssaMeth.getDefinitionForRegister(reg);
+ TypeBearer typeBearer = defn.getResult().getTypeBearer();
+
+ if (typeBearer.isConstant()) {
+ /*
+ * The definition was a constant already.
+ * The uses should be as well.
+ */
+ continue;
+ }
+
+ // Update the destination RegisterSpec with the constant value
+ RegisterSpec dest = defn.getResult();
+ RegisterSpec newDest
+ = dest.withType((TypedConstant)latticeConstants[reg]);
+ defn.setResult(newDest);
+
+ /*
+ * Update the sources RegisterSpec's of all non-move uses.
+ * These will be used in later steps.
+ */
+ for (SsaInsn insn : ssaMeth.getUseListForRegister(reg)) {
+ if (insn.isPhiOrMove()) {
+ continue;
+ }
+
+ NormalSsaInsn nInsn = (NormalSsaInsn) insn;
+ RegisterSpecList sources = insn.getSources();
+
+ int index = sources.indexOfRegister(reg);
+
+ RegisterSpec spec = sources.get(index);
+ RegisterSpec newSpec
+ = spec.withType((TypedConstant)latticeConstants[reg]);
+
+ nInsn.changeOneSource(index, newSpec);
+ }
+ }
+ }
+
+ /**
+ * Replaces branches that have constant conditions with gotos
+ */
+ private void replaceBranches() {
+ for (SsaInsn insn : branchWorklist) {
+ // Find if a successor block is never executed
+ int oldSuccessor = -1;
+ SsaBasicBlock block = insn.getBlock();
+ int successorSize = block.getSuccessorList().size();
+ for (int i = 0; i < successorSize; i++) {
+ int successorBlock = block.getSuccessorList().get(i);
+ if (!executableBlocks.get(successorBlock)) {
+ oldSuccessor = successorBlock;
+ }
+ }
+
+ /*
+ * Prune branches that have already been handled and ones that no
+ * longer have constant conditions (no nonexecutable successors)
+ */
+ if (successorSize != 2 || oldSuccessor == -1) continue;
+
+ // Replace branch with goto
+ Insn originalRopInsn = insn.getOriginalRopInsn();
+ block.replaceLastInsn(new PlainInsn(Rops.GOTO,
+ originalRopInsn.getPosition(), null, RegisterSpecList.EMPTY));
+ block.removeSuccessor(oldSuccessor);
+ }
+ }
+}
diff --git a/dx/src/com/android/jack/dx/ssa/SetFactory.java b/dx/src/com/android/jack/dx/ssa/SetFactory.java
new file mode 100644
index 00000000..9c24b8ba
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/SetFactory.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa;
+
+import com.android.jack.dx.util.BitIntSet;
+import com.android.jack.dx.util.IntSet;
+import com.android.jack.dx.util.ListIntSet;
+
+
+/**
+ * Makes int sets for various parts of the optimizer.
+ */
+public final class SetFactory {
+
+ /**
+ * BitIntSet/ListIntSet threshold for dominance frontier sets. These
+ * sets are kept per basic block until phi placement and tend to be,
+ * like the CFG itself, very sparse at large sizes.
+ *
+ * A value of 3072 here is somewhere around 1.125mb of total bitset size.
+ */
+ private static final int DOMFRONT_SET_THRESHOLD_SIZE = 3072;
+
+ /**
+ * BitIntSet/ListIntSet threshold for interference graph sets. These
+ * sets are kept per register until register allocation is done.
+ *
+ * A value of 3072 here is somewhere around 1.125mb of total bitset size.
+ */
+ private static final int INTERFERENCE_SET_THRESHOLD_SIZE = 3072;
+
+ /**
+ * BitIntSet/ListIntSet threshold for the live in/out sets kept by
+ * {@link SsaBasicBlock}. These are sets of SSA registers kept per basic
+ * block during register allocation.
+ *
+ * The total size of a bitset for this would be the count of blocks
+ * times the size of registers. The threshold value here is merely
+ * the register count, which is typically on the order of the block
+ * count as well.
+ */
+ private static final int LIVENESS_SET_THRESHOLD_SIZE = 3072;
+
+
+ /**
+ * Make IntSet for the dominance-frontier sets.
+ *
+ * @param szBlocks {@code >=0;} count of basic blocks in method
+ * @return {@code non-null;} appropriate set
+ */
+ /*package*/ static IntSet makeDomFrontSet(int szBlocks) {
+ return szBlocks <= DOMFRONT_SET_THRESHOLD_SIZE
+ ? new BitIntSet(szBlocks)
+ : new ListIntSet();
+ }
+
+ /**
+ * Make IntSet for the interference graph sets. Public because
+ * InterferenceGraph is in another package.
+ *
+ * @param countRegs {@code >=0;} count of SSA registers used in method
+ * @return {@code non-null;} appropriate set
+ */
+ public static IntSet makeInterferenceSet(int countRegs) {
+ return countRegs <= INTERFERENCE_SET_THRESHOLD_SIZE
+ ? new BitIntSet(countRegs)
+ : new ListIntSet();
+ }
+
+ /**
+ * Make IntSet for register live in/out sets.
+ *
+ * @param countRegs {@code >=0;} count of SSA registers used in method
+ * @return {@code non-null;} appropriate set
+ */
+ /*package*/ static IntSet makeLivenessSet(int countRegs) {
+ return countRegs <= LIVENESS_SET_THRESHOLD_SIZE
+ ? new BitIntSet(countRegs)
+ : new ListIntSet();
+ }
+}
diff --git a/dx/src/com/android/jack/dx/ssa/SsaBasicBlock.java b/dx/src/com/android/jack/dx/ssa/SsaBasicBlock.java
new file mode 100644
index 00000000..4217184d
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/SsaBasicBlock.java
@@ -0,0 +1,1032 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa;
+
+import com.android.jack.dx.rop.code.BasicBlock;
+import com.android.jack.dx.rop.code.BasicBlockList;
+import com.android.jack.dx.rop.code.Insn;
+import com.android.jack.dx.rop.code.InsnList;
+import com.android.jack.dx.rop.code.PlainInsn;
+import com.android.jack.dx.rop.code.RegisterSpec;
+import com.android.jack.dx.rop.code.RegisterSpecList;
+import com.android.jack.dx.rop.code.Rop;
+import com.android.jack.dx.rop.code.RopMethod;
+import com.android.jack.dx.rop.code.Rops;
+import com.android.jack.dx.rop.code.SourcePosition;
+import com.android.jack.dx.util.Hex;
+import com.android.jack.dx.util.IntList;
+import com.android.jack.dx.util.IntSet;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * An SSA representation of a basic block.
+ */
+public final class SsaBasicBlock {
+ /**
+ * {@code non-null;} comparator for instances of this class that
+ * just compares block labels
+ */
+ public static final Comparator<SsaBasicBlock> LABEL_COMPARATOR =
+ new LabelComparator();
+
+ /** {@code non-null;} insn list associated with this instance */
+ private ArrayList<SsaInsn> insns;
+
+ /** {@code non-null;} predecessor set (by block list index) */
+ private BitSet predecessors;
+
+ /** {@code non-null;} successor set (by block list index) */
+ private BitSet successors;
+
+ /**
+ * {@code non-null;} ordered successor list
+ * (same block may be listed more than once)
+ */
+ private IntList successorList;
+
+ /**
+ * block list index of primary successor, or {@code -1} for no primary
+ * successor
+ */
+ private int primarySuccessor = -1;
+
+ /** label of block in rop form */
+ private int ropLabel;
+
+ /** {@code non-null;} method we belong to */
+ private SsaMethod parent;
+
+ /** our index into parent.getBlock() */
+ private int index;
+
+ /** list of dom children */
+ private final ArrayList<SsaBasicBlock> domChildren;
+
+ /**
+ * the number of moves added to the end of the block during the
+ * phi-removal process. Retained for subsequent move scheduling.
+ */
+ private int movesFromPhisAtEnd = 0;
+
+ /**
+ * the number of moves added to the beginning of the block during the
+ * phi-removal process. Retained for subsequent move scheduling.
+ */
+ private int movesFromPhisAtBeginning = 0;
+
+ /**
+ * contains last computed value of reachability of this block, or -1
+ * if reachability hasn't been calculated yet
+ */
+ private int reachable = -1;
+
+ /**
+ * {@code null-ok;} indexed by reg: the regs that are live-in at
+ * this block
+ */
+ private IntSet liveIn;
+
+ /**
+ * {@code null-ok;} indexed by reg: the regs that are live-out at
+ * this block
+ */
+ private IntSet liveOut;
+
+ /**
+ * Creates a new empty basic block.
+ *
+ * @param basicBlockIndex index this block will have
+ * @param ropLabel original rop-form label
+ * @param parent method of this block
+ */
+ public SsaBasicBlock(final int basicBlockIndex, final int ropLabel,
+ final SsaMethod parent) {
+ this.parent = parent;
+ this.index = basicBlockIndex;
+ this.insns = new ArrayList<SsaInsn>();
+ this.ropLabel = ropLabel;
+
+ this.predecessors = new BitSet(parent.getBlocks().size());
+ this.successors = new BitSet(parent.getBlocks().size());
+ this.successorList = new IntList();
+
+ domChildren = new ArrayList<SsaBasicBlock>();
+ }
+
+ /**
+ * Creates a new SSA basic block from a ROP form basic block.
+ *
+ * @param rmeth original method
+ * @param basicBlockIndex index this block will have
+ * @param parent method of this block predecessor set will be
+ * updated
+ * @return new instance
+ */
+ public static SsaBasicBlock newFromRop(RopMethod rmeth,
+ int basicBlockIndex, final SsaMethod parent) {
+ BasicBlockList ropBlocks = rmeth.getBlocks();
+ BasicBlock bb = ropBlocks.get(basicBlockIndex);
+ SsaBasicBlock result =
+ new SsaBasicBlock(basicBlockIndex, bb.getLabel(), parent);
+ InsnList ropInsns = bb.getInsns();
+
+ result.insns.ensureCapacity(ropInsns.size());
+
+ for (int i = 0, sz = ropInsns.size() ; i < sz ; i++) {
+ result.insns.add(new NormalSsaInsn (ropInsns.get(i), result));
+ }
+
+ result.predecessors = SsaMethod.bitSetFromLabelList(
+ ropBlocks,
+ rmeth.labelToPredecessors(bb.getLabel()));
+
+ result.successors
+ = SsaMethod.bitSetFromLabelList(ropBlocks, bb.getSuccessors());
+
+ result.successorList
+ = SsaMethod.indexListFromLabelList(ropBlocks,
+ bb.getSuccessors());
+
+ if (result.successorList.size() != 0) {
+ int primarySuccessor = bb.getPrimarySuccessor();
+
+ result.primarySuccessor = (primarySuccessor < 0)
+ ? -1 : ropBlocks.indexOfLabel(primarySuccessor);
+ }
+
+ return result;
+ }
+
+ /**
+ * Adds a basic block as a dom child for this block. Used when constructing
+ * the dom tree.
+ *
+ * @param child {@code non-null;} new dom child
+ */
+ public void addDomChild(SsaBasicBlock child) {
+ domChildren.add(child);
+ }
+
+ /**
+ * Gets the dom children for this node. Don't modify this list.
+ *
+ * @return {@code non-null;} list of dom children
+ */
+ public ArrayList<SsaBasicBlock> getDomChildren() {
+ return domChildren;
+ }
+
+ /**
+ * Adds a phi insn to the beginning of this block. The result type of
+ * the phi will be set to void, to indicate that it's currently unknown.
+ *
+ * @param reg {@code >=0;} result reg
+ */
+ public void addPhiInsnForReg(int reg) {
+ insns.add(0, new PhiInsn(reg, this));
+ }
+
+ /**
+ * Adds a phi insn to the beginning of this block. This is to be used
+ * when the result type or local-association can be determined at phi
+ * insert time.
+ *
+ * @param resultSpec {@code non-null;} reg
+ */
+ public void addPhiInsnForReg(RegisterSpec resultSpec) {
+ insns.add(0, new PhiInsn(resultSpec, this));
+ }
+
+ /**
+ * Adds an insn to the head of this basic block, just after any phi
+ * insns.
+ *
+ * @param insn {@code non-null;} rop-form insn to add
+ */
+ public void addInsnToHead(Insn insn) {
+ SsaInsn newInsn = SsaInsn.makeFromRop(insn, this);
+ insns.add(getCountPhiInsns(), newInsn);
+ parent.onInsnAdded(newInsn);
+ }
+
+ /**
+ * Replaces the last insn in this block. The provided insn must have
+ * some branchingness.
+ *
+ * @param insn {@code non-null;} rop-form insn to add, which must branch.
+ */
+ public void replaceLastInsn(Insn insn) {
+ if (insn.getOpcode().getBranchingness() == Rop.BRANCH_NONE) {
+ throw new IllegalArgumentException("last insn must branch");
+ }
+
+ SsaInsn oldInsn = insns.get(insns.size() - 1);
+ SsaInsn newInsn = SsaInsn.makeFromRop(insn, this);
+
+ insns.set(insns.size() - 1, newInsn);
+
+ parent.onInsnRemoved(oldInsn);
+ parent.onInsnAdded(newInsn);
+ }
+
+ /**
+ * Visits each phi insn.
+ *
+ * @param v {@code non-null;} the callback
+ */
+ public void forEachPhiInsn(PhiInsn.Visitor v) {
+ int sz = insns.size();
+
+ for (int i = 0; i < sz; i++) {
+ SsaInsn insn = insns.get(i);
+ if (insn instanceof PhiInsn) {
+ v.visitPhiInsn((PhiInsn) insn);
+ } else {
+ /*
+ * Presently we assume PhiInsn's are in a continuous
+ * block at the top of the list
+ */
+ break;
+ }
+ }
+ }
+
+ /**
+ * Deletes all phi insns. Do this after adding appropriate move insns.
+ */
+ public void removeAllPhiInsns() {
+ /*
+ * Presently we assume PhiInsn's are in a continuous
+ * block at the top of the list.
+ */
+
+ insns.subList(0, getCountPhiInsns()).clear();
+ }
+
+ /**
+ * Gets the number of phi insns at the top of this basic block.
+ *
+ * @return count of phi insns
+ */
+ private int getCountPhiInsns() {
+ int countPhiInsns;
+
+ int sz = insns.size();
+ for (countPhiInsns = 0; countPhiInsns < sz; countPhiInsns++) {
+ SsaInsn insn = insns.get(countPhiInsns);
+ if (!(insn instanceof PhiInsn)) {
+ break;
+ }
+ }
+
+ return countPhiInsns;
+ }
+
+ /**
+ * @return {@code non-null;} the (mutable) instruction list for this block,
+ * with phi insns at the beginning
+ */
+ public ArrayList<SsaInsn> getInsns() {
+ return insns;
+ }
+
+ /**
+ * @return {@code non-null;} the (mutable) list of phi insns for this block
+ */
+ public List<SsaInsn> getPhiInsns() {
+ return insns.subList(0, getCountPhiInsns());
+ }
+
+ /**
+ * @return the block index of this block
+ */
+ public int getIndex() {
+ return index;
+ }
+
+ /**
+ * @return the label of this block in rop form
+ */
+ public int getRopLabel() {
+ return ropLabel;
+ }
+
+ /**
+ * @return the label of this block in rop form as a hex string
+ */
+ public String getRopLabelString() {
+ return Hex.u2(ropLabel);
+ }
+
+ /**
+ * @return {@code non-null;} predecessors set, indexed by block index
+ */
+ public BitSet getPredecessors() {
+ return predecessors;
+ }
+
+ /**
+ * @return {@code non-null;} successors set, indexed by block index
+ */
+ public BitSet getSuccessors() {
+ return successors;
+ }
+
+ /**
+ * @return {@code non-null;} ordered successor list, containing block
+ * indicies
+ */
+ public IntList getSuccessorList() {
+ return successorList;
+ }
+
+ /**
+ * @return {@code >= -1;} block index of primary successor or
+ * {@code -1} if no primary successor
+ */
+ public int getPrimarySuccessorIndex() {
+ return primarySuccessor;
+ }
+
+ /**
+ * @return rop label of primary successor
+ */
+ public int getPrimarySuccessorRopLabel() {
+ return parent.blockIndexToRopLabel(primarySuccessor);
+ }
+
+ /**
+ * @return {@code null-ok;} the primary successor block or {@code null}
+ * if there is none
+ */
+ public SsaBasicBlock getPrimarySuccessor() {
+ if (primarySuccessor < 0) {
+ return null;
+ } else {
+ return parent.getBlocks().get(primarySuccessor);
+ }
+ }
+
+ /**
+ * @return successor list of rop labels
+ */
+ public IntList getRopLabelSuccessorList() {
+ IntList result = new IntList(successorList.size());
+
+ int sz = successorList.size();
+
+ for (int i = 0; i < sz; i++) {
+ result.add(parent.blockIndexToRopLabel(successorList.get(i)));
+ }
+ return result;
+ }
+
+ /**
+ * @return {@code non-null;} method that contains this block
+ */
+ public SsaMethod getParent() {
+ return parent;
+ }
+
+ /**
+ * Inserts a new empty GOTO block as a predecessor to this block.
+ * All previous predecessors will be predecessors to the new block.
+ *
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public SsaBasicBlock insertNewPredecessor() {
+ SsaBasicBlock newPred = parent.makeNewGotoBlock();
+
+ // Update the new block.
+ newPred.predecessors = predecessors;
+ newPred.successors.set(index) ;
+ newPred.successorList.add(index);
+ newPred.primarySuccessor = index;
+
+
+ // Update us.
+ predecessors = new BitSet(parent.getBlocks().size());
+ predecessors.set(newPred.index);
+
+ // Update our (soon-to-be) old predecessors.
+ for (int i = newPred.predecessors.nextSetBit(0); i >= 0;
+ i = newPred.predecessors.nextSetBit(i + 1)) {
+
+ SsaBasicBlock predBlock = parent.getBlocks().get(i);
+
+ predBlock.replaceSuccessor(index, newPred.index);
+ }
+
+ return newPred;
+ }
+
+ /**
+ * Constructs and inserts a new empty GOTO block {@code Z} between
+ * this block ({@code A}) and a current successor block
+ * ({@code B}). The new block will replace B as A's successor and
+ * A as B's predecessor. A and B will no longer be directly connected.
+ * If B is listed as a successor multiple times, all references
+ * are replaced.
+ *
+ * @param other current successor (B)
+ * @return {@code non-null;} an appropriately-constructed instance
+ */
+ public SsaBasicBlock insertNewSuccessor(SsaBasicBlock other) {
+ SsaBasicBlock newSucc = parent.makeNewGotoBlock();
+
+ if (!successors.get(other.index)) {
+ throw new RuntimeException("Block " + other.getRopLabelString()
+ + " not successor of " + getRopLabelString());
+ }
+
+ // Update the new block.
+ newSucc.predecessors.set(this.index);
+ newSucc.successors.set(other.index) ;
+ newSucc.successorList.add(other.index);
+ newSucc.primarySuccessor = other.index;
+
+ // Update us.
+ for (int i = successorList.size() - 1 ; i >= 0; i--) {
+ if (successorList.get(i) == other.index) {
+ successorList.set(i, newSucc.index);
+ }
+ }
+
+ if (primarySuccessor == other.index) {
+ primarySuccessor = newSucc.index;
+ }
+ successors.clear(other.index);
+ successors.set(newSucc.index);
+
+ // Update "other".
+ other.predecessors.set(newSucc.index);
+ other.predecessors.set(index, successors.get(other.index));
+
+ return newSucc;
+ }
+
+ /**
+ * Replaces an old successor with a new successor. This will throw
+ * RuntimeException if {@code oldIndex} was not a successor.
+ *
+ * @param oldIndex index of old successor block
+ * @param newIndex index of new successor block
+ */
+ public void replaceSuccessor(int oldIndex, int newIndex) {
+ if (oldIndex == newIndex) {
+ return;
+ }
+
+ // Update us.
+ successors.set(newIndex);
+
+ if (primarySuccessor == oldIndex) {
+ primarySuccessor = newIndex;
+ }
+
+ for (int i = successorList.size() - 1 ; i >= 0; i--) {
+ if (successorList.get(i) == oldIndex) {
+ successorList.set(i, newIndex);
+ }
+ }
+
+ successors.clear(oldIndex);
+
+ // Update new successor.
+ parent.getBlocks().get(newIndex).predecessors.set(index);
+
+ // Update old successor.
+ parent.getBlocks().get(oldIndex).predecessors.clear(index);
+ }
+
+ /**
+ * Removes a successor from this block's successor list.
+ *
+ * @param oldIndex index of successor block to remove
+ */
+ public void removeSuccessor(int oldIndex) {
+ int removeIndex = 0;
+
+ for (int i = successorList.size() - 1; i >= 0; i--) {
+ if (successorList.get(i) == oldIndex) {
+ removeIndex = i;
+ } else {
+ primarySuccessor = successorList.get(i);
+ }
+ }
+
+ successorList.removeIndex(removeIndex);
+ successors.clear(oldIndex);
+ parent.getBlocks().get(oldIndex).predecessors.clear(index);
+ }
+
+ /**
+ * Attaches block to an exit block if necessary. If this block
+ * is not an exit predecessor or is the exit block, this block does
+ * nothing. For use by {@link com.android.jack.dx.ssa.SsaMethod#makeExitBlock}
+ *
+ * @param exitBlock {@code non-null;} exit block
+ */
+ public void exitBlockFixup(SsaBasicBlock exitBlock) {
+ if (this == exitBlock) {
+ return;
+ }
+
+ if (successorList.size() == 0) {
+ /*
+ * This is an exit predecessor.
+ * Set the successor to the exit block
+ */
+ successors.set(exitBlock.index);
+ successorList.add(exitBlock.index);
+ primarySuccessor = exitBlock.index;
+ exitBlock.predecessors.set(this.index);
+ }
+ }
+
+ /**
+ * Adds a move instruction to the end of this basic block, just
+ * before the last instruction. If the result of the final instruction
+ * is the source in question, then the move is placed at the beginning of
+ * the primary successor block. This is for unversioned registers.
+ *
+ * @param result move destination
+ * @param source move source
+ */
+ public void addMoveToEnd(RegisterSpec result, RegisterSpec source) {
+
+ if (result.getReg() == source.getReg()) {
+ // Sometimes we end up with no-op moves. Ignore them here.
+ return;
+ }
+
+ /*
+ * The last Insn has to be a normal SSA insn: a phi can't branch
+ * or return or cause an exception, etc.
+ */
+ NormalSsaInsn lastInsn;
+ lastInsn = (NormalSsaInsn)insns.get(insns.size()-1);
+
+ if (lastInsn.getResult() != null || lastInsn.getSources().size() > 0) {
+ /*
+ * The final insn in this block has a source or result
+ * register, and the moves we may need to place and
+ * schedule may interfere. We need to insert this
+ * instruction at the beginning of the primary successor
+ * block instead. We know this is safe, because when we
+ * edge-split earlier, we ensured that each successor has
+ * only us as a predecessor.
+ */
+
+ for (int i = successors.nextSetBit(0)
+ ; i >= 0
+ ; i = successors.nextSetBit(i + 1)) {
+
+ SsaBasicBlock succ;
+
+ succ = parent.getBlocks().get(i);
+ succ.addMoveToBeginning(result, source);
+ }
+ } else {
+ /*
+ * We can safely add a move to the end of the block just
+ * before the last instruction, because the final insn does
+ * not assign to anything.
+ */
+ RegisterSpecList sources = RegisterSpecList.make(source);
+ NormalSsaInsn toAdd = new NormalSsaInsn(
+ new PlainInsn(Rops.opMove(result.getType()),
+ SourcePosition.NO_INFO, result, sources), this);
+
+ insns.add(insns.size() - 1, toAdd);
+
+ movesFromPhisAtEnd++;
+ }
+ }
+
+ /**
+ * Adds a move instruction after the phi insn block.
+ *
+ * @param result move destination
+ * @param source move source
+ */
+ public void addMoveToBeginning (RegisterSpec result, RegisterSpec source) {
+ if (result.getReg() == source.getReg()) {
+ // Sometimes we end up with no-op moves. Ignore them here.
+ return;
+ }
+
+ RegisterSpecList sources = RegisterSpecList.make(source);
+ NormalSsaInsn toAdd = new NormalSsaInsn(
+ new PlainInsn(Rops.opMove(result.getType()),
+ SourcePosition.NO_INFO, result, sources), this);
+
+ insns.add(getCountPhiInsns(), toAdd);
+ movesFromPhisAtBeginning++;
+ }
+
+ /**
+ * Sets the register as used in a bitset, taking into account its
+ * category/width.
+ *
+ * @param regsUsed set, indexed by register number
+ * @param rs register to mark as used
+ */
+ private static void setRegsUsed (BitSet regsUsed, RegisterSpec rs) {
+ regsUsed.set(rs.getReg());
+ if (rs.getCategory() > 1) {
+ regsUsed.set(rs.getReg() + 1);
+ }
+ }
+
+ /**
+ * Checks to see if the register is used in a bitset, taking
+ * into account its category/width.
+ *
+ * @param regsUsed set, indexed by register number
+ * @param rs register to mark as used
+ * @return true if register is fully or partially (for the case of wide
+ * registers) used.
+ */
+ private static boolean checkRegUsed (BitSet regsUsed, RegisterSpec rs) {
+ int reg = rs.getReg();
+ int category = rs.getCategory();
+
+ return regsUsed.get(reg)
+ || (category == 2 ? regsUsed.get(reg + 1) : false);
+ }
+
+ /**
+ * Ensures that all move operations in this block occur such that
+ * reads of any register happen before writes to that register.
+ * NOTE: caller is expected to returnSpareRegisters()!
+ *
+ * TODO: See Briggs, et al "Practical Improvements to the Construction and
+ * Destruction of Static Single Assignment Form" section 5. a) This can
+ * be done in three passes.
+ *
+ * @param toSchedule List of instructions. Must consist only of moves.
+ */
+ private void scheduleUseBeforeAssigned(List<SsaInsn> toSchedule) {
+ BitSet regsUsedAsSources = new BitSet(parent.getRegCount());
+
+ // TODO: Get rid of this.
+ BitSet regsUsedAsResults = new BitSet(parent.getRegCount());
+
+ int sz = toSchedule.size();
+
+ int insertPlace = 0;
+
+ while (insertPlace < sz) {
+ int oldInsertPlace = insertPlace;
+
+ // Record all registers used as sources in this block.
+ for (int i = insertPlace; i < sz; i++) {
+ setRegsUsed(regsUsedAsSources,
+ toSchedule.get(i).getSources().get(0));
+
+ setRegsUsed(regsUsedAsResults,
+ toSchedule.get(i).getResult());
+ }
+
+ /*
+ * If there are no circular dependencies, then there exists
+ * n instructions where n > 1 whose result is not used as a source.
+ */
+ for (int i = insertPlace; i <sz; i++) {
+ SsaInsn insn = toSchedule.get(i);
+
+ /*
+ * Move these n registers to the front, since they overwrite
+ * nothing.
+ */
+ if (!checkRegUsed(regsUsedAsSources, insn.getResult())) {
+ Collections.swap(toSchedule, i, insertPlace++);
+ }
+ }
+
+ /*
+ * If we've made no progress in this iteration, there's a
+ * circular dependency. Split it using the temp reg.
+ */
+ if (oldInsertPlace == insertPlace) {
+
+ SsaInsn insnToSplit = null;
+
+ // Find an insn whose result is used as a source.
+ for (int i = insertPlace; i < sz; i++) {
+ SsaInsn insn = toSchedule.get(i);
+ if (checkRegUsed(regsUsedAsSources, insn.getResult())
+ && checkRegUsed(regsUsedAsResults,
+ insn.getSources().get(0))) {
+
+ insnToSplit = insn;
+ /*
+ * We're going to split this insn; move it to the
+ * front.
+ */
+ Collections.swap(toSchedule, insertPlace, i);
+ break;
+ }
+ }
+
+ // At least one insn will be set above.
+
+ RegisterSpec result = insnToSplit.getResult();
+ RegisterSpec tempSpec = result.withReg(
+ parent.borrowSpareRegister(result.getCategory()));
+
+ NormalSsaInsn toAdd = new NormalSsaInsn(
+ new PlainInsn(Rops.opMove(result.getType()),
+ SourcePosition.NO_INFO,
+ tempSpec,
+ insnToSplit.getSources()), this);
+
+ toSchedule.add(insertPlace++, toAdd);
+
+ RegisterSpecList newSources = RegisterSpecList.make(tempSpec);
+
+ NormalSsaInsn toReplace = new NormalSsaInsn(
+ new PlainInsn(Rops.opMove(result.getType()),
+ SourcePosition.NO_INFO,
+ result,
+ newSources), this);
+
+ toSchedule.set(insertPlace, toReplace);
+
+ // The size changed.
+ sz = toSchedule.size();
+ }
+
+ regsUsedAsSources.clear();
+ regsUsedAsResults.clear();
+ }
+ }
+
+ /**
+ * Adds {@code regV} to the live-out list for this block. This is called
+ * by the liveness analyzer.
+ *
+ * @param regV register that is live-out for this block.
+ */
+ public void addLiveOut (int regV) {
+ if (liveOut == null) {
+ liveOut = SetFactory.makeLivenessSet(parent.getRegCount());
+ }
+
+ liveOut.add(regV);
+ }
+
+ /**
+ * Adds {@code regV} to the live-in list for this block. This is
+ * called by the liveness analyzer.
+ *
+ * @param regV register that is live-in for this block.
+ */
+ public void addLiveIn (int regV) {
+ if (liveIn == null) {
+ liveIn = SetFactory.makeLivenessSet(parent.getRegCount());
+ }
+
+ liveIn.add(regV);
+ }
+
+ /**
+ * Returns the set of live-in registers. Valid after register
+ * interference graph has been generated, otherwise empty.
+ *
+ * @return {@code non-null;} live-in register set.
+ */
+ public IntSet getLiveInRegs() {
+ if (liveIn == null) {
+ liveIn = SetFactory.makeLivenessSet(parent.getRegCount());
+ }
+ return liveIn;
+ }
+
+ /**
+ * Returns the set of live-out registers. Valid after register
+ * interference graph has been generated, otherwise empty.
+ *
+ * @return {@code non-null;} live-out register set
+ */
+ public IntSet getLiveOutRegs() {
+ if (liveOut == null) {
+ liveOut = SetFactory.makeLivenessSet(parent.getRegCount());
+ }
+ return liveOut;
+ }
+
+ /**
+ * @return true if this is the one-and-only exit block for this method
+ */
+ public boolean isExitBlock() {
+ return index == parent.getExitBlockIndex();
+ }
+
+ /**
+ * Returns true if this block was last calculated to be reachable.
+ * Recalculates reachability if value has never been computed.
+ *
+ * @return {@code true} if reachable
+ */
+ public boolean isReachable() {
+ if (reachable == -1) {
+ parent.computeReachability();
+ }
+ return (reachable == 1);
+ }
+
+ /**
+ * Sets reachability of block to specified value
+ *
+ * @param reach new value of reachability for block
+ */
+ public void setReachable(int reach) {
+ reachable = reach;
+ }
+
+ /**
+ * Sorts move instructions added via {@code addMoveToEnd} during
+ * phi removal so that results don't overwrite sources that are used.
+ * For use after all phis have been removed and all calls to
+ * addMoveToEnd() have been made.<p>
+ *
+ * This is necessary because copy-propogation may have left us in a state
+ * where the same basic block has the same register as a phi operand
+ * and a result. In this case, the register in the phi operand always
+ * refers value before any other phis have executed.
+ */
+ public void scheduleMovesFromPhis() {
+ if (movesFromPhisAtBeginning > 1) {
+ List<SsaInsn> toSchedule;
+
+ toSchedule = insns.subList(0, movesFromPhisAtBeginning);
+
+ scheduleUseBeforeAssigned(toSchedule);
+
+ SsaInsn firstNonPhiMoveInsn = insns.get(movesFromPhisAtBeginning);
+
+ /*
+ * TODO: It's actually possible that this case never happens,
+ * because a move-exception block, having only one predecessor
+ * in SSA form, perhaps is never on a dominance frontier.
+ */
+ if (firstNonPhiMoveInsn.isMoveException()) {
+ if (true) {
+ /*
+ * We've yet to observe this case, and if it can
+ * occur the code written to handle it probably
+ * does not work.
+ */
+ throw new RuntimeException(
+ "Unexpected: moves from "
+ +"phis before move-exception");
+ } else {
+ /*
+ * A move-exception insn must be placed first in this block
+ * We need to move it there, and deal with possible
+ * interference.
+ */
+ boolean moveExceptionInterferes = false;
+
+ int moveExceptionResult
+ = firstNonPhiMoveInsn.getResult().getReg();
+
+ /*
+ * Does the move-exception result reg interfere with the
+ * phi moves?
+ */
+ for (SsaInsn insn : toSchedule) {
+ if (insn.isResultReg(moveExceptionResult)
+ || insn.isRegASource(moveExceptionResult)) {
+ moveExceptionInterferes = true;
+ break;
+ }
+ }
+
+ if (!moveExceptionInterferes) {
+ // This is the easy case.
+ insns.remove(movesFromPhisAtBeginning);
+ insns.add(0, firstNonPhiMoveInsn);
+ } else {
+ /*
+ * We need to move the result to a spare reg
+ * and move it back.
+ */
+ RegisterSpec originalResultSpec
+ = firstNonPhiMoveInsn.getResult();
+ int spareRegister = parent.borrowSpareRegister(
+ originalResultSpec.getCategory());
+
+ // We now move it to a spare register.
+ firstNonPhiMoveInsn.changeResultReg(spareRegister);
+ RegisterSpec tempSpec =
+ firstNonPhiMoveInsn.getResult();
+
+ insns.add(0, firstNonPhiMoveInsn);
+
+ // And here we move it back.
+
+ NormalSsaInsn toAdd = new NormalSsaInsn(
+ new PlainInsn(
+ Rops.opMove(tempSpec.getType()),
+ SourcePosition.NO_INFO,
+ originalResultSpec,
+ RegisterSpecList.make(tempSpec)),
+ this);
+
+
+ /*
+ * Place it immediately after the phi-moves,
+ * overwriting the move-exception that was there.
+ */
+ insns.set(movesFromPhisAtBeginning + 1, toAdd);
+ }
+ }
+ }
+ }
+
+ if (movesFromPhisAtEnd > 1) {
+ scheduleUseBeforeAssigned(
+ insns.subList(insns.size() - movesFromPhisAtEnd - 1,
+ insns.size() - 1));
+ }
+
+ // Return registers borrowed here and in scheduleUseBeforeAssigned().
+ parent.returnSpareRegisters();
+
+ }
+
+ /**
+ * Visits all insns in this block.
+ *
+ * @param visitor {@code non-null;} callback interface
+ */
+ public void forEachInsn(SsaInsn.Visitor visitor) {
+ // This gets called a LOT, and not using an iterator
+ // saves a lot of allocations and reduces memory usage
+ int len = insns.size();
+ for (int i = 0; i < len; i++) {
+ insns.get(i).accept(visitor);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return "{" + index + ":" + Hex.u2(ropLabel) + '}';
+ }
+
+ /**
+ * Visitor interface for basic blocks.
+ */
+ public interface Visitor {
+ /**
+ * Indicates a block has been visited by an iterator method.
+ *
+ * @param v {@code non-null;} block visited
+ * @param parent {@code null-ok;} parent node if applicable
+ */
+ void visitBlock (SsaBasicBlock v, SsaBasicBlock parent);
+ }
+
+ /**
+ * Label comparator.
+ */
+ public static final class LabelComparator
+ implements Comparator<SsaBasicBlock> {
+ /** {@inheritDoc} */
+ public int compare(SsaBasicBlock b1, SsaBasicBlock b2) {
+ int label1 = b1.ropLabel;
+ int label2 = b2.ropLabel;
+
+ if (label1 < label2) {
+ return -1;
+ } else if (label1 > label2) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ }
+}
diff --git a/dx/src/com/android/jack/dx/ssa/SsaConverter.java b/dx/src/com/android/jack/dx/ssa/SsaConverter.java
new file mode 100644
index 00000000..89dd19dd
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/SsaConverter.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa;
+
+import com.android.jack.dx.rop.code.RegisterSpec;
+import com.android.jack.dx.rop.code.RopMethod;
+import com.android.jack.dx.util.IntIterator;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+
+/**
+ * Converts ROP methods to SSA Methods
+ */
+public class SsaConverter {
+ public static final boolean DEBUG = false;
+
+ /**
+ * Returns an SSA representation, edge-split and with phi
+ * functions placed.
+ *
+ * @param rmeth input
+ * @param paramWidth the total width, in register-units, of the method's
+ * parameters
+ * @param isStatic {@code true} if this method has no {@code this}
+ * pointer argument
+ * @return output in SSA form
+ */
+ public static SsaMethod convertToSsaMethod(RopMethod rmeth,
+ int paramWidth, boolean isStatic) {
+ SsaMethod result
+ = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic);
+
+ edgeSplit(result);
+
+ LocalVariableInfo localInfo = LocalVariableExtractor.extract(result);
+
+ placePhiFunctions(result, localInfo, 0);
+ new SsaRenamer(result).run();
+
+ /*
+ * The exit block, added here, is not considered for edge splitting
+ * or phi placement since no actual control flows to it.
+ */
+ result.makeExitBlock();
+
+ return result;
+ }
+
+ /**
+ * Updates an SSA representation, placing phi functions and renaming all
+ * registers above a certain threshold number.
+ *
+ * @param ssaMeth input
+ * @param threshold registers below this number are unchanged
+ */
+ public static void updateSsaMethod(SsaMethod ssaMeth, int threshold) {
+ LocalVariableInfo localInfo = LocalVariableExtractor.extract(ssaMeth);
+ placePhiFunctions(ssaMeth, localInfo, threshold);
+ new SsaRenamer(ssaMeth, threshold).run();
+ }
+
+ /**
+ * Returns an SSA represention with only the edge-splitter run.
+ *
+ * @param rmeth method to process
+ * @param paramWidth width of all arguments in the method
+ * @param isStatic {@code true} if this method has no {@code this}
+ * pointer argument
+ * @return an SSA represention with only the edge-splitter run
+ */
+ public static SsaMethod testEdgeSplit (RopMethod rmeth, int paramWidth,
+ boolean isStatic) {
+ SsaMethod result;
+
+ result = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic);
+
+ edgeSplit(result);
+ return result;
+ }
+
+ /**
+ * Returns an SSA represention with only the steps through the
+ * phi placement run.
+ *
+ * @param rmeth method to process
+ * @param paramWidth width of all arguments in the method
+ * @param isStatic {@code true} if this method has no {@code this}
+ * pointer argument
+ * @return an SSA represention with only the edge-splitter run
+ */
+ public static SsaMethod testPhiPlacement (RopMethod rmeth, int paramWidth,
+ boolean isStatic) {
+ SsaMethod result;
+
+ result = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic);
+
+ edgeSplit(result);
+
+ LocalVariableInfo localInfo = LocalVariableExtractor.extract(result);
+
+ placePhiFunctions(result, localInfo, 0);
+ return result;
+ }
+
+ /**
+ * See Appel section 19.1:
+ *
+ * Converts CFG into "edge-split" form, such that each node either a
+ * unique successor or unique predecessor.<p>
+ *
+ * In addition, the SSA form we use enforces a further constraint,
+ * requiring each block with a final instruction that returns a
+ * value to have a primary successor that has no other
+ * predecessor. This ensures move statements can always be
+ * inserted correctly when phi statements are removed.
+ *
+ * @param result method to process
+ */
+ private static void edgeSplit(SsaMethod result) {
+ edgeSplitPredecessors(result);
+ edgeSplitMoveExceptionsAndResults(result);
+ edgeSplitSuccessors(result);
+ }
+
+ /**
+ * Inserts Z nodes as new predecessors for every node that has multiple
+ * successors and multiple predecessors.
+ *
+ * @param result {@code non-null;} method to process
+ */
+ private static void edgeSplitPredecessors(SsaMethod result) {
+ ArrayList<SsaBasicBlock> blocks = result.getBlocks();
+
+ /*
+ * New blocks are added to the end of the block list during
+ * this iteration.
+ */
+ for (int i = blocks.size() - 1; i >= 0; i-- ) {
+ SsaBasicBlock block = blocks.get(i);
+ if (nodeNeedsUniquePredecessor(block)) {
+ block.insertNewPredecessor();
+ }
+ }
+ }
+
+ /**
+ * @param block {@code non-null;} block in question
+ * @return {@code true} if this node needs to have a unique
+ * predecessor created for it
+ */
+ private static boolean nodeNeedsUniquePredecessor(SsaBasicBlock block) {
+ /*
+ * Any block with that has both multiple successors and multiple
+ * predecessors needs a new predecessor node.
+ */
+
+ int countPredecessors = block.getPredecessors().cardinality();
+ int countSuccessors = block.getSuccessors().cardinality();
+
+ return (countPredecessors > 1 && countSuccessors > 1);
+ }
+
+ /**
+ * In ROP form, move-exception must occur as the first insn in a block
+ * immediately succeeding the insn that could thrown an exception.
+ * We may need room to insert move insns later, so make sure to split
+ * any block that starts with a move-exception such that there is a
+ * unique move-exception block for each predecessor.
+ *
+ * @param ssaMeth method to process
+ */
+ private static void edgeSplitMoveExceptionsAndResults(SsaMethod ssaMeth) {
+ ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks();
+
+ /*
+ * New blocks are added to the end of the block list during
+ * this iteration.
+ */
+ for (int i = blocks.size() - 1; i >= 0; i-- ) {
+ SsaBasicBlock block = blocks.get(i);
+
+ /*
+ * Any block that starts with a move-exception and has more than
+ * one predecessor...
+ */
+ if (!block.isExitBlock()
+ && block.getPredecessors().cardinality() > 1
+ && block.getInsns().get(0).isMoveException()) {
+
+ // block.getPredecessors() is changed in the loop below.
+ BitSet preds = (BitSet)block.getPredecessors().clone();
+ for (int j = preds.nextSetBit(0); j >= 0;
+ j = preds.nextSetBit(j + 1)) {
+ SsaBasicBlock predecessor = blocks.get(j);
+ SsaBasicBlock zNode
+ = predecessor.insertNewSuccessor(block);
+
+ /*
+ * Make sure to place the move-exception as the
+ * first insn.
+ */
+ zNode.getInsns().add(0, block.getInsns().get(0).clone());
+ }
+
+ // Remove the move-exception from the original block.
+ block.getInsns().remove(0);
+ }
+ }
+ }
+
+ /**
+ * Inserts Z nodes for every node that needs a new
+ * successor.
+ *
+ * @param result {@code non-null;} method to process
+ */
+ private static void edgeSplitSuccessors(SsaMethod result) {
+ ArrayList<SsaBasicBlock> blocks = result.getBlocks();
+
+ /*
+ * New blocks are added to the end of the block list during
+ * this iteration.
+ */
+ for (int i = blocks.size() - 1; i >= 0; i-- ) {
+ SsaBasicBlock block = blocks.get(i);
+
+ // Successors list is modified in loop below.
+ BitSet successors = (BitSet)block.getSuccessors().clone();
+ for (int j = successors.nextSetBit(0);
+ j >= 0; j = successors.nextSetBit(j+1)) {
+
+ SsaBasicBlock succ = blocks.get(j);
+
+ if (needsNewSuccessor(block, succ)) {
+ block.insertNewSuccessor(succ);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns {@code true} if block and successor need a Z-node
+ * between them. Presently, this is {@code true} if the final
+ * instruction has any sources or results and the current
+ * successor block has more than one predecessor.
+ *
+ * @param block predecessor node
+ * @param succ successor node
+ * @return {@code true} if a Z node is needed
+ */
+ private static boolean needsNewSuccessor(SsaBasicBlock block,
+ SsaBasicBlock succ) {
+ ArrayList<SsaInsn> insns = block.getInsns();
+ SsaInsn lastInsn = insns.get(insns.size() - 1);
+
+ return ((lastInsn.getResult() != null)
+ || (lastInsn.getSources().size() > 0))
+ && succ.getPredecessors().cardinality() > 1;
+ }
+
+ /**
+ * See Appel algorithm 19.6:
+ *
+ * Place Phi functions in appropriate locations.
+ *
+ * @param ssaMeth {@code non-null;} method to process.
+ * Modifications are made in-place.
+ * @param localInfo {@code non-null;} local variable info, used
+ * when placing phis
+ * @param threshold registers below this number are ignored
+ */
+ private static void placePhiFunctions (SsaMethod ssaMeth,
+ LocalVariableInfo localInfo, int threshold) {
+ ArrayList<SsaBasicBlock> ssaBlocks;
+ int regCount;
+ int blockCount;
+
+ ssaBlocks = ssaMeth.getBlocks();
+ blockCount = ssaBlocks.size();
+ regCount = ssaMeth.getRegCount() - threshold;
+
+ DomFront df = new DomFront(ssaMeth);
+ DomFront.DomInfo[] domInfos = df.run();
+
+ // Bit set of registers vs block index "definition sites"
+ BitSet[] defsites = new BitSet[regCount];
+
+ // Bit set of registers vs block index "phi placement sites"
+ BitSet[] phisites = new BitSet[regCount];
+
+ for (int i = 0; i < regCount; i++) {
+ defsites[i] = new BitSet(blockCount);
+ phisites[i] = new BitSet(blockCount);
+ }
+
+ /*
+ * For each register, build a set of all basic blocks where
+ * containing an assignment to that register.
+ */
+ for (int bi = 0, s = ssaBlocks.size(); bi < s; bi++) {
+ SsaBasicBlock b = ssaBlocks.get(bi);
+
+ for (SsaInsn insn : b.getInsns()) {
+ RegisterSpec rs = insn.getResult();
+
+ if (rs != null && rs.getReg() - threshold >= 0) {
+ defsites[rs.getReg() - threshold].set(bi);
+ }
+ }
+ }
+
+ if (DEBUG) {
+ System.out.println("defsites");
+
+ for (int i = 0; i < regCount; i++) {
+ StringBuilder sb = new StringBuilder();
+ sb.append('v').append(i).append(": ");
+ sb.append(defsites[i].toString());
+ System.out.println(sb);
+ }
+ }
+
+ BitSet worklist;
+
+ /*
+ * For each register, compute all locations for phi placement
+ * based on dominance-frontier algorithm.
+ */
+ for (int reg = 0, s = regCount; reg < s; reg++) {
+ int workBlockIndex;
+
+ /* Worklist set starts out with each node where reg is assigned. */
+
+ worklist = (BitSet) (defsites[reg].clone());
+
+ while (0 <= (workBlockIndex = worklist.nextSetBit(0))) {
+ worklist.clear(workBlockIndex);
+ IntIterator dfIterator
+ = domInfos[workBlockIndex].dominanceFrontiers.iterator();
+
+ while (dfIterator.hasNext()) {
+ int dfBlockIndex = dfIterator.next();
+
+ if (!phisites[reg].get(dfBlockIndex)) {
+ phisites[reg].set(dfBlockIndex);
+
+ int tReg = reg + threshold;
+ RegisterSpec rs
+ = localInfo.getStarts(dfBlockIndex).get(tReg);
+
+ if (rs == null) {
+ ssaBlocks.get(dfBlockIndex).addPhiInsnForReg(tReg);
+ } else {
+ ssaBlocks.get(dfBlockIndex).addPhiInsnForReg(rs);
+ }
+
+ if (!defsites[reg].get(dfBlockIndex)) {
+ worklist.set(dfBlockIndex);
+ }
+ }
+ }
+ }
+ }
+
+ if (DEBUG) {
+ System.out.println("phisites");
+
+ for (int i = 0; i < regCount; i++) {
+ StringBuilder sb = new StringBuilder();
+ sb.append('v').append(i).append(": ");
+ sb.append(phisites[i].toString());
+ System.out.println(sb);
+ }
+ }
+ }
+}
diff --git a/dx/src/com/android/jack/dx/ssa/SsaInsn.java b/dx/src/com/android/jack/dx/ssa/SsaInsn.java
new file mode 100644
index 00000000..bf76db26
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/SsaInsn.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa;
+
+import com.android.jack.dx.rop.code.*;
+import com.android.jack.dx.util.ToHuman;
+
+/**
+ * An instruction in SSA form
+ */
+public abstract class SsaInsn implements ToHuman, Cloneable {
+ /** {@code non-null;} the block that contains this instance */
+ private final SsaBasicBlock block;
+
+ /** {@code null-ok;} result register */
+ private RegisterSpec result;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param result {@code null-ok;} initial result register. May be changed.
+ * @param block {@code non-null;} block containing this insn. Can
+ * never change.
+ */
+ protected SsaInsn(RegisterSpec result, SsaBasicBlock block) {
+ if (block == null) {
+ throw new NullPointerException("block == null");
+ }
+
+ this.block = block;
+ this.result = result;
+ }
+
+ /**
+ * Makes a new SSA insn form a rop insn.
+ *
+ * @param insn {@code non-null;} rop insn
+ * @param block {@code non-null;} owning block
+ * @return {@code non-null;} an appropriately constructed instance
+ */
+ public static SsaInsn makeFromRop(Insn insn, SsaBasicBlock block) {
+ return new NormalSsaInsn(insn, block);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public SsaInsn clone() {
+ try {
+ return (SsaInsn)super.clone();
+ } catch (CloneNotSupportedException ex) {
+ throw new RuntimeException ("unexpected", ex);
+ }
+ }
+
+ /**
+ * Like {@link com.android.jack.dx.rop.code.Insn getResult()}.
+ *
+ * @return result register
+ */
+ public RegisterSpec getResult() {
+ return result;
+ }
+
+ /**
+ * Set the result register.
+ *
+ * @param result {@code non-null;} the new result register
+ */
+ protected void setResult(RegisterSpec result) {
+ if (result == null) {
+ throw new NullPointerException("result == null");
+ }
+
+ this.result = result;
+ }
+
+ /**
+ * Like {@link com.android.jack.dx.rop.code.Insn getSources()}.
+ *
+ * @return {@code non-null;} sources list
+ */
+ abstract public RegisterSpecList getSources();
+
+ /**
+ * Gets the block to which this insn instance belongs.
+ *
+ * @return owning block
+ */
+ public SsaBasicBlock getBlock() {
+ return block;
+ }
+
+ /**
+ * Returns whether or not the specified reg is the result reg.
+ *
+ * @param reg register to test
+ * @return true if there is a result and it is stored in the specified
+ * register
+ */
+ public boolean isResultReg(int reg) {
+ return result != null && result.getReg() == reg;
+ }
+
+
+ /**
+ * Changes the result register if this insn has a result. This is used
+ * during renaming.
+ *
+ * @param reg new result register
+ */
+ public void changeResultReg(int reg) {
+ if (result != null) {
+ result = result.withReg(reg);
+ }
+ }
+
+ /**
+ * Sets the local association for the result of this insn. This is
+ * sometimes updated during the SsaRenamer process.
+ *
+ * @param local {@code null-ok;} new debug/local variable info
+ */
+ public final void setResultLocal(LocalItem local) {
+ LocalItem oldItem = result.getLocalItem();
+
+ if (local != oldItem && (local == null
+ || !local.equals(result.getLocalItem()))) {
+ result = RegisterSpec.makeLocalOptional(
+ result.getReg(), result.getType(), local);
+ }
+ }
+
+ /**
+ * Map registers after register allocation.
+ *
+ * @param mapper {@code non-null;} mapping from old to new registers
+ */
+ public final void mapRegisters(RegisterMapper mapper) {
+ RegisterSpec oldResult = result;
+
+ result = mapper.map(result);
+ block.getParent().updateOneDefinition(this, oldResult);
+ mapSourceRegisters(mapper);
+ }
+
+ /**
+ * Maps only source registers.
+ *
+ * @param mapper new mapping
+ */
+ abstract public void mapSourceRegisters(RegisterMapper mapper);
+
+ /**
+ * Returns the Rop opcode for this insn, or null if this is a phi insn.
+ *
+ * TODO: Move this up into NormalSsaInsn.
+ *
+ * @return {@code null-ok;} Rop opcode if there is one.
+ */
+ abstract public Rop getOpcode();
+
+ /**
+ * Returns the original Rop insn for this insn, or null if this is
+ * a phi insn.
+ *
+ * TODO: Move this up into NormalSsaInsn.
+ *
+ * @return {@code null-ok;} Rop insn if there is one.
+ */
+ abstract public Insn getOriginalRopInsn();
+
+ /**
+ * Gets the spec of a local variable assignment that occurs at this
+ * instruction, or null if no local variable assignment occurs. This
+ * may be the result register, or for {@code mark-local} insns
+ * it may be the source.
+ *
+ * @see com.android.jack.dx.rop.code.Insn#getLocalAssignment()
+ *
+ * @return {@code null-ok;} a local-associated register spec or null
+ */
+ public RegisterSpec getLocalAssignment() {
+ if (result != null && result.getLocalItem() != null) {
+ return result;
+ }
+
+ return null;
+ }
+
+ /**
+ * Indicates whether the specified register is amongst the registers
+ * used as sources for this instruction.
+ *
+ * @param reg the register in question
+ * @return true if the reg is a source
+ */
+ public boolean isRegASource(int reg) {
+ return null != getSources().specForRegister(reg);
+ }
+
+ /**
+ * Transform back to ROP form.
+ *
+ * TODO: Move this up into NormalSsaInsn.
+ *
+ * @return {@code non-null;} a ROP representation of this instruction, with
+ * updated registers.
+ */
+ public abstract Insn toRopInsn();
+
+ /**
+ * @return true if this is a PhiInsn or a normal move insn
+ */
+ public abstract boolean isPhiOrMove();
+
+ /**
+ * Returns true if this insn is considered to have a side effect beyond
+ * that of assigning to the result reg.
+ *
+ * @return true if this insn is considered to have a side effect beyond
+ * that of assigning to the result reg.
+ */
+ public abstract boolean hasSideEffect();
+
+ /**
+ * @return true if this is a move (but not a move-operand or
+ * move-exception) instruction
+ */
+ public boolean isNormalMoveInsn() {
+ return false;
+ }
+
+ /**
+ * @return true if this is a move-exception instruction.
+ * These instructions must immediately follow a preceeding invoke*
+ */
+ public boolean isMoveException() {
+ return false;
+ }
+
+ /**
+ * @return true if this instruction can throw.
+ */
+ abstract public boolean canThrow();
+
+ /**
+ * Accepts a visitor.
+ *
+ * @param v {@code non-null} the visitor
+ */
+ public abstract void accept(Visitor v);
+
+ /**
+ * Visitor interface for this class.
+ */
+ public static interface Visitor {
+ /**
+ * Any non-phi move instruction
+ * @param insn {@code non-null;} the instruction to visit
+ */
+ public void visitMoveInsn(NormalSsaInsn insn);
+
+ /**
+ * Any phi insn
+ * @param insn {@code non-null;} the instruction to visit
+ */
+ public void visitPhiInsn(PhiInsn insn);
+
+ /**
+ * Any insn that isn't a move or a phi (which is also a move).
+ * @param insn {@code non-null;} the instruction to visit
+ */
+ public void visitNonMoveInsn(NormalSsaInsn insn);
+ }
+}
diff --git a/dx/src/com/android/jack/dx/ssa/SsaMethod.java b/dx/src/com/android/jack/dx/ssa/SsaMethod.java
new file mode 100644
index 00000000..f0166df3
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/SsaMethod.java
@@ -0,0 +1,873 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa;
+
+import com.android.jack.dx.rop.code.BasicBlockList;
+import com.android.jack.dx.rop.code.Insn;
+import com.android.jack.dx.rop.code.PlainInsn;
+import com.android.jack.dx.rop.code.RegOps;
+import com.android.jack.dx.rop.code.RegisterSpec;
+import com.android.jack.dx.rop.code.RegisterSpecList;
+import com.android.jack.dx.rop.code.Rop;
+import com.android.jack.dx.rop.code.RopMethod;
+import com.android.jack.dx.rop.code.Rops;
+import com.android.jack.dx.rop.code.SourcePosition;
+import com.android.jack.dx.util.IntList;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.Stack;
+
+/**
+ * A method in SSA form.
+ */
+public final class SsaMethod {
+ /** basic blocks, indexed by block index */
+ private ArrayList<SsaBasicBlock> blocks;
+
+ /** Index of first executed block in method */
+ private int entryBlockIndex;
+
+ /**
+ * Index of exit block, which exists only in SSA form,
+ * or or {@code -1} if there is none
+ */
+ private int exitBlockIndex;
+
+ /** total number of registers required */
+ private int registerCount;
+
+ /** first register number to use for any temporary "spares" */
+ private int spareRegisterBase;
+
+ /** current count of spare registers used */
+ private int borrowedSpareRegisters;
+
+ /** really one greater than the max label */
+ private int maxLabel;
+
+ /** the total width, in register-units, of the method's parameters */
+ private final int paramWidth;
+
+ /** true if this method has no {@code this} pointer argument */
+ private final boolean isStatic;
+
+ /**
+ * indexed by register: the insn where said register is defined or null
+ * if undefined. null until (lazily) created.
+ */
+ private SsaInsn[] definitionList;
+
+ /** indexed by register: the list of all insns that use a register */
+ private ArrayList<SsaInsn>[] useList;
+
+ /** A version of useList with each List unmodifiable */
+ private List<SsaInsn>[] unmodifiableUseList;
+
+ /**
+ * "back-convert mode". Set during back-conversion when registers
+ * are about to be mapped into a non-SSA namespace. When true,
+ * use and def lists are unavailable.
+ *
+ * TODO: Remove this mode, and place the functionality elsewhere
+ */
+ private boolean backMode;
+
+ /**
+ * @param ropMethod rop-form method to convert from
+ * @param paramWidth the total width, in register-units, of the
+ * method's parameters
+ * @param isStatic {@code true} if this method has no {@code this}
+ * pointer argument
+ */
+ public static SsaMethod newFromRopMethod(RopMethod ropMethod,
+ int paramWidth, boolean isStatic) {
+ SsaMethod result = new SsaMethod(ropMethod, paramWidth, isStatic);
+
+ result.convertRopToSsaBlocks(ropMethod);
+
+ return result;
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param ropMethod {@code non-null;} the original rop-form method that
+ * this instance is based on
+ * @param paramWidth the total width, in register-units, of the
+ * method's parameters
+ * @param isStatic {@code true} if this method has no {@code this}
+ * pointer argument
+ */
+ private SsaMethod(RopMethod ropMethod, int paramWidth, boolean isStatic) {
+ this.paramWidth = paramWidth;
+ this.isStatic = isStatic;
+ this.backMode = false;
+ this.maxLabel = ropMethod.getBlocks().getMaxLabel();
+ this.registerCount = ropMethod.getBlocks().getRegCount();
+ this.spareRegisterBase = registerCount;
+ }
+
+ /**
+ * Builds a BitSet of block indices from a basic block list and a list
+ * of labels taken from Rop form.
+ *
+ * @param blocks Rop blocks
+ * @param labelList list of rop block labels
+ * @return BitSet of block indices
+ */
+ static BitSet bitSetFromLabelList(BasicBlockList blocks,
+ IntList labelList) {
+ BitSet result = new BitSet(blocks.size());
+
+ for (int i = 0, sz = labelList.size(); i < sz; i++) {
+ result.set(blocks.indexOfLabel(labelList.get(i)));
+ }
+
+ return result;
+ }
+
+ /**
+ * Builds an IntList of block indices from a basic block list and a list
+ * of labels taken from Rop form.
+ *
+ * @param ropBlocks Rop blocks
+ * @param labelList list of rop block labels
+ * @return IntList of block indices
+ */
+ public static IntList indexListFromLabelList(BasicBlockList ropBlocks,
+ IntList labelList) {
+
+ IntList result = new IntList(labelList.size());
+
+ for (int i = 0, sz = labelList.size(); i < sz; i++) {
+ result.add(ropBlocks.indexOfLabel(labelList.get(i)));
+ }
+
+ return result;
+ }
+
+ private void convertRopToSsaBlocks(RopMethod rmeth) {
+ BasicBlockList ropBlocks = rmeth.getBlocks();
+ int sz = ropBlocks.size();
+
+ blocks = new ArrayList<SsaBasicBlock>(sz + 2);
+
+ for (int i = 0; i < sz; i++) {
+ SsaBasicBlock sbb = SsaBasicBlock.newFromRop(rmeth, i, this);
+ blocks.add(sbb);
+ }
+
+ // Add an no-op entry block.
+ int origEntryBlockIndex = rmeth.getBlocks()
+ .indexOfLabel(rmeth.getFirstLabel());
+
+ SsaBasicBlock entryBlock
+ = blocks.get(origEntryBlockIndex).insertNewPredecessor();
+
+ entryBlockIndex = entryBlock.getIndex();
+ exitBlockIndex = -1; // This gets made later.
+ }
+
+ /**
+ * Creates an exit block and attaches it to the CFG if this method
+ * exits. Methods that never exit will not have an exit block. This
+ * is called after edge-splitting and phi insertion, since the edges
+ * going into the exit block should not be considered in those steps.
+ */
+ /*package*/ void makeExitBlock() {
+ if (exitBlockIndex >= 0) {
+ throw new RuntimeException("must be called at most once");
+ }
+
+ exitBlockIndex = blocks.size();
+ SsaBasicBlock exitBlock
+ = new SsaBasicBlock(exitBlockIndex, maxLabel++, this);
+
+ blocks.add(exitBlock);
+
+ for (SsaBasicBlock block : blocks) {
+ block.exitBlockFixup(exitBlock);
+ }
+
+ if (exitBlock.getPredecessors().cardinality() == 0) {
+ // In cases where there is no exit...
+ blocks.remove(exitBlockIndex);
+ exitBlockIndex = -1;
+ maxLabel--;
+ }
+ }
+
+ /**
+ * Gets a new {@code GOTO} insn.
+ *
+ * @param block block to which this GOTO will be added
+ * (not it's destination!)
+ * @return an appropriately-constructed instance.
+ */
+ private static SsaInsn getGoto(SsaBasicBlock block) {
+ return new NormalSsaInsn (
+ new PlainInsn(Rops.GOTO, SourcePosition.NO_INFO,
+ null, RegisterSpecList.EMPTY), block);
+ }
+
+ /**
+ * Makes a new basic block for this method, which is empty besides
+ * a single {@code GOTO}. Successors and predecessors are not yet
+ * set.
+ *
+ * @return new block
+ */
+ public SsaBasicBlock makeNewGotoBlock() {
+ int newIndex = blocks.size();
+ SsaBasicBlock newBlock = new SsaBasicBlock(newIndex, maxLabel++, this);
+
+ newBlock.getInsns().add(getGoto(newBlock));
+ blocks.add(newBlock);
+
+ return newBlock;
+ }
+
+ /**
+ * @return block index of first execution block
+ */
+ public int getEntryBlockIndex() {
+ return entryBlockIndex;
+ }
+
+ /**
+ * @return first execution block
+ */
+ public SsaBasicBlock getEntryBlock() {
+ return blocks.get(entryBlockIndex);
+ }
+
+ /**
+ * @return block index of exit block or {@code -1} if there is none
+ */
+ public int getExitBlockIndex() {
+ return exitBlockIndex;
+ }
+
+ /**
+ * @return {@code null-ok;} block of exit block or {@code null} if
+ * there is none
+ */
+ public SsaBasicBlock getExitBlock() {
+ return exitBlockIndex < 0 ? null : blocks.get(exitBlockIndex);
+ }
+
+ /**
+ * @param bi block index or {@code -1} for none
+ * @return rop label or {code -1} if {@code bi} was {@code -1}
+ */
+ public int blockIndexToRopLabel(int bi) {
+ if (bi < 0) {
+ return -1;
+ }
+ return blocks.get(bi).getRopLabel();
+ }
+
+ /**
+ * @return count of registers used in this method
+ */
+ public int getRegCount() {
+ return registerCount;
+ }
+
+ /**
+ * @return the total width, in register units, of the method's
+ * parameters
+ */
+ public int getParamWidth() {
+ return paramWidth;
+ }
+
+ /**
+ * Returns {@code true} if this is a static method.
+ *
+ * @return {@code true} if this is a static method
+ */
+ public boolean isStatic() {
+ return isStatic;
+ }
+
+ /**
+ * Borrows a register to use as a temp. Used in the phi removal process.
+ * Call returnSpareRegisters() when done.
+ *
+ * @param category width (1 or 2) of the register
+ * @return register number to use
+ */
+ public int borrowSpareRegister(int category) {
+ int result = spareRegisterBase + borrowedSpareRegisters;
+
+ borrowedSpareRegisters += category;
+ registerCount = Math.max(registerCount, result + category);
+
+ return result;
+ }
+
+ /**
+ * Returns all borrowed registers.
+ */
+ public void returnSpareRegisters() {
+ borrowedSpareRegisters = 0;
+ }
+
+ /**
+ * @return {@code non-null;} basic block list. Do not modify.
+ */
+ public ArrayList<SsaBasicBlock> getBlocks() {
+ return blocks;
+ }
+
+ /**
+ * Returns the count of reachable blocks in this method: blocks that have
+ * predecessors (or are the start block)
+ *
+ * @return {@code >= 0;} number of reachable basic blocks
+ */
+ public int getCountReachableBlocks() {
+ int ret = 0;
+
+ for (SsaBasicBlock b : blocks) {
+ // Blocks that have been disconnected don't count.
+ if (b.isReachable()) {
+ ret++;
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * Computes reachability for all blocks in the method. First clears old
+ * values from all blocks, then starts with the entry block and walks down
+ * the control flow graph, marking all blocks it finds as reachable.
+ */
+ public void computeReachability() {
+ for (SsaBasicBlock block : blocks) {
+ block.setReachable(0);
+ }
+
+ ArrayList<SsaBasicBlock> blockList = new ArrayList<SsaBasicBlock>();
+ blockList.add(this.getEntryBlock());
+
+ while (!blockList.isEmpty()) {
+ SsaBasicBlock block = blockList.remove(0);
+ if (block.isReachable()) continue;
+
+ block.setReachable(1);
+ BitSet succs = block.getSuccessors();
+ for (int i = succs.nextSetBit(0); i >= 0;
+ i = succs.nextSetBit(i + 1)) {
+ blockList.add(blocks.get(i));
+ }
+ }
+ }
+
+ /**
+ * Remaps unversioned registers.
+ *
+ * @param mapper maps old registers to new.
+ */
+ public void mapRegisters(RegisterMapper mapper) {
+ for (SsaBasicBlock block : getBlocks()) {
+ for (SsaInsn insn : block.getInsns()) {
+ insn.mapRegisters(mapper);
+ }
+ }
+
+ registerCount = mapper.getNewRegisterCount();
+ spareRegisterBase = registerCount;
+ }
+
+ /**
+ * Returns the insn that defines the given register
+ * @param reg register in question
+ * @return insn (actual instance from code) that defined this reg or null
+ * if reg is not defined.
+ */
+ public SsaInsn getDefinitionForRegister(int reg) {
+ if (backMode) {
+ throw new RuntimeException("No def list in back mode");
+ }
+
+ if (definitionList != null) {
+ return definitionList[reg];
+ }
+
+ definitionList = new SsaInsn[getRegCount()];
+
+ forEachInsn(new SsaInsn.Visitor() {
+ public void visitMoveInsn (NormalSsaInsn insn) {
+ definitionList[insn.getResult().getReg()] = insn;
+ }
+ public void visitPhiInsn (PhiInsn phi) {
+ definitionList[phi.getResult().getReg()] = phi;
+ }
+ public void visitNonMoveInsn (NormalSsaInsn insn) {
+ RegisterSpec result = insn.getResult();
+ if (result != null) {
+ definitionList[insn.getResult().getReg()] = insn;
+ }
+ }
+ });
+
+ return definitionList[reg];
+ }
+
+ /**
+ * Builds useList and unmodifiableUseList.
+ */
+ private void buildUseList() {
+ if (backMode) {
+ throw new RuntimeException("No use list in back mode");
+ }
+
+ useList = new ArrayList[registerCount];
+
+ for (int i = 0; i < registerCount; i++) {
+ useList[i] = new ArrayList();
+ }
+
+ forEachInsn(new SsaInsn.Visitor() {
+ /** {@inheritDoc} */
+ public void visitMoveInsn (NormalSsaInsn insn) {
+ addToUses(insn);
+ }
+ /** {@inheritDoc} */
+ public void visitPhiInsn (PhiInsn phi) {
+ addToUses(phi);
+ }
+ /** {@inheritDoc} */
+ public void visitNonMoveInsn (NormalSsaInsn insn) {
+ addToUses(insn);
+ }
+ /**
+ * Adds specified insn to the uses list for all of its sources.
+ * @param insn {@code non-null;} insn to process
+ */
+ private void addToUses(SsaInsn insn) {
+ RegisterSpecList rl = insn.getSources();
+ int sz = rl.size();
+
+ for (int i = 0; i < sz; i++) {
+ useList[rl.get(i).getReg()].add(insn);
+ }
+ }
+ });
+
+ unmodifiableUseList = new List[registerCount];
+
+ for (int i = 0; i < registerCount; i++) {
+ unmodifiableUseList[i] = Collections.unmodifiableList(useList[i]);
+ }
+ }
+
+ /**
+ * Updates the use list for a single change in source register.
+ *
+ * @param insn {@code non-null;} insn being changed
+ * @param oldSource {@code null-ok;} The source that was used, if
+ * applicable
+ * @param newSource {@code non-null;} the new source being used
+ */
+ /*package*/ void onSourceChanged(SsaInsn insn,
+ RegisterSpec oldSource, RegisterSpec newSource) {
+ if (useList == null) return;
+
+ if (oldSource != null) {
+ int reg = oldSource.getReg();
+ useList[reg].remove(insn);
+ }
+
+ int reg = newSource.getReg();
+ if (useList.length <= reg) {
+ useList = null;
+ return;
+ }
+ useList[reg].add(insn);
+ }
+
+ /**
+ * Updates the use list for a source list change.
+ *
+ * @param insn {@code insn non-null;} insn being changed.
+ * {@code insn.getSources()} must return the new source list.
+ * @param oldSources {@code null-ok;} list of sources that were
+ * previously used
+ */
+ /*package*/ void onSourcesChanged(SsaInsn insn,
+ RegisterSpecList oldSources) {
+ if (useList == null) return;
+
+ if (oldSources != null) {
+ removeFromUseList(insn, oldSources);
+ }
+
+ RegisterSpecList sources = insn.getSources();
+ int szNew = sources.size();
+
+ for (int i = 0; i < szNew; i++) {
+ int reg = sources.get(i).getReg();
+ useList[reg].add(insn);
+ }
+ }
+
+ /**
+ * Removes a given {@code insn} from the use lists for the given
+ * {@code oldSources} (rather than the sources currently
+ * returned by insn.getSources()).
+ *
+ * @param insn {@code non-null;} insn in question
+ * @param oldSources {@code null-ok;} registers whose use lists
+ * {@code insn} should be removed form
+ */
+ private void removeFromUseList(SsaInsn insn, RegisterSpecList oldSources) {
+ if (oldSources == null) {
+ return;
+ }
+
+ int szNew = oldSources.size();
+ for (int i = 0; i < szNew; i++) {
+ if (!useList[oldSources.get(i).getReg()].remove(insn)) {
+ throw new RuntimeException("use not found");
+ }
+ }
+ }
+
+ /**
+ * Adds an insn to both the use and def lists. For use when adding
+ * a new insn to the method.
+ *
+ * @param insn {@code non-null;} insn to add
+ */
+ /*package*/ void onInsnAdded(SsaInsn insn) {
+ onSourcesChanged(insn, null);
+ updateOneDefinition(insn, null);
+ }
+
+ /**
+ * Removes an instruction from use and def lists. For use during
+ * instruction removal.
+ *
+ * @param insn {@code non-null;} insn to remove
+ */
+ /*package*/ void onInsnRemoved(SsaInsn insn) {
+ if (useList != null) {
+ removeFromUseList(insn, insn.getSources());
+ }
+
+ RegisterSpec resultReg = insn.getResult();
+ if (definitionList != null && resultReg != null) {
+ definitionList[resultReg.getReg()] = null;
+ }
+ }
+
+ /**
+ * Indicates that the instruction list has changed or the SSA register
+ * count has increased, so that internal datastructures that rely on
+ * it should be rebuild. In general, the various other on* methods
+ * should be called in preference when changes occur if they are
+ * applicable.
+ */
+ public void onInsnsChanged() {
+ // Definition list will need to be recomputed
+ definitionList = null;
+
+ // Use list will need to be recomputed
+ useList = null;
+ unmodifiableUseList = null;
+ }
+
+ /**
+ * Updates a single definition.
+ *
+ * @param insn {@code non-null;} insn who's result should be recorded as
+ * a definition
+ * @param oldResult {@code null-ok;} a previous result that should
+ * be no longer considered a definition by this insn
+ */
+ /*package*/ void updateOneDefinition(SsaInsn insn,
+ RegisterSpec oldResult) {
+ if (definitionList == null) return;
+
+ if (oldResult != null) {
+ int reg = oldResult.getReg();
+ definitionList[reg] = null;
+ }
+
+ RegisterSpec resultReg = insn.getResult();
+
+ if (resultReg != null) {
+ int reg = resultReg.getReg();
+
+ if (definitionList[reg] != null) {
+ throw new RuntimeException("Duplicate add of insn");
+ } else {
+ definitionList[resultReg.getReg()] = insn;
+ }
+ }
+ }
+
+ /**
+ * Returns the list of all source uses (not results) for a register.
+ *
+ * @param reg register in question
+ * @return unmodifiable instruction list
+ */
+ public List<SsaInsn> getUseListForRegister(int reg) {
+
+ if (unmodifiableUseList == null) {
+ buildUseList();
+ }
+
+ return unmodifiableUseList[reg];
+ }
+
+ /**
+ * Returns a modifiable copy of the register use list.
+ *
+ * @return modifiable copy of the use-list, indexed by register
+ */
+ public ArrayList<SsaInsn>[] getUseListCopy() {
+ if (useList == null) {
+ buildUseList();
+ }
+
+ ArrayList<SsaInsn>[] useListCopy
+ = (ArrayList<SsaInsn>[])(new ArrayList[registerCount]);
+
+ for (int i = 0; i < registerCount; i++) {
+ useListCopy[i] = (ArrayList<SsaInsn>)(new ArrayList(useList[i]));
+ }
+
+ return useListCopy;
+ }
+
+ /**
+ * Checks to see if the given SSA reg is ever associated with a local
+ * local variable. Each SSA reg may be associated with at most one
+ * local var.
+ *
+ * @param spec {@code non-null;} ssa reg
+ * @return true if reg is ever associated with a local
+ */
+ public boolean isRegALocal(RegisterSpec spec) {
+ SsaInsn defn = getDefinitionForRegister(spec.getReg());
+
+ if (defn == null) {
+ // version 0 registers are never used as locals
+ return false;
+ }
+
+ // Does the definition have a local associated with it?
+ if (defn.getLocalAssignment() != null) return true;
+
+ // If not, is there a mark-local insn?
+ for (SsaInsn use : getUseListForRegister(spec.getReg())) {
+ Insn insn = use.getOriginalRopInsn();
+
+ if (insn != null
+ && insn.getOpcode().getOpcode() == RegOps.MARK_LOCAL) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Sets the new register count after renaming.
+ *
+ * @param newRegCount new register count
+ */
+ /*package*/ void setNewRegCount(int newRegCount) {
+ registerCount = newRegCount;
+ spareRegisterBase = registerCount;
+ onInsnsChanged();
+ }
+
+ /**
+ * Makes a new SSA register. For use after renaming has completed.
+ *
+ * @return {@code >=0;} new SSA register.
+ */
+ public int makeNewSsaReg() {
+ int reg = registerCount++;
+ spareRegisterBase = registerCount;
+ onInsnsChanged();
+ return reg;
+ }
+
+ /**
+ * Visits all insns in this method.
+ *
+ * @param visitor {@code non-null;} callback interface
+ */
+ public void forEachInsn(SsaInsn.Visitor visitor) {
+ for (SsaBasicBlock block : blocks) {
+ block.forEachInsn(visitor);
+ }
+ }
+
+ /**
+ * Visits each phi insn in this method
+ * @param v {@code non-null;} callback.
+ *
+ */
+ public void forEachPhiInsn(PhiInsn.Visitor v) {
+ for (SsaBasicBlock block : blocks) {
+ block.forEachPhiInsn(v);
+ }
+ }
+
+
+ /**
+ * Walks the basic block tree in depth-first order, calling the visitor
+ * method once for every block. This depth-first walk may be run forward
+ * from the method entry point or backwards from the method exit points.
+ *
+ * @param reverse true if this should walk backwards from the exit points
+ * @param v {@code non-null;} callback interface. {@code parent} is set
+ * unless this is the root node
+ */
+ public void forEachBlockDepthFirst(boolean reverse,
+ SsaBasicBlock.Visitor v) {
+ BitSet visited = new BitSet(blocks.size());
+
+ // We push the parent first, then the child on the stack.
+ Stack<SsaBasicBlock> stack = new Stack<SsaBasicBlock>();
+
+ SsaBasicBlock rootBlock = reverse ? getExitBlock() : getEntryBlock();
+
+ if (rootBlock == null) {
+ // in the case there's no exit block
+ return;
+ }
+
+ stack.add(null); // Start with null parent.
+ stack.add(rootBlock);
+
+ while (stack.size() > 0) {
+ SsaBasicBlock cur = stack.pop();
+ SsaBasicBlock parent = stack.pop();
+
+ if (!visited.get(cur.getIndex())) {
+ BitSet children
+ = reverse ? cur.getPredecessors() : cur.getSuccessors();
+ for (int i = children.nextSetBit(0); i >= 0
+ ; i = children.nextSetBit(i + 1)) {
+ stack.add(cur);
+ stack.add(blocks.get(i));
+ }
+ visited.set(cur.getIndex());
+ v.visitBlock(cur, parent);
+ }
+ }
+ }
+
+ /**
+ * Visits blocks in dom-tree order, starting at the current node.
+ * The {@code parent} parameter of the Visitor.visitBlock callback
+ * is currently always set to null.
+ *
+ * @param v {@code non-null;} callback interface
+ */
+ public void forEachBlockDepthFirstDom(SsaBasicBlock.Visitor v) {
+ BitSet visited = new BitSet(getBlocks().size());
+ Stack<SsaBasicBlock> stack = new Stack<SsaBasicBlock>();
+
+ stack.add(getEntryBlock());
+
+ while (stack.size() > 0) {
+ SsaBasicBlock cur = stack.pop();
+ ArrayList<SsaBasicBlock> curDomChildren = cur.getDomChildren();
+
+ if (!visited.get(cur.getIndex())) {
+ // We walk the tree this way for historical reasons...
+ for (int i = curDomChildren.size() - 1; i >= 0; i--) {
+ SsaBasicBlock child = curDomChildren.get(i);
+ stack.add(child);
+ }
+ visited.set(cur.getIndex());
+ v.visitBlock(cur, null);
+ }
+ }
+ }
+
+ /**
+ * Deletes all insns in the set from this method.
+ *
+ * @param deletedInsns {@code non-null;} insns to delete
+ */
+ public void deleteInsns(Set<SsaInsn> deletedInsns) {
+ for (SsaBasicBlock block : getBlocks()) {
+ ArrayList<SsaInsn> insns = block.getInsns();
+
+ for (int i = insns.size() - 1; i >= 0; i--) {
+ SsaInsn insn = insns.get(i);
+
+ if (deletedInsns.contains(insn)) {
+ onInsnRemoved(insn);
+ insns.remove(i);
+ }
+ }
+
+ // Check to see if we need to add a GOTO
+
+ int insnsSz = insns.size();
+ SsaInsn lastInsn = (insnsSz == 0) ? null : insns.get(insnsSz - 1);
+
+ if (block != getExitBlock() && (insnsSz == 0
+ || lastInsn.getOriginalRopInsn() == null
+ || lastInsn.getOriginalRopInsn().getOpcode()
+ .getBranchingness() == Rop.BRANCH_NONE)) {
+ // We managed to eat a throwable insn
+
+ Insn gotoInsn = new PlainInsn(Rops.GOTO,
+ SourcePosition.NO_INFO, null, RegisterSpecList.EMPTY);
+ insns.add(SsaInsn.makeFromRop(gotoInsn, block));
+
+ // Remove secondary successors from this block
+ BitSet succs = block.getSuccessors();
+ for (int i = succs.nextSetBit(0); i >= 0;
+ i = succs.nextSetBit(i + 1)) {
+ if (i != block.getPrimarySuccessorIndex()) {
+ block.removeSuccessor(i);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Sets "back-convert mode". Set during back-conversion when registers
+ * are about to be mapped into a non-SSA namespace. When true,
+ * use and def lists are unavailable.
+ */
+ public void setBackMode() {
+ backMode = true;
+ useList = null;
+ definitionList = null;
+ }
+}
diff --git a/dx/src/com/android/jack/dx/ssa/SsaRenamer.java b/dx/src/com/android/jack/dx/ssa/SsaRenamer.java
new file mode 100644
index 00000000..be9c51bc
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/SsaRenamer.java
@@ -0,0 +1,662 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa;
+
+import com.android.jack.dx.rop.code.LocalItem;
+import com.android.jack.dx.rop.code.PlainInsn;
+import com.android.jack.dx.rop.code.RegisterSpec;
+import com.android.jack.dx.rop.code.RegisterSpecList;
+import com.android.jack.dx.rop.code.Rops;
+import com.android.jack.dx.rop.code.SourcePosition;
+import com.android.jack.dx.rop.type.Type;
+import com.android.jack.dx.util.IntList;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashMap;
+import java.util.HashSet;
+
+/**
+ * Complete transformation to SSA form by renaming all registers accessed.<p>
+ *
+ * See Appel algorithm 19.7<p>
+ *
+ * Unlike the original algorithm presented in Appel, this renamer converts
+ * to a new flat (versionless) register space. The "version 0" registers,
+ * which represent the initial state of the Rop registers and should never
+ * actually be meaningfully accessed in a legal program, are represented
+ * as the first N registers in the SSA namespace. Subsequent assignments
+ * are assigned new unique names. Note that the incoming Rop representation
+ * has a concept of register widths, where 64-bit values are stored into
+ * two adjoining Rop registers. This adjoining register representation is
+ * ignored in SSA form conversion and while in SSA form, each register can be e
+ * either 32 or 64 bits wide depending on use. The adjoining-register
+ * represention is re-created later when converting back to Rop form. <p>
+ *
+ * But, please note, the SSA Renamer's ignoring of the adjoining-register ROP
+ * representation means that unaligned accesses to 64-bit registers are not
+ * supported. For example, you cannot do a 32-bit operation on a portion of
+ * a 64-bit register. This will never be observed to happen when coming
+ * from Java code, of course.<p>
+ *
+ * The implementation here, rather than keeping a single register version
+ * stack for the entire method as the dom tree is walked, instead keeps
+ * a mapping table for the current block being processed. Once the
+ * current block has been processed, this mapping table is then copied
+ * and used as the initial state for child blocks.<p>
+ */
+public class SsaRenamer implements Runnable {
+ /** debug flag */
+ private static final boolean DEBUG = false;
+
+ /** method we're processing */
+ private final SsaMethod ssaMeth;
+
+ /** next available SSA register */
+ private int nextSsaReg;
+
+ /** the number of original rop registers */
+ private final int ropRegCount;
+
+ /** work only on registers above this value */
+ private int threshold;
+
+ /**
+ * indexed by block index; register version state for each block start.
+ * This list is updated by each dom parent for its children. The only
+ * sub-arrays that exist at any one time are the start states for blocks
+ * yet to be processed by a {@code BlockRenamer} instance.
+ */
+ private final RegisterSpec[][] startsForBlocks;
+
+ /** map of SSA register number to debug (local var names) or null of n/a */
+ private final ArrayList<LocalItem> ssaRegToLocalItems;
+
+ /**
+ * maps SSA registers back to the original rop number. Used for
+ * debug only.
+ */
+ private IntList ssaRegToRopReg;
+
+ /**
+ * Constructs an instance of the renamer
+ *
+ * @param ssaMeth {@code non-null;} un-renamed SSA method that will
+ * be renamed.
+ */
+ public SsaRenamer(SsaMethod ssaMeth) {
+ ropRegCount = ssaMeth.getRegCount();
+
+ this.ssaMeth = ssaMeth;
+
+ /*
+ * Reserve the first N registers in the SSA register space for
+ * "version 0" registers.
+ */
+ nextSsaReg = ropRegCount;
+ threshold = 0;
+ startsForBlocks = new RegisterSpec[ssaMeth.getBlocks().size()][];
+
+ ssaRegToLocalItems = new ArrayList<LocalItem>();
+
+ if (DEBUG) {
+ ssaRegToRopReg = new IntList(ropRegCount);
+ }
+
+ /*
+ * Appel 19.7
+ *
+ * Initialization:
+ * for each variable a // register i
+ * Count[a] <- 0 // nextSsaReg, flattened
+ * Stack[a] <- 0 // versionStack
+ * push 0 onto Stack[a]
+ *
+ */
+
+ // top entry for the version stack is version 0
+ RegisterSpec[] initialRegMapping = new RegisterSpec[ropRegCount];
+ for (int i = 0; i < ropRegCount; i++) {
+ // everyone starts with a version 0 register
+ initialRegMapping[i] = RegisterSpec.make(i, Type.VOID);
+
+ if (DEBUG) {
+ ssaRegToRopReg.add(i);
+ }
+ }
+
+ // Initial state for entry block
+ startsForBlocks[ssaMeth.getEntryBlockIndex()] = initialRegMapping;
+ }
+
+ /**
+ * Constructs an instance of the renamer with threshold set
+ *
+ * @param ssaMeth {@code non-null;} un-renamed SSA method that will
+ * be renamed.
+ * @param thresh registers below this number are unchanged
+ */
+ public SsaRenamer(SsaMethod ssaMeth, int thresh) {
+ this(ssaMeth);
+ threshold = thresh;
+ }
+
+ /**
+ * Performs renaming transformation, modifying the method's instructions
+ * in-place.
+ */
+ public void run() {
+ // Rename each block in dom-tree DFS order.
+ ssaMeth.forEachBlockDepthFirstDom(new SsaBasicBlock.Visitor() {
+ public void visitBlock (SsaBasicBlock block,
+ SsaBasicBlock unused) {
+ new BlockRenamer(block).process();
+ }
+ });
+
+ ssaMeth.setNewRegCount(nextSsaReg);
+ ssaMeth.onInsnsChanged();
+
+ if (DEBUG) {
+ System.out.println("SSA\tRop");
+ /*
+ * We're going to compute the version of the rop register
+ * by keeping a running total of how many times the rop
+ * register has been mapped.
+ */
+ int[] versions = new int[ropRegCount];
+
+ int sz = ssaRegToRopReg.size();
+ for (int i = 0; i < sz; i++) {
+ int ropReg = ssaRegToRopReg.get(i);
+ System.out.println(i + "\t" + ropReg + "["
+ + versions[ropReg] + "]");
+ versions[ropReg]++;
+ }
+ }
+ }
+
+ /**
+ * Duplicates a RegisterSpec array.
+ *
+ * @param orig {@code non-null;} array to duplicate
+ * @return {@code non-null;} new instance
+ */
+ private static RegisterSpec[] dupArray(RegisterSpec[] orig) {
+ RegisterSpec[] copy = new RegisterSpec[orig.length];
+
+ System.arraycopy(orig, 0, copy, 0, orig.length);
+
+ return copy;
+ }
+
+ /**
+ * Gets a local variable item for a specified register.
+ *
+ * @param ssaReg register in SSA name space
+ * @return {@code null-ok;} Local variable name or null if none
+ */
+ private LocalItem getLocalForNewReg(int ssaReg) {
+ if (ssaReg < ssaRegToLocalItems.size()) {
+ return ssaRegToLocalItems.get(ssaReg);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Records a debug (local variable) name for a specified register.
+ *
+ * @param ssaReg non-null named register spec in SSA name space
+ */
+ private void setNameForSsaReg(RegisterSpec ssaReg) {
+ int reg = ssaReg.getReg();
+ LocalItem local = ssaReg.getLocalItem();
+
+ ssaRegToLocalItems.ensureCapacity(reg + 1);
+ while (ssaRegToLocalItems.size() <= reg) {
+ ssaRegToLocalItems.add(null);
+ }
+
+ ssaRegToLocalItems.set(reg, local);
+ }
+
+ /**
+ * Returns true if this SSA register is below the specified threshold.
+ * Used when most code is already in SSA form, and renaming is needed only
+ * for registers above a certain threshold.
+ *
+ * @param ssaReg the SSA register in question
+ * @return {@code true} if its register number is below the threshold
+ */
+ private boolean isBelowThresholdRegister(int ssaReg) {
+ return ssaReg < threshold;
+ }
+
+ /**
+ * Returns true if this SSA register is a "version 0"
+ * register. All version 0 registers are assigned the first N register
+ * numbers, where N is the count of original rop registers.
+ *
+ * @param ssaReg the SSA register in question
+ * @return true if it is a version 0 register.
+ */
+ private boolean isVersionZeroRegister(int ssaReg) {
+ return ssaReg < ropRegCount;
+ }
+
+ /**
+ * Returns true if a and b are equal or are both null.
+ *
+ * @param a null-ok
+ * @param b null-ok
+ * @return Returns true if a and b are equal or are both null
+ */
+ private static boolean equalsHandlesNulls(Object a, Object b) {
+ return a == b || (a != null && a.equals(b));
+ }
+
+ /**
+ * Processes all insns in a block and renames their registers
+ * as appropriate.
+ */
+ private class BlockRenamer implements SsaInsn.Visitor{
+ /** {@code non-null;} block we're processing. */
+ private final SsaBasicBlock block;
+
+ /**
+ * {@code non-null;} indexed by old register name. The current
+ * top of the version stack as seen by this block. It's
+ * initialized from the ending state of its dom parent,
+ * updated as the block's instructions are processed, and then
+ * copied to each one of its dom children.
+ */
+ private final RegisterSpec[] currentMapping;
+
+ /**
+ * contains the set of moves we need to keep to preserve local
+ * var info. All other moves will be deleted.
+ */
+ private final HashSet<SsaInsn> movesToKeep;
+
+ /**
+ * maps the set of insns to replace after renaming is finished
+ * on the block.
+ */
+ private final HashMap<SsaInsn, SsaInsn> insnsToReplace;
+
+ private final RenamingMapper mapper;
+
+ /**
+ * Constructs a block renamer instance. Call {@code process}
+ * to process.
+ *
+ * @param block {@code non-null;} block to process
+ */
+ BlockRenamer(final SsaBasicBlock block) {
+ this.block = block;
+ currentMapping = startsForBlocks[block.getIndex()];
+ movesToKeep = new HashSet<SsaInsn>();
+ insnsToReplace = new HashMap<SsaInsn, SsaInsn>();
+ mapper = new RenamingMapper();
+
+ // We don't need our own start state anymore
+ startsForBlocks[block.getIndex()] = null;
+ }
+
+ /**
+ * Provides a register mapping between the old register space
+ * and the current renaming mapping. The mapping is updated
+ * as the current block's instructions are processed.
+ */
+ private class RenamingMapper extends RegisterMapper {
+ public RenamingMapper() {
+ // This space intentionally left blank.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getNewRegisterCount() {
+ return nextSsaReg;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RegisterSpec map(RegisterSpec registerSpec) {
+ if (registerSpec == null) return null;
+
+ int reg = registerSpec.getReg();
+
+ // For debugging: assert that the mapped types are compatible.
+ if (DEBUG) {
+ RegisterSpec newVersion = currentMapping[reg];
+ if (newVersion.getBasicType() != Type.BT_VOID
+ && registerSpec.getBasicFrameType()
+ != newVersion.getBasicFrameType()) {
+
+ throw new RuntimeException(
+ "mapping registers of incompatible types! "
+ + registerSpec
+ + " " + currentMapping[reg]);
+ }
+ }
+
+ return registerSpec.withReg(currentMapping[reg].getReg());
+ }
+ }
+
+ /**
+ * Renames all the variables in this block and inserts appriopriate
+ * phis in successor blocks.
+ */
+ public void process() {
+ /*
+ * From Appel:
+ *
+ * Rename(n) =
+ * for each statement S in block n // 'statement' in 'block'
+ */
+
+ block.forEachInsn(this);
+
+ updateSuccessorPhis();
+
+ // Delete all move insns in this block.
+ ArrayList<SsaInsn> insns = block.getInsns();
+ int szInsns = insns.size();
+
+ for (int i = szInsns - 1; i >= 0 ; i--) {
+ SsaInsn insn = insns.get(i);
+ SsaInsn replaceInsn;
+
+ replaceInsn = insnsToReplace.get(insn);
+
+ if (replaceInsn != null) {
+ insns.set(i, replaceInsn);
+ } else if (insn.isNormalMoveInsn()
+ && !movesToKeep.contains(insn)) {
+ insns.remove(i);
+ }
+ }
+
+ // Store the start states for our dom children.
+ boolean first = true;
+ for (SsaBasicBlock child : block.getDomChildren()) {
+ if (child != block) {
+ // Don't bother duplicating the array for the first child.
+ RegisterSpec[] childStart = first ? currentMapping
+ : dupArray(currentMapping);
+
+ startsForBlocks[child.getIndex()] = childStart;
+ first = false;
+ }
+ }
+
+ // currentMapping is owned by a child now.
+ }
+
+ /**
+ * Enforces a few contraints when a register mapping is added.
+ *
+ * <ol>
+ * <li> Ensures that all new SSA registers specs in the mapping
+ * table with the same register number are identical. In effect, once
+ * an SSA register spec has received or lost a local variable name,
+ * then every old-namespace register that maps to it should gain or
+ * lose its local variable name as well.
+ * <li> Records the local name associated with the
+ * register so that a register is never associated with more than one
+ * local.
+ * <li> ensures that only one SSA register
+ * at a time is considered to be associated with a local variable. When
+ * {@code currentMapping} is updated and the newly added element
+ * is named, strip that name from any other SSA registers.
+ * </ol>
+ *
+ * @param ropReg {@code >= 0;} rop register number
+ * @param ssaReg {@code non-null;} an SSA register that has just
+ * been added to {@code currentMapping}
+ */
+ private void addMapping(int ropReg, RegisterSpec ssaReg) {
+ int ssaRegNum = ssaReg.getReg();
+ LocalItem ssaRegLocal = ssaReg.getLocalItem();
+
+ currentMapping[ropReg] = ssaReg;
+
+ /*
+ * Ensure all SSA register specs with the same reg are identical.
+ */
+ for (int i = currentMapping.length - 1; i >= 0; i--) {
+ RegisterSpec cur = currentMapping[i];
+
+ if (ssaRegNum == cur.getReg()) {
+ currentMapping[i] = ssaReg;
+ }
+ }
+
+ // All further steps are for registers with local information.
+ if (ssaRegLocal == null) {
+ return;
+ }
+
+ // Record that this SSA reg has been associated with a local.
+ setNameForSsaReg(ssaReg);
+
+ // Ensure that no other SSA regs are associated with this local.
+ for (int i = currentMapping.length - 1; i >= 0; i--) {
+ RegisterSpec cur = currentMapping[i];
+
+ if (ssaRegNum != cur.getReg()
+ && ssaRegLocal.equals(cur.getLocalItem())) {
+ currentMapping[i] = cur.withLocalItem(null);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Phi insns have their result registers renamed.
+ */
+ public void visitPhiInsn(PhiInsn phi) {
+ /* don't process sources for phi's */
+ processResultReg(phi);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Move insns are treated as a simple mapping operation, and
+ * will later be removed unless they represent a local variable
+ * assignment. If they represent a local variable assignement, they
+ * are preserved.
+ */
+ public void visitMoveInsn(NormalSsaInsn insn) {
+ /*
+ * For moves: copy propogate the move if we can, but don't
+ * if we need to preserve local variable info and the
+ * result has a different name than the source.
+ */
+
+ RegisterSpec ropResult = insn.getResult();
+ int ropResultReg = ropResult.getReg();
+ int ropSourceReg = insn.getSources().get(0).getReg();
+
+ insn.mapSourceRegisters(mapper);
+ int ssaSourceReg = insn.getSources().get(0).getReg();
+
+ LocalItem sourceLocal
+ = currentMapping[ropSourceReg].getLocalItem();
+ LocalItem resultLocal = ropResult.getLocalItem();
+
+ /*
+ * A move from a register that's currently associated with a local
+ * to one that will not be associated with a local does not need
+ * to be preserved, but the local association should remain.
+ * Hence, we inherit the sourceLocal where the resultLocal is null.
+ */
+
+ LocalItem newLocal
+ = (resultLocal == null) ? sourceLocal : resultLocal;
+ LocalItem associatedLocal = getLocalForNewReg(ssaSourceReg);
+
+ /*
+ * If we take the new local, will only one local have ever
+ * been associated with this SSA reg?
+ */
+ boolean onlyOneAssociatedLocal
+ = associatedLocal == null || newLocal == null
+ || newLocal.equals(associatedLocal);
+
+ /*
+ * If we're going to copy-propogate, then the ssa register
+ * spec that's going to go into the mapping is made up of
+ * the source register number mapped from above, the type
+ * of the result, and the name either from the result (if
+ * specified) or inherited from the existing mapping.
+ *
+ * The move source has incomplete type information in null
+ * object cases, so the result type is used.
+ */
+ RegisterSpec ssaReg
+ = RegisterSpec.makeLocalOptional(
+ ssaSourceReg, ropResult.getType(), newLocal);
+
+ if (!Optimizer.getPreserveLocals() || (onlyOneAssociatedLocal
+ && equalsHandlesNulls(newLocal, sourceLocal)) &&
+ threshold == 0) {
+ /*
+ * We don't have to keep this move to preserve local
+ * information. Either the name is the same, or the result
+ * register spec is unnamed.
+ */
+
+ addMapping(ropResultReg, ssaReg);
+ } else if (onlyOneAssociatedLocal && sourceLocal == null &&
+ threshold == 0) {
+ /*
+ * The register was previously unnamed. This means that a
+ * local starts after it's first assignment in SSA form
+ */
+
+ RegisterSpecList ssaSources = RegisterSpecList.make(
+ RegisterSpec.make(ssaReg.getReg(),
+ ssaReg.getType(), newLocal));
+
+ SsaInsn newInsn
+ = SsaInsn.makeFromRop(
+ new PlainInsn(Rops.opMarkLocal(ssaReg),
+ SourcePosition.NO_INFO, null, ssaSources),block);
+
+ insnsToReplace.put(insn, newInsn);
+
+ // Just map as above.
+ addMapping(ropResultReg, ssaReg);
+ } else {
+ /*
+ * Do not copy-propogate, since the two registers have
+ * two different local-variable names.
+ */
+ processResultReg(insn);
+
+ movesToKeep.add(insn);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * All insns that are not move or phi insns have their source registers
+ * mapped ot the current mapping. Their result registers are then
+ * renamed to a new SSA register which is then added to the current
+ * register mapping.
+ */
+ public void visitNonMoveInsn(NormalSsaInsn insn) {
+ /* for each use of some variable X in S */
+ insn.mapSourceRegisters(mapper);
+
+ processResultReg(insn);
+ }
+
+ /**
+ * Renames the result register of this insn and updates the
+ * current register mapping. Does nothing if this insn has no result.
+ * Applied to all non-move insns.
+ *
+ * @param insn insn to process.
+ */
+ void processResultReg(SsaInsn insn) {
+ RegisterSpec ropResult = insn.getResult();
+
+ if (ropResult == null) {
+ return;
+ }
+
+ int ropReg = ropResult.getReg();
+ if (isBelowThresholdRegister(ropReg)) {
+ return;
+ }
+
+ insn.changeResultReg(nextSsaReg);
+ addMapping(ropReg, insn.getResult());
+
+ if (DEBUG) {
+ ssaRegToRopReg.add(ropReg);
+ }
+
+ nextSsaReg++;
+ }
+
+ /**
+ * Updates the phi insns in successor blocks with operands based
+ * on the current mapping of the rop register the phis represent.
+ */
+ private void updateSuccessorPhis() {
+ PhiInsn.Visitor visitor = new PhiInsn.Visitor() {
+ public void visitPhiInsn (PhiInsn insn) {
+ int ropReg;
+
+ ropReg = insn.getRopResultReg();
+ if (isBelowThresholdRegister(ropReg)) {
+ return;
+ }
+
+ /*
+ * Never add a version 0 register as a phi
+ * operand. Version 0 registers represent the
+ * initial register state, and thus are never
+ * significant. Furthermore, the register liveness
+ * algorithm doesn't properly count them as "live
+ * in" at the beginning of the method.
+ */
+
+ RegisterSpec stackTop = currentMapping[ropReg];
+ if (!isVersionZeroRegister(stackTop.getReg())) {
+ insn.addPhiOperand(stackTop, block);
+ }
+ }
+ };
+
+ BitSet successors = block.getSuccessors();
+ for (int i = successors.nextSetBit(0); i >= 0;
+ i = successors.nextSetBit(i + 1)) {
+ SsaBasicBlock successor = ssaMeth.getBlocks().get(i);
+ successor.forEachPhiInsn(visitor);
+ }
+ }
+ }
+}
diff --git a/dx/src/com/android/jack/dx/ssa/back/FirstFitAllocator.java b/dx/src/com/android/jack/dx/ssa/back/FirstFitAllocator.java
new file mode 100644
index 00000000..4fed8f2d
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/back/FirstFitAllocator.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa.back;
+
+import com.android.jack.dx.rop.code.CstInsn;
+import com.android.jack.dx.rop.cst.CstInteger;
+import com.android.jack.dx.ssa.BasicRegisterMapper;
+import com.android.jack.dx.ssa.NormalSsaInsn;
+import com.android.jack.dx.ssa.RegisterMapper;
+import com.android.jack.dx.ssa.SsaMethod;
+import com.android.jack.dx.util.BitIntSet;
+import com.android.jack.dx.util.IntSet;
+
+import java.util.BitSet;
+import java.util.ArrayList;
+
+/**
+ * Allocates registers via a naive n^2 register allocator.
+ * This allocator does not try to co-locate local variables or deal
+ * intelligently with different size register uses.
+ */
+public class FirstFitAllocator extends RegisterAllocator {
+ /**
+ * If true, allocator places parameters at the top of the frame
+ * in calling-convention order.
+ */
+ private static final boolean PRESLOT_PARAMS = true;
+
+ /** indexed by old reg; the set of old regs we've mapped */
+ private final BitSet mapped;
+
+ /** {@inheritDoc} */
+ public FirstFitAllocator(
+ final SsaMethod ssaMeth, final InterferenceGraph interference) {
+ super(ssaMeth, interference);
+
+ mapped = new BitSet(ssaMeth.getRegCount());
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean wantsParamsMovedHigh() {
+ return PRESLOT_PARAMS;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RegisterMapper allocateRegisters() {
+ int oldRegCount = ssaMeth.getRegCount();
+
+ BasicRegisterMapper mapper
+ = new BasicRegisterMapper(oldRegCount);
+
+ int nextNewRegister = 0;
+
+ if (PRESLOT_PARAMS) {
+ /*
+ * Reserve space for the params at the bottom of the register
+ * space. Later, we'll flip the params to the end of the register
+ * space.
+ */
+
+ nextNewRegister = ssaMeth.getParamWidth();
+ }
+
+ for (int i = 0; i < oldRegCount; i++) {
+ if (mapped.get(i)) {
+ // we already got this one
+ continue;
+ }
+
+ int maxCategory = getCategoryForSsaReg(i);
+ IntSet current = new BitIntSet(oldRegCount);
+
+ interference.mergeInterferenceSet(i, current);
+
+ boolean isPreslotted = false;
+ int newReg = 0;
+
+ if (PRESLOT_PARAMS && isDefinitionMoveParam(i)) {
+ // Any move-param definition must be a NormalSsaInsn
+ NormalSsaInsn defInsn = (NormalSsaInsn)
+ ssaMeth.getDefinitionForRegister(i);
+
+ newReg = paramNumberFromMoveParam(defInsn);
+
+ mapper.addMapping(i, newReg, maxCategory);
+ isPreslotted = true;
+ } else {
+ mapper.addMapping(i, nextNewRegister, maxCategory);
+ newReg = nextNewRegister;
+ }
+
+ for (int j = i + 1; j < oldRegCount; j++) {
+ if (mapped.get(j) || isDefinitionMoveParam(j)) {
+ continue;
+ }
+
+ /*
+ * If reg j doesn't interfere with the current mapping.
+ * Also, if this is a pre-slotted method parameter, we
+ * can't use more than the original param width.
+ */
+ if (!current.has(j)
+ && !(isPreslotted
+ && (maxCategory < getCategoryForSsaReg(j)))) {
+
+ interference.mergeInterferenceSet(j, current);
+
+ maxCategory = Math.max(maxCategory,
+ getCategoryForSsaReg(j));
+
+ mapper.addMapping(j, newReg, maxCategory);
+ mapped.set(j);
+ }
+ }
+
+ mapped.set(i);
+ if (!isPreslotted) {
+ nextNewRegister += maxCategory;
+ }
+ }
+
+ return mapper;
+ }
+
+ /**
+ * Returns the parameter number that this move-param insn refers to
+ * @param ndefInsn a move-param insn (otherwise, exceptions will be thrown)
+ * @return parameter number (offset in the total parameter width)
+ */
+ private int paramNumberFromMoveParam(NormalSsaInsn ndefInsn) {
+ CstInsn origInsn = (CstInsn) ndefInsn.getOriginalRopInsn();
+
+ return ((CstInteger) origInsn.getConstant()).getValue();
+ }
+}
diff --git a/dx/src/com/android/jack/dx/ssa/back/FirstFitLocalCombiningAllocator.java b/dx/src/com/android/jack/dx/ssa/back/FirstFitLocalCombiningAllocator.java
new file mode 100644
index 00000000..65a5d1d1
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/back/FirstFitLocalCombiningAllocator.java
@@ -0,0 +1,1139 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa.back;
+
+import com.android.jack.dx.rop.code.*;
+import com.android.jack.dx.rop.cst.CstInteger;
+import com.android.jack.dx.ssa.InterferenceRegisterMapper;
+import com.android.jack.dx.ssa.NormalSsaInsn;
+import com.android.jack.dx.ssa.Optimizer;
+import com.android.jack.dx.ssa.PhiInsn;
+import com.android.jack.dx.ssa.RegisterMapper;
+import com.android.jack.dx.ssa.SsaBasicBlock;
+import com.android.jack.dx.ssa.SsaInsn;
+import com.android.jack.dx.ssa.SsaMethod;
+import com.android.jack.dx.util.IntIterator;
+import com.android.jack.dx.util.IntSet;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Allocates registers in a first-fit fashion, with the bottom reserved for
+ * method parameters and all SSAregisters representing the same local variable
+ * kept together if possible.
+ */
+public class FirstFitLocalCombiningAllocator extends RegisterAllocator {
+ /** local debug flag */
+ private static final boolean DEBUG = false;
+
+ /** maps local variable to a list of associated SSA registers */
+ private final Map<LocalItem, ArrayList<RegisterSpec>> localVariables;
+
+ /** list of move-result-pesudo instructions seen in this method */
+ private final ArrayList<NormalSsaInsn> moveResultPseudoInsns;
+
+ /** list of invoke-range instructions seen in this method */
+ private final ArrayList<NormalSsaInsn> invokeRangeInsns;
+
+ /** list of phi instructions seen in this method */
+ private final ArrayList<PhiInsn> phiInsns;
+
+ /** indexed by SSA reg; the set of SSA regs we've mapped */
+ private final BitSet ssaRegsMapped;
+
+ /** Register mapper which will be our result */
+ private final InterferenceRegisterMapper mapper;
+
+ /** end of rop registers range (starting at 0) reserved for parameters */
+ private final int paramRangeEnd;
+
+ /** set of rop registers reserved for parameters or local variables */
+ private final BitSet reservedRopRegs;
+
+ /** set of rop registers that have been used by anything */
+ private final BitSet usedRopRegs;
+
+ /** true if converter should take steps to minimize rop-form registers */
+ private final boolean minimizeRegisters;
+
+ /**
+ * Constructs instance.
+ *
+ * @param ssaMeth {@code non-null;} method to process
+ * @param interference non-null interference graph for SSA registers
+ * @param minimizeRegisters true if converter should take steps to
+ * minimize rop-form registers
+ */
+ public FirstFitLocalCombiningAllocator(
+ SsaMethod ssaMeth, InterferenceGraph interference,
+ boolean minimizeRegisters) {
+ super(ssaMeth, interference);
+
+ ssaRegsMapped = new BitSet(ssaMeth.getRegCount());
+
+ mapper = new InterferenceRegisterMapper(
+ interference, ssaMeth.getRegCount());
+
+ this.minimizeRegisters = minimizeRegisters;
+
+ /*
+ * Reserve space for the params at the bottom of the register
+ * space. Later, we'll flip the params to the end of the register
+ * space.
+ */
+
+ paramRangeEnd = ssaMeth.getParamWidth();
+
+ reservedRopRegs = new BitSet(paramRangeEnd * 2);
+ reservedRopRegs.set(0, paramRangeEnd);
+ usedRopRegs = new BitSet(paramRangeEnd * 2);
+ localVariables = new TreeMap<LocalItem, ArrayList<RegisterSpec>>();
+ moveResultPseudoInsns = new ArrayList<NormalSsaInsn>();
+ invokeRangeInsns = new ArrayList<NormalSsaInsn>();
+ phiInsns = new ArrayList<PhiInsn>();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean wantsParamsMovedHigh() {
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RegisterMapper allocateRegisters() {
+
+ analyzeInstructions();
+
+ if (DEBUG) {
+ printLocalVars();
+ }
+
+ if (DEBUG) System.out.println("--->Mapping local-associated params");
+ handleLocalAssociatedParams();
+
+ if (DEBUG) System.out.println("--->Mapping other params");
+ handleUnassociatedParameters();
+
+ if (DEBUG) System.out.println("--->Mapping invoke-range");
+ handleInvokeRangeInsns();
+
+ if (DEBUG) {
+ System.out.println("--->Mapping local-associated non-params");
+ }
+ handleLocalAssociatedOther();
+
+ if (DEBUG) System.out.println("--->Mapping check-cast results");
+ handleCheckCastResults();
+
+ if (DEBUG) System.out.println("--->Mapping phis");
+ handlePhiInsns();
+
+ if (DEBUG) System.out.println("--->Mapping others");
+ handleNormalUnassociated();
+
+ return mapper;
+ }
+
+ /**
+ * Dumps local variable table to stdout for debugging.
+ */
+ private void printLocalVars() {
+ System.out.println("Printing local vars");
+ for (Map.Entry<LocalItem, ArrayList<RegisterSpec>> e :
+ localVariables.entrySet()) {
+ StringBuilder regs = new StringBuilder();
+
+ regs.append('{');
+ regs.append(' ');
+ for (RegisterSpec reg : e.getValue()) {
+ regs.append('v');
+ regs.append(reg.getReg());
+ regs.append(' ');
+ }
+ regs.append('}');
+ System.out.printf("Local: %s Registers: %s\n", e.getKey(), regs);
+ }
+ }
+
+ /**
+ * Maps all local-associated parameters to rop registers.
+ */
+ private void handleLocalAssociatedParams() {
+ for (ArrayList<RegisterSpec> ssaRegs : localVariables.values()) {
+ int sz = ssaRegs.size();
+ int paramIndex = -1;
+ int paramCategory = 0;
+
+ // First, find out if this local variable is a parameter.
+ for (int i = 0; i < sz; i++) {
+ RegisterSpec ssaSpec = ssaRegs.get(i);
+ int ssaReg = ssaSpec.getReg();
+
+ paramIndex = getParameterIndexForReg(ssaReg);
+
+ if (paramIndex >= 0) {
+ paramCategory = ssaSpec.getCategory();
+ addMapping(ssaSpec, paramIndex);
+ break;
+ }
+ }
+
+ if (paramIndex < 0) {
+ // This local wasn't a parameter.
+ continue;
+ }
+
+ // Any remaining local-associated registers will be mapped later.
+ tryMapRegs(ssaRegs, paramIndex, paramCategory, true);
+ }
+ }
+
+ /**
+ * Gets the parameter index for SSA registers that are method parameters.
+ * {@code -1} is returned for non-parameter registers.
+ *
+ * @param ssaReg {@code >=0;} SSA register to look up
+ * @return parameter index or {@code -1} if not a parameter
+ */
+ private int getParameterIndexForReg(int ssaReg) {
+ SsaInsn defInsn = ssaMeth.getDefinitionForRegister(ssaReg);
+ if (defInsn == null) {
+ return -1;
+ }
+
+ Rop opcode = defInsn.getOpcode();
+
+ // opcode == null for phi insns.
+ if (opcode != null && opcode.getOpcode() == RegOps.MOVE_PARAM) {
+ CstInsn origInsn = (CstInsn) defInsn.getOriginalRopInsn();
+ return ((CstInteger) origInsn.getConstant()).getValue();
+ }
+
+ return -1;
+ }
+
+ /**
+ * Maps all local-associated registers that are not parameters.
+ * Tries to find an unreserved range that's wide enough for all of
+ * the SSA registers, and then tries to map them all to that
+ * range. If not all fit, a new range is tried until all registers
+ * have been fit.
+ */
+ private void handleLocalAssociatedOther() {
+ for (ArrayList<RegisterSpec> specs : localVariables.values()) {
+ int ropReg = paramRangeEnd;
+
+ boolean done = false;
+ do {
+ int maxCategory = 1;
+
+ // Compute max category for remaining unmapped registers.
+ int sz = specs.size();
+ for (int i = 0; i < sz; i++) {
+ RegisterSpec ssaSpec = specs.get(i);
+ int category = ssaSpec.getCategory();
+ if (!ssaRegsMapped.get(ssaSpec.getReg())
+ && category > maxCategory) {
+ maxCategory = category;
+ }
+ }
+
+ ropReg = findRopRegForLocal(ropReg, maxCategory);
+ if (canMapRegs(specs, ropReg)) {
+ done = tryMapRegs(specs, ropReg, maxCategory, true);
+ }
+
+ // Increment for next call to findRopRegForLocal.
+ ropReg++;
+ } while (!done);
+ }
+ }
+
+ /**
+ * Tries to map a list of SSA registers into the a rop reg, marking
+ * used rop space as reserved. SSA registers that don't fit are left
+ * unmapped.
+ *
+ * @param specs {@code non-null;} SSA registers to attempt to map
+ * @param ropReg {@code >=0;} rop register to map to
+ * @param maxAllowedCategory {@code 1..2;} maximum category
+ * allowed in mapping.
+ * @param markReserved do so if {@code true}
+ * @return {@code true} if all registers were mapped, {@code false}
+ * if some remain unmapped
+ */
+ private boolean tryMapRegs(
+ ArrayList<RegisterSpec> specs, int ropReg,
+ int maxAllowedCategory, boolean markReserved) {
+ boolean remaining = false;
+ for (RegisterSpec spec : specs) {
+ if (ssaRegsMapped.get(spec.getReg())) {
+ continue;
+ }
+
+ boolean succeeded;
+ succeeded = tryMapReg(spec, ropReg, maxAllowedCategory);
+ remaining = !succeeded || remaining;
+ if (succeeded && markReserved) {
+ // This only needs to be called once really with
+ // the widest category used, but <shrug>
+ markReserved(ropReg, spec.getCategory());
+ }
+ }
+ return !remaining;
+ }
+
+ /**
+ * Tries to map an SSA register to a rop register.
+ *
+ * @param ssaSpec {@code non-null;} SSA register
+ * @param ropReg {@code >=0;} rop register
+ * @param maxAllowedCategory {@code 1..2;} the maximum category
+ * that the SSA register is allowed to be
+ * @return {@code true} if map succeeded, {@code false} if not
+ */
+ private boolean tryMapReg(RegisterSpec ssaSpec, int ropReg,
+ int maxAllowedCategory) {
+ if (ssaSpec.getCategory() <= maxAllowedCategory
+ && !ssaRegsMapped.get(ssaSpec.getReg())
+ && canMapReg(ssaSpec, ropReg)) {
+ addMapping(ssaSpec, ropReg);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Marks a range of rop registers as "reserved for a local variable."
+ *
+ * @param ropReg {@code >= 0;} rop register to reserve
+ * @param category {@code > 0;} width to reserve
+ */
+ private void markReserved(int ropReg, int category) {
+ reservedRopRegs.set(ropReg, ropReg + category, true);
+ }
+
+ /**
+ * Checks to see if any rop registers in the specified range are reserved
+ * for local variables or parameters.
+ *
+ * @param ropRangeStart {@code >= 0;} lowest rop register
+ * @param width {@code > 0;} number of rop registers in range.
+ * @return {@code true} if any register in range is marked reserved
+ */
+ private boolean rangeContainsReserved(int ropRangeStart, int width) {
+ for (int i = ropRangeStart; i < (ropRangeStart + width); i++) {
+ if (reservedRopRegs.get(i)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if given rop register represents the {@code this} pointer
+ * for a non-static method.
+ *
+ * @param startReg rop register
+ * @return true if the "this" pointer is located here.
+ */
+ private boolean isThisPointerReg(int startReg) {
+ // "this" is always the first parameter.
+ return startReg == 0 && !ssaMeth.isStatic();
+ }
+
+ /**
+ * Finds a range of unreserved rop registers.
+ *
+ * @param startReg {@code >= 0;} a rop register to start the search at
+ * @param width {@code > 0;} the width, in registers, required.
+ * @return {@code >= 0;} start of available register range.
+ */
+ private int findNextUnreservedRopReg(int startReg, int width) {
+ int reg;
+
+ reg = reservedRopRegs.nextClearBit(startReg);
+
+ while (true) {
+ int i = 1;
+
+ while (i < width && !reservedRopRegs.get(reg + i)) {
+ i++;
+ }
+
+ if (i == width) {
+ return reg;
+ }
+
+ reg = reservedRopRegs.nextClearBit(reg + i);
+ }
+ }
+
+ /**
+ * Finds a range of rop regs that can be used for local variables.
+ * If {@code MIX_LOCALS_AND_OTHER} is {@code false}, this means any
+ * rop register that has not yet been used.
+ *
+ * @param startReg {@code >= 0;} a rop register to start the search at
+ * @param width {@code > 0;} the width, in registers, required.
+ * @return {@code >= 0;} start of available register range.
+ */
+ private int findRopRegForLocal(int startReg, int width) {
+ int reg;
+
+ reg = usedRopRegs.nextClearBit(startReg);
+
+ while (true) {
+ int i = 1;
+
+ while (i < width && !usedRopRegs.get(reg + i)) {
+ i++;
+ }
+
+ if (i == width) {
+ return reg;
+ }
+
+ reg = usedRopRegs.nextClearBit(reg + i);
+ }
+ }
+
+ /**
+ * Maps any parameter that isn't local-associated, which can happen
+ * in the case where there is no java debug info.
+ */
+ private void handleUnassociatedParameters() {
+ int szSsaRegs = ssaMeth.getRegCount();
+
+ for (int ssaReg = 0; ssaReg < szSsaRegs; ssaReg++) {
+ if (ssaRegsMapped.get(ssaReg)) {
+ // We already did this one above
+ continue;
+ }
+
+ int paramIndex = getParameterIndexForReg(ssaReg);
+
+ RegisterSpec ssaSpec = getDefinitionSpecForSsaReg(ssaReg);
+ if (paramIndex >= 0) {
+ addMapping(ssaSpec, paramIndex);
+ }
+ }
+ }
+
+ /**
+ * Handles all insns that want a register range for their sources.
+ */
+ private void handleInvokeRangeInsns() {
+ for (NormalSsaInsn insn : invokeRangeInsns) {
+ adjustAndMapSourceRangeRange(insn);
+ }
+ }
+
+ /**
+ * Handles check cast results to reuse the same source register.
+ * Inserts a move if it can't map the same register to both and the
+ * check cast is not caught.
+ */
+ private void handleCheckCastResults() {
+ for (NormalSsaInsn insn : moveResultPseudoInsns) {
+ RegisterSpec moveRegSpec = insn.getResult();
+ int moveReg = moveRegSpec.getReg();
+ BitSet predBlocks = insn.getBlock().getPredecessors();
+
+ // Expect one predecessor block only
+ if (predBlocks.cardinality() != 1) {
+ continue;
+ }
+
+ SsaBasicBlock predBlock =
+ ssaMeth.getBlocks().get(predBlocks.nextSetBit(0));
+ ArrayList<SsaInsn> insnList = predBlock.getInsns();
+
+ /**
+ * If the predecessor block has a check-cast, it will be the last
+ * instruction
+ */
+ SsaInsn checkCastInsn = insnList.get(insnList.size() - 1);
+ if (checkCastInsn.getOpcode().getOpcode() != RegOps.CHECK_CAST) {
+ continue;
+ }
+
+ RegisterSpec checkRegSpec = checkCastInsn.getSources().get(0);
+ int checkReg = checkRegSpec.getReg();
+
+ /**
+ * See if either register is already mapped. Most likely the move
+ * result will be mapped already since the cast result is stored
+ * in a local variable.
+ */
+ int category = checkRegSpec.getCategory();
+ boolean moveMapped = ssaRegsMapped.get(moveReg);
+ boolean checkMapped = ssaRegsMapped.get(checkReg);
+ if (moveMapped & !checkMapped) {
+ int moveRopReg = mapper.oldToNew(moveReg);
+ checkMapped = tryMapReg(checkRegSpec, moveRopReg, category);
+ }
+ if (checkMapped & !moveMapped) {
+ int checkRopReg = mapper.oldToNew(checkReg);
+ moveMapped = tryMapReg(moveRegSpec, checkRopReg, category);
+ }
+
+ // Map any unmapped registers to anything available
+ if (!moveMapped || !checkMapped) {
+ int ropReg = findNextUnreservedRopReg(paramRangeEnd, category);
+ ArrayList<RegisterSpec> ssaRegs =
+ new ArrayList<RegisterSpec>(2);
+ ssaRegs.add(moveRegSpec);
+ ssaRegs.add(checkRegSpec);
+
+ while (!tryMapRegs(ssaRegs, ropReg, category, false)) {
+ ropReg = findNextUnreservedRopReg(ropReg + 1, category);
+ }
+ }
+
+ /*
+ * If source and result have a different mapping, insert a move so
+ * they can have the same mapping. Don't do this if the check cast
+ * is caught, since it will overwrite a potentially live value.
+ */
+ boolean hasExceptionHandlers =
+ checkCastInsn.getOriginalRopInsn().getCatches().size() != 0;
+ int moveRopReg = mapper.oldToNew(moveReg);
+ int checkRopReg = mapper.oldToNew(checkReg);
+ if (moveRopReg != checkRopReg && !hasExceptionHandlers) {
+ ((NormalSsaInsn) checkCastInsn).changeOneSource(0,
+ insertMoveBefore(checkCastInsn, checkRegSpec));
+ addMapping(checkCastInsn.getSources().get(0), moveRopReg);
+ }
+ }
+ }
+
+ /**
+ * Handles all phi instructions, trying to map them to a common register.
+ */
+ private void handlePhiInsns() {
+ for (PhiInsn insn : phiInsns) {
+ processPhiInsn(insn);
+ }
+ }
+
+ /**
+ * Maps all non-parameter, non-local variable registers.
+ */
+ private void handleNormalUnassociated() {
+ int szSsaRegs = ssaMeth.getRegCount();
+
+ for (int ssaReg = 0; ssaReg < szSsaRegs; ssaReg++) {
+ if (ssaRegsMapped.get(ssaReg)) {
+ // We already did this one
+ continue;
+ }
+
+ RegisterSpec ssaSpec = getDefinitionSpecForSsaReg(ssaReg);
+
+ if (ssaSpec == null) continue;
+
+ int category = ssaSpec.getCategory();
+ // Find a rop reg that does not interfere
+ int ropReg = findNextUnreservedRopReg(paramRangeEnd, category);
+ while (!canMapReg(ssaSpec, ropReg)) {
+ ropReg = findNextUnreservedRopReg(ropReg + 1, category);
+ }
+
+ addMapping(ssaSpec, ropReg);
+ }
+ }
+
+ /**
+ * Checks to see if a list of SSA registers can all be mapped into
+ * the same rop reg. Ignores registers that have already been mapped,
+ * and checks the interference graph and ensures the range does not
+ * cross the parameter range.
+ *
+ * @param specs {@code non-null;} SSA registers to check
+ * @param ropReg {@code >=0;} rop register to check mapping to
+ * @return {@code true} if all unmapped registers can be mapped
+ */
+ private boolean canMapRegs(ArrayList<RegisterSpec> specs, int ropReg) {
+ for (RegisterSpec spec : specs) {
+ if (ssaRegsMapped.get(spec.getReg())) continue;
+ if (!canMapReg(spec, ropReg)) return false;
+ }
+ return true;
+ }
+
+ /**
+ * Checks to see if {@code ssaSpec} can be mapped to
+ * {@code ropReg}. Checks interference graph and ensures
+ * the range does not cross the parameter range.
+ *
+ * @param ssaSpec {@code non-null;} SSA spec
+ * @param ropReg prosepctive new-namespace reg
+ * @return {@code true} if mapping is possible
+ */
+ private boolean canMapReg(RegisterSpec ssaSpec, int ropReg) {
+ int category = ssaSpec.getCategory();
+ return !(spansParamRange(ropReg, category)
+ || mapper.interferes(ssaSpec, ropReg));
+ }
+
+ /**
+ * Returns true if the specified rop register + category
+ * will cross the boundry between the lower {@code paramWidth}
+ * registers reserved for method params and the upper registers. We cannot
+ * allocate a register that spans the param block and the normal block,
+ * because we will be moving the param block to high registers later.
+ *
+ * @param ssaReg register in new namespace
+ * @param category width that the register will have
+ * @return {@code true} in the case noted above
+ */
+ private boolean spansParamRange(int ssaReg, int category) {
+ return ((ssaReg < paramRangeEnd)
+ && ((ssaReg + category) > paramRangeEnd));
+ }
+
+ /**
+ * Analyze each instruction and find out all the local variable assignments
+ * and move-result-pseudo/invoke-range instrucitons.
+ */
+ private void analyzeInstructions() {
+ ssaMeth.forEachInsn(new SsaInsn.Visitor() {
+ /** {@inheritDoc} */
+ public void visitMoveInsn(NormalSsaInsn insn) {
+ processInsn(insn);
+ }
+
+ /** {@inheritDoc} */
+ public void visitPhiInsn(PhiInsn insn) {
+ processInsn(insn);
+ }
+
+ /** {@inheritDoc} */
+ public void visitNonMoveInsn(NormalSsaInsn insn) {
+ processInsn(insn);
+ }
+
+ /**
+ * This method collects three types of instructions:
+ *
+ * 1) Adds a local variable assignment to the
+ * {@code localVariables} map.
+ * 2) Add move-result-pseudo to the
+ * {@code moveResultPseudoInsns} list.
+ * 3) Add invoke-range to the
+ * {@code invokeRangeInsns} list.
+ *
+ * @param insn {@code non-null;} insn that may represent a
+ * local variable assignment
+ */
+ private void processInsn(SsaInsn insn) {
+ RegisterSpec assignment;
+ assignment = insn.getLocalAssignment();
+
+ if (assignment != null) {
+ LocalItem local = assignment.getLocalItem();
+
+ ArrayList<RegisterSpec> regList
+ = localVariables.get(local);
+
+ if (regList == null) {
+ regList = new ArrayList<RegisterSpec>();
+ localVariables.put(local, regList);
+ }
+
+ regList.add(assignment);
+ }
+
+ if (insn instanceof NormalSsaInsn) {
+ if (insn.getOpcode().getOpcode() ==
+ RegOps.MOVE_RESULT_PSEUDO) {
+ moveResultPseudoInsns.add((NormalSsaInsn) insn);
+ } else if (Optimizer.getAdvice().requiresSourcesInOrder(
+ insn.getOriginalRopInsn().getOpcode(),
+ insn.getSources())) {
+ invokeRangeInsns.add((NormalSsaInsn) insn);
+ }
+ } else if (insn instanceof PhiInsn) {
+ phiInsns.add((PhiInsn) insn);
+ }
+
+ }
+ });
+ }
+
+ /**
+ * Adds a mapping from an SSA register to a rop register.
+ * {@link #canMapReg} should have already been called.
+ *
+ * @param ssaSpec {@code non-null;} SSA register to map from
+ * @param ropReg {@code >=0;} rop register to map to
+ */
+ private void addMapping(RegisterSpec ssaSpec, int ropReg) {
+ int ssaReg = ssaSpec.getReg();
+
+ // An assertion.
+ if (ssaRegsMapped.get(ssaReg) || !canMapReg(ssaSpec, ropReg)) {
+ throw new RuntimeException(
+ "attempt to add invalid register mapping");
+ }
+
+ if (DEBUG) {
+ System.out.printf("Add mapping s%d -> v%d c:%d\n",
+ ssaSpec.getReg(), ropReg, ssaSpec.getCategory());
+ }
+
+ int category = ssaSpec.getCategory();
+ mapper.addMapping(ssaSpec.getReg(), ropReg, category);
+ ssaRegsMapped.set(ssaReg);
+ usedRopRegs.set(ropReg, ropReg + category);
+ }
+
+
+ /**
+ * Maps the source registers of the specified instruction such that they
+ * will fall in a contiguous range in rop form. Moves are inserted as
+ * necessary to allow the range to be allocated.
+ *
+ * @param insn {@code non-null;} insn whos sources to process
+ */
+ private void adjustAndMapSourceRangeRange(NormalSsaInsn insn) {
+ int newRegStart = findRangeAndAdjust(insn);
+
+ RegisterSpecList sources = insn.getSources();
+ int szSources = sources.size();
+ int nextRopReg = newRegStart;
+
+ for (int i = 0; i < szSources; i++) {
+ RegisterSpec source = sources.get(i);
+ int sourceReg = source.getReg();
+ int category = source.getCategory();
+ int curRopReg = nextRopReg;
+ nextRopReg += category;
+
+ if (ssaRegsMapped.get(sourceReg)) {
+ continue;
+ }
+
+ LocalItem localItem = getLocalItemForReg(sourceReg);
+ addMapping(source, curRopReg);
+
+ if (localItem != null) {
+ markReserved(curRopReg, category);
+ ArrayList<RegisterSpec> similarRegisters
+ = localVariables.get(localItem);
+
+ int szSimilar = similarRegisters.size();
+
+ /*
+ * Try to map all SSA registers also associated with
+ * this local.
+ */
+ for (int j = 0; j < szSimilar; j++) {
+ RegisterSpec similarSpec = similarRegisters.get(j);
+ int similarReg = similarSpec.getReg();
+
+ // Don't map anything that's also a source.
+ if (-1 != sources.indexOfRegister(similarReg)) {
+ continue;
+ }
+
+ // Registers left unmapped will get handled later.
+ tryMapReg(similarSpec, curRopReg, category);
+ }
+ }
+ }
+ }
+
+ /**
+ * Find a contiguous rop register range that fits the specified
+ * instruction's sources. First, try to center the range around
+ * sources that have already been mapped to rop registers. If that fails,
+ * just find a new contiguous range that doesn't interfere.
+ *
+ * @param insn {@code non-null;} the insn whose sources need to
+ * fit. Must be last insn in basic block.
+ * @return {@code >= 0;} rop register of start of range
+ */
+ private int findRangeAndAdjust(NormalSsaInsn insn) {
+ RegisterSpecList sources = insn.getSources();
+ int szSources = sources.size();
+ // the category for each source index
+ int categoriesForIndex[] = new int[szSources];
+ int rangeLength = 0;
+
+ // Compute rangeLength and categoriesForIndex
+ for (int i = 0; i < szSources; i++) {
+ int category = sources.get(i).getCategory();
+ categoriesForIndex[i] = category;
+ rangeLength += categoriesForIndex[i];
+ }
+
+ // the highest score of fits tried so far
+ int maxScore = Integer.MIN_VALUE;
+ // the high scoring range's start
+ int resultRangeStart = -1;
+ // by source index: set of sources needing moves in high scoring plan
+ BitSet resultMovesRequired = null;
+
+ /*
+ * First, go through each source that's already been mapped. Try
+ * to center the range around the rop register this source is mapped
+ * to.
+ */
+ int rangeStartOffset = 0;
+ for (int i = 0; i < szSources; i++) {
+ int ssaCenterReg = sources.get(i).getReg();
+
+ if (i != 0) {
+ rangeStartOffset -= categoriesForIndex[i - 1];
+ }
+ if (!ssaRegsMapped.get(ssaCenterReg)) {
+ continue;
+ }
+
+ int rangeStart = mapper.oldToNew(ssaCenterReg) + rangeStartOffset;
+
+ if (rangeStart < 0 || spansParamRange(rangeStart, rangeLength)) {
+ continue;
+ }
+
+ BitSet curMovesRequired = new BitSet(szSources);
+
+ int fitWidth
+ = fitPlanForRange(rangeStart, insn, categoriesForIndex,
+ curMovesRequired);
+
+ if (fitWidth < 0) {
+ continue;
+ }
+
+ int score = fitWidth - curMovesRequired.cardinality();
+
+ if (score > maxScore) {
+ maxScore = score;
+ resultRangeStart = rangeStart;
+ resultMovesRequired = curMovesRequired;
+ }
+
+ if (fitWidth == rangeLength) {
+ // We can't do any better than this, so stop here
+ break;
+ }
+ }
+
+ /*
+ * If we were unable to find a plan for a fit centered around
+ * an already-mapped source, just try to find a range of
+ * registers we can move the range into.
+ */
+
+ if (resultRangeStart == -1) {
+ resultMovesRequired = new BitSet(szSources);
+
+ resultRangeStart = findAnyFittingRange(insn, rangeLength,
+ categoriesForIndex, resultMovesRequired);
+ }
+
+ /*
+ * Now, insert any moves required.
+ */
+
+ for (int i = resultMovesRequired.nextSetBit(0); i >= 0;
+ i = resultMovesRequired.nextSetBit(i+1)) {
+ insn.changeOneSource(i, insertMoveBefore(insn, sources.get(i)));
+ }
+
+ return resultRangeStart;
+ }
+
+ /**
+ * Finds an unreserved range that will fit the sources of the
+ * specified instruction. Does not bother trying to center the range
+ * around an already-mapped source register;
+ *
+ * @param insn {@code non-null;} insn to build range for
+ * @param rangeLength {@code >=0;} length required in register units
+ * @param categoriesForIndex {@code non-null;} indexed by source index;
+ * the category for each source
+ * @param outMovesRequired {@code non-null;} an output parameter indexed by
+ * source index that will contain the set of sources which need
+ * moves inserted
+ * @return the rop register that starts the fitting range
+ */
+ private int findAnyFittingRange(NormalSsaInsn insn, int rangeLength,
+ int[] categoriesForIndex, BitSet outMovesRequired) {
+ int rangeStart = paramRangeEnd;
+ while (true) {
+ rangeStart = findNextUnreservedRopReg(rangeStart, rangeLength);
+ int fitWidth
+ = fitPlanForRange(rangeStart, insn,
+ categoriesForIndex, outMovesRequired);
+
+ if (fitWidth >= 0) {
+ break;
+ }
+ rangeStart++;
+ outMovesRequired.clear();
+ }
+ return rangeStart;
+ }
+
+ /**
+ * Attempts to build a plan for fitting a range of sources into rop
+ * registers.
+ *
+ * @param ropReg {@code >= 0;} rop reg that begins range
+ * @param insn {@code non-null;} insn to plan range for
+ * @param categoriesForIndex {@code non-null;} indexed by source index;
+ * the category for each source
+ * @param outMovesRequired {@code non-null;} an output parameter indexed by
+ * source index that will contain the set of sources which need
+ * moves inserted
+ * @return the width of the fit that that does not involve added moves or
+ * {@code -1} if "no fit possible"
+ */
+ private int fitPlanForRange(int ropReg, NormalSsaInsn insn,
+ int[] categoriesForIndex, BitSet outMovesRequired) {
+ RegisterSpecList sources = insn.getSources();
+ int szSources = sources.size();
+ int fitWidth = 0;
+ IntSet liveOut = insn.getBlock().getLiveOutRegs();
+ RegisterSpecList liveOutSpecs = ssaSetToSpecs(liveOut);
+
+ // An SSA reg may only be mapped into a range once.
+ BitSet seen = new BitSet(ssaMeth.getRegCount());
+
+ for (int i = 0; i < szSources ; i++) {
+ RegisterSpec ssaSpec = sources.get(i);
+ int ssaReg = ssaSpec.getReg();
+ int category = categoriesForIndex[i];
+
+ if (i != 0) {
+ ropReg += categoriesForIndex[i-1];
+ }
+
+ if (ssaRegsMapped.get(ssaReg)
+ && mapper.oldToNew(ssaReg) == ropReg) {
+ // This is a register that is already mapped appropriately.
+ fitWidth += category;
+ } else if (rangeContainsReserved(ropReg, category)) {
+ fitWidth = -1;
+ break;
+ } else if (!ssaRegsMapped.get(ssaReg)
+ && canMapReg(ssaSpec, ropReg)
+ && !seen.get(ssaReg)) {
+ // This is a register that can be mapped appropriately.
+ fitWidth += category;
+ } else if (!mapper.areAnyPinned(liveOutSpecs, ropReg, category)
+ && !mapper.areAnyPinned(sources, ropReg, category)) {
+ /*
+ * This is a source that can be moved. We can insert a
+ * move as long as:
+ *
+ * * no SSA register pinned to the desired rop reg
+ * is live out on the block
+ *
+ * * no SSA register pinned to desired rop reg is
+ * a source of this insn (since this may require
+ * overlapping moves, which we can't presently handle)
+ */
+
+ outMovesRequired.set(i);
+ } else {
+ fitWidth = -1;
+ break;
+ }
+
+ seen.set(ssaReg);
+ }
+ return fitWidth;
+ }
+
+ /**
+ * Converts a bit set of SSA registers into a RegisterSpecList containing
+ * the definition specs of all the registers.
+ *
+ * @param ssaSet {@code non-null;} set of SSA registers
+ * @return list of RegisterSpecs as noted above
+ */
+ RegisterSpecList ssaSetToSpecs(IntSet ssaSet) {
+ RegisterSpecList result = new RegisterSpecList(ssaSet.elements());
+
+ IntIterator iter = ssaSet.iterator();
+
+ int i = 0;
+ while (iter.hasNext()) {
+ result.set(i++, getDefinitionSpecForSsaReg(iter.next()));
+ }
+
+ return result;
+ }
+
+ /**
+ * Gets a local item associated with an ssa register, if one exists.
+ *
+ * @param ssaReg {@code >= 0;} SSA register
+ * @return {@code null-ok;} associated local item or null
+ */
+ private LocalItem getLocalItemForReg(int ssaReg) {
+ for (Map.Entry<LocalItem, ArrayList<RegisterSpec>> entry :
+ localVariables.entrySet()) {
+ for (RegisterSpec spec : entry.getValue()) {
+ if (spec.getReg() == ssaReg) {
+ return entry.getKey();
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Attempts to map the sources and result of a phi to a common register.
+ * Will try existing mappings first, from most to least common. If none
+ * of the registers have mappings yet, a new mapping is created.
+ */
+ private void processPhiInsn(PhiInsn insn) {
+ RegisterSpec result = insn.getResult();
+ int resultReg = result.getReg();
+ int category = result.getCategory();
+
+ RegisterSpecList sources = insn.getSources();
+ int sourcesSize = sources.size();
+
+ // List of phi sources / result that need mapping
+ ArrayList<RegisterSpec> ssaRegs = new ArrayList<RegisterSpec>();
+
+ // Track how many times a particular mapping is found
+ Multiset mapSet = new Multiset(sourcesSize + 1);
+
+ /*
+ * If the result of the phi has an existing mapping, get it.
+ * Otherwise, add it to the list of regs that need mapping.
+ */
+ if (ssaRegsMapped.get(resultReg)) {
+ mapSet.add(mapper.oldToNew(resultReg));
+ } else {
+ ssaRegs.add(result);
+ }
+
+ for (int i = 0; i < sourcesSize; i++) {
+ RegisterSpec source = sources.get(i);
+ SsaInsn def = ssaMeth.getDefinitionForRegister(source.getReg());
+ RegisterSpec sourceDef = def.getResult();
+ int sourceReg = sourceDef.getReg();
+
+ /*
+ * If a source of the phi has an existing mapping, get it.
+ * Otherwise, add it to the list of regs that need mapping.
+ */
+ if (ssaRegsMapped.get(sourceReg)) {
+ mapSet.add(mapper.oldToNew(sourceReg));
+ } else {
+ ssaRegs.add(sourceDef);
+ }
+ }
+
+ // Try all existing mappings, with the most common ones first
+ for (int i = 0; i < mapSet.getSize(); i++) {
+ int maxReg = mapSet.getAndRemoveHighestCount();
+ tryMapRegs(ssaRegs, maxReg, category, false);
+ }
+
+ // Map any remaining unmapped regs with whatever fits
+ int mapReg = findNextUnreservedRopReg(paramRangeEnd, category);
+ while (!tryMapRegs(ssaRegs, mapReg, category, false)) {
+ mapReg = findNextUnreservedRopReg(mapReg + 1, category);
+ }
+ }
+
+ // A set that tracks how often elements are added to it.
+ private static class Multiset {
+ private final int[] reg;
+ private final int[] count;
+ private int size;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param maxSize the maximum distinct elements the set may have
+ */
+ public Multiset(int maxSize) {
+ reg = new int[maxSize];
+ count = new int[maxSize];
+ size = 0;
+ }
+
+ /**
+ * Adds an element to the set.
+ *
+ * @param element element to add
+ */
+ public void add(int element) {
+ for (int i = 0; i < size; i++) {
+ if (reg[i] == element) {
+ count[i]++;
+ return;
+ }
+ }
+
+ reg[size] = element;
+ count[size] = 1;
+ size++;
+ }
+
+ /**
+ * Searches the set for the element that has been added the most.
+ * In the case of a tie, the element that was added first is returned.
+ * Then, it clears the count on that element. The size of the set
+ * remains unchanged.
+ *
+ * @return element with the highest count
+ */
+ public int getAndRemoveHighestCount() {
+ int maxIndex = -1;
+ int maxReg = -1;
+ int maxCount = 0;
+
+ for (int i = 0; i < size; i++) {
+ if (maxCount < count[i]) {
+ maxIndex = i;
+ maxReg = reg[i];
+ maxCount = count[i];
+ }
+ }
+
+ count[maxIndex] = 0;
+ return maxReg;
+ }
+
+ /**
+ * Gets the number of distinct elements in the set.
+ *
+ * @return size of the set
+ */
+ public int getSize() {
+ return size;
+ }
+ }
+}
diff --git a/dx/src/com/android/jack/dx/ssa/back/IdenticalBlockCombiner.java b/dx/src/com/android/jack/dx/ssa/back/IdenticalBlockCombiner.java
new file mode 100644
index 00000000..c679d61e
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/back/IdenticalBlockCombiner.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa.back;
+
+import com.android.jack.dx.rop.code.BasicBlock;
+import com.android.jack.dx.rop.code.BasicBlockList;
+import com.android.jack.dx.rop.code.CstInsn;
+import com.android.jack.dx.rop.code.Insn;
+import com.android.jack.dx.rop.code.InsnList;
+import com.android.jack.dx.rop.code.RegOps;
+import com.android.jack.dx.rop.code.RopMethod;
+import com.android.jack.dx.rop.code.SwitchInsn;
+import com.android.jack.dx.util.IntList;
+
+import java.util.BitSet;
+
+/**
+ * Searches for basic blocks that all have the same successor and insns
+ * but different predecessors. These blocks are then combined into a single
+ * block and the now-unused blocks are deleted. These identical blocks
+ * frequently are created when catch blocks are edge-split.
+ */
+public class IdenticalBlockCombiner {
+ private final RopMethod ropMethod;
+ private final BasicBlockList blocks;
+ private final BasicBlockList newBlocks;
+
+ /**
+ * Constructs instance. Call {@code process()} to run.
+ *
+ * @param rm {@code non-null;} instance to process
+ */
+ public IdenticalBlockCombiner(RopMethod rm) {
+ ropMethod = rm;
+ blocks = ropMethod.getBlocks();
+ newBlocks = blocks.getMutableCopy();
+ }
+
+ /**
+ * Runs algorithm. TODO: This is n^2, and could be made linear-ish with
+ * a hash. In particular, hash the contents of each block and only
+ * compare blocks with the same hash.
+ *
+ * @return {@code non-null;} new method that has been processed
+ */
+ public RopMethod process() {
+ int szBlocks = blocks.size();
+ // indexed by label
+ BitSet toDelete = new BitSet(blocks.getMaxLabel());
+
+ // For each non-deleted block...
+ for (int bindex = 0; bindex < szBlocks; bindex++) {
+ BasicBlock b = blocks.get(bindex);
+
+ if (toDelete.get(b.getLabel())) {
+ // doomed block
+ continue;
+ }
+
+ IntList preds = ropMethod.labelToPredecessors(b.getLabel());
+
+ // ...look at all of it's predecessors that have only one succ...
+ int szPreds = preds.size();
+ for (int i = 0; i < szPreds; i++) {
+ int iLabel = preds.get(i);
+
+ BasicBlock iBlock = blocks.labelToBlock(iLabel);
+
+ if (toDelete.get(iLabel)
+ || iBlock.getSuccessors().size() > 1
+ || iBlock.getFirstInsn().getOpcode().getOpcode() ==
+ RegOps.MOVE_RESULT) {
+ continue;
+ }
+
+ IntList toCombine = new IntList();
+
+ // ...and see if they can be combined with any other preds...
+ for (int j = i + 1; j < szPreds; j++) {
+ int jLabel = preds.get(j);
+ BasicBlock jBlock = blocks.labelToBlock(jLabel);
+
+ if (jBlock.getSuccessors().size() == 1
+ && compareInsns(iBlock, jBlock)) {
+
+ toCombine.add(jLabel);
+ toDelete.set(jLabel);
+ }
+ }
+
+ combineBlocks(iLabel, toCombine);
+ }
+ }
+
+ for (int i = szBlocks - 1; i >= 0; i--) {
+ if (toDelete.get(newBlocks.get(i).getLabel())) {
+ newBlocks.set(i, null);
+ }
+ }
+
+ newBlocks.shrinkToFit();
+ newBlocks.setImmutable();
+
+ return new RopMethod(newBlocks, ropMethod.getFirstLabel());
+ }
+
+ /**
+ * Helper method to compare the contents of two blocks.
+ *
+ * @param a {@code non-null;} a block to compare
+ * @param b {@code non-null;} another block to compare
+ * @return {@code true} iff the two blocks' instructions are the same
+ */
+ private static boolean compareInsns(BasicBlock a, BasicBlock b) {
+ return a.getInsns().contentEquals(b.getInsns());
+ }
+
+ /**
+ * Combines blocks proven identical into one alpha block, re-writing
+ * all of the successor links that point to the beta blocks to point
+ * to the alpha block instead.
+ *
+ * @param alphaLabel block that will replace all the beta block
+ * @param betaLabels label list of blocks to combine
+ */
+ private void combineBlocks(int alphaLabel, IntList betaLabels) {
+ int szBetas = betaLabels.size();
+
+ for (int i = 0; i < szBetas; i++) {
+ int betaLabel = betaLabels.get(i);
+ BasicBlock bb = blocks.labelToBlock(betaLabel);
+ IntList preds = ropMethod.labelToPredecessors(bb.getLabel());
+ int szPreds = preds.size();
+
+ for (int j = 0; j < szPreds; j++) {
+ BasicBlock predBlock = newBlocks.labelToBlock(preds.get(j));
+ replaceSucc(predBlock, betaLabel, alphaLabel);
+ }
+ }
+ }
+
+ /**
+ * Replaces one of a block's successors with a different label. Constructs
+ * an updated BasicBlock instance and places it in {@code newBlocks}.
+ *
+ * @param block block to replace
+ * @param oldLabel label of successor to replace
+ * @param newLabel label of new successor
+ */
+ private void replaceSucc(BasicBlock block, int oldLabel, int newLabel) {
+ IntList newSuccessors = block.getSuccessors().mutableCopy();
+ int newPrimarySuccessor;
+
+ newSuccessors.set(newSuccessors.indexOf(oldLabel), newLabel);
+ newPrimarySuccessor = block.getPrimarySuccessor();
+
+ if (newPrimarySuccessor == oldLabel) {
+ newPrimarySuccessor = newLabel;
+ }
+
+ newSuccessors.setImmutable();
+
+ BasicBlock newBB = new BasicBlock(block.getLabel(),
+ block.getInsns(), newSuccessors, newPrimarySuccessor);
+
+ newBlocks.set(newBlocks.indexOfLabel(block.getLabel()), newBB);
+ }
+}
diff --git a/dx/src/com/android/jack/dx/ssa/back/InterferenceGraph.java b/dx/src/com/android/jack/dx/ssa/back/InterferenceGraph.java
new file mode 100644
index 00000000..00a2c699
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/back/InterferenceGraph.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa.back;
+
+import com.android.jack.dx.rop.code.RegisterSpec;
+import com.android.jack.dx.ssa.PhiInsn;
+import com.android.jack.dx.ssa.SetFactory;
+import com.android.jack.dx.ssa.SsaBasicBlock;
+import com.android.jack.dx.ssa.SsaInsn;
+import com.android.jack.dx.ssa.SsaMethod;
+import com.android.jack.dx.util.BitIntSet;
+import com.android.jack.dx.util.IntSet;
+import com.android.jack.dx.util.ListIntSet;
+
+import java.util.BitSet;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * A register interference graph
+ */
+public class InterferenceGraph {
+ /**
+ * {@code non-null;} interference graph, indexed by register in
+ * both dimensions
+ */
+ private final ArrayList<IntSet> interference;
+
+ /**
+ * Creates a new graph.
+ *
+ * @param countRegs {@code >= 0;} the start count of registers in
+ * the namespace. New registers can be added subsequently.
+ */
+ public InterferenceGraph(int countRegs) {
+ interference = new ArrayList<IntSet>(countRegs);
+
+ for (int i = 0; i < countRegs; i++) {
+ interference.add(SetFactory.makeInterferenceSet(countRegs));
+ }
+ }
+
+ /**
+ * Adds a register pair to the interference/liveness graph. Parameter
+ * order is insignificant.
+ *
+ * @param regV one register index
+ * @param regW another register index
+ */
+ public void add(int regV, int regW) {
+ ensureCapacity(Math.max(regV, regW) + 1);
+
+ interference.get(regV).add(regW);
+ interference.get(regW).add(regV);
+ }
+
+ /**
+ * Dumps interference graph to stdout for debugging.
+ */
+ public void dumpToStdout() {
+ int oldRegCount = interference.size();
+
+ for (int i = 0; i < oldRegCount; i++) {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("Reg " + i + ":" + interference.get(i).toString());
+
+ System.out.println(sb.toString());
+ }
+ }
+
+ /**
+ * Merges the interference set for a register into a given bit set
+ *
+ * @param reg {@code >= 0;} register
+ * @param set {@code non-null;} interference set; will be merged
+ * with set for given register
+ */
+ public void mergeInterferenceSet(int reg, IntSet set) {
+ if (reg < interference.size()) {
+ set.merge(interference.get(reg));
+ }
+ }
+
+ /**
+ * Ensures that the interference graph is appropriately sized.
+ *
+ * @param size requested minumum size
+ */
+ private void ensureCapacity(int size) {
+ int countRegs = interference.size();
+
+ interference.ensureCapacity(size);
+
+ for (int i = countRegs; i < size; i++) {
+ interference.add(SetFactory.makeInterferenceSet(size));
+ }
+ }
+}
diff --git a/dx/src/com/android/jack/dx/ssa/back/LivenessAnalyzer.java b/dx/src/com/android/jack/dx/ssa/back/LivenessAnalyzer.java
new file mode 100644
index 00000000..44c9964c
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/back/LivenessAnalyzer.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa.back;
+
+import com.android.jack.dx.rop.code.RegisterSpec;
+import com.android.jack.dx.ssa.PhiInsn;
+import com.android.jack.dx.ssa.SsaBasicBlock;
+import com.android.jack.dx.ssa.SsaInsn;
+import com.android.jack.dx.ssa.SsaMethod;
+
+import java.util.BitSet;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * From Appel "Modern Compiler Implementation in Java" algorithm 19.17
+ * Calculate the live ranges for register {@code reg}.<p>
+ *
+ * v = regV <p>
+ * s = insn <p>
+ * M = visitedBlocks <p>
+ */
+public class LivenessAnalyzer {
+ /**
+ * {@code non-null;} index by basic block indexed set of basic blocks
+ * that have already been visited. "M" as written in the original Appel
+ * algorithm.
+ */
+ private final BitSet visitedBlocks;
+
+ /**
+ * {@code non-null;} set of blocks remaing to visit as "live out as block"
+ */
+ private final BitSet liveOutBlocks;
+
+ /**
+ * {@code >=0;} SSA register currently being analyzed.
+ * "v" in the original Appel algorithm.
+ */
+ private final int regV;
+
+ /** method to process */
+ private final SsaMethod ssaMeth;
+
+ /** interference graph being updated */
+ private final InterferenceGraph interference;
+
+ /** block "n" in Appel 19.17 */
+ private SsaBasicBlock blockN;
+
+ /** index of statement {@code s} in {@code blockN} */
+ private int statementIndex;
+
+ /** the next function to call */
+ private NextFunction nextFunction;
+
+ /** constants for {@link #nextFunction} */
+ private static enum NextFunction {
+ LIVE_IN_AT_STATEMENT,
+ LIVE_OUT_AT_STATEMENT,
+ LIVE_OUT_AT_BLOCK,
+ DONE;
+ }
+
+ /**
+ * Runs register liveness algorithm for a method, updating the
+ * live in/out information in {@code SsaBasicBlock} instances and
+ * returning an interference graph.
+ *
+ * @param ssaMeth {@code non-null;} method to process
+ * @return {@code non-null;} interference graph indexed by SSA
+ * registers in both directions
+ */
+ public static InterferenceGraph constructInterferenceGraph(
+ SsaMethod ssaMeth) {
+ int szRegs = ssaMeth.getRegCount();
+ InterferenceGraph interference = new InterferenceGraph(szRegs);
+
+ for (int i = 0; i < szRegs; i++) {
+ new LivenessAnalyzer(ssaMeth, i, interference).run();
+ }
+
+ coInterferePhis(ssaMeth, interference);
+
+ return interference;
+ }
+
+ /**
+ * Makes liveness analyzer instance for specific register.
+ *
+ * @param ssaMeth {@code non-null;} method to process
+ * @param reg register whose liveness to analyze
+ * @param interference {@code non-null;} indexed by SSA reg in
+ * both dimensions; graph to update
+ *
+ */
+ private LivenessAnalyzer(SsaMethod ssaMeth, int reg,
+ InterferenceGraph interference) {
+ int blocksSz = ssaMeth.getBlocks().size();
+
+ this.ssaMeth = ssaMeth;
+ this.regV = reg;
+ visitedBlocks = new BitSet(blocksSz);
+ liveOutBlocks = new BitSet(blocksSz);
+ this.interference = interference;
+ }
+
+ /**
+ * The algorithm in Appel is presented in partial tail-recursion
+ * form. Obviously, that's not efficient in java, so this function
+ * serves as the dispatcher instead.
+ */
+ private void handleTailRecursion() {
+ while (nextFunction != NextFunction.DONE) {
+ switch (nextFunction) {
+ case LIVE_IN_AT_STATEMENT:
+ nextFunction = NextFunction.DONE;
+ liveInAtStatement();
+ break;
+
+ case LIVE_OUT_AT_STATEMENT:
+ nextFunction = NextFunction.DONE;
+ liveOutAtStatement();
+ break;
+
+ case LIVE_OUT_AT_BLOCK:
+ nextFunction = NextFunction.DONE;
+ liveOutAtBlock();
+ break;
+
+ default:
+ }
+ }
+ }
+
+ /**
+ * From Appel algorithm 19.17.
+ */
+ public void run() {
+ List<SsaInsn> useList = ssaMeth.getUseListForRegister(regV);
+
+ for (SsaInsn insn : useList) {
+ nextFunction = NextFunction.DONE;
+
+ if (insn instanceof PhiInsn) {
+ // If s is a phi-function with V as it's ith argument.
+ PhiInsn phi = (PhiInsn) insn;
+
+ for (SsaBasicBlock pred :
+ phi.predBlocksForReg(regV, ssaMeth)) {
+ blockN = pred;
+
+ nextFunction = NextFunction.LIVE_OUT_AT_BLOCK;
+ handleTailRecursion();
+ }
+ } else {
+ // Special management for registers of category 2, indeed they use
+ // two 32-bits registers thus there is an implicit interference between
+ // the result register and operands contrary to category 1 where there
+ // is no writing of registers before all operands are read.
+ RegisterSpec resultSpec = insn.getResult();
+ if (resultSpec != null && resultSpec.getCategory() == 2
+ && insn.getSources().specForRegister(regV).getCategory() == 2) {
+ interference.add(regV, resultSpec.getReg());
+ }
+
+ blockN = insn.getBlock();
+ statementIndex = blockN.getInsns().indexOf(insn);
+
+ if (statementIndex < 0) {
+ throw new RuntimeException(
+ "insn not found in it's own block");
+ }
+
+ nextFunction = NextFunction.LIVE_IN_AT_STATEMENT;
+ handleTailRecursion();
+ }
+ }
+
+ int nextLiveOutBlock;
+ while ((nextLiveOutBlock = liveOutBlocks.nextSetBit(0)) >= 0) {
+ blockN = ssaMeth.getBlocks().get(nextLiveOutBlock);
+ liveOutBlocks.clear(nextLiveOutBlock);
+ nextFunction = NextFunction.LIVE_OUT_AT_BLOCK;
+ handleTailRecursion();
+ }
+ }
+
+ /**
+ * "v is live-out at n."
+ */
+ private void liveOutAtBlock() {
+ if (! visitedBlocks.get(blockN.getIndex())) {
+ visitedBlocks.set(blockN.getIndex());
+
+ blockN.addLiveOut(regV);
+
+ ArrayList<SsaInsn> insns;
+
+ insns = blockN.getInsns();
+
+ // Live out at last statement in blockN
+ statementIndex = insns.size() - 1;
+ nextFunction = NextFunction.LIVE_OUT_AT_STATEMENT;
+ }
+ }
+
+ /**
+ * "v is live-in at s."
+ */
+ private void liveInAtStatement() {
+ // if s is the first statement in block N
+ if (statementIndex == 0) {
+ // v is live-in at n
+ blockN.addLiveIn(regV);
+
+ BitSet preds = blockN.getPredecessors();
+
+ liveOutBlocks.or(preds);
+ } else {
+ // Let s' be the statement preceeding s
+ statementIndex -= 1;
+ nextFunction = NextFunction.LIVE_OUT_AT_STATEMENT;
+ }
+ }
+
+ /**
+ * "v is live-out at s."
+ */
+ private void liveOutAtStatement() {
+ SsaInsn statement = blockN.getInsns().get(statementIndex);
+ RegisterSpec rs = statement.getResult();
+
+ if (!statement.isResultReg(regV)) {
+ if (rs != null) {
+ interference.add(regV, rs.getReg());
+ }
+ nextFunction = NextFunction.LIVE_IN_AT_STATEMENT;
+ }
+ }
+
+ /**
+ * Ensures that all the phi result registers for all the phis in the
+ * same basic block interfere with each other. This is needed since
+ * the dead code remover has allowed through "dead-end phis" whose
+ * results are not used except as local assignments. Without this step,
+ * a the result of a dead-end phi might be assigned the same register
+ * as the result of another phi, and the phi removal move scheduler may
+ * generate moves that over-write the live result.
+ *
+ * @param ssaMeth {@code non-null;} method to pricess
+ * @param interference {@code non-null;} interference graph
+ */
+ private static void coInterferePhis(SsaMethod ssaMeth,
+ InterferenceGraph interference) {
+ for (SsaBasicBlock b : ssaMeth.getBlocks()) {
+ List<SsaInsn> phis = b.getPhiInsns();
+
+ int szPhis = phis.size();
+
+ for (int i = 0; i < szPhis; i++) {
+ for (int j = 0; j < szPhis; j++) {
+ if (i == j) {
+ continue;
+ }
+
+ interference.add(phis.get(i).getResult().getReg(),
+ phis.get(j).getResult().getReg());
+ }
+ }
+ }
+ }
+}
diff --git a/dx/src/com/android/jack/dx/ssa/back/NullRegisterAllocator.java b/dx/src/com/android/jack/dx/ssa/back/NullRegisterAllocator.java
new file mode 100644
index 00000000..28b20009
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/back/NullRegisterAllocator.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa.back;
+
+import com.android.jack.dx.ssa.BasicRegisterMapper;
+import com.android.jack.dx.ssa.RegisterMapper;
+import com.android.jack.dx.ssa.SsaMethod;
+
+import java.util.BitSet;
+import java.util.ArrayList;
+
+/**
+ * A register allocator that maps SSA register n to Rop register 2*n,
+ * essentially preserving the original mapping and remaining agnostic
+ * about normal or wide categories. Used for debugging.
+ */
+public class NullRegisterAllocator extends RegisterAllocator {
+ /** {@inheritDoc} */
+ public NullRegisterAllocator(SsaMethod ssaMeth,
+ InterferenceGraph interference) {
+ super(ssaMeth, interference);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean wantsParamsMovedHigh() {
+ // We're not smart enough for this.
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public RegisterMapper allocateRegisters() {
+ int oldRegCount = ssaMeth.getRegCount();
+
+ BasicRegisterMapper mapper = new BasicRegisterMapper(oldRegCount);
+
+ for (int i = 0; i < oldRegCount; i++) {
+ mapper.addMapping(i, i*2, 2);
+ }
+
+ return mapper;
+ }
+}
diff --git a/dx/src/com/android/jack/dx/ssa/back/RegisterAllocator.java b/dx/src/com/android/jack/dx/ssa/back/RegisterAllocator.java
new file mode 100644
index 00000000..e81a9d68
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/back/RegisterAllocator.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa.back;
+
+import com.android.jack.dx.rop.code.PlainInsn;
+import com.android.jack.dx.rop.code.RegOps;
+import com.android.jack.dx.rop.code.RegisterSpec;
+import com.android.jack.dx.rop.code.RegisterSpecList;
+import com.android.jack.dx.rop.code.Rops;
+import com.android.jack.dx.rop.code.SourcePosition;
+import com.android.jack.dx.ssa.NormalSsaInsn;
+import com.android.jack.dx.ssa.RegisterMapper;
+import com.android.jack.dx.ssa.SsaBasicBlock;
+import com.android.jack.dx.ssa.SsaInsn;
+import com.android.jack.dx.ssa.SsaMethod;
+import com.android.jack.dx.util.IntIterator;
+import com.android.jack.dx.util.IntSet;
+
+import java.util.BitSet;
+import java.util.ArrayList;
+
+/**
+ * Base class of all register allocators.
+ */
+public abstract class RegisterAllocator {
+ /** method being processed */
+ protected final SsaMethod ssaMeth;
+
+ /** interference graph, indexed by register in both dimensions */
+ protected final InterferenceGraph interference;
+
+ /**
+ * Creates an instance. Call {@code allocateRegisters} to run.
+ * @param ssaMeth method to process.
+ * @param interference Interference graph, indexed by register in both
+ * dimensions.
+ */
+ public RegisterAllocator(SsaMethod ssaMeth,
+ InterferenceGraph interference) {
+ this.ssaMeth = ssaMeth;
+ this.interference = interference;
+ }
+
+ /**
+ * Indicates whether the method params were allocated at the bottom
+ * of the namespace, and thus should be moved up to the top of the
+ * namespace after phi removal.
+ *
+ * @return {@code true} if params should be moved from low to high
+ */
+ public abstract boolean wantsParamsMovedHigh();
+
+ /**
+ * Runs the algorithm.
+ *
+ * @return a register mapper to apply to the {@code SsaMethod}
+ */
+ public abstract RegisterMapper allocateRegisters();
+
+ /**
+ * Returns the category (width) of the definition site of the register.
+ * Returns {@code 1} for undefined registers.
+ *
+ * @param reg register
+ * @return {@code 1..2}
+ */
+ protected final int getCategoryForSsaReg(int reg) {
+ SsaInsn definition = ssaMeth.getDefinitionForRegister(reg);
+
+ if (definition == null) {
+ // an undefined reg
+ return 1;
+ } else {
+ return definition.getResult().getCategory();
+ }
+ }
+
+ /**
+ * Returns the RegisterSpec of the definition of the register.
+ *
+ * @param reg {@code >= 0;} SSA register
+ * @return definition spec of the register or null if it is never defined
+ * (for the case of "version 0" SSA registers)
+ */
+ protected final RegisterSpec getDefinitionSpecForSsaReg(int reg) {
+ SsaInsn definition = ssaMeth.getDefinitionForRegister(reg);
+
+ return definition == null ? null : definition.getResult();
+ }
+
+ /**
+ * Returns true if the definition site of this register is a
+ * move-param (ie, this is a method parameter).
+ *
+ * @param reg register in question
+ * @return {@code true} if this is a method parameter
+ */
+ protected boolean isDefinitionMoveParam(int reg) {
+ SsaInsn defInsn = ssaMeth.getDefinitionForRegister(reg);
+
+ if (defInsn instanceof NormalSsaInsn) {
+ NormalSsaInsn ndefInsn = (NormalSsaInsn) defInsn;
+
+ return ndefInsn.getOpcode().getOpcode() == RegOps.MOVE_PARAM;
+ }
+
+ return false;
+ }
+
+ /**
+ * Inserts a move instruction for a specified SSA register before a
+ * specified instruction, creating a new SSA register and adjusting the
+ * interference graph in the process. The insn currently must be the
+ * last insn in a block.
+ *
+ * @param insn {@code non-null;} insn to insert move before, must
+ * be last insn in block
+ * @param reg {@code non-null;} SSA register to duplicate
+ * @return {@code non-null;} spec of new SSA register created by move
+ */
+ protected final RegisterSpec insertMoveBefore(SsaInsn insn,
+ RegisterSpec reg) {
+ SsaBasicBlock block = insn.getBlock();
+ ArrayList<SsaInsn> insns = block.getInsns();
+ int insnIndex = insns.indexOf(insn);
+
+ if (insnIndex < 0) {
+ throw new IllegalArgumentException (
+ "specified insn is not in this block");
+ }
+
+ if (insnIndex != insns.size() - 1) {
+ /*
+ * Presently, the interference updater only works when
+ * adding before the last insn, and the last insn must have no
+ * result
+ */
+ throw new IllegalArgumentException(
+ "Adding move here not supported:" + insn.toHuman());
+ }
+
+ /*
+ * Get new register and make new move instruction.
+ */
+
+ // The new result must not have an associated local variable.
+ RegisterSpec newRegSpec = RegisterSpec.make(ssaMeth.makeNewSsaReg(),
+ reg.getTypeBearer());
+
+ SsaInsn toAdd = SsaInsn.makeFromRop(
+ new PlainInsn(Rops.opMove(newRegSpec.getType()),
+ SourcePosition.NO_INFO, newRegSpec,
+ RegisterSpecList.make(reg)), block);
+
+ insns.add(insnIndex, toAdd);
+
+ int newReg = newRegSpec.getReg();
+
+ /*
+ * Adjust interference graph based on what's live out of the current
+ * block and what's used by the final instruction.
+ */
+
+ IntSet liveOut = block.getLiveOutRegs();
+ IntIterator liveOutIter = liveOut.iterator();
+
+ while (liveOutIter.hasNext()) {
+ interference.add(newReg, liveOutIter.next());
+ }
+
+ // Everything that's a source in the last insn interferes.
+ RegisterSpecList sources = insn.getSources();
+ int szSources = sources.size();
+
+ for (int i = 0; i < szSources; i++) {
+ interference.add(newReg, sources.get(i).getReg());
+ }
+
+ ssaMeth.onInsnsChanged();
+
+ return newRegSpec;
+ }
+}
diff --git a/dx/src/com/android/jack/dx/ssa/back/SsaToRop.java b/dx/src/com/android/jack/dx/ssa/back/SsaToRop.java
new file mode 100644
index 00000000..2c63a55f
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/back/SsaToRop.java
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa.back;
+
+import com.android.jack.dx.rop.code.BasicBlock;
+import com.android.jack.dx.rop.code.BasicBlockList;
+import com.android.jack.dx.rop.code.InsnList;
+import com.android.jack.dx.rop.code.RegisterSpec;
+import com.android.jack.dx.rop.code.RegisterSpecList;
+import com.android.jack.dx.rop.code.Rop;
+import com.android.jack.dx.rop.code.RopMethod;
+import com.android.jack.dx.rop.code.Rops;
+import com.android.jack.dx.ssa.BasicRegisterMapper;
+import com.android.jack.dx.ssa.PhiInsn;
+import com.android.jack.dx.ssa.RegisterMapper;
+import com.android.jack.dx.ssa.SsaBasicBlock;
+import com.android.jack.dx.ssa.SsaInsn;
+import com.android.jack.dx.ssa.SsaMethod;
+import com.android.jack.dx.util.Hex;
+import com.android.jack.dx.util.IntList;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Comparator;
+
+/**
+ * Converts a method in SSA form to ROP form.
+ */
+public class SsaToRop {
+ /** local debug flag */
+ private static final boolean DEBUG = false;
+
+ /** {@code non-null;} method to process */
+ private final SsaMethod ssaMeth;
+
+ /**
+ * {@code true} if the converter should attempt to minimize
+ * the rop-form register count
+ */
+ private final boolean minimizeRegisters;
+
+ /** {@code non-null;} interference graph */
+ private final InterferenceGraph interference;
+
+ /**
+ * Converts a method in SSA form to ROP form.
+ *
+ * @param ssaMeth {@code non-null;} method to process
+ * @param minimizeRegisters {@code true} if the converter should
+ * attempt to minimize the rop-form register count
+ * @return {@code non-null;} rop-form output
+ */
+ public static RopMethod convertToRopMethod(SsaMethod ssaMeth,
+ boolean minimizeRegisters) {
+ return new SsaToRop(ssaMeth, minimizeRegisters).convert();
+ }
+
+ /**
+ * Constructs an instance.
+ *
+ * @param ssaMeth {@code non-null;} method to process
+ * @param minimizeRegisters {@code true} if the converter should
+ * attempt to minimize the rop-form register count
+ */
+ private SsaToRop(SsaMethod ssaMethod, boolean minimizeRegisters) {
+ this.minimizeRegisters = minimizeRegisters;
+ this.ssaMeth = ssaMethod;
+ this.interference =
+ LivenessAnalyzer.constructInterferenceGraph(ssaMethod);
+ }
+
+ /**
+ * Performs the conversion.
+ *
+ * @return {@code non-null;} rop-form output
+ */
+ private RopMethod convert() {
+ if (DEBUG) {
+ interference.dumpToStdout();
+ }
+
+ // These are other allocators for debugging or historical comparison:
+ // allocator = new NullRegisterAllocator(ssaMeth, interference);
+ // allocator = new FirstFitAllocator(ssaMeth, interference);
+
+ RegisterAllocator allocator =
+ new FirstFitLocalCombiningAllocator(ssaMeth, interference,
+ minimizeRegisters);
+
+ RegisterMapper mapper = allocator.allocateRegisters();
+
+ if (DEBUG) {
+ System.out.println("Printing reg map");
+ System.out.println(((BasicRegisterMapper)mapper).toHuman());
+ }
+
+ ssaMeth.setBackMode();
+
+ ssaMeth.mapRegisters(mapper);
+
+ removePhiFunctions();
+
+ if (allocator.wantsParamsMovedHigh()) {
+ moveParametersToHighRegisters();
+ }
+
+ removeEmptyGotos();
+
+ RopMethod ropMethod = new RopMethod(convertBasicBlocks(),
+ ssaMeth.blockIndexToRopLabel(ssaMeth.getEntryBlockIndex()));
+ ropMethod = new IdenticalBlockCombiner(ropMethod).process();
+
+ return ropMethod;
+ }
+
+ /**
+ * Removes all blocks containing only GOTOs from the control flow.
+ * Although much of this work will be done later when converting
+ * from rop to dex, not all simplification cases can be handled
+ * there. Furthermore, any no-op block between the exit block and
+ * blocks containing the real return or throw statements must be
+ * removed.
+ */
+ private void removeEmptyGotos() {
+ final ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks();
+
+ ssaMeth.forEachBlockDepthFirst(false, new SsaBasicBlock.Visitor() {
+ public void visitBlock(SsaBasicBlock b, SsaBasicBlock parent) {
+ ArrayList<SsaInsn> insns = b.getInsns();
+
+ if ((insns.size() == 1)
+ && (insns.get(0).getOpcode() == Rops.GOTO)) {
+ BitSet preds = (BitSet) b.getPredecessors().clone();
+
+ for (int i = preds.nextSetBit(0); i >= 0;
+ i = preds.nextSetBit(i + 1)) {
+ SsaBasicBlock pb = blocks.get(i);
+ pb.replaceSuccessor(b.getIndex(),
+ b.getPrimarySuccessorIndex());
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * See Appel 19.6. To remove the phi instructions in an edge-split
+ * SSA representation we know we can always insert a move in a
+ * predecessor block.
+ */
+ private void removePhiFunctions() {
+ ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks();
+
+ for (SsaBasicBlock block : blocks) {
+ // Add moves in all the pred blocks for each phi insn.
+ block.forEachPhiInsn(new PhiVisitor(blocks));
+
+ // Delete the phi insns.
+ block.removeAllPhiInsns();
+ }
+
+ /*
+ * After all move insns have been added, sort them so they don't
+ * destructively interfere.
+ */
+ for (SsaBasicBlock block : blocks) {
+ block.scheduleMovesFromPhis();
+ }
+ }
+
+ /**
+ * Helper for {@link #removePhiFunctions}: PhiSuccessorUpdater for
+ * adding move instructions to predecessors based on phi insns.
+ */
+ private static class PhiVisitor implements PhiInsn.Visitor {
+ private final ArrayList<SsaBasicBlock> blocks;
+
+ public PhiVisitor(ArrayList<SsaBasicBlock> blocks) {
+ this.blocks = blocks;
+ }
+
+ public void visitPhiInsn(PhiInsn insn) {
+ RegisterSpecList sources = insn.getSources();
+ RegisterSpec result = insn.getResult();
+ int sz = sources.size();
+
+ for (int i = 0; i < sz; i++) {
+ RegisterSpec source = sources.get(i);
+ SsaBasicBlock predBlock = blocks.get(
+ insn.predBlockIndexForSourcesIndex(i));
+
+ predBlock.addMoveToEnd(result, source);
+ }
+ }
+ }
+
+ /**
+ * Moves the parameter registers, which allocateRegisters() places
+ * at the bottom of the frame, up to the top of the frame to match
+ * Dalvik calling convention.
+ */
+ private void moveParametersToHighRegisters() {
+ int paramWidth = ssaMeth.getParamWidth();
+ BasicRegisterMapper mapper
+ = new BasicRegisterMapper(ssaMeth.getRegCount());
+ int regCount = ssaMeth.getRegCount();
+
+ for (int i = 0; i < regCount; i++) {
+ if (i < paramWidth) {
+ mapper.addMapping(i, regCount - paramWidth + i, 1);
+ } else {
+ mapper.addMapping(i, i - paramWidth, 1);
+ }
+ }
+
+ if (DEBUG) {
+ System.out.printf("Moving %d registers from 0 to %d\n",
+ paramWidth, regCount - paramWidth);
+ }
+
+ ssaMeth.mapRegisters(mapper);
+ }
+
+ /**
+ * @return rop-form basic block list
+ */
+ private BasicBlockList convertBasicBlocks() {
+ ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks();
+
+ // Exit block may be null.
+ SsaBasicBlock exitBlock = ssaMeth.getExitBlock();
+
+ ssaMeth.computeReachability();
+ int ropBlockCount = ssaMeth.getCountReachableBlocks();
+
+ // Don't count the exit block, if it exists and is reachable.
+ ropBlockCount -= (exitBlock != null && exitBlock.isReachable()) ? 1 : 0;
+
+ BasicBlockList result = new BasicBlockList(ropBlockCount);
+
+ // Convert all the reachable blocks except the exit block.
+ int ropBlockIndex = 0;
+ for (SsaBasicBlock b : blocks) {
+ if (b.isReachable() && b != exitBlock) {
+ result.set(ropBlockIndex++, convertBasicBlock(b));
+ }
+ }
+
+ // The exit block, which is discarded, must do nothing.
+ if (exitBlock != null && exitBlock.getInsns().size() != 0) {
+ throw new RuntimeException(
+ "Exit block must have no insns when leaving SSA form");
+ }
+
+ return result;
+ }
+
+ /**
+ * Validates that a basic block is a valid end predecessor. It must
+ * end in a RETURN or a THROW. Throws a runtime exception on error.
+ *
+ * @param b {@code non-null;} block to validate
+ * @throws RuntimeException on error
+ */
+ private void verifyValidExitPredecessor(SsaBasicBlock b) {
+ ArrayList<SsaInsn> insns = b.getInsns();
+ SsaInsn lastInsn = insns.get(insns.size() - 1);
+ Rop opcode = lastInsn.getOpcode();
+
+ if (opcode.getBranchingness() != Rop.BRANCH_RETURN
+ && opcode != Rops.THROW) {
+ throw new RuntimeException("Exit predecessor must end"
+ + " in valid exit statement.");
+ }
+ }
+
+ /**
+ * Converts a single basic block to rop form.
+ *
+ * @param block SSA block to process
+ * @return {@code non-null;} ROP block
+ */
+ private BasicBlock convertBasicBlock(SsaBasicBlock block) {
+ IntList successorList = block.getRopLabelSuccessorList();
+ int primarySuccessorLabel = block.getPrimarySuccessorRopLabel();
+
+ // Filter out any reference to the SSA form's exit block.
+
+ // Exit block may be null.
+ SsaBasicBlock exitBlock = ssaMeth.getExitBlock();
+ int exitRopLabel = (exitBlock == null) ? -1 : exitBlock.getRopLabel();
+
+ if (successorList.contains(exitRopLabel)) {
+ if (successorList.size() > 1) {
+ throw new RuntimeException(
+ "Exit predecessor must have no other successors"
+ + Hex.u2(block.getRopLabel()));
+ } else {
+ successorList = IntList.EMPTY;
+ primarySuccessorLabel = -1;
+
+ verifyValidExitPredecessor(block);
+ }
+ }
+
+ successorList.setImmutable();
+
+ BasicBlock result = new BasicBlock(
+ block.getRopLabel(), convertInsns(block.getInsns()),
+ successorList,
+ primarySuccessorLabel);
+
+ return result;
+ }
+
+ /**
+ * Converts an insn list to rop form.
+ *
+ * @param ssaInsns {@code non-null;} old instructions
+ * @return {@code non-null;} immutable instruction list
+ */
+ private InsnList convertInsns(ArrayList<SsaInsn> ssaInsns) {
+ int insnCount = ssaInsns.size();
+ InsnList result = new InsnList(insnCount);
+
+ for (int i = 0; i < insnCount; i++) {
+ result.set(i, ssaInsns.get(i).toRopInsn());
+ }
+
+ result.setImmutable();
+
+ return result;
+ }
+
+ /**
+ * <b>Note:</b> This method is not presently used.
+ *
+ * @return a list of registers ordered by most-frequently-used to
+ * least-frequently-used. Each register is listed once and only
+ * once.
+ */
+ public int[] getRegistersByFrequency() {
+ int regCount = ssaMeth.getRegCount();
+ Integer[] ret = new Integer[regCount];
+
+ for (int i = 0; i < regCount; i++) {
+ ret[i] = i;
+ }
+
+ Arrays.sort(ret, new Comparator<Integer>() {
+ public int compare(Integer o1, Integer o2) {
+ return ssaMeth.getUseListForRegister(o2).size()
+ - ssaMeth.getUseListForRegister(o1).size();
+ }
+ });
+
+ int result[] = new int[regCount];
+
+ for (int i = 0; i < regCount; i++) {
+ result[i] = ret[i];
+ }
+
+ return result;
+ }
+}
diff --git a/dx/src/com/android/jack/dx/ssa/package-info.java b/dx/src/com/android/jack/dx/ssa/package-info.java
new file mode 100644
index 00000000..eb704fde
--- /dev/null
+++ b/dx/src/com/android/jack/dx/ssa/package-info.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * 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.android.jack.dx.ssa;
+
+/**
+ * <h1>An introduction to SSA Form</h1>
+ *
+ * This package contains classes associated with dx's {@code SSA}
+ * intermediate form. This form is a static-single-assignment representation of
+ * Rop-form a method with Rop-form-like instructions (with the addition of a
+ * {@link PhiInsn phi instriction}. This form is intended to make it easy to
+ * implement basic optimization steps and register allocation so that a
+ * reasonably efficient register machine representation can be produced from a
+ * stack machine source bytecode.<p>
+ *
+ * <h2>Key Classes</h2>
+ *
+ * <h3>Classes related to conversion and lifetime</h3>
+ * <ul>
+ * <li> {@link Optimizer} is a singleton class containing methods for
+ * converting, optimizing, and then back-converting Rop-form methods. It's the
+ * typical gateway into the rest of the package.
+ * <li> {@link SsaConverter} converts a Rop-form method to SSA form.
+ * <li> {@link SsaToRop} converts an SSA-form method back to Rop form.
+ * </ul>
+ *
+ * <h3>Classes related to method representation</h3>
+ * <ul>
+ * <li> A {@link SsaMethod} instance represents a method.
+ * <li> A {@link SsaBasicBlock} instance represents a basic block, whose
+ * semantics are quite similar to basic blocks in
+ * {@link com.android.jack.dx.rop Rop form}.
+ * <li> {@link PhiInsn} instances represent "phi" operators defined in SSA
+ * literature. They must be the first N instructions in a basic block.
+ * <li> {@link NormalSsaInsn} instances represent instructions that directly
+ * correspond to {@code Rop} form.
+ * </ul>
+ *
+ * <h3>Classes related to optimization steps</h3>
+ * <ul>
+ * <li> {@link MoveParamCombiner} is a simple step that ensures each method
+ * parameter is represented by at most one SSA register.
+ * <li> {@link SCCP} is a (partially implemented) sparse-conditional
+ * constant propogator.
+ * <li> {@link LiteralOpUpgrader} is a step that attempts to use constant
+ * information to convert math and comparison instructions into
+ * constant-bearing "literal ops" in cases where they can be represented in the
+ * output form (see {@link TranslationAdvice#hasConstantOperation}).
+ * <li> {@link ConstCollector} is a step that attempts to trade (modest)
+ * increased register space for decreased instruction count in cases where
+ * the same constant value is used repeatedly in a single method.
+ * <li> {@link DeadCodeRemover} is a dead code remover. This phase must
+ * always be run to remove unused phi instructions.
+ * </ul>
+ *
+ * <h2>SSA Lifetime</h2>
+ * The representation of a method in SSA form obeys slightly different
+ * constraints depending upon whether it is in the process of being converted
+ * into or out of SSA form.
+ *
+ * <h3>Conversion into SSA Form</h3>
+ *
+ * {@link SsaConverter#convertToSsaMethod} takes a {@code RopMethod} and
+ * returns a fully-converted {@code SsaMethod}. The conversion process
+ * is roughly as follows:
+ *
+ * <ol>
+ * <li> The Rop-form method, its blocks and their instructions are directly
+ * wrapped in {@code SsaMethod}, {@code SsaBasicBlock} and
+ * {@code SsaInsn} instances. Nothing else changes.
+ * <li> Critical control-flow graph edges are {@link SsaConverter#edgeSplit
+ * split} and new basic blocks inserted as required to meet the constraints
+ * necessary for the ultimate SSA representation.
+ * <li> A {@link LocalVariableExtractor} is run to produce a table of
+ * Rop registers to local variables necessary during phi placement. This
+ * step could also be done in Rop form and then updated through the preceding
+ * steps.
+ * <li> {@code Phi} instructions are {link SsaConverter#placePhiFunctions}
+ * placed in a semi-pruned fashion, which requires computation of {@link
+ * Dominators dominance graph} and each node's {@link DomFront
+ * dominance-frontier set}.
+ * <li> Finally, source and result registers for all instructions are {@link
+ * SsaRenamer renamed} such that each assignment is given a unique register
+ * number (register categories or widths, significant in Rop form, do not
+ * exist in SSA). Move instructions are eliminated except where necessary
+ * to preserve local variable assignments.
+ * </ol>
+ *
+ */