| // 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; |
| } |
| } |