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