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