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