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.compilers.baseline.ia32;
014
015import static org.jikesrvm.ia32.BaselineConstants.BRIDGE_FRAME_EXTRA_SIZE;
016import static org.jikesrvm.ia32.BaselineConstants.EBP_SAVE_OFFSET;
017import static org.jikesrvm.ia32.BaselineConstants.EBX_SAVE_OFFSET;
018import static org.jikesrvm.ia32.BaselineConstants.EDI_SAVE_OFFSET;
019import static org.jikesrvm.ia32.BaselineConstants.LG_WORDSIZE;
020import static org.jikesrvm.ia32.BaselineConstants.STACKFRAME_FIRST_PARAMETER_OFFSET;
021import static org.jikesrvm.ia32.BaselineConstants.T0;
022import static org.jikesrvm.ia32.BaselineConstants.T0_SAVE_OFFSET;
023import static org.jikesrvm.ia32.BaselineConstants.T1;
024import static org.jikesrvm.ia32.BaselineConstants.T1_SAVE_OFFSET;
025import static org.jikesrvm.ia32.BaselineConstants.WORDSIZE;
026import static org.jikesrvm.ia32.RegisterConstants.EBP;
027import static org.jikesrvm.ia32.RegisterConstants.EBX;
028import static org.jikesrvm.ia32.RegisterConstants.EDI;
029import static org.jikesrvm.ia32.RegisterConstants.NUM_PARAMETER_GPRS;
030import static org.jikesrvm.runtime.UnboxedSizeConstants.BYTES_IN_ADDRESS;
031
032import org.jikesrvm.VM;
033import org.jikesrvm.classloader.MethodReference;
034import org.jikesrvm.classloader.NormalMethod;
035import org.jikesrvm.classloader.TypeReference;
036import org.jikesrvm.compilers.baseline.BaselineCompiledMethod;
037import org.jikesrvm.compilers.baseline.ReferenceMaps;
038import org.jikesrvm.compilers.common.CompiledMethod;
039import org.jikesrvm.compilers.common.CompiledMethods;
040import org.jikesrvm.mm.mminterface.GCMapIterator;
041import org.jikesrvm.runtime.DynamicLink;
042import org.jikesrvm.runtime.Magic;
043import org.vmmagic.pragma.Uninterruptible;
044import org.vmmagic.unboxed.Address;
045import org.vmmagic.unboxed.AddressArray;
046import org.vmmagic.unboxed.Offset;
047
048/**
049 * Iterator for stack frame  built by the Baseline compiler.
050 * <p>
051 * An Instance of this class will iterate through a particular
052 * reference map of a method returning the offsets of any references
053 * that are part of the input parameters, local variables, and
054 * java stack for the stack frame.
055 */
056@Uninterruptible
057public final class BaselineGCMapIterator extends GCMapIterator {
058  private static final boolean TRACE_ALL = false;
059  private static final boolean TRACE_DL = false; // dynamic link frames
060
061  /*
062   * Iterator state for mapping any stackframe.
063   */
064  /** Compiled method for the frame */
065  private NormalMethod currentMethod;
066  /** Compiled method for the frame */
067  private BaselineCompiledMethod currentCompiledMethod;
068  private int currentNumLocals;
069  /** Current index in current map */
070  private int mapIndex;
071  /** id of current map out of all maps */
072  private int mapId;
073  /** set of maps for this method */
074  private ReferenceMaps maps;
075  /** have we reported the base ptr of the edge counter array? */
076  private boolean counterArrayBase;
077
078  /*
079   *  Additional iterator state for mapping dynamic bridge stackframes.
080   */
081  /** place to keep info returned by CompiledMethod.getDynamicLink */
082  private final DynamicLink dynamicLink;
083  /** method to be invoked via dynamic bridge (null: current frame is not a dynamic bridge) */
084  private MethodReference bridgeTarget;
085  /** parameter types passed by that method */
086  private TypeReference[] bridgeParameterTypes;
087  /** have all bridge parameters been mapped yet? */
088  private boolean bridgeParameterMappingRequired;
089  /** do we need to map spilled params (baseline compiler = no, opt = yes) */
090  private boolean bridgeSpilledParameterMappingRequired;
091  /** have the register location been updated */
092  private boolean bridgeRegistersLocationUpdated;
093  /** have we processed all the values in the regular map yet? */
094  private boolean finishedWithRegularMap;
095  /** first parameter to be mapped (-1 == "this") */
096  private int bridgeParameterInitialIndex;
097  /** current parameter being mapped (-1 == "this") */
098  private int bridgeParameterIndex;
099  /** gpr register it lives in */
100  private int bridgeRegisterIndex;
101  /** memory address at which that register was saved */
102  private Address bridgeRegisterLocation;
103  /** current spilled param location */
104  private Address bridgeSpilledParamLocation;
105  /** starting offset to stack location for param0 */
106  private int bridgeSpilledParamInitialOffset;
107
108  /**
109   * Constructs a BaselineGCMapIterator for IA32.
110   * <p>
111   * Note: the location array for registers needs to be remembered. It also needs to
112   * be updated with the location of any saved registers. The locations are kept
113   * as addresses within the stack. This information is not used by this iterator
114   * but must be updated for the other types of iterators (e.g. iterators for
115   * the opt compiler built frames).
116   *
117   * @param registerLocations locations of saved registers
118   */
119  public BaselineGCMapIterator(AddressArray registerLocations) {
120    super(registerLocations);
121    dynamicLink = new DynamicLink();
122  }
123
124  /*
125   * Interface
126   */
127
128  /**
129   * Set the iterator to scan the map at the machine instruction offset
130   * provided. The iterator is positioned to the beginning of the map. NOTE: An
131   * iterator may be reused to scan a different method and map.
132   *
133   * @param compiledMethod
134   *          identifies the method and class
135   * @param instructionOffset
136   *          identifies the map to be scanned.
137   * @param fp
138   *          identifies a specific occurrence of this method and allows for
139   *          processing instance specific information i.e JSR return address
140   *          values
141   */
142  @Override
143  public void setupIterator(CompiledMethod compiledMethod, Offset instructionOffset, Address fp) {
144    currentCompiledMethod = (BaselineCompiledMethod) compiledMethod;
145    currentMethod = (NormalMethod) currentCompiledMethod.getMethod();
146    currentNumLocals = currentMethod.getLocalWords();
147
148    // setup superclass
149    //
150    framePtr = fp;
151
152    // setup stackframe mapping
153    //
154    maps = ((BaselineCompiledMethod) compiledMethod).referenceMaps;
155    mapId = maps.locateGCPoint(instructionOffset, currentMethod);
156    mapIndex = 0;
157    if (mapId < 0) {
158      // lock the jsr lock to serialize jsr processing
159      ReferenceMaps.jsrLock.lock();
160      int JSRindex = maps.setupJSRSubroutineMap(mapId);
161      while (JSRindex != 0) {
162        Address nextCallerAddress = framePtr.plus(convertIndexToOffset(JSRindex)).loadAddress();
163        Offset nextMachineCodeOffset = compiledMethod.getInstructionOffset(nextCallerAddress);
164        if (VM.TraceStkMaps) {
165          VM.sysWriteln("     setupJSRsubroutineMap- nested jsrs end of loop- = ");
166          VM.sysWriteln("      next jsraddress offset = ", JSRindex);
167          VM.sysWriteln("      next callers address = ", nextCallerAddress);
168          VM.sysWriteln("      next machinecodeoffset = ", nextMachineCodeOffset);
169          if (nextMachineCodeOffset.sLT(Offset.zero())) {
170            VM.sysWriteln("BAD MACHINE CODE OFFSET");
171          }
172        }
173        JSRindex = maps.getNextJSRAddressIndex(nextMachineCodeOffset, currentMethod);
174      }
175    }
176    if (VM.TraceStkMaps || TRACE_ALL) {
177      VM.sysWrite("BaselineGCMapIterator setupIterator mapId = ");
178      VM.sysWrite(mapId);
179      VM.sysWrite(" for ");
180      VM.sysWrite(compiledMethod.getMethod());
181      VM.sysWrite(".\n");
182    }
183
184    // setup dynamic bridge mapping
185    //
186    bridgeTarget = null;
187    bridgeParameterTypes = null;
188    bridgeParameterMappingRequired = false;
189    bridgeRegistersLocationUpdated = false;
190    bridgeParameterIndex = 0;
191    bridgeRegisterIndex = 0;
192    bridgeRegisterLocation = Address.zero();
193    bridgeSpilledParamLocation = Address.zero();
194
195    if (currentMethod.getDeclaringClass().hasDynamicBridgeAnnotation()) {
196      Address ip = Magic.getReturnAddressUnchecked(fp);
197      fp = Magic.getCallerFramePointer(fp);
198      int callingCompiledMethodId = Magic.getCompiledMethodID(fp);
199      CompiledMethod callingCompiledMethod = CompiledMethods.getCompiledMethod(callingCompiledMethodId);
200      Offset callingInstructionOffset = callingCompiledMethod.getInstructionOffset(ip);
201
202      callingCompiledMethod.getDynamicLink(dynamicLink, callingInstructionOffset);
203      bridgeTarget = dynamicLink.methodRef();
204      bridgeParameterTypes = bridgeTarget.getParameterTypes();
205      if (dynamicLink.isInvokedWithImplicitThisParameter()) {
206        bridgeParameterInitialIndex = -1;
207        bridgeSpilledParamInitialOffset = 2 * WORDSIZE; // this + return addr
208      } else {
209        bridgeParameterInitialIndex = 0;
210        bridgeSpilledParamInitialOffset = WORDSIZE; // return addr
211      }
212      bridgeSpilledParamInitialOffset += (bridgeTarget.getParameterWords() << LG_WORDSIZE);
213      bridgeSpilledParameterMappingRequired = callingCompiledMethod.getCompilerType() != CompiledMethod.BASELINE;
214    }
215
216    reset();
217  }
218
219  /**
220   * Reset iteration to initial state. This allows a map to be scanned multiple
221   * times.
222   */
223  @Override
224  public void reset() {
225    mapIndex = 0;
226    finishedWithRegularMap = false;
227
228    // setup map to report EBX if this method is holding the base of counter array in it.
229    counterArrayBase = currentCompiledMethod.hasCounterArray();
230
231    if (bridgeTarget != null) {
232      bridgeParameterMappingRequired = true;
233      bridgeParameterIndex = bridgeParameterInitialIndex;
234      bridgeRegisterIndex = 0;
235      bridgeRegisterLocation = framePtr.plus(STACKFRAME_FIRST_PARAMETER_OFFSET); // top of frame
236      bridgeSpilledParamLocation = framePtr.plus(bridgeSpilledParamInitialOffset);
237    }
238  }
239
240  /**
241   * Converts a biased index from a local area into an offset in the stack.
242   *
243   * @param index index in the local area (biased : local0 has index 1)
244   * @return corresponding offset in the stack
245   */
246  public short convertIndexToLocation(int index) {
247    if (index == 0) return 0;
248    if (index <= currentNumLocals) { //index is biased by 1;
249      return currentCompiledMethod.getGeneralLocalLocation(index - 1);
250    } else {
251      return currentCompiledMethod.getGeneralStackLocation(index - 1 - currentNumLocals);
252    }
253  }
254
255  private int convertIndexToOffset(int index) {
256    //for ia32: always offset, never registers
257    if (index == 0) return 0; //invalid
258
259    // index is biased by 1, index 1 means local 0, this is at offset -BYTES_IN_ADDRESS from startLocalOffset
260    int offset = BaselineCompilerImpl.locationToOffset(convertIndexToLocation(index)) - BYTES_IN_ADDRESS; // no jsrbit here
261    if (VM.TraceStkMaps) {
262      VM.sysWriteln("convertIndexToOffset- input index = ", index, "  offset = ", offset);
263    }
264    return offset;
265  }
266
267  @Override
268  public Address getNextReferenceAddress() {
269    if (!finishedWithRegularMap) {
270      if (counterArrayBase) {
271        counterArrayBase = false;
272        return registerLocations.get(EBX.value());
273      }
274      if (mapId < 0) {
275        mapIndex = maps.getNextJSRRefIndex(mapIndex);
276      } else {
277        mapIndex = maps.getNextRefIndex(mapIndex, mapId);
278      }
279
280      if (mapIndex != 0) {
281        int mapOffset = convertIndexToOffset(mapIndex);
282        if (VM.TraceStkMaps || TRACE_ALL) {
283          VM.sysWrite("BaselineGCMapIterator getNextReferenceOffset = ");
284          VM.sysWriteHex(mapOffset);
285          VM.sysWrite(".\n");
286          VM.sysWrite("Reference is ");
287        }
288        if (bridgeParameterMappingRequired) {
289          if (VM.TraceStkMaps || TRACE_ALL) {
290            VM.sysWriteHex(framePtr.plus(mapOffset - BRIDGE_FRAME_EXTRA_SIZE).loadAddress());
291            VM.sysWrite(".\n");
292            if (mapId < 0) {
293              VM.sysWrite("Offset is a JSR return address ie internal pointer.\n");
294            }
295          }
296
297          // TODO  clean this
298          return (framePtr.plus(mapOffset - BRIDGE_FRAME_EXTRA_SIZE));
299        } else {
300          if (VM.TraceStkMaps || TRACE_ALL) {
301            VM.sysWriteHex(framePtr.plus(mapOffset).loadAddress());
302            VM.sysWrite(".\n");
303            if (mapId < 0) {
304              VM.sysWrite("Offset is a JSR return address ie internal pointer.\n");
305            }
306          }
307          return (framePtr.plus(mapOffset));
308        }
309      } else {
310        // remember that we are done with the map for future calls, and then
311        //   drop down to the code below
312        finishedWithRegularMap = true;
313      }
314    }
315
316    if (bridgeParameterMappingRequired) {
317      if (VM.TraceStkMaps || TRACE_ALL || TRACE_DL) {
318        VM.sysWrite("getNextReferenceAddress: bridgeTarget=");
319        VM.sysWrite(bridgeTarget);
320        VM.sysWrite("\n");
321      }
322
323      if (!bridgeRegistersLocationUpdated) {
324        // point registerLocations[] to our callers stackframe
325        //
326        registerLocations.set(EDI.value(), framePtr.plus(EDI_SAVE_OFFSET));
327        registerLocations.set(T0.value(), framePtr.plus(T0_SAVE_OFFSET));
328        registerLocations.set(T1.value(), framePtr.plus(T1_SAVE_OFFSET));
329        registerLocations.set(EBX.value(), framePtr.plus(EBX_SAVE_OFFSET));
330
331        bridgeRegistersLocationUpdated = true;
332      }
333
334      // handle implicit "this" parameter, if any
335      //
336      if (bridgeParameterIndex == -1) {
337        bridgeParameterIndex += 1;
338        bridgeRegisterIndex += 1;
339        bridgeRegisterLocation = bridgeRegisterLocation.minus(WORDSIZE);
340        bridgeSpilledParamLocation = bridgeSpilledParamLocation.minus(WORDSIZE);
341
342        if (VM.TraceStkMaps || TRACE_ALL || TRACE_DL) {
343          VM.sysWrite("BaselineGCMapIterator getNextReferenceOffset = dynamic link GPR this ");
344          VM.sysWrite(bridgeRegisterLocation.plus(WORDSIZE));
345          VM.sysWrite(".\n");
346        }
347        return bridgeRegisterLocation.plus(WORDSIZE);
348      }
349
350      // now the remaining parameters
351      //
352      while (bridgeParameterIndex < bridgeParameterTypes.length) {
353        TypeReference bridgeParameterType = bridgeParameterTypes[bridgeParameterIndex++];
354
355        if (bridgeParameterType.isReferenceType()) {
356          bridgeRegisterIndex += 1;
357          bridgeRegisterLocation = bridgeRegisterLocation.minus(WORDSIZE);
358          bridgeSpilledParamLocation = bridgeSpilledParamLocation.minus(WORDSIZE);
359
360          if (bridgeRegisterIndex <= NUM_PARAMETER_GPRS) {
361            if (VM.TraceStkMaps || TRACE_ALL || TRACE_DL) {
362              VM.sysWrite("BaselineGCMapIterator getNextReferenceOffset = dynamic link GPR parameter ");
363              VM.sysWrite(bridgeRegisterLocation.plus(WORDSIZE));
364              VM.sysWrite(".\n");
365            }
366            return bridgeRegisterLocation.plus(WORDSIZE);
367          } else {
368            if (bridgeSpilledParameterMappingRequired) {
369              if (VM.TraceStkMaps || TRACE_ALL || TRACE_DL) {
370                VM.sysWrite("BaselineGCMapIterator getNextReferenceOffset = dynamic link spilled parameter ");
371                VM.sysWrite(bridgeSpilledParamLocation.plus(WORDSIZE));
372                VM.sysWrite(".\n");
373              }
374              return bridgeSpilledParamLocation.plus(WORDSIZE);
375            } else {
376              break;
377            }
378          }
379        } else if (bridgeParameterType.isLongType()) {
380          bridgeRegisterIndex += VM.BuildFor32Addr ? 2 : 1;
381          bridgeRegisterLocation = bridgeRegisterLocation.minus(2 * WORDSIZE);
382          bridgeSpilledParamLocation = bridgeSpilledParamLocation.minus(2 * WORDSIZE);
383        } else if (bridgeParameterType.isDoubleType()) {
384          bridgeSpilledParamLocation = bridgeSpilledParamLocation.minus(2 * WORDSIZE);
385        } else if (bridgeParameterType.isFloatType()) {
386          bridgeSpilledParamLocation = bridgeSpilledParamLocation.minus(WORDSIZE);
387        } else {
388          // boolean, byte, char, short, int
389          bridgeRegisterIndex += 1;
390          bridgeRegisterLocation = bridgeRegisterLocation.minus(WORDSIZE);
391          bridgeSpilledParamLocation = bridgeSpilledParamLocation.minus(WORDSIZE);
392        }
393      }
394    } else {
395      // point registerLocations[] to our callers stackframe
396      //
397      registerLocations.set(EDI.value(), framePtr.plus(EDI_SAVE_OFFSET));
398      registerLocations.set(EBX.value(), framePtr.plus(EBX_SAVE_OFFSET));
399      if (currentMethod.hasBaselineSaveLSRegistersAnnotation()) {
400        registerLocations.set(EBP.value(), framePtr.plus(EBP_SAVE_OFFSET));
401      }
402    }
403
404    return Address.zero();
405  }
406
407  @Override
408  public Address getNextReturnAddressAddress() {
409    if (mapId >= 0) {
410      if (VM.TraceStkMaps || TRACE_ALL) {
411        VM.sysWrite("BaselineGCMapIterator getNextReturnAddressOffset mapId = ");
412        VM.sysWrite(mapId);
413        VM.sysWrite(".\n");
414      }
415      return Address.zero();
416    }
417    mapIndex = maps.getNextJSRReturnAddrIndex(mapIndex);
418    if (VM.TraceStkMaps || TRACE_ALL) {
419      VM.sysWrite("BaselineGCMapIterator getNextReturnAddressOffset = ");
420      VM.sysWrite(convertIndexToOffset(mapIndex));
421      VM.sysWrite(".\n");
422    }
423    return (mapIndex == 0) ? Address.zero() : framePtr.plus(convertIndexToOffset(mapIndex));
424  }
425
426  /**
427   * Cleanup pointers - used with method maps to release data structures early
428   * ... they may be in temporary storage i.e. storage only used during garbage
429   * collection
430   */
431  @Override
432  public void cleanupPointers() {
433    maps.cleanupPointers();
434    maps = null;
435    if (mapId < 0) {
436      ReferenceMaps.jsrLock.unlock();
437    }
438    bridgeTarget = null;
439    bridgeParameterTypes = null;
440  }
441
442  @Override
443  public int getType() {
444    return CompiledMethod.BASELINE;
445  }
446
447  public int getStackDepth() {
448    return maps.getStackDepth(mapId);
449  }
450}
451