| // 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; |
| import com.android.tools.r8.ir.optimize.enums.EnumUnboxer; |
| import com.android.tools.r8.ir.optimize.enums.EnumUnboxerImpl; |
| |
| /** |
| * Special instruction used by {@link EnumUnboxerImpl}. |
| * |
| * <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 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; |
| } |
| } |