Add stack overflow check

Change-Id: I67fcb5ad4bda304879ce05561b03aa7cd46e9990
diff --git a/src/compiler/CompilerIR.h b/src/compiler/CompilerIR.h
index 4ce3177..9c9bcb0 100644
--- a/src/compiler/CompilerIR.h
+++ b/src/compiler/CompilerIR.h
@@ -263,6 +263,7 @@
     int frameSize;
     unsigned int coreSpillMask;
     unsigned int fpSpillMask;
+    unsigned int attrs;
     /*
      * CLEANUP/RESTRUCTURE: The code generation utilities don't have a built-in
      * mechanism to propogate the original Dalvik opcode address to the
diff --git a/src/compiler/Dataflow.cc b/src/compiler/Dataflow.cc
index 4e3c9c4..cb208a5 100644
--- a/src/compiler/Dataflow.cc
+++ b/src/compiler/Dataflow.cc
@@ -1989,6 +1989,16 @@
         int dfAttributes =
             oatDataFlowAttributes[mir->dalvikInsn.opcode];
 
+        int flags = dexGetFlagsFromOpcode(mir->dalvikInsn.opcode);
+
+        if (flags & kInstrCanThrow) {
+            cUnit->attrs &= ~METHOD_IS_THROW_FREE;
+        }
+
+        if (flags & kInstrInvoke) {
+            cUnit->attrs &= ~METHOD_IS_LEAF;
+        }
+
         int numUses = 0;
 
         if (dfAttributes & DF_FORMAT_35C) {
diff --git a/src/compiler/Frontend.cc b/src/compiler/Frontend.cc
index 702611d..2f86c48 100644
--- a/src/compiler/Frontend.cc
+++ b/src/compiler/Frontend.cc
@@ -707,6 +707,9 @@
          0;
 #endif
 
+    /* Assume non-throwing leaf */
+    cUnit.attrs = (METHOD_IS_LEAF | METHOD_IS_THROW_FREE);
+
     /* Initialize the block list */
     oatInitGrowableList(&cUnit.blockList, 40);
 
diff --git a/src/compiler/codegen/arm/MethodCodegenDriver.cc b/src/compiler/codegen/arm/MethodCodegenDriver.cc
index cf1ad04..2319a71 100644
--- a/src/compiler/codegen/arm/MethodCodegenDriver.cc
+++ b/src/compiler/codegen/arm/MethodCodegenDriver.cc
@@ -1809,14 +1809,42 @@
         oatLockTemp(cUnit, r1);
         oatLockTemp(cUnit, r2);
         oatLockTemp(cUnit, r3);
+
+        /*
+         * We can safely skip the stack overflow check if we're
+         * a leaf *and* our frame size < fudge factor.
+         */
+        bool skipOverflowCheck = ((cUnit->attrs & METHOD_IS_LEAF) &&
+                                  ((size_t)cUnit->frameSize <
+                                  art::Thread::kStackOverflowReservedBytes));
         newLIR0(cUnit, kArmPseudoMethodEntry);
+        if (!skipOverflowCheck) {
+            /* Load stack limit */
+            loadWordDisp(cUnit, rSELF,
+                         art::Thread::StackEndOffset().Int32Value(), r12);
+        }
         /* Spill core callee saves */
         newLIR1(cUnit, kThumb2Push, cUnit->coreSpillMask);
         /* Need to spill any FP regs? */
         if (cUnit->numFPSpills) {
             newLIR1(cUnit, kThumb2VPushCS, cUnit->numFPSpills);
         }
-        opRegImm(cUnit, kOpSub, rSP, cUnit->frameSize - (cUnit->numSpills * 4));
+        if (!skipOverflowCheck) {
+            opRegRegImm(cUnit, kOpSub, rLR, rSP,
+                        cUnit->frameSize - (cUnit->numSpills * 4));
+            opRegReg(cUnit, kOpCmp, rLR, r12);   // Stack overflow?
+            /* Begin conditional skip */
+            genIT(cUnit, kArmCondCc, "TT"); // Carry clear; unsigned <
+            loadWordDisp(cUnit, rSELF,
+                         OFFSETOF_MEMBER(Thread, pStackOverflowFromCode), rLR);
+            newLIR2(cUnit, kThumbAddRI8, rSP, cUnit->numSpills * 4);
+            opReg(cUnit, kOpBlx, rLR);
+            /* End conditional skip */
+            genRegCopy(cUnit, rSP, rLR);         // Establish stack
+        } else {
+            opRegImm(cUnit, kOpSub, rSP,
+                     cUnit->frameSize - (cUnit->numSpills * 4));
+        }
         storeBaseDisp(cUnit, rSP, 0, r0, kWord);
         flushIns(cUnit);
         oatFreeTemp(cUnit, r0);
diff --git a/src/thread.cc b/src/thread.cc
index e1de92d..45ff6dd 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -136,6 +136,12 @@
      */
 }
 
+// TODO: placeholder
+static void StackOverflowFromCode(Method* method) {
+    //NOTE: to save code space, this handler needs to look up its own Thread*
+    UNIMPLEMENTED(FATAL) << "Stack overflow: " << PrettyMethod(method);
+}
+
 void Thread::InitFunctionPointers() {
 #if defined(__arm__)
   pShlLong = art_shl_long;
@@ -188,6 +194,7 @@
   pUnlockObjectFromCode = UnlockObjectFromCode;
   pFindFieldFromCode = Field::FindFieldFromCode;
   pCheckSuspendFromCode = CheckSuspendFromCode;
+  pStackOverflowFromCode = StackOverflowFromCode;
   pDebugMe = DebugMe;
 }
 
@@ -380,7 +387,6 @@
     PLOG(FATAL) << "pthread_attr_getstack failed";
   }
 
-  const size_t kStackOverflowReservedBytes = 1024; // Space to throw a StackOverflowError in.
   if (stack_size <= kStackOverflowReservedBytes) {
     LOG(FATAL) << "attempt to attach a thread with a too-small stack (" << stack_size << " bytes)";
   }
@@ -388,7 +394,7 @@
   // stack_base is the "lowest addressable byte" of the stack.
   // Our stacks grow down, so we want stack_end_ to be near there, but reserving enough room
   // to throw a StackOverflowError.
-  stack_end_ = reinterpret_cast<byte*>(stack_base) - kStackOverflowReservedBytes;
+  stack_end_ = reinterpret_cast<byte*>(stack_base) + kStackOverflowReservedBytes;
 
   // Sanity check.
   int stack_variable;
diff --git a/src/thread.h b/src/thread.h
index 6095407..9573458 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -166,6 +166,7 @@
     kTerminated,
   };
 
+  static const size_t kStackOverflowReservedBytes = 1024; // Space to throw a StackOverflowError in.
 
   static const size_t kDefaultStackSize = 64 * KB;
 
@@ -222,6 +223,7 @@
   StaticStorageBase* (*pInitializeStaticStorage)(uint32_t, const Method*);
   Field* (*pFindFieldFromCode)(uint32_t, const Method*);
   void (*pCheckSuspendFromCode)(Thread*);
+  void (*pStackOverflowFromCode)(Method*);
 
   class StackVisitor {
    public: