/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Common Public License (CPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/cpl1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.ppc; import org.jikesrvm.ArchitectureSpecific; import org.jikesrvm.VM; import org.jikesrvm.compilers.common.assembler.VM_ForwardReference; import org.jikesrvm.compilers.common.assembler.ppc.VM_Assembler; import org.jikesrvm.compilers.common.assembler.ppc.VM_AssemblerConstants; import org.jikesrvm.jni.ppc.VM_JNIStackframeLayoutConstants; import org.jikesrvm.objectmodel.VM_ObjectModel; import org.jikesrvm.runtime.VM_ArchEntrypoints; import org.jikesrvm.runtime.VM_Entrypoints; import org.jikesrvm.scheduler.VM_Processor; import org.vmmagic.unboxed.Offset; /** * A place to put hand written machine code typically invoked by VM_Magic * methods. * * Hand coding of small inline instruction sequences is typically handled by * each compiler's implementation of VM_Magic methods. A few VM_Magic methods * are so complex that their implementations require many instructions. * But our compilers do not inline arbitrary amounts of machine code. * We therefore write such code blocks here, out of line. * * These code blocks can be shared by all compilers. They can be branched to * via a jtoc offset (obtained from VM_Entrypoints.XXXInstructionsMethod). * * 17 Mar 1999 Derek Lieber * * 15 Jun 2001 Dave Grove and Bowen Alpern (Derek believed that compilers * could inline these methods if they wanted. We do not believe this would * be very easy since they return thru the LR.) */ public abstract class VM_OutOfLineMachineCode implements VM_BaselineConstants, VM_JNIStackframeLayoutConstants, VM_AssemblerConstants { public static void init() { reflectiveMethodInvokerInstructions = generateReflectiveMethodInvokerInstructions(); saveThreadStateInstructions = generateSaveThreadStateInstructions(); threadSwitchInstructions = generateThreadSwitchInstructions(); restoreHardwareExceptionStateInstructions = generateRestoreHardwareExceptionStateInstructions(); invokeNativeFunctionInstructions = generateInvokeNativeFunctionInstructions(); } @SuppressWarnings("unused") // Accessed via VM_EntryPoints private static ArchitectureSpecific.VM_CodeArray reflectiveMethodInvokerInstructions; @SuppressWarnings("unused") // Accessed via VM_EntryPoints private static ArchitectureSpecific.VM_CodeArray saveThreadStateInstructions; @SuppressWarnings("unused") // Accessed via VM_EntryPoints private static ArchitectureSpecific.VM_CodeArray threadSwitchInstructions; @SuppressWarnings("unused") // Accessed via VM_EntryPoints private static ArchitectureSpecific.VM_CodeArray restoreHardwareExceptionStateInstructions; @SuppressWarnings("unused") // Accessed via VM_EntryPoints private static ArchitectureSpecific.VM_CodeArray invokeNativeFunctionInstructions; // Machine code for reflective method invocation. // See also: "VM_Compiler.generateMethodInvocation". // // Registers taken at runtime: // T0 == address of method entrypoint to be called // T1 == address of gpr registers to be loaded // T2 == address of fpr registers to be loaded // T4 == address of spill area in calling frame // // Registers returned at runtime: // standard return value conventions used // // Side effects at runtime: // artificial stackframe created and destroyed // R0, volatile, and scratch registers destroyed // private static ArchitectureSpecific.VM_CodeArray generateReflectiveMethodInvokerInstructions() { VM_Assembler asm = new ArchitectureSpecific.VM_Assembler(0); // // free registers: 0, S0 // asm.emitMFLR(0); // save... asm.emitSTAddr(0, STACKFRAME_NEXT_INSTRUCTION_OFFSET, FP); // ...return address asm.emitMTCTR(T0); // CTR := start of method code // // free registers: 0, S0, T0 // // create new frame // asm.emitMR(S0, FP); // S0 := old frame pointer asm.emitLIntOffset(T0, T4, VM_ObjectModel.getArrayLengthOffset()); // T0 := number of spill words asm.emitADDI(T4, -BYTES_IN_ADDRESS, T4); // T4 -= 4 (predecrement, ie. T4 + 4 is &spill[0] ) int spillLoopLabel = asm.getMachineCodeIndex(); asm.emitADDICr(T0, T0, -1); // T0 -= 1 (and set CR) VM_ForwardReference fr1 = asm.emitForwardBC(LT); // if T0 < 0 then break asm.emitLAddrU(0, BYTES_IN_ADDRESS, T4); // R0 := *(T4 += 4) asm.emitSTAddrU(0, -BYTES_IN_ADDRESS, FP); // put one word of spill area asm.emitB(spillLoopLabel); // goto spillLoop: fr1.resolve(asm); asm.emitSTAddrU(S0, -STACKFRAME_HEADER_SIZE, FP); // allocate frame header and save old fp asm.emitLVAL(T0, INVISIBLE_METHOD_ID); asm.emitSTWoffset(T0, FP, Offset.fromIntSignExtend(STACKFRAME_METHOD_ID_OFFSET)); // set method id // // free registers: 0, S0, T0, T4 // // load up fprs // VM_ForwardReference setupFPRLoader = asm.emitForwardBL(); for (int i = LAST_VOLATILE_FPR; i >= FIRST_VOLATILE_FPR; --i) { asm.emitLFDU(i, BYTES_IN_DOUBLE, T2); // FPRi := fprs[i] } // // free registers: 0, S0, T0, T2, T4 // // load up gprs // VM_ForwardReference setupGPRLoader = asm.emitForwardBL(); for (int i = LAST_VOLATILE_GPR; i >= FIRST_VOLATILE_GPR; --i) { asm.emitLAddrU(i, BYTES_IN_ADDRESS, S0); // GPRi := gprs[i] } // // free registers: 0, S0 // // invoke method // asm.emitBCCTRL(); // branch and link to method code // emit method epilog // asm.emitLAddr(FP, 0, FP); // restore caller's frame asm.emitLAddr(S0, STACKFRAME_NEXT_INSTRUCTION_OFFSET, FP); // pick up return address asm.emitMTLR(S0); // asm.emitBCLR(); // return to caller setupFPRLoader.resolve(asm); asm.emitMFLR(T4); // T4 := address of first fpr load instruction asm.emitLIntOffset(T0, T2, VM_ObjectModel.getArrayLengthOffset()); // T0 := number of fprs to be loaded asm.emitADDI(T4, VOLATILE_FPRS << LG_INSTRUCTION_WIDTH, T4); // T4 := address of first instruction following fpr loads asm.emitSLWI(T0, T0, LG_INSTRUCTION_WIDTH); // T0 := number of bytes of fpr load instructions asm.emitSUBFC(T4, T0, T4); // T4 := address of instruction for highest numbered fpr to be loaded asm.emitMTLR(T4); // LR := """ asm.emitADDI(T2, -BYTES_IN_DOUBLE, T2); // predecrement fpr index (to prepare for update instruction) asm.emitBCLR(); // branch to fpr loading instructions setupGPRLoader.resolve(asm); asm.emitMFLR(T4); // T4 := address of first gpr load instruction asm.emitLIntOffset(T0, T1, VM_ObjectModel.getArrayLengthOffset()); // T0 := number of gprs to be loaded asm.emitADDI(T4, VOLATILE_GPRS << LG_INSTRUCTION_WIDTH, T4); // T4 := address of first instruction following gpr loads asm.emitSLWI(T0, T0, LG_INSTRUCTION_WIDTH); // T0 := number of bytes of gpr load instructions asm.emitSUBFC(T4, T0, T4); // T4 := address of instruction for highest numbered gpr to be loaded asm.emitMTLR(T4); // LR := """ asm.emitADDI(S0, -BYTES_IN_ADDRESS, T1); // predecrement gpr index (to prepare for update instruction) asm.emitBCLR(); // branch to gpr loading instructions return asm.makeMachineCode().getInstructions(); } // Machine code to implement "VM_Magic.saveThreadState()". // // Registers taken at runtime: // T0 == address of VM_Registers object // // Registers returned at runtime: // none // // Side effects at runtime: // T1 destroyed // private static ArchitectureSpecific.VM_CodeArray generateSaveThreadStateInstructions() { VM_Assembler asm = new ArchitectureSpecific.VM_Assembler(0); // save return address // asm.emitMFLR(T1); // T1 = LR (return address) asm.emitSTAddrOffset(T1, T0, VM_ArchEntrypoints.registersIPField.getOffset()); // registers.ip = return address // save non-volatile fprs // asm.emitLAddrOffset(T1, T0, VM_ArchEntrypoints.registersFPRsField.getOffset()); // T1 := registers.fprs[] for (int i = FIRST_NONVOLATILE_FPR; i <= LAST_NONVOLATILE_FPR; ++i) { asm.emitSTFD(i, i << LOG_BYTES_IN_DOUBLE, T1); } // save non-volatile gprs // asm.emitLAddrOffset(T1, T0, VM_ArchEntrypoints.registersGPRsField.getOffset()); // T1 := registers.gprs[] for (int i = FIRST_NONVOLATILE_GPR; i <= LAST_NONVOLATILE_GPR; ++i) { asm.emitSTAddr(i, i << LOG_BYTES_IN_ADDRESS, T1); } // save fp // asm.emitSTAddr(FP, FP << LOG_BYTES_IN_ADDRESS, T1); // return to caller // asm.emitBCLR(); return asm.makeMachineCode().getInstructions(); } /** * Machine code to implement "VM_Magic.threadSwitch()". * * Parameters taken at runtime: * T0 == address of VM_Thread object for the current thread * T1 == address of VM_Registers object for the new thread * * Registers returned at runtime: * none * * Side effects at runtime: * sets current Thread's beingDispatched field to false * saves current Thread's nonvolatile hardware state in its VM_Registers object * restores new thread's VM_Registers nonvolatile hardware state. * execution resumes at address specificed by restored thread's VM_Registers ip field */ private static ArchitectureSpecific.VM_CodeArray generateThreadSwitchInstructions() { VM_Assembler asm = new ArchitectureSpecific.VM_Assembler(0); Offset ipOffset = VM_ArchEntrypoints.registersIPField.getOffset(); Offset fprsOffset = VM_ArchEntrypoints.registersFPRsField.getOffset(); Offset gprsOffset = VM_ArchEntrypoints.registersGPRsField.getOffset(); // (1) Save nonvolatile hardware state of current thread. asm.emitMFLR(T3); // T3 gets return address asm.emitLAddrOffset(T2, T0, VM_Entrypoints.threadContextRegistersField.getOffset()); // T2 = T0.contextRegisters asm.emitSTAddrOffset(T3, T2, ipOffset); // T0.contextRegisters.ip = return address // save non-volatile fprs asm.emitLAddrOffset(T3, T2, fprsOffset); // T3 := T0.contextRegisters.fprs[] for (int i = FIRST_NONVOLATILE_FPR; i <= LAST_NONVOLATILE_FPR; ++i) { asm.emitSTFD(i, i << LOG_BYTES_IN_DOUBLE, T3); } // save non-volatile gprs asm.emitLAddrOffset(T3, T2, gprsOffset); // T3 := registers.gprs[] for (int i = FIRST_NONVOLATILE_GPR; i <= LAST_NONVOLATILE_GPR; ++i) { asm.emitSTAddr(i, i << LOG_BYTES_IN_ADDRESS, T3); } // save fp asm.emitSTAddr(FP, FP << LOG_BYTES_IN_ADDRESS, T3); // (2) Set currentThread.beingDispatched to false asm.emitLVAL(0, 0); // R0 := 0 asm.emitSTBoffset(0, T0, VM_Entrypoints.beingDispatchedField.getOffset()); // T0.beingDispatched := R0 // (3) Restore nonvolatile hardware state of new thread. // restore non-volatile fprs asm.emitLAddrOffset(T0, T1, fprsOffset); // T0 := T1.fprs[] for (int i = FIRST_NONVOLATILE_FPR; i <= LAST_NONVOLATILE_FPR; ++i) { asm.emitLFD(i, i << LOG_BYTES_IN_DOUBLE, T0); } // restore non-volatile gprs asm.emitLAddrOffset(T0, T1, gprsOffset); // T0 := T1.gprs[] for (int i = FIRST_NONVOLATILE_GPR; i <= LAST_NONVOLATILE_GPR; ++i) { asm.emitLAddr(i, i << LOG_BYTES_IN_ADDRESS, T0); } // restore fp asm.emitLAddr(FP, FP << LOG_BYTES_IN_ADDRESS, T0); // resume execution at saved ip (T1.ipOffset) asm.emitLAddrOffset(T0, T1, ipOffset); asm.emitMTLR(T0); asm.emitBCLR(); return asm.makeMachineCode().getInstructions(); } // Machine code to implement "VM_Magic.restoreHardwareExceptionState()". // // Registers taken at runtime: // T0 == address of VM_Registers object // // Registers returned at runtime: // none // // Side effects at runtime: // all registers are restored except condition registers, count register, // JTOC_POINTER, and PROCESSOR_REGISTER with execution resuming at "registers.ip" // private static ArchitectureSpecific.VM_CodeArray generateRestoreHardwareExceptionStateInstructions() { VM_Assembler asm = new ArchitectureSpecific.VM_Assembler(0); // restore LR // asm.emitLAddrOffset(REGISTER_ZERO, T0, VM_ArchEntrypoints.registersLRField.getOffset()); asm.emitMTLR(REGISTER_ZERO); // restore IP (hold it in CT register for a moment) // asm.emitLAddrOffset(REGISTER_ZERO, T0, VM_ArchEntrypoints.registersIPField.getOffset()); asm.emitMTCTR(REGISTER_ZERO); // restore fprs // asm.emitLAddrOffset(T1, T0, VM_ArchEntrypoints.registersFPRsField.getOffset()); // T1 := registers.fprs[] for (int i = 0; i < NUM_FPRS; ++i) { asm.emitLFD(i, i << LOG_BYTES_IN_DOUBLE, T1); } // restore gprs // asm.emitLAddrOffset(T1, T0, VM_ArchEntrypoints.registersGPRsField.getOffset()); // T1 := registers.gprs[] for (int i = FIRST_NONVOLATILE_GPR; i <= LAST_NONVOLATILE_GPR; ++i) { asm.emitLAddr(i, i << LOG_BYTES_IN_ADDRESS, T1); } for (int i = FIRST_SCRATCH_GPR; i <= LAST_SCRATCH_GPR; ++i) { asm.emitLAddr(i, i << LOG_BYTES_IN_ADDRESS, T1); } for (int i = FIRST_VOLATILE_GPR; i <= LAST_VOLATILE_GPR; ++i) { if (i != T1) asm.emitLAddr(i, i << LOG_BYTES_IN_ADDRESS, T1); } // restore specials // asm.emitLAddr(REGISTER_ZERO, REGISTER_ZERO << LOG_BYTES_IN_ADDRESS, T1); asm.emitLAddr(FP, FP << LOG_BYTES_IN_ADDRESS, T1); // restore last gpr // asm.emitLAddr(T1, T1 << LOG_BYTES_IN_ADDRESS, T1); // resume execution at IP // asm.emitBCCTR(); return asm.makeMachineCode().getInstructions(); } /** * Generate innermost transition from Java => C code used by native method. * on entry: * JTOC = TOC for native call * S0 = threads JNIEnvironment, which contains saved PR reg * S1 = IP address of native function to branch to * Parameter regs R4-R10, FP1-FP6 loaded for call to native * (R3 will be set here before branching to the native function) * * GPR3 (T0), PR regs are available for scratch regs on entry * * on exit: * RVM JTOC and PR restored * return values from native call stored in stackframe */ private static ArchitectureSpecific.VM_CodeArray generateInvokeNativeFunctionInstructions() { VM_Assembler asm = new ArchitectureSpecific.VM_Assembler(0); // move native code address to CTR reg; // do this early so that S1 will be available as a scratch. asm.emitMTCTR(S1); // // store the return address to the Java to C glue prolog, which is now in LR // into transition frame. If GC occurs, the JNIGCMapIterator will cause // this ip address to be relocated if the generated glue code is moved. // asm.emitLAddr(S1, 0, FP); asm.emitMFLR(T0); if (VM.BuildForSVR4ABI || VM.BuildForMachOABI) { // save return address of JNI method in mini frame (1) asm.emitSTAddr(T0, STACKFRAME_NEXT_INSTRUCTION_OFFSET, S1); } else { if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerOpenABI); // save return address in stack frame if (VM.BuildForPowerOpenABI) { asm.emitSTAddr(T0, -JNI_PROLOG_RETURN_ADDRESS_OFFSET, S1); } } // // Load required JNI function ptr into first parameter reg (GPR3/T0) // This pointer is an interior pointer to the VM_JNIEnvironment which is // currently in S0. // asm.emitADDI(T0, VM_Entrypoints.JNIExternalFunctionsField.getOffset(), S0); // // change the vpstatus of the VP to IN_NATIVE // asm.emitLAddrOffset(PROCESSOR_REGISTER, S0, VM_Entrypoints.JNIEnvSavedPRField.getOffset()); asm.emitLVAL(S0, VM_Processor.IN_NATIVE); asm.emitSTWoffset(S0, PROCESSOR_REGISTER, VM_Entrypoints.vpStatusField.getOffset()); // // CALL NATIVE METHOD // asm.emitBCCTRL(); // save the return value in R3-R4 in the glue frame spill area since they may be overwritten // if we have to call sysVirtualProcessorYield because we are locked in native. if (VM.BuildFor64Addr) { asm.emitSTD(T0, NATIVE_FRAME_HEADER_SIZE, FP); } else { asm.emitSTW(T0, NATIVE_FRAME_HEADER_SIZE, FP); asm.emitSTW(T1, NATIVE_FRAME_HEADER_SIZE + BYTES_IN_ADDRESS, FP); } // // try to return virtual processor to vpStatus IN_JAVA // int label1 = asm.getMachineCodeIndex(); asm.emitLAddr(S0, 0, FP); // get previous frame if (VM.BuildForSVR4ABI || VM.BuildForMachOABI) { // mini frame (2) FP -> mini frame (1) FP -> java caller asm.emitLAddr(S0, 0, S0); } asm.emitLAddr(PROCESSOR_REGISTER, -JNI_ENV_OFFSET, S0); // load VM_JNIEnvironment asm.emitLAddrOffset(JTOC, PROCESSOR_REGISTER, VM_ArchEntrypoints.JNIEnvSavedJTOCField.getOffset()); // load JTOC asm.emitLAddrOffset(PROCESSOR_REGISTER, PROCESSOR_REGISTER, VM_Entrypoints.JNIEnvSavedPRField.getOffset()); // load PR asm.emitLVALAddr(S1, VM_Entrypoints.vpStatusField.getOffset()); asm.emitLWARX(S0, S1, PROCESSOR_REGISTER); // get status for processor asm.emitCMPI(S0, VM_Processor.BLOCKED_IN_NATIVE); // are we blocked in native code? VM_ForwardReference fr = asm.emitForwardBC(NE); // // if blocked in native, call C routine to do pthread_yield // asm.emitLAddrOffset(T2, JTOC, VM_Entrypoints.the_boot_recordField.getOffset()); // T2 gets boot record address asm.emitLAddrOffset(T1, T2, VM_Entrypoints.sysVirtualProcessorYieldIPField.getOffset()); // load addr of function if (VM.BuildForPowerOpenABI) { /* T1 points to the function descriptor, so we'll load TOC and IP from that */ asm.emitLAddrOffset(JTOC, T1, Offset.fromIntSignExtend(BYTES_IN_ADDRESS)); // load TOC asm.emitLAddrOffset(T1, T1, Offset.zero()); } asm.emitMTLR(T1); asm.emitBCLRL(); // call sysVirtualProcessorYield in sys.C asm.emitB(label1); // retest the attempt to change status to IN_JAVAE // // br to here -not blocked in native // fr.resolve(asm); asm.emitLVAL(S0, VM_Processor.IN_JAVA); // S0 <- new state value asm.emitSTWCXr(S0, S1, PROCESSOR_REGISTER); // attempt to change state to java asm.emitBC(NE, label1); // br if failure -retry lwarx // // return to caller // asm.emitLAddr(S0, 0, FP); // get previous frame if (VM.BuildForSVR4ABI || VM.BuildForMachOABI) { asm.emitLAddr(S0, STACKFRAME_NEXT_INSTRUCTION_OFFSET, S0); } else { if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerOpenABI); asm.emitLAddr(S0, -JNI_PROLOG_RETURN_ADDRESS_OFFSET, S0); // get return address from stack frame } asm.emitMTLR(S0); asm.emitBCLR(); return asm.makeMachineCode().getInstructions(); } }