blob: 06aef0ad91d282397f1c28cf458343d5e613d9d6 [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.LoadStoreHelper;
import com.android.tools.r8.cf.TypeVerificationHelper;
import com.android.tools.r8.cf.code.CfConstNull;
import com.android.tools.r8.cf.code.CfConstNumber;
import com.android.tools.r8.code.Const;
import com.android.tools.r8.code.Const16;
import com.android.tools.r8.code.Const4;
import com.android.tools.r8.code.ConstHigh16;
import com.android.tools.r8.code.ConstWide;
import com.android.tools.r8.code.ConstWide16;
import com.android.tools.r8.code.ConstWide32;
import com.android.tools.r8.code.ConstWideHigh16;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.constant.Bottom;
import com.android.tools.r8.ir.analysis.constant.ConstLatticeElement;
import com.android.tools.r8.ir.analysis.constant.LatticeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.utils.InternalOutputMode;
import com.android.tools.r8.utils.NumberUtils;
import java.util.Set;
import java.util.function.Function;
public class ConstNumber extends ConstInstruction {
private final long value;
public ConstNumber(Value dest, long value) {
super(dest);
// We create const numbers after register allocation for rematerialization of values. Those
// are all for fixed register values. All other values that are used as the destination for
// const number instructions should be marked as constants.
assert dest.isFixedRegisterValue() || dest.definition.isConstNumber();
this.value = value;
}
@Override
public int opcode() {
return Opcodes.CONST_NUMBER;
}
@Override
public <T> T accept(InstructionVisitor<T> visitor) {
return visitor.visit(this);
}
public static ConstNumber copyOf(IRCode code, ConstNumber original) {
Value newValue =
new Value(code.valueNumberGenerator.next(), original.getOutType(), original.getLocalInfo());
return copyOf(newValue, original);
}
public static ConstNumber copyOf(Value newValue, ConstNumber original) {
assert newValue != original.outValue();
return new ConstNumber(newValue, original.getRawValue());
}
public Value dest() {
return outValue;
}
public boolean getBooleanValue() {
return !isZero();
}
public int getIntValue() {
assert outType() == ValueType.INT
|| outType() == ValueType.OBJECT; // Used for is-null conditionals.
return (int) value;
}
public long getLongValue() {
assert outType() == ValueType.LONG;
return value;
}
public float getFloatValue() {
assert outType() == ValueType.FLOAT;
return Float.intBitsToFloat((int) value);
}
public double getDoubleValue() {
assert outType() == ValueType.DOUBLE;
return Double.longBitsToDouble(value);
}
public long getRawValue() {
return value;
}
public boolean isZero() {
return value == 0;
}
public boolean isIntegerZero() {
return outType() == ValueType.INT && getIntValue() == 0;
}
public boolean isIntegerOne() {
return outType() == ValueType.INT && getIntValue() == 1;
}
public boolean isIntegerNegativeOne(NumericType type) {
assert type == NumericType.INT || type == NumericType.LONG;
if (type == NumericType.INT) {
return getIntValue() == -1;
}
return getLongValue() == -1;
}
@Override
public boolean instructionTypeCanBeCanonicalized() {
return true;
}
@Override
public void buildDex(DexBuilder builder) {
if (!dest().needsRegister()) {
builder.addNothing(this);
return;
}
int register = builder.allocatedRegister(dest(), getNumber());
if (outType().isObject() || outType().isSingle()) {
assert NumberUtils.is32Bit(value);
if ((register & 0xf) == register && NumberUtils.is4Bit(value)) {
builder.add(this, new Const4(register, (int) value));
} else if (NumberUtils.is16Bit(value)) {
builder.add(this, new Const16(register, (int) value));
} else if ((value & 0x0000ffffL) == 0) {
builder.add(this, new ConstHigh16(register, ((int) value) >>> 16));
} else {
builder.add(this, new Const(register, (int) value));
}
} else {
assert outType().isWide();
if (NumberUtils.is16Bit(value)) {
builder.add(this, new ConstWide16(register, (int) value));
} else if ((value & 0x0000ffffffffffffL) == 0) {
builder.add(this, new ConstWideHigh16(register, (int) (value >>> 48)));
} else if (NumberUtils.is32Bit(value)) {
builder.add(this, new ConstWide32(register, (int) value));
} else {
builder.add(this, new ConstWide(register, value));
}
}
}
@Override
public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
helper.storeOutValue(this, it);
}
@Override
public void buildCf(CfBuilder builder) {
if (outType().isObject()) {
builder.add(new CfConstNull());
} else {
builder.add(new CfConstNumber(value, outType()));
}
}
// Estimated size of the resulting instructions in code units (bytes in CF, 16-bit in Dex).
public static int estimatedSize(InternalOutputMode mode, ValueType type, long value) {
return mode.isGeneratingDex() ? estimatedDexSize(type, value) : estimatedCfSize(type, value);
}
private static int estimatedCfSize(ValueType type, long value) {
switch (type) {
case INT:
if (-1 <= value && value <= 5) {
return 1;
} else if (Byte.MIN_VALUE <= value && value <= Byte.MAX_VALUE) {
return 2;
} else {
return 3;
}
case LONG:
if (value == 0 || value == 1) {
return 1;
} else {
return 3;
}
case FLOAT:
if (value == 0 || value == 1 || value == 2) {
return CfConstNumber.isNegativeZeroFloat((float) value) ? 2 : 1;
} else {
return 3;
}
case DOUBLE:
if (value == 0 || value == 1) {
return CfConstNumber.isNegativeZeroDouble((double) value) ? 2 : 1;
} else {
return 3;
}
case OBJECT:
return 1;
}
throw new UnsupportedOperationException("Not a constant number");
}
private static int estimatedDexSize(ValueType type, long value) {
if (type.isSingle()) {
assert NumberUtils.is32Bit(value);
if (NumberUtils.is4Bit(value)) {
return Const4.SIZE;
} else if (NumberUtils.is16Bit(value)) {
return Const16.SIZE;
} else if ((value & 0x0000ffffL) == 0) {
return ConstHigh16.SIZE;
} else {
return Const.SIZE;
}
} else {
assert type.isWide();
if (NumberUtils.is16Bit(value)) {
return ConstWide16.SIZE;
} else if ((value & 0x0000ffffffffffffL) == 0) {
return ConstWideHigh16.SIZE;
} else if (NumberUtils.is32Bit(value)) {
return ConstWide32.SIZE;
} else {
return ConstWide.SIZE;
}
}
}
@Override
public int maxInValueRegister() {
assert false : "Const has no register arguments.";
return 0;
}
@Override
public int maxOutValueRegister() {
return Constants.U8BIT_MAX;
}
@Override
public String toString() {
if (outValue != null) {
return super.toString() + " " + value + " (" + getOutType() + ")";
} else {
return super.toString() + " " + value + " (dead)";
}
}
@Override
public boolean identicalNonValueNonPositionParts(Instruction other) {
if (other == this) {
return true;
}
if (!other.isConstNumber()) {
return false;
}
ConstNumber o = other.asConstNumber();
return o.outType() == outType() && o.value == value;
}
public boolean is8Bit() {
return NumberUtils.is8Bit(value);
}
public boolean negativeIs8Bit() {
return NumberUtils.negativeIs8Bit(value);
}
public boolean is16Bit() {
return NumberUtils.is16Bit(value);
}
public boolean negativeIs16Bit() {
return NumberUtils.negativeIs16Bit(value);
}
@Override
public boolean isOutConstant() {
return true;
}
@Override
public boolean isConstNumber() {
return true;
}
@Override
public ConstNumber asConstNumber() {
return this;
}
@Override
public DexType computeVerificationType(AppView<?> appView, TypeVerificationHelper helper) {
assert outType().isObject();
return appView.dexItemFactory().nullValueType;
}
@Override
public LatticeElement evaluate(IRCode code, Function<Value, LatticeElement> getLatticeElement) {
if (outValue.hasLocalInfo()) {
return Bottom.getInstance();
}
return new ConstLatticeElement(this);
}
@Override
public TypeElement evaluate(AppView<?> appView) {
return getOutType();
}
@Override
public boolean verifyTypes(AppView<?> appView) {
assert super.verifyTypes(appView);
assert !isZero() || getOutType().isPrimitiveType() || getOutType().isNullType();
return true;
}
@Override
public boolean outTypeKnownToBeBoolean(Set<Phi> seen) {
return this.value == 0 || this.value == 1;
}
@Override
public AbstractValue getAbstractValue(AppView<?> appView, DexType context) {
return appView.abstractValueFactory().createSingleNumberValue(value);
}
}