/*
 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
 *
 *  This file is licensed to You under the Common Public License (CPL);
 *  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/cpl1.0.php
 *
 *  See the COPYRIGHT.txt file distributed with this work for information
 *  regarding copyright ownership.
 */
package org.jikesrvm.osr.ppc;

import org.jikesrvm.ArchitectureSpecific;
import org.jikesrvm.VM;
import org.jikesrvm.adaptive.util.AOSLogging;
import org.jikesrvm.compilers.baseline.BaselineCompiledMethod;
import org.jikesrvm.compilers.baseline.ppc.BaselineCompilerImpl;
import org.jikesrvm.compilers.common.CompiledMethod;
import org.jikesrvm.compilers.common.CompiledMethods;
import org.jikesrvm.compilers.common.assembler.ppc.Assembler;
import org.jikesrvm.compilers.opt.runtimesupport.OptCompiledMethod;
import org.jikesrvm.osr.ExecutionState;
import org.jikesrvm.ppc.BaselineConstants;
import org.jikesrvm.ppc.MachineCode;
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 adjusts registers and return address to make a
 * specialized thread as a normal thread get scheduled. The method
 * prologue ( machine code ) is adjusted to cooperate with the code
 * installer.
 */
public abstract class CodeInstaller implements BaselineConstants {

  /* install the newly compiled instructions. */
  public static boolean install(ExecutionState state, CompiledMethod cm) {

    RVMThread thread = state.getThread();
    byte[] stack = thread.getStack();

    Offset fooFPOffset = state.getFPOffset();

    // we are going to dynamically generate some code recover
    // register values from the stack frame.
    int foomid = Magic.getIntAtOffset(stack, fooFPOffset.plus(STACKFRAME_METHOD_ID_OFFSET));

    CompiledMethod foo = CompiledMethods.getCompiledMethod(foomid);
    int cType = foo.getCompilerType();

    Assembler asm = new ArchitectureSpecific.Assembler(0, VM.TraceOnStackReplacement);

    /////////////////////////////////////
    ////// recover saved registers.
    /////////////////////////////////////
    if (cType == CompiledMethod.BASELINE) {
      BaselineCompiledMethod bcm = (BaselineCompiledMethod) foo;
      int offset = BaselineCompilerImpl.getFrameSize(bcm);
      for (int i = bcm.getLastFloatStackRegister(); i >= FIRST_FLOAT_LOCAL_REGISTER; --i) {
        offset -= BYTES_IN_DOUBLE;
        asm.emitLFD(i, offset, FP);
      }
      for (int i = bcm.getLastFixedStackRegister(); i >= FIRST_FIXED_LOCAL_REGISTER; --i) {
        offset -= BYTES_IN_ADDRESS;
        asm.emitLAddr(i, offset, FP);
      }
    } else if (cType == CompiledMethod.OPT) {
      OptCompiledMethod fooOpt = (OptCompiledMethod) foo;

      // foo definitely not save volatile.
      boolean saveVolatile = fooOpt.isSaveVolatile();
      if (VM.VerifyAssertions) {
        VM._assert(!saveVolatile);
      }

      int offset = fooOpt.getUnsignedNonVolatileOffset();

      // recover nonvolatile GPRs
      int firstGPR = fooOpt.getFirstNonVolatileGPR();
      if (firstGPR != -1) {
        for (int i = firstGPR; i <= LAST_NONVOLATILE_GPR; i++) {
          asm.emitLAddr(i, offset, FP);
          offset += BYTES_IN_STACKSLOT;
        }
      }

      // recover nonvolatile FPRs
      int firstFPR = fooOpt.getFirstNonVolatileFPR();
      if (firstFPR != -1) {
        for (int i = firstFPR; i <= LAST_NONVOLATILE_FPR; i++) {
          asm.emitLFD(i, offset, FP);
          offset += BYTES_IN_DOUBLE;
        }
      }
    }

    if (VM.VerifyAssertions) {
      Object jtocContent = Statics.getSlotContentsAsObject(cm.getOsrJTOCoffset());
      VM._assert(jtocContent == cm.getEntryCodeArray());
    }

    // load address of newInstructions from JTOC
    asm.emitLAddrToc(S0, cm.getOsrJTOCoffset());
    // mov CTR addr
    asm.emitMTCTR(S0);
    // lwz FP, 0(FP)
    asm.emitLAddr(FP, 0, FP);
    // lwz T0, NEXT_INSTR(FP)
    asm.emitLAddr(S0, STACKFRAME_NEXT_INSTRUCTION_OFFSET, FP);
    // mov LR, addr
    asm.emitMTLR(S0);
    // bctr
    asm.emitBCCTR();

    MachineCode mc = asm.makeMachineCode();

    // mark the thread as waiting for on stack replacement.
    thread.isWaitingForOsr = true;
    thread.bridgeInstructions = mc.getInstructions();
    thread.fooFPOffset = fooFPOffset;

    Address bridgeaddr = Magic.objectAsAddress(thread.bridgeInstructions);

    Memory.sync(bridgeaddr, thread.bridgeInstructions.length() << LG_INSTRUCTION_WIDTH);

    AOSLogging.logOsrEvent("OSR code installation succeeded");

    return true;
  }
}
