package org.graalvm.compiler.lir.constopt;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
import org.graalvm.compiler.core.common.cfg.AbstractControlFlowGraph;
import org.graalvm.compiler.core.common.cfg.BlockMap;
import org.graalvm.compiler.core.common.cfg.PrintableDominatorOptimizationProblem;
import org.graalvm.compiler.core.common.cfg.PropertyConsumable;
public class ConstantTree extends PrintableDominatorOptimizationProblem<ConstantTree.Flags, ConstantTree.NodeCost> {
public enum Flags {
SUBTREE,
USAGE,
MATERIALIZE,
CANDIDATE,
}
public static class NodeCost implements PropertyConsumable {
private List<UseEntry> usages;
private double bestCost;
private int numMat;
public NodeCost(double bestCost, List<UseEntry> usages, int numMat) {
this.bestCost = bestCost;
this.usages = usages;
this.numMat = numMat;
}
@Override
public void forEachProperty(BiConsumer<String, String> action) {
action.accept("bestCost", Double.toString(getBestCost()));
action.accept("numMat", Integer.toString(getNumMaterializations()));
action.accept("numUsages", Integer.toString(usages.size()));
}
public void addUsage(UseEntry usage) {
if (usages == null) {
usages = new ArrayList<>();
}
usages.add(usage);
}
public List<UseEntry> getUsages() {
if (usages == null) {
Collections.emptyList();
}
return usages;
}
public double getBestCost() {
return bestCost;
}
public int getNumMaterializations() {
return numMat;
}
public void setBestCost(double cost) {
bestCost = cost;
}
@Override
public String toString() {
return "NodeCost [bestCost=" + bestCost + ", numUsages=" + usages.size() + ", numMat=" + numMat + "]";
}
}
private final BlockMap<List<UseEntry>> blockMap;
public ConstantTree(AbstractControlFlowGraph<?> cfg, DefUseTree tree) {
super(Flags.class, cfg);
this.blockMap = new BlockMap<>(cfg);
tree.forEach(u -> getOrInitList(u.getBlock()).add(u));
}
private List<UseEntry> getOrInitList(AbstractBlockBase<?> block) {
List<UseEntry> list = blockMap.get(block);
if (list == null) {
list = new ArrayList<>();
blockMap.put(block, list);
}
return list;
}
public List<UseEntry> getUsages(AbstractBlockBase<?> block) {
List<UseEntry> list = blockMap.get(block);
if (list == null) {
return Collections.emptyList();
}
return Collections.unmodifiableList(list);
}
NodeCost getOrInitCost(AbstractBlockBase<?> block) {
NodeCost cost = getCost(block);
if (cost == null) {
cost = new NodeCost(block.probability(), blockMap.get(block), 1);
setCost(block, cost);
}
return cost;
}
@Override
public String getName(Flags type) {
switch (type) {
case USAGE:
return "hasUsage";
case SUBTREE:
return "inSubtree";
case MATERIALIZE:
return "materialize";
case CANDIDATE:
return "candidate";
}
return super.getName(type);
}
@Override
public void forEachPropertyPair(AbstractBlockBase<?> block, BiConsumer<String, String> action) {
if (get(Flags.SUBTREE, block) && (block.getDominator() == null || !get(Flags.SUBTREE, block.getDominator()))) {
action.accept("hasDefinition", "true");
}
super.forEachPropertyPair(block, action);
}
public long subTreeSize() {
return stream(Flags.SUBTREE).count();
}
public AbstractBlockBase<?> getStartBlock() {
return stream(Flags.SUBTREE).findFirst().get();
}
public void markBlocks() {
for (AbstractBlockBase<?> block : getBlocks()) {
if (get(Flags.USAGE, block)) {
setDominatorPath(Flags.SUBTREE, block);
}
}
}
public boolean isMarked(AbstractBlockBase<?> block) {
return get(Flags.SUBTREE, block);
}
public boolean isLeafBlock(AbstractBlockBase<?> block) {
for (AbstractBlockBase<?> dom : block.getDominated()) {
if (isMarked(dom)) {
return false;
}
}
return true;
}
public void setSolution(AbstractBlockBase<?> block) {
set(Flags.MATERIALIZE, block);
}
public int size() {
return getBlocks().length;
}
public void traverseTreeWhileTrue(AbstractBlockBase<?> block, Predicate<AbstractBlockBase<?>> action) {
assert block != null : "block must not be null!";
if (action.test(block)) {
block.getDominated().stream().filter(this::isMarked).forEach(dominated -> traverseTreeWhileTrue(dominated, action));
}
}
}