Complete compiler portion of exception handling

Will still need much testing once the runtime portions are in place.

Change-Id: I90fc7c1fd89bfae89dfd19a6e422024b6b5454ec
diff --git a/build/Android.common.mk b/build/Android.common.mk
index 07a40d2..8b22b83 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -64,7 +64,6 @@
 	src/compiler/codegen/arm/Assemble.cc \
 	src/compiler/codegen/arm/LocalOptimizations.cc \
 	src/compiler/codegen/arm/armv7-a/Codegen.cc \
-	src/compiler/RuntimeUtilities.cc \
 	src/dex_cache.cc \
 	src/dex_file.cc \
 	src/dex_instruction.cc \
diff --git a/src/compiler/CompilerIR.h b/src/compiler/CompilerIR.h
index 9c9bcb0..e9933ff 100644
--- a/src/compiler/CompilerIR.h
+++ b/src/compiler/CompilerIR.h
@@ -235,6 +235,7 @@
     BasicBlock* nextCodegenBlock;       // for extended trace codegen
     GrowableList dfsOrder;
     GrowableList domPostOrderTraversal;
+    GrowableList throwLaunchpads;
     ArenaBitVector* tryBlockAddr;
     ArenaBitVector** defBlockMatrix;    // numDalvikRegister x numBlocks
     ArenaBitVector* tempBlockV;
@@ -245,12 +246,10 @@
     bool quitLoopMode;                  // cold path/complex bytecode
     int preservedRegsUsed;              // How many callee save regs used
     /*
-     * Frame layout details.  TODO: Reorganize, remove reduncancy
-     * and move elsewhere.  Some of this is already in struct Method,
-     * at least frameSize should eventually move there.  Delay regorg
-     * until we get a feel for how this will be used by the low-level
-     * codegen utilities.  "num" fields are in 4-byte words, "Size" and
-     * "Offset" in bytes.
+     * Frame layout details.
+     * NOTE: for debug support it will be necessary to add a structure
+     * to map the Dalvik virtual registers to the promoted registers.
+     * NOTE: "num" fields are in 4-byte words, "Size" and "Offset" in bytes.
      */
     int numIns;
     int numOuts;
diff --git a/src/compiler/Frontend.cc b/src/compiler/Frontend.cc
index 2f86c48..2e94516 100644
--- a/src/compiler/Frontend.cc
+++ b/src/compiler/Frontend.cc
@@ -719,6 +719,9 @@
     /* Intialize the fillArrayData list */
     oatInitGrowableList(&cUnit.fillArrayData, 4);
 
+    /* Intialize the throwLaunchpads list */
+    oatInitGrowableList(&cUnit.throwLaunchpads, 4);
+
     /* Allocate the bit-vector to track the beginning of basic blocks */
     ArenaBitVector *tryBlockAddr = oatAllocBitVector(cUnit.insnsSize,
                                                      true /* expandable */);
diff --git a/src/compiler/RuntimeUtilities.cc b/src/compiler/RuntimeUtilities.cc
deleted file mode 100644
index 8c94d62..0000000
--- a/src/compiler/RuntimeUtilities.cc
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2011 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 "Dalvik.h"
-#include "CompilerInternals.h"
-
-namespace art {
-/* FIXME - codegen helper functions, move to art runtime proper */
-
-/*
- * Float/double conversion requires clamping to min and max of integer form.  If
- * target doesn't support this normally, use these.
- */
-int64_t D2L(double d)
-{
-    static const double kMaxLong = (double)(s8)0x7fffffffffffffffULL;
-    static const double kMinLong = (double)(s8)0x8000000000000000ULL;
-    if (d >= kMaxLong)
-        return (s8)0x7fffffffffffffffULL;
-    else if (d <= kMinLong)
-        return (s8)0x8000000000000000ULL;
-    else if (d != d) // NaN case
-        return 0;
-    else
-        return (s8)d;
-}
-
-int64_t F2L(float f)
-{
-    static const float kMaxLong = (float)(s8)0x7fffffffffffffffULL;
-    static const float kMinLong = (float)(s8)0x8000000000000000ULL;
-    if (f >= kMaxLong)
-        return (s8)0x7fffffffffffffffULL;
-    else if (f <= kMinLong)
-        return (s8)0x8000000000000000ULL;
-    else if (f != f) // NaN case
-        return 0;
-    else
-        return (s8)f;
-}
-
-/*
- * Temporary placeholder.  Should include run-time checks for size
- * of fill data <= size of array.  If not, throw arrayOutOfBoundsException.
- * As with other new "FromCode" routines, this should return to the caller
- * only if no exception has been thrown.
- *
- * NOTE: When dealing with a raw dex file, the data to be copied uses
- * little-endian ordering.  Require that oat2dex do any required swapping
- * so this routine can get by with a memcpy().
- *
- * Format of the data:
- *  ushort ident = 0x0300   magic value
- *  ushort width            width of each element in the table
- *  uint   size             number of elements in the table
- *  ubyte  data[size*width] table of data values (may contain a single-byte
- *                          padding at the end)
- */
-void HandleFillArrayDataFromCode(Array* array, const uint16_t* table)
-{
-    uint32_t size = (uint32_t)table[2] | (((uint32_t)table[3]) << 16);
-    uint32_t size_in_bytes = size * table[1];
-    if (static_cast<int32_t>(size) > array->GetLength()) {
-      UNIMPLEMENTED(FATAL) << "need to throw AIOOBE and unwind";
-    }
-    memcpy((char*)array + art::Array::DataOffset().Int32Value(),
-           (char*)&table[4], size_in_bytes);
-}
-
-} // namespace art
diff --git a/src/compiler/codegen/Ralloc.h b/src/compiler/codegen/Ralloc.h
index 67ed6ee..82ce23e 100644
--- a/src/compiler/codegen/Ralloc.h
+++ b/src/compiler/codegen/Ralloc.h
@@ -165,7 +165,6 @@
 extern void oatKillNullCheckedLoc(CompilationUnit* cUnit,
                                   RegLocation loc);
 
-//FIXME - this needs to also check the preserved pool.
 extern RegisterInfo *oatIsLive(CompilationUnit* cUnit, int reg);
 
 /* To be used when explicitly managing register use */
diff --git a/src/compiler/codegen/arm/ArchFactory.cc b/src/compiler/codegen/arm/ArchFactory.cc
index 574bb57..66eaf54 100644
--- a/src/compiler/codegen/arm/ArchFactory.cc
+++ b/src/compiler/codegen/arm/ArchFactory.cc
@@ -22,6 +22,10 @@
  *
  */
 
+static ArmLIR* genUnconditionalBranch(CompilationUnit*, ArmLIR*);
+static ArmLIR* genConditionalBranch(CompilationUnit*, ArmConditionCode,
+                                    ArmLIR*);
+
 /*
  * Utiltiy to load the current Method*.  Broken out
  * to allow easy change between placing the current Method* in a
@@ -47,26 +51,23 @@
 #endif
 }
 
-/*
- * Perform a "reg cmp imm" operation and jump to the PCR region if condition
- * satisfies.
- */
-static TGT_LIR* genRegImmCheck(CompilationUnit* cUnit,
-                               ArmConditionCode cond, int reg,
-                               int checkValue, int dOffset,
-                               TGT_LIR* pcrLabel)
+static ArmLIR* genImmedCheck(CompilationUnit* cUnit, ArmConditionCode cCode,
+                             int reg, int immVal, MIR* mir, ArmThrowKind kind)
 {
-    TGT_LIR* branch = genCmpImmBranch(cUnit, cond, reg, checkValue);
-    BasicBlock* bb = cUnit->curBlock;
-    if (bb->taken) {
-        ArmLIR  *exceptionLabel = (ArmLIR* ) cUnit->blockLabelList;
-        exceptionLabel += bb->taken->id;
-        branch->generic.target = (LIR* ) exceptionLabel;
-        return exceptionLabel;
+    ArmLIR* tgt = (ArmLIR*)oatNew(sizeof(ArmLIR), true);
+    tgt->opcode = kArmPseudoThrowTarget;
+    tgt->operands[0] = kind;
+    tgt->operands[1] = mir->offset;
+    ArmLIR* branch;
+    if (cCode == kArmCondAl) {
+        branch = genUnconditionalBranch(cUnit, tgt);
     } else {
-        LOG(FATAL) << "Catch blocks not handled yet";
-        return NULL; // quiet gcc
+        branch = genCmpImmBranch(cUnit, kArmCondEq, reg, 0);
+        branch->generic.target = (LIR*)tgt;
     }
+    // Remember branch target - will process later
+    oatInsertGrowableList(&cUnit->throwLaunchpads, (intptr_t)tgt);
+    return branch;
 }
 
 /*
@@ -74,42 +75,30 @@
  * and mReg is the machine register holding the actual value. If internal state
  * indicates that sReg has been checked before the check request is ignored.
  */
-static TGT_LIR* genNullCheck(CompilationUnit* cUnit, int sReg, int mReg,
-                             int dOffset, TGT_LIR* pcrLabel)
+static ArmLIR* genNullCheck(CompilationUnit* cUnit, int sReg, int mReg,
+                             MIR* mir)
 {
-    /* This particular Dalvik register has been null-checked */
-#if 0
-    // Yes, I know.  Please be quiet.
-    UNIMPLEMENTED(WARNING) << "Need null check & throw support";
-#endif
-    return pcrLabel;
     if (oatIsBitSet(cUnit->regPool->nullCheckedRegs, sReg)) {
-        return pcrLabel;
+        /* This particular Dalvik register has been null-checked */
+        return NULL;
     }
     oatSetBit(cUnit->regPool->nullCheckedRegs, sReg);
-    return genRegImmCheck(cUnit, kArmCondEq, mReg, 0, dOffset, pcrLabel);
-}
-
-/*
- * Perform a "reg cmp reg" operation and jump to the PCR region if condition
- * satisfies.
- */
-static TGT_LIR* genRegRegCheck(CompilationUnit* cUnit,
-                               ArmConditionCode cond,
-                               int reg1, int reg2, int dOffset,
-                               TGT_LIR* pcrLabel)
-{
-    TGT_LIR* res;
-    res = opRegReg(cUnit, kOpCmp, reg1, reg2);
-    TGT_LIR* branch = opCondBranch(cUnit, cond);
-    genCheckCommon(cUnit, dOffset, branch, pcrLabel);
-    return res;
+    return genImmedCheck(cUnit, kArmCondEq, mReg, 0, mir, kArmThrowNullPointer);
 }
 
 /* Perform bound check on two registers */
 static TGT_LIR* genBoundsCheck(CompilationUnit* cUnit, int rIndex,
-                               int rBound, int dOffset, TGT_LIR* pcrLabel)
+                               int rBound, MIR* mir, ArmThrowKind kind)
 {
-    return genRegRegCheck(cUnit, kArmCondCs, rIndex, rBound, dOffset,
-                          pcrLabel);
+    ArmLIR* tgt = (ArmLIR*)oatNew(sizeof(ArmLIR), true);
+    tgt->opcode = kArmPseudoThrowTarget;
+    tgt->operands[0] = kind;
+    tgt->operands[1] = mir->offset;
+    tgt->operands[2] = rIndex;
+    tgt->operands[3] = rBound;
+    opRegReg(cUnit, kOpCmp, rIndex, rBound);
+    ArmLIR* branch = genConditionalBranch(cUnit, kArmCondCs, tgt);
+    // Remember branch target - will process later
+    oatInsertGrowableList(&cUnit->throwLaunchpads, (intptr_t)tgt);
+    return branch;
 }
diff --git a/src/compiler/codegen/arm/ArchUtility.cc b/src/compiler/codegen/arm/ArchUtility.cc
index e51774e..6f435e7 100644
--- a/src/compiler/codegen/arm/ArchUtility.cc
+++ b/src/compiler/codegen/arm/ArchUtility.cc
@@ -351,6 +351,9 @@
         case kArmPseudoNormalBlockLabel:
             LOG(INFO) << "L" << (intptr_t)lir << ":";
             break;
+        case kArmPseudoThrowTarget:
+            LOG(INFO) << "LT" << (intptr_t)lir << ":";
+            break;
         case kArmPseudoCaseLabel:
             LOG(INFO) << "LC" << (intptr_t)lir << ": Case target 0x" <<
                 std::hex << lir->operands[0] << "|" << std::dec <<
diff --git a/src/compiler/codegen/arm/ArmLIR.h b/src/compiler/codegen/arm/ArmLIR.h
index 9d22c4f..b35f39f 100644
--- a/src/compiler/codegen/arm/ArmLIR.h
+++ b/src/compiler/codegen/arm/ArmLIR.h
@@ -347,6 +347,17 @@
     kArmCondNv = 0xf,    /* 1111 */
 } ArmConditionCode;
 
+typedef enum ArmThrowKind {
+    kArmThrowNullPointer,
+    kArmThrowDivZero,
+    kArmThrowArrayBounds,
+    kArmThrowVerificationError,
+    kArmThrowNegArraySize,
+    kArmThrowInternalError,
+    kArmThrowRuntimeException,
+    kArmThrowNoSuchMethod,
+} ArmThrowKind;
+
 #define isPseudoOpcode(opcode) ((int)(opcode) < 0)
 
 /*
@@ -355,6 +366,7 @@
  * Assemble.c.
  */
 typedef enum ArmOpcode {
+    kArmPseudoThrowTarget = -14,
     kArmPseudoCaseLabel = -13,
     kArmPseudoMethodEntry = -12,
     kArmPseudoMethodExit = -11,
diff --git a/src/compiler/codegen/arm/ArmRallocUtil.cc b/src/compiler/codegen/arm/ArmRallocUtil.cc
index bea85ea..030131f 100644
--- a/src/compiler/codegen/arm/ArmRallocUtil.cc
+++ b/src/compiler/codegen/arm/ArmRallocUtil.cc
@@ -178,7 +178,6 @@
     qsort(coreRegs, numRegs, sizeof(RefCounts), sortCounts);
     qsort(fpRegs, numRegs, sizeof(RefCounts), sortCounts);
 
-    // TODO: temp for debugging, too verbose.  Remove when unneeded
     if (cUnit->printMeVerbose) {
         dumpCounts(coreRegs, numRegs, "coreRegs");
         dumpCounts(fpRegs, numRegs, "fpRegs");
diff --git a/src/compiler/codegen/arm/Assemble.cc b/src/compiler/codegen/arm/Assemble.cc
index 2004d46..475f096 100644
--- a/src/compiler/codegen/arm/Assemble.cc
+++ b/src/compiler/codegen/arm/Assemble.cc
@@ -1195,17 +1195,14 @@
                    lir->opcode == kThumb2BCond) {
             ArmLIR *targetLIR = (ArmLIR *) lir->generic.target;
             int delta = 0;
-            if (targetLIR == NULL) {
-                UNIMPLEMENTED(WARNING) << "Throw targets unimplemented";
-            } else {
-                intptr_t pc = lir->generic.offset + 4;
-                intptr_t target = targetLIR->generic.offset;
-                delta = target - pc;
-                if ((lir->opcode == kThumbBCond) && (delta > 254 || delta < -256)) {
-                    lir->opcode = kThumb2BCond;
-                    oatSetupResourceMasks(lir);
-                    res = kRetryAll;
-                }
+            assert(targetLIR);
+            intptr_t pc = lir->generic.offset + 4;
+            intptr_t target = targetLIR->generic.offset;
+            delta = target - pc;
+            if ((lir->opcode == kThumbBCond) && (delta > 254 || delta < -256)) {
+                lir->opcode = kThumb2BCond;
+                oatSetupResourceMasks(lir);
+                res = kRetryAll;
             }
             lir->operands[0] = delta >> 1;
         } else if (lir->opcode == kThumb2BUncond) {
diff --git a/src/compiler/codegen/arm/CodegenCommon.cc b/src/compiler/codegen/arm/CodegenCommon.cc
index 1aba82c..208eecf 100644
--- a/src/compiler/codegen/arm/CodegenCommon.cc
+++ b/src/compiler/codegen/arm/CodegenCommon.cc
@@ -226,37 +226,6 @@
 }
 
 /*
- * Set up the accurate resource mask for branch instructions
- */
-static void relaxBranchMasks(ArmLIR* lir)
-{
-    int flags = EncodingMap[lir->opcode].flags;
-
-    /* Make sure only branch instructions are passed here */
-    assert(flags & IS_BRANCH);
-
-    lir->useMask = lir->defMask = ENCODE_REG_PC;
-
-    if (flags & REG_DEF_LR) {
-        lir->defMask |= ENCODE_REG_LR;
-    }
-
-    if (flags & (REG_USE0 | REG_USE1 | REG_USE2 | REG_USE3)) {
-        int i;
-
-        for (i = 0; i < 4; i++) {
-            if (flags & (1 << (kRegUse0 + i))) {
-                setupRegMask(&lir->useMask, lir->operands[i]);
-            }
-        }
-    }
-
-    if (flags & USES_CCODES) {
-        lir->useMask |= ENCODE_CCODE;
-    }
-}
-
-/*
  * The following are building blocks to construct low-level IRs with 0 - 4
  * operands.
  */
@@ -383,36 +352,3 @@
     /* Mark all resources as being clobbered */
     barrier->defMask = -1;
 }
-
-/* Create the PC reconstruction slot if not already done */
-static ArmLIR* genCheckCommon(CompilationUnit* cUnit, int dOffset,
-                              ArmLIR* branch,
-                              ArmLIR* pcrLabel)
-{
-    //FIXME - won't be rolling back, need to throw now.
-    UNIMPLEMENTED(WARNING);
-#if 0
-
-    /* Forget all def info (because we might rollback here.  Bug #2367397 */
-    oatResetDefTracking(cUnit);
-
-    /* Set up the place holder to reconstruct this Dalvik PC */
-    if (pcrLabel == NULL) {
-        int dPC = (int) (cUnit->insns + dOffset);
-        pcrLabel = (ArmLIR* ) oatNew(sizeof(ArmLIR), true);
-        pcrLabel->opcode = kArmPseudoPCReconstructionCell;
-        pcrLabel->operands[0] = dPC;
-        pcrLabel->operands[1] = dOffset;
-        /* Insert the place holder to the growable list */
-        oatInsertGrowableList(&cUnit->pcReconstructionList,
-                              (intptr_t) pcrLabel);
-    }
-#endif
-    /* Branch to the PC reconstruction code */
-    branch->generic.target = (LIR*) pcrLabel;
-
-    /* Clear the conservative flags for branches that punt to the interpreter */
-    relaxBranchMasks(branch);
-
-    return pcrLabel;
-}
diff --git a/src/compiler/codegen/arm/MethodCodegenDriver.cc b/src/compiler/codegen/arm/MethodCodegenDriver.cc
index c7c15cf..a9d77ec 100644
--- a/src/compiler/codegen/arm/MethodCodegenDriver.cc
+++ b/src/compiler/codegen/arm/MethodCodegenDriver.cc
@@ -438,7 +438,7 @@
             loadValueDirectFixed(cUnit, rlArg, r1);
             break;
         case 1: // Is "this" null? [use r1]
-            genNullCheck(cUnit, oatSSASrc(mir,0), r1, mir->offset, NULL);
+            genNullCheck(cUnit, oatSSASrc(mir,0), r1, mir);
             // get this->klass_ [use r1, set rLR]
             loadWordDisp(cUnit, r1, Object::ClassOffset().Int32Value(), rLR);
             break;
@@ -507,7 +507,7 @@
             break;
         case 4:
             // Is "this" null? [use r1]
-            genNullCheck(cUnit, oatSSASrc(mir,0), r1, mir->offset, NULL);
+            genNullCheck(cUnit, oatSSASrc(mir,0), r1, mir);
             // get this->clazz [use r1, set rLR]
             loadWordDisp(cUnit, r1, Object::ClassOffset().Int32Value(), rLR);
             break;
@@ -601,8 +601,7 @@
             loadWordDisp(cUnit, r0, Method::DeclaringClassOffset().Int32Value(),
                          rLR);
             // Is "this" null? [use r1]
-            genNullCheck(cUnit, oatSSASrc(mir,0), r1,
-                           mir->offset, NULL);
+            genNullCheck(cUnit, oatSSASrc(mir,0), r1, mir);
             break;
         case 1: // Get method->declaring_class_->super_class [usr rLR, set rLR]
             loadWordDisp(cUnit, rLR, Class::SuperClassOffset().Int32Value(),
@@ -676,7 +675,7 @@
             loadWordDisp(cUnit, r0, Method::DeclaringClassOffset().Int32Value(),
                          r0);
             // Null this?
-            genNullCheck(cUnit, oatSSASrc(mir,0), r1, mir->offset, NULL);
+            genNullCheck(cUnit, oatSSASrc(mir,0), r1, mir);
             // Get method->declaring_class_->super_class [usr r0, set r0]
             loadWordDisp(cUnit, r0, Class::SuperClassOffset().Int32Value(), r0);
             break;
@@ -687,7 +686,7 @@
                 tReg = oatAllocTemp(cUnit);
                 loadWordDisp(cUnit, r0, art::Array::LengthOffset().Int32Value(),
                              tReg);
-                genBoundsCheck(cUnit, tReg, rLR, mir->offset, NULL);
+                genBoundsCheck(cUnit, tReg, rLR, mir, kArmThrowNoSuchMethod);
                 oatFreeTemp(cUnit, tReg);
             }
             // Adjust vtable_ base past object header
@@ -761,8 +760,7 @@
     //TODO: better to move this into CallInsn lists
     // Load direct & need a "this" null check?
     if (pcrLabel) {
-        *pcrLabel = genNullCheck(cUnit, oatSSASrc(mir,0), r1,
-                                 mir->offset, NULL);
+        *pcrLabel = genNullCheck(cUnit, oatSSASrc(mir,0), r1, mir);
     }
     return callState;
 }
@@ -1215,12 +1213,19 @@
             genThrow(cUnit, mir, rlSrc[0]);
             break;
 
+        case OP_THROW_VERIFICATION_ERROR:
+            loadWordDisp(cUnit, rSELF,
+                OFFSETOF_MEMBER(Thread, pThrowVerificationErrorFromCode), rLR);
+            loadConstant(cUnit, r0, mir->dalvikInsn.vA);
+            loadConstant(cUnit, r1, mir->dalvikInsn.vB);
+            opReg(cUnit, kOpBlx, rLR);
+            break;
+
         case OP_ARRAY_LENGTH:
             int lenOffset;
             lenOffset = Array::LengthOffset().Int32Value();
             rlSrc[0] = loadValue(cUnit, rlSrc[0], kCoreReg);
-            genNullCheck(cUnit, rlSrc[0].sRegLow, rlSrc[0].lowReg,
-                         mir->offset, NULL);
+            genNullCheck(cUnit, rlSrc[0].sRegLow, rlSrc[0].lowReg, mir);
             rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
             loadWordDisp(cUnit, rlSrc[0].lowReg, lenOffset,
                          rlResult.lowReg);
@@ -1986,6 +1991,77 @@
     }
 }
 
+static void handleThrowLaunchpads(CompilationUnit *cUnit)
+{
+    ArmLIR** throwLabel =
+        (ArmLIR **) cUnit->throwLaunchpads.elemList;
+    int numElems = cUnit->throwLaunchpads.numUsed;
+    int i;
+
+    for (i = 0; i < numElems; i++) {
+        ArmLIR* lab = throwLabel[i];
+        cUnit->currentDalvikOffset = lab->operands[1];
+        oatAppendLIR(cUnit, (LIR *)lab);
+        int funcOffset = 0;
+        int v1 = lab->operands[2];
+        int v2 = lab->operands[3];
+        switch(lab->operands[0]) {
+            case kArmThrowNullPointer:
+                funcOffset = OFFSETOF_MEMBER(Thread, pThrowNullPointerFromCode);
+                break;
+            case kArmThrowArrayBounds:
+                if (v2 != r0) {
+                    genRegCopy(cUnit, r0, v1);
+                    genRegCopy(cUnit, r1, v2);
+                } else {
+                    if (v1 == r1) {
+                        genRegCopy(cUnit, r12, v1);
+                        genRegCopy(cUnit, r1, v2);
+                        genRegCopy(cUnit, r0, r12);
+                    } else {
+                        genRegCopy(cUnit, r1, v2);
+                        genRegCopy(cUnit, r0, v1);
+                    }
+                }
+                funcOffset = OFFSETOF_MEMBER(Thread, pThrowArrayBoundsFromCode);
+                break;
+            case kArmThrowDivZero:
+                funcOffset = OFFSETOF_MEMBER(Thread, pThrowDivZeroFromCode);
+                break;
+            case kArmThrowVerificationError:
+                loadConstant(cUnit, r0, v1);
+                loadConstant(cUnit, r1, v2);
+                funcOffset =
+                    OFFSETOF_MEMBER(Thread, pThrowVerificationErrorFromCode);
+                break;
+            case kArmThrowNegArraySize:
+                genRegCopy(cUnit, r0, v1);
+                funcOffset =
+                    OFFSETOF_MEMBER(Thread, pThrowNegArraySizeFromCode);
+                break;
+            case kArmThrowInternalError:
+                genRegCopy(cUnit, r0, v1);
+                funcOffset =
+                    OFFSETOF_MEMBER(Thread, pThrowInternalErrorFromCode);
+                break;
+            case kArmThrowRuntimeException:
+                genRegCopy(cUnit, r0, v1);
+                funcOffset =
+                    OFFSETOF_MEMBER(Thread, pThrowRuntimeExceptionFromCode);
+                break;
+            case kArmThrowNoSuchMethod:
+                genRegCopy(cUnit, r0, v1);
+                funcOffset =
+                    OFFSETOF_MEMBER(Thread, pThrowNoSuchMethodFromCode);
+                break;
+            default:
+                LOG(FATAL) << "Unexpected throw kind: " << lab->operands[0];
+        }
+        loadWordDisp(cUnit, rSELF, funcOffset, rLR);
+        opReg(cUnit, kOpBlx, rLR);
+    }
+}
+
 void oatMethodMIR2LIR(CompilationUnit* cUnit)
 {
     /* Used to hold the labels of each block */
@@ -1995,6 +2071,8 @@
     oatDataFlowAnalysisDispatcher(cUnit, methodBlockCodeGen,
                                   kPreOrderDFSTraversal, false /* Iterative */);
     removeRedundantBranches(cUnit);
+
+    handleThrowLaunchpads(cUnit);
 }
 
 /* Common initialization routine for an architecture family */
diff --git a/src/compiler/codegen/arm/Thumb2/Gen.cc b/src/compiler/codegen/arm/Thumb2/Gen.cc
index 89911fe..0db63f9 100644
--- a/src/compiler/codegen/arm/Thumb2/Gen.cc
+++ b/src/compiler/codegen/arm/Thumb2/Gen.cc
@@ -445,8 +445,7 @@
         // Field offset in r0
         rlObj = loadValue(cUnit, rlObj, kCoreReg);
         rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
-        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
-                     NULL);/* null object? */
+        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null object? */
         loadBaseIndexed(cUnit, rlObj.lowReg, r0, rlResult.lowReg, 0, size);
         oatGenMemBarrier(cUnit, kSY);
         storeValue(cUnit, rlDest, rlResult);
@@ -459,8 +458,7 @@
         int fieldOffset = fieldPtr->GetOffset().Int32Value();
         rlObj = loadValue(cUnit, rlObj, kCoreReg);
         rlResult = oatEvalLoc(cUnit, rlDest, regClass, true);
-        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
-                     NULL);/* null object? */
+        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null object? */
         loadBaseDisp(cUnit, mir, rlObj.lowReg, fieldOffset, rlResult.lowReg,
                      size, rlObj.sRegLow);
         if (isVolatile) {
@@ -481,8 +479,7 @@
         // Field offset in r0
         rlObj = loadValue(cUnit, rlObj, kCoreReg);
         rlSrc = loadValue(cUnit, rlSrc, regClass);
-        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
-                     NULL);/* null object? */
+        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null object? */
         oatGenMemBarrier(cUnit, kSY);
         storeBaseIndexed(cUnit, rlObj.lowReg, r0, rlSrc.lowReg, 0, size);
     } else {
@@ -494,8 +491,7 @@
         int fieldOffset = fieldPtr->GetOffset().Int32Value();
         rlObj = loadValue(cUnit, rlObj, kCoreReg);
         rlSrc = loadValue(cUnit, rlSrc, regClass);
-        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
-                    NULL);/* null object? */
+        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
 
         if (isVolatile) {
             oatGenMemBarrier(cUnit, kSY);
@@ -519,8 +515,7 @@
         // Field offset in r0
         rlObj = loadValue(cUnit, rlObj, kCoreReg);
         rlResult = oatEvalLoc(cUnit, rlDest, kAnyReg, true);
-        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
-                     NULL);/* null object? */
+        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
         opRegReg(cUnit, kOpAdd, r0, rlObj.lowReg);
         loadPair(cUnit, r0, rlResult.lowReg, rlResult.highReg);
         oatGenMemBarrier(cUnit, kSY);
@@ -537,8 +532,7 @@
 
         assert(rlDest.wide);
 
-        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
-                     NULL);/* null object? */
+        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
         opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
         rlResult = oatEvalLoc(cUnit, rlDest, kAnyReg, true);
 
@@ -563,8 +557,7 @@
         // Field offset in r0
         rlObj = loadValue(cUnit, rlObj, kCoreReg);
         rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
-        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
-                     NULL);/* null object? */
+        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
         opRegReg(cUnit, kOpAdd, r0, rlObj.lowReg);
         oatGenMemBarrier(cUnit, kSY);
         storePair(cUnit, r0, rlSrc.lowReg, rlSrc.highReg);
@@ -579,8 +572,7 @@
         rlObj = loadValue(cUnit, rlObj, kCoreReg);
         int regPtr;
         rlSrc = loadValueWide(cUnit, rlSrc, kAnyReg);
-        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir->offset,
-                     NULL);/* null object? */
+        genNullCheck(cUnit, rlObj.sRegLow, rlObj.lowReg, mir);/* null obj? */
         regPtr = oatAllocTemp(cUnit);
         opRegRegImm(cUnit, kOpAdd, regPtr, rlObj.lowReg, fieldOffset);
 
@@ -919,7 +911,7 @@
     assert(art::Monitor::kLwShapeThin == 0);
     loadValueDirectFixed(cUnit, rlSrc, r1);  // Get obj
     oatLockCallTemps(cUnit);  // Prepare for explicit register usage
-    genNullCheck(cUnit, rlSrc.sRegLow, r1, mir->offset, NULL);
+    genNullCheck(cUnit, rlSrc.sRegLow, r1, mir);
     loadWordDisp(cUnit, rSELF, Thread::IdOffset().Int32Value(), r3);
     newLIR3(cUnit, kThumb2Ldrex, r2, r1,
             Object::MonitorOffset().Int32Value() >> 2); // Get object->lock
@@ -970,7 +962,7 @@
     oatFlushAllRegs(cUnit);
     loadValueDirectFixed(cUnit, rlSrc, r1);  // Get obj
     oatLockCallTemps(cUnit);  // Prepare for explicit register usage
-    genNullCheck(cUnit, rlSrc.sRegLow, r1, mir->offset, NULL);
+    genNullCheck(cUnit, rlSrc.sRegLow, r1, mir);
     loadWordDisp(cUnit, r1, Object::MonitorOffset().Int32Value(), r2); // Get lock
     loadWordDisp(cUnit, rSELF, Thread::IdOffset().Int32Value(), r3);
     // Is lock unheld on lock or held by us (==threadId) on unlock?
@@ -1241,14 +1233,6 @@
     return branch;
 }
 
-/* Generate a unconditional branch to go to the interpreter */
-static inline ArmLIR* genTrap(CompilationUnit* cUnit, int dOffset,
-                                  ArmLIR* pcrLabel)
-{
-    ArmLIR* branch = opNone(cUnit, kOpUncondBr);
-    return genCheckCommon(cUnit, dOffset, branch, pcrLabel);
-}
-
 /*
  * Generate array store
  *
@@ -1269,8 +1253,7 @@
     ArmLIR*  pcrLabel = NULL;
 
     if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) {
-        pcrLabel = genNullCheck(cUnit, rlArray.sRegLow, r1,
-                                mir->offset, NULL);
+        pcrLabel = genNullCheck(cUnit, rlArray.sRegLow, r1, mir);
     }
     loadWordDisp(cUnit, rSELF,
                  OFFSETOF_MEMBER(Thread, pCanPutArrayElementFromCode), rLR);
@@ -1302,8 +1285,8 @@
         loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
         /* regPtr -> array data */
         opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
-        genBoundsCheck(cUnit, rlIndex.lowReg, regLen, mir->offset,
-                       pcrLabel);
+        genBoundsCheck(cUnit, rlIndex.lowReg, regLen, mir,
+                       kArmThrowArrayBounds);
         oatFreeTemp(cUnit, regLen);
     } else {
         /* regPtr -> array data */
@@ -1334,8 +1317,7 @@
     ArmLIR*  pcrLabel = NULL;
 
     if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) {
-        pcrLabel = genNullCheck(cUnit, rlArray.sRegLow,
-                                rlArray.lowReg, mir->offset, NULL);
+        pcrLabel = genNullCheck(cUnit, rlArray.sRegLow, rlArray.lowReg, mir);
     }
 
     regPtr = oatAllocTemp(cUnit);
@@ -1346,8 +1328,8 @@
         loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
         /* regPtr -> array data */
         opRegRegImm(cUnit, kOpAdd, regPtr, rlArray.lowReg, dataOffset);
-        genBoundsCheck(cUnit, rlIndex.lowReg, regLen, mir->offset,
-                       pcrLabel);
+        genBoundsCheck(cUnit, rlIndex.lowReg, regLen, mir,
+                       kArmThrowArrayBounds);
         oatFreeTemp(cUnit, regLen);
     } else {
         /* regPtr -> array data */
@@ -1409,8 +1391,7 @@
     ArmLIR*  pcrLabel = NULL;
 
     if (!(mir->OptimizationFlags & MIR_IGNORE_NULL_CHECK)) {
-        pcrLabel = genNullCheck(cUnit, rlArray.sRegLow, rlArray.lowReg,
-                                mir->offset, NULL);
+        pcrLabel = genNullCheck(cUnit, rlArray.sRegLow, rlArray.lowReg, mir);
     }
 
     if (!(mir->OptimizationFlags & MIR_IGNORE_RANGE_CHECK)) {
@@ -1420,8 +1401,8 @@
         loadWordDisp(cUnit, rlArray.lowReg, lenOffset, regLen);
         /* regPtr -> array data */
         opRegImm(cUnit, kOpAdd, regPtr, dataOffset);
-        genBoundsCheck(cUnit, rlIndex.lowReg, regLen, mir->offset,
-                       pcrLabel);
+        genBoundsCheck(cUnit, rlIndex.lowReg, regLen, mir,
+                       kArmThrowArrayBounds);
         oatFreeTemp(cUnit, regLen);
     } else {
         /* regPtr -> array data */
@@ -1429,7 +1410,7 @@
     }
     /* at this point, regPtr points to array, 2 live temps */
     if ((size == kLong) || (size == kDouble)) {
-        //TODO: need specific wide routine that can handle fp regs
+        //TUNING: specific wide routine that can handle fp regs
         if (scale) {
             int rNewIndex = oatAllocTemp(cUnit);
             opRegRegImm(cUnit, kOpLsl, rNewIndex, rlIndex.lowReg, scale);
@@ -1693,7 +1674,7 @@
         loadWordDisp(cUnit, rSELF, funcOffset, rLR);
         loadValueDirectFixed(cUnit, rlSrc1, r0);
         if (checkZero) {
-            genNullCheck(cUnit, rlSrc2.sRegLow, r1, mir->offset, NULL);
+            genImmedCheck(cUnit, kArmCondEq, r1, 0, mir, kArmThrowDivZero);
         }
         opReg(cUnit, kOpBlx, rLR);
         oatClobberCallRegs(cUnit);
@@ -1844,7 +1825,7 @@
     } else {
         // Reverse subtract: (src << (shift + 1)) - src.
         assert(powerOfTwoMinusOne);
-        // TODO: rsb dst, src, src lsl#lowestSetBit(lit + 1)
+        // TUNING: rsb dst, src, src lsl#lowestSetBit(lit + 1)
         int tReg = oatAllocTemp(cUnit);
         opRegRegImm(cUnit, kOpLsl, tReg, rlSrc.lowReg, lowestSetBit(lit + 1));
         opRegRegReg(cUnit, kOpSub, rlResult.lowReg, tReg, rlSrc.lowReg);
@@ -1925,8 +1906,7 @@
         case OP_REM_INT_LIT8:
         case OP_REM_INT_LIT16:
             if (lit == 0) {
-                UNIMPLEMENTED(FATAL);
-                // FIXME: generate an explicit throw here
+                genImmedCheck(cUnit, kArmCondAl, 0, 0, mir, kArmThrowDivZero);
                 return false;
             }
             if (handleEasyDivide(cUnit, dalvikOpcode, rlSrc, rlDest, lit)) {
diff --git a/src/runtime_support.h b/src/runtime_support.h
index 23126d7..04c464f 100644
--- a/src/runtime_support.h
+++ b/src/runtime_support.h
@@ -44,10 +44,4 @@
 
 #endif
 
-namespace art {
-  int64_t D2L(double);
-  int64_t F2L(float);
-  void HandleFillArrayDataFromCode(art::Array*, const uint16_t*);
-}
-
 #endif  // ART_SRC_RUNTIME_SUPPORT_H_
diff --git a/src/thread.cc b/src/thread.cc
index 45ff6dd..d5174ab 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -142,6 +142,110 @@
     UNIMPLEMENTED(FATAL) << "Stack overflow: " << PrettyMethod(method);
 }
 
+// TODO: placeholder
+static void ThrowNullPointerFromCode() {
+    //NOTE: to save code space, this handler must look up caller's Method*
+    UNIMPLEMENTED(FATAL) << "Null pointer exception";
+}
+
+// TODO: placeholder
+static void ThrowDivZeroFromCode() {
+    UNIMPLEMENTED(FATAL) << "Divide by zero";
+}
+
+// TODO: placeholder
+static void ThrowArrayBoundsFromCode(int32_t index, int32_t limit) {
+    UNIMPLEMENTED(FATAL) << "Bound check exception, idx: " << index <<
+        ", limit: " << limit;
+}
+
+// TODO: placeholder
+static void ThrowVerificationErrorFromCode(int32_t src1, int32_t ref) {
+    UNIMPLEMENTED(FATAL) << "Verification error, src1: " << src1 <<
+        " ref: " << ref;
+}
+
+// TODO: placeholder
+static void ThrowNegArraySizeFromCode(int32_t index) {
+    UNIMPLEMENTED(FATAL) << "Negative array size: " << index;
+}
+
+// TODO: placeholder
+static void ThrowInternalErrorFromCode(int32_t errnum) {
+    UNIMPLEMENTED(FATAL) << "Internal error: " << errnum;
+}
+
+// TODO: placeholder
+static void ThrowRuntimeExceptionFromCode(int32_t errnum) {
+    UNIMPLEMENTED(FATAL) << "Internal error: " << errnum;
+}
+
+// TODO: placeholder
+static void ThrowNoSuchMethodFromCode(int32_t method_idx) {
+    UNIMPLEMENTED(FATAL) << "No such method, idx: " << method_idx;
+}
+
+/*
+ * Temporary placeholder.  Should include run-time checks for size
+ * of fill data <= size of array.  If not, throw arrayOutOfBoundsException.
+ * As with other new "FromCode" routines, this should return to the caller
+ * only if no exception has been thrown.
+ *
+ * NOTE: When dealing with a raw dex file, the data to be copied uses
+ * little-endian ordering.  Require that oat2dex do any required swapping
+ * so this routine can get by with a memcpy().
+ *
+ * Format of the data:
+ *  ushort ident = 0x0300   magic value
+ *  ushort width            width of each element in the table
+ *  uint   size             number of elements in the table
+ *  ubyte  data[size*width] table of data values (may contain a single-byte
+ *                          padding at the end)
+ */
+static void HandleFillArrayDataFromCode(Array* array, const uint16_t* table)
+{
+    uint32_t size = (uint32_t)table[2] | (((uint32_t)table[3]) << 16);
+    uint32_t size_in_bytes = size * table[1];
+    if (static_cast<int32_t>(size) > array->GetLength()) {
+      ThrowArrayBoundsFromCode(array->GetLength(), size);
+    }
+    memcpy((char*)array + art::Array::DataOffset().Int32Value(),
+           (char*)&table[4], size_in_bytes);
+}
+
+// TODO: move to more appropriate location
+/*
+ * Float/double conversion requires clamping to min and max of integer form.  If
+ * target doesn't support this normally, use these.
+ */
+static int64_t D2L(double d)
+{
+    static const double kMaxLong = (double)(int64_t)0x7fffffffffffffffULL;
+    static const double kMinLong = (double)(int64_t)0x8000000000000000ULL;
+    if (d >= kMaxLong)
+        return (int64_t)0x7fffffffffffffffULL;
+    else if (d <= kMinLong)
+        return (int64_t)0x8000000000000000ULL;
+    else if (d != d) // NaN case
+        return 0;
+    else
+        return (int64_t)d;
+}
+
+static int64_t F2L(float f)
+{
+    static const float kMaxLong = (float)(int64_t)0x7fffffffffffffffULL;
+    static const float kMinLong = (float)(int64_t)0x8000000000000000ULL;
+    if (f >= kMaxLong)
+        return (int64_t)0x7fffffffffffffffULL;
+    else if (f <= kMinLong)
+        return (int64_t)0x8000000000000000ULL;
+    else if (f != f) // NaN case
+        return 0;
+    else
+        return (int64_t)f;
+}
+
 void Thread::InitFunctionPointers() {
 #if defined(__arm__)
   pShlLong = art_shl_long;
@@ -195,6 +299,14 @@
   pFindFieldFromCode = Field::FindFieldFromCode;
   pCheckSuspendFromCode = CheckSuspendFromCode;
   pStackOverflowFromCode = StackOverflowFromCode;
+  pThrowNullPointerFromCode = ThrowNullPointerFromCode;
+  pThrowArrayBoundsFromCode = ThrowArrayBoundsFromCode;
+  pThrowDivZeroFromCode = ThrowDivZeroFromCode;
+  pThrowVerificationErrorFromCode = ThrowVerificationErrorFromCode;
+  pThrowNegArraySizeFromCode = ThrowNegArraySizeFromCode;
+  pThrowRuntimeExceptionFromCode = ThrowRuntimeExceptionFromCode;
+  pThrowInternalErrorFromCode = ThrowInternalErrorFromCode;
+  pThrowNoSuchMethodFromCode = ThrowNoSuchMethodFromCode;
   pDebugMe = DebugMe;
 }
 
diff --git a/src/thread.h b/src/thread.h
index 9573458..4d27151 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -224,6 +224,14 @@
   Field* (*pFindFieldFromCode)(uint32_t, const Method*);
   void (*pCheckSuspendFromCode)(Thread*);
   void (*pStackOverflowFromCode)(Method*);
+  void (*pThrowNullPointerFromCode)();
+  void (*pThrowArrayBoundsFromCode)(int32_t, int32_t);
+  void (*pThrowDivZeroFromCode)();
+  void (*pThrowVerificationErrorFromCode)(int32_t, int32_t);
+  void (*pThrowNegArraySizeFromCode)(int32_t);
+  void (*pThrowRuntimeExceptionFromCode)(int32_t);
+  void (*pThrowInternalErrorFromCode)(int32_t);
+  void (*pThrowNoSuchMethodFromCode)(int32_t);
 
   class StackVisitor {
    public: