/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#include "Lower.h"
#include "NcgAot.h"
#include "NcgHelper.h"

//returns # of ops generated by this function
//entries relocatable: eip + relativePC
int get_eip_API() {
    call("ncgGetEIP");//%edx //will push eip to stack
    return 1;
}
#define NEW_EXPORT_PC
//!update current PC in the stack frame with %eip

//!
int export_pc() {
    /* for trace-based JIT, pc points to bytecode
       for NCG, pc points to native code */
    move_imm_to_mem(OpndSize_32, (int)rPC,
                    -sizeofStackSaveArea+offStackSaveArea_localRefTop, PhysicalReg_FP, true);
    return 1; //return number of ops
}

/* jump from JIT'ed code to interpreter without chaining */
int jumpToInterpNoChain() {
    typedef void (*vmHelper)(int);
    vmHelper funcPtr = dvmJitToInterpNoChain;
    move_imm_to_reg(OpndSize_32, (int)funcPtr, C_SCRATCH_1, isScratchPhysical);

    unconditional_jump_reg(C_SCRATCH_1, isScratchPhysical);
    if(gDvm.executionMode == kExecutionModeNcgO1) touchEax();
    return 0;
}

/* jump from JIT'ed code to interpreter becaues of exception */
int jumpToInterpPunt() {
    typedef void (*vmHelper)(int);
    vmHelper funcPtr = dvmJitToInterpPunt;
    move_imm_to_reg(OpndSize_32, (int)funcPtr, C_SCRATCH_1, isScratchPhysical);

    unconditional_jump_reg(C_SCRATCH_1, isScratchPhysical);
    //if(gDvm.executionMode == kExecutionModeNcgO1) touchEax();
    return 0;
}

/* jump to common_exceptionThrown from JIT'ed code */
int jumpToExceptionThrown(int exceptionNum) {
    if(gDvm.executionMode == kExecutionModeNcgO1) {
        rememberState(exceptionNum);
        export_pc();
        constVREndOfBB();
        beforeCall("exception"); //dump GG, GL VRs
    }

    typedef void (*vmHelper)(int);
    vmHelper funcPtr = dvmJitToExceptionThrown;
    move_imm_to_reg(OpndSize_32, (int)funcPtr, C_SCRATCH_1, isScratchPhysical);
    unconditional_jump_reg(C_SCRATCH_1, isScratchPhysical);

    if(gDvm.executionMode == kExecutionModeNcgO1) {
        goToState(exceptionNum);
    }
    return 0;
}

//! generate native code to call dvmNcgInvokeInterpreter

//!the interpreter will start execution from %eax
int invokeInterpreter(bool fromApp)
{
    typedef void (*vmHelper)(int);
    vmHelper funcPtr = dvmNcgInvokeInterpreter;

    move_imm_to_reg(OpndSize_32, (int)funcPtr, C_SCRATCH_1, isScratchPhysical);

    unconditional_jump_reg(C_SCRATCH_1, isScratchPhysical);
    if(gDvm.executionMode == kExecutionModeNcgO1) touchEax();
    return 0;
}

//!work to do before calling a function pointer with code cache enabled

//!
void callFuncPtr(int funcPtr, const char* funcName) {

    move_imm_to_reg(OpndSize_32, (int)funcPtr, C_SCRATCH_1, isScratchPhysical);
    call_reg(C_SCRATCH_1, isScratchPhysical);
}
//.const_string_resolve: input in %eax, output in %eax
//.const_string_helper:
//.class_resolve: input in %eax, output in %eax
int call_helper_API(const char* helperName) {
    call(helperName);
    return 1;
}

/* check whether we are throwing an exception */
bool jumpToException(const char* target) {
    bool isException = false;
    if(!strncmp(target, "common_err", 10)) isException = true;
    if(!strncmp(target, "common_throw", 12)) isException = true;
    if(!strncmp(target, "common_exception", 16)) isException = true;
    return isException;
}

int conditional_jump_global_API(
                                ConditionCode cc, const char* target,
                                bool isShortTerm) {
    if(jumpToException(target) && currentExceptionBlockIdx >= 0) { //jump to the exceptionThrow block
        condJumpToBasicBlock(stream, cc, currentExceptionBlockIdx);
        return 1; //return number of ops
    }
    conditional_jump(cc, target, isShortTerm);
    return 1;
}
int unconditional_jump_global_API(
                                  const char* target, bool isShortTerm) {
    if(jumpToException(target) && currentExceptionBlockIdx >= 0) { //jump to the exceptionThrow block
        jumpToBasicBlock(stream, currentExceptionBlockIdx);
        return 1; //return number of ops
    }
    unconditional_jump(target, isShortTerm);
    return 1;
}
int getGlobalDataAddr(const char* dataName) {
    int dataAddr = -1;
    if(!strcmp(dataName, "doubNeg")) dataAddr = LdoubNeg;
    else if(!strcmp(dataName, "intMax")) dataAddr = LintMax;
    else if(!strcmp(dataName, "intMin")) dataAddr = LintMin;
    else if(!strcmp(dataName, "valueNanLong")) dataAddr = LvalueNanLong;
    else if(!strcmp(dataName, "valuePosInfLong")) dataAddr = LvaluePosInfLong;
    else if(!strcmp(dataName, "valueNegInfLong")) dataAddr = LvalueNegInfLong;
    else if(!strcmp(dataName, "shiftMask")) dataAddr = LshiftMask;
    else if(!strcmp(dataName, "value64")) dataAddr = Lvalue64;
    else if(!strcmp(dataName, "64bits")) dataAddr = L64bits;
    else if(!strcmp(dataName, "strClassCastExceptionPtr")) dataAddr = LstrClassCastExceptionPtr;
    else if(!strcmp(dataName, "strInstantiationError")) dataAddr = LstrInstantiationErrorPtr;
    else if(!strcmp(dataName, "gDvmInlineOpsTable")) dataAddr = (int)gDvmInlineOpsTable;
    else LOGE("ERROR: global data %s not supported\n", dataName);
    return dataAddr;
}

//for shared code cache, we use scratchRegs[0] & [1]
int load_imm_global_data_API(const char* dataName,
                         OpndSize size,
                         int reg, bool isPhysical) {

    //find the address from name
    int dataAddr = getGlobalDataAddr(dataName);
    move_imm_to_reg(size, dataAddr, reg, isPhysical);
    return 0;
}
//for shared code cache, we use scratchRegs[0] & [1] & [2]
//FIXME: [2] is assumed to be hard-coded register
int load_global_data_API(const char* dataName,
                         OpndSize size,
                         int reg, bool isPhysical) {

    //find the address from name
    int dataAddr = getGlobalDataAddr(dataName);
    move_mem_to_reg(size, dataAddr, PhysicalReg_Null, true, reg, isPhysical);
    return 0;
}
int load_sd_global_data_API(const char* dataName,
                            int reg, bool isPhysical) {

    //find the address from name
    int dataAddr = getGlobalDataAddr(dataName);
    move_sd_mem_to_reg(dataAddr, PhysicalReg_Null, true, reg, isPhysical);
    return 0;
}

int load_fp_stack_global_data_API(const char* dataName,
                                  OpndSize size) {

    int dataAddr = getGlobalDataAddr(dataName);
    load_int_fp_stack_imm(size, dataAddr); //fildl
    return 0;
}
