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.mm.mmtk;
014
015import static org.jikesrvm.mm.mminterface.MemoryManagerConstants.MOVES_CODE;
016import static org.jikesrvm.runtime.UnboxedSizeConstants.LOG_BYTES_IN_ADDRESS;
017
018import org.jikesrvm.VM;
019import org.jikesrvm.compilers.common.CompiledMethods;
020import org.jikesrvm.jni.JNIEnvironment;
021import org.jikesrvm.jni.JNIGenericHelpers;
022import org.jikesrvm.jni.JNIGlobalRefTable;
023import org.jikesrvm.mm.mminterface.AlignmentEncoding;
024import org.jikesrvm.mm.mminterface.HandInlinedScanning;
025import org.jikesrvm.mm.mminterface.Selected;
026import org.jikesrvm.mm.mminterface.SpecializedScanMethod;
027import org.jikesrvm.runtime.Magic;
028import org.jikesrvm.scheduler.RVMThread;
029import org.mmtk.plan.CollectorContext;
030import org.mmtk.plan.TraceLocal;
031import org.mmtk.plan.TransitiveClosure;
032import org.vmmagic.pragma.Inline;
033import org.vmmagic.pragma.Uninterruptible;
034import org.vmmagic.unboxed.Address;
035import org.vmmagic.unboxed.ObjectReference;
036
037@Uninterruptible
038public final class Scanning extends org.mmtk.vm.Scanning {
039  /****************************************************************************
040   *
041   * Class variables
042   */
043
044  /** Counter to track index into thread table for root tracing.  */
045  private static final SynchronizedCounter threadCounter = new SynchronizedCounter();
046
047  /**
048   * Scanning of a object, processing each pointer field encountered.
049   *
050   * @param trace The closure being used.
051   * @param object The object to be scanned.
052   */
053  @Override
054  @Inline
055  public void scanObject(TransitiveClosure trace, ObjectReference object) {
056    if (HandInlinedScanning.ENABLED) {
057      int tibCode = AlignmentEncoding.getTibCode(object);
058      HandInlinedScanning.scanObject(tibCode, object.toObject(), trace);
059    } else {
060      SpecializedScanMethod.fallback(object.toObject(), trace);
061    }
062  }
063
064  @Override
065  @Inline
066  public void specializedScanObject(int id, TransitiveClosure trace, ObjectReference object) {
067    if (HandInlinedScanning.ENABLED) {
068      int tibCode = AlignmentEncoding.getTibCode(object);
069      HandInlinedScanning.scanObject(tibCode, id, object.toObject(), trace);
070    } else {
071      if (SpecializedScanMethod.ENABLED) {
072        SpecializedScanMethod.invoke(id, object.toObject(), trace);
073      } else {
074        SpecializedScanMethod.fallback(object.toObject(), trace);
075      }
076    }
077  }
078
079  @Override
080  public void resetThreadCounter() {
081    threadCounter.reset();
082  }
083
084  @Override
085  public void notifyInitialThreadScanComplete(boolean partialScan) {
086    if (!partialScan)
087      CompiledMethods.snipObsoleteCompiledMethods();
088    /* flush out any remset entries generated during the above activities */
089    Selected.Mutator.get().flushRememberedSets();
090  }
091
092  /**
093   * Computes static roots.  This method establishes all such roots for
094   * collection and places them in the root locations queue.  This method
095   * should not have side effects (such as copying or forwarding of
096   * objects).  There are a number of important preconditions:
097   *
098   * <ul>
099   * <li> The <code>threadCounter</code> must be reset so that load
100   * balancing parallel GC can share the work of scanning threads.
101   * </ul>
102   *
103   * @param trace The trace to use for computing roots.
104   */
105  @Override
106  public void computeStaticRoots(TraceLocal trace) {
107    /* scan statics */
108    ScanStatics.scanStatics(trace);
109  }
110
111  /**
112   * Computes global roots.  This method establishes all such roots for
113   * collection and places them in the root locations queue.  This method
114   * should not have side effects (such as copying or forwarding of
115   * objects).  There are a number of important preconditions:
116   *
117   * <ul>
118   * <li> The <code>threadCounter</code> must be reset so that load
119   * balancing parallel GC can share the work of scanning threads.
120   * </ul>
121   *
122   * @param trace The trace to use for computing roots.
123   */
124  @Override
125  public void computeGlobalRoots(TraceLocal trace) {
126    /* scan JNI functions */
127    CollectorContext cc = RVMThread.getCurrentThread().getCollectorContext();
128    Address jniFunctions = Magic.objectAsAddress(JNIEnvironment.JNIFunctions);
129    int threads = cc.parallelWorkerCount();
130    int size = JNIEnvironment.JNIFunctions.length();
131    int chunkSize = size / threads;
132    int start = cc.parallelWorkerOrdinal() * chunkSize;
133    int end = (cc.parallelWorkerOrdinal() + 1 == threads) ? size : threads * chunkSize;
134
135    for (int i = start; i < end; i++) {
136      Address functionAddressSlot = jniFunctions.plus(i << LOG_BYTES_IN_ADDRESS);
137      if (JNIGenericHelpers.implementedInJava(i)) {
138        trace.processRootEdge(functionAddressSlot, true);
139      } else {
140        // Function implemented as a C function, must not be
141        // scanned.
142      }
143    }
144
145    Address linkageTriplets = Magic.objectAsAddress(JNIEnvironment.linkageTriplets);
146    if (!linkageTriplets.isZero()) {
147      for (int i = start; i < end; i++) {
148        trace.processRootEdge(linkageTriplets.plus(i << LOG_BYTES_IN_ADDRESS), true);
149      }
150    }
151
152    /* scan jni global refs */
153    Address jniGlobalRefs = Magic.objectAsAddress(JNIGlobalRefTable.JNIGlobalRefs);
154    size = JNIGlobalRefTable.JNIGlobalRefs.length();
155    chunkSize = size / threads;
156    start = cc.parallelWorkerOrdinal() * chunkSize;
157    end = (cc.parallelWorkerOrdinal() + 1 == threads) ? size : threads * chunkSize;
158
159    for (int i = start; i < end; i++) {
160      trace.processRootEdge(jniGlobalRefs.plus(i << LOG_BYTES_IN_ADDRESS), true);
161    }
162  }
163
164  /**
165   * {@inheritDoc}
166   */
167  @Override
168  public void computeThreadRoots(TraceLocal trace) {
169    computeThreadRoots(trace, false);
170  }
171
172  /**
173   * {@inheritDoc}
174   */
175  @Override
176  public void computeNewThreadRoots(TraceLocal trace) {
177    computeThreadRoots(trace, true);
178  }
179
180  /**
181   * Compute roots pointed to by threads.
182   *
183   * @param trace The trace to use for computing roots.
184   * @param newRootsSufficient  True if it sufficient for this method to only
185   * compute those roots that are new since the previous stack scan.   If false
186   * then all roots must be computed (both new and preexisting).
187   */
188  private void computeThreadRoots(TraceLocal trace, boolean newRootsSufficient) {
189    boolean processCodeLocations = MOVES_CODE;
190
191    /* scan all threads */
192    while (true) {
193      int threadIndex = threadCounter.increment();
194      if (threadIndex > RVMThread.numThreads) break;
195
196      RVMThread thread = RVMThread.threads[threadIndex];
197      if (thread == null || thread.isCollectorThread()) continue;
198
199      /* scan the thread (stack etc.) */
200      ScanThread.scanThread(thread, trace, processCodeLocations, newRootsSufficient);
201    }
202
203    /* flush out any remset entries generated during the above activities */
204    Selected.Mutator.get().flushRememberedSets();
205  }
206
207  @Override
208  public void computeBootImageRoots(TraceLocal trace) {
209    ScanBootImage.scanBootImage(trace);
210  }
211
212  @Override
213  public boolean supportsReturnBarrier() {
214    return VM.BuildForIA32 && VM.BuildFor32Addr;
215  }
216}