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 }