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.osr.ia32;
014
015 import org.jikesrvm.VM;
016 import org.jikesrvm.Constants;
017 import org.jikesrvm.classloader.MemberReference;
018 import org.jikesrvm.classloader.MethodReference;
019 import org.jikesrvm.classloader.NormalMethod;
020 import org.jikesrvm.compilers.common.CompiledMethod;
021 import org.jikesrvm.compilers.common.CompiledMethods;
022 import org.jikesrvm.compilers.opt.regalloc.ia32.PhysicalRegisterConstants;
023 import org.jikesrvm.compilers.opt.runtimesupport.OptCompiledMethod;
024 import org.jikesrvm.ia32.ArchConstants;
025 import org.jikesrvm.osr.OSRConstants;
026 import org.jikesrvm.osr.EncodedOSRMap;
027 import org.jikesrvm.osr.ExecutionStateExtractor;
028 import org.jikesrvm.osr.ExecutionState;
029 import org.jikesrvm.osr.OSRMapIterator;
030 import org.jikesrvm.osr.VariableElement;
031 import org.jikesrvm.runtime.Magic;
032 import org.jikesrvm.runtime.RuntimeEntrypoints;
033 import org.jikesrvm.scheduler.RVMThread;
034 import org.vmmagic.unboxed.Address;
035 import org.vmmagic.unboxed.Offset;
036 import org.vmmagic.unboxed.Word;
037 import org.vmmagic.unboxed.WordArray;
038
039 /**
040 * OptExecutionStateExtractor is a subclass of ExecutionStateExtractor.
041 * It extracts the execution state from an optimized activation.
042 */
043 public abstract class OptExecutionStateExtractor extends ExecutionStateExtractor
044 implements Constants, ArchConstants, OSRConstants, PhysicalRegisterConstants {
045
046 public ExecutionState extractState(RVMThread thread, Offset osrFPoff, Offset methFPoff, int cmid) {
047
048 /* perform machine and compiler dependent operations here
049 * osrFPoff is the fp offset of
050 * OptSaveVolatile.threadSwithFrom<...>
051 *
052 * (stack grows downward)
053 * foo
054 * |-> <-- methFPoff
055 * |
056 * | <tsfrom>
057 * |-- <-- osrFPoff
058 *
059 *
060 * The threadSwitchFrom method saves all volatiles, nonvolatiles, and
061 * scratch registers. All register values for 'foo' can be obtained
062 * from the register save area of '<tsfrom>' method.
063 */
064
065 byte[] stack = thread.getStack();
066
067 // get registers for the caller ( real method )
068 TempRegisters registers = new TempRegisters(thread.contextRegisters);
069
070 if (VM.VerifyAssertions) {
071 int foocmid = Magic.getIntAtOffset(stack, methFPoff.plus(STACKFRAME_METHOD_ID_OFFSET));
072 if (foocmid != cmid) {
073 CompiledMethod cm = CompiledMethods.getCompiledMethod(cmid);
074 VM.sysWriteln("unmatch method, it should be " + cm.getMethod());
075 CompiledMethod foo = CompiledMethods.getCompiledMethod(foocmid);
076 VM.sysWriteln("but now it is " + foo.getMethod());
077 walkOnStack(stack, osrFPoff);
078 }
079 VM._assert(foocmid == cmid);
080 }
081
082 OptCompiledMethod fooCM = (OptCompiledMethod) CompiledMethods.getCompiledMethod(cmid);
083
084 /* Following code get the machine code offset to the
085 * next instruction. All operation of the stack frame
086 * are kept in GC critical section.
087 * All code in the section should not cause any GC
088 * activities, and avoid lazy compilation.
089 */
090
091 /* Following code is architecture dependent. In IA32, the return address
092 * saved in caller stack frames, so use osrFP to get the next instruction
093 * address of foo
094 */
095
096 // get the next machine code offset of the real method
097 VM.disableGC();
098 Address osrFP = Magic.objectAsAddress(stack).plus(osrFPoff);
099 Address nextIP = Magic.getReturnAddress(osrFP);
100 Offset ipOffset = fooCM.getInstructionOffset(nextIP);
101 VM.enableGC();
102
103 EncodedOSRMap fooOSRMap = fooCM.getOSRMap();
104
105 /* get register reference map from OSR map
106 * we are using this map to convert addresses to objects,
107 * thus we can operate objects out of GC section.
108 */
109 int regmap = fooOSRMap.getRegisterMapForMCOffset(ipOffset);
110
111 {
112 int bufCMID = Magic.getIntAtOffset(stack, osrFPoff.plus(STACKFRAME_METHOD_ID_OFFSET));
113 CompiledMethod bufCM = CompiledMethods.getCompiledMethod(bufCMID);
114
115 // offset in bytes, convert it to stack words from fpIndex
116 // SaveVolatile can only be compiled by OPT compiler
117 if (VM.VerifyAssertions) VM._assert(bufCM instanceof OptCompiledMethod);
118 restoreValuesFromOptSaveVolatile(stack, osrFPoff, registers, regmap, bufCM);
119 }
120
121 // return a list of states: from caller to callee
122 // if the osr happens in an inlined method, the state is
123 // a chain of recoverd methods.
124 ExecutionState state =
125 getExecStateSequence(thread, stack, ipOffset, methFPoff, cmid, osrFPoff, registers, fooOSRMap);
126
127 // reverse callerState points, it becomes callee -> caller
128 ExecutionState prevState = null;
129 ExecutionState nextState = state;
130 while (nextState != null) {
131 // 1. current node
132 state = nextState;
133 // 1. hold the next state first
134 nextState = nextState.callerState;
135 // 2. redirect pointer
136 state.callerState = prevState;
137 // 3. move prev to current
138 prevState = state;
139 }
140
141 if (VM.TraceOnStackReplacement) {
142 VM.sysWriteln("OptExecState : recovered states " + thread.toString());
143 ExecutionState temp = state;
144 do {
145 VM.sysWriteln(temp.toString());
146 temp = temp.callerState;
147 } while (temp != null);
148 }
149
150 return state;
151 }
152
153 /* OptSaveVolatile has different stack layout from DynamicBridge
154 * Have to separately recover them now, but there should be unified
155 * later on.
156 *
157 * |----------|
158 * | NON |
159 * |Volatiles |
160 * | | <-- volatile offset
161 * |Volatiles |
162 * | |
163 * |FPR states|
164 * |__________| ___ FP
165 */
166 private void restoreValuesFromOptSaveVolatile(byte[] stack, Offset osrFPoff, TempRegisters registers, int regmap,
167 CompiledMethod cm) {
168
169 OptCompiledMethod tsfromCM = (OptCompiledMethod) cm;
170
171 boolean saveVolatile = tsfromCM.isSaveVolatile();
172 if (VM.VerifyAssertions) {
173 VM._assert(saveVolatile);
174 }
175
176 WordArray gprs = registers.gprs;
177
178 // enter critical section
179 // precall methods potientially causing dynamic compilation
180 int firstNonVolatile = tsfromCM.getFirstNonVolatileGPR();
181 int nonVolatiles = tsfromCM.getNumberOfNonvolatileGPRs();
182 int nonVolatileOffset = tsfromCM.getUnsignedNonVolatileOffset() + (nonVolatiles - 1) * BYTES_IN_STACKSLOT;
183
184 VM.disableGC();
185
186 // recover nonvolatile GPRs
187 for (int i = firstNonVolatile + nonVolatiles - 1; i >= firstNonVolatile; i--) {
188 gprs.set(NONVOLATILE_GPRS[i].value(), Magic.objectAsAddress(stack).loadWord(osrFPoff.minus(nonVolatileOffset)));
189 nonVolatileOffset -= BYTES_IN_STACKSLOT;
190 }
191
192 // restore with VOLATILES yet
193 int volatileOffset = nonVolatileOffset;
194 for (int i = NUM_VOLATILE_GPRS - 1; i >= 0; i--) {
195 gprs.set(VOLATILE_GPRS[i].value(), Magic.objectAsAddress(stack).loadWord(osrFPoff.minus(volatileOffset)));
196 volatileOffset -= BYTES_IN_STACKSLOT;
197 }
198
199 // currently, all FPRS are volatile on intel,
200 // DO nothing.
201
202 // convert addresses in registers to references, starting from register 0
203 // powerPC starts from register 1
204 for (int i = 0; i < NUM_GPRS; i++) {
205 if (EncodedOSRMap.registerIsSet(regmap, i)) {
206 registers.objs[i] = Magic.addressAsObject(registers.gprs.get(i).toAddress());
207 }
208 }
209
210 VM.enableGC();
211
212 if (VM.TraceOnStackReplacement) {
213 for (GPR reg : GPR.values()) {
214 VM.sysWrite(reg.toString());
215 VM.sysWrite(" = ");
216 VM.sysWrite(registers.gprs.get(reg.value()).toAddress());
217 VM.sysWrite("\n");
218 }
219 }
220 }
221
222 private ExecutionState getExecStateSequence(RVMThread thread, byte[] stack, Offset ipOffset, Offset fpOffset,
223 int cmid, Offset tsFPOffset, TempRegisters registers,
224 EncodedOSRMap osrmap) {
225
226 // go through the stack frame and extract values
227 // In the variable value list, we keep the order as follows:
228 // L0, L1, ..., S0, S1, ....
229
230 /* go over osr map element, build list of VariableElement.
231 * assuming iterator has ordered element as
232 * L0, L1, ..., S0, S1, ...
233 *
234 * ThreadSwitch
235 * threadSwitchFromOsr
236 * FOO <-- fpOffset
237 *
238 * Also, all registers saved by threadSwitchFromDeopt method
239 * is restored in "registers", address for object is converted
240 * back to object references.
241 *
242 * This method should be called in non-GC critical section since
243 * it allocates many objects.
244 */
245
246 // for 64-bit type values which have two int parts.
247 // this holds the high part.
248 int lvalue_one = 0;
249 int lvtype_one = 0;
250
251 // now recover execution states
252 OSRMapIterator iterator = osrmap.getOsrMapIteratorForMCOffset(ipOffset);
253 if (VM.VerifyAssertions) VM._assert(iterator != null);
254
255 ExecutionState state = new ExecutionState(thread, fpOffset, cmid, iterator.getBcIndex(), tsFPOffset);
256 MethodReference mref = MemberReference.getMemberRef(iterator.getMethodId()).asMethodReference();
257 state.setMethod((NormalMethod) mref.peekResolvedMethod());
258 // this is not caller, but the callee, reverse it when outside
259 // of this function.
260 state.callerState = null;
261
262 if (VM.TraceOnStackReplacement) {
263 VM.sysWriteln("osr map table of " + state.meth.toString());
264 }
265
266 while (iterator.hasMore()) {
267
268 if (iterator.getMethodId() != state.meth.getId()) {
269 ExecutionState newstate = new ExecutionState(thread, fpOffset, cmid, iterator.getBcIndex(), tsFPOffset);
270 mref = MemberReference.getMemberRef(iterator.getMethodId()).asMethodReference();
271 newstate.setMethod((NormalMethod) mref.peekResolvedMethod());
272 // this is not caller, but the callee, reverse it when outside
273 // of this function.
274 newstate.callerState = state;
275
276 state = newstate;
277
278 if (VM.TraceOnStackReplacement) {
279 VM.sysWriteln("osr map table of " + state.meth.toString());
280 }
281
282 }
283
284 // create a VariableElement for it.
285 boolean kind = iterator.getKind();
286 char num = iterator.getNumber();
287 byte tcode = iterator.getTypeCode();
288 byte vtype = iterator.getValueType();
289 int value = iterator.getValue();
290
291 iterator.moveToNext();
292
293 if (VM.TraceOnStackReplacement) {
294 VM.sysWrite((kind == LOCAL) ? "L" : "S");
295 VM.sysWrite((int)num);
296 VM.sysWrite(" , ");
297 if (vtype == ICONST) {
298 VM.sysWrite("ICONST ");
299 VM.sysWrite(value);
300 } else if (vtype == PHYREG) {
301 VM.sysWrite("PHYREG ");
302 VM.sysWrite(GPR.lookup(value).toString());
303 } else if (vtype == SPILL) {
304 VM.sysWrite("SPILL ");
305 VM.sysWrite(value);
306 }
307 VM.sysWriteln();
308 }
309
310 switch (tcode) {
311 case INT: {
312 int ibits = getIntBitsFrom(vtype, value, stack, fpOffset, registers);
313 state.add(new VariableElement(kind, num, tcode, ibits));
314 break;
315 }
316 case FLOAT: {
317 float fv = (float) getDoubleFrom(vtype, value, stack, fpOffset, registers);
318 int ibits = Magic.floatAsIntBits(fv);
319 state.add(new VariableElement(kind, num, tcode, ibits));
320 break;
321 }
322 case HIGH_64BIT: {
323 lvalue_one = value;
324 lvtype_one = vtype;
325 break;
326 }
327 case LONG: {
328 long lbits = getLongBitsFrom(lvtype_one, lvalue_one, vtype, value, stack, fpOffset, registers);
329 lvalue_one = 0;
330 lvtype_one = 0;
331 state.add(new VariableElement(kind, num, LONG, lbits));
332
333 break;
334 }
335 case DOUBLE: {
336 double dv = getDoubleFrom(vtype, value, stack, fpOffset, registers);
337 long lbits = Magic.doubleAsLongBits(dv);
338 state.add(new VariableElement(kind, num, tcode, lbits));
339 break;
340 }
341 // I believe I did not handle return address correctly because
342 // the opt compiler did inlining of JSR/RET.
343 // To be VERIFIED.
344 case RET_ADDR: {
345 int bcIndex = getIntBitsFrom(vtype, value, stack, fpOffset, registers);
346 state.add(new VariableElement(kind, num, tcode, bcIndex));
347 break;
348 }
349 case WORD: { //KV:TODO
350 if (VM.BuildFor64Addr) VM._assert(VM.NOT_REACHED);
351 int word = getIntBitsFrom(vtype, value, stack, fpOffset, registers);
352
353 state.add(new VariableElement(kind, num, tcode, word));
354 break;
355 }
356 case REF: {
357 Object ref = getObjectFrom(vtype, value, stack, fpOffset, registers);
358
359 state.add(new VariableElement(kind, num, tcode, ref));
360 break;
361 }
362 default:
363 if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED);
364 break;
365 } // switch
366 } // for loop
367
368 return state;
369 }
370
371 /** auxillary functions to get value from different places. */
372 private static int getIntBitsFrom(int vtype, int value, byte[] stack, Offset fpOffset, TempRegisters registers) {
373 // for INT_CONST type, the value is the value
374 if (vtype == ICONST || vtype == ACONST) {
375 return value;
376
377 // for physical register type, it is the register number
378 // because all registers are saved in threadswitch's stack
379 // frame, we get value from it.
380 } else if (vtype == PHYREG) {
381 return registers.gprs.get(value).toInt();
382
383 // for spilled locals, the value is the spilled position
384 // it is on FOO's stackframe.
385 // ASSUMING, spill offset is offset to FP in bytes.
386 } else if (vtype == SPILL) {
387
388 return Magic.getIntAtOffset(stack, fpOffset.minus(value));
389
390 } else {
391 if (VM.VerifyAssertions) VM._assert(NOT_REACHED);
392 return -1;
393 }
394 }
395
396 private static long getLongBitsFrom(int vtypeHigh, int valueHigh, int vtypeLow, int valueLow, byte[] stack, Offset fpOffset,
397 TempRegisters registers) {
398
399 // for LCONST type, the value is the value
400 if (vtypeLow == LCONST || vtypeLow == ACONST) {
401 if (VM.VerifyAssertions) VM._assert(vtypeHigh == vtypeLow);
402 return ((((long) valueHigh) << 32) | (((long) valueLow) & 0x0FFFFFFFFL));
403
404 } else if (VM.BuildFor32Addr) {
405 if (VM.VerifyAssertions) VM._assert(vtypeHigh == PHYREG || vtypeHigh == SPILL);
406 if (VM.VerifyAssertions) VM._assert(vtypeLow == PHYREG || vtypeLow == SPILL);
407 /* For physical registers, value is the register number.
408 * For spilled locals, the value is the spilled position on FOO's stackframe. */
409 long lowPart, highPart;
410
411 if (vtypeLow == PHYREG) {
412 lowPart = ((long)registers.gprs.get(valueLow).toInt()) & 0x0FFFFFFFFL;
413 } else {
414 lowPart = ((long)Magic.getIntAtOffset(stack, fpOffset.minus(valueLow))) & 0x0FFFFFFFFL;
415 }
416
417 if (vtypeHigh == PHYREG) {
418 highPart = ((long)registers.gprs.get(valueHigh).toInt());
419 } else {
420 highPart = ((long)Magic.getIntAtOffset(stack, fpOffset.minus(valueHigh)));
421 }
422
423 return (highPart << 32) | lowPart;
424 } else if (VM.BuildFor64Addr) {
425 // for physical register type, it is the register number
426 // because all registers are saved in threadswitch's stack
427 // frame, we get value from it.
428 if (vtypeLow == PHYREG) {
429 return registers.gprs.get(valueLow).toLong();
430
431 // for spilled locals, the value is the spilled position
432 // it is on FOO's stackframe.
433 // ASSUMING, spill offset is offset to FP in bytes.
434 } else if (vtypeLow == SPILL) {
435 return Magic.getLongAtOffset(stack, fpOffset.minus(valueLow));
436 }
437 }
438 if (VM.VerifyAssertions) VM._assert(NOT_REACHED);
439 return -1L;
440 }
441
442 private static double getDoubleFrom(int vtype, int value, byte[] stack, Offset fpOffset,
443 TempRegisters registers) {
444 if (vtype == PHYREG) {
445 return registers.fprs[value - FIRST_DOUBLE];
446
447 } else if (vtype == SPILL) {
448
449 long lbits = Magic.getLongAtOffset(stack, fpOffset.minus(value));
450 return Magic.longBitsAsDouble(lbits);
451 //KV:TODO: why not use getDoubleAtOffset ???
452
453 } else {
454 if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED);
455 return -1.0;
456 }
457 }
458
459 private static Object getObjectFrom(int vtype, int value, byte[] stack, Offset fpOffset,
460 TempRegisters registers) {
461 if (vtype == ICONST) { //kv:todo : to become ACONST
462 // the only constant object for 64bit addressing is NULL
463 if (VM.VerifyAssertions) VM._assert(VM.BuildFor32Addr || value == 0);
464 return Magic.addressAsObject(Address.fromIntSignExtend(value));
465
466 } else if (vtype == PHYREG) {
467 return registers.objs[value];
468
469 } else if (vtype == SPILL) {
470 return Magic.getObjectAtOffset(stack, fpOffset.minus(value));
471
472 } else {
473 VM.sysWrite("fatal error : ( vtype = " + vtype + " )\n");
474 if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED);
475 return null;
476 }
477 }
478
479 @SuppressWarnings("unused")
480 private static void dumpStackContent(byte[] stack, Offset fpOffset) {
481 int cmid = Magic.getIntAtOffset(stack, fpOffset.plus(STACKFRAME_METHOD_ID_OFFSET));
482 OptCompiledMethod cm = (OptCompiledMethod) CompiledMethods.getCompiledMethod(cmid);
483
484 int firstNonVolatile = cm.getFirstNonVolatileGPR();
485 int nonVolatiles = cm.getNumberOfNonvolatileGPRs();
486 int nonVolatileOffset = cm.getUnsignedNonVolatileOffset() + (nonVolatiles - 1) * BYTES_IN_STACKSLOT;
487
488 VM.sysWriteln("stack of " + cm.getMethod());
489 VM.sysWriteln(" fp offset ", fpOffset);
490 VM.sysWriteln(" NV area offset ", nonVolatileOffset);
491 VM.sysWriteln(" first NV GPR ", firstNonVolatile);
492
493 Address aFP = Magic.objectAsAddress(stack).plus(fpOffset);
494 for (Address a = aFP.plus(nonVolatileOffset); a.GE(aFP); a = a.minus(BYTES_IN_STACKSLOT)) {
495 Word content = a.loadWord();
496 VM.sysWriteHex(a);
497 VM.sysWrite(" ");
498 VM.sysWrite(content);
499 VM.sysWriteln();
500 }
501 }
502
503 @SuppressWarnings("unused")
504 private static void dumpRegisterContent(WordArray gprs) {
505 for (GPR reg : GPR.values()) {
506 VM.sysWrite(reg.toString());
507 VM.sysWrite(" = ");
508 VM.sysWriteln(gprs.get(reg.value()));
509 }
510 }
511
512 /* walk on stack frame, print out methods
513 */
514 private static void walkOnStack(byte[] stack, Offset fpOffset) {
515 VM.disableGC();
516
517 Address fp = Magic.objectAsAddress(stack).plus(fpOffset);
518
519 while (Magic.getCallerFramePointer(fp).NE(STACKFRAME_SENTINEL_FP)) {
520 int cmid = Magic.getCompiledMethodID(fp);
521
522 if (cmid == INVISIBLE_METHOD_ID) {
523 VM.sysWriteln(" invisible method ");
524 } else {
525 CompiledMethod cm = CompiledMethods.getCompiledMethod(cmid);
526 fpOffset = fp.diff(Magic.objectAsAddress(stack));
527 VM.enableGC();
528
529 VM.sysWriteln(cm.getMethod().toString());
530
531 VM.disableGC();
532 fp = Magic.objectAsAddress(stack).plus(fpOffset);
533 if (cm.getMethod().getDeclaringClass().hasBridgeFromNativeAnnotation()) {
534 fp = RuntimeEntrypoints.unwindNativeStackFrame(fp);
535 }
536 }
537
538 fp = Magic.getCallerFramePointer(fp);
539 }
540
541 VM.enableGC();
542 }
543 }