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 
  24 
  25 package org.graalvm.compiler.phases.contract;
  26 
  27 import java.util.ArrayList;
  28 import java.util.List;
  29 import java.util.function.Function;
  30 
  31 import org.graalvm.compiler.core.common.cfg.BlockMap;
  32 import org.graalvm.compiler.debug.CounterKey;
  33 import org.graalvm.compiler.debug.DebugContext;
  34 import org.graalvm.compiler.graph.Node;
  35 import org.graalvm.compiler.graph.VerificationError;
  36 import org.graalvm.compiler.nodes.FixedNode;
  37 import org.graalvm.compiler.nodes.StructuredGraph;
  38 import org.graalvm.compiler.nodes.cfg.Block;
  39 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
  40 import org.graalvm.compiler.phases.schedule.SchedulePhase;
  41 
  42 import jdk.vm.ci.meta.ResolvedJavaMethod;
  43 
  44 public class NodeCostUtil {
  45 
  46     private static final CounterKey sizeComputationCount = DebugContext.counter("GraphCostComputationCount_Size");
  47     private static final CounterKey sizeVerificationCount = DebugContext.counter("GraphCostVerificationCount_Size");
  48 
  49     @SuppressWarnings("try")
  50     public static int computeGraphSize(StructuredGraph graph) {
  51         sizeComputationCount.increment(graph.getDebug());
  52         int size = 0;
  53         for (Node n : graph.getNodes()) {
  54             size += n.estimatedNodeSize().value;
  55         }
  56         assert size >= 0;
  57         return size;
  58     }
  59 
  60     @SuppressWarnings("try")
  61     public static double computeGraphCycles(StructuredGraph graph, boolean fullSchedule) {
  62         Function<Block, Iterable<? extends Node>> blockToNodes;
  63         ControlFlowGraph cfg;
  64         if (fullSchedule) {
  65             SchedulePhase schedule = new SchedulePhase(SchedulePhase.SchedulingStrategy.LATEST_OUT_OF_LOOPS, true);
  66             schedule.apply(graph);
  67             cfg = graph.getLastSchedule().getCFG();
  68             blockToNodes = b -> graph.getLastSchedule().getBlockToNodesMap().get(b);
  69         } else {
  70             cfg = ControlFlowGraph.compute(graph, true, true, false, false);
  71             BlockMap<List<FixedNode>> nodes = new BlockMap<>(cfg);
  72             for (Block b : cfg.getBlocks()) {
  73                 ArrayList<FixedNode> curNodes = new ArrayList<>();
  74                 for (FixedNode node : b.getNodes()) {
  75                     curNodes.add(node);
  76                 }
  77                 nodes.put(b, curNodes);
  78             }
  79             blockToNodes = b -> nodes.get(b);
  80         }
  81         double weightedCycles = 0D;
  82         DebugContext debug = graph.getDebug();
  83         try (DebugContext.Scope s = debug.scope("NodeCostSummary")) {
  84             for (Block block : cfg.getBlocks()) {
  85                 for (Node n : blockToNodes.apply(block)) {
  86                     double probWeighted = n.estimatedNodeCycles().value * block.getRelativeFrequency();
  87                     assert Double.isFinite(probWeighted);
  88                     weightedCycles += probWeighted;
  89                     if (debug.isLogEnabled()) {
  90                         debug.log("Node %s contributes cycles:%f size:%d to graph %s [block freq:%f]", n, n.estimatedNodeCycles().value * block.getRelativeFrequency(),
  91                                         n.estimatedNodeSize().value, graph, block.getRelativeFrequency());
  92                     }
  93                 }
  94             }
  95         }
  96         assert weightedCycles >= 0D;
  97         assert Double.isFinite(weightedCycles);
  98         return weightedCycles;
  99     }
 100 
 101     private static int deltaCompare(double a, double b, double delta) {
 102         if (Math.abs(a - b) <= delta) {
 103             return 0;
 104         }
 105         return Double.compare(a, b);
 106     }
 107 
 108     /**
 109      * Factor to control the "imprecision" of the before - after relation when verifying phase
 110      * effects. If the cost model is perfect the best theoretical value is 0.0D (Ignoring the fact
 111      * that profiling information is not reliable and thus the, probability based, profiling view on
 112      * a graph is different than the reality).
 113      */
 114     private static final double DELTA = 0.001D;
 115 
 116     public static void phaseFulfillsSizeContract(StructuredGraph graph, int codeSizeBefore, int codeSizeAfter, PhaseSizeContract contract) {
 117         sizeVerificationCount.increment(graph.getDebug());
 118         final double codeSizeIncrease = contract.codeSizeIncrease();
 119         final double graphSizeDelta = codeSizeBefore * DELTA;
 120         if (deltaCompare(codeSizeAfter, codeSizeBefore * codeSizeIncrease, graphSizeDelta) > 0) {
 121             ResolvedJavaMethod method = graph.method();
 122             double increase = (double) codeSizeAfter / (double) codeSizeBefore;
 123             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",
 124                             contract.contractorName(), codeSizeIncrease, increase, codeSizeBefore, codeSizeAfter,
 125                             method != null ? " when compiling method " + method.format("%H.%n(%p)") + "." : ".");
 126         }
 127     }
 128 
 129 }