/* * 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.compilers.baseline.ia32; import static org.jikesrvm.classloader.ClassLoaderConstants.CP_CLASS; import static org.jikesrvm.classloader.ClassLoaderConstants.CP_STRING; import static org.jikesrvm.compilers.common.assembler.ia32.AssemblerConstants.*; import static org.jikesrvm.ia32.ArchConstants.SSE2_BASE; import static org.jikesrvm.ia32.ArchConstants.SSE2_FULL; import static org.jikesrvm.ia32.BaselineConstants.EBP_SAVE_OFFSET; import static org.jikesrvm.ia32.BaselineConstants.EBX_SAVE_OFFSET; import static org.jikesrvm.ia32.BaselineConstants.EDI_SAVE_OFFSET; import static org.jikesrvm.ia32.BaselineConstants.FPU_SAVE_OFFSET; import static org.jikesrvm.ia32.BaselineConstants.LG_WORDSIZE; import static org.jikesrvm.ia32.BaselineConstants.S0; import static org.jikesrvm.ia32.BaselineConstants.S1; import static org.jikesrvm.ia32.BaselineConstants.SAVED_GPRS; import static org.jikesrvm.ia32.BaselineConstants.SAVED_GPRS_FOR_SAVE_LS_REGISTERS; import static org.jikesrvm.ia32.BaselineConstants.SP; import static org.jikesrvm.ia32.BaselineConstants.T0; import static org.jikesrvm.ia32.BaselineConstants.T0_SAVE_OFFSET; import static org.jikesrvm.ia32.BaselineConstants.T1; import static org.jikesrvm.ia32.BaselineConstants.T1_SAVE_OFFSET; import static org.jikesrvm.ia32.BaselineConstants.TR; import static org.jikesrvm.ia32.BaselineConstants.WORDSIZE; import static org.jikesrvm.ia32.BaselineConstants.XMM_SAVE_OFFSET; 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.ESP; import static org.jikesrvm.ia32.RegisterConstants.FP0; import static org.jikesrvm.ia32.RegisterConstants.FP1; import static org.jikesrvm.ia32.RegisterConstants.NATIVE_PARAMETER_FPRS; import static org.jikesrvm.ia32.RegisterConstants.NATIVE_PARAMETER_GPRS; import static org.jikesrvm.ia32.RegisterConstants.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.X87_FPU_STATE_SIZE; import static org.jikesrvm.ia32.StackframeLayoutConstants.STACKFRAME_BODY_OFFSET; import static org.jikesrvm.ia32.StackframeLayoutConstants.STACKFRAME_HEADER_SIZE; import static org.jikesrvm.ia32.StackframeLayoutConstants.STACKFRAME_METHOD_ID_OFFSET; import static org.jikesrvm.ia32.StackframeLayoutConstants.BASELINE_XMM_STATE_SIZE; import static org.jikesrvm.ia32.TrapConstants.RVM_TRAP_BASE; import static org.jikesrvm.mm.mminterface.Barriers.*; import static org.jikesrvm.objectmodel.JavaHeaderConstants.ARRAY_LENGTH_BYTES; import static org.jikesrvm.objectmodel.TIBLayoutConstants.NEEDS_DYNAMIC_LINK; import static org.jikesrvm.objectmodel.TIBLayoutConstants.TIB_DOES_IMPLEMENT_INDEX; import static org.jikesrvm.objectmodel.TIBLayoutConstants.TIB_INTERFACE_DISPATCH_TABLE_INDEX; import static org.jikesrvm.objectmodel.TIBLayoutConstants.TIB_SUPERCLASS_IDS_INDEX; import static org.jikesrvm.runtime.JavaSizeConstants.BYTES_IN_BYTE; import static org.jikesrvm.runtime.JavaSizeConstants.BYTES_IN_INT; import static org.jikesrvm.runtime.JavaSizeConstants.BYTES_IN_LONG; import static org.jikesrvm.runtime.JavaSizeConstants.BYTES_IN_SHORT; import static org.jikesrvm.runtime.JavaSizeConstants.LOG_BYTES_IN_INT; import static org.jikesrvm.runtime.JavaSizeConstants.LOG_BYTES_IN_SHORT; import static org.jikesrvm.runtime.RuntimeEntrypoints.TRAP_UNREACHABLE_BYTECODE; import org.jikesrvm.VM; import org.jikesrvm.adaptive.AosEntrypoints; import org.jikesrvm.adaptive.recompilation.InvocationCounts; import org.jikesrvm.classloader.DynamicTypeCheck; import org.jikesrvm.classloader.FieldReference; import org.jikesrvm.classloader.InterfaceInvocation; import org.jikesrvm.classloader.InterfaceMethodSignature; import org.jikesrvm.classloader.MemberReference; import org.jikesrvm.classloader.MethodReference; import org.jikesrvm.classloader.NormalMethod; import org.jikesrvm.classloader.RVMArray; import org.jikesrvm.classloader.RVMClass; import org.jikesrvm.classloader.RVMField; import org.jikesrvm.classloader.RVMMethod; import org.jikesrvm.classloader.RVMType; import org.jikesrvm.classloader.TypeReference; import org.jikesrvm.compilers.baseline.BaselineCompiledMethod; import org.jikesrvm.compilers.baseline.BaselineCompiler; import org.jikesrvm.compilers.baseline.EdgeCounts; import org.jikesrvm.compilers.baseline.TemplateCompilerFramework; import org.jikesrvm.compilers.common.CompiledMethod; import org.jikesrvm.compilers.common.assembler.AbstractAssembler; import org.jikesrvm.compilers.common.assembler.AbstractLister; import org.jikesrvm.compilers.common.assembler.ForwardReference; import org.jikesrvm.compilers.common.assembler.ia32.Assembler; import org.jikesrvm.compilers.common.assembler.ia32.Lister; import org.jikesrvm.ia32.RegisterConstants.GPR; import org.jikesrvm.ia32.RegisterConstants.XMM; import org.jikesrvm.jni.ia32.JNICompiler; import org.jikesrvm.mm.mminterface.MemoryManager; import org.jikesrvm.objectmodel.ObjectModel; import org.jikesrvm.runtime.ArchEntrypoints; import org.jikesrvm.runtime.Entrypoints; import org.jikesrvm.runtime.Magic; import org.jikesrvm.runtime.RuntimeEntrypoints; import org.jikesrvm.runtime.Statics; import org.jikesrvm.scheduler.RVMThread; import org.vmmagic.pragma.Inline; import org.vmmagic.pragma.Pure; import org.vmmagic.pragma.Uninterruptible; import org.vmmagic.unboxed.Offset; /** * BaselineCompilerImpl is the baseline compiler implementation for the IA32 architecture. */ public final class BaselineCompilerImpl extends BaselineCompiler { private final Assembler asm; private final Lister lister; static { // Force resolution of BaselineMagic before using in genMagic Object x = BaselineMagic.generateMagic(null, null, null, Offset.zero()); } private final int parameterWords; private Offset firstLocalOffset; static final Offset NO_SLOT = Offset.zero(); static final Offset ONE_SLOT = NO_SLOT.plus(WORDSIZE); static final Offset TWO_SLOTS = ONE_SLOT.plus(WORDSIZE); static final Offset THREE_SLOTS = TWO_SLOTS.plus(WORDSIZE); static final Offset FOUR_SLOTS = THREE_SLOTS.plus(WORDSIZE); static final Offset FIVE_SLOTS = FOUR_SLOTS.plus(WORDSIZE); private static final Offset MINUS_ONE_SLOT = NO_SLOT.minus(WORDSIZE); /** * Create a BaselineCompilerImpl object for the compilation of method. * * @param cm the method that will be associated with this compilation */ public BaselineCompilerImpl(BaselineCompiledMethod cm) { super(cm); stackHeights = new int[bcodes.length()]; parameterWords = method.getParameterWords() + (method.isStatic() ? 0 : 1); // add 1 for this pointer asm = new Assembler(bcodes.length(),shouldPrint, this); lister = asm.getLister(); } @Override protected AbstractAssembler getAssembler() { return asm; } @Override protected AbstractLister getLister() { return lister; } /** * Have we encountered a bytecode without valid stack heights? if so throw this exception */ private static final class UnreachableBytecodeException extends Exception { private static final long serialVersionUID = 8300835844142105706L; UnreachableBytecodeException() {} } @Override protected void initializeCompiler() { //nothing to do for Intel } @Uninterruptible public static int locationToOffset(short location) { return -location; } @Uninterruptible public static short offsetToLocation(Offset offset) { return (short)-offset.toInt(); } @Uninterruptible public static short offsetToLocation(int offset) { return (short)-offset; } @Uninterruptible static short getEmptyStackOffset(NormalMethod m) { return (short)getFirstLocalOffset(m).minus(m.getLocalWords() << LG_WORDSIZE).plus(WORDSIZE).toInt(); } /** * @param method the method in question * * @return offset of first parameter */ @Uninterruptible private static Offset getFirstLocalOffset(NormalMethod method) { if (method.getDeclaringClass().hasBridgeFromNativeAnnotation()) { return STACKFRAME_BODY_OFFSET.minus(JNICompiler.SAVED_GPRS_FOR_JNI << LG_WORDSIZE); } else if (method.hasBaselineSaveLSRegistersAnnotation()) { return STACKFRAME_BODY_OFFSET.minus(SAVED_GPRS_FOR_SAVE_LS_REGISTERS << LG_WORDSIZE); } else { return STACKFRAME_BODY_OFFSET.minus(SAVED_GPRS << LG_WORDSIZE); } } @Uninterruptible static Offset getStartLocalOffset(NormalMethod method) { return getFirstLocalOffset(method).plus(WORDSIZE); } /** * Adjust the value of ESP/RSP * * @param size amount to change ESP/RSP by * @param mayClobber can the value in S0 or memory be destroyed? * (i.e. can we use a destructive short push/pop opcode) */ private void adjustStack(int size, boolean mayClobber) { final boolean debug = false; if (size != 0) { if (mayClobber) { // first try short opcodes switch(size >> LG_WORDSIZE) { case -2: if (debug) { asm.emitPUSH_Imm(0xFA1FACE); asm.emitPUSH_Imm(0xFA2FACE); } else { asm.emitPUSH_Reg(EAX); asm.emitPUSH_Reg(EAX); } return; case -1: if (debug) { asm.emitPUSH_Imm(0xFA3FACE); } else { asm.emitPUSH_Reg(EAX); } return; case 1: asm.emitPOP_Reg(S1); if (debug) { asm.emitMOV_Reg_Imm(S1, 0xFA4FACE); } return; case 2: asm.emitPOP_Reg(S1); asm.emitPOP_Reg(S1); if (debug) { asm.emitMOV_Reg_Imm(S1, 0xFA5FACE); } return; } } if (VM.BuildFor32Addr) { asm.emitADD_Reg_Imm(SP, size); } else { asm.emitADD_Reg_Imm_Quad(SP, size); } } } /** * Move a value from the stack into a register using the shortest encoding and * the appropriate width for 32/64 * * @param dest register to load into * @param off offset on stack */ private void stackMoveHelper(GPR dest, Offset off) { stackMoveHelper(asm, dest, off); } /** * Move a value from the stack into a register using the shortest encoding and * the appropriate width for 32/64 * * @param asm the assembler instance * @param dest register to load into * @param off offset on stack */ private static void stackMoveHelper(Assembler asm, GPR dest, Offset off) { if (WORDSIZE == 4) { if (off.isZero()) { asm.emitMOV_Reg_RegInd(dest, SP); } else { asm.emitMOV_Reg_RegDisp(dest, SP, off); } } else { if (off.isZero()) { asm.emitMOV_Reg_RegInd_Quad(dest, SP); } else { asm.emitMOV_Reg_RegDisp_Quad(dest, SP, off); } } } /* * implementation of abstract methods of BaselineCompiler */ /** * Nothing to do on IA32. */ @Override protected void starting_bytecode() {} @Override protected void emit_prologue() { genPrologue(); } @Override protected void emit_threadSwitchTest(int whereFrom) { genThreadSwitchTest(whereFrom); } @Override protected boolean emit_Magic(MethodReference magicMethod) { return genMagic(magicMethod); } /* * Loading constants */ @Override protected void emit_aconst_null() { asm.emitPUSH_Imm(0); } @Override protected void emit_iconst(int val) { asm.emitPUSH_Imm(val); } @Override protected void emit_lconst(int val) { asm.emitPUSH_Imm(0); // high part asm.emitPUSH_Imm(val); // low part } @Override protected void emit_fconst_0() { asm.emitPUSH_Imm(0); } @Override protected void emit_fconst_1() { asm.emitPUSH_Imm(0x3f800000); } @Override protected void emit_fconst_2() { asm.emitPUSH_Imm(0x40000000); } @Override protected void emit_dconst_0() { if (VM.BuildFor32Addr) { asm.emitPUSH_Imm(0x00000000); asm.emitPUSH_Imm(0x00000000); } else { adjustStack(-WORDSIZE, true); asm.emitPUSH_Imm(0x00000000); } } @Override protected void emit_dconst_1() { if (VM.BuildFor32Addr) { asm.emitPUSH_Imm(0x3ff00000); asm.emitPUSH_Imm(0x00000000); } else { adjustStack(-WORDSIZE, true); asm.generateJTOCpush(Entrypoints.oneDoubleField.getOffset()); } } @Override protected void emit_ldc(Offset offset, byte type) { if (VM.BuildFor32Addr || (type == CP_CLASS) || (type == CP_STRING)) { asm.generateJTOCpush(offset); } else { asm.generateJTOCloadInt(T0, offset); asm.emitPUSH_Reg(T0); } } @Override protected void emit_ldc2(Offset offset, byte type) { if (VM.BuildFor32Addr) { asm.emitPUSH_Abs(Magic.getTocPointer().plus(offset).plus(WORDSIZE)); // high 32 bits asm.emitPUSH_Abs(Magic.getTocPointer().plus(offset)); // low 32 bits } else { adjustStack(-WORDSIZE, true); asm.generateJTOCpush(offset); } } /* * loading local variables */ @Override protected void emit_regular_iload(int index) { try { Offset offset = localOffset(index); if (offset.EQ(Offset.zero())) { asm.emitPUSH_RegInd(ESP); } else { asm.emitPUSH_RegDisp(ESP, offset); } } catch (UnreachableBytecodeException e) { asm.emitINT_Imm(TRAP_UNREACHABLE_BYTECODE + RVM_TRAP_BASE); } } @Override protected void emit_fload(int index) { // identical to iload emit_regular_iload(index); } @Override protected void emit_regular_aload(int index) { // identical to iload emit_regular_iload(index); } @Override protected void emit_lload(int index) { try { Offset offset = localOffset(index); if (VM.BuildFor32Addr) { asm.emitPUSH_RegDisp(ESP, offset); // high part asm.emitPUSH_RegDisp(ESP, offset); // low part (ESP has moved by 4!!) } else { adjustStack(-WORDSIZE, true); asm.emitPUSH_RegDisp(ESP, offset); } } catch (UnreachableBytecodeException e) { asm.emitINT_Imm(TRAP_UNREACHABLE_BYTECODE + RVM_TRAP_BASE); } } @Override protected void emit_dload(int index) { // identical to lload emit_lload(index); } /* * storing local variables */ @Override protected void emit_istore(int index) { try { Offset offset = localOffset(index).minus(WORDSIZE); // pop computes EA after ESP has moved by WORDSIZE! if (offset.EQ(Offset.zero())) { asm.emitPOP_RegInd(ESP); } else { asm.emitPOP_RegDisp(ESP, offset); } } catch (UnreachableBytecodeException e) { asm.emitINT_Imm(TRAP_UNREACHABLE_BYTECODE + RVM_TRAP_BASE); } } @Override protected void emit_fstore(int index) { // identical to istore emit_istore(index); } @Override protected void emit_astore(int index) { // identical to istore emit_istore(index); } @Override protected void emit_lstore(int index) { try { if (VM.BuildFor32Addr) { // pop computes EA after ESP has moved by 4! Offset offset = localOffset(index + 1).minus(WORDSIZE); asm.emitPOP_RegDisp(ESP, offset); // high part asm.emitPOP_RegDisp(ESP, offset); // low part (ESP has moved by 4!!) } else { Offset offset = localOffset(index + 1).minus(WORDSIZE); asm.emitPOP_RegDisp(ESP, offset); adjustStack(WORDSIZE, true); // throw away top word } } catch (UnreachableBytecodeException e) { asm.emitINT_Imm(TRAP_UNREACHABLE_BYTECODE + RVM_TRAP_BASE); } } @Override protected void emit_dstore(int index) { // identical to lstore emit_lstore(index); } /* * array loads */ @Override protected void emit_iaload() { asm.emitPOP_Reg(T0); // T0 is array index asm.emitPOP_Reg(S0); // S0 is array ref if (VM.BuildFor64Addr) { asm.emitAND_Reg_Reg(T0, T0); // clear MSBs } genBoundsCheck(asm, T0, S0); // T0 is index, S0 is address of array // push [S0+T0<<2] asm.emitPUSH_RegIdx(S0, T0, WORD, NO_SLOT); } @Override protected void emit_faload() { // identical to iaload emit_iaload(); } @Override protected void emit_aaload() { asm.emitPOP_Reg(T0); // T0 is array index asm.emitPOP_Reg(T1); // T1 is array ref if (VM.BuildFor64Addr) { asm.emitAND_Reg_Reg(T0, T0); // clear MSBs } genBoundsCheck(asm, T0, T1); // T0 is index, T1 is address of array if (NEEDS_OBJECT_ALOAD_BARRIER) { // rewind 2 args on stack asm.emitPUSH_Reg(T1); // T1 is array ref asm.emitPUSH_Reg(T0); // T0 is array index Barriers.compileArrayLoadBarrier(asm, true); } else { asm.emitPUSH_RegIdx(T1, T0, (short)LG_WORDSIZE, NO_SLOT); // push [S0+T0*WORDSIZE] } } @Override protected void emit_caload() { asm.emitPOP_Reg(T0); // T0 is array index asm.emitPOP_Reg(S0); // S0 is array ref if (VM.BuildFor64Addr) { asm.emitAND_Reg_Reg(T0, T0); // clear MSBs } genBoundsCheck(asm, T0, S0); // T0 is index, S0 is address of array // T1 = (int)[S0+T0<<1] if (VM.BuildFor32Addr) { asm.emitMOVZX_Reg_RegIdx_Word(T1, S0, T0, SHORT, NO_SLOT); } else { asm.emitMOVZXQ_Reg_RegIdx_Word(T1, S0, T0, SHORT, NO_SLOT); } asm.emitPUSH_Reg(T1); // push short onto stack } /** * Emits code to load an int local variable and then load from a character array * @param index the local index to load */ @Override protected void emit_iload_caload(int index) { try { Offset offset = localOffset(index); if (offset.EQ(Offset.zero())) { asm.emitMOV_Reg_RegInd(T0, SP); // T0 is array index } else { asm.emitMOV_Reg_RegDisp(T0, SP, offset); // T0 is array index } // NB MSBs of T0 are already clear in 64bit asm.emitPOP_Reg(S0); // S0 is array ref genBoundsCheck(asm, T0, S0); // T0 is index, S0 is address of array // T1 = (int)[S0+T0<<1] if (VM.BuildFor32Addr) { asm.emitMOVZX_Reg_RegIdx_Word(T1, S0, T0, SHORT, NO_SLOT); } else { asm.emitMOVZXQ_Reg_RegIdx_Word(T1, S0, T0, SHORT, NO_SLOT); } asm.emitPUSH_Reg(T1); // push short onto stack } catch (UnreachableBytecodeException e) { asm.emitINT_Imm(TRAP_UNREACHABLE_BYTECODE + RVM_TRAP_BASE); } } @Override protected void emit_saload() { asm.emitPOP_Reg(T0); // T0 is array index asm.emitPOP_Reg(S0); // S0 is array ref if (VM.BuildFor64Addr) { asm.emitAND_Reg_Reg(T0, T0); // clear MSBs } genBoundsCheck(asm, T0, S0); // T0 is index, S0 is address of array // T1 = (int)[S0+T0<<1] if (VM.BuildFor32Addr) { asm.emitMOVSX_Reg_RegIdx_Word(T1, S0, T0, SHORT, NO_SLOT); } else { asm.emitMOVSXQ_Reg_RegIdx_Word(T1, S0, T0, SHORT, NO_SLOT); } asm.emitPUSH_Reg(T1); // push short onto stack } @Override protected void emit_baload() { asm.emitPOP_Reg(T0); // T0 is array index asm.emitPOP_Reg(S0); // S0 is array ref if (VM.BuildFor64Addr) { asm.emitAND_Reg_Reg(T0, T0); // clear MSBs } genBoundsCheck(asm, T0, S0); // T0 is index, S0 is address of array // T1 = (int)[S0+T0<<1] if (VM.BuildFor32Addr) { asm.emitMOVSX_Reg_RegIdx_Byte(T1, S0, T0, BYTE, NO_SLOT); } else { asm.emitMOVSXQ_Reg_RegIdx_Byte(T1, S0, T0, BYTE, NO_SLOT); } asm.emitPUSH_Reg(T1); // push byte onto stack } @Override protected void emit_laload() { asm.emitPOP_Reg(T0); // T0 is array index asm.emitPOP_Reg(T1); // T1 is array ref if (VM.BuildFor64Addr) { asm.emitAND_Reg_Reg(T0, T0); // clear MSBs } genBoundsCheck(asm, T0, T1); // T0 is index, T1 is address of array if (VM.BuildFor32Addr) { asm.emitPUSH_RegIdx(T1, T0, LONG, ONE_SLOT); // load high part of desired long array element asm.emitPUSH_RegIdx(T1, T0, LONG, NO_SLOT); // load low part of desired long array element } else { adjustStack(-WORDSIZE, true); asm.emitPUSH_RegIdx(T1, T0, LONG, NO_SLOT); // load desired long array element } } @Override protected void emit_daload() { // identical to laload emit_laload(); } /* * array stores */ /** * Generates a primitive array store for the given size. * * @param size in bytes of the array store to generate */ private void primitiveArrayStoreHelper(int size) { Barriers.compileModifyCheck(asm, (size == 8) ? 3 * WORDSIZE : 2 * WORDSIZE); if (VM.BuildFor32Addr) { if (size == 8) { asm.emitPOP_Reg(S1); // S1 is the low value } asm.emitPOP_Reg(T1); // T1 is the value/high value asm.emitPOP_Reg(T0); // T0 is array index asm.emitPOP_Reg(S0); // S0 is array ref } else { asm.emitPOP_Reg(T1); // T1 is the value if (size == 8) { adjustStack(WORDSIZE, true); // throw away slot } asm.emitPOP_Reg(T0); // T0 is array index asm.emitPOP_Reg(S0); // S0 is array ref asm.emitAND_Reg_Reg(T0, T0); // clear MSBs } genBoundsCheck(asm, T0, S0); // T0 is index, S0 is address of array switch (size) { case 8: if (VM.BuildFor32Addr) { asm.emitMOV_RegIdx_Reg(S0, T0, LONG, NO_SLOT, S1); // [S0+T0<<<3] <- S1 asm.emitMOV_RegIdx_Reg(S0, T0, LONG, ONE_SLOT, T1); // [4+S0+T0<<<3] <- T1 } else { asm.emitMOV_RegIdx_Reg_Quad(S0, T0, LONG, NO_SLOT, T1); // [S0+T0<<<3] <- T1 } break; case 4: asm.emitMOV_RegIdx_Reg(S0, T0, WORD, NO_SLOT, T1); // [S0 + T0<<2] <- T1 break; case 2: // store halfword element into array i.e. [S0 +T0] <- T1 (halfword) asm.emitMOV_RegIdx_Reg_Word(S0, T0, SHORT, NO_SLOT, T1); break; case 1: asm.emitMOV_RegIdx_Reg_Byte(S0, T0, BYTE, NO_SLOT, T1); // [S0 + T0<<2] <- T1 break; default: if (VM.VerifyAssertions) { VM._assert(VM.NOT_REACHED, "Unhandled byte size!"); } else { VM.sysFail("Unhandled byte size"); } } } /** * Private helper to perform an array bounds check * @param index offset from current SP to the array index * @param arrayRef offset from current SP to the array reference */ private void boundsCheckHelper(Offset index, Offset arrayRef) { stackMoveHelper(T0, index); // T0 is array index if (VM.BuildFor64Addr) { asm.emitAND_Reg_Reg(T0, T0); // clear MSBs } stackMoveHelper(S0, arrayRef); // S0 is array ref genBoundsCheck(asm, T0, S0); // T0 is index, S0 is address of array } @Override protected void emit_iastore() { Barriers.compileModifyCheck(asm, 2 * WORDSIZE); if (NEEDS_INT_ASTORE_BARRIER) { boundsCheckHelper(ONE_SLOT, TWO_SLOTS); Barriers.compileArrayStoreBarrierInt(asm, this); } else { primitiveArrayStoreHelper(4); } } @Override protected void emit_fastore() { Barriers.compileModifyCheck(asm, 2 * WORDSIZE); if (NEEDS_FLOAT_ASTORE_BARRIER) { boundsCheckHelper(ONE_SLOT, TWO_SLOTS); Barriers.compileArrayStoreBarrierFloat(asm, this); } else { primitiveArrayStoreHelper(4); } } @Override protected void emit_aastore() { Barriers.compileModifyCheck(asm, 2 * WORDSIZE); if (doesCheckStore) { genParameterRegisterLoad(asm, 3); asm.generateJTOCcall(Entrypoints.aastoreMethod.getOffset()); } else { genParameterRegisterLoad(asm, 3); asm.generateJTOCcall(Entrypoints.aastoreUninterruptibleMethod.getOffset()); } } @Override protected void emit_castore() { Barriers.compileModifyCheck(asm, 2 * WORDSIZE); if (NEEDS_CHAR_ASTORE_BARRIER) { boundsCheckHelper(ONE_SLOT, TWO_SLOTS); Barriers.compileArrayStoreBarrierChar(asm, this); } else { primitiveArrayStoreHelper(2); } } @Override protected void emit_sastore() { Barriers.compileModifyCheck(asm, 2 * WORDSIZE); if (NEEDS_SHORT_ASTORE_BARRIER) { boundsCheckHelper(ONE_SLOT, TWO_SLOTS); Barriers.compileArrayStoreBarrierShort(asm, this); } else { primitiveArrayStoreHelper(2); } } @Override protected void emit_bastore() { Barriers.compileModifyCheck(asm, 2 * WORDSIZE); if (NEEDS_BYTE_ASTORE_BARRIER) { boundsCheckHelper(ONE_SLOT, TWO_SLOTS); Barriers.compileArrayStoreBarrierByte(asm, this); } else { primitiveArrayStoreHelper(1); } } @Override protected void emit_lastore() { Barriers.compileModifyCheck(asm, 3 * WORDSIZE); if (NEEDS_LONG_ASTORE_BARRIER) { boundsCheckHelper(TWO_SLOTS, THREE_SLOTS); Barriers.compileArrayStoreBarrierLong(asm, this); } else { primitiveArrayStoreHelper(8); } } @Override protected void emit_dastore() { Barriers.compileModifyCheck(asm, 3 * WORDSIZE); if (NEEDS_DOUBLE_ASTORE_BARRIER) { boundsCheckHelper(TWO_SLOTS, THREE_SLOTS); Barriers.compileArrayStoreBarrierDouble(asm, this); } else { primitiveArrayStoreHelper(8); } } /* * expression stack manipulation */ @Override protected void emit_pop() { adjustStack(WORDSIZE, true); } @Override protected void emit_pop2() { // This could be encoded as the single 3 byte instruction // asm.emitADD_Reg_Imm(SP, 8); // or as the following 2 1 byte instructions. There doesn't appear to be any // performance difference. adjustStack(WORDSIZE * 2, true); } @Override protected void emit_dup() { // This could be encoded as the 2 instructions totalling 4 bytes: // asm.emitMOV_Reg_RegInd(T0, SP); // asm.emitPUSH_Reg(T0); // However, there doesn't seem to be any performance difference to: asm.emitPUSH_RegInd(SP); } @Override protected void emit_dup_x1() { asm.emitPOP_Reg(T0); asm.emitPOP_Reg(S0); asm.emitPUSH_Reg(T0); asm.emitPUSH_Reg(S0); asm.emitPUSH_Reg(T0); } @Override protected void emit_dup_x2() { asm.emitPOP_Reg(T0); asm.emitPOP_Reg(S0); asm.emitPOP_Reg(T1); asm.emitPUSH_Reg(T0); asm.emitPUSH_Reg(T1); asm.emitPUSH_Reg(S0); asm.emitPUSH_Reg(T0); } @Override protected void emit_dup2() { asm.emitPOP_Reg(T0); asm.emitPOP_Reg(S0); asm.emitPUSH_Reg(S0); asm.emitPUSH_Reg(T0); asm.emitPUSH_Reg(S0); asm.emitPUSH_Reg(T0); } @Override protected void emit_dup2_x1() { asm.emitPOP_Reg(T0); asm.emitPOP_Reg(S0); asm.emitPOP_Reg(T1); asm.emitPUSH_Reg(S0); asm.emitPUSH_Reg(T0); asm.emitPUSH_Reg(T1); asm.emitPUSH_Reg(S0); asm.emitPUSH_Reg(T0); } @Override protected void emit_dup2_x2() { asm.emitPOP_Reg(T0); asm.emitPOP_Reg(S0); asm.emitPOP_Reg(T1); asm.emitPOP_Reg(S1); asm.emitPUSH_Reg(S0); asm.emitPUSH_Reg(T0); asm.emitPUSH_Reg(S1); asm.emitPUSH_Reg(T1); asm.emitPUSH_Reg(S0); asm.emitPUSH_Reg(T0); } @Override protected void emit_swap() { // This could be encoded as the 4 instructions totalling 14 bytes: // asm.emitMOV_Reg_RegInd(T0, SP); // asm.emitMOV_Reg_RegDisp(S0, SP, ONE_SLOT); // asm.emitMOV_RegDisp_Reg(SP, ONE_SLOT, T0); // asm.emitMOV_RegInd_Reg(SP, S0); // But the following is 4bytes: asm.emitPOP_Reg(T0); asm.emitPOP_Reg(S0); asm.emitPUSH_Reg(T0); asm.emitPUSH_Reg(S0); } /* * int ALU */ @Override protected void emit_iadd() { asm.emitPOP_Reg(T0); asm.emitADD_RegInd_Reg(SP, T0); } @Override protected void emit_isub() { asm.emitPOP_Reg(T0); asm.emitSUB_RegInd_Reg(SP, T0); } @Override protected void emit_imul() { asm.emitPOP_Reg(T0); asm.emitPOP_Reg(T1); asm.emitIMUL2_Reg_Reg(T0, T1); asm.emitPUSH_Reg(T0); } @Override protected void emit_idiv() { asm.emitPOP_Reg(ECX); // ECX is divisor; NOTE: can't use symbolic registers because of intel hardware requirements asm.emitPOP_Reg(EAX); // EAX is dividend asm.emitCDQ(); // sign extend EAX into EDX asm.emitIDIV_Reg_Reg(EAX, ECX); asm.emitPUSH_Reg(EAX); // push result } @Override protected void emit_irem() { asm.emitPOP_Reg(ECX); // ECX is divisor; NOTE: can't use symbolic registers because of intel hardware requirements asm.emitPOP_Reg(EAX); // EAX is dividend asm.emitCDQ(); // sign extend EAX into EDX asm.emitIDIV_Reg_Reg(EAX, ECX); asm.emitPUSH_Reg(EDX); // push remainder } @Override protected void emit_ineg() { asm.emitNEG_RegInd(SP); // [SP] <- -[SP] } @Override protected void emit_ishl() { asm.emitPOP_Reg(ECX); asm.emitSHL_RegInd_Reg(SP, ECX); } @Override protected void emit_ishr() { asm.emitPOP_Reg(ECX); asm.emitSAR_RegInd_Reg(SP, ECX); } @Override protected void emit_iushr() { asm.emitPOP_Reg(ECX); asm.emitSHR_RegInd_Reg(SP, ECX); } @Override protected void emit_iand() { asm.emitPOP_Reg(T0); asm.emitAND_RegInd_Reg(SP, T0); } @Override protected void emit_ior() { asm.emitPOP_Reg(T0); asm.emitOR_RegInd_Reg(SP, T0); } @Override protected void emit_ixor() { asm.emitPOP_Reg(T0); asm.emitXOR_RegInd_Reg(SP, T0); } @Override protected void emit_iinc(int index, int val) { try { Offset offset = localOffset(index); asm.emitADD_RegDisp_Imm(ESP, offset, val); } catch (UnreachableBytecodeException e) { asm.emitINT_Imm(TRAP_UNREACHABLE_BYTECODE + RVM_TRAP_BASE); } } /* * long ALU */ @Override protected void emit_ladd() { if (VM.BuildFor32Addr) { asm.emitPOP_Reg(T0); // the low half of one long asm.emitPOP_Reg(S0); // the high half asm.emitADD_RegInd_Reg(SP, T0); // add low halves asm.emitADC_RegDisp_Reg(SP, ONE_SLOT, S0); // add high halves with carry } else { asm.emitPOP_Reg(T0); // the long value asm.emitPOP_Reg(S0); // throw away slot asm.emitADD_RegInd_Reg_Quad(SP, T0); // add values } } @Override protected void emit_lsub() { if (VM.BuildFor32Addr) { asm.emitPOP_Reg(T0); // the low half of one long asm.emitPOP_Reg(S0); // the high half asm.emitSUB_RegInd_Reg(SP, T0); // subtract low halves asm.emitSBB_RegDisp_Reg(SP, ONE_SLOT, S0); // subtract high halves with borrow } else { asm.emitPOP_Reg(T0); // the long value adjustStack(WORDSIZE, true); // throw away slot asm.emitSUB_RegInd_Reg_Quad(SP, T0); // sub values } } @Override protected void emit_lmul() { if (VM.BuildFor64Addr) { asm.emitPOP_Reg(T0); // the long value asm.emitPOP_Reg(S0); // throw away slot asm.emitIMUL2_Reg_RegInd_Quad(T0, SP); asm.emitMOV_RegInd_Reg_Quad(SP, T0); } else { // stack: value1.high = mulitplier // value1.low // value2.high = multiplicand // value2.low <-- ESP if (VM.VerifyAssertions) VM._assert(S0 != EAX); if (VM.VerifyAssertions) VM._assert(S0 != EDX); // EAX = multiplicand low; SP changed! asm.emitPOP_Reg(EAX); // EDX = multiplicand high asm.emitPOP_Reg(EDX); // stack: value1.high = mulitplier // value1.low <-- ESP // value2.high = multiplicand // value2.low // S0 = multiplier high asm.emitMOV_Reg_RegDisp(S0, SP, ONE_SLOT); // is one operand > 2^32 ? asm.emitOR_Reg_Reg(EDX, S0); // EDX = multiplier low asm.emitMOV_Reg_RegInd(EDX, SP); // Jump if we need a 64bit multiply ForwardReference fr1 = asm.forwardJcc(NE); // EDX:EAX = 32bit multiply of multiplier and multiplicand low asm.emitMUL_Reg_Reg(EAX, EDX); // Jump over 64bit multiply ForwardReference fr2 = asm.forwardJMP(); // Start of 64bit multiply fr1.resolve(asm); // EDX = multiplicand high * multiplier low asm.emitIMUL2_Reg_RegDisp(EDX, SP, MINUS_ONE_SLOT); // S0 = multiplier high * multiplicand low asm.emitIMUL2_Reg_Reg(S0, EAX); // S0 = S0 + EDX asm.emitADD_Reg_Reg(S0, EDX); // EDX:EAX = 32bit multiply of multiplier and multiplicand low asm.emitMUL_Reg_RegInd(EAX, SP); // EDX = EDX + S0 asm.emitADD_Reg_Reg(EDX, S0); // Finish up fr2.resolve(asm); // store EDX:EAX to stack asm.emitMOV_RegDisp_Reg(SP, ONE_SLOT, EDX); asm.emitMOV_RegInd_Reg(SP, EAX); } } @Override protected void emit_ldiv() { if (VM.BuildFor64Addr) { asm.emitPOP_Reg(ECX); // ECX is divisor; NOTE: can't use symbolic registers because of intel hardware requirements asm.emitPOP_Reg(EAX); // throw away slot asm.emitPOP_Reg(EAX); // EAX is dividend asm.emitCDO(); // sign extend EAX into EDX asm.emitIDIV_Reg_Reg_Quad(EAX, ECX); asm.emitPUSH_Reg(EAX); // push result } else { // (1) zero check asm.emitMOV_Reg_RegInd(T0, SP); asm.emitOR_Reg_RegDisp(T0, SP, ONE_SLOT); asm.emitBranchLikelyNextInstruction(); ForwardReference fr1 = asm.forwardJcc(NE); asm.emitINT_Imm(RuntimeEntrypoints.TRAP_DIVIDE_BY_ZERO + RVM_TRAP_BASE); // trap if divisor is 0 fr1.resolve(asm); // (2) save RVM nonvolatiles int numNonVols = NONVOLATILE_GPRS.length; Offset off = Offset.fromIntSignExtend(numNonVols * WORDSIZE); for (int i = 0; i < numNonVols; i++) { asm.emitPUSH_Reg(NONVOLATILE_GPRS[i]); } // (3) Push args to C function (reversed) asm.emitPUSH_RegDisp(SP, off.plus(4)); asm.emitPUSH_RegDisp(SP, off.plus(4)); asm.emitPUSH_RegDisp(SP, off.plus(20)); asm.emitPUSH_RegDisp(SP, off.plus(20)); // (4) invoke C function through bootrecord asm.emitMOV_Reg_Abs(S0, Magic.getTocPointer().plus(Entrypoints.the_boot_recordField.getOffset())); asm.emitCALL_RegDisp(S0, Entrypoints.sysLongDivideIPField.getOffset()); // (5) pop space for arguments adjustStack(4 * WORDSIZE, true); // (6) restore RVM nonvolatiles for (int i = numNonVols - 1; i >= 0; i--) { asm.emitPOP_Reg(NONVOLATILE_GPRS[i]); } // (7) pop expression stack adjustStack(WORDSIZE * 4, true); // (8) push results asm.emitPUSH_Reg(T1); asm.emitPUSH_Reg(T0); } } @Override protected void emit_lrem() { if (VM.BuildFor64Addr) { asm.emitPOP_Reg(ECX); // ECX is divisor; NOTE: can't use symbolic registers because of intel hardware requirements asm.emitPOP_Reg(EAX); // throw away slot asm.emitPOP_Reg(EAX); // EAX is dividend asm.emitCDO(); // sign extend EAX into EDX asm.emitIDIV_Reg_Reg_Quad(EAX, ECX); asm.emitPUSH_Reg(EDX); // push result } else { // (1) zero check asm.emitMOV_Reg_RegInd(T0, SP); asm.emitOR_Reg_RegDisp(T0, SP, ONE_SLOT); asm.emitBranchLikelyNextInstruction(); ForwardReference fr1 = asm.forwardJcc(NE); asm.emitINT_Imm(RuntimeEntrypoints.TRAP_DIVIDE_BY_ZERO + RVM_TRAP_BASE); // trap if divisor is 0 fr1.resolve(asm); // (2) save RVM nonvolatiles int numNonVols = NONVOLATILE_GPRS.length; Offset off = Offset.fromIntSignExtend(numNonVols * WORDSIZE); for (int i = 0; i < numNonVols; i++) { asm.emitPUSH_Reg(NONVOLATILE_GPRS[i]); } // (3) Push args to C function (reversed) asm.emitPUSH_RegDisp(SP, off.plus(4)); asm.emitPUSH_RegDisp(SP, off.plus(4)); asm.emitPUSH_RegDisp(SP, off.plus(20)); asm.emitPUSH_RegDisp(SP, off.plus(20)); // (4) invoke C function through bootrecord asm.emitMOV_Reg_Abs(S0, Magic.getTocPointer().plus(Entrypoints.the_boot_recordField.getOffset())); asm.emitCALL_RegDisp(S0, Entrypoints.sysLongRemainderIPField.getOffset()); // (5) pop space for arguments adjustStack(4 * WORDSIZE, true); // (6) restore RVM nonvolatiles for (int i = numNonVols - 1; i >= 0; i--) { asm.emitPOP_Reg(NONVOLATILE_GPRS[i]); } // (7) pop expression stack adjustStack(WORDSIZE * 4, true); // (8) push results asm.emitPUSH_Reg(T1); asm.emitPUSH_Reg(T0); } } @Override protected void emit_lneg() { if (VM.BuildFor32Addr) { // The following is fewer instructions, but larger code // asm.emitNOT_RegDisp(SP, ONE_SLOT); // asm.emitNEG_RegInd(SP); // asm.emitSBB_RegDisp_Imm(SP, ONE_SLOT, -1); // this implementation is shorter and promotes ESP folding asm.emitPOP_Reg(T0); // T0 = low asm.emitNEG_Reg(T0); // T0 = -low asm.emitPOP_Reg(T1); // T1 = high asm.emitNOT_Reg(T1); // T1 = ~T1 (doesn't effect flags) asm.emitSBB_Reg_Imm(T1, -1); // T1 = high + 1 - CF asm.emitPUSH_Reg(T1); asm.emitPUSH_Reg(T0); } else { asm.emitNEG_RegInd_Quad(SP); } } @Override protected void emit_lshl() { if (VM.BuildFor32Addr) { if (SSE2_BASE) { asm.emitPOP_Reg(T0); // shift amount (6 bits) asm.emitMOVQ_Reg_RegInd(XMM1, SP); // XMM1 <- [SP] asm.emitAND_Reg_Imm(T0, 0x3F); // mask to 6bits asm.emitMOVD_Reg_Reg(XMM0, T0); // XMM0 <- T0 asm.emitPSLLQ_Reg_Reg(XMM1, XMM0); // XMM1 <<= XMM0 asm.emitMOVQ_RegInd_Reg(SP, XMM1); // [SP] <- XMM1 } else { if (VM.VerifyAssertions) VM._assert(ECX != T0); // ECX is constrained to be the shift count if (VM.VerifyAssertions) VM._assert(ECX != T1); asm.emitPOP_Reg(ECX); // shift amount (6 bits) asm.emitPOP_Reg(T0); // pop low half asm.emitPOP_Reg(T1); // pop high half asm.emitAND_Reg_Imm(ECX, 0x3F); asm.emitCMP_Reg_Imm(ECX, 32); ForwardReference fr1 = asm.forwardJcc(LT); asm.emitMOV_Reg_Reg(T1, T0); // high half = low half asm.emitXOR_Reg_Reg(T0, T0); // low half = 0 fr1.resolve(asm); asm.emitSHLD_Reg_Reg_Reg(T1, T0, ECX); // shift high half, filling from low asm.emitSHL_Reg_Reg(T0, ECX); // shift low half asm.emitPUSH_Reg(T1); // push high half asm.emitPUSH_Reg(T0); // push low half } } else { asm.emitPOP_Reg(ECX); asm.emitSHL_RegInd_Reg_Quad(SP, ECX); } } @Override protected void emit_lshr() { if (VM.BuildFor32Addr) { if (VM.VerifyAssertions) VM._assert(ECX != T0); // ECX is constrained to be the shift count if (VM.VerifyAssertions) VM._assert(ECX != T1); asm.emitPOP_Reg(ECX); // shift amount (6 bits) asm.emitPOP_Reg(T0); // pop low half asm.emitPOP_Reg(T1); // pop high half asm.emitAND_Reg_Imm(ECX, 0x3F); asm.emitCMP_Reg_Imm(ECX, 32); ForwardReference fr1 = asm.forwardJcc(LT); asm.emitMOV_Reg_Reg(T0, T1); // low half = high half asm.emitSAR_Reg_Imm(T1, 31); // high half = sign extension of low half fr1.resolve(asm); asm.emitSHRD_Reg_Reg_Reg(T0, T1, ECX); // shift low half, filling from high asm.emitSAR_Reg_Reg(T1, ECX); // shift high half asm.emitPUSH_Reg(T1); // push high half asm.emitPUSH_Reg(T0); // push low half } else { asm.emitPOP_Reg(ECX); asm.emitSAR_RegInd_Reg_Quad(SP, ECX); } } @Override protected void emit_lushr() { if (VM.BuildFor32Addr) { if (SSE2_BASE) { asm.emitPOP_Reg(T0); // shift amount (6 bits) asm.emitMOVQ_Reg_RegInd(XMM1, SP); // XMM1 <- [SP] asm.emitAND_Reg_Imm(T0, 0x3F); // mask to 6bits asm.emitMOVD_Reg_Reg(XMM0, T0); // XMM0 <- T0 asm.emitPSRLQ_Reg_Reg(XMM1, XMM0); // XMM1 >>>= XMM0 asm.emitMOVQ_RegInd_Reg(SP, XMM1); // [SP] <- XMM1 } else { if (VM.VerifyAssertions) VM._assert(ECX != T0); // ECX is constrained to be the shift count if (VM.VerifyAssertions) VM._assert(ECX != T1); asm.emitPOP_Reg(ECX); // shift amount (6 bits) asm.emitPOP_Reg(T0); // pop low half asm.emitPOP_Reg(T1); // pop high half asm.emitAND_Reg_Imm(ECX, 0x3F); asm.emitCMP_Reg_Imm(ECX, 32); ForwardReference fr1 = asm.forwardJcc(LT); asm.emitMOV_Reg_Reg(T0, T1); // low half = high half asm.emitXOR_Reg_Reg(T1, T1); // high half = 0 fr1.resolve(asm); asm.emitSHRD_Reg_Reg_Reg(T0, T1, ECX); // shift low half, filling from high asm.emitSHR_Reg_Reg(T1, ECX); // shift high half asm.emitPUSH_Reg(T1); // push high half asm.emitPUSH_Reg(T0); // push low half } } else { asm.emitPOP_Reg(ECX); asm.emitSHR_RegInd_Reg_Quad(SP, ECX); } } @Override protected void emit_land() { if (VM.BuildFor32Addr) { asm.emitPOP_Reg(T0); // low asm.emitPOP_Reg(S0); // high asm.emitAND_RegInd_Reg(SP, T0); asm.emitAND_RegDisp_Reg(SP, ONE_SLOT, S0); } else { asm.emitPOP_Reg(T0); // long value asm.emitPOP_Reg(S0); // throw away slot asm.emitAND_RegInd_Reg_Quad(SP, T0); } } @Override protected void emit_lor() { if (VM.BuildFor32Addr) { asm.emitPOP_Reg(T0); // low asm.emitPOP_Reg(S0); // high asm.emitOR_RegInd_Reg(SP, T0); asm.emitOR_RegDisp_Reg(SP, ONE_SLOT, S0); } else { asm.emitPOP_Reg(T0); // long value asm.emitPOP_Reg(S0); // throw away slot asm.emitOR_RegInd_Reg_Quad(SP, T0); } } /** * Emit code to implement the lxor bytecode */ @Override protected void emit_lxor() { if (VM.BuildFor32Addr) { asm.emitPOP_Reg(T0); // low asm.emitPOP_Reg(S0); // high asm.emitXOR_RegInd_Reg(SP, T0); asm.emitXOR_RegDisp_Reg(SP, ONE_SLOT, S0); } else { asm.emitPOP_Reg(T0); // long value asm.emitPOP_Reg(S0); // throw away slot asm.emitXOR_RegInd_Reg_Quad(SP, T0); } } /* * float ALU */ @Override protected void emit_fadd() { if (SSE2_BASE) { asm.emitMOVSS_Reg_RegInd(XMM0, SP); // XMM0 = value2 adjustStack(WORDSIZE, true); // throw away slot asm.emitADDSS_Reg_RegInd(XMM0, SP); // XMM0 += value1 asm.emitMOVSS_RegInd_Reg(SP, XMM0); // set result on stack } else { asm.emitFLD_Reg_RegInd(FP0, SP); // FPU reg. stack <- value2 adjustStack(WORDSIZE, true); // throw away slot asm.emitFADD_Reg_RegInd(FP0, SP); // FPU reg. stack += value1 asm.emitFSTP_RegInd_Reg(SP, FP0); // POP FPU reg. stack onto stack } } @Override protected void emit_fsub() { if (SSE2_BASE) { asm.emitMOVSS_Reg_RegDisp(XMM0, SP, ONE_SLOT); // XMM0 = value1 asm.emitSUBSS_Reg_RegInd(XMM0, SP); // XMM0 -= value2 adjustStack(WORDSIZE, true); // throw away slot asm.emitMOVSS_RegInd_Reg(SP, XMM0); // set result on stack } else { asm.emitFLD_Reg_RegDisp(FP0, SP, ONE_SLOT); // FPU reg. stack <- value1 asm.emitFSUB_Reg_RegInd(FP0, SP); // FPU reg. stack -= value2 adjustStack(WORDSIZE, true); // throw away slot asm.emitFSTP_RegInd_Reg(SP, FP0); // POP FPU reg. stack onto stack } } @Override protected void emit_fmul() { if (SSE2_BASE) { asm.emitMOVSS_Reg_RegInd(XMM0, SP); // XMM0 = value2 adjustStack(WORDSIZE, true); // throw away slot asm.emitMULSS_Reg_RegInd(XMM0, SP); // XMM0 *= value1 asm.emitMOVSS_RegInd_Reg(SP, XMM0); // set result on stack } else { asm.emitFLD_Reg_RegInd(FP0, SP); // FPU reg. stack <- value2 adjustStack(WORDSIZE, true); // throw away slot asm.emitFMUL_Reg_RegInd(FP0, SP); // FPU reg. stack *= value1 asm.emitFSTP_RegInd_Reg(SP, FP0); // POP FPU reg. stack onto stack } } /** * Emit code to implement the fdiv bytecode */ @Override protected void emit_fdiv() { if (SSE2_BASE) { asm.emitMOVSS_Reg_RegDisp(XMM0, SP, ONE_SLOT); // XMM0 = value1 asm.emitDIVSS_Reg_RegInd(XMM0, SP); // XMM0 /= value2 adjustStack(WORDSIZE, true); // throw away slot asm.emitMOVSS_RegInd_Reg(SP, XMM0); // set result on stack } else { asm.emitFLD_Reg_RegDisp(FP0, SP, ONE_SLOT); // FPU reg. stack <- value1 asm.emitFDIV_Reg_RegInd(FP0, SP); // FPU reg. stack /= value2 adjustStack(WORDSIZE, true); // throw away slot asm.emitFSTP_RegInd_Reg(SP, FP0); // POP FPU reg. stack onto stack } } @Override protected void emit_frem() { asm.emitFLD_Reg_RegInd(FP0, SP); // FPU reg. stack <- value2, or a adjustStack(WORDSIZE, true); // throw away slot asm.emitFLD_Reg_RegInd(FP0, SP); // FPU reg. stack <- value1, or b int retryLabel = asm.getMachineCodeIndex(); // come here if partial remainder not complete asm.emitFPREM(); // FPU reg. stack <- a%b asm.emitFSTSW_Reg(EAX); // AX = fpsw asm.emitAND_Reg_Imm(EAX, 0x400); // is C2 set? asm.emitJCC_Cond_Imm(NE, retryLabel); // if yes then goto retryLabel and continue to compute remainder asm.emitFSTP_RegInd_Reg(SP, FP0); // POP FPU reg. stack (results) onto java stack asm.emitFFREEP_Reg(FP0); } @Override protected void emit_fneg() { // flip sign bit asm.emitXOR_RegInd_Imm(SP, 0x80000000); } /* * double ALU */ @Override protected void emit_dadd() { if (SSE2_BASE) { asm.emitMOVSD_Reg_RegInd(XMM0, SP); // XMM0 = value2 adjustStack(WORDSIZE * 2, true); // throw away long slot asm.emitADDSD_Reg_RegInd(XMM0, SP); // XMM0 += value1 asm.emitMOVSD_RegInd_Reg(SP, XMM0); // set result on stack } else { asm.emitFLD_Reg_RegInd_Quad(FP0, SP); // FPU reg. stack <- value2 adjustStack(WORDSIZE * 2, true); // throw away long slot asm.emitFADD_Reg_RegInd_Quad(FP0, SP); // FPU reg. stack += value1 asm.emitFSTP_RegInd_Reg_Quad(SP, FP0); // POP FPU reg. stack onto stack } } @Override protected void emit_dsub() { if (SSE2_BASE) { asm.emitMOVSD_Reg_RegDisp(XMM0, SP, TWO_SLOTS); // XMM0 = value1 asm.emitSUBSD_Reg_RegInd(XMM0, SP); // XMM0 -= value2 adjustStack(WORDSIZE * 2, true); // throw away long slot asm.emitMOVSD_RegInd_Reg(SP, XMM0); // set result on stack } else { asm.emitFLD_Reg_RegDisp_Quad(FP0, SP, TWO_SLOTS); // FPU reg. stack <- value1 asm.emitFSUB_Reg_RegInd_Quad(FP0, SP); // FPU reg. stack -= value2 adjustStack(WORDSIZE * 2, true); // throw away long slot asm.emitFSTP_RegInd_Reg_Quad(SP, FP0); // POP FPU reg. stack onto stack } } @Override protected void emit_dmul() { if (SSE2_BASE) { asm.emitMOVSD_Reg_RegInd(XMM0, SP); // XMM0 = value2 adjustStack(WORDSIZE * 2, true); // throw away long slot asm.emitMULSD_Reg_RegInd(XMM0, SP); // XMM0 *= value1 asm.emitMOVSD_RegInd_Reg(SP, XMM0); // set result on stack } else { asm.emitFLD_Reg_RegInd_Quad(FP0, SP); // FPU reg. stack <- value2 adjustStack(WORDSIZE * 2, true); // throw away long slot asm.emitFMUL_Reg_RegInd_Quad(FP0, SP); // FPU reg. stack *= value1 asm.emitFSTP_RegInd_Reg_Quad(SP, FP0); // POP FPU reg. stack onto stack } } @Override protected void emit_ddiv() { if (SSE2_BASE) { asm.emitMOVSD_Reg_RegDisp(XMM0, SP, TWO_SLOTS); // XMM0 = value1 asm.emitDIVSD_Reg_RegInd(XMM0, SP); // XMM0 /= value2 adjustStack(WORDSIZE * 2, true); // throw away long slot asm.emitMOVSD_RegInd_Reg(SP, XMM0); // set result on stack } else { asm.emitFLD_Reg_RegDisp_Quad(FP0, SP, TWO_SLOTS); // FPU reg. stack <- value1 asm.emitFDIV_Reg_RegInd_Quad(FP0, SP); // FPU reg. stack /= value2 adjustStack(WORDSIZE * 2, true); // throw away long slot asm.emitFSTP_RegInd_Reg_Quad(SP, FP0); // POP FPU reg. stack onto stack } } @Override protected void emit_drem() { asm.emitFLD_Reg_RegInd_Quad(FP0, SP); // FPU reg. stack <- value2, or a adjustStack(WORDSIZE * 2, true); // throw away slot asm.emitFLD_Reg_RegInd_Quad(FP0, SP); // FPU reg. stack <- value1, or b int retryLabel = asm.getMachineCodeIndex(); // come here if partial remainder not complete asm.emitFPREM(); // FPU reg. stack <- a%b asm.emitFSTSW_Reg(EAX); // AX = fpsw asm.emitAND_Reg_Imm(EAX, 0x400); // is C2 set? asm.emitJCC_Cond_Imm(NE, retryLabel); // if yes then goto retryLabel and continue to compute remainder asm.emitFSTP_RegInd_Reg_Quad(SP, FP0); // POP FPU reg. stack (results) onto java stack asm.emitFFREEP_Reg(FP0); // throw away top of stack } @Override protected void emit_dneg() { // flip sign bit asm.emitXOR_RegDisp_Imm_Byte(SP, Offset.fromIntZeroExtend(7), 0x80); } /* * conversion ops */ @Override protected void emit_i2l() { if (VM.BuildFor32Addr) { asm.emitPUSH_RegInd(SP); // duplicate int on stack asm.emitSAR_RegDisp_Imm(SP, ONE_SLOT, 31); // sign extend as high word of long } else { asm.emitPOP_Reg(EAX); asm.emitCDQE(); adjustStack(-WORDSIZE, true); asm.emitPUSH_Reg(EAX); } } @Override protected void emit_l2i() { asm.emitPOP_Reg(T0); // long value if (VM.BuildFor64Addr) { asm.emitAND_Reg_Reg(T0, T0); // clear MSBs } adjustStack(WORDSIZE, true); // throw away slot asm.emitPUSH_Reg(T0); } @Override protected void emit_i2f() { if (SSE2_BASE) { asm.emitCVTSI2SS_Reg_RegInd(XMM0, SP); asm.emitMOVSS_RegInd_Reg(SP, XMM0); } else { asm.emitFILD_Reg_RegInd(FP0, SP); asm.emitFSTP_RegInd_Reg(SP, FP0); } } @Override protected void emit_i2d() { if (SSE2_BASE) { asm.emitCVTSI2SD_Reg_RegInd(XMM0, SP); adjustStack(-WORDSIZE, true); // grow the stack asm.emitMOVSD_RegInd_Reg(SP, XMM0); } else { asm.emitFILD_Reg_RegInd(FP0, SP); adjustStack(-WORDSIZE, true); // grow the stack asm.emitFSTP_RegInd_Reg_Quad(SP, FP0); } } @Override protected void emit_l2f() { asm.emitFILD_Reg_RegInd_Quad(FP0, SP); adjustStack(WORDSIZE, true); // shrink the stack asm.emitFSTP_RegInd_Reg(SP, FP0); } @Override protected void emit_l2d() { asm.emitFILD_Reg_RegInd_Quad(FP0, SP); asm.emitFSTP_RegInd_Reg_Quad(SP, FP0); } @Override protected void emit_f2d() { if (SSE2_BASE) { asm.emitCVTSS2SD_Reg_RegInd(XMM0, SP); adjustStack(-WORDSIZE, true); // throw away slot asm.emitMOVSD_RegInd_Reg(SP, XMM0); } else { asm.emitFLD_Reg_RegInd(FP0, SP); adjustStack(-WORDSIZE, true); // throw away slot asm.emitFSTP_RegInd_Reg_Quad(SP, FP0); } } @Override protected void emit_d2f() { if (SSE2_BASE) { asm.emitCVTSD2SS_Reg_RegInd(XMM0, SP); adjustStack(WORDSIZE, true); // throw away slot asm.emitMOVSS_RegInd_Reg(SP, XMM0); } else { asm.emitFLD_Reg_RegInd_Quad(FP0, SP); adjustStack(WORDSIZE, true); // throw away slot asm.emitFSTP_RegInd_Reg(SP, FP0); } } @Override protected void emit_f2i() { if (SSE2_BASE) { // Set up value in XMM0 asm.emitMOVSS_Reg_RegInd(XMM0, SP); asm.emitXOR_Reg_Reg(T1, T1); // adjust = 0 asm.emitXOR_Reg_Reg(T0, T0); // result = 0 // value cmp maxint asm.generateJTOCcmpFloat(XMM0, Entrypoints.maxintFloatField.getOffset()); ForwardReference fr1 = asm.forwardJcc(PE); // if NaN goto fr1 asm.emitSET_Cond_Reg_Byte(LGE, T1); // T1 = (value >= maxint) ? 1 : 0; asm.emitCVTTSS2SI_Reg_Reg(T0, XMM0); // T0 = (int)value, or 0x80000000 if value > maxint asm.emitSUB_Reg_Reg(T0, T1); // T0 = T0 - T1, ie fix max int case fr1.resolve(asm); asm.emitMOV_RegInd_Reg(SP,T0); // push result } else { // TODO: use x87 operations to do this conversion inline taking care of // the boundary cases that differ between x87 and Java // (1) save RVM nonvolatiles int numNonVols = NONVOLATILE_GPRS.length; Offset off = Offset.fromIntSignExtend(numNonVols * WORDSIZE); for (int i = 0; i < numNonVols; i++) { asm.emitPUSH_Reg(NONVOLATILE_GPRS[i]); } // (2) Push arg to C function asm.emitPUSH_RegDisp(SP, off); // (3) invoke C function through bootrecord asm.emitMOV_Reg_Abs(S0, Magic.getTocPointer().plus(Entrypoints.the_boot_recordField.getOffset())); asm.emitCALL_RegDisp(S0, Entrypoints.sysFloatToIntIPField.getOffset()); // (4) pop argument; asm.emitPOP_Reg(S0); // (5) restore RVM nonvolatiles for (int i = numNonVols - 1; i >= 0; i--) { asm.emitPOP_Reg(NONVOLATILE_GPRS[i]); } // (6) put result on expression stack asm.emitMOV_RegInd_Reg(SP, T0); } } @Override protected void emit_f2l() { if (VM.BuildFor32Addr) { // TODO: SSE3 has a FISTTP instruction that stores the value with truncation // meaning the FPSCW can be left alone // Setup value into FP1 asm.emitFLD_Reg_RegInd(FP0, SP); // Setup maxlong into FP0 asm.emitFLD_Reg_Abs(FP0, Magic.getTocPointer().plus(Entrypoints.maxlongFloatField.getOffset())); // if value > maxlong or NaN goto fr1; FP0 = value asm.emitFUCOMIP_Reg_Reg(FP0, FP1); ForwardReference fr1 = asm.forwardJcc(LLE); // Normally the status and control word rounds numbers, but for conversion // to an integer/long value we want truncation. We therefore save the FPSCW, // set it to truncation perform operation then restore adjustStack(-WORDSIZE, true); // Grow the stack asm.emitFNSTCW_RegDisp(SP, MINUS_ONE_SLOT); // [SP-4] = fpscw asm.emitMOVZX_Reg_RegDisp_Word(T0, SP, MINUS_ONE_SLOT); // EAX = fpscw asm.emitOR_Reg_Imm(T0, 0xC00); // EAX = FPSCW in truncate mode asm.emitMOV_RegInd_Reg(SP, T0); // [SP] = new fpscw value asm.emitFLDCW_RegInd(SP); // Set FPSCW asm.emitFISTP_RegInd_Reg_Quad(SP, FP0); // Store 64bit long asm.emitFLDCW_RegDisp(SP, MINUS_ONE_SLOT); // Restore FPSCW ForwardReference fr2 = asm.forwardJMP(); fr1.resolve(asm); asm.emitFSTP_Reg_Reg(FP0, FP0); // pop FPU*1 ForwardReference fr3 = asm.forwardJcc(PE); // if value == NaN goto fr3 asm.emitMOV_RegInd_Imm(SP, 0x7FFFFFFF); asm.emitPUSH_Imm(-1); ForwardReference fr4 = asm.forwardJMP(); fr3.resolve(asm); asm.emitMOV_RegInd_Imm(SP, 0); asm.emitPUSH_Imm(0); fr2.resolve(asm); fr4.resolve(asm); } else { // Set up value in XMM0 asm.emitMOVSS_Reg_RegInd(XMM0, SP); asm.emitXOR_Reg_Reg(T1, T1); // adjust = 0 asm.emitXOR_Reg_Reg(T0, T0); // result = 0 // value cmp maxlong asm.generateJTOCcmpFloat(XMM0, Entrypoints.maxlongFloatField.getOffset()); ForwardReference fr1 = asm.forwardJcc(PE); // if NaN goto fr1 asm.emitSET_Cond_Reg_Byte(LGE, T1); // T1 = (value >= maxint) ? 1 : 0; asm.emitCVTTSS2SI_Reg_Reg_Quad(T0, XMM0); // T0 = (int)value, or 0x80000000 if value > maxint asm.emitSUB_Reg_Reg_Quad(T0, T1); // T0 = T0 - T1, ie fix max long case fr1.resolve(asm); asm.emitPUSH_Reg(T0); // push result } } @Override protected void emit_d2i() { if (SSE2_BASE) { // Set up value in XMM0 asm.emitMOVSD_Reg_RegInd(XMM0, SP); adjustStack(2 * WORDSIZE, true); // throw away slots asm.emitXOR_Reg_Reg(T1, T1); // adjust = 0 asm.emitXOR_Reg_Reg(T0, T0); // result = 0 // value cmp maxint asm.generateJTOCcmpDouble(XMM0, Entrypoints.maxintField.getOffset()); ForwardReference fr1 = asm.forwardJcc(PE); // if NaN goto fr1 asm.emitSET_Cond_Reg_Byte(LGE, T1); // T1 = (value >= maxint) ? 1 : 0; asm.emitCVTTSD2SI_Reg_Reg(T0, XMM0); // T0 = (int)value, or 0x80000000 if value > maxint asm.emitSUB_Reg_Reg(T0, T1); // T0 = T0 - T1, ie fix max int case fr1.resolve(asm); asm.emitPUSH_Reg(T0); // push result } else { // TODO: use x87 operations to do this conversion inline taking care of // the boundary cases that differ between x87 and Java // (1) save RVM nonvolatiles int numNonVols = NONVOLATILE_GPRS.length; Offset off = Offset.fromIntSignExtend(numNonVols * WORDSIZE); for (int i = 0; i < numNonVols; i++) { asm.emitPUSH_Reg(NONVOLATILE_GPRS[i]); } // (2) Push args to C function (reversed) asm.emitPUSH_RegDisp(SP, off.plus(4)); asm.emitPUSH_RegDisp(SP, off.plus(4)); // (3) invoke C function through bootrecord asm.emitMOV_Reg_Abs(S0, Magic.getTocPointer().plus(Entrypoints.the_boot_recordField.getOffset())); asm.emitCALL_RegDisp(S0, Entrypoints.sysDoubleToIntIPField.getOffset()); // (4) pop arguments asm.emitPOP_Reg(S0); asm.emitPOP_Reg(S0); // (5) restore RVM nonvolatiles for (int i = numNonVols - 1; i >= 0; i--) { asm.emitPOP_Reg(NONVOLATILE_GPRS[i]); } // (6) put result on expression stack adjustStack(WORDSIZE, true); // throw away slot asm.emitMOV_RegInd_Reg(SP, T0); } } @Override protected void emit_d2l() { if (VM.BuildFor32Addr) { // TODO: SSE3 has a FISTTP instruction that stores the value with truncation // meaning the FPSCW can be left alone // Setup value into FP1 asm.emitFLD_Reg_RegInd_Quad(FP0, SP); // Setup maxlong into FP0 asm.emitFLD_Reg_Abs_Quad(FP0, Magic.getTocPointer().plus(Entrypoints.maxlongField.getOffset())); // if value > maxlong or NaN goto fr1; FP0 = value asm.emitFUCOMIP_Reg_Reg(FP0, FP1); ForwardReference fr1 = asm.forwardJcc(LLE); // Normally the status and control word rounds numbers, but for conversion // to an integer/long value we want truncation. We therefore save the FPSCW, // set it to truncation perform operation then restore asm.emitFNSTCW_RegDisp(SP, MINUS_ONE_SLOT); // [SP-4] = fpscw asm.emitMOVZX_Reg_RegDisp_Word(T0, SP, MINUS_ONE_SLOT); // EAX = fpscw asm.emitOR_Reg_Imm(T0, 0xC00); // EAX = FPSCW in truncate mode asm.emitMOV_RegInd_Reg(SP, T0); // [SP] = new fpscw value asm.emitFLDCW_RegInd(SP); // Set FPSCW asm.emitFISTP_RegInd_Reg_Quad(SP, FP0); // Store 64bit long asm.emitFLDCW_RegDisp(SP, MINUS_ONE_SLOT); // Restore FPSCW ForwardReference fr2 = asm.forwardJMP(); fr1.resolve(asm); asm.emitFSTP_Reg_Reg(FP0, FP0); // pop FPU*1 ForwardReference fr3 = asm.forwardJcc(PE); // if value == NaN goto fr3 asm.emitMOV_RegDisp_Imm(SP, ONE_SLOT, 0x7FFFFFFF); asm.emitMOV_RegInd_Imm(SP, -1); ForwardReference fr4 = asm.forwardJMP(); fr3.resolve(asm); asm.emitMOV_RegDisp_Imm(SP, ONE_SLOT, 0); asm.emitMOV_RegInd_Imm(SP, 0); fr2.resolve(asm); fr4.resolve(asm); } else { // Set up value in XMM0 asm.emitMOVSD_Reg_RegInd(XMM0, SP); asm.emitXOR_Reg_Reg(T1, T1); // adjust = 0 asm.emitXOR_Reg_Reg(T0, T0); // result = 0 // value cmp maxlong asm.generateJTOCcmpDouble(XMM0, Entrypoints.maxlongField.getOffset()); ForwardReference fr1 = asm.forwardJcc(PE); // if NaN goto fr1 asm.emitSET_Cond_Reg_Byte(LGE, T1); // T1 = (value >= maxlong) ? 1 : 0; asm.emitCVTTSD2SIQ_Reg_Reg_Quad(T0, XMM0); // T0 = (int)value, or 0x80...00 if value > maxlong asm.emitSUB_Reg_Reg_Quad(T0, T1); // T0 = T0 - T1, ie fix max long case fr1.resolve(asm); asm.emitMOV_RegInd_Reg_Quad(SP, T0); // push result } } @Override protected void emit_i2b() { // This could be coded as 2 instructions as follows: // asm.emitMOVSX_Reg_RegInd_Byte(T0, SP); // asm.emitMOV_RegInd_Reg(SP, T0); // Indirection via ESP requires an extra byte for the indirection, so the // total code size is 6 bytes. The 3 instruction version below is only 4 // bytes long and faster on Pentium 4 benchmarks. asm.emitPOP_Reg(T0); asm.emitMOVSX_Reg_Reg_Byte(T0, T0); asm.emitPUSH_Reg(T0); } @Override protected void emit_i2c() { // This could be coded as zeroing the high 16bits on stack: // asm.emitMOV_RegDisp_Imm_Word(SP, Offset.fromIntSignExtend(2), 0); // or as 2 instructions: // asm.emitMOVZX_Reg_RegInd_Word(T0, SP); // asm.emitMOV_RegInd_Reg(SP, T0); // Benchmarks show the following sequence to be more optimal on a Pentium 4 asm.emitPOP_Reg(T0); asm.emitMOVZX_Reg_Reg_Word(T0, T0); asm.emitPUSH_Reg(T0); } @Override protected void emit_i2s() { // This could be coded as 2 instructions as follows: // asm.emitMOVSX_Reg_RegInd_Word(T0, SP); // asm.emitMOV_RegInd_Reg(SP, T0); // Indirection via ESP requires an extra byte for the indirection, so the // total code size is 6 bytes. The 3 instruction version below is only 4 // bytes long and faster on Pentium 4 benchmarks. asm.emitPOP_Reg(T0); asm.emitMOVSX_Reg_Reg_Word(T0, T0); asm.emitPUSH_Reg(T0); } /* * comparision ops */ @Override protected void emit_regular_lcmp() { if (VM.BuildFor32Addr) { asm.emitPOP_Reg(T0); // (S0:T0) = (high half value2: low half value2) asm.emitPOP_Reg(S0); asm.emitPOP_Reg(T1); // (..:T1) = (.. : low half of value1) asm.emitSUB_Reg_Reg(T1, T0); // T1 = T1 - T0 asm.emitPOP_Reg(T0); // (T0:..) = (high half of value1 : ..) // NB pop does not alter the carry register asm.emitSBB_Reg_Reg(T0, S0); // T0 = T0 - S0 - CF asm.emitOR_Reg_Reg(T1, T0); // T1 = T1 | T0 updating ZF asm.emitSET_Cond_Reg_Byte(NE, T1); asm.emitMOVZX_Reg_Reg_Byte(T1, T1); // T1 = (value1 != value2) ? 1 : 0 asm.emitSAR_Reg_Imm(T0, 31); // T0 = (value1 < value2) ? -1 : 0 asm.emitOR_Reg_Reg(T1, T0); // T1 = T1 | T0 asm.emitPUSH_Reg(T1); // push result on stack } else { // using a shift in 64bits costs an extra byte in the opcode asm.emitPOP_Reg(T0); // T0 is long value2 adjustStack(WORDSIZE, true); // throw away slot asm.emitPOP_Reg(T1); // T1 is long value1 adjustStack(WORDSIZE, true); // throw away slot asm.emitCMP_Reg_Reg_Quad(T1, T0); // 64bit compare asm.emitSET_Cond_Reg_Byte(LT, T0); // T0 = value1 < value2 ? 1 : 0 asm.emitSET_Cond_Reg_Byte(GT, T1); // T1 = value1 > value2 ? 1 : 0 asm.emitSUB_Reg_Reg_Byte(T1, T0); // T1 = (value1 > value2 ? 1 : 0) - (value1 < value2 ? 1 : 0) asm.emitMOVSX_Reg_Reg_Byte(T1, T1); // Fix sign extension asm.emitPUSH_Reg(T1); // push result on stack } } @Override protected void emit_regular_DFcmpGL(boolean single, boolean unorderedGT) { if (SSE2_BASE) { if (single) { asm.emitMOVSS_Reg_RegInd(XMM0, SP); // XMM0 = value2 asm.emitMOVSS_Reg_RegDisp(XMM1, SP, ONE_SLOT); // XMM1 = value1 adjustStack(WORDSIZE * 2, true); // throw away slots } else { asm.emitMOVSD_Reg_RegInd(XMM0, SP); // XMM0 = value2 asm.emitMOVSD_Reg_RegDisp(XMM1, SP, TWO_SLOTS); // XMM1 = value1 adjustStack(WORDSIZE * 4, true); // throw away slots } } else { if (single) { asm.emitFLD_Reg_RegInd(FP0, SP); // Setup value2 into FP1, asm.emitFLD_Reg_RegDisp(FP0, SP, ONE_SLOT); // value1 into FP0 adjustStack(WORDSIZE * 2, true); // throw away slots } else { asm.emitFLD_Reg_RegInd_Quad(FP0, SP); // Setup value2 into FP1, asm.emitFLD_Reg_RegDisp_Quad(FP0, SP, TWO_SLOTS); // value1 into FP0 adjustStack(WORDSIZE * 4, true); // throw away slots } } if (unorderedGT) { asm.emitMOV_Reg_Imm(T0, 1); // result/T0 = 1 (high bits are 0) } else { asm.emitXOR_Reg_Reg(T0, T0); // clear high bits of result } if (SSE2_BASE) { if (single) { asm.emitUCOMISS_Reg_Reg(XMM1, XMM0); // compare value1 and value2 } else { asm.emitUCOMISD_Reg_Reg(XMM1, XMM0); // compare value1 and value2 } } else { asm.emitFUCOMIP_Reg_Reg(FP0, FP1); // compare and pop FPU *1 } ForwardReference fr1 = null; if (unorderedGT) { fr1 = asm.forwardJcc(PE); // if unordered goto push result (1) } asm.emitSET_Cond_Reg_Byte(LGT, T0); // T0 = XMM0 > XMM1 ? 1 : 0 asm.emitSBB_Reg_Imm(T0, 0); // T0 -= XMM0 < or unordered XMM1 ? 1 : 0 if (unorderedGT) { fr1.resolve(asm); } asm.emitPUSH_Reg(T0); // push result on stack if (!SSE2_BASE) { asm.emitFSTP_Reg_Reg(FP0, FP0); // pop FPU*1 } } /* * branching */ /** * @param bc the branch condition * @return assembler constant equivalent for the branch condition */ @Pure private byte mapCondition(BranchCondition bc) { switch (bc) { case EQ: return EQ; case NE: return NE; case LT: return LT; case GE: return GE; case GT: return GT; case LE: return LE; default: if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED); return -1; } } @Override @Inline(value = Inline.When.ArgumentsAreConstant, arguments = {2}) protected void emit_lcmp_if(int bTarget, BranchCondition bc) { if (VM.BuildFor32Addr) { if (bc == BranchCondition.LE || bc == BranchCondition.GT) { // flip operands in these cases if (bc == BranchCondition.LE) { bc = BranchCondition.GE; } else { bc = BranchCondition.LT; } asm.emitPOP_Reg(T1); // (T0:T1) = (high half value2: low half value2) asm.emitPOP_Reg(T0); asm.emitPOP_Reg(S0); // (..:S0) = (.. : low half of value1) asm.emitSUB_Reg_Reg(T1, S0); // T1 = T1 - S0 asm.emitPOP_Reg(S0); // (S0:..) = (high half of value1 : ..) // NB pop does not alter the carry register asm.emitSBB_Reg_Reg(T0, S0); // T0 = T0 - S0 - CF } else { asm.emitPOP_Reg(T0); // (S0:T0) = (high half value2: low half value2) asm.emitPOP_Reg(S0); asm.emitPOP_Reg(T1); // (..:T1) = (.. : low half of value1) asm.emitSUB_Reg_Reg(T1, T0); // T1 = T1 - T0 asm.emitPOP_Reg(T0); // (T0:..) = (high half of value1 : ..) // NB pop does not alter the carry register asm.emitSBB_Reg_Reg(T0, S0); // T0 = T0 - S0 - CF if (bc == BranchCondition.EQ || bc == BranchCondition.NE) { asm.emitOR_Reg_Reg(T1, T0); // T1 = T1 | T0 updating ZF } } } else { asm.emitPOP_Reg(T0); // T0 is long value2 adjustStack(WORDSIZE, true); // throw away slot asm.emitPOP_Reg(T1); // T1 is long value1 adjustStack(WORDSIZE, true); // throw away slot asm.emitCMP_Reg_Reg_Quad(T1, T0); // 64bit compare } genCondBranch(mapCondition(bc), bTarget); } @Override protected void emit_DFcmpGL_if(boolean single, boolean unorderedGT, int bTarget, BranchCondition bc) { if (SSE2_BASE) { if (single) { asm.emitMOVSS_Reg_RegInd(XMM0, SP); // XMM0 = value2 asm.emitMOVSS_Reg_RegDisp(XMM1, SP, ONE_SLOT); // XMM1 = value1 adjustStack(WORDSIZE * 2, true); // throw away slots } else { asm.emitMOVSD_Reg_RegInd(XMM0, SP); // XMM0 = value2 asm.emitMOVSD_Reg_RegDisp(XMM1, SP, TWO_SLOTS); // XMM1 = value1 adjustStack(WORDSIZE * 4, true); // throw away slots } } else { if (single) { asm.emitFLD_Reg_RegInd(FP0, SP); // Setup value2 into FP1, asm.emitFLD_Reg_RegDisp(FP0, SP, ONE_SLOT); // value1 into FP0 adjustStack(WORDSIZE * 2, true); // throw away slots } else { asm.emitFLD_Reg_RegInd_Quad(FP0, SP); // Setup value2 into FP1, asm.emitFLD_Reg_RegDisp_Quad(FP0, SP, TWO_SLOTS); // value1 into FP0 adjustStack(WORDSIZE * 4, true); // throw away slots } } if (SSE2_BASE) { if (single) { asm.emitUCOMISS_Reg_Reg(XMM1, XMM0); // compare value1 and value2 } else { asm.emitUCOMISD_Reg_Reg(XMM1, XMM0); // compare value1 and value2 } } else { asm.emitFUCOMIP_Reg_Reg(FP0, FP1); // compare and pop FPU *1 asm.emitFSTP_Reg_Reg(FP0, FP0); // pop FPU*1 } byte asm_bc = -1; boolean unordered_taken = false; switch (bc) { case EQ: asm_bc = EQ; unordered_taken = false; break; case NE: asm_bc = NE; unordered_taken = true; break; case LT: asm_bc = LLT; unordered_taken = !unorderedGT; break; case GE: asm_bc = LGE; unordered_taken = unorderedGT; break; case GT: asm_bc = LGT; unordered_taken = unorderedGT; break; case LE: asm_bc = LLE; unordered_taken = !unorderedGT; break; default: if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED); } int mTarget = bytecodeMap[bTarget]; if (!VM.runningTool && ((BaselineCompiledMethod) compiledMethod).hasCounterArray()) { // Allocate two counters: taken and not taken int entry = edgeCounterIdx; edgeCounterIdx += 2; if (!unordered_taken) { ForwardReference notTaken1 = asm.forwardJcc(PE); ForwardReference notTaken2 = asm.forwardJcc(asm.flipCode(asm_bc)); // Increment taken counter & jump to target incEdgeCounter(T1, null, entry + EdgeCounts.TAKEN); asm.emitJMP_ImmOrLabel(mTarget, bTarget); // Increment not taken counter notTaken1.resolve(asm); notTaken2.resolve(asm); incEdgeCounter(T1, null, entry + EdgeCounts.NOT_TAKEN); } else { ForwardReference taken1 = asm.forwardJcc(PE); ForwardReference taken2 = asm.forwardJcc(asm_bc); // Increment taken counter & jump to target incEdgeCounter(T1, null, entry + EdgeCounts.NOT_TAKEN); ForwardReference notTaken = asm.forwardJMP(); // Increment taken counter taken1.resolve(asm); taken2.resolve(asm); incEdgeCounter(T1, null, entry + EdgeCounts.TAKEN); asm.emitJMP_ImmOrLabel(mTarget, bTarget); notTaken.resolve(asm); } } else { if (unordered_taken) { asm.emitJCC_Cond_ImmOrLabel(PE, mTarget, bTarget); asm.emitJCC_Cond_ImmOrLabel(asm_bc, mTarget, bTarget); } else { ForwardReference notTaken = asm.forwardJcc(PE); asm.emitJCC_Cond_ImmOrLabel(asm_bc, mTarget, bTarget); notTaken.resolve(asm); } } } @Override @Inline(value = Inline.When.ArgumentsAreConstant, arguments = {2}) protected void emit_if(int bTarget, BranchCondition bc) { asm.emitPOP_Reg(T0); asm.emitTEST_Reg_Reg(T0, T0); genCondBranch(mapCondition(bc), bTarget); } @Override @Inline(value = Inline.When.ArgumentsAreConstant, arguments = {2}) protected void emit_if_icmp(int bTarget, BranchCondition bc) { asm.emitPOP_Reg(T1); asm.emitPOP_Reg(T0); asm.emitCMP_Reg_Reg(T0, T1); genCondBranch(mapCondition(bc), bTarget); } @Override protected void emit_if_acmpeq(int bTarget) { asm.emitPOP_Reg(S0); asm.emitPOP_Reg(T0); if (VM.BuildFor32Addr) { asm.emitCMP_Reg_Reg(T0, S0); } else { asm.emitCMP_Reg_Reg_Quad(T0, S0); } genCondBranch(EQ, bTarget); } @Override protected void emit_if_acmpne(int bTarget) { asm.emitPOP_Reg(S0); asm.emitPOP_Reg(T0); if (VM.BuildFor32Addr) { asm.emitCMP_Reg_Reg(T0, S0); } else { asm.emitCMP_Reg_Reg_Quad(T0, S0); } genCondBranch(NE, bTarget); } @Override protected void emit_ifnull(int bTarget) { asm.emitPOP_Reg(T0); if (VM.BuildFor32Addr) { asm.emitTEST_Reg_Reg(T0, T0); } else { asm.emitTEST_Reg_Reg_Quad(T0, T0); } genCondBranch(EQ, bTarget); } @Override protected void emit_ifnonnull(int bTarget) { asm.emitPOP_Reg(T0); if (VM.BuildFor32Addr) { asm.emitTEST_Reg_Reg(T0, T0); } else { asm.emitTEST_Reg_Reg_Quad(T0, T0); } genCondBranch(NE, bTarget); } @Override protected void emit_goto(int bTarget) { int mTarget = bytecodeMap[bTarget]; asm.emitJMP_ImmOrLabel(mTarget, bTarget); } @Override protected void emit_jsr(int bTarget) { int mTarget = bytecodeMap[bTarget]; asm.emitCALL_ImmOrLabel(mTarget, bTarget); } @Override protected void emit_ret(int index) { try { Offset offset = localOffset(index); // Can be: // asm.emitJMP_RegDisp(ESP, offset); // but this will cause call-return branch prediction pairing to fail asm.emitPUSH_RegDisp(ESP, offset); asm.emitRET(); } catch (UnreachableBytecodeException e) { asm.emitINT_Imm(TRAP_UNREACHABLE_BYTECODE + RVM_TRAP_BASE); } } @Override protected void emit_tableswitch(int defaultval, int low, int high) { int bTarget = biStart + defaultval; int mTarget = bytecodeMap[bTarget]; int n = high - low + 1; // n = number of normal cases (0..n-1) asm.emitPOP_Reg(T1); // T1 is index of desired case asm.emitSUB_Reg_Imm(T1, low); // relativize T1 asm.emitCMP_Reg_Imm(T1, n); // 0 <= relative index < n if (!VM.runningTool && ((BaselineCompiledMethod) compiledMethod).hasCounterArray()) { int firstCounter = edgeCounterIdx; edgeCounterIdx += (n + 1); // Jump around code for default case ForwardReference fr = asm.forwardJcc(LLT); incEdgeCounter(S0, null, firstCounter + n); asm.emitJMP_ImmOrLabel(mTarget, bTarget); fr.resolve(asm); // Increment counter for the appropriate case incEdgeCounter(S0, T1, firstCounter); } else { asm.emitJCC_Cond_ImmOrLabel(LGE, mTarget, bTarget); // if not, goto default case } // T0 = EIP at start of method asm.emitMETHODSTART_Reg(T0); asm.emitTableswitchCode(T0, T1); // Emit data for the tableswitch, i.e. the addresses that will be // loaded for the cases for (int i = 0; i < n; i++) { int offset = bcodes.getTableSwitchOffset(i); bTarget = biStart + offset; mTarget = bytecodeMap[bTarget]; asm.emitOFFSET_Imm_ImmOrLabel(i, mTarget, bTarget); } bcodes.skipTableSwitchOffsets(n); } /** * Emit code to implement the lookupswitch bytecode. * Uses linear search, one could use a binary search tree instead, * but this is the baseline compiler, so don't worry about it. * * @param defaultval bcIndex of the default target * @param npairs number of pairs in the lookup switch */ @Override protected void emit_lookupswitch(int defaultval, int npairs) { asm.emitPOP_Reg(T0); for (int i = 0; i < npairs; i++) { int match = bcodes.getLookupSwitchValue(i); asm.emitCMP_Reg_Imm(T0, match); int offset = bcodes.getLookupSwitchOffset(i); int bTarget = biStart + offset; int mTarget = bytecodeMap[bTarget]; if (!VM.runningTool && ((BaselineCompiledMethod) compiledMethod).hasCounterArray()) { // Flip conditions so we can jump over the increment of the taken counter. ForwardReference fr = asm.forwardJcc(NE); incEdgeCounter(S0, null, edgeCounterIdx++); asm.emitJMP_ImmOrLabel(mTarget, bTarget); fr.resolve(asm); } else { asm.emitJCC_Cond_ImmOrLabel(EQ, mTarget, bTarget); } } bcodes.skipLookupSwitchPairs(npairs); int bTarget = biStart + defaultval; int mTarget = bytecodeMap[bTarget]; if (!VM.runningTool && ((BaselineCompiledMethod) compiledMethod).hasCounterArray()) { incEdgeCounter(S0, null, edgeCounterIdx++); // increment default counter } asm.emitJMP_ImmOrLabel(mTarget, bTarget); } /* * returns (from function; NOT ret) */ @Override protected void emit_ireturn() { if (method.isSynchronized()) genMonitorExit(); asm.emitPOP_Reg(T0); if (VM.BuildFor64Addr) { asm.emitAND_Reg_Reg(T0, T0); // clear MSBs } genEpilogue(WORDSIZE, WORDSIZE); } @Override protected void emit_lreturn() { if (method.isSynchronized()) genMonitorExit(); if (VM.BuildFor32Addr) { asm.emitPOP_Reg(T1); // low half asm.emitPOP_Reg(T0); // high half genEpilogue(2 * WORDSIZE, 2 * WORDSIZE); } else { asm.emitPOP_Reg(T0); genEpilogue(2 * WORDSIZE, WORDSIZE); } } @Override protected void emit_freturn() { if (method.isSynchronized()) genMonitorExit(); if (SSE2_FULL) { asm.emitMOVSS_Reg_RegInd(XMM0, SP); } else { asm.emitFLD_Reg_RegInd(FP0, SP); } genEpilogue(WORDSIZE, 0); } @Override protected void emit_dreturn() { if (method.isSynchronized()) genMonitorExit(); if (SSE2_FULL) { asm.emitMOVSD_Reg_RegInd(XMM0, SP); } else { asm.emitFLD_Reg_RegInd_Quad(FP0, SP); } genEpilogue(2 * WORDSIZE, 0); } @Override protected void emit_areturn() { if (method.isSynchronized()) genMonitorExit(); asm.emitPOP_Reg(T0); genEpilogue(WORDSIZE, WORDSIZE); } @Override protected void emit_return() { if (method.isSynchronized()) genMonitorExit(); genEpilogue(0, 0); } /* * field access */ @Override protected void emit_unresolved_getstatic(FieldReference fieldRef) { emitDynamicLinkingSequence(asm, T0, fieldRef, true); if (NEEDS_OBJECT_GETSTATIC_BARRIER && !fieldRef.getFieldContentsType().isPrimitiveType()) { Barriers.compileGetstaticBarrier(asm, T0, fieldRef.getId()); return; } if (fieldRef.getSize() <= BYTES_IN_INT) { // get static field - [SP--] = [T0<<0+JTOC] if (VM.BuildFor32Addr) { asm.emitPUSH_RegDisp(T0, Magic.getTocPointer().toWord().toOffset()); } else { asm.generateJTOCloadInt(T0, T0); asm.emitPUSH_Reg(T0); } } else { // field is two words (double or long) if (VM.VerifyAssertions) VM._assert(fieldRef.getSize() == BYTES_IN_LONG); if (VM.BuildFor32Addr) { asm.emitPUSH_RegDisp(T0, Magic.getTocPointer().toWord().toOffset().plus(WORDSIZE)); // get high part asm.emitPUSH_RegDisp(T0, Magic.getTocPointer().toWord().toOffset()); // get low part } else { if (fieldRef.getNumberOfStackSlots() != 1) { adjustStack(-WORDSIZE, true); } asm.generateJTOCpush(T0); } } } @Override protected void emit_resolved_getstatic(FieldReference fieldRef) { RVMField field = fieldRef.peekResolvedField(); Offset fieldOffset = field.getOffset(); if (NEEDS_OBJECT_GETSTATIC_BARRIER && !fieldRef.getFieldContentsType().isPrimitiveType() && !field.isUntraced()) { Barriers.compileGetstaticBarrierImm(asm, fieldOffset, fieldRef.getId()); return; } if (fieldRef.getSize() <= BYTES_IN_INT) { // field is one word if (VM.BuildFor32Addr) { asm.emitPUSH_Abs(Magic.getTocPointer().plus(fieldOffset)); } else { asm.generateJTOCloadInt(T0, fieldOffset); asm.emitPUSH_Reg(T0); } } else { // field is two words (double or long) if (VM.VerifyAssertions) VM._assert(fieldRef.getSize() == BYTES_IN_LONG); if (VM.BuildFor32Addr) { asm.emitPUSH_Abs(Magic.getTocPointer().plus(fieldOffset).plus(WORDSIZE)); // get high part asm.emitPUSH_Abs(Magic.getTocPointer().plus(fieldOffset)); // get low part } else { if (fieldRef.getNumberOfStackSlots() != 1) { adjustStack(-WORDSIZE, true); } asm.generateJTOCpush(fieldOffset); } } } @Override protected void emit_unresolved_putstatic(FieldReference fieldRef) { emitDynamicLinkingSequence(asm, T0, fieldRef, true); if (NEEDS_OBJECT_PUTSTATIC_BARRIER && fieldRef.getFieldContentsType().isReferenceType()) { Barriers.compilePutstaticBarrier(asm, T0, fieldRef.getId()); } else { if (fieldRef.getSize() <= BYTES_IN_INT) { // field is one word if (VM.BuildFor32Addr) { asm.emitPOP_RegDisp(T0, Magic.getTocPointer().toWord().toOffset()); } else { asm.emitPOP_Reg(T1); asm.generateJTOCstoreInt(T0, T1); } } else { // field is two words (double or long) if (VM.VerifyAssertions) VM._assert(fieldRef.getSize() == BYTES_IN_LONG); if (VM.BuildFor32Addr) { asm.emitPOP_RegDisp(T0, Magic.getTocPointer().toWord().toOffset()); // store low part asm.emitPOP_RegDisp(T0, Magic.getTocPointer().toWord().toOffset().plus(WORDSIZE)); // store high part } else { asm.generateJTOCpop(T0); if (fieldRef.getNumberOfStackSlots() != 1) { adjustStack(WORDSIZE, true); } } } } // The field may be volatile asm.emitMFENCE(); } @Override protected void emit_resolved_putstatic(FieldReference fieldRef) { RVMField field = fieldRef.peekResolvedField(); Offset fieldOffset = field.getOffset(); if (NEEDS_OBJECT_PUTSTATIC_BARRIER && field.isReferenceType() && !field.isUntraced()) { Barriers.compilePutstaticBarrierImm(asm, fieldOffset, fieldRef.getId()); } else { if (field.getSize() <= BYTES_IN_INT) { // field is one word if (VM.BuildFor32Addr) { asm.emitPOP_Abs(Magic.getTocPointer().plus(fieldOffset)); } else { asm.emitPOP_Reg(T1); asm.generateJTOCstoreInt(fieldOffset, T1); } } else { // field is two words (double or long) if (VM.VerifyAssertions) VM._assert(fieldRef.getSize() == BYTES_IN_LONG); if (VM.BuildFor32Addr) { asm.generateJTOCpop(fieldOffset); // store low part asm.generateJTOCpop(fieldOffset.plus(WORDSIZE)); // store high part } else { asm.generateJTOCpop(fieldOffset); if (fieldRef.getNumberOfStackSlots() != 1) { adjustStack(WORDSIZE, true); } } } } if (field.isVolatile()) { asm.emitMFENCE(); } } @Override protected void emit_unresolved_getfield(FieldReference fieldRef) { TypeReference fieldType = fieldRef.getFieldContentsType(); emitDynamicLinkingSequence(asm, T0, fieldRef, true); if (fieldType.isReferenceType()) { // 32/64bit reference load if (NEEDS_OBJECT_GETFIELD_BARRIER) { Barriers.compileGetfieldBarrier(asm, T0, fieldRef.getId()); } else { asm.emitPOP_Reg(S0); // S0 is object reference asm.emitPUSH_RegIdx(S0, T0, BYTE, NO_SLOT); // place field value on stack } } else if (fieldType.isBooleanType()) { // 8bit unsigned load asm.emitPOP_Reg(S0); // S0 is object reference asm.emitMOVZX_Reg_RegIdx_Byte(T1, S0, T0, BYTE, NO_SLOT); // T1 is field value asm.emitPUSH_Reg(T1); // place value on stack } else if (fieldType.isByteType()) { // 8bit signed load asm.emitPOP_Reg(S0); // S0 is object reference asm.emitMOVSX_Reg_RegIdx_Byte(T1, S0, T0, BYTE, NO_SLOT); // T1 is field value asm.emitPUSH_Reg(T1); // place value on stack } else if (fieldType.isShortType()) { // 16bit signed load asm.emitPOP_Reg(S0); // S0 is object reference asm.emitMOVSX_Reg_RegIdx_Word(T1, S0, T0, BYTE, NO_SLOT); // T1 is field value asm.emitPUSH_Reg(T1); // place value on stack } else if (fieldType.isCharType()) { // 16bit unsigned load asm.emitPOP_Reg(S0); // S0 is object reference asm.emitMOVZX_Reg_RegIdx_Word(T1, S0, T0, BYTE, NO_SLOT); // T1 is field value asm.emitPUSH_Reg(T1); // place value on stack } else if (fieldType.isIntType() || fieldType.isFloatType() || (VM.BuildFor32Addr && fieldType.isWordLikeType())) { // 32bit load asm.emitPOP_Reg(S0); // S0 is object reference if (VM.BuildFor32Addr) { asm.emitPUSH_RegIdx(S0, T0, BYTE, NO_SLOT); // place field value on stack } else { asm.emitMOV_Reg_RegIdx(T1, S0, T0, BYTE, NO_SLOT); // T1 is field value asm.emitPUSH_Reg(T1); // place value on stack } } else { // 64bit load if (VM.VerifyAssertions) { VM._assert(fieldType.isLongType() || fieldType.isDoubleType() || (VM.BuildFor64Addr && fieldType.isWordLikeType())); } asm.emitPOP_Reg(T1); // T1 is object reference // NB it's unknown whether the field is volatile, so it is necessary to // emit instruction sequences that provide atomic access. if (VM.BuildFor32Addr) { // NB this is a 64bit copy from memory to the stack so implement // as a slightly optimized Intel memory copy using the FPU if (SSE2_BASE) { asm.emitMOVQ_Reg_RegIdx(XMM0, T1, T0, BYTE, NO_SLOT); // XMM0 is field value adjustStack(-2 * WORDSIZE, true); // adjust stack down to hold 64bit value asm.emitMOVQ_RegInd_Reg(SP, XMM0); // place value on stack } else { asm.emitFLD_Reg_RegIdx_Quad(FP0, T1, T0, BYTE, NO_SLOT); // FP0 is field value adjustStack(-2 * WORDSIZE, true); // adjust stack down to hold 64bit value asm.emitFSTP_RegInd_Reg_Quad(SP, FP0); // place value on stack } } else { if (!fieldType.isWordLikeType()) { // Note that the stack musn't be clobbered at this point. // If the stack was clobbered and a NPE occurred and a // garbage collection was triggered in exception handling // (e.g. in a gcstress build), the stack state would not be // as the GC maps expect which would lead to a failure with // a "bad GC map". adjustStack(-WORDSIZE, false); // add empty slot } asm.emitPUSH_RegIdx(T1, T0, BYTE, NO_SLOT); // place value on stack } } } @Override protected void emit_resolved_getfield(FieldReference fieldRef) { TypeReference fieldType = fieldRef.getFieldContentsType(); RVMField field = fieldRef.peekResolvedField(); Offset fieldOffset = field.getOffset(); if (field.isReferenceType()) { // 32/64bit reference load if (NEEDS_OBJECT_GETFIELD_BARRIER && !field.isUntraced()) { Barriers.compileGetfieldBarrierImm(asm, fieldOffset, fieldRef.getId()); } else { asm.emitPOP_Reg(T0); // T0 is object reference asm.emitPUSH_RegDisp(T0, fieldOffset); // place field value on stack } } else if (fieldType.isBooleanType()) { // 8bit unsigned load asm.emitPOP_Reg(S0); // S0 is object reference asm.emitMOVZX_Reg_RegDisp_Byte(T0, S0, fieldOffset); // T0 is field value asm.emitPUSH_Reg(T0); // place value on stack } else if (fieldType.isByteType()) { // 8bit signed load asm.emitPOP_Reg(S0); // S0 is object reference asm.emitMOVSX_Reg_RegDisp_Byte(T0, S0, fieldOffset); // T0 is field value asm.emitPUSH_Reg(T0); // place value on stack } else if (fieldType.isShortType()) { // 16bit signed load asm.emitPOP_Reg(S0); // S0 is object reference asm.emitMOVSX_Reg_RegDisp_Word(T0, S0, fieldOffset); // T0 is field value asm.emitPUSH_Reg(T0); // place value on stack } else if (fieldType.isCharType()) { // 16bit unsigned load asm.emitPOP_Reg(S0); // S0 is object reference asm.emitMOVZX_Reg_RegDisp_Word(T0, S0, fieldOffset); // T0 is field value asm.emitPUSH_Reg(T0); // place value on stack } else if (fieldType.isIntType() || fieldType.isFloatType() || (VM.BuildFor32Addr && fieldType.isWordLikeType())) { // 32bit load asm.emitPOP_Reg(S0); // S0 is object reference if (VM.BuildFor32Addr) { asm.emitPUSH_RegDisp(S0, fieldOffset); // place value on stack } else { asm.emitMOV_Reg_RegDisp(T0, S0, fieldOffset); // T0 is field value asm.emitPUSH_Reg(T0); // place value on stack } } else { // 64bit load if (VM.VerifyAssertions) { VM._assert(fieldType.isLongType() || fieldType.isDoubleType() || (VM.BuildFor64Addr && fieldType.isWordLikeType())); } asm.emitPOP_Reg(T0); // T0 is object reference if (VM.BuildFor32Addr && field.isVolatile()) { // NB this is a 64bit copy from memory to the stack so implement // as a slightly optimized Intel memory copy using the FPU if (SSE2_BASE) { asm.emitMOVQ_Reg_RegDisp(XMM0, T0, fieldOffset); // XMM0 is field value adjustStack(-2 * WORDSIZE, true); // adjust stack down to hold 64bit value asm.emitMOVQ_RegInd_Reg(SP, XMM0); // replace reference with value on stack } else { asm.emitFLD_Reg_RegDisp_Quad(FP0, T0, fieldOffset); // FP0 is field value adjustStack(-2 * WORDSIZE, true); // adjust stack down to hold 64bit value asm.emitFSTP_RegInd_Reg_Quad(SP, FP0); // replace reference with value on stack } } else if (VM.BuildFor32Addr && !field.isVolatile()) { asm.emitPUSH_RegDisp(T0, fieldOffset.plus(ONE_SLOT)); // place high half on stack asm.emitPUSH_RegDisp(T0, fieldOffset); // place low half on stack } else { if (!fieldType.isWordLikeType()) { adjustStack(-WORDSIZE, true); // add empty slot } asm.emitPUSH_RegDisp(T0, fieldOffset); // place value on stack } } } /** * Emits code to load a reference local variable and then perform a field load * @param index the local index to load * @param fieldRef the referenced field */ @Override protected void emit_aload_resolved_getfield(int index, FieldReference fieldRef) { try { Offset offset = localOffset(index); TypeReference fieldType = fieldRef.getFieldContentsType(); RVMField field = fieldRef.peekResolvedField(); Offset fieldOffset = field.getOffset(); if (field.isReferenceType()) { // 32/64bit reference load if (NEEDS_OBJECT_GETFIELD_BARRIER && !field.isUntraced()) { emit_regular_aload(index); Barriers.compileGetfieldBarrierImm(asm, fieldOffset, fieldRef.getId()); } else { stackMoveHelper(S0, offset); // S0 is object reference asm.emitPUSH_RegDisp(S0, fieldOffset); // place field value on stack } } else if (fieldType.isBooleanType()) { // 8bit unsigned load stackMoveHelper(S0, offset); // S0 is object reference asm.emitMOVZX_Reg_RegDisp_Byte(T0, S0, fieldOffset); // T0 is field value asm.emitPUSH_Reg(T0); // place value on stack } else if (fieldType.isByteType()) { // 8bit signed load stackMoveHelper(S0, offset); // S0 is object reference asm.emitMOVSX_Reg_RegDisp_Byte(T0, S0, fieldOffset); // T0 is field value asm.emitPUSH_Reg(T0); // place value on stack } else if (fieldType.isShortType()) { // 16bit signed load stackMoveHelper(S0, offset); // S0 is object reference asm.emitMOVSX_Reg_RegDisp_Word(T0, S0, fieldOffset); // T0 is field value asm.emitPUSH_Reg(T0); // place value on stack } else if (fieldType.isCharType()) { // 16bit unsigned load stackMoveHelper(S0, offset); // S0 is object reference asm.emitMOVZX_Reg_RegDisp_Word(T0, S0, fieldOffset); // T0 is field value asm.emitPUSH_Reg(T0); // place value on stack } else if (fieldType.isIntType() || fieldType.isFloatType() || (VM.BuildFor32Addr && fieldType.isWordType())) { // 32bit load stackMoveHelper(S0, offset); // S0 is object reference if (VM.BuildFor32Addr) { asm.emitPUSH_RegDisp(S0, fieldOffset); // place value on stack } else { asm.emitMOV_Reg_RegDisp(T0, S0, fieldOffset); // T0 is field value asm.emitPUSH_Reg(T0); // place value on stack } } else { // 64bit load if (VM.VerifyAssertions) { VM._assert(fieldType.isLongType() || fieldType.isDoubleType() || (VM.BuildFor64Addr && fieldType.isWordType())); } stackMoveHelper(S0, offset); // S0 is object reference if (VM.BuildFor32Addr && field.isVolatile()) { // NB this is a 64bit copy from memory to the stack so implement // as a slightly optimized Intel memory copy using the FPU if (SSE2_BASE) { asm.emitMOVQ_Reg_RegDisp(XMM0, S0, fieldOffset); // XMM0 is field value adjustStack(-2 * WORDSIZE, true); // adjust stack down to hold 64bit value asm.emitMOVQ_RegInd_Reg(SP, XMM0); // replace reference with value on stack } else { asm.emitFLD_Reg_RegDisp_Quad(FP0, S0, fieldOffset); // FP0 is field value adjustStack(-2 * WORDSIZE, true); // adjust stack down to hold 64bit value asm.emitFSTP_RegInd_Reg_Quad(SP, FP0); // replace reference with value on stack } } else if (VM.BuildFor32Addr && !field.isVolatile()) { asm.emitPUSH_RegDisp(S0, fieldOffset.plus(ONE_SLOT)); // place high half on stack asm.emitPUSH_RegDisp(S0, fieldOffset); // place low half on stack } else { if (!fieldType.isWordType()) { adjustStack(-WORDSIZE, true); // add empty slot } asm.emitPUSH_RegDisp(S0, fieldOffset); // place value on stack } } } catch (UnreachableBytecodeException e) { asm.emitINT_Imm(TRAP_UNREACHABLE_BYTECODE + RVM_TRAP_BASE); } } @Override protected void emit_unresolved_putfield(FieldReference fieldRef) { Barriers.compileModifyCheck(asm, fieldRef.getNumberOfStackSlots() * WORDSIZE); TypeReference fieldType = fieldRef.getFieldContentsType(); emitDynamicLinkingSequence(asm, T0, fieldRef, true); if (fieldType.isReferenceType()) { // 32/64bit reference store if (NEEDS_OBJECT_PUTFIELD_BARRIER) { Barriers.compilePutfieldBarrier(asm, T0, fieldRef.getId()); } else { asm.emitPOP_Reg(T1); // T1 is the value to be stored asm.emitPOP_Reg(S0); // S0 is the object reference if (VM.BuildFor32Addr) { asm.emitMOV_RegIdx_Reg(S0, T0, BYTE, NO_SLOT, T1); // [S0+T0] <- T1 } else { asm.emitMOV_RegIdx_Reg_Quad(S0, T0, BYTE, NO_SLOT, T1); // [S0+T0] <- T1 } } } else if (NEEDS_BOOLEAN_PUTFIELD_BARRIER && fieldType.isBooleanType()) { Barriers.compilePutfieldBarrierBoolean(asm, T0, fieldRef.getId(), this); } else if (NEEDS_BYTE_PUTFIELD_BARRIER && fieldType.isByteType()) { Barriers.compilePutfieldBarrierByte(asm, T0, fieldRef.getId(), this); } else if (NEEDS_CHAR_PUTFIELD_BARRIER && fieldType.isCharType()) { Barriers.compilePutfieldBarrierChar(asm, T0, fieldRef.getId(), this); } else if (NEEDS_DOUBLE_PUTFIELD_BARRIER && fieldType.isDoubleType()) { Barriers.compilePutfieldBarrierDouble(asm, T0, fieldRef.getId(), this); } else if (NEEDS_FLOAT_PUTFIELD_BARRIER && fieldType.isFloatType()) { Barriers.compilePutfieldBarrierFloat(asm, T0, fieldRef.getId(), this); } else if (NEEDS_INT_PUTFIELD_BARRIER && fieldType.isIntType()) { Barriers.compilePutfieldBarrierInt(asm, T0, fieldRef.getId(), this); } else if (NEEDS_LONG_PUTFIELD_BARRIER && fieldType.isLongType()) { Barriers.compilePutfieldBarrierLong(asm, T0, fieldRef.getId(), this); } else if (NEEDS_SHORT_PUTFIELD_BARRIER && fieldType.isShortType()) { Barriers.compilePutfieldBarrierShort(asm, T0, fieldRef.getId(), this); } else if (NEEDS_WORD_PUTFIELD_BARRIER && fieldType.isWordType()) { Barriers.compilePutfieldBarrierWord(asm, T0, fieldRef.getId(), this); } else if (NEEDS_ADDRESS_PUTFIELD_BARRIER && fieldType.isAddressType()) { Barriers.compilePutfieldBarrierAddress(asm, T0, fieldRef.getId(), this); } else if (NEEDS_OFFSET_PUTFIELD_BARRIER && fieldType.isOffsetType()) { Barriers.compilePutfieldBarrierOffset(asm, T0, fieldRef.getId(), this); } else if (NEEDS_EXTENT_PUTFIELD_BARRIER && fieldType.isExtentType()) { Barriers.compilePutfieldBarrierExtent(asm, T0, fieldRef.getId(), this); } else if (fieldType.isBooleanType() || fieldType.isByteType()) { // no need for primitive write barriers // 8bit store asm.emitPOP_Reg(T1); // T1 is the value to be stored asm.emitPOP_Reg(S0); // S0 is the object reference asm.emitMOV_RegIdx_Reg_Byte(S0, T0, BYTE, NO_SLOT, T1); // [S0+T0] <- T1 } else if (fieldType.isShortType() || fieldType.isCharType()) { // 16bit store asm.emitPOP_Reg(T1); // T1 is the value to be stored asm.emitPOP_Reg(S0); // S0 is the object reference asm.emitMOV_RegIdx_Reg_Word(S0, T0, BYTE, NO_SLOT, T1); // [S0+T0] <- T1 } else if (fieldType.isIntType() || fieldType.isFloatType() || (VM.BuildFor32Addr && fieldType.isWordLikeType())) { // 32bit store asm.emitPOP_Reg(T1); // T1 is the value to be stored asm.emitPOP_Reg(S0); // S0 is the object reference asm.emitMOV_RegIdx_Reg(S0, T0, BYTE, NO_SLOT, T1); // [S0+T0] <- T1 } else { // 64bit store if (VM.VerifyAssertions) { VM._assert(fieldType.isLongType() || fieldType.isDoubleType() || (VM.BuildFor64Addr && fieldType.isWordLikeType())); } if (VM.BuildFor32Addr) { // NB this is a 64bit copy from the stack to memory so implement // as a slightly optimized Intel memory copy using the FPU asm.emitMOV_Reg_RegDisp(S0, SP, TWO_SLOTS); // S0 is the object reference if (SSE2_BASE) { asm.emitMOVQ_Reg_RegInd(XMM0, SP); // XMM0 is the value to be stored asm.emitMOVQ_RegIdx_Reg(S0, T0, BYTE, NO_SLOT, XMM0); // [S0+T0] <- XMM0 } else { asm.emitFLD_Reg_RegInd_Quad(FP0, SP); // FP0 is the value to be stored asm.emitFSTP_RegIdx_Reg_Quad(S0, T0, BYTE, NO_SLOT, FP0); // [S0+T0] <- FP0 } if (!fieldType.isWordLikeType()) { adjustStack(WORDSIZE * 3, true); // complete popping the values and reference } else { adjustStack(WORDSIZE * 2, true); // complete popping the values and reference } } else { asm.emitPOP_Reg(T1); // T1 is the value to be stored if (!fieldType.isWordLikeType()) { adjustStack(WORDSIZE, true); // throw away slot } asm.emitPOP_Reg(S0); // S0 is the object reference asm.emitMOV_RegIdx_Reg_Quad(S0, T0, BYTE, NO_SLOT, T1); // [S0+T0] <- T1 } } // The field may be volatile. asm.emitMFENCE(); } @Override protected void emit_resolved_putfield(FieldReference fieldRef) { Barriers.compileModifyCheck(asm, fieldRef.getNumberOfStackSlots() * WORDSIZE); RVMField field = fieldRef.peekResolvedField(); TypeReference fieldType = fieldRef.getFieldContentsType(); Offset fieldOffset = field.getOffset(); if (field.isReferenceType()) { // 32/64bit reference store if (NEEDS_OBJECT_PUTFIELD_BARRIER && !field.isUntraced()) { Barriers.compilePutfieldBarrierImm(asm, fieldOffset, fieldRef.getId()); } else { asm.emitPOP_Reg(T0); // T0 is the value to be stored asm.emitPOP_Reg(S0); // S0 is the object reference // [S0+fieldOffset] <- T0 if (VM.BuildFor32Addr) { asm.emitMOV_RegDisp_Reg(S0, fieldOffset, T0); } else { asm.emitMOV_RegDisp_Reg_Quad(S0, fieldOffset, T0); } } } else if (NEEDS_BOOLEAN_PUTFIELD_BARRIER && fieldType.isBooleanType()) { Barriers.compilePutfieldBarrierBooleanImm(asm, fieldOffset, fieldRef.getId(), this); } else if (NEEDS_BYTE_PUTFIELD_BARRIER && fieldType.isByteType()) { Barriers.compilePutfieldBarrierByteImm(asm, fieldOffset, fieldRef.getId(), this); } else if (NEEDS_CHAR_PUTFIELD_BARRIER && fieldType.isCharType()) { Barriers.compilePutfieldBarrierCharImm(asm, fieldOffset, fieldRef.getId(), this); } else if (NEEDS_DOUBLE_PUTFIELD_BARRIER && fieldType.isDoubleType()) { Barriers.compilePutfieldBarrierDoubleImm(asm, fieldOffset, fieldRef.getId(), this); } else if (NEEDS_FLOAT_PUTFIELD_BARRIER && fieldType.isFloatType()) { Barriers.compilePutfieldBarrierFloatImm(asm, fieldOffset, fieldRef.getId(), this); } else if (NEEDS_INT_PUTFIELD_BARRIER && fieldType.isIntType()) { Barriers.compilePutfieldBarrierIntImm(asm, fieldOffset, fieldRef.getId(), this); } else if (NEEDS_LONG_PUTFIELD_BARRIER && fieldType.isLongType()) { Barriers.compilePutfieldBarrierLongImm(asm, fieldOffset, fieldRef.getId(), this); } else if (NEEDS_SHORT_PUTFIELD_BARRIER && fieldType.isShortType()) { Barriers.compilePutfieldBarrierShortImm(asm, fieldOffset, fieldRef.getId(), this); } else if (NEEDS_WORD_PUTFIELD_BARRIER && fieldType.isWordType()) { Barriers.compilePutfieldBarrierWordImm(asm, fieldOffset, fieldRef.getId(), this); } else if (NEEDS_ADDRESS_PUTFIELD_BARRIER && fieldType.isAddressType()) { Barriers.compilePutfieldBarrierAddressImm(asm, fieldOffset, fieldRef.getId(), this); } else if (NEEDS_OFFSET_PUTFIELD_BARRIER && fieldType.isOffsetType()) { Barriers.compilePutfieldBarrierOffsetImm(asm, fieldOffset, fieldRef.getId(), this); } else if (NEEDS_EXTENT_PUTFIELD_BARRIER && fieldType.isExtentType()) { Barriers.compilePutfieldBarrierExtentImm(asm, fieldOffset, fieldRef.getId(), this); } else if (field.getSize() == BYTES_IN_BYTE) { // no need for primitive write barriers // 8bit store asm.emitPOP_Reg(T0); // T0 is the value to be stored asm.emitPOP_Reg(S0); // S0 is the object reference // [S0+fieldOffset] <- T0 asm.emitMOV_RegDisp_Reg_Byte(S0, fieldOffset, T0); } else if (field.getSize() == BYTES_IN_SHORT) { // 16bit store asm.emitPOP_Reg(T0); // T0 is the value to be stored asm.emitPOP_Reg(S0); // S0 is the object reference // [S0+fieldOffset] <- T0 asm.emitMOV_RegDisp_Reg_Word(S0, fieldOffset, T0); } else if (field.getSize() == BYTES_IN_INT) { // 32bit store asm.emitPOP_Reg(T0); // T0 is the value to be stored asm.emitPOP_Reg(S0); // S0 is the object reference // [S0+fieldOffset] <- T0 asm.emitMOV_RegDisp_Reg(S0, fieldOffset, T0); } else { // 64bit store if (VM.VerifyAssertions) { VM._assert(field.getSize() == BYTES_IN_LONG); } if (VM.BuildFor32Addr) { // NB this is a 64bit copy from the stack to memory so implement // as a slightly optimized Intel memory copy using the FPU asm.emitMOV_Reg_RegDisp(S0, SP, TWO_SLOTS); // S0 is the object reference if (SSE2_BASE) { asm.emitMOVQ_Reg_RegInd(XMM0, SP); // XMM0 is the value to be stored asm.emitMOVQ_RegDisp_Reg(S0, fieldOffset, XMM0); // [S0+fieldOffset] <- XMM0 } else { asm.emitFLD_Reg_RegInd_Quad(FP0, SP); // FP0 is the value to be stored asm.emitFSTP_RegDisp_Reg_Quad(S0, fieldOffset, FP0); } adjustStack(WORDSIZE * 3, true); // complete popping the values and reference } else { asm.emitPOP_Reg(T1); // T1 is the value to be stored if (!field.getType().isWordLikeType()) { adjustStack(WORDSIZE, true); // throw away slot } asm.emitPOP_Reg(S0); // S0 is the object reference asm.emitMOV_RegDisp_Reg_Quad(S0, fieldOffset, T1); // [S0+fieldOffset] <- T1 } } if (field.isVolatile()) { asm.emitMFENCE(); } } /* * method invocation */ @Override protected void emit_unresolved_invokevirtual(MethodReference methodRef) { emitDynamicLinkingSequence(asm, T0, methodRef, true); // T0 has offset of method int methodRefparameterWords = methodRef.getParameterWords() + 1; // +1 for "this" parameter Offset objectOffset = Offset.fromIntZeroExtend(methodRefparameterWords << LG_WORDSIZE).minus(WORDSIZE); // object offset into stack stackMoveHelper(T1, objectOffset); // T1 has "this" parameter asm.baselineEmitLoadTIB(S0, T1); // S0 has TIB if (VM.BuildFor32Addr) { asm.emitMOV_Reg_RegIdx(S0, S0, T0, BYTE, NO_SLOT); // S0 has address of virtual method } else { asm.emitMOV_Reg_RegIdx_Quad(S0, S0, T0, BYTE, NO_SLOT); // S0 has address of virtual method } genParameterRegisterLoad(methodRef, true); asm.emitCALL_Reg(S0); // call virtual method genResultRegisterUnload(methodRef); // push return value, if any } @Override protected void emit_resolved_invokevirtual(MethodReference methodRef) { int methodRefparameterWords = methodRef.getParameterWords() + 1; // +1 for "this" parameter Offset methodRefOffset = methodRef.peekResolvedMethod().getOffset(); Offset objectOffset = Offset.fromIntZeroExtend(methodRefparameterWords << LG_WORDSIZE).minus(WORDSIZE); // object offset into stack stackMoveHelper(T1, objectOffset); // T1 has "this" parameter asm.baselineEmitLoadTIB(S0, T1); // S0 has TIB genParameterRegisterLoad(methodRef, true); asm.emitCALL_RegDisp(S0, methodRefOffset); // call virtual method genResultRegisterUnload(methodRef); // push return value, if any } @Override protected void emit_resolved_invokespecial(MethodReference methodRef, RVMMethod target) { if (target.isObjectInitializer()) { genParameterRegisterLoad(methodRef, true); asm.generateJTOCcall(target.getOffset()); genResultRegisterUnload(target.getMemberRef().asMethodReference()); } else { if (VM.VerifyAssertions) VM._assert(!target.isStatic()); // invoke via class's tib slot Offset methodRefOffset = target.getOffset(); asm.generateJTOCloadWord(S0, target.getDeclaringClass().getTibOffset()); genParameterRegisterLoad(methodRef, true); asm.emitCALL_RegDisp(S0, methodRefOffset); genResultRegisterUnload(methodRef); } } @Override protected void emit_unresolved_invokespecial(MethodReference methodRef) { emitDynamicLinkingSequence(asm, S0, methodRef, true); genParameterRegisterLoad(methodRef, true); asm.generateJTOCcall(S0); genResultRegisterUnload(methodRef); } @Override protected void emit_unresolved_invokestatic(MethodReference methodRef) { emitDynamicLinkingSequence(asm, S0, methodRef, true); genParameterRegisterLoad(methodRef, false); asm.generateJTOCcall(S0); genResultRegisterUnload(methodRef); } @Override protected void emit_resolved_invokestatic(MethodReference methodRef) { Offset methodOffset = methodRef.peekResolvedMethod().getOffset(); genParameterRegisterLoad(methodRef, false); asm.generateJTOCcall(methodOffset); genResultRegisterUnload(methodRef); } @Override protected void emit_invokeinterface(MethodReference methodRef) { final int count = methodRef.getParameterWords() + 1; // +1 for "this" parameter RVMMethod resolvedMethod = null; resolvedMethod = methodRef.peekInterfaceMethod(); // (1) Emit dynamic type checking sequence if required to do so inline. if (VM.BuildForIMTInterfaceInvocation) { if (methodRef.isMiranda()) { // TODO: It's not entirely clear that we can just assume that // the class actually implements the interface. // However, we don't know what interface we need to be checking // so there doesn't appear to be much else we can do here. } else { if (resolvedMethod == null) { // Can't successfully resolve it at compile time. // Call uncommon case typechecking routine to do the right thing when this code actually executes. // T1 = "this" object stackMoveHelper(T1, Offset.fromIntZeroExtend((count - 1) << LG_WORDSIZE)); asm.emitPUSH_Imm(methodRef.getId()); // push dict id of target asm.emitPUSH_Reg(T1); // push "this" genParameterRegisterLoad(asm, 2); // pass 2 parameter word // check that "this" class implements the interface asm.generateJTOCcall(Entrypoints.unresolvedInvokeinterfaceImplementsTestMethod.getOffset()); } else { RVMClass interfaceClass = resolvedMethod.getDeclaringClass(); int interfaceIndex = interfaceClass.getDoesImplementIndex(); int interfaceMask = interfaceClass.getDoesImplementBitMask(); // T1 = "this" object stackMoveHelper(T1, Offset.fromIntZeroExtend((count - 1) << LG_WORDSIZE)); asm.baselineEmitLoadTIB(S0, T1); // S0 = tib of "this" object if (VM.BuildFor32Addr) { asm.emitMOV_Reg_RegDisp(S0, S0, Offset.fromIntZeroExtend(TIB_DOES_IMPLEMENT_INDEX << LG_WORDSIZE)); // implements bit vector } else { asm.emitMOV_Reg_RegDisp_Quad(S0, S0, Offset.fromIntZeroExtend(TIB_DOES_IMPLEMENT_INDEX << LG_WORDSIZE)); // implements bit vector } if (DynamicTypeCheck.MIN_DOES_IMPLEMENT_SIZE <= interfaceIndex) { // must do arraybounds check of implements bit vector if (ARRAY_LENGTH_BYTES == 4) { asm.emitCMP_RegDisp_Imm(S0, ObjectModel.getArrayLengthOffset(), interfaceIndex); } else { asm.emitCMP_RegDisp_Imm_Quad(S0, ObjectModel.getArrayLengthOffset(), interfaceIndex); } asm.emitBranchLikelyNextInstruction(); ForwardReference fr = asm.forwardJcc(LGT); asm.emitINT_Imm(RuntimeEntrypoints.TRAP_MUST_IMPLEMENT + RVM_TRAP_BASE); fr.resolve(asm); } // Test the appropriate bit and if set, branch around another trap imm if (interfaceIndex == 0) { asm.emitTEST_RegInd_Imm(S0, interfaceMask); } else { asm.emitTEST_RegDisp_Imm(S0, Offset.fromIntZeroExtend(interfaceIndex << LOG_BYTES_IN_INT), interfaceMask); } asm.emitBranchLikelyNextInstruction(); ForwardReference fr = asm.forwardJcc(NE); asm.emitINT_Imm(RuntimeEntrypoints.TRAP_MUST_IMPLEMENT + RVM_TRAP_BASE); fr.resolve(asm); } } } // (2) Emit interface invocation sequence. if (VM.BuildForIMTInterfaceInvocation) { InterfaceMethodSignature sig = InterfaceMethodSignature.findOrCreate(methodRef); // squirrel away signature ID Offset offset = ArchEntrypoints.hiddenSignatureIdField.getOffset(); asm.emitMOV_RegDisp_Imm(THREAD_REGISTER, offset, sig.getId()); // T1 = "this" object stackMoveHelper(T1, Offset.fromIntZeroExtend((count - 1) << LG_WORDSIZE)); asm.baselineEmitLoadTIB(S0, T1); // Load the IMT Base into S0 if (VM.BuildFor32Addr) { asm.emitMOV_Reg_RegDisp(S0, S0, Offset.fromIntZeroExtend(TIB_INTERFACE_DISPATCH_TABLE_INDEX << LG_WORDSIZE)); } else { asm.emitMOV_Reg_RegDisp_Quad(S0, S0, Offset.fromIntZeroExtend(TIB_INTERFACE_DISPATCH_TABLE_INDEX << LG_WORDSIZE)); } genParameterRegisterLoad(methodRef, true); asm.emitCALL_RegDisp(S0, sig.getIMTOffset()); // the interface call } else { int itableIndex = -1; if (VM.BuildForITableInterfaceInvocation && resolvedMethod != null) { // get the index of the method in the Itable itableIndex = InterfaceInvocation.getITableIndex(resolvedMethod.getDeclaringClass(), methodRef.getName(), methodRef.getDescriptor()); } if (itableIndex == -1) { // itable index is not known at compile-time. // call "invokeInterface" to resolve object + method id into // method address int methodRefId = methodRef.getId(); // "this" parameter is obj if (count == 1) { asm.emitPUSH_RegInd(SP); } else { asm.emitPUSH_RegDisp(SP, Offset.fromIntZeroExtend((count - 1) << LG_WORDSIZE)); } asm.emitPUSH_Imm(methodRefId); // id of method to call genParameterRegisterLoad(asm, 2); // pass 2 parameter words // invokeinterface(obj, id) returns address to call asm.generateJTOCcall(Entrypoints.invokeInterfaceMethod.getOffset()); if (VM.BuildFor32Addr) { asm.emitMOV_Reg_Reg(S0, T0); // S0 has address of method } else { asm.emitMOV_Reg_Reg_Quad(S0, T0); // S0 has address of method } genParameterRegisterLoad(methodRef, true); asm.emitCALL_Reg(S0); // the interface method (its parameters are on stack) } else { // itable index is known at compile-time. // call "findITable" to resolve object + interface id into // itable address // T0 = "this" object stackMoveHelper(T0, Offset.fromIntZeroExtend((count - 1) << LG_WORDSIZE)); asm.baselineEmitLoadTIB(S0, T0); asm.emitPUSH_Reg(S0); asm.emitPUSH_Imm(resolvedMethod.getDeclaringClass().getInterfaceId()); // interface id genParameterRegisterLoad(asm, 2); // pass 2 parameter words asm.generateJTOCcall(Entrypoints.findItableMethod.getOffset()); // findItableOffset(tib, id) returns iTable if (VM.BuildFor32Addr) { asm.emitMOV_Reg_Reg(S0, T0); // S0 has iTable } else { asm.emitMOV_Reg_Reg_Quad(S0, T0); // S0 has iTable } genParameterRegisterLoad(methodRef, true); // the interface call asm.emitCALL_RegDisp(S0, Offset.fromIntZeroExtend(itableIndex << LG_WORDSIZE)); } } genResultRegisterUnload(methodRef); } /* * other object model functions */ @Override protected void emit_resolved_new(RVMClass typeRef) { int instanceSize = typeRef.getInstanceSize(); Offset tibOffset = typeRef.getTibOffset(); int whichAllocator = MemoryManager.pickAllocator(typeRef, method); int align = ObjectModel.getAlignment(typeRef); int offset = ObjectModel.getOffsetForAlignment(typeRef, false); int site = MemoryManager.getAllocationSite(true); asm.emitPUSH_Imm(instanceSize); asm.generateJTOCpush(tibOffset); // put tib on stack asm.emitPUSH_Imm(typeRef.hasFinalizer() ? 1 : 0); // does the class have a finalizer? asm.emitPUSH_Imm(whichAllocator); asm.emitPUSH_Imm(align); asm.emitPUSH_Imm(offset); asm.emitPUSH_Imm(site); genParameterRegisterLoad(asm, 7); // pass 7 parameter words asm.generateJTOCcall(Entrypoints.resolvedNewScalarMethod.getOffset()); asm.emitPUSH_Reg(T0); } @Override protected void emit_unresolved_new(TypeReference typeRef) { int site = MemoryManager.getAllocationSite(true); asm.emitPUSH_Imm(typeRef.getId()); asm.emitPUSH_Imm(site); // site genParameterRegisterLoad(asm, 2); // pass 2 parameter words asm.generateJTOCcall(Entrypoints.unresolvedNewScalarMethod.getOffset()); asm.emitPUSH_Reg(T0); } @Override protected void emit_resolved_newarray(RVMArray array) { int width = array.getLogElementSize(); Offset tibOffset = array.getTibOffset(); int headerSize = ObjectModel.computeHeaderSize(array); int whichAllocator = MemoryManager.pickAllocator(array, method); int site = MemoryManager.getAllocationSite(true); int align = ObjectModel.getAlignment(array); int offset = ObjectModel.getOffsetForAlignment(array, false); // count is already on stack- nothing required asm.emitPUSH_Imm(width); // logElementSize asm.emitPUSH_Imm(headerSize); // headerSize asm.generateJTOCpush(tibOffset); // tib asm.emitPUSH_Imm(whichAllocator); // allocator asm.emitPUSH_Imm(align); asm.emitPUSH_Imm(offset); asm.emitPUSH_Imm(site); genParameterRegisterLoad(asm, 8); // pass 8 parameter words asm.generateJTOCcall(Entrypoints.resolvedNewArrayMethod.getOffset()); asm.emitPUSH_Reg(T0); } @Override protected void emit_unresolved_newarray(TypeReference tRef) { int site = MemoryManager.getAllocationSite(true); // count is already on stack- nothing required asm.emitPUSH_Imm(tRef.getId()); asm.emitPUSH_Imm(site); // site genParameterRegisterLoad(asm, 3); // pass 3 parameter words asm.generateJTOCcall(Entrypoints.unresolvedNewArrayMethod.getOffset()); asm.emitPUSH_Reg(T0); } @Override protected void emit_multianewarray(TypeReference typeRef, int dimensions) { // TODO: implement direct call to RuntimeEntrypoints.buildTwoDimensionalArray // Calculate the offset from FP on entry to newarray: // 1 word for each parameter, plus 1 for return address on // stack and 1 for code technique in Linker final int PARAMETERS = 4; final int OFFSET_WORDS = PARAMETERS + 2; // setup parameters for newarrayarray routine asm.emitPUSH_Imm(method.getId()); // caller asm.emitPUSH_Imm(dimensions); // dimension of arrays asm.emitPUSH_Imm(typeRef.getId()); // type of array elements asm.emitPUSH_Imm((dimensions + OFFSET_WORDS) << LG_WORDSIZE); // offset to dimensions from FP on entry to newarray genParameterRegisterLoad(asm, PARAMETERS); asm.generateJTOCcall(ArchEntrypoints.newArrayArrayMethod.getOffset()); adjustStack(dimensions * WORDSIZE, true); // clear stack of dimensions asm.emitPUSH_Reg(T0); // push array ref on stack } @Override protected void emit_arraylength() { asm.emitPOP_Reg(T0); // T0 is array reference if (ARRAY_LENGTH_BYTES == 4) { if (VM.BuildFor32Addr) { asm.emitPUSH_RegDisp(T0, ObjectModel.getArrayLengthOffset()); } else { asm.emitMOV_Reg_RegDisp(T0, T0, ObjectModel.getArrayLengthOffset()); asm.emitPUSH_Reg(T0); } } else { asm.emitPUSH_RegDisp(T0, ObjectModel.getArrayLengthOffset()); } } @Override protected void emit_athrow() { genParameterRegisterLoad(asm, 1); // pass 1 parameter word asm.generateJTOCcall(Entrypoints.athrowMethod.getOffset()); } @Override protected void emit_checkcast(TypeReference typeRef) { asm.emitPUSH_RegInd(SP); // duplicate the object ref on the stack asm.emitPUSH_Imm(typeRef.getId()); // TypeReference id. genParameterRegisterLoad(asm, 2); // pass 2 parameter words asm.generateJTOCcall(Entrypoints.checkcastMethod.getOffset()); // checkcast(obj, type reference id); } @Override protected void emit_checkcast_resolvedInterface(RVMClass type) { int interfaceIndex = type.getDoesImplementIndex(); int interfaceMask = type.getDoesImplementBitMask(); if (VM.BuildFor32Addr) { asm.emitMOV_Reg_RegInd(ECX, SP); // load object from stack into ECX } else { asm.emitMOV_Reg_RegInd_Quad(ECX, SP); // load object from stack into ECX } ForwardReference isNull = asm.forwardJECXZ(); // forward branch if ECX == 0 asm.baselineEmitLoadTIB(S0, ECX); // S0 = TIB of object // S0 = implements bit vector if (VM.BuildFor32Addr) { asm.emitMOV_Reg_RegDisp(S0, S0, Offset.fromIntZeroExtend(TIB_DOES_IMPLEMENT_INDEX << LG_WORDSIZE)); } else { asm.emitMOV_Reg_RegDisp_Quad(S0, S0, Offset.fromIntZeroExtend(TIB_DOES_IMPLEMENT_INDEX << LG_WORDSIZE)); } if (DynamicTypeCheck.MIN_DOES_IMPLEMENT_SIZE <= interfaceIndex) { // must do arraybounds check of implements bit vector if (ARRAY_LENGTH_BYTES == 4) { asm.emitCMP_RegDisp_Imm(S0, ObjectModel.getArrayLengthOffset(), interfaceIndex); } else { asm.emitCMP_RegDisp_Imm_Quad(S0, ObjectModel.getArrayLengthOffset(), interfaceIndex); } asm.emitBranchLikelyNextInstruction(); ForwardReference fr = asm.forwardJcc(LGT); asm.emitINT_Imm(RuntimeEntrypoints.TRAP_CHECKCAST + RVM_TRAP_BASE); fr.resolve(asm); } // Test the appropriate bit and if set, branch around another trap imm asm.emitTEST_RegDisp_Imm(S0, Offset.fromIntZeroExtend(interfaceIndex << LOG_BYTES_IN_INT), interfaceMask); asm.emitBranchLikelyNextInstruction(); ForwardReference fr = asm.forwardJcc(NE); asm.emitINT_Imm(RuntimeEntrypoints.TRAP_CHECKCAST + RVM_TRAP_BASE); fr.resolve(asm); isNull.resolve(asm); } @Override protected void emit_checkcast_resolvedClass(RVMClass type) { int LHSDepth = type.getTypeDepth(); int LHSId = type.getId(); if (VM.BuildFor32Addr) { asm.emitMOV_Reg_RegInd(ECX, SP); // load object from stack } else { asm.emitMOV_Reg_RegInd_Quad(ECX, SP); // load object from stack } ForwardReference isNull = asm.forwardJECXZ(); // jump forward if ECX == 0 asm.baselineEmitLoadTIB(S0, ECX); // S0 = TIB of object // S0 = superclass IDs if (VM.BuildFor32Addr) { asm.emitMOV_Reg_RegDisp(S0, S0, Offset.fromIntZeroExtend(TIB_SUPERCLASS_IDS_INDEX << LG_WORDSIZE)); } else { asm.emitMOV_Reg_RegDisp_Quad(S0, S0, Offset.fromIntZeroExtend(TIB_SUPERCLASS_IDS_INDEX << LG_WORDSIZE)); } if (DynamicTypeCheck.MIN_SUPERCLASS_IDS_SIZE <= LHSDepth) { // must do arraybounds check of superclass display if (ARRAY_LENGTH_BYTES == 4) { asm.emitCMP_RegDisp_Imm(S0, ObjectModel.getArrayLengthOffset(), LHSDepth); } else { asm.emitCMP_RegDisp_Imm_Quad(S0, ObjectModel.getArrayLengthOffset(), LHSDepth); } asm.emitBranchLikelyNextInstruction(); ForwardReference fr = asm.forwardJcc(LGT); asm.emitINT_Imm(RuntimeEntrypoints.TRAP_CHECKCAST + RVM_TRAP_BASE); fr.resolve(asm); } // Load id from display at required depth and compare against target id. asm.emitMOVZX_Reg_RegDisp_Word(S0, S0, Offset.fromIntZeroExtend(LHSDepth << LOG_BYTES_IN_SHORT)); asm.emitCMP_Reg_Imm(S0, LHSId); asm.emitBranchLikelyNextInstruction(); ForwardReference fr = asm.forwardJcc(EQ); asm.emitINT_Imm(RuntimeEntrypoints.TRAP_CHECKCAST + RVM_TRAP_BASE); fr.resolve(asm); isNull.resolve(asm); } @Override protected void emit_checkcast_final(RVMType type) { if (VM.BuildFor32Addr) { asm.emitMOV_Reg_RegInd(ECX, SP); // load object from stack } else { asm.emitMOV_Reg_RegInd_Quad(ECX, SP); // load object from stack } ForwardReference isNull = asm.forwardJECXZ(); // jump forward if ECX == 0 asm.baselineEmitLoadTIB(S0, ECX); // TIB of object asm.generateJTOCcmpWord(S0, type.getTibOffset()); asm.emitBranchLikelyNextInstruction(); ForwardReference fr = asm.forwardJcc(EQ); asm.emitINT_Imm(RuntimeEntrypoints.TRAP_CHECKCAST + RVM_TRAP_BASE); fr.resolve(asm); isNull.resolve(asm); } @Override protected void emit_instanceof(TypeReference typeRef) { asm.emitPUSH_Imm(typeRef.getId()); genParameterRegisterLoad(asm, 2); // pass 2 parameter words asm.generateJTOCcall(Entrypoints.instanceOfMethod.getOffset()); asm.emitPUSH_Reg(T0); } @Override protected void emit_instanceof_resolvedInterface(RVMClass type) { int interfaceIndex = type.getDoesImplementIndex(); int interfaceMask = type.getDoesImplementBitMask(); asm.emitPOP_Reg(ECX); // load object from stack ForwardReference isNull = asm.forwardJECXZ(); // test for null asm.baselineEmitLoadTIB(S0, ECX); // S0 = TIB of object // S0 = implements bit vector if (VM.BuildFor32Addr) { asm.emitMOV_Reg_RegDisp(S0, S0, Offset.fromIntZeroExtend(TIB_DOES_IMPLEMENT_INDEX << LG_WORDSIZE)); } else { asm.emitMOV_Reg_RegDisp_Quad(S0, S0, Offset.fromIntZeroExtend(TIB_DOES_IMPLEMENT_INDEX << LG_WORDSIZE)); } ForwardReference outOfBounds = null; if (DynamicTypeCheck.MIN_DOES_IMPLEMENT_SIZE <= interfaceIndex) { // must do arraybounds check of implements bit vector if (ARRAY_LENGTH_BYTES == 4) { asm.emitCMP_RegDisp_Imm(S0, ObjectModel.getArrayLengthOffset(), interfaceIndex); } else { asm.emitCMP_RegDisp_Imm_Quad(S0, ObjectModel.getArrayLengthOffset(), interfaceIndex); } outOfBounds = asm.forwardJcc(LLE); } // Test the implements bit and push true if it is set asm.emitTEST_RegDisp_Imm(S0, Offset.fromIntZeroExtend(interfaceIndex << LOG_BYTES_IN_INT), interfaceMask); ForwardReference notMatched = asm.forwardJcc(EQ); asm.emitPUSH_Imm(1); ForwardReference done = asm.forwardJMP(); // push false isNull.resolve(asm); if (outOfBounds != null) outOfBounds.resolve(asm); notMatched.resolve(asm); asm.emitPUSH_Imm(0); done.resolve(asm); } @Override protected void emit_instanceof_resolvedClass(RVMClass type) { int LHSDepth = type.getTypeDepth(); int LHSId = type.getId(); asm.emitPOP_Reg(ECX); // load object from stack ForwardReference isNull = asm.forwardJECXZ(); // test for null // get superclass display from object's TIB asm.baselineEmitLoadTIB(S0, ECX); if (VM.BuildFor32Addr) { asm.emitMOV_Reg_RegDisp(S0, S0, Offset.fromIntZeroExtend(TIB_SUPERCLASS_IDS_INDEX << LG_WORDSIZE)); } else { asm.emitMOV_Reg_RegDisp_Quad(S0, S0, Offset.fromIntZeroExtend(TIB_SUPERCLASS_IDS_INDEX << LG_WORDSIZE)); } ForwardReference outOfBounds = null; if (DynamicTypeCheck.MIN_SUPERCLASS_IDS_SIZE <= LHSDepth) { // must do arraybounds check of superclass display if (ARRAY_LENGTH_BYTES == 4) { asm.emitCMP_RegDisp_Imm(S0, ObjectModel.getArrayLengthOffset(), LHSDepth); } else { asm.emitCMP_RegDisp_Imm_Quad(S0, ObjectModel.getArrayLengthOffset(), LHSDepth); } outOfBounds = asm.forwardJcc(LLE); } // Load id from display at required depth and compare against target id; push true if matched asm.emitMOVZX_Reg_RegDisp_Word(S0, S0, Offset.fromIntZeroExtend(LHSDepth << LOG_BYTES_IN_SHORT)); asm.emitCMP_Reg_Imm(S0, LHSId); ForwardReference notMatched = asm.forwardJcc(NE); asm.emitPUSH_Imm(1); ForwardReference done = asm.forwardJMP(); // push false isNull.resolve(asm); if (outOfBounds != null) outOfBounds.resolve(asm); notMatched.resolve(asm); asm.emitPUSH_Imm(0); done.resolve(asm); } @Override protected void emit_instanceof_final(RVMType type) { asm.emitPOP_Reg(ECX); // load object from stack ForwardReference isNull = asm.forwardJECXZ(); // test for null // compare TIB of object to desired TIB and push true if equal asm.baselineEmitLoadTIB(S0, ECX); asm.generateJTOCcmpWord(S0, type.getTibOffset()); ForwardReference notMatched = asm.forwardJcc(NE); asm.emitPUSH_Imm(1); ForwardReference done = asm.forwardJMP(); // push false isNull.resolve(asm); notMatched.resolve(asm); asm.emitPUSH_Imm(0); done.resolve(asm); } @Override protected void emit_monitorenter() { if (VM.BuildFor32Addr) { asm.emitMOV_Reg_RegInd(T0, SP); // T0 is object reference } else { asm.emitMOV_Reg_RegInd_Quad(T0, SP); // T0 is object reference } genNullCheck(asm, T0); genParameterRegisterLoad(asm, 1); // pass 1 parameter word asm.generateJTOCcall(Entrypoints.lockMethod.getOffset()); } @Override protected void emit_monitorexit() { genParameterRegisterLoad(asm, 1); // pass 1 parameter word asm.generateJTOCcall(Entrypoints.unlockMethod.getOffset()); } //----------------// // implementation // //----------------// private void genPrologue() { if (shouldPrint) asm.comment("prologue for " + method); if (klass.hasBridgeFromNativeAnnotation()) { // replace the normal prologue with a special prolog JNICompiler.generateGlueCodeForJNIMethod(asm, method, compiledMethod.getId()); // set some constants for the code generation of the rest of the method // firstLocalOffset is shifted down because more registers are saved firstLocalOffset = STACKFRAME_BODY_OFFSET.minus(JNICompiler.SAVED_GPRS_FOR_JNI << LG_WORDSIZE); } else { genStackOverflowCheck(); /* paramaters are on the stack and/or in registers; There is space * on the stack for all the paramaters; Parameter slots in the * stack are such that the first paramater has the higher address, * i.e., it pushed below all the other paramaters; The return * address is the topmost entry on the stack. The frame pointer * still addresses the previous frame. * The first word of the header, currently addressed by the stack * pointer, contains the return address. */ /* establish a new frame: * push the caller's frame pointer in the stack, and * reset the frame pointer to the current stack top, * ie, the frame pointer addresses directly the word * that contains the previous frame pointer. * The second word of the header contains the frame * point of the caller. * The third word of the header contains the compiled method id of the called method. */ // store caller's frame pointer asm.emitPUSH_RegDisp(TR, ArchEntrypoints.framePointerField.getOffset()); // establish new frame if (VM.BuildFor32Addr) { asm.emitMOV_RegDisp_Reg(THREAD_REGISTER, ArchEntrypoints.framePointerField.getOffset(), SP); } else { asm.emitMOV_RegDisp_Reg_Quad(THREAD_REGISTER, ArchEntrypoints.framePointerField.getOffset(), SP); } /* * NOTE: until the end of the prologue SP holds the framepointer. */ if (VM.VerifyAssertions) VM._assert(STACKFRAME_METHOD_ID_OFFSET.toInt() == -WORDSIZE); asm.emitPUSH_Imm(compiledMethod.getId()); /* * save registers */ if (VM.VerifyAssertions) VM._assert(EDI_SAVE_OFFSET.toInt() == -2 * WORDSIZE); asm.emitPUSH_Reg(EDI); // save nonvolatile EDI register if (VM.VerifyAssertions) VM._assert(EBX_SAVE_OFFSET.toInt() == -3 * WORDSIZE); asm.emitPUSH_Reg(EBX); // save nonvolatile EBX register int savedRegistersSize; if (method.hasBaselineSaveLSRegistersAnnotation()) { if (VM.VerifyAssertions) VM._assert(EBP_SAVE_OFFSET.toInt() == -4 * WORDSIZE); asm.emitPUSH_Reg(EBP); savedRegistersSize = SAVED_GPRS_FOR_SAVE_LS_REGISTERS << LG_WORDSIZE; } else { savedRegistersSize = SAVED_GPRS << LG_WORDSIZE; // default } /* handle "dynamic brige" methods: * save all registers except FP, SP, TR, S0 (scratch), and * EDI and EBX saved above. */ // TODO: (SJF): When I try to reclaim ESI, I may have to save it here? if (klass.hasDynamicBridgeAnnotation()) { savedRegistersSize += 2 << LG_WORDSIZE; if (VM.VerifyAssertions) VM._assert(T0_SAVE_OFFSET.toInt() == -4 * WORDSIZE); asm.emitPUSH_Reg(T0); if (VM.VerifyAssertions) VM._assert(T1_SAVE_OFFSET.toInt() == -5 * WORDSIZE); asm.emitPUSH_Reg(T1); if (SSE2_FULL) { // TODO: Store SSE2 Control word? adjustStack(-BASELINE_XMM_STATE_SIZE, true); // adjust stack to bottom of saved area if (VM.VerifyAssertions) VM._assert(XMM_SAVE_OFFSET.toInt() == (-5 * WORDSIZE) - BASELINE_XMM_STATE_SIZE); asm.emitMOVQ_RegDisp_Reg(SP, Offset.fromIntSignExtend(24), XMM3); asm.emitMOVQ_RegDisp_Reg(SP, Offset.fromIntSignExtend(16), XMM2); asm.emitMOVQ_RegDisp_Reg(SP, Offset.fromIntSignExtend(8), XMM1); asm.emitMOVQ_RegInd_Reg(SP, XMM0); savedRegistersSize += BASELINE_XMM_STATE_SIZE; } else { if (VM.VerifyAssertions) VM._assert(FPU_SAVE_OFFSET.toInt() == (-5 * WORDSIZE) - X87_FPU_STATE_SIZE); adjustStack(-X87_FPU_STATE_SIZE, true); // adjust stack to bottom of saved area asm.emitFNSAVE_RegInd(SP); savedRegistersSize += X87_FPU_STATE_SIZE; } } // copy registers to callee's stackframe firstLocalOffset = STACKFRAME_BODY_OFFSET.minus(savedRegistersSize); Offset firstParameterOffset = Offset.fromIntSignExtend(savedRegistersSize + STACKFRAME_HEADER_SIZE + (parameterWords << LG_WORDSIZE) - WORDSIZE); genParameterCopy(firstParameterOffset); int emptyStackOffset = (method.getLocalWords() << LG_WORDSIZE) - (parameterWords << LG_WORDSIZE); if (emptyStackOffset != 0) { adjustStack(-emptyStackOffset, true); // set aside room for non parameter locals } /* defer generating code which may cause GC until * locals were initialized. see emit_deferred_prologue */ if (method.isForOsrSpecialization()) { return; } if (!VM.runningTool && ((BaselineCompiledMethod) compiledMethod).hasCounterArray()) { // use (nonvolatile) EBX to hold base of this method's counter array if (NEEDS_OBJECT_ALOAD_BARRIER) { asm.generateJTOCpush(Entrypoints.edgeCountersField.getOffset()); asm.emitPUSH_Imm(getEdgeCounterIndex()); Barriers.compileArrayLoadBarrier(asm, false); if (VM.BuildFor32Addr) { asm.emitMOV_Reg_Reg(EBX, T0); } else { asm.emitMOV_Reg_Reg_Quad(EBX, T0); } } else { if (VM.BuildFor32Addr) { asm.emitMOV_Reg_Abs(EBX, Magic.getTocPointer().plus(Entrypoints.edgeCountersField.getOffset())); asm.emitMOV_Reg_RegDisp(EBX, EBX, getEdgeCounterOffset()); } else { asm.generateJTOCpush(Entrypoints.edgeCountersField.getOffset()); asm.emitPOP_Reg(EBX); asm.emitMOV_Reg_RegDisp_Quad(EBX, EBX, getEdgeCounterOffset()); } } } if (method.isSynchronized()) genMonitorEnter(); genThreadSwitchTest(RVMThread.PROLOGUE); } } /** * Emit deferred prologue */ @Override protected void emit_deferred_prologue() { if (VM.VerifyAssertions) VM._assert(method.isForOsrSpecialization()); if (isInterruptible) { Offset offset = Entrypoints.stackLimitField.getOffset(); if (VM.BuildFor32Addr) { // S0<-limit asm.emitMOV_Reg_RegDisp(S0, THREAD_REGISTER, offset); asm.emitSUB_Reg_Reg(S0, SP); asm.emitADD_Reg_Imm(S0, method.getOperandWords() << LG_WORDSIZE); } else { // S0<-limit asm.emitMOV_Reg_RegDisp_Quad(S0, THREAD_REGISTER, offset); asm.emitSUB_Reg_Reg_Quad(S0, SP); asm.emitADD_Reg_Imm_Quad(S0, method.getOperandWords() << LG_WORDSIZE); } asm.emitBranchLikelyNextInstruction(); ForwardReference fr = asm.forwardJcc(LT); // Jmp around trap asm.emitINT_Imm(RuntimeEntrypoints.TRAP_STACK_OVERFLOW + RVM_TRAP_BASE); // trap fr.resolve(asm); } else { // TODO!! make sure stackframe of uninterruptible method doesn't overflow } /* never do monitor enter for synced method since the specialized * code starts after original monitor enter. */ genThreadSwitchTest(RVMThread.PROLOGUE); } /** * Generate method epilogue, releasing values from stack and returning * @param returnSize the size in bytes of the returned value * @param bytesPopped number of paramter bytes already released */ private void genEpilogue(int returnSize, int bytesPopped) { if (klass.hasBridgeFromNativeAnnotation()) { // pop locals and parameters, get to saved GPR's adjustStack((method.getLocalWords() << LG_WORDSIZE) + (returnSize - bytesPopped), true); JNICompiler.generateEpilogForJNIMethod(asm, this.method); } else if (klass.hasDynamicBridgeAnnotation()) { // we never return from a DynamicBridge frame asm.emitINT_Imm(0xFF); } else { // normal method if (method.hasBaselineSaveLSRegistersAnnotation()) { // There is one more word out of the total that is for callee-saves, hense 4 * WORDSIZE here rather than 3 * WORDSIZE below. int spaceToRelease = fp2spOffset(NO_SLOT).toInt() - bytesPopped - (4 * WORDSIZE); adjustStack(spaceToRelease, true); if (VM.VerifyAssertions) VM._assert(EBP_SAVE_OFFSET.toInt() == -(4 * WORDSIZE)); asm.emitPOP_Reg(EBP); // restore nonvolatile EBP register } else { int spaceToRelease = fp2spOffset(NO_SLOT).toInt() - bytesPopped - (3 * WORDSIZE); adjustStack(spaceToRelease, true); } if (VM.VerifyAssertions) VM._assert(EBX_SAVE_OFFSET.toInt() == -(3 * WORDSIZE)); asm.emitPOP_Reg(EBX); // restore non-volatile EBX register if (VM.VerifyAssertions) VM._assert(EDI_SAVE_OFFSET.toInt() == -(2 * WORDSIZE)); asm.emitPOP_Reg(EDI); // restore non-volatile EDI register asm.emitPOP_Reg(ECX); // throw away CMID // SP == frame pointer asm.emitPOP_RegDisp(TR, ArchEntrypoints.framePointerField.getOffset()); // discard frame // return to caller, pop parameters from stack if (parameterWords == 0) { asm.emitRET(); } else { asm.emitRET_Imm(parameterWords << LG_WORDSIZE); } } } private void genStackOverflowCheck() { /* * Generate stacklimit check. * * NOTE: The stack overflow check MUST happen before the frame is created. * If the check were to happen after frame creation, the stack pointer * could already be well below the stack limit. This would be a problem * because the IA32 stack overflow handling code imposes a bound on the * difference between the stack pointer and the stack limit. * * NOTE: Frame sizes for the baseline compiler can get very large because * each non-parameter local slot and each slot for the operand stack * requires one machine word. * * The Java Virtual Machine Specification has an overview of the limits for * the local words and operand words in section 4.11, * "Limitations of the Java Virtual Machine". */ if (isInterruptible) { int frameSize = calculateRequiredSpaceForFrame(method); // S0<-limit if (VM.BuildFor32Addr) { asm.emitMOV_Reg_Reg(S0, ESP); asm.emitSUB_Reg_Imm(S0, frameSize); asm.emitCMP_Reg_RegDisp(S0, TR, Entrypoints.stackLimitField.getOffset()); } else { asm.emitMOV_Reg_Reg_Quad(S0, ESP); asm.emitSUB_Reg_Imm_Quad(S0, frameSize); asm.emitCMP_Reg_RegDisp_Quad(S0, TR, Entrypoints.stackLimitField.getOffset()); } asm.emitBranchLikelyNextInstruction(); ForwardReference fr = asm.forwardJcc(LGT); // Jmp around trap if OK asm.emitINT_Imm(RuntimeEntrypoints.TRAP_STACK_OVERFLOW + RVM_TRAP_BASE); // trap fr.resolve(asm); } else { // TODO!! make sure stackframe of uninterruptible method doesn't overflow guard page } } /** * Calculates the space that is required for creating a frame for the * given method, in bytes. This quantity is necessary to be able to do * a stack overflow check before creating the frame. * <p> * Note that this method doesn't return the complete frame size: * the parameters are in the caller's frame for the baseline compiler * and the caller's frame has already been created when the callee is * called. The additional space that's required is necessary * * @param method a method with bytecodes * @return space required to create the frame, in bytes */ public static int calculateRequiredSpaceForFrame(NormalMethod method) { int frameWords = 3; // method id, EDI, EDX if (method.hasBaselineSaveLSRegistersAnnotation()) { frameWords++; // EBP } if (method.getDeclaringClass().hasDynamicBridgeAnnotation()) { frameWords += 2; // T0, T1 if (SSE2_FULL) { frameWords += (BASELINE_XMM_STATE_SIZE / WORDSIZE); } else { frameWords += (X87_FPU_STATE_SIZE / WORDSIZE); } } frameWords += method.getOperandWords(); frameWords += method.getLocalWords(); // parameters are in the caller's frame so they don't // count towards the space for the method's frame frameWords -= method.getParameterWords(); if (!method.isStatic()) frameWords--; return frameWords * WORDSIZE; } /** * Generate instructions to acquire lock on entry to a method */ private void genMonitorEnter() { try { if (method.isStatic()) { Offset klassOffset = Offset.fromIntSignExtend(Statics.findOrCreateObjectLiteral(klass.getClassForType())); // push java.lang.Class object for klass asm.generateJTOCpush(klassOffset); } else { // push "this" object asm.emitPUSH_RegDisp(ESP, localOffset(0)); } // pass 1 parameter genParameterRegisterLoad(asm, 1); asm.generateJTOCcall(Entrypoints.lockMethod.getOffset()); // after this instruction, the method has the monitor lockOffset = asm.getMachineCodeIndex(); } catch (UnreachableBytecodeException e) { asm.emitINT_Imm(TRAP_UNREACHABLE_BYTECODE + RVM_TRAP_BASE); } } /** * Generate instructions to release lock on exit from a method */ private void genMonitorExit() { try { if (method.isStatic()) { Offset klassOffset = Offset.fromIntSignExtend(Statics.findOrCreateObjectLiteral(klass.getClassForType())); // push java.lang.Class object for klass asm.generateJTOCpush(klassOffset); } else { asm.emitPUSH_RegDisp(ESP, localOffset(0)); // push "this" object } genParameterRegisterLoad(asm, 1); // pass 1 parameter asm.generateJTOCcall(Entrypoints.unlockMethod.getOffset()); } catch (UnreachableBytecodeException e) { asm.emitINT_Imm(TRAP_UNREACHABLE_BYTECODE + RVM_TRAP_BASE); } } /** * Generate an explicit null check (compare to zero). * * @param asm the assembler to generate into * @param objRefReg the register containing the reference */ @Inline private static void genNullCheck(Assembler asm, GPR objRefReg) { // compare to zero asm.emitTEST_Reg_Reg(objRefReg, objRefReg); // Jmp around trap if index is OK asm.emitBranchLikelyNextInstruction(); ForwardReference fr = asm.forwardJcc(NE); // trap asm.emitINT_Imm(RuntimeEntrypoints.TRAP_NULL_POINTER + RVM_TRAP_BASE); fr.resolve(asm); } /** * Generate an array bounds check trapping if the array bound check fails, * otherwise falling through. * @param asm the assembler to generate into * @param indexReg the register containing the index * @param arrayRefReg the register containing the array reference */ @Inline(value = Inline.When.ArgumentsAreConstant, arguments = {1,2}) static void genBoundsCheck(Assembler asm, GPR indexReg, GPR arrayRefReg) { // compare index to array length if (ARRAY_LENGTH_BYTES == 4) { asm.emitCMP_RegDisp_Reg(arrayRefReg, ObjectModel.getArrayLengthOffset(), indexReg); } else { asm.emitCMP_RegDisp_Reg_Quad(arrayRefReg, ObjectModel.getArrayLengthOffset(), indexReg); } // Jmp around trap if index is OK asm.emitBranchLikelyNextInstruction(); ForwardReference fr = asm.forwardJcc(LGT); // "pass" index param to C trap handler asm.emitMOV_RegDisp_Reg(THREAD_REGISTER, ArchEntrypoints.arrayIndexTrapParamField.getOffset(), indexReg); // trap asm.emitINT_Imm(RuntimeEntrypoints.TRAP_ARRAY_BOUNDS + RVM_TRAP_BASE); fr.resolve(asm); } /** * Emits a conditional branch on the given condition and bytecode target. * The caller has just emitted the instruction sequence to set the condition codes. * * @param cond condition byte * @param bTarget target bytecode index */ private void genCondBranch(byte cond, int bTarget) { int mTarget = bytecodeMap[bTarget]; if (!VM.runningTool && ((BaselineCompiledMethod) compiledMethod).hasCounterArray()) { // Allocate two counters: taken and not taken int entry = edgeCounterIdx; edgeCounterIdx += 2; // Flip conditions so we can jump over the increment of the taken counter. ForwardReference notTaken = asm.forwardJcc(asm.flipCode(cond)); // Increment taken counter & jump to target incEdgeCounter(T1, null, entry + EdgeCounts.TAKEN); asm.emitJMP_ImmOrLabel(mTarget, bTarget); // Increment not taken counter notTaken.resolve(asm); incEdgeCounter(T1, null, entry + EdgeCounts.NOT_TAKEN); } else { asm.emitJCC_Cond_ImmOrLabel(cond, mTarget, bTarget); } } /** * Generate code to increment edge counter * @param scratch register to use as scratch * @param idx optional register holding index value or null * @param counterIdx index in to counters array */ @Inline(value = Inline.When.ArgumentsAreConstant, arguments = {1,2}) private void incEdgeCounter(GPR scratch, GPR idx, int counterIdx) { if (VM.VerifyAssertions) VM._assert(((BaselineCompiledMethod) compiledMethod).hasCounterArray()); if (idx == null) { asm.emitMOV_Reg_RegDisp(scratch, EBX, Offset.fromIntZeroExtend(counterIdx << LOG_BYTES_IN_INT)); } else { asm.emitMOV_Reg_RegIdx(scratch, EBX, idx, WORD, Offset.fromIntZeroExtend(counterIdx << LOG_BYTES_IN_INT)); } asm.emitADD_Reg_Imm(scratch, 1); // Add 1 to scratch, if the add overflows subtract 1 (the carry flag). // Add saturates at 0xFFFFFFFF asm.emitSBB_Reg_Imm(scratch, 0); if (idx == null) { asm.emitMOV_RegDisp_Reg(EBX, Offset.fromIntSignExtend(counterIdx << LOG_BYTES_IN_INT), scratch); } else { asm.emitMOV_RegIdx_Reg(EBX, idx, WORD, Offset.fromIntSignExtend(counterIdx << LOG_BYTES_IN_INT), scratch); } } /** * Copy parameters from operand stack into registers. * Assumption: parameters are laid out on the stack in order * with SP pointing to the last parameter. * Also, this method is called before the generation of a "helper" method call. * Assumption: no floating-point parameters. * @param asm assembler to use for generation * @param params number of parameter words (including "this" if any). */ static void genParameterRegisterLoad(Assembler asm, int params) { if (VM.VerifyAssertions) VM._assert(0 < params); if (0 < NUM_PARAMETER_GPRS) { stackMoveHelper(asm, T0, Offset.fromIntZeroExtend((params - 1) << LG_WORDSIZE)); } if (1 < params && 1 < NUM_PARAMETER_GPRS) { stackMoveHelper(asm, T1, Offset.fromIntZeroExtend((params - 2) << LG_WORDSIZE)); } } /** * Copy parameters from operand stack into registers. * Assumption: parameters are layed out on the stack in order * with SP pointing to the last parameter. * Also, this method is called before the generation of an explicit method call. * @param method is the method to be called. * @param hasThisParam is the method virtual? */ protected void genParameterRegisterLoad(MethodReference method, boolean hasThisParam) { int max = NUM_PARAMETER_GPRS + NUM_PARAMETER_FPRS; if (max == 0) return; // quit looking when all registers are full int gpr = 0; // number of general purpose registers filled int fpr = 0; // number of floating point registers filled GPR T = T0; // next GPR to get a parameter int params = method.getParameterWords() + (hasThisParam ? 1 : 0); Offset offset = Offset.fromIntSignExtend((params - 1) << LG_WORDSIZE); // stack offset of first parameter word if (hasThisParam) { if (gpr < NUM_PARAMETER_GPRS) { stackMoveHelper(T, offset); T = T1; // at most 2 parameters can be passed in general purpose registers gpr++; max--; } offset = offset.minus(WORDSIZE); } for (TypeReference type : method.getParameterTypes()) { if (max == 0) return; // quit looking when all registers are full TypeReference t = type; if (t.isLongType()) { if (gpr < NUM_PARAMETER_GPRS) { if (WORDSIZE == 4) { stackMoveHelper(T, offset); // lo register := hi mem (== hi order word) T = T1; // at most 2 parameters can be passed in general purpose registers gpr++; max--; if (gpr < NUM_PARAMETER_GPRS) { stackMoveHelper(T, offset.minus(WORDSIZE)); // hi register := lo mem (== lo order word) gpr++; max--; } } else { // initially offset will point at junk word, move down and over stackMoveHelper(T, offset.minus(WORDSIZE)); T = T1; // at most 2 parameters can be passed in general purpose registers gpr++; max--; } } offset = offset.minus(2 * WORDSIZE); } else if (t.isFloatType()) { if (fpr < NUM_PARAMETER_FPRS) { if (SSE2_FULL) { asm.emitMOVSS_Reg_RegDisp(XMM.lookup(fpr), SP, offset); } else { asm.emitFLD_Reg_RegDisp(FP0, SP, offset); } fpr++; max--; } offset = offset.minus(WORDSIZE); } else if (t.isDoubleType()) { if (fpr < NUM_PARAMETER_FPRS) { if (SSE2_FULL) { asm.emitMOVSD_Reg_RegDisp(XMM.lookup(fpr), SP, offset.minus(WORDSIZE)); } else { asm.emitFLD_Reg_RegDisp_Quad(FP0, SP, offset.minus(WORDSIZE)); } fpr++; max--; } offset = offset.minus(2 * WORDSIZE); } else if (t.isReferenceType() || t.isWordLikeType()) { if (gpr < NUM_PARAMETER_GPRS) { stackMoveHelper(T, offset); T = T1; // at most 2 parameters can be passed in general purpose registers gpr++; max--; } offset = offset.minus(WORDSIZE); } else { // t is object, int, short, char, byte, or boolean if (gpr < NUM_PARAMETER_GPRS) { if (offset.isZero()) { asm.emitMOV_Reg_RegInd(T, SP); } else { asm.emitMOV_Reg_RegDisp(T, SP, offset); } T = T1; // at most 2 parameters can be passed in general purpose registers gpr++; max--; } offset = offset.minus(WORDSIZE); } } if (VM.VerifyAssertions) VM._assert(offset.EQ(Offset.fromIntSignExtend(-WORDSIZE))); } /** * Stores parameters into local space of the callee's stackframe. * <p> * Assumption: although some parameters may be passed in registers, * space for all parameters is laid out in order on the caller's stackframe. * * @param srcOffset offset from frame pointer of first parameter in caller's stackframe. */ private void genParameterCopy(Offset srcOffset) { int gpr = 0; // number of general purpose registers unloaded int fpr = 0; // number of floating point registers unloaded GPR T = T0; // next GPR to get a parameter int dstOffset = 0; // offset from the bottom of the locals for the current parameter if (!method.isStatic()) { // handle "this" parameter if (gpr < NUM_PARAMETER_GPRS) { asm.emitPUSH_Reg(T); T = T1; // at most 2 parameters can be passed in general purpose registers gpr++; } else { // no parameters passed in registers asm.emitPUSH_RegDisp(SP, srcOffset); } dstOffset -= WORDSIZE; } int[] fprOffset = new int[NUM_PARAMETER_FPRS]; // to handle floating point parameters in registers boolean[] is32bit = new boolean[NUM_PARAMETER_FPRS]; // to handle floating point parameters in registers int spIsOffBy = 0; // in the case of doubles and floats SP may drift from the expected value as we don't use push/pop for (TypeReference t : method.getParameterTypes()) { if (t.isLongType()) { if (spIsOffBy != 0) { // fix up SP if it drifted adjustStack(-spIsOffBy, true); spIsOffBy = 0; } if (gpr < NUM_PARAMETER_GPRS) { if (VM.BuildFor32Addr) { asm.emitPUSH_Reg(T); // hi mem := lo register (== hi order word) T = T1; // at most 2 parameters can be passed in general purpose registers gpr++; if (gpr < NUM_PARAMETER_GPRS) { asm.emitPUSH_Reg(T); // lo mem := hi register (== lo order word) gpr++; } else { asm.emitPUSH_RegDisp(SP, srcOffset); // lo mem from caller's stackframe } } else { adjustStack(-WORDSIZE, true); // create empty slot asm.emitPUSH_Reg(T); // push long T = T1; // at most 2 parameters can be passed in general purpose registers gpr++; } } else { if (VM.BuildFor32Addr) { asm.emitPUSH_RegDisp(SP, srcOffset); // hi mem from caller's stackframe asm.emitPUSH_RegDisp(SP, srcOffset); // lo mem from caller's stackframe } else { adjustStack(-WORDSIZE, true); // create empty slot asm.emitPUSH_RegDisp(SP, srcOffset); // push long } } dstOffset -= 2 * WORDSIZE; } else if (t.isFloatType()) { if (fpr < NUM_PARAMETER_FPRS) { spIsOffBy += WORDSIZE; fprOffset[fpr] = dstOffset; is32bit[fpr] = true; fpr++; } else { if (spIsOffBy != 0) { // fix up SP if it drifted adjustStack(-spIsOffBy, true); spIsOffBy = 0; } asm.emitPUSH_RegDisp(SP, srcOffset); } dstOffset -= WORDSIZE; } else if (t.isDoubleType()) { if (fpr < NUM_PARAMETER_FPRS) { spIsOffBy += 2 * WORDSIZE; dstOffset -= WORDSIZE; fprOffset[fpr] = dstOffset; dstOffset -= WORDSIZE; is32bit[fpr] = false; fpr++; } else { if (spIsOffBy != 0) { // fix up SP if it drifted adjustStack(-spIsOffBy, true); spIsOffBy = 0; } if (VM.BuildFor32Addr) { asm.emitPUSH_RegDisp(SP, srcOffset); // hi mem from caller's stackframe asm.emitPUSH_RegDisp(SP, srcOffset); // lo mem from caller's stackframe } else { adjustStack(-WORDSIZE, true); // create empty slot asm.emitPUSH_RegDisp(SP, srcOffset); // push double } dstOffset -= 2 * WORDSIZE; } } else { // t is object, int, short, char, byte, or boolean if (spIsOffBy != 0) { // fix up SP if it drifted adjustStack(-spIsOffBy, true); spIsOffBy = 0; } if (gpr < NUM_PARAMETER_GPRS) { asm.emitPUSH_Reg(T); T = T1; // at most 2 parameters can be passed in general purpose registers gpr++; } else { asm.emitPUSH_RegDisp(SP, srcOffset); } dstOffset -= WORDSIZE; } } if (spIsOffBy != 0) { // fix up SP if it drifted adjustStack(-spIsOffBy, true); } for (int i = fpr - 1; 0 <= i; i--) { // unload the floating point register stack (backwards) if (is32bit[i]) { if (SSE2_BASE) { asm.emitMOVSS_RegDisp_Reg(SP, Offset.fromIntSignExtend(fprOffset[i] - dstOffset - WORDSIZE), XMM.lookup(i)); } else { asm.emitFSTP_RegDisp_Reg(SP, Offset.fromIntSignExtend(fprOffset[i] - dstOffset - WORDSIZE), FP0); } } else { if (SSE2_BASE) { asm.emitMOVSD_RegDisp_Reg(SP, Offset.fromIntSignExtend(fprOffset[i] - dstOffset - WORDSIZE), XMM.lookup(i)); } else { asm.emitFSTP_RegDisp_Reg_Quad(SP, Offset.fromIntSignExtend(fprOffset[i] - dstOffset - WORDSIZE), FP0); } } } } /** * Pushes return value of method from register to operand stack. * * @param m the method whose return value is to be pushed */ private void genResultRegisterUnload(MethodReference m) { TypeReference t = m.getReturnType(); if (t.isVoidType()) { // nothing to do } else if (t.isLongType()) { if (VM.BuildFor32Addr) { asm.emitPUSH_Reg(T0); // high half asm.emitPUSH_Reg(T1); // low half } else { adjustStack(-WORDSIZE, true); asm.emitPUSH_Reg(T0); // long value } } else if (t.isFloatType()) { adjustStack(-WORDSIZE, true); if (SSE2_FULL) { asm.emitMOVSS_RegInd_Reg(SP, XMM0); } else { asm.emitFSTP_RegInd_Reg(SP, FP0); } } else if (t.isDoubleType()) { adjustStack(-2 * WORDSIZE, true); if (SSE2_FULL) { asm.emitMOVSD_RegInd_Reg(SP, XMM0); } else { asm.emitFSTP_RegInd_Reg_Quad(SP, FP0); } } else { // t is object, int, short, char, byte, or boolean asm.emitPUSH_Reg(T0); } } /** * @param whereFrom is this thread switch from a PROLOGUE, BACKEDGE, or EPILOGUE? */ private void genThreadSwitchTest(int whereFrom) { if (!isInterruptible) { return; } // thread switch requested ?? asm.emitCMP_RegDisp_Imm(THREAD_REGISTER, Entrypoints.takeYieldpointField.getOffset(), 0); ForwardReference fr1; Offset yieldOffset; if (whereFrom == RVMThread.PROLOGUE) { // Take yieldpoint if yieldpoint flag is non-zero (either 1 or -1) fr1 = asm.forwardJcc(EQ); yieldOffset = Entrypoints.yieldpointFromPrologueMethod.getOffset(); } else if (whereFrom == RVMThread.BACKEDGE) { // Take yieldpoint if yieldpoint flag is >0 fr1 = asm.forwardJcc(LE); yieldOffset = Entrypoints.yieldpointFromBackedgeMethod.getOffset(); } else { // EPILOGUE // Take yieldpoint if yieldpoint flag is non-zero (either 1 or -1) fr1 = asm.forwardJcc(EQ); yieldOffset = Entrypoints.yieldpointFromEpilogueMethod.getOffset(); } asm.generateJTOCcall(yieldOffset); fr1.resolve(asm); if (VM.BuildForAdaptiveSystem && options.INVOCATION_COUNTERS) { int id = compiledMethod.getId(); InvocationCounts.allocateCounter(id); asm.generateJTOCloadWord(ECX, AosEntrypoints.invocationCountsField.getOffset()); if (VM.BuildFor32Addr) { asm.emitSUB_RegDisp_Imm(ECX, Offset.fromIntZeroExtend(compiledMethod.getId() << LOG_BYTES_IN_INT), 1); } else { asm.emitSUB_RegDisp_Imm_Quad(ECX, Offset.fromIntZeroExtend(compiledMethod.getId() << LOG_BYTES_IN_INT), 1); } ForwardReference notTaken = asm.forwardJcc(GT); asm.emitPUSH_Imm(id); genParameterRegisterLoad(asm, 1); asm.generateJTOCcall(AosEntrypoints.invocationCounterTrippedMethod.getOffset()); notTaken.resolve(asm); } } /** * Generate magic method * @param m method to generate * @return true if magic method was generated */ private boolean genMagic(MethodReference m) { if (BaselineMagic.generateMagic(asm, m, method, fp2spOffset(NO_SLOT))) { return true; } else if (m.isSysCall()) { TypeReference[] args = m.getParameterTypes(); TypeReference rtype = m.getReturnType(); Offset offsetToLastArg = THREE_SLOTS; // the three regs saved in (1) Offset offsetToFirstArg = offsetToLastArg.plus((m.getParameterWords() - 1) << LG_WORDSIZE); boolean[] inRegister = VM.BuildFor32Addr ? null : new boolean[args.length]; int paramBytes = 0; // (1) save three RVM nonvolatile/special registers // we don't have to save EBP: the callee will // treat it as a framepointer and save/restore // it for us. asm.emitPUSH_Reg(EBX); asm.emitPUSH_Reg(ESI); asm.emitPUSH_Reg(EDI); // (2) Pass args in registers passing from left-to-right // (NB avoid the first argument holding the target function address) int gpRegistersInUse = 0; int fpRegistersInUse = 0; Offset offsetToJavaArg = offsetToFirstArg; if (VM.BuildFor64Addr) { for (int i = 1; i < args.length; i++) { TypeReference arg = args[i]; if (arg.isFloatType()) { if (fpRegistersInUse < NATIVE_PARAMETER_FPRS.length) { inRegister[i] = true; offsetToJavaArg = offsetToJavaArg.minus(WORDSIZE); asm.emitMOVSS_Reg_RegDisp((XMM)NATIVE_PARAMETER_FPRS[fpRegistersInUse], SP, offsetToJavaArg); fpRegistersInUse++; } } else if (arg.isDoubleType()) { if (fpRegistersInUse < NATIVE_PARAMETER_FPRS.length) { inRegister[i] = true; offsetToJavaArg = offsetToJavaArg.minus(2 * WORDSIZE); asm.emitMOVSD_Reg_RegDisp((XMM)NATIVE_PARAMETER_FPRS[fpRegistersInUse], SP, offsetToJavaArg); fpRegistersInUse++; } } else if (arg.isLongType()) { if (gpRegistersInUse < NATIVE_PARAMETER_GPRS.length) { inRegister[i] = true; offsetToJavaArg = offsetToJavaArg.minus(2 * WORDSIZE); asm.emitMOV_Reg_RegDisp_Quad(NATIVE_PARAMETER_GPRS[gpRegistersInUse], SP, offsetToJavaArg); gpRegistersInUse++; } } else if (arg.isWordLikeType() || arg.isReferenceType()) { if (gpRegistersInUse < NATIVE_PARAMETER_GPRS.length) { inRegister[i] = true; offsetToJavaArg = offsetToJavaArg.minus(WORDSIZE); asm.emitMOV_Reg_RegDisp_Quad(NATIVE_PARAMETER_GPRS[gpRegistersInUse], SP, offsetToJavaArg); gpRegistersInUse++; } } else { if (gpRegistersInUse < NATIVE_PARAMETER_GPRS.length) { inRegister[i] = true; offsetToJavaArg = offsetToJavaArg.minus(WORDSIZE); asm.emitMOV_Reg_RegDisp(NATIVE_PARAMETER_GPRS[gpRegistersInUse], SP, offsetToJavaArg); gpRegistersInUse++; } } } } // (3) Stack alignment ForwardReference dontRealignStack = null; int argsToPush = 0; if (VM.BuildFor64Addr) { for (int i = args.length - 1; i >= 1; i--) { if (!inRegister[i]) { TypeReference arg = args[i]; if (arg.isLongType() || arg.isDoubleType()) { argsToPush += 2; } else { argsToPush ++; } } } asm.emitTEST_Reg_Imm(SP, 0x8); if ((argsToPush & 1) != 0) { dontRealignStack = asm.forwardJcc(NE); } else { dontRealignStack = asm.forwardJcc(EQ); } } // Generate argument pushing and call code upto twice, once with realignment ForwardReference afterCalls = null; for (int j = VM.BuildFor32Addr ? 1 : 0; j < 2; j++) { if (j == 0) { adjustStack(-WORDSIZE, true); offsetToFirstArg = offsetToFirstArg.plus(WORDSIZE); offsetToLastArg = offsetToLastArg.plus(WORDSIZE); } else { if (dontRealignStack != null) dontRealignStack.resolve(asm); } // (4) Stack remaining args to target function from right-to-left // (NB avoid the first argument holding the target function address) offsetToJavaArg = offsetToLastArg; for (int i = args.length - 1; i >= 1; i--) { TypeReference arg = args[i]; if (VM.BuildFor32Addr) { if (arg.isLongType() || arg.isDoubleType()) { asm.emitPUSH_RegDisp(SP, offsetToJavaArg.plus(WORDSIZE)); asm.emitPUSH_RegDisp(SP, offsetToJavaArg.plus(WORDSIZE)); offsetToJavaArg = offsetToJavaArg.plus(4 * WORDSIZE); offsetToFirstArg = offsetToFirstArg.plus(2 * WORDSIZE); offsetToLastArg = offsetToLastArg.plus(2 * WORDSIZE); paramBytes += 2 * WORDSIZE; } else { asm.emitPUSH_RegDisp(SP, offsetToJavaArg); offsetToJavaArg = offsetToJavaArg.plus(2 * WORDSIZE); offsetToFirstArg = offsetToFirstArg.plus(WORDSIZE); offsetToLastArg = offsetToLastArg.plus(WORDSIZE); paramBytes += WORDSIZE; } } else { if (!inRegister[i]) { if (arg.isLongType() || arg.isDoubleType()) { adjustStack(-WORDSIZE, true); asm.emitPUSH_RegDisp(SP, offsetToJavaArg.plus(WORDSIZE)); offsetToJavaArg = offsetToJavaArg.plus(4 * WORDSIZE); offsetToFirstArg = offsetToFirstArg.plus(2 * WORDSIZE); offsetToLastArg = offsetToLastArg.plus(2 * WORDSIZE); paramBytes += 2 * WORDSIZE; } else { asm.emitPUSH_RegDisp(SP, offsetToJavaArg); offsetToJavaArg = offsetToJavaArg.plus(2 * WORDSIZE); offsetToFirstArg = offsetToFirstArg.plus(WORDSIZE); offsetToLastArg = offsetToLastArg.plus(WORDSIZE); paramBytes += WORDSIZE; } } else { if (arg.isLongType() || arg.isDoubleType()) { offsetToJavaArg = offsetToJavaArg.plus(2 * WORDSIZE); } else { offsetToJavaArg = offsetToJavaArg.plus(WORDSIZE); } } } } if (VM.VerifyAssertions) VM._assert(offsetToFirstArg.EQ(offsetToJavaArg)); // (5) invoke target function with address given by the first argument if (VM.BuildFor32Addr) { asm.emitMOV_Reg_RegDisp(S0, SP, offsetToFirstArg); asm.emitCALL_Reg(S0); } else { asm.emitMOV_Reg_RegDisp_Quad(T0, SP, offsetToFirstArg); asm.emitCALL_Reg(T0); } // (6) pop space for arguments if (j == 0) { offsetToFirstArg = offsetToFirstArg.minus(WORDSIZE); offsetToLastArg = offsetToLastArg.minus(WORDSIZE); adjustStack(paramBytes + WORDSIZE, true); afterCalls = asm.forwardJMP(); } else { adjustStack(paramBytes, true); } } if (afterCalls != null) afterCalls.resolve(asm); // (7) restore RVM registers asm.emitPOP_Reg(EDI); asm.emitPOP_Reg(ESI); asm.emitPOP_Reg(EBX); // (8) pop expression stack (including the first parameter) adjustStack(m.getParameterWords() << LG_WORDSIZE, true); // (9) push return value if (rtype.isLongType()) { if (VM.BuildFor32Addr) { asm.emitPUSH_Reg(T1); asm.emitPUSH_Reg(T0); } else { adjustStack(-WORDSIZE, true); asm.emitPUSH_Reg(T0); } } else if (rtype.isDoubleType()) { adjustStack(-2 * WORDSIZE, true); if (VM.BuildFor32Addr) { asm.emitFSTP_RegInd_Reg_Quad(SP, FP0); } else { asm.emitMOVSD_RegInd_Reg(SP, XMM0); } } else if (rtype.isFloatType()) { adjustStack(-WORDSIZE, true); if (VM.BuildFor32Addr) { asm.emitFSTP_RegInd_Reg(SP, FP0); } else { asm.emitMOVSS_RegInd_Reg(SP, XMM0); } } else if (!rtype.isVoidType()) { asm.emitPUSH_Reg(T0); } return true; } else { return false; } } /** * @param local index of local * @return offset of Java local variable (off stack pointer) * assuming ESP is still positioned as it was at the * start of the current bytecode (biStart) * @throws UnreachableBytecodeException when the stack heights information for * the current bytecode is invalid. This can only happen when the bytecode is * unreachable. */ private Offset localOffset(int local) throws UnreachableBytecodeException { int stackHeight = stackHeights[biStart]; // Have we computed stack height information? if (stackHeight < TemplateCompilerFramework.stackHeightForEmptyBasicBlock(method)) { // If stack heights weren't computed, the bytecode must be unreachable. throw new UnreachableBytecodeException(); } if (VM.VerifyAssertions) VM._assert(method.getLocalWords() > local); return Offset.fromIntZeroExtend((stackHeights[biStart] - local) << LG_WORDSIZE); } /** * Translates a FP offset into an SP offset * assuming ESP is still positioned as it was at the * start of the current bytecode (biStart). * * @param offset the FP offset * @return the SP offset */ private Offset fp2spOffset(Offset offset) { Offset offsetToFrameHead = Offset.fromIntSignExtend(stackHeights[biStart] << LG_WORDSIZE).minus(firstLocalOffset); return offset.plus(offsetToFrameHead); } /** * Emit dynamic linking sequence placing the offset of the given member in reg * @param asm assembler to generate code into * @param reg register to hold offset to method * @param ref method reference to be resolved * @param couldBeZero could the value in the offsets table require resolving */ static void emitDynamicLinkingSequence(Assembler asm, GPR reg, MemberReference ref, boolean couldBeZero) { int memberId = ref.getId(); Offset memberOffset = Offset.fromIntZeroExtend(memberId << 2); Offset tableOffset = Entrypoints.memberOffsetsField.getOffset(); if (couldBeZero) { int retryLabel = asm.getMachineCodeIndex(); // branch here after dynamic class loading asm.generateJTOCloadWord(reg, tableOffset); // reg is offsets table if (VM.BuildFor32Addr) { asm.emitMOV_Reg_RegDisp(reg, reg, memberOffset); // reg is offset of member, or 0 if member's class isn't loaded } else { asm.emitMOVSXDQ_Reg_RegDisp(reg, reg, memberOffset); // reg is offset of member, or 0 if member's class isn't loaded } if (NEEDS_DYNAMIC_LINK == 0) { asm.emitTEST_Reg_Reg(reg, reg); // reg ?= NEEDS_DYNAMIC_LINK, is field's class loaded? } else { asm.emitCMP_Reg_Imm(reg, NEEDS_DYNAMIC_LINK); // reg ?= NEEDS_DYNAMIC_LINK, is field's class loaded? } ForwardReference fr = asm.forwardJcc(NE); // if so, skip call instructions asm.emitPUSH_Imm(memberId); // pass member's dictId genParameterRegisterLoad(asm, 1); // pass 1 parameter word Offset resolverOffset = Entrypoints.resolveMemberMethod.getOffset(); asm.generateJTOCcall(resolverOffset); // does class loading as sideffect asm.emitJMP_Imm(retryLabel); // reload reg with valid value fr.resolve(asm); // come from Jcc above. } else { asm.generateJTOCloadWord(reg, tableOffset); // reg is offsets table if (VM.BuildFor32Addr) { asm.emitMOV_Reg_RegDisp(reg, reg, memberOffset); // reg is offset of member } else { asm.emitMOVSXDQ_Reg_RegDisp(reg, reg, memberOffset); // reg is offset of member } } } /** * OSR routine to emit code to invoke a compiled method (with known jtoc * offset). Treat it like a resolved invoke static, but take care of * this object in the case.<p> * * I have not thought about GCMaps for invoke_compiledmethod.<p> * TODO: Figure out what the above GCMaps comment means and fix it! */ @Override protected void emit_invoke_compiledmethod(CompiledMethod cm) { Offset methodOffset = cm.getOsrJTOCoffset(); boolean takeThis = !cm.method.isStatic(); MethodReference ref = cm.method.getMemberRef().asMethodReference(); genParameterRegisterLoad(ref, takeThis); asm.generateJTOCcall(methodOffset); genResultRegisterUnload(ref); } /** * Implementation for OSR load return address bytecode */ @Override protected void emit_loadretaddrconst(int bcIndex) { asm.generateLoadReturnAddress(bcIndex); } /** * Generate branch for pending goto OSR mechanism * @param bTarget is optional, it emits a JUMP instruction, but the caller * is responsible for patching the target address by calling the resolve method * of the returned forward reference. */ @Override protected ForwardReference emit_pending_goto(int bTarget) { return asm.generatePendingJMP(bTarget); } @Override protected void ending_method() { asm.noteEndOfBytecodes(); } }