blob: 53dd0bf4909cd3e7b1cfe3f93ad39e50a21e6bb4 [file] [log] [blame]
// Copyright (c) 2021, 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.optimize.membervaluepropagation.assume;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.SingleFieldValue;
import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
import java.util.Objects;
public class AssumeInfo {
private static final AssumeInfo EMPTY =
new AssumeInfo(DynamicType.unknown(), AbstractValue.unknown(), false);
private final DynamicType assumeType;
private final AbstractValue assumeValue;
private final boolean isSideEffectFree;
private AssumeInfo(DynamicType assumeType, AbstractValue assumeValue, boolean isSideEffectFree) {
this.assumeType = assumeType;
this.assumeValue = assumeValue;
this.isSideEffectFree = isSideEffectFree;
}
public static Builder builder() {
return new Builder();
}
public static AssumeInfo create(
DynamicType assumeType, AbstractValue assumeValue, boolean isSideEffectFree) {
return assumeType.isUnknown() && assumeValue.isUnknown() && !isSideEffectFree
? empty()
: new AssumeInfo(assumeType, assumeValue, isSideEffectFree);
}
public static AssumeInfo empty() {
return EMPTY;
}
public DynamicType getAssumeType() {
return assumeType;
}
public AbstractValue getAssumeValue() {
return assumeValue;
}
public boolean isEmpty() {
if (this == empty()) {
return true;
}
assert !assumeType.isUnknown() || !assumeValue.isUnknown() || isSideEffectFree;
return false;
}
public boolean isSideEffectFree() {
return isSideEffectFree;
}
public AssumeInfo meet(AssumeInfo other) {
DynamicType meetType = internalMeetType(assumeType, other.assumeType);
AbstractValue meetValue = internalMeetValue(assumeValue, other.assumeValue);
boolean meetIsSideEffectFree =
internalMeetIsSideEffectFree(isSideEffectFree, other.isSideEffectFree);
return AssumeInfo.create(meetType, meetValue, meetIsSideEffectFree);
}
private static DynamicType internalMeetType(DynamicType type, DynamicType other) {
if (type.equals(other)) {
return type;
}
if (type.isUnknown()) {
return other;
}
if (other.isUnknown()) {
return type;
}
return DynamicType.unknown();
}
private static AbstractValue internalMeetValue(AbstractValue value, AbstractValue other) {
if (value.equals(other)) {
return value;
}
if (value.isUnknown()) {
return other;
}
if (other.isUnknown()) {
return value;
}
return AbstractValue.unknown();
}
private static boolean internalMeetIsSideEffectFree(
boolean isSideEffectFree, boolean otherIsSideEffectFree) {
return isSideEffectFree || otherIsSideEffectFree;
}
public AssumeInfo rewrittenWithLens(AppView<?> appView, GraphLens graphLens) {
// Verify that there is no need to rewrite the assumed type.
assert assumeType.isNotNullType() || assumeType.isUnknown();
// If the assumed value is a static field, then rewrite it.
if (assumeValue.isSingleFieldValue()) {
DexField field = assumeValue.asSingleFieldValue().getField();
DexField rewrittenField = graphLens.getRenamedFieldSignature(field);
if (rewrittenField != field) {
SingleFieldValue rewrittenAssumeValue =
appView
.abstractValueFactory()
.createSingleFieldValue(rewrittenField, ObjectState.empty());
return create(assumeType, rewrittenAssumeValue, isSideEffectFree);
}
}
return this;
}
public AssumeInfo withoutPrunedItems(PrunedItems prunedItems) {
// Verify that there is no need to prune the assumed type.
assert assumeType.isNotNullType() || assumeType.isUnknown();
// If the assumed value is a static field, and the static field is removed, then prune the
// assumed value.
if (assumeValue.isSingleFieldValue()
&& prunedItems.isRemoved(assumeValue.asSingleFieldValue().getField())) {
return create(assumeType, AbstractValue.unknown(), isSideEffectFree);
}
return this;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other == null || getClass() != other.getClass()) {
return false;
}
AssumeInfo assumeInfo = (AssumeInfo) other;
return assumeValue.equals(assumeInfo.assumeValue)
&& assumeType.equals(assumeInfo.assumeType)
&& isSideEffectFree == assumeInfo.isSideEffectFree;
}
@Override
public int hashCode() {
return Objects.hash(assumeValue, assumeType, isSideEffectFree);
}
public static class Builder {
private DynamicType assumeType = DynamicType.unknown();
private AbstractValue assumeValue = AbstractValue.unknown();
private boolean isSideEffectFree = false;
public Builder meet(AssumeInfo assumeInfo) {
return meetAssumeType(assumeInfo.assumeType)
.meetAssumeValue(assumeInfo.assumeValue)
.meetIsSideEffectFree(assumeInfo.isSideEffectFree);
}
public Builder meetAssumeType(DynamicType assumeType) {
this.assumeType = internalMeetType(this.assumeType, assumeType);
return this;
}
public Builder meetAssumeValue(AbstractValue assumeValue) {
this.assumeValue = internalMeetValue(this.assumeValue, assumeValue);
return this;
}
public Builder meetIsSideEffectFree(boolean isSideEffectFree) {
this.isSideEffectFree = internalMeetIsSideEffectFree(this.isSideEffectFree, isSideEffectFree);
return this;
}
public Builder setIsSideEffectFree() {
this.isSideEffectFree = true;
return this;
}
public AssumeInfo build() {
return create(assumeType, assumeValue, isSideEffectFree);
}
}
}