private StubForeignCallNode createTargetCall(
     GraphKit kit, ParameterNode[] params, ReadRegisterNode thread) {
   if (prependThread) {
     ValueNode[] targetArguments = new ValueNode[1 + params.length];
     targetArguments[0] = thread;
     System.arraycopy(params, 0, targetArguments, 1, params.length);
     return kit.append(
         new StubForeignCallNode(
             providers.getForeignCalls(), target.getDescriptor(), targetArguments));
   } else {
     return kit.append(
         new StubForeignCallNode(providers.getForeignCalls(), target.getDescriptor(), params));
   }
 }
  @Override
  public Variable emitForeignCall(ForeignCallLinkage linkage, LIRFrameState state, Value... args) {
    HotSpotForeignCallLinkage hotspotLinkage = (HotSpotForeignCallLinkage) linkage;
    Variable result;
    LIRFrameState debugInfo = null;
    if (hotspotLinkage.needsDebugInfo()) {
      debugInfo = state;
      assert debugInfo != null || getStub() != null;
    }

    if (linkage.destroysRegisters() || hotspotLinkage.needsJavaFrameAnchor()) {
      HotSpotRegistersProvider registers = getProviders().getRegisters();
      Register thread = registers.getThreadRegister();
      Variable scratch = newVariable(LIRKind.value(target().arch.getWordKind()));

      // We need a label for the return address.
      label = new Label();

      append(
          new AArch64HotSpotCRuntimeCallPrologueOp(
              config.threadLastJavaSpOffset(),
              config.threadLastJavaPcOffset(),
              config.threadLastJavaFpOffset(),
              thread,
              scratch,
              label));
      result = super.emitForeignCall(hotspotLinkage, debugInfo, args);
      append(
          new AArch64HotSpotCRuntimeCallEpilogueOp(
              config.threadLastJavaSpOffset(), config.threadLastJavaFpOffset(), thread));

      // Clear it out so it's not being reused later.
      label = null;
    } else {
      result = super.emitForeignCall(hotspotLinkage, debugInfo, args);
    }

    return result;
  }