gt:                 String bar = String.join("", Collections.nCopies(length, "*"));
context: /*
   * Copyright (c) 2016, 2017, 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.truffle.phases;
  
  import jdk.vm.ci.code.CodeUtil;
  import jdk.vm.ci.meta.JavaConstant;
  import jdk.vm.ci.meta.JavaKind;
  import jdk.vm.ci.meta.MetaUtil;
  import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
  import org.graalvm.compiler.core.common.type.StampFactory;
  import org.graalvm.compiler.core.common.type.TypeReference;
  import org.graalvm.compiler.debug.MethodFilter;
  import org.graalvm.compiler.debug.TTY;
  import org.graalvm.compiler.graph.Node;
  import org.graalvm.compiler.graph.NodeSourcePosition;
  import org.graalvm.compiler.nodes.ConstantNode;
  import org.graalvm.compiler.nodes.FixedWithNextNode;
  import org.graalvm.compiler.nodes.StructuredGraph;
  import org.graalvm.compiler.nodes.ValueNode;
  import org.graalvm.compiler.nodes.calc.AddNode;
  import org.graalvm.compiler.nodes.java.LoadIndexedNode;
  import org.graalvm.compiler.nodes.java.StoreIndexedNode;
  import org.graalvm.compiler.options.OptionValues;
  import org.graalvm.compiler.phases.BasePhase;
  import org.graalvm.compiler.phases.tiers.HighTierContext;
  import org.graalvm.compiler.truffle.OptimizedCallTarget;
  import org.graalvm.compiler.truffle.OptimizedDirectCallNode;
  import org.graalvm.compiler.truffle.TruffleCompilerOptions;
  
  import java.util.AbstractMap;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.Comparator;
  import java.util.LinkedHashMap;
  import java.util.List;
  import java.util.Map;
  
  public abstract class InstrumentPhase extends BasePhase<HighTierContext> {
      private static final String[] OMITTED_STACK_PATTERNS = new String[]{
                      OptimizedCallTarget.class.getName() + ".callProxy",
                      OptimizedCallTarget.class.getName() + ".callRoot",
                      OptimizedCallTarget.class.getName() + ".callInlined",
                      OptimizedDirectCallNode.class.getName() + ".callProxy",
                      OptimizedDirectCallNode.class.getName() + ".call"
      };
      private final Instrumentation instrumentation;
      protected final MethodFilter[] methodFilter;
      protected final SnippetReflectionProvider snippetReflection;
  
      public InstrumentPhase(OptionValues options, SnippetReflectionProvider snippetReflection, Instrumentation instrumentation) {
          String filterValue = instrumentationFilter(options);
          if (filterValue != null) {
              methodFilter = MethodFilter.parse(filterValue);
          } else {
              methodFilter = new MethodFilter[0];
          }
          this.snippetReflection = snippetReflection;
          this.instrumentation = instrumentation;
      }
  
      public Instrumentation getInstrumentation() {
          return instrumentation;
      }
  
      protected String instrumentationFilter(OptionValues options) {
          return TruffleCompilerOptions.TruffleInstrumentFilter.getValue(options);
      }
  
      protected static void insertCounter(StructuredGraph graph, HighTierContext context, JavaConstant tableConstant,
                      FixedWithNextNode targetNode, int slotIndex) {
          assert (tableConstant != null);
          TypeReference typeRef = TypeReference.createExactTrusted(context.getMetaAccess().lookupJavaType(tableConstant));
          ConstantNode table = graph.unique(new ConstantNode(tableConstant, StampFactory.object(typeRef, true)));
          ConstantNode rawIndex = graph.unique(ConstantNode.forInt(slotIndex));
          LoadIndexedNode load = graph.add(new LoadIndexedNode(null, table, rawIndex, JavaKind.Long));
          ConstantNode one = graph.unique(ConstantNode.forLong(1L));
          ValueNode add = graph.unique(new AddNode(load, one));
          StoreIndexedNode store = graph.add(new StoreIndexedNode(table, rawIndex, JavaKind.Long, add));
  
          graph.addAfterFixed(targetNode, load);
          graph.addAfterFixed(load, store);
      }
  
      @Override
      public float codeSizeIncrease() {
          return 2.5f;
      }
  
      @Override
      protected void run(StructuredGraph graph, HighTierContext context) {
          JavaConstant tableConstant = snippetReflection.forObject(instrumentation.getAccessTable());
          try {
              instrumentGraph(graph, context, tableConstant);
          } catch (Exception e) {
              throw new RuntimeException(e);
          }
      }
  
      protected abstract void instrumentGraph(StructuredGraph graph, HighTierContext context, JavaConstant tableConstant);
  
      protected abstract int instrumentationPointSlotCount();
  
      protected abstract boolean instrumentPerInlineSite(OptionValues options);
  
      protected abstract Point createPoint(int id, int startIndex, Node n);
  
      public Point getOrCreatePoint(Node n) {
          Point point = instrumentation.getOrCreatePoint(methodFilter, n, this);
          assert point == null || point.slotCount() == instrumentationPointSlotCount() : "Slot count mismatch between instrumentation point and expected value.";
          return point;
      }
  
      public static class Instrumentation {
          private Comparator<Point> pointsComparator = new Comparator<Point>() {
              @Override
              public int compare(Point x, Point y) {
                  long diff = y.getHotness() - x.getHotness();
                  if (diff < 0) {
                      return -1;
                  } else if (diff == 0) {
                      return 0;
                  } else {
                      return 1;
                  }
              }
          };
          private Comparator<Map.Entry<String, Point>> entriesComparator = new Comparator<Map.Entry<String, Point>>() {
              @Override
              public int compare(Map.Entry<String, Point> x, Map.Entry<String, Point> y) {
                  long diff = y.getValue().getHotness() - x.getValue().getHotness();
                  if (diff < 0) {
                      return -1;
                  } else if (diff == 0) {
                      return 0;
                  } else {
                      return 1;
                  }
              }
          };
          private final long[] accessTable;
          public Map<String, Point> pointMap = new LinkedHashMap<>();
          public int tableIdCount;
          public int tableStartIndex;
  
          public Instrumentation(long[] accessTable) {
              this.accessTable = accessTable;
          }
  
          /*
           * Node source location is determined by its inlining chain. A flag value controls whether
           * we discriminate nodes by their inlining site, or only by the method in which they were
           * defined.
           */
          private static String filterAndEncode(MethodFilter[] methodFilter, Node node, InstrumentPhase phase) {
              NodeSourcePosition pos = node.getNodeSourcePosition();
              if (pos != null) {
                  if (!MethodFilter.matches(methodFilter, pos.getMethod())) {
                      return null;
                  }
                  if (phase.instrumentPerInlineSite(node.getOptions())) {
                      StringBuilder sb = new StringBuilder();
                      while (pos != null) {
                          MetaUtil.appendLocation(sb.append("at "), pos.getMethod(), pos.getBCI());
                          pos = pos.getCaller();
                          if (pos != null) {
                              sb.append(CodeUtil.NEW_LINE);
                          }
                      }
                      return sb.toString();
                  } else {
                      return MetaUtil.appendLocation(new StringBuilder(), pos.getMethod(), pos.getBCI()).toString();
                  }
              } else {
                  // IfNode has no position information, and is probably synthetic, so we do not
                  // instrument it.
                  return null;
              }
          }
  
          private static String prettify(String key, Point p, OptionValues options) {
              if (p.isPrettified(options)) {
                  StringBuilder sb = new StringBuilder();
                  NodeSourcePosition pos = p.getPosition();
                  NodeSourcePosition lastPos = null;
                  int repetitions = 1;
  
                  callerChainLoop: while (pos != null) {
                      // Skip stack frame if it is a known pattern.
                      for (String pattern : OMITTED_STACK_PATTERNS) {
                          if (pos.getMethod().format("%H.%n(%p)").contains(pattern)) {
                              pos = pos.getCaller();
                              continue callerChainLoop;
                          }
                      }
  
                      if (lastPos == null) {
                          // Always output first method.
                          lastPos = pos;
                          MetaUtil.appendLocation(sb, pos.getMethod(), pos.getBCI());
                      } else if (!lastPos.getMethod().equals(pos.getMethod())) {
                          // Output count for identical BCI outputs, and output next method.
                          if (repetitions > 1) {
                              sb.append(" x" + repetitions);
                              repetitions = 1;
                          }
                          sb.append(CodeUtil.NEW_LINE);
                          lastPos = pos;
                          MetaUtil.appendLocation(sb, pos.getMethod(), pos.getBCI());
                      } else if (lastPos.getBCI() != pos.getBCI()) {
                          // Conflate identical BCI outputs.
                          if (repetitions > 1) {
                              sb.append(" x" + repetitions);
                              repetitions = 1;
                          }
                          lastPos = pos;
                          sb.append(" [bci: " + pos.getBCI() + "]");
                      } else {
                          // Identical BCI to the one seen previously.
                          repetitions++;
                      }
                      pos = pos.getCaller();
                  }
                  if (repetitions > 1) {
                      sb.append(" x" + repetitions);
                      repetitions = 1;
                  }
                  return sb.toString();
              } else {
                  return key;
              }
          }
  
          public synchronized ArrayList<String> accessTableToList(OptionValues options) {
  
              /*
               * Using sortedEntries.addAll(pointMap.entrySet(), instead of the iteration bellow, is
               * not safe and is detected by FindBugs. From FindBugs:
               *
               * "The entrySet() method is allowed to return a view of the underlying Map in which a
               * single Entry object is reused and returned during the iteration. As of Java 1.6, both
               * IdentityHashMap and EnumMap did so. When iterating through such a Map, the Entry
               * value is only valid until you advance to the next iteration. If, for example, you try
               * to pass such an entrySet to an addAll method, things will go badly wrong."
               */
              List<Map.Entry<String, Point>> sortedEntries = new ArrayList<>();
              for (Map.Entry<String, Point> entry : pointMap.entrySet()) {
                  Map.Entry<String, Point> immutableEntry = new AbstractMap.SimpleImmutableEntry<>(entry.getKey(), entry.getValue());
                  sortedEntries.add(immutableEntry);
              }
  
              Collections.sort(sortedEntries, entriesComparator);
  
              ArrayList<String> list = new ArrayList<>();
              for (Map.Entry<String, Point> entry : sortedEntries) {
                  list.add(prettify(entry.getKey(), entry.getValue(), options) + CodeUtil.NEW_LINE + entry.getValue());
              }
              return list;
          }
  
          public synchronized ArrayList<String> accessTableToHistogram() {
              long totalExecutions = 0;
              for (Point point : pointMap.values()) {
                  totalExecutions += point.getHotness();
              }
  
              List<Point> sortedPoints = new ArrayList<>();
              sortedPoints.addAll(pointMap.values());
  
              Collections.sort(sortedPoints, pointsComparator);
  
              ArrayList<String> histogram = new ArrayList<>();
              for (Point point : sortedPoints) {
                  int length = (int) ((1.0 * point.getHotness() / totalExecutions) * 80);
  