src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/InliningLog.java
changeset 48861 47f19ff9903c
child 49451 e06f9607f370
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/InliningLog.java	Fri Feb 02 17:28:17 2018 -0800
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.graalvm.compiler.nodes;
+
+import jdk.vm.ci.code.BytecodePosition;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class contains all inlining decisions performed on a graph during the compilation.
+ *
+ * Each inlining decision consists of:
+ *
+ * <ul>
+ * <li>a value indicating whether the decision was positive or negative</li>
+ * <li>the call target method</li>
+ * <li>the reason for the inlining decision</li>
+ * <li>the name of the phase in which the inlining decision took place</li>
+ * <li>the special {@link BytecodePositionWithId} value that describes the position in the bytecode
+ * together with the callsite-specific unique identifier</li>
+ * <li>the inlining log of the inlined graph, or {@code null} if the decision was negative</li>
+ * </ul>
+ *
+ * A phase that does inlining should use the instance of this class contained in the
+ * {@link StructuredGraph} by calling {@link #addDecision} whenever it decides to inline a method.
+ * If there are invokes in the graph at the end of the respective phase, then that phase must call
+ * {@link #addDecision} to log negative decisions.
+ *
+ * At the end of the compilation, the contents of the inlining log can be converted into a list of
+ * decisions by calling {@link #formatAsList} or into an inlining tree, by calling
+ * {@link #formatAsTree}.
+ */
+public class InliningLog {
+    /**
+     * A bytecode position with a unique identifier attached.
+     *
+     * The purpose of this class is to disambiguate callsites that are duplicated by a
+     * transformation (such as loop peeling or path duplication).
+     */
+    public static final class BytecodePositionWithId extends BytecodePosition implements Comparable<BytecodePositionWithId> {
+        private final int id;
+
+        public BytecodePositionWithId(BytecodePositionWithId caller, ResolvedJavaMethod method, int bci, int id) {
+            super(caller, method, bci);
+            this.id = id;
+        }
+
+        public BytecodePositionWithId addCallerWithId(BytecodePositionWithId caller) {
+            if (getCaller() == null) {
+                return new BytecodePositionWithId(caller, getMethod(), getBCI(), id);
+            } else {
+                return new BytecodePositionWithId(getCaller().addCallerWithId(caller), getMethod(), getBCI(), id);
+            }
+        }
+
+        public static BytecodePositionWithId create(FrameState state) {
+            return create(state, true);
+        }
+
+        @SuppressWarnings("deprecation")
+        private static BytecodePositionWithId create(FrameState state, boolean topLevel) {
+            if (state == null) {
+                return null;
+            }
+            ResolvedJavaMethod method = state.getMethod();
+            int bci = topLevel ? state.bci - 3 : state.bci;
+            int id = state.getId();
+            return new BytecodePositionWithId(create(state.outerFrameState(), false), method, bci, id);
+        }
+
+        @Override
+        public BytecodePositionWithId getCaller() {
+            return (BytecodePositionWithId) super.getCaller();
+        }
+
+        public BytecodePositionWithId withoutCaller() {
+            return new BytecodePositionWithId(null, getMethod(), getBCI(), id);
+        }
+
+        public long getId() {
+            return id;
+        }
+
+        @Override
+        public boolean equals(Object that) {
+            return super.equals(that) && this.id == ((BytecodePositionWithId) that).id;
+        }
+
+        @Override
+        public int hashCode() {
+            return super.hashCode() ^ (id << 16);
+        }
+
+        @Override
+        public int compareTo(BytecodePositionWithId that) {
+            int diff = this.getBCI() - that.getBCI();
+            if (diff != 0) {
+                return diff;
+            }
+            diff = (int) (this.getId() - that.getId());
+            return diff;
+        }
+    }
+
+    public static final class Decision {
+        private final boolean positive;
+        private final String reason;
+        private final String phase;
+        private final ResolvedJavaMethod target;
+        private final BytecodePositionWithId position;
+        private final InliningLog childLog;
+
+        private Decision(boolean positive, String reason, String phase, ResolvedJavaMethod target, BytecodePositionWithId position, InliningLog childLog) {
+            assert position != null;
+            this.positive = positive;
+            this.reason = reason;
+            this.phase = phase;
+            this.target = target;
+            this.position = position;
+            this.childLog = childLog;
+        }
+
+        public boolean isPositive() {
+            return positive;
+        }
+
+        public String getReason() {
+            return reason;
+        }
+
+        public String getPhase() {
+            return phase;
+        }
+
+        public BytecodePositionWithId getPosition() {
+            return position;
+        }
+
+        public InliningLog getChildLog() {
+            return childLog;
+        }
+
+        public ResolvedJavaMethod getTarget() {
+            return target;
+        }
+    }
+
+    private static class Callsite {
+        public final List<String> decisions;
+        public final Map<BytecodePositionWithId, Callsite> children;
+        public final BytecodePositionWithId position;
+
+        Callsite(BytecodePositionWithId position) {
+            this.children = new HashMap<>();
+            this.position = position;
+            this.decisions = new ArrayList<>();
+        }
+
+        public Callsite getOrCreateChild(BytecodePositionWithId fromRootPosition) {
+            Callsite child = children.get(fromRootPosition.withoutCaller());
+            if (child == null) {
+                child = new Callsite(fromRootPosition);
+                children.put(fromRootPosition.withoutCaller(), child);
+            }
+            return child;
+        }
+
+        public Callsite createCallsite(BytecodePositionWithId fromRootPosition, String decision) {
+            Callsite parent = getOrCreateCallsite(fromRootPosition.getCaller());
+            Callsite callsite = parent.getOrCreateChild(fromRootPosition);
+            callsite.decisions.add(decision);
+            return null;
+        }
+
+        private Callsite getOrCreateCallsite(BytecodePositionWithId fromRootPosition) {
+            if (fromRootPosition == null) {
+                return this;
+            } else {
+                Callsite parent = getOrCreateCallsite(fromRootPosition.getCaller());
+                Callsite callsite = parent.getOrCreateChild(fromRootPosition);
+                return callsite;
+            }
+        }
+    }
+
+    private final List<Decision> decisions;
+
+    public InliningLog() {
+        this.decisions = new ArrayList<>();
+    }
+
+    public List<Decision> getDecisions() {
+        return decisions;
+    }
+
+    public void addDecision(boolean positive, String reason, String phase, ResolvedJavaMethod target, BytecodePositionWithId position,
+                    InliningLog calleeLog) {
+        Decision decision = new Decision(positive, reason, phase, target, position, calleeLog);
+        decisions.add(decision);
+    }
+
+    public String formatAsList() {
+        StringBuilder builder = new StringBuilder();
+        formatAsList("", null, decisions, builder);
+        return builder.toString();
+    }
+
+    private void formatAsList(String phasePrefix, BytecodePositionWithId caller, List<Decision> subDecisions, StringBuilder builder) {
+        for (Decision decision : subDecisions) {
+            String phaseStack = phasePrefix.equals("") ? decision.getPhase() : phasePrefix + "-" + decision.getPhase();
+            String target = decision.getTarget().format("%H.%n(%p)");
+            String positive = decision.isPositive() ? "inline" : "do not inline";
+            BytecodePositionWithId absolutePosition = decision.getPosition().addCallerWithId(caller);
+            String position = "  " + decision.getPosition().toString().replaceAll("\n", "\n  ");
+            String line = String.format("<%s> %s %s: %s\n%s", phaseStack, positive, target, decision.getReason(), position);
+            builder.append(line).append(System.lineSeparator());
+            if (decision.getChildLog() != null) {
+                formatAsList(phaseStack, absolutePosition, decision.getChildLog().getDecisions(), builder);
+            }
+        }
+    }
+
+    public String formatAsTree() {
+        Callsite root = new Callsite(null);
+        createTree("", null, root, decisions);
+        StringBuilder builder = new StringBuilder();
+        formatAsTree(root, "", builder);
+        return builder.toString();
+    }
+
+    private void createTree(String phasePrefix, BytecodePositionWithId caller, Callsite root, List<Decision> subDecisions) {
+        for (Decision decision : subDecisions) {
+            String phaseStack = phasePrefix.equals("") ? decision.getPhase() : phasePrefix + "-" + decision.getPhase();
+            String target = decision.getTarget().format("%H.%n(%p)");
+            BytecodePositionWithId absolutePosition = decision.getPosition().addCallerWithId(caller);
+            String line = String.format("<%s> %s: %s", phaseStack, target, decision.getReason());
+            root.createCallsite(absolutePosition, line);
+            if (decision.getChildLog() != null) {
+                createTree(phaseStack, absolutePosition, root, decision.getChildLog().getDecisions());
+            }
+        }
+    }
+
+    private void formatAsTree(Callsite site, String indent, StringBuilder builder) {
+        String position = site.position != null ? site.position.withoutCaller().toString() : "<root>";
+        String decision = String.join("; ", site.decisions);
+        String line = String.format("%s%s; %s", indent, position, decision);
+        builder.append(line).append(System.lineSeparator());
+        String childIndent = indent + "  ";
+        site.children.entrySet().stream().sorted((x, y) -> x.getKey().compareTo(y.getKey())).forEach(e -> {
+            formatAsTree(e.getValue(), childIndent, builder);
+        });
+    }
+}