blob: d8b3d668c00a3be3cfa3cade2ad32f731915c7dc [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.shaking.rules;
import com.android.tools.r8.shaking.Enqueuer;
import com.android.tools.r8.shaking.MinimumKeepInfoCollection;
import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.BiConsumer;
public class ApplicableRulesEvaluatorImpl<T, R extends PendingConditionalRuleBase<T>>
extends ApplicableRulesEvaluator {
private final MinimumKeepInfoCollection rootConsequences;
// TODO(b/323816623): Revaluate these numbers. They are set low/tight now to hit in tests.
private static final int reallocMinThreshold = 1;
private static final int reallocRatioThreshold = 10;
private int prunedCount = 0;
private List<R> pendingConditionalRules;
private final List<MaterializedConditionalRule> materializedRules = new ArrayList<>();
private BiConsumer<R, Enqueuer> onSatisfiedRuleCallback;
ApplicableRulesEvaluatorImpl(
MinimumKeepInfoCollection rootConsequences, List<R> conditionalRules) {
this(rootConsequences, conditionalRules, (unusedRule, unusedEnqueuer) -> {});
}
ApplicableRulesEvaluatorImpl(
MinimumKeepInfoCollection rootConsequences,
List<R> conditionalRules,
BiConsumer<R, Enqueuer> onSatisfiedRuleCallback) {
assert !rootConsequences.isEmpty() || !conditionalRules.isEmpty();
this.rootConsequences = rootConsequences;
this.pendingConditionalRules = conditionalRules;
this.onSatisfiedRuleCallback = onSatisfiedRuleCallback;
}
@Override
public void evaluateUnconditionalRules(Enqueuer enqueuer) {
assert materializedRules.isEmpty();
if (!rootConsequences.isEmpty()) {
enqueuer.includeMinimumKeepInfo(rootConsequences);
}
}
@Override
public void evaluateConditionalRules(Enqueuer enqueuer) {
if (pendingConditionalRules.isEmpty()) {
return;
}
// TODO(b/323816623): If we tracked newly live, we could speed up finding rules.
// TODO(b/323816623): Parallelize this.
for (int i = 0; i < pendingConditionalRules.size(); i++) {
R rule = pendingConditionalRules.get(i);
if (rule != null && rule.isSatisfiedAfterUpdate(enqueuer)) {
++prunedCount;
pendingConditionalRules.set(i, null);
enqueuer.includeMinimumKeepInfo(rule.getConsequences());
materializedRules.add(rule.asMaterialized());
onSatisfiedRuleCallback.accept(rule, enqueuer);
}
}
if (prunedCount == pendingConditionalRules.size()) {
assert Iterables.all(pendingConditionalRules, Objects::isNull);
prunedCount = 0;
pendingConditionalRules = Collections.emptyList();
return;
}
int threshold =
Math.max(reallocMinThreshold, pendingConditionalRules.size() / reallocRatioThreshold);
if (prunedCount >= threshold) {
int newSize = pendingConditionalRules.size() - prunedCount;
List<R> newPending = new ArrayList<>(newSize);
for (R rule : pendingConditionalRules) {
if (rule != null) {
assert rule.isOutstanding();
newPending.add(rule);
}
}
assert newPending.size() == newSize;
prunedCount = 0;
pendingConditionalRules = newPending;
}
}
@Override
public MaterializedRules getMaterializedRules() {
return new MaterializedRules(rootConsequences, materializedRules);
}
}