/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Eclipse Public License (EPL); * 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/eclipse-1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.osr.ia32; import static org.jikesrvm.ia32.BaselineConstants.EBX_SAVE_OFFSET; import static org.jikesrvm.ia32.BaselineConstants.EDI_SAVE_OFFSET; import static org.jikesrvm.ia32.BaselineConstants.S0; import static org.jikesrvm.ia32.BaselineConstants.SP; import static org.jikesrvm.ia32.BaselineConstants.TR; import static org.jikesrvm.ia32.RegisterConstants.EBX; import static org.jikesrvm.ia32.RegisterConstants.EDI; import static org.jikesrvm.ia32.RegisterConstants.LG_INSTRUCTION_WIDTH; import static org.jikesrvm.ia32.RegisterConstants.NONVOLATILE_GPRS; import static org.jikesrvm.ia32.StackframeLayoutConstants.BYTES_IN_STACKSLOT; import static org.jikesrvm.ia32.StackframeLayoutConstants.STACKFRAME_METHOD_ID_OFFSET; import org.jikesrvm.VM; import org.jikesrvm.adaptive.util.AOSLogging; import org.jikesrvm.compilers.common.CompiledMethod; import org.jikesrvm.compilers.common.CompiledMethods; import org.jikesrvm.compilers.common.assembler.ia32.Assembler; import org.jikesrvm.compilers.opt.runtimesupport.OptCompiledMethod; import org.jikesrvm.osr.ExecutionState; import org.jikesrvm.runtime.ArchEntrypoints; import org.jikesrvm.runtime.Magic; import org.jikesrvm.runtime.Memory; import org.jikesrvm.runtime.Statics; import org.jikesrvm.scheduler.RVMThread; import org.vmmagic.unboxed.Address; import org.vmmagic.unboxed.Offset; /** * 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 PostThreadSwitch */ public abstract class CodeInstaller { public static boolean install(ExecutionState state, CompiledMethod cm) { RVMThread thread = state.getThread(); byte[] stack = thread.getStack(); Offset tsfromFPOffset = state.getTSFPOffset(); Offset fooFPOffset = state.getFPOffset(); int foomid = Magic.getIntAtOffset(stack, fooFPOffset.plus(STACKFRAME_METHOD_ID_OFFSET)); CompiledMethod foo = CompiledMethods.getCompiledMethod(foomid); int cType = foo.getCompilerType(); int SW_WIDTH = BYTES_IN_STACKSLOT; // this offset is used to adjust SP to FP right after return // from a call. 1 stack slot for return address and // 1 stack slot 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 Assembler asm = new Assembler(50, VM.TraceOnStackReplacement); // 1. generate bridge instructions to recover saved registers if (cType == CompiledMethod.BASELINE) { // asm.emitINT_Imm(3); // break here for debugging // unwind stack pointer, SP is FP now if (VM.BuildFor32Addr) { asm.emitADD_Reg_Imm(SP, sp2fpOffset.toInt()); } else { asm.emitADD_Reg_Imm_Quad(SP, sp2fpOffset.toInt()); } asm.generateJTOCloadWord(S0, cm.getOsrJTOCoffset()); // restore saved EDI if (VM.BuildFor32Addr) { asm.emitMOV_Reg_RegDisp(EDI, SP, EDI_SAVE_OFFSET); } else { asm.emitMOV_Reg_RegDisp_Quad(EDI, SP, EDI_SAVE_OFFSET); } // restore saved EBX if (VM.BuildFor32Addr) { asm.emitMOV_Reg_RegDisp(EBX, SP, EBX_SAVE_OFFSET); } else { asm.emitMOV_Reg_RegDisp_Quad(EBX, SP, EBX_SAVE_OFFSET); } // restore frame pointer asm.emitPOP_RegDisp(TR, 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 == CompiledMethod.OPT) { /////////////////////////////////////////////////// // recover saved registers from foo's stack frame /////////////////////////////////////////////////// OptCompiledMethod fooOpt = (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++) { if (VM.BuildFor32Addr) { asm.emitMOV_Reg_RegDisp(NONVOLATILE_GPRS[i], SP, sp2fpOffset.minus(nonVolatileOffset)); } else { asm.emitMOV_Reg_RegDisp_Quad(NONVOLATILE_GPRS[i], SP, sp2fpOffset.minus(nonVolatileOffset)); } nonVolatileOffset += SW_WIDTH; } // adjust SP to frame pointer if (VM.BuildFor32Addr) { asm.emitADD_Reg_Imm(SP, sp2fpOffset.toInt()); } else { asm.emitADD_Reg_Imm_Quad(SP, sp2fpOffset.toInt()); } // restore frame pointer asm.emitPOP_RegDisp(TR, ArchEntrypoints.framePointerField.getOffset()); // branch to the newly compiled instructions asm.generateJTOCjmp(cm.getOsrJTOCoffset()); } if (VM.TraceOnStackReplacement) { VM.sysWrite("new CM instr addr "); VM.sysWriteHex(Statics.getSlotContentsAsInt(cm.getOsrJTOCoffset())); VM.sysWriteln(); VM.sysWrite("JTOC register "); VM.sysWriteHex(Magic.getTocPointer()); VM.sysWriteln(); VM.sysWrite("Thread register "); VM.sysWriteHex(Magic.objectAsAddress(Magic.getThreadRegister())); 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 = Magic.objectAsAddress(thread.bridgeInstructions); Memory.sync(bridgeaddr, thread.bridgeInstructions.length() << LG_INSTRUCTION_WIDTH); AOSLogging.logger.logOsrEvent("OSR code installation succeeded"); return true; } }