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