/*
 * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 *
 */

package sun.jvm.hotspot.runtime.sparc;

import java.util.*;

import sun.jvm.hotspot.asm.sparc.*;
import sun.jvm.hotspot.code.*;
import sun.jvm.hotspot.compiler.*;
import sun.jvm.hotspot.debugger.*;
import sun.jvm.hotspot.debugger.cdbg.*;
import sun.jvm.hotspot.oops.*;
import sun.jvm.hotspot.runtime.*;
import sun.jvm.hotspot.runtime.posix.*;
import sun.jvm.hotspot.types.*;
import sun.jvm.hotspot.utilities.*;

/** Specialization of and implementation of abstract methods of the
    Frame class for the SPARC CPU. (FIXME: this is as quick a port as
    possible to get things running; will have to do a better job right
    away.) */

public class SPARCFrame extends Frame {
  // The pc value is the raw return address, plus 8 (pcReturnOffset()).
  // the value of sp and youngerSP that is stored in this object
  // is always, always, always the value that would be found in the
  // register (or window save area) while the target VM was executing.
  // The caller of the constructor will alwasy know if has a biased or
  // unbiased version of the stack pointer and can convert real (unbiased)
  // value via a helper routine we supply.
  // Whenever we return sp or youngerSP values we do not return the internal
  // value but the real (unbiased) pointers since these are the true, usable
  // memory addresses. The outlier case is that of the null pointer. The current
  // mechanism makes null pointers always look null whether biased or not.
  // This seems to cause no problems. In theory null real pointers could be biased
  // just like other values however this has impact on things like addOffsetTo()
  // to be able to take an Address that represents null and add an offset to it.
  // This doesn't seem worth the bother and the impact on the rest of the code
  // when the biasSP and unbiasSP can make this invisible.
  //
  // The general rule in this code is that when we have a variable like FP, youngerSP, SP
  // that these are real (i.e. unbiased) addresses. The instance variables in a Frame are
  // always raw values. The other rule is that it except for the frame constructors and
  // the unBiasSP helper all methods accept parameters that are real addresses.
  //

  /** Optional next-younger SP (used to locate O7, the PC) */
  private Address raw_youngerSP;

  /** Intepreter adjusts the stack pointer to make all locals contiguous */
  private long    interpreterSPAdjustmentOffset;

  /** Number of stack entries for longs */
  private static final int WORDS_PER_LONG = 2;

  /** Normal SPARC return is 2 words past PC */
  public static final int PC_RETURN_OFFSET = 8;

  /** Size of each block, in order of increasing address */
  public static final int REGISTER_SAVE_WORDS   = 16;
  // FIXME: read these from the remote process
  //#ifdef _LP64
  //    callee_aggregate_return_pointer_words        =  0,
  //#else
  //    callee_aggregate_return_pointer_words        =  1,
  //#endif
  public static final int CALLEE_AGGREGATE_RETURN_POINTER_WORDS     = 1;
  public static final int CALLEE_REGISTER_ARGUMENT_SAVE_AREA_WORDS  = 6;

  // offset of each block, in order of increasing address:
  public static final int REGISTER_SAVE_WORDS_SP_OFFSET             = 0;
  public static final int CALLEE_AGGREGATE_RETURN_POINTER_SP_OFFSET = REGISTER_SAVE_WORDS_SP_OFFSET + REGISTER_SAVE_WORDS;
  public static final int CALLEE_REGISTER_ARGUMENT_SAVE_AREA_SP_OFFSET = (CALLEE_AGGREGATE_RETURN_POINTER_SP_OFFSET +
                                                                          CALLEE_AGGREGATE_RETURN_POINTER_WORDS);
  public static final int MEMORY_PARAMETER_WORD_SP_OFFSET              = (CALLEE_REGISTER_ARGUMENT_SAVE_AREA_SP_OFFSET +
                                                                          CALLEE_REGISTER_ARGUMENT_SAVE_AREA_WORDS);
  public static final int VARARGS_OFFSET                               = MEMORY_PARAMETER_WORD_SP_OFFSET;

  private static final boolean DEBUG = System.getProperty("sun.jvm.hotspot.runtime.sparc.SPARCFrame.DEBUG") != null;

  public static Address unBiasSP(Address raw_sp) {
    if (raw_sp != null) {
      return raw_sp.addOffsetTo(VM.getVM().getStackBias());
    } else {
      return null;
    }
  }

  public static Address biasSP(Address real_sp) {
    if (real_sp != null) {
      if (DEBUG) {
        System.out.println("biasing realsp: " + real_sp + " biased: " + real_sp.addOffsetTo(-VM.getVM().getStackBias()) );
      }
      return real_sp.addOffsetTo(-VM.getVM().getStackBias());
    } else {
      if (DEBUG) {
        System.out.println("biasing null realsp");
      }
      return null;
    }
  }
  //
  // This is used to find the younger sp for a thread thatn has stopped but hasn't
  // conveniently told us the information where we can find the pc or the frame
  // containing the pc that corresponds to last_java_sp. This method will walk
  // the frames trying to find the frame which we contains the data we need.
  //
  public static Address findYoungerSP(Address top, Address find) {
    // top and find are unBiased sp values
    // we return an unBiased value
    Address findRaw = biasSP(find);
    if (top == null || find == null || findRaw == null) {
      throw new RuntimeException("bad values for findYoungerSP top: " + top + " find: " + find);
    }
    // It would be unusual to find more than 20 native frames before we find the java frame
    // we are looking for.
    final int maxFrames = 20;
    int count = 0;
    Address search = top;
    Address next;
    Address pc;
    if (DEBUG) {
      System.out.println("findYoungerSP top: " + top + " find: " + find + " findRaw: " + findRaw);
    }
    while ( count != maxFrames && search != null) {
      next = search.getAddressAt(SPARCRegisters.I6.spOffsetInSavedWindow());
      pc = search.getAddressAt(SPARCRegisters.I7.spOffsetInSavedWindow());
      if (DEBUG) {
        System.out.println("findYoungerSP next: " + next + " pc: " + pc);
      }
      if (next.equals(findRaw)) {
        return search;
      }
      search = unBiasSP(next);
    }
    if (DEBUG) {
      System.out.println("findYoungerSP: never found younger, top: " + top + " find: " + find);
    }
    return null;
  }

  public Address getSP()              {
    if (DEBUG) {
      System.out.println("getSP raw: " + raw_sp + " unbiased: " + unBiasSP(raw_sp));
    }
    return  unBiasSP(raw_sp);
  }

  public Address getID()              {
    return getSP();
  }

  public Address getYoungerSP()       {
    if (DEBUG) {
      System.out.println("getYoungerSP: " + raw_youngerSP + " unbiased: " + unBiasSP(raw_youngerSP));
    }
    return unBiasSP(raw_youngerSP);
  }

  /** This constructor relies on the fact that the creator of a frame
      has flushed register windows which the frame will refer to, and
      that those register windows will not be reloaded until the frame
      is done reading and writing the stack.  Moreover, if the
      "younger_pc" argument points into the register save area of the
      next younger frame (though it need not), the register window for
      that next younger frame must also stay flushed.  (The caller is
      responsible for ensuring this.) */
  public SPARCFrame(Address raw_sp, Address raw_youngerSP, boolean youngerFrameIsInterpreted) {
    super();
    if (DEBUG) {
      System.out.println("Constructing frame(1) raw_sp: " + raw_sp + " raw_youngerSP: " + raw_youngerSP);
    }
    if (Assert.ASSERTS_ENABLED) {
      Assert.that((unBiasSP(raw_sp).andWithMask(VM.getVM().getAddressSize() - 1) == null),
                   "Expected raw sp likely got real sp, value was " + raw_sp);
      if (raw_youngerSP != null) {
        Assert.that((unBiasSP(raw_youngerSP).andWithMask(VM.getVM().getAddressSize() - 1) == null),
                    "Expected raw youngerSP likely got real youngerSP, value was " + raw_youngerSP);
      }
    }
    this.raw_sp = raw_sp;
    this.raw_youngerSP = raw_youngerSP;
    if (raw_youngerSP == null) {
      // make a deficient frame which doesn't know where its PC is
      pc = null;
    } else {
      Address youngerSP = unBiasSP(raw_youngerSP);
      pc = youngerSP.getAddressAt(SPARCRegisters.I7.spOffsetInSavedWindow()).addOffsetTo(PC_RETURN_OFFSET);

      if (Assert.ASSERTS_ENABLED) {
        Assert.that(youngerSP.getAddressAt(SPARCRegisters.FP.spOffsetInSavedWindow()).
                    equals(raw_sp),
                    "youngerSP must be valid");
      }
    }

    if (youngerFrameIsInterpreted) {
      long IsavedSP = SPARCRegisters.IsavedSP.spOffsetInSavedWindow();
      // compute adjustment to this frame's SP made by its interpreted callee
      interpreterSPAdjustmentOffset = 0;
      Address savedSP = unBiasSP(getYoungerSP().getAddressAt(IsavedSP));
      if (savedSP == null) {
        if ( DEBUG) {
          System.out.println("WARNING: IsavedSP was null for frame " + this);
        }
      } else {
        interpreterSPAdjustmentOffset = savedSP.minus(getSP());
      }
    } else {
      interpreterSPAdjustmentOffset = 0;
    }
    if ( pc != null) {
      // Look for a deopt pc and if it is deopted convert to original pc
      CodeBlob cb = VM.getVM().getCodeCache().findBlob(pc);
      if (cb != null && cb.isJavaMethod()) {
        NMethod nm = (NMethod) cb;
        if (pc.equals(nm.deoptBegin())) {
          // adjust pc if frame is deoptimized.
          pc = this.getUnextendedSP().getAddressAt(nm.origPCOffset());
          deoptimized = true;
        }
      }
    }
  }

  /** Make a deficient frame which doesn't know where its PC is (note
      no youngerSP argument) */
  public SPARCFrame(Address raw_sp, Address pc) {
    super();
    if (DEBUG) {
      System.out.println("Constructing frame(2) raw_sp: " + raw_sp );
    }
    this.raw_sp = raw_sp;
    if (Assert.ASSERTS_ENABLED) {
      Assert.that((unBiasSP(raw_sp).andWithMask(VM.getVM().getAddressSize() - 1) == null),
                   "Expected raw sp likely got real sp, value was " + raw_sp);
    }
    raw_youngerSP = null;
    this.pc = pc;
    interpreterSPAdjustmentOffset = 0;
  }

  /** Only used internally */
  private SPARCFrame() {
  }

  public Object clone() {
    SPARCFrame frame = new SPARCFrame();
    frame.raw_sp = raw_sp;
    frame.pc = pc;
    frame.raw_youngerSP = raw_youngerSP;
    frame.interpreterSPAdjustmentOffset = interpreterSPAdjustmentOffset;
    frame.deoptimized = deoptimized;
    return frame;
  }

  public boolean equals(Object arg) {
    if (arg == null) {
      return false;
    }

    if (!(arg instanceof SPARCFrame)) {
      return false;
    }

    SPARCFrame other = (SPARCFrame) arg;

    return (AddressOps.equal(getSP(), other.getSP()) &&
            AddressOps.equal(getFP(), other.getFP()) &&
            AddressOps.equal(getPC(), other.getPC()));
  }

  public int hashCode() {
    if (raw_sp == null) {
      return 0;
    }

    return raw_sp.hashCode();
  }

  public String toString() {
    Address fp = getFP();
    Address sp = getSP();
    Address youngerSP = getYoungerSP();

    return "sp: " + (sp == null? "null" : sp.toString()) +
         ", younger_sp: " + (youngerSP == null? "null" : youngerSP.toString()) +
         ", fp: " + (fp == null? "null" : fp.toString()) +
         ", pc: " + (pc == null? "null" : pc.toString());
  }

  /** <P> Identifies a signal handler frame on the stack. </P>

      <P> There are a few different algorithms for doing this, and
      they vary from platform to platform. For example, based on a
      conversation with Dave Dice, Solaris/x86 will be substantially
      simpler to handle than Solaris/SPARC because the signal handler
      frame can be identified because of a program counter == -1. </P>

      <P> The dbx group provided code and advice on these topics; the
      code below evolved from theirs, but is not correct/robust.
      Without going into too many details, it seems that looking for
      the incoming argument to the sigacthandler frame (which is what
      this code identifies) is not guaranteed to be stable across
      versions of Solaris, since that function is supplied by
      libthread and is not guaranteed not to clobber I2 before it
      calls __sighndlr later. From discussions, it sounds like a
      robust algorithm which wouldn't require traversal of the
      ucontext chain (used by dbx, but which Dave Dice thinks isn't
      robust in the face of libthread -- need to follow up) would be
      to be able to properly identify the __sighndlr frame, then get
      I2 and treat that as a ucontext. To identify __sighndlr we would
      need to look up that symbol in the remote process and look for a
      program counter within a certain (small) distance. </P>

      <P> If the underlying Debugger supports CDebugger interface, we
      take the approach of __sighnldr symbol. This approach is more robust
      compared to the original hueristic approach. Of course, if there
      is no CDebugger support, we fallback to the hueristic approach. </P>

      <P> The current implementation seems to work with Solaris 2.8.
      A nice property of this system is that if we find a core file
      this algorithm doesn't work on, we can change the code and try
      again, so I'm putting this in as the current mechanism for
      finding signal handler frames on Solaris/SPARC. </P> */
  public boolean isSignalHandlerFrameDbg() {
    CDebugger cdbg = VM.getVM().getDebugger().getCDebugger();
    if (cdbg != null) {
      LoadObject dso = cdbg.loadObjectContainingPC(getPC());
      if (dso != null) {
        ClosestSymbol cs = dso.closestSymbolToPC(getPC());
        if (cs != null && cs.getName().equals("__sighndlr")) {
          return true;
        } else {
          return false;
        }
      } else {
        return false;
      }
    } else {
      if (getYoungerSP() == null) {
        //      System.err.println("  SPARCFrame.isSignalHandlerFrameDbg: youngerSP = " + getYoungerSP());
        return false;
      }
      Address i2 = getSP().getAddressAt(SPARCRegisters.I2.spOffsetInSavedWindow());
      if (i2 == null) {
        return false;
      }
      Address fp = getFP();
      // My (mistaken) understanding of the dbx group's code was that
      // the signal handler frame could be identified by testing the
      // incoming argument to see whether it was a certain distance
      // below the frame pointer; in fact, their code did substantially
      // more than this (traversal of the ucontext chain, which this
      // code can't do because the topmost ucontext is not currently
      // available via the proc_service APIs in dbx). The current code
      // appears to work, but is probably not robust.
      int MAJOR_HACK_OFFSET = 8;  // Difference between expected location of the ucontext and reality
      // System.err.println("  SPARCFrame.isSignalHandlerFrameDbg: I2 = " + i2 +
      //                          ", fp = " + fp + ", raw_youngerSP = " + getYoungerSP());
      boolean res = i2.equals(fp.addOffsetTo(VM.getVM().getAddressSize() * (REGISTER_SAVE_WORDS + MAJOR_HACK_OFFSET)));
      if (res) {
        // Qualify this with another test (FIXME: this is a gross heuristic found while testing)
        Address sigInfoAddr = getSP().getAddressAt(SPARCRegisters.I5.spOffsetInSavedWindow());
        if (sigInfoAddr == null) {
          System.err.println("Frame with fp = " + fp + " looked like a signal handler frame but wasn't");
          res = false;
        }
      }
      return res;
    }
  }

  public int getSignalNumberDbg() {
    // From looking at the stack trace in dbx, it looks like the
    // siginfo* comes into sigacthandler in I5. It would be much more
    // robust to look at the __sighndlr frame instead, but we can't
    // currently identify that frame.

    Address sigInfoAddr = getSP().getAddressAt(SPARCRegisters.I5.spOffsetInSavedWindow());
    // Read si_signo out of siginfo*
    return (int) sigInfoAddr.getCIntegerAt(0, 4, false);
  }

  public String getSignalNameDbg() {
    return POSIXSignals.getSignalName(getSignalNumberDbg());
  }

  public boolean isInterpretedFrameValid() {
    if (Assert.ASSERTS_ENABLED) {
      Assert.that(isInterpretedFrame(), "Not an interpreted frame");
    }
    // These are reasonable sanity checks
    if (getFP() == null || (getFP().andWithMask(2 * VM.getVM().getAddressSize() - 1)) != null) {
      return false;
    }
    if (getSP() == null || (getSP().andWithMask(2 * VM.getVM().getAddressSize() - 1)) != null) {
      return false;
    }
    if (getFP().addOffsetTo(INTERPRETER_FRAME_VM_LOCAL_WORDS * VM.getVM().getAddressSize()).lessThan(getSP())) {
      return false;
    }

    OopHandle methodHandle = addressOfInterpreterFrameMethod().getOopHandleAt(0);

    if (VM.getVM().getObjectHeap().isValidMethod(methodHandle) == false) {
      return false;
    }

    // These are hacks to keep us out of trouble.
    // The problem with these is that they mask other problems
    if (getFP().lessThanOrEqual(getSP())) {        // this attempts to deal with unsigned comparison above
      return false;
    }
    if (getFP().minus(getSP()) > 4096 * VM.getVM().getAddressSize()) {  // stack frames shouldn't be large.
      return false;
    }
    // FIXME: this is not atomic with respect to GC and is unsuitable
    // for use in a non-debugging, or reflective, system. Need to
    // figure out how to express this.
    Address bcx =  addressOfInterpreterFrameBCX().getAddressAt(0);

    Method method;
    try {
       method = (Method) VM.getVM().getObjectHeap().newOop(methodHandle);
    } catch (UnknownOopException ex) {
       return false;
    }
    int  bci = bcpToBci(bcx, method);
    //validate bci
    if (bci < 0) return false;

    return true;
  }

  //--------------------------------------------------------------------------------
  // Accessors:
  //

  /** Accessors */

  public long frameSize() {
    return (getSenderSP().minus(getSP()) / VM.getVM().getAddressSize());
  }

  public Address getLink() {
    return unBiasSP(getFP().getAddressAt(SPARCRegisters.FP.spOffsetInSavedWindow()));
  }

  // FIXME: not implementable yet
  //  public void setLink(Address addr) {
  //    if (Assert.ASSERTS_ENABLED) {
  //      Assert.that(getLink().equals(addr), "frame nesting is controlled by hardware");
  //    }
  //  }

  public Frame sender(RegisterMap regMap, CodeBlob cb) {
    SPARCRegisterMap map = (SPARCRegisterMap) regMap;

    if (Assert.ASSERTS_ENABLED) {
      Assert.that(map != null, "map must be set");
    }

    // Default is we don't have to follow them. The sender_for_xxx
    // will update it accordingly
    map.setIncludeArgumentOops(false);

    if (isEntryFrame()) {
      return senderForEntryFrame(map);
    }

    Address youngerSP = getSP();
    Address sp        = getSenderSP();
    boolean isInterpreted = false;

    // FIXME: this is a hack to get stackwalking to work in the face
    // of a signal like a SEGV. For debugging purposes it's important
    // that (a) we are able to traverse the stack if we take a signal
    // and (b) that we get the correct program counter in this
    // situation. If we are not using alternate signal stacks then (a)
    // seems to work all the time (on SPARC), but (b) is violated for
    // the frame just below the signal handler.

    // The mechanism for finding the ucontext is not robust. In
    // addition, we may find that we need to be able to fetch more
    // registers from the ucontext than just the program counter,
    // since the register windows on the stack are "stale". This will
    // require substantial restructuring of this frame code, so has
    // been avoided for now.

    // It is difficult to find a clean solution for mixing debugging
    // situations with VM frame traversal. One could consider
    // implementing generic frame traversal in the dbx style and only
    // using the VM's stack walking mechanism on a per-frame basis,
    // for example to traverse Java-level activations in a compiled
    // frame. However, this will probably not interact well with the
    // mechanism for finding oops on the stack.

    if (VM.getVM().isDebugging()) {
      // If we are a signal handler frame, use a trick: make the
      // youngerSP of the caller frame point to the top of the
      // ucontext's contained register set. This should allow fetching
      // of the registers for the frame just below the signal handler
      // frame in the usual fashion.
      if (isSignalHandlerFrameDbg()) {

        if (DEBUG) {
          System.out.println("SPARCFrame.sender: found signal handler frame");
        }

        // Try to give a valid SP and PC for a "deficient frame" since
        // we don't have a real register save area; making this class
        // work by reading its information from a ucontext as well as
        // a register save area is a major undertaking and has been
        // deferred for now. It is very important that the PC is
        // correct, which is why we don't just fall through to the
        // other code (which would read the PC from the stale register
        // window and thereby fail to get the actual location of the
        // fault).

        long offset = getMContextAreaOffsetInUContext();
        Address fp = sp;
        // System.out.println("  FP: " + fp);
        fp = fp.addOffsetTo(getUContextOffset() + getMContextAreaOffsetInUContext());
        // System.out.println("  start of mcontext: " + fp);
        // FIXME: put these elsewhere. These are the register numbers
        // in /usr/include/sys/regset.h. They might belong in
        // SPARCReigsters.java, but we currently don't have that list
        // of numbers in the SA code (because all of the registers are
        // listed as instances of SPARCRegister) and it appears that
        // our numbering of the registers and this one don't match up.
        int PC_OFFSET_IN_GREGSET = 1;
        int SP_OFFSET_IN_GREGSET = 17;
        raw_sp = fp.getAddressAt(VM.getVM().getAddressSize() * SP_OFFSET_IN_GREGSET);
        Address pc = fp.getAddressAt(VM.getVM().getAddressSize() * PC_OFFSET_IN_GREGSET);
        return new SPARCFrame(raw_sp, pc);
      }
    }

    if (!VM.getVM().isCore()) {
      // Note:  The version of this operation on any platform with callee-save
      //        registers must update the register map (if not null).
      //        In order to do this correctly, the various subtypes of
      //        of frame (interpreted, compiled, glue, native),
      //        must be distinguished.  There is no need on SPARC for
      //        such distinctions, because all callee-save registers are
      //        preserved for all frames via SPARC-specific mechanisms.
      //
      //        *** HOWEVER, *** if and when we make any floating-point
      //        registers callee-saved, then we will have to copy over
      //        the RegisterMap update logic from the Intel code.


      // The constructor of the sender must know whether this frame is interpreted so it can set the
      // sender's _interpreter_sp_adjustment field.
      if (VM.getVM().getInterpreter().contains(pc)) {
        isInterpreted = true;
        map.makeIntegerRegsUnsaved();
        map.shiftWindow(sp, youngerSP);
      } else {
        // Find a CodeBlob containing this frame's pc or elide the lookup and use the
        // supplied blob which is already known to be associated with this frame.
        cb = VM.getVM().getCodeCache().findBlob(pc);
        if (cb != null) {

          if (cb.callerMustGCArguments(map.getThread())) {
            map.setIncludeArgumentOops(true);
          }

          // Update the location of all implicitly saved registers
          // as the address of these registers in the register save
          // area (for %o registers we use the address of the %i
          // register in the next younger frame)
          map.shiftWindow(sp, youngerSP);
          if (map.getUpdateMap()) {
            if (cb.getOopMaps() != null) {
              OopMapSet.updateRegisterMap(this, cb, map, VM.getVM().isDebugging());
            }
          }
        }
      }
    } // #ifndef CORE

    return new SPARCFrame(biasSP(sp), biasSP(youngerSP), isInterpreted);
  }

  protected boolean hasSenderPD() {
    try {
      // FIXME: should not happen!!!
      if (getSP() == null) {
        return false;
      }
      if ( unBiasSP(getSP().getAddressAt(SPARCRegisters.FP.spOffsetInSavedWindow())) == null ) {
        return false;
      }
      return true;
    } catch (RuntimeException e) {
      if (DEBUG) {
        System.out.println("Bad frame " + this);
      }
      throw e;
    }
  }

  //--------------------------------------------------------------------------------
  // Return address:
  //

  public Address getSenderPC() {
    return addressOfI7().getAddressAt(0).addOffsetTo(PC_RETURN_OFFSET);
  }

  // FIXME: currently unimplementable
  // inline void     frame::set_sender_pc(address addr) { *I7_addr() = addr - pc_return_offset; }

  public Address getUnextendedSP() {
    return getSP().addOffsetTo(interpreterSPAdjustmentOffset);
  }

  public Address getSenderSP() {
    return getFP();
  }

  /** Given the next-younger sp for a given frame's sp, compute the
      frame. We need the next-younger sp, because its register save
      area holds the flushed copy of its I7, which is the PC of the
      frame we are interested in. */
  public SPARCFrame afterSave() {
    return new SPARCFrame(biasSP(getYoungerSP()), null);
  }

  /** Accessors for the instance variables */
  public Address getFP() {
    Address sp = getSP();
    if (sp == null) {
      System.out.println("SPARCFrame.getFP(): sp == null");
    }
    Address fpAddr = sp.addOffsetTo(SPARCRegisters.FP.spOffsetInSavedWindow());
    try {
      Address fp = unBiasSP(fpAddr.getAddressAt(0));
      if (fp == null) {
        System.out.println("SPARCFrame.getFP(): fp == null (&fp == " + fpAddr + ")");
      }
      return fp;
    } catch (RuntimeException e) {
      System.out.println("SPARCFrame.getFP(): is bad (&fp == " + fpAddr + " sp = " + sp + ")");
      return null;
    }
  }

  private Address addressOfFPSlot(int index) {
    return getFP().addOffsetTo(index * VM.getVM().getAddressSize());
  }

  // FIXME: temporarily elided
  //  // All frames
  //
  //  intptr_t*  fp_addr_at(int index) const   { return &fp()[index];    }
  //  intptr_t*  sp_addr_at(int index) const   { return &sp()[index];    }
  //  intptr_t    fp_at(     int index) const   { return *fp_addr_at(index); }
  //  intptr_t    sp_at(     int index) const   { return *sp_addr_at(index); }
  //
  // private:
  //  inline address* I7_addr() const;
  //  inline address* O7_addr() const;
  //
  //  inline address* I0_addr() const;
  //  inline address* O0_addr() const;
  //
  // public:
  //  // access to SPARC arguments and argument registers
  //
  //  intptr_t*     register_addr(Register reg) const {
  //    return sp_addr_at(reg.sp_offset_in_saved_window());
  //  }
  //  intptr_t* memory_param_addr(int param_ix, bool is_in) const {
  //    int offset = callee_register_argument_save_area_sp_offset + param_ix;
  //    if (is_in)
  //      return fp_addr_at(offset);
  //    else
  //      return sp_addr_at(offset);
  //  }
  //  intptr_t*        param_addr(int param_ix, bool is_in) const {
  //    if (param_ix >= callee_register_argument_save_area_words)
  //      return memory_param_addr(param_ix, is_in);
  //    else if (is_in)
  //      return register_addr(Argument(param_ix, true).as_register());
  //    else {
  //      // the registers are stored in the next younger frame
  //      // %%% is this really necessary?
  //      frame next_younger = after_save();
  //      return next_younger.register_addr(Argument(param_ix, true).as_register());
  //    }
  //  }

  //--------------------------------------------------------------------------------
  // Interpreter frames:
  //

  /** 2 words, also used to save float regs across  calls to C */
  public static final int INTERPRETER_FRAME_D_SCRATCH_FP_OFFSET           = -2;
  public static final int INTERPRETER_FRAME_L_SCRATCH_FP_OFFSET           = -4;
  /** For native calls only */
  public static final int INTERPRETER_FRAME_PADDING_OFFSET                = -5;
  /** For native calls only */
  public static final int INTERPRETER_FRAME_MIRROR_OFFSET                 = -6;
  /** Should be same as above, and should be zero mod 8 */
  public static final int INTERPRETER_FRAME_VM_LOCALS_FP_OFFSET           = -6;
  public static final int INTERPRETER_FRAME_VM_LOCAL_WORDS                = -INTERPRETER_FRAME_VM_LOCALS_FP_OFFSET;

  /** Interpreter frame set-up needs to save 2 extra words in outgoing
      param area for class and jnienv arguments for native stubs (see
      nativeStubGen_sparc.cpp) */
  public static final int INTERPRETER_FRAME_EXTRA_OUTGOING_ARGUMENT_WORDS = 2;

  // FIXME: elided for now
  //
  //  // the compiler frame has many of the same fields as the interpreter frame
  //  // %%%%% factor out declarations of the shared fields
  //  enum compiler_frame_fixed_locals {
  //       compiler_frame_d_scratch_fp_offset          = -2,
  //       compiler_frame_vm_locals_fp_offset          = -2, // should be same as above
  //
  //       compiler_frame_vm_local_words = -compiler_frame_vm_locals_fp_offset
  //  };
  //
  // private:
  //
  //  // where LcpoolCache is saved:
  //  constantPoolCacheOop* interpreter_frame_cpoolcache_addr() const {
  //    return (constantPoolCacheOop*)sp_addr_at( LcpoolCache.sp_offset_in_saved_window());
  //  }
  //
  //  // where Lmonitors is saved:
  //  BasicObjectLock**  interpreter_frame_monitors_addr() const {
  //    return (BasicObjectLock**) sp_addr_at( Lmonitors.sp_offset_in_saved_window());
  //  }
  //  intptr_t** interpreter_frame_esp_addr() const {
  //    return (intptr_t**)sp_addr_at( Lesp.sp_offset_in_saved_window());
  //  }
  //
  //  inline void interpreter_frame_set_tos_address(intptr_t* x);
  //
  //  // next two fns read and write Lmonitors value,
  // private:
  //  BasicObjectLock* interpreter_frame_monitors()           const  { return *interpreter_frame_monitors_addr(); }
  //  void interpreter_frame_set_monitors(BasicObjectLock* monitors) {        *interpreter_frame_monitors_addr() = monitors; }
  //
  //#ifndef CORE
  //inline oop *frame::pd_compiled_argument_to_location(VMReg::Name reg, RegisterMap reg_map, int arg_size) const {
  //  COMPILER1_ONLY(return (oop *) (arg_size - 1 - reg + sp() + memory_parameter_word_sp_offset);   )
  //  COMPILER2_ONLY(return oopmapreg_to_location(reg, &reg_map); )
  //}
  //#endif

  // FIXME: NOT FINISHED
  public Address addressOfInterpreterFrameLocals() {
    return getSP().addOffsetTo(SPARCRegisters.Llocals.spOffsetInSavedWindow());
  }

  // FIXME: this is not atomic with respect to GC and is unsuitable
  // for use in a non-debugging, or reflective, system.
  private Address addressOfInterpreterFrameBCX() {
    // %%%%% reinterpreting Lbcp as a bcx
    return getSP().addOffsetTo(SPARCRegisters.Lbcp.spOffsetInSavedWindow());
  }

  public int getInterpreterFrameBCI() {
    // FIXME: this is not atomic with respect to GC and is unsuitable
    // for use in a non-debugging, or reflective, system. Need to
    // figure out how to express this.
    Address bcp = addressOfInterpreterFrameBCX().getAddressAt(0);
    OopHandle methodHandle = addressOfInterpreterFrameMethod().getOopHandleAt(0);
    Method method = (Method) VM.getVM().getObjectHeap().newOop(methodHandle);
    return bcpToBci(bcp, method);
  }

  public Address addressOfInterpreterFrameExpressionStack() {
    return addressOfInterpreterFrameMonitors().addOffsetTo(-1 * VM.getVM().getAddressSize());
  }

  public int getInterpreterFrameExpressionStackDirection() {
    return -1;
  }

  /** Top of expression stack */
  public Address addressOfInterpreterFrameTOS() {
    return getSP().getAddressAt(SPARCRegisters.Lesp.spOffsetInSavedWindow()).addOffsetTo(VM.getVM().getAddressSize());
  }

  /** Expression stack from top down */
  public Address addressOfInterpreterFrameTOSAt(int slot) {
    return addressOfInterpreterFrameTOS().addOffsetTo(slot * VM.getVM().getAddressSize());
  }

  public Address getInterpreterFrameSenderSP() {
    if (Assert.ASSERTS_ENABLED) {
      Assert.that(isInterpretedFrame(), "interpreted frame expected");
    }
    return getFP();
  }

  // FIXME: elided for now
  //inline void frame::interpreter_frame_set_tos_address( intptr_t* x ) {
  //  *interpreter_frame_esp_addr() = x - 1;
  //}

  //--------------------------------------------------------------------------------
  // Monitors:
  //

  private Address addressOfInterpreterFrameMonitors() {
    return getSP().addOffsetTo(SPARCRegisters.Lmonitors.spOffsetInSavedWindow()).getAddressAt(0);
  }

  // Monitors
  public BasicObjectLock interpreterFrameMonitorBegin() {
    int roundedVMLocalWords = Bits.roundTo(INTERPRETER_FRAME_VM_LOCAL_WORDS, WORDS_PER_LONG);
    return new BasicObjectLock(addressOfFPSlot(-1 * roundedVMLocalWords));
  }

  public BasicObjectLock interpreterFrameMonitorEnd() {
    return new BasicObjectLock(addressOfInterpreterFrameMonitors());
  }

  public int interpreterFrameMonitorSize() {
    return Bits.roundTo(BasicObjectLock.size(), WORDS_PER_LONG * (int) VM.getVM().getAddressSize());
  }

  // FIXME: elided for now
  // // monitor elements
  //
  // // in keeping with Intel side: end is lower in memory than begin;
  // // and beginning element is oldest element
  // // Also begin is one past last monitor.
  //
  // inline BasicObjectLock* frame::interpreter_frame_monitor_begin()       const  {
  //   int rounded_vm_local_words = round_to(frame::interpreter_frame_vm_local_words, WordsPerLong);
  //   return (BasicObjectLock *)fp_addr_at(-rounded_vm_local_words);
  // }
  //
  // inline BasicObjectLock* frame::interpreter_frame_monitor_end()         const  {
  //   return interpreter_frame_monitors();
  // }
  //
  //
  // inline void frame::interpreter_frame_set_monitor_end(BasicObjectLock* value) {
  //   interpreter_frame_set_monitors(value);
  // }
  //
  //
  // inline int frame::interpreter_frame_monitor_size() {
  //   return round_to(BasicObjectLock::size(), WordsPerLong);
  // }

  public Address addressOfInterpreterFrameMethod() {
    return getSP().addOffsetTo(SPARCRegisters.Lmethod.spOffsetInSavedWindow());
  }

  public Address addressOfInterpreterFrameCPCache() {
    return getSP().addOffsetTo(SPARCRegisters.LcpoolCache.spOffsetInSavedWindow());
  }

  //--------------------------------------------------------------------------------
  // Entry frames:
  //

  public JavaCallWrapper getEntryFrameCallWrapper() {
    // Note: adjust this code if the link argument in StubGenerator::call_stub() changes!
    SPARCArgument link = new SPARCArgument(0, false);
    return (JavaCallWrapper) VMObjectFactory.newObject(JavaCallWrapper.class,
                                                       getSP().getAddressAt(link.asIn().asRegister().spOffsetInSavedWindow()));
  }

  //
  //
  // inline JavaCallWrapper* frame::entry_frame_call_wrapper() const {
  //   // note: adjust this code if the link argument in StubGenerator::call_stub() changes!
  //   const Argument link = Argument(0, false);
  //   return (JavaCallWrapper*)sp()[link.as_in().as_register().sp_offset_in_saved_window()];
  // }

  //--------------------------------------------------------------------------------
  // Safepoints:
  //

  protected Address addressOfSavedOopResult() {
    return addressOfO0();
  }

  protected Address addressOfSavedReceiver() {
    return addressOfO0();
  }


  //--------------------------------------------------------------------------------
  // Internals only below this point
  //

  private Address addressOfI7() {
    return getSP().addOffsetTo(SPARCRegisters.I7.spOffsetInSavedWindow());
  }

  private Address addressOfO7() {
    return afterSave().addressOfI7();
  }

  private Address addressOfI0() {
    return getSP().addOffsetTo(SPARCRegisters.I0.spOffsetInSavedWindow());
  }

  private Address addressOfO0() {
    return afterSave().addressOfI0();
  }

  private static boolean addressesEqual(Address a1, Address a2) {
    if ((a1 == null) && (a2 == null)) {
      return true;
    }

    if ((a1 == null) || (a2 == null)) {
      return false;
    }

    return (a1.equals(a2));
  }


  private Frame senderForEntryFrame(RegisterMap regMap) {
    SPARCRegisterMap map = (SPARCRegisterMap) regMap;

    if (Assert.ASSERTS_ENABLED) {
      Assert.that(map != null, "map must be set");
    }
    // Java frame called from C; skip all C frames and return top C
    // frame of that chunk as the sender
    JavaCallWrapper jcw = getEntryFrameCallWrapper();
    if (Assert.ASSERTS_ENABLED) {
      Assert.that(!entryFrameIsFirst(), "next Java fp must be non zero");
      Assert.that(jcw.getLastJavaSP().greaterThan(getSP()), "must be above this frame on stack");
    }
    Address lastJavaSP = jcw.getLastJavaSP();
    Address lastJavaPC = jcw.getLastJavaPC();
    map.clear();

    if (!VM.getVM().isCore()) {
      map.makeIntegerRegsUnsaved();
      map.shiftWindow(lastJavaSP, null);
    }

    if (Assert.ASSERTS_ENABLED) {
      Assert.that(map.getIncludeArgumentOops(), "should be set by clear");
    }

    if (lastJavaPC != null) {
      return new SPARCFrame(biasSP(lastJavaSP), lastJavaPC);
    } else {
      Address youngerSP  = getNextYoungerSP(lastJavaSP, getSP());
      return new SPARCFrame(biasSP(lastJavaSP), biasSP(youngerSP), false);
    }
  }

  private static Address getNextYoungerSP(Address oldSP, Address youngSP) {
    Address sp = getNextYoungerSPOrNull(oldSP, youngSP, null);
    if (Assert.ASSERTS_ENABLED) {
      Assert.that(sp != null, "missed the SP");
    }
    return sp;
  }

  private static Address getNextYoungerSPOrNull(Address oldSP, Address youngSP, Address sp) {
    if (youngSP == null) {
      // FIXME
      throw new RuntimeException("can not handle null youngSP in debugging system (seems to require register window flush)");
    }

    if (sp == null) {
      sp = youngSP;
    }

    Address previousSP = null;

    /** Minimum frame size is 16 */
    int maxFrames = (int) (oldSP.minus(sp) / (16 * VM.getVM().getAddressSize()));

    while(!sp.equals(oldSP) && spIsValid(oldSP, youngSP, sp)) {
      if (maxFrames-- <= 0) {
        // too many frames have gone by; invalid parameters given to this function
        break;
      }
      previousSP = sp;
      sp = unBiasSP(sp.getAddressAt(SPARCRegisters.FP.spOffsetInSavedWindow()));
    }

    return (sp.equals(oldSP) ? previousSP : null);
  }

  private static boolean spIsValid(Address oldSP, Address youngSP, Address sp) {
    long mask = VM.getVM().getAddressSize();
    mask = 2 * mask - 1;
    return ((sp.andWithMask(mask) == null) &&
            (sp.lessThanOrEqual(oldSP)) &&
            (sp.greaterThanOrEqual(youngSP)));
  }

  // FIXME: this is a hopefully temporary hack (not sure what is going on)
  public long getUContextOffset() {
    // FIXME: there is something I clearly don't understand about the
    // way the signal handler frame is laid out, because I shouldn't need this extra offset
    int MAJOR_HACK_OFFSET = 8;
    //    System.out.println("  SPARCFrame.isSignalHandlerFrameDbg: I2 = " + i2 + ", fp = " + fp + ", youngerSP = " + youngerSP);
    return VM.getVM().getAddressSize() * (REGISTER_SAVE_WORDS + MAJOR_HACK_OFFSET);
  }

  public long getMContextAreaOffsetInUContext() {
    // From dbx-related sources:
    // /*
    //  * struct sigframe is declaredf in the kernel sources in
    //  * .../uts/sun4c/os/machdep.c/sendsig()
    //  * unfortunately we only get a pointer to the 'uc' passed
    //  * to the sighandler so we need to do this stuff to get
    //  * to 'rwin'.
    //  * Have to do it like this to take account of alignment.
    //  */
    // static struct sigframe {
    //     struct rwindow rwin;
    //     ucontext_t uc;
    // } sf_help;

    // From /usr/include/sys/ucontext.h:
    // #if !defined(_XPG4_2) || defined(__EXTENSIONS__)
    // struct   ucontext {
    // #else
    // struct   __ucontext {
    // #endif
    //  uint_t          uc_flags;
    //  ucontext_t      *uc_link;
    //  sigset_t        uc_sigmask;
    //  stack_t         uc_stack;
    //  mcontext_t      uc_mcontext;
    // #ifdef   __sparcv9
    //  long            uc_filler[4];
    // #else    /* __sparcv9 */
    //  long            uc_filler[23];
    // #endif   /* __sparcv9 */
    // };

    // This walks to the start of the gregs in the mcontext_t
    // (first entry in that data structure). Really should read
    // this from header file.

    // Also not sure exactly how alignment works...maybe should read these offsets from the target VM
    // (When you have a hammer, everything looks like a nail)
    long offset = VM.getVM().alignUp(4, VM.getVM().getAddressSize());   // uc_flags
    offset      = VM.getVM().alignUp(offset + VM.getVM().getAddressSize(), 8); // uc_link plus
                                                                        // doubleword alignment for structs?
    offset     += 16 +                                                  // uc_sigmask
                   2 * VM.getVM().getAddressSize() + 4;                 // uc_stack
    offset      = VM.getVM().alignUp(offset + VM.getVM().getAddressSize(), 8); // doubleword alignment for structs?

    //    System.out.println("SPARCFrame.getMContextAreaOffsetInUContext: offset = " + offset);

    return offset;
  }
}
