1 /*
   2  * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * Copyright (c) 2015, 2019, Red Hat Inc.
   4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5  *
   6  * This code is free software; you can redistribute it and/or modify it
   7  * under the terms of the GNU General Public License version 2 only, as
   8  * published by the Free Software Foundation.
   9  *
  10  * This code is distributed in the hope that it will be useful, but WITHOUT
  11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13  * version 2 for more details (a copy is included in the LICENSE file that
  14  * accompanied this code).
  15  *
  16  * You should have received a copy of the GNU General Public License version
  17  * 2 along with this work; if not, write to the Free Software Foundation,
  18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  19  *
  20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  21  * or visit www.oracle.com if you need additional information or have any
  22  * questions.
  23  *
  24  */
  25 
  26 package sun.jvm.hotspot.runtime.aarch64;
  27 
  28 import java.util.*;
  29 import sun.jvm.hotspot.code.*;
  30 import sun.jvm.hotspot.compiler.*;
  31 import sun.jvm.hotspot.debugger.*;
  32 import sun.jvm.hotspot.oops.*;
  33 import sun.jvm.hotspot.runtime.*;
  34 import sun.jvm.hotspot.types.*;
  35 import sun.jvm.hotspot.utilities.*;
  36 
  37 /** Specialization of and implementation of abstract methods of the
  38     Frame class for the aarch64 family of CPUs. */
  39 
  40 public class AARCH64Frame extends Frame {
  41   private static final boolean DEBUG;
  42   static {
  43     DEBUG = System.getProperty("sun.jvm.hotspot.runtime.aarch64.AARCH64Frame.DEBUG") != null;
  44   }
  45 
  46   // All frames
  47   private static final int LINK_OFFSET                =  0;
  48   private static final int RETURN_ADDR_OFFSET         =  1;
  49   private static final int SENDER_SP_OFFSET           =  2;
  50 
  51   // Interpreter frames
  52   private static final int INTERPRETER_FRAME_MIRROR_OFFSET    =  2; // for native calls only
  53   private static final int INTERPRETER_FRAME_SENDER_SP_OFFSET = -1;
  54   private static final int INTERPRETER_FRAME_LAST_SP_OFFSET   = INTERPRETER_FRAME_SENDER_SP_OFFSET - 1;
  55   private static final int INTERPRETER_FRAME_METHOD_OFFSET    = INTERPRETER_FRAME_LAST_SP_OFFSET - 1;
  56   private static       int INTERPRETER_FRAME_MDX_OFFSET;         // Non-core builds only
  57   private static       int INTERPRETER_FRAME_CACHE_OFFSET;
  58   private static       int INTERPRETER_FRAME_LOCALS_OFFSET;
  59   private static       int INTERPRETER_FRAME_BCX_OFFSET;
  60   private static       int INTERPRETER_FRAME_INITIAL_SP_OFFSET;
  61   private static       int INTERPRETER_FRAME_MONITOR_BLOCK_TOP_OFFSET;
  62   private static       int INTERPRETER_FRAME_MONITOR_BLOCK_BOTTOM_OFFSET;
  63 
  64   // Entry frames
  65   private static       int ENTRY_FRAME_CALL_WRAPPER_OFFSET = -8;
  66 
  67   // Native frames
  68   private static final int NATIVE_FRAME_INITIAL_PARAM_OFFSET =  2;
  69 
  70   private static VMReg fp = new VMReg(29);
  71 
  72   static {
  73     VM.registerVMInitializedObserver(new Observer() {
  74         public void update(Observable o, Object data) {
  75           initialize(VM.getVM().getTypeDataBase());
  76         }
  77       });
  78   }
  79 
  80   private static synchronized void initialize(TypeDataBase db) {
  81     INTERPRETER_FRAME_MDX_OFFSET                  = INTERPRETER_FRAME_METHOD_OFFSET - 1;
  82     INTERPRETER_FRAME_CACHE_OFFSET                = INTERPRETER_FRAME_MDX_OFFSET - 1;
  83     INTERPRETER_FRAME_LOCALS_OFFSET               = INTERPRETER_FRAME_CACHE_OFFSET - 1;
  84     INTERPRETER_FRAME_BCX_OFFSET                  = INTERPRETER_FRAME_LOCALS_OFFSET - 1;
  85     INTERPRETER_FRAME_INITIAL_SP_OFFSET           = INTERPRETER_FRAME_BCX_OFFSET - 1;
  86     INTERPRETER_FRAME_MONITOR_BLOCK_TOP_OFFSET    = INTERPRETER_FRAME_INITIAL_SP_OFFSET;
  87     INTERPRETER_FRAME_MONITOR_BLOCK_BOTTOM_OFFSET = INTERPRETER_FRAME_INITIAL_SP_OFFSET;
  88   }
  89 
  90 
  91   // an additional field beyond sp and pc:
  92   Address raw_fp; // frame pointer
  93   private Address raw_unextendedSP;
  94 
  95   private AARCH64Frame() {
  96   }
  97 
  98   private void adjustForDeopt() {
  99     if ( pc != null) {
 100       // Look for a deopt pc and if it is deopted convert to original pc
 101       CodeBlob cb = VM.getVM().getCodeCache().findBlob(pc);
 102       if (cb != null && cb.isJavaMethod()) {
 103         NMethod nm = (NMethod) cb;
 104         if (pc.equals(nm.deoptHandlerBegin())) {
 105           if (Assert.ASSERTS_ENABLED) {
 106             Assert.that(this.getUnextendedSP() != null, "null SP in Java frame");
 107           }
 108           // adjust pc if frame is deoptimized.
 109           pc = this.getUnextendedSP().getAddressAt(nm.origPCOffset());
 110           deoptimized = true;
 111         }
 112       }
 113     }
 114   }
 115 
 116   public AARCH64Frame(Address raw_sp, Address raw_fp, Address pc) {
 117     this.raw_sp = raw_sp;
 118     this.raw_unextendedSP = raw_sp;
 119     this.raw_fp = raw_fp;
 120     this.pc = pc;
 121     adjustUnextendedSP();
 122 
 123     // Frame must be fully constructed before this call
 124     adjustForDeopt();
 125 
 126     if (DEBUG) {
 127       System.out.println("AARCH64Frame(sp, fp, pc): " + this);
 128       dumpStack();
 129     }
 130   }
 131 
 132   public AARCH64Frame(Address raw_sp, Address raw_fp) {
 133     this.raw_sp = raw_sp;
 134     this.raw_unextendedSP = raw_sp;
 135     this.raw_fp = raw_fp;
 136 
 137     // We cannot assume SP[-1] always contains a valid return PC (e.g. if
 138     // the callee is a C/C++ compiled frame). If the PC is not known to
 139     // Java then this.pc is null.
 140     Address savedPC = raw_sp.getAddressAt(-1 * VM.getVM().getAddressSize());
 141     if (VM.getVM().isJavaPCDbg(savedPC)) {
 142       this.pc = savedPC;
 143     }
 144 
 145     adjustUnextendedSP();
 146 
 147     // Frame must be fully constructed before this call
 148     adjustForDeopt();
 149 
 150     if (DEBUG) {
 151       System.out.println("AARCH64Frame(sp, fp): " + this);
 152       dumpStack();
 153     }
 154   }
 155 
 156   public AARCH64Frame(Address raw_sp, Address raw_unextendedSp, Address raw_fp, Address pc) {
 157     this.raw_sp = raw_sp;
 158     this.raw_unextendedSP = raw_unextendedSp;
 159     this.raw_fp = raw_fp;
 160     this.pc = pc;
 161     adjustUnextendedSP();
 162 
 163     // Frame must be fully constructed before this call
 164     adjustForDeopt();
 165 
 166     if (DEBUG) {
 167       System.out.println("AARCH64Frame(sp, unextendedSP, fp, pc): " + this);
 168       dumpStack();
 169     }
 170 
 171   }
 172 
 173   public Object clone() {
 174     AARCH64Frame frame = new AARCH64Frame();
 175     frame.raw_sp = raw_sp;
 176     frame.raw_unextendedSP = raw_unextendedSP;
 177     frame.raw_fp = raw_fp;
 178     frame.pc = pc;
 179     frame.deoptimized = deoptimized;
 180     return frame;
 181   }
 182 
 183   public boolean equals(Object arg) {
 184     if (arg == null) {
 185       return false;
 186     }
 187 
 188     if (!(arg instanceof AARCH64Frame)) {
 189       return false;
 190     }
 191 
 192     AARCH64Frame other = (AARCH64Frame) arg;
 193 
 194     return (AddressOps.equal(getSP(), other.getSP()) &&
 195             AddressOps.equal(getUnextendedSP(), other.getUnextendedSP()) &&
 196             AddressOps.equal(getFP(), other.getFP()) &&
 197             AddressOps.equal(getPC(), other.getPC()));
 198   }
 199 
 200   public int hashCode() {
 201     if (raw_sp == null) {
 202       return 0;
 203     }
 204 
 205     return raw_sp.hashCode();
 206   }
 207 
 208   public String toString() {
 209     return "sp: " + (getSP() == null? "null" : getSP().toString()) +
 210          ", unextendedSP: " + (getUnextendedSP() == null? "null" : getUnextendedSP().toString()) +
 211          ", fp: " + (getFP() == null? "null" : getFP().toString()) +
 212          ", pc: " + (pc == null? "null" : pc.toString());
 213   }
 214 
 215   // accessors for the instance variables
 216   public Address getFP() { return raw_fp; }
 217   public Address getSP() { return raw_sp; }
 218   public Address getID() { return raw_sp; }
 219 
 220   // FIXME: not implemented yet
 221   public boolean isSignalHandlerFrameDbg() { return false; }
 222   public int     getSignalNumberDbg()      { return 0;     }
 223   public String  getSignalNameDbg()        { return null;  }
 224 
 225   public boolean isInterpretedFrameValid() {
 226     if (Assert.ASSERTS_ENABLED) {
 227       Assert.that(isInterpretedFrame(), "Not an interpreted frame");
 228     }
 229 
 230     // These are reasonable sanity checks
 231     if (getFP() == null || getFP().andWithMask(0x3) != null) {
 232       return false;
 233     }
 234 
 235     if (getSP() == null || getSP().andWithMask(0x3) != null) {
 236       return false;
 237     }
 238 
 239     if (getFP().addOffsetTo(INTERPRETER_FRAME_INITIAL_SP_OFFSET * VM.getVM().getAddressSize()).lessThan(getSP())) {
 240       return false;
 241     }
 242 
 243     // These are hacks to keep us out of trouble.
 244     // The problem with these is that they mask other problems
 245     if (getFP().lessThanOrEqual(getSP())) {
 246       // this attempts to deal with unsigned comparison above
 247       return false;
 248     }
 249 
 250     if (getFP().minus(getSP()) > 4096 * VM.getVM().getAddressSize()) {
 251       // stack frames shouldn't be large.
 252       return false;
 253     }
 254 
 255     return true;
 256   }
 257 
 258   // FIXME: not applicable in current system
 259   //  void    patch_pc(Thread* thread, address pc);
 260 
 261   public Frame sender(RegisterMap regMap, CodeBlob cb) {
 262     AARCH64RegisterMap map = (AARCH64RegisterMap) regMap;
 263 
 264     if (Assert.ASSERTS_ENABLED) {
 265       Assert.that(map != null, "map must be set");
 266     }
 267 
 268     // Default is we done have to follow them. The sender_for_xxx will
 269     // update it accordingly
 270     map.setIncludeArgumentOops(false);
 271 
 272     if (isEntryFrame())       return senderForEntryFrame(map);
 273     if (isInterpretedFrame()) return senderForInterpreterFrame(map);
 274 
 275     if(cb == null) {
 276       cb = VM.getVM().getCodeCache().findBlob(getPC());
 277     } else {
 278       if (Assert.ASSERTS_ENABLED) {
 279         Assert.that(cb.equals(VM.getVM().getCodeCache().findBlob(getPC())), "Must be the same");
 280       }
 281     }
 282 
 283     if (cb != null) {
 284       return senderForCompiledFrame(map, cb);
 285     }
 286 
 287     // Must be native-compiled frame, i.e. the marshaling code for native
 288     // methods that exists in the core system.
 289     return new AARCH64Frame(getSenderSP(), getLink(), getSenderPC());
 290   }
 291 
 292   private Frame senderForEntryFrame(AARCH64RegisterMap map) {
 293     if (DEBUG) {
 294       System.out.println("senderForEntryFrame");
 295     }
 296     if (Assert.ASSERTS_ENABLED) {
 297       Assert.that(map != null, "map must be set");
 298     }
 299     // Java frame called from C; skip all C frames and return top C
 300     // frame of that chunk as the sender
 301     AARCH64JavaCallWrapper jcw = (AARCH64JavaCallWrapper) getEntryFrameCallWrapper();
 302     if (Assert.ASSERTS_ENABLED) {
 303       Assert.that(!entryFrameIsFirst(), "next Java fp must be non zero");
 304       Assert.that(jcw.getLastJavaSP().greaterThan(getSP()), "must be above this frame on stack");
 305     }
 306     AARCH64Frame fr;
 307     if (jcw.getLastJavaPC() != null) {
 308       fr = new AARCH64Frame(jcw.getLastJavaSP(), jcw.getLastJavaFP(), jcw.getLastJavaPC());
 309     } else {
 310       fr = new AARCH64Frame(jcw.getLastJavaSP(), jcw.getLastJavaFP());
 311     }
 312     map.clear();
 313     if (Assert.ASSERTS_ENABLED) {
 314       Assert.that(map.getIncludeArgumentOops(), "should be set by clear");
 315     }
 316     return fr;
 317   }
 318 
 319   //------------------------------------------------------------------------------
 320   // frame::adjust_unextended_sp
 321   private void adjustUnextendedSP() {
 322     // If we are returning to a compiled MethodHandle call site, the
 323     // saved_fp will in fact be a saved value of the unextended SP.  The
 324     // simplest way to tell whether we are returning to such a call site
 325     // is as follows:
 326 
 327     CodeBlob cb = cb();
 328     NMethod senderNm = (cb == null) ? null : cb.asNMethodOrNull();
 329     if (senderNm != null) {
 330       // If the sender PC is a deoptimization point, get the original
 331       // PC.  For MethodHandle call site the unextended_sp is stored in
 332       // saved_fp.
 333       if (senderNm.isDeoptMhEntry(getPC())) {
 334         // DEBUG_ONLY(verifyDeoptMhOriginalPc(senderNm, getFP()));
 335         raw_unextendedSP = getFP();
 336       }
 337       else if (senderNm.isDeoptEntry(getPC())) {
 338         // DEBUG_ONLY(verifyDeoptOriginalPc(senderNm, raw_unextendedSp));
 339       }
 340       else if (senderNm.isMethodHandleReturn(getPC())) {
 341         raw_unextendedSP = getFP();
 342       }
 343     }
 344   }
 345 
 346   private Frame senderForInterpreterFrame(AARCH64RegisterMap map) {
 347     if (DEBUG) {
 348       System.out.println("senderForInterpreterFrame");
 349     }
 350     Address unextendedSP = addressOfStackSlot(INTERPRETER_FRAME_SENDER_SP_OFFSET).getAddressAt(0);
 351     Address sp = addressOfStackSlot(SENDER_SP_OFFSET);
 352     // We do not need to update the callee-save register mapping because above
 353     // us is either another interpreter frame or a converter-frame, but never
 354     // directly a compiled frame.
 355     // 11/24/04 SFG. With the removal of adapter frames this is no longer true.
 356     // However c2 no longer uses callee save register for java calls so there
 357     // are no callee register to find.
 358 
 359     if (map.getUpdateMap())
 360       updateMapWithSavedLink(map, addressOfStackSlot(LINK_OFFSET));
 361 
 362     return new AARCH64Frame(sp, unextendedSP, getLink(), getSenderPC());
 363   }
 364 
 365   private void updateMapWithSavedLink(RegisterMap map, Address savedFPAddr) {
 366     map.setLocation(fp, savedFPAddr);
 367   }
 368 
 369   private Frame senderForCompiledFrame(AARCH64RegisterMap map, CodeBlob cb) {
 370     if (DEBUG) {
 371       System.out.println("senderForCompiledFrame");
 372     }
 373 
 374     //
 375     // NOTE: some of this code is (unfortunately) duplicated  AARCH64CurrentFrameGuess
 376     //
 377 
 378     if (Assert.ASSERTS_ENABLED) {
 379       Assert.that(map != null, "map must be set");
 380     }
 381 
 382     // frame owned by optimizing compiler
 383     if (Assert.ASSERTS_ENABLED) {
 384         Assert.that(cb.getFrameSize() >= 0, "must have non-zero frame size");
 385     }
 386     Address senderSP = getUnextendedSP().addOffsetTo(cb.getFrameSize());
 387 
 388     // The return_address is always the word on the stack
 389     Address senderPC = senderSP.getAddressAt(-1 * VM.getVM().getAddressSize());
 390 
 391     // This is the saved value of FP which may or may not really be an FP.
 392     // It is only an FP if the sender is an interpreter frame.
 393     Address savedFPAddr = senderSP.addOffsetTo(- SENDER_SP_OFFSET * VM.getVM().getAddressSize());
 394 
 395     if (map.getUpdateMap()) {
 396       // Tell GC to use argument oopmaps for some runtime stubs that need it.
 397       // For C1, the runtime stub might not have oop maps, so set this flag
 398       // outside of update_register_map.
 399       map.setIncludeArgumentOops(cb.callerMustGCArguments());
 400 
 401       if (cb.getOopMaps() != null) {
 402         OopMapSet.updateRegisterMap(this, cb, map, true);
 403       }
 404 
 405       // Since the prolog does the save and restore of FP there is no oopmap
 406       // for it so we must fill in its location as if there was an oopmap entry
 407       // since if our caller was compiled code there could be live jvm state in it.
 408       updateMapWithSavedLink(map, savedFPAddr);
 409     }
 410 
 411     return new AARCH64Frame(senderSP, savedFPAddr.getAddressAt(0), senderPC);
 412   }
 413 
 414   protected boolean hasSenderPD() {
 415     return true;
 416   }
 417 
 418   public long frameSize() {
 419     return (getSenderSP().minus(getSP()) / VM.getVM().getAddressSize());
 420   }
 421 
 422     public Address getLink() {
 423         try {
 424             if (DEBUG) {
 425                 System.out.println("Reading link at " + addressOfStackSlot(LINK_OFFSET)
 426                         + " = " + addressOfStackSlot(LINK_OFFSET).getAddressAt(0));
 427             }
 428             return addressOfStackSlot(LINK_OFFSET).getAddressAt(0);
 429         } catch (Exception e) {
 430             if (DEBUG)
 431                 System.out.println("Returning null");
 432             return null;
 433         }
 434     }
 435 
 436   // FIXME: not implementable yet
 437   //inline void      frame::set_link(intptr_t* addr)  { *(intptr_t **)addr_at(link_offset) = addr; }
 438 
 439   public Address getUnextendedSP() { return raw_unextendedSP; }
 440 
 441   // Return address:
 442   public Address getSenderPCAddr() { return addressOfStackSlot(RETURN_ADDR_OFFSET); }
 443   public Address getSenderPC()     { return getSenderPCAddr().getAddressAt(0);      }
 444 
 445   // return address of param, zero origin index.
 446   public Address getNativeParamAddr(int idx) {
 447     return addressOfStackSlot(NATIVE_FRAME_INITIAL_PARAM_OFFSET + idx);
 448   }
 449 
 450   public Address getSenderSP()     { return addressOfStackSlot(SENDER_SP_OFFSET); }
 451 
 452   public Address addressOfInterpreterFrameLocals() {
 453     return addressOfStackSlot(INTERPRETER_FRAME_LOCALS_OFFSET);
 454   }
 455 
 456   private Address addressOfInterpreterFrameBCX() {
 457     return addressOfStackSlot(INTERPRETER_FRAME_BCX_OFFSET);
 458   }
 459 
 460   public int getInterpreterFrameBCI() {
 461     // FIXME: this is not atomic with respect to GC and is unsuitable
 462     // for use in a non-debugging, or reflective, system. Need to
 463     // figure out how to express this.
 464     Address bcp = addressOfInterpreterFrameBCX().getAddressAt(0);
 465     Address methodHandle = addressOfInterpreterFrameMethod().getAddressAt(0);
 466     Method method = (Method)Metadata.instantiateWrapperFor(methodHandle);
 467     return bcpToBci(bcp, method);
 468   }
 469 
 470   public Address addressOfInterpreterFrameMDX() {
 471     return addressOfStackSlot(INTERPRETER_FRAME_MDX_OFFSET);
 472   }
 473 
 474   // FIXME
 475   //inline int frame::interpreter_frame_monitor_size() {
 476   //  return BasicObjectLock::size();
 477   //}
 478 
 479   // expression stack
 480   // (the max_stack arguments are used by the GC; see class FrameClosure)
 481 
 482   public Address addressOfInterpreterFrameExpressionStack() {
 483     Address monitorEnd = interpreterFrameMonitorEnd().address();
 484     return monitorEnd.addOffsetTo(-1 * VM.getVM().getAddressSize());
 485   }
 486 
 487   public int getInterpreterFrameExpressionStackDirection() { return -1; }
 488 
 489   // top of expression stack
 490   public Address addressOfInterpreterFrameTOS() {
 491     return getSP();
 492   }
 493 
 494   /** Expression stack from top down */
 495   public Address addressOfInterpreterFrameTOSAt(int slot) {
 496     return addressOfInterpreterFrameTOS().addOffsetTo(slot * VM.getVM().getAddressSize());
 497   }
 498 
 499   public Address getInterpreterFrameSenderSP() {
 500     if (Assert.ASSERTS_ENABLED) {
 501       Assert.that(isInterpretedFrame(), "interpreted frame expected");
 502     }
 503     return addressOfStackSlot(INTERPRETER_FRAME_SENDER_SP_OFFSET).getAddressAt(0);
 504   }
 505 
 506   // Monitors
 507   public BasicObjectLock interpreterFrameMonitorBegin() {
 508     return new BasicObjectLock(addressOfStackSlot(INTERPRETER_FRAME_MONITOR_BLOCK_BOTTOM_OFFSET));
 509   }
 510 
 511   public BasicObjectLock interpreterFrameMonitorEnd() {
 512     Address result = addressOfStackSlot(INTERPRETER_FRAME_MONITOR_BLOCK_TOP_OFFSET).getAddressAt(0);
 513     if (Assert.ASSERTS_ENABLED) {
 514       // make sure the pointer points inside the frame
 515       Assert.that(AddressOps.gt(getFP(), result), "result must <  than frame pointer");
 516       Assert.that(AddressOps.lte(getSP(), result), "result must >= than stack pointer");
 517     }
 518     return new BasicObjectLock(result);
 519   }
 520 
 521   public int interpreterFrameMonitorSize() {
 522     return BasicObjectLock.size();
 523   }
 524 
 525   // Method
 526   public Address addressOfInterpreterFrameMethod() {
 527     return addressOfStackSlot(INTERPRETER_FRAME_METHOD_OFFSET);
 528   }
 529 
 530   // Constant pool cache
 531   public Address addressOfInterpreterFrameCPCache() {
 532     return addressOfStackSlot(INTERPRETER_FRAME_CACHE_OFFSET);
 533   }
 534 
 535   // Entry frames
 536   public JavaCallWrapper getEntryFrameCallWrapper() {
 537     return new AARCH64JavaCallWrapper(addressOfStackSlot(ENTRY_FRAME_CALL_WRAPPER_OFFSET).getAddressAt(0));
 538   }
 539 
 540   protected Address addressOfSavedOopResult() {
 541     // offset is 2 for compiler2 and 3 for compiler1
 542     return getSP().addOffsetTo((VM.getVM().isClientCompiler() ? 2 : 3) *
 543                                VM.getVM().getAddressSize());
 544   }
 545 
 546   protected Address addressOfSavedReceiver() {
 547     return getSP().addOffsetTo(-4 * VM.getVM().getAddressSize());
 548   }
 549 
 550   private void dumpStack() {
 551     for (Address addr = getSP().addOffsetTo(-4 * VM.getVM().getAddressSize());
 552          AddressOps.lt(addr, getSP());
 553          addr = addr.addOffsetTo(VM.getVM().getAddressSize())) {
 554       System.out.println(addr + ": " + addr.getAddressAt(0));
 555     }
 556     System.out.println("-----------------------");
 557     for (Address addr = getSP();
 558          AddressOps.lte(addr, getSP().addOffsetTo(20 * VM.getVM().getAddressSize()));
 559          addr = addr.addOffsetTo(VM.getVM().getAddressSize())) {
 560       System.out.println(addr + ": " + addr.getAddressAt(0));
 561     }
 562   }
 563 }