Package jadx.core.dex.visitors.ssa

Source Code of jadx.core.dex.visitors.ssa.SSATransform

package jadx.core.dex.visitors.ssa;

import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.nodes.PhiListAttr;
import jadx.core.dex.instructions.InsnType;
import jadx.core.dex.instructions.PhiInsn;
import jadx.core.dex.instructions.args.InsnArg;
import jadx.core.dex.instructions.args.RegisterArg;
import jadx.core.dex.instructions.args.SSAVar;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.visitors.AbstractVisitor;
import jadx.core.utils.InstructionRemover;
import jadx.core.utils.exceptions.JadxException;
import jadx.core.utils.exceptions.JadxRuntimeException;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;

public class SSATransform extends AbstractVisitor {

  @Override
  public void visit(MethodNode mth) throws JadxException {
    if (mth.isNoCode()) {
      return;
    }
    process(mth);
  }

  private static void process(MethodNode mth) {
    LiveVarAnalysis la = new LiveVarAnalysis(mth);
    la.runAnalysis();
    int regsCount = mth.getRegsCount();
    for (int i = 0; i < regsCount; i++) {
      placePhi(mth, i, la);
    }
    renameVariables(mth);
    fixLastTryCatchAssign(mth);
    if (removeUselessPhi(mth)) {
      renameVariables(mth);
    }
  }

  private static void placePhi(MethodNode mth, int regNum, LiveVarAnalysis la) {
    List<BlockNode> blocks = mth.getBasicBlocks();
    int blocksCount = blocks.size();
    BitSet hasPhi = new BitSet(blocksCount);
    BitSet processed = new BitSet(blocksCount);
    Deque<BlockNode> workList = new LinkedList<BlockNode>();

    BitSet assignBlocks = la.getAssignBlocks(regNum);
    for (int id = assignBlocks.nextSetBit(0); id >= 0; id = assignBlocks.nextSetBit(id + 1)) {
      processed.set(id);
      workList.add(blocks.get(id));
    }
    while (!workList.isEmpty()) {
      BlockNode block = workList.pop();
      BitSet domFrontier = block.getDomFrontier();
      for (int id = domFrontier.nextSetBit(0); id >= 0; id = domFrontier.nextSetBit(id + 1)) {
        if (!hasPhi.get(id) && la.isLive(id, regNum)) {
          BlockNode df = blocks.get(id);
          addPhi(df, regNum);
          hasPhi.set(id);
          if (!processed.get(id)) {
            processed.set(id);
            workList.add(df);
          }
        }
      }
    }
  }

  private static void addPhi(BlockNode block, int regNum) {
    PhiListAttr phiList = block.get(AType.PHI_LIST);
    if (phiList == null) {
      phiList = new PhiListAttr();
      block.addAttr(phiList);
    }
    PhiInsn phiInsn = new PhiInsn(regNum, block.getPredecessors().size());
    phiList.getList().add(phiInsn);
    phiInsn.setOffset(block.getStartOffset());
    block.getInstructions().add(0, phiInsn);
  }

  private static void renameVariables(MethodNode mth) {
    int regsCount = mth.getRegsCount();
    SSAVar[] vars = new SSAVar[regsCount];
    int[] versions = new int[regsCount];
    // init method arguments
    for (RegisterArg arg : mth.getArguments(true)) {
      int regNum = arg.getRegNum();
      vars[regNum] = mth.makeNewSVar(regNum, versions, arg);
    }
    renameVar(mth, vars, versions, mth.getEnterBlock());
  }

  private static void renameVar(MethodNode mth, SSAVar[] vars, int[] vers, BlockNode block) {
    SSAVar[] inputVars = Arrays.copyOf(vars, vars.length);
    for (InsnNode insn : block.getInstructions()) {
      if (insn.getType() != InsnType.PHI) {
        for (InsnArg arg : insn.getArguments()) {
          if (arg.isRegister()) {
            RegisterArg reg = (RegisterArg) arg;
            int regNum = reg.getRegNum();
            SSAVar var = vars[regNum];
            if (var == null) {
              var = mth.makeNewSVar(regNum, vers, null);
              vars[regNum] = var;
            }
            var.use(reg);
          }
        }
      }
      RegisterArg result = insn.getResult();
      if (result != null) {
        int regNum = result.getRegNum();
        vars[regNum] = mth.makeNewSVar(regNum, vers, result);
      }
    }
    for (BlockNode s : block.getSuccessors()) {
      PhiListAttr phiList = s.get(AType.PHI_LIST);
      if (phiList != null) {
        int j = s.getPredecessors().indexOf(block);
        if (j == -1) {
          throw new JadxRuntimeException("Can't find predecessor for " + block + " " + s);
        }
        for (PhiInsn phiInsn : phiList.getList()) {
          if (j >= phiInsn.getArgsCount()) {
            continue;
          }
          int regNum = phiInsn.getResult().getRegNum();
          SSAVar var = vars[regNum];
          if (var == null) {
            var = mth.makeNewSVar(regNum, vers, null);
            vars[regNum] = var;
          }
          var.use(phiInsn.getArg(j));
          var.setUsedInPhi(phiInsn);
        }
      }
    }
    for (BlockNode domOn : block.getDominatesOn()) {
      renameVar(mth, vars, vers, domOn);
    }
    System.arraycopy(inputVars, 0, vars, 0, vars.length);
  }

  private static void fixLastTryCatchAssign(MethodNode mth) {
    for (BlockNode block : mth.getBasicBlocks()) {
      PhiListAttr phiList = block.get(AType.PHI_LIST);
      if (phiList == null || !block.contains(AType.EXC_HANDLER)) {
        continue;
      }
      for (PhiInsn phi : phiList.getList()) {
        for (int i = 0; i < phi.getArgsCount(); i++) {
          RegisterArg arg = phi.getArg(i);
          InsnNode parentInsn = arg.getAssignInsn();
          if (parentInsn != null
              && parentInsn.getResult() != null
              && parentInsn.contains(AFlag.TRY_LEAVE)) {
            phi.removeArg(arg);
          }
        }
      }
    }
  }

  private static boolean removeUselessPhi(MethodNode mth) {
    List<PhiInsn> insnToRemove = new ArrayList<PhiInsn>();
    for (SSAVar var : mth.getSVars()) {
      // phi result not used
      if (var.getUseCount() == 0) {
        InsnNode assignInsn = var.getAssign().getParentInsn();
        if (assignInsn != null && assignInsn.getType() == InsnType.PHI) {
          insnToRemove.add((PhiInsn) assignInsn);
        }
      }
    }
    for (BlockNode block : mth.getBasicBlocks()) {
      PhiListAttr phiList = block.get(AType.PHI_LIST);
      if (phiList == null) {
        continue;
      }
      for (PhiInsn phi : phiList.getList()) {
        removePhiWithSameArgs(phi, insnToRemove);
      }
    }
    return removePhiList(mth, insnToRemove);
  }

  private static void removePhiWithSameArgs(PhiInsn phi, List<PhiInsn> insnToRemove) {
    if (phi.getArgsCount() <= 1) {
      insnToRemove.add(phi);
      return;
    }
    boolean allSame = true;
    SSAVar var = phi.getArg(0).getSVar();
    for (int i = 1; i < phi.getArgsCount(); i++) {
      if (var != phi.getArg(i).getSVar()) {
        allSame = false;
        break;
      }
    }
    if (allSame) {
      // replace
      insnToRemove.add(phi);
      SSAVar assign = phi.getResult().getSVar();
      for (RegisterArg arg : new ArrayList<RegisterArg>(assign.getUseList())) {
        assign.removeUse(arg);
        var.use(arg);
      }
    }
  }

  private static boolean removePhiList(MethodNode mth, List<PhiInsn> insnToRemove) {
    if (insnToRemove.isEmpty()) {
      return false;
    }
    for (BlockNode block : mth.getBasicBlocks()) {
      PhiListAttr phiList = block.get(AType.PHI_LIST);
      if (phiList == null) {
        continue;
      }
      List<PhiInsn> list = phiList.getList();
      for (PhiInsn phiInsn : insnToRemove) {
        if (list.remove(phiInsn)) {
          for (InsnArg arg : phiInsn.getArguments()) {
            ((RegisterArg) arg).getSVar().setUsedInPhi(null);
          }
          InstructionRemover.remove(mth, block, phiInsn);
        }
      }
      if (list.isEmpty()) {
        block.remove(AType.PHI_LIST);
      }
    }
    insnToRemove.clear();
    return true;
  }
}
TOP

Related Classes of jadx.core.dex.visitors.ssa.SSATransform

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.