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*);