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 }