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 */
013package org.jikesrvm.jni.ia32;
014
015import static org.jikesrvm.ia32.RegisterConstants.EBP;
016import static org.jikesrvm.ia32.RegisterConstants.EBX;
017import static org.jikesrvm.ia32.RegisterConstants.EDI;
018import static org.jikesrvm.runtime.UnboxedSizeConstants.BYTES_IN_ADDRESS;
019import static org.jikesrvm.runtime.UnboxedSizeConstants.LOG_BYTES_IN_ADDRESS;
020
021import org.jikesrvm.compilers.common.CompiledMethod;
022import org.jikesrvm.jni.JNIEnvironment;
023import org.jikesrvm.mm.mminterface.GCMapIterator;
024import org.jikesrvm.runtime.Magic;
025import org.jikesrvm.scheduler.RVMThread;
026import org.vmmagic.pragma.Uninterruptible;
027import org.vmmagic.unboxed.Address;
028import org.vmmagic.unboxed.AddressArray;
029import org.vmmagic.unboxed.Offset;
030
031/**
032 * Iterator for stack frames inserted at the transition from Java to
033 * JNI Native C.  It will report JREFs associated with the executing
034 * C frames which are in the "JREFs stack" attached to the executing
035 * Threads JNIEnvironment.  It will update register location addresses
036 * for the non-volatile registers to point to the registers saved
037 * in the transition frame.
038 *
039 * @see JNICompiler
040 */
041@Uninterruptible
042public final class JNIGCMapIterator extends GCMapIterator {
043
044  // Java to Native C transition frame...(see JNICompiler)
045  //
046  //  0         + saved FP   + <---- FP for Java to Native C glue frame
047  // -4         | methodID   |
048  // -8         | saved EDI  |  non-volatile GPR
049  // -C         | saved EBX  |  non-volatile GPR
050  // -10        | saved EBP  |  non-volatile GPR
051  // -14        | returnAddr |  (for return from OutOfLineMachineCode)
052  // -18        | saved PR   |
053  // -1C        | arg n-1    |  reordered arguments to native method
054  // -20        |  ...       |  ...
055  // -24        | arg 1      |  ...
056  // -28        | arg 0      |  ...
057  // -2C        | class/obj  |  required 2nd argument to all native methods
058  // -30        | jniEnv     |  required 1st argument to all native methods
059  // -34        | returnAddr |  return address pushed by call to native method
060  //            + saved FP   +  <---- FP for called native method
061
062  // additional instance fields added by this subclass of GCMapIterator
063  AddressArray jniRefs;
064  int jniNextRef;
065  int jniFramePtr;
066
067  public JNIGCMapIterator(AddressArray registerLocations) {
068    super(registerLocations);
069  }
070
071  // Override newStackWalk() in parent class GCMapIterator to
072  // initialize iterator for scan of JNI JREFs stack of refs
073  // Taken:    thread
074  // Returned: nothing
075  //
076  @Override
077  public void newStackWalk(RVMThread thread) {
078    super.newStackWalk(thread);   // sets this.thread, inits registerLocations[]
079    JNIEnvironment env = this.thread.getJNIEnv();
080    // the "primordial" thread, created by JDK in the bootimage, does not have
081    // a JniEnv object, all threads created by the VM will.
082    if (env != null) {
083      this.jniRefs = env.refsArray();
084      this.jniNextRef = env.refsTop();
085      this.jniFramePtr = env.savedRefsFP();
086    }
087  }
088
089  @Override
090  public void setupIterator(CompiledMethod compiledMethod, Offset instructionOffset, Address framePtr) {
091    this.framePtr = framePtr;
092  }
093
094  // return (address of) next ref in the current "frame" on the
095  // threads JNIEnvironment stack of refs
096  // When at the end of the current frame, update register locations to point
097  // to the non-volatile registers saved in the JNI transition frame.
098  //
099  @Override
100  public Address getNextReferenceAddress() {
101    // first report jni refs in the current frame in the jniRef side stack
102    // until all in the frame are reported
103    //
104    if (jniNextRef > jniFramePtr) {
105      Address ref_address = Magic.objectAsAddress(jniRefs).plus(jniNextRef);
106      jniNextRef -= BYTES_IN_ADDRESS;
107      return ref_address;
108    }
109
110    // no more refs to report, before returning 0, setup for processing
111    // the next jni frame, if any
112
113    // jniNextRef -> savedFramePtr for another "frame" of refs for another
114    // sequence of Native C frames lower in the stack, or to 0 if this is the
115    // last jni frame in the JNIRefs stack.  If more frames, initialize for a
116    // later scan of those refs.
117    //
118    if (jniFramePtr > 0) {
119      jniFramePtr = jniRefs.get(jniFramePtr >> LOG_BYTES_IN_ADDRESS).toInt();
120      jniNextRef = jniNextRef - BYTES_IN_ADDRESS;
121    }
122
123    // set register locations for non-volatiles to point to registers saved in
124    // the JNI transition frame at a fixed negative offset from the callers FP.
125    // the save non-volatiles are EBX EBP and EDI.
126    //
127    registerLocations.set(EDI.value(), framePtr.plus(JNICompiler.EDI_SAVE_OFFSET));
128    registerLocations.set(EBX.value(), framePtr.plus(JNICompiler.EBX_SAVE_OFFSET));
129    registerLocations.set(EBP.value(), framePtr.plus(JNICompiler.EBP_SAVE_OFFSET));
130
131    return Address.zero();  // no more refs to report
132  }
133
134  @Override
135  public Address getNextReturnAddressAddress() {
136    return Address.zero();
137  }
138
139  @Override
140  public void reset() { }
141
142  @Override
143  public void cleanupPointers() { }
144
145  @Override
146  public int getType() {
147    return CompiledMethod.JNI;
148  }
149}