diff options
180 files changed, 7930 insertions, 2452 deletions
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk index fc4dd55d67..123bcaa3bd 100644 --- a/build/Android.common_build.mk +++ b/build/Android.common_build.mk @@ -296,8 +296,8 @@ art_asflags := ifdef ART_IMT_SIZE art_cflags += -DIMT_SIZE=$(ART_IMT_SIZE) else - # Default is 64 - art_cflags += -DIMT_SIZE=64 + # Default is 43 + art_cflags += -DIMT_SIZE=43 endif ifeq ($(ART_HEAP_POISONING),true) diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index a14265e30d..c09116ff5d 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -277,6 +277,7 @@ COMPILER_GTEST_COMMON_SRC_FILES := \ compiler/optimizing/suspend_check_test.cc \ compiler/utils/dedupe_set_test.cc \ compiler/utils/intrusive_forward_list_test.cc \ + compiler/utils/string_reference_test.cc \ compiler/utils/swap_space_test.cc \ compiler/utils/test_dex_file_builder_test.cc \ compiler/utils/transform_array_ref_test.cc \ @@ -365,6 +366,7 @@ COMPILER_GTEST_HOST_SRC_FILES_arm64 := \ COMPILER_GTEST_HOST_SRC_FILES_mips := \ $(COMPILER_GTEST_COMMON_SRC_FILES_mips) \ compiler/utils/mips/assembler_mips_test.cc \ + compiler/utils/mips/assembler_mips32r6_test.cc \ COMPILER_GTEST_HOST_SRC_FILES_mips64 := \ $(COMPILER_GTEST_COMMON_SRC_FILES_mips64) \ diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h index 9479ff38be..2a81804f64 100644 --- a/compiler/compiled_method.h +++ b/compiler/compiled_method.h @@ -174,6 +174,7 @@ class LinkerPatch { kCall, kCallRelative, // NOTE: Actual patching is instruction_set-dependent. kType, + kTypeRelative, // NOTE: Actual patching is instruction_set-dependent. kString, kStringRelative, // NOTE: Actual patching is instruction_set-dependent. kDexCacheArray, // NOTE: Actual patching is instruction_set-dependent. @@ -215,6 +216,16 @@ class LinkerPatch { return patch; } + static LinkerPatch RelativeTypePatch(size_t literal_offset, + const DexFile* target_dex_file, + uint32_t pc_insn_offset, + uint32_t target_type_idx) { + LinkerPatch patch(literal_offset, Type::kTypeRelative, target_dex_file); + patch.type_idx_ = target_type_idx; + patch.pc_insn_offset_ = pc_insn_offset; + return patch; + } + static LinkerPatch StringPatch(size_t literal_offset, const DexFile* target_dex_file, uint32_t target_string_idx) { @@ -258,6 +269,7 @@ class LinkerPatch { bool IsPcRelative() const { switch (GetType()) { case Type::kCallRelative: + case Type::kTypeRelative: case Type::kStringRelative: case Type::kDexCacheArray: return true; @@ -274,12 +286,12 @@ class LinkerPatch { } const DexFile* TargetTypeDexFile() const { - DCHECK(patch_type_ == Type::kType); + DCHECK(patch_type_ == Type::kType || patch_type_ == Type::kTypeRelative); return target_dex_file_; } uint32_t TargetTypeIndex() const { - DCHECK(patch_type_ == Type::kType); + DCHECK(patch_type_ == Type::kType || patch_type_ == Type::kTypeRelative); return type_idx_; } @@ -304,7 +316,9 @@ class LinkerPatch { } uint32_t PcInsnOffset() const { - DCHECK(patch_type_ == Type::kStringRelative || patch_type_ == Type::kDexCacheArray); + DCHECK(patch_type_ == Type::kTypeRelative || + patch_type_ == Type::kStringRelative || + patch_type_ == Type::kDexCacheArray); return pc_insn_offset_; } diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h index 26ab281741..7f2e1931d0 100644 --- a/compiler/elf_builder.h +++ b/compiler/elf_builder.h @@ -780,9 +780,9 @@ class ElfBuilder FINAL { EF_MIPS_PIC | EF_MIPS_CPIC | EF_MIPS_ABI_O32 | - features->AsMipsInstructionSetFeatures()->IsR6() - ? EF_MIPS_ARCH_32R6 - : EF_MIPS_ARCH_32R2); + (features->AsMipsInstructionSetFeatures()->IsR6() + ? EF_MIPS_ARCH_32R6 + : EF_MIPS_ARCH_32R2)); break; } case kMips64: { diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc index 72771079be..fdd14be4c4 100644 --- a/compiler/linker/arm64/relative_patcher_arm64.cc +++ b/compiler/linker/arm64/relative_patcher_arm64.cc @@ -211,11 +211,13 @@ void Arm64RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code, if ((insn & 0xfffffc00) == 0x91000000) { // ADD immediate, 64-bit with imm12 == 0 (unset). if (!kEmitCompilerReadBarrier) { - DCHECK(patch.GetType() == LinkerPatch::Type::kStringRelative) << patch.GetType(); + DCHECK(patch.GetType() == LinkerPatch::Type::kStringRelative || + patch.GetType() == LinkerPatch::Type::kTypeRelative) << patch.GetType(); } else { // With the read barrier (non-baker) enabled, it could be kDexCacheArray in the // HLoadString::LoadKind::kDexCachePcRelative case of VisitLoadString(). DCHECK(patch.GetType() == LinkerPatch::Type::kStringRelative || + patch.GetType() == LinkerPatch::Type::kTypeRelative || patch.GetType() == LinkerPatch::Type::kDexCacheArray) << patch.GetType(); } shift = 0u; // No shift for ADD. diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 6d1f94491e..672018b355 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -444,7 +444,7 @@ TEST_F(OatTest, OatHeaderSizeCheck) { EXPECT_EQ(72U, sizeof(OatHeader)); EXPECT_EQ(4U, sizeof(OatMethodOffsets)); EXPECT_EQ(20U, sizeof(OatQuickMethodHeader)); - EXPECT_EQ(132 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints)); + EXPECT_EQ(133 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints)); } TEST_F(OatTest, OatHeaderIsValid) { diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index 42320028e5..cdc7df11b6 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -1066,6 +1066,14 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { target_offset); break; } + case LinkerPatch::Type::kTypeRelative: { + uint32_t target_offset = GetTargetObjectOffset(GetTargetType(patch)); + writer_->relative_patcher_->PatchPcRelativeReference(&patched_code_, + patch, + offset_ + literal_offset, + target_offset); + break; + } case LinkerPatch::Type::kCall: { uint32_t target_offset = GetTargetOffset(patch); PatchCodeAddress(&patched_code_, literal_offset, target_offset); @@ -2187,6 +2195,7 @@ bool OatWriter::OpenDexFiles( oat_dex_file.dex_file_location_checksum_, /* oat_dex_file */ nullptr, verify, + verify, &error_msg)); if (dex_files.back() == nullptr) { LOG(ERROR) << "Failed to open dex file from oat file. File: " << oat_dex_file.GetLocation() diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 6e851bf1ba..12aa15207c 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -146,6 +146,13 @@ uint32_t CodeGenerator::GetArrayLengthOffset(HArrayLength* array_length) { : mirror::Array::LengthOffset().Uint32Value(); } +uint32_t CodeGenerator::GetArrayDataOffset(HArrayGet* array_get) { + DCHECK(array_get->GetType() == Primitive::kPrimChar || !array_get->IsStringCharAt()); + return array_get->IsStringCharAt() + ? mirror::String::ValueOffset().Uint32Value() + : mirror::Array::DataOffset(Primitive::ComponentSize(array_get->GetType())).Uint32Value(); +} + bool CodeGenerator::GoesToNextBlock(HBasicBlock* current, HBasicBlock* next) const { DCHECK_EQ((*block_order_)[current_block_index_], current); return GetNextBlockToEmit() == FirstNonEmptyBlock(next); diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 82a54d2ed1..9364be35ff 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -345,6 +345,11 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { // accessing the String's `count` field in String intrinsics. static uint32_t GetArrayLengthOffset(HArrayLength* array_length); + // Helper that returns the offset of the array's data. + // Note: Besides the normal arrays, we also use the HArrayGet for + // accessing the String's `value` field in String intrinsics. + static uint32_t GetArrayDataOffset(HArrayGet* array_get); + void EmitParallelMoves(Location from1, Location to1, Primitive::Type type1, @@ -449,10 +454,15 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { SlowPathCode* slow_path) = 0; // Check if the desired_string_load_kind is supported. If it is, return it, - // otherwise return a fall-back info that should be used instead. + // otherwise return a fall-back kind that should be used instead. virtual HLoadString::LoadKind GetSupportedLoadStringKind( HLoadString::LoadKind desired_string_load_kind) = 0; + // Check if the desired_class_load_kind is supported. If it is, return it, + // otherwise return a fall-back kind that should be used instead. + virtual HLoadClass::LoadKind GetSupportedLoadClassKind( + HLoadClass::LoadKind desired_class_load_kind) = 0; + // Check if the desired_dispatch_info is supported. If it is, return it, // otherwise return a fall-back info that should be used instead. virtual HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch( @@ -496,6 +506,20 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { LabelType label; }; + // Type patch info used for recording locations of required linker patches and + // target types. The actual type address can be absolute or PC-relative. + // TODO: Consider merging with MethodPatchInfo and StringPatchInfo - all these + // classes contain the dex file, some index and the label. + template <typename LabelType> + struct TypePatchInfo { + TypePatchInfo(const DexFile& df, uint32_t index) + : dex_file(df), type_index(index), label() { } + + const DexFile& dex_file; + uint32_t type_index; + LabelType label; + }; + CodeGenerator(HGraph* graph, size_t number_of_core_registers, size_t number_of_fpu_registers, diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 4fc3b5434b..663c68a17b 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -176,8 +176,11 @@ class BoundsCheckSlowPathARM : public SlowPathCode { locations->InAt(1), Location::RegisterLocation(calling_convention.GetRegisterAt(1)), Primitive::kPrimInt); - arm_codegen->InvokeRuntime( - QUICK_ENTRY_POINT(pThrowArrayBounds), instruction_, instruction_->GetDexPc(), this); + uint32_t entry_point_offset = instruction_->AsBoundsCheck()->IsStringCharAt() + ? QUICK_ENTRY_POINT(pThrowStringBounds) + : QUICK_ENTRY_POINT(pThrowArrayBounds); + arm_codegen->InvokeRuntime(entry_point_offset, instruction_, instruction_->GetDexPc(), this); + CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>(); CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>(); } @@ -427,7 +430,9 @@ class ReadBarrierMarkSlowPathARM : public SlowPathCode { instruction_->IsLoadClass() || instruction_->IsLoadString() || instruction_->IsInstanceOf() || - instruction_->IsCheckCast()) + instruction_->IsCheckCast() || + ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) && + instruction_->GetLocations()->Intrinsified())) << "Unexpected instruction in read barrier marking slow path: " << instruction_->DebugName(); @@ -490,8 +495,12 @@ class ReadBarrierForHeapReferenceSlowPathARM : public SlowPathCode { Register reg_out = out_.AsRegister<Register>(); DCHECK(locations->CanCall()); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out)); - DCHECK(!instruction_->IsInvoke() || - (instruction_->IsInvokeStaticOrDirect() && + DCHECK(instruction_->IsInstanceFieldGet() || + instruction_->IsStaticFieldGet() || + instruction_->IsArrayGet() || + instruction_->IsInstanceOf() || + instruction_->IsCheckCast() || + ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) && instruction_->GetLocations()->Intrinsified())) << "Unexpected instruction in read barrier for heap reference slow path: " << instruction_->DebugName(); @@ -504,7 +513,7 @@ class ReadBarrierForHeapReferenceSlowPathARM : public SlowPathCode { // introduce a copy of it, `index`. Location index = index_; if (index_.IsValid()) { - // Handle `index_` for HArrayGet and intrinsic UnsafeGetObject. + // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics. if (instruction_->IsArrayGet()) { // Compute the actual memory offset and store it in `index`. Register index_reg = index_.AsRegister<Register>(); @@ -552,7 +561,11 @@ class ReadBarrierForHeapReferenceSlowPathARM : public SlowPathCode { "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); __ AddConstant(index_reg, index_reg, offset_); } else { - DCHECK(instruction_->IsInvoke()); + // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile + // intrinsics, `index_` is not shifted by a scale factor of 2 + // (as in the case of ArrayGet), as it is actually an offset + // to an object field within an object. + DCHECK(instruction_->IsInvoke()) << instruction_->DebugName(); DCHECK(instruction_->GetLocations()->Intrinsified()); DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) || (instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile)) @@ -792,6 +805,9 @@ CodeGeneratorARM::CodeGeneratorARM(HGraph* graph, boot_image_string_patches_(StringReferenceValueComparator(), graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), pc_relative_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + boot_image_type_patches_(TypeReferenceValueComparator(), + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), boot_image_address_patches_(std::less<uint32_t>(), graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { // Always save the LR register to mimic Quick. @@ -4286,11 +4302,11 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { Register obj = obj_loc.AsRegister<Register>(); Location index = locations->InAt(1); Location out_loc = locations->Out(); + uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction); Primitive::Type type = instruction->GetType(); switch (type) { case Primitive::kPrimBoolean: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); Register out = out_loc.AsRegister<Register>(); if (index.IsConstant()) { size_t offset = @@ -4304,7 +4320,6 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimByte: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value(); Register out = out_loc.AsRegister<Register>(); if (index.IsConstant()) { size_t offset = @@ -4318,7 +4333,6 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimShort: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value(); Register out = out_loc.AsRegister<Register>(); if (index.IsConstant()) { size_t offset = @@ -4332,7 +4346,6 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimChar: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); Register out = out_loc.AsRegister<Register>(); if (index.IsConstant()) { size_t offset = @@ -4346,7 +4359,6 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimInt: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); Register out = out_loc.AsRegister<Register>(); if (index.IsConstant()) { size_t offset = @@ -4363,7 +4375,6 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { static_assert( sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); // /* HeapReference<Object> */ out = // *(obj + data_offset + index * sizeof(HeapReference<Object>)) if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { @@ -4398,7 +4409,6 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimLong: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); if (index.IsConstant()) { size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; @@ -4411,7 +4421,6 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimFloat: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value(); SRegister out = out_loc.AsFpuRegister<SRegister>(); if (index.IsConstant()) { size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset; @@ -4424,7 +4433,6 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimDouble: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value(); SRegister out = out_loc.AsFpuRegisterPairLow<SRegister>(); if (index.IsConstant()) { size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; @@ -5077,13 +5085,71 @@ void ParallelMoveResolverARM::RestoreScratch(int reg) { __ Pop(static_cast<Register>(reg)); } +HLoadClass::LoadKind CodeGeneratorARM::GetSupportedLoadClassKind( + HLoadClass::LoadKind desired_class_load_kind) { + if (kEmitCompilerReadBarrier) { + switch (desired_class_load_kind) { + case HLoadClass::LoadKind::kBootImageLinkTimeAddress: + case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: + case HLoadClass::LoadKind::kBootImageAddress: + // TODO: Implement for read barrier. + return HLoadClass::LoadKind::kDexCacheViaMethod; + default: + break; + } + } + switch (desired_class_load_kind) { + case HLoadClass::LoadKind::kReferrersClass: + break; + case HLoadClass::LoadKind::kBootImageLinkTimeAddress: + DCHECK(!GetCompilerOptions().GetCompilePic()); + break; + case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: + DCHECK(GetCompilerOptions().GetCompilePic()); + break; + case HLoadClass::LoadKind::kBootImageAddress: + break; + case HLoadClass::LoadKind::kDexCacheAddress: + DCHECK(Runtime::Current()->UseJitCompilation()); + break; + case HLoadClass::LoadKind::kDexCachePcRelative: + DCHECK(!Runtime::Current()->UseJitCompilation()); + // We disable pc-relative load when there is an irreducible loop, as the optimization + // is incompatible with it. + // TODO: Create as many ArmDexCacheArraysBase instructions as needed for methods + // with irreducible loops. + if (GetGraph()->HasIrreducibleLoops()) { + return HLoadClass::LoadKind::kDexCacheViaMethod; + } + break; + case HLoadClass::LoadKind::kDexCacheViaMethod: + break; + } + return desired_class_load_kind; +} + void LocationsBuilderARM::VisitLoadClass(HLoadClass* cls) { - InvokeRuntimeCallingConvention calling_convention; - CodeGenerator::CreateLoadClassLocationSummary( - cls, - Location::RegisterLocation(calling_convention.GetRegisterAt(0)), - Location::RegisterLocation(R0), - /* code_generator_supports_read_barrier */ true); + if (cls->NeedsAccessCheck()) { + InvokeRuntimeCallingConvention calling_convention; + CodeGenerator::CreateLoadClassLocationSummary( + cls, + Location::RegisterLocation(calling_convention.GetRegisterAt(0)), + Location::RegisterLocation(R0), + /* code_generator_supports_read_barrier */ true); + return; + } + + LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier) + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind); + HLoadClass::LoadKind load_kind = cls->GetLoadKind(); + if (load_kind == HLoadClass::LoadKind::kReferrersClass || + load_kind == HLoadClass::LoadKind::kDexCacheViaMethod || + load_kind == HLoadClass::LoadKind::kDexCachePcRelative) { + locations->SetInAt(0, Location::RequiresRegister()); + } + locations->SetOut(Location::RequiresRegister()); } void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) { @@ -5100,37 +5166,97 @@ void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) { Location out_loc = locations->Out(); Register out = out_loc.AsRegister<Register>(); - Register current_method = locations->InAt(0).AsRegister<Register>(); - - if (cls->IsReferrersClass()) { - DCHECK(!cls->CanCallRuntime()); - DCHECK(!cls->MustGenerateClinitCheck()); - // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_ - GenerateGcRootFieldLoad( - cls, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value()); - } else { - // /* GcRoot<mirror::Class>[] */ out = - // current_method.ptr_sized_fields_->dex_cache_resolved_types_ - __ LoadFromOffset(kLoadWord, - out, - current_method, - ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value()); - // /* GcRoot<mirror::Class> */ out = out[type_index] - GenerateGcRootFieldLoad(cls, out_loc, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())); - - if (!cls->IsInDexCache() || cls->MustGenerateClinitCheck()) { - DCHECK(cls->CanCallRuntime()); - SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM( - cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck()); - codegen_->AddSlowPath(slow_path); - if (!cls->IsInDexCache()) { - __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel()); - } - if (cls->MustGenerateClinitCheck()) { - GenerateClassInitializationCheck(slow_path, out); - } else { - __ Bind(slow_path->GetExitLabel()); - } + + bool generate_null_check = false; + switch (cls->GetLoadKind()) { + case HLoadClass::LoadKind::kReferrersClass: { + DCHECK(!cls->CanCallRuntime()); + DCHECK(!cls->MustGenerateClinitCheck()); + // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_ + Register current_method = locations->InAt(0).AsRegister<Register>(); + GenerateGcRootFieldLoad( + cls, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value()); + break; + } + case HLoadClass::LoadKind::kBootImageLinkTimeAddress: { + DCHECK(!kEmitCompilerReadBarrier); + __ LoadLiteral(out, codegen_->DeduplicateBootImageTypeLiteral(cls->GetDexFile(), + cls->GetTypeIndex())); + break; + } + case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: { + DCHECK(!kEmitCompilerReadBarrier); + CodeGeneratorARM::PcRelativePatchInfo* labels = + codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex()); + __ BindTrackedLabel(&labels->movw_label); + __ movw(out, /* placeholder */ 0u); + __ BindTrackedLabel(&labels->movt_label); + __ movt(out, /* placeholder */ 0u); + __ BindTrackedLabel(&labels->add_pc_label); + __ add(out, out, ShifterOperand(PC)); + break; + } + case HLoadClass::LoadKind::kBootImageAddress: { + DCHECK(!kEmitCompilerReadBarrier); + DCHECK_NE(cls->GetAddress(), 0u); + uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress()); + __ LoadLiteral(out, codegen_->DeduplicateBootImageAddressLiteral(address)); + break; + } + case HLoadClass::LoadKind::kDexCacheAddress: { + DCHECK_NE(cls->GetAddress(), 0u); + uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress()); + // 16-bit LDR immediate has a 5-bit offset multiplied by the size and that gives + // a 128B range. To try and reduce the number of literals if we load multiple types, + // simply split the dex cache address to a 128B aligned base loaded from a literal + // and the remaining offset embedded in the load. + static_assert(sizeof(GcRoot<mirror::Class>) == 4u, "Expected GC root to be 4 bytes."); + DCHECK_ALIGNED(cls->GetAddress(), 4u); + constexpr size_t offset_bits = /* encoded bits */ 5 + /* scale */ 2; + uint32_t base_address = address & ~MaxInt<uint32_t>(offset_bits); + uint32_t offset = address & MaxInt<uint32_t>(offset_bits); + __ LoadLiteral(out, codegen_->DeduplicateDexCacheAddressLiteral(base_address)); + // /* GcRoot<mirror::Class> */ out = *(base_address + offset) + GenerateGcRootFieldLoad(cls, out_loc, out, offset); + generate_null_check = !cls->IsInDexCache(); + break; + } + case HLoadClass::LoadKind::kDexCachePcRelative: { + Register base_reg = locations->InAt(0).AsRegister<Register>(); + HArmDexCacheArraysBase* base = cls->InputAt(0)->AsArmDexCacheArraysBase(); + int32_t offset = cls->GetDexCacheElementOffset() - base->GetElementOffset(); + // /* GcRoot<mirror::Class> */ out = *(dex_cache_arrays_base + offset) + GenerateGcRootFieldLoad(cls, out_loc, base_reg, offset); + generate_null_check = !cls->IsInDexCache(); + break; + } + case HLoadClass::LoadKind::kDexCacheViaMethod: { + // /* GcRoot<mirror::Class>[] */ out = + // current_method.ptr_sized_fields_->dex_cache_resolved_types_ + Register current_method = locations->InAt(0).AsRegister<Register>(); + __ LoadFromOffset(kLoadWord, + out, + current_method, + ArtMethod::DexCacheResolvedTypesOffset(kArmPointerSize).Int32Value()); + // /* GcRoot<mirror::Class> */ out = out[type_index] + size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex()); + GenerateGcRootFieldLoad(cls, out_loc, out, offset); + generate_null_check = !cls->IsInDexCache(); + } + } + + if (generate_null_check || cls->MustGenerateClinitCheck()) { + DCHECK(cls->CanCallRuntime()); + SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM( + cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck()); + codegen_->AddSlowPath(slow_path); + if (generate_null_check) { + __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel()); + } + if (cls->MustGenerateClinitCheck()) { + GenerateClassInitializationCheck(slow_path, out); + } else { + __ Bind(slow_path->GetExitLabel()); } } } @@ -5262,6 +5388,7 @@ void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) { uint32_t base_address = address & ~MaxInt<uint32_t>(offset_bits); uint32_t offset = address & MaxInt<uint32_t>(offset_bits); __ LoadLiteral(out, codegen_->DeduplicateDexCacheAddressLiteral(base_address)); + // /* GcRoot<mirror::String> */ out = *(base_address + offset) GenerateGcRootFieldLoad(load, out_loc, out, offset); break; } @@ -5269,6 +5396,7 @@ void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) { Register base_reg = locations->InAt(0).AsRegister<Register>(); HArmDexCacheArraysBase* base = load->InputAt(0)->AsArmDexCacheArraysBase(); int32_t offset = load->GetDexCacheElementOffset() - base->GetElementOffset(); + // /* GcRoot<mirror::String> */ out = *(dex_cache_arrays_base + offset) GenerateGcRootFieldLoad(load, out_loc, base_reg, offset); break; } @@ -6085,8 +6213,9 @@ void CodeGeneratorARM::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instr // /* HeapReference<Object> */ ref = *(obj + offset) Location no_index = Location::NoLocation(); + ScaleFactor no_scale_factor = TIMES_1; GenerateReferenceLoadWithBakerReadBarrier( - instruction, ref, obj, offset, no_index, temp, needs_null_check); + instruction, ref, obj, offset, no_index, no_scale_factor, temp, needs_null_check); } void CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction, @@ -6099,10 +6228,14 @@ void CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instr DCHECK(kEmitCompilerReadBarrier); DCHECK(kUseBakerReadBarrier); + static_assert( + sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), + "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); // /* HeapReference<Object> */ ref = // *(obj + data_offset + index * sizeof(HeapReference<Object>)) + ScaleFactor scale_factor = TIMES_4; GenerateReferenceLoadWithBakerReadBarrier( - instruction, ref, obj, data_offset, index, temp, needs_null_check); + instruction, ref, obj, data_offset, index, scale_factor, temp, needs_null_check); } void CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction, @@ -6110,6 +6243,7 @@ void CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* i Register obj, uint32_t offset, Location index, + ScaleFactor scale_factor, Location temp, bool needs_null_check) { DCHECK(kEmitCompilerReadBarrier); @@ -6164,17 +6298,22 @@ void CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* i // The actual reference load. if (index.IsValid()) { - static_assert( - sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), - "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); - // /* HeapReference<Object> */ ref = - // *(obj + offset + index * sizeof(HeapReference<Object>)) + // Load types involving an "index": ArrayGet and + // UnsafeGetObject/UnsafeGetObjectVolatile intrinsics. + // /* HeapReference<Object> */ ref = *(obj + offset + (index << scale_factor)) if (index.IsConstant()) { size_t computed_offset = - (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + offset; + (index.GetConstant()->AsIntConstant()->GetValue() << scale_factor) + offset; __ LoadFromOffset(kLoadWord, ref_reg, obj, computed_offset); } else { - __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4)); + // Handle the special case of the + // UnsafeGetObject/UnsafeGetObjectVolatile intrinsics, which use + // a register pair as index ("long offset"), of which only the low + // part contains data. + Register index_reg = index.IsRegisterPair() + ? index.AsRegisterPairLow<Register>() + : index.AsRegister<Register>(); + __ add(IP, obj, ShifterOperand(index_reg, LSL, scale_factor)); __ LoadFromOffset(kLoadWord, ref_reg, IP, offset); } } else { @@ -6452,6 +6591,11 @@ CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeStringPatc return NewPcRelativePatch(dex_file, string_index, &pc_relative_string_patches_); } +CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeTypePatch( + const DexFile& dex_file, uint32_t type_index) { + return NewPcRelativePatch(dex_file, type_index, &pc_relative_type_patches_); +} + CodeGeneratorARM::PcRelativePatchInfo* CodeGeneratorARM::NewPcRelativeDexCacheArrayPatch( const DexFile& dex_file, uint32_t element_offset) { return NewPcRelativePatch(dex_file, element_offset, &pc_relative_dex_cache_patches_); @@ -6470,6 +6614,13 @@ Literal* CodeGeneratorARM::DeduplicateBootImageStringLiteral(const DexFile& dex_ [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); }); } +Literal* CodeGeneratorARM::DeduplicateBootImageTypeLiteral(const DexFile& dex_file, + uint32_t type_index) { + return boot_image_type_patches_.GetOrCreate( + TypeReference(&dex_file, type_index), + [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); }); +} + Literal* CodeGeneratorARM::DeduplicateBootImageAddressLiteral(uint32_t address) { bool needs_patch = GetCompilerOptions().GetIncludePatchInformation(); Uint32ToLiteralMap* map = needs_patch ? &boot_image_address_patches_ : &uint32_literals_; @@ -6489,6 +6640,8 @@ void CodeGeneratorARM::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patche /* MOVW+MOVT for each base */ 2u * pc_relative_dex_cache_patches_.size() + boot_image_string_patches_.size() + /* MOVW+MOVT for each base */ 2u * pc_relative_string_patches_.size() + + boot_image_type_patches_.size() + + /* MOVW+MOVT for each base */ 2u * pc_relative_type_patches_.size() + boot_image_address_patches_.size(); linker_patches->reserve(size); for (const auto& entry : method_patches_) { @@ -6564,6 +6717,35 @@ void CodeGeneratorARM::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patche add_pc_offset, string_index)); } + for (const auto& entry : boot_image_type_patches_) { + const TypeReference& target_type = entry.first; + Literal* literal = entry.second; + DCHECK(literal->GetLabel()->IsBound()); + uint32_t literal_offset = literal->GetLabel()->Position(); + linker_patches->push_back(LinkerPatch::TypePatch(literal_offset, + target_type.dex_file, + target_type.type_index)); + } + for (const PcRelativePatchInfo& info : pc_relative_type_patches_) { + const DexFile& dex_file = info.target_dex_file; + uint32_t type_index = info.offset_or_index; + DCHECK(info.add_pc_label.IsBound()); + uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(info.add_pc_label.Position()); + // Add MOVW patch. + DCHECK(info.movw_label.IsBound()); + uint32_t movw_offset = dchecked_integral_cast<uint32_t>(info.movw_label.Position()); + linker_patches->push_back(LinkerPatch::RelativeTypePatch(movw_offset, + &dex_file, + add_pc_offset, + type_index)); + // Add MOVT patch. + DCHECK(info.movt_label.IsBound()); + uint32_t movt_offset = dchecked_integral_cast<uint32_t>(info.movt_label.Position()); + linker_patches->push_back(LinkerPatch::RelativeTypePatch(movt_offset, + &dex_file, + add_pc_offset, + type_index)); + } for (const auto& entry : boot_image_address_patches_) { DCHECK(GetCompilerOptions().GetIncludePatchInformation()); Literal* literal = entry.second; diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index 0020f7b4f4..477c4f18c1 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -24,6 +24,7 @@ #include "parallel_move_resolver.h" #include "utils/arm/assembler_thumb2.h" #include "utils/string_reference.h" +#include "utils/type_reference.h" namespace art { namespace arm { @@ -407,6 +408,11 @@ class CodeGeneratorARM : public CodeGenerator { HLoadString::LoadKind GetSupportedLoadStringKind( HLoadString::LoadKind desired_string_load_kind) OVERRIDE; + // Check if the desired_class_load_kind is supported. If it is, return it, + // otherwise return a fall-back kind that should be used instead. + HLoadClass::LoadKind GetSupportedLoadClassKind( + HLoadClass::LoadKind desired_class_load_kind) OVERRIDE; + // Check if the desired_dispatch_info is supported. If it is, return it, // otherwise return a fall-back info that should be used instead. HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch( @@ -419,10 +425,10 @@ class CodeGeneratorARM : public CodeGenerator { void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE; // The PcRelativePatchInfo is used for PC-relative addressing of dex cache arrays - // and boot image strings. The only difference is the interpretation of the offset_or_index. - // The PC-relative address is loaded with three instructions, MOVW+MOVT - // to load the offset to base_reg and then ADD base_reg, PC. The offset is - // calculated from the ADD's effective PC, i.e. PC+4 on Thumb2. Though we + // and boot image strings/types. The only difference is the interpretation of the + // offset_or_index. The PC-relative address is loaded with three instructions, + // MOVW+MOVT to load the offset to base_reg and then ADD base_reg, PC. The offset + // is calculated from the ADD's effective PC, i.e. PC+4 on Thumb2. Though we // currently emit these 3 instructions together, instruction scheduling could // split this sequence apart, so we keep separate labels for each of them. struct PcRelativePatchInfo { @@ -431,7 +437,7 @@ class CodeGeneratorARM : public CodeGenerator { PcRelativePatchInfo(PcRelativePatchInfo&& other) = default; const DexFile& target_dex_file; - // Either the dex cache array element offset or the string index. + // Either the dex cache array element offset or the string/type index. uint32_t offset_or_index; Label movw_label; Label movt_label; @@ -439,9 +445,11 @@ class CodeGeneratorARM : public CodeGenerator { }; PcRelativePatchInfo* NewPcRelativeStringPatch(const DexFile& dex_file, uint32_t string_index); + PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, uint32_t type_index); PcRelativePatchInfo* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset); Literal* DeduplicateBootImageStringLiteral(const DexFile& dex_file, uint32_t string_index); + Literal* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, uint32_t type_index); Literal* DeduplicateBootImageAddressLiteral(uint32_t address); Literal* DeduplicateDexCacheAddressLiteral(uint32_t address); @@ -464,6 +472,16 @@ class CodeGeneratorARM : public CodeGenerator { Location index, Location temp, bool needs_null_check); + // Factored implementation used by GenerateFieldLoadWithBakerReadBarrier + // and GenerateArrayLoadWithBakerReadBarrier. + void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction, + Location ref, + Register obj, + uint32_t offset, + Location index, + ScaleFactor scale_factor, + Location temp, + bool needs_null_check); // Generate a read barrier for a heap reference within `instruction` // using a slow path. @@ -519,16 +537,6 @@ class CodeGeneratorARM : public CodeGenerator { void GenerateExplicitNullCheck(HNullCheck* instruction); private: - // Factored implementation of GenerateFieldLoadWithBakerReadBarrier - // and GenerateArrayLoadWithBakerReadBarrier. - void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction, - Location ref, - Register obj, - uint32_t offset, - Location index, - Location temp, - bool needs_null_check); - Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, Register temp); using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, Literal*>; @@ -536,6 +544,9 @@ class CodeGeneratorARM : public CodeGenerator { using BootStringToLiteralMap = ArenaSafeMap<StringReference, Literal*, StringReferenceValueComparator>; + using BootTypeToLiteralMap = ArenaSafeMap<TypeReference, + Literal*, + TypeReferenceValueComparator>; Literal* DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map); Literal* DeduplicateMethodLiteral(MethodReference target_method, MethodToLiteralMap* map); @@ -568,6 +579,10 @@ class CodeGeneratorARM : public CodeGenerator { BootStringToLiteralMap boot_image_string_patches_; // PC-relative String patch info. ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_; + // Deduplication map for boot type literals for kBootImageLinkTimeAddress. + BootTypeToLiteralMap boot_image_type_patches_; + // PC-relative type patch info. + ArenaDeque<PcRelativePatchInfo> pc_relative_type_patches_; // Deduplication map for patchable boot image addresses. Uint32ToLiteralMap boot_image_address_patches_; diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index b63a3d4c1a..c8d33d5743 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -237,8 +237,11 @@ class BoundsCheckSlowPathARM64 : public SlowPathCodeARM64 { codegen->EmitParallelMoves( locations->InAt(0), LocationFrom(calling_convention.GetRegisterAt(0)), Primitive::kPrimInt, locations->InAt(1), LocationFrom(calling_convention.GetRegisterAt(1)), Primitive::kPrimInt); - arm64_codegen->InvokeRuntime( - QUICK_ENTRY_POINT(pThrowArrayBounds), instruction_, instruction_->GetDexPc(), this); + uint32_t entry_point_offset = instruction_->AsBoundsCheck()->IsStringCharAt() + ? QUICK_ENTRY_POINT(pThrowStringBounds) + : QUICK_ENTRY_POINT(pThrowArrayBounds); + arm64_codegen->InvokeRuntime(entry_point_offset, instruction_, instruction_->GetDexPc(), this); + CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>(); CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>(); } @@ -595,7 +598,9 @@ class ReadBarrierMarkSlowPathARM64 : public SlowPathCodeARM64 { instruction_->IsLoadClass() || instruction_->IsLoadString() || instruction_->IsInstanceOf() || - instruction_->IsCheckCast()) + instruction_->IsCheckCast() || + ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) && + instruction_->GetLocations()->Intrinsified())) << "Unexpected instruction in read barrier marking slow path: " << instruction_->DebugName(); @@ -658,8 +663,12 @@ class ReadBarrierForHeapReferenceSlowPathARM64 : public SlowPathCodeARM64 { Primitive::Type type = Primitive::kPrimNot; DCHECK(locations->CanCall()); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(out_.reg())); - DCHECK(!instruction_->IsInvoke() || - (instruction_->IsInvokeStaticOrDirect() && + DCHECK(instruction_->IsInstanceFieldGet() || + instruction_->IsStaticFieldGet() || + instruction_->IsArrayGet() || + instruction_->IsInstanceOf() || + instruction_->IsCheckCast() || + ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) && instruction_->GetLocations()->Intrinsified())) << "Unexpected instruction in read barrier for heap reference slow path: " << instruction_->DebugName(); @@ -677,7 +686,7 @@ class ReadBarrierForHeapReferenceSlowPathARM64 : public SlowPathCodeARM64 { // introduce a copy of it, `index`. Location index = index_; if (index_.IsValid()) { - // Handle `index_` for HArrayGet and intrinsic UnsafeGetObject. + // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics. if (instruction_->IsArrayGet()) { // Compute the actual memory offset and store it in `index`. Register index_reg = RegisterFrom(index_, Primitive::kPrimInt); @@ -725,7 +734,11 @@ class ReadBarrierForHeapReferenceSlowPathARM64 : public SlowPathCodeARM64 { "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); __ Add(index_reg, index_reg, Operand(offset_)); } else { - DCHECK(instruction_->IsInvoke()); + // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile + // intrinsics, `index_` is not shifted by a scale factor of 2 + // (as in the case of ArrayGet), as it is actually an offset + // to an object field within an object. + DCHECK(instruction_->IsInvoke()) << instruction_->DebugName(); DCHECK(instruction_->GetLocations()->Intrinsified()); DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) || (instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile)) @@ -920,6 +933,9 @@ CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph, boot_image_string_patches_(StringReferenceValueComparator(), graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), pc_relative_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + boot_image_type_patches_(TypeReferenceValueComparator(), + graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), boot_image_address_patches_(std::less<uint32_t>(), graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { // Save the link register (containing the return address) to mimic Quick. @@ -2051,8 +2067,8 @@ void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) { Register obj = InputRegisterAt(instruction, 0); LocationSummary* locations = instruction->GetLocations(); Location index = locations->InAt(1); - uint32_t offset = mirror::Array::DataOffset(Primitive::ComponentSize(type)).Uint32Value(); Location out = locations->Out(); + uint32_t offset = CodeGenerator::GetArrayDataOffset(instruction); MacroAssembler* masm = GetVIXLAssembler(); UseScratchRegisterScope temps(masm); @@ -3725,6 +3741,12 @@ vixl::Label* CodeGeneratorARM64::NewPcRelativeStringPatch(const DexFile& dex_fil return NewPcRelativePatch(dex_file, string_index, adrp_label, &pc_relative_string_patches_); } +vixl::Label* CodeGeneratorARM64::NewPcRelativeTypePatch(const DexFile& dex_file, + uint32_t type_index, + vixl::Label* adrp_label) { + return NewPcRelativePatch(dex_file, type_index, adrp_label, &pc_relative_type_patches_); +} + vixl::Label* CodeGeneratorARM64::NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset, vixl::Label* adrp_label) { @@ -3751,6 +3773,13 @@ vixl::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateBootImageStringLiteral( [this]() { return __ CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u); }); } +vixl::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateBootImageTypeLiteral( + const DexFile& dex_file, uint32_t type_index) { + return boot_image_type_patches_.GetOrCreate( + TypeReference(&dex_file, type_index), + [this]() { return __ CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u); }); +} + vixl::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateBootImageAddressLiteral(uint64_t address) { bool needs_patch = GetCompilerOptions().GetIncludePatchInformation(); Uint32ToLiteralMap* map = needs_patch ? &boot_image_address_patches_ : &uint32_literals_; @@ -3770,6 +3799,8 @@ void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patc pc_relative_dex_cache_patches_.size() + boot_image_string_patches_.size() + pc_relative_string_patches_.size() + + boot_image_type_patches_.size() + + pc_relative_type_patches_.size() + boot_image_address_patches_.size(); linker_patches->reserve(size); for (const auto& entry : method_patches_) { @@ -3810,6 +3841,19 @@ void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patc info.pc_insn_label->location(), info.offset_or_index)); } + for (const auto& entry : boot_image_type_patches_) { + const TypeReference& target_type = entry.first; + vixl::Literal<uint32_t>* literal = entry.second; + linker_patches->push_back(LinkerPatch::TypePatch(literal->offset(), + target_type.dex_file, + target_type.type_index)); + } + for (const PcRelativePatchInfo& info : pc_relative_type_patches_) { + linker_patches->push_back(LinkerPatch::RelativeTypePatch(info.label.location(), + &info.target_dex_file, + info.pc_insn_label->location(), + info.offset_or_index)); + } for (const auto& entry : boot_image_address_patches_) { DCHECK(GetCompilerOptions().GetIncludePatchInformation()); vixl::Literal<uint32_t>* literal = entry.second; @@ -3875,13 +3919,63 @@ void InstructionCodeGeneratorARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) { codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } +HLoadClass::LoadKind CodeGeneratorARM64::GetSupportedLoadClassKind( + HLoadClass::LoadKind desired_class_load_kind) { + if (kEmitCompilerReadBarrier) { + switch (desired_class_load_kind) { + case HLoadClass::LoadKind::kBootImageLinkTimeAddress: + case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: + case HLoadClass::LoadKind::kBootImageAddress: + // TODO: Implement for read barrier. + return HLoadClass::LoadKind::kDexCacheViaMethod; + default: + break; + } + } + switch (desired_class_load_kind) { + case HLoadClass::LoadKind::kReferrersClass: + break; + case HLoadClass::LoadKind::kBootImageLinkTimeAddress: + DCHECK(!GetCompilerOptions().GetCompilePic()); + break; + case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: + DCHECK(GetCompilerOptions().GetCompilePic()); + break; + case HLoadClass::LoadKind::kBootImageAddress: + break; + case HLoadClass::LoadKind::kDexCacheAddress: + DCHECK(Runtime::Current()->UseJitCompilation()); + break; + case HLoadClass::LoadKind::kDexCachePcRelative: + DCHECK(!Runtime::Current()->UseJitCompilation()); + break; + case HLoadClass::LoadKind::kDexCacheViaMethod: + break; + } + return desired_class_load_kind; +} + void LocationsBuilderARM64::VisitLoadClass(HLoadClass* cls) { - InvokeRuntimeCallingConvention calling_convention; - CodeGenerator::CreateLoadClassLocationSummary( - cls, - LocationFrom(calling_convention.GetRegisterAt(0)), - LocationFrom(vixl::x0), - /* code_generator_supports_read_barrier */ true); + if (cls->NeedsAccessCheck()) { + InvokeRuntimeCallingConvention calling_convention; + CodeGenerator::CreateLoadClassLocationSummary( + cls, + LocationFrom(calling_convention.GetRegisterAt(0)), + LocationFrom(vixl::x0), + /* code_generator_supports_read_barrier */ true); + return; + } + + LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier) + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind); + HLoadClass::LoadKind load_kind = cls->GetLoadKind(); + if (load_kind == HLoadClass::LoadKind::kReferrersClass || + load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) { + locations->SetInAt(0, Location::RequiresRegister()); + } + locations->SetOut(Location::RequiresRegister()); } void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) { @@ -3897,35 +3991,111 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) { Location out_loc = cls->GetLocations()->Out(); Register out = OutputRegister(cls); - Register current_method = InputRegisterAt(cls, 0); - if (cls->IsReferrersClass()) { - DCHECK(!cls->CanCallRuntime()); - DCHECK(!cls->MustGenerateClinitCheck()); - // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_ - GenerateGcRootFieldLoad( - cls, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value()); - } else { - MemberOffset resolved_types_offset = ArtMethod::DexCacheResolvedTypesOffset(kArm64PointerSize); - // /* GcRoot<mirror::Class>[] */ out = - // current_method.ptr_sized_fields_->dex_cache_resolved_types_ - __ Ldr(out.X(), MemOperand(current_method, resolved_types_offset.Int32Value())); - // /* GcRoot<mirror::Class> */ out = out[type_index] - GenerateGcRootFieldLoad( - cls, out_loc, out.X(), CodeGenerator::GetCacheOffset(cls->GetTypeIndex())); - - if (!cls->IsInDexCache() || cls->MustGenerateClinitCheck()) { - DCHECK(cls->CanCallRuntime()); - SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM64( - cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck()); - codegen_->AddSlowPath(slow_path); - if (!cls->IsInDexCache()) { - __ Cbz(out, slow_path->GetEntryLabel()); + + bool generate_null_check = false; + switch (cls->GetLoadKind()) { + case HLoadClass::LoadKind::kReferrersClass: { + DCHECK(!cls->CanCallRuntime()); + DCHECK(!cls->MustGenerateClinitCheck()); + // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_ + Register current_method = InputRegisterAt(cls, 0); + GenerateGcRootFieldLoad( + cls, out_loc, current_method, ArtMethod::DeclaringClassOffset().Int32Value()); + break; + } + case HLoadClass::LoadKind::kBootImageLinkTimeAddress: + DCHECK(!kEmitCompilerReadBarrier); + __ Ldr(out, codegen_->DeduplicateBootImageTypeLiteral(cls->GetDexFile(), + cls->GetTypeIndex())); + break; + case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: { + DCHECK(!kEmitCompilerReadBarrier); + // Add ADRP with its PC-relative type patch. + const DexFile& dex_file = cls->GetDexFile(); + uint32_t type_index = cls->GetTypeIndex(); + vixl::Label* adrp_label = codegen_->NewPcRelativeTypePatch(dex_file, type_index); + { + vixl::SingleEmissionCheckScope guard(GetVIXLAssembler()); + __ Bind(adrp_label); + __ adrp(out.X(), /* offset placeholder */ 0); } - if (cls->MustGenerateClinitCheck()) { - GenerateClassInitializationCheck(slow_path, out); - } else { - __ Bind(slow_path->GetExitLabel()); + // Add ADD with its PC-relative type patch. + vixl::Label* add_label = codegen_->NewPcRelativeTypePatch(dex_file, type_index, adrp_label); + { + vixl::SingleEmissionCheckScope guard(GetVIXLAssembler()); + __ Bind(add_label); + __ add(out.X(), out.X(), Operand(/* offset placeholder */ 0)); + } + break; + } + case HLoadClass::LoadKind::kBootImageAddress: { + DCHECK(!kEmitCompilerReadBarrier); + DCHECK(cls->GetAddress() != 0u && IsUint<32>(cls->GetAddress())); + __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(cls->GetAddress())); + break; + } + case HLoadClass::LoadKind::kDexCacheAddress: { + DCHECK_NE(cls->GetAddress(), 0u); + // LDR immediate has a 12-bit offset multiplied by the size and for 32-bit loads + // that gives a 16KiB range. To try and reduce the number of literals if we load + // multiple types, simply split the dex cache address to a 16KiB aligned base + // loaded from a literal and the remaining offset embedded in the load. + static_assert(sizeof(GcRoot<mirror::Class>) == 4u, "Expected GC root to be 4 bytes."); + DCHECK_ALIGNED(cls->GetAddress(), 4u); + constexpr size_t offset_bits = /* encoded bits */ 12 + /* scale */ 2; + uint64_t base_address = cls->GetAddress() & ~MaxInt<uint64_t>(offset_bits); + uint32_t offset = cls->GetAddress() & MaxInt<uint64_t>(offset_bits); + __ Ldr(out.X(), codegen_->DeduplicateDexCacheAddressLiteral(base_address)); + // /* GcRoot<mirror::Class> */ out = *(base_address + offset) + GenerateGcRootFieldLoad(cls, out_loc, out.X(), offset); + generate_null_check = !cls->IsInDexCache(); + break; + } + case HLoadClass::LoadKind::kDexCachePcRelative: { + // Add ADRP with its PC-relative DexCache access patch. + const DexFile& dex_file = cls->GetDexFile(); + uint32_t element_offset = cls->GetDexCacheElementOffset(); + vixl::Label* adrp_label = codegen_->NewPcRelativeDexCacheArrayPatch(dex_file, element_offset); + { + vixl::SingleEmissionCheckScope guard(GetVIXLAssembler()); + __ Bind(adrp_label); + __ adrp(out.X(), /* offset placeholder */ 0); } + // Add LDR with its PC-relative DexCache access patch. + vixl::Label* ldr_label = + codegen_->NewPcRelativeDexCacheArrayPatch(dex_file, element_offset, adrp_label); + // /* GcRoot<mirror::Class> */ out = *(base_address + offset) /* PC-relative */ + GenerateGcRootFieldLoad(cls, out_loc, out.X(), /* offset placeholder */ 0, ldr_label); + generate_null_check = !cls->IsInDexCache(); + break; + } + case HLoadClass::LoadKind::kDexCacheViaMethod: { + MemberOffset resolved_types_offset = + ArtMethod::DexCacheResolvedTypesOffset(kArm64PointerSize); + // /* GcRoot<mirror::Class>[] */ out = + // current_method.ptr_sized_fields_->dex_cache_resolved_types_ + Register current_method = InputRegisterAt(cls, 0); + __ Ldr(out.X(), MemOperand(current_method, resolved_types_offset.Int32Value())); + // /* GcRoot<mirror::Class> */ out = out[type_index] + GenerateGcRootFieldLoad( + cls, out_loc, out.X(), CodeGenerator::GetCacheOffset(cls->GetTypeIndex())); + generate_null_check = !cls->IsInDexCache(); + break; + } + } + + if (generate_null_check || cls->MustGenerateClinitCheck()) { + DCHECK(cls->CanCallRuntime()); + SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM64( + cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck()); + codegen_->AddSlowPath(slow_path); + if (generate_null_check) { + __ Cbz(out, slow_path->GetEntryLabel()); + } + if (cls->MustGenerateClinitCheck()) { + GenerateClassInitializationCheck(slow_path, out); + } else { + __ Bind(slow_path->GetExitLabel()); } } } @@ -4046,6 +4216,7 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) { uint64_t base_address = load->GetAddress() & ~MaxInt<uint64_t>(offset_bits); uint32_t offset = load->GetAddress() & MaxInt<uint64_t>(offset_bits); __ Ldr(out.X(), codegen_->DeduplicateDexCacheAddressLiteral(base_address)); + // /* GcRoot<mirror::String> */ out = *(base_address + offset) GenerateGcRootFieldLoad(load, out_loc, out.X(), offset); break; } @@ -4062,6 +4233,7 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) { // Add LDR with its PC-relative DexCache access patch. vixl::Label* ldr_label = codegen_->NewPcRelativeDexCacheArrayPatch(dex_file, element_offset, adrp_label); + // /* GcRoot<mirror::String> */ out = *(base_address + offset) /* PC-relative */ GenerateGcRootFieldLoad(load, out_loc, out.X(), /* offset placeholder */ 0, ldr_label); break; } @@ -4940,8 +5112,16 @@ void CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier(HInstruction* ins // /* HeapReference<Object> */ ref = *(obj + offset) Location no_index = Location::NoLocation(); - GenerateReferenceLoadWithBakerReadBarrier( - instruction, ref, obj, offset, no_index, temp, needs_null_check, use_load_acquire); + size_t no_scale_factor = 0U; + GenerateReferenceLoadWithBakerReadBarrier(instruction, + ref, + obj, + offset, + no_index, + no_scale_factor, + temp, + needs_null_check, + use_load_acquire); } void CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction, @@ -4958,10 +5138,21 @@ void CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier(HInstruction* ins // never use Load-Acquire instructions on ARM64. const bool use_load_acquire = false; + static_assert( + sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), + "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); // /* HeapReference<Object> */ ref = // *(obj + data_offset + index * sizeof(HeapReference<Object>)) - GenerateReferenceLoadWithBakerReadBarrier( - instruction, ref, obj, data_offset, index, temp, needs_null_check, use_load_acquire); + size_t scale_factor = Primitive::ComponentSizeShift(Primitive::kPrimNot); + GenerateReferenceLoadWithBakerReadBarrier(instruction, + ref, + obj, + data_offset, + index, + scale_factor, + temp, + needs_null_check, + use_load_acquire); } void CodeGeneratorARM64::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction, @@ -4969,15 +5160,16 @@ void CodeGeneratorARM64::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* vixl::Register obj, uint32_t offset, Location index, + size_t scale_factor, Register temp, bool needs_null_check, bool use_load_acquire) { DCHECK(kEmitCompilerReadBarrier); DCHECK(kUseBakerReadBarrier); - // If `index` is a valid location, then we are emitting an array - // load, so we shouldn't be using a Load Acquire instruction. - // In other words: `index.IsValid()` => `!use_load_acquire`. - DCHECK(!index.IsValid() || !use_load_acquire); + // If we are emitting an array load, we should not be using a + // Load Acquire instruction. In other words: + // `instruction->IsArrayGet()` => `!use_load_acquire`. + DCHECK(!instruction->IsArrayGet() || !use_load_acquire); MacroAssembler* masm = GetVIXLAssembler(); UseScratchRegisterScope temps(masm); @@ -5034,20 +5226,33 @@ void CodeGeneratorARM64::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* // The actual reference load. if (index.IsValid()) { - static_assert( - sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), - "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); - // /* HeapReference<Object> */ ref = - // *(obj + offset + index * sizeof(HeapReference<Object>)) - const size_t shift_amount = Primitive::ComponentSizeShift(type); - if (index.IsConstant()) { - uint32_t computed_offset = offset + (Int64ConstantFrom(index) << shift_amount); - Load(type, ref_reg, HeapOperand(obj, computed_offset)); + // Load types involving an "index". + if (use_load_acquire) { + // UnsafeGetObjectVolatile intrinsic case. + // Register `index` is not an index in an object array, but an + // offset to an object reference field within object `obj`. + DCHECK(instruction->IsInvoke()) << instruction->DebugName(); + DCHECK(instruction->GetLocations()->Intrinsified()); + DCHECK(instruction->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile) + << instruction->AsInvoke()->GetIntrinsic(); + DCHECK_EQ(offset, 0U); + DCHECK_EQ(scale_factor, 0U); + DCHECK_EQ(needs_null_check, 0U); + // /* HeapReference<Object> */ ref = *(obj + index) + MemOperand field = HeapOperand(obj, XRegisterFrom(index)); + LoadAcquire(instruction, ref_reg, field, /* needs_null_check */ false); } else { - temp2 = temps.AcquireW(); - __ Add(temp2, obj, offset); - Load(type, ref_reg, HeapOperand(temp2, XRegisterFrom(index), LSL, shift_amount)); - temps.Release(temp2); + // ArrayGet and UnsafeGetObject intrinsics cases. + // /* HeapReference<Object> */ ref = *(obj + offset + (index << scale_factor)) + if (index.IsConstant()) { + uint32_t computed_offset = offset + (Int64ConstantFrom(index) << scale_factor); + Load(type, ref_reg, HeapOperand(obj, computed_offset)); + } else { + temp2 = temps.AcquireW(); + __ Add(temp2, obj, offset); + Load(type, ref_reg, HeapOperand(temp2, XRegisterFrom(index), LSL, scale_factor)); + temps.Release(temp2); + } } } else { // /* HeapReference<Object> */ ref = *(obj + offset) diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index 422963e7d0..d4bf695602 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -26,6 +26,7 @@ #include "parallel_move_resolver.h" #include "utils/arm64/assembler_arm64.h" #include "utils/string_reference.h" +#include "utils/type_reference.h" #include "vixl/a64/disasm-a64.h" #include "vixl/a64/macro-assembler-a64.h" @@ -460,6 +461,11 @@ class CodeGeneratorARM64 : public CodeGenerator { HLoadString::LoadKind GetSupportedLoadStringKind( HLoadString::LoadKind desired_string_load_kind) OVERRIDE; + // Check if the desired_class_load_kind is supported. If it is, return it, + // otherwise return a fall-back kind that should be used instead. + HLoadClass::LoadKind GetSupportedLoadClassKind( + HLoadClass::LoadKind desired_class_load_kind) OVERRIDE; + // Check if the desired_dispatch_info is supported. If it is, return it, // otherwise return a fall-back info that should be used instead. HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch( @@ -482,6 +488,14 @@ class CodeGeneratorARM64 : public CodeGenerator { uint32_t string_index, vixl::Label* adrp_label = nullptr); + // Add a new PC-relative type patch for an instruction and return the label + // to be bound before the instruction. The instruction will be either the + // ADRP (pass `adrp_label = null`) or the ADD (pass `adrp_label` pointing + // to the associated ADRP patch label). + vixl::Label* NewPcRelativeTypePatch(const DexFile& dex_file, + uint32_t type_index, + vixl::Label* adrp_label = nullptr); + // Add a new PC-relative dex cache array patch for an instruction and return // the label to be bound before the instruction. The instruction will be // either the ADRP (pass `adrp_label = null`) or the LDR (pass `adrp_label` @@ -492,6 +506,8 @@ class CodeGeneratorARM64 : public CodeGenerator { vixl::Literal<uint32_t>* DeduplicateBootImageStringLiteral(const DexFile& dex_file, uint32_t string_index); + vixl::Literal<uint32_t>* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, + uint32_t type_index); vixl::Literal<uint32_t>* DeduplicateBootImageAddressLiteral(uint64_t address); vixl::Literal<uint64_t>* DeduplicateDexCacheAddressLiteral(uint64_t address); @@ -515,6 +531,17 @@ class CodeGeneratorARM64 : public CodeGenerator { Location index, vixl::Register temp, bool needs_null_check); + // Factored implementation used by GenerateFieldLoadWithBakerReadBarrier + // and GenerateArrayLoadWithBakerReadBarrier. + void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction, + Location ref, + vixl::Register obj, + uint32_t offset, + Location index, + size_t scale_factor, + vixl::Register temp, + bool needs_null_check, + bool use_load_acquire); // Generate a read barrier for a heap reference within `instruction` // using a slow path. @@ -570,17 +597,6 @@ class CodeGeneratorARM64 : public CodeGenerator { void GenerateExplicitNullCheck(HNullCheck* instruction); private: - // Factored implementation of GenerateFieldLoadWithBakerReadBarrier - // and GenerateArrayLoadWithBakerReadBarrier. - void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction, - Location ref, - vixl::Register obj, - uint32_t offset, - Location index, - vixl::Register temp, - bool needs_null_check, - bool use_load_acquire); - using Uint64ToLiteralMap = ArenaSafeMap<uint64_t, vixl::Literal<uint64_t>*>; using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, vixl::Literal<uint32_t>*>; using MethodToLiteralMap = ArenaSafeMap<MethodReference, @@ -589,6 +605,9 @@ class CodeGeneratorARM64 : public CodeGenerator { using BootStringToLiteralMap = ArenaSafeMap<StringReference, vixl::Literal<uint32_t>*, StringReferenceValueComparator>; + using BootTypeToLiteralMap = ArenaSafeMap<TypeReference, + vixl::Literal<uint32_t>*, + TypeReferenceValueComparator>; vixl::Literal<uint32_t>* DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map); vixl::Literal<uint64_t>* DeduplicateUint64Literal(uint64_t value); @@ -598,13 +617,14 @@ class CodeGeneratorARM64 : public CodeGenerator { vixl::Literal<uint64_t>* DeduplicateMethodCodeLiteral(MethodReference target_method); // The PcRelativePatchInfo is used for PC-relative addressing of dex cache arrays - // and boot image strings. The only difference is the interpretation of the offset_or_index. + // and boot image strings/types. The only difference is the interpretation of the + // offset_or_index. struct PcRelativePatchInfo { PcRelativePatchInfo(const DexFile& dex_file, uint32_t off_or_idx) : target_dex_file(dex_file), offset_or_index(off_or_idx), label(), pc_insn_label() { } const DexFile& target_dex_file; - // Either the dex cache array element offset or the string index. + // Either the dex cache array element offset or the string/type index. uint32_t offset_or_index; vixl::Label label; vixl::Label* pc_insn_label; @@ -646,6 +666,10 @@ class CodeGeneratorARM64 : public CodeGenerator { BootStringToLiteralMap boot_image_string_patches_; // PC-relative String patch info. ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_; + // Deduplication map for boot type literals for kBootImageLinkTimeAddress. + BootTypeToLiteralMap boot_image_type_patches_; + // PC-relative type patch info. + ArenaDeque<PcRelativePatchInfo> pc_relative_type_patches_; // Deduplication map for patchable boot image addresses. Uint32ToLiteralMap boot_image_address_patches_; diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index c8e927d026..810db20888 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -166,11 +166,15 @@ class BoundsCheckSlowPathMIPS : public SlowPathCodeMIPS { locations->InAt(1), Location::RegisterLocation(calling_convention.GetRegisterAt(1)), Primitive::kPrimInt); - mips_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowArrayBounds), + uint32_t entry_point_offset = instruction_->AsBoundsCheck()->IsStringCharAt() + ? QUICK_ENTRY_POINT(pThrowStringBounds) + : QUICK_ENTRY_POINT(pThrowArrayBounds); + mips_codegen->InvokeRuntime(entry_point_offset, instruction_, instruction_->GetDexPc(), this, IsDirectEntrypoint(kQuickThrowArrayBounds)); + CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>(); CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>(); } @@ -1635,11 +1639,11 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) { LocationSummary* locations = instruction->GetLocations(); Register obj = locations->InAt(0).AsRegister<Register>(); Location index = locations->InAt(1); - Primitive::Type type = instruction->GetType(); + uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction); + Primitive::Type type = instruction->GetType(); switch (type) { case Primitive::kPrimBoolean: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); Register out = locations->Out().AsRegister<Register>(); if (index.IsConstant()) { size_t offset = @@ -1653,7 +1657,6 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimByte: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value(); Register out = locations->Out().AsRegister<Register>(); if (index.IsConstant()) { size_t offset = @@ -1667,7 +1670,6 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimShort: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value(); Register out = locations->Out().AsRegister<Register>(); if (index.IsConstant()) { size_t offset = @@ -1682,7 +1684,6 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimChar: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); Register out = locations->Out().AsRegister<Register>(); if (index.IsConstant()) { size_t offset = @@ -1699,7 +1700,6 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) { case Primitive::kPrimInt: case Primitive::kPrimNot: { DCHECK_EQ(sizeof(mirror::HeapReference<mirror::Object>), sizeof(int32_t)); - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); Register out = locations->Out().AsRegister<Register>(); if (index.IsConstant()) { size_t offset = @@ -1714,7 +1714,6 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimLong: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); Register out = locations->Out().AsRegisterPairLow<Register>(); if (index.IsConstant()) { size_t offset = @@ -1729,7 +1728,6 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimFloat: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value(); FRegister out = locations->Out().AsFpuRegister<FRegister>(); if (index.IsConstant()) { size_t offset = @@ -1744,7 +1742,6 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimDouble: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value(); FRegister out = locations->Out().AsFpuRegister<FRegister>(); if (index.IsConstant()) { size_t offset = @@ -3769,6 +3766,13 @@ HLoadString::LoadKind CodeGeneratorMIPS::GetSupportedLoadStringKind( return HLoadString::LoadKind::kDexCacheViaMethod; } +HLoadClass::LoadKind CodeGeneratorMIPS::GetSupportedLoadClassKind( + HLoadClass::LoadKind desired_class_load_kind) { + DCHECK_NE(desired_class_load_kind, HLoadClass::LoadKind::kReferrersClass); + // TODO: Implement other kinds. + return HLoadClass::LoadKind::kDexCacheViaMethod; +} + HInvokeStaticOrDirect::DispatchInfo CodeGeneratorMIPS::GetSupportedInvokeStaticOrDirectDispatch( const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, MethodReference target_method ATTRIBUTE_UNUSED) { @@ -4726,7 +4730,6 @@ void InstructionCodeGeneratorMIPS::VisitTypeConversion(HTypeConversion* conversi Primitive::Type input_type = conversion->GetInputType(); bool has_sign_extension = codegen_->GetInstructionSetFeatures().IsMipsIsaRevGreaterThanEqual2(); bool isR6 = codegen_->GetInstructionSetFeatures().IsR6(); - bool fpu_32bit = codegen_->GetInstructionSetFeatures().Is32BitFloatingPoint(); DCHECK_NE(input_type, result_type); @@ -4735,7 +4738,9 @@ void InstructionCodeGeneratorMIPS::VisitTypeConversion(HTypeConversion* conversi Register dst_low = locations->Out().AsRegisterPairLow<Register>(); Register src = locations->InAt(0).AsRegister<Register>(); - __ Move(dst_low, src); + if (dst_low != src) { + __ Move(dst_low, src); + } __ Sra(dst_high, src, 31); } else if (Primitive::IsIntegralType(result_type) && Primitive::IsIntegralType(input_type)) { Register dst = locations->Out().AsRegister<Register>(); @@ -4764,7 +4769,9 @@ void InstructionCodeGeneratorMIPS::VisitTypeConversion(HTypeConversion* conversi } break; case Primitive::kPrimInt: - __ Move(dst, src); + if (dst != src) { + __ Move(dst, src); + } break; default: @@ -4921,11 +4928,7 @@ void InstructionCodeGeneratorMIPS::VisitTypeConversion(HTypeConversion* conversi uint64_t min_val = bit_cast<uint64_t, double>(std::numeric_limits<int32_t>::min()); __ LoadConst32(TMP, High32Bits(min_val)); __ Mtc1(ZERO, FTMP); - if (fpu_32bit) { - __ Mtc1(TMP, static_cast<FRegister>(FTMP + 1)); - } else { - __ Mthc1(TMP, FTMP); - } + __ MoveToFpuHigh(TMP, FTMP); } if (isR6) { diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index 8c0bae628e..6487f28ad5 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -349,6 +349,11 @@ class CodeGeneratorMIPS : public CodeGenerator { HLoadString::LoadKind GetSupportedLoadStringKind( HLoadString::LoadKind desired_string_load_kind) OVERRIDE; + // Check if the desired_class_load_kind is supported. If it is, return it, + // otherwise return a fall-back kind that should be used instead. + HLoadClass::LoadKind GetSupportedLoadClassKind( + HLoadClass::LoadKind desired_class_load_kind) OVERRIDE; + // Check if the desired_dispatch_info is supported. If it is, return it, // otherwise return a fall-back info that should be used instead. HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch( diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 8d5dc84df9..9f2664c0a5 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -127,10 +127,14 @@ class BoundsCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 { locations->InAt(1), Location::RegisterLocation(calling_convention.GetRegisterAt(1)), Primitive::kPrimInt); - mips64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowArrayBounds), + uint32_t entry_point_offset = instruction_->AsBoundsCheck()->IsStringCharAt() + ? QUICK_ENTRY_POINT(pThrowStringBounds) + : QUICK_ENTRY_POINT(pThrowArrayBounds); + mips64_codegen->InvokeRuntime(entry_point_offset, instruction_, instruction_->GetDexPc(), this); + CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>(); CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>(); } @@ -918,13 +922,13 @@ void CodeGeneratorMIPS64::SetupBlockedRegisters() const { // TODO: review; anything else? - // TODO: remove once all the issues with register saving/restoring are sorted out. - for (size_t i = 0; i < arraysize(kCoreCalleeSaves); ++i) { - blocked_core_registers_[kCoreCalleeSaves[i]] = true; - } - - for (size_t i = 0; i < arraysize(kFpuCalleeSaves); ++i) { - blocked_fpu_registers_[kFpuCalleeSaves[i]] = true; + if (GetGraph()->IsDebuggable()) { + // Stubs do not save callee-save floating point registers. If the graph + // is debuggable, we need to deal with these registers differently. For + // now, just block them. + for (size_t i = 0; i < arraysize(kFpuCalleeSaves); ++i) { + blocked_fpu_registers_[kFpuCalleeSaves[i]] = true; + } } } @@ -1289,11 +1293,11 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { LocationSummary* locations = instruction->GetLocations(); GpuRegister obj = locations->InAt(0).AsRegister<GpuRegister>(); Location index = locations->InAt(1); - Primitive::Type type = instruction->GetType(); + uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction); + Primitive::Type type = instruction->GetType(); switch (type) { case Primitive::kPrimBoolean: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); GpuRegister out = locations->Out().AsRegister<GpuRegister>(); if (index.IsConstant()) { size_t offset = @@ -1307,7 +1311,6 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimByte: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value(); GpuRegister out = locations->Out().AsRegister<GpuRegister>(); if (index.IsConstant()) { size_t offset = @@ -1321,7 +1324,6 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimShort: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value(); GpuRegister out = locations->Out().AsRegister<GpuRegister>(); if (index.IsConstant()) { size_t offset = @@ -1336,7 +1338,6 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimChar: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); GpuRegister out = locations->Out().AsRegister<GpuRegister>(); if (index.IsConstant()) { size_t offset = @@ -1353,7 +1354,6 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { case Primitive::kPrimInt: case Primitive::kPrimNot: { DCHECK_EQ(sizeof(mirror::HeapReference<mirror::Object>), sizeof(int32_t)); - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); GpuRegister out = locations->Out().AsRegister<GpuRegister>(); LoadOperandType load_type = (type == Primitive::kPrimNot) ? kLoadUnsignedWord : kLoadWord; if (index.IsConstant()) { @@ -1369,7 +1369,6 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimLong: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); GpuRegister out = locations->Out().AsRegister<GpuRegister>(); if (index.IsConstant()) { size_t offset = @@ -1384,7 +1383,6 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimFloat: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value(); FpuRegister out = locations->Out().AsFpuRegister<FpuRegister>(); if (index.IsConstant()) { size_t offset = @@ -1399,7 +1397,6 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimDouble: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value(); FpuRegister out = locations->Out().AsFpuRegister<FpuRegister>(); if (index.IsConstant()) { size_t offset = @@ -2986,19 +2983,6 @@ void LocationsBuilderMIPS64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* in } HandleInvoke(invoke); - - // While SetupBlockedRegisters() blocks registers S2-S8 due to their - // clobbering somewhere else, reduce further register pressure by avoiding - // allocation of a register for the current method pointer like on x86 baseline. - // TODO: remove this once all the issues with register saving/restoring are - // sorted out. - if (invoke->HasCurrentMethodInput()) { - LocationSummary* locations = invoke->GetLocations(); - Location location = locations->InAt(invoke->GetSpecialInputIndex()); - if (location.IsUnallocated() && location.GetPolicy() == Location::kRequiresRegister) { - locations->SetInAt(invoke->GetSpecialInputIndex(), Location::NoLocation()); - } - } } static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorMIPS64* codegen) { @@ -3016,6 +3000,13 @@ HLoadString::LoadKind CodeGeneratorMIPS64::GetSupportedLoadStringKind( return HLoadString::LoadKind::kDexCacheViaMethod; } +HLoadClass::LoadKind CodeGeneratorMIPS64::GetSupportedLoadClassKind( + HLoadClass::LoadKind desired_class_load_kind) { + DCHECK_NE(desired_class_load_kind, HLoadClass::LoadKind::kReferrersClass); + // TODO: Implement other kinds. + return HLoadClass::LoadKind::kDexCacheViaMethod; +} + HInvokeStaticOrDirect::DispatchInfo CodeGeneratorMIPS64::GetSupportedInvokeStaticOrDirectDispatch( const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info, MethodReference target_method ATTRIBUTE_UNUSED) { diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index 9785a2e8a8..4b462cc800 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -340,6 +340,11 @@ class CodeGeneratorMIPS64 : public CodeGenerator { HLoadString::LoadKind GetSupportedLoadStringKind( HLoadString::LoadKind desired_string_load_kind) OVERRIDE; + // Check if the desired_class_load_kind is supported. If it is, return it, + // otherwise return a fall-back kind that should be used instead. + HLoadClass::LoadKind GetSupportedLoadClassKind( + HLoadClass::LoadKind desired_class_load_kind) OVERRIDE; + // Check if the desired_dispatch_info is supported. If it is, return it, // otherwise return a fall-back info that should be used instead. HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch( diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 9d0092b674..be20f1f7cc 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -148,10 +148,14 @@ class BoundsCheckSlowPathX86 : public SlowPathCode { locations->InAt(1), Location::RegisterLocation(calling_convention.GetRegisterAt(1)), Primitive::kPrimInt); - x86_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowArrayBounds), + uint32_t entry_point_offset = instruction_->AsBoundsCheck()->IsStringCharAt() + ? QUICK_ENTRY_POINT(pThrowStringBounds) + : QUICK_ENTRY_POINT(pThrowArrayBounds); + x86_codegen->InvokeRuntime(entry_point_offset, instruction_, instruction_->GetDexPc(), this); + CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>(); CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>(); } @@ -444,7 +448,9 @@ class ReadBarrierMarkSlowPathX86 : public SlowPathCode { instruction_->IsLoadClass() || instruction_->IsLoadString() || instruction_->IsInstanceOf() || - instruction_->IsCheckCast()) + instruction_->IsCheckCast() || + ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) && + instruction_->GetLocations()->Intrinsified())) << "Unexpected instruction in read barrier marking slow path: " << instruction_->DebugName(); @@ -507,8 +513,12 @@ class ReadBarrierForHeapReferenceSlowPathX86 : public SlowPathCode { Register reg_out = out_.AsRegister<Register>(); DCHECK(locations->CanCall()); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out)); - DCHECK(!instruction_->IsInvoke() || - (instruction_->IsInvokeStaticOrDirect() && + DCHECK(instruction_->IsInstanceFieldGet() || + instruction_->IsStaticFieldGet() || + instruction_->IsArrayGet() || + instruction_->IsInstanceOf() || + instruction_->IsCheckCast() || + ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) && instruction_->GetLocations()->Intrinsified())) << "Unexpected instruction in read barrier for heap reference slow path: " << instruction_->DebugName(); @@ -521,7 +531,7 @@ class ReadBarrierForHeapReferenceSlowPathX86 : public SlowPathCode { // introduce a copy of it, `index`. Location index = index_; if (index_.IsValid()) { - // Handle `index_` for HArrayGet and intrinsic UnsafeGetObject. + // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics. if (instruction_->IsArrayGet()) { // Compute the actual memory offset and store it in `index`. Register index_reg = index_.AsRegister<Register>(); @@ -569,7 +579,11 @@ class ReadBarrierForHeapReferenceSlowPathX86 : public SlowPathCode { "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); __ AddImmediate(index_reg, Immediate(offset_)); } else { - DCHECK(instruction_->IsInvoke()); + // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile + // intrinsics, `index_` is not shifted by a scale factor of 2 + // (as in the case of ArrayGet), as it is actually an offset + // to an object field within an object. + DCHECK(instruction_->IsInvoke()) << instruction_->DebugName(); DCHECK(instruction_->GetLocations()->Intrinsified()); DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) || (instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile)) @@ -804,6 +818,7 @@ CodeGeneratorX86::CodeGeneratorX86(HGraph* graph, pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), simple_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), constant_area_start_(-1), fixups_to_jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), method_address_offset_(-1) { @@ -4452,6 +4467,11 @@ void CodeGeneratorX86::RecordStringPatch(HLoadString* load_string) { __ Bind(&string_patches_.back().label); } +void CodeGeneratorX86::RecordTypePatch(HLoadClass* load_class) { + type_patches_.emplace_back(load_class->GetDexFile(), load_class->GetTypeIndex()); + __ Bind(&type_patches_.back().label); +} + Label* CodeGeneratorX86::NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset) { // Add the patch entry and bind its label at the end of the instruction. @@ -4466,7 +4486,8 @@ void CodeGeneratorX86::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patche relative_call_patches_.size() + pc_relative_dex_cache_patches_.size() + simple_patches_.size() + - string_patches_.size(); + string_patches_.size() + + type_patches_.size(); linker_patches->reserve(size); // The label points to the end of the "movl" insn but the literal offset for method // patch needs to point to the embedded constant which occupies the last 4 bytes. @@ -4502,6 +4523,13 @@ void CodeGeneratorX86::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patche GetMethodAddressOffset(), info.string_index)); } + for (const TypePatchInfo<Label>& info : type_patches_) { + uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment; + linker_patches->push_back(LinkerPatch::RelativeTypePatch(literal_offset, + &info.dex_file, + GetMethodAddressOffset(), + info.type_index)); + } } else { for (const StringPatchInfo<Label>& info : string_patches_) { uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment; @@ -4509,6 +4537,12 @@ void CodeGeneratorX86::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_patche &info.dex_file, info.string_index)); } + for (const TypePatchInfo<Label>& info : type_patches_) { + uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment; + linker_patches->push_back(LinkerPatch::TypePatch(literal_offset, + &info.dex_file, + info.type_index)); + } } } @@ -5040,11 +5074,11 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) { Register obj = obj_loc.AsRegister<Register>(); Location index = locations->InAt(1); Location out_loc = locations->Out(); + uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction); Primitive::Type type = instruction->GetType(); switch (type) { case Primitive::kPrimBoolean: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); Register out = out_loc.AsRegister<Register>(); if (index.IsConstant()) { __ movzxb(out, Address(obj, @@ -5056,7 +5090,6 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimByte: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value(); Register out = out_loc.AsRegister<Register>(); if (index.IsConstant()) { __ movsxb(out, Address(obj, @@ -5068,7 +5101,6 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimShort: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value(); Register out = out_loc.AsRegister<Register>(); if (index.IsConstant()) { __ movsxw(out, Address(obj, @@ -5080,7 +5112,6 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimChar: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); Register out = out_loc.AsRegister<Register>(); if (index.IsConstant()) { __ movzxw(out, Address(obj, @@ -5092,7 +5123,6 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimInt: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); Register out = out_loc.AsRegister<Register>(); if (index.IsConstant()) { __ movl(out, Address(obj, @@ -5107,7 +5137,6 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) { static_assert( sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); // /* HeapReference<Object> */ out = // *(obj + data_offset + index * sizeof(HeapReference<Object>)) if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { @@ -5141,7 +5170,6 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimLong: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); DCHECK_NE(obj, out_loc.AsRegisterPairLow<Register>()); if (index.IsConstant()) { size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset; @@ -5159,7 +5187,6 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimFloat: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value(); XmmRegister out = out_loc.AsFpuRegister<XmmRegister>(); if (index.IsConstant()) { __ movss(out, Address(obj, @@ -5171,7 +5198,6 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimDouble: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value(); XmmRegister out = out_loc.AsFpuRegister<XmmRegister>(); if (index.IsConstant()) { __ movsd(out, Address(obj, @@ -5873,13 +5899,72 @@ void ParallelMoveResolverX86::RestoreScratch(int reg) { __ popl(static_cast<Register>(reg)); } +HLoadClass::LoadKind CodeGeneratorX86::GetSupportedLoadClassKind( + HLoadClass::LoadKind desired_class_load_kind) { + if (kEmitCompilerReadBarrier) { + switch (desired_class_load_kind) { + case HLoadClass::LoadKind::kBootImageLinkTimeAddress: + case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: + case HLoadClass::LoadKind::kBootImageAddress: + // TODO: Implement for read barrier. + return HLoadClass::LoadKind::kDexCacheViaMethod; + default: + break; + } + } + switch (desired_class_load_kind) { + case HLoadClass::LoadKind::kReferrersClass: + break; + case HLoadClass::LoadKind::kBootImageLinkTimeAddress: + DCHECK(!GetCompilerOptions().GetCompilePic()); + break; + case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: + DCHECK(GetCompilerOptions().GetCompilePic()); + FALLTHROUGH_INTENDED; + case HLoadClass::LoadKind::kDexCachePcRelative: + DCHECK(!Runtime::Current()->UseJitCompilation()); // Note: boot image is also non-JIT. + // We disable pc-relative load when there is an irreducible loop, as the optimization + // is incompatible with it. + // TODO: Create as many X86ComputeBaseMethodAddress instructions as needed for methods + // with irreducible loops. + if (GetGraph()->HasIrreducibleLoops()) { + return HLoadClass::LoadKind::kDexCacheViaMethod; + } + break; + case HLoadClass::LoadKind::kBootImageAddress: + break; + case HLoadClass::LoadKind::kDexCacheAddress: + DCHECK(Runtime::Current()->UseJitCompilation()); + break; + case HLoadClass::LoadKind::kDexCacheViaMethod: + break; + } + return desired_class_load_kind; +} + void LocationsBuilderX86::VisitLoadClass(HLoadClass* cls) { - InvokeRuntimeCallingConvention calling_convention; - CodeGenerator::CreateLoadClassLocationSummary( - cls, - Location::RegisterLocation(calling_convention.GetRegisterAt(0)), - Location::RegisterLocation(EAX), - /* code_generator_supports_read_barrier */ true); + if (cls->NeedsAccessCheck()) { + InvokeRuntimeCallingConvention calling_convention; + CodeGenerator::CreateLoadClassLocationSummary( + cls, + Location::RegisterLocation(calling_convention.GetRegisterAt(0)), + Location::RegisterLocation(EAX), + /* code_generator_supports_read_barrier */ true); + return; + } + + LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier) + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind); + HLoadClass::LoadKind load_kind = cls->GetLoadKind(); + if (load_kind == HLoadClass::LoadKind::kReferrersClass || + load_kind == HLoadClass::LoadKind::kDexCacheViaMethod || + load_kind == HLoadClass::LoadKind::kBootImageLinkTimePcRelative || + load_kind == HLoadClass::LoadKind::kDexCachePcRelative) { + locations->SetInAt(0, Location::RequiresRegister()); + } + locations->SetOut(Location::RequiresRegister()); } void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) { @@ -5896,39 +5981,86 @@ void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) { Location out_loc = locations->Out(); Register out = out_loc.AsRegister<Register>(); - Register current_method = locations->InAt(0).AsRegister<Register>(); - - if (cls->IsReferrersClass()) { - DCHECK(!cls->CanCallRuntime()); - DCHECK(!cls->MustGenerateClinitCheck()); - // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_ - GenerateGcRootFieldLoad( - cls, out_loc, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value())); - } else { - // /* GcRoot<mirror::Class>[] */ out = - // current_method.ptr_sized_fields_->dex_cache_resolved_types_ - __ movl(out, Address(current_method, - ArtMethod::DexCacheResolvedTypesOffset(kX86PointerSize).Int32Value())); - // /* GcRoot<mirror::Class> */ out = out[type_index] - GenerateGcRootFieldLoad( - cls, out_loc, Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex()))); - - if (!cls->IsInDexCache() || cls->MustGenerateClinitCheck()) { - DCHECK(cls->CanCallRuntime()); - SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathX86( - cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck()); - codegen_->AddSlowPath(slow_path); - if (!cls->IsInDexCache()) { - __ testl(out, out); - __ j(kEqual, slow_path->GetEntryLabel()); - } + bool generate_null_check = false; + switch (cls->GetLoadKind()) { + case HLoadClass::LoadKind::kReferrersClass: { + DCHECK(!cls->CanCallRuntime()); + DCHECK(!cls->MustGenerateClinitCheck()); + // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_ + Register current_method = locations->InAt(0).AsRegister<Register>(); + GenerateGcRootFieldLoad( + cls, out_loc, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value())); + break; + } + case HLoadClass::LoadKind::kBootImageLinkTimeAddress: { + DCHECK(!kEmitCompilerReadBarrier); + __ movl(out, Immediate(/* placeholder */ 0)); + codegen_->RecordTypePatch(cls); + break; + } + case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: { + DCHECK(!kEmitCompilerReadBarrier); + Register method_address = locations->InAt(0).AsRegister<Register>(); + __ leal(out, Address(method_address, CodeGeneratorX86::kDummy32BitOffset)); + codegen_->RecordTypePatch(cls); + break; + } + case HLoadClass::LoadKind::kBootImageAddress: { + DCHECK(!kEmitCompilerReadBarrier); + DCHECK_NE(cls->GetAddress(), 0u); + uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress()); + __ movl(out, Immediate(address)); + codegen_->RecordSimplePatch(); + break; + } + case HLoadClass::LoadKind::kDexCacheAddress: { + DCHECK_NE(cls->GetAddress(), 0u); + uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress()); + // /* GcRoot<mirror::Class> */ out = *address + GenerateGcRootFieldLoad(cls, out_loc, Address::Absolute(address)); + generate_null_check = !cls->IsInDexCache(); + break; + } + case HLoadClass::LoadKind::kDexCachePcRelative: { + Register base_reg = locations->InAt(0).AsRegister<Register>(); + uint32_t offset = cls->GetDexCacheElementOffset(); + Label* fixup_label = codegen_->NewPcRelativeDexCacheArrayPatch(cls->GetDexFile(), offset); + // /* GcRoot<mirror::Class> */ out = *(base + offset) /* PC-relative */ + GenerateGcRootFieldLoad( + cls, out_loc, Address(base_reg, CodeGeneratorX86::kDummy32BitOffset), fixup_label); + generate_null_check = !cls->IsInDexCache(); + break; + } + case HLoadClass::LoadKind::kDexCacheViaMethod: { + // /* GcRoot<mirror::Class>[] */ out = + // current_method.ptr_sized_fields_->dex_cache_resolved_types_ + Register current_method = locations->InAt(0).AsRegister<Register>(); + __ movl(out, Address(current_method, + ArtMethod::DexCacheResolvedTypesOffset(kX86PointerSize).Int32Value())); + // /* GcRoot<mirror::Class> */ out = out[type_index] + GenerateGcRootFieldLoad( + cls, out_loc, Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex()))); + generate_null_check = !cls->IsInDexCache(); + break; + } + } - if (cls->MustGenerateClinitCheck()) { - GenerateClassInitializationCheck(slow_path, out); - } else { - __ Bind(slow_path->GetExitLabel()); - } + if (generate_null_check || cls->MustGenerateClinitCheck()) { + DCHECK(cls->CanCallRuntime()); + SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathX86( + cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck()); + codegen_->AddSlowPath(slow_path); + + if (generate_null_check) { + __ testl(out, out); + __ j(kEqual, slow_path->GetEntryLabel()); + } + + if (cls->MustGenerateClinitCheck()) { + GenerateClassInitializationCheck(slow_path, out); + } else { + __ Bind(slow_path->GetExitLabel()); } } } @@ -6045,6 +6177,7 @@ void InstructionCodeGeneratorX86::VisitLoadString(HLoadString* load) { case HLoadString::LoadKind::kDexCacheAddress: { DCHECK_NE(load->GetAddress(), 0u); uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress()); + // /* GcRoot<mirror::String> */ out = *address GenerateGcRootFieldLoad(load, out_loc, Address::Absolute(address)); break; } @@ -6052,6 +6185,7 @@ void InstructionCodeGeneratorX86::VisitLoadString(HLoadString* load) { Register base_reg = locations->InAt(0).AsRegister<Register>(); uint32_t offset = load->GetDexCacheElementOffset(); Label* fixup_label = codegen_->NewPcRelativeDexCacheArrayPatch(load->GetDexFile(), offset); + // /* GcRoot<mirror::String> */ out = *(base + offset) /* PC-relative */ GenerateGcRootFieldLoad( load, out_loc, Address(base_reg, CodeGeneratorX86::kDummy32BitOffset), fixup_label); break; @@ -6853,6 +6987,9 @@ void CodeGeneratorX86::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instr DCHECK(kEmitCompilerReadBarrier); DCHECK(kUseBakerReadBarrier); + static_assert( + sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), + "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); // /* HeapReference<Object> */ ref = // *(obj + data_offset + index * sizeof(HeapReference<Object>)) Address src = index.IsConstant() ? diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 98dc8ca280..2a9fb80995 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -391,6 +391,11 @@ class CodeGeneratorX86 : public CodeGenerator { HLoadString::LoadKind GetSupportedLoadStringKind( HLoadString::LoadKind desired_string_load_kind) OVERRIDE; + // Check if the desired_class_load_kind is supported. If it is, return it, + // otherwise return a fall-back kind that should be used instead. + HLoadClass::LoadKind GetSupportedLoadClassKind( + HLoadClass::LoadKind desired_class_load_kind) OVERRIDE; + // Check if the desired_dispatch_info is supported. If it is, return it, // otherwise return a fall-back info that should be used instead. HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch( @@ -405,6 +410,7 @@ class CodeGeneratorX86 : public CodeGenerator { void RecordSimplePatch(); void RecordStringPatch(HLoadString* load_string); + void RecordTypePatch(HLoadClass* load_class); Label* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset); void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE; @@ -485,6 +491,14 @@ class CodeGeneratorX86 : public CodeGenerator { Location index, Location temp, bool needs_null_check); + // Factored implementation used by GenerateFieldLoadWithBakerReadBarrier + // and GenerateArrayLoadWithBakerReadBarrier. + void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction, + Location ref, + Register obj, + const Address& src, + Location temp, + bool needs_null_check); // Generate a read barrier for a heap reference within `instruction` // using a slow path. @@ -555,15 +569,6 @@ class CodeGeneratorX86 : public CodeGenerator { static constexpr int32_t kDummy32BitOffset = 256; private: - // Factored implementation of GenerateFieldLoadWithBakerReadBarrier - // and GenerateArrayLoadWithBakerReadBarrier. - void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction, - Location ref, - Register obj, - const Address& src, - Location temp, - bool needs_null_check); - Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, Register temp); struct PcRelativeDexCacheAccessInfo { @@ -594,6 +599,8 @@ class CodeGeneratorX86 : public CodeGenerator { ArenaDeque<Label> simple_patches_; // String patch locations. ArenaDeque<StringPatchInfo<Label>> string_patches_; + // Type patch locations. + ArenaDeque<TypePatchInfo<Label>> type_patches_; // Offset to the start of the constant area in the assembled code. // Used for fixups to the constant area. diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index a8da5f2ea5..cac33cddb8 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -204,10 +204,14 @@ class BoundsCheckSlowPathX86_64 : public SlowPathCode { locations->InAt(1), Location::RegisterLocation(calling_convention.GetRegisterAt(1)), Primitive::kPrimInt); - x86_64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowArrayBounds), + uint32_t entry_point_offset = instruction_->AsBoundsCheck()->IsStringCharAt() + ? QUICK_ENTRY_POINT(pThrowStringBounds) + : QUICK_ENTRY_POINT(pThrowArrayBounds); + x86_64_codegen->InvokeRuntime(entry_point_offset, instruction_, instruction_->GetDexPc(), this); + CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>(); CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>(); } @@ -465,7 +469,9 @@ class ReadBarrierMarkSlowPathX86_64 : public SlowPathCode { instruction_->IsLoadClass() || instruction_->IsLoadString() || instruction_->IsInstanceOf() || - instruction_->IsCheckCast()) + instruction_->IsCheckCast() || + ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) && + instruction_->GetLocations()->Intrinsified())) << "Unexpected instruction in read barrier marking slow path: " << instruction_->DebugName(); @@ -528,8 +534,12 @@ class ReadBarrierForHeapReferenceSlowPathX86_64 : public SlowPathCode { CpuRegister reg_out = out_.AsRegister<CpuRegister>(); DCHECK(locations->CanCall()); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out.AsRegister())) << out_; - DCHECK(!instruction_->IsInvoke() || - (instruction_->IsInvokeStaticOrDirect() && + DCHECK(instruction_->IsInstanceFieldGet() || + instruction_->IsStaticFieldGet() || + instruction_->IsArrayGet() || + instruction_->IsInstanceOf() || + instruction_->IsCheckCast() || + ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) && instruction_->GetLocations()->Intrinsified())) << "Unexpected instruction in read barrier for heap reference slow path: " << instruction_->DebugName(); @@ -542,7 +552,7 @@ class ReadBarrierForHeapReferenceSlowPathX86_64 : public SlowPathCode { // introduce a copy of it, `index`. Location index = index_; if (index_.IsValid()) { - // Handle `index_` for HArrayGet and intrinsic UnsafeGetObject. + // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics. if (instruction_->IsArrayGet()) { // Compute real offset and store it in index_. Register index_reg = index_.AsRegister<CpuRegister>().AsRegister(); @@ -590,7 +600,11 @@ class ReadBarrierForHeapReferenceSlowPathX86_64 : public SlowPathCode { "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); __ AddImmediate(CpuRegister(index_reg), Immediate(offset_)); } else { - DCHECK(instruction_->IsInvoke()); + // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile + // intrinsics, `index_` is not shifted by a scale factor of 2 + // (as in the case of ArrayGet), as it is actually an offset + // to an object field within an object. + DCHECK(instruction_->IsInvoke()) << instruction_->DebugName(); DCHECK(instruction_->GetLocations()->Intrinsified()); DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) || (instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile)) @@ -894,6 +908,11 @@ void CodeGeneratorX86_64::RecordStringPatch(HLoadString* load_string) { __ Bind(&string_patches_.back().label); } +void CodeGeneratorX86_64::RecordTypePatch(HLoadClass* load_class) { + type_patches_.emplace_back(load_class->GetDexFile(), load_class->GetTypeIndex()); + __ Bind(&type_patches_.back().label); +} + Label* CodeGeneratorX86_64::NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset) { // Add a patch entry and return the label. @@ -908,7 +927,8 @@ void CodeGeneratorX86_64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_pat relative_call_patches_.size() + pc_relative_dex_cache_patches_.size() + simple_patches_.size() + - string_patches_.size(); + string_patches_.size() + + type_patches_.size(); linker_patches->reserve(size); // The label points to the end of the "movl" insn but the literal offset for method // patch needs to point to the embedded constant which occupies the last 4 bytes. @@ -944,6 +964,14 @@ void CodeGeneratorX86_64::EmitLinkerPatches(ArenaVector<LinkerPatch>* linker_pat info.label.Position(), info.string_index)); } + for (const TypePatchInfo<Label>& info : type_patches_) { + // These are always PC-relative, see GetSupportedLoadClassKind(). + uint32_t literal_offset = info.label.Position() - kLabelPositionToLiteralOffsetAdjustment; + linker_patches->push_back(LinkerPatch::RelativeTypePatch(literal_offset, + &info.dex_file, + info.label.Position(), + info.type_index)); + } } void CodeGeneratorX86_64::DumpCoreRegister(std::ostream& stream, int reg) const { @@ -1023,6 +1051,7 @@ CodeGeneratorX86_64::CodeGeneratorX86_64(HGraph* graph, pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), simple_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), + type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)), fixups_to_jump_tables_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) { AddAllocatedRegister(Location::RegisterLocation(kFakeReturnRegister)); } @@ -4540,11 +4569,11 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) { CpuRegister obj = obj_loc.AsRegister<CpuRegister>(); Location index = locations->InAt(1); Location out_loc = locations->Out(); + uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction); Primitive::Type type = instruction->GetType(); switch (type) { case Primitive::kPrimBoolean: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value(); CpuRegister out = out_loc.AsRegister<CpuRegister>(); if (index.IsConstant()) { __ movzxb(out, Address(obj, @@ -4556,7 +4585,6 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimByte: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value(); CpuRegister out = out_loc.AsRegister<CpuRegister>(); if (index.IsConstant()) { __ movsxb(out, Address(obj, @@ -4568,7 +4596,6 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimShort: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value(); CpuRegister out = out_loc.AsRegister<CpuRegister>(); if (index.IsConstant()) { __ movsxw(out, Address(obj, @@ -4580,7 +4607,6 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimChar: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value(); CpuRegister out = out_loc.AsRegister<CpuRegister>(); if (index.IsConstant()) { __ movzxw(out, Address(obj, @@ -4592,7 +4618,6 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimInt: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); CpuRegister out = out_loc.AsRegister<CpuRegister>(); if (index.IsConstant()) { __ movl(out, Address(obj, @@ -4607,7 +4632,6 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) { static_assert( sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value(); // /* HeapReference<Object> */ out = // *(obj + data_offset + index * sizeof(HeapReference<Object>)) if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { @@ -4641,7 +4665,6 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimLong: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value(); CpuRegister out = out_loc.AsRegister<CpuRegister>(); if (index.IsConstant()) { __ movq(out, Address(obj, @@ -4653,7 +4676,6 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimFloat: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value(); XmmRegister out = out_loc.AsFpuRegister<XmmRegister>(); if (index.IsConstant()) { __ movss(out, Address(obj, @@ -4665,7 +4687,6 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) { } case Primitive::kPrimDouble: { - uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value(); XmmRegister out = out_loc.AsFpuRegister<XmmRegister>(); if (index.IsConstant()) { __ movsd(out, Address(obj, @@ -5317,13 +5338,64 @@ void InstructionCodeGeneratorX86_64::GenerateClassInitializationCheck( // No need for memory fence, thanks to the x86-64 memory model. } +HLoadClass::LoadKind CodeGeneratorX86_64::GetSupportedLoadClassKind( + HLoadClass::LoadKind desired_class_load_kind) { + if (kEmitCompilerReadBarrier) { + switch (desired_class_load_kind) { + case HLoadClass::LoadKind::kBootImageLinkTimeAddress: + case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: + case HLoadClass::LoadKind::kBootImageAddress: + // TODO: Implement for read barrier. + return HLoadClass::LoadKind::kDexCacheViaMethod; + default: + break; + } + } + switch (desired_class_load_kind) { + case HLoadClass::LoadKind::kReferrersClass: + break; + case HLoadClass::LoadKind::kBootImageLinkTimeAddress: + DCHECK(!GetCompilerOptions().GetCompilePic()); + // We prefer the always-available RIP-relative address for the x86-64 boot image. + return HLoadClass::LoadKind::kBootImageLinkTimePcRelative; + case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: + DCHECK(GetCompilerOptions().GetCompilePic()); + break; + case HLoadClass::LoadKind::kBootImageAddress: + break; + case HLoadClass::LoadKind::kDexCacheAddress: + DCHECK(Runtime::Current()->UseJitCompilation()); + break; + case HLoadClass::LoadKind::kDexCachePcRelative: + DCHECK(!Runtime::Current()->UseJitCompilation()); + break; + case HLoadClass::LoadKind::kDexCacheViaMethod: + break; + } + return desired_class_load_kind; +} + void LocationsBuilderX86_64::VisitLoadClass(HLoadClass* cls) { - InvokeRuntimeCallingConvention calling_convention; - CodeGenerator::CreateLoadClassLocationSummary( - cls, - Location::RegisterLocation(calling_convention.GetRegisterAt(0)), - Location::RegisterLocation(RAX), - /* code_generator_supports_read_barrier */ true); + if (cls->NeedsAccessCheck()) { + InvokeRuntimeCallingConvention calling_convention; + CodeGenerator::CreateLoadClassLocationSummary( + cls, + Location::RegisterLocation(calling_convention.GetRegisterAt(0)), + Location::RegisterLocation(RAX), + /* code_generator_supports_read_barrier */ true); + return; + } + + LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier) + ? LocationSummary::kCallOnSlowPath + : LocationSummary::kNoCall; + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind); + HLoadClass::LoadKind load_kind = cls->GetLoadKind(); + if (load_kind == HLoadClass::LoadKind::kReferrersClass || + load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) { + locations->SetInAt(0, Location::RequiresRegister()); + } + locations->SetOut(Location::RequiresRegister()); } void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) { @@ -5340,37 +5412,86 @@ void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) { Location out_loc = locations->Out(); CpuRegister out = out_loc.AsRegister<CpuRegister>(); - CpuRegister current_method = locations->InAt(0).AsRegister<CpuRegister>(); - - if (cls->IsReferrersClass()) { - DCHECK(!cls->CanCallRuntime()); - DCHECK(!cls->MustGenerateClinitCheck()); - // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_ - GenerateGcRootFieldLoad( - cls, out_loc, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value())); - } else { - // /* GcRoot<mirror::Class>[] */ out = - // current_method.ptr_sized_fields_->dex_cache_resolved_types_ - __ movq(out, Address(current_method, - ArtMethod::DexCacheResolvedTypesOffset(kX86_64PointerSize).Int32Value())); - // /* GcRoot<mirror::Class> */ out = out[type_index] - GenerateGcRootFieldLoad( - cls, out_loc, Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex()))); - - if (!cls->IsInDexCache() || cls->MustGenerateClinitCheck()) { - DCHECK(cls->CanCallRuntime()); - SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathX86_64( - cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck()); - codegen_->AddSlowPath(slow_path); - if (!cls->IsInDexCache()) { - __ testl(out, out); - __ j(kEqual, slow_path->GetEntryLabel()); - } - if (cls->MustGenerateClinitCheck()) { - GenerateClassInitializationCheck(slow_path, out); + + bool generate_null_check = false; + switch (cls->GetLoadKind()) { + case HLoadClass::LoadKind::kReferrersClass: { + DCHECK(!cls->CanCallRuntime()); + DCHECK(!cls->MustGenerateClinitCheck()); + // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_ + CpuRegister current_method = locations->InAt(0).AsRegister<CpuRegister>(); + GenerateGcRootFieldLoad( + cls, out_loc, Address(current_method, ArtMethod::DeclaringClassOffset().Int32Value())); + break; + } + case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: + DCHECK(!kEmitCompilerReadBarrier); + __ leal(out, Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, /* no_rip */ false)); + codegen_->RecordTypePatch(cls); + break; + case HLoadClass::LoadKind::kBootImageAddress: { + DCHECK(!kEmitCompilerReadBarrier); + DCHECK_NE(cls->GetAddress(), 0u); + uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress()); + __ movl(out, Immediate(address)); // Zero-extended. + codegen_->RecordSimplePatch(); + break; + } + case HLoadClass::LoadKind::kDexCacheAddress: { + DCHECK_NE(cls->GetAddress(), 0u); + // /* GcRoot<mirror::Class> */ out = *address + if (IsUint<32>(cls->GetAddress())) { + Address address = Address::Absolute(cls->GetAddress(), /* no_rip */ true); + GenerateGcRootFieldLoad(cls, out_loc, address); } else { - __ Bind(slow_path->GetExitLabel()); + // TODO: Consider using opcode A1, i.e. movl eax, moff32 (with 64-bit address). + __ movq(out, Immediate(cls->GetAddress())); + GenerateGcRootFieldLoad(cls, out_loc, Address(out, 0)); } + generate_null_check = !cls->IsInDexCache(); + break; + } + case HLoadClass::LoadKind::kDexCachePcRelative: { + uint32_t offset = cls->GetDexCacheElementOffset(); + Label* fixup_label = codegen_->NewPcRelativeDexCacheArrayPatch(cls->GetDexFile(), offset); + Address address = Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, + /* no_rip */ false); + // /* GcRoot<mirror::Class> */ out = *address /* PC-relative */ + GenerateGcRootFieldLoad(cls, out_loc, address, fixup_label); + generate_null_check = !cls->IsInDexCache(); + break; + } + case HLoadClass::LoadKind::kDexCacheViaMethod: { + // /* GcRoot<mirror::Class>[] */ out = + // current_method.ptr_sized_fields_->dex_cache_resolved_types_ + CpuRegister current_method = locations->InAt(0).AsRegister<CpuRegister>(); + __ movq(out, + Address(current_method, + ArtMethod::DexCacheResolvedTypesOffset(kX86_64PointerSize).Int32Value())); + // /* GcRoot<mirror::Class> */ out = out[type_index] + GenerateGcRootFieldLoad( + cls, out_loc, Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex()))); + generate_null_check = !cls->IsInDexCache(); + break; + } + default: + LOG(FATAL) << "Unexpected load kind: " << cls->GetLoadKind(); + UNREACHABLE(); + } + + if (generate_null_check || cls->MustGenerateClinitCheck()) { + DCHECK(cls->CanCallRuntime()); + SlowPathCode* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathX86_64( + cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck()); + codegen_->AddSlowPath(slow_path); + if (generate_null_check) { + __ testl(out, out); + __ j(kEqual, slow_path->GetEntryLabel()); + } + if (cls->MustGenerateClinitCheck()) { + GenerateClassInitializationCheck(slow_path, out); + } else { + __ Bind(slow_path->GetExitLabel()); } } } @@ -5461,6 +5582,7 @@ void InstructionCodeGeneratorX86_64::VisitLoadString(HLoadString* load) { } case HLoadString::LoadKind::kDexCacheAddress: { DCHECK_NE(load->GetAddress(), 0u); + // /* GcRoot<mirror::String> */ out = *address if (IsUint<32>(load->GetAddress())) { Address address = Address::Absolute(load->GetAddress(), /* no_rip */ true); GenerateGcRootFieldLoad(load, out_loc, address); @@ -5476,6 +5598,7 @@ void InstructionCodeGeneratorX86_64::VisitLoadString(HLoadString* load) { Label* fixup_label = codegen_->NewPcRelativeDexCacheArrayPatch(load->GetDexFile(), offset); Address address = Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, /* no_rip */ false); + // /* GcRoot<mirror::String> */ out = *address /* PC-relative */ GenerateGcRootFieldLoad(load, out_loc, address, fixup_label); break; } @@ -6317,6 +6440,9 @@ void CodeGeneratorX86_64::GenerateArrayLoadWithBakerReadBarrier(HInstruction* in DCHECK(kEmitCompilerReadBarrier); DCHECK(kUseBakerReadBarrier); + static_assert( + sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t), + "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes."); // /* HeapReference<Object> */ ref = // *(obj + data_offset + index * sizeof(HeapReference<Object>)) Address src = index.IsConstant() ? diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 7cf12459b0..d7cfd37c33 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -388,6 +388,11 @@ class CodeGeneratorX86_64 : public CodeGenerator { HLoadString::LoadKind GetSupportedLoadStringKind( HLoadString::LoadKind desired_string_load_kind) OVERRIDE; + // Check if the desired_class_load_kind is supported. If it is, return it, + // otherwise return a fall-back kind that should be used instead. + HLoadClass::LoadKind GetSupportedLoadClassKind( + HLoadClass::LoadKind desired_class_load_kind) OVERRIDE; + // Check if the desired_dispatch_info is supported. If it is, return it, // otherwise return a fall-back info that should be used instead. HInvokeStaticOrDirect::DispatchInfo GetSupportedInvokeStaticOrDirectDispatch( @@ -400,6 +405,7 @@ class CodeGeneratorX86_64 : public CodeGenerator { void RecordSimplePatch(); void RecordStringPatch(HLoadString* load_string); + void RecordTypePatch(HLoadClass* load_class); Label* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file, uint32_t element_offset); void MoveFromReturnRegister(Location trg, Primitive::Type type) OVERRIDE; @@ -427,6 +433,14 @@ class CodeGeneratorX86_64 : public CodeGenerator { Location index, Location temp, bool needs_null_check); + // Factored implementation used by GenerateFieldLoadWithBakerReadBarrier + // and GenerateArrayLoadWithBakerReadBarrier. + void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction, + Location ref, + CpuRegister obj, + const Address& src, + Location temp, + bool needs_null_check); // Generate a read barrier for a heap reference within `instruction` // using a slow path. @@ -529,15 +543,6 @@ class CodeGeneratorX86_64 : public CodeGenerator { static constexpr int32_t kDummy32BitOffset = 256; private: - // Factored implementation of GenerateFieldLoadWithBakerReadBarrier - // and GenerateArrayLoadWithBakerReadBarrier. - void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction, - Location ref, - CpuRegister obj, - const Address& src, - Location temp, - bool needs_null_check); - struct PcRelativeDexCacheAccessInfo { PcRelativeDexCacheAccessInfo(const DexFile& dex_file, uint32_t element_off) : target_dex_file(dex_file), element_offset(element_off), label() { } @@ -569,6 +574,8 @@ class CodeGeneratorX86_64 : public CodeGenerator { ArenaDeque<Label> simple_patches_; // String patch locations. ArenaDeque<StringPatchInfo<Label>> string_patches_; + // Type patch locations. + ArenaDeque<TypePatchInfo<Label>> type_patches_; // Fixups for jump tables need to be handled specially. ArenaVector<JumpTableRIPFixup*> fixups_to_jump_tables_; diff --git a/compiler/optimizing/dex_cache_array_fixups_arm.cc b/compiler/optimizing/dex_cache_array_fixups_arm.cc index e9072b9c77..14c318e21f 100644 --- a/compiler/optimizing/dex_cache_array_fixups_arm.cc +++ b/compiler/optimizing/dex_cache_array_fixups_arm.cc @@ -44,8 +44,23 @@ class DexCacheArrayFixupsVisitor : public HGraphVisitor { } private: + void VisitLoadClass(HLoadClass* load_class) OVERRIDE { + // If this is a load with PC-relative access to the dex cache types array, + // we need to add the dex cache arrays base as the special input. + if (load_class->GetLoadKind() == HLoadClass::LoadKind::kDexCachePcRelative) { + // Initialize base for target dex file if needed. + const DexFile& dex_file = load_class->GetDexFile(); + HArmDexCacheArraysBase* base = GetOrCreateDexCacheArrayBase(dex_file); + // Update the element offset in base. + DexCacheArraysLayout layout(kArmPointerSize, &dex_file); + base->UpdateElementOffset(layout.TypeOffset(load_class->GetTypeIndex())); + // Add the special argument base to the load. + load_class->AddSpecialInput(base); + } + } + void VisitLoadString(HLoadString* load_string) OVERRIDE { - // If this is a load with PC-relative access to the dex cache methods array, + // If this is a load with PC-relative access to the dex cache strings array, // we need to add the dex cache arrays base as the special input. if (load_string->GetLoadKind() == HLoadString::LoadKind::kDexCachePcRelative) { // Initialize base for target dex file if needed. diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 3084a4ff2b..4af8d1985b 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -372,6 +372,10 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { } void VisitLoadClass(HLoadClass* load_class) OVERRIDE { + StartAttributeStream("load_kind") << load_class->GetLoadKind(); + const char* descriptor = load_class->GetDexFile().GetTypeDescriptor( + load_class->GetDexFile().GetTypeId(load_class->GetTypeIndex())); + StartAttributeStream("class_name") << PrettyDescriptor(descriptor); StartAttributeStream("gen_clinit_check") << std::boolalpha << load_class->MustGenerateClinitCheck() << std::noboolalpha; StartAttributeStream("needs_access_check") << std::boolalpha @@ -399,6 +403,16 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { << array_length->IsStringLength() << std::noboolalpha; } + void VisitBoundsCheck(HBoundsCheck* bounds_check) OVERRIDE { + StartAttributeStream("is_string_char_at") << std::boolalpha + << bounds_check->IsStringCharAt() << std::noboolalpha; + } + + void VisitArrayGet(HArrayGet* array_get) OVERRIDE { + StartAttributeStream("is_string_char_at") << std::boolalpha + << array_get->IsStringCharAt() << std::noboolalpha; + } + void VisitArraySet(HArraySet* array_set) OVERRIDE { StartAttributeStream("value_can_be_null") << std::boolalpha << array_set->GetValueCanBeNull() << std::noboolalpha; diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc index 0a5cf80e9d..52426d73c6 100644 --- a/compiler/optimizing/induction_var_analysis.cc +++ b/compiler/optimizing/induction_var_analysis.cc @@ -670,7 +670,7 @@ void HInductionVarAnalysis::VisitTripCount(HLoopInformation* loop, // an unsigned entity, for example, as in the following loop that uses the full range: // for (int i = INT_MIN; i < INT_MAX; i++) // TC = UINT_MAX // (2) The TC is only valid if the loop is taken, otherwise TC = 0, as in: - // for (int i = 12; i < U; i++) // TC = 0 when U < 12 + // for (int i = 12; i < U; i++) // TC = 0 when U <= 12 // If this cannot be determined at compile-time, the TC is only valid within the // loop-body proper, not the loop-header unless enforced with an explicit taken-test. // (3) The TC is only valid if the loop is finite, otherwise TC has no value, as in: diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 27b6896150..8f2db3d1d3 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -756,7 +756,15 @@ bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction, ArtMethod* metho invoke_instruction->ReplaceWith(return_replacement); } invoke_instruction->GetBlock()->RemoveInstruction(invoke_instruction); - FixUpReturnReferenceType(invoke_instruction, method, return_replacement, do_rtp); + FixUpReturnReferenceType(method, return_replacement); + if (do_rtp && ReturnTypeMoreSpecific(invoke_instruction, return_replacement)) { + // Actual return value has a more specific type than the method's declared + // return type. Run RTP again on the outer graph to propagate it. + ReferenceTypePropagation(graph_, + outer_compilation_unit_.GetDexCache(), + handles_, + /* is_first_run */ false).Run(); + } return true; } @@ -1159,6 +1167,15 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, } } + // We have replaced formal arguments with actual arguments. If actual types + // are more specific than the declared ones, run RTP again on the inner graph. + if (ArgumentTypesMoreSpecific(invoke_instruction, resolved_method)) { + ReferenceTypePropagation(callee_graph, + dex_compilation_unit.GetDexCache(), + handles_, + /* is_first_run */ false).Run(); + } + size_t number_of_instructions_budget = kMaximumNumberOfHInstructions; size_t number_of_inlined_instructions = RunOptimizations(callee_graph, code_item, dex_compilation_unit); @@ -1332,13 +1349,87 @@ size_t HInliner::RunOptimizations(HGraph* callee_graph, return number_of_inlined_instructions; } -void HInliner::FixUpReturnReferenceType(HInvoke* invoke_instruction, - ArtMethod* resolved_method, - HInstruction* return_replacement, - bool do_rtp) { +static bool IsReferenceTypeRefinement(ReferenceTypeInfo declared_rti, + bool declared_can_be_null, + HInstruction* actual_obj) + SHARED_REQUIRES(Locks::mutator_lock_) { + if (declared_can_be_null && !actual_obj->CanBeNull()) { + return true; + } + + ReferenceTypeInfo actual_rti = actual_obj->GetReferenceTypeInfo(); + return (actual_rti.IsExact() && !declared_rti.IsExact()) || + declared_rti.IsStrictSupertypeOf(actual_rti); +} + +ReferenceTypeInfo HInliner::GetClassRTI(mirror::Class* klass) { + return ReferenceTypePropagation::IsAdmissible(klass) + ? ReferenceTypeInfo::Create(handles_->NewHandle(klass)) + : graph_->GetInexactObjectRti(); +} + +bool HInliner::ArgumentTypesMoreSpecific(HInvoke* invoke_instruction, ArtMethod* resolved_method) { + // If this is an instance call, test whether the type of the `this` argument + // is more specific than the class which declares the method. + if (!resolved_method->IsStatic()) { + if (IsReferenceTypeRefinement(GetClassRTI(resolved_method->GetDeclaringClass()), + /* declared_can_be_null */ false, + invoke_instruction->InputAt(0u))) { + return true; + } + } + + size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); + + // Iterate over the list of parameter types and test whether any of the + // actual inputs has a more specific reference type than the type declared in + // the signature. + const DexFile::TypeList* param_list = resolved_method->GetParameterTypeList(); + for (size_t param_idx = 0, + input_idx = resolved_method->IsStatic() ? 0 : 1, + e = (param_list == nullptr ? 0 : param_list->Size()); + param_idx < e; + ++param_idx, ++input_idx) { + HInstruction* input = invoke_instruction->InputAt(input_idx); + if (input->GetType() == Primitive::kPrimNot) { + mirror::Class* param_cls = resolved_method->GetDexCacheResolvedType( + param_list->GetTypeItem(param_idx).type_idx_, + pointer_size); + if (IsReferenceTypeRefinement(GetClassRTI(param_cls), + /* declared_can_be_null */ true, + input)) { + return true; + } + } + } + + return false; +} + +bool HInliner::ReturnTypeMoreSpecific(HInvoke* invoke_instruction, + HInstruction* return_replacement) { // Check the integrity of reference types and run another type propagation if needed. if (return_replacement != nullptr) { if (return_replacement->GetType() == Primitive::kPrimNot) { + // Test if the return type is a refinement of the declared return type. + if (IsReferenceTypeRefinement(invoke_instruction->GetReferenceTypeInfo(), + /* declared_can_be_null */ true, + return_replacement)) { + return true; + } + } else if (return_replacement->IsInstanceOf()) { + // Inlining InstanceOf into an If may put a tighter bound on reference types. + return true; + } + } + + return false; +} + +void HInliner::FixUpReturnReferenceType(ArtMethod* resolved_method, + HInstruction* return_replacement) { + if (return_replacement != nullptr) { + if (return_replacement->GetType() == Primitive::kPrimNot) { if (!return_replacement->GetReferenceTypeInfo().IsValid()) { // Make sure that we have a valid type for the return. We may get an invalid one when // we inline invokes with multiple branches and create a Phi for the result. @@ -1347,36 +1438,7 @@ void HInliner::FixUpReturnReferenceType(HInvoke* invoke_instruction, DCHECK(return_replacement->IsPhi()); size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); mirror::Class* cls = resolved_method->GetReturnType(false /* resolve */, pointer_size); - if (cls != nullptr && !cls->IsErroneous()) { - ReferenceTypeInfo::TypeHandle return_handle = handles_->NewHandle(cls); - return_replacement->SetReferenceTypeInfo(ReferenceTypeInfo::Create( - return_handle, return_handle->CannotBeAssignedFromOtherTypes() /* is_exact */)); - } else { - // Return inexact object type on failures. - return_replacement->SetReferenceTypeInfo(graph_->GetInexactObjectRti()); - } - } - - if (do_rtp) { - // If the return type is a refinement of the declared type run the type propagation again. - ReferenceTypeInfo return_rti = return_replacement->GetReferenceTypeInfo(); - ReferenceTypeInfo invoke_rti = invoke_instruction->GetReferenceTypeInfo(); - if (invoke_rti.IsStrictSupertypeOf(return_rti) - || (return_rti.IsExact() && !invoke_rti.IsExact()) - || !return_replacement->CanBeNull()) { - ReferenceTypePropagation(graph_, - outer_compilation_unit_.GetDexCache(), - handles_, - /* is_first_run */ false).Run(); - } - } - } else if (return_replacement->IsInstanceOf()) { - if (do_rtp) { - // Inlining InstanceOf into an If may put a tighter bound on reference types. - ReferenceTypePropagation(graph_, - outer_compilation_unit_.GetDexCache(), - handles_, - /* is_first_run */ false).Run(); + return_replacement->SetReferenceTypeInfo(GetClassRTI(cls)); } } } diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h index 7cf1424b6d..02d3a5f499 100644 --- a/compiler/optimizing/inliner.h +++ b/compiler/optimizing/inliner.h @@ -124,10 +124,18 @@ class HInliner : public HOptimization { uint32_t dex_pc) const SHARED_REQUIRES(Locks::mutator_lock_); - void FixUpReturnReferenceType(HInvoke* invoke_instruction, - ArtMethod* resolved_method, - HInstruction* return_replacement, - bool do_rtp) + void FixUpReturnReferenceType(ArtMethod* resolved_method, HInstruction* return_replacement) + SHARED_REQUIRES(Locks::mutator_lock_); + + // Creates an instance of ReferenceTypeInfo from `klass` if `klass` is + // admissible (see ReferenceTypePropagation::IsAdmissible for details). + // Otherwise returns inexact Object RTI. + ReferenceTypeInfo GetClassRTI(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_); + + bool ArgumentTypesMoreSpecific(HInvoke* invoke_instruction, ArtMethod* resolved_method) + SHARED_REQUIRES(Locks::mutator_lock_); + + bool ReturnTypeMoreSpecific(HInvoke* invoke_instruction, HInstruction* return_replacement) SHARED_REQUIRES(Locks::mutator_lock_); // Add a type guard on the given `receiver`. This will add to the graph: diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index f2286e46e6..b4125299ea 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -933,7 +933,7 @@ bool HInstructionBuilder::BuildNewInstance(uint16_t type_index, uint32_t dex_pc) IsOutermostCompilingClass(type_index), dex_pc, needs_access_check, - compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_cache, type_index)); + /* is_in_dex_cache */ false); AppendInstruction(load_class); HInstruction* cls = load_class; @@ -1024,7 +1024,7 @@ HClinitCheck* HInstructionBuilder::ProcessClinitCheckForInvoke( is_outer_class, dex_pc, /*needs_access_check*/ false, - compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_cache, storage_index)); + /* is_in_dex_cache */ false); AppendInstruction(load_class); clinit_check = new (arena_) HClinitCheck(load_class, dex_pc); AppendInstruction(clinit_check); @@ -1376,15 +1376,13 @@ bool HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, } } - bool is_in_cache = - compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_cache, storage_index); HLoadClass* constant = new (arena_) HLoadClass(graph_->GetCurrentMethod(), storage_index, outer_dex_file, is_outer_class, dex_pc, /*needs_access_check*/ false, - is_in_cache); + /* is_in_dex_cache */ false); AppendInstruction(constant); HInstruction* cls = constant; @@ -1653,7 +1651,7 @@ void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction, IsOutermostCompilingClass(type_index), dex_pc, !can_access, - compiler_driver_->CanAssumeTypeIsPresentInDexCache(dex_cache, type_index)); + /* is_in_dex_cache */ false); AppendInstruction(cls); TypeCheckKind check_kind = ComputeTypeCheckKind(resolved_class); @@ -2621,8 +2619,6 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); bool can_access = compiler_driver_->CanAccessTypeWithoutChecks( dex_compilation_unit_->GetDexMethodIndex(), dex_cache, type_index); - bool is_in_dex_cache = - compiler_driver_->CanAssumeTypeIsPresentInDexCache(dex_cache, type_index); AppendInstruction(new (arena_) HLoadClass( graph_->GetCurrentMethod(), type_index, @@ -2630,7 +2626,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, IsOutermostCompilingClass(type_index), dex_pc, !can_access, - is_in_dex_cache)); + /* is_in_dex_cache */ false)); UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction()); break; } diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index eb1d1560db..3041c4d2c7 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -101,6 +101,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { void SimplifyCompare(HInvoke* invoke, bool is_signum, Primitive::Type type); void SimplifyIsNaN(HInvoke* invoke); void SimplifyFP2Int(HInvoke* invoke); + void SimplifyStringCharAt(HInvoke* invoke); void SimplifyStringIsEmptyOrLength(HInvoke* invoke); void SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind); @@ -235,22 +236,40 @@ bool InstructionSimplifierVisitor::TryDeMorganNegationFactoring(HBinaryOperation void InstructionSimplifierVisitor::VisitShift(HBinaryOperation* instruction) { DCHECK(instruction->IsShl() || instruction->IsShr() || instruction->IsUShr()); - HConstant* input_cst = instruction->GetConstantRight(); - HInstruction* input_other = instruction->GetLeastConstantLeft(); + HInstruction* shift_amount = instruction->GetRight(); + HInstruction* value = instruction->GetLeft(); - if (input_cst != nullptr) { - int64_t cst = Int64FromConstant(input_cst); - int64_t mask = (input_other->GetType() == Primitive::kPrimLong) - ? kMaxLongShiftDistance - : kMaxIntShiftDistance; - if ((cst & mask) == 0) { + int64_t implicit_mask = (value->GetType() == Primitive::kPrimLong) + ? kMaxLongShiftDistance + : kMaxIntShiftDistance; + + if (shift_amount->IsConstant()) { + int64_t cst = Int64FromConstant(shift_amount->AsConstant()); + if ((cst & implicit_mask) == 0) { // Replace code looking like - // SHL dst, src, 0 + // SHL dst, value, 0 // with - // src - instruction->ReplaceWith(input_other); + // value + instruction->ReplaceWith(value); instruction->GetBlock()->RemoveInstruction(instruction); RecordSimplification(); + return; + } + } + + // Shift operations implicitly mask the shift amount according to the type width. Get rid of + // unnecessary explicit masking operations on the shift amount. + // Replace code looking like + // AND masked_shift, shift, <superset of implicit mask> + // SHL dst, value, masked_shift + // with + // SHL dst, value, shift + if (shift_amount->IsAnd()) { + HAnd* and_insn = shift_amount->AsAnd(); + HConstant* mask = and_insn->GetConstantRight(); + if ((mask != nullptr) && ((Int64FromConstant(mask) & implicit_mask) == implicit_mask)) { + instruction->ReplaceInput(and_insn->GetLeastConstantLeft(), 1); + RecordSimplification(); } } } @@ -1685,13 +1704,32 @@ void InstructionSimplifierVisitor::SimplifyFP2Int(HInvoke* invoke) { invoke->ReplaceWithExceptInReplacementAtIndex(select, 0); // false at index 0 } +void InstructionSimplifierVisitor::SimplifyStringCharAt(HInvoke* invoke) { + HInstruction* str = invoke->InputAt(0); + HInstruction* index = invoke->InputAt(1); + uint32_t dex_pc = invoke->GetDexPc(); + ArenaAllocator* arena = GetGraph()->GetArena(); + // We treat String as an array to allow DCE and BCE to seamlessly work on strings, + // so create the HArrayLength, HBoundsCheck and HArrayGet. + HArrayLength* length = new (arena) HArrayLength(str, dex_pc, /* is_string_length */ true); + invoke->GetBlock()->InsertInstructionBefore(length, invoke); + HBoundsCheck* bounds_check = + new (arena) HBoundsCheck(index, length, dex_pc, invoke->GetDexMethodIndex()); + invoke->GetBlock()->InsertInstructionBefore(bounds_check, invoke); + HArrayGet* array_get = + new (arena) HArrayGet(str, index, Primitive::kPrimChar, dex_pc, /* is_string_char_at */ true); + invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, array_get); + bounds_check->CopyEnvironmentFrom(invoke->GetEnvironment()); + GetGraph()->SetHasBoundsChecks(true); +} + void InstructionSimplifierVisitor::SimplifyStringIsEmptyOrLength(HInvoke* invoke) { HInstruction* str = invoke->InputAt(0); uint32_t dex_pc = invoke->GetDexPc(); // We treat String as an array to allow DCE and BCE to seamlessly work on strings, // so create the HArrayLength. - HArrayLength* length = new (GetGraph()->GetArena()) HArrayLength(str, dex_pc); - length->MarkAsStringLength(); + HArrayLength* length = + new (GetGraph()->GetArena()) HArrayLength(str, dex_pc, /* is_string_length */ true); HInstruction* replacement; if (invoke->GetIntrinsic() == Intrinsics::kStringIsEmpty) { // For String.isEmpty(), create the `HEqual` representing the `length == 0`. @@ -1752,6 +1790,9 @@ void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) { case Intrinsics::kDoubleDoubleToLongBits: SimplifyFP2Int(instruction); break; + case Intrinsics::kStringCharAt: + SimplifyStringCharAt(instruction); + break; case Intrinsics::kStringIsEmpty: case Intrinsics::kStringLength: SimplifyStringIsEmptyOrLength(instruction); diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc index e4a711ec83..983d31d168 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.cc +++ b/compiler/optimizing/instruction_simplifier_arm64.cc @@ -19,6 +19,7 @@ #include "common_arm64.h" #include "instruction_simplifier_shared.h" #include "mirror/array-inl.h" +#include "mirror/string.h" namespace art { namespace arm64 { @@ -30,7 +31,7 @@ using helpers::ShifterOperandSupportsExtension; void InstructionSimplifierArm64Visitor::TryExtractArrayAccessAddress(HInstruction* access, HInstruction* array, HInstruction* index, - int access_size) { + size_t data_offset) { if (kEmitCompilerReadBarrier) { // The read barrier instrumentation does not support the // HArm64IntermediateAddress instruction yet. @@ -55,8 +56,7 @@ void InstructionSimplifierArm64Visitor::TryExtractArrayAccessAddress(HInstructio // Proceed to extract the base address computation. ArenaAllocator* arena = GetGraph()->GetArena(); - HIntConstant* offset = - GetGraph()->GetIntConstant(mirror::Array::DataOffset(access_size).Uint32Value()); + HIntConstant* offset = GetGraph()->GetIntConstant(data_offset); HArm64IntermediateAddress* address = new (arena) HArm64IntermediateAddress(array, offset, kNoDexPc); address->SetReferenceTypeInfo(array->GetReferenceTypeInfo()); @@ -189,17 +189,20 @@ void InstructionSimplifierArm64Visitor::VisitAnd(HAnd* instruction) { } void InstructionSimplifierArm64Visitor::VisitArrayGet(HArrayGet* instruction) { + size_t data_offset = CodeGenerator::GetArrayDataOffset(instruction); TryExtractArrayAccessAddress(instruction, instruction->GetArray(), instruction->GetIndex(), - Primitive::ComponentSize(instruction->GetType())); + data_offset); } void InstructionSimplifierArm64Visitor::VisitArraySet(HArraySet* instruction) { + size_t access_size = Primitive::ComponentSize(instruction->GetComponentType()); + size_t data_offset = mirror::Array::DataOffset(access_size).Uint32Value(); TryExtractArrayAccessAddress(instruction, instruction->GetArray(), instruction->GetIndex(), - Primitive::ComponentSize(instruction->GetComponentType())); + data_offset); } void InstructionSimplifierArm64Visitor::VisitMul(HMul* instruction) { diff --git a/compiler/optimizing/instruction_simplifier_arm64.h b/compiler/optimizing/instruction_simplifier_arm64.h index da269980e8..4735f85ab0 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.h +++ b/compiler/optimizing/instruction_simplifier_arm64.h @@ -38,7 +38,7 @@ class InstructionSimplifierArm64Visitor : public HGraphVisitor { void TryExtractArrayAccessAddress(HInstruction* access, HInstruction* array, HInstruction* index, - int access_size); + size_t data_offset); bool TryMergeIntoUsersShifterOperand(HInstruction* instruction); bool TryMergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op, diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h index 83a512738b..3429a8fdbb 100644 --- a/compiler/optimizing/intrinsics.h +++ b/compiler/optimizing/intrinsics.h @@ -239,6 +239,7 @@ UNREACHABLE_INTRINSIC(Arch, IntegerCompare) \ UNREACHABLE_INTRINSIC(Arch, LongCompare) \ UNREACHABLE_INTRINSIC(Arch, IntegerSignum) \ UNREACHABLE_INTRINSIC(Arch, LongSignum) \ +UNREACHABLE_INTRINSIC(Arch, StringCharAt) \ UNREACHABLE_INTRINSIC(Arch, StringIsEmpty) \ UNREACHABLE_INTRINSIC(Arch, StringLength) \ UNREACHABLE_INTRINSIC(Arch, UnsafeLoadFence) \ diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index 29f7672b0a..f43f8edf06 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -47,19 +47,6 @@ bool IntrinsicLocationsBuilderARM::TryDispatch(HInvoke* invoke) { if (res == nullptr) { return false; } - if (kEmitCompilerReadBarrier && res->CanCall()) { - // Generating an intrinsic for this HInvoke may produce an - // IntrinsicSlowPathARM slow path. Currently this approach - // does not work when using read barriers, as the emitted - // calling sequence will make use of another slow path - // (ReadBarrierForRootSlowPathARM for HInvokeStaticOrDirect, - // ReadBarrierSlowPathARM for HInvokeVirtual). So we bail - // out in this case. - // - // TODO: Find a way to have intrinsics work with read barriers. - invoke->SetLocations(nullptr); - return false; - } return res->Intrinsified(); } @@ -524,8 +511,8 @@ static void GenUnsafeGet(HInvoke* invoke, if (kEmitCompilerReadBarrier) { if (kUseBakerReadBarrier) { Location temp = locations->GetTemp(0); - codegen->GenerateArrayLoadWithBakerReadBarrier( - invoke, trg_loc, base, 0U, offset_loc, temp, /* needs_null_check */ false); + codegen->GenerateReferenceLoadWithBakerReadBarrier( + invoke, trg_loc, base, 0U, offset_loc, TIMES_1, temp, /* needs_null_check */ false); if (is_volatile) { __ dmb(ISH); } @@ -581,10 +568,11 @@ static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, locations->SetInAt(0, Location::NoLocation()); // Unused receiver. locations->SetInAt(1, Location::RequiresRegister()); locations->SetInAt(2, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + locations->SetOut(Location::RequiresRegister(), + can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap); if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) { // We need a temporary register for the read barrier marking slow - // path in InstructionCodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier. + // path in InstructionCodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier. locations->AddTemp(Location::RequiresRegister()); } } @@ -919,9 +907,10 @@ void IntrinsicLocationsBuilderARM::VisitUnsafeCASObject(HInvoke* invoke) { // The UnsafeCASObject intrinsic is missing a read barrier, and // therefore sometimes does not work as expected (b/25883050). // Turn it off temporarily as a quick fix, until the read barrier is - // implemented (see TODO in GenCAS below). + // implemented (see TODO in GenCAS). // - // TODO(rpl): Fix this issue and re-enable this intrinsic with read barriers. + // TODO(rpl): Implement read barrier support in GenCAS and re-enable + // this intrinsic. if (kEmitCompilerReadBarrier) { return; } @@ -932,56 +921,16 @@ void IntrinsicCodeGeneratorARM::VisitUnsafeCASInt(HInvoke* invoke) { GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_); } void IntrinsicCodeGeneratorARM::VisitUnsafeCASObject(HInvoke* invoke) { - GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_); -} - -void IntrinsicLocationsBuilderARM::VisitStringCharAt(HInvoke* invoke) { - LocationSummary* locations = new (arena_) LocationSummary(invoke, - LocationSummary::kCallOnSlowPath, - kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); - - locations->AddTemp(Location::RequiresRegister()); - locations->AddTemp(Location::RequiresRegister()); -} - -void IntrinsicCodeGeneratorARM::VisitStringCharAt(HInvoke* invoke) { - ArmAssembler* assembler = GetAssembler(); - LocationSummary* locations = invoke->GetLocations(); - - // Location of reference to data array - const MemberOffset value_offset = mirror::String::ValueOffset(); - // Location of count - const MemberOffset count_offset = mirror::String::CountOffset(); - - Register obj = locations->InAt(0).AsRegister<Register>(); // String object pointer. - Register idx = locations->InAt(1).AsRegister<Register>(); // Index of character. - Register out = locations->Out().AsRegister<Register>(); // Result character. - - Register temp = locations->GetTemp(0).AsRegister<Register>(); - Register array_temp = locations->GetTemp(1).AsRegister<Register>(); - - // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth - // the cost. - // TODO: For simplicity, the index parameter is requested in a register, so different from Quick - // we will not optimize the code for constants (which would save a register). - - SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke); - codegen_->AddSlowPath(slow_path); - - __ ldr(temp, Address(obj, count_offset.Int32Value())); // temp = str.length. - codegen_->MaybeRecordImplicitNullCheck(invoke); - __ cmp(idx, ShifterOperand(temp)); - __ b(slow_path->GetEntryLabel(), CS); - - __ add(array_temp, obj, ShifterOperand(value_offset.Int32Value())); // array_temp := str.value. - - // Load the value. - __ ldrh(out, Address(array_temp, idx, LSL, 1)); // out := array_temp[idx]. + // The UnsafeCASObject intrinsic is missing a read barrier, and + // therefore sometimes does not work as expected (b/25883050). + // Turn it off temporarily as a quick fix, until the read barrier is + // implemented (see TODO in GenCAS). + // + // TODO(rpl): Implement read barrier support in GenCAS and re-enable + // this intrinsic. + DCHECK(!kEmitCompilerReadBarrier); - __ Bind(slow_path->GetExitLabel()); + GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_); } void IntrinsicLocationsBuilderARM::VisitStringCompareTo(HInvoke* invoke) { @@ -1384,6 +1333,12 @@ void IntrinsicCodeGeneratorARM::VisitStringNewStringFromString(HInvoke* invoke) } void IntrinsicLocationsBuilderARM::VisitSystemArrayCopy(HInvoke* invoke) { + // TODO(rpl): Implement read barriers in the SystemArrayCopy + // intrinsic and re-enable it (b/29516905). + if (kEmitCompilerReadBarrier) { + return; + } + CodeGenerator::CreateSystemArrayCopyLocationSummary(invoke); LocationSummary* locations = invoke->GetLocations(); if (locations == nullptr) { @@ -1468,11 +1423,11 @@ static void CheckPosition(ArmAssembler* assembler, } } -// TODO: Implement read barriers in the SystemArrayCopy intrinsic. -// Note that this code path is not used (yet) because we do not -// intrinsify methods that can go into the IntrinsicSlowPathARM -// slow path. void IntrinsicCodeGeneratorARM::VisitSystemArrayCopy(HInvoke* invoke) { + // TODO(rpl): Implement read barriers in the SystemArrayCopy + // intrinsic and re-enable it (b/29516905). + DCHECK(!kEmitCompilerReadBarrier); + ArmAssembler* assembler = GetAssembler(); LocationSummary* locations = invoke->GetLocations(); @@ -2031,7 +1986,7 @@ void IntrinsicLocationsBuilderARM::VisitStringGetCharsNoCheck(HInvoke* invoke) { locations->SetInAt(3, Location::RequiresRegister()); locations->SetInAt(4, Location::RequiresRegister()); - locations->AddTemp(Location::RequiresRegister()); + // Temporary registers to store lengths of strings and for calculations. locations->AddTemp(Location::RequiresRegister()); locations->AddTemp(Location::RequiresRegister()); locations->AddTemp(Location::RequiresRegister()); @@ -2059,28 +2014,55 @@ void IntrinsicCodeGeneratorARM::VisitStringGetCharsNoCheck(HInvoke* invoke) { Register dstObj = locations->InAt(3).AsRegister<Register>(); Register dstBegin = locations->InAt(4).AsRegister<Register>(); - Register src_ptr = locations->GetTemp(0).AsRegister<Register>(); - Register src_ptr_end = locations->GetTemp(1).AsRegister<Register>(); + Register num_chr = locations->GetTemp(0).AsRegister<Register>(); + Register src_ptr = locations->GetTemp(1).AsRegister<Register>(); Register dst_ptr = locations->GetTemp(2).AsRegister<Register>(); - Register tmp = locations->GetTemp(3).AsRegister<Register>(); // src range to copy. __ add(src_ptr, srcObj, ShifterOperand(value_offset)); - __ add(src_ptr_end, src_ptr, ShifterOperand(srcEnd, LSL, 1)); __ add(src_ptr, src_ptr, ShifterOperand(srcBegin, LSL, 1)); // dst to be copied. __ add(dst_ptr, dstObj, ShifterOperand(data_offset)); __ add(dst_ptr, dst_ptr, ShifterOperand(dstBegin, LSL, 1)); + __ subs(num_chr, srcEnd, ShifterOperand(srcBegin)); + // Do the copy. - Label loop, done; + Label loop, remainder, done; + + // Early out for valid zero-length retrievals. + __ b(&done, EQ); + + // Save repairing the value of num_chr on the < 4 character path. + __ subs(IP, num_chr, ShifterOperand(4)); + __ b(&remainder, LT); + + // Keep the result of the earlier subs, we are going to fetch at least 4 characters. + __ mov(num_chr, ShifterOperand(IP)); + + // Main loop used for longer fetches loads and stores 4x16-bit characters at a time. + // (LDRD/STRD fault on unaligned addresses and it's not worth inlining extra code + // to rectify these everywhere this intrinsic applies.) __ Bind(&loop); - __ cmp(src_ptr, ShifterOperand(src_ptr_end)); + __ ldr(IP, Address(src_ptr, char_size * 2)); + __ subs(num_chr, num_chr, ShifterOperand(4)); + __ str(IP, Address(dst_ptr, char_size * 2)); + __ ldr(IP, Address(src_ptr, char_size * 4, Address::PostIndex)); + __ str(IP, Address(dst_ptr, char_size * 4, Address::PostIndex)); + __ b(&loop, GE); + + __ adds(num_chr, num_chr, ShifterOperand(4)); __ b(&done, EQ); - __ ldrh(tmp, Address(src_ptr, char_size, Address::PostIndex)); - __ strh(tmp, Address(dst_ptr, char_size, Address::PostIndex)); - __ b(&loop); + + // Main loop for < 4 character case and remainder handling. Loads and stores one + // 16-bit Java character at a time. + __ Bind(&remainder); + __ ldrh(IP, Address(src_ptr, char_size, Address::PostIndex)); + __ subs(num_chr, num_chr, ShifterOperand(1)); + __ strh(IP, Address(dst_ptr, char_size, Address::PostIndex)); + __ b(&remainder, GT); + __ Bind(&done); } diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index d776fb4406..1685cf9c3c 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -149,19 +149,6 @@ bool IntrinsicLocationsBuilderARM64::TryDispatch(HInvoke* invoke) { if (res == nullptr) { return false; } - if (kEmitCompilerReadBarrier && res->CanCall()) { - // Generating an intrinsic for this HInvoke may produce an - // IntrinsicSlowPathARM64 slow path. Currently this approach - // does not work when using read barriers, as the emitted - // calling sequence will make use of another slow path - // (ReadBarrierForRootSlowPathARM64 for HInvokeStaticOrDirect, - // ReadBarrierSlowPathARM64 for HInvokeVirtual). So we bail - // out in this case. - // - // TODO: Find a way to have intrinsics work with read barriers. - invoke->SetLocations(nullptr); - return false; - } return res->Intrinsified(); } @@ -791,8 +778,15 @@ static void GenUnsafeGet(HInvoke* invoke, // UnsafeGetObject/UnsafeGetObjectVolatile with Baker's read barrier case. UseScratchRegisterScope temps(masm); Register temp = temps.AcquireW(); - codegen->GenerateArrayLoadWithBakerReadBarrier( - invoke, trg_loc, base, 0U, offset_loc, temp, /* needs_null_check */ false); + codegen->GenerateReferenceLoadWithBakerReadBarrier(invoke, + trg_loc, + base, + /* offset */ 0U, + /* index */ offset_loc, + /* scale_factor */ 0U, + temp, + /* needs_null_check */ false, + is_volatile); } else { // Other cases. MemOperand mem_op(base.X(), offset); @@ -821,7 +815,8 @@ static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke locations->SetInAt(0, Location::NoLocation()); // Unused receiver. locations->SetInAt(1, Location::RequiresRegister()); locations->SetInAt(2, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + locations->SetOut(Location::RequiresRegister(), + can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap); } void IntrinsicLocationsBuilderARM64::VisitUnsafeGet(HInvoke* invoke) { @@ -1102,9 +1097,10 @@ void IntrinsicLocationsBuilderARM64::VisitUnsafeCASObject(HInvoke* invoke) { // The UnsafeCASObject intrinsic is missing a read barrier, and // therefore sometimes does not work as expected (b/25883050). // Turn it off temporarily as a quick fix, until the read barrier is - // implemented (see TODO in GenCAS below). + // implemented (see TODO in GenCAS). // - // TODO(rpl): Fix this issue and re-enable this intrinsic with read barriers. + // TODO(rpl): Implement read barrier support in GenCAS and re-enable + // this intrinsic. if (kEmitCompilerReadBarrier) { return; } @@ -1119,57 +1115,16 @@ void IntrinsicCodeGeneratorARM64::VisitUnsafeCASLong(HInvoke* invoke) { GenCas(invoke->GetLocations(), Primitive::kPrimLong, codegen_); } void IntrinsicCodeGeneratorARM64::VisitUnsafeCASObject(HInvoke* invoke) { - GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_); -} - -void IntrinsicLocationsBuilderARM64::VisitStringCharAt(HInvoke* invoke) { - LocationSummary* locations = new (arena_) LocationSummary(invoke, - LocationSummary::kCallOnSlowPath, - kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - // In case we need to go in the slow path, we can't have the output be the same - // as the input: the current liveness analysis considers the input to be live - // at the point of the call. - locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); -} - -void IntrinsicCodeGeneratorARM64::VisitStringCharAt(HInvoke* invoke) { - vixl::MacroAssembler* masm = GetVIXLAssembler(); - LocationSummary* locations = invoke->GetLocations(); - - // Location of reference to data array - const MemberOffset value_offset = mirror::String::ValueOffset(); - // Location of count - const MemberOffset count_offset = mirror::String::CountOffset(); - - Register obj = WRegisterFrom(locations->InAt(0)); // String object pointer. - Register idx = WRegisterFrom(locations->InAt(1)); // Index of character. - Register out = WRegisterFrom(locations->Out()); // Result character. - - UseScratchRegisterScope temps(masm); - Register temp = temps.AcquireW(); - Register array_temp = temps.AcquireW(); // We can trade this for worse scheduling. - - // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth - // the cost. - // TODO: For simplicity, the index parameter is requested in a register, so different from Quick - // we will not optimize the code for constants (which would save a register). - - SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke); - codegen_->AddSlowPath(slow_path); - - __ Ldr(temp, HeapOperand(obj, count_offset)); // temp = str.length. - codegen_->MaybeRecordImplicitNullCheck(invoke); - __ Cmp(idx, temp); - __ B(hs, slow_path->GetEntryLabel()); - - __ Add(array_temp, obj, Operand(value_offset.Int32Value())); // array_temp := str.value. - - // Load the value. - __ Ldrh(out, MemOperand(array_temp.X(), idx, UXTW, 1)); // out := array_temp[idx]. + // The UnsafeCASObject intrinsic is missing a read barrier, and + // therefore sometimes does not work as expected (b/25883050). + // Turn it off temporarily as a quick fix, until the read barrier is + // implemented (see TODO in GenCAS). + // + // TODO(rpl): Implement read barrier support in GenCAS and re-enable + // this intrinsic. + DCHECK(!kEmitCompilerReadBarrier); - __ Bind(slow_path->GetExitLabel()); + GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_); } void IntrinsicLocationsBuilderARM64::VisitStringCompareTo(HInvoke* invoke) { @@ -1745,6 +1700,7 @@ void IntrinsicLocationsBuilderARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) locations->AddTemp(Location::RequiresRegister()); locations->AddTemp(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresRegister()); } void IntrinsicCodeGeneratorARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) { @@ -1770,29 +1726,57 @@ void IntrinsicCodeGeneratorARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) { Register dstBegin = XRegisterFrom(locations->InAt(4)); Register src_ptr = XRegisterFrom(locations->GetTemp(0)); - Register src_ptr_end = XRegisterFrom(locations->GetTemp(1)); + Register num_chr = XRegisterFrom(locations->GetTemp(1)); + Register tmp1 = XRegisterFrom(locations->GetTemp(2)); UseScratchRegisterScope temps(masm); Register dst_ptr = temps.AcquireX(); - Register tmp = temps.AcquireW(); + Register tmp2 = temps.AcquireX(); - // src range to copy. + // src address to copy from. __ Add(src_ptr, srcObj, Operand(value_offset)); - __ Add(src_ptr_end, src_ptr, Operand(srcEnd, LSL, 1)); __ Add(src_ptr, src_ptr, Operand(srcBegin, LSL, 1)); - // dst to be copied. + // dst address start to copy to. __ Add(dst_ptr, dstObj, Operand(data_offset)); __ Add(dst_ptr, dst_ptr, Operand(dstBegin, LSL, 1)); + __ Sub(num_chr, srcEnd, srcBegin); + // Do the copy. - vixl::Label loop, done; + vixl::Label loop; + vixl::Label done; + vixl::Label remainder; + + // Early out for valid zero-length retrievals. + __ Cbz(num_chr, &done); + + // Save repairing the value of num_chr on the < 8 character path. + __ Subs(tmp1, num_chr, 8); + __ B(lt, &remainder); + + // Keep the result of the earlier subs, we are going to fetch at least 8 characters. + __ Mov(num_chr, tmp1); + + // Main loop used for longer fetches loads and stores 8x16-bit characters at a time. + // (Unaligned addresses are acceptable here and not worth inlining extra code to rectify.) __ Bind(&loop); - __ Cmp(src_ptr, src_ptr_end); - __ B(&done, eq); - __ Ldrh(tmp, MemOperand(src_ptr, char_size, vixl::PostIndex)); - __ Strh(tmp, MemOperand(dst_ptr, char_size, vixl::PostIndex)); - __ B(&loop); + __ Ldp(tmp1, tmp2, MemOperand(src_ptr, char_size * 8, vixl::PostIndex)); + __ Subs(num_chr, num_chr, 8); + __ Stp(tmp1, tmp2, MemOperand(dst_ptr, char_size * 8, vixl::PostIndex)); + __ B(ge, &loop); + + __ Adds(num_chr, num_chr, 8); + __ B(eq, &done); + + // Main loop for < 8 character case and remainder handling. Loads and stores one + // 16-bit Java character at a time. + __ Bind(&remainder); + __ Ldrh(tmp1, MemOperand(src_ptr, char_size, vixl::PostIndex)); + __ Subs(num_chr, num_chr, 1); + __ Strh(tmp1, MemOperand(dst_ptr, char_size, vixl::PostIndex)); + __ B(gt, &remainder); + __ Bind(&done); } @@ -2033,6 +2017,12 @@ static constexpr int32_t kSystemArrayCopyThreshold = 128; // We want to use two temporary registers in order to reduce the register pressure in arm64. // So we don't use the CodeGenerator::CreateSystemArrayCopyLocationSummary. void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopy(HInvoke* invoke) { + // TODO(rpl): Implement read barriers in the SystemArrayCopy + // intrinsic and re-enable it (b/29516905). + if (kEmitCompilerReadBarrier) { + return; + } + // Check to see if we have known failures that will cause us to have to bail out // to the runtime, and just generate the runtime call directly. HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant(); @@ -2085,6 +2075,10 @@ void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopy(HInvoke* invoke) { } void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopy(HInvoke* invoke) { + // TODO(rpl): Implement read barriers in the SystemArrayCopy + // intrinsic and re-enable it (b/29516905). + DCHECK(!kEmitCompilerReadBarrier); + vixl::MacroAssembler* masm = GetVIXLAssembler(); LocationSummary* locations = invoke->GetLocations(); diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 140f56a870..d4f44d63e2 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -1872,54 +1872,6 @@ void IntrinsicCodeGeneratorMIPS::VisitUnsafeCASObject(HInvoke* invoke) { GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_); } -// char java.lang.String.charAt(int index) -void IntrinsicLocationsBuilderMIPS::VisitStringCharAt(HInvoke* invoke) { - LocationSummary* locations = new (arena_) LocationSummary(invoke, - LocationSummary::kCallOnSlowPath, - kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - // The inputs will be considered live at the last instruction and restored. This would overwrite - // the output with kNoOutputOverlap. - locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); -} - -void IntrinsicCodeGeneratorMIPS::VisitStringCharAt(HInvoke* invoke) { - LocationSummary* locations = invoke->GetLocations(); - MipsAssembler* assembler = GetAssembler(); - - // Location of reference to data array - const int32_t value_offset = mirror::String::ValueOffset().Int32Value(); - // Location of count - const int32_t count_offset = mirror::String::CountOffset().Int32Value(); - - Register obj = locations->InAt(0).AsRegister<Register>(); - Register idx = locations->InAt(1).AsRegister<Register>(); - Register out = locations->Out().AsRegister<Register>(); - - // TODO: Maybe we can support range check elimination. Overall, - // though, I think it's not worth the cost. - // TODO: For simplicity, the index parameter is requested in a - // register, so different from Quick we will not optimize the - // code for constants (which would save a register). - - SlowPathCodeMIPS* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS(invoke); - codegen_->AddSlowPath(slow_path); - - // Load the string size - __ Lw(TMP, obj, count_offset); - codegen_->MaybeRecordImplicitNullCheck(invoke); - // Revert to slow path if idx is too large, or negative - __ Bgeu(idx, TMP, slow_path->GetEntryLabel()); - - // out = obj[2*idx]. - __ Sll(TMP, idx, 1); // idx * 2 - __ Addu(TMP, TMP, obj); // Address of char at location idx - __ Lhu(out, TMP, value_offset); // Load char at location idx - - __ Bind(slow_path->GetExitLabel()); -} - // int java.lang.String.compareTo(String anotherString) void IntrinsicLocationsBuilderMIPS::VisitStringCompareTo(HInvoke* invoke) { LocationSummary* locations = new (arena_) LocationSummary(invoke, diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index 6c4e64e4b1..9243f4c93f 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -1371,52 +1371,6 @@ void IntrinsicCodeGeneratorMIPS64::VisitUnsafeCASObject(HInvoke* invoke) { GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_); } -// char java.lang.String.charAt(int index) -void IntrinsicLocationsBuilderMIPS64::VisitStringCharAt(HInvoke* invoke) { - LocationSummary* locations = new (arena_) LocationSummary(invoke, - LocationSummary::kCallOnSlowPath, - kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::SameAsFirstInput()); -} - -void IntrinsicCodeGeneratorMIPS64::VisitStringCharAt(HInvoke* invoke) { - LocationSummary* locations = invoke->GetLocations(); - Mips64Assembler* assembler = GetAssembler(); - - // Location of reference to data array - const int32_t value_offset = mirror::String::ValueOffset().Int32Value(); - // Location of count - const int32_t count_offset = mirror::String::CountOffset().Int32Value(); - - GpuRegister obj = locations->InAt(0).AsRegister<GpuRegister>(); - GpuRegister idx = locations->InAt(1).AsRegister<GpuRegister>(); - GpuRegister out = locations->Out().AsRegister<GpuRegister>(); - - // TODO: Maybe we can support range check elimination. Overall, - // though, I think it's not worth the cost. - // TODO: For simplicity, the index parameter is requested in a - // register, so different from Quick we will not optimize the - // code for constants (which would save a register). - - SlowPathCodeMIPS64* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS64(invoke); - codegen_->AddSlowPath(slow_path); - - // Load the string size - __ Lw(TMP, obj, count_offset); - codegen_->MaybeRecordImplicitNullCheck(invoke); - // Revert to slow path if idx is too large, or negative - __ Bgeuc(idx, TMP, slow_path->GetEntryLabel()); - - // out = obj[2*idx]. - __ Sll(TMP, idx, 1); // idx * 2 - __ Daddu(TMP, TMP, obj); // Address of char at location idx - __ Lhu(out, TMP, value_offset); // Load char at location idx - - __ Bind(slow_path->GetExitLabel()); -} - // int java.lang.String.compareTo(String anotherString) void IntrinsicLocationsBuilderMIPS64::VisitStringCompareTo(HInvoke* invoke) { LocationSummary* locations = new (arena_) LocationSummary(invoke, diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 05377f984b..031cd1313c 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -60,19 +60,6 @@ bool IntrinsicLocationsBuilderX86::TryDispatch(HInvoke* invoke) { if (res == nullptr) { return false; } - if (kEmitCompilerReadBarrier && res->CanCall()) { - // Generating an intrinsic for this HInvoke may produce an - // IntrinsicSlowPathX86 slow path. Currently this approach - // does not work when using read barriers, as the emitted - // calling sequence will make use of another slow path - // (ReadBarrierForRootSlowPathX86 for HInvokeStaticOrDirect, - // ReadBarrierSlowPathX86 for HInvokeVirtual). So we bail - // out in this case. - // - // TODO: Find a way to have intrinsics work with read barriers. - invoke->SetLocations(nullptr); - return false; - } return res->Intrinsified(); } @@ -1030,48 +1017,6 @@ void IntrinsicCodeGeneratorX86::VisitMathNextAfter(HInvoke* invoke) { GenFPToFPCall(invoke, codegen_, kQuickNextAfter); } -void IntrinsicLocationsBuilderX86::VisitStringCharAt(HInvoke* invoke) { - // The inputs plus one temp. - LocationSummary* locations = new (arena_) LocationSummary(invoke, - LocationSummary::kCallOnSlowPath, - kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::SameAsFirstInput()); -} - -void IntrinsicCodeGeneratorX86::VisitStringCharAt(HInvoke* invoke) { - LocationSummary* locations = invoke->GetLocations(); - - // Location of reference to data array. - const int32_t value_offset = mirror::String::ValueOffset().Int32Value(); - // Location of count. - const int32_t count_offset = mirror::String::CountOffset().Int32Value(); - - Register obj = locations->InAt(0).AsRegister<Register>(); - Register idx = locations->InAt(1).AsRegister<Register>(); - Register out = locations->Out().AsRegister<Register>(); - - // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth - // the cost. - // TODO: For simplicity, the index parameter is requested in a register, so different from Quick - // we will not optimize the code for constants (which would save a register). - - SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathX86(invoke); - codegen_->AddSlowPath(slow_path); - - X86Assembler* assembler = GetAssembler(); - - __ cmpl(idx, Address(obj, count_offset)); - codegen_->MaybeRecordImplicitNullCheck(invoke); - __ j(kAboveEqual, slow_path->GetEntryLabel()); - - // out = out[2*idx]. - __ movzxw(out, Address(out, idx, ScaleFactor::TIMES_2, value_offset)); - - __ Bind(slow_path->GetExitLabel()); -} - void IntrinsicLocationsBuilderX86::VisitSystemArrayCopyChar(HInvoke* invoke) { // We need at least two of the positions or length to be an integer constant, // or else we won't have enough free registers. @@ -1864,8 +1809,9 @@ static void GenUnsafeGet(HInvoke* invoke, if (kEmitCompilerReadBarrier) { if (kUseBakerReadBarrier) { Location temp = locations->GetTemp(0); - codegen->GenerateArrayLoadWithBakerReadBarrier( - invoke, output_loc, base, 0U, offset_loc, temp, /* needs_null_check */ false); + Address src(base, offset, ScaleFactor::TIMES_1, 0); + codegen->GenerateReferenceLoadWithBakerReadBarrier( + invoke, output_loc, base, src, temp, /* needs_null_check */ false); } else { __ movl(output, Address(base, offset, ScaleFactor::TIMES_1, 0)); codegen->GenerateReadBarrierSlow( @@ -1920,16 +1866,17 @@ static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, if (is_volatile) { // Need to use XMM to read volatile. locations->AddTemp(Location::RequiresFpuRegister()); - locations->SetOut(Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } else { locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); } } else { - locations->SetOut(Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), + can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap); } if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) { // We need a temporary register for the read barrier marking slow - // path in InstructionCodeGeneratorX86::GenerateArrayLoadWithBakerReadBarrier. + // path in InstructionCodeGeneratorX86::GenerateReferenceLoadWithBakerReadBarrier. locations->AddTemp(Location::RequiresRegister()); } } @@ -2151,9 +2098,9 @@ void IntrinsicLocationsBuilderX86::VisitUnsafeCASObject(HInvoke* invoke) { // The UnsafeCASObject intrinsic is missing a read barrier, and // therefore sometimes does not work as expected (b/25883050). // Turn it off temporarily as a quick fix, until the read barrier is - // implemented. + // implemented (see TODO in GenCAS). // - // TODO(rpl): Implement a read barrier in GenCAS below and re-enable + // TODO(rpl): Implement read barrier support in GenCAS and re-enable // this intrinsic. if (kEmitCompilerReadBarrier) { return; @@ -2278,6 +2225,15 @@ void IntrinsicCodeGeneratorX86::VisitUnsafeCASLong(HInvoke* invoke) { } void IntrinsicCodeGeneratorX86::VisitUnsafeCASObject(HInvoke* invoke) { + // The UnsafeCASObject intrinsic is missing a read barrier, and + // therefore sometimes does not work as expected (b/25883050). + // Turn it off temporarily as a quick fix, until the read barrier is + // implemented (see TODO in GenCAS). + // + // TODO(rpl): Implement read barrier support in GenCAS and re-enable + // this intrinsic. + DCHECK(!kEmitCompilerReadBarrier); + GenCAS(Primitive::kPrimNot, invoke, codegen_); } diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 67c2f3a866..c5b44d4f5c 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -54,19 +54,6 @@ bool IntrinsicLocationsBuilderX86_64::TryDispatch(HInvoke* invoke) { if (res == nullptr) { return false; } - if (kEmitCompilerReadBarrier && res->CanCall()) { - // Generating an intrinsic for this HInvoke may produce an - // IntrinsicSlowPathX86_64 slow path. Currently this approach - // does not work when using read barriers, as the emitted - // calling sequence will make use of another slow path - // (ReadBarrierForRootSlowPathX86_64 for HInvokeStaticOrDirect, - // ReadBarrierSlowPathX86_64 for HInvokeVirtual). So we bail - // out in this case. - // - // TODO: Find a way to have intrinsics work with read barriers. - invoke->SetLocations(nullptr); - return false; - } return res->Intrinsified(); } @@ -891,49 +878,6 @@ void IntrinsicCodeGeneratorX86_64::VisitMathNextAfter(HInvoke* invoke) { GenFPToFPCall(invoke, codegen_, kQuickNextAfter); } -void IntrinsicLocationsBuilderX86_64::VisitStringCharAt(HInvoke* invoke) { - // The inputs plus one temp. - LocationSummary* locations = new (arena_) LocationSummary(invoke, - LocationSummary::kCallOnSlowPath, - kIntrinsified); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::SameAsFirstInput()); - locations->AddTemp(Location::RequiresRegister()); -} - -void IntrinsicCodeGeneratorX86_64::VisitStringCharAt(HInvoke* invoke) { - LocationSummary* locations = invoke->GetLocations(); - - // Location of reference to data array. - const int32_t value_offset = mirror::String::ValueOffset().Int32Value(); - // Location of count. - const int32_t count_offset = mirror::String::CountOffset().Int32Value(); - - CpuRegister obj = locations->InAt(0).AsRegister<CpuRegister>(); - CpuRegister idx = locations->InAt(1).AsRegister<CpuRegister>(); - CpuRegister out = locations->Out().AsRegister<CpuRegister>(); - - // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth - // the cost. - // TODO: For simplicity, the index parameter is requested in a register, so different from Quick - // we will not optimize the code for constants (which would save a register). - - SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathX86_64(invoke); - codegen_->AddSlowPath(slow_path); - - X86_64Assembler* assembler = GetAssembler(); - - __ cmpl(idx, Address(obj, count_offset)); - codegen_->MaybeRecordImplicitNullCheck(invoke); - __ j(kAboveEqual, slow_path->GetEntryLabel()); - - // out = out[2*idx]. - __ movzxw(out, Address(out, idx, ScaleFactor::TIMES_2, value_offset)); - - __ Bind(slow_path->GetExitLabel()); -} - void IntrinsicLocationsBuilderX86_64::VisitSystemArrayCopyChar(HInvoke* invoke) { // Check to see if we have known failures that will cause us to have to bail out // to the runtime, and just generate the runtime call directly. @@ -1122,14 +1066,20 @@ void IntrinsicCodeGeneratorX86_64::VisitSystemArrayCopyChar(HInvoke* invoke) { void IntrinsicLocationsBuilderX86_64::VisitSystemArrayCopy(HInvoke* invoke) { + // TODO(rpl): Implement read barriers in the SystemArrayCopy + // intrinsic and re-enable it (b/29516905). + if (kEmitCompilerReadBarrier) { + return; + } + CodeGenerator::CreateSystemArrayCopyLocationSummary(invoke); } -// TODO: Implement read barriers in the SystemArrayCopy intrinsic. -// Note that this code path is not used (yet) because we do not -// intrinsify methods that can go into the IntrinsicSlowPathX86_64 -// slow path. void IntrinsicCodeGeneratorX86_64::VisitSystemArrayCopy(HInvoke* invoke) { + // TODO(rpl): Implement read barriers in the SystemArrayCopy + // intrinsic and re-enable it (b/29516905). + DCHECK(!kEmitCompilerReadBarrier); + X86_64Assembler* assembler = GetAssembler(); LocationSummary* locations = invoke->GetLocations(); @@ -1953,8 +1903,9 @@ static void GenUnsafeGet(HInvoke* invoke, if (kEmitCompilerReadBarrier) { if (kUseBakerReadBarrier) { Location temp = locations->GetTemp(0); - codegen->GenerateArrayLoadWithBakerReadBarrier( - invoke, output_loc, base, 0U, offset_loc, temp, /* needs_null_check */ false); + Address src(base, offset, ScaleFactor::TIMES_1, 0); + codegen->GenerateReferenceLoadWithBakerReadBarrier( + invoke, output_loc, base, src, temp, /* needs_null_check */ false); } else { __ movl(output, Address(base, offset, ScaleFactor::TIMES_1, 0)); codegen->GenerateReadBarrierSlow( @@ -1991,10 +1942,11 @@ static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, locations->SetInAt(0, Location::NoLocation()); // Unused receiver. locations->SetInAt(1, Location::RequiresRegister()); locations->SetInAt(2, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), + can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap); if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) { // We need a temporary register for the read barrier marking slow - // path in InstructionCodeGeneratorX86_64::GenerateArrayLoadWithBakerReadBarrier. + // path in InstructionCodeGeneratorX86_64::GenerateReferenceLoadWithBakerReadBarrier. locations->AddTemp(Location::RequiresRegister()); } } @@ -2178,9 +2130,9 @@ void IntrinsicLocationsBuilderX86_64::VisitUnsafeCASObject(HInvoke* invoke) { // The UnsafeCASObject intrinsic is missing a read barrier, and // therefore sometimes does not work as expected (b/25883050). // Turn it off temporarily as a quick fix, until the read barrier is - // implemented. + // implemented (see TODO in GenCAS). // - // TODO(rpl): Implement a read barrier in GenCAS below and re-enable + // TODO(rpl): Implement read barrier support in GenCAS and re-enable // this intrinsic. if (kEmitCompilerReadBarrier) { return; @@ -2296,6 +2248,15 @@ void IntrinsicCodeGeneratorX86_64::VisitUnsafeCASLong(HInvoke* invoke) { } void IntrinsicCodeGeneratorX86_64::VisitUnsafeCASObject(HInvoke* invoke) { + // The UnsafeCASObject intrinsic is missing a read barrier, and + // therefore sometimes does not work as expected (b/25883050). + // Turn it off temporarily as a quick fix, until the read barrier is + // implemented (see TODO in GenCAS). + // + // TODO(rpl): Implement read barrier support in GenCAS and re-enable + // this intrinsic. + DCHECK(!kEmitCompilerReadBarrier); + GenCAS(Primitive::kPrimNot, invoke, codegen_); } @@ -2484,7 +2445,7 @@ static void GenOneBit(X86_64Assembler* assembler, : CTZ(static_cast<uint32_t>(value)); } if (is_long) { - codegen->Load64BitValue(out, 1L << value); + codegen->Load64BitValue(out, 1ULL << value); } else { codegen->Load32BitValue(out, 1 << value); } diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index 8a75a90cfd..7347686830 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -65,6 +65,16 @@ class ReferenceInfo : public ArenaObject<kArenaAllocMisc> { is_singleton_and_not_returned_ = false; return; } + if ((user->IsUnresolvedInstanceFieldGet() && (reference_ == user->InputAt(0))) || + (user->IsUnresolvedInstanceFieldSet() && (reference_ == user->InputAt(0)))) { + // The field is accessed in an unresolved way. We mark the object as a singleton to + // disable load/store optimizations on it. + // Note that we could optimize this case and still perform some optimizations until + // we hit the unresolved access, but disabling is the simplest. + is_singleton_ = false; + is_singleton_and_not_returned_ = false; + return; + } if (user->IsReturn()) { is_singleton_and_not_returned_ = false; } diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h index 63bbc2cd0a..3f27c911be 100644 --- a/compiler/optimizing/locations.h +++ b/compiler/optimizing/locations.h @@ -38,7 +38,13 @@ std::ostream& operator<<(std::ostream& os, const Location& location); class Location : public ValueObject { public: enum OutputOverlap { + // The liveness of the output overlaps the liveness of one or + // several input(s); the register allocator cannot reuse an + // input's location for the output's location. kOutputOverlap, + // The liveness of the output does not overlap the liveness of any + // input; the register allocator is allowed to reuse an input's + // location for the output's location. kNoOutputOverlap }; @@ -494,6 +500,10 @@ class LocationSummary : public ArenaObject<kArenaAllocLocationSummary> { return inputs_.size(); } + // Set the output location. Argument `overlaps` tells whether the + // output overlaps any of the inputs (if so, it cannot share the + // same register as one of the inputs); it is set to + // `Location::kOutputOverlap` by default for safety. void SetOut(Location location, Location::OutputOverlap overlaps = Location::kOutputOverlap) { DCHECK(output_.IsInvalid()); output_overlaps_ = overlaps; diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 4b4e549e20..c2c212b66f 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -2430,8 +2430,69 @@ std::ostream& operator<<(std::ostream& os, HInvokeStaticOrDirect::ClinitCheckReq } } +bool HLoadClass::InstructionDataEquals(const HInstruction* other) const { + const HLoadClass* other_load_class = other->AsLoadClass(); + // TODO: To allow GVN for HLoadClass from different dex files, we should compare the type + // names rather than type indexes. However, we shall also have to re-think the hash code. + if (type_index_ != other_load_class->type_index_ || + GetPackedFields() != other_load_class->GetPackedFields()) { + return false; + } + LoadKind load_kind = GetLoadKind(); + if (HasAddress(load_kind)) { + return GetAddress() == other_load_class->GetAddress(); + } else if (HasTypeReference(load_kind)) { + return IsSameDexFile(GetDexFile(), other_load_class->GetDexFile()); + } else { + DCHECK(HasDexCacheReference(load_kind)) << load_kind; + // If the type indexes and dex files are the same, dex cache element offsets + // must also be the same, so we don't need to compare them. + return IsSameDexFile(GetDexFile(), other_load_class->GetDexFile()); + } +} + +void HLoadClass::SetLoadKindInternal(LoadKind load_kind) { + // Once sharpened, the load kind should not be changed again. + // Also, kReferrersClass should never be overwritten. + DCHECK_EQ(GetLoadKind(), LoadKind::kDexCacheViaMethod); + SetPackedField<LoadKindField>(load_kind); + + if (load_kind != LoadKind::kDexCacheViaMethod) { + RemoveAsUserOfInput(0u); + SetRawInputAt(0u, nullptr); + } + if (!NeedsEnvironment()) { + RemoveEnvironment(); + SetSideEffects(SideEffects::None()); + } +} + +std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs) { + switch (rhs) { + case HLoadClass::LoadKind::kReferrersClass: + return os << "ReferrersClass"; + case HLoadClass::LoadKind::kBootImageLinkTimeAddress: + return os << "BootImageLinkTimeAddress"; + case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: + return os << "BootImageLinkTimePcRelative"; + case HLoadClass::LoadKind::kBootImageAddress: + return os << "BootImageAddress"; + case HLoadClass::LoadKind::kDexCacheAddress: + return os << "DexCacheAddress"; + case HLoadClass::LoadKind::kDexCachePcRelative: + return os << "DexCachePcRelative"; + case HLoadClass::LoadKind::kDexCacheViaMethod: + return os << "DexCacheViaMethod"; + default: + LOG(FATAL) << "Unknown HLoadClass::LoadKind: " << static_cast<int>(rhs); + UNREACHABLE(); + } +} + bool HLoadString::InstructionDataEquals(const HInstruction* other) const { const HLoadString* other_load_string = other->AsLoadString(); + // TODO: To allow GVN for HLoadString from different dex files, we should compare the strings + // rather than their indexes. However, we shall also have to re-think the hash code. if (string_index_ != other_load_string->string_index_ || GetPackedFields() != other_load_string->GetPackedFields()) { return false; diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 711a6c1b2d..29df7c8ab8 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -26,6 +26,7 @@ #include "base/arena_object.h" #include "base/stl_util.h" #include "dex/compiler_enums.h" +#include "dex_file.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "handle.h" #include "handle_scope.h" @@ -85,6 +86,16 @@ static constexpr InvokeType kInvalidInvokeType = static_cast<InvokeType>(-1); static constexpr uint32_t kNoDexPc = -1; +inline bool IsSameDexFile(const DexFile& lhs, const DexFile& rhs) { + // For the purposes of the compiler, the dex files must actually be the same object + // if we want to safely treat them as the same. This is especially important for JIT + // as custom class loaders can open the same underlying file (or memory) multiple + // times and provide different class resolution but no two class loaders should ever + // use the same DexFile object - doing so is an unsupported hack that can lead to + // all sorts of weird failures. + return &lhs == &rhs; +} + enum IfCondition { // All types. kCondEQ, // == @@ -161,6 +172,10 @@ class ReferenceTypeInfo : ValueObject { static ReferenceTypeInfo Create(TypeHandle type_handle, bool is_exact); + static ReferenceTypeInfo Create(TypeHandle type_handle) SHARED_REQUIRES(Locks::mutator_lock_) { + return Create(type_handle, type_handle->CannotBeAssignedFromOtherTypes()); + } + static ReferenceTypeInfo CreateUnchecked(TypeHandle type_handle, bool is_exact) { return ReferenceTypeInfo(type_handle, is_exact); } @@ -1920,6 +1935,14 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { environment_ = environment; } + void InsertRawEnvironment(HEnvironment* environment) { + DCHECK(environment_ != nullptr); + DCHECK_EQ(environment->GetHolder(), this); + DCHECK(environment->GetParent() == nullptr); + environment->parent_ = environment_; + environment_ = environment; + } + void RemoveEnvironment(); // Set the environment of this instruction, copying it from `environment`. While @@ -5079,8 +5102,13 @@ class HInstanceFieldSet FINAL : public HTemplateInstruction<2> { class HArrayGet FINAL : public HExpression<2> { public: - HArrayGet(HInstruction* array, HInstruction* index, Primitive::Type type, uint32_t dex_pc) + HArrayGet(HInstruction* array, + HInstruction* index, + Primitive::Type type, + uint32_t dex_pc, + bool is_string_char_at = false) : HExpression(type, SideEffects::ArrayReadOfType(type), dex_pc) { + SetPackedFlag<kFlagIsStringCharAt>(is_string_char_at); SetRawInputAt(0, array); SetRawInputAt(1, index); } @@ -5114,12 +5142,24 @@ class HArrayGet FINAL : public HExpression<2> { return result; } + bool IsStringCharAt() const { return GetPackedFlag<kFlagIsStringCharAt>(); } + HInstruction* GetArray() const { return InputAt(0); } HInstruction* GetIndex() const { return InputAt(1); } DECLARE_INSTRUCTION(ArrayGet); private: + // We treat a String as an array, creating the HArrayGet from String.charAt() + // intrinsic in the instruction simplifier. We can always determine whether + // a particular HArrayGet is actually a String.charAt() by looking at the type + // of the input but that requires holding the mutator lock, so we prefer to use + // a flag, so that code generators don't need to do the locking. + static constexpr size_t kFlagIsStringCharAt = kNumberOfExpressionPackedBits; + static constexpr size_t kNumberOfArrayGetPackedBits = kFlagIsStringCharAt + 1; + static_assert(kNumberOfArrayGetPackedBits <= HInstruction::kMaxNumberOfPackedBits, + "Too many packed fields."); + DISALLOW_COPY_AND_ASSIGN(HArrayGet); }; @@ -5225,8 +5265,9 @@ class HArraySet FINAL : public HTemplateInstruction<3> { class HArrayLength FINAL : public HExpression<1> { public: - HArrayLength(HInstruction* array, uint32_t dex_pc) + HArrayLength(HInstruction* array, uint32_t dex_pc, bool is_string_length = false) : HExpression(Primitive::kPrimInt, SideEffects::None(), dex_pc) { + SetPackedFlag<kFlagIsStringLength>(is_string_length); // Note that arrays do not change length, so the instruction does not // depend on any write. SetRawInputAt(0, array); @@ -5240,7 +5281,6 @@ class HArrayLength FINAL : public HExpression<1> { return obj == InputAt(0); } - void MarkAsStringLength() { SetPackedFlag<kFlagIsStringLength>(); } bool IsStringLength() const { return GetPackedFlag<kFlagIsStringLength>(); } DECLARE_INSTRUCTION(ArrayLength); @@ -5263,8 +5303,12 @@ class HBoundsCheck FINAL : public HExpression<2> { public: // `HBoundsCheck` can trigger GC, as it may call the `IndexOutOfBoundsException` // constructor. - HBoundsCheck(HInstruction* index, HInstruction* length, uint32_t dex_pc) - : HExpression(index->GetType(), SideEffects::CanTriggerGC(), dex_pc) { + HBoundsCheck(HInstruction* index, + HInstruction* length, + uint32_t dex_pc, + uint32_t string_char_at_method_index = DexFile::kDexNoIndex) + : HExpression(index->GetType(), SideEffects::CanTriggerGC(), dex_pc), + string_char_at_method_index_(string_char_at_method_index) { DCHECK_EQ(Primitive::kPrimInt, Primitive::PrimitiveKind(index->GetType())); SetRawInputAt(0, index); SetRawInputAt(1, length); @@ -5279,11 +5323,23 @@ class HBoundsCheck FINAL : public HExpression<2> { bool CanThrow() const OVERRIDE { return true; } + bool IsStringCharAt() const { return GetStringCharAtMethodIndex() != DexFile::kDexNoIndex; } + uint32_t GetStringCharAtMethodIndex() const { return string_char_at_method_index_; } + HInstruction* GetIndex() const { return InputAt(0); } DECLARE_INSTRUCTION(BoundsCheck); private: + // We treat a String as an array, creating the HBoundsCheck from String.charAt() + // intrinsic in the instruction simplifier. We want to include the String.charAt() + // in the stack trace if we actually throw the StringIndexOutOfBoundsException, + // so we need to create an HEnvironment which will be translated to an InlineInfo + // indicating the extra stack frame. Since we add this HEnvironment quite late, + // in the PrepareForRegisterAllocation pass, we need to remember the method index + // from the invoke as we don't want to look again at the dex bytecode. + uint32_t string_char_at_method_index_; // DexFile::kDexNoIndex if regular array. + DISALLOW_COPY_AND_ASSIGN(HBoundsCheck); }; @@ -5329,8 +5385,44 @@ class HNativeDebugInfo : public HTemplateInstruction<0> { /** * Instruction to load a Class object. */ -class HLoadClass FINAL : public HExpression<1> { +class HLoadClass FINAL : public HInstruction { public: + // Determines how to load the Class. + enum class LoadKind { + // Use the Class* from the method's own ArtMethod*. + kReferrersClass, + + // Use boot image Class* address that will be known at link time. + // Used for boot image classes referenced by boot image code in non-PIC mode. + kBootImageLinkTimeAddress, + + // Use PC-relative boot image Class* address that will be known at link time. + // Used for boot image classes referenced by boot image code in PIC mode. + kBootImageLinkTimePcRelative, + + // Use a known boot image Class* address, embedded in the code by the codegen. + // Used for boot image classes referenced by apps in AOT- and JIT-compiled code. + // Note: codegen needs to emit a linker patch if indicated by compiler options' + // GetIncludePatchInformation(). + kBootImageAddress, + + // Load from the resolved types array at an absolute address. + // Used for classes outside the boot image referenced by JIT-compiled code. + kDexCacheAddress, + + // Load from resolved types array in the dex cache using a PC-relative load. + // Used for classes outside boot image when we know that we can access + // the dex cache arrays using a PC-relative load. + kDexCachePcRelative, + + // Load from resolved types array accessed through the class loaded from + // the compiled method's own ArtMethod*. This is the default access type when + // all other types are unavailable. + kDexCacheViaMethod, + + kLast = kDexCacheViaMethod + }; + HLoadClass(HCurrentMethod* current_method, uint16_t type_index, const DexFile& dex_file, @@ -5338,7 +5430,8 @@ class HLoadClass FINAL : public HExpression<1> { uint32_t dex_pc, bool needs_access_check, bool is_in_dex_cache) - : HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls(), dex_pc), + : HInstruction(SideEffectsForArchRuntimeCalls(), dex_pc), + special_input_(HUserRecord<HInstruction*>(current_method)), type_index_(type_index), dex_file_(dex_file), loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) { @@ -5346,26 +5439,47 @@ class HLoadClass FINAL : public HExpression<1> { // methods so we can't possibly end up in this situation. DCHECK(!is_referrers_class || !needs_access_check); - SetPackedFlag<kFlagIsReferrersClass>(is_referrers_class); + SetPackedField<LoadKindField>( + is_referrers_class ? LoadKind::kReferrersClass : LoadKind::kDexCacheViaMethod); SetPackedFlag<kFlagNeedsAccessCheck>(needs_access_check); SetPackedFlag<kFlagIsInDexCache>(is_in_dex_cache); SetPackedFlag<kFlagGenerateClInitCheck>(false); - SetRawInputAt(0, current_method); } - bool CanBeMoved() const OVERRIDE { return true; } + void SetLoadKindWithAddress(LoadKind load_kind, uint64_t address) { + DCHECK(HasAddress(load_kind)); + load_data_.address = address; + SetLoadKindInternal(load_kind); + } - bool InstructionDataEquals(const HInstruction* other) const OVERRIDE { - // Note that we don't need to test for generate_clinit_check_. - // Whether or not we need to generate the clinit check is processed in - // prepare_for_register_allocator based on existing HInvokes and HClinitChecks. - return other->AsLoadClass()->type_index_ == type_index_ && - other->AsLoadClass()->GetPackedFields() == GetPackedFields(); + void SetLoadKindWithTypeReference(LoadKind load_kind, + const DexFile& dex_file, + uint32_t type_index) { + DCHECK(HasTypeReference(load_kind)); + DCHECK(IsSameDexFile(dex_file_, dex_file)); + DCHECK_EQ(type_index_, type_index); + SetLoadKindInternal(load_kind); + } + + void SetLoadKindWithDexCacheReference(LoadKind load_kind, + const DexFile& dex_file, + uint32_t element_index) { + DCHECK(HasDexCacheReference(load_kind)); + DCHECK(IsSameDexFile(dex_file_, dex_file)); + load_data_.dex_cache_element_index = element_index; + SetLoadKindInternal(load_kind); } + LoadKind GetLoadKind() const { + return GetPackedField<LoadKindField>(); + } + + bool CanBeMoved() const OVERRIDE { return true; } + + bool InstructionDataEquals(const HInstruction* other) const; + size_t ComputeHashCode() const OVERRIDE { return type_index_; } - uint16_t GetTypeIndex() const { return type_index_; } bool CanBeNull() const OVERRIDE { return false; } bool NeedsEnvironment() const OVERRIDE { @@ -5400,7 +5514,15 @@ class HLoadClass FINAL : public HExpression<1> { loaded_class_rti_ = rti; } - const DexFile& GetDexFile() { return dex_file_; } + uint32_t GetTypeIndex() const { return type_index_; } + const DexFile& GetDexFile() const { return dex_file_; } + + uint32_t GetDexCacheElementOffset() const; + + uint64_t GetAddress() const { + DCHECK(HasAddress(GetLoadKind())); + return load_data_.address; + } bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return !IsReferrersClass(); } @@ -5408,30 +5530,96 @@ class HLoadClass FINAL : public HExpression<1> { return SideEffects::CanTriggerGC(); } - bool IsReferrersClass() const { return GetPackedFlag<kFlagIsReferrersClass>(); } + bool IsReferrersClass() const { return GetLoadKind() == LoadKind::kReferrersClass; } bool NeedsAccessCheck() const { return GetPackedFlag<kFlagNeedsAccessCheck>(); } bool IsInDexCache() const { return GetPackedFlag<kFlagIsInDexCache>(); } bool MustGenerateClinitCheck() const { return GetPackedFlag<kFlagGenerateClInitCheck>(); } + void MarkInDexCache() { + SetPackedFlag<kFlagIsInDexCache>(true); + DCHECK(!NeedsEnvironment()); + RemoveEnvironment(); + SetSideEffects(SideEffects::None()); + } + + void AddSpecialInput(HInstruction* special_input); + + using HInstruction::GetInputRecords; // Keep the const version visible. + ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE FINAL { + return ArrayRef<HUserRecord<HInstruction*>>( + &special_input_, (special_input_.GetInstruction() != nullptr) ? 1u : 0u); + } + + Primitive::Type GetType() const OVERRIDE { + return Primitive::kPrimNot; + } + DECLARE_INSTRUCTION(LoadClass); private: - static constexpr size_t kFlagIsReferrersClass = kNumberOfExpressionPackedBits; - static constexpr size_t kFlagNeedsAccessCheck = kFlagIsReferrersClass + 1; + static constexpr size_t kFlagNeedsAccessCheck = kNumberOfGenericPackedBits; static constexpr size_t kFlagIsInDexCache = kFlagNeedsAccessCheck + 1; // Whether this instruction must generate the initialization check. // Used for code generation. static constexpr size_t kFlagGenerateClInitCheck = kFlagIsInDexCache + 1; - static constexpr size_t kNumberOfLoadClassPackedBits = kFlagGenerateClInitCheck + 1; + static constexpr size_t kFieldLoadKind = kFlagGenerateClInitCheck + 1; + static constexpr size_t kFieldLoadKindSize = + MinimumBitsToStore(static_cast<size_t>(LoadKind::kLast)); + static constexpr size_t kNumberOfLoadClassPackedBits = kFieldLoadKind + kFieldLoadKindSize; static_assert(kNumberOfLoadClassPackedBits < kMaxNumberOfPackedBits, "Too many packed fields."); + using LoadKindField = BitField<LoadKind, kFieldLoadKind, kFieldLoadKindSize>; + + static bool HasTypeReference(LoadKind load_kind) { + return load_kind == LoadKind::kBootImageLinkTimeAddress || + load_kind == LoadKind::kBootImageLinkTimePcRelative || + load_kind == LoadKind::kDexCacheViaMethod || + load_kind == LoadKind::kReferrersClass; + } + + static bool HasAddress(LoadKind load_kind) { + return load_kind == LoadKind::kBootImageAddress || load_kind == LoadKind::kDexCacheAddress; + } + + static bool HasDexCacheReference(LoadKind load_kind) { + return load_kind == LoadKind::kDexCachePcRelative; + } + + void SetLoadKindInternal(LoadKind load_kind); + + // The special input is the HCurrentMethod for kDexCacheViaMethod or kReferrersClass. + // For other load kinds it's empty or possibly some architecture-specific instruction + // for PC-relative loads, i.e. kDexCachePcRelative or kBootImageLinkTimePcRelative. + HUserRecord<HInstruction*> special_input_; const uint16_t type_index_; const DexFile& dex_file_; + union { + uint32_t dex_cache_element_index; // Only for dex cache reference. + uint64_t address; // Up to 64-bit, needed for kDexCacheAddress on 64-bit targets. + } load_data_; + ReferenceTypeInfo loaded_class_rti_; DISALLOW_COPY_AND_ASSIGN(HLoadClass); }; +std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs); + +// Note: defined outside class to see operator<<(., HLoadClass::LoadKind). +inline uint32_t HLoadClass::GetDexCacheElementOffset() const { + DCHECK(HasDexCacheReference(GetLoadKind())) << GetLoadKind(); + return load_data_.dex_cache_element_index; +} + +// Note: defined outside class to see operator<<(., HLoadClass::LoadKind). +inline void HLoadClass::AddSpecialInput(HInstruction* special_input) { + // The special input is used for PC-relative loads on some architectures. + DCHECK(GetLoadKind() == LoadKind::kBootImageLinkTimePcRelative || + GetLoadKind() == LoadKind::kDexCachePcRelative) << GetLoadKind(); + DCHECK(special_input_.GetInstruction() == nullptr); + special_input_ = HUserRecord<HInstruction*>(special_input); + special_input->AddUseAt(this, 0); +} class HLoadString FINAL : public HInstruction { public: @@ -5599,6 +5787,9 @@ class HLoadString FINAL : public HInstruction { void SetLoadKindInternal(LoadKind load_kind); + // The special input is the HCurrentMethod for kDexCacheViaMethod. + // For other load kinds it's empty or possibly some architecture-specific instruction + // for PC-relative loads, i.e. kDexCachePcRelative or kBootImageLinkTimePcRelative. HUserRecord<HInstruction*> special_input_; // String index serves also as the hash code and it's also needed for slow-paths, @@ -6572,16 +6763,6 @@ inline int64_t Int64FromConstant(HConstant* constant) { } } -inline bool IsSameDexFile(const DexFile& lhs, const DexFile& rhs) { - // For the purposes of the compiler, the dex files must actually be the same object - // if we want to safely treat them as the same. This is especially important for JIT - // as custom class loaders can open the same underlying file (or memory) multiple - // times and provide different class resolution but no two class loaders should ever - // use the same DexFile object - doing so is an unsupported hack that can lead to - // all sorts of weird failures. - return &lhs == &rhs; -} - #define INSTRUCTION_TYPE_CHECK(type, super) \ inline bool HInstruction::Is##type() const { return GetKind() == k##type; } \ inline const H##type* HInstruction::As##type() const { \ diff --git a/compiler/optimizing/pc_relative_fixups_x86.cc b/compiler/optimizing/pc_relative_fixups_x86.cc index cb2fc0a19a..93116f8bab 100644 --- a/compiler/optimizing/pc_relative_fixups_x86.cc +++ b/compiler/optimizing/pc_relative_fixups_x86.cc @@ -80,6 +80,15 @@ class PCRelativeHandlerVisitor : public HGraphVisitor { HandleInvoke(invoke); } + void VisitLoadClass(HLoadClass* load_class) OVERRIDE { + HLoadClass::LoadKind load_kind = load_class->GetLoadKind(); + if (load_kind == HLoadClass::LoadKind::kBootImageLinkTimePcRelative || + load_kind == HLoadClass::LoadKind::kDexCachePcRelative) { + InitializePCRelativeBasePointer(); + load_class->AddSpecialInput(base_); + } + } + void VisitLoadString(HLoadString* load_string) OVERRIDE { HLoadString::LoadKind load_kind = load_string->GetLoadKind(); if (load_kind == HLoadString::LoadKind::kBootImageLinkTimePcRelative || diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc index c941c0c086..696b8c6859 100644 --- a/compiler/optimizing/prepare_for_register_allocation.cc +++ b/compiler/optimizing/prepare_for_register_allocation.cc @@ -40,6 +40,22 @@ void PrepareForRegisterAllocation::VisitDivZeroCheck(HDivZeroCheck* check) { void PrepareForRegisterAllocation::VisitBoundsCheck(HBoundsCheck* check) { check->ReplaceWith(check->InputAt(0)); + if (check->IsStringCharAt()) { + // Add a fake environment for String.charAt() inline info as we want + // the exception to appear as being thrown from there. + const DexFile& dex_file = check->GetEnvironment()->GetDexFile(); + DCHECK_STREQ(PrettyMethod(check->GetStringCharAtMethodIndex(), dex_file).c_str(), + "char java.lang.String.charAt(int)"); + ArenaAllocator* arena = GetGraph()->GetArena(); + HEnvironment* environment = new (arena) HEnvironment(arena, + /* number_of_vregs */ 0u, + dex_file, + check->GetStringCharAtMethodIndex(), + /* dex_pc */ DexFile::kDexNoIndex, + kVirtual, + check); + check->InsertRawEnvironment(environment); + } } void PrepareForRegisterAllocation::VisitBoundType(HBoundType* bound_type) { diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index 3e6adcb172..3dfd7282cd 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -46,13 +46,6 @@ static inline ReferenceTypeInfo::TypeHandle GetRootHandle(StackHandleScopeCollec return *cache; } -// Returns true if klass is admissible to the propagation: non-null and resolved. -// For an array type, we also check if the component type is admissible. -static bool IsAdmissible(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_) { - return klass != nullptr && klass->IsResolved() && - (!klass->IsArrayClass() || IsAdmissible(klass->GetComponentType())); -} - ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetObjectClassHandle() { return GetRootHandle(handles_, ClassLinker::kJavaLangObject, &object_class_handle_); } diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h index 2106be6b53..edd83bf5de 100644 --- a/compiler/optimizing/reference_type_propagation.h +++ b/compiler/optimizing/reference_type_propagation.h @@ -42,6 +42,14 @@ class ReferenceTypePropagation : public HOptimization { void Run() OVERRIDE; + // Returns true if klass is admissible to the propagation: non-null and resolved. + // For an array type, we also check if the component type is admissible. + static bool IsAdmissible(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_) { + return klass != nullptr && + klass->IsResolved() && + (!klass->IsArrayClass() || IsAdmissible(klass->GetComponentType())); + } + static constexpr const char* kReferenceTypePropagationPassName = "reference_type_propagation"; private: diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index 08bd35f14a..97f34e6c32 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -40,13 +40,14 @@ void HSharpening::Run() { HInstruction* instruction = it.Current(); if (instruction->IsInvokeStaticOrDirect()) { ProcessInvokeStaticOrDirect(instruction->AsInvokeStaticOrDirect()); + } else if (instruction->IsLoadClass()) { + ProcessLoadClass(instruction->AsLoadClass()); } else if (instruction->IsLoadString()) { ProcessLoadString(instruction->AsLoadString()); } // TODO: Move the sharpening of invoke-virtual/-interface/-super from HGraphBuilder // here. Rewrite it to avoid the CompilerDriver's reliance on verifier data // because we know the type better when inlining. - // TODO: HLoadClass - select better load kind if available. } } } @@ -153,6 +154,123 @@ void HSharpening::ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { invoke->SetDispatchInfo(dispatch_info); } +void HSharpening::ProcessLoadClass(HLoadClass* load_class) { + if (load_class->NeedsAccessCheck()) { + // We need to call the runtime anyway, so we simply get the class as that call's return value. + return; + } + if (load_class->GetLoadKind() == HLoadClass::LoadKind::kReferrersClass) { + // Loading from the ArtMethod* is the most efficient retrieval. + // TODO: This may not actually be true for all architectures and + // locations of target classes. The additional register pressure + // for using the ArtMethod* should be considered. + return; + } + + DCHECK_EQ(load_class->GetLoadKind(), HLoadClass::LoadKind::kDexCacheViaMethod); + DCHECK(!load_class->IsInDexCache()) << "HLoadClass should not be optimized before sharpening."; + + const DexFile& dex_file = load_class->GetDexFile(); + uint32_t type_index = load_class->GetTypeIndex(); + + bool is_in_dex_cache = false; + HLoadClass::LoadKind desired_load_kind; + uint64_t address = 0u; // Class or dex cache element address. + { + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<1> hs(soa.Self()); + Runtime* runtime = Runtime::Current(); + ClassLinker* class_linker = runtime->GetClassLinker(); + Handle<mirror::DexCache> dex_cache = IsSameDexFile(dex_file, *compilation_unit_.GetDexFile()) + ? compilation_unit_.GetDexCache() + : hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file)); + mirror::Class* klass = dex_cache->GetResolvedType(type_index); + + if (compiler_driver_->IsBootImage()) { + // Compiling boot image. Check if the class is a boot image class. + DCHECK(!runtime->UseJitCompilation()); + if (!compiler_driver_->GetSupportBootImageFixup()) { + // MIPS/MIPS64 or compiler_driver_test. Do not sharpen. + desired_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod; + } else { + if (klass != nullptr && + compiler_driver_->IsImageClass( + dex_file.StringDataByIdx(dex_file.GetTypeId(type_index).descriptor_idx_))) { + is_in_dex_cache = true; + desired_load_kind = codegen_->GetCompilerOptions().GetCompilePic() + ? HLoadClass::LoadKind::kBootImageLinkTimePcRelative + : HLoadClass::LoadKind::kBootImageLinkTimeAddress; + } else { + // Not a boot image class. We must go through the dex cache. + DCHECK(ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &dex_file)); + desired_load_kind = HLoadClass::LoadKind::kDexCachePcRelative; + } + } + } else if (runtime->UseJitCompilation()) { + // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus. + // DCHECK(!codegen_->GetCompilerOptions().GetCompilePic()); + is_in_dex_cache = (klass != nullptr); + if (klass != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(klass)) { + // TODO: Use direct pointers for all non-moving spaces, not just boot image. Bug: 29530787 + desired_load_kind = HLoadClass::LoadKind::kBootImageAddress; + address = reinterpret_cast64<uint64_t>(klass); + } else { + // Note: If the class is not in the dex cache or isn't initialized, the + // instruction needs environment and will not be inlined across dex files. + // Within a dex file, the slow-path helper loads the correct class and + // inlined frames are used correctly for OOM stack trace. + // TODO: Write a test for this. Bug: 29416588 + desired_load_kind = HLoadClass::LoadKind::kDexCacheAddress; + void* dex_cache_element_address = &dex_cache->GetResolvedTypes()[type_index]; + address = reinterpret_cast64<uint64_t>(dex_cache_element_address); + } + } else { + // AOT app compilation. Check if the class is in the boot image. + if ((klass != nullptr) && + runtime->GetHeap()->ObjectIsInBootImageSpace(klass) && + !codegen_->GetCompilerOptions().GetCompilePic()) { + desired_load_kind = HLoadClass::LoadKind::kBootImageAddress; + address = reinterpret_cast64<uint64_t>(klass); + } else { + // Not JIT and either the klass is not in boot image or we are compiling in PIC mode. + // Use PC-relative load from the dex cache if the dex file belongs + // to the oat file that we're currently compiling. + desired_load_kind = + ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &load_class->GetDexFile()) + ? HLoadClass::LoadKind::kDexCachePcRelative + : HLoadClass::LoadKind::kDexCacheViaMethod; + } + } + } + if (is_in_dex_cache) { + load_class->MarkInDexCache(); + } + + HLoadClass::LoadKind load_kind = codegen_->GetSupportedLoadClassKind(desired_load_kind); + switch (load_kind) { + case HLoadClass::LoadKind::kBootImageLinkTimeAddress: + case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: + case HLoadClass::LoadKind::kDexCacheViaMethod: + load_class->SetLoadKindWithTypeReference(load_kind, dex_file, type_index); + break; + case HLoadClass::LoadKind::kBootImageAddress: + case HLoadClass::LoadKind::kDexCacheAddress: + DCHECK_NE(address, 0u); + load_class->SetLoadKindWithAddress(load_kind, address); + break; + case HLoadClass::LoadKind::kDexCachePcRelative: { + size_t pointer_size = InstructionSetPointerSize(codegen_->GetInstructionSet()); + DexCacheArraysLayout layout(pointer_size, &dex_file); + size_t element_index = layout.TypeOffset(type_index); + load_class->SetLoadKindWithDexCacheReference(load_kind, dex_file, element_index); + break; + } + default: + LOG(FATAL) << "Unexpected load kind: " << load_kind; + UNREACHABLE(); + } +} + void HSharpening::ProcessLoadString(HLoadString* load_string) { DCHECK_EQ(load_string->GetLoadKind(), HLoadString::LoadKind::kDexCacheViaMethod); DCHECK(!load_string->IsInDexCache()); @@ -193,13 +311,14 @@ void HSharpening::ProcessLoadString(HLoadString* load_string) { mirror::String* string = dex_cache->GetResolvedString(string_index); is_in_dex_cache = (string != nullptr); if (string != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(string)) { + // TODO: Use direct pointers for all non-moving spaces, not just boot image. Bug: 29530787 desired_load_kind = HLoadString::LoadKind::kBootImageAddress; address = reinterpret_cast64<uint64_t>(string); } else { // Note: If the string is not in the dex cache, the instruction needs environment // and will not be inlined across dex files. Within a dex file, the slow-path helper // loads the correct string and inlined frames are used correctly for OOM stack trace. - // TODO: Write a test for this. + // TODO: Write a test for this. Bug: 29416588 desired_load_kind = HLoadString::LoadKind::kDexCacheAddress; void* dex_cache_element_address = &dex_cache->GetStrings()[string_index]; address = reinterpret_cast64<uint64_t>(dex_cache_element_address); @@ -207,20 +326,18 @@ void HSharpening::ProcessLoadString(HLoadString* load_string) { } else { // AOT app compilation. Try to lookup the string without allocating if not found. mirror::String* string = class_linker->LookupString(dex_file, string_index, dex_cache); - if (string != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(string)) { - if (codegen_->GetCompilerOptions().GetCompilePic()) { - // Use PC-relative load from the dex cache if the dex file belongs - // to the oat file that we're currently compiling. - desired_load_kind = ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &dex_file) - ? HLoadString::LoadKind::kDexCachePcRelative - : HLoadString::LoadKind::kDexCacheViaMethod; - } else { - desired_load_kind = HLoadString::LoadKind::kBootImageAddress; - address = reinterpret_cast64<uint64_t>(string); - } + if (string != nullptr && + runtime->GetHeap()->ObjectIsInBootImageSpace(string) && + !codegen_->GetCompilerOptions().GetCompilePic()) { + desired_load_kind = HLoadString::LoadKind::kBootImageAddress; + address = reinterpret_cast64<uint64_t>(string); } else { - // Not JIT and the string is not in boot image. - desired_load_kind = HLoadString::LoadKind::kDexCachePcRelative; + // Not JIT and either the string is not in boot image or we are compiling in PIC mode. + // Use PC-relative load from the dex cache if the dex file belongs + // to the oat file that we're currently compiling. + desired_load_kind = ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &dex_file) + ? HLoadString::LoadKind::kDexCachePcRelative + : HLoadString::LoadKind::kDexCacheViaMethod; } } } diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h index 24152f6b71..d35ae66e05 100644 --- a/compiler/optimizing/sharpening.h +++ b/compiler/optimizing/sharpening.h @@ -47,6 +47,7 @@ class HSharpening : public HOptimization { private: void ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke); + void ProcessLoadClass(HLoadClass* load_class); void ProcessLoadString(HLoadString* load_string); CodeGenerator* codegen_; diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index 11a254ef63..fc8af6462a 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -228,7 +228,7 @@ size_t StackMapStream::ComputeDexRegisterMapsSize() const { void StackMapStream::ComputeInlineInfoEncoding() { uint32_t method_index_max = 0; - uint32_t dex_pc_max = 0; + uint32_t dex_pc_max = DexFile::kDexNoIndex; uint32_t invoke_type_max = 0; uint32_t inline_info_index = 0; @@ -236,7 +236,10 @@ void StackMapStream::ComputeInlineInfoEncoding() { for (size_t j = 0; j < entry.inlining_depth; ++j) { InlineInfoEntry inline_entry = inline_infos_[inline_info_index++]; method_index_max = std::max(method_index_max, inline_entry.method_index); - dex_pc_max = std::max(dex_pc_max, inline_entry.dex_pc); + if (inline_entry.dex_pc != DexFile::kDexNoIndex && + (dex_pc_max == DexFile::kDexNoIndex || dex_pc_max < inline_entry.dex_pc)) { + dex_pc_max = inline_entry.dex_pc; + } invoke_type_max = std::max(invoke_type_max, static_cast<uint32_t>(inline_entry.invoke_type)); } } diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index 41f72f508b..53a9795d52 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -108,7 +108,7 @@ class StackMapStream : public ValueObject { }; struct InlineInfoEntry { - uint32_t dex_pc; + uint32_t dex_pc; // DexFile::kDexNoIndex for intrinsified native methods. uint32_t method_index; InvokeType invoke_type; uint32_t num_dex_registers; diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h index 274d0de166..a571d14a71 100644 --- a/compiler/utils/arm/assembler_arm.h +++ b/compiler/utils/arm/assembler_arm.h @@ -671,6 +671,9 @@ class ArmAssembler : public Assembler { virtual void vcmpdz(DRegister dd, Condition cond = AL) = 0; virtual void vmstat(Condition cond = AL) = 0; // VMRS APSR_nzcv, FPSCR + virtual void vcntd(DRegister dd, DRegister dm) = 0; + virtual void vpaddld(DRegister dd, DRegister dm, int32_t size, bool is_unsigned) = 0; + virtual void vpushs(SRegister reg, int nregs, Condition cond = AL) = 0; virtual void vpushd(DRegister reg, int nregs, Condition cond = AL) = 0; virtual void vpops(SRegister reg, int nregs, Condition cond = AL) = 0; diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc index 0a227b21cd..6f7119d578 100644 --- a/compiler/utils/arm/assembler_arm32.cc +++ b/compiler/utils/arm/assembler_arm32.cc @@ -1264,6 +1264,31 @@ void Arm32Assembler::vmstat(Condition cond) { // VMRS APSR_nzcv, FPSCR Emit(encoding); } +void Arm32Assembler::vcntd(DRegister dd, DRegister dm) { + uint32_t encoding = (B31 | B30 | B29 | B28 | B25 | B24 | B23 | B21 | B20) | + ((static_cast<int32_t>(dd) >> 4) * B22) | + ((static_cast<uint32_t>(dd) & 0xf) * B12) | + (B10 | B8) | + ((static_cast<int32_t>(dm) >> 4) * B5) | + (static_cast<uint32_t>(dm) & 0xf); + + Emit(encoding); +} + +void Arm32Assembler::vpaddld(DRegister dd, DRegister dm, int32_t size, bool is_unsigned) { + CHECK(size == 8 || size == 16 || size == 32) << size; + uint32_t encoding = (B31 | B30 | B29 | B28 | B25 | B24 | B23 | B21 | B20) | + ((static_cast<uint32_t>(size >> 4) & 0x3) * B18) | + ((static_cast<int32_t>(dd) >> 4) * B22) | + ((static_cast<uint32_t>(dd) & 0xf) * B12) | + (B9) | + (is_unsigned ? B7 : 0) | + ((static_cast<int32_t>(dm) >> 4) * B5) | + (static_cast<uint32_t>(dm) & 0xf); + + Emit(encoding); +} + void Arm32Assembler::svc(uint32_t imm24) { CHECK(IsUint<24>(imm24)) << imm24; diff --git a/compiler/utils/arm/assembler_arm32.h b/compiler/utils/arm/assembler_arm32.h index bc6020e008..8726ac85fd 100644 --- a/compiler/utils/arm/assembler_arm32.h +++ b/compiler/utils/arm/assembler_arm32.h @@ -205,6 +205,9 @@ class Arm32Assembler FINAL : public ArmAssembler { void vcmpdz(DRegister dd, Condition cond = AL) OVERRIDE; void vmstat(Condition cond = AL) OVERRIDE; // VMRS APSR_nzcv, FPSCR + void vcntd(DRegister dd, DRegister dm) OVERRIDE; + void vpaddld(DRegister dd, DRegister dm, int32_t size, bool is_unsigned) OVERRIDE; + void vpushs(SRegister reg, int nregs, Condition cond = AL) OVERRIDE; void vpushd(DRegister reg, int nregs, Condition cond = AL) OVERRIDE; void vpops(SRegister reg, int nregs, Condition cond = AL) OVERRIDE; diff --git a/compiler/utils/arm/assembler_arm32_test.cc b/compiler/utils/arm/assembler_arm32_test.cc index e570e22fca..b214062e18 100644 --- a/compiler/utils/arm/assembler_arm32_test.cc +++ b/compiler/utils/arm/assembler_arm32_test.cc @@ -899,4 +899,43 @@ TEST_F(AssemblerArm32Test, revsh) { T3Helper(&arm::Arm32Assembler::revsh, true, "revsh{cond} {reg1}, {reg2}", "revsh"); } +TEST_F(AssemblerArm32Test, vcnt) { + // Different D register numbers are used here, to test register encoding. + // Source register number is encoded as M:Vm, destination register number is encoded as D:Vd, + // For source and destination registers which use D0..D15, the M bit and D bit should be 0. + // For source and destination registers which use D16..D32, the M bit and D bit should be 1. + GetAssembler()->vcntd(arm::D0, arm::D1); + GetAssembler()->vcntd(arm::D19, arm::D20); + GetAssembler()->vcntd(arm::D0, arm::D9); + GetAssembler()->vcntd(arm::D16, arm::D20); + + std::string expected = + "vcnt.8 d0, d1\n" + "vcnt.8 d19, d20\n" + "vcnt.8 d0, d9\n" + "vcnt.8 d16, d20\n"; + + DriverStr(expected, "vcnt"); +} + +TEST_F(AssemblerArm32Test, vpaddl) { + // Different D register numbers are used here, to test register encoding. + // Source register number is encoded as M:Vm, destination register number is encoded as D:Vd, + // For source and destination registers which use D0..D15, the M bit and D bit should be 0. + // For source and destination registers which use D16..D32, the M bit and D bit should be 1. + // Different data types (signed and unsigned) are also tested. + GetAssembler()->vpaddld(arm::D0, arm::D0, 8, true); + GetAssembler()->vpaddld(arm::D20, arm::D20, 8, false); + GetAssembler()->vpaddld(arm::D0, arm::D20, 16, false); + GetAssembler()->vpaddld(arm::D20, arm::D0, 32, true); + + std::string expected = + "vpaddl.u8 d0, d0\n" + "vpaddl.s8 d20, d20\n" + "vpaddl.s16 d0, d20\n" + "vpaddl.u32 d20, d0\n"; + + DriverStr(expected, "vpaddl"); +} + } // namespace art diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc index 546dd653af..a72ea410ce 100644 --- a/compiler/utils/arm/assembler_thumb2.cc +++ b/compiler/utils/arm/assembler_thumb2.cc @@ -3117,6 +3117,30 @@ void Thumb2Assembler::vmstat(Condition cond) { // VMRS APSR_nzcv, FPSCR. Emit32(encoding); } +void Thumb2Assembler::vcntd(DRegister dd, DRegister dm) { + uint32_t encoding = (B31 | B30 | B29 | B28 | B27 | B26 | B25 | B24 | B23 | B21 | B20) | + ((static_cast<int32_t>(dd) >> 4) * B22) | + ((static_cast<uint32_t>(dd) & 0xf) * B12) | + (B10 | B8) | + ((static_cast<int32_t>(dm) >> 4) * B5) | + (static_cast<uint32_t>(dm) & 0xf); + + Emit32(encoding); +} + +void Thumb2Assembler::vpaddld(DRegister dd, DRegister dm, int32_t size, bool is_unsigned) { + CHECK(size == 8 || size == 16 || size == 32) << size; + uint32_t encoding = (B31 | B30 | B29 | B28 | B27 | B26 | B25 | B24 | B23 | B21 | B20) | + ((static_cast<uint32_t>(size >> 4) & 0x3) * B18) | + ((static_cast<int32_t>(dd) >> 4) * B22) | + ((static_cast<uint32_t>(dd) & 0xf) * B12) | + (B9) | + (is_unsigned ? B7 : 0) | + ((static_cast<int32_t>(dm) >> 4) * B5) | + (static_cast<uint32_t>(dm) & 0xf); + + Emit32(encoding); +} void Thumb2Assembler::svc(uint32_t imm8) { CHECK(IsUint<8>(imm8)) << imm8; diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h index ce310a4da8..2ca74fc863 100644 --- a/compiler/utils/arm/assembler_thumb2.h +++ b/compiler/utils/arm/assembler_thumb2.h @@ -250,6 +250,9 @@ class Thumb2Assembler FINAL : public ArmAssembler { void vcmpdz(DRegister dd, Condition cond = AL) OVERRIDE; void vmstat(Condition cond = AL) OVERRIDE; // VMRS APSR_nzcv, FPSCR + void vcntd(DRegister dd, DRegister dm) OVERRIDE; + void vpaddld(DRegister dd, DRegister dm, int32_t size, bool is_unsigned) OVERRIDE; + void vpushs(SRegister reg, int nregs, Condition cond = AL) OVERRIDE; void vpushd(DRegister reg, int nregs, Condition cond = AL) OVERRIDE; void vpops(SRegister reg, int nregs, Condition cond = AL) OVERRIDE; diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc index b5cafcbf66..7f1dc49734 100644 --- a/compiler/utils/arm/assembler_thumb2_test.cc +++ b/compiler/utils/arm/assembler_thumb2_test.cc @@ -1380,4 +1380,43 @@ TEST_F(AssemblerThumb2Test, revsh) { DriverStr(expected, "revsh"); } +TEST_F(AssemblerThumb2Test, vcnt) { + // Different D register numbers are used here, to test register encoding. + // Source register number is encoded as M:Vm, destination register number is encoded as D:Vd, + // For source and destination registers which use D0..D15, the M bit and D bit should be 0. + // For source and destination registers which use D16..D32, the M bit and D bit should be 1. + __ vcntd(arm::D0, arm::D1); + __ vcntd(arm::D19, arm::D20); + __ vcntd(arm::D0, arm::D9); + __ vcntd(arm::D16, arm::D20); + + std::string expected = + "vcnt.8 d0, d1\n" + "vcnt.8 d19, d20\n" + "vcnt.8 d0, d9\n" + "vcnt.8 d16, d20\n"; + + DriverStr(expected, "vcnt"); +} + +TEST_F(AssemblerThumb2Test, vpaddl) { + // Different D register numbers are used here, to test register encoding. + // Source register number is encoded as M:Vm, destination register number is encoded as D:Vd, + // For source and destination registers which use D0..D15, the M bit and D bit should be 0. + // For source and destination registers which use D16..D32, the M bit and D bit should be 1. + // Different data types (signed and unsigned) are also tested. + __ vpaddld(arm::D0, arm::D0, 8, true); + __ vpaddld(arm::D20, arm::D20, 8, false); + __ vpaddld(arm::D0, arm::D20, 16, false); + __ vpaddld(arm::D20, arm::D0, 32, true); + + std::string expected = + "vpaddl.u8 d0, d0\n" + "vpaddl.s8 d20, d20\n" + "vpaddl.s16 d0, d20\n" + "vpaddl.u32 d20, d0\n"; + + DriverStr(expected, "vpaddl"); +} + } // namespace art diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h index 084e9011ba..afe0576906 100644 --- a/compiler/utils/assembler_test.h +++ b/compiler/utils/assembler_test.h @@ -461,7 +461,7 @@ class AssemblerTest : public testing::Test { void SetUp() OVERRIDE { arena_.reset(new ArenaAllocator(&pool_)); - assembler_.reset(new (arena_.get()) Ass(arena_.get())); + assembler_.reset(CreateAssembler(arena_.get())); test_helper_.reset( new AssemblerTestInfrastructure(GetArchitectureString(), GetAssemblerCmdName(), @@ -481,6 +481,11 @@ class AssemblerTest : public testing::Test { arena_.reset(); } + // Override this to set up any architecture-specific things, e.g., CPU revision. + virtual Ass* CreateAssembler(ArenaAllocator* arena) { + return new (arena) Ass(arena); + } + // Override this to set up any architecture-specific things, e.g., register vectors. virtual void SetUpHelpers() {} diff --git a/compiler/utils/mips/assembler_mips32r6_test.cc b/compiler/utils/mips/assembler_mips32r6_test.cc new file mode 100644 index 0000000000..ce92d602d0 --- /dev/null +++ b/compiler/utils/mips/assembler_mips32r6_test.cc @@ -0,0 +1,644 @@ +/* + * Copyright (C) 2016 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. + */ + +#include "assembler_mips.h" + +#include <map> + +#include "base/stl_util.h" +#include "utils/assembler_test.h" + +#define __ GetAssembler()-> + +namespace art { + +struct MIPSCpuRegisterCompare { + bool operator()(const mips::Register& a, const mips::Register& b) const { + return a < b; + } +}; + +class AssemblerMIPS32r6Test : public AssemblerTest<mips::MipsAssembler, + mips::Register, + mips::FRegister, + uint32_t> { + public: + typedef AssemblerTest<mips::MipsAssembler, mips::Register, mips::FRegister, uint32_t> Base; + + AssemblerMIPS32r6Test() : + instruction_set_features_(MipsInstructionSetFeatures::FromVariant("mips32r6", nullptr)) { + } + + protected: + // Get the typically used name for this architecture, e.g., aarch64, x86-64, ... + std::string GetArchitectureString() OVERRIDE { + return "mips"; + } + + std::string GetAssemblerParameters() OVERRIDE { + return " --no-warn -32 -march=mips32r6"; + } + + std::string GetDisassembleParameters() OVERRIDE { + return " -D -bbinary -mmips:isa32r6"; + } + + mips::MipsAssembler* CreateAssembler(ArenaAllocator* arena) OVERRIDE { + return new (arena) mips::MipsAssembler(arena, instruction_set_features_.get()); + } + + void SetUpHelpers() OVERRIDE { + if (registers_.size() == 0) { + registers_.push_back(new mips::Register(mips::ZERO)); + registers_.push_back(new mips::Register(mips::AT)); + registers_.push_back(new mips::Register(mips::V0)); + registers_.push_back(new mips::Register(mips::V1)); + registers_.push_back(new mips::Register(mips::A0)); + registers_.push_back(new mips::Register(mips::A1)); + registers_.push_back(new mips::Register(mips::A2)); + registers_.push_back(new mips::Register(mips::A3)); + registers_.push_back(new mips::Register(mips::T0)); + registers_.push_back(new mips::Register(mips::T1)); + registers_.push_back(new mips::Register(mips::T2)); + registers_.push_back(new mips::Register(mips::T3)); + registers_.push_back(new mips::Register(mips::T4)); + registers_.push_back(new mips::Register(mips::T5)); + registers_.push_back(new mips::Register(mips::T6)); + registers_.push_back(new mips::Register(mips::T7)); + registers_.push_back(new mips::Register(mips::S0)); + registers_.push_back(new mips::Register(mips::S1)); + registers_.push_back(new mips::Register(mips::S2)); + registers_.push_back(new mips::Register(mips::S3)); + registers_.push_back(new mips::Register(mips::S4)); + registers_.push_back(new mips::Register(mips::S5)); + registers_.push_back(new mips::Register(mips::S6)); + registers_.push_back(new mips::Register(mips::S7)); + registers_.push_back(new mips::Register(mips::T8)); + registers_.push_back(new mips::Register(mips::T9)); + registers_.push_back(new mips::Register(mips::K0)); + registers_.push_back(new mips::Register(mips::K1)); + registers_.push_back(new mips::Register(mips::GP)); + registers_.push_back(new mips::Register(mips::SP)); + registers_.push_back(new mips::Register(mips::FP)); + registers_.push_back(new mips::Register(mips::RA)); + + secondary_register_names_.emplace(mips::Register(mips::ZERO), "zero"); + secondary_register_names_.emplace(mips::Register(mips::AT), "at"); + secondary_register_names_.emplace(mips::Register(mips::V0), "v0"); + secondary_register_names_.emplace(mips::Register(mips::V1), "v1"); + secondary_register_names_.emplace(mips::Register(mips::A0), "a0"); + secondary_register_names_.emplace(mips::Register(mips::A1), "a1"); + secondary_register_names_.emplace(mips::Register(mips::A2), "a2"); + secondary_register_names_.emplace(mips::Register(mips::A3), "a3"); + secondary_register_names_.emplace(mips::Register(mips::T0), "t0"); + secondary_register_names_.emplace(mips::Register(mips::T1), "t1"); + secondary_register_names_.emplace(mips::Register(mips::T2), "t2"); + secondary_register_names_.emplace(mips::Register(mips::T3), "t3"); + secondary_register_names_.emplace(mips::Register(mips::T4), "t4"); + secondary_register_names_.emplace(mips::Register(mips::T5), "t5"); + secondary_register_names_.emplace(mips::Register(mips::T6), "t6"); + secondary_register_names_.emplace(mips::Register(mips::T7), "t7"); + secondary_register_names_.emplace(mips::Register(mips::S0), "s0"); + secondary_register_names_.emplace(mips::Register(mips::S1), "s1"); + secondary_register_names_.emplace(mips::Register(mips::S2), "s2"); + secondary_register_names_.emplace(mips::Register(mips::S3), "s3"); + secondary_register_names_.emplace(mips::Register(mips::S4), "s4"); + secondary_register_names_.emplace(mips::Register(mips::S5), "s5"); + secondary_register_names_.emplace(mips::Register(mips::S6), "s6"); + secondary_register_names_.emplace(mips::Register(mips::S7), "s7"); + secondary_register_names_.emplace(mips::Register(mips::T8), "t8"); + secondary_register_names_.emplace(mips::Register(mips::T9), "t9"); + secondary_register_names_.emplace(mips::Register(mips::K0), "k0"); + secondary_register_names_.emplace(mips::Register(mips::K1), "k1"); + secondary_register_names_.emplace(mips::Register(mips::GP), "gp"); + secondary_register_names_.emplace(mips::Register(mips::SP), "sp"); + secondary_register_names_.emplace(mips::Register(mips::FP), "fp"); + secondary_register_names_.emplace(mips::Register(mips::RA), "ra"); + + fp_registers_.push_back(new mips::FRegister(mips::F0)); + fp_registers_.push_back(new mips::FRegister(mips::F1)); + fp_registers_.push_back(new mips::FRegister(mips::F2)); + fp_registers_.push_back(new mips::FRegister(mips::F3)); + fp_registers_.push_back(new mips::FRegister(mips::F4)); + fp_registers_.push_back(new mips::FRegister(mips::F5)); + fp_registers_.push_back(new mips::FRegister(mips::F6)); + fp_registers_.push_back(new mips::FRegister(mips::F7)); + fp_registers_.push_back(new mips::FRegister(mips::F8)); + fp_registers_.push_back(new mips::FRegister(mips::F9)); + fp_registers_.push_back(new mips::FRegister(mips::F10)); + fp_registers_.push_back(new mips::FRegister(mips::F11)); + fp_registers_.push_back(new mips::FRegister(mips::F12)); + fp_registers_.push_back(new mips::FRegister(mips::F13)); + fp_registers_.push_back(new mips::FRegister(mips::F14)); + fp_registers_.push_back(new mips::FRegister(mips::F15)); + fp_registers_.push_back(new mips::FRegister(mips::F16)); + fp_registers_.push_back(new mips::FRegister(mips::F17)); + fp_registers_.push_back(new mips::FRegister(mips::F18)); + fp_registers_.push_back(new mips::FRegister(mips::F19)); + fp_registers_.push_back(new mips::FRegister(mips::F20)); + fp_registers_.push_back(new mips::FRegister(mips::F21)); + fp_registers_.push_back(new mips::FRegister(mips::F22)); + fp_registers_.push_back(new mips::FRegister(mips::F23)); + fp_registers_.push_back(new mips::FRegister(mips::F24)); + fp_registers_.push_back(new mips::FRegister(mips::F25)); + fp_registers_.push_back(new mips::FRegister(mips::F26)); + fp_registers_.push_back(new mips::FRegister(mips::F27)); + fp_registers_.push_back(new mips::FRegister(mips::F28)); + fp_registers_.push_back(new mips::FRegister(mips::F29)); + fp_registers_.push_back(new mips::FRegister(mips::F30)); + fp_registers_.push_back(new mips::FRegister(mips::F31)); + } + } + + void TearDown() OVERRIDE { + AssemblerTest::TearDown(); + STLDeleteElements(®isters_); + STLDeleteElements(&fp_registers_); + } + + std::vector<mips::Register*> GetRegisters() OVERRIDE { + return registers_; + } + + std::vector<mips::FRegister*> GetFPRegisters() OVERRIDE { + return fp_registers_; + } + + uint32_t CreateImmediate(int64_t imm_value) OVERRIDE { + return imm_value; + } + + std::string GetSecondaryRegisterName(const mips::Register& reg) OVERRIDE { + CHECK(secondary_register_names_.find(reg) != secondary_register_names_.end()); + return secondary_register_names_[reg]; + } + + std::string RepeatInsn(size_t count, const std::string& insn) { + std::string result; + for (; count != 0u; --count) { + result += insn; + } + return result; + } + + void BranchCondTwoRegsHelper(void (mips::MipsAssembler::*f)(mips::Register, + mips::Register, + mips::MipsLabel*), + std::string instr_name) { + mips::MipsLabel label; + (Base::GetAssembler()->*f)(mips::A0, mips::A1, &label); + constexpr size_t kAdduCount1 = 63; + for (size_t i = 0; i != kAdduCount1; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + __ Bind(&label); + constexpr size_t kAdduCount2 = 64; + for (size_t i = 0; i != kAdduCount2; ++i) { + __ Addu(mips::ZERO, mips::ZERO, mips::ZERO); + } + (Base::GetAssembler()->*f)(mips::A2, mips::A3, &label); + + std::string expected = + ".set noreorder\n" + + instr_name + " $a0, $a1, 1f\n" + "nop\n" + + RepeatInsn(kAdduCount1, "addu $zero, $zero, $zero\n") + + "1:\n" + + RepeatInsn(kAdduCount2, "addu $zero, $zero, $zero\n") + + instr_name + " $a2, $a3, 1b\n" + "nop\n"; + DriverStr(expected, instr_name); + } + + private: + std::vector<mips::Register*> registers_; + std::map<mips::Register, std::string, MIPSCpuRegisterCompare> secondary_register_names_; + + std::vector<mips::FRegister*> fp_registers_; + std::unique_ptr<const MipsInstructionSetFeatures> instruction_set_features_; +}; + + +TEST_F(AssemblerMIPS32r6Test, Toolchain) { + EXPECT_TRUE(CheckTools()); +} + +TEST_F(AssemblerMIPS32r6Test, MulR6) { + DriverStr(RepeatRRR(&mips::MipsAssembler::MulR6, "mul ${reg1}, ${reg2}, ${reg3}"), "MulR6"); +} + +TEST_F(AssemblerMIPS32r6Test, MuhR6) { + DriverStr(RepeatRRR(&mips::MipsAssembler::MuhR6, "muh ${reg1}, ${reg2}, ${reg3}"), "MuhR6"); +} + +TEST_F(AssemblerMIPS32r6Test, MuhuR6) { + DriverStr(RepeatRRR(&mips::MipsAssembler::MuhuR6, "muhu ${reg1}, ${reg2}, ${reg3}"), "MuhuR6"); +} + +TEST_F(AssemblerMIPS32r6Test, DivR6) { + DriverStr(RepeatRRR(&mips::MipsAssembler::DivR6, "div ${reg1}, ${reg2}, ${reg3}"), "DivR6"); +} + +TEST_F(AssemblerMIPS32r6Test, ModR6) { + DriverStr(RepeatRRR(&mips::MipsAssembler::ModR6, "mod ${reg1}, ${reg2}, ${reg3}"), "ModR6"); +} + +TEST_F(AssemblerMIPS32r6Test, DivuR6) { + DriverStr(RepeatRRR(&mips::MipsAssembler::DivuR6, "divu ${reg1}, ${reg2}, ${reg3}"), "DivuR6"); +} + +TEST_F(AssemblerMIPS32r6Test, ModuR6) { + DriverStr(RepeatRRR(&mips::MipsAssembler::ModuR6, "modu ${reg1}, ${reg2}, ${reg3}"), "ModuR6"); +} + +////////// +// MISC // +////////// + +TEST_F(AssemblerMIPS32r6Test, Aui) { + DriverStr(RepeatRRIb(&mips::MipsAssembler::Aui, 16, "aui ${reg1}, ${reg2}, {imm}"), "Aui"); +} + +TEST_F(AssemblerMIPS32r6Test, Bitswap) { + DriverStr(RepeatRR(&mips::MipsAssembler::Bitswap, "bitswap ${reg1}, ${reg2}"), "bitswap"); +} + +TEST_F(AssemblerMIPS32r6Test, Seleqz) { + DriverStr(RepeatRRR(&mips::MipsAssembler::Seleqz, "seleqz ${reg1}, ${reg2}, ${reg3}"), + "seleqz"); +} + +TEST_F(AssemblerMIPS32r6Test, Selnez) { + DriverStr(RepeatRRR(&mips::MipsAssembler::Selnez, "selnez ${reg1}, ${reg2}, ${reg3}"), + "selnez"); +} + +TEST_F(AssemblerMIPS32r6Test, ClzR6) { + DriverStr(RepeatRR(&mips::MipsAssembler::ClzR6, "clz ${reg1}, ${reg2}"), "clzR6"); +} + +TEST_F(AssemblerMIPS32r6Test, CloR6) { + DriverStr(RepeatRR(&mips::MipsAssembler::CloR6, "clo ${reg1}, ${reg2}"), "cloR6"); +} + +//////////////////// +// FLOATING POINT // +//////////////////// + +TEST_F(AssemblerMIPS32r6Test, SelS) { + DriverStr(RepeatFFF(&mips::MipsAssembler::SelS, "sel.s ${reg1}, ${reg2}, ${reg3}"), "sel.s"); +} + +TEST_F(AssemblerMIPS32r6Test, SelD) { + DriverStr(RepeatFFF(&mips::MipsAssembler::SelD, "sel.d ${reg1}, ${reg2}, ${reg3}"), "sel.d"); +} + +TEST_F(AssemblerMIPS32r6Test, ClassS) { + DriverStr(RepeatFF(&mips::MipsAssembler::ClassS, "class.s ${reg1}, ${reg2}"), "class.s"); +} + +TEST_F(AssemblerMIPS32r6Test, ClassD) { + DriverStr(RepeatFF(&mips::MipsAssembler::ClassD, "class.d ${reg1}, ${reg2}"), "class.d"); +} + +TEST_F(AssemblerMIPS32r6Test, MinS) { + DriverStr(RepeatFFF(&mips::MipsAssembler::MinS, "min.s ${reg1}, ${reg2}, ${reg3}"), "min.s"); +} + +TEST_F(AssemblerMIPS32r6Test, MinD) { + DriverStr(RepeatFFF(&mips::MipsAssembler::MinD, "min.d ${reg1}, ${reg2}, ${reg3}"), "min.d"); +} + +TEST_F(AssemblerMIPS32r6Test, MaxS) { + DriverStr(RepeatFFF(&mips::MipsAssembler::MaxS, "max.s ${reg1}, ${reg2}, ${reg3}"), "max.s"); +} + +TEST_F(AssemblerMIPS32r6Test, MaxD) { + DriverStr(RepeatFFF(&mips::MipsAssembler::MaxD, "max.d ${reg1}, ${reg2}, ${reg3}"), "max.d"); +} + +TEST_F(AssemblerMIPS32r6Test, CmpUnS) { + DriverStr(RepeatFFF(&mips::MipsAssembler::CmpUnS, "cmp.un.s ${reg1}, ${reg2}, ${reg3}"), + "cmp.un.s"); +} + +TEST_F(AssemblerMIPS32r6Test, CmpEqS) { + DriverStr(RepeatFFF(&mips::MipsAssembler::CmpEqS, "cmp.eq.s ${reg1}, ${reg2}, ${reg3}"), + "cmp.eq.s"); +} + +TEST_F(AssemblerMIPS32r6Test, CmpUeqS) { + DriverStr(RepeatFFF(&mips::MipsAssembler::CmpUeqS, "cmp.ueq.s ${reg1}, ${reg2}, ${reg3}"), + "cmp.ueq.s"); +} + +TEST_F(AssemblerMIPS32r6Test, CmpLtS) { + DriverStr(RepeatFFF(&mips::MipsAssembler::CmpLtS, "cmp.lt.s ${reg1}, ${reg2}, ${reg3}"), + "cmp.lt.s"); +} + +TEST_F(AssemblerMIPS32r6Test, CmpUltS) { + DriverStr(RepeatFFF(&mips::MipsAssembler::CmpUltS, "cmp.ult.s ${reg1}, ${reg2}, ${reg3}"), + "cmp.ult.s"); +} + +TEST_F(AssemblerMIPS32r6Test, CmpLeS) { + DriverStr(RepeatFFF(&mips::MipsAssembler::CmpLeS, "cmp.le.s ${reg1}, ${reg2}, ${reg3}"), + "cmp.le.s"); +} + +TEST_F(AssemblerMIPS32r6Test, CmpUleS) { + DriverStr(RepeatFFF(&mips::MipsAssembler::CmpUleS, "cmp.ule.s ${reg1}, ${reg2}, ${reg3}"), + "cmp.ule.s"); +} + +TEST_F(AssemblerMIPS32r6Test, CmpOrS) { + DriverStr(RepeatFFF(&mips::MipsAssembler::CmpOrS, "cmp.or.s ${reg1}, ${reg2}, ${reg3}"), + "cmp.or.s"); +} + +TEST_F(AssemblerMIPS32r6Test, CmpUneS) { + DriverStr(RepeatFFF(&mips::MipsAssembler::CmpUneS, "cmp.une.s ${reg1}, ${reg2}, ${reg3}"), + "cmp.une.s"); +} + +TEST_F(AssemblerMIPS32r6Test, CmpNeS) { + DriverStr(RepeatFFF(&mips::MipsAssembler::CmpNeS, "cmp.ne.s ${reg1}, ${reg2}, ${reg3}"), + "cmp.ne.s"); +} + +TEST_F(AssemblerMIPS32r6Test, CmpUnD) { + DriverStr(RepeatFFF(&mips::MipsAssembler::CmpUnD, "cmp.un.d ${reg1}, ${reg2}, ${reg3}"), + "cmp.un.d"); +} + +TEST_F(AssemblerMIPS32r6Test, CmpEqD) { + DriverStr(RepeatFFF(&mips::MipsAssembler::CmpEqD, "cmp.eq.d ${reg1}, ${reg2}, ${reg3}"), + "cmp.eq.d"); +} + +TEST_F(AssemblerMIPS32r6Test, CmpUeqD) { + DriverStr(RepeatFFF(&mips::MipsAssembler::CmpUeqD, "cmp.ueq.d ${reg1}, ${reg2}, ${reg3}"), + "cmp.ueq.d"); +} + +TEST_F(AssemblerMIPS32r6Test, CmpLtD) { + DriverStr(RepeatFFF(&mips::MipsAssembler::CmpLtD, "cmp.lt.d ${reg1}, ${reg2}, ${reg3}"), + "cmp.lt.d"); +} + +TEST_F(AssemblerMIPS32r6Test, CmpUltD) { + DriverStr(RepeatFFF(&mips::MipsAssembler::CmpUltD, "cmp.ult.d ${reg1}, ${reg2}, ${reg3}"), + "cmp.ult.d"); +} + +TEST_F(AssemblerMIPS32r6Test, CmpLeD) { + DriverStr(RepeatFFF(&mips::MipsAssembler::CmpLeD, "cmp.le.d ${reg1}, ${reg2}, ${reg3}"), + "cmp.le.d"); +} + +TEST_F(AssemblerMIPS32r6Test, CmpUleD) { + DriverStr(RepeatFFF(&mips::MipsAssembler::CmpUleD, "cmp.ule.d ${reg1}, ${reg2}, ${reg3}"), + "cmp.ule.d"); +} + +TEST_F(AssemblerMIPS32r6Test, CmpOrD) { + DriverStr(RepeatFFF(&mips::MipsAssembler::CmpOrD, "cmp.or.d ${reg1}, ${reg2}, ${reg3}"), + "cmp.or.d"); +} + +TEST_F(AssemblerMIPS32r6Test, CmpUneD) { + DriverStr(RepeatFFF(&mips::MipsAssembler::CmpUneD, "cmp.une.d ${reg1}, ${reg2}, ${reg3}"), + "cmp.une.d"); +} + +TEST_F(AssemblerMIPS32r6Test, CmpNeD) { + DriverStr(RepeatFFF(&mips::MipsAssembler::CmpNeD, "cmp.ne.d ${reg1}, ${reg2}, ${reg3}"), + "cmp.ne.d"); +} + +TEST_F(AssemblerMIPS32r6Test, LoadDFromOffset) { + __ LoadDFromOffset(mips::F0, mips::A0, -0x8000); + __ LoadDFromOffset(mips::F0, mips::A0, +0); + __ LoadDFromOffset(mips::F0, mips::A0, +0x7FF8); + __ LoadDFromOffset(mips::F0, mips::A0, +0x7FFB); + __ LoadDFromOffset(mips::F0, mips::A0, +0x7FFC); + __ LoadDFromOffset(mips::F0, mips::A0, +0x7FFF); + __ LoadDFromOffset(mips::F0, mips::A0, -0xFFF0); + __ LoadDFromOffset(mips::F0, mips::A0, -0x8008); + __ LoadDFromOffset(mips::F0, mips::A0, -0x8001); + __ LoadDFromOffset(mips::F0, mips::A0, +0x8000); + __ LoadDFromOffset(mips::F0, mips::A0, +0xFFF0); + __ LoadDFromOffset(mips::F0, mips::A0, -0x17FE8); + __ LoadDFromOffset(mips::F0, mips::A0, -0x0FFF8); + __ LoadDFromOffset(mips::F0, mips::A0, -0x0FFF1); + __ LoadDFromOffset(mips::F0, mips::A0, +0x0FFF1); + __ LoadDFromOffset(mips::F0, mips::A0, +0x0FFF8); + __ LoadDFromOffset(mips::F0, mips::A0, +0x17FE8); + __ LoadDFromOffset(mips::F0, mips::A0, -0x17FF0); + __ LoadDFromOffset(mips::F0, mips::A0, -0x17FE9); + __ LoadDFromOffset(mips::F0, mips::A0, +0x17FE9); + __ LoadDFromOffset(mips::F0, mips::A0, +0x17FF0); + __ LoadDFromOffset(mips::F0, mips::A0, +0x12345678); + + const char* expected = + "ldc1 $f0, -0x8000($a0)\n" + "ldc1 $f0, 0($a0)\n" + "ldc1 $f0, 0x7FF8($a0)\n" + "lwc1 $f0, 0x7FFB($a0)\n" + "lw $t8, 0x7FFF($a0)\n" + "mthc1 $t8, $f0\n" + "addiu $at, $a0, 0x7FF8\n" + "lwc1 $f0, 4($at)\n" + "lw $t8, 8($at)\n" + "mthc1 $t8, $f0\n" + "addiu $at, $a0, 0x7FF8\n" + "lwc1 $f0, 7($at)\n" + "lw $t8, 11($at)\n" + "mthc1 $t8, $f0\n" + "addiu $at, $a0, -0x7FF8\n" + "ldc1 $f0, -0x7FF8($at)\n" + "addiu $at, $a0, -0x7FF8\n" + "ldc1 $f0, -0x10($at)\n" + "addiu $at, $a0, -0x7FF8\n" + "lwc1 $f0, -9($at)\n" + "lw $t8, -5($at)\n" + "mthc1 $t8, $f0\n" + "addiu $at, $a0, 0x7FF8\n" + "ldc1 $f0, 8($at)\n" + "addiu $at, $a0, 0x7FF8\n" + "ldc1 $f0, 0x7FF8($at)\n" + "aui $at, $a0, 0xFFFF\n" + "ldc1 $f0, -0x7FE8($at)\n" + "aui $at, $a0, 0xFFFF\n" + "ldc1 $f0, 0x8($at)\n" + "aui $at, $a0, 0xFFFF\n" + "lwc1 $f0, 0xF($at)\n" + "lw $t8, 0x13($at)\n" + "mthc1 $t8, $f0\n" + "aui $at, $a0, 0x1\n" + "lwc1 $f0, -0xF($at)\n" + "lw $t8, -0xB($at)\n" + "mthc1 $t8, $f0\n" + "aui $at, $a0, 0x1\n" + "ldc1 $f0, -0x8($at)\n" + "aui $at, $a0, 0x1\n" + "ldc1 $f0, 0x7FE8($at)\n" + "aui $at, $a0, 0xFFFF\n" + "ldc1 $f0, -0x7FF0($at)\n" + "aui $at, $a0, 0xFFFF\n" + "lwc1 $f0, -0x7FE9($at)\n" + "lw $t8, -0x7FE5($at)\n" + "mthc1 $t8, $f0\n" + "aui $at, $a0, 0x1\n" + "lwc1 $f0, 0x7FE9($at)\n" + "lw $t8, 0x7FED($at)\n" + "mthc1 $t8, $f0\n" + "aui $at, $a0, 0x1\n" + "ldc1 $f0, 0x7FF0($at)\n" + "aui $at, $a0, 0x1234\n" + "ldc1 $f0, 0x5678($at)\n"; + DriverStr(expected, "LoadDFromOffset"); +} + +TEST_F(AssemblerMIPS32r6Test, StoreDToOffset) { + __ StoreDToOffset(mips::F0, mips::A0, -0x8000); + __ StoreDToOffset(mips::F0, mips::A0, +0); + __ StoreDToOffset(mips::F0, mips::A0, +0x7FF8); + __ StoreDToOffset(mips::F0, mips::A0, +0x7FFB); + __ StoreDToOffset(mips::F0, mips::A0, +0x7FFC); + __ StoreDToOffset(mips::F0, mips::A0, +0x7FFF); + __ StoreDToOffset(mips::F0, mips::A0, -0xFFF0); + __ StoreDToOffset(mips::F0, mips::A0, -0x8008); + __ StoreDToOffset(mips::F0, mips::A0, -0x8001); + __ StoreDToOffset(mips::F0, mips::A0, +0x8000); + __ StoreDToOffset(mips::F0, mips::A0, +0xFFF0); + __ StoreDToOffset(mips::F0, mips::A0, -0x17FE8); + __ StoreDToOffset(mips::F0, mips::A0, -0x0FFF8); + __ StoreDToOffset(mips::F0, mips::A0, -0x0FFF1); + __ StoreDToOffset(mips::F0, mips::A0, +0x0FFF1); + __ StoreDToOffset(mips::F0, mips::A0, +0x0FFF8); + __ StoreDToOffset(mips::F0, mips::A0, +0x17FE8); + __ StoreDToOffset(mips::F0, mips::A0, -0x17FF0); + __ StoreDToOffset(mips::F0, mips::A0, -0x17FE9); + __ StoreDToOffset(mips::F0, mips::A0, +0x17FE9); + __ StoreDToOffset(mips::F0, mips::A0, +0x17FF0); + __ StoreDToOffset(mips::F0, mips::A0, +0x12345678); + + const char* expected = + "sdc1 $f0, -0x8000($a0)\n" + "sdc1 $f0, 0($a0)\n" + "sdc1 $f0, 0x7FF8($a0)\n" + "mfhc1 $t8, $f0\n" + "swc1 $f0, 0x7FFB($a0)\n" + "sw $t8, 0x7FFF($a0)\n" + "addiu $at, $a0, 0x7FF8\n" + "mfhc1 $t8, $f0\n" + "swc1 $f0, 4($at)\n" + "sw $t8, 8($at)\n" + "addiu $at, $a0, 0x7FF8\n" + "mfhc1 $t8, $f0\n" + "swc1 $f0, 7($at)\n" + "sw $t8, 11($at)\n" + "addiu $at, $a0, -0x7FF8\n" + "sdc1 $f0, -0x7FF8($at)\n" + "addiu $at, $a0, -0x7FF8\n" + "sdc1 $f0, -0x10($at)\n" + "addiu $at, $a0, -0x7FF8\n" + "mfhc1 $t8, $f0\n" + "swc1 $f0, -9($at)\n" + "sw $t8, -5($at)\n" + "addiu $at, $a0, 0x7FF8\n" + "sdc1 $f0, 8($at)\n" + "addiu $at, $a0, 0x7FF8\n" + "sdc1 $f0, 0x7FF8($at)\n" + "aui $at, $a0, 0xFFFF\n" + "sdc1 $f0, -0x7FE8($at)\n" + "aui $at, $a0, 0xFFFF\n" + "sdc1 $f0, 0x8($at)\n" + "aui $at, $a0, 0xFFFF\n" + "mfhc1 $t8, $f0\n" + "swc1 $f0, 0xF($at)\n" + "sw $t8, 0x13($at)\n" + "aui $at, $a0, 0x1\n" + "mfhc1 $t8, $f0\n" + "swc1 $f0, -0xF($at)\n" + "sw $t8, -0xB($at)\n" + "aui $at, $a0, 0x1\n" + "sdc1 $f0, -0x8($at)\n" + "aui $at, $a0, 0x1\n" + "sdc1 $f0, 0x7FE8($at)\n" + "aui $at, $a0, 0xFFFF\n" + "sdc1 $f0, -0x7FF0($at)\n" + "aui $at, $a0, 0xFFFF\n" + "mfhc1 $t8, $f0\n" + "swc1 $f0, -0x7FE9($at)\n" + "sw $t8, -0x7FE5($at)\n" + "aui $at, $a0, 0x1\n" + "mfhc1 $t8, $f0\n" + "swc1 $f0, 0x7FE9($at)\n" + "sw $t8, 0x7FED($at)\n" + "aui $at, $a0, 0x1\n" + "sdc1 $f0, 0x7FF0($at)\n" + "aui $at, $a0, 0x1234\n" + "sdc1 $f0, 0x5678($at)\n"; + DriverStr(expected, "StoreDToOffset"); +} + +////////////// +// BRANCHES // +////////////// + +// TODO: MipsAssembler::Auipc +// MipsAssembler::Addiupc +// MipsAssembler::Bc +// MipsAssembler::Jic +// MipsAssembler::Jialc +// MipsAssembler::Bltc +// MipsAssembler::Bltzc +// MipsAssembler::Bgtzc +// MipsAssembler::Bgec +// MipsAssembler::Bgezc +// MipsAssembler::Blezc +// MipsAssembler::Bltuc +// MipsAssembler::Bgeuc +// MipsAssembler::Beqc +// MipsAssembler::Bnec +// MipsAssembler::Beqzc +// MipsAssembler::Bnezc +// MipsAssembler::Bc1eqz +// MipsAssembler::Bc1nez +// MipsAssembler::Buncond +// MipsAssembler::Bcond +// MipsAssembler::Call + +// TODO: AssemblerMIPS32r6Test.B +// AssemblerMIPS32r6Test.Beq +// AssemblerMIPS32r6Test.Bne +// AssemblerMIPS32r6Test.Beqz +// AssemblerMIPS32r6Test.Bnez +// AssemblerMIPS32r6Test.Bltz +// AssemblerMIPS32r6Test.Bgez +// AssemblerMIPS32r6Test.Blez +// AssemblerMIPS32r6Test.Bgtz +// AssemblerMIPS32r6Test.Blt +// AssemblerMIPS32r6Test.Bge +// AssemblerMIPS32r6Test.Bltu +// AssemblerMIPS32r6Test.Bgeu + +#undef __ + +} // namespace art diff --git a/compiler/utils/mips/assembler_mips_test.cc b/compiler/utils/mips/assembler_mips_test.cc index 57d51835f0..c722d0c333 100644 --- a/compiler/utils/mips/assembler_mips_test.cc +++ b/compiler/utils/mips/assembler_mips_test.cc @@ -2228,6 +2228,51 @@ TEST_F(AssemblerMIPSTest, Bc1t) { DriverStr(expected, "Bc1t"); } +/////////////////////// +// Loading Constants // +/////////////////////// + +TEST_F(AssemblerMIPSTest, LoadConst32) { + // IsUint<16>(value) + __ LoadConst32(mips::V0, 0); + __ LoadConst32(mips::V0, 65535); + // IsInt<16>(value) + __ LoadConst32(mips::V0, -1); + __ LoadConst32(mips::V0, -32768); + // Everything else + __ LoadConst32(mips::V0, 65536); + __ LoadConst32(mips::V0, 65537); + __ LoadConst32(mips::V0, 2147483647); + __ LoadConst32(mips::V0, -32769); + __ LoadConst32(mips::V0, -65536); + __ LoadConst32(mips::V0, -65537); + __ LoadConst32(mips::V0, -2147483647); + __ LoadConst32(mips::V0, -2147483648); + + const char* expected = + // IsUint<16>(value) + "ori $v0, $zero, 0\n" // __ LoadConst32(mips::V0, 0); + "ori $v0, $zero, 65535\n" // __ LoadConst32(mips::V0, 65535); + // IsInt<16>(value) + "addiu $v0, $zero, -1\n" // __ LoadConst32(mips::V0, -1); + "addiu $v0, $zero, -32768\n" // __ LoadConst32(mips::V0, -32768); + // Everything else + "lui $v0, 1\n" // __ LoadConst32(mips::V0, 65536); + "lui $v0, 1\n" // __ LoadConst32(mips::V0, 65537); + "ori $v0, 1\n" // " + "lui $v0, 32767\n" // __ LoadConst32(mips::V0, 2147483647); + "ori $v0, 65535\n" // " + "lui $v0, 65535\n" // __ LoadConst32(mips::V0, -32769); + "ori $v0, 32767\n" // " + "lui $v0, 65535\n" // __ LoadConst32(mips::V0, -65536); + "lui $v0, 65534\n" // __ LoadConst32(mips::V0, -65537); + "ori $v0, 65535\n" // " + "lui $v0, 32768\n" // __ LoadConst32(mips::V0, -2147483647); + "ori $v0, 1\n" // " + "lui $v0, 32768\n"; // __ LoadConst32(mips::V0, -2147483648); + DriverStr(expected, "LoadConst32"); +} + #undef __ } // namespace art diff --git a/compiler/utils/string_reference.h b/compiler/utils/string_reference.h index 72552f21aa..e4c34ca605 100644 --- a/compiler/utils/string_reference.h +++ b/compiler/utils/string_reference.h @@ -20,16 +20,19 @@ #include <stdint.h> #include "base/logging.h" +#include "dex_file-inl.h" #include "utf-inl.h" namespace art { -class DexFile; - -// A string is uniquely located by its DexFile and the string_ids_ table index into that DexFile. +// A string is located by its DexFile and the string_ids_ table index into that DexFile. struct StringReference { StringReference(const DexFile* file, uint32_t index) : dex_file(file), string_index(index) { } + const char* GetStringData() const { + return dex_file->GetStringData(dex_file->GetStringId(string_index)); + } + const DexFile* dex_file; uint32_t string_index; }; @@ -46,15 +49,13 @@ struct StringReferenceValueComparator { // Use the string order enforced by the dex file verifier. DCHECK_EQ( sr1.string_index < sr2.string_index, - CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues( - sr1.dex_file->GetStringData(sr1.dex_file->GetStringId(sr1.string_index)), - sr1.dex_file->GetStringData(sr2.dex_file->GetStringId(sr2.string_index))) < 0); + CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(sr1.GetStringData(), + sr2.GetStringData()) < 0); return sr1.string_index < sr2.string_index; } else { // Cannot compare indexes, so do the string comparison. - return CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues( - sr1.dex_file->GetStringData(sr1.dex_file->GetStringId(sr1.string_index)), - sr1.dex_file->GetStringData(sr2.dex_file->GetStringId(sr2.string_index))) < 0; + return CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(sr1.GetStringData(), + sr2.GetStringData()) < 0; } } }; diff --git a/compiler/utils/string_reference_test.cc b/compiler/utils/string_reference_test.cc new file mode 100644 index 0000000000..df5080e93e --- /dev/null +++ b/compiler/utils/string_reference_test.cc @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2016 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. + */ + +#include "utils/string_reference.h" + +#include <memory> + +#include "gtest/gtest.h" +#include "utils/test_dex_file_builder.h" + +namespace art { + +TEST(StringReference, ValueComparator) { + // This is a regression test for the StringReferenceValueComparator using the wrong + // dex file to get the string data from a StringId. We construct two dex files with + // just a single string with the same length but different value. This creates dex + // files that have the same layout, so the byte offset read from the StringId in one + // dex file, when used in the other dex file still points to valid string data, except + // that it's the wrong string. Without the fix the strings would then compare equal. + TestDexFileBuilder builder1; + builder1.AddString("String1"); + std::unique_ptr<const DexFile> dex_file1 = builder1.Build("dummy location 1"); + ASSERT_EQ(1u, dex_file1->NumStringIds()); + ASSERT_STREQ("String1", dex_file1->GetStringData(dex_file1->GetStringId(0))); + StringReference sr1(dex_file1.get(), 0); + + TestDexFileBuilder builder2; + builder2.AddString("String2"); + std::unique_ptr<const DexFile> dex_file2 = builder2.Build("dummy location 2"); + ASSERT_EQ(1u, dex_file2->NumStringIds()); + ASSERT_STREQ("String2", dex_file2->GetStringData(dex_file2->GetStringId(0))); + StringReference sr2(dex_file2.get(), 0); + + StringReferenceValueComparator cmp; + EXPECT_TRUE(cmp(sr1, sr2)); // "String1" < "String2" is true. + EXPECT_FALSE(cmp(sr2, sr1)); // "String2" < "String1" is false. +} + +TEST(StringReference, ValueComparator2) { + const char* const kDexFile1Strings[] = { + "", + "abc", + "abcxyz", + }; + const char* const kDexFile2Strings[] = { + "a", + "abc", + "abcdef", + "def", + }; + const bool expectedCmp12[arraysize(kDexFile1Strings)][arraysize(kDexFile2Strings)] = { + { true, true, true, true }, + { false, false, true, true }, + { false, false, false, true }, + }; + const bool expectedCmp21[arraysize(kDexFile2Strings)][arraysize(kDexFile1Strings)] = { + { false, true, true }, + { false, false, true }, + { false, false, true }, + { false, false, false }, + }; + + TestDexFileBuilder builder1; + for (const char* s : kDexFile1Strings) { + builder1.AddString(s); + } + std::unique_ptr<const DexFile> dex_file1 = builder1.Build("dummy location 1"); + ASSERT_EQ(arraysize(kDexFile1Strings), dex_file1->NumStringIds()); + for (size_t index = 0; index != arraysize(kDexFile1Strings); ++index) { + ASSERT_STREQ(kDexFile1Strings[index], dex_file1->GetStringData(dex_file1->GetStringId(index))); + } + + TestDexFileBuilder builder2; + for (const char* s : kDexFile2Strings) { + builder2.AddString(s); + } + std::unique_ptr<const DexFile> dex_file2 = builder2.Build("dummy location 1"); + ASSERT_EQ(arraysize(kDexFile2Strings), dex_file2->NumStringIds()); + for (size_t index = 0; index != arraysize(kDexFile2Strings); ++index) { + ASSERT_STREQ(kDexFile2Strings[index], dex_file2->GetStringData(dex_file2->GetStringId(index))); + } + + StringReferenceValueComparator cmp; + for (size_t index1 = 0; index1 != arraysize(kDexFile1Strings); ++index1) { + for (size_t index2 = 0; index2 != arraysize(kDexFile2Strings); ++index2) { + StringReference sr1(dex_file1.get(), index1); + StringReference sr2(dex_file2.get(), index2); + EXPECT_EQ(expectedCmp12[index1][index2], cmp(sr1, sr2)) << index1 << " " << index2; + EXPECT_EQ(expectedCmp21[index2][index1], cmp(sr2, sr1)) << index1 << " " << index2; + } + } +} + +} // namespace art diff --git a/compiler/utils/test_dex_file_builder.h b/compiler/utils/test_dex_file_builder.h index fb37804649..6921780a85 100644 --- a/compiler/utils/test_dex_file_builder.h +++ b/compiler/utils/test_dex_file_builder.h @@ -227,9 +227,18 @@ class TestDexFileBuilder { // Write the complete header again, just simpler that way. std::memcpy(&dex_file_data_[0], header_data.data, sizeof(DexFile::Header)); + static constexpr bool kVerify = false; + static constexpr bool kVerifyChecksum = false; std::string error_msg; std::unique_ptr<const DexFile> dex_file(DexFile::Open( - &dex_file_data_[0], dex_file_data_.size(), dex_location, 0u, nullptr, false, &error_msg)); + &dex_file_data_[0], + dex_file_data_.size(), + dex_location, + 0u, + nullptr, + kVerify, + kVerifyChecksum, + &error_msg)); CHECK(dex_file != nullptr) << error_msg; return dex_file; } diff --git a/compiler/utils/type_reference.h b/compiler/utils/type_reference.h new file mode 100644 index 0000000000..bd0739fc98 --- /dev/null +++ b/compiler/utils/type_reference.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2016 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. + */ + +#ifndef ART_COMPILER_UTILS_TYPE_REFERENCE_H_ +#define ART_COMPILER_UTILS_TYPE_REFERENCE_H_ + +#include <stdint.h> + +#include "base/logging.h" +#include "utils/string_reference.h" + +namespace art { + +class DexFile; + +// A type is located by its DexFile and the string_ids_ table index into that DexFile. +struct TypeReference { + TypeReference(const DexFile* file, uint32_t index) : dex_file(file), type_index(index) { } + + const DexFile* dex_file; + uint32_t type_index; +}; + +// Compare the actual referenced type names. Used for type reference deduplication. +struct TypeReferenceValueComparator { + bool operator()(TypeReference tr1, TypeReference tr2) const { + // Note that we want to deduplicate identical boot image types even if they are + // referenced by different dex files, so we simply compare the descriptors. + StringReference sr1(tr1.dex_file, tr1.dex_file->GetTypeId(tr1.type_index).descriptor_idx_); + StringReference sr2(tr2.dex_file, tr2.dex_file->GetTypeId(tr2.type_index).descriptor_idx_); + return StringReferenceValueComparator()(sr1, sr2); + } +}; + +} // namespace art + +#endif // ART_COMPILER_UTILS_TYPE_REFERENCE_H_ diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 2dce2f1d47..24a4d586a2 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1985,8 +1985,10 @@ class Dex2Oat FINAL { if (location == OatFile::kSpecialSharedLibrary) { break; } + static constexpr bool kVerifyChecksum = true; std::string error_msg; - if (!DexFile::Open(location.c_str(), location.c_str(), &error_msg, opened_dex_files)) { + if (!DexFile::Open( + location.c_str(), location.c_str(), kVerifyChecksum, &error_msg, opened_dex_files)) { // If we fail to open the dex file because it's been stripped, try to open the dex file // from its corresponding oat file. OatFileAssistant oat_file_assistant(location.c_str(), isa, false, false); diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc index 1a2f2c2bc3..48b773e90b 100644 --- a/dexdump/dexdump.cc +++ b/dexdump/dexdump.cc @@ -17,8 +17,8 @@ * * This is a re-implementation of the original dexdump utility that was * based on Dalvik functions in libdex into a new dexdump that is now - * based on Art functions in libart instead. The output is identical to - * the original for correct DEX files. Error messages may differ, however. + * based on Art functions in libart instead. The output is very similar to + * to the original for correct DEX files. Error messages may differ, however. * Also, ODEX files are no longer supported. * * The dexdump tool is intended to mimic objdump. When possible, use @@ -65,6 +65,8 @@ typedef uint8_t u1; typedef uint16_t u2; typedef uint32_t u4; typedef uint64_t u8; +typedef int8_t s1; +typedef int16_t s2; typedef int32_t s4; typedef int64_t s8; @@ -187,6 +189,13 @@ static char* descriptorClassToDot(const char* str) { } /* + * Returns string representing the boolean value. + */ +static const char* strBool(bool val) { + return val ? "true" : "false"; +} + +/* * Returns a quoted string representing the boolean value. */ static const char* quotedBool(bool val) { @@ -346,10 +355,197 @@ static void asciify(char* out, const unsigned char* data, size_t len) { } /* + * Dumps a string value with some escape characters. + */ +static void dumpEscapedString(const char* p) { + fputs("\"", gOutFile); + for (; *p; p++) { + switch (*p) { + case '\\': + fputs("\\\\", gOutFile); + break; + case '\"': + fputs("\\\"", gOutFile); + break; + case '\t': + fputs("\\t", gOutFile); + break; + case '\n': + fputs("\\n", gOutFile); + break; + case '\r': + fputs("\\r", gOutFile); + break; + default: + putc(*p, gOutFile); + } // switch + } // for + fputs("\"", gOutFile); +} + +/* + * Dumps a string as an XML attribute value. + */ +static void dumpXmlAttribute(const char* p) { + for (; *p; p++) { + switch (*p) { + case '&': + fputs("&", gOutFile); + break; + case '<': + fputs("<", gOutFile); + break; + case '>': + fputs(">", gOutFile); + break; + case '"': + fputs(""", gOutFile); + break; + case '\t': + fputs("	", gOutFile); + break; + case '\n': + fputs("
", gOutFile); + break; + case '\r': + fputs("
", gOutFile); + break; + default: + putc(*p, gOutFile); + } // switch + } // for +} + +/* + * Reads variable width value, possibly sign extended at the last defined byte. + */ +static u8 readVarWidth(const u1** data, u1 arg, bool sign_extend) { + u8 value = 0; + for (u4 i = 0; i <= arg; i++) { + value |= static_cast<u8>(*(*data)++) << (i * 8); + } + if (sign_extend) { + int shift = (7 - arg) * 8; + return (static_cast<s8>(value) << shift) >> shift; + } + return value; +} + +/* + * Dumps encoded value. + */ +static void dumpEncodedValue(const DexFile* pDexFile, const u1** data); // forward +static void dumpEncodedValue(const DexFile* pDexFile, const u1** data, u1 type, u1 arg) { + switch (type) { + case DexFile::kDexAnnotationByte: + fprintf(gOutFile, "%" PRId8, static_cast<s1>(readVarWidth(data, arg, false))); + break; + case DexFile::kDexAnnotationShort: + fprintf(gOutFile, "%" PRId16, static_cast<s2>(readVarWidth(data, arg, true))); + break; + case DexFile::kDexAnnotationChar: + fprintf(gOutFile, "%" PRIu16, static_cast<u2>(readVarWidth(data, arg, false))); + break; + case DexFile::kDexAnnotationInt: + fprintf(gOutFile, "%" PRId32, static_cast<s4>(readVarWidth(data, arg, true))); + break; + case DexFile::kDexAnnotationLong: + fprintf(gOutFile, "%" PRId64, static_cast<s8>(readVarWidth(data, arg, true))); + break; + case DexFile::kDexAnnotationFloat: { + // Fill on right. + union { + float f; + u4 data; + } conv; + conv.data = static_cast<u4>(readVarWidth(data, arg, false)) << (3 - arg) * 8; + fprintf(gOutFile, "%g", conv.f); + break; + } + case DexFile::kDexAnnotationDouble: { + // Fill on right. + union { + double d; + u8 data; + } conv; + conv.data = readVarWidth(data, arg, false) << (7 - arg) * 8; + fprintf(gOutFile, "%g", conv.d); + break; + } + case DexFile::kDexAnnotationString: { + const u4 idx = static_cast<u4>(readVarWidth(data, arg, false)); + if (gOptions.outputFormat == OUTPUT_PLAIN) { + dumpEscapedString(pDexFile->StringDataByIdx(idx)); + } else { + dumpXmlAttribute(pDexFile->StringDataByIdx(idx)); + } + break; + } + case DexFile::kDexAnnotationType: { + const u4 str_idx = static_cast<u4>(readVarWidth(data, arg, false)); + fputs(pDexFile->StringByTypeIdx(str_idx), gOutFile); + break; + } + case DexFile::kDexAnnotationField: + case DexFile::kDexAnnotationEnum: { + const u4 field_idx = static_cast<u4>(readVarWidth(data, arg, false)); + const DexFile::FieldId& pFieldId = pDexFile->GetFieldId(field_idx); + fputs(pDexFile->StringDataByIdx(pFieldId.name_idx_), gOutFile); + break; + } + case DexFile::kDexAnnotationMethod: { + const u4 method_idx = static_cast<u4>(readVarWidth(data, arg, false)); + const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(method_idx); + fputs(pDexFile->StringDataByIdx(pMethodId.name_idx_), gOutFile); + break; + } + case DexFile::kDexAnnotationArray: { + fputc('{', gOutFile); + // Decode and display all elements. + const u4 size = DecodeUnsignedLeb128(data); + for (u4 i = 0; i < size; i++) { + fputc(' ', gOutFile); + dumpEncodedValue(pDexFile, data); + } + fputs(" }", gOutFile); + break; + } + case DexFile::kDexAnnotationAnnotation: { + const u4 type_idx = DecodeUnsignedLeb128(data); + fputs(pDexFile->StringByTypeIdx(type_idx), gOutFile); + // Decode and display all name=value pairs. + const u4 size = DecodeUnsignedLeb128(data); + for (u4 i = 0; i < size; i++) { + const u4 name_idx = DecodeUnsignedLeb128(data); + fputc(' ', gOutFile); + fputs(pDexFile->StringDataByIdx(name_idx), gOutFile); + fputc('=', gOutFile); + dumpEncodedValue(pDexFile, data); + } + break; + } + case DexFile::kDexAnnotationNull: + fputs("null", gOutFile); + break; + case DexFile::kDexAnnotationBoolean: + fputs(strBool(arg), gOutFile); + break; + default: + fputs("????", gOutFile); + break; + } // switch +} + +/* + * Dumps encoded value with prefix. + */ +static void dumpEncodedValue(const DexFile* pDexFile, const u1** data) { + const u1 enc = *(*data)++; + dumpEncodedValue(pDexFile, data, enc & 0x1f, enc >> 5); +} + +/* * Dumps the file header. - * - * Note that some of the : are misaligned on purpose to preserve - * the exact output of the original Dalvik dexdump. */ static void dumpFileHeader(const DexFile* pDexFile) { const DexFile::Header& pHeader = pDexFile->GetHeader(); @@ -373,8 +569,8 @@ static void dumpFileHeader(const DexFile* pDexFile) { fprintf(gOutFile, "type_ids_size : %d\n", pHeader.type_ids_size_); fprintf(gOutFile, "type_ids_off : %d (0x%06x)\n", pHeader.type_ids_off_, pHeader.type_ids_off_); - fprintf(gOutFile, "proto_ids_size : %d\n", pHeader.proto_ids_size_); - fprintf(gOutFile, "proto_ids_off : %d (0x%06x)\n", + fprintf(gOutFile, "proto_ids_size : %d\n", pHeader.proto_ids_size_); + fprintf(gOutFile, "proto_ids_off : %d (0x%06x)\n", pHeader.proto_ids_off_, pHeader.proto_ids_off_); fprintf(gOutFile, "field_ids_size : %d\n", pHeader.field_ids_size_); fprintf(gOutFile, "field_ids_off : %d (0x%06x)\n", @@ -426,6 +622,99 @@ static void dumpClassDef(const DexFile* pDexFile, int idx) { fprintf(gOutFile, "\n"); } +/** + * Dumps an annotation set item. + */ +static void dumpAnnotationSetItem(const DexFile* pDexFile, const DexFile::AnnotationSetItem* set_item) { + if (set_item == nullptr || set_item->size_ == 0) { + fputs(" empty-annotation-set\n", gOutFile); + return; + } + for (u4 i = 0; i < set_item->size_; i++) { + const DexFile::AnnotationItem* annotation = pDexFile->GetAnnotationItem(set_item, i); + if (annotation == nullptr) { + continue; + } + fputs(" ", gOutFile); + switch (annotation->visibility_) { + case DexFile::kDexVisibilityBuild: fputs("VISIBILITY_BUILD ", gOutFile); break; + case DexFile::kDexVisibilityRuntime: fputs("VISIBILITY_RUNTIME ", gOutFile); break; + case DexFile::kDexVisibilitySystem: fputs("VISIBILITY_SYSTEM ", gOutFile); break; + default: fputs("VISIBILITY_UNKNOWN ", gOutFile); break; + } // switch + // Decode raw bytes in annotation. + const u1* rData = annotation->annotation_; + dumpEncodedValue(pDexFile, &rData, DexFile::kDexAnnotationAnnotation, 0); + fputc('\n', gOutFile); + } +} + +/* + * Dumps class annotations. + */ +static void dumpClassAnnotations(const DexFile* pDexFile, int idx) { + const DexFile::ClassDef& pClassDef = pDexFile->GetClassDef(idx); + const DexFile::AnnotationsDirectoryItem* dir = pDexFile->GetAnnotationsDirectory(pClassDef); + if (dir == nullptr) { + return; // none + } + + fprintf(gOutFile, "Class #%d annotations:\n", idx); + + const DexFile::AnnotationSetItem* class_set_item = pDexFile->GetClassAnnotationSet(dir); + const DexFile::FieldAnnotationsItem* fields = pDexFile->GetFieldAnnotations(dir); + const DexFile::MethodAnnotationsItem* methods = pDexFile->GetMethodAnnotations(dir); + const DexFile::ParameterAnnotationsItem* pars = pDexFile->GetParameterAnnotations(dir); + + // Annotations on the class itself. + if (class_set_item != nullptr) { + fprintf(gOutFile, "Annotations on class\n"); + dumpAnnotationSetItem(pDexFile, class_set_item); + } + + // Annotations on fields. + if (fields != nullptr) { + for (u4 i = 0; i < dir->fields_size_; i++) { + const u4 field_idx = fields[i].field_idx_; + const DexFile::FieldId& pFieldId = pDexFile->GetFieldId(field_idx); + const char* field_name = pDexFile->StringDataByIdx(pFieldId.name_idx_); + fprintf(gOutFile, "Annotations on field #%u '%s'\n", field_idx, field_name); + dumpAnnotationSetItem(pDexFile, pDexFile->GetFieldAnnotationSetItem(fields[i])); + } + } + + // Annotations on methods. + if (methods != nullptr) { + for (u4 i = 0; i < dir->methods_size_; i++) { + const u4 method_idx = methods[i].method_idx_; + const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(method_idx); + const char* method_name = pDexFile->StringDataByIdx(pMethodId.name_idx_); + fprintf(gOutFile, "Annotations on method #%u '%s'\n", method_idx, method_name); + dumpAnnotationSetItem(pDexFile, pDexFile->GetMethodAnnotationSetItem(methods[i])); + } + } + + // Annotations on method parameters. + if (pars != nullptr) { + for (u4 i = 0; i < dir->parameters_size_; i++) { + const u4 method_idx = pars[i].method_idx_; + const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(method_idx); + const char* method_name = pDexFile->StringDataByIdx(pMethodId.name_idx_); + fprintf(gOutFile, "Annotations on method #%u '%s' parameters\n", method_idx, method_name); + const DexFile::AnnotationSetRefList* + list = pDexFile->GetParameterAnnotationSetRefList(&pars[i]); + if (list != nullptr) { + for (u4 j = 0; j < list->size_; j++) { + fprintf(gOutFile, "#%u\n", j); + dumpAnnotationSetItem(pDexFile, pDexFile->GetSetRefItemItem(&list->list_[j])); + } + } + } + } + + fputc('\n', gOutFile); +} + /* * Dumps an interface that a class declares to implement. */ @@ -677,27 +966,25 @@ static void dumpInstruction(const DexFile* pDexFile, fprintf(gOutFile, " v%d", pDecInsn->VRegA()); break; case Instruction::k10t: // op +AA - case Instruction::k20t: // op +AAAA - { - const s4 targ = (s4) pDecInsn->VRegA(); - fprintf(gOutFile, " %04x // %c%04x", - insnIdx + targ, - (targ < 0) ? '-' : '+', - (targ < 0) ? -targ : targ); - } + case Instruction::k20t: { // op +AAAA + const s4 targ = (s4) pDecInsn->VRegA(); + fprintf(gOutFile, " %04x // %c%04x", + insnIdx + targ, + (targ < 0) ? '-' : '+', + (targ < 0) ? -targ : targ); break; + } case Instruction::k22x: // op vAA, vBBBB fprintf(gOutFile, " v%d, v%d", pDecInsn->VRegA(), pDecInsn->VRegB()); break; - case Instruction::k21t: // op vAA, +BBBB - { - const s4 targ = (s4) pDecInsn->VRegB(); - fprintf(gOutFile, " v%d, %04x // %c%04x", pDecInsn->VRegA(), - insnIdx + targ, - (targ < 0) ? '-' : '+', - (targ < 0) ? -targ : targ); - } + case Instruction::k21t: { // op vAA, +BBBB + const s4 targ = (s4) pDecInsn->VRegB(); + fprintf(gOutFile, " v%d, %04x // %c%04x", pDecInsn->VRegA(), + insnIdx + targ, + (targ < 0) ? '-' : '+', + (targ < 0) ? -targ : targ); break; + } case Instruction::k21s: // op vAA, #+BBBB fprintf(gOutFile, " v%d, #int %d // #%x", pDecInsn->VRegA(), (s4) pDecInsn->VRegB(), (u2)pDecInsn->VRegB()); @@ -727,16 +1014,15 @@ static void dumpInstruction(const DexFile* pDexFile, pDecInsn->VRegA(), pDecInsn->VRegB(), (s4) pDecInsn->VRegC(), (u1) pDecInsn->VRegC()); break; - case Instruction::k22t: // op vA, vB, +CCCC - { - const s4 targ = (s4) pDecInsn->VRegC(); - fprintf(gOutFile, " v%d, v%d, %04x // %c%04x", - pDecInsn->VRegA(), pDecInsn->VRegB(), - insnIdx + targ, - (targ < 0) ? '-' : '+', - (targ < 0) ? -targ : targ); - } + case Instruction::k22t: { // op vA, vB, +CCCC + const s4 targ = (s4) pDecInsn->VRegC(); + fprintf(gOutFile, " v%d, v%d, %04x // %c%04x", + pDecInsn->VRegA(), pDecInsn->VRegB(), + insnIdx + targ, + (targ < 0) ? '-' : '+', + (targ < 0) ? -targ : targ); break; + } case Instruction::k22s: // op vA, vB, #+CCCC fprintf(gOutFile, " v%d, v%d, #int %d // #%04x", pDecInsn->VRegA(), pDecInsn->VRegB(), @@ -751,18 +1037,17 @@ static void dumpInstruction(const DexFile* pDexFile, case Instruction::k30t: fprintf(gOutFile, " #%08x", pDecInsn->VRegA()); break; - case Instruction::k31i: // op vAA, #+BBBBBBBB - { - // This is often, but not always, a float. - union { - float f; - u4 i; - } conv; - conv.i = pDecInsn->VRegB(); - fprintf(gOutFile, " v%d, #float %f // #%08x", - pDecInsn->VRegA(), conv.f, pDecInsn->VRegB()); - } + case Instruction::k31i: { // op vAA, #+BBBBBBBB + // This is often, but not always, a float. + union { + float f; + u4 i; + } conv; + conv.i = pDecInsn->VRegB(); + fprintf(gOutFile, " v%d, #float %g // #%08x", + pDecInsn->VRegA(), conv.f, pDecInsn->VRegB()); break; + } case Instruction::k31t: // op vAA, offset +BBBBBBBB fprintf(gOutFile, " v%d, %08x // +%08x", pDecInsn->VRegA(), insnIdx + pDecInsn->VRegB(), pDecInsn->VRegB()); @@ -770,39 +1055,37 @@ static void dumpInstruction(const DexFile* pDexFile, case Instruction::k32x: // op vAAAA, vBBBB fprintf(gOutFile, " v%d, v%d", pDecInsn->VRegA(), pDecInsn->VRegB()); break; - case Instruction::k35c: // op {vC, vD, vE, vF, vG}, thing@BBBB + case Instruction::k35c: { // op {vC, vD, vE, vF, vG}, thing@BBBB // NOT SUPPORTED: // case Instruction::k35ms: // [opt] invoke-virtual+super // case Instruction::k35mi: // [opt] inline invoke - { - u4 arg[Instruction::kMaxVarArgRegs]; - pDecInsn->GetVarArgs(arg); - fputs(" {", gOutFile); - for (int i = 0, n = pDecInsn->VRegA(); i < n; i++) { - if (i == 0) { - fprintf(gOutFile, "v%d", arg[i]); - } else { - fprintf(gOutFile, ", v%d", arg[i]); - } - } // for - fprintf(gOutFile, "}, %s", indexBuf); - } + u4 arg[Instruction::kMaxVarArgRegs]; + pDecInsn->GetVarArgs(arg); + fputs(" {", gOutFile); + for (int i = 0, n = pDecInsn->VRegA(); i < n; i++) { + if (i == 0) { + fprintf(gOutFile, "v%d", arg[i]); + } else { + fprintf(gOutFile, ", v%d", arg[i]); + } + } // for + fprintf(gOutFile, "}, %s", indexBuf); break; - case Instruction::k25x: // op vC, {vD, vE, vF, vG} (B: count) - { - u4 arg[Instruction::kMaxVarArgRegs25x]; - pDecInsn->GetAllArgs25x(arg); - fprintf(gOutFile, " v%d, {", arg[0]); - for (int i = 0, n = pDecInsn->VRegB(); i < n; i++) { - if (i == 0) { - fprintf(gOutFile, "v%d", arg[Instruction::kLambdaVirtualRegisterWidth + i]); - } else { - fprintf(gOutFile, ", v%d", arg[Instruction::kLambdaVirtualRegisterWidth + i]); - } - } // for - fputc('}', gOutFile); - } + } + case Instruction::k25x: { // op vC, {vD, vE, vF, vG} (B: count) + u4 arg[Instruction::kMaxVarArgRegs25x]; + pDecInsn->GetAllArgs25x(arg); + fprintf(gOutFile, " v%d, {", arg[0]); + for (int i = 0, n = pDecInsn->VRegB(); i < n; i++) { + if (i == 0) { + fprintf(gOutFile, "v%d", arg[Instruction::kLambdaVirtualRegisterWidth + i]); + } else { + fprintf(gOutFile, ", v%d", arg[Instruction::kLambdaVirtualRegisterWidth + i]); + } + } // for + fputc('}', gOutFile); break; + } case Instruction::k3rc: // op {vCCCC .. v(CCCC+AA-1)}, thing@BBBB // NOT SUPPORTED: // case Instruction::k3rms: // [opt] invoke-virtual+super/range @@ -821,18 +1104,17 @@ static void dumpInstruction(const DexFile* pDexFile, fprintf(gOutFile, "}, %s", indexBuf); } break; - case Instruction::k51l: // op vAA, #+BBBBBBBBBBBBBBBB - { - // This is often, but not always, a double. - union { - double d; - u8 j; - } conv; - conv.j = pDecInsn->WideVRegB(); - fprintf(gOutFile, " v%d, #double %f // #%016" PRIx64, - pDecInsn->VRegA(), conv.d, pDecInsn->WideVRegB()); - } + case Instruction::k51l: { // op vAA, #+BBBBBBBBBBBBBBBB + // This is often, but not always, a double. + union { + double d; + u8 j; + } conv; + conv.j = pDecInsn->WideVRegB(); + fprintf(gOutFile, " v%d, #double %g // #%016" PRIx64, + pDecInsn->VRegA(), conv.d, pDecInsn->WideVRegB()); break; + } // NOT SUPPORTED: // case Instruction::k00x: // unknown op or breakpoint // break; @@ -1017,126 +1299,9 @@ static void dumpMethod(const DexFile* pDexFile, u4 idx, u4 flags, } /* - * Dumps a string value with some escape characters. - */ -static void dumpEscapedString(const char* p) { - for (; *p; p++) { - switch (*p) { - case '\\': - fputs("\\\\", gOutFile); - break; - case '\"': - fputs("\\\"", gOutFile); - break; - case '\t': - fputs("\\t", gOutFile); - break; - case '\n': - fputs("\\n", gOutFile); - break; - case '\r': - fputs("\\r", gOutFile); - break; - default: - putc(*p, gOutFile); - } - } -} - -/* - * Dumps an XML attribute value between double-quotes. - */ -static void dumpXmlAttribute(const char* p) { - for (; *p; p++) { - switch (*p) { - case '&': - fputs("&", gOutFile); - break; - case '<': - fputs("<", gOutFile); - break; - case '"': - fputs(""", gOutFile); - break; - case '\t': - fputs("	", gOutFile); - break; - case '\n': - fputs("
", gOutFile); - break; - case '\r': - fputs("
", gOutFile); - break; - default: - putc(*p, gOutFile); - } - } -} - -/* - * Dumps a value of static (class) field. - */ -static void dumpSFieldValue(const DexFile* pDexFile, - EncodedStaticFieldValueIterator::ValueType valueType, - const jvalue* pValue) { - switch (valueType) { - case EncodedStaticFieldValueIterator::kByte: - fprintf(gOutFile, "%" PRIu8, pValue->b); - break; - case EncodedStaticFieldValueIterator::kShort: - fprintf(gOutFile, "%" PRId16, pValue->s); - break; - case EncodedStaticFieldValueIterator::kChar: - fprintf(gOutFile, "%" PRIu16, pValue->c); - break; - case EncodedStaticFieldValueIterator::kInt: - fprintf(gOutFile, "%" PRId32, pValue->i); - break; - case EncodedStaticFieldValueIterator::kLong: - fprintf(gOutFile, "%" PRId64, pValue->j); - break; - case EncodedStaticFieldValueIterator::kFloat: - fprintf(gOutFile, "%f", pValue->f); - break; - case EncodedStaticFieldValueIterator::kDouble: - fprintf(gOutFile, "%f", pValue->d); - break; - case EncodedStaticFieldValueIterator::kString: { - const char* str = - pDexFile->GetStringData(pDexFile->GetStringId(pValue->i)); - if (gOptions.outputFormat == OUTPUT_PLAIN) { - fputs("\"", gOutFile); - dumpEscapedString(str); - fputs("\"", gOutFile); - } else { - dumpXmlAttribute(str); - } - break; - } - case EncodedStaticFieldValueIterator::kNull: - fputs("null", gOutFile); - break; - case EncodedStaticFieldValueIterator::kBoolean: - fputs(pValue->z ? "true" : "false", gOutFile); - break; - - case EncodedStaticFieldValueIterator::kAnnotation: - case EncodedStaticFieldValueIterator::kArray: - case EncodedStaticFieldValueIterator::kEnum: - case EncodedStaticFieldValueIterator::kField: - case EncodedStaticFieldValueIterator::kMethod: - case EncodedStaticFieldValueIterator::kType: - default: - fprintf(gOutFile, "Unexpected static field type: %d", valueType); - } -} - -/* * Dumps a static (class) field. */ -static void dumpSField(const DexFile* pDexFile, u4 idx, u4 flags, int i, - EncodedStaticFieldValueIterator::ValueType valueType, - const jvalue* pValue) { +static void dumpSField(const DexFile* pDexFile, u4 idx, u4 flags, int i, const u1** data) { // Bail for anything private if export only requested. if (gOptions.exportsOnly && (flags & (kAccPublic | kAccProtected)) == 0) { return; @@ -1153,9 +1318,9 @@ static void dumpSField(const DexFile* pDexFile, u4 idx, u4 flags, int i, fprintf(gOutFile, " name : '%s'\n", name); fprintf(gOutFile, " type : '%s'\n", typeDescriptor); fprintf(gOutFile, " access : 0x%04x (%s)\n", flags, accessStr); - if (pValue != nullptr) { + if (data != nullptr) { fputs(" value : ", gOutFile); - dumpSFieldValue(pDexFile, valueType, pValue); + dumpEncodedValue(pDexFile, data); fputs("\n", gOutFile); } } else if (gOptions.outputFormat == OUTPUT_XML) { @@ -1170,9 +1335,9 @@ static void dumpSField(const DexFile* pDexFile, u4 idx, u4 flags, int i, fprintf(gOutFile, " final=%s\n", quotedBool((flags & kAccFinal) != 0)); // The "deprecated=" is not knowable w/o parsing annotations. fprintf(gOutFile, " visibility=%s\n", quotedVisibility(flags)); - if (pValue != nullptr) { + if (data != nullptr) { fputs(" value=\"", gOutFile); - dumpSFieldValue(pDexFile, valueType, pValue); + dumpEncodedValue(pDexFile, data); fputs("\"\n", gOutFile); } fputs(">\n</field>\n", gOutFile); @@ -1185,8 +1350,7 @@ static void dumpSField(const DexFile* pDexFile, u4 idx, u4 flags, int i, * Dumps an instance field. */ static void dumpIField(const DexFile* pDexFile, u4 idx, u4 flags, int i) { - dumpSField(pDexFile, idx, flags, i, - EncodedStaticFieldValueIterator::kByte, nullptr); + dumpSField(pDexFile, idx, flags, i, nullptr); } /* @@ -1196,7 +1360,7 @@ static void dumpIField(const DexFile* pDexFile, u4 idx, u4 flags, int i) { */ static void dumpCfg(const DexFile* dex_file, - uint32_t dex_method_idx, + u4 dex_method_idx, const DexFile::CodeItem* code_item) { if (code_item != nullptr) { std::ostringstream oss; @@ -1207,7 +1371,7 @@ static void dumpCfg(const DexFile* dex_file, static void dumpCfg(const DexFile* dex_file, int idx) { const DexFile::ClassDef& class_def = dex_file->GetClassDef(idx); - const uint8_t* class_data = dex_file->GetClassData(class_def); + const u1* class_data = dex_file->GetClassData(class_def); if (class_data == nullptr) { // empty class such as a marker interface? return; } @@ -1248,7 +1412,15 @@ static void dumpClass(const DexFile* pDexFile, int idx, char** pLastPackage) { return; } - if (gOptions.cfg) { + if (gOptions.showSectionHeaders) { + dumpClassDef(pDexFile, idx); + } + + if (gOptions.showAnnotations) { + dumpClassAnnotations(pDexFile, idx); + } + + if (gOptions.showCfg) { dumpCfg(pDexFile, idx); return; } @@ -1347,33 +1519,35 @@ static void dumpClass(const DexFile* pDexFile, int idx, char** pLastPackage) { } } else { ClassDataItemIterator pClassData(*pDexFile, pEncodedData); + + // Prepare data for static fields. + const u1* sData = pDexFile->GetEncodedStaticFieldValuesArray(pClassDef); + const u4 sSize = sData != nullptr ? DecodeUnsignedLeb128(&sData) : 0; + + // Static fields. if (gOptions.outputFormat == OUTPUT_PLAIN) { fprintf(gOutFile, " Static fields -\n"); } - EncodedStaticFieldValueIterator staticFieldValues(*pDexFile, pClassDef); - for (int i = 0; pClassData.HasNextStaticField(); i++, pClassData.Next()) { - EncodedStaticFieldValueIterator::ValueType valueType = - EncodedStaticFieldValueIterator::kByte; - const jvalue* pValue = nullptr; - if (staticFieldValues.HasNext()) { - valueType = staticFieldValues.GetValueType(); - pValue = &staticFieldValues.GetJavaValue(); - } - dumpSField(pDexFile, pClassData.GetMemberIndex(), - pClassData.GetRawMemberAccessFlags(), i, - valueType, pValue); - if (staticFieldValues.HasNext()) { - staticFieldValues.Next(); - } + for (u4 i = 0; pClassData.HasNextStaticField(); i++, pClassData.Next()) { + dumpSField(pDexFile, + pClassData.GetMemberIndex(), + pClassData.GetRawMemberAccessFlags(), + i, + i < sSize ? &sData : nullptr); } // for - DCHECK(!staticFieldValues.HasNext()); + + // Instance fields. if (gOptions.outputFormat == OUTPUT_PLAIN) { fprintf(gOutFile, " Instance fields -\n"); } - for (int i = 0; pClassData.HasNextInstanceField(); i++, pClassData.Next()) { - dumpIField(pDexFile, pClassData.GetMemberIndex(), - pClassData.GetRawMemberAccessFlags(), i); + for (u4 i = 0; pClassData.HasNextInstanceField(); i++, pClassData.Next()) { + dumpIField(pDexFile, + pClassData.GetMemberIndex(), + pClassData.GetRawMemberAccessFlags(), + i); } // for + + // Direct methods. if (gOptions.outputFormat == OUTPUT_PLAIN) { fprintf(gOutFile, " Direct methods -\n"); } @@ -1383,6 +1557,8 @@ static void dumpClass(const DexFile* pDexFile, int idx, char** pLastPackage) { pClassData.GetMethodCodeItem(), pClassData.GetMethodCodeItemOffset(), i); } // for + + // Virtual methods. if (gOptions.outputFormat == OUTPUT_PLAIN) { fprintf(gOutFile, " Virtual methods -\n"); } @@ -1434,9 +1610,6 @@ static void processDexFile(const char* fileName, const DexFile* pDexFile) { char* package = nullptr; const u4 classDefsSize = pDexFile->GetHeader().class_defs_size_; for (u4 i = 0; i < classDefsSize; i++) { - if (gOptions.showSectionHeaders) { - dumpClassDef(pDexFile, i); - } dumpClass(pDexFile, i, &package); } // for @@ -1461,17 +1634,11 @@ int processFile(const char* fileName) { } // If the file is not a .dex file, the function tries .zip/.jar/.apk files, - // all of which are Zip archives with "classes.dex" inside. The compressed - // data needs to be extracted to a temp file, the location of which varies. - // - // TODO(ajcbik): fix following issues - // - // (1) gOptions.tempFileName is not accounted for - // (2) gOptions.ignoreBadChecksum is not accounted for - // + // all of which are Zip archives with "classes.dex" inside. + const bool kVerifyChecksum = !gOptions.ignoreBadChecksum; std::string error_msg; std::vector<std::unique_ptr<const DexFile>> dex_files; - if (!DexFile::Open(fileName, fileName, &error_msg, &dex_files)) { + if (!DexFile::Open(fileName, fileName, kVerifyChecksum, &error_msg, &dex_files)) { // Display returned error message to user. Note that this error behavior // differs from the error messages shown by the original Dalvik dexdump. fputs(error_msg.c_str(), stderr); diff --git a/dexdump/dexdump.h b/dexdump/dexdump.h index 50280a9f28..6939f9076b 100644 --- a/dexdump/dexdump.h +++ b/dexdump/dexdump.h @@ -42,13 +42,13 @@ struct Options { bool disassemble; bool exportsOnly; bool ignoreBadChecksum; + bool showAnnotations; + bool showCfg; bool showFileHeaders; bool showSectionHeaders; bool verbose; - bool cfg; OutputFormat outputFormat; const char* outputFileName; - const char* tempFileName; }; /* Prototypes. */ diff --git a/dexdump/dexdump_main.cc b/dexdump/dexdump_main.cc index dd1002c4c7..f716ba8be7 100644 --- a/dexdump/dexdump_main.cc +++ b/dexdump/dexdump_main.cc @@ -17,8 +17,8 @@ * * This is a re-implementation of the original dexdump utility that was * based on Dalvik functions in libdex into a new dexdump that is now - * based on Art functions in libart instead. The output is identical to - * the original for correct DEX files. Error messages may differ, however. + * based on Art functions in libart instead. The output is very similar to + * to the original for correct DEX files. Error messages may differ, however. * Also, ODEX files are no longer supported. */ @@ -40,19 +40,18 @@ static const char* gProgName = "dexdump"; */ static void usage(void) { fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n"); - fprintf(stderr, "%s: [-c] [-d] [-e] [-f] [-h] [-i] [-l layout] [-o outfile]" - " [-t tempfile] dexfile...\n", gProgName); - fprintf(stderr, "\n"); + fprintf(stderr, "%s: [-a] [-c] [-d] [-e] [-f] [-h] [-i] [-l layout] [-o outfile]" + " dexfile...\n\n", gProgName); + fprintf(stderr, " -a : display annotations\n"); fprintf(stderr, " -c : verify checksum and exit\n"); fprintf(stderr, " -d : disassemble code sections\n"); fprintf(stderr, " -e : display exported items only\n"); fprintf(stderr, " -f : display summary information from file header\n"); - fprintf(stderr, " -g : dump CFG for dex\n"); + fprintf(stderr, " -g : display CFG for dex\n"); fprintf(stderr, " -h : display file header details\n"); fprintf(stderr, " -i : ignore checksum failures\n"); fprintf(stderr, " -l : output layout, either 'plain' or 'xml'\n"); fprintf(stderr, " -o : output file name (defaults to stdout)\n"); - fprintf(stderr, " -t : temp file name (defaults to /sdcard/dex-temp-*)\n"); } /* @@ -70,11 +69,14 @@ int dexdumpDriver(int argc, char** argv) { // Parse all arguments. while (1) { - const int ic = getopt(argc, argv, "cdefghil:t:o:"); + const int ic = getopt(argc, argv, "acdefghil:o:"); if (ic < 0) { break; // done } switch (ic) { + case 'a': // display annotations + gOptions.showAnnotations = true; + break; case 'c': // verify the checksum then exit gOptions.checksumOnly = true; break; @@ -84,13 +86,13 @@ int dexdumpDriver(int argc, char** argv) { case 'e': // exported items only gOptions.exportsOnly = true; break; - case 'f': // dump outer file header + case 'f': // display outer file header gOptions.showFileHeaders = true; break; - case 'g': // dump cfg - gOptions.cfg = true; + case 'g': // display cfg + gOptions.showCfg = true; break; - case 'h': // dump section headers, i.e. all meta-data + case 'h': // display section headers, i.e. all meta-data gOptions.showSectionHeaders = true; break; case 'i': // continue even if checksum is bad @@ -106,9 +108,6 @@ int dexdumpDriver(int argc, char** argv) { wantUsage = true; } break; - case 't': // temp file, used when opening compressed Jar - gOptions.tempFileName = optarg; - break; case 'o': // output file gOptions.outputFileName = optarg; break; diff --git a/dexlist/dexlist.cc b/dexlist/dexlist.cc index d20c16919a..6f19df55fd 100644 --- a/dexlist/dexlist.cc +++ b/dexlist/dexlist.cc @@ -180,9 +180,10 @@ void dumpClass(const DexFile* pDexFile, u4 idx) { static int processFile(const char* fileName) { // If the file is not a .dex file, the function tries .zip/.jar/.apk files, // all of which are Zip archives with "classes.dex" inside. + static constexpr bool kVerifyChecksum = true; std::string error_msg; std::vector<std::unique_ptr<const DexFile>> dex_files; - if (!DexFile::Open(fileName, fileName, &error_msg, &dex_files)) { + if (!DexFile::Open(fileName, fileName, kVerifyChecksum, &error_msg, &dex_files)) { fputs(error_msg.c_str(), stderr); fputc('\n', stderr); return -1; diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc index 286faf215a..1a3e3f5d24 100644 --- a/disassembler/disassembler_arm.cc +++ b/disassembler/disassembler_arm.cc @@ -1497,6 +1497,25 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) } break; } + case 0x7B: case 0x7F: { + FpRegister d(instr, 12, 22); + FpRegister m(instr, 0, 5); + uint32_t sz = (instr >> 18) & 0x3; // Decode size bits. + uint32_t size = (sz == 0) ? 8 : sz << 4; + uint32_t opc2 = (instr >> 7) & 0xF; + uint32_t Q = (instr >> 6) & 1; + if (Q == 0 && opc2 == 0xA && size == 8) { // 1010, VCNT + opcode << "vcnt." << size; + args << d << ", " << m; + } else if (Q == 0 && (opc2 == 0x4 || opc2 == 0x5) && size <= 32) { // 010x, VPADDL + bool op = HasBitSet(instr, 7); + opcode << "vpaddl." << (op ? "u" : "s") << size; + args << d << ", " << m; + } else { + opcode << "UNKNOWN " << op2; + } + break; + } default: // more formats if ((op2 >> 4) == 2) { // 010xxxx // data processing (register) diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 7239a479db..bb35f8dc30 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -35,7 +35,7 @@ #include "debug/elf_debug_writer.h" #include "debug/method_debug_info.h" #include "dex_file-inl.h" -#include "dex_instruction.h" +#include "dex_instruction-inl.h" #include "disassembler.h" #include "elf_builder.h" #include "gc/space/image_space.h" @@ -578,6 +578,7 @@ class OatDumper { // Print embedded dex file data range. const uint8_t* const oat_file_begin = oat_dex_file.GetOatFile()->Begin(); const uint8_t* const dex_file_pointer = oat_dex_file.GetDexFilePointer(); + std::set<uint32_t> string_ids; uint32_t dex_offset = dchecked_integral_cast<uint32_t>(dex_file_pointer - oat_file_begin); os << StringPrintf("dex-file: 0x%08x..0x%08x\n", dex_offset, @@ -623,7 +624,7 @@ class OatDumper { << " (" << oat_class.GetType() << ")\n"; // TODO: include bitmap here if type is kOatClassSomeCompiled? if (options_.list_classes_) continue; - if (!DumpOatClass(&vios, oat_class, *dex_file, class_def, &stop_analysis)) { + if (!DumpOatClass(&vios, oat_class, *dex_file, class_def, &stop_analysis, string_ids)) { success = false; } if (stop_analysis) { @@ -631,7 +632,7 @@ class OatDumper { return success; } } - + os << "Number of unique strings loaded from dex code: " << string_ids.size() << "\n"; os << std::flush; return success; } @@ -725,7 +726,8 @@ class OatDumper { bool DumpOatClass(VariableIndentationOutputStream* vios, const OatFile::OatClass& oat_class, const DexFile& dex_file, - const DexFile::ClassDef& class_def, bool* stop_analysis) { + const DexFile::ClassDef& class_def, bool* stop_analysis, + std::set<uint32_t>& string_ids) { bool success = true; bool addr_found = false; const uint8_t* class_data = dex_file.GetClassData(class_def); @@ -739,7 +741,7 @@ class OatDumper { while (it.HasNextDirectMethod()) { if (!DumpOatMethod(vios, class_def, class_method_index, oat_class, dex_file, it.GetMemberIndex(), it.GetMethodCodeItem(), - it.GetRawMemberAccessFlags(), &addr_found)) { + it.GetRawMemberAccessFlags(), &addr_found, string_ids)) { success = false; } if (addr_found) { @@ -752,7 +754,7 @@ class OatDumper { while (it.HasNextVirtualMethod()) { if (!DumpOatMethod(vios, class_def, class_method_index, oat_class, dex_file, it.GetMemberIndex(), it.GetMethodCodeItem(), - it.GetRawMemberAccessFlags(), &addr_found)) { + it.GetRawMemberAccessFlags(), &addr_found, string_ids)) { success = false; } if (addr_found) { @@ -777,9 +779,35 @@ class OatDumper { uint32_t class_method_index, const OatFile::OatClass& oat_class, const DexFile& dex_file, uint32_t dex_method_idx, const DexFile::CodeItem* code_item, - uint32_t method_access_flags, bool* addr_found) { + uint32_t method_access_flags, bool* addr_found, + std::set<uint32_t>& string_ids) { bool success = true; + if (code_item != nullptr) { + const uint16_t* code_ptr = code_item->insns_; + const uint16_t* code_end = code_item->insns_ + code_item->insns_size_in_code_units_; + + while (code_ptr < code_end) { + const Instruction* inst = Instruction::At(code_ptr); + switch (inst->Opcode()) { + case Instruction::CONST_STRING: { + uint32_t string_index = inst->VRegB_21c(); + string_ids.insert(string_index); + break; + } + case Instruction::CONST_STRING_JUMBO: { + uint32_t string_index = inst->VRegB_31c(); + string_ids.insert(string_index); + break; + } + + default: + break; + } + + code_ptr += inst->SizeInCodeUnits(); + } + } // TODO: Support regex std::string method_name = dex_file.GetMethodName(dex_file.GetMethodId(dex_method_idx)); if (method_name.find(options_.method_filter_) == std::string::npos) { diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc index 462c39735e..cd0aa6fd7d 100644 --- a/profman/profile_assistant_test.cc +++ b/profman/profile_assistant_test.cc @@ -61,17 +61,21 @@ class ProfileAssistantTest : public CommonRuntimeTest { ASSERT_TRUE(file_info.Equals(info)); } - // Runs test with given arguments. - int ProcessProfiles(const std::vector<int>& profiles_fd, int reference_profile_fd) { + std::string GetProfmanCmd() { std::string file_path = GetTestAndroidRoot(); file_path += "/bin/profman"; if (kIsDebugBuild) { file_path += "d"; } - - EXPECT_TRUE(OS::FileExists(file_path.c_str())) << file_path << " should be a valid file path"; + EXPECT_TRUE(OS::FileExists(file_path.c_str())) + << file_path << " should be a valid file path"; + return file_path; + } + // Runs test with given arguments. + int ProcessProfiles(const std::vector<int>& profiles_fd, int reference_profile_fd) { + std::string profman_cmd = GetProfmanCmd(); std::vector<std::string> argv_str; - argv_str.push_back(file_path); + argv_str.push_back(profman_cmd); for (size_t k = 0; k < profiles_fd.size(); k++) { argv_str.push_back("--profile-file-fd=" + std::to_string(profiles_fd[k])); } @@ -80,6 +84,15 @@ class ProfileAssistantTest : public CommonRuntimeTest { std::string error; return ExecAndReturnCode(argv_str, &error); } + + bool GenerateTestProfile(const std::string& filename) { + std::string profman_cmd = GetProfmanCmd(); + std::vector<std::string> argv_str; + argv_str.push_back(profman_cmd); + argv_str.push_back("--generate-test-profile=" + filename); + std::string error; + return ExecAndReturnCode(argv_str, &error); + } }; TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferences) { @@ -282,4 +295,15 @@ TEST_F(ProfileAssistantTest, FailProcessingBecauseOfReferenceProfiles) { CheckProfileInfo(profile1, info1); } +TEST_F(ProfileAssistantTest, TestProfileGeneration) { + ScratchFile profile; + // Generate a test profile. + GenerateTestProfile(profile.GetFilename()); + + // Verify that the generated profile is valid and can be loaded. + ASSERT_TRUE(profile.GetFile()->ResetOffset()); + ProfileCompilationInfo info; + ASSERT_TRUE(info.Load(GetFd(profile))); +} + } // namespace art diff --git a/profman/profman.cc b/profman/profman.cc index 754e4315d0..a5fefa71d4 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -100,6 +100,14 @@ NO_RETURN static void Usage(const char *fmt, ...) { UsageError(" --reference-profile-file-fd=<number>: same as --reference-profile-file but"); UsageError(" accepts a file descriptor. Cannot be used together with"); UsageError(" --reference-profile-file."); + UsageError(" --generate-test-profile=<filename>: generates a random profile file for testing."); + UsageError(" --generate-test-profile-num-dex=<number>: number of dex files that should be"); + UsageError(" included in the generated profile. Defaults to 20."); + UsageError(" --generate-test-profile-method-ratio=<number>: the percentage from the maximum"); + UsageError(" number of methods that should be generated. Defaults to 5."); + UsageError(" --generate-test-profile-class-ratio=<number>: the percentage from the maximum"); + UsageError(" number of classes that should be generated. Defaults to 5."); + UsageError(""); UsageError(""); UsageError(" --dex-location=<string>: location string to use with corresponding"); UsageError(" apk-fd to find dex files"); @@ -111,12 +119,20 @@ NO_RETURN static void Usage(const char *fmt, ...) { exit(EXIT_FAILURE); } +// Note: make sure you update the Usage if you change these values. +static constexpr uint16_t kDefaultTestProfileNumDex = 20; +static constexpr uint16_t kDefaultTestProfileMethodRatio = 5; +static constexpr uint16_t kDefaultTestProfileClassRatio = 5; + class ProfMan FINAL { public: ProfMan() : reference_profile_file_fd_(kInvalidFd), dump_only_(false), dump_output_to_fd_(kInvalidFd), + test_profile_num_dex_(kDefaultTestProfileNumDex), + test_profile_method_ratio_(kDefaultTestProfileMethodRatio), + test_profile_class_ratio_(kDefaultTestProfileClassRatio), start_ns_(NanoTime()) {} ~ProfMan() { @@ -159,6 +175,23 @@ class ProfMan FINAL { dex_locations_.push_back(option.substr(strlen("--dex-location=")).ToString()); } else if (option.starts_with("--apk-fd=")) { ParseFdForCollection(option, "--apk-fd", &apks_fd_); + } else if (option.starts_with("--generate-test-profile=")) { + test_profile_ = option.substr(strlen("--generate-test-profile=")).ToString(); + } else if (option.starts_with("--generate-test-profile-num-dex=")) { + ParseUintOption(option, + "--generate-test-profile-num-dex", + &test_profile_num_dex_, + Usage); + } else if (option.starts_with("--generate-test-profile-method-ratio")) { + ParseUintOption(option, + "--generate-test-profile-method-ratio", + &test_profile_method_ratio_, + Usage); + } else if (option.starts_with("--generate-test-profile-class-ratio")) { + ParseUintOption(option, + "--generate-test-profile-class-ratio", + &test_profile_class_ratio_, + Usage); } else { Usage("Unknown argument '%s'", option.data()); } @@ -168,6 +201,15 @@ class ProfMan FINAL { bool has_reference_profile = !reference_profile_file_.empty() || FdIsValid(reference_profile_file_fd_); + if (!test_profile_.empty()) { + if (test_profile_method_ratio_ > 100) { + Usage("Invalid ratio for --generate-test-profile-method-ratio"); + } + if (test_profile_class_ratio_ > 100) { + Usage("Invalid ratio for --generate-test-profile-class-ratio"); + } + return; + } // --dump-only may be specified with only --reference-profiles present. if (!dump_only_ && !has_profiles) { Usage("No profile files specified."); @@ -234,6 +276,7 @@ class ProfMan FINAL { MemMap::Init(); // for ZipArchive::OpenFromFd std::vector<const DexFile*> dex_files; assert(dex_locations_.size() == apks_fd_.size()); + static constexpr bool kVerifyChecksum = true; for (size_t i = 0; i < dex_locations_.size(); ++i) { std::string error_msg; std::vector<std::unique_ptr<const DexFile>> dex_files_for_location; @@ -246,6 +289,7 @@ class ProfMan FINAL { } if (DexFile::OpenFromZip(*zip_archive, dex_locations_[i], + kVerifyChecksum, &error_msg, &dex_files_for_location)) { } else { @@ -315,6 +359,25 @@ class ProfMan FINAL { return dump_only_; } + int GenerateTestProfile() { + int profile_test_fd = open(test_profile_.c_str(), O_CREAT | O_TRUNC | O_WRONLY); + if (profile_test_fd < 0) { + std::cerr << "Cannot open " << test_profile_ << strerror(errno); + return -1; + } + + bool result = ProfileCompilationInfo::GenerateTestProfile(profile_test_fd, + test_profile_num_dex_, + test_profile_method_ratio_, + test_profile_class_ratio_); + close(profile_test_fd); // ignore close result. + return result ? 0 : -1; + } + + bool ShouldGenerateTestProfile() { + return !test_profile_.empty(); + } + private: static void ParseFdForCollection(const StringPiece& option, const char* arg_name, @@ -348,6 +411,10 @@ class ProfMan FINAL { int reference_profile_file_fd_; bool dump_only_; int dump_output_to_fd_; + std::string test_profile_; + uint16_t test_profile_num_dex_; + uint16_t test_profile_method_ratio_; + uint16_t test_profile_class_ratio_; uint64_t start_ns_; }; @@ -358,6 +425,9 @@ static int profman(int argc, char** argv) { // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError. profman.ParseArgs(argc, argv); + if (profman.ShouldGenerateTestProfile()) { + return profman.GenerateTestProfile(); + } if (profman.ShouldOnlyDumpProfile()) { return profman.DumpProfileInfo(); } diff --git a/runtime/arch/arm/asm_support_arm.S b/runtime/arch/arm/asm_support_arm.S index 44c7649dea..38ca76a6a9 100644 --- a/runtime/arch/arm/asm_support_arm.S +++ b/runtime/arch/arm/asm_support_arm.S @@ -30,18 +30,17 @@ .arch armv7-a .thumb -// Macro to generate the value of Runtime::Current into rDest clobbering rTemp. As it uses labels +// Macro to generate the value of Runtime::Current into rDest. As it uses labels // then the labels need to be unique. We bind these to the function name in the ENTRY macros. -.macro RUNTIME_CURRENT name, num, rDest, rTemp +.macro RUNTIME_CURRENT name, num, rDest .if .Lruntime_current\num\()_used .error .endif .set .Lruntime_current\num\()_used, 1 - ldr \rDest, .Lgot_\name\()_\num @ Load offset of the GOT. - ldr \rTemp, .Lruntime_instance_\name\()_\num @ Load GOT offset of Runtime::instance_. + ldr \rDest, .Lruntime_instance_\name\()_\num @ Load GOT_PREL offset of Runtime::instance_. .Lload_got_\name\()_\num\(): - add \rDest, pc @ Fixup GOT address. - ldr \rDest, [\rDest, \rTemp] @ Load address of Runtime::instance_. + add \rDest, pc @ Fixup GOT_PREL address. + ldr \rDest, [\rDest] @ Load address of Runtime::instance_. ldr \rDest, [\rDest] @ Load Runtime::instance_. .endm @@ -90,26 +89,20 @@ DEF_ENTRY .arm, \name .endm -// Terminate an ENTRY and generate GOT references. +// Terminate an ENTRY and generate GOT_PREL references. .macro END name // Generate offsets of GOT and Runtime::instance_ used in RUNTIME_CURRENT. .if .Lruntime_current1_used - .Lgot_\name\()_1: - .word _GLOBAL_OFFSET_TABLE_-(.Lload_got_\name\()_1+4) .Lruntime_instance_\name\()_1: - .word _ZN3art7Runtime9instance_E(GOT) + .word _ZN3art7Runtime9instance_E(GOT_PREL)-(.Lload_got_\name\()_1+4) .endif .if .Lruntime_current2_used - .Lgot_\name\()_2: - .word _GLOBAL_OFFSET_TABLE_-(.Lload_got_\name\()_2+4) .Lruntime_instance_\name\()_2: - .word _ZN3art7Runtime9instance_E(GOT) + .word _ZN3art7Runtime9instance_E(GOT_PREL)-(.Lload_got_\name\()_2+4) .endif .if .Lruntime_current3_used - .Lgot_\name\()_3: - .word _GLOBAL_OFFSET_TABLE_-(.Lload_got_\name\()_3+4) .Lruntime_instance_\name\()_3: - .word _ZN3art7Runtime9instance_E(GOT) + .word _ZN3art7Runtime9instance_E(GOT_PREL)-(.Lload_got_\name\()_3+4) .endif // Remove the RUNTIME_CURRENTx macros so they get rebound in the next function entry. .purgem RUNTIME_CURRENT1 diff --git a/runtime/arch/arm/instruction_set_features_arm.cc b/runtime/arch/arm/instruction_set_features_arm.cc index ffac0307b7..c3a5829979 100644 --- a/runtime/arch/arm/instruction_set_features_arm.cc +++ b/runtime/arch/arm/instruction_set_features_arm.cc @@ -206,6 +206,7 @@ const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromAssembly() { struct sigaction sa, osa; sa.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO; sa.sa_sigaction = bad_divide_inst_handle; + sigemptyset(&sa.sa_mask); sigaction(SIGILL, &sa, &osa); bool has_div = false; diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 5209bb6ab6..27a41f09ad 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -44,15 +44,15 @@ * Macro that sets up the callee save frame to conform with * Runtime::CreateCalleeSaveMethod(kSaveAll) */ -.macro SETUP_SAVE_ALL_CALLEE_SAVE_FRAME rTemp1, rTemp2 +.macro SETUP_SAVE_ALL_CALLEE_SAVE_FRAME rTemp SPILL_ALL_CALLEE_SAVE_GPRS @ 9 words (36 bytes) of callee saves. vpush {s16-s31} @ 16 words (64 bytes) of floats. .cfi_adjust_cfa_offset 64 sub sp, #12 @ 3 words of space, bottom word will hold Method* .cfi_adjust_cfa_offset 12 - RUNTIME_CURRENT1 \rTemp1, \rTemp2 @ Load Runtime::Current into rTemp1. - ldr \rTemp1, [\rTemp1, #RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET] @ rTemp1 is kSaveAll Method*. - str \rTemp1, [sp, #0] @ Place Method* at bottom of stack. + RUNTIME_CURRENT1 \rTemp @ Load Runtime::Current into rTemp. + ldr \rTemp, [\rTemp, #RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET] @ rTemp is kSaveAll Method*. + str \rTemp, [sp, #0] @ Place Method* at bottom of stack. str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. // Ugly compile-time check, but we only have the preprocessor. @@ -65,7 +65,7 @@ * Macro that sets up the callee save frame to conform with * Runtime::CreateCalleeSaveMethod(kRefsOnly). */ -.macro SETUP_REFS_ONLY_CALLEE_SAVE_FRAME rTemp1, rTemp2 +.macro SETUP_REFS_ONLY_CALLEE_SAVE_FRAME rTemp push {r5-r8, r10-r11, lr} @ 7 words of callee saves .cfi_adjust_cfa_offset 28 .cfi_rel_offset r5, 0 @@ -77,9 +77,9 @@ .cfi_rel_offset lr, 24 sub sp, #4 @ bottom word will hold Method* .cfi_adjust_cfa_offset 4 - RUNTIME_CURRENT2 \rTemp1, \rTemp2 @ Load Runtime::Current into rTemp1. - ldr \rTemp1, [\rTemp1, #RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET] @ rTemp1 is kRefsOnly Method*. - str \rTemp1, [sp, #0] @ Place Method* at bottom of stack. + RUNTIME_CURRENT2 \rTemp @ Load Runtime::Current into rTemp. + ldr \rTemp, [\rTemp, #RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET] @ rTemp is kRefsOnly Method*. + str \rTemp, [sp, #0] @ Place Method* at bottom of stack. str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. // Ugly compile-time check, but we only have the preprocessor. @@ -88,30 +88,6 @@ #endif .endm - /* - * Macro that sets up the callee save frame to conform with - * Runtime::CreateCalleeSaveMethod(kRefsOnly) - * and preserves the value of rTemp2 at entry. - */ -.macro SETUP_REFS_ONLY_CALLEE_SAVE_FRAME_PRESERVE_RTEMP2 rTemp1, rTemp2 - push {r5-r8, r10-r11, lr} @ 7 words of callee saves - .cfi_adjust_cfa_offset 28 - .cfi_rel_offset r5, 0 - .cfi_rel_offset r6, 4 - .cfi_rel_offset r7, 8 - .cfi_rel_offset r8, 12 - .cfi_rel_offset r10, 16 - .cfi_rel_offset r11, 20 - .cfi_rel_offset lr, 24 - sub sp, #4 @ bottom word will hold Method* - .cfi_adjust_cfa_offset 4 - str \rTemp2, [sp, #0] @ save rTemp2 - RUNTIME_CURRENT2 \rTemp1, \rTemp2 @ Load Runtime::Current into rTemp1. - ldr \rTemp1, [\rTemp1, #RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET] @ rTemp1 is kRefsOnly Method*. - ldr \rTemp2, [sp, #0] @ restore rTemp2 - str \rTemp1, [sp, #0] @ Place Method* at bottom of stack. - str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. - // Ugly compile-time check, but we only have the preprocessor. #if (FRAME_SIZE_REFS_ONLY_CALLEE_SAVE != 28 + 4) #error "REFS_ONLY_CALLEE_SAVE_FRAME(ARM) size not as expected." @@ -164,12 +140,12 @@ #endif .endm -.macro SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME rTemp1, rTemp2 +.macro SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME rTemp SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_REGISTERS_ONLY - RUNTIME_CURRENT3 \rTemp1, \rTemp2 @ Load Runtime::Current into rTemp1. - @ rTemp1 is kRefsAndArgs Method*. - ldr \rTemp1, [\rTemp1, #RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET] - str \rTemp1, [sp, #0] @ Place Method* at bottom of stack. + RUNTIME_CURRENT3 \rTemp @ Load Runtime::Current into rTemp. + @ rTemp is kRefsAndArgs Method*. + ldr \rTemp, [\rTemp, #RUNTIME_REFS_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET] + str \rTemp, [sp, #0] @ Place Method* at bottom of stack. str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame. .endm @@ -217,7 +193,7 @@ .macro DELIVER_PENDING_EXCEPTION .fnend .fnstart - SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r0, r1 @ save callee saves for throw + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r0 @ save callee saves for throw mov r0, r9 @ pass Thread::Current b artDeliverPendingExceptionFromCode @ artDeliverPendingExceptionFromCode(Thread*) .endm @@ -225,7 +201,7 @@ .macro NO_ARG_RUNTIME_EXCEPTION c_name, cxx_name .extern \cxx_name ENTRY \c_name - SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r0, r1 // save all registers as basis for long jump context + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r0 @ save all registers as basis for long jump context mov r0, r9 @ pass Thread::Current b \cxx_name @ \cxx_name(Thread*) END \c_name @@ -234,7 +210,7 @@ END \c_name .macro ONE_ARG_RUNTIME_EXCEPTION c_name, cxx_name .extern \cxx_name ENTRY \c_name - SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r1, r2 // save all registers as basis for long jump context + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r1 @ save all registers as basis for long jump context mov r1, r9 @ pass Thread::Current b \cxx_name @ \cxx_name(Thread*) END \c_name @@ -243,7 +219,7 @@ END \c_name .macro TWO_ARG_RUNTIME_EXCEPTION c_name, cxx_name .extern \cxx_name ENTRY \c_name - SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r2, r3 // save all registers as basis for long jump context + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r2 @ save all registers as basis for long jump context mov r2, r9 @ pass Thread::Current b \cxx_name @ \cxx_name(Thread*) END \c_name @@ -275,7 +251,7 @@ END \c_name .macro ONE_ARG_REF_DOWNCALL name, entrypoint, return .extern \entrypoint ENTRY \name - SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2 @ save callee saves in case of GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1 @ save callee saves in case of GC ldr r1, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] @ pass referrer mov r2, r9 @ pass Thread::Current bl \entrypoint @ (uint32_t field_idx, const Method* referrer, Thread*) @@ -287,7 +263,7 @@ END \name .macro TWO_ARG_REF_DOWNCALL name, entrypoint, return .extern \entrypoint ENTRY \name - SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case of GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2 @ save callee saves in case of GC ldr r2, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] @ pass referrer mov r3, r9 @ pass Thread::Current bl \entrypoint @ (field_idx, Object*, referrer, Thread*) @@ -299,7 +275,7 @@ END \name .macro THREE_ARG_REF_DOWNCALL name, entrypoint, return .extern \entrypoint ENTRY \name - SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r3, r12 @ save callee saves in case of GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r3 @ save callee saves in case of GC ldr r3, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] @ pass referrer str r9, [sp, #-16]! @ expand the frame and pass Thread::Current .cfi_adjust_cfa_offset 16 @@ -334,6 +310,12 @@ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_div_zero, artThrowDivZeroFromCode TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_array_bounds, artThrowArrayBoundsFromCode /* + * Called by managed code to create and deliver a StringIndexOutOfBoundsException + * as if thrown from a call to String.charAt(). Arg1 holds index, arg2 holds limit. + */ +TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_string_bounds, artThrowStringBoundsFromCode + + /* * Called by managed code to create and deliver a StackOverflowError. */ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_stack_overflow, artThrowStackOverflowFromCode @@ -360,7 +342,7 @@ ONE_ARG_RUNTIME_EXCEPTION art_quick_throw_no_such_method, artThrowNoSuchMethodFr */ .macro INVOKE_TRAMPOLINE_BODY cxx_name .extern \cxx_name - SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case allocation triggers GC + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME r2 @ save callee saves in case allocation triggers GC mov r2, r9 @ pass Thread::Current mov r3, sp bl \cxx_name @ (method_idx, this, Thread*, SP) @@ -566,7 +548,7 @@ ENTRY art_quick_lock_object .Llock_strex_fail: b .Lretry_lock @ retry .Lslow_lock: - SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2 @ save callee saves in case we block + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1 @ save callee saves in case we block mov r1, r9 @ pass Thread::Current bl artLockObjectFromCode @ (Object* obj, Thread*) RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME @@ -575,7 +557,7 @@ ENTRY art_quick_lock_object END art_quick_lock_object ENTRY art_quick_lock_object_no_inline - SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2 @ save callee saves in case we block + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1 @ save callee saves in case we block mov r1, r9 @ pass Thread::Current bl artLockObjectFromCode @ (Object* obj, Thread*) RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME @@ -632,7 +614,7 @@ ENTRY art_quick_unlock_object b .Lretry_unlock @ retry .Lslow_unlock: @ save callee saves in case exception allocation triggers GC - SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2 + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1 mov r1, r9 @ pass Thread::Current bl artUnlockObjectFromCode @ (Object* obj, Thread*) RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME @@ -642,7 +624,7 @@ END art_quick_unlock_object ENTRY art_quick_unlock_object_no_inline @ save callee saves in case exception allocation triggers GC - SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2 + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1 mov r1, r9 @ pass Thread::Current bl artUnlockObjectFromCode @ (Object* obj, Thread*) RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME @@ -677,7 +659,7 @@ ENTRY art_quick_check_cast .cfi_restore r0 .cfi_restore r1 .cfi_restore lr - SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r2, r3 // save all registers as basis for long jump context + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r2 @ save all registers as basis for long jump context mov r2, r9 @ pass Thread::Current b artThrowClassCastException @ (Class*, Class*, Thread*) bkpt @@ -813,7 +795,7 @@ ENTRY art_quick_aput_obj .Lthrow_array_store_exception: pop {r0-r2, lr} /* No need to repeat restore cfi directives, the ones above apply here. */ - SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r3, ip + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r3 mov r1, r2 mov r2, r9 @ pass Thread::Current b artThrowArrayStoreException @ (Class*, Class*, Thread*) @@ -824,7 +806,7 @@ END art_quick_aput_obj .macro ONE_ARG_DOWNCALL name, entrypoint, return .extern \entrypoint ENTRY \name - SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2 @ save callee saves in case of GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1 @ save callee saves in case of GC mov r1, r9 @ pass Thread::Current bl \entrypoint @ (uint32_t type_idx, Method* method, Thread*) RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME @@ -836,7 +818,7 @@ END \name .macro TWO_ARG_DOWNCALL name, entrypoint, return .extern \entrypoint ENTRY \name - SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case of GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2 @ save callee saves in case of GC mov r2, r9 @ pass Thread::Current bl \entrypoint @ (uint32_t type_idx, Method* method, Thread*) RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME @@ -848,7 +830,7 @@ END \name .macro THREE_ARG_DOWNCALL name, entrypoint, return .extern \entrypoint ENTRY \name - SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r3, r12 @ save callee saves in case of GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r3 @ save callee saves in case of GC mov r3, r9 @ pass Thread::Current @ (uint32_t type_idx, Method* method, int32_t component_count, Thread*) bl \entrypoint @@ -861,7 +843,7 @@ END \name .macro FOUR_ARG_DOWNCALL name, entrypoint, return .extern \entrypoint ENTRY \name - SETUP_REFS_ONLY_CALLEE_SAVE_FRAME_PRESERVE_RTEMP2 r12, r3 @ save callee saves in case of GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r12 @ save callee saves in case of GC str r9, [sp, #-16]! @ expand the frame and pass Thread::Current .cfi_adjust_cfa_offset 16 bl \entrypoint @@ -890,7 +872,7 @@ ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCode, RETURN_O */ .extern artGet64StaticFromCode ENTRY art_quick_get64_static - SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case of GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2 @ save callee saves in case of GC ldr r1, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] @ pass referrer mov r2, r9 @ pass Thread::Current bl artGet64StaticFromCode @ (uint32_t field_idx, const Method* referrer, Thread*) @@ -916,7 +898,7 @@ TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCode, RETU */ .extern artGet64InstanceFromCode ENTRY art_quick_get64_instance - SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case of GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2 @ save callee saves in case of GC ldr r2, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] @ pass referrer mov r3, r9 @ pass Thread::Current bl artGet64InstanceFromCode @ (field_idx, Object*, referrer, Thread*) @@ -941,7 +923,7 @@ TWO_ARG_REF_DOWNCALL art_quick_set_obj_static, artSetObjStaticFromCode, RETURN_I */ .extern artSet64StaticFromCode ENTRY art_quick_set64_static - SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r12 @ save callee saves in case of GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1 @ save callee saves in case of GC @ r2:r3 contain the wide argument ldr r1, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] @ pass referrer str r9, [sp, #-16]! @ expand the frame and pass Thread::Current @@ -966,7 +948,7 @@ THREE_ARG_REF_DOWNCALL art_quick_set_obj_instance, artSetObjInstanceFromCode, RE */ .extern artSet64InstanceFromCode ENTRY art_quick_set64_instance - SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r12, lr @ save callee saves in case of GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r12 @ save callee saves in case of GC @ r2:r3 contain the wide argument ldr r12, [sp, #FRAME_SIZE_REFS_ONLY_CALLEE_SAVE] @ pass referrer str r9, [sp, #-12]! @ expand the frame and pass Thread::Current @@ -1087,7 +1069,7 @@ ENTRY art_quick_alloc_object_rosalloc bx lr .Lart_quick_alloc_object_rosalloc_slow_path: - SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 @ save callee saves in case of GC + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2 @ save callee saves in case of GC mov r2, r9 @ pass Thread::Current bl artAllocObjectFromCodeRosAlloc @ (uint32_t type_idx, Method* method, Thread*) RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME @@ -1172,7 +1154,7 @@ ENTRY art_quick_alloc_object_tlab ldr r2, [r2, r0, lsl #COMPRESSED_REFERENCE_SIZE_SHIFT] ALLOC_OBJECT_TLAB_FAST_PATH .Lart_quick_alloc_object_tlab_slow_path .Lart_quick_alloc_object_tlab_slow_path: - SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 // Save callee saves in case of GC. + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2 // Save callee saves in case of GC. mov r2, r9 // Pass Thread::Current. bl artAllocObjectFromCodeTLAB // (uint32_t type_idx, Method* method, Thread*) RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME @@ -1207,7 +1189,7 @@ ENTRY art_quick_alloc_object_region_tlab pop {r0, r1, r3, lr} b .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit .Lart_quick_alloc_object_region_tlab_slow_path: - SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 // Save callee saves in case of GC. + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2 // Save callee saves in case of GC. mov r2, r9 // Pass Thread::Current. bl artAllocObjectFromCodeRegionTLAB // (uint32_t type_idx, Method* method, Thread*) RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME @@ -1227,7 +1209,7 @@ ENTRY art_quick_test_suspend 1: #endif mov r0, rSELF - SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2 @ save callee saves for GC stack crawl + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1 @ save callee saves for GC stack crawl @ TODO: save FPRs to enable access in the debugger? bl artTestSuspendFromCode @ (Thread*) RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN @@ -1235,7 +1217,7 @@ END art_quick_test_suspend ENTRY art_quick_implicit_suspend mov r0, rSELF - SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1, r2 @ save callee saves for stack crawl + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r1 @ save callee saves for stack crawl bl artTestSuspendFromCode @ (Thread*) RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN END art_quick_implicit_suspend @@ -1298,7 +1280,7 @@ END art_quick_imt_conflict_trampoline .extern artQuickResolutionTrampoline ENTRY art_quick_resolution_trampoline - SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME r2, r3 + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME r2 mov r2, r9 @ pass Thread::Current mov r3, sp @ pass SP blx artQuickResolutionTrampoline @ (Method* called, receiver, Thread*, SP) @@ -1403,7 +1385,7 @@ END art_quick_generic_jni_trampoline .extern artQuickToInterpreterBridge ENTRY art_quick_to_interpreter_bridge - SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME r1, r2 + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME r1 mov r1, r9 @ pass Thread::Current mov r2, sp @ pass SP blx artQuickToInterpreterBridge @ (Method* method, Thread*, SP) @@ -1426,7 +1408,7 @@ END art_quick_to_interpreter_bridge .extern artInstrumentationMethodExitFromCode ENTRY art_quick_instrumentation_entry @ Make stack crawlable and clobber r2 and r3 (post saving) - SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME r2, r3 + SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME r2 @ preserve r0 (not normally an arg) knowing there is a spare slot in kRefsAndArgs. str r0, [sp, #4] mov r2, r9 @ pass Thread::Current @@ -1441,7 +1423,7 @@ ENTRY art_quick_instrumentation_entry .global art_quick_instrumentation_exit art_quick_instrumentation_exit: mov lr, #0 @ link register is to here, so clobber with 0 for later checks - SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2, r3 @ set up frame knowing r2 and r3 must be dead on exit + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r2 @ set up frame knowing r2 and r3 must be dead on exit mov r12, sp @ remember bottom of caller's frame push {r0-r1} @ save return value .cfi_adjust_cfa_offset 8 @@ -1480,7 +1462,7 @@ END art_quick_instrumentation_entry */ .extern artDeoptimize ENTRY art_quick_deoptimize - SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r0, r1 + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r0 mov r0, r9 @ Set up args. blx artDeoptimize @ artDeoptimize(Thread*) END art_quick_deoptimize @@ -1491,7 +1473,7 @@ END art_quick_deoptimize */ .extern artDeoptimizeFromCompiledCode ENTRY art_quick_deoptimize_from_compiled_code - SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r0, r1 + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME r0 mov r0, r9 @ Set up args. blx artDeoptimizeFromCompiledCode @ artDeoptimizeFromCompiledCode(Thread*) END art_quick_deoptimize_from_compiled_code diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 1fba09bae3..a6490aed33 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -417,6 +417,12 @@ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_div_zero, artThrowDivZeroFromCode TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_array_bounds, artThrowArrayBoundsFromCode /* + * Called by managed code to create and deliver a StringIndexOutOfBoundsException + * as if thrown from a call to String.charAt(). Arg1 holds index, arg2 holds limit. + */ +TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_string_bounds, artThrowStringBoundsFromCode + + /* * Called by managed code to create and deliver a StackOverflowError. */ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_stack_overflow, artThrowStackOverflowFromCode diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc index 45e33a8500..833ba1b612 100644 --- a/runtime/arch/mips/entrypoints_init_mips.cc +++ b/runtime/arch/mips/entrypoints_init_mips.cc @@ -268,6 +268,8 @@ void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) { static_assert(!IsDirectEntrypoint(kQuickThrowNullPointer), "Non-direct C stub marked direct."); qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow; static_assert(!IsDirectEntrypoint(kQuickThrowStackOverflow), "Non-direct C stub marked direct."); + qpoints->pThrowStringBounds = art_quick_throw_string_bounds; + static_assert(!IsDirectEntrypoint(kQuickThrowStringBounds), "Non-direct C stub marked direct."); // Deoptimization from compiled code. qpoints->pDeoptimize = art_quick_deoptimize_from_compiled_code; diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 3ee26afc4f..bb89674caf 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -529,6 +529,18 @@ ENTRY art_quick_throw_array_bounds END art_quick_throw_array_bounds /* + * Called by managed code to create and deliver a StringIndexOutOfBoundsException + * as if thrown from a call to String.charAt(). + */ + .extern artThrowStringBoundsFromCode +ENTRY art_quick_throw_string_bounds + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME + la $t9, artThrowStringBoundsFromCode + jalr $zero, $t9 # artThrowStringBoundsFromCode(index, limit, Thread*) + move $a2, rSELF # pass Thread::Current +END art_quick_throw_string_bounds + + /* * Called by managed code to create and deliver a StackOverflowError. */ .extern artThrowStackOverflowFromCode diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index 8f1a35a693..78ac748e32 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -617,6 +617,19 @@ ENTRY art_quick_throw_array_bounds END art_quick_throw_array_bounds /* + * Called by managed code to create and deliver a StringIndexOutOfBoundsException + * as if thrown from a call to String.charAt(). + */ + .extern artThrowStringBoundsFromCode +ENTRY art_quick_throw_string_bounds +.Lart_quick_throw_string_bounds_gp_set: + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME + dla $t9, artThrowStringBoundsFromCode + jalr $zero, $t9 # artThrowStringBoundsFromCode(index, limit, Thread*) + move $a2, rSELF # pass Thread::Current +END art_quick_throw_string_bounds + + /* * Called by managed code to create and deliver a StackOverflowError. */ .extern artThrowStackOverflowFromCode diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 2d7f664809..b3dd4545f4 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -82,7 +82,7 @@ MACRO2(SETUP_REFS_ONLY_CALLEE_SAVE_FRAME_PRESERVE_GOT_REG, got_reg, temp_reg) PUSH edi // Save callee saves (ebx is saved/restored by the upcall) PUSH esi PUSH ebp - pushl REG_VAR(got_reg) // Save got_reg + PUSH RAW_VAR(got_reg) // Save got_reg subl MACRO_LITERAL(8), %esp // Grow stack by 2 words. CFI_ADJUST_CFA_OFFSET(8) @@ -97,6 +97,7 @@ MACRO2(SETUP_REFS_ONLY_CALLEE_SAVE_FRAME_PRESERVE_GOT_REG, got_reg, temp_reg) movl %esp, %fs:THREAD_TOP_QUICK_FRAME_OFFSET // Restore got_reg. movl 12(%esp), REG_VAR(got_reg) + CFI_RESTORE(RAW_VAR(got_reg)) // Ugly compile-time check, but we only have the preprocessor. // Last +4: implicit return address pushed on stack when caller made call. @@ -310,6 +311,12 @@ ONE_ARG_RUNTIME_EXCEPTION art_quick_throw_no_such_method, artThrowNoSuchMethodFr TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_array_bounds, artThrowArrayBoundsFromCode /* + * Called by managed code to create and deliver a StringIndexOutOfBoundsException + * as if thrown from a call to String.charAt(). Arg1 holds index, arg2 holds limit. + */ +TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_string_bounds, artThrowStringBoundsFromCode + + /* * All generated callsites for interface invokes and invocation slow paths will load arguments * as usual - except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain * the method_idx. This wrapper will save arg1-arg3 and call the appropriate C helper. diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index 8064ed696f..205307ce67 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -336,6 +336,12 @@ ONE_ARG_RUNTIME_EXCEPTION art_quick_throw_no_such_method, artThrowNoSuchMethodFr TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_array_bounds, artThrowArrayBoundsFromCode /* + * Called by managed code to create and deliver a StringIndexOutOfBoundsException + * as if thrown from a call to String.charAt(). Arg1 holds index, arg2 holds limit. + */ +TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_string_bounds, artThrowStringBoundsFromCode + + /* * All generated callsites for interface invokes and invocation slow paths will load arguments * as usual - except instead of loading arg0/rdi with the target Method*, arg0/rdi will contain * the method_idx. This wrapper will save arg1-arg3, and call the appropriate C helper. diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h index 98d33453e2..a102858acc 100644 --- a/runtime/art_field-inl.h +++ b/runtime/art_field-inl.h @@ -33,9 +33,10 @@ namespace art { +template<ReadBarrierOption kReadBarrierOption> inline mirror::Class* ArtField::GetDeclaringClass() { GcRootSource gc_root_source(this); - mirror::Class* result = declaring_class_.Read(&gc_root_source); + mirror::Class* result = declaring_class_.Read<kReadBarrierOption>(&gc_root_source); DCHECK(result != nullptr); DCHECK(result->IsLoaded() || result->IsErroneous()) << result->GetStatus(); return result; diff --git a/runtime/art_field.h b/runtime/art_field.h index b64b70fa8d..aaccbf3699 100644 --- a/runtime/art_field.h +++ b/runtime/art_field.h @@ -41,6 +41,7 @@ class ArtField FINAL { public: ArtField(); + template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier> mirror::Class* GetDeclaringClass() SHARED_REQUIRES(Locks::mutator_lock_); void SetDeclaringClass(mirror::Class *new_declaring_class) diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 7647ad6e57..26450c41c7 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -395,8 +395,9 @@ inline mirror::DexCache* ArtMethod::GetDexCache() { return GetDeclaringClass()->GetDexCache(); } +template<ReadBarrierOption kReadBarrierOption> inline bool ArtMethod::IsProxyMethod() { - return GetDeclaringClass()->IsProxyClass(); + return GetDeclaringClass<kReadBarrierOption>()->IsProxyClass(); } inline ArtMethod* ArtMethod::GetInterfaceMethodIfProxy(size_t pointer_size) { @@ -438,24 +439,24 @@ inline mirror::Class* ArtMethod::GetReturnType(bool resolve, size_t ptr_size) { return type; } -template<typename RootVisitorType> +template<ReadBarrierOption kReadBarrierOption, typename RootVisitorType> void ArtMethod::VisitRoots(RootVisitorType& visitor, size_t pointer_size) { - ArtMethod* interface_method = nullptr; - mirror::Class* klass = declaring_class_.Read(); - if (LIKELY(klass != nullptr)) { + if (LIKELY(!declaring_class_.IsNull())) { + visitor.VisitRoot(declaring_class_.AddressWithoutBarrier()); + mirror::Class* klass = declaring_class_.Read<kReadBarrierOption>(); if (UNLIKELY(klass->IsProxyClass())) { // For normal methods, dex cache shortcuts will be visited through the declaring class. // However, for proxies we need to keep the interface method alive, so we visit its roots. - interface_method = mirror::DexCache::GetElementPtrSize( + ArtMethod* interface_method = mirror::DexCache::GetElementPtrSize( GetDexCacheResolvedMethods(pointer_size), GetDexMethodIndex(), pointer_size); DCHECK(interface_method != nullptr); DCHECK_EQ(interface_method, - Runtime::Current()->GetClassLinker()->FindMethodForProxy(klass, this)); + Runtime::Current()->GetClassLinker()->FindMethodForProxy<kReadBarrierOption>( + klass, this)); interface_method->VisitRoots(visitor, pointer_size); } - visitor.VisitRoot(declaring_class_.AddressWithoutBarrier()); // We know we don't have profiling information if the class hasn't been verified. Note // that this check also ensures the IsNative call can be made, as IsNative expects a fully // created class (and not a retired one). @@ -463,7 +464,7 @@ void ArtMethod::VisitRoots(RootVisitorType& visitor, size_t pointer_size) { // Runtime methods and native methods use the same field as the profiling info for // storing their own data (jni entrypoint for native methods, and ImtConflictTable for // some runtime methods). - if (!IsNative() && !IsRuntimeMethod()) { + if (!IsNative<kReadBarrierOption>() && !IsRuntimeMethod()) { ProfilingInfo* profiling_info = GetProfilingInfo(pointer_size); if (profiling_info != nullptr) { profiling_info->VisitRoots(visitor); diff --git a/runtime/art_method.h b/runtime/art_method.h index b65cb23516..90b2406a1d 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -356,6 +356,7 @@ class ArtMethod FINAL { return (GetAccessFlags() & kAccSynthetic) != 0; } + template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier> bool IsProxyMethod() SHARED_REQUIRES(Locks::mutator_lock_); bool SkipAccessChecks() { @@ -580,7 +581,7 @@ class ArtMethod FINAL { SHARED_REQUIRES(Locks::mutator_lock_); // NO_THREAD_SAFETY_ANALYSIS since we don't know what the callback requires. - template<typename RootVisitorType> + template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename RootVisitorType> void VisitRoots(RootVisitorType& visitor, size_t pointer_size) NO_THREAD_SAFETY_ANALYSIS; const DexFile* GetDexFile() SHARED_REQUIRES(Locks::mutator_lock_); diff --git a/runtime/asm_support.h b/runtime/asm_support.h index 8eb3742b61..2d702f6af7 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -127,32 +127,32 @@ ADD_TEST_EQ(THREAD_TOP_QUICK_FRAME_OFFSET, ADD_TEST_EQ(THREAD_SELF_OFFSET, art::Thread::SelfOffset<__SIZEOF_POINTER__>().Int32Value()) -// Offset of field Thread::tlsPtr_.thread_local_objects. -#define THREAD_LOCAL_OBJECTS_OFFSET (THREAD_CARD_TABLE_OFFSET + 168 * __SIZEOF_POINTER__) -ADD_TEST_EQ(THREAD_LOCAL_OBJECTS_OFFSET, - art::Thread::ThreadLocalObjectsOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.thread_local_pos. -#define THREAD_LOCAL_POS_OFFSET (THREAD_LOCAL_OBJECTS_OFFSET + 2 * __SIZEOF_POINTER__) +#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 168 * __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_LOCAL_POS_OFFSET, art::Thread::ThreadLocalPosOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.thread_local_end. #define THREAD_LOCAL_END_OFFSET (THREAD_LOCAL_POS_OFFSET + __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_LOCAL_END_OFFSET, art::Thread::ThreadLocalEndOffset<__SIZEOF_POINTER__>().Int32Value()) +// Offset of field Thread::tlsPtr_.thread_local_objects. +#define THREAD_LOCAL_OBJECTS_OFFSET (THREAD_LOCAL_END_OFFSET + __SIZEOF_POINTER__) +ADD_TEST_EQ(THREAD_LOCAL_OBJECTS_OFFSET, + art::Thread::ThreadLocalObjectsOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.mterp_current_ibase. -#define THREAD_CURRENT_IBASE_OFFSET (THREAD_LOCAL_POS_OFFSET + 2 * __SIZEOF_POINTER__) +#define THREAD_CURRENT_IBASE_OFFSET (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_CURRENT_IBASE_OFFSET, art::Thread::MterpCurrentIBaseOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.mterp_default_ibase. -#define THREAD_DEFAULT_IBASE_OFFSET (THREAD_LOCAL_POS_OFFSET + 3 * __SIZEOF_POINTER__) +#define THREAD_DEFAULT_IBASE_OFFSET (THREAD_CURRENT_IBASE_OFFSET + __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_DEFAULT_IBASE_OFFSET, art::Thread::MterpDefaultIBaseOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.mterp_alt_ibase. -#define THREAD_ALT_IBASE_OFFSET (THREAD_LOCAL_POS_OFFSET + 4 * __SIZEOF_POINTER__) +#define THREAD_ALT_IBASE_OFFSET (THREAD_DEFAULT_IBASE_OFFSET + __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_ALT_IBASE_OFFSET, art::Thread::MterpAltIBaseOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.rosalloc_runs. -#define THREAD_ROSALLOC_RUNS_OFFSET (THREAD_LOCAL_POS_OFFSET + 5 * __SIZEOF_POINTER__) +#define THREAD_ROSALLOC_RUNS_OFFSET (THREAD_ALT_IBASE_OFFSET + __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_ROSALLOC_RUNS_OFFSET, art::Thread::RosAllocRunsOffset<__SIZEOF_POINTER__>().Int32Value()) // Offset of field Thread::tlsPtr_.thread_local_alloc_stack_top. diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index f3e260be56..f2575f702f 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -225,6 +225,34 @@ inline mirror::Class* ClassLinker::GetClassRoot(ClassRoot class_root) return klass; } +template<ReadBarrierOption kReadBarrierOption> +ArtMethod* ClassLinker::FindMethodForProxy(mirror::Class* proxy_class, ArtMethod* proxy_method) { + DCHECK(proxy_class->IsProxyClass()); + DCHECK(proxy_method->IsProxyMethod<kReadBarrierOption>()); + { + Thread* const self = Thread::Current(); + ReaderMutexLock mu(self, dex_lock_); + // Locate the dex cache of the original interface/Object + for (const DexCacheData& data : dex_caches_) { + if (!self->IsJWeakCleared(data.weak_root) && + proxy_method->HasSameDexCacheResolvedTypes(data.resolved_types, + image_pointer_size_)) { + mirror::DexCache* dex_cache = down_cast<mirror::DexCache*>( + self->DecodeJObject(data.weak_root)); + if (dex_cache != nullptr) { + ArtMethod* resolved_method = dex_cache->GetResolvedMethod( + proxy_method->GetDexMethodIndex(), image_pointer_size_); + CHECK(resolved_method != nullptr); + return resolved_method; + } + } + } + } + LOG(FATAL) << "Didn't find dex cache for " << PrettyClass(proxy_class) << " " + << PrettyMethod(proxy_method); + UNREACHABLE(); +} + } // namespace art #endif // ART_RUNTIME_CLASS_LINKER_INL_H_ diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 4406c0aee4..cb34d8a121 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -4277,33 +4277,6 @@ std::string ClassLinker::GetDescriptorForProxy(mirror::Class* proxy_class) { return DotToDescriptor(name->ToModifiedUtf8().c_str()); } -ArtMethod* ClassLinker::FindMethodForProxy(mirror::Class* proxy_class, ArtMethod* proxy_method) { - DCHECK(proxy_class->IsProxyClass()); - DCHECK(proxy_method->IsProxyMethod()); - { - Thread* const self = Thread::Current(); - ReaderMutexLock mu(self, dex_lock_); - // Locate the dex cache of the original interface/Object - for (const DexCacheData& data : dex_caches_) { - if (!self->IsJWeakCleared(data.weak_root) && - proxy_method->HasSameDexCacheResolvedTypes(data.resolved_types, - image_pointer_size_)) { - mirror::DexCache* dex_cache = down_cast<mirror::DexCache*>( - self->DecodeJObject(data.weak_root)); - if (dex_cache != nullptr) { - ArtMethod* resolved_method = dex_cache->GetResolvedMethod( - proxy_method->GetDexMethodIndex(), image_pointer_size_); - CHECK(resolved_method != nullptr); - return resolved_method; - } - } - } - } - LOG(FATAL) << "Didn't find dex cache for " << PrettyClass(proxy_class) << " " - << PrettyMethod(proxy_method); - UNREACHABLE(); -} - void ClassLinker::CreateProxyConstructor(Handle<mirror::Class> klass, ArtMethod* out) { // Create constructor for Proxy that must initialize the method. CHECK_EQ(GetClassRoot(kJavaLangReflectProxy)->NumDirectMethods(), 18u); diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 4832d32184..d6822c5225 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -483,6 +483,7 @@ class ClassLinker { SHARED_REQUIRES(Locks::mutator_lock_); std::string GetDescriptorForProxy(mirror::Class* proxy_class) SHARED_REQUIRES(Locks::mutator_lock_); + template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier> ArtMethod* FindMethodForProxy(mirror::Class* proxy_class, ArtMethod* proxy_method) REQUIRES(!dex_lock_) SHARED_REQUIRES(Locks::mutator_lock_); diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index f58af5a8da..3509d9aef9 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -284,7 +284,8 @@ std::unique_ptr<const DexFile> CommonRuntimeTestImpl::LoadExpectSingleDexFile( std::vector<std::unique_ptr<const DexFile>> dex_files; std::string error_msg; MemMap::Init(); - if (!DexFile::Open(location, location, &error_msg, &dex_files)) { + static constexpr bool kVerifyChecksum = true; + if (!DexFile::Open(location, location, kVerifyChecksum, &error_msg, &dex_files)) { LOG(FATAL) << "Could not open .dex file '" << location << "': " << error_msg << "\n"; UNREACHABLE(); } else { @@ -418,26 +419,6 @@ void CommonRuntimeTestImpl::TearDown() { (*icu_cleanup_fn)(); Runtime::Current()->GetHeap()->VerifyHeap(); // Check for heap corruption after the test - - // Manually closing the JNI libraries. - // Runtime does not support repeatedly doing JNI->CreateVM, thus we need to manually clean up the - // dynamic linking loader so that gtests would not fail. - // Bug: 25785594 - if (runtime_->IsStarted()) { - { - // We retrieve the handle by calling dlopen on the library. To close it, we need to call - // dlclose twice, the first time to undo our dlopen and the second time to actually unload it. - // See man dlopen. - void* handle = dlopen("libjavacore.so", RTLD_LAZY); - dlclose(handle); - CHECK_EQ(0, dlclose(handle)); - } - { - void* handle = dlopen("libopenjdkd.so", RTLD_LAZY); - dlclose(handle); - CHECK_EQ(0, dlclose(handle)); - } - } } static std::string GetDexFileName(const std::string& jar_prefix, bool host) { @@ -500,9 +481,11 @@ std::string CommonRuntimeTestImpl::GetTestDexFileName(const char* name) { std::vector<std::unique_ptr<const DexFile>> CommonRuntimeTestImpl::OpenTestDexFiles( const char* name) { std::string filename = GetTestDexFileName(name); + static constexpr bool kVerifyChecksum = true; std::string error_msg; std::vector<std::unique_ptr<const DexFile>> dex_files; - bool success = DexFile::Open(filename.c_str(), filename.c_str(), &error_msg, &dex_files); + bool success = DexFile::Open( + filename.c_str(), filename.c_str(), kVerifyChecksum, &error_msg, &dex_files); CHECK(success) << "Failed to open '" << filename << "': " << error_msg; for (auto& dex_file : dex_files) { CHECK_EQ(PROT_READ, dex_file->GetPermissions()); diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc index 75cce424e9..4f705f2056 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -654,6 +654,13 @@ void ThrowStackOverflowError(Thread* self) { } } +// StringIndexOutOfBoundsException + +void ThrowStringIndexOutOfBoundsException(int index, int length) { + ThrowException("Ljava/lang/StringIndexOutOfBoundsException;", nullptr, + StringPrintf("length=%d; index=%d", length, index).c_str()); +} + // VerifyError void ThrowVerifyError(mirror::Class* referrer, const char* fmt, ...) { diff --git a/runtime/common_throws.h b/runtime/common_throws.h index c3a1f09db3..7a335859e5 100644 --- a/runtime/common_throws.h +++ b/runtime/common_throws.h @@ -211,6 +211,11 @@ void ThrowRuntimeException(const char* fmt, ...) void ThrowStackOverflowError(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) COLD_ATTR; +// StringIndexOutOfBoundsException + +void ThrowStringIndexOutOfBoundsException(int index, int length) + SHARED_REQUIRES(Locks::mutator_lock_) COLD_ATTR; + // VerifyError void ThrowVerifyError(mirror::Class* referrer, const char* fmt, ...) diff --git a/runtime/dex_file-inl.h b/runtime/dex_file-inl.h index 4e6c3ca279..108a5af908 100644 --- a/runtime/dex_file-inl.h +++ b/runtime/dex_file-inl.h @@ -38,10 +38,88 @@ inline const char* DexFile::GetStringDataAndUtf16Length(const StringId& string_i return reinterpret_cast<const char*>(ptr); } +inline const char* DexFile::GetStringData(const StringId& string_id) const { + uint32_t ignored; + return GetStringDataAndUtf16Length(string_id, &ignored); +} + +inline const char* DexFile::StringDataAndUtf16LengthByIdx(uint32_t idx, + uint32_t* utf16_length) const { + if (idx == kDexNoIndex) { + *utf16_length = 0; + return nullptr; + } + const StringId& string_id = GetStringId(idx); + return GetStringDataAndUtf16Length(string_id, utf16_length); +} + +inline const char* DexFile::StringDataByIdx(uint32_t idx) const { + uint32_t unicode_length; + return StringDataAndUtf16LengthByIdx(idx, &unicode_length); +} + +inline const char* DexFile::StringByTypeIdx(uint32_t idx, uint32_t* unicode_length) const { + const TypeId& type_id = GetTypeId(idx); + return StringDataAndUtf16LengthByIdx(type_id.descriptor_idx_, unicode_length); +} + +inline const char* DexFile::StringByTypeIdx(uint32_t idx) const { + const TypeId& type_id = GetTypeId(idx); + return StringDataByIdx(type_id.descriptor_idx_); +} + +inline const char* DexFile::GetTypeDescriptor(const TypeId& type_id) const { + return StringDataByIdx(type_id.descriptor_idx_); +} + +inline const char* DexFile::GetFieldTypeDescriptor(const FieldId& field_id) const { + const DexFile::TypeId& type_id = GetTypeId(field_id.type_idx_); + return GetTypeDescriptor(type_id); +} + +inline const char* DexFile::GetFieldName(const FieldId& field_id) const { + return StringDataByIdx(field_id.name_idx_); +} + +inline const char* DexFile::GetMethodDeclaringClassDescriptor(const MethodId& method_id) const { + const DexFile::TypeId& type_id = GetTypeId(method_id.class_idx_); + return GetTypeDescriptor(type_id); +} + inline const Signature DexFile::GetMethodSignature(const MethodId& method_id) const { return Signature(this, GetProtoId(method_id.proto_idx_)); } +inline const char* DexFile::GetMethodName(const MethodId& method_id) const { + return StringDataByIdx(method_id.name_idx_); +} + +inline const char* DexFile::GetMethodShorty(uint32_t idx) const { + return StringDataByIdx(GetProtoId(GetMethodId(idx).proto_idx_).shorty_idx_); +} + +inline const char* DexFile::GetMethodShorty(const MethodId& method_id) const { + return StringDataByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_); +} + +inline const char* DexFile::GetMethodShorty(const MethodId& method_id, uint32_t* length) const { + // Using the UTF16 length is safe here as shorties are guaranteed to be ASCII characters. + return StringDataAndUtf16LengthByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_, length); +} + +inline const char* DexFile::GetClassDescriptor(const ClassDef& class_def) const { + return StringByTypeIdx(class_def.class_idx_); +} + +inline const char* DexFile::GetReturnTypeDescriptor(const ProtoId& proto_id) const { + return StringByTypeIdx(proto_id.return_type_idx_); +} + +inline const char* DexFile::GetShorty(uint32_t proto_idx) const { + const ProtoId& proto_id = GetProtoId(proto_idx); + return StringDataByIdx(proto_id.shorty_idx_); +} + inline const DexFile::TryItem* DexFile::GetTryItems(const CodeItem& code_item, uint32_t offset) { const uint16_t* insns_end_ = &code_item.insns_[code_item.insns_size_in_code_units_]; return reinterpret_cast<const TryItem*> diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index 05c95e069e..5a203afd1a 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -109,7 +109,7 @@ bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* } if (IsDexMagic(magic)) { std::unique_ptr<const DexFile> dex_file( - DexFile::OpenFile(fd.release(), filename, false, error_msg)); + DexFile::OpenFile(fd.release(), filename, false, false, error_msg)); if (dex_file.get() == nullptr) { return false; } @@ -120,7 +120,10 @@ bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* return false; } -bool DexFile::Open(const char* filename, const char* location, std::string* error_msg, +bool DexFile::Open(const char* filename, + const char* location, + bool verify_checksum, + std::string* error_msg, std::vector<std::unique_ptr<const DexFile>>* dex_files) { ScopedTrace trace(std::string("Open dex file ") + location); DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr"; @@ -131,10 +134,13 @@ bool DexFile::Open(const char* filename, const char* location, std::string* erro return false; } if (IsZipMagic(magic)) { - return DexFile::OpenZip(fd.release(), location, error_msg, dex_files); + return DexFile::OpenZip(fd.release(), location, verify_checksum, error_msg, dex_files); } if (IsDexMagic(magic)) { - std::unique_ptr<const DexFile> dex_file(DexFile::OpenFile(fd.release(), location, true, + std::unique_ptr<const DexFile> dex_file(DexFile::OpenFile(fd.release(), + location, + /* verify */ true, + verify_checksum, error_msg)); if (dex_file.get() != nullptr) { dex_files->push_back(std::move(dex_file)); @@ -207,6 +213,7 @@ std::unique_ptr<const DexFile> DexFile::Open(const uint8_t* base, size_t size, uint32_t location_checksum, const OatDexFile* oat_dex_file, bool verify, + bool verify_checksum, std::string* error_msg) { ScopedTrace trace(std::string("Open dex file from RAM ") + location); std::unique_ptr<const DexFile> dex_file = OpenMemory(base, @@ -220,6 +227,7 @@ std::unique_ptr<const DexFile> DexFile::Open(const uint8_t* base, size_t size, dex_file->Begin(), dex_file->Size(), location.c_str(), + verify_checksum, error_msg)) { return nullptr; } @@ -227,7 +235,10 @@ std::unique_ptr<const DexFile> DexFile::Open(const uint8_t* base, size_t size, return dex_file; } -std::unique_ptr<const DexFile> DexFile::OpenFile(int fd, const char* location, bool verify, +std::unique_ptr<const DexFile> DexFile::OpenFile(int fd, + const char* location, + bool verify, + bool verify_checksum, std::string* error_msg) { ScopedTrace trace(std::string("Open dex file ") + location); CHECK(location != nullptr); @@ -276,7 +287,9 @@ std::unique_ptr<const DexFile> DexFile::OpenFile(int fd, const char* location, b } if (verify && !DexFileVerifier::Verify(dex_file.get(), dex_file->Begin(), dex_file->Size(), - location, error_msg)) { + location, + verify_checksum, + error_msg)) { return nullptr; } @@ -285,7 +298,10 @@ std::unique_ptr<const DexFile> DexFile::OpenFile(int fd, const char* location, b const char* DexFile::kClassesDex = "classes.dex"; -bool DexFile::OpenZip(int fd, const std::string& location, std::string* error_msg, +bool DexFile::OpenZip(int fd, + const std::string& location, + bool verify_checksum, + std::string* error_msg, std::vector<std::unique_ptr<const DexFile>>* dex_files) { ScopedTrace trace("Dex file open Zip " + std::string(location)); DCHECK(dex_files != nullptr) << "DexFile::OpenZip: out-param is nullptr"; @@ -294,7 +310,7 @@ bool DexFile::OpenZip(int fd, const std::string& location, std::string* error_ms DCHECK(!error_msg->empty()); return false; } - return DexFile::OpenFromZip(*zip_archive, location, error_msg, dex_files); + return DexFile::OpenFromZip(*zip_archive, location, verify_checksum, error_msg, dex_files); } std::unique_ptr<const DexFile> DexFile::OpenMemory(const std::string& location, @@ -310,8 +326,11 @@ std::unique_ptr<const DexFile> DexFile::OpenMemory(const std::string& location, error_msg); } -std::unique_ptr<const DexFile> DexFile::Open(const ZipArchive& zip_archive, const char* entry_name, - const std::string& location, std::string* error_msg, +std::unique_ptr<const DexFile> DexFile::Open(const ZipArchive& zip_archive, + const char* entry_name, + const std::string& location, + bool verify_checksum, + std::string* error_msg, ZipOpenErrorCode* error_code) { ScopedTrace trace("Dex file open from Zip Archive " + std::string(location)); CHECK(!location.empty()); @@ -342,7 +361,9 @@ std::unique_ptr<const DexFile> DexFile::Open(const ZipArchive& zip_archive, cons } CHECK(dex_file->IsReadOnly()) << location; if (!DexFileVerifier::Verify(dex_file.get(), dex_file->Begin(), dex_file->Size(), - location.c_str(), error_msg)) { + location.c_str(), + verify_checksum, + error_msg)) { *error_code = ZipOpenErrorCode::kVerifyError; return nullptr; } @@ -356,14 +377,16 @@ std::unique_ptr<const DexFile> DexFile::Open(const ZipArchive& zip_archive, cons // seems an excessive number. static constexpr size_t kWarnOnManyDexFilesThreshold = 100; -bool DexFile::OpenFromZip(const ZipArchive& zip_archive, const std::string& location, +bool DexFile::OpenFromZip(const ZipArchive& zip_archive, + const std::string& location, + bool verify_checksum, std::string* error_msg, std::vector<std::unique_ptr<const DexFile>>* dex_files) { ScopedTrace trace("Dex file open from Zip " + std::string(location)); DCHECK(dex_files != nullptr) << "DexFile::OpenFromZip: out-param is nullptr"; ZipOpenErrorCode error_code; - std::unique_ptr<const DexFile> dex_file(Open(zip_archive, kClassesDex, location, error_msg, - &error_code)); + std::unique_ptr<const DexFile> dex_file( + Open(zip_archive, kClassesDex, location, verify_checksum, error_msg, &error_code)); if (dex_file.get() == nullptr) { return false; } else { @@ -378,8 +401,8 @@ bool DexFile::OpenFromZip(const ZipArchive& zip_archive, const std::string& loca for (size_t i = 1; ; ++i) { std::string name = GetMultiDexClassesDexName(i); std::string fake_location = GetMultiDexLocation(i, location.c_str()); - std::unique_ptr<const DexFile> next_dex_file(Open(zip_archive, name.c_str(), fake_location, - error_msg, &error_code)); + std::unique_ptr<const DexFile> next_dex_file( + Open(zip_archive, name.c_str(), fake_location, verify_checksum, error_msg, &error_code)); if (next_dex_file.get() == nullptr) { if (error_code != ZipOpenErrorCode::kEntryNotFound) { LOG(WARNING) << error_msg; diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 638821bfb7..3dffe4b6f1 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -416,7 +416,10 @@ class DexFile { static bool GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg); // Opens .dex files found in the container, guessing the container format based on file extension. - static bool Open(const char* filename, const char* location, std::string* error_msg, + static bool Open(const char* filename, + const char* location, + bool verify_checksum, + std::string* error_msg, std::vector<std::unique_ptr<const DexFile>>* dex_files); // Checks whether the given file has the dex magic, or is a zip file with a classes.dex entry. @@ -429,10 +432,13 @@ class DexFile { uint32_t location_checksum, const OatDexFile* oat_dex_file, bool verify, + bool verify_checksum, std::string* error_msg); // Open all classesXXX.dex files from a zip archive. - static bool OpenFromZip(const ZipArchive& zip_archive, const std::string& location, + static bool OpenFromZip(const ZipArchive& zip_archive, + const std::string& location, + bool verify_checksum, std::string* error_msg, std::vector<std::unique_ptr<const DexFile>>* dex_files); @@ -522,25 +528,12 @@ class DexFile { // as the string length of the string data. const char* GetStringDataAndUtf16Length(const StringId& string_id, uint32_t* utf16_length) const; - const char* GetStringData(const StringId& string_id) const { - uint32_t ignored; - return GetStringDataAndUtf16Length(string_id, &ignored); - } + const char* GetStringData(const StringId& string_id) const; // Index version of GetStringDataAndUtf16Length. - const char* StringDataAndUtf16LengthByIdx(uint32_t idx, uint32_t* utf16_length) const { - if (idx == kDexNoIndex) { - *utf16_length = 0; - return nullptr; - } - const StringId& string_id = GetStringId(idx); - return GetStringDataAndUtf16Length(string_id, utf16_length); - } + const char* StringDataAndUtf16LengthByIdx(uint32_t idx, uint32_t* utf16_length) const; - const char* StringDataByIdx(uint32_t idx) const { - uint32_t unicode_length; - return StringDataAndUtf16LengthByIdx(idx, &unicode_length); - } + const char* StringDataByIdx(uint32_t idx) const; // Looks up a string id for a given modified utf8 string. const StringId* FindStringId(const char* string) const; @@ -571,20 +564,12 @@ class DexFile { } // Get the descriptor string associated with a given type index. - const char* StringByTypeIdx(uint32_t idx, uint32_t* unicode_length) const { - const TypeId& type_id = GetTypeId(idx); - return StringDataAndUtf16LengthByIdx(type_id.descriptor_idx_, unicode_length); - } + const char* StringByTypeIdx(uint32_t idx, uint32_t* unicode_length) const; - const char* StringByTypeIdx(uint32_t idx) const { - const TypeId& type_id = GetTypeId(idx); - return StringDataByIdx(type_id.descriptor_idx_); - } + const char* StringByTypeIdx(uint32_t idx) const; // Returns the type descriptor string of a type id. - const char* GetTypeDescriptor(const TypeId& type_id) const { - return StringDataByIdx(type_id.descriptor_idx_); - } + const char* GetTypeDescriptor(const TypeId& type_id) const; // Looks up a type for the given string index const TypeId* FindTypeId(uint32_t string_idx) const; @@ -619,15 +604,10 @@ class DexFile { } // Returns the class descriptor string of a field id. - const char* GetFieldTypeDescriptor(const FieldId& field_id) const { - const DexFile::TypeId& type_id = GetTypeId(field_id.type_idx_); - return GetTypeDescriptor(type_id); - } + const char* GetFieldTypeDescriptor(const FieldId& field_id) const; // Returns the name of a field id. - const char* GetFieldName(const FieldId& field_id) const { - return StringDataByIdx(field_id.name_idx_); - } + const char* GetFieldName(const FieldId& field_id) const; // Returns the number of method identifiers in the .dex file. size_t NumMethodIds() const { @@ -653,10 +633,7 @@ class DexFile { const DexFile::ProtoId& signature) const; // Returns the declaring class descriptor string of a method id. - const char* GetMethodDeclaringClassDescriptor(const MethodId& method_id) const { - const DexFile::TypeId& type_id = GetTypeId(method_id.class_idx_); - return GetTypeDescriptor(type_id); - } + const char* GetMethodDeclaringClassDescriptor(const MethodId& method_id) const; // Returns the prototype of a method id. const ProtoId& GetMethodPrototype(const MethodId& method_id) const { @@ -667,23 +644,15 @@ class DexFile { const Signature GetMethodSignature(const MethodId& method_id) const; // Returns the name of a method id. - const char* GetMethodName(const MethodId& method_id) const { - return StringDataByIdx(method_id.name_idx_); - } + const char* GetMethodName(const MethodId& method_id) const; // Returns the shorty of a method by its index. - const char* GetMethodShorty(uint32_t idx) const { - return StringDataByIdx(GetProtoId(GetMethodId(idx).proto_idx_).shorty_idx_); - } + const char* GetMethodShorty(uint32_t idx) const; // Returns the shorty of a method id. - const char* GetMethodShorty(const MethodId& method_id) const { - return StringDataByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_); - } - const char* GetMethodShorty(const MethodId& method_id, uint32_t* length) const { - // Using the UTF16 length is safe here as shorties are guaranteed to be ASCII characters. - return StringDataAndUtf16LengthByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_, length); - } + const char* GetMethodShorty(const MethodId& method_id) const; + const char* GetMethodShorty(const MethodId& method_id, uint32_t* length) const; + // Returns the number of class definitions in the .dex file. uint32_t NumClassDefs() const { DCHECK(header_ != nullptr) << GetLocation(); @@ -703,9 +672,7 @@ class DexFile { } // Returns the class descriptor string of a class definition. - const char* GetClassDescriptor(const ClassDef& class_def) const { - return StringByTypeIdx(class_def.class_idx_); - } + const char* GetClassDescriptor(const ClassDef& class_def) const; // Looks up a class definition by its class descriptor. Hash must be // ComputeModifiedUtf8Hash(descriptor). @@ -743,9 +710,7 @@ class DexFile { } } - const char* GetReturnTypeDescriptor(const ProtoId& proto_id) const { - return StringByTypeIdx(proto_id.return_type_idx_); - } + const char* GetReturnTypeDescriptor(const ProtoId& proto_id) const; // Returns the number of prototype identifiers in the .dex file. size_t NumProtoIds() const { @@ -782,10 +747,7 @@ class DexFile { const Signature CreateSignature(const StringPiece& signature) const; // Returns the short form method descriptor for the given prototype. - const char* GetShorty(uint32_t proto_idx) const { - const ProtoId& proto_id = GetProtoId(proto_idx); - return StringDataByIdx(proto_id.shorty_idx_); - } + const char* GetShorty(uint32_t proto_idx) const; const TypeList* GetProtoParameters(const ProtoId& proto_id) const { if (proto_id.parameters_off_ == 0) { @@ -1177,11 +1139,17 @@ class DexFile { private: // Opens a .dex file - static std::unique_ptr<const DexFile> OpenFile(int fd, const char* location, - bool verify, std::string* error_msg); + static std::unique_ptr<const DexFile> OpenFile(int fd, + const char* location, + bool verify, + bool verify_checksum, + std::string* error_msg); // Opens dex files from within a .jar, .zip, or .apk file - static bool OpenZip(int fd, const std::string& location, std::string* error_msg, + static bool OpenZip(int fd, + const std::string& location, + bool verify_checksum, + std::string* error_msg, std::vector<std::unique_ptr<const DexFile>>* dex_files); enum class ZipOpenErrorCode { // private @@ -1195,8 +1163,11 @@ class DexFile { // Opens .dex file from the entry_name in a zip archive. error_code is undefined when non-null // return. - static std::unique_ptr<const DexFile> Open(const ZipArchive& zip_archive, const char* entry_name, - const std::string& location, std::string* error_msg, + static std::unique_ptr<const DexFile> Open(const ZipArchive& zip_archive, + const char* entry_name, + const std::string& location, + bool verify_checksum, + std::string* error_msg, ZipOpenErrorCode* error_code); // Opens a .dex file at the given address backed by a MemMap diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc index 796701d86b..4f8e6f1fc0 100644 --- a/runtime/dex_file_test.cc +++ b/runtime/dex_file_test.cc @@ -154,9 +154,10 @@ static std::unique_ptr<const DexFile> OpenDexFileBase64(const char* base64, // read dex file ScopedObjectAccess soa(Thread::Current()); + static constexpr bool kVerifyChecksum = true; std::string error_msg; std::vector<std::unique_ptr<const DexFile>> tmp; - bool success = DexFile::Open(location, location, &error_msg, &tmp); + bool success = DexFile::Open(location, location, kVerifyChecksum, &error_msg, &tmp); CHECK(success) << error_msg; EXPECT_EQ(1U, tmp.size()); std::unique_ptr<const DexFile> dex_file = std::move(tmp[0]); diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc index 1d243490ab..5132efc03c 100644 --- a/runtime/dex_file_verifier.cc +++ b/runtime/dex_file_verifier.cc @@ -128,9 +128,14 @@ const DexFile::MethodId* DexFileVerifier::CheckLoadMethodId(uint32_t idx, const error_stmt; \ } -bool DexFileVerifier::Verify(const DexFile* dex_file, const uint8_t* begin, size_t size, - const char* location, std::string* error_msg) { - std::unique_ptr<DexFileVerifier> verifier(new DexFileVerifier(dex_file, begin, size, location)); +bool DexFileVerifier::Verify(const DexFile* dex_file, + const uint8_t* begin, + size_t size, + const char* location, + bool verify_checksum, + std::string* error_msg) { + std::unique_ptr<DexFileVerifier> verifier( + new DexFileVerifier(dex_file, begin, size, location, verify_checksum)); if (!verifier->Verify()) { *error_msg = verifier->FailureReason(); return false; @@ -273,8 +278,13 @@ bool DexFileVerifier::CheckHeader() { const uint8_t* non_sum_ptr = reinterpret_cast<const uint8_t*>(header_) + non_sum; adler_checksum = adler32(adler_checksum, non_sum_ptr, expected_size - non_sum); if (adler_checksum != header_->checksum_) { - ErrorStringPrintf("Bad checksum (%08x, expected %08x)", adler_checksum, header_->checksum_); - return false; + if (verify_checksum_) { + ErrorStringPrintf("Bad checksum (%08x, expected %08x)", adler_checksum, header_->checksum_); + return false; + } else { + LOG(WARNING) << StringPrintf( + "Ignoring bad checksum (%08x, expected %08x)", adler_checksum, header_->checksum_); + } } // Check the contents of the header. diff --git a/runtime/dex_file_verifier.h b/runtime/dex_file_verifier.h index 90409db44b..133e4326bc 100644 --- a/runtime/dex_file_verifier.h +++ b/runtime/dex_file_verifier.h @@ -26,17 +26,31 @@ namespace art { class DexFileVerifier { public: - static bool Verify(const DexFile* dex_file, const uint8_t* begin, size_t size, - const char* location, std::string* error_msg); + static bool Verify(const DexFile* dex_file, + const uint8_t* begin, + size_t size, + const char* location, + bool verify_checksum, + std::string* error_msg); const std::string& FailureReason() const { return failure_reason_; } private: - DexFileVerifier(const DexFile* dex_file, const uint8_t* begin, size_t size, const char* location) - : dex_file_(dex_file), begin_(begin), size_(size), location_(location), - header_(&dex_file->GetHeader()), ptr_(nullptr), previous_item_(nullptr) { + DexFileVerifier(const DexFile* dex_file, + const uint8_t* begin, + size_t size, + const char* location, + bool verify_checksum) + : dex_file_(dex_file), + begin_(begin), + size_(size), + location_(location), + verify_checksum_(verify_checksum), + header_(&dex_file->GetHeader()), + ptr_(nullptr), + previous_item_(nullptr) { } bool Verify(); @@ -176,6 +190,7 @@ class DexFileVerifier { const uint8_t* const begin_; const size_t size_; const char* const location_; + const bool verify_checksum_; const DexFile::Header* const header_; struct OffsetTypeMapEmptyFn { diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc index 4e53914374..71c0ad9295 100644 --- a/runtime/dex_file_verifier_test.cc +++ b/runtime/dex_file_verifier_test.cc @@ -122,6 +122,10 @@ static void FixUpChecksum(uint8_t* dex_file) { class DexFileVerifierTest : public CommonRuntimeTest { protected: + DexFile* GetDexFile(const uint8_t* dex_bytes, size_t length) { + return new DexFile(dex_bytes, length, "tmp", 0, nullptr, nullptr); + } + void VerifyModification(const char* dex_file_base64_content, const char* location, std::function<void(DexFile*)> f, @@ -130,16 +134,17 @@ class DexFileVerifierTest : public CommonRuntimeTest { std::unique_ptr<uint8_t[]> dex_bytes = DecodeBase64(dex_file_base64_content, &length); CHECK(dex_bytes != nullptr); // Note: `dex_file` will be destroyed before `dex_bytes`. - std::unique_ptr<DexFile> dex_file( - new DexFile(dex_bytes.get(), length, "tmp", 0, nullptr, nullptr)); + std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length)); f(dex_file.get()); FixUpChecksum(const_cast<uint8_t*>(dex_file->Begin())); + static constexpr bool kVerifyChecksum = true; std::string error_msg; bool success = DexFileVerifier::Verify(dex_file.get(), dex_file->Begin(), dex_file->Size(), location, + kVerifyChecksum, &error_msg); if (expected_error == nullptr) { EXPECT_TRUE(success) << error_msg; @@ -175,7 +180,7 @@ static std::unique_ptr<const DexFile> OpenDexFileBase64(const char* base64, // read dex file ScopedObjectAccess soa(Thread::Current()); std::vector<std::unique_ptr<const DexFile>> tmp; - bool success = DexFile::Open(location, location, error_msg, &tmp); + bool success = DexFile::Open(location, location, true, error_msg, &tmp); CHECK(success) << error_msg; EXPECT_EQ(1U, tmp.size()); std::unique_ptr<const DexFile> dex_file = std::move(tmp[0]); @@ -1697,4 +1702,45 @@ TEST_F(DexFileVerifierTest, CircularInterfaceImplementation) { " implemented interface with type idx: '0'"); } +TEST_F(DexFileVerifierTest, Checksum) { + size_t length; + std::unique_ptr<uint8_t[]> dex_bytes = DecodeBase64(kGoodTestDex, &length); + CHECK(dex_bytes != nullptr); + // Note: `dex_file` will be destroyed before `dex_bytes`. + std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length)); + std::string error_msg; + + // Good checksum: all pass. + EXPECT_TRUE(DexFileVerifier::Verify(dex_file.get(), + dex_file->Begin(), + dex_file->Size(), + "good checksum, no verify", + /*verify_checksum*/ false, + &error_msg)); + EXPECT_TRUE(DexFileVerifier::Verify(dex_file.get(), + dex_file->Begin(), + dex_file->Size(), + "good checksum, verify", + /*verify_checksum*/ true, + &error_msg)); + + // Bad checksum: !verify_checksum passes verify_checksum fails. + DexFile::Header* header = reinterpret_cast<DexFile::Header*>( + const_cast<uint8_t*>(dex_file->Begin())); + header->checksum_ = 0; + EXPECT_TRUE(DexFileVerifier::Verify(dex_file.get(), + dex_file->Begin(), + dex_file->Size(), + "bad checksum, no verify", + /*verify_checksum*/ false, + &error_msg)); + EXPECT_FALSE(DexFileVerifier::Verify(dex_file.get(), + dex_file->Begin(), + dex_file->Size(), + "bad checksum, verify", + /*verify_checksum*/ true, + &error_msg)); + EXPECT_NE(error_msg.find("Bad checksum"), std::string::npos) << error_msg; +} + } // namespace art diff --git a/runtime/entrypoints/quick/quick_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h index f3a0d2f3ef..771e14396e 100644 --- a/runtime/entrypoints/quick/quick_default_externs.h +++ b/runtime/entrypoints/quick/quick_default_externs.h @@ -121,5 +121,6 @@ extern "C" void art_quick_throw_div_zero(); extern "C" void art_quick_throw_no_such_method(int32_t method_idx); extern "C" void art_quick_throw_null_pointer_exception(); extern "C" void art_quick_throw_stack_overflow(void*); +extern "C" void art_quick_throw_string_bounds(int32_t index, int32_t limit); #endif // ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_DEFAULT_EXTERNS_H_ diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h index 5dafa8b599..f98de95fcb 100644 --- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h +++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h @@ -114,6 +114,7 @@ void DefaultInitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) qpoints->pThrowNoSuchMethod = art_quick_throw_no_such_method; qpoints->pThrowNullPointer = art_quick_throw_null_pointer_exception; qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow; + qpoints->pThrowStringBounds = art_quick_throw_string_bounds; // Deoptimize qpoints->pDeoptimize = art_quick_deoptimize_from_compiled_code; diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h index 79d1c1377b..30b639eaec 100644 --- a/runtime/entrypoints/quick/quick_entrypoints_list.h +++ b/runtime/entrypoints/quick/quick_entrypoints_list.h @@ -140,6 +140,7 @@ V(ThrowNoSuchMethod, void, int32_t) \ V(ThrowNullPointer, void, void) \ V(ThrowStackOverflow, void, void*) \ + V(ThrowStringBounds, void, int32_t, int32_t) \ V(Deoptimize, void, void) \ \ V(A64Load, int64_t, volatile const int64_t *) \ diff --git a/runtime/entrypoints/quick/quick_throw_entrypoints.cc b/runtime/entrypoints/quick/quick_throw_entrypoints.cc index 5256feae2b..2778e32ece 100644 --- a/runtime/entrypoints/quick/quick_throw_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_throw_entrypoints.cc @@ -29,7 +29,7 @@ extern "C" NO_RETURN void artDeliverPendingExceptionFromCode(Thread* self) self->QuickDeliverException(); } -// Called by generated call to throw an exception. +// Called by generated code to throw an exception. extern "C" NO_RETURN void artDeliverExceptionFromCode(mirror::Throwable* exception, Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) { /* @@ -48,7 +48,7 @@ extern "C" NO_RETURN void artDeliverExceptionFromCode(mirror::Throwable* excepti self->QuickDeliverException(); } -// Called by generated call to throw a NPE exception. +// Called by generated code to throw a NPE exception. extern "C" NO_RETURN void artThrowNullPointerExceptionFromCode(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) { ScopedQuickEntrypointChecks sqec(self); @@ -58,7 +58,7 @@ extern "C" NO_RETURN void artThrowNullPointerExceptionFromCode(Thread* self) self->QuickDeliverException(); } -// Called by generated call to throw an arithmetic divide by zero exception. +// Called by generated code to throw an arithmetic divide by zero exception. extern "C" NO_RETURN void artThrowDivZeroFromCode(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) { ScopedQuickEntrypointChecks sqec(self); @@ -66,7 +66,7 @@ extern "C" NO_RETURN void artThrowDivZeroFromCode(Thread* self) self->QuickDeliverException(); } -// Called by generated call to throw an array index out of bounds exception. +// Called by generated code to throw an array index out of bounds exception. extern "C" NO_RETURN void artThrowArrayBoundsFromCode(int index, int length, Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) { ScopedQuickEntrypointChecks sqec(self); @@ -74,6 +74,14 @@ extern "C" NO_RETURN void artThrowArrayBoundsFromCode(int index, int length, Thr self->QuickDeliverException(); } +// Called by generated code to throw a string index out of bounds exception. +extern "C" NO_RETURN void artThrowStringBoundsFromCode(int index, int length, Thread* self) + SHARED_REQUIRES(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); + ThrowStringIndexOutOfBoundsException(index, length); + self->QuickDeliverException(); +} + extern "C" NO_RETURN void artThrowStackOverflowFromCode(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) { ScopedQuickEntrypointChecks sqec(self); diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc index c621672ae7..7a624b211c 100644 --- a/runtime/entrypoints_order_test.cc +++ b/runtime/entrypoints_order_test.cc @@ -112,17 +112,19 @@ class EntrypointsOrderTest : public CommonRuntimeTest { EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, name, pthread_self, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, pthread_self, last_no_thread_suspension_cause, sizeof(void*)); - EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, last_no_thread_suspension_cause, checkpoint_functions, + EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, last_no_thread_suspension_cause, checkpoint_function, sizeof(void*)); - EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, checkpoint_functions, jni_entrypoints, - sizeof(void*) * 6); + EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, checkpoint_function, active_suspend_barriers, + sizeof(void*)); + EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, active_suspend_barriers, jni_entrypoints, + sizeof(Thread::tls_ptr_sized_values::active_suspend_barriers)); // Skip across the entrypoints structures. - EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_objects, thread_local_start, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_start, thread_local_pos, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_pos, thread_local_end, sizeof(void*)); - EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_end, mterp_current_ibase, sizeof(void*)); + EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_end, thread_local_objects, sizeof(void*)); + EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_objects, mterp_current_ibase, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_current_ibase, mterp_default_ibase, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_default_ibase, mterp_alt_ibase, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_alt_ibase, rosalloc_runs, sizeof(void*)); @@ -285,7 +287,8 @@ class EntrypointsOrderTest : public CommonRuntimeTest { EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowDivZero, pThrowNoSuchMethod, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowNoSuchMethod, pThrowNullPointer, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowNullPointer, pThrowStackOverflow, sizeof(void*)); - EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowStackOverflow, pDeoptimize, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowStackOverflow, pThrowStringBounds, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowStringBounds, pDeoptimize, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pDeoptimize, pA64Load, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pA64Load, pA64Store, sizeof(void*)); diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index 3f8f6284c0..dd750060b8 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -192,7 +192,7 @@ void ConcurrentCopying::InitializePhase() { } // Used to switch the thread roots of a thread from from-space refs to to-space refs. -class ThreadFlipVisitor : public Closure { +class ConcurrentCopying::ThreadFlipVisitor : public Closure { public: ThreadFlipVisitor(ConcurrentCopying* concurrent_copying, bool use_tlab) : concurrent_copying_(concurrent_copying), use_tlab_(use_tlab) { @@ -229,7 +229,7 @@ class ThreadFlipVisitor : public Closure { }; // Called back from Runtime::FlipThreadRoots() during a pause. -class FlipCallback : public Closure { +class ConcurrentCopying::FlipCallback : public Closure { public: explicit FlipCallback(ConcurrentCopying* concurrent_copying) : concurrent_copying_(concurrent_copying) { @@ -304,10 +304,9 @@ void ConcurrentCopying::RecordLiveStackFreezeSize(Thread* self) { } // Used to visit objects in the immune spaces. -class ConcurrentCopyingImmuneSpaceObjVisitor { +class ConcurrentCopying::ImmuneSpaceObjVisitor { public: - explicit ConcurrentCopyingImmuneSpaceObjVisitor(ConcurrentCopying* cc) - : collector_(cc) {} + explicit ImmuneSpaceObjVisitor(ConcurrentCopying* cc) : collector_(cc) {} void operator()(mirror::Object* obj) const SHARED_REQUIRES(Locks::mutator_lock_) SHARED_REQUIRES(Locks::heap_bitmap_lock_) { @@ -388,7 +387,7 @@ void ConcurrentCopying::MarkingPhase() { for (auto& space : immune_spaces_.GetSpaces()) { DCHECK(space->IsImageSpace() || space->IsZygoteSpace()); accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap(); - ConcurrentCopyingImmuneSpaceObjVisitor visitor(this); + ImmuneSpaceObjVisitor visitor(this); live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(space->Begin()), reinterpret_cast<uintptr_t>(space->Limit()), visitor); @@ -487,7 +486,7 @@ void ConcurrentCopying::ReenableWeakRefAccess(Thread* self) { Runtime::Current()->BroadcastForNewSystemWeaks(); } -class DisableMarkingCheckpoint : public Closure { +class ConcurrentCopying::DisableMarkingCheckpoint : public Closure { public: explicit DisableMarkingCheckpoint(ConcurrentCopying* concurrent_copying) : concurrent_copying_(concurrent_copying) { @@ -683,9 +682,9 @@ accounting::ObjectStack* ConcurrentCopying::GetLiveStack() { // The following visitors are used to verify that there's no references to the from-space left after // marking. -class ConcurrentCopyingVerifyNoFromSpaceRefsVisitor : public SingleRootVisitor { +class ConcurrentCopying::VerifyNoFromSpaceRefsVisitor : public SingleRootVisitor { public: - explicit ConcurrentCopyingVerifyNoFromSpaceRefsVisitor(ConcurrentCopying* collector) + explicit VerifyNoFromSpaceRefsVisitor(ConcurrentCopying* collector) : collector_(collector) {} void operator()(mirror::Object* ref) const @@ -712,16 +711,16 @@ class ConcurrentCopyingVerifyNoFromSpaceRefsVisitor : public SingleRootVisitor { ConcurrentCopying* const collector_; }; -class ConcurrentCopyingVerifyNoFromSpaceRefsFieldVisitor { +class ConcurrentCopying::VerifyNoFromSpaceRefsFieldVisitor { public: - explicit ConcurrentCopyingVerifyNoFromSpaceRefsFieldVisitor(ConcurrentCopying* collector) + explicit VerifyNoFromSpaceRefsFieldVisitor(ConcurrentCopying* collector) : collector_(collector) {} void operator()(mirror::Object* obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const SHARED_REQUIRES(Locks::mutator_lock_) ALWAYS_INLINE { mirror::Object* ref = obj->GetFieldObject<mirror::Object, kDefaultVerifyFlags, kWithoutReadBarrier>(offset); - ConcurrentCopyingVerifyNoFromSpaceRefsVisitor visitor(collector_); + VerifyNoFromSpaceRefsVisitor visitor(collector_); visitor(ref); } void operator()(mirror::Class* klass, mirror::Reference* ref) const @@ -739,7 +738,7 @@ class ConcurrentCopyingVerifyNoFromSpaceRefsFieldVisitor { void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const SHARED_REQUIRES(Locks::mutator_lock_) { - ConcurrentCopyingVerifyNoFromSpaceRefsVisitor visitor(collector_); + VerifyNoFromSpaceRefsVisitor visitor(collector_); visitor(root->AsMirrorPtr()); } @@ -747,9 +746,9 @@ class ConcurrentCopyingVerifyNoFromSpaceRefsFieldVisitor { ConcurrentCopying* const collector_; }; -class ConcurrentCopyingVerifyNoFromSpaceRefsObjectVisitor { +class ConcurrentCopying::VerifyNoFromSpaceRefsObjectVisitor { public: - explicit ConcurrentCopyingVerifyNoFromSpaceRefsObjectVisitor(ConcurrentCopying* collector) + explicit VerifyNoFromSpaceRefsObjectVisitor(ConcurrentCopying* collector) : collector_(collector) {} void operator()(mirror::Object* obj) const SHARED_REQUIRES(Locks::mutator_lock_) { @@ -761,7 +760,7 @@ class ConcurrentCopyingVerifyNoFromSpaceRefsObjectVisitor { ConcurrentCopying* collector = reinterpret_cast<ConcurrentCopying*>(arg); space::RegionSpace* region_space = collector->RegionSpace(); CHECK(!region_space->IsInFromSpace(obj)) << "Scanning object " << obj << " in from space"; - ConcurrentCopyingVerifyNoFromSpaceRefsFieldVisitor visitor(collector); + VerifyNoFromSpaceRefsFieldVisitor visitor(collector); obj->VisitReferences(visitor, visitor); if (kUseBakerReadBarrier) { CHECK(obj->GetReadBarrierPointer() == ReadBarrier::WhitePtr()) @@ -785,16 +784,15 @@ void ConcurrentCopying::VerifyNoFromSpaceReferences() { CHECK(!thread->GetIsGcMarking()); } } - ConcurrentCopyingVerifyNoFromSpaceRefsObjectVisitor visitor(this); + VerifyNoFromSpaceRefsObjectVisitor visitor(this); // Roots. { ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); - ConcurrentCopyingVerifyNoFromSpaceRefsVisitor ref_visitor(this); + VerifyNoFromSpaceRefsVisitor ref_visitor(this); Runtime::Current()->VisitRoots(&ref_visitor); } // The to-space. - region_space_->WalkToSpace(ConcurrentCopyingVerifyNoFromSpaceRefsObjectVisitor::ObjectCallback, - this); + region_space_->WalkToSpace(VerifyNoFromSpaceRefsObjectVisitor::ObjectCallback, this); // Non-moving spaces. { WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); @@ -802,7 +800,7 @@ void ConcurrentCopying::VerifyNoFromSpaceReferences() { } // The alloc stack. { - ConcurrentCopyingVerifyNoFromSpaceRefsVisitor ref_visitor(this); + VerifyNoFromSpaceRefsVisitor ref_visitor(this); for (auto* it = heap_->allocation_stack_->Begin(), *end = heap_->allocation_stack_->End(); it < end; ++it) { mirror::Object* const obj = it->AsMirrorPtr(); @@ -817,9 +815,9 @@ void ConcurrentCopying::VerifyNoFromSpaceReferences() { } // The following visitors are used to assert the to-space invariant. -class ConcurrentCopyingAssertToSpaceInvariantRefsVisitor { +class ConcurrentCopying::AssertToSpaceInvariantRefsVisitor { public: - explicit ConcurrentCopyingAssertToSpaceInvariantRefsVisitor(ConcurrentCopying* collector) + explicit AssertToSpaceInvariantRefsVisitor(ConcurrentCopying* collector) : collector_(collector) {} void operator()(mirror::Object* ref) const @@ -835,16 +833,16 @@ class ConcurrentCopyingAssertToSpaceInvariantRefsVisitor { ConcurrentCopying* const collector_; }; -class ConcurrentCopyingAssertToSpaceInvariantFieldVisitor { +class ConcurrentCopying::AssertToSpaceInvariantFieldVisitor { public: - explicit ConcurrentCopyingAssertToSpaceInvariantFieldVisitor(ConcurrentCopying* collector) + explicit AssertToSpaceInvariantFieldVisitor(ConcurrentCopying* collector) : collector_(collector) {} void operator()(mirror::Object* obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const SHARED_REQUIRES(Locks::mutator_lock_) ALWAYS_INLINE { mirror::Object* ref = obj->GetFieldObject<mirror::Object, kDefaultVerifyFlags, kWithoutReadBarrier>(offset); - ConcurrentCopyingAssertToSpaceInvariantRefsVisitor visitor(collector_); + AssertToSpaceInvariantRefsVisitor visitor(collector_); visitor(ref); } void operator()(mirror::Class* klass, mirror::Reference* ref ATTRIBUTE_UNUSED) const @@ -861,7 +859,7 @@ class ConcurrentCopyingAssertToSpaceInvariantFieldVisitor { void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const SHARED_REQUIRES(Locks::mutator_lock_) { - ConcurrentCopyingAssertToSpaceInvariantRefsVisitor visitor(collector_); + AssertToSpaceInvariantRefsVisitor visitor(collector_); visitor(root->AsMirrorPtr()); } @@ -869,9 +867,9 @@ class ConcurrentCopyingAssertToSpaceInvariantFieldVisitor { ConcurrentCopying* const collector_; }; -class ConcurrentCopyingAssertToSpaceInvariantObjectVisitor { +class ConcurrentCopying::AssertToSpaceInvariantObjectVisitor { public: - explicit ConcurrentCopyingAssertToSpaceInvariantObjectVisitor(ConcurrentCopying* collector) + explicit AssertToSpaceInvariantObjectVisitor(ConcurrentCopying* collector) : collector_(collector) {} void operator()(mirror::Object* obj) const SHARED_REQUIRES(Locks::mutator_lock_) { @@ -884,7 +882,7 @@ class ConcurrentCopyingAssertToSpaceInvariantObjectVisitor { space::RegionSpace* region_space = collector->RegionSpace(); CHECK(!region_space->IsInFromSpace(obj)) << "Scanning object " << obj << " in from space"; collector->AssertToSpaceInvariant(nullptr, MemberOffset(0), obj); - ConcurrentCopyingAssertToSpaceInvariantFieldVisitor visitor(collector); + AssertToSpaceInvariantFieldVisitor visitor(collector); obj->VisitReferences(visitor, visitor); } @@ -892,7 +890,7 @@ class ConcurrentCopyingAssertToSpaceInvariantObjectVisitor { ConcurrentCopying* const collector_; }; -class RevokeThreadLocalMarkStackCheckpoint : public Closure { +class ConcurrentCopying::RevokeThreadLocalMarkStackCheckpoint : public Closure { public: RevokeThreadLocalMarkStackCheckpoint(ConcurrentCopying* concurrent_copying, bool disable_weak_ref_access) @@ -1112,7 +1110,7 @@ inline void ConcurrentCopying::ProcessMarkStackRef(mirror::Object* to_ref) { region_space_->AddLiveBytes(to_ref, alloc_size); } if (ReadBarrier::kEnableToSpaceInvariantChecks || kIsDebugBuild) { - ConcurrentCopyingAssertToSpaceInvariantObjectVisitor visitor(this); + AssertToSpaceInvariantObjectVisitor visitor(this); visitor(to_ref); } } @@ -1484,9 +1482,9 @@ void ConcurrentCopying::AssertToSpaceInvariantInNonMovingSpace(mirror::Object* o } // Used to scan ref fields of an object. -class ConcurrentCopyingRefFieldsVisitor { +class ConcurrentCopying::RefFieldsVisitor { public: - explicit ConcurrentCopyingRefFieldsVisitor(ConcurrentCopying* collector) + explicit RefFieldsVisitor(ConcurrentCopying* collector) : collector_(collector) {} void operator()(mirror::Object* obj, MemberOffset offset, bool /* is_static */) @@ -1522,7 +1520,7 @@ class ConcurrentCopyingRefFieldsVisitor { // Scan ref fields of an object. inline void ConcurrentCopying::Scan(mirror::Object* to_ref) { DCHECK(!region_space_->IsInFromSpace(to_ref)); - ConcurrentCopyingRefFieldsVisitor visitor(this); + RefFieldsVisitor visitor(this); // Disable the read barrier for a performance reason. to_ref->VisitReferences</*kVisitNativeRoots*/true, kDefaultVerifyFlags, kWithoutReadBarrier>( visitor, visitor); diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h index afdc0f1f98..a986a7a1db 100644 --- a/runtime/gc/collector/concurrent_copying.h +++ b/runtime/gc/collector/concurrent_copying.h @@ -243,16 +243,21 @@ class ConcurrentCopying : public GarbageCollector { accounting::ReadBarrierTable* rb_table_; bool force_evacuate_all_; // True if all regions are evacuated. - friend class ConcurrentCopyingRefFieldsVisitor; - friend class ConcurrentCopyingImmuneSpaceObjVisitor; - friend class ConcurrentCopyingVerifyNoFromSpaceRefsVisitor; - friend class ConcurrentCopyingVerifyNoFromSpaceRefsObjectVisitor; - friend class ConcurrentCopyingClearBlackPtrsVisitor; - friend class ConcurrentCopyingLostCopyVisitor; - friend class ThreadFlipVisitor; - friend class FlipCallback; - friend class ConcurrentCopyingComputeUnevacFromSpaceLiveRatioVisitor; - friend class RevokeThreadLocalMarkStackCheckpoint; + class AssertToSpaceInvariantFieldVisitor; + class AssertToSpaceInvariantObjectVisitor; + class AssertToSpaceInvariantRefsVisitor; + class ClearBlackPtrsVisitor; + class ComputeUnevacFromSpaceLiveRatioVisitor; + class DisableMarkingCheckpoint; + class FlipCallback; + class ImmuneSpaceObjVisitor; + class LostCopyVisitor; + class RefFieldsVisitor; + class RevokeThreadLocalMarkStackCheckpoint; + class VerifyNoFromSpaceRefsFieldVisitor; + class VerifyNoFromSpaceRefsObjectVisitor; + class VerifyNoFromSpaceRefsVisitor; + class ThreadFlipVisitor; DISALLOW_IMPLICIT_CONSTRUCTORS(ConcurrentCopying); }; diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc index 6beb60608c..43482eb7cc 100644 --- a/runtime/gc/collector/mark_compact.cc +++ b/runtime/gc/collector/mark_compact.cc @@ -52,8 +52,9 @@ void MarkCompact::BindBitmaps() { MarkCompact::MarkCompact(Heap* heap, const std::string& name_prefix) : GarbageCollector(heap, name_prefix + (name_prefix.empty() ? "" : " ") + "mark compact"), - space_(nullptr), collector_name_(name_), updating_references_(false) { -} + space_(nullptr), + collector_name_(name_), + updating_references_(false) {} void MarkCompact::RunPhases() { Thread* self = Thread::Current(); @@ -85,30 +86,20 @@ void MarkCompact::ForwardObject(mirror::Object* obj) { ++live_objects_in_space_; } -class CalculateObjectForwardingAddressVisitor { - public: - explicit CalculateObjectForwardingAddressVisitor(MarkCompact* collector) - : collector_(collector) {} - void operator()(mirror::Object* obj) const REQUIRES(Locks::mutator_lock_, - Locks::heap_bitmap_lock_) { - DCHECK_ALIGNED(obj, space::BumpPointerSpace::kAlignment); - DCHECK(collector_->IsMarked(obj) != nullptr); - collector_->ForwardObject(obj); - } - - private: - MarkCompact* const collector_; -}; void MarkCompact::CalculateObjectForwardingAddresses() { TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); // The bump pointer in the space where the next forwarding address will be. bump_pointer_ = reinterpret_cast<uint8_t*>(space_->Begin()); // Visit all the marked objects in the bitmap. - CalculateObjectForwardingAddressVisitor visitor(this); objects_before_forwarding_->VisitMarkedRange(reinterpret_cast<uintptr_t>(space_->Begin()), reinterpret_cast<uintptr_t>(space_->End()), - visitor); + [this](mirror::Object* obj) + REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { + DCHECK_ALIGNED(obj, space::BumpPointerSpace::kAlignment); + DCHECK(IsMarked(obj) != nullptr); + ForwardObject(obj); + }); } void MarkCompact::InitializePhase() { @@ -129,17 +120,6 @@ void MarkCompact::ProcessReferences(Thread* self) { false, GetTimings(), GetCurrentIteration()->GetClearSoftReferences(), this); } -class BitmapSetSlowPathVisitor { - public: - void operator()(const mirror::Object* obj) const SHARED_REQUIRES(Locks::mutator_lock_) { - // Marking a large object, make sure its aligned as a sanity check. - if (!IsAligned<kPageSize>(obj)) { - Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR)); - LOG(FATAL) << obj; - } - } -}; - inline mirror::Object* MarkCompact::MarkObject(mirror::Object* obj) { if (obj == nullptr) { return nullptr; @@ -155,8 +135,15 @@ inline mirror::Object* MarkCompact::MarkObject(mirror::Object* obj) { } } else { DCHECK(!space_->HasAddress(obj)); - BitmapSetSlowPathVisitor visitor; - if (!mark_bitmap_->Set(obj, visitor)) { + auto slow_path = [this](const mirror::Object* ref) + SHARED_REQUIRES(Locks::mutator_lock_) { + // Marking a large object, make sure its aligned as a sanity check. + if (!IsAligned<kPageSize>(ref)) { + Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR)); + LOG(FATAL) << ref; + } + }; + if (!mark_bitmap_->Set(obj, slow_path)) { // This object was not previously marked. MarkStackPush(obj); } @@ -296,10 +283,9 @@ void MarkCompact::VisitRoots( } } -class UpdateRootVisitor : public RootVisitor { +class MarkCompact::UpdateRootVisitor : public RootVisitor { public: - explicit UpdateRootVisitor(MarkCompact* collector) : collector_(collector) { - } + explicit UpdateRootVisitor(MarkCompact* collector) : collector_(collector) {} void VisitRoots(mirror::Object*** roots, size_t count, const RootInfo& info ATTRIBUTE_UNUSED) OVERRIDE REQUIRES(Locks::mutator_lock_) @@ -332,10 +318,10 @@ class UpdateRootVisitor : public RootVisitor { MarkCompact* const collector_; }; -class UpdateObjectReferencesVisitor { +class MarkCompact::UpdateObjectReferencesVisitor { public: - explicit UpdateObjectReferencesVisitor(MarkCompact* collector) : collector_(collector) { - } + explicit UpdateObjectReferencesVisitor(MarkCompact* collector) : collector_(collector) {} + void operator()(mirror::Object* obj) const SHARED_REQUIRES(Locks::heap_bitmap_lock_) REQUIRES(Locks::mutator_lock_) ALWAYS_INLINE { collector_->UpdateObjectReferences(obj); @@ -423,10 +409,9 @@ inline void MarkCompact::UpdateHeapReference(mirror::HeapReference<mirror::Objec } } -class UpdateReferenceVisitor { +class MarkCompact::UpdateReferenceVisitor { public: - explicit UpdateReferenceVisitor(MarkCompact* collector) : collector_(collector) { - } + explicit UpdateReferenceVisitor(MarkCompact* collector) : collector_(collector) {} void operator()(mirror::Object* obj, MemberOffset offset, bool /*is_static*/) const ALWAYS_INLINE REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { @@ -501,19 +486,6 @@ bool MarkCompact::ShouldSweepSpace(space::ContinuousSpace* space) const { return space != space_ && !immune_spaces_.ContainsSpace(space); } -class MoveObjectVisitor { - public: - explicit MoveObjectVisitor(MarkCompact* collector) : collector_(collector) { - } - void operator()(mirror::Object* obj) const SHARED_REQUIRES(Locks::heap_bitmap_lock_) - REQUIRES(Locks::mutator_lock_) ALWAYS_INLINE { - collector_->MoveObject(obj, obj->SizeOf()); - } - - private: - MarkCompact* const collector_; -}; - void MarkCompact::MoveObject(mirror::Object* obj, size_t len) { // Look at the forwarding address stored in the lock word to know where to copy. DCHECK(space_->HasAddress(obj)) << obj; @@ -534,10 +506,13 @@ void MarkCompact::MoveObject(mirror::Object* obj, size_t len) { void MarkCompact::MoveObjects() { TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); // Move the objects in the before forwarding bitmap. - MoveObjectVisitor visitor(this); objects_before_forwarding_->VisitMarkedRange(reinterpret_cast<uintptr_t>(space_->Begin()), reinterpret_cast<uintptr_t>(space_->End()), - visitor); + [this](mirror::Object* obj) + SHARED_REQUIRES(Locks::heap_bitmap_lock_) + REQUIRES(Locks::mutator_lock_) ALWAYS_INLINE { + MoveObject(obj, obj->SizeOf()); + }); CHECK(lock_words_to_restore_.empty()); } @@ -572,10 +547,9 @@ void MarkCompact::DelayReferenceReferent(mirror::Class* klass, mirror::Reference heap_->GetReferenceProcessor()->DelayReferenceReferent(klass, reference, this); } -class MarkCompactMarkObjectVisitor { +class MarkCompact::MarkObjectVisitor { public: - explicit MarkCompactMarkObjectVisitor(MarkCompact* collector) : collector_(collector) { - } + explicit MarkObjectVisitor(MarkCompact* collector) : collector_(collector) {} void operator()(mirror::Object* obj, MemberOffset offset, bool /*is_static*/) const ALWAYS_INLINE REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { @@ -608,7 +582,7 @@ class MarkCompactMarkObjectVisitor { // Visit all of the references of an object and update. void MarkCompact::ScanObject(mirror::Object* obj) { - MarkCompactMarkObjectVisitor visitor(this); + MarkObjectVisitor visitor(this); obj->VisitReferences(visitor, visitor); } diff --git a/runtime/gc/collector/mark_compact.h b/runtime/gc/collector/mark_compact.h index 48311570b5..16abfb73b8 100644 --- a/runtime/gc/collector/mark_compact.h +++ b/runtime/gc/collector/mark_compact.h @@ -222,13 +222,10 @@ class MarkCompact : public GarbageCollector { bool updating_references_; private: - friend class BitmapSetSlowPathVisitor; - friend class CalculateObjectForwardingAddressVisitor; - friend class MarkCompactMarkObjectVisitor; - friend class MoveObjectVisitor; - friend class UpdateObjectReferencesVisitor; - friend class UpdateReferenceVisitor; - friend class UpdateRootVisitor; + class MarkObjectVisitor; + class UpdateObjectReferencesVisitor; + class UpdateReferenceVisitor; + class UpdateRootVisitor; DISALLOW_IMPLICIT_CONSTRUCTORS(MarkCompact); }; diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index 894ceba216..9f54f1cdd4 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -266,7 +266,7 @@ void MarkSweep::MarkingPhase() { PreCleanCards(); } -class ScanObjectVisitor { +class MarkSweep::ScanObjectVisitor { public: explicit ScanObjectVisitor(MarkSweep* const mark_sweep) ALWAYS_INLINE : mark_sweep_(mark_sweep) {} @@ -393,12 +393,14 @@ bool MarkSweep::IsMarkedHeapReference(mirror::HeapReference<mirror::Object>* ref return IsMarked(ref->AsMirrorPtr()); } -class MarkSweepMarkObjectSlowPath { +class MarkSweep::MarkObjectSlowPath { public: - explicit MarkSweepMarkObjectSlowPath(MarkSweep* mark_sweep, - mirror::Object* holder = nullptr, - MemberOffset offset = MemberOffset(0)) - : mark_sweep_(mark_sweep), holder_(holder), offset_(offset) {} + explicit MarkObjectSlowPath(MarkSweep* mark_sweep, + mirror::Object* holder = nullptr, + MemberOffset offset = MemberOffset(0)) + : mark_sweep_(mark_sweep), + holder_(holder), + offset_(offset) {} void operator()(const mirror::Object* obj) const NO_THREAD_SAFETY_ANALYSIS { if (kProfileLargeObjects) { @@ -444,27 +446,8 @@ class MarkSweepMarkObjectSlowPath { } PrintFileToLog("/proc/self/maps", LogSeverity::INTERNAL_FATAL); MemMap::DumpMaps(LOG(INTERNAL_FATAL), true); - { - LOG(INTERNAL_FATAL) << "Attempting see if it's a bad root"; - Thread* self = Thread::Current(); - if (Locks::mutator_lock_->IsExclusiveHeld(self)) { - mark_sweep_->VerifyRoots(); - } else { - const bool heap_bitmap_exclusive_locked = - Locks::heap_bitmap_lock_->IsExclusiveHeld(self); - if (heap_bitmap_exclusive_locked) { - Locks::heap_bitmap_lock_->ExclusiveUnlock(self); - } - { - ScopedThreadSuspension(self, kSuspended); - ScopedSuspendAll ssa(__FUNCTION__); - mark_sweep_->VerifyRoots(); - } - if (heap_bitmap_exclusive_locked) { - Locks::heap_bitmap_lock_->ExclusiveLock(self); - } - } - } + LOG(INTERNAL_FATAL) << "Attempting see if it's a bad thread root\n"; + mark_sweep_->VerifySuspendedThreadRoots(); LOG(FATAL) << "Can't mark invalid object"; } } @@ -499,7 +482,7 @@ inline void MarkSweep::MarkObjectNonNull(mirror::Object* obj, if (kCountMarkedObjects) { ++mark_slowpath_count_; } - MarkSweepMarkObjectSlowPath visitor(this, holder, offset); + MarkObjectSlowPath visitor(this, holder, offset); // TODO: We already know that the object is not in the current_space_bitmap_ but MarkBitmap::Set // will check again. if (!mark_bitmap_->Set(obj, visitor)) { @@ -534,7 +517,7 @@ inline bool MarkSweep::MarkObjectParallel(mirror::Object* obj) { if (LIKELY(object_bitmap->HasAddress(obj))) { return !object_bitmap->AtomicTestAndSet(obj); } - MarkSweepMarkObjectSlowPath visitor(this); + MarkObjectSlowPath visitor(this); return !mark_bitmap_->AtomicTestAndSet(obj, visitor); } @@ -553,7 +536,7 @@ inline void MarkSweep::MarkObject(mirror::Object* obj, } } -class VerifyRootMarkedVisitor : public SingleRootVisitor { +class MarkSweep::VerifyRootMarkedVisitor : public SingleRootVisitor { public: explicit VerifyRootMarkedVisitor(MarkSweep* collector) : collector_(collector) { } @@ -582,7 +565,7 @@ void MarkSweep::VisitRoots(mirror::CompressedReference<mirror::Object>** roots, } } -class VerifyRootVisitor : public SingleRootVisitor { +class MarkSweep::VerifyRootVisitor : public SingleRootVisitor { public: void VisitRoot(mirror::Object* root, const RootInfo& info) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { @@ -591,15 +574,15 @@ class VerifyRootVisitor : public SingleRootVisitor { if (heap->GetLiveBitmap()->GetContinuousSpaceBitmap(root) == nullptr) { space::LargeObjectSpace* large_object_space = heap->GetLargeObjectsSpace(); if (large_object_space != nullptr && !large_object_space->Contains(root)) { - LOG(INTERNAL_FATAL) << "Found invalid root: " << root << " " << info; + LOG(INTERNAL_FATAL) << "Found invalid root: " << root << " " << info << "\n"; } } } }; -void MarkSweep::VerifyRoots() { +void MarkSweep::VerifySuspendedThreadRoots() { VerifyRootVisitor visitor; - Runtime::Current()->GetThreadList()->VisitRoots(&visitor); + Runtime::Current()->GetThreadList()->VisitRootsForSuspendedThreads(&visitor); } void MarkSweep::MarkRoots(Thread* self) { @@ -629,7 +612,7 @@ void MarkSweep::MarkConcurrentRoots(VisitRootFlags flags) { this, static_cast<VisitRootFlags>(flags | kVisitRootFlagNonMoving)); } -class DelayReferenceReferentVisitor { +class MarkSweep::DelayReferenceReferentVisitor { public: explicit DelayReferenceReferentVisitor(MarkSweep* collector) : collector_(collector) {} @@ -644,7 +627,7 @@ class DelayReferenceReferentVisitor { }; template <bool kUseFinger = false> -class MarkStackTask : public Task { +class MarkSweep::MarkStackTask : public Task { public: MarkStackTask(ThreadPool* thread_pool, MarkSweep* mark_sweep, @@ -802,7 +785,7 @@ class MarkStackTask : public Task { } }; -class CardScanTask : public MarkStackTask<false> { +class MarkSweep::CardScanTask : public MarkStackTask<false> { public: CardScanTask(ThreadPool* thread_pool, MarkSweep* mark_sweep, @@ -967,7 +950,7 @@ void MarkSweep::ScanGrayObjects(bool paused, uint8_t minimum_age) { } } -class RecursiveMarkTask : public MarkStackTask<false> { +class MarkSweep::RecursiveMarkTask : public MarkStackTask<false> { public: RecursiveMarkTask(ThreadPool* thread_pool, MarkSweep* mark_sweep, @@ -1080,7 +1063,7 @@ void MarkSweep::SweepSystemWeaks(Thread* self) { Runtime::Current()->SweepSystemWeaks(this); } -class VerifySystemWeakVisitor : public IsMarkedVisitor { +class MarkSweep::VerifySystemWeakVisitor : public IsMarkedVisitor { public: explicit VerifySystemWeakVisitor(MarkSweep* mark_sweep) : mark_sweep_(mark_sweep) {} @@ -1109,7 +1092,7 @@ void MarkSweep::VerifySystemWeaks() { Runtime::Current()->SweepSystemWeaks(&visitor); } -class CheckpointMarkThreadRoots : public Closure, public RootVisitor { +class MarkSweep::CheckpointMarkThreadRoots : public Closure, public RootVisitor { public: CheckpointMarkThreadRoots(MarkSweep* mark_sweep, bool revoke_ros_alloc_thread_local_buffers_at_checkpoint) diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h index c19107a626..9747031152 100644 --- a/runtime/gc/collector/mark_sweep.h +++ b/runtime/gc/collector/mark_sweep.h @@ -250,8 +250,8 @@ class MarkSweep : public GarbageCollector { // Verify the roots of the heap and print out information related to any invalid roots. // Called in MarkObject, so may we may not hold the mutator lock. - void VerifyRoots() - NO_THREAD_SAFETY_ANALYSIS; + void VerifySuspendedThreadRoots() + SHARED_REQUIRES(Locks::mutator_lock_); // Expand mark stack to 2x its current size. void ExpandMarkStack() @@ -353,17 +353,17 @@ class MarkSweep : public GarbageCollector { std::unique_ptr<MemMap> sweep_array_free_buffer_mem_map_; private: - friend class CardScanTask; - friend class CheckBitmapVisitor; - friend class CheckReferenceVisitor; - friend class CheckpointMarkThreadRoots; - friend class Heap; - friend class FifoMarkStackChunk; - friend class MarkObjectVisitor; - template<bool kUseFinger> friend class MarkStackTask; - friend class MarkSweepMarkObjectSlowPath; - friend class VerifyRootMarkedVisitor; - friend class VerifyRootVisitor; + class CardScanTask; + class CheckpointMarkThreadRoots; + class DelayReferenceReferentVisitor; + template<bool kUseFinger> class MarkStackTask; + class MarkObjectSlowPath; + class RecursiveMarkTask; + class ScanObjectParallelVisitor; + class ScanObjectVisitor; + class VerifyRootMarkedVisitor; + class VerifyRootVisitor; + class VerifySystemWeakVisitor; DISALLOW_IMPLICIT_CONSTRUCTORS(MarkSweep); }; diff --git a/runtime/gc/collector/semi_space-inl.h b/runtime/gc/collector/semi_space-inl.h index e87b5ff332..78fb2d24ae 100644 --- a/runtime/gc/collector/semi_space-inl.h +++ b/runtime/gc/collector/semi_space-inl.h @@ -26,21 +26,6 @@ namespace art { namespace gc { namespace collector { -class BitmapSetSlowPathVisitor { - public: - explicit BitmapSetSlowPathVisitor(SemiSpace* semi_space) : semi_space_(semi_space) { - } - - void operator()(const mirror::Object* obj) const { - CHECK(!semi_space_->to_space_->HasAddress(obj)) << "Marking " << obj << " in to_space_"; - // Marking a large object, make sure its aligned as a sanity check. - CHECK_ALIGNED(obj, kPageSize); - } - - private: - SemiSpace* const semi_space_; -}; - inline mirror::Object* SemiSpace::GetForwardingAddressInFromSpace(mirror::Object* obj) const { DCHECK(from_space_->HasAddress(obj)); LockWord lock_word = obj->GetLockWord(false); @@ -76,8 +61,12 @@ inline void SemiSpace::MarkObject( obj_ptr->Assign(forward_address); } else if (!collect_from_space_only_ && !immune_spaces_.IsInImmuneRegion(obj)) { DCHECK(!to_space_->HasAddress(obj)) << "Tried to mark " << obj << " in to-space"; - BitmapSetSlowPathVisitor visitor(this); - if (!mark_bitmap_->Set(obj, visitor)) { + auto slow_path = [this](const mirror::Object* ref) { + CHECK(!to_space_->HasAddress(ref)) << "Marking " << ref << " in to_space_"; + // Marking a large object, make sure its aligned as a sanity check. + CHECK_ALIGNED(ref, kPageSize); + }; + if (!mark_bitmap_->Set(obj, slow_path)) { // This object was not previously marked. MarkStackPush(obj); } diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index f37daa54e9..7a4c025c30 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -282,22 +282,11 @@ void SemiSpace::MarkingPhase() { } } -class SemiSpaceScanObjectVisitor { - public: - explicit SemiSpaceScanObjectVisitor(SemiSpace* ss) : semi_space_(ss) {} - void operator()(Object* obj) const REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { - DCHECK(obj != nullptr); - semi_space_->ScanObject(obj); - } - private: - SemiSpace* const semi_space_; -}; - // Used to verify that there's no references to the from-space. -class SemiSpaceVerifyNoFromSpaceReferencesVisitor { +class SemiSpace::VerifyNoFromSpaceReferencesVisitor { public: - explicit SemiSpaceVerifyNoFromSpaceReferencesVisitor(space::ContinuousMemMapAllocSpace* from_space) : - from_space_(from_space) {} + explicit VerifyNoFromSpaceReferencesVisitor(space::ContinuousMemMapAllocSpace* from_space) + : from_space_(from_space) {} void operator()(Object* obj, MemberOffset offset, bool /* is_static */) const SHARED_REQUIRES(Locks::mutator_lock_) ALWAYS_INLINE { @@ -331,23 +320,10 @@ class SemiSpaceVerifyNoFromSpaceReferencesVisitor { void SemiSpace::VerifyNoFromSpaceReferences(Object* obj) { DCHECK(!from_space_->HasAddress(obj)) << "Scanning object " << obj << " in from space"; - SemiSpaceVerifyNoFromSpaceReferencesVisitor visitor(from_space_); + VerifyNoFromSpaceReferencesVisitor visitor(from_space_); obj->VisitReferences(visitor, VoidFunctor()); } -class SemiSpaceVerifyNoFromSpaceReferencesObjectVisitor { - public: - explicit SemiSpaceVerifyNoFromSpaceReferencesObjectVisitor(SemiSpace* ss) : semi_space_(ss) {} - void operator()(Object* obj) const - SHARED_REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_) { - DCHECK(obj != nullptr); - semi_space_->VerifyNoFromSpaceReferences(obj); - } - - private: - SemiSpace* const semi_space_; -}; - void SemiSpace::MarkReachableObjects() { TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); { @@ -390,10 +366,12 @@ void SemiSpace::MarkReachableObjects() { } else { TimingLogger::ScopedTiming t2("VisitLiveBits", GetTimings()); accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap(); - SemiSpaceScanObjectVisitor visitor(this); live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(space->Begin()), reinterpret_cast<uintptr_t>(space->End()), - visitor); + [this](mirror::Object* obj) + REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { + ScanObject(obj); + }); } if (kIsDebugBuild) { // Verify that there are no from-space references that @@ -401,10 +379,13 @@ void SemiSpace::MarkReachableObjects() { // card table) didn't miss any from-space references in the // space. accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap(); - SemiSpaceVerifyNoFromSpaceReferencesObjectVisitor visitor(this); live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(space->Begin()), reinterpret_cast<uintptr_t>(space->End()), - visitor); + [this](Object* obj) + SHARED_REQUIRES(Locks::heap_bitmap_lock_, Locks::mutator_lock_) { + DCHECK(obj != nullptr); + VerifyNoFromSpaceReferences(obj); + }); } } } @@ -424,10 +405,12 @@ void SemiSpace::MarkReachableObjects() { // classes (primitive array classes) that could move though they // don't contain any other references. accounting::LargeObjectBitmap* large_live_bitmap = los->GetLiveBitmap(); - SemiSpaceScanObjectVisitor visitor(this); large_live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(los->Begin()), reinterpret_cast<uintptr_t>(los->End()), - visitor); + [this](mirror::Object* obj) + REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { + ScanObject(obj); + }); } // Recursively process the mark stack. ProcessMarkStack(); @@ -697,10 +680,9 @@ void SemiSpace::DelayReferenceReferent(mirror::Class* klass, mirror::Reference* heap_->GetReferenceProcessor()->DelayReferenceReferent(klass, reference, this); } -class SemiSpaceMarkObjectVisitor { +class SemiSpace::MarkObjectVisitor { public: - explicit SemiSpaceMarkObjectVisitor(SemiSpace* collector) : collector_(collector) { - } + explicit MarkObjectVisitor(SemiSpace* collector) : collector_(collector) {} void operator()(Object* obj, MemberOffset offset, bool /* is_static */) const ALWAYS_INLINE REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { @@ -739,7 +721,7 @@ class SemiSpaceMarkObjectVisitor { // Visit all of the references of an object and update. void SemiSpace::ScanObject(Object* obj) { DCHECK(!from_space_->HasAddress(obj)) << "Scanning object " << obj << " in from space"; - SemiSpaceMarkObjectVisitor visitor(this); + MarkObjectVisitor visitor(this); obj->VisitReferences(visitor, visitor); } diff --git a/runtime/gc/collector/semi_space.h b/runtime/gc/collector/semi_space.h index 0199e1ae56..694e536b7d 100644 --- a/runtime/gc/collector/semi_space.h +++ b/runtime/gc/collector/semi_space.h @@ -272,7 +272,9 @@ class SemiSpace : public GarbageCollector { bool swap_semi_spaces_; private: - friend class BitmapSetSlowPathVisitor; + class BitmapSetSlowPathVisitor; + class MarkObjectVisitor; + class VerifyNoFromSpaceReferencesVisitor; DISALLOW_IMPLICIT_CONSTRUCTORS(SemiSpace); }; diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 1ebe5cc47b..8cadc2e0fc 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -1158,6 +1158,80 @@ static bool RelocateInPlace(ImageHeader& image_header, return true; } +static MemMap* LoadImageFile(const char* image_filename, + const char* image_location, + const ImageHeader& image_header, + uint8_t* address, + int fd, + TimingLogger& logger, + std::string* error_msg) { + TimingLogger::ScopedTiming timing("MapImageFile", &logger); + const ImageHeader::StorageMode storage_mode = image_header.GetStorageMode(); + if (storage_mode == ImageHeader::kStorageModeUncompressed) { + return MemMap::MapFileAtAddress(address, + image_header.GetImageSize(), + PROT_READ | PROT_WRITE, + MAP_PRIVATE, + fd, + 0, + /*low_4gb*/true, + /*reuse*/false, + image_filename, + error_msg); + } + + if (storage_mode != ImageHeader::kStorageModeLZ4 && + storage_mode != ImageHeader::kStorageModeLZ4HC) { + *error_msg = StringPrintf("Invalid storage mode in image header %d", + static_cast<int>(storage_mode)); + return nullptr; + } + + // Reserve output and decompress into it. + std::unique_ptr<MemMap> map(MemMap::MapAnonymous(image_location, + address, + image_header.GetImageSize(), + PROT_READ | PROT_WRITE, + /*low_4gb*/true, + /*reuse*/false, + error_msg)); + if (map != nullptr) { + const size_t stored_size = image_header.GetDataSize(); + const size_t decompress_offset = sizeof(ImageHeader); // Skip the header. + std::unique_ptr<MemMap> temp_map(MemMap::MapFile(sizeof(ImageHeader) + stored_size, + PROT_READ, + MAP_PRIVATE, + fd, + /*offset*/0, + /*low_4gb*/false, + image_filename, + error_msg)); + if (temp_map == nullptr) { + DCHECK(!error_msg->empty()); + return nullptr; + } + memcpy(map->Begin(), &image_header, sizeof(ImageHeader)); + const uint64_t start = NanoTime(); + // LZ4HC and LZ4 have same internal format, both use LZ4_decompress. + TimingLogger::ScopedTiming timing2("LZ4 decompress image", &logger); + const size_t decompressed_size = LZ4_decompress_safe( + reinterpret_cast<char*>(temp_map->Begin()) + sizeof(ImageHeader), + reinterpret_cast<char*>(map->Begin()) + decompress_offset, + stored_size, + map->Size() - decompress_offset); + VLOG(image) << "Decompressing image took " << PrettyDuration(NanoTime() - start); + if (decompressed_size + sizeof(ImageHeader) != image_header.GetImageSize()) { + *error_msg = StringPrintf( + "Decompressed size does not match expected image size %zu vs %zu", + decompressed_size + sizeof(ImageHeader), + image_header.GetImageSize()); + return nullptr; + } + } + + return map.release(); +} + ImageSpace* ImageSpace::Init(const char* image_filename, const char* image_location, bool validate_oat_file, @@ -1235,91 +1309,30 @@ ImageSpace* ImageSpace::Init(const char* image_filename, return nullptr; } - // The preferred address to map the image, null specifies any address. If we manage to map the - // image at the image begin, the amount of fixup work required is minimized. - std::vector<uint8_t*> addresses(1, image_header->GetImageBegin()); - if (image_header->IsPic()) { - // Can also map at a random low_4gb address since we can relocate in-place. - addresses.push_back(nullptr); - } - - // Note: The image header is part of the image due to mmap page alignment required of offset. std::unique_ptr<MemMap> map; - std::string temp_error_msg; - for (uint8_t* address : addresses) { - TimingLogger::ScopedTiming timing("MapImageFile", &logger); - // Only care about the error message for the last address in addresses. We want to avoid the - // overhead of printing the process maps if we can relocate. - std::string* out_error_msg = (address == addresses.back()) ? &temp_error_msg : nullptr; - const ImageHeader::StorageMode storage_mode = image_header->GetStorageMode(); - if (storage_mode == ImageHeader::kStorageModeUncompressed) { - map.reset(MemMap::MapFileAtAddress(address, - image_header->GetImageSize(), - PROT_READ | PROT_WRITE, - MAP_PRIVATE, - file->Fd(), - 0, - /*low_4gb*/true, - /*reuse*/false, - image_filename, - /*out*/out_error_msg)); - } else { - if (storage_mode != ImageHeader::kStorageModeLZ4 && - storage_mode != ImageHeader::kStorageModeLZ4HC) { - *error_msg = StringPrintf("Invalid storage mode in image header %d", - static_cast<int>(storage_mode)); - return nullptr; - } - // Reserve output and decompress into it. - map.reset(MemMap::MapAnonymous(image_location, - address, - image_header->GetImageSize(), - PROT_READ | PROT_WRITE, - /*low_4gb*/true, - /*reuse*/false, - /*out*/out_error_msg)); - if (map != nullptr) { - const size_t stored_size = image_header->GetDataSize(); - const size_t decompress_offset = sizeof(ImageHeader); // Skip the header. - std::unique_ptr<MemMap> temp_map(MemMap::MapFile(sizeof(ImageHeader) + stored_size, - PROT_READ, - MAP_PRIVATE, - file->Fd(), - /*offset*/0, - /*low_4gb*/false, - image_filename, - out_error_msg)); - if (temp_map == nullptr) { - DCHECK(!out_error_msg->empty()); - return nullptr; - } - memcpy(map->Begin(), image_header, sizeof(ImageHeader)); - const uint64_t start = NanoTime(); - // LZ4HC and LZ4 have same internal format, both use LZ4_decompress. - TimingLogger::ScopedTiming timing2("LZ4 decompress image", &logger); - const size_t decompressed_size = LZ4_decompress_safe( - reinterpret_cast<char*>(temp_map->Begin()) + sizeof(ImageHeader), - reinterpret_cast<char*>(map->Begin()) + decompress_offset, - stored_size, - map->Size() - decompress_offset); - VLOG(image) << "Decompressing image took " << PrettyDuration(NanoTime() - start); - if (decompressed_size + sizeof(ImageHeader) != image_header->GetImageSize()) { - *error_msg = StringPrintf( - "Decompressed size does not match expected image size %zu vs %zu", - decompressed_size + sizeof(ImageHeader), - image_header->GetImageSize()); - return nullptr; - } - } - } - if (map != nullptr) { - break; - } - } - + // GetImageBegin is the preferred address to map the image. If we manage to map the + // image at the image begin, the amount of fixup work required is minimized. + map.reset(LoadImageFile(image_filename, + image_location, + *image_header, + image_header->GetImageBegin(), + file->Fd(), + logger, + error_msg)); + // If the header specifies PIC mode, we can also map at a random low_4gb address since we can + // relocate in-place. + if (map == nullptr && image_header->IsPic()) { + map.reset(LoadImageFile(image_filename, + image_location, + *image_header, + /* address */ nullptr, + file->Fd(), + logger, + error_msg)); + } + // Were we able to load something and continue? if (map == nullptr) { - DCHECK(!temp_error_msg.empty()); - *error_msg = temp_error_msg; + DCHECK(!error_msg->empty()); return nullptr; } DCHECK_EQ(0, memcmp(image_header, map->Begin(), sizeof(ImageHeader))); diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc index d983a9fa19..c644cde5db 100644 --- a/runtime/java_vm_ext.cc +++ b/runtime/java_vm_ext.cc @@ -74,6 +74,10 @@ class SharedLibrary { if (self != nullptr) { self->GetJniEnv()->DeleteWeakGlobalRef(class_loader_); } + + if (!needs_native_bridge_) { + android::CloseNativeLibrary(handle_); + } } jweak GetClassLoader() const { @@ -271,8 +275,7 @@ class Libraries { REQUIRES(!Locks::jni_libraries_lock_) SHARED_REQUIRES(Locks::mutator_lock_) { ScopedObjectAccessUnchecked soa(Thread::Current()); - typedef void (*JNI_OnUnloadFn)(JavaVM*, void*); - std::vector<JNI_OnUnloadFn> unload_functions; + std::vector<SharedLibrary*> unload_libraries; { MutexLock mu(soa.Self(), *Locks::jni_libraries_lock_); for (auto it = libraries_.begin(); it != libraries_.end(); ) { @@ -283,15 +286,7 @@ class Libraries { // the native libraries of the boot class loader. if (class_loader != nullptr && soa.Self()->IsJWeakCleared(class_loader)) { - void* const sym = library->FindSymbol("JNI_OnUnload", nullptr); - if (sym == nullptr) { - VLOG(jni) << "[No JNI_OnUnload found in \"" << library->GetPath() << "\"]"; - } else { - VLOG(jni) << "[JNI_OnUnload found for \"" << library->GetPath() << "\"]"; - JNI_OnUnloadFn jni_on_unload = reinterpret_cast<JNI_OnUnloadFn>(sym); - unload_functions.push_back(jni_on_unload); - } - delete library; + unload_libraries.push_back(library); it = libraries_.erase(it); } else { ++it; @@ -299,9 +294,17 @@ class Libraries { } } // Do this without holding the jni libraries lock to prevent possible deadlocks. - for (JNI_OnUnloadFn fn : unload_functions) { - VLOG(jni) << "Calling JNI_OnUnload"; - (*fn)(soa.Vm(), nullptr); + typedef void (*JNI_OnUnloadFn)(JavaVM*, void*); + for (auto library : unload_libraries) { + void* const sym = library->FindSymbol("JNI_OnUnload", nullptr); + if (sym == nullptr) { + VLOG(jni) << "[No JNI_OnUnload found in \"" << library->GetPath() << "\"]"; + } else { + VLOG(jni) << "[JNI_OnUnload found for \"" << library->GetPath() << "\"]: Calling..."; + JNI_OnUnloadFn jni_on_unload = reinterpret_cast<JNI_OnUnloadFn>(sym); + jni_on_unload(soa.Vm(), nullptr); + } + delete library; } } @@ -739,8 +742,14 @@ bool JavaVMExt::LoadNativeLibrary(JNIEnv* env, // As the incoming class loader is reachable/alive during the call of this function, // it's okay to decode it without worrying about unexpectedly marking it alive. mirror::ClassLoader* loader = soa.Decode<mirror::ClassLoader*>(class_loader); - class_loader_allocator = - Runtime::Current()->GetClassLinker()->GetAllocatorForClassLoader(loader); + + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + if (class_linker->IsBootClassLoader(soa, loader)) { + loader = nullptr; + class_loader = nullptr; + } + + class_loader_allocator = class_linker->GetAllocatorForClassLoader(loader); CHECK(class_loader_allocator != nullptr); } if (library != nullptr) { diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc index c99d3636a1..5039d2de07 100644 --- a/runtime/jit/offline_profiling_info.cc +++ b/runtime/jit/offline_profiling_info.cc @@ -19,6 +19,7 @@ #include "errno.h" #include <limits.h> #include <vector> +#include <stdlib.h> #include <sys/file.h> #include <sys/stat.h> #include <sys/uio.h> @@ -40,6 +41,11 @@ const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '1', '\0' static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX; +// Debug flag to ignore checksums when testing if a method or a class is present in the profile. +// Used to facilitate testing profile guided compilation across a large number of apps +// using the same test profile. +static constexpr bool kDebugIgnoreChecksum = false; + // Transform the actual dex location into relative paths. // Note: this is OK because we don't store profiles of different apps into the same file. // Apps with split apks don't cause trouble because each split has a different name and will not @@ -547,10 +553,14 @@ bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other) { return true; } +static bool ChecksumMatch(const DexFile& dex_file, uint32_t checksum) { + return kDebugIgnoreChecksum || dex_file.GetLocationChecksum() == checksum; +} + bool ProfileCompilationInfo::ContainsMethod(const MethodReference& method_ref) const { auto info_it = info_.find(GetProfileDexFileKey(method_ref.dex_file->GetLocation())); if (info_it != info_.end()) { - if (method_ref.dex_file->GetLocationChecksum() != info_it->second.checksum) { + if (!ChecksumMatch(*method_ref.dex_file, info_it->second.checksum)) { return false; } const std::set<uint16_t>& methods = info_it->second.method_set; @@ -562,7 +572,7 @@ bool ProfileCompilationInfo::ContainsMethod(const MethodReference& method_ref) c bool ProfileCompilationInfo::ContainsClass(const DexFile& dex_file, uint16_t class_def_idx) const { auto info_it = info_.find(GetProfileDexFileKey(dex_file.GetLocation())); if (info_it != info_.end()) { - if (dex_file.GetLocationChecksum() != info_it->second.checksum) { + if (!ChecksumMatch(dex_file, info_it->second.checksum)) { return false; } const std::set<uint16_t>& classes = info_it->second.class_set; @@ -659,4 +669,47 @@ void ProfileCompilationInfo::ClearResolvedClasses() { } } +// Naive implementation to generate a random profile file suitable for testing. +bool ProfileCompilationInfo::GenerateTestProfile(int fd, + uint16_t number_of_dex_files, + uint16_t method_ratio, + uint16_t class_ratio) { + const std::string base_dex_location = "base.apk"; + ProfileCompilationInfo info; + // The limits are defined by the dex specification. + uint16_t max_method = std::numeric_limits<uint16_t>::max(); + uint16_t max_classes = std::numeric_limits<uint16_t>::max(); + uint16_t number_of_methods = max_method * method_ratio / 100; + uint16_t number_of_classes = max_classes * class_ratio / 100; + + srand(MicroTime()); + + // Make sure we generate more samples with a low index value. + // This makes it more likely to hit valid method/class indices in small apps. + const uint16_t kFavorFirstN = 10000; + const uint16_t kFavorSplit = 2; + + for (uint16_t i = 0; i < number_of_dex_files; i++) { + std::string dex_location = DexFile::GetMultiDexLocation(i, base_dex_location.c_str()); + std::string profile_key = GetProfileDexFileKey(dex_location); + + for (uint16_t m = 0; m < number_of_methods; m++) { + uint16_t method_idx = rand() % max_method; + if (m < (number_of_methods / kFavorSplit)) { + method_idx %= kFavorFirstN; + } + info.AddMethodIndex(profile_key, 0, method_idx); + } + + for (uint16_t c = 0; c < number_of_classes; c++) { + uint16_t class_idx = rand() % max_classes; + if (c < (number_of_classes / kFavorSplit)) { + class_idx %= kFavorFirstN; + } + info.AddClassIndex(profile_key, 0, class_idx); + } + } + return info.Save(fd); +} + } // namespace art diff --git a/runtime/jit/offline_profiling_info.h b/runtime/jit/offline_profiling_info.h index 5a07da79a1..0b26f9bd0c 100644 --- a/runtime/jit/offline_profiling_info.h +++ b/runtime/jit/offline_profiling_info.h @@ -87,6 +87,11 @@ class ProfileCompilationInfo { // Clears the resolved classes from the current object. void ClearResolvedClasses(); + static bool GenerateTestProfile(int fd, + uint16_t number_of_dex_files, + uint16_t method_ratio, + uint16_t class_ratio); + private: enum ProfileLoadSatus { kProfileLoadIOError, diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc index 771f8ed290..c047ba20f5 100644 --- a/runtime/mem_map.cc +++ b/runtime/mem_map.cc @@ -157,6 +157,8 @@ bool MemMap::ContainedWithinExistingMap(uint8_t* ptr, size_t size, std::string* } return false; } + + ScopedBacktraceMapIteratorLock lock(map.get()); for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) { if ((begin >= it->start && begin < it->end) // start of new within old && (end > it->start && end <= it->end)) { // end of new within old @@ -180,6 +182,7 @@ static bool CheckNonOverlapping(uintptr_t begin, *error_msg = StringPrintf("Failed to build process map"); return false; } + ScopedBacktraceMapIteratorLock(map.get()); for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) { if ((begin >= it->start && begin < it->end) // start of new within old || (end > it->start && end < it->end) // end of new within old @@ -339,7 +342,9 @@ MemMap* MemMap::MapAnonymous(const char* name, if (actual == MAP_FAILED) { if (error_msg != nullptr) { - PrintFileToLog("/proc/self/maps", LogSeverity::WARNING); + if (kIsDebugBuild || VLOG_IS_ON(oat)) { + PrintFileToLog("/proc/self/maps", LogSeverity::WARNING); + } *error_msg = StringPrintf("Failed anonymous mmap(%p, %zd, 0x%x, 0x%x, %d, 0): %s. " "See process maps in the log.", diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 0d95bb175b..b783a019e7 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -521,7 +521,7 @@ template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption> inline MemberOffset Class::GetFirstReferenceInstanceFieldOffset() { Class* super_class = GetSuperClass<kVerifyFlags, kReadBarrierOption>(); return (super_class != nullptr) - ? MemberOffset(RoundUp(super_class->GetObjectSize(), + ? MemberOffset(RoundUp(super_class->GetObjectSize<kVerifyFlags, kReadBarrierOption>(), sizeof(mirror::HeapReference<mirror::Object>))) : ClassOffset(); } @@ -766,7 +766,8 @@ inline void Class::VisitReferences(mirror::Class* klass, const Visitor& visitor) } if (kVisitNativeRoots) { // Since this class is reachable, we must also visit the associated roots when we scan it. - VisitNativeRoots(visitor, Runtime::Current()->GetClassLinker()->GetImagePointerSize()); + VisitNativeRoots<kReadBarrierOption>( + visitor, Runtime::Current()->GetClassLinker()->GetImagePointerSize()); } } @@ -905,24 +906,24 @@ inline GcRoot<String>* Class::GetDexCacheStrings() { return GetFieldPtr<GcRoot<String>*>(DexCacheStringsOffset()); } -template<class Visitor> +template<ReadBarrierOption kReadBarrierOption, class Visitor> void mirror::Class::VisitNativeRoots(Visitor& visitor, size_t pointer_size) { for (ArtField& field : GetSFieldsUnchecked()) { // Visit roots first in case the declaring class gets moved. field.VisitRoots(visitor); if (kIsDebugBuild && IsResolved()) { - CHECK_EQ(field.GetDeclaringClass(), this) << GetStatus(); + CHECK_EQ(field.GetDeclaringClass<kReadBarrierOption>(), this) << GetStatus(); } } for (ArtField& field : GetIFieldsUnchecked()) { // Visit roots first in case the declaring class gets moved. field.VisitRoots(visitor); if (kIsDebugBuild && IsResolved()) { - CHECK_EQ(field.GetDeclaringClass(), this) << GetStatus(); + CHECK_EQ(field.GetDeclaringClass<kReadBarrierOption>(), this) << GetStatus(); } } for (ArtMethod& method : GetMethods(pointer_size)) { - method.VisitRoots(visitor, pointer_size); + method.VisitRoots<kReadBarrierOption>(visitor, pointer_size); } } diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index b1abf948ab..9670accf56 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -1152,7 +1152,7 @@ class MANAGED Class FINAL : public Object { // Visit native roots visits roots which are keyed off the native pointers such as ArtFields and // ArtMethods. - template<class Visitor> + template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier, class Visitor> void VisitNativeRoots(Visitor& visitor, size_t pointer_size) SHARED_REQUIRES(Locks::mutator_lock_); diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h index 628554225c..96f2098ab0 100644 --- a/runtime/mirror/string-inl.h +++ b/runtime/mirror/string-inl.h @@ -20,6 +20,7 @@ #include "array.h" #include "base/bit_utils.h" #include "class.h" +#include "common_throws.h" #include "gc/heap-inl.h" #include "globals.h" #include "intern_table.h" @@ -33,7 +34,7 @@ namespace art { namespace mirror { inline uint32_t String::ClassSize(size_t pointer_size) { - uint32_t vtable_entries = Object::kVTableLength + 56; + uint32_t vtable_entries = Object::kVTableLength + 57; return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 1, 2, pointer_size); } @@ -134,9 +135,7 @@ inline String* String::Intern() { inline uint16_t String::CharAt(int32_t index) { int32_t count = GetField32(OFFSET_OF_OBJECT_MEMBER(String, count_)); if (UNLIKELY((index < 0) || (index >= count))) { - Thread* self = Thread::Current(); - self->ThrowNewExceptionF("Ljava/lang/StringIndexOutOfBoundsException;", - "length=%i; index=%i", count, index); + ThrowStringIndexOutOfBoundsException(index, count); return 0; } return GetValue()[index]; diff --git a/runtime/oat.h b/runtime/oat.h index 57675dc738..6243660494 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,7 +32,7 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - static constexpr uint8_t kOatVersion[] = { '0', '7', '9', '\0' }; + static constexpr uint8_t kOatVersion[] = { '0', '8', '2', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 62c723e76f..61dc287927 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -1167,12 +1167,15 @@ size_t OatFile::OatDexFile::FileSize() const { std::unique_ptr<const DexFile> OatFile::OatDexFile::OpenDexFile(std::string* error_msg) const { ScopedTrace trace(__PRETTY_FUNCTION__); + static constexpr bool kVerify = false; + static constexpr bool kVerifyChecksum = false; return DexFile::Open(dex_file_pointer_, FileSize(), dex_file_location_, dex_file_location_checksum_, this, - false /* verify */, + kVerify, + kVerifyChecksum, error_msg); } diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index c79a9a67b3..e3cc77f2c0 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -56,12 +56,11 @@ class OatFileAssistantTest : public CommonRuntimeTest { odex_dir_ = odex_oat_dir_ + "/" + std::string(GetInstructionSetString(kRuntimeISA)); ASSERT_EQ(0, mkdir(odex_dir_.c_str(), 0700)); - // Verify the environment is as we expect uint32_t checksum; std::string error_msg; - ASSERT_TRUE(OS::FileExists(GetImageFile().c_str())) - << "Expected pre-compiled boot image to be at: " << GetImageFile(); + ASSERT_TRUE(OS::FileExists(GetSystemImageFile().c_str())) + << "Expected pre-compiled boot image to be at: " << GetSystemImageFile(); ASSERT_TRUE(OS::FileExists(GetDexSrc1().c_str())) << "Expected dex file to be at: " << GetDexSrc1(); ASSERT_TRUE(OS::FileExists(GetStrippedDexSrc1().c_str())) @@ -73,20 +72,42 @@ class OatFileAssistantTest : public CommonRuntimeTest { // GetMultiDexSrc2 should have the same primary dex checksum as // GetMultiDexSrc1, but a different secondary dex checksum. + static constexpr bool kVerifyChecksum = true; std::vector<std::unique_ptr<const DexFile>> multi1; ASSERT_TRUE(DexFile::Open(GetMultiDexSrc1().c_str(), - GetMultiDexSrc1().c_str(), &error_msg, &multi1)) << error_msg; + GetMultiDexSrc1().c_str(), kVerifyChecksum, &error_msg, &multi1)) << error_msg; ASSERT_GT(multi1.size(), 1u); std::vector<std::unique_ptr<const DexFile>> multi2; ASSERT_TRUE(DexFile::Open(GetMultiDexSrc2().c_str(), - GetMultiDexSrc2().c_str(), &error_msg, &multi2)) << error_msg; + GetMultiDexSrc2().c_str(), kVerifyChecksum, &error_msg, &multi2)) << error_msg; ASSERT_GT(multi2.size(), 1u); ASSERT_EQ(multi1[0]->GetLocationChecksum(), multi2[0]->GetLocationChecksum()); ASSERT_NE(multi1[1]->GetLocationChecksum(), multi2[1]->GetLocationChecksum()); } + // Pre-Relocate the image to a known non-zero offset so we don't have to + // deal with the runtime randomly relocating the image by 0 and messing up + // the expected results of the tests. + bool PreRelocateImage(std::string* error_msg) { + std::string image; + if (!GetCachedImageFile(&image, error_msg)) { + return false; + } + + std::string patchoat = GetAndroidRoot(); + patchoat += kIsDebugBuild ? "/bin/patchoatd" : "/bin/patchoat"; + + std::vector<std::string> argv; + argv.push_back(patchoat); + argv.push_back("--input-image-location=" + GetImageLocation()); + argv.push_back("--output-image-file=" + image); + argv.push_back("--instruction-set=" + std::string(GetInstructionSetString(kRuntimeISA))); + argv.push_back("--base-offset-delta=0x00008000"); + return Exec(argv, error_msg); + } + virtual void SetUpRuntimeOptions(RuntimeOptions* options) { // options->push_back(std::make_pair("-verbose:oat", nullptr)); @@ -99,6 +120,9 @@ class OatFileAssistantTest : public CommonRuntimeTest { } virtual void PreRuntimeCreate() { + std::string error_msg; + ASSERT_TRUE(PreRelocateImage(&error_msg)) << error_msg; + UnreserveImageSpace(); } @@ -144,11 +168,16 @@ class OatFileAssistantTest : public CommonRuntimeTest { return GetImageDirectory() + "/core.art"; } - std::string GetImageFile() { + std::string GetSystemImageFile() { return GetImageDirectory() + "/" + GetInstructionSetString(kRuntimeISA) + "/core.art"; } + bool GetCachedImageFile(/*out*/std::string* image, std::string* error_msg) { + std::string cache = GetDalvikCache(GetInstructionSetString(kRuntimeISA), true); + return GetDalvikCacheFilename(GetImageLocation().c_str(), cache.c_str(), image, error_msg); + } + std::string GetDexSrc1() { return GetTestDexFileName("Main"); } @@ -189,34 +218,31 @@ class OatFileAssistantTest : public CommonRuntimeTest { // The generated odex file will be un-relocated. void GenerateOdexForTest(const std::string& dex_location, const std::string& odex_location, - CompilerFilter::Filter filter) { - // To generate an un-relocated odex file, we first compile a relocated - // version of the file, then manually call patchoat to make it look as if - // it is unrelocated. - std::string relocated_odex_location = odex_location + ".relocated"; + CompilerFilter::Filter filter, + bool pic = false, + bool with_patch_info = true) { + // Temporarily redirect the dalvik cache so dex2oat doesn't find the + // relocated image file. + std::string android_data_tmp = GetScratchDir() + "AndroidDataTmp"; + setenv("ANDROID_DATA", android_data_tmp.c_str(), 1); std::vector<std::string> args; args.push_back("--dex-file=" + dex_location); - args.push_back("--oat-file=" + relocated_odex_location); + args.push_back("--oat-file=" + odex_location); args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter)); + args.push_back("--runtime-arg"); + args.push_back("-Xnorelocate"); - // We need to use the quick compiler to generate non-PIC code, because - // the optimizing compiler always generates PIC. - args.push_back("--compiler-backend=Quick"); - args.push_back("--include-patch-information"); + if (pic) { + args.push_back("--compile-pic"); + } + + if (with_patch_info) { + args.push_back("--include-patch-information"); + } std::string error_msg; ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg; - - // Use patchoat to unrelocate the relocated odex file. - Runtime* runtime = Runtime::Current(); - std::vector<std::string> argv; - argv.push_back(runtime->GetPatchoatExecutable()); - argv.push_back("--instruction-set=" + std::string(GetInstructionSetString(kRuntimeISA))); - argv.push_back("--input-oat-file=" + relocated_odex_location); - argv.push_back("--output-oat-file=" + odex_location); - argv.push_back("--base-offset-delta=0x00008000"); - std::string command_line(Join(argv, ' ')); - ASSERT_TRUE(Exec(argv, &error_msg)) << error_msg; + setenv("ANDROID_DATA", android_data_.c_str(), 1); // Verify the odex file was generated as expected and really is // unrelocated. @@ -229,13 +255,13 @@ class OatFileAssistantTest : public CommonRuntimeTest { dex_location.c_str(), &error_msg)); ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; - EXPECT_FALSE(odex_file->IsPic()); - EXPECT_TRUE(odex_file->HasPatchInfo()); + EXPECT_EQ(pic, odex_file->IsPic()); + EXPECT_EQ(with_patch_info, odex_file->HasPatchInfo()); EXPECT_EQ(filter, odex_file->GetCompilerFilter()); if (CompilerFilter::IsBytecodeCompilationEnabled(filter)) { const std::vector<gc::space::ImageSpace*> image_spaces = - runtime->GetHeap()->GetBootImageSpaces(); + Runtime::Current()->GetHeap()->GetBootImageSpaces(); ASSERT_TRUE(!image_spaces.empty() && image_spaces[0] != nullptr); const ImageHeader& image_header = image_spaces[0]->GetImageHeader(); const OatHeader& oat_header = odex_file->GetOatHeader(); @@ -250,71 +276,15 @@ class OatFileAssistantTest : public CommonRuntimeTest { void GeneratePicOdexForTest(const std::string& dex_location, const std::string& odex_location, CompilerFilter::Filter filter) { - // Temporarily redirect the dalvik cache so dex2oat doesn't find the - // relocated image file. - std::string android_data_tmp = GetScratchDir() + "AndroidDataTmp"; - setenv("ANDROID_DATA", android_data_tmp.c_str(), 1); - std::vector<std::string> args; - args.push_back("--dex-file=" + dex_location); - args.push_back("--oat-file=" + odex_location); - args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter)); - args.push_back("--compile-pic"); - args.push_back("--runtime-arg"); - args.push_back("-Xnorelocate"); - std::string error_msg; - ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg; - setenv("ANDROID_DATA", android_data_.c_str(), 1); - - // Verify the odex file was generated as expected. - std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(), - odex_location.c_str(), - nullptr, - nullptr, - false, - /*low_4gb*/false, - dex_location.c_str(), - &error_msg)); - ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; - EXPECT_TRUE(odex_file->IsPic()); - EXPECT_EQ(filter, odex_file->GetCompilerFilter()); + GenerateOdexForTest(dex_location, odex_location, filter, true, false); } // Generate a non-PIC odex file without patch information for the purposes // of test. The generated odex file will be un-relocated. - // TODO: This won't work correctly if we depend on the boot image being - // randomly relocated by a non-zero amount. We should have a better solution - // for avoiding that flakiness and duplicating code to generate odex and oat - // files for test. void GenerateNoPatchOdexForTest(const std::string& dex_location, const std::string& odex_location, CompilerFilter::Filter filter) { - // Temporarily redirect the dalvik cache so dex2oat doesn't find the - // relocated image file. - std::string android_data_tmp = GetScratchDir() + "AndroidDataTmp"; - setenv("ANDROID_DATA", android_data_tmp.c_str(), 1); - std::vector<std::string> args; - args.push_back("--dex-file=" + dex_location); - args.push_back("--oat-file=" + odex_location); - args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter)); - args.push_back("--runtime-arg"); - args.push_back("-Xnorelocate"); - std::string error_msg; - ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg; - setenv("ANDROID_DATA", android_data_.c_str(), 1); - - // Verify the odex file was generated as expected. - std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(), - odex_location.c_str(), - nullptr, - nullptr, - false, - /*low_4gb*/false, - dex_location.c_str(), - &error_msg)); - ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; - EXPECT_FALSE(odex_file->IsPic()); - EXPECT_FALSE(odex_file->HasPatchInfo()); - EXPECT_EQ(filter, odex_file->GetCompilerFilter()); + GenerateOdexForTest(dex_location, odex_location, filter, false, false); } private: @@ -326,11 +296,10 @@ class OatFileAssistantTest : public CommonRuntimeTest { MemMap::Init(); // Ensure a chunk of memory is reserved for the image space. - uintptr_t reservation_start = ART_BASE_ADDRESS + ART_BASE_ADDRESS_MIN_DELTA; - uintptr_t reservation_end = ART_BASE_ADDRESS + ART_BASE_ADDRESS_MAX_DELTA - // Include the main space that has to come right after the - // image in case of the GSS collector. - + 384 * MB; + // The reservation_end includes room for the main space that has to come + // right after the image in case of the GSS collector. + uintptr_t reservation_start = ART_BASE_ADDRESS; + uintptr_t reservation_end = ART_BASE_ADDRESS + 384 * MB; std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid(), true)); ASSERT_TRUE(map.get() != nullptr) << "Failed to build process map"; diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index fbae1daf43..b7e604036e 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -696,7 +696,9 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( if (dex_files.empty()) { if (oat_file_assistant.HasOriginalDexFiles()) { if (Runtime::Current()->IsDexFileFallbackEnabled()) { - if (!DexFile::Open(dex_location, dex_location, /*out*/ &error_msg, &dex_files)) { + static constexpr bool kVerifyChecksum = true; + if (!DexFile::Open( + dex_location, dex_location, kVerifyChecksum, /*out*/ &error_msg, &dex_files)) { LOG(WARNING) << error_msg; error_msgs->push_back("Failed to open dex files from " + std::string(dex_location) + " because: " + error_msg); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index caf554550e..40e1b1363a 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -883,12 +883,13 @@ static size_t OpenDexFiles(const std::vector<std::string>& dex_filenames, for (size_t i = 0; i < dex_filenames.size(); i++) { const char* dex_filename = dex_filenames[i].c_str(); const char* dex_location = dex_locations[i].c_str(); + static constexpr bool kVerifyChecksum = true; std::string error_msg; if (!OS::FileExists(dex_filename)) { LOG(WARNING) << "Skipping non-existent dex file '" << dex_filename << "'"; continue; } - if (!DexFile::Open(dex_filename, dex_location, &error_msg, dex_files)) { + if (!DexFile::Open(dex_filename, dex_location, kVerifyChecksum, &error_msg, dex_files)) { LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "': " << error_msg; ++failure_count; } diff --git a/runtime/signal_set.h b/runtime/signal_set.h index c272514f61..6f888525cb 100644 --- a/runtime/signal_set.h +++ b/runtime/signal_set.h @@ -38,8 +38,8 @@ class SignalSet { } void Block() { - if (sigprocmask(SIG_BLOCK, &set_, nullptr) == -1) { - PLOG(FATAL) << "sigprocmask failed"; + if (pthread_sigmask(SIG_BLOCK, &set_, nullptr) != 0) { + PLOG(FATAL) << "pthread_sigmask failed"; } } diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 7c50f97d39..4647d67699 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -19,6 +19,7 @@ #include "base/bit_vector.h" #include "base/bit_utils.h" +#include "dex_file.h" #include "memory_region.h" #include "leb128.h" @@ -892,7 +893,11 @@ class InlineInfoEncoding { total_bit_size_ += MinimumBitsToStore(method_index_max); dex_pc_bit_offset_ = dchecked_integral_cast<uint8_t>(total_bit_size_); - total_bit_size_ += MinimumBitsToStore(1 /* kNoDexPc */ + dex_pc_max); + // Note: We're not encoding the dex pc if there is none. That's the case + // for an intrinsified native method, such as String.charAt(). + if (dex_pc_max != DexFile::kDexNoIndex) { + total_bit_size_ += MinimumBitsToStore(1 /* kNoDexPc */ + dex_pc_max); + } invoke_type_bit_offset_ = dchecked_integral_cast<uint8_t>(total_bit_size_); total_bit_size_ += MinimumBitsToStore(invoke_type_max); diff --git a/runtime/thread.cc b/runtime/thread.cc index f1f4a122b4..b9ee4421b9 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -924,10 +924,22 @@ bool Thread::InitStackHwm() { Runtime* runtime = Runtime::Current(); bool implicit_stack_check = !runtime->ExplicitStackOverflowChecks() && !runtime->IsAotCompiler(); + + // Valgrind on arm doesn't give the right values here. Do not install the guard page, and + // effectively disable stack overflow checks (we'll get segfaults, potentially) by setting + // stack_begin to 0. + const bool valgrind_on_arm = + (kRuntimeISA == kArm || kRuntimeISA == kArm64) && + kMemoryToolIsValgrind && + RUNNING_ON_MEMORY_TOOL != 0; + if (valgrind_on_arm) { + tlsPtr_.stack_begin = nullptr; + } + ResetDefaultStackEnd(); // Install the protected region if we are doing implicit overflow checks. - if (implicit_stack_check) { + if (implicit_stack_check && !valgrind_on_arm) { // The thread might have protected region at the bottom. We need // to install our own region so we need to move the limits // of the stack to make room for it. @@ -1122,32 +1134,36 @@ void Thread::ClearSuspendBarrier(AtomicInteger* target) { } void Thread::RunCheckpointFunction() { - Closure *checkpoints[kMaxCheckpoints]; - - // Grab the suspend_count lock and copy the current set of - // checkpoints. Then clear the list and the flag. The RequestCheckpoint - // function will also grab this lock so we prevent a race between setting - // the kCheckpointRequest flag and clearing it. - { - MutexLock mu(this, *Locks::thread_suspend_count_lock_); - for (uint32_t i = 0; i < kMaxCheckpoints; ++i) { - checkpoints[i] = tlsPtr_.checkpoint_functions[i]; - tlsPtr_.checkpoint_functions[i] = nullptr; + bool done = false; + do { + // Grab the suspend_count lock and copy the checkpoints one by one. When the last checkpoint is + // copied, clear the list and the flag. The RequestCheckpoint function will also grab this lock + // to prevent a race between setting the kCheckpointRequest flag and clearing it. + Closure* checkpoint = nullptr; + { + MutexLock mu(this, *Locks::thread_suspend_count_lock_); + if (tlsPtr_.checkpoint_function != nullptr) { + checkpoint = tlsPtr_.checkpoint_function; + if (!checkpoint_overflow_.empty()) { + // Overflow list not empty, copy the first one out and continue. + tlsPtr_.checkpoint_function = checkpoint_overflow_.front(); + checkpoint_overflow_.pop_front(); + } else { + // No overflow checkpoints, this means that we are on the last pending checkpoint. + tlsPtr_.checkpoint_function = nullptr; + AtomicClearFlag(kCheckpointRequest); + done = true; + } + } else { + LOG(FATAL) << "Checkpoint flag set without pending checkpoint"; + } } - AtomicClearFlag(kCheckpointRequest); - } - // Outside the lock, run all the checkpoint functions that - // we collected. - bool found_checkpoint = false; - for (uint32_t i = 0; i < kMaxCheckpoints; ++i) { - if (checkpoints[i] != nullptr) { - ScopedTrace trace("Run checkpoint function"); - checkpoints[i]->Run(this); - found_checkpoint = true; - } - } - CHECK(found_checkpoint); + // Outside the lock, run the checkpoint functions that we collected. + ScopedTrace trace("Run checkpoint function"); + DCHECK(checkpoint != nullptr); + checkpoint->Run(this); + } while (!done); } bool Thread::RequestCheckpoint(Closure* function) { @@ -1157,20 +1173,6 @@ bool Thread::RequestCheckpoint(Closure* function) { return false; // Fail, thread is suspended and so can't run a checkpoint. } - uint32_t available_checkpoint = kMaxCheckpoints; - for (uint32_t i = 0 ; i < kMaxCheckpoints; ++i) { - if (tlsPtr_.checkpoint_functions[i] == nullptr) { - available_checkpoint = i; - break; - } - } - if (available_checkpoint == kMaxCheckpoints) { - // No checkpoint functions available, we can't run a checkpoint - return false; - } - tlsPtr_.checkpoint_functions[available_checkpoint] = function; - - // Checkpoint function installed now install flag bit. // We must be runnable to request a checkpoint. DCHECK_EQ(old_state_and_flags.as_struct.state, kRunnable); union StateAndFlags new_state_and_flags; @@ -1178,11 +1180,13 @@ bool Thread::RequestCheckpoint(Closure* function) { new_state_and_flags.as_struct.flags |= kCheckpointRequest; bool success = tls32_.state_and_flags.as_atomic_int.CompareExchangeStrongSequentiallyConsistent( old_state_and_flags.as_int, new_state_and_flags.as_int); - if (UNLIKELY(!success)) { - // The thread changed state before the checkpoint was installed. - CHECK_EQ(tlsPtr_.checkpoint_functions[available_checkpoint], function); - tlsPtr_.checkpoint_functions[available_checkpoint] = nullptr; - } else { + if (success) { + // Succeeded setting checkpoint flag, now insert the actual checkpoint. + if (tlsPtr_.checkpoint_function == nullptr) { + tlsPtr_.checkpoint_function = function; + } else { + checkpoint_overflow_.push_back(function); + } CHECK_EQ(ReadFlag(kCheckpointRequest), true); TriggerSuspend(); } @@ -1624,9 +1628,7 @@ Thread::Thread(bool daemon) : tls32_(daemon), wait_monitor_(nullptr), interrupte std::fill(tlsPtr_.rosalloc_runs, tlsPtr_.rosalloc_runs + kNumRosAllocThreadLocalSizeBracketsInThread, gc::allocator::RosAlloc::GetDedicatedFullRun()); - for (uint32_t i = 0; i < kMaxCheckpoints; ++i) { - tlsPtr_.checkpoint_functions[i] = nullptr; - } + tlsPtr_.checkpoint_function = nullptr; for (uint32_t i = 0; i < kMaxSuspendBarriers; ++i) { tlsPtr_.active_suspend_barriers[i] = nullptr; } @@ -1767,9 +1769,8 @@ Thread::~Thread() { } CHECK_NE(GetState(), kRunnable); CHECK_NE(ReadFlag(kCheckpointRequest), true); - CHECK(tlsPtr_.checkpoint_functions[0] == nullptr); - CHECK(tlsPtr_.checkpoint_functions[1] == nullptr); - CHECK(tlsPtr_.checkpoint_functions[2] == nullptr); + CHECK(tlsPtr_.checkpoint_function == nullptr); + CHECK_EQ(checkpoint_overflow_.size(), 0u); CHECK(tlsPtr_.flip_function == nullptr); CHECK_EQ(tls32_.suspended_at_suspend_check, false); diff --git a/runtime/thread.h b/runtime/thread.h index 3c367ee5b6..9b6a20ec59 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -1220,9 +1220,6 @@ class Thread { static void ThreadExitCallback(void* arg); - // Maximum number of checkpoint functions. - static constexpr uint32_t kMaxCheckpoints = 3; - // Maximum number of suspend barriers. static constexpr uint32_t kMaxSuspendBarriers = 3; @@ -1352,8 +1349,8 @@ class Thread { instrumentation_stack(nullptr), debug_invoke_req(nullptr), single_step_control(nullptr), stacked_shadow_frame_record(nullptr), deoptimization_context_stack(nullptr), frame_id_to_shadow_frame(nullptr), name(nullptr), pthread_self(0), - last_no_thread_suspension_cause(nullptr), thread_local_objects(0), - thread_local_start(nullptr), thread_local_pos(nullptr), thread_local_end(nullptr), + last_no_thread_suspension_cause(nullptr), thread_local_start(nullptr), + thread_local_pos(nullptr), thread_local_end(nullptr), thread_local_objects(0), mterp_current_ibase(nullptr), mterp_default_ibase(nullptr), mterp_alt_ibase(nullptr), thread_local_alloc_stack_top(nullptr), thread_local_alloc_stack_end(nullptr), nested_signal_state(nullptr), flip_function(nullptr), method_verifier(nullptr), @@ -1452,9 +1449,9 @@ class Thread { // If no_thread_suspension_ is > 0, what is causing that assertion. const char* last_no_thread_suspension_cause; - // Pending checkpoint function or null if non-pending. Installation guarding by - // Locks::thread_suspend_count_lock_. - Closure* checkpoint_functions[kMaxCheckpoints]; + // Pending checkpoint function or null if non-pending. If this checkpoint is set and someone\ + // requests another checkpoint, it goes to the checkpoint overflow list. + Closure* checkpoint_function GUARDED_BY(Locks::thread_suspend_count_lock_); // Pending barriers that require passing or NULL if non-pending. Installation guarding by // Locks::thread_suspend_count_lock_. @@ -1468,12 +1465,12 @@ class Thread { QuickEntryPoints quick_entrypoints; // Thread-local allocation pointer. - size_t thread_local_objects; uint8_t* thread_local_start; // thread_local_pos and thread_local_end must be consecutive for ldrd and are 8 byte aligned for // potentially better performance. uint8_t* thread_local_pos; uint8_t* thread_local_end; + size_t thread_local_objects; // Mterp jump table bases. void* mterp_current_ibase; @@ -1517,6 +1514,9 @@ class Thread { // Debug disable read barrier count, only is checked for debug builds and only in the runtime. uint8_t debug_disallow_read_barrier_ = 0; + // Pending extra checkpoints if checkpoint_function_ is already used. + std::list<Closure*> checkpoint_overflow_ GUARDED_BY(Locks::thread_suspend_count_lock_); + friend class Dbg; // For SetStateUnsafe. friend class gc::collector::SemiSpace; // For getting stack traces. friend class Runtime; // For CreatePeer. diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 80b99fcd02..97bcb7d406 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -1146,9 +1146,10 @@ void ThreadList::WaitForOtherNonDaemonThreadsToExit() { void ThreadList::SuspendAllDaemonThreadsForShutdown() { ScopedTrace trace(__PRETTY_FUNCTION__); Thread* self = Thread::Current(); - MutexLock mu(self, *Locks::thread_list_lock_); size_t daemons_left = 0; - { // Tell all the daemons it's time to suspend. + { + // Tell all the daemons it's time to suspend. + MutexLock mu(self, *Locks::thread_list_lock_); MutexLock mu2(self, *Locks::thread_suspend_count_lock_); for (const auto& thread : list_) { // This is only run after all non-daemon threads have exited, so the remainder should all be @@ -1177,13 +1178,16 @@ void ThreadList::SuspendAllDaemonThreadsForShutdown() { static constexpr size_t kSleepMicroseconds = 1000; for (size_t i = 0; i < kTimeoutMicroseconds / kSleepMicroseconds; ++i) { bool all_suspended = true; - for (const auto& thread : list_) { - if (thread != self && thread->GetState() == kRunnable) { - if (!have_complained) { - LOG(WARNING) << "daemon thread not yet suspended: " << *thread; - have_complained = true; + { + MutexLock mu(self, *Locks::thread_list_lock_); + for (const auto& thread : list_) { + if (thread != self && thread->GetState() == kRunnable) { + if (!have_complained) { + LOG(WARNING) << "daemon thread not yet suspended: " << *thread; + have_complained = true; + } + all_suspended = false; } - all_suspended = false; } } if (all_suspended) { @@ -1300,6 +1304,39 @@ void ThreadList::ForEach(void (*callback)(Thread*, void*), void* context) { } } +void ThreadList::VisitRootsForSuspendedThreads(RootVisitor* visitor) { + Thread* const self = Thread::Current(); + std::vector<Thread*> threads_to_visit; + + // Tell threads to suspend and copy them into list. + { + MutexLock mu(self, *Locks::thread_list_lock_); + MutexLock mu2(self, *Locks::thread_suspend_count_lock_); + for (Thread* thread : list_) { + thread->ModifySuspendCount(self, +1, nullptr, false); + if (thread == self || thread->IsSuspended()) { + threads_to_visit.push_back(thread); + } else { + thread->ModifySuspendCount(self, -1, nullptr, false); + } + } + } + + // Visit roots without holding thread_list_lock_ and thread_suspend_count_lock_ to prevent lock + // order violations. + for (Thread* thread : threads_to_visit) { + thread->VisitRoots(visitor); + } + + // Restore suspend counts. + { + MutexLock mu2(self, *Locks::thread_suspend_count_lock_); + for (Thread* thread : threads_to_visit) { + thread->ModifySuspendCount(self, -1, nullptr, false); + } + } +} + void ThreadList::VisitRoots(RootVisitor* visitor) const { MutexLock mu(Thread::Current(), *Locks::thread_list_lock_); for (const auto& thread : list_) { diff --git a/runtime/thread_list.h b/runtime/thread_list.h index df81ad1a7b..49f65e16a7 100644 --- a/runtime/thread_list.h +++ b/runtime/thread_list.h @@ -144,6 +144,10 @@ class ThreadList { void VisitRoots(RootVisitor* visitor) const SHARED_REQUIRES(Locks::mutator_lock_); + void VisitRootsForSuspendedThreads(RootVisitor* visitor) + REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_) + SHARED_REQUIRES(Locks::mutator_lock_); + // Return a copy of the thread list. std::list<Thread*> GetList() REQUIRES(Locks::thread_list_lock_) { return list_; diff --git a/test/004-ThreadStress/check b/test/004-ThreadStress/check index ffbb8cf17e..77e4cdbda0 100755 --- a/test/004-ThreadStress/check +++ b/test/004-ThreadStress/check @@ -14,5 +14,5 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Only compare the last line. -tail -n 1 "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null
\ No newline at end of file +# Do not compare numbers, so replace numbers with 'N'. +sed '-es/[0-9][0-9]*/N/g' "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null
\ No newline at end of file diff --git a/test/004-ThreadStress/expected.txt b/test/004-ThreadStress/expected.txt index a26fb4f191..772faf6e9f 100644 --- a/test/004-ThreadStress/expected.txt +++ b/test/004-ThreadStress/expected.txt @@ -1 +1,11 @@ +JNI_OnLoad called +Starting worker for N +Starting worker for N +Starting worker for N +Starting worker for N +Starting worker for N +Finishing worker +Finishing worker +Finishing worker +Finishing worker Finishing worker diff --git a/test/004-ThreadStress/src/Main.java b/test/004-ThreadStress/src/Main.java index b9a46deba8..5cae3983bf 100644 --- a/test/004-ThreadStress/src/Main.java +++ b/test/004-ThreadStress/src/Main.java @@ -93,9 +93,7 @@ public class Main implements Runnable { killTemp = osClass.getDeclaredMethod("kill", int.class, int.class); } catch (Exception e) { - if (!e.getClass().getName().equals("ErrnoException")) { - e.printStackTrace(System.out); - } + Main.printThrowable(e); } pid = pidTemp; @@ -107,9 +105,10 @@ public class Main implements Runnable { public boolean perform() { try { kill.invoke(null, pid, sigquit); + } catch (OutOfMemoryError e) { } catch (Exception e) { - if (!e.getClass().getName().equals("ErrnoException")) { - e.printStackTrace(System.out); + if (!e.getClass().getName().equals(Main.errnoExceptionName)) { + Main.printThrowable(e); } } return true; @@ -154,7 +153,10 @@ public class Main implements Runnable { private final static class StackTrace extends Operation { @Override public boolean perform() { - Thread.currentThread().getStackTrace(); + try { + Thread.currentThread().getStackTrace(); + } catch (OutOfMemoryError e) { + } return true; } } @@ -264,6 +266,7 @@ public class Main implements Runnable { } public static void main(String[] args) throws Exception { + System.loadLibrary(args[0]); parseAndRun(args); } @@ -395,12 +398,21 @@ public class Main implements Runnable { System.out.println(frequencyMap); } - runTest(numberOfThreads, numberOfDaemons, operationsPerThread, lock, frequencyMap); + try { + runTest(numberOfThreads, numberOfDaemons, operationsPerThread, lock, frequencyMap); + } catch (Throwable t) { + // In this case, the output should not contain all the required + // "Finishing worker" lines. + Main.printThrowable(t); + } } public static void runTest(final int numberOfThreads, final int numberOfDaemons, final int operationsPerThread, final Object lock, Map<Operation, Double> frequencyMap) throws Exception { + final Thread mainThread = Thread.currentThread(); + final Barrier startBarrier = new Barrier(numberOfThreads + numberOfDaemons + 1); + // Each normal thread is going to do operationsPerThread // operations. Each daemon thread will loop over all // the operations and will not stop. @@ -434,8 +446,9 @@ public class Main implements Runnable { } // Randomize the operation order Collections.shuffle(Arrays.asList(operations)); - threadStresses[t] = t < numberOfThreads ? new Main(lock, t, operations) : - new Daemon(lock, t, operations); + threadStresses[t] = (t < numberOfThreads) + ? new Main(lock, t, operations) + : new Daemon(lock, t, operations, mainThread, startBarrier); } // Enable to dump operation counts per thread to make sure its @@ -470,32 +483,41 @@ public class Main implements Runnable { runners[r] = new Thread("Runner thread " + r) { final Main threadStress = ts; public void run() { - int id = threadStress.id; - System.out.println("Starting worker for " + id); - while (threadStress.nextOperation < operationsPerThread) { - try { - Thread thread = new Thread(ts, "Worker thread " + id); - thread.start(); + try { + int id = threadStress.id; + // No memory hungry task are running yet, so println() should succeed. + System.out.println("Starting worker for " + id); + // Wait until all runners and daemons reach the starting point. + startBarrier.await(); + // Run the stress tasks. + while (threadStress.nextOperation < operationsPerThread) { try { + Thread thread = new Thread(ts, "Worker thread " + id); + thread.start(); thread.join(); - } catch (InterruptedException e) { - } - System.out.println("Thread exited for " + id + " with " - + (operationsPerThread - threadStress.nextOperation) - + " operations remaining."); - } catch (OutOfMemoryError e) { - // Ignore OOME since we need to print "Finishing worker" for the test - // to pass. - } - } - // Keep trying to print "Finishing worker" until it succeeds. - while (true) { - try { - System.out.println("Finishing worker"); - break; - } catch (OutOfMemoryError e) { + if (DEBUG) { + System.out.println( + "Thread exited for " + id + " with " + + (operationsPerThread - threadStress.nextOperation) + + " operations remaining."); + } + } catch (OutOfMemoryError e) { + // Ignore OOME since we need to print "Finishing worker" + // for the test to pass. This OOM can come from creating + // the Thread or from the DEBUG output. + // Note that the Thread creation may fail repeatedly, + // preventing the runner from making any progress, + // especially if the number of daemons is too high. + } } + // Print "Finishing worker" through JNI to avoid OOME. + Main.printString(Main.finishingWorkerMessage); + } catch (Throwable t) { + Main.printThrowable(t); + // Interrupt the main thread, so that it can orderly shut down + // instead of waiting indefinitely for some Barrier. + mainThread.interrupt(); } } }; @@ -528,6 +550,9 @@ public class Main implements Runnable { for (int r = 0; r < runners.length; r++) { runners[r].start(); } + // Wait for all threads to reach the starting point. + startBarrier.await(); + // Wait for runners to finish. for (int r = 0; r < runners.length; r++) { runners[r].join(); } @@ -570,8 +595,14 @@ public class Main implements Runnable { } private static class Daemon extends Main { - private Daemon(Object lock, int id, Operation[] operations) { + private Daemon(Object lock, + int id, + Operation[] operations, + Thread mainThread, + Barrier startBarrier) { super(lock, id, operations); + this.mainThread = mainThread; + this.startBarrier = startBarrier; } public void run() { @@ -579,26 +610,74 @@ public class Main implements Runnable { 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); + startBarrier.await(); + try { + 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; } - operation.perform(); - i = (i + 1) % operations.length; + } catch (OutOfMemoryError e) { + // Catch OutOfMemoryErrors since these can cause the test to fail it they print + // the stack trace after "Finishing worker". Note that operations should catch + // their own OOME, this guards only agains OOME in the DEBUG output. } - } catch (OutOfMemoryError e) { - // Catch OutOfMemoryErrors since these can cause the test to fail it they print - // the stack trace after "Finishing worker". - } finally { if (DEBUG) { System.out.println("Finishing ThreadStress Daemon for " + id); } + } catch (Throwable t) { + Main.printThrowable(t); + // Interrupt the main thread, so that it can orderly shut down + // instead of waiting indefinitely for some Barrier. + mainThread.interrupt(); + } + } + + final Thread mainThread; + final Barrier startBarrier; + } + + // Note: java.util.concurrent.CyclicBarrier.await() allocates memory and may throw OOM. + // That is highly undesirable in this test, so we use our own simple barrier class. + // The only memory allocation that can happen here is the lock inflation which uses + // a native allocation. As such, it should succeed even if the Java heap is full. + // If the native allocation surprisingly fails, the program shall abort(). + private static class Barrier { + public Barrier(int initialCount) { + count = initialCount; + } + + public synchronized void await() throws InterruptedException { + --count; + if (count != 0) { + do { + wait(); + } while (count != 0); // Check for spurious wakeup. + } else { + notifyAll(); } } + + private int count; } + // Printing a String/Throwable through JNI requires only native memory and space + // in the local reference table, so it should succeed even if the Java heap is full. + private static native void printString(String s); + private static native void printThrowable(Throwable t); + + static final String finishingWorkerMessage; + static final String errnoExceptionName; + static { + // We pre-allocate the strings in class initializer to avoid const-string + // instructions in code using these strings later as they may throw OOME. + finishingWorkerMessage = "Finishing worker\n"; + errnoExceptionName = "ErrnoException"; + } } diff --git a/test/004-ThreadStress/thread_stress.cc b/test/004-ThreadStress/thread_stress.cc new file mode 100644 index 0000000000..573c352423 --- /dev/null +++ b/test/004-ThreadStress/thread_stress.cc @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016 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. + */ + +#include <iostream> + +#include "jni.h" +#include "mirror/string.h" +#include "mirror/throwable.h" +#include "scoped_thread_state_change.h" + +namespace art { + +extern "C" JNIEXPORT void JNICALL Java_Main_printString(JNIEnv*, jclass, jstring s) { + ScopedObjectAccess soa(Thread::Current()); + std::cout << soa.Decode<mirror::String*>(s)->ToModifiedUtf8(); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_printThrowable(JNIEnv*, jclass, jthrowable t) { + ScopedObjectAccess soa(Thread::Current()); + std::cout << soa.Decode<mirror::Throwable*>(t)->Dump(); +} + +} // namespace art diff --git a/test/004-UnsafeTest/src/Main.java b/test/004-UnsafeTest/src/Main.java index b2f905e0ee..9d4618a07c 100644 --- a/test/004-UnsafeTest/src/Main.java +++ b/test/004-UnsafeTest/src/Main.java @@ -39,16 +39,24 @@ public class Main { } } - private static Unsafe getUnsafe() throws Exception { + private static Unsafe getUnsafe() throws NoSuchFieldException, IllegalAccessException { Class<?> unsafeClass = Unsafe.class; Field f = unsafeClass.getDeclaredField("theUnsafe"); f.setAccessible(true); return (Unsafe) f.get(null); } - public static void main(String[] args) throws Exception { + public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException { System.loadLibrary(args[0]); Unsafe unsafe = getUnsafe(); + + testArrayBaseOffset(unsafe); + testArrayIndexScale(unsafe); + testGetAndPutAndCAS(unsafe); + testGetAndPutVolatile(unsafe); + } + + private static void testArrayBaseOffset(Unsafe unsafe) { check(unsafe.arrayBaseOffset(boolean[].class), vmArrayBaseOffset(boolean[].class), "Unsafe.arrayBaseOffset(boolean[])"); check(unsafe.arrayBaseOffset(byte[].class), vmArrayBaseOffset(byte[].class), @@ -65,7 +73,9 @@ public class Main { "Unsafe.arrayBaseOffset(long[])"); check(unsafe.arrayBaseOffset(Object[].class), vmArrayBaseOffset(Object[].class), "Unsafe.arrayBaseOffset(Object[])"); + } + private static void testArrayIndexScale(Unsafe unsafe) { check(unsafe.arrayIndexScale(boolean[].class), vmArrayIndexScale(boolean[].class), "Unsafe.arrayIndexScale(boolean[])"); check(unsafe.arrayIndexScale(byte[].class), vmArrayIndexScale(byte[].class), @@ -82,7 +92,9 @@ public class Main { "Unsafe.arrayIndexScale(long[])"); check(unsafe.arrayIndexScale(Object[].class), vmArrayIndexScale(Object[].class), "Unsafe.arrayIndexScale(Object[])"); + } + private static void testGetAndPutAndCAS(Unsafe unsafe) throws NoSuchFieldException { TestClass t = new TestClass(); int intValue = 12345678; @@ -185,12 +197,58 @@ public class Main { } } + private static void testGetAndPutVolatile(Unsafe unsafe) throws NoSuchFieldException { + TestVolatileClass tv = new TestVolatileClass(); + + int intValue = 12345678; + Field volatileIntField = TestVolatileClass.class.getDeclaredField("volatileIntVar"); + long volatileIntOffset = unsafe.objectFieldOffset(volatileIntField); + check(unsafe.getIntVolatile(tv, volatileIntOffset), + 0, + "Unsafe.getIntVolatile(Object, long) - initial"); + unsafe.putIntVolatile(tv, volatileIntOffset, intValue); + check(tv.volatileIntVar, intValue, "Unsafe.putIntVolatile(Object, long, int)"); + check(unsafe.getIntVolatile(tv, volatileIntOffset), + intValue, + "Unsafe.getIntVolatile(Object, long)"); + + long longValue = 1234567887654321L; + Field volatileLongField = TestVolatileClass.class.getDeclaredField("volatileLongVar"); + long volatileLongOffset = unsafe.objectFieldOffset(volatileLongField); + check(unsafe.getLongVolatile(tv, volatileLongOffset), + 0, + "Unsafe.getLongVolatile(Object, long) - initial"); + unsafe.putLongVolatile(tv, volatileLongOffset, longValue); + check(tv.volatileLongVar, longValue, "Unsafe.putLongVolatile(Object, long, long)"); + check(unsafe.getLongVolatile(tv, volatileLongOffset), + longValue, + "Unsafe.getLongVolatile(Object, long)"); + + Object objectValue = new Object(); + Field volatileObjectField = TestVolatileClass.class.getDeclaredField("volatileObjectVar"); + long volatileObjectOffset = unsafe.objectFieldOffset(volatileObjectField); + check(unsafe.getObjectVolatile(tv, volatileObjectOffset), + null, + "Unsafe.getObjectVolatile(Object, long) - initial"); + unsafe.putObjectVolatile(tv, volatileObjectOffset, objectValue); + check(tv.volatileObjectVar, objectValue, "Unsafe.putObjectVolatile(Object, long, Object)"); + check(unsafe.getObjectVolatile(tv, volatileObjectOffset), + objectValue, + "Unsafe.getObjectVolatile(Object, long)"); + } + private static class TestClass { public int intVar = 0; public long longVar = 0; public Object objectVar = null; } + private static class TestVolatileClass { + public volatile int volatileIntVar = 0; + public volatile long volatileLongVar = 0; + public volatile Object volatileObjectVar = null; + } + private static native int vmArrayBaseOffset(Class clazz); private static native int vmArrayIndexScale(Class clazz); } diff --git a/test/033-class-init-deadlock/expected.txt b/test/033-class-init-deadlock/expected.txt index 182d0da00d..9e843a06f6 100644 --- a/test/033-class-init-deadlock/expected.txt +++ b/test/033-class-init-deadlock/expected.txt @@ -1,6 +1,4 @@ Deadlock test starting. -A initializing... -B initializing... Deadlock test interrupting threads. Deadlock test main thread bailing. A initialized: false diff --git a/test/033-class-init-deadlock/src/Main.java b/test/033-class-init-deadlock/src/Main.java index 32332307f5..bd4d4ab7b5 100644 --- a/test/033-class-init-deadlock/src/Main.java +++ b/test/033-class-init-deadlock/src/Main.java @@ -14,6 +14,8 @@ * limitations under the License. */ +import java.util.concurrent.CyclicBarrier; + /** * This causes most VMs to lock up. * @@ -23,6 +25,8 @@ public class Main { public static boolean aInitialized = false; public static boolean bInitialized = false; + public static CyclicBarrier barrier = new CyclicBarrier(3); + static public void main(String[] args) { Thread thread1, thread2; @@ -30,10 +34,10 @@ public class Main { thread1 = new Thread() { public void run() { new A(); } }; thread2 = new Thread() { public void run() { new B(); } }; thread1.start(); - // Give thread1 a chance to start before starting thread2. - try { Thread.sleep(1000); } catch (InterruptedException ie) { } thread2.start(); + // Not expecting any exceptions, so print them out if we get them. + try { barrier.await(); } catch (Exception e) { System.out.println(e); } try { Thread.sleep(6000); } catch (InterruptedException ie) { } System.out.println("Deadlock test interrupting threads."); @@ -48,8 +52,8 @@ public class Main { class A { static { - System.out.println("A initializing..."); - try { Thread.sleep(3000); } catch (InterruptedException ie) { } + // Not expecting any exceptions, so print them out if we get them. + try { Main.barrier.await(); } catch (Exception e) { System.out.println(e); } new B(); System.out.println("A initialized"); Main.aInitialized = true; @@ -58,8 +62,8 @@ class A { class B { static { - System.out.println("B initializing..."); - try { Thread.sleep(3000); } catch (InterruptedException ie) { } + // Not expecting any exceptions, so print them out if we get them. + try { Main.barrier.await(); } catch (Exception e) { System.out.println(e); } new A(); System.out.println("B initialized"); Main.bInitialized = true; diff --git a/test/100-reflect2/expected.txt b/test/100-reflect2/expected.txt index d878e69aed..dd89d644a8 100644 --- a/test/100-reflect2/expected.txt +++ b/test/100-reflect2/expected.txt @@ -33,7 +33,7 @@ z (class java.lang.Character) 14 (class java.lang.Short) [java.lang.String(int,int,char[]), public java.lang.String(), public java.lang.String(byte[]), public java.lang.String(byte[],int), public java.lang.String(byte[],int,int), public java.lang.String(byte[],int,int,int), public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],int,int,java.nio.charset.Charset), public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],java.nio.charset.Charset), public java.lang.String(char[]), public java.lang.String(char[],int,int), public java.lang.String(int[],int,int), public java.lang.String(java.lang.String), public java.lang.String(java.lang.StringBuffer), public java.lang.String(java.lang.StringBuilder)] [private final int java.lang.String.count, private int java.lang.String.hash, private static final java.io.ObjectStreamField[] java.lang.String.serialPersistentFields, private static final long java.lang.String.serialVersionUID, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER] -[native void java.lang.String.getCharsNoCheck(int,int,char[],int), native void java.lang.String.setCharAt(int,char), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private native int java.lang.String.fastIndexOf(int,int), private native java.lang.String java.lang.String.fastSubstring(int,int), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public boolean java.lang.String.isEmpty(), public boolean java.lang.String.matches(java.lang.String), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public byte[] java.lang.String.getBytes(), public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public byte[] java.lang.String.getBytes(java.nio.charset.Charset), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public int java.lang.String.compareTo(java.lang.Object), public int java.lang.String.compareToIgnoreCase(java.lang.String), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public int java.lang.String.offsetByCodePoints(int,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), public java.lang.String[] java.lang.String.split(java.lang.String), public java.lang.String[] java.lang.String.split(java.lang.String,int), public native char java.lang.String.charAt(int), public native char[] java.lang.String.toCharArray(), public native int java.lang.String.compareTo(java.lang.String), public native java.lang.String java.lang.String.concat(java.lang.String), public native java.lang.String java.lang.String.intern(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(long), public void java.lang.String.getBytes(int,int,byte[],int), public void java.lang.String.getChars(int,int,char[],int), static int java.lang.String.indexOf(char[],int,int,char[],int,int,int), static int java.lang.String.indexOf(java.lang.String,java.lang.String,int), static int java.lang.String.lastIndexOf(char[],int,int,char[],int,int,int), static int java.lang.String.lastIndexOf(java.lang.String,java.lang.String,int)] +[native void java.lang.String.getCharsNoCheck(int,int,char[],int), native void java.lang.String.setCharAt(int,char), private boolean java.lang.String.nonSyncContentEquals(java.lang.AbstractStringBuilder), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private native int java.lang.String.fastIndexOf(int,int), private native java.lang.String java.lang.String.fastSubstring(int,int), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public boolean java.lang.String.isEmpty(), public boolean java.lang.String.matches(java.lang.String), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public byte[] java.lang.String.getBytes(), public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public byte[] java.lang.String.getBytes(java.nio.charset.Charset), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public int java.lang.String.compareTo(java.lang.Object), public int java.lang.String.compareToIgnoreCase(java.lang.String), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public int java.lang.String.offsetByCodePoints(int,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), public java.lang.String[] java.lang.String.split(java.lang.String), public java.lang.String[] java.lang.String.split(java.lang.String,int), public native char java.lang.String.charAt(int), public native char[] java.lang.String.toCharArray(), public native int java.lang.String.compareTo(java.lang.String), public native java.lang.String java.lang.String.concat(java.lang.String), public native java.lang.String java.lang.String.intern(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.CharSequence[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.Iterable), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(long), public void java.lang.String.getBytes(int,int,byte[],int), public void java.lang.String.getChars(int,int,char[],int), static int java.lang.String.indexOf(char[],int,int,char[],int,int,int), static int java.lang.String.indexOf(java.lang.String,java.lang.String,int), static int java.lang.String.lastIndexOf(char[],int,int,char[],int,int,int), static int java.lang.String.lastIndexOf(java.lang.String,java.lang.String,int), void java.lang.String.getChars(char[],int)] [] [interface java.io.Serializable, interface java.lang.Comparable, interface java.lang.CharSequence] 0 diff --git a/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc b/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc index c9110a905d..b7293015cf 100644 --- a/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc +++ b/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc @@ -27,8 +27,20 @@ namespace art { namespace { static volatile std::atomic<bool> vm_was_shutdown(false); +static const int kThreadCount = 4; + +static std::atomic<int> barrier_count(kThreadCount + 1); + +static void JniThreadBarrierWait() { + barrier_count--; + while (barrier_count.load() != 0) { + usleep(1000); + } +} extern "C" JNIEXPORT void JNICALL Java_Main_waitAndCallIntoJniEnv(JNIEnv* env, jclass) { + // Wait for all threads to enter JNI together. + JniThreadBarrierWait(); // Wait until the runtime is shutdown. while (!vm_was_shutdown.load()) { usleep(1000); @@ -40,6 +52,8 @@ extern "C" JNIEXPORT void JNICALL Java_Main_waitAndCallIntoJniEnv(JNIEnv* env, j // NO_RETURN does not work with extern "C" for target builds. extern "C" JNIEXPORT void JNICALL Java_Main_destroyJavaVMAndExit(JNIEnv* env, jclass) { + // Wait for all threads to enter JNI together. + JniThreadBarrierWait(); // Fake up the managed stack so we can detach. Thread* const self = Thread::Current(); self->SetTopOfStack(nullptr); diff --git a/test/149-suspend-all-stress/check b/test/149-suspend-all-stress/check new file mode 100755 index 0000000000..d30b8888ca --- /dev/null +++ b/test/149-suspend-all-stress/check @@ -0,0 +1,18 @@ +#!/bin/bash +# +# Copyright (C) 2016 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. + +# Only compare the last line. +tail -n 1 "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null
\ No newline at end of file diff --git a/test/149-suspend-all-stress/expected.txt b/test/149-suspend-all-stress/expected.txt index f993efcdad..134d8d0b47 100644 --- a/test/149-suspend-all-stress/expected.txt +++ b/test/149-suspend-all-stress/expected.txt @@ -1,2 +1 @@ -JNI_OnLoad called Finishing diff --git a/test/149-suspend-all-stress/src/Main.java b/test/149-suspend-all-stress/src/Main.java index aa94fc9f1a..6a27c4b12d 100644 --- a/test/149-suspend-all-stress/src/Main.java +++ b/test/149-suspend-all-stress/src/Main.java @@ -17,7 +17,7 @@ import java.util.Map; public class Main implements Runnable { - static final int numberOfThreads = 4; + static final int numberOfThreads = 8; public static void main(String[] args) throws Exception { System.loadLibrary(args[0]); diff --git a/test/149-suspend-all-stress/suspend_all.cc b/test/149-suspend-all-stress/suspend_all.cc index de479a50bb..dfd944a267 100644 --- a/test/149-suspend-all-stress/suspend_all.cc +++ b/test/149-suspend-all-stress/suspend_all.cc @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "base/time_utils.h" #include "jni.h" #include "runtime.h" #include "thread_list.h" @@ -22,7 +23,6 @@ namespace art { extern "C" JNIEXPORT void JNICALL Java_Main_suspendAndResume(JNIEnv*, jclass) { static constexpr size_t kInitialSleepUS = 100 * 1000; // 100ms. - static constexpr size_t kIterations = 500; usleep(kInitialSleepUS); // Leave some time for threads to get in here before we start suspending. enum Operation { kOPSuspendAll, @@ -31,8 +31,11 @@ extern "C" JNIEXPORT void JNICALL Java_Main_suspendAndResume(JNIEnv*, jclass) { // Total number of operations. kOPNumber, }; - for (size_t i = 0; i < kIterations; ++i) { - switch (static_cast<Operation>(i % kOPNumber)) { + const uint64_t start_time = NanoTime(); + size_t iterations = 0; + // Run for a fixed period of 10 seconds. + while (NanoTime() - start_time < MsToNs(10 * 1000)) { + switch (static_cast<Operation>(iterations % kOPNumber)) { case kOPSuspendAll: { ScopedSuspendAll ssa(__FUNCTION__); usleep(500); @@ -52,7 +55,9 @@ extern "C" JNIEXPORT void JNICALL Java_Main_suspendAndResume(JNIEnv*, jclass) { case kOPNumber: break; } + ++iterations; } + LOG(INFO) << "Did " << iterations << " iterations"; } } // namespace art diff --git a/test/150-loadlibrary/expected.txt b/test/150-loadlibrary/expected.txt new file mode 100644 index 0000000000..41feacf298 --- /dev/null +++ b/test/150-loadlibrary/expected.txt @@ -0,0 +1,2 @@ +JNI_OnLoad called +Success. diff --git a/test/150-loadlibrary/info.txt b/test/150-loadlibrary/info.txt new file mode 100644 index 0000000000..089d04414b --- /dev/null +++ b/test/150-loadlibrary/info.txt @@ -0,0 +1 @@ +Check that passing the BootClassLoader to loadLibrary works. diff --git a/test/150-loadlibrary/src/Main.java b/test/150-loadlibrary/src/Main.java new file mode 100644 index 0000000000..908693760f --- /dev/null +++ b/test/150-loadlibrary/src/Main.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016 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.io.File; +import java.lang.reflect.Method; +import java.util.Arrays; + +public class Main { + public static void main(String[] args) throws Exception { + // Check whether we get the BootClassLoader (not null). + ClassLoader bootClassLoader = Object.class.getClassLoader(); + if (bootClassLoader == null) { + throw new IllegalStateException("Expected non-null classloader for Object"); + } + + // Try to load libarttest(d) with the BootClassLoader. First construct the filename. + String libName = System.mapLibraryName(args[0]); + Method libPathsMethod = Runtime.class.getDeclaredMethod("getLibPaths"); + libPathsMethod.setAccessible(true); + String[] libPaths = (String[])libPathsMethod.invoke(Runtime.getRuntime()); + String fileName = null; + for (String p : libPaths) { + String candidate = p + libName; + if (new File(candidate).exists()) { + fileName = candidate; + break; + } + } + if (fileName == null) { + throw new IllegalStateException("Didn't find " + libName + " in " + + Arrays.toString(libPaths)); + } + + // Then call an internal function that accepts the classloader. Do not use load(), as it + // is deprecated and only there for backwards compatibility, and prints a warning to the + // log that we'd have to strip (it contains the pid). + Method m = Runtime.class.getDeclaredMethod("doLoad", String.class, ClassLoader.class); + m.setAccessible(true); + Object result = m.invoke(Runtime.getRuntime(), fileName, bootClassLoader); + if (result != null) { + throw new IllegalStateException(result.toString()); + } + + System.out.println("Success."); + } +} diff --git a/test/201-built-in-exception-detail-messages/src/Main.java b/test/201-built-in-exception-detail-messages/src/Main.java index 52d4259330..f0bb6ddd4f 100644 --- a/test/201-built-in-exception-detail-messages/src/Main.java +++ b/test/201-built-in-exception-detail-messages/src/Main.java @@ -461,7 +461,7 @@ public class Main { "hello there".substring(9,14); fail(); } catch (StringIndexOutOfBoundsException ex) { - assertEquals("length=11; regionStart=9; regionLength=5", ex.getMessage()); + assertEquals("length=11; index=14", ex.getMessage()); } } } diff --git a/test/450-checker-types/src/Main.java b/test/450-checker-types/src/Main.java index 08b6cec35f..36f14d8779 100644 --- a/test/450-checker-types/src/Main.java +++ b/test/450-checker-types/src/Main.java @@ -30,6 +30,11 @@ class Super implements Interface { public void $noinline$f() { throw new RuntimeException(); } + + public int $inline$h(boolean cond) { + Super obj = (cond ? this : null); + return obj.hashCode(); + } } class SubclassA extends Super { @@ -620,6 +625,46 @@ public class Main { o.mainField = 0; } + /// CHECK-START: void Main.testThisArgumentMoreSpecific(boolean) inliner (before) + /// CHECK-DAG: <<Arg:l\d+>> NewInstance + /// CHECK-DAG: InvokeVirtual [<<Arg>>,{{z\d+}}] method_name:Super.$inline$h + + /// CHECK-START: void Main.testThisArgumentMoreSpecific(boolean) inliner (after) + /// CHECK-DAG: <<Arg:l\d+>> NewInstance + /// CHECK-DAG: <<Null:l\d+>> NullConstant + /// CHECK-DAG: <<Phi:l\d+>> Phi [<<Arg>>,<<Null>>] klass:SubclassA + /// CHECK-DAG: <<NCPhi:l\d+>> NullCheck [<<Phi>>] + /// CHECK-DAG: InvokeVirtual [<<NCPhi>>] method_name:Super.hashCode + + public void testThisArgumentMoreSpecific(boolean cond) { + // Inlining method from Super will build it with `this` typed as Super. + // Running RTP will sharpen it to SubclassA. + SubclassA obj = new SubclassA(); + ((Super) obj).$inline$h(cond); + } + + public static int $inline$hashCode(Super obj) { + return obj.hashCode(); + } + + /// CHECK-START: void Main.testExplicitArgumentMoreSpecific(SubclassA) inliner (before) + /// CHECK-DAG: <<Arg:l\d+>> ParameterValue klass:SubclassA + // Note: The ArtMethod* (typed as int or long) is optional after sharpening. + /// CHECK-DAG: InvokeStaticOrDirect [<<Arg>>{{(,[ij]\d+)?}}] method_name:Main.$inline$hashCode + + /// CHECK-START: void Main.testExplicitArgumentMoreSpecific(SubclassA) inliner (after) + /// CHECK-DAG: <<Arg:l\d+>> ParameterValue klass:SubclassA + /// CHECK-DAG: <<NCArg:l\d+>> NullCheck [<<Arg>>] klass:SubclassA + /// CHECK-DAG: InvokeVirtual [<<NCArg>>] method_name:Super.hashCode + + public void testExplicitArgumentMoreSpecific(SubclassA obj) { + // Inlining a method will build it with reference types from its signature, + // here the callee graph is built with Super as the type of its only argument. + // Running RTP after its ParameterValue instructions are replaced with actual + // arguments will type the inner graph more precisely. + $inline$hashCode(obj); + } + /// CHECK-START: void Main.testPhiHasOnlyNullInputs(boolean) inliner (before) /// CHECK: <<Int:i\d+>> IntConstant 0 /// CHECK: <<Phi:l\d+>> Phi klass:Main exact:false diff --git a/test/458-checker-instruction-simplification/src/Main.java b/test/458-checker-instruction-simplification/src/Main.java index 53c2e0b820..ffce49d2e1 100644 --- a/test/458-checker-instruction-simplification/src/Main.java +++ b/test/458-checker-instruction-simplification/src/Main.java @@ -18,6 +18,8 @@ import java.lang.reflect.Method; public class Main { + static boolean doThrow = false; + public static void assertBooleanEquals(boolean expected, boolean result) { if (expected != result) { throw new Error("Expected: " + expected + ", found: " + result); @@ -58,41 +60,43 @@ public class Main { * Tiny programs exercising optimizations of arithmetic identities. */ - /// CHECK-START: long Main.Add0(long) instruction_simplifier (before) + /// CHECK-START: long Main.$noinline$Add0(long) instruction_simplifier (before) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Const0:j\d+>> LongConstant 0 /// CHECK-DAG: <<Add:j\d+>> Add [<<Const0>>,<<Arg>>] /// CHECK-DAG: Return [<<Add>>] - /// CHECK-START: long Main.Add0(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$Add0(long) instruction_simplifier (after) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: Return [<<Arg>>] - /// CHECK-START: long Main.Add0(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$Add0(long) instruction_simplifier (after) /// CHECK-NOT: Add - public static long Add0(long arg) { + public static long $noinline$Add0(long arg) { + if (doThrow) { throw new Error(); } return 0 + arg; } - /// CHECK-START: int Main.AndAllOnes(int) instruction_simplifier (before) + /// CHECK-START: int Main.$noinline$AndAllOnes(int) instruction_simplifier (before) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<ConstF:i\d+>> IntConstant -1 /// CHECK-DAG: <<And:i\d+>> And [<<Arg>>,<<ConstF>>] /// CHECK-DAG: Return [<<And>>] - /// CHECK-START: int Main.AndAllOnes(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$AndAllOnes(int) instruction_simplifier (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: Return [<<Arg>>] - /// CHECK-START: int Main.AndAllOnes(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$AndAllOnes(int) instruction_simplifier (after) /// CHECK-NOT: And - public static int AndAllOnes(int arg) { + public static int $noinline$AndAllOnes(int arg) { + if (doThrow) { throw new Error(); } return arg & -1; } - /// CHECK-START: int Main.UShr28And15(int) instruction_simplifier (before) + /// CHECK-START: int Main.$noinline$UShr28And15(int) instruction_simplifier (before) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Const28:i\d+>> IntConstant 28 /// CHECK-DAG: <<Const15:i\d+>> IntConstant 15 @@ -100,20 +104,21 @@ public class Main { /// CHECK-DAG: <<And:i\d+>> And [<<UShr>>,<<Const15>>] /// CHECK-DAG: Return [<<And>>] - /// CHECK-START: int Main.UShr28And15(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$UShr28And15(int) instruction_simplifier (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Const28:i\d+>> IntConstant 28 /// CHECK-DAG: <<UShr:i\d+>> UShr [<<Arg>>,<<Const28>>] /// CHECK-DAG: Return [<<UShr>>] - /// CHECK-START: int Main.UShr28And15(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$UShr28And15(int) instruction_simplifier (after) /// CHECK-NOT: And - public static int UShr28And15(int arg) { + public static int $noinline$UShr28And15(int arg) { + if (doThrow) { throw new Error(); } return (arg >>> 28) & 15; } - /// CHECK-START: long Main.UShr60And15(long) instruction_simplifier (before) + /// CHECK-START: long Main.$noinline$UShr60And15(long) instruction_simplifier (before) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Const60:i\d+>> IntConstant 60 /// CHECK-DAG: <<Const15:j\d+>> LongConstant 15 @@ -121,20 +126,21 @@ public class Main { /// CHECK-DAG: <<And:j\d+>> And [<<UShr>>,<<Const15>>] /// CHECK-DAG: Return [<<And>>] - /// CHECK-START: long Main.UShr60And15(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$UShr60And15(long) instruction_simplifier (after) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Const60:i\d+>> IntConstant 60 /// CHECK-DAG: <<UShr:j\d+>> UShr [<<Arg>>,<<Const60>>] /// CHECK-DAG: Return [<<UShr>>] - /// CHECK-START: long Main.UShr60And15(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$UShr60And15(long) instruction_simplifier (after) /// CHECK-NOT: And - public static long UShr60And15(long arg) { + public static long $noinline$UShr60And15(long arg) { + if (doThrow) { throw new Error(); } return (arg >>> 60) & 15; } - /// CHECK-START: int Main.UShr28And7(int) instruction_simplifier (before) + /// CHECK-START: int Main.$noinline$UShr28And7(int) instruction_simplifier (before) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Const28:i\d+>> IntConstant 28 /// CHECK-DAG: <<Const7:i\d+>> IntConstant 7 @@ -142,7 +148,7 @@ public class Main { /// CHECK-DAG: <<And:i\d+>> And [<<UShr>>,<<Const7>>] /// CHECK-DAG: Return [<<And>>] - /// CHECK-START: int Main.UShr28And7(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$UShr28And7(int) instruction_simplifier (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Const28:i\d+>> IntConstant 28 /// CHECK-DAG: <<Const7:i\d+>> IntConstant 7 @@ -150,11 +156,12 @@ public class Main { /// CHECK-DAG: <<And:i\d+>> And [<<UShr>>,<<Const7>>] /// CHECK-DAG: Return [<<And>>] - public static int UShr28And7(int arg) { + public static int $noinline$UShr28And7(int arg) { + if (doThrow) { throw new Error(); } return (arg >>> 28) & 7; } - /// CHECK-START: long Main.UShr60And7(long) instruction_simplifier (before) + /// CHECK-START: long Main.$noinline$UShr60And7(long) instruction_simplifier (before) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Const60:i\d+>> IntConstant 60 /// CHECK-DAG: <<Const7:j\d+>> LongConstant 7 @@ -162,7 +169,7 @@ public class Main { /// CHECK-DAG: <<And:j\d+>> And [<<UShr>>,<<Const7>>] /// CHECK-DAG: Return [<<And>>] - /// CHECK-START: long Main.UShr60And7(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$UShr60And7(long) instruction_simplifier (after) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Const60:i\d+>> IntConstant 60 /// CHECK-DAG: <<Const7:j\d+>> LongConstant 7 @@ -170,11 +177,12 @@ public class Main { /// CHECK-DAG: <<And:j\d+>> And [<<UShr>>,<<Const7>>] /// CHECK-DAG: Return [<<And>>] - public static long UShr60And7(long arg) { + public static long $noinline$UShr60And7(long arg) { + if (doThrow) { throw new Error(); } return (arg >>> 60) & 7; } - /// CHECK-START: int Main.Shr24And255(int) instruction_simplifier (before) + /// CHECK-START: int Main.$noinline$Shr24And255(int) instruction_simplifier (before) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Const24:i\d+>> IntConstant 24 /// CHECK-DAG: <<Const255:i\d+>> IntConstant 255 @@ -182,21 +190,22 @@ public class Main { /// CHECK-DAG: <<And:i\d+>> And [<<Shr>>,<<Const255>>] /// CHECK-DAG: Return [<<And>>] - /// CHECK-START: int Main.Shr24And255(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$Shr24And255(int) instruction_simplifier (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Const24:i\d+>> IntConstant 24 /// CHECK-DAG: <<UShr:i\d+>> UShr [<<Arg>>,<<Const24>>] /// CHECK-DAG: Return [<<UShr>>] - /// CHECK-START: int Main.Shr24And255(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$Shr24And255(int) instruction_simplifier (after) /// CHECK-NOT: Shr /// CHECK-NOT: And - public static int Shr24And255(int arg) { + public static int $noinline$Shr24And255(int arg) { + if (doThrow) { throw new Error(); } return (arg >> 24) & 255; } - /// CHECK-START: long Main.Shr56And255(long) instruction_simplifier (before) + /// CHECK-START: long Main.$noinline$Shr56And255(long) instruction_simplifier (before) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Const56:i\d+>> IntConstant 56 /// CHECK-DAG: <<Const255:j\d+>> LongConstant 255 @@ -204,21 +213,22 @@ public class Main { /// CHECK-DAG: <<And:j\d+>> And [<<Shr>>,<<Const255>>] /// CHECK-DAG: Return [<<And>>] - /// CHECK-START: long Main.Shr56And255(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$Shr56And255(long) instruction_simplifier (after) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Const56:i\d+>> IntConstant 56 /// CHECK-DAG: <<UShr:j\d+>> UShr [<<Arg>>,<<Const56>>] /// CHECK-DAG: Return [<<UShr>>] - /// CHECK-START: long Main.Shr56And255(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$Shr56And255(long) instruction_simplifier (after) /// CHECK-NOT: Shr /// CHECK-NOT: And - public static long Shr56And255(long arg) { + public static long $noinline$Shr56And255(long arg) { + if (doThrow) { throw new Error(); } return (arg >> 56) & 255; } - /// CHECK-START: int Main.Shr24And127(int) instruction_simplifier (before) + /// CHECK-START: int Main.$noinline$Shr24And127(int) instruction_simplifier (before) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Const24:i\d+>> IntConstant 24 /// CHECK-DAG: <<Const127:i\d+>> IntConstant 127 @@ -226,7 +236,7 @@ public class Main { /// CHECK-DAG: <<And:i\d+>> And [<<Shr>>,<<Const127>>] /// CHECK-DAG: Return [<<And>>] - /// CHECK-START: int Main.Shr24And127(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$Shr24And127(int) instruction_simplifier (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Const24:i\d+>> IntConstant 24 /// CHECK-DAG: <<Const127:i\d+>> IntConstant 127 @@ -234,11 +244,12 @@ public class Main { /// CHECK-DAG: <<And:i\d+>> And [<<Shr>>,<<Const127>>] /// CHECK-DAG: Return [<<And>>] - public static int Shr24And127(int arg) { + public static int $noinline$Shr24And127(int arg) { + if (doThrow) { throw new Error(); } return (arg >> 24) & 127; } - /// CHECK-START: long Main.Shr56And127(long) instruction_simplifier (before) + /// CHECK-START: long Main.$noinline$Shr56And127(long) instruction_simplifier (before) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Const56:i\d+>> IntConstant 56 /// CHECK-DAG: <<Const127:j\d+>> LongConstant 127 @@ -246,7 +257,7 @@ public class Main { /// CHECK-DAG: <<And:j\d+>> And [<<Shr>>,<<Const127>>] /// CHECK-DAG: Return [<<And>>] - /// CHECK-START: long Main.Shr56And127(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$Shr56And127(long) instruction_simplifier (after) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Const56:i\d+>> IntConstant 56 /// CHECK-DAG: <<Const127:j\d+>> LongConstant 127 @@ -254,267 +265,283 @@ public class Main { /// CHECK-DAG: <<And:j\d+>> And [<<Shr>>,<<Const127>>] /// CHECK-DAG: Return [<<And>>] - public static long Shr56And127(long arg) { + public static long $noinline$Shr56And127(long arg) { + if (doThrow) { throw new Error(); } return (arg >> 56) & 127; } - /// CHECK-START: long Main.Div1(long) instruction_simplifier (before) + /// CHECK-START: long Main.$noinline$Div1(long) instruction_simplifier (before) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Const1:j\d+>> LongConstant 1 /// CHECK-DAG: <<Div:j\d+>> Div [<<Arg>>,<<Const1>>] /// CHECK-DAG: Return [<<Div>>] - /// CHECK-START: long Main.Div1(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$Div1(long) instruction_simplifier (after) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: Return [<<Arg>>] - /// CHECK-START: long Main.Div1(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$Div1(long) instruction_simplifier (after) /// CHECK-NOT: Div - public static long Div1(long arg) { + public static long $noinline$Div1(long arg) { + if (doThrow) { throw new Error(); } return arg / 1; } - /// CHECK-START: int Main.DivN1(int) instruction_simplifier (before) + /// CHECK-START: int Main.$noinline$DivN1(int) instruction_simplifier (before) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<ConstN1:i\d+>> IntConstant -1 /// CHECK-DAG: <<Div:i\d+>> Div [<<Arg>>,<<ConstN1>>] /// CHECK-DAG: Return [<<Div>>] - /// CHECK-START: int Main.DivN1(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$DivN1(int) instruction_simplifier (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Neg:i\d+>> Neg [<<Arg>>] /// CHECK-DAG: Return [<<Neg>>] - /// CHECK-START: int Main.DivN1(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$DivN1(int) instruction_simplifier (after) /// CHECK-NOT: Div - public static int DivN1(int arg) { + public static int $noinline$DivN1(int arg) { + if (doThrow) { throw new Error(); } return arg / -1; } - /// CHECK-START: long Main.Mul1(long) instruction_simplifier (before) + /// CHECK-START: long Main.$noinline$Mul1(long) instruction_simplifier (before) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Const1:j\d+>> LongConstant 1 /// CHECK-DAG: <<Mul:j\d+>> Mul [<<Const1>>,<<Arg>>] /// CHECK-DAG: Return [<<Mul>>] - /// CHECK-START: long Main.Mul1(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$Mul1(long) instruction_simplifier (after) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: Return [<<Arg>>] - /// CHECK-START: long Main.Mul1(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$Mul1(long) instruction_simplifier (after) /// CHECK-NOT: Mul - public static long Mul1(long arg) { + public static long $noinline$Mul1(long arg) { + if (doThrow) { throw new Error(); } return arg * 1; } - /// CHECK-START: int Main.MulN1(int) instruction_simplifier (before) + /// CHECK-START: int Main.$noinline$MulN1(int) instruction_simplifier (before) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<ConstN1:i\d+>> IntConstant -1 /// CHECK-DAG: <<Mul:i\d+>> Mul [<<Arg>>,<<ConstN1>>] /// CHECK-DAG: Return [<<Mul>>] - /// CHECK-START: int Main.MulN1(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$MulN1(int) instruction_simplifier (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Neg:i\d+>> Neg [<<Arg>>] /// CHECK-DAG: Return [<<Neg>>] - /// CHECK-START: int Main.MulN1(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$MulN1(int) instruction_simplifier (after) /// CHECK-NOT: Mul - public static int MulN1(int arg) { + public static int $noinline$MulN1(int arg) { + if (doThrow) { throw new Error(); } return arg * -1; } - /// CHECK-START: long Main.MulPowerOfTwo128(long) instruction_simplifier (before) + /// CHECK-START: long Main.$noinline$MulPowerOfTwo128(long) instruction_simplifier (before) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Const128:j\d+>> LongConstant 128 /// CHECK-DAG: <<Mul:j\d+>> Mul [<<Const128>>,<<Arg>>] /// CHECK-DAG: Return [<<Mul>>] - /// CHECK-START: long Main.MulPowerOfTwo128(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$MulPowerOfTwo128(long) instruction_simplifier (after) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Const7:i\d+>> IntConstant 7 /// CHECK-DAG: <<Shl:j\d+>> Shl [<<Arg>>,<<Const7>>] /// CHECK-DAG: Return [<<Shl>>] - /// CHECK-START: long Main.MulPowerOfTwo128(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$MulPowerOfTwo128(long) instruction_simplifier (after) /// CHECK-NOT: Mul - public static long MulPowerOfTwo128(long arg) { + public static long $noinline$MulPowerOfTwo128(long arg) { + if (doThrow) { throw new Error(); } return arg * 128; } - /// CHECK-START: int Main.Or0(int) instruction_simplifier (before) + /// CHECK-START: int Main.$noinline$Or0(int) instruction_simplifier (before) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 /// CHECK-DAG: <<Or:i\d+>> Or [<<Arg>>,<<Const0>>] /// CHECK-DAG: Return [<<Or>>] - /// CHECK-START: int Main.Or0(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$Or0(int) instruction_simplifier (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: Return [<<Arg>>] - /// CHECK-START: int Main.Or0(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$Or0(int) instruction_simplifier (after) /// CHECK-NOT: Or - public static int Or0(int arg) { + public static int $noinline$Or0(int arg) { + if (doThrow) { throw new Error(); } return arg | 0; } - /// CHECK-START: long Main.OrSame(long) instruction_simplifier (before) + /// CHECK-START: long Main.$noinline$OrSame(long) instruction_simplifier (before) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Or:j\d+>> Or [<<Arg>>,<<Arg>>] /// CHECK-DAG: Return [<<Or>>] - /// CHECK-START: long Main.OrSame(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$OrSame(long) instruction_simplifier (after) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: Return [<<Arg>>] - /// CHECK-START: long Main.OrSame(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$OrSame(long) instruction_simplifier (after) /// CHECK-NOT: Or - public static long OrSame(long arg) { + public static long $noinline$OrSame(long arg) { + if (doThrow) { throw new Error(); } return arg | arg; } - /// CHECK-START: int Main.Shl0(int) instruction_simplifier (before) + /// CHECK-START: int Main.$noinline$Shl0(int) instruction_simplifier (before) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 /// CHECK-DAG: <<Shl:i\d+>> Shl [<<Arg>>,<<Const0>>] /// CHECK-DAG: Return [<<Shl>>] - /// CHECK-START: int Main.Shl0(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$Shl0(int) instruction_simplifier (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: Return [<<Arg>>] - /// CHECK-START: int Main.Shl0(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$Shl0(int) instruction_simplifier (after) /// CHECK-NOT: Shl - public static int Shl0(int arg) { + public static int $noinline$Shl0(int arg) { + if (doThrow) { throw new Error(); } return arg << 0; } - /// CHECK-START: long Main.Shr0(long) instruction_simplifier (before) + /// CHECK-START: long Main.$noinline$Shr0(long) instruction_simplifier (before) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 /// CHECK-DAG: <<Shr:j\d+>> Shr [<<Arg>>,<<Const0>>] /// CHECK-DAG: Return [<<Shr>>] - /// CHECK-START: long Main.Shr0(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$Shr0(long) instruction_simplifier (after) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: Return [<<Arg>>] - /// CHECK-START: long Main.Shr0(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$Shr0(long) instruction_simplifier (after) /// CHECK-NOT: Shr - public static long Shr0(long arg) { + public static long $noinline$Shr0(long arg) { + if (doThrow) { throw new Error(); } return arg >> 0; } - /// CHECK-START: long Main.Shr64(long) instruction_simplifier (before) + /// CHECK-START: long Main.$noinline$Shr64(long) instruction_simplifier (before) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Const64:i\d+>> IntConstant 64 /// CHECK-DAG: <<Shr:j\d+>> Shr [<<Arg>>,<<Const64>>] /// CHECK-DAG: Return [<<Shr>>] - /// CHECK-START: long Main.Shr64(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$Shr64(long) instruction_simplifier (after) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: Return [<<Arg>>] - /// CHECK-START: long Main.Shr64(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$Shr64(long) instruction_simplifier (after) /// CHECK-NOT: Shr - public static long Shr64(long arg) { + public static long $noinline$Shr64(long arg) { + if (doThrow) { throw new Error(); } return arg >> 64; } - /// CHECK-START: long Main.Sub0(long) instruction_simplifier (before) + /// CHECK-START: long Main.$noinline$Sub0(long) instruction_simplifier (before) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Const0:j\d+>> LongConstant 0 /// CHECK-DAG: <<Sub:j\d+>> Sub [<<Arg>>,<<Const0>>] /// CHECK-DAG: Return [<<Sub>>] - /// CHECK-START: long Main.Sub0(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$Sub0(long) instruction_simplifier (after) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: Return [<<Arg>>] - /// CHECK-START: long Main.Sub0(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$Sub0(long) instruction_simplifier (after) /// CHECK-NOT: Sub - public static long Sub0(long arg) { + public static long $noinline$Sub0(long arg) { + if (doThrow) { throw new Error(); } return arg - 0; } - /// CHECK-START: int Main.SubAliasNeg(int) instruction_simplifier (before) + /// CHECK-START: int Main.$noinline$SubAliasNeg(int) instruction_simplifier (before) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 /// CHECK-DAG: <<Sub:i\d+>> Sub [<<Const0>>,<<Arg>>] /// CHECK-DAG: Return [<<Sub>>] - /// CHECK-START: int Main.SubAliasNeg(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$SubAliasNeg(int) instruction_simplifier (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Neg:i\d+>> Neg [<<Arg>>] /// CHECK-DAG: Return [<<Neg>>] - /// CHECK-START: int Main.SubAliasNeg(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$SubAliasNeg(int) instruction_simplifier (after) /// CHECK-NOT: Sub - public static int SubAliasNeg(int arg) { + public static int $noinline$SubAliasNeg(int arg) { + if (doThrow) { throw new Error(); } return 0 - arg; } - /// CHECK-START: long Main.UShr0(long) instruction_simplifier (before) + /// CHECK-START: long Main.$noinline$UShr0(long) instruction_simplifier (before) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 /// CHECK-DAG: <<UShr:j\d+>> UShr [<<Arg>>,<<Const0>>] /// CHECK-DAG: Return [<<UShr>>] - /// CHECK-START: long Main.UShr0(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$UShr0(long) instruction_simplifier (after) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: Return [<<Arg>>] - /// CHECK-START: long Main.UShr0(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$UShr0(long) instruction_simplifier (after) /// CHECK-NOT: UShr - public static long UShr0(long arg) { + public static long $noinline$UShr0(long arg) { + if (doThrow) { throw new Error(); } return arg >>> 0; } - /// CHECK-START: int Main.Xor0(int) instruction_simplifier (before) + /// CHECK-START: int Main.$noinline$Xor0(int) instruction_simplifier (before) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 /// CHECK-DAG: <<Xor:i\d+>> Xor [<<Arg>>,<<Const0>>] /// CHECK-DAG: Return [<<Xor>>] - /// CHECK-START: int Main.Xor0(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$Xor0(int) instruction_simplifier (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: Return [<<Arg>>] - /// CHECK-START: int Main.Xor0(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$Xor0(int) instruction_simplifier (after) /// CHECK-NOT: Xor - public static int Xor0(int arg) { + public static int $noinline$Xor0(int arg) { + if (doThrow) { throw new Error(); } return arg ^ 0; } - /// CHECK-START: int Main.XorAllOnes(int) instruction_simplifier (before) + /// CHECK-START: int Main.$noinline$XorAllOnes(int) instruction_simplifier (before) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<ConstF:i\d+>> IntConstant -1 /// CHECK-DAG: <<Xor:i\d+>> Xor [<<Arg>>,<<ConstF>>] /// CHECK-DAG: Return [<<Xor>>] - /// CHECK-START: int Main.XorAllOnes(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$XorAllOnes(int) instruction_simplifier (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Not:i\d+>> Not [<<Arg>>] /// CHECK-DAG: Return [<<Not>>] - /// CHECK-START: int Main.XorAllOnes(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$XorAllOnes(int) instruction_simplifier (after) /// CHECK-NOT: Xor - public static int XorAllOnes(int arg) { + public static int $noinline$XorAllOnes(int arg) { + if (doThrow) { throw new Error(); } return arg ^ -1; } @@ -525,7 +552,7 @@ public class Main { * `InstructionSimplifierVisitor::TryMoveNegOnInputsAfterBinop`. */ - /// CHECK-START: int Main.AddNegs1(int, int) instruction_simplifier (before) + /// CHECK-START: int Main.$noinline$AddNegs1(int, int) instruction_simplifier (before) /// CHECK-DAG: <<Arg1:i\d+>> ParameterValue /// CHECK-DAG: <<Arg2:i\d+>> ParameterValue /// CHECK-DAG: <<Neg1:i\d+>> Neg [<<Arg1>>] @@ -533,7 +560,7 @@ public class Main { /// CHECK-DAG: <<Add:i\d+>> Add [<<Neg1>>,<<Neg2>>] /// CHECK-DAG: Return [<<Add>>] - /// CHECK-START: int Main.AddNegs1(int, int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$AddNegs1(int, int) instruction_simplifier (after) /// CHECK-DAG: <<Arg1:i\d+>> ParameterValue /// CHECK-DAG: <<Arg2:i\d+>> ParameterValue /// CHECK-NOT: Neg @@ -541,7 +568,8 @@ public class Main { /// CHECK-DAG: <<Neg:i\d+>> Neg [<<Add>>] /// CHECK-DAG: Return [<<Neg>>] - public static int AddNegs1(int arg1, int arg2) { + public static int $noinline$AddNegs1(int arg1, int arg2) { + if (doThrow) { throw new Error(); } return -arg1 + -arg2; } @@ -556,7 +584,7 @@ public class Main { * increasing the register pressure by creating or extending live ranges. */ - /// CHECK-START: int Main.AddNegs2(int, int) instruction_simplifier (before) + /// CHECK-START: int Main.$noinline$AddNegs2(int, int) instruction_simplifier (before) /// CHECK-DAG: <<Arg1:i\d+>> ParameterValue /// CHECK-DAG: <<Arg2:i\d+>> ParameterValue /// CHECK-DAG: <<Neg1:i\d+>> Neg [<<Arg1>>] @@ -566,7 +594,7 @@ public class Main { /// CHECK-DAG: <<Or:i\d+>> Or [<<Add1>>,<<Add2>>] /// CHECK-DAG: Return [<<Or>>] - /// CHECK-START: int Main.AddNegs2(int, int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$AddNegs2(int, int) instruction_simplifier (after) /// CHECK-DAG: <<Arg1:i\d+>> ParameterValue /// CHECK-DAG: <<Arg2:i\d+>> ParameterValue /// CHECK-DAG: <<Neg1:i\d+>> Neg [<<Arg1>>] @@ -577,7 +605,7 @@ public class Main { /// CHECK-DAG: <<Or:i\d+>> Or [<<Add1>>,<<Add2>>] /// CHECK-DAG: Return [<<Or>>] - /// CHECK-START: int Main.AddNegs2(int, int) GVN (after) + /// CHECK-START: int Main.$noinline$AddNegs2(int, int) GVN (after) /// CHECK-DAG: <<Arg1:i\d+>> ParameterValue /// CHECK-DAG: <<Arg2:i\d+>> ParameterValue /// CHECK-DAG: <<Neg1:i\d+>> Neg [<<Arg1>>] @@ -586,7 +614,8 @@ public class Main { /// CHECK-DAG: <<Or:i\d+>> Or [<<Add>>,<<Add>>] /// CHECK-DAG: Return [<<Or>>] - public static int AddNegs2(int arg1, int arg2) { + public static int $noinline$AddNegs2(int arg1, int arg2) { + if (doThrow) { throw new Error(); } int temp1 = -arg1; int temp2 = -arg2; return (temp1 + temp2) | (temp1 + temp2); @@ -600,7 +629,7 @@ public class Main { * the loop. */ - /// CHECK-START: long Main.AddNegs3(long, long) instruction_simplifier (before) + /// CHECK-START: long Main.$noinline$AddNegs3(long, long) instruction_simplifier (before) // -------------- Arguments and initial negation operations. /// CHECK-DAG: <<Arg1:j\d+>> ParameterValue /// CHECK-DAG: <<Arg2:j\d+>> ParameterValue @@ -612,7 +641,7 @@ public class Main { /// CHECK: <<Add:j\d+>> Add [<<Neg1>>,<<Neg2>>] /// CHECK: Goto - /// CHECK-START: long Main.AddNegs3(long, long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$AddNegs3(long, long) instruction_simplifier (after) // -------------- Arguments and initial negation operations. /// CHECK-DAG: <<Arg1:j\d+>> ParameterValue /// CHECK-DAG: <<Arg2:j\d+>> ParameterValue @@ -625,7 +654,8 @@ public class Main { /// CHECK-NOT: Neg /// CHECK: Goto - public static long AddNegs3(long arg1, long arg2) { + public static long $noinline$AddNegs3(long arg1, long arg2) { + if (doThrow) { throw new Error(); } long res = 0; long n_arg1 = -arg1; long n_arg2 = -arg2; @@ -641,24 +671,25 @@ public class Main { * The transformation tested is implemented in `InstructionSimplifierVisitor::VisitAdd`. */ - /// CHECK-START: long Main.AddNeg1(long, long) instruction_simplifier (before) + /// CHECK-START: long Main.$noinline$AddNeg1(long, long) instruction_simplifier (before) /// CHECK-DAG: <<Arg1:j\d+>> ParameterValue /// CHECK-DAG: <<Arg2:j\d+>> ParameterValue /// CHECK-DAG: <<Neg:j\d+>> Neg [<<Arg1>>] /// CHECK-DAG: <<Add:j\d+>> Add [<<Neg>>,<<Arg2>>] /// CHECK-DAG: Return [<<Add>>] - /// CHECK-START: long Main.AddNeg1(long, long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$AddNeg1(long, long) instruction_simplifier (after) /// CHECK-DAG: <<Arg1:j\d+>> ParameterValue /// CHECK-DAG: <<Arg2:j\d+>> ParameterValue /// CHECK-DAG: <<Sub:j\d+>> Sub [<<Arg2>>,<<Arg1>>] /// CHECK-DAG: Return [<<Sub>>] - /// CHECK-START: long Main.AddNeg1(long, long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$AddNeg1(long, long) instruction_simplifier (after) /// CHECK-NOT: Neg /// CHECK-NOT: Add - public static long AddNeg1(long arg1, long arg2) { + public static long $noinline$AddNeg1(long arg1, long arg2) { + if (doThrow) { throw new Error(); } return -arg1 + arg2; } @@ -671,7 +702,7 @@ public class Main { * increasing the register pressure by creating or extending live ranges. */ - /// CHECK-START: long Main.AddNeg2(long, long) instruction_simplifier (before) + /// CHECK-START: long Main.$noinline$AddNeg2(long, long) instruction_simplifier (before) /// CHECK-DAG: <<Arg1:j\d+>> ParameterValue /// CHECK-DAG: <<Arg2:j\d+>> ParameterValue /// CHECK-DAG: <<Neg:j\d+>> Neg [<<Arg2>>] @@ -680,7 +711,7 @@ public class Main { /// CHECK-DAG: <<Res:j\d+>> Or [<<Add1>>,<<Add2>>] /// CHECK-DAG: Return [<<Res>>] - /// CHECK-START: long Main.AddNeg2(long, long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$AddNeg2(long, long) instruction_simplifier (after) /// CHECK-DAG: <<Arg1:j\d+>> ParameterValue /// CHECK-DAG: <<Arg2:j\d+>> ParameterValue /// CHECK-DAG: <<Neg:j\d+>> Neg [<<Arg2>>] @@ -689,10 +720,11 @@ public class Main { /// CHECK-DAG: <<Res:j\d+>> Or [<<Add1>>,<<Add2>>] /// CHECK-DAG: Return [<<Res>>] - /// CHECK-START: long Main.AddNeg2(long, long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$AddNeg2(long, long) instruction_simplifier (after) /// CHECK-NOT: Sub - public static long AddNeg2(long arg1, long arg2) { + public static long $noinline$AddNeg2(long arg1, long arg2) { + if (doThrow) { throw new Error(); } long temp = -arg2; return (arg1 + temp) | (arg1 + temp); } @@ -702,20 +734,21 @@ public class Main { * The transformation tested is implemented in `InstructionSimplifierVisitor::VisitNeg`. */ - /// CHECK-START: long Main.NegNeg1(long) instruction_simplifier (before) + /// CHECK-START: long Main.$noinline$NegNeg1(long) instruction_simplifier (before) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Neg1:j\d+>> Neg [<<Arg>>] /// CHECK-DAG: <<Neg2:j\d+>> Neg [<<Neg1>>] /// CHECK-DAG: Return [<<Neg2>>] - /// CHECK-START: long Main.NegNeg1(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$NegNeg1(long) instruction_simplifier (after) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: Return [<<Arg>>] - /// CHECK-START: long Main.NegNeg1(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$NegNeg1(long) instruction_simplifier (after) /// CHECK-NOT: Neg - public static long NegNeg1(long arg) { + public static long $noinline$NegNeg1(long arg) { + if (doThrow) { throw new Error(); } return -(-arg); } @@ -726,29 +759,30 @@ public class Main { * and in `InstructionSimplifierVisitor::VisitAdd`. */ - /// CHECK-START: int Main.NegNeg2(int) instruction_simplifier (before) + /// CHECK-START: int Main.$noinline$NegNeg2(int) instruction_simplifier (before) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Neg1:i\d+>> Neg [<<Arg>>] /// CHECK-DAG: <<Neg2:i\d+>> Neg [<<Neg1>>] /// CHECK-DAG: <<Add:i\d+>> Add [<<Neg2>>,<<Neg1>>] /// CHECK-DAG: Return [<<Add>>] - /// CHECK-START: int Main.NegNeg2(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$NegNeg2(int) instruction_simplifier (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Sub:i\d+>> Sub [<<Arg>>,<<Arg>>] /// CHECK-DAG: Return [<<Sub>>] - /// CHECK-START: int Main.NegNeg2(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$NegNeg2(int) instruction_simplifier (after) /// CHECK-NOT: Neg /// CHECK-NOT: Add - /// CHECK-START: int Main.NegNeg2(int) constant_folding_after_inlining (after) + /// CHECK-START: int Main.$noinline$NegNeg2(int) constant_folding_after_inlining (after) /// CHECK: <<Const0:i\d+>> IntConstant 0 /// CHECK-NOT: Neg /// CHECK-NOT: Add /// CHECK: Return [<<Const0>>] - public static int NegNeg2(int arg) { + public static int $noinline$NegNeg2(int arg) { + if (doThrow) { throw new Error(); } int temp = -arg; return temp + -temp; } @@ -760,22 +794,23 @@ public class Main { * and in `InstructionSimplifierVisitor::VisitSub`. */ - /// CHECK-START: long Main.NegNeg3(long) instruction_simplifier (before) + /// CHECK-START: long Main.$noinline$NegNeg3(long) instruction_simplifier (before) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Const0:j\d+>> LongConstant 0 /// CHECK-DAG: <<Neg:j\d+>> Neg [<<Arg>>] /// CHECK-DAG: <<Sub:j\d+>> Sub [<<Const0>>,<<Neg>>] /// CHECK-DAG: Return [<<Sub>>] - /// CHECK-START: long Main.NegNeg3(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$NegNeg3(long) instruction_simplifier (after) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: Return [<<Arg>>] - /// CHECK-START: long Main.NegNeg3(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$NegNeg3(long) instruction_simplifier (after) /// CHECK-NOT: Neg /// CHECK-NOT: Sub - public static long NegNeg3(long arg) { + public static long $noinline$NegNeg3(long arg) { + if (doThrow) { throw new Error(); } return 0 - -arg; } @@ -785,23 +820,24 @@ public class Main { * The transformation tested is implemented in `InstructionSimplifierVisitor::VisitNeg`. */ - /// CHECK-START: int Main.NegSub1(int, int) instruction_simplifier (before) + /// CHECK-START: int Main.$noinline$NegSub1(int, int) instruction_simplifier (before) /// CHECK-DAG: <<Arg1:i\d+>> ParameterValue /// CHECK-DAG: <<Arg2:i\d+>> ParameterValue /// CHECK-DAG: <<Sub:i\d+>> Sub [<<Arg1>>,<<Arg2>>] /// CHECK-DAG: <<Neg:i\d+>> Neg [<<Sub>>] /// CHECK-DAG: Return [<<Neg>>] - /// CHECK-START: int Main.NegSub1(int, int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$NegSub1(int, int) instruction_simplifier (after) /// CHECK-DAG: <<Arg1:i\d+>> ParameterValue /// CHECK-DAG: <<Arg2:i\d+>> ParameterValue /// CHECK-DAG: <<Sub:i\d+>> Sub [<<Arg2>>,<<Arg1>>] /// CHECK-DAG: Return [<<Sub>>] - /// CHECK-START: int Main.NegSub1(int, int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$NegSub1(int, int) instruction_simplifier (after) /// CHECK-NOT: Neg - public static int NegSub1(int arg1, int arg2) { + public static int $noinline$NegSub1(int arg1, int arg2) { + if (doThrow) { throw new Error(); } return -(arg1 - arg2); } @@ -815,7 +851,7 @@ public class Main { * increasing the register pressure by creating or extending live ranges. */ - /// CHECK-START: int Main.NegSub2(int, int) instruction_simplifier (before) + /// CHECK-START: int Main.$noinline$NegSub2(int, int) instruction_simplifier (before) /// CHECK-DAG: <<Arg1:i\d+>> ParameterValue /// CHECK-DAG: <<Arg2:i\d+>> ParameterValue /// CHECK-DAG: <<Sub:i\d+>> Sub [<<Arg1>>,<<Arg2>>] @@ -824,7 +860,7 @@ public class Main { /// CHECK-DAG: <<Or:i\d+>> Or [<<Neg1>>,<<Neg2>>] /// CHECK-DAG: Return [<<Or>>] - /// CHECK-START: int Main.NegSub2(int, int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$NegSub2(int, int) instruction_simplifier (after) /// CHECK-DAG: <<Arg1:i\d+>> ParameterValue /// CHECK-DAG: <<Arg2:i\d+>> ParameterValue /// CHECK-DAG: <<Sub:i\d+>> Sub [<<Arg1>>,<<Arg2>>] @@ -833,7 +869,8 @@ public class Main { /// CHECK-DAG: <<Or:i\d+>> Or [<<Neg1>>,<<Neg2>>] /// CHECK-DAG: Return [<<Or>>] - public static int NegSub2(int arg1, int arg2) { + public static int $noinline$NegSub2(int arg1, int arg2) { + if (doThrow) { throw new Error(); } int temp = arg1 - arg2; return -temp | -temp; } @@ -843,41 +880,43 @@ public class Main { * The transformation tested is implemented in `InstructionSimplifierVisitor::VisitNot`. */ - /// CHECK-START: long Main.NotNot1(long) instruction_simplifier (before) + /// CHECK-START: long Main.$noinline$NotNot1(long) instruction_simplifier (before) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Not1:j\d+>> Not [<<Arg>>] /// CHECK-DAG: <<Not2:j\d+>> Not [<<Not1>>] /// CHECK-DAG: Return [<<Not2>>] - /// CHECK-START: long Main.NotNot1(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$NotNot1(long) instruction_simplifier (after) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: Return [<<Arg>>] - /// CHECK-START: long Main.NotNot1(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$NotNot1(long) instruction_simplifier (after) /// CHECK-NOT: Not - public static long NotNot1(long arg) { + public static long $noinline$NotNot1(long arg) { + if (doThrow) { throw new Error(); } return ~~arg; } - /// CHECK-START: int Main.NotNot2(int) instruction_simplifier (before) + /// CHECK-START: int Main.$noinline$NotNot2(int) instruction_simplifier (before) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Not1:i\d+>> Not [<<Arg>>] /// CHECK-DAG: <<Not2:i\d+>> Not [<<Not1>>] /// CHECK-DAG: <<Add:i\d+>> Add [<<Not2>>,<<Not1>>] /// CHECK-DAG: Return [<<Add>>] - /// CHECK-START: int Main.NotNot2(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$NotNot2(int) instruction_simplifier (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Not:i\d+>> Not [<<Arg>>] /// CHECK-DAG: <<Add:i\d+>> Add [<<Arg>>,<<Not>>] /// CHECK-DAG: Return [<<Add>>] - /// CHECK-START: int Main.NotNot2(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$NotNot2(int) instruction_simplifier (after) /// CHECK: Not /// CHECK-NOT: Not - public static int NotNot2(int arg) { + public static int $noinline$NotNot2(int arg) { + if (doThrow) { throw new Error(); } int temp = ~arg; return temp + ~temp; } @@ -887,24 +926,25 @@ public class Main { * The transformation tested is implemented in `InstructionSimplifierVisitor::VisitSub`. */ - /// CHECK-START: int Main.SubNeg1(int, int) instruction_simplifier (before) + /// CHECK-START: int Main.$noinline$SubNeg1(int, int) instruction_simplifier (before) /// CHECK-DAG: <<Arg1:i\d+>> ParameterValue /// CHECK-DAG: <<Arg2:i\d+>> ParameterValue /// CHECK-DAG: <<Neg:i\d+>> Neg [<<Arg1>>] /// CHECK-DAG: <<Sub:i\d+>> Sub [<<Neg>>,<<Arg2>>] /// CHECK-DAG: Return [<<Sub>>] - /// CHECK-START: int Main.SubNeg1(int, int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$SubNeg1(int, int) instruction_simplifier (after) /// CHECK-DAG: <<Arg1:i\d+>> ParameterValue /// CHECK-DAG: <<Arg2:i\d+>> ParameterValue /// CHECK-DAG: <<Add:i\d+>> Add [<<Arg1>>,<<Arg2>>] /// CHECK-DAG: <<Neg:i\d+>> Neg [<<Add>>] /// CHECK-DAG: Return [<<Neg>>] - /// CHECK-START: int Main.SubNeg1(int, int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$SubNeg1(int, int) instruction_simplifier (after) /// CHECK-NOT: Sub - public static int SubNeg1(int arg1, int arg2) { + public static int $noinline$SubNeg1(int arg1, int arg2) { + if (doThrow) { throw new Error(); } return -arg1 - arg2; } @@ -918,7 +958,7 @@ public class Main { * increasing the register pressure by creating or extending live ranges. */ - /// CHECK-START: int Main.SubNeg2(int, int) instruction_simplifier (before) + /// CHECK-START: int Main.$noinline$SubNeg2(int, int) instruction_simplifier (before) /// CHECK-DAG: <<Arg1:i\d+>> ParameterValue /// CHECK-DAG: <<Arg2:i\d+>> ParameterValue /// CHECK-DAG: <<Neg:i\d+>> Neg [<<Arg1>>] @@ -927,7 +967,7 @@ public class Main { /// CHECK-DAG: <<Or:i\d+>> Or [<<Sub1>>,<<Sub2>>] /// CHECK-DAG: Return [<<Or>>] - /// CHECK-START: int Main.SubNeg2(int, int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$SubNeg2(int, int) instruction_simplifier (after) /// CHECK-DAG: <<Arg1:i\d+>> ParameterValue /// CHECK-DAG: <<Arg2:i\d+>> ParameterValue /// CHECK-DAG: <<Neg:i\d+>> Neg [<<Arg1>>] @@ -936,10 +976,11 @@ public class Main { /// CHECK-DAG: <<Or:i\d+>> Or [<<Sub1>>,<<Sub2>>] /// CHECK-DAG: Return [<<Or>>] - /// CHECK-START: int Main.SubNeg2(int, int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$SubNeg2(int, int) instruction_simplifier (after) /// CHECK-NOT: Add - public static int SubNeg2(int arg1, int arg2) { + public static int $noinline$SubNeg2(int arg1, int arg2) { + if (doThrow) { throw new Error(); } int temp = -arg1; return (temp - arg2) | (temp - arg2); } @@ -951,7 +992,7 @@ public class Main { * the loop. */ - /// CHECK-START: long Main.SubNeg3(long, long) instruction_simplifier (before) + /// CHECK-START: long Main.$noinline$SubNeg3(long, long) instruction_simplifier (before) // -------------- Arguments and initial negation operation. /// CHECK-DAG: <<Arg1:j\d+>> ParameterValue /// CHECK-DAG: <<Arg2:j\d+>> ParameterValue @@ -962,7 +1003,7 @@ public class Main { /// CHECK: <<Sub:j\d+>> Sub [<<Neg>>,<<Arg2>>] /// CHECK: Goto - /// CHECK-START: long Main.SubNeg3(long, long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$SubNeg3(long, long) instruction_simplifier (after) // -------------- Arguments and initial negation operation. /// CHECK-DAG: <<Arg1:j\d+>> ParameterValue /// CHECK-DAG: <<Arg2:j\d+>> ParameterValue @@ -974,7 +1015,8 @@ public class Main { /// CHECK-NOT: Neg /// CHECK: Goto - public static long SubNeg3(long arg1, long arg2) { + public static long $noinline$SubNeg3(long arg1, long arg2) { + if (doThrow) { throw new Error(); } long res = 0; long temp = -arg1; for (long i = 0; i < 1; i++) { @@ -983,7 +1025,7 @@ public class Main { return res; } - /// CHECK-START: boolean Main.EqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (before) + /// CHECK-START: boolean Main.$noinline$EqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (before) /// CHECK-DAG: <<Arg:z\d+>> ParameterValue /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 @@ -993,15 +1035,16 @@ public class Main { /// CHECK-DAG: <<NotCond:i\d+>> Select [<<Const1>>,<<Const0>>,<<Cond>>] /// CHECK-DAG: Return [<<NotCond>>] - /// CHECK-START: boolean Main.EqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (after) + /// CHECK-START: boolean Main.$noinline$EqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (after) /// CHECK-DAG: <<True:i\d+>> IntConstant 1 /// CHECK-DAG: Return [<<True>>] - public static boolean EqualBoolVsIntConst(boolean arg) { + public static boolean $noinline$EqualBoolVsIntConst(boolean arg) { + if (doThrow) { throw new Error(); } return (arg ? 0 : 1) != 2; } - /// CHECK-START: boolean Main.NotEqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (before) + /// CHECK-START: boolean Main.$noinline$NotEqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (before) /// CHECK-DAG: <<Arg:z\d+>> ParameterValue /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 @@ -1011,11 +1054,12 @@ public class Main { /// CHECK-DAG: <<NotCond:i\d+>> Select [<<Const1>>,<<Const0>>,<<Cond>>] /// CHECK-DAG: Return [<<NotCond>>] - /// CHECK-START: boolean Main.NotEqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (after) + /// CHECK-START: boolean Main.$noinline$NotEqualBoolVsIntConst(boolean) instruction_simplifier_after_bce (after) /// CHECK-DAG: <<False:i\d+>> IntConstant 0 /// CHECK-DAG: Return [<<False>>] - public static boolean NotEqualBoolVsIntConst(boolean arg) { + public static boolean $noinline$NotEqualBoolVsIntConst(boolean arg) { + if (doThrow) { throw new Error(); } return (arg ? 0 : 1) == 2; } @@ -1025,7 +1069,7 @@ public class Main { * remove the second. */ - /// CHECK-START: boolean Main.NotNotBool(boolean) instruction_simplifier_after_bce (before) + /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier_after_bce (before) /// CHECK-DAG: <<Arg:z\d+>> ParameterValue /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 @@ -1033,7 +1077,7 @@ public class Main { /// CHECK-DAG: <<NotNotArg:i\d+>> Select [<<Const1>>,<<Const0>>,<<NotArg>>] /// CHECK-DAG: Return [<<NotNotArg>>] - /// CHECK-START: boolean Main.NotNotBool(boolean) instruction_simplifier_after_bce (after) + /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier_after_bce (after) /// CHECK-DAG: <<Arg:z\d+>> ParameterValue /// CHECK-DAG: Return [<<Arg>>] @@ -1041,81 +1085,86 @@ public class Main { return !arg; } - public static boolean NotNotBool(boolean arg) { + public static boolean $noinline$NotNotBool(boolean arg) { + if (doThrow) { throw new Error(); } return !(NegateValue(arg)); } - /// CHECK-START: float Main.Div2(float) instruction_simplifier (before) + /// CHECK-START: float Main.$noinline$Div2(float) instruction_simplifier (before) /// CHECK-DAG: <<Arg:f\d+>> ParameterValue /// CHECK-DAG: <<Const2:f\d+>> FloatConstant 2 /// CHECK-DAG: <<Div:f\d+>> Div [<<Arg>>,<<Const2>>] /// CHECK-DAG: Return [<<Div>>] - /// CHECK-START: float Main.Div2(float) instruction_simplifier (after) + /// CHECK-START: float Main.$noinline$Div2(float) instruction_simplifier (after) /// CHECK-DAG: <<Arg:f\d+>> ParameterValue /// CHECK-DAG: <<ConstP5:f\d+>> FloatConstant 0.5 /// CHECK-DAG: <<Mul:f\d+>> Mul [<<Arg>>,<<ConstP5>>] /// CHECK-DAG: Return [<<Mul>>] - /// CHECK-START: float Main.Div2(float) instruction_simplifier (after) + /// CHECK-START: float Main.$noinline$Div2(float) instruction_simplifier (after) /// CHECK-NOT: Div - public static float Div2(float arg) { + public static float $noinline$Div2(float arg) { + if (doThrow) { throw new Error(); } return arg / 2.0f; } - /// CHECK-START: double Main.Div2(double) instruction_simplifier (before) + /// CHECK-START: double Main.$noinline$Div2(double) instruction_simplifier (before) /// CHECK-DAG: <<Arg:d\d+>> ParameterValue /// CHECK-DAG: <<Const2:d\d+>> DoubleConstant 2 /// CHECK-DAG: <<Div:d\d+>> Div [<<Arg>>,<<Const2>>] /// CHECK-DAG: Return [<<Div>>] - /// CHECK-START: double Main.Div2(double) instruction_simplifier (after) + /// CHECK-START: double Main.$noinline$Div2(double) instruction_simplifier (after) /// CHECK-DAG: <<Arg:d\d+>> ParameterValue /// CHECK-DAG: <<ConstP5:d\d+>> DoubleConstant 0.5 /// CHECK-DAG: <<Mul:d\d+>> Mul [<<Arg>>,<<ConstP5>>] /// CHECK-DAG: Return [<<Mul>>] - /// CHECK-START: double Main.Div2(double) instruction_simplifier (after) + /// CHECK-START: double Main.$noinline$Div2(double) instruction_simplifier (after) /// CHECK-NOT: Div - public static double Div2(double arg) { + public static double $noinline$Div2(double arg) { + if (doThrow) { throw new Error(); } return arg / 2.0; } - /// CHECK-START: float Main.DivMP25(float) instruction_simplifier (before) + /// CHECK-START: float Main.$noinline$DivMP25(float) instruction_simplifier (before) /// CHECK-DAG: <<Arg:f\d+>> ParameterValue /// CHECK-DAG: <<ConstMP25:f\d+>> FloatConstant -0.25 /// CHECK-DAG: <<Div:f\d+>> Div [<<Arg>>,<<ConstMP25>>] /// CHECK-DAG: Return [<<Div>>] - /// CHECK-START: float Main.DivMP25(float) instruction_simplifier (after) + /// CHECK-START: float Main.$noinline$DivMP25(float) instruction_simplifier (after) /// CHECK-DAG: <<Arg:f\d+>> ParameterValue /// CHECK-DAG: <<ConstM4:f\d+>> FloatConstant -4 /// CHECK-DAG: <<Mul:f\d+>> Mul [<<Arg>>,<<ConstM4>>] /// CHECK-DAG: Return [<<Mul>>] - /// CHECK-START: float Main.DivMP25(float) instruction_simplifier (after) + /// CHECK-START: float Main.$noinline$DivMP25(float) instruction_simplifier (after) /// CHECK-NOT: Div - public static float DivMP25(float arg) { + public static float $noinline$DivMP25(float arg) { + if (doThrow) { throw new Error(); } return arg / -0.25f; } - /// CHECK-START: double Main.DivMP25(double) instruction_simplifier (before) + /// CHECK-START: double Main.$noinline$DivMP25(double) instruction_simplifier (before) /// CHECK-DAG: <<Arg:d\d+>> ParameterValue /// CHECK-DAG: <<ConstMP25:d\d+>> DoubleConstant -0.25 /// CHECK-DAG: <<Div:d\d+>> Div [<<Arg>>,<<ConstMP25>>] /// CHECK-DAG: Return [<<Div>>] - /// CHECK-START: double Main.DivMP25(double) instruction_simplifier (after) + /// CHECK-START: double Main.$noinline$DivMP25(double) instruction_simplifier (after) /// CHECK-DAG: <<Arg:d\d+>> ParameterValue /// CHECK-DAG: <<ConstM4:d\d+>> DoubleConstant -4 /// CHECK-DAG: <<Mul:d\d+>> Mul [<<Arg>>,<<ConstM4>>] /// CHECK-DAG: Return [<<Mul>>] - /// CHECK-START: double Main.DivMP25(double) instruction_simplifier (after) + /// CHECK-START: double Main.$noinline$DivMP25(double) instruction_simplifier (after) /// CHECK-NOT: Div - public static double DivMP25(double arg) { + public static double $noinline$DivMP25(double arg) { + if (doThrow) { throw new Error(); } return arg / -0.25f; } @@ -1123,18 +1172,19 @@ public class Main { * Test strength reduction of factors of the form (2^n + 1). */ - /// CHECK-START: int Main.mulPow2Plus1(int) instruction_simplifier (before) + /// CHECK-START: int Main.$noinline$mulPow2Plus1(int) instruction_simplifier (before) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Const9:i\d+>> IntConstant 9 /// CHECK: Mul [<<Arg>>,<<Const9>>] - /// CHECK-START: int Main.mulPow2Plus1(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$mulPow2Plus1(int) instruction_simplifier (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3 /// CHECK: <<Shift:i\d+>> Shl [<<Arg>>,<<Const3>>] /// CHECK-NEXT: Add [<<Arg>>,<<Shift>>] - public static int mulPow2Plus1(int arg) { + public static int $noinline$mulPow2Plus1(int arg) { + if (doThrow) { throw new Error(); } return arg * 9; } @@ -1142,62 +1192,69 @@ public class Main { * Test strength reduction of factors of the form (2^n - 1). */ - /// CHECK-START: long Main.mulPow2Minus1(long) instruction_simplifier (before) + /// CHECK-START: long Main.$noinline$mulPow2Minus1(long) instruction_simplifier (before) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Const31:j\d+>> LongConstant 31 /// CHECK: Mul [<<Const31>>,<<Arg>>] - /// CHECK-START: long Main.mulPow2Minus1(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$mulPow2Minus1(long) instruction_simplifier (after) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Const5:i\d+>> IntConstant 5 /// CHECK: <<Shift:j\d+>> Shl [<<Arg>>,<<Const5>>] /// CHECK-NEXT: Sub [<<Shift>>,<<Arg>>] - public static long mulPow2Minus1(long arg) { + public static long $noinline$mulPow2Minus1(long arg) { + if (doThrow) { throw new Error(); } return arg * 31; } - /// CHECK-START: int Main.booleanFieldNotEqualOne() instruction_simplifier_after_bce (before) + /// CHECK-START: int Main.$noinline$booleanFieldNotEqualOne() instruction_simplifier_after_bce (before) /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13 /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54 + /// CHECK-DAG: <<doThrow:z\d+>> StaticFieldGet /// CHECK-DAG: <<Field:z\d+>> StaticFieldGet /// CHECK-DAG: <<NE:z\d+>> NotEqual [<<Field>>,<<Const1>>] /// CHECK-DAG: <<Select:i\d+>> Select [<<Const13>>,<<Const54>>,<<NE>>] /// CHECK-DAG: Return [<<Select>>] - /// CHECK-START: int Main.booleanFieldNotEqualOne() instruction_simplifier_after_bce (after) + /// CHECK-START: int Main.$noinline$booleanFieldNotEqualOne() instruction_simplifier_after_bce (after) + /// CHECK-DAG: <<doThrow:z\d+>> StaticFieldGet /// CHECK-DAG: <<Field:z\d+>> StaticFieldGet /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13 /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54 /// CHECK-DAG: <<Select:i\d+>> Select [<<Const54>>,<<Const13>>,<<Field>>] /// CHECK-DAG: Return [<<Select>>] - public static int booleanFieldNotEqualOne() { + public static int $noinline$booleanFieldNotEqualOne() { + if (doThrow) { throw new Error(); } return (booleanField == $inline$true()) ? 13 : 54; } - /// CHECK-START: int Main.booleanFieldEqualZero() instruction_simplifier_after_bce (before) + /// CHECK-START: int Main.$noinline$booleanFieldEqualZero() instruction_simplifier_after_bce (before) /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13 /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54 + /// CHECK-DAG: <<doThrow:z\d+>> StaticFieldGet /// CHECK-DAG: <<Field:z\d+>> StaticFieldGet /// CHECK-DAG: <<NE:z\d+>> Equal [<<Field>>,<<Const0>>] /// CHECK-DAG: <<Select:i\d+>> Select [<<Const13>>,<<Const54>>,<<NE>>] /// CHECK-DAG: Return [<<Select>>] - /// CHECK-START: int Main.booleanFieldEqualZero() instruction_simplifier_after_bce (after) + /// CHECK-START: int Main.$noinline$booleanFieldEqualZero() instruction_simplifier_after_bce (after) + /// CHECK-DAG: <<doThrow:z\d+>> StaticFieldGet /// CHECK-DAG: <<Field:z\d+>> StaticFieldGet /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13 /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54 /// CHECK-DAG: <<Select:i\d+>> Select [<<Const54>>,<<Const13>>,<<Field>>] /// CHECK-DAG: Return [<<Select>>] - public static int booleanFieldEqualZero() { + public static int $noinline$booleanFieldEqualZero() { + if (doThrow) { throw new Error(); } return (booleanField != $inline$false()) ? 13 : 54; } - /// CHECK-START: int Main.intConditionNotEqualOne(int) instruction_simplifier_after_bce (before) + /// CHECK-START: int Main.$noinline$intConditionNotEqualOne(int) instruction_simplifier_after_bce (before) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 @@ -1210,7 +1267,7 @@ public class Main { /// CHECK-DAG: <<Result:i\d+>> Select [<<Const13>>,<<Const54>>,<<NE>>] /// CHECK-DAG: Return [<<Result>>] - /// CHECK-START: int Main.intConditionNotEqualOne(int) instruction_simplifier_after_bce (after) + /// CHECK-START: int Main.$noinline$intConditionNotEqualOne(int) instruction_simplifier_after_bce (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13 /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42 @@ -1221,11 +1278,12 @@ public class Main { // Note that we match `LE` from Select because there are two identical // LessThanOrEqual instructions. - public static int intConditionNotEqualOne(int i) { + public static int $noinline$intConditionNotEqualOne(int i) { + if (doThrow) { throw new Error(); } return ((i > 42) == $inline$true()) ? 13 : 54; } - /// CHECK-START: int Main.intConditionEqualZero(int) instruction_simplifier_after_bce (before) + /// CHECK-START: int Main.$noinline$intConditionEqualZero(int) instruction_simplifier_after_bce (before) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 @@ -1238,7 +1296,7 @@ public class Main { /// CHECK-DAG: <<Result:i\d+>> Select [<<Const13>>,<<Const54>>,<<NE>>] /// CHECK-DAG: Return [<<Result>>] - /// CHECK-START: int Main.intConditionEqualZero(int) instruction_simplifier_after_bce (after) + /// CHECK-START: int Main.$noinline$intConditionEqualZero(int) instruction_simplifier_after_bce (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13 /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42 @@ -1249,16 +1307,17 @@ public class Main { // Note that we match `LE` from Select because there are two identical // LessThanOrEqual instructions. - public static int intConditionEqualZero(int i) { + public static int $noinline$intConditionEqualZero(int i) { + if (doThrow) { throw new Error(); } return ((i > 42) != $inline$false()) ? 13 : 54; } // Test that conditions on float/double are not flipped. - /// CHECK-START: int Main.floatConditionNotEqualOne(float) builder (after) + /// CHECK-START: int Main.$noinline$floatConditionNotEqualOne(float) builder (after) /// CHECK: LessThanOrEqual - /// CHECK-START: int Main.floatConditionNotEqualOne(float) instruction_simplifier_before_codegen (after) + /// CHECK-START: int Main.$noinline$floatConditionNotEqualOne(float) instruction_simplifier_before_codegen (after) /// CHECK-DAG: <<Arg:f\d+>> ParameterValue /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13 /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54 @@ -1267,14 +1326,15 @@ public class Main { /// CHECK-DAG: <<Select:i\d+>> Select [<<Const13>>,<<Const54>>,<<LE>>] /// CHECK-DAG: Return [<<Select>>] - public static int floatConditionNotEqualOne(float f) { + public static int $noinline$floatConditionNotEqualOne(float f) { + if (doThrow) { throw new Error(); } return ((f > 42.0f) == true) ? 13 : 54; } - /// CHECK-START: int Main.doubleConditionEqualZero(double) builder (after) + /// CHECK-START: int Main.$noinline$doubleConditionEqualZero(double) builder (after) /// CHECK: LessThanOrEqual - /// CHECK-START: int Main.doubleConditionEqualZero(double) instruction_simplifier_before_codegen (after) + /// CHECK-START: int Main.$noinline$doubleConditionEqualZero(double) instruction_simplifier_before_codegen (after) /// CHECK-DAG: <<Arg:d\d+>> ParameterValue /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13 /// CHECK-DAG: <<Const54:i\d+>> IntConstant 54 @@ -1283,42 +1343,45 @@ public class Main { /// CHECK-DAG: <<Select:i\d+>> Select [<<Const13>>,<<Const54>>,<<LE>>] /// CHECK-DAG: Return [<<Select>>] - public static int doubleConditionEqualZero(double d) { + public static int $noinline$doubleConditionEqualZero(double d) { + if (doThrow) { throw new Error(); } return ((d > 42.0) != false) ? 13 : 54; } - /// CHECK-START: int Main.intToDoubleToInt(int) instruction_simplifier (before) + /// CHECK-START: int Main.$noinline$intToDoubleToInt(int) instruction_simplifier (before) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>] /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Double>>] /// CHECK-DAG: Return [<<Int>>] - /// CHECK-START: int Main.intToDoubleToInt(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$intToDoubleToInt(int) instruction_simplifier (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: Return [<<Arg>>] - /// CHECK-START: int Main.intToDoubleToInt(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$intToDoubleToInt(int) instruction_simplifier (after) /// CHECK-NOT: TypeConversion - public static int intToDoubleToInt(int value) { + public static int $noinline$intToDoubleToInt(int value) { + if (doThrow) { throw new Error(); } // Lossless conversion followed by a conversion back. return (int) (double) value; } - /// CHECK-START: java.lang.String Main.intToDoubleToIntPrint(int) instruction_simplifier (before) + /// CHECK-START: java.lang.String Main.$noinline$intToDoubleToIntPrint(int) instruction_simplifier (before) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>] /// CHECK-DAG: {{i\d+}} TypeConversion [<<Double>>] - /// CHECK-START: java.lang.String Main.intToDoubleToIntPrint(int) instruction_simplifier (after) + /// CHECK-START: java.lang.String Main.$noinline$intToDoubleToIntPrint(int) instruction_simplifier (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: {{d\d+}} TypeConversion [<<Arg>>] - /// CHECK-START: java.lang.String Main.intToDoubleToIntPrint(int) instruction_simplifier (after) + /// CHECK-START: java.lang.String Main.$noinline$intToDoubleToIntPrint(int) instruction_simplifier (after) /// CHECK-DAG: TypeConversion /// CHECK-NOT: TypeConversion - public static String intToDoubleToIntPrint(int value) { + public static String $noinline$intToDoubleToIntPrint(int value) { + if (doThrow) { throw new Error(); } // Lossless conversion followed by a conversion back // with another use of the intermediate result. double d = (double) value; @@ -1326,55 +1389,58 @@ public class Main { return "d=" + d + ", i=" + i; } - /// CHECK-START: int Main.byteToDoubleToInt(byte) instruction_simplifier (before) + /// CHECK-START: int Main.$noinline$byteToDoubleToInt(byte) instruction_simplifier (before) /// CHECK-DAG: <<Arg:b\d+>> ParameterValue /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>] /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Double>>] /// CHECK-DAG: Return [<<Int>>] - /// CHECK-START: int Main.byteToDoubleToInt(byte) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$byteToDoubleToInt(byte) instruction_simplifier (after) /// CHECK-DAG: <<Arg:b\d+>> ParameterValue /// CHECK-DAG: Return [<<Arg>>] - /// CHECK-START: int Main.byteToDoubleToInt(byte) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$byteToDoubleToInt(byte) instruction_simplifier (after) /// CHECK-NOT: TypeConversion - public static int byteToDoubleToInt(byte value) { + public static int $noinline$byteToDoubleToInt(byte value) { + if (doThrow) { throw new Error(); } // Lossless conversion followed by another conversion, use implicit conversion. return (int) (double) value; } - /// CHECK-START: int Main.floatToDoubleToInt(float) instruction_simplifier (before) + /// CHECK-START: int Main.$noinline$floatToDoubleToInt(float) instruction_simplifier (before) /// CHECK-DAG: <<Arg:f\d+>> ParameterValue /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>] /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Double>>] /// CHECK-DAG: Return [<<Int>>] - /// CHECK-START: int Main.floatToDoubleToInt(float) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$floatToDoubleToInt(float) instruction_simplifier (after) /// CHECK-DAG: <<Arg:f\d+>> ParameterValue /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>] /// CHECK-DAG: Return [<<Int>>] - /// CHECK-START: int Main.floatToDoubleToInt(float) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$floatToDoubleToInt(float) instruction_simplifier (after) /// CHECK-DAG: TypeConversion /// CHECK-NOT: TypeConversion - public static int floatToDoubleToInt(float value) { + public static int $noinline$floatToDoubleToInt(float value) { + if (doThrow) { throw new Error(); } // Lossless conversion followed by another conversion. return (int) (double) value; } - /// CHECK-START: java.lang.String Main.floatToDoubleToIntPrint(float) instruction_simplifier (before) + /// CHECK-START: java.lang.String Main.$noinline$floatToDoubleToIntPrint(float) instruction_simplifier (before) /// CHECK-DAG: <<Arg:f\d+>> ParameterValue /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>] /// CHECK-DAG: {{i\d+}} TypeConversion [<<Double>>] - /// CHECK-START: java.lang.String Main.floatToDoubleToIntPrint(float) instruction_simplifier (after) + /// CHECK-START: java.lang.String Main.$noinline$floatToDoubleToIntPrint(float) instruction_simplifier (after) /// CHECK-DAG: <<Arg:f\d+>> ParameterValue /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>] /// CHECK-DAG: {{i\d+}} TypeConversion [<<Double>>] - public static String floatToDoubleToIntPrint(float value) { + public static String $noinline$floatToDoubleToIntPrint(float value) { + if (doThrow) { throw new Error(); } // Lossless conversion followed by another conversion with // an extra use of the intermediate result. double d = (double) value; @@ -1382,176 +1448,186 @@ public class Main { return "d=" + d + ", i=" + i; } - /// CHECK-START: short Main.byteToDoubleToShort(byte) instruction_simplifier (before) + /// CHECK-START: short Main.$noinline$byteToDoubleToShort(byte) instruction_simplifier (before) /// CHECK-DAG: <<Arg:b\d+>> ParameterValue /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>] /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Double>>] /// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Int>>] /// CHECK-DAG: Return [<<Short>>] - /// CHECK-START: short Main.byteToDoubleToShort(byte) instruction_simplifier (after) + /// CHECK-START: short Main.$noinline$byteToDoubleToShort(byte) instruction_simplifier (after) /// CHECK-DAG: <<Arg:b\d+>> ParameterValue /// CHECK-DAG: Return [<<Arg>>] - /// CHECK-START: short Main.byteToDoubleToShort(byte) instruction_simplifier (after) + /// CHECK-START: short Main.$noinline$byteToDoubleToShort(byte) instruction_simplifier (after) /// CHECK-NOT: TypeConversion - public static short byteToDoubleToShort(byte value) { + public static short $noinline$byteToDoubleToShort(byte value) { + if (doThrow) { throw new Error(); } // Originally, this is byte->double->int->short. The first conversion is lossless, // so we merge this with the second one to byte->int which we omit as it's an implicit // conversion. Then we eliminate the resulting byte->short as an implicit conversion. return (short) (double) value; } - /// CHECK-START: short Main.charToDoubleToShort(char) instruction_simplifier (before) + /// CHECK-START: short Main.$noinline$charToDoubleToShort(char) instruction_simplifier (before) /// CHECK-DAG: <<Arg:c\d+>> ParameterValue /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>] /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Double>>] /// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Int>>] /// CHECK-DAG: Return [<<Short>>] - /// CHECK-START: short Main.charToDoubleToShort(char) instruction_simplifier (after) + /// CHECK-START: short Main.$noinline$charToDoubleToShort(char) instruction_simplifier (after) /// CHECK-DAG: <<Arg:c\d+>> ParameterValue /// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Arg>>] /// CHECK-DAG: Return [<<Short>>] - /// CHECK-START: short Main.charToDoubleToShort(char) instruction_simplifier (after) + /// CHECK-START: short Main.$noinline$charToDoubleToShort(char) instruction_simplifier (after) /// CHECK-DAG: TypeConversion /// CHECK-NOT: TypeConversion - public static short charToDoubleToShort(char value) { + public static short $noinline$charToDoubleToShort(char value) { + if (doThrow) { throw new Error(); } // Originally, this is char->double->int->short. The first conversion is lossless, // so we merge this with the second one to char->int which we omit as it's an implicit // conversion. Then we are left with the resulting char->short conversion. return (short) (double) value; } - /// CHECK-START: short Main.floatToIntToShort(float) instruction_simplifier (before) + /// CHECK-START: short Main.$noinline$floatToIntToShort(float) instruction_simplifier (before) /// CHECK-DAG: <<Arg:f\d+>> ParameterValue /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>] /// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Int>>] /// CHECK-DAG: Return [<<Short>>] - /// CHECK-START: short Main.floatToIntToShort(float) instruction_simplifier (after) + /// CHECK-START: short Main.$noinline$floatToIntToShort(float) instruction_simplifier (after) /// CHECK-DAG: <<Arg:f\d+>> ParameterValue /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>] /// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Int>>] /// CHECK-DAG: Return [<<Short>>] - public static short floatToIntToShort(float value) { + public static short $noinline$floatToIntToShort(float value) { + if (doThrow) { throw new Error(); } // Lossy FP to integral conversion followed by another conversion: no simplification. return (short) value; } - /// CHECK-START: int Main.intToFloatToInt(int) instruction_simplifier (before) + /// CHECK-START: int Main.$noinline$intToFloatToInt(int) instruction_simplifier (before) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Float:f\d+>> TypeConversion [<<Arg>>] /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Float>>] /// CHECK-DAG: Return [<<Int>>] - /// CHECK-START: int Main.intToFloatToInt(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$intToFloatToInt(int) instruction_simplifier (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Float:f\d+>> TypeConversion [<<Arg>>] /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Float>>] /// CHECK-DAG: Return [<<Int>>] - public static int intToFloatToInt(int value) { + public static int $noinline$intToFloatToInt(int value) { + if (doThrow) { throw new Error(); } // Lossy integral to FP conversion followed another conversion: no simplification. return (int) (float) value; } - /// CHECK-START: double Main.longToIntToDouble(long) instruction_simplifier (before) + /// CHECK-START: double Main.$noinline$longToIntToDouble(long) instruction_simplifier (before) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>] /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Int>>] /// CHECK-DAG: Return [<<Double>>] - /// CHECK-START: double Main.longToIntToDouble(long) instruction_simplifier (after) + /// CHECK-START: double Main.$noinline$longToIntToDouble(long) instruction_simplifier (after) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>] /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Int>>] /// CHECK-DAG: Return [<<Double>>] - public static double longToIntToDouble(long value) { + public static double $noinline$longToIntToDouble(long value) { + if (doThrow) { throw new Error(); } // Lossy long-to-int conversion followed an integral to FP conversion: no simplification. return (double) (int) value; } - /// CHECK-START: long Main.longToIntToLong(long) instruction_simplifier (before) + /// CHECK-START: long Main.$noinline$longToIntToLong(long) instruction_simplifier (before) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>] /// CHECK-DAG: <<Long:j\d+>> TypeConversion [<<Int>>] /// CHECK-DAG: Return [<<Long>>] - /// CHECK-START: long Main.longToIntToLong(long) instruction_simplifier (after) + /// CHECK-START: long Main.$noinline$longToIntToLong(long) instruction_simplifier (after) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>] /// CHECK-DAG: <<Long:j\d+>> TypeConversion [<<Int>>] /// CHECK-DAG: Return [<<Long>>] - public static long longToIntToLong(long value) { + public static long $noinline$longToIntToLong(long value) { + if (doThrow) { throw new Error(); } // Lossy long-to-int conversion followed an int-to-long conversion: no simplification. return (long) (int) value; } - /// CHECK-START: short Main.shortToCharToShort(short) instruction_simplifier (before) + /// CHECK-START: short Main.$noinline$shortToCharToShort(short) instruction_simplifier (before) /// CHECK-DAG: <<Arg:s\d+>> ParameterValue /// CHECK-DAG: <<Char:c\d+>> TypeConversion [<<Arg>>] /// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Char>>] /// CHECK-DAG: Return [<<Short>>] - /// CHECK-START: short Main.shortToCharToShort(short) instruction_simplifier (after) + /// CHECK-START: short Main.$noinline$shortToCharToShort(short) instruction_simplifier (after) /// CHECK-DAG: <<Arg:s\d+>> ParameterValue /// CHECK-DAG: Return [<<Arg>>] - public static short shortToCharToShort(short value) { + public static short $noinline$shortToCharToShort(short value) { + if (doThrow) { throw new Error(); } // Integral conversion followed by non-widening integral conversion to original type. return (short) (char) value; } - /// CHECK-START: int Main.shortToLongToInt(short) instruction_simplifier (before) + /// CHECK-START: int Main.$noinline$shortToLongToInt(short) instruction_simplifier (before) /// CHECK-DAG: <<Arg:s\d+>> ParameterValue /// CHECK-DAG: <<Long:j\d+>> TypeConversion [<<Arg>>] /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Long>>] /// CHECK-DAG: Return [<<Int>>] - /// CHECK-START: int Main.shortToLongToInt(short) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$shortToLongToInt(short) instruction_simplifier (after) /// CHECK-DAG: <<Arg:s\d+>> ParameterValue /// CHECK-DAG: Return [<<Arg>>] - public static int shortToLongToInt(short value) { + public static int $noinline$shortToLongToInt(short value) { + if (doThrow) { throw new Error(); } // Integral conversion followed by non-widening integral conversion, use implicit conversion. return (int) (long) value; } - /// CHECK-START: byte Main.shortToCharToByte(short) instruction_simplifier (before) + /// CHECK-START: byte Main.$noinline$shortToCharToByte(short) instruction_simplifier (before) /// CHECK-DAG: <<Arg:s\d+>> ParameterValue /// CHECK-DAG: <<Char:c\d+>> TypeConversion [<<Arg>>] /// CHECK-DAG: <<Byte:b\d+>> TypeConversion [<<Char>>] /// CHECK-DAG: Return [<<Byte>>] - /// CHECK-START: byte Main.shortToCharToByte(short) instruction_simplifier (after) + /// CHECK-START: byte Main.$noinline$shortToCharToByte(short) instruction_simplifier (after) /// CHECK-DAG: <<Arg:s\d+>> ParameterValue /// CHECK-DAG: <<Byte:b\d+>> TypeConversion [<<Arg>>] /// CHECK-DAG: Return [<<Byte>>] - public static byte shortToCharToByte(short value) { + public static byte $noinline$shortToCharToByte(short value) { + if (doThrow) { throw new Error(); } // Integral conversion followed by non-widening integral conversion losing bits // from the original type. Simplify to use only one conversion. return (byte) (char) value; } - /// CHECK-START: java.lang.String Main.shortToCharToBytePrint(short) instruction_simplifier (before) + /// CHECK-START: java.lang.String Main.$noinline$shortToCharToBytePrint(short) instruction_simplifier (before) /// CHECK-DAG: <<Arg:s\d+>> ParameterValue /// CHECK-DAG: <<Char:c\d+>> TypeConversion [<<Arg>>] /// CHECK-DAG: {{b\d+}} TypeConversion [<<Char>>] - /// CHECK-START: java.lang.String Main.shortToCharToBytePrint(short) instruction_simplifier (after) + /// CHECK-START: java.lang.String Main.$noinline$shortToCharToBytePrint(short) instruction_simplifier (after) /// CHECK-DAG: <<Arg:s\d+>> ParameterValue /// CHECK-DAG: <<Char:c\d+>> TypeConversion [<<Arg>>] /// CHECK-DAG: {{b\d+}} TypeConversion [<<Char>>] - public static String shortToCharToBytePrint(short value) { + public static String $noinline$shortToCharToBytePrint(short value) { + if (doThrow) { throw new Error(); } // Integral conversion followed by non-widening integral conversion losing bits // from the original type with an extra use of the intermediate result. char c = (char) value; @@ -1559,7 +1635,7 @@ public class Main { return "c=" + ((int) c) + ", b=" + ((int) b); // implicit conversions. } - /// CHECK-START: byte Main.longAnd0xffToByte(long) instruction_simplifier (before) + /// CHECK-START: byte Main.$noinline$longAnd0xffToByte(long) instruction_simplifier (before) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Mask:j\d+>> LongConstant 255 /// CHECK-DAG: <<And:j\d+>> And [<<Mask>>,<<Arg>>] @@ -1567,58 +1643,61 @@ public class Main { /// CHECK-DAG: <<Byte:b\d+>> TypeConversion [<<Int>>] /// CHECK-DAG: Return [<<Byte>>] - /// CHECK-START: byte Main.longAnd0xffToByte(long) instruction_simplifier (after) + /// CHECK-START: byte Main.$noinline$longAnd0xffToByte(long) instruction_simplifier (after) /// CHECK-DAG: <<Arg:j\d+>> ParameterValue /// CHECK-DAG: <<Byte:b\d+>> TypeConversion [<<Arg>>] /// CHECK-DAG: Return [<<Byte>>] - /// CHECK-START: byte Main.longAnd0xffToByte(long) instruction_simplifier (after) + /// CHECK-START: byte Main.$noinline$longAnd0xffToByte(long) instruction_simplifier (after) /// CHECK-NOT: And - public static byte longAnd0xffToByte(long value) { + public static byte $noinline$longAnd0xffToByte(long value) { + if (doThrow) { throw new Error(); } return (byte) (value & 0xff); } - /// CHECK-START: char Main.intAnd0x1ffffToChar(int) instruction_simplifier (before) + /// CHECK-START: char Main.$noinline$intAnd0x1ffffToChar(int) instruction_simplifier (before) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Mask:i\d+>> IntConstant 131071 /// CHECK-DAG: <<And:i\d+>> And [<<Mask>>,<<Arg>>] /// CHECK-DAG: <<Char:c\d+>> TypeConversion [<<And>>] /// CHECK-DAG: Return [<<Char>>] - /// CHECK-START: char Main.intAnd0x1ffffToChar(int) instruction_simplifier (after) + /// CHECK-START: char Main.$noinline$intAnd0x1ffffToChar(int) instruction_simplifier (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Char:c\d+>> TypeConversion [<<Arg>>] /// CHECK-DAG: Return [<<Char>>] - /// CHECK-START: char Main.intAnd0x1ffffToChar(int) instruction_simplifier (after) + /// CHECK-START: char Main.$noinline$intAnd0x1ffffToChar(int) instruction_simplifier (after) /// CHECK-NOT: And - public static char intAnd0x1ffffToChar(int value) { + public static char $noinline$intAnd0x1ffffToChar(int value) { + if (doThrow) { throw new Error(); } // Keeping all significant bits and one more. return (char) (value & 0x1ffff); } - /// CHECK-START: short Main.intAnd0x17fffToShort(int) instruction_simplifier (before) + /// CHECK-START: short Main.$noinline$intAnd0x17fffToShort(int) instruction_simplifier (before) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Mask:i\d+>> IntConstant 98303 /// CHECK-DAG: <<And:i\d+>> And [<<Mask>>,<<Arg>>] /// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<And>>] /// CHECK-DAG: Return [<<Short>>] - /// CHECK-START: short Main.intAnd0x17fffToShort(int) instruction_simplifier (after) + /// CHECK-START: short Main.$noinline$intAnd0x17fffToShort(int) instruction_simplifier (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Mask:i\d+>> IntConstant 98303 /// CHECK-DAG: <<And:i\d+>> And [<<Mask>>,<<Arg>>] /// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<And>>] /// CHECK-DAG: Return [<<Short>>] - public static short intAnd0x17fffToShort(int value) { + public static short $noinline$intAnd0x17fffToShort(int value) { + if (doThrow) { throw new Error(); } // No simplification: clearing a significant bit. return (short) (value & 0x17fff); } - /// CHECK-START: double Main.shortAnd0xffffToShortToDouble(short) instruction_simplifier (before) + /// CHECK-START: double Main.$noinline$shortAnd0xffffToShortToDouble(short) instruction_simplifier (before) /// CHECK-DAG: <<Arg:s\d+>> ParameterValue /// CHECK-DAG: <<Mask:i\d+>> IntConstant 65535 /// CHECK-DAG: <<And:i\d+>> And [<<Mask>>,<<Arg>>] @@ -1626,45 +1705,49 @@ public class Main { /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Same>>] /// CHECK-DAG: Return [<<Double>>] - /// CHECK-START: double Main.shortAnd0xffffToShortToDouble(short) instruction_simplifier (after) + /// CHECK-START: double Main.$noinline$shortAnd0xffffToShortToDouble(short) instruction_simplifier (after) /// CHECK-DAG: <<Arg:s\d+>> ParameterValue /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>] /// CHECK-DAG: Return [<<Double>>] - public static double shortAnd0xffffToShortToDouble(short value) { + public static double $noinline$shortAnd0xffffToShortToDouble(short value) { + if (doThrow) { throw new Error(); } short same = (short) (value & 0xffff); return (double) same; } - /// CHECK-START: int Main.intReverseCondition(int) instruction_simplifier (before) + /// CHECK-START: int Main.$noinline$intReverseCondition(int) instruction_simplifier (before) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42 /// CHECK-DAG: <<LE:z\d+>> LessThanOrEqual [<<Const42>>,<<Arg>>] - /// CHECK-START: int Main.intReverseCondition(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$intReverseCondition(int) instruction_simplifier (after) /// CHECK-DAG: <<Arg:i\d+>> ParameterValue /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42 /// CHECK-DAG: <<GE:z\d+>> GreaterThanOrEqual [<<Arg>>,<<Const42>>] - public static int intReverseCondition(int i) { + public static int $noinline$intReverseCondition(int i) { + if (doThrow) { throw new Error(); } return (42 > i) ? 13 : 54; } - /// CHECK-START: int Main.intReverseConditionNaN(int) instruction_simplifier (before) + /// CHECK-START: int Main.$noinline$intReverseConditionNaN(int) instruction_simplifier (before) /// CHECK-DAG: <<Const42:d\d+>> DoubleConstant 42 /// CHECK-DAG: <<Result:d\d+>> InvokeStaticOrDirect /// CHECK-DAG: <<CMP:i\d+>> Compare [<<Const42>>,<<Result>>] - /// CHECK-START: int Main.intReverseConditionNaN(int) instruction_simplifier (after) + /// CHECK-START: int Main.$noinline$intReverseConditionNaN(int) instruction_simplifier (after) /// CHECK-DAG: <<Const42:d\d+>> DoubleConstant 42 /// CHECK-DAG: <<Result:d\d+>> InvokeStaticOrDirect /// CHECK-DAG: <<EQ:z\d+>> Equal [<<Result>>,<<Const42>>] - public static int intReverseConditionNaN(int i) { + public static int $noinline$intReverseConditionNaN(int i) { + if (doThrow) { throw new Error(); } return (42 != Math.sqrt(i)) ? 13 : 54; } - public static int runSmaliTest(String name, boolean input) { + public static int $noinline$runSmaliTest(String name, boolean input) { + if (doThrow) { throw new Error(); } try { Class<?> c = Class.forName("SmaliTests"); Method m = c.getMethod(name, new Class[] { boolean.class }); @@ -1674,155 +1757,270 @@ public class Main { } } + /// CHECK-START: int Main.$noinline$intUnnecessaryShiftMasking(int, int) instruction_simplifier (before) + /// CHECK: <<Value:i\d+>> ParameterValue + /// CHECK: <<Shift:i\d+>> ParameterValue + /// CHECK-DAG: <<Const31:i\d+>> IntConstant 31 + /// CHECK-DAG: <<And:i\d+>> And [<<Shift>>,<<Const31>>] + /// CHECK-DAG: <<Shl:i\d+>> Shl [<<Value>>,<<And>>] + /// CHECK-DAG: Return [<<Shl>>] + + /// CHECK-START: int Main.$noinline$intUnnecessaryShiftMasking(int, int) instruction_simplifier (after) + /// CHECK: <<Value:i\d+>> ParameterValue + /// CHECK: <<Shift:i\d+>> ParameterValue + /// CHECK-DAG: <<Shl:i\d+>> Shl [<<Value>>,<<Shift>>] + /// CHECK-DAG: Return [<<Shl>>] + + public static int $noinline$intUnnecessaryShiftMasking(int value, int shift) { + if (doThrow) { throw new Error(); } + return value << (shift & 31); + } + + /// CHECK-START: long Main.$noinline$longUnnecessaryShiftMasking(long, int) instruction_simplifier (before) + /// CHECK: <<Value:j\d+>> ParameterValue + /// CHECK: <<Shift:i\d+>> ParameterValue + /// CHECK-DAG: <<Const63:i\d+>> IntConstant 63 + /// CHECK-DAG: <<And:i\d+>> And [<<Shift>>,<<Const63>>] + /// CHECK-DAG: <<Shr:j\d+>> Shr [<<Value>>,<<And>>] + /// CHECK-DAG: Return [<<Shr>>] + + /// CHECK-START: long Main.$noinline$longUnnecessaryShiftMasking(long, int) instruction_simplifier (after) + /// CHECK: <<Value:j\d+>> ParameterValue + /// CHECK: <<Shift:i\d+>> ParameterValue + /// CHECK-DAG: <<Shr:j\d+>> Shr [<<Value>>,<<Shift>>] + /// CHECK-DAG: Return [<<Shr>>] + + public static long $noinline$longUnnecessaryShiftMasking(long value, int shift) { + if (doThrow) { throw new Error(); } + return value >> (shift & 63); + } + + /// CHECK-START: int Main.$noinline$intUnnecessaryWiderShiftMasking(int, int) instruction_simplifier (before) + /// CHECK: <<Value:i\d+>> ParameterValue + /// CHECK: <<Shift:i\d+>> ParameterValue + /// CHECK-DAG: <<Const255:i\d+>> IntConstant 255 + /// CHECK-DAG: <<And:i\d+>> And [<<Shift>>,<<Const255>>] + /// CHECK-DAG: <<UShr:i\d+>> UShr [<<Value>>,<<And>>] + /// CHECK-DAG: Return [<<UShr>>] + + /// CHECK-START: int Main.$noinline$intUnnecessaryWiderShiftMasking(int, int) instruction_simplifier (after) + /// CHECK: <<Value:i\d+>> ParameterValue + /// CHECK: <<Shift:i\d+>> ParameterValue + /// CHECK-DAG: <<UShr:i\d+>> UShr [<<Value>>,<<Shift>>] + /// CHECK-DAG: Return [<<UShr>>] + + public static int $noinline$intUnnecessaryWiderShiftMasking(int value, int shift) { + if (doThrow) { throw new Error(); } + return value >>> (shift & 0xff); + } + + /// CHECK-START: long Main.$noinline$longSmallerShiftMasking(long, int) instruction_simplifier (before) + /// CHECK: <<Value:j\d+>> ParameterValue + /// CHECK: <<Shift:i\d+>> ParameterValue + /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3 + /// CHECK-DAG: <<And:i\d+>> And [<<Shift>>,<<Const3>>] + /// CHECK-DAG: <<Shl:j\d+>> Shl [<<Value>>,<<And>>] + /// CHECK-DAG: Return [<<Shl>>] + + /// CHECK-START: long Main.$noinline$longSmallerShiftMasking(long, int) instruction_simplifier (after) + /// CHECK: <<Value:j\d+>> ParameterValue + /// CHECK: <<Shift:i\d+>> ParameterValue + /// CHECK-DAG: <<Const3:i\d+>> IntConstant 3 + /// CHECK-DAG: <<And:i\d+>> And [<<Shift>>,<<Const3>>] + /// CHECK-DAG: <<Shl:j\d+>> Shl [<<Value>>,<<And>>] + /// CHECK-DAG: Return [<<Shl>>] + + public static long $noinline$longSmallerShiftMasking(long value, int shift) { + if (doThrow) { throw new Error(); } + return value << (shift & 3); + } + + /// CHECK-START: int Main.$noinline$otherUseOfUnnecessaryShiftMasking(int, int) instruction_simplifier (before) + /// CHECK: <<Value:i\d+>> ParameterValue + /// CHECK: <<Shift:i\d+>> ParameterValue + /// CHECK-DAG: <<Const31:i\d+>> IntConstant 31 + /// CHECK-DAG: <<And:i\d+>> And [<<Shift>>,<<Const31>>] + /// CHECK-DAG: <<Shr:i\d+>> Shr [<<Value>>,<<And>>] + /// CHECK-DAG: <<Add:i\d+>> Add [<<Shr>>,<<And>>] + /// CHECK-DAG: Return [<<Add>>] + + /// CHECK-START: int Main.$noinline$otherUseOfUnnecessaryShiftMasking(int, int) instruction_simplifier (after) + /// CHECK: <<Value:i\d+>> ParameterValue + /// CHECK: <<Shift:i\d+>> ParameterValue + /// CHECK-DAG: <<Const31:i\d+>> IntConstant 31 + /// CHECK-DAG: <<And:i\d+>> And [<<Shift>>,<<Const31>>] + /// CHECK-DAG: <<Shr:i\d+>> Shr [<<Value>>,<<Shift>>] + /// CHECK-DAG: <<Add:i\d+>> Add [<<Shr>>,<<And>>] + /// CHECK-DAG: Return [<<Add>>] + + public static int $noinline$otherUseOfUnnecessaryShiftMasking(int value, int shift) { + if (doThrow) { throw new Error(); } + int temp = shift & 31; + return (value >> temp) + temp; + } + public static void main(String[] args) { int arg = 123456; - assertLongEquals(Add0(arg), arg); - assertIntEquals(AndAllOnes(arg), arg); - assertLongEquals(Div1(arg), arg); - assertIntEquals(DivN1(arg), -arg); - assertLongEquals(Mul1(arg), arg); - assertIntEquals(MulN1(arg), -arg); - assertLongEquals(MulPowerOfTwo128(arg), (128 * arg)); - assertIntEquals(Or0(arg), arg); - assertLongEquals(OrSame(arg), arg); - assertIntEquals(Shl0(arg), arg); - assertLongEquals(Shr0(arg), arg); - assertLongEquals(Shr64(arg), arg); - assertLongEquals(Sub0(arg), arg); - assertIntEquals(SubAliasNeg(arg), -arg); - assertLongEquals(UShr0(arg), arg); - assertIntEquals(Xor0(arg), arg); - assertIntEquals(XorAllOnes(arg), ~arg); - assertIntEquals(AddNegs1(arg, arg + 1), -(arg + arg + 1)); - assertIntEquals(AddNegs2(arg, arg + 1), -(arg + arg + 1)); - assertLongEquals(AddNegs3(arg, arg + 1), -(2 * arg + 1)); - assertLongEquals(AddNeg1(arg, arg + 1), 1); - assertLongEquals(AddNeg2(arg, arg + 1), -1); - assertLongEquals(NegNeg1(arg), arg); - assertIntEquals(NegNeg2(arg), 0); - assertLongEquals(NegNeg3(arg), arg); - assertIntEquals(NegSub1(arg, arg + 1), 1); - assertIntEquals(NegSub2(arg, arg + 1), 1); - assertLongEquals(NotNot1(arg), arg); - assertIntEquals(NotNot2(arg), -1); - assertIntEquals(SubNeg1(arg, arg + 1), -(arg + arg + 1)); - assertIntEquals(SubNeg2(arg, arg + 1), -(arg + arg + 1)); - assertLongEquals(SubNeg3(arg, arg + 1), -(2 * arg + 1)); - assertBooleanEquals(EqualBoolVsIntConst(true), true); - assertBooleanEquals(EqualBoolVsIntConst(true), true); - assertBooleanEquals(NotEqualBoolVsIntConst(false), false); - assertBooleanEquals(NotEqualBoolVsIntConst(false), false); - assertBooleanEquals(NotNotBool(true), true); - assertBooleanEquals(NotNotBool(false), false); - assertFloatEquals(Div2(100.0f), 50.0f); - assertDoubleEquals(Div2(150.0), 75.0); - assertFloatEquals(DivMP25(100.0f), -400.0f); - assertDoubleEquals(DivMP25(150.0), -600.0); - assertIntEquals(UShr28And15(0xc1234567), 0xc); - assertLongEquals(UShr60And15(0xc123456787654321L), 0xcL); - assertIntEquals(UShr28And7(0xc1234567), 0x4); - assertLongEquals(UShr60And7(0xc123456787654321L), 0x4L); - assertIntEquals(Shr24And255(0xc1234567), 0xc1); - assertLongEquals(Shr56And255(0xc123456787654321L), 0xc1L); - assertIntEquals(Shr24And127(0xc1234567), 0x41); - assertLongEquals(Shr56And127(0xc123456787654321L), 0x41L); - assertIntEquals(0, mulPow2Plus1(0)); - assertIntEquals(9, mulPow2Plus1(1)); - assertIntEquals(18, mulPow2Plus1(2)); - assertIntEquals(900, mulPow2Plus1(100)); - assertIntEquals(111105, mulPow2Plus1(12345)); - assertLongEquals(0, mulPow2Minus1(0)); - assertLongEquals(31, mulPow2Minus1(1)); - assertLongEquals(62, mulPow2Minus1(2)); - assertLongEquals(3100, mulPow2Minus1(100)); - assertLongEquals(382695, mulPow2Minus1(12345)); + assertLongEquals(arg, $noinline$Add0(arg)); + assertIntEquals(arg, $noinline$AndAllOnes(arg)); + assertLongEquals(arg, $noinline$Div1(arg)); + assertIntEquals(-arg, $noinline$DivN1(arg)); + assertLongEquals(arg, $noinline$Mul1(arg)); + assertIntEquals(-arg, $noinline$MulN1(arg)); + assertLongEquals((128 * arg), $noinline$MulPowerOfTwo128(arg)); + assertIntEquals(arg, $noinline$Or0(arg)); + assertLongEquals(arg, $noinline$OrSame(arg)); + assertIntEquals(arg, $noinline$Shl0(arg)); + assertLongEquals(arg, $noinline$Shr0(arg)); + assertLongEquals(arg, $noinline$Shr64(arg)); + assertLongEquals(arg, $noinline$Sub0(arg)); + assertIntEquals(-arg, $noinline$SubAliasNeg(arg)); + assertLongEquals(arg, $noinline$UShr0(arg)); + assertIntEquals(arg, $noinline$Xor0(arg)); + assertIntEquals(~arg, $noinline$XorAllOnes(arg)); + assertIntEquals(-(arg + arg + 1), $noinline$AddNegs1(arg, arg + 1)); + assertIntEquals(-(arg + arg + 1), $noinline$AddNegs2(arg, arg + 1)); + assertLongEquals(-(2 * arg + 1), $noinline$AddNegs3(arg, arg + 1)); + assertLongEquals(1, $noinline$AddNeg1(arg, arg + 1)); + assertLongEquals(-1, $noinline$AddNeg2(arg, arg + 1)); + assertLongEquals(arg, $noinline$NegNeg1(arg)); + assertIntEquals(0, $noinline$NegNeg2(arg)); + assertLongEquals(arg, $noinline$NegNeg3(arg)); + assertIntEquals(1, $noinline$NegSub1(arg, arg + 1)); + assertIntEquals(1, $noinline$NegSub2(arg, arg + 1)); + assertLongEquals(arg, $noinline$NotNot1(arg)); + assertIntEquals(-1, $noinline$NotNot2(arg)); + assertIntEquals(-(arg + arg + 1), $noinline$SubNeg1(arg, arg + 1)); + assertIntEquals(-(arg + arg + 1), $noinline$SubNeg2(arg, arg + 1)); + assertLongEquals(-(2 * arg + 1), $noinline$SubNeg3(arg, arg + 1)); + assertBooleanEquals(true, $noinline$EqualBoolVsIntConst(true)); + assertBooleanEquals(true, $noinline$EqualBoolVsIntConst(true)); + assertBooleanEquals(false, $noinline$NotEqualBoolVsIntConst(false)); + assertBooleanEquals(false, $noinline$NotEqualBoolVsIntConst(false)); + assertBooleanEquals(true, $noinline$NotNotBool(true)); + assertBooleanEquals(false, $noinline$NotNotBool(false)); + assertFloatEquals(50.0f, $noinline$Div2(100.0f)); + assertDoubleEquals(75.0, $noinline$Div2(150.0)); + assertFloatEquals(-400.0f, $noinline$DivMP25(100.0f)); + assertDoubleEquals(-600.0, $noinline$DivMP25(150.0)); + assertIntEquals(0xc, $noinline$UShr28And15(0xc1234567)); + assertLongEquals(0xcL, $noinline$UShr60And15(0xc123456787654321L)); + assertIntEquals(0x4, $noinline$UShr28And7(0xc1234567)); + assertLongEquals(0x4L, $noinline$UShr60And7(0xc123456787654321L)); + assertIntEquals(0xc1, $noinline$Shr24And255(0xc1234567)); + assertLongEquals(0xc1L, $noinline$Shr56And255(0xc123456787654321L)); + assertIntEquals(0x41, $noinline$Shr24And127(0xc1234567)); + assertLongEquals(0x41L, $noinline$Shr56And127(0xc123456787654321L)); + assertIntEquals(0, $noinline$mulPow2Plus1(0)); + assertIntEquals(9, $noinline$mulPow2Plus1(1)); + assertIntEquals(18, $noinline$mulPow2Plus1(2)); + assertIntEquals(900, $noinline$mulPow2Plus1(100)); + assertIntEquals(111105, $noinline$mulPow2Plus1(12345)); + assertLongEquals(0, $noinline$mulPow2Minus1(0)); + assertLongEquals(31, $noinline$mulPow2Minus1(1)); + assertLongEquals(62, $noinline$mulPow2Minus1(2)); + assertLongEquals(3100, $noinline$mulPow2Minus1(100)); + assertLongEquals(382695, $noinline$mulPow2Minus1(12345)); booleanField = false; - assertIntEquals(booleanFieldNotEqualOne(), 54); - assertIntEquals(booleanFieldEqualZero(), 54); + assertIntEquals($noinline$booleanFieldNotEqualOne(), 54); + assertIntEquals($noinline$booleanFieldEqualZero(), 54); booleanField = true; - assertIntEquals(booleanFieldNotEqualOne(), 13); - assertIntEquals(booleanFieldEqualZero(), 13); - assertIntEquals(intConditionNotEqualOne(6), 54); - assertIntEquals(intConditionNotEqualOne(43), 13); - assertIntEquals(intConditionEqualZero(6), 54); - assertIntEquals(intConditionEqualZero(43), 13); - assertIntEquals(floatConditionNotEqualOne(6.0f), 54); - assertIntEquals(floatConditionNotEqualOne(43.0f), 13); - assertIntEquals(doubleConditionEqualZero(6.0), 54); - assertIntEquals(doubleConditionEqualZero(43.0), 13); - - assertIntEquals(1234567, intToDoubleToInt(1234567)); - assertIntEquals(Integer.MIN_VALUE, intToDoubleToInt(Integer.MIN_VALUE)); - assertIntEquals(Integer.MAX_VALUE, intToDoubleToInt(Integer.MAX_VALUE)); - assertStringEquals("d=7654321.0, i=7654321", intToDoubleToIntPrint(7654321)); - assertIntEquals(12, byteToDoubleToInt((byte) 12)); - assertIntEquals(Byte.MIN_VALUE, byteToDoubleToInt(Byte.MIN_VALUE)); - assertIntEquals(Byte.MAX_VALUE, byteToDoubleToInt(Byte.MAX_VALUE)); - assertIntEquals(11, floatToDoubleToInt(11.3f)); - assertStringEquals("d=12.25, i=12", floatToDoubleToIntPrint(12.25f)); - assertIntEquals(123, byteToDoubleToShort((byte) 123)); - assertIntEquals(Byte.MIN_VALUE, byteToDoubleToShort(Byte.MIN_VALUE)); - assertIntEquals(Byte.MAX_VALUE, byteToDoubleToShort(Byte.MAX_VALUE)); - assertIntEquals(1234, charToDoubleToShort((char) 1234)); - assertIntEquals(Character.MIN_VALUE, charToDoubleToShort(Character.MIN_VALUE)); - assertIntEquals(/* sign-extended */ -1, charToDoubleToShort(Character.MAX_VALUE)); - assertIntEquals(12345, floatToIntToShort(12345.75f)); - assertIntEquals(Short.MAX_VALUE, floatToIntToShort((float)(Short.MIN_VALUE - 1))); - assertIntEquals(Short.MIN_VALUE, floatToIntToShort((float)(Short.MAX_VALUE + 1))); - assertIntEquals(-54321, intToFloatToInt(-54321)); - assertDoubleEquals((double) 0x12345678, longToIntToDouble(0x1234567812345678L)); - assertDoubleEquals(0.0, longToIntToDouble(Long.MIN_VALUE)); - assertDoubleEquals(-1.0, longToIntToDouble(Long.MAX_VALUE)); - assertLongEquals(0x0000000012345678L, longToIntToLong(0x1234567812345678L)); - assertLongEquals(0xffffffff87654321L, longToIntToLong(0x1234567887654321L)); - assertLongEquals(0L, longToIntToLong(Long.MIN_VALUE)); - assertLongEquals(-1L, longToIntToLong(Long.MAX_VALUE)); - assertIntEquals((short) -5678, shortToCharToShort((short) -5678)); - assertIntEquals(Short.MIN_VALUE, shortToCharToShort(Short.MIN_VALUE)); - assertIntEquals(Short.MAX_VALUE, shortToCharToShort(Short.MAX_VALUE)); - assertIntEquals(5678, shortToLongToInt((short) 5678)); - assertIntEquals(Short.MIN_VALUE, shortToLongToInt(Short.MIN_VALUE)); - assertIntEquals(Short.MAX_VALUE, shortToLongToInt(Short.MAX_VALUE)); - assertIntEquals(0x34, shortToCharToByte((short) 0x1234)); - assertIntEquals(-0x10, shortToCharToByte((short) 0x12f0)); - assertIntEquals(0, shortToCharToByte(Short.MIN_VALUE)); - assertIntEquals(-1, shortToCharToByte(Short.MAX_VALUE)); - assertStringEquals("c=1025, b=1", shortToCharToBytePrint((short) 1025)); - assertStringEquals("c=1023, b=-1", shortToCharToBytePrint((short) 1023)); - assertStringEquals("c=65535, b=-1", shortToCharToBytePrint((short) -1)); - - assertIntEquals(0x21, longAnd0xffToByte(0x1234432112344321L)); - assertIntEquals(0, longAnd0xffToByte(Long.MIN_VALUE)); - assertIntEquals(-1, longAnd0xffToByte(Long.MAX_VALUE)); - assertIntEquals(0x1234, intAnd0x1ffffToChar(0x43211234)); - assertIntEquals(0, intAnd0x1ffffToChar(Integer.MIN_VALUE)); - assertIntEquals(Character.MAX_VALUE, intAnd0x1ffffToChar(Integer.MAX_VALUE)); - assertIntEquals(0x4321, intAnd0x17fffToShort(0x87654321)); - assertIntEquals(0x0888, intAnd0x17fffToShort(0x88888888)); - assertIntEquals(0, intAnd0x17fffToShort(Integer.MIN_VALUE)); - assertIntEquals(Short.MAX_VALUE, intAnd0x17fffToShort(Integer.MAX_VALUE)); - - assertDoubleEquals(0.0, shortAnd0xffffToShortToDouble((short) 0)); - assertDoubleEquals(1.0, shortAnd0xffffToShortToDouble((short) 1)); - assertDoubleEquals(-2.0, shortAnd0xffffToShortToDouble((short) -2)); - assertDoubleEquals(12345.0, shortAnd0xffffToShortToDouble((short) 12345)); - assertDoubleEquals((double)Short.MAX_VALUE, shortAnd0xffffToShortToDouble(Short.MAX_VALUE)); - assertDoubleEquals((double)Short.MIN_VALUE, shortAnd0xffffToShortToDouble(Short.MIN_VALUE)); - - assertIntEquals(intReverseCondition(41), 13); - assertIntEquals(intReverseConditionNaN(-5), 13); + assertIntEquals(13, $noinline$booleanFieldNotEqualOne()); + assertIntEquals(13, $noinline$booleanFieldEqualZero()); + assertIntEquals(54, $noinline$intConditionNotEqualOne(6)); + assertIntEquals(13, $noinline$intConditionNotEqualOne(43)); + assertIntEquals(54, $noinline$intConditionEqualZero(6)); + assertIntEquals(13, $noinline$intConditionEqualZero(43)); + assertIntEquals(54, $noinline$floatConditionNotEqualOne(6.0f)); + assertIntEquals(13, $noinline$floatConditionNotEqualOne(43.0f)); + assertIntEquals(54, $noinline$doubleConditionEqualZero(6.0)); + assertIntEquals(13, $noinline$doubleConditionEqualZero(43.0)); + + assertIntEquals(1234567, $noinline$intToDoubleToInt(1234567)); + assertIntEquals(Integer.MIN_VALUE, $noinline$intToDoubleToInt(Integer.MIN_VALUE)); + assertIntEquals(Integer.MAX_VALUE, $noinline$intToDoubleToInt(Integer.MAX_VALUE)); + assertStringEquals("d=7654321.0, i=7654321", $noinline$intToDoubleToIntPrint(7654321)); + assertIntEquals(12, $noinline$byteToDoubleToInt((byte) 12)); + assertIntEquals(Byte.MIN_VALUE, $noinline$byteToDoubleToInt(Byte.MIN_VALUE)); + assertIntEquals(Byte.MAX_VALUE, $noinline$byteToDoubleToInt(Byte.MAX_VALUE)); + assertIntEquals(11, $noinline$floatToDoubleToInt(11.3f)); + assertStringEquals("d=12.25, i=12", $noinline$floatToDoubleToIntPrint(12.25f)); + assertIntEquals(123, $noinline$byteToDoubleToShort((byte) 123)); + assertIntEquals(Byte.MIN_VALUE, $noinline$byteToDoubleToShort(Byte.MIN_VALUE)); + assertIntEquals(Byte.MAX_VALUE, $noinline$byteToDoubleToShort(Byte.MAX_VALUE)); + assertIntEquals(1234, $noinline$charToDoubleToShort((char) 1234)); + assertIntEquals(Character.MIN_VALUE, $noinline$charToDoubleToShort(Character.MIN_VALUE)); + assertIntEquals(/* sign-extended */ -1, $noinline$charToDoubleToShort(Character.MAX_VALUE)); + assertIntEquals(12345, $noinline$floatToIntToShort(12345.75f)); + assertIntEquals(Short.MAX_VALUE, $noinline$floatToIntToShort((float)(Short.MIN_VALUE - 1))); + assertIntEquals(Short.MIN_VALUE, $noinline$floatToIntToShort((float)(Short.MAX_VALUE + 1))); + assertIntEquals(-54321, $noinline$intToFloatToInt(-54321)); + assertDoubleEquals((double) 0x12345678, $noinline$longToIntToDouble(0x1234567812345678L)); + assertDoubleEquals(0.0, $noinline$longToIntToDouble(Long.MIN_VALUE)); + assertDoubleEquals(-1.0, $noinline$longToIntToDouble(Long.MAX_VALUE)); + assertLongEquals(0x0000000012345678L, $noinline$longToIntToLong(0x1234567812345678L)); + assertLongEquals(0xffffffff87654321L, $noinline$longToIntToLong(0x1234567887654321L)); + assertLongEquals(0L, $noinline$longToIntToLong(Long.MIN_VALUE)); + assertLongEquals(-1L, $noinline$longToIntToLong(Long.MAX_VALUE)); + assertIntEquals((short) -5678, $noinline$shortToCharToShort((short) -5678)); + assertIntEquals(Short.MIN_VALUE, $noinline$shortToCharToShort(Short.MIN_VALUE)); + assertIntEquals(Short.MAX_VALUE, $noinline$shortToCharToShort(Short.MAX_VALUE)); + assertIntEquals(5678, $noinline$shortToLongToInt((short) 5678)); + assertIntEquals(Short.MIN_VALUE, $noinline$shortToLongToInt(Short.MIN_VALUE)); + assertIntEquals(Short.MAX_VALUE, $noinline$shortToLongToInt(Short.MAX_VALUE)); + assertIntEquals(0x34, $noinline$shortToCharToByte((short) 0x1234)); + assertIntEquals(-0x10, $noinline$shortToCharToByte((short) 0x12f0)); + assertIntEquals(0, $noinline$shortToCharToByte(Short.MIN_VALUE)); + assertIntEquals(-1, $noinline$shortToCharToByte(Short.MAX_VALUE)); + assertStringEquals("c=1025, b=1", $noinline$shortToCharToBytePrint((short) 1025)); + assertStringEquals("c=1023, b=-1", $noinline$shortToCharToBytePrint((short) 1023)); + assertStringEquals("c=65535, b=-1", $noinline$shortToCharToBytePrint((short) -1)); + + assertIntEquals(0x21, $noinline$longAnd0xffToByte(0x1234432112344321L)); + assertIntEquals(0, $noinline$longAnd0xffToByte(Long.MIN_VALUE)); + assertIntEquals(-1, $noinline$longAnd0xffToByte(Long.MAX_VALUE)); + assertIntEquals(0x1234, $noinline$intAnd0x1ffffToChar(0x43211234)); + assertIntEquals(0, $noinline$intAnd0x1ffffToChar(Integer.MIN_VALUE)); + assertIntEquals(Character.MAX_VALUE, $noinline$intAnd0x1ffffToChar(Integer.MAX_VALUE)); + assertIntEquals(0x4321, $noinline$intAnd0x17fffToShort(0x87654321)); + assertIntEquals(0x0888, $noinline$intAnd0x17fffToShort(0x88888888)); + assertIntEquals(0, $noinline$intAnd0x17fffToShort(Integer.MIN_VALUE)); + assertIntEquals(Short.MAX_VALUE, $noinline$intAnd0x17fffToShort(Integer.MAX_VALUE)); + + assertDoubleEquals(0.0, $noinline$shortAnd0xffffToShortToDouble((short) 0)); + assertDoubleEquals(1.0, $noinline$shortAnd0xffffToShortToDouble((short) 1)); + assertDoubleEquals(-2.0, $noinline$shortAnd0xffffToShortToDouble((short) -2)); + assertDoubleEquals(12345.0, $noinline$shortAnd0xffffToShortToDouble((short) 12345)); + assertDoubleEquals((double)Short.MAX_VALUE, + $noinline$shortAnd0xffffToShortToDouble(Short.MAX_VALUE)); + assertDoubleEquals((double)Short.MIN_VALUE, + $noinline$shortAnd0xffffToShortToDouble(Short.MIN_VALUE)); + + assertIntEquals(13, $noinline$intReverseCondition(41)); + assertIntEquals(13, $noinline$intReverseConditionNaN(-5)); for (String condition : new String[] { "Equal", "NotEqual" }) { for (String constant : new String[] { "True", "False" }) { for (String side : new String[] { "Rhs", "Lhs" }) { String name = condition + constant + side; - assertIntEquals(runSmaliTest(name, true), 5); - assertIntEquals(runSmaliTest(name, false), 3); + assertIntEquals(5, $noinline$runSmaliTest(name, true)); + assertIntEquals(3, $noinline$runSmaliTest(name, false)); } } } + + assertIntEquals(0x5e6f7808, $noinline$intUnnecessaryShiftMasking(0xabcdef01, 3)); + assertIntEquals(0x5e6f7808, $noinline$intUnnecessaryShiftMasking(0xabcdef01, 3 + 32)); + assertLongEquals(0xffffffffffffeaf3L, $noinline$longUnnecessaryShiftMasking(0xabcdef0123456789L, 50)); + assertLongEquals(0xffffffffffffeaf3L, $noinline$longUnnecessaryShiftMasking(0xabcdef0123456789L, 50 + 64)); + assertIntEquals(0x2af37b, $noinline$intUnnecessaryWiderShiftMasking(0xabcdef01, 10)); + assertIntEquals(0x2af37b, $noinline$intUnnecessaryWiderShiftMasking(0xabcdef01, 10 + 128)); + assertLongEquals(0xaf37bc048d159e24L, $noinline$longSmallerShiftMasking(0xabcdef0123456789L, 2)); + assertLongEquals(0xaf37bc048d159e24L, $noinline$longSmallerShiftMasking(0xabcdef0123456789L, 2 + 256)); + assertIntEquals(0xfffd5e7c, $noinline$otherUseOfUnnecessaryShiftMasking(0xabcdef01, 13)); + assertIntEquals(0xfffd5e7c, $noinline$otherUseOfUnnecessaryShiftMasking(0xabcdef01, 13 + 512)); } private static boolean $inline$true() { return true; } diff --git a/test/536-checker-intrinsic-optimization/src/Main.java b/test/536-checker-intrinsic-optimization/src/Main.java index 24ed2feff7..26475ae55c 100644 --- a/test/536-checker-intrinsic-optimization/src/Main.java +++ b/test/536-checker-intrinsic-optimization/src/Main.java @@ -30,6 +30,18 @@ public class Main { } } + public static void assertCharEquals(char expected, char result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void assertStringContains(String searchTerm, String result) { + if (result == null || !result.contains(searchTerm)) { + throw new Error("Search term: " + searchTerm + ", not found in: " + result); + } + } + public static void main(String[] args) { stringEqualsSame(); stringArgumentNotNull("Foo"); @@ -41,6 +53,57 @@ public class Main { assertBooleanEquals(true, $opt$noinline$isStringEmpty("")); assertBooleanEquals(false, $opt$noinline$isStringEmpty("abc")); assertBooleanEquals(false, $opt$noinline$isStringEmpty("0123456789")); + + assertCharEquals('a', $opt$noinline$stringCharAt("a", 0)); + assertCharEquals('a', $opt$noinline$stringCharAt("abc", 0)); + assertCharEquals('b', $opt$noinline$stringCharAt("abc", 1)); + assertCharEquals('c', $opt$noinline$stringCharAt("abc", 2)); + assertCharEquals('7', $opt$noinline$stringCharAt("0123456789", 7)); + + try { + $opt$noinline$stringCharAt("abc", -1); + throw new Error("Should throw SIOOB."); + } catch (StringIndexOutOfBoundsException sioob) { + assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString()); + assertStringContains("Main.$opt$noinline$stringCharAt", sioob.getStackTrace()[1].toString()); + } + try { + $opt$noinline$stringCharAt("abc", 3); + throw new Error("Should throw SIOOB."); + } catch (StringIndexOutOfBoundsException sioob) { + assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString()); + assertStringContains("Main.$opt$noinline$stringCharAt", sioob.getStackTrace()[1].toString()); + } + try { + $opt$noinline$stringCharAt("abc", Integer.MAX_VALUE); + throw new Error("Should throw SIOOB."); + } catch (StringIndexOutOfBoundsException sioob) { + assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString()); + assertStringContains("Main.$opt$noinline$stringCharAt", sioob.getStackTrace()[1].toString()); + } + + assertCharEquals('7', $opt$noinline$stringCharAtCatch("0123456789", 7)); + assertCharEquals('\0', $opt$noinline$stringCharAtCatch("0123456789", 10)); + + assertIntEquals('a' + 'b' + 'c', $opt$noinline$stringSumChars("abc")); + assertIntEquals('a' + 'b' + 'c', $opt$noinline$stringSumLeadingChars("abcdef", 3)); + try { + $opt$noinline$stringSumLeadingChars("abcdef", 7); + throw new Error("Should throw SIOOB."); + } catch (StringIndexOutOfBoundsException sioob) { + assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString()); + assertStringContains("Main.$opt$noinline$stringSumLeadingChars", + sioob.getStackTrace()[1].toString()); + } + assertIntEquals('a' + 'b' + 'c' + 'd', $opt$noinline$stringSum4LeadingChars("abcdef")); + try { + $opt$noinline$stringSum4LeadingChars("abc"); + throw new Error("Should throw SIOOB."); + } catch (StringIndexOutOfBoundsException sioob) { + assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString()); + assertStringContains("Main.$opt$noinline$stringSum4LeadingChars", + sioob.getStackTrace()[1].toString()); + } } /// CHECK-START: int Main.$opt$noinline$getStringLength(java.lang.String) instruction_simplifier (before) @@ -81,6 +144,144 @@ public class Main { return s.isEmpty(); } + /// CHECK-START: char Main.$opt$noinline$stringCharAt(java.lang.String, int) instruction_simplifier (before) + /// CHECK-DAG: <<Char:c\d+>> InvokeVirtual intrinsic:StringCharAt + /// CHECK-DAG: Return [<<Char>>] + + /// CHECK-START: char Main.$opt$noinline$stringCharAt(java.lang.String, int) instruction_simplifier (after) + /// CHECK-DAG: <<String:l\d+>> ParameterValue + /// CHECK-DAG: <<Pos:i\d+>> ParameterValue + /// CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<String>>] + /// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<NullCk>>] is_string_length:true + /// CHECK-DAG: BoundsCheck [<<Pos>>,<<Length>>] is_string_char_at:true + /// CHECK-DAG: <<Char:c\d+>> ArrayGet [<<NullCk>>,<<Pos>>] is_string_char_at:true + /// CHECK-DAG: Return [<<Char>>] + + /// CHECK-START: char Main.$opt$noinline$stringCharAt(java.lang.String, int) instruction_simplifier (after) + /// CHECK-NOT: InvokeVirtual intrinsic:StringCharAt + + static public char $opt$noinline$stringCharAt(String s, int pos) { + if (doThrow) { throw new Error(); } + return s.charAt(pos); + } + + /// CHECK-START: char Main.$opt$noinline$stringCharAtCatch(java.lang.String, int) instruction_simplifier (before) + /// CHECK-DAG: <<Char:c\d+>> InvokeVirtual intrinsic:StringCharAt + /// CHECK-DAG: Return [<<Char>>] + + /// CHECK-START: char Main.$opt$noinline$stringCharAtCatch(java.lang.String, int) instruction_simplifier (after) + /// CHECK-DAG: <<String:l\d+>> ParameterValue + /// CHECK-DAG: <<Pos:i\d+>> ParameterValue + /// CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<String>>] + /// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<NullCk>>] is_string_length:true + /// CHECK-DAG: BoundsCheck [<<Pos>>,<<Length>>] is_string_char_at:true + /// CHECK-DAG: <<Char:c\d+>> ArrayGet [<<NullCk>>,<<Pos>>] is_string_char_at:true + /// CHECK-DAG: Return [<<Char>>] + + /// CHECK-START: char Main.$opt$noinline$stringCharAtCatch(java.lang.String, int) instruction_simplifier (after) + /// CHECK-NOT: InvokeVirtual intrinsic:StringCharAt + + static public char $opt$noinline$stringCharAtCatch(String s, int pos) { + if (doThrow) { throw new Error(); } + try { + return s.charAt(pos); + } catch (StringIndexOutOfBoundsException ignored) { + return '\0'; + } + } + + /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) instruction_simplifier (before) + /// CHECK-DAG: InvokeVirtual intrinsic:StringLength + /// CHECK-DAG: InvokeVirtual intrinsic:StringCharAt + + /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) instruction_simplifier (after) + /// CHECK-DAG: ArrayLength is_string_length:true + /// CHECK-DAG: ArrayLength is_string_length:true + /// CHECK-DAG: BoundsCheck is_string_char_at:true + /// CHECK-DAG: ArrayGet is_string_char_at:true + + /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) instruction_simplifier (after) + /// CHECK-NOT: InvokeVirtual intrinsic:StringLength + /// CHECK-NOT: InvokeVirtual intrinsic:StringCharAt + + /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) GVN (after) + /// CHECK-DAG: ArrayLength is_string_length:true + /// CHECK-NOT: ArrayLength is_string_length:true + + /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) BCE (after) + /// CHECK-NOT: BoundsCheck + + static public int $opt$noinline$stringSumChars(String s) { + if (doThrow) { throw new Error(); } + int sum = 0; + int len = s.length(); + for (int i = 0; i < len; ++i) { + sum += s.charAt(i); + } + return sum; + } + + /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) instruction_simplifier (before) + /// CHECK-DAG: InvokeVirtual intrinsic:StringCharAt + + /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) instruction_simplifier (after) + /// CHECK-DAG: ArrayLength is_string_length:true + /// CHECK-DAG: BoundsCheck is_string_char_at:true + /// CHECK-DAG: ArrayGet is_string_char_at:true + + /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) instruction_simplifier (after) + /// CHECK-NOT: InvokeVirtual intrinsic:StringCharAt + + /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) BCE (after) + /// CHECK-DAG: Deoptimize env:[[{{[^\]]*}}]] + + /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) BCE (after) + /// CHECK-NOT: BoundsCheck is_string_char_at:true + + static public int $opt$noinline$stringSumLeadingChars(String s, int n) { + if (doThrow) { throw new Error(); } + int sum = 0; + for (int i = 0; i < n; ++i) { + sum += s.charAt(i); + } + return sum; + } + + /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) instruction_simplifier (before) + /// CHECK-DAG: InvokeVirtual intrinsic:StringCharAt + /// CHECK-DAG: InvokeVirtual intrinsic:StringCharAt + /// CHECK-DAG: InvokeVirtual intrinsic:StringCharAt + /// CHECK-DAG: InvokeVirtual intrinsic:StringCharAt + + /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) instruction_simplifier (after) + /// CHECK-DAG: ArrayLength is_string_length:true + /// CHECK-DAG: BoundsCheck is_string_char_at:true + /// CHECK-DAG: ArrayGet is_string_char_at:true + /// CHECK-DAG: ArrayLength is_string_length:true + /// CHECK-DAG: BoundsCheck is_string_char_at:true + /// CHECK-DAG: ArrayGet is_string_char_at:true + /// CHECK-DAG: ArrayLength is_string_length:true + /// CHECK-DAG: BoundsCheck is_string_char_at:true + /// CHECK-DAG: ArrayGet is_string_char_at:true + /// CHECK-DAG: ArrayLength is_string_length:true + /// CHECK-DAG: BoundsCheck is_string_char_at:true + /// CHECK-DAG: ArrayGet is_string_char_at:true + + /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) instruction_simplifier (after) + /// CHECK-NOT: InvokeVirtual intrinsic:StringCharAt + + /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) BCE (after) + /// CHECK-DAG: Deoptimize env:[[{{[^\]]*}}]] + + /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) BCE (after) + /// CHECK-NOT: BoundsCheck is_string_char_at:true + + static public int $opt$noinline$stringSum4LeadingChars(String s) { + if (doThrow) { throw new Error(); } + int sum = s.charAt(0) + s.charAt(1) + s.charAt(2) + s.charAt(3); + return sum; + } + /// CHECK-START: boolean Main.stringEqualsSame() instruction_simplifier (before) /// CHECK: InvokeStaticOrDirect diff --git a/test/552-checker-sharpening/src/Main.java b/test/552-checker-sharpening/src/Main.java index 3d985bfaf0..09a77ed285 100644 --- a/test/552-checker-sharpening/src/Main.java +++ b/test/552-checker-sharpening/src/Main.java @@ -28,6 +28,12 @@ public class Main { } } + public static void assertClassEquals(Class<?> expected, Class<?> result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + public static boolean doThrow = false; private static int $noinline$foo(int x) { @@ -251,6 +257,66 @@ public class Main { return "non-boot-image-string"; } + /// CHECK-START: java.lang.Class Main.$noinline$getStringClass() sharpening (before) + /// CHECK: LoadClass load_kind:DexCacheViaMethod class_name:java.lang.String + + /// CHECK-START-X86: java.lang.Class Main.$noinline$getStringClass() sharpening (after) + // Note: load kind depends on PIC/non-PIC + // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress. + /// CHECK: LoadClass load_kind:{{BootImageAddress|DexCachePcRelative|DexCacheViaMethod}} class_name:java.lang.String + + /// CHECK-START-X86_64: java.lang.Class Main.$noinline$getStringClass() sharpening (after) + // Note: load kind depends on PIC/non-PIC + // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress. + /// CHECK: LoadClass load_kind:{{BootImageAddress|DexCachePcRelative|DexCacheViaMethod}} class_name:java.lang.String + + /// CHECK-START-ARM: java.lang.Class Main.$noinline$getStringClass() sharpening (after) + // Note: load kind depends on PIC/non-PIC + // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress. + /// CHECK: LoadClass load_kind:{{BootImageAddress|DexCachePcRelative|DexCacheViaMethod}} class_name:java.lang.String + + /// CHECK-START-ARM64: java.lang.Class Main.$noinline$getStringClass() sharpening (after) + // Note: load kind depends on PIC/non-PIC + // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress. + /// CHECK: LoadClass load_kind:{{BootImageAddress|DexCachePcRelative|DexCacheViaMethod}} class_name:java.lang.String + + public static Class<?> $noinline$getStringClass() { + // Prevent inlining to avoid the string comparison being optimized away. + if (doThrow) { throw new Error(); } + // String class is known to be in the boot image. + return String.class; + } + + /// CHECK-START: java.lang.Class Main.$noinline$getOtherClass() sharpening (before) + /// CHECK: LoadClass load_kind:DexCacheViaMethod class_name:Other + + /// CHECK-START-X86: java.lang.Class Main.$noinline$getOtherClass() sharpening (after) + /// CHECK: LoadClass load_kind:DexCachePcRelative class_name:Other + + /// CHECK-START-X86: java.lang.Class Main.$noinline$getOtherClass() pc_relative_fixups_x86 (after) + /// CHECK-DAG: X86ComputeBaseMethodAddress + /// CHECK-DAG: LoadClass load_kind:DexCachePcRelative class_name:Other + + /// CHECK-START-X86_64: java.lang.Class Main.$noinline$getOtherClass() sharpening (after) + /// CHECK: LoadClass load_kind:DexCachePcRelative class_name:Other + + /// CHECK-START-ARM: java.lang.Class Main.$noinline$getOtherClass() sharpening (after) + /// CHECK: LoadClass load_kind:DexCachePcRelative class_name:Other + + /// CHECK-START-ARM: java.lang.Class Main.$noinline$getOtherClass() dex_cache_array_fixups_arm (after) + /// CHECK-DAG: ArmDexCacheArraysBase + /// CHECK-DAG: LoadClass load_kind:DexCachePcRelative class_name:Other + + /// CHECK-START-ARM64: java.lang.Class Main.$noinline$getOtherClass() sharpening (after) + /// CHECK: LoadClass load_kind:DexCachePcRelative class_name:Other + + public static Class<?> $noinline$getOtherClass() { + // Prevent inlining to avoid the string comparison being optimized away. + if (doThrow) { throw new Error(); } + // Other class is not in the boot image. + return Other.class; + } + public static void main(String[] args) { assertIntEquals(1, testSimple(1)); assertIntEquals(1, testDiamond(false, 1)); @@ -262,5 +328,10 @@ public class Main { assertIntEquals(-6, testLoopWithDiamond(new int[]{ 3, 4 }, true, 1)); assertStringEquals("", $noinline$getBootImageString()); assertStringEquals("non-boot-image-string", $noinline$getNonBootImageString()); + assertClassEquals(String.class, $noinline$getStringClass()); + assertClassEquals(Other.class, $noinline$getOtherClass()); } } + +class Other { +} diff --git a/test/566-polymorphic-inlining/polymorphic_inline.cc b/test/566-polymorphic-inlining/polymorphic_inline.cc index 7b2c6cbcd5..c0d93dd8a1 100644 --- a/test/566-polymorphic-inlining/polymorphic_inline.cc +++ b/test/566-polymorphic-inlining/polymorphic_inline.cc @@ -17,6 +17,7 @@ #include "art_method.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" +#include "jit/profiling_info.h" #include "oat_quick_method_header.h" #include "scoped_thread_state_change.h" #include "stack_map.h" @@ -37,8 +38,10 @@ static void do_checks(jclass cls, const char* method_name) { if (code_cache->ContainsPc(header->GetCode())) { break; } else { - // sleep one second to give time to the JIT compiler. - sleep(1); + // Sleep to yield to the compiler thread. + usleep(1000); + // Will either ensure it's compiled or do the compilation itself. + jit->CompileMethod(method, soa.Self(), /* osr */ false); } } @@ -47,7 +50,25 @@ static void do_checks(jclass cls, const char* method_name) { CHECK(info.HasInlineInfo(encoding)); } -extern "C" JNIEXPORT void JNICALL Java_Main_ensureJittedAndPolymorphicInline(JNIEnv*, jclass cls) { +static void allocate_profiling_info(jclass cls, const char* method_name) { + ScopedObjectAccess soa(Thread::Current()); + mirror::Class* klass = soa.Decode<mirror::Class*>(cls); + ArtMethod* method = klass->FindDeclaredDirectMethodByName(method_name, sizeof(void*)); + ProfilingInfo::Create(soa.Self(), method, /* retry_allocation */ true); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_ensureProfilingInfo566(JNIEnv*, jclass cls) { + jit::Jit* jit = Runtime::Current()->GetJit(); + if (jit == nullptr) { + return; + } + + allocate_profiling_info(cls, "testInvokeVirtual"); + allocate_profiling_info(cls, "testInvokeInterface"); + allocate_profiling_info(cls, "$noinline$testInlineToSameTarget"); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_ensureJittedAndPolymorphicInline566(JNIEnv*, jclass cls) { jit::Jit* jit = Runtime::Current()->GetJit(); if (jit == nullptr) { return; diff --git a/test/566-polymorphic-inlining/src/Main.java b/test/566-polymorphic-inlining/src/Main.java index a59ce5b344..d39e6ed57b 100644 --- a/test/566-polymorphic-inlining/src/Main.java +++ b/test/566-polymorphic-inlining/src/Main.java @@ -39,6 +39,9 @@ public class Main implements Itf { itfs[1] = mains[1] = new Subclass(); itfs[2] = mains[2] = new OtherSubclass(); + // Create the profiling info eagerly to make sure they are filled. + ensureProfilingInfo566(); + // Make testInvokeVirtual and testInvokeInterface hot to get them jitted. // We pass Main and Subclass to get polymorphic inlining based on calling // the same method. @@ -51,7 +54,7 @@ public class Main implements Itf { $noinline$testInlineToSameTarget(mains[1]); } - ensureJittedAndPolymorphicInline(); + ensureJittedAndPolymorphicInline566(); // At this point, the JIT should have compiled both methods, and inline // sameInvokeVirtual and sameInvokeInterface. @@ -71,12 +74,12 @@ public class Main implements Itf { } public Class sameInvokeVirtual() { - field.getClass(); // null check to ensure we get an inlined frame in the CodeInfo + field.getClass(); // null check to ensure we get an inlined frame in the CodeInfo. return Main.class; } public Class sameInvokeInterface() { - field.getClass(); // null check to ensure we get an inlined frame in the CodeInfo + field.getClass(); // null check to ensure we get an inlined frame in the CodeInfo. return Itf.class; } @@ -95,7 +98,8 @@ public class Main implements Itf { public Object field = new Object(); - public static native void ensureJittedAndPolymorphicInline(); + public static native void ensureJittedAndPolymorphicInline566(); + public static native void ensureProfilingInfo566(); public void increment() { field.getClass(); // null check to ensure we get an inlined frame in the CodeInfo diff --git a/test/607-daemon-stress/expected.txt b/test/607-daemon-stress/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/607-daemon-stress/expected.txt diff --git a/test/607-daemon-stress/info.txt b/test/607-daemon-stress/info.txt new file mode 100644 index 0000000000..1047b767fa --- /dev/null +++ b/test/607-daemon-stress/info.txt @@ -0,0 +1,3 @@ +Stress test for daemon threads stuck in a method that requires the thread list lock. +(for example Thread.isInterrupted). The shutdown thread used to block those daemons +from making progress. diff --git a/test/607-daemon-stress/src/Main.java b/test/607-daemon-stress/src/Main.java new file mode 100644 index 0000000000..56ef4102b1 --- /dev/null +++ b/test/607-daemon-stress/src/Main.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2016 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. + */ + +public class Main extends Thread { + public static void main(String[] args) throws Exception { + for (int i = 0; i < 5; i++) { + Main m = new Main(); + m.setDaemon(true); + m.start(); + } + // Sleep a while to give some time for the threads to start. + Thread.sleep(1000); + } + + public void run() { + while (!isInterrupted()); + } +} diff --git a/test/608-checker-unresolved-lse/expected.txt b/test/608-checker-unresolved-lse/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/608-checker-unresolved-lse/expected.txt diff --git a/test/608-checker-unresolved-lse/info.txt b/test/608-checker-unresolved-lse/info.txt new file mode 100644 index 0000000000..466d5f44ec --- /dev/null +++ b/test/608-checker-unresolved-lse/info.txt @@ -0,0 +1,3 @@ +Regression test for the load store elimination optimization, +which used to wrongly remove field stores in the presence of +unresolved accesses. diff --git a/test/608-checker-unresolved-lse/run b/test/608-checker-unresolved-lse/run new file mode 100644 index 0000000000..226891f65a --- /dev/null +++ b/test/608-checker-unresolved-lse/run @@ -0,0 +1,18 @@ +#!/bin/bash +# +# Copyright (C) 2016 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. + +# Use secondary switch to add secondary dex file to class path. +exec ${RUN} "${@}" --secondary diff --git a/test/608-checker-unresolved-lse/src-dex2oat-unresolved/MissingSuperClass.java b/test/608-checker-unresolved-lse/src-dex2oat-unresolved/MissingSuperClass.java new file mode 100644 index 0000000000..b11b9be78e --- /dev/null +++ b/test/608-checker-unresolved-lse/src-dex2oat-unresolved/MissingSuperClass.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2016 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. + */ + +public class MissingSuperClass { +} diff --git a/test/608-checker-unresolved-lse/src/Main.java b/test/608-checker-unresolved-lse/src/Main.java new file mode 100644 index 0000000000..c6f8854b49 --- /dev/null +++ b/test/608-checker-unresolved-lse/src/Main.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2016 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. + */ + +// We make Main extend an unresolved super class. This will lead to an +// unresolved access to Foo.field, as we won't know if Main can access +// a package private field. +public class Main extends MissingSuperClass { + + public static void main(String[] args) { + instanceFieldTest(); + staticFieldTest(); + instanceFieldTest2(); + } + + /// CHECK-START: void Main.instanceFieldTest() inliner (before) + /// CHECK-NOT: InstanceFieldSet + + /// CHECK-START: void Main.instanceFieldTest() inliner (after) + /// CHECK: InstanceFieldSet + /// CHECK: UnresolvedInstanceFieldGet + + // Load store elimination used to remove the InstanceFieldSet, thinking + // that the UnresolvedInstanceFieldGet was not related. However inlining + // can put you in a situation where the UnresolvedInstanceFieldGet resolves + // to the same field as the one in InstanceFieldSet. So the InstanceFieldSet + // must be preserved. + + /// CHECK-START: void Main.instanceFieldTest() load_store_elimination (after) + /// CHECK: InstanceFieldSet + /// CHECK: UnresolvedInstanceFieldGet + public static void instanceFieldTest() { + Foo f = new Foo(); + if (f.iField != 42) { + throw new Error("Expected 42, got " + f.iField); + } + } + + /// CHECK-START: void Main.instanceFieldTest2() inliner (before) + /// CHECK-NOT: InstanceFieldSet + /// CHECK-NOT: InstanceFieldGet + + /// CHECK-START: void Main.instanceFieldTest2() inliner (after) + /// CHECK: InstanceFieldSet + /// CHECK: InstanceFieldGet + /// CHECK: UnresolvedInstanceFieldSet + /// CHECK: InstanceFieldGet + + // Load store elimination will eliminate the first InstanceFieldGet because + // it simply follows an InstanceFieldSet. It must however not eliminate the second + // InstanceFieldGet, as the UnresolvedInstanceFieldSet might resolve to the same + // field. + + /// CHECK-START: void Main.instanceFieldTest2() load_store_elimination (after) + /// CHECK: InstanceFieldSet + /// CHECK-NOT: InstanceFieldGet + /// CHECK: UnresolvedInstanceFieldSet + /// CHECK: InstanceFieldGet + public static void instanceFieldTest2() { + Foo f = new Foo(); + int a = f.$inline$GetInstanceField(); + f.iField = 43; + a = f.$inline$GetInstanceField(); + if (a != 43) { + throw new Error("Expected 43, got " + a); + } + } + + /// CHECK-START: void Main.staticFieldTest() inliner (before) + /// CHECK-NOT: StaticFieldSet + + /// CHECK-START: void Main.staticFieldTest() inliner (after) + /// CHECK: StaticFieldSet + /// CHECK: StaticFieldSet + /// CHECK: UnresolvedStaticFieldGet + + /// CHECK-START: void Main.staticFieldTest() load_store_elimination (after) + /// CHECK: StaticFieldSet + /// CHECK: StaticFieldSet + /// CHECK: UnresolvedStaticFieldGet + public static void staticFieldTest() { + // Ensure Foo is initialized. + Foo f = new Foo(); + f.$inline$StaticSet42(); + f.$inline$StaticSet43(); + if (Foo.sField != 43) { + throw new Error("Expected 43, got " + Foo.sField); + } + } +} + +class Foo { + // field needs to be package-private to make the access in Main.main + // unresolved. + int iField; + static int sField; + + public void $inline$StaticSet42() { + sField = 42; + } + + public void $inline$StaticSet43() { + sField = 43; + } + + public int $inline$GetInstanceField() { + return iField; + } + + // Constructor needs to be public to get it resolved in Main.main + // and therefore inlined. + public Foo() { + iField = 42; + } +} diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk index feee7c2c3d..01790aea12 100644 --- a/test/Android.libarttest.mk +++ b/test/Android.libarttest.mk @@ -25,6 +25,7 @@ LIBARTTEST_COMMON_SRC_FILES := \ 004-SignalTest/signaltest.cc \ 004-ReferenceMap/stack_walk_refmap_jni.cc \ 004-StackWalk/stack_walk_jni.cc \ + 004-ThreadStress/thread_stress.cc \ 004-UnsafeTest/unsafe_test.cc \ 044-proxy/native_proxy.cc \ 051-thread/thread_test.cc \ @@ -87,7 +88,11 @@ define build-libarttest LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.libarttest.mk ifeq ($$(art_target_or_host),target) $(call set-target-local-clang-vars) - $(call set-target-local-cflags-vars,debug) + ifeq ($$(suffix),d) + $(call set-target-local-cflags-vars,debug) + else + $(call set-target-local-cflags-vars,ndebug) + endif LOCAL_SHARED_LIBRARIES += libdl LOCAL_MULTILIB := both LOCAL_MODULE_PATH_32 := $(ART_TARGET_TEST_OUT)/$(ART_TARGET_ARCH_32) diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index f118a7625a..dd6b6f3fbc 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -294,7 +294,8 @@ TEST_ART_BROKEN_NO_PREBUILD_TESTS := \ 147-stripped-dex-fallback \ 554-jit-profile-file \ 529-checker-unresolved \ - 555-checker-regression-x86const + 555-checker-regression-x86const \ + 608-checker-unresolved-lse ifneq (,$(filter no-prebuild,$(PREBUILD_TYPES))) ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),no-prebuild, \ diff --git a/test/dexdump/all.dex b/test/dexdump/all.dex Binary files differnew file mode 100644 index 0000000000..caf678da4a --- /dev/null +++ b/test/dexdump/all.dex diff --git a/test/dexdump/all.lst b/test/dexdump/all.lst new file mode 100644 index 0000000000..17ab9cacdd --- /dev/null +++ b/test/dexdump/all.lst @@ -0,0 +1,21 @@ +#all.dex +0x0000043c 8 A <init> ()V (none) -1 +0x00000454 58 A arrays ()V (none) -1 +0x000004a0 130 A binary_ops ()V (none) -1 +0x00000534 66 A binary_ops_2addr ()V (none) -1 +0x00000588 34 A binary_ops_lit16 ()V (none) -1 +0x000005bc 46 A binary_ops_lit8 ()V (none) -1 +0x000005fc 22 A compares ()V (none) -1 +0x00000624 50 A conditionals ()V (none) -1 +0x00000668 56 A constants ()V (none) -1 +0x000006b0 108 A misc ()V (none) -1 +0x0000072c 46 A moves ()V (none) -1 +0x0000076c 32 A packed_switch ()V (none) -1 +0x0000079c 2 A return32 ()I (none) -1 +0x000007b0 2 A return64 ()I (none) -1 +0x000007c4 2 A return_object ()Ljava/lang/Object; (none) -1 +0x000007d8 44 A sparse_switch ()V (none) -1 +0x00000814 58 A static_fields ()V (none) -1 +0x00000860 44 A unary_ops ()V (none) -1 +0x0000089c 58 A instance_fields ()V (none) -1 +0x000008e8 30 A invokes ()V (none) -1 diff --git a/test/dexdump/all.txt b/test/dexdump/all.txt new file mode 100644 index 0000000000..af4fb4c12c --- /dev/null +++ b/test/dexdump/all.txt @@ -0,0 +1,622 @@ +Processing 'all.dex'... +Opened 'all.dex', DEX version '035' +DEX file header: +magic : 'dex\n035\0' +checksum : d5134208 +signature : 7af6...100f +file_size : 2572 +header_size : 112 +link_size : 0 +link_off : 0 (0x000000) +string_ids_size : 46 +string_ids_off : 112 (0x000070) +type_ids_size : 10 +type_ids_off : 296 (0x000128) +proto_ids_size : 3 +proto_ids_off : 336 (0x000150) +field_ids_size : 14 +field_ids_off : 372 (0x000174) +method_ids_size : 21 +method_ids_off : 484 (0x0001e4) +class_defs_size : 1 +class_defs_off : 652 (0x00028c) +data_size : 1888 +data_off : 684 (0x0002ac) + +Class #0 header: +class_idx : 4 +access_flags : 1 (0x0001) +superclass_idx : 5 +interfaces_off : 0 (0x000000) +source_file_idx : -1 +annotations_off : 0 (0x000000) +class_data_off : 2310 (0x000906) +static_fields_size : 7 +instance_fields_size: 7 +direct_methods_size : 18 +virtual_methods_size: 2 + +Class #0 - + Class descriptor : 'LA;' + Access flags : 0x0001 (PUBLIC) + Superclass : 'Ljava/lang/Object;' + Interfaces - + Static fields - + #0 : (in LA;) + name : 'sB' + type : 'B' + access : 0x000a (PRIVATE STATIC) + #1 : (in LA;) + name : 'sC' + type : 'C' + access : 0x000a (PRIVATE STATIC) + #2 : (in LA;) + name : 'sI' + type : 'I' + access : 0x000a (PRIVATE STATIC) + #3 : (in LA;) + name : 'sJ' + type : 'J' + access : 0x000a (PRIVATE STATIC) + #4 : (in LA;) + name : 'sO' + type : 'LA;' + access : 0x000a (PRIVATE STATIC) + #5 : (in LA;) + name : 'sS' + type : 'S' + access : 0x000a (PRIVATE STATIC) + #6 : (in LA;) + name : 'sZ' + type : 'Z' + access : 0x000a (PRIVATE STATIC) + Instance fields - + #0 : (in LA;) + name : 'mB' + type : 'B' + access : 0x0002 (PRIVATE) + #1 : (in LA;) + name : 'mC' + type : 'C' + access : 0x0002 (PRIVATE) + #2 : (in LA;) + name : 'mI' + type : 'I' + access : 0x0002 (PRIVATE) + #3 : (in LA;) + name : 'mJ' + type : 'J' + access : 0x0002 (PRIVATE) + #4 : (in LA;) + name : 'mO' + type : 'LA;' + access : 0x0002 (PRIVATE) + #5 : (in LA;) + name : 'mS' + type : 'S' + access : 0x0002 (PRIVATE) + #6 : (in LA;) + name : 'mZ' + type : 'Z' + access : 0x0002 (PRIVATE) + Direct methods - + #0 : (in LA;) + name : '<init>' + type : '()V' + access : 0x10001 (PUBLIC CONSTRUCTOR) + code - + registers : 1 + ins : 1 + outs : 1 + insns size : 4 16-bit code units +00042c: |[00042c] A.<init>:()V +00043c: 7010 1400 0000 |0000: invoke-direct {v0}, Ljava/lang/Object;.<init>:()V // method@0014 +000442: 0e00 |0003: return-void + catches : (none) + positions : + locals : + + #1 : (in LA;) + name : 'arrays' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 3 + ins : 0 + outs : 0 + insns size : 29 16-bit code units +000444: |[000444] A.arrays:()V +000454: 4400 0102 |0000: aget v0, v1, v2 +000458: 4500 0102 |0002: aget-wide v0, v1, v2 +00045c: 4600 0102 |0004: aget-object v0, v1, v2 +000460: 4700 0102 |0006: aget-boolean v0, v1, v2 +000464: 4800 0102 |0008: aget-byte v0, v1, v2 +000468: 4900 0102 |000a: aget-char v0, v1, v2 +00046c: 4a00 0102 |000c: aget-short v0, v1, v2 +000470: 4b00 0102 |000e: aput v0, v1, v2 +000474: 4c00 0102 |0010: aput-wide v0, v1, v2 +000478: 4d00 0102 |0012: aput-object v0, v1, v2 +00047c: 4e00 0102 |0014: aput-boolean v0, v1, v2 +000480: 4f00 0102 |0016: aput-byte v0, v1, v2 +000484: 5000 0102 |0018: aput-char v0, v1, v2 +000488: 5100 0102 |001a: aput-short v0, v1, v2 +00048c: 0e00 |001c: return-void + catches : (none) + positions : + locals : + + #2 : (in LA;) + name : 'binary_ops' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 3 + ins : 0 + outs : 0 + insns size : 65 16-bit code units +000490: |[000490] A.binary_ops:()V +0004a0: 9000 0102 |0000: add-int v0, v1, v2 +0004a4: 9100 0102 |0002: sub-int v0, v1, v2 +0004a8: 9200 0102 |0004: mul-int v0, v1, v2 +0004ac: 9300 0102 |0006: div-int v0, v1, v2 +0004b0: 9400 0102 |0008: rem-int v0, v1, v2 +0004b4: 9500 0102 |000a: and-int v0, v1, v2 +0004b8: 9600 0102 |000c: or-int v0, v1, v2 +0004bc: 9700 0102 |000e: xor-int v0, v1, v2 +0004c0: 9800 0102 |0010: shl-int v0, v1, v2 +0004c4: 9900 0102 |0012: shr-int v0, v1, v2 +0004c8: 9a00 0102 |0014: ushr-int v0, v1, v2 +0004cc: 9b00 0102 |0016: add-long v0, v1, v2 +0004d0: 9c00 0102 |0018: sub-long v0, v1, v2 +0004d4: 9d00 0102 |001a: mul-long v0, v1, v2 +0004d8: 9e00 0102 |001c: div-long v0, v1, v2 +0004dc: 9f00 0102 |001e: rem-long v0, v1, v2 +0004e0: a000 0102 |0020: and-long v0, v1, v2 +0004e4: a100 0102 |0022: or-long v0, v1, v2 +0004e8: a200 0102 |0024: xor-long v0, v1, v2 +0004ec: a300 0102 |0026: shl-long v0, v1, v2 +0004f0: a400 0102 |0028: shr-long v0, v1, v2 +0004f4: a500 0102 |002a: ushr-long v0, v1, v2 +0004f8: a600 0102 |002c: add-float v0, v1, v2 +0004fc: a700 0102 |002e: sub-float v0, v1, v2 +000500: a800 0102 |0030: mul-float v0, v1, v2 +000504: a900 0102 |0032: div-float v0, v1, v2 +000508: aa00 0102 |0034: rem-float v0, v1, v2 +00050c: ab00 0102 |0036: add-double v0, v1, v2 +000510: ac00 0102 |0038: sub-double v0, v1, v2 +000514: ad00 0102 |003a: mul-double v0, v1, v2 +000518: ae00 0102 |003c: div-double v0, v1, v2 +00051c: af00 0102 |003e: rem-double v0, v1, v2 +000520: 0e00 |0040: return-void + catches : (none) + positions : + locals : + + #3 : (in LA;) + name : 'binary_ops_2addr' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 2 + ins : 0 + outs : 0 + insns size : 33 16-bit code units +000524: |[000524] A.binary_ops_2addr:()V +000534: b010 |0000: add-int/2addr v0, v1 +000536: b110 |0001: sub-int/2addr v0, v1 +000538: b210 |0002: mul-int/2addr v0, v1 +00053a: b310 |0003: div-int/2addr v0, v1 +00053c: b410 |0004: rem-int/2addr v0, v1 +00053e: b510 |0005: and-int/2addr v0, v1 +000540: b610 |0006: or-int/2addr v0, v1 +000542: b710 |0007: xor-int/2addr v0, v1 +000544: b810 |0008: shl-int/2addr v0, v1 +000546: b910 |0009: shr-int/2addr v0, v1 +000548: ba10 |000a: ushr-int/2addr v0, v1 +00054a: bb10 |000b: add-long/2addr v0, v1 +00054c: bc10 |000c: sub-long/2addr v0, v1 +00054e: bd10 |000d: mul-long/2addr v0, v1 +000550: be10 |000e: div-long/2addr v0, v1 +000552: bf10 |000f: rem-long/2addr v0, v1 +000554: c010 |0010: and-long/2addr v0, v1 +000556: c110 |0011: or-long/2addr v0, v1 +000558: c210 |0012: xor-long/2addr v0, v1 +00055a: c310 |0013: shl-long/2addr v0, v1 +00055c: c410 |0014: shr-long/2addr v0, v1 +00055e: c510 |0015: ushr-long/2addr v0, v1 +000560: c610 |0016: add-float/2addr v0, v1 +000562: c710 |0017: sub-float/2addr v0, v1 +000564: c810 |0018: mul-float/2addr v0, v1 +000566: c910 |0019: div-float/2addr v0, v1 +000568: ca10 |001a: rem-float/2addr v0, v1 +00056a: cb10 |001b: add-double/2addr v0, v1 +00056c: cc10 |001c: sub-double/2addr v0, v1 +00056e: cd10 |001d: mul-double/2addr v0, v1 +000570: ce10 |001e: div-double/2addr v0, v1 +000572: cf10 |001f: rem-double/2addr v0, v1 +000574: 0e00 |0020: return-void + catches : (none) + positions : + locals : + + #4 : (in LA;) + name : 'binary_ops_lit16' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 2 + ins : 0 + outs : 0 + insns size : 17 16-bit code units +000578: |[000578] A.binary_ops_lit16:()V +000588: d010 3412 |0000: add-int/lit16 v0, v1, #int 4660 // #1234 +00058c: d110 3412 |0002: rsub-int v0, v1, #int 4660 // #1234 +000590: d210 3412 |0004: mul-int/lit16 v0, v1, #int 4660 // #1234 +000594: d310 3412 |0006: div-int/lit16 v0, v1, #int 4660 // #1234 +000598: d410 3412 |0008: rem-int/lit16 v0, v1, #int 4660 // #1234 +00059c: d510 3412 |000a: and-int/lit16 v0, v1, #int 4660 // #1234 +0005a0: d610 3412 |000c: or-int/lit16 v0, v1, #int 4660 // #1234 +0005a4: d710 3412 |000e: xor-int/lit16 v0, v1, #int 4660 // #1234 +0005a8: 0e00 |0010: return-void + catches : (none) + positions : + locals : + + #5 : (in LA;) + name : 'binary_ops_lit8' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 2 + ins : 0 + outs : 0 + insns size : 23 16-bit code units +0005ac: |[0005ac] A.binary_ops_lit8:()V +0005bc: d800 0112 |0000: add-int/lit8 v0, v1, #int 18 // #12 +0005c0: d900 0112 |0002: rsub-int/lit8 v0, v1, #int 18 // #12 +0005c4: da00 0112 |0004: mul-int/lit8 v0, v1, #int 18 // #12 +0005c8: db00 0112 |0006: div-int/lit8 v0, v1, #int 18 // #12 +0005cc: dc00 0112 |0008: rem-int/lit8 v0, v1, #int 18 // #12 +0005d0: dd00 0112 |000a: and-int/lit8 v0, v1, #int 18 // #12 +0005d4: de00 0112 |000c: or-int/lit8 v0, v1, #int 18 // #12 +0005d8: df00 0112 |000e: xor-int/lit8 v0, v1, #int 18 // #12 +0005dc: e000 0112 |0010: shl-int/lit8 v0, v1, #int 18 // #12 +0005e0: e100 0112 |0012: shr-int/lit8 v0, v1, #int 18 // #12 +0005e4: e200 0112 |0014: ushr-int/lit8 v0, v1, #int 18 // #12 +0005e8: 0e00 |0016: return-void + catches : (none) + positions : + locals : + + #6 : (in LA;) + name : 'compares' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 3 + ins : 0 + outs : 0 + insns size : 11 16-bit code units +0005ec: |[0005ec] A.compares:()V +0005fc: 2d00 0102 |0000: cmpl-float v0, v1, v2 +000600: 2e00 0102 |0002: cmpg-float v0, v1, v2 +000604: 2f00 0102 |0004: cmpl-double v0, v1, v2 +000608: 3000 0102 |0006: cmpg-double v0, v1, v2 +00060c: 3100 0102 |0008: cmp-long v0, v1, v2 +000610: 0e00 |000a: return-void + catches : (none) + positions : + locals : + + #7 : (in LA;) + name : 'conditionals' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 2 + ins : 0 + outs : 0 + insns size : 25 16-bit code units +000614: |[000614] A.conditionals:()V +000624: 3210 1800 |0000: if-eq v0, v1, 0018 // +0018 +000628: 3310 1600 |0002: if-ne v0, v1, 0018 // +0016 +00062c: 3410 1400 |0004: if-lt v0, v1, 0018 // +0014 +000630: 3510 1200 |0006: if-ge v0, v1, 0018 // +0012 +000634: 3610 1000 |0008: if-gt v0, v1, 0018 // +0010 +000638: 3710 0e00 |000a: if-le v0, v1, 0018 // +000e +00063c: 3800 0c00 |000c: if-eqz v0, 0018 // +000c +000640: 3900 0a00 |000e: if-nez v0, 0018 // +000a +000644: 3a00 0800 |0010: if-ltz v0, 0018 // +0008 +000648: 3b00 0600 |0012: if-gez v0, 0018 // +0006 +00064c: 3c00 0400 |0014: if-gtz v0, 0018 // +0004 +000650: 3d00 0200 |0016: if-lez v0, 0018 // +0002 +000654: 0e00 |0018: return-void + catches : (none) + positions : + locals : + + #8 : (in LA;) + name : 'constants' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 1 + ins : 0 + outs : 0 + insns size : 28 16-bit code units +000658: |[000658] A.constants:()V +000668: 1210 |0000: const/4 v0, #int 1 // #1 +00066a: 1300 3412 |0001: const/16 v0, #int 4660 // #1234 +00066e: 1400 7856 3412 |0003: const v0, #float 5.69046e-28 // #12345678 +000674: 1500 3412 |0006: const/high16 v0, #int 305397760 // #1234 +000678: 1600 3412 |0008: const-wide/16 v0, #int 4660 // #1234 +00067c: 1700 7856 3412 |000a: const-wide/32 v0, #float 5.69046e-28 // #12345678 +000682: 1800 efcd ab90 7856 3412 |000d: const-wide v0, #double 5.62635e-221 // #1234567890abcdef +00068c: 1900 3412 |0012: const-wide/high16 v0, #long 1311673391471656960 // #1234 +000690: 1a00 2c00 |0014: const-string v0, "string" // string@002c +000694: 1b00 2c00 0000 |0016: const-string/jumbo v0, "string" // string@0000002c +00069a: 1c00 0500 |0019: const-class v0, Ljava/lang/Object; // type@0005 +00069e: 0e00 |001b: return-void + catches : (none) + positions : + locals : + + #9 : (in LA;) + name : 'misc' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 5 + ins : 0 + outs : 0 + insns size : 54 16-bit code units +0006a0: |[0006a0] A.misc:()V +0006b0: 0000 |0000: nop // spacer +0006b2: 1d00 |0001: monitor-enter v0 +0006b4: 1e00 |0002: monitor-exit v0 +0006b6: 1f00 0500 |0003: check-cast v0, Ljava/lang/Object; // type@0005 +0006ba: 2010 0500 |0005: instance-of v0, v1, Ljava/lang/Object; // type@0005 +0006be: 2110 |0007: array-length v0, v1 +0006c0: 2200 0500 |0008: new-instance v0, Ljava/lang/Object; // type@0005 +0006c4: 2310 0500 |000a: new-array v0, v1, Ljava/lang/Object; // type@0005 +0006c8: 2454 0900 1032 |000c: filled-new-array {v0, v1, v2, v3, v4}, [Ljava/lang/Object; // type@0009 +0006ce: 2505 0900 0000 |000f: filled-new-array/range {v0, v1, v2, v3, v4}, [Ljava/lang/Object; // type@0009 +0006d4: 2600 0c00 0000 |0012: fill-array-data v0, 0000001e // +0000000c +0006da: 2700 |0015: throw v0 +0006dc: 2806 |0016: goto 001c // +0006 +0006de: 2900 0500 |0017: goto/16 001c // +0005 +0006e2: 2a00 0300 0000 |0019: goto/32 #00000003 +0006e8: 0e00 |001c: return-void +0006ea: 0000 |001d: nop // spacer +0006ec: 0003 0400 0a00 0000 0100 0000 0200 ... |001e: array-data (24 units) + catches : (none) + positions : + locals : + + #10 : (in LA;) + name : 'moves' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 2 + ins : 0 + outs : 0 + insns size : 23 16-bit code units +00071c: |[00071c] A.moves:()V +00072c: 0110 |0000: move v0, v1 +00072e: 0200 0100 |0001: move/from16 v0, v1 +000732: 0300 0000 0100 |0003: move/16 v0, v1 +000738: 0410 |0006: move-wide v0, v1 +00073a: 0500 0100 |0007: move-wide/from16 v0, v1 +00073e: 0600 0000 0100 |0009: move-wide/16 v0, v1 +000744: 0710 |000c: move-object v0, v1 +000746: 0800 0100 |000d: move-object/from16 v0, v1 +00074a: 0900 0000 0100 |000f: move-object/16 v0, v1 +000750: 0a00 |0012: move-result v0 +000752: 0b00 |0013: move-result-wide v0 +000754: 0c00 |0014: move-result-object v0 +000756: 0d00 |0015: move-exception v0 +000758: 0e00 |0016: return-void + catches : (none) + positions : + locals : + + #11 : (in LA;) + name : 'packed_switch' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 1 + ins : 0 + outs : 0 + insns size : 16 16-bit code units +00075c: |[00075c] A.packed_switch:()V +00076c: 2b00 0800 0000 |0000: packed-switch v0, 00000008 // +00000008 +000772: 0e00 |0003: return-void +000774: 28ff |0004: goto 0003 // -0001 +000776: 28fe |0005: goto 0003 // -0002 +000778: 28fd |0006: goto 0003 // -0003 +00077a: 0000 |0007: nop // spacer +00077c: 0001 0200 feff ff7f 0500 0000 0600 ... |0008: packed-switch-data (8 units) + catches : (none) + positions : + locals : + + #12 : (in LA;) + name : 'return32' + type : '()I' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 1 + ins : 0 + outs : 0 + insns size : 1 16-bit code units +00078c: |[00078c] A.return32:()I +00079c: 0f00 |0000: return v0 + catches : (none) + positions : + locals : + + #13 : (in LA;) + name : 'return64' + type : '()I' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 2 + ins : 0 + outs : 0 + insns size : 1 16-bit code units +0007a0: |[0007a0] A.return64:()I +0007b0: 1000 |0000: return-wide v0 + catches : (none) + positions : + locals : + + #14 : (in LA;) + name : 'return_object' + type : '()Ljava/lang/Object;' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 1 + ins : 0 + outs : 0 + insns size : 1 16-bit code units +0007b4: |[0007b4] A.return_object:()Ljava/lang/Object; +0007c4: 1100 |0000: return-object v0 + catches : (none) + positions : + locals : + + #15 : (in LA;) + name : 'sparse_switch' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 2 + ins : 0 + outs : 0 + insns size : 22 16-bit code units +0007c8: |[0007c8] A.sparse_switch:()V +0007d8: 2c00 0400 0000 |0000: sparse-switch v0, 00000004 // +00000004 +0007de: 0e00 |0003: return-void +0007e0: 0002 0400 1111 0000 2222 0000 3333 ... |0004: sparse-switch-data (18 units) + catches : (none) + positions : + locals : + + #16 : (in LA;) + name : 'static_fields' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 1 + ins : 0 + outs : 0 + insns size : 29 16-bit code units +000804: |[000804] A.static_fields:()V +000814: 6000 0900 |0000: sget v0, LA;.sI:I // field@0009 +000818: 6100 0a00 |0002: sget-wide v0, LA;.sJ:J // field@000a +00081c: 6200 0b00 |0004: sget-object v0, LA;.sO:LA; // field@000b +000820: 6300 0d00 |0006: sget-boolean v0, LA;.sZ:Z // field@000d +000824: 6400 0700 |0008: sget-byte v0, LA;.sB:B // field@0007 +000828: 6500 0800 |000a: sget-char v0, LA;.sC:C // field@0008 +00082c: 6600 0c00 |000c: sget-short v0, LA;.sS:S // field@000c +000830: 6700 0900 |000e: sput v0, LA;.sI:I // field@0009 +000834: 6800 0a00 |0010: sput-wide v0, LA;.sJ:J // field@000a +000838: 6900 0b00 |0012: sput-object v0, LA;.sO:LA; // field@000b +00083c: 6a00 0d00 |0014: sput-boolean v0, LA;.sZ:Z // field@000d +000840: 6b00 0700 |0016: sput-byte v0, LA;.sB:B // field@0007 +000844: 6c00 0800 |0018: sput-char v0, LA;.sC:C // field@0008 +000848: 6d00 0500 |001a: sput-short v0, LA;.mS:S // field@0005 +00084c: 0e00 |001c: return-void + catches : (none) + positions : + locals : + + #17 : (in LA;) + name : 'unary_ops' + type : '()V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 2 + ins : 0 + outs : 0 + insns size : 22 16-bit code units +000850: |[000850] A.unary_ops:()V +000860: 7b10 |0000: neg-int v0, v1 +000862: 7c10 |0001: not-int v0, v1 +000864: 7d10 |0002: neg-long v0, v1 +000866: 7e10 |0003: not-long v0, v1 +000868: 7f10 |0004: neg-float v0, v1 +00086a: 8010 |0005: neg-double v0, v1 +00086c: 8110 |0006: int-to-long v0, v1 +00086e: 8210 |0007: int-to-float v0, v1 +000870: 8310 |0008: int-to-double v0, v1 +000872: 8410 |0009: long-to-int v0, v1 +000874: 8510 |000a: long-to-float v0, v1 +000876: 8610 |000b: long-to-double v0, v1 +000878: 8710 |000c: float-to-int v0, v1 +00087a: 8810 |000d: float-to-long v0, v1 +00087c: 8910 |000e: float-to-double v0, v1 +00087e: 8a10 |000f: double-to-int v0, v1 +000880: 8b10 |0010: double-to-long v0, v1 +000882: 8c10 |0011: double-to-float v0, v1 +000884: 8d10 |0012: int-to-byte v0, v1 +000886: 8e10 |0013: int-to-char v0, v1 +000888: 8f10 |0014: int-to-short v0, v1 +00088a: 0e00 |0015: return-void + catches : (none) + positions : + locals : + + Virtual methods - + #0 : (in LA;) + name : 'instance_fields' + type : '()V' + access : 0x0001 (PUBLIC) + code - + registers : 2 + ins : 1 + outs : 0 + insns size : 29 16-bit code units +00088c: |[00088c] A.instance_fields:()V +00089c: 5210 0900 |0000: iget v0, v1, LA;.sI:I // field@0009 +0008a0: 5310 0a00 |0002: iget-wide v0, v1, LA;.sJ:J // field@000a +0008a4: 5410 0b00 |0004: iget-object v0, v1, LA;.sO:LA; // field@000b +0008a8: 5510 0d00 |0006: iget-boolean v0, v1, LA;.sZ:Z // field@000d +0008ac: 5610 0700 |0008: iget-byte v0, v1, LA;.sB:B // field@0007 +0008b0: 5710 0800 |000a: iget-char v0, v1, LA;.sC:C // field@0008 +0008b4: 5810 0c00 |000c: iget-short v0, v1, LA;.sS:S // field@000c +0008b8: 5910 0900 |000e: iput v0, v1, LA;.sI:I // field@0009 +0008bc: 5a10 0a00 |0010: iput-wide v0, v1, LA;.sJ:J // field@000a +0008c0: 5b10 0b00 |0012: iput-object v0, v1, LA;.sO:LA; // field@000b +0008c4: 5c10 0d00 |0014: iput-boolean v0, v1, LA;.sZ:Z // field@000d +0008c8: 5d10 0700 |0016: iput-byte v0, v1, LA;.sB:B // field@0007 +0008cc: 5e10 0800 |0018: iput-char v0, v1, LA;.sC:C // field@0008 +0008d0: 5f10 0c00 |001a: iput-short v0, v1, LA;.sS:S // field@000c +0008d4: 0e00 |001c: return-void + catches : (none) + positions : + locals : + + #1 : (in LA;) + name : 'invokes' + type : '()V' + access : 0x0001 (PUBLIC) + code - + registers : 5 + ins : 1 + outs : 1 + insns size : 15 16-bit code units +0008d8: |[0008d8] A.invokes:()V +0008e8: 6e54 0a00 1032 |0000: invoke-virtual {v0, v1, v2, v3, v4}, LA;.invokes:()V // method@000a +0008ee: 6f54 0a00 1032 |0003: invoke-super {v0, v1, v2, v3, v4}, LA;.invokes:()V // method@000a +0008f4: 7054 0a00 1032 |0006: invoke-direct {v0, v1, v2, v3, v4}, LA;.invokes:()V // method@000a +0008fa: 7154 0a00 1032 |0009: invoke-static {v0, v1, v2, v3, v4}, LA;.invokes:()V // method@000a +000900: 7254 0a00 1032 |000c: invoke-interface {v0, v1, v2, v3, v4}, LA;.invokes:()V // method@000a + catches : (none) + positions : + locals : + + source_file_idx : -1 (unknown) + diff --git a/test/dexdump/all.xml b/test/dexdump/all.xml new file mode 100644 index 0000000000..b623ecb2c1 --- /dev/null +++ b/test/dexdump/all.xml @@ -0,0 +1,211 @@ +<api> +<package name="" +> +<class name="A" + extends="java.lang.Object" + interface="false" + abstract="false" + static="false" + final="false" + visibility="public" +> +<constructor name="A" + type="A" + static="false" + final="false" + visibility="public" +> +</constructor> +<method name="arrays" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="binary_ops" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="binary_ops_2addr" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="binary_ops_lit16" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="binary_ops_lit8" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="compares" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="conditionals" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="constants" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="misc" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="moves" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="packed_switch" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="return32" + return="int" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="return64" + return="int" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="return_object" + return="java.lang.Object" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="sparse_switch" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="static_fields" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="unary_ops" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +</method> +<method name="instance_fields" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + visibility="public" +> +</method> +<method name="invokes" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + visibility="public" +> +</method> +</class> +</package> +</api> diff --git a/test/dexdump/bytecodes.txt b/test/dexdump/bytecodes.txt index 4c8b79b7dd..e1a381ec09 100755..100644 --- a/test/dexdump/bytecodes.txt +++ b/test/dexdump/bytecodes.txt @@ -12,8 +12,8 @@ string_ids_size : 153 string_ids_off : 112 (0x000070) type_ids_size : 42 type_ids_off : 724 (0x0002d4) -proto_ids_size : 12 -proto_ids_off : 892 (0x00037c) +proto_ids_size : 12 +proto_ids_off : 892 (0x00037c) field_ids_size : 40 field_ids_off : 1036 (0x00040c) method_ids_size : 28 @@ -36,6 +36,11 @@ instance_fields_size: 0 direct_methods_size : 0 virtual_methods_size: 1 +Class #0 annotations: +Annotations on class + VISIBILITY_RUNTIME Ljava/lang/annotation/Retention; value=CLASS + VISIBILITY_RUNTIME Ljava/lang/annotation/Target; value={ TYPE FIELD METHOD PARAMETER CONSTRUCTOR LOCAL_VARIABLE } + Class #0 - Class descriptor : 'Landroid/annotation/SuppressLint;' Access flags : 0x2601 (PUBLIC INTERFACE ABSTRACT ANNOTATION) @@ -67,6 +72,11 @@ instance_fields_size: 0 direct_methods_size : 0 virtual_methods_size: 1 +Class #1 annotations: +Annotations on class + VISIBILITY_RUNTIME Ljava/lang/annotation/Retention; value=CLASS + VISIBILITY_RUNTIME Ljava/lang/annotation/Target; value={ TYPE METHOD CONSTRUCTOR } + Class #1 - Class descriptor : 'Landroid/annotation/TargetApi;' Access flags : 0x2601 (PUBLIC INTERFACE ABSTRACT ANNOTATION) @@ -144,6 +154,11 @@ instance_fields_size: 0 direct_methods_size : 1 virtual_methods_size: 0 +Class #3 annotations: +Annotations on class + VISIBILITY_SYSTEM Ldalvik/annotation/EnclosingClass; value=Lcom/google/android/test/R; + VISIBILITY_SYSTEM Ldalvik/annotation/InnerClass; accessFlags=25 name="attr" + Class #3 - Class descriptor : 'Lcom/google/android/test/R$attr;' Access flags : 0x0011 (PUBLIC FINAL) @@ -186,6 +201,11 @@ instance_fields_size: 0 direct_methods_size : 1 virtual_methods_size: 0 +Class #4 annotations: +Annotations on class + VISIBILITY_SYSTEM Ldalvik/annotation/EnclosingClass; value=Lcom/google/android/test/R; + VISIBILITY_SYSTEM Ldalvik/annotation/InnerClass; accessFlags=25 name="drawable" + Class #4 - Class descriptor : 'Lcom/google/android/test/R$drawable;' Access flags : 0x0011 (PUBLIC FINAL) @@ -233,6 +253,10 @@ instance_fields_size: 0 direct_methods_size : 1 virtual_methods_size: 0 +Class #5 annotations: +Annotations on class + VISIBILITY_SYSTEM Ldalvik/annotation/MemberClasses; value={ Lcom/google/android/test/R$attr; Lcom/google/android/test/R$drawable; } + Class #5 - Class descriptor : 'Lcom/google/android/test/R;' Access flags : 0x0011 (PUBLIC FINAL) @@ -275,6 +299,10 @@ instance_fields_size: 20 direct_methods_size : 13 virtual_methods_size: 2 +Class #6 annotations: +Annotations on method #13 'doit' + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Exception; } + Class #6 - Class descriptor : 'Lcom/google/android/test/Test;' Access flags : 0x0001 (PUBLIC) @@ -418,17 +446,17 @@ Class #6 - 000a02: 6a00 1800 |0001: sput-boolean v0, Lcom/google/android/test/Test;.sBool:Z // field@0018 000a06: 1300 1f00 |0003: const/16 v0, #int 31 // #1f 000a0a: 6b00 1700 |0005: sput-byte v0, Lcom/google/android/test/Test;.sB:B // field@0017 -000a0e: 1400 ffff 0000 |0007: const v0, #float 0.000000 // #0000ffff +000a0e: 1400 ffff 0000 |0007: const v0, #float 9.18341e-41 // #0000ffff 000a14: 6c00 1900 |000a: sput-char v0, Lcom/google/android/test/Test;.sC:C // field@0019 000a18: 1300 3412 |000c: const/16 v0, #int 4660 // #1234 000a1c: 6d00 1f00 |000e: sput-short v0, Lcom/google/android/test/Test;.sS:S // field@001f -000a20: 1400 7856 3412 |0010: const v0, #float 0.000000 // #12345678 +000a20: 1400 7856 3412 |0010: const v0, #float 5.69046e-28 // #12345678 000a26: 6700 1c00 |0013: sput v0, Lcom/google/android/test/Test;.sI:I // field@001c -000a2a: 1800 ffff cdab 7956 3412 |0015: const-wide v0, #double 0.000000 // #12345679abcdffff +000a2a: 1800 ffff cdab 7956 3412 |0015: const-wide v0, #double 5.62635e-221 // #12345679abcdffff 000a34: 6800 1d00 |001a: sput-wide v0, Lcom/google/android/test/Test;.sL:J // field@001d -000a38: 1400 00e4 4046 |001c: const v0, #float 12345.000000 // #4640e400 +000a38: 1400 00e4 4046 |001c: const v0, #float 12345 // #4640e400 000a3e: 6700 1b00 |001f: sput v0, Lcom/google/android/test/Test;.sF:F // field@001b -000a42: 1800 0000 0000 801c c840 |0021: const-wide v0, #double 12345.000000 // #40c81c8000000000 +000a42: 1800 0000 0000 801c c840 |0021: const-wide v0, #double 12345 // #40c81c8000000000 000a4c: 6800 1a00 |0026: sput-wide v0, Lcom/google/android/test/Test;.sD:D // field@001a 000a50: 1200 |0028: const/4 v0, #int 0 // #0 000a52: 6900 1e00 |0029: sput-object v0, Lcom/google/android/test/Test;.sO:Ljava/lang/Object; // field@001e @@ -471,17 +499,17 @@ Class #6 - 000ab4: 5c81 0d00 |0008: iput-boolean v1, v8, Lcom/google/android/test/Test;.mBool:Z // field@000d 000ab8: 1301 1f00 |000a: const/16 v1, #int 31 // #1f 000abc: 5d81 0c00 |000c: iput-byte v1, v8, Lcom/google/android/test/Test;.mB:B // field@000c -000ac0: 1401 ffff 0000 |000e: const v1, #float 0.000000 // #0000ffff +000ac0: 1401 ffff 0000 |000e: const v1, #float 9.18341e-41 // #0000ffff 000ac6: 5e81 0e00 |0011: iput-char v1, v8, Lcom/google/android/test/Test;.mC:C // field@000e 000aca: 1301 3412 |0013: const/16 v1, #int 4660 // #1234 000ace: 5f81 1500 |0015: iput-short v1, v8, Lcom/google/android/test/Test;.mS:S // field@0015 -000ad2: 1401 7856 3412 |0017: const v1, #float 0.000000 // #12345678 +000ad2: 1401 7856 3412 |0017: const v1, #float 5.69046e-28 // #12345678 000ad8: 5981 1100 |001a: iput v1, v8, Lcom/google/android/test/Test;.mI:I // field@0011 -000adc: 1802 ffff cdab 7956 3412 |001c: const-wide v2, #double 0.000000 // #12345679abcdffff +000adc: 1802 ffff cdab 7956 3412 |001c: const-wide v2, #double 5.62635e-221 // #12345679abcdffff 000ae6: 5a82 1200 |0021: iput-wide v2, v8, Lcom/google/android/test/Test;.mL:J // field@0012 -000aea: 1401 00e4 4046 |0023: const v1, #float 12345.000000 // #4640e400 +000aea: 1401 00e4 4046 |0023: const v1, #float 12345 // #4640e400 000af0: 5981 1000 |0026: iput v1, v8, Lcom/google/android/test/Test;.mF:F // field@0010 -000af4: 1802 0000 0000 801c c840 |0028: const-wide v2, #double 12345.000000 // #40c81c8000000000 +000af4: 1802 0000 0000 801c c840 |0028: const-wide v2, #double 12345 // #40c81c8000000000 000afe: 5a82 0f00 |002d: iput-wide v2, v8, Lcom/google/android/test/Test;.mD:D // field@000f 000b02: 1201 |002f: const/4 v1, #int 0 // #0 000b04: 5b81 1300 |0030: iput-object v1, v8, Lcom/google/android/test/Test;.mO:Ljava/lang/Object; // field@0013 @@ -626,7 +654,7 @@ Class #6 - 000cc6: 8d00 |0011: int-to-byte v0, v0 000cc8: 5db0 0c00 |0012: iput-byte v0, v11, Lcom/google/android/test/Test;.mB:B // field@000c 000ccc: 57b0 0e00 |0014: iget-char v0, v11, Lcom/google/android/test/Test;.mC:C // field@000e -000cd0: 1401 ffff 0000 |0016: const v1, #float 0.000000 // #0000ffff +000cd0: 1401 ffff 0000 |0016: const v1, #float 9.18341e-41 // #0000ffff 000cd6: b010 |0019: add-int/2addr v0, v1 000cd8: 8e00 |001a: int-to-char v0, v0 000cda: 5eb0 0e00 |001b: iput-char v0, v11, Lcom/google/android/test/Test;.mC:C // field@000e @@ -635,7 +663,7 @@ Class #6 - 000ce6: 8f00 |0021: int-to-short v0, v0 000ce8: 5fb0 1500 |0022: iput-short v0, v11, Lcom/google/android/test/Test;.mS:S // field@0015 000cec: 52b0 1100 |0024: iget v0, v11, Lcom/google/android/test/Test;.mI:I // field@0011 -000cf0: 1401 7856 3412 |0026: const v1, #float 0.000000 // #12345678 +000cf0: 1401 7856 3412 |0026: const v1, #float 5.69046e-28 // #12345678 000cf6: b010 |0029: add-int/2addr v0, v1 000cf8: 59b0 1100 |002a: iput v0, v11, Lcom/google/android/test/Test;.mI:I // field@0011 000cfc: 52b0 1100 |002c: iget v0, v11, Lcom/google/android/test/Test;.mI:I // field@0011 @@ -643,7 +671,7 @@ Class #6 - 000d04: b010 |0030: add-int/2addr v0, v1 000d06: 59b0 1100 |0031: iput v0, v11, Lcom/google/android/test/Test;.mI:I // field@0011 000d0a: 53b0 1200 |0033: iget-wide v0, v11, Lcom/google/android/test/Test;.mL:J // field@0012 -000d0e: 1802 ffff cdab 7956 3412 |0035: const-wide v2, #double 0.000000 // #12345679abcdffff +000d0e: 1802 ffff cdab 7956 3412 |0035: const-wide v2, #double 5.62635e-221 // #12345679abcdffff 000d18: bb20 |003a: add-long/2addr v0, v2 000d1a: 5ab0 1200 |003b: iput-wide v0, v11, Lcom/google/android/test/Test;.mL:J // field@0012 000d1e: 53b0 1200 |003d: iget-wide v0, v11, Lcom/google/android/test/Test;.mL:J // field@0012 @@ -651,7 +679,7 @@ Class #6 - 000d26: bb20 |0041: add-long/2addr v0, v2 000d28: 5ab0 1200 |0042: iput-wide v0, v11, Lcom/google/android/test/Test;.mL:J // field@0012 000d2c: 52b0 1000 |0044: iget v0, v11, Lcom/google/android/test/Test;.mF:F // field@0010 -000d30: 1401 00e4 4046 |0046: const v1, #float 12345.000000 // #4640e400 +000d30: 1401 00e4 4046 |0046: const v1, #float 12345 // #4640e400 000d36: 52b2 1000 |0049: iget v2, v11, Lcom/google/android/test/Test;.mF:F // field@0010 000d3a: 1503 803f |004b: const/high16 v3, #int 1065353216 // #3f80 000d3e: c732 |004d: sub-float/2addr v2, v3 @@ -664,7 +692,7 @@ Class #6 - 000d50: c610 |0056: add-float/2addr v0, v1 000d52: 59b0 1000 |0057: iput v0, v11, Lcom/google/android/test/Test;.mF:F // field@0010 000d56: 53b0 0f00 |0059: iget-wide v0, v11, Lcom/google/android/test/Test;.mD:D // field@000f -000d5a: 1802 0000 0000 801c c840 |005b: const-wide v2, #double 12345.000000 // #40c81c8000000000 +000d5a: 1802 0000 0000 801c c840 |005b: const-wide v2, #double 12345 // #40c81c8000000000 000d64: 53b4 0f00 |0060: iget-wide v4, v11, Lcom/google/android/test/Test;.mD:D // field@000f 000d68: 1906 f03f |0062: const-wide/high16 v6, #long 4607182418800017408 // #3ff0 000d6c: cc64 |0064: sub-double/2addr v4, v6 @@ -681,7 +709,7 @@ Class #6 - 000d8a: 2d00 0001 |0073: cmpl-float v0, v0, v1 000d8e: 3800 2900 |0075: if-eqz v0, 009e // +0029 000d92: 52b0 1000 |0077: iget v0, v11, Lcom/google/android/test/Test;.mF:F // field@0010 -000d96: 1401 9a99 993e |0079: const v1, #float 0.300000 // #3e99999a +000d96: 1401 9a99 993e |0079: const v1, #float 0.3 // #3e99999a 000d9c: 2d00 0001 |007c: cmpl-float v0, v0, v1 000da0: 3900 2000 |007e: if-nez v0, 009e // +0020 000da4: 52b0 1000 |0080: iget v0, v11, Lcom/google/android/test/Test;.mF:F // field@0010 @@ -707,7 +735,7 @@ Class #6 - 000df2: 2f00 0002 |00a7: cmpl-double v0, v0, v2 000df6: 3800 2b00 |00a9: if-eqz v0, 00d4 // +002b 000dfa: 53b0 0f00 |00ab: iget-wide v0, v11, Lcom/google/android/test/Test;.mD:D // field@000f -000dfe: 1802 3333 3333 3333 d33f |00ad: const-wide v2, #double 0.300000 // #3fd3333333333333 +000dfe: 1802 3333 3333 3333 d33f |00ad: const-wide v2, #double 0.3 // #3fd3333333333333 000e08: 2f00 0002 |00b2: cmpl-double v0, v0, v2 000e0c: 3900 2000 |00b4: if-nez v0, 00d4 // +0020 000e10: 53b0 0f00 |00b6: iget-wide v0, v11, Lcom/google/android/test/Test;.mD:D // field@000f @@ -790,7 +818,7 @@ Class #6 - 000eb8: 8d00 |000c: int-to-byte v0, v0 000eba: 6b00 1700 |000d: sput-byte v0, Lcom/google/android/test/Test;.sB:B // field@0017 000ebe: 6500 1900 |000f: sget-char v0, Lcom/google/android/test/Test;.sC:C // field@0019 -000ec2: 1401 ffff 0000 |0011: const v1, #float 0.000000 // #0000ffff +000ec2: 1401 ffff 0000 |0011: const v1, #float 9.18341e-41 // #0000ffff 000ec8: b010 |0014: add-int/2addr v0, v1 000eca: 8e00 |0015: int-to-char v0, v0 000ecc: 6c00 1900 |0016: sput-char v0, Lcom/google/android/test/Test;.sC:C // field@0019 @@ -799,7 +827,7 @@ Class #6 - 000ed8: 8f00 |001c: int-to-short v0, v0 000eda: 6d00 1f00 |001d: sput-short v0, Lcom/google/android/test/Test;.sS:S // field@001f 000ede: 6000 1c00 |001f: sget v0, Lcom/google/android/test/Test;.sI:I // field@001c -000ee2: 1401 7856 3412 |0021: const v1, #float 0.000000 // #12345678 +000ee2: 1401 7856 3412 |0021: const v1, #float 5.69046e-28 // #12345678 000ee8: b010 |0024: add-int/2addr v0, v1 000eea: 6700 1c00 |0025: sput v0, Lcom/google/android/test/Test;.sI:I // field@001c 000eee: 6000 1c00 |0027: sget v0, Lcom/google/android/test/Test;.sI:I // field@001c @@ -807,7 +835,7 @@ Class #6 - 000ef6: b010 |002b: add-int/2addr v0, v1 000ef8: 6700 1c00 |002c: sput v0, Lcom/google/android/test/Test;.sI:I // field@001c 000efc: 6100 1d00 |002e: sget-wide v0, Lcom/google/android/test/Test;.sL:J // field@001d -000f00: 1802 ffff cdab 7956 3412 |0030: const-wide v2, #double 0.000000 // #12345679abcdffff +000f00: 1802 ffff cdab 7956 3412 |0030: const-wide v2, #double 5.62635e-221 // #12345679abcdffff 000f0a: bb20 |0035: add-long/2addr v0, v2 000f0c: 6800 1d00 |0036: sput-wide v0, Lcom/google/android/test/Test;.sL:J // field@001d 000f10: 6100 1d00 |0038: sget-wide v0, Lcom/google/android/test/Test;.sL:J // field@001d @@ -815,7 +843,7 @@ Class #6 - 000f18: bb20 |003c: add-long/2addr v0, v2 000f1a: 6800 1d00 |003d: sput-wide v0, Lcom/google/android/test/Test;.sL:J // field@001d 000f1e: 6000 1b00 |003f: sget v0, Lcom/google/android/test/Test;.sF:F // field@001b -000f22: 1401 00e4 4046 |0041: const v1, #float 12345.000000 // #4640e400 +000f22: 1401 00e4 4046 |0041: const v1, #float 12345 // #4640e400 000f28: 6002 1b00 |0044: sget v2, Lcom/google/android/test/Test;.sF:F // field@001b 000f2c: 7f22 |0046: neg-float v2, v2 000f2e: 1503 803f |0047: const/high16 v3, #int 1065353216 // #3f80 @@ -830,7 +858,7 @@ Class #6 - 000f48: c610 |0054: add-float/2addr v0, v1 000f4a: 6700 1b00 |0055: sput v0, Lcom/google/android/test/Test;.sF:F // field@001b 000f4e: 6100 1a00 |0057: sget-wide v0, Lcom/google/android/test/Test;.sD:D // field@001a -000f52: 1802 0000 0000 801c c840 |0059: const-wide v2, #double 12345.000000 // #40c81c8000000000 +000f52: 1802 0000 0000 801c c840 |0059: const-wide v2, #double 12345 // #40c81c8000000000 000f5c: 6104 1a00 |005e: sget-wide v4, Lcom/google/android/test/Test;.sD:D // field@001a 000f60: 8044 |0060: neg-double v4, v4 000f62: 1906 f03f |0061: const-wide/high16 v6, #long 4607182418800017408 // #3ff0 diff --git a/test/dexdump/checkers.txt b/test/dexdump/checkers.txt index 5c8336f94c..aee6e647f7 100755..100644 --- a/test/dexdump/checkers.txt +++ b/test/dexdump/checkers.txt @@ -12,8 +12,8 @@ string_ids_size : 323 string_ids_off : 112 (0x000070) type_ids_size : 58 type_ids_off : 1404 (0x00057c) -proto_ids_size : 88 -proto_ids_off : 1636 (0x000664) +proto_ids_size : 88 +proto_ids_off : 1636 (0x000664) field_ids_size : 108 field_ids_off : 2692 (0x000a84) method_ids_size : 177 @@ -836,7 +836,7 @@ Class #1 - 001c8a: 1300 5829 |0147: const/16 v0, #int 10584 // #2958 001c8e: 2300 3600 |0149: new-array v0, v0, [B // type@0036 001c92: 6900 6800 |014b: sput-object v0, Lcom/google/android/checkers/g;.p:[B // field@0068 -001c96: 1400 00c1 0300 |014d: const v0, #float 0.000000 // #0003c100 +001c96: 1400 00c1 0300 |014d: const v0, #float 3.44742e-40 // #0003c100 001c9c: 2300 3600 |0150: new-array v0, v0, [B // type@0036 001ca0: 6900 6900 |0152: sput-object v0, Lcom/google/android/checkers/g;.q:[B // field@0069 001ca4: 6e10 1100 0a00 |0154: invoke-virtual {v10}, Landroid/content/Context;.getResources:()Landroid/content/res/Resources; // method@0011 @@ -2044,7 +2044,7 @@ goto options to rotate board" // string@00c2 002bd0: 5433 3b00 |004c: iget-object v3, v3, Lcom/google/android/checkers/a;.b:[I // field@003b 002bd4: 4403 0309 |004e: aget v3, v3, v9 002bd8: 5983 2a00 |0050: iput v3, v8, Lcom/google/android/checkers/CheckersView;.x:I // field@002a -002bdc: 1403 6666 663f |0052: const v3, #float 0.900000 // #3f666666 +002bdc: 1403 6666 663f |0052: const v3, #float 0.9 // #3f666666 002be2: 5983 1e00 |0055: iput v3, v8, Lcom/google/android/checkers/CheckersView;.l:F // field@001e 002be6: 3800 4500 |0057: if-eqz v0, 009c // +0045 002bea: 5483 2200 |0059: iget-object v3, v8, Lcom/google/android/checkers/CheckersView;.p:Lcom/google/android/checkers/a; // field@0022 @@ -2943,7 +2943,7 @@ goto options to rotate board" // string@00c2 0036f2: 0800 1c00 |0197: move-object/from16 v0, v28 0036f6: 5202 1e00 |0199: iget v2, v0, Lcom/google/android/checkers/CheckersView;.l:F // field@001e 0036fa: 8922 |019b: float-to-double v2, v2 -0036fc: 1804 9a99 9999 9999 a93f |019c: const-wide v4, #double 0.050000 // #3fa999999999999a +0036fc: 1804 9a99 9999 9999 a93f |019c: const-wide v4, #double 0.05 // #3fa999999999999a 003706: cc42 |01a1: sub-double/2addr v2, v4 003708: 8c22 |01a2: double-to-float v2, v2 00370a: 0800 1c00 |01a3: move-object/from16 v0, v28 @@ -3568,7 +3568,7 @@ goto options to rotate board" // string@00c2 003f38: 28e9 |001e: goto 0007 // -0017 003f3a: 1300 3075 |001f: const/16 v0, #int 30000 // #7530 003f3e: 28e6 |0021: goto 0007 // -001a -003f40: 1400 60ea 0000 |0022: const v0, #float 0.000000 // #0000ea60 +003f40: 1400 60ea 0000 |0022: const v0, #float 8.40779e-41 // #0000ea60 003f46: 28e2 |0025: goto 0007 // -001e 003f48: 0d00 |0026: move-exception v0 003f4a: 1e02 |0027: monitor-exit v2 @@ -3811,7 +3811,7 @@ Class #2 - 004024: 1302 0040 |0046: const/16 v2, #int 16384 // #4000 004028: 4b02 0001 |0048: aput v2, v0, v1 00402c: 1301 1300 |004a: const/16 v1, #int 19 // #13 -004030: 1402 0080 0000 |004c: const v2, #float 0.000000 // #00008000 +004030: 1402 0080 0000 |004c: const v2, #float 4.59177e-41 // #00008000 004036: 4b02 0001 |004f: aput v2, v0, v1 00403a: 1501 0100 |0051: const/high16 v1, #int 65536 // #1 00403e: 4b01 0006 |0053: aput v1, v0, v6 @@ -3931,7 +3931,7 @@ Class #2 - 0041f6: 1302 0040 |012f: const/16 v2, #int 16384 // #4000 0041fa: 4b02 0001 |0131: aput v2, v0, v1 0041fe: 1301 1200 |0133: const/16 v1, #int 18 // #12 -004202: 1402 0080 0000 |0135: const v2, #float 0.000000 // #00008000 +004202: 1402 0080 0000 |0135: const v2, #float 4.59177e-41 // #00008000 004208: 4b02 0001 |0138: aput v2, v0, v1 00420c: 1301 1400 |013a: const/16 v1, #int 20 // #14 004210: 1502 0100 |013c: const/high16 v2, #int 65536 // #1 @@ -3996,7 +3996,7 @@ Class #2 - 0042fa: 1301 0040 |01b1: const/16 v1, #int 16384 // #4000 0042fe: 4b01 0006 |01b3: aput v1, v0, v6 004302: 1301 1600 |01b5: const/16 v1, #int 22 // #16 -004306: 1402 0080 0000 |01b7: const v2, #float 0.000000 // #00008000 +004306: 1402 0080 0000 |01b7: const v2, #float 4.59177e-41 // #00008000 00430c: 4b02 0001 |01ba: aput v2, v0, v1 004310: 1301 1800 |01bc: const/16 v1, #int 24 // #18 004314: 1502 0200 |01be: const/high16 v2, #int 131072 // #2 @@ -4045,7 +4045,7 @@ Class #2 - 0043b4: 1301 0040 |020e: const/16 v1, #int 16384 // #4000 0043b8: 4b01 0004 |0210: aput v1, v0, v4 0043bc: 1301 0b00 |0212: const/16 v1, #int 11 // #b -0043c0: 1402 0080 0000 |0214: const v2, #float 0.000000 // #00008000 +0043c0: 1402 0080 0000 |0214: const v2, #float 4.59177e-41 // #00008000 0043c6: 4b02 0001 |0217: aput v2, v0, v1 0043ca: 1301 0d00 |0219: const/16 v1, #int 13 // #d 0043ce: 1502 0100 |021b: const/high16 v2, #int 65536 // #1 @@ -4167,7 +4167,7 @@ Class #2 - 004588: 1301 0900 |02f8: const/16 v1, #int 9 // #9 00458c: 1302 0040 |02fa: const/16 v2, #int 16384 // #4000 004590: 4b02 0001 |02fc: aput v2, v0, v1 -004594: 1401 0080 0000 |02fe: const v1, #float 0.000000 // #00008000 +004594: 1401 0080 0000 |02fe: const v1, #float 4.59177e-41 // #00008000 00459a: 4b01 0004 |0301: aput v1, v0, v4 00459e: 1301 0c00 |0303: const/16 v1, #int 12 // #c 0045a2: 1502 0100 |0305: const/high16 v2, #int 65536 // #1 @@ -4226,7 +4226,7 @@ Class #2 - 00466e: 1302 0040 |036b: const/16 v2, #int 16384 // #4000 004672: 4b02 0001 |036d: aput v2, v0, v1 004676: 1261 |036f: const/4 v1, #int 6 // #6 -004678: 1402 0080 0000 |0370: const v2, #float 0.000000 // #00008000 +004678: 1402 0080 0000 |0370: const v2, #float 4.59177e-41 // #00008000 00467e: 4b02 0001 |0373: aput v2, v0, v1 004682: 1301 0800 |0375: const/16 v1, #int 8 // #8 004686: 1502 0200 |0377: const/high16 v2, #int 131072 // #2 @@ -4496,7 +4496,7 @@ Class #2 - 004c16: 3803 3400 |0047: if-eqz v3, 007b // +0034 004c1a: 0800 1800 |0049: move-object/from16 v0, v24 004c1e: 5203 5100 |004b: iget v3, v0, Lcom/google/android/checkers/a;.x:I // field@0051 -004c22: 1404 ffff 0f00 |004d: const v4, #float 0.000000 // #000fffff +004c22: 1404 ffff 0f00 |004d: const v4, #float 1.46937e-39 // #000fffff 004c28: b534 |0050: and-int/2addr v4, v3 004c2a: 0800 1800 |0051: move-object/from16 v0, v24 004c2e: 5405 5200 |0053: iget-object v5, v0, Lcom/google/android/checkers/a;.y:[I // field@0052 @@ -4516,7 +4516,7 @@ Class #2 - 004c66: 5405 5300 |006f: iget-object v5, v0, Lcom/google/android/checkers/a;.z:[S // field@0053 004c6a: 4a04 0504 |0071: aget-short v4, v5, v4 004c6e: 2c03 8104 0000 |0073: sparse-switch v3, 000004f4 // +00000481 -004c74: 1403 3f42 0f00 |0076: const v3, #float 0.000000 // #000f423f +004c74: 1403 3f42 0f00 |0076: const v3, #float 1.4013e-39 // #000f423f 004c7a: 3334 a1ff |0079: if-ne v4, v3, 001a // -005f 004c7e: 0800 1800 |007b: move-object/from16 v0, v24 004c82: 0201 1b00 |007d: move/from16 v1, v27 @@ -4897,7 +4897,7 @@ Class #2 - 0051da: 28c4 |0329: goto 02ed // -003c 0051dc: 0200 1900 |032a: move/from16 v0, v25 0051e0: 3704 4afd |032c: if-le v4, v0, 0076 // -02b6 -0051e4: 1404 3f42 0f00 |032e: const v4, #float 0.000000 // #000f423f +0051e4: 1404 3f42 0f00 |032e: const v4, #float 1.4013e-39 // #000f423f 0051ea: 2900 45fd |0331: goto/16 0076 // -02bb 0051ee: 0200 1a00 |0333: move/from16 v0, v26 0051f2: 3404 f9ff |0335: if-lt v4, v0, 032e // -0007 @@ -5020,7 +5020,7 @@ Class #2 - 0053a2: 3545 bd00 |040d: if-ge v5, v4, 04ca // +00bd 0053a6: 0800 1800 |040f: move-object/from16 v0, v24 0053aa: 5204 3e00 |0411: iget v4, v0, Lcom/google/android/checkers/a;.e:I // field@003e -0053ae: 1405 1100 0088 |0413: const v5, #float -0.000000 // #88000011 +0053ae: 1405 1100 0088 |0413: const v5, #float -3.85187e-34 // #88000011 0053b4: b554 |0416: and-int/2addr v4, v5 0053b6: 3804 0900 |0417: if-eqz v4, 0420 // +0009 0053ba: 7110 9e00 0400 |0419: invoke-static {v4}, Ljava/lang/Integer;.bitCount:(I)I // method@009e @@ -5052,11 +5052,11 @@ Class #2 - 005418: 1507 00a0 |0448: const/high16 v7, #int -1610612736 // #a000 00541c: 3376 0400 |044a: if-ne v6, v7, 044e // +0004 005420: d803 03f4 |044c: add-int/lit8 v3, v3, #int -12 // #f4 -005424: 1406 0066 6600 |044e: const v6, #float 0.000000 // #00666600 +005424: 1406 0066 6600 |044e: const v6, #float 9.40381e-39 // #00666600 00542a: b564 |0451: and-int/2addr v4, v6 00542c: 7110 9e00 0400 |0452: invoke-static {v4}, Ljava/lang/Integer;.bitCount:(I)I // method@009e 005432: 0a04 |0455: move-result v4 -005434: 1406 0066 6600 |0456: const v6, #float 0.000000 // #00666600 +005434: 1406 0066 6600 |0456: const v6, #float 9.40381e-39 // #00666600 00543a: b565 |0459: and-int/2addr v5, v6 00543c: 7110 9e00 0500 |045a: invoke-static {v5}, Ljava/lang/Integer;.bitCount:(I)I // method@009e 005442: 0a05 |045d: move-result v5 @@ -5064,13 +5064,13 @@ Class #2 - 005446: b043 |045f: add-int/2addr v3, v4 005448: 0800 1800 |0460: move-object/from16 v0, v24 00544c: 5204 3d00 |0462: iget v4, v0, Lcom/google/android/checkers/a;.d:I // field@003d -005450: 1405 1818 1818 |0464: const v5, #float 0.000000 // #18181818 +005450: 1405 1818 1818 |0464: const v5, #float 1.96577e-24 // #18181818 005456: b554 |0467: and-int/2addr v4, v5 005458: 7110 9e00 0400 |0468: invoke-static {v4}, Ljava/lang/Integer;.bitCount:(I)I // method@009e 00545e: 0a04 |046b: move-result v4 005460: 0800 1800 |046c: move-object/from16 v0, v24 005464: 5205 3f00 |046e: iget v5, v0, Lcom/google/android/checkers/a;.f:I // field@003f -005468: 1406 1818 1818 |0470: const v6, #float 0.000000 // #18181818 +005468: 1406 1818 1818 |0470: const v6, #float 1.96577e-24 // #18181818 00546e: b565 |0473: and-int/2addr v5, v6 005470: 7110 9e00 0500 |0474: invoke-static {v5}, Ljava/lang/Integer;.bitCount:(I)I // method@009e 005476: 0a05 |0477: move-result v5 @@ -5078,7 +5078,7 @@ Class #2 - 00547a: b143 |0479: sub-int/2addr v3, v4 00547c: 0800 1800 |047a: move-object/from16 v0, v24 005480: 5204 3e00 |047c: iget v4, v0, Lcom/google/android/checkers/a;.e:I // field@003e -005484: 1405 0800 0010 |047e: const v5, #float 0.000000 // #10000008 +005484: 1405 0800 0010 |047e: const v5, #float 2.52436e-29 // #10000008 00548a: b554 |0481: and-int/2addr v4, v5 00548c: 3804 0900 |0482: if-eqz v4, 048b // +0009 005490: 7110 9e00 0400 |0484: invoke-static {v4}, Ljava/lang/Integer;.bitCount:(I)I // method@009e @@ -5087,7 +5087,7 @@ Class #2 - 00549c: b143 |048a: sub-int/2addr v3, v4 00549e: 0800 1800 |048b: move-object/from16 v0, v24 0054a2: 5204 4000 |048d: iget v4, v0, Lcom/google/android/checkers/a;.g:I // field@0040 -0054a6: 1405 0800 0010 |048f: const v5, #float 0.000000 // #10000008 +0054a6: 1405 0800 0010 |048f: const v5, #float 2.52436e-29 // #10000008 0054ac: b554 |0492: and-int/2addr v4, v5 0054ae: 3804 4c00 |0493: if-eqz v4, 04df // +004c 0054b2: 7110 9e00 0400 |0495: invoke-static {v4}, Ljava/lang/Integer;.bitCount:(I)I // method@009e @@ -5124,7 +5124,7 @@ Class #2 - 00551c: 3745 56ff |04ca: if-le v5, v4, 0420 // -00aa 005520: 0800 1800 |04cc: move-object/from16 v0, v24 005524: 5204 4000 |04ce: iget v4, v0, Lcom/google/android/checkers/a;.g:I // field@0040 -005528: 1405 1100 0088 |04d0: const v5, #float -0.000000 // #88000011 +005528: 1405 1100 0088 |04d0: const v5, #float -3.85187e-34 // #88000011 00552e: b554 |04d3: and-int/2addr v4, v5 005530: 3804 4cff |04d4: if-eqz v4, 0420 // -00b4 005534: 7110 9e00 0400 |04d6: invoke-static {v4}, Ljava/lang/Integer;.bitCount:(I)I // method@009e @@ -5407,7 +5407,7 @@ Class #2 - 005868: 0126 |0010: move v6, v2 00586a: 0135 |0011: move v5, v3 00586c: 5240 5100 |0012: iget v0, v4, Lcom/google/android/checkers/a;.x:I // field@0051 -005870: 1401 ffff 0f00 |0014: const v1, #float 0.000000 // #000fffff +005870: 1401 ffff 0f00 |0014: const v1, #float 1.46937e-39 // #000fffff 005876: b501 |0017: and-int/2addr v1, v0 005878: 5442 5200 |0018: iget-object v2, v4, Lcom/google/android/checkers/a;.y:[I // field@0052 00587c: 4b00 0201 |001a: aput v0, v2, v1 @@ -5568,19 +5568,19 @@ Class #2 - 005a54: e203 1404 |0016: ushr-int/lit8 v3, v20, #int 4 // #04 005a58: b543 |0018: and-int/2addr v3, v4 005a5a: 3803 1200 |0019: if-eqz v3, 002b // +0012 -005a5e: 1401 e0e0 e0e0 |001b: const v1, #float -129633581999069331456.000000 // #e0e0e0e0 +005a5e: 1401 e0e0 e0e0 |001b: const v1, #float -1.29634e+20 // #e0e0e0e0 005a64: b531 |001e: and-int/2addr v1, v3 005a66: e201 0105 |001f: ushr-int/lit8 v1, v1, #int 5 // #05 -005a6a: 1405 0007 0707 |0021: const v5, #float 0.000000 // #07070700 +005a6a: 1405 0007 0707 |0021: const v5, #float 1.01583e-34 // #07070700 005a70: b553 |0024: and-int/2addr v3, v5 005a72: e203 0303 |0025: ushr-int/lit8 v3, v3, #int 3 // #03 005a76: b631 |0027: or-int/2addr v1, v3 005a78: b521 |0028: and-int/2addr v1, v2 005a7a: de01 0100 |0029: or-int/lit8 v1, v1, #int 0 // #00 -005a7e: 1403 e0e0 e0e0 |002b: const v3, #float -129633581999069331456.000000 // #e0e0e0e0 +005a7e: 1403 e0e0 e0e0 |002b: const v3, #float -1.29634e+20 // #e0e0e0e0 005a84: 9503 0314 |002e: and-int v3, v3, v20 005a88: e203 0305 |0030: ushr-int/lit8 v3, v3, #int 5 // #05 -005a8c: 1405 0007 0707 |0032: const v5, #float 0.000000 // #07070700 +005a8c: 1405 0007 0707 |0032: const v5, #float 1.01583e-34 // #07070700 005a92: 9505 0514 |0035: and-int v5, v5, v20 005a96: e205 0503 |0037: ushr-int/lit8 v5, v5, #int 3 // #03 005a9a: b653 |0039: or-int/2addr v3, v5 @@ -5597,19 +5597,19 @@ Class #2 - 005abc: 3802 1500 |004a: if-eqz v2, 005f // +0015 005ac0: 0800 1100 |004c: move-object/from16 v0, v17 005ac4: 5203 3e00 |004e: iget v3, v0, Lcom/google/android/checkers/a;.e:I // field@003e -005ac8: 1405 0707 0707 |0050: const v5, #float 0.000000 // #07070707 +005ac8: 1405 0707 0707 |0050: const v5, #float 1.01583e-34 // #07070707 005ace: b525 |0053: and-int/2addr v5, v2 005ad0: e005 0505 |0054: shl-int/lit8 v5, v5, #int 5 // #05 -005ad4: 1406 e0e0 e000 |0056: const v6, #float 0.000000 // #00e0e0e0 +005ad4: 1406 e0e0 e000 |0056: const v6, #float 2.06518e-38 // #00e0e0e0 005ada: b562 |0059: and-int/2addr v2, v6 005adc: e002 0203 |005a: shl-int/lit8 v2, v2, #int 3 // #03 005ae0: b652 |005c: or-int/2addr v2, v5 005ae2: b532 |005d: and-int/2addr v2, v3 005ae4: b621 |005e: or-int/2addr v1, v2 -005ae6: 1402 0707 0707 |005f: const v2, #float 0.000000 // #07070707 +005ae6: 1402 0707 0707 |005f: const v2, #float 1.01583e-34 // #07070707 005aec: 9502 0214 |0062: and-int v2, v2, v20 005af0: e002 0205 |0064: shl-int/lit8 v2, v2, #int 5 // #05 -005af4: 1403 e0e0 e000 |0066: const v3, #float 0.000000 // #00e0e0e0 +005af4: 1403 e0e0 e000 |0066: const v3, #float 2.06518e-38 // #00e0e0e0 005afa: 9503 0314 |0069: and-int v3, v3, v20 005afe: e003 0303 |006b: shl-int/lit8 v3, v3, #int 3 // #03 005b02: b632 |006d: or-int/2addr v2, v3 @@ -5802,19 +5802,19 @@ Class #2 - 005dd4: 3803 1600 |01d6: if-eqz v3, 01ec // +0016 005dd8: 0800 1100 |01d8: move-object/from16 v0, v17 005ddc: 5201 4000 |01da: iget v1, v0, Lcom/google/android/checkers/a;.g:I // field@0040 -005de0: 1405 e0e0 e0e0 |01dc: const v5, #float -129633581999069331456.000000 // #e0e0e0e0 +005de0: 1405 e0e0 e0e0 |01dc: const v5, #float -1.29634e+20 // #e0e0e0e0 005de6: b535 |01df: and-int/2addr v5, v3 005de8: e205 0505 |01e0: ushr-int/lit8 v5, v5, #int 5 // #05 -005dec: 1406 0007 0707 |01e2: const v6, #float 0.000000 // #07070700 +005dec: 1406 0007 0707 |01e2: const v6, #float 1.01583e-34 // #07070700 005df2: b563 |01e5: and-int/2addr v3, v6 005df4: e203 0303 |01e6: ushr-int/lit8 v3, v3, #int 3 // #03 005df8: b653 |01e8: or-int/2addr v3, v5 005dfa: b531 |01e9: and-int/2addr v1, v3 005dfc: de01 0100 |01ea: or-int/lit8 v1, v1, #int 0 // #00 -005e00: 1403 e0e0 e0e0 |01ec: const v3, #float -129633581999069331456.000000 // #e0e0e0e0 +005e00: 1403 e0e0 e0e0 |01ec: const v3, #float -1.29634e+20 // #e0e0e0e0 005e06: 9503 0314 |01ef: and-int v3, v3, v20 005e0a: e203 0305 |01f1: ushr-int/lit8 v3, v3, #int 5 // #05 -005e0e: 1405 0007 0707 |01f3: const v5, #float 0.000000 // #07070700 +005e0e: 1405 0007 0707 |01f3: const v5, #float 1.01583e-34 // #07070700 005e14: 9505 0514 |01f6: and-int v5, v5, v20 005e18: e205 0503 |01f8: ushr-int/lit8 v5, v5, #int 3 // #03 005e1c: b653 |01fa: or-int/2addr v3, v5 @@ -5828,19 +5828,19 @@ Class #2 - 005e34: e003 1404 |0206: shl-int/lit8 v3, v20, #int 4 // #04 005e38: b543 |0208: and-int/2addr v3, v4 005e3a: 3803 1100 |0209: if-eqz v3, 021a // +0011 -005e3e: 1405 0707 0707 |020b: const v5, #float 0.000000 // #07070707 +005e3e: 1405 0707 0707 |020b: const v5, #float 1.01583e-34 // #07070707 005e44: b535 |020e: and-int/2addr v5, v3 005e46: e005 0505 |020f: shl-int/lit8 v5, v5, #int 5 // #05 -005e4a: 1406 e0e0 e000 |0211: const v6, #float 0.000000 // #00e0e0e0 +005e4a: 1406 e0e0 e000 |0211: const v6, #float 2.06518e-38 // #00e0e0e0 005e50: b563 |0214: and-int/2addr v3, v6 005e52: e003 0303 |0215: shl-int/lit8 v3, v3, #int 3 // #03 005e56: b653 |0217: or-int/2addr v3, v5 005e58: b523 |0218: and-int/2addr v3, v2 005e5a: b631 |0219: or-int/2addr v1, v3 -005e5c: 1403 0707 0707 |021a: const v3, #float 0.000000 // #07070707 +005e5c: 1403 0707 0707 |021a: const v3, #float 1.01583e-34 // #07070707 005e62: 9503 0314 |021d: and-int v3, v3, v20 005e66: e003 0305 |021f: shl-int/lit8 v3, v3, #int 5 // #05 -005e6a: 1405 e0e0 e000 |0221: const v5, #float 0.000000 // #00e0e0e0 +005e6a: 1405 e0e0 e000 |0221: const v5, #float 2.06518e-38 // #00e0e0e0 005e70: 9505 0514 |0224: and-int v5, v5, v20 005e74: e005 0503 |0226: shl-int/lit8 v5, v5, #int 3 // #03 005e78: b653 |0228: or-int/2addr v3, v5 @@ -6423,9 +6423,9 @@ Class #2 - outs : 6 insns size : 461 16-bit code units 006604: |[006604] com.google.android.checkers.a.b:(IZI)Z -006614: 1404 e0e0 e000 |0000: const v4, #float 0.000000 // #00e0e0e0 +006614: 1404 e0e0 e000 |0000: const v4, #float 2.06518e-38 // #00e0e0e0 00661a: 1216 |0003: const/4 v6, #int 1 // #1 -00661c: 1403 e0e0 e0e0 |0004: const v3, #float -129633581999069331456.000000 // #e0e0e0e0 +00661c: 1403 e0e0 e0e0 |0004: const v3, #float -1.29634e+20 // #e0e0e0e0 006622: 130a 0008 |0007: const/16 v10, #int 2048 // #800 006626: 1309 0002 |0009: const/16 v9, #int 512 // #200 00662a: 380d e400 |000b: if-eqz v13, 00ef // +00e4 @@ -6436,7 +6436,7 @@ Class #2 - 00663e: 9502 0e03 |0015: and-int v2, v14, v3 006642: e202 0205 |0017: ushr-int/lit8 v2, v2, #int 5 // #05 006646: b621 |0019: or-int/2addr v1, v2 -006648: 1402 0007 0707 |001a: const v2, #float 0.000000 // #07070700 +006648: 1402 0007 0707 |001a: const v2, #float 1.01583e-34 // #07070700 00664e: b5e2 |001d: and-int/2addr v2, v14 006650: e202 0203 |001e: ushr-int/lit8 v2, v2, #int 3 // #03 006654: b621 |0020: or-int/2addr v1, v2 @@ -6453,14 +6453,14 @@ Class #2 - 006676: 9502 0e03 |0031: and-int v2, v14, v3 00667a: e202 0205 |0033: ushr-int/lit8 v2, v2, #int 5 // #05 00667e: b621 |0035: or-int/2addr v1, v2 -006680: 1402 0007 0707 |0036: const v2, #float 0.000000 // #07070700 +006680: 1402 0007 0707 |0036: const v2, #float 1.01583e-34 // #07070700 006686: b5e2 |0039: and-int/2addr v2, v14 006688: e202 0203 |003a: ushr-int/lit8 v2, v2, #int 3 // #03 00668c: b621 |003c: or-int/2addr v1, v2 00668e: b510 |003d: and-int/2addr v0, v1 006690: 52b1 3e00 |003e: iget v1, v11, Lcom/google/android/checkers/a;.e:I // field@003e 006694: e002 0e04 |0040: shl-int/lit8 v2, v14, #int 4 // #04 -006698: 1403 0707 0707 |0042: const v3, #float 0.000000 // #07070707 +006698: 1403 0707 0707 |0042: const v3, #float 1.01583e-34 // #07070707 00669e: b5e3 |0045: and-int/2addr v3, v14 0066a0: e003 0305 |0046: shl-int/lit8 v3, v3, #int 5 // #05 0066a4: b632 |0048: or-int/2addr v2, v3 @@ -6563,7 +6563,7 @@ Class #2 - 0067f6: 3900 5400 |00f1: if-nez v0, 0145 // +0054 0067fa: 52b0 3f00 |00f3: iget v0, v11, Lcom/google/android/checkers/a;.f:I // field@003f 0067fe: e001 0e04 |00f5: shl-int/lit8 v1, v14, #int 4 // #04 -006802: 1402 0707 0707 |00f7: const v2, #float 0.000000 // #07070707 +006802: 1402 0707 0707 |00f7: const v2, #float 1.01583e-34 // #07070707 006808: b5e2 |00fa: and-int/2addr v2, v14 00680a: e002 0205 |00fb: shl-int/lit8 v2, v2, #int 5 // #05 00680e: b621 |00fd: or-int/2addr v1, v2 @@ -6611,7 +6611,7 @@ Class #2 - 0068a2: 52b1 3f00 |0147: iget v1, v11, Lcom/google/android/checkers/a;.f:I // field@003f 0068a6: b610 |0149: or-int/2addr v0, v1 0068a8: e001 0e04 |014a: shl-int/lit8 v1, v14, #int 4 // #04 -0068ac: 1402 0707 0707 |014c: const v2, #float 0.000000 // #07070707 +0068ac: 1402 0707 0707 |014c: const v2, #float 1.01583e-34 // #07070707 0068b2: b5e2 |014f: and-int/2addr v2, v14 0068b4: e002 0205 |0150: shl-int/lit8 v2, v2, #int 5 // #05 0068b8: b621 |0152: or-int/2addr v1, v2 @@ -6624,7 +6624,7 @@ Class #2 - 0068ce: b5e3 |015d: and-int/2addr v3, v14 0068d0: e203 0305 |015e: ushr-int/lit8 v3, v3, #int 5 // #05 0068d4: b632 |0160: or-int/2addr v2, v3 -0068d6: 1403 0007 0707 |0161: const v3, #float 0.000000 // #07070700 +0068d6: 1403 0007 0707 |0161: const v3, #float 1.01583e-34 // #07070700 0068dc: b5e3 |0164: and-int/2addr v3, v14 0068de: e203 0303 |0165: ushr-int/lit8 v3, v3, #int 3 // #03 0068e2: b632 |0167: or-int/2addr v2, v3 diff --git a/test/dexdump/run-all-tests b/test/dexdump/run-all-tests index 11ab55ae30..c9976cd090 100755 --- a/test/dexdump/run-all-tests +++ b/test/dexdump/run-all-tests @@ -39,7 +39,7 @@ mkdir ${tmpdir} # Set up dexdump binary and flags to test. DEXD="${ANDROID_HOST_OUT}/bin/dexdump2" -DEXDFLAGS1="-dfh" +DEXDFLAGS1="-adfh" DEXDFLAGS2="-e -l xml" # Set up dexlist binary and flags to test. diff --git a/test/dexdump/staticfields.txt b/test/dexdump/staticfields.txt index 022605f90d..f6d8f1924f 100644 --- a/test/dexdump/staticfields.txt +++ b/test/dexdump/staticfields.txt @@ -12,8 +12,8 @@ string_ids_size : 28 string_ids_off : 112 (0x000070) type_ids_size : 12 type_ids_off : 224 (0x0000e0) -proto_ids_size : 1 -proto_ids_off : 272 (0x000110) +proto_ids_size : 1 +proto_ids_off : 272 (0x000110) field_ids_size : 12 field_ids_off : 284 (0x00011c) method_ids_size : 2 @@ -71,12 +71,12 @@ Class #0 - name : 'test05_public_static_final_float_46_47' type : 'F' access : 0x0019 (PUBLIC STATIC FINAL) - value : 46.470001 + value : 46.47 #6 : (in LStaticFields;) name : 'test06_public_static_final_double_48_49' type : 'D' access : 0x0019 (PUBLIC STATIC FINAL) - value : 48.490000 + value : 48.49 #7 : (in LStaticFields;) name : 'test07_public_static_final_string' type : 'Ljava/lang/String;' diff --git a/test/dexdump/staticfields.xml b/test/dexdump/staticfields.xml index c906f0a3dd..9082f0ef4d 100644 --- a/test/dexdump/staticfields.xml +++ b/test/dexdump/staticfields.xml @@ -66,7 +66,7 @@ static="true" final="true" visibility="public" - value="46.470001" + value="46.47" > </field> <field name="test06_public_static_final_double_48_49" @@ -76,7 +76,7 @@ static="true" final="true" visibility="public" - value="48.490000" + value="48.49" > </field> <field name="test07_public_static_final_string" @@ -86,7 +86,7 @@ static="true" final="true" visibility="public" - value="abc \><"'&	
" + value="abc \><"'&	
" > </field> <field name="test08_public_static_final_object_null" diff --git a/test/dexdump/values.dex b/test/dexdump/values.dex Binary files differnew file mode 100644 index 0000000000..84602d03c2 --- /dev/null +++ b/test/dexdump/values.dex diff --git a/test/dexdump/values.lst b/test/dexdump/values.lst new file mode 100644 index 0000000000..0dbe3a9ae9 --- /dev/null +++ b/test/dexdump/values.lst @@ -0,0 +1,3 @@ +#values.dex +0x000003bc 8 Test <clinit> ()V Test.java 66 +0x000003d4 8 Test <init> ()V Test.java 1 diff --git a/test/dexdump/values.txt b/test/dexdump/values.txt new file mode 100644 index 0000000000..7f831b17b8 --- /dev/null +++ b/test/dexdump/values.txt @@ -0,0 +1,355 @@ +Processing 'values.dex'... +Opened 'values.dex', DEX version '035' +DEX file header: +magic : 'dex\n035\0' +checksum : 7605eec0 +signature : c197...a065 +file_size : 1864 +header_size : 112 +link_size : 0 +link_off : 0 (0x000000) +string_ids_size : 70 +string_ids_off : 112 (0x000070) +type_ids_size : 12 +type_ids_off : 392 (0x000188) +proto_ids_size : 1 +proto_ids_off : 440 (0x0001b8) +field_ids_size : 54 +field_ids_off : 452 (0x0001c4) +method_ids_size : 3 +method_ids_off : 884 (0x000374) +class_defs_size : 1 +class_defs_off : 908 (0x00038c) +data_size : 924 +data_off : 940 (0x0003ac) + +Class #0 header: +class_idx : 6 +access_flags : 1 (0x0001) +superclass_idx : 7 +interfaces_off : 0 (0x000000) +source_file_idx : 13 +annotations_off : 0 (0x000000) +class_data_off : 1578 (0x00062a) +static_fields_size : 54 +instance_fields_size: 0 +direct_methods_size : 2 +virtual_methods_size: 0 + +Class #0 - + Class descriptor : 'LTest;' + Access flags : 0x0001 (PUBLIC) + Superclass : 'Ljava/lang/Object;' + Interfaces - + Static fields - + #0 : (in LTest;) + name : 'mB0' + type : 'B' + access : 0x0019 (PUBLIC STATIC FINAL) + value : 0 + #1 : (in LTest;) + name : 'mB1' + type : 'B' + access : 0x0019 (PUBLIC STATIC FINAL) + value : 127 + #2 : (in LTest;) + name : 'mB2' + type : 'B' + access : 0x0019 (PUBLIC STATIC FINAL) + value : -128 + #3 : (in LTest;) + name : 'mB3' + type : 'B' + access : 0x0019 (PUBLIC STATIC FINAL) + value : -1 + #4 : (in LTest;) + name : 'mC0' + type : 'C' + access : 0x0019 (PUBLIC STATIC FINAL) + value : 0 + #5 : (in LTest;) + name : 'mC1' + type : 'C' + access : 0x0019 (PUBLIC STATIC FINAL) + value : 32767 + #6 : (in LTest;) + name : 'mC2' + type : 'C' + access : 0x0019 (PUBLIC STATIC FINAL) + value : 32768 + #7 : (in LTest;) + name : 'mC3' + type : 'C' + access : 0x0019 (PUBLIC STATIC FINAL) + value : 255 + #8 : (in LTest;) + name : 'mC4' + type : 'C' + access : 0x0019 (PUBLIC STATIC FINAL) + value : 65520 + #9 : (in LTest;) + name : 'mC5' + type : 'C' + access : 0x0019 (PUBLIC STATIC FINAL) + value : 65535 + #10 : (in LTest;) + name : 'mD0' + type : 'D' + access : 0x0019 (PUBLIC STATIC FINAL) + value : -inf + #11 : (in LTest;) + name : 'mD1' + type : 'D' + access : 0x0019 (PUBLIC STATIC FINAL) + value : 4.94066e-324 + #12 : (in LTest;) + name : 'mD2' + type : 'D' + access : 0x0019 (PUBLIC STATIC FINAL) + value : -0 + #13 : (in LTest;) + name : 'mD3' + type : 'D' + access : 0x0019 (PUBLIC STATIC FINAL) + value : 0 + #14 : (in LTest;) + name : 'mD4' + type : 'D' + access : 0x0019 (PUBLIC STATIC FINAL) + value : 1.79769e+308 + #15 : (in LTest;) + name : 'mD5' + type : 'D' + access : 0x0019 (PUBLIC STATIC FINAL) + value : inf + #16 : (in LTest;) + name : 'mD6' + type : 'D' + access : 0x0019 (PUBLIC STATIC FINAL) + value : nan + #17 : (in LTest;) + name : 'mF0' + type : 'F' + access : 0x0019 (PUBLIC STATIC FINAL) + value : -inf + #18 : (in LTest;) + name : 'mF1' + type : 'F' + access : 0x0019 (PUBLIC STATIC FINAL) + value : 1.4013e-45 + #19 : (in LTest;) + name : 'mF2' + type : 'F' + access : 0x0019 (PUBLIC STATIC FINAL) + value : -0 + #20 : (in LTest;) + name : 'mF3' + type : 'F' + access : 0x0019 (PUBLIC STATIC FINAL) + value : 0 + #21 : (in LTest;) + name : 'mF4' + type : 'F' + access : 0x0019 (PUBLIC STATIC FINAL) + value : 3.40282e+38 + #22 : (in LTest;) + name : 'mF5' + type : 'F' + access : 0x0019 (PUBLIC STATIC FINAL) + value : inf + #23 : (in LTest;) + name : 'mF6' + type : 'F' + access : 0x0019 (PUBLIC STATIC FINAL) + value : nan + #24 : (in LTest;) + name : 'mI0' + type : 'I' + access : 0x0019 (PUBLIC STATIC FINAL) + value : 0 + #25 : (in LTest;) + name : 'mI1' + type : 'I' + access : 0x0019 (PUBLIC STATIC FINAL) + value : 2147483647 + #26 : (in LTest;) + name : 'mI2' + type : 'I' + access : 0x0019 (PUBLIC STATIC FINAL) + value : -2147483648 + #27 : (in LTest;) + name : 'mI3' + type : 'I' + access : 0x0019 (PUBLIC STATIC FINAL) + value : 255 + #28 : (in LTest;) + name : 'mI4' + type : 'I' + access : 0x0019 (PUBLIC STATIC FINAL) + value : -16 + #29 : (in LTest;) + name : 'mI5' + type : 'I' + access : 0x0019 (PUBLIC STATIC FINAL) + value : -4096 + #30 : (in LTest;) + name : 'mI6' + type : 'I' + access : 0x0019 (PUBLIC STATIC FINAL) + value : -1048576 + #31 : (in LTest;) + name : 'mI7' + type : 'I' + access : 0x0019 (PUBLIC STATIC FINAL) + value : -1 + #32 : (in LTest;) + name : 'mJ0' + type : 'J' + access : 0x0019 (PUBLIC STATIC FINAL) + value : 0 + #33 : (in LTest;) + name : 'mJ1' + type : 'J' + access : 0x0019 (PUBLIC STATIC FINAL) + value : 9223372036854775807 + #34 : (in LTest;) + name : 'mJ2' + type : 'J' + access : 0x0019 (PUBLIC STATIC FINAL) + value : -9223372036854775808 + #35 : (in LTest;) + name : 'mJ3' + type : 'J' + access : 0x0019 (PUBLIC STATIC FINAL) + value : 255 + #36 : (in LTest;) + name : 'mJ4' + type : 'J' + access : 0x0019 (PUBLIC STATIC FINAL) + value : -16 + #37 : (in LTest;) + name : 'mJ5' + type : 'J' + access : 0x0019 (PUBLIC STATIC FINAL) + value : -4096 + #38 : (in LTest;) + name : 'mJ6' + type : 'J' + access : 0x0019 (PUBLIC STATIC FINAL) + value : -1048576 + #39 : (in LTest;) + name : 'mJ7' + type : 'J' + access : 0x0019 (PUBLIC STATIC FINAL) + value : -268435456 + #40 : (in LTest;) + name : 'mJ8' + type : 'J' + access : 0x0019 (PUBLIC STATIC FINAL) + value : -68719476736 + #41 : (in LTest;) + name : 'mJ9' + type : 'J' + access : 0x0019 (PUBLIC STATIC FINAL) + value : -17592186044416 + #42 : (in LTest;) + name : 'mJa' + type : 'J' + access : 0x0019 (PUBLIC STATIC FINAL) + value : -4503599627370496 + #43 : (in LTest;) + name : 'mJb' + type : 'J' + access : 0x0019 (PUBLIC STATIC FINAL) + value : -1 + #44 : (in LTest;) + name : 'mObject' + type : 'Ljava/lang/Object;' + access : 0x0019 (PUBLIC STATIC FINAL) + value : null + #45 : (in LTest;) + name : 'mS0' + type : 'S' + access : 0x0019 (PUBLIC STATIC FINAL) + value : 0 + #46 : (in LTest;) + name : 'mS1' + type : 'S' + access : 0x0019 (PUBLIC STATIC FINAL) + value : 32767 + #47 : (in LTest;) + name : 'mS2' + type : 'S' + access : 0x0019 (PUBLIC STATIC FINAL) + value : -32768 + #48 : (in LTest;) + name : 'mS3' + type : 'S' + access : 0x0019 (PUBLIC STATIC FINAL) + value : 255 + #49 : (in LTest;) + name : 'mS4' + type : 'S' + access : 0x0019 (PUBLIC STATIC FINAL) + value : -16 + #50 : (in LTest;) + name : 'mS5' + type : 'S' + access : 0x0019 (PUBLIC STATIC FINAL) + value : -1 + #51 : (in LTest;) + name : 'mString' + type : 'Ljava/lang/String;' + access : 0x0019 (PUBLIC STATIC FINAL) + value : "<&\"JOHO\"&>\n" + #52 : (in LTest;) + name : 'mZ0' + type : 'Z' + access : 0x0019 (PUBLIC STATIC FINAL) + value : false + #53 : (in LTest;) + name : 'mZ1' + type : 'Z' + access : 0x0019 (PUBLIC STATIC FINAL) + value : true + Instance fields - + Direct methods - + #0 : (in LTest;) + name : '<clinit>' + type : '()V' + access : 0x10008 (STATIC CONSTRUCTOR) + code - + registers : 1 + ins : 0 + outs : 0 + insns size : 4 16-bit code units +0003ac: |[0003ac] Test.<clinit>:()V +0003bc: 1200 |0000: const/4 v0, #int 0 // #0 +0003be: 6900 2c00 |0001: sput-object v0, LTest;.mObject:Ljava/lang/Object; // field@002c +0003c2: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=66 + locals : + + #1 : (in LTest;) + name : '<init>' + type : '()V' + access : 0x10001 (PUBLIC CONSTRUCTOR) + code - + registers : 1 + ins : 1 + outs : 1 + insns size : 4 16-bit code units +0003c4: |[0003c4] Test.<init>:()V +0003d4: 7010 0200 0000 |0000: invoke-direct {v0}, Ljava/lang/Object;.<init>:()V // method@0002 +0003da: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=1 + locals : + 0x0000 - 0x0004 reg=0 this LTest; + + Virtual methods - + source_file_idx : 13 (Test.java) + diff --git a/test/dexdump/values.xml b/test/dexdump/values.xml new file mode 100644 index 0000000000..d6ba48da79 --- /dev/null +++ b/test/dexdump/values.xml @@ -0,0 +1,561 @@ +<api> +<package name="" +> +<class name="Test" + extends="java.lang.Object" + interface="false" + abstract="false" + static="false" + final="false" + visibility="public" +> +<field name="mB0" + type="byte" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="0" +> +</field> +<field name="mB1" + type="byte" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="127" +> +</field> +<field name="mB2" + type="byte" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="-128" +> +</field> +<field name="mB3" + type="byte" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="-1" +> +</field> +<field name="mC0" + type="char" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="0" +> +</field> +<field name="mC1" + type="char" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="32767" +> +</field> +<field name="mC2" + type="char" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="32768" +> +</field> +<field name="mC3" + type="char" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="255" +> +</field> +<field name="mC4" + type="char" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="65520" +> +</field> +<field name="mC5" + type="char" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="65535" +> +</field> +<field name="mD0" + type="double" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="-inf" +> +</field> +<field name="mD1" + type="double" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="4.94066e-324" +> +</field> +<field name="mD2" + type="double" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="-0" +> +</field> +<field name="mD3" + type="double" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="0" +> +</field> +<field name="mD4" + type="double" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="1.79769e+308" +> +</field> +<field name="mD5" + type="double" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="inf" +> +</field> +<field name="mD6" + type="double" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="nan" +> +</field> +<field name="mF0" + type="float" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="-inf" +> +</field> +<field name="mF1" + type="float" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="1.4013e-45" +> +</field> +<field name="mF2" + type="float" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="-0" +> +</field> +<field name="mF3" + type="float" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="0" +> +</field> +<field name="mF4" + type="float" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="3.40282e+38" +> +</field> +<field name="mF5" + type="float" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="inf" +> +</field> +<field name="mF6" + type="float" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="nan" +> +</field> +<field name="mI0" + type="int" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="0" +> +</field> +<field name="mI1" + type="int" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="2147483647" +> +</field> +<field name="mI2" + type="int" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="-2147483648" +> +</field> +<field name="mI3" + type="int" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="255" +> +</field> +<field name="mI4" + type="int" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="-16" +> +</field> +<field name="mI5" + type="int" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="-4096" +> +</field> +<field name="mI6" + type="int" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="-1048576" +> +</field> +<field name="mI7" + type="int" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="-1" +> +</field> +<field name="mJ0" + type="long" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="0" +> +</field> +<field name="mJ1" + type="long" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="9223372036854775807" +> +</field> +<field name="mJ2" + type="long" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="-9223372036854775808" +> +</field> +<field name="mJ3" + type="long" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="255" +> +</field> +<field name="mJ4" + type="long" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="-16" +> +</field> +<field name="mJ5" + type="long" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="-4096" +> +</field> +<field name="mJ6" + type="long" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="-1048576" +> +</field> +<field name="mJ7" + type="long" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="-268435456" +> +</field> +<field name="mJ8" + type="long" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="-68719476736" +> +</field> +<field name="mJ9" + type="long" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="-17592186044416" +> +</field> +<field name="mJa" + type="long" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="-4503599627370496" +> +</field> +<field name="mJb" + type="long" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="-1" +> +</field> +<field name="mObject" + type="java.lang.Object" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="null" +> +</field> +<field name="mS0" + type="short" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="0" +> +</field> +<field name="mS1" + type="short" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="32767" +> +</field> +<field name="mS2" + type="short" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="-32768" +> +</field> +<field name="mS3" + type="short" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="255" +> +</field> +<field name="mS4" + type="short" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="-16" +> +</field> +<field name="mS5" + type="short" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="-1" +> +</field> +<field name="mString" + type="java.lang.String" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="<&"JOHO"&>
" +> +</field> +<field name="mZ0" + type="boolean" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="false" +> +</field> +<field name="mZ1" + type="boolean" + transient="false" + volatile="false" + static="true" + final="true" + visibility="public" + value="true" +> +</field> +<constructor name="Test" + type="Test" + static="false" + final="false" + visibility="public" +> +</constructor> +</class> +</package> +</api> diff --git a/test/valgrind-target-suppressions.txt b/test/valgrind-target-suppressions.txt index 896850c151..7ae6d539f3 100644 --- a/test/valgrind-target-suppressions.txt +++ b/test/valgrind-target-suppressions.txt @@ -40,3 +40,13 @@ fun:je_tsd_fetch fun:je_malloc_tsd_boot0 } + +# Setenv is known-leaking when overwriting mappings. This is triggered by re-initializing +# ANDROID_DATA. Ignore all setenv leaks. +{ + SetenvAndroidDataReinit + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:setenv +} diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh index 304c2a9398..d88a4a027e 100755 --- a/tools/buildbot-build.sh +++ b/tools/buildbot-build.sh @@ -51,7 +51,7 @@ if [[ $mode == "host" ]]; then make_command+=" ${out_dir}/host/linux-x86/lib64/libjavacoretests.so" elif [[ $mode == "target" ]]; then make_command="make $j_arg $showcommands build-art-target-tests $common_targets" - make_command+=" libjavacrypto libjavacoretests linker toybox toolbox sh" + make_command+=" libjavacrypto libjavacoretests libnetd_client linker toybox toolbox sh" make_command+=" ${out_dir}/host/linux-x86/bin/adb libstdc++ " make_command+=" ${out_dir}/target/product/${TARGET_PRODUCT}/system/etc/public.libraries.txt" fi |