package org.graalvm.compiler.phases.contract;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import org.graalvm.compiler.core.common.cfg.BlockMap;
import org.graalvm.compiler.debug.Debug;
import org.graalvm.compiler.debug.DebugCounter;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.graph.VerificationError;
import org.graalvm.compiler.nodes.FixedNode;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.cfg.Block;
import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
import org.graalvm.compiler.nodes.spi.NodeCostProvider;
import org.graalvm.compiler.phases.schedule.SchedulePhase;
import jdk.vm.ci.meta.ResolvedJavaMethod;
public class NodeCostUtil {
private static final DebugCounter sizeComputationCount = Debug.counter("GraphCostComputationCount_Size");
private static final DebugCounter sizeVerificationCount = Debug.counter("GraphCostVerificationCount_Size");
@SuppressWarnings("try")
public static int computeGraphSize(StructuredGraph graph, NodeCostProvider nodeCostProvider) {
sizeComputationCount.increment();
int size = 0;
for (Node n : graph.getNodes()) {
size += nodeCostProvider.getEstimatedCodeSize(n);
}
assert size >= 0;
return size;
}
@SuppressWarnings("try")
public static double computeGraphCycles(StructuredGraph graph, NodeCostProvider nodeCostProvider, boolean fullSchedule) {
Function<Block, Iterable<? extends Node>> blockToNodes;
ControlFlowGraph cfg;
if (fullSchedule) {
SchedulePhase schedule = new SchedulePhase(SchedulePhase.SchedulingStrategy.LATEST_OUT_OF_LOOPS, true);
schedule.apply(graph);
cfg = graph.getLastSchedule().getCFG();
blockToNodes = b -> graph.getLastSchedule().getBlockToNodesMap().get(b);
} else {
cfg = ControlFlowGraph.compute(graph, true, true, false, false);
BlockMap<List<FixedNode>> nodes = new BlockMap<>(cfg);
for (Block b : cfg.getBlocks()) {
ArrayList<FixedNode> curNodes = new ArrayList<>();
for (FixedNode node : b.getNodes()) {
curNodes.add(node);
}
nodes.put(b, curNodes);
}
blockToNodes = b -> nodes.get(b);
}
double weightedCycles = 0D;
try (Debug.Scope s = Debug.scope("NodeCostSummary")) {
for (Block block : cfg.getBlocks()) {
for (Node n : blockToNodes.apply(block)) {
double probWeighted = nodeCostProvider.getEstimatedCPUCycles(n) * block.probability();
assert Double.isFinite(probWeighted);
weightedCycles += probWeighted;
if (Debug.isLogEnabled()) {
Debug.log("Node %s contributes cycles:%f size:%d to graph %s [block prob:%f]", n, nodeCostProvider.getEstimatedCPUCycles(n) * block.probability(),
nodeCostProvider.getEstimatedCodeSize(n), graph, block.probability());
}
}
}
}
assert weightedCycles >= 0D;
assert Double.isFinite(weightedCycles);
return weightedCycles;
}
private static int deltaCompare(double a, double b, double delta) {
if (Math.abs(a - b) <= delta) {
return 0;
}
return Double.compare(a, b);
}
private static final double DELTA = 0.001D;
public static void phaseFulfillsSizeContract(StructuredGraph graph, int codeSizeBefore, int codeSizeAfter, PhaseSizeContract contract) {
sizeVerificationCount.increment();
final double codeSizeIncrease = contract.codeSizeIncrease();
final double graphSizeDelta = codeSizeBefore * DELTA;
if (deltaCompare(codeSizeAfter, codeSizeBefore * codeSizeIncrease, graphSizeDelta) > 0) {
ResolvedJavaMethod method = graph.method();
double increase = (double) codeSizeAfter / (double) codeSizeBefore;
throw new VerificationError("Phase %s expects to increase code size by at most a factor of %.2f but an increase of %.2f was seen (code size before: %d, after: %d)%s",
contract.contractorName(), codeSizeIncrease, increase, codeSizeBefore, codeSizeAfter,
method != null ? " when compiling method " + method.format("%H.%n(%p)") + "." : ".");
}
}
}