diff options
27 files changed, 396 insertions, 266 deletions
diff --git a/Android.mk b/Android.mk index 62d40bbeec..8e43879b52 100644 --- a/Android.mk +++ b/Android.mk @@ -92,6 +92,7 @@ include $(art_path)/dex2oat/Android.mk include $(art_path)/disassembler/Android.mk include $(art_path)/oatdump/Android.mk include $(art_path)/dalvikvm/Android.mk +include $(art_path)/tools/Android.mk include $(art_build_path)/Android.oat.mk # ART_HOST_DEPENDENCIES depends on Android.executable.mk above for ART_HOST_EXECUTABLES @@ -300,6 +301,12 @@ build-art-host: $(ART_HOST_EXECUTABLES) $(ART_HOST_TEST_EXECUTABLES) $(HOS .PHONY: build-art-target build-art-target: $(ART_TARGET_EXECUTABLES) $(ART_TARGET_TEST_EXECUTABLES) $(TARGET_CORE_IMG_OUT) $(TARGET_OUT)/lib/libjavacore.so +.PHONY: art-host +art-host: $(HOST_OUT_EXECUTABLES)/art $(HOST_OUT)/bin/dalvikvm $(HOST_OUT)/lib/libart.so $(HOST_OUT)/bin/dex2oat $(HOST_OUT_JAVA_LIBRARIES)/core.art $(HOST_OUT)/lib/libjavacore.so + +.PHONY: art-host-debug +art-host-debug: art-host $(HOST_OUT)/lib/libartd.so $(HOST_OUT)/bin/dex2oatd + ######################################################################## # oatdump targets diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc index fdc609a5f7..a30e80a575 100644 --- a/compiler/dex/quick/arm/call_arm.cc +++ b/compiler/dex/quick/arm/call_arm.cc @@ -24,70 +24,62 @@ namespace art { - -/* Return the position of an ssa name within the argument list */ -int ArmMir2Lir::InPosition(int s_reg) { - int v_reg = mir_graph_->SRegToVReg(s_reg); - return v_reg - cu_->num_regs; -} - -/* - * Describe an argument. If it's already in an arg register, just leave it - * there. NOTE: all live arg registers must be locked prior to this call - * to avoid having them allocated as a temp by downstream utilities. - */ -RegLocation ArmMir2Lir::ArgLoc(RegLocation loc) { - int arg_num = InPosition(loc.s_reg_low); - if (loc.wide) { - if (arg_num == 2) { - // Bad case - half in register, half in frame. Just punt - loc.location = kLocInvalid; - } else if (arg_num < 2) { - loc.low_reg = rARM_ARG1 + arg_num; - loc.high_reg = loc.low_reg + 1; - loc.location = kLocPhysReg; - } else { - loc.location = kLocDalvikFrame; - } - } else { - if (arg_num < 3) { - loc.low_reg = rARM_ARG1 + arg_num; - loc.location = kLocPhysReg; - } else { - loc.location = kLocDalvikFrame; - } +// TODO: generalize & move to RegUtil.cc +// The number of dalvik registers passed in core registers. +constexpr int kInArgsInCoreRegs = 3; +// The core register corresponding to the first (index 0) input argument. +constexpr int kInArg0CoreReg = r1; // r0 is Method*. +// Offset, in words, for getting args from stack (even core reg args have space on stack). +constexpr int kInArgToStackOffset = 1; + +/* Lock argument if it's in register. */ +void ArmMir2Lir::LockArg(int in_position, bool wide) { + if (in_position < kInArgsInCoreRegs) { + LockTemp(kInArg0CoreReg + in_position); + } + if (wide && in_position + 1 < kInArgsInCoreRegs) { + LockTemp(kInArg0CoreReg + in_position + 1); } - return loc; } -/* - * Load an argument. If already in a register, just return. If in - * the frame, we can't use the normal LoadValue() because it assumed - * a proper frame - and we're frameless. - */ -RegLocation ArmMir2Lir::LoadArg(RegLocation loc) { - if (loc.location == kLocDalvikFrame) { - int start = (InPosition(loc.s_reg_low) + 1) * sizeof(uint32_t); - loc.low_reg = AllocTemp(); - LoadWordDisp(rARM_SP, start, loc.low_reg); - if (loc.wide) { - loc.high_reg = AllocTemp(); - LoadWordDisp(rARM_SP, start + sizeof(uint32_t), loc.high_reg); +/* Load argument into register. LockArg(in_position, wide) must have been previously called. */ +int ArmMir2Lir::LoadArg(int in_position, bool wide) { + if (in_position < kInArgsInCoreRegs) { + int low_reg = kInArg0CoreReg + in_position; + if (!wide) { + return low_reg; } - loc.location = kLocPhysReg; + int high_reg = (in_position != kInArgsInCoreRegs - 1) ? low_reg + 1 : LoadArg(in_position + 1); + return (low_reg & 0xff) | ((high_reg & 0xff) << 8); + } + int low_reg = AllocTemp(); + int offset = (in_position + kInArgToStackOffset) * sizeof(uint32_t); + if (!wide) { + LoadWordDisp(rARM_SP, offset, low_reg); + return low_reg; } - return loc; + int high_reg = AllocTemp(); + LoadBaseDispWide(rARM_SP, offset, low_reg, high_reg, INVALID_SREG); + return (low_reg & 0xff) | ((high_reg & 0xff) << 8); } -/* Lock any referenced arguments that arrive in registers */ -void ArmMir2Lir::LockLiveArgs(MIR* mir) { - int first_in = cu_->num_regs; - const int num_arg_regs = 3; // TODO: generalize & move to RegUtil.cc - for (int i = 0; i < mir->ssa_rep->num_uses; i++) { - int v_reg = mir_graph_->SRegToVReg(mir->ssa_rep->uses[i]); - int InPosition = v_reg - first_in; - if (InPosition < num_arg_regs) { - LockTemp(rARM_ARG1 + InPosition); +void ArmMir2Lir::LoadArgDirect(int in_position, RegLocation rl_dest) { + int reg = kInArg0CoreReg + in_position; + int offset = (in_position + kInArgToStackOffset) * sizeof(uint32_t); + if (!rl_dest.wide) { + if (in_position < kInArgsInCoreRegs) { + OpRegCopy(rl_dest.low_reg, reg); + } else { + LoadWordDisp(rARM_SP, offset, rl_dest.low_reg); + } + } else { + if (in_position < kInArgsInCoreRegs - 1) { + OpRegCopyWide(rl_dest.low_reg, rl_dest.high_reg, reg, reg + 1); + } else if (in_position == kInArgsInCoreRegs - 1) { + OpRegCopy(rl_dest.low_reg, reg); + LoadWordDisp(rARM_SP, offset + sizeof(uint32_t), rl_dest.high_reg); + } else { + LoadBaseDispWide(rARM_SP, offset, rl_dest.low_reg, rl_dest.high_reg, INVALID_SREG); } } } @@ -134,26 +126,22 @@ MIR* ArmMir2Lir::SpecialIGet(BasicBlock** bb, MIR* mir, const InlineMethod& spec return NULL; // The object is not "this" and has to be null-checked. } - OpSize size = static_cast<OpSize>(data.op_size); DCHECK_NE(data.op_size, kDouble); // The inliner doesn't distinguish kDouble, uses kLong. - bool long_or_double = (data.op_size == kLong); - bool is_object = data.is_object; - - // TODO: Generate the method using only the data in special. - RegLocation rl_obj = mir_graph_->GetSrc(mir, 0); - LockLiveArgs(mir); - rl_obj = ArmMir2Lir::ArgLoc(rl_obj); - RegLocation rl_dest; - if (long_or_double) { - rl_dest = GetReturnWide(false); - } else { - rl_dest = GetReturn(false); - } + bool wide = (data.op_size == kLong); + // Point of no return - no aborts after this ArmMir2Lir::GenPrintLabel(mir); - rl_obj = LoadArg(rl_obj); - uint32_t field_idx = mir->dalvikInsn.vC; - GenIGet(field_idx, mir->optimization_flags, size, rl_dest, rl_obj, long_or_double, is_object); + LockArg(data.object_arg); + RegLocation rl_dest = wide ? GetReturnWide(false) : GetReturn(false); + int reg_obj = LoadArg(data.object_arg); + if (wide) { + LoadBaseDispWide(reg_obj, data.field_offset, rl_dest.low_reg, rl_dest.high_reg, INVALID_SREG); + } else { + LoadBaseDisp(reg_obj, data.field_offset, rl_dest.low_reg, kWord, INVALID_SREG); + } + if (data.is_volatile) { + GenMemBarrier(kLoadLoad); + } return GetNextMir(bb, mir); } @@ -164,63 +152,42 @@ MIR* ArmMir2Lir::SpecialIPut(BasicBlock** bb, MIR* mir, const InlineMethod& spec return NULL; // The object is not "this" and has to be null-checked. } - OpSize size = static_cast<OpSize>(data.op_size); DCHECK_NE(data.op_size, kDouble); // The inliner doesn't distinguish kDouble, uses kLong. - bool long_or_double = (data.op_size == kLong); - bool is_object = data.is_object; - - // TODO: Generate the method using only the data in special. - RegLocation rl_src; - RegLocation rl_obj; - LockLiveArgs(mir); - if (long_or_double) { - rl_src = mir_graph_->GetSrcWide(mir, 0); - rl_obj = mir_graph_->GetSrc(mir, 2); - } else { - rl_src = mir_graph_->GetSrc(mir, 0); - rl_obj = mir_graph_->GetSrc(mir, 1); - } - rl_src = ArmMir2Lir::ArgLoc(rl_src); - rl_obj = ArmMir2Lir::ArgLoc(rl_obj); - // Reject if source is split across registers & frame - if (rl_src.location == kLocInvalid) { - ResetRegPool(); - return NULL; - } + bool wide = (data.op_size == kLong); + // Point of no return - no aborts after this ArmMir2Lir::GenPrintLabel(mir); - rl_obj = LoadArg(rl_obj); - rl_src = LoadArg(rl_src); - uint32_t field_idx = mir->dalvikInsn.vC; - GenIPut(field_idx, mir->optimization_flags, size, rl_src, rl_obj, long_or_double, is_object); - return GetNextMir(bb, mir); -} - -MIR* ArmMir2Lir::SpecialIdentity(MIR* mir) { - RegLocation rl_src; - RegLocation rl_dest; - bool wide = (mir->ssa_rep->num_uses == 2); + LockArg(data.object_arg); + LockArg(data.src_arg, wide); + int reg_obj = LoadArg(data.object_arg); + int reg_src = LoadArg(data.src_arg, wide); + if (data.is_volatile) { + GenMemBarrier(kStoreStore); + } if (wide) { - rl_src = mir_graph_->GetSrcWide(mir, 0); - rl_dest = GetReturnWide(false); + StoreBaseDispWide(reg_obj, data.field_offset, reg_src & 0xff, reg_src >> 8); } else { - rl_src = mir_graph_->GetSrc(mir, 0); - rl_dest = GetReturn(false); + StoreBaseDisp(reg_obj, data.field_offset, reg_src, kWord); + } + if (data.is_volatile) { + GenMemBarrier(kLoadLoad); } - LockLiveArgs(mir); - rl_src = ArmMir2Lir::ArgLoc(rl_src); - if (rl_src.location == kLocInvalid) { - ResetRegPool(); - return NULL; + if (data.is_object) { + MarkGCCard(reg_src, reg_obj); } + return GetNextMir(bb, mir); +} + +MIR* ArmMir2Lir::SpecialIdentity(MIR* mir, const InlineMethod& special) { + const InlineReturnArgData& data = special.d.return_data; + DCHECK_NE(data.op_size, kDouble); // The inliner doesn't distinguish kDouble, uses kLong. + bool wide = (data.op_size == kLong); + // Point of no return - no aborts after this ArmMir2Lir::GenPrintLabel(mir); - rl_src = LoadArg(rl_src); - if (wide) { - StoreValueWide(rl_dest, rl_src); - } else { - StoreValue(rl_dest, rl_src); - } + LockArg(data.arg, wide); + RegLocation rl_dest = wide ? GetReturnWide(false) : GetReturn(false); + LoadArgDirect(data.arg, rl_dest); return mir; } @@ -249,8 +216,7 @@ void ArmMir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir, next_mir = SpecialIPut(&bb, mir, special); break; case kInlineOpReturnArg: - // TODO: Generate the method using only the data in special. - next_mir = SpecialIdentity(mir); + next_mir = SpecialIdentity(mir, special); break; default: return; diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h index 598da89372..7ee241c54f 100644 --- a/compiler/dex/quick/arm/codegen_arm.h +++ b/compiler/dex/quick/arm/codegen_arm.h @@ -167,7 +167,6 @@ class ArmMir2Lir : public Mir2Lir { void OpRegCopyWide(int dest_lo, int dest_hi, int src_lo, int src_hi); void OpTlsCmp(ThreadOffset offset, int val); - RegLocation ArgLoc(RegLocation loc); LIR* LoadBaseDispBody(int rBase, int displacement, int r_dest, int r_dest_hi, OpSize size, int s_reg); LIR* StoreBaseDispBody(int rBase, int displacement, int r_src, int r_src_hi, OpSize size); @@ -186,13 +185,13 @@ class ArmMir2Lir : public Mir2Lir { private: void GenFusedLongCmpImmBranch(BasicBlock* bb, RegLocation rl_src1, int64_t val, ConditionCode ccode); - int InPosition(int s_reg); - RegLocation LoadArg(RegLocation loc); - void LockLiveArgs(MIR* mir); + void LockArg(int in_position, bool wide = false); + int LoadArg(int in_position, bool wide = false); + void LoadArgDirect(int in_position, RegLocation rl_dest); MIR* GetNextMir(BasicBlock** p_bb, MIR* mir); MIR* SpecialIGet(BasicBlock** bb, MIR* mir, const InlineMethod& special); MIR* SpecialIPut(BasicBlock** bb, MIR* mir, const InlineMethod& special); - MIR* SpecialIdentity(MIR* mir); + MIR* SpecialIdentity(MIR* mir, const InlineMethod& special); LIR* LoadFPConstantValue(int r_dest, int value); void ReplaceFixup(LIR* prev_lir, LIR* orig_lir, LIR* new_lir); void InsertFixupBefore(LIR* prev_lir, LIR* orig_lir, LIR* new_lir); diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index 150794e75d..43928fc5e2 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -357,6 +357,7 @@ void ArmMir2Lir::OpRegCopyWide(int dest_lo, int dest_hi, int src_lo, } else { // Handle overlap if (src_hi == dest_lo) { + DCHECK_NE(src_lo, dest_hi); OpRegCopy(dest_hi, src_hi); OpRegCopy(dest_lo, src_lo); } else { diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h index c49f627661..8c385a181f 100644 --- a/compiler/dex/quick/x86/x86_lir.h +++ b/compiler/dex/quick/x86/x86_lir.h @@ -22,54 +22,53 @@ namespace art { /* - * Runtime register conventions. We consider both x86, x86-64 and x32 (32bit mode x86-64), although - * we currently only target x86. The ABI has different conventions and we hope to have a single - * convention to simplify code generation. Changing something that is callee save and making it - * caller save places a burden on up-calls to save/restore the callee save register, however, there - * are few registers that are callee save in the ABI. Changing something that is caller save and - * making it callee save places a burden on down-calls to save/restore the callee save register. - * For these reasons we aim to match native conventions for caller and callee save. The first 4 - * registers can be used for byte operations, for this reason they are preferred for temporary - * scratch registers. + * Runtime register conventions. We consider both x86, x86-64 and x32 (32bit mode x86-64). The ABI + * has different conventions and we capture those here. Changing something that is callee save and + * making it caller save places a burden on up-calls to save/restore the callee save register, + * however, there are few registers that are callee save in the ABI. Changing something that is + * caller save and making it callee save places a burden on down-calls to save/restore the callee + * save register. For these reasons we aim to match native conventions for caller and callee save. + * On x86 only the first 4 registers can be used for byte operations, for this reason they are + * preferred for temporary scratch registers. * * General Purpose Register: - * Native: x86 | x86-64 / x32 | ART - * r0/eax: caller save | caller save | caller, Method*, scratch, return value - * r1/ecx: caller save | caller save, arg4 | caller, arg1, scratch - * r2/edx: caller save | caller save, arg3 | caller, arg2, scratch, high half of long return - * r3/ebx: callEE save | callEE save | callER, arg3, scratch + * Native: x86 | x86-64 / x32 | ART x86 | ART x86-64 + * r0/eax: caller | caller | caller, Method*, scratch, return value | caller, scratch, return value + * r1/ecx: caller | caller, arg4 | caller, arg1, scratch | caller, arg3, scratch + * r2/edx: caller | caller, arg3 | caller, arg2, scratch, high half of long return | caller, arg2, scratch + * r3/ebx: callEE | callEE | callER, arg3, scratch | callee, promotable * r4/esp: stack pointer - * r5/ebp: callee save | callee save | callee, available for dalvik register promotion - * r6/esi: callEE save | callER save, arg2 | callee, available for dalvik register promotion - * r7/edi: callEE save | callER save, arg1 | callee, available for dalvik register promotion + * r5/ebp: callee | callee | callee, promotable | callee, promotable + * r6/esi: callEE | callER, arg2 | callee, promotable | caller, arg1, scratch + * r7/edi: callEE | callER, arg1 | callee, promotable | caller, Method*, scratch * --- x86-64/x32 registers * Native: x86-64 / x32 | ART - * r8: caller save, arg5 | caller, scratch - * r9: caller save, arg6 | caller, scratch + * r8: caller save, arg5 | caller, arg4, scratch + * r9: caller save, arg6 | caller, arg5, scratch * r10: caller save | caller, scratch * r11: caller save | caller, scratch - * r12: callee save | callee, available for dalvik register promotion - * r13: callee save | callee, available for dalvik register promotion - * r14: callee save | callee, available for dalvik register promotion - * r15: callee save | callee, available for dalvik register promotion + * r12: callee save | callee, available for register promotion (promotable) + * r13: callee save | callee, available for register promotion (promotable) + * r14: callee save | callee, available for register promotion (promotable) + * r15: callee save | callee, available for register promotion (promotable) * * There is no rSELF, instead on x86 fs: has a base address of Thread::Current, whereas on * x86-64/x32 gs: holds it. * * For floating point we don't support CPUs without SSE2 support (ie newer than PIII): - * Native: x86 | x86-64 / x32 | ART - * XMM0: caller save |caller save, arg1 | caller, float/double return value (except for native x86 code) - * XMM1: caller save |caller save, arg2 | caller, scratch - * XMM2: caller save |caller save, arg3 | caller, scratch - * XMM3: caller save |caller save, arg4 | caller, scratch - * XMM4: caller save |caller save, arg5 | caller, scratch - * XMM5: caller save |caller save, arg6 | caller, scratch - * XMM6: caller save |caller save, arg7 | caller, scratch - * XMM7: caller save |caller save, arg8 | caller, scratch + * Native: x86 | x86-64 / x32 | ART x86 | ART x86-64 + * XMM0: caller | caller, arg1 | caller, float return value | caller, arg1, float return value + * XMM1: caller | caller, arg2 | caller, scratch | caller, arg2, scratch + * XMM2: caller | caller, arg3 | caller, scratch | caller, arg3, scratch + * XMM3: caller | caller, arg4 | caller, scratch | caller, arg4, scratch + * XMM4: caller | caller, arg5 | caller, scratch | caller, arg5, scratch + * XMM5: caller | caller, arg6 | caller, scratch | caller, arg6, scratch + * XMM6: caller | caller, arg7 | caller, scratch | caller, arg7, scratch + * XMM7: caller | caller, arg8 | caller, scratch | caller, arg8, scratch * --- x86-64/x32 registers - * XMM8 .. 15: caller save + * XMM8 .. 15: caller save available as scratch registers for ART. * - * X87 is a necessary evil outside of ART code: + * X87 is a necessary evil outside of ART code for x86: * ST0: x86 float/double native return value, caller save * ST1 .. ST7: caller save * diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 402d4f4b18..61e9fbb151 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1200,7 +1200,7 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType if (no_guarantee_of_dex_cache_entry) { // See if the method is also declared in this dex cache. uint32_t dex_method_idx = MethodHelper(method).FindDexMethodIndexInOtherDexFile( - *target_method->dex_file); + *target_method->dex_file, target_method->dex_method_index); if (dex_method_idx != DexFile::kDexNoIndex) { target_method->dex_method_index = dex_method_idx; } else { diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 0e5c60ae8d..cf3f72e4d0 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -274,7 +274,7 @@ INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvo * r2 = size of argument array in bytes * r3 = (managed) thread pointer * [sp] = JValue* result - * [sp + 4] = result type char + * [sp + 4] = shorty */ ENTRY art_quick_invoke_stub push {r0, r4, r5, r9, r11, lr} @ spill regs diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index c60bca0e5e..f9a200a770 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -456,7 +456,7 @@ INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvo * a2 = size of argument array in bytes * a3 = (managed) thread pointer * [sp + 16] = JValue* result - * [sp + 20] = result type char + * [sp + 20] = shorty */ ENTRY art_quick_invoke_stub GENERATE_GLOBAL_POINTER @@ -502,7 +502,8 @@ ENTRY art_quick_invoke_stub addiu $sp, $sp, 16 .cfi_adjust_cfa_offset -16 lw $t0, 16($sp) # get result pointer - lw $t1, 20($sp) # get result type char + lw $t1, 20($sp) # get shorty + lb $t1, 0($t1) # get result type char li $t2, 68 # put char 'D' into t2 beq $t1, $t2, 1f # branch if result type char == 'D' li $t3, 70 # put char 'F' into t3 diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 9c3eb30736..c76c6b269b 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -251,7 +251,7 @@ INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvo * [sp + 12] = size of argument array in bytes * [sp + 16] = (managed) thread pointer * [sp + 20] = JValue* result - * [sp + 24] = result type char + * [sp + 24] = shorty */ DEFINE_FUNCTION art_quick_invoke_stub PUSH ebp // save ebp @@ -281,17 +281,20 @@ DEFINE_FUNCTION art_quick_invoke_stub POP ebx // pop ebx POP ebp // pop ebp mov 20(%esp), %ecx // get result pointer - cmpl LITERAL(68), 24(%esp) // test if result type char == 'D' + mov %eax, (%ecx) // store the result assuming its a long, int or Object* + mov %edx, 4(%ecx) // store the other half of the result + mov 24(%esp), %edx // get the shorty + cmpb LITERAL(68), (%edx) // test if result type char == 'D' je return_double_quick - cmpl LITERAL(70), 24(%esp) // test if result type char == 'F' + cmpb LITERAL(70), (%edx) // test if result type char == 'F' je return_float_quick - mov %eax, (%ecx) // store the result - mov %edx, 4(%ecx) // store the other half of the result ret return_double_quick: -return_float_quick: movsd %xmm0, (%ecx) // store the floating point result ret +return_float_quick: + movss %xmm0, (%ecx) // store the floating point result + ret END_FUNCTION art_quick_invoke_stub MACRO3(NO_ARG_DOWNCALL, c_name, cxx_name, return_macro) diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index e01a31b6b4..44bc7a20af 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -149,6 +149,14 @@ INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvo /* * Quick invocation stub. + * On entry: + * [sp] = return address + * rdi = method pointer + * rsi = argument array or NULL for no argument methods + * rdx = size of argument array in bytes + * rcx = (managed) thread pointer + * r8 = JValue* result + * r9 = char* shorty */ DEFINE_FUNCTION art_quick_invoke_stub int3 diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 5acef70a82..fac1e1406e 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -3154,7 +3154,7 @@ bool ClassLinker::InitializeClass(const SirtRef<mirror::Class>& klass, bool can_ CHECK(can_init_statics); if (LIKELY(Runtime::Current()->IsStarted())) { JValue result; - clinit->Invoke(self, NULL, 0, &result, 'V'); + clinit->Invoke(self, NULL, 0, &result, "V"); } else { art::interpreter::EnterInterpreterFromInvoke(self, clinit, NULL, NULL, NULL); } diff --git a/runtime/common_test.h b/runtime/common_test.h index af7e8aecf8..f7859ea0bc 100644 --- a/runtime/common_test.h +++ b/runtime/common_test.h @@ -268,7 +268,7 @@ class CommonTest : public testing::Test { MakeExecutable(&code[0], code.size()); } - // Create an OatMethod based on pointers (for unit tests) + // Create an OatMethod based on pointers (for unit tests). OatFile::OatMethod CreateOatMethod(const void* code, const size_t frame_size_in_bytes, const uint32_t core_spill_mask, @@ -276,11 +276,23 @@ class CommonTest : public testing::Test { const uint8_t* mapping_table, const uint8_t* vmap_table, const uint8_t* gc_map) { - const byte* base = nullptr; // Base of data in oat file, ie 0. - uint32_t code_offset = PointerToLowMemUInt32(code); - uint32_t mapping_table_offset = PointerToLowMemUInt32(mapping_table); - uint32_t vmap_table_offset = PointerToLowMemUInt32(vmap_table); - uint32_t gc_map_offset = PointerToLowMemUInt32(gc_map); + const byte* base; + uint32_t code_offset, mapping_table_offset, vmap_table_offset, gc_map_offset; + if (mapping_table == nullptr && vmap_table == nullptr && gc_map == nullptr) { + base = reinterpret_cast<const byte*>(code); // Base of data points at code. + base -= kPointerSize; // Move backward so that code_offset != 0. + code_offset = kPointerSize; + mapping_table_offset = 0; + vmap_table_offset = 0; + gc_map_offset = 0; + } else { + // TODO: 64bit support. + base = nullptr; // Base of data in oat file, ie 0. + code_offset = PointerToLowMemUInt32(code); + mapping_table_offset = PointerToLowMemUInt32(mapping_table); + vmap_table_offset = PointerToLowMemUInt32(vmap_table); + gc_map_offset = PointerToLowMemUInt32(gc_map); + } return OatFile::OatMethod(base, code_offset, frame_size_in_bytes, @@ -470,6 +482,8 @@ class CommonTest : public testing::Test { instruction_set = kX86; #elif defined(__x86_64__) instruction_set = kX86_64; + // TODO: x86_64 compilation support. + runtime_->SetCompilerFilter(Runtime::kInterpretOnly); #endif for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) { diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 67db2abe7b..733e8430c8 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -3019,7 +3019,7 @@ void Dbg::ExecuteMethod(DebugInvokeReq* pReq) { MethodHelper mh(m.get()); ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength()); arg_array.BuildArgArray(soa, pReq->receiver, reinterpret_cast<jvalue*>(pReq->arg_values)); - InvokeWithArgArray(soa, m.get(), &arg_array, &pReq->result_value, mh.GetShorty()[0]); + InvokeWithArgArray(soa, m.get(), &arg_array, &pReq->result_value, mh.GetShorty()); mirror::Throwable* exception = soa.Self()->GetException(NULL); soa.Self()->ClearException(); diff --git a/runtime/entrypoints/interpreter/interpreter_entrypoints.cc b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc index 8a2ce510f5..2067a455af 100644 --- a/runtime/entrypoints/interpreter/interpreter_entrypoints.cc +++ b/runtime/entrypoints/interpreter/interpreter_entrypoints.cc @@ -48,11 +48,11 @@ extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper& m if (kUsePortableCompiler) { ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength()); arg_array.BuildArgArrayFromFrame(shadow_frame, arg_offset); - method->Invoke(self, arg_array.GetArray(), arg_array.GetNumBytes(), result, mh.GetShorty()[0]); + method->Invoke(self, arg_array.GetArray(), arg_array.GetNumBytes(), result, mh.GetShorty()); } else { method->Invoke(self, shadow_frame->GetVRegArgs(arg_offset), (shadow_frame->NumberOfVRegs() - arg_offset) * sizeof(uint32_t), - result, mh.GetShorty()[0]); + result, mh.GetShorty()); } } diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 9f301907e2..f9486c3506 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -561,9 +561,11 @@ extern "C" const void* artQuickResolutionTrampoline(mirror::ArtMethod* called, caller->GetDexCacheResolvedMethods()->Set(called->GetDexMethodIndex(), called); } else { // Calling from one dex file to another, need to compute the method index appropriate to - // the caller's dex file. + // the caller's dex file. Since we get here only if the original called was a runtime + // method, we've got the correct dex_file and a dex_method_idx from above. + DCHECK(&MethodHelper(caller).GetDexFile() == dex_file); uint32_t method_index = - MethodHelper(called).FindDexMethodIndexInOtherDexFile(MethodHelper(caller).GetDexFile()); + MethodHelper(called).FindDexMethodIndexInOtherDexFile(*dex_file, dex_method_idx); if (method_index != DexFile::kDexNoIndex) { caller->GetDexCacheResolvedMethods()->Set(method_index, called); } diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index d98fe370d8..a324925094 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -2319,10 +2319,10 @@ mirror::Object* Heap::GetReferenceReferent(mirror::Object* reference) { void Heap::AddFinalizerReference(Thread* self, mirror::Object* object) { ScopedObjectAccess soa(self); JValue result; - ArgArray arg_array(NULL, 0); + ArgArray arg_array("VL", 2); arg_array.Append(object); soa.DecodeMethod(WellKnownClasses::java_lang_ref_FinalizerReference_add)->Invoke(self, - arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'V'); + arg_array.GetArray(), arg_array.GetNumBytes(), &result, "VL"); } void Heap::EnqueueClearedReferences() { @@ -2333,10 +2333,10 @@ void Heap::EnqueueClearedReferences() { if (LIKELY(Runtime::Current()->IsStarted())) { ScopedObjectAccess soa(self); JValue result; - ArgArray arg_array(NULL, 0); + ArgArray arg_array("VL", 2); arg_array.Append(cleared_references_.GetList()); soa.DecodeMethod(WellKnownClasses::java_lang_ref_ReferenceQueue_add)->Invoke(soa.Self(), - arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'V'); + arg_array.GetArray(), arg_array.GetNumBytes(), &result, "VL"); } cleared_references_.Clear(); } diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index fbaadfb21d..6f31ca7715 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -139,13 +139,13 @@ static void CheckMethodArguments(ArtMethod* m, uint32_t* args) } void InvokeWithArgArray(const ScopedObjectAccess& soa, ArtMethod* method, - ArgArray* arg_array, JValue* result, char result_type) + ArgArray* arg_array, JValue* result, const char* shorty) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { uint32_t* args = arg_array->GetArray(); if (UNLIKELY(soa.Env()->check_jni)) { CheckMethodArguments(method, args); } - method->Invoke(soa.Self(), args, arg_array->GetNumBytes(), result, result_type); + method->Invoke(soa.Self(), args, arg_array->GetNumBytes(), result, shorty); } static JValue InvokeWithVarArgs(const ScopedObjectAccess& soa, jobject obj, @@ -157,7 +157,7 @@ static JValue InvokeWithVarArgs(const ScopedObjectAccess& soa, jobject obj, JValue result; ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength()); arg_array.BuildArgArray(soa, receiver, args); - InvokeWithArgArray(soa, method, &arg_array, &result, mh.GetShorty()[0]); + InvokeWithArgArray(soa, method, &arg_array, &result, mh.GetShorty()); return result; } @@ -175,7 +175,7 @@ static JValue InvokeVirtualOrInterfaceWithJValues(const ScopedObjectAccess& soa, JValue result; ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength()); arg_array.BuildArgArray(soa, receiver, args); - InvokeWithArgArray(soa, method, &arg_array, &result, mh.GetShorty()[0]); + InvokeWithArgArray(soa, method, &arg_array, &result, mh.GetShorty()); return result; } @@ -188,7 +188,7 @@ static JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccess& soa, JValue result; ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength()); arg_array.BuildArgArray(soa, receiver, args); - InvokeWithArgArray(soa, method, &arg_array, &result, mh.GetShorty()[0]); + InvokeWithArgArray(soa, method, &arg_array, &result, mh.GetShorty()); return result; } @@ -637,7 +637,7 @@ JValue InvokeWithJValues(const ScopedObjectAccess& soa, jobject obj, jmethodID m JValue result; ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength()); arg_array.BuildArgArray(soa, receiver, args); - InvokeWithArgArray(soa, method, &arg_array, &result, mh.GetShorty()[0]); + InvokeWithArgArray(soa, method, &arg_array, &result, mh.GetShorty()); return result; } @@ -2437,8 +2437,10 @@ class JNI { m = c->FindVirtualMethod(name, sig); } if (m == NULL) { + c->DumpClass(LOG(ERROR), mirror::Class::kDumpClassFullDetail); LOG(return_errors ? ERROR : FATAL) << "Failed to register native method " - << PrettyDescriptor(c) << "." << name << sig; + << PrettyDescriptor(c) << "." << name << sig << " in " + << c->GetDexCache()->GetLocation()->ToModifiedUtf8(); ThrowNoSuchMethodError(soa, c, name, sig, "static or non-static"); return JNI_ERR; } else if (!m->IsNative()) { diff --git a/runtime/jni_internal.h b/runtime/jni_internal.h index 26905c7a27..9e109875cd 100644 --- a/runtime/jni_internal.h +++ b/runtime/jni_internal.h @@ -57,7 +57,7 @@ void RegisterNativeMethods(JNIEnv* env, const char* jni_class_name, const JNINat JValue InvokeWithJValues(const ScopedObjectAccess&, jobject obj, jmethodID mid, jvalue* args) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void InvokeWithArgArray(const ScopedObjectAccess& soa, mirror::ArtMethod* method, - ArgArray *arg_array, JValue* result, char result_type) + ArgArray *arg_array, JValue* result, const char* shorty) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); int ThrowNewException(JNIEnv* env, jclass exception_class, const char* msg, jobject cause); diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc index fed734ef5e..4c58c84eb4 100644 --- a/runtime/jni_internal_test.cc +++ b/runtime/jni_internal_test.cc @@ -134,14 +134,14 @@ class JniInternalTest : public CommonTest { arg_array.Append(receiver); } - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'V'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "V"); } void InvokeIdentityByteMethod(bool is_static) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::ArtMethod* method; mirror::Object* receiver; - JniInternalTestMakeExecutable(&method, &receiver, is_static, "identity", "(I)I"); + JniInternalTestMakeExecutable(&method, &receiver, is_static, "identity", "(B)B"); ArgArray arg_array(NULL, 0); uint32_t* args = arg_array.GetArray(); @@ -154,22 +154,22 @@ class JniInternalTest : public CommonTest { arg_array.Append(0U); result.SetB(-1); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'B'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "BB"); EXPECT_EQ(0, result.GetB()); args[0] = -1; result.SetB(0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'B'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "BB"); EXPECT_EQ(-1, result.GetB()); args[0] = SCHAR_MAX; result.SetB(0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'B'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "BB"); EXPECT_EQ(SCHAR_MAX, result.GetB()); args[0] = (SCHAR_MIN << 24) >> 24; result.SetB(0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'B'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "BB"); EXPECT_EQ(SCHAR_MIN, result.GetB()); } @@ -190,22 +190,22 @@ class JniInternalTest : public CommonTest { arg_array.Append(0U); result.SetI(-1); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'I'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "II"); EXPECT_EQ(0, result.GetI()); args[0] = -1; result.SetI(0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'I'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "II"); EXPECT_EQ(-1, result.GetI()); args[0] = INT_MAX; result.SetI(0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'I'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "II"); EXPECT_EQ(INT_MAX, result.GetI()); args[0] = INT_MIN; result.SetI(0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'I'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "II"); EXPECT_EQ(INT_MIN, result.GetI()); } @@ -228,28 +228,28 @@ class JniInternalTest : public CommonTest { value.SetD(0.0); arg_array.AppendWide(value.GetJ()); result.SetD(-1.0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'D'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "DD"); EXPECT_EQ(0.0, result.GetD()); value.SetD(-1.0); args[0] = value.GetJ(); args[1] = value.GetJ() >> 32; result.SetD(0.0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'D'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "DD"); EXPECT_EQ(-1.0, result.GetD()); value.SetD(DBL_MAX); args[0] = value.GetJ(); args[1] = value.GetJ() >> 32; result.SetD(0.0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'D'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "DD"); EXPECT_EQ(DBL_MAX, result.GetD()); value.SetD(DBL_MIN); args[0] = value.GetJ(); args[1] = value.GetJ() >> 32; result.SetD(0.0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'D'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "DD"); EXPECT_EQ(DBL_MIN, result.GetD()); } @@ -271,31 +271,31 @@ class JniInternalTest : public CommonTest { arg_array.Append(0U); arg_array.Append(0U); result.SetI(-1); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'I'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "III"); EXPECT_EQ(0, result.GetI()); args[0] = 1; args[1] = 2; result.SetI(0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'I'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "III"); EXPECT_EQ(3, result.GetI()); args[0] = -2; args[1] = 5; result.SetI(0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'I'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "III"); EXPECT_EQ(3, result.GetI()); args[0] = INT_MAX; args[1] = INT_MIN; result.SetI(1234); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'I'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "III"); EXPECT_EQ(-1, result.GetI()); args[0] = INT_MAX; args[1] = INT_MAX; result.SetI(INT_MIN); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'I'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "III"); EXPECT_EQ(-2, result.GetI()); } @@ -318,35 +318,40 @@ class JniInternalTest : public CommonTest { arg_array.Append(0U); arg_array.Append(0U); result.SetI(-1); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'I'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, + "IIII"); EXPECT_EQ(0, result.GetI()); args[0] = 1; args[1] = 2; args[2] = 3; result.SetI(0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'I'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, + "IIII"); EXPECT_EQ(6, result.GetI()); args[0] = -1; args[1] = 2; args[2] = -3; result.SetI(0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'I'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, + "IIII"); EXPECT_EQ(-2, result.GetI()); args[0] = INT_MAX; args[1] = INT_MIN; args[2] = INT_MAX; result.SetI(1234); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'I'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, + "IIII"); EXPECT_EQ(2147483646, result.GetI()); args[0] = INT_MAX; args[1] = INT_MAX; args[2] = INT_MAX; result.SetI(INT_MIN); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'I'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, + "IIII"); EXPECT_EQ(2147483645, result.GetI()); } @@ -370,7 +375,8 @@ class JniInternalTest : public CommonTest { arg_array.Append(0U); arg_array.Append(0U); result.SetI(-1); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'I'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, + "IIIII"); EXPECT_EQ(0, result.GetI()); args[0] = 1; @@ -378,7 +384,8 @@ class JniInternalTest : public CommonTest { args[2] = 3; args[3] = 4; result.SetI(0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'I'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, + "IIIII"); EXPECT_EQ(10, result.GetI()); args[0] = -1; @@ -386,7 +393,8 @@ class JniInternalTest : public CommonTest { args[2] = -3; args[3] = 4; result.SetI(0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'I'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, + "IIIII"); EXPECT_EQ(2, result.GetI()); args[0] = INT_MAX; @@ -394,7 +402,8 @@ class JniInternalTest : public CommonTest { args[2] = INT_MAX; args[3] = INT_MIN; result.SetI(1234); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'I'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, + "IIIII"); EXPECT_EQ(-2, result.GetI()); args[0] = INT_MAX; @@ -402,7 +411,8 @@ class JniInternalTest : public CommonTest { args[2] = INT_MAX; args[3] = INT_MAX; result.SetI(INT_MIN); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'I'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, + "IIIII"); EXPECT_EQ(-4, result.GetI()); } @@ -427,7 +437,8 @@ class JniInternalTest : public CommonTest { arg_array.Append(0U); arg_array.Append(0U); result.SetI(-1.0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'I'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, + "IIIIII"); EXPECT_EQ(0, result.GetI()); args[0] = 1; @@ -436,7 +447,8 @@ class JniInternalTest : public CommonTest { args[3] = 4; args[4] = 5; result.SetI(0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'I'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, + "IIIIII"); EXPECT_EQ(15, result.GetI()); args[0] = -1; @@ -445,7 +457,8 @@ class JniInternalTest : public CommonTest { args[3] = 4; args[4] = -5; result.SetI(0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'I'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, + "IIIIII"); EXPECT_EQ(-3, result.GetI()); args[0] = INT_MAX; @@ -454,7 +467,8 @@ class JniInternalTest : public CommonTest { args[3] = INT_MIN; args[4] = INT_MAX; result.SetI(1234); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'I'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, + "IIIIII"); EXPECT_EQ(2147483645, result.GetI()); args[0] = INT_MAX; @@ -463,7 +477,8 @@ class JniInternalTest : public CommonTest { args[3] = INT_MAX; args[4] = INT_MAX; result.SetI(INT_MIN); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'I'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, + "IIIIII"); EXPECT_EQ(2147483643, result.GetI()); } @@ -489,7 +504,8 @@ class JniInternalTest : public CommonTest { arg_array.AppendWide(value.GetJ()); arg_array.AppendWide(value2.GetJ()); result.SetD(-1.0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'D'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, + "DDD"); EXPECT_EQ(0.0, result.GetD()); value.SetD(1.0); @@ -499,7 +515,8 @@ class JniInternalTest : public CommonTest { args[2] = value2.GetJ(); args[3] = value2.GetJ() >> 32; result.SetD(0.0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'D'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, + "DDD"); EXPECT_EQ(3.0, result.GetD()); value.SetD(1.0); @@ -509,7 +526,8 @@ class JniInternalTest : public CommonTest { args[2] = value2.GetJ(); args[3] = value2.GetJ() >> 32; result.SetD(0.0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'D'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, + "DDD"); EXPECT_EQ(-1.0, result.GetD()); value.SetD(DBL_MAX); @@ -519,7 +537,8 @@ class JniInternalTest : public CommonTest { args[2] = value2.GetJ(); args[3] = value2.GetJ() >> 32; result.SetD(0.0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'D'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, + "DDD"); EXPECT_EQ(1.7976931348623157e308, result.GetD()); value.SetD(DBL_MAX); @@ -529,7 +548,8 @@ class JniInternalTest : public CommonTest { args[2] = value2.GetJ(); args[3] = value2.GetJ() >> 32; result.SetD(0.0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'D'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, + "DDD"); EXPECT_EQ(INFINITY, result.GetD()); } @@ -558,7 +578,8 @@ class JniInternalTest : public CommonTest { arg_array.AppendWide(value2.GetJ()); arg_array.AppendWide(value3.GetJ()); result.SetD(-1.0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'D'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, + "DDDD"); EXPECT_EQ(0.0, result.GetD()); value.SetD(1.0); @@ -571,7 +592,8 @@ class JniInternalTest : public CommonTest { args[4] = value3.GetJ(); args[5] = value3.GetJ() >> 32; result.SetD(0.0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'D'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, + "DDDD"); EXPECT_EQ(6.0, result.GetD()); value.SetD(1.0); @@ -584,7 +606,8 @@ class JniInternalTest : public CommonTest { args[4] = value3.GetJ(); args[5] = value3.GetJ() >> 32; result.SetD(0.0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'D'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, + "DDDD"); EXPECT_EQ(2.0, result.GetD()); } @@ -616,7 +639,8 @@ class JniInternalTest : public CommonTest { arg_array.AppendWide(value3.GetJ()); arg_array.AppendWide(value4.GetJ()); result.SetD(-1.0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'D'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, + "DDDDD"); EXPECT_EQ(0.0, result.GetD()); value.SetD(1.0); @@ -632,7 +656,8 @@ class JniInternalTest : public CommonTest { args[6] = value4.GetJ(); args[7] = value4.GetJ() >> 32; result.SetD(0.0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'D'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, + "DDDDD"); EXPECT_EQ(10.0, result.GetD()); value.SetD(1.0); @@ -648,7 +673,8 @@ class JniInternalTest : public CommonTest { args[6] = value4.GetJ(); args[7] = value4.GetJ() >> 32; result.SetD(0.0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'D'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, + "DDDDD"); EXPECT_EQ(-2.0, result.GetD()); } @@ -683,7 +709,8 @@ class JniInternalTest : public CommonTest { arg_array.AppendWide(value4.GetJ()); arg_array.AppendWide(value5.GetJ()); result.SetD(-1.0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'D'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, + "DDDDDD"); EXPECT_EQ(0.0, result.GetD()); value.SetD(1.0); @@ -702,7 +729,8 @@ class JniInternalTest : public CommonTest { args[8] = value5.GetJ(); args[9] = value5.GetJ() >> 32; result.SetD(0.0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'D'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, + "DDDDDD"); EXPECT_EQ(15.0, result.GetD()); value.SetD(1.0); @@ -721,7 +749,8 @@ class JniInternalTest : public CommonTest { args[8] = value5.GetJ(); args[9] = value5.GetJ() >> 32; result.SetD(0.0); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'D'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, + "DDDDDD"); EXPECT_EQ(3.0, result.GetD()); } @@ -1764,7 +1793,7 @@ TEST_F(JniInternalTest, StaticMainMethod) { CHECK(started); Thread::Current()->TransitionFromSuspendedToRunnable(); - method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'V'); + method->Invoke(Thread::Current(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, "VL"); } TEST_F(JniInternalTest, StaticNopMethod) { diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc index 3359d596d7..67e6c7db3b 100644 --- a/runtime/mirror/art_method.cc +++ b/runtime/mirror/art_method.cc @@ -35,7 +35,8 @@ namespace art { namespace mirror { extern "C" void art_portable_invoke_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*, char); -extern "C" void art_quick_invoke_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*, char); +extern "C" void art_quick_invoke_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*, + const char*); // TODO: get global references for these Class* ArtMethod::java_lang_reflect_ArtMethod_ = NULL; @@ -245,10 +246,11 @@ uint32_t ArtMethod::FindCatchBlock(Class* exception_type, uint32_t dex_pc, } void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result, - char result_type) { + const char* shorty) { if (kIsDebugBuild) { self->AssertThreadSuspensionIsAllowable(); CHECK_EQ(kRunnable, self->GetState()); + CHECK_STREQ(MethodHelper(this).GetShorty(), shorty); } // Push a transition back into managed code onto the linked list in thread. @@ -274,9 +276,9 @@ void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* : GetEntryPointFromPortableCompiledCode()); } if (!IsPortableCompiled()) { - (*art_quick_invoke_stub)(this, args, args_size, self, result, result_type); + (*art_quick_invoke_stub)(this, args, args_size, self, result, shorty); } else { - (*art_portable_invoke_stub)(this, args, args_size, self, result, result_type); + (*art_portable_invoke_stub)(this, args, args_size, self, result, shorty[0]); } if (UNLIKELY(reinterpret_cast<intptr_t>(self->GetException(NULL)) == -1)) { // Unusual case where we were running LLVM generated code and an diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h index dfaf063c07..e678503e4c 100644 --- a/runtime/mirror/art_method.h +++ b/runtime/mirror/art_method.h @@ -207,8 +207,8 @@ class MANAGED ArtMethod : public Object { // Find the method that this method overrides ArtMethod* FindOverriddenMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result, char result_type) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result, + const char* shorty) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); EntryPointFromInterpreter* GetEntryPointFromInterpreter() { return GetFieldPtr<EntryPointFromInterpreter*>( diff --git a/runtime/object_utils.h b/runtime/object_utils.h index 0451f5d5ed..a981fabf3d 100644 --- a/runtime/object_utils.h +++ b/runtime/object_utils.h @@ -607,6 +607,37 @@ class MethodHelper { return DexFile::kDexNoIndex; } + // The name_and_signature_idx MUST point to a MethodId with the same name and signature in the + // other_dexfile, such as the method index used to resolve this method in the other_dexfile. + uint32_t FindDexMethodIndexInOtherDexFile(const DexFile& other_dexfile, + uint32_t name_and_signature_idx) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + const DexFile& dexfile = GetDexFile(); + const DexFile::MethodId& mid = dexfile.GetMethodId(method_->GetDexMethodIndex()); + const DexFile::MethodId& name_and_sig_mid = other_dexfile.GetMethodId(name_and_signature_idx); + DCHECK_STREQ(dexfile.GetMethodName(mid), other_dexfile.GetMethodName(name_and_sig_mid)); + DCHECK_EQ(dexfile.GetMethodSignature(mid), other_dexfile.GetMethodSignature(name_and_sig_mid)); + if (&dexfile == &other_dexfile) { + return method_->GetDexMethodIndex(); + } + const char* mid_declaring_class_descriptor = dexfile.StringByTypeIdx(mid.class_idx_); + const DexFile::StringId* other_descriptor = + other_dexfile.FindStringId(mid_declaring_class_descriptor); + if (other_descriptor != nullptr) { + const DexFile::TypeId* other_type_id = + other_dexfile.FindTypeId(other_dexfile.GetIndexForStringId(*other_descriptor)); + if (other_type_id != nullptr) { + const DexFile::MethodId* other_mid = other_dexfile.FindMethodId( + *other_type_id, other_dexfile.GetStringId(name_and_sig_mid.name_idx_), + other_dexfile.GetProtoId(name_and_sig_mid.proto_idx_)); + if (other_mid != nullptr) { + return other_dexfile.GetIndexForMethodId(*other_mid); + } + } + } + return DexFile::kDexNoIndex; + } + private: // Set the method_ field, for proxy methods looking up the interface method via the resolved // methods table. diff --git a/runtime/reflection.cc b/runtime/reflection.cc index ac8f5ef6c4..0bfa70f279 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -220,36 +220,46 @@ mirror::Object* BoxPrimitive(Primitive::Type src_class, const JValue& value) { } jmethodID m = NULL; + const char* shorty; switch (src_class) { case Primitive::kPrimBoolean: m = WellKnownClasses::java_lang_Boolean_valueOf; + shorty = "LZ"; break; case Primitive::kPrimByte: m = WellKnownClasses::java_lang_Byte_valueOf; + shorty = "LB"; break; case Primitive::kPrimChar: m = WellKnownClasses::java_lang_Character_valueOf; + shorty = "LC"; break; case Primitive::kPrimDouble: m = WellKnownClasses::java_lang_Double_valueOf; + shorty = "LD"; break; case Primitive::kPrimFloat: m = WellKnownClasses::java_lang_Float_valueOf; + shorty = "LF"; break; case Primitive::kPrimInt: m = WellKnownClasses::java_lang_Integer_valueOf; + shorty = "LI"; break; case Primitive::kPrimLong: m = WellKnownClasses::java_lang_Long_valueOf; + shorty = "LJ"; break; case Primitive::kPrimShort: m = WellKnownClasses::java_lang_Short_valueOf; + shorty = "LS"; break; case Primitive::kPrimVoid: // There's no such thing as a void field, and void methods invoked via reflection return null. - return NULL; + return nullptr; default: LOG(FATAL) << static_cast<int>(src_class); + shorty = nullptr; } ScopedObjectAccessUnchecked soa(Thread::Current()); @@ -257,7 +267,7 @@ mirror::Object* BoxPrimitive(Primitive::Type src_class, const JValue& value) { CHECK_EQ(soa.Self()->GetState(), kRunnable); } - ArgArray arg_array(NULL, 0); + ArgArray arg_array(nullptr, 0); JValue result; if (src_class == Primitive::kPrimDouble || src_class == Primitive::kPrimLong) { arg_array.AppendWide(value.GetJ()); @@ -266,7 +276,7 @@ mirror::Object* BoxPrimitive(Primitive::Type src_class, const JValue& value) { } soa.DecodeMethod(m)->Invoke(soa.Self(), arg_array.GetArray(), arg_array.GetNumBytes(), - &result, 'L'); + &result, shorty); return result.GetL(); } diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 9193be5e8e..d8f9ca3af0 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -765,6 +765,12 @@ Runtime::ParsedOptions* Runtime::ParsedOptions::Create(const Options& options, b parsed->image_ += GetAndroidRoot(); parsed->image_ += "/framework/boot.art"; } + if (!kIsTargetBuild && parsed->host_prefix_.empty()) { + const char* build_top = getenv("ANDROID_BUILD_TOP"); + if (build_top != NULL) { + parsed->host_prefix_ = build_top; + } + } if (parsed->heap_growth_limit_ == 0) { parsed->heap_growth_limit_ = parsed->heap_maximum_size_; } @@ -807,7 +813,7 @@ jobject CreateSystemClassLoader() { JValue result; ArgArray arg_array(nullptr, 0); - InvokeWithArgArray(soa, getSystemClassLoader, &arg_array, &result, 'L'); + InvokeWithArgArray(soa, getSystemClassLoader, &arg_array, &result, "L"); SirtRef<mirror::ClassLoader> class_loader(soa.Self(), down_cast<mirror::ClassLoader*>(result.GetL())); CHECK(class_loader.get() != nullptr); @@ -834,7 +840,7 @@ jobject CreateSystemClassLoader() { bool Runtime::Start() { VLOG(startup) << "Runtime::Start entering"; - CHECK(host_prefix_.empty()) << host_prefix_; + CHECK(!kIsTargetBuild || host_prefix_.empty()) << host_prefix_; // Restore main thread state to kNative as expected by native code. Thread* self = Thread::Current(); @@ -1476,12 +1482,11 @@ mirror::ArtMethod* Runtime::CreateCalleeSaveMethod(InstructionSet instruction_se method->SetFpSpillMask(0); } else if (instruction_set == kX86_64) { uint32_t ref_spills = - (1 << art::x86_64::RBP) | (1 << art::x86_64::RSI) | (1 << art::x86_64::RDI) | - (1 << art::x86_64::R8) | (1 << art::x86_64::R9) | (1 << art::x86_64::R10) | - (1 << art::x86_64::R11) | (1 << art::x86_64::R12) | (1 << art::x86_64::R13) | - (1 << art::x86_64::R14) | (1 << art::x86_64::R15); + (1 << art::x86_64::RBX) | (1 << art::x86_64::RBP) | (1 << art::x86_64::R12) | + (1 << art::x86_64::R13) | (1 << art::x86_64::R14) | (1 << art::x86_64::R15); uint32_t arg_spills = - (1 << art::x86_64::RCX) | (1 << art::x86_64::RDX) | (1 << art::x86_64::RBX); + (1 << art::x86_64::RSI) | (1 << art::x86_64::RDX) | (1 << art::x86_64::RCX) | + (1 << art::x86_64::R8) | (1 << art::x86_64::R9); uint32_t core_spills = ref_spills | (type == kRefsAndArgs ? arg_spills : 0) | (1 << art::x86::kNumberOfCpuRegisters); // fake return address callee save size_t frame_size = RoundUp((__builtin_popcount(core_spills) /* gprs */ + diff --git a/runtime/thread.cc b/runtime/thread.cc index 6da9c1c095..9797a48ed2 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -179,7 +179,7 @@ void* Thread::CreateCallback(void* arg) { JValue result; ArgArray arg_array(nullptr, 0); arg_array.Append(receiver); - m->Invoke(self, arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'V'); + m->Invoke(self, arg_array.GetArray(), arg_array.GetNumBytes(), &result, "V"); } // Detach and delete self. Runtime::Current()->GetThreadList()->Unregister(self); @@ -1531,6 +1531,7 @@ void Thread::ThrowNewWrappedException(const ThrowLocation& throw_location, // Choose an appropriate constructor and set up the arguments. const char* signature; + const char* shorty; SirtRef<mirror::String> msg_string(this, nullptr); if (msg != nullptr) { // Ensure we remember this and the method over the String allocation. @@ -1540,14 +1541,18 @@ void Thread::ThrowNewWrappedException(const ThrowLocation& throw_location, return; } if (cause.get() == nullptr) { + shorty = "VL"; signature = "(Ljava/lang/String;)V"; } else { + shorty = "VLL"; signature = "(Ljava/lang/String;Ljava/lang/Throwable;)V"; } } else { if (cause.get() == nullptr) { + shorty = "V"; signature = "()V"; } else { + shorty = "VL"; signature = "(Ljava/lang/Throwable;)V"; } } @@ -1571,7 +1576,7 @@ void Thread::ThrowNewWrappedException(const ThrowLocation& throw_location, throw_location.GetDexPc()); SetException(gc_safe_throw_location, exception.get()); } else { - ArgArray args("VLL", 3); + ArgArray args(shorty, strlen(shorty)); args.Append(exception.get()); if (msg != nullptr) { args.Append(msg_string.get()); @@ -1580,7 +1585,7 @@ void Thread::ThrowNewWrappedException(const ThrowLocation& throw_location, args.Append(cause.get()); } JValue result; - exception_init_method->Invoke(this, args.GetArray(), args.GetNumBytes(), &result, 'V'); + exception_init_method->Invoke(this, args.GetArray(), args.GetNumBytes(), &result, shorty); if (LIKELY(!IsExceptionPending())) { ThrowLocation gc_safe_throw_location(saved_throw_this.get(), saved_throw_method.get(), throw_location.GetDexPc()); diff --git a/tools/Android.mk b/tools/Android.mk new file mode 100644 index 0000000000..6c385dcb45 --- /dev/null +++ b/tools/Android.mk @@ -0,0 +1,32 @@ +# +# Copyright (C) 2014 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. +# + +LOCAL_PATH := $(call my-dir) + +ifeq ($(WITH_HOST_DALVIK),true) +# Copy the art shell script to the host's bin directory +include $(CLEAR_VARS) +LOCAL_IS_HOST_MODULE := true +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_MODULE := art +include $(BUILD_SYSTEM)/base_rules.mk +$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/art $(ACP) + @echo "Copy: $(PRIVATE_MODULE) ($@)" + $(copy-file-to-new-target) + $(hide) chmod 755 $@ + +endif @@ -34,8 +34,22 @@ while true; do done unset ANDROID_PRODUCT_OUT # avoid defaulting dex2oat --host-prefix to target output + +function follow_links() { + file="$1" + while [ -h "$file" ]; do + # On Mac OS, readlink -f doesn't work. + file="$(readlink "$file")" + done + echo "$file" +} + +PROG_NAME="$(follow_links "$BASH_SOURCE")" +PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)" +ANDROID_BUILD_TOP="$(cd "${PROG_DIR}/../../../../" ; pwd -P)/" +ANDROID_HOST_OUT=$PROG_DIR/.. + mkdir -p /tmp/android-data/dalvik-cache -cd $ANDROID_BUILD_TOP ANDROID_DATA=/tmp/android-data \ ANDROID_ROOT=$ANDROID_HOST_OUT \ LD_LIBRARY_PATH=$ANDROID_HOST_OUT/lib \ |