Revert "Use field-never-read-before-write analysis in field assignment tracker" This reverts commit c88998af9b6480aedd935388711f2896b1e1ef30. Reason for revert: b/276916308, b/276936384 Change-Id: Ife9ff9598e813fb3b363f928baa253a985d3ecc7
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java index 97ceb6e..a664d9d 100644 --- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java +++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -747,7 +747,7 @@ dataResourceConsumer.accept(adapted, options.reporter); } else { options.reporter.warning( - new StringDiagnostic("Resource '" + adapted.getName() + "' already exists.")); + new StringDiagnostic("Resource '" + file.getName() + "' already exists.")); } options.reporter.failIfPendingErrors(); }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java index 637026f..d871616 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
@@ -11,7 +11,6 @@ import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.ProgramField; import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider; -import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis; import com.android.tools.r8.ir.code.FieldInstruction; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.Instruction; @@ -70,7 +69,6 @@ IRCode code, BytecodeMetadataProvider.Builder bytecodeMetadataProviderBuilder, OptimizationFeedback feedback, - FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis, MethodProcessor methodProcessor) { if (!methodProcessor.isPrimaryMethodProcessor()) { return; @@ -87,8 +85,7 @@ appView.appInfo().resolveField(fieldInstruction.getField()).getProgramField(); if (field != null) { if (fieldAssignmentTracker != null) { - fieldAssignmentTracker.recordFieldAccess( - fieldInstruction, field, fieldReadBeforeWriteAnalysis); + fieldAssignmentTracker.recordFieldAccess(fieldInstruction, field, code.context()); } if (fieldBitAccessAnalysis != null) { fieldBitAccessAnalysis.recordFieldAccess(
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java index dc90a85..0db5825 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
@@ -19,7 +19,6 @@ import com.android.tools.r8.graph.ProgramField; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.horizontalclassmerging.HorizontalClassMergerUtils; -import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis; import com.android.tools.r8.ir.analysis.fieldaccess.state.ConcreteArrayTypeFieldState; import com.android.tools.r8.ir.analysis.fieldaccess.state.ConcreteClassTypeFieldState; import com.android.tools.r8.ir.analysis.fieldaccess.state.ConcretePrimitiveTypeFieldState; @@ -35,8 +34,6 @@ import com.android.tools.r8.ir.analysis.value.SingleValue; import com.android.tools.r8.ir.analysis.value.UnknownValue; import com.android.tools.r8.ir.code.FieldInstruction; -import com.android.tools.r8.ir.code.FieldPut; -import com.android.tools.r8.ir.code.InstancePut; import com.android.tools.r8.ir.code.InvokeDirect; import com.android.tools.r8.ir.code.NewInstance; import com.android.tools.r8.ir.code.Value; @@ -182,23 +179,16 @@ }); } - void recordFieldAccess( - FieldInstruction instruction, - ProgramField field, - FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) { + void recordFieldAccess(FieldInstruction instruction, ProgramField field, ProgramMethod context) { if (instruction.isFieldPut()) { - recordFieldPut(instruction.asFieldPut(), field, fieldReadBeforeWriteAnalysis); + recordFieldPut(field, instruction.value(), context); } } - private void recordFieldPut( - FieldPut fieldPut, - ProgramField field, - FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) { + private void recordFieldPut(ProgramField field, Value value, ProgramMethod context) { // For now only attempt to prove that fields are definitely null. In order to prove a single // value for fields that are not definitely null, we need to prove that the given field is never // read before it is written. - Value value = fieldPut.value(); AbstractValue abstractValue = value.isZero() ? abstractValueFactory.createZeroValue() : AbstractValue.unknown(); fieldStates.compute( @@ -213,8 +203,12 @@ return ConcretePrimitiveTypeFieldState.create(abstractValue); } assert fieldType.isClassType(); - return ConcreteClassTypeFieldState.create( - abstractValue, getDynamicType(fieldPut, field, fieldReadBeforeWriteAnalysis)); + DynamicType dynamicType = + WideningUtils.widenDynamicNonReceiverType( + appView, + value.getDynamicType(appView).withNullability(Nullability.maybeNull()), + field.getType()); + return ConcreteClassTypeFieldState.create(abstractValue, dynamicType); } if (fieldState.isUnknown()) { @@ -237,31 +231,10 @@ ConcreteClassTypeFieldState classFieldState = fieldState.asClass(); return classFieldState.mutableJoin( - appView, - abstractValue, - getDynamicType(fieldPut, field, fieldReadBeforeWriteAnalysis), - field); + appView, abstractValue, value.getDynamicType(appView), field); }); } - private DynamicType getDynamicType( - FieldPut fieldPut, - ProgramField field, - FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) { - DynamicTypeWithUpperBound dynamicType = fieldPut.value().getDynamicType(appView); - if (fieldPut.isInstancePut()) { - InstancePut instancePut = fieldPut.asInstancePut(); - if (fieldReadBeforeWriteAnalysis.isInstanceFieldMaybeReadBeforeInstruction( - instancePut.object(), field.getDefinition(), instancePut)) { - dynamicType = dynamicType.withNullability(Nullability.maybeNull()); - } - } else if (fieldReadBeforeWriteAnalysis.isStaticFieldMaybeReadBeforeInstruction( - field.getDefinition(), fieldPut.asStaticPut())) { - dynamicType = dynamicType.withNullability(Nullability.maybeNull()); - } - return WideningUtils.widenDynamicNonReceiverType(appView, dynamicType, field.getType()); - } - void recordAllocationSite(NewInstance instruction, DexProgramClass clazz, ProgramMethod context) { Map<DexEncodedField, AbstractValue> abstractInstanceFieldValuesForClass = abstractInstanceFieldValues.get(clazz);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysis.java deleted file mode 100644 index fb6bae0..0000000 --- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysis.java +++ /dev/null
@@ -1,33 +0,0 @@ -// Copyright (c) 2023, 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.readbeforewrite; - -import com.android.tools.r8.graph.AppView; -import com.android.tools.r8.graph.DexEncodedField; -import com.android.tools.r8.graph.ProgramMethod; -import com.android.tools.r8.ir.code.IRCode; -import com.android.tools.r8.ir.code.Instruction; -import com.android.tools.r8.ir.code.Value; - -public abstract class FieldReadBeforeWriteAnalysis { - - public static FieldReadBeforeWriteAnalysis create( - AppView<?> appView, IRCode code, ProgramMethod context) { - if (appView.hasLiveness()) { - return new FieldReadBeforeWriteAnalysisImpl(appView.withLiveness(), code, context); - } - return trivial(); - } - - public static TrivialFieldReadBeforeWriteAnalysis trivial() { - return new TrivialFieldReadBeforeWriteAnalysis(); - } - - public abstract boolean isInstanceFieldMaybeReadBeforeInstruction( - Value receiver, DexEncodedField field, Instruction instruction); - - public abstract boolean isStaticFieldMaybeReadBeforeInstruction( - DexEncodedField field, Instruction instruction); -}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysisImpl.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysisImpl.java deleted file mode 100644 index 7e7ac89..0000000 --- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/FieldReadBeforeWriteAnalysisImpl.java +++ /dev/null
@@ -1,209 +0,0 @@ -// Copyright (c) 2023, 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.readbeforewrite; - -import com.android.tools.r8.graph.AppView; -import com.android.tools.r8.graph.DexEncodedField; -import com.android.tools.r8.graph.ProgramMethod; -import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet; -import com.android.tools.r8.ir.analysis.fieldvalueanalysis.ConcreteMutableFieldSet; -import com.android.tools.r8.ir.analysis.fieldvalueanalysis.EmptyFieldSet; -import com.android.tools.r8.ir.analysis.fieldvalueanalysis.KnownFieldSet; -import com.android.tools.r8.ir.analysis.fieldvalueanalysis.UnknownFieldSet; -import com.android.tools.r8.ir.code.BasicBlock; -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.Value; -import com.android.tools.r8.shaking.AppInfoWithLiveness; -import com.android.tools.r8.utils.DequeUtils; -import java.util.Deque; -import java.util.IdentityHashMap; -import java.util.Map; - -class FieldReadBeforeWriteAnalysisImpl extends FieldReadBeforeWriteAnalysis { - - private final AppView<AppInfoWithLiveness> appView; - private final IRCode code; - private final ProgramMethod context; - - private Map<BasicBlock, AbstractFieldSet> fieldsMaybeReadBeforeBlockInclusiveCache; - - public FieldReadBeforeWriteAnalysisImpl( - AppView<AppInfoWithLiveness> appView, IRCode code, ProgramMethod context) { - this.appView = appView; - this.code = code; - this.context = context; - } - - @Override - public boolean isInstanceFieldMaybeReadBeforeInstruction( - Value receiver, DexEncodedField field, Instruction instruction) { - if (!code.context().getDefinition().isInstanceInitializer() - || receiver.getAliasedValue() != code.getThis()) { - return true; - } - return isFieldMaybeReadBeforeInstructionInInitializer(field, instruction); - } - - @Override - public boolean isStaticFieldMaybeReadBeforeInstruction( - DexEncodedField field, Instruction instruction) { - if (!code.context().getDefinition().isClassInitializer() - || field.getHolderType() != code.context().getHolderType()) { - return true; - } - return isFieldMaybeReadBeforeInstructionInInitializer(field, instruction); - } - - public boolean isFieldMaybeReadBeforeInstructionInInitializer( - DexEncodedField field, Instruction instruction) { - BasicBlock block = instruction.getBlock(); - - // First check if the field may be read in any of the (transitive) predecessor blocks. - if (fieldMaybeReadBeforeBlock(field, block)) { - return true; - } - - // Then check if any of the instructions that precede the given instruction in the current block - // may read the field. - InstructionIterator instructionIterator = block.iterator(); - while (instructionIterator.hasNext()) { - Instruction current = instructionIterator.next(); - if (current == instruction) { - break; - } - if (current.readSet(appView, context).contains(field)) { - return true; - } - } - - // Otherwise, the field is not read prior to the given instruction. - return false; - } - - private boolean fieldMaybeReadBeforeBlock(DexEncodedField field, BasicBlock block) { - for (BasicBlock predecessor : block.getPredecessors()) { - if (fieldMaybeReadBeforeBlockInclusive(field, predecessor)) { - return true; - } - } - return false; - } - - private boolean fieldMaybeReadBeforeBlockInclusive(DexEncodedField field, BasicBlock block) { - return getOrCreateFieldsMaybeReadBeforeBlockInclusive().get(block).contains(field); - } - - private Map<BasicBlock, AbstractFieldSet> getOrCreateFieldsMaybeReadBeforeBlockInclusive() { - if (fieldsMaybeReadBeforeBlockInclusiveCache == null) { - fieldsMaybeReadBeforeBlockInclusiveCache = createFieldsMaybeReadBeforeBlockInclusive(); - } - return fieldsMaybeReadBeforeBlockInclusiveCache; - } - - /** - * Eagerly creates a mapping from each block to the set of fields that may be read in that block - * and its transitive predecessors. - */ - private Map<BasicBlock, AbstractFieldSet> createFieldsMaybeReadBeforeBlockInclusive() { - Map<BasicBlock, AbstractFieldSet> result = new IdentityHashMap<>(); - Deque<BasicBlock> worklist = DequeUtils.newArrayDeque(code.entryBlock()); - while (!worklist.isEmpty()) { - BasicBlock block = worklist.removeFirst(); - boolean seenBefore = result.containsKey(block); - AbstractFieldSet readSet = - result.computeIfAbsent(block, ignore -> EmptyFieldSet.getInstance()); - if (readSet.isTop()) { - // We already have unknown information for this block. - continue; - } - - assert readSet.isKnownFieldSet(); - KnownFieldSet knownReadSet = readSet.asKnownFieldSet(); - int oldSize = seenBefore ? knownReadSet.size() : -1; - - // Everything that is read in the predecessor blocks should also be included in the read set - // for the current block, so here we join the information from the predecessor blocks into the - // current read set. - boolean blockOrPredecessorMaybeReadAnyField = false; - for (BasicBlock predecessor : block.getPredecessors()) { - AbstractFieldSet predecessorReadSet = - result.getOrDefault(predecessor, EmptyFieldSet.getInstance()); - if (predecessorReadSet.isBottom()) { - continue; - } - if (predecessorReadSet.isTop()) { - blockOrPredecessorMaybeReadAnyField = true; - break; - } - assert predecessorReadSet.isConcreteFieldSet(); - if (!knownReadSet.isConcreteFieldSet()) { - knownReadSet = new ConcreteMutableFieldSet(); - } - knownReadSet.asConcreteFieldSet().addAll(predecessorReadSet.asConcreteFieldSet()); - } - - if (!blockOrPredecessorMaybeReadAnyField) { - // Finally, we update the read set with the fields that are read by the instructions in the - // current block. This can be skipped if the block has already been processed. - if (seenBefore) { - assert verifyFieldSetContainsAllFieldReadsInBlock(knownReadSet, block, context); - } else { - for (Instruction instruction : block.getInstructions()) { - AbstractFieldSet instructionReadSet = instruction.readSet(appView, context); - if (instructionReadSet.isBottom()) { - continue; - } - if (instructionReadSet.isTop()) { - blockOrPredecessorMaybeReadAnyField = true; - break; - } - if (!knownReadSet.isConcreteFieldSet()) { - knownReadSet = new ConcreteMutableFieldSet(); - } - knownReadSet.asConcreteFieldSet().addAll(instructionReadSet.asConcreteFieldSet()); - } - } - } - - boolean changed = false; - if (blockOrPredecessorMaybeReadAnyField) { - // Record that this block reads all fields. - result.put(block, UnknownFieldSet.getInstance()); - changed = true; - } else { - if (knownReadSet != readSet) { - result.put(block, knownReadSet.asConcreteFieldSet()); - } - if (knownReadSet.size() != oldSize) { - assert knownReadSet.size() > oldSize; - changed = true; - } - } - - if (changed) { - // Rerun the analysis for all successors because the state of the current block changed. - worklist.addAll(block.getSuccessors()); - } - } - return result; - } - - private boolean verifyFieldSetContainsAllFieldReadsInBlock( - KnownFieldSet readSet, BasicBlock block, ProgramMethod context) { - for (Instruction instruction : block.getInstructions()) { - AbstractFieldSet instructionReadSet = instruction.readSet(appView, context); - assert !instructionReadSet.isTop(); - if (instructionReadSet.isBottom()) { - continue; - } - for (DexEncodedField field : instructionReadSet.asConcreteFieldSet().getFields()) { - assert readSet.contains(field); - } - } - return true; - } -}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/TrivialFieldReadBeforeWriteAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/TrivialFieldReadBeforeWriteAnalysis.java deleted file mode 100644 index 9cd76dc..0000000 --- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/readbeforewrite/TrivialFieldReadBeforeWriteAnalysis.java +++ /dev/null
@@ -1,24 +0,0 @@ -// Copyright (c) 2023, 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.readbeforewrite; - -import com.android.tools.r8.graph.DexEncodedField; -import com.android.tools.r8.ir.code.Instruction; -import com.android.tools.r8.ir.code.Value; - -class TrivialFieldReadBeforeWriteAnalysis extends FieldReadBeforeWriteAnalysis { - - @Override - public boolean isInstanceFieldMaybeReadBeforeInstruction( - Value receiver, DexEncodedField field, Instruction instruction) { - return true; - } - - @Override - public boolean isStaticFieldMaybeReadBeforeInstruction( - DexEncodedField field, Instruction instruction) { - return true; - } -}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java index 854d046..b18d02a 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
@@ -39,7 +39,7 @@ return this; } - public Set<DexEncodedField> getFields() { + Set<DexEncodedField> getFields() { if (InternalOptions.assertionsEnabled()) { return Collections.unmodifiableSet(fields); }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java index 50c075e..9c09ef2 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
@@ -10,14 +10,13 @@ import com.android.tools.r8.graph.DexValue; import com.android.tools.r8.graph.ProgramField; import com.android.tools.r8.graph.ProgramMethod; -import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis; import com.android.tools.r8.ir.code.BasicBlock; import com.android.tools.r8.ir.code.DominatorTree; import com.android.tools.r8.ir.code.DominatorTree.Assumption; import com.android.tools.r8.ir.code.FieldInstruction; import com.android.tools.r8.ir.code.IRCode; -import com.android.tools.r8.ir.code.InstancePut; import com.android.tools.r8.ir.code.Instruction; +import com.android.tools.r8.ir.code.InstructionIterator; import com.android.tools.r8.ir.code.InvokeDirect; import com.android.tools.r8.ir.code.Value; import com.android.tools.r8.ir.optimize.ClassInitializerDefaultsOptimization.ClassInitializerDefaultsResult; @@ -25,11 +24,14 @@ import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfo; import com.android.tools.r8.ir.optimize.info.field.UnknownInstanceFieldInitializationInfo; import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.utils.DequeUtils; import com.android.tools.r8.utils.ListUtils; import java.util.ArrayList; +import java.util.Deque; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; public abstract class FieldValueAnalysis { @@ -51,20 +53,16 @@ final OptimizationFeedback feedback; private DominatorTree dominatorTree; - private FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis; + private Map<BasicBlock, AbstractFieldSet> fieldsMaybeReadBeforeBlockInclusiveCache; final Map<DexEncodedField, List<FieldInitializationInfo>> putsPerField = new IdentityHashMap<>(); FieldValueAnalysis( - AppView<AppInfoWithLiveness> appView, - IRCode code, - OptimizationFeedback feedback, - FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) { + AppView<AppInfoWithLiveness> appView, IRCode code, OptimizationFeedback feedback) { this.appView = appView; this.code = code; - this.context = code.context(); this.feedback = feedback; - this.fieldReadBeforeWriteAnalysis = fieldReadBeforeWriteAnalysis; + this.context = code.context(); } DominatorTree getOrCreateDominatorTree() { @@ -74,6 +72,13 @@ return dominatorTree; } + private Map<BasicBlock, AbstractFieldSet> getOrCreateFieldsMaybeReadBeforeBlockInclusive() { + if (fieldsMaybeReadBeforeBlockInclusiveCache == null) { + fieldsMaybeReadBeforeBlockInclusiveCache = createFieldsMaybeReadBeforeBlockInclusive(); + } + return fieldsMaybeReadBeforeBlockInclusiveCache; + } + boolean isInstanceFieldValueAnalysis() { return false; } @@ -90,9 +95,9 @@ return null; } - abstract boolean isSubjectToOptimizationIgnoringPinning(ProgramField field); + abstract boolean isSubjectToOptimizationIgnoringPinning(DexEncodedField field); - abstract boolean isSubjectToOptimization(ProgramField field); + abstract boolean isSubjectToOptimization(DexEncodedField field); void recordFieldPut(DexEncodedField field, Instruction instruction) { recordFieldPut(field, instruction, UnknownInstanceFieldInitializationInfo.getInstance()); @@ -122,12 +127,13 @@ DexField field = fieldPut.getField(); ProgramField programField = appInfo.resolveField(field).getProgramField(); if (programField != null) { - if (isSubjectToOptimization(programField)) { - recordFieldPut(programField.getDefinition(), fieldPut); + DexEncodedField encodedField = programField.getDefinition(); + if (isSubjectToOptimization(encodedField)) { + recordFieldPut(encodedField, fieldPut); } else if (isStaticFieldValueAnalysis() && programField.getHolder().isEnum() - && isSubjectToOptimizationIgnoringPinning(programField)) { - recordFieldPut(programField.getDefinition(), fieldPut); + && isSubjectToOptimizationIgnoringPinning(encodedField)) { + recordFieldPut(encodedField, fieldPut); } } } else if (isInstanceFieldValueAnalysis() @@ -138,54 +144,186 @@ } } - boolean checkDominance = !isStraightLineCode; List<BasicBlock> normalExitBlocks = code.computeNormalExitBlocks(); - putsPerField.forEach( - (field, fieldPuts) -> { - if (fieldPuts.size() > 1) { - return; - } - FieldInitializationInfo info = ListUtils.first(fieldPuts); - Instruction instruction = info.instruction; - if (instruction.isInvokeDirect()) { - asInstanceFieldValueAnalysis() - .recordInstanceFieldIsInitializedWithInfo( - field, info.instanceFieldInitializationInfo); - return; - } - FieldInstruction fieldPut = instruction.asFieldInstruction(); - if (checkDominance - && !getOrCreateDominatorTree() - .dominatesAllOf(fieldPut.getBlock(), normalExitBlocks)) { - return; - } - boolean priorReadsWillReadSameValue = - !classInitializerDefaultsResult.hasStaticValue(field) && fieldPut.value().isZero(); - if (!priorReadsWillReadSameValue) { - if (fieldPut.isInstancePut()) { - InstancePut instancePut = fieldPut.asInstancePut(); - if (fieldReadBeforeWriteAnalysis.isInstanceFieldMaybeReadBeforeInstruction( - instancePut.object(), field, instancePut)) { - return; - } - } else { - if (fieldReadBeforeWriteAnalysis.isStaticFieldMaybeReadBeforeInstruction( - field, fieldPut)) { - // TODO(b/172528424): Generalize to InstanceFieldValueAnalysis. - // At this point the value read in the field can be only the default static value, - // if read prior to the put, or the value put, if read after the put. We still want - // to record it because the default static value is typically null/0, so code - // present after a null/0 check can take advantage of the optimization. - DexValue valueBeforePut = classInitializerDefaultsResult.getStaticValue(field); - asStaticFieldValueAnalysis() - .updateFieldOptimizationInfoWith2Values( - field, fieldPut.value(), valueBeforePut); - return; - } + for (Entry<DexEncodedField, List<FieldInitializationInfo>> entry : putsPerField.entrySet()) { + DexEncodedField field = entry.getKey(); + List<FieldInitializationInfo> fieldPuts = entry.getValue(); + if (fieldPuts.size() > 1) { + continue; + } + FieldInitializationInfo info = ListUtils.first(fieldPuts); + Instruction instruction = info.instruction; + if (instruction.isInvokeDirect()) { + asInstanceFieldValueAnalysis() + .recordInstanceFieldIsInitializedWithInfo(field, info.instanceFieldInitializationInfo); + continue; + } + FieldInstruction fieldPut = instruction.asFieldInstruction(); + if (!isStraightLineCode) { + if (!getOrCreateDominatorTree().dominatesAllOf(fieldPut.getBlock(), normalExitBlocks)) { + continue; + } + } + boolean priorReadsWillReadSameValue = + !classInitializerDefaultsResult.hasStaticValue(field) && fieldPut.value().isZero(); + if (!priorReadsWillReadSameValue && fieldMaybeReadBeforeInstruction(field, fieldPut)) { + // TODO(b/172528424): Generalize to InstanceFieldValueAnalysis. + if (isStaticFieldValueAnalysis()) { + // At this point the value read in the field can be only the default static value, if read + // prior to the put, or the value put, if read after the put. We still want to record it + // because the default static value is typically null/0, so code present after a null/0 + // check can take advantage of the optimization. + DexValue valueBeforePut = classInitializerDefaultsResult.getStaticValue(field); + asStaticFieldValueAnalysis() + .updateFieldOptimizationInfoWith2Values(field, fieldPut.value(), valueBeforePut); + } + continue; + } + updateFieldOptimizationInfo(field, fieldPut, fieldPut.value()); + } + } + + private boolean fieldMaybeReadBeforeInstruction( + DexEncodedField encodedField, Instruction instruction) { + BasicBlock block = instruction.getBlock(); + + // First check if the field may be read in any of the (transitive) predecessor blocks. + if (fieldMaybeReadBeforeBlock(encodedField, block)) { + return true; + } + + // Then check if any of the instructions that precede the given instruction in the current block + // may read the field. + InstructionIterator instructionIterator = block.iterator(); + while (instructionIterator.hasNext()) { + Instruction current = instructionIterator.next(); + if (current == instruction) { + break; + } + if (current.readSet(appView, context).contains(encodedField)) { + return true; + } + } + + // Otherwise, the field is not read prior to the given instruction. + return false; + } + + private boolean fieldMaybeReadBeforeBlock(DexEncodedField encodedField, BasicBlock block) { + for (BasicBlock predecessor : block.getPredecessors()) { + if (fieldMaybeReadBeforeBlockInclusive(encodedField, predecessor)) { + return true; + } + } + return false; + } + + private boolean fieldMaybeReadBeforeBlockInclusive( + DexEncodedField encodedField, BasicBlock block) { + return getOrCreateFieldsMaybeReadBeforeBlockInclusive().get(block).contains(encodedField); + } + + /** + * Eagerly creates a mapping from each block to the set of fields that may be read in that block + * and its transitive predecessors. + */ + private Map<BasicBlock, AbstractFieldSet> createFieldsMaybeReadBeforeBlockInclusive() { + Map<BasicBlock, AbstractFieldSet> result = new IdentityHashMap<>(); + Deque<BasicBlock> worklist = DequeUtils.newArrayDeque(code.entryBlock()); + while (!worklist.isEmpty()) { + BasicBlock block = worklist.removeFirst(); + boolean seenBefore = result.containsKey(block); + AbstractFieldSet readSet = + result.computeIfAbsent(block, ignore -> EmptyFieldSet.getInstance()); + if (readSet.isTop()) { + // We already have unknown information for this block. + continue; + } + + assert readSet.isKnownFieldSet(); + KnownFieldSet knownReadSet = readSet.asKnownFieldSet(); + int oldSize = seenBefore ? knownReadSet.size() : -1; + + // Everything that is read in the predecessor blocks should also be included in the read set + // for the current block, so here we join the information from the predecessor blocks into the + // current read set. + boolean blockOrPredecessorMaybeReadAnyField = false; + for (BasicBlock predecessor : block.getPredecessors()) { + AbstractFieldSet predecessorReadSet = + result.getOrDefault(predecessor, EmptyFieldSet.getInstance()); + if (predecessorReadSet.isBottom()) { + continue; + } + if (predecessorReadSet.isTop()) { + blockOrPredecessorMaybeReadAnyField = true; + break; + } + assert predecessorReadSet.isConcreteFieldSet(); + if (!knownReadSet.isConcreteFieldSet()) { + knownReadSet = new ConcreteMutableFieldSet(); + } + knownReadSet.asConcreteFieldSet().addAll(predecessorReadSet.asConcreteFieldSet()); + } + + if (!blockOrPredecessorMaybeReadAnyField) { + // Finally, we update the read set with the fields that are read by the instructions in the + // current block. This can be skipped if the block has already been processed. + if (seenBefore) { + assert verifyFieldSetContainsAllFieldReadsInBlock(knownReadSet, block, context); + } else { + for (Instruction instruction : block.getInstructions()) { + AbstractFieldSet instructionReadSet = instruction.readSet(appView, context); + if (instructionReadSet.isBottom()) { + continue; } + if (instructionReadSet.isTop()) { + blockOrPredecessorMaybeReadAnyField = true; + break; + } + if (!knownReadSet.isConcreteFieldSet()) { + knownReadSet = new ConcreteMutableFieldSet(); + } + knownReadSet.asConcreteFieldSet().addAll(instructionReadSet.asConcreteFieldSet()); } - updateFieldOptimizationInfo(field, fieldPut, fieldPut.value()); - }); + } + } + + boolean changed = false; + if (blockOrPredecessorMaybeReadAnyField) { + // Record that this block reads all fields. + result.put(block, UnknownFieldSet.getInstance()); + changed = true; + } else { + if (knownReadSet != readSet) { + result.put(block, knownReadSet.asConcreteFieldSet()); + } + if (knownReadSet.size() != oldSize) { + assert knownReadSet.size() > oldSize; + changed = true; + } + } + + if (changed) { + // Rerun the analysis for all successors because the state of the current block changed. + worklist.addAll(block.getSuccessors()); + } + } + return result; + } + + private boolean verifyFieldSetContainsAllFieldReadsInBlock( + KnownFieldSet readSet, BasicBlock block, ProgramMethod context) { + for (Instruction instruction : block.getInstructions()) { + AbstractFieldSet instructionReadSet = instruction.readSet(appView, context); + assert !instructionReadSet.isTop(); + if (instructionReadSet.isBottom()) { + continue; + } + for (DexEncodedField field : instructionReadSet.asConcreteFieldSet().getFields()) { + assert readSet.contains(field); + } + } + return true; } abstract void updateFieldOptimizationInfo(
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java index 9ae014f..c4061ec 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
@@ -11,9 +11,7 @@ import com.android.tools.r8.graph.DexClassAndMethod; import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.ProgramField; import com.android.tools.r8.graph.ProgramMethod; -import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis; import com.android.tools.r8.ir.analysis.type.ClassTypeElement; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.analysis.value.AbstractValue; @@ -52,10 +50,9 @@ AppView<AppInfoWithLiveness> appView, IRCode code, OptimizationFeedback feedback, - FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis, DexClassAndMethod parentConstructor, InvokeDirect parentConstructorCall) { - super(appView, code, feedback, fieldReadBeforeWriteAnalysis); + super(appView, code, feedback); this.factory = appView.instanceFieldInitializationInfoFactory(); this.parentConstructor = parentConstructor; this.parentConstructorCall = parentConstructorCall; @@ -70,11 +67,10 @@ IRCode code, ClassInitializerDefaultsResult classInitializerDefaultsResult, OptimizationFeedback feedback, - FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis, Timing timing) { timing.begin("Analyze instance initializer"); InstanceFieldInitializationInfoCollection result = - run(appView, code, classInitializerDefaultsResult, feedback, fieldReadBeforeWriteAnalysis); + run(appView, code, classInitializerDefaultsResult, feedback); timing.end(); return result; } @@ -83,8 +79,7 @@ AppView<?> appView, IRCode code, ClassInitializerDefaultsResult classInitializerDefaultsResult, - OptimizationFeedback feedback, - FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) { + OptimizationFeedback feedback) { assert appView.appInfo().hasLiveness(); assert appView.enableWholeProgramOptimizations(); assert code.context().getDefinition().isInstanceInitializer(); @@ -106,7 +101,6 @@ appView.withLiveness(), code, feedback, - fieldReadBeforeWriteAnalysis, parentConstructor, parentConstructorCall); analysis.computeFieldOptimizationInfo(classInitializerDefaultsResult); @@ -125,12 +119,12 @@ } @Override - boolean isSubjectToOptimization(ProgramField field) { - return !field.getAccessFlags().isStatic() && field.getHolderType() == context.getHolderType(); + boolean isSubjectToOptimization(DexEncodedField field) { + return !field.isStatic() && field.getHolderType() == context.getHolderType(); } @Override - boolean isSubjectToOptimizationIgnoringPinning(ProgramField field) { + boolean isSubjectToOptimizationIgnoringPinning(DexEncodedField field) { throw new Unreachable("Used by static analysis only."); } @@ -209,7 +203,7 @@ if (abstractValue.isSingleValue()) { return abstractValue.asSingleValue(); } - DexType fieldType = field.getType(); + DexType fieldType = field.type(); if (fieldType.isClassType()) { ClassTypeElement dynamicLowerBoundType = value.getDynamicLowerBoundType(appView); TypeElement dynamicUpperBoundType = value.getDynamicUpperBoundType(appView); @@ -235,7 +229,7 @@ private boolean fieldNeverWrittenBetweenInstancePutAndMethodExit( DexEncodedField field, InstancePut instancePut) { - if (field.getAccessFlags().isFinal()) { + if (field.isFinal()) { return true; }
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java index 5f8381d..4df6b75 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
@@ -15,8 +15,6 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.DexValue; import com.android.tools.r8.graph.DexValue.DexValueNull; -import com.android.tools.r8.graph.ProgramField; -import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis; import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound; import com.android.tools.r8.ir.analysis.type.Nullability; import com.android.tools.r8.ir.analysis.value.AbstractValue; @@ -51,11 +49,8 @@ private final Map<Value, AbstractValue> computedValues = new IdentityHashMap<>(); private StaticFieldValueAnalysis( - AppView<AppInfoWithLiveness> appView, - IRCode code, - OptimizationFeedback feedback, - FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis) { - super(appView, code, feedback, fieldReadBeforeWriteAnalysis); + AppView<AppInfoWithLiveness> appView, IRCode code, OptimizationFeedback feedback) { + super(appView, code, feedback); builder = StaticFieldValues.builder(code.context().getHolder()); } @@ -64,15 +59,13 @@ IRCode code, ClassInitializerDefaultsResult classInitializerDefaultsResult, OptimizationFeedback feedback, - FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis, Timing timing) { assert appView.appInfo().hasLiveness(); assert appView.enableWholeProgramOptimizations(); assert code.context().getDefinition().isClassInitializer(); timing.begin("Analyze class initializer"); StaticFieldValues result = - new StaticFieldValueAnalysis( - appView.withLiveness(), code, feedback, fieldReadBeforeWriteAnalysis) + new StaticFieldValueAnalysis(appView.withLiveness(), code, feedback) .analyze(classInitializerDefaultsResult); timing.end(); return result; @@ -124,22 +117,19 @@ } @Override - boolean isSubjectToOptimization(ProgramField field) { - return field.getAccessFlags().isStatic() + boolean isSubjectToOptimization(DexEncodedField field) { + return field.isStatic() && field.getHolderType() == context.getHolderType() - && appView - .appInfo() - .isFieldOnlyWrittenInMethod(field.getDefinition(), context.getDefinition()); + && appView.appInfo().isFieldOnlyWrittenInMethod(field, context.getDefinition()); } @Override - boolean isSubjectToOptimizationIgnoringPinning(ProgramField field) { - return field.getAccessFlags().isStatic() + boolean isSubjectToOptimizationIgnoringPinning(DexEncodedField field) { + return field.isStatic() && field.getHolderType() == context.getHolderType() && appView .appInfo() - .isFieldOnlyWrittenInMethodIgnoringPinning( - field.getDefinition(), context.getDefinition()); + .isFieldOnlyWrittenInMethodIgnoringPinning(field, context.getDefinition()); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java index bc02268..fc8d1f2 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -18,7 +18,6 @@ import com.android.tools.r8.ir.analysis.VerifyTypesHelper; import com.android.tools.r8.ir.analysis.constant.SparseConditionalConstantPropagation; import com.android.tools.r8.ir.analysis.fieldaccess.FieldAccessAnalysis; -import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis; import com.android.tools.r8.ir.analysis.fieldvalueanalysis.InstanceFieldValueAnalysis; import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValueAnalysis; import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues; @@ -980,16 +979,10 @@ timing.end(); } - FieldReadBeforeWriteAnalysis fieldReadBeforeWriteAnalysis = - FieldReadBeforeWriteAnalysis.create(appView, code, method); if (fieldAccessAnalysis != null) { timing.begin("Analyze field accesses"); fieldAccessAnalysis.recordFieldAccesses( - code, - bytecodeMetadataProviderBuilder, - feedback, - fieldReadBeforeWriteAnalysis, - methodProcessor); + code, bytecodeMetadataProviderBuilder, feedback, methodProcessor); if (classInitializerDefaultsResult != null) { fieldAccessAnalysis.acceptClassInitializerDefaultsResult(classInitializerDefaultsResult); } @@ -1006,21 +999,11 @@ if (method.getDefinition().isClassInitializer()) { staticFieldValues = StaticFieldValueAnalysis.run( - appView, - code, - classInitializerDefaultsResult, - feedback, - fieldReadBeforeWriteAnalysis, - timing); + appView, code, classInitializerDefaultsResult, feedback, timing); } else { instanceFieldInitializationInfos = InstanceFieldValueAnalysis.run( - appView, - code, - classInitializerDefaultsResult, - feedback, - fieldReadBeforeWriteAnalysis, - timing); + appView, code, classInitializerDefaultsResult, feedback, timing); } } enumUnboxer.recordEnumState(method.getHolder(), staticFieldValues);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java index c92342f..f8e784e 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -420,10 +420,9 @@ } builder.setParent(invokedMethod); } else { - builder.markAllFieldsAsRead(); - if (invoke.instructionMayHaveSideEffects(appView, context)) { - builder.setMayHaveOtherSideEffectsThanInstanceFieldAssignments(); - } + builder + .markAllFieldsAsRead() + .setMayHaveOtherSideEffectsThanInstanceFieldAssignments(); for (Value inValue : invoke.inValues()) { if (couldBeReceiverValue(inValue, receiver, aliasesThroughAssumeAndCheckCasts)) { builder.setReceiverMayEscapeOutsideConstructorChain(); @@ -454,10 +453,9 @@ case INVOKE_VIRTUAL: { InvokeMethod invoke = instruction.asInvokeMethod(); - builder.markAllFieldsAsRead(); - if (invoke.instructionMayHaveSideEffects(appView, context)) { - builder.setMayHaveOtherSideEffectsThanInstanceFieldAssignments(); - } + builder + .markAllFieldsAsRead() + .setMayHaveOtherSideEffectsThanInstanceFieldAssignments(); for (Value argument : invoke.arguments()) { if (couldBeReceiverValue(argument, receiver, aliasesThroughAssumeAndCheckCasts)) { builder.setReceiverMayEscapeOutsideConstructorChain();
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java index 07a5bb2..39d8035 100644 --- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java +++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -954,6 +954,14 @@ return staticInitializer != null && isFieldOnlyWrittenInMethod(field, staticInitializer); } + public boolean mayPropagateArgumentsTo(ProgramMethod method) { + DexMethod reference = method.getReference(); + return method.getDefinition().hasCode() + && !method.getDefinition().isLibraryMethodOverride().isPossiblyTrue() + && !neverReprocess.contains(reference) + && !keepInfo.getMethodInfo(method).isPinned(options()); + } + public boolean mayPropagateValueFor( AppView<AppInfoWithLiveness> appView, DexClassAndMember<?, ?> member) { assert checkIfObsolete();
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java index bb382f8..2cdeffd 100644 --- a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java +++ b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
@@ -26,7 +26,6 @@ import com.android.tools.r8.graph.DirectMappedDexApplication; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider; -import com.android.tools.r8.ir.analysis.fieldaccess.readbeforewrite.FieldReadBeforeWriteAnalysis; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer; import com.android.tools.r8.ir.conversion.MethodProcessorWithWave; @@ -97,11 +96,7 @@ method -> { IRCode code = method.buildIR(appView); fieldAccessAnalysis.recordFieldAccesses( - code, - BytecodeMetadataProvider.builder(), - feedback, - FieldReadBeforeWriteAnalysis.trivial(), - new PrimaryMethodProcessorMock()); + code, BytecodeMetadataProvider.builder(), feedback, new PrimaryMethodProcessorMock()); }); int bitsReadInBitField = feedback.bitsReadPerField.getInt(uniqueFieldByName(clazz, "bitField"));
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonNullInstanceFieldPropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonNullInstanceFieldPropagationTest.java deleted file mode 100644 index 972cc79..0000000 --- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonNullInstanceFieldPropagationTest.java +++ /dev/null
@@ -1,68 +0,0 @@ -// Copyright (c) 2023, 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.membervaluepropagation; - -import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertEquals; - -import com.android.tools.r8.TestBase; -import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.utils.codeinspector.ClassSubject; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameter; -import org.junit.runners.Parameterized.Parameters; - -@RunWith(Parameterized.class) -public class NonNullInstanceFieldPropagationTest extends TestBase { - - @Parameter(0) - public TestParameters parameters; - - @Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); - } - - @Test - public void test() throws Exception { - testForR8(parameters.getBackend()) - .addInnerClasses(getClass()) - .addKeepMainRule(Main.class) - .setMinApi(parameters) - .compile() - .inspect( - inspector -> { - ClassSubject mainClassSubject = inspector.clazz(Main.class); - assertThat(mainClassSubject, isPresent()); - assertEquals(1, inspector.allClasses().size()); - assertEquals(1, mainClassSubject.allMethods().size()); - assertEquals(0, mainClassSubject.allFields().size()); - }) - .run(parameters.getRuntime(), Main.class) - .assertSuccessWithEmptyOutput(); - } - - static class Main { - - public static void main(String[] args) { - Main p = new Main(); - p.mThreadChecker.assertOnValidThread(); - } - - public final ThreadChecker mThreadChecker = new ThreadChecker(); - } - - static class ThreadChecker { - public void assertOnValidThread() { - if (System.currentTimeMillis() == 0) { - throw new RuntimeException(); - } - } - } -}
diff --git a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java index 8078ed6..92404f0 100644 --- a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java +++ b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
@@ -101,7 +101,6 @@ " void <init>();", "}", "-neverclassinline class *", - "-nohorizontalclassmerging class adaptresourcefilenames.B", "-nohorizontalclassmerging class adaptresourcefilenames.pkg.C", "-nohorizontalclassmerging class adaptresourcefilenames.pkg.innerpkg.D"); }
diff --git a/src/test/java/com/android/tools/r8/shaking/KeepClassMembersFieldTest.java b/src/test/java/com/android/tools/r8/shaking/KeepClassMembersFieldTest.java index babd7a7..31d557e 100644 --- a/src/test/java/com/android/tools/r8/shaking/KeepClassMembersFieldTest.java +++ b/src/test/java/com/android/tools/r8/shaking/KeepClassMembersFieldTest.java
@@ -59,10 +59,7 @@ Bar value = new Bar(); public static void main(String[] args) { - Foo foo = new Foo(); - if (System.currentTimeMillis() < 0) { - System.out.println(foo); - } + new Foo(); } } }