blob: 3b4f8dcd5a24bade16b5feee854e961636fe7dcb [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.analysis.fieldaccess;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.bytecodemetadata.BytecodeInstructionMetadata.Builder;
import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
import com.android.tools.r8.ir.code.FieldGet;
import com.android.tools.r8.ir.code.FieldInstruction;
import com.android.tools.r8.ir.code.FieldPut;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.WorkList;
public class FieldReadForWriteAnalysis {
private final AppView<AppInfoWithLiveness> appView;
FieldReadForWriteAnalysis(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
}
public void recordFieldAccess(
FieldInstruction instruction,
ProgramField field,
BytecodeMetadataProvider.Builder bytecodeMetadataProviderBuilder) {
if (instruction.isFieldPut()) {
return;
}
FieldGet fieldGet = instruction.asFieldGet();
if (isValueOnlyUsedToWriteField(fieldGet.outValue(), field)) {
bytecodeMetadataProviderBuilder.addMetadata(instruction, Builder::setIsReadForWrite);
}
}
private boolean isValueOnlyUsedToWriteField(Value value, ProgramField field) {
WorkList<Instruction> users = WorkList.newIdentityWorkList(value.uniqueUsers());
if (!enqueueUsersForAnalysis(value, users)) {
return false;
}
boolean foundWrite = false;
while (users.hasNext()) {
Instruction user = users.next();
if (user.isArithmeticBinop() || user.isLogicalBinop() || user.isUnop()) {
if (enqueueUsersForAnalysis(user.outValue(), users)) {
// OK.
continue;
}
} else if (user.isFieldPut()) {
FieldPut fieldPut = user.asFieldPut();
DexField writtenFieldReference = fieldPut.getField();
if (writtenFieldReference.match(field.getReference())
&& fieldPut.isStaticPut() == field.getAccessFlags().isStatic()) {
ProgramField writtenField =
appView.appInfo().resolveField(writtenFieldReference).getProgramField();
if (writtenField != null && writtenField.isStructurallyEqualTo(field)) {
// OK.
foundWrite = true;
continue;
}
}
}
return false;
}
return foundWrite;
}
private boolean enqueueUsersForAnalysis(Value value, WorkList<Instruction> users) {
if (value.hasDebugUsers() || value.hasPhiUsers()) {
return false;
}
users.addIfNotSeen(value.uniqueUsers());
return true;
}
}