| // Copyright (c) 2022, 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; | |
| import static com.android.tools.r8.utils.MapUtils.ignoreKey; | |
| import com.android.tools.r8.graph.AppView; | |
| import com.android.tools.r8.graph.DexClassAndMember; | |
| import com.android.tools.r8.graph.DexMember; | |
| import com.android.tools.r8.graph.PrunedItems; | |
| import com.android.tools.r8.graph.lens.GraphLens; | |
| import com.android.tools.r8.ir.analysis.type.DynamicType; | |
| import com.android.tools.r8.ir.analysis.value.AbstractValue; | |
| import com.android.tools.r8.ir.optimize.membervaluepropagation.assume.AssumeInfo; | |
| import com.android.tools.r8.utils.MapUtils; | |
| import com.android.tools.r8.utils.timing.Timing; | |
| import java.util.IdentityHashMap; | |
| import java.util.Map; | |
| import java.util.concurrent.ConcurrentHashMap; | |
| import java.util.function.Consumer; | |
| public class AssumeInfoCollection { | |
| private final Map<DexMember<?, ?>, AssumeInfo> backing; | |
| AssumeInfoCollection(Map<DexMember<?, ?>, AssumeInfo> backing) { | |
| assert backing.values().stream().noneMatch(AssumeInfo::isEmpty); | |
| this.backing = backing; | |
| } | |
| public static Builder builder() { | |
| return new Builder(); | |
| } | |
| public boolean contains(DexClassAndMember<?, ?> member) { | |
| return backing.containsKey(member.getReference()); | |
| } | |
| public AssumeInfo get(DexMember<?, ?> member) { | |
| return backing.getOrDefault(member, AssumeInfo.empty()); | |
| } | |
| public AssumeInfo get(DexClassAndMember<?, ?> member) { | |
| return get(member.getReference()); | |
| } | |
| public boolean isEmpty() { | |
| return backing.isEmpty(); | |
| } | |
| public boolean isMaterializableInAllContexts( | |
| AppView<? extends AppInfoWithLiveness> appView, DexClassAndMember<?, ?> member) { | |
| AbstractValue assumeValue = get(member).getAssumeValue(); | |
| return assumeValue.isSingleValue() | |
| && assumeValue.asSingleValue().isMaterializableInAllContexts(appView); | |
| } | |
| public boolean isSideEffectFree(DexMember<?, ?> member) { | |
| return get(member).isSideEffectFree(); | |
| } | |
| public boolean isSideEffectFree(DexClassAndMember<?, ?> member) { | |
| return isSideEffectFree(member.getReference()); | |
| } | |
| public AssumeInfoCollection rewrittenWithLens( | |
| AppView<?> appView, GraphLens graphLens, GraphLens appliedLens, Timing timing) { | |
| return timing.time( | |
| "Rewrite AssumeInfoCollection", () -> rewrittenWithLens(appView, graphLens, appliedLens)); | |
| } | |
| private AssumeInfoCollection rewrittenWithLens( | |
| AppView<?> appView, GraphLens graphLens, GraphLens appliedLens) { | |
| Map<DexMember<?, ?>, AssumeInfo> rewrittenCollection = new IdentityHashMap<>(); | |
| backing.forEach( | |
| (reference, info) -> { | |
| DexMember<?, ?> rewrittenReference = | |
| graphLens.getRenamedMemberSignature(reference, appliedLens); | |
| AssumeInfo rewrittenInfo = info.rewrittenWithLens(appView, graphLens); | |
| assert !rewrittenInfo.isEmpty(); | |
| rewrittenCollection.put(rewrittenReference, rewrittenInfo); | |
| }); | |
| return new AssumeInfoCollection(rewrittenCollection); | |
| } | |
| public AssumeInfoCollection withoutPrunedItems(PrunedItems prunedItems, Timing timing) { | |
| timing.begin("Prune AssumeInfoCollection"); | |
| Map<DexMember<?, ?>, AssumeInfo> rewrittenCollection = new IdentityHashMap<>(); | |
| backing.forEach( | |
| (reference, info) -> { | |
| if (!prunedItems.isRemoved(reference)) { | |
| AssumeInfo rewrittenInfo = info.withoutPrunedItems(prunedItems); | |
| if (!rewrittenInfo.isEmpty()) { | |
| rewrittenCollection.put(reference, rewrittenInfo); | |
| } | |
| } | |
| }); | |
| AssumeInfoCollection result = new AssumeInfoCollection(rewrittenCollection); | |
| timing.end(); | |
| return result; | |
| } | |
| public static class Builder { | |
| private final Map<DexMember<?, ?>, AssumeInfo.Builder> backing = new ConcurrentHashMap<>(); | |
| public Builder applyIf(boolean condition, Consumer<Builder> consumer) { | |
| if (condition) { | |
| consumer.accept(this); | |
| } | |
| return this; | |
| } | |
| public AssumeInfo buildInfo(DexClassAndMember<?, ?> member) { | |
| AssumeInfo.Builder builder = backing.get(member.getReference()); | |
| return builder != null ? builder.build() : AssumeInfo.empty(); | |
| } | |
| private AssumeInfo.Builder getOrCreateAssumeInfo(DexMember<?, ?> member) { | |
| return backing.computeIfAbsent(member, ignoreKey(AssumeInfo::builder)); | |
| } | |
| private AssumeInfo.Builder getOrCreateAssumeInfo(DexClassAndMember<?, ?> member) { | |
| return getOrCreateAssumeInfo(member.getReference()); | |
| } | |
| public boolean isEmpty() { | |
| return backing.isEmpty(); | |
| } | |
| public Builder meet(DexMember<?, ?> member, AssumeInfo assumeInfo) { | |
| getOrCreateAssumeInfo(member).meet(assumeInfo); | |
| return this; | |
| } | |
| public Builder meetAssumeType(DexClassAndMember<?, ?> member, DynamicType assumeType) { | |
| getOrCreateAssumeInfo(member).meetAssumeType(assumeType); | |
| return this; | |
| } | |
| public Builder meetAssumeValue(DexMember<?, ?> member, AbstractValue assumeValue) { | |
| getOrCreateAssumeInfo(member).meetAssumeValue(assumeValue); | |
| return this; | |
| } | |
| public Builder meetAssumeValue(DexClassAndMember<?, ?> member, AbstractValue assumeValue) { | |
| return meetAssumeValue(member.getReference(), assumeValue); | |
| } | |
| public Builder setIsSideEffectFree(DexMember<?, ?> member) { | |
| getOrCreateAssumeInfo(member).setIsSideEffectFree(); | |
| return this; | |
| } | |
| public Builder setIsSideEffectFree(DexClassAndMember<?, ?> member) { | |
| return setIsSideEffectFree(member.getReference()); | |
| } | |
| public AssumeInfoCollection build() { | |
| return new AssumeInfoCollection( | |
| MapUtils.newIdentityHashMap( | |
| builder -> | |
| backing.forEach( | |
| (reference, infoBuilder) -> { | |
| AssumeInfo info = infoBuilder.build(); | |
| if (!info.isEmpty()) { | |
| builder.accept(reference, info); | |
| } | |
| }), | |
| backing.size())); | |
| } | |
| } | |
| } |