/*
 * Copyright (C) 2009 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.
 */

/*
 * This file contains codegen for the Thumb ISA and is intended to be
 * includes by:
 *
 *        Codegen-$(TARGET_ARCH_VARIANT).c
 *
 */

static int coreTemps[] = {r_V0, r_V1, r_A0, r_A1, r_A2, r_A3, r_T0, r_T1, r_T2,
                          r_T3, r_T4, r_T5, r_T6, r_T7, r_T8, r_T9, r_S0, r_S4};
#ifdef __mips_hard_float
static int fpTemps[] = {r_F0, r_F1, r_F2, r_F3, r_F4, r_F5, r_F6, r_F7,
                        r_F8, r_F9, r_F10, r_F11, r_F12, r_F13, r_F14, r_F15};
#endif

static void storePair(CompilationUnit *cUnit, int base, int lowReg,
                      int highReg);
static void loadPair(CompilationUnit *cUnit, int base, int lowReg, int highReg);
static MipsLIR *loadWordDisp(CompilationUnit *cUnit, int rBase, int displacement,
                            int rDest);
static MipsLIR *storeWordDisp(CompilationUnit *cUnit, int rBase,
                             int displacement, int rSrc);
static MipsLIR *genRegRegCheck(CompilationUnit *cUnit,
                              MipsConditionCode cond,
                              int reg1, int reg2, int dOffset,
                              MipsLIR *pcrLabel);
static MipsLIR *loadConstant(CompilationUnit *cUnit, int rDest, int value);

#ifdef __mips_hard_float
static MipsLIR *fpRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
{
    MipsLIR* res = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
    res->operands[0] = rDest;
    res->operands[1] = rSrc;
    if (rDest == rSrc) {
        res->flags.isNop = true;
    } else {
        /* must be both DOUBLE or both not DOUBLE */
        assert(DOUBLEREG(rDest) == DOUBLEREG(rSrc));
        if (DOUBLEREG(rDest)) {
            res->opcode = kMipsFmovd;
        } else {
            if (SINGLEREG(rDest)) {
                if (SINGLEREG(rSrc)) {
                    res->opcode = kMipsFmovs;
                } else {
                    /* note the operands are swapped for the mtc1 instr */
                    res->opcode = kMipsMtc1;
                    res->operands[0] = rSrc;
                    res->operands[1] = rDest;
                }
            } else {
                assert(SINGLEREG(rSrc));
                res->opcode = kMipsMfc1;
            }
        }
    }
    setupResourceMasks(res);
    return res;
}
#endif

/*
 * Load a immediate using a shortcut if possible; otherwise
 * grab from the per-translation literal pool.  If target is
 * a high register, build constant into a low register and copy.
 *
 * No additional register clobbering operation performed. Use this version when
 * 1) rDest is freshly returned from dvmCompilerAllocTemp or
 * 2) The codegen is under fixed register usage
 */
static MipsLIR *loadConstantNoClobber(CompilationUnit *cUnit, int rDest,
                                     int value)
{
    MipsLIR *res;

#ifdef __mips_hard_float
    int rDestSave = rDest;
    int isFpReg = FPREG(rDest);
    if (isFpReg) {
        assert(SINGLEREG(rDest));
        rDest = dvmCompilerAllocTemp(cUnit);
    }
#endif

    /* See if the value can be constructed cheaply */
    if (value == 0) {
        res = newLIR2(cUnit, kMipsMove, rDest, r_ZERO);
    } else if ((value > 0) && (value <= 65535)) {
        res = newLIR3(cUnit, kMipsOri, rDest, r_ZERO, value);
    } else if ((value < 0) && (value >= -32768)) {
        res = newLIR3(cUnit, kMipsAddiu, rDest, r_ZERO, value);
    } else {
        res = newLIR2(cUnit, kMipsLui, rDest, value>>16);
        if (value & 0xffff)
	    newLIR3(cUnit, kMipsOri, rDest, rDest, value);
    }

#ifdef __mips_hard_float
    if (isFpReg) {
        newLIR2(cUnit, kMipsMtc1, rDest, rDestSave);
        dvmCompilerFreeTemp(cUnit, rDest);
    }
#endif

    return res;
}

/*
 * Load an immediate value into a fixed or temp register.  Target
 * register is clobbered, and marked inUse.
 */
static MipsLIR *loadConstant(CompilationUnit *cUnit, int rDest, int value)
{
    if (dvmCompilerIsTemp(cUnit, rDest)) {
        dvmCompilerClobber(cUnit, rDest);
        dvmCompilerMarkInUse(cUnit, rDest);
    }
    return loadConstantNoClobber(cUnit, rDest, value);
}

/*
 * Load a class pointer value into a fixed or temp register.  Target
 * register is clobbered, and marked inUse.
 */
static MipsLIR *loadClassPointer(CompilationUnit *cUnit, int rDest, int value)
{
    MipsLIR *res;
    if (dvmCompilerIsTemp(cUnit, rDest)) {
        dvmCompilerClobber(cUnit, rDest);
        dvmCompilerMarkInUse(cUnit, rDest);
    }
    res = newLIR2(cUnit, kMipsLui, rDest, value>>16);
    if (value & 0xffff)
        newLIR3(cUnit, kMipsOri, rDest, rDest, value);
    return res;
}

static MipsLIR *opNone(CompilationUnit *cUnit, OpKind op)
{
    MipsLIR *res;
    MipsOpCode opcode = kMipsNop;
    switch (op) {
        case kOpUncondBr:
            opcode = kMipsB;
            break;
        default:
            LOGE("Jit: bad case in opNone");
            dvmCompilerAbort(cUnit);
    }
    res = newLIR0(cUnit, opcode);
    return res;
}

static MipsLIR *opCompareBranch(CompilationUnit *cUnit, MipsOpCode opc, int rs, int rt)
{
    MipsLIR *res;
    if (rt < 0) {
      assert(opc >= kMipsBeqz && opc <= kMipsBnez);
      res = newLIR1(cUnit, opc, rs);
    } else  {
      assert(opc == kMipsBeq || opc == kMipsBne);
      res = newLIR2(cUnit, opc, rs, rt);
    }
    return res;
}

static MipsLIR *loadMultiple(CompilationUnit *cUnit, int rBase, int rMask);

static MipsLIR *opReg(CompilationUnit *cUnit, OpKind op, int rDestSrc)
{
    MipsOpCode opcode = kMipsNop;
    switch (op) {
        case kOpBlx:
            opcode = kMipsJalr;
            break;
        default:
            assert(0);
    }
    return newLIR2(cUnit, opcode, r_RA, rDestSrc);
}

static MipsLIR *opRegRegImm(CompilationUnit *cUnit, OpKind op, int rDest,
                           int rSrc1, int value);
static MipsLIR *opRegImm(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
                        int value)
{
    MipsLIR *res;
    bool neg = (value < 0);
    int absValue = (neg) ? -value : value;
    bool shortForm = (absValue & 0xff) == absValue;
    MipsOpCode opcode = kMipsNop;
    switch (op) {
        case kOpAdd:
            return opRegRegImm(cUnit, op, rDestSrc1, rDestSrc1, value);
            break;
        case kOpSub:
            return opRegRegImm(cUnit, op, rDestSrc1, rDestSrc1, value);
            break;
        default:
            LOGE("Jit: bad case in opRegImm");
            dvmCompilerAbort(cUnit);
            break;
    }
    if (shortForm)
        res = newLIR2(cUnit, opcode, rDestSrc1, absValue);
    else {
        int rScratch = dvmCompilerAllocTemp(cUnit);
        res = loadConstant(cUnit, rScratch, value);
        if (op == kOpCmp)
            newLIR2(cUnit, opcode, rDestSrc1, rScratch);
        else
            newLIR3(cUnit, opcode, rDestSrc1, rDestSrc1, rScratch);
    }
    return res;
}

static MipsLIR *opRegRegReg(CompilationUnit *cUnit, OpKind op, int rDest,
                           int rSrc1, int rSrc2)
{
    MipsOpCode opcode = kMipsNop;
    switch (op) {
        case kOpAdd:
            opcode = kMipsAddu;
            break;
        case kOpSub:
            opcode = kMipsSubu;
            break;
        case kOpAnd:
            opcode = kMipsAnd;
            break;
        case kOpMul:
            opcode = kMipsMul;
            break;
        case kOpOr:
            opcode = kMipsOr;
            break;
        case kOpXor:
            opcode = kMipsXor;
            break;
        case kOpLsl:
            opcode = kMipsSllv;
            break;
        case kOpLsr:
            opcode = kMipsSrlv;
            break;
        case kOpAsr:
            opcode = kMipsSrav;
            break;
        default:
            LOGE("Jit: bad case in opRegRegReg");
            dvmCompilerAbort(cUnit);
            break;
    }
    return newLIR3(cUnit, opcode, rDest, rSrc1, rSrc2);
}

static MipsLIR *opRegRegImm(CompilationUnit *cUnit, OpKind op, int rDest,
                           int rSrc1, int value)
{
    MipsLIR *res;
    MipsOpCode opcode = kMipsNop;
    bool shortForm = true;

    switch(op) {
        case kOpAdd:
            if (IS_SIMM16(value)) {
                opcode = kMipsAddiu;
            }
            else {
                shortForm = false;
                opcode = kMipsAddu;
            }
            break;
        case kOpSub:
            if (IS_SIMM16((-value))) {
                value = -value;
                opcode = kMipsAddiu;
            }
            else {
                shortForm = false;
                opcode = kMipsSubu;
            }
            break;
        case kOpLsl:
                assert(value >= 0 && value <= 31);
                opcode = kMipsSll;
                break;
        case kOpLsr:
                assert(value >= 0 && value <= 31);
                opcode = kMipsSrl;
                break;
        case kOpAsr:
                assert(value >= 0 && value <= 31);
                opcode = kMipsSra;
                break;
        case kOpAnd:
            if (IS_UIMM16((value))) {
                opcode = kMipsAndi;
            }
            else {
                shortForm = false;
                opcode = kMipsAnd;
            }
            break;
        case kOpOr:
            if (IS_UIMM16((value))) {
                opcode = kMipsOri;
            }
            else {
                shortForm = false;
                opcode = kMipsOr;
            }
            break;
        case kOpXor:
            if (IS_UIMM16((value))) {
                opcode = kMipsXori;
            }
            else {
                shortForm = false;
                opcode = kMipsXor;
            }
            break;
        case kOpMul:
            shortForm = false;
            opcode = kMipsMul;
            break;
        default:
            LOGE("Jit: bad case in opRegRegImm");
            dvmCompilerAbort(cUnit);
            break;
    }

    if (shortForm)
        res = newLIR3(cUnit, opcode, rDest, rSrc1, value);
    else {
        if (rDest != rSrc1) {
            res = loadConstant(cUnit, rDest, value);
            newLIR3(cUnit, opcode, rDest, rSrc1, rDest);
        } else {
            int rScratch = dvmCompilerAllocTemp(cUnit);
            res = loadConstant(cUnit, rScratch, value);
            newLIR3(cUnit, opcode, rDest, rSrc1, rScratch);
        }
    }
    return res;
}

static MipsLIR *opRegReg(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
                        int rSrc2)
{
    MipsOpCode opcode = kMipsNop;
    MipsLIR *res;
    switch (op) {
        case kOpMov:
            opcode = kMipsMove;
            break;
        case kOpMvn:
            return newLIR3(cUnit, kMipsNor, rDestSrc1, rSrc2, r_ZERO);
        case kOpNeg:
            return newLIR3(cUnit, kMipsSubu, rDestSrc1, r_ZERO, rSrc2);
        case kOpAdd:
        case kOpAnd:
        case kOpMul:
        case kOpOr:
        case kOpSub:
        case kOpXor:
            return opRegRegReg(cUnit, op, rDestSrc1, rDestSrc1, rSrc2);
        case kOp2Byte:
#if __mips_isa_rev>=2
            res = newLIR2(cUnit, kMipsSeb, rDestSrc1, rSrc2);
#else
            res = opRegRegImm(cUnit, kOpLsl, rDestSrc1, rSrc2, 24);
            opRegRegImm(cUnit, kOpAsr, rDestSrc1, rDestSrc1, 24);
#endif
            return res;
        case kOp2Short:
#if __mips_isa_rev>=2
            res = newLIR2(cUnit, kMipsSeh, rDestSrc1, rSrc2);
#else
            res = opRegRegImm(cUnit, kOpLsl, rDestSrc1, rSrc2, 16);
            opRegRegImm(cUnit, kOpAsr, rDestSrc1, rDestSrc1, 16);
#endif
            return res;
        case kOp2Char:
             return newLIR3(cUnit, kMipsAndi, rDestSrc1, rSrc2, 0xFFFF);
        default:
            LOGE("Jit: bad case in opRegReg");
            dvmCompilerAbort(cUnit);
            break;
    }
    return newLIR2(cUnit, opcode, rDestSrc1, rSrc2);
}

static MipsLIR *loadConstantValueWide(CompilationUnit *cUnit, int rDestLo,
                                     int rDestHi, int valLo, int valHi)
{
    MipsLIR *res;
    res = loadConstantNoClobber(cUnit, rDestLo, valLo);
    loadConstantNoClobber(cUnit, rDestHi, valHi);
    return res;
}

/* Load value from base + scaled index. */
static MipsLIR *loadBaseIndexed(CompilationUnit *cUnit, int rBase,
                               int rIndex, int rDest, int scale, OpSize size)
{
    MipsLIR *first = NULL;
    MipsLIR *res;
    MipsOpCode opcode = kMipsNop;
    int tReg = dvmCompilerAllocTemp(cUnit);

#ifdef __mips_hard_float
    if (FPREG(rDest)) {
        assert(SINGLEREG(rDest));
        assert((size == kWord) || (size == kSingle));
        size = kSingle;
    } else {
        if (size == kSingle)
            size = kWord;
    }
#endif

    if (!scale) {
        first = newLIR3(cUnit, kMipsAddu, tReg , rBase, rIndex);
    } else {
        first = opRegRegImm(cUnit, kOpLsl, tReg, rIndex, scale);
        newLIR3(cUnit, kMipsAddu, tReg , rBase, tReg);
    }

    switch (size) {
#ifdef __mips_hard_float
        case kSingle:
            opcode = kMipsFlwc1;
            break;
#endif
        case kWord:
            opcode = kMipsLw;
            break;
        case kUnsignedHalf:
            opcode = kMipsLhu;
            break;
        case kSignedHalf:
            opcode = kMipsLh;
            break;
        case kUnsignedByte:
            opcode = kMipsLbu;
            break;
        case kSignedByte:
            opcode = kMipsLb;
            break;
        default:
            LOGE("Jit: bad case in loadBaseIndexed");
            dvmCompilerAbort(cUnit);
    }

    res = newLIR3(cUnit, opcode, rDest, 0, tReg);
#if defined(WITH_SELF_VERIFICATION)
    if (cUnit->heapMemOp)
        res->flags.insertWrapper = true;
#endif
    dvmCompilerFreeTemp(cUnit, tReg);
    return (first) ? first : res;
}

/* store value base base + scaled index. */
static MipsLIR *storeBaseIndexed(CompilationUnit *cUnit, int rBase,
                                int rIndex, int rSrc, int scale, OpSize size)
{
    MipsLIR *first = NULL;
    MipsLIR *res;
    MipsOpCode opcode = kMipsNop;
    int rNewIndex = rIndex;
    int tReg = dvmCompilerAllocTemp(cUnit);

#ifdef __mips_hard_float
    if (FPREG(rSrc)) {
        assert(SINGLEREG(rSrc));
        assert((size == kWord) || (size == kSingle));
        size = kSingle;
    } else {
        if (size == kSingle)
            size = kWord;
    }
#endif

    if (!scale) {
        first = newLIR3(cUnit, kMipsAddu, tReg , rBase, rIndex);
    } else {
        first = opRegRegImm(cUnit, kOpLsl, tReg, rIndex, scale);
        newLIR3(cUnit, kMipsAddu, tReg , rBase, tReg);
    }

    switch (size) {
#ifdef __mips_hard_float
        case kSingle:
            opcode = kMipsFswc1;
            break;
#endif
        case kWord:
            opcode = kMipsSw;
            break;
        case kUnsignedHalf:
        case kSignedHalf:
            opcode = kMipsSh;
            break;
        case kUnsignedByte:
        case kSignedByte:
            opcode = kMipsSb;
            break;
        default:
            LOGE("Jit: bad case in storeBaseIndexed");
            dvmCompilerAbort(cUnit);
    }
    res = newLIR3(cUnit, opcode, rSrc, 0, tReg);
#if defined(WITH_SELF_VERIFICATION)
    if (cUnit->heapMemOp)
        res->flags.insertWrapper = true;
#endif
    dvmCompilerFreeTemp(cUnit, rNewIndex);
    return first;
}

static MipsLIR *loadMultiple(CompilationUnit *cUnit, int rBase, int rMask)
{
    int i;
    int loadCnt = 0;
    MipsLIR *res = NULL ;
    genBarrier(cUnit);

    for (i = 0; i < 8; i++, rMask >>= 1) {
        if (rMask & 0x1) { /* map r0 to MIPS r_A0 */
            newLIR3(cUnit, kMipsLw, i+r_A0, loadCnt*4, rBase);
            loadCnt++;
        }
    }

    if (loadCnt) {/* increment after */
        newLIR3(cUnit, kMipsAddiu, rBase, rBase, loadCnt*4);
    }

#if defined(WITH_SELF_VERIFICATION)
    if (cUnit->heapMemOp)
        res->flags.insertWrapper = true;
#endif
    genBarrier(cUnit);
    return res; /* NULL always returned which should be ok since no callers use it */
}

static MipsLIR *storeMultiple(CompilationUnit *cUnit, int rBase, int rMask)
{
    int i;
    int storeCnt = 0;
    MipsLIR *res = NULL ;
    genBarrier(cUnit);

    for (i = 0; i < 8; i++, rMask >>= 1) {
        if (rMask & 0x1) { /* map r0 to MIPS r_A0 */
            newLIR3(cUnit, kMipsSw, i+r_A0, storeCnt*4, rBase);
            storeCnt++;
        }
    }

    if (storeCnt) { /* increment after */
        newLIR3(cUnit, kMipsAddiu, rBase, rBase, storeCnt*4);
    }

#if defined(WITH_SELF_VERIFICATION)
    if (cUnit->heapMemOp)
        res->flags.insertWrapper = true;
#endif
    genBarrier(cUnit);
    return res; /* NULL always returned which should be ok since no callers use it */
}

static MipsLIR *loadBaseDispBody(CompilationUnit *cUnit, MIR *mir, int rBase,
                                int displacement, int rDest, int rDestHi,
                                OpSize size, int sReg)
/*
 * Load value from base + displacement.  Optionally perform null check
 * on base (which must have an associated sReg and MIR).  If not
 * performing null check, incoming MIR can be null. IMPORTANT: this
 * code must not allocate any new temps.  If a new register is needed
 * and base and dest are the same, spill some other register to
 * rlp and then restore.
 */
{
    MipsLIR *res;
    MipsLIR *load = NULL;
    MipsLIR *load2 = NULL;
    MipsOpCode opcode = kMipsNop;
    bool shortForm = IS_SIMM16(displacement);
    bool pair = false;

    switch (size) {
        case kLong:
        case kDouble:
            pair = true;
            opcode = kMipsLw;
#ifdef __mips_hard_float
            if (FPREG(rDest)) {
                opcode = kMipsFlwc1;
                if (DOUBLEREG(rDest)) {
                    rDest = rDest - FP_DOUBLE;
                } else {
                    assert(FPREG(rDestHi));
                    assert(rDest == (rDestHi - 1));
                }
                rDestHi = rDest + 1;
            }
#endif
            shortForm = IS_SIMM16_2WORD(displacement);
            assert((displacement & 0x3) == 0);
            break;
        case kWord:
        case kSingle:
            opcode = kMipsLw;
#ifdef __mips_hard_float
            if (FPREG(rDest)) {
                opcode = kMipsFlwc1;
                assert(SINGLEREG(rDest));
            }
#endif
            assert((displacement & 0x3) == 0);
            break;
        case kUnsignedHalf:
            opcode = kMipsLhu;
            assert((displacement & 0x1) == 0);
            break;
        case kSignedHalf:
            opcode = kMipsLh;
            assert((displacement & 0x1) == 0);
            break;
        case kUnsignedByte:
            opcode = kMipsLbu;
            break;
        case kSignedByte:
            opcode = kMipsLb;
            break;
        default:
            LOGE("Jit: bad case in loadBaseIndexedBody");
            dvmCompilerAbort(cUnit);
    }

    if (shortForm) {
        if (!pair) {
            load = res = newLIR3(cUnit, opcode, rDest, displacement, rBase);
        } else {
            load = res = newLIR3(cUnit, opcode, rDest, displacement + LOWORD_OFFSET, rBase);
            load2 = newLIR3(cUnit, opcode, rDestHi, displacement + HIWORD_OFFSET, rBase);
        }
    } else {
        if (pair) {
            int rTmp = dvmCompilerAllocFreeTemp(cUnit);
            res = opRegRegImm(cUnit, kOpAdd, rTmp, rBase, displacement);
            load = newLIR3(cUnit, opcode, rDest, LOWORD_OFFSET, rTmp);
            load2 = newLIR3(cUnit, opcode, rDestHi, HIWORD_OFFSET, rTmp);
            dvmCompilerFreeTemp(cUnit, rTmp);
        } else {
            int rTmp = (rBase == rDest) ? dvmCompilerAllocFreeTemp(cUnit)
                                        : rDest;
            res = loadConstant(cUnit, rTmp, displacement);
            load = newLIR3(cUnit, opcode, rDest, rBase, rTmp);
            if (rTmp != rDest)
                dvmCompilerFreeTemp(cUnit, rTmp);
        }
    }

    if (rBase == rFP) {
        if (load != NULL)
            annotateDalvikRegAccess(load, (displacement + (pair ? LOWORD_OFFSET : 0)) >> 2,
                                    true /* isLoad */);
        if (load2 != NULL)
            annotateDalvikRegAccess(load2, (displacement + HIWORD_OFFSET) >> 2,
                                    true /* isLoad */);
    }
#if defined(WITH_SELF_VERIFICATION)
    if (load != NULL && cUnit->heapMemOp)
        load->flags.insertWrapper = true;
    if (load2 != NULL && cUnit->heapMemOp)
        load2->flags.insertWrapper = true;
#endif
    return load;
}

static MipsLIR *loadBaseDisp(CompilationUnit *cUnit, MIR *mir, int rBase,
                            int displacement, int rDest, OpSize size,
                            int sReg)
{
    return loadBaseDispBody(cUnit, mir, rBase, displacement, rDest, -1,
                            size, sReg);
}

static MipsLIR *loadBaseDispWide(CompilationUnit *cUnit, MIR *mir, int rBase,
                                int displacement, int rDestLo, int rDestHi,
                                int sReg)
{
    return loadBaseDispBody(cUnit, mir, rBase, displacement, rDestLo, rDestHi,
                            kLong, sReg);
}

static MipsLIR *storeBaseDispBody(CompilationUnit *cUnit, int rBase,
                                 int displacement, int rSrc, int rSrcHi,
                                 OpSize size)
{
    MipsLIR *res;
    MipsLIR *store = NULL;
    MipsLIR *store2 = NULL;
    MipsOpCode opcode = kMipsNop;
    bool shortForm = IS_SIMM16(displacement);
    bool pair = false;

    switch (size) {
        case kLong:
        case kDouble:
            pair = true;
            opcode = kMipsSw;
#ifdef __mips_hard_float
            if (FPREG(rSrc)) {
                opcode = kMipsFswc1;
                if (DOUBLEREG(rSrc)) {
                    rSrc = rSrc - FP_DOUBLE;
                } else {
                    assert(FPREG(rSrcHi));
                    assert(rSrc == (rSrcHi - 1));
                }
                rSrcHi = rSrc + 1;
            }
#endif
            shortForm = IS_SIMM16_2WORD(displacement);
            assert((displacement & 0x3) == 0);
            break;
        case kWord:
        case kSingle:
            opcode = kMipsSw;
#ifdef __mips_hard_float
            if (FPREG(rSrc)) {
                opcode = kMipsFswc1;
                assert(SINGLEREG(rSrc));
            }
#endif
            assert((displacement & 0x3) == 0);
            break;
        case kUnsignedHalf:
        case kSignedHalf:
            opcode = kMipsSh;
            assert((displacement & 0x1) == 0);
            break;
        case kUnsignedByte:
        case kSignedByte:
            opcode = kMipsSb;
            break;
        default:
            LOGE("Jit: bad case in storeBaseIndexedBody");
            dvmCompilerAbort(cUnit);
    }

    if (shortForm) {
        if (!pair) {
            store = res = newLIR3(cUnit, opcode, rSrc, displacement, rBase);
        } else {
            store = res = newLIR3(cUnit, opcode, rSrc, displacement + LOWORD_OFFSET, rBase);
            store2 = newLIR3(cUnit, opcode, rSrcHi, displacement + HIWORD_OFFSET, rBase);
        }
    } else {
        int rScratch = dvmCompilerAllocTemp(cUnit);
        res = opRegRegImm(cUnit, kOpAdd, rScratch, rBase, displacement);
        if (!pair) {
            store =  newLIR3(cUnit, opcode, rSrc, 0, rScratch);
        } else {
            store =  newLIR3(cUnit, opcode, rSrc, LOWORD_OFFSET, rScratch);
            store2 = newLIR3(cUnit, opcode, rSrcHi, HIWORD_OFFSET, rScratch);
        }
        dvmCompilerFreeTemp(cUnit, rScratch);
    }

    if (rBase == rFP) {
        if (store != NULL)
            annotateDalvikRegAccess(store, (displacement + (pair ? LOWORD_OFFSET : 0)) >> 2,
                                    false /* isLoad */);
        if (store2 != NULL)
            annotateDalvikRegAccess(store2, (displacement + HIWORD_OFFSET) >> 2,
                                    false /* isLoad */);
    }

#if defined(WITH_SELF_VERIFICATION)
    if (store != NULL && cUnit->heapMemOp)
        store->flags.insertWrapper = true;
    if (store2 != NULL && cUnit->heapMemOp)
        store2->flags.insertWrapper = true;
#endif
    return res;
}

static MipsLIR *storeBaseDisp(CompilationUnit *cUnit, int rBase,
                             int displacement, int rSrc, OpSize size)
{
    return storeBaseDispBody(cUnit, rBase, displacement, rSrc, -1, size);
}

static MipsLIR *storeBaseDispWide(CompilationUnit *cUnit, int rBase,
                                 int displacement, int rSrcLo, int rSrcHi)
{
    return storeBaseDispBody(cUnit, rBase, displacement, rSrcLo, rSrcHi, kLong);
}

static void storePair(CompilationUnit *cUnit, int base, int lowReg, int highReg)
{
    storeWordDisp(cUnit, base, LOWORD_OFFSET, lowReg);
    storeWordDisp(cUnit, base, HIWORD_OFFSET, highReg);
}

static void loadPair(CompilationUnit *cUnit, int base, int lowReg, int highReg)
{
    loadWordDisp(cUnit, base, LOWORD_OFFSET , lowReg);
    loadWordDisp(cUnit, base, HIWORD_OFFSET , highReg);
}

static MipsLIR* genRegCopyNoInsert(CompilationUnit *cUnit, int rDest, int rSrc)
{
    MipsLIR* res;
    MipsOpCode opcode;
#ifdef __mips_hard_float
    if (FPREG(rDest) || FPREG(rSrc))
        return fpRegCopy(cUnit, rDest, rSrc);
#endif
    res = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
    opcode = kMipsMove;
    assert(LOWREG(rDest) && LOWREG(rSrc));
    res->operands[0] = rDest;
    res->operands[1] = rSrc;
    res->opcode = opcode;
    setupResourceMasks(res);
    if (rDest == rSrc) {
        res->flags.isNop = true;
    }
    return res;
}

static MipsLIR* genRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
{
    MipsLIR *res = genRegCopyNoInsert(cUnit, rDest, rSrc);
    dvmCompilerAppendLIR(cUnit, (LIR*)res);
    return res;
}

static void genRegCopyWide(CompilationUnit *cUnit, int destLo, int destHi,
                           int srcLo, int srcHi)
{
#ifdef __mips_hard_float
    bool destFP = FPREG(destLo) && FPREG(destHi);
    bool srcFP = FPREG(srcLo) && FPREG(srcHi);
    assert(FPREG(srcLo) == FPREG(srcHi));
    assert(FPREG(destLo) == FPREG(destHi));
    if (destFP) {
        if (srcFP) {
            genRegCopy(cUnit, S2D(destLo, destHi), S2D(srcLo, srcHi));
        } else {
           /* note the operands are swapped for the mtc1 instr */
            newLIR2(cUnit, kMipsMtc1, srcLo, destLo);
            newLIR2(cUnit, kMipsMtc1, srcHi, destHi);
        }
    } else {
        if (srcFP) {
            newLIR2(cUnit, kMipsMfc1, destLo, srcLo);
            newLIR2(cUnit, kMipsMfc1, destHi, srcHi);
        } else {
            // Handle overlap
            if (srcHi == destLo) {
                genRegCopy(cUnit, destHi, srcHi);
                genRegCopy(cUnit, destLo, srcLo);
            } else {
                genRegCopy(cUnit, destLo, srcLo);
                genRegCopy(cUnit, destHi, srcHi);
            }
        }
    }
#else
    // Handle overlap
    if (srcHi == destLo) {
        genRegCopy(cUnit, destHi, srcHi);
        genRegCopy(cUnit, destLo, srcLo);
    } else {
        genRegCopy(cUnit, destLo, srcLo);
        genRegCopy(cUnit, destHi, srcHi);
    }
#endif
}

static inline MipsLIR *genRegImmCheck(CompilationUnit *cUnit,
                                     MipsConditionCode cond, int reg,
                                     int checkValue, int dOffset,
                                     MipsLIR *pcrLabel)
{
    MipsLIR *branch = NULL;

    if (checkValue == 0) {
        MipsOpCode opc = kMipsNop;
        if (cond == kMipsCondEq) {
            opc = kMipsBeqz;
	} else if (cond == kMipsCondNe) {
            opc = kMipsBnez;
        } else if (cond == kMipsCondLt || cond == kMipsCondMi) {
            opc = kMipsBltz;
        } else if (cond == kMipsCondLe) {
            opc = kMipsBlez;
        } else if (cond == kMipsCondGt) {
            opc = kMipsBgtz;
        } else if (cond == kMipsCondGe) {
            opc = kMipsBgez;
        } else {
            LOGE("Jit: bad case in genRegImmCheck");
            dvmCompilerAbort(cUnit);
        }
        branch = opCompareBranch(cUnit, opc, reg, -1);
    } else if (IS_SIMM16(checkValue)) {
        if (cond == kMipsCondLt) {
            int tReg = dvmCompilerAllocTemp(cUnit);
            newLIR3(cUnit, kMipsSlti, tReg, reg, checkValue);
            branch = opCompareBranch(cUnit, kMipsBne, tReg, r_ZERO);
            dvmCompilerFreeTemp(cUnit, tReg);
        } else {
            LOGE("Jit: bad case in genRegImmCheck");
            dvmCompilerAbort(cUnit);
        }
    } else {
        LOGE("Jit: bad case in genRegImmCheck");
        dvmCompilerAbort(cUnit);
    }

    if (cUnit->jitMode == kJitMethod) {
        BasicBlock *bb = cUnit->curBlock;
        if (bb->taken) {
            MipsLIR  *exceptionLabel = (MipsLIR *) cUnit->blockLabelList;
            exceptionLabel += bb->taken->id;
            branch->generic.target = (LIR *) exceptionLabel;
            return exceptionLabel;
        } else {
            LOGE("Catch blocks not handled yet");
            dvmAbort();
            return NULL;
        }
    } else {
        return genCheckCommon(cUnit, dOffset, branch, pcrLabel);
    }
}

#if defined(WITH_SELF_VERIFICATION)
static void genSelfVerificationPreBranch(CompilationUnit *cUnit,
                                         MipsLIR *origLIR) {
// DOUGLAS - this still needs to be implemented for MIPS.
#if 0
    /*
     * We need two separate pushes, since we want r5 to be pushed first.
     * Store multiple will push LR first.
     */
    MipsLIR *pushFP = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
    pushFP->opcode = kThumbPush;
    pushFP->operands[0] = 1 << r5FP;
    setupResourceMasks(pushFP);
    dvmCompilerInsertLIRBefore((LIR *) origLIR, (LIR *) pushFP);

    MipsLIR *pushLR = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
    pushLR->opcode = kThumbPush;
    /* Thumb push can handle LR, but is encoded differently at bit 8 */
    pushLR->operands[0] = 1 << 8;
    setupResourceMasks(pushLR);
    dvmCompilerInsertLIRBefore((LIR *) origLIR, (LIR *) pushLR);
#endif
}

static void genSelfVerificationPostBranch(CompilationUnit *cUnit,
                                         MipsLIR *origLIR) {
// DOUGLAS - this still needs to be implemented for MIPS.
#if 0
    /*
     * Since Thumb cannot pop memory content into LR, we have to pop LR
     * to a temp first (r5 in this case). Then we move r5 to LR, then pop the
     * original r5 from stack.
     */
    /* Pop memory content(LR) into r5 first */
    MipsLIR *popForLR = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
    popForLR->opcode = kThumbPop;
    popForLR->operands[0] = 1 << r5FP;
    setupResourceMasks(popForLR);
    dvmCompilerInsertLIRAfter((LIR *) origLIR, (LIR *) popForLR);

    MipsLIR *copy = genRegCopyNoInsert(cUnit, r14lr, r5FP);
    dvmCompilerInsertLIRAfter((LIR *) popForLR, (LIR *) copy);

    /* Now restore the original r5 */
    MipsLIR *popFP = (MipsLIR *) dvmCompilerNew(sizeof(MipsLIR), true);
    popFP->opcode = kThumbPop;
    popFP->operands[0] = 1 << r5FP;
    setupResourceMasks(popFP);
    dvmCompilerInsertLIRAfter((LIR *) copy, (LIR *) popFP);
#endif
}
#endif
