diff options
35 files changed, 904 insertions, 248 deletions
diff --git a/compiler/dex/gvn_dead_code_elimination.cc b/compiler/dex/gvn_dead_code_elimination.cc index d29b865ce9..4de3410616 100644 --- a/compiler/dex/gvn_dead_code_elimination.cc +++ b/compiler/dex/gvn_dead_code_elimination.cc @@ -715,6 +715,7 @@ void GvnDeadCodeElimination::RecordPassTryToKillOverwrittenMoveOrMoveSrc(uint16_ // Try to find a MOVE to a vreg that wasn't changed since check_change. uint16_t value_name = data->wide_def ? lvn_->GetSregValueWide(dest_s_reg) : lvn_->GetSregValue(dest_s_reg); + uint32_t dest_v_reg = mir_graph_->SRegToVReg(dest_s_reg); for (size_t c = check_change + 1u, size = vreg_chains_.NumMIRs(); c != size; ++c) { MIRData* d = vreg_chains_.GetMIRData(c); if (d->is_move && d->wide_def == data->wide_def && @@ -731,8 +732,21 @@ void GvnDeadCodeElimination::RecordPassTryToKillOverwrittenMoveOrMoveSrc(uint16_ if (!vreg_chains_.IsVRegUsed(check_change + 1u, c, new_dest_v_reg, mir_graph_) && (!d->wide_def || !vreg_chains_.IsVRegUsed(check_change + 1u, c, new_dest_v_reg + 1, mir_graph_))) { - RecordPassKillMoveByRenamingSrcDef(check_change, c); - return; + // If the move's destination vreg changed, check if the vreg we're trying + // to rename is unused after that change. + uint16_t dest_change = vreg_chains_.FindFirstChangeAfter(new_dest_v_reg, c); + if (d->wide_def) { + uint16_t dest_change_high = vreg_chains_.FindFirstChangeAfter(new_dest_v_reg + 1, c); + if (dest_change_high != kNPos && + (dest_change == kNPos || dest_change_high < dest_change)) { + dest_change = dest_change_high; + } + } + if (dest_change == kNPos || + !vreg_chains_.IsVRegUsed(dest_change + 1u, size, dest_v_reg, mir_graph_)) { + RecordPassKillMoveByRenamingSrcDef(check_change, c); + return; + } } } } diff --git a/compiler/dex/gvn_dead_code_elimination_test.cc b/compiler/dex/gvn_dead_code_elimination_test.cc index 6ba91b64b6..4df0a8b98d 100644 --- a/compiler/dex/gvn_dead_code_elimination_test.cc +++ b/compiler/dex/gvn_dead_code_elimination_test.cc @@ -1933,6 +1933,78 @@ TEST_F(GvnDeadCodeEliminationTestDiamond, LongOverlaps1) { } } +TEST_F(GvnDeadCodeEliminationTestSimple, LongOverlaps2) { + static const MIRDef mirs[] = { + DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 0u, 1000u), + DEF_MOVE_WIDE(3, Instruction::MOVE_WIDE, 2u, 0u), + DEF_MOVE_WIDE(3, Instruction::MOVE_WIDE, 4u, 2u), + }; + + // The last insn should overlap the first and second. + static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 1, 2 }; + PrepareSRegToVRegMap(sreg_to_vreg_map); + + PrepareMIRs(mirs); + static const int32_t wide_sregs[] = { 0, 2, 4 }; + MarkAsWideSRegs(wide_sregs); + PerformGVN_DCE(); + + ASSERT_EQ(arraysize(mirs), value_names_.size()); + EXPECT_EQ(value_names_[0], value_names_[1]); + EXPECT_EQ(value_names_[0], value_names_[2]); + + static const bool eliminated[] = { + false, true, true, + }; + static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); + for (size_t i = 0; i != arraysize(eliminated); ++i) { + bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); + EXPECT_EQ(eliminated[i], actually_eliminated) << i; + } + // Check that the CONST_WIDE registers have been correctly renamed. + MIR* const_wide = &mirs_[0]; + ASSERT_EQ(2u, const_wide->ssa_rep->num_defs); + EXPECT_EQ(4, const_wide->ssa_rep->defs[0]); + EXPECT_EQ(5, const_wide->ssa_rep->defs[1]); + EXPECT_EQ(1u, const_wide->dalvikInsn.vA); +} + +TEST_F(GvnDeadCodeEliminationTestSimple, LongOverlaps3) { + static const MIRDef mirs[] = { + DEF_CONST_WIDE(3, Instruction::CONST_WIDE, 0u, 1000u), + DEF_MOVE_WIDE(3, Instruction::MOVE_WIDE, 2u, 0u), + DEF_MOVE_WIDE(3, Instruction::MOVE_WIDE, 4u, 2u), + }; + + // The last insn should overlap the first and second. + static const int32_t sreg_to_vreg_map[] = { 2, 3, 0, 1, 1, 2 }; + PrepareSRegToVRegMap(sreg_to_vreg_map); + + PrepareMIRs(mirs); + static const int32_t wide_sregs[] = { 0, 2, 4 }; + MarkAsWideSRegs(wide_sregs); + PerformGVN_DCE(); + + ASSERT_EQ(arraysize(mirs), value_names_.size()); + EXPECT_EQ(value_names_[0], value_names_[1]); + EXPECT_EQ(value_names_[0], value_names_[2]); + + static const bool eliminated[] = { + false, true, true, + }; + static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); + for (size_t i = 0; i != arraysize(eliminated); ++i) { + bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); + EXPECT_EQ(eliminated[i], actually_eliminated) << i; + } + // Check that the CONST_WIDE registers have been correctly renamed. + MIR* const_wide = &mirs_[0]; + ASSERT_EQ(2u, const_wide->ssa_rep->num_defs); + EXPECT_EQ(4, const_wide->ssa_rep->defs[0]); + EXPECT_EQ(5, const_wide->ssa_rep->defs[1]); + EXPECT_EQ(1u, const_wide->dalvikInsn.vA); +} + TEST_F(GvnDeadCodeEliminationTestSimple, MixedOverlaps1) { static const MIRDef mirs[] = { DEF_CONST(3, Instruction::CONST, 0u, 1000u), @@ -2093,4 +2165,37 @@ TEST_F(GvnDeadCodeEliminationTestSimple, ArrayLengthThrows) { } } +TEST_F(GvnDeadCodeEliminationTestSimple, Dependancy) { + static const MIRDef mirs[] = { + DEF_MOVE(3, Instruction::MOVE, 5u, 1u), // move v5,v1 + DEF_MOVE(3, Instruction::MOVE, 6u, 1u), // move v12,v1 + DEF_MOVE(3, Instruction::MOVE, 7u, 0u), // move v13,v0 + DEF_MOVE_WIDE(3, Instruction::MOVE_WIDE, 8u, 2u), // move v0_1,v2_3 + DEF_MOVE(3, Instruction::MOVE, 10u, 6u), // move v3,v12 + DEF_MOVE(3, Instruction::MOVE, 11u, 4u), // move v2,v4 + DEF_MOVE(3, Instruction::MOVE, 12u, 7u), // move v4,v13 + DEF_MOVE(3, Instruction::MOVE, 13, 11u), // move v12,v2 + DEF_MOVE(3, Instruction::MOVE, 14u, 10u), // move v2,v3 + DEF_MOVE(3, Instruction::MOVE, 15u, 5u), // move v3,v5 + DEF_MOVE(3, Instruction::MOVE, 16u, 12u), // move v5,v4 + }; + + static const int32_t sreg_to_vreg_map[] = { 0, 1, 2, 3, 4, 5, 12, 13, 0, 1, 3, 2, 4, 12, 2, 3, 5 }; + PrepareSRegToVRegMap(sreg_to_vreg_map); + + PrepareMIRs(mirs); + static const int32_t wide_sregs[] = { 2, 8 }; + MarkAsWideSRegs(wide_sregs); + PerformGVN_DCE(); + + static const bool eliminated[] = { + false, false, false, false, false, false, false, true, true, false, false, + }; + static_assert(arraysize(eliminated) == arraysize(mirs), "array size mismatch"); + for (size_t i = 0; i != arraysize(eliminated); ++i) { + bool actually_eliminated = (static_cast<int>(mirs_[i].dalvikInsn.opcode) == kMirOpNop); + EXPECT_EQ(eliminated[i], actually_eliminated) << i; + } +} + } // namespace art diff --git a/compiler/dex/quick/mips/target_mips.cc b/compiler/dex/quick/mips/target_mips.cc index b098bc2b5d..ec4bad778c 100644 --- a/compiler/dex/quick/mips/target_mips.cc +++ b/compiler/dex/quick/mips/target_mips.cc @@ -49,9 +49,11 @@ static constexpr RegStorage reserved_regs_arr_32[] = static constexpr RegStorage core_temps_arr_32[] = {rs_rV0, rs_rV1, rs_rA0, rs_rA1, rs_rA2, rs_rA3, rs_rT0_32, rs_rT1_32, rs_rT2_32, rs_rT3_32, rs_rT4_32, rs_rT5_32, rs_rT6_32, rs_rT7_32, rs_rT8}; -static constexpr RegStorage sp_temps_arr_32[] = +static constexpr RegStorage sp_fr0_temps_arr_32[] = {rs_rF0, rs_rF1, rs_rF2, rs_rF3, rs_rF4, rs_rF5, rs_rF6, rs_rF7, rs_rF8, rs_rF9, rs_rF10, rs_rF11, rs_rF12, rs_rF13, rs_rF14, rs_rF15}; +static constexpr RegStorage sp_fr1_temps_arr_32[] = + {rs_rF0, rs_rF2, rs_rF4, rs_rF6, rs_rF8, rs_rF10, rs_rF12, rs_rF14}; static constexpr RegStorage dp_fr0_temps_arr_32[] = {rs_rD0_fr0, rs_rD1_fr0, rs_rD2_fr0, rs_rD3_fr0, rs_rD4_fr0, rs_rD5_fr0, rs_rD6_fr0, rs_rD7_fr0}; @@ -130,7 +132,8 @@ static constexpr ArrayRef<const RegStorage> dp_fr0_regs_32(dp_fr0_regs_arr_32); static constexpr ArrayRef<const RegStorage> dp_fr1_regs_32(dp_fr1_regs_arr_32); static constexpr ArrayRef<const RegStorage> reserved_regs_32(reserved_regs_arr_32); static constexpr ArrayRef<const RegStorage> core_temps_32(core_temps_arr_32); -static constexpr ArrayRef<const RegStorage> sp_temps_32(sp_temps_arr_32); +static constexpr ArrayRef<const RegStorage> sp_fr0_temps_32(sp_fr0_temps_arr_32); +static constexpr ArrayRef<const RegStorage> sp_fr1_temps_32(sp_fr1_temps_arr_32); static constexpr ArrayRef<const RegStorage> dp_fr0_temps_32(dp_fr0_temps_arr_32); static constexpr ArrayRef<const RegStorage> dp_fr1_temps_32(dp_fr1_temps_arr_32); @@ -591,22 +594,22 @@ void MipsMir2Lir::ClobberCallerSave() { Clobber(rs_rFP); Clobber(rs_rRA); Clobber(rs_rF0); - Clobber(rs_rF1); Clobber(rs_rF2); - Clobber(rs_rF3); Clobber(rs_rF4); - Clobber(rs_rF5); Clobber(rs_rF6); - Clobber(rs_rF7); Clobber(rs_rF8); - Clobber(rs_rF9); Clobber(rs_rF10); - Clobber(rs_rF11); Clobber(rs_rF12); - Clobber(rs_rF13); Clobber(rs_rF14); - Clobber(rs_rF15); if (fpuIs32Bit_) { + Clobber(rs_rF1); + Clobber(rs_rF3); + Clobber(rs_rF5); + Clobber(rs_rF7); + Clobber(rs_rF9); + Clobber(rs_rF11); + Clobber(rs_rF13); + Clobber(rs_rF15); Clobber(rs_rD0_fr0); Clobber(rs_rD1_fr0); Clobber(rs_rD2_fr0); @@ -717,24 +720,26 @@ void MipsMir2Lir::CompilerInitializeRegAlloc() { fpuIs32Bit_ ? dp_fr0_regs_32 : dp_fr1_regs_32, reserved_regs_32, empty_pool, // reserved64 core_temps_32, empty_pool, // core64_temps - sp_temps_32, + fpuIs32Bit_ ? sp_fr0_temps_32 : sp_fr1_temps_32, fpuIs32Bit_ ? dp_fr0_temps_32 : dp_fr1_temps_32)); // Alias single precision floats to appropriate half of overlapping double. for (RegisterInfo* info : reg_pool_->sp_regs_) { int sp_reg_num = info->GetReg().GetRegNum(); int dp_reg_num = sp_reg_num & ~1; - RegStorage dp_reg = RegStorage::Solo64(RegStorage::kFloatingPoint | dp_reg_num); - RegisterInfo* dp_reg_info = GetRegInfo(dp_reg); - // Double precision register's master storage should refer to itself. - DCHECK_EQ(dp_reg_info, dp_reg_info->Master()); - // Redirect single precision's master storage to master. - info->SetMaster(dp_reg_info); - // Singles should show a single 32-bit mask bit, at first referring to the low half. - DCHECK_EQ(info->StorageMask(), 0x1U); - if (sp_reg_num & 1) { - // For odd singles, change to user the high word of the backing double. - info->SetStorageMask(0x2); + if (fpuIs32Bit_ || (sp_reg_num == dp_reg_num)) { + RegStorage dp_reg = RegStorage::Solo64(RegStorage::kFloatingPoint | dp_reg_num); + RegisterInfo* dp_reg_info = GetRegInfo(dp_reg); + // Double precision register's master storage should refer to itself. + DCHECK_EQ(dp_reg_info, dp_reg_info->Master()); + // Redirect single precision's master storage to master. + info->SetMaster(dp_reg_info); + // Singles should show a single 32-bit mask bit, at first referring to the low half. + DCHECK_EQ(info->StorageMask(), 0x1U); + if (sp_reg_num & 1) { + // For odd singles, change to user the high word of the backing double. + info->SetStorageMask(0x2); + } } } } diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 4ce3129f55..a03ff755ab 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -972,8 +972,10 @@ void ImageWriter::CalculateNewObjectOffsets() { size_t& offset = bin_slot_sizes_[BinTypeForNativeRelocationType(image_method_type)]; native_object_relocations_.emplace(&image_method_array_, NativeObjectRelocation { offset, image_method_type }); - CHECK_EQ(sizeof(image_method_array_), 8u); - offset += sizeof(image_method_array_); + const size_t array_size = LengthPrefixedArray<ArtMethod>::ComputeSize( + 0, ArtMethod::ObjectSize(target_ptr_size_)); + CHECK_ALIGNED(array_size, 8u); + offset += array_size; for (auto* m : image_methods_) { CHECK(m != nullptr); CHECK(m->IsRuntimeMethod()); @@ -1203,7 +1205,7 @@ void ImageWriter::FixupPointerArray(mirror::Object* dst, mirror::PointerArray* a if (elem != nullptr) { auto it = native_object_relocations_.find(elem); if (it == native_object_relocations_.end()) { - if (true) { + if (it->second.IsArtMethodRelocation()) { auto* method = reinterpret_cast<ArtMethod*>(elem); LOG(FATAL) << "No relocation entry for ArtMethod " << PrettyMethod(method) << " @ " << method << " idx=" << i << "/" << num_elements << " with declaring class " @@ -1300,8 +1302,8 @@ void* ImageWriter::NativeLocationInImage(void* obj) { return nullptr; } auto it = native_object_relocations_.find(obj); - const NativeObjectRelocation& relocation = it->second; CHECK(it != native_object_relocations_.end()) << obj; + const NativeObjectRelocation& relocation = it->second; return reinterpret_cast<void*>(image_begin_ + relocation.offset); } diff --git a/compiler/image_writer.h b/compiler/image_writer.h index eb6aa6f346..f4e10cc6ea 100644 --- a/compiler/image_writer.h +++ b/compiler/image_writer.h @@ -381,7 +381,8 @@ class ImageWriter FINAL { // Runtime ArtMethods which aren't reachable from any Class but need to be copied into the image. ArtMethod* image_methods_[ImageHeader::kImageMethodsCount]; - // Fake length prefixed array for image methods. + // Fake length prefixed array for image methods. This array does not contain the actual + // ArtMethods. We only use it for the header and relocation addresses. LengthPrefixedArray<ArtMethod> image_method_array_; // Counters for measurements, used for logging only. diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index d89d2b2dda..6c0292c551 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -2789,6 +2789,9 @@ void InstructionCodeGeneratorARM::VisitDivZeroCheck(HDivZeroCheck* instruction) Location value = locations->InAt(0); switch (instruction->GetType()) { + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: case Primitive::kPrimInt: { if (value.IsRegister()) { __ CompareAndBranchIfZero(value.AsRegister<Register>(), slow_path->GetEntryLabel()); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 7fab5cfcaf..b44c5ba9f8 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -2012,8 +2012,8 @@ void InstructionCodeGeneratorARM64::VisitDivZeroCheck(HDivZeroCheck* instruction Primitive::Type type = instruction->GetType(); - if ((type != Primitive::kPrimInt) && (type != Primitive::kPrimLong)) { - LOG(FATAL) << "Unexpected type " << type << "for DivZeroCheck."; + if ((type == Primitive::kPrimBoolean) || !Primitive::IsIntegralType(type)) { + LOG(FATAL) << "Unexpected type " << type << " for DivZeroCheck."; return; } diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index b6d67de181..b6ebeb4977 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -1908,8 +1908,9 @@ void InstructionCodeGeneratorMIPS64::VisitDivZeroCheck(HDivZeroCheck* instructio Primitive::Type type = instruction->GetType(); - if ((type != Primitive::kPrimInt) && (type != Primitive::kPrimLong)) { + if ((type == Primitive::kPrimBoolean) || !Primitive::IsIntegralType(type)) { LOG(FATAL) << "Unexpected type " << type << " for DivZeroCheck."; + return; } if (value.IsConstant()) { diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 5ffab33190..4efdbb922e 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -2995,6 +2995,9 @@ void LocationsBuilderX86::VisitDivZeroCheck(HDivZeroCheck* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); switch (instruction->GetType()) { + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: case Primitive::kPrimInt: { locations->SetInAt(0, Location::Any()); break; @@ -3022,6 +3025,9 @@ void InstructionCodeGeneratorX86::VisitDivZeroCheck(HDivZeroCheck* instruction) Location value = locations->InAt(0); switch (instruction->GetType()) { + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: case Primitive::kPrimInt: { if (value.IsRegister()) { __ testl(value.AsRegister<Register>(), value.AsRegister<Register>()); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index a0f45ed73e..1585104789 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -3161,6 +3161,9 @@ void InstructionCodeGeneratorX86_64::VisitDivZeroCheck(HDivZeroCheck* instructio Location value = locations->InAt(0); switch (instruction->GetType()) { + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: case Primitive::kPrimInt: { if (value.IsRegister()) { __ testl(value.AsRegister<CpuRegister>(), value.AsRegister<CpuRegister>()); diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 01065959d8..4c746798be 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -276,12 +276,12 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method, nullptr, caller_compilation_unit_.GetClassLoader(), class_linker, - *resolved_method->GetDexFile(), + callee_dex_file, code_item, resolved_method->GetDeclaringClass()->GetDexClassDefIndex(), - resolved_method->GetDexMethodIndex(), + method_index, resolved_method->GetAccessFlags(), - nullptr); + compiler_driver_->GetVerifiedMethod(&callee_dex_file, method_index)); bool requires_ctor_barrier = false; diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 814cebb99d..ca2c9989b0 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -3298,6 +3298,8 @@ class HDivZeroCheck : public HExpression<1> { SetRawInputAt(0, value); } + Primitive::Type GetType() const OVERRIDE { return InputAt(0)->GetType(); } + bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(HInstruction* other) const OVERRIDE { diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 7821da3397..0886e327d9 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -4063,10 +4063,10 @@ bool ClassLinker::LinkClass(Thread* self, const char* descriptor, Handle<mirror: // Retire the temporary class and create the correctly sized resolved class. StackHandleScope<1> hs(self); auto h_new_class = hs.NewHandle(klass->CopyOf(self, class_size, imt, image_pointer_size_)); - // Set array lengths to 0 since we don't want the GC to visit two different classes with the - // same ArtFields with the same If this occurs, it causes bugs in remembered sets since the GC - // may not see any references to the from space and clean the card. Though there was references - // to the from space that got marked by the first class. + // Set arrays to null since we don't want to have multiple classes with the same ArtField or + // ArtMethod array pointers. If this occurs, it causes bugs in remembered sets since the GC + // may not see any references to the target space and clean the card for a class if another + // class had the same array pointer. klass->SetDirectMethodsPtrUnchecked(nullptr); klass->SetVirtualMethodsPtr(nullptr); klass->SetSFieldsPtrUnchecked(nullptr); @@ -4973,8 +4973,8 @@ bool ClassLinker::LinkInterfaceMethods(Thread* self, Handle<mirror::Class> klass LengthPrefixedArray<ArtMethod>::ComputeSize(old_method_count, method_size) : 0u; const size_t new_size = LengthPrefixedArray<ArtMethod>::ComputeSize(new_method_count, method_size); - auto* virtuals = new(runtime->GetLinearAlloc()->Realloc( - self, old_virtuals, old_size, new_size))LengthPrefixedArray<ArtMethod>(new_method_count); + auto* virtuals = reinterpret_cast<LengthPrefixedArray<ArtMethod>*>( + runtime->GetLinearAlloc()->Realloc(self, old_virtuals, old_size, new_size)); if (UNLIKELY(virtuals == nullptr)) { self->AssertPendingOOMException(); self->EndAssertNoThreadSuspension(old_cause); @@ -5002,6 +5002,7 @@ bool ClassLinker::LinkInterfaceMethods(Thread* self, Handle<mirror::Class> klass move_table.emplace(mir_method, &*out); ++out; } + virtuals->SetLength(new_method_count); UpdateClassVirtualMethods(klass.Get(), virtuals); // Done copying methods, they are all roots in the class now, so we can end the no thread // suspension assert. diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index ec689f8528..8e329d6162 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -229,7 +229,7 @@ class FlipCallback : public Closure { CHECK(thread == self); Locks::mutator_lock_->AssertExclusiveHeld(self); cc->region_space_->SetFromSpace(cc->rb_table_, cc->force_evacuate_all_); - cc->SwapStacks(self); + cc->SwapStacks(); if (ConcurrentCopying::kEnableFromSpaceAccountingCheck) { cc->RecordLiveStackFreezeSize(self); cc->from_space_num_objects_at_first_pause_ = cc->region_space_->GetObjectsAllocated(); @@ -275,8 +275,8 @@ void ConcurrentCopying::FlipThreadRoots() { } } -void ConcurrentCopying::SwapStacks(Thread* self) { - heap_->SwapStacks(self); +void ConcurrentCopying::SwapStacks() { + heap_->SwapStacks(); } void ConcurrentCopying::RecordLiveStackFreezeSize(Thread* self) { diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h index a4fd71c0a5..f382448615 100644 --- a/runtime/gc/collector/concurrent_copying.h +++ b/runtime/gc/collector/concurrent_copying.h @@ -172,7 +172,7 @@ class ConcurrentCopying : public GarbageCollector { mirror::Object* GetFwdPtr(mirror::Object* from_ref) SHARED_REQUIRES(Locks::mutator_lock_); void FlipThreadRoots() REQUIRES(!Locks::mutator_lock_); - void SwapStacks(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_); + void SwapStacks() SHARED_REQUIRES(Locks::mutator_lock_); void RecordLiveStackFreezeSize(Thread* self); void ComputeUnevacFromSpaceLiveRatio(); void LogFromSpaceRefHolder(mirror::Object* obj, MemberOffset offset) diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc index 4b2c588dae..94ffe6ec79 100644 --- a/runtime/gc/collector/mark_compact.cc +++ b/runtime/gc/collector/mark_compact.cc @@ -191,7 +191,7 @@ void MarkCompact::MarkingPhase() { heap_->RevokeAllThreadLocalAllocationStacks(self); } t.NewTiming("SwapStacks"); - heap_->SwapStacks(self); + heap_->SwapStacks(); { WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); MarkRoots(); diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index 7f2c2048f6..e2bcca23fb 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -190,7 +190,7 @@ void MarkSweep::PausePhase() { { TimingLogger::ScopedTiming t2("SwapStacks", GetTimings()); WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); - heap_->SwapStacks(self); + heap_->SwapStacks(); live_stack_freeze_size_ = heap_->GetLiveStack()->Size(); // Need to revoke all the thread local allocation stacks since we just swapped the allocation // stacks and don't want anybody to allocate into the live stack. diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index 63def2452b..c11c134326 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -226,7 +226,7 @@ void SemiSpace::MarkingPhase() { TimingLogger::ScopedTiming t2("RevokeAllThreadLocalAllocationStacks", GetTimings()); heap_->RevokeAllThreadLocalAllocationStacks(self_); } - heap_->SwapStacks(self_); + heap_->SwapStacks(); { WriterMutexLock mu(self_, *Locks::heap_bitmap_lock_); MarkRoots(); diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h index cb750eb4b3..b814432c1d 100644 --- a/runtime/gc/heap-inl.h +++ b/runtime/gc/heap-inl.h @@ -39,8 +39,10 @@ namespace art { namespace gc { template <bool kInstrumented, bool kCheckLargeObject, typename PreFenceVisitor> -inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, mirror::Class* klass, - size_t byte_count, AllocatorType allocator, +inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, + mirror::Class* klass, + size_t byte_count, + AllocatorType allocator, const PreFenceVisitor& pre_fence_visitor) { if (kIsDebugBuild) { CheckPreconditionsForAllocObject(klass, byte_count); @@ -209,7 +211,8 @@ inline void Heap::PushOnAllocationStack(Thread* self, mirror::Object** obj) { } template <bool kInstrumented, typename PreFenceVisitor> -inline mirror::Object* Heap::AllocLargeObject(Thread* self, mirror::Class** klass, +inline mirror::Object* Heap::AllocLargeObject(Thread* self, + mirror::Class** klass, size_t byte_count, const PreFenceVisitor& pre_fence_visitor) { // Save and restore the class in case it moves. @@ -221,11 +224,14 @@ inline mirror::Object* Heap::AllocLargeObject(Thread* self, mirror::Class** klas } template <const bool kInstrumented, const bool kGrow> -inline mirror::Object* Heap::TryToAllocate(Thread* self, AllocatorType allocator_type, - size_t alloc_size, size_t* bytes_allocated, +inline mirror::Object* Heap::TryToAllocate(Thread* self, + AllocatorType allocator_type, + size_t alloc_size, + size_t* bytes_allocated, size_t* usable_size, size_t* bytes_tl_bulk_allocated) { - if (allocator_type != kAllocatorTypeTLAB && allocator_type != kAllocatorTypeRegionTLAB && + if (allocator_type != kAllocatorTypeTLAB && + allocator_type != kAllocatorTypeRegionTLAB && allocator_type != kAllocatorTypeRosAlloc && UNLIKELY(IsOutOfMemoryOnAllocation<kGrow>(allocator_type, alloc_size))) { return nullptr; @@ -390,7 +396,8 @@ inline Heap::AllocationTimer::~AllocationTimer() { // Only if the allocation succeeded, record the time. if (allocated_obj != nullptr) { uint64_t allocation_end_time = NanoTime() / kTimeAdjust; - heap_->total_allocation_time_.FetchAndAddSequentiallyConsistent(allocation_end_time - allocation_start_time_); + heap_->total_allocation_time_.FetchAndAddSequentiallyConsistent( + allocation_end_time - allocation_start_time_); } } } @@ -423,7 +430,8 @@ inline bool Heap::IsOutOfMemoryOnAllocation(AllocatorType allocator_type, size_t return false; } -inline void Heap::CheckConcurrentGC(Thread* self, size_t new_num_bytes_allocated, +inline void Heap::CheckConcurrentGC(Thread* self, + size_t new_num_bytes_allocated, mirror::Object** obj) { if (UNLIKELY(new_num_bytes_allocated >= concurrent_start_bytes_)) { RequestConcurrentGCAndSaveObject(self, false, obj); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 59e39df9ee..141fed283f 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -113,18 +113,34 @@ static constexpr size_t kDefaultAllocationStackSize = 8 * MB / // timeout on how long we wait for finalizers to run. b/21544853 static constexpr uint64_t kNativeAllocationFinalizeTimeout = MsToNs(250u); -Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max_free, - double target_utilization, double foreground_heap_growth_multiplier, - size_t capacity, size_t non_moving_space_capacity, const std::string& image_file_name, - const InstructionSet image_instruction_set, CollectorType foreground_collector_type, +Heap::Heap(size_t initial_size, + size_t growth_limit, + size_t min_free, + size_t max_free, + double target_utilization, + double foreground_heap_growth_multiplier, + size_t capacity, + size_t non_moving_space_capacity, + const std::string& image_file_name, + const InstructionSet image_instruction_set, + CollectorType foreground_collector_type, CollectorType background_collector_type, - space::LargeObjectSpaceType large_object_space_type, size_t large_object_threshold, - size_t parallel_gc_threads, size_t conc_gc_threads, bool low_memory_mode, - size_t long_pause_log_threshold, size_t long_gc_log_threshold, - bool ignore_max_footprint, bool use_tlab, - bool verify_pre_gc_heap, bool verify_pre_sweeping_heap, bool verify_post_gc_heap, - bool verify_pre_gc_rosalloc, bool verify_pre_sweeping_rosalloc, - bool verify_post_gc_rosalloc, bool gc_stress_mode, + space::LargeObjectSpaceType large_object_space_type, + size_t large_object_threshold, + size_t parallel_gc_threads, + size_t conc_gc_threads, + bool low_memory_mode, + size_t long_pause_log_threshold, + size_t long_gc_log_threshold, + bool ignore_max_footprint, + bool use_tlab, + bool verify_pre_gc_heap, + bool verify_pre_sweeping_heap, + bool verify_post_gc_heap, + bool verify_pre_gc_rosalloc, + bool verify_pre_sweeping_rosalloc, + bool verify_post_gc_rosalloc, + bool gc_stress_mode, bool use_homogeneous_space_compaction_for_oom, uint64_t min_interval_homogeneous_space_compaction_by_oom) : non_moving_space_(nullptr), @@ -526,8 +542,10 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max } } -MemMap* Heap::MapAnonymousPreferredAddress(const char* name, uint8_t* request_begin, - size_t capacity, std::string* out_error_str) { +MemMap* Heap::MapAnonymousPreferredAddress(const char* name, + uint8_t* request_begin, + size_t capacity, + std::string* out_error_str) { while (true) { MemMap* map = MemMap::MapAnonymous(name, request_begin, capacity, PROT_READ | PROT_WRITE, true, false, out_error_str); @@ -543,9 +561,12 @@ bool Heap::MayUseCollector(CollectorType type) const { return foreground_collector_type_ == type || background_collector_type_ == type; } -space::MallocSpace* Heap::CreateMallocSpaceFromMemMap(MemMap* mem_map, size_t initial_size, - size_t growth_limit, size_t capacity, - const char* name, bool can_move_objects) { +space::MallocSpace* Heap::CreateMallocSpaceFromMemMap(MemMap* mem_map, + size_t initial_size, + size_t growth_limit, + size_t capacity, + const char* name, + bool can_move_objects) { space::MallocSpace* malloc_space = nullptr; if (kUseRosAlloc) { // Create rosalloc space. @@ -1494,8 +1515,10 @@ space::RosAllocSpace* Heap::GetRosAllocSpace(gc::allocator::RosAlloc* rosalloc) return nullptr; } -mirror::Object* Heap::AllocateInternalWithGc(Thread* self, AllocatorType allocator, - size_t alloc_size, size_t* bytes_allocated, +mirror::Object* Heap::AllocateInternalWithGc(Thread* self, + AllocatorType allocator, + size_t alloc_size, + size_t* bytes_allocated, size_t* usable_size, size_t* bytes_tl_bulk_allocated, mirror::Class** klass) { @@ -1694,10 +1717,12 @@ uint64_t Heap::GetBytesAllocatedEver() const { class InstanceCounter { public: - InstanceCounter(const std::vector<mirror::Class*>& classes, bool use_is_assignable_from, uint64_t* counts) + InstanceCounter(const std::vector<mirror::Class*>& classes, + bool use_is_assignable_from, + uint64_t* counts) SHARED_REQUIRES(Locks::mutator_lock_) - : classes_(classes), use_is_assignable_from_(use_is_assignable_from), counts_(counts) { - } + : classes_(classes), use_is_assignable_from_(use_is_assignable_from), counts_(counts) {} + static void Callback(mirror::Object* obj, void* arg) SHARED_REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { InstanceCounter* instance_counter = reinterpret_cast<InstanceCounter*>(arg); @@ -1753,7 +1778,8 @@ class InstanceCollector { DISALLOW_COPY_AND_ASSIGN(InstanceCollector); }; -void Heap::GetInstances(mirror::Class* c, int32_t max_count, +void Heap::GetInstances(mirror::Class* c, + int32_t max_count, std::vector<mirror::Object*>& instances) { InstanceCollector collector(c, max_count, instances); VisitObjects(&InstanceCollector::Callback, &collector); @@ -1761,7 +1787,8 @@ void Heap::GetInstances(mirror::Class* c, int32_t max_count, class ReferringObjectsFinder { public: - ReferringObjectsFinder(mirror::Object* object, int32_t max_count, + ReferringObjectsFinder(mirror::Object* object, + int32_t max_count, std::vector<mirror::Object*>& referring_objects) SHARED_REQUIRES(Locks::mutator_lock_) : object_(object), max_count_(max_count), referring_objects_(referring_objects) { @@ -2081,8 +2108,7 @@ void Heap::ChangeCollector(CollectorType collector_type) { // Special compacting collector which uses sub-optimal bin packing to reduce zygote space size. class ZygoteCompactingCollector FINAL : public collector::SemiSpace { public: - explicit ZygoteCompactingCollector(gc::Heap* heap, - bool is_running_on_memory_tool) + explicit ZygoteCompactingCollector(gc::Heap* heap, bool is_running_on_memory_tool) : SemiSpace(heap, false, "zygote collector"), bin_live_bitmap_(nullptr), bin_mark_bitmap_(nullptr), @@ -2135,10 +2161,9 @@ class ZygoteCompactingCollector FINAL : public collector::SemiSpace { } } - virtual bool ShouldSweepSpace(space::ContinuousSpace* space) const { + virtual bool ShouldSweepSpace(space::ContinuousSpace* space ATTRIBUTE_UNUSED) const { // Don't sweep any spaces since we probably blasted the internal accounting of the free list // allocator. - UNUSED(space); return false; } @@ -2380,7 +2405,8 @@ collector::GarbageCollector* Heap::Compact(space::ContinuousMemMapAllocSpace* ta } } -collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, GcCause gc_cause, +collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, + GcCause gc_cause, bool clear_soft_references) { Thread* self = Thread::Current(); Runtime* runtime = Runtime::Current(); @@ -2759,8 +2785,7 @@ class VerifyReferenceVisitor : public SingleRootVisitor { class VerifyObjectVisitor { public: explicit VerifyObjectVisitor(Heap* heap, Atomic<size_t>* fail_count, bool verify_referent) - : heap_(heap), fail_count_(fail_count), verify_referent_(verify_referent) { - } + : heap_(heap), fail_count_(fail_count), verify_referent_(verify_referent) {} void operator()(mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { @@ -2912,7 +2937,7 @@ class VerifyReferenceCardVisitor { if (!obj->IsObjectArray()) { mirror::Class* klass = is_static ? obj->AsClass() : obj->GetClass(); CHECK(klass != nullptr); - for (ArtField& field : is_static ? klass->GetSFields() : klass->GetIFields()) { + for (ArtField& field : (is_static ? klass->GetSFields() : klass->GetIFields())) { if (field.GetOffset().Int32Value() == offset.Int32Value()) { LOG(ERROR) << (is_static ? "Static " : "") << "field in the live stack is " << PrettyField(&field); @@ -2980,8 +3005,7 @@ bool Heap::VerifyMissingCardMarks() { return !visitor.Failed(); } -void Heap::SwapStacks(Thread* self) { - UNUSED(self); +void Heap::SwapStacks() { if (kUseThreadLocalAllocationStack) { live_stack_->AssertAllZero(); } @@ -3034,7 +3058,9 @@ accounting::RememberedSet* Heap::FindRememberedSetFromSpace(space::Space* space) return it->second; } -void Heap::ProcessCards(TimingLogger* timings, bool use_rem_sets, bool process_alloc_space_cards, +void Heap::ProcessCards(TimingLogger* timings, + bool use_rem_sets, + bool process_alloc_space_cards, bool clear_alloc_space_cards) { TimingLogger::ScopedTiming t(__FUNCTION__, timings); // Clear cards and keep track of cards cleared in the mod-union table. @@ -3094,11 +3120,11 @@ void Heap::PreGcVerificationPaused(collector::GarbageCollector* gc) { if (verify_missing_card_marks_) { TimingLogger::ScopedTiming t2("(Paused)PreGcVerifyMissingCardMarks", timings); ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); - SwapStacks(self); + SwapStacks(); // Sort the live stack so that we can quickly binary search it later. CHECK(VerifyMissingCardMarks()) << "Pre " << gc->GetName() << " missing card mark verification failed\n" << DumpSpaces(); - SwapStacks(self); + SwapStacks(); } if (verify_mod_union_table_) { TimingLogger::ScopedTiming t2("(Paused)PreGcVerifyModUnionTables", timings); @@ -3119,8 +3145,7 @@ void Heap::PreGcVerification(collector::GarbageCollector* gc) { } } -void Heap::PrePauseRosAllocVerification(collector::GarbageCollector* gc) { - UNUSED(gc); +void Heap::PrePauseRosAllocVerification(collector::GarbageCollector* gc ATTRIBUTE_UNUSED) { // TODO: Add a new runtime option for this? if (verify_pre_gc_rosalloc_) { RosAllocVerification(current_gc_iteration_.GetTimings(), "PreGcRosAllocVerification"); @@ -3486,7 +3511,8 @@ void Heap::ConcurrentGC(Thread* self, bool force_full) { class Heap::CollectorTransitionTask : public HeapTask { public: - explicit CollectorTransitionTask(uint64_t target_time) : HeapTask(target_time) { } + explicit CollectorTransitionTask(uint64_t target_time) : HeapTask(target_time) {} + virtual void Run(Thread* self) OVERRIDE { gc::Heap* heap = Runtime::Current()->GetHeap(); heap->DoPendingCollectorTransition(); diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 09c18b8f75..66760493ea 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -96,11 +96,7 @@ namespace space { class AgeCardVisitor { public: uint8_t operator()(uint8_t card) const { - if (card == accounting::CardTable::kCardDirty) { - return card - 1; - } else { - return 0; - } + return (card == accounting::CardTable::kCardDirty) ? card - 1 : 0; } }; @@ -165,20 +161,34 @@ class Heap { // Create a heap with the requested sizes. The possible empty // image_file_names names specify Spaces to load based on // ImageWriter output. - explicit Heap(size_t initial_size, size_t growth_limit, size_t min_free, - size_t max_free, double target_utilization, - double foreground_heap_growth_multiplier, size_t capacity, + explicit Heap(size_t initial_size, + size_t growth_limit, + size_t min_free, + size_t max_free, + double target_utilization, + double foreground_heap_growth_multiplier, + size_t capacity, size_t non_moving_space_capacity, const std::string& original_image_file_name, InstructionSet image_instruction_set, - CollectorType foreground_collector_type, CollectorType background_collector_type, - space::LargeObjectSpaceType large_object_space_type, size_t large_object_threshold, - size_t parallel_gc_threads, size_t conc_gc_threads, bool low_memory_mode, - size_t long_pause_threshold, size_t long_gc_threshold, - bool ignore_max_footprint, bool use_tlab, - bool verify_pre_gc_heap, bool verify_pre_sweeping_heap, bool verify_post_gc_heap, - bool verify_pre_gc_rosalloc, bool verify_pre_sweeping_rosalloc, - bool verify_post_gc_rosalloc, bool gc_stress_mode, + CollectorType foreground_collector_type, + CollectorType background_collector_type, + space::LargeObjectSpaceType large_object_space_type, + size_t large_object_threshold, + size_t parallel_gc_threads, + size_t conc_gc_threads, + bool low_memory_mode, + size_t long_pause_threshold, + size_t long_gc_threshold, + bool ignore_max_footprint, + bool use_tlab, + bool verify_pre_gc_heap, + bool verify_pre_sweeping_heap, + bool verify_post_gc_heap, + bool verify_pre_gc_rosalloc, + bool verify_pre_sweeping_rosalloc, + bool verify_post_gc_rosalloc, + bool gc_stress_mode, bool use_homogeneous_space_compaction, uint64_t min_interval_homogeneous_space_compaction_by_oom); @@ -186,7 +196,9 @@ class Heap { // Allocates and initializes storage for an object instance. template <bool kInstrumented, typename PreFenceVisitor> - mirror::Object* AllocObject(Thread* self, mirror::Class* klass, size_t num_bytes, + mirror::Object* AllocObject(Thread* self, + mirror::Class* klass, + size_t num_bytes, const PreFenceVisitor& pre_fence_visitor) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!*gc_complete_lock_, !*pending_task_lock_, !*backtrace_lock_, @@ -196,7 +208,9 @@ class Heap { } template <bool kInstrumented, typename PreFenceVisitor> - mirror::Object* AllocNonMovableObject(Thread* self, mirror::Class* klass, size_t num_bytes, + mirror::Object* AllocNonMovableObject(Thread* self, + mirror::Class* klass, + size_t num_bytes, const PreFenceVisitor& pre_fence_visitor) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!*gc_complete_lock_, !*pending_task_lock_, !*backtrace_lock_, @@ -206,9 +220,11 @@ class Heap { } template <bool kInstrumented, bool kCheckLargeObject, typename PreFenceVisitor> - ALWAYS_INLINE mirror::Object* AllocObjectWithAllocator( - Thread* self, mirror::Class* klass, size_t byte_count, AllocatorType allocator, - const PreFenceVisitor& pre_fence_visitor) + ALWAYS_INLINE mirror::Object* AllocObjectWithAllocator(Thread* self, + mirror::Class* klass, + size_t byte_count, + AllocatorType allocator, + const PreFenceVisitor& pre_fence_visitor) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!*gc_complete_lock_, !*pending_task_lock_, !*backtrace_lock_, !Roles::uninterruptible_); @@ -263,8 +279,7 @@ class Heap { // A weaker test than IsLiveObject or VerifyObject that doesn't require the heap lock, // and doesn't abort on error, allowing the caller to report more // meaningful diagnostics. - bool IsValidObjectAddress(const mirror::Object* obj) const - SHARED_REQUIRES(Locks::mutator_lock_); + bool IsValidObjectAddress(const mirror::Object* obj) const SHARED_REQUIRES(Locks::mutator_lock_); // Faster alternative to IsHeapAddress since finding if an object is in the large object space is // very slow. @@ -273,8 +288,10 @@ class Heap { // Returns true if 'obj' is a live heap object, false otherwise (including for invalid addresses). // Requires the heap lock to be held. - bool IsLiveObjectLocked(mirror::Object* obj, bool search_allocation_stack = true, - bool search_live_stack = true, bool sorted = false) + bool IsLiveObjectLocked(mirror::Object* obj, + bool search_allocation_stack = true, + bool search_live_stack = true, + bool sorted = false) SHARED_REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_); // Returns true if there is any chance that the object (obj) will move. @@ -298,7 +315,8 @@ class Heap { // Implements VMDebug.countInstancesOfClass and JDWP VM_InstanceCount. // The boolean decides whether to use IsAssignableFrom or == when comparing classes. - void CountInstances(const std::vector<mirror::Class*>& classes, bool use_is_assignable_from, + void CountInstances(const std::vector<mirror::Class*>& classes, + bool use_is_assignable_from, uint64_t* counts) REQUIRES(!Locks::heap_bitmap_lock_, !*gc_complete_lock_) SHARED_REQUIRES(Locks::mutator_lock_); @@ -307,7 +325,8 @@ class Heap { REQUIRES(!Locks::heap_bitmap_lock_, !*gc_complete_lock_) SHARED_REQUIRES(Locks::mutator_lock_); // Implements JDWP OR_ReferringObjects. - void GetReferringObjects(mirror::Object* o, int32_t max_count, + void GetReferringObjects(mirror::Object* o, + int32_t max_count, std::vector<mirror::Object*>& referring_objects) REQUIRES(!Locks::heap_bitmap_lock_, !*gc_complete_lock_) SHARED_REQUIRES(Locks::mutator_lock_); @@ -347,8 +366,7 @@ class Heap { // Blocks the caller until the garbage collector becomes idle and returns the type of GC we // waited for. - collector::GcType WaitForGcToComplete(GcCause cause, Thread* self) - REQUIRES(!*gc_complete_lock_); + collector::GcType WaitForGcToComplete(GcCause cause, Thread* self) REQUIRES(!*gc_complete_lock_); // Update the heap's process state to a new value, may cause compaction to occur. void UpdateProcessState(ProcessState process_state) @@ -405,14 +423,17 @@ class Heap { // Must be called if a field of an Object in the heap changes, and before any GC safe-point. // The call is not needed if null is stored in the field. - ALWAYS_INLINE void WriteBarrierField(const mirror::Object* dst, MemberOffset /*offset*/, - const mirror::Object* /*new_value*/) { + ALWAYS_INLINE void WriteBarrierField(const mirror::Object* dst, + MemberOffset offset ATTRIBUTE_UNUSED, + const mirror::Object* new_value ATTRIBUTE_UNUSED) { card_table_->MarkCard(dst); } // Write barrier for array operations that update many field positions - ALWAYS_INLINE void WriteBarrierArray(const mirror::Object* dst, int /*start_offset*/, - size_t /*length TODO: element_count or byte_count?*/) { + ALWAYS_INLINE void WriteBarrierArray(const mirror::Object* dst, + int start_offset ATTRIBUTE_UNUSED, + // TODO: element_count or byte_count? + size_t length ATTRIBUTE_UNUSED) { card_table_->MarkCard(dst); } @@ -436,7 +457,8 @@ class Heap { } // Returns the number of objects currently allocated. - size_t GetObjectsAllocated() const REQUIRES(!Locks::heap_bitmap_lock_); + size_t GetObjectsAllocated() const + REQUIRES(!Locks::heap_bitmap_lock_); // Returns the total number of objects allocated since the heap was created. uint64_t GetObjectsAllocatedEver() const; @@ -540,11 +562,13 @@ class Heap { accounting::SpaceBitmap<kObjectAlignment>* bitmap2, accounting::SpaceBitmap<kLargeObjectAlignment>* large_objects, accounting::ObjectStack* stack) - SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) + REQUIRES(Locks::heap_bitmap_lock_); // Mark the specified allocation stack as live. void MarkAllocStackAsLive(accounting::ObjectStack* stack) - SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) + REQUIRES(Locks::heap_bitmap_lock_); // Unbind any bound bitmaps. void UnBindBitmaps() REQUIRES(Locks::heap_bitmap_lock_); @@ -758,15 +782,20 @@ class Heap { allocator_type != kAllocatorTypeTLAB; } static bool IsMovingGc(CollectorType collector_type) { - return collector_type == kCollectorTypeSS || collector_type == kCollectorTypeGSS || - collector_type == kCollectorTypeCC || collector_type == kCollectorTypeMC || + return + collector_type == kCollectorTypeSS || + collector_type == kCollectorTypeGSS || + collector_type == kCollectorTypeCC || + collector_type == kCollectorTypeMC || collector_type == kCollectorTypeHomogeneousSpaceCompact; } bool ShouldAllocLargeObject(mirror::Class* c, size_t byte_count) const SHARED_REQUIRES(Locks::mutator_lock_); - ALWAYS_INLINE void CheckConcurrentGC(Thread* self, size_t new_num_bytes_allocated, + ALWAYS_INLINE void CheckConcurrentGC(Thread* self, + size_t new_num_bytes_allocated, mirror::Object** obj) - SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!*pending_task_lock_, !*gc_complete_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) + REQUIRES(!*pending_task_lock_, !*gc_complete_lock_); accounting::ObjectStack* GetMarkStack() { return mark_stack_.get(); @@ -774,22 +803,29 @@ class Heap { // We don't force this to be inlined since it is a slow path. template <bool kInstrumented, typename PreFenceVisitor> - mirror::Object* AllocLargeObject(Thread* self, mirror::Class** klass, size_t byte_count, + mirror::Object* AllocLargeObject(Thread* self, + mirror::Class** klass, + size_t byte_count, const PreFenceVisitor& pre_fence_visitor) SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!*gc_complete_lock_, !*pending_task_lock_, !*backtrace_lock_); // Handles Allocate()'s slow allocation path with GC involved after // an initial allocation attempt failed. - mirror::Object* AllocateInternalWithGc(Thread* self, AllocatorType allocator, size_t num_bytes, - size_t* bytes_allocated, size_t* usable_size, + mirror::Object* AllocateInternalWithGc(Thread* self, + AllocatorType allocator, + size_t num_bytes, + size_t* bytes_allocated, + size_t* usable_size, size_t* bytes_tl_bulk_allocated, mirror::Class** klass) REQUIRES(!Locks::thread_suspend_count_lock_, !*gc_complete_lock_, !*pending_task_lock_) SHARED_REQUIRES(Locks::mutator_lock_); // Allocate into a specific space. - mirror::Object* AllocateInto(Thread* self, space::AllocSpace* space, mirror::Class* c, + mirror::Object* AllocateInto(Thread* self, + space::AllocSpace* space, + mirror::Class* c, size_t bytes) SHARED_REQUIRES(Locks::mutator_lock_); @@ -800,8 +836,10 @@ class Heap { // Try to allocate a number of bytes, this function never does any GCs. Needs to be inlined so // that the switch statement is constant optimized in the entrypoints. template <const bool kInstrumented, const bool kGrow> - ALWAYS_INLINE mirror::Object* TryToAllocate(Thread* self, AllocatorType allocator_type, - size_t alloc_size, size_t* bytes_allocated, + ALWAYS_INLINE mirror::Object* TryToAllocate(Thread* self, + AllocatorType allocator_type, + size_t alloc_size, + size_t* bytes_allocated, size_t* usable_size, size_t* bytes_tl_bulk_allocated) SHARED_REQUIRES(Locks::mutator_lock_); @@ -828,12 +866,14 @@ class Heap { REQUIRES(!*pending_task_lock_); void RequestConcurrentGCAndSaveObject(Thread* self, bool force_full, mirror::Object** obj) - SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!*pending_task_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) + REQUIRES(!*pending_task_lock_); bool IsGCRequestPending() const; // Sometimes CollectGarbageInternal decides to run a different Gc than you requested. Returns // which type of Gc was actually ran. - collector::GcType CollectGarbageInternal(collector::GcType gc_plan, GcCause gc_cause, + collector::GcType CollectGarbageInternal(collector::GcType gc_plan, + GcCause gc_cause, bool clear_soft_references) REQUIRES(!*gc_complete_lock_, !Locks::heap_bitmap_lock_, !Locks::thread_suspend_count_lock_, !*pending_task_lock_); @@ -862,13 +902,18 @@ class Heap { HomogeneousSpaceCompactResult PerformHomogeneousSpaceCompact() REQUIRES(!*gc_complete_lock_); // Create the main free list malloc space, either a RosAlloc space or DlMalloc space. - void CreateMainMallocSpace(MemMap* mem_map, size_t initial_size, size_t growth_limit, + void CreateMainMallocSpace(MemMap* mem_map, + size_t initial_size, + size_t growth_limit, size_t capacity); // Create a malloc space based on a mem map. Does not set the space as default. - space::MallocSpace* CreateMallocSpaceFromMemMap(MemMap* mem_map, size_t initial_size, - size_t growth_limit, size_t capacity, - const char* name, bool can_move_objects); + space::MallocSpace* CreateMallocSpaceFromMemMap(MemMap* mem_map, + size_t initial_size, + size_t growth_limit, + size_t capacity, + const char* name, + bool can_move_objects); // Given the current contents of the alloc space, increase the allowed heap footprint to match // the target utilization ratio. This should only be called immediately after a full garbage @@ -883,21 +928,26 @@ class Heap { SHARED_REQUIRES(Locks::heap_bitmap_lock_); // Swap the allocation stack with the live stack. - void SwapStacks(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_); + void SwapStacks() SHARED_REQUIRES(Locks::mutator_lock_); // Clear cards and update the mod union table. When process_alloc_space_cards is true, // if clear_alloc_space_cards is true, then we clear cards instead of ageing them. We do // not process the alloc space if process_alloc_space_cards is false. - void ProcessCards(TimingLogger* timings, bool use_rem_sets, bool process_alloc_space_cards, + void ProcessCards(TimingLogger* timings, + bool use_rem_sets, + bool process_alloc_space_cards, bool clear_alloc_space_cards); // Push an object onto the allocation stack. void PushOnAllocationStack(Thread* self, mirror::Object** obj) - SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!*gc_complete_lock_, !*pending_task_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) + REQUIRES(!*gc_complete_lock_, !*pending_task_lock_); void PushOnAllocationStackWithInternalGC(Thread* self, mirror::Object** obj) - SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!*gc_complete_lock_, !*pending_task_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) + REQUIRES(!*gc_complete_lock_, !*pending_task_lock_); void PushOnThreadLocalAllocationStackWithInternalGC(Thread* thread, mirror::Object** obj) - SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!*gc_complete_lock_, !*pending_task_lock_); + SHARED_REQUIRES(Locks::mutator_lock_) + REQUIRES(!*gc_complete_lock_, !*pending_task_lock_); void ClearConcurrentGCRequest(); void ClearPendingTrim(Thread* self) REQUIRES(!*pending_task_lock_); @@ -1257,7 +1307,6 @@ class Heap { friend class VerifyReferenceCardVisitor; friend class VerifyReferenceVisitor; friend class VerifyObjectVisitor; - friend class ScopedHeapFill; friend class space::SpaceTest; class AllocationTimer { diff --git a/runtime/jit/jit_code_cache_test.cc b/runtime/jit/jit_code_cache_test.cc index 555ad7c1d3..a6cbb710af 100644 --- a/runtime/jit/jit_code_cache_test.cc +++ b/runtime/jit/jit_code_cache_test.cc @@ -50,7 +50,7 @@ TEST_F(JitCodeCacheTest, TestCoverage) { ASSERT_TRUE(code_cache->ContainsCodePtr(reserved_code)); ASSERT_EQ(code_cache->NumMethods(), 1u); ClassLinker* const cl = Runtime::Current()->GetClassLinker(); - ArtMethod* method = &cl->AllocArtMethodArray(soa.Self(), 1)->At(0, 0); + ArtMethod* method = &cl->AllocArtMethodArray(soa.Self(), 1)->At(0); ASSERT_FALSE(code_cache->ContainsMethod(method)); method->SetEntryPointFromQuickCompiledCode(reserved_code); ASSERT_TRUE(code_cache->ContainsMethod(method)); diff --git a/runtime/length_prefixed_array.h b/runtime/length_prefixed_array.h index 82176e376d..2b2e8d34d2 100644 --- a/runtime/length_prefixed_array.h +++ b/runtime/length_prefixed_array.h @@ -48,16 +48,22 @@ class LengthPrefixedArray { return offsetof(LengthPrefixedArray<T>, data_) + index * element_size; } + // Alignment is the caller's responsibility. static size_t ComputeSize(size_t num_elements, size_t element_size = sizeof(T)) { - return sizeof(LengthPrefixedArray<T>) + num_elements * element_size; + return OffsetOfElement(num_elements, element_size); } uint64_t Length() const { return length_; } + // Update the length but does not reallocate storage. + void SetLength(uint64_t length) { + length_ = length; + } + private: - uint64_t length_; // 64 bits for padding reasons. + uint64_t length_; // 64 bits for 8 byte alignment of data_. uint8_t data_[0]; }; diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 701ba4a78b..6af90bbc65 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -824,6 +824,34 @@ void Class::PopulateEmbeddedImtAndVTable(ArtMethod* const (&methods)[kImtSize], } } +class ReadBarrierOnNativeRootsVisitor { + public: + void operator()(mirror::Object* obj ATTRIBUTE_UNUSED, + MemberOffset offset ATTRIBUTE_UNUSED, + bool is_static ATTRIBUTE_UNUSED) const {} + + void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const + SHARED_REQUIRES(Locks::mutator_lock_) { + if (!root->IsNull()) { + VisitRoot(root); + } + } + + void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const + SHARED_REQUIRES(Locks::mutator_lock_) { + mirror::Object* old_ref = root->AsMirrorPtr(); + mirror::Object* new_ref = ReadBarrier::BarrierForRoot(root); + if (old_ref != new_ref) { + // Update the field atomically. This may fail if mutator updates before us, but it's ok. + auto* atomic_root = + reinterpret_cast<Atomic<mirror::CompressedReference<mirror::Object>>*>(root); + atomic_root->CompareExchangeStrongSequentiallyConsistent( + mirror::CompressedReference<mirror::Object>::FromMirrorPtr(old_ref), + mirror::CompressedReference<mirror::Object>::FromMirrorPtr(new_ref)); + } + } +}; + // The pre-fence visitor for Class::CopyOf(). class CopyClassVisitor { public: @@ -842,6 +870,10 @@ class CopyClassVisitor { mirror::Class::SetStatus(h_new_class_obj, Class::kStatusResolving, self_); h_new_class_obj->PopulateEmbeddedImtAndVTable(imt_, pointer_size_); h_new_class_obj->SetClassSize(new_length_); + // Visit all of the references to make sure there is no from space references in the native + // roots. + h_new_class_obj->VisitReferences<true>(h_new_class_obj->GetClass(), + ReadBarrierOnNativeRootsVisitor()); } private: diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index f13893658c..513ab37033 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -1205,7 +1205,8 @@ class MANAGED Class FINAL : public Object { // listed in ifields; fields declared by a superclass are listed in // the superclass's Class.ifields. // - // ArtField arrays are allocated as an array of fields, and not an array of fields pointers. + // ArtFields are allocated as a length prefixed ArtField array, and not an array of pointers to + // ArtFields. uint64_t ifields_; // Static fields length-prefixed array. diff --git a/runtime/stride_iterator.h b/runtime/stride_iterator.h index c69f30ed5f..a9da51ba29 100644 --- a/runtime/stride_iterator.h +++ b/runtime/stride_iterator.h @@ -31,7 +31,7 @@ class StrideIterator : public std::iterator<std::forward_iterator_tag, T> { StrideIterator(T* ptr, size_t stride) : ptr_(reinterpret_cast<uintptr_t>(ptr)), - stride_(reinterpret_cast<uintptr_t>(stride)) {} + stride_(stride) {} bool operator==(const StrideIterator& other) const { DCHECK_EQ(stride_, other.stride_); @@ -48,17 +48,22 @@ class StrideIterator : public std::iterator<std::forward_iterator_tag, T> { } StrideIterator operator++(int) { - auto temp = *this; + StrideIterator<T> temp = *this; ptr_ += stride_; return temp; } StrideIterator operator+(ssize_t delta) const { - auto temp = *this; - temp.ptr_ += static_cast<ssize_t>(stride_) * delta; + StrideIterator<T> temp = *this; + temp += delta; return temp; } + StrideIterator& operator+=(ssize_t delta) { + ptr_ += static_cast<ssize_t>(stride_) * delta; + return *this; + } + T& operator*() const { return *reinterpret_cast<T*>(ptr_); } diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc index 7fe8bb93ff..b86a4c8d25 100644 --- a/runtime/verifier/reg_type.cc +++ b/runtime/verifier/reg_type.cc @@ -16,6 +16,7 @@ #include "reg_type-inl.h" +#include "base/bit_vector-inl.h" #include "base/casts.h" #include "class_linker-inl.h" #include "dex_file-inl.h" @@ -309,13 +310,17 @@ PreciseReferenceType::PreciseReferenceType(mirror::Class* klass, const std::stri std::string UnresolvedMergedType::Dump() const { std::stringstream result; - std::set<uint16_t> types = GetMergedTypes(); - result << "UnresolvedMergedReferences("; - auto it = types.begin(); - result << reg_type_cache_->GetFromId(*it).Dump(); - for (++it; it != types.end(); ++it) { - result << ", "; - result << reg_type_cache_->GetFromId(*it).Dump(); + result << "UnresolvedMergedReferences(" << GetResolvedPart().Dump() << " | "; + const BitVector& types = GetUnresolvedTypes(); + + bool first = true; + for (uint32_t idx : types.Indexes()) { + if (!first) { + result << ", "; + } else { + first = false; + } + result << reg_type_cache_->GetFromId(idx).Dump(); } result << ")"; return result.str(); @@ -492,32 +497,6 @@ bool UnresolvedType::IsNonZeroReferenceTypes() const { return true; } -std::set<uint16_t> UnresolvedMergedType::GetMergedTypes() const { - std::pair<uint16_t, uint16_t> refs = GetTopMergedTypes(); - const RegType& left = reg_type_cache_->GetFromId(refs.first); - const RegType& right = reg_type_cache_->GetFromId(refs.second); - - std::set<uint16_t> types; - if (left.IsUnresolvedMergedReference()) { - types = down_cast<const UnresolvedMergedType*>(&left)->GetMergedTypes(); - } else { - types.insert(refs.first); - } - if (right.IsUnresolvedMergedReference()) { - std::set<uint16_t> right_types = - down_cast<const UnresolvedMergedType*>(&right)->GetMergedTypes(); - types.insert(right_types.begin(), right_types.end()); - } else { - types.insert(refs.second); - } - if (kIsDebugBuild) { - for (const auto& type : types) { - CHECK(!reg_type_cache_->GetFromId(type).IsUnresolvedMergedReference()); - } - } - return types; -} - const RegType& RegType::GetSuperClass(RegTypeCache* cache) const { if (!IsUnresolvedTypes()) { mirror::Class* super_klass = GetClass()->GetSuperClass(); @@ -803,12 +782,24 @@ void UnresolvedUninitializedRefType::CheckInvariants() const { CHECK(klass_.IsNull()) << *this; } +UnresolvedMergedType::UnresolvedMergedType(const RegType& resolved, + const BitVector& unresolved, + const RegTypeCache* reg_type_cache, + uint16_t cache_id) + : UnresolvedType("", cache_id), + reg_type_cache_(reg_type_cache), + resolved_part_(resolved), + unresolved_types_(unresolved, false, unresolved.GetAllocator()) { + if (kIsDebugBuild) { + CheckInvariants(); + } +} void UnresolvedMergedType::CheckInvariants() const { // Unresolved merged types: merged types should be defined. CHECK(descriptor_.empty()) << *this; CHECK(klass_.IsNull()) << *this; - CHECK_NE(merged_types_.first, 0U) << *this; - CHECK_NE(merged_types_.second, 0U) << *this; + CHECK(resolved_part_.IsReferenceTypes()); + CHECK(!resolved_part_.IsUnresolvedTypes()); } void UnresolvedReferenceType::CheckInvariants() const { diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h index 4893088832..2834a9a54a 100644 --- a/runtime/verifier/reg_type.h +++ b/runtime/verifier/reg_type.h @@ -22,6 +22,7 @@ #include <set> #include <string> +#include "base/bit_vector.h" #include "base/macros.h" #include "base/mutex.h" #include "gc_root.h" @@ -230,6 +231,14 @@ class RegType { // from another. const RegType& Merge(const RegType& incoming_type, RegTypeCache* reg_types) const SHARED_REQUIRES(Locks::mutator_lock_); + // Same as above, but also handles the case where incoming_type == this. + const RegType& SafeMerge(const RegType& incoming_type, RegTypeCache* reg_types) const + SHARED_REQUIRES(Locks::mutator_lock_) { + if (Equals(incoming_type)) { + return *this; + } + return Merge(incoming_type, reg_types); + } /* * A basic Join operation on classes. For a pair of types S and T the Join, @@ -868,30 +877,23 @@ class UnresolvedSuperClass FINAL : public UnresolvedType { const RegTypeCache* const reg_type_cache_; }; -// A merge of two unresolved types. If the types were resolved this may be -// Conflict or another -// known ReferenceType. +// A merge of unresolved (and resolved) types. If the types were resolved this may be +// Conflict or another known ReferenceType. class UnresolvedMergedType FINAL : public UnresolvedType { public: - UnresolvedMergedType(uint16_t left_id, uint16_t right_id, + // Note: the constructor will copy the unresolved BitVector, not use it directly. + UnresolvedMergedType(const RegType& resolved, const BitVector& unresolved, const RegTypeCache* reg_type_cache, uint16_t cache_id) - SHARED_REQUIRES(Locks::mutator_lock_) - : UnresolvedType("", cache_id), - reg_type_cache_(reg_type_cache), - merged_types_(left_id, right_id) { - if (kIsDebugBuild) { - CheckInvariants(); - } - } + SHARED_REQUIRES(Locks::mutator_lock_); - // The top of a tree of merged types. - std::pair<uint16_t, uint16_t> GetTopMergedTypes() const { - DCHECK(IsUnresolvedMergedReference()); - return merged_types_; + // The resolved part. See description below. + const RegType& GetResolvedPart() const { + return resolved_part_; + } + // The unresolved part. + const BitVector& GetUnresolvedTypes() const { + return unresolved_types_; } - - // The complete set of merged types. - std::set<uint16_t> GetMergedTypes() const; bool IsUnresolvedMergedReference() const OVERRIDE { return true; } @@ -903,7 +905,16 @@ class UnresolvedMergedType FINAL : public UnresolvedType { void CheckInvariants() const SHARED_REQUIRES(Locks::mutator_lock_); const RegTypeCache* const reg_type_cache_; - const std::pair<uint16_t, uint16_t> merged_types_; + + // The original implementation of merged types was a binary tree. Collection of the flattened + // types ("leaves") can be expensive, so we store the expanded list now, as two components: + // 1) A resolved component. We use Zero when there is no resolved component, as that will be + // an identity merge. + // 2) A bitvector of the unresolved reference types. A bitvector was chosen with the assumption + // that there should not be too many types in flight in practice. (We also bias the index + // against the index of Zero, which is one of the later default entries in any cache.) + const RegType& resolved_part_; + const BitVector unresolved_types_; }; std::ostream& operator<<(std::ostream& os, const RegType& rhs) diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc index 4469e64130..e14306c0ae 100644 --- a/runtime/verifier/reg_type_cache.cc +++ b/runtime/verifier/reg_type_cache.cc @@ -317,39 +317,62 @@ void RegTypeCache::CreatePrimitiveAndSmallConstantTypes() { } const RegType& RegTypeCache::FromUnresolvedMerge(const RegType& left, const RegType& right) { - std::set<uint16_t> types; + BitVector types(1, // Allocate at least a word. + true, // Is expandable. + Allocator::GetMallocAllocator()); // TODO: Arenas in the verifier. + const RegType* left_resolved; if (left.IsUnresolvedMergedReference()) { - RegType& non_const(const_cast<RegType&>(left)); - types = (down_cast<UnresolvedMergedType*>(&non_const))->GetMergedTypes(); + const UnresolvedMergedType* left_merge = down_cast<const UnresolvedMergedType*>(&left); + types.Copy(&left_merge->GetUnresolvedTypes()); + left_resolved = &left_merge->GetResolvedPart(); + } else if (left.IsUnresolvedTypes()) { + types.SetBit(left.GetId()); + left_resolved = &Zero(); } else { - types.insert(left.GetId()); + left_resolved = &left; } + + const RegType* right_resolved; if (right.IsUnresolvedMergedReference()) { - RegType& non_const(const_cast<RegType&>(right)); - std::set<uint16_t> right_types = (down_cast<UnresolvedMergedType*>(&non_const))->GetMergedTypes(); - types.insert(right_types.begin(), right_types.end()); + const UnresolvedMergedType* right_merge = down_cast<const UnresolvedMergedType*>(&right); + types.Union(&right_merge->GetUnresolvedTypes()); + right_resolved = &right_merge->GetResolvedPart(); + } else if (right.IsUnresolvedTypes()) { + types.SetBit(right.GetId()); + right_resolved = &Zero(); } else { - types.insert(right.GetId()); + right_resolved = &right; + } + + // Merge the resolved parts. Left and right might be equal, so use SafeMerge. + const RegType& resolved_parts_merged = left_resolved->SafeMerge(*right_resolved, this); + // If we get a conflict here, the merge result is a conflict, not an unresolved merge type. + if (resolved_parts_merged.IsConflict()) { + return Conflict(); } + // Check if entry already exists. for (size_t i = primitive_count_; i < entries_.size(); i++) { const RegType* cur_entry = entries_[i]; if (cur_entry->IsUnresolvedMergedReference()) { - std::set<uint16_t> cur_entry_types = - (down_cast<const UnresolvedMergedType*>(cur_entry))->GetMergedTypes(); - if (cur_entry_types == types) { + const UnresolvedMergedType* cmp_type = down_cast<const UnresolvedMergedType*>(cur_entry); + const RegType& resolved_part = cmp_type->GetResolvedPart(); + const BitVector& unresolved_part = cmp_type->GetUnresolvedTypes(); + // Use SameBitsSet. "types" is expandable to allow merging in the components, but the + // BitVector in the final RegType will be made non-expandable. + if (&resolved_part == &resolved_parts_merged && + types.SameBitsSet(&unresolved_part)) { return *cur_entry; } } } + // Create entry. - RegType* entry = new UnresolvedMergedType(left.GetId(), right.GetId(), this, entries_.size()); + RegType* entry = new UnresolvedMergedType(resolved_parts_merged, + types, + this, + entries_.size()); AddEntry(entry); - if (kIsDebugBuild) { - UnresolvedMergedType* tmp_entry = down_cast<UnresolvedMergedType*>(entry); - std::set<uint16_t> check_types = tmp_entry->GetMergedTypes(); - CHECK(check_types == types); - } return *entry; } diff --git a/runtime/verifier/reg_type_test.cc b/runtime/verifier/reg_type_test.cc index 2fecc8b25f..971b1f5d0f 100644 --- a/runtime/verifier/reg_type_test.cc +++ b/runtime/verifier/reg_type_test.cc @@ -18,6 +18,7 @@ #include <set> +#include "base/bit_vector.h" #include "base/casts.h" #include "common_runtime_test.h" #include "reg_type_cache-inl.h" @@ -421,7 +422,7 @@ TEST_F(RegTypeReferenceTest, Dump) { EXPECT_EQ(expected, resolved_unintialiesd.Dump()); expected = "Unresolved And Uninitialized Reference: java.lang.DoesNotExist Allocation PC: 12"; EXPECT_EQ(expected, unresolved_unintialized.Dump()); - expected = "UnresolvedMergedReferences(Unresolved Reference: java.lang.DoesNotExist, Unresolved Reference: java.lang.DoesNotExistEither)"; + expected = "UnresolvedMergedReferences(Zero/null | Unresolved Reference: java.lang.DoesNotExist, Unresolved Reference: java.lang.DoesNotExistEither)"; EXPECT_EQ(expected, unresolved_merged.Dump()); } @@ -477,9 +478,10 @@ TEST_F(RegTypeReferenceTest, Merging) { EXPECT_TRUE(merged.IsUnresolvedMergedReference()); RegType& merged_nonconst = const_cast<RegType&>(merged); - std::set<uint16_t> merged_ids = (down_cast<UnresolvedMergedType*>(&merged_nonconst))->GetMergedTypes(); - EXPECT_EQ(ref_type_0.GetId(), *(merged_ids.begin())); - EXPECT_EQ(ref_type_1.GetId(), *((++merged_ids.begin()))); + const BitVector& unresolved_parts = + down_cast<UnresolvedMergedType*>(&merged_nonconst)->GetUnresolvedTypes(); + EXPECT_TRUE(unresolved_parts.IsBitSet(ref_type_0.GetId())); + EXPECT_TRUE(unresolved_parts.IsBitSet(ref_type_1.GetId())); } TEST_F(RegTypeTest, MergingFloat) { diff --git a/test/004-ThreadStress/src/Main.java b/test/004-ThreadStress/src/Main.java index 6e7d5b6658..1db7cc8385 100644 --- a/test/004-ThreadStress/src/Main.java +++ b/test/004-ThreadStress/src/Main.java @@ -32,6 +32,7 @@ import java.util.Set; // // ThreadStress command line parameters: // -n X ............ number of threads +// -d X ............ number of daemon threads // -o X ............ number of overall operations // -t X ............ number of operations per thread // --dumpmap ....... print the frequency map @@ -301,6 +302,7 @@ public class Main implements Runnable { public static void parseAndRun(String[] args) throws Exception { int numberOfThreads = -1; + int numberOfDaemons = -1; int totalOperations = -1; int operationsPerThread = -1; Object lock = new Object(); @@ -312,6 +314,9 @@ public class Main implements Runnable { if (args[i].equals("-n")) { i++; numberOfThreads = Integer.parseInt(args[i]); + } else if (args[i].equals("-d")) { + i++; + numberOfDaemons = Integer.parseInt(args[i]); } else if (args[i].equals("-o")) { i++; totalOperations = Integer.parseInt(args[i]); @@ -338,6 +343,10 @@ public class Main implements Runnable { numberOfThreads = 5; } + if (numberOfDaemons == -1) { + numberOfDaemons = 3; + } + if (totalOperations == -1) { totalOperations = 1000; } @@ -355,14 +364,16 @@ public class Main implements Runnable { System.out.println(frequencyMap); } - runTest(numberOfThreads, operationsPerThread, lock, frequencyMap); + runTest(numberOfThreads, numberOfDaemons, operationsPerThread, lock, frequencyMap); } - public static void runTest(final int numberOfThreads, final int operationsPerThread, - final Object lock, Map<Operation, Double> frequencyMap) - throws Exception { - // Each thread is going to do operationsPerThread - // operations. The distribution of operations is determined by + public static void runTest(final int numberOfThreads, final int numberOfDaemons, + final int operationsPerThread, final Object lock, + Map<Operation, Double> frequencyMap) throws Exception { + // Each normal thread is going to do operationsPerThread + // operations. Each daemon thread will loop over all + // the operations and will not stop. + // The distribution of operations is determined by // the Operation.frequency values. We fill out an Operation[] // for each thread with the operations it is to perform. The // Operation[] is shuffled so that there is more random @@ -371,7 +382,9 @@ public class Main implements Runnable { // Fill in the Operation[] array for each thread by laying // down references to operation according to their desired // frequency. - final Main[] threadStresses = new Main[numberOfThreads]; + // The first numberOfThreads elements are normal threads, the last + // numberOfDaemons elements are daemon threads. + final Main[] threadStresses = new Main[numberOfThreads + numberOfDaemons]; for (int t = 0; t < threadStresses.length; t++) { Operation[] operations = new Operation[operationsPerThread]; int o = 0; @@ -388,9 +401,10 @@ public class Main implements Runnable { } } } - // Randomize the oepration order + // Randomize the operation order Collections.shuffle(Arrays.asList(operations)); - threadStresses[t] = new Main(lock, t, operations); + threadStresses[t] = t < numberOfThreads ? new Main(lock, t, operations) : + new Daemon(lock, t, operations); } // Enable to dump operation counts per thread to make sure its @@ -459,6 +473,14 @@ public class Main implements Runnable { notifier.start(); } + // Create and start the daemon threads. + for (int r = 0; r < numberOfDaemons; r++) { + Main daemon = threadStresses[numberOfThreads + r]; + Thread t = new Thread(daemon, "Daemon thread " + daemon.id); + t.setDaemon(true); + t.start(); + } + for (int r = 0; r < runners.length; r++) { runners[r].start(); } @@ -467,9 +489,9 @@ public class Main implements Runnable { } } - private final Operation[] operations; + protected final Operation[] operations; private final Object lock; - private final int id; + protected final int id; private int nextOperation; @@ -503,4 +525,33 @@ public class Main implements Runnable { } } + private static class Daemon extends Main { + private Daemon(Object lock, int id, Operation[] operations) { + super(lock, id, operations); + } + + public void run() { + try { + if (DEBUG) { + System.out.println("Starting ThreadStress Daemon " + id); + } + int i = 0; + while (true) { + Operation operation = operations[i]; + if (DEBUG) { + System.out.println("ThreadStress Daemon " + id + + " operation " + i + + " is " + operation); + } + operation.perform(); + i = (i + 1) % operations.length; + } + } finally { + if (DEBUG) { + System.out.println("Finishing ThreadStress Daemon for " + id); + } + } + } + } + } diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt index 728ccea256..884f280a12 100644 --- a/test/800-smali/expected.txt +++ b/test/800-smali/expected.txt @@ -37,4 +37,5 @@ b/22411633 (3) b/22411633 (4) b/22411633 (5) b/22777307 +b/22881413 Done! diff --git a/test/800-smali/smali/b_22881413.smali b/test/800-smali/smali/b_22881413.smali new file mode 100644 index 0000000000..29dd82a358 --- /dev/null +++ b/test/800-smali/smali/b_22881413.smali @@ -0,0 +1,295 @@ +.class public LB22881413; +.super Ljava/lang/Object; + +# A couple of fields to allow "loading" resolved and unresolved types. Use non-final classes to +# avoid automatically getting precise reference types. +.field private static res1:Ljava/lang/Number; +.field private static res2:Ljava/lang/ClassLoader; +.field private static res3:Ljava/lang/Package; +.field private static res4:Ljava/lang/RuntimeException; +.field private static res5:Ljava/lang/Exception; +.field private static res6:Ljava/util/ArrayList; +.field private static res7:Ljava/util/LinkedList; +.field private static res8:Ljava/lang/Thread; +.field private static res9:Ljava/lang/ThreadGroup; +.field private static res10:Ljava/lang/Runtime; + +.field private static unres1:La/b/c/d1; +.field private static unres2:La/b/c/d2; +.field private static unres3:La/b/c/d3; +.field private static unres4:La/b/c/d4; +.field private static unres5:La/b/c/d5; +.field private static unres6:La/b/c/d6; +.field private static unres7:La/b/c/d7; +.field private static unres8:La/b/c/d8; +.field private static unres9:La/b/c/d9; +.field private static unres10:La/b/c/d10; + +.field private static unresBase0:La/b/c/dBase0; +.field private static unresBase1:La/b/c/dBase1; +.field private static unresBase2:La/b/c/dBase2; +.field private static unresBase3:La/b/c/dBase3; +.field private static unresBase4:La/b/c/dBase4; +.field private static unresBase5:La/b/c/dBase5; +.field private static unresBase6:La/b/c/dBase6; +.field private static unresBase7:La/b/c/dBase7; +.field private static unresBase8:La/b/c/dBase8; + +# Empty, ignore this. We want to see if the other method can be verified in a reasonable amount of +# time. +.method public static run()V +.registers 2 + return-void +.end method + +.method public static foo(IZZ) V +.registers 11 + # v8 = int, v9 = boolean, v10 = boolean + + sget-object v0, LB22881413;->unresBase0:La/b/c/dBase0; + +# Test an UnresolvedUninitializedReference type. + new-instance v0, La/b/c/dBaseInit; + + const v1, 0 + const v2, 0 + +# We're trying to create something like this (with more loops to amplify things). +# +# v0 = Unresolved1 +# while (something) { +# +# [Repeatedly] +# if (cond) { +# v0 = ResolvedX; +# } else { +# v0 = UnresolvedX; +# } +# +# v0 = Unresolved2 +# }; +# +# Important points: +# 1) Use a while, so that the end of the loop is a goto. That way, the merging of outer-loop +# unresolved classes is postponed. +# 2) Put the else cases after all if cases. That way there are backward gotos that will lead +# to stabilization loops in the body. +# + +:Loop1 + + const v6, 0 + add-int/lit16 v8, v8, -1 + if-ge v8, v6, :Loop1End + +:Loop2 + + const v6, 0 + add-int/lit16 v8, v8, -1 + if-ge v8, v6, :Loop2End + +:Loop3 + + const v6, 0 + add-int/lit16 v8, v8, -1 + if-ge v8, v6, :Loop3End + +:Loop4 + + const v6, 0 + add-int/lit16 v8, v8, -1 + if-ge v8, v6, :Loop4End + +:Loop5 + + const v6, 0 + add-int/lit16 v8, v8, -1 + if-ge v8, v6, :Loop5End + +:Loop6 + + const v6, 0 + add-int/lit16 v8, v8, -1 + if-ge v8, v6, :Loop6End + +:Loop7 + + const v6, 0 + add-int/lit16 v8, v8, -1 + if-ge v8, v6, :Loop7End + +:Loop8 + + const v6, 0 + add-int/lit16 v8, v8, -1 + if-ge v8, v6, :Loop8End + +# Prototype: +# +# if-eqz v9, :ElseX +# sget-object v0, LB22881413;->res1:Ljava/lang/Number; +#:JoinX +# +# And somewhere at the end +# +#:ElseX +# sget-object v0, LB22881413;->unresX:La/b/c/dX; +# goto :JoinX +# +# + + if-eqz v10, :Join1 + if-eqz v9, :Else1 + sget-object v0, LB22881413;->res1:Ljava/lang/Number; +:Join1 + + + if-eqz v10, :Join2 + if-eqz v9, :Else2 + sget-object v0, LB22881413;->res2:Ljava/lang/ClassLoader; +:Join2 + + + if-eqz v10, :Join3 + if-eqz v9, :Else3 + sget-object v0, LB22881413;->res3:Ljava/lang/Package; +:Join3 + + + if-eqz v10, :Join4 + if-eqz v9, :Else4 + sget-object v0, LB22881413;->res4:Ljava/lang/RuntimeException; +:Join4 + + + if-eqz v10, :Join5 + if-eqz v9, :Else5 + sget-object v0, LB22881413;->res5:Ljava/lang/Exception; +:Join5 + + + if-eqz v10, :Join6 + if-eqz v9, :Else6 + sget-object v0, LB22881413;->res6:Ljava/util/ArrayList; +:Join6 + + + if-eqz v10, :Join7 + if-eqz v9, :Else7 + sget-object v0, LB22881413;->res7:Ljava/util/LinkedList; +:Join7 + + + if-eqz v10, :Join8 + if-eqz v9, :Else8 + sget-object v0, LB22881413;->res8:Ljava/lang/Thread; +:Join8 + + + if-eqz v10, :Join9 + if-eqz v9, :Else9 + sget-object v0, LB22881413;->res9:Ljava/lang/ThreadGroup; +:Join9 + + + if-eqz v10, :Join10 + if-eqz v9, :Else10 + sget-object v0, LB22881413;->res10:Ljava/lang/Runtime; +:Join10 + + + goto :InnerMostLoopEnd + +:Else1 + sget-object v0, LB22881413;->unres1:La/b/c/d1; + goto :Join1 + +:Else2 + sget-object v0, LB22881413;->unres2:La/b/c/d2; + goto :Join2 + +:Else3 + sget-object v0, LB22881413;->unres3:La/b/c/d3; + goto :Join3 + +:Else4 + sget-object v0, LB22881413;->unres4:La/b/c/d4; + goto :Join4 + +:Else5 + sget-object v0, LB22881413;->unres5:La/b/c/d5; + goto :Join5 + +:Else6 + sget-object v0, LB22881413;->unres6:La/b/c/d6; + goto :Join6 + +:Else7 + sget-object v0, LB22881413;->unres7:La/b/c/d7; + goto :Join7 + +:Else8 + sget-object v0, LB22881413;->unres8:La/b/c/d8; + goto :Join8 + +:Else9 + sget-object v0, LB22881413;->unres9:La/b/c/d9; + goto :Join9 + +:Else10 + sget-object v0, LB22881413;->unres10:La/b/c/d10; + goto :Join10 + +:InnerMostLoopEnd + + # Loop 8 end of body. + sget-object v0, LB22881413;->unresBase8:La/b/c/dBase8; + goto :Loop8 + +:Loop8End + + # Loop 7 end of body. + sget-object v0, LB22881413;->unresBase7:La/b/c/dBase7; + goto :Loop7 + +:Loop7End + + # Loop 6 end of body. + sget-object v0, LB22881413;->unresBase6:La/b/c/dBase6; + goto :Loop6 + +:Loop6End + + # Loop 5 end of body + sget-object v0, LB22881413;->unresBase5:La/b/c/dBase5; + goto :Loop5 + +:Loop5End + + # Loop 4 end of body + sget-object v0, LB22881413;->unresBase4:La/b/c/dBase4; + goto :Loop4 + +:Loop4End + + # Loop 3 end of body + sget-object v0, LB22881413;->unresBase3:La/b/c/dBase3; + goto :Loop3 + +:Loop3End + + # Loop 2 end of body + sget-object v0, LB22881413;->unresBase2:La/b/c/dBase2; + goto :Loop2 + +:Loop2End + + # Loop 1 end of body + sget-object v0, LB22881413;->unresBase1:La/b/c/dBase1; + goto :Loop1 + +:Loop1End + + return-void + +.end method diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java index 438e21481c..e1ac7493ae 100644 --- a/test/800-smali/src/Main.java +++ b/test/800-smali/src/Main.java @@ -121,6 +121,7 @@ public class Main { null, null)); testCases.add(new TestCase("b/22777307", "B22777307", "run", null, new InstantiationError(), null)); + testCases.add(new TestCase("b/22881413", "B22881413", "run", null, null, null)); } public void runTests() { diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index 750a29fadd..a1af5774f3 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -338,6 +338,17 @@ if [ "$PREBUILD" = "y" ]; then if [ "x$INSTRUCTION_SET_FEATURES" != "x" ] ; then dex2oat_cmdline="${dex2oat_cmdline} --instruction-set-features=${INSTRUCTION_SET_FEATURES}" fi + + # Add in a timeout. This is important for testing the compilation/verification time of + # pathological cases. + # Note: as we don't know how decent targets are (e.g., emulator), only do this on the host for + # now. We should try to improve this. + # The current value is rather arbitrary. run-tests should compile quickly. + if [ "$HOST" != "n" ]; then + # Use SIGRTMIN+2 to try to dump threads. + # Use -k 1m to SIGKILL it a minute later if it hasn't ended. + dex2oat_cmdline="timeout -k 1m -s SIGRTMIN+2 1m ${dex2oat_cmdline}" + fi fi DALVIKVM_ISA_FEATURES_ARGS="" |