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 static org.jikesrvm.compilers.opt.ir.Operators.IG_PATCH_POINT;
016
017 import org.jikesrvm.ArchitectureSpecific;
018 import org.jikesrvm.ArchitectureSpecificOpt;
019 import org.jikesrvm.VM;
020 import org.jikesrvm.PrintLN;
021 import org.jikesrvm.classloader.RVMArray;
022 import org.jikesrvm.classloader.MemberReference;
023 import org.jikesrvm.classloader.RVMMethod;
024 import org.jikesrvm.classloader.NormalMethod;
025 import org.jikesrvm.classloader.RVMType;
026 import org.jikesrvm.classloader.TypeReference;
027 import org.jikesrvm.compilers.common.CompiledMethod;
028 import org.jikesrvm.compilers.common.ExceptionTable;
029 import org.jikesrvm.compilers.opt.ir.IR;
030 import org.jikesrvm.compilers.opt.ir.InlineGuard;
031 import org.jikesrvm.compilers.opt.ir.Instruction;
032 import org.jikesrvm.osr.EncodedOSRMap;
033 import org.jikesrvm.runtime.DynamicLink;
034 import org.jikesrvm.runtime.ExceptionDeliverer;
035 import org.jikesrvm.runtime.Magic;
036 import org.jikesrvm.runtime.Memory;
037 import org.jikesrvm.runtime.StackBrowser;
038 import org.jikesrvm.scheduler.RVMThread;
039 import org.vmmagic.pragma.Interruptible;
040 import org.vmmagic.pragma.Uninterruptible;
041 import org.vmmagic.pragma.Unpreemptible;
042 import org.vmmagic.unboxed.Offset;
043
044 /**
045 * An implementation of CompiledMethod for the OPT compiler.
046 *
047 * <p> NOTE: OptCompiledMethod live as long as their corresponding
048 * compiled machine code. Therefore, they should only contain
049 * state that is really required to be persistent. Anything
050 * transitory should be stored on the IR object.
051 */
052 @Uninterruptible
053 public final class OptCompiledMethod extends CompiledMethod {
054
055 public OptCompiledMethod(int id, RVMMethod m) {
056 super(id, m);
057 }
058
059 /**
060 * Get compiler that generated this method's machine code.
061 */
062 public int getCompilerType() {
063 return CompiledMethod.OPT;
064 }
065
066 /**
067 * @return Name of the compiler that produced this compiled method.
068 */
069 public String getCompilerName() {
070 return "optimizing compiler";
071 }
072
073 /**
074 * Get handler to deal with stack unwinding and exception delivery
075 * for this method's stackframes.
076 */
077 public ExceptionDeliverer getExceptionDeliverer() {
078 return exceptionDeliverer;
079 }
080
081 /**
082 * Find "catch" block for a machine instruction of this method.
083 */
084 @Unpreemptible
085 public int findCatchBlockForInstruction(Offset instructionOffset, RVMType exceptionType) {
086 if (eTable == null) {
087 return -1;
088 } else {
089 return ExceptionTable.findCatchBlockForInstruction(eTable, instructionOffset, exceptionType);
090 }
091 }
092
093 /**
094 * Fetch symbolic reference to a method that's called
095 * by one of this method's instructions.
096 * @param dynamicLink place to put return information
097 * @param instructionOffset offset of machine instruction that issued
098 * the call
099 */
100 public void getDynamicLink(DynamicLink dynamicLink, Offset instructionOffset) {
101 int bci = _mcMap.getBytecodeIndexForMCOffset(instructionOffset);
102 NormalMethod realMethod = _mcMap.getMethodForMCOffset(instructionOffset);
103 if (bci == -1 || realMethod == null) {
104 VM.sysFail("Mapping to source code location not available at Dynamic Linking point\n");
105 }
106 realMethod.getDynamicLink(dynamicLink, bci);
107 }
108
109 /**
110 * Return whether or not the instruction offset corresponds to an uninterruptible context.
111 *
112 * @param instructionOffset offset of addr from start of instructions in bytes
113 * @return true if the IP is within an Uninterruptible method, false otherwise.
114 */
115 @Interruptible
116 public boolean isWithinUninterruptibleCode(Offset instructionOffset) {
117 NormalMethod realMethod = _mcMap.getMethodForMCOffset(instructionOffset);
118 return realMethod.isUninterruptible();
119 }
120
121 /**
122 * Find source line number corresponding to one of this method's
123 * machine instructions.
124 */
125 public int findLineNumberForInstruction(Offset instructionOffset) {
126 int bci = _mcMap.getBytecodeIndexForMCOffset(instructionOffset);
127 if (bci < 0) {
128 return 0;
129 }
130 return ((NormalMethod) method).getLineNumberForBCIndex(bci);
131 }
132
133 /**
134 * Set the stack browser to the innermost logical stack frame of this method
135 */
136 @Interruptible
137 public void set(StackBrowser browser, Offset instr) {
138 OptMachineCodeMap map = getMCMap();
139 int iei = map.getInlineEncodingForMCOffset(instr);
140 if (iei >= 0) {
141 int[] inlineEncoding = map.inlineEncoding;
142 int mid = OptEncodedCallSiteTree.getMethodID(iei, inlineEncoding);
143
144 browser.setInlineEncodingIndex(iei);
145 browser.setBytecodeIndex(map.getBytecodeIndexForMCOffset(instr));
146 browser.setCompiledMethod(this);
147 browser.setMethod(MemberReference.getMemberRef(mid).asMethodReference().peekResolvedMethod());
148
149 if (VM.TraceStackTrace) {
150 VM.sysWrite("setting stack to frame (opt): ");
151 VM.sysWrite(browser.getMethod());
152 VM.sysWrite(browser.getBytecodeIndex());
153 VM.sysWrite("\n");
154 }
155 } else {
156 if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED);
157 }
158 }
159
160 /**
161 * Advance the StackBrowser up one internal stack frame, if possible
162 */
163 @Interruptible
164 public boolean up(StackBrowser browser) {
165 OptMachineCodeMap map = getMCMap();
166 int iei = browser.getInlineEncodingIndex();
167 int[] ie = map.inlineEncoding;
168 int next = OptEncodedCallSiteTree.getParent(iei, ie);
169 if (next >= 0) {
170 int mid = OptEncodedCallSiteTree.getMethodID(next, ie);
171 int bci = OptEncodedCallSiteTree.getByteCodeOffset(iei, ie);
172
173 browser.setInlineEncodingIndex(next);
174 browser.setBytecodeIndex(bci);
175 browser.setMethod(MemberReference.getMemberRef(mid).asMethodReference().peekResolvedMethod());
176
177 if (VM.TraceStackTrace) {
178 VM.sysWrite("up within frame stack (opt): ");
179 VM.sysWrite(browser.getMethod());
180 VM.sysWrite(browser.getBytecodeIndex());
181 VM.sysWrite("\n");
182 }
183
184 return true;
185 } else {
186 return false;
187 }
188 }
189
190 /**
191 * Print this compiled method's portion of a stack trace.
192 * @param instructionOffset The offset of machine instruction from
193 * start of method
194 * @param out The PrintStream to print the stack trace to.
195 */
196 @Interruptible
197 public void printStackTrace(Offset instructionOffset, PrintLN out) {
198 OptMachineCodeMap map = getMCMap();
199 int iei = map.getInlineEncodingForMCOffset(instructionOffset);
200 if (iei >= 0) {
201 int[] inlineEncoding = map.inlineEncoding;
202 int bci = map.getBytecodeIndexForMCOffset(instructionOffset);
203 for (int j = iei; j >= 0; j = OptEncodedCallSiteTree.getParent(j, inlineEncoding)) {
204 int mid = OptEncodedCallSiteTree.getMethodID(j, inlineEncoding);
205 NormalMethod m =
206 (NormalMethod) MemberReference.getMemberRef(mid).asMethodReference().peekResolvedMethod();
207 int lineNumber = m.getLineNumberForBCIndex(bci); // might be 0 if unavailable.
208 out.print("\tat ");
209 out.print(m.getDeclaringClass());
210 out.print('.');
211 out.print(m.getName());
212 out.print('(');
213 out.print(m.getDeclaringClass().getSourceName());
214 out.print(':');
215 out.print(lineNumber);
216 out.print(')');
217 out.println();
218 if (j > 0) {
219 bci = OptEncodedCallSiteTree.getByteCodeOffset(j, inlineEncoding);
220 }
221 }
222 } else {
223 out.print("\tat ");
224 out.print(method.getDeclaringClass());
225 out.print('.');
226 out.print(method.getName());
227 out.print('(');
228 out.print(method.getDeclaringClass().getSourceName());
229 out.print("; machine code offset: ");
230 out.printHex(instructionOffset.toInt());
231 out.print(')');
232 out.println();
233 }
234 }
235
236 @Interruptible
237 public int size() {
238 int size = TypeReference.ExceptionTable.peekType().asClass().getInstanceSize();
239 size += _mcMap.size();
240 if (eTable != null) size += RVMArray.IntArray.getInstanceSize(eTable.length);
241 if (patchMap != null) size += RVMArray.IntArray.getInstanceSize(patchMap.length);
242 return size;
243 }
244
245 //----------------//
246 // implementation //
247 //----------------//
248 private static final ArchitectureSpecificOpt.OptExceptionDeliverer exceptionDeliverer =
249 new ArchitectureSpecificOpt.OptExceptionDeliverer();
250
251 private EncodedOSRMap _osrMap;
252
253 @Interruptible
254 public void createFinalOSRMap(IR ir) {
255 this._osrMap = EncodedOSRMap.makeMap(ir.MIRInfo.osrVarMap);
256 }
257
258 public EncodedOSRMap getOSRMap() {
259 return this._osrMap;
260 }
261
262 //////////////////////////////////////
263 // Information the opt compiler needs to persistently associate
264 // with a particular compiled method.
265
266 /** The primary machine code maps */
267 private OptMachineCodeMap _mcMap;
268 /** The encoded exception tables (null if there are none) */
269 private int[] eTable;
270 private int[] patchMap;
271
272 /**
273 * unsigned offset (off the framepointer) of nonvolatile save area
274 * in bytes
275 */
276 private char nonvolatileOffset;
277 /**
278 * unsigned offset (off the framepointer) of caught exception
279 * object in bytes
280 */
281 private char exceptionObjectOffset;
282 /**
283 * size of the fixed portion of the stackframe
284 */
285 private char stackFrameFixedSize;
286 /**
287 * first saved nonvolatile integer register (-1 if no nonvolatile
288 * GPRs)
289 */
290 private byte firstNonvolatileGPR;
291 /**
292 * first saved nonvolatile floating point register (-1 if no
293 * nonvolatile FPRs)
294 */
295 private byte firstNonvolatileFPR;
296 /** opt level at which the method was compiled */
297 private byte optLevel;
298 /** were the volatile registers saved? */
299 private boolean volatilesSaved;
300 /** is the current method executing with instrumentation */
301 private boolean instrumented;
302
303 public int getUnsignedNonVolatileOffset() {
304 return nonvolatileOffset;
305 }
306
307 public int getUnsignedExceptionOffset() {
308 return exceptionObjectOffset;
309 }
310
311 public int getFirstNonVolatileGPR() {
312 return firstNonvolatileGPR;
313 }
314
315 public int getFirstNonVolatileFPR() {
316 return firstNonvolatileFPR;
317 }
318
319 public int getOptLevel() {
320 return optLevel;
321 }
322
323 public boolean isSaveVolatile() {
324 return volatilesSaved;
325 }
326
327 public boolean isInstrumentedMethod() {
328 return instrumented;
329 }
330
331 public int getFrameFixedSize() {
332 return stackFrameFixedSize;
333 }
334
335 public void setUnsignedNonVolatileOffset(int x) {
336 if (VM.VerifyAssertions) VM._assert(x >= 0 && x < 0xFFFF);
337 nonvolatileOffset = (char) x;
338 }
339
340 public void setUnsignedExceptionOffset(int x) {
341 if (VM.VerifyAssertions) VM._assert(x >= 0 && x < 0xFFFF);
342 exceptionObjectOffset = (char) x;
343 }
344
345 public void setFirstNonVolatileGPR(int x) {
346 if (VM.VerifyAssertions) VM._assert(x >= -1 && x < 0x7F);
347 firstNonvolatileGPR = (byte) x;
348 }
349
350 public void setFirstNonVolatileFPR(int x) {
351 if (VM.VerifyAssertions) VM._assert(x >= -1 && x < 0x7F);
352 firstNonvolatileFPR = (byte) x;
353 }
354
355 public void setOptLevel(int x) {
356 if (VM.VerifyAssertions) VM._assert(x >= 0 && x < 0x7F);
357 optLevel = (byte) x;
358 }
359
360 public void setSaveVolatile(boolean sv) {
361 volatilesSaved = sv;
362 }
363
364 public void setInstrumentedMethod(boolean _instrumented) {
365 instrumented = _instrumented;
366 }
367
368 public void setFrameFixedSize(int x) {
369 if (VM.VerifyAssertions) VM._assert(x >= 0 && x < 0xFFFF);
370 stackFrameFixedSize = (char) x;
371 }
372
373 /**
374 * Return the number of non-volatile GPRs used by this method.
375 */
376 public int getNumberOfNonvolatileGPRs() {
377 if (VM.BuildForPowerPC) {
378 return ArchitectureSpecific.RegisterConstants.NUM_GPRS - getFirstNonVolatileGPR();
379 } else if (VM.BuildForIA32) {
380 return ArchitectureSpecific.RegisterConstants.NUM_NONVOLATILE_GPRS - getFirstNonVolatileGPR();
381 } else if (VM.VerifyAssertions) {
382 VM._assert(VM.NOT_REACHED);
383 }
384 return -1;
385 }
386
387 /**
388 * Return the number of non-volatile FPRs used by this method.
389 */
390 public int getNumberOfNonvolatileFPRs() {
391 if (VM.BuildForPowerPC) {
392 return ArchitectureSpecific.RegisterConstants.NUM_FPRS - getFirstNonVolatileFPR();
393 } else if (VM.BuildForIA32) {
394 return ArchitectureSpecific.RegisterConstants.NUM_NONVOLATILE_FPRS - getFirstNonVolatileFPR();
395 } else if (VM.VerifyAssertions) {
396 VM._assert(VM.NOT_REACHED);
397 }
398 return -1;
399 }
400
401 /**
402 * Set the number of non-volatile GPRs used by this method.
403 */
404 public void setNumberOfNonvolatileGPRs(short n) {
405 if (VM.BuildForPowerPC) {
406 setFirstNonVolatileGPR(ArchitectureSpecific.RegisterConstants.NUM_GPRS - n);
407 } else if (VM.BuildForIA32) {
408 setFirstNonVolatileGPR(ArchitectureSpecific.RegisterConstants.NUM_NONVOLATILE_GPRS - n);
409 } else if (VM.VerifyAssertions) {
410 VM._assert(VM.NOT_REACHED);
411 }
412 }
413
414 /**
415 * Set the number of non-volatile FPRs used by this method.
416 */
417 public void setNumberOfNonvolatileFPRs(short n) {
418 if (VM.BuildForPowerPC) {
419 setFirstNonVolatileFPR(ArchitectureSpecific.RegisterConstants.NUM_FPRS - n);
420 } else if (VM.BuildForIA32) {
421 setFirstNonVolatileFPR(ArchitectureSpecific.RegisterConstants.NUM_NONVOLATILE_FPRS - n);
422 } else if (VM.VerifyAssertions) {
423 VM._assert(VM.NOT_REACHED);
424 }
425 }
426
427 /**
428 * Print the eTable
429 */
430 @Interruptible
431 public void printExceptionTable() {
432 if (eTable != null) ExceptionTable.printExceptionTable(eTable);
433 }
434
435 /**
436 * @return the machine code map for the compiled method.
437 */
438 public OptMachineCodeMap getMCMap() {
439 return _mcMap;
440 }
441
442 /**
443 * Create the final machine code map for the compiled method.
444 * Remember the offset for the end of prologue too for debugger.
445 * @param ir the ir
446 * @param machineCodeLength the number of machine code instructions.
447 */
448 @Interruptible
449 public void createFinalMCMap(IR ir, int machineCodeLength) {
450 _mcMap = OptMachineCodeMap.create(ir, machineCodeLength);
451 }
452
453 /**
454 * Create the final exception table from the IR for the method.
455 * @param ir the ir
456 */
457 @Interruptible
458 public void createFinalExceptionTable(IR ir) {
459 if (ir.hasReachableExceptionHandlers()) {
460 eTable = OptExceptionTable.encode(ir);
461 }
462 }
463
464 /**
465 * Create the code patching maps from the IR for the method
466 * @param ir the ir
467 */
468 @Interruptible
469 public void createCodePatchMaps(IR ir) {
470 // (1) count the patch points
471 int patchPoints = 0;
472 for (Instruction s = ir.firstInstructionInCodeOrder(); s != null; s = s.nextInstructionInCodeOrder()) {
473 if (s.operator() == IG_PATCH_POINT) {
474 patchPoints++;
475 }
476 }
477 // (2) if we have patch points, create the map.
478 if (patchPoints != 0) {
479 patchMap = new int[patchPoints * 2];
480 int idx = 0;
481 for (Instruction s = ir.firstInstructionInCodeOrder(); s != null; s = s.nextInstructionInCodeOrder()) {
482 if (s.operator() == IG_PATCH_POINT) {
483 int patchPoint = s.getmcOffset();
484 int newTarget = InlineGuard.getTarget(s).target.getmcOffset();
485 // A patch map is the offset of the last byte of the patch point
486 // and the new branch immediate to lay down if the code is ever patched.
487 if (VM.BuildForIA32) {
488 patchMap[idx++] = patchPoint - 1;
489 patchMap[idx++] = newTarget - patchPoint;
490 } else if (VM.BuildForPowerPC) {
491
492 // otherwise, it must be RFOR_POWERPC
493 /* since currently we use only one NOP scheme, the offset
494 * is adjusted for one word
495 */
496 patchMap[idx++] = (patchPoint >> ArchitectureSpecific.RegisterConstants.LG_INSTRUCTION_WIDTH) - 1;
497 patchMap[idx++] =
498 (newTarget - patchPoint + (1 << ArchitectureSpecific.RegisterConstants.LG_INSTRUCTION_WIDTH));
499 } else if (VM.VerifyAssertions) {
500 VM._assert(VM.NOT_REACHED);
501 }
502 }
503 }
504 }
505 }
506
507 /**
508 * Apply the code patches to the INSTRUCTION array of cm
509 */
510 @Interruptible
511 public void applyCodePatches(CompiledMethod cm) {
512 if (patchMap != null) {
513 for (int idx = 0; idx < patchMap.length; idx += 2) {
514 ArchitectureSpecific.CodeArray code = cm.codeArrayForOffset(Offset.fromIntZeroExtend(patchMap[idx]));
515 if (VM.BuildForIA32) {
516 ArchitectureSpecific.Assembler.patchCode(code, patchMap[idx], patchMap[idx + 1]);
517 } else if (VM.BuildForPowerPC) {
518 ArchitectureSpecificOpt.AssemblerOpt.patchCode(code, patchMap[idx], patchMap[idx + 1]);
519 } else if (VM.VerifyAssertions) {
520 VM._assert(VM.NOT_REACHED);
521 }
522 }
523
524 if (VM.BuildForPowerPC) {
525 /* we need synchronization on PPC to handle the weak memory model
526 * and its icache/dcache synchronization requriements.
527 * before the class loading finish, other processor should get
528 * synchronized.
529 */
530 boolean DEBUG_CODE_PATCH = false;
531
532 // let other processors see changes; although really physical processors
533 // need synchronization, we set each virtual processor to execute
534 // isync at thread switch point.
535 Magic.sync();
536
537 // All other processors now will see the patched code in their data cache.
538 // We now need to force everyone's instruction caches to be in synch with their
539 // data caches. Some of the work of this call is redundant (since we already have
540 // forced the data caches to be in synch), but we need the icbi instructions
541 Memory.sync(Magic.objectAsAddress(instructions),
542 instructions.length() << ArchitectureSpecific.RegisterConstants.LG_INSTRUCTION_WIDTH);
543 RVMThread.softHandshake(codePatchSyncRequestVisitor);
544
545 if (DEBUG_CODE_PATCH) {
546 VM.sysWrite("all processors get synchronized!\n");
547 }
548 }
549
550 }
551 }
552
553 private static RVMThread.SoftHandshakeVisitor codePatchSyncRequestVisitor =
554 new RVMThread.SoftHandshakeVisitor() {
555 @Uninterruptible
556 public boolean checkAndSignal(RVMThread t) {
557 t.codePatchSyncRequested = true;
558 return true; // handshake with everyone but ourselves.
559 }
560 };
561 }