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.NormalMethod;
018 import org.jikesrvm.compilers.baseline.BaselineCompiledMethod;
019 import org.jikesrvm.compilers.baseline.ia32.BaselineCompilerImpl;
020 import org.jikesrvm.compilers.common.CompiledMethods;
021 import org.jikesrvm.compilers.opt.regalloc.ia32.PhysicalRegisterConstants;
022 import org.jikesrvm.ia32.ArchConstants;
023 import org.jikesrvm.osr.BytecodeTraverser;
024 import org.jikesrvm.osr.OSRConstants;
025 import org.jikesrvm.osr.ExecutionStateExtractor;
026 import org.jikesrvm.osr.ExecutionState;
027 import org.jikesrvm.osr.VariableElement;
028 import org.jikesrvm.runtime.Magic;
029 import org.jikesrvm.scheduler.RVMThread;
030 import org.vmmagic.unboxed.Address;
031 import org.vmmagic.unboxed.Offset;
032 import org.vmmagic.unboxed.Word;
033
034 /**
035 * OSR_BaselineExecStateExtractor retrieves the VM scope descriptor
036 * from a suspended thread whose top method was compiled by the
037 * baseline compiler.
038 */
039
040 public abstract class BaselineExecutionStateExtractor extends ExecutionStateExtractor
041 implements Constants, ArchConstants, OSRConstants, PhysicalRegisterConstants {
042
043 /**
044 * Implements ExecutionStateExtractor.extractState.
045 *
046 * @param thread : the suspended thread, the registers and stack frames are used.
047 * @param osrFPoff : the osr method's stack frame offset
048 * @param methFPoff : the real method's stack frame offset
049 * @param cmid : the top application method ( system calls are unwounded ).
050 *
051 * return a ExecutionStateExtractor object.
052 */
053 public ExecutionState extractState(RVMThread thread, Offset osrFPoff, Offset methFPoff, int cmid) {
054
055 /* performs architecture and compiler dependent operations here
056 *
057 * When a thread is hung called from baseline compiled code,
058 * the hierarchy of calls on stack looks like follows
059 * ( starting from FP in the FP register ):
060 *
061 * morph
062 * yield
063 * threadSwitch
064 * threadSwitchFrom[Prologue|Backedge|Epilong]
065 * foo ( real method ).
066 *
067 * The returned ExecutionState should have following
068 *
069 * current thread
070 * compiled method ID of "foo"
071 * fp of foo's stack frame
072 * bytecode index of foo's next instruction
073 * the list of variable,value of foo at that point
074 * which method (foo)
075 */
076
077 if (VM.TraceOnStackReplacement) {
078 VM.sysWriteln("BASE execStateExtractor starting ...");
079 }
080
081 byte[] stack = thread.getStack();
082
083 if (VM.VerifyAssertions) {
084 int fooCmid = Magic.getIntAtOffset(stack, methFPoff.plus(STACKFRAME_METHOD_ID_OFFSET));
085
086 if (VM.TraceOnStackReplacement) {
087 VM.sysWriteln("fooCmid = " + fooCmid);
088 VM.sysWriteln(" cmid = " + cmid);
089 }
090
091 VM._assert(fooCmid == cmid);
092 }
093
094 BaselineCompiledMethod fooCM = (BaselineCompiledMethod) CompiledMethods.getCompiledMethod(cmid);
095
096 NormalMethod fooM = (NormalMethod) fooCM.getMethod();
097
098 VM.disableGC();
099 Address rowIP = Magic.objectAsAddress(stack).loadAddress(osrFPoff.plus(STACKFRAME_RETURN_ADDRESS_OFFSET));
100 Offset ipOffset = fooCM.getInstructionOffset(rowIP);
101 VM.enableGC();
102
103 // CAUTION: IP Offset should point to next instruction
104 int bcIndex = fooCM.findBytecodeIndexForInstruction(ipOffset.plus(INSTRUCTION_WIDTH));
105
106 // assertions
107 if (VM.VerifyAssertions) {
108 if (bcIndex == -1) {
109
110 VM.sysWriteln("osrFPoff = ", osrFPoff);
111 VM.sysWriteln("instr_beg = ", Magic.objectAsAddress(fooCM.getEntryCodeArray()));
112
113 for (int i = (osrFPoff.toInt()) - 10; i < (osrFPoff.toInt()) + 10; i++) {
114 VM.sysWriteln(" stack[" + i + "] = " + stack[i]);
115 }
116
117 Offset ipIndex = ipOffset.toWord().rsha(LG_INSTRUCTION_WIDTH).toOffset();
118 VM.sysWriteln("ipIndex : ", ipIndex);
119 VM.sysWriteln("bcIndex : " + bcIndex);
120 }
121 VM._assert(bcIndex != -1);
122 }
123
124 // create execution state object
125 ExecutionState state = new ExecutionState(thread, methFPoff, cmid, bcIndex, osrFPoff);
126
127 /* extract values for local and stack, but first of all
128 * we need to get type information for current PC.
129 */
130 BytecodeTraverser typer = new BytecodeTraverser();
131 typer.computeLocalStackTypes(fooM, bcIndex);
132 byte[] localTypes = typer.getLocalTypes();
133 byte[] stackTypes = typer.getStackTypes();
134
135 if (VM.TraceOnStackReplacement) {
136 VM.sysWrite("BC Index : " + bcIndex + "\n");
137 VM.sysWrite("Local Types :");
138 for (byte localType : localTypes) {
139 VM.sysWrite(" " + (char) localType);
140 }
141 VM.sysWrite("\nStack Types :");
142 for (byte stackType : stackTypes) {
143 VM.sysWrite(" " + (char) stackType);
144 }
145 VM.sysWrite("\n");
146 }
147
148 // consult GC reference map again since the type matcher does not complete
149 // the flow analysis, it can not distinguish reference or non-reference
150 // type. We should remove non-reference type
151 for (int i = 0, n = localTypes.length; i < n; i++) {
152 // if typer reports a local is reference type, but the GC map says no
153 // then set the localType to uninitialized, see VM spec, bytecode verifier
154 if (localTypes[i] == ClassTypeCode) {
155 if (!fooCM.referenceMaps.isLocalRefType(fooM, ipOffset.plus(1 << LG_INSTRUCTION_WIDTH), i)) {
156 localTypes[i] = VoidTypeCode;
157 if (VM.TraceOnStackReplacement) {
158 VM.sysWriteln("GC maps disagrees with type matcher at " + i + "th local\n");
159 }
160 }
161 }
162 }
163
164 // go through the stack frame and extract values
165 // In the variable value list, we keep the order as follows:
166 // L0, L1, ..., S0, S1, ....
167
168 // adjust local offset and stack offset
169 // NOTE: do not call BaselineCompilerImpl.getFirstLocalOffset(method)
170 Offset startLocalOffset = methFPoff.plus(BaselineCompilerImpl.locationToOffset(fooCM.getGeneralLocalLocation(0)));
171
172 Offset stackOffset = methFPoff.plus(fooCM.getEmptyStackOffset());
173
174 // for locals
175 getVariableValue(stack, startLocalOffset, localTypes, fooCM, LOCAL, state);
176
177 // for stacks
178 getVariableValue(stack, stackOffset, stackTypes, fooCM, STACK, state);
179
180 if (VM.TraceOnStackReplacement) {
181 state.printState();
182 }
183
184 if (VM.TraceOnStackReplacement) {
185 VM.sysWriteln("BASE executionStateExtractor done ");
186 }
187 return state;
188 }
189
190 /* go over local/stack array, and build VariableElement. */
191 private static void getVariableValue(byte[] stack, Offset offset, byte[] types,
192 BaselineCompiledMethod compiledMethod, boolean kind, ExecutionState state) {
193 int size = types.length;
194 Offset vOffset = offset;
195 for (int i = 0; i < size; i++) {
196 if (VM.TraceOnStackReplacement) {
197 Word content = Magic.getWordAtOffset(stack, vOffset.minus(BYTES_IN_ADDRESS));
198 VM.sysWrite("0x", vOffset.minus(BYTES_IN_ADDRESS), " 0x");
199 VM.sysWriteln(content);
200 if ((types[i] == LongTypeCode) || (types[i] == DoubleTypeCode)) {
201 content = Magic.getWordAtOffset(stack, vOffset.minus(2 * BYTES_IN_ADDRESS));
202 VM.sysWrite("0x", vOffset.minus(2 * BYTES_IN_ADDRESS), " 0x");
203 VM.sysWriteln(content);
204 }
205 }
206
207 switch (types[i]) {
208 case VoidTypeCode:
209 vOffset = vOffset.minus(BYTES_IN_STACKSLOT);
210 break;
211
212 case BooleanTypeCode:
213 case ByteTypeCode:
214 case ShortTypeCode:
215 case CharTypeCode:
216 case IntTypeCode:
217 case FloatTypeCode: {
218 int value = Magic.getIntAtOffset(stack, vOffset.minus(BYTES_IN_INT));
219 vOffset = vOffset.minus(BYTES_IN_STACKSLOT);
220
221 byte tcode = (types[i] == FloatTypeCode) ? FLOAT : INT;
222
223 state.add(new VariableElement(kind, i, tcode, value));
224 break;
225 }
226 case LongTypeCode:
227 case DoubleTypeCode: {
228 //KV: this code would be nicer if VoidTypeCode would always follow a 64-bit value. Rigth now for LOCAL it follows, for STACK it proceeds
229 Offset memoff =
230 (kind == LOCAL) ? vOffset.minus(BYTES_IN_DOUBLE) : VM.BuildFor64Addr ? vOffset : vOffset.minus(
231 BYTES_IN_STACKSLOT);
232 long value = Magic.getLongAtOffset(stack, memoff);
233
234 byte tcode = (types[i] == LongTypeCode) ? LONG : DOUBLE;
235
236 state.add(new VariableElement(kind, i, tcode, value));
237
238 if (kind == LOCAL) { //KV:VoidTypeCode is next
239 vOffset = vOffset.minus(2 * BYTES_IN_STACKSLOT);
240 i++;
241 } else {
242 vOffset = vOffset.minus(BYTES_IN_STACKSLOT); //KV:VoidTypeCode was already in front
243 }
244
245 break;
246 }
247 case ReturnAddressTypeCode: {
248 VM.disableGC();
249 Address rowIP = Magic.objectAsAddress(stack).loadAddress(vOffset);
250 Offset ipOffset = compiledMethod.getInstructionOffset(rowIP);
251 VM.enableGC();
252
253 vOffset = vOffset.minus(BYTES_IN_STACKSLOT);
254
255 if (VM.TraceOnStackReplacement) {
256 Offset ipIndex = ipOffset.toWord().rsha(LG_INSTRUCTION_WIDTH).toOffset();
257 VM.sysWrite("baseline ret_addr ip ", ipIndex, " --> ");
258 }
259
260 int bcIndex = compiledMethod.findBytecodeIndexForInstruction(ipOffset.plus(INSTRUCTION_WIDTH));
261
262 if (VM.TraceOnStackReplacement) {
263 VM.sysWrite(" bc " + bcIndex + "\n");
264 }
265
266 state.add(new VariableElement(kind, i, RET_ADDR, bcIndex));
267 break;
268 }
269
270 case ClassTypeCode:
271 case ArrayTypeCode: {
272 VM.disableGC();
273 Object ref = Magic.getObjectAtOffset(stack, vOffset.minus(BYTES_IN_ADDRESS));
274 VM.enableGC();
275
276 vOffset = vOffset.minus(BYTES_IN_STACKSLOT);
277
278 state.add(new VariableElement(kind, i, REF, ref));
279 break;
280 }
281 case WordTypeCode: {
282 Word value = Magic.getWordAtOffset(stack, vOffset.minus(BYTES_IN_ADDRESS));
283 vOffset = vOffset.minus(BYTES_IN_STACKSLOT);
284
285 state.add(new VariableElement(kind, i, WORD, value));
286 break;
287 }
288 default:
289 if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED);
290 break;
291 } // switch
292 } // for loop
293 }
294 }