| // 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.lightir.LirBuilder; |
| import com.android.tools.r8.utils.BooleanUtils; |
| 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 isNullTest() { |
| return isZeroTest() && lhs().getType().isReferenceType(); |
| } |
| |
| public boolean isNonTrivialNullTest() { |
| return isNullTest() && lhs().getType().isNullable(); |
| } |
| |
| 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 targetFromTrue() { |
| return targetFromBoolean(true); |
| } |
| |
| public BasicBlock targetFromFalse() { |
| return targetFromBoolean(false); |
| } |
| |
| public BasicBlock targetFromBoolean(boolean cond) { |
| assert isZeroTest(); |
| return targetFromCondition(BooleanUtils.intValue(cond)); |
| } |
| |
| 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 targetFromNullObject() { |
| assert isZeroTest(); |
| assert inValues.get(0).outType().isObject(); |
| return targetFromCondition(0); |
| } |
| |
| 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())), this); |
| return; |
| } |
| assert inValues.size() == 2; |
| assert inValues.get(0).outType() == inValues.get(1).outType(); |
| builder.add(new CfIfCmp(type, ifType, builder.getLabel(getTrueTarget())), this); |
| } |
| |
| @Override |
| public void buildLir(LirBuilder<Value, BasicBlock> builder) { |
| ValueType ifType = inValues.get(0).outType(); |
| if (inValues.size() == 1) { |
| builder.addIf(type, ifType, inValues.get(0), getTrueTarget()); |
| return; |
| } |
| assert inValues.size() == 2; |
| assert inValues.get(0).outType() == inValues.get(1).outType(); |
| builder.addIfCmp(type, ifType, inValues, getTrueTarget()); |
| } |
| } |