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