/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.ia32;
import static org.jikesrvm.compilers.common.assembler.ia32.AssemblerConstants.EQ;
import static org.jikesrvm.compilers.common.assembler.ia32.AssemblerConstants.NE;
import static org.jikesrvm.ia32.ArchConstants.SSE2_FULL;
import static org.jikesrvm.ia32.BaselineConstants.LG_WORDSIZE;
import static org.jikesrvm.ia32.BaselineConstants.S0;
import static org.jikesrvm.ia32.BaselineConstants.SP;
import static org.jikesrvm.ia32.BaselineConstants.T0;
import static org.jikesrvm.ia32.BaselineConstants.T1;
import static org.jikesrvm.ia32.BaselineConstants.TR;
import static org.jikesrvm.ia32.BaselineConstants.WORDSIZE;
import static org.jikesrvm.ia32.RegisterConstants.ALL_GPRS;
import static org.jikesrvm.ia32.RegisterConstants.EAX;
import static org.jikesrvm.ia32.RegisterConstants.EBP;
import static org.jikesrvm.ia32.RegisterConstants.EBX;
import static org.jikesrvm.ia32.RegisterConstants.ECX;
import static org.jikesrvm.ia32.RegisterConstants.EDI;
import static org.jikesrvm.ia32.RegisterConstants.EDX;
import static org.jikesrvm.ia32.RegisterConstants.ESI;
import static org.jikesrvm.ia32.RegisterConstants.FP0;
import static org.jikesrvm.ia32.RegisterConstants.JTOC_REGISTER;
import static org.jikesrvm.ia32.RegisterConstants.NONVOLATILE_GPRS;
import static org.jikesrvm.ia32.RegisterConstants.NUM_GPRS;
import static org.jikesrvm.ia32.RegisterConstants.NUM_NONVOLATILE_FPRS;
import static org.jikesrvm.ia32.RegisterConstants.NUM_NONVOLATILE_GPRS;
import static org.jikesrvm.ia32.RegisterConstants.NUM_PARAMETER_FPRS;
import static org.jikesrvm.ia32.RegisterConstants.NUM_PARAMETER_GPRS;
import static org.jikesrvm.ia32.RegisterConstants.THREAD_REGISTER;
import static org.jikesrvm.ia32.RegisterConstants.XMM0;
import static org.jikesrvm.ia32.RegisterConstants.XMM1;
import static org.jikesrvm.ia32.RegisterConstants.XMM2;
import static org.jikesrvm.ia32.RegisterConstants.XMM3;
import static org.jikesrvm.ia32.StackframeLayoutConstants.INVISIBLE_METHOD_ID;
import static org.jikesrvm.ia32.StackframeLayoutConstants.STACKFRAME_BODY_OFFSET;
import static org.jikesrvm.objectmodel.JavaHeaderConstants.ARRAY_LENGTH_BYTES;
import static org.jikesrvm.runtime.JavaSizeConstants.BYTES_IN_DOUBLE;
import org.jikesrvm.VM;
import org.jikesrvm.classloader.RVMField;
import org.jikesrvm.compilers.common.CodeArray;
import org.jikesrvm.compilers.common.assembler.ForwardReference;
import org.jikesrvm.compilers.common.assembler.ia32.Assembler;
import org.jikesrvm.ia32.RegisterConstants.GPR;
import org.jikesrvm.objectmodel.ObjectModel;
import org.jikesrvm.runtime.ArchEntrypoints;
import org.jikesrvm.runtime.EntrypointHelper;
import org.jikesrvm.runtime.Entrypoints;
import org.jikesrvm.scheduler.RVMThread;
import org.vmmagic.pragma.Entrypoint;
import org.vmmagic.unboxed.Offset;
/**
* A place to put hand written machine code typically invoked by Magic
* methods.
*
* <p>Hand coding of small inline instruction sequences is typically handled by
* each compiler's implementation of Magic methods.
* A few Magic methods are so complex that their implementations require
* many instructions. But our compilers do not inline
* arbitrary amounts of machine code. We therefore write such code blocks
* here, out of line.
*
* <p>These code blocks can be shared by all compilers. They can be branched to
* via a jtoc offset (obtained from Entrypoints.XXXInstructionsField).
*/
public abstract class OutOfLineMachineCode {
//-----------//
// interface //
//-----------//
public static void init() {
generatePcThunkInstructions();
reflectiveMethodInvokerInstructions = generateReflectiveMethodInvokerInstructions();
saveThreadStateInstructions = generateSaveThreadStateInstructions();
threadSwitchInstructions = generateThreadSwitchInstructions();
RVMThread.stackTrampolineBridgeInstructions = generateStackTrampolineBridgeInstructions();
restoreHardwareExceptionStateInstructions = generateRestoreHardwareExceptionStateInstructions();
}
//----------------//
// implementation //
//----------------//
public static final RVMField[] pcThunkInstructionsField = new RVMField[8];
@SuppressWarnings({"unused", "UnusedDeclaration", "FieldCanBeLocal"})
// Accessed via field array above
@Entrypoint
private static CodeArray pcThunkEAXInstructions;
@SuppressWarnings({"unused", "UnusedDeclaration", "FieldCanBeLocal"})
// Accessed via field array above
@Entrypoint
private static CodeArray pcThunkEBXInstructions;
@SuppressWarnings({"unused", "UnusedDeclaration", "FieldCanBeLocal"})
// Accessed via field array above
@Entrypoint
private static CodeArray pcThunkECXInstructions;
@SuppressWarnings({"unused", "UnusedDeclaration", "FieldCanBeLocal"})
// Accessed via field array above
@Entrypoint
private static CodeArray pcThunkEDXInstructions;
@SuppressWarnings({"unused", "UnusedDeclaration", "FieldCanBeLocal"})
// Accessed via field array above
@Entrypoint
private static CodeArray pcThunkEBPInstructions;
@SuppressWarnings({"unused", "UnusedDeclaration", "FieldCanBeLocal"})
// Accessed via field array above
@Entrypoint
private static CodeArray pcThunkESIInstructions;
@SuppressWarnings({"unused", "UnusedDeclaration", "FieldCanBeLocal"})
// Accessed via field array above
@Entrypoint
private static CodeArray pcThunkEDIInstructions;
@SuppressWarnings({"unused", "UnusedDeclaration", "FieldCanBeLocal"})
@Entrypoint
private static CodeArray reflectiveMethodInvokerInstructions;
@SuppressWarnings({"unused", "UnusedDeclaration", "FieldCanBeLocal"})
@Entrypoint
private static CodeArray saveThreadStateInstructions;
@SuppressWarnings({"unused", "UnusedDeclaration", "FieldCanBeLocal"})
@Entrypoint
private static CodeArray threadSwitchInstructions;
@SuppressWarnings({"unused", "UnusedDeclaration", "FieldCanBeLocal"})
@Entrypoint
private static CodeArray restoreHardwareExceptionStateInstructions;
private static final Offset PARAMS_FP_OFFSET = Offset.fromIntSignExtend(WORDSIZE * 2);
private static final Offset FPRMETA_FP_OFFSET = Offset.fromIntSignExtend(WORDSIZE * 3);
private static final Offset FPRS_FP_OFFSET = Offset.fromIntSignExtend(WORDSIZE * 4);
private static final Offset GPRS_FP_OFFSET = Offset.fromIntSignExtend(WORDSIZE * 5);
private static final Offset CODE_FP_OFFSET = Offset.fromIntSignExtend(WORDSIZE * 6);
/**
* Machine code to get the address of the instruction after the call to this
* method
*/
private static void generatePcThunkInstructions() {
Assembler asm = new Assembler(0);
asm.emitMOV_Reg_RegInd(EAX, SP);
asm.emitRET();
pcThunkEAXInstructions = asm.getMachineCodes();
pcThunkInstructionsField[EAX.value()] =
EntrypointHelper.getField(OutOfLineMachineCode.class,
"pcThunkEAXInstructions", CodeArray.class);
asm = new Assembler(0);
asm.emitMOV_Reg_RegInd(EBX, SP);
asm.emitRET();
pcThunkEBXInstructions = asm.getMachineCodes();
pcThunkInstructionsField[EBX.value()] =
EntrypointHelper.getField(OutOfLineMachineCode.class,
"pcThunkEBXInstructions", CodeArray.class);
asm = new Assembler(0);
asm.emitMOV_Reg_RegInd(ECX, SP);
asm.emitRET();
pcThunkECXInstructions = asm.getMachineCodes();
pcThunkInstructionsField[ECX.value()] =
EntrypointHelper.getField(OutOfLineMachineCode.class,
"pcThunkECXInstructions", CodeArray.class);
asm = new Assembler(0);
asm.emitMOV_Reg_RegInd(EDX, SP);
asm.emitRET();
pcThunkEDXInstructions = asm.getMachineCodes();
pcThunkInstructionsField[EDX.value()] =
EntrypointHelper.getField(OutOfLineMachineCode.class,
"pcThunkEDXInstructions", CodeArray.class);
// NB a PC thunk into ESP isn't allowed
asm = new Assembler(0);
asm.emitMOV_Reg_RegInd(EBP, SP);
asm.emitRET();
pcThunkEBPInstructions = asm.getMachineCodes();
pcThunkInstructionsField[EBP.value()] =
EntrypointHelper.getField(OutOfLineMachineCode.class,
"pcThunkEBPInstructions", CodeArray.class);
asm = new Assembler(0);
asm.emitMOV_Reg_RegInd(ESI, SP);
asm.emitRET();
pcThunkESIInstructions = asm.getMachineCodes();
pcThunkInstructionsField[ESI.value()] =
EntrypointHelper.getField(OutOfLineMachineCode.class,
"pcThunkESIInstructions", CodeArray.class);
asm = new Assembler(0);
asm.emitMOV_Reg_RegInd(EDI, SP);
asm.emitRET();
pcThunkEDIInstructions = asm.getMachineCodes();
pcThunkInstructionsField[EDI.value()] =
EntrypointHelper.getField(OutOfLineMachineCode.class,
"pcThunkEDIInstructions", CodeArray.class);
}
/**
* Machine code for reflective method invocation.
* <pre>
* VM compiled with NUM_PARAMETERS_GPRS == 0
* Registers taken at runtime:
* none
* Stack taken at runtime:
* hi-mem
* address of method entrypoint to be called
* address of gpr registers to be loaded
* address of fpr registers to be loaded
* address of parameters area in calling frame
* return address
* low-mem
*
* VM compiled with NUM_PARAMETERS_GPRS == 1
* T0 == address of method entrypoint to be called
* Stack taken at runtime:
* hi-mem
* space ???
* address of gpr registers to be loaded
* address of fpr registers to be loaded
* address of parameters area in calling frame
* return address
* low-mem
*
* VM compiled with NUM_PARAMETERS_GPRS == 2
* T0 == address of method entrypoint to be called
* T1 == address of gpr registers to be loaded
* Stack taken at runtime:
* hi-mem
* space ???
* space ???
* address of fpr registers to be loaded
* address of parameters area in calling frame
* return address
* low-mem
*
* Registers returned at runtime:
* standard return value conventions used
*
* Side effects at runtime:
* artificial stackframe created and destroyed
* volatile, and scratch registers destroyed
* </pre>
*
* @return instructions for the reflective method invoker
*/
private static CodeArray generateReflectiveMethodInvokerInstructions() {
Assembler asm = new Assembler(100);
int gprs;
Offset fpOffset = ArchEntrypoints.framePointerField.getOffset();
GPR T = T0;
gprs = NUM_PARAMETER_GPRS;
// we have exactly 5 paramaters, offset 0 from SP is the return address the
// parameters are at offsets 5 to 1
Offset offset = Offset.fromIntZeroExtend(5 << LG_WORDSIZE);
// Write at most 2 parameters from registers in the stack. This is
// logically equivalent to ParamaterRegisterUnload in the compiler
if (gprs > 0) {
gprs--;
if (VM.BuildFor32Addr) {
asm.emitMOV_RegDisp_Reg(SP, offset, T);
} else {
asm.emitMOV_RegDisp_Reg_Quad(SP, offset, T);
}
T = T1;
offset = offset.minus(WORDSIZE);
}
if (gprs > 0) {
if (VM.BuildFor32Addr) {
asm.emitMOV_RegDisp_Reg(SP, offset, T);
} else {
asm.emitMOV_RegDisp_Reg_Quad(SP, offset, T);
}
}
/* available registers S0, T0, T1 */
/* push a new frame */
asm.emitPUSH_RegDisp(TR, fpOffset); // link this frame with next
if (VM.BuildFor32Addr) {
asm.emitMOV_RegDisp_Reg(THREAD_REGISTER, fpOffset, SP); // establish base of new frame
asm.emitPUSH_Imm(INVISIBLE_METHOD_ID);
asm.emitADD_Reg_Imm(SP, STACKFRAME_BODY_OFFSET.toInt());
} else {
asm.emitMOV_RegDisp_Reg_Quad(THREAD_REGISTER, fpOffset, SP); // establish base of new frame
asm.emitPUSH_Imm(INVISIBLE_METHOD_ID);
asm.emitADD_Reg_Imm_Quad(SP, STACKFRAME_BODY_OFFSET.toInt());
}
/* write parameters on stack
* move data from memory addressed by Paramaters array, the fourth
* parameter to this, into the stack.
* SP target address
* S0 source address
* T1 length
* T0 scratch
*/
if (VM.BuildFor32Addr) {
asm.emitMOV_Reg_RegDisp(S0, THREAD_REGISTER, fpOffset);
asm.emitMOV_Reg_RegDisp(S0, S0, PARAMS_FP_OFFSET); // S0 <- Parameters
asm.emitMOV_Reg_RegDisp(T1, S0, ObjectModel.getArrayLengthOffset()); // T1 <- Parameters.length()
asm.emitCMP_Reg_Imm(T1, 0); // length == 0 ?
} else {
asm.emitMOV_Reg_RegDisp_Quad(S0, THREAD_REGISTER, fpOffset);
asm.emitMOV_Reg_RegDisp_Quad(S0, S0, PARAMS_FP_OFFSET);// S0 <- Parameters
if (ARRAY_LENGTH_BYTES == 4) {
asm.emitMOV_Reg_RegDisp(T1, S0, ObjectModel.getArrayLengthOffset()); // T1 <- Parameters.length()
asm.emitCMP_Reg_Imm(T1, 0); // length == 0 ?
} else {
asm.emitMOV_Reg_RegDisp_Quad(T1, S0, ObjectModel.getArrayLengthOffset()); // T1 <- Parameters.length()
asm.emitCMP_Reg_Imm_Quad(T1, 0); // length == 0 ?
}
}
int parameterLoopLabel = asm.getMachineCodeIndex();
ForwardReference fr1 = asm.forwardJcc(EQ); // done? --> branch to end
if (VM.BuildFor32Addr) {
asm.emitMOV_Reg_RegInd(T0, S0); // T0 <- Paramaters[i]
} else {
asm.emitMOV_Reg_RegInd_Quad(T0, S0); // T0 <- Paramaters[i]
}
asm.emitPUSH_Reg(T0); // mem[j++] <- Parameters[i]
if (VM.BuildFor32Addr) {
asm.emitADD_Reg_Imm(S0, WORDSIZE); // i++
} else {
asm.emitADD_Reg_Imm_Quad(S0, WORDSIZE); // i++
}
if (ARRAY_LENGTH_BYTES == 4) {
asm.emitADD_Reg_Imm(T1, -1); // length--
} else {
asm.emitADD_Reg_Imm_Quad(T1, -1); // length--
}
asm.emitJMP_Imm(parameterLoopLabel);
fr1.resolve(asm); // end of the loop
if (SSE2_FULL) {
/* write fprs onto fprs registers */
if (VM.BuildFor32Addr) {
asm.emitMOV_Reg_RegDisp(S0, THREAD_REGISTER, fpOffset);
asm.emitMOV_Reg_RegDisp(T0, S0, FPRS_FP_OFFSET); // T0 <- FPRs
asm.emitMOV_Reg_RegDisp(T1, T0, ObjectModel.getArrayLengthOffset()); // T1 <- FPRs.length()
asm.emitMOV_Reg_RegDisp(S0, S0, FPRMETA_FP_OFFSET); // S0 <- FPRmeta
} else {
asm.emitMOV_Reg_RegDisp_Quad(S0, THREAD_REGISTER, fpOffset);
asm.emitMOV_Reg_RegDisp_Quad(T0, S0, FPRS_FP_OFFSET); // T0 <- FPRs
if (ARRAY_LENGTH_BYTES == 4) {
asm.emitMOV_Reg_RegDisp(T1, T0, ObjectModel.getArrayLengthOffset()); // T1 <- FPRs.length()
} else {
asm.emitMOV_Reg_RegDisp_Quad(T1, T0, ObjectModel.getArrayLengthOffset()); // T1 <- FPRs.length()
}
asm.emitMOV_Reg_RegDisp_Quad(S0, S0, FPRMETA_FP_OFFSET); // S0 <- FPRmeta
}
if (VM.VerifyAssertions) VM._assert(NUM_PARAMETER_FPRS <= 4);
ForwardReference fr_next;
asm.emitCMP_Reg_Imm(T1, 0); // length == 0 ?
ForwardReference fpr_r1 = asm.forwardJcc(EQ);
asm.emitMOVSD_Reg_RegInd(XMM0, T0);
asm.emitCMP_RegInd_Imm_Byte(S0, 0);
fr_next = asm.forwardJcc(NE);
asm.emitCVTSD2SS_Reg_Reg(XMM0, XMM0);
fr_next.resolve(asm);
asm.emitSUB_Reg_Imm(T1, 1); // length == 0 ?
ForwardReference fpr_r2 = asm.forwardJcc(EQ);
asm.emitMOVSD_Reg_RegDisp(XMM1, T0, Offset.fromIntZeroExtend(BYTES_IN_DOUBLE));
asm.emitCMP_RegDisp_Imm_Byte(S0, Offset.fromIntZeroExtend(1), 0);
fr_next = asm.forwardJcc(NE);
asm.emitCVTSD2SS_Reg_Reg(XMM1, XMM1);
fr_next.resolve(asm);
asm.emitSUB_Reg_Imm(T1, 1); // length == 0 ?
ForwardReference fpr_r3 = asm.forwardJcc(EQ);
asm.emitMOVSD_Reg_RegDisp(XMM2, T0, Offset.fromIntZeroExtend(BYTES_IN_DOUBLE * 2));
asm.emitCMP_RegDisp_Imm_Byte(S0, Offset.fromIntZeroExtend(2), 0);
fr_next = asm.forwardJcc(NE);
asm.emitCVTSD2SS_Reg_Reg(XMM2, XMM2);
fr_next.resolve(asm);
asm.emitSUB_Reg_Imm(T1, 1); // length == 0 ?
ForwardReference fpr_r4 = asm.forwardJcc(EQ);
asm.emitMOVSD_Reg_RegDisp(XMM3, T0, Offset.fromIntZeroExtend(BYTES_IN_DOUBLE * 3));
asm.emitCMP_RegDisp_Imm_Byte(S0, Offset.fromIntZeroExtend(3), 0);
fr_next = asm.forwardJcc(NE);
asm.emitCVTSD2SS_Reg_Reg(XMM3, XMM3);
fr_next.resolve(asm);
fpr_r1.resolve(asm);
fpr_r2.resolve(asm);
fpr_r3.resolve(asm);
fpr_r4.resolve(asm);
} else {
if (VM.VerifyAssertions) VM._assert(VM.BuildFor32Addr);
/* write fprs onto fprs registers */
if (VM.BuildFor32Addr) {
asm.emitMOV_Reg_RegDisp(S0, THREAD_REGISTER, fpOffset);
} else {
asm.emitMOV_Reg_RegDisp_Quad(S0, THREAD_REGISTER, fpOffset);
}
asm.emitMOV_Reg_RegDisp(S0, S0, FPRS_FP_OFFSET); // S0 <- FPRs
asm.emitMOV_Reg_RegDisp(T1, S0, ObjectModel.getArrayLengthOffset()); // T1 <- FPRs.length()
asm.emitSHL_Reg_Imm(T1, LG_WORDSIZE + 1); // length in bytes
asm.emitADD_Reg_Reg(S0, T1); // S0 <- last FPR + 8
asm.emitCMP_Reg_Imm(T1, 0); // length == 0 ?
int fprsLoopLabel = asm.getMachineCodeIndex();
ForwardReference fr2 = asm.forwardJcc(EQ); // done? --> branch to end
asm.emitSUB_Reg_Imm(S0, 2 * WORDSIZE); // i--
asm.emitFLD_Reg_RegInd_Quad(FP0, S0); // frp[fpr_sp++] <-FPRs[i]
asm.emitSUB_Reg_Imm(T1, 2 * WORDSIZE); // length--
asm.emitJMP_Imm(fprsLoopLabel);
fr2.resolve(asm); // end of the loop
}
/* write gprs: S0 = Base address of GPRs[], T1 = GPRs.length */
if (VM.BuildFor32Addr) {
asm.emitMOV_Reg_RegDisp(S0, THREAD_REGISTER, fpOffset);
asm.emitMOV_Reg_RegDisp(S0, S0, GPRS_FP_OFFSET); // S0 <- GPRs
asm.emitMOV_Reg_RegDisp(T1, S0, ObjectModel.getArrayLengthOffset()); // T1 <- GPRs.length()
asm.emitCMP_Reg_Imm(T1, 0); // length == 0 ?
} else {
asm.emitMOV_Reg_RegDisp_Quad(S0, THREAD_REGISTER, fpOffset);
asm.emitMOV_Reg_RegDisp_Quad(S0, S0, GPRS_FP_OFFSET); // S0 <- GPRs
if (ARRAY_LENGTH_BYTES == 4) {
asm.emitMOV_Reg_RegDisp(T1, S0, ObjectModel.getArrayLengthOffset()); // T1 <- GPRs.length()
asm.emitCMP_Reg_Imm(T1, 0); // length == 0 ?
} else {
asm.emitMOV_Reg_RegDisp_Quad(T1, S0, ObjectModel.getArrayLengthOffset()); // T1 <- GPRs.length()
asm.emitCMP_Reg_Imm_Quad(T1, 0); // length == 0 ?
}
}
ForwardReference fr3 = asm.forwardJcc(EQ); // result 0 --> branch to end
if (VM.BuildFor32Addr) {
asm.emitMOV_Reg_RegInd(T0, S0); // T0 <- GPRs[0]
asm.emitADD_Reg_Imm(S0, WORDSIZE); // S0 += WORDSIZE
asm.emitADD_Reg_Imm(T1, -1); // T1--
} else {
asm.emitMOV_Reg_RegInd_Quad(T0, S0); // T0 <- GPRs[0]
asm.emitADD_Reg_Imm_Quad(S0, WORDSIZE); // S0 += WORDSIZE
asm.emitADD_Reg_Imm_Quad(T1, -1); // T1--
}
ForwardReference fr4 = asm.forwardJcc(EQ); // result 0 --> branch to end
if (VM.BuildFor32Addr) {
asm.emitMOV_Reg_RegInd(T1, S0); // T1 <- GPRs[1]
} else {
asm.emitMOV_Reg_RegInd_Quad(T1, S0); // T1 <- GPRs[1]
}
fr3.resolve(asm);
fr4.resolve(asm);
/* branch to method. On a good day we might even be back */
if (VM.BuildFor32Addr) {
asm.emitMOV_Reg_RegDisp(S0, THREAD_REGISTER, fpOffset);
asm.emitMOV_Reg_RegDisp(S0, S0, CODE_FP_OFFSET); // S0 <- code
} else {
asm.emitMOV_Reg_RegDisp_Quad(S0, THREAD_REGISTER, fpOffset);
asm.emitMOV_Reg_RegDisp_Quad(S0, S0, CODE_FP_OFFSET); // S0 <- code
}
asm.emitCALL_Reg(S0); // go there
// T0/T1 have returned value
/* and get out */
// NOTE: RVM callee has popped the params, so we can simply
// add back in the initial SP to FP delta to get SP to be a framepointer again!
if (VM.BuildFor32Addr) {
asm.emitADD_Reg_Imm(SP, -STACKFRAME_BODY_OFFSET.toInt() + WORDSIZE);
} else {
asm.emitADD_Reg_Imm_Quad(SP, -STACKFRAME_BODY_OFFSET.toInt() + WORDSIZE);
}
asm.emitPOP_RegDisp(TR, fpOffset);
asm.emitRET_Imm(5 << LG_WORDSIZE); // again, exactly 5 parameters
return asm.getMachineCodes();
}
/**
* Machine code to implement "Magic.saveThreadState()".
* <pre>
* Registers taken at runtime:
* T0 == address of Registers object
*
* Registers returned at runtime:
* none
*
* Side effects at runtime:
* S0, T1 destroyed
* Thread state stored into Registers object
* </pre>
*
* @return instructions for saving the thread state
*/
private static CodeArray generateSaveThreadStateInstructions() {
if (VM.VerifyAssertions) {
VM._assert(NUM_NONVOLATILE_FPRS == 0); // assuming no NV FPRs (otherwise would have to save them here)
}
Assembler asm = new Assembler(0);
Offset ipOffset = ArchEntrypoints.registersIPField.getOffset();
Offset fpOffset = ArchEntrypoints.registersFPField.getOffset();
Offset gprsOffset = ArchEntrypoints.registersGPRsField.getOffset();
if (VM.BuildFor32Addr) {
asm.emitMOV_Reg_RegDisp(S0, TR, ArchEntrypoints.framePointerField.getOffset());
asm.emitMOV_RegDisp_Reg(T0, fpOffset, S0); // registers.fp := pr.framePointer
} else {
asm.emitMOV_Reg_RegDisp_Quad(S0, TR, ArchEntrypoints.framePointerField.getOffset());
asm.emitMOV_RegDisp_Reg_Quad(T0, fpOffset, S0); // registers.fp := pr.framePointer
}
asm.emitPOP_Reg(T1); // T1 := return address (target of final jmp)
if (VM.BuildFor32Addr) {
asm.emitMOV_RegDisp_Reg(T0, ipOffset, T1); // registers.ip := return address
} else {
asm.emitMOV_RegDisp_Reg_Quad(T0, ipOffset, T1); // registers.ip := return address
}
asm.emitPOP_Reg(S0); // throw away space for registers parameter (in T0)
if (VM.BuildFor32Addr) {
asm.emitMOV_Reg_RegDisp(S0, T0, gprsOffset); // S0 := registers.gprs[]
asm.emitMOV_RegDisp_Reg(S0, Offset.fromIntZeroExtend(SP.value() << LG_WORDSIZE), SP); // registers.gprs[#SP] := SP
for (int i = 0; i < NUM_NONVOLATILE_GPRS; i++) {
asm.emitMOV_RegDisp_Reg(S0,
Offset.fromIntZeroExtend(NONVOLATILE_GPRS[i].value() << LG_WORDSIZE),
NONVOLATILE_GPRS[i]); // registers.gprs[i] := i'th register
}
} else {
asm.emitMOV_Reg_RegDisp_Quad(S0, T0, gprsOffset); // S0 := registers.gprs[]
asm.emitMOV_RegDisp_Reg_Quad(S0, Offset.fromIntZeroExtend(SP.value() << LG_WORDSIZE), SP); // registers.gprs[#SP] := SP
for (int i = 0; i < NUM_NONVOLATILE_GPRS; i++) {
asm.emitMOV_RegDisp_Reg_Quad(S0,
Offset.fromIntZeroExtend(NONVOLATILE_GPRS[i].value() << LG_WORDSIZE),
NONVOLATILE_GPRS[i]); // registers.gprs[i] := i'th register
}
}
asm.emitJMP_Reg(T1); // return to return address
return asm.getMachineCodes();
}
/**
* Machine code to perform a stack trampoline bridge for
* implementing a return barrier.
* <p>
* This code is used to hijack a return and bridge to some
* other method (which implements the return barrier) before
* falling back to the intended return address.
* <p>
* The key here is to preserve register and stack state so that
* the caller is oblivious of the detour that occurred during
* the return.
*
* @return instructions for the stack trampoline bridge
*/
private static CodeArray generateStackTrampolineBridgeInstructions() {
if (VM.VerifyAssertions) {
VM._assert(NUM_NONVOLATILE_FPRS == 0); // assuming no NV FPRs (otherwise would have to save them here)
}
Assembler asm = new Assembler(0);
/* push the hijacked return address (which is held in thread-local state) */
asm.emitPUSH_RegDisp(TR, ArchEntrypoints.hijackedReturnAddressField.getOffset());
/* push the GPRs and fp */
for (int i = 0; i < NUM_GPRS; i++) {
asm.emitPUSH_Reg(ALL_GPRS[i]);
}
asm.emitPUSH_RegDisp(TR, ArchEntrypoints.framePointerField.getOffset());
/* call the handler */
asm.generateJTOCcall(Entrypoints.returnBarrierMethod.getOffset());
/* pop the fp and GPRs */
asm.emitPOP_RegDisp(TR, ArchEntrypoints.framePointerField.getOffset());
for (int i = NUM_GPRS - 1; i >= 0; i--) {
asm.emitPOP_Reg(ALL_GPRS[i]);
}
/* pop the hijacked return address and return */
asm.emitRET();
return asm.getMachineCodes();
}
/**
* Machine code to implement "Magic.threadSwitch()".
* <pre>
* NOTE: Currently not functional for PNT: left as a guide for possible reimplementation.
*
* Parameters taken at runtime:
* T0 == address of Thread object for the current thread
* T1 == address of Registers object for the new thread
*
* Registers returned at runtime:
* none
*
* Side effects at runtime:
* sets current Thread's beingDispatched field to false
* saves current Thread's nonvolatile hardware state in its Registers object
* restores new thread's Registers nonvolatile hardware state.
* execution resumes at address specificed by restored thread's Registers ip field
* </pre>
*
* @return instructions for doing a thread switch
*/
private static CodeArray generateThreadSwitchInstructions() {
if (VM.VerifyAssertions) {
VM._assert(NUM_NONVOLATILE_FPRS == 0); // assuming no NV FPRs (otherwise would have to save them here)
}
Assembler asm = new Assembler(0);
Offset ipOffset = ArchEntrypoints.registersIPField.getOffset();
Offset fpOffset = ArchEntrypoints.registersFPField.getOffset();
Offset gprsOffset = ArchEntrypoints.registersGPRsField.getOffset();
Offset regsOffset = Entrypoints.threadContextRegistersField.getOffset();
// (1) Save hardware state of thread we are switching off of.
if (VM.BuildFor32Addr) {
asm.emitMOV_Reg_RegDisp(S0, T0, regsOffset); // S0 = T0.contextRegisters
} else {
asm.emitMOV_Reg_RegDisp_Quad(S0, T0, regsOffset); // S0 = T0.contextRegisters
}
asm.emitPOP_RegDisp(S0, ipOffset); // T0.contextRegisters.ip = returnAddress
asm.emitPUSH_RegDisp(TR, ArchEntrypoints.framePointerField.getOffset()); // push TR.framePointer
asm.emitPOP_RegDisp(S0, fpOffset); // T0.contextRegisters.fp = pushed framepointer
if (VM.BuildFor32Addr) {
asm.emitADD_Reg_Imm(SP, 2 * WORDSIZE); // discard 2 words of parameters (T0, T1)
asm.emitMOV_Reg_RegDisp(S0, S0, gprsOffset); // S0 = T0.contextRegisters.gprs;
asm.emitMOV_RegDisp_Reg(S0, Offset.fromIntZeroExtend(SP.value() << LG_WORDSIZE), SP); // T0.contextRegisters.gprs[#SP] := SP
for (int i = 0; i < NUM_NONVOLATILE_GPRS; i++) {
// T0.contextRegisters.gprs[i] := i'th register
asm.emitMOV_RegDisp_Reg(S0,
Offset.fromIntZeroExtend(NONVOLATILE_GPRS[i].value() << LG_WORDSIZE),
NONVOLATILE_GPRS[i]);
}
} else {
asm.emitADD_Reg_Imm_Quad(SP, 2 * WORDSIZE); // discard 2 words of parameters (T0, T1)
asm.emitMOV_Reg_RegDisp_Quad(S0, S0, gprsOffset); // S0 = T0.contextRegisters.gprs;
asm.emitMOV_RegDisp_Reg_Quad(S0, Offset.fromIntZeroExtend(SP.value() << LG_WORDSIZE), SP); // T0.contextRegisters.gprs[#SP] := SP
for (int i = 0; i < NUM_NONVOLATILE_GPRS; i++) {
// T0.contextRegisters.gprs[i] := i'th register
asm.emitMOV_RegDisp_Reg_Quad(S0,
Offset.fromIntZeroExtend(NONVOLATILE_GPRS[i].value() << LG_WORDSIZE),
NONVOLATILE_GPRS[i]);
}
}
// (2) Set currentThread.beingDispatched to false
// PNT: don't have this field anymore
//asm.emitMOV_RegDisp_Imm_Byte(T0,
// Entrypoints.beingDispatchedField.getOffset(),
// 0); // previous thread's stack is nolonger in use, so it can now be dispatched on any virtual processor
// (3) Restore hardware state of thread we are switching to.
if (VM.BuildFor32Addr) {
asm.emitMOV_Reg_RegDisp(S0, T1, fpOffset); // S0 := restoreRegs.fp
} else {
asm.emitMOV_Reg_RegDisp_Quad(S0, T1, fpOffset); // S0 := restoreRegs.fp
}
// TR.framePointer = restoreRegs.fp
if (VM.BuildFor32Addr) {
asm.emitMOV_RegDisp_Reg(THREAD_REGISTER, ArchEntrypoints.framePointerField.getOffset(), S0);
asm.emitMOV_Reg_RegDisp(S0, T1, gprsOffset); // S0 := restoreRegs.gprs[]
asm.emitMOV_Reg_RegDisp(SP, S0, Offset.fromIntZeroExtend(SP.value() << LG_WORDSIZE)); // SP := restoreRegs.gprs[#SP]
for (int i = 0; i < NUM_NONVOLATILE_GPRS; i++) {
// i'th register := restoreRegs.gprs[i]
asm.emitMOV_Reg_RegDisp(NONVOLATILE_GPRS[i],
S0,
Offset.fromIntZeroExtend(NONVOLATILE_GPRS[i].value() <<
LG_WORDSIZE));
}
} else {
asm.emitMOV_RegDisp_Reg_Quad(THREAD_REGISTER, ArchEntrypoints.framePointerField.getOffset(), S0);
asm.emitMOV_Reg_RegDisp_Quad(S0, T1, gprsOffset); // S0 := restoreRegs.gprs[]
asm.emitMOV_Reg_RegDisp_Quad(SP, S0, Offset.fromIntZeroExtend(SP.value() << LG_WORDSIZE)); // SP := restoreRegs.gprs[#SP]
for (int i = 0; i < NUM_NONVOLATILE_GPRS; i++) {
// i'th register := restoreRegs.gprs[i]
asm.emitMOV_Reg_RegDisp_Quad(NONVOLATILE_GPRS[i],
S0,
Offset.fromIntZeroExtend(NONVOLATILE_GPRS[i].value() << LG_WORDSIZE));
}
}
asm.emitJMP_RegDisp(T1, ipOffset); // return to (save) return address
return asm.getMachineCodes();
}
/**
* Machine code to implement "Magic.restoreHardwareExceptionState()".
* <pre>
* Registers taken at runtime:
* T0 == address of Registers object
*
* Registers returned at runtime:
* none
*
* Side effects at runtime:
* all registers are restored except THREAD_REGISTER and EFLAGS;
* execution resumes at "registers.ip"
* </pre>
*
* @return instructions to restore the hardware exception state
*/
private static CodeArray generateRestoreHardwareExceptionStateInstructions() {
Assembler asm = new Assembler(0);
// Set TR.framePointer to be registers.fp
if (VM.BuildFor32Addr) {
asm.emitMOV_Reg_RegDisp(S0, T0, ArchEntrypoints.registersFPField.getOffset());
asm.emitMOV_RegDisp_Reg(THREAD_REGISTER, ArchEntrypoints.framePointerField.getOffset(), S0);
} else {
asm.emitMOV_Reg_RegDisp_Quad(S0, T0, ArchEntrypoints.registersFPField.getOffset());
asm.emitMOV_RegDisp_Reg_Quad(THREAD_REGISTER, ArchEntrypoints.framePointerField.getOffset(), S0);
}
// Restore SP
if (VM.BuildFor32Addr) {
asm.emitMOV_Reg_RegDisp(S0, T0, ArchEntrypoints.registersGPRsField.getOffset());
asm.emitMOV_Reg_RegDisp(SP, S0, Offset.fromIntZeroExtend(SP.value() << LG_WORDSIZE));
} else {
asm.emitMOV_Reg_RegDisp_Quad(S0, T0, ArchEntrypoints.registersGPRsField.getOffset());
asm.emitMOV_Reg_RegDisp_Quad(SP, S0, Offset.fromIntZeroExtend(SP.value() << LG_WORDSIZE));
}
// Push registers.ip to stack (now that SP has been restored)
asm.emitPUSH_RegDisp(T0, ArchEntrypoints.registersIPField.getOffset());
// Restore the GPRs except for S0, TR, SP and JTOC
// (restored above and then modified by pushing registers.ip!)
Offset off = Offset.zero();
for (byte i = 0; i < NUM_GPRS; i++, off = off.plus(WORDSIZE)) {
if (i != S0.value() && i != ESI.value() && i != SP.value() &&
(JTOC_REGISTER == null || i != JTOC_REGISTER.value())) {
if (VM.BuildFor32Addr) {
asm.emitMOV_Reg_RegDisp(GPR.lookup(i), S0, off);
} else {
asm.emitMOV_Reg_RegDisp_Quad(GPR.lookup(i), S0, off);
}
}
}
// Restore S0
if (VM.BuildFor32Addr) {
asm.emitMOV_Reg_RegDisp(S0, S0, Offset.fromIntZeroExtend(S0.value() << LG_WORDSIZE));
} else {
asm.emitMOV_Reg_RegDisp_Quad(S0, S0, Offset.fromIntZeroExtend(S0.value() << LG_WORDSIZE));
}
// Return to registers.ip (popping stack)
asm.emitRET();
return asm.getMachineCodes();
}
}