blob: 833ceaafbc4dc199d7cbbe1edb16e4180e7c97d2 [file] [log] [blame]
// Copyright (c) 2018, 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.classinliner;
import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionIterator;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.Phi.RegisterReadType;
import com.android.tools.r8.ir.code.Value;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
// Describes and caches what values are supposed to be used instead of field reads.
final class FieldValueHelper {
private final DexField field;
private final IRCode code;
private final Instruction root;
private final AppView<?> appView;
private Value defaultValue = null;
private final Map<BasicBlock, Value> ins = new IdentityHashMap<>();
private final Map<BasicBlock, Value> outs = new IdentityHashMap<>();
FieldValueHelper(DexField field, IRCode code, Instruction root, AppView<?> appView) {
this.field = field;
this.code = code;
this.root = root;
this.appView = appView;
// Verify that `root` is not aliased.
assert root.hasOutValue();
assert root.outValue() == root.outValue().getAliasedValue();
}
void replaceValue(Value oldValue, Value newValue) {
for (Entry<BasicBlock, Value> entry : ins.entrySet()) {
if (entry.getValue() == oldValue) {
entry.setValue(newValue);
}
}
for (Entry<BasicBlock, Value> entry : outs.entrySet()) {
if (entry.getValue() == oldValue) {
entry.setValue(newValue);
}
}
}
Value getValueForFieldRead(BasicBlock block, Instruction valueUser) {
assert valueUser != null;
Value value = getValueDefinedInTheBlock(block, valueUser);
return value != null ? value : getOrCreateInValue(block);
}
private Value getOrCreateOutValue(BasicBlock block) {
Value value = outs.get(block);
if (value != null) {
return value;
}
value = getValueDefinedInTheBlock(block, null);
if (value == null) {
// No value defined in the block.
value = getOrCreateInValue(block);
}
assert value != null;
outs.put(block, value);
return value;
}
private Value getOrCreateInValue(BasicBlock block) {
Value value = ins.get(block);
if (value != null) {
return value;
}
List<BasicBlock> predecessors = block.getPredecessors();
if (predecessors.size() == 1) {
value = getOrCreateOutValue(predecessors.get(0));
ins.put(block, value);
} else {
// Create phi, add it to the block, cache in ins map for future use.
Phi phi =
new Phi(
code.valueNumberGenerator.next(),
block,
TypeElement.fromDexType(field.type, maybeNull(), appView),
null,
RegisterReadType.NORMAL);
ins.put(block, phi);
List<Value> operands = new ArrayList<>();
for (BasicBlock predecessor : block.getPredecessors()) {
operands.add(getOrCreateOutValue(predecessor));
}
// Add phi, but don't remove trivial phis; since we cache the phi
// we just created for future use we should delay removing trivial
// phis until we are done with replacing fields reads.
phi.addOperands(operands, false);
TypeElement phiType = phi.computePhiType(appView);
assert phiType.lessThanOrEqual(phi.getType(), appView);
phi.setType(phiType);
value = phi;
}
assert value != null;
return value;
}
private Value getValueDefinedInTheBlock(BasicBlock block, Instruction stopAt) {
InstructionIterator iterator =
stopAt == null ? block.iterator(block.getInstructions().size()) : block.iterator(stopAt);
Instruction valueProducingInsn = null;
while (iterator.hasPrevious()) {
Instruction instruction = iterator.previous();
assert instruction != null;
if (instruction == root
|| (instruction.isInstancePut()
&& instruction.asInstancePut().getField() == field
&& instruction.asInstancePut().object().getAliasedValue() == root.outValue())) {
valueProducingInsn = instruction;
break;
}
}
if (valueProducingInsn == null) {
return null;
}
if (valueProducingInsn.isInstancePut()) {
return valueProducingInsn.asInstancePut().value();
}
assert root == valueProducingInsn;
if (defaultValue == null) {
InstructionListIterator it = block.listIterator(code, root);
// If we met newInstance it means that default value is supposed to be used.
if (field.type.isPrimitiveType()) {
defaultValue =
code.createValue(TypeElement.fromDexType(field.type, definitelyNotNull(), appView));
ConstNumber defaultValueInsn = new ConstNumber(defaultValue, 0);
defaultValueInsn.setPosition(root.getPosition());
it.add(defaultValueInsn);
} else {
defaultValue = it.insertConstNullInstruction(code, appView.options());
}
}
return defaultValue;
}
}