/**
   * Creates a graph for this stub.
   *
   * <p>If the stub returns an object, the graph created corresponds to this pseudo code:
   *
   * <pre>
   *     Object foreignFunctionStub(args...) {
   *         foreignFunction(currentThread,  args);
   *         if (clearPendingException(thread())) {
   *             getAndClearObjectResult(thread());
   *             DeoptimizeCallerNode.deopt(InvalidateReprofile, RuntimeConstraint);
   *         }
   *         return verifyObject(getAndClearObjectResult(thread()));
   *     }
   * </pre>
   *
   * If the stub returns a primitive or word, the graph created corresponds to this pseudo code
   * (using {@code int} as the primitive return type):
   *
   * <pre>
   *     int foreignFunctionStub(args...) {
   *         int result = foreignFunction(currentThread,  args);
   *         if (clearPendingException(thread())) {
   *             DeoptimizeCallerNode.deopt(InvalidateReprofile, RuntimeConstraint);
   *         }
   *         return result;
   *     }
   * </pre>
   *
   * If the stub is void, the graph created corresponds to this pseudo code:
   *
   * <pre>
   *     void foreignFunctionStub(args...) {
   *         foreignFunction(currentThread,  args);
   *         if (clearPendingException(thread())) {
   *             DeoptimizeCallerNode.deopt(InvalidateReprofile, RuntimeConstraint);
   *         }
   *     }
   * </pre>
   *
   * In each example above, the {@code currentThread} argument is the C++ JavaThread value (i.e.,
   * %r15 on AMD64) and is only prepended if {@link #prependThread} is true.
   */
  @Override
  protected StructuredGraph getGraph() {
    WordTypes wordTypes = providers.getWordTypes();
    Class<?>[] args = linkage.getDescriptor().getArgumentTypes();
    boolean isObjectResult =
        !linkage.getOutgoingCallingConvention().getReturn().getLIRKind().isValue();

    StructuredGraph graph = new StructuredGraph(toString(), null, AllowAssumptions.NO);
    graph.disableUnsafeAccessTracking();

    GraphKit kit = new GraphKit(graph, providers, wordTypes, providers.getGraphBuilderPlugins());
    ParameterNode[] params = createParameters(kit, args);

    ReadRegisterNode thread =
        kit.append(
            new ReadRegisterNode(
                providers.getRegisters().getThreadRegister(),
                wordTypes.getWordKind(),
                true,
                false));
    ValueNode result = createTargetCall(kit, params, thread);
    kit.createInvoke(
        StubUtil.class,
        "handlePendingException",
        thread,
        ConstantNode.forBoolean(isObjectResult, graph));
    if (isObjectResult) {
      InvokeNode object =
          kit.createInvoke(HotSpotReplacementsUtil.class, "getAndClearObjectResult", thread);
      result = kit.createInvoke(StubUtil.class, "verifyObject", object);
    }
    kit.append(
        new ReturnNode(linkage.getDescriptor().getResultType() == void.class ? null : result));

    if (Debug.isDumpEnabled()) {
      Debug.dump(graph, "Initial stub graph");
    }

    kit.inlineInvokes();

    if (Debug.isDumpEnabled()) {
      Debug.dump(graph, "Stub graph before compilation");
    }

    return graph;
  }