/*
* 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;
}
}