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