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 */
013package org.jikesrvm.osr.ia32;
014
015import static org.jikesrvm.ia32.BaselineConstants.EBX_SAVE_OFFSET;
016import static org.jikesrvm.ia32.BaselineConstants.EDI_SAVE_OFFSET;
017import static org.jikesrvm.ia32.BaselineConstants.S0;
018import static org.jikesrvm.ia32.BaselineConstants.SP;
019import static org.jikesrvm.ia32.BaselineConstants.TR;
020import static org.jikesrvm.ia32.RegisterConstants.EBX;
021import static org.jikesrvm.ia32.RegisterConstants.EDI;
022import static org.jikesrvm.ia32.RegisterConstants.LG_INSTRUCTION_WIDTH;
023import static org.jikesrvm.ia32.RegisterConstants.NONVOLATILE_GPRS;
024import static org.jikesrvm.ia32.StackframeLayoutConstants.STACKFRAME_METHOD_ID_OFFSET;
025
026import org.jikesrvm.VM;
027import org.jikesrvm.adaptive.util.AOSLogging;
028import org.jikesrvm.compilers.common.CompiledMethod;
029import org.jikesrvm.compilers.common.CompiledMethods;
030import org.jikesrvm.compilers.common.assembler.ia32.Assembler;
031import org.jikesrvm.compilers.opt.runtimesupport.OptCompiledMethod;
032import org.jikesrvm.osr.ExecutionState;
033import org.jikesrvm.runtime.ArchEntrypoints;
034import org.jikesrvm.runtime.Magic;
035import org.jikesrvm.runtime.Memory;
036import org.jikesrvm.runtime.Statics;
037import org.jikesrvm.scheduler.RVMThread;
038import org.vmmagic.unboxed.Address;
039import org.vmmagic.unboxed.Offset;
040
041/**
042 * CodeInstaller generates a glue code which recovers registers and
043 * from the stack frames and branch to the newly compiled method instructions.
044 * The glue code is installed right before returning to the threading method
045 * by PostThreadSwitch
046 */
047public abstract class CodeInstaller {
048
049  public static boolean install(ExecutionState state, CompiledMethod cm) {
050    RVMThread thread = state.getThread();
051    byte[] stack = thread.getStack();
052
053    Offset tsfromFPOffset = state.getTSFPOffset();
054    Offset fooFPOffset = state.getFPOffset();
055
056    int foomid = Magic.getIntAtOffset(stack, fooFPOffset.plus(STACKFRAME_METHOD_ID_OFFSET));
057
058    CompiledMethod foo = CompiledMethods.getCompiledMethod(foomid);
059    int cType = foo.getCompilerType();
060
061    int SW_WIDTH = 4;
062
063    // this offset is used to adjust SP to FP right after return
064    // from a call. 4 bytes for return address and
065    // 4 bytes for saved FP of tsfrom.
066    Offset sp2fpOffset = fooFPOffset.minus(tsfromFPOffset).minus(2 * SW_WIDTH);
067
068    // should given an estimated length, and print the instructions
069    // for debugging
070    Assembler asm = new Assembler(50, VM.TraceOnStackReplacement);
071
072    // 1. generate bridge instructions to recover saved registers
073    if (cType == CompiledMethod.BASELINE) {
074
075//        asm.emitINT_Imm(3);  // break here for debugging
076
077      // unwind stack pointer, SP is FP now
078      asm.emitADD_Reg_Imm(SP, sp2fpOffset.toInt());
079
080      // TODO is this ok for 64-bit with JTOC in register?
081      asm.emitMOV_Reg_Abs(S0, Magic.getTocPointer().plus(cm.getOsrJTOCoffset()));
082
083      // restore saved EDI
084      asm.emitMOV_Reg_RegDisp(EDI, SP, EDI_SAVE_OFFSET);
085      // restore saved EBX
086      asm.emitMOV_Reg_RegDisp(EBX, SP, EBX_SAVE_OFFSET);
087      // restore frame pointer
088      asm.emitPOP_RegDisp(TR, ArchEntrypoints.framePointerField.getOffset());
089      // do not pop return address and parameters,
090      // we make a faked call to newly compiled method
091      asm.emitJMP_Reg(S0);
092    } else if (cType == CompiledMethod.OPT) {
093      ///////////////////////////////////////////////////
094      // recover saved registers from foo's stack frame
095      ///////////////////////////////////////////////////
096      OptCompiledMethod fooOpt = (OptCompiledMethod) foo;
097
098      // foo definitely not save volatile
099      boolean saveVolatile = fooOpt.isSaveVolatile();
100      if (VM.VerifyAssertions) {
101        VM._assert(!saveVolatile);
102      }
103
104      // assume SP is on foo's stack frame,
105      int firstNonVolatile = fooOpt.getFirstNonVolatileGPR();
106      int nonVolatiles = fooOpt.getNumberOfNonvolatileGPRs();
107      int nonVolatileOffset = fooOpt.getUnsignedNonVolatileOffset();
108
109      for (int i = firstNonVolatile; i < firstNonVolatile + nonVolatiles; i++) {
110        asm.emitMOV_Reg_RegDisp(NONVOLATILE_GPRS[i], SP, sp2fpOffset.minus(nonVolatileOffset));
111        nonVolatileOffset += SW_WIDTH;
112      }
113      // adjust SP to frame pointer
114      asm.emitADD_Reg_Imm(SP, sp2fpOffset.toInt());
115      // restore frame pointer
116      asm.emitPOP_RegDisp(TR, ArchEntrypoints.framePointerField.getOffset());
117
118      // branch to the newly compiled instructions
119      asm.emitJMP_Abs(Magic.getTocPointer().plus(cm.getOsrJTOCoffset()));
120    }
121
122    if (VM.TraceOnStackReplacement) {
123      VM.sysWrite("new CM instr addr ");
124      VM.sysWriteHex(Statics.getSlotContentsAsInt(cm.getOsrJTOCoffset()));
125      VM.sysWriteln();
126      VM.sysWrite("JTOC register ");
127      VM.sysWriteHex(Magic.getTocPointer());
128      VM.sysWriteln();
129      VM.sysWrite("Thread register ");
130      VM.sysWriteHex(Magic.objectAsAddress(Magic.getThreadRegister()));
131      VM.sysWriteln();
132
133      VM.sysWriteln("tsfromFPOffset ", tsfromFPOffset);
134      VM.sysWriteln("fooFPOffset ", fooFPOffset);
135      VM.sysWriteln("SP + ", sp2fpOffset.plus(4));
136    }
137
138    // 3. set thread flags
139    thread.isWaitingForOsr = true;
140    thread.bridgeInstructions = asm.getMachineCodes();
141    thread.fooFPOffset = fooFPOffset;
142    thread.tsFPOffset = tsfromFPOffset;
143
144    Address bridgeaddr = Magic.objectAsAddress(thread.bridgeInstructions);
145
146    Memory.sync(bridgeaddr, thread.bridgeInstructions.length() << LG_INSTRUCTION_WIDTH);
147
148    AOSLogging.logger.logOsrEvent("OSR code installation succeeded");
149
150    return true;
151  }
152}