blob: c6fdbab899708b84d1eb3da342220933522e9993 [file] [log] [blame]
// Copyright (c) 2019, 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.analysis.sideeffect;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.ValueMayDependOnEnvironmentAnalysis;
import com.android.tools.r8.ir.code.ArrayPut;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.StaticPut;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.Sets;
import java.util.Set;
public class ClassInitializerSideEffectAnalysis {
public enum ClassInitializerSideEffect {
SIDE_EFFECTS_THAT_CANNOT_BE_POSTPONED,
SIDE_EFFECTS_THAT_CAN_BE_POSTPONED,
NONE;
public boolean canBePostponed() {
return this != SIDE_EFFECTS_THAT_CANNOT_BE_POSTPONED;
}
public boolean isNone() {
return this == NONE;
}
}
/**
* A class initializer cannot be postponed if it writes a static field of another class, or if any
* non-static-put instructions may have side effects.
*/
public static ClassInitializerSideEffect classInitializerCanBePostponed(
AppView<AppInfoWithLiveness> appView, IRCode code) {
ProgramMethod context = code.context();
// Will be set to true if the control flow must be independent of the environment in order for
// this class initializer to be postponeable.
boolean controlFlowRequiredToBeIndependentOfControlFlow = false;
// The set of values that must be independent of the environment in order for this class
// initializer to be postponeable.
Set<Value> valuesRequiredToBeIndependentOfEnvironment = Sets.newIdentityHashSet();
for (Instruction instruction : code.instructions()) {
// Array stores are observable if they mutate a non-local array or if they may throw.
if (instruction.isArrayPut()) {
ArrayPut arrayPut = instruction.asArrayPut();
Value array = arrayPut.array().getAliasedValue();
if (!array.isDefinedByInstructionSatisfying(Instruction::isCreatingArray)
|| arrayPut.instructionInstanceCanThrow(appView, context)) {
return ClassInitializerSideEffect.SIDE_EFFECTS_THAT_CANNOT_BE_POSTPONED;
}
continue;
}
// Array creations are observable if they may throw.
if (instruction.isInvokeNewArray()
|| instruction.isNewArrayEmpty()
|| instruction.isNewArrayFilledData()) {
if (instruction.instructionInstanceCanThrow(appView, context)) {
return ClassInitializerSideEffect.SIDE_EFFECTS_THAT_CANNOT_BE_POSTPONED;
}
continue;
}
if (instruction.isStaticPut()) {
StaticPut staticPut = instruction.asStaticPut();
DexEncodedField field =
appView.appInfo().resolveField(staticPut.getField()).getResolvedField();
if (field == null
|| field.getHolderType() != context.getHolderType()
|| instruction.instructionInstanceCanThrow(appView, context)) {
return ClassInitializerSideEffect.SIDE_EFFECTS_THAT_CANNOT_BE_POSTPONED;
}
controlFlowRequiredToBeIndependentOfControlFlow = true;
valuesRequiredToBeIndependentOfEnvironment.add(staticPut.value());
continue;
}
// For other instructions, bail out if they may have side effects.
if (instruction.instructionMayHaveSideEffects(appView, context)) {
return ClassInitializerSideEffect.SIDE_EFFECTS_THAT_CANNOT_BE_POSTPONED;
}
}
if (controlFlowRequiredToBeIndependentOfControlFlow) {
if (code.controlFlowMayDependOnEnvironment(valuesRequiredToBeIndependentOfEnvironment::add)) {
return ClassInitializerSideEffect.SIDE_EFFECTS_THAT_CANNOT_BE_POSTPONED;
}
}
if (!valuesRequiredToBeIndependentOfEnvironment.isEmpty()) {
ValueMayDependOnEnvironmentAnalysis environmentAnalysis =
new ValueMayDependOnEnvironmentAnalysis(appView, code);
if (environmentAnalysis.anyValueMayDependOnEnvironment(
valuesRequiredToBeIndependentOfEnvironment)) {
return ClassInitializerSideEffect.SIDE_EFFECTS_THAT_CANNOT_BE_POSTPONED;
}
return ClassInitializerSideEffect.SIDE_EFFECTS_THAT_CAN_BE_POSTPONED;
}
return ClassInitializerSideEffect.NONE;
}
}