001/*
002 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003 *
004 *  This file is licensed to You under the Eclipse Public License (EPL);
005 *  You may not use this file except in compliance with the License. You
006 *  may obtain a copy of the License at
007 *
008 *      http://www.opensource.org/licenses/eclipse-1.0.php
009 *
010 *  See the COPYRIGHT.txt file distributed with this work for information
011 *  regarding copyright ownership.
012 */
013package org.jikesrvm.jni.ia32;
014
015import static org.jikesrvm.compilers.common.assembler.ia32.AssemblerConstants.EQ;
016import static org.jikesrvm.compilers.common.assembler.ia32.AssemblerConstants.LGE;
017import static org.jikesrvm.ia32.ArchConstants.SSE2_FULL;
018import static org.jikesrvm.ia32.BaselineConstants.LG_WORDSIZE;
019import static org.jikesrvm.ia32.BaselineConstants.S0;
020import static org.jikesrvm.ia32.BaselineConstants.SP;
021import static org.jikesrvm.ia32.BaselineConstants.T0;
022import static org.jikesrvm.ia32.BaselineConstants.T1;
023import static org.jikesrvm.ia32.BaselineConstants.WORDSIZE;
024import static org.jikesrvm.ia32.RegisterConstants.EBP;
025import static org.jikesrvm.ia32.RegisterConstants.EBX;
026import static org.jikesrvm.ia32.RegisterConstants.EDI;
027import static org.jikesrvm.ia32.RegisterConstants.ESP;
028import static org.jikesrvm.ia32.RegisterConstants.FP0;
029import static org.jikesrvm.ia32.RegisterConstants.JTOC_REGISTER;
030import static org.jikesrvm.ia32.RegisterConstants.NATIVE_NONVOLATILE_FPRS;
031import static org.jikesrvm.ia32.RegisterConstants.NATIVE_NONVOLATILE_GPRS;
032import static org.jikesrvm.ia32.RegisterConstants.NATIVE_PARAMETER_FPRS;
033import static org.jikesrvm.ia32.RegisterConstants.NATIVE_PARAMETER_GPRS;
034import static org.jikesrvm.ia32.RegisterConstants.PARAMETER_FPRS;
035import static org.jikesrvm.ia32.RegisterConstants.PARAMETER_GPRS;
036import static org.jikesrvm.ia32.RegisterConstants.THREAD_REGISTER;
037import static org.jikesrvm.ia32.RegisterConstants.XMM0;
038import static org.jikesrvm.ia32.StackframeLayoutConstants.STACKFRAME_BODY_OFFSET;
039import static org.jikesrvm.ia32.StackframeLayoutConstants.STACKFRAME_FRAME_POINTER_OFFSET;
040import static org.jikesrvm.ia32.StackframeLayoutConstants.STACKFRAME_METHOD_ID_OFFSET;
041import static org.jikesrvm.runtime.UnboxedSizeConstants.LOG_BYTES_IN_WORD;
042
043import org.jikesrvm.VM;
044import org.jikesrvm.classloader.NativeMethod;
045import org.jikesrvm.classloader.NormalMethod;
046import org.jikesrvm.classloader.RVMMethod;
047import org.jikesrvm.classloader.TypeReference;
048import org.jikesrvm.compilers.common.CodeArray;
049import org.jikesrvm.compilers.common.CompiledMethod;
050import org.jikesrvm.compilers.common.CompiledMethods;
051import org.jikesrvm.compilers.common.assembler.ForwardReference;
052import org.jikesrvm.compilers.common.assembler.ia32.Assembler;
053import org.jikesrvm.ia32.RegisterConstants.FPR;
054import org.jikesrvm.ia32.RegisterConstants.FloatingPointMachineRegister;
055import org.jikesrvm.ia32.RegisterConstants.GPR;
056import org.jikesrvm.ia32.RegisterConstants.XMM;
057import org.jikesrvm.jni.JNICompiledMethod;
058import org.jikesrvm.runtime.ArchEntrypoints;
059import org.jikesrvm.runtime.BootRecord;
060import org.jikesrvm.runtime.Entrypoints;
061import org.jikesrvm.runtime.Statics;
062import org.jikesrvm.scheduler.RVMThread;
063import org.vmmagic.unboxed.Address;
064import org.vmmagic.unboxed.Offset;
065
066/**
067 * This class compiles the prolog and epilog for all code that makes
068 * the transition between Java and Native C for the 2 cases:
069 * <ul>
070 * <li>from Java to C:  all user-defined native methods</li>
071 * <li>C to Java:  all JNI functions in {@link org.jikesrvm.jni.JNIFunctions}</li>
072 * </ul>
073 * When performing the transitions the values in registers and on the stack need
074 * to be treated with care, but also when transitioning into Java we need to do
075 * a spin-wait if a GC is underway.
076 *
077 * Transitioning from Java to C then back:
078 * <ol>
079 * <li>Set up stack frame and save non-volatile registers</li>
080 * <li>Move all native method arguments on to stack (NB at this point all non-volatile state is saved)</li>
081 * <li>Set up jniEnv</li>
082 * <li>Record the frame pointer of the last Java frame (this) in the jniEnv</li>
083 * <li>Set up JNI refs, a stack that holds references accessed via JNI</li>
084 * <li>Call out to convert reference arguments to IDs</li>
085 * <li>Set processor as being "in native"</li>
086 * <li>Set up stack frame and registers for transition to C</li>
087 * <li>Call out to C</li>
088 * <li>Save result to stack</li>
089 * <li>Transition back from "in native" to "in Java", take care that the
090 *     Processor isn't "blocked in native", ie other processors have decided to
091 *     start a GC and we're not permitted to execute Java code whilst this
092 *     occurs</li>
093 * <li>Convert a reference result (currently a JNI ref) into a true reference</li>
094 * <li>Release JNI refs</li>
095 * <li>Restore stack and place result in register</li>
096 * </ol>
097 *
098 * Prologue generation from C to Java:
099 * <ol>
100 * <li>Set up stack frame with C arguments in Jikes RVM convention</li>
101 * <li>Set up extra stack frame entry that records the previous last top Java FP</li>
102 * <li>Transition from "in native" to "in Java" taking care of blocked (in GC)
103       case</li>
104 * </ol>
105 *
106 * Epilogue generation from Java to C:
107 * <ol>
108 * <li>Restore record of the previous last top Java FP in the jniEnv</li>
109 * <li>Transition from "in Java" to "in native"</li>
110 * <li>Set up result in registers. NB. JNIFunctions don't return references but
111 *     JNI refs directly, so we don't need to transition these</li>
112 * </ol>
113 */
114public abstract class JNICompiler {
115
116  /** Dummy field to force compilation of the exception deliverer */
117  private org.jikesrvm.jni.ia32.JNIExceptionDeliverer unused;
118
119  /** Offset of external functions field in JNIEnvironment */
120  private static final  int jniExternalFunctionsFieldOffset =
121    Entrypoints.JNIExternalFunctionsField.getOffset().toInt();
122
123  // --- Java to C fields ---
124
125  /** Location of non-volatile EDI register when saved to stack */
126  static final Offset EDI_SAVE_OFFSET = STACKFRAME_BODY_OFFSET;
127  /** Location of non-volatile EBX register when saved to stack */
128  static final Offset EBX_SAVE_OFFSET = EDI_SAVE_OFFSET.minus(WORDSIZE);
129  /** Location of non-volatile EBP register when saved to stack */
130  static final Offset EBP_SAVE_OFFSET = EBX_SAVE_OFFSET.minus(WORDSIZE);
131  /** Location of an extra copy of the RVMThread.jniEnv when saved to stack */
132  private static final Offset JNI_ENV_OFFSET = EBP_SAVE_OFFSET.minus(WORDSIZE);
133  /** Location of a saved version of the field JNIEnvironment.basePointerOnEntryToNative */
134  private static final Offset BP_ON_ENTRY_OFFSET = JNI_ENV_OFFSET.minus(WORDSIZE);
135
136  // --- C to Java fields ---
137  /**
138   * Stack frame location for saved JNIEnvironment.JNITopJavaFP that
139   * will be clobbered by a transition from Java to C.  Only used in
140   * the prologue &amp; epilogue for JNIFunctions.
141   */
142  private static final Offset SAVED_JAVA_FP_OFFSET = STACKFRAME_BODY_OFFSET;
143
144  /**
145   * The following is used in BaselineCompilerImpl to compute offset to first local.
146   * Number of non-volatile GPRs and FPRs and then 1 slot for the SAVED_JAVA_FP.
147   */
148  public static final int SAVED_GPRS_FOR_JNI = NATIVE_NONVOLATILE_GPRS.length + NATIVE_NONVOLATILE_FPRS.length + 1;
149
150  /**
151   * Compiles a method to handle the Java to C transition and back
152   * Transitioning from Java to C then back:
153   * <ol>
154   * <li>Set up stack frame and save non-volatile registers<li>
155   * <li>Set up jniEnv - set up a register to hold JNIEnv and store
156   *     the Processor in the JNIEnv for easy access</li>
157   * <li>Move all native method arguments on to stack (NB at this point all
158   *     non-volatile state is saved)</li>
159   * <li>Record the frame pointer of the last Java frame (this) in the jniEnv</li>
160   * <li>Call out to convert reference arguments to IDs</li>
161   * <li>Set processor as being "in native"</li>
162   * <li>Set up stack frame and registers for transition to C</li>
163   * <li>Call out to C</li>
164   * <li>Save result to stack</li>
165   * <li>Transition back from "in native" to "in Java", take care that the
166   *     Processor isn't "blocked in native", ie other processors have decided to
167   *     start a GC and we're not permitted to execute Java code whilst this
168   *     occurs</li>
169   * <li>Convert a reference result (currently a JNI ref) into a true reference</li>
170   * <li>Release JNI refs</li>
171   * <li>Restore stack and place result in register</li>
172   * </ol>
173   *
174   * @param method the method to compile
175   * @return the compiled method (always a {@link JNICompiledMethod})
176   */
177  public static synchronized CompiledMethod compile(NativeMethod method) {
178    // Meaning of constant offset into frame (assuming 4byte word size):
179    // Stack frame:
180    //        on entry          after prolog
181    //
182    //      high address        high address
183    //      |          |        |          | Caller frame
184    //      |          |        |          |
185    // +    |arg 0     |        |arg 0     | <- firstParameterOffset
186    // +    |arg 1     |        |arg 1     |
187    // +    |...       |        |...       |
188    // +8   |arg n-1   |        |arg n-1   | <- lastParameterOffset
189    // +4   |returnAddr|        |returnAddr|
190    //  0   +          +        +saved FP  + <- EBP/FP value in glue frame
191    // -4   |          |        |methodID  |
192    // -8   |          |        |saved EDI |
193    // -C   |          |        |saved EBX |
194    // -10  |          |        |saved EBP |
195    // -14  |          |        |saved ENV |  (JNIEnvironment)
196    // -18  |          |        |arg n-1   |  reordered args to native method
197    // -1C  |          |        | ...      |  ...
198    // -20  |          |        |arg 1     |  ...
199    // -24  |          |        |arg 0     |  ...
200    // -28  |          |        |class/obj |  required second arg to native method
201    // -2C  |          |        |jni funcs |  required first arg to native method
202    // -30  |          |        |          |
203    //      |          |        |          |
204    //      |          |        |          |
205    //       low address         low address
206    // Register values:
207    // EBP    - after step 1 EBP holds a frame pointer allowing easy
208    //          access to both this and the proceeding frame
209    // ESP    - gradually floats down as the stack frame is initialized
210    // S0/ECX - reference to the JNI environment after step 3
211
212    JNICompiledMethod cm = (JNICompiledMethod)CompiledMethods.createCompiledMethod(method, CompiledMethod.JNI);
213    Assembler asm = new Assembler(100 /*, true*/);   // some size for the instruction array
214
215    Address nativeIP = method.getNativeIP();
216    final Offset lastParameterOffset = Offset.fromIntSignExtend(2 * WORDSIZE);
217    //final Offset firstParameterOffset = Offset.fromIntSignExtend(WORDSIZE+(method.getParameterWords() << LG_WORDSIZE));
218    final TypeReference[] args = method.getParameterTypes();
219
220    // (1) Set up stack frame and save non-volatile registers
221
222    // TODO:  check and resize stack once on the lowest Java to C transition
223    // on the stack.  Not needed if we use the thread original stack
224
225    // set 2nd word of header = return address already pushed by CALL
226    asm.emitPUSH_RegDisp(THREAD_REGISTER, ArchEntrypoints.framePointerField.getOffset());
227    // establish new frame
228    if (VM.BuildFor32Addr) {
229      asm.emitMOV_RegDisp_Reg(THREAD_REGISTER, ArchEntrypoints.framePointerField.getOffset(), SP);
230    } else {
231      asm.emitMOV_RegDisp_Reg_Quad(THREAD_REGISTER, ArchEntrypoints.framePointerField.getOffset(), SP);
232    }
233
234    // set first word of header: method ID
235    if (VM.VerifyAssertions) VM._assert(STACKFRAME_METHOD_ID_OFFSET.toInt() == -WORDSIZE);
236    asm.emitPUSH_Imm(cm.getId());
237
238    // save nonvolatile registrs: EDI, EBX, EBP
239    if (VM.VerifyAssertions) VM._assert(EDI_SAVE_OFFSET.toInt() == -2 * WORDSIZE);
240    asm.emitPUSH_Reg(EDI); // save nonvolatile EDI register
241    if (VM.VerifyAssertions) VM._assert(EBX_SAVE_OFFSET.toInt() == -3 * WORDSIZE);
242    asm.emitPUSH_Reg(EBX); // save nonvolatile EBX register
243    if (VM.VerifyAssertions) VM._assert(EBP_SAVE_OFFSET.toInt() == -4 * WORDSIZE);
244    asm.emitPUSH_Reg(EBP); // save nonvolatile EBP register
245
246    // Establish EBP as the framepointer for use in the rest of the glue frame
247    if (VM.BuildFor32Addr) {
248      asm.emitLEA_Reg_RegDisp(EBP, SP, Offset.fromIntSignExtend(4 * WORDSIZE));
249    } else {
250      asm.emitLEA_Reg_RegDisp_Quad(EBP, SP, Offset.fromIntSignExtend(4 * WORDSIZE));
251    }
252
253    // (2) Set up jniEnv - set up a register to hold JNIEnv and store
254    // the Processor in the JNIEnv for easy access
255
256    // S0 = RVMThread.jniEnv
257    if (VM.BuildFor32Addr) {
258      asm.emitMOV_Reg_RegDisp(S0, THREAD_REGISTER, Entrypoints.jniEnvField.getOffset());
259    } else {
260      asm.emitMOV_Reg_RegDisp_Quad(S0, THREAD_REGISTER, Entrypoints.jniEnvField.getOffset());
261    }
262    if (VM.VerifyAssertions) VM._assert(JNI_ENV_OFFSET.toInt() == -5 * WORDSIZE);
263    asm.emitPUSH_Reg(S0); // save JNI Env for after call
264
265    if (VM.VerifyAssertions) VM._assert(BP_ON_ENTRY_OFFSET.toInt() == -6 * WORDSIZE);
266    asm.emitPUSH_RegDisp(S0, Entrypoints.JNIEnvBasePointerOnEntryToNative.getOffset());
267    // save BP into JNIEnv
268    if (VM.BuildFor32Addr) {
269      asm.emitMOV_RegDisp_Reg(S0, Entrypoints.JNIEnvBasePointerOnEntryToNative.getOffset(), EBP);
270    } else {
271      asm.emitMOV_RegDisp_Reg_Quad(S0, Entrypoints.JNIEnvBasePointerOnEntryToNative.getOffset(), EBP);
272    }
273    // (3) Move all native method arguments on to stack (NB at this
274    // point all non-volatile state is saved)
275
276    // (3.1) Count how many arguments could be passed in either FPRs or GPRs
277    int numFprArgs = 0;
278    int numGprArgs = 0;
279    for (TypeReference arg : args) {
280      if (arg.isFloatType() || arg.isDoubleType()) {
281        numFprArgs++;
282      } else if (VM.BuildFor32Addr && arg.isLongType()) {
283        numGprArgs += 2;
284      } else {
285        numGprArgs++;
286      }
287    }
288
289    // (3.2) add stack aligning padding
290    if (VM.BuildFor64Addr) {
291      int argsInRegisters = Math.min(numFprArgs, NATIVE_PARAMETER_FPRS.length) +
292                            Math.min(numGprArgs + 2, NATIVE_PARAMETER_GPRS.length);
293      int argsOnStack = numGprArgs + numFprArgs + 2 - argsInRegisters;
294      if (VM.VerifyAssertions) VM._assert(argsOnStack >= 0);
295      if ((argsOnStack & 1) != 0) {
296        // need odd alignment prior to pushes
297        asm.emitAND_Reg_Imm_Quad(SP, -16);
298        asm.emitPUSH_Reg(T0);
299      } else {
300        // need even alignment prior to pushes
301        asm.emitAND_Reg_Imm_Quad(SP, -16);
302      }
303    }
304    // include this ptr now padding calculation is complete
305    // (we always pass a this or a class but we only pop this)
306    if (!method.isStatic()) {
307      numGprArgs++;
308    }
309
310    // (3.3) Walk over arguments backwards pushing either from memory or registers
311    Offset currentArg = lastParameterOffset;
312    int argFpr = numFprArgs - 1;
313    int argGpr = numGprArgs - 1;
314    for (int i = args.length - 1; i >= 0; i--) {
315      TypeReference arg = args[i];
316      if (arg.isFloatType()) {
317        if (argFpr < PARAMETER_FPRS.length) {
318          asm.emitPUSH_Reg(T0); // make space
319          if (SSE2_FULL) {
320            asm.emitMOVSS_RegInd_Reg(SP, (XMM)PARAMETER_FPRS[argFpr]);
321          } else {
322            asm.emitFSTP_RegInd_Reg(SP, FP0);
323          }
324        } else {
325          asm.emitPUSH_RegDisp(EBP, currentArg);
326        }
327        argFpr--;
328      } else if (arg.isDoubleType()) {
329        if (VM.BuildFor32Addr) {
330          if (argFpr < PARAMETER_FPRS.length) {
331            asm.emitPUSH_Reg(T0); // make space
332            asm.emitPUSH_Reg(T0); // need 2 slots with 32bit addresses
333            if (SSE2_FULL) {
334              asm.emitMOVSD_RegInd_Reg(SP, (XMM)PARAMETER_FPRS[argFpr]);
335            } else {
336              asm.emitFSTP_RegInd_Reg_Quad(SP, FP0);
337            }
338          } else {
339            asm.emitPUSH_RegDisp(EBP, currentArg.plus(WORDSIZE));
340            asm.emitPUSH_RegDisp(EBP, currentArg);  // need 2 slots with 32bit addresses
341          }
342        } else {
343          if (argFpr < PARAMETER_FPRS.length) {
344            asm.emitPUSH_Reg(T0); // make space
345            if (SSE2_FULL) {
346              asm.emitMOVSD_RegInd_Reg(SP, (XMM)PARAMETER_FPRS[argFpr]);
347            } else {
348              asm.emitFSTP_RegInd_Reg_Quad(SP, FP0);
349            }
350          } else {
351            asm.emitPUSH_RegDisp(EBP, currentArg);
352          }
353        }
354        argFpr--;
355        currentArg = currentArg.plus(WORDSIZE);
356      } else if (VM.BuildFor32Addr && arg.isLongType()) {
357        if (argGpr < PARAMETER_GPRS.length) {
358          asm.emitPUSH_Reg(PARAMETER_GPRS[argGpr - 1]);
359          asm.emitPUSH_Reg(PARAMETER_GPRS[argGpr]);
360        } else if (argGpr - 1 < PARAMETER_GPRS.length) {
361          asm.emitPUSH_Reg(PARAMETER_GPRS[argGpr - 1]);
362          asm.emitPUSH_RegDisp(EBP, currentArg);
363        } else {
364          asm.emitPUSH_RegDisp(EBP, currentArg.plus(WORDSIZE));
365          asm.emitPUSH_RegDisp(EBP, currentArg);
366        }
367        argGpr -= 2;
368        currentArg = currentArg.plus(WORDSIZE);
369      } else {
370        if (argGpr < PARAMETER_GPRS.length) {
371          asm.emitPUSH_Reg(PARAMETER_GPRS[argGpr]);
372        } else {
373          asm.emitPUSH_RegDisp(EBP, currentArg);
374        }
375        argGpr--;
376        if (VM.BuildFor64Addr && arg.isLongType()) {
377          currentArg = currentArg.plus(WORDSIZE);
378        }
379      }
380      currentArg = currentArg.plus(WORDSIZE);
381    }
382    // (3.4) push class or object argument
383    if (method.isStatic()) {
384      // push java.lang.Class object for klass
385      Offset klassOffset = Offset.fromIntSignExtend(
386          Statics.findOrCreateObjectLiteral(method.getDeclaringClass().getClassForType()));
387      asm.generateJTOCpush(klassOffset);
388    } else {
389      if (VM.VerifyAssertions) VM._assert(argGpr == 0);
390      asm.emitPUSH_Reg(PARAMETER_GPRS[0]);
391    }
392    // (3.5) push a pointer to the JNI functions that will be
393    // dereferenced in native code
394    asm.emitPUSH_Reg(S0);
395    if (jniExternalFunctionsFieldOffset != 0) {
396      if (VM.BuildFor32Addr) {
397        asm.emitADD_RegInd_Imm(ESP, jniExternalFunctionsFieldOffset);
398      } else {
399        asm.emitADD_RegInd_Imm_Quad(ESP, jniExternalFunctionsFieldOffset);
400      }
401    }
402
403    // (4) Call out to convert reference arguments to IDs, set thread as
404    // being "in native" and record the frame pointer of the last Java frame
405    // (this) in the jniEnv
406
407    // Encode reference arguments into a long
408    int encodedReferenceOffsets = 0;
409    for (int i = 0, pos = 0; i < args.length; i++, pos++) {
410      TypeReference arg = args[i];
411      if (arg.isReferenceType()) {
412        if (VM.VerifyAssertions) VM._assert(pos < 32);
413        encodedReferenceOffsets |= 1 << pos;
414      } else if (VM.BuildFor32Addr && (arg.isLongType() || arg.isDoubleType())) {
415        pos++;
416      }
417    }
418    // Call out to JNI environment JNI entry
419    if (VM.BuildFor32Addr) {
420      asm.emitMOV_Reg_RegDisp(PARAMETER_GPRS[0], EBP, JNI_ENV_OFFSET);
421    } else {
422      asm.emitMOV_Reg_RegDisp_Quad(PARAMETER_GPRS[0], EBP, JNI_ENV_OFFSET);
423    }
424    asm.emitPUSH_Reg(PARAMETER_GPRS[0]);
425    asm.emitMOV_Reg_Imm(PARAMETER_GPRS[1], encodedReferenceOffsets);
426    asm.emitPUSH_Reg(PARAMETER_GPRS[1]);
427    asm.baselineEmitLoadTIB(S0, PARAMETER_GPRS[0]);
428    asm.emitCALL_RegDisp(S0, Entrypoints.jniEntry.getOffset());
429
430    // (5) Set up stack frame and registers for transition to C
431    int stackholes = 0;
432    int position = 0;
433    int argsPassedInRegister = 0;
434    if (VM.BuildFor64Addr) {
435      int gpRegistersInUse = 2;
436      int fpRegistersInUse = 0;
437      boolean dataOnStack = false;
438      asm.emitPOP_Reg(NATIVE_PARAMETER_GPRS[0]); // JNI env
439      asm.emitPOP_Reg(NATIVE_PARAMETER_GPRS[1]); // Object/Class
440      argsPassedInRegister += 2;
441      for (TypeReference arg : method.getParameterTypes()) {
442        if (arg.isFloatType()) {
443          if (fpRegistersInUse < NATIVE_PARAMETER_FPRS.length) {
444            asm.emitMOVSS_Reg_RegDisp((XMM)NATIVE_PARAMETER_FPRS[fpRegistersInUse], SP,
445                Offset.fromIntZeroExtend(position << LG_WORDSIZE));
446            if (dataOnStack) {
447              stackholes |= 1 << position;
448            } else {
449              asm.emitPOP_Reg(T0);
450            }
451            fpRegistersInUse++;
452            argsPassedInRegister++;
453          } else {
454            // no register available so we have data on the stack
455            dataOnStack = true;
456          }
457        } else if (arg.isDoubleType()) {
458          if (fpRegistersInUse < NATIVE_PARAMETER_FPRS.length) {
459            asm.emitMOVSD_Reg_RegDisp((XMM)NATIVE_PARAMETER_FPRS[fpRegistersInUse], SP,
460                Offset.fromIntZeroExtend(position << LG_WORDSIZE));
461            if (dataOnStack) {
462              stackholes |= 1 << position;
463            } else {
464              asm.emitPOP_Reg(T0);
465            }
466            if (VM.BuildFor32Addr) asm.emitPOP_Reg(T0);
467            fpRegistersInUse++;
468            argsPassedInRegister += VM.BuildFor32Addr ? 2 : 1;
469          } else {
470            // no register available so we have data on the stack
471            dataOnStack = true;
472          }
473        } else {
474          if (gpRegistersInUse < NATIVE_PARAMETER_GPRS.length) {
475            // TODO: we can't have holes in the data that is on the stack, we need to shuffle it up
476            asm.emitMOV_Reg_RegDisp_Quad(NATIVE_PARAMETER_GPRS[gpRegistersInUse],
477                SP, Offset.fromIntZeroExtend(position << LG_WORDSIZE));
478            if (dataOnStack) {
479              stackholes |= 1 << position;
480            } else {
481              asm.emitPOP_Reg(T0);
482            }
483            gpRegistersInUse++;
484            argsPassedInRegister++;
485          } else {
486            // no register available so we have data on the stack
487            dataOnStack = true;
488          }
489        }
490        if (dataOnStack) {
491          position++;
492        }
493      }
494      position--;
495      int onStackOffset = position;
496      int mask = 0;
497      for (int i = position; i >= 0; i--) {
498        mask = 1 << i;
499        if ((stackholes & mask) != 0) {
500          continue;
501        }
502        if (i < onStackOffset) {
503          asm.emitMOV_Reg_RegDisp_Quad(T0, SP, Offset.fromIntZeroExtend(i << LOG_BYTES_IN_WORD));
504          asm.emitMOV_RegDisp_Reg_Quad(SP, Offset.fromIntZeroExtend(onStackOffset << LOG_BYTES_IN_WORD), T0);
505        }
506        onStackOffset--;
507      }
508      while (onStackOffset >= 0) {
509        asm.emitPOP_Reg(T0);
510        onStackOffset--;
511      }
512    }
513
514    // (6) Call out to C
515    // move address of native code to invoke into T0
516    if (VM.BuildFor32Addr) {
517      asm.emitMOV_Reg_Imm(T0, nativeIP.toInt());
518    } else {
519      asm.emitMOV_Reg_Imm_Quad(T0, nativeIP.toLong());
520    }
521    // Trap if stack alignment fails
522    if (VM.ExtremeAssertions && VM.BuildFor64Addr) {
523      asm.emitBT_Reg_Imm(ESP, 3);
524      ForwardReference fr = asm.forwardJcc(LGE);
525      asm.emitINT_Imm(3);
526      fr.resolve(asm);
527    }
528    // make the call to native code
529    asm.emitCALL_Reg(T0);
530
531    // (7) Discard parameters on stack
532    if (VM.BuildFor32Addr) {
533      // throw away args, class/this ptr and env
534      int argsToThrowAway = method.getParameterWords() + 2 - argsPassedInRegister;
535      if (argsToThrowAway != 0) {
536        asm.emitLEA_Reg_RegDisp(SP, EBP, BP_ON_ENTRY_OFFSET);
537      }
538    } else {
539      // throw away args, class/this ptr and env (and padding)
540      asm.emitLEA_Reg_RegDisp_Quad(SP, EBP, BP_ON_ENTRY_OFFSET);
541    }
542
543    // (8) Save result to stack
544    final TypeReference returnType = method.getReturnType();
545    if (returnType.isVoidType()) {
546      // Nothing to save
547    } else if (returnType.isFloatType()) {
548      asm.emitPUSH_Reg(T0); // adjust stack
549      if (VM.BuildFor32Addr) {
550        asm.emitFSTP_RegInd_Reg(ESP, FP0);
551      } else {
552        asm.emitMOVSS_RegInd_Reg(ESP, XMM0);
553      }
554    } else if (returnType.isDoubleType()) {
555      asm.emitPUSH_Reg(T0); // adjust stack
556      asm.emitPUSH_Reg(T0); // adjust stack
557      if (VM.BuildFor32Addr) {
558        asm.emitFSTP_RegInd_Reg_Quad(ESP, FP0);
559      } else {
560        asm.emitMOVSD_RegInd_Reg(ESP, XMM0);
561      }
562    } else if (VM.BuildFor32Addr && returnType.isLongType()) {
563      asm.emitPUSH_Reg(T0);
564      asm.emitPUSH_Reg(T1);
565    } else {
566      // Ensure sign-extension is correct
567      if (returnType.isBooleanType()) {
568        asm.emitMOVZX_Reg_Reg_Byte(T0, T0);
569      } else if (returnType.isByteType()) {
570        asm.emitMOVSX_Reg_Reg_Byte(T0, T0);
571      } else if (returnType.isCharType()) {
572        asm.emitMOVZX_Reg_Reg_Word(T0, T0);
573      } else if (returnType.isShortType()) {
574        asm.emitMOVSX_Reg_Reg_Word(T0, T0);
575      }
576      asm.emitPUSH_Reg(T0);
577    }
578
579    // (9) Recover RVM style frame
580    // (9.1) reload JNIEnvironment from glue frame
581    if (VM.BuildFor32Addr) {
582      asm.emitMOV_Reg_RegDisp(S0, EBP, JNICompiler.JNI_ENV_OFFSET);
583    } else {
584      asm.emitMOV_Reg_RegDisp_Quad(S0, EBP, JNICompiler.JNI_ENV_OFFSET);
585    }
586    // (9.2) Reload thread register from JNIEnvironment
587    if (VM.BuildFor32Addr) {
588      asm.emitMOV_Reg_RegDisp(THREAD_REGISTER, S0, Entrypoints.JNIEnvSavedTRField.getOffset());
589    } else {
590      asm.emitMOV_Reg_RegDisp_Quad(THREAD_REGISTER, S0, Entrypoints.JNIEnvSavedTRField.getOffset());
591    }
592    // (9.3) Establish frame pointer to this glue method
593    if (VM.BuildFor32Addr) {
594      asm.emitMOV_RegDisp_Reg(THREAD_REGISTER, ArchEntrypoints.framePointerField.getOffset(), EBP);
595    } else {
596      asm.emitMOV_RegDisp_Reg_Quad(THREAD_REGISTER, ArchEntrypoints.framePointerField.getOffset(), EBP);
597    }
598
599    // (10) Transition back from "in native" to "in Java", convert a reference
600    // result (currently a JNI ref) into a true reference, release JNI refs
601    if (VM.BuildFor32Addr) {
602      asm.emitMOV_Reg_Reg(PARAMETER_GPRS[0], S0); // 1st arg is JNI Env
603    } else {
604      asm.emitMOV_Reg_Reg_Quad(PARAMETER_GPRS[0], S0); // 1st arg is JNI Env
605    }
606
607    if (returnType.isReferenceType()) {
608      asm.emitPOP_Reg(PARAMETER_GPRS[1]);       // 2nd arg is ref result
609    } else {
610      // place dummy (null) operand on stack
611      asm.emitXOR_Reg_Reg(PARAMETER_GPRS[1], PARAMETER_GPRS[1]);
612    }
613    asm.emitPUSH_Reg(S0);                       // save JNIEnv
614    asm.emitPUSH_Reg(S0);                       // push arg 1
615    asm.emitPUSH_Reg(PARAMETER_GPRS[1]);        // push arg 2
616    // Do the call
617    asm.baselineEmitLoadTIB(S0, S0);
618    asm.emitCALL_RegDisp(S0, Entrypoints.jniExit.getOffset());
619    asm.emitPOP_Reg(S0); // restore JNIEnv
620
621    // (11) Restore stack and place result in register
622    // place result in register
623    if (returnType.isVoidType()) {
624      // Nothing to save
625    } else if (returnType.isReferenceType()) {
626      // value already in register
627    } else if (returnType.isFloatType()) {
628      if (SSE2_FULL) {
629        asm.emitMOVSS_Reg_RegInd(XMM0, ESP);
630      } else {
631        asm.emitFLD_Reg_RegInd(FP0, ESP);
632      }
633      asm.emitPOP_Reg(T0); // adjust stack
634    } else if (returnType.isDoubleType()) {
635      if (SSE2_FULL) {
636        asm.emitMOVSD_Reg_RegInd(XMM0, ESP);
637      } else {
638        asm.emitFLD_Reg_RegInd_Quad(FP0, ESP);
639      }
640      asm.emitPOP_Reg(T0); // adjust stack
641      asm.emitPOP_Reg(T0); // adjust stack
642    } else if (VM.BuildFor32Addr && returnType.isLongType()) {
643      asm.emitPOP_Reg(T0);
644      asm.emitPOP_Reg(T1);
645    } else {
646      asm.emitPOP_Reg(T0);
647    }
648
649    asm.emitPOP_Reg(EBX); // saved previous native BP
650    if (VM.BuildFor32Addr) {
651      asm.emitMOV_RegDisp_Reg(S0, Entrypoints.JNIEnvBasePointerOnEntryToNative.getOffset(), EBX);
652    } else {
653      asm.emitMOV_RegDisp_Reg_Quad(S0, Entrypoints.JNIEnvBasePointerOnEntryToNative.getOffset(), EBX);
654    }
655    asm.emitPOP_Reg(EBX); // throw away JNI env
656    asm.emitPOP_Reg(EBP); // restore non-volatile EBP
657    asm.emitPOP_Reg(EBX); // restore non-volatile EBX
658    asm.emitPOP_Reg(EDI); // restore non-volatile EDI
659    asm.emitPOP_Reg(S0);  // throw away cmid
660    asm.emitPOP_RegDisp(THREAD_REGISTER, ArchEntrypoints.framePointerField.getOffset());
661
662    // (12) Return to caller
663    // pop parameters from stack (Note that parameterWords does not include "this")
664    if (method.isStatic()) {
665      asm.emitRET_Imm(method.getParameterWords() << LG_WORDSIZE);
666    } else {
667      asm.emitRET_Imm((method.getParameterWords() + 1) << LG_WORDSIZE);
668    }
669
670    CodeArray code = asm.getMachineCodes();
671    cm.compileComplete(code);
672    return cm;
673  }
674
675  /**
676   * Handles the C to Java transition:  JNI methods in JNIFunctions.java.
677   * Creates a prologue for the baseline compiler.
678   * <pre>
679   * NOTE:
680   *   -We need THREAD_REGISTER to access Java environment; we can get it from
681   *    the JNIEnv* (which is an interior pointer to the JNIEnvironment)
682   *   -Unlike the powerPC scheme which has a special prolog preceding
683   *    the normal Java prolog, the Intel scheme replaces the Java prolog
684   *    completely with the special prolog
685   *
686   *            Stack on entry            Stack at end of prolog after call
687   *             high memory                       high memory
688   *            |            |                   |            |
689   *    EBP -&gt;  |saved FP    |                   |saved FP    |
690   *            |  ...       |                   |  ...       |
691   *            |            |                   |            |
692   *            |arg n-1     |                   |arg n-1     |
693   * native     |  ...       |                   |  ...       |
694   * caller     |arg 0       | JNIEnv*           |arg 0       | JNIEnvironment
695   *    ESP -&gt;  |return addr |                   |return addr |
696   *            |            |           EBP -&gt;  |saved FP    | outer most native frame pointer
697   *            |            |                   |methodID    | normal MethodID for JNI function
698   *            |            |                   |saved JavaFP| offset to preceeding java frame
699   *            |            |                   |saved nonvol| to be used for nonvolatile storage
700   *            |            |                   |  ...       |   including ebp on entry
701   *            |            |                   |arg 0       | copied in reverse order (JNIEnvironment)
702   *            |            |                   |  ...       |
703   *            |            |           ESP -&gt;  |arg n-1     |
704   *            |            |                   |            | normally compiled Java code continue
705   *            |            |                   |            |
706   *            |            |                   |            |
707   *            |            |                   |            |
708   *             low memory                        low memory
709   * </pre>
710   *
711   * @param asm the assembler to use
712   * @param method the method that's being compiled (i.e. the method which is a bridge
713   *  from native).
714   * @param methodID the id of the compiled method
715   */
716  public static void generateGlueCodeForJNIMethod(Assembler asm, NormalMethod method, int methodID) {
717    // Variable tracking the depth of the stack as we generate the prologue
718    int stackDepth = 0;
719    // 1st word of header = return address already pushed by CALL
720    // 2nd word of header = space for frame pointer
721    if (VM.VerifyAssertions) VM._assert(STACKFRAME_FRAME_POINTER_OFFSET.toInt() == stackDepth << LG_WORDSIZE);
722    asm.emitPUSH_Reg(EBP);
723    stackDepth--;
724    // start new frame:  set FP to point to the new frame
725    if (VM.BuildFor32Addr) {
726      asm.emitMOV_Reg_Reg(EBP, SP);
727    } else {
728      asm.emitMOV_Reg_Reg_Quad(EBP, SP);
729    }
730    // set 3rd word of header: method ID
731    if (VM.VerifyAssertions) VM._assert(STACKFRAME_METHOD_ID_OFFSET.toInt() == stackDepth << LG_WORDSIZE);
732    asm.emitPUSH_Imm(methodID);
733    stackDepth--;
734    // buy space for the SAVED_JAVA_FP
735    if (VM.VerifyAssertions) VM._assert(STACKFRAME_BODY_OFFSET.toInt() == stackDepth << LG_WORDSIZE);
736    asm.emitPUSH_Reg(T0);
737    stackDepth--;
738    // store non-volatiles
739    for (GPR r : NATIVE_NONVOLATILE_GPRS) {
740      if (r != EBP) {
741        asm.emitPUSH_Reg(r);
742      } else {
743        asm.emitPUSH_RegInd(EBP); // save original EBP value
744      }
745      stackDepth--;
746    }
747    for (FloatingPointMachineRegister r : NATIVE_NONVOLATILE_FPRS) {
748      // TODO: we assume non-volatile will hold at most a double
749      asm.emitPUSH_Reg(T0); // adjust space for double
750      asm.emitPUSH_Reg(T0);
751      stackDepth -= 2;
752      if (r instanceof XMM) {
753        asm.emitMOVSD_RegInd_Reg(SP, (XMM)r);
754      } else {
755        // NB this will fail for anything other than FPR0
756        asm.emitFST_RegInd_Reg_Quad(SP, (FPR)r);
757      }
758    }
759    if (VM.VerifyAssertions) {
760      boolean b = stackDepth << LG_WORDSIZE == STACKFRAME_BODY_OFFSET.toInt() - (SAVED_GPRS_FOR_JNI << LG_WORDSIZE);
761      if (!b) {
762        String msg = "of2fp=" + stackDepth + " sg4j=" + SAVED_GPRS_FOR_JNI;
763        VM._assert(VM.NOT_REACHED, msg);
764      }
765
766    }
767    // Adjust first param from JNIEnv* to JNIEnvironment.
768    final Offset firstStackArgOffset = Offset.fromIntSignExtend(2 * WORDSIZE);
769    if (jniExternalFunctionsFieldOffset != 0) {
770      if (NATIVE_PARAMETER_GPRS.length > 0) {
771        if (VM.BuildFor32Addr) {
772          asm.emitSUB_Reg_Imm(NATIVE_PARAMETER_GPRS[0], jniExternalFunctionsFieldOffset);
773        } else {
774          asm.emitSUB_Reg_Imm_Quad(NATIVE_PARAMETER_GPRS[0], jniExternalFunctionsFieldOffset);
775        }
776      } else {
777        if (VM.BuildFor32Addr) {
778          asm.emitSUB_RegDisp_Imm(EBP, firstStackArgOffset, jniExternalFunctionsFieldOffset);
779        } else {
780          asm.emitSUB_RegDisp_Imm_Quad(EBP, firstStackArgOffset, jniExternalFunctionsFieldOffset);
781        }
782      }
783    }
784
785    // copy the arguments in reverse order
786    final TypeReference[] argTypes = method.getParameterTypes(); // does NOT include implicit this or class ptr
787    Offset stackArgOffset = firstStackArgOffset;
788    final int startOfStackedArgs = stackDepth + 1; // negative value relative to EBP
789    int argGPR = 0;
790    int argFPR = 0;
791    for (TypeReference argType : argTypes) {
792      if (argType.isFloatType()) {
793        if (argFPR < NATIVE_PARAMETER_FPRS.length) {
794          asm.emitPUSH_Reg(T0); // adjust stack
795          if (VM.BuildForSSE2) {
796            asm.emitMOVSS_RegInd_Reg(SP, (XMM)NATIVE_PARAMETER_FPRS[argFPR]);
797          } else {
798            asm.emitFSTP_RegInd_Reg(SP, FP0);
799          }
800          argFPR++;
801        } else {
802          asm.emitPUSH_RegDisp(EBP, stackArgOffset);
803          stackArgOffset = stackArgOffset.plus(WORDSIZE);
804        }
805        stackDepth--;
806      } else if (argType.isDoubleType()) {
807        if (argFPR < NATIVE_PARAMETER_FPRS.length) {
808          asm.emitPUSH_Reg(T0); // adjust stack
809          asm.emitPUSH_Reg(T0);
810          if (VM.BuildForSSE2) {
811            asm.emitMOVSD_RegInd_Reg(SP, (XMM)NATIVE_PARAMETER_FPRS[argFPR]);
812          } else {
813            asm.emitFSTP_RegInd_Reg_Quad(SP, FP0);
814          }
815          argFPR++;
816        } else {
817          if (VM.BuildFor32Addr) {
818            asm.emitPUSH_RegDisp(EBP, stackArgOffset.plus(WORDSIZE));
819            asm.emitPUSH_RegDisp(EBP, stackArgOffset);
820            stackArgOffset = stackArgOffset.plus(2 * WORDSIZE);
821          } else {
822            asm.emitPUSH_Reg(T0); // adjust stack
823            asm.emitPUSH_RegDisp(EBP, stackArgOffset);
824            stackArgOffset = stackArgOffset.plus(WORDSIZE);
825          }
826        }
827        stackDepth -= 2;
828      } else if (argType.isLongType()) {
829        if (VM.BuildFor32Addr) {
830          if (argGPR + 1 < NATIVE_PARAMETER_GPRS.length) {
831            asm.emitPUSH_Reg(NATIVE_PARAMETER_GPRS[argGPR]);
832            asm.emitPUSH_Reg(NATIVE_PARAMETER_GPRS[argGPR + 1]);
833            argGPR += 2;
834          } else if (argGPR < NATIVE_PARAMETER_GPRS.length) {
835            asm.emitPUSH_RegDisp(EBP, stackArgOffset);
836            asm.emitPUSH_Reg(NATIVE_PARAMETER_GPRS[argGPR]);
837            argGPR++;
838            stackArgOffset = stackArgOffset.plus(WORDSIZE);
839          } else {
840            asm.emitPUSH_RegDisp(EBP, stackArgOffset.plus(WORDSIZE));
841            asm.emitPUSH_RegDisp(EBP, stackArgOffset);
842            stackArgOffset = stackArgOffset.plus(WORDSIZE * 2);
843          }
844          stackDepth -= 2;
845        } else {
846          asm.emitPUSH_Reg(T0); // adjust stack
847          if (argGPR < NATIVE_PARAMETER_GPRS.length) {
848            asm.emitPUSH_Reg(NATIVE_PARAMETER_GPRS[argGPR]);
849            argGPR++;
850          } else {
851            asm.emitPUSH_RegDisp(EBP, stackArgOffset);
852            stackDepth -= 2;
853            stackArgOffset = stackArgOffset.plus(WORDSIZE);
854          }
855          stackDepth -= 2;
856        }
857      } else {
858        // Reference, int or smaller type
859        // NB we don't convert JNI references to true references (as
860        // in the entry/exit to a JNI method) as our JNI functions
861        // expect integer arguments
862        if (argGPR < NATIVE_PARAMETER_GPRS.length) {
863          asm.emitPUSH_Reg(NATIVE_PARAMETER_GPRS[argGPR]);
864          argGPR++;
865        } else {
866          asm.emitPUSH_RegDisp(EBP, stackArgOffset);
867          stackArgOffset = stackArgOffset.plus(WORDSIZE);
868        }
869        stackDepth--;
870      }
871    }
872
873    // Restore JTOC register
874    if (JTOC_REGISTER != null) {
875      asm.emitMOV_Reg_Imm_Quad(JTOC_REGISTER, BootRecord.the_boot_record.tocRegister.toLong());
876    }
877
878    // START of code sequence to atomically change thread status from
879    // IN_JNI to IN_JAVA, looping in a call to
880    // RVMThread.leaveJNIBlockedFromJNIFunctionCallMethod if
881    // BLOCKED_IN_NATIVE
882    int retryLabel = asm.getMachineCodeIndex(); // backward branch label
883
884    // Restore THREAD_REGISTER from JNIEnvironment
885    if (VM.BuildFor32Addr) {
886      asm.emitMOV_Reg_RegDisp(EBX, EBP, Offset.fromIntSignExtend((startOfStackedArgs - 1) * WORDSIZE));   // pick up arg 0 (from our frame)
887      asm.emitMOV_Reg_RegDisp(THREAD_REGISTER, EBX, Entrypoints.JNIEnvSavedTRField.getOffset());
888    } else {
889      asm.emitMOV_Reg_RegDisp_Quad(EBX, EBP, Offset.fromIntSignExtend((startOfStackedArgs - 1) * WORDSIZE));   // pick up arg 0 (from our frame)
890      asm.emitMOV_Reg_RegDisp_Quad(THREAD_REGISTER, EBX, Entrypoints.JNIEnvSavedTRField.getOffset());
891    }
892
893    // what we need to keep in mind at this point:
894    // - EBX has JNI env (but it's nonvolatile)
895    // - EBP has the FP (but it's nonvolatile)
896    // - stack has the args but not the locals
897    // - TR has been restored
898
899    // attempt to change the thread state to IN_JAVA
900    asm.emitMOV_Reg_Imm(T0, RVMThread.IN_JNI);
901    asm.emitMOV_Reg_Imm(T1, RVMThread.IN_JAVA);
902    asm.emitLockNextInstruction();
903    asm.emitCMPXCHG_RegDisp_Reg(THREAD_REGISTER, Entrypoints.execStatusField.getOffset(), T1);
904
905    // if we succeeded, move on, else go into slow path
906    ForwardReference doneLeaveJNIRef = asm.forwardJcc(EQ);
907
908    // make the slow call
909    asm.generateJTOCcall(Entrypoints.leaveJNIBlockedFromJNIFunctionCallMethod.getOffset());
910
911    // arrive here when we've switched to IN_JAVA
912    doneLeaveJNIRef.resolve(asm);
913    // END of code sequence to change state from IN_JNI to IN_JAVA
914
915    // status is now IN_JAVA. GC can not occur while we execute on a processor
916    // in this state, so it is safe to access fields of objects.
917    // RVM TR register has been restored and EBX contains a pointer to
918    // the thread's JNIEnvironment.
919
920    // done saving, bump SP to reserve room for the local variables
921    // SP should now be at the point normally marked as emptyStackOffset
922    int numLocalVariables = method.getLocalWords() - method.getParameterWords();
923    // TODO: optimize this space adjustment
924    if (VM.BuildFor32Addr) {
925      asm.emitSUB_Reg_Imm(SP, (numLocalVariables << LG_WORDSIZE));
926    } else {
927      asm.emitSUB_Reg_Imm_Quad(SP, (numLocalVariables << LG_WORDSIZE));
928    }
929    // Retrieve -> preceeding "top" java FP from jniEnv and save in current
930    // frame of JNIFunction
931    if (VM.BuildFor32Addr) {
932      asm.emitMOV_Reg_RegDisp(S0, EBX, Entrypoints.JNITopJavaFPField.getOffset());
933    } else {
934      asm.emitMOV_Reg_RegDisp_Quad(S0, EBX, Entrypoints.JNITopJavaFPField.getOffset());
935    }
936    // get offset from current FP and save in hdr of current frame
937    if (VM.BuildFor32Addr) {
938      asm.emitSUB_Reg_Reg(S0, EBP);
939      asm.emitMOV_RegDisp_Reg(EBP, SAVED_JAVA_FP_OFFSET, S0);
940    } else {
941      asm.emitSUB_Reg_Reg_Quad(S0, EBP);
942      asm.emitMOV_RegDisp_Reg_Quad(EBP, SAVED_JAVA_FP_OFFSET, S0);
943    }
944
945    // clobber the saved frame pointer with that from the JNIEnvironment (work around for omit-frame-pointer)
946    if (VM.BuildFor32Addr) {
947      asm.emitMOV_Reg_RegDisp(S0, EBX, Entrypoints.JNIEnvBasePointerOnEntryToNative.getOffset());
948      asm.emitMOV_RegInd_Reg(EBP, S0);
949    } else {
950      asm.emitMOV_Reg_RegDisp_Quad(S0, EBX, Entrypoints.JNIEnvBasePointerOnEntryToNative.getOffset());
951      asm.emitMOV_RegInd_Reg_Quad(EBP, S0);
952    }
953    // put framePointer in Thread following Jikes RVM conventions.
954    if (VM.BuildFor32Addr) {
955      asm.emitMOV_RegDisp_Reg(THREAD_REGISTER, ArchEntrypoints.framePointerField.getOffset(), EBP);
956    } else {
957      asm.emitMOV_RegDisp_Reg_Quad(THREAD_REGISTER, ArchEntrypoints.framePointerField.getOffset(), EBP);
958    }
959
960    // at this point: TR has been restored &
961    // processor status = IN_JAVA,
962    // arguments for the call have been setup, space on the stack for locals
963    // has been acquired.
964
965    // finally proceed with the normal Java compiled code
966    // skip the thread switch test for now, see BaselineCompilerImpl.genThreadSwitchTest(true)
967    //asm.emitNOP(1); // end of prologue marker
968  }
969
970  /**
971   * Handles the C to Java transition:  JNI methods in JNIFunctions.java.
972   * Creates an epilogue for the baseline compiler.
973   *
974   * @param asm the assembler to use
975   * @param method the method that's being compiled
976   */
977  public static void generateEpilogForJNIMethod(Assembler asm, RVMMethod method) {
978    // assume RVM TR regs still valid. potentially T1 & T0 contain return
979    // values and should not be modified. we use regs saved in prolog and restored
980    // before return to do whatever needs to be done.
981
982    if (VM.BuildFor32Addr) {
983      // if returning long, switch the order of the hi/lo word in T0 and T1
984      if (method.getReturnType().isLongType()) {
985        asm.emitPUSH_Reg(T1);
986        asm.emitMOV_Reg_Reg(T1, T0);
987        asm.emitPOP_Reg(T0);
988      } else {
989        if (SSE2_FULL && VM.BuildFor32Addr) {
990          // Marshall from XMM0 -> FP0
991          if (method.getReturnType().isDoubleType()) {
992            if (VM.VerifyAssertions) VM._assert(VM.BuildFor32Addr);
993            asm.emitMOVSD_RegDisp_Reg(THREAD_REGISTER, Entrypoints.scratchStorageField.getOffset(), XMM0);
994            asm.emitFLD_Reg_RegDisp_Quad(FP0, THREAD_REGISTER, Entrypoints.scratchStorageField.getOffset());
995          } else if (method.getReturnType().isFloatType()) {
996            if (VM.VerifyAssertions) VM._assert(VM.BuildFor32Addr);
997            asm.emitMOVSS_RegDisp_Reg(THREAD_REGISTER, Entrypoints.scratchStorageField.getOffset(), XMM0);
998            asm.emitFLD_Reg_RegDisp(FP0, THREAD_REGISTER, Entrypoints.scratchStorageField.getOffset());
999          }
1000        }
1001      }
1002    }
1003
1004    // current processor status is IN_JAVA, so we only GC at yieldpoints
1005
1006    // S0 <- JNIEnvironment
1007    if (VM.BuildFor32Addr) {
1008      asm.emitMOV_Reg_RegDisp(S0, THREAD_REGISTER, Entrypoints.jniEnvField.getOffset());
1009    } else {
1010      asm.emitMOV_Reg_RegDisp_Quad(S0, THREAD_REGISTER, Entrypoints.jniEnvField.getOffset());
1011    }
1012
1013    // set jniEnv TopJavaFP using value saved in frame in prolog
1014    if (VM.BuildFor32Addr) {
1015      asm.emitMOV_Reg_RegDisp(EDI, EBP, SAVED_JAVA_FP_OFFSET);      // EDI<-saved TopJavaFP (offset)
1016      asm.emitADD_Reg_Reg(EDI, EBP);                                // change offset from FP into address
1017      asm.emitMOV_RegDisp_Reg(S0, Entrypoints.JNITopJavaFPField.getOffset(), EDI); // jniEnv.TopJavaFP <- EDI
1018    } else {
1019      asm.emitMOV_Reg_RegDisp_Quad(EDI, EBP, SAVED_JAVA_FP_OFFSET);      // EDI<-saved TopJavaFP (offset)
1020      asm.emitADD_Reg_Reg_Quad(EDI, EBP);                                // change offset from FP into address
1021      asm.emitMOV_RegDisp_Reg_Quad(S0, Entrypoints.JNITopJavaFPField.getOffset(), EDI); // jniEnv.TopJavaFP <- EDI
1022    }
1023
1024    // NOTE: we could save the TR in the JNI env, but no need, that would have
1025    // already been done.
1026
1027    // what's going on here:
1028    // - SP and EBP have important stuff in them, but that's fine, since
1029    //   a call will restore SP and EBP is non-volatile for RVM code
1030    // - TR still refers to the thread
1031
1032    // save return values
1033    asm.emitPUSH_Reg(T0);
1034    asm.emitPUSH_Reg(T1);
1035
1036    // attempt to change the thread state to IN_JNI
1037    asm.emitMOV_Reg_Imm(T0, RVMThread.IN_JAVA);
1038    asm.emitMOV_Reg_Imm(T1, RVMThread.IN_JNI);
1039    asm.emitLockNextInstruction();
1040    asm.emitCMPXCHG_RegDisp_Reg(THREAD_REGISTER, Entrypoints.execStatusField.getOffset(), T1);
1041
1042    // if success, skip the slow path call
1043    ForwardReference doneEnterJNIRef = asm.forwardJcc(EQ);
1044
1045    // fast path failed, make the call
1046    asm.generateJTOCcall(Entrypoints.enterJNIBlockedFromJNIFunctionCallMethod.getOffset());
1047
1048    // OK - we reach here when we have set the state to IN_JNI
1049    doneEnterJNIRef.resolve(asm);
1050
1051    // restore return values
1052    asm.emitPOP_Reg(T1);
1053    asm.emitPOP_Reg(T0);
1054
1055    // reload native/C nonvolatile regs - saved in prolog
1056    for (FloatingPointMachineRegister r : NATIVE_NONVOLATILE_FPRS) {
1057      // TODO: we assume non-volatile will hold at most a double
1058      if (r instanceof XMM) {
1059        asm.emitMOVSD_Reg_RegInd((XMM)r, SP);
1060      } else {
1061        // NB this will fail for anything other than FPR0
1062        asm.emitFLD_Reg_RegInd_Quad((FPR)r, SP);
1063      }
1064      asm.emitPOP_Reg(T0); // adjust space for double
1065      asm.emitPOP_Reg(T0);
1066    }
1067    // NB when EBP is restored it isn't our outer most EBP but rather than
1068    // nonvolatile push as the 1st instruction of the prologue
1069    for (int i = NATIVE_NONVOLATILE_GPRS.length - 1; i >= 0; i--) {
1070      GPR r = NATIVE_NONVOLATILE_GPRS[i];
1071      asm.emitPOP_Reg(r);
1072    }
1073
1074    // Discard JNIEnv, CMID and outer most native frame pointer
1075    if (VM.BuildFor32Addr) {
1076      asm.emitADD_Reg_Imm(SP, 3 * WORDSIZE); // discard current stack frame
1077    } else {
1078      asm.emitADD_Reg_Imm_Quad(SP, 3 * WORDSIZE); // discard current stack frame
1079    }
1080    asm.emitRET();                // return to caller
1081  }
1082}