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 * A class that retrieves the VM scope descriptor 036 * from a suspended thread whose top method was compiled by the 037 * baseline compiler. 038 */ 039 public abstract class BaselineExecutionStateExtractor extends ExecutionStateExtractor 040 implements Constants, ArchConstants, OSRConstants, PhysicalRegisterConstants { 041 042 /** 043 * Implements ExecutionStateExtractor.extractState. 044 * 045 * @param thread : the suspended thread, the registers and stack frames are used. 046 * @param osrFPoff : the osr method's stack frame offset 047 * @param methFPoff : the real method's stack frame offset 048 * @param cmid : the top application method ( system calls are unwounded ). 049 * 050 * return a ExecutionStateExtractor object. 051 */ 052 @Override 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 }