if (VM.VerifyAssertions) VM.assert(T3 <= LAST_VOLATILE_GPR); if (VM.VerifyAssertions) VM.assert(F3 <= LAST_VOLATILE_FPR); if (VM.VerifyAssertions) VM.assert(S0 < SP && SP <= LAST_SCRATCH_GPR);
if (VM.VerifyAssertions) VM._assert(T3 <= LAST_VOLATILE_GPR); if (VM.VerifyAssertions) VM._assert(F3 <= LAST_VOLATILE_FPR); if (VM.VerifyAssertions) VM._assert(S0 < SP && SP <= LAST_SCRATCH_GPR);
  static VM_MachineCode generateGlueCodeForNative (VM_CompiledMethod cm) {    int compiledMethodId = cm.getId();    VM_Method method     = cm.getMethod();    VM_Assembler asm	= new VM_Assembler(0);    int frameSize	= VM_Compiler.getFrameSize(method);    VM_Class klass	= method.getDeclaringClass();    /* initialization */    if (VM.VerifyAssertions) VM.assert(T3 <= LAST_VOLATILE_GPR);           // need 4 gp temps    if (VM.VerifyAssertions) VM.assert(F3 <= LAST_VOLATILE_FPR);           // need 4 fp temps    if (VM.VerifyAssertions) VM.assert(S0 < SP && SP <= LAST_SCRATCH_GPR); // need 2 scratch    VM_Address bootRecordAddress = VM_Magic.objectAsAddress(VM_BootRecord.the_boot_record);    int lockoutLockOffset = VM_Entrypoints.lockoutProcessorField.getOffset();    VM_Address lockoutLockAddress = bootRecordAddress.add(lockoutLockOffset);    int sysTOCOffset      = VM_Entrypoints.sysTOCField.getOffset();    int sysYieldIPOffset  = VM_Entrypoints.sysVirtualProcessorYieldIPField.getOffset();    VM_Address gCFlagAddress = VM_Magic.objectAsAddress(VM_BootRecord.the_boot_record).add(VM_Entrypoints.globalGCInProgressFlagField.getOffset());    int nativeIP  = method.getNativeIP();    int nativeTOC = method.getNativeTOC();    // NOTE:  this must be done before the condition VM_Thread.hasNativeStackFrame() become true    // so that the first Java to C transition will be allowed to resize the stack    // (currently, this is true when the JNIRefsTop index has been incremented from 0)    asm.emitNativeStackOverflowCheck(frameSize + 14);   // add at least 14 for C frame (header + spill)    int numValues = method.getParameterWords();     // number of arguments for this method    int parameterAreaSize = (numValues) * 4;    asm.emitMFLR(0);                                // save return address in caller frame    asm.emitST(0, STACKFRAME_NEXT_INSTRUCTION_OFFSET, FP);	// this was set up the first time by DynamicBridgeTo()        asm.emitSTU (FP,  -frameSize, FP);                      // get transition frame on stack    asm.emitST  (JTOC, frameSize - JNI_JTOC_OFFSET, FP);    // save RVM JTOC in frame    asm.emitST  (PROCESSOR_REGISTER, frameSize - JNI_PR_OFFSET, FP);  // save PR in frame    // store method ID for JNI frame, occupies AIX saved CR slot, which we don't use    // hardcoded in the glue routine    asm.emitLVAL (S0, compiledMethodId);     asm.emitST   (S0, STACKFRAME_METHOD_ID_OFFSET, FP);    // establish SP -> VM_Thread, S0 -> threads JNIEnv structure          asm.emitL(SP, VM_Entrypoints.activeThreadField.getOffset(), PROCESSOR_REGISTER);    asm.emitL(S0, VM_Entrypoints.jniEnvField.getOffset(), SP);      // save the TI & PR registers in the JNIEnvironment object for possible calls back into Java    asm.emitST(TI, VM_Entrypoints.JNIEnvSavedTIField.getOffset(), S0);               asm.emitST(PROCESSOR_REGISTER, VM_Entrypoints.JNIEnvSavedPRField.getOffset(), S0);       // save current frame pointer in JNIEnv, JNITopJavaFP, which will be the frame    // to start scanning this stack during GC, if top of stack is still executing in C    asm.emitST(FP, VM_Entrypoints.JNITopJavaFPField.getOffset(), S0);               // save the RVM nonvolatile registers, to be scanned by GC stack mapper    // remember to skip past the saved JTOC and SP by starting with offset=-12    //    for (int i = LAST_NONVOLATILE_GPR, offset = JNI_RVM_NONVOLATILE_OFFSET;	 i >= FIRST_NONVOLATILE_GPR; --i, offset+=4) {      asm.emitST (i, frameSize - offset, FP);    }    // clear the GC flag on entry to native code    asm.emitCAL(PROCESSOR_REGISTER,0,0);          // use PR as scratch    asm.emitST(PROCESSOR_REGISTER, frameSize-JNI_GC_FLAG_OFFSET, FP);    // generate the code to map the parameters to AIX convention and add the    // second parameter 2 (either the "this" ptr or class if a static method).    // The JNI Function ptr first parameter is set before making the call.    // Opens a new frame in the JNIRefs table to register the references    // Assumes S0 set to JNIEnv, kills TI, SP & PROCESSOR_REGISTER    // On return, S0 is still valid.    //    storeParametersForAIX(asm, frameSize, method, klass);    // Get address of out_of_line prolog into SP, before setting TOC reg.    asm.emitL   (SP, VM_Entrypoints.invokeNativeFunctionInstructionsField.getOffset(), JTOC);    asm.emitMTLR(SP);    // set the TOC and IP for branch to out_of_line code    asm.emitLVAL (JTOC,  nativeTOC);         // load TOC for native function into TOC reg    asm.emitLVAL (TI,    nativeIP);	      // load TI with address of native code    // go to part of prologue that is in the boot image. It will change the Processor    // status to "in_native" and transfer to the native code.  On return it will    // change the state back to "in_java" (waiting if blocked).    //    // The native address entrypoint is in register TI    // The native TOC has been loaded into the TOC register    // S0 still points to threads JNIEnvironment    //    asm.emitBLRL  ();    // restore PR, saved in this glue frame before the call. If GC occurred, this saved    // VM_Processor reference was relocated while scanning this stack. If while in native,    // the thread became "stuck" and was moved to a Native Processor, then this saved    // PR was reset to point to that native processor    //    asm.emitL     (PROCESSOR_REGISTER, frameSize - JNI_PR_OFFSET, FP);      // at this point, test if in blue processor: if yes, return to    // Java caller; by picking up at "check if GC..." below    // if not, must transfer to blue processor by using    // become RVM thread.    asm.emitL     (S0, VM_Entrypoints.processorModeField.getOffset(), PROCESSOR_REGISTER); // get processorMode    asm.emitCMPI  (S0, VM_Processor.RVM)     ;           // are we still on blue processor    VM_ForwardReference frBlue = asm.emitForwardBC(EQ);    asm.emitL(T0, VM_Entrypoints.activeThreadField.getOffset(), PROCESSOR_REGISTER);  // get activeThread from Processor    asm.emitL(S0, VM_Entrypoints.jniEnvField.getOffset(), T0);             // get JNIEnvironment from activeThread    asm.emitL (TI, VM_Entrypoints.JNIEnvSavedTIField.getOffset(), S0);     // and restore TI from JNIEnvironment      // restore RVM JTOC    asm.emitL   (JTOC, frameSize - 4, FP);              // Branch to becomeRVMThread:  we will yield and get rescheduled to execute     // on a regular VM_Processor for Java code    asm.emitL   (S0, VM_Entrypoints.becomeRVMThreadMethod.getOffset(), JTOC);    asm.emitMTLR(S0);    asm.emitBLRL  ();         // At this point, we have resumed execution in the regular Java VM_Processor    // so the PR contains the correct values for this VM_Processor.  TI is also correct    // branch to here if on blue processor	    //    // check if GC has occurred, If GC did not occur, then     // VM NON_VOLATILE regs were restored by AIX and are valid.  If GC did occur    // objects referenced by these restored regs may have moved, in this case we    // restore the nonvolatile registers from our savearea,    // where any object references would have been relocated during GC.    // use T2 as scratch (not needed any more on return from call)    //    frBlue.resolve(asm);    asm.emitL(T2, frameSize - JNI_GC_FLAG_OFFSET, FP);    asm.emitCMPI(T2,0);    VM_ForwardReference fr1 = asm.emitForwardBC(EQ);    for (int i = LAST_NONVOLATILE_GPR, offset = JNI_RVM_NONVOLATILE_OFFSET;	 i >= FIRST_NONVOLATILE_GPR; --i, offset+=4) {      asm.emitL (i, frameSize - offset, FP);    }    fr1.resolve(asm);    asm.emitL(S0, VM_Entrypoints.activeThreadField.getOffset(), PROCESSOR_REGISTER);  // S0 holds thread pointer    // reset threads processor affinity if it was previously zero, before the call.    // ...again, rethink if the setting & resetting of processor affinity is still necessary    //    asm.emitL(T2, frameSize - JNI_AFFINITY_OFFSET, FP);          // saved affinity in glue frame    asm.emitCMPI(T2,0);    VM_ForwardReference fr2 = asm.emitForwardBC(NE);    asm.emitST(T2, VM_Entrypoints.processorAffinityField.getOffset(), S0);  // store 0 into affinity field    fr2.resolve(asm);		     // reestablish S0 to hold jniEnv pointer    asm.emitL(S0, VM_Entrypoints.jniEnvField.getOffset(), S0);           // pop jrefs frame off the JNIRefs stack, "reopen" the previous top jref frame    // use SP as scratch before it's restored, also use T2, T3 for scratch which are no longer needed    asm.emitL  (SP, VM_Entrypoints.JNIRefsField.getOffset(), S0);          // load base of JNIRefs array    asm.emitL  (T2, VM_Entrypoints.JNIRefsSavedFPField.getOffset(), S0);   // get saved offset for JNIRefs frame ptr previously pushed onto JNIRefs array    asm.emitCAL(T3, -4, T2);                                    // compute offset for new TOP    asm.emitST (T3, VM_Entrypoints.JNIRefsTopField.getOffset(), S0);       // store new offset for TOP into JNIEnv    asm.emitLX (T2, SP, T2);                                    // retrieve the previous frame ptr    asm.emitST (T2, VM_Entrypoints.JNIRefsSavedFPField.getOffset(), S0);   // store new offset for JNIRefs frame ptr into JNIEnv    // Restore the return value R3-R4 saved in the glue frame spill area before the migration    asm.emitL (T0, AIX_FRAME_HEADER_SIZE, FP);    asm.emitL (T1, AIX_FRAME_HEADER_SIZE+4, FP);          // if the the return type is a reference, the native C is returning a jref    // which is a byte offset from the beginning of the threads JNIRefs stack/array    // of the corresponding ref.  In this case, emit code to replace the returned    // offset (in R3) with the ref from the JNIRefs array    VM_Type returnType = method.getReturnType();    if ( returnType.isReferenceType() ) {      // use returned offset to load ref from JNIRefs into R3      asm.emitLX (3, SP, 3);         // SP is still the base of the JNIRefs array    }    // reload TI ref saved in JNIEnv    asm.emitL (TI, VM_Entrypoints.JNIEnvSavedTIField.getOffset(), S0);               // pop the glue stack frame, restore the Java caller frame    asm.emitCAL (FP,  +frameSize, FP);              // remove linkage area    // C return value is already where caller expected it (R3, R4 or F0)    // and SP is a scratch register, so we actually don't have to     // restore it and/or pop arguments off it.    // So, just restore the return address to the link register.    asm.emitL(0, STACKFRAME_NEXT_INSTRUCTION_OFFSET, FP);     asm.emitMTLR(0);                           // restore return address    // CHECK EXCEPTION AND BRANCH TO ATHROW CODE OR RETURN NORMALLY    asm.emitL  (T2, VM_Entrypoints.JNIPendingExceptionField.getOffset(), S0);   // get pending exception from JNIEnv    asm.emitCAL (T3,  0, 0);             // get a null value to compare    asm.emitST  (T3, VM_Entrypoints.JNIPendingExceptionField.getOffset(), S0); // clear the current pending exception    asm.emitCMP (T2, T3);                      // check for pending exception on return from native    VM_ForwardReference fr3 = asm.emitForwardBC(NE);    asm.emitBLR();                             // if no pending exception, proceed to return to caller    fr3.resolve(asm);    // An exception is pending, deliver the exception to the caller as if executing an athrow in the caller    // at the location of the call to the native method    asm.emitLtoc(T3, VM_Entrypoints.athrowMethod.getOffset());    asm.emitMTCTR(T3);                         // point LR to the exception delivery code    asm.emitCAL (T0, 0, T2);                   // copy the saved exception to T0    asm.emitBCTR();                            // then branch to the exception delivery code, does not return    return asm.makeMachineCode();  } 