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.ia32; 014 015import static org.jikesrvm.compilers.common.assembler.ia32.AssemblerConstants.EQ; 016import static org.jikesrvm.compilers.common.assembler.ia32.AssemblerConstants.NE; 017import static org.jikesrvm.ia32.ArchConstants.SSE2_FULL; 018import static org.jikesrvm.ia32.BaselineConstants.LG_WORDSIZE; 019import static org.jikesrvm.ia32.BaselineConstants.S0; 020import static org.jikesrvm.ia32.BaselineConstants.SP; 021import static org.jikesrvm.ia32.BaselineConstants.T0; 022import static org.jikesrvm.ia32.BaselineConstants.T1; 023import static org.jikesrvm.ia32.BaselineConstants.TR; 024import static org.jikesrvm.ia32.BaselineConstants.WORDSIZE; 025import static org.jikesrvm.ia32.RegisterConstants.ALL_GPRS; 026import static org.jikesrvm.ia32.RegisterConstants.EAX; 027import static org.jikesrvm.ia32.RegisterConstants.EBP; 028import static org.jikesrvm.ia32.RegisterConstants.EBX; 029import static org.jikesrvm.ia32.RegisterConstants.ECX; 030import static org.jikesrvm.ia32.RegisterConstants.EDI; 031import static org.jikesrvm.ia32.RegisterConstants.EDX; 032import static org.jikesrvm.ia32.RegisterConstants.ESI; 033import static org.jikesrvm.ia32.RegisterConstants.FP0; 034import static org.jikesrvm.ia32.RegisterConstants.JTOC_REGISTER; 035import static org.jikesrvm.ia32.RegisterConstants.NONVOLATILE_GPRS; 036import static org.jikesrvm.ia32.RegisterConstants.NUM_GPRS; 037import static org.jikesrvm.ia32.RegisterConstants.NUM_NONVOLATILE_FPRS; 038import static org.jikesrvm.ia32.RegisterConstants.NUM_NONVOLATILE_GPRS; 039import static org.jikesrvm.ia32.RegisterConstants.NUM_PARAMETER_FPRS; 040import static org.jikesrvm.ia32.RegisterConstants.NUM_PARAMETER_GPRS; 041import static org.jikesrvm.ia32.RegisterConstants.THREAD_REGISTER; 042import static org.jikesrvm.ia32.RegisterConstants.XMM0; 043import static org.jikesrvm.ia32.RegisterConstants.XMM1; 044import static org.jikesrvm.ia32.RegisterConstants.XMM2; 045import static org.jikesrvm.ia32.RegisterConstants.XMM3; 046import static org.jikesrvm.ia32.StackframeLayoutConstants.INVISIBLE_METHOD_ID; 047import static org.jikesrvm.ia32.StackframeLayoutConstants.STACKFRAME_BODY_OFFSET; 048import static org.jikesrvm.ia32.TrapConstants.RVM_TRAP_BASE; 049import static org.jikesrvm.objectmodel.JavaHeaderConstants.ARRAY_LENGTH_BYTES; 050import static org.jikesrvm.runtime.JavaSizeConstants.BYTES_IN_DOUBLE; 051import static org.jikesrvm.runtime.RuntimeEntrypoints.TRAP_UNKNOWN; 052 053import org.jikesrvm.VM; 054import org.jikesrvm.classloader.RVMField; 055import org.jikesrvm.compilers.common.CodeArray; 056import org.jikesrvm.compilers.common.assembler.ForwardReference; 057import org.jikesrvm.compilers.common.assembler.ia32.Assembler; 058import org.jikesrvm.ia32.RegisterConstants.GPR; 059import org.jikesrvm.objectmodel.ObjectModel; 060import org.jikesrvm.runtime.ArchEntrypoints; 061import org.jikesrvm.runtime.EntrypointHelper; 062import org.jikesrvm.runtime.Entrypoints; 063import org.jikesrvm.scheduler.RVMThread; 064import org.vmmagic.pragma.Entrypoint; 065import org.vmmagic.unboxed.Offset; 066 067/** 068 * A place to put hand written machine code typically invoked by Magic 069 * methods. 070 * 071 * <p>Hand coding of small inline instruction sequences is typically handled by 072 * each compiler's implementation of Magic methods. 073 * A few Magic methods are so complex that their implementations require 074 * many instructions. But our compilers do not inline 075 * arbitrary amounts of machine code. We therefore write such code blocks 076 * here, out of line. 077 * 078 * <p>These code blocks can be shared by all compilers. They can be branched to 079 * via a jtoc offset (obtained from Entrypoints.XXXInstructionsField). 080 */ 081public abstract class OutOfLineMachineCode { 082 //-----------// 083 // interface // 084 //-----------// 085 086 public static void init() { 087 generatePcThunkInstructions(); 088 reflectiveMethodInvokerInstructions = generateReflectiveMethodInvokerInstructions(); 089 saveThreadStateInstructions = generateSaveThreadStateInstructions(); 090 threadSwitchInstructions = generateThreadSwitchInstructions(); 091 RVMThread.stackTrampolineBridgeInstructions = generateStackTrampolineBridgeInstructions(); 092 restoreHardwareExceptionStateInstructions = generateRestoreHardwareExceptionStateInstructions(); 093 } 094 095 //----------------// 096 // implementation // 097 //----------------// 098 099 public static final RVMField[] pcThunkInstructionsField = new RVMField[8]; 100 101 @SuppressWarnings({"unused", "UnusedDeclaration", "FieldCanBeLocal"}) 102 // Accessed via field array above 103 private static CodeArray pcThunkEAXInstructions; 104 105 @SuppressWarnings({"unused", "UnusedDeclaration", "FieldCanBeLocal"}) 106 // Accessed via field array above 107 private static CodeArray pcThunkEBXInstructions; 108 109 @SuppressWarnings({"unused", "UnusedDeclaration", "FieldCanBeLocal"}) 110 // Accessed via field array above 111 private static CodeArray pcThunkECXInstructions; 112 113 @SuppressWarnings({"unused", "UnusedDeclaration", "FieldCanBeLocal"}) 114 // Accessed via field array above 115 private static CodeArray pcThunkEDXInstructions; 116 117 @SuppressWarnings({"unused", "UnusedDeclaration", "FieldCanBeLocal"}) 118 // Accessed via field array above 119 private static CodeArray pcThunkEBPInstructions; 120 121 @SuppressWarnings({"unused", "UnusedDeclaration", "FieldCanBeLocal"}) 122 // Accessed via field array above 123 private static CodeArray pcThunkESIInstructions; 124 125 @SuppressWarnings({"unused", "UnusedDeclaration", "FieldCanBeLocal"}) 126 // Accessed via field array above 127 private static CodeArray pcThunkEDIInstructions; 128 129 @SuppressWarnings({"unused", "UnusedDeclaration", "FieldCanBeLocal"}) 130 @Entrypoint 131 private static CodeArray reflectiveMethodInvokerInstructions; 132 @SuppressWarnings({"unused", "UnusedDeclaration", "FieldCanBeLocal"}) 133 @Entrypoint 134 private static CodeArray saveThreadStateInstructions; 135 @SuppressWarnings({"unused", "UnusedDeclaration", "FieldCanBeLocal"}) 136 @Entrypoint 137 private static CodeArray threadSwitchInstructions; 138 @SuppressWarnings({"unused", "UnusedDeclaration", "FieldCanBeLocal"}) 139 @Entrypoint 140 private static CodeArray restoreHardwareExceptionStateInstructions; 141 142 private static final Offset PARAMS_FP_OFFSET = Offset.fromIntSignExtend(WORDSIZE * 2); 143 private static final Offset FPRMETA_FP_OFFSET = Offset.fromIntSignExtend(WORDSIZE * 3); 144 private static final Offset FPRS_FP_OFFSET = Offset.fromIntSignExtend(WORDSIZE * 4); 145 private static final Offset GPRS_FP_OFFSET = Offset.fromIntSignExtend(WORDSIZE * 5); 146 private static final Offset CODE_FP_OFFSET = Offset.fromIntSignExtend(WORDSIZE * 6); 147 148 /** 149 * Machine code to get the address of the instruction after the call to this 150 * method 151 */ 152 private static void generatePcThunkInstructions() { 153 Assembler asm = new Assembler(0); 154 asm.emitMOV_Reg_RegInd(EAX, SP); 155 asm.emitRET(); 156 pcThunkEAXInstructions = asm.getMachineCodes(); 157 pcThunkInstructionsField[EAX.value()] = 158 EntrypointHelper.getField(OutOfLineMachineCode.class, 159 "pcThunkEAXInstructions", CodeArray.class); 160 161 asm = new Assembler(0); 162 asm.emitMOV_Reg_RegInd(EBX, SP); 163 asm.emitRET(); 164 pcThunkEBXInstructions = asm.getMachineCodes(); 165 pcThunkInstructionsField[EBX.value()] = 166 EntrypointHelper.getField(OutOfLineMachineCode.class, 167 "pcThunkEBXInstructions", CodeArray.class); 168 169 asm = new Assembler(0); 170 asm.emitMOV_Reg_RegInd(ECX, SP); 171 asm.emitRET(); 172 pcThunkECXInstructions = asm.getMachineCodes(); 173 pcThunkInstructionsField[ECX.value()] = 174 EntrypointHelper.getField(OutOfLineMachineCode.class, 175 "pcThunkECXInstructions", CodeArray.class); 176 177 asm = new Assembler(0); 178 asm.emitMOV_Reg_RegInd(EDX, SP); 179 asm.emitRET(); 180 pcThunkEDXInstructions = asm.getMachineCodes(); 181 pcThunkInstructionsField[EDX.value()] = 182 EntrypointHelper.getField(OutOfLineMachineCode.class, 183 "pcThunkEDXInstructions", CodeArray.class); 184 185 // NB a PC thunk into ESP isn't allowed 186 187 asm = new Assembler(0); 188 asm.emitMOV_Reg_RegInd(EBP, SP); 189 asm.emitRET(); 190 pcThunkEBPInstructions = asm.getMachineCodes(); 191 pcThunkInstructionsField[EBP.value()] = 192 EntrypointHelper.getField(OutOfLineMachineCode.class, 193 "pcThunkEBPInstructions", CodeArray.class); 194 195 asm = new Assembler(0); 196 asm.emitMOV_Reg_RegInd(ESI, SP); 197 asm.emitRET(); 198 pcThunkESIInstructions = asm.getMachineCodes(); 199 pcThunkInstructionsField[ESI.value()] = 200 EntrypointHelper.getField(OutOfLineMachineCode.class, 201 "pcThunkESIInstructions", CodeArray.class); 202 203 asm = new Assembler(0); 204 asm.emitMOV_Reg_RegInd(EDI, SP); 205 asm.emitRET(); 206 pcThunkEDIInstructions = asm.getMachineCodes(); 207 pcThunkInstructionsField[EDI.value()] = 208 EntrypointHelper.getField(OutOfLineMachineCode.class, 209 "pcThunkEDIInstructions", CodeArray.class); 210 } 211 212 /** 213 * Machine code for reflective method invocation. 214 * <pre> 215 * VM compiled with NUM_PARAMETERS_GPRS == 0 216 * Registers taken at runtime: 217 * none 218 * Stack taken at runtime: 219 * hi-mem 220 * address of method entrypoint to be called 221 * address of gpr registers to be loaded 222 * address of fpr registers to be loaded 223 * address of parameters area in calling frame 224 * return address 225 * low-mem 226 * 227 * VM compiled with NUM_PARAMETERS_GPRS == 1 228 * T0 == address of method entrypoint to be called 229 * Stack taken at runtime: 230 * hi-mem 231 * space ??? 232 * address of gpr registers to be loaded 233 * address of fpr registers to be loaded 234 * address of parameters area in calling frame 235 * return address 236 * low-mem 237 * 238 * VM compiled with NUM_PARAMETERS_GPRS == 2 239 * T0 == address of method entrypoint to be called 240 * T1 == address of gpr registers to be loaded 241 * Stack taken at runtime: 242 * hi-mem 243 * space ??? 244 * space ??? 245 * address of fpr registers to be loaded 246 * address of parameters area in calling frame 247 * return address 248 * low-mem 249 * 250 * Registers returned at runtime: 251 * standard return value conventions used 252 * 253 * Side effects at runtime: 254 * artificial stackframe created and destroyed 255 * volatile, and scratch registers destroyed 256 * </pre> 257 * 258 * @return instructions for the reflective method invoker 259 */ 260 private static CodeArray generateReflectiveMethodInvokerInstructions() { 261 Assembler asm = new Assembler(100); 262 int gprs; 263 Offset fpOffset = ArchEntrypoints.framePointerField.getOffset(); 264 GPR T = T0; 265 gprs = NUM_PARAMETER_GPRS; 266 // we have exactly 5 paramaters, offset 0 from SP is the return address the 267 // parameters are at offsets 5 to 1 268 Offset offset = Offset.fromIntZeroExtend(5 << LG_WORDSIZE); 269 // Write at most 2 parameters from registers in the stack. This is 270 // logically equivalent to ParamaterRegisterUnload in the compiler 271 if (gprs > 0) { 272 gprs--; 273 if (VM.BuildFor32Addr) { 274 asm.emitMOV_RegDisp_Reg(SP, offset, T); 275 } else { 276 asm.emitMOV_RegDisp_Reg_Quad(SP, offset, T); 277 } 278 T = T1; 279 offset = offset.minus(WORDSIZE); 280 } 281 if (gprs > 0) { 282 if (VM.BuildFor32Addr) { 283 asm.emitMOV_RegDisp_Reg(SP, offset, T); 284 } else { 285 asm.emitMOV_RegDisp_Reg_Quad(SP, offset, T); 286 } 287 } 288 289 /* available registers S0, T0, T1 */ 290 291 /* push a new frame */ 292 asm.emitPUSH_RegDisp(TR, fpOffset); // link this frame with next 293 if (VM.BuildFor32Addr) { 294 asm.emitMOV_RegDisp_Reg(THREAD_REGISTER, fpOffset, SP); // establish base of new frame 295 asm.emitPUSH_Imm(INVISIBLE_METHOD_ID); 296 asm.emitADD_Reg_Imm(SP, STACKFRAME_BODY_OFFSET.toInt()); 297 } else { 298 asm.emitMOV_RegDisp_Reg_Quad(THREAD_REGISTER, fpOffset, SP); // establish base of new frame 299 asm.emitPUSH_Imm(INVISIBLE_METHOD_ID); 300 asm.emitADD_Reg_Imm_Quad(SP, STACKFRAME_BODY_OFFSET.toInt()); 301 } 302 303 /* write parameters on stack 304 * move data from memory addressed by Paramaters array, the fourth 305 * parameter to this, into the stack. 306 * SP target address 307 * S0 source address 308 * T1 length 309 * T0 scratch 310 */ 311 if (VM.BuildFor32Addr) { 312 asm.emitMOV_Reg_RegDisp(S0, THREAD_REGISTER, fpOffset); 313 asm.emitMOV_Reg_RegDisp(S0, S0, PARAMS_FP_OFFSET); // S0 <- Parameters 314 asm.emitMOV_Reg_RegDisp(T1, S0, ObjectModel.getArrayLengthOffset()); // T1 <- Parameters.length() 315 asm.emitCMP_Reg_Imm(T1, 0); // length == 0 ? 316 } else { 317 asm.emitMOV_Reg_RegDisp_Quad(S0, THREAD_REGISTER, fpOffset); 318 asm.emitMOV_Reg_RegDisp_Quad(S0, S0, PARAMS_FP_OFFSET);// S0 <- Parameters 319 if (ARRAY_LENGTH_BYTES == 4) { 320 asm.emitMOV_Reg_RegDisp(T1, S0, ObjectModel.getArrayLengthOffset()); // T1 <- Parameters.length() 321 asm.emitCMP_Reg_Imm(T1, 0); // length == 0 ? 322 } else { 323 asm.emitMOV_Reg_RegDisp_Quad(T1, S0, ObjectModel.getArrayLengthOffset()); // T1 <- Parameters.length() 324 asm.emitCMP_Reg_Imm_Quad(T1, 0); // length == 0 ? 325 } 326 } 327 328 int parameterLoopLabel = asm.getMachineCodeIndex(); 329 ForwardReference fr1 = asm.forwardJcc(EQ); // done? --> branch to end 330 if (VM.BuildFor32Addr) { 331 asm.emitMOV_Reg_RegInd(T0, S0); // T0 <- Paramaters[i] 332 } else { 333 asm.emitMOV_Reg_RegInd_Quad(T0, S0); // T0 <- Paramaters[i] 334 } 335 asm.emitPUSH_Reg(T0); // mem[j++] <- Parameters[i] 336 if (VM.BuildFor32Addr) { 337 asm.emitADD_Reg_Imm(S0, WORDSIZE); // i++ 338 } else { 339 asm.emitADD_Reg_Imm_Quad(S0, WORDSIZE); // i++ 340 } 341 if (ARRAY_LENGTH_BYTES == 4) { 342 asm.emitADD_Reg_Imm(T1, -1); // length-- 343 } else { 344 asm.emitADD_Reg_Imm_Quad(T1, -1); // length-- 345 } 346 asm.emitJMP_Imm(parameterLoopLabel); 347 348 fr1.resolve(asm); // end of the loop 349 350 if (SSE2_FULL) { 351 /* write fprs onto fprs registers */ 352 if (VM.BuildFor32Addr) { 353 asm.emitMOV_Reg_RegDisp(S0, THREAD_REGISTER, fpOffset); 354 asm.emitMOV_Reg_RegDisp(T0, S0, FPRS_FP_OFFSET); // T0 <- FPRs 355 asm.emitMOV_Reg_RegDisp(T1, T0, ObjectModel.getArrayLengthOffset()); // T1 <- FPRs.length() 356 asm.emitMOV_Reg_RegDisp(S0, S0, FPRMETA_FP_OFFSET); // S0 <- FPRmeta 357 } else { 358 asm.emitMOV_Reg_RegDisp_Quad(S0, THREAD_REGISTER, fpOffset); 359 asm.emitMOV_Reg_RegDisp_Quad(T0, S0, FPRS_FP_OFFSET); // T0 <- FPRs 360 if (ARRAY_LENGTH_BYTES == 4) { 361 asm.emitMOV_Reg_RegDisp(T1, T0, ObjectModel.getArrayLengthOffset()); // T1 <- FPRs.length() 362 } else { 363 asm.emitMOV_Reg_RegDisp_Quad(T1, T0, ObjectModel.getArrayLengthOffset()); // T1 <- FPRs.length() 364 } 365 asm.emitMOV_Reg_RegDisp_Quad(S0, S0, FPRMETA_FP_OFFSET); // S0 <- FPRmeta 366 } 367 368 if (VM.VerifyAssertions) VM._assert(NUM_PARAMETER_FPRS <= 4); 369 370 ForwardReference fr_next; 371 372 asm.emitCMP_Reg_Imm(T1, 0); // length == 0 ? 373 ForwardReference fpr_r1 = asm.forwardJcc(EQ); 374 asm.emitMOVSD_Reg_RegInd(XMM0, T0); 375 asm.emitCMP_RegInd_Imm_Byte(S0, 0); 376 fr_next = asm.forwardJcc(NE); 377 asm.emitCVTSD2SS_Reg_Reg(XMM0, XMM0); 378 fr_next.resolve(asm); 379 380 asm.emitSUB_Reg_Imm(T1, 1); // length == 0 ? 381 ForwardReference fpr_r2 = asm.forwardJcc(EQ); 382 asm.emitMOVSD_Reg_RegDisp(XMM1, T0, Offset.fromIntZeroExtend(BYTES_IN_DOUBLE)); 383 asm.emitCMP_RegDisp_Imm_Byte(S0, Offset.fromIntZeroExtend(1), 0); 384 fr_next = asm.forwardJcc(NE); 385 asm.emitCVTSD2SS_Reg_Reg(XMM1, XMM1); 386 fr_next.resolve(asm); 387 388 asm.emitSUB_Reg_Imm(T1, 1); // length == 0 ? 389 ForwardReference fpr_r3 = asm.forwardJcc(EQ); 390 asm.emitMOVSD_Reg_RegDisp(XMM2, T0, Offset.fromIntZeroExtend(BYTES_IN_DOUBLE * 2)); 391 asm.emitCMP_RegDisp_Imm_Byte(S0, Offset.fromIntZeroExtend(2), 0); 392 fr_next = asm.forwardJcc(NE); 393 asm.emitCVTSD2SS_Reg_Reg(XMM2, XMM2); 394 fr_next.resolve(asm); 395 396 asm.emitSUB_Reg_Imm(T1, 1); // length == 0 ? 397 ForwardReference fpr_r4 = asm.forwardJcc(EQ); 398 asm.emitMOVSD_Reg_RegDisp(XMM3, T0, Offset.fromIntZeroExtend(BYTES_IN_DOUBLE * 3)); 399 asm.emitCMP_RegDisp_Imm_Byte(S0, Offset.fromIntZeroExtend(3), 0); 400 fr_next = asm.forwardJcc(NE); 401 asm.emitCVTSD2SS_Reg_Reg(XMM3, XMM3); 402 fr_next.resolve(asm); 403 404 fpr_r1.resolve(asm); 405 fpr_r2.resolve(asm); 406 fpr_r3.resolve(asm); 407 fpr_r4.resolve(asm); 408 409 } else { 410 if (VM.VerifyAssertions) VM._assert(VM.BuildFor32Addr); 411 /* write fprs onto fprs registers */ 412 if (VM.BuildFor32Addr) { 413 asm.emitMOV_Reg_RegDisp(S0, THREAD_REGISTER, fpOffset); 414 } else { 415 asm.emitMOV_Reg_RegDisp_Quad(S0, THREAD_REGISTER, fpOffset); 416 } 417 asm.emitMOV_Reg_RegDisp(S0, S0, FPRS_FP_OFFSET); // S0 <- FPRs 418 asm.emitMOV_Reg_RegDisp(T1, S0, ObjectModel.getArrayLengthOffset()); // T1 <- FPRs.length() 419 asm.emitSHL_Reg_Imm(T1, LG_WORDSIZE + 1); // length in bytes 420 asm.emitADD_Reg_Reg(S0, T1); // S0 <- last FPR + 8 421 asm.emitCMP_Reg_Imm(T1, 0); // length == 0 ? 422 423 int fprsLoopLabel = asm.getMachineCodeIndex(); 424 ForwardReference fr2 = asm.forwardJcc(EQ); // done? --> branch to end 425 asm.emitSUB_Reg_Imm(S0, 2 * WORDSIZE); // i-- 426 asm.emitFLD_Reg_RegInd_Quad(FP0, S0); // frp[fpr_sp++] <-FPRs[i] 427 asm.emitSUB_Reg_Imm(T1, 2 * WORDSIZE); // length-- 428 asm.emitJMP_Imm(fprsLoopLabel); 429 430 fr2.resolve(asm); // end of the loop 431 } 432 433 /* write gprs: S0 = Base address of GPRs[], T1 = GPRs.length */ 434 if (VM.BuildFor32Addr) { 435 asm.emitMOV_Reg_RegDisp(S0, THREAD_REGISTER, fpOffset); 436 asm.emitMOV_Reg_RegDisp(S0, S0, GPRS_FP_OFFSET); // S0 <- GPRs 437 asm.emitMOV_Reg_RegDisp(T1, S0, ObjectModel.getArrayLengthOffset()); // T1 <- GPRs.length() 438 asm.emitCMP_Reg_Imm(T1, 0); // length == 0 ? 439 } else { 440 asm.emitMOV_Reg_RegDisp_Quad(S0, THREAD_REGISTER, fpOffset); 441 asm.emitMOV_Reg_RegDisp_Quad(S0, S0, GPRS_FP_OFFSET); // S0 <- GPRs 442 if (ARRAY_LENGTH_BYTES == 4) { 443 asm.emitMOV_Reg_RegDisp(T1, S0, ObjectModel.getArrayLengthOffset()); // T1 <- GPRs.length() 444 asm.emitCMP_Reg_Imm(T1, 0); // length == 0 ? 445 } else { 446 asm.emitMOV_Reg_RegDisp_Quad(T1, S0, ObjectModel.getArrayLengthOffset()); // T1 <- GPRs.length() 447 asm.emitCMP_Reg_Imm_Quad(T1, 0); // length == 0 ? 448 } 449 } 450 ForwardReference fr3 = asm.forwardJcc(EQ); // result 0 --> branch to end 451 if (VM.BuildFor32Addr) { 452 asm.emitMOV_Reg_RegInd(T0, S0); // T0 <- GPRs[0] 453 asm.emitADD_Reg_Imm(S0, WORDSIZE); // S0 += WORDSIZE 454 asm.emitADD_Reg_Imm(T1, -1); // T1-- 455 } else { 456 asm.emitMOV_Reg_RegInd_Quad(T0, S0); // T0 <- GPRs[0] 457 asm.emitADD_Reg_Imm_Quad(S0, WORDSIZE); // S0 += WORDSIZE 458 asm.emitADD_Reg_Imm_Quad(T1, -1); // T1-- 459 } 460 461 ForwardReference fr4 = asm.forwardJcc(EQ); // result 0 --> branch to end 462 if (VM.BuildFor32Addr) { 463 asm.emitMOV_Reg_RegInd(T1, S0); // T1 <- GPRs[1] 464 } else { 465 asm.emitMOV_Reg_RegInd_Quad(T1, S0); // T1 <- GPRs[1] 466 } 467 fr3.resolve(asm); 468 fr4.resolve(asm); 469 470 /* branch to method. On a good day we might even be back */ 471 if (VM.BuildFor32Addr) { 472 asm.emitMOV_Reg_RegDisp(S0, THREAD_REGISTER, fpOffset); 473 asm.emitMOV_Reg_RegDisp(S0, S0, CODE_FP_OFFSET); // S0 <- code 474 } else { 475 asm.emitMOV_Reg_RegDisp_Quad(S0, THREAD_REGISTER, fpOffset); 476 asm.emitMOV_Reg_RegDisp_Quad(S0, S0, CODE_FP_OFFSET); // S0 <- code 477 } 478 asm.emitCALL_Reg(S0); // go there 479 // T0/T1 have returned value 480 481 /* and get out */ 482 // NOTE: RVM callee has popped the params, so we can simply 483 // add back in the initial SP to FP delta to get SP to be a framepointer again! 484 if (VM.BuildFor32Addr) { 485 asm.emitADD_Reg_Imm(SP, -STACKFRAME_BODY_OFFSET.toInt() + WORDSIZE); 486 } else { 487 asm.emitADD_Reg_Imm_Quad(SP, -STACKFRAME_BODY_OFFSET.toInt() + WORDSIZE); 488 } 489 asm.emitPOP_RegDisp(TR, fpOffset); 490 491 asm.emitRET_Imm(5 << LG_WORDSIZE); // again, exactly 5 parameters 492 493 return asm.getMachineCodes(); 494 } 495 496 /** 497 * Machine code to implement "Magic.saveThreadState()". 498 * <pre> 499 * Registers taken at runtime: 500 * T0 == address of Registers object 501 * 502 * Registers returned at runtime: 503 * none 504 * 505 * Side effects at runtime: 506 * S0, T1 destroyed 507 * Thread state stored into Registers object 508 * </pre> 509 * 510 * @return instructions for saving the thread state 511 */ 512 private static CodeArray generateSaveThreadStateInstructions() { 513 if (VM.VerifyAssertions) { 514 VM._assert(NUM_NONVOLATILE_FPRS == 0); // assuming no NV FPRs (otherwise would have to save them here) 515 } 516 Assembler asm = new Assembler(0); 517 Offset ipOffset = ArchEntrypoints.registersIPField.getOffset(); 518 Offset fpOffset = ArchEntrypoints.registersFPField.getOffset(); 519 Offset gprsOffset = ArchEntrypoints.registersGPRsField.getOffset(); 520 if (VM.BuildFor32Addr) { 521 asm.emitMOV_Reg_RegDisp(S0, TR, ArchEntrypoints.framePointerField.getOffset()); 522 asm.emitMOV_RegDisp_Reg(T0, fpOffset, S0); // registers.fp := pr.framePointer 523 } else { 524 asm.emitMOV_Reg_RegDisp_Quad(S0, TR, ArchEntrypoints.framePointerField.getOffset()); 525 asm.emitMOV_RegDisp_Reg_Quad(T0, fpOffset, S0); // registers.fp := pr.framePointer 526 } 527 asm.emitPOP_Reg(T1); // T1 := return address (target of final jmp) 528 if (VM.BuildFor32Addr) { 529 asm.emitMOV_RegDisp_Reg(T0, ipOffset, T1); // registers.ip := return address 530 } else { 531 asm.emitMOV_RegDisp_Reg_Quad(T0, ipOffset, T1); // registers.ip := return address 532 } 533 asm.emitPOP_Reg(S0); // throw away space for registers parameter (in T0) 534 if (VM.BuildFor32Addr) { 535 asm.emitMOV_Reg_RegDisp(S0, T0, gprsOffset); // S0 := registers.gprs[] 536 asm.emitMOV_RegDisp_Reg(S0, Offset.fromIntZeroExtend(SP.value() << LG_WORDSIZE), SP); // registers.gprs[#SP] := SP 537 for (int i = 0; i < NUM_NONVOLATILE_GPRS; i++) { 538 asm.emitMOV_RegDisp_Reg(S0, 539 Offset.fromIntZeroExtend(NONVOLATILE_GPRS[i].value() << LG_WORDSIZE), 540 NONVOLATILE_GPRS[i]); // registers.gprs[i] := i'th register 541 } 542 } else { 543 asm.emitMOV_Reg_RegDisp_Quad(S0, T0, gprsOffset); // S0 := registers.gprs[] 544 asm.emitMOV_RegDisp_Reg_Quad(S0, Offset.fromIntZeroExtend(SP.value() << LG_WORDSIZE), SP); // registers.gprs[#SP] := SP 545 for (int i = 0; i < NUM_NONVOLATILE_GPRS; i++) { 546 asm.emitMOV_RegDisp_Reg_Quad(S0, 547 Offset.fromIntZeroExtend(NONVOLATILE_GPRS[i].value() << LG_WORDSIZE), 548 NONVOLATILE_GPRS[i]); // registers.gprs[i] := i'th register 549 } 550 } 551 asm.emitJMP_Reg(T1); // return to return address 552 return asm.getMachineCodes(); 553 } 554 555 /** 556 * Machine code to perform a stack trampoline bridge for 557 * implementing a return barrier. 558 * <p> 559 * This code is used to hijack a return and bridge to some 560 * other method (which implements the return barrier) before 561 * falling back to the intended return address. 562 * <p> 563 * The key here is to preserve register and stack state so that 564 * the caller is oblivious of the detour that occurred during 565 * the return. 566 * 567 * @return instructions for the stack trampoline bridge 568 */ 569 private static CodeArray generateStackTrampolineBridgeInstructions() { 570 if (VM.VerifyAssertions) { 571 VM._assert(NUM_NONVOLATILE_FPRS == 0); // assuming no NV FPRs (otherwise would have to save them here) 572 } 573 574 // NYI: 64-bit version. Do not use an assertion here because a failing assertion 575 // would fail the bootimage build on x64. Return barriers are currently 576 // optional. Therefore, just crash the VM if the code ever gets executed on 577 // x64. 578 if (VM.BuildFor64Addr) { 579 Assembler asm = new Assembler(0); 580 asm.emitINT_Imm(TRAP_UNKNOWN + RVM_TRAP_BASE); 581 return asm.getMachineCodes(); 582 } 583 584 Assembler asm = new Assembler(0); 585 586 /* push the hijacked return address (which is held in thread-local state) */ 587 asm.emitPUSH_RegDisp(TR, ArchEntrypoints.hijackedReturnAddressField.getOffset()); 588 589 /* push the GPRs and fp */ 590 for (int i = 0; i < NUM_GPRS; i++) { 591 asm.emitPUSH_Reg(ALL_GPRS[i]); 592 } 593 asm.emitPUSH_RegDisp(TR, ArchEntrypoints.framePointerField.getOffset()); 594 595 /* call the handler */ 596 asm.generateJTOCcall(Entrypoints.returnBarrierMethod.getOffset()); 597 598 /* pop the fp and GPRs */ 599 asm.emitPOP_RegDisp(TR, ArchEntrypoints.framePointerField.getOffset()); 600 for (int i = NUM_GPRS - 1; i >= 0; i--) { 601 asm.emitPOP_Reg(ALL_GPRS[i]); 602 } 603 604 /* pop the hijacked return address and return */ 605 asm.emitRET(); 606 607 return asm.getMachineCodes(); 608 } 609 610 611 /** 612 * Machine code to implement "Magic.threadSwitch()". 613 * <pre> 614 * NOTE: Currently not functional for PNT: left as a guide for possible reimplementation. 615 * 616 * Parameters taken at runtime: 617 * T0 == address of Thread object for the current thread 618 * T1 == address of Registers object for the new thread 619 * 620 * Registers returned at runtime: 621 * none 622 * 623 * Side effects at runtime: 624 * sets current Thread's beingDispatched field to false 625 * saves current Thread's nonvolatile hardware state in its Registers object 626 * restores new thread's Registers nonvolatile hardware state. 627 * execution resumes at address specificed by restored thread's Registers ip field 628 * </pre> 629 * 630 * @return instructions for doing a thread switch 631 */ 632 private static CodeArray generateThreadSwitchInstructions() { 633 if (VM.VerifyAssertions) { 634 VM._assert(NUM_NONVOLATILE_FPRS == 0); // assuming no NV FPRs (otherwise would have to save them here) 635 } 636 Assembler asm = new Assembler(0); 637 Offset ipOffset = ArchEntrypoints.registersIPField.getOffset(); 638 Offset fpOffset = ArchEntrypoints.registersFPField.getOffset(); 639 Offset gprsOffset = ArchEntrypoints.registersGPRsField.getOffset(); 640 Offset regsOffset = Entrypoints.threadContextRegistersField.getOffset(); 641 642 // (1) Save hardware state of thread we are switching off of. 643 if (VM.BuildFor32Addr) { 644 asm.emitMOV_Reg_RegDisp(S0, T0, regsOffset); // S0 = T0.contextRegisters 645 } else { 646 asm.emitMOV_Reg_RegDisp_Quad(S0, T0, regsOffset); // S0 = T0.contextRegisters 647 } 648 asm.emitPOP_RegDisp(S0, ipOffset); // T0.contextRegisters.ip = returnAddress 649 asm.emitPUSH_RegDisp(TR, ArchEntrypoints.framePointerField.getOffset()); // push TR.framePointer 650 asm.emitPOP_RegDisp(S0, fpOffset); // T0.contextRegisters.fp = pushed framepointer 651 if (VM.BuildFor32Addr) { 652 asm.emitADD_Reg_Imm(SP, 2 * WORDSIZE); // discard 2 words of parameters (T0, T1) 653 asm.emitMOV_Reg_RegDisp(S0, S0, gprsOffset); // S0 = T0.contextRegisters.gprs; 654 asm.emitMOV_RegDisp_Reg(S0, Offset.fromIntZeroExtend(SP.value() << LG_WORDSIZE), SP); // T0.contextRegisters.gprs[#SP] := SP 655 for (int i = 0; i < NUM_NONVOLATILE_GPRS; i++) { 656 // T0.contextRegisters.gprs[i] := i'th register 657 asm.emitMOV_RegDisp_Reg(S0, 658 Offset.fromIntZeroExtend(NONVOLATILE_GPRS[i].value() << LG_WORDSIZE), 659 NONVOLATILE_GPRS[i]); 660 } 661 } else { 662 asm.emitADD_Reg_Imm_Quad(SP, 2 * WORDSIZE); // discard 2 words of parameters (T0, T1) 663 asm.emitMOV_Reg_RegDisp_Quad(S0, S0, gprsOffset); // S0 = T0.contextRegisters.gprs; 664 asm.emitMOV_RegDisp_Reg_Quad(S0, Offset.fromIntZeroExtend(SP.value() << LG_WORDSIZE), SP); // T0.contextRegisters.gprs[#SP] := SP 665 for (int i = 0; i < NUM_NONVOLATILE_GPRS; i++) { 666 // T0.contextRegisters.gprs[i] := i'th register 667 asm.emitMOV_RegDisp_Reg_Quad(S0, 668 Offset.fromIntZeroExtend(NONVOLATILE_GPRS[i].value() << LG_WORDSIZE), 669 NONVOLATILE_GPRS[i]); 670 } 671 } 672 673 // (2) Set currentThread.beingDispatched to false 674 // PNT: don't have this field anymore 675 //asm.emitMOV_RegDisp_Imm_Byte(T0, 676 // Entrypoints.beingDispatchedField.getOffset(), 677 // 0); // previous thread's stack is nolonger in use, so it can now be dispatched on any virtual processor 678 679 // (3) Restore hardware state of thread we are switching to. 680 if (VM.BuildFor32Addr) { 681 asm.emitMOV_Reg_RegDisp(S0, T1, fpOffset); // S0 := restoreRegs.fp 682 } else { 683 asm.emitMOV_Reg_RegDisp_Quad(S0, T1, fpOffset); // S0 := restoreRegs.fp 684 } 685 // TR.framePointer = restoreRegs.fp 686 if (VM.BuildFor32Addr) { 687 asm.emitMOV_RegDisp_Reg(THREAD_REGISTER, ArchEntrypoints.framePointerField.getOffset(), S0); 688 asm.emitMOV_Reg_RegDisp(S0, T1, gprsOffset); // S0 := restoreRegs.gprs[] 689 asm.emitMOV_Reg_RegDisp(SP, S0, Offset.fromIntZeroExtend(SP.value() << LG_WORDSIZE)); // SP := restoreRegs.gprs[#SP] 690 for (int i = 0; i < NUM_NONVOLATILE_GPRS; i++) { 691 // i'th register := restoreRegs.gprs[i] 692 asm.emitMOV_Reg_RegDisp(NONVOLATILE_GPRS[i], 693 S0, 694 Offset.fromIntZeroExtend(NONVOLATILE_GPRS[i].value() << 695 LG_WORDSIZE)); 696 } 697 } else { 698 asm.emitMOV_RegDisp_Reg_Quad(THREAD_REGISTER, ArchEntrypoints.framePointerField.getOffset(), S0); 699 asm.emitMOV_Reg_RegDisp_Quad(S0, T1, gprsOffset); // S0 := restoreRegs.gprs[] 700 asm.emitMOV_Reg_RegDisp_Quad(SP, S0, Offset.fromIntZeroExtend(SP.value() << LG_WORDSIZE)); // SP := restoreRegs.gprs[#SP] 701 for (int i = 0; i < NUM_NONVOLATILE_GPRS; i++) { 702 // i'th register := restoreRegs.gprs[i] 703 asm.emitMOV_Reg_RegDisp_Quad(NONVOLATILE_GPRS[i], 704 S0, 705 Offset.fromIntZeroExtend(NONVOLATILE_GPRS[i].value() << LG_WORDSIZE)); 706 } 707 } 708 asm.emitJMP_RegDisp(T1, ipOffset); // return to (save) return address 709 return asm.getMachineCodes(); 710 } 711 712 /** 713 * Machine code to implement "Magic.restoreHardwareExceptionState()". 714 * <pre> 715 * Registers taken at runtime: 716 * T0 == address of Registers object 717 * 718 * Registers returned at runtime: 719 * none 720 * 721 * Side effects at runtime: 722 * all registers are restored except THREAD_REGISTER and EFLAGS; 723 * execution resumes at "registers.ip" 724 * </pre> 725 * 726 * @return instructions to restore the hardware exception state 727 */ 728 private static CodeArray generateRestoreHardwareExceptionStateInstructions() { 729 Assembler asm = new Assembler(0); 730 731 // Set TR.framePointer to be registers.fp 732 if (VM.BuildFor32Addr) { 733 asm.emitMOV_Reg_RegDisp(S0, T0, ArchEntrypoints.registersFPField.getOffset()); 734 asm.emitMOV_RegDisp_Reg(THREAD_REGISTER, ArchEntrypoints.framePointerField.getOffset(), S0); 735 } else { 736 asm.emitMOV_Reg_RegDisp_Quad(S0, T0, ArchEntrypoints.registersFPField.getOffset()); 737 asm.emitMOV_RegDisp_Reg_Quad(THREAD_REGISTER, ArchEntrypoints.framePointerField.getOffset(), S0); 738 } 739 740 // Restore SP 741 if (VM.BuildFor32Addr) { 742 asm.emitMOV_Reg_RegDisp(S0, T0, ArchEntrypoints.registersGPRsField.getOffset()); 743 asm.emitMOV_Reg_RegDisp(SP, S0, Offset.fromIntZeroExtend(SP.value() << LG_WORDSIZE)); 744 } else { 745 asm.emitMOV_Reg_RegDisp_Quad(S0, T0, ArchEntrypoints.registersGPRsField.getOffset()); 746 asm.emitMOV_Reg_RegDisp_Quad(SP, S0, Offset.fromIntZeroExtend(SP.value() << LG_WORDSIZE)); 747 } 748 749 // Push registers.ip to stack (now that SP has been restored) 750 asm.emitPUSH_RegDisp(T0, ArchEntrypoints.registersIPField.getOffset()); 751 752 // Restore the GPRs except for S0, TR, SP and JTOC 753 // (restored above and then modified by pushing registers.ip!) 754 Offset off = Offset.zero(); 755 for (byte i = 0; i < NUM_GPRS; i++, off = off.plus(WORDSIZE)) { 756 if (i != S0.value() && i != ESI.value() && i != SP.value() && 757 (JTOC_REGISTER == null || i != JTOC_REGISTER.value())) { 758 if (VM.BuildFor32Addr) { 759 asm.emitMOV_Reg_RegDisp(GPR.lookup(i), S0, off); 760 } else { 761 asm.emitMOV_Reg_RegDisp_Quad(GPR.lookup(i), S0, off); 762 } 763 } 764 } 765 766 // Restore S0 767 if (VM.BuildFor32Addr) { 768 asm.emitMOV_Reg_RegDisp(S0, S0, Offset.fromIntZeroExtend(S0.value() << LG_WORDSIZE)); 769 } else { 770 asm.emitMOV_Reg_RegDisp_Quad(S0, S0, Offset.fromIntZeroExtend(S0.value() << LG_WORDSIZE)); 771 } 772 773 // Return to registers.ip (popping stack) 774 asm.emitRET(); 775 return asm.getMachineCodes(); 776 } 777}