Check-cast, instance-of, misc fixes

Support for check-cast and instanceof (largely untested).  Added a bunch of
helper stubs, a debugging option to show the method name if we try to branch
to an uncompiled method, new tests and a missing call to reset the compiler's
arena storage.

Change-Id: I933ad1fbdbca110f92c9201cae2353bf4862a8ac
diff --git a/src/compiler/Frontend.cc b/src/compiler/Frontend.cc
index 8488606..2abefe10 100644
--- a/src/compiler/Frontend.cc
+++ b/src/compiler/Frontend.cc
@@ -671,6 +671,7 @@
 bool oatCompileMethod(Method* method, art::InstructionSet insnSet)
 {
     LOG(INFO) << "Compiling " << PrettyMethod(method) << "...";
+    oatArenaReset();
 
     CompilationUnit cUnit;
     art::ClassLinker* class_linker = art::Runtime::Current()->GetClassLinker();
diff --git a/src/compiler/codegen/arm/MethodCodegenDriver.cc b/src/compiler/codegen/arm/MethodCodegenDriver.cc
index 9c87659..44cbc0e 100644
--- a/src/compiler/codegen/arm/MethodCodegenDriver.cc
+++ b/src/compiler/codegen/arm/MethodCodegenDriver.cc
@@ -15,6 +15,7 @@
  */
 
 #define FORCE_SLOW 0
+#define DISPLAY_MISSING_TARGETS
 
 static const RegLocation badLoc = {kLocDalvikFrame, 0, 0, INVALID_REG,
                                    INVALID_REG, INVALID_SREG, 0,
@@ -863,6 +864,19 @@
     return callState;
 }
 
+#ifdef DISPLAY_MISSING_TARGETS
+// Debugging routine - if null target, branch to DebugMe
+static void genShowTarget(CompilationUnit* cUnit)
+{
+    ArmLIR* branchOver = genCmpImmBranch(cUnit, kArmCondNe, rLR, 0);
+    loadWordDisp(cUnit, rSELF,
+                 OFFSETOF_MEMBER(Thread, pDebugMe), rLR);
+    ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
+    target->defMask = -1;
+    branchOver->generic.target = (LIR*)target;
+}
+#endif
+
 static void genInvokeStaticDirect(CompilationUnit* cUnit, MIR* mir,
                                   bool direct, bool range)
 {
@@ -886,6 +900,9 @@
     while (callState >= 0) {
         callState = nextCallInsn(cUnit, mir, dInsn, callState, NULL);
     }
+#ifdef DISPLAY_MISSING_TARGETS
+    genShowTarget(cUnit);
+#endif
     newLIR1(cUnit, kThumbBlxR, rLR);
 }
 
@@ -914,6 +931,9 @@
     while (callState >= 0) {
         callState = nextInterfaceCallInsn(cUnit, mir, dInsn, callState, NULL);
     }
+#ifdef DISPLAY_MISSING_TARGETS
+    genShowTarget(cUnit);
+#endif
     newLIR1(cUnit, kThumbBlxR, rLR);
 }
 
@@ -963,6 +983,9 @@
     while (callState >= 0) {
         callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
     }
+#ifdef DISPLAY_MISSING_TARGETS
+    genShowTarget(cUnit);
+#endif
     newLIR1(cUnit, kThumbBlxR, rLR);
 }
 
@@ -999,6 +1022,9 @@
     while (callState >= 0) {
         callState = nextCallInsn(cUnit, mir, dInsn, callState, rollback);
     }
+#ifdef DISPLAY_MISSING_TARGETS
+    genShowTarget(cUnit);
+#endif
     newLIR1(cUnit, kThumbBlxR, rLR);
 }
 
diff --git a/src/compiler/codegen/arm/Thumb2/Gen.cc b/src/compiler/codegen/arm/Thumb2/Gen.cc
index b9e135c..dbaf9ea 100644
--- a/src/compiler/codegen/arm/Thumb2/Gen.cc
+++ b/src/compiler/codegen/arm/Thumb2/Gen.cc
@@ -513,7 +513,7 @@
     int mReg = loadCurrMethod(cUnit);
     int resReg = oatAllocTemp(cUnit);
     RegLocation rlResult = oatEvalLoc(cUnit, rlDest, kCoreReg, true);
-    loadWordDisp(cUnit, mReg, Method::DexCacheStringsOffset().Int32Value(),
+    loadWordDisp(cUnit, mReg, Method::DexCacheResolvedTypesOffset().Int32Value(),
                  resReg);
     loadWordDisp(cUnit, resReg, Array::DataOffset().Int32Value() +
                  (sizeof(String*) * mir->dalvikInsn.vB), rlResult.lowReg);
@@ -595,35 +595,52 @@
 static void genInstanceof(CompilationUnit* cUnit, MIR* mir, RegLocation rlDest,
                           RegLocation rlSrc)
 {
-   // May generate a call - use explicit registers
-    RegLocation rlResult;
-    Class* classPtr = cUnit->method->GetDeclaringClass()->GetDexCache()->
-        GetResolvedType(mir->dalvikInsn.vC);
+    // May generate a call - use explicit registers
+    oatLockCallTemps(cUnit);
+    art::Class* classPtr = cUnit->method->GetDexCacheResolvedTypes()->
+        Get(mir->dalvikInsn.vC);
+    int classReg = r2;   // Fixed usage
+    loadCurrMethodDirect(cUnit, r1);  // r1 <= current Method*
+    loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(),
+                 classReg);
+    loadWordDisp(cUnit, classReg, Array::DataOffset().Int32Value() +
+                 (sizeof(String*) * mir->dalvikInsn.vC), classReg);
     if (classPtr == NULL) {
-        UNIMPLEMENTED(FATAL) << "Handle null class pointer";
+        // Generate a runtime test
+        ArmLIR* hopBranch = genCmpImmBranch(cUnit, kArmCondNe, classReg, 0);
+        // Not resolved
+        // Call out to helper, which will return resolved type in r0
+        loadWordDisp(cUnit, rSELF,
+                     OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
+        loadConstant(cUnit, r0, mir->dalvikInsn.vC);
+        opReg(cUnit, kOpBlx, rLR); // resolveTypeFromCode(idx, method)
+        genRegCopy(cUnit, r2, r0); // Align usage with fast path
+        // Rejoin code paths
+        ArmLIR* hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
+        hopTarget->defMask = ENCODE_ALL;
+        hopBranch->generic.target = (LIR*)hopTarget;
     }
-    oatFlushAllRegs(cUnit);   /* Everything to home location */
-    loadValueDirectFixed(cUnit, rlSrc, r0);  /* Ref */
-    loadConstant(cUnit, r2, (int) classPtr );
+    // At this point, r2 has class
+    loadValueDirectFixed(cUnit, rlSrc, r3);  /* Ref */
     /* When taken r0 has NULL which can be used for store directly */
-    ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, r0, 0);
-    /* r1 now contains object->clazz */
+    ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, r3, 0);
+    /* load object->clazz */
     assert(Object::ClassOffset().Int32Value() == 0);
-    loadWordDisp(cUnit, r0,  Object::ClassOffset().Int32Value(), r1);
+    loadWordDisp(cUnit, r3,  Object::ClassOffset().Int32Value(), r1);
     /* r1 now contains object->clazz */
     loadWordDisp(cUnit, rSELF,
                  OFFSETOF_MEMBER(Thread, pInstanceofNonTrivialFromCode), rLR);
     loadConstant(cUnit, r0, 1);                /* Assume true */
     opRegReg(cUnit, kOpCmp, r1, r2);
     ArmLIR* branch2 = opCondBranch(cUnit, kArmCondEq);
-    genRegCopy(cUnit, r0, r1);
+    genRegCopy(cUnit, r0, r3);
     genRegCopy(cUnit, r1, r2);
     opReg(cUnit, kOpBlx, rLR);
     oatClobberCallRegs(cUnit);
     /* branch target here */
     ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
     target->defMask = ENCODE_ALL;
-    rlResult = oatGetReturn(cUnit);
+    RegLocation rlResult = oatGetReturn(cUnit);
     storeValue(cUnit, rlDest, rlResult);
     branch1->generic.target = (LIR*)target;
     branch2->generic.target = (LIR*)target;
@@ -631,32 +648,48 @@
 
 static void genCheckCast(CompilationUnit* cUnit, MIR* mir, RegLocation rlSrc)
 {
-    Class* classPtr = cUnit->method->GetDeclaringClass()->GetDexCache()->
-        GetResolvedType(mir->dalvikInsn.vB);
+    // May generate a call - use explicit registers
+    oatLockCallTemps(cUnit);
+    art::Class* classPtr = cUnit->method->GetDexCacheResolvedTypes()->
+        Get(mir->dalvikInsn.vB);
+    int classReg = r2;   // Fixed usage
+    loadCurrMethodDirect(cUnit, r1);  // r1 <= current Method*
+    loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(),
+                 classReg);
+    loadWordDisp(cUnit, classReg, Array::DataOffset().Int32Value() +
+                 (sizeof(String*) * mir->dalvikInsn.vB), classReg);
     if (classPtr == NULL) {
-        UNIMPLEMENTED(FATAL) << "Unimplemented null class pointer";
+        // Generate a runtime test
+        ArmLIR* hopBranch = genCmpImmBranch(cUnit, kArmCondNe, classReg, 0);
+        // Not resolved
+        // Call out to helper, which will return resolved type in r0
+        loadWordDisp(cUnit, rSELF,
+                     OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
+        loadConstant(cUnit, r0, mir->dalvikInsn.vB);
+        opReg(cUnit, kOpBlx, rLR); // resolveTypeFromCode(idx, method)
+        genRegCopy(cUnit, r2, r0); // Align usage with fast path
+        // Rejoin code paths
+        ArmLIR* hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
+        hopTarget->defMask = ENCODE_ALL;
+        hopBranch->generic.target = (LIR*)hopTarget;
     }
-    oatFlushAllRegs(cUnit);   /* Everything to home location */
-    loadConstant(cUnit, r1, (int) classPtr );
-    rlSrc = loadValue(cUnit, rlSrc, kCoreReg);
-    /* Null? */
-    ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq,
-                                      rlSrc.lowReg, 0);
-    /*
-     *  rlSrc.lowReg now contains object->clazz.  Note that
-     *  it could have been allocated r0, but we're okay so long
-     *  as we don't do anything desctructive until r0 is loaded
-     *  with clazz.
-     */
-    /* r0 now contains object->clazz */
-    loadWordDisp(cUnit, rlSrc.lowReg, Object::ClassOffset().Int32Value(), r0);
+    // At this point, r2 has class
+    loadValueDirectFixed(cUnit, rlSrc, r0);  /* Ref */
+    /* Null is OK - continue */
+    ArmLIR* branch1 = genCmpImmBranch(cUnit, kArmCondEq, r0, 0);
+    /* load object->clazz */
+    assert(Object::ClassOffset().Int32Value() == 0);
+    loadWordDisp(cUnit, r0,  Object::ClassOffset().Int32Value(), r1);
+    /* r1 now contains object->clazz */
     loadWordDisp(cUnit, rSELF,
-                 OFFSETOF_MEMBER(Thread, pInstanceofNonTrivialFromCode), rLR);
-    opRegReg(cUnit, kOpCmp, r0, r1);
-    ArmLIR* branch2 = opCondBranch(cUnit, kArmCondEq);
-    // Assume success - if not, artInstanceOfNonTrivial will handle throw
+                 OFFSETOF_MEMBER(Thread, pCheckCastFromCode), rLR);
+    opRegReg(cUnit, kOpCmp, r1, r2);
+    ArmLIR* branch2 = opCondBranch(cUnit, kArmCondEq); /* If equal, trivial yes */
+    genRegCopy(cUnit, r0, r1);
+    genRegCopy(cUnit, r1, r2);
     opReg(cUnit, kOpBlx, rLR);
     oatClobberCallRegs(cUnit);
+    /* branch target here */
     ArmLIR* target = newLIR0(cUnit, kArmPseudoTargetLabel);
     target->defMask = ENCODE_ALL;
     branch1->generic.target = (LIR*)target;
diff --git a/src/compiler_test.cc b/src/compiler_test.cc
index 9d2bb30..e232858 100644
--- a/src/compiler_test.cc
+++ b/src/compiler_test.cc
@@ -103,6 +103,17 @@
                         10);
 }
 
+// TODO: need stub for InstanceofNonTrivialFromCode
+TEST_F(CompilerTest, InstanceTest) {
+  CompileDirectMethod(NULL, "java.lang.Object", "<init>", "()V");
+  const ClassLoader* class_loader = LoadDex("IntMath");
+  CompileDirectMethod(class_loader, "IntMath", "<init>", "()V");
+  CompileDirectMethod(class_loader, "IntMathBase", "<init>", "()V");
+  AssertStaticIntMethod(class_loader, "IntMath", "instanceTest", "(I)I", 1352, 10);
+}
+
+// TODO: need check-cast test (when stub complete & we can throw/catch
+
 // TODO: Need invoke-interface test
 
 TEST_F(CompilerTest, SuperTest) {
@@ -117,8 +128,14 @@
 }
 
 TEST_F(CompilerTest, ConstStringTest) {
+  CompileDirectMethod(NULL, "java.lang.String", "<clinit>", "()V");
+  CompileDirectMethod(NULL, "java.lang.String", "<init>", "(II[C)V");
+  CompileDirectMethod(NULL, "java.lang.String", "<init>", "([CII)V");
+  CompileVirtualMethod(NULL, "java.lang.String", "_getChars", "(II[CI)V");
+  CompileVirtualMethod(NULL, "java.lang.String", "charAt", "(I)C");
+  CompileVirtualMethod(NULL, "java.lang.String", "length", "()I");
   AssertStaticIntMethod(LoadDex("IntMath"), "IntMath", "constStringTest",
-                                "(I)I", 2468, 1234);
+                                "(I)I", 1246, 1234);
 }
 
 TEST_F(CompilerTest, ConstClassTest) {
@@ -126,9 +143,18 @@
                                 "(I)I", 2222, 1111);
 }
 
+// TODO: Need native nativeFillInStackTrace()
 TEST_F(CompilerTest, DISABLED_CatchTest) {
   CompileDirectMethod(NULL, "java.lang.Object", "<init>", "()V");
   CompileDirectMethod(NULL, "java.lang.NullPointerException", "<init>", "()V");
+  CompileDirectMethod(NULL, "java.lang.RuntimeException", "<init>", "()V");
+  CompileDirectMethod(NULL, "java.lang.Exception", "<init>", "()V");
+  CompileDirectMethod(NULL, "java.lang.Throwable","<init>", "()V");
+  CompileDirectMethod(NULL, "java.util.ArrayList","<init>","()V");
+  CompileDirectMethod(NULL, "java.util.AbstractList","<init>","()V");
+  CompileDirectMethod(NULL, "java.util.AbstractCollection","<init>","()V");
+  CompileVirtualMethod(NULL, "java.lang.Throwable","fillInStackTrace","()Ljava/lang/Throwable;");
+  CompileDirectMethod(NULL, "java.lang.Throwable","nativeFillInStackTrace","()Ljava/lang/Object;");
   const ClassLoader* class_loader = LoadDex("IntMath");
   CompileDirectMethod(class_loader, "IntMath", "throwNullPointerException", "()V");
   AssertStaticIntMethod(class_loader, "IntMath", "catchBlock", "(I)I", 1579,
diff --git a/src/object.h b/src/object.h
index f26d9ef..bbd8acf 100644
--- a/src/object.h
+++ b/src/object.h
@@ -763,6 +763,10 @@
     return OFFSET_OF_OBJECT_MEMBER(Method, dex_cache_strings_);
   }
 
+  static MemberOffset DexCacheResolvedTypesOffset() {
+    return OFFSET_OF_OBJECT_MEMBER(Method, dex_cache_resolved_types_);
+  }
+
   static MemberOffset DexCacheInitializedStaticStorageOffset() {
     return OFFSET_OF_OBJECT_MEMBER(Method,
         dex_cache_initialized_static_storage_);
diff --git a/src/thread.cc b/src/thread.cc
index f7938f1..d40451e 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -107,6 +107,27 @@
     return Array::AllocFromCode(type_index, method, component_count);
 }
 
+// TODO: placeholder (throw on failure)
+static void CheckCastFromCode(const Class* a, const Class* b) {
+    if (a->IsAssignableFrom(b)) {
+        return;
+    }
+    UNIMPLEMENTED(FATAL);
+}
+
+// TODO: placeholder
+static void UnlockObjectFromCode(Thread* thread, Object* obj) {
+    // TODO: throw and unwind if lock not held
+    // TODO: throw and unwind on NPE
+    obj->MonitorExit();
+}
+
+// TODO: placeholder
+static void LockObjectFromCode(Thread* thread, Object* obj) {
+    // Need thread for ownership?
+    obj->MonitorEnter();
+}
+
 void Thread::InitFunctionPointers() {
 #if defined(__arm__)
   pShlLong = art_shl_long;
@@ -153,13 +174,11 @@
   pInitializeTypeFromCode = InitializeTypeFromCode;
   pResolveMethodFromCode = ResolveMethodFromCode;
   pInitializeStaticStorage = ClassLinker::InitializeStaticStorageFromCode;
+  pInstanceofNonTrivialFromCode = Object::InstanceOf;
+  pCheckCastFromCode = CheckCastFromCode;
+  pLockObjectFromCode = LockObjectFromCode;
+  pUnlockObjectFromCode = UnlockObjectFromCode;
   pDebugMe = DebugMe;
-#if 0
-bool (Thread::*pUnlockObject)(Thread*, Object*);
-int (Thread::*pInstanceofNonTrivialFromCode)(const Class*, const Class*);
-bool (Thread::*pUnlockObjectFromCode)(Thread*, Object*);
-void (Thread::*pLockObjectFromCode)(Thread*, Object*);
-#endif
 }
 
 Mutex* Mutex::Create(const char* name) {
diff --git a/src/thread.h b/src/thread.h
index a527deb..b255d6b 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -218,9 +218,10 @@
   Object* (*pGetObjStatic)(uint32_t, const Method*);
   void (*pSetObjStatic)(uint32_t, const Method*, Object*);
   void (*pCanPutArrayElementFromCode)(const Class*, const Class*);
-  int (*pInstanceofNonTrivialFromCode) (const Class*, const Class*);
+  bool (*pInstanceofNonTrivialFromCode) (const Object*, const Class*);
+  void (*pCheckCastFromCode) (const Class*, const Class*);
   Method* (*pFindInterfaceMethodInCache)(Class*, uint32_t, const Method*, struct DvmDex*);
-  bool (*pUnlockObjectFromCode)(Thread*, Object*);
+  void (*pUnlockObjectFromCode)(Thread*, Object*);
   void (*pLockObjectFromCode)(Thread*, Object*);
   void (*pThrowException)(Thread*, Throwable*);
   void (*pHandleFillArrayDataFromCode)(Array*, const uint16_t*);
diff --git a/test/IntMath/IntMath.java b/test/IntMath/IntMath.java
index 14c68d1..758e6df 100644
--- a/test/IntMath/IntMath.java
+++ b/test/IntMath/IntMath.java
@@ -25,6 +25,28 @@
         foo_ = 123;
     }
 
+    static int instanceTest(int x) {
+        IntMathBase a = new IntMathBase();
+        IntMath b = new IntMath();
+
+        if (a instanceof IntMathBase) {
+            x = x * 2;
+        }
+
+        if (a instanceof IntMath) {
+            x = x + 13;
+        }
+
+        if (b instanceof IntMathBase) {
+            x = x -1;
+        }
+
+        if (b instanceof IntMath) {
+            x = x + 1333;
+        }
+        return x;
+    }
+
     int tryThing() {
         int val = super.tryThing();
         return val + 10;
@@ -48,9 +70,8 @@
     }
 
     static int constStringTest(int x) {
-        /* TODO: flesh this test out when we can call string library */
         String str = "Hello World!";
-        return x * 2;
+        return x + str.length();
     }
 
     static void throwNullPointerException() {
@@ -867,6 +888,20 @@
         } else {
             System.out.println("superTest FAILED: " + res);
         }
+
+        res = constStringTest(10);
+        if (res == 22) {
+            System.out.println("stringTest PASSED");
+        } else {
+            System.out.println("stringTest FAILED: " + res);
+        }
+
+        res = instanceTest(10);
+        if (res == 1352) {
+            System.out.println("instanceTest PASSED");
+        } else {
+            System.out.println("instanceTest FAILED: " + res);
+        }
     }
 }