/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Common Public License (CPL); * 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/cpl1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.compilers.baseline.ia32; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import org.jikesrvm.ArchitectureSpecific; import org.jikesrvm.VM; import org.jikesrvm.VM_MachineCode; import org.jikesrvm.VM_SizeConstants; import org.jikesrvm.adaptive.VM_AosEntrypoints; import org.jikesrvm.adaptive.recompilation.VM_InvocationCounts; import org.jikesrvm.classloader.VM_Array; import org.jikesrvm.classloader.VM_Atom; import org.jikesrvm.classloader.VM_Class; import org.jikesrvm.classloader.VM_Field; import org.jikesrvm.classloader.VM_FieldReference; import org.jikesrvm.classloader.VM_InterfaceInvocation; import org.jikesrvm.classloader.VM_InterfaceMethodSignature; import org.jikesrvm.classloader.VM_MemberReference; import org.jikesrvm.classloader.VM_Method; import org.jikesrvm.classloader.VM_MethodReference; import org.jikesrvm.classloader.VM_NormalMethod; import org.jikesrvm.classloader.VM_Type; import org.jikesrvm.classloader.VM_TypeReference; import org.jikesrvm.compilers.baseline.VM_BaselineCompiledMethod; import org.jikesrvm.compilers.baseline.VM_BaselineCompiler; import org.jikesrvm.compilers.baseline.VM_EdgeCounts; import org.jikesrvm.compilers.common.VM_CompiledMethod; import org.jikesrvm.compilers.common.assembler.VM_ForwardReference; import org.jikesrvm.compilers.common.assembler.ia32.VM_Assembler; import org.jikesrvm.ia32.VM_BaselineConstants; import org.jikesrvm.ia32.VM_ProcessorLocalState; import org.jikesrvm.ia32.VM_StackframeLayoutConstants; import org.jikesrvm.jni.ia32.VM_JNICompiler; import org.jikesrvm.memorymanagers.mminterface.MM_Constants; import org.jikesrvm.memorymanagers.mminterface.MM_Interface; import org.jikesrvm.objectmodel.VM_ObjectModel; import org.jikesrvm.runtime.VM_ArchEntrypoints; import org.jikesrvm.runtime.VM_Entrypoints; import org.jikesrvm.runtime.VM_Magic; import org.jikesrvm.runtime.VM_MagicNames; import org.jikesrvm.runtime.VM_Runtime; import org.jikesrvm.runtime.VM_Statics; import org.jikesrvm.scheduler.VM_Thread; import org.vmmagic.pragma.Entrypoint; import org.vmmagic.pragma.Inline; import org.vmmagic.pragma.Uninterruptible; import org.vmmagic.unboxed.Offset; /** * VM_Compiler is the baseline compiler class for the IA32 architecture. */ public abstract class VM_Compiler extends VM_BaselineCompiler implements VM_BaselineConstants, VM_SizeConstants, VM_StackframeLayoutConstants { private final int parameterWords; private int firstLocalOffset; /** Generate array index out of bounds checks? */ private final boolean generateBoundsChecks; private static final Offset NO_SLOT = Offset.zero(); private static final Offset ONE_SLOT = NO_SLOT.plus(WORDSIZE); private static final Offset TWO_SLOTS = ONE_SLOT.plus(WORDSIZE); private static final Offset THREE_SLOTS = TWO_SLOTS.plus(WORDSIZE); private static final Offset FOUR_SLOTS = THREE_SLOTS.plus(WORDSIZE); private static final Offset FIVE_SLOTS = FOUR_SLOTS.plus(WORDSIZE); private static final Offset MINUS_ONE_SLOT = NO_SLOT.minus(WORDSIZE); private final ArchitectureSpecific.VM_Assembler asm; /** * Create a VM_Compiler object for the compilation of method. */ protected VM_Compiler(VM_BaselineCompiledMethod cm) { super(cm); asm = (ArchitectureSpecific.VM_Assembler) abstractAsm; stackHeights = new int[bcodes.length()]; parameterWords = method.getParameterWords() + (method.isStatic() ? 0 : 1); // add 1 for this pointer generateBoundsChecks = !method.hasNoBoundsCheckAnnotation(); } @Override protected void initializeCompiler() { //nothing to do for intel } public final int getLastFixedStackRegister() { return -1; //doesn't dedicate registers to stack; } public final int getLastFloatStackRegister() { return -1; //doesn't dedicate registers to stack; } @Uninterruptible @Inline public static int getGeneralLocalLocation(int index, int[] localloc, VM_NormalMethod m) { return offsetToLocation(getStartLocalOffset(m) - (index << LOG_BYTES_IN_ADDRESS)); //we currently do not use location arrays on intel } @Uninterruptible @Inline public static int getFloatLocalLocation(int index, int[] localloc, VM_NormalMethod m) { return offsetToLocation(getStartLocalOffset(m) - (index << LOG_BYTES_IN_ADDRESS)); //we currently do not use location arrays on intel } @Inline @Uninterruptible public static int locationToOffset(int location) { return -location; } @Inline @Uninterruptible public static int offsetToLocation(int offset) { return -offset; } /** * The last true local */ public static int getEmptyStackOffset(VM_NormalMethod m) { return getFirstLocalOffset(m) - (m.getLocalWords() << LG_WORDSIZE) + WORDSIZE; } /** * This is misnamed. It should be getFirstParameterOffset. * It will not work as a base to access true locals. * TODO!! make sure it is not being used incorrectly */ @Uninterruptible @Inline private static int getFirstLocalOffset(VM_NormalMethod method) { if (method.getDeclaringClass().hasBridgeFromNativeAnnotation()) { return STACKFRAME_BODY_OFFSET - (VM_JNICompiler.SAVED_GPRS_FOR_JNI << LG_WORDSIZE); } else { return STACKFRAME_BODY_OFFSET - (SAVED_GPRS << LG_WORDSIZE); } } @Uninterruptible @Inline private static int getStartLocalOffset(VM_NormalMethod method) { return getFirstLocalOffset(method) + WORDSIZE; } /* * implementation of abstract methods of VM_BaselineCompiler */ /* * Misc routines not directly tied to a particular bytecode */ /** * Notify VM_Compiler that we are starting code gen for the bytecode biStart */ @Override protected final void starting_bytecode() {} /** * Emit the prologue for the method */ @Override protected final void emit_prologue() { genPrologue(); } /** * Emit the code for a threadswitch tests (aka a yieldpoint). * @param whereFrom is this thread switch from a PROLOGUE, BACKEDGE, or EPILOGUE? */ @Override protected final void emit_threadSwitchTest(int whereFrom) { genThreadSwitchTest(whereFrom); } /** * Emit the code to implement the spcified magic. * @param magicMethod desired magic */ @Override protected final boolean emit_Magic(VM_MethodReference magicMethod) { return genMagic(magicMethod); } /* * Loading constants */ /** * Emit code to load the null constant. */ @Override protected final void emit_aconst_null() { asm.emitPUSH_Imm(0); } /** * Emit code to load an int constant. * @param val the int constant to load */ @Override protected final void emit_iconst(int val) { asm.emitPUSH_Imm(val); } /** * Emit code to load a long constant * @param val the lower 32 bits of long constant (upper32 are 0). */ @Override protected final void emit_lconst(int val) { asm.emitPUSH_Imm(0); // high part asm.emitPUSH_Imm(val); // low part } /** * Emit code to load 0.0f */ @Override protected final void emit_fconst_0() { asm.emitPUSH_Imm(0); } /** * Emit code to load 1.0f */ @Override protected final void emit_fconst_1() { asm.emitPUSH_Imm(0x3f800000); } /** * Emit code to load 2.0f */ @Override protected final void emit_fconst_2() { asm.emitPUSH_Imm(0x40000000); } /** * Emit code to load 0.0d */ @Override protected final void emit_dconst_0() { asm.emitPUSH_Imm(0x00000000); asm.emitPUSH_Imm(0x00000000); } /** * Emit code to load 1.0d */ @Override protected final void emit_dconst_1() { asm.emitPUSH_Imm(0x3ff00000); asm.emitPUSH_Imm(0x00000000); } /** * Emit code to load a 32 bit constant * @param offset JTOC offset of the constant * @param type the type of the constant */ @Override protected final void emit_ldc(Offset offset, byte type) { asm.emitPUSH_RegDisp(JTOC, offset); } /** * Emit code to load a 64 bit constant * @param offset JTOC offset of the constant * @param type the type of the constant */ @Override protected final void emit_ldc2(Offset offset, byte type) { if (SSE2_BASE) { asm.emitMOVQ_Reg_RegDisp(XMM0, JTOC, offset); // XMM0 is constant value asm.emitADD_Reg_Imm(SP, -8); // adjust stack asm.emitMOVQ_RegInd_Reg(SP, XMM0); // place value on stack } else { asm.emitPUSH_RegDisp(JTOC, offset.plus(4)); // high 32 bits asm.emitPUSH_RegDisp(JTOC, offset); // low 32 bits } } /* * loading local variables */ /** * Emit code to load an int local variable * @param index the local index to load */ @Override protected final void emit_iload(int index) { Offset offset = localOffset(index); asm.emitPUSH_RegDisp(ESP, offset); } /** * Emit code to local a float local variable * @param index the local index to load */ @Override protected final void emit_fload(int index) { // identical to iload - code replicated for BaseBase compiler performance Offset offset = localOffset(index); asm.emitPUSH_RegDisp(ESP, offset); } /** * Emit code to load a reference local variable * @param index the local index to load */ @Override protected final void emit_aload(int index) { // identical to iload - code replicated for BaseBase compiler performance Offset offset = localOffset(index); asm.emitPUSH_RegDisp(ESP, offset); } /** * Emit code to load a long local variable * @param index the local index to load */ @Override protected final void emit_lload(int index) { Offset offset = localOffset(index); if (SSE2_BASE) { asm.emitMOVQ_Reg_RegDisp(XMM0, SP, offset.minus(4)); // XMM0 is local value asm.emitADD_Reg_Imm(SP, -8); // adjust stack asm.emitMOVQ_RegInd_Reg(SP, XMM0); // place value on stack } else { asm.emitPUSH_RegDisp(ESP, offset); // high part asm.emitPUSH_RegDisp(ESP, offset); // low part (ESP has moved by 4!!) } } /** * Emit code to load a double local variable * @param index the local index to load */ @Override protected final void emit_dload(int index) { // identical to lload - code replicated for BaseBase compiler performance Offset offset = localOffset(index); if (SSE2_BASE) { asm.emitMOVQ_Reg_RegDisp(XMM0, SP, offset.minus(4)); // XMM0 is local value asm.emitADD_Reg_Imm(SP, -8); // adjust stack asm.emitMOVQ_RegInd_Reg(SP, XMM0); // place value on stack } else { asm.emitPUSH_RegDisp(ESP, offset); // high part asm.emitPUSH_RegDisp(ESP, offset); // low part (ESP has moved by 4!!) } } /* * storing local variables */ /** * Emit code to store an int to a local variable * @param index the local index to load */ @Override protected final void emit_istore(int index) { Offset offset = localOffset(index).minus(4); // pop computes EA after ESP has moved by 4! asm.emitPOP_RegDisp(ESP, offset); } /** * Emit code to store a float to a local variable * @param index the local index to load */ @Override protected final void emit_fstore(int index) { // identical to istore - code replicated for BaseBase compiler performance Offset offset = localOffset(index).minus(4); // pop computes EA after ESP has moved by 4! asm.emitPOP_RegDisp(ESP, offset); } /** * Emit code to store a reference to a local variable * @param index the local index to load */ @Override protected final void emit_astore(int index) { // identical to istore - code replicated for BaseBase compiler performance Offset offset = localOffset(index).minus(4); // pop computes EA after ESP has moved by 4! asm.emitPOP_RegDisp(ESP, offset); } /** * Emit code to store a long to a local variable * @param index the local index to load */ @Override protected final void emit_lstore(int index) { if (SSE2_BASE) { Offset offset = localOffset(index).minus(4); asm.emitMOVQ_Reg_RegInd(XMM0, SP); // XMM0 is stack value asm.emitMOVQ_RegDisp_Reg(SP, offset, XMM0); // place value in local asm.emitADD_Reg_Imm(SP, 8); } else { // pop computes EA after ESP has moved by 4! Offset offset = localOffset(index + 1).minus(4); asm.emitPOP_RegDisp(ESP, offset); // high part asm.emitPOP_RegDisp(ESP, offset); // low part (ESP has moved by 4!!) } } /** * Emit code to store an double to a local variable * @param index the local index to load */ @Override protected final void emit_dstore(int index) { // identical to lstore - code replicated for BaseBase compiler performance if (SSE2_BASE) { Offset offset = localOffset(index).minus(4); asm.emitMOVQ_Reg_RegInd(XMM0, SP); // XMM0 is stack value asm.emitMOVQ_RegDisp_Reg(SP, offset, XMM0); // place value in local asm.emitADD_Reg_Imm(SP, 8); } else { // pop computes EA after ESP has moved by 4! Offset offset = localOffset(index + 1).minus(4); asm.emitPOP_RegDisp(ESP, offset); // high part asm.emitPOP_RegDisp(ESP, offset); // low part (ESP has moved by 4!!) } } /* * array loads */ /** * Emit code to load from an int array */ @Override protected final void emit_iaload() { asm.emitMOV_Reg_RegInd(T0, SP); // T0 is array index asm.emitMOV_Reg_RegDisp(S0, SP, ONE_SLOT); // S0 is array ref asm.emitADD_Reg_Imm(SP, WORDSIZE * 2); // complete popping the 2 args genBoundsCheck(asm, T0, S0); // T0 is index, S0 is address of array // push [S0+T0<<2] asm.emitPUSH_RegIdx(S0, T0, VM_Assembler.WORD, NO_SLOT); } /** * Emit code to load from a float array */ @Override protected final void emit_faload() { // identical to iaload - code replicated for BaseBase compiler performance asm.emitMOV_Reg_RegInd(T0, SP); // T0 is array index asm.emitMOV_Reg_RegDisp(S0, SP, ONE_SLOT); // S0 is array ref asm.emitADD_Reg_Imm(SP, WORDSIZE * 2); // complete popping the 2 args genBoundsCheck(asm, T0, S0); // T0 is index, S0 is address of array // push [S0+T0<<2] asm.emitPUSH_RegIdx(S0, T0, VM_Assembler.WORD, NO_SLOT); } /** * Emit code to load from a reference array */ @Override protected final void emit_aaload() { // identical to iaload - code replicated for BaseBase compiler performance asm.emitMOV_Reg_RegInd(T0, SP); // T0 is array index asm.emitMOV_Reg_RegDisp(S0, SP, ONE_SLOT); // S0 is array ref asm.emitADD_Reg_Imm(SP, WORDSIZE * 2); // complete popping the 2 args genBoundsCheck(asm, T0, S0); // T0 is index, S0 is address of array // push [S0+T0<<2] asm.emitPUSH_RegIdx(S0, T0, VM_Assembler.WORD, NO_SLOT); } /** * Emit code to load from a char array */ @Override protected final void emit_caload() { asm.emitMOV_Reg_RegInd(T0, SP); // T0 is array index asm.emitMOV_Reg_RegDisp(S0, SP, ONE_SLOT); // S0 is array ref asm.emitADD_Reg_Imm(SP, WORDSIZE * 2); // complete popping the 2 args genBoundsCheck(asm, T0, S0); // T0 is index, S0 is address of array // T1 = (int)[S0+T0<<1] asm.emitMOVZX_Reg_RegIdx_Word(T1, S0, T0, VM_Assembler.SHORT, NO_SLOT); asm.emitPUSH_Reg(T1); // push short onto stack } /** * Emit code to load from a short array */ @Override protected final void emit_saload() { asm.emitMOV_Reg_RegInd(T0, SP); // T0 is array index asm.emitMOV_Reg_RegDisp(S0, SP, ONE_SLOT); // S0 is array ref asm.emitADD_Reg_Imm(SP, WORDSIZE * 2); // complete popping the 2 args genBoundsCheck(asm, T0, S0); // T0 is index, S0 is address of array // T1 = (int)[S0+T0<<1] asm.emitMOVSX_Reg_RegIdx_Word(T1, S0, T0, VM_Assembler.SHORT, NO_SLOT); asm.emitPUSH_Reg(T1); // push short onto stack } /** * Emit code to load from a byte/boolean array */ @Override protected final void emit_baload() { asm.emitMOV_Reg_RegInd(T0, SP); // T0 is array index asm.emitMOV_Reg_RegDisp(S0, SP, ONE_SLOT); // S0 is array ref asm.emitADD_Reg_Imm(SP, WORDSIZE * 2); // complete popping the 2 args genBoundsCheck(asm, T0, S0); // T0 is index, S0 is address of array // T1 = (int)[S0+T0<<1] asm.emitMOVSX_Reg_RegIdx_Byte(T1, S0, T0, VM_Assembler.BYTE, NO_SLOT); asm.emitPUSH_Reg(T1); // push short onto stack } /** * Emit code to load from a long array */ @Override protected final void emit_laload() { asm.emitMOV_Reg_RegDisp(T0, SP, NO_SLOT); // T0 is array index asm.emitMOV_Reg_RegDisp(S0, SP, ONE_SLOT); // S0 is the array ref if (!SSE2_BASE) { asm.emitADD_Reg_Imm(SP, WORDSIZE * 2); // complete popping the 2 args } genBoundsCheck(asm, T0, S0); // T0 is index, S0 is address of array if (SSE2_BASE) { asm.emitMOVQ_Reg_RegIdx(XMM0, S0, T0, VM_Assembler.LONG, NO_SLOT); asm.emitMOVQ_RegInd_Reg(SP, XMM0); } else { asm.emitPUSH_RegIdx(S0, T0, VM_Assembler.LONG, ONE_SLOT); // load high part of desired long array element asm.emitPUSH_RegIdx(S0, T0, VM_Assembler.LONG, NO_SLOT); // load low part of desired long array element } } /** * Emit code to load from a double array */ @Override protected final void emit_daload() { // identical to laload - code replicated for BaseBase compiler performance asm.emitMOV_Reg_RegDisp(T0, SP, NO_SLOT); // T0 is array index asm.emitMOV_Reg_RegDisp(S0, SP, ONE_SLOT); // S0 is the array ref if (!SSE2_BASE) { asm.emitADD_Reg_Imm(SP, WORDSIZE * 2); // complete popping the 2 args } genBoundsCheck(asm, T0, S0); // T0 is index, S0 is address of array if (SSE2_BASE) { asm.emitMOVQ_Reg_RegIdx(XMM0, S0, T0, VM_Assembler.LONG, NO_SLOT); asm.emitMOVQ_RegInd_Reg(SP, XMM0); } else { asm.emitPUSH_RegIdx(S0, T0, VM_Assembler.LONG, ONE_SLOT); // load high part of desired long array element asm.emitPUSH_RegIdx(S0, T0, VM_Assembler.LONG, NO_SLOT); // load low part of desired long array element } } /* * array stores */ /** * Emit code to store to an int array */ @Override protected final void emit_iastore() { VM_Barriers.compileModifyCheck(asm, 8); asm.emitMOV_Reg_RegDisp(T0, SP, ONE_SLOT); // T0 is array index asm.emitMOV_Reg_RegDisp(S0, SP, TWO_SLOTS); // S0 is array ref asm.emitMOV_Reg_RegInd(T1, SP); // T1 is the byte value asm.emitADD_Reg_Imm(SP, 3*WORDSIZE); // Shrink stack genBoundsCheck(asm, T0, S0); // T0 is index, S0 is address of array asm.emitMOV_RegIdx_Reg(S0, T0, VM_Assembler.WORD, NO_SLOT, T1); // [S0 + T0<<2] <- T1 } /** * Emit code to store to a float array */ @Override protected final void emit_fastore() { // identical to iastore - code replicated for BaseBase compiler performance VM_Barriers.compileModifyCheck(asm, 8); asm.emitMOV_Reg_RegDisp(T0, SP, ONE_SLOT); // T0 is array index asm.emitMOV_Reg_RegDisp(S0, SP, TWO_SLOTS); // S0 is array ref asm.emitMOV_Reg_RegInd(T1, SP); // T1 is the byte value asm.emitADD_Reg_Imm(SP, 3*WORDSIZE); // Shrink stack genBoundsCheck(asm, T0, S0); // T0 is index, S0 is address of array asm.emitMOV_RegIdx_Reg(S0, T0, VM_Assembler.WORD, NO_SLOT, T1); // [S0 + T0<<2] <- T1 } /** * Emit code to store to a reference array */ @Override protected final void emit_aastore() { VM_Barriers.compileModifyCheck(asm, 8); asm.emitPUSH_RegDisp(SP, TWO_SLOTS); // duplicate array ref asm.emitPUSH_RegDisp(SP, ONE_SLOT); // duplicate object value genParameterRegisterLoad(2); // pass 2 parameter // call checkstore(array ref, value) asm.emitCALL_RegDisp(JTOC, VM_Entrypoints.checkstoreMethod.getOffset()); asm.emitMOV_Reg_RegDisp(T0, SP, ONE_SLOT); // T0 is array index asm.emitMOV_Reg_RegDisp(S0, SP, TWO_SLOTS); // S0 is the array ref genBoundsCheck(asm, T0, S0); // T0 is index, S0 is address of array if (MM_Constants.NEEDS_WRITE_BARRIER) { VM_Barriers.compileArrayStoreBarrier(asm); asm.emitADD_Reg_Imm(SP, WORDSIZE * 3); // complete popping the 3 args } else { asm.emitMOV_Reg_RegDisp(T1, SP, NO_SLOT); // T1 is the object value asm.emitADD_Reg_Imm(SP, WORDSIZE * 3); // complete popping the 3 args asm.emitMOV_RegIdx_Reg(S0, T0, VM_Assembler.WORD, NO_SLOT, T1); // [S0 + T0<<2] <- T1 } } /** * Emit code to store to a char array */ @Override protected final void emit_castore() { VM_Barriers.compileModifyCheck(asm, 8); asm.emitMOV_Reg_RegDisp(T0, SP, ONE_SLOT); // T0 is array index asm.emitMOV_Reg_RegDisp(S0, SP, TWO_SLOTS); // S0 is array ref asm.emitMOV_Reg_RegInd(T1, SP); // T1 is the char value asm.emitADD_Reg_Imm(SP, 3*WORDSIZE); // Shrink stack genBoundsCheck(asm, T0, S0); // T0 is index, S0 is address of array // store halfword element into array i.e. [S0 +T0] <- T1 (halfword) asm.emitMOV_RegIdx_Reg_Word(S0, T0, VM_Assembler.SHORT, NO_SLOT, T1); } /** * Emit code to store to a short array */ @Override protected final void emit_sastore() { // identical to castore - code replicated for BaseBase compiler performance VM_Barriers.compileModifyCheck(asm, 8); asm.emitMOV_Reg_RegDisp(T0, SP, ONE_SLOT); // T0 is array index asm.emitMOV_Reg_RegDisp(S0, SP, TWO_SLOTS); // S0 is array ref asm.emitMOV_Reg_RegInd(T1, SP); // T1 is the char value asm.emitADD_Reg_Imm(SP, 3*WORDSIZE); // Shrink stack genBoundsCheck(asm, T0, S0); // T0 is index, S0 is address of array // store halfword element into array i.e. [S0 +T0] <- T1 (halfword) asm.emitMOV_RegIdx_Reg_Word(S0, T0, VM_Assembler.SHORT, NO_SLOT, T1); } /** * Emit code to store to a byte/boolean array */ @Override protected final void emit_bastore() { VM_Barriers.compileModifyCheck(asm, 8); asm.emitMOV_Reg_RegDisp(T0, SP, ONE_SLOT); // T0 is array index asm.emitMOV_Reg_RegDisp(S0, SP, TWO_SLOTS); // S0 is array ref asm.emitMOV_Reg_RegInd(T1, SP); // T1 is the byte value asm.emitADD_Reg_Imm(SP, 3*WORDSIZE); // Shrink stack genBoundsCheck(asm, T0, S0); // T0 is index, S0 is address of array asm.emitMOV_RegIdx_Reg_Byte(S0, T0, VM_Assembler.BYTE, NO_SLOT, T1); // [S0 + T0<<2] <- T1 } /** * Emit code to store to a long array */ @Override protected final void emit_lastore() { VM_Barriers.compileModifyCheck(asm, 12); asm.emitMOV_Reg_RegDisp(T0, SP, TWO_SLOTS); // T0 is the array index asm.emitMOV_Reg_RegDisp(S0, SP, THREE_SLOTS); // S0 is the array ref if (SSE2_BASE) { asm.emitMOVQ_Reg_RegInd(XMM0,SP); // XMM0 is the value asm.emitADD_Reg_Imm(SP, WORDSIZE * 4); // remove index and ref from the stack } else { asm.emitMOV_Reg_RegInd(T1, SP); // low part of long value } genBoundsCheck(asm, T0, S0); // T0 is index, S0 is address of array if (SSE2_BASE) { asm.emitMOVQ_RegIdx_Reg(S0, T0, VM_Assembler.LONG, NO_SLOT, XMM0); // [S0+T0<<<3] <- XMM0 } else { // [S0 + T0<<3 + 0] <- T1 store low part into array asm.emitMOV_RegIdx_Reg(S0, T0, VM_Assembler.LONG, NO_SLOT, T1); asm.emitMOV_Reg_RegDisp(T1, SP, ONE_SLOT); // high part of long value // [S0 + T0<<3 + 4] <- T1 store high part into array asm.emitADD_Reg_Imm(SP, WORDSIZE * 4); // remove index and ref from the stack asm.emitMOV_RegIdx_Reg(S0, T0, VM_Assembler.LONG, ONE_SLOT, T1); } } /** * Emit code to store to a double array */ @Override protected final void emit_dastore() { // identical to lastore - code replicated for BaseBase compiler performance VM_Barriers.compileModifyCheck(asm, 12); asm.emitMOV_Reg_RegDisp(T0, SP, TWO_SLOTS); // T0 is the array index asm.emitMOV_Reg_RegDisp(S0, SP, THREE_SLOTS); // S0 is the array ref if (SSE2_BASE) { asm.emitMOVQ_Reg_RegInd(XMM0,SP); // XMM0 is the value asm.emitADD_Reg_Imm(SP, WORDSIZE * 4); // remove index and ref from the stack } else { asm.emitMOV_Reg_RegInd(T1, SP); // low part of long value } genBoundsCheck(asm, T0, S0); // T0 is index, S0 is address of array if (SSE2_BASE) { asm.emitMOVQ_RegIdx_Reg(S0, T0, VM_Assembler.LONG, NO_SLOT, XMM0); // [S0+T0<<<3] <- XMM0 } else { // [S0 + T0<<3 + 0] <- T1 store low part into array asm.emitMOV_RegIdx_Reg(S0, T0, VM_Assembler.LONG, NO_SLOT, T1); asm.emitMOV_Reg_RegDisp(T1, SP, ONE_SLOT); // high part of long value // [S0 + T0<<3 + 4] <- T1 store high part into array asm.emitADD_Reg_Imm(SP, WORDSIZE * 4); // remove index and ref from the stack asm.emitMOV_RegIdx_Reg(S0, T0, VM_Assembler.LONG, ONE_SLOT, T1); } } /* * expression stack manipulation */ /** * Emit code to implement the pop bytecode */ @Override protected final void emit_pop() { asm.emitPOP_Reg(T0); } /** * Emit code to implement the pop2 bytecode */ @Override protected final 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. asm.emitPOP_Reg(T0); asm.emitPOP_Reg(T0); } /** * Emit code to implement the dup bytecode */ @Override protected final 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); } /** * Emit code to implement the dup_x1 bytecode */ @Override protected final void emit_dup_x1() { asm.emitPOP_Reg(T0); asm.emitPOP_Reg(S0); asm.emitPUSH_Reg(T0); asm.emitPUSH_Reg(S0); asm.emitPUSH_Reg(T0); } /** * Emit code to implement the dup_x2 bytecode */ @Override protected final 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); } /** * Emit code to implement the dup2 bytecode */ @Override protected final void emit_dup2() { asm.emitMOV_Reg_RegDisp(T0, SP, ONE_SLOT); asm.emitMOV_Reg_RegInd(S0, SP); asm.emitPUSH_Reg(T0); asm.emitPUSH_Reg(S0); } /** * Emit code to implement the dup2_x1 bytecode */ @Override protected final 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); } /** * Emit code to implement the dup2_x2 bytecode */ @Override protected final void emit_dup2_x2() { asm.emitPOP_Reg(T0); asm.emitPOP_Reg(S0); asm.emitPOP_Reg(T1); asm.emitPOP_Reg(JTOC); // JTOC is scratch register asm.emitPUSH_Reg(S0); asm.emitPUSH_Reg(T0); asm.emitPUSH_Reg(JTOC); asm.emitPUSH_Reg(T1); asm.emitPUSH_Reg(S0); asm.emitPUSH_Reg(T0); // restore JTOC register VM_ProcessorLocalState.emitMoveFieldToReg(asm, JTOC, VM_ArchEntrypoints.jtocField.getOffset()); } /** * Emit code to implement the swap bytecode */ @Override protected final 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 */ /** * Emit code to implement the iadd bytecode */ @Override protected final void emit_iadd() { asm.emitPOP_Reg(T0); asm.emitADD_RegInd_Reg(SP, T0); } /** * Emit code to implement the isub bytecode */ @Override protected final void emit_isub() { asm.emitPOP_Reg(T0); asm.emitSUB_RegInd_Reg(SP, T0); } /** * Emit code to implement the imul bytecode */ @Override protected final void emit_imul() { asm.emitPOP_Reg(T0); asm.emitIMUL2_Reg_RegInd(T0, SP); asm.emitMOV_RegInd_Reg(SP, T0); } /** * Emit code to implement the idiv bytecode */ @Override protected final void emit_idiv() { asm.emitMOV_Reg_RegDisp(ECX, SP, NO_SLOT); // ECX is divisor; NOTE: can't use symbolic registers because of intel hardware requirements asm.emitMOV_Reg_RegDisp(EAX, SP, ONE_SLOT); // EAX is dividend asm.emitCDQ(); // sign extend EAX into EDX asm.emitIDIV_Reg_Reg(EAX, ECX); // compute EAX/ECX - Quotient in EAX, remainder in EDX asm.emitADD_Reg_Imm(SP, WORDSIZE * 2); // complete popping the 2 values asm.emitPUSH_Reg(EAX); // push result } /** * Emit code to implement the irem bytecode */ @Override protected final void emit_irem() { asm.emitMOV_Reg_RegDisp(ECX, SP, NO_SLOT); // ECX is divisor; NOTE: can't use symbolic registers because of intel hardware requirements asm.emitMOV_Reg_RegDisp(EAX, SP, ONE_SLOT); // EAX is dividend asm.emitCDQ(); // sign extend EAX into EDX asm.emitIDIV_Reg_Reg(EAX, ECX); // compute EAX/ECX - Quotient in EAX, remainder in EDX asm.emitADD_Reg_Imm(SP, WORDSIZE * 2); // complete popping the 2 values asm.emitPUSH_Reg(EDX); // push remainder } /** * Emit code to implement the ineg bytecode */ @Override protected final void emit_ineg() { asm.emitNEG_RegInd(SP); // [SP] <- -[SP] } /** * Emit code to implement the ishl bytecode */ @Override protected final void emit_ishl() { asm.emitPOP_Reg(ECX); asm.emitSHL_RegInd_Reg(SP, ECX); } /** * Emit code to implement the ishr bytecode */ @Override protected final void emit_ishr() { asm.emitPOP_Reg(ECX); asm.emitSAR_RegInd_Reg(SP, ECX); } /** * Emit code to implement the iushr bytecode */ @Override protected final void emit_iushr() { asm.emitPOP_Reg(ECX); asm.emitSHR_RegInd_Reg(SP, ECX); } /** * Emit code to implement the iand bytecode */ @Override protected final void emit_iand() { asm.emitPOP_Reg(T0); asm.emitAND_RegInd_Reg(SP, T0); } /** * Emit code to implement the ior bytecode */ @Override protected final void emit_ior() { asm.emitPOP_Reg(T0); asm.emitOR_RegInd_Reg(SP, T0); } /** * Emit code to implement the ixor bytecode */ @Override protected final void emit_ixor() { asm.emitPOP_Reg(T0); asm.emitXOR_RegInd_Reg(SP, T0); } /** * Emit code to implement the iinc bytecode * @param index index of local * @param val value to increment it by */ @Override protected final void emit_iinc(int index, int val) { Offset offset = localOffset(index); asm.emitADD_RegDisp_Imm(ESP, offset, val); } /* * long ALU */ /** * Emit code to implement the ladd bytecode */ @Override protected final void emit_ladd() { 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 } /** * Emit code to implement the lsub bytecode */ @Override protected final void emit_lsub() { 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 } /** * Emit code to implement the lmul bytecode */ @Override protected final void emit_lmul() { // 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 VM_ForwardReference fr1 = asm.forwardJcc(VM_Assembler.NE); // EDX:EAX = 32bit multiply of multiplier and multiplicand low asm.emitMUL_Reg_Reg(EAX, EDX); // Jump over 64bit multiply VM_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); } /** * Emit code to implement the ldiv bytecode */ @Override protected final void emit_ldiv() { // (1) zero check asm.emitMOV_Reg_RegDisp(T0, SP, NO_SLOT); asm.emitOR_Reg_RegDisp(T0, SP, ONE_SLOT); asm.emitBranchLikelyNextInstruction(); VM_ForwardReference fr1 = asm.forwardJcc(VM_Assembler.NE); asm.emitINT_Imm(VM_Runtime.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_RegDisp(S0, JTOC, VM_Entrypoints.the_boot_recordField.getOffset()); asm.emitCALL_RegDisp(S0, VM_Entrypoints.sysLongDivideIPField.getOffset()); // (5) pop space for arguments asm.emitADD_Reg_Imm(SP, 4 * WORDSIZE); // (6) restore RVM nonvolatiles for (int i = numNonVols - 1; i >= 0; i--) { asm.emitPOP_Reg(NONVOLATILE_GPRS[i]); } // (7) pop expression stack asm.emitADD_Reg_Imm(SP, 4 * WORDSIZE); // (8) push results asm.emitPUSH_Reg(T1); asm.emitPUSH_Reg(T0); } /** * Emit code to implement the lrem bytecode */ @Override protected final void emit_lrem() { // (1) zero check asm.emitMOV_Reg_RegDisp(T0, SP, NO_SLOT); asm.emitOR_Reg_RegDisp(T0, SP, ONE_SLOT); asm.emitBranchLikelyNextInstruction(); VM_ForwardReference fr1 = asm.forwardJcc(VM_Assembler.NE); asm.emitINT_Imm(VM_Runtime.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_RegDisp(S0, JTOC, VM_Entrypoints.the_boot_recordField.getOffset()); asm.emitCALL_RegDisp(S0, VM_Entrypoints.sysLongRemainderIPField.getOffset()); // (5) pop space for arguments asm.emitADD_Reg_Imm(SP, 4 * WORDSIZE); // (6) restore RVM nonvolatiles for (int i = numNonVols - 1; i >= 0; i--) { asm.emitPOP_Reg(NONVOLATILE_GPRS[i]); } // (7) pop expression stack asm.emitADD_Reg_Imm(SP, 4 * WORDSIZE); // (8) push results asm.emitPUSH_Reg(T1); asm.emitPUSH_Reg(T0); } /** * Emit code to implement the lneg bytecode */ @Override protected final void emit_lneg() { asm.emitNOT_RegDisp(SP, ONE_SLOT); // [SP+4] <- ~[SP+4] or high <- ~high asm.emitNEG_RegInd(SP); // [SP] <- -[SP] or low <- -low asm.emitSBB_RegDisp_Imm(SP, ONE_SLOT, -1); // [SP+4] += 1+borrow or high += 1+borrow } /** * Emit code to implement the lshsl bytecode */ @Override protected final void emit_lshl() { 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.emitTEST_Reg_Imm(ECX, 32); VM_ForwardReference fr1 = asm.forwardJcc(VM_Assembler.NE); asm.emitSHLD_Reg_Reg_Reg(T1, T0, ECX); // shift high half asm.emitSHL_Reg_Reg(T0, ECX); // shift low half VM_ForwardReference fr2 = asm.forwardJMP(); fr1.resolve(asm); asm.emitMOV_Reg_Reg(T1, T0); // shift high half asm.emitSHL_Reg_Reg(T1, ECX); asm.emitXOR_Reg_Reg(T0, T0); // low half == 0 fr2.resolve(asm); asm.emitPUSH_Reg(T1); // push high half asm.emitPUSH_Reg(T0); // push low half } /** * Emit code to implement the lshr bytecode */ @Override protected final void emit_lshr() { 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.emitTEST_Reg_Imm(ECX, 32); VM_ForwardReference fr1 = asm.forwardJcc(VM_Assembler.NE); asm.emitSHRD_Reg_Reg_Reg(T0, T1, ECX); // shift high half asm.emitSAR_Reg_Reg(T1, ECX); // shift low half VM_ForwardReference fr2 = asm.forwardJMP(); fr1.resolve(asm); asm.emitMOV_Reg_Reg(T0, T1); // low half = high half asm.emitSAR_Reg_Imm(T1, 31); // high half = high half >> 31 asm.emitSAR_Reg_Reg(T0, ECX); // low half = high half >> ecx fr2.resolve(asm); asm.emitPUSH_Reg(T1); // push high half asm.emitPUSH_Reg(T0); // push low half } /** * Emit code to implement the lushr bytecode */ @Override protected final void emit_lushr() { 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.emitTEST_Reg_Imm(ECX, 32); VM_ForwardReference fr1 = asm.forwardJcc(VM_Assembler.NE); asm.emitSHRD_Reg_Reg_Reg(T0, T1, ECX); // shift high half asm.emitSHR_Reg_Reg(T1, ECX); // shift low half VM_ForwardReference fr2 = asm.forwardJMP(); fr1.resolve(asm); asm.emitMOV_Reg_Reg(T0, T1); // low half = high half asm.emitXOR_Reg_Reg(T1, T1); // high half = 0 asm.emitSHR_Reg_Reg(T0, ECX); // low half = high half >>> ecx fr2.resolve(asm); asm.emitPUSH_Reg(T1); // push high half asm.emitPUSH_Reg(T0); // push low half } /** * Emit code to implement the land bytecode */ @Override protected final void emit_land() { asm.emitPOP_Reg(T0); // low asm.emitPOP_Reg(S0); // high asm.emitAND_RegInd_Reg(SP, T0); asm.emitAND_RegDisp_Reg(SP, ONE_SLOT, S0); } /** * Emit code to implement the lor bytecode */ @Override protected final void emit_lor() { asm.emitPOP_Reg(T0); // low asm.emitPOP_Reg(S0); // high asm.emitOR_RegInd_Reg(SP, T0); asm.emitOR_RegDisp_Reg(SP, ONE_SLOT, S0); } /** * Emit code to implement the lxor bytecode */ @Override protected final void emit_lxor() { asm.emitPOP_Reg(T0); // low asm.emitPOP_Reg(S0); // high asm.emitXOR_RegInd_Reg(SP, T0); asm.emitXOR_RegDisp_Reg(SP, ONE_SLOT, S0); } /* * float ALU */ /** * Emit code to implement the fadd bytecode */ @Override protected final void emit_fadd() { if (SSE2_BASE) { asm.emitMOVSS_Reg_RegInd(XMM0, SP); // XMM0 = value2 asm.emitADDSS_Reg_RegDisp(XMM0, SP, ONE_SLOT); // XMM0 += value1 asm.emitPOP_Reg(T0); // discard asm.emitMOVSS_RegInd_Reg(SP, XMM0); // set result on stack } else { asm.emitFLD_Reg_RegInd(FP0, SP); // FPU reg. stack <- value2 asm.emitFADD_Reg_RegDisp(FP0, SP, ONE_SLOT); // FPU reg. stack += value1 asm.emitPOP_Reg(T0); // discard asm.emitFSTP_RegInd_Reg(SP, FP0); // POP FPU reg. stack onto stack } } /** * Emit code to implement the fsub bytecode */ @Override protected final void emit_fsub() { if (SSE2_BASE) { asm.emitMOVSS_Reg_RegDisp(XMM0, SP, ONE_SLOT); // XMM0 = value1 asm.emitSUBSS_Reg_RegInd(XMM0, SP); // XMM0 -= value2 asm.emitPOP_Reg(T0); // discard 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_RegDisp(FP0, SP, NO_SLOT); // FPU reg. stack -= value2 asm.emitPOP_Reg(T0); // discard asm.emitFSTP_RegInd_Reg(SP, FP0); // POP FPU reg. stack onto stack } } /** * Emit code to implement the fmul bytecode */ @Override protected final void emit_fmul() { if (SSE2_BASE) { asm.emitMOVSS_Reg_RegInd(XMM0, SP); // XMM0 = value2 asm.emitMULSS_Reg_RegDisp(XMM0, SP, ONE_SLOT); // XMM0 *= value1 asm.emitPOP_Reg(T0); // discard asm.emitMOVSS_RegInd_Reg(SP, XMM0); // set result on stack } else { asm.emitFLD_Reg_RegInd(FP0, SP); // FPU reg. stack <- value2 asm.emitFMUL_Reg_RegDisp(FP0, SP, ONE_SLOT); // FPU reg. stack *= value1 asm.emitPOP_Reg(T0); // discard asm.emitFSTP_RegInd_Reg(SP, FP0); // POP FPU reg. stack onto stack } } /** * Emit code to implement the fdiv bytecode */ @Override protected final void emit_fdiv() { if (SSE2_BASE) { asm.emitMOVSS_Reg_RegDisp(XMM0, SP, ONE_SLOT); // XMM0 = value1 asm.emitDIVSS_Reg_RegInd(XMM0, SP); // XMM0 /= value2 asm.emitPOP_Reg(T0); // discard 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_RegDisp(FP0, SP, NO_SLOT); // FPU reg. stack /= value2 asm.emitPOP_Reg(T0); // discard asm.emitFSTP_RegInd_Reg(SP, FP0); // POP FPU reg. stack onto stack } } /** * Emit code to implement the frem bytecode */ @Override protected final void emit_frem() { // TODO: Something else when SSE2? asm.emitFLD_Reg_RegInd(FP0, SP); // FPU reg. stack <- value2, or a asm.emitFLD_Reg_RegDisp(FP0, SP, ONE_SLOT); // FPU reg. stack <- value1, or b asm.emitFPREM(); // FPU reg. stack <- a%b asm.emitFSTP_RegDisp_Reg(SP, ONE_SLOT, FP0); // POP FPU reg. stack (results) onto java stack asm.emitFSTP_RegInd_Reg(SP, FP0); // POP FPU reg. stack onto java stack asm.emitPOP_Reg(T0); // shrink the stack (T0 discarded) } /** * Emit code to implement the fneg bytecode */ @Override protected final void emit_fneg() { if (SSE2_BASE) { asm.emitXORPS_Reg_Reg(XMM0, XMM0); // XMM0 = 0 asm.emitSUBSS_Reg_RegInd(XMM0, SP); // XMM0 -= value asm.emitMOVSS_RegInd_Reg(SP, XMM0); // set result on stack } else { asm.emitFLD_Reg_RegInd(FP0, SP); // FPU reg. stack <- value1 asm.emitFCHS(); // change sign to stop of FPU stack asm.emitFSTP_RegInd_Reg(SP, FP0); // POP FPU reg. stack onto stack } } /* * double ALU */ /** * Emit code to implement the dadd bytecode */ @Override protected final void emit_dadd() { if (SSE2_BASE) { asm.emitMOVSD_Reg_RegInd(XMM0, SP); // XMM0 = value2 asm.emitADDSD_Reg_RegDisp(XMM0, SP, TWO_SLOTS); // XMM0 += value1 asm.emitADD_Reg_Imm(SP, 2 * WORDSIZE); // discard asm.emitMOVSD_RegInd_Reg(SP, XMM0); // set result on stack } else { asm.emitFLD_Reg_RegInd_Quad(FP0, SP); // FPU reg. stack <- value2 asm.emitFADD_Reg_RegDisp_Quad(FP0, SP, TWO_SLOTS); // FPU reg. stack += value1 asm.emitADD_Reg_Imm(SP, 2 * WORDSIZE); // shrink the stack asm.emitFSTP_RegInd_Reg_Quad(SP, FP0); // POP FPU reg. stack onto stack } } /** * Emit code to implement the dsub bytecode */ @Override protected final void emit_dsub() { if (SSE2_BASE) { asm.emitMOVSD_Reg_RegDisp(XMM0, SP, TWO_SLOTS); // XMM0 = value1 asm.emitSUBSD_Reg_RegInd(XMM0, SP); // XMM0 -= value2 asm.emitADD_Reg_Imm(SP, 2 * WORDSIZE); // discard 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_RegDisp_Quad(FP0, SP, NO_SLOT); // FPU reg. stack -= value2 asm.emitADD_Reg_Imm(SP, 2 * WORDSIZE); // shrink the stack asm.emitFSTP_RegInd_Reg_Quad(SP, FP0); // POP FPU reg. stack onto stack } } /** * Emit code to implement the dmul bytecode */ @Override protected final void emit_dmul() { if (SSE2_BASE) { asm.emitMOVSD_Reg_RegInd(XMM0, SP); // XMM0 = value2 asm.emitMULSD_Reg_RegDisp(XMM0, SP, TWO_SLOTS); // XMM0 *= value1 asm.emitADD_Reg_Imm(SP, 2 * WORDSIZE); // discard asm.emitMOVSD_RegInd_Reg(SP, XMM0); // set result on stack } else { asm.emitFLD_Reg_RegInd_Quad(FP0, SP); // FPU reg. stack <- value2 asm.emitFMUL_Reg_RegDisp_Quad(FP0, SP, TWO_SLOTS); // FPU reg. stack *= value1 asm.emitADD_Reg_Imm(SP, 2 * WORDSIZE); // shrink the stack asm.emitFSTP_RegInd_Reg_Quad(SP, FP0); // POP FPU reg. stack onto stack } } /** * Emit code to implement the ddiv bytecode */ @Override protected final void emit_ddiv() { if (SSE2_BASE) { asm.emitMOVSD_Reg_RegDisp(XMM0, SP, TWO_SLOTS); // XMM0 = value1 asm.emitDIVSD_Reg_RegInd(XMM0, SP); // XMM0 /= value2 asm.emitADD_Reg_Imm(SP, 2 * WORDSIZE); // discard 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 asm.emitADD_Reg_Imm(SP, 2 * WORDSIZE); // shrink the stack asm.emitFSTP_RegInd_Reg_Quad(SP, FP0); // POP FPU reg. stack onto stack } } /** * Emit code to implement the drem bytecode */ @Override protected final void emit_drem() { // TODO: Something else when SSE2? asm.emitFLD_Reg_RegInd_Quad(FP0, SP); // FPU reg. stack <- value2, or a asm.emitFLD_Reg_RegDisp_Quad(FP0, SP, TWO_SLOTS); // FPU reg. stack <- value1, or b asm.emitFPREM(); // FPU reg. stack <- a%b asm.emitFSTP_RegDisp_Reg_Quad(SP, TWO_SLOTS, FP0); // POP FPU reg. stack (result) onto java stack asm.emitFSTP_RegInd_Reg_Quad(SP, FP0); // POP FPU reg. stack onto java stack asm.emitADD_Reg_Imm(SP, 2 * WORDSIZE); // shrink the stack } /** * Emit code to implement the dneg bytecode */ @Override protected final void emit_dneg() { if (SSE2_BASE) { asm.emitXORPD_Reg_Reg(XMM0, XMM0); // XMM0 = 0 asm.emitSUBSD_Reg_RegInd(XMM0, SP); // XMM0 -= value asm.emitMOVSD_RegInd_Reg(SP, XMM0); // set result on stack } else { asm.emitFLD_Reg_RegInd_Quad(FP0, SP); // FPU reg. stack <- value1 asm.emitFCHS(); // change sign to stop of FPU stack asm.emitFSTP_RegInd_Reg_Quad(SP, FP0); // POP FPU reg. stack onto stack } } /* * conversion ops */ /** * Emit code to implement the i2l bytecode */ @Override protected final void emit_i2l() { asm.emitPUSH_RegInd(SP); // duplicate int on stack asm.emitSAR_RegDisp_Imm(SP, ONE_SLOT, 31); // sign extend as high word of long } /** * Emit code to implement the l2i bytecode */ @Override protected final void emit_l2i() { asm.emitMOV_Reg_RegInd(T0, SP); // low half of the long asm.emitADD_Reg_Imm(SP, WORDSIZE); // throw away high half of the long asm.emitMOV_RegInd_Reg(SP, T0); } /** * Emit code to implement the i2f bytecode */ @Override protected final 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); } } /** * Emit code to implement the i2d bytecode */ @Override protected final void emit_i2d() { if (SSE2_BASE) { asm.emitCVTSI2SD_Reg_RegInd(XMM0, SP); asm.emitADD_Reg_Imm(SP, -WORDSIZE); // grow the stack asm.emitMOVSD_RegInd_Reg(SP, XMM0); } else { asm.emitFILD_Reg_RegInd(FP0, SP); asm.emitADD_Reg_Imm(SP, -WORDSIZE); // grow the stack asm.emitFSTP_RegInd_Reg_Quad(SP, FP0); } } /** * Emit code to implement the l2f bytecode */ @Override protected final void emit_l2f() { asm.emitFILD_Reg_RegInd_Quad(FP0, SP); asm.emitADD_Reg_Imm(SP, WORDSIZE); // shrink the stack asm.emitFSTP_RegInd_Reg(SP, FP0); } /** * Emit code to implement the l2d bytecode */ @Override protected final void emit_l2d() { asm.emitFILD_Reg_RegInd_Quad(FP0, SP); asm.emitFSTP_RegInd_Reg_Quad(SP, FP0); } /** * Emit code to implement the f2d bytecode */ @Override protected final void emit_f2d() { if (SSE2_BASE) { asm.emitCVTSS2SD_Reg_RegInd(XMM0, SP); asm.emitADD_Reg_Imm(SP, -WORDSIZE); // grow the stack asm.emitMOVSD_RegInd_Reg(SP, XMM0); } else { asm.emitFLD_Reg_RegInd(FP0, SP); asm.emitADD_Reg_Imm(SP, -WORDSIZE); // grow the stack asm.emitFSTP_RegInd_Reg_Quad(SP, FP0); } } /** * Emit code to implement the d2f bytecode */ @Override protected final void emit_d2f() { if (SSE2_BASE) { asm.emitCVTSD2SS_Reg_RegInd(XMM0, SP); asm.emitADD_Reg_Imm(SP, WORDSIZE); // shrink the stack asm.emitMOVSS_RegInd_Reg(SP, XMM0); } else { asm.emitFLD_Reg_RegInd_Quad(FP0, SP); asm.emitADD_Reg_Imm(SP, WORDSIZE); // shrink the stack asm.emitFSTP_RegInd_Reg(SP, FP0); } } /** * Emit code to implement the f2i bytecode */ @Override protected final void emit_f2i() { if (SSE2_BASE) { // Set up max int in XMM0 asm.emitMOVSS_Reg_RegDisp(XMM0, JTOC, VM_Entrypoints.maxintFloatField.getOffset()); // Set up value in XMM1 asm.emitMOVSS_Reg_RegInd(XMM1, SP); // if value > maxint or NaN goto fr1; FP0 = value asm.emitUCOMISS_Reg_Reg(XMM0, XMM1); VM_ForwardReference fr1 = asm.forwardJcc(VM_Assembler.LLE); asm.emitCVTTSS2SI_Reg_Reg(T0, XMM1); asm.emitMOV_RegInd_Reg(SP, T0); VM_ForwardReference fr2 = asm.forwardJMP(); fr1.resolve(asm); VM_ForwardReference fr3 = asm.forwardJcc(VM_Assembler.PE); // if value == NaN goto fr3 asm.emitMOV_RegInd_Imm(SP, 0x7FFFFFFF); VM_ForwardReference fr4 = asm.forwardJMP(); fr3.resolve(asm); asm.emitMOV_RegInd_Imm(SP, 0); fr2.resolve(asm); fr4.resolve(asm); } else { // TODO: use SSE/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_RegDisp(S0, JTOC, VM_Entrypoints.the_boot_recordField.getOffset()); asm.emitCALL_RegDisp(S0, VM_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_RegDisp_Reg(SP, NO_SLOT, T0); } } /** * Emit code to implement the f2l bytecode */ @Override protected final void emit_f2l() { // 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_RegDisp(FP0, JTOC, VM_Entrypoints.maxlongFloatField.getOffset()); // if value > maxlong or NaN goto fr1; FP0 = value asm.emitFUCOMIP_Reg_Reg(FP0, FP1); VM_ForwardReference fr1 = asm.forwardJcc(VM_Assembler.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.emitADD_Reg_Imm(SP, -WORDSIZE); // 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 VM_ForwardReference fr2 = asm.forwardJMP(); fr1.resolve(asm); asm.emitFSTP_Reg_Reg(FP0, FP0); // pop FPU*1 VM_ForwardReference fr3 = asm.forwardJcc(VM_Assembler.PE); // if value == NaN goto fr3 asm.emitMOV_RegInd_Imm(SP, 0x7FFFFFFF); asm.emitPUSH_Imm(-1); VM_ForwardReference fr4 = asm.forwardJMP(); fr3.resolve(asm); asm.emitMOV_RegInd_Imm(SP, 0); asm.emitPUSH_Imm(0); fr2.resolve(asm); fr4.resolve(asm); } /** * Emit code to implement the d2i bytecode */ @Override protected final void emit_d2i() { if (SSE2_BASE) { // Set up max int in XMM0 asm.emitMOVSD_Reg_RegDisp(XMM0, JTOC, VM_Entrypoints.maxintField.getOffset()); // Set up value in XMM1 asm.emitMOVSD_Reg_RegInd(XMM1, SP); asm.emitADD_Reg_Imm(SP, 4); // adjust stack // if value > maxint or NaN goto fr1; FP0 = value asm.emitUCOMISD_Reg_Reg(XMM0, XMM1); VM_ForwardReference fr1 = asm.forwardJcc(VM_Assembler.LLE); asm.emitCVTTSD2SI_Reg_Reg(T0, XMM1); asm.emitMOV_RegInd_Reg(SP, T0); VM_ForwardReference fr2 = asm.forwardJMP(); fr1.resolve(asm); VM_ForwardReference fr3 = asm.forwardJcc(VM_Assembler.PE); // if value == NaN goto fr3 asm.emitMOV_RegInd_Imm(SP, 0x7FFFFFFF); VM_ForwardReference fr4 = asm.forwardJMP(); fr3.resolve(asm); asm.emitMOV_RegInd_Imm(SP, 0); fr2.resolve(asm); fr4.resolve(asm); } else { // TODO: use SSE/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_RegDisp(S0, JTOC, VM_Entrypoints.the_boot_recordField.getOffset()); asm.emitCALL_RegDisp(S0, VM_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 asm.emitPOP_Reg(S0); // shrink stack by 1 word asm.emitMOV_RegDisp_Reg(SP, NO_SLOT, T0); } } /** * Emit code to implement the d2l bytecode */ @Override protected final void emit_d2l() { // 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_RegDisp_Quad(FP0, JTOC, VM_Entrypoints.maxlongField.getOffset()); // if value > maxlong or NaN goto fr1; FP0 = value asm.emitFUCOMIP_Reg_Reg(FP0, FP1); VM_ForwardReference fr1 = asm.forwardJcc(VM_Assembler.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 VM_ForwardReference fr2 = asm.forwardJMP(); fr1.resolve(asm); asm.emitFSTP_Reg_Reg(FP0, FP0); // pop FPU*1 VM_ForwardReference fr3 = asm.forwardJcc(VM_Assembler.PE); // if value == NaN goto fr3 asm.emitMOV_RegDisp_Imm(SP, ONE_SLOT, 0x7FFFFFFF); asm.emitMOV_RegInd_Imm(SP, -1); VM_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); } /** * Emit code to implement the i2b bytecode */ @Override protected final 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); } /** * Emit code to implement the i2c bytecode */ @Override protected final 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); } /** * Emit code to implement the i2s bytecode */ @Override protected final 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 */ /** * Emit code to implement the lcmp bytecode */ @Override protected final void emit_lcmp() { 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 VM_ForwardReference fr1 = asm.forwardJcc(VM_Assembler.LT); asm.emitOR_Reg_Reg(T0, T1); // T0 = T0 | T1 VM_ForwardReference fr2 = asm.forwardJcc(VM_Assembler.NE); asm.emitPUSH_Imm(0); // push result on stack VM_ForwardReference fr3 = asm.forwardJMP(); fr2.resolve(asm); asm.emitPUSH_Imm(1); // push result on stack VM_ForwardReference fr4 = asm.forwardJMP(); fr1.resolve(asm); asm.emitPUSH_Imm(-1); // push result on stack fr3.resolve(asm); fr4.resolve(asm); } /** * Emit code to implement the fcmpl bytecode */ @Override protected final void emit_fcmpl() { if (SSE2_BASE) { asm.emitMOVSS_Reg_RegInd(XMM0, SP); // XMM0 = value2 asm.emitMOVSS_Reg_RegDisp(XMM1, SP, ONE_SLOT); // XMM1 = value1 asm.emitADD_Reg_Imm(SP, 2 * WORDSIZE); // popping the stack asm.emitUCOMISS_Reg_Reg(XMM1, XMM0); // compare value1 and value2 } else { asm.emitFLD_Reg_RegInd(FP0, SP); // Setup value2 into FP1, asm.emitFLD_Reg_RegDisp(FP0, SP, ONE_SLOT); // value1 into FP0 asm.emitADD_Reg_Imm(SP, 2 * WORDSIZE); // popping the stack asm.emitFUCOMIP_Reg_Reg(FP0, FP1); // compare and pop FPU *1 } VM_ForwardReference fr1 = asm.forwardJcc(VM_Assembler.LLT); VM_ForwardReference fr2 = asm.forwardJcc(VM_Assembler.LGT); asm.emitPUSH_Imm(0); // push result on stack VM_ForwardReference fr3 = asm.forwardJMP(); fr2.resolve(asm); asm.emitPUSH_Imm(1); // push result on stack VM_ForwardReference fr4 = asm.forwardJMP(); fr1.resolve(asm); asm.emitPUSH_Imm(-1); // push result on stack fr3.resolve(asm); fr4.resolve(asm); if (!SSE2_BASE) { asm.emitFSTP_Reg_Reg(FP0, FP0); // pop FPU*1 } } /** * Emit code to implement the fcmpg bytecode */ @Override protected final void emit_fcmpg() { if (SSE2_BASE) { asm.emitMOVSS_Reg_RegInd(XMM0, SP); // XMM0 = value2 asm.emitMOVSS_Reg_RegDisp(XMM1, SP, ONE_SLOT); // XMM1 = value1 asm.emitADD_Reg_Imm(SP, 2 * WORDSIZE); // popping the stack asm.emitUCOMISS_Reg_Reg(XMM1, XMM0); // compare value1 and value2 } else { asm.emitFLD_Reg_RegInd(FP0, SP); // Setup value2 into FP1, asm.emitFLD_Reg_RegDisp(FP0, SP, ONE_SLOT); // value1 into FP0 asm.emitADD_Reg_Imm(SP, 2 * WORDSIZE); // popping the stack asm.emitFUCOMIP_Reg_Reg(FP0, FP1); // compare and pop FPU *1 } // TODO: It's bad to have 2 conditional jumps within 16bytes of each other VM_ForwardReference fr1 = asm.forwardJcc(VM_Assembler.LGT); // if > goto push 1 VM_ForwardReference fr3 = asm.forwardJcc(VM_Assembler.NE); // if < goto push -1 VM_ForwardReference fr2 = asm.forwardJcc(VM_Assembler.PE); // if unordered goto push 1 asm.emitPUSH_Imm(0); // push result of 0 on stack VM_ForwardReference fr4 = asm.forwardJMP(); fr1.resolve(asm); fr2.resolve(asm); asm.emitPUSH_Imm(1); // push result of 1 on stack VM_ForwardReference fr5 = asm.forwardJMP(); fr3.resolve(asm); asm.emitPUSH_Imm(-1); // push result of -1 on stack fr4.resolve(asm); fr5.resolve(asm); if (!SSE2_BASE) { asm.emitFSTP_Reg_Reg(FP0, FP0); // pop FPU*1 } } /** * Emit code to implement the dcmpl bytecode */ @Override protected final void emit_dcmpl() { if (SSE2_BASE) { asm.emitMOVSD_Reg_RegInd(XMM0, SP); // XMM0 = value2 asm.emitMOVSD_Reg_RegDisp(XMM1, SP, TWO_SLOTS); // XMM1 = value1 asm.emitADD_Reg_Imm(SP, 4 * WORDSIZE); // popping the stack asm.emitUCOMISD_Reg_Reg(XMM1, XMM0); // compare value1 and value2 } else { asm.emitFLD_Reg_RegInd_Quad(FP0, SP); // Setup value2 into FP1, asm.emitFLD_Reg_RegDisp_Quad(FP0, SP, TWO_SLOTS); // value1 into FP0 asm.emitADD_Reg_Imm(SP, 4 * WORDSIZE); // popping the stack asm.emitFUCOMIP_Reg_Reg(FP0, FP1); // compare and pop FPU *1 } VM_ForwardReference fr1 = asm.forwardJcc(VM_Assembler.LLT); VM_ForwardReference fr2 = asm.forwardJcc(VM_Assembler.LGT); asm.emitPUSH_Imm(0); // push result on stack VM_ForwardReference fr3 = asm.forwardJMP(); fr2.resolve(asm); asm.emitPUSH_Imm(1); // push result on stack VM_ForwardReference fr4 = asm.forwardJMP(); fr1.resolve(asm); asm.emitPUSH_Imm(-1); // push result on stack fr3.resolve(asm); fr4.resolve(asm); if (!SSE2_BASE) { asm.emitFSTP_Reg_Reg(FP0, FP0); // pop FPU*1 } } /** * Emit code to implement the dcmpg bytecode */ @Override protected final void emit_dcmpg() { if (SSE2_BASE) { asm.emitMOVSD_Reg_RegInd(XMM0, SP); // XMM0 = value2 asm.emitMOVSD_Reg_RegDisp(XMM1, SP, TWO_SLOTS); // XMM1 = value1 asm.emitADD_Reg_Imm(SP, 4 * WORDSIZE); // popping the stack asm.emitUCOMISD_Reg_Reg(XMM1, XMM0); // compare value1 and value2 } else { asm.emitFLD_Reg_RegInd_Quad(FP0, SP); // Setup value2 into FP1, asm.emitFLD_Reg_RegDisp_Quad(FP0, SP, TWO_SLOTS); // value1 into FP0 asm.emitADD_Reg_Imm(SP, 4 * WORDSIZE); // popping the stack asm.emitFUCOMIP_Reg_Reg(FP0, FP1); // compare and pop FPU *1 } // TODO: It's bad to have 2 conditional jumps within 16bytes of each other VM_ForwardReference fr1 = asm.forwardJcc(VM_Assembler.LGT); // if > goto push1 VM_ForwardReference fr3 = asm.forwardJcc(VM_Assembler.NE); // if < goto push -1 VM_ForwardReference fr2 = asm.forwardJcc(VM_Assembler.PE); // if unordered goto push 1 asm.emitPUSH_Imm(0); // push result of 0 on stack VM_ForwardReference fr4 = asm.forwardJMP(); fr1.resolve(asm); fr2.resolve(asm); asm.emitPUSH_Imm(1); // push result of 1 on stack VM_ForwardReference fr5 = asm.forwardJMP(); fr3.resolve(asm); asm.emitPUSH_Imm(-1); // push result of -1 on stack fr4.resolve(asm); fr5.resolve(asm); if (!SSE2_BASE) { asm.emitFSTP_Reg_Reg(FP0, FP0); // pop FPU*1 } } /* * branching */ /** * Emit code to implement the ifeg bytecode * @param bTarget target bytecode of the branch */ @Override protected final void emit_ifeq(int bTarget) { asm.emitPOP_Reg(T0); asm.emitTEST_Reg_Reg(T0, T0); genCondBranch(VM_Assembler.EQ, bTarget); } /** * Emit code to implement the ifne bytecode * @param bTarget target bytecode of the branch */ @Override protected final void emit_ifne(int bTarget) { asm.emitPOP_Reg(T0); asm.emitTEST_Reg_Reg(T0, T0); genCondBranch(VM_Assembler.NE, bTarget); } /** * Emit code to implement the iflt bytecode * @param bTarget target bytecode of the branch */ @Override protected final void emit_iflt(int bTarget) { asm.emitPOP_Reg(T0); asm.emitCMP_Reg_Imm(T0, 0); genCondBranch(VM_Assembler.LT, bTarget); } /** * Emit code to implement the ifge bytecode * @param bTarget target bytecode of the branch */ @Override protected final void emit_ifge(int bTarget) { asm.emitPOP_Reg(T0); asm.emitCMP_Reg_Imm(T0, 0); genCondBranch(VM_Assembler.GE, bTarget); } /** * Emit code to implement the ifgt bytecode * @param bTarget target bytecode of the branch */ @Override protected final void emit_ifgt(int bTarget) { asm.emitPOP_Reg(T0); asm.emitCMP_Reg_Imm(T0, 0); genCondBranch(VM_Assembler.GT, bTarget); } /** * Emit code to implement the ifle bytecode * @param bTarget target bytecode of the branch */ @Override protected final void emit_ifle(int bTarget) { asm.emitPOP_Reg(T0); asm.emitCMP_Reg_Imm(T0, 0); genCondBranch(VM_Assembler.LE, bTarget); } /** * Emit code to implement the if_icmpeq bytecode * @param bTarget target bytecode of the branch */ @Override protected final void emit_if_icmpeq(int bTarget) { asm.emitPOP_Reg(S0); asm.emitPOP_Reg(T0); asm.emitCMP_Reg_Reg(T0, S0); genCondBranch(VM_Assembler.EQ, bTarget); } /** * Emit code to implement the if_icmpne bytecode * @param bTarget target bytecode of the branch */ @Override protected final void emit_if_icmpne(int bTarget) { asm.emitPOP_Reg(S0); asm.emitPOP_Reg(T0); asm.emitCMP_Reg_Reg(T0, S0); genCondBranch(VM_Assembler.NE, bTarget); } /** * Emit code to implement the if_icmplt bytecode * @param bTarget target bytecode of the branch */ @Override protected final void emit_if_icmplt(int bTarget) { asm.emitPOP_Reg(S0); asm.emitPOP_Reg(T0); asm.emitCMP_Reg_Reg(T0, S0); genCondBranch(VM_Assembler.LT, bTarget); } /** * Emit code to implement the if_icmpge bytecode * @param bTarget target bytecode of the branch */ @Override protected final void emit_if_icmpge(int bTarget) { asm.emitPOP_Reg(S0); asm.emitPOP_Reg(T0); asm.emitCMP_Reg_Reg(T0, S0); genCondBranch(VM_Assembler.GE, bTarget); } /** * Emit code to implement the if_icmpgt bytecode * @param bTarget target bytecode of the branch */ @Override protected final void emit_if_icmpgt(int bTarget) { asm.emitPOP_Reg(S0); asm.emitPOP_Reg(T0); asm.emitCMP_Reg_Reg(T0, S0); genCondBranch(VM_Assembler.GT, bTarget); } /** * Emit code to implement the if_icmple bytecode * @param bTarget target bytecode of the branch */ @Override protected final void emit_if_icmple(int bTarget) { asm.emitPOP_Reg(S0); asm.emitPOP_Reg(T0); asm.emitCMP_Reg_Reg(T0, S0); genCondBranch(VM_Assembler.LE, bTarget); } /** * Emit code to implement the if_acmpeq bytecode * @param bTarget target bytecode of the branch */ @Override protected final void emit_if_acmpeq(int bTarget) { asm.emitPOP_Reg(S0); asm.emitPOP_Reg(T0); asm.emitCMP_Reg_Reg(T0, S0); genCondBranch(VM_Assembler.EQ, bTarget); } /** * Emit code to implement the if_acmpne bytecode * @param bTarget target bytecode of the branch */ @Override protected final void emit_if_acmpne(int bTarget) { asm.emitPOP_Reg(S0); asm.emitPOP_Reg(T0); asm.emitCMP_Reg_Reg(T0, S0); genCondBranch(VM_Assembler.NE, bTarget); } /** * Emit code to implement the ifnull bytecode * @param bTarget target bytecode of the branch */ @Override protected final void emit_ifnull(int bTarget) { asm.emitPOP_Reg(T0); asm.emitTEST_Reg_Reg(T0, T0); genCondBranch(VM_Assembler.EQ, bTarget); } /** * Emit code to implement the ifnonnull bytecode * @param bTarget target bytecode of the branch */ @Override protected final void emit_ifnonnull(int bTarget) { asm.emitPOP_Reg(T0); asm.emitTEST_Reg_Reg(T0, T0); genCondBranch(VM_Assembler.NE, bTarget); } /** * Emit code to implement the goto and gotow bytecodes * @param bTarget target bytecode of the branch */ @Override protected final void emit_goto(int bTarget) { int mTarget = bytecodeMap[bTarget]; asm.emitJMP_ImmOrLabel(mTarget, bTarget); } /** * Emit code to implement the jsr and jsrw bytecode * @param bTarget target bytecode of the jsr */ @Override protected final void emit_jsr(int bTarget) { int mTarget = bytecodeMap[bTarget]; asm.emitCALL_ImmOrLabel(mTarget, bTarget); } /** * Emit code to implement the ret bytecode * @param index local variable containing the return address */ @Override protected final void emit_ret(int index) { Offset offset = localOffset(index); asm.emitJMP_RegDisp(ESP, offset); } /** * Emit code to implement the tableswitch bytecode * @param defaultval bcIndex of the default target * @param low low value of switch * @param high high value of switch */ @Override protected final 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(T0); // T0 is index of desired case asm.emitSUB_Reg_Imm(T0, low); // relativize T0 asm.emitCMP_Reg_Imm(T0, n); // 0 <= relative index < n if (!VM.runningTool && ((VM_BaselineCompiledMethod) compiledMethod).hasCounterArray()) { int firstCounter = edgeCounterIdx; edgeCounterIdx += (n + 1); // Jump around code for default case VM_ForwardReference fr = asm.forwardJcc(VM_Assembler.LLT); incEdgeCounter(S0, firstCounter + n); asm.emitJMP_ImmOrLabel(mTarget, bTarget); fr.resolve(asm); // Increment counter for the appropriate case incEdgeCounterIdx(S0, T0, firstCounter); } else { asm.emitJCC_Cond_ImmOrLabel(VM_Assembler.LGE, mTarget, bTarget); // if not, goto default case } // make table aligned if doing alignment checking if (VM.AlignmentChecking) { while (((asm.getMachineCodeIndex() + 5) % WORDSIZE) != 0) { asm.emitNOP(); } } asm.emitCALL_Imm(asm.getMachineCodeIndex() + 5 + (n << LG_WORDSIZE)); // jump around table, pushing address of 0th delta for (int i = 0; i < n; i++) { // create table of deltas int offset = bcodes.getTableSwitchOffset(i); bTarget = biStart + offset; mTarget = bytecodeMap[bTarget]; // delta i: difference between address of case i and of delta 0 asm.emitOFFSET_Imm_ImmOrLabel(i, mTarget, bTarget); } bcodes.skipTableSwitchOffsets(n); asm.emitPOP_Reg(S0); // S0 = address of 0th delta asm.emitADD_Reg_RegIdx(S0, S0, T0, VM_Assembler.WORD, NO_SLOT); // S0 += [S0 + T0<<2] asm.emitPUSH_Reg(S0); // push computed case address asm.emitRET(); // goto case } /** * 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 final 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 && ((VM_BaselineCompiledMethod) compiledMethod).hasCounterArray()) { // Flip conditions so we can jump over the increment of the taken counter. VM_ForwardReference fr = asm.forwardJcc(VM_Assembler.NE); incEdgeCounter(S0, edgeCounterIdx++); asm.emitJMP_ImmOrLabel(mTarget, bTarget); fr.resolve(asm); } else { asm.emitJCC_Cond_ImmOrLabel(VM_Assembler.EQ, mTarget, bTarget); } } bcodes.skipLookupSwitchPairs(npairs); int bTarget = biStart + defaultval; int mTarget = bytecodeMap[bTarget]; if (!VM.runningTool && ((VM_BaselineCompiledMethod) compiledMethod).hasCounterArray()) { incEdgeCounter(S0, edgeCounterIdx++); // increment default counter } asm.emitJMP_ImmOrLabel(mTarget, bTarget); } /* * returns (from function; NOT ret) */ /** * Emit code to implement the ireturn bytecode */ @Override protected final void emit_ireturn() { if (method.isSynchronized()) genMonitorExit(); asm.emitPOP_Reg(T0); genEpilogue(4); } /** * Emit code to implement the lreturn bytecode */ @Override protected final void emit_lreturn() { if (method.isSynchronized()) genMonitorExit(); asm.emitPOP_Reg(T1); // low half asm.emitPOP_Reg(T0); // high half genEpilogue(8); } /** * Emit code to implement the freturn bytecode */ @Override protected final void emit_freturn() { if (method.isSynchronized()) genMonitorExit(); if (SSE2_FULL) { asm.emitMOVSS_Reg_RegInd(XMM0, SP); } else { asm.emitFLD_Reg_RegInd(FP0, SP); } asm.emitADD_Reg_Imm(SP, WORDSIZE); // pop the stack genEpilogue(4); } /** * Emit code to implement the dreturn bytecode */ @Override protected final void emit_dreturn() { if (method.isSynchronized()) genMonitorExit(); if (SSE2_FULL) { asm.emitMOVSD_Reg_RegInd(XMM0, SP); } else { asm.emitFLD_Reg_RegInd_Quad(FP0, SP); } asm.emitADD_Reg_Imm(SP, WORDSIZE << 1); // pop the stack genEpilogue(8); } /** * Emit code to implement the areturn bytecode */ @Override protected final void emit_areturn() { if (method.isSynchronized()) genMonitorExit(); asm.emitPOP_Reg(T0); genEpilogue(4); } /** * Emit code to implement the return bytecode */ @Override protected final void emit_return() { if (method.isSynchronized()) genMonitorExit(); genEpilogue(0); } /* * field access */ /** * Emit code to implement a dynamically linked getstatic * @param fieldRef the referenced field */ @Override protected final void emit_unresolved_getstatic(VM_FieldReference fieldRef) { emitDynamicLinkingSequence(T0, fieldRef, true); if (fieldRef.getSize() <= BYTES_IN_INT) { asm.emitPUSH_RegIdx(JTOC, T0, VM_Assembler.BYTE, NO_SLOT); // get static field } else { // field is two words (double or long) if (VM.VerifyAssertions) VM._assert(fieldRef.getSize() == BYTES_IN_LONG); asm.emitPUSH_RegIdx(JTOC, T0, VM_Assembler.BYTE, ONE_SLOT); // get high part asm.emitPUSH_RegIdx(JTOC, T0, VM_Assembler.BYTE, NO_SLOT); // get low part } } /** * Emit code to implement a getstatic * @param fieldRef the referenced field */ @Override protected final void emit_resolved_getstatic(VM_FieldReference fieldRef) { Offset fieldOffset = fieldRef.peekResolvedField(false).getOffset(); if (fieldRef.getSize() <= BYTES_IN_INT) { // field is one word asm.emitPUSH_RegDisp(JTOC, fieldOffset); } else { // field is two words (double or long) if (VM.VerifyAssertions) VM._assert(fieldRef.getSize() == BYTES_IN_LONG); asm.emitPUSH_RegDisp(JTOC, fieldOffset.plus(WORDSIZE)); // get high part asm.emitPUSH_RegDisp(JTOC, fieldOffset); // get low part } } /** * Emit code to implement a dynamically linked putstatic * @param fieldRef the referenced field */ @Override protected final void emit_unresolved_putstatic(VM_FieldReference fieldRef) { emitDynamicLinkingSequence(T0, fieldRef, true); if (MM_Constants.NEEDS_PUTSTATIC_WRITE_BARRIER && fieldRef.getFieldContentsType().isReferenceType()) { VM_Barriers.compilePutstaticBarrier(asm, T0, fieldRef.getId()); asm.emitADD_Reg_Imm(SP, WORDSIZE); } else { if (fieldRef.getSize() <= BYTES_IN_INT) { // field is one word asm.emitPOP_RegIdx(JTOC, T0, VM_Assembler.BYTE, NO_SLOT); } else { // field is two words (double or long) if (VM.VerifyAssertions) VM._assert(fieldRef.getSize() == BYTES_IN_LONG); asm.emitPOP_RegIdx(JTOC, T0, VM_Assembler.BYTE, NO_SLOT); // store low part asm.emitPOP_RegIdx(JTOC, T0, VM_Assembler.BYTE, ONE_SLOT); // store high part } } } /** * Emit code to implement a putstatic * @param fieldRef the referenced field */ @Override protected final void emit_resolved_putstatic(VM_FieldReference fieldRef) { Offset fieldOffset = fieldRef.peekResolvedField(false).getOffset(); if (MM_Constants.NEEDS_PUTSTATIC_WRITE_BARRIER && fieldRef.getFieldContentsType().isReferenceType()) { VM_Barriers.compilePutstaticBarrierImm(asm, fieldOffset, fieldRef.getId()); asm.emitADD_Reg_Imm(SP, WORDSIZE); } else { if (fieldRef.getSize() <= BYTES_IN_INT) { // field is one word asm.emitPOP_RegDisp(JTOC, fieldOffset); } else { // field is two words (double or long) if (VM.VerifyAssertions) VM._assert(fieldRef.getSize() == BYTES_IN_LONG); asm.emitPOP_RegDisp(JTOC, fieldOffset); // store low part asm.emitPOP_RegDisp(JTOC, fieldOffset.plus(WORDSIZE)); // store high part } } } /** * Emit code to implement a dynamically linked getfield * @param fieldRef the referenced field */ @Override protected final void emit_unresolved_getfield(VM_FieldReference fieldRef) { VM_TypeReference fieldType = fieldRef.getFieldContentsType(); emitDynamicLinkingSequence(T0, fieldRef, true); if (fieldType.isReferenceType()) { // 32bit reference load asm.emitMOV_Reg_RegDisp(S0, SP, NO_SLOT); // S0 is object reference asm.emitMOV_Reg_RegIdx(T1, S0, T0, VM_Assembler.BYTE, NO_SLOT); // T1 is field value asm.emitMOV_RegDisp_Reg(SP, NO_SLOT, T1); // replace reference with value on stack } else if (fieldType.isBooleanType()) { // 8bit unsigned load asm.emitMOV_Reg_RegDisp(S0, SP, NO_SLOT); // S0 is object reference asm.emitMOVZX_Reg_RegIdx_Byte(T1, S0, T0, VM_Assembler.BYTE, NO_SLOT); // T1 is field value asm.emitMOV_RegDisp_Reg(SP, NO_SLOT, T1); // replace reference with value on stack } else if (fieldType.isByteType()) { // 8bit signed load asm.emitMOV_Reg_RegDisp(S0, SP, NO_SLOT); // S0 is object reference asm.emitMOVSX_Reg_RegIdx_Byte(T1, S0, T0, VM_Assembler.BYTE, NO_SLOT); // T1 is field value asm.emitMOV_RegDisp_Reg(SP, NO_SLOT, T1); // replace reference with value on stack } else if (fieldType.isShortType()) { // 16bit signed load asm.emitMOV_Reg_RegDisp(S0, SP, NO_SLOT); // S0 is object reference asm.emitMOVSX_Reg_RegIdx_Word(T1, S0, T0, VM_Assembler.BYTE, NO_SLOT); // T1 is field value asm.emitMOV_RegDisp_Reg(SP, NO_SLOT, T1); // replace reference with value on stack } else if (fieldType.isCharType()) { // 16bit unsigned load asm.emitMOV_Reg_RegDisp(S0, SP, NO_SLOT); // S0 is object reference asm.emitMOVZX_Reg_RegIdx_Word(T1, S0, T0, VM_Assembler.BYTE, NO_SLOT); // T1 is field value asm.emitMOV_RegDisp_Reg(SP, NO_SLOT, T1); // replace reference with value on stack } else if (fieldType.isIntType() || fieldType.isFloatType() || fieldType.isWordType()) { // 32bit load asm.emitMOV_Reg_RegDisp(S0, SP, NO_SLOT); // S0 is object reference asm.emitMOV_Reg_RegIdx(T1, S0, T0, VM_Assembler.BYTE, NO_SLOT); // T1 is field value asm.emitMOV_RegDisp_Reg(SP, NO_SLOT, T1); // replace reference with value on stack } else { // 64bit load if (VM.VerifyAssertions) VM._assert(fieldType.isLongType() || fieldType.isDoubleType()); // NB this is a 64bit copy from memory to the stack so implement // as a slightly optimized Intel memory copy using the FPU asm.emitMOV_Reg_RegDisp(S0, SP, NO_SLOT); // S0 is object reference asm.emitSUB_Reg_Imm(SP, WORDSIZE); // adjust stack down one word to hold 64bit value if (SSE2_BASE) { asm.emitMOVQ_Reg_RegIdx(XMM0, S0, T0, VM_Assembler.BYTE, NO_SLOT); // XMM0 is field value asm.emitMOVQ_RegInd_Reg(SP, XMM0); // replace reference with value on stack } else { asm.emitFLD_Reg_RegIdx_Quad(FP0, S0, T0, VM_Assembler.BYTE, NO_SLOT); // FP0 is field value asm.emitFSTP_RegInd_Reg_Quad(SP, FP0); // replace reference with value on stack } } } /** * Emit code to implement a getfield * @param fieldRef the referenced field */ @Override protected final void emit_resolved_getfield(VM_FieldReference fieldRef) { VM_TypeReference fieldType = fieldRef.getFieldContentsType(); Offset fieldOffset = fieldRef.peekResolvedField(false).getOffset(); if (fieldType.isReferenceType()) { // 32bit reference load asm.emitMOV_Reg_RegDisp(S0, SP, NO_SLOT); // S0 is object reference asm.emitMOV_Reg_RegDisp(T0, S0, fieldOffset); // T0 is field value asm.emitMOV_RegDisp_Reg(SP, NO_SLOT, T0); // replace reference with value on stack } else if (fieldType.isBooleanType()) { // 8bit unsigned load asm.emitMOV_Reg_RegDisp(S0, SP, NO_SLOT); // S0 is object reference asm.emitMOVZX_Reg_RegDisp_Byte(T0, S0, fieldOffset); // T0 is field value asm.emitMOV_RegDisp_Reg(SP, NO_SLOT, T0); // replace reference with value on stack } else if (fieldType.isByteType()) { // 8bit signed load asm.emitMOV_Reg_RegDisp(S0, SP, NO_SLOT); // S0 is object reference asm.emitMOVSX_Reg_RegDisp_Byte(T0, S0, fieldOffset); // T0 is field value asm.emitMOV_RegDisp_Reg(SP, NO_SLOT, T0); // replace reference with value on stack } else if (fieldType.isShortType()) { // 16bit signed load asm.emitMOV_Reg_RegDisp(S0, SP, NO_SLOT); // S0 is object reference asm.emitMOVSX_Reg_RegDisp_Word(T0, S0, fieldOffset); // T0 is field value asm.emitMOV_RegDisp_Reg(SP, NO_SLOT, T0); // replace reference with value on stack } else if (fieldType.isCharType()) { // 16bit unsigned load asm.emitMOV_Reg_RegDisp(S0, SP, NO_SLOT); // S0 is object reference asm.emitMOVZX_Reg_RegDisp_Word(T0, S0, fieldOffset); // T0 is field value asm.emitMOV_RegDisp_Reg(SP, NO_SLOT, T0); // replace reference with value on stack } else if (fieldType.isIntType() || fieldType.isFloatType() || fieldType.isWordType()) { // 32bit load asm.emitMOV_Reg_RegDisp(S0, SP, NO_SLOT); // S0 is object reference asm.emitMOV_Reg_RegDisp(T0, S0, fieldOffset); // T0 is field value asm.emitMOV_RegDisp_Reg(SP, NO_SLOT, T0); // replace reference with value on stack } else { // 64bit load if (VM.VerifyAssertions) VM._assert(fieldType.isLongType() || fieldType.isDoubleType()); // NB this is a 64bit copy from memory to the stack so implement // as a slightly optimized Intel memory copy using the FPU asm.emitMOV_Reg_RegDisp(S0, SP, NO_SLOT); // S0 is object reference asm.emitSUB_Reg_Imm(SP, WORDSIZE); // adjust stack down one word to hold 64bit value if (SSE2_BASE) { asm.emitMOVQ_Reg_RegDisp(XMM0, S0, fieldOffset); // XMM0 is field 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 asm.emitFSTP_RegInd_Reg_Quad(SP, FP0); // replace reference with value on stack } } } /** * Emit code to implement a dynamically linked putfield * @param fieldRef the referenced field */ @Override protected final void emit_unresolved_putfield(VM_FieldReference fieldRef) { VM_TypeReference fieldType = fieldRef.getFieldContentsType(); emitDynamicLinkingSequence(T0, fieldRef, true); if (fieldType.isReferenceType()) { // 32bit reference store if (MM_Constants.NEEDS_WRITE_BARRIER) { VM_Barriers.compilePutfieldBarrier(asm, T0, fieldRef.getId()); asm.emitADD_Reg_Imm(SP, WORDSIZE * 2); // complete popping the value and reference } else { asm.emitMOV_Reg_RegDisp(T1, SP, NO_SLOT); // T1 is the value to be stored asm.emitMOV_Reg_RegDisp(S0, SP, ONE_SLOT); // S0 is the object reference asm.emitADD_Reg_Imm(SP, WORDSIZE * 2); // complete popping the value and reference asm.emitMOV_RegIdx_Reg(S0, T0, VM_Assembler.BYTE, NO_SLOT, T1); // [S0+T0] <- T1 } } else if (fieldType.isBooleanType() || fieldType.isByteType()) { // 8bit store asm.emitMOV_Reg_RegDisp(T1, SP, NO_SLOT); // T1 is the value to be stored asm.emitMOV_Reg_RegDisp(S0, SP, ONE_SLOT); // S0 is the object reference asm.emitADD_Reg_Imm(SP, WORDSIZE * 2); // complete popping the value and reference asm.emitMOV_RegIdx_Reg_Byte(S0, T0, VM_Assembler.BYTE, NO_SLOT, T1); // [S0+T0] <- T1 } else if (fieldType.isShortType() || fieldType.isCharType()) { // 16bit store asm.emitMOV_Reg_RegDisp(T1, SP, NO_SLOT); // T1 is the value to be stored asm.emitMOV_Reg_RegDisp(S0, SP, ONE_SLOT); // S0 is the object reference asm.emitADD_Reg_Imm(SP, WORDSIZE * 2); // complete popping the value and reference asm.emitMOV_RegIdx_Reg_Word(S0, T0, VM_Assembler.BYTE, NO_SLOT, T1); // [S0+T0] <- T1 } else if (fieldType.isIntType() || fieldType.isFloatType() || fieldType.isWordType()) { // 32bit store asm.emitMOV_Reg_RegDisp(T1, SP, NO_SLOT); // T1 is the value to be stored asm.emitMOV_Reg_RegDisp(S0, SP, ONE_SLOT); // S0 is the object reference asm.emitADD_Reg_Imm(SP, WORDSIZE * 2); // complete popping the value and reference asm.emitMOV_RegIdx_Reg(S0, T0, VM_Assembler.BYTE, NO_SLOT, T1); // [S0+T0] <- T1 } else { // 64bit store if (VM.VerifyAssertions) VM._assert(fieldType.isLongType() || fieldType.isDoubleType()); // 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, VM_Assembler.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, VM_Assembler.BYTE, NO_SLOT, FP0); // [S0+T0] <- FP0 } asm.emitADD_Reg_Imm(SP, WORDSIZE * 3); // complete popping the values and reference } } /** * Emit code to implement a putfield * @param fieldRef the referenced field */ @Override protected final void emit_resolved_putfield(VM_FieldReference fieldRef) { VM_TypeReference fieldType = fieldRef.getFieldContentsType(); Offset fieldOffset = fieldRef.peekResolvedField(false).getOffset(); VM_Barriers.compileModifyCheck(asm, 4); if (fieldType.isReferenceType()) { // 32bit reference store if (MM_Constants.NEEDS_WRITE_BARRIER) { VM_Barriers.compilePutfieldBarrierImm(asm, fieldOffset, fieldRef.getId()); asm.emitADD_Reg_Imm(SP, WORDSIZE * 2); // complete popping the value and reference } else { asm.emitMOV_Reg_RegDisp(T0, SP, NO_SLOT); // T0 is the value to be stored asm.emitMOV_Reg_RegDisp(S0, SP, ONE_SLOT); // S0 is the object reference asm.emitADD_Reg_Imm(SP, WORDSIZE * 2); // complete popping the value and reference // [S0+fieldOffset] <- T0 asm.emitMOV_RegDisp_Reg(S0, fieldOffset, T0); } } else if (fieldType.isBooleanType() || fieldType.isByteType()) { // 8bit store asm.emitMOV_Reg_RegDisp(T0, SP, NO_SLOT); // T0 is the value to be stored asm.emitMOV_Reg_RegDisp(S0, SP, ONE_SLOT); // S0 is the object reference asm.emitADD_Reg_Imm(SP, WORDSIZE * 2); // complete popping the value and reference // [S0+fieldOffset] <- T0 asm.emitMOV_RegDisp_Reg_Byte(S0, fieldOffset, T0); } else if (fieldType.isShortType() || fieldType.isCharType()) { // 16bit store asm.emitMOV_Reg_RegDisp(T0, SP, NO_SLOT); // T0 is the value to be stored asm.emitMOV_Reg_RegDisp(S0, SP, ONE_SLOT); // S0 is the object reference asm.emitADD_Reg_Imm(SP, WORDSIZE * 2); // complete popping the value and reference // [S0+fieldOffset] <- T0 asm.emitMOV_RegDisp_Reg_Word(S0, fieldOffset, T0); } else if (fieldType.isIntType() || fieldType.isFloatType() || fieldType.isWordType()) { // 32bit store asm.emitMOV_Reg_RegDisp(T0, SP, NO_SLOT); // T0 is the value to be stored asm.emitMOV_Reg_RegDisp(S0, SP, ONE_SLOT); // S0 is the object reference asm.emitADD_Reg_Imm(SP, WORDSIZE * 2); // complete popping the value and reference // [S0+fieldOffset] <- T0 asm.emitMOV_RegDisp_Reg(S0, fieldOffset, T0); } else { // 64bit store if (VM.VerifyAssertions) { VM._assert(fieldType.isLongType() || fieldType.isDoubleType(), "What type is this?" + fieldType); } // 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); } asm.emitADD_Reg_Imm(SP, WORDSIZE * 3); // complete popping the values and reference } } /* * method invocation */ /** * Emit code to implement a dynamically linked invokevirtual * @param methodRef the referenced method */ @Override protected final void emit_unresolved_invokevirtual(VM_MethodReference methodRef) { emitDynamicLinkingSequence(T0, methodRef, true); int methodRefparameterWords = methodRef.getParameterWords() + 1; // +1 for "this" parameter Offset objectOffset = Offset.fromIntZeroExtend(methodRefparameterWords << 2).minus(4); // object offset into stack asm.emitMOV_Reg_RegDisp(T1, SP, objectOffset); // S0 has "this" parameter VM_ObjectModel.baselineEmitLoadTIB(asm, S0, T1, false); asm.emitMOV_Reg_RegIdx(S0, S0, T0, VM_Assembler.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 } /** * Emit code to implement invokevirtual * @param methodRef the referenced method */ @Override protected final void emit_resolved_invokevirtual(VM_MethodReference methodRef) { int methodRefparameterWords = methodRef.getParameterWords() + 1; // +1 for "this" parameter Offset methodRefOffset = methodRef.peekResolvedMethod(false).getOffset(); Offset objectOffset = Offset.fromIntZeroExtend(methodRefparameterWords << 2).minus(WORDSIZE); // object offset into stack asm.emitMOV_Reg_RegDisp(T1, SP, objectOffset); VM_ObjectModel.baselineEmitLoadTIB(asm, S0, T1, false); genParameterRegisterLoad(methodRef, true); asm.emitCALL_RegDisp(S0, methodRefOffset); genResultRegisterUnload(methodRef); } /** * Emit code to implement a dynamically linked invokespecial * @param methodRef The referenced method * @param target The method to invoke */ @Override protected final void emit_resolved_invokespecial(VM_MethodReference methodRef, VM_Method target) { if (target.isObjectInitializer()) { genParameterRegisterLoad(methodRef, true); asm.emitCALL_RegDisp(JTOC, 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.emitMOV_Reg_RegDisp(S0, JTOC, target.getDeclaringClass().getTibOffset()); genParameterRegisterLoad(methodRef, true); asm.emitCALL_RegDisp(S0, methodRefOffset); genResultRegisterUnload(methodRef); } } /** * Emit code to implement invokespecial * @param methodRef the referenced method */ @Override protected final void emit_unresolved_invokespecial(VM_MethodReference methodRef) { emitDynamicLinkingSequence(S0, methodRef, true); genParameterRegisterLoad(methodRef, true); asm.emitCALL_RegIdx(JTOC, S0, VM_Assembler.BYTE, NO_SLOT); // call static method genResultRegisterUnload(methodRef); } /** * Emit code to implement a dynamically linked invokestatic * @param methodRef the referenced method */ @Override protected final void emit_unresolved_invokestatic(VM_MethodReference methodRef) { emitDynamicLinkingSequence(S0, methodRef, true); genParameterRegisterLoad(methodRef, false); asm.emitCALL_RegIdx(JTOC, S0, VM_Assembler.BYTE, NO_SLOT); genResultRegisterUnload(methodRef); } /** * Emit code to implement invokestatic * @param methodRef the referenced method */ @Override protected final void emit_resolved_invokestatic(VM_MethodReference methodRef) { Offset methodOffset = methodRef.peekResolvedMethod(false).getOffset(); genParameterRegisterLoad(methodRef, false); asm.emitCALL_RegDisp(JTOC, methodOffset); genResultRegisterUnload(methodRef); } /** * Emit code to implement the invokeinterface bytecode * @param methodRef the referenced method */ @Override protected final void emit_invokeinterface(VM_MethodReference methodRef) { int count = methodRef.getParameterWords() + 1; // +1 for "this" parameter VM_Method resolvedMethod = null; resolvedMethod = methodRef.peekInterfaceMethod(); // (1) Emit dynamic type checking sequence if required to do so inline. if (VM.BuildForIMTInterfaceInvocation || (VM.BuildForITableInterfaceInvocation && VM.DirectlyIndexedITables)) { 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. asm.emitMOV_Reg_RegDisp(T1, SP, Offset.fromIntZeroExtend((count - 1) << 2)); // "this" object asm.emitPUSH_Imm(methodRef.getId()); // dict id of target VM_ObjectModel.baselineEmitLoadTIB(asm, S0, T1, false); asm.emitPUSH_Reg(S0); genParameterRegisterLoad(2); // pass 2 parameter word asm.emitCALL_RegDisp(JTOC, VM_Entrypoints.unresolvedInvokeinterfaceImplementsTestMethod.getOffset());// check that "this" class implements the interface } else { asm.emitMOV_Reg_RegDisp(T0, JTOC, resolvedMethod.getDeclaringClass().getTibOffset()); // tib of the interface method asm.emitMOV_Reg_RegDisp(T1, SP, Offset.fromIntZeroExtend((count - 1) << 2)); // "this" object asm.emitPUSH_RegDisp(T0, Offset.fromIntZeroExtend(TIB_TYPE_INDEX << 2)); // type of the interface method VM_ObjectModel.baselineEmitLoadTIB(asm, S0, T1, false); asm.emitPUSH_Reg(S0); genParameterRegisterLoad(2); // pass 2 parameter word asm.emitCALL_RegDisp(JTOC, VM_Entrypoints.invokeinterfaceImplementsTestMethod.getOffset());// check that "this" class implements the interface } } } // (2) Emit interface invocation sequence. if (VM.BuildForIMTInterfaceInvocation) { VM_InterfaceMethodSignature sig = VM_InterfaceMethodSignature.findOrCreate(methodRef); // squirrel away signature ID VM_ProcessorLocalState.emitMoveImmToField(asm, VM_ArchEntrypoints.hiddenSignatureIdField.getOffset(), sig.getId()); // T1 = "this" object asm.emitMOV_Reg_RegDisp(T1, SP, Offset.fromIntZeroExtend((count - 1) << 2)); VM_ObjectModel.baselineEmitLoadTIB(asm, S0, T1, false); if (VM.BuildForIndirectIMT) { // Load the IMT Base into S0 asm.emitMOV_Reg_RegDisp(S0, S0, Offset.fromIntZeroExtend(TIB_IMT_TIB_INDEX << 2)); } genParameterRegisterLoad(methodRef, true); asm.emitCALL_RegDisp(S0, sig.getIMTOffset()); // the interface call } else if (VM.BuildForITableInterfaceInvocation && VM.DirectlyIndexedITables && resolvedMethod != null) { VM_Class I = resolvedMethod.getDeclaringClass(); // T1 = "this" object asm.emitMOV_Reg_RegDisp(T1, SP, Offset.fromIntZeroExtend((count - 1) << 2)); VM_ObjectModel.baselineEmitLoadTIB(asm, S0, T1, false); // S0 = iTables asm.emitMOV_Reg_RegDisp(S0, S0, Offset.fromIntZeroExtend(TIB_ITABLES_TIB_INDEX << 2)); // S0 = iTable asm.emitMOV_Reg_RegDisp(S0, S0, Offset.fromIntZeroExtend(I.getInterfaceId() << 2)); genParameterRegisterLoad(methodRef, true); int idx = VM_InterfaceInvocation.getITableIndex(I, methodRef.getName(), methodRef.getDescriptor()); asm.emitCALL_RegDisp(S0, Offset.fromIntZeroExtend(idx << 2)); // the interface call } else { int itableIndex = -1; if (VM.BuildForITableInterfaceInvocation && resolvedMethod != null) { // get the index of the method in the Itable itableIndex = VM_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 asm.emitPUSH_RegDisp(SP, Offset.fromIntZeroExtend((count - 1) << LG_WORDSIZE)); asm.emitPUSH_Imm(methodRefId); // id of method to call genParameterRegisterLoad(2); // pass 2 parameter words // invokeinterface(obj, id) returns address to call asm.emitCALL_RegDisp(JTOC, VM_Entrypoints.invokeInterfaceMethod.getOffset()); asm.emitMOV_Reg_Reg(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 asm.emitMOV_Reg_RegDisp(T0, SP, Offset.fromIntZeroExtend((count - 1) << 2)); VM_ObjectModel.baselineEmitLoadTIB(asm, S0, T0, false); asm.emitPUSH_Reg(S0); asm.emitPUSH_Imm(resolvedMethod.getDeclaringClass().getInterfaceId()); // interface id genParameterRegisterLoad(2); // pass 2 parameter words asm.emitCALL_RegDisp(JTOC, VM_Entrypoints.findItableMethod.getOffset()); // findItableOffset(tib, id) returns iTable asm.emitMOV_Reg_Reg(S0, T0); // S0 has iTable genParameterRegisterLoad(methodRef, true); // the interface call asm.emitCALL_RegDisp(S0, Offset.fromIntZeroExtend(itableIndex << 2)); } } genResultRegisterUnload(methodRef); } /* * other object model functions */ /** * Emit code to allocate a scalar object * @param typeRef the VM_Class to instantiate */ @Override protected final void emit_resolved_new(VM_Class typeRef) { int instanceSize = typeRef.getInstanceSize(); Offset tibOffset = typeRef.getTibOffset(); int whichAllocator = MM_Interface.pickAllocator(typeRef, method); int align = VM_ObjectModel.getAlignment(typeRef); int offset = VM_ObjectModel.getOffsetForAlignment(typeRef); int site = MM_Interface.getAllocationSite(true); asm.emitPUSH_Imm(instanceSize); asm.emitPUSH_RegDisp(JTOC, 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(7); // pass 7 parameter words asm.emitCALL_RegDisp(JTOC, VM_Entrypoints.resolvedNewScalarMethod.getOffset()); asm.emitPUSH_Reg(T0); } /** * Emit code to dynamically link and allocate a scalar object * @param typeRef typeReference to dynamically link & instantiate */ @Override protected final void emit_unresolved_new(VM_TypeReference typeRef) { int site = MM_Interface.getAllocationSite(true); asm.emitPUSH_Imm(typeRef.getId()); asm.emitPUSH_Imm(site); // site genParameterRegisterLoad(2); // pass 2 parameter words asm.emitCALL_RegDisp(JTOC, VM_Entrypoints.unresolvedNewScalarMethod.getOffset()); asm.emitPUSH_Reg(T0); } /** * Emit code to allocate an array * @param array the VM_Array to instantiate */ @Override protected final void emit_resolved_newarray(VM_Array array) { int width = array.getLogElementSize(); Offset tibOffset = array.getTibOffset(); int headerSize = VM_ObjectModel.computeHeaderSize(array); int whichAllocator = MM_Interface.pickAllocator(array, method); int site = MM_Interface.getAllocationSite(true); int align = VM_ObjectModel.getAlignment(array); int offset = VM_ObjectModel.getOffsetForAlignment(array); // count is already on stack- nothing required asm.emitPUSH_Imm(width); // logElementSize asm.emitPUSH_Imm(headerSize); // headerSize asm.emitPUSH_RegDisp(JTOC, tibOffset); // tib asm.emitPUSH_Imm(whichAllocator); // allocator asm.emitPUSH_Imm(align); asm.emitPUSH_Imm(offset); asm.emitPUSH_Imm(site); genParameterRegisterLoad(8); // pass 8 parameter words asm.emitCALL_RegDisp(JTOC, VM_Entrypoints.resolvedNewArrayMethod.getOffset()); asm.emitPUSH_Reg(T0); } /** * Emit code to dynamically link and allocate an array * @param tRef the type reference to dynamically link & instantiate */ @Override protected final void emit_unresolved_newarray(VM_TypeReference tRef) { int site = MM_Interface.getAllocationSite(true); // count is already on stack- nothing required asm.emitPUSH_Imm(tRef.getId()); asm.emitPUSH_Imm(site); // site genParameterRegisterLoad(3); // pass 3 parameter words asm.emitCALL_RegDisp(JTOC, VM_Entrypoints.unresolvedNewArrayMethod.getOffset()); asm.emitPUSH_Reg(T0); } /** * Emit code to allocate a multi-dimensional array * @param typeRef the type reference to instantiate * @param dimensions the number of dimensions */ @Override protected final void emit_multianewarray(VM_TypeReference typeRef, int dimensions) { // 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 VM_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 arays 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(PARAMETERS); asm.emitCALL_RegDisp(JTOC, VM_ArchEntrypoints.newArrayArrayMethod.getOffset()); for (int i = 0; i < dimensions; i++) { asm.emitPOP_Reg(S0); // clear stack of dimensions (todo use and add immediate to do this) } asm.emitPUSH_Reg(T0); // push array ref on stack } /** * Emit code to implement the arraylength bytecode */ @Override protected final void emit_arraylength() { asm.emitMOV_Reg_RegDisp(T0, SP, NO_SLOT); // T0 is array reference asm.emitMOV_Reg_RegDisp(T0, T0, VM_ObjectModel.getArrayLengthOffset()); // T0 is array length asm.emitMOV_RegDisp_Reg(SP, NO_SLOT, T0); // replace reference with length on stack } /** * Emit code to implement the athrow bytecode */ @Override protected final void emit_athrow() { genParameterRegisterLoad(1); // pass 1 parameter word asm.emitCALL_RegDisp(JTOC, VM_Entrypoints.athrowMethod.getOffset()); } /** * Emit code to implement the checkcast bytecode * @param typeRef the LHS type */ @Override protected final void emit_checkcast(VM_TypeReference typeRef) { asm.emitPUSH_RegInd(SP); // duplicate the object ref on the stack asm.emitPUSH_Imm(typeRef.getId()); // VM_TypeReference id. genParameterRegisterLoad(2); // pass 2 parameter words asm.emitCALL_RegDisp(JTOC, VM_Entrypoints.checkcastMethod.getOffset()); // checkcast(obj, type reference id); } /** * Emit code to implement the checkcast bytecode * @param type the LHS type */ @Override protected final void emit_checkcast_resolvedClass(VM_Type type) { asm.emitPUSH_RegInd(SP); // duplicate the object ref on the stack asm.emitPUSH_Imm(type.getId()); // VM_Type id. genParameterRegisterLoad(2); // pass 2 parameter words asm.emitCALL_RegDisp(JTOC, VM_Entrypoints.checkcastResolvedClassMethod.getOffset()); // checkcast(obj, type id) } /** * Emit code to implement the checkcast bytecode * @param type the LHS type */ @Override protected final void emit_checkcast_final(VM_Type type) { asm.emitPUSH_RegInd(SP); // duplicate the object ref on the stack asm.emitPUSH_Imm(type.getTibOffset().toInt()); // JTOC index that identifies klass genParameterRegisterLoad(2); // pass 2 parameter words asm.emitCALL_RegDisp(JTOC, VM_Entrypoints.checkcastFinalMethod.getOffset()); // checkcast(obj, TIB's JTOC offset) } /** * Emit code to implement the instanceof bytecode * @param typeRef the LHS type */ @Override protected final void emit_instanceof(VM_TypeReference typeRef) { asm.emitPUSH_Imm(typeRef.getId()); genParameterRegisterLoad(2); // pass 2 parameter words asm.emitCALL_RegDisp(JTOC, VM_Entrypoints.instanceOfMethod.getOffset()); asm.emitPUSH_Reg(T0); } /** * Emit code to implement the instanceof bytecode * @param type the LHS type */ @Override protected final void emit_instanceof_resolvedClass(VM_Type type) { asm.emitPUSH_Imm(type.getId()); genParameterRegisterLoad(2); // pass 2 parameter words asm.emitCALL_RegDisp(JTOC, VM_Entrypoints.instanceOfResolvedClassMethod.getOffset()); asm.emitPUSH_Reg(T0); } /** * Emit code to implement the instanceof bytecode * @param type the LHS type */ @Override protected final void emit_instanceof_final(VM_Type type) { asm.emitPUSH_Imm(type.getTibOffset().toInt()); genParameterRegisterLoad(2); // pass 2 parameter words asm.emitCALL_RegDisp(JTOC, VM_Entrypoints.instanceOfFinalMethod.getOffset()); asm.emitPUSH_Reg(T0); } /** * Emit code to implement the monitorenter bytecode */ @Override protected final void emit_monitorenter() { genParameterRegisterLoad(1); // pass 1 parameter word asm.emitCALL_RegDisp(JTOC, VM_Entrypoints.lockMethod.getOffset()); } /** * Emit code to implement the monitorexit bytecode */ @Override protected final void emit_monitorexit() { genParameterRegisterLoad(1); // pass 1 parameter word asm.emitCALL_RegDisp(JTOC, VM_Entrypoints.unlockMethod.getOffset()); } @Override protected final void emit_trap(int trapId) { // FIXME } //----------------// // implementation // //----------------// private void genPrologue() { if (shouldPrint) asm.comment("prologue for " + method); if (klass.hasBridgeFromNativeAnnotation()) { // replace the normal prologue with a special prolog VM_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 - (VM_JNICompiler.SAVED_GPRS_FOR_JNI << LG_WORDSIZE); } else { /* 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. */ asm.emitPUSH_RegDisp(PR, VM_ArchEntrypoints.framePointerField.getOffset()); // store caller's frame pointer VM_ProcessorLocalState.emitMoveRegToField(asm, VM_ArchEntrypoints.framePointerField.getOffset(), SP); // establish new frame /* * NOTE: until the end of the prologue SP holds the framepointer. */ asm.emitMOV_RegDisp_Imm(SP, Offset.fromIntSignExtend(STACKFRAME_METHOD_ID_OFFSET), compiledMethod.getId()); // 3rd word of header /* * save registers */ asm.emitMOV_RegDisp_Reg(SP, JTOC_SAVE_OFFSET, JTOC); // save nonvolatile JTOC register asm.emitMOV_RegDisp_Reg(SP, EBX_SAVE_OFFSET, EBX); // save nonvolatile EBX register // establish the JTOC register VM_ProcessorLocalState.emitMoveFieldToReg(asm, JTOC, VM_ArchEntrypoints.jtocField.getOffset()); if (!VM.runningTool && ((VM_BaselineCompiledMethod) compiledMethod).hasCounterArray()) { // use (nonvolatile) EBX to hold base of this methods counter array asm.emitMOV_Reg_RegDisp(EBX, JTOC, VM_Entrypoints.edgeCountersField.getOffset()); asm.emitMOV_Reg_RegDisp(EBX, EBX, getEdgeCounterOffset()); } int savedRegistersSize = SAVED_GPRS << LG_WORDSIZE; // default /* handle "dynamic brige" methods: * save all registers except FP, SP, PR, S0 (scratch), and * JTOC 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; asm.emitMOV_RegDisp_Reg(SP, T0_SAVE_OFFSET, T0); asm.emitMOV_RegDisp_Reg(SP, T1_SAVE_OFFSET, T1); if (SSE2_FULL) { // TODO: Store SSE2 Control word? asm.emitMOVQ_RegDisp_Reg(SP, XMM_SAVE_OFFSET.plus(0), XMM0); asm.emitMOVQ_RegDisp_Reg(SP, XMM_SAVE_OFFSET.plus(8), XMM1); asm.emitMOVQ_RegDisp_Reg(SP, XMM_SAVE_OFFSET.plus(16), XMM2); asm.emitMOVQ_RegDisp_Reg(SP, XMM_SAVE_OFFSET.plus(24), XMM3); savedRegistersSize += XMM_STATE_SIZE; } else { asm.emitFNSAVE_RegDisp(SP, FPU_SAVE_OFFSET); savedRegistersSize += FPU_STATE_SIZE; } } // copy registers to callee's stackframe firstLocalOffset = STACKFRAME_BODY_OFFSET - savedRegistersSize; Offset firstParameterOffset = Offset.fromIntSignExtend((parameterWords << LG_WORDSIZE) + WORDSIZE); genParameterCopy(firstParameterOffset, Offset.fromIntSignExtend(firstLocalOffset)); int emptyStackOffset = firstLocalOffset - (method.getLocalWords() << LG_WORDSIZE) + WORDSIZE; asm.emitADD_Reg_Imm(SP, emptyStackOffset); // 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; } /* * generate stacklimit check */ if (isInterruptible) { // S0<-limit VM_ProcessorLocalState.emitMoveFieldToReg(asm, S0, VM_Entrypoints.activeThreadStackLimitField.getOffset()); asm.emitSUB_Reg_Reg(S0, SP); // space left asm.emitADD_Reg_Imm(S0, method.getOperandWords() << LG_WORDSIZE); // space left after this expression stack asm.emitBranchLikelyNextInstruction(); VM_ForwardReference fr = asm.forwardJcc(VM_Assembler.LT); // Jmp around trap if OK asm.emitINT_Imm(VM_Runtime.TRAP_STACK_OVERFLOW + RVM_TRAP_BASE); // trap fr.resolve(asm); } else { // TODO!! make sure stackframe of uninterruptible method doesn't overflow guard page } if (method.isSynchronized()) genMonitorEnter(); genThreadSwitchTest(VM_Thread.PROLOGUE); } } @Override protected final void emit_deferred_prologue() { if (VM.VerifyAssertions) VM._assert(method.isForOsrSpecialization()); if (isInterruptible) { // S0<-limit VM_ProcessorLocalState.emitMoveFieldToReg(asm, S0, VM_Entrypoints.activeThreadStackLimitField.getOffset()); asm.emitSUB_Reg_Reg(S0, SP); // spa asm.emitADD_Reg_Imm(S0, method.getOperandWords() << LG_WORDSIZE); // spa asm.emitBranchLikelyNextInstruction(); VM_ForwardReference fr = asm.forwardJcc(VM_Assembler.LT); // Jmp around trap if asm.emitINT_Imm(VM_Runtime.TRAP_STACK_OVERFLOW + RVM_TRAP_BASE); // tra 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(VM_Thread.PROLOGUE); } private void genEpilogue(int bytesPopped) { if (klass.hasBridgeFromNativeAnnotation()) { // pop locals and parameters, get to saved GPR's asm.emitADD_Reg_Imm(SP, (this.method.getLocalWords() << LG_WORDSIZE)); VM_JNICompiler.generateEpilogForJNIMethod(asm, this.method); } else if (klass.hasDynamicBridgeAnnotation()) { // we never return from a DynamicBridge frame asm.emitINT_Imm(0xFF); } else { // normal method asm.emitADD_Reg_Imm(SP, fp2spOffset(NO_SLOT).toInt() - bytesPopped); // SP becomes frame pointer asm.emitMOV_Reg_RegDisp(JTOC, SP, JTOC_SAVE_OFFSET); // restore nonvolatile JTOC register asm.emitMOV_Reg_RegDisp(EBX, SP, EBX_SAVE_OFFSET); // restore nonvolatile EBX register asm.emitPOP_RegDisp(PR, VM_ArchEntrypoints.framePointerField.getOffset()); // discard frame asm.emitRET_Imm(parameterWords << LG_WORDSIZE); // return to caller- pop parameters from stack } } private void genMonitorEnter() { if (method.isStatic()) { Offset klassOffset = Offset.fromIntSignExtend(VM_Statics.findOrCreateObjectLiteral(klass.getClassForType())); // push java.lang.Class object for klass asm.emitPUSH_RegDisp(JTOC, klassOffset); } else { // push "this" object asm.emitPUSH_RegDisp(ESP, localOffset(0)); } // pass 1 parameter genParameterRegisterLoad(1); asm.emitCALL_RegDisp(JTOC, VM_Entrypoints.lockMethod.getOffset()); // after this instruction, the method has the monitor lockOffset = asm.getMachineCodeIndex(); } private void genMonitorExit() { if (method.isStatic()) { asm.emitMOV_Reg_RegDisp(T0, JTOC, klass.getTibOffset()); // T0 = tib for klass asm.emitMOV_Reg_RegInd(T0, T0); // T0 = VM_Class for klass asm.emitPUSH_RegDisp(T0, VM_Entrypoints.classForTypeField.getOffset()); // push java.lang.Class object for klass } else { asm.emitPUSH_RegDisp(ESP, localOffset(0)); // push "this" object } genParameterRegisterLoad(1); // pass 1 parameter asm.emitCALL_RegDisp(JTOC, VM_Entrypoints.unlockMethod.getOffset()); } /** * 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 */ private void genBoundsCheck(VM_Assembler asm, byte indexReg, byte arrayRefReg) { if (generateBoundsChecks) { // compare index to array length asm.emitCMP_RegDisp_Reg(arrayRefReg, VM_ObjectModel.getArrayLengthOffset(), indexReg); // Jmp around trap if index is OK asm.emitBranchLikelyNextInstruction(); VM_ForwardReference fr = asm.forwardJcc(VM_Assembler.LGT); // "pass" index param to C trap handler VM_ProcessorLocalState.emitMoveRegToField(asm, VM_ArchEntrypoints.arrayIndexTrapParamField.getOffset(), indexReg); // trap asm.emitINT_Imm(VM_Runtime.TRAP_ARRAY_BOUNDS + RVM_TRAP_BASE); fr.resolve(asm); } } /** * Emit a conditional branch on the given condition and bytecode target. * The caller has just emitted the instruction sequence to set the condition codes. */ private void genCondBranch(byte cond, int bTarget) { int mTarget = bytecodeMap[bTarget]; if (!VM.runningTool && ((VM_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. VM_ForwardReference notTaken = asm.forwardJcc(asm.flipCode(cond)); // Increment taken counter & jump to target incEdgeCounter(T1, entry + VM_EdgeCounts.TAKEN); asm.emitJMP_ImmOrLabel(mTarget, bTarget); // Increment not taken counter notTaken.resolve(asm); incEdgeCounter(T1, entry + VM_EdgeCounts.NOT_TAKEN); } else { asm.emitJCC_Cond_ImmOrLabel(cond, mTarget, bTarget); } } private void incEdgeCounter(byte scratch, int counterIdx) { if (VM.VerifyAssertions) VM._assert(((VM_BaselineCompiledMethod) compiledMethod).hasCounterArray()); asm.emitMOV_Reg_RegDisp(scratch, EBX, Offset.fromIntZeroExtend(counterIdx << 2)); asm.emitADD_Reg_Imm(scratch, 1); // Don't write back result if it would make the counter negative (ie // saturate at 0x7FFFFFFF) VM_ForwardReference fr1 = asm.forwardJcc(VM_Assembler.S); asm.emitMOV_RegDisp_Reg(EBX, Offset.fromIntSignExtend(counterIdx << 2), scratch); fr1.resolve(asm); } private void incEdgeCounterIdx(byte scratch, byte idx, int counterIdx) { if (VM.VerifyAssertions) VM._assert(((VM_BaselineCompiledMethod) compiledMethod).hasCounterArray()); asm.emitMOV_Reg_RegIdx(scratch, EBX, idx, VM_Assembler.WORD, Offset.fromIntZeroExtend(counterIdx << 2)); asm.emitADD_Reg_Imm(scratch, 1); // Don't write back result if it would make the counter negative (ie // saturate at 0x7FFFFFFF) VM_ForwardReference fr1 = asm.forwardJcc(VM_Assembler.S); asm.emitMOV_RegIdx_Reg(EBX, idx, VM_Assembler.WORD, Offset.fromIntSignExtend(counterIdx << 2), scratch); fr1.resolve(asm); } /** * 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 a "helper" method call. * Assumption: no floating-point parameters. * @param params number of parameter words (including "this" if any). */ private void genParameterRegisterLoad(int params) { if (VM.VerifyAssertions) VM._assert(0 < params); if (0 < NUM_PARAMETER_GPRS) { asm.emitMOV_Reg_RegDisp(T0, SP, Offset.fromIntZeroExtend((params - 1) << LG_WORDSIZE)); } if (1 < params && 1 < NUM_PARAMETER_GPRS) { asm.emitMOV_Reg_RegDisp(T1, SP, 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? */ private void genParameterRegisterLoad(VM_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 byte T = T0; // next GPR to get a parameter int params = method.getParameterWords() + (hasThisParam ? 1 : 0); Offset offset = Offset.fromIntZeroExtend((params - 1) << LG_WORDSIZE); // stack offset of first parameter word if (hasThisParam) { if (gpr < NUM_PARAMETER_GPRS) { 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); } for (VM_TypeReference type : method.getParameterTypes()) { if (max == 0) return; // quit looking when all registers are full VM_TypeReference t = type; if (t.isLongType()) { if (gpr < NUM_PARAMETER_GPRS) { asm.emitMOV_Reg_RegDisp(T, SP, 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) { asm.emitMOV_Reg_RegDisp(T, SP, offset.minus(WORDSIZE)); // hi register := lo mem (== lo order word) gpr++; max--; } } offset = offset.minus(2 * WORDSIZE); } else if (t.isFloatType()) { if (fpr < NUM_PARAMETER_FPRS) { if (SSE2_FULL) { asm.emitMOVSS_Reg_RegDisp((byte)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((byte)fpr, SP, offset.minus(WORDSIZE)); } else { asm.emitFLD_Reg_RegDisp_Quad(FP0, SP, offset.minus(WORDSIZE)); } fpr++; max--; } offset = offset.minus(2 * WORDSIZE); } else { // t is object, int, short, char, byte, or boolean if (gpr < NUM_PARAMETER_GPRS) { 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))); } /** * Store parameters into local space of the callee's stackframe. * Taken: srcOffset - offset from frame pointer of first parameter in caller's stackframe. * dstOffset - offset from frame pointer of first local in callee's stackframe * Assumption: although some parameters may be passed in registers, * space for all parameters is layed out in order on the caller's stackframe. */ private void genParameterCopy(Offset srcOffset, Offset dstOffset) { int gpr = 0; // number of general purpose registers unloaded int fpr = 0; // number of floating point registers unloaded byte T = T0; // next GPR to get a parameter if (!method.isStatic()) { // handle "this" parameter if (gpr < NUM_PARAMETER_GPRS) { asm.emitMOV_RegDisp_Reg(SP, dstOffset, T); T = T1; // at most 2 parameters can be passed in general purpose registers gpr++; } else { // no parameters passed in registers asm.emitMOV_Reg_RegDisp(S0, SP, srcOffset); asm.emitMOV_RegDisp_Reg(SP, dstOffset, S0); } srcOffset = srcOffset.minus(WORDSIZE); dstOffset = dstOffset.minus(WORDSIZE); } //KV: todo: This seems not to work //OffsetArray fprOffset = OffsetArray.create(NUM_PARAMETER_FPRS); // to handle floating point parameters in registers 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 for (VM_TypeReference t : method.getParameterTypes()) { if (t.isLongType()) { if (gpr < NUM_PARAMETER_GPRS) { asm.emitMOV_RegDisp_Reg(SP, dstOffset, T); // hi mem := lo register (== hi order word) T = T1; // at most 2 parameters can be passed in general purpose registers gpr++; srcOffset = srcOffset.minus(WORDSIZE); dstOffset = dstOffset.minus(WORDSIZE); if (gpr < NUM_PARAMETER_GPRS) { asm.emitMOV_RegDisp_Reg(SP, dstOffset, T); // lo mem := hi register (== lo order word) gpr++; } else { asm.emitMOV_Reg_RegDisp(S0, SP, srcOffset); // lo mem from caller's stackframe asm.emitMOV_RegDisp_Reg(SP, dstOffset, S0); } } else { asm.emitMOV_Reg_RegDisp(S0, SP, srcOffset); // hi mem from caller's stackframe asm.emitMOV_RegDisp_Reg(SP, dstOffset, S0); srcOffset = srcOffset.minus(WORDSIZE); dstOffset = dstOffset.minus(WORDSIZE); asm.emitMOV_Reg_RegDisp(S0, SP, srcOffset); // lo mem from caller's stackframe asm.emitMOV_RegDisp_Reg(SP, dstOffset, S0); } srcOffset = srcOffset.minus(WORDSIZE); dstOffset = dstOffset.minus(WORDSIZE); } else if (t.isFloatType()) { if (fpr < NUM_PARAMETER_FPRS) { //fprOffset.set(fpr, dstOffset); if (SSE2_FULL) { asm.emitMOVSS_RegDisp_Reg(SP, dstOffset, (byte)fpr); } else { fprOffset[fpr] = dstOffset.toInt(); is32bit[fpr] = true; } fpr++; } else { asm.emitMOV_Reg_RegDisp(S0, SP, srcOffset); asm.emitMOV_RegDisp_Reg(SP, dstOffset, S0); } srcOffset = srcOffset.minus(WORDSIZE); dstOffset = dstOffset.minus(WORDSIZE); } else if (t.isDoubleType()) { if (fpr < NUM_PARAMETER_FPRS) { srcOffset = srcOffset.minus(WORDSIZE); dstOffset = dstOffset.minus(WORDSIZE); //fprOffset.set(fpr, dstOffset); if (SSE2_FULL) { asm.emitMOVSD_RegDisp_Reg(SP, dstOffset, (byte)fpr); } else { fprOffset[fpr] = dstOffset.toInt(); is32bit[fpr] = false; } fpr++; } else { asm.emitMOV_Reg_RegDisp(S0, SP, srcOffset); // hi mem from caller's stackframe asm.emitMOV_RegDisp_Reg(SP, dstOffset, S0); srcOffset = srcOffset.minus(WORDSIZE); dstOffset = dstOffset.minus(WORDSIZE); asm.emitMOV_Reg_RegDisp(S0, SP, srcOffset); // lo mem from caller's stackframe asm.emitMOV_RegDisp_Reg(SP, dstOffset, S0); } srcOffset = srcOffset.minus(WORDSIZE); dstOffset = dstOffset.minus(WORDSIZE); } else { // t is object, int, short, char, byte, or boolean if (gpr < NUM_PARAMETER_GPRS) { asm.emitMOV_RegDisp_Reg(SP, dstOffset, T); T = T1; // at most 2 parameters can be passed in general purpose registers gpr++; } else { asm.emitMOV_Reg_RegDisp(S0, SP, srcOffset); asm.emitMOV_RegDisp_Reg(SP, dstOffset, S0); } srcOffset = srcOffset.minus(WORDSIZE); dstOffset = dstOffset.minus(WORDSIZE); } } if (!SSE2_FULL) { for (int i = fpr - 1; 0 <= i; i--) { // unload the floating point register stack (backwards) if (is32bit[i]) { //asm.emitFSTP_RegDisp_Reg(SP, fprOffset.get(i), FP0); asm.emitFSTP_RegDisp_Reg(SP, Offset.fromIntSignExtend(fprOffset[i]), FP0); } else { //asm.emitFSTP_RegDisp_Reg_Quad(SP, fprOffset.get(i), FP0); asm.emitFSTP_RegDisp_Reg_Quad(SP, Offset.fromIntSignExtend(fprOffset[i]), FP0); } } } } /** * Push return value of method from register to operand stack. */ private void genResultRegisterUnload(VM_MethodReference m) { VM_TypeReference t = m.getReturnType(); if (t.isVoidType()) return; if (t.isLongType()) { asm.emitPUSH_Reg(T0); // high half asm.emitPUSH_Reg(T1); // low half } else if (t.isFloatType()) { asm.emitSUB_Reg_Imm(SP, 4); if (SSE2_FULL) { asm.emitMOVSS_RegInd_Reg(SP, XMM0); } else { asm.emitFSTP_RegInd_Reg(SP, FP0); } } else if (t.isDoubleType()) { asm.emitSUB_Reg_Imm(SP, 8); 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 ?? VM_ProcessorLocalState.emitCompareFieldWithImm(asm, VM_Entrypoints.takeYieldpointField.getOffset(), 0); VM_ForwardReference fr1; if (whereFrom == VM_Thread.PROLOGUE) { // Take yieldpoint if yieldpoint flag is non-zero (either 1 or -1) fr1 = asm.forwardJcc(VM_Assembler.EQ); asm.emitCALL_RegDisp(JTOC, VM_Entrypoints.yieldpointFromPrologueMethod.getOffset()); } else if (whereFrom == VM_Thread.BACKEDGE) { // Take yieldpoint if yieldpoint flag is >0 fr1 = asm.forwardJcc(VM_Assembler.LE); asm.emitCALL_RegDisp(JTOC, VM_Entrypoints.yieldpointFromBackedgeMethod.getOffset()); } else { // EPILOGUE // Take yieldpoint if yieldpoint flag is non-zero (either 1 or -1) fr1 = asm.forwardJcc(VM_Assembler.EQ); asm.emitCALL_RegDisp(JTOC, VM_Entrypoints.yieldpointFromEpilogueMethod.getOffset()); } fr1.resolve(asm); if (VM.BuildForAdaptiveSystem && options.INVOCATION_COUNTERS) { int id = compiledMethod.getId(); VM_InvocationCounts.allocateCounter(id); asm.emitMOV_Reg_RegDisp(ECX, JTOC, VM_AosEntrypoints.invocationCountsField.getOffset()); asm.emitSUB_RegDisp_Imm(ECX, Offset.fromIntZeroExtend(compiledMethod.getId() << 2), 1); VM_ForwardReference notTaken = asm.forwardJcc(VM_Assembler.GT); asm.emitPUSH_Imm(id); genParameterRegisterLoad(1); asm.emitCALL_RegDisp(JTOC, VM_AosEntrypoints.invocationCounterTrippedMethod.getOffset()); notTaken.resolve(asm); } } // Indicate if specified VM_Magic method causes a frame to be created on the runtime stack. // Taken: VM_Method of the magic method being called // Returned: true if method causes a stackframe to be created // public static boolean checkForActualCall(VM_MethodReference methodToBeCalled) { VM_Atom methodName = methodToBeCalled.getName(); return methodName == VM_MagicNames.invokeClassInitializer || methodName == VM_MagicNames.invokeMethodReturningVoid || methodName == VM_MagicNames.invokeMethodReturningInt || methodName == VM_MagicNames.invokeMethodReturningLong || methodName == VM_MagicNames.invokeMethodReturningFloat || methodName == VM_MagicNames.invokeMethodReturningDouble || methodName == VM_MagicNames.invokeMethodReturningObject || methodName == VM_MagicNames.addressArrayCreate; } private boolean genMagic(VM_MethodReference m) { VM_Atom methodName = m.getName(); if (m.getType() == VM_TypeReference.Address || m.getType() == VM_TypeReference.LocalAddress) { // Address magic VM_TypeReference[] types = m.getParameterTypes(); // Loads all take the form: // ..., Address, [Offset] -> ..., Value if (methodName == VM_MagicNames.loadAddress || methodName == VM_MagicNames.loadLocalAddress || methodName == VM_MagicNames.prepareAddress || methodName == VM_MagicNames.prepareObjectReference || methodName == VM_MagicNames.loadWord || methodName == VM_MagicNames.loadObjectReference || methodName == VM_MagicNames.prepareWord || methodName == VM_MagicNames.loadInt || methodName == VM_MagicNames.prepareInt || methodName == VM_MagicNames.loadFloat) { if (types.length == 0) { // No offset asm.emitPOP_Reg(T0); // address asm.emitPUSH_RegInd(T0); // pushes [T0+0] } else { // Load at offset asm.emitPOP_Reg(S0); // offset asm.emitPOP_Reg(T0); // object ref asm.emitPUSH_RegIdx(T0, S0, VM_Assembler.BYTE, NO_SLOT); // pushes [T0+S0] } return true; } if (methodName == VM_MagicNames.loadByte) { if (types.length == 0) { // No offset asm.emitPOP_Reg(T0); // base asm.emitMOVSX_Reg_RegInd_Byte(T0, T0); asm.emitPUSH_Reg(T0); } else { // Load at offset asm.emitPOP_Reg(S0); // offset asm.emitPOP_Reg(T0); // base asm.emitMOVSX_Reg_RegIdx_Byte(T0, T0, S0, VM_Assembler.BYTE, NO_SLOT); // load and sign extend byte [T0+S0] asm.emitPUSH_Reg(T0); } return true; } if (methodName == VM_MagicNames.loadShort) { if (types.length == 0) { // No offset asm.emitPOP_Reg(T0); // base asm.emitMOVSX_Reg_RegInd_Word(T0, T0); asm.emitPUSH_Reg(T0); } else { // Load at offset asm.emitPOP_Reg(S0); // offset asm.emitPOP_Reg(T0); // base asm.emitMOVSX_Reg_RegIdx_Word(T0, T0, S0, VM_Assembler.BYTE, NO_SLOT); // load and sign extend word [T0+S0] asm.emitPUSH_Reg(T0); } return true; } if (methodName == VM_MagicNames.loadChar) { if (types.length == 0) { // No offset asm.emitPOP_Reg(T0); // base asm.emitMOVZX_Reg_RegInd_Word(T0, T0); asm.emitPUSH_Reg(T0); } else { // Load at offset asm.emitPOP_Reg(S0); // offset asm.emitPOP_Reg(T0); // base asm.emitMOVZX_Reg_RegIdx_Word(T0, T0, S0, VM_Assembler.BYTE, NO_SLOT); // load and zero extend word [T0+S0] asm.emitPUSH_Reg(T0); } return true; } if (methodName == VM_MagicNames.prepareLong || methodName == VM_MagicNames.loadLong || methodName == VM_MagicNames.loadDouble) { if (types.length == 0) { // No offset asm.emitPOP_Reg(T0); // base asm.emitPUSH_RegDisp(T0, ONE_SLOT); // pushes [T0+4] asm.emitPUSH_RegInd(T0); // pushes [T0] } else { // Load at offset asm.emitPOP_Reg(S0); // offset asm.emitPOP_Reg(T0); // base asm.emitPUSH_RegIdx(T0, S0, VM_Assembler.BYTE, ONE_SLOT); // pushes [T0+S0+4] asm.emitPUSH_RegIdx(T0, S0, VM_Assembler.BYTE, NO_SLOT); // pushes [T0+S0] } return true; } // Stores all take the form: // ..., Address, [Offset], Value -> ... if (methodName == VM_MagicNames.store) { // Always at least one parameter to a store. // First parameter is the type to be stored. VM_TypeReference storeType = types[0]; if (storeType == VM_TypeReference.Int || storeType == VM_TypeReference.Address || storeType == VM_TypeReference.LocalAddress || storeType == VM_TypeReference.ObjectReference || storeType == VM_TypeReference.Word || storeType == VM_TypeReference.Float) { if (types.length == 1) { // No offset asm.emitPOP_Reg(T0); // value asm.emitPOP_Reg(S0); // address asm.emitMOV_RegInd_Reg(S0, T0); // [S0+0] <- T0 } else { // Store at offset asm.emitPOP_Reg(S0); // offset asm.emitPOP_Reg(T0); // value asm.emitPOP_Reg(T1); // address asm.emitMOV_RegIdx_Reg(T1, S0, VM_Assembler.BYTE, NO_SLOT, T0); // [T1+S0] <- T0 } return true; } if (storeType == VM_TypeReference.Byte) { if (types.length == 1) { // No offset asm.emitPOP_Reg(T0); // value asm.emitPOP_Reg(T1); // base asm.emitMOV_RegInd_Reg_Byte(T1, T0); } else { // Store at offset asm.emitPOP_Reg(S0); // offset asm.emitPOP_Reg(T0); // value asm.emitPOP_Reg(T1); // base asm.emitMOV_RegIdx_Reg_Byte(T1, S0, VM_Assembler.BYTE, NO_SLOT, T0); // [T1+S0] <- (byte) T0 } return true; } if (storeType == VM_TypeReference.Short || storeType == VM_TypeReference.Char) { if (types.length == 1) { // No offset asm.emitPOP_Reg(T0); // value asm.emitPOP_Reg(T1); // base asm.emitMOV_RegInd_Reg_Word(T1, T0); } else { // Store at offset asm.emitPOP_Reg(S0); // offset asm.emitPOP_Reg(T0); // value asm.emitPOP_Reg(T1); // base asm.emitMOV_RegIdx_Reg_Word(T1, S0, VM_Assembler.BYTE, NO_SLOT, T0); // [T1+S0] <- (word) T0 } return true; } if (storeType == VM_TypeReference.Double || storeType == VM_TypeReference.Long) { if (types.length == 1) { // No offset asm.emitMOV_Reg_RegInd(T0, SP); // value high asm.emitMOV_Reg_RegDisp(T1, SP, TWO_SLOTS); // base asm.emitMOV_RegInd_Reg(T1, T0); // [T1] <- T0 asm.emitMOV_Reg_RegDisp(T0, SP, ONE_SLOT); // value low asm.emitMOV_RegDisp_Reg(T1, ONE_SLOT, T0); // [T1+4] <- T0 asm.emitADD_Reg_Imm(SP, WORDSIZE * 3); // pop stack locations } else { // Store at offset asm.emitMOV_Reg_RegDisp(T0, SP, ONE_SLOT); // value high asm.emitMOV_Reg_RegInd(S0, SP); // offset asm.emitMOV_Reg_RegDisp(T1, SP, THREE_SLOTS); // base asm.emitMOV_RegIdx_Reg(T1, S0, VM_Assembler.BYTE, NO_SLOT, T0); // [T1+S0] <- T0 asm.emitMOV_Reg_RegDisp(T0, SP, TWO_SLOTS); // value low asm.emitMOV_RegIdx_Reg(T1, S0, VM_Assembler.BYTE, ONE_SLOT, T0); // [T1+S0+4] <- T0 asm.emitADD_Reg_Imm(SP, WORDSIZE * 4); // pop stack locations } return true; } } else if (methodName == VM_MagicNames.attempt) { // All attempts are similar 32 bit values. if (types.length == 3) { // Offset passed asm.emitPOP_Reg(S0); // S0 = offset } asm.emitPOP_Reg(T1); // newVal asm.emitPOP_Reg(EAX); // oldVal (EAX is implicit arg to LCMPX if (types.length == 3) { asm.emitADD_Reg_RegInd(S0, SP); // S0 += base } else { // No offset asm.emitMOV_Reg_RegInd(S0, SP); // S0 = base } asm.emitLockNextInstruction(); asm.emitCMPXCHG_RegInd_Reg(S0, T1); // atomic compare-and-exchange asm.emitMOV_RegInd_Imm(SP, 1); // 'push' true (overwriting base) asm.emitBranchLikelyNextInstruction(); VM_ForwardReference fr = asm.forwardJcc(VM_Assembler.EQ); // skip if compare fails asm.emitMOV_RegInd_Imm(SP, 0); // 'push' false (overwriting base) fr.resolve(asm); return true; } } if (methodName == VM_MagicNames.attemptInt || methodName == VM_MagicNames.attemptObject || methodName == VM_MagicNames.attemptAddress || methodName == VM_MagicNames.attemptWord) { // attempt gets called with four arguments: base, offset, oldVal, newVal // returns ([base+offset] == oldVal) // if ([base+offset] == oldVal) [base+offset] := newVal // (operation on memory is atomic) asm.emitPOP_Reg(T1); // newVal asm.emitPOP_Reg(EAX); // oldVal (EAX is implicit arg to LCMPXCNG asm.emitPOP_Reg(S0); // S0 = offset asm.emitADD_Reg_RegInd(S0, SP); // S0 += base asm.emitLockNextInstruction(); asm.emitCMPXCHG_RegInd_Reg(S0, T1); // atomic compare-and-exchange asm.emitMOV_RegInd_Imm(SP, 1); // 'push' true (overwriting base) asm.emitBranchLikelyNextInstruction(); VM_ForwardReference fr = asm.forwardJcc(VM_Assembler.EQ); // skip if compare fails asm.emitMOV_RegInd_Imm(SP, 0); // 'push' false (overwriting base) fr.resolve(asm); return true; } if (methodName == VM_MagicNames.attemptLong) { // attempt gets called with four arguments: base, offset, oldVal, newVal // returns ([base+offset] == oldVal) // if ([base+offset] == oldVal) [base+offset] := newVal // (operation on memory is atomic) //t1:t0 with s0:ebx asm.emitMOV_Reg_RegDisp(T1, SP, THREE_SLOTS); asm.emitMOV_Reg_RegDisp(T0, SP, TWO_SLOTS); // T1:T0 (EDX:EAX) -> oldVal asm.emitMOV_RegDisp_Reg(SP, THREE_SLOTS, EBX); // Save EBX asm.emitMOV_RegDisp_Reg(SP, TWO_SLOTS, ESI); // Save ESI asm.emitMOV_Reg_RegInd(EBX, SP); asm.emitMOV_Reg_RegDisp(S0, SP, ONE_SLOT); // S0:EBX (ECX:EBX) -> newVal asm.emitMOV_Reg_RegDisp(ESI, SP, FIVE_SLOTS); // ESI := base asm.emitADD_Reg_RegDisp(ESI, SP, FOUR_SLOTS); // ESI += offset asm.emitLockNextInstruction(); asm.emitCMPXCHG8B_RegInd(ESI); // atomic compare-and-exchange VM_ForwardReference fr1 = asm.forwardJcc(VM_Assembler.NE); // skip if compare fails asm.emitMOV_RegDisp_Imm(SP, FIVE_SLOTS, 1); // 'push' true (overwriting base) VM_ForwardReference fr2 = asm.forwardJMP(); // skip if compare fails fr1.resolve(asm); asm.emitMOV_RegDisp_Imm(SP, FIVE_SLOTS, 0); // 'push' false (overwriting base) fr2.resolve(asm); asm.emitMOV_Reg_RegDisp(EBX, SP, THREE_SLOTS); // Restore EBX asm.emitMOV_Reg_RegDisp(ESI, SP, TWO_SLOTS); // Restore ESI asm.emitADD_Reg_Imm(SP, WORDSIZE*5); // adjust SP popping the 4 args (6 slots) and pushing the result return true; } if (methodName == VM_MagicNames.saveThreadState) { Offset offset = VM_ArchEntrypoints.saveThreadStateInstructionsField.getOffset(); genParameterRegisterLoad(1); // pass 1 parameter word asm.emitCALL_RegDisp(JTOC, offset); return true; } if (methodName == VM_MagicNames.threadSwitch) { Offset offset = VM_ArchEntrypoints.threadSwitchInstructionsField.getOffset(); genParameterRegisterLoad(2); // pass 2 parameter words asm.emitCALL_RegDisp(JTOC, offset); return true; } if (methodName == VM_MagicNames.restoreHardwareExceptionState) { Offset offset = VM_ArchEntrypoints.restoreHardwareExceptionStateInstructionsField.getOffset(); genParameterRegisterLoad(1); // pass 1 parameter word asm.emitCALL_RegDisp(JTOC, offset); return true; } if (methodName == VM_MagicNames.invokeClassInitializer) { asm.emitPOP_Reg(S0); asm.emitCALL_Reg(S0); // call address just popped return true; } if (m.isSysCall()) { VM_TypeReference[] args = m.getParameterTypes(); VM_TypeReference rtype = m.getReturnType(); Offset offsetToJavaArg = THREE_SLOTS; // the three regs saved in (2) 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) Push args to target function (reversed), except for the first argument for (int i = args.length - 1; i >= 1; i--) { VM_TypeReference arg = args[i]; if (arg.isLongType() || arg.isDoubleType()) { asm.emitPUSH_RegDisp(SP, offsetToJavaArg.plus(4)); asm.emitPUSH_RegDisp(SP, offsetToJavaArg.plus(4)); offsetToJavaArg = offsetToJavaArg.plus(16); paramBytes += 8; } else { asm.emitPUSH_RegDisp(SP, offsetToJavaArg); offsetToJavaArg = offsetToJavaArg.plus(8); paramBytes += 4; } } // (3) invoke target function with address given by the first argument asm.emitMOV_Reg_RegDisp(S0, SP, offsetToJavaArg); asm.emitCALL_Reg(S0); // (4) pop space for arguments asm.emitADD_Reg_Imm(SP, paramBytes); // (5) restore RVM registers asm.emitPOP_Reg(EDI); asm.emitPOP_Reg(ESI); asm.emitPOP_Reg(EBX); // (6) pop expression stack (including the first parameter) asm.emitADD_Reg_Imm(SP, paramBytes + 4); // (7) push return value if (rtype.isLongType()) { asm.emitPUSH_Reg(T1); asm.emitPUSH_Reg(T0); } else if (rtype.isDoubleType()) { asm.emitSUB_Reg_Imm(SP, 8); asm.emitFSTP_RegInd_Reg_Quad(SP, FP0); } else if (rtype.isFloatType()) { asm.emitSUB_Reg_Imm(SP, 4); asm.emitFSTP_RegInd_Reg(SP, FP0); } else if (!rtype.isVoidType()) { asm.emitPUSH_Reg(T0); } return true; } if (methodName == VM_MagicNames.getFramePointer) { asm.emitLEA_Reg_RegDisp(S0, SP, fp2spOffset(NO_SLOT)); asm.emitPUSH_Reg(S0); return true; } if (methodName == VM_MagicNames.getCallerFramePointer) { asm.emitPOP_Reg(T0); // Callee FP asm.emitPUSH_RegDisp(T0, Offset.fromIntSignExtend(STACKFRAME_FRAME_POINTER_OFFSET)); // Caller FP return true; } if (methodName == VM_MagicNames.setCallerFramePointer) { asm.emitPOP_Reg(T0); // value asm.emitPOP_Reg(S0); // fp asm.emitMOV_RegDisp_Reg(S0, Offset.fromIntSignExtend(STACKFRAME_FRAME_POINTER_OFFSET), T0); // [S0+SFPO] <- T0 return true; } if (methodName == VM_MagicNames.getCompiledMethodID) { asm.emitPOP_Reg(T0); // Callee FP asm.emitPUSH_RegDisp(T0, Offset.fromIntSignExtend(STACKFRAME_METHOD_ID_OFFSET)); // Callee CMID return true; } if (methodName == VM_MagicNames.setCompiledMethodID) { asm.emitPOP_Reg(T0); // value asm.emitPOP_Reg(S0); // fp asm.emitMOV_RegDisp_Reg(S0, Offset.fromIntSignExtend(STACKFRAME_METHOD_ID_OFFSET), T0); // [S0+SMIO] <- T0 return true; } if (methodName == VM_MagicNames.getReturnAddressLocation) { asm.emitPOP_Reg(T0); // Callee FP asm.emitADD_Reg_Imm(T0, STACKFRAME_RETURN_ADDRESS_OFFSET); // location containing callee return address asm.emitPUSH_Reg(T0); // Callee return address return true; } if (methodName == VM_MagicNames.getTocPointer || methodName == VM_MagicNames.getJTOC) { asm.emitPUSH_Reg(JTOC); return true; } // get the processor register (PR) if (methodName == VM_MagicNames.getProcessorRegister) { asm.emitPUSH_Reg(PR); return true; } // set the processor register (PR) if (methodName == VM_MagicNames.setProcessorRegister) { asm.emitPOP_Reg(PR); return true; } // Get the value in ESI if (methodName == VM_MagicNames.getESIAsProcessor) { asm.emitPUSH_Reg(ESI); return true; } // Set the value in ESI if (methodName == VM_MagicNames.setESIAsProcessor) { asm.emitPOP_Reg(ESI); return true; } // return false (IA is a main arch) if (methodName == VM_MagicNames.runningOnSubArch) { asm.emitPUSH_Imm(0); return true; } if (methodName == VM_MagicNames.getIntAtOffset || methodName == VM_MagicNames.getWordAtOffset || methodName == VM_MagicNames.getObjectAtOffset || methodName == VM_MagicNames.getObjectArrayAtOffset || methodName == VM_MagicNames.prepareInt || methodName == VM_MagicNames.prepareObject || methodName == VM_MagicNames.prepareAddress || methodName == VM_MagicNames.prepareWord) { asm.emitPOP_Reg(T0); // object ref asm.emitPOP_Reg(S0); // offset asm.emitPUSH_RegIdx(T0, S0, VM_Assembler.BYTE, NO_SLOT); // pushes [T0+S0] return true; } if (methodName == VM_MagicNames.getUnsignedByteAtOffset) { asm.emitPOP_Reg(T0); // object ref asm.emitPOP_Reg(S0); // offset asm.emitMOVZX_Reg_RegIdx_Byte(T0, T0, S0, VM_Assembler.BYTE, NO_SLOT); // load and zero extend byte [T0+S0] asm.emitPUSH_Reg(T0); return true; } if (methodName == VM_MagicNames.getByteAtOffset) { asm.emitPOP_Reg(T0); // object ref asm.emitPOP_Reg(S0); // offset asm.emitMOVSX_Reg_RegIdx_Byte(T0, T0, S0, VM_Assembler.BYTE, NO_SLOT); // load and zero extend byte [T0+S0] asm.emitPUSH_Reg(T0); return true; } if (methodName == VM_MagicNames.getCharAtOffset) { asm.emitPOP_Reg(T0); // object ref asm.emitPOP_Reg(S0); // offset asm.emitMOVZX_Reg_RegIdx_Word(T0, T0, S0, VM_Assembler.BYTE, NO_SLOT); // load and zero extend char [T0+S0] asm.emitPUSH_Reg(T0); return true; } if (methodName == VM_MagicNames.getShortAtOffset) { asm.emitPOP_Reg(T0); // object ref asm.emitPOP_Reg(S0); // offset asm.emitMOVSX_Reg_RegIdx_Word(T0, T0, S0, VM_Assembler.BYTE, NO_SLOT); // load and zero extend char [T0+S0] asm.emitPUSH_Reg(T0); return true; } if (methodName == VM_MagicNames.setIntAtOffset || methodName == VM_MagicNames.setWordAtOffset || methodName == VM_MagicNames.setObjectAtOffset) { if (m.getParameterTypes().length == 4) { // discard locationMetadata parameter asm.emitPOP_Reg(T0); // locationMetadata, not needed by baseline compiler. } asm.emitPOP_Reg(T0); // value asm.emitPOP_Reg(S0); // offset asm.emitPOP_Reg(T1); // obj ref asm.emitMOV_RegIdx_Reg(T1, S0, VM_Assembler.BYTE, NO_SLOT, T0); // [T1+S0] <- T0 return true; } if (methodName == VM_MagicNames.setByteAtOffset) { asm.emitPOP_Reg(T0); // value asm.emitPOP_Reg(S0); // offset asm.emitPOP_Reg(T1); // obj ref asm.emitMOV_RegIdx_Reg_Byte(T1, S0, VM_Assembler.BYTE, NO_SLOT, T0); // [T1+S0] <- (byte) T0 return true; } if (methodName == VM_MagicNames.setCharAtOffset) { asm.emitPOP_Reg(T0); // value asm.emitPOP_Reg(S0); // offset asm.emitPOP_Reg(T1); // obj ref asm.emitMOV_RegIdx_Reg_Word(T1, S0, VM_Assembler.BYTE, NO_SLOT, T0); // [T1+S0] <- (char) T0 return true; } if (methodName == VM_MagicNames.prepareLong || methodName == VM_MagicNames.getLongAtOffset || methodName == VM_MagicNames.getDoubleAtOffset) { asm.emitPOP_Reg(T0); // object ref asm.emitPOP_Reg(S0); // offset asm.emitPUSH_RegIdx(T0, S0, VM_Assembler.BYTE, ONE_SLOT); // pushes [T0+S0+4] asm.emitPUSH_RegIdx(T0, S0, VM_Assembler.BYTE, NO_SLOT); // pushes [T0+S0] return true; } if (methodName == VM_MagicNames.setLongAtOffset || methodName == VM_MagicNames.setDoubleAtOffset) { asm.emitMOV_Reg_RegInd(T0, SP); // value high asm.emitMOV_Reg_RegDisp(S0, SP, TWO_SLOTS); // offset asm.emitMOV_Reg_RegDisp(T1, SP, THREE_SLOTS); // obj ref asm.emitMOV_RegIdx_Reg(T1, S0, VM_Assembler.BYTE, NO_SLOT, T0); // [T1+S0] <- T0 asm.emitMOV_Reg_RegDisp(T0, SP, ONE_SLOT); // value low asm.emitMOV_RegIdx_Reg(T1, S0, VM_Assembler.BYTE, ONE_SLOT, T0); // [T1+S0+4] <- T0 asm.emitADD_Reg_Imm(SP, WORDSIZE * 4); // pop stack locations return true; } if (methodName == VM_MagicNames.addressArrayCreate) { // no resolution problem possible. emit_resolved_newarray(m.getType().resolve(false).asArray()); return true; } if (methodName == VM_MagicNames.addressArrayLength) { emit_arraylength(); // argument order already correct return true; } if (methodName == VM_MagicNames.addressArrayGet) { if (m.getType() == VM_TypeReference.CodeArray) { emit_baload(); } else { if (VM.BuildFor32Addr) { emit_iaload(); } else if (VM.BuildFor64Addr) { emit_laload(); } else { VM._assert(NOT_REACHED); } } return true; } if (methodName == VM_MagicNames.addressArraySet) { if (m.getType() == VM_TypeReference.CodeArray) { emit_bastore(); } else { if (VM.BuildFor32Addr) { emit_iastore(); } else if (VM.BuildFor64Addr) { VM._assert(false); // not implemented } else { VM._assert(NOT_REACHED); } } return true; } if (methodName == VM_MagicNames.getMemoryInt || methodName == VM_MagicNames.getMemoryWord || methodName == VM_MagicNames.getMemoryAddress) { asm.emitPOP_Reg(T0); // address asm.emitPUSH_RegInd(T0); // pushes [T0+0] return true; } if (methodName == VM_MagicNames.setMemoryInt || methodName == VM_MagicNames.setMemoryWord) { if (m.getParameterTypes().length == 3) { // discard locationMetadata parameter asm.emitPOP_Reg(T0); // locationMetadata, not needed by baseline compiler. } asm.emitPOP_Reg(T0); // value asm.emitPOP_Reg(S0); // address asm.emitMOV_RegInd_Reg(S0, T0); // [S0+0] <- T0 return true; } if (methodName == VM_MagicNames.objectAsAddress || methodName == VM_MagicNames.objectAsLocalAddress || methodName == VM_MagicNames.addressAsByteArray || methodName == VM_MagicNames.addressAsObject || methodName == VM_MagicNames.localAddressAsObject || methodName == VM_MagicNames.addressAsObjectArray || methodName == VM_MagicNames.objectAsType || methodName == VM_MagicNames.objectAsShortArray || methodName == VM_MagicNames.objectAsIntArray || methodName == VM_MagicNames.objectAsProcessor || methodName == VM_MagicNames.objectAsThread || methodName == VM_MagicNames.threadAsCollectorThread || methodName == VM_MagicNames.floatAsIntBits || methodName == VM_MagicNames.intBitsAsFloat || methodName == VM_MagicNames.doubleAsLongBits || methodName == VM_MagicNames.longBitsAsDouble || methodName == VM_MagicNames.addressAsLocalAddress || methodName == VM_MagicNames.localAddressAsAddress || methodName == VM_MagicNames.addressArrayGetBacking) { // no-op (a type change, not a representation change) return true; } // code for VM_Type VM_Magic.getObjectType(Object object) if (methodName == VM_MagicNames.getObjectType) { asm.emitPOP_Reg(T0); // object ref VM_ObjectModel.baselineEmitLoadTIB(asm, S0, T0, false); asm.emitPUSH_RegDisp(S0, Offset.fromIntZeroExtend(TIB_TYPE_INDEX << LG_WORDSIZE)); // push VM_Type slot of TIB return true; } if (methodName == VM_MagicNames.getArrayLength) { asm.emitPOP_Reg(T0); // object ref asm.emitPUSH_RegDisp(T0, VM_ObjectModel.getArrayLengthOffset()); return true; } if (methodName == VM_MagicNames.sync) { // nothing required on IA32 return true; } if (methodName == VM_MagicNames.isync) { // nothing required on IA32 return true; } // baseline compiled invocation only: all paramaters on the stack // hi mem // Code // GPRs // FPRs // Spills // low-mem if (methodName == VM_MagicNames.invokeMethodReturningVoid) { Offset offset = VM_ArchEntrypoints.reflectiveMethodInvokerInstructionsField.getOffset(); genParameterRegisterLoad(5); // pass 5 parameter words asm.emitCALL_RegDisp(JTOC, offset); return true; } if (methodName == VM_MagicNames.invokeMethodReturningInt) { Offset offset = VM_ArchEntrypoints.reflectiveMethodInvokerInstructionsField.getOffset(); genParameterRegisterLoad(5); // pass 5 parameter words asm.emitCALL_RegDisp(JTOC, offset); asm.emitPUSH_Reg(T0); return true; } if (methodName == VM_MagicNames.invokeMethodReturningLong) { Offset offset = VM_ArchEntrypoints.reflectiveMethodInvokerInstructionsField.getOffset(); genParameterRegisterLoad(5); // pass 5 parameter words asm.emitCALL_RegDisp(JTOC, offset); asm.emitPUSH_Reg(T0); // high half asm.emitPUSH_Reg(T1); // low half return true; } if (methodName == VM_MagicNames.invokeMethodReturningFloat) { Offset offset = VM_ArchEntrypoints.reflectiveMethodInvokerInstructionsField.getOffset(); genParameterRegisterLoad(5); // pass 5 parameter words asm.emitCALL_RegDisp(JTOC, offset); asm.emitSUB_Reg_Imm(SP, 4); if (SSE2_FULL) { asm.emitMOVSS_RegInd_Reg(SP, XMM0); } else { asm.emitFSTP_RegInd_Reg(SP, FP0); } return true; } if (methodName == VM_MagicNames.invokeMethodReturningDouble) { Offset offset = VM_ArchEntrypoints.reflectiveMethodInvokerInstructionsField.getOffset(); genParameterRegisterLoad(5); // pass 5 parameter words asm.emitCALL_RegDisp(JTOC, offset); asm.emitSUB_Reg_Imm(SP, 8); if (SSE2_FULL) { asm.emitMOVSD_RegInd_Reg(SP, XMM0); } else { asm.emitFSTP_RegInd_Reg_Quad(SP, FP0); } return true; } if (methodName == VM_MagicNames.invokeMethodReturningObject) { Offset offset = VM_ArchEntrypoints.reflectiveMethodInvokerInstructionsField.getOffset(); genParameterRegisterLoad(5); // pass 5 parameter words asm.emitCALL_RegDisp(JTOC, offset); asm.emitPUSH_Reg(T0); return true; } // baseline invocation // one paramater, on the stack -- actual code if (methodName == VM_MagicNames.dynamicBridgeTo) { if (VM.VerifyAssertions) VM._assert(klass.hasDynamicBridgeAnnotation()); // save the branch address for later asm.emitPOP_Reg(S0); // S0<-code address asm.emitADD_Reg_Imm(SP, fp2spOffset(NO_SLOT).toInt() - 4); // just popped 4 bytes above. if (SSE2_FULL) { // TODO: Restore SSE2 Control word? asm.emitMOVQ_Reg_RegDisp(XMM0, SP, XMM_SAVE_OFFSET.plus(0)); asm.emitMOVQ_Reg_RegDisp(XMM1, SP, XMM_SAVE_OFFSET.plus(8)); asm.emitMOVQ_Reg_RegDisp(XMM2, SP, XMM_SAVE_OFFSET.plus(16)); asm.emitMOVQ_Reg_RegDisp(XMM3, SP, XMM_SAVE_OFFSET.plus(24)); } else { // restore FPU state asm.emitFRSTOR_RegDisp(SP, FPU_SAVE_OFFSET); } // restore GPRs asm.emitMOV_Reg_RegDisp(T0, SP, T0_SAVE_OFFSET); asm.emitMOV_Reg_RegDisp(T1, SP, T1_SAVE_OFFSET); asm.emitMOV_Reg_RegDisp(EBX, SP, EBX_SAVE_OFFSET); asm.emitMOV_Reg_RegDisp(JTOC, SP, JTOC_SAVE_OFFSET); // pop frame asm.emitPOP_RegDisp(PR, VM_ArchEntrypoints.framePointerField.getOffset()); // FP<-previous FP // branch asm.emitJMP_Reg(S0); return true; } if (methodName == VM_MagicNames.returnToNewStack) { // SP gets frame pointer for new stack asm.emitPOP_Reg(SP); // restore nonvolatile registers asm.emitMOV_Reg_RegDisp(JTOC, SP, JTOC_SAVE_OFFSET); asm.emitMOV_Reg_RegDisp(EBX, SP, EBX_SAVE_OFFSET); // discard current stack frame asm.emitPOP_RegDisp(PR, VM_ArchEntrypoints.framePointerField.getOffset()); // return to caller- pop parameters from stack asm.emitRET_Imm(parameterWords << LG_WORDSIZE); return true; } // software prefetch if (methodName == VM_MagicNames.prefetch || methodName == VM_MagicNames.prefetchNTA) { asm.emitPOP_Reg(T0); asm.emitPREFETCHNTA_Reg(T0); return true; } if (methodName == VM_MagicNames.getTimeBase) { asm.emitRDTSC(); // read timestamp counter instruction asm.emitPUSH_Reg(EDX); // upper 32 bits asm.emitPUSH_Reg(EAX); // lower 32 bits return true; } if (methodName == VM_MagicNames.pause) { asm.emitPAUSE(); // read timestamp counter instruction return true; } if (methodName == VM_MagicNames.wordFromInt || methodName == VM_MagicNames.wordFromObject || methodName == VM_MagicNames.wordFromIntZeroExtend || methodName == VM_MagicNames.wordFromIntSignExtend || methodName == VM_MagicNames.wordToInt || methodName == VM_MagicNames.wordToAddress || methodName == VM_MagicNames.wordToLocalAddress || methodName == VM_MagicNames.wordToOffset || methodName == VM_MagicNames.wordToObject || methodName == VM_MagicNames.wordToObjectReference || methodName == VM_MagicNames.wordToExtent || methodName == VM_MagicNames.wordToWord || methodName == VM_MagicNames.codeArrayAsObject) { if (VM.BuildFor32Addr) return true; // no-op for 32-bit if (VM.VerifyAssertions) VM._assert(false); } if (methodName == VM_MagicNames.wordToLong) { if (VM.BuildFor32Addr) { asm.emitPOP_Reg(T0); asm.emitPUSH_Imm(0); // upper 32 bits asm.emitPUSH_Reg(T0); // lower 32 bits return true; } // else fill unused stackslot if (VM.VerifyAssertions) VM._assert(false); } if (methodName == VM_MagicNames.wordAnd) { asm.emitPOP_Reg(T0); asm.emitAND_RegInd_Reg(SP, T0); return true; } if (methodName == VM_MagicNames.wordOr) { asm.emitPOP_Reg(T0); asm.emitOR_RegInd_Reg(SP, T0); return true; } if (methodName == VM_MagicNames.wordXor) { asm.emitPOP_Reg(T0); asm.emitXOR_RegInd_Reg(SP, T0); return true; } if (methodName == VM_MagicNames.wordNot) { asm.emitNOT_RegInd(SP); return true; } if (methodName == VM_MagicNames.wordPlus) { asm.emitPOP_Reg(T0); asm.emitADD_RegInd_Reg(SP, T0); return true; } if (methodName == VM_MagicNames.wordMinus || methodName == VM_MagicNames.wordDiff) { asm.emitPOP_Reg(T0); asm.emitSUB_RegInd_Reg(SP, T0); return true; } if (methodName == VM_MagicNames.wordZero || methodName == VM_MagicNames.wordNull) { asm.emitPUSH_Imm(0); return true; } if (methodName == VM_MagicNames.wordOne) { asm.emitPUSH_Imm(1); return true; } if (methodName == VM_MagicNames.wordMax) { asm.emitPUSH_Imm(-1); return true; } if (methodName == VM_MagicNames.wordLT) { generateAddrComparison(VM_Assembler.LLT); return true; } if (methodName == VM_MagicNames.wordLE) { generateAddrComparison(VM_Assembler.LLE); return true; } if (methodName == VM_MagicNames.wordGT) { generateAddrComparison(VM_Assembler.LGT); return true; } if (methodName == VM_MagicNames.wordGE) { generateAddrComparison(VM_Assembler.LGE); return true; } if (methodName == VM_MagicNames.wordsLT) { generateAddrComparison(VM_Assembler.LT); return true; } if (methodName == VM_MagicNames.wordsLE) { generateAddrComparison(VM_Assembler.LE); return true; } if (methodName == VM_MagicNames.wordsGT) { generateAddrComparison(VM_Assembler.GT); return true; } if (methodName == VM_MagicNames.wordsGE) { generateAddrComparison(VM_Assembler.GE); return true; } if (methodName == VM_MagicNames.wordEQ) { generateAddrComparison(VM_Assembler.EQ); return true; } if (methodName == VM_MagicNames.wordNE) { generateAddrComparison(VM_Assembler.NE); return true; } if (methodName == VM_MagicNames.wordIsZero) { asm.emitPUSH_Imm(0); generateAddrComparison(VM_Assembler.EQ); return true; } if (methodName == VM_MagicNames.wordIsNull) { asm.emitPUSH_Imm(0); generateAddrComparison(VM_Assembler.EQ); return true; } if (methodName == VM_MagicNames.wordIsMax) { asm.emitPUSH_Imm(-1); generateAddrComparison(VM_Assembler.EQ); return true; } if (methodName == VM_MagicNames.wordLsh) { if (VM.BuildFor32Addr) { asm.emitPOP_Reg(ECX); asm.emitSHL_RegInd_Reg(SP, ECX); } else { VM._assert(false); } return true; } if (methodName == VM_MagicNames.wordRshl) { if (VM.BuildFor32Addr) { asm.emitPOP_Reg(ECX); asm.emitSHR_RegInd_Reg(SP, ECX); } else { VM._assert(false); } return true; } if (methodName == VM_MagicNames.wordRsha) { if (VM.BuildFor32Addr) { asm.emitPOP_Reg(ECX); asm.emitSAR_RegInd_Reg(SP, ECX); } else { VM._assert(false); } return true; } return false; } private void generateAddrComparison(byte comparator) { asm.emitPOP_Reg(S0); asm.emitPOP_Reg(T0); asm.emitCMP_Reg_Reg(T0, S0); VM_ForwardReference fr1 = asm.forwardJcc(comparator); asm.emitPUSH_Imm(0); VM_ForwardReference fr2 = asm.forwardJMP(); fr1.resolve(asm); asm.emitPUSH_Imm(1); fr2.resolve(asm); } // Offset of Java local variable (off stack pointer) // assuming ESP is still positioned as it was at the // start of the current bytecode (biStart) private Offset localOffset(int local) { return Offset.fromIntZeroExtend((stackHeights[biStart] - local) << LG_WORDSIZE); } // Translate a FP offset into an SP offset // assuming ESP is still positioned as it was at the // start of the current bytecode (biStart) private Offset fp2spOffset(Offset offset) { int offsetToFrameHead = (stackHeights[biStart] << LG_WORDSIZE) - firstLocalOffset; return offset.plus(offsetToFrameHead); } private void emitDynamicLinkingSequence(byte reg, VM_MemberReference ref, boolean couldBeZero) { int memberId = ref.getId(); Offset memberOffset = Offset.fromIntZeroExtend(memberId << 2); Offset tableOffset = VM_Entrypoints.memberOffsetsField.getOffset(); if (couldBeZero) { Offset resolverOffset = VM_Entrypoints.resolveMemberMethod.getOffset(); int retryLabel = asm.getMachineCodeIndex(); // branch here after dynamic class loading asm.emitMOV_Reg_RegDisp(reg, JTOC, tableOffset); // reg is offsets table asm.emitMOV_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? } VM_ForwardReference fr = asm.forwardJcc(VM_Assembler.NE); // if so, skip call instructions asm.emitPUSH_Imm(memberId); // pass member's dictId asm.emitPUSH_Imm(0); // false for subarch genParameterRegisterLoad(2); // pass 2ss parameter word asm.emitCALL_RegDisp(JTOC, resolverOffset); // does class loading as sideffect asm.emitJMP_Imm(retryLabel); // reload reg with valid value fr.resolve(asm); // come from Jcc above. } else { asm.emitMOV_Reg_RegDisp(reg, JTOC, tableOffset); // reg is offsets table asm.emitMOV_Reg_RegDisp(reg, reg, memberOffset); // reg is offset of member } } /** * 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. * * I havenot thought about GCMaps for invoke_compiledmethod * TODO: Figure out what the above GCMaps comment means and fix it! */ @Override protected final void emit_invoke_compiledmethod(VM_CompiledMethod cm) { Offset methodOffset = cm.getOsrJTOCoffset(); boolean takeThis = !cm.method.isStatic(); VM_MethodReference ref = cm.method.getMemberRef().asMethodReference(); genParameterRegisterLoad(ref, takeThis); asm.emitCALL_RegDisp(JTOC, methodOffset); genResultRegisterUnload(ref); } @Override protected final void emit_loadretaddrconst(int bcIndex) { asm.generateLoadReturnAddress(bcIndex); } /* bTarget is optional, it emits a JUMP instruction, but the caller * in resposible for patch the target address by call resolve method * of returned forward reference */ @Override protected final VM_ForwardReference emit_pending_goto(int bTarget) { return asm.generatePendingJMP(bTarget); } public final VM_MachineCode genStub() { return null; } }