blob: 880ab89bc67e346c62f4395b8ab417b3320833f0 [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 com.android.tools.r8.graph.DexField;
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.InstructionListIterator;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.code.ValueType;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.LinkedList;
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 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) {
this.field = field;
this.code = code;
this.root = root;
}
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, ValueType.fromDexType(field.type), null);
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);
value = phi;
}
assert value != null;
return value;
}
private Value getValueDefinedInTheBlock(BasicBlock block, Instruction stopAt) {
InstructionListIterator iterator = stopAt == null ?
block.listIterator(block.getInstructions().size()) : block.listIterator(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() == root.outValue())) {
valueProducingInsn = instruction;
break;
}
}
if (valueProducingInsn == null) {
return null;
}
if (valueProducingInsn.isInstancePut()) {
return valueProducingInsn.asInstancePut().value();
}
assert root == valueProducingInsn;
if (defaultValue == null) {
// If we met newInstance it means that default value is supposed to be used.
defaultValue = code.createValue(ValueType.fromDexType(field.type));
ConstNumber defaultValueInsn = new ConstNumber(defaultValue, 0);
defaultValueInsn.setPosition(root.getPosition());
LinkedList<Instruction> instructions = block.getInstructions();
instructions.add(instructions.indexOf(root) + 1, defaultValueInsn);
defaultValueInsn.setBlock(block);
}
return defaultValue;
}
}