Thumb/Thumb2 instruction selection rework.

Change-id: I7428278f07f49e675d0271c58b3cbf1f6a4e9da1
diff --git a/vm/compiler/codegen/arm/ThumbUtil.c b/vm/compiler/codegen/arm/ThumbUtil.c
index 8be50ad..cde1f71 100644
--- a/vm/compiler/codegen/arm/ThumbUtil.c
+++ b/vm/compiler/codegen/arm/ThumbUtil.c
@@ -16,46 +16,83 @@
 
 /*
  * This file contains codegen for the Thumb ISA and is intended to be
- * includes by:and support common to all supported
+ * includes by:
  *
  *        Codegen-$(TARGET_ARCH_VARIANT).c
  *
  */
 
 #include "Codegen.h"
+/* Forward decls */
+static ArmLIR *genNullCheck(CompilationUnit *cUnit, int vReg, int mReg,
+                            int dOffset, ArmLIR *pcrLabel);
+static ArmLIR *loadValueAddress(CompilationUnit *cUnit, int vSrc, int rDest);
+static ArmLIR *loadValue(CompilationUnit *cUnit, int vSrc, int rDest);
+static ArmLIR *loadWordDisp(CompilationUnit *cUnit, int rBase,
+                            int displacement, int rDest);
+static ArmLIR *storeWordDisp(CompilationUnit *cUnit, int rBase,
+                             int displacement, int rSrc, int rScratch);
+static ArmLIR *storeValue(CompilationUnit *cUnit, int rSrc, int vDest,
+                          int rScratch);
+static ArmLIR *genConditionalBranch(CompilationUnit *cUnit,
+                                    ArmConditionCode cond,
+                                    ArmLIR *target);
+static ArmLIR *genUnconditionalBranch(CompilationUnit *cUnit, ArmLIR *target);
+static ArmLIR *loadValuePair(CompilationUnit *cUnit, int vSrc, int rDestLo,
+                             int rDestHi);
+static ArmLIR *storeValuePair(CompilationUnit *cUnit, int rSrcLo, int rSrcHi,
+                              int vDest, int rScratch);
+static ArmLIR *genBoundsCheck(CompilationUnit *cUnit, int rIndex,
+                              int rBound, int dOffset, ArmLIR *pcrLabel);
+static ArmLIR *genRegCopy(CompilationUnit *cUnit, int rDest, int rSrc);
+
 
 /* Routines which must be supplied here */
-static void loadConstant(CompilationUnit *cUnit, int rDest, int value);
-static void genExportPC(CompilationUnit *cUnit, MIR *mir, int rDPC, int rAddr);
-static void genConditionalBranch(CompilationUnit *cUnit,
-                                 ArmConditionCode cond,
-                                 ArmLIR *target);
-static ArmLIR *genUnconditionalBranch(CompilationUnit *cUnit, ArmLIR *target);
-static void loadValuePair(CompilationUnit *cUnit, int vSrc, int rDestLo,
-                          int rDestHi);
-static void storeValuePair(CompilationUnit *cUnit, int rSrcLo, int rSrcHi,
-                           int vDest, int rScratch);
-static void loadValueAddress(CompilationUnit *cUnit, int vSrc, int vDest);
-static void loadValue(CompilationUnit *cUnit, int vSrc, int rDest);
-static void loadWordDisp(CompilationUnit *cUnit, int rBase, int displacement,
-                         int rDest);
-static void storeValue(CompilationUnit *cUnit, int rSrc, int vDest,
-                       int rScratch);
+static ArmLIR *loadConstant(CompilationUnit *cUnit, int rDest, int value);
+static ArmLIR *genExportPC(CompilationUnit *cUnit, MIR *mir, int rDPC,
+                           int rAddr);
+static ArmLIR *loadBaseDisp(CompilationUnit *cUnit, MIR *mir, int rBase,
+                            int displacement, int rDest, OpSize size,
+                            bool nullCheck, int vReg);
+static ArmLIR *storeBaseDisp(CompilationUnit *cUnit, int rBase,
+                             int displacement, int rSrc, OpSize size,
+                             int rScratch);
 static inline ArmLIR *genRegImmCheck(CompilationUnit *cUnit,
-                                         ArmConditionCode cond, int reg,
-                                         int checkValue, int dOffset,
-                                         ArmLIR *pcrLabel);
+                                     ArmConditionCode cond, int reg,
+                                     int checkValue, int dOffset,
+                                     ArmLIR *pcrLabel);
 ArmLIR* dvmCompilerRegCopy(CompilationUnit *cUnit, int rDest, int rSrc);
+static ArmLIR *loadMultiple(CompilationUnit *cUnit, int rBase, int rMask);
+static ArmLIR *storeMultiple(CompilationUnit *cUnit, int rBase, int rMask);
 
-/*****************************************************************************/
+static ArmLIR *opNone(CompilationUnit *cUnit, OpKind op);
+static ArmLIR *opImm(CompilationUnit *cUnit, OpKind op, int value);
+static ArmLIR *opImmImm(CompilationUnit *cUnit, OpKind op, int value1,
+                        int value2);
+static ArmLIR *opReg(CompilationUnit *cUnit, OpKind op, int rDestSrc);
+static ArmLIR *opRegReg(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+                        int rSrc2);
+static ArmLIR *opRegImm(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+                        int value, int rScratch);
+static ArmLIR *opRegRegImm(CompilationUnit *cUnit, OpKind op, int rDest,
+                           int rSrc1, int value, int rScratch);
+static ArmLIR *opRegRegReg(CompilationUnit *cUnit, OpKind op, int rDest,
+                           int rSrc1, int rSrc2);
+static ArmLIR *loadBaseIndexed(CompilationUnit *cUnit, int rBase,
+                               int rIndex, int rDest, int scale, OpSize size);
+
+static bool genInlinedStringLength(CompilationUnit *cUnit, MIR *mir);
+static bool genInlinedStringCharAt(CompilationUnit *cUnit, MIR *mir);
+static bool genInlinedAbsInt(CompilationUnit *cUnit, MIR *mir);
+static bool genInlinedAbsFloat(CompilationUnit *cUnit, MIR *mir);
+static bool genInlinedAbsDouble(CompilationUnit *cUnit, MIR *mir);
+static bool genInlinedMinMaxInt(CompilationUnit *cUnit, MIR *mir, bool isMin);
+static bool genInlinedAbsLong(CompilationUnit *cUnit, MIR *mir);
 
 /*
  * Support for register allocation
  */
 
-/* non-existent register */
-#define vNone   (-1)
-
 /* get the next register in r0..r3 in a round-robin fashion */
 #define NEXT_REG(reg) ((reg + 1) & 3)
 /*
@@ -131,15 +168,25 @@
 
 }
 
-/*****************************************************************************/
-
 ArmLIR* dvmCompilerRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
 {
-    ArmLIR* res = dvmCompilerNew(sizeof(ArmLIR), true);
-    assert(LOWREG(rDest) && LOWREG(rSrc));
-    res->operands[0] = rDest;
-    res->operands[1] = rSrc;
-    res->opCode = THUMB_MOV_RR;
+    ArmLIR* res;
+    ArmOpCode opCode;
+    res = dvmCompilerNew(sizeof(ArmLIR), true);
+    if (LOWREG(rDest) && LOWREG(rSrc))
+        opCode = THUMB_MOV_RR;
+    else if (!LOWREG(rDest) && !LOWREG(rSrc))
+         opCode = THUMB_MOV_RR_H2H;
+    else if (LOWREG(rDest))
+         opCode = THUMB_MOV_RR_H2L;
+    else
+         opCode = THUMB_MOV_RR_L2H;
+    rDest &= THUMB_REG_MASK;
+    rSrc &= THUMB_REG_MASK;
+
+    res->operands[0] = rDest & THUMB_REG_MASK;
+    res->operands[1] = rSrc & THUMB_REG_MASK;
+    res->opCode = opCode;
     if (rDest == rSrc) {
         res->isNop = true;
     }
@@ -150,16 +197,16 @@
  * Load a immediate using a shortcut if possible; otherwise
  * grab from the per-translation literal pool
  */
-static void loadConstant(CompilationUnit *cUnit, int rDest, int value)
+static ArmLIR *loadConstant(CompilationUnit *cUnit, int rDest, int value)
 {
+    ArmLIR *res;
     /* See if the value can be constructed cheaply */
     if ((value >= 0) && (value <= 255)) {
-        newLIR2(cUnit, THUMB_MOV_IMM, rDest, value);
-        return;
+        return newLIR2(cUnit, THUMB_MOV_IMM, rDest, value);
     } else if ((value & 0xFFFFFF00) == 0xFFFFFF00) {
-        newLIR2(cUnit, THUMB_MOV_IMM, rDest, ~value);
+        res = newLIR2(cUnit, THUMB_MOV_IMM, rDest, ~value);
         newLIR2(cUnit, THUMB_MVN, rDest, rDest);
-        return;
+        return res;
     }
     /* No shortcut - go ahead and use literal pool */
     ArmLIR *dataTarget = scanLiteralPool(cUnit, value, 255);
@@ -170,6 +217,7 @@
     loadPcRel->opCode = THUMB_LDR_PC_REL;
     loadPcRel->generic.target = (LIR *) dataTarget;
     loadPcRel->operands[0] = rDest;
+    res = loadPcRel;
     dvmCompilerAppendLIR(cUnit, (LIR *) loadPcRel);
 
     /*
@@ -179,147 +227,208 @@
     if (dataTarget->operands[0] != value) {
         newLIR2(cUnit, THUMB_ADD_RI8, rDest, value - dataTarget->operands[0]);
     }
+    return res;
 }
 
 /* Export the Dalvik PC assicated with an instruction to the StackSave area */
-static void genExportPC(CompilationUnit *cUnit, MIR *mir, int rDPC, int rAddr)
+static ArmLIR *genExportPC(CompilationUnit *cUnit, MIR *mir, int rDPC,
+                           int rAddr)
 {
+    ArmLIR *res;
     int offset = offsetof(StackSaveArea, xtra.currentPc);
-    loadConstant(cUnit, rDPC, (int) (cUnit->method->insns + mir->offset));
+    res = loadConstant(cUnit, rDPC, (int) (cUnit->method->insns + mir->offset));
     newLIR2(cUnit, THUMB_MOV_RR, rAddr, rFP);
     newLIR2(cUnit, THUMB_SUB_RI8, rAddr, sizeof(StackSaveArea) - offset);
-    newLIR3(cUnit, THUMB_STR_RRI5, rDPC, rAddr, 0);
+    storeWordDisp( cUnit, rAddr, 0, rDPC, -1);
+    return res;
 }
 
-/* Generate conditional branch instructions */
-static void genConditionalBranch(CompilationUnit *cUnit,
-                                 ArmConditionCode cond,
-                                 ArmLIR *target)
+/* Load value from base + scaled index. Note: index reg killed */
+static ArmLIR *loadBaseIndexed(CompilationUnit *cUnit, int rBase,
+                               int rIndex, int rDest, int scale, OpSize size)
 {
-    ArmLIR *branch = newLIR2(cUnit, THUMB_B_COND, 0, cond);
-    branch->generic.target = (LIR *) target;
+    ArmLIR *first = NULL;
+    ArmLIR *res;
+    ArmOpCode opCode = THUMB_BKPT;
+    if (scale)
+        first = opRegRegImm(cUnit, OP_LSL, rIndex, rIndex, scale, rNone);
+    switch (size) {
+        case WORD:
+            opCode = THUMB_LDR_RRR;
+            break;
+        case UNSIGNED_HALF:
+            opCode = THUMB_LDRH_RRR;
+            break;
+        case SIGNED_HALF:
+            opCode = THUMB_LDRSH_RRR;
+            break;
+        case UNSIGNED_BYTE:
+            opCode = THUMB_LDRB_RRR;
+            break;
+        case SIGNED_BYTE:
+            opCode = THUMB_LDRSB_RRR;
+            break;
+        default:
+            assert(0);
+    }
+    res = newLIR3(cUnit, opCode, rDest, rBase, rIndex);
+    return (first) ? first : res;
 }
 
-/* Generate unconditional branch instructions */
-static ArmLIR *genUnconditionalBranch(CompilationUnit *cUnit, ArmLIR *target)
+/* store value base base + scaled index. Note: index reg killed */
+static ArmLIR *storeBaseIndexed(CompilationUnit *cUnit, int rBase,
+                                int rIndex, int rSrc, int scale, OpSize size)
 {
-    ArmLIR *branch = newLIR0(cUnit, THUMB_B_UNCOND);
-    branch->generic.target = (LIR *) target;
-    return branch;
+    ArmLIR *first = NULL;
+    ArmLIR *res;
+    ArmOpCode opCode = THUMB_BKPT;
+    if (scale)
+        first = opRegRegImm(cUnit, OP_LSL, rIndex, rIndex, scale, rNone);
+    switch (size) {
+        case WORD:
+            opCode = THUMB_STR_RRR;
+            break;
+        case UNSIGNED_HALF:
+        case SIGNED_HALF:
+            opCode = THUMB_STRH_RRR;
+            break;
+        case UNSIGNED_BYTE:
+        case SIGNED_BYTE:
+            opCode = THUMB_STRB_RRR;
+            break;
+        default:
+            assert(0);
+    }
+    res = newLIR3(cUnit, opCode, rSrc, rBase, rIndex);
+    return (first) ? first : res;
 }
 
 /*
- * Load a pair of values of rFP[src..src+1] and store them into rDestLo and
- * rDestHi
+ * Load value from base + displacement.  Optionally perform null check
+ * on base (which must have an associated vReg and MIR).  If not
+ * performing null check, incoming MIR can be null. Note: base and
+ * dest must not be the same if there is any chance that the long
+ * form must be used.
  */
-static void loadValuePair(CompilationUnit *cUnit, int vSrc, int rDestLo,
-                          int rDestHi)
+static ArmLIR *loadBaseDisp(CompilationUnit *cUnit, MIR *mir, int rBase,
+                            int displacement, int rDest, OpSize size,
+                            bool nullCheck, int vReg)
 {
-    /* Use reg + imm5*4 to load the values if possible */
-    if (vSrc <= 30) {
-        newLIR3(cUnit, THUMB_LDR_RRI5, rDestLo, rFP, vSrc);
-        newLIR3(cUnit, THUMB_LDR_RRI5, rDestHi, rFP, vSrc+1);
-    } else {
-        if (vSrc <= 64) {
-            /* Sneak 4 into the base address first */
-            newLIR3(cUnit, THUMB_ADD_RRI3, rDestLo, rFP, 4);
-            newLIR2(cUnit, THUMB_ADD_RI8, rDestLo, (vSrc-1)*4);
-        } else {
-            /* Offset too far from rFP */
-            loadConstant(cUnit, rDestLo, vSrc*4);
-            newLIR3(cUnit, THUMB_ADD_RRR, rDestLo, rFP, rDestLo);
-        }
-        assert(rDestLo < rDestHi);
-        newLIR2(cUnit, THUMB_LDMIA, rDestLo, (1<<rDestLo) | (1<<(rDestHi)));
+    ArmLIR *first = NULL;
+    ArmLIR *res;
+    ArmOpCode opCode = THUMB_BKPT;
+    bool shortForm = false;
+    int shortMax = 128;
+    switch (size) {
+        case WORD:
+            if (LOWREG(rDest) && (rBase == rpc) &&
+                (displacement <= 1020) && (displacement >= 0)) {
+                shortForm = true;
+                displacement >>= 2;
+                opCode = THUMB_LDR_PC_REL;
+            } else if (LOWREG(rDest) && (rBase == r13) &&
+                      (displacement <= 1020) && (displacement >= 0)) {
+                shortForm = true;
+                displacement >>= 2;
+                opCode = THUMB_LDR_SP_REL;
+            } else if (displacement < 128 && displacement >= 0) {
+                assert((displacement & 0x3) == 0);
+                shortForm = true;
+                displacement >>= 2;
+                opCode = THUMB_LDR_RRI5;
+            } else {
+                opCode = THUMB_LDR_RRR;
+            }
+            break;
+        case UNSIGNED_HALF:
+            if (displacement < 64 && displacement >= 0) {
+                assert((displacement & 0x1) == 0);
+                shortForm = true;
+                displacement >>= 1;
+                opCode = THUMB_LDRH_RRI5;
+            } else {
+                opCode = THUMB_LDRH_RRR;
+            }
+            break;
+        case SIGNED_HALF:
+            opCode = THUMB_LDRSH_RRR;
+            break;
+        case UNSIGNED_BYTE:
+            if (displacement < 32 && displacement >= 0) {
+                shortForm = true;
+                opCode = THUMB_LDRB_RRI5;
+            } else {
+                opCode = THUMB_LDRB_RRR;
+            }
+            break;
+        case SIGNED_BYTE:
+            opCode = THUMB_LDRSB_RRR;
+            break;
+        default:
+            assert(0);
     }
+    if (nullCheck)
+        first = genNullCheck(cUnit, vReg, rBase, mir->offset, NULL);
+    if (shortForm) {
+        res = newLIR3(cUnit, opCode, rDest, rBase, displacement);
+    } else {
+        assert(rBase != rDest);
+        res = loadConstant(cUnit, rDest, displacement);
+        newLIR3(cUnit, opCode, rDest, rBase, rDest);
+    }
+    return (first) ? first : res;
 }
 
-/*
- * Store a pair of values of rSrc and rSrc+1 and store them into vDest and
- * vDest+1
- */
-static void storeValuePair(CompilationUnit *cUnit, int rSrcLo, int rSrcHi,
-                           int vDest, int rScratch)
+static ArmLIR *storeBaseDisp(CompilationUnit *cUnit, int rBase,
+                             int displacement, int rSrc, OpSize size,
+                             int rScratch)
 {
-    killNullCheckedRegister(cUnit, vDest);
-    killNullCheckedRegister(cUnit, vDest+1);
-    updateLiveRegisterPair(cUnit, vDest, rSrcLo, rSrcHi);
-
-    /* Use reg + imm5*4 to store the values if possible */
-    if (vDest <= 30) {
-        newLIR3(cUnit, THUMB_STR_RRI5, rSrcLo, rFP, vDest);
-        newLIR3(cUnit, THUMB_STR_RRI5, rSrcHi, rFP, vDest+1);
-    } else {
-        if (vDest <= 64) {
-            /* Sneak 4 into the base address first */
-            newLIR3(cUnit, THUMB_ADD_RRI3, rScratch, rFP, 4);
-            newLIR2(cUnit, THUMB_ADD_RI8, rScratch, (vDest-1)*4);
-        } else {
-            /* Offset too far from rFP */
-            loadConstant(cUnit, rScratch, vDest*4);
-            newLIR3(cUnit, THUMB_ADD_RRR, rScratch, rFP, rScratch);
-        }
-        assert(rSrcLo < rSrcHi);
-        newLIR2(cUnit, THUMB_STMIA, rScratch, (1<<rSrcLo) | (1 << (rSrcHi)));
+    ArmLIR *res;
+    ArmOpCode opCode = THUMB_BKPT;
+    bool shortForm = false;
+    int shortMax = 128;
+    switch (size) {
+        case WORD:
+            if (displacement < 128 && displacement >= 0) {
+                assert((displacement & 0x3) == 0);
+                shortForm = true;
+                displacement >>= 2;
+                opCode = THUMB_STR_RRI5;
+            } else {
+                opCode = THUMB_STR_RRR;
+            }
+            break;
+        case UNSIGNED_HALF:
+        case SIGNED_HALF:
+            if (displacement < 64 && displacement >= 0) {
+                assert((displacement & 0x1) == 0);
+                shortForm = true;
+                displacement >>= 1;
+                opCode = THUMB_STRH_RRI5;
+            } else {
+                opCode = THUMB_STRH_RRR;
+            }
+            break;
+        case UNSIGNED_BYTE:
+        case SIGNED_BYTE:
+            if (displacement < 32 && displacement >= 0) {
+                shortForm = true;
+                opCode = THUMB_STRB_RRI5;
+            } else {
+                opCode = THUMB_STRB_RRR;
+            }
+            break;
+        default:
+            assert(0);
     }
-}
-
-/* Load the address of a Dalvik register on the frame */
-static void loadValueAddress(CompilationUnit *cUnit, int vSrc, int rDest)
-{
-    /* RRI3 can add up to 7 */
-    if (vSrc <= 1) {
-        newLIR3(cUnit, THUMB_ADD_RRI3, rDest, rFP, vSrc*4);
-    } else if (vSrc <= 64) {
-        /* Sneak 4 into the base address first */
-        newLIR3(cUnit, THUMB_ADD_RRI3, rDest, rFP, 4);
-        newLIR2(cUnit, THUMB_ADD_RI8, rDest, (vSrc-1)*4);
+    if (shortForm) {
+        res = newLIR3(cUnit, opCode, rSrc, rBase, displacement);
     } else {
-        loadConstant(cUnit, rDest, vSrc*4);
-        newLIR3(cUnit, THUMB_ADD_RRR, rDest, rFP, rDest);
+        assert(rScratch != -1);
+        res = loadConstant(cUnit, rScratch, displacement);
+        newLIR3(cUnit, opCode, rSrc, rBase, rScratch);
     }
-}
-
-/* Load a single value from rFP[src] and store them into rDest */
-static void loadValue(CompilationUnit *cUnit, int vSrc, int rDest)
-{
-    /* Use reg + imm5*4 to load the value if possible */
-    if (vSrc <= 31) {
-        newLIR3(cUnit, THUMB_LDR_RRI5, rDest, rFP, vSrc);
-    } else {
-        loadConstant(cUnit, rDest, vSrc*4);
-        newLIR3(cUnit, THUMB_LDR_RRR, rDest, rFP, rDest);
-    }
-}
-
-/* Load a word at base + displacement.  Displacement must be word multiple */
-static void loadWordDisp(CompilationUnit *cUnit, int rBase, int displacement,
-                         int rDest)
-{
-    assert((displacement & 0x3) == 0);
-    /* Can it fit in a RRI5? */
-    if (displacement < 128) {
-        newLIR3(cUnit, THUMB_LDR_RRI5, rDest, rBase, displacement >> 2);
-    } else {
-        loadConstant(cUnit, rDest, displacement);
-        newLIR3(cUnit, THUMB_LDR_RRR, rDest, rBase, rDest);
-    }
-}
-
-/* Store a value from rSrc to vDest */
-static void storeValue(CompilationUnit *cUnit, int rSrc, int vDest,
-                       int rScratch)
-{
-    killNullCheckedRegister(cUnit, vDest);
-    updateLiveRegister(cUnit, vDest, rSrc);
-
-    /* Use reg + imm5*4 to store the value if possible */
-    if (vDest <= 31) {
-        newLIR3(cUnit, THUMB_STR_RRI5, rSrc, rFP, vDest);
-    } else {
-        loadConstant(cUnit, rScratch, vDest*4);
-        newLIR3(cUnit, THUMB_STR_RRR, rSrc, rFP, rScratch);
-    }
+    return res;
 }
 
 /*
@@ -331,7 +440,439 @@
                                          int checkValue, int dOffset,
                                          ArmLIR *pcrLabel)
 {
+    assert((checkValue & 0xff) == checkValue);
     newLIR2(cUnit, THUMB_CMP_RI8, reg, checkValue);
     ArmLIR *branch = newLIR2(cUnit, THUMB_B_COND, 0, cond);
     return genCheckCommon(cUnit, dOffset, branch, pcrLabel);
 }
+
+static ArmLIR *loadMultiple(CompilationUnit *cUnit, int rBase, int rMask)
+{
+    return newLIR2(cUnit, THUMB_LDMIA, rBase, rMask);
+}
+
+static ArmLIR *storeMultiple(CompilationUnit *cUnit, int rBase, int rMask)
+{
+    return newLIR2(cUnit, THUMB_STMIA, rBase, rMask);
+}
+
+static ArmLIR *opNone(CompilationUnit *cUnit, OpKind op)
+{
+    ArmOpCode opCode = THUMB_BKPT;
+    switch (op) {
+        case OP_UNCOND_BR:
+            opCode = THUMB_B_UNCOND;
+            break;
+        default:
+            assert(0);
+    }
+    return newLIR0(cUnit, opCode);
+}
+
+static ArmLIR *opImmImm(CompilationUnit *cUnit, OpKind op, int value1,
+                        int value2)
+{
+    ArmOpCode opCode = THUMB_BKPT;
+    switch (op) {
+        case OP_COND_BR:
+            opCode = THUMB_B_COND;
+            break;
+        default:
+            assert(0);
+    }
+    return newLIR2(cUnit, opCode, value1, value2);
+}
+
+static ArmLIR *opImm(CompilationUnit *cUnit, OpKind op, int value)
+{
+    ArmOpCode opCode = THUMB_BKPT;
+    switch (op) {
+        case OP_PUSH:
+            opCode = THUMB_PUSH;
+            break;
+        case OP_POP:
+            opCode = THUMB_POP;
+            break;
+        default:
+            assert(0);
+    }
+    return newLIR1(cUnit, opCode, value);
+}
+
+static ArmLIR *opReg(CompilationUnit *cUnit, OpKind op, int rDestSrc)
+{
+    ArmOpCode opCode = THUMB_BKPT;
+    switch (op) {
+        case OP_BLX:
+            opCode = THUMB_BLX_R;
+            break;
+        default:
+            assert(0);
+    }
+    return newLIR1(cUnit, opCode, rDestSrc);
+}
+
+static ArmLIR *opRegReg(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+                        int rSrc2)
+{
+    ArmLIR *res;
+    ArmOpCode opCode = THUMB_BKPT;
+    switch (op) {
+        case OP_ADC:
+            opCode = THUMB_ADC;
+            break;
+        case OP_AND:
+            opCode = THUMB_AND_RR;
+            break;
+        case OP_BIC:
+            opCode = THUMB_BIC;
+            break;
+        case OP_CMN:
+            opCode = THUMB_CMN;
+            break;
+        case OP_CMP:
+            opCode = THUMB_CMP_RR;
+            break;
+        case OP_XOR:
+            opCode = THUMB_EOR;
+            break;
+        case OP_MOV:
+            if (LOWREG(rDestSrc1) && LOWREG(rSrc2))
+                opCode = THUMB_MOV_RR;
+            else if (!LOWREG(rDestSrc1) && !LOWREG(rSrc2))
+                opCode = THUMB_MOV_RR_H2H;
+            else if (LOWREG(rDestSrc1))
+                opCode = THUMB_MOV_RR_H2L;
+            else
+                opCode = THUMB_MOV_RR_L2H;
+            rDestSrc1 &= THUMB_REG_MASK;
+            rSrc2 &= THUMB_REG_MASK;
+            break;
+        case OP_MUL:
+            opCode = THUMB_MUL;
+            break;
+        case OP_MVN:
+            opCode = THUMB_MVN;
+            break;
+        case OP_NEG:
+            opCode = THUMB_NEG;
+            break;
+        case OP_OR:
+            opCode = THUMB_ORR;
+            break;
+        case OP_SBC:
+            opCode = THUMB_SBC;
+            break;
+        case OP_TST:
+            opCode = THUMB_TST;
+            break;
+        case OP_LSL:
+            opCode = THUMB_LSLV;
+            break;
+        case OP_LSR:
+            opCode = THUMB_LSRV;
+            break;
+        case OP_ASR:
+            opCode = THUMB_ASRV;
+            break;
+        case OP_ROR:
+            opCode = THUMB_RORV;
+        case OP_ADD:
+        case OP_SUB:
+            return opRegRegReg(cUnit, op, rDestSrc1, rDestSrc1, rSrc2);
+        case OP_2BYTE:
+             res = opRegRegImm(cUnit, OP_LSL, rDestSrc1, rSrc2, 24, rNone);
+             opRegRegImm(cUnit, OP_ASR, rDestSrc1, rDestSrc1, 24, rNone);
+             return res;
+        case OP_2SHORT:
+             res = opRegRegImm(cUnit, OP_LSL, rDestSrc1, rSrc2, 16, rNone);
+             opRegRegImm(cUnit, OP_ASR, rDestSrc1, rDestSrc1, 16, rNone);
+             return res;
+        case OP_2CHAR:
+             res = opRegRegImm(cUnit, OP_LSL, rDestSrc1, rSrc2, 16, rNone);
+             opRegRegImm(cUnit, OP_LSR, rDestSrc1, rDestSrc1, 16, rNone);
+             return res;
+        default:
+            assert(0);
+            break;
+    }
+    return newLIR2(cUnit, opCode, rDestSrc1, rSrc2);
+}
+
+static ArmLIR *opRegImm(CompilationUnit *cUnit, OpKind op, int rDestSrc1,
+                        int value, int rScratch)
+{
+    ArmLIR *res;
+    bool neg = (value < 0);
+    int absValue = (neg) ? -value : value;
+    bool shortForm = (absValue & 0xff) == absValue;
+    ArmOpCode opCode = THUMB_BKPT;
+    switch (op) {
+        case OP_ADD:
+            if ( !neg && (rDestSrc1 == 13) && (value <= 508)) { /* sp */
+                assert((value & 0x3) == 0);
+                return newLIR1(cUnit, THUMB_ADD_SPI7, value >> 2);
+            } else if (shortForm) {
+                opCode = (neg) ? THUMB_SUB_RI8 : THUMB_ADD_RI8;
+            } else
+                opCode = THUMB_ADD_RRR;
+            break;
+        case OP_SUB:
+            if (!neg && (rDestSrc1 == 13) && (value <= 508)) { /* sp */
+                assert((value & 0x3) == 0);
+                return newLIR1(cUnit, THUMB_SUB_SPI7, value >> 2);
+            } else if (shortForm) {
+                opCode = (neg) ? THUMB_ADD_RI8 : THUMB_SUB_RI8;
+            } else
+                opCode = THUMB_SUB_RRR;
+            break;
+        case OP_CMP:
+            if (LOWREG(rDestSrc1) && shortForm)
+                opCode = (shortForm) ?  THUMB_CMP_RI8 : THUMB_CMP_RR;
+            else if (LOWREG(rDestSrc1))
+                opCode = THUMB_CMP_RR;
+            else {
+                shortForm = false;
+                opCode = THUMB_CMP_HL;
+            }
+            break;
+        default:
+            assert(0);
+            break;
+    }
+    if (shortForm)
+        res = newLIR2(cUnit, opCode, rDestSrc1, absValue);
+    else {
+        assert(rScratch != rNone);
+        res = loadConstant(cUnit, rScratch, value);
+        newLIR3(cUnit, opCode, rDestSrc1, rDestSrc1, rScratch);
+    }
+    return res;
+}
+
+static ArmLIR *opRegRegReg(CompilationUnit *cUnit, OpKind op, int rDest,
+                           int rSrc1, int rSrc2)
+{
+    ArmOpCode opCode = THUMB_BKPT;
+    switch (op) {
+        case OP_ADD:
+            opCode = THUMB_ADD_RRR;
+            break;
+        case OP_SUB:
+            opCode = THUMB_SUB_RRR;
+            break;
+        default:
+            assert(0);
+            break;
+    }
+    return newLIR3(cUnit, opCode, rDest, rSrc1, rSrc2);
+}
+
+static ArmLIR *opRegRegImm(CompilationUnit *cUnit, OpKind op, int rDest,
+                           int rSrc1, int value, int rScratch)
+{
+    ArmLIR *res;
+    bool neg = (value < 0);
+    int absValue = (neg) ? -value : value;
+    ArmOpCode opCode = THUMB_BKPT;
+    bool shortForm = (absValue & 0x7) == absValue;
+    switch(op) {
+        case OP_ADD:
+            if ((rSrc1 == 13) && (value <= 1020)) { /* sp */
+                assert((value & 0x3) == 0);
+                shortForm = true;
+                opCode = THUMB_ADD_SP_REL;
+                value >>= 2;
+            } else if ((rSrc1 == 15) && (value <= 1020)) { /* pc */
+                assert((value & 0x3) == 0);
+                shortForm = true;
+                opCode = THUMB_ADD_PC_REL;
+                value >>= 2;
+            } else if (shortForm) {
+                opCode = (neg) ? THUMB_SUB_RRI3 : THUMB_ADD_RRI3;
+            } else if ((absValue > 0) && (absValue <= (255 + 7))) {
+                /* Two shots - 1st handle the 7 */
+                opCode = (neg) ? THUMB_SUB_RRI3 : THUMB_ADD_RRI3;
+                res = newLIR3(cUnit, opCode, rDest, rSrc1, 7);
+                opCode = (neg) ? THUMB_SUB_RI8 : THUMB_ADD_RI8;
+                newLIR2(cUnit, opCode, rDest, absValue - 7);
+                return res;
+            } else
+                opCode = THUMB_ADD_RRR;
+            break;
+
+        case OP_SUB:
+            if (shortForm) {
+                opCode = (neg) ? THUMB_ADD_RRI3 : THUMB_SUB_RRI3;
+            } else if ((absValue > 0) && (absValue <= (255 + 7))) {
+                /* Two shots - 1st handle the 7 */
+                opCode = (neg) ? THUMB_ADD_RRI3 : THUMB_SUB_RRI3;
+                res = newLIR3(cUnit, opCode, rDest, rSrc1, 7);
+                opCode = (neg) ? THUMB_ADD_RI8 : THUMB_SUB_RI8;
+                newLIR2(cUnit, opCode, rDest, absValue - 7);
+                return res;
+            } else
+                opCode = THUMB_SUB_RRR;
+            break;
+        case OP_LSL:
+                shortForm = (!neg && value <= 31);
+                opCode = THUMB_LSL;
+                break;
+        case OP_LSR:
+                shortForm = (!neg && value <= 31);
+                opCode = THUMB_LSR;
+                break;
+        case OP_ASR:
+                shortForm = (!neg && value <= 31);
+                opCode = THUMB_ASR;
+                break;
+        case OP_MUL:
+        case OP_AND:
+        case OP_OR:
+        case OP_XOR:
+                if (rDest == rSrc1) {
+                    res = loadConstant(cUnit, rScratch, value);
+                    opRegReg(cUnit, op, rDest, rScratch);
+                } else {
+                    res = loadConstant(cUnit, rDest, value);
+                    opRegReg(cUnit, op, rDest, rSrc1);
+                }
+                return res;
+        default:
+            assert(0);
+            break;
+    }
+    if (shortForm)
+        res = newLIR3(cUnit, opCode, rDest, rSrc1, absValue);
+    else {
+        if (rDest != rSrc1) {
+            res = loadConstant(cUnit, rDest, value);
+            newLIR3(cUnit, opCode, rDest, rSrc1, rDest);
+        } else {
+            assert(rScratch != rNone);
+            res = loadConstant(cUnit, rScratch, value);
+            newLIR3(cUnit, opCode, rDest, rSrc1, rScratch);
+        }
+    }
+    return res;
+}
+
+static bool genInlinedStringLength(CompilationUnit *cUnit, MIR *mir)
+{
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    int offset = offsetof(InterpState, retval);
+    int regObj = selectFirstRegister(cUnit, dInsn->arg[0], false);
+    int reg1 = NEXT_REG(regObj);
+    loadValue(cUnit, dInsn->arg[0], regObj);
+    genNullCheck(cUnit, dInsn->arg[0], regObj, mir->offset, NULL);
+    loadWordDisp(cUnit, regObj, gDvm.offJavaLangString_count, reg1);
+    storeWordDisp(cUnit, rGLUE, offset, reg1, regObj);
+    return false;
+}
+
+static bool genInlinedStringCharAt(CompilationUnit *cUnit, MIR *mir)
+{
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    int offset = offsetof(InterpState, retval);
+    int contents = offsetof(ArrayObject, contents);
+    int regObj = selectFirstRegister(cUnit, dInsn->arg[0], false);
+    int regIdx = NEXT_REG(regObj);
+    int regMax = NEXT_REG(regIdx);
+    int regOff = NEXT_REG(regMax);
+    loadValue(cUnit, dInsn->arg[0], regObj);
+    loadValue(cUnit, dInsn->arg[1], regIdx);
+    ArmLIR * pcrLabel = genNullCheck(cUnit, dInsn->arg[0], regObj,
+                                         mir->offset, NULL);
+    loadWordDisp(cUnit, regObj, gDvm.offJavaLangString_count, regMax);
+    loadWordDisp(cUnit, regObj, gDvm.offJavaLangString_offset, regOff);
+    loadWordDisp(cUnit, regObj, gDvm.offJavaLangString_value, regObj);
+    genBoundsCheck(cUnit, regIdx, regMax, mir->offset, pcrLabel);
+
+    newLIR2(cUnit, THUMB_ADD_RI8, regObj, contents);
+    newLIR3(cUnit, THUMB_ADD_RRR, regIdx, regIdx, regOff);
+    newLIR3(cUnit, THUMB_ADD_RRR, regIdx, regIdx, regIdx);
+    newLIR3(cUnit, THUMB_LDRH_RRR, regMax, regObj, regIdx);
+    storeWordDisp(cUnit, rGLUE, offset, regMax, regObj);
+    return false;
+}
+
+static bool genInlinedAbsInt(CompilationUnit *cUnit, MIR *mir)
+{
+    int offset = offsetof(InterpState, retval);
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    int reg0 = selectFirstRegister(cUnit, dInsn->arg[0], false);
+    int sign = NEXT_REG(reg0);
+    /* abs(x) = y<=x>>31, (x+y)^y.  Shorter in ARM/THUMB2, no skip in THUMB */
+    loadValue(cUnit, dInsn->arg[0], reg0);
+    newLIR3(cUnit, THUMB_ASR, sign, reg0, 31);
+    newLIR3(cUnit, THUMB_ADD_RRR, reg0, reg0, sign);
+    newLIR2(cUnit, THUMB_EOR, reg0, sign);
+    storeWordDisp(cUnit, rGLUE, offset, reg0, sign);
+    return false;
+}
+
+static bool genInlinedAbsFloat(CompilationUnit *cUnit, MIR *mir)
+{
+    int offset = offsetof(InterpState, retval);
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    int reg0 = selectFirstRegister(cUnit, dInsn->arg[0], false);
+    int signMask = NEXT_REG(reg0);
+    loadValue(cUnit, dInsn->arg[0], reg0);
+    loadConstant(cUnit, signMask, 0x7fffffff);
+    newLIR2(cUnit, THUMB_AND_RR, reg0, signMask);
+    storeWordDisp(cUnit, rGLUE, offset, reg0, signMask);
+    return false;
+}
+
+static bool genInlinedAbsDouble(CompilationUnit *cUnit, MIR *mir)
+{
+    int offset = offsetof(InterpState, retval);
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    int oplo = selectFirstRegister(cUnit, dInsn->arg[0], true);
+    int ophi = NEXT_REG(oplo);
+    int signMask = NEXT_REG(ophi);
+    loadValuePair(cUnit, dInsn->arg[0], oplo, ophi);
+    loadConstant(cUnit, signMask, 0x7fffffff);
+    storeWordDisp(cUnit, rGLUE, offset, oplo, ophi);
+    newLIR2(cUnit, THUMB_AND_RR, ophi, signMask);
+    storeWordDisp(cUnit, rGLUE, offset + 4, ophi, oplo);
+    return false;
+}
+
+ /* No select in thumb, so we need to branch.  Thumb2 will do better */
+static bool genInlinedMinMaxInt(CompilationUnit *cUnit, MIR *mir, bool isMin)
+{
+    int offset = offsetof(InterpState, retval);
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    int reg0 = selectFirstRegister(cUnit, dInsn->arg[0], false);
+    int reg1 = NEXT_REG(reg0);
+    loadValue(cUnit, dInsn->arg[0], reg0);
+    loadValue(cUnit, dInsn->arg[1], reg1);
+    newLIR2(cUnit, THUMB_CMP_RR, reg0, reg1);
+    ArmLIR *branch1 = newLIR2(cUnit, THUMB_B_COND, 2,
+           isMin ? ARM_COND_LT : ARM_COND_GT);
+    newLIR2(cUnit, THUMB_MOV_RR, reg0, reg1);
+    ArmLIR *target =
+        newLIR3(cUnit, THUMB_STR_RRI5, reg0, rGLUE, offset >> 2);
+    branch1->generic.target = (LIR *)target;
+    return false;
+}
+
+static bool genInlinedAbsLong(CompilationUnit *cUnit, MIR *mir)
+{
+    int offset = offsetof(InterpState, retval);
+    DecodedInstruction *dInsn = &mir->dalvikInsn;
+    int oplo = selectFirstRegister(cUnit, dInsn->arg[0], true);
+    int ophi = NEXT_REG(oplo);
+    int sign = NEXT_REG(ophi);
+    /* abs(x) = y<=x>>31, (x+y)^y.  Shorter in ARM/THUMB2, no skip in THUMB */
+    loadValuePair(cUnit, dInsn->arg[0], oplo, ophi);
+    newLIR3(cUnit, THUMB_ASR, sign, ophi, 31);
+    newLIR3(cUnit, THUMB_ADD_RRR, oplo, oplo, sign);
+    newLIR2(cUnit, THUMB_ADC, ophi, sign);
+    newLIR2(cUnit, THUMB_EOR, oplo, sign);
+    newLIR2(cUnit, THUMB_EOR, ophi, sign);
+    storeWordDisp(cUnit, rGLUE, offset, oplo, sign);
+    storeWordDisp(cUnit, rGLUE, offset + 4, ophi, sign);
+    return false;
+}