/* * 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.osr.ia32; import org.jikesrvm.ArchitectureSpecific; import org.jikesrvm.VM; import org.jikesrvm.adaptive.util.VM_AOSLogging; import org.jikesrvm.compilers.common.VM_CompiledMethod; import org.jikesrvm.compilers.common.VM_CompiledMethods; import org.jikesrvm.compilers.common.assembler.ia32.VM_Assembler; import org.jikesrvm.compilers.opt.VM_OptCompiledMethod; import org.jikesrvm.ia32.VM_BaselineConstants; import org.jikesrvm.osr.OSR_ExecutionState; import org.jikesrvm.runtime.VM_ArchEntrypoints; import org.jikesrvm.runtime.VM_Magic; import org.jikesrvm.runtime.VM_Memory; import org.jikesrvm.runtime.VM_Statics; import org.jikesrvm.scheduler.VM_Thread; import org.vmmagic.unboxed.Address; import org.vmmagic.unboxed.Offset; /** * OSR_CodeInstaller generates a glue code which recovers registers and * from the stack frames and branch to the newly compiled method instructions. * The glue code is installed right before returning to the threading method * by OSR_PostThreadSwitch */ public abstract class OSR_CodeInstaller implements VM_BaselineConstants { public static boolean install(OSR_ExecutionState state, VM_CompiledMethod cm) { VM_Thread thread = state.getThread(); byte[] stack = thread.getStack(); Offset tsfromFPOffset = state.getTSFPOffset(); Offset fooFPOffset = state.getFPOffset(); int foomid = VM_Magic.getIntAtOffset(stack, fooFPOffset.plus(STACKFRAME_METHOD_ID_OFFSET)); VM_CompiledMethod foo = VM_CompiledMethods.getCompiledMethod(foomid); int cType = foo.getCompilerType(); int SW_WIDTH = 4; // this offset is used to adjust SP to FP right after return // from a call. 4 bytes for return address and // 4 bytes for saved FP of tsfrom. Offset sp2fpOffset = fooFPOffset.minus(tsfromFPOffset).minus(2 * SW_WIDTH); // should given an estimated length, and print the instructions // for debugging VM_Assembler asm = new ArchitectureSpecific.VM_Assembler(50, VM.TraceOnStackReplacement); // 1. generate bridge instructions to recover saved registers if (cType == VM_CompiledMethod.BASELINE) { // asm.emitINT_Imm(3); // break here for debugging // unwind stack pointer, SP is FP now asm.emitADD_Reg_Imm(SP, sp2fpOffset.toInt()); // before restoring caller's JTOC (maybe from opt compiler), we need // to use the true JTOC to get target address // use scratch register S0 to hold the address // ASSUMPTION: JTOC is really a JTOC, it is true for baseline compiler // VM_ProcessorLocalState.emitMoveFieldToReg(asm, JTOC, VM_Entrypoints.jtocField.getOffset()); asm.emitMOV_Reg_RegDisp(S0, JTOC, cm.getOsrJTOCoffset()); // restore the caller's JTOC asm.emitMOV_Reg_RegDisp(JTOC, SP, JTOC_SAVE_OFFSET); // this is the caller's JTOC // restore saved EBX asm.emitMOV_Reg_RegDisp(EBX, SP, EBX_SAVE_OFFSET); // restore frame pointer asm.emitPOP_RegDisp(PR, VM_ArchEntrypoints.framePointerField.getOffset()); // do not pop return address and parameters, // we make a faked call to newly compiled method asm.emitJMP_Reg(S0); } else if (cType == VM_CompiledMethod.OPT) { /////////////////////////////////////////////////// // recover saved registers from foo's stack frame /////////////////////////////////////////////////// VM_OptCompiledMethod fooOpt = (VM_OptCompiledMethod) foo; // foo definitely not save volatile boolean saveVolatile = fooOpt.isSaveVolatile(); if (VM.VerifyAssertions) { VM._assert(!saveVolatile); } // assume SP is on foo's stack frame, int firstNonVolatile = fooOpt.getFirstNonVolatileGPR(); int nonVolatiles = fooOpt.getNumberOfNonvolatileGPRs(); int nonVolatileOffset = fooOpt.getUnsignedNonVolatileOffset(); for (int i = firstNonVolatile; i < firstNonVolatile + nonVolatiles; i++) { asm.emitMOV_Reg_RegDisp(NONVOLATILE_GPRS[i], SP, sp2fpOffset.minus(nonVolatileOffset)); nonVolatileOffset += SW_WIDTH; } // adjust SP to frame pointer asm.emitADD_Reg_Imm(SP, sp2fpOffset.toInt()); // restore frame pointer asm.emitPOP_RegDisp(PR, VM_ArchEntrypoints.framePointerField.getOffset()); // we need a scratch registers here, using scratch register here // get JTOC content into S0 (ECX) asm.emitMOV_Reg_RegDisp(S0, PR, VM_ArchEntrypoints.jtocField.getOffset()); // move the address to S0 asm.emitMOV_Reg_RegDisp(S0, S0, cm.getOsrJTOCoffset()); // branch to the newly compiled instructions asm.emitJMP_Reg(S0); } if (VM.TraceOnStackReplacement) { VM.sysWrite("new CM instr addr "); VM.sysWriteHex(VM_Statics.getSlotContentsAsInt(cm.getOsrJTOCoffset())); VM.sysWriteln(); VM.sysWrite("JTOC register "); VM.sysWriteHex(VM_Magic.getTocPointer()); VM.sysWriteln(); VM.sysWrite("Processor register "); VM.sysWriteHex(VM_Magic.objectAsAddress(VM_Magic.getProcessorRegister())); VM.sysWriteln(); VM.sysWriteln("tsfromFPOffset ", tsfromFPOffset); VM.sysWriteln("fooFPOffset ", fooFPOffset); VM.sysWriteln("SP + ", sp2fpOffset.plus(4)); } // 3. set thread flags thread.isWaitingForOsr = true; thread.bridgeInstructions = asm.getMachineCodes(); thread.fooFPOffset = fooFPOffset; thread.tsFPOffset = tsfromFPOffset; Address bridgeaddr = VM_Magic.objectAsAddress(thread.bridgeInstructions); VM_Memory.sync(bridgeaddr, thread.bridgeInstructions.length() << LG_INSTRUCTION_WIDTH); VM_AOSLogging.logOsrEvent("OSR code installation succeeded"); return true; } }