blob: 544c3653f3db03d382c1d4280e68b7d2eb735806 [file] [log] [blame]
// Copyright (c) 2024, 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.optimize.argumentpropagation.codescanner;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.Action;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.collections.ProgramFieldMap;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Supplier;
public class FieldStateCollection {
private final ProgramFieldMap<NonEmptyValueState> fieldStates;
private FieldStateCollection(ProgramFieldMap<NonEmptyValueState> fieldStates) {
this.fieldStates = fieldStates;
}
public static FieldStateCollection createConcurrent() {
return new FieldStateCollection(ProgramFieldMap.createConcurrent());
}
public NonEmptyValueState addTemporaryFieldState(
AppView<AppInfoWithLiveness> appView,
ProgramField field,
Supplier<NonEmptyValueState> fieldStateSupplier,
Timing timing) {
return addTemporaryFieldState(
field,
fieldStateSupplier,
timing,
(existingFieldState, fieldStateToAdd) ->
existingFieldState.mutableJoin(
appView,
fieldStateToAdd,
field.getType(),
StateCloner.getCloner(),
Action.empty()));
}
/**
* This intentionally takes a {@link Supplier<NonEmptyValueState>} to avoid computing the field
* state for a given field put when nothing is known about the value of the field.
*/
public NonEmptyValueState addTemporaryFieldState(
ProgramField field,
Supplier<NonEmptyValueState> fieldStateSupplier,
Timing timing,
BiFunction<ConcreteValueState, ConcreteValueState, NonEmptyValueState> joiner) {
ValueState joinState =
fieldStates.compute(
field,
(f, existingFieldState) -> {
if (existingFieldState == null) {
return fieldStateSupplier.get();
}
assert !existingFieldState.isBottom();
if (existingFieldState.isUnknown()) {
return existingFieldState;
}
NonEmptyValueState fieldStateToAdd = fieldStateSupplier.get();
if (fieldStateToAdd.isUnknown()) {
return fieldStateToAdd;
}
timing.begin("Join temporary field state");
ConcreteValueState existingConcreteFieldState = existingFieldState.asConcrete();
ConcreteValueState concreteFieldStateToAdd = fieldStateToAdd.asConcrete();
NonEmptyValueState joinResult =
joiner.apply(existingConcreteFieldState, concreteFieldStateToAdd);
timing.end();
return joinResult;
});
assert joinState.isNonEmpty();
return joinState.asNonEmpty();
}
public void forEach(BiConsumer<ProgramField, ValueState> consumer) {
fieldStates.forEach(consumer);
}
public ValueState get(ProgramField field) {
NonEmptyValueState fieldState = fieldStates.get(field);
return fieldState != null ? fieldState : ValueState.bottom(field);
}
public ValueState remove(ProgramField field) {
ValueState removed = fieldStates.remove(field);
return removed != null ? removed : ValueState.bottom(field);
}
public ValueState set(ProgramField field, ValueState state) {
if (state.isNonEmpty()) {
return set(field, state.asNonEmpty());
}
return remove(field);
}
public ValueState set(ProgramField field, NonEmptyValueState state) {
NonEmptyValueState previous = fieldStates.put(field, state);
return previous != null ? previous : ValueState.bottom(field);
}
}