blob: 42068dee7352da5964846c30c766de3552b59162 [file] [log] [blame]
// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
import static com.android.tools.r8.dex.Constants.U4BIT_MAX;
import static com.android.tools.r8.dex.Constants.U8BIT_MAX;
import com.android.tools.r8.cf.LoadStoreHelper;
import com.android.tools.r8.cf.code.CfIf;
import com.android.tools.r8.cf.code.CfIfCmp;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.utils.CfgPrinter;
import com.android.tools.r8.utils.InternalOutputMode;
import java.util.List;
public class If extends JumpInstruction {
public enum Type {
EQ, GE, GT, LE, LT, NE;
// Returns the comparison type if the operands are swapped.
public Type forSwappedOperands() {
switch (this) {
case EQ:
case NE:
return this;
case GE:
return Type.LE;
case GT:
return Type.LT;
case LE:
return Type.GE;
case LT:
return Type.GT;
default:
throw new Unreachable("Unknown if condition type.");
}
}
public Type inverted() {
switch (this) {
case EQ:
return Type.NE;
case GE:
return Type.LT;
case GT:
return Type.LE;
case LE:
return Type.GT;
case LT:
return Type.GE;
case NE:
return Type.EQ;
default:
throw new Unreachable("Unknown if condition type.");
}
}
}
private static boolean verifyTypeCompatible(TypeElement valueType, If.Type ifType) {
return valueType.isInt()
|| (valueType.isFloat() && (ifType == Type.EQ || ifType == Type.NE))
|| (valueType.isReferenceType() && (ifType == Type.EQ || ifType == Type.NE));
}
private Type type;
public If(Type type, Value value) {
super(value);
this.type = type;
}
public If(Type type, List<Value> values) {
super(values);
this.type = type;
}
@Override
public int opcode() {
return Opcodes.IF;
}
@Override
public <T> T accept(InstructionVisitor<T> visitor) {
return visitor.visit(this);
}
public boolean isZeroTest() {
return inValues.size() == 1;
}
public Value lhs() {
return inValues.get(0);
}
public Value rhs() {
assert !isZeroTest();
return inValues.get(1);
}
public Type getType() {
return type;
}
public void invert() {
BasicBlock tmp = getTrueTarget();
setTrueTarget(fallthroughBlock());
setFallthroughBlock(tmp);
type = type.inverted();
}
public BasicBlock getTrueTarget() {
assert getBlock().exit() == this;
List<BasicBlock> successors = getBlock().getSuccessors();
assert successors.size() >= 2;
return successors.get(successors.size() - 2);
}
public void setTrueTarget(BasicBlock block) {
assert getBlock().exit() == this;
List<BasicBlock> successors = getBlock().getMutableSuccessors();
assert successors.size() >= 2;
successors.set(successors.size() - 2, block);
}
@Override
public BasicBlock fallthroughBlock() {
assert getBlock().exit() == this;
List<BasicBlock> successors = getBlock().getSuccessors();
assert successors.size() >= 2;
return successors.get(successors.size() - 1);
}
@Override
public void setFallthroughBlock(BasicBlock block) {
List<BasicBlock> successors = getBlock().getMutableSuccessors();
successors.set(successors.size() - 1, block);
}
@Override
public void buildDex(DexBuilder builder) {
builder.addIf(this);
}
// Estimated size of the resulting instructions in code units (bytes in CF, 16-bit in Dex).
public static int estimatedSize(InternalOutputMode mode) {
if (mode.isGeneratingClassFiles()) {
// op + branch1 + branch2
return 3;
} else {
return 2;
}
}
@Override
public String toString() {
return super.toString()
+ " "
+ type
+ (isZeroTest() ? "Z" : " ")
+ " block "
+ getTrueTarget().getNumberAsString()
+ " (fallthrough "
+ fallthroughBlock().getNumberAsString()
+ ")";
}
@Override
public int maxInValueRegister() {
return isZeroTest() ? U8BIT_MAX : U4BIT_MAX;
}
@Override
public int maxOutValueRegister() {
assert false : "If instructions define no values.";
return 0;
}
@Override
public void print(CfgPrinter printer) {
super.print(printer);
printer.append(" B").append(getTrueTarget().getNumber());
}
@Override
public boolean identicalNonValueNonPositionParts(Instruction other) {
if (!other.isIf()) {
return false;
}
If o = other.asIf();
return o.getTrueTarget() == getTrueTarget()
&& o.fallthroughBlock() == fallthroughBlock()
&& o.type == type;
}
public BasicBlock targetFromCondition(ConstNumber value) {
assert isZeroTest();
assert verifyTypeCompatible(value.getOutType(), type);
return targetFromCondition(Long.signum(value.getRawValue()));
}
public BasicBlock targetFromCondition(ConstNumber left, ConstNumber right) {
assert !isZeroTest();
assert left.outType() == right.outType();
assert verifyTypeCompatible(left.getOutType(), type);
return targetFromCondition(Long.signum(left.getRawValue() - right.getRawValue()));
}
public BasicBlock targetFromNonNullObject() {
assert isZeroTest();
assert inValues.get(0).outType().isObject();
return targetFromCondition(1);
}
public BasicBlock targetFromCondition(int cond) {
assert Integer.signum(cond) == cond;
switch (type) {
case EQ:
return cond == 0 ? getTrueTarget() : fallthroughBlock();
case NE:
return cond != 0 ? getTrueTarget() : fallthroughBlock();
case GE:
return cond >= 0 ? getTrueTarget() : fallthroughBlock();
case GT:
return cond > 0 ? getTrueTarget() : fallthroughBlock();
case LE:
return cond <= 0 ? getTrueTarget() : fallthroughBlock();
case LT:
return cond < 0 ? getTrueTarget() : fallthroughBlock();
}
throw new Unreachable("Unexpected condition type " + type);
}
@Override
public boolean isIf() {
return true;
}
@Override
public If asIf() {
return this;
}
@Override
public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
helper.loadInValues(this, it);
}
@Override
public void buildCf(CfBuilder builder) {
ValueType ifType = inValues.get(0).outType();
if (inValues.size() == 1) {
builder.add(new CfIf(type, ifType, builder.getLabel(getTrueTarget())));
return;
}
assert inValues.size() == 2;
assert inValues.get(0).outType() == inValues.get(1).outType();
builder.add(new CfIfCmp(type, ifType, builder.getLabel(getTrueTarget())));
}
}