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, 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 }