Search in sources :

Example 1 with XMM

use of org.jikesrvm.ia32.RegisterConstants.XMM in project JikesRVM by JikesRVM.

the class JNICompiler method compile.

/**
 * Compiles a method to handle the Java to C transition and back
 * Transitioning from Java to C then back:
 * <ol>
 * <li>Set up stack frame and save non-volatile registers<li>
 * <li>Set up jniEnv - set up a register to hold JNIEnv and store
 *     the Processor in the JNIEnv for easy access</li>
 * <li>Move all native method arguments on to stack (NB at this point all
 *     non-volatile state is saved)</li>
 * <li>Record the frame pointer of the last Java frame (this) in the jniEnv</li>
 * <li>Call out to convert reference arguments to IDs</li>
 * <li>Set processor as being "in native"</li>
 * <li>Set up stack frame and registers for transition to C</li>
 * <li>Call out to C</li>
 * <li>Save result to stack</li>
 * <li>Transition back from "in native" to "in Java", take care that the
 *     Processor isn't "blocked in native", ie other processors have decided to
 *     start a GC and we're not permitted to execute Java code whilst this
 *     occurs</li>
 * <li>Convert a reference result (currently a JNI ref) into a true reference</li>
 * <li>Release JNI refs</li>
 * <li>Restore stack and place result in register</li>
 * </ol>
 *
 * @param method the method to compile
 * @return the compiled method (always a {@link JNICompiledMethod})
 */
public static synchronized CompiledMethod compile(NativeMethod method) {
    // Meaning of constant offset into frame (assuming 4byte word size):
    // Stack frame:
    // on entry          after prolog
    // 
    // high address        high address
    // |          |        |          | Caller frame
    // |          |        |          |
    // +    |arg 0     |        |arg 0     | <- firstParameterOffset
    // +    |arg 1     |        |arg 1     |
    // +    |...       |        |...       |
    // +8   |arg n-1   |        |arg n-1   | <- lastParameterOffset
    // +4   |returnAddr|        |returnAddr|
    // 0   +          +        +saved FP  + <- EBP/FP value in glue frame
    // -4   |          |        |methodID  |
    // -8   |          |        |saved EDI |
    // -C   |          |        |saved EBX |
    // -10  |          |        |saved EBP |
    // -14  |          |        |saved ENV |  (JNIEnvironment)
    // -18  |          |        |arg n-1   |  reordered args to native method
    // -1C  |          |        | ...      |  ...
    // -20  |          |        |arg 1     |  ...
    // -24  |          |        |arg 0     |  ...
    // -28  |          |        |class/obj |  required second arg to native method
    // -2C  |          |        |jni funcs |  required first arg to native method
    // -30  |          |        |          |
    // |          |        |          |
    // |          |        |          |
    // low address         low address
    // Register values:
    // EBP    - after step 1 EBP holds a frame pointer allowing easy
    // access to both this and the proceeding frame
    // ESP    - gradually floats down as the stack frame is initialized
    // S0/ECX - reference to the JNI environment after step 3
    JNICompiledMethod cm = (JNICompiledMethod) CompiledMethods.createCompiledMethod(method, CompiledMethod.JNI);
    // some size for the instruction array
    Assembler asm = new Assembler(100);
    Address nativeIP = method.getNativeIP();
    final Offset lastParameterOffset = Offset.fromIntSignExtend(2 * WORDSIZE);
    // final Offset firstParameterOffset = Offset.fromIntSignExtend(WORDSIZE+(method.getParameterWords() << LG_WORDSIZE));
    final TypeReference[] args = method.getParameterTypes();
    // (1) Set up stack frame and save non-volatile registers
    // TODO:  check and resize stack once on the lowest Java to C transition
    // on the stack.  Not needed if we use the thread original stack
    // set 2nd word of header = return address already pushed by CALL
    asm.emitPUSH_RegDisp(THREAD_REGISTER, ArchEntrypoints.framePointerField.getOffset());
    // establish new frame
    if (VM.BuildFor32Addr) {
        asm.emitMOV_RegDisp_Reg(THREAD_REGISTER, ArchEntrypoints.framePointerField.getOffset(), SP);
    } else {
        asm.emitMOV_RegDisp_Reg_Quad(THREAD_REGISTER, ArchEntrypoints.framePointerField.getOffset(), SP);
    }
    // set first word of header: method ID
    if (VM.VerifyAssertions)
        VM._assert(STACKFRAME_METHOD_ID_OFFSET.toInt() == -WORDSIZE);
    asm.emitPUSH_Imm(cm.getId());
    // save nonvolatile registrs: EDI, EBX, EBP
    if (VM.VerifyAssertions)
        VM._assert(EDI_SAVE_OFFSET.toInt() == -2 * WORDSIZE);
    // save nonvolatile EDI register
    asm.emitPUSH_Reg(EDI);
    if (VM.VerifyAssertions)
        VM._assert(EBX_SAVE_OFFSET.toInt() == -3 * WORDSIZE);
    // save nonvolatile EBX register
    asm.emitPUSH_Reg(EBX);
    if (VM.VerifyAssertions)
        VM._assert(EBP_SAVE_OFFSET.toInt() == -4 * WORDSIZE);
    // save nonvolatile EBP register
    asm.emitPUSH_Reg(EBP);
    // Establish EBP as the framepointer for use in the rest of the glue frame
    if (VM.BuildFor32Addr) {
        asm.emitLEA_Reg_RegDisp(EBP, SP, Offset.fromIntSignExtend(4 * WORDSIZE));
    } else {
        asm.emitLEA_Reg_RegDisp_Quad(EBP, SP, Offset.fromIntSignExtend(4 * WORDSIZE));
    }
    // S0 = RVMThread.jniEnv
    if (VM.BuildFor32Addr) {
        asm.emitMOV_Reg_RegDisp(S0, THREAD_REGISTER, Entrypoints.jniEnvField.getOffset());
    } else {
        asm.emitMOV_Reg_RegDisp_Quad(S0, THREAD_REGISTER, Entrypoints.jniEnvField.getOffset());
    }
    if (VM.VerifyAssertions)
        VM._assert(JNI_ENV_OFFSET.toInt() == -5 * WORDSIZE);
    // save JNI Env for after call
    asm.emitPUSH_Reg(S0);
    if (VM.VerifyAssertions)
        VM._assert(BP_ON_ENTRY_OFFSET.toInt() == -6 * WORDSIZE);
    asm.emitPUSH_RegDisp(S0, Entrypoints.JNIEnvBasePointerOnEntryToNative.getOffset());
    // save BP into JNIEnv
    if (VM.BuildFor32Addr) {
        asm.emitMOV_RegDisp_Reg(S0, Entrypoints.JNIEnvBasePointerOnEntryToNative.getOffset(), EBP);
    } else {
        asm.emitMOV_RegDisp_Reg_Quad(S0, Entrypoints.JNIEnvBasePointerOnEntryToNative.getOffset(), EBP);
    }
    // (3) Move all native method arguments on to stack (NB at this
    // point all non-volatile state is saved)
    // (3.1) Count how many arguments could be passed in either FPRs or GPRs
    int numFprArgs = 0;
    int numGprArgs = 0;
    for (TypeReference arg : args) {
        if (arg.isFloatingPointType()) {
            numFprArgs++;
        } else if (VM.BuildFor32Addr && arg.isLongType()) {
            numGprArgs += 2;
        } else {
            numGprArgs++;
        }
    }
    // (3.2) add stack aligning padding
    if (VM.BuildFor64Addr) {
        int argsInRegisters = Math.min(numFprArgs, NATIVE_PARAMETER_FPRS.length) + Math.min(numGprArgs + 2, NATIVE_PARAMETER_GPRS.length);
        int argsOnStack = numGprArgs + numFprArgs + 2 - argsInRegisters;
        if (VM.VerifyAssertions)
            VM._assert(argsOnStack >= 0);
        if ((argsOnStack & 1) != 0) {
            // need odd alignment prior to pushes
            asm.emitAND_Reg_Imm_Quad(SP, -16);
            asm.emitPUSH_Reg(T0);
        } else {
            // need even alignment prior to pushes
            asm.emitAND_Reg_Imm_Quad(SP, -16);
        }
    }
    // (we always pass a this or a class but we only pop this)
    if (!method.isStatic()) {
        numGprArgs++;
    }
    // (3.3) Walk over arguments backwards pushing either from memory or registers
    Offset currentArg = lastParameterOffset;
    int argFpr = numFprArgs - 1;
    int argGpr = numGprArgs - 1;
    for (int i = args.length - 1; i >= 0; i--) {
        TypeReference arg = args[i];
        if (arg.isFloatType()) {
            if (argFpr < PARAMETER_FPRS.length) {
                // make space
                asm.emitPUSH_Reg(T0);
                if (SSE2_FULL) {
                    asm.emitMOVSS_RegInd_Reg(SP, (XMM) PARAMETER_FPRS[argFpr]);
                } else {
                    asm.emitFSTP_RegInd_Reg(SP, FP0);
                }
            } else {
                asm.emitPUSH_RegDisp(EBP, currentArg);
            }
            argFpr--;
        } else if (arg.isDoubleType()) {
            if (VM.BuildFor32Addr) {
                if (argFpr < PARAMETER_FPRS.length) {
                    // make space
                    asm.emitPUSH_Reg(T0);
                    // need 2 slots with 32bit addresses
                    asm.emitPUSH_Reg(T0);
                    if (SSE2_FULL) {
                        asm.emitMOVSD_RegInd_Reg(SP, (XMM) PARAMETER_FPRS[argFpr]);
                    } else {
                        asm.emitFSTP_RegInd_Reg_Quad(SP, FP0);
                    }
                } else {
                    asm.emitPUSH_RegDisp(EBP, currentArg.plus(WORDSIZE));
                    // need 2 slots with 32bit addresses
                    asm.emitPUSH_RegDisp(EBP, currentArg);
                }
            } else {
                if (argFpr < PARAMETER_FPRS.length) {
                    // make space
                    asm.emitPUSH_Reg(T0);
                    if (SSE2_FULL) {
                        asm.emitMOVSD_RegInd_Reg(SP, (XMM) PARAMETER_FPRS[argFpr]);
                    } else {
                        asm.emitFSTP_RegInd_Reg_Quad(SP, FP0);
                    }
                } else {
                    asm.emitPUSH_RegDisp(EBP, currentArg);
                }
            }
            argFpr--;
            currentArg = currentArg.plus(WORDSIZE);
        } else if (VM.BuildFor32Addr && arg.isLongType()) {
            if (argGpr < PARAMETER_GPRS.length) {
                asm.emitPUSH_Reg(PARAMETER_GPRS[argGpr - 1]);
                asm.emitPUSH_Reg(PARAMETER_GPRS[argGpr]);
            } else if (argGpr - 1 < PARAMETER_GPRS.length) {
                asm.emitPUSH_Reg(PARAMETER_GPRS[argGpr - 1]);
                asm.emitPUSH_RegDisp(EBP, currentArg);
            } else {
                asm.emitPUSH_RegDisp(EBP, currentArg.plus(WORDSIZE));
                asm.emitPUSH_RegDisp(EBP, currentArg);
            }
            argGpr -= 2;
            currentArg = currentArg.plus(WORDSIZE);
        } else {
            if (argGpr < PARAMETER_GPRS.length) {
                asm.emitPUSH_Reg(PARAMETER_GPRS[argGpr]);
            } else {
                asm.emitPUSH_RegDisp(EBP, currentArg);
            }
            argGpr--;
            if (VM.BuildFor64Addr && arg.isLongType()) {
                currentArg = currentArg.plus(WORDSIZE);
            }
        }
        currentArg = currentArg.plus(WORDSIZE);
    }
    // (3.4) push class or object argument
    if (method.isStatic()) {
        // push java.lang.Class object for klass
        Offset klassOffset = Offset.fromIntSignExtend(Statics.findOrCreateObjectLiteral(method.getDeclaringClass().getClassForType()));
        asm.generateJTOCpush(klassOffset);
    } else {
        if (VM.VerifyAssertions)
            VM._assert(argGpr == 0);
        asm.emitPUSH_Reg(PARAMETER_GPRS[0]);
    }
    // (3.5) push a pointer to the JNI functions that will be
    // dereferenced in native code
    asm.emitPUSH_Reg(S0);
    if (jniExternalFunctionsFieldOffset != 0) {
        if (VM.BuildFor32Addr) {
            asm.emitADD_RegInd_Imm(ESP, jniExternalFunctionsFieldOffset);
        } else {
            asm.emitADD_RegInd_Imm_Quad(ESP, jniExternalFunctionsFieldOffset);
        }
    }
    // (4) Call out to convert reference arguments to IDs, set thread as
    // being "in native" and record the frame pointer of the last Java frame
    // (this) in the jniEnv
    // Encode reference arguments into a long
    int encodedReferenceOffsets = 0;
    for (int i = 0, pos = 0; i < args.length; i++, pos++) {
        TypeReference arg = args[i];
        if (arg.isReferenceType()) {
            if (VM.VerifyAssertions)
                VM._assert(pos < 32);
            encodedReferenceOffsets |= 1 << pos;
        } else if (VM.BuildFor32Addr && (arg.isLongType() || arg.isDoubleType())) {
            pos++;
        }
    }
    // Call out to JNI environment JNI entry
    if (VM.BuildFor32Addr) {
        asm.emitMOV_Reg_RegDisp(PARAMETER_GPRS[0], EBP, JNI_ENV_OFFSET);
    } else {
        asm.emitMOV_Reg_RegDisp_Quad(PARAMETER_GPRS[0], EBP, JNI_ENV_OFFSET);
    }
    asm.emitPUSH_Reg(PARAMETER_GPRS[0]);
    asm.emitMOV_Reg_Imm(PARAMETER_GPRS[1], encodedReferenceOffsets);
    asm.emitPUSH_Reg(PARAMETER_GPRS[1]);
    asm.baselineEmitLoadTIB(S0, PARAMETER_GPRS[0]);
    asm.emitCALL_RegDisp(S0, Entrypoints.jniEntry.getOffset());
    // (5) Set up stack frame and registers for transition to C
    int stackholes = 0;
    int position = 0;
    int argsPassedInRegister = 0;
    if (VM.BuildFor64Addr) {
        int gpRegistersInUse = 2;
        int fpRegistersInUse = 0;
        boolean dataOnStack = false;
        // JNI env
        asm.emitPOP_Reg(NATIVE_PARAMETER_GPRS[0]);
        // Object/Class
        asm.emitPOP_Reg(NATIVE_PARAMETER_GPRS[1]);
        argsPassedInRegister += 2;
        for (TypeReference arg : method.getParameterTypes()) {
            if (arg.isFloatType()) {
                if (fpRegistersInUse < NATIVE_PARAMETER_FPRS.length) {
                    asm.emitMOVSS_Reg_RegDisp((XMM) NATIVE_PARAMETER_FPRS[fpRegistersInUse], SP, Offset.fromIntZeroExtend(position << LG_WORDSIZE));
                    if (dataOnStack) {
                        stackholes |= 1 << position;
                    } else {
                        asm.emitPOP_Reg(T0);
                    }
                    fpRegistersInUse++;
                    argsPassedInRegister++;
                } else {
                    // no register available so we have data on the stack
                    dataOnStack = true;
                }
            } else if (arg.isDoubleType()) {
                if (fpRegistersInUse < NATIVE_PARAMETER_FPRS.length) {
                    asm.emitMOVSD_Reg_RegDisp((XMM) NATIVE_PARAMETER_FPRS[fpRegistersInUse], SP, Offset.fromIntZeroExtend(position << LG_WORDSIZE));
                    if (dataOnStack) {
                        stackholes |= 1 << position;
                    } else {
                        asm.emitPOP_Reg(T0);
                    }
                    if (VM.BuildFor32Addr)
                        asm.emitPOP_Reg(T0);
                    fpRegistersInUse++;
                    argsPassedInRegister += VM.BuildFor32Addr ? 2 : 1;
                } else {
                    // no register available so we have data on the stack
                    dataOnStack = true;
                }
            } else {
                if (gpRegistersInUse < NATIVE_PARAMETER_GPRS.length) {
                    // TODO: we can't have holes in the data that is on the stack, we need to shuffle it up
                    asm.emitMOV_Reg_RegDisp_Quad(NATIVE_PARAMETER_GPRS[gpRegistersInUse], SP, Offset.fromIntZeroExtend(position << LG_WORDSIZE));
                    if (dataOnStack) {
                        stackholes |= 1 << position;
                    } else {
                        asm.emitPOP_Reg(T0);
                    }
                    gpRegistersInUse++;
                    argsPassedInRegister++;
                } else {
                    // no register available so we have data on the stack
                    dataOnStack = true;
                }
            }
            if (dataOnStack) {
                position++;
            }
        }
        position--;
        int onStackOffset = position;
        int mask = 0;
        for (int i = position; i >= 0; i--) {
            mask = 1 << i;
            if ((stackholes & mask) != 0) {
                continue;
            }
            if (i < onStackOffset) {
                asm.emitMOV_Reg_RegDisp_Quad(T0, SP, Offset.fromIntZeroExtend(i << LOG_BYTES_IN_WORD));
                asm.emitMOV_RegDisp_Reg_Quad(SP, Offset.fromIntZeroExtend(onStackOffset << LOG_BYTES_IN_WORD), T0);
            }
            onStackOffset--;
        }
        while (onStackOffset >= 0) {
            asm.emitPOP_Reg(T0);
            onStackOffset--;
        }
    }
    // move address of native code to invoke into T0
    if (VM.BuildFor32Addr) {
        asm.emitMOV_Reg_Imm(T0, nativeIP.toInt());
    } else {
        asm.emitMOV_Reg_Imm_Quad(T0, nativeIP.toLong());
    }
    // Trap if stack alignment fails
    if (VM.ExtremeAssertions && VM.BuildFor64Addr) {
        asm.emitBT_Reg_Imm(ESP, 3);
        ForwardReference fr = asm.forwardJcc(LGE);
        asm.emitINT_Imm(3);
        fr.resolve(asm);
    }
    // make the call to native code
    asm.emitCALL_Reg(T0);
    // (7) Discard parameters on stack
    if (VM.BuildFor32Addr) {
        // throw away args, class/this ptr and env
        int argsToThrowAway = method.getParameterWords() + 2 - argsPassedInRegister;
        if (argsToThrowAway != 0) {
            asm.emitLEA_Reg_RegDisp(SP, EBP, BP_ON_ENTRY_OFFSET);
        }
    } else {
        // throw away args, class/this ptr and env (and padding)
        asm.emitLEA_Reg_RegDisp_Quad(SP, EBP, BP_ON_ENTRY_OFFSET);
    }
    // (8) Save result to stack
    final TypeReference returnType = method.getReturnType();
    if (returnType.isVoidType()) {
    // Nothing to save
    } else if (returnType.isFloatType()) {
        // adjust stack
        asm.emitPUSH_Reg(T0);
        if (VM.BuildFor32Addr) {
            asm.emitFSTP_RegInd_Reg(ESP, FP0);
        } else {
            asm.emitMOVSS_RegInd_Reg(ESP, XMM0);
        }
    } else if (returnType.isDoubleType()) {
        // adjust stack
        asm.emitPUSH_Reg(T0);
        // adjust stack
        asm.emitPUSH_Reg(T0);
        if (VM.BuildFor32Addr) {
            asm.emitFSTP_RegInd_Reg_Quad(ESP, FP0);
        } else {
            asm.emitMOVSD_RegInd_Reg(ESP, XMM0);
        }
    } else if (VM.BuildFor32Addr && returnType.isLongType()) {
        asm.emitPUSH_Reg(T0);
        asm.emitPUSH_Reg(T1);
    } else {
        // Ensure sign-extension is correct
        if (returnType.isBooleanType()) {
            asm.emitMOVZX_Reg_Reg_Byte(T0, T0);
        } else if (returnType.isByteType()) {
            asm.emitMOVSX_Reg_Reg_Byte(T0, T0);
        } else if (returnType.isCharType()) {
            asm.emitMOVZX_Reg_Reg_Word(T0, T0);
        } else if (returnType.isShortType()) {
            asm.emitMOVSX_Reg_Reg_Word(T0, T0);
        }
        asm.emitPUSH_Reg(T0);
    }
    // (9.1) reload JNIEnvironment from glue frame
    if (VM.BuildFor32Addr) {
        asm.emitMOV_Reg_RegDisp(S0, EBP, JNICompiler.JNI_ENV_OFFSET);
    } else {
        asm.emitMOV_Reg_RegDisp_Quad(S0, EBP, JNICompiler.JNI_ENV_OFFSET);
    }
    // (9.2) Reload thread register from JNIEnvironment
    if (VM.BuildFor32Addr) {
        asm.emitMOV_Reg_RegDisp(THREAD_REGISTER, S0, Entrypoints.JNIEnvSavedTRField.getOffset());
    } else {
        asm.emitMOV_Reg_RegDisp_Quad(THREAD_REGISTER, S0, Entrypoints.JNIEnvSavedTRField.getOffset());
    }
    // (9.3) Establish frame pointer to this glue method
    if (VM.BuildFor32Addr) {
        asm.emitMOV_RegDisp_Reg(THREAD_REGISTER, ArchEntrypoints.framePointerField.getOffset(), EBP);
    } else {
        asm.emitMOV_RegDisp_Reg_Quad(THREAD_REGISTER, ArchEntrypoints.framePointerField.getOffset(), EBP);
    }
    // result (currently a JNI ref) into a true reference, release JNI refs
    if (VM.BuildFor32Addr) {
        // 1st arg is JNI Env
        asm.emitMOV_Reg_Reg(PARAMETER_GPRS[0], S0);
    } else {
        // 1st arg is JNI Env
        asm.emitMOV_Reg_Reg_Quad(PARAMETER_GPRS[0], S0);
    }
    if (returnType.isReferenceType()) {
        // 2nd arg is ref result
        asm.emitPOP_Reg(PARAMETER_GPRS[1]);
    } else {
        // place dummy (null) operand on stack
        asm.emitXOR_Reg_Reg(PARAMETER_GPRS[1], PARAMETER_GPRS[1]);
    }
    // save JNIEnv
    asm.emitPUSH_Reg(S0);
    // push arg 1
    asm.emitPUSH_Reg(S0);
    // push arg 2
    asm.emitPUSH_Reg(PARAMETER_GPRS[1]);
    // Do the call
    asm.baselineEmitLoadTIB(S0, S0);
    asm.emitCALL_RegDisp(S0, Entrypoints.jniExit.getOffset());
    // restore JNIEnv
    asm.emitPOP_Reg(S0);
    // place result in register
    if (returnType.isVoidType()) {
    // Nothing to save
    } else if (returnType.isReferenceType()) {
    // value already in register
    } else if (returnType.isFloatType()) {
        if (SSE2_FULL) {
            asm.emitMOVSS_Reg_RegInd(XMM0, ESP);
        } else {
            asm.emitFLD_Reg_RegInd(FP0, ESP);
        }
        // adjust stack
        asm.emitPOP_Reg(T0);
    } else if (returnType.isDoubleType()) {
        if (SSE2_FULL) {
            asm.emitMOVSD_Reg_RegInd(XMM0, ESP);
        } else {
            asm.emitFLD_Reg_RegInd_Quad(FP0, ESP);
        }
        // adjust stack
        asm.emitPOP_Reg(T0);
        // adjust stack
        asm.emitPOP_Reg(T0);
    } else if (VM.BuildFor32Addr && returnType.isLongType()) {
        asm.emitPOP_Reg(T0);
        asm.emitPOP_Reg(T1);
    } else {
        asm.emitPOP_Reg(T0);
    }
    // saved previous native BP
    asm.emitPOP_Reg(EBX);
    if (VM.BuildFor32Addr) {
        asm.emitMOV_RegDisp_Reg(S0, Entrypoints.JNIEnvBasePointerOnEntryToNative.getOffset(), EBX);
    } else {
        asm.emitMOV_RegDisp_Reg_Quad(S0, Entrypoints.JNIEnvBasePointerOnEntryToNative.getOffset(), EBX);
    }
    // throw away JNI env
    asm.emitPOP_Reg(EBX);
    // restore non-volatile EBP
    asm.emitPOP_Reg(EBP);
    // restore non-volatile EBX
    asm.emitPOP_Reg(EBX);
    // restore non-volatile EDI
    asm.emitPOP_Reg(EDI);
    // throw away cmid
    asm.emitPOP_Reg(S0);
    asm.emitPOP_RegDisp(THREAD_REGISTER, ArchEntrypoints.framePointerField.getOffset());
    // pop parameters from stack (Note that parameterWords does not include "this")
    if (method.isStatic()) {
        asm.emitRET_Imm(method.getParameterWords() << LG_WORDSIZE);
    } else {
        asm.emitRET_Imm((method.getParameterWords() + 1) << LG_WORDSIZE);
    }
    CodeArray code = asm.getMachineCodes();
    cm.compileComplete(code);
    return cm;
}
Also used : ForwardReference(org.jikesrvm.compilers.common.assembler.ForwardReference) CodeArray(org.jikesrvm.compilers.common.CodeArray) Address(org.vmmagic.unboxed.Address) XMM(org.jikesrvm.ia32.RegisterConstants.XMM) Assembler(org.jikesrvm.compilers.common.assembler.ia32.Assembler) TypeReference(org.jikesrvm.classloader.TypeReference) JNICompiledMethod(org.jikesrvm.jni.JNICompiledMethod) Offset(org.vmmagic.unboxed.Offset)

Example 2 with XMM

use of org.jikesrvm.ia32.RegisterConstants.XMM in project JikesRVM by JikesRVM.

the class JNICompiler method generateEpilogForJNIMethod.

/**
 * Handles the C to Java transition:  JNI methods in JNIFunctions.java.
 * Creates an epilogue for the baseline compiler.
 *
 * @param asm the assembler to use
 * @param method the method that's being compiled
 */
public static void generateEpilogForJNIMethod(Assembler asm, RVMMethod method) {
    if (VM.BuildFor32Addr) {
        // if returning long, switch the order of the hi/lo word in T0 and T1
        if (method.getReturnType().isLongType()) {
            asm.emitPUSH_Reg(T1);
            asm.emitMOV_Reg_Reg(T1, T0);
            asm.emitPOP_Reg(T0);
        } else {
            if (SSE2_FULL && VM.BuildFor32Addr) {
                // Marshall from XMM0 -> FP0
                if (method.getReturnType().isDoubleType()) {
                    if (VM.VerifyAssertions)
                        VM._assert(VM.BuildFor32Addr);
                    asm.emitMOVSD_RegDisp_Reg(THREAD_REGISTER, Entrypoints.scratchStorageField.getOffset(), XMM0);
                    asm.emitFLD_Reg_RegDisp_Quad(FP0, THREAD_REGISTER, Entrypoints.scratchStorageField.getOffset());
                } else if (method.getReturnType().isFloatType()) {
                    if (VM.VerifyAssertions)
                        VM._assert(VM.BuildFor32Addr);
                    asm.emitMOVSS_RegDisp_Reg(THREAD_REGISTER, Entrypoints.scratchStorageField.getOffset(), XMM0);
                    asm.emitFLD_Reg_RegDisp(FP0, THREAD_REGISTER, Entrypoints.scratchStorageField.getOffset());
                }
            }
        }
    }
    // S0 <- JNIEnvironment
    if (VM.BuildFor32Addr) {
        asm.emitMOV_Reg_RegDisp(S0, THREAD_REGISTER, Entrypoints.jniEnvField.getOffset());
    } else {
        asm.emitMOV_Reg_RegDisp_Quad(S0, THREAD_REGISTER, Entrypoints.jniEnvField.getOffset());
    }
    // set jniEnv TopJavaFP using value saved in frame in prolog
    if (VM.BuildFor32Addr) {
        // EDI<-saved TopJavaFP (offset)
        asm.emitMOV_Reg_RegDisp(EDI, EBP, SAVED_JAVA_FP_OFFSET);
        // change offset from FP into address
        asm.emitADD_Reg_Reg(EDI, EBP);
        // jniEnv.TopJavaFP <- EDI
        asm.emitMOV_RegDisp_Reg(S0, Entrypoints.JNITopJavaFPField.getOffset(), EDI);
    } else {
        // EDI<-saved TopJavaFP (offset)
        asm.emitMOV_Reg_RegDisp_Quad(EDI, EBP, SAVED_JAVA_FP_OFFSET);
        // change offset from FP into address
        asm.emitADD_Reg_Reg_Quad(EDI, EBP);
        // jniEnv.TopJavaFP <- EDI
        asm.emitMOV_RegDisp_Reg_Quad(S0, Entrypoints.JNITopJavaFPField.getOffset(), EDI);
    }
    // NOTE: we could save the TR in the JNI env, but no need, that would have
    // already been done.
    // what's going on here:
    // - SP and EBP have important stuff in them, but that's fine, since
    // a call will restore SP and EBP is non-volatile for RVM code
    // - TR still refers to the thread
    // save return values
    asm.emitPUSH_Reg(T0);
    asm.emitPUSH_Reg(T1);
    // attempt to change the thread state to IN_JNI
    asm.emitMOV_Reg_Imm(T0, RVMThread.IN_JAVA);
    asm.emitMOV_Reg_Imm(T1, RVMThread.IN_JNI);
    asm.emitLockNextInstruction();
    asm.emitCMPXCHG_RegDisp_Reg(THREAD_REGISTER, Entrypoints.execStatusField.getOffset(), T1);
    // if success, skip the slow path call
    ForwardReference doneEnterJNIRef = asm.forwardJcc(EQ);
    // fast path failed, make the call
    asm.generateJTOCcall(Entrypoints.enterJNIBlockedFromJNIFunctionCallMethod.getOffset());
    // OK - we reach here when we have set the state to IN_JNI
    doneEnterJNIRef.resolve(asm);
    // restore return values
    asm.emitPOP_Reg(T1);
    asm.emitPOP_Reg(T0);
    // reload native/C nonvolatile regs - saved in prolog
    for (FloatingPointMachineRegister r : NATIVE_NONVOLATILE_FPRS) {
        // TODO: we assume non-volatile will hold at most a double
        if (r instanceof XMM) {
            asm.emitMOVSD_Reg_RegInd((XMM) r, SP);
        } else {
            // NB this will fail for anything other than FPR0
            asm.emitFLD_Reg_RegInd_Quad((FPR) r, SP);
        }
        // adjust space for double
        asm.emitPOP_Reg(T0);
        asm.emitPOP_Reg(T0);
    }
    // nonvolatile push as the 1st instruction of the prologue
    for (int i = NATIVE_NONVOLATILE_GPRS.length - 1; i >= 0; i--) {
        GPR r = NATIVE_NONVOLATILE_GPRS[i];
        asm.emitPOP_Reg(r);
    }
    // Discard JNIEnv, CMID and outer most native frame pointer
    if (VM.BuildFor32Addr) {
        // discard current stack frame
        asm.emitADD_Reg_Imm(SP, 3 * WORDSIZE);
    } else {
        // discard current stack frame
        asm.emitADD_Reg_Imm_Quad(SP, 3 * WORDSIZE);
    }
    // return to caller
    asm.emitRET();
}
Also used : ForwardReference(org.jikesrvm.compilers.common.assembler.ForwardReference) FloatingPointMachineRegister(org.jikesrvm.ia32.RegisterConstants.FloatingPointMachineRegister) GPR(org.jikesrvm.ia32.RegisterConstants.GPR) XMM(org.jikesrvm.ia32.RegisterConstants.XMM)

Example 3 with XMM

use of org.jikesrvm.ia32.RegisterConstants.XMM in project JikesRVM by JikesRVM.

the class JNICompiler method generateGlueCodeForJNIMethod.

/**
 * Handles the C to Java transition:  JNI methods in JNIFunctions.java.
 * Creates a prologue for the baseline compiler.
 * <pre>
 * NOTE:
 *   -We need THREAD_REGISTER to access Java environment; we can get it from
 *    the JNIEnv* (which is an interior pointer to the JNIEnvironment)
 *   -Unlike the powerPC scheme which has a special prolog preceding
 *    the normal Java prolog, the Intel scheme replaces the Java prolog
 *    completely with the special prolog
 *
 *            Stack on entry            Stack at end of prolog after call
 *             high memory                       high memory
 *            |            |                   |            |
 *    EBP -&gt;  |saved FP    |                   |saved FP    |
 *            |  ...       |                   |  ...       |
 *            |            |                   |            |
 *            |arg n-1     |                   |arg n-1     |
 * native     |  ...       |                   |  ...       |
 * caller     |arg 0       | JNIEnv*           |arg 0       | JNIEnvironment
 *    ESP -&gt;  |return addr |                   |return addr |
 *            |            |           EBP -&gt;  |saved FP    | outer most native frame pointer
 *            |            |                   |methodID    | normal MethodID for JNI function
 *            |            |                   |saved JavaFP| offset to preceeding java frame
 *            |            |                   |saved nonvol| to be used for nonvolatile storage
 *            |            |                   |  ...       |   including ebp on entry
 *            |            |                   |arg 0       | copied in reverse order (JNIEnvironment)
 *            |            |                   |  ...       |
 *            |            |           ESP -&gt;  |arg n-1     |
 *            |            |                   |            | normally compiled Java code continue
 *            |            |                   |            |
 *            |            |                   |            |
 *            |            |                   |            |
 *             low memory                        low memory
 * </pre>
 *
 * @param asm the assembler to use
 * @param method the method that's being compiled (i.e. the method which is a bridge
 *  from native).
 * @param methodID the id of the compiled method
 */
public static void generateGlueCodeForJNIMethod(Assembler asm, NormalMethod method, int methodID) {
    // Variable tracking the depth of the stack as we generate the prologue
    int stackDepth = 0;
    // 2nd word of header = space for frame pointer
    if (VM.VerifyAssertions)
        VM._assert(STACKFRAME_FRAME_POINTER_OFFSET.toInt() == stackDepth << LG_WORDSIZE);
    asm.emitPUSH_Reg(EBP);
    stackDepth--;
    // start new frame:  set FP to point to the new frame
    if (VM.BuildFor32Addr) {
        asm.emitMOV_Reg_Reg(EBP, SP);
    } else {
        asm.emitMOV_Reg_Reg_Quad(EBP, SP);
    }
    // set 3rd word of header: method ID
    if (VM.VerifyAssertions)
        VM._assert(STACKFRAME_METHOD_ID_OFFSET.toInt() == stackDepth << LG_WORDSIZE);
    asm.emitPUSH_Imm(methodID);
    stackDepth--;
    // buy space for the SAVED_JAVA_FP
    if (VM.VerifyAssertions)
        VM._assert(STACKFRAME_BODY_OFFSET.toInt() == stackDepth << LG_WORDSIZE);
    asm.emitPUSH_Reg(T0);
    stackDepth--;
    // store non-volatiles
    for (GPR r : NATIVE_NONVOLATILE_GPRS) {
        if (r != EBP) {
            asm.emitPUSH_Reg(r);
        } else {
            // save original EBP value
            asm.emitPUSH_RegInd(EBP);
        }
        stackDepth--;
    }
    for (FloatingPointMachineRegister r : NATIVE_NONVOLATILE_FPRS) {
        // TODO: we assume non-volatile will hold at most a double
        // adjust space for double
        asm.emitPUSH_Reg(T0);
        asm.emitPUSH_Reg(T0);
        stackDepth -= 2;
        if (r instanceof XMM) {
            asm.emitMOVSD_RegInd_Reg(SP, (XMM) r);
        } else {
            // NB this will fail for anything other than FPR0
            asm.emitFST_RegInd_Reg_Quad(SP, (FPR) r);
        }
    }
    if (VM.VerifyAssertions) {
        boolean b = stackDepth << LG_WORDSIZE == STACKFRAME_BODY_OFFSET.toInt() - (SAVED_GPRS_FOR_JNI << LG_WORDSIZE);
        if (!b) {
            String msg = "of2fp=" + stackDepth + " sg4j=" + SAVED_GPRS_FOR_JNI;
            VM._assert(VM.NOT_REACHED, msg);
        }
    }
    // Adjust first param from JNIEnv* to JNIEnvironment.
    final Offset firstStackArgOffset = Offset.fromIntSignExtend(2 * WORDSIZE);
    if (jniExternalFunctionsFieldOffset != 0) {
        if (NATIVE_PARAMETER_GPRS.length > 0) {
            if (VM.BuildFor32Addr) {
                asm.emitSUB_Reg_Imm(NATIVE_PARAMETER_GPRS[0], jniExternalFunctionsFieldOffset);
            } else {
                asm.emitSUB_Reg_Imm_Quad(NATIVE_PARAMETER_GPRS[0], jniExternalFunctionsFieldOffset);
            }
        } else {
            if (VM.BuildFor32Addr) {
                asm.emitSUB_RegDisp_Imm(EBP, firstStackArgOffset, jniExternalFunctionsFieldOffset);
            } else {
                asm.emitSUB_RegDisp_Imm_Quad(EBP, firstStackArgOffset, jniExternalFunctionsFieldOffset);
            }
        }
    }
    // copy the arguments in reverse order
    // does NOT include implicit this or class ptr
    final TypeReference[] argTypes = method.getParameterTypes();
    Offset stackArgOffset = firstStackArgOffset;
    // negative value relative to EBP
    final int startOfStackedArgs = stackDepth + 1;
    int argGPR = 0;
    int argFPR = 0;
    for (TypeReference argType : argTypes) {
        if (argType.isFloatType()) {
            if (argFPR < NATIVE_PARAMETER_FPRS.length) {
                // adjust stack
                asm.emitPUSH_Reg(T0);
                if (VM.BuildForSSE2) {
                    asm.emitMOVSS_RegInd_Reg(SP, (XMM) NATIVE_PARAMETER_FPRS[argFPR]);
                } else {
                    asm.emitFSTP_RegInd_Reg(SP, FP0);
                }
                argFPR++;
            } else {
                asm.emitPUSH_RegDisp(EBP, stackArgOffset);
                stackArgOffset = stackArgOffset.plus(WORDSIZE);
            }
            stackDepth--;
        } else if (argType.isDoubleType()) {
            if (argFPR < NATIVE_PARAMETER_FPRS.length) {
                // adjust stack
                asm.emitPUSH_Reg(T0);
                asm.emitPUSH_Reg(T0);
                if (VM.BuildForSSE2) {
                    asm.emitMOVSD_RegInd_Reg(SP, (XMM) NATIVE_PARAMETER_FPRS[argFPR]);
                } else {
                    asm.emitFSTP_RegInd_Reg_Quad(SP, FP0);
                }
                argFPR++;
            } else {
                if (VM.BuildFor32Addr) {
                    asm.emitPUSH_RegDisp(EBP, stackArgOffset.plus(WORDSIZE));
                    asm.emitPUSH_RegDisp(EBP, stackArgOffset);
                    stackArgOffset = stackArgOffset.plus(2 * WORDSIZE);
                } else {
                    // adjust stack
                    asm.emitPUSH_Reg(T0);
                    asm.emitPUSH_RegDisp(EBP, stackArgOffset);
                    stackArgOffset = stackArgOffset.plus(WORDSIZE);
                }
            }
            stackDepth -= 2;
        } else if (argType.isLongType()) {
            if (VM.BuildFor32Addr) {
                if (argGPR + 1 < NATIVE_PARAMETER_GPRS.length) {
                    asm.emitPUSH_Reg(NATIVE_PARAMETER_GPRS[argGPR]);
                    asm.emitPUSH_Reg(NATIVE_PARAMETER_GPRS[argGPR + 1]);
                    argGPR += 2;
                } else if (argGPR < NATIVE_PARAMETER_GPRS.length) {
                    asm.emitPUSH_RegDisp(EBP, stackArgOffset);
                    asm.emitPUSH_Reg(NATIVE_PARAMETER_GPRS[argGPR]);
                    argGPR++;
                    stackArgOffset = stackArgOffset.plus(WORDSIZE);
                } else {
                    asm.emitPUSH_RegDisp(EBP, stackArgOffset.plus(WORDSIZE));
                    asm.emitPUSH_RegDisp(EBP, stackArgOffset);
                    stackArgOffset = stackArgOffset.plus(WORDSIZE * 2);
                }
                stackDepth -= 2;
            } else {
                // adjust stack
                asm.emitPUSH_Reg(T0);
                if (argGPR < NATIVE_PARAMETER_GPRS.length) {
                    asm.emitPUSH_Reg(NATIVE_PARAMETER_GPRS[argGPR]);
                    argGPR++;
                } else {
                    asm.emitPUSH_RegDisp(EBP, stackArgOffset);
                    stackDepth -= 2;
                    stackArgOffset = stackArgOffset.plus(WORDSIZE);
                }
                stackDepth -= 2;
            }
        } else {
            // expect integer arguments
            if (argGPR < NATIVE_PARAMETER_GPRS.length) {
                asm.emitPUSH_Reg(NATIVE_PARAMETER_GPRS[argGPR]);
                argGPR++;
            } else {
                asm.emitPUSH_RegDisp(EBP, stackArgOffset);
                stackArgOffset = stackArgOffset.plus(WORDSIZE);
            }
            stackDepth--;
        }
    }
    // Restore JTOC register
    if (JTOC_REGISTER != null) {
        asm.emitMOV_Reg_Imm_Quad(JTOC_REGISTER, BootRecord.the_boot_record.tocRegister.toLong());
    }
    // START of code sequence to atomically change thread status from
    // IN_JNI to IN_JAVA, looping in a call to
    // RVMThread.leaveJNIBlockedFromJNIFunctionCallMethod if
    // BLOCKED_IN_NATIVE
    // backward branch label
    int retryLabel = asm.getMachineCodeIndex();
    // Restore THREAD_REGISTER from JNIEnvironment
    if (VM.BuildFor32Addr) {
        // pick up arg 0 (from our frame)
        asm.emitMOV_Reg_RegDisp(EBX, EBP, Offset.fromIntSignExtend((startOfStackedArgs - 1) * WORDSIZE));
        asm.emitMOV_Reg_RegDisp(THREAD_REGISTER, EBX, Entrypoints.JNIEnvSavedTRField.getOffset());
    } else {
        // pick up arg 0 (from our frame)
        asm.emitMOV_Reg_RegDisp_Quad(EBX, EBP, Offset.fromIntSignExtend((startOfStackedArgs - 1) * WORDSIZE));
        asm.emitMOV_Reg_RegDisp_Quad(THREAD_REGISTER, EBX, Entrypoints.JNIEnvSavedTRField.getOffset());
    }
    // what we need to keep in mind at this point:
    // - EBX has JNI env (but it's nonvolatile)
    // - EBP has the FP (but it's nonvolatile)
    // - stack has the args but not the locals
    // - TR has been restored
    // attempt to change the thread state to IN_JAVA
    asm.emitMOV_Reg_Imm(T0, RVMThread.IN_JNI);
    asm.emitMOV_Reg_Imm(T1, RVMThread.IN_JAVA);
    asm.emitLockNextInstruction();
    asm.emitCMPXCHG_RegDisp_Reg(THREAD_REGISTER, Entrypoints.execStatusField.getOffset(), T1);
    // if we succeeded, move on, else go into slow path
    ForwardReference doneLeaveJNIRef = asm.forwardJcc(EQ);
    // make the slow call
    asm.generateJTOCcall(Entrypoints.leaveJNIBlockedFromJNIFunctionCallMethod.getOffset());
    // arrive here when we've switched to IN_JAVA
    doneLeaveJNIRef.resolve(asm);
    // END of code sequence to change state from IN_JNI to IN_JAVA
    // status is now IN_JAVA. GC can not occur while we execute on a processor
    // in this state, so it is safe to access fields of objects.
    // RVM TR register has been restored and EBX contains a pointer to
    // the thread's JNIEnvironment.
    // done saving, bump SP to reserve room for the local variables
    // SP should now be at the point normally marked as emptyStackOffset
    int numLocalVariables = method.getLocalWords() - method.getParameterWords();
    // TODO: optimize this space adjustment
    if (VM.BuildFor32Addr) {
        asm.emitSUB_Reg_Imm(SP, (numLocalVariables << LG_WORDSIZE));
    } else {
        asm.emitSUB_Reg_Imm_Quad(SP, (numLocalVariables << LG_WORDSIZE));
    }
    // frame of JNIFunction
    if (VM.BuildFor32Addr) {
        asm.emitMOV_Reg_RegDisp(S0, EBX, Entrypoints.JNITopJavaFPField.getOffset());
    } else {
        asm.emitMOV_Reg_RegDisp_Quad(S0, EBX, Entrypoints.JNITopJavaFPField.getOffset());
    }
    // get offset from current FP and save in hdr of current frame
    if (VM.BuildFor32Addr) {
        asm.emitSUB_Reg_Reg(S0, EBP);
        asm.emitMOV_RegDisp_Reg(EBP, SAVED_JAVA_FP_OFFSET, S0);
    } else {
        asm.emitSUB_Reg_Reg_Quad(S0, EBP);
        asm.emitMOV_RegDisp_Reg_Quad(EBP, SAVED_JAVA_FP_OFFSET, S0);
    }
    // clobber the saved frame pointer with that from the JNIEnvironment (work around for omit-frame-pointer)
    if (VM.BuildFor32Addr) {
        asm.emitMOV_Reg_RegDisp(S0, EBX, Entrypoints.JNIEnvBasePointerOnEntryToNative.getOffset());
        asm.emitMOV_RegInd_Reg(EBP, S0);
    } else {
        asm.emitMOV_Reg_RegDisp_Quad(S0, EBX, Entrypoints.JNIEnvBasePointerOnEntryToNative.getOffset());
        asm.emitMOV_RegInd_Reg_Quad(EBP, S0);
    }
    // put framePointer in Thread following Jikes RVM conventions.
    if (VM.BuildFor32Addr) {
        asm.emitMOV_RegDisp_Reg(THREAD_REGISTER, ArchEntrypoints.framePointerField.getOffset(), EBP);
    } else {
        asm.emitMOV_RegDisp_Reg_Quad(THREAD_REGISTER, ArchEntrypoints.framePointerField.getOffset(), EBP);
    }
// at this point: TR has been restored &
// processor status = IN_JAVA,
// arguments for the call have been setup, space on the stack for locals
// has been acquired.
// finally proceed with the normal Java compiled code
// skip the thread switch test for now, see BaselineCompilerImpl.genThreadSwitchTest(true)
// asm.emitNOP(1); // end of prologue marker
}
Also used : ForwardReference(org.jikesrvm.compilers.common.assembler.ForwardReference) FloatingPointMachineRegister(org.jikesrvm.ia32.RegisterConstants.FloatingPointMachineRegister) GPR(org.jikesrvm.ia32.RegisterConstants.GPR) XMM(org.jikesrvm.ia32.RegisterConstants.XMM) TypeReference(org.jikesrvm.classloader.TypeReference) Offset(org.vmmagic.unboxed.Offset)

Aggregations

ForwardReference (org.jikesrvm.compilers.common.assembler.ForwardReference)3 XMM (org.jikesrvm.ia32.RegisterConstants.XMM)3 TypeReference (org.jikesrvm.classloader.TypeReference)2 FloatingPointMachineRegister (org.jikesrvm.ia32.RegisterConstants.FloatingPointMachineRegister)2 GPR (org.jikesrvm.ia32.RegisterConstants.GPR)2 Offset (org.vmmagic.unboxed.Offset)2 CodeArray (org.jikesrvm.compilers.common.CodeArray)1 Assembler (org.jikesrvm.compilers.common.assembler.ia32.Assembler)1 JNICompiledMethod (org.jikesrvm.jni.JNICompiledMethod)1 Address (org.vmmagic.unboxed.Address)1