blob: fab70d76db4612a70348a1e8dee5a1f6a96b1cf6 [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 com.android.tools.r8.cf.code.CfArithmeticBinop;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.dex.code.DexAddIntLit16;
import com.android.tools.r8.dex.code.DexAddIntLit8;
import com.android.tools.r8.dex.code.DexInstruction;
import com.android.tools.r8.dex.code.DexRsubInt;
import com.android.tools.r8.dex.code.DexRsubIntLit8;
import com.android.tools.r8.dex.code.DexSubDouble;
import com.android.tools.r8.dex.code.DexSubDouble2Addr;
import com.android.tools.r8.dex.code.DexSubFloat;
import com.android.tools.r8.dex.code.DexSubFloat2Addr;
import com.android.tools.r8.dex.code.DexSubInt;
import com.android.tools.r8.dex.code.DexSubInt2Addr;
import com.android.tools.r8.dex.code.DexSubLong;
import com.android.tools.r8.dex.code.DexSubLong2Addr;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.conversion.DexBuilder;
public class Sub extends ArithmeticBinop {
public Sub(NumericType type, Value dest, Value left, Value right) {
super(type, dest, left, right);
}
@Override
public int opcode() {
return Opcodes.SUB;
}
@Override
public <T> T accept(InstructionVisitor<T> visitor) {
return visitor.visit(this);
}
@Override
public boolean isCommutative() {
return false;
}
@Override
public DexInstruction CreateInt(int dest, int left, int right) {
return new DexSubInt(dest, left, right);
}
@Override
public DexInstruction CreateLong(int dest, int left, int right) {
return new DexSubLong(dest, left, right);
}
@Override
public DexInstruction CreateFloat(int dest, int left, int right) {
return new DexSubFloat(dest, left, right);
}
@Override
public DexInstruction CreateDouble(int dest, int left, int right) {
return new DexSubDouble(dest, left, right);
}
@Override
public DexInstruction CreateInt2Addr(int left, int right) {
return new DexSubInt2Addr(left, right);
}
@Override
public DexInstruction CreateLong2Addr(int left, int right) {
return new DexSubLong2Addr(left, right);
}
@Override
public DexInstruction CreateFloat2Addr(int left, int right) {
return new DexSubFloat2Addr(left, right);
}
@Override
public DexInstruction CreateDouble2Addr(int left, int right) {
return new DexSubDouble2Addr(left, right);
}
@Override
public DexInstruction CreateIntLit8(int dest, int left, int constant) {
// The sub instructions with constants are rsub, and is handled below.
throw new Unreachable("Unsupported instruction SubIntLit8");
}
@Override
public DexInstruction CreateIntLit16(int dest, int left, int constant) {
// The sub instructions with constants are rsub, and is handled below.
throw new Unreachable("Unsupported instruction SubIntLit16");
}
@Override
public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.isSub() && other.asSub().type == type;
}
@Override
int foldIntegers(int left, int right) {
return left - right;
}
@Override
long foldLongs(long left, long right) {
return left - right;
}
@Override
float foldFloat(float left, float right) {
return left - right;
}
@Override
double foldDouble(double left, double right) {
return left - right;
}
boolean negativeFitsInDexInstruction(Value value) {
return type == NumericType.INT &&
value.isConstant() &&
value.getConstInstruction().asConstNumber().negativeIs16Bit();
}
// This is overridden to give the correct value when adding the negative constant.
@Override
int maxInOutValueRegisterSize() {
if (!needsValueInRegister(leftValue())) {
assert fitsInDexInstruction(leftValue());
ConstNumber left = leftValue().getConstInstruction().asConstNumber();
return left.is8Bit() ? Constants.U8BIT_MAX : Constants.U4BIT_MAX;
} else if (!needsValueInRegister(rightValue())) {
assert negativeFitsInDexInstruction(rightValue());
ConstNumber right = rightValue().getConstInstruction().asConstNumber();
return right.negativeIs8Bit() ? Constants.U8BIT_MAX : Constants.U4BIT_MAX;
}
return Constants.U8BIT_MAX;
}
@Override
public boolean needsValueInRegister(Value value) {
if (leftValue() == rightValue()) {
// We cannot distinguish the the two values, so both must end up in registers no matter what.
return true;
}
if (value == leftValue()) {
// If the left value fits in the dex instruction no register is needed for that (rsub
// instruction).
return !fitsInDexInstruction(value);
} else {
assert value == rightValue();
// If the negative right value fits in the dex instruction no register is needed for that (add
// instruction with the negative value), unless the left is taking that place.
return !negativeFitsInDexInstruction(value) || fitsInDexInstruction(leftValue());
}
}
@Override
public void buildDex(DexBuilder builder) {
// Handle two address and non-int case through the generic arithmetic binop.
if (isTwoAddr(builder.getRegisterAllocator()) || type != NumericType.INT) {
super.buildDex(builder);
return;
}
DexInstruction instruction = null;
if (!needsValueInRegister(leftValue())) {
// Sub instructions with small left constant is emitted as rsub.
assert fitsInDexInstruction(leftValue());
ConstNumber left = leftValue().getConstInstruction().asConstNumber();
int right = builder.allocatedRegister(rightValue(), getNumber());
int dest = builder.allocatedRegister(outValue, getNumber());
if (left.is8Bit()) {
instruction = new DexRsubIntLit8(dest, right, left.getIntValue());
} else {
assert left.is16Bit();
instruction = new DexRsubInt(dest, right, left.getIntValue());
}
} else if (!needsValueInRegister(rightValue())) {
// Sub instructions with small right constant are emitted as add of the negative constant.
assert negativeFitsInDexInstruction(rightValue());
int dest = builder.allocatedRegister(outValue, getNumber());
assert needsValueInRegister(leftValue());
int left = builder.allocatedRegister(leftValue(), getNumber());
ConstNumber right = rightValue().getConstInstruction().asConstNumber();
if (right.negativeIs8Bit()) {
instruction = new DexAddIntLit8(dest, left, -right.getIntValue());
} else {
assert right.negativeIs16Bit();
instruction = new DexAddIntLit16(dest, left, -right.getIntValue());
}
} else {
assert type == NumericType.INT;
int left = builder.allocatedRegister(leftValue(), getNumber());
int right = builder.allocatedRegister(rightValue(), getNumber());
int dest = builder.allocatedRegister(outValue, getNumber());
instruction = CreateInt(dest, left, right);
}
builder.add(this, instruction);
}
@Override
public boolean isSub() {
return true;
}
@Override
public Sub asSub() {
return this;
}
@Override
CfArithmeticBinop.Opcode getCfOpcode() {
return CfArithmeticBinop.Opcode.Sub;
}
}