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    }