diff options
40 files changed, 1012 insertions, 426 deletions
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index 5fa4596055..35d193c65c 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -1257,17 +1257,13 @@ bool Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { } else { LoadValueDirectFixed(rl_start, reg_start); } - int r_tgt = (cu_->instruction_set != kX86) ? LoadHelper(QUICK_ENTRYPOINT_OFFSET(pIndexOf)) : 0; + int r_tgt = LoadHelper(QUICK_ENTRYPOINT_OFFSET(pIndexOf)); GenNullCheck(rl_obj.s_reg_low, reg_ptr, info->opt_flags); LIR* launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info)); intrinsic_launchpads_.Insert(launch_pad); OpCmpImmBranch(kCondGt, reg_char, 0xFFFF, launch_pad); // NOTE: not a safepoint - if (cu_->instruction_set != kX86) { - OpReg(kOpBlx, r_tgt); - } else { - OpThreadMem(kOpBlx, QUICK_ENTRYPOINT_OFFSET(pIndexOf)); - } + OpReg(kOpBlx, r_tgt); LIR* resume_tgt = NewLIR0(kPseudoTargetLabel); launch_pad->operands[2] = WrapPointer(resume_tgt); // Record that we've already inlined & null checked diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index c60c394d61..6e97c531cd 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -676,7 +676,7 @@ class Mir2Lir : public Backend { bool GenInlinedAbsDouble(CallInfo* info); bool GenInlinedFloatCvt(CallInfo* info); bool GenInlinedDoubleCvt(CallInfo* info); - bool GenInlinedIndexOf(CallInfo* info, bool zero_based); + virtual bool GenInlinedIndexOf(CallInfo* info, bool zero_based); bool GenInlinedStringCompareTo(CallInfo* info); bool GenInlinedCurrentThread(CallInfo* info); bool GenInlinedUnsafeGet(CallInfo* info, bool is_long, bool is_volatile); diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc index 6481589f17..538ce0d183 100644 --- a/compiler/dex/quick/x86/assemble_x86.cc +++ b/compiler/dex/quick/x86/assemble_x86.cc @@ -175,6 +175,8 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, { kX86Mov32AI, kArrayImm, IS_STORE | IS_QUIN_OP | REG_USE01, { 0, 0, 0xC7, 0, 0, 0, 0, 4 }, "Mov32AI", "[!0r+!1r<<!2d+!3d],!4d" }, { kX86Mov32TI, kThreadImm, IS_STORE | IS_BINARY_OP, { THREAD_PREFIX, 0, 0xC7, 0, 0, 0, 0, 4 }, "Mov32TI", "fs:[!0d],!1d" }, + { kX86Lea32RM, kRegMem, IS_TERTIARY_OP | IS_LOAD | REG_DEF0_USE12, { 0, 0, 0x8D, 0, 0, 0, 0, 0 }, "Lea32RM", "!0r,[!1r+!2d]" }, + { kX86Lea32RA, kRegArray, IS_QUIN_OP | REG_DEF0_USE12, { 0, 0, 0x8D, 0, 0, 0, 0, 0 }, "Lea32RA", "!0r,[!1r+!2r<<!3d+!4d]" }, { kX86Cmov32RRC, kRegRegCond, IS_TERTIARY_OP | REG_DEF0_USE01 | USES_CCODES, {0, 0, 0x0F, 0x40, 0, 0, 0, 0}, "Cmovcc32RR", "!2c !0r,!1r" }, @@ -354,6 +356,7 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, { kX86Jmp8, kJmp, IS_UNARY_OP | IS_BRANCH | NEEDS_FIXUP, { 0, 0, 0xEB, 0, 0, 0, 0, 0 }, "Jmp8", "!0t" }, { kX86Jmp32, kJmp, IS_UNARY_OP | IS_BRANCH | NEEDS_FIXUP, { 0, 0, 0xE9, 0, 0, 0, 0, 0 }, "Jmp32", "!0t" }, { kX86JmpR, kJmp, IS_UNARY_OP | IS_BRANCH | REG_USE0, { 0, 0, 0xFF, 0, 0, 4, 0, 0 }, "JmpR", "!0r" }, + { kX86Jecxz8, kJmp, NO_OPERAND | IS_BRANCH | NEEDS_FIXUP | REG_USEC, { 0, 0, 0xE3, 0, 0, 0, 0, 0 }, "Jecxz", "!0t" }, { kX86CallR, kCall, IS_UNARY_OP | IS_BRANCH | REG_USE0, { 0, 0, 0xE8, 0, 0, 0, 0, 0 }, "CallR", "!0r" }, { kX86CallM, kCall, IS_BINARY_OP | IS_BRANCH | IS_LOAD | REG_USE0, { 0, 0, 0xFF, 0, 0, 2, 0, 0 }, "CallM", "[!0r+!1d]" }, { kX86CallA, kCall, IS_QUAD_OP | IS_BRANCH | IS_LOAD | REG_USE01, { 0, 0, 0xFF, 0, 0, 2, 0, 0 }, "CallA", "[!0r+!1r<<!2d+!3d]" }, @@ -364,6 +367,7 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, { kX86StartOfMethod, kMacro, IS_UNARY_OP | SETS_CCODES, { 0, 0, 0, 0, 0, 0, 0, 0 }, "StartOfMethod", "!0r" }, { kX86PcRelLoadRA, kPcRel, IS_LOAD | IS_QUIN_OP | REG_DEF0_USE12, { 0, 0, 0x8B, 0, 0, 0, 0, 0 }, "PcRelLoadRA", "!0r,[!1r+!2r<<!3d+!4p]" }, { kX86PcRelAdr, kPcRel, IS_LOAD | IS_BINARY_OP | REG_DEF0, { 0, 0, 0xB8, 0, 0, 0, 0, 4 }, "PcRelAdr", "!0r,!1d" }, + { kX86RepneScasw, kPrefix2Nullary, NO_OPERAND | SETS_CCODES, { 0x66, 0xF2, 0xAF, 0, 0, 0, 0, 0 }, "RepNE ScasW", "" }, }; static size_t ComputeSize(const X86EncodingMap* entry, int base, int displacement, bool has_sib) { @@ -407,6 +411,8 @@ int X86Mir2Lir::GetInsnSize(LIR* lir) { return lir->operands[0]; // length of nop is sole operand case kNullary: return 1; // 1 byte of opcode + case kPrefix2Nullary: + return 3; // 1 byte of opcode + 2 prefixes case kRegOpcode: // lir operands - 0: reg return ComputeSize(entry, 0, 0, false) - 1; // substract 1 for modrm case kReg: // lir operands - 0: reg @@ -489,7 +495,7 @@ int X86Mir2Lir::GetInsnSize(LIR* lir) { return 6; // 2 byte opcode + rel32 } case kJmp: - if (lir->opcode == kX86Jmp8) { + if (lir->opcode == kX86Jmp8 || lir->opcode == kX86Jecxz8) { return 2; // opcode + rel8 } else if (lir->opcode == kX86Jmp32) { return 5; // opcode + rel32 @@ -957,6 +963,10 @@ void X86Mir2Lir::EmitJmp(const X86EncodingMap* entry, int rel) { code_buffer_.push_back((rel >> 8) & 0xFF); code_buffer_.push_back((rel >> 16) & 0xFF); code_buffer_.push_back((rel >> 24) & 0xFF); + } else if (entry->opcode == kX86Jecxz8) { + DCHECK(IS_SIMM8(rel)); + code_buffer_.push_back(0xE3); + code_buffer_.push_back(rel & 0xFF); } else { DCHECK(entry->opcode == kX86JmpR); code_buffer_.push_back(entry->skeleton.opcode); @@ -1148,6 +1158,17 @@ AssemblerStatus X86Mir2Lir::AssembleInstructions(CodeOffset start_addr) { lir->operands[0] = delta; break; } + case kX86Jecxz8: { + LIR *target_lir = lir->target; + DCHECK(target_lir != NULL); + CodeOffset pc; + pc = lir->offset + 2; // opcode + rel8 + CodeOffset target = target_lir->offset; + int delta = target - pc; + lir->operands[0] = delta; + DCHECK(IS_SIMM8(delta)); + break; + } case kX86Jmp8: { LIR *target_lir = lir->target; DCHECK(target_lir != NULL); @@ -1226,6 +1247,14 @@ AssemblerStatus X86Mir2Lir::AssembleInstructions(CodeOffset start_addr) { DCHECK_EQ(0, entry->skeleton.ax_opcode); DCHECK_EQ(0, entry->skeleton.immediate_bytes); break; + case kPrefix2Nullary: // 1 byte of opcode + 2 prefixes. + DCHECK_NE(0, entry->skeleton.prefix1); + DCHECK_NE(0, entry->skeleton.prefix2); + EmitPrefixAndOpcode(entry); + DCHECK_EQ(0, entry->skeleton.modrm_opcode); + DCHECK_EQ(0, entry->skeleton.ax_opcode); + DCHECK_EQ(0, entry->skeleton.immediate_bytes); + break; case kRegOpcode: // lir operands - 0: reg EmitOpRegOpcode(entry, lir->operands[0]); break; diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h index 6100a1d158..421d51e4fd 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -359,6 +359,15 @@ class X86Mir2Lir : public Mir2Lir { void GenConstWide(RegLocation rl_dest, int64_t value); /* + * @brief generate inline code for fast case of Strng.indexOf. + * @param info Call parameters + * @param zero_based 'true' if the index into the string is 0. + * @returns 'true' if the call was inlined, 'false' if a regular call needs to be + * generated. + */ + bool GenInlinedIndexOf(CallInfo* info, bool zero_based); + + /* * @brief Return the correct x86 opcode for the Dex operation * @param op Dex opcode for the operation * @param loc Register location of the operand diff --git a/compiler/dex/quick/x86/target_x86.cc b/compiler/dex/quick/x86/target_x86.cc index fa9a9447de..ad5b154388 100644 --- a/compiler/dex/quick/x86/target_x86.cc +++ b/compiler/dex/quick/x86/target_x86.cc @@ -185,6 +185,14 @@ void X86Mir2Lir::SetupTargetResourceMasks(LIR* lir, uint64_t flags) { if (flags & REG_USEB) { SetupRegMask(&lir->u.m.use_mask, rBX); } + + // Fixup hard to describe instruction: Uses rAX, rCX, rDI; sets rDI. + if (lir->opcode == kX86RepneScasw) { + SetupRegMask(&lir->u.m.use_mask, rAX); + SetupRegMask(&lir->u.m.use_mask, rCX); + SetupRegMask(&lir->u.m.use_mask, rDI); + SetupRegMask(&lir->u.m.def_mask, rDI); + } } /* For dumping instructions */ @@ -936,4 +944,174 @@ void X86Mir2Lir::InstallLiteralPools() { Mir2Lir::InstallLiteralPools(); } +// Offsets within java.lang.String. +#define STRING_VALUE_OFFSET 8 +#define STRING_COUNT_OFFSET 12 +#define STRING_OFFSET_OFFSET 20 +#define STRING_DATA_OFFSET 12 + +/* + * Fast string.index_of(I) & (II). Inline check for simple case of char <= 0xffff, + * otherwise bails to standard library code. + */ +bool X86Mir2Lir::GenInlinedIndexOf(CallInfo* info, bool zero_based) { + ClobberCallerSave(); + LockCallTemps(); // Using fixed registers + + // EAX: 16 bit character being searched. + // ECX: count: number of words to be searched. + // EDI: String being searched. + // EDX: temporary during execution. + // EBX: temporary during execution. + + RegLocation rl_obj = info->args[0]; + RegLocation rl_char = info->args[1]; + RegLocation rl_start = info->args[2]; + + uint32_t char_value = + rl_char.is_const ? mir_graph_->ConstantValue(rl_char.orig_sreg) : 0; + + if (char_value > 0xFFFF) { + // We have to punt to the real String.indexOf. + return false; + } + + // Okay, we are commited to inlining this. + RegLocation rl_return = GetReturn(false); + RegLocation rl_dest = InlineTarget(info); + + // Is the string non-NULL? + LoadValueDirectFixed(rl_obj, rDX); + GenNullCheck(rl_obj.s_reg_low, rDX, info->opt_flags); + + // Record that we have inlined & null checked the object. + info->opt_flags |= (MIR_INLINED | MIR_IGNORE_NULL_CHECK); + + // Does the character fit in 16 bits? + LIR* launch_pad = nullptr; + if (rl_char.is_const) { + // We need the value in EAX. + LoadConstantNoClobber(rAX, char_value); + } else { + // Character is not a constant; compare at runtime. + LoadValueDirectFixed(rl_char, rAX); + launch_pad = RawLIR(0, kPseudoIntrinsicRetry, WrapPointer(info)); + intrinsic_launchpads_.Insert(launch_pad); + OpCmpImmBranch(kCondGt, rAX, 0xFFFF, launch_pad); + } + + // From here down, we know that we are looking for a char that fits in 16 bits. + + // Character is in EAX. + // Object pointer is in EDX. + + // We need to preserve EDI, but have no spare registers, so push it on the stack. + // We have to remember that all stack addresses after this are offset by sizeof(EDI). + NewLIR1(kX86Push32R, rDI); + + // Compute the number of words to search in to rCX. + LoadWordDisp(rDX, STRING_COUNT_OFFSET, rCX); + LIR *length_compare = nullptr; + int start_value = 0; + if (zero_based) { + // We have to handle an empty string. Use special instruction JECXZ. + length_compare = NewLIR0(kX86Jecxz8); + } else { + // We have to offset by the start index. + if (rl_start.is_const) { + start_value = mir_graph_->ConstantValue(rl_start.orig_sreg); + start_value = std::max(start_value, 0); + + // Is the start > count? + length_compare = OpCmpImmBranch(kCondLe, rCX, start_value, nullptr); + + if (start_value != 0) { + OpRegImm(kOpSub, rCX, start_value); + } + } else { + // Runtime start index. + rl_start = UpdateLoc(rl_start); + if (rl_start.location == kLocPhysReg) { + length_compare = OpCmpBranch(kCondLe, rCX, rl_start.low_reg, nullptr); + OpRegReg(kOpSub, rCX, rl_start.low_reg); + } else { + // Compare to memory to avoid a register load. Handle pushed EDI. + int displacement = SRegOffset(rl_start.s_reg_low) + sizeof(uint32_t); + OpRegMem(kOpCmp, rDX, rX86_SP, displacement); + length_compare = NewLIR2(kX86Jcc8, 0, kX86CondLe); + OpRegMem(kOpSub, rCX, rX86_SP, displacement); + } + } + } + DCHECK(length_compare != nullptr); + + // ECX now contains the count in words to be searched. + + // Load the address of the string into EBX. + // The string starts at VALUE(String) + 2 * OFFSET(String) + STRING_DATA_OFFSET. + LoadWordDisp(rDX, STRING_VALUE_OFFSET, rDI); + LoadWordDisp(rDX, STRING_OFFSET_OFFSET, rBX); + OpLea(rBX, rDI, rBX, 1, STRING_DATA_OFFSET); + + // Now compute into EDI where the search will start. + if (zero_based || rl_start.is_const) { + if (start_value == 0) { + OpRegCopy(rDI, rBX); + } else { + NewLIR3(kX86Lea32RM, rDI, rBX, 2 * start_value); + } + } else { + if (rl_start.location == kLocPhysReg) { + if (rl_start.low_reg == rDI) { + // We have a slight problem here. We are already using RDI! + // Grab the value from the stack. + LoadWordDisp(rX86_SP, 0, rDX); + OpLea(rDI, rBX, rDX, 1, 0); + } else { + OpLea(rDI, rBX, rl_start.low_reg, 1, 0); + } + } else { + OpRegCopy(rDI, rBX); + // Load the start index from stack, remembering that we pushed EDI. + int displacement = SRegOffset(rl_start.s_reg_low) + sizeof(uint32_t); + LoadWordDisp(rX86_SP, displacement, rDX); + OpLea(rDI, rBX, rDX, 1, 0); + } + } + + // EDI now contains the start of the string to be searched. + // We are all prepared to do the search for the character. + NewLIR0(kX86RepneScasw); + + // Did we find a match? + LIR* failed_branch = OpCondBranch(kCondNe, nullptr); + + // yes, we matched. Compute the index of the result. + // index = ((curr_ptr - orig_ptr) / 2) - 1. + OpRegReg(kOpSub, rDI, rBX); + OpRegImm(kOpAsr, rDI, 1); + NewLIR3(kX86Lea32RM, rl_return.low_reg, rDI, -1); + LIR *all_done = NewLIR1(kX86Jmp8, 0); + + // Failed to match; return -1. + LIR *not_found = NewLIR0(kPseudoTargetLabel); + length_compare->target = not_found; + failed_branch->target = not_found; + LoadConstantNoClobber(rl_return.low_reg, -1); + + // And join up at the end. + all_done->target = NewLIR0(kPseudoTargetLabel); + // Restore EDI from the stack. + NewLIR1(kX86Pop32R, rDI); + + // Out of line code returns here. + if (launch_pad != nullptr) { + LIR *return_point = NewLIR0(kPseudoTargetLabel); + launch_pad->operands[2] = WrapPointer(return_point); + } + + StoreValue(rl_dest, rl_return); + return true; +} + } // namespace art diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h index 480d5f5a45..4064bd6550 100644 --- a/compiler/dex/quick/x86/x86_lir.h +++ b/compiler/dex/quick/x86/x86_lir.h @@ -277,6 +277,7 @@ enum X86OpCode { kX86Mov32MR, kX86Mov32AR, kX86Mov32TR, kX86Mov32RR, kX86Mov32RM, kX86Mov32RA, kX86Mov32RT, kX86Mov32RI, kX86Mov32MI, kX86Mov32AI, kX86Mov32TI, + kX86Lea32RM, kX86Lea32RA, // RRC - Register Register ConditionCode - cond_opcode reg1, reg2 // - lir operands - 0: reg1, 1: reg2, 2: CC @@ -384,6 +385,7 @@ enum X86OpCode { kX86Jcc8, kX86Jcc32, // jCC rel8/32; lir operands - 0: rel, 1: CC, target assigned kX86Jmp8, kX86Jmp32, // jmp rel8/32; lir operands - 0: rel, target assigned kX86JmpR, // jmp reg; lir operands - 0: reg + kX86Jecxz8, // jcexz rel8; jump relative if ECX is zero. kX86CallR, // call reg; lir operands - 0: reg kX86CallM, // call [base + disp]; lir operands - 0: base, 1: disp kX86CallA, // call [base + index * scale + disp] @@ -396,6 +398,7 @@ enum X86OpCode { kX86PcRelLoadRA, // mov reg, [base + index * scale + PC relative displacement] // lir operands - 0: reg, 1: base, 2: index, 3: scale, 4: table kX86PcRelAdr, // mov reg, PC relative displacement; lir operands - 0: reg, 1: table + kX86RepneScasw, // repne scasw kX86Last }; @@ -404,6 +407,7 @@ enum X86EncodingKind { kData, // Special case for raw data. kNop, // Special case for variable length nop. kNullary, // Opcode that takes no arguments. + kPrefix2Nullary, // Opcode that takes no arguments, but 2 prefixes. kRegOpcode, // Shorter form of R instruction kind (opcode+rd) kReg, kMem, kArray, // R, M and A instruction kinds. kMemReg, kArrayReg, kThreadReg, // MR, AR and TR instruction kinds. diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index dab419f721..ce339bfc51 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1043,6 +1043,21 @@ static int dex2oat(int argc, char** argv) { } } + const bool kSaveDexInput = false; + if (kSaveDexInput) { + for (size_t i = 0; i < dex_files.size(); ++i) { + const DexFile* dex_file = dex_files[i]; + std::string tmp_file_name(StringPrintf("/data/local/tmp/dex2oat.%d.%d.dex", getpid(), i)); + UniquePtr<File> tmp_file(OS::CreateEmptyFile(tmp_file_name.c_str())); + if (tmp_file.get() == nullptr) { + PLOG(ERROR) << "Failed to open file " << tmp_file_name << ". Try: adb shell chmod 777 /data/local/tmp"; + continue; + } + tmp_file->WriteFully(dex_file->Begin(), dex_file->Size()); + LOG(INFO) << "Wrote input to " << tmp_file_name; + } + } + // Ensure opened dex files are writable for dex-to-dex transformations. for (const auto& dex_file : dex_files) { if (!dex_file->EnableWrite()) { diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc index b6ddc95769..d86ba7b77a 100644 --- a/disassembler/disassembler_x86.cc +++ b/disassembler/disassembler_x86.cc @@ -633,6 +633,9 @@ DISASSEMBLER_ENTRY(cmp, case 0x99: opcode << "cdq"; break; + case 0xAF: + opcode << (prefix[2] == 0x66 ? "scasw" : "scasl"); + break; case 0xB0: case 0xB1: case 0xB2: case 0xB3: case 0xB4: case 0xB5: case 0xB6: case 0xB7: opcode << "mov"; immediate_bytes = 1; @@ -693,6 +696,7 @@ DISASSEMBLER_ENTRY(cmp, has_modrm = true; reg_is_opcode = true; break; + case 0xE3: opcode << "jecxz"; branch_bytes = 1; break; case 0xE8: opcode << "call"; branch_bytes = 4; break; case 0xE9: opcode << "jmp"; branch_bytes = 4; break; case 0xEB: opcode << "jmp"; branch_bytes = 1; break; diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 53b07f9d12..2a0d8265ae 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -808,6 +808,10 @@ class ImageDumper { { WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); heap->FlushAllocStack(); + // Since FlushAllocStack() above resets the (active) allocation + // stack. Need to revoke the thread-local allocation stacks that + // point into it. + heap->RevokeAllThreadLocalAllocationStacks(self); } { std::ostream* saved_os = os_; diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc index 589c7d9dc9..888310ac51 100644 --- a/runtime/arch/x86/entrypoints_init_x86.cc +++ b/runtime/arch/x86/entrypoints_init_x86.cc @@ -84,7 +84,6 @@ extern "C" uint64_t art_quick_lushr(uint64_t, uint32_t); // Intrinsic entrypoints. extern "C" int32_t art_quick_memcmp16(void*, void*, int32_t); -extern "C" int32_t art_quick_indexof(void*, uint32_t, uint32_t, uint32_t); extern "C" int32_t art_quick_string_compareto(void*, void*); extern "C" void* art_quick_memcpy(void*, const void*, size_t); @@ -193,7 +192,7 @@ void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, qpoints->pUshrLong = art_quick_lushr; // Intrinsics - qpoints->pIndexOf = art_quick_indexof; + // qpoints->pIndexOf = nullptr; // Not needed on x86 qpoints->pMemcmp16 = art_quick_memcmp16; qpoints->pStringCompareTo = art_quick_string_compareto; qpoints->pMemcpy = art_quick_memcpy; diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index c76c6b269b..7597a4e276 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -1277,56 +1277,6 @@ DEFINE_FUNCTION art_quick_deoptimize END_FUNCTION art_quick_deoptimize /* - * String's indexOf. - * - * On entry: - * eax: string object (known non-null) - * ecx: char to match (known <= 0xFFFF) - * edx: Starting offset in string data - */ -DEFINE_FUNCTION art_quick_indexof - PUSH edi // push callee save reg - mov STRING_COUNT_OFFSET(%eax), %ebx - mov STRING_VALUE_OFFSET(%eax), %edi - mov STRING_OFFSET_OFFSET(%eax), %eax - testl %edx, %edx // check if start < 0 - jl clamp_min -clamp_done: - cmpl %ebx, %edx // check if start >= count - jge not_found - lea STRING_DATA_OFFSET(%edi, %eax, 2), %edi // build a pointer to the start of string data - mov %edi, %eax // save a copy in eax to later compute result - lea (%edi, %edx, 2), %edi // build pointer to start of data to compare - subl %edx, %ebx // compute iteration count - /* - * At this point we have: - * eax: original start of string data - * ecx: char to compare - * ebx: length to compare - * edi: start of data to test - */ - mov %eax, %edx - mov %ecx, %eax // put char to match in %eax - mov %ebx, %ecx // put length to compare in %ecx - repne scasw // find %ax, starting at [%edi], up to length %ecx - jne not_found - subl %edx, %edi - sar LITERAL(1), %edi - decl %edi // index = ((curr_ptr - orig_ptr) / 2) - 1 - mov %edi, %eax - POP edi // pop callee save reg - ret - .balign 16 -not_found: - mov LITERAL(-1), %eax // return -1 (not found) - POP edi // pop callee save reg - ret -clamp_min: - xor %edx, %edx // clamp start to 0 - jmp clamp_done -END_FUNCTION art_quick_indexof - - /* * String's compareTo. * * On entry: diff --git a/runtime/arch/x86_64/asm_support_x86_64.h b/runtime/arch/x86_64/asm_support_x86_64.h index d425ed8c81..444fa22a22 100644 --- a/runtime/arch/x86_64/asm_support_x86_64.h +++ b/runtime/arch/x86_64/asm_support_x86_64.h @@ -19,6 +19,13 @@ #include "asm_support.h" +// Offset of field Runtime::callee_save_methods_[kSaveAll] +#define RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET 208 +// Offset of field Runtime::callee_save_methods_[kRefsOnly] +#define RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET 216 +// Offset of field Runtime::callee_save_methods_[kRefsAndArgs] +#define RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET 224 + // Offset of field Thread::self_ verified in InitCpu #define THREAD_SELF_OFFSET 72 // Offset of field Thread::card_table_ verified in InitCpu diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index 44bc7a20af..ac238f0d50 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -46,12 +46,63 @@ END_MACRO * Runtime::CreateCalleeSaveMethod(kRefsAndArgs) */ MACRO0(SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME) - int3 - int3 + // R10 := Runtime::Current() + movq _ZN3art7Runtime9instance_E@GOTPCREL(%rip), %r10 + movq (%r10), %r10 + // Save callee and GPR args, mixed together to agree with core spills bitmap. + PUSH r15 // Callee save. + PUSH r14 // Callee save. + PUSH r13 // Callee save. + PUSH r12 // Callee save. + PUSH r9 // Arg. + PUSH r8 // Arg. + PUSH rsi // Arg. + PUSH rbp // Callee save. + PUSH rbx // Callee save. + PUSH rdx // Arg. + PUSH rcx // Arg. + // Create space for FPR args and create 2 slots, 1 of padding and 1 for the ArtMethod*. + subq LITERAL(80), %rsp + CFI_ADJUST_CFA_OFFSET(80) + // R10 := ArtMethod* for ref and args callee save frame method. + movq RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET(%r10), %r10 + // Save FPRs. + movq %xmm0, 16(%rsp) + movq %xmm1, 24(%rsp) + movq %xmm2, 32(%rsp) + movq %xmm3, 40(%rsp) + movq %xmm4, 48(%rsp) + movq %xmm5, 56(%rsp) + movq %xmm6, 64(%rsp) + movq %xmm7, 72(%rsp) + // Store ArtMethod* to bottom of stack. + movq %r10, 0(%rsp) END_MACRO MACRO0(RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME) - int3 + // Restore FPRs. + movq 16(%rsp), %xmm0 + movq 24(%rsp), %xmm1 + movq 32(%rsp), %xmm2 + movq 40(%rsp), %xmm3 + movq 48(%rsp), %xmm4 + movq 56(%rsp), %xmm5 + movq 64(%rsp), %xmm6 + movq 72(%rsp), %xmm7 + addq LITERAL(80), %rsp + CFI_ADJUST_CFA_OFFSET(-80) + // Save callee and GPR args, mixed together to agree with core spills bitmap. + POP rcx + POP rdx + POP rbx + POP rbp + POP rsi + POP r8 + POP r9 + POP r12 + POP r13 + POP r14 + POP r15 END_MACRO /* @@ -147,20 +198,210 @@ INVOKE_TRAMPOLINE art_quick_invoke_direct_trampoline_with_access_check, artInvok INVOKE_TRAMPOLINE art_quick_invoke_super_trampoline_with_access_check, artInvokeSuperTrampolineWithAccessCheck INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvokeVirtualTrampolineWithAccessCheck + + /* + * Helper for quick invocation stub to set up XMM registers. Assumes r10 == shorty, + * r11 == arg_array. Clobbers r10, r11 and al. Branches to xmm_setup_finished if it encounters + * the end of the shorty. + */ +MACRO2(LOOP_OVER_SHORTY_LOADING_XMMS, xmm_reg, finished) +1: // LOOP + movb (%r10), %al // al := *shorty + addq LITERAL(1), %r10 // shorty++ + cmpb LITERAL(0), %al // if (al == '\0') goto xmm_setup_finished + je VAR(finished, 1) + cmpb LITERAL(68), %al // if (al == 'D') goto FOUND_DOUBLE + je 2f + cmpb LITERAL(70), %al // if (al == 'F') goto FOUND_FLOAT + je 3f + addq LITERAL(4), %r11 // arg_array++ + // Handle extra space in arg array taken by a long. + cmpb LITERAL(74), %al // if (al != 'J') goto LOOP + jne 1b + addq LITERAL(4), %r11 // arg_array++ + jmp 1b // goto LOOP +2: // FOUND_DOUBLE + movsd (%r11), REG_VAR(xmm_reg, 0) + addq LITERAL(8), %r11 // arg_array+=2 + jmp 4f +3: // FOUND_FLOAT + movss (%r11), REG_VAR(xmm_reg, 0) + addq LITERAL(4), %r11 // arg_array++ +4: +END_MACRO + + /* + * Helper for quick invocation stub to set up GPR registers. Assumes r10 == shorty, + * r11 == arg_array. Clobbers r10, r11 and al. Branches to gpr_setup_finished if it encounters + * the end of the shorty. + */ +MACRO3(LOOP_OVER_SHORTY_LOADING_GPRS, gpr_reg64, gpr_reg32, finished) +1: // LOOP + movb (%r10), %al // al := *shorty + addq LITERAL(1), %r10 // shorty++ + cmpb LITERAL(0), %al // if (al == '\0') goto gpr_setup_finished + je VAR(finished, 2) + cmpb LITERAL(74), %al // if (al == 'J') goto FOUND_LONG + je 2f + cmpb LITERAL(70), %al // if (al == 'F') goto SKIP_FLOAT + je 3f + cmpb LITERAL(68), %al // if (al == 'D') goto SKIP_DOUBLE + je 4f + movl (%r11), REG_VAR(gpr_reg32, 1) + addq LITERAL(4), %r11 // arg_array++ + jmp 5f +2: // FOUND_LONG + movq (%r11), REG_VAR(gpr_reg64, 0) + addq LITERAL(8), %r11 // arg_array+=2 + jmp 5f +3: // SKIP_FLOAT + addq LITERAL(4), %r11 // arg_array++ + jmp 1b +4: // SKIP_DOUBLE + addq LITERAL(8), %r11 // arg_array+=2 + jmp 1b +5: +END_MACRO + /* * Quick invocation stub. * On entry: * [sp] = return address * rdi = method pointer - * rsi = argument array or NULL for no argument methods + * rsi = argument array that must at least contain the this pointer. * rdx = size of argument array in bytes * rcx = (managed) thread pointer * r8 = JValue* result * r9 = char* shorty */ DEFINE_FUNCTION art_quick_invoke_stub - int3 - int3 + // Set up argument XMM registers. + leaq 1(%r9), %r10 // R10 := shorty + 1 ; ie skip return arg character. + leaq 4(%rsi), %r11 // R11 := arg_array + 4 ; ie skip this pointer. + LOOP_OVER_SHORTY_LOADING_XMMS xmm0, .Lxmm_setup_finished + LOOP_OVER_SHORTY_LOADING_XMMS xmm1, .Lxmm_setup_finished + LOOP_OVER_SHORTY_LOADING_XMMS xmm2, .Lxmm_setup_finished + LOOP_OVER_SHORTY_LOADING_XMMS xmm3, .Lxmm_setup_finished + LOOP_OVER_SHORTY_LOADING_XMMS xmm4, .Lxmm_setup_finished + LOOP_OVER_SHORTY_LOADING_XMMS xmm5, .Lxmm_setup_finished + LOOP_OVER_SHORTY_LOADING_XMMS xmm6, .Lxmm_setup_finished + LOOP_OVER_SHORTY_LOADING_XMMS xmm7, .Lxmm_setup_finished + .balign 16 +.Lxmm_setup_finished: + PUSH rbp // Save rbp. + PUSH r8 // Save r8/result*. + PUSH r9 // Save r9/shorty*. + mov %rsp, %rbp // Copy value of stack pointer into base pointer. + CFI_DEF_CFA_REGISTER(rbp) + movl %edx, %r10d + addl LITERAL(64), %edx // Reserve space for return addr, method*, rbp, r8 and r9 in frame. + andl LITERAL(0xFFFFFFF0), %edx // Align frame size to 16 bytes. + subl LITERAL(32), %edx // Remove space for return address, rbp, r8 and r9. + subq %rdx, %rsp // Reserve stack space for argument array. + movq LITERAL(0), (%rsp) // Store NULL for method* + movl %r10d, %ecx // Place size of args in rcx. + movq %rdi, %rax // RAX := method to be called + movq %rsi, %r11 // R11 := arg_array + leaq 8(%rsp), %rdi // Rdi is pointing just above the method* in the stack arguments. + // Copy arg array into stack. + rep movsb // while (rcx--) { *rdi++ = *rsi++ } + leaq 1(%r9), %r10 // R10 := shorty + 1 ; ie skip return arg character + movq %rax, %rdi // RDI := method to be called + movl (%r11), %esi // RSI := this pointer + addq LITERAL(4), %r11 // arg_array++ + LOOP_OVER_SHORTY_LOADING_GPRS rdx, edx, .Lgpr_setup_finished + LOOP_OVER_SHORTY_LOADING_GPRS rcx, ecx, .Lgpr_setup_finished + LOOP_OVER_SHORTY_LOADING_GPRS r8, r8d, .Lgpr_setup_finished + LOOP_OVER_SHORTY_LOADING_GPRS r9, r9d, .Lgpr_setup_finished +.Lgpr_setup_finished: + call *METHOD_QUICK_CODE_OFFSET(%rdi) // Call the method. + movq %rbp, %rsp // Restore stack pointer. + CFI_DEF_CFA_REGISTER(rsp) + POP r9 // Pop r9 - shorty*. + POP r8 // Pop r8 - result*. + POP rbp // Pop rbp + cmpb LITERAL(68), (%r9) // Test if result type char == 'D'. + je .Lreturn_double_quick + cmpb LITERAL(70), (%r9) // Test if result type char == 'F'. + je .Lreturn_float_quick + movq %rax, (%r8) // Store the result assuming its a long, int or Object* + ret +.Lreturn_double_quick: + movsd %xmm0, (%r8) // Store the double floating point result. + ret +.Lreturn_float_quick: + movss %xmm0, (%r8) // Store the floating point result. + ret +END_FUNCTION art_quick_invoke_stub + + /* + * Quick invocation stub. + * On entry: + * [sp] = return address + * rdi = method pointer + * rsi = argument array or NULL if no arguments. + * rdx = size of argument array in bytes + * rcx = (managed) thread pointer + * r8 = JValue* result + * r9 = char* shorty + */ +DEFINE_FUNCTION art_quick_invoke_static_stub + // Set up argument XMM registers. + leaq 1(%r9), %r10 // R10 := shorty + 1 ; ie skip return arg character + movq %rsi, %r11 // R11 := arg_array + LOOP_OVER_SHORTY_LOADING_XMMS xmm0, .Lxmm_setup_finished2 + LOOP_OVER_SHORTY_LOADING_XMMS xmm1, .Lxmm_setup_finished2 + LOOP_OVER_SHORTY_LOADING_XMMS xmm2, .Lxmm_setup_finished2 + LOOP_OVER_SHORTY_LOADING_XMMS xmm3, .Lxmm_setup_finished2 + LOOP_OVER_SHORTY_LOADING_XMMS xmm4, .Lxmm_setup_finished2 + LOOP_OVER_SHORTY_LOADING_XMMS xmm5, .Lxmm_setup_finished2 + LOOP_OVER_SHORTY_LOADING_XMMS xmm6, .Lxmm_setup_finished2 + LOOP_OVER_SHORTY_LOADING_XMMS xmm7, .Lxmm_setup_finished2 + .balign 16 +.Lxmm_setup_finished2: + PUSH rbp // Save rbp. + PUSH r8 // Save r8/result*. + PUSH r9 // Save r9/shorty*. + mov %rsp, %rbp // Copy value of stack pointer into base pointer. + CFI_DEF_CFA_REGISTER(rbp) + movl %edx, %r10d + addl LITERAL(64), %edx // Reserve space for return addr, method*, rbp, r8 and r9 in frame. + andl LITERAL(0xFFFFFFF0), %edx // Align frame size to 16 bytes. + subl LITERAL(32), %edx // Remove space for return address, rbp, r8 and r9. + subq %rdx, %rsp // Reserve stack space for argument array. + movq LITERAL(0), (%rsp) // Store NULL for method* + movl %r10d, %ecx // Place size of args in rcx. + movq %rdi, %rax // RAX := method to be called + movq %rsi, %r11 // R11 := arg_array + leaq 8(%rsp), %rdi // Rdi is pointing just above the method* in the stack arguments. + // Copy arg array into stack. + rep movsb // while (rcx--) { *rdi++ = *rsi++ } + leaq 1(%r9), %r10 // R10 := shorty + 1 ; ie skip return arg character + movq %rax, %rdi // RDI := method to be called + LOOP_OVER_SHORTY_LOADING_GPRS rsi, esi, .Lgpr_setup_finished2 + LOOP_OVER_SHORTY_LOADING_GPRS rdx, edx, .Lgpr_setup_finished2 + LOOP_OVER_SHORTY_LOADING_GPRS rcx, ecx, .Lgpr_setup_finished2 + LOOP_OVER_SHORTY_LOADING_GPRS r8, r8d, .Lgpr_setup_finished2 + LOOP_OVER_SHORTY_LOADING_GPRS r9, r9d, .Lgpr_setup_finished2 +.Lgpr_setup_finished2: + call *METHOD_QUICK_CODE_OFFSET(%rdi) // Call the method. + movq %rbp, %rsp // Restore stack pointer. + CFI_DEF_CFA_REGISTER(rsp) + POP r9 // Pop r9 - shorty*. + POP r8 // Pop r8 - result*. + POP rbp // Pop rbp + cmpb LITERAL(68), (%r9) // Test if result type char == 'D'. + je .Lreturn_double_quick2 + cmpb LITERAL(70), (%r9) // Test if result type char == 'F'. + je .Lreturn_float_quick2 + movq %rax, (%r8) // Store the result assuming its a long, int or Object* + ret +.Lreturn_double_quick2: + movsd %xmm0, (%r8) // Store the double floating point result. + ret +.Lreturn_float_quick2: + movss %xmm0, (%r8) // Store the floating point result. + ret END_FUNCTION art_quick_invoke_stub MACRO3(NO_ARG_DOWNCALL, c_name, cxx_name, return_macro) @@ -210,8 +451,11 @@ MACRO0(RETURN_IF_EAX_ZERO) END_MACRO MACRO0(RETURN_OR_DELIVER_PENDING_EXCEPTION) - int3 - int3 + movq %gs:THREAD_EXCEPTION_OFFSET, %rcx // get exception field + testq %rcx, %rcx // rcx == 0 ? + jnz 1f // if rcx != 0 goto 1 + ret // return +1: // deliver exception on current thread DELIVER_PENDING_EXCEPTION END_MACRO @@ -390,7 +634,22 @@ UNIMPLEMENTED art_quick_proxy_invoke_handler */ UNIMPLEMENTED art_quick_imt_conflict_trampoline UNIMPLEMENTED art_quick_resolution_trampoline -UNIMPLEMENTED art_quick_to_interpreter_bridge + + /* + * Called to bridge from the quick to interpreter ABI. On entry the arguments match those + * of a quick call: + * RDI = method being called / to bridge to. + * RSI, RDX, RCX, R8, R9 are arguments to that method. + */ +DEFINE_FUNCTION art_quick_to_interpreter_bridge + SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME // Set up frame and save arguments. + movq %gs:THREAD_SELF_OFFSET, %rsi // RSI := Thread::Current() + movq %rsp, %rdx // RDX := sp + call PLT_SYMBOL(artQuickToInterpreterBridge) // (method, Thread*, SP) + RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME // TODO: no need to restore arguments in this case. + movq %rax, %xmm0 // Place return value also into floating point return value. + RETURN_OR_DELIVER_PENDING_EXCEPTION // return or deliver exception +END_FUNCTION art_quick_to_interpreter_bridge /* * Routine that intercepts method calls and returns. diff --git a/runtime/arch/x86_64/registers_x86_64.h b/runtime/arch/x86_64/registers_x86_64.h index 9808d910b9..c1a9942f9d 100644 --- a/runtime/arch/x86_64/registers_x86_64.h +++ b/runtime/arch/x86_64/registers_x86_64.h @@ -48,6 +48,26 @@ enum Register { }; std::ostream& operator<<(std::ostream& os, const Register& rhs); +enum FloatRegister { + XMM0 = 0, + XMM1 = 1, + XMM2 = 2, + XMM3 = 3, + XMM4 = 4, + XMM5 = 5, + XMM6 = 6, + XMM7 = 7, + XMM8 = 8, + XMM9 = 9, + XMM10 = 10, + XMM11 = 11, + XMM12 = 12, + XMM13 = 13, + XMM14 = 14, + XMM15 = 15, +}; +std::ostream& operator<<(std::ostream& os, const FloatRegister& rhs); + } // namespace x86_64 } // namespace art diff --git a/runtime/arch/x86_64/thread_x86_64.cc b/runtime/arch/x86_64/thread_x86_64.cc index 9e45a72e4b..b74fc5d64b 100644 --- a/runtime/arch/x86_64/thread_x86_64.cc +++ b/runtime/arch/x86_64/thread_x86_64.cc @@ -48,6 +48,12 @@ void Thread::InitCpu() { CHECK_EQ(self_check, this); // Sanity check other offsets. + CHECK_EQ(static_cast<size_t>(RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET), + Runtime::GetCalleeSaveMethodOffset(Runtime::kSaveAll)); + CHECK_EQ(static_cast<size_t>(RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET), + Runtime::GetCalleeSaveMethodOffset(Runtime::kRefsOnly)); + CHECK_EQ(static_cast<size_t>(RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET), + Runtime::GetCalleeSaveMethodOffset(Runtime::kRefsAndArgs)); CHECK_EQ(THREAD_EXCEPTION_OFFSET, OFFSETOF_MEMBER(Thread, exception_)); CHECK_EQ(THREAD_CARD_TABLE_OFFSET, OFFSETOF_MEMBER(Thread, card_table_)); CHECK_EQ(THREAD_ID_OFFSET, OFFSETOF_MEMBER(Thread, thin_lock_thread_id_)); diff --git a/runtime/atomic.h b/runtime/atomic.h index 2a47e46137..795f917841 100644 --- a/runtime/atomic.h +++ b/runtime/atomic.h @@ -96,7 +96,7 @@ typedef Atomic<int32_t> AtomicInteger; // quasiatomic operations that are performed on partially-overlapping // memory. class QuasiAtomic { -#if !defined(__arm__) && !defined(__i386__) +#if defined(__mips__) && !defined(__LP64__) static constexpr bool kNeedSwapMutexes = true; #else static constexpr bool kNeedSwapMutexes = false; @@ -141,7 +141,7 @@ class QuasiAtomic { } static void MembarLoadStore() { - #if defined(__arm__) + #if defined(__arm__) || defined(__aarch64__) __asm__ __volatile__("dmb ish" : : : "memory"); #elif defined(__i386__) || defined(__x86_64__) __asm__ __volatile__("" : : : "memory"); @@ -153,7 +153,7 @@ class QuasiAtomic { } static void MembarLoadLoad() { - #if defined(__arm__) + #if defined(__arm__) || defined(__aarch64__) __asm__ __volatile__("dmb ish" : : : "memory"); #elif defined(__i386__) || defined(__x86_64__) __asm__ __volatile__("" : : : "memory"); @@ -165,7 +165,7 @@ class QuasiAtomic { } static void MembarStoreStore() { - #if defined(__arm__) + #if defined(__arm__) || defined(__aarch64__) __asm__ __volatile__("dmb ishst" : : : "memory"); #elif defined(__i386__) || defined(__x86_64__) __asm__ __volatile__("" : : : "memory"); @@ -177,7 +177,7 @@ class QuasiAtomic { } static void MembarStoreLoad() { - #if defined(__arm__) + #if defined(__arm__) || defined(__aarch64__) __asm__ __volatile__("dmb ish" : : : "memory"); #elif defined(__i386__) || defined(__x86_64__) __asm__ __volatile__("mfence" : : : "memory"); diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index 5e2b9ff5ad..a3415d3f70 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -721,7 +721,8 @@ void DexFile::DecodeDebugInfo0(const CodeItem* code_item, bool is_static, uint32 } if (it.HasNext()) { - LOG(ERROR) << "invalid stream - problem with parameter iterator in " << GetLocation(); + LOG(ERROR) << "invalid stream - problem with parameter iterator in " << GetLocation() + << " for method " << PrettyMethod(method_idx, *this); return; } diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 012dabbeef..b3fce5aefa 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -35,10 +35,14 @@ namespace art { // Visits the arguments as saved to the stack by a Runtime::kRefAndArgs callee save frame. class QuickArgumentVisitor { - public: -// Offset to first (not the Method*) argument in a Runtime::kRefAndArgs callee save frame. -// Size of Runtime::kRefAndArgs callee save frame. -// Size of Method* and register parameters in out stack arguments. + // Size of each spilled GPR. +#ifdef __LP64__ + static constexpr size_t kBytesPerGprSpillLocation = 8; +#else + static constexpr size_t kBytesPerGprSpillLocation = 4; +#endif + // Number of bytes for each out register in the caller method's frame. + static constexpr size_t kBytesStackArgLocation = 4; #if defined(__arm__) // The callee save frame is pointed to by SP. // | argN | | @@ -53,12 +57,19 @@ class QuickArgumentVisitor { // | R3 | arg3 // | R2 | arg2 // | R1 | arg1 - // | R0 | + // | R0 | padding // | Method* | <- sp -#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 8 -#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__LR_OFFSET 44 -#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 48 -#define QUICK_STACK_ARG_SKIP 16 + static constexpr bool kSoftFloatAbi = true; // This is a soft float ABI. + static constexpr size_t kNumGprArgs = 3; // 3 arguments passed in GPRs. + static constexpr size_t kNumFprArgs = 0; // 0 arguments passed in FPRs. + static constexpr size_t kBytesPerFprSpillLocation = 4; // FPR spill size is 4 bytes. + static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 0; // Offset of first FPR arg. + static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 8; // Offset of first GPR arg. + static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 44; // Offset of return address. + static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_FrameSize = 48; // Frame size. + static size_t GprIndexToGprOffset(uint32_t gpr_index) { + return gpr_index * kBytesPerGprSpillLocation; + } #elif defined(__mips__) // The callee save frame is pointed to by SP. // | argN | | @@ -74,10 +85,17 @@ class QuickArgumentVisitor { // | A2 | arg2 // | A1 | arg1 // | A0/Method* | <- sp -#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 4 -#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__LR_OFFSET 60 -#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 64 -#define QUICK_STACK_ARG_SKIP 16 + static constexpr bool kSoftFloatAbi = true; // This is a soft float ABI. + static constexpr size_t kNumGprArgs = 3; // 3 arguments passed in GPRs. + static constexpr size_t kNumFprArgs = 0; // 0 arguments passed in FPRs. + static constexpr size_t kBytesPerFprSpillLocation = 4; // FPR spill size is 4 bytes. + static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 0; // Offset of first FPR arg. + static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 4; // Offset of first GPR arg. + static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 60; // Offset of return address. + static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_FrameSize = 64; // Frame size. + static size_t GprIndexToGprOffset(uint32_t gpr_index) { + return gpr_index * kBytesPerGprSpillLocation; + } #elif defined(__i386__) // The callee save frame is pointed to by SP. // | argN | | @@ -93,49 +111,96 @@ class QuickArgumentVisitor { // | EDX | arg2 // | ECX | arg1 // | EAX/Method* | <- sp -#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 4 -#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__LR_OFFSET 28 -#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 32 -#define QUICK_STACK_ARG_SKIP 16 + static constexpr bool kSoftFloatAbi = true; // This is a soft float ABI. + static constexpr size_t kNumGprArgs = 3; // 3 arguments passed in GPRs. + static constexpr size_t kNumFprArgs = 0; // 0 arguments passed in FPRs. + static constexpr size_t kBytesPerFprSpillLocation = 8; // FPR spill size is 8 bytes. + static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 0; // Offset of first FPR arg. + static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 4; // Offset of first GPR arg. + static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 28; // Offset of return address. + static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_FrameSize = 32; // Frame size. + static size_t GprIndexToGprOffset(uint32_t gpr_index) { + return gpr_index * kBytesPerGprSpillLocation; + } #elif defined(__x86_64__) -// TODO: implement and check these. -#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 8 -#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__LR_OFFSET 56 -#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 64 -#define QUICK_STACK_ARG_SKIP 32 + // The callee save frame is pointed to by SP. + // | argN | | + // | ... | | + // | reg. arg spills | | Caller's frame + // | Method* | --- + // | Return | + // | R15 | callee save + // | R14 | callee save + // | R13 | callee save + // | R12 | callee save + // | R9 | arg5 + // | R8 | arg4 + // | RSI/R6 | arg1 + // | RBP/R5 | callee save + // | RBX/R3 | callee save + // | RDX/R2 | arg2 + // | RCX/R1 | arg3 + // | XMM7 | float arg 8 + // | XMM6 | float arg 7 + // | XMM5 | float arg 6 + // | XMM4 | float arg 5 + // | XMM3 | float arg 4 + // | XMM2 | float arg 3 + // | XMM1 | float arg 2 + // | XMM0 | float arg 1 + // | Padding | + // | RDI/Method* | <- sp + static constexpr bool kSoftFloatAbi = false; // This is a hard float ABI. + static constexpr size_t kNumGprArgs = 5; // 3 arguments passed in GPRs. + static constexpr size_t kNumFprArgs = 8; // 0 arguments passed in FPRs. + static constexpr size_t kBytesPerFprSpillLocation = 8; // FPR spill size is 8 bytes. + static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 16; // Offset of first FPR arg. + static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 80; // Offset of first GPR arg. + static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 168; // Offset of return address. + static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_FrameSize = 176; // Frame size. + static size_t GprIndexToGprOffset(uint32_t gpr_index) { + switch (gpr_index) { + case 0: return (4 * kBytesPerGprSpillLocation); + case 1: return (1 * kBytesPerGprSpillLocation); + case 2: return (0 * kBytesPerGprSpillLocation); + case 3: return (5 * kBytesPerGprSpillLocation); + case 4: return (6 * kBytesPerGprSpillLocation); + default: + LOG(FATAL) << "Unexpected GPR index: " << gpr_index; + return 0; + } + } #else #error "Unsupported architecture" -#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 0 -#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__LR_OFFSET 0 -#define QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 0 -#define QUICK_STACK_ARG_SKIP 0 #endif - static mirror::ArtMethod* GetCallingMethod(mirror::ArtMethod** sp) { - byte* previous_sp = reinterpret_cast<byte*>(sp) + - QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE; + public: + static mirror::ArtMethod* GetCallingMethod(mirror::ArtMethod** sp) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK((*sp)->IsCalleeSaveMethod()); + byte* previous_sp = reinterpret_cast<byte*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_FrameSize; return *reinterpret_cast<mirror::ArtMethod**>(previous_sp); } - static uintptr_t GetCallingPc(mirror::ArtMethod** sp) { - byte* lr = reinterpret_cast<byte*>(sp) + QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__LR_OFFSET; + // For the given quick ref and args quick frame, return the caller's PC. + static uintptr_t GetCallingPc(mirror::ArtMethod** sp) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK((*sp)->IsCalleeSaveMethod()); + byte* lr = reinterpret_cast<byte*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_LrOffset; return *reinterpret_cast<uintptr_t*>(lr); } QuickArgumentVisitor(mirror::ArtMethod** sp, bool is_static, const char* shorty, uint32_t shorty_len) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : - is_static_(is_static), shorty_(shorty), shorty_len_(shorty_len), - args_in_regs_(ComputeArgsInRegs(is_static, shorty, shorty_len)), - num_params_((is_static ? 0 : 1) + shorty_len - 1), // +1 for this, -1 for return type - reg_args_(reinterpret_cast<byte*>(sp) + QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET), - stack_args_(reinterpret_cast<byte*>(sp) + QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE - + QUICK_STACK_ARG_SKIP), - cur_args_(reg_args_), - cur_arg_index_(0), - param_index_(0), - is_split_long_or_double_(false) { - DCHECK_EQ(static_cast<size_t>(QUICK_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE), + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : + is_static_(is_static), shorty_(shorty), shorty_len_(shorty_len), + gpr_args_(reinterpret_cast<byte*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset), + fpr_args_(reinterpret_cast<byte*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset), + stack_args_(reinterpret_cast<byte*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_FrameSize + + StackArgumentStartFromShorty(is_static, shorty, shorty_len)), + gpr_index_(0), fpr_index_(0), stack_index_(0), cur_type_(Primitive::kPrimVoid), + is_split_long_or_double_(false) { + DCHECK_EQ(kQuickCalleeSaveFrame_RefAndArgs_FrameSize, Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs)->GetFrameSizeInBytes()); } @@ -143,30 +208,38 @@ class QuickArgumentVisitor { virtual void Visit() = 0; - Primitive::Type GetParamPrimitiveType() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - size_t index = param_index_; - if (is_static_) { - index++; // 0th argument must skip return value at start of the shorty - } else if (index == 0) { - return Primitive::kPrimNot; - } - CHECK_LT(index, shorty_len_); - return Primitive::GetType(shorty_[index]); + Primitive::Type GetParamPrimitiveType() const { + return cur_type_; } byte* GetParamAddress() const { - return cur_args_ + (cur_arg_index_ * kPointerSize); + if (!kSoftFloatAbi) { + Primitive::Type type = GetParamPrimitiveType(); + if (UNLIKELY((type == Primitive::kPrimDouble) || (type == Primitive::kPrimFloat))) { + if ((kNumFprArgs != 0) && (fpr_index_ + 1 < kNumFprArgs + 1)) { + return fpr_args_ + (fpr_index_ * kBytesPerFprSpillLocation); + } + } + } + if (gpr_index_ < kNumGprArgs) { + return gpr_args_ + GprIndexToGprOffset(gpr_index_); + } + return stack_args_ + (stack_index_ * kBytesStackArgLocation); } bool IsSplitLongOrDouble() const { - return is_split_long_or_double_; + if ((kBytesPerGprSpillLocation == 4) || (kBytesPerFprSpillLocation == 4)) { + return is_split_long_or_double_; + } else { + return false; // An optimization for when GPR and FPRs are 64bit. + } } - bool IsParamAReference() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + bool IsParamAReference() const { return GetParamPrimitiveType() == Primitive::kPrimNot; } - bool IsParamALongOrDouble() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + bool IsParamALongOrDouble() const { Primitive::Type type = GetParamPrimitiveType(); return type == Primitive::kPrimLong || type == Primitive::kPrimDouble; } @@ -179,51 +252,179 @@ class QuickArgumentVisitor { } void VisitArguments() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - for (cur_arg_index_ = 0; cur_arg_index_ < args_in_regs_ && param_index_ < num_params_; ) { - is_split_long_or_double_ = (cur_arg_index_ == 2) && IsParamALongOrDouble(); + gpr_index_ = 0; + fpr_index_ = 0; + stack_index_ = 0; + if (!is_static_) { // Handle this. + cur_type_ = Primitive::kPrimNot; + is_split_long_or_double_ = false; Visit(); - cur_arg_index_ += (IsParamALongOrDouble() ? 2 : 1); - param_index_++; + if (kNumGprArgs > 0) { + gpr_index_++; + } else { + stack_index_++; + } } - cur_args_ = stack_args_; - cur_arg_index_ = is_split_long_or_double_ ? 1 : 0; - is_split_long_or_double_ = false; - while (param_index_ < num_params_) { - Visit(); - cur_arg_index_ += (IsParamALongOrDouble() ? 2 : 1); - param_index_++; + for (uint32_t shorty_index = 1; shorty_index < shorty_len_; ++shorty_index) { + cur_type_ = Primitive::GetType(shorty_[shorty_index]); + switch (cur_type_) { + case Primitive::kPrimNot: + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + is_split_long_or_double_ = false; + Visit(); + if (gpr_index_ < kNumGprArgs) { + gpr_index_++; + } else { + stack_index_++; + } + break; + case Primitive::kPrimFloat: + is_split_long_or_double_ = false; + Visit(); + if (kSoftFloatAbi) { + if (gpr_index_ < kNumGprArgs) { + gpr_index_++; + } else { + stack_index_++; + } + } else { + if ((kNumFprArgs != 0) && (fpr_index_ + 1 < kNumFprArgs + 1)) { + fpr_index_++; + } else { + stack_index_++; + } + } + break; + case Primitive::kPrimDouble: + case Primitive::kPrimLong: + if (kSoftFloatAbi || (cur_type_ == Primitive::kPrimLong)) { + is_split_long_or_double_ = (kBytesPerGprSpillLocation == 4) && + ((gpr_index_ + 1) == kNumGprArgs); + Visit(); + if (gpr_index_ < kNumGprArgs) { + gpr_index_++; + if (kBytesPerGprSpillLocation == 4) { + if (gpr_index_ < kNumGprArgs) { + gpr_index_++; + } else { + stack_index_++; + } + } + } else { + if (kBytesStackArgLocation == 4) { + stack_index_+= 2; + } else { + CHECK_EQ(kBytesStackArgLocation, 8U); + stack_index_++; + } + } + } else { + is_split_long_or_double_ = (kBytesPerFprSpillLocation == 4) && + ((fpr_index_ + 1) == kNumFprArgs); + Visit(); + if ((kNumFprArgs != 0) && (fpr_index_ + 1 < kNumFprArgs + 1)) { + fpr_index_++; + if (kBytesPerFprSpillLocation == 4) { + if ((kNumFprArgs != 0) && (fpr_index_ + 1 < kNumFprArgs + 1)) { + fpr_index_++; + } else { + stack_index_++; + } + } + } else { + if (kBytesStackArgLocation == 4) { + stack_index_+= 2; + } else { + CHECK_EQ(kBytesStackArgLocation, 8U); + stack_index_++; + } + } + } + break; + default: + LOG(FATAL) << "Unexpected type: " << cur_type_ << " in " << shorty_; + } } } private: - static size_t ComputeArgsInRegs(bool is_static, const char* shorty, uint32_t shorty_len) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - size_t args_in_regs = (is_static ? 0 : 1); - for (size_t i = 0; i < shorty_len; i++) { - char s = shorty[i]; - if (s == 'J' || s == 'D') { - args_in_regs += 2; - } else { - args_in_regs++; + static size_t StackArgumentStartFromShorty(bool is_static, const char* shorty, + uint32_t shorty_len) { + if (kSoftFloatAbi) { + CHECK_EQ(kNumFprArgs, 0U); + return (kNumGprArgs * kBytesPerGprSpillLocation) + kBytesPerGprSpillLocation /* ArtMethod* */; + } else { + size_t offset = kBytesPerGprSpillLocation; // Skip Method*. + size_t gprs_seen = 0; + size_t fprs_seen = 0; + if (!is_static && (gprs_seen < kNumGprArgs)) { + gprs_seen++; + offset += kBytesStackArgLocation; } - if (args_in_regs > 3) { - args_in_regs = 3; - break; + for (uint32_t i = 1; i < shorty_len; ++i) { + switch (shorty[i]) { + case 'Z': + case 'B': + case 'C': + case 'S': + case 'I': + case 'L': + if (gprs_seen < kNumGprArgs) { + gprs_seen++; + offset += kBytesStackArgLocation; + } + break; + case 'J': + if (gprs_seen < kNumGprArgs) { + gprs_seen++; + offset += 2 * kBytesStackArgLocation; + if (kBytesPerGprSpillLocation == 4) { + if (gprs_seen < kNumGprArgs) { + gprs_seen++; + } + } + } + break; + case 'F': + if ((kNumFprArgs != 0) && (fprs_seen + 1 < kNumFprArgs + 1)) { + fprs_seen++; + offset += kBytesStackArgLocation; + } + break; + case 'D': + if ((kNumFprArgs != 0) && (fprs_seen + 1 < kNumFprArgs + 1)) { + fprs_seen++; + offset += 2 * kBytesStackArgLocation; + if (kBytesPerFprSpillLocation == 4) { + if ((kNumFprArgs != 0) && (fprs_seen + 1 < kNumFprArgs + 1)) { + fprs_seen++; + } + } + } + break; + default: + LOG(FATAL) << "Unexpected shorty character: " << shorty[i] << " in " << shorty; + } } + return offset; } - return args_in_regs; } const bool is_static_; const char* const shorty_; const uint32_t shorty_len_; - const size_t args_in_regs_; - const size_t num_params_; - byte* const reg_args_; - byte* const stack_args_; - byte* cur_args_; - size_t cur_arg_index_; - size_t param_index_; + byte* const gpr_args_; // Address of GPR arguments in callee save frame. + byte* const fpr_args_; // Address of FPR arguments in callee save frame. + byte* const stack_args_; // Address of stack arguments in caller's frame. + uint32_t gpr_index_; // Index into spilled GPRs. + uint32_t fpr_index_; // Index into spilled FPRs. + uint32_t stack_index_; // Index into arguments on the stack. + // The current type of argument during VisitArguments. + Primitive::Type cur_type_; // Does a 64bit parameter straddle the register and stack arguments? bool is_split_long_or_double_; }; @@ -231,9 +432,8 @@ class QuickArgumentVisitor { // Visits arguments on the stack placing them into the shadow frame. class BuildQuickShadowFrameVisitor : public QuickArgumentVisitor { public: - BuildQuickShadowFrameVisitor(mirror::ArtMethod** sp, - bool is_static, const char* shorty, - uint32_t shorty_len, ShadowFrame& sf, size_t first_arg_reg) : + BuildQuickShadowFrameVisitor(mirror::ArtMethod** sp, bool is_static, const char* shorty, + uint32_t shorty_len, ShadowFrame* sf, size_t first_arg_reg) : QuickArgumentVisitor(sp, is_static, shorty, shorty_len), sf_(sf), cur_reg_(first_arg_reg) {} virtual void Visit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -242,14 +442,14 @@ class BuildQuickShadowFrameVisitor : public QuickArgumentVisitor { case Primitive::kPrimLong: // Fall-through. case Primitive::kPrimDouble: if (IsSplitLongOrDouble()) { - sf_.SetVRegLong(cur_reg_, ReadSplitLongParam()); + sf_->SetVRegLong(cur_reg_, ReadSplitLongParam()); } else { - sf_.SetVRegLong(cur_reg_, *reinterpret_cast<jlong*>(GetParamAddress())); + sf_->SetVRegLong(cur_reg_, *reinterpret_cast<jlong*>(GetParamAddress())); } ++cur_reg_; break; case Primitive::kPrimNot: - sf_.SetVRegReference(cur_reg_, *reinterpret_cast<mirror::Object**>(GetParamAddress())); + sf_->SetVRegReference(cur_reg_, *reinterpret_cast<mirror::Object**>(GetParamAddress())); break; case Primitive::kPrimBoolean: // Fall-through. case Primitive::kPrimByte: // Fall-through. @@ -257,7 +457,7 @@ class BuildQuickShadowFrameVisitor : public QuickArgumentVisitor { case Primitive::kPrimShort: // Fall-through. case Primitive::kPrimInt: // Fall-through. case Primitive::kPrimFloat: - sf_.SetVReg(cur_reg_, *reinterpret_cast<jint*>(GetParamAddress())); + sf_->SetVReg(cur_reg_, *reinterpret_cast<jint*>(GetParamAddress())); break; case Primitive::kPrimVoid: LOG(FATAL) << "UNREACHABLE"; @@ -267,8 +467,8 @@ class BuildQuickShadowFrameVisitor : public QuickArgumentVisitor { } private: - ShadowFrame& sf_; - size_t cur_reg_; + ShadowFrame* const sf_; + uint32_t cur_reg_; DISALLOW_COPY_AND_ASSIGN(BuildQuickShadowFrameVisitor); }; @@ -293,8 +493,8 @@ extern "C" uint64_t artQuickToInterpreterBridge(mirror::ArtMethod* method, Threa method, 0, memory)); size_t first_arg_reg = code_item->registers_size_ - code_item->ins_size_; BuildQuickShadowFrameVisitor shadow_frame_builder(sp, mh.IsStatic(), mh.GetShorty(), - mh.GetShortyLength(), - *shadow_frame, first_arg_reg); + mh.GetShortyLength(), + shadow_frame, first_arg_reg); shadow_frame_builder.VisitArguments(); // Push a transition back into managed code onto the linked list in thread. ManagedStack fragment; diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index 006c271261..7b9d67578b 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -179,11 +179,11 @@ void MarkSweep::ProcessReferences(Thread* self) { TimingLogger::ScopedSplit split("ProcessReferences", &timings_); WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); GetHeap()->ProcessReferences(timings_, clear_soft_references_, &IsMarkedCallback, - &RecursiveMarkObjectCallback, this); + &MarkObjectCallback, &ProcessMarkStackPausedCallback, this); } bool MarkSweep::HandleDirtyObjectsPhase() { - TimingLogger::ScopedSplit split("HandleDirtyObjectsPhase", &timings_); + TimingLogger::ScopedSplit split("(Paused)HandleDirtyObjectsPhase", &timings_); Thread* self = Thread::Current(); Locks::mutator_lock_->AssertExclusiveHeld(self); @@ -400,10 +400,9 @@ inline void MarkSweep::MarkObjectNonNullParallel(const Object* obj) { } } -mirror::Object* MarkSweep::RecursiveMarkObjectCallback(mirror::Object* obj, void* arg) { +mirror::Object* MarkSweep::MarkObjectCallback(mirror::Object* obj, void* arg) { MarkSweep* mark_sweep = reinterpret_cast<MarkSweep*>(arg); mark_sweep->MarkObject(obj); - mark_sweep->ProcessMarkStack(true); return obj; } @@ -546,13 +545,6 @@ void MarkSweep::MarkRootCallback(Object** root, void* arg, uint32_t /*thread_id* reinterpret_cast<MarkSweep*>(arg)->MarkObjectNonNull(*root); } -mirror::Object* MarkSweep::MarkObjectCallback(mirror::Object* object, void* arg) { - DCHECK(object != nullptr); - DCHECK(arg != nullptr); - reinterpret_cast<MarkSweep*>(arg)->MarkObjectNonNull(object); - return object; -} - void MarkSweep::VerifyRootCallback(const Object* root, void* arg, size_t vreg, const StackVisitor* visitor) { reinterpret_cast<MarkSweep*>(arg)->VerifyRoot(root, vreg, visitor); @@ -957,7 +949,7 @@ void MarkSweep::RecursiveMarkDirtyObjects(bool paused, byte minimum_age) { } void MarkSweep::ReMarkRoots() { - timings_.StartSplit("ReMarkRoots"); + timings_.StartSplit("(Paused)ReMarkRoots"); Runtime::Current()->VisitRoots(MarkRootCallback, this, true, true); timings_.EndSplit(); } @@ -1208,6 +1200,11 @@ void MarkSweep::ScanObject(Object* obj) { ScanObjectVisit(obj, visitor); } +void MarkSweep::ProcessMarkStackPausedCallback(void* arg) { + DCHECK(arg != nullptr); + reinterpret_cast<MarkSweep*>(arg)->ProcessMarkStack(true); +} + void MarkSweep::ProcessMarkStackParallel(size_t thread_count) { Thread* self = Thread::Current(); ThreadPool* thread_pool = GetHeap()->GetThreadPool(); @@ -1231,7 +1228,7 @@ void MarkSweep::ProcessMarkStackParallel(size_t thread_count) { // Scan anything that's on the mark stack. void MarkSweep::ProcessMarkStack(bool paused) { - timings_.StartSplit("ProcessMarkStack"); + timings_.StartSplit(paused ? "(Paused)ProcessMarkStack" : "ProcessMarkStack"); size_t thread_count = GetThreadCount(paused); if (kParallelProcessMarkStack && thread_count > 1 && mark_stack_->Size() >= kMinimumParallelMarkStackSize) { diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h index 6a48cf7e99..963b9ea76f 100644 --- a/runtime/gc/collector/mark_sweep.h +++ b/runtime/gc/collector/mark_sweep.h @@ -176,7 +176,7 @@ class MarkSweep : public GarbageCollector { SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); - static mirror::Object* RecursiveMarkObjectCallback(mirror::Object* obj, void* arg) + static mirror::Object* MarkObjectCallback(mirror::Object* obj, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); @@ -185,9 +185,8 @@ class MarkSweep : public GarbageCollector { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); - static mirror::Object* MarkObjectCallback(mirror::Object* object, void* arg) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + static void ProcessMarkStackPausedCallback(void* arg) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); static void MarkRootParallelCallback(mirror::Object** root, void* arg, uint32_t thread_id, RootType root_type) diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index d64ec61e0f..882867b5d3 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -163,7 +163,7 @@ void SemiSpace::ProcessReferences(Thread* self) { TimingLogger::ScopedSplit split("ProcessReferences", &timings_); WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); GetHeap()->ProcessReferences(timings_, clear_soft_references_, &MarkedForwardingAddressCallback, - &RecursiveMarkObjectCallback, this); + &MarkObjectCallback, &ProcessMarkStackCallback, this); } void SemiSpace::MarkingPhase() { @@ -310,7 +310,7 @@ void SemiSpace::MarkReachableObjects() { } // Recursively process the mark stack. - ProcessMarkStack(true); + ProcessMarkStack(); } void SemiSpace::ReclaimPhase() { @@ -571,13 +571,15 @@ Object* SemiSpace::MarkObject(Object* obj) { return forward_address; } -mirror::Object* SemiSpace::RecursiveMarkObjectCallback(mirror::Object* root, void* arg) { +void SemiSpace::ProcessMarkStackCallback(void* arg) { + DCHECK(arg != nullptr); + reinterpret_cast<SemiSpace*>(arg)->ProcessMarkStack(); +} + +mirror::Object* SemiSpace::MarkObjectCallback(mirror::Object* root, void* arg) { DCHECK(root != nullptr); DCHECK(arg != nullptr); - SemiSpace* semi_space = reinterpret_cast<SemiSpace*>(arg); - mirror::Object* ret = semi_space->MarkObject(root); - semi_space->ProcessMarkStack(true); - return ret; + return reinterpret_cast<SemiSpace*>(arg)->MarkObject(root); } void SemiSpace::MarkRootCallback(Object** root, void* arg, uint32_t /*thread_id*/, @@ -587,12 +589,6 @@ void SemiSpace::MarkRootCallback(Object** root, void* arg, uint32_t /*thread_id* *root = reinterpret_cast<SemiSpace*>(arg)->MarkObject(*root); } -Object* SemiSpace::MarkObjectCallback(Object* object, void* arg) { - DCHECK(object != nullptr); - DCHECK(arg != nullptr); - return reinterpret_cast<SemiSpace*>(arg)->MarkObject(object); -} - // Marks all objects in the root set. void SemiSpace::MarkRoots() { timings_.StartSplit("MarkRoots"); @@ -680,7 +676,7 @@ void SemiSpace::ScanObject(Object* obj) { } // Scan anything that's on the mark stack. -void SemiSpace::ProcessMarkStack(bool paused) { +void SemiSpace::ProcessMarkStack() { space::MallocSpace* promo_dest_space = NULL; accounting::SpaceBitmap* live_bitmap = NULL; if (generational_ && !whole_heap_collection_) { @@ -694,7 +690,7 @@ void SemiSpace::ProcessMarkStack(bool paused) { DCHECK(mark_bitmap != nullptr); DCHECK_EQ(live_bitmap, mark_bitmap); } - timings_.StartSplit(paused ? "(paused)ProcessMarkStack" : "ProcessMarkStack"); + timings_.StartSplit("ProcessMarkStack"); while (!mark_stack_->IsEmpty()) { Object* obj = mark_stack_->PopBack(); if (generational_ && !whole_heap_collection_ && promo_dest_space->HasAddress(obj)) { diff --git a/runtime/gc/collector/semi_space.h b/runtime/gc/collector/semi_space.h index 89fe326150..ba973765b7 100644 --- a/runtime/gc/collector/semi_space.h +++ b/runtime/gc/collector/semi_space.h @@ -146,12 +146,12 @@ class SemiSpace : public GarbageCollector { RootType /*root_type*/) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); - static mirror::Object* MarkObjectCallback(mirror::Object* objecgt, void* arg) - EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); - - static mirror::Object* RecursiveMarkObjectCallback(mirror::Object* root, void* arg) + static mirror::Object* MarkObjectCallback(mirror::Object* root, void* arg) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + static void ProcessMarkStackCallback(void* arg) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_); + virtual mirror::Object* MarkNonForwardedObject(mirror::Object* obj) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); @@ -174,10 +174,6 @@ class SemiSpace : public GarbageCollector { // Returns true if we should sweep the space. virtual bool ShouldSweepSpace(space::ContinuousSpace* space) const; - // Returns how many threads we should use for the current GC phase based on if we are paused, - // whether or not we care about pauses. - size_t GetThreadCount(bool paused) const; - // Returns true if an object is inside of the immune region (assumed to be marked). bool IsImmune(const mirror::Object* obj) const ALWAYS_INLINE { return obj >= immune_begin_ && obj < immune_end_; @@ -237,7 +233,7 @@ class SemiSpace : public GarbageCollector { SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); // Recursively blackens objects on the mark stack. - void ProcessMarkStack(bool paused) + void ProcessMarkStack() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_); void EnqueueFinalizerReferences(mirror::Object** ref) diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 9e5e3abf89..8c89cdc251 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -610,41 +610,44 @@ space::Space* Heap::FindSpaceFromObject(const mirror::Object* obj, bool fail_ok) struct SoftReferenceArgs { IsMarkedCallback* is_marked_callback_; - MarkObjectCallback* recursive_mark_callback_; + MarkObjectCallback* mark_callback_; void* arg_; }; mirror::Object* Heap::PreserveSoftReferenceCallback(mirror::Object* obj, void* arg) { SoftReferenceArgs* args = reinterpret_cast<SoftReferenceArgs*>(arg); // TODO: Not preserve all soft references. - return args->recursive_mark_callback_(obj, args->arg_); + return args->mark_callback_(obj, args->arg_); } // Process reference class instances and schedule finalizations. void Heap::ProcessReferences(TimingLogger& timings, bool clear_soft, IsMarkedCallback* is_marked_callback, - MarkObjectCallback* recursive_mark_object_callback, void* arg) { + MarkObjectCallback* mark_object_callback, + ProcessMarkStackCallback* process_mark_stack_callback, void* arg) { // Unless we are in the zygote or required to clear soft references with white references, // preserve some white referents. if (!clear_soft && !Runtime::Current()->IsZygote()) { SoftReferenceArgs soft_reference_args; soft_reference_args.is_marked_callback_ = is_marked_callback; - soft_reference_args.recursive_mark_callback_ = recursive_mark_object_callback; + soft_reference_args.mark_callback_ = mark_object_callback; soft_reference_args.arg_ = arg; soft_reference_queue_.PreserveSomeSoftReferences(&PreserveSoftReferenceCallback, &soft_reference_args); + process_mark_stack_callback(arg); } - timings.StartSplit("ProcessReferences"); + timings.StartSplit("(Paused)ProcessReferences"); // Clear all remaining soft and weak references with white referents. soft_reference_queue_.ClearWhiteReferences(cleared_references_, is_marked_callback, arg); weak_reference_queue_.ClearWhiteReferences(cleared_references_, is_marked_callback, arg); timings.EndSplit(); // Preserve all white objects with finalize methods and schedule them for finalization. - timings.StartSplit("EnqueueFinalizerReferences"); + timings.StartSplit("(Paused)EnqueueFinalizerReferences"); finalizer_reference_queue_.EnqueueFinalizerReferences(cleared_references_, is_marked_callback, - recursive_mark_object_callback, arg); + mark_object_callback, arg); + process_mark_stack_callback(arg); timings.EndSplit(); - timings.StartSplit("ProcessReferences"); + timings.StartSplit("(Paused)ProcessReferences"); // Clear all f-reachable soft and weak references with white referents. soft_reference_queue_.ClearWhiteReferences(cleared_references_, is_marked_callback, arg); weak_reference_queue_.ClearWhiteReferences(cleared_references_, is_marked_callback, arg); diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 80a5a1aa19..21a2365cc2 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -339,7 +339,9 @@ class Heap { static mirror::Object* PreserveSoftReferenceCallback(mirror::Object* obj, void* arg); void ProcessReferences(TimingLogger& timings, bool clear_soft, IsMarkedCallback* is_marked_callback, - MarkObjectCallback* recursive_mark_object_callback, void* arg) + MarkObjectCallback* mark_object_callback, + ProcessMarkStackCallback* process_mark_stack_callback, + void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); @@ -474,6 +476,9 @@ class Heap { void FlushAllocStack() EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + // Revoke all the thread-local allocation stacks. + void RevokeAllThreadLocalAllocationStacks(Thread* self); + // Mark all the objects in the allocation stack in the specified bitmap. void MarkAllocStack(accounting::SpaceBitmap* bitmap1, accounting::SpaceBitmap* bitmap2, accounting::ObjectSet* large_objects, accounting::ObjectStack* stack) @@ -670,9 +675,6 @@ class Heap { // Swap the allocation stack with the live stack. void SwapStacks(Thread* self); - // Revoke all the thread-local allocation stacks. - void RevokeAllThreadLocalAllocationStacks(Thread* self); - // Clear cards and update the mod union table. void ProcessCards(TimingLogger& timings); diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 362df8cb13..a0665b57a8 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -2509,8 +2509,7 @@ class JNI { JniAbortF("NewDirectByteBuffer", "non-zero capacity for nullptr pointer: %" PRId64, capacity); } - // At the moment, the Java side is limited to 32 bits. - CHECK_LE(reinterpret_cast<uintptr_t>(address), 0xffffffff); + // At the moment, the capacity is limited to 32 bits. CHECK_LE(capacity, 0xffffffff); jlong address_arg = reinterpret_cast<jlong>(address); jint capacity_arg = static_cast<jint>(capacity); diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc index 4c58c84eb4..2dd7d96c05 100644 --- a/runtime/jni_internal_test.cc +++ b/runtime/jni_internal_test.cc @@ -127,7 +127,7 @@ class JniInternalTest : public CommonTest { mirror::Object* receiver; JniInternalTestMakeExecutable(&method, &receiver, is_static, "nop", "()V"); - ArgArray arg_array(NULL, 0); + ArgArray arg_array("V", 1); JValue result; if (!is_static) { @@ -143,7 +143,7 @@ class JniInternalTest : public CommonTest { mirror::Object* receiver; JniInternalTestMakeExecutable(&method, &receiver, is_static, "identity", "(B)B"); - ArgArray arg_array(NULL, 0); + ArgArray arg_array("BB", 2); uint32_t* args = arg_array.GetArray(); JValue result; @@ -179,7 +179,7 @@ class JniInternalTest : public CommonTest { mirror::Object* receiver; JniInternalTestMakeExecutable(&method, &receiver, is_static, "identity", "(I)I"); - ArgArray arg_array(NULL, 0); + ArgArray arg_array("II", 2); uint32_t* args = arg_array.GetArray(); JValue result; @@ -215,7 +215,7 @@ class JniInternalTest : public CommonTest { mirror::Object* receiver; JniInternalTestMakeExecutable(&method, &receiver, is_static, "identity", "(D)D"); - ArgArray arg_array(NULL, 0); + ArgArray arg_array("DD", 2); uint32_t* args = arg_array.GetArray(); JValue value; JValue result; @@ -259,7 +259,7 @@ class JniInternalTest : public CommonTest { mirror::Object* receiver; JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(II)I"); - ArgArray arg_array(NULL, 0); + ArgArray arg_array("III", 3); uint32_t* args = arg_array.GetArray(); JValue result; @@ -305,7 +305,7 @@ class JniInternalTest : public CommonTest { mirror::Object* receiver; JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(III)I"); - ArgArray arg_array(NULL, 0); + ArgArray arg_array("IIII", 4); uint32_t* args = arg_array.GetArray(); JValue result; @@ -361,7 +361,7 @@ class JniInternalTest : public CommonTest { mirror::Object* receiver; JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(IIII)I"); - ArgArray arg_array(NULL, 0); + ArgArray arg_array("IIIII", 5); uint32_t* args = arg_array.GetArray(); JValue result; @@ -422,7 +422,7 @@ class JniInternalTest : public CommonTest { mirror::Object* receiver; JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(IIIII)I"); - ArgArray arg_array(NULL, 0); + ArgArray arg_array("IIIIII", 6); uint32_t* args = arg_array.GetArray(); JValue result; @@ -488,7 +488,7 @@ class JniInternalTest : public CommonTest { mirror::Object* receiver; JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(DD)D"); - ArgArray arg_array(NULL, 0); + ArgArray arg_array("DDD", 3); uint32_t* args = arg_array.GetArray(); JValue value; JValue value2; @@ -559,7 +559,7 @@ class JniInternalTest : public CommonTest { mirror::Object* receiver; JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(DDD)D"); - ArgArray arg_array(NULL, 0); + ArgArray arg_array("DDDD", 4); uint32_t* args = arg_array.GetArray(); JValue value; JValue value2; @@ -617,7 +617,7 @@ class JniInternalTest : public CommonTest { mirror::Object* receiver; JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(DDDD)D"); - ArgArray arg_array(NULL, 0); + ArgArray arg_array("DDDDD", 5); uint32_t* args = arg_array.GetArray(); JValue value; JValue value2; @@ -684,7 +684,7 @@ class JniInternalTest : public CommonTest { mirror::Object* receiver; JniInternalTestMakeExecutable(&method, &receiver, is_static, "sum", "(DDDDD)D"); - ArgArray arg_array(NULL, 0); + ArgArray arg_array("DDDDDD", 6); uint32_t* args = arg_array.GetArray(); JValue value; JValue value2; @@ -1784,7 +1784,7 @@ TEST_F(JniInternalTest, StaticMainMethod) { mirror::ArtMethod* method = klass->FindDirectMethod("main", "([Ljava/lang/String;)V"); ASSERT_TRUE(method != NULL); - ArgArray arg_array(NULL, 0); + ArgArray arg_array("VL", 2); arg_array.Append(0U); JValue result; diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc index d5f7597df8..20d2b1881b 100644 --- a/runtime/mirror/art_method.cc +++ b/runtime/mirror/art_method.cc @@ -37,6 +37,10 @@ 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*, const char*); +#ifdef __x86_64__ +extern "C" void art_quick_invoke_static_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*, + const char*); +#endif // TODO: get global references for these Class* ArtMethod::java_lang_reflect_ArtMethod_ = NULL; @@ -276,7 +280,15 @@ void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* : GetEntryPointFromPortableCompiledCode()); } if (!IsPortableCompiled()) { +#ifdef __x86_64__ + if (!IsStatic()) { + (*art_quick_invoke_stub)(this, args, args_size, self, result, shorty); + } else { + (*art_quick_invoke_static_stub)(this, args, args_size, self, result, shorty); + } +#else (*art_quick_invoke_stub)(this, args, args_size, self, result, shorty); +#endif } else { (*art_portable_invoke_stub)(this, args, args_size, self, result, shorty[0]); } diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h index 71cc7af2e2..86f53480f3 100644 --- a/runtime/mirror/art_method.h +++ b/runtime/mirror/art_method.h @@ -313,9 +313,9 @@ class MANAGED ArtMethod : public Object { void SetOatNativeGcMapOffset(uint32_t gc_map_offset); uint32_t GetOatNativeGcMapOffset(); - size_t GetFrameSizeInBytes() { - DCHECK_EQ(sizeof(size_t), sizeof(uint32_t)); - size_t result = GetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_frame_size_in_bytes_), false); + uint32_t GetFrameSizeInBytes() { + uint32_t result = GetField32(OFFSET_OF_OBJECT_MEMBER(ArtMethod, quick_frame_size_in_bytes_), + false); DCHECK_LE(static_cast<size_t>(kStackAlignment), result); return result; } diff --git a/runtime/object_callbacks.h b/runtime/object_callbacks.h index 6af338bd57..468ba08a43 100644 --- a/runtime/object_callbacks.h +++ b/runtime/object_callbacks.h @@ -60,6 +60,7 @@ typedef void (VerifyRootCallback)(const mirror::Object* root, void* arg, size_t // address the object (if the object didn't move, returns the object input parameter). typedef mirror::Object* (IsMarkedCallback)(mirror::Object* object, void* arg) __attribute__((warn_unused_result)); +typedef void (ProcessMarkStackCallback)(void* arg); } // namespace art diff --git a/runtime/runtime.cc b/runtime/runtime.cc index e66e5afbc3..3ccea36013 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1486,12 +1486,18 @@ mirror::ArtMethod* Runtime::CreateCalleeSaveMethod(InstructionSet instruction_se (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 + (1 << art::x86_64::kNumberOfCpuRegisters); // fake return address callee save + uint32_t fp_arg_spills = + (1 << art::x86_64::XMM0) | (1 << art::x86_64::XMM1) | (1 << art::x86_64::XMM2) | + (1 << art::x86_64::XMM3) | (1 << art::x86_64::XMM4) | (1 << art::x86_64::XMM5) | + (1 << art::x86_64::XMM6) | (1 << art::x86_64::XMM7); + uint32_t fp_spills = (type == kRefsAndArgs ? fp_arg_spills : 0); size_t frame_size = RoundUp((__builtin_popcount(core_spills) /* gprs */ + + __builtin_popcount(fp_spills) /* fprs */ + 1 /* Method* */) * kPointerSize, kStackAlignment); method->SetFrameSizeInBytes(frame_size); method->SetCoreSpillMask(core_spills); - method->SetFpSpillMask(0); + method->SetFpSpillMask(fp_spills); } else { UNIMPLEMENTED(FATAL) << instruction_set; } diff --git a/runtime/runtime.h b/runtime/runtime.h index 159de2ef2f..223b8d5c45 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -429,6 +429,10 @@ class Runtime { return callee_save_methods_[type]; } + static size_t GetCalleeSaveMethodOffset(CalleeSaveType type) { + return OFFSETOF_MEMBER(Runtime, callee_save_methods_[type]); + } + void SetCalleeSaveMethod(mirror::ArtMethod* method, CalleeSaveType type); mirror::ArtMethod* CreateCalleeSaveMethod(InstructionSet instruction_set, diff --git a/test/030-bad-finalizer/expected.txt b/test/030-bad-finalizer/expected.txt index 88b18967d0..ee9cfff107 100644 --- a/test/030-bad-finalizer/expected.txt +++ b/test/030-bad-finalizer/expected.txt @@ -1,7 +1,4 @@ -Constructed object. -Nulled. Requestion gc. +About to null reference and request GC. Finalizer started and spinning... Finalizer done spinning. Finalizer sleeping forever now. -Requesting another GC. -Requesting another GC. diff --git a/test/030-bad-finalizer/src/BadFinalizer.java b/test/030-bad-finalizer/src/BadFinalizer.java deleted file mode 100644 index 6911a02781..0000000000 --- a/test/030-bad-finalizer/src/BadFinalizer.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2007 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. - */ - -/** - * Class with a bad finalizer. - */ -public class BadFinalizer { - public static void snooze(int ms) { - try { - Thread.sleep(ms); - } catch (InterruptedException ie) { - System.out.println("Snooze: " + ie.getMessage()); - } - } - - protected void finalize() { - System.out.println("Finalizer started and spinning..."); - int j = 0; - - /* spin for a bit */ - long start, end; - start = System.nanoTime(); - for (int i = 0; i < 1000000; i++) - j++; - end = System.nanoTime(); - System.out.println("Finalizer done spinning."); - - System.out.println("Finalizer sleeping forever now."); - while (true) { - snooze(10000); - } - } -} diff --git a/test/030-bad-finalizer/src/Main.java b/test/030-bad-finalizer/src/Main.java index 330e3442c3..942ee25900 100644 --- a/test/030-bad-finalizer/src/Main.java +++ b/test/030-bad-finalizer/src/Main.java @@ -21,19 +21,47 @@ public class Main { public static void main(String[] args) { BadFinalizer bf = new BadFinalizer(); - System.out.println("Constructed object."); + System.out.println("About to null reference and request GC."); bf = null; - - System.out.println("Nulled. Requestion gc."); Runtime.getRuntime().gc(); for (int i = 0; i < 8; i++) { - BadFinalizer.snooze(4000); - System.out.println("Requesting another GC."); + snooze(4000); Runtime.getRuntime().gc(); } - System.out.println("Done waiting."); + System.out.println("UNREACHABLE"); System.exit(0); } + + public static void snooze(int ms) { + try { + Thread.sleep(ms); + } catch (InterruptedException ie) { + } + } + + /** + * Class with a bad finalizer. + */ + public static class BadFinalizer { + protected void finalize() { + System.out.println("Finalizer started and spinning..."); + int j = 0; + + /* spin for a bit */ + long start, end; + start = System.nanoTime(); + for (int i = 0; i < 1000000; i++) { + j++; + } + end = System.nanoTime(); + System.out.println("Finalizer done spinning."); + + System.out.println("Finalizer sleeping forever now."); + while (true) { + snooze(10000); + } + } + } } diff --git a/test/036-finalizer/expected.txt b/test/036-finalizer/expected.txt index f9b29b0d59..a2a74fcbf8 100644 --- a/test/036-finalizer/expected.txt +++ b/test/036-finalizer/expected.txt @@ -1,14 +1,13 @@ -wimp: wahoo +wimp: [FinalizerTest message=wahoo, finalized=false] gc -finalizer executed: wahoo wimp: null finalize wimp: null sleep -reborn: wahoo +reborn: [FinalizerTest message=wahoo, finalized=true] wimp: null reset reborn gc + finalize sleep -reborn: nothing +reborn: [FinalizerTest message=nothing, finalized=false] wimp: null diff --git a/test/036-finalizer/src/FinalizerTest.java b/test/036-finalizer/src/FinalizerTest.java deleted file mode 100644 index b0d014d765..0000000000 --- a/test/036-finalizer/src/FinalizerTest.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2008 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. - */ - -import java.lang.ref.WeakReference; - -public class FinalizerTest { - public static FinalizerTest mNothing = new FinalizerTest("nothing"); - public static FinalizerTest mReborn = mNothing; - - public String mMsg = "default"; - - public FinalizerTest(String msg) { - mMsg = msg; - } - - public String toString() { - return mMsg; - } - - protected void finalize() { - System.out.println("finalizer executed: " + mMsg); - mReborn = this; - } -} diff --git a/test/036-finalizer/src/Main.java b/test/036-finalizer/src/Main.java index 6195aff3cc..328425f840 100644 --- a/test/036-finalizer/src/Main.java +++ b/test/036-finalizer/src/Main.java @@ -15,6 +15,8 @@ */ import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; /** * Some finalizer tests. @@ -31,18 +33,19 @@ public class Main { } } - public static WeakReference makeRef() { + public static WeakReference<FinalizerTest> makeRef() { /* * Make ft in another thread, so there is no danger of * a conservative reference leaking onto the main thread's * stack. */ - final WeakReference[] wimp = new WeakReference[1]; + final List<WeakReference<FinalizerTest>> wimp = + new ArrayList<WeakReference<FinalizerTest>>(); Thread t = new Thread() { public void run() { FinalizerTest ft = new FinalizerTest("wahoo"); - wimp[0] = new WeakReference(ft); + wimp.add(new WeakReference<FinalizerTest>(ft)); ft = null; } }; @@ -55,10 +58,10 @@ public class Main { throw new RuntimeException(ie); } - return wimp[0]; + return wimp.get(0); } - public static String wimpString(final WeakReference wimp) { + public static String wimpString(final WeakReference<FinalizerTest> wimp) { /* * Do the work in another thread, so there is no danger of a * conservative reference to ft leaking onto the main thread's @@ -68,7 +71,7 @@ public class Main { final String[] s = new String[1]; Thread t = new Thread() { public void run() { - Object ref = wimp.get(); + FinalizerTest ref = wimp.get(); if (ref != null) { s[0] = ref.toString(); } @@ -87,7 +90,7 @@ public class Main { } public static void main(String[] args) { - WeakReference wimp = makeRef(); + WeakReference<FinalizerTest> wimp = makeRef(); System.out.println("wimp: " + wimpString(wimp)); @@ -118,4 +121,26 @@ public class Main { System.out.println("reborn: " + FinalizerTest.mReborn); System.out.println("wimp: " + wimpString(wimp)); } + + public static class FinalizerTest { + public static FinalizerTest mNothing = new FinalizerTest("nothing"); + public static FinalizerTest mReborn = mNothing; + + private final String message; + private boolean finalized = false; + + public FinalizerTest(String message) { + this.message = message; + } + + public String toString() { + return "[FinalizerTest message=" + message + + ", finalized=" + finalized + "]"; + } + + protected void finalize() { + finalized = true; + mReborn = this; + } + } } diff --git a/test/048-server-socket/expected.txt b/test/048-server-socket/expected.txt deleted file mode 100644 index 23c3e8494f..0000000000 --- a/test/048-server-socket/expected.txt +++ /dev/null @@ -1,4 +0,0 @@ -opened! -closed! -reopened! -done diff --git a/test/048-server-socket/info.txt b/test/048-server-socket/info.txt deleted file mode 100644 index 08127da231..0000000000 --- a/test/048-server-socket/info.txt +++ /dev/null @@ -1,6 +0,0 @@ -This is a miscellaneous test that was imported into the new-at-the-time -runtime test framework. The test is intended to exercise basic features, -and as such cannot be build on top of junit, since failure of such basic -features might disrupt junit. - -TODO: Real description goes here. diff --git a/test/048-server-socket/src/Main.java b/test/048-server-socket/src/Main.java deleted file mode 100644 index 5b287ca42a..0000000000 --- a/test/048-server-socket/src/Main.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2007 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. - */ - -import java.net.ServerSocket; -import java.io.IOException; - - -/** - * Quick server socket test. - */ -public class Main { - private static void snooze(int sec) { - try { - Thread.sleep(sec * 1000); - } catch (InterruptedException ie) { - ie.printStackTrace(); - } - } - - public static void main(String[] args) { - ServerSocket socket; - - try { - socket = new ServerSocket(7890); - } catch (IOException ioe) { - System.out.println("couldn't open socket " + ioe.getMessage()); - return; - } - - System.out.println("opened!"); - snooze(1); - - try { - socket.close(); - } catch (IOException ioe) { - System.out.println("couldn't close socket " + ioe.getMessage()); - return; - } - - System.out.println("closed!"); - snooze(1); - - try { - socket = new ServerSocket(7890); - } catch (IOException ioe) { - System.out.println("couldn't reopen socket " + ioe.getMessage()); - return; - } - - System.out.println("reopened!"); - System.out.println("done"); - } -} |