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     */
013    package org.jikesrvm.compilers.opt.runtimesupport;
014    
015    import java.util.ArrayList;
016    import org.jikesrvm.ArchitectureSpecific;
017    import org.jikesrvm.VM;
018    import org.jikesrvm.Constants;
019    import org.jikesrvm.adaptive.database.callgraph.CallSite;
020    import org.jikesrvm.classloader.RVMArray;
021    import org.jikesrvm.classloader.MemberReference;
022    import org.jikesrvm.classloader.RVMMethod;
023    import org.jikesrvm.classloader.NormalMethod;
024    import org.jikesrvm.classloader.TypeReference;
025    import org.jikesrvm.compilers.opt.OptimizingCompilerException;
026    import org.jikesrvm.compilers.opt.driver.OptConstants;
027    import org.jikesrvm.compilers.opt.inlining.CallSiteTree;
028    import org.jikesrvm.compilers.opt.ir.MIR_Call;
029    import org.jikesrvm.compilers.opt.ir.GCIRMap;
030    import org.jikesrvm.compilers.opt.ir.GCIRMapElement;
031    import org.jikesrvm.compilers.opt.ir.IR;
032    import org.jikesrvm.compilers.opt.ir.Instruction;
033    import org.jikesrvm.compilers.opt.ir.operand.MethodOperand;
034    import org.vmmagic.pragma.Inline;
035    import org.vmmagic.pragma.Uninterruptible;
036    import org.vmmagic.unboxed.Offset;
037    
038    /**
039     * A class that encapsulates mapping information about generated machine code.
040     * Since there will be an instance of this class with every OptCompiledMethod,
041     * we attempt to pack the data into a reasonably small number of bits.
042     *
043     * <p> The supported functions are:
044     * <ul>
045     *  <li> (1) Map from a machine code offset to a GC map (register & stack map).
046     *  <li> (2) Map from machinecode offset to <method, bcIndex> pair.
047     *        Used for:
048     *                  <ul>
049     *                  <li> dynamic linking
050     *                  <li> lazy compilation
051     *                  <li> adaptive system profiling
052     *                  </ul>
053     *  <li> (3) Map from a machine code offset to a tree of <method, bcIndex> pairs
054     *      that encodes the inlining sequence.
055     *        Used for:
056     *                  <ul>
057     *                  <li> IPA
058     *                  <li> stack inspection (print stack trace,
059     *                                         security manager, etc).
060     *                  <li> general debugging support.
061     *                  <li> adaptive system profiling
062     *                  </ul>
063     *</ul>
064     *<p>
065     *  Note: This file contains two types of methods
066     *  <ul>
067     *       <li>1) methods called during compilation to create the maps
068     *       <li>2) methods called at GC time (no allocation allowed!)
069     *  </ul>
070     */
071    public final class OptMachineCodeMap implements Constants, OptConstants {
072    
073      /**
074       * Private constructor, object should be created via create
075       */
076      private OptMachineCodeMap(int[] _MCInformation, int[] _gcMaps, int[] _inlineEncoding) {
077        MCInformation = _MCInformation;
078        gcMaps = _gcMaps;
079        inlineEncoding = _inlineEncoding;
080      }
081    
082      /**
083       * Private constructor for no information.
084       */
085      private OptMachineCodeMap() {
086        MCInformation = null;
087        gcMaps = null;
088        inlineEncoding = null;
089      }
090    
091      /**
092       * Create the map, called during compilation
093       * @param ir   the ir object for this method
094       * @param machineCodeSize the number of machine code instructions generated.
095       */
096      static OptMachineCodeMap create(IR ir, int machineCodeSize) {
097        /** Dump maps as methods are compiled */
098        final boolean DUMP_MAPS =
099          ir.options.PRINT_GC_MAPS &&
100             (!ir.options.hasMETHOD_TO_PRINT() ||
101              (ir.options.hasMETHOD_TO_PRINT() && ir.options.fuzzyMatchMETHOD_TO_PRINT(ir.method.toString()))
102              );
103        /** Dump stats on map size as maps are compiled */
104        final boolean DUMP_MAP_SIZES = false;
105        if (DUMP_MAPS) {
106          VM.sysWrite("Creating final machine code map for " + ir.method + "\n");
107        }
108    
109        // create all machine code maps
110        final OptMachineCodeMap map = generateMCInformation(ir.MIRInfo.gcIRMap, DUMP_MAPS);
111    
112        if (DUMP_MAP_SIZES) {
113          map.recordStats(ir.method,
114                          map.size(),
115                          machineCodeSize << ArchitectureSpecific.RegisterConstants.LG_INSTRUCTION_WIDTH, DUMP_MAP_SIZES);
116        }
117    
118        if (DUMP_MAPS) {
119          VM.sysWrite("Final Machine code information:\n");
120          map.dumpMCInformation(DUMP_MAPS);
121          for (Instruction i = ir.firstInstructionInCodeOrder(); i != null; i = i.nextInstructionInCodeOrder()) {
122            VM.sysWriteln(i.getmcOffset() + "\t" + i);
123          }
124        }
125        return map;
126      }
127    
128      /**
129       * Get the bytecode index for a machine instruction offset.
130       *
131       * @param MCOffset the machine code offset of interest
132       * @return -1 if unknown.
133       */
134      @Uninterruptible
135      public int getBytecodeIndexForMCOffset(Offset MCOffset) {
136        int entry = findMCEntry(MCOffset);
137        if (entry == -1) {
138          return -1;
139        }
140        return getBytecodeIndex(entry);
141      }
142    
143      /**
144       * Get the RVMMethod for a machine instruction offset.
145       * This method is the source method that the instruction came from.
146       *
147       * @param MCOffset the machine code offset of interest
148       * @return {@code null} if unknown
149       */
150      @Uninterruptible
151      public NormalMethod getMethodForMCOffset(Offset MCOffset) {
152        int entry = findMCEntry(MCOffset);
153        if (entry == -1) {
154          return null;
155        }
156        int iei = getInlineEncodingIndex(entry);
157        if (iei == -1) {
158          return null;
159        }
160        int mid = OptEncodedCallSiteTree.getMethodID(iei, inlineEncoding);
161        return (NormalMethod) MemberReference.getMemberRef(mid).asMethodReference().getResolvedMember();
162      }
163    
164      /**
165       * Return the inlining encoding index for the machine instruction offset.
166       *
167       * @param MCOffset the machine code offset of interest
168       * @return -1 if unknown.
169       */
170      @Uninterruptible
171      public int getInlineEncodingForMCOffset(Offset MCOffset) {
172        int entry = findMCEntry(MCOffset);
173        if (entry == -1) {
174          return -1;
175        }
176        return getInlineEncodingIndex(entry);
177      }
178    
179      /**
180       *  This method searches for the GC map corresponding to the
181       *  passed machine code offset.
182       *  If no map is present, an error has occurred and OptGCMap.ERROR
183       *  is returned.
184       *
185       *  @param MCOffset the machine code offset to look for
186       *  @return the GC map index or OptGCMap.ERROR
187       */
188      @Uninterruptible
189      public int findGCMapIndex(Offset MCOffset) {
190        int entry = findMCEntry(MCOffset);
191        if (entry == -1) return OptGCMap.ERROR;
192        return getGCMapIndex(entry);
193      }
194    
195      /**
196       * @return an arraylist of CallSite objects representing all non-inlined
197       *         callsites in the method. Returns null if there are no such callsites.
198       */
199      public ArrayList<CallSite> getNonInlinedCallSites() {
200        ArrayList<CallSite> ans = null;
201        if (MCInformation == null) return ans;
202        for (int entry = 0; entry < MCInformation.length;) {
203          int callInfo = getCallInfo(entry);
204          if (callInfo == IS_UNGUARDED_CALL) {
205            int bcIndex = getBytecodeIndex(entry);
206            if (bcIndex != -1) {
207              int iei = getInlineEncodingIndex(entry);
208              if (iei != -1) {
209                int mid = OptEncodedCallSiteTree.getMethodID(iei, inlineEncoding);
210                RVMMethod caller = MemberReference.getMemberRef(mid).asMethodReference().peekResolvedMethod();
211                if (caller != null) {
212                  if (ans == null) ans = new ArrayList<CallSite>();
213                  ans.add(new CallSite(caller, bcIndex));
214                }
215              }
216            }
217          }
218          entry = nextEntry(entry);
219        }
220        return ans;
221      }
222    
223      /**
224       * This method searches the machine code maps and determines if
225       * the given call edge is definitely inlined into the method.
226       * NOTE: This current implementation may return false even if the
227       * edge actually was inlined.  This happens when no GC point occurs within
228       * the inlined body.  This is less than ideal; we need to fix this at some point.
229       * @param caller caller RVMMethod
230       * @param bcIndex bytecode index of the caller method
231       * @param callee callee RVMMethod
232       * @return {@code true} if the call edge is <em>definitely</em> inlined in this compiled method.
233       */
234      public boolean hasInlinedEdge(RVMMethod caller, int bcIndex, RVMMethod callee) {
235        if (MCInformation == null) return false;
236        if (inlineEncoding == null) return false;
237        return OptEncodedCallSiteTree.edgePresent(caller.getId(), bcIndex, callee.getId(), inlineEncoding);
238      }
239    
240      /**
241       * Returns the GC map information for the GC map information entry passed
242       * @param  index     GCmap entry
243       */
244      @Uninterruptible
245      public int gcMapInformation(int index) {
246        return OptGCMap.gcMapInformation(index, gcMaps);
247      }
248    
249      /**
250       * Determines if the register map information for the entry passed is true
251       * @param  entry            map entry
252       * @param  registerNumber   the register number
253       */
254      @Uninterruptible
255      public boolean registerIsSet(int entry, int registerNumber) {
256        return OptGCMap.registerIsSet(entry, registerNumber, gcMaps);
257      }
258    
259      /**
260       * @return the next (relative) location or -1 for no more locations
261       */
262      @Uninterruptible
263      public int nextLocation(int currentIndex) {
264        return OptGCMap.nextLocation(currentIndex, gcMaps);
265      }
266    
267      ///////////////////////////////////////
268      // Implementation
269      ///////////////////////////////////////
270    
271      /**
272       * Do a binary search of the machine code maps to find the index
273       * in MCInformation where the entry for the argument machine code
274       * offset starts. Will return -1 if the entry doesn't exist.
275       *
276       * @param MCOffset the machine code offset of interest
277       */
278      @Uninterruptible
279      private int findMCEntry(Offset MCOffset) {
280        // Given a machine code instruction MCOffset, find the corresponding entry
281        if (MCInformation == null) return -1;
282        if (MCInformation.length == 0) return -1;
283    
284        int left = 0;
285        int right = MCInformation.length - 1;
286        while (left <= right) {
287          int middle = (left + right) >> 1;         // take the average
288          while ((MCInformation[middle] & START_OF_ENTRY) != START_OF_ENTRY) {
289            // if necessary, step backwards to beginning of entry.
290            middle--;
291          }
292          Offset offset = Offset.fromIntSignExtend(getMCOffset(middle));
293          if (MCOffset.EQ(offset)) {
294            return middle;
295          } else if (MCOffset.sGT(offset)) {
296            // middle is too small, shift interval to the right
297            left = middle + 1;
298            if (left >= MCInformation.length) return -1;
299            while ((MCInformation[left] & START_OF_ENTRY) != START_OF_ENTRY) {
300              // if necessary, step forward to find next entry, but not passed end
301              // Need to do this to avoid finding middle again
302              left++;
303              if (left >= MCInformation.length) {
304                return -1;
305              }
306            }
307          } else {
308            // middle is too small, shift interval to the left
309            right = middle - 1;
310            // Note no need to adjust as, we won't chance finding middle again
311          }
312        }
313        return -1;
314      }
315    
316      private int nextEntry(int entry) {
317        if (isBigEntry(entry)) return entry + SIZEOF_BIG_ENTRY;
318        if (isHugeEntry(entry)) return entry + SIZEOF_HUGE_ENTRY;
319        return entry + SIZEOF_ENTRY;
320      }
321    
322      ////////////////////////////////////////////
323      // Create the map (at compile time)
324      ////////////////////////////////////////////
325    
326      /**
327       *  This method walks the IR map, and for each entry it creates
328       *  the machine code mapping information for the entry.
329       *  It is called during the compilation of the method, not at GC time.
330       *  @param irMap  the irmap to translate from
331       *  @param DUMP_MAPS dump while we work
332       */
333      private static OptMachineCodeMap generateMCInformation(GCIRMap irMap, boolean DUMP_MAPS) {
334        CallSiteTree inliningMap = new CallSiteTree();
335        int numEntries = 0;
336    
337        // (1) Count how many entries we are going to have and
338        //     construct and encode the inlining information for those entries.
339        for (GCIRMapElement irMapElem : irMap) {
340          numEntries++;
341          Instruction instr = irMapElem.getInstruction();
342          if (instr.position == null && instr.bcIndex != INSTRUMENTATION_BCI) {
343            if (MIR_Call.conforms(instr) && MIR_Call.hasMethod(instr)) {
344              throw new OptimizingCompilerException("position required for all call instructions " + instr);
345            }
346          } else {
347            inliningMap.addLocation(instr.position);
348          }
349        }
350    
351        if (numEntries == 0) return emptyMachineCodeMap; // if no entries, then we are done.
352    
353        int[] inlineEncoding = OptEncodedCallSiteTree.getEncoding(inliningMap);
354    
355        // (2) Encode the primary machine code mapping information and the GCMaps.
356        OptGCMap gcMapBuilder = new OptGCMap();
357        int[] tmpMC = new int[numEntries * SIZEOF_HUGE_ENTRY];
358        int lastMCInfoEntry = 0;
359        for (GCIRMapElement irMapElem : irMap) {
360          Instruction instr = irMapElem.getInstruction();
361          if (DUMP_MAPS) VM.sysWrite("IR Map for " + instr + "\n\t" + irMapElem);
362    
363          // retrieve the machine code offset (in bytes) from the instruction,
364          int mco = instr.getmcOffset();
365          if (mco < 0) {
366            VM.sysWrite("Negative machine code MCOffset found:" + mco);
367            Instruction i = irMapElem.getInstruction();
368            VM.sysWrite(i.bcIndex + ", " + i + ", " + i.getmcOffset() + "\n");
369            throw new OptimizingCompilerException("Negative machine code MCOffset found");
370          }
371          // create GC map and get GCI
372          int gci = gcMapBuilder.generateGCMapEntry(irMapElem);
373          // get bci information
374          int bci = instr.getBytecodeIndex();
375          if (bci < 0) {
376            if (bci == UNKNOWN_BCI && MIR_Call.conforms(instr) && MIR_Call.hasMethod(instr)) {
377              throw new OptimizingCompilerException("valid bytecode index required for all calls " + instr);
378            }
379            bci = -1;
380          }
381          // get index into inline encoding
382          int iei = -1;
383          if (instr.position != null) {
384            iei = inliningMap.find(instr.position).encodedOffset;
385          }
386          // set the call info
387          int cm = 0;
388          if (MIR_Call.conforms(instr)) {
389            MethodOperand mo = MIR_Call.getMethod(instr);
390            if (mo != null && mo.isGuardedInlineOffBranch()) {
391              cm = IS_GUARDED_CALL;
392            } else {
393              cm = IS_UNGUARDED_CALL;
394            }
395          }
396    
397          // Encode this entry into MCInformation
398          if (bci < INVALID_BCI && iei < INVALID_IEI && gci < INVALID_GCI && mco < (OFFSET_MASK >>> OFFSET_SHIFT)) {
399            // use a small entry
400            if (bci == -1) bci = INVALID_BCI;
401            if (iei == -1) iei = INVALID_IEI;
402            if (gci == -1) gci = INVALID_GCI;
403            if (VM.VerifyAssertions) {
404              VM._assert((cm & (CALL_MASK >>> CALL_SHIFT)) == cm);
405              VM._assert((bci & (BCI_MASK >>> BCI_SHIFT)) == bci);
406              VM._assert((iei & (IEI_MASK >>> IEI_SHIFT)) == iei);
407              VM._assert((gci & (GCI_MASK >>> GCI_SHIFT)) == gci);
408              VM._assert((mco & (OFFSET_MASK >>> OFFSET_SHIFT)) == mco);
409            }
410            int t = START_OF_ENTRY;
411            t |= (cm << CALL_SHIFT);
412            t |= (bci << BCI_SHIFT);
413            t |= (iei << IEI_SHIFT);
414            t |= (gci << GCI_SHIFT);
415            t |= (mco << OFFSET_SHIFT);
416            tmpMC[lastMCInfoEntry++] = t;
417          } else if (bci < BIG_INVALID_BCI &&
418                     iei < BIG_INVALID_IEI &&
419                     gci < BIG_INVALID_GCI &&
420                     mco < (BIG_OFFSET_MASK >>> BIG_OFFSET_SHIFT)) {
421            // use a big entry
422            if (bci == -1) bci = BIG_INVALID_BCI;
423            if (iei == -1) iei = BIG_INVALID_IEI;
424            if (gci == -1) gci = BIG_INVALID_GCI;
425            if (VM.VerifyAssertions) {
426              VM._assert((cm & (BIG_CALL_MASK >>> BIG_CALL_SHIFT)) == cm);
427              VM._assert((bci & (BIG_BCI_MASK >>> BIG_BCI_SHIFT)) == bci);
428              VM._assert((iei & (BIG_IEI_MASK >>> BIG_IEI_SHIFT)) == iei);
429              VM._assert((gci & (BIG_GCI_MASK >>> BIG_GCI_SHIFT)) == gci);
430              VM._assert((mco & (BIG_OFFSET_MASK >>> BIG_OFFSET_SHIFT)) == mco);
431            }
432            int startIdx = lastMCInfoEntry;
433            tmpMC[startIdx] = START_OF_BIG_ENTRY;
434            tmpMC[startIdx + BIG_CALL_IDX_ADJ] |= (cm << BIG_CALL_SHIFT);
435            tmpMC[startIdx + BIG_BCI_IDX_ADJ] |= (bci << BIG_BCI_SHIFT);
436            tmpMC[startIdx + BIG_OFFSET_IDX_ADJ] |= (mco << BIG_OFFSET_SHIFT);
437            tmpMC[startIdx + BIG_GCI_IDX_ADJ] |= (gci << BIG_GCI_SHIFT);
438            tmpMC[startIdx + BIG_IEI_IDX_ADJ] |= (iei << BIG_IEI_SHIFT);
439            lastMCInfoEntry += SIZEOF_BIG_ENTRY;
440          } else {
441            // use a huge entry
442            if (bci == -1) bci = HUGE_INVALID_BCI;
443            if (iei == -1) iei = HUGE_INVALID_IEI;
444            if (gci == -1) gci = HUGE_INVALID_GCI;
445            if (VM.VerifyAssertions) {
446              VM._assert((cm & (HUGE_CALL_MASK >>> HUGE_CALL_SHIFT)) == cm);
447              VM._assert((bci & (HUGE_BCI_MASK >>> HUGE_BCI_SHIFT)) == bci);
448              VM._assert((iei & (HUGE_IEI_MASK >>> HUGE_IEI_SHIFT)) == iei);
449              VM._assert((gci & (HUGE_GCI_MASK >>> HUGE_GCI_SHIFT)) == gci);
450              VM._assert((mco & (HUGE_OFFSET_MASK >>> HUGE_OFFSET_SHIFT)) == mco);
451            }
452            int startIdx = lastMCInfoEntry;
453            tmpMC[startIdx] = START_OF_HUGE_ENTRY;
454            tmpMC[startIdx + HUGE_CALL_IDX_ADJ] |= (cm << HUGE_CALL_SHIFT);
455            tmpMC[startIdx + HUGE_BCI_IDX_ADJ] |= (bci << HUGE_BCI_SHIFT);
456            tmpMC[startIdx + HUGE_OFFSET_IDX_ADJ] |= (mco << HUGE_OFFSET_SHIFT);
457            tmpMC[startIdx + HUGE_GCI_IDX_ADJ] |= (gci << HUGE_GCI_SHIFT);
458            tmpMC[startIdx + HUGE_IEI_IDX_ADJ] |= (iei << HUGE_IEI_SHIFT);
459            lastMCInfoEntry += SIZEOF_HUGE_ENTRY;
460          }
461        }
462        int[] mcInformation = new int[lastMCInfoEntry];
463        System.arraycopy(tmpMC, 0, mcInformation, 0, mcInformation.length);
464        int[] gcMaps = gcMapBuilder.finish();
465    
466        return new OptMachineCodeMap(mcInformation, gcMaps, inlineEncoding);
467      }
468    
469      ////////////////////////////////////////////
470      //  Accessors
471      //  NB: The accessors take an entry number, which is defined to
472      //      be the index of word I of the MCInformation entry
473      ////////////////////////////////////////////
474      /**
475       * Returns the MCOffset for the entry passed
476       * @param  entry the index of the start of the entry
477       * @return the MCOffset for this entry
478       */
479      @Uninterruptible
480      private int getMCOffset(int entry) {
481        if (isBigEntry(entry)) {
482          int t = MCInformation[entry + BIG_OFFSET_IDX_ADJ];
483          return (t & BIG_OFFSET_MASK) >>> BIG_OFFSET_SHIFT;
484        } else if (isHugeEntry(entry)) {
485          int t = MCInformation[entry + HUGE_OFFSET_IDX_ADJ];
486          return (t & HUGE_OFFSET_MASK) >>> HUGE_OFFSET_SHIFT;
487        } else {
488          int t = MCInformation[entry];
489          return (t & OFFSET_MASK) >>> OFFSET_SHIFT;
490        }
491      }
492    
493      /**
494       * Returns the GC map index for the entry passed
495       * @param   entry the index of the start of the entry
496       * @return the GC map entry index for this entry (or -1 if none)
497       */
498      @Uninterruptible
499      private int getGCMapIndex(int entry) {
500        if (isBigEntry(entry)) {
501          int t = MCInformation[entry + BIG_GCI_IDX_ADJ];
502          int gci = (t & BIG_GCI_MASK) >>> BIG_GCI_SHIFT;
503          if (gci == BIG_INVALID_GCI) return -1;
504          return gci;
505        } else if (isHugeEntry(entry)) {
506          int t = MCInformation[entry + HUGE_GCI_IDX_ADJ];
507          int gci = (t & HUGE_GCI_MASK) >>> HUGE_GCI_SHIFT;
508          if (gci == HUGE_INVALID_GCI) return -1;
509          return gci;
510        } else {
511          int t = MCInformation[entry];
512          int gci = (t & GCI_MASK) >>> GCI_SHIFT;
513          if (gci == INVALID_GCI) return -1;
514          return gci;
515        }
516      }
517    
518      /**
519       * Returns the Bytecode index for the entry passed
520       * @param entry the index of the start of the entry
521       * @return the bytecode index for this entry (-1 if unknown)
522       */
523      @Uninterruptible
524      private int getBytecodeIndex(int entry) {
525        if (isBigEntry(entry)) {
526          int t = MCInformation[entry + BIG_BCI_IDX_ADJ];
527          int bci = (t & BIG_BCI_MASK) >>> BIG_BCI_SHIFT;
528          if (bci == BIG_INVALID_BCI) return -1;
529          return bci;
530        } else if (isHugeEntry(entry)) {
531          int t = MCInformation[entry + HUGE_BCI_IDX_ADJ];
532          int bci = (t & HUGE_BCI_MASK) >>> HUGE_BCI_SHIFT;
533          if (bci == HUGE_INVALID_BCI) return -1;
534          return bci;
535        } else {
536          int t = MCInformation[entry];
537          int bci = (t & BCI_MASK) >>> BCI_SHIFT;
538          if (bci == INVALID_BCI) return -1;
539          return bci;
540        }
541      }
542    
543      /**
544       * Returns the inline encoding index for the entry passed.
545       * @param entry the index of the start of the entry
546       * @return the inline encoding index for this entry (-1 if unknown)
547       */
548      @Uninterruptible
549      private int getInlineEncodingIndex(int entry) {
550        if (isBigEntry(entry)) {
551          int t = MCInformation[entry + BIG_IEI_IDX_ADJ];
552          int iei = (t & BIG_IEI_MASK) >>> BIG_IEI_SHIFT;
553          if (iei == BIG_INVALID_IEI) return -1;
554          return iei;
555        } else if (isHugeEntry(entry)) {
556          int t = MCInformation[entry + HUGE_IEI_IDX_ADJ];
557          int iei = (t & HUGE_IEI_MASK) >>> HUGE_IEI_SHIFT;
558          if (iei == HUGE_INVALID_IEI) return -1;
559          return iei;
560        } else {
561          int t = MCInformation[entry];
562          int iei = (t & IEI_MASK) >>> IEI_SHIFT;
563          if (iei == INVALID_IEI) return -1;
564          return iei;
565        }
566      }
567    
568      /**
569       * Returns the call info for the entry passed.
570       * @param entry the index of the start of the entry
571       * @return the call info for this entry
572       */
573      @Uninterruptible
574      private int getCallInfo(int entry) {
575        if (isBigEntry(entry)) {
576          int t = MCInformation[entry + BIG_CALL_IDX_ADJ];
577          return (t & BIG_CALL_MASK) >>> BIG_CALL_SHIFT;
578        } else if (isHugeEntry(entry)) {
579          int t = MCInformation[entry + HUGE_CALL_IDX_ADJ];
580          return (t & HUGE_CALL_MASK) >>> HUGE_CALL_SHIFT;
581        } else {
582          int t = MCInformation[entry];
583          return (t & CALL_MASK) >>> CALL_SHIFT;
584        }
585      }
586    
587      /**
588       * Is the entry a big entry?
589       */
590      @Uninterruptible
591      @Inline
592      private boolean isBigEntry(int entry) {
593        if (VM.VerifyAssertions) {
594          VM._assert((MCInformation[entry] & START_OF_ENTRY) == START_OF_ENTRY);
595        }
596        return (MCInformation[entry] & START_BITS) == START_OF_BIG_ENTRY;
597      }
598    
599      /**
600       * Is the entry a big entry?
601       */
602      @Uninterruptible
603      @Inline
604      private boolean isHugeEntry(int entry) {
605        if (VM.VerifyAssertions) {
606          VM._assert((MCInformation[entry] & START_OF_ENTRY) == START_OF_ENTRY);
607        }
608        return (MCInformation[entry] & START_BITS) == START_OF_HUGE_ENTRY;
609      }
610    
611      ////////////////////////////////////////////
612      //  Debugging
613      ////////////////////////////////////////////
614    
615      public void dumpMCInformation(boolean DUMP_MAPS) {
616        if (DUMP_MAPS) {
617          VM.sysWrite("  Dumping the MCInformation\n");
618          if (MCInformation == null) return;
619          for (int idx = 0; idx < MCInformation.length;) {
620            printMCInformationEntry(idx, DUMP_MAPS);
621            idx = nextEntry(idx);
622          }
623        }
624      }
625    
626      /**
627       * Prints the MCInformation for this entry
628       * @param entry  the entry to print
629       */
630      private void printMCInformationEntry(int entry, boolean DUMP_MAPS) {
631        if (DUMP_MAPS) {
632          String sep = "\tMC: ";
633          if (isBigEntry(entry)) sep = "B\tMC: ";
634          if (isHugeEntry(entry)) sep = "H\tMC: ";
635          VM.sysWrite(entry + sep + getMCOffset(entry));
636          int bci = getBytecodeIndex(entry);
637          if (bci != -1) {
638            VM.sysWrite("\n\tBCI: " + bci);
639          }
640          int iei = getInlineEncodingIndex(entry);
641          if (iei != -1) {
642            VM.sysWrite("\n\tIEI: " + iei);
643          }
644          boolean first = true;
645          while (iei >= 0) {
646            int mid = OptEncodedCallSiteTree.getMethodID(iei, inlineEncoding);
647            RVMMethod meth = MemberReference.getMemberRef(mid).asMethodReference().getResolvedMember();
648            if (first) {
649              first = false;
650              VM.sysWrite("\n\tIn method    " + meth + " at bytecode " + bci);
651            } else {
652              VM.sysWrite("\n\tInlined into " + meth + " at bytecode " + bci);
653            }
654            if (iei > 0) {
655              bci = OptEncodedCallSiteTree.getByteCodeOffset(iei, inlineEncoding);
656            }
657            iei = OptEncodedCallSiteTree.getParent(iei, inlineEncoding);
658          }
659          if (getGCMapIndex(entry) != OptGCMap.NO_MAP_ENTRY) {
660            VM.sysWrite("\n\tGC Map Idx: " + getGCMapIndex(entry) + " ");
661            OptGCMap.dumpMap(getGCMapIndex(entry), gcMaps);
662          } else {
663            VM.sysWrite("\n\tno GC map");
664          }
665          VM.sysWrite("\n");
666        }
667      }
668    
669      /**
670       * Gather cumulative stats about the space consumed by maps.
671       * @param method
672       * @param mapSize
673       * @param machineCodeSize
674       * @param DUMP_MAP_SIZES
675       */
676      private void recordStats(RVMMethod method, int mapSize, int machineCodeSize, boolean DUMP_MAP_SIZES) {
677        if (DUMP_MAP_SIZES) {
678          double mapMCPercent = (double) mapSize / machineCodeSize;
679          VM.sysWrite(method);
680          VM.sysWrite(" map is " + (int) (mapMCPercent * 100) + "% (" + mapSize + "/" + machineCodeSize + ") of MC.\n");
681          totalMCSize += machineCodeSize;
682          totalMapSize += mapSize;
683          double MCPct = (double) totalMapSize / totalMCSize;
684          VM.sysWrite("  Cumulative maps are now " +
685                      (int) (MCPct * 100) +
686                      "% (" +
687                      totalMapSize +
688                      "/" +
689                      totalMCSize +
690                      ") of MC.\n");
691        }
692      }
693    
694      /**
695       * Total bytes of machine code maps
696       */
697      int size() {
698        int size = TYPE.peekType().asClass().getInstanceSize();
699        if (MCInformation != null) size += RVMArray.IntArray.getInstanceSize(MCInformation.length);
700        if (inlineEncoding != null) size += RVMArray.IntArray.getInstanceSize(inlineEncoding.length);
701        if (gcMaps != null) size += RVMArray.IntArray.getInstanceSize(gcMaps.length);
702        return size;
703      }
704    
705      ////////////////////////////////////////////
706      //
707      //  Encoding constants and backing data.
708      //
709      ////////////////////////////////////////////
710      // An entry contains the following data:
711      //   o: a machine code offset (in bytes)
712      //   g: an index into the GC maps array
713      //   b: bytecode index of the instruction
714      //   i: index into the inline encoding.
715      //   c: bits to encode one of three possibilites
716      //      (a) the instruction is not a call
717      //      (b) the instruction is a "normal" call
718      //      (c) the instruction is a call in the off-branch
719      //          of a guarded inline.
720      //   U indicates an unused bit; its value is undefined.
721      //
722      // We support three entry formats as defined below
723      //
724      private static final int START_OF_ENTRY = 0x80000000;
725      private static final int START_OF_BIG_ENTRY = 0xc0000000;
726      private static final int START_OF_HUGE_ENTRY = 0xe0000000;
727      private static final int START_BITS = 0xe0000000;
728    
729      // A small entry is 1 int used as follows:
730      // 10cc bbbb bbii iiii iggg ggoo oooo oooo
731      private static final int CALL_MASK = 0x30000000;
732      private static final int CALL_SHIFT = 28;
733      private static final int BCI_MASK = 0x0fc00000;
734      private static final int BCI_SHIFT = 22;
735      private static final int IEI_MASK = 0x003f8000;
736      private static final int IEI_SHIFT = 15;
737      private static final int GCI_MASK = 0x00007c00;
738      private static final int GCI_SHIFT = 10;
739      private static final int OFFSET_MASK = 0x000003ff;
740      private static final int OFFSET_SHIFT = 0;
741      private static final int INVALID_GCI = (GCI_MASK >>> GCI_SHIFT);
742      private static final int INVALID_BCI = (BCI_MASK >>> BCI_SHIFT);
743      private static final int INVALID_IEI = (IEI_MASK >>> IEI_SHIFT);
744      private static final int SIZEOF_ENTRY = 1;
745    
746      // A big entry is 2 ints used as follows:
747      // 110c cbbb bbbb bbbb biii iiii iiii iiii
748      // 0ggg gggg gggg ggoo oooo oooo oooo oooo
749      private static final int BIG_CALL_MASK = 0x18000000;
750      private static final int BIG_CALL_SHIFT = 27;
751      private static final int BIG_CALL_IDX_ADJ = 0;
752      private static final int BIG_BCI_MASK = 0x07ff8000;
753      private static final int BIG_BCI_SHIFT = 15;
754      private static final int BIG_BCI_IDX_ADJ = 0;
755      private static final int BIG_IEI_MASK = 0x00007fff;
756      private static final int BIG_IEI_SHIFT = 0;
757      private static final int BIG_IEI_IDX_ADJ = 0;
758      private static final int BIG_GCI_MASK = 0x7ffc0000;
759      private static final int BIG_GCI_SHIFT = 18;
760      private static final int BIG_GCI_IDX_ADJ = 1;
761      private static final int BIG_OFFSET_MASK = 0x0003ffff;
762      private static final int BIG_OFFSET_SHIFT = 0;
763      private static final int BIG_OFFSET_IDX_ADJ = 1;
764      private static final int BIG_INVALID_GCI = (BIG_GCI_MASK >>> BIG_GCI_SHIFT);
765      private static final int BIG_INVALID_BCI = (BIG_BCI_MASK >>> BIG_BCI_SHIFT);
766      private static final int BIG_INVALID_IEI = (BIG_IEI_MASK >>> BIG_IEI_SHIFT);
767      private static final int SIZEOF_BIG_ENTRY = 2;
768    
769      // A huge entry is 4 ints used as follows:
770      // 111c cUUU UUUU UUUU bbbb bbbb bbbb bbbb
771      // 0iii iiii iiii iiii iiii iiii iiii iiii
772      // 0ggg gggg gggg gggg gggg gggg gggg gggg
773      // 0ooo oooo oooo oooo oooo oooo oooo oooo
774      private static final int HUGE_CALL_MASK = 0x18000000;
775      private static final int HUGE_CALL_SHIFT = 27;
776      private static final int HUGE_CALL_IDX_ADJ = 0;
777      private static final int HUGE_BCI_MASK = 0x0000ffff;
778      private static final int HUGE_BCI_SHIFT = 0;
779      private static final int HUGE_BCI_IDX_ADJ = 0;
780      private static final int HUGE_IEI_MASK = 0x7fffffff;
781      private static final int HUGE_IEI_SHIFT = 0;
782      private static final int HUGE_IEI_IDX_ADJ = 1;
783      private static final int HUGE_GCI_MASK = 0x7fffffff;
784      private static final int HUGE_GCI_SHIFT = 0;
785      private static final int HUGE_GCI_IDX_ADJ = 2;
786      private static final int HUGE_OFFSET_MASK = 0x7fffffff;
787      private static final int HUGE_OFFSET_SHIFT = 0;
788      private static final int HUGE_OFFSET_IDX_ADJ = 3;
789      private static final int HUGE_INVALID_GCI = (HUGE_GCI_MASK >>> HUGE_GCI_SHIFT);
790      private static final int HUGE_INVALID_BCI = (HUGE_BCI_MASK >>> HUGE_BCI_SHIFT);
791      private static final int HUGE_INVALID_IEI = (HUGE_IEI_MASK >>> HUGE_IEI_SHIFT);
792      private static final int SIZEOF_HUGE_ENTRY = 4;
793    
794      // bit patterns for cc portion of machine code map */
795      private static final int IS_UNGUARDED_CALL = 0x1;
796      private static final int IS_GUARDED_CALL = 0x3;
797    
798      /**
799       * Hold entries as defined by the constants above.
800       */
801      private final int[] MCInformation;
802      /**
803       * array of GC maps as defined by OptGCMap
804       */
805      private final int[] gcMaps;
806      /**
807       * encoded data as defined by OptEncodedCallSiteTree.
808       */
809      public final int[] inlineEncoding;
810      /**
811       * Running totals for the size of machine code and maps
812       */
813      private static int totalMCSize = 0;
814      private static int totalMapSize = 0;
815      /**
816       * A machine code map when no information is present
817       */
818      private static final OptMachineCodeMap emptyMachineCodeMap = new OptMachineCodeMap();
819    
820      private static final TypeReference TYPE = TypeReference.findOrCreate(OptMachineCodeMap.class);
821    }