blob: e161b8ea5df6788077d90222ba7cd5c77113e700 [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.code.AddIntLit16;
import com.android.tools.r8.code.AddIntLit8;
import com.android.tools.r8.code.RsubInt;
import com.android.tools.r8.code.RsubIntLit8;
import com.android.tools.r8.code.SubDouble;
import com.android.tools.r8.code.SubDouble2Addr;
import com.android.tools.r8.code.SubFloat;
import com.android.tools.r8.code.SubFloat2Addr;
import com.android.tools.r8.code.SubInt;
import com.android.tools.r8.code.SubInt2Addr;
import com.android.tools.r8.code.SubLong;
import com.android.tools.r8.code.SubLong2Addr;
import com.android.tools.r8.dex.Constants;
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 com.android.tools.r8.code.Instruction CreateInt(int dest, int left, int right) {
return new SubInt(dest, left, right);
}
@Override
public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
return new SubLong(dest, left, right);
}
@Override
public com.android.tools.r8.code.Instruction CreateFloat(int dest, int left, int right) {
return new SubFloat(dest, left, right);
}
@Override
public com.android.tools.r8.code.Instruction CreateDouble(int dest, int left, int right) {
return new SubDouble(dest, left, right);
}
@Override
public com.android.tools.r8.code.Instruction CreateInt2Addr(int left, int right) {
return new SubInt2Addr(left, right);
}
@Override
public com.android.tools.r8.code.Instruction CreateLong2Addr(int left, int right) {
return new SubLong2Addr(left, right);
}
@Override
public com.android.tools.r8.code.Instruction CreateFloat2Addr(int left, int right) {
return new SubFloat2Addr(left, right);
}
@Override
public com.android.tools.r8.code.Instruction CreateDouble2Addr(int left, int right) {
return new SubDouble2Addr(left, right);
}
@Override
public com.android.tools.r8.code.Instruction 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 com.android.tools.r8.code.Instruction 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;
}
com.android.tools.r8.code.Instruction 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 RsubIntLit8(dest, right, left.getIntValue());
} else {
assert left.is16Bit();
instruction = new RsubInt(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 AddIntLit8(dest, left, -right.getIntValue());
} else {
assert right.negativeIs16Bit();
instruction = new AddIntLit16(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;
}
}