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.baseline; 014 015 import org.jikesrvm.PrintLN; 016 import org.jikesrvm.VM; 017 import org.jikesrvm.ArchitectureSpecific.BaselineCompilerImpl; 018 import org.jikesrvm.ArchitectureSpecific.BaselineConstants; 019 import org.jikesrvm.ArchitectureSpecific.BaselineExceptionDeliverer; 020 import org.jikesrvm.classloader.ExceptionHandlerMap; 021 import org.jikesrvm.classloader.NormalMethod; 022 import org.jikesrvm.classloader.RVMArray; 023 import org.jikesrvm.classloader.RVMMethod; 024 import org.jikesrvm.classloader.RVMType; 025 import org.jikesrvm.classloader.TypeReference; 026 import org.jikesrvm.compilers.common.CompiledMethod; 027 import org.jikesrvm.compilers.common.ExceptionTable; 028 import org.jikesrvm.runtime.DynamicLink; 029 import org.jikesrvm.runtime.ExceptionDeliverer; 030 import org.jikesrvm.runtime.StackBrowser; 031 import org.vmmagic.pragma.Uninterruptible; 032 import org.vmmagic.pragma.Unpreemptible; 033 import org.vmmagic.unboxed.Offset; 034 035 /** 036 * Compiler-specific information associated with a method's machine 037 * instructions. 038 */ 039 public final class BaselineCompiledMethod extends CompiledMethod implements BaselineConstants { 040 041 /** Does the baseline compiled method have a counters array? */ 042 private boolean hasCounters; 043 044 /** 045 * The lock acquisition offset for synchronized methods. For 046 * synchronized methods, the offset (in the method prologue) after 047 * which the monitor has been obtained. At, or before, this point, 048 * the method does not own the lock. Used by deliverException to 049 * determine whether the lock needs to be released. Note: for this 050 * scheme to work, Lock must not allow a yield after it has been 051 * obtained. 052 */ 053 private char lockOffset; 054 055 /** 056 * Baseline exception deliverer object 057 */ 058 private static final ExceptionDeliverer exceptionDeliverer = new BaselineExceptionDeliverer(); 059 060 /** 061 * Stack-slot reference maps for the compiled method. 062 */ 063 public ReferenceMaps referenceMaps; 064 065 /** 066 * Encoded representation of bytecode index to offset in code array 067 * map. Currently needed to support dynamic bridge magic; Consider 068 * integrating with GC maps 069 */ 070 private byte[] bytecodeMap; 071 072 /** 073 * Exception table, null if not present. 074 */ 075 private int[] eTable; 076 077 /** Offset into stack frame when operand stack is empty */ 078 private final short emptyStackOffset; 079 /** PPC only: last general purpose register holding part of the operand stack */ 080 private byte lastFixedStackRegister; 081 /** PPC only: last floating point register holding part of the operand stack */ 082 private byte lastFloatStackRegister; 083 084 /** 085 * PPC only: location of general purpose local variables, positive 086 * values are register numbers, negative are stack offsets 087 */ 088 private final short[] localFixedLocations; 089 090 /** 091 * PPC only: location of floating point local variables, positive 092 * values are register numbers, negative are stack offsets 093 */ 094 private final short[] localFloatLocations; 095 096 /** @return offset into stack frame when operand stack is empty */ 097 public int getEmptyStackOffset() { 098 return emptyStackOffset; 099 } 100 101 /** 102 * Location of local general purpose variable. These Locations are 103 * positioned at the top of the stackslot that contains the value 104 * before accessing, substract size of value you want to access.<p> 105 * e.g. to load int: load at BaselineCompilerImpl.locationToOffset(location) - BYTES_IN_INT<br> 106 * e.g. to load long: load at BaselineCompilerImpl.locationToOffset(location) - BYTES_IN_LONG 107 */ 108 @Uninterruptible 109 public short getGeneralLocalLocation(int localIndex) { 110 return BaselineCompilerImpl.getGeneralLocalLocation(localIndex, localFixedLocations, (NormalMethod) method); 111 } 112 113 /** 114 * Location of local floating point variable. These Locations are 115 * positioned at the top of the stackslot that contains the value 116 * before accessing, substract size of value you want to access.<p> 117 * e.g. to load float: load at BaselineCompilerImpl.locationToOffset(location) - BYTES_IN_FLOAT<br> 118 * e.g. to load double: load at BaselineCompilerImpl.locationToOffset(location) - BYTES_IN_DOUBLE 119 */ 120 @Uninterruptible 121 public short getFloatLocalLocation(int localIndex) { 122 return BaselineCompilerImpl.getFloatLocalLocation(localIndex, localFloatLocations, (NormalMethod) method); 123 } 124 125 /** Offset onto stack of a particular general purpose operand stack location */ 126 @Uninterruptible 127 public short getGeneralStackLocation(int stackIndex) { 128 return BaselineCompilerImpl.offsetToLocation(emptyStackOffset - (stackIndex << LOG_BYTES_IN_ADDRESS)); 129 } 130 131 /** Offset onto stack of a particular operand stack location for a floating point value */ 132 @Uninterruptible 133 public short getFloatStackLocation(int stackIndex) { 134 // for now same implementation as getGeneralStackLocation 135 return getGeneralStackLocation(stackIndex); 136 } 137 138 /** Last general purpose register holding part of the operand stack */ 139 @Uninterruptible 140 public int getLastFixedStackRegister() { 141 return lastFixedStackRegister; 142 } 143 144 /** Last floating point register holding part of the operand stack */ 145 @Uninterruptible 146 public int getLastFloatStackRegister() { 147 return lastFloatStackRegister; 148 } 149 150 /** Constructor */ 151 public BaselineCompiledMethod(int id, RVMMethod m) { 152 super(id, m); 153 NormalMethod nm = (NormalMethod) m; 154 //this.startLocalOffset = BaselineCompilerImpl.getStartLocalOffset(nm); 155 this.emptyStackOffset = (short)BaselineCompilerImpl.getEmptyStackOffset(nm); 156 this.localFixedLocations = VM.BuildForIA32 ? null : new short[nm.getLocalWords()]; 157 this.localFloatLocations = VM.BuildForIA32 ? null : new short[nm.getLocalWords()]; 158 this.lastFixedStackRegister = -1; 159 this.lastFloatStackRegister = -1; 160 } 161 162 /** Compile method */ 163 public void compile() { 164 BaselineCompilerImpl comp = new BaselineCompilerImpl(this, localFixedLocations, localFloatLocations); 165 comp.compile(); 166 this.lastFixedStackRegister = comp.getLastFixedStackRegister(); 167 this.lastFloatStackRegister = comp.getLastFloatStackRegister(); 168 } 169 170 /** @return BASELINE */ 171 @Override 172 @Uninterruptible 173 public int getCompilerType() { 174 return BASELINE; 175 } 176 177 /** @return "baseline compiler" */ 178 @Override 179 public String getCompilerName() { 180 return "baseline compiler"; 181 } 182 183 /** 184 * Get the exception deliverer for this kind of compiled method 185 */ 186 @Override 187 @Uninterruptible 188 public ExceptionDeliverer getExceptionDeliverer() { 189 return exceptionDeliverer; 190 } 191 192 /** 193 * Find a catch block within the compiled method 194 * @param instructionOffset offset of faulting instruction in compiled code 195 * @param exceptionType the type of the thrown exception 196 * @return the machine code offset of the catch block. 197 */ 198 @Override 199 @Unpreemptible 200 public int findCatchBlockForInstruction(Offset instructionOffset, RVMType exceptionType) { 201 if (eTable == null) { 202 return -1; 203 } else { 204 return ExceptionTable.findCatchBlockForInstruction(eTable, instructionOffset, exceptionType); 205 } 206 } 207 208 @Override 209 @Uninterruptible 210 public void getDynamicLink(DynamicLink dynamicLink, Offset instructionOffset) { 211 int bytecodeIndex = findBytecodeIndexForInstruction(instructionOffset); 212 ((NormalMethod) method).getDynamicLink(dynamicLink, bytecodeIndex); 213 } 214 215 /** 216 * @return The line number, a positive integer. Zero means unable to find. 217 */ 218 @Override 219 @Uninterruptible 220 public int findLineNumberForInstruction(Offset instructionOffset) { 221 int bci = findBytecodeIndexForInstruction(instructionOffset); 222 if (bci == -1) return 0; 223 return ((NormalMethod) method).getLineNumberForBCIndex(bci); 224 } 225 226 @Override 227 public boolean isWithinUninterruptibleCode(Offset instructionOffset) { 228 return method.isUninterruptible(); 229 } 230 231 /** 232 * Find bytecode index corresponding to one of this method's 233 * machine instructions. 234 * 235 * @param instructionOffset instruction offset to map to a bytecode index.<br> 236 * Note: This method expects the offset to refer to the machine 237 * instruction immediately FOLLOWING the bytecode in question. just 238 * like findLineNumberForInstruction. See CompiledMethod for 239 * rationale.<br> 240 * NOTE: instructionIndex is in units of instructions, not bytes 241 * (different from all the other methods in this interface!!) 242 * @return the bytecode index for the machine instruction, -1 if not 243 * available or not found. 244 */ 245 @Uninterruptible 246 public int findBytecodeIndexForInstruction(Offset instructionOffset) { 247 Offset instructionIndex = instructionOffset.toWord().rsha(LG_INSTRUCTION_WIDTH).toOffset(); 248 int candidateIndex = -1; 249 int bcIndex = 0; 250 Offset instrIndex = Offset.zero(); 251 for (int i = 0; i < bytecodeMap.length;) { 252 int b0 = (bytecodeMap[i++]) & 255; // unsign-extend 253 int deltaBC, deltaIns; 254 if (b0 != 255) { 255 deltaBC = b0 >> 5; 256 deltaIns = b0 & 31; 257 } else { 258 int b1 = (bytecodeMap[i++]) & 255; // unsign-extend 259 int b2 = (bytecodeMap[i++]) & 255; // unsign-extend 260 int b3 = (bytecodeMap[i++]) & 255; // unsign-extend 261 int b4 = (bytecodeMap[i++]) & 255; // unsign-extend 262 deltaBC = (b1 << 8) | b2; 263 deltaIns = (b3 << 8) | b4; 264 } 265 bcIndex += deltaBC; 266 instrIndex = instrIndex.plus(deltaIns); 267 if (instrIndex.sGE(instructionIndex)) { 268 break; 269 } 270 candidateIndex = bcIndex; 271 } 272 return candidateIndex; 273 } 274 275 @Override 276 public void set(StackBrowser browser, Offset instr) { 277 browser.setMethod(method); 278 browser.setCompiledMethod(this); 279 browser.setBytecodeIndex(findBytecodeIndexForInstruction(instr)); 280 281 if (VM.TraceStackTrace) { 282 VM.sysWrite("setting stack to frame (base): "); 283 VM.sysWrite(browser.getMethod()); 284 VM.sysWrite(browser.getBytecodeIndex()); 285 VM.sysWrite("\n"); 286 } 287 } 288 289 @Override 290 public boolean up(StackBrowser browser) { 291 return false; 292 } 293 294 @Override 295 public void printStackTrace(Offset instructionOffset, PrintLN out) { 296 out.print("\tat "); 297 out.print(method.getDeclaringClass()); // RVMClass 298 out.print('.'); 299 out.print(method.getName()); // a Atom, returned via MemberReference.getName(). 300 out.print("("); 301 out.print(method.getDeclaringClass().getSourceName()); // a Atom 302 int lineNumber = findLineNumberForInstruction(instructionOffset); 303 if (lineNumber <= 0) { // unknown line 304 out.print("; machine code offset: "); 305 out.printHex(instructionOffset.toInt()); 306 } else { 307 out.print(':'); 308 out.print(lineNumber); 309 } 310 out.print(')'); 311 out.println(); 312 } 313 314 /** 315 * Print the eTable 316 */ 317 public void printExceptionTable() { 318 if (eTable != null) ExceptionTable.printExceptionTable(eTable); 319 } 320 321 /** Set the lock acquisition offset for synchronized methods */ 322 public void setLockAcquisitionOffset(int off) { 323 if (VM.VerifyAssertions) VM._assert((off & 0xFFFF) == off); 324 lockOffset = (char) off; 325 } 326 327 /** Get the lock acquisition offset */ 328 @Uninterruptible 329 public Offset getLockAcquisitionOffset() { 330 return Offset.fromIntZeroExtend(lockOffset); 331 } 332 333 /** Set the method has a counters array */ 334 void setHasCounterArray() { 335 hasCounters = true; 336 } 337 338 /** Does the method have a counters array? */ 339 @Uninterruptible 340 public boolean hasCounterArray() { 341 return hasCounters; 342 } 343 344 /** 345 * Encode/compress the bytecode map, reference (GC) map and exception table 346 * 347 * @param referenceMaps to encode 348 * @param bcMap unencoded bytecode to code array offset map 349 */ 350 public void encodeMappingInfo(ReferenceMaps referenceMaps, int[] bcMap) { 351 int count = 0; 352 int lastBC = 0, lastIns = 0; 353 for (int i = 0; i < bcMap.length; i++) { 354 if (bcMap[i] != 0) { 355 int deltaBC = i - lastBC; 356 int deltaIns = bcMap[i] - lastIns; 357 if (VM.VerifyAssertions) { 358 VM._assert(deltaBC >= 0 && deltaIns >= 0); 359 } 360 if (deltaBC <= 6 && deltaIns <= 31) { 361 count++; 362 } else { 363 if (deltaBC > 65535 || deltaIns > 65535) { 364 VM.sysFail("BaselineCompiledMethod: a fancier encoding is needed"); 365 } 366 count += 5; 367 } 368 lastBC = i; 369 lastIns = bcMap[i]; 370 } 371 } 372 bytecodeMap = new byte[count]; 373 count = lastBC = lastIns = 0; 374 for (int i = 0; i < bcMap.length; i++) { 375 if (bcMap[i] != 0) { 376 int deltaBC = i - lastBC; 377 int deltaIns = bcMap[i] - lastIns; 378 if (VM.VerifyAssertions) { 379 VM._assert(deltaBC >= 0 && deltaIns >= 0); 380 } 381 if (deltaBC <= 6 && deltaIns <= 31) { 382 bytecodeMap[count++] = (byte) ((deltaBC << 5) | deltaIns); 383 } else { // From before, we know that deltaBC <= 65535 and deltaIns <= 65535 384 bytecodeMap[count++] = (byte) 255; 385 bytecodeMap[count++] = (byte) (deltaBC >> 8); 386 bytecodeMap[count++] = (byte) (deltaBC & 255); 387 bytecodeMap[count++] = (byte) (deltaIns >> 8); 388 bytecodeMap[count++] = (byte) (deltaIns & 255); 389 } 390 lastBC = i; 391 lastIns = bcMap[i]; 392 } 393 } 394 // TODO: it's likely for short methods we can share the bytecodeMap 395 referenceMaps.translateByte2Machine(bcMap); 396 this.referenceMaps = referenceMaps; 397 ExceptionHandlerMap emap = ((NormalMethod) method).getExceptionHandlerMap(); 398 if (emap != null) { 399 eTable = BaselineExceptionTable.encode(emap, bcMap); 400 } 401 } 402 403 @Override 404 public int size() { 405 TypeReference TYPE = TypeReference.findOrCreate(BaselineCompiledMethod.class); 406 int size = TYPE.peekType().asClass().getInstanceSize(); 407 if (bytecodeMap != null) size += RVMArray.ByteArray.getInstanceSize(bytecodeMap.length); 408 if (eTable != null) size += RVMArray.IntArray.getInstanceSize(eTable.length); 409 if (referenceMaps != null) size += referenceMaps.size(); 410 return size; 411 } 412 }