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    }