/**
     * Instantiate the snippet template and fix up the FrameState of any Invokes of System.arraycopy
     * and propagate the captured bci in the ArrayCopySlowPathNode.
     *
     * @param args
     * @param arraycopy
     */
    private void instantiate(Arguments args, BasicArrayCopyNode arraycopy) {
      StructuredGraph graph = arraycopy.graph();
      SnippetTemplate template = template(args);
      Map<Node, Node> replacements =
          template.instantiate(
              providers.getMetaAccess(), arraycopy, SnippetTemplate.DEFAULT_REPLACER, args);
      for (Node originalNode : replacements.keySet()) {
        if (originalNode instanceof Invoke) {
          Invoke invoke = (Invoke) replacements.get(originalNode);
          assert invoke.asNode().graph() == graph;
          CallTargetNode call = invoke.callTarget();

          if (!call.targetMethod().equals(originalArraycopy)) {
            throw new GraalError("unexpected invoke %s in snippet", call.targetMethod());
          }
          // Here we need to fix the bci of the invoke
          InvokeNode newInvoke = graph.add(new InvokeNode(invoke.callTarget(), arraycopy.getBci()));
          if (arraycopy.stateDuring() != null) {
            newInvoke.setStateDuring(arraycopy.stateDuring());
          } else {
            assert arraycopy.stateAfter() != null;
            newInvoke.setStateAfter(arraycopy.stateAfter());
          }
          graph.replaceFixedWithFixed((InvokeNode) invoke.asNode(), newInvoke);
        } else if (originalNode instanceof ArrayCopySlowPathNode) {
          ArrayCopySlowPathNode slowPath = (ArrayCopySlowPathNode) replacements.get(originalNode);
          assert arraycopy.stateAfter() != null;
          slowPath.setStateAfter(arraycopy.stateAfter());
          slowPath.setBci(arraycopy.getBci());
        }
      }
    }
 /**
  * For one or more `invoke` arguments, flow-sensitive information may suggest their narrowing or
  * simplification. In those cases, a new {@link com.oracle.graal.nodes.java.MethodCallTargetNode
  * MethodCallTargetNode} is prepared just for this callsite, consuming reduced arguments.
  *
  * <p>Specializing the {@link com.oracle.graal.nodes.java.MethodCallTargetNode
  * MethodCallTargetNode} as described above may enable two optimizations:
  *
  * <ul>
  *   <li>devirtualization of an {@link com.oracle.graal.nodes.CallTargetNode.InvokeKind#Interface}
  *       or {@link com.oracle.graal.nodes.CallTargetNode.InvokeKind#Virtual} callsite
  *       (devirtualization made possible after narrowing the type of the receiver)
  *   <li>(future work) actual-argument-aware inlining, ie, to specialize callees on the types of
  *       arguments other than the receiver (examples: multi-methods, the inlining problem, lambdas
  *       as arguments).
  * </ul>
  *
  * <p>Precondition: inputs haven't been deverbosified yet.
  */
 private void visitInvoke(Invoke invoke) {
   if (invoke.asNode().stamp() instanceof IllegalStamp) {
     return; // just to be safe
   }
   boolean isMethodCallTarget = invoke.callTarget() instanceof MethodCallTargetNode;
   if (!isMethodCallTarget) {
     return;
   }
   FlowUtil.replaceInPlace(
       invoke.asNode(),
       invoke.callTarget(),
       deverbosifyInputsCopyOnWrite((MethodCallTargetNode) invoke.callTarget()));
   MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget();
   if (callTarget.invokeKind() != CallTargetNode.InvokeKind.Interface
       && callTarget.invokeKind() != CallTargetNode.InvokeKind.Virtual) {
     return;
   }
   ValueNode receiver = callTarget.receiver();
   if (receiver == null) {
     return;
   }
   if (!FlowUtil.hasLegalObjectStamp(receiver)) {
     return;
   }
   Witness w = state.typeInfo(receiver);
   ResolvedJavaType type;
   ResolvedJavaType stampType = StampTool.typeOrNull(receiver);
   if (w == null || w.cluelessAboutType()) {
     // can't improve on stamp but wil try to devirtualize anyway
     type = stampType;
   } else {
     type = FlowUtil.tighten(w.type(), stampType);
   }
   if (type == null) {
     return;
   }
   ResolvedJavaMethod method =
       type.resolveMethod(callTarget.targetMethod(), invoke.getContextType());
   if (method == null) {
     return;
   }
   if (method.canBeStaticallyBound() || Modifier.isFinal(type.getModifiers())) {
     metricMethodResolved.increment();
     callTarget.setInvokeKind(CallTargetNode.InvokeKind.Special);
     callTarget.setTargetMethod(method);
   }
 }