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