1 /*
   2  * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package org.graalvm.compiler.phases.contract;
  24 
  25 import java.util.ArrayList;
  26 import java.util.List;
  27 import java.util.function.Function;
  28 
  29 import org.graalvm.compiler.core.common.cfg.BlockMap;
  30 import org.graalvm.compiler.debug.Debug;
  31 import org.graalvm.compiler.debug.DebugCounter;
  32 import org.graalvm.compiler.graph.Node;
  33 import org.graalvm.compiler.graph.VerificationError;
  34 import org.graalvm.compiler.nodes.FixedNode;
  35 import org.graalvm.compiler.nodes.StructuredGraph;
  36 import org.graalvm.compiler.nodes.cfg.Block;
  37 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
  38 import org.graalvm.compiler.nodes.spi.NodeCostProvider;
  39 import org.graalvm.compiler.phases.schedule.SchedulePhase;
  40 
  41 import jdk.vm.ci.meta.ResolvedJavaMethod;
  42 
  43 public class NodeCostUtil {
  44 
  45     private static final DebugCounter sizeComputationCount = Debug.counter("GraphCostComputationCount_Size");
  46     private static final DebugCounter sizeVerificationCount = Debug.counter("GraphCostVerificationCount_Size");
  47 
  48     @SuppressWarnings("try")
  49     public static int computeGraphSize(StructuredGraph graph, NodeCostProvider nodeCostProvider) {
  50         sizeComputationCount.increment();
  51         int size = 0;
  52         for (Node n : graph.getNodes()) {
  53             size += nodeCostProvider.getEstimatedCodeSize(n);
  54         }
  55         assert size >= 0;
  56         return size;
  57     }
  58 
  59     @SuppressWarnings("try")
  60     public static double computeGraphCycles(StructuredGraph graph, NodeCostProvider nodeCostProvider, boolean fullSchedule) {
  61         Function<Block, Iterable<? extends Node>> blockToNodes;
  62         ControlFlowGraph cfg;
  63         if (fullSchedule) {
  64             SchedulePhase schedule = new SchedulePhase(SchedulePhase.SchedulingStrategy.LATEST_OUT_OF_LOOPS, true);
  65             schedule.apply(graph);
  66             cfg = graph.getLastSchedule().getCFG();
  67             blockToNodes = b -> graph.getLastSchedule().getBlockToNodesMap().get(b);
  68         } else {
  69             cfg = ControlFlowGraph.compute(graph, true, true, false, false);
  70             BlockMap<List<FixedNode>> nodes = new BlockMap<>(cfg);
  71             for (Block b : cfg.getBlocks()) {
  72                 ArrayList<FixedNode> curNodes = new ArrayList<>();
  73                 for (FixedNode node : b.getNodes()) {
  74                     curNodes.add(node);
  75                 }
  76                 nodes.put(b, curNodes);
  77             }
  78             blockToNodes = b -> nodes.get(b);
  79         }
  80         double weightedCycles = 0D;
  81         try (Debug.Scope s = Debug.scope("NodeCostSummary")) {
  82             for (Block block : cfg.getBlocks()) {
  83                 for (Node n : blockToNodes.apply(block)) {
  84                     double probWeighted = nodeCostProvider.getEstimatedCPUCycles(n) * block.probability();
  85                     assert Double.isFinite(probWeighted);
  86                     weightedCycles += probWeighted;
  87                     if (Debug.isLogEnabled()) {
  88                         Debug.log("Node %s contributes cycles:%f size:%d to graph %s [block prob:%f]", n, nodeCostProvider.getEstimatedCPUCycles(n) * block.probability(),
  89                                         nodeCostProvider.getEstimatedCodeSize(n), graph, block.probability());
  90                     }
  91                 }
  92             }
  93         }
  94         assert weightedCycles >= 0D;
  95         assert Double.isFinite(weightedCycles);
  96         return weightedCycles;
  97     }
  98 
  99     private static int deltaCompare(double a, double b, double delta) {
 100         if (Math.abs(a - b) <= delta) {
 101             return 0;
 102         }
 103         return Double.compare(a, b);
 104     }
 105 
 106     /**
 107      * Factor to control the "imprecision" of the before - after relation when verifying phase
 108      * effects. If the cost model is perfect the best theoretical value is 0.0D (Ignoring the fact
 109      * that profiling information is not reliable and thus the, probability based, profiling view on
 110      * a graph is different than the reality).
 111      */
 112     private static final double DELTA = 0.001D;
 113 
 114     public static void phaseFulfillsSizeContract(StructuredGraph graph, int codeSizeBefore, int codeSizeAfter, PhaseSizeContract contract) {
 115         sizeVerificationCount.increment();
 116         final double codeSizeIncrease = contract.codeSizeIncrease();
 117         final double graphSizeDelta = codeSizeBefore * DELTA;
 118         if (deltaCompare(codeSizeAfter, codeSizeBefore * codeSizeIncrease, graphSizeDelta) > 0) {
 119             ResolvedJavaMethod method = graph.method();
 120             double increase = (double) codeSizeAfter / (double) codeSizeBefore;
 121             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",
 122                             contract.contractorName(), codeSizeIncrease, increase, codeSizeBefore, codeSizeAfter,
 123                             method != null ? " when compiling method " + method.format("%H.%n(%p)") + "." : ".");
 124         }
 125     }
 126 
 127 }