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 & 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 -> |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 -> |return addr | |return addr | 696 * | | EBP -> |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 -> |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}