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 acquistion 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.
105 * e.g. to load int: load at BaselineCompilerImpl.locationToOffset(location) - BYTES_IN_INT
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.
117 * e.g. to load float: load at BaselineCompilerImpl.locationToOffset(location) - BYTES_IN_FLOAT
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 @Uninterruptible
172 public int getCompilerType() {
173 return BASELINE;
174 }
175
176 /** @return "baseline compiler" */
177 public String getCompilerName() {
178 return "baseline compiler";
179 }
180
181 /**
182 * Get the exception deliverer for this kind of compiled method
183 */
184 @Uninterruptible
185 public ExceptionDeliverer getExceptionDeliverer() {
186 return exceptionDeliverer;
187 }
188
189 /**
190 * Find a catch block within the compiled method
191 * @param instructionOffset offset of faulting instruction in compiled code
192 * @param exceptionType the type of the thrown exception
193 * @return the machine code offset of the catch block.
194 */
195 @Unpreemptible
196 public int findCatchBlockForInstruction(Offset instructionOffset, RVMType exceptionType) {
197 if (eTable == null) {
198 return -1;
199 } else {
200 return ExceptionTable.findCatchBlockForInstruction(eTable, instructionOffset, exceptionType);
201 }
202 }
203
204 /**
205 * Fetch symbolic reference to a method that's called by one of
206 * this method's instructions.
207 * @param dynamicLink place to put return information
208 * @param instructionOffset offset of machine instruction from start of
209 * this method, in bytes
210 *
211 * Notes:
212 * <ul>
213 * <li> The "instructionOffset" must point to the instruction i
214 * <em> following </em> the call
215 * instruction whose target method is sought.
216 * This allows us to properly handle the case where
217 * the only address we have to work with is a return address
218 * (ie. from a stackframe)
219 * on a machine architecture with variable length instructions.
220 * In such situations we'd have no idea how far to back up the
221 * instruction pointer
222 * to point to the "call site".
223 *
224 * <li> The implementation must not cause any allocations,
225 * because it executes with
226 * gc disabled when called by GCMapIterator.
227 * <ul>
228 */
229 @Uninterruptible
230 public void getDynamicLink(DynamicLink dynamicLink, Offset instructionOffset) {
231 int bytecodeIndex = findBytecodeIndexForInstruction(instructionOffset);
232 ((NormalMethod) method).getDynamicLink(dynamicLink, bytecodeIndex);
233 }
234
235 /**
236 * @return The line number, a positive integer. Zero means unable to find.
237 */
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 /**
246 * Return whether or not the instruction offset corresponds to an uninterruptible context.
247 *
248 * @param instructionOffset of addr from start of instructions in bytes
249 * @return true if the IP is within an Uninterruptible method, false otherwise.
250 */
251 public boolean isWithinUninterruptibleCode(Offset instructionOffset) {
252 return method.isUninterruptible();
253 }
254
255 /**
256 * Find bytecode index corresponding to one of this method's
257 * machine instructions.
258 *
259 * @param instructionOffset instruction offset to map to a bytecode index
260 * Note: This method expects the offset to refer to the machine
261 * instruction immediately FOLLOWING the bytecode in question. just
262 * like findLineNumberForInstruction. See CompiledMethod for
263 * rationale
264 * NOTE: instructionIndex is in units of instructions, not bytes
265 * (different from all the other methods in this interface!!)
266 * @return the bytecode index for the machine instruction, -1 if not
267 * available or not found.
268 */
269 @Uninterruptible
270 public int findBytecodeIndexForInstruction(Offset instructionOffset) {
271 Offset instructionIndex = instructionOffset.toWord().rsha(LG_INSTRUCTION_WIDTH).toOffset();
272 int candidateIndex = -1;
273 int bcIndex = 0;
274 Offset instrIndex = Offset.zero();
275 for (int i = 0; i < bytecodeMap.length;) {
276 int b0 = ((int) bytecodeMap[i++]) & 255; // unsign-extend
277 int deltaBC, deltaIns;
278 if (b0 != 255) {
279 deltaBC = b0 >> 5;
280 deltaIns = b0 & 31;
281 } else {
282 int b1 = ((int) bytecodeMap[i++]) & 255; // unsign-extend
283 int b2 = ((int) bytecodeMap[i++]) & 255; // unsign-extend
284 int b3 = ((int) bytecodeMap[i++]) & 255; // unsign-extend
285 int b4 = ((int) bytecodeMap[i++]) & 255; // unsign-extend
286 deltaBC = (b1 << 8) | b2;
287 deltaIns = (b3 << 8) | b4;
288 }
289 bcIndex += deltaBC;
290 instrIndex = instrIndex.plus(deltaIns);
291 if (instrIndex.sGE(instructionIndex)) {
292 break;
293 }
294 candidateIndex = bcIndex;
295 }
296 return candidateIndex;
297 }
298
299 /**
300 * Set the stack browser to the innermost logical stack frame of this method
301 */
302 public void set(StackBrowser browser, Offset instr) {
303 browser.setMethod(method);
304 browser.setCompiledMethod(this);
305 browser.setBytecodeIndex(findBytecodeIndexForInstruction(instr));
306
307 if (VM.TraceStackTrace) {
308 VM.sysWrite("setting stack to frame (base): ");
309 VM.sysWrite(browser.getMethod());
310 VM.sysWrite(browser.getBytecodeIndex());
311 VM.sysWrite("\n");
312 }
313 }
314
315 /**
316 * Advance the StackBrowser up one internal stack frame, if possible
317 */
318 public boolean up(StackBrowser browser) {
319 return false;
320 }
321
322 /**
323 * Print this compiled method's portion of a stack trace
324 * @param instructionOffset of machine instruction from start of method
325 * @param out the PrintLN to print the stack trace to.
326 */
327 public void printStackTrace(Offset instructionOffset, PrintLN out) {
328 out.print("\tat ");
329 out.print(method.getDeclaringClass()); // RVMClass
330 out.print('.');
331 out.print(method.getName()); // a Atom, returned via MemberReference.getName().
332 out.print("(");
333 out.print(method.getDeclaringClass().getSourceName()); // a Atom
334 int lineNumber = findLineNumberForInstruction(instructionOffset);
335 if (lineNumber <= 0) { // unknown line
336 out.print("; machine code offset: ");
337 out.printHex(instructionOffset.toInt());
338 } else {
339 out.print(':');
340 out.print(lineNumber);
341 }
342 out.print(')');
343 out.println();
344 }
345
346 /**
347 * Print the eTable
348 */
349 public void printExceptionTable() {
350 if (eTable != null) ExceptionTable.printExceptionTable(eTable);
351 }
352
353 /** Set the lock acquisition offset for synchronized methods */
354 public void setLockAcquisitionOffset(int off) {
355 if (VM.VerifyAssertions) VM._assert((off & 0xFFFF) == off);
356 lockOffset = (char) off;
357 }
358
359 /** Get the lock acquisition offset */
360 @Uninterruptible
361 public Offset getLockAcquisitionOffset() {
362 return Offset.fromIntZeroExtend(lockOffset);
363 }
364
365 /** Set the method has a counters array */
366 void setHasCounterArray() {
367 hasCounters = true;
368 }
369
370 /** Does the method have a counters array? */
371 @Uninterruptible
372 public boolean hasCounterArray() {
373 return hasCounters;
374 }
375
376 /**
377 * Encode/compress the bytecode map, reference (GC) map and exception table
378 *
379 * @param referenceMaps to encode
380 * @param bcMap unencoded bytecode to code array offset map
381 */
382 public void encodeMappingInfo(ReferenceMaps referenceMaps, int[] bcMap) {
383 int count = 0;
384 int lastBC = 0, lastIns = 0;
385 for (int i = 0; i < bcMap.length; i++) {
386 if (bcMap[i] != 0) {
387 int deltaBC = i - lastBC;
388 int deltaIns = bcMap[i] - lastIns;
389 if (VM.VerifyAssertions) {
390 VM._assert(deltaBC >= 0 && deltaIns >= 0);
391 }
392 if (deltaBC <= 6 && deltaIns <= 31) {
393 count++;
394 } else {
395 if (deltaBC > 65535 || deltaIns > 65535) {
396 VM.sysFail("BaselineCompiledMethod: a fancier encoding is needed");
397 }
398 count += 5;
399 }
400 lastBC = i;
401 lastIns = bcMap[i];
402 }
403 }
404 bytecodeMap = new byte[count];
405 count = lastBC = lastIns = 0;
406 for (int i = 0; i < bcMap.length; i++) {
407 if (bcMap[i] != 0) {
408 int deltaBC = i - lastBC;
409 int deltaIns = bcMap[i] - lastIns;
410 if (VM.VerifyAssertions) {
411 VM._assert(deltaBC >= 0 && deltaIns >= 0);
412 }
413 if (deltaBC <= 6 && deltaIns <= 31) {
414 bytecodeMap[count++] = (byte) ((deltaBC << 5) | deltaIns);
415 } else { // From before, we know that deltaBC <= 65535 and deltaIns <= 65535
416 bytecodeMap[count++] = (byte) 255;
417 bytecodeMap[count++] = (byte) (deltaBC >> 8);
418 bytecodeMap[count++] = (byte) (deltaBC & 255);
419 bytecodeMap[count++] = (byte) (deltaIns >> 8);
420 bytecodeMap[count++] = (byte) (deltaIns & 255);
421 }
422 lastBC = i;
423 lastIns = bcMap[i];
424 }
425 }
426 // TODO: it's likely for short methods we can share the bytecodeMap
427 referenceMaps.translateByte2Machine(bcMap);
428 this.referenceMaps = referenceMaps;
429 ExceptionHandlerMap emap = ((NormalMethod) method).getExceptionHandlerMap();
430 if (emap != null) {
431 eTable = BaselineExceptionTable.encode(emap, bcMap);
432 }
433 }
434
435 /**
436 * Return the number of bytes used to encode the compiler-specific mapping
437 * information for this compiled method.
438 * Used to gather stats on the space costs of mapping schemes.
439 */
440 public int size() {
441 TypeReference TYPE = TypeReference.findOrCreate(BaselineCompiledMethod.class);
442 int size = TYPE.peekType().asClass().getInstanceSize();
443 if (bytecodeMap != null) size += RVMArray.ByteArray.getInstanceSize(bytecodeMap.length);
444 if (eTable != null) size += RVMArray.IntArray.getInstanceSize(eTable.length);
445 if (referenceMaps != null) size += referenceMaps.size();
446 return size;
447 }
448 }