blob: 4689b6de4ade31d5d13707d08ef46790e5c7f5e0 [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.desugar.records;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.FieldResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.ArrayPut;
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.MemberType;
import com.android.tools.r8.ir.code.NewArrayEmpty;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.RecordFieldValues;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.Timing;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
/** Used to shrink record field arrays in dex compilations */
public class RecordFieldValuesRewriter {
private final AppView<AppInfoWithLiveness> appView;
private final IRConverter irConverter;
public static RecordFieldValuesRewriter create(AppView<AppInfoWithLiveness> appView) {
if (appView.enableWholeProgramOptimizations()
&& appView.options().isGeneratingDex()
&& appView.options().testing.enableRecordModeling) {
return new RecordFieldValuesRewriter(appView);
}
return null;
}
private RecordFieldValuesRewriter(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
irConverter = new IRConverter(appView, Timing.empty());
}
// Called after final tree shaking, prune and minify field names and field values.
// At least one instruction is a newRecordFieldArray.
public void rewriteRecordFieldValues() {
for (DexMethod recordFieldValuesReference : appView.appInfo().recordFieldValuesReferences) {
DexClass dexClass =
appView.contextIndependentDefinitionFor(recordFieldValuesReference.getHolderType());
assert dexClass.isProgramClass();
ProgramMethod programMethod =
dexClass.asProgramClass().lookupProgramMethod(recordFieldValuesReference);
assert programMethod != null;
rewriteRecordFieldValues(programMethod);
}
}
public void rewriteRecordFieldValues(ProgramMethod programMethod) {
IRCode irCode =
programMethod
.getDefinition()
.getCode()
.buildIR(programMethod, appView, programMethod.getOrigin());
boolean done = false;
ListIterator<BasicBlock> blockIterator = irCode.listIterator();
while (blockIterator.hasNext()) {
BasicBlock block = blockIterator.next();
InstructionListIterator iterator = block.listIterator(irCode);
while (iterator.hasNext()) {
Instruction instruction = iterator.next();
if (instruction.isRecordFieldValues()) {
rewriteRecordFieldArray(
instruction.asRecordFieldValues(), irCode, blockIterator, iterator);
done = true;
}
}
}
assert done;
irConverter.removeDeadCodeAndFinalizeIR(
irCode, OptimizationFeedbackIgnore.getInstance(), Timing.empty());
}
public void rewriteRecordFieldArray(
RecordFieldValues recordFieldArray,
IRCode code,
ListIterator<BasicBlock> blockIterator,
InstructionListIterator iterator) {
List<Value> newInValues = computePresentFields(recordFieldArray, code.context());
ConstNumber arrayLengthIntConstant = code.createIntConstant(newInValues.size());
Position constantPosition =
appView.options().debug ? Position.none() : recordFieldArray.getPosition();
arrayLengthIntConstant.setPosition(constantPosition);
iterator.previous();
iterator.add(arrayLengthIntConstant);
iterator.next();
NewArrayEmpty newArrayEmpty =
new NewArrayEmpty(
recordFieldArray.outValue(),
arrayLengthIntConstant.outValue(),
appView.dexItemFactory().objectArrayType);
newArrayEmpty.setPosition(recordFieldArray.getPosition());
iterator.replaceCurrentInstruction(newArrayEmpty);
for (int i = 0; i < newInValues.size(); i++) {
ConstNumber intConstantI = code.createIntConstant(i);
intConstantI.setPosition(constantPosition);
iterator.add(intConstantI);
ArrayPut arrayPut =
new ArrayPut(
MemberType.OBJECT,
newArrayEmpty.outValue(),
intConstantI.outValue(),
newInValues.get(i));
iterator.add(arrayPut);
arrayPut.setPosition(recordFieldArray.getPosition());
}
if (newArrayEmpty.getBlock().hasCatchHandlers()) {
splitIfCatchHandlers(code, newArrayEmpty.getBlock(), blockIterator);
}
}
private void splitIfCatchHandlers(
IRCode code,
BasicBlock blockWithIncorrectThrowingInstructions,
ListIterator<BasicBlock> blockIterator) {
InstructionListIterator instructionsIterator =
blockWithIncorrectThrowingInstructions.listIterator(code);
BasicBlock currentBlock = blockWithIncorrectThrowingInstructions;
while (currentBlock != null && instructionsIterator.hasNext()) {
Instruction throwingInstruction =
instructionsIterator.nextUntil(Instruction::instructionTypeCanThrow);
BasicBlock nextBlock;
if (throwingInstruction != null) {
nextBlock = instructionsIterator.split(code, blockIterator);
// Back up to before the split before inserting catch handlers.
blockIterator.previous();
nextBlock.copyCatchHandlers(code, blockIterator, currentBlock, appView.options());
BasicBlock b = blockIterator.next();
assert b == nextBlock;
// Switch iteration to the split block.
instructionsIterator = nextBlock.listIterator(code);
currentBlock = nextBlock;
} else {
assert !instructionsIterator.hasNext();
instructionsIterator = null;
currentBlock = null;
}
}
}
private List<Value> computePresentFields(
RecordFieldValues recordFieldValues, ProgramMethod context) {
List<Value> inValues = recordFieldValues.inValues();
DexField[] fields = recordFieldValues.getFields();
assert inValues.size() == fields.length;
List<Value> newInValues = new ArrayList<>();
for (int index = 0; index < fields.length; index++) {
FieldResolutionResult resolution =
appView
.appInfo()
.resolveField(appView.graphLens().getRenamedFieldSignature(fields[index]), context);
if (resolution.isSuccessfulResolution()) {
newInValues.add(inValues.get(index));
}
}
return newInValues;
}
}