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