use of com.android.dx.util.IntList in project buck by facebook.
the class IdenticalBlockCombiner method combineBlocks.
/**
* 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);
}
}
}
use of com.android.dx.util.IntList in project buck by facebook.
the class Ropper method inlineSubroutines.
/**
* Inlines any subroutine calls.
*/
private void inlineSubroutines() {
final IntList reachableSubroutineCallerLabels = new IntList(4);
/*
* Compile a list of all subroutine calls reachable
* through the normal (non-subroutine) flow. We do this first, since
* we'll be affecting the call flow as we go.
*
* Start at label 0 -- the param assignment block has nothing for us
*/
forEachNonSubBlockDepthFirst(0, new BasicBlock.Visitor() {
public void visitBlock(BasicBlock b) {
if (isSubroutineCaller(b)) {
reachableSubroutineCallerLabels.add(b.getLabel());
}
}
});
/*
* Convert the resultSubroutines list, indexed by block index,
* to a label-to-subroutines mapping used by the inliner.
*/
int largestAllocedLabel = getAvailableLabel();
ArrayList<IntList> labelToSubroutines = new ArrayList<IntList>(largestAllocedLabel);
for (int i = 0; i < largestAllocedLabel; i++) {
labelToSubroutines.add(null);
}
for (int i = 0; i < result.size(); i++) {
BasicBlock b = result.get(i);
if (b == null) {
continue;
}
IntList subroutineList = resultSubroutines.get(i);
labelToSubroutines.set(b.getLabel(), subroutineList);
}
/*
* Inline all reachable subroutines.
* Inner subroutines will be inlined as they are encountered.
*/
int sz = reachableSubroutineCallerLabels.size();
for (int i = 0; i < sz; i++) {
int label = reachableSubroutineCallerLabels.get(i);
new SubroutineInliner(new LabelAllocator(getAvailableLabel()), labelToSubroutines).inlineSubroutineCalledFrom(labelToBlock(label));
}
// Now find the blocks that aren't reachable and remove them
deleteUnreachableBlocks();
}
use of com.android.dx.util.IntList in project buck by facebook.
the class RopperMachine method run.
/** {@inheritDoc} */
@Override
public void run(Frame frame, int offset, int opcode) {
/*
* This is the stack pointer after the opcode's arguments have been
* popped.
*/
int stackPointer = maxLocals + frame.getStack().size();
// The sources have to be retrieved before super.run() gets called.
RegisterSpecList sources = getSources(opcode, stackPointer);
int sourceCount = sources.size();
super.run(frame, offset, opcode);
SourcePosition pos = method.makeSourcePosistion(offset);
RegisterSpec localTarget = getLocalTarget(opcode == ByteOps.ISTORE);
int destCount = resultCount();
RegisterSpec dest;
if (destCount == 0) {
dest = null;
switch(opcode) {
case ByteOps.POP:
case ByteOps.POP2:
{
// These simply don't appear in the rop form.
return;
}
}
} else if (localTarget != null) {
dest = localTarget;
} else if (destCount == 1) {
dest = RegisterSpec.make(stackPointer, result(0));
} else {
/*
* This clause only ever applies to the stack manipulation
* ops that have results (that is, dup* and swap but not
* pop*).
*
* What we do is first move all the source registers into
* the "temporary stack" area defined for the method, and
* then move stuff back down onto the main "stack" in the
* arrangement specified by the stack op pattern.
*
* Note: This code ends up emitting a lot of what will
* turn out to be superfluous moves (e.g., moving back and
* forth to the same local when doing a dup); however,
* that makes this code a bit easier (and goodness knows
* it doesn't need any extra complexity), and all the SSA
* stuff is going to want to deal with this sort of
* superfluous assignment anyway, so it should be a wash
* in the end.
*/
int scratchAt = ropper.getFirstTempStackReg();
RegisterSpec[] scratchRegs = new RegisterSpec[sourceCount];
for (int i = 0; i < sourceCount; i++) {
RegisterSpec src = sources.get(i);
TypeBearer type = src.getTypeBearer();
RegisterSpec scratch = src.withReg(scratchAt);
insns.add(new PlainInsn(Rops.opMove(type), pos, scratch, src));
scratchRegs[i] = scratch;
scratchAt += src.getCategory();
}
for (int pattern = getAuxInt(); pattern != 0; pattern >>= 4) {
int which = (pattern & 0x0f) - 1;
RegisterSpec scratch = scratchRegs[which];
TypeBearer type = scratch.getTypeBearer();
insns.add(new PlainInsn(Rops.opMove(type), pos, scratch.withReg(stackPointer), scratch));
stackPointer += type.getType().getCategory();
}
return;
}
TypeBearer destType = (dest != null) ? dest : Type.VOID;
Constant cst = getAuxCst();
int ropOpcode;
Rop rop;
Insn insn;
if (opcode == ByteOps.MULTIANEWARRAY) {
blockCanThrow = true;
// Add the extra instructions for handling multianewarray.
extraBlockCount = 6;
/*
* Add an array constructor for the int[] containing all the
* dimensions.
*/
RegisterSpec dimsReg = RegisterSpec.make(dest.getNextReg(), Type.INT_ARRAY);
rop = Rops.opFilledNewArray(Type.INT_ARRAY, sourceCount);
insn = new ThrowingCstInsn(rop, pos, sources, catches, CstType.INT_ARRAY);
insns.add(insn);
// Add a move-result for the new-filled-array
rop = Rops.opMoveResult(Type.INT_ARRAY);
insn = new PlainInsn(rop, pos, dimsReg, RegisterSpecList.EMPTY);
insns.add(insn);
/*
* Add a const-class instruction for the specified array
* class.
*/
/*
* Remove as many dimensions from the originally specified
* class as are given in the explicit list of dimensions,
* so as to pass the right component class to the standard
* Java library array constructor.
*/
Type componentType = ((CstType) cst).getClassType();
for (int i = 0; i < sourceCount; i++) {
componentType = componentType.getComponentType();
}
RegisterSpec classReg = RegisterSpec.make(dest.getReg(), Type.CLASS);
if (componentType.isPrimitive()) {
/*
* The component type is primitive (e.g., int as opposed
* to Integer), so we have to fetch the corresponding
* TYPE class.
*/
CstFieldRef typeField = CstFieldRef.forPrimitiveType(componentType);
insn = new ThrowingCstInsn(Rops.GET_STATIC_OBJECT, pos, RegisterSpecList.EMPTY, catches, typeField);
} else {
/*
* The component type is an object type, so just make a
* normal class reference.
*/
insn = new ThrowingCstInsn(Rops.CONST_OBJECT, pos, RegisterSpecList.EMPTY, catches, new CstType(componentType));
}
insns.add(insn);
// Add a move-result-pseudo for the get-static or const
rop = Rops.opMoveResultPseudo(classReg.getType());
insn = new PlainInsn(rop, pos, classReg, RegisterSpecList.EMPTY);
insns.add(insn);
/*
* Add a call to the "multianewarray method," that is,
* Array.newInstance(class, dims). Note: The result type
* of newInstance() is Object, which is why the last
* instruction in this sequence is a cast to the right
* type for the original instruction.
*/
RegisterSpec objectReg = RegisterSpec.make(dest.getReg(), Type.OBJECT);
insn = new ThrowingCstInsn(Rops.opInvokeStatic(MULTIANEWARRAY_METHOD.getPrototype()), pos, RegisterSpecList.make(classReg, dimsReg), catches, MULTIANEWARRAY_METHOD);
insns.add(insn);
// Add a move-result.
rop = Rops.opMoveResult(MULTIANEWARRAY_METHOD.getPrototype().getReturnType());
insn = new PlainInsn(rop, pos, objectReg, RegisterSpecList.EMPTY);
insns.add(insn);
/*
* And finally, set up for the remainder of this method to
* add an appropriate cast.
*/
opcode = ByteOps.CHECKCAST;
sources = RegisterSpecList.make(objectReg);
} else if (opcode == ByteOps.JSR) {
// JSR has no Rop instruction
hasJsr = true;
return;
} else if (opcode == ByteOps.RET) {
try {
returnAddress = (ReturnAddress) arg(0);
} catch (ClassCastException ex) {
throw new RuntimeException("Argument to RET was not a ReturnAddress", ex);
}
// RET has no Rop instruction.
return;
}
ropOpcode = jopToRopOpcode(opcode, cst);
rop = Rops.ropFor(ropOpcode, destType, sources, cst);
Insn moveResult = null;
if (dest != null && rop.isCallLike()) {
/*
* We're going to want to have a move-result in the next
* basic block.
*/
extraBlockCount++;
moveResult = new PlainInsn(Rops.opMoveResult(((CstMethodRef) cst).getPrototype().getReturnType()), pos, dest, RegisterSpecList.EMPTY);
dest = null;
} else if (dest != null && rop.canThrow()) {
/*
* We're going to want to have a move-result-pseudo in the
* next basic block.
*/
extraBlockCount++;
moveResult = new PlainInsn(Rops.opMoveResultPseudo(dest.getTypeBearer()), pos, dest, RegisterSpecList.EMPTY);
dest = null;
}
if (ropOpcode == RegOps.NEW_ARRAY) {
/*
* In the original bytecode, this was either a primitive
* array constructor "newarray" or an object array
* constructor "anewarray". In the former case, there is
* no explicit constant, and in the latter, the constant
* is for the element type and not the array type. The rop
* instruction form for both of these is supposed to be
* the resulting array type, so we initialize / alter
* "cst" here, accordingly. Conveniently enough, the rop
* opcode already gets constructed with the proper array
* type.
*/
cst = CstType.intern(rop.getResult());
} else if ((cst == null) && (sourceCount == 2)) {
TypeBearer firstType = sources.get(0).getTypeBearer();
TypeBearer lastType = sources.get(1).getTypeBearer();
if ((lastType.isConstant() || firstType.isConstant()) && advice.hasConstantOperation(rop, sources.get(0), sources.get(1))) {
if (lastType.isConstant()) {
/*
* The target architecture has an instruction that can
* build in the constant found in the second argument,
* so pull it out of the sources and just use it as a
* constant here.
*/
cst = (Constant) lastType;
sources = sources.withoutLast();
// For subtraction, change to addition and invert constant
if (rop.getOpcode() == RegOps.SUB) {
ropOpcode = RegOps.ADD;
CstInteger cstInt = (CstInteger) lastType;
cst = CstInteger.make(-cstInt.getValue());
}
} else {
/*
* The target architecture has an instruction that can
* build in the constant found in the first argument,
* so pull it out of the sources and just use it as a
* constant here.
*/
cst = (Constant) firstType;
sources = sources.withoutFirst();
}
rop = Rops.ropFor(ropOpcode, destType, sources, cst);
}
}
SwitchList cases = getAuxCases();
ArrayList<Constant> initValues = getInitValues();
boolean canThrow = rop.canThrow();
blockCanThrow |= canThrow;
if (cases != null) {
if (cases.size() == 0) {
// It's a default-only switch statement. It can happen!
insn = new PlainInsn(Rops.GOTO, pos, null, RegisterSpecList.EMPTY);
primarySuccessorIndex = 0;
} else {
IntList values = cases.getValues();
insn = new SwitchInsn(rop, pos, dest, sources, values);
primarySuccessorIndex = values.size();
}
} else if (ropOpcode == RegOps.RETURN) {
/*
* Returns get turned into the combination of a move (if
* non-void and if the return doesn't already mention
* register 0) and a goto (to the return block).
*/
if (sources.size() != 0) {
RegisterSpec source = sources.get(0);
TypeBearer type = source.getTypeBearer();
if (source.getReg() != 0) {
insns.add(new PlainInsn(Rops.opMove(type), pos, RegisterSpec.make(0, type), source));
}
}
insn = new PlainInsn(Rops.GOTO, pos, null, RegisterSpecList.EMPTY);
primarySuccessorIndex = 0;
updateReturnOp(rop, pos);
returns = true;
} else if (cst != null) {
if (canThrow) {
insn = new ThrowingCstInsn(rop, pos, sources, catches, cst);
catchesUsed = true;
primarySuccessorIndex = catches.size();
} else {
insn = new PlainCstInsn(rop, pos, dest, sources, cst);
}
} else if (canThrow) {
insn = new ThrowingInsn(rop, pos, sources, catches);
catchesUsed = true;
if (opcode == ByteOps.ATHROW) {
/*
* The op athrow is the only one where it's possible
* to have non-empty successors and yet not have a
* primary successor.
*/
primarySuccessorIndex = -1;
} else {
primarySuccessorIndex = catches.size();
}
} else {
insn = new PlainInsn(rop, pos, dest, sources);
}
insns.add(insn);
if (moveResult != null) {
insns.add(moveResult);
}
/*
* If initValues is non-null, it means that the parser has
* seen a group of compatible constant initialization
* bytecodes that are applied to the current newarray. The
* action we take here is to convert these initialization
* bytecodes into a single fill-array-data ROP which lays out
* all the constant values in a table.
*/
if (initValues != null) {
extraBlockCount++;
insn = new FillArrayDataInsn(Rops.FILL_ARRAY_DATA, pos, RegisterSpecList.make(moveResult.getResult()), initValues, cst);
insns.add(insn);
}
}
use of com.android.dx.util.IntList in project buck by facebook.
the class LocalVariableExtractor method processBlock.
/**
* 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;
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);
}
}
}
use of com.android.dx.util.IntList in project buck by facebook.
the class BasicBlockList method catchesEqual.
/**
* Compares the catches of two blocks for equality. This includes
* both the catch types and target labels.
*
* @param block1 {@code non-null;} one block to compare
* @param block2 {@code non-null;} the other block to compare
* @return {@code true} if the two blocks' non-primary successors
* are identical
*/
public boolean catchesEqual(BasicBlock block1, BasicBlock block2) {
TypeList catches1 = block1.getExceptionHandlerTypes();
TypeList catches2 = block2.getExceptionHandlerTypes();
if (!StdTypeList.equalContents(catches1, catches2)) {
return false;
}
IntList succ1 = block1.getSuccessors();
IntList succ2 = block2.getSuccessors();
// Both are guaranteed to be the same size.
int size = succ1.size();
int primary1 = block1.getPrimarySuccessor();
int primary2 = block2.getPrimarySuccessor();
if (((primary1 == -1) || (primary2 == -1)) && (primary1 != primary2)) {
/*
* For the current purpose, both blocks in question must
* either both have a primary or both not have a primary to
* be considered equal, and it turns out here that that's not
* the case.
*/
return false;
}
for (int i = 0; i < size; i++) {
int label1 = succ1.get(i);
int label2 = succ2.get(i);
if (label1 == primary1) {
/*
* It should be the case that block2's primary is at the
* same index. If not, we consider the blocks unequal for
* the current purpose.
*/
if (label2 != primary2) {
return false;
}
continue;
}
if (label1 != label2) {
return false;
}
}
return true;
}
Aggregations