Add NewUnboxedEnumInstance instruction for enum unboxing
Change-Id: Ieae19a988cfefe758610707fc1932af655a93405
diff --git a/src/main/java/com/android/tools/r8/cf/CfPrinter.java b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
index 64dd9a5..c6ba520 100644
--- a/src/main/java/com/android/tools/r8/cf/CfPrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
@@ -37,6 +37,7 @@
import com.android.tools.r8.cf.code.CfNeg;
import com.android.tools.r8.cf.code.CfNew;
import com.android.tools.r8.cf.code.CfNewArray;
+import com.android.tools.r8.cf.code.CfNewUnboxedEnum;
import com.android.tools.r8.cf.code.CfNop;
import com.android.tools.r8.cf.code.CfNumberConversion;
import com.android.tools.r8.cf.code.CfPosition;
@@ -503,6 +504,12 @@
}
}
+ public void print(CfNewUnboxedEnum newInstance) {
+ indent();
+ builder.append("newunboxedenum ");
+ appendClass(newInstance.getType());
+ }
+
public void print(CfMultiANewArray multiANewArray) {
indent();
builder.append("multianewarray ");
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNewUnboxedEnum.java b/src/main/java/com/android/tools/r8/cf/code/CfNewUnboxedEnum.java
new file mode 100644
index 0000000..89f8686
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNewUnboxedEnum.java
@@ -0,0 +1,124 @@
+// Copyright (c) 2017, 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.cf.code;
+
+import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.cf.code.CfFrame.FrameType;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.CfCompareHelper;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.InitClassLens;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.conversion.CfSourceCode;
+import com.android.tools.r8.ir.conversion.CfState;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
+import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
+import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
+import java.util.ListIterator;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/** The class-file representation of {@link com.android.tools.r8.ir.code.NewUnboxedEnumInstance}. */
+public class CfNewUnboxedEnum extends CfInstruction implements CfTypeInstruction {
+
+ private final DexType type;
+ private final int ordinal;
+
+ public CfNewUnboxedEnum(DexType type, int ordinal) {
+ this.type = type;
+ this.ordinal = ordinal;
+ }
+
+ @Override
+ public CfTypeInstruction asTypeInstruction() {
+ return this;
+ }
+
+ @Override
+ public boolean isTypeInstruction() {
+ return true;
+ }
+
+ @Override
+ public DexType getType() {
+ return type;
+ }
+
+ @Override
+ public CfInstruction withType(DexType newType) {
+ return new CfNewUnboxedEnum(newType, ordinal);
+ }
+
+ @Override
+ public int getCompareToId() {
+ return Opcodes.NEW;
+ }
+
+ @Override
+ public int internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ return type.acceptCompareTo(((CfNewUnboxedEnum) other).type, visitor);
+ }
+
+ @Override
+ public void write(
+ AppView<?> appView,
+ ProgramMethod context,
+ DexItemFactory dexItemFactory,
+ GraphLens graphLens,
+ InitClassLens initClassLens,
+ NamingLens namingLens,
+ LensCodeRewriterUtils rewriter,
+ MethodVisitor visitor) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public void print(CfPrinter printer) {
+ printer.print(this);
+ }
+
+ @Override
+ void internalRegisterUse(
+ UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) {
+ registry.registerNewUnboxedEnumInstance(type);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder, CfState state, CfSourceCode code) {
+ builder.addNewUnboxedEnumInstance(state.push(type).register, type, ordinal);
+ }
+
+ @Override
+ public ConstraintWithTarget inliningConstraint(
+ InliningConstraints inliningConstraints, CfCode code, ProgramMethod context) {
+ return inliningConstraints.forNewUnboxedEnumInstance(type, context);
+ }
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ... →
+ // ..., objectref
+ frameBuilder.push(FrameType.initialized(type));
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/DexNewUnboxedEnumInstance.java b/src/main/java/com/android/tools/r8/code/DexNewUnboxedEnumInstance.java
new file mode 100644
index 0000000..f9c26e6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DexNewUnboxedEnumInstance.java
@@ -0,0 +1,89 @@
+// 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.code;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
+import java.nio.ShortBuffer;
+
+/** The dex representation of {@link com.android.tools.r8.ir.code.NewUnboxedEnumInstance}. */
+public class DexNewUnboxedEnumInstance extends Format21c<DexType> {
+
+ public static final int OPCODE = 0x22;
+ public static final String NAME = "NewUnboxedEnumInstance";
+ public static final String SMALI_NAME = "new-unboxed-enum-instance";
+
+ private final int ordinal;
+
+ public DexNewUnboxedEnumInstance(int AA, DexType BBBB, int ordinal) {
+ super(AA, BBBB);
+ this.ordinal = ordinal;
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ @Override
+ public int getOpcode() {
+ throw new Unreachable();
+ }
+
+ @Override
+ void internalSubSpecify(StructuralSpecification<Format21c<DexType>, ?> spec) {
+ spec.withItem(i -> i.BBBB);
+ }
+
+ @Override
+ public void collectIndexedItems(
+ IndexedItemCollection indexedItems,
+ ProgramMethod context,
+ GraphLens graphLens,
+ LensCodeRewriterUtils rewriter) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public void write(
+ ShortBuffer dest,
+ ProgramMethod context,
+ GraphLens graphLens,
+ ObjectToOffsetMapping mapping,
+ LensCodeRewriterUtils rewriter) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerNewUnboxedEnumInstance(getType());
+ }
+
+ public DexType getType() {
+ return BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addNewUnboxedEnumInstance(AA, getType(), ordinal);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistry.java b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
index 708a02f..c9c0bc9 100644
--- a/src/main/java/com/android/tools/r8/graph/UseRegistry.java
+++ b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
@@ -55,6 +55,10 @@
registerTypeReference(type);
}
+ public void registerNewUnboxedEnumInstance(DexType type) {
+ registerTypeReference(type);
+ }
+
public abstract void registerStaticFieldRead(DexField field);
public void registerStaticFieldReadFromMethodHandle(DexField field) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java
index eb26afa..c791889 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.NewInstance;
+import com.android.tools.r8.ir.code.NewUnboxedEnumInstance;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.Sets;
import java.util.ArrayList;
@@ -153,5 +154,11 @@
markInitializedOnNormalExit(instruction.clazz);
return null;
}
+
+ @Override
+ public Void visit(NewUnboxedEnumInstance instruction) {
+ assert false;
+ return null;
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 708ffcf..3ac5e73b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -1011,6 +1011,14 @@
return null;
}
+ public boolean isNewUnboxedEnumInstance() {
+ return false;
+ }
+
+ public NewUnboxedEnumInstance asNewUnboxedEnumInstance() {
+ return null;
+ }
+
public boolean isNot() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionVisitor.java b/src/main/java/com/android/tools/r8/ir/code/InstructionVisitor.java
index a374439..8aa631d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionVisitor.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionVisitor.java
@@ -108,6 +108,8 @@
T visit(NewInstance instruction);
+ T visit(NewUnboxedEnumInstance instruction);
+
T visit(Not instruction);
T visit(NumberConversion instruction);
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java
new file mode 100644
index 0000000..8230766
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java
@@ -0,0 +1,161 @@
+// 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.CfNewUnboxedEnum;
+import com.android.tools.r8.code.DexNewUnboxedEnumInstance;
+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.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.VerifyTypesHelper;
+import com.android.tools.r8.ir.analysis.type.Nullability;
+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.ir.optimize.Inliner.ConstraintWithTarget;
+import com.android.tools.r8.ir.optimize.InliningConstraints;
+
+/**
+ * Special instruction used by {@link com.android.tools.r8.ir.optimize.enums.EnumUnboxer}.
+ *
+ * <p>When applying the enum unboxer to the application, we move the class initializer of each
+ * unboxed enum to its utility class, and change each {@link NewInstance} instruction that
+ * instantiates the unboxed enum into a {@link NewUnboxedEnumInstance} that holds the ordinal of the
+ * enum instance.
+ *
+ * <p>The {@link NewUnboxedEnumInstance} is an instruction that produces an (initialized) instance
+ * of the unboxed enum, i.e., the out-type is a non-nullable class type. This is important for the
+ * code to type check until lens code rewriting, which replaces the {@link NewUnboxedEnumInstance}
+ * instructions by {@link ConstNumber} instructions.
+ *
+ * <p>Note: The {@link NewUnboxedEnumInstance} is only used from {@link
+ * com.android.tools.r8.ir.optimize.enums.EnumUnboxer#unboxEnums} until the execution of the {@link
+ * com.android.tools.r8.ir.conversion.PostMethodProcessor}. There should be no instances of {@link
+ * NewUnboxedEnumInstance} (nor {@link CfNewUnboxedEnum}, {@link DexNewUnboxedEnumInstance}) after
+ * IR processing has finished.
+ */
+public class NewUnboxedEnumInstance extends Instruction {
+
+ public final DexType clazz;
+ private final int ordinal;
+
+ public NewUnboxedEnumInstance(DexType clazz, int ordinal, Value dest) {
+ super(dest);
+ assert clazz != null;
+ this.clazz = clazz;
+ this.ordinal = ordinal;
+ }
+
+ public int getOrdinal() {
+ return ordinal;
+ }
+
+ public DexType getType() {
+ return clazz;
+ }
+
+ @Override
+ public int opcode() {
+ return Opcodes.NEW_UNBOXED_ENUM_INSTANCE;
+ }
+
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ int dest = builder.allocatedRegister(outValue(), getNumber());
+ builder.add(this, new DexNewUnboxedEnumInstance(dest, clazz, ordinal));
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " " + clazz;
+ }
+
+ @Override
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
+ return other.isNewUnboxedEnumInstance() && other.asNewUnboxedEnumInstance().clazz == clazz;
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ assert false : "NewUnboxedEnumInstance has no register arguments";
+ return 0;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ return Constants.U8BIT_MAX;
+ }
+
+ @Override
+ public boolean instructionTypeCanThrow() {
+ // Depending on how this instruction is lowered to CF/DEX the instruction type may throw. If we
+ // lower the instruction to a const-number, then it can't throw, but if we lower it to something
+ // that triggers the class initialization of the enum utility class, then it could throw.
+ return true;
+ }
+
+ @Override
+ public boolean isNewUnboxedEnumInstance() {
+ return true;
+ }
+
+ @Override
+ public NewUnboxedEnumInstance asNewUnboxedEnumInstance() {
+ return this;
+ }
+
+ @Override
+ public ConstraintWithTarget inliningConstraint(
+ InliningConstraints inliningConstraints, ProgramMethod context) {
+ return inliningConstraints.forNewUnboxedEnumInstance(clazz, context);
+ }
+
+ @Override
+ public boolean hasInvariantOutType() {
+ return true;
+ }
+
+ @Override
+ public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+ helper.storeOutValue(this, it);
+ }
+
+ @Override
+ public void buildCf(CfBuilder builder) {
+ builder.add(new CfNewUnboxedEnum(clazz, ordinal));
+ }
+
+ @Override
+ public DexType computeVerificationType(AppView<?> appView, TypeVerificationHelper helper) {
+ return clazz;
+ }
+
+ @Override
+ public TypeElement evaluate(AppView<?> appView) {
+ return TypeElement.fromDexType(clazz, Nullability.definitelyNotNull(), appView);
+ }
+
+ @Override
+ public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
+ // Conservatively return true.
+ return true;
+ }
+
+ @Override
+ public boolean verifyTypes(AppView<?> appView, VerifyTypesHelper verifyTypesHelper) {
+ TypeElement type = getOutType();
+ assert type.isClassType();
+ assert type.asClassType().getClassType() == clazz || appView.options().testing.allowTypeErrors;
+ assert type.isDefinitelyNotNull();
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Opcodes.java b/src/main/java/com/android/tools/r8/ir/code/Opcodes.java
index 51b19d8e..d2c79d4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Opcodes.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Opcodes.java
@@ -56,22 +56,23 @@
int NEW_ARRAY_EMPTY = 47;
int NEW_ARRAY_FILLED_DATA = 48;
int NEW_INSTANCE = 49;
- int NOT = 50;
- int NUMBER_CONVERSION = 51;
- int OR = 52;
- int POP = 53;
- int REM = 54;
- int RETURN = 55;
- int SHL = 56;
- int SHR = 57;
- int STATIC_GET = 58;
- int STATIC_PUT = 59;
- int STORE = 60;
- int STRING_SWITCH = 61;
- int SUB = 62;
- int SWAP = 63;
- int THROW = 64;
- int USHR = 65;
- int XOR = 66;
- int UNINITIALIZED_THIS_LOCAL_READ = 67;
+ int NEW_UNBOXED_ENUM_INSTANCE = 50;
+ int NOT = 51;
+ int NUMBER_CONVERSION = 52;
+ int OR = 53;
+ int POP = 54;
+ int REM = 55;
+ int RETURN = 56;
+ int SHL = 57;
+ int SHR = 58;
+ int STATIC_GET = 59;
+ int STATIC_PUT = 60;
+ int STORE = 61;
+ int STRING_SWITCH = 62;
+ int SUB = 63;
+ int SWAP = 64;
+ int THROW = 65;
+ int USHR = 66;
+ int XOR = 67;
+ int UNINITIALIZED_THIS_LOCAL_READ = 68;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index ba1a15c..2563fa8 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -91,6 +91,7 @@
import com.android.tools.r8.ir.code.NewArrayEmpty;
import com.android.tools.r8.ir.code.NewArrayFilledData;
import com.android.tools.r8.ir.code.NewInstance;
+import com.android.tools.r8.ir.code.NewUnboxedEnumInstance;
import com.android.tools.r8.ir.code.Not;
import com.android.tools.r8.ir.code.NumberConversion;
import com.android.tools.r8.ir.code.NumberGenerator;
@@ -1813,6 +1814,14 @@
addInstruction(instruction);
}
+ public void addNewUnboxedEnumInstance(int dest, DexType type, int ordinal) {
+ TypeElement instanceType = TypeElement.fromDexType(type, definitelyNotNull(), appView);
+ Value out = writeRegister(dest, instanceType, ThrowingInfo.CAN_THROW);
+ NewUnboxedEnumInstance instruction = new NewUnboxedEnumInstance(type, ordinal, out);
+ assert instruction.instructionTypeCanThrow();
+ addInstruction(instruction);
+ }
+
public void addReturn(int value) {
DexType returnType = method.getDefinition().returnType();
if (returnType.isVoidType()) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index f44ec67..70a2316 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -26,6 +26,7 @@
import static com.android.tools.r8.ir.code.Opcodes.MOVE_EXCEPTION;
import static com.android.tools.r8.ir.code.Opcodes.NEW_ARRAY_EMPTY;
import static com.android.tools.r8.ir.code.Opcodes.NEW_INSTANCE;
+import static com.android.tools.r8.ir.code.Opcodes.NEW_UNBOXED_ENUM_INSTANCE;
import static com.android.tools.r8.ir.code.Opcodes.RETURN;
import static com.android.tools.r8.ir.code.Opcodes.STATIC_GET;
import static com.android.tools.r8.ir.code.Opcodes.STATIC_PUT;
@@ -590,6 +591,9 @@
}
break;
+ case NEW_UNBOXED_ENUM_INSTANCE:
+ break;
+
case RETURN:
{
Return ret = current.asReturn();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
index e00c267..17db09c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
@@ -292,6 +292,10 @@
return ConstraintWithTarget.classIsVisible(context, type, appView);
}
+ public ConstraintWithTarget forNewUnboxedEnumInstance(DexType type, ProgramMethod context) {
+ return ConstraintWithTarget.ALWAYS;
+ }
+
public ConstraintWithTarget forAssume() {
return ConstraintWithTarget.ALWAYS;
}