diff options
| author | Andy McFadden <fadden@android.com> | 2011-02-16 16:50:40 -0800 |
|---|---|---|
| committer | Andy McFadden <fadden@android.com> | 2011-02-18 13:28:12 -0800 |
| commit | 6af2ddd107842c3737c04c37343cac9be17f4209 (patch) | |
| tree | 100466b99dc76fe04ff875758b717ac4b4f18b32 | |
| parent | d8dc6b738a5b531e4ed39e696754bfecb2533c62 (diff) | |
| download | android_dalvik-6af2ddd107842c3737c04c37343cac9be17f4209.tar.gz android_dalvik-6af2ddd107842c3737c04c37343cac9be17f4209.tar.bz2 android_dalvik-6af2ddd107842c3737c04c37343cac9be17f4209.zip | |
Defer marking of objects as finalizable
This shifts responsibility for marking an object as "finalizable" from
object creation to object initialization. We want to make the object
finalizable when Object.<init> completes. For performance reasons we
skip the call to the Object constructor (which doesn't do anything)
and just take the opportunity to check the class flag.
Handling of clone()d object isn't quite right yet.
Also, fixed a minor glitch in stubdefs.
Bug 3342343
Change-Id: I5b7b819079e5862dc9cbd1830bb445a852dc63bf
31 files changed, 363 insertions, 149 deletions
diff --git a/vm/alloc/Alloc.c b/vm/alloc/Alloc.c index a95e63f18..f77a232cd 100644 --- a/vm/alloc/Alloc.c +++ b/vm/alloc/Alloc.c @@ -171,15 +171,11 @@ Object* dvmAllocObject(ClassObject* clazz, int flags) assert(dvmIsClassInitialized(clazz) || dvmIsClassInitializing(clazz)); - if (IS_CLASS_FLAG_SET(clazz, CLASS_ISFINALIZABLE)) { - flags |= ALLOC_FINALIZABLE; - } - /* allocate on GC heap; memory is zeroed out */ newObj = (Object*)dvmMalloc(clazz->objectSize, flags); if (newObj != NULL) { DVM_OBJECT_INIT(newObj, clazz); - dvmTrackAllocation(clazz, clazz->objectSize); + dvmTrackAllocation(clazz, clazz->objectSize); /* notify DDMS */ } return newObj; @@ -193,37 +189,48 @@ Object* dvmAllocObject(ClassObject* clazz, int flags) */ Object* dvmCloneObject(Object* obj) { + ClassObject* clazz; Object* copy; - int size; - int flags; + size_t size; assert(dvmIsValidObject(obj)); + clazz = obj->clazz; /* Class.java shouldn't let us get here (java.lang.Class is final * and does not implement Clonable), but make extra sure. * A memcpy() clone will wreak havoc on a ClassObject's "innards". */ - assert(obj->clazz != gDvm.classJavaLangClass); - - if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISFINALIZABLE)) - flags = ALLOC_DEFAULT | ALLOC_FINALIZABLE; - else - flags = ALLOC_DEFAULT; + assert(clazz != gDvm.classJavaLangClass); - if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISARRAY)) { + if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) { size = dvmArrayObjectSize((ArrayObject *)obj); } else { - size = obj->clazz->objectSize; + size = clazz->objectSize; } - copy = (Object*)dvmMalloc(size, flags); + copy = (Object*)dvmMalloc(size, ALLOC_DEFAULT); if (copy == NULL) return NULL; + /* We assume that memcpy will copy obj by words. */ memcpy(copy, obj, size); DVM_LOCK_INIT(©->lock); dvmWriteBarrierObject(copy); + /* + * Mark the clone as finalizable if appropriate. + * + * TODO: this is wrong if the source object hasn't finished construction + * through Object.<init>. We need to make "copy" finalizable iff + * "obj" is finalizable, i.e. it's present in gcHeap->finalizableRefs. + * Currently no quick way to do that. See also b/2645458. + */ + if (IS_CLASS_FLAG_SET(clazz, CLASS_ISFINALIZABLE)) { + dvmSetFinalizable(copy); + } + + dvmTrackAllocation(clazz, size); /* notify DDMS */ + return copy; } diff --git a/vm/alloc/Alloc.h b/vm/alloc/Alloc.h index e1b3e3123..9747fc06c 100644 --- a/vm/alloc/Alloc.h +++ b/vm/alloc/Alloc.h @@ -59,7 +59,6 @@ Object* dvmAllocObject(ClassObject* clazz, int flags); enum { ALLOC_DEFAULT = 0x00, ALLOC_DONT_TRACK = 0x01, /* don't add to internal tracking list */ - ALLOC_FINALIZABLE = 0x02, /* call finalize() before freeing */ }; /* @@ -93,6 +92,11 @@ bool dvmIsValidObject(const Object* obj); Object* dvmCloneObject(Object* obj); /* + * Make the object finalizable. + */ +void dvmSetFinalizable(Object* obj); + +/* * Determine the exact number of GC heap bytes used by an object. (Internal * to heap code except for debugging.) */ diff --git a/vm/alloc/Heap.c b/vm/alloc/Heap.c index dcc6e425b..7eb3ff269 100644 --- a/vm/alloc/Heap.c +++ b/vm/alloc/Heap.c @@ -416,7 +416,6 @@ static void throwOOME() */ void* dvmMalloc(size_t size, int flags) { - GcHeap *gcHeap = gDvm.gcHeap; void *ptr; dvmLockHeap(); @@ -427,19 +426,6 @@ void* dvmMalloc(size_t size, int flags) if (ptr != NULL) { /* We've got the memory. */ - if ((flags & ALLOC_FINALIZABLE) != 0) { - /* This object is an instance of a class that - * overrides finalize(). Add it to the finalizable list. - */ - if (!dvmHeapAddRefToLargeTable(&gcHeap->finalizableRefs, - (Object *)ptr)) - { - LOGE_HEAP("dvmMalloc(): no room for any more " - "finalizable objects\n"); - dvmAbort(); - } - } - if (gDvm.allocProf.enabled) { Thread* self = dvmThreadSelf(); gDvm.allocProf.allocCount++; diff --git a/vm/alloc/MarkSweep.c b/vm/alloc/MarkSweep.c index 9ef3da759..1fb3f248d 100644 --- a/vm/alloc/MarkSweep.c +++ b/vm/alloc/MarkSweep.c @@ -880,6 +880,24 @@ static void scheduleFinalizations(void) } /* + * This object is an instance of a class that overrides finalize(). Mark + * it as finalizable. + * + * This is called when Object.<init> completes normally. It's also + * called for clones of finalizable objects. + */ +void dvmSetFinalizable(Object* obj) +{ + dvmLockHeap(); + GcHeap* gcHeap = gDvm.gcHeap; + if (!dvmHeapAddRefToLargeTable(&gcHeap->finalizableRefs, obj)) { + LOGE_HEAP("No room for any more finalizable objects"); + dvmAbort(); + } + dvmUnlockHeap(); +} + +/* * Process reference class instances and schedule finalizations. */ void dvmHeapProcessReferences(Object **softReferences, bool clearSoftRefs, diff --git a/vm/analysis/Optimize.c b/vm/analysis/Optimize.c index d7704a487..c50230304 100644 --- a/vm/analysis/Optimize.c +++ b/vm/analysis/Optimize.c @@ -41,7 +41,7 @@ static bool rewriteInstField(Method* method, u2* insns, Opcode quickOpc, Opcode volatileOpc); static bool rewriteStaticField(Method* method, u2* insns, Opcode volatileOpc); static bool rewriteVirtualInvoke(Method* method, u2* insns, Opcode newOpc); -static bool rewriteEmptyDirectInvoke(Method* method, u2* insns); +static bool rewriteInvokeObjectInit(Method* method, u2* insns); static bool rewriteExecuteInline(Method* method, u2* insns, MethodType methodType); static bool rewriteExecuteInlineRange(Method* method, u2* insns, @@ -251,7 +251,7 @@ rewrite_static_field: break; case OP_INVOKE_DIRECT: if (!rewriteExecuteInline(method, insns, METHOD_DIRECT)) { - rewriteEmptyDirectInvoke(method, insns); + rewriteInvokeObjectInit(method, insns); } break; case OP_INVOKE_DIRECT_RANGE: @@ -904,16 +904,18 @@ static bool rewriteVirtualInvoke(Method* method, u2* insns, Opcode newOpc) } /* - * Rewrite invoke-direct, which has the form: + * Rewrite invoke-direct of Object.<init>, which has the form: * op vAA, meth@BBBB, reg stuff @CCCC * - * There isn't a lot we can do to make this faster, but in some situations - * we can make it go away entirely. + * This is useful as an optimization, because otherwise every object + * instantiation will cause us to call a method that does nothing. + * It also allows us to inexpensively mark objects as finalizable at the + * correct time. * - * This must only be used when the invoked method does nothing and has - * no return value (the latter being very important for verification). + * TODO: verifier should ensure Object.<init> contains only return-void, + * and issue a warning if not. */ -static bool rewriteEmptyDirectInvoke(Method* method, u2* insns) +static bool rewriteInvokeObjectInit(Method* method, u2* insns) { ClassObject* clazz = method->clazz; Method* calledMethod; @@ -922,27 +924,24 @@ static bool rewriteEmptyDirectInvoke(Method* method, u2* insns) calledMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_DIRECT, NULL); if (calledMethod == NULL) { LOGD("DexOpt: unable to opt direct call 0x%04x at 0x%02x in %s.%s\n", - methodIdx, - (int) (insns - method->insns), clazz->descriptor, - method->name); + methodIdx, (int) (insns - method->insns), + clazz->descriptor, method->name); return false; } - /* TODO: verify that java.lang.Object() is actually empty! */ if (calledMethod->clazz == gDvm.classJavaLangObject && dvmCompareNameDescriptorAndMethod("<init>", "()V", calledMethod) == 0) { /* - * Replace with "empty" instruction. DO NOT disturb anything - * else about it, as we want it to function the same as - * OP_INVOKE_DIRECT when debugging is enabled. + * Replace the instruction. We want to modify as little as possible + * because, if the debugger is attached, the interpreter will + * forward execution to the invoke-direct handler. */ assert((insns[0] & 0xff) == OP_INVOKE_DIRECT); updateOpcode(method, insns, OP_INVOKE_OBJECT_INIT); - //LOGI("DexOpt: marked-empty call to %s.%s --> %s.%s\n", - // method->clazz->descriptor, method->name, - // calledMethod->clazz->descriptor, calledMethod->name); + LOGVV("DexOpt: replaced Object.<init> in %s.%s\n", + method->clazz->descriptor, method->name); } return true; diff --git a/vm/compiler/codegen/arm/CodegenDriver.c b/vm/compiler/codegen/arm/CodegenDriver.c index ade419772..2ed17d629 100644 --- a/vm/compiler/codegen/arm/CodegenDriver.c +++ b/vm/compiler/codegen/arm/CodegenDriver.c @@ -3271,10 +3271,8 @@ static bool handleFmt35c_3rc_5rc(CompilationUnit *cUnit, MIR *mir, genTrap(cUnit, mir->offset, pcrLabel); break; } - /* NOP */ case OP_INVOKE_OBJECT_INIT: { - if (gDvmJit.methodTraceSupport) - genInterpSingleStep(cUnit, mir); + genInterpSingleStep(cUnit, mir); break; } case OP_FILLED_NEW_ARRAY: diff --git a/vm/mterp/armv5te/OP_INVOKE_OBJECT_INIT.S b/vm/mterp/armv5te/OP_INVOKE_OBJECT_INIT.S index b75e1244b..2ae5f5b79 100644 --- a/vm/mterp/armv5te/OP_INVOKE_OBJECT_INIT.S +++ b/vm/mterp/armv5te/OP_INVOKE_OBJECT_INIT.S @@ -1,7 +1,20 @@ %verify "executed" +%verify "finalizable class" /* - * invoke-object-init is a no-op in a "standard" interpreter. + * Invoke Object.<init> on an object. In practice we know that + * Object's nullary constructor doesn't do anything, so we just + * skip it (we know a debugger isn't active). */ - FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST + FETCH(r0, 2) @ r0<- GFED + and r1, r0, #15 @ r1<- D + GET_VREG(r0, r1) @ r0<- "this" ptr + cmp r0, #0 @ check for NULL + beq common_errNullObject @ export PC and throw NPE + ldr r1, [r0, #offObject_clazz] @ r1<- obj->clazz + ldr r2, [r1, #offClassObject_accessFlags] @ r2<- clazz->accessFlags + tst r2, #CLASS_ISFINALIZABLE @ is this class finalizable? + beq 1f @ nope, done + bl dvmSetFinalizable @ call dvmSetFinalizable(obj) +1: FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST GET_INST_OPCODE(ip) @ ip<- opcode from rINST GOTO_OPCODE(ip) @ execute it diff --git a/vm/mterp/c/OP_INVOKE_OBJECT_INIT.c b/vm/mterp/c/OP_INVOKE_OBJECT_INIT.c index cc65e3f56..1da8b0635 100644 --- a/vm/mterp/c/OP_INVOKE_OBJECT_INIT.c +++ b/vm/mterp/c/OP_INVOKE_OBJECT_INIT.c @@ -1,15 +1,33 @@ HANDLE_OPCODE(OP_INVOKE_OBJECT_INIT /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/) -#if INTERP_TYPE != INTERP_DBG - //LOGI("Ignoring empty\n"); - FINISH(3); + { + Object* obj; + + vsrc1 = FETCH(2) & 0x0f; /* reg number of "this" pointer */ + obj = GET_REGISTER_AS_OBJECT(vsrc1); + + if (!checkForNullExportPC(obj, fp, pc)) + GOTO_exceptionThrown(); + + /* + * The object should be marked "finalizable" when Object.<init> + * completes normally. We're going to assume it does complete + * (by virtue of being nothing but a return-void) and set it now. + */ + if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISFINALIZABLE)) { + dvmSetFinalizable(obj); + } + +#if INTERP_TYPE == INTERP_DBG + if (!DEBUGGER_ACTIVE) { + /* skip method invocation */ + FINISH(3); + } else { + /* behave like OP_INVOKE_DIRECT */ + GOTO_invoke(invokeDirect, false, false); + } #else - if (!DEBUGGER_ACTIVE) { - //LOGI("Skipping empty\n"); - FINISH(3); // don't want it to show up in profiler output - } else { - //LOGI("Running empty\n"); - /* fall through to OP_INVOKE_DIRECT */ - GOTO_invoke(invokeDirect, false, false); - } + /* debugger can't be attached, skip method invocation */ + FINISH(3); #endif + } OP_END diff --git a/vm/mterp/common/asm-constants.h b/vm/mterp/common/asm-constants.h index e4070ec4f..df72f08e2 100644 --- a/vm/mterp/common/asm-constants.h +++ b/vm/mterp/common/asm-constants.h @@ -318,6 +318,7 @@ MTERP_CONSTANT(ACC_STATIC, 0x0008) MTERP_CONSTANT(ACC_NATIVE, 0x0100) MTERP_CONSTANT(ACC_INTERFACE, 0x0200) MTERP_CONSTANT(ACC_ABSTRACT, 0x0400) +MTERP_CONSTANT(CLASS_ISFINALIZABLE, 1<<31) /* flags for dvmMalloc */ MTERP_CONSTANT(ALLOC_DONT_TRACK, 0x01) diff --git a/vm/mterp/config-armv7-a b/vm/mterp/config-armv7-a index e66640c65..e0ccdd33a 100644 --- a/vm/mterp/config-armv7-a +++ b/vm/mterp/config-armv7-a @@ -156,6 +156,8 @@ op-end # "helper" code for C; include if you use any of the C stubs (this generates # object code, so it's normally excluded) +# +# Add this if you see linker failures for stuff like "dvmMterp_exceptionThrown". ##import c/gotoTargets.c # end of defs; include this when cstubs/stubdefs.c is included diff --git a/vm/mterp/config-x86 b/vm/mterp/config-x86 index f13719860..4d67c5102 100644 --- a/vm/mterp/config-x86 +++ b/vm/mterp/config-x86 @@ -40,6 +40,7 @@ op-start x86 op OP_SGET_WIDE_VOLATILE c op OP_SPUT_WIDE_VOLATILE c op OP_RETURN_VOID_BARRIER c + op OP_INVOKE_OBJECT_INIT c op-end # arch-specific entry point to interpreter diff --git a/vm/mterp/config-x86-atom b/vm/mterp/config-x86-atom index e1e88669e..11e667c80 100644 --- a/vm/mterp/config-x86-atom +++ b/vm/mterp/config-x86-atom @@ -298,6 +298,7 @@ op OP_IPUT_WIDE_VOLATILE c op OP_SGET_WIDE_VOLATILE c op OP_SPUT_WIDE_VOLATILE c op OP_RETURN_VOID_BARRIER c +op OP_INVOKE_OBJECT_INIT c op-end # arch-specific entry point to interpreter diff --git a/vm/mterp/cstubs/stubdefs.c b/vm/mterp/cstubs/stubdefs.c index d9d6f3e82..ea061ea6d 100644 --- a/vm/mterp/cstubs/stubdefs.c +++ b/vm/mterp/cstubs/stubdefs.c @@ -86,9 +86,9 @@ return; \ } while(false) -#define GOTO_invoke(_target, _methodCallRange) \ +#define GOTO_invoke(_target, _methodCallRange, _jumboFormat) \ do { \ - dvmMterp_##_target(glue, _methodCallRange); \ + dvmMterp_##_target(glue, _methodCallRange, _jumboFormat); \ return; \ } while(false) diff --git a/vm/mterp/out/InterpAsm-armv5te-vfp.S b/vm/mterp/out/InterpAsm-armv5te-vfp.S index 9537885c6..1d2abb3c5 100644 --- a/vm/mterp/out/InterpAsm-armv5te-vfp.S +++ b/vm/mterp/out/InterpAsm-armv5te-vfp.S @@ -7412,9 +7412,21 @@ dalvik_inst: .L_OP_INVOKE_OBJECT_INIT: /* 0xf0 */ /* File: armv5te/OP_INVOKE_OBJECT_INIT.S */ /* - * invoke-object-init is a no-op in a "standard" interpreter. - */ - FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST + * Invoke Object.<init> on an object. In practice we know that + * Object's nullary constructor doesn't do anything, so we just + * skip it (we know a debugger isn't active). + */ + FETCH(r0, 2) @ r0<- GFED + and r1, r0, #15 @ r1<- D + GET_VREG(r0, r1) @ r0<- "this" ptr + cmp r0, #0 @ check for NULL + beq common_errNullObject @ export PC and throw NPE + ldr r1, [r0, #offObject_clazz] @ r1<- obj->clazz + ldr r2, [r1, #offClassObject_accessFlags] @ r2<- clazz->accessFlags + tst r2, #CLASS_ISFINALIZABLE @ is this class finalizable? + beq 1f @ nope, done + bl dvmSetFinalizable @ call dvmSetFinalizable(obj) +1: FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST GET_INST_OPCODE(ip) @ ip<- opcode from rINST GOTO_OPCODE(ip) @ execute it diff --git a/vm/mterp/out/InterpAsm-armv5te.S b/vm/mterp/out/InterpAsm-armv5te.S index fb0d1e90c..62cdd2e79 100644 --- a/vm/mterp/out/InterpAsm-armv5te.S +++ b/vm/mterp/out/InterpAsm-armv5te.S @@ -7734,9 +7734,21 @@ d2i_doconv: .L_OP_INVOKE_OBJECT_INIT: /* 0xf0 */ /* File: armv5te/OP_INVOKE_OBJECT_INIT.S */ /* - * invoke-object-init is a no-op in a "standard" interpreter. - */ - FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST + * Invoke Object.<init> on an object. In practice we know that + * Object's nullary constructor doesn't do anything, so we just + * skip it (we know a debugger isn't active). + */ + FETCH(r0, 2) @ r0<- GFED + and r1, r0, #15 @ r1<- D + GET_VREG(r0, r1) @ r0<- "this" ptr + cmp r0, #0 @ check for NULL + beq common_errNullObject @ export PC and throw NPE + ldr r1, [r0, #offObject_clazz] @ r1<- obj->clazz + ldr r2, [r1, #offClassObject_accessFlags] @ r2<- clazz->accessFlags + tst r2, #CLASS_ISFINALIZABLE @ is this class finalizable? + beq 1f @ nope, done + bl dvmSetFinalizable @ call dvmSetFinalizable(obj) +1: FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST GET_INST_OPCODE(ip) @ ip<- opcode from rINST GOTO_OPCODE(ip) @ execute it diff --git a/vm/mterp/out/InterpAsm-armv7-a-neon.S b/vm/mterp/out/InterpAsm-armv7-a-neon.S index dc317d37f..3ae8c21c9 100644 --- a/vm/mterp/out/InterpAsm-armv7-a-neon.S +++ b/vm/mterp/out/InterpAsm-armv7-a-neon.S @@ -7370,9 +7370,21 @@ dalvik_inst: .L_OP_INVOKE_OBJECT_INIT: /* 0xf0 */ /* File: armv5te/OP_INVOKE_OBJECT_INIT.S */ /* - * invoke-object-init is a no-op in a "standard" interpreter. - */ - FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST + * Invoke Object.<init> on an object. In practice we know that + * Object's nullary constructor doesn't do anything, so we just + * skip it (we know a debugger isn't active). + */ + FETCH(r0, 2) @ r0<- GFED + and r1, r0, #15 @ r1<- D + GET_VREG(r0, r1) @ r0<- "this" ptr + cmp r0, #0 @ check for NULL + beq common_errNullObject @ export PC and throw NPE + ldr r1, [r0, #offObject_clazz] @ r1<- obj->clazz + ldr r2, [r1, #offClassObject_accessFlags] @ r2<- clazz->accessFlags + tst r2, #CLASS_ISFINALIZABLE @ is this class finalizable? + beq 1f @ nope, done + bl dvmSetFinalizable @ call dvmSetFinalizable(obj) +1: FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST GET_INST_OPCODE(ip) @ ip<- opcode from rINST GOTO_OPCODE(ip) @ execute it diff --git a/vm/mterp/out/InterpAsm-armv7-a.S b/vm/mterp/out/InterpAsm-armv7-a.S index babba04b5..f276cd5fb 100644 --- a/vm/mterp/out/InterpAsm-armv7-a.S +++ b/vm/mterp/out/InterpAsm-armv7-a.S @@ -7370,9 +7370,21 @@ dalvik_inst: .L_OP_INVOKE_OBJECT_INIT: /* 0xf0 */ /* File: armv5te/OP_INVOKE_OBJECT_INIT.S */ /* - * invoke-object-init is a no-op in a "standard" interpreter. - */ - FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST + * Invoke Object.<init> on an object. In practice we know that + * Object's nullary constructor doesn't do anything, so we just + * skip it (we know a debugger isn't active). + */ + FETCH(r0, 2) @ r0<- GFED + and r1, r0, #15 @ r1<- D + GET_VREG(r0, r1) @ r0<- "this" ptr + cmp r0, #0 @ check for NULL + beq common_errNullObject @ export PC and throw NPE + ldr r1, [r0, #offObject_clazz] @ r1<- obj->clazz + ldr r2, [r1, #offClassObject_accessFlags] @ r2<- clazz->accessFlags + tst r2, #CLASS_ISFINALIZABLE @ is this class finalizable? + beq 1f @ nope, done + bl dvmSetFinalizable @ call dvmSetFinalizable(obj) +1: FETCH_ADVANCE_INST(3) @ advance to next instr, load rINST GET_INST_OPCODE(ip) @ ip<- opcode from rINST GOTO_OPCODE(ip) @ execute it diff --git a/vm/mterp/out/InterpAsm-x86-atom.S b/vm/mterp/out/InterpAsm-x86-atom.S index 50cc7b378..c53fe6cff 100644 --- a/vm/mterp/out/InterpAsm-x86-atom.S +++ b/vm/mterp/out/InterpAsm-x86-atom.S @@ -14664,7 +14664,6 @@ OP_IF_LEZ_2f: /* ------------------------------ */ .balign 64 .L_OP_INVOKE_OBJECT_INIT: /* 0xf0 */ -/* File: x86-atom/OP_INVOKE_OBJECT_INIT.S */ /* Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -14681,17 +14680,15 @@ OP_IF_LEZ_2f: */ /* - * File: OP_INVOKE_OBJECT_INIT.S - * - * Code: Used as a no-op. Uses no substitutions. - * - * For: invoke-object-init - * - * Format: B|A|op CCCC G|F|E|D (35c) + * File: stub.S */ - FINISH 3 - + SAVE_PC_FP_TO_GLUE %edx # save program counter and frame pointer + pushl rGLUE # push parameter glue + call dvmMterp_OP_INVOKE_OBJECT_INIT # call c-based implementation + lea 4(%esp), %esp + LOAD_PC_FP_FROM_GLUE # restore program counter and frame pointer + FINISH_A # jump to next instruction /* ------------------------------ */ .balign 64 .L_OP_RETURN_VOID_BARRIER: /* 0xf1 */ diff --git a/vm/mterp/out/InterpAsm-x86.S b/vm/mterp/out/InterpAsm-x86.S index 74ec78d75..6e5290f26 100644 --- a/vm/mterp/out/InterpAsm-x86.S +++ b/vm/mterp/out/InterpAsm-x86.S @@ -6125,14 +6125,14 @@ dvmAsmInstructionStart = .L_OP_NOP /* ------------------------------ */ .balign 64 .L_OP_INVOKE_OBJECT_INIT: /* 0xf0 */ -/* File: x86/OP_INVOKE_OBJECT_INIT.S */ - /* - * invoke-object-init is a no-op in a "standard" interpreter. - */ - FETCH_INST_WORD 3 - ADVANCE_PC 3 + /* (stub) */ + SAVE_PC_FP_TO_GLUE %ecx # leaves rGLUE in %ecx + movl %ecx,OUT_ARG0(%esp) # glue is first arg to function + call dvmMterp_OP_INVOKE_OBJECT_INIT # do the real work + mov rGLUE,%ecx + LOAD_PC_FP_FROM_GLUE # retrieve updated values + FETCH_INST GOTO_NEXT - /* ------------------------------ */ .balign 64 .L_OP_RETURN_VOID_BARRIER: /* 0xf1 */ diff --git a/vm/mterp/out/InterpC-allstubs.c b/vm/mterp/out/InterpC-allstubs.c index 03bb746e7..eda4f6993 100644 --- a/vm/mterp/out/InterpC-allstubs.c +++ b/vm/mterp/out/InterpC-allstubs.c @@ -499,9 +499,9 @@ static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc) return; \ } while(false) -#define GOTO_invoke(_target, _methodCallRange) \ +#define GOTO_invoke(_target, _methodCallRange, _jumboFormat) \ do { \ - dvmMterp_##_target(glue, _methodCallRange); \ + dvmMterp_##_target(glue, _methodCallRange, _jumboFormat); \ return; \ } while(false) @@ -3054,19 +3054,37 @@ OP_END /* File: c/OP_INVOKE_OBJECT_INIT.c */ HANDLE_OPCODE(OP_INVOKE_OBJECT_INIT /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/) -#if INTERP_TYPE != INTERP_DBG - //LOGI("Ignoring empty\n"); - FINISH(3); + { + Object* obj; + + vsrc1 = FETCH(2) & 0x0f; /* reg number of "this" pointer */ + obj = GET_REGISTER_AS_OBJECT(vsrc1); + + if (!checkForNullExportPC(obj, fp, pc)) + GOTO_exceptionThrown(); + + /* + * The object should be marked "finalizable" when Object.<init> + * completes normally. We're going to assume it does complete + * (by virtue of being nothing but a return-void) and set it now. + */ + if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISFINALIZABLE)) { + dvmSetFinalizable(obj); + } + +#if INTERP_TYPE == INTERP_DBG + if (!DEBUGGER_ACTIVE) { + /* skip method invocation */ + FINISH(3); + } else { + /* behave like OP_INVOKE_DIRECT */ + GOTO_invoke(invokeDirect, false, false); + } #else - if (!DEBUGGER_ACTIVE) { - //LOGI("Skipping empty\n"); - FINISH(3); // don't want it to show up in profiler output - } else { - //LOGI("Running empty\n"); - /* fall through to OP_INVOKE_DIRECT */ - GOTO_invoke(invokeDirect, false, false); - } + /* debugger can't be attached, skip method invocation */ + FINISH(3); #endif + } OP_END /* File: c/OP_RETURN_VOID_BARRIER.c */ diff --git a/vm/mterp/out/InterpC-armv5te-vfp.c b/vm/mterp/out/InterpC-armv5te-vfp.c index 0643dec51..8fc5605b9 100644 --- a/vm/mterp/out/InterpC-armv5te-vfp.c +++ b/vm/mterp/out/InterpC-armv5te-vfp.c @@ -499,9 +499,9 @@ static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc) return; \ } while(false) -#define GOTO_invoke(_target, _methodCallRange) \ +#define GOTO_invoke(_target, _methodCallRange, _jumboFormat) \ do { \ - dvmMterp_##_target(glue, _methodCallRange); \ + dvmMterp_##_target(glue, _methodCallRange, _jumboFormat); \ return; \ } while(false) diff --git a/vm/mterp/out/InterpC-armv5te.c b/vm/mterp/out/InterpC-armv5te.c index a64764318..261891ee4 100644 --- a/vm/mterp/out/InterpC-armv5te.c +++ b/vm/mterp/out/InterpC-armv5te.c @@ -499,9 +499,9 @@ static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc) return; \ } while(false) -#define GOTO_invoke(_target, _methodCallRange) \ +#define GOTO_invoke(_target, _methodCallRange, _jumboFormat) \ do { \ - dvmMterp_##_target(glue, _methodCallRange); \ + dvmMterp_##_target(glue, _methodCallRange, _jumboFormat); \ return; \ } while(false) diff --git a/vm/mterp/out/InterpC-armv7-a-neon.c b/vm/mterp/out/InterpC-armv7-a-neon.c index 0bc7f4e68..28603ef0f 100644 --- a/vm/mterp/out/InterpC-armv7-a-neon.c +++ b/vm/mterp/out/InterpC-armv7-a-neon.c @@ -499,9 +499,9 @@ static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc) return; \ } while(false) -#define GOTO_invoke(_target, _methodCallRange) \ +#define GOTO_invoke(_target, _methodCallRange, _jumboFormat) \ do { \ - dvmMterp_##_target(glue, _methodCallRange); \ + dvmMterp_##_target(glue, _methodCallRange, _jumboFormat); \ return; \ } while(false) diff --git a/vm/mterp/out/InterpC-armv7-a.c b/vm/mterp/out/InterpC-armv7-a.c index d771fa631..0d0c8c468 100644 --- a/vm/mterp/out/InterpC-armv7-a.c +++ b/vm/mterp/out/InterpC-armv7-a.c @@ -499,9 +499,9 @@ static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc) return; \ } while(false) -#define GOTO_invoke(_target, _methodCallRange) \ +#define GOTO_invoke(_target, _methodCallRange, _jumboFormat) \ do { \ - dvmMterp_##_target(glue, _methodCallRange); \ + dvmMterp_##_target(glue, _methodCallRange, _jumboFormat); \ return; \ } while(false) diff --git a/vm/mterp/out/InterpC-portdbg.c b/vm/mterp/out/InterpC-portdbg.c index 8497502ab..9cd97eba0 100644 --- a/vm/mterp/out/InterpC-portdbg.c +++ b/vm/mterp/out/InterpC-portdbg.c @@ -3416,19 +3416,37 @@ OP_END /* File: c/OP_INVOKE_OBJECT_INIT.c */ HANDLE_OPCODE(OP_INVOKE_OBJECT_INIT /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/) -#if INTERP_TYPE != INTERP_DBG - //LOGI("Ignoring empty\n"); - FINISH(3); + { + Object* obj; + + vsrc1 = FETCH(2) & 0x0f; /* reg number of "this" pointer */ + obj = GET_REGISTER_AS_OBJECT(vsrc1); + + if (!checkForNullExportPC(obj, fp, pc)) + GOTO_exceptionThrown(); + + /* + * The object should be marked "finalizable" when Object.<init> + * completes normally. We're going to assume it does complete + * (by virtue of being nothing but a return-void) and set it now. + */ + if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISFINALIZABLE)) { + dvmSetFinalizable(obj); + } + +#if INTERP_TYPE == INTERP_DBG + if (!DEBUGGER_ACTIVE) { + /* skip method invocation */ + FINISH(3); + } else { + /* behave like OP_INVOKE_DIRECT */ + GOTO_invoke(invokeDirect, false, false); + } #else - if (!DEBUGGER_ACTIVE) { - //LOGI("Skipping empty\n"); - FINISH(3); // don't want it to show up in profiler output - } else { - //LOGI("Running empty\n"); - /* fall through to OP_INVOKE_DIRECT */ - GOTO_invoke(invokeDirect, false, false); - } + /* debugger can't be attached, skip method invocation */ + FINISH(3); #endif + } OP_END /* File: c/OP_RETURN_VOID_BARRIER.c */ diff --git a/vm/mterp/out/InterpC-portstd.c b/vm/mterp/out/InterpC-portstd.c index 08d06b8dc..43bcb441f 100644 --- a/vm/mterp/out/InterpC-portstd.c +++ b/vm/mterp/out/InterpC-portstd.c @@ -3166,19 +3166,37 @@ OP_END /* File: c/OP_INVOKE_OBJECT_INIT.c */ HANDLE_OPCODE(OP_INVOKE_OBJECT_INIT /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/) -#if INTERP_TYPE != INTERP_DBG - //LOGI("Ignoring empty\n"); - FINISH(3); + { + Object* obj; + + vsrc1 = FETCH(2) & 0x0f; /* reg number of "this" pointer */ + obj = GET_REGISTER_AS_OBJECT(vsrc1); + + if (!checkForNullExportPC(obj, fp, pc)) + GOTO_exceptionThrown(); + + /* + * The object should be marked "finalizable" when Object.<init> + * completes normally. We're going to assume it does complete + * (by virtue of being nothing but a return-void) and set it now. + */ + if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISFINALIZABLE)) { + dvmSetFinalizable(obj); + } + +#if INTERP_TYPE == INTERP_DBG + if (!DEBUGGER_ACTIVE) { + /* skip method invocation */ + FINISH(3); + } else { + /* behave like OP_INVOKE_DIRECT */ + GOTO_invoke(invokeDirect, false, false); + } #else - if (!DEBUGGER_ACTIVE) { - //LOGI("Skipping empty\n"); - FINISH(3); // don't want it to show up in profiler output - } else { - //LOGI("Running empty\n"); - /* fall through to OP_INVOKE_DIRECT */ - GOTO_invoke(invokeDirect, false, false); - } + /* debugger can't be attached, skip method invocation */ + FINISH(3); #endif + } OP_END /* File: c/OP_RETURN_VOID_BARRIER.c */ diff --git a/vm/mterp/out/InterpC-x86-atom.c b/vm/mterp/out/InterpC-x86-atom.c index 98d5c27d3..f98969a61 100644 --- a/vm/mterp/out/InterpC-x86-atom.c +++ b/vm/mterp/out/InterpC-x86-atom.c @@ -499,9 +499,9 @@ static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc) return; \ } while(false) -#define GOTO_invoke(_target, _methodCallRange) \ +#define GOTO_invoke(_target, _methodCallRange, _jumboFormat) \ do { \ - dvmMterp_##_target(glue, _methodCallRange); \ + dvmMterp_##_target(glue, _methodCallRange, _jumboFormat); \ return; \ } while(false) @@ -1335,6 +1335,41 @@ OP_END HANDLE_SPUT_X(OP_SPUT_WIDE_VOLATILE, "-wide-volatile", LongVolatile, _WIDE) OP_END +/* File: c/OP_INVOKE_OBJECT_INIT.c */ +HANDLE_OPCODE(OP_INVOKE_OBJECT_INIT /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/) + { + Object* obj; + + vsrc1 = FETCH(2) & 0x0f; /* reg number of "this" pointer */ + obj = GET_REGISTER_AS_OBJECT(vsrc1); + + if (!checkForNullExportPC(obj, fp, pc)) + GOTO_exceptionThrown(); + + /* + * The object should be marked "finalizable" when Object.<init> + * completes normally. We're going to assume it does complete + * (by virtue of being nothing but a return-void) and set it now. + */ + if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISFINALIZABLE)) { + dvmSetFinalizable(obj); + } + +#if INTERP_TYPE == INTERP_DBG + if (!DEBUGGER_ACTIVE) { + /* skip method invocation */ + FINISH(3); + } else { + /* behave like OP_INVOKE_DIRECT */ + GOTO_invoke(invokeDirect, false, false); + } +#else + /* debugger can't be attached, skip method invocation */ + FINISH(3); +#endif + } +OP_END + /* File: c/OP_RETURN_VOID_BARRIER.c */ HANDLE_OPCODE(OP_RETURN_VOID_BARRIER /**/) ILOGV("|return-void"); diff --git a/vm/mterp/out/InterpC-x86.c b/vm/mterp/out/InterpC-x86.c index 8d4720969..7445ad4f3 100644 --- a/vm/mterp/out/InterpC-x86.c +++ b/vm/mterp/out/InterpC-x86.c @@ -499,9 +499,9 @@ static inline bool checkForNullExportPC(Object* obj, u4* fp, const u2* pc) return; \ } while(false) -#define GOTO_invoke(_target, _methodCallRange) \ +#define GOTO_invoke(_target, _methodCallRange, _jumboFormat) \ do { \ - dvmMterp_##_target(glue, _methodCallRange); \ + dvmMterp_##_target(glue, _methodCallRange, _jumboFormat); \ return; \ } while(false) @@ -1360,6 +1360,41 @@ HANDLE_OPCODE(OP_EXECUTE_INLINE_RANGE /*{vCCCC..v(CCCC+AA-1)}, inline@BBBB*/) FINISH(3); OP_END +/* File: c/OP_INVOKE_OBJECT_INIT.c */ +HANDLE_OPCODE(OP_INVOKE_OBJECT_INIT /*vB, {vD, vE, vF, vG, vA}, meth@CCCC*/) + { + Object* obj; + + vsrc1 = FETCH(2) & 0x0f; /* reg number of "this" pointer */ + obj = GET_REGISTER_AS_OBJECT(vsrc1); + + if (!checkForNullExportPC(obj, fp, pc)) + GOTO_exceptionThrown(); + + /* + * The object should be marked "finalizable" when Object.<init> + * completes normally. We're going to assume it does complete + * (by virtue of being nothing but a return-void) and set it now. + */ + if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISFINALIZABLE)) { + dvmSetFinalizable(obj); + } + +#if INTERP_TYPE == INTERP_DBG + if (!DEBUGGER_ACTIVE) { + /* skip method invocation */ + FINISH(3); + } else { + /* behave like OP_INVOKE_DIRECT */ + GOTO_invoke(invokeDirect, false, false); + } +#else + /* debugger can't be attached, skip method invocation */ + FINISH(3); +#endif + } +OP_END + /* File: c/OP_RETURN_VOID_BARRIER.c */ HANDLE_OPCODE(OP_RETURN_VOID_BARRIER /**/) ILOGV("|return-void"); diff --git a/vm/mterp/x86-atom/OP_INVOKE_OBJECT_INIT.S b/vm/mterp/x86-atom/OP_INVOKE_OBJECT_INIT.S index 16ae56eb5..faf1565bc 100644 --- a/vm/mterp/x86-atom/OP_INVOKE_OBJECT_INIT.S +++ b/vm/mterp/x86-atom/OP_INVOKE_OBJECT_INIT.S @@ -16,11 +16,10 @@ /* * File: OP_INVOKE_OBJECT_INIT.S * - * Code: Used as a no-op. Uses no substitutions. + * Code: TODO * * For: invoke-object-init * * Format: B|A|op CCCC G|F|E|D (35c) */ - FINISH 3 diff --git a/vm/mterp/x86-atom/TODO.txt b/vm/mterp/x86-atom/TODO.txt index 7dc624eac..df094a6be 100644 --- a/vm/mterp/x86-atom/TODO.txt +++ b/vm/mterp/x86-atom/TODO.txt @@ -23,3 +23,4 @@ Items requiring attention: (lo) Implement OP_EXECUTE_INLINE_RANGE (lo) Implement OP_*_VOLATILE (12 instructions) (lo) Implement OP_RETURN_VOID_BARRIER +(lo) Implement OP_INVOKE_OBJECT_INIT diff --git a/vm/mterp/x86/OP_INVOKE_OBJECT_INIT.S b/vm/mterp/x86/OP_INVOKE_OBJECT_INIT.S index 640239261..fb84b32bb 100644 --- a/vm/mterp/x86/OP_INVOKE_OBJECT_INIT.S +++ b/vm/mterp/x86/OP_INVOKE_OBJECT_INIT.S @@ -1,7 +1,4 @@ %verify "executed" /* - * invoke-object-init is a no-op in a "standard" interpreter. + * TODO (currently punting to stub) */ - FETCH_INST_WORD 3 - ADVANCE_PC 3 - GOTO_NEXT |
