diff options
125 files changed, 5139 insertions, 1580 deletions
diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk index 3d1f4343f1..7fae7f6200 100644 --- a/build/Android.common_test.mk +++ b/build/Android.common_test.mk @@ -23,10 +23,10 @@ include art/build/Android.common_path.mk ifneq ($(TMPDIR),) ART_HOST_TEST_DIR := $(TMPDIR)/test-art-$(shell echo $$PPID) else -# Use a BSD checksum calculated from PPID and USER as one of the path +# Use a BSD checksum calculated from CWD and USER as one of the path # components for the test output. This should allow us to run tests from # multiple repositories at the same time. -ART_HOST_TEST_DIR := /tmp/test-art-$(shell echo $$PPID-${USER} | sum | cut -d ' ' -f1) +ART_HOST_TEST_DIR := /tmp/test-art-$(shell echo $$CWD-${USER} | sum | cut -d ' ' -f1) endif # List of known broken tests that we won't attempt to execute. The test name must be the full diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 1f36cb4e46..45f4e2d4d8 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -155,6 +155,11 @@ ART_GTEST_dex2oat_environment_tests_TARGET_DEPS := \ $(TARGET_CORE_IMAGE_interpreter_32) \ patchoatd-target +ART_GTEST_oat_file_test_HOST_DEPS := \ + $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) +ART_GTEST_oat_file_test_TARGET_DEPS := \ + $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) + ART_GTEST_oat_file_assistant_test_HOST_DEPS := \ $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) ART_GTEST_oat_file_assistant_test_TARGET_DEPS := \ diff --git a/build/art.go b/build/art.go index bf6eee6c41..59480a0d0f 100644 --- a/build/art.go +++ b/build/art.go @@ -66,7 +66,7 @@ func globalFlags(ctx android.BaseContext) ([]string, []string) { "-DART_READ_BARRIER_TYPE_IS_"+barrierType+"=1") } - cdexLevel := envDefault(ctx, "ART_DEFAULT_COMPACT_DEX_LEVEL", "none") + cdexLevel := envDefault(ctx, "ART_DEFAULT_COMPACT_DEX_LEVEL", "fast") cflags = append(cflags, "-DART_DEFAULT_COMPACT_DEX_LEVEL="+cdexLevel) // We need larger stack overflow guards for ASAN, as the compiled code will have diff --git a/compiler/Android.bp b/compiler/Android.bp index 164f9c1e8f..d4d72f380b 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -249,6 +249,13 @@ art_cc_library { shared_libs: [ "libart", ], + + pgo: { + instrumentation: true, + profile_file: "dex2oat.profdata", + benchmarks: ["dex2oat"], + enable_profile_use: false, + } } art_cc_library { diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h index 713f8eb05d..893cad288b 100644 --- a/compiler/debug/elf_debug_info_writer.h +++ b/compiler/debug/elf_debug_info_writer.h @@ -49,7 +49,7 @@ static void LocalInfoCallback(void* ctx, const DexFile::LocalInfo& entry) { static std::vector<const char*> GetParamNames(const MethodDebugInfo* mi) { std::vector<const char*> names; - CodeItemDebugInfoAccessor accessor(*mi->dex_file, mi->code_item); + CodeItemDebugInfoAccessor accessor(*mi->dex_file, mi->code_item, mi->dex_method_index); if (accessor.HasCodeItem()) { DCHECK(mi->dex_file != nullptr); const uint8_t* stream = mi->dex_file->GetDebugInfoStream(accessor.DebugInfoOffset()); @@ -163,7 +163,7 @@ class ElfCompilationUnitWriter { for (auto mi : compilation_unit.methods) { DCHECK(mi->dex_file != nullptr); const DexFile* dex = mi->dex_file; - CodeItemDebugInfoAccessor accessor(*dex, mi->code_item); + CodeItemDebugInfoAccessor accessor(*dex, mi->code_item, mi->dex_method_index); const DexFile::MethodId& dex_method = dex->GetMethodId(mi->dex_method_index); const DexFile::ProtoId& dex_proto = dex->GetMethodPrototype(dex_method); const DexFile::TypeList* dex_params = dex->GetProtoParameters(dex_proto); diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h index 4e37f4e4ba..44504c1efb 100644 --- a/compiler/debug/elf_debug_line_writer.h +++ b/compiler/debug/elf_debug_line_writer.h @@ -159,7 +159,7 @@ class ElfDebugLineWriter { PositionInfos dex2line_map; DCHECK(mi->dex_file != nullptr); const DexFile* dex = mi->dex_file; - CodeItemDebugInfoAccessor accessor(*dex, mi->code_item); + CodeItemDebugInfoAccessor accessor(*dex, mi->code_item, mi->dex_method_index); const uint32_t debug_info_offset = accessor.DebugInfoOffset(); if (!dex->DecodeDebugPositionInfo(debug_info_offset, PositionInfoCallback, &dex2line_map)) { continue; diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index 52cb217980..308e75d9c1 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -373,15 +373,15 @@ CompiledMethod* ArtCompileDEX( CHECK_EQ(quicken_count, dex_compiler.GetQuickenedInfo().size()); } std::vector<uint8_t> quicken_data; + QuickenInfoTable::Builder builder(&quicken_data, dex_compiler.GetQuickenedInfo().size()); + // Length is encoded by the constructor. for (QuickenedInfo info : dex_compiler.GetQuickenedInfo()) { // Dex pc is not serialized, only used for checking the instructions. Since we access the // array based on the index of the quickened instruction, the indexes must line up perfectly. // The reader side uses the NeedsIndexForInstruction function too. const Instruction& inst = unit.GetCodeItemAccessor().InstructionAt(info.dex_pc); CHECK(QuickenInfoTable::NeedsIndexForInstruction(&inst)) << inst.Opcode(); - // Add the index. - quicken_data.push_back(static_cast<uint8_t>(info.dex_member_index >> 0)); - quicken_data.push_back(static_cast<uint8_t>(info.dex_member_index >> 8)); + builder.AddIndex(info.dex_member_index); } InstructionSet instruction_set = driver->GetInstructionSet(); if (instruction_set == InstructionSet::kThumb2) { diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index c0886d0185..869865956c 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -424,10 +424,6 @@ static optimizer::DexToDexCompilationLevel GetDexToDexCompilationLevel( // optimizations that could break that. max_level = optimizer::DexToDexCompilationLevel::kDontDexToDexCompile; } - if (!VdexFile::CanEncodeQuickenedData(dex_file)) { - // Don't do any dex level optimizations if we cannot encode the quickening. - return optimizer::DexToDexCompilationLevel::kDontDexToDexCompile; - } if (klass->IsVerified()) { // Class is verified so we can enable DEX-to-DEX compilation for performance. return max_level; diff --git a/compiler/linker/elf_builder.h b/compiler/linker/elf_builder.h index aa3cd98595..5262ab6f3b 100644 --- a/compiler/linker/elf_builder.h +++ b/compiler/linker/elf_builder.h @@ -38,9 +38,10 @@ namespace linker { // Elf_Ehdr - The ELF header. // Elf_Phdr[] - Program headers for the linker. // .note.gnu.build-id - Optional build ID section (SHA-1 digest). -// .rodata - DEX files and oat metadata. +// .rodata - Oat metadata. // .text - Compiled code. // .bss - Zero-initialized writeable section. +// .dex - Reserved NOBITS space for dex-related data. // .MIPS.abiflags - MIPS specific section. // .dynstr - Names for .dynsym. // .dynsym - A few oat-specific dynamic symbols. @@ -503,6 +504,7 @@ class ElfBuilder FINAL { rodata_(this, ".rodata", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0), text_(this, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, nullptr, 0, kPageSize, 0), bss_(this, ".bss", SHT_NOBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0), + dex_(this, ".dex", SHT_NOBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0), dynstr_(this, ".dynstr", SHF_ALLOC, kPageSize), dynsym_(this, ".dynsym", SHT_DYNSYM, SHF_ALLOC, &dynstr_), hash_(this, ".hash", SHT_HASH, SHF_ALLOC, &dynsym_, 0, sizeof(Elf_Word), sizeof(Elf_Word)), @@ -525,6 +527,7 @@ class ElfBuilder FINAL { virtual_address_(0) { text_.phdr_flags_ = PF_R | PF_X; bss_.phdr_flags_ = PF_R | PF_W; + dex_.phdr_flags_ = PF_R; dynamic_.phdr_flags_ = PF_R | PF_W; dynamic_.phdr_type_ = PT_DYNAMIC; eh_frame_hdr_.phdr_type_ = PT_GNU_EH_FRAME; @@ -538,6 +541,7 @@ class ElfBuilder FINAL { Section* GetRoData() { return &rodata_; } Section* GetText() { return &text_; } Section* GetBss() { return &bss_; } + Section* GetDex() { return &dex_; } StringSection* GetStrTab() { return &strtab_; } SymbolSection* GetSymTab() { return &symtab_; } Section* GetEhFrame() { return &eh_frame_; } @@ -666,7 +670,8 @@ class ElfBuilder FINAL { Elf_Word text_size, Elf_Word bss_size, Elf_Word bss_methods_offset, - Elf_Word bss_roots_offset) { + Elf_Word bss_roots_offset, + Elf_Word dex_size) { std::string soname(elf_file_path); size_t directory_separator_pos = soname.rfind('/'); if (directory_separator_pos != std::string::npos) { @@ -679,6 +684,9 @@ class ElfBuilder FINAL { if (bss_size != 0) { bss_.AllocateVirtualMemory(bss_size); } + if (dex_size != 0) { + dex_.AllocateVirtualMemory(dex_size); + } if (isa_ == InstructionSet::kMips || isa_ == InstructionSet::kMips64) { abiflags_.AllocateVirtualMemory(abiflags_.GetSize()); } @@ -725,6 +733,14 @@ class ElfBuilder FINAL { Elf_Word bsslastword_address = bss_.GetAddress() + bss_size - 4; dynsym_.Add(oatbsslastword, &bss_, bsslastword_address, 4, STB_GLOBAL, STT_OBJECT); } + if (dex_size != 0u) { + Elf_Word oatdex = dynstr_.Add("oatdex"); + dynsym_.Add(oatdex, &dex_, dex_.GetAddress(), dex_size, STB_GLOBAL, STT_OBJECT); + Elf_Word oatdexlastword = dynstr_.Add("oatdexlastword"); + Elf_Word oatdexlastword_address = dex_.GetAddress() + dex_size - 4; + dynsym_.Add(oatdexlastword, &dex_, oatdexlastword_address, 4, STB_GLOBAL, STT_OBJECT); + } + Elf_Word soname_offset = dynstr_.Add(soname); // We do not really need a hash-table since there is so few entries. @@ -967,6 +983,7 @@ class ElfBuilder FINAL { Section rodata_; Section text_; Section bss_; + Section dex_; CachedStringSection dynstr_; SymbolSection dynsym_; CachedSection hash_; diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index af537dd653..a1a5692ef6 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -43,7 +43,7 @@ HGraphBuilder::HGraphBuilder(HGraph* graph, CompilerDriver* driver, CodeGenerator* code_generator, OptimizingCompilerStats* compiler_stats, - const uint8_t* interpreter_metadata, + ArrayRef<const uint8_t> interpreter_metadata, VariableSizedHandleScope* handles) : graph_(graph), dex_file_(&graph->GetDexFile()), @@ -70,7 +70,6 @@ HGraphBuilder::HGraphBuilder(HGraph* graph, compiler_driver_(nullptr), code_generator_(nullptr), compilation_stats_(nullptr), - interpreter_metadata_(nullptr), handles_(handles), return_type_(return_type) {} diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index c16a3a928d..5a1914ce08 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -18,6 +18,7 @@ #define ART_COMPILER_OPTIMIZING_BUILDER_H_ #include "base/arena_object.h" +#include "base/array_ref.h" #include "dex/code_item_accessors.h" #include "dex/dex_file-inl.h" #include "dex/dex_file.h" @@ -40,7 +41,7 @@ class HGraphBuilder : public ValueObject { CompilerDriver* driver, CodeGenerator* code_generator, OptimizingCompilerStats* compiler_stats, - const uint8_t* interpreter_metadata, + ArrayRef<const uint8_t> interpreter_metadata, VariableSizedHandleScope* handles); // Only for unit testing. @@ -73,7 +74,7 @@ class HGraphBuilder : public ValueObject { CodeGenerator* const code_generator_; OptimizingCompilerStats* const compilation_stats_; - const uint8_t* const interpreter_metadata_; + const ArrayRef<const uint8_t> interpreter_metadata_; VariableSizedHandleScope* const handles_; const DataType::Type return_type_; diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 13886b32b3..28f481670c 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -2097,13 +2097,17 @@ void InstructionCodeGeneratorARM64::GenerateClassInitializationCheck(SlowPathCod Register class_reg) { UseScratchRegisterScope temps(GetVIXLAssembler()); Register temp = temps.AcquireW(); - size_t status_offset = mirror::Class::StatusOffset().SizeValue(); + constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf(); + const size_t status_byte_offset = + mirror::Class::StatusOffset().SizeValue() + (status_lsb_position / kBitsPerByte); + constexpr uint32_t shifted_initialized_value = + enum_cast<uint32_t>(ClassStatus::kInitialized) << (status_lsb_position % kBitsPerByte); // Even if the initialized flag is set, we need to ensure consistent memory ordering. // TODO(vixl): Let the MacroAssembler handle MemOperand. - __ Add(temp, class_reg, status_offset); + __ Add(temp, class_reg, status_byte_offset); __ Ldarb(temp, HeapOperand(temp)); - __ Cmp(temp, enum_cast<>(ClassStatus::kInitialized)); + __ Cmp(temp, shifted_initialized_value); __ B(lo, slow_path->GetEntryLabel()); __ Bind(slow_path->GetExitLabel()); } diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 7c6a5fde40..f1ad4e187e 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -7172,11 +7172,14 @@ void InstructionCodeGeneratorARMVIXL::GenerateClassInitializationCheck( LoadClassSlowPathARMVIXL* slow_path, vixl32::Register class_reg) { UseScratchRegisterScope temps(GetVIXLAssembler()); vixl32::Register temp = temps.Acquire(); - GetAssembler()->LoadFromOffset(kLoadUnsignedByte, - temp, - class_reg, - mirror::Class::StatusOffset().Int32Value()); - __ Cmp(temp, enum_cast<>(ClassStatus::kInitialized)); + constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf(); + const size_t status_byte_offset = + mirror::Class::StatusOffset().SizeValue() + (status_lsb_position / kBitsPerByte); + constexpr uint32_t shifted_initialized_value = + enum_cast<uint32_t>(ClassStatus::kInitialized) << (status_lsb_position % kBitsPerByte); + + GetAssembler()->LoadFromOffset(kLoadUnsignedByte, temp, class_reg, status_byte_offset); + __ Cmp(temp, shifted_initialized_value); __ B(lo, slow_path->GetEntryLabel()); // Even if the initialized flag is set, we may be in a situation where caches are not synced // properly. Therefore, we do a memory fence. diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index ebe252a9c8..c8bd5d4fc8 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -1915,8 +1915,14 @@ void CodeGeneratorMIPS::GenerateInvokeRuntime(int32_t entry_point_offset, bool d void InstructionCodeGeneratorMIPS::GenerateClassInitializationCheck(SlowPathCodeMIPS* slow_path, Register class_reg) { - __ LoadFromOffset(kLoadUnsignedByte, TMP, class_reg, mirror::Class::StatusOffset().Int32Value()); - __ LoadConst32(AT, enum_cast<>(ClassStatus::kInitialized)); + constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf(); + const size_t status_byte_offset = + mirror::Class::StatusOffset().SizeValue() + (status_lsb_position / kBitsPerByte); + constexpr uint32_t shifted_initialized_value = + enum_cast<uint32_t>(ClassStatus::kInitialized) << (status_lsb_position % kBitsPerByte); + + __ LoadFromOffset(kLoadUnsignedByte, TMP, class_reg, status_byte_offset); + __ LoadConst32(AT, shifted_initialized_value); __ Bltu(TMP, AT, slow_path->GetEntryLabel()); // Even if the initialized flag is set, we need to ensure consistent memory ordering. __ Sync(0); diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 3ea7b827bb..bbdc3be5c1 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -1761,8 +1761,14 @@ void CodeGeneratorMIPS64::GenerateInvokeRuntime(int32_t entry_point_offset) { void InstructionCodeGeneratorMIPS64::GenerateClassInitializationCheck(SlowPathCodeMIPS64* slow_path, GpuRegister class_reg) { - __ LoadFromOffset(kLoadUnsignedByte, TMP, class_reg, mirror::Class::StatusOffset().Int32Value()); - __ LoadConst32(AT, enum_cast<>(ClassStatus::kInitialized)); + constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf(); + const size_t status_byte_offset = + mirror::Class::StatusOffset().SizeValue() + (status_lsb_position / kBitsPerByte); + constexpr uint32_t shifted_initialized_value = + enum_cast<uint32_t>(ClassStatus::kInitialized) << (status_lsb_position % kBitsPerByte); + + __ LoadFromOffset(kLoadUnsignedByte, TMP, class_reg, status_byte_offset); + __ LoadConst32(AT, shifted_initialized_value); __ Bltuc(TMP, AT, slow_path->GetEntryLabel()); // Even if the initialized flag is set, we need to ensure consistent memory ordering. __ Sync(0); diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 68532386e1..537e97aacf 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -6219,8 +6219,13 @@ void InstructionCodeGeneratorX86::VisitClinitCheck(HClinitCheck* check) { void InstructionCodeGeneratorX86::GenerateClassInitializationCheck( SlowPathCode* slow_path, Register class_reg) { - __ cmpb(Address(class_reg, mirror::Class::StatusOffset().Int32Value()), - Immediate(enum_cast<>(ClassStatus::kInitialized))); + constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf(); + const size_t status_byte_offset = + mirror::Class::StatusOffset().SizeValue() + (status_lsb_position / kBitsPerByte); + constexpr uint32_t shifted_initialized_value = + enum_cast<uint32_t>(ClassStatus::kInitialized) << (status_lsb_position % kBitsPerByte); + + __ cmpb(Address(class_reg, status_byte_offset), Immediate(shifted_initialized_value)); __ j(kBelow, slow_path->GetEntryLabel()); __ Bind(slow_path->GetExitLabel()); // No need for memory fence, thanks to the X86 memory model. diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 1f8d822507..4a6428592e 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -5425,8 +5425,13 @@ void ParallelMoveResolverX86_64::RestoreScratch(int reg) { void InstructionCodeGeneratorX86_64::GenerateClassInitializationCheck( SlowPathCode* slow_path, CpuRegister class_reg) { - __ cmpb(Address(class_reg, mirror::Class::StatusOffset().Int32Value()), - Immediate(enum_cast<>(ClassStatus::kInitialized))); + constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf(); + const size_t status_byte_offset = + mirror::Class::StatusOffset().SizeValue() + (status_lsb_position / kBitsPerByte); + constexpr uint32_t shifted_initialized_value = + enum_cast<uint32_t>(ClassStatus::kInitialized) << (status_lsb_position % kBitsPerByte); + + __ cmpb(Address(class_reg, status_byte_offset), Immediate(shifted_initialized_value)); __ j(kBelow, slow_path->GetEntryLabel()); __ Bind(slow_path->GetExitLabel()); // No need for memory fence, thanks to the x86-64 memory model. diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc index 6eda289861..ba4040acad 100644 --- a/compiler/optimizing/codegen_test.cc +++ b/compiler/optimizing/codegen_test.cc @@ -74,8 +74,8 @@ static ::std::vector<CodegenTargetConfig> GetTargetConfigs() { class CodegenTest : public OptimizingUnitTest { protected: - void TestCode(const uint16_t* data, bool has_result = false, int32_t expected = 0); - void TestCodeLong(const uint16_t* data, bool has_result, int64_t expected); + void TestCode(const std::vector<uint16_t>& data, bool has_result = false, int32_t expected = 0); + void TestCodeLong(const std::vector<uint16_t>& data, bool has_result, int64_t expected); void TestComparison(IfCondition condition, int64_t i, int64_t j, @@ -83,7 +83,7 @@ class CodegenTest : public OptimizingUnitTest { const CodegenTargetConfig target_config); }; -void CodegenTest::TestCode(const uint16_t* data, bool has_result, int32_t expected) { +void CodegenTest::TestCode(const std::vector<uint16_t>& data, bool has_result, int32_t expected) { for (const CodegenTargetConfig& target_config : GetTargetConfigs()) { ResetPoolAndAllocator(); HGraph* graph = CreateCFG(data); @@ -93,7 +93,8 @@ void CodegenTest::TestCode(const uint16_t* data, bool has_result, int32_t expect } } -void CodegenTest::TestCodeLong(const uint16_t* data, bool has_result, int64_t expected) { +void CodegenTest::TestCodeLong(const std::vector<uint16_t>& data, + bool has_result, int64_t expected) { for (const CodegenTargetConfig& target_config : GetTargetConfigs()) { ResetPoolAndAllocator(); HGraph* graph = CreateCFG(data, DataType::Type::kInt64); @@ -104,12 +105,12 @@ void CodegenTest::TestCodeLong(const uint16_t* data, bool has_result, int64_t ex } TEST_F(CodegenTest, ReturnVoid) { - const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(Instruction::RETURN_VOID); + const std::vector<uint16_t> data = ZERO_REGISTER_CODE_ITEM(Instruction::RETURN_VOID); TestCode(data); } TEST_F(CodegenTest, CFG1) { - const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO | 0x100, Instruction::RETURN_VOID); @@ -117,7 +118,7 @@ TEST_F(CodegenTest, CFG1) { } TEST_F(CodegenTest, CFG2) { - const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO | 0x100, Instruction::GOTO | 0x100, Instruction::RETURN_VOID); @@ -126,21 +127,21 @@ TEST_F(CodegenTest, CFG2) { } TEST_F(CodegenTest, CFG3) { - const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data1 = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO | 0x200, Instruction::RETURN_VOID, Instruction::GOTO | 0xFF00); TestCode(data1); - const uint16_t data2[] = ZERO_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data2 = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO_16, 3, Instruction::RETURN_VOID, Instruction::GOTO_16, 0xFFFF); TestCode(data2); - const uint16_t data3[] = ZERO_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data3 = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO_32, 4, 0, Instruction::RETURN_VOID, Instruction::GOTO_32, 0xFFFF, 0xFFFF); @@ -149,7 +150,7 @@ TEST_F(CodegenTest, CFG3) { } TEST_F(CodegenTest, CFG4) { - const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ZERO_REGISTER_CODE_ITEM( Instruction::RETURN_VOID, Instruction::GOTO | 0x100, Instruction::GOTO | 0xFE00); @@ -158,7 +159,7 @@ TEST_F(CodegenTest, CFG4) { } TEST_F(CodegenTest, CFG5) { - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, Instruction::GOTO | 0x100, @@ -168,7 +169,7 @@ TEST_F(CodegenTest, CFG5) { } TEST_F(CodegenTest, IntConstant) { - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::RETURN_VOID); @@ -176,7 +177,7 @@ TEST_F(CodegenTest, IntConstant) { } TEST_F(CodegenTest, Return1) { - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::RETURN | 0); @@ -184,7 +185,7 @@ TEST_F(CodegenTest, Return1) { } TEST_F(CodegenTest, Return2) { - const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::CONST_4 | 0 | 1 << 8, Instruction::RETURN | 1 << 8); @@ -193,7 +194,7 @@ TEST_F(CodegenTest, Return2) { } TEST_F(CodegenTest, Return3) { - const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::CONST_4 | 1 << 8 | 1 << 12, Instruction::RETURN | 1 << 8); @@ -202,7 +203,7 @@ TEST_F(CodegenTest, Return3) { } TEST_F(CodegenTest, ReturnIf1) { - const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::CONST_4 | 1 << 8 | 1 << 12, Instruction::IF_EQ, 3, @@ -213,7 +214,7 @@ TEST_F(CodegenTest, ReturnIf1) { } TEST_F(CodegenTest, ReturnIf2) { - const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::CONST_4 | 1 << 8 | 1 << 12, Instruction::IF_EQ | 0 << 4 | 1 << 8, 3, @@ -224,17 +225,17 @@ TEST_F(CodegenTest, ReturnIf2) { } // Exercise bit-wise (one's complement) not-int instruction. -#define NOT_INT_TEST(TEST_NAME, INPUT, EXPECTED_OUTPUT) \ -TEST_F(CodegenTest, TEST_NAME) { \ - const int32_t input = INPUT; \ - const uint16_t input_lo = Low16Bits(input); \ - const uint16_t input_hi = High16Bits(input); \ - const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( \ - Instruction::CONST | 0 << 8, input_lo, input_hi, \ - Instruction::NOT_INT | 1 << 8 | 0 << 12 , \ - Instruction::RETURN | 1 << 8); \ - \ - TestCode(data, true, EXPECTED_OUTPUT); \ +#define NOT_INT_TEST(TEST_NAME, INPUT, EXPECTED_OUTPUT) \ +TEST_F(CodegenTest, TEST_NAME) { \ + const int32_t input = INPUT; \ + const uint16_t input_lo = Low16Bits(input); \ + const uint16_t input_hi = High16Bits(input); \ + const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM( \ + Instruction::CONST | 0 << 8, input_lo, input_hi, \ + Instruction::NOT_INT | 1 << 8 | 0 << 12 , \ + Instruction::RETURN | 1 << 8); \ + \ + TestCode(data, true, EXPECTED_OUTPUT); \ } NOT_INT_TEST(ReturnNotIntMinus2, -2, 1) @@ -256,7 +257,7 @@ TEST_F(CodegenTest, TEST_NAME) { \ const uint16_t word1 = High16Bits(Low32Bits(input)); \ const uint16_t word2 = Low16Bits(High32Bits(input)); \ const uint16_t word3 = High16Bits(High32Bits(input)); /* MSW. */ \ - const uint16_t data[] = FOUR_REGISTERS_CODE_ITEM( \ + const std::vector<uint16_t> data = FOUR_REGISTERS_CODE_ITEM( \ Instruction::CONST_WIDE | 0 << 8, word0, word1, word2, word3, \ Instruction::NOT_LONG | 2 << 8 | 0 << 12, \ Instruction::RETURN_WIDE | 2 << 8); \ @@ -306,7 +307,7 @@ TEST_F(CodegenTest, IntToLongOfLongToInt) { const uint16_t word1 = High16Bits(Low32Bits(input)); const uint16_t word2 = Low16Bits(High32Bits(input)); const uint16_t word3 = High16Bits(High32Bits(input)); // MSW. - const uint16_t data[] = FIVE_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = FIVE_REGISTERS_CODE_ITEM( Instruction::CONST_WIDE | 0 << 8, word0, word1, word2, word3, Instruction::CONST_WIDE | 2 << 8, 1, 0, 0, 0, Instruction::ADD_LONG | 0, 0 << 8 | 2, // v0 <- 2^32 + 1 @@ -318,7 +319,7 @@ TEST_F(CodegenTest, IntToLongOfLongToInt) { } TEST_F(CodegenTest, ReturnAdd1) { - const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 3 << 12 | 0, Instruction::CONST_4 | 4 << 12 | 1 << 8, Instruction::ADD_INT, 1 << 8 | 0, @@ -328,7 +329,7 @@ TEST_F(CodegenTest, ReturnAdd1) { } TEST_F(CodegenTest, ReturnAdd2) { - const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 3 << 12 | 0, Instruction::CONST_4 | 4 << 12 | 1 << 8, Instruction::ADD_INT_2ADDR | 1 << 12, @@ -338,7 +339,7 @@ TEST_F(CodegenTest, ReturnAdd2) { } TEST_F(CodegenTest, ReturnAdd3) { - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 4 << 12 | 0 << 8, Instruction::ADD_INT_LIT8, 3 << 8 | 0, Instruction::RETURN); @@ -347,7 +348,7 @@ TEST_F(CodegenTest, ReturnAdd3) { } TEST_F(CodegenTest, ReturnAdd4) { - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 4 << 12 | 0 << 8, Instruction::ADD_INT_LIT16, 3, Instruction::RETURN); @@ -356,7 +357,7 @@ TEST_F(CodegenTest, ReturnAdd4) { } TEST_F(CodegenTest, ReturnMulInt) { - const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 3 << 12 | 0, Instruction::CONST_4 | 4 << 12 | 1 << 8, Instruction::MUL_INT, 1 << 8 | 0, @@ -366,7 +367,7 @@ TEST_F(CodegenTest, ReturnMulInt) { } TEST_F(CodegenTest, ReturnMulInt2addr) { - const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 3 << 12 | 0, Instruction::CONST_4 | 4 << 12 | 1 << 8, Instruction::MUL_INT_2ADDR | 1 << 12, @@ -376,7 +377,7 @@ TEST_F(CodegenTest, ReturnMulInt2addr) { } TEST_F(CodegenTest, ReturnMulLong) { - const uint16_t data[] = FOUR_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = FOUR_REGISTERS_CODE_ITEM( Instruction::CONST_WIDE | 0 << 8, 3, 0, 0, 0, Instruction::CONST_WIDE | 2 << 8, 4, 0, 0, 0, Instruction::MUL_LONG, 2 << 8 | 0, @@ -386,7 +387,7 @@ TEST_F(CodegenTest, ReturnMulLong) { } TEST_F(CodegenTest, ReturnMulLong2addr) { - const uint16_t data[] = FOUR_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = FOUR_REGISTERS_CODE_ITEM( Instruction::CONST_WIDE | 0 << 8, 3, 0, 0, 0, Instruction::CONST_WIDE | 2 << 8, 4, 0, 0, 0, Instruction::MUL_LONG_2ADDR | 2 << 12, @@ -396,7 +397,7 @@ TEST_F(CodegenTest, ReturnMulLong2addr) { } TEST_F(CodegenTest, ReturnMulIntLit8) { - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 4 << 12 | 0 << 8, Instruction::MUL_INT_LIT8, 3 << 8 | 0, Instruction::RETURN); @@ -405,7 +406,7 @@ TEST_F(CodegenTest, ReturnMulIntLit8) { } TEST_F(CodegenTest, ReturnMulIntLit16) { - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 4 << 12 | 0 << 8, Instruction::MUL_INT_LIT16, 3, Instruction::RETURN); @@ -578,7 +579,7 @@ TEST_F(CodegenTest, MaterializedCondition2) { } TEST_F(CodegenTest, ReturnDivIntLit8) { - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 4 << 12 | 0 << 8, Instruction::DIV_INT_LIT8, 3 << 8 | 0, Instruction::RETURN); @@ -587,7 +588,7 @@ TEST_F(CodegenTest, ReturnDivIntLit8) { } TEST_F(CodegenTest, ReturnDivInt2Addr) { - const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 4 << 12 | 0, Instruction::CONST_4 | 2 << 12 | 1 << 8, Instruction::DIV_INT_2ADDR | 1 << 12, diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc index e1980e080e..d27104752b 100644 --- a/compiler/optimizing/constant_folding_test.cc +++ b/compiler/optimizing/constant_folding_test.cc @@ -36,7 +36,7 @@ class ConstantFoldingTest : public OptimizingUnitTest { public: ConstantFoldingTest() : graph_(nullptr) { } - void TestCode(const uint16_t* data, + void TestCode(const std::vector<uint16_t>& data, const std::string& expected_before, const std::string& expected_after_cf, const std::string& expected_after_dce, @@ -100,7 +100,7 @@ class ConstantFoldingTest : public OptimizingUnitTest { * return v1 2. return v1 */ TEST_F(ConstantFoldingTest, IntConstantFoldingNegation) { - const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 << 8 | 1 << 12, Instruction::NEG_INT | 1 << 8 | 0 << 12, Instruction::RETURN | 1 << 8); @@ -161,7 +161,7 @@ TEST_F(ConstantFoldingTest, LongConstantFoldingNegation) { const uint16_t word1 = High16Bits(Low32Bits(input)); const uint16_t word2 = Low16Bits(High32Bits(input)); const uint16_t word3 = High16Bits(High32Bits(input)); // MSW. - const uint16_t data[] = FOUR_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = FOUR_REGISTERS_CODE_ITEM( Instruction::CONST_WIDE | 0 << 8, word0, word1, word2, word3, Instruction::NEG_LONG | 2 << 8 | 0 << 12, Instruction::RETURN_WIDE | 2 << 8); @@ -219,7 +219,7 @@ TEST_F(ConstantFoldingTest, LongConstantFoldingNegation) { * return v2 4. return v2 */ TEST_F(ConstantFoldingTest, IntConstantFoldingOnAddition1) { - const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = THREE_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 << 8 | 1 << 12, Instruction::CONST_4 | 1 << 8 | 2 << 12, Instruction::ADD_INT | 2 << 8, 0 | 1 << 8, @@ -284,7 +284,7 @@ TEST_F(ConstantFoldingTest, IntConstantFoldingOnAddition1) { * return v2 8. return v2 */ TEST_F(ConstantFoldingTest, IntConstantFoldingOnAddition2) { - const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = THREE_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 << 8 | 1 << 12, Instruction::CONST_4 | 1 << 8 | 2 << 12, Instruction::ADD_INT_2ADDR | 0 << 8 | 1 << 12, @@ -369,7 +369,7 @@ TEST_F(ConstantFoldingTest, IntConstantFoldingOnAddition2) { * return v2 4. return v2 */ TEST_F(ConstantFoldingTest, IntConstantFoldingOnSubtraction) { - const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = THREE_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 << 8 | 3 << 12, Instruction::CONST_4 | 1 << 8 | 2 << 12, Instruction::SUB_INT | 2 << 8, 0 | 1 << 8, @@ -432,7 +432,7 @@ TEST_F(ConstantFoldingTest, IntConstantFoldingOnSubtraction) { * return (v4, v5) 6. return-wide v4 */ TEST_F(ConstantFoldingTest, LongConstantFoldingOnAddition) { - const uint16_t data[] = SIX_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = SIX_REGISTERS_CODE_ITEM( Instruction::CONST_WIDE_16 | 0 << 8, 1, Instruction::CONST_WIDE_16 | 2 << 8, 2, Instruction::ADD_LONG | 4 << 8, 0 | 2 << 8, @@ -496,7 +496,7 @@ TEST_F(ConstantFoldingTest, LongConstantFoldingOnAddition) { * return (v4, v5) 6. return-wide v4 */ TEST_F(ConstantFoldingTest, LongConstantFoldingOnSubtraction) { - const uint16_t data[] = SIX_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = SIX_REGISTERS_CODE_ITEM( Instruction::CONST_WIDE_16 | 0 << 8, 3, Instruction::CONST_WIDE_16 | 2 << 8, 2, Instruction::SUB_LONG | 4 << 8, 0 | 2 << 8, @@ -569,7 +569,7 @@ TEST_F(ConstantFoldingTest, LongConstantFoldingOnSubtraction) { * return v2 13. return v2 */ TEST_F(ConstantFoldingTest, IntConstantFoldingAndJumps) { - const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = THREE_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 << 8 | 1 << 12, Instruction::CONST_4 | 1 << 8 | 2 << 12, Instruction::ADD_INT | 2 << 8, 0 | 1 << 8, @@ -672,7 +672,7 @@ TEST_F(ConstantFoldingTest, IntConstantFoldingAndJumps) { * return-void 7. return */ TEST_F(ConstantFoldingTest, ConstantCondition) { - const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = THREE_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 1 << 8 | 1 << 12, Instruction::CONST_4 | 0 << 8 | 0 << 12, Instruction::IF_GEZ | 1 << 8, 3, diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc index 929572ee3b..adb6ce1187 100644 --- a/compiler/optimizing/dead_code_elimination_test.cc +++ b/compiler/optimizing/dead_code_elimination_test.cc @@ -29,12 +29,12 @@ namespace art { class DeadCodeEliminationTest : public OptimizingUnitTest { protected: - void TestCode(const uint16_t* data, + void TestCode(const std::vector<uint16_t>& data, const std::string& expected_before, const std::string& expected_after); }; -void DeadCodeEliminationTest::TestCode(const uint16_t* data, +void DeadCodeEliminationTest::TestCode(const std::vector<uint16_t>& data, const std::string& expected_before, const std::string& expected_after) { HGraph* graph = CreateCFG(data); @@ -73,7 +73,7 @@ void DeadCodeEliminationTest::TestCode(const uint16_t* data, * return-void 7. return */ TEST_F(DeadCodeEliminationTest, AdditionAndConditionalJump) { - const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = THREE_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 1 << 8 | 1 << 12, Instruction::CONST_4 | 0 << 8 | 0 << 12, Instruction::IF_GEZ | 1 << 8, 3, @@ -135,7 +135,7 @@ TEST_F(DeadCodeEliminationTest, AdditionAndConditionalJump) { * return 13. return-void */ TEST_F(DeadCodeEliminationTest, AdditionsAndInconditionalJumps) { - const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = THREE_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 << 8 | 0 << 12, Instruction::CONST_4 | 1 << 8 | 1 << 12, Instruction::ADD_INT | 2 << 8, 0 | 1 << 8, diff --git a/compiler/optimizing/dominator_test.cc b/compiler/optimizing/dominator_test.cc index 572466eec8..1d72ba116e 100644 --- a/compiler/optimizing/dominator_test.cc +++ b/compiler/optimizing/dominator_test.cc @@ -26,10 +26,12 @@ namespace art { class OptimizerTest : public OptimizingUnitTest { protected: - void TestCode(const uint16_t* data, const uint32_t* blocks, size_t blocks_length); + void TestCode(const std::vector<uint16_t>& data, const uint32_t* blocks, size_t blocks_length); }; -void OptimizerTest::TestCode(const uint16_t* data, const uint32_t* blocks, size_t blocks_length) { +void OptimizerTest::TestCode(const std::vector<uint16_t>& data, + const uint32_t* blocks, + size_t blocks_length) { HGraph* graph = CreateCFG(data); ASSERT_EQ(graph->GetBlocks().size(), blocks_length); for (size_t i = 0, e = blocks_length; i < e; ++i) { @@ -49,7 +51,7 @@ void OptimizerTest::TestCode(const uint16_t* data, const uint32_t* blocks, size_ } TEST_F(OptimizerTest, ReturnVoid) { - const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ZERO_REGISTER_CODE_ITEM( Instruction::RETURN_VOID); // Block number 1 const uint32_t dominators[] = { @@ -62,7 +64,7 @@ TEST_F(OptimizerTest, ReturnVoid) { } TEST_F(OptimizerTest, CFG1) { - const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO | 0x100, // Block number 1 Instruction::RETURN_VOID); // Block number 2 @@ -77,7 +79,7 @@ TEST_F(OptimizerTest, CFG1) { } TEST_F(OptimizerTest, CFG2) { - const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO | 0x100, // Block number 1 Instruction::GOTO | 0x100, // Block number 2 Instruction::RETURN_VOID); // Block number 3 @@ -94,7 +96,7 @@ TEST_F(OptimizerTest, CFG2) { } TEST_F(OptimizerTest, CFG3) { - const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data1 = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO | 0x200, // Block number 1 Instruction::RETURN_VOID, // Block number 2 Instruction::GOTO | 0xFF00); // Block number 3 @@ -109,14 +111,14 @@ TEST_F(OptimizerTest, CFG3) { TestCode(data1, dominators, sizeof(dominators) / sizeof(int)); - const uint16_t data2[] = ZERO_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data2 = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO_16, 3, Instruction::RETURN_VOID, Instruction::GOTO_16, 0xFFFF); TestCode(data2, dominators, sizeof(dominators) / sizeof(int)); - const uint16_t data3[] = ZERO_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data3 = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO_32, 4, 0, Instruction::RETURN_VOID, Instruction::GOTO_32, 0xFFFF, 0xFFFF); @@ -125,7 +127,7 @@ TEST_F(OptimizerTest, CFG3) { } TEST_F(OptimizerTest, CFG4) { - const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data1 = ZERO_REGISTER_CODE_ITEM( Instruction::NOP, Instruction::GOTO | 0xFF00); @@ -138,14 +140,14 @@ TEST_F(OptimizerTest, CFG4) { TestCode(data1, dominators, sizeof(dominators) / sizeof(int)); - const uint16_t data2[] = ZERO_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data2 = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO_32, 0, 0); TestCode(data2, dominators, sizeof(dominators) / sizeof(int)); } TEST_F(OptimizerTest, CFG5) { - const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ZERO_REGISTER_CODE_ITEM( Instruction::RETURN_VOID, // Block number 1 Instruction::GOTO | 0x100, // Dead block Instruction::GOTO | 0xFE00); // Block number 2 @@ -162,7 +164,7 @@ TEST_F(OptimizerTest, CFG5) { } TEST_F(OptimizerTest, CFG6) { - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, Instruction::GOTO | 0x100, @@ -181,7 +183,7 @@ TEST_F(OptimizerTest, CFG6) { } TEST_F(OptimizerTest, CFG7) { - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, // Block number 1 Instruction::GOTO | 0x100, // Block number 2 @@ -201,7 +203,7 @@ TEST_F(OptimizerTest, CFG7) { } TEST_F(OptimizerTest, CFG8) { - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, // Block number 1 Instruction::GOTO | 0x200, // Block number 2 @@ -222,7 +224,7 @@ TEST_F(OptimizerTest, CFG8) { } TEST_F(OptimizerTest, CFG9) { - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, // Block number 1 Instruction::GOTO | 0x200, // Block number 2 @@ -243,7 +245,7 @@ TEST_F(OptimizerTest, CFG9) { } TEST_F(OptimizerTest, CFG10) { - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 6, // Block number 1 Instruction::IF_EQ, 3, // Block number 2 diff --git a/compiler/optimizing/find_loops_test.cc b/compiler/optimizing/find_loops_test.cc index b799fb4688..75b8e9609e 100644 --- a/compiler/optimizing/find_loops_test.cc +++ b/compiler/optimizing/find_loops_test.cc @@ -31,7 +31,7 @@ class FindLoopsTest : public OptimizingUnitTest {}; TEST_F(FindLoopsTest, CFG1) { // Constant is not used. - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::RETURN_VOID); @@ -42,7 +42,7 @@ TEST_F(FindLoopsTest, CFG1) { } TEST_F(FindLoopsTest, CFG2) { - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::RETURN); @@ -53,7 +53,7 @@ TEST_F(FindLoopsTest, CFG2) { } TEST_F(FindLoopsTest, CFG3) { - const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 3 << 12 | 0, Instruction::CONST_4 | 4 << 12 | 1 << 8, Instruction::ADD_INT_2ADDR | 1 << 12, @@ -67,7 +67,7 @@ TEST_F(FindLoopsTest, CFG3) { } TEST_F(FindLoopsTest, CFG4) { - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 4, Instruction::CONST_4 | 4 << 12 | 0, @@ -82,7 +82,7 @@ TEST_F(FindLoopsTest, CFG4) { } TEST_F(FindLoopsTest, CFG5) { - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, Instruction::CONST_4 | 4 << 12 | 0, @@ -126,7 +126,7 @@ TEST_F(FindLoopsTest, Loop1) { // while (a == a) { // } // return; - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, Instruction::GOTO | 0xFE00, @@ -150,7 +150,7 @@ TEST_F(FindLoopsTest, Loop2) { // while (a == a) { // } // return a; - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::GOTO | 0x400, Instruction::IF_EQ, 4, @@ -173,7 +173,7 @@ TEST_F(FindLoopsTest, Loop2) { TEST_F(FindLoopsTest, Loop3) { // Make sure we create a preheader of a loop when a header originally has two // incoming blocks and one back edge. - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, Instruction::GOTO | 0x100, @@ -197,7 +197,7 @@ TEST_F(FindLoopsTest, Loop3) { TEST_F(FindLoopsTest, Loop4) { // Test loop with originally two back edges. - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 6, Instruction::IF_EQ, 3, @@ -221,7 +221,7 @@ TEST_F(FindLoopsTest, Loop4) { TEST_F(FindLoopsTest, Loop5) { // Test loop with two exit edges. - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 6, Instruction::IF_EQ, 3, @@ -244,7 +244,7 @@ TEST_F(FindLoopsTest, Loop5) { } TEST_F(FindLoopsTest, InnerLoop) { - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 6, Instruction::IF_EQ, 3, @@ -273,7 +273,7 @@ TEST_F(FindLoopsTest, InnerLoop) { } TEST_F(FindLoopsTest, TwoLoops) { - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, Instruction::GOTO | 0xFE00, // first loop @@ -301,7 +301,7 @@ TEST_F(FindLoopsTest, TwoLoops) { } TEST_F(FindLoopsTest, NonNaturalLoop) { - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, Instruction::GOTO | 0x0100, @@ -317,7 +317,7 @@ TEST_F(FindLoopsTest, NonNaturalLoop) { } TEST_F(FindLoopsTest, DoWhileLoop) { - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::GOTO | 0x0100, Instruction::IF_EQ, 0xFFFF, diff --git a/compiler/optimizing/graph_checker_test.cc b/compiler/optimizing/graph_checker_test.cc index 9ca3e4953a..08bfa5d80f 100644 --- a/compiler/optimizing/graph_checker_test.cc +++ b/compiler/optimizing/graph_checker_test.cc @@ -22,7 +22,7 @@ namespace art { class GraphCheckerTest : public OptimizingUnitTest { protected: HGraph* CreateSimpleCFG(); - void TestCode(const uint16_t* data); + void TestCode(const std::vector<uint16_t>& data); }; /** @@ -48,7 +48,7 @@ HGraph* GraphCheckerTest::CreateSimpleCFG() { return graph; } -void GraphCheckerTest::TestCode(const uint16_t* data) { +void GraphCheckerTest::TestCode(const std::vector<uint16_t>& data) { HGraph* graph = CreateCFG(data); ASSERT_NE(graph, nullptr); @@ -58,14 +58,14 @@ void GraphCheckerTest::TestCode(const uint16_t* data) { } TEST_F(GraphCheckerTest, ReturnVoid) { - const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ZERO_REGISTER_CODE_ITEM( Instruction::RETURN_VOID); TestCode(data); } TEST_F(GraphCheckerTest, CFG1) { - const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO | 0x100, Instruction::RETURN_VOID); @@ -73,7 +73,7 @@ TEST_F(GraphCheckerTest, CFG1) { } TEST_F(GraphCheckerTest, CFG2) { - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, Instruction::GOTO | 0x100, @@ -83,7 +83,7 @@ TEST_F(GraphCheckerTest, CFG2) { } TEST_F(GraphCheckerTest, CFG3) { - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, Instruction::GOTO | 0x100, @@ -128,7 +128,7 @@ TEST_F(GraphCheckerTest, BlockEndingWithNonBranchInstruction) { TEST_F(GraphCheckerTest, SSAPhi) { // This code creates one Phi function during the conversion to SSA form. - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, Instruction::CONST_4 | 4 << 12 | 0, diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index b2ad8ec400..81a75584a4 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -1660,7 +1660,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, const DexFile::CodeItem* code_item = resolved_method->GetCodeItem(); const DexFile& callee_dex_file = *resolved_method->GetDexFile(); uint32_t method_index = resolved_method->GetDexMethodIndex(); - CodeItemDebugInfoAccessor code_item_accessor(callee_dex_file, code_item); + CodeItemDebugInfoAccessor code_item_accessor(resolved_method); ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker(); Handle<mirror::DexCache> dex_cache = NewHandleIfDifferent(resolved_method->GetDexCache(), caller_compilation_unit_.GetDexCache(), diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 72a93c1f77..64a1eccf60 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -49,7 +49,7 @@ HInstructionBuilder::HInstructionBuilder(HGraph* graph, const DexCompilationUnit* outer_compilation_unit, CompilerDriver* compiler_driver, CodeGenerator* code_generator, - const uint8_t* interpreter_metadata, + ArrayRef<const uint8_t> interpreter_metadata, OptimizingCompilerStats* compiler_stats, VariableSizedHandleScope* handles, ScopedArenaAllocator* local_allocator) diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index 708a09711a..4428c53277 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -17,6 +17,7 @@ #ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_BUILDER_H_ #define ART_COMPILER_OPTIMIZING_INSTRUCTION_BUILDER_H_ +#include "base/array_ref.h" #include "base/scoped_arena_allocator.h" #include "base/scoped_arena_containers.h" #include "data_type.h" @@ -57,7 +58,7 @@ class HInstructionBuilder : public ValueObject { const DexCompilationUnit* outer_compilation_unit, CompilerDriver* compiler_driver, CodeGenerator* code_generator, - const uint8_t* interpreter_metadata, + ArrayRef<const uint8_t> interpreter_metadata, OptimizingCompilerStats* compiler_stats, VariableSizedHandleScope* handles, ScopedArenaAllocator* local_allocator); diff --git a/compiler/optimizing/linearize_test.cc b/compiler/optimizing/linearize_test.cc index 43b63a73ef..9fa5b74c62 100644 --- a/compiler/optimizing/linearize_test.cc +++ b/compiler/optimizing/linearize_test.cc @@ -35,11 +35,12 @@ namespace art { class LinearizeTest : public OptimizingUnitTest { protected: template <size_t number_of_blocks> - void TestCode(const uint16_t* data, const uint32_t (&expected_order)[number_of_blocks]); + void TestCode(const std::vector<uint16_t>& data, + const uint32_t (&expected_order)[number_of_blocks]); }; template <size_t number_of_blocks> -void LinearizeTest::TestCode(const uint16_t* data, +void LinearizeTest::TestCode(const std::vector<uint16_t>& data, const uint32_t (&expected_order)[number_of_blocks]) { HGraph* graph = CreateCFG(data); std::unique_ptr<const X86InstructionSetFeatures> features_x86( @@ -68,7 +69,7 @@ TEST_F(LinearizeTest, CFG1) { // + / \ + // Block4 Block8 - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 5, Instruction::IF_EQ, 0xFFFE, @@ -93,7 +94,7 @@ TEST_F(LinearizeTest, CFG2) { // + / \ + // Block5 Block8 - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, Instruction::RETURN_VOID, @@ -119,7 +120,7 @@ TEST_F(LinearizeTest, CFG3) { // Block6 + Block9 // | + // Block4 ++ - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 4, Instruction::RETURN_VOID, @@ -149,7 +150,7 @@ TEST_F(LinearizeTest, CFG4) { // + / \ + // Block5 Block11 */ - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 7, Instruction::IF_EQ, 0xFFFE, @@ -179,7 +180,7 @@ TEST_F(LinearizeTest, CFG5) { // +/ \ + // Block6 Block11 */ - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, Instruction::RETURN_VOID, @@ -205,7 +206,7 @@ TEST_F(LinearizeTest, CFG6) { // Block5 <- Block9 Block6 + // | // Block7 - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::GOTO | 0x0100, Instruction::IF_EQ, 0x0004, @@ -233,7 +234,7 @@ TEST_F(LinearizeTest, CFG7) { // | // Block7 // - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::GOTO | 0x0100, Instruction::IF_EQ, 0x0005, diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc index e45d7c820c..66660662e4 100644 --- a/compiler/optimizing/live_ranges_test.cc +++ b/compiler/optimizing/live_ranges_test.cc @@ -31,10 +31,10 @@ namespace art { class LiveRangesTest : public OptimizingUnitTest { public: - HGraph* BuildGraph(const uint16_t* data); + HGraph* BuildGraph(const std::vector<uint16_t>& data); }; -HGraph* LiveRangesTest::BuildGraph(const uint16_t* data) { +HGraph* LiveRangesTest::BuildGraph(const std::vector<uint16_t>& data) { HGraph* graph = CreateCFG(data); // Suspend checks implementation may change in the future, and this test relies // on how instructions are ordered. @@ -57,7 +57,7 @@ TEST_F(LiveRangesTest, CFG1) { * | * 12: exit */ - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::RETURN); @@ -102,7 +102,7 @@ TEST_F(LiveRangesTest, CFG2) { * | * 26: exit */ - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, Instruction::GOTO | 0x100, @@ -151,7 +151,7 @@ TEST_F(LiveRangesTest, CFG3) { * | * 28: exit */ - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, Instruction::CONST_4 | 4 << 12 | 0, @@ -225,7 +225,7 @@ TEST_F(LiveRangesTest, Loop1) { * 30: exit */ - const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 4, Instruction::CONST_4 | 4 << 12 | 0, @@ -304,7 +304,7 @@ TEST_F(LiveRangesTest, Loop2) { * We want to make sure the phi at 10 has a lifetime hole after the add at 20. */ - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 6, Instruction::ADD_INT, 0, 0, @@ -378,7 +378,7 @@ TEST_F(LiveRangesTest, CFG4) { * * We want to make sure the constant0 has a lifetime hole after the 16: add. */ - const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::CONST_4 | 4 << 12 | 1 << 8, Instruction::IF_EQ, 5, diff --git a/compiler/optimizing/liveness_test.cc b/compiler/optimizing/liveness_test.cc index 35bc4ff8b3..6621a03568 100644 --- a/compiler/optimizing/liveness_test.cc +++ b/compiler/optimizing/liveness_test.cc @@ -31,7 +31,7 @@ namespace art { class LivenessTest : public OptimizingUnitTest { protected: - void TestCode(const uint16_t* data, const char* expected); + void TestCode(const std::vector<uint16_t>& data, const char* expected); }; static void DumpBitVector(BitVector* vector, @@ -46,7 +46,7 @@ static void DumpBitVector(BitVector* vector, buffer << ")\n"; } -void LivenessTest::TestCode(const uint16_t* data, const char* expected) { +void LivenessTest::TestCode(const std::vector<uint16_t>& data, const char* expected) { HGraph* graph = CreateCFG(data); // `Inline` conditions into ifs. PrepareForRegisterAllocation(graph).Run(); @@ -86,7 +86,7 @@ TEST_F(LivenessTest, CFG1) { " kill: (0)\n"; // Constant is not used. - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::RETURN_VOID); @@ -108,7 +108,7 @@ TEST_F(LivenessTest, CFG2) { " live out: (0)\n" " kill: (0)\n"; - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::RETURN); @@ -134,7 +134,7 @@ TEST_F(LivenessTest, CFG3) { " live out: (000)\n" " kill: (000)\n"; - const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 3 << 12 | 0, Instruction::CONST_4 | 4 << 12 | 1 << 8, Instruction::ADD_INT_2ADDR | 1 << 12, @@ -181,7 +181,7 @@ TEST_F(LivenessTest, CFG4) { " live out: (0000)\n" " kill: (0000)\n"; - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 4, Instruction::CONST_4 | 4 << 12 | 0, @@ -228,7 +228,7 @@ TEST_F(LivenessTest, CFG5) { " live out: (000)\n" " kill: (000)\n"; - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, Instruction::CONST_4 | 4 << 12 | 0, @@ -273,7 +273,7 @@ TEST_F(LivenessTest, Loop1) { " kill: (000)\n"; - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 4, Instruction::CONST_4 | 4 << 12 | 0, @@ -318,7 +318,7 @@ TEST_F(LivenessTest, Loop3) { " live out: (0000)\n" " kill: (0000)\n"; - const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 4, Instruction::CONST_4 | 4 << 12 | 0, @@ -370,7 +370,7 @@ TEST_F(LivenessTest, Loop4) { " live out: (000)\n" " kill: (000)\n"; - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::GOTO | 0x500, Instruction::IF_EQ, 5, @@ -425,7 +425,7 @@ TEST_F(LivenessTest, Loop5) { " live out: (0001)\n" " kill: (0001)\n"; - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 4, Instruction::CONST_4 | 4 << 12 | 0, @@ -475,7 +475,7 @@ TEST_F(LivenessTest, Loop6) { " live out: (0000)\n" " kill: (0000)\n"; - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 8, Instruction::CONST_4 | 4 << 12 | 0, @@ -530,7 +530,7 @@ TEST_F(LivenessTest, Loop7) { " live out: (00000)\n" " kill: (00000)\n"; - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 8, Instruction::CONST_4 | 4 << 12 | 0, @@ -580,7 +580,7 @@ TEST_F(LivenessTest, Loop8) { " live out: (000)\n" " kill: (000)\n"; - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 6, Instruction::ADD_INT, 0, 0, diff --git a/compiler/optimizing/optimizing_cfi_test.cc b/compiler/optimizing/optimizing_cfi_test.cc index e2b2106f65..d20b681b49 100644 --- a/compiler/optimizing/optimizing_cfi_test.cc +++ b/compiler/optimizing/optimizing_cfi_test.cc @@ -41,7 +41,7 @@ namespace art { // Run the tests only on host. #ifndef ART_TARGET_ANDROID -class OptimizingCFITest : public CFITest { +class OptimizingCFITest : public CFITest, public OptimizingUnitTestHelper { public: // Enable this flag to generate the expected outputs. static constexpr bool kGenerateExpected = false; @@ -63,7 +63,7 @@ class OptimizingCFITest : public CFITest { // Setup simple context. std::string error; isa_features_ = InstructionSetFeatures::FromVariant(isa, "default", &error); - graph_ = CreateGraph(&pool_and_allocator_); + graph_ = CreateGraph(); // Generate simple frame with some spills. code_gen_ = CodeGenerator::Create(graph_, isa, *isa_features_, opts_); code_gen_->GetAssembler()->cfi().SetEnabled(true); diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index f4115f7e7b..a3b1f0c5af 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -772,7 +772,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, return nullptr; } - CodeItemDebugInfoAccessor code_item_accessor(dex_file, code_item); + CodeItemDebugInfoAccessor code_item_accessor(dex_file, code_item, method_idx); HGraph* graph = new (allocator) HGraph( allocator, arena_stack, @@ -783,7 +783,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, compiler_driver->GetCompilerOptions().GetDebuggable(), osr); - const uint8_t* interpreter_metadata = nullptr; + ArrayRef<const uint8_t> interpreter_metadata; // For AOT compilation, we may not get a method, for example if its class is erroneous. // JIT should always have a method. DCHECK(Runtime::Current()->IsAotCompiler() || method != nullptr); @@ -940,7 +940,7 @@ CodeGenerator* OptimizingCompiler::TryCompileIntrinsic( compiler_driver, codegen.get(), compilation_stats_.get(), - /* interpreter_metadata */ nullptr, + /* interpreter_metadata */ ArrayRef<const uint8_t>(), handles); builder.BuildIntrinsicGraph(method); } diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h index 8c97d57f4a..6dcbadba6e 100644 --- a/compiler/optimizing/optimizing_unit_test.h +++ b/compiler/optimizing/optimizing_unit_test.h @@ -17,12 +17,16 @@ #ifndef ART_COMPILER_OPTIMIZING_OPTIMIZING_UNIT_TEST_H_ #define ART_COMPILER_OPTIMIZING_OPTIMIZING_UNIT_TEST_H_ +#include <memory> +#include <vector> + #include "base/scoped_arena_allocator.h" #include "builder.h" #include "common_compiler_test.h" #include "dex/code_item_accessors-inl.h" #include "dex/dex_file.h" #include "dex/dex_instruction.h" +#include "dex/standard_dex_file.h" #include "driver/dex_compilation_unit.h" #include "handle_scope-inl.h" #include "mirror/class_loader.h" @@ -99,18 +103,11 @@ class ArenaPoolAndAllocator { ScopedArenaAllocator scoped_allocator_; }; -inline HGraph* CreateGraph(ArenaPoolAndAllocator* pool_and_allocator) { - return new (pool_and_allocator->GetAllocator()) HGraph( - pool_and_allocator->GetAllocator(), - pool_and_allocator->GetArenaStack(), - *reinterpret_cast<DexFile*>(pool_and_allocator->GetAllocator()->Alloc(sizeof(DexFile))), - /*method_idx*/-1, - kRuntimeISA); -} - -class OptimizingUnitTest : public CommonCompilerTest { - protected: - OptimizingUnitTest() : pool_and_allocator_(new ArenaPoolAndAllocator()) { } +// Have a separate helper so the OptimizingCFITest can inherit it without causing +// multiple inheritance errors from having two gtest as a parent twice. +class OptimizingUnitTestHelper { + public: + OptimizingUnitTestHelper() : pool_and_allocator_(new ArenaPoolAndAllocator()) { } ArenaAllocator* GetAllocator() { return pool_and_allocator_->GetAllocator(); } ArenaStack* GetArenaStack() { return pool_and_allocator_->GetArenaStack(); } @@ -122,14 +119,42 @@ class OptimizingUnitTest : public CommonCompilerTest { } HGraph* CreateGraph() { - return art::CreateGraph(pool_and_allocator_.get()); + ArenaAllocator* const allocator = pool_and_allocator_->GetAllocator(); + + // Reserve a big array of 0s so the dex file constructor can offsets from the header. + static constexpr size_t kDexDataSize = 4 * KB; + const uint8_t* dex_data = reinterpret_cast<uint8_t*>(allocator->Alloc(kDexDataSize)); + + // Create the dex file based on the fake data. Call the constructor so that we can use virtual + // functions. Don't use the arena for the StandardDexFile otherwise the dex location leaks. + dex_files_.emplace_back(new StandardDexFile( + dex_data, + sizeof(StandardDexFile::Header), + "no_location", + /*location_checksum*/ 0, + /*oat_dex_file*/ nullptr, + /*container*/ nullptr)); + + return new (allocator) HGraph( + allocator, + pool_and_allocator_->GetArenaStack(), + *dex_files_.back(), + /*method_idx*/-1, + kRuntimeISA); } // Create a control-flow graph from Dex instructions. - HGraph* CreateCFG(const uint16_t* data, DataType::Type return_type = DataType::Type::kInt32) { - const DexFile::CodeItem* code_item = reinterpret_cast<const DexFile::CodeItem*>(data); + HGraph* CreateCFG(const std::vector<uint16_t>& data, + DataType::Type return_type = DataType::Type::kInt32) { HGraph* graph = CreateGraph(); + // The code item data might not aligned to 4 bytes, copy it to ensure that. + const size_t code_item_size = data.size() * sizeof(data.front()); + void* aligned_data = GetAllocator()->Alloc(code_item_size); + memcpy(aligned_data, &data[0], code_item_size); + CHECK_ALIGNED(aligned_data, StandardDexFile::CodeItem::kAlignment); + const DexFile::CodeItem* code_item = reinterpret_cast<const DexFile::CodeItem*>(aligned_data); + { ScopedObjectAccess soa(Thread::Current()); if (handles_ == nullptr) { @@ -146,7 +171,7 @@ class OptimizingUnitTest : public CommonCompilerTest { /* access_flags */ 0u, /* verified_method */ nullptr, handles_->NewHandle<mirror::DexCache>(nullptr)); - CodeItemDebugInfoAccessor accessor(graph->GetDexFile(), code_item); + CodeItemDebugInfoAccessor accessor(graph->GetDexFile(), code_item, /*dex_method_idx*/ 0u); HGraphBuilder builder(graph, dex_compilation_unit, accessor, handles_.get(), return_type); bool graph_built = (builder.BuildGraph() == kAnalysisSuccess); return graph_built ? graph : nullptr; @@ -154,10 +179,13 @@ class OptimizingUnitTest : public CommonCompilerTest { } private: + std::vector<std::unique_ptr<const StandardDexFile>> dex_files_; std::unique_ptr<ArenaPoolAndAllocator> pool_and_allocator_; std::unique_ptr<VariableSizedHandleScope> handles_; }; +class OptimizingUnitTest : public CommonCompilerTest, public OptimizingUnitTestHelper {}; + // Naive string diff data type. typedef std::list<std::pair<std::string, std::string>> diff_t; diff --git a/compiler/optimizing/pretty_printer_test.cc b/compiler/optimizing/pretty_printer_test.cc index 4fc7fe9427..6ef386b4a5 100644 --- a/compiler/optimizing/pretty_printer_test.cc +++ b/compiler/optimizing/pretty_printer_test.cc @@ -29,10 +29,10 @@ namespace art { class PrettyPrinterTest : public OptimizingUnitTest { protected: - void TestCode(const uint16_t* data, const char* expected); + void TestCode(const std::vector<uint16_t>& data, const char* expected); }; -void PrettyPrinterTest::TestCode(const uint16_t* data, const char* expected) { +void PrettyPrinterTest::TestCode(const std::vector<uint16_t>& data, const char* expected) { HGraph* graph = CreateCFG(data); StringPrettyPrinter printer(graph); printer.VisitInsertionOrder(); @@ -40,7 +40,7 @@ void PrettyPrinterTest::TestCode(const uint16_t* data, const char* expected) { } TEST_F(PrettyPrinterTest, ReturnVoid) { - const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ZERO_REGISTER_CODE_ITEM( Instruction::RETURN_VOID); const char* expected = @@ -67,7 +67,7 @@ TEST_F(PrettyPrinterTest, CFG1) { "BasicBlock 3, pred: 2\n" " 4: Exit\n"; - const uint16_t data[] = + const std::vector<uint16_t> data = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO | 0x100, Instruction::RETURN_VOID); @@ -89,7 +89,7 @@ TEST_F(PrettyPrinterTest, CFG2) { "BasicBlock 4, pred: 3\n" " 5: Exit\n"; - const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO | 0x100, Instruction::GOTO | 0x100, Instruction::RETURN_VOID); @@ -111,21 +111,21 @@ TEST_F(PrettyPrinterTest, CFG3) { "BasicBlock 4, pred: 2\n" " 5: Exit\n"; - const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data1 = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO | 0x200, Instruction::RETURN_VOID, Instruction::GOTO | 0xFF00); TestCode(data1, expected); - const uint16_t data2[] = ZERO_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data2 = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO_16, 3, Instruction::RETURN_VOID, Instruction::GOTO_16, 0xFFFF); TestCode(data2, expected); - const uint16_t data3[] = ZERO_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data3 = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO_32, 4, 0, Instruction::RETURN_VOID, Instruction::GOTO_32, 0xFFFF, 0xFFFF); @@ -144,13 +144,13 @@ TEST_F(PrettyPrinterTest, CFG4) { "BasicBlock 3, pred: 0, succ: 1\n" " 0: Goto 1\n"; - const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data1 = ZERO_REGISTER_CODE_ITEM( Instruction::NOP, Instruction::GOTO | 0xFF00); TestCode(data1, expected); - const uint16_t data2[] = ZERO_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data2 = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO_32, 0, 0); TestCode(data2, expected); @@ -166,7 +166,7 @@ TEST_F(PrettyPrinterTest, CFG5) { "BasicBlock 3, pred: 1\n" " 3: Exit\n"; - const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ZERO_REGISTER_CODE_ITEM( Instruction::RETURN_VOID, Instruction::GOTO | 0x100, Instruction::GOTO | 0xFE00); @@ -192,7 +192,7 @@ TEST_F(PrettyPrinterTest, CFG6) { "BasicBlock 5, pred: 1, succ: 3\n" " 0: Goto 3\n"; - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, Instruction::GOTO | 0x100, @@ -220,7 +220,7 @@ TEST_F(PrettyPrinterTest, CFG7) { "BasicBlock 6, pred: 1, succ: 2\n" " 1: Goto 2\n"; - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, Instruction::GOTO | 0x100, @@ -240,7 +240,7 @@ TEST_F(PrettyPrinterTest, IntConstant) { "BasicBlock 2, pred: 1\n" " 4: Exit\n"; - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::RETURN_VOID); diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc index 3748d599a3..a70b0664dc 100644 --- a/compiler/optimizing/register_allocator_test.cc +++ b/compiler/optimizing/register_allocator_test.cc @@ -46,7 +46,7 @@ class RegisterAllocatorTest : public OptimizingUnitTest { void ExpectedInRegisterHint(Strategy strategy); // Helper functions that make use of the OptimizingUnitTest's members. - bool Check(const uint16_t* data, Strategy strategy); + bool Check(const std::vector<uint16_t>& data, Strategy strategy); void CFG1(Strategy strategy); void Loop1(Strategy strategy); void Loop2(Strategy strategy); @@ -79,7 +79,7 @@ TEST_F(RegisterAllocatorTest, test_name##_GraphColor) {\ test_name(Strategy::kRegisterAllocatorGraphColor);\ } -bool RegisterAllocatorTest::Check(const uint16_t* data, Strategy strategy) { +bool RegisterAllocatorTest::Check(const std::vector<uint16_t>& data, Strategy strategy) { HGraph* graph = CreateCFG(data); std::unique_ptr<const X86InstructionSetFeatures> features_x86( X86InstructionSetFeatures::FromCppDefines()); @@ -185,7 +185,7 @@ void RegisterAllocatorTest::CFG1(Strategy strategy) { * | * exit */ - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::RETURN); @@ -222,7 +222,7 @@ void RegisterAllocatorTest::Loop1(Strategy strategy) { * exit */ - const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 4, Instruction::CONST_4 | 4 << 12 | 0, @@ -268,7 +268,7 @@ void RegisterAllocatorTest::Loop2(Strategy strategy) { * exit */ - const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::CONST_4 | 8 << 12 | 1 << 8, Instruction::IF_EQ | 1 << 8, 7, @@ -314,7 +314,7 @@ void RegisterAllocatorTest::Loop3(Strategy strategy) { * exit */ - const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = THREE_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::ADD_INT_LIT8 | 1 << 8, 1 << 8, Instruction::CONST_4 | 5 << 12 | 2 << 8, @@ -351,7 +351,7 @@ void RegisterAllocatorTest::Loop3(Strategy strategy) { TEST_ALL_STRATEGIES(Loop3); TEST_F(RegisterAllocatorTest, FirstRegisterUse) { - const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = THREE_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::XOR_INT_LIT8 | 1 << 8, 1 << 8, Instruction::XOR_INT_LIT8 | 0 << 8, 1 << 8, @@ -402,7 +402,7 @@ void RegisterAllocatorTest::DeadPhi(Strategy strategy) { * } while (true); */ - const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::CONST_4 | 1 << 8 | 0, Instruction::IF_NE | 1 << 8 | 1 << 12, 3, @@ -432,7 +432,7 @@ TEST_ALL_STRATEGIES(DeadPhi); * This test only applies to the linear scan allocator. */ TEST_F(RegisterAllocatorTest, FreeUntil) { - const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::RETURN); diff --git a/compiler/optimizing/scheduler_test.cc b/compiler/optimizing/scheduler_test.cc index 104ebc79c2..fb15fc8975 100644 --- a/compiler/optimizing/scheduler_test.cc +++ b/compiler/optimizing/scheduler_test.cc @@ -182,7 +182,9 @@ class SchedulerTest : public OptimizingUnitTest { scheduler->Schedule(graph_); } - void CompileWithRandomSchedulerAndRun(const uint16_t* data, bool has_result, int expected) { + void CompileWithRandomSchedulerAndRun(const std::vector<uint16_t>& data, + bool has_result, + int expected) { for (CodegenTargetConfig target_config : GetTargetConfigs()) { HGraph* graph = CreateCFG(data); @@ -393,7 +395,7 @@ TEST_F(SchedulerTest, RandomScheduling) { // } // return result; // - const uint16_t data[] = SIX_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = SIX_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 << 12 | 2 << 8, // const/4 v2, #int 0 Instruction::CONST_HIGH16 | 0 << 8, 0x4120, // const/high16 v0, #float 10.0 // #41200000 Instruction::CONST_4 | 1 << 12 | 1 << 8, // const/4 v1, #int 1 diff --git a/compiler/optimizing/ssa_test.cc b/compiler/optimizing/ssa_test.cc index 77e70d733e..85ed06eb9b 100644 --- a/compiler/optimizing/ssa_test.cc +++ b/compiler/optimizing/ssa_test.cc @@ -31,7 +31,7 @@ namespace art { class SsaTest : public OptimizingUnitTest { protected: - void TestCode(const uint16_t* data, const char* expected); + void TestCode(const std::vector<uint16_t>& data, const char* expected); }; class SsaPrettyPrinter : public HPrettyPrinter { @@ -80,7 +80,7 @@ static void ReNumberInstructions(HGraph* graph) { } } -void SsaTest::TestCode(const uint16_t* data, const char* expected) { +void SsaTest::TestCode(const std::vector<uint16_t>& data, const char* expected) { HGraph* graph = CreateCFG(data); // Suspend checks implementation may change in the future, and this test relies // on how instructions are ordered. @@ -119,7 +119,7 @@ TEST_F(SsaTest, CFG1) { "BasicBlock 5, pred: 1, succ: 3\n" " 7: Goto\n"; - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, Instruction::GOTO | 0x100, @@ -150,7 +150,7 @@ TEST_F(SsaTest, CFG2) { "BasicBlock 5, pred: 1, succ: 3\n" " 9: Goto\n"; - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, Instruction::CONST_4 | 4 << 12 | 0, @@ -181,7 +181,7 @@ TEST_F(SsaTest, CFG3) { "BasicBlock 5, pred: 4\n" " 10: Exit\n"; - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 4, Instruction::CONST_4 | 4 << 12 | 0, @@ -214,7 +214,7 @@ TEST_F(SsaTest, Loop1) { "BasicBlock 6, pred: 5\n" " 10: Exit\n"; - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 4, Instruction::CONST_4 | 4 << 12 | 0, @@ -245,7 +245,7 @@ TEST_F(SsaTest, Loop2) { "BasicBlock 5, pred: 4\n" " 9: Exit\n"; - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 4, Instruction::CONST_4 | 4 << 12 | 0, @@ -276,7 +276,7 @@ TEST_F(SsaTest, Loop3) { "BasicBlock 5, pred: 4\n" " 10: Exit\n"; - const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 4, Instruction::CONST_4 | 4 << 12 | 0, @@ -310,7 +310,7 @@ TEST_F(SsaTest, Loop4) { "BasicBlock 6, pred: 5\n" " 10: Exit\n"; - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::GOTO | 0x500, Instruction::IF_EQ, 5, @@ -351,7 +351,7 @@ TEST_F(SsaTest, Loop5) { " 13: Phi(2, 1) [11, 8, 8]\n" " 14: Goto\n"; - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 4, Instruction::CONST_4 | 4 << 12 | 0, @@ -390,7 +390,7 @@ TEST_F(SsaTest, Loop6) { "BasicBlock 7, pred: 6\n" " 13: Exit\n"; - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 8, Instruction::CONST_4 | 4 << 12 | 0, @@ -432,7 +432,7 @@ TEST_F(SsaTest, Loop7) { "BasicBlock 8, pred: 2, succ: 6\n" " 15: Goto\n"; - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 8, Instruction::CONST_4 | 4 << 12 | 0, @@ -456,7 +456,7 @@ TEST_F(SsaTest, DeadLocal) { "BasicBlock 2, pred: 1\n" " 3: Exit\n"; - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::RETURN_VOID); @@ -484,7 +484,7 @@ TEST_F(SsaTest, LocalInIf) { "BasicBlock 5, pred: 1, succ: 3\n" " 8: Goto\n"; - const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, Instruction::CONST_4 | 4 << 12 | 1 << 8, @@ -520,7 +520,7 @@ TEST_F(SsaTest, MultiplePredecessors) { "BasicBlock 7, pred: 3, succ: 5\n" " 12: Goto\n"; - const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( + const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 5, Instruction::ADD_INT_LIT8 | 1 << 8, 0 << 8, diff --git a/compiler/optimizing/suspend_check_test.cc b/compiler/optimizing/suspend_check_test.cc index 7e83f8ce5f..33823e2a11 100644 --- a/compiler/optimizing/suspend_check_test.cc +++ b/compiler/optimizing/suspend_check_test.cc @@ -30,10 +30,10 @@ namespace art { class SuspendCheckTest : public OptimizingUnitTest { protected: - void TestCode(const uint16_t* data); + void TestCode(const std::vector<uint16_t>& data); }; -void SuspendCheckTest::TestCode(const uint16_t* data) { +void SuspendCheckTest::TestCode(const std::vector<uint16_t>& data) { HGraph* graph = CreateCFG(data); HBasicBlock* first_block = graph->GetEntryBlock()->GetSingleSuccessor(); HBasicBlock* loop_header = first_block->GetSingleSuccessor(); @@ -43,7 +43,7 @@ void SuspendCheckTest::TestCode(const uint16_t* data) { } TEST_F(SuspendCheckTest, CFG1) { - const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ZERO_REGISTER_CODE_ITEM( Instruction::NOP, Instruction::GOTO | 0xFF00); @@ -51,14 +51,14 @@ TEST_F(SuspendCheckTest, CFG1) { } TEST_F(SuspendCheckTest, CFG2) { - const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO_32, 0, 0); TestCode(data); } TEST_F(SuspendCheckTest, CFG3) { - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 0xFFFF, Instruction::RETURN_VOID); @@ -67,7 +67,7 @@ TEST_F(SuspendCheckTest, CFG3) { } TEST_F(SuspendCheckTest, CFG4) { - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_NE, 0xFFFF, Instruction::RETURN_VOID); @@ -76,7 +76,7 @@ TEST_F(SuspendCheckTest, CFG4) { } TEST_F(SuspendCheckTest, CFG5) { - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQZ, 0xFFFF, Instruction::RETURN_VOID); @@ -85,7 +85,7 @@ TEST_F(SuspendCheckTest, CFG5) { } TEST_F(SuspendCheckTest, CFG6) { - const uint16_t data[] = ONE_REGISTER_CODE_ITEM( + const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_NEZ, 0xFFFF, Instruction::RETURN_VOID); diff --git a/compiler/utils/test_dex_file_builder.h b/compiler/utils/test_dex_file_builder.h index 04fba51dc1..58f1ec7b08 100644 --- a/compiler/utils/test_dex_file_builder.h +++ b/compiler/utils/test_dex_file_builder.h @@ -27,6 +27,7 @@ #include <android-base/logging.h> #include "base/bit_utils.h" +#include "dex/art_dex_file_loader.h" #include "dex/dex_file_loader.h" #include "dex/standard_dex_file.h" @@ -233,7 +234,8 @@ class TestDexFileBuilder { static constexpr bool kVerify = false; static constexpr bool kVerifyChecksum = false; std::string error_msg; - std::unique_ptr<const DexFile> dex_file(DexFileLoader::Open( + const ArtDexFileLoader dex_file_loader; + std::unique_ptr<const DexFile> dex_file(dex_file_loader.Open( &dex_file_data_[0], dex_file_data_.size(), dex_location, diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp index 6bebf7d2da..dc71b9ba3e 100644 --- a/dex2oat/Android.bp +++ b/dex2oat/Android.bp @@ -129,6 +129,18 @@ art_cc_binary { static_libs: [ "libart-dex2oat", ], + + pgo: { + instrumentation: true, + profile_file: "dex2oat.profdata", + benchmarks: ["dex2oat"], + enable_profile_use: false, + cflags: [ + // Ignore frame-size increase resulting from instrumentation. + "-Wno-frame-larger-than=", + "-DART_PGO_INSTRUMENTATION", + ], + } } art_cc_binary { diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index dabe07f9ce..83a86567a3 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1807,9 +1807,7 @@ class Dex2Oat FINAL { // We do not decompile a RETURN_VOID_NO_BARRIER into a RETURN_VOID, as the quickening // optimization does not depend on the boot image (the optimization relies on not // having final fields in a class, which does not change for an app). - VdexFile::Unquicken(dex_files_, - input_vdex_file_->GetQuickeningInfo(), - /* decompile_return_instruction */ false); + input_vdex_file_->Unquicken(dex_files_, /* decompile_return_instruction */ false); } else { // Create the main VerifierDeps, here instead of in the compiler since we want to aggregate // the results for all the dex files, not just the results for the current dex file. @@ -2015,8 +2013,8 @@ class Dex2Oat FINAL { text_size, oat_writer->GetBssSize(), oat_writer->GetBssMethodsOffset(), - oat_writer->GetBssRootsOffset()); - + oat_writer->GetBssRootsOffset(), + oat_writer->GetVdexSize()); if (IsImage()) { // Update oat layout. DCHECK(image_writer_ != nullptr); @@ -3063,9 +3061,9 @@ static dex2oat::ReturnCode Dex2oat(int argc, char** argv) { int main(int argc, char** argv) { int result = static_cast<int>(art::Dex2oat(argc, argv)); // Everything was done, do an explicit exit here to avoid running Runtime destructors that take - // time (bug 10645725) unless we're a debug build or running on valgrind. Note: The Dex2Oat class - // should not destruct the runtime in this case. - if (!art::kIsDebugBuild && (RUNNING_ON_MEMORY_TOOL == 0)) { + // time (bug 10645725) unless we're a debug or instrumented build or running on valgrind. Note: + // The Dex2Oat class should not destruct the runtime in this case. + if (!art::kIsDebugBuild && !art::kIsPGOInstrumentation && (RUNNING_ON_MEMORY_TOOL == 0)) { _exit(result); } return result; diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc index 980363b1bb..05592f1806 100644 --- a/dex2oat/dex2oat_image_test.cc +++ b/dex2oat/dex2oat_image_test.cc @@ -29,6 +29,7 @@ #include "base/file_utils.h" #include "base/macros.h" #include "base/unix_file/fd_file.h" +#include "dex/art_dex_file_loader.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" #include "jit/profile_compilation_info.h" @@ -65,12 +66,13 @@ class Dex2oatImageTest : public CommonRuntimeTest { for (const std::string& dex : GetLibCoreDexFileNames()) { std::vector<std::unique_ptr<const DexFile>> dex_files; std::string error_msg; - CHECK(DexFileLoader::Open(dex.c_str(), - dex, - /*verify*/ true, - /*verify_checksum*/ false, - &error_msg, - &dex_files)) + const ArtDexFileLoader dex_file_loader; + CHECK(dex_file_loader.Open(dex.c_str(), + dex, + /*verify*/ true, + /*verify_checksum*/ false, + &error_msg, + &dex_files)) << error_msg; for (const std::unique_ptr<const DexFile>& dex_file : dex_files) { for (size_t i = 0; i < dex_file->NumMethodIds(); ++i) { diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index f176cc2839..8799540fd3 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -30,6 +30,7 @@ #include "base/macros.h" #include "base/mutex-inl.h" #include "bytecode_utils.h" +#include "dex/art_dex_file_loader.h" #include "dex/code_item_accessors-inl.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" @@ -108,6 +109,8 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { [](const OatFile&) {}); } + bool test_accepts_odex_file_on_failure = false; + template <typename T> void GenerateOdexForTest( const std::string& dex_location, @@ -124,7 +127,7 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { &error_msg, extra_args, use_fd); - bool success = (status == 0); + bool success = (WIFEXITED(status) && WEXITSTATUS(status) == 0); if (expect_success) { ASSERT_TRUE(success) << error_msg << std::endl << output_; @@ -146,16 +149,18 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { error_msg_ = error_msg; - // Verify there's no loadable odex file. - 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); + if (!test_accepts_odex_file_on_failure) { + // Verify there's no loadable odex file. + 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); + } } } @@ -680,7 +685,8 @@ class Dex2oatLayoutTest : public Dex2oatTest { const char* location = dex_location.c_str(); std::string error_msg; std::vector<std::unique_ptr<const DexFile>> dex_files; - ASSERT_TRUE(DexFileLoader::Open( + const ArtDexFileLoader dex_file_loader; + ASSERT_TRUE(dex_file_loader.Open( location, location, /* verify */ true, /* verify_checksum */ true, &error_msg, &dex_files)); EXPECT_EQ(dex_files.size(), 1U); std::unique_ptr<const DexFile>& dex_file = dex_files[0]; @@ -815,7 +821,8 @@ class Dex2oatLayoutTest : public Dex2oatTest { const char* location = dex_location.c_str(); std::vector<std::unique_ptr<const DexFile>> dex_files; - ASSERT_TRUE(DexFileLoader::Open( + const ArtDexFileLoader dex_file_loader; + ASSERT_TRUE(dex_file_loader.Open( location, location, /* verify */ true, /* verify_checksum */ true, &error_msg, &dex_files)); EXPECT_EQ(dex_files.size(), 1U); std::unique_ptr<const DexFile>& old_dex_file = dex_files[0]; @@ -993,7 +1000,12 @@ TEST_F(Dex2oatWatchdogTest, TestWatchdogOK) { TEST_F(Dex2oatWatchdogTest, TestWatchdogTrigger) { TEST_DISABLED_FOR_MEMORY_TOOL_VALGRIND(); // b/63052624 - TEST_DISABLED_WITHOUT_BAKER_READ_BARRIERS(); // b/63052624 + + // The watchdog is independent of dex2oat and will not delete intermediates. It is possible + // that the compilation succeeds and the file is completely written by the time the watchdog + // kills dex2oat (but the dex2oat threads must have been scheduled pretty badly). + test_accepts_odex_file_on_failure = true; + // Check with ten milliseconds. RunTest(false, { "--watchdog-timeout=10" }); } diff --git a/dex2oat/linker/elf_writer.h b/dex2oat/linker/elf_writer.h index 0eb36eda0f..a49112bfe6 100644 --- a/dex2oat/linker/elf_writer.h +++ b/dex2oat/linker/elf_writer.h @@ -55,11 +55,17 @@ class ElfWriter { virtual ~ElfWriter() {} virtual void Start() = 0; + // Prepares memory layout of the whole ELF file, and creates dynamic symbols + // which point to specific areas of interest (usually section begin and end). + // This is needed as multi-image needs to know the memory layout of all ELF + // files, before starting to write them. + // This method must be called before calling GetLoadedSize(). virtual void PrepareDynamicSection(size_t rodata_size, size_t text_size, size_t bss_size, size_t bss_methods_offset, - size_t bss_roots_offset) = 0; + size_t bss_roots_offset, + size_t dex_section_size) = 0; virtual void PrepareDebugInfo(const ArrayRef<const debug::MethodDebugInfo>& method_infos) = 0; virtual OutputStream* StartRoData() = 0; virtual void EndRoData(OutputStream* rodata) = 0; diff --git a/dex2oat/linker/elf_writer_quick.cc b/dex2oat/linker/elf_writer_quick.cc index aa64b7d59e..af11d5864d 100644 --- a/dex2oat/linker/elf_writer_quick.cc +++ b/dex2oat/linker/elf_writer_quick.cc @@ -99,7 +99,8 @@ class ElfWriterQuick FINAL : public ElfWriter { size_t text_size, size_t bss_size, size_t bss_methods_offset, - size_t bss_roots_offset) OVERRIDE; + size_t bss_roots_offset, + size_t dex_section_size) OVERRIDE; void PrepareDebugInfo(const ArrayRef<const debug::MethodDebugInfo>& method_infos) OVERRIDE; OutputStream* StartRoData() OVERRIDE; void EndRoData(OutputStream* rodata) OVERRIDE; @@ -183,7 +184,8 @@ void ElfWriterQuick<ElfTypes>::PrepareDynamicSection(size_t rodata_size, size_t text_size, size_t bss_size, size_t bss_methods_offset, - size_t bss_roots_offset) { + size_t bss_roots_offset, + size_t dex_section_size) { DCHECK_EQ(rodata_size_, 0u); rodata_size_ = rodata_size; DCHECK_EQ(text_size_, 0u); @@ -195,7 +197,8 @@ void ElfWriterQuick<ElfTypes>::PrepareDynamicSection(size_t rodata_size, text_size_, bss_size_, bss_methods_offset, - bss_roots_offset); + bss_roots_offset, + dex_section_size); } template <typename ElfTypes> diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h index 85145d3d64..067111824a 100644 --- a/dex2oat/linker/image_test.h +++ b/dex2oat/linker/image_test.h @@ -316,7 +316,8 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, text_size, oat_writer->GetBssSize(), oat_writer->GetBssMethodsOffset(), - oat_writer->GetBssRootsOffset()); + oat_writer->GetBssRootsOffset(), + oat_writer->GetVdexSize()); writer->UpdateOatFileLayout(i, elf_writer->GetLoadedSize(), diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index 16d70daddf..a81fa76e80 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -33,6 +33,7 @@ #include "class_table-inl.h" #include "compiled_method-inl.h" #include "debug/method_debug_info.h" +#include "dex/art_dex_file_loader.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" #include "dex/dex_file_types.h" @@ -57,6 +58,7 @@ #include "mirror/object-inl.h" #include "oat_quick_method_header.h" #include "os.h" +#include "quicken_info.h" #include "safe_map.h" #include "scoped_thread_state_change-inl.h" #include "type_lookup_table.h" @@ -2617,42 +2619,54 @@ bool OatWriter::WriteRodata(OutputStream* out) { return true; } -class OatWriter::WriteQuickeningInfoMethodVisitor : public DexMethodVisitor { +class OatWriter::WriteQuickeningInfoMethodVisitor { public: - WriteQuickeningInfoMethodVisitor(OatWriter* writer, - OutputStream* out, - uint32_t offset, - SafeMap<const uint8_t*, uint32_t>* offset_map) - : DexMethodVisitor(writer, offset), - out_(out), - written_bytes_(0u), - offset_map_(offset_map) {} + WriteQuickeningInfoMethodVisitor(OatWriter* writer, OutputStream* out) + : writer_(writer), + out_(out) {} - bool VisitMethod(size_t class_def_method_index ATTRIBUTE_UNUSED, const ClassDataItemIterator& it) - OVERRIDE { - uint32_t method_idx = it.GetMemberIndex(); - CompiledMethod* compiled_method = - writer_->compiler_driver_->GetCompiledMethod(MethodReference(dex_file_, method_idx)); + bool VisitDexMethods(const std::vector<const DexFile*>& dex_files) { + std::vector<uint8_t> empty_quicken_info; + { + // Since we need to be able to access by dex method index, put a one byte empty quicken info + // for any method that isn't quickened. + QuickenInfoTable::Builder empty_info(&empty_quicken_info, /*num_elements*/ 0u); + CHECK(!empty_quicken_info.empty()); + } + for (const DexFile* dex_file : dex_files) { + std::vector<uint32_t>* const offsets = + &quicken_info_offset_indices_.Put(dex_file, std::vector<uint32_t>())->second; + + // Every method needs an index in the table. + for (uint32_t method_idx = 0; method_idx < dex_file->NumMethodIds(); ++method_idx) { + ArrayRef<const uint8_t> map(empty_quicken_info); + + // Use the existing quicken info if it exists. + MethodReference method_ref(dex_file, method_idx); + CompiledMethod* compiled_method = writer_->compiler_driver_->GetCompiledMethod(method_ref); + if (compiled_method != nullptr && HasQuickeningInfo(compiled_method)) { + map = compiled_method->GetVmapTable(); + } - if (HasQuickeningInfo(compiled_method)) { - ArrayRef<const uint8_t> map = compiled_method->GetVmapTable(); - // Deduplication is already done on a pointer basis by the compiler driver, - // so we can simply compare the pointers to find out if things are duplicated. - if (offset_map_->find(map.data()) == offset_map_->end()) { - uint32_t length = map.size() * sizeof(map.front()); - offset_map_->Put(map.data(), written_bytes_); - if (!out_->WriteFully(&length, sizeof(length)) || - !out_->WriteFully(map.data(), length)) { - PLOG(ERROR) << "Failed to write quickening info for " - << dex_file_->PrettyMethod(it.GetMemberIndex()) << " to " - << out_->GetLocation(); + // The current approach prevents deduplication of quicken infos since each method index + // has one unique quicken info. Deduplication does not provide much savings for dex indices + // since they are rarely duplicated. + const uint32_t length = map.size() * sizeof(map.front()); + + // Record each index if required. written_bytes_ is the offset from the start of the + // quicken info data. + if (QuickenInfoOffsetTableAccessor::IsCoveredIndex(method_idx)) { + offsets->push_back(written_bytes_); + } + + if (!out_->WriteFully(map.data(), length)) { + PLOG(ERROR) << "Failed to write quickening info for " << method_ref.PrettyMethod() + << " to " << out_->GetLocation(); return false; } - written_bytes_ += sizeof(length) + length; - offset_ += sizeof(length) + length; + written_bytes_ += length; } } - return true; } @@ -2660,71 +2674,59 @@ class OatWriter::WriteQuickeningInfoMethodVisitor : public DexMethodVisitor { return written_bytes_; } + SafeMap<const DexFile*, std::vector<uint32_t>>& GetQuickenInfoOffsetIndicies() { + return quicken_info_offset_indices_; + } + + private: + OatWriter* const writer_; OutputStream* const out_; - size_t written_bytes_; - // Maps quickening map to its offset in the file. - SafeMap<const uint8_t*, uint32_t>* offset_map_; + size_t written_bytes_ = 0u; + // Map of offsets for quicken info related to method indices. + SafeMap<const DexFile*, std::vector<uint32_t>> quicken_info_offset_indices_; }; -class OatWriter::WriteQuickeningIndicesMethodVisitor { +class OatWriter::WriteQuickeningInfoOffsetsMethodVisitor { public: - WriteQuickeningIndicesMethodVisitor(OutputStream* out, - uint32_t quickening_info_bytes, - const SafeMap<const uint8_t*, uint32_t>& offset_map) + WriteQuickeningInfoOffsetsMethodVisitor( + OutputStream* out, + uint32_t start_offset, + SafeMap<const DexFile*, std::vector<uint32_t>>* quicken_info_offset_indices, + std::vector<uint32_t>* out_table_offsets) : out_(out), - quickening_info_bytes_(quickening_info_bytes), - written_bytes_(0u), - offset_map_(offset_map) {} + start_offset_(start_offset), + quicken_info_offset_indices_(quicken_info_offset_indices), + out_table_offsets_(out_table_offsets) {} - bool VisitDexMethods(const std::vector<const DexFile*>& dex_files, const CompilerDriver& driver) { + bool VisitDexMethods(const std::vector<const DexFile*>& dex_files) { for (const DexFile* dex_file : dex_files) { - const size_t class_def_count = dex_file->NumClassDefs(); - for (size_t class_def_index = 0; class_def_index != class_def_count; ++class_def_index) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); - const uint8_t* class_data = dex_file->GetClassData(class_def); - if (class_data == nullptr) { - continue; - } - for (ClassDataItemIterator class_it(*dex_file, class_data); - class_it.HasNext(); - class_it.Next()) { - if (!class_it.IsAtMethod() || class_it.GetMethodCodeItem() == nullptr) { - continue; - } - uint32_t method_idx = class_it.GetMemberIndex(); - CompiledMethod* compiled_method = - driver.GetCompiledMethod(MethodReference(dex_file, method_idx)); - const DexFile::CodeItem* code_item = class_it.GetMethodCodeItem(); - CodeItemDebugInfoAccessor accessor(*dex_file, code_item); - const uint32_t existing_debug_info_offset = accessor.DebugInfoOffset(); - // If the existing offset is already out of bounds (and not magic marker 0xFFFFFFFF) - // we will pretend the method has been quickened. - bool existing_offset_out_of_bounds = - (existing_debug_info_offset >= dex_file->Size() && - existing_debug_info_offset != 0xFFFFFFFF); - bool has_quickening_info = HasQuickeningInfo(compiled_method); - if (has_quickening_info || existing_offset_out_of_bounds) { - uint32_t new_debug_info_offset = - dex_file->Size() + quickening_info_bytes_ + written_bytes_; - // Abort if overflow. - CHECK_GE(new_debug_info_offset, dex_file->Size()); - const_cast<DexFile::CodeItem*>(code_item)->SetDebugInfoOffset(new_debug_info_offset); - uint32_t quickening_offset = has_quickening_info - ? offset_map_.Get(compiled_method->GetVmapTable().data()) - : VdexFile::kNoQuickeningInfoOffset; - if (!out_->WriteFully(&existing_debug_info_offset, - sizeof(existing_debug_info_offset)) || - !out_->WriteFully(&quickening_offset, sizeof(quickening_offset))) { - PLOG(ERROR) << "Failed to write quickening info for " - << dex_file->PrettyMethod(method_idx) << " to " - << out_->GetLocation(); - return false; - } - written_bytes_ += sizeof(existing_debug_info_offset) + sizeof(quickening_offset); - } - } + auto it = quicken_info_offset_indices_->find(dex_file); + DCHECK(it != quicken_info_offset_indices_->end()) << "Failed to find dex file " + << dex_file->GetLocation(); + const std::vector<uint32_t>* const offsets = &it->second; + + const uint32_t current_offset = start_offset_ + written_bytes_; + CHECK_ALIGNED_PARAM(current_offset, QuickenInfoOffsetTableAccessor::Alignment()); + + // Generate and write the data. + std::vector<uint8_t> table_data; + QuickenInfoOffsetTableAccessor::Builder builder(&table_data); + for (uint32_t offset : *offsets) { + builder.AddOffset(offset); + } + + // Store the offset since we need to put those after the dex file. Table offsets are relative + // to the start of the quicken info section. + out_table_offsets_->push_back(current_offset); + + const uint32_t length = table_data.size() * sizeof(table_data.front()); + if (!out_->WriteFully(table_data.data(), length)) { + PLOG(ERROR) << "Failed to write quickening offset table for " << dex_file->GetLocation() + << " to " << out_->GetLocation(); + return false; } + written_bytes_ += length; } return true; } @@ -2735,14 +2737,16 @@ class OatWriter::WriteQuickeningIndicesMethodVisitor { private: OutputStream* const out_; - const uint32_t quickening_info_bytes_; - size_t written_bytes_; - // Maps quickening map to its offset in the file. - const SafeMap<const uint8_t*, uint32_t>& offset_map_; + const uint32_t start_offset_; + size_t written_bytes_ = 0u; + // Maps containing the offsets for the tables. + SafeMap<const DexFile*, std::vector<uint32_t>>* const quicken_info_offset_indices_; + std::vector<uint32_t>* const out_table_offsets_; }; bool OatWriter::WriteQuickeningInfo(OutputStream* vdex_out) { size_t initial_offset = vdex_size_; + // Make sure the table is properly aligned. size_t start_offset = RoundUp(initial_offset, 4u); off_t actual_offset = vdex_out->Seek(start_offset, kSeekSet); @@ -2753,36 +2757,71 @@ bool OatWriter::WriteQuickeningInfo(OutputStream* vdex_out) { return false; } - if (compiler_driver_->GetCompilerOptions().IsAnyCompilationEnabled()) { + size_t current_offset = start_offset; + if (compiler_driver_->GetCompilerOptions().IsQuickeningCompilationEnabled()) { std::vector<uint32_t> dex_files_indices; - SafeMap<const uint8_t*, uint32_t> offset_map; - WriteQuickeningInfoMethodVisitor visitor1(this, vdex_out, start_offset, &offset_map); - if (!VisitDexMethods(&visitor1)) { + WriteQuickeningInfoMethodVisitor write_quicken_info_visitor(this, vdex_out); + if (!write_quicken_info_visitor.VisitDexMethods(*dex_files_)) { PLOG(ERROR) << "Failed to write the vdex quickening info. File: " << vdex_out->GetLocation(); return false; } - if (visitor1.GetNumberOfWrittenBytes() > 0) { - WriteQuickeningIndicesMethodVisitor visitor2(vdex_out, - visitor1.GetNumberOfWrittenBytes(), - offset_map); - if (!visitor2.VisitDexMethods(*dex_files_, *compiler_driver_)) { - PLOG(ERROR) << "Failed to write the vdex quickening info. File: " - << vdex_out->GetLocation(); + uint32_t quicken_info_offset = write_quicken_info_visitor.GetNumberOfWrittenBytes(); + current_offset = current_offset + quicken_info_offset; + uint32_t before_offset = current_offset; + current_offset = RoundUp(current_offset, QuickenInfoOffsetTableAccessor::Alignment()); + const size_t extra_bytes = current_offset - before_offset; + quicken_info_offset += extra_bytes; + actual_offset = vdex_out->Seek(current_offset, kSeekSet); + if (actual_offset != static_cast<off_t>(current_offset)) { + PLOG(ERROR) << "Failed to seek to quickening offset table section. Actual: " << actual_offset + << " Expected: " << current_offset + << " Output: " << vdex_out->GetLocation(); + return false; + } + + std::vector<uint32_t> table_offsets; + WriteQuickeningInfoOffsetsMethodVisitor table_visitor( + vdex_out, + quicken_info_offset, + &write_quicken_info_visitor.GetQuickenInfoOffsetIndicies(), + /*out*/ &table_offsets); + if (!table_visitor.VisitDexMethods(*dex_files_)) { + PLOG(ERROR) << "Failed to write the vdex quickening info. File: " + << vdex_out->GetLocation(); + return false; + } + + CHECK_EQ(table_offsets.size(), dex_files_->size()); + + current_offset += table_visitor.GetNumberOfWrittenBytes(); + + // Store the offset table offset as a preheader for each dex. + size_t index = 0; + for (const OatDexFile& oat_dex_file : oat_dex_files_) { + const off_t desired_offset = oat_dex_file.dex_file_offset_ - + sizeof(VdexFile::QuickeningTableOffsetType); + actual_offset = vdex_out->Seek(desired_offset, kSeekSet); + if (actual_offset != desired_offset) { + PLOG(ERROR) << "Failed to seek to before dex file for writing offset table offset: " + << actual_offset << " Expected: " << desired_offset + << " Output: " << vdex_out->GetLocation(); return false; } - - if (!vdex_out->Flush()) { - PLOG(ERROR) << "Failed to flush stream after writing quickening info." + uint32_t offset = table_offsets[index]; + if (!vdex_out->WriteFully(reinterpret_cast<const uint8_t*>(&offset), sizeof(offset))) { + PLOG(ERROR) << "Failed to write verifier deps." << " File: " << vdex_out->GetLocation(); return false; } - size_quickening_info_ = visitor1.GetNumberOfWrittenBytes() + - visitor2.GetNumberOfWrittenBytes(); - } else { - // We know we did not quicken. - size_quickening_info_ = 0; + ++index; } + if (!vdex_out->Flush()) { + PLOG(ERROR) << "Failed to flush stream after writing quickening info." + << " File: " << vdex_out->GetLocation(); + return false; + } + size_quickening_info_ = current_offset - start_offset; } else { // We know we did not quicken. size_quickening_info_ = 0; @@ -3357,9 +3396,15 @@ bool OatWriter::SeekToDexFile(OutputStream* out, File* file, OatDexFile* oat_dex // Dex files are required to be 4 byte aligned. size_t initial_offset = vdex_size_; size_t start_offset = RoundUp(initial_offset, 4); - size_t file_offset = start_offset; size_dex_file_alignment_ += start_offset - initial_offset; + // Leave extra room for the quicken offset table offset. + start_offset += sizeof(VdexFile::QuickeningTableOffsetType); + // TODO: Not count the offset as part of alignment. + size_dex_file_alignment_ += sizeof(VdexFile::QuickeningTableOffsetType); + + size_t file_offset = start_offset; + // Seek to the start of the dex file and flush any pending operations in the stream. // Verify that, after flushing the stream, the file is at the same offset as the stream. off_t actual_offset = out->Seek(file_offset, kSeekSet); @@ -3392,6 +3437,7 @@ bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_fil std::string error_msg; std::string location(oat_dex_file->GetLocation()); std::unique_ptr<const DexFile> dex_file; + const ArtDexFileLoader dex_file_loader; if (oat_dex_file->source_.IsZipEntry()) { ZipEntry* zip_entry = oat_dex_file->source_.GetZipEntry(); std::unique_ptr<MemMap> mem_map( @@ -3400,12 +3446,12 @@ bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_fil LOG(ERROR) << "Failed to extract dex file to mem map for layout: " << error_msg; return false; } - dex_file = DexFileLoader::Open(location, - zip_entry->GetCrc32(), - std::move(mem_map), - /* verify */ true, - /* verify_checksum */ true, - &error_msg); + dex_file = dex_file_loader.Open(location, + zip_entry->GetCrc32(), + std::move(mem_map), + /* verify */ true, + /* verify_checksum */ true, + &error_msg); } else if (oat_dex_file->source_.IsRawFile()) { File* raw_file = oat_dex_file->source_.GetRawFile(); int dup_fd = dup(raw_file->Fd()); @@ -3413,7 +3459,7 @@ bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_fil PLOG(ERROR) << "Failed to dup dex file descriptor (" << raw_file->Fd() << ") at " << location; return false; } - dex_file = DexFileLoader::OpenDex( + dex_file = dex_file_loader.OpenDex( dup_fd, location, /* verify */ true, /* verify_checksum */ true, &error_msg); } else { // The source data is a vdex file. @@ -3426,14 +3472,14 @@ bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_fil DCHECK(ValidateDexFileHeader(raw_dex_file, oat_dex_file->GetLocation())); const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_dex_file); // Since the source may have had its layout changed, or may be quickened, don't verify it. - dex_file = DexFileLoader::Open(raw_dex_file, - header->file_size_, - location, - oat_dex_file->dex_file_location_checksum_, - nullptr, - /* verify */ false, - /* verify_checksum */ false, - &error_msg); + dex_file = dex_file_loader.Open(raw_dex_file, + header->file_size_, + location, + oat_dex_file->dex_file_location_checksum_, + nullptr, + /* verify */ false, + /* verify_checksum */ false, + &error_msg); } if (dex_file == nullptr) { LOG(ERROR) << "Failed to open dex file for layout: " << error_msg; @@ -3653,6 +3699,7 @@ bool OatWriter::OpenDexFiles( << " error: " << error_msg; return false; } + const ArtDexFileLoader dex_file_loader; std::vector<std::unique_ptr<const DexFile>> dex_files; for (OatDexFile& oat_dex_file : oat_dex_files_) { const uint8_t* raw_dex_file = @@ -3674,14 +3721,14 @@ bool OatWriter::OpenDexFiles( } // Now, open the dex file. - dex_files.emplace_back(DexFileLoader::Open(raw_dex_file, - oat_dex_file.dex_file_size_, - oat_dex_file.GetLocation(), - oat_dex_file.dex_file_location_checksum_, - /* oat_dex_file */ nullptr, - verify, - verify, - &error_msg)); + dex_files.emplace_back(dex_file_loader.Open(raw_dex_file, + oat_dex_file.dex_file_size_, + oat_dex_file.GetLocation(), + 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() << " Error: " << error_msg; @@ -3689,7 +3736,7 @@ bool OatWriter::OpenDexFiles( } // Set the class_offsets size now that we have easy access to the DexFile and - // it has been verified in DexFileLoader::Open. + // it has been verified in dex_file_loader.Open. oat_dex_file.class_offsets_.resize(dex_files.back()->GetHeader().class_defs_size_); } diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h index ba29e3b3a2..824b395b02 100644 --- a/dex2oat/linker/oat_writer.h +++ b/dex2oat/linker/oat_writer.h @@ -230,6 +230,10 @@ class OatWriter { return bss_roots_offset_; } + size_t GetVdexSize() const { + return vdex_size_; + } + size_t GetOatDataOffset() const { return oat_data_offset_; } @@ -271,7 +275,7 @@ class OatWriter { class WriteMapMethodVisitor; class WriteMethodInfoVisitor; class WriteQuickeningInfoMethodVisitor; - class WriteQuickeningIndicesMethodVisitor; + class WriteQuickeningInfoOffsetsMethodVisitor; // Visit all the methods in all the compiled dex files in their definition order // with a given DexMethodVisitor. diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index 488806092b..321a2e4d46 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -219,7 +219,8 @@ class OatTest : public CommonCompilerTest { text_size, oat_writer.GetBssSize(), oat_writer.GetBssMethodsOffset(), - oat_writer.GetBssRootsOffset()); + oat_writer.GetBssRootsOffset(), + oat_writer.GetVdexSize()); std::unique_ptr<BufferedOutputStream> vdex_out = std::make_unique<BufferedOutputStream>(std::make_unique<FileOutputStream>(vdex_file)); @@ -658,7 +659,11 @@ void OatTest::TestDexFileInput(bool verify, bool low_4gb, bool use_profile) { ASSERT_EQ(dex_file2_data->GetLocation(), opened_dex_file2->GetLocation()); const VdexFile::Header &vdex_header = opened_oat_file->GetVdexFile()->GetHeader(); - ASSERT_EQ(vdex_header.GetQuickeningInfoSize(), 0u); + if (!compiler_driver_->GetCompilerOptions().IsQuickeningCompilationEnabled()) { + // If quickening is enabled we will always write the table since there is no special logic that + // checks for all methods not being quickened (not worth the complexity). + ASSERT_EQ(vdex_header.GetQuickeningInfoSize(), 0u); + } int64_t actual_vdex_size = vdex_file.GetFile()->GetLength(); ASSERT_GE(actual_vdex_size, 0); diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc index 2c98e12741..1518e1d205 100644 --- a/dexdump/dexdump.cc +++ b/dexdump/dexdump.cc @@ -44,6 +44,7 @@ #include "android-base/stringprintf.h" +#include "dex/art_dex_file_loader.h" #include "dex/code_item_accessors-no_art-inl.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_exception_helpers.h" @@ -1186,7 +1187,7 @@ static void dumpBytecodes(const DexFile* pDexFile, u4 idx, */ static void dumpCode(const DexFile* pDexFile, u4 idx, u4 flags, const DexFile::CodeItem* pCode, u4 codeOffset) { - CodeItemDebugInfoAccessor accessor(*pDexFile, pCode, pDexFile->GetDebugInfoOffset(pCode)); + CodeItemDebugInfoAccessor accessor(*pDexFile, pCode, idx); fprintf(gOutFile, " registers : %d\n", accessor.RegistersSize()); fprintf(gOutFile, " ins : %d\n", accessor.InsSize()); @@ -1879,8 +1880,10 @@ int processFile(const char* fileName) { // all of which are Zip archives with "classes.dex" inside. const bool kVerifyChecksum = !gOptions.ignoreBadChecksum; std::string error_msg; + // TODO: Use DexFileLoader when that is implemented. + const ArtDexFileLoader dex_file_loader; std::vector<std::unique_ptr<const DexFile>> dex_files; - if (!DexFileLoader::Open( + if (!dex_file_loader.Open( fileName, fileName, /* verify */ true, 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. diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp index a02f75ad00..4f5d81095f 100644 --- a/dexlayout/Android.bp +++ b/dexlayout/Android.bp @@ -34,6 +34,13 @@ art_cc_library { name: "libart-dexlayout", defaults: ["libart-dexlayout-defaults"], shared_libs: ["libart"], + + pgo: { + instrumentation: true, + profile_file: "dex2oat.profdata", + benchmarks: ["dex2oat"], + enable_profile_use: false, + } } art_cc_library { diff --git a/dexlayout/compact_dex_writer.cc b/dexlayout/compact_dex_writer.cc index 1c5b16d84b..d1dc6587c0 100644 --- a/dexlayout/compact_dex_writer.cc +++ b/dexlayout/compact_dex_writer.cc @@ -16,10 +16,144 @@ #include "compact_dex_writer.h" +#include "base/logging.h" +#include "base/time_utils.h" +#include "dex/compact_dex_debug_info.h" #include "dex/compact_dex_file.h" +#include "dexlayout.h" namespace art { +uint32_t CompactDexWriter::WriteDebugInfoOffsetTable(uint32_t offset) { + const uint32_t start_offset = offset; + const dex_ir::Collections& collections = header_->GetCollections(); + // Debug offsets for method indexes. 0 means no debug info. + std::vector<uint32_t> debug_info_offsets(collections.MethodIdsSize(), 0u); + + static constexpr InvokeType invoke_types[] = { + kDirect, + kVirtual + }; + + for (InvokeType invoke_type : invoke_types) { + for (const std::unique_ptr<dex_ir::ClassDef>& class_def : collections.ClassDefs()) { + // Skip classes that are not defined in this dex file. + dex_ir::ClassData* class_data = class_def->GetClassData(); + if (class_data == nullptr) { + continue; + } + for (auto& method : *(invoke_type == InvokeType::kDirect + ? class_data->DirectMethods() + : class_data->VirtualMethods())) { + const dex_ir::MethodId* method_id = method->GetMethodId(); + dex_ir::CodeItem* code_item = method->GetCodeItem(); + if (code_item != nullptr && code_item->DebugInfo() != nullptr) { + const uint32_t debug_info_offset = code_item->DebugInfo()->GetOffset(); + const uint32_t method_idx = method_id->GetIndex(); + if (debug_info_offsets[method_idx] != 0u) { + CHECK_EQ(debug_info_offset, debug_info_offsets[method_idx]); + } + debug_info_offsets[method_idx] = debug_info_offset; + } + } + } + } + + std::vector<uint8_t> data; + debug_info_base_ = 0u; + debug_info_offsets_table_offset_ = 0u; + CompactDexDebugInfoOffsetTable::Build(debug_info_offsets, + &data, + &debug_info_base_, + &debug_info_offsets_table_offset_); + // Align the table and write it out. + offset = RoundUp(offset, CompactDexDebugInfoOffsetTable::kAlignment); + debug_info_offsets_pos_ = offset; + offset += Write(data.data(), data.size(), offset); + + // Verify that the whole table decodes as expected and measure average performance. + const bool kMeasureAndTestOutput = dex_layout_->GetOptions().verify_output_; + if (kMeasureAndTestOutput && !debug_info_offsets.empty()) { + uint64_t start_time = NanoTime(); + CompactDexDebugInfoOffsetTable::Accessor accessor(mem_map_->Begin() + debug_info_offsets_pos_, + debug_info_base_, + debug_info_offsets_table_offset_); + + for (size_t i = 0; i < debug_info_offsets.size(); ++i) { + CHECK_EQ(accessor.GetDebugInfoOffset(i), debug_info_offsets[i]); + } + uint64_t end_time = NanoTime(); + VLOG(dex) << "Average lookup time (ns) for debug info offsets: " + << (end_time - start_time) / debug_info_offsets.size(); + } + + return offset - start_offset; +} + +uint32_t CompactDexWriter::WriteCodeItem(dex_ir::CodeItem* code_item, + uint32_t offset, + bool reserve_only) { + DCHECK(code_item != nullptr); + const uint32_t start_offset = offset; + offset = RoundUp(offset, CompactDexFile::CodeItem::kAlignment); + ProcessOffset(&offset, code_item); + + CompactDexFile::CodeItem disk_code_item; + disk_code_item.registers_size_ = code_item->RegistersSize(); + disk_code_item.ins_size_ = code_item->InsSize(); + disk_code_item.outs_size_ = code_item->OutsSize(); + disk_code_item.tries_size_ = code_item->TriesSize(); + disk_code_item.insns_size_in_code_units_ = code_item->InsnsSize(); + // Avoid using sizeof so that we don't write the fake instruction array at the end of the code + // item. + offset += Write(&disk_code_item, + OFFSETOF_MEMBER(CompactDexFile::CodeItem, insns_), + offset); + // Write the instructions. + offset += Write(code_item->Insns(), code_item->InsnsSize() * sizeof(uint16_t), offset); + // Write the post instruction data. + offset += WriteCodeItemPostInstructionData(code_item, offset, reserve_only); + return offset - start_offset; +} + +void CompactDexWriter::SortDebugInfosByMethodIndex() { + dex_ir::Collections& collections = header_->GetCollections(); + static constexpr InvokeType invoke_types[] = { + kDirect, + kVirtual + }; + std::map<const dex_ir::DebugInfoItem*, uint32_t> method_idx_map; + for (InvokeType invoke_type : invoke_types) { + for (std::unique_ptr<dex_ir::ClassDef>& class_def : collections.ClassDefs()) { + // Skip classes that are not defined in this dex file. + dex_ir::ClassData* class_data = class_def->GetClassData(); + if (class_data == nullptr) { + continue; + } + for (auto& method : *(invoke_type == InvokeType::kDirect + ? class_data->DirectMethods() + : class_data->VirtualMethods())) { + const dex_ir::MethodId* method_id = method->GetMethodId(); + dex_ir::CodeItem* code_item = method->GetCodeItem(); + if (code_item != nullptr && code_item->DebugInfo() != nullptr) { + const dex_ir::DebugInfoItem* debug_item = code_item->DebugInfo(); + method_idx_map.insert(std::make_pair(debug_item, method_id->GetIndex())); + } + } + } + } + std::sort(collections.DebugInfoItems().begin(), + collections.DebugInfoItems().end(), + [&](const std::unique_ptr<dex_ir::DebugInfoItem>& a, + const std::unique_ptr<dex_ir::DebugInfoItem>& b) { + auto it_a = method_idx_map.find(a.get()); + auto it_b = method_idx_map.find(b.get()); + uint32_t idx_a = it_a != method_idx_map.end() ? it_a->second : 0u; + uint32_t idx_b = it_b != method_idx_map.end() ? it_b->second : 0u; + return idx_a < idx_b; + }); +} + void CompactDexWriter::WriteHeader() { CompactDexFile::Header header; CompactDexFile::WriteMagic(&header.magic_[0]); @@ -49,6 +183,11 @@ void CompactDexWriter::WriteHeader() { header.class_defs_off_ = collections.ClassDefsOffset(); header.data_size_ = header_->DataSize(); header.data_off_ = header_->DataOffset(); + + // Compact dex specific flags. + header.debug_info_offsets_pos_ = debug_info_offsets_pos_; + header.debug_info_offsets_table_offset_ = debug_info_offsets_table_offset_; + header.debug_info_base_ = debug_info_base_; header.feature_flags_ = 0u; // In cases where apps are converted to cdex during install, maintain feature flags so that // the verifier correctly verifies apps that aren't targetting default methods. @@ -62,4 +201,103 @@ size_t CompactDexWriter::GetHeaderSize() const { return sizeof(CompactDexFile::Header); } +void CompactDexWriter::WriteMemMap() { + // Starting offset is right after the header. + uint32_t offset = GetHeaderSize(); + + dex_ir::Collections& collection = header_->GetCollections(); + + // Based on: https://source.android.com/devices/tech/dalvik/dex-format + // Since the offsets may not be calculated already, the writing must be done in the correct order. + const uint32_t string_ids_offset = offset; + offset += WriteStringIds(offset, /*reserve_only*/ true); + offset += WriteTypeIds(offset); + const uint32_t proto_ids_offset = offset; + offset += WriteProtoIds(offset, /*reserve_only*/ true); + offset += WriteFieldIds(offset); + offset += WriteMethodIds(offset); + const uint32_t class_defs_offset = offset; + offset += WriteClassDefs(offset, /*reserve_only*/ true); + const uint32_t call_site_ids_offset = offset; + offset += WriteCallSiteIds(offset, /*reserve_only*/ true); + offset += WriteMethodHandles(offset); + + uint32_t data_offset_ = 0u; + if (compute_offsets_) { + // Data section. + offset = RoundUp(offset, kDataSectionAlignment); + data_offset_ = offset; + } + + // Write code item first to minimize the space required for encoded methods. + // For cdex, the code items don't depend on the debug info. + offset += WriteCodeItems(offset, /*reserve_only*/ false); + + // Sort the debug infos by method index order, this reduces size by ~0.1% by reducing the size of + // the debug info offset table. + SortDebugInfosByMethodIndex(); + offset += WriteDebugInfoItems(offset); + + offset += WriteEncodedArrays(offset); + offset += WriteAnnotations(offset); + offset += WriteAnnotationSets(offset); + offset += WriteAnnotationSetRefs(offset); + offset += WriteAnnotationsDirectories(offset); + offset += WriteTypeLists(offset); + offset += WriteClassDatas(offset); + offset += WriteStringDatas(offset); + + // Write delayed id sections that depend on data sections. + WriteStringIds(string_ids_offset, /*reserve_only*/ false); + WriteProtoIds(proto_ids_offset, /*reserve_only*/ false); + WriteClassDefs(class_defs_offset, /*reserve_only*/ false); + WriteCallSiteIds(call_site_ids_offset, /*reserve_only*/ false); + + // Write the map list. + if (compute_offsets_) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeMapList)); + collection.SetMapListOffset(offset); + } else { + offset = collection.MapListOffset(); + } + offset += GenerateAndWriteMapItems(offset); + offset = RoundUp(offset, kDataSectionAlignment); + + // Map items are included in the data section. + if (compute_offsets_) { + header_->SetDataSize(offset - data_offset_); + if (header_->DataSize() != 0) { + // Offset must be zero when the size is zero. + header_->SetDataOffset(data_offset_); + } else { + header_->SetDataOffset(0u); + } + } + + // Write link data if it exists. + const std::vector<uint8_t>& link_data = collection.LinkData(); + if (link_data.size() > 0) { + CHECK_EQ(header_->LinkSize(), static_cast<uint32_t>(link_data.size())); + if (compute_offsets_) { + header_->SetLinkOffset(offset); + } + offset += Write(&link_data[0], link_data.size(), header_->LinkOffset()); + } + + // Write debug info offset table last to make dex file verifier happy. + offset += WriteDebugInfoOffsetTable(offset); + + // Write header last. + if (compute_offsets_) { + header_->SetFileSize(offset); + } + WriteHeader(); + + if (dex_layout_->GetOptions().update_checksum_) { + header_->SetChecksum(DexFile::CalculateChecksum(mem_map_->Begin(), offset)); + // Rewrite the header with the calculated checksum. + WriteHeader(); + } +} + } // namespace art diff --git a/dexlayout/compact_dex_writer.h b/dexlayout/compact_dex_writer.h index d13333bb18..37f6ff11a0 100644 --- a/dexlayout/compact_dex_writer.h +++ b/dexlayout/compact_dex_writer.h @@ -33,13 +33,30 @@ class CompactDexWriter : public DexWriter { compact_dex_level_(compact_dex_level) {} protected: + void WriteMemMap() OVERRIDE; + void WriteHeader() OVERRIDE; size_t GetHeaderSize() const OVERRIDE; + uint32_t WriteDebugInfoOffsetTable(uint32_t offset); + const CompactDexLevel compact_dex_level_; + uint32_t WriteCodeItem(dex_ir::CodeItem* code_item, uint32_t offset, bool reserve_only) OVERRIDE; + + void SortDebugInfosByMethodIndex(); + private: + // Position in the compact dex file for the debug info table data starts. + uint32_t debug_info_offsets_pos_ = 0u; + + // Offset into the debug info table data where the lookup table is. + uint32_t debug_info_offsets_table_offset_ = 0u; + + // Base offset of where debug info starts in the dex file. + uint32_t debug_info_base_ = 0u; + DISALLOW_COPY_AND_ASSIGN(CompactDexWriter); }; diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc index 2191ea601f..0a59cc9ba2 100644 --- a/dexlayout/dex_ir.cc +++ b/dexlayout/dex_ir.cc @@ -566,8 +566,10 @@ ParameterAnnotation* Collections::GenerateParameterAnnotation( } CodeItem* Collections::CreateCodeItem(const DexFile& dex_file, - const DexFile::CodeItem& disk_code_item, uint32_t offset) { - CodeItemDebugInfoAccessor accessor(dex_file, &disk_code_item); + const DexFile::CodeItem& disk_code_item, + uint32_t offset, + uint32_t dex_method_index) { + CodeItemDebugInfoAccessor accessor(dex_file, &disk_code_item, dex_method_index); const uint16_t registers_size = accessor.RegistersSize(); const uint16_t ins_size = accessor.InsSize(); const uint16_t outs_size = accessor.OutsSize(); @@ -705,7 +707,10 @@ MethodItem* Collections::GenerateMethodItem(const DexFile& dex_file, ClassDataIt DebugInfoItem* debug_info = nullptr; if (disk_code_item != nullptr) { if (code_item == nullptr) { - code_item = CreateCodeItem(dex_file, *disk_code_item, cdii.GetMethodCodeItemOffset()); + code_item = CreateCodeItem(dex_file, + *disk_code_item, + cdii.GetMethodCodeItemOffset(), + cdii.GetMemberIndex()); } debug_info = code_item->DebugInfo(); } diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h index 6797fa5dd6..ca47b348f1 100644 --- a/dexlayout/dex_ir.h +++ b/dexlayout/dex_ir.h @@ -133,6 +133,7 @@ template<class T> class CollectionVector : public CollectionBase<T> { uint32_t Size() const { return collection_.size(); } Vector& Collection() { return collection_; } + const Vector& Collection() const { return collection_; } protected: Vector collection_; @@ -230,6 +231,8 @@ class Collections { CollectionVector<CodeItem>::Vector& CodeItems() { return code_items_.Collection(); } CollectionVector<ClassData>::Vector& ClassDatas() { return class_datas_.Collection(); } + const CollectionVector<ClassDef>::Vector& ClassDefs() const { return class_defs_.Collection(); } + void CreateStringId(const DexFile& dex_file, uint32_t i); void CreateTypeId(const DexFile& dex_file, uint32_t i); void CreateProtoId(const DexFile& dex_file, uint32_t i); @@ -251,8 +254,10 @@ class Collections { const DexFile::AnnotationSetItem* disk_annotations_item, uint32_t offset); AnnotationsDirectoryItem* CreateAnnotationsDirectoryItem(const DexFile& dex_file, const DexFile::AnnotationsDirectoryItem* disk_annotations_item, uint32_t offset); - CodeItem* CreateCodeItem( - const DexFile& dex_file, const DexFile::CodeItem& disk_code_item, uint32_t offset); + CodeItem* CreateCodeItem(const DexFile& dex_file, + const DexFile::CodeItem& disk_code_item, + uint32_t offset, + uint32_t dex_method_index); ClassData* CreateClassData(const DexFile& dex_file, const uint8_t* encoded_data, uint32_t offset); void AddAnnotationsFromMapListSection(const DexFile& dex_file, uint32_t start_offset, diff --git a/dexlayout/dex_writer.cc b/dexlayout/dex_writer.cc index 489a6b15ba..6e1cb62f0b 100644 --- a/dexlayout/dex_writer.cc +++ b/dexlayout/dex_writer.cc @@ -30,25 +30,6 @@ namespace art { -static constexpr uint32_t kDataSectionAlignment = sizeof(uint32_t) * 2; -static constexpr uint32_t kDexSectionWordAlignment = 4; - -static constexpr uint32_t SectionAlignment(DexFile::MapItemType type) { - switch (type) { - case DexFile::kDexTypeClassDataItem: - case DexFile::kDexTypeStringDataItem: - case DexFile::kDexTypeDebugInfoItem: - case DexFile::kDexTypeAnnotationItem: - case DexFile::kDexTypeEncodedArrayItem: - return alignof(uint8_t); - - default: - // All other sections are kDexAlignedSection. - return kDexSectionWordAlignment; - } -} - - size_t EncodeIntValue(int32_t value, uint8_t* buffer) { size_t length = 0; if (value >= 0) { @@ -526,69 +507,96 @@ uint32_t DexWriter::WriteDebugInfoItems(uint32_t offset) { return offset - start; } +uint32_t DexWriter::WriteCodeItemPostInstructionData(dex_ir::CodeItem* code_item, + uint32_t offset, + bool reserve_only) { + const uint32_t start_offset = offset; + if (code_item->TriesSize() != 0) { + // Make sure the try items are properly aligned. + offset = RoundUp(offset, kDexTryItemAlignment); + // Write try items. + for (std::unique_ptr<const dex_ir::TryItem>& try_item : *code_item->Tries()) { + DexFile::TryItem disk_try_item; + if (!reserve_only) { + disk_try_item.start_addr_ = try_item->StartAddr(); + disk_try_item.insn_count_ = try_item->InsnCount(); + disk_try_item.handler_off_ = try_item->GetHandlers()->GetListOffset(); + } + offset += Write(&disk_try_item, sizeof(disk_try_item), offset); + } + size_t max_offset = offset; + // Leave offset pointing to the end of the try items. + UNUSED(WriteUleb128(code_item->Handlers()->size(), offset)); + for (std::unique_ptr<const dex_ir::CatchHandler>& handlers : *code_item->Handlers()) { + size_t list_offset = offset + handlers->GetListOffset(); + uint32_t size = handlers->HasCatchAll() ? (handlers->GetHandlers()->size() - 1) * -1 : + handlers->GetHandlers()->size(); + list_offset += WriteSleb128(size, list_offset); + for (std::unique_ptr<const dex_ir::TypeAddrPair>& handler : *handlers->GetHandlers()) { + if (handler->GetTypeId() != nullptr) { + list_offset += WriteUleb128(handler->GetTypeId()->GetIndex(), list_offset); + } + list_offset += WriteUleb128(handler->GetAddress(), list_offset); + } + // TODO: Clean this up to write the handlers in address order. + max_offset = std::max(max_offset, list_offset); + } + offset = max_offset; + } + + return offset - start_offset; +} + +uint32_t DexWriter::WriteCodeItem(dex_ir::CodeItem* code_item, + uint32_t offset, + bool reserve_only) { + DCHECK(code_item != nullptr); + const uint32_t start_offset = offset; + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeCodeItem)); + ProcessOffset(&offset, code_item); + + StandardDexFile::CodeItem disk_code_item; + if (!reserve_only) { + disk_code_item.registers_size_ = code_item->RegistersSize(); + disk_code_item.ins_size_ = code_item->InsSize(); + disk_code_item.outs_size_ = code_item->OutsSize(); + disk_code_item.tries_size_ = code_item->TriesSize(); + disk_code_item.debug_info_off_ = code_item->DebugInfo() == nullptr + ? 0 + : code_item->DebugInfo()->GetOffset(); + disk_code_item.insns_size_in_code_units_ = code_item->InsnsSize(); + } + // Avoid using sizeof so that we don't write the fake instruction array at the end of the code + // item. + offset += Write(&disk_code_item, + OFFSETOF_MEMBER(StandardDexFile::CodeItem, insns_), + offset); + // Write the instructions. + offset += Write(code_item->Insns(), code_item->InsnsSize() * sizeof(uint16_t), offset); + // Write the post instruction data. + offset += WriteCodeItemPostInstructionData(code_item, offset, reserve_only); + return offset - start_offset; +} + uint32_t DexWriter::WriteCodeItems(uint32_t offset, bool reserve_only) { DexLayoutSection* code_section = nullptr; if (!reserve_only && dex_layout_ != nullptr) { code_section = &dex_layout_->GetSections().sections_[static_cast<size_t>( DexLayoutSections::SectionType::kSectionTypeCode)]; } - uint16_t uint16_buffer[4] = {}; - uint32_t uint32_buffer[2] = {}; uint32_t start = offset; for (auto& code_item : header_->GetCollections().CodeItems()) { - offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeCodeItem)); - ProcessOffset(&offset, code_item.get()); - if (!reserve_only) { - uint16_buffer[0] = code_item->RegistersSize(); - uint16_buffer[1] = code_item->InsSize(); - uint16_buffer[2] = code_item->OutsSize(); - uint16_buffer[3] = code_item->TriesSize(); - uint32_buffer[0] = code_item->DebugInfo() == nullptr ? 0 : - code_item->DebugInfo()->GetOffset(); - uint32_buffer[1] = code_item->InsnsSize(); - // Only add the section hotness info once. - if (code_section != nullptr) { - auto it = dex_layout_->LayoutHotnessInfo().code_item_layout_.find(code_item.get()); - if (it != dex_layout_->LayoutHotnessInfo().code_item_layout_.end()) { - code_section->parts_[static_cast<size_t>(it->second)].CombineSection( - code_item->GetOffset(), code_item->GetOffset() + code_item->GetSize()); - } - } - } - offset += Write(uint16_buffer, 4 * sizeof(uint16_t), offset); - offset += Write(uint32_buffer, 2 * sizeof(uint32_t), offset); - offset += Write(code_item->Insns(), code_item->InsnsSize() * sizeof(uint16_t), offset); - if (code_item->TriesSize() != 0) { - if (code_item->InsnsSize() % 2 != 0) { - uint16_t padding[1] = { 0 }; - offset += Write(padding, sizeof(uint16_t), offset); - } - uint32_t start_addr[1]; - uint16_t insn_count_and_handler_off[2]; - for (std::unique_ptr<const dex_ir::TryItem>& try_item : *code_item->Tries()) { - start_addr[0] = try_item->StartAddr(); - insn_count_and_handler_off[0] = try_item->InsnCount(); - insn_count_and_handler_off[1] = try_item->GetHandlers()->GetListOffset(); - offset += Write(start_addr, sizeof(uint32_t), offset); - offset += Write(insn_count_and_handler_off, 2 * sizeof(uint16_t), offset); - } - // Leave offset pointing to the end of the try items. - UNUSED(WriteUleb128(code_item->Handlers()->size(), offset)); - for (std::unique_ptr<const dex_ir::CatchHandler>& handlers : *code_item->Handlers()) { - size_t list_offset = offset + handlers->GetListOffset(); - uint32_t size = handlers->HasCatchAll() ? (handlers->GetHandlers()->size() - 1) * -1 : - handlers->GetHandlers()->size(); - list_offset += WriteSleb128(size, list_offset); - for (std::unique_ptr<const dex_ir::TypeAddrPair>& handler : *handlers->GetHandlers()) { - if (handler->GetTypeId() != nullptr) { - list_offset += WriteUleb128(handler->GetTypeId()->GetIndex(), list_offset); - } - list_offset += WriteUleb128(handler->GetAddress(), list_offset); - } + const size_t code_item_size = WriteCodeItem(code_item.get(), offset, reserve_only); + // Only add the section hotness info once. + if (!reserve_only && code_section != nullptr) { + auto it = dex_layout_->LayoutHotnessInfo().code_item_layout_.find(code_item.get()); + if (it != dex_layout_->LayoutHotnessInfo().code_item_layout_.end()) { + code_section->parts_[static_cast<size_t>(it->second)].CombineSection( + offset, + offset + code_item_size); } } - // TODO: Clean this up to properly calculate the size instead of assuming it doesn't change. - offset = code_item->GetOffset() + code_item->GetSize(); + offset += code_item_size; } if (compute_offsets_ && start != offset) { diff --git a/dexlayout/dex_writer.h b/dexlayout/dex_writer.h index 92a002edc7..fdeb299aa4 100644 --- a/dexlayout/dex_writer.h +++ b/dexlayout/dex_writer.h @@ -23,6 +23,7 @@ #include "base/unix_file/fd_file.h" #include "dex/compact_dex_level.h" +#include "dex/dex_file.h" #include "dex_ir.h" #include "mem_map.h" #include "os.h" @@ -59,6 +60,25 @@ class MapItemQueue : public class DexWriter { public: + static constexpr uint32_t kDataSectionAlignment = sizeof(uint32_t) * 2; + static constexpr uint32_t kDexSectionWordAlignment = 4; + static constexpr uint32_t kDexTryItemAlignment = sizeof(uint32_t); + + static inline constexpr uint32_t SectionAlignment(DexFile::MapItemType type) { + switch (type) { + case DexFile::kDexTypeClassDataItem: + case DexFile::kDexTypeStringDataItem: + case DexFile::kDexTypeDebugInfoItem: + case DexFile::kDexTypeAnnotationItem: + case DexFile::kDexTypeEncodedArrayItem: + return alignof(uint8_t); + + default: + // All other sections are kDexAlignedSection. + return DexWriter::kDexSectionWordAlignment; + } + } + DexWriter(dex_ir::Header* header, MemMap* mem_map, DexLayout* dex_layout, @@ -77,7 +97,7 @@ class DexWriter { virtual ~DexWriter() {} protected: - void WriteMemMap(); + virtual void WriteMemMap(); size_t Write(const void* buffer, size_t length, size_t offset) WARN_UNUSED; size_t WriteSleb128(uint32_t value, size_t offset) WARN_UNUSED; @@ -118,6 +138,11 @@ class DexWriter { uint32_t WriteMapItems(uint32_t offset, MapItemQueue* queue); uint32_t GenerateAndWriteMapItems(uint32_t offset); + virtual uint32_t WriteCodeItemPostInstructionData(dex_ir::CodeItem* item, + uint32_t offset, + bool reserve_only); + virtual uint32_t WriteCodeItem(dex_ir::CodeItem* item, uint32_t offset, bool reserve_only); + // Process an offset, if compute_offset is set, write into the dex ir item, otherwise read the // existing offset and use that for writing. void ProcessOffset(uint32_t* const offset, dex_ir::Item* item) { diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc index 000d1356b9..a43dd074f8 100644 --- a/dexlayout/dexlayout.cc +++ b/dexlayout/dexlayout.cc @@ -34,6 +34,7 @@ #include "android-base/stringprintf.h" #include "base/logging.h" // For VLOG_IS_ON. +#include "dex/art_dex_file_loader.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_layout.h" #include "dex/dex_file_loader.h" @@ -1923,14 +1924,16 @@ void DexLayout::ProcessDexFile(const char* file_name, if (options_.verify_output_) { std::string error_msg; std::string location = "memory mapped file for " + std::string(file_name); - std::unique_ptr<const DexFile> output_dex_file(DexFileLoader::Open(mem_map_->Begin(), - file_size, - location, - /* checksum */ 0, - /*oat_dex_file*/ nullptr, - /*verify*/ true, - /*verify_checksum*/ false, - &error_msg)); + const ArtDexFileLoader dex_file_loader; + std::unique_ptr<const DexFile> output_dex_file( + dex_file_loader.Open(mem_map_->Begin(), + file_size, + location, + /* checksum */ 0, + /*oat_dex_file*/ nullptr, + /*verify*/ true, + /*verify_checksum*/ false, + &error_msg)); CHECK(output_dex_file != nullptr) << "Failed to re-open output file:" << error_msg; // Do IR-level comparison between input and output. This check ignores potential differences @@ -1961,8 +1964,9 @@ int DexLayout::ProcessFile(const char* file_name) { // all of which are Zip archives with "classes.dex" inside. const bool verify_checksum = !options_.ignore_bad_checksum_; std::string error_msg; + const ArtDexFileLoader dex_file_loader; std::vector<std::unique_ptr<const DexFile>> dex_files; - if (!DexFileLoader::Open( + if (!dex_file_loader.Open( file_name, file_name, /* verify */ true, verify_checksum, &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. diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc index 5da3b1d366..3a7f9eeda6 100644 --- a/dexlayout/dexlayout_test.cc +++ b/dexlayout/dexlayout_test.cc @@ -23,6 +23,7 @@ #include "base/unix_file/fd_file.h" #include "common_runtime_test.h" +#include "dex/art_dex_file_loader.h" #include "dex/code_item_accessors-inl.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" @@ -318,12 +319,13 @@ class DexLayoutTest : public CommonRuntimeTest { bool MutateDexFile(File* output_dex, const std::string& input_jar, const Mutator& mutator) { std::vector<std::unique_ptr<const DexFile>> dex_files; std::string error_msg; - CHECK(DexFileLoader::Open(input_jar.c_str(), - input_jar.c_str(), - /*verify*/ true, - /*verify_checksum*/ true, - &error_msg, - &dex_files)) << error_msg; + const ArtDexFileLoader dex_file_loader; + CHECK(dex_file_loader.Open(input_jar.c_str(), + input_jar.c_str(), + /*verify*/ true, + /*verify_checksum*/ true, + &error_msg, + &dex_files)) << error_msg; EXPECT_EQ(dex_files.size(), 1u) << "Only one input dex is supported"; for (const std::unique_ptr<const DexFile>& dex : dex_files) { CHECK(dex->EnableWrite()) << "Failed to enable write"; @@ -344,12 +346,13 @@ class DexLayoutTest : public CommonRuntimeTest { const std::string& dex_location) { std::vector<std::unique_ptr<const DexFile>> dex_files; std::string error_msg; - bool result = DexFileLoader::Open(input_dex.c_str(), - input_dex, - /*verify*/ true, - /*verify_checksum*/ false, - &error_msg, - &dex_files); + const ArtDexFileLoader dex_file_loader; + bool result = dex_file_loader.Open(input_dex.c_str(), + input_dex, + /*verify*/ true, + /*verify_checksum*/ false, + &error_msg, + &dex_files); ASSERT_TRUE(result) << error_msg; ASSERT_GE(dex_files.size(), 1u); diff --git a/dexlist/dexlist.cc b/dexlist/dexlist.cc index 556938b563..1ced8ca771 100644 --- a/dexlist/dexlist.cc +++ b/dexlist/dexlist.cc @@ -27,6 +27,7 @@ #include <stdlib.h> #include "base/logging.h" // For InitLogging. +#include "dex/art_dex_file_loader.h" #include "dex/code_item_accessors-no_art-inl.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" @@ -100,7 +101,7 @@ static void dumpMethod(const DexFile* pDexFile, if (pCode == nullptr || codeOffset == 0) { return; } - CodeItemDebugInfoAccessor accessor(*pDexFile, pCode, pDexFile->GetDebugInfoOffset(pCode)); + CodeItemDebugInfoAccessor accessor(*pDexFile, pCode, idx); // Method information. const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(idx); @@ -174,7 +175,8 @@ static int processFile(const char* fileName) { static constexpr bool kVerifyChecksum = true; std::string error_msg; std::vector<std::unique_ptr<const DexFile>> dex_files; - if (!DexFileLoader::Open( + const ArtDexFileLoader dex_file_loader; + if (!dex_file_loader.Open( fileName, fileName, /* verify */ true, kVerifyChecksum, &error_msg, &dex_files)) { fputs(error_msg.c_str(), stderr); fputc('\n', stderr); diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index ca8077fea1..f53846c04d 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -171,7 +171,8 @@ class OatSymbolizer FINAL { text_size, oat_file_->BssSize(), oat_file_->BssMethodsOffset(), - oat_file_->BssRootsOffset()); + oat_file_->BssRootsOffset(), + oat_file_->VdexSize()); builder_->WriteDynamicSection(); const OatHeader& oat_header = oat_file_->GetOatHeader(); @@ -717,7 +718,6 @@ class OatDumper { } vdex_file->Unquicken(MakeNonOwningPointerVector(tmp_dex_files), - vdex_file->GetQuickeningInfo(), /* decompile_return_instruction */ true); *dex_files = std::move(tmp_dex_files); diff --git a/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc index da7d60ac2f..dcc834abe9 100644 --- a/openjdkjvmti/fixed_up_dex_file.cc +++ b/openjdkjvmti/fixed_up_dex_file.cc @@ -30,6 +30,7 @@ */ #include "fixed_up_dex_file.h" +#include "dex/art_dex_file_loader.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" @@ -62,8 +63,7 @@ static void DoDexUnquicken(const art::DexFile& new_dex_file, const art::DexFile& if (vdex == nullptr) { return; } - art::VdexFile::UnquickenDexFile( - new_dex_file, vdex->GetQuickeningInfo(), /* decompile_return_instruction */true); + vdex->UnquickenDexFile(new_dex_file, original_dex_file, /* decompile_return_instruction */true); } std::unique_ptr<FixedUpDexFile> FixedUpDexFile::Create(const art::DexFile& original) { @@ -72,7 +72,8 @@ std::unique_ptr<FixedUpDexFile> FixedUpDexFile::Create(const art::DexFile& origi data.resize(original.Size()); memcpy(data.data(), original.Begin(), original.Size()); std::string error; - std::unique_ptr<const art::DexFile> new_dex_file(art::DexFileLoader::Open( + const art::ArtDexFileLoader dex_file_loader; + std::unique_ptr<const art::DexFile> new_dex_file(dex_file_loader.Open( data.data(), data.size(), /*location*/"Unquickening_dexfile.dex", @@ -103,7 +104,7 @@ std::unique_ptr<FixedUpDexFile> FixedUpDexFile::Create(const art::DexFile& origi // Overwrite the dex file stored in data with the new result. data.clear(); data.insert(data.end(), mem_map->Begin(), mem_map->Begin() + dex_file_size); - new_dex_file = art::DexFileLoader::Open( + new_dex_file = dex_file_loader.Open( data.data(), data.size(), /*location*/"Unquickening_dexfile.dex", diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc index f9eb008af2..b3f5c1886e 100644 --- a/openjdkjvmti/ti_class.cc +++ b/openjdkjvmti/ti_class.cc @@ -42,6 +42,7 @@ #include "class_linker.h" #include "class_table-inl.h" #include "common_throws.h" +#include "dex/art_dex_file_loader.h" #include "dex/dex_file_annotations.h" #include "dex/dex_file_loader.h" #include "events-inl.h" @@ -107,12 +108,13 @@ static std::unique_ptr<const art::DexFile> MakeSingleDexFile(art::Thread* self, } uint32_t checksum = reinterpret_cast<const art::DexFile::Header*>(map->Begin())->checksum_; std::string map_name = map->GetName(); - std::unique_ptr<const art::DexFile> dex_file(art::DexFileLoader::Open(map_name, - checksum, - std::move(map), - /*verify*/true, - /*verify_checksum*/true, - &error_msg)); + const art::ArtDexFileLoader dex_file_loader; + std::unique_ptr<const art::DexFile> dex_file(dex_file_loader.Open(map_name, + checksum, + std::move(map), + /*verify*/true, + /*verify_checksum*/true, + &error_msg)); if (dex_file.get() == nullptr) { LOG(WARNING) << "Unable to load modified dex file for " << descriptor << ": " << error_msg; art::ThrowClassFormatError(nullptr, diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc index 6194d1e42c..717b2ba669 100644 --- a/openjdkjvmti/ti_redefine.cc +++ b/openjdkjvmti/ti_redefine.cc @@ -43,6 +43,7 @@ #include "base/stringpiece.h" #include "class_linker-inl.h" #include "debugger.h" +#include "dex/art_dex_file_loader.h" #include "dex/dex_file.h" #include "dex/dex_file_loader.h" #include "dex/dex_file_types.h" @@ -426,12 +427,13 @@ jvmtiError Redefiner::AddRedefinition(ArtJvmTiEnv* env, const ArtClassDefinition return ERR(INVALID_CLASS_FORMAT); } uint32_t checksum = reinterpret_cast<const art::DexFile::Header*>(map->Begin())->checksum_; - std::unique_ptr<const art::DexFile> dex_file(art::DexFileLoader::Open(map->GetName(), - checksum, - std::move(map), - /*verify*/true, - /*verify_checksum*/true, - error_msg_)); + const art::ArtDexFileLoader dex_file_loader; + std::unique_ptr<const art::DexFile> dex_file(dex_file_loader.Open(map->GetName(), + checksum, + std::move(map), + /*verify*/true, + /*verify_checksum*/true, + error_msg_)); if (dex_file.get() == nullptr) { os << "Unable to load modified dex file for " << def.GetName() << ": " << *error_msg_; *error_msg_ = os.str(); diff --git a/openjdkjvmti/ti_search.cc b/openjdkjvmti/ti_search.cc index 9d5f4ea3f9..cbb7b53bff 100644 --- a/openjdkjvmti/ti_search.cc +++ b/openjdkjvmti/ti_search.cc @@ -38,6 +38,7 @@ #include "base/enums.h" #include "base/macros.h" #include "class_linker.h" +#include "dex/art_dex_file_loader.h" #include "dex/dex_file.h" #include "dex/dex_file_loader.h" #include "jni_internal.h" @@ -227,7 +228,8 @@ jvmtiError SearchUtil::AddToBootstrapClassLoaderSearch(jvmtiEnv* env ATTRIBUTE_U std::string error_msg; std::vector<std::unique_ptr<const art::DexFile>> dex_files; - if (!art::DexFileLoader::Open( + const art::ArtDexFileLoader dex_file_loader; + if (!dex_file_loader.Open( segment, segment, /* verify */ true, /* verify_checksum */ true, &error_msg, &dex_files)) { LOG(WARNING) << "Could not open " << segment << " for boot classpath extension: " << error_msg; return ERR(ILLEGAL_ARGUMENT); diff --git a/profman/profman.cc b/profman/profman.cc index c4216fab99..9f3e3b6ac5 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -39,6 +39,7 @@ #include "base/unix_file/fd_file.h" #include "boot_image_profile.h" #include "bytecode_utils.h" +#include "dex/art_dex_file_loader.h" #include "dex/code_item_accessors-inl.h" #include "dex/dex_file.h" #include "dex/dex_file_loader.h" @@ -329,25 +330,26 @@ class ProfMan FINAL { static constexpr bool kVerifyChecksum = true; for (size_t i = 0; i < dex_locations_.size(); ++i) { std::string error_msg; + const ArtDexFileLoader dex_file_loader; std::vector<std::unique_ptr<const DexFile>> dex_files_for_location; if (use_apk_fd_list) { - if (DexFileLoader::OpenZip(apks_fd_[i], - dex_locations_[i], - /* verify */ true, - kVerifyChecksum, - &error_msg, - &dex_files_for_location)) { + if (dex_file_loader.OpenZip(apks_fd_[i], + dex_locations_[i], + /* verify */ true, + kVerifyChecksum, + &error_msg, + &dex_files_for_location)) { } else { LOG(WARNING) << "OpenZip failed for '" << dex_locations_[i] << "' " << error_msg; continue; } } else { - if (DexFileLoader::Open(apk_files_[i].c_str(), - dex_locations_[i], - /* verify */ true, - kVerifyChecksum, - &error_msg, - &dex_files_for_location)) { + if (dex_file_loader.Open(apk_files_[i].c_str(), + dex_locations_[i], + /* verify */ true, + kVerifyChecksum, + &error_msg, + &dex_files_for_location)) { } else { LOG(WARNING) << "Open failed for '" << dex_locations_[i] << "' " << error_msg; continue; diff --git a/runtime/Android.bp b/runtime/Android.bp index 2657f4fa86..2e34bafd54 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -21,6 +21,71 @@ JIT_DEBUG_REGISTER_CODE_LDFLAGS = ["-Wl,--keep-unique,__jit_debug_register_code"] cc_defaults { + name: "libdexfile_defaults", + defaults: ["art_defaults"], + host_supported: true, + srcs: [ + "dex/compact_dex_debug_info.cc", + "dex/compact_dex_file.cc", + "dex/dex_file.cc", + "dex/dex_file_exception_helpers.cc", + "dex/dex_file_loader.cc", + "dex/dex_file_tracking_registrar.cc", + "dex/dex_file_verifier.cc", + "dex/dex_instruction.cc", + "dex/standard_dex_file.cc", + "utf.cc", + "utils.cc", + ], + + target: { + android: { + shared_libs: [ + "libutils", + ], + static_libs: [ + "libz", + "libbase", + ], + }, + host: { + shared_libs: [ + "libz", + ], + }, + }, + generated_sources: ["art_operator_srcs"], + // asm_support_gen.h (used by asm_support.h) is generated with cpp-define-generator + generated_headers: ["cpp-define-generator-asm-support"], + // export our headers so the libart-gtest targets can use it as well. + export_generated_headers: ["cpp-define-generator-asm-support"], + include_dirs: [ + "external/icu/icu4c/source/common", + "external/zlib", + ], + shared_libs: [ + "liblog", + // For common macros. + "libbase", + ], + export_include_dirs: ["."], + // ART's macros.h depends on libbase's macros.h. + // Note: runtime_options.h depends on cmdline. But we don't really want to export this + // generically. dex2oat takes care of it itself. + export_shared_lib_headers: ["libbase"], +} + +art_cc_library { + name: "libdexfile", + defaults: ["libdexfile_defaults"], + // Leave the symbols in the shared library so that stack unwinders can + // produce meaningful name resolution. + strip: { + keep_symbols: true, + }, +} + +cc_defaults { name: "libart_defaults", defaults: ["art_defaults"], host_supported: true, @@ -56,12 +121,14 @@ cc_defaults { "common_throws.cc", "compiler_filter.cc", "debugger.cc", + "dex/compact_dex_debug_info.cc", "dex/compact_dex_file.cc", "dex/dex_file.cc", "dex/dex_file_annotations.cc", "dex/dex_file_exception_helpers.cc", "dex/dex_file_layout.cc", "dex/dex_file_loader.cc", + "dex/art_dex_file_loader.cc", "dex/dex_file_tracking_registrar.cc", "dex/dex_file_verifier.cc", "dex/dex_instruction.cc", @@ -572,6 +639,7 @@ art_cc_test { "class_table_test.cc", "compiler_filter_test.cc", "dex/code_item_accessors_test.cc", + "dex/compact_dex_debug_info_test.cc", "dex/compact_dex_file_test.cc", "dex/dex_file_test.cc", "dex/dex_file_verifier_test.cc", diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 44a5dde485..96468bba60 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -562,14 +562,14 @@ bool ArtMethod::EqualParameters(Handle<mirror::ObjectArray<mirror::Class>> param return true; } -const uint8_t* ArtMethod::GetQuickenedInfo() { +ArrayRef<const uint8_t> ArtMethod::GetQuickenedInfo() { const DexFile& dex_file = GetDeclaringClass()->GetDexFile(); const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); if (oat_dex_file == nullptr || (oat_dex_file->GetOatFile() == nullptr)) { - return nullptr; + return ArrayRef<const uint8_t>(); } - return oat_dex_file->GetOatFile()->GetVdexFile()->GetQuickenedInfoOf( - dex_file, GetCodeItemOffset()); + return oat_dex_file->GetOatFile()->GetVdexFile()->GetQuickenedInfoOf(dex_file, + GetDexMethodIndex()); } const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { diff --git a/runtime/art_method.h b/runtime/art_method.h index c4a586ed92..cd06354859 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -21,6 +21,7 @@ #include <android-base/logging.h> +#include "base/array_ref.h" #include "base/bit_utils.h" #include "base/casts.h" #include "base/enums.h" @@ -662,7 +663,7 @@ class ArtMethod FINAL { return hotness_count_; } - const uint8_t* GetQuickenedInfo() REQUIRES_SHARED(Locks::mutator_lock_); + ArrayRef<const uint8_t> GetQuickenedInfo() REQUIRES_SHARED(Locks::mutator_lock_); // Returns the method header for the compiled code containing 'pc'. Note that runtime // methods will return null for this method, as they are not oat based. diff --git a/runtime/base/bit_string.h b/runtime/base/bit_string.h index bfbe8eaf71..7d9fb70de7 100644 --- a/runtime/base/bit_string.h +++ b/runtime/base/bit_string.h @@ -114,13 +114,13 @@ inline std::ostream& operator<<(std::ostream& os, const BitStringChar& bc) { /** * BitString * - * lsb (least significant bit) msb - * +------------+------------+------------+-----+------------+ - * | | | | | | - * | Char0 | Char1 | Char2 | ... | CharN | - * | | | | | | - * +------------+------------+------------+-----+------------+ - * <- len[0] -> <- len[1] -> <- len[2] -> ... <- len[N] -> + * MSB (most significant bit) LSB + * +------------+-----+------------+------------+------------+ + * | | | | | | + * | CharN | ... | Char2 | Char1 | Char0 | + * | | | | | | + * +------------+-----+------------+------------+------------+ + * <- len[N] -> ... <- len[2] -> <- len[1] -> <- len[0] -> * * Stores up to "N+1" characters in a subset of a machine word. Each character has a different * bitlength, as defined by len[pos]. This BitString can be nested inside of a BitStruct @@ -145,7 +145,7 @@ struct BitString { // As this is meant to be used only with "SubtypeCheckInfo", // the bitlengths and the maximum string length is tuned by maximizing the coverage of "Assigned" // bitstrings for instance-of and check-cast targets during Optimizing compilation. - static constexpr size_t kBitSizeAtPosition[] = {12, 3, 8}; // len[] from header docs. + static constexpr size_t kBitSizeAtPosition[] = {12, 4, 11}; // len[] from header docs. static constexpr size_t kCapacity = arraysize(kBitSizeAtPosition); // MaxBitstringLen above. // How many bits are needed to represent BitString[0..position)? @@ -165,8 +165,7 @@ struct BitString { // (e.g. to use with BitField{Insert,Extract,Clear}.) static constexpr size_t GetLsbForPosition(size_t position) { DCHECK_GE(kCapacity, position); - constexpr size_t kMaximumBitLength = GetBitLengthTotalAtPosition(kCapacity); - return kMaximumBitLength - GetBitLengthTotalAtPosition(position + 1u); + return GetBitLengthTotalAtPosition(position); } // How many bits are needed for a BitStringChar at the position? @@ -183,9 +182,7 @@ struct BitString { BitStringChar operator[](size_t idx) const { DCHECK_LT(idx, kCapacity); - StorageType data = - BitFieldExtract(storage_, - GetLsbForPosition(idx), kBitSizeAtPosition[idx]); + StorageType data = BitFieldExtract(storage_, GetLsbForPosition(idx), kBitSizeAtPosition[idx]); return BitStringChar(data, kBitSizeAtPosition[idx]); } @@ -259,17 +256,10 @@ struct BitString { DCHECK_GE(kCapacity, end); BitString copy = *this; - size_t bit_size = 0; - for (size_t idx = end; idx < kCapacity; ++idx) { - bit_size += kBitSizeAtPosition[idx]; - } - // TODO: precompute above table. - - if (bit_size > 0) { - StorageType data = - BitFieldClear(copy.storage_, - GetLsbForPosition(kCapacity), - bit_size); + if (end < kCapacity) { + size_t lsb = GetLsbForPosition(end); + size_t bit_size = GetLsbForPosition(kCapacity) - lsb; + StorageType data = BitFieldClear(copy.storage_, lsb, bit_size); copy.storage_ = data; } diff --git a/runtime/base/bit_string_test.cc b/runtime/base/bit_string_test.cc index 96aa154ef3..23274e3f2f 100644 --- a/runtime/base/bit_string_test.cc +++ b/runtime/base/bit_string_test.cc @@ -65,7 +65,7 @@ size_t AsUint(const T& value) { return uint_value; } -// Make max bitstring, e.g. BitString[4095,7,255] for {12,3,8} +// Make max bitstring, e.g. BitString[4095,15,2047] for {12,4,11} template <size_t kCount = BitString::kCapacity> BitString MakeBitStringMax() { BitString bs{}; @@ -87,15 +87,14 @@ BitString SetBitStringCharAt(BitString bit_string, size_t i, size_t val) { #define EXPECT_BITSTRING_STR(expected_str, actual_value) \ EXPECT_STREQ((expected_str), Stringify((actual_value)).c_str()) +// TODO: Consider removing this test, it's kind of replicating the logic in GetLsbForPosition(). TEST(InstanceOfBitString, GetLsbForPosition) { ASSERT_LE(3u, BitString::kCapacity); // Test will fail if kCapacity is not at least 3. Update it. - EXPECT_EQ(0u, BitString::GetLsbForPosition(BitString::kCapacity - 1u)); - EXPECT_EQ(BitString::kBitSizeAtPosition[BitString::kCapacity - 1u], - BitString::GetLsbForPosition(BitString::kCapacity - 2u)); - EXPECT_EQ(BitString::kBitSizeAtPosition[BitString::kCapacity - 1u] + - BitString::kBitSizeAtPosition[BitString::kCapacity - 2u], - BitString::GetLsbForPosition(BitString::kCapacity - 3u)); + EXPECT_EQ(0u, BitString::GetLsbForPosition(0u)); + EXPECT_EQ(BitString::kBitSizeAtPosition[0u], BitString::GetLsbForPosition(1u)); + EXPECT_EQ(BitString::kBitSizeAtPosition[0u] + BitString::kBitSizeAtPosition[1u], + BitString::GetLsbForPosition(2u)); } TEST(InstanceOfBitString, ToString) { @@ -126,8 +125,8 @@ TEST(InstanceOfBitString, ReadWrite) { // Each maximal value should be tested here for each position. uint32_t max_bitstring_ints[] = { MaxInt<uint32_t>(12), - MaxInt<uint32_t>(3), - MaxInt<uint32_t>(8), + MaxInt<uint32_t>(4), + MaxInt<uint32_t>(11), }; // Update tests if changing the tuning values above. @@ -151,14 +150,13 @@ constexpr auto MaxForPos() { } TEST(InstanceOfBitString, MemoryRepresentation) { - // Verify that the lower positions are stored in more significant bits. + // Verify that the lower positions are stored in less significant bits. BitString bs = MakeBitString({MaxForPos<0>(), MaxForPos<1>()}); BitString::StorageType as_int = static_cast<BitString::StorageType>(bs); - // Below tests assumes the capacity is 3. Update if it this changes. - ASSERT_EQ(3u, BitString::kCapacity); - EXPECT_EQ(MaxForPos<0>() << (BitString::kBitSizeAtPosition[2] + BitString::kBitSizeAtPosition[1]) | - (MaxForPos<1>() << BitString::kBitSizeAtPosition[2]), + // Below tests assumes the capacity is at least 3. + ASSERT_LE(3u, BitString::kCapacity); + EXPECT_EQ((MaxForPos<0>() << 0) | (MaxForPos<1>() << BitString::kBitSizeAtPosition[0]), as_int); } diff --git a/runtime/base/bit_utils.h b/runtime/base/bit_utils.h index 34cddbff6a..d2a99f1a39 100644 --- a/runtime/base/bit_utils.h +++ b/runtime/base/bit_utils.h @@ -46,10 +46,14 @@ template<typename T> constexpr int CLZ(T x) { static_assert(std::is_integral<T>::value, "T must be integral"); static_assert(std::is_unsigned<T>::value, "T must be unsigned"); - static_assert(sizeof(T) <= sizeof(long long), // NOLINT [runtime/int] [4] - "T too large, must be smaller than long long"); + static_assert(std::numeric_limits<T>::radix == 2, "Unexpected radix!"); + static_assert(sizeof(T) == sizeof(uint64_t) || sizeof(T) <= sizeof(uint32_t), + "Unsupported sizeof(T)"); DCHECK_NE(x, 0u); - return (sizeof(T) == sizeof(uint32_t)) ? __builtin_clz(x) : __builtin_clzll(x); + constexpr bool is_64_bit = (sizeof(T) == sizeof(uint64_t)); + constexpr size_t adjustment = + is_64_bit ? 0u : std::numeric_limits<uint32_t>::digits - std::numeric_limits<T>::digits; + return is_64_bit ? __builtin_clzll(x) : __builtin_clz(x) - adjustment; } // Similar to CLZ except that on zero input it returns bitwidth and supports signed integers. @@ -65,10 +69,10 @@ constexpr int CTZ(T x) { static_assert(std::is_integral<T>::value, "T must be integral"); // It is not unreasonable to ask for trailing zeros in a negative number. As such, do not check // that T is an unsigned type. - static_assert(sizeof(T) <= sizeof(long long), // NOLINT [runtime/int] [4] - "T too large, must be smaller than long long"); + static_assert(sizeof(T) == sizeof(uint64_t) || sizeof(T) <= sizeof(uint32_t), + "Unsupported sizeof(T)"); DCHECK_NE(x, static_cast<T>(0)); - return (sizeof(T) == sizeof(uint32_t)) ? __builtin_ctz(x) : __builtin_ctzll(x); + return (sizeof(T) == sizeof(uint64_t)) ? __builtin_ctzll(x) : __builtin_ctz(x); } // Similar to CTZ except that on zero input it returns bitwidth and supports signed integers. diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc index 3ec5335a80..e646520f3d 100644 --- a/runtime/class_loader_context.cc +++ b/runtime/class_loader_context.cc @@ -21,6 +21,7 @@ #include "base/stl_util.h" #include "class_linker.h" #include "class_loader_utils.h" +#include "dex/art_dex_file_loader.h" #include "dex/dex_file.h" #include "dex/dex_file_loader.h" #include "handle_scope-inl.h" @@ -203,6 +204,7 @@ bool ClassLoaderContext::OpenDexFiles(InstructionSet isa, const std::string& cla // We may get resource-only apks which we cannot load. // TODO(calin): Refine the dex opening interface to be able to tell if an archive contains // no dex files. So that we can distinguish the real failures... + const ArtDexFileLoader dex_file_loader; for (ClassLoaderInfo& info : class_loader_chain_) { size_t opened_dex_files_index = info.opened_dex_files.size(); for (const std::string& cp_elem : info.classpath) { @@ -215,12 +217,12 @@ bool ClassLoaderContext::OpenDexFiles(InstructionSet isa, const std::string& cla std::string error_msg; // When opening the dex files from the context we expect their checksum to match their // contents. So pass true to verify_checksum. - if (!DexFileLoader::Open(location.c_str(), - location.c_str(), - Runtime::Current()->IsVerificationEnabled(), - /*verify_checksum*/ true, - &error_msg, - &info.opened_dex_files)) { + if (!dex_file_loader.Open(location.c_str(), + location.c_str(), + Runtime::Current()->IsVerificationEnabled(), + /*verify_checksum*/ true, + &error_msg, + &info.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. // This could happen when we need to recompile a pre-build whose dex code has been stripped. diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index 96d660fd64..39dbebfdf2 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -35,6 +35,7 @@ #include "base/unix_file/fd_file.h" #include "class_linker.h" #include "compiler_callbacks.h" +#include "dex/art_dex_file_loader.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" #include "gc/heap.h" @@ -375,7 +376,8 @@ std::unique_ptr<const DexFile> CommonRuntimeTestImpl::LoadExpectSingleDexFile( std::string error_msg; MemMap::Init(); static constexpr bool kVerifyChecksum = true; - if (!DexFileLoader::Open( + const ArtDexFileLoader dex_file_loader; + if (!dex_file_loader.Open( location, location, /* verify */ true, kVerifyChecksum, &error_msg, &dex_files)) { LOG(FATAL) << "Could not open .dex file '" << location << "': " << error_msg << "\n"; UNREACHABLE(); @@ -574,12 +576,13 @@ std::vector<std::unique_ptr<const DexFile>> CommonRuntimeTestImpl::OpenTestDexFi std::string filename = GetTestDexFileName(name); static constexpr bool kVerifyChecksum = true; std::string error_msg; + const ArtDexFileLoader dex_file_loader; std::vector<std::unique_ptr<const DexFile>> dex_files; - bool success = DexFileLoader::Open(filename.c_str(), - filename.c_str(), - /* verify */ true, - kVerifyChecksum, - &error_msg, &dex_files); + bool success = dex_file_loader.Open(filename.c_str(), + filename.c_str(), + /* verify */ true, + 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/dex/art_dex_file_loader.cc b/runtime/dex/art_dex_file_loader.cc new file mode 100644 index 0000000000..282b282707 --- /dev/null +++ b/runtime/dex/art_dex_file_loader.cc @@ -0,0 +1,465 @@ +/* + * Copyright (C) 2017 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 "art_dex_file_loader.h" + +#include <sys/mman.h> // For the PROT_* and MAP_* constants. +#include <sys/stat.h> + +#include "android-base/stringprintf.h" + +#include "base/file_magic.h" +#include "base/stl_util.h" +#include "base/systrace.h" +#include "base/unix_file/fd_file.h" +#include "compact_dex_file.h" +#include "dex_file.h" +#include "dex_file_verifier.h" +#include "standard_dex_file.h" +#include "zip_archive.h" + +namespace art { + +namespace { + +class MemMapContainer : public DexFileContainer { + public: + explicit MemMapContainer(std::unique_ptr<MemMap>&& mem_map) : mem_map_(std::move(mem_map)) { } + virtual ~MemMapContainer() OVERRIDE { } + + int GetPermissions() OVERRIDE { + if (mem_map_.get() == nullptr) { + return 0; + } else { + return mem_map_->GetProtect(); + } + } + + bool IsReadOnly() OVERRIDE { + return GetPermissions() == PROT_READ; + } + + bool EnableWrite() OVERRIDE { + CHECK(IsReadOnly()); + if (mem_map_.get() == nullptr) { + return false; + } else { + return mem_map_->Protect(PROT_READ | PROT_WRITE); + } + } + + bool DisableWrite() OVERRIDE { + CHECK(!IsReadOnly()); + if (mem_map_.get() == nullptr) { + return false; + } else { + return mem_map_->Protect(PROT_READ); + } + } + + private: + std::unique_ptr<MemMap> mem_map_; + DISALLOW_COPY_AND_ASSIGN(MemMapContainer); +}; + +} // namespace + +using android::base::StringPrintf; + +static constexpr OatDexFile* kNoOatDexFile = nullptr; + + +bool ArtDexFileLoader::GetMultiDexChecksums(const char* filename, + std::vector<uint32_t>* checksums, + std::string* error_msg, + int zip_fd) const { + CHECK(checksums != nullptr); + uint32_t magic; + + File fd; + if (zip_fd != -1) { + if (ReadMagicAndReset(zip_fd, &magic, error_msg)) { + fd = File(zip_fd, false /* check_usage */); + } + } else { + fd = OpenAndReadMagic(filename, &magic, error_msg); + } + if (fd.Fd() == -1) { + DCHECK(!error_msg->empty()); + return false; + } + if (IsZipMagic(magic)) { + std::unique_ptr<ZipArchive> zip_archive( + ZipArchive::OpenFromFd(fd.Release(), filename, error_msg)); + if (zip_archive.get() == nullptr) { + *error_msg = StringPrintf("Failed to open zip archive '%s' (error msg: %s)", filename, + error_msg->c_str()); + return false; + } + + uint32_t i = 0; + std::string zip_entry_name = GetMultiDexClassesDexName(i++); + std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(zip_entry_name.c_str(), error_msg)); + if (zip_entry.get() == nullptr) { + *error_msg = StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", filename, + zip_entry_name.c_str(), error_msg->c_str()); + return false; + } + + do { + checksums->push_back(zip_entry->GetCrc32()); + zip_entry_name = GetMultiDexClassesDexName(i++); + zip_entry.reset(zip_archive->Find(zip_entry_name.c_str(), error_msg)); + } while (zip_entry.get() != nullptr); + return true; + } + if (IsMagicValid(magic)) { + std::unique_ptr<const DexFile> dex_file( + OpenFile(fd.Release(), filename, false, false, error_msg)); + if (dex_file == nullptr) { + return false; + } + checksums->push_back(dex_file->GetHeader().checksum_); + return true; + } + *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename); + return false; +} + +std::unique_ptr<const DexFile> ArtDexFileLoader::Open(const uint8_t* base, + size_t size, + const std::string& location, + uint32_t location_checksum, + const OatDexFile* oat_dex_file, + bool verify, + bool verify_checksum, + std::string* error_msg) const { + ScopedTrace trace(std::string("Open dex file from RAM ") + location); + return OpenCommon(base, + size, + location, + location_checksum, + oat_dex_file, + verify, + verify_checksum, + error_msg, + /*container*/ nullptr, + /*verify_result*/ nullptr); +} + +std::unique_ptr<const DexFile> ArtDexFileLoader::Open(const std::string& location, + uint32_t location_checksum, + std::unique_ptr<MemMap> map, + bool verify, + bool verify_checksum, + std::string* error_msg) const { + ScopedTrace trace(std::string("Open dex file from mapped-memory ") + location); + CHECK(map.get() != nullptr); + + if (map->Size() < sizeof(DexFile::Header)) { + *error_msg = StringPrintf( + "DexFile: failed to open dex file '%s' that is too short to have a header", + location.c_str()); + return nullptr; + } + + std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(), + map->Size(), + location, + location_checksum, + kNoOatDexFile, + verify, + verify_checksum, + error_msg, + new MemMapContainer(std::move(map)), + /*verify_result*/ nullptr); + return dex_file; +} + +bool ArtDexFileLoader::Open(const char* filename, + const std::string& location, + bool verify, + bool verify_checksum, + std::string* error_msg, + std::vector<std::unique_ptr<const DexFile>>* dex_files) const { + ScopedTrace trace(std::string("Open dex file ") + std::string(location)); + DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr"; + uint32_t magic; + File fd = OpenAndReadMagic(filename, &magic, error_msg); + if (fd.Fd() == -1) { + DCHECK(!error_msg->empty()); + return false; + } + if (IsZipMagic(magic)) { + return OpenZip(fd.Release(), location, verify, verify_checksum, error_msg, dex_files); + } + if (IsMagicValid(magic)) { + std::unique_ptr<const DexFile> dex_file(OpenFile(fd.Release(), + location, + verify, + verify_checksum, + error_msg)); + if (dex_file.get() != nullptr) { + dex_files->push_back(std::move(dex_file)); + return true; + } else { + return false; + } + } + *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename); + return false; +} + +std::unique_ptr<const DexFile> ArtDexFileLoader::OpenDex(int fd, + const std::string& location, + bool verify, + bool verify_checksum, + std::string* error_msg) const { + ScopedTrace trace("Open dex file " + std::string(location)); + return OpenFile(fd, location, verify, verify_checksum, error_msg); +} + +bool ArtDexFileLoader::OpenZip(int fd, + const std::string& location, + bool verify, + bool verify_checksum, + std::string* error_msg, + std::vector<std::unique_ptr<const DexFile>>* dex_files) const { + ScopedTrace trace("Dex file open Zip " + std::string(location)); + DCHECK(dex_files != nullptr) << "DexFile::OpenZip: out-param is nullptr"; + std::unique_ptr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(fd, location.c_str(), error_msg)); + if (zip_archive.get() == nullptr) { + DCHECK(!error_msg->empty()); + return false; + } + return OpenAllDexFilesFromZip( + *zip_archive, location, verify, verify_checksum, error_msg, dex_files); +} + +std::unique_ptr<const DexFile> ArtDexFileLoader::OpenFile(int fd, + const std::string& location, + bool verify, + bool verify_checksum, + std::string* error_msg) const { + ScopedTrace trace(std::string("Open dex file ") + std::string(location)); + CHECK(!location.empty()); + std::unique_ptr<MemMap> map; + { + File delayed_close(fd, /* check_usage */ false); + struct stat sbuf; + memset(&sbuf, 0, sizeof(sbuf)); + if (fstat(fd, &sbuf) == -1) { + *error_msg = StringPrintf("DexFile: fstat '%s' failed: %s", location.c_str(), + strerror(errno)); + return nullptr; + } + if (S_ISDIR(sbuf.st_mode)) { + *error_msg = StringPrintf("Attempt to mmap directory '%s'", location.c_str()); + return nullptr; + } + size_t length = sbuf.st_size; + map.reset(MemMap::MapFile(length, + PROT_READ, + MAP_PRIVATE, + fd, + 0, + /*low_4gb*/false, + location.c_str(), + error_msg)); + if (map == nullptr) { + DCHECK(!error_msg->empty()); + return nullptr; + } + } + + if (map->Size() < sizeof(DexFile::Header)) { + *error_msg = StringPrintf( + "DexFile: failed to open dex file '%s' that is too short to have a header", + location.c_str()); + return nullptr; + } + + const DexFile::Header* dex_header = reinterpret_cast<const DexFile::Header*>(map->Begin()); + + std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(), + map->Size(), + location, + dex_header->checksum_, + kNoOatDexFile, + verify, + verify_checksum, + error_msg, + new MemMapContainer(std::move(map)), + /*verify_result*/ nullptr); + + return dex_file; +} + +std::unique_ptr<const DexFile> ArtDexFileLoader::OpenOneDexFileFromZip( + const ZipArchive& zip_archive, + const char* entry_name, + const std::string& location, + bool verify, + bool verify_checksum, + std::string* error_msg, + ZipOpenErrorCode* error_code) const { + ScopedTrace trace("Dex file open from Zip Archive " + std::string(location)); + CHECK(!location.empty()); + std::unique_ptr<ZipEntry> zip_entry(zip_archive.Find(entry_name, error_msg)); + if (zip_entry == nullptr) { + *error_code = ZipOpenErrorCode::kEntryNotFound; + return nullptr; + } + if (zip_entry->GetUncompressedLength() == 0) { + *error_msg = StringPrintf("Dex file '%s' has zero length", location.c_str()); + *error_code = ZipOpenErrorCode::kDexFileError; + return nullptr; + } + + std::unique_ptr<MemMap> map; + if (zip_entry->IsUncompressed()) { + if (!zip_entry->IsAlignedTo(alignof(DexFile::Header))) { + // Do not mmap unaligned ZIP entries because + // doing so would fail dex verification which requires 4 byte alignment. + LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; " + << "please zipalign to " << alignof(DexFile::Header) << " bytes. " + << "Falling back to extracting file."; + } else { + // Map uncompressed files within zip as file-backed to avoid a dirty copy. + map.reset(zip_entry->MapDirectlyFromFile(location.c_str(), /*out*/error_msg)); + if (map == nullptr) { + LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; " + << "is your ZIP file corrupted? Falling back to extraction."; + // Try again with Extraction which still has a chance of recovery. + } + } + } + + if (map == nullptr) { + // Default path for compressed ZIP entries, + // and fallback for stored ZIP entries. + map.reset(zip_entry->ExtractToMemMap(location.c_str(), entry_name, error_msg)); + } + + if (map == nullptr) { + *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", entry_name, location.c_str(), + error_msg->c_str()); + *error_code = ZipOpenErrorCode::kExtractToMemoryError; + return nullptr; + } + VerifyResult verify_result; + std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(), + map->Size(), + location, + zip_entry->GetCrc32(), + kNoOatDexFile, + verify, + verify_checksum, + error_msg, + new MemMapContainer(std::move(map)), + &verify_result); + if (dex_file == nullptr) { + if (verify_result == VerifyResult::kVerifyNotAttempted) { + *error_code = ZipOpenErrorCode::kDexFileError; + } else { + *error_code = ZipOpenErrorCode::kVerifyError; + } + return nullptr; + } + if (!dex_file->DisableWrite()) { + *error_msg = StringPrintf("Failed to make dex file '%s' read only", location.c_str()); + *error_code = ZipOpenErrorCode::kMakeReadOnlyError; + return nullptr; + } + CHECK(dex_file->IsReadOnly()) << location; + if (verify_result != VerifyResult::kVerifySucceeded) { + *error_code = ZipOpenErrorCode::kVerifyError; + return nullptr; + } + *error_code = ZipOpenErrorCode::kNoError; + return dex_file; +} + +// Technically we do not have a limitation with respect to the number of dex files that can be in a +// multidex APK. However, it's bad practice, as each dex file requires its own tables for symbols +// (types, classes, methods, ...) and dex caches. So warn the user that we open a zip with what +// seems an excessive number. +static constexpr size_t kWarnOnManyDexFilesThreshold = 100; + +bool ArtDexFileLoader::OpenAllDexFilesFromZip( + const ZipArchive& zip_archive, + const std::string& location, + bool verify, + bool verify_checksum, + std::string* error_msg, + std::vector<std::unique_ptr<const DexFile>>* dex_files) const { + 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(OpenOneDexFileFromZip(zip_archive, + kClassesDex, + location, + verify, + verify_checksum, + error_msg, + &error_code)); + if (dex_file.get() == nullptr) { + return false; + } else { + // Had at least classes.dex. + dex_files->push_back(std::move(dex_file)); + + // Now try some more. + + // We could try to avoid std::string allocations by working on a char array directly. As we + // do not expect a lot of iterations, this seems too involved and brittle. + + 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(OpenOneDexFileFromZip(zip_archive, + name.c_str(), + fake_location, + verify, + verify_checksum, + error_msg, + &error_code)); + if (next_dex_file.get() == nullptr) { + if (error_code != ZipOpenErrorCode::kEntryNotFound) { + LOG(WARNING) << "Zip open failed: " << *error_msg; + } + break; + } else { + dex_files->push_back(std::move(next_dex_file)); + } + + if (i == kWarnOnManyDexFilesThreshold) { + LOG(WARNING) << location << " has in excess of " << kWarnOnManyDexFilesThreshold + << " dex files. Please consider coalescing and shrinking the number to " + " avoid runtime overhead."; + } + + if (i == std::numeric_limits<size_t>::max()) { + LOG(ERROR) << "Overflow in number of dex files!"; + break; + } + } + + return true; + } +} + +} // namespace art diff --git a/runtime/dex/art_dex_file_loader.h b/runtime/dex/art_dex_file_loader.h new file mode 100644 index 0000000000..a6191d9f54 --- /dev/null +++ b/runtime/dex/art_dex_file_loader.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2017 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_RUNTIME_DEX_ART_DEX_FILE_LOADER_H_ +#define ART_RUNTIME_DEX_ART_DEX_FILE_LOADER_H_ + +#include <cstdint> +#include <memory> +#include <string> +#include <vector> + +#include "dex_file_loader.h" +#include "base/macros.h" + +namespace art { + +class DexFile; +class DexFileContainer; +class MemMap; +class OatDexFile; +class ZipArchive; + +// Class that is used to open dex files and deal with corresponding multidex and location logic. +class ArtDexFileLoader : public DexFileLoader { + public: + virtual ~ArtDexFileLoader() { } + + // Returns the checksums of a file for comparison with GetLocationChecksum(). + // For .dex files, this is the single header checksum. + // For zip files, this is the zip entry CRC32 checksum for classes.dex and + // each additional multidex entry classes2.dex, classes3.dex, etc. + // If a valid zip_fd is provided the file content will be read directly from + // the descriptor and `filename` will be used as alias for error logging. If + // zip_fd is -1, the method will try to open the `filename` and read the + // content from it. + // Return true if the checksums could be found, false otherwise. + bool GetMultiDexChecksums(const char* filename, + std::vector<uint32_t>* checksums, + std::string* error_msg, + int zip_fd = -1) const OVERRIDE; + + // Opens .dex file, backed by existing memory + std::unique_ptr<const DexFile> Open(const uint8_t* base, + size_t size, + const std::string& location, + uint32_t location_checksum, + const OatDexFile* oat_dex_file, + bool verify, + bool verify_checksum, + std::string* error_msg) const OVERRIDE; + + // Opens .dex file that has been memory-mapped by the caller. + std::unique_ptr<const DexFile> Open(const std::string& location, + uint32_t location_checkum, + std::unique_ptr<MemMap> mem_map, + bool verify, + bool verify_checksum, + std::string* error_msg) const OVERRIDE; + + // Opens all .dex files found in the file, guessing the container format based on file extension. + bool Open(const char* filename, + const std::string& location, + bool verify, + bool verify_checksum, + std::string* error_msg, + std::vector<std::unique_ptr<const DexFile>>* dex_files) const OVERRIDE; + + // Open a single dex file from an fd. This function closes the fd. + std::unique_ptr<const DexFile> OpenDex(int fd, + const std::string& location, + bool verify, + bool verify_checksum, + std::string* error_msg) const OVERRIDE; + + // Opens dex files from within a .jar, .zip, or .apk file + bool OpenZip(int fd, + const std::string& location, + bool verify, + bool verify_checksum, + std::string* error_msg, + std::vector<std::unique_ptr<const DexFile>>* dex_files) const OVERRIDE; + + private: + std::unique_ptr<const DexFile> OpenFile(int fd, + const std::string& location, + bool verify, + bool verify_checksum, + std::string* error_msg) const OVERRIDE; + + // Open all classesXXX.dex files from a zip archive. + bool OpenAllDexFilesFromZip(const ZipArchive& zip_archive, + const std::string& location, + bool verify, + bool verify_checksum, + std::string* error_msg, + std::vector<std::unique_ptr<const DexFile>>* dex_files) + const OVERRIDE; + + // Opens .dex file from the entry_name in a zip archive. error_code is undefined when non-null + // return. + std::unique_ptr<const DexFile> OpenOneDexFileFromZip(const ZipArchive& zip_archive, + const char* entry_name, + const std::string& location, + bool verify, + bool verify_checksum, + std::string* error_msg, + ZipOpenErrorCode* error_code) const OVERRIDE; +}; + +} // namespace art + +#endif // ART_RUNTIME_DEX_ART_DEX_FILE_LOADER_H_ diff --git a/runtime/dex/code_item_accessors-inl.h b/runtime/dex/code_item_accessors-inl.h index 2792dc0663..63fd120991 100644 --- a/runtime/dex/code_item_accessors-inl.h +++ b/runtime/dex/code_item_accessors-inl.h @@ -34,15 +34,9 @@ inline CodeItemDataAccessor::CodeItemDataAccessor(ArtMethod* method) : CodeItemDataAccessor(*method->GetDexFile(), method->GetCodeItem()) {} inline CodeItemDebugInfoAccessor::CodeItemDebugInfoAccessor(ArtMethod* method) - : CodeItemDebugInfoAccessor(*method->GetDexFile(), method->GetCodeItem()) {} - -inline CodeItemDebugInfoAccessor::CodeItemDebugInfoAccessor(const DexFile& dex_file, - const DexFile::CodeItem* code_item) { - if (code_item == nullptr) { - return; - } - Init(dex_file, code_item, OatFile::GetDebugInfoOffset(dex_file, code_item->debug_info_off_)); -} + : CodeItemDebugInfoAccessor(*method->GetDexFile(), + method->GetCodeItem(), + method->GetDexMethodIndex()) {} } // namespace art diff --git a/runtime/dex/code_item_accessors-no_art-inl.h b/runtime/dex/code_item_accessors-no_art-inl.h index baea856e71..aaa86d4b14 100644 --- a/runtime/dex/code_item_accessors-no_art-inl.h +++ b/runtime/dex/code_item_accessors-no_art-inl.h @@ -146,22 +146,28 @@ inline const void* CodeItemDataAccessor::CodeItemDataEnd() const { inline void CodeItemDebugInfoAccessor::Init(const DexFile& dex_file, const DexFile::CodeItem* code_item, - uint32_t debug_info_offset) { + uint32_t dex_method_index) { + if (code_item == nullptr) { + return; + } dex_file_ = &dex_file; - debug_info_offset_ = debug_info_offset; if (dex_file.IsCompactDexFile()) { - Init(down_cast<const CompactDexFile::CodeItem&>(*code_item)); + Init(down_cast<const CompactDexFile::CodeItem&>(*code_item), dex_method_index); } else { DCHECK(dex_file.IsStandardDexFile()); Init(down_cast<const StandardDexFile::CodeItem&>(*code_item)); } } -inline void CodeItemDebugInfoAccessor::Init(const CompactDexFile::CodeItem& code_item) { +inline void CodeItemDebugInfoAccessor::Init(const CompactDexFile::CodeItem& code_item, + uint32_t dex_method_index) { + debug_info_offset_ = down_cast<const CompactDexFile*>(dex_file_)->GetDebugInfoOffset( + dex_method_index); CodeItemDataAccessor::Init(code_item); } inline void CodeItemDebugInfoAccessor::Init(const StandardDexFile::CodeItem& code_item) { + debug_info_offset_ = code_item.debug_info_off_; CodeItemDataAccessor::Init(code_item); } diff --git a/runtime/dex/code_item_accessors.h b/runtime/dex/code_item_accessors.h index b5a6957548..66531f96bc 100644 --- a/runtime/dex/code_item_accessors.h +++ b/runtime/dex/code_item_accessors.h @@ -131,20 +131,16 @@ class CodeItemDebugInfoAccessor : public CodeItemDataAccessor { public: CodeItemDebugInfoAccessor() = default; - // Handles null code items, but not null dex files. - ALWAYS_INLINE CodeItemDebugInfoAccessor(const DexFile& dex_file, - const DexFile::CodeItem* code_item); - // Initialize with an existing offset. ALWAYS_INLINE CodeItemDebugInfoAccessor(const DexFile& dex_file, const DexFile::CodeItem* code_item, - uint32_t debug_info_offset) { - Init(dex_file, code_item, debug_info_offset); + uint32_t dex_method_index) { + Init(dex_file, code_item, dex_method_index); } ALWAYS_INLINE void Init(const DexFile& dex_file, const DexFile::CodeItem* code_item, - uint32_t debug_info_offset); + uint32_t dex_method_index); ALWAYS_INLINE explicit CodeItemDebugInfoAccessor(ArtMethod* method); @@ -159,7 +155,7 @@ class CodeItemDebugInfoAccessor : public CodeItemDataAccessor { void* context) const; protected: - ALWAYS_INLINE void Init(const CompactDexFile::CodeItem& code_item); + ALWAYS_INLINE void Init(const CompactDexFile::CodeItem& code_item, uint32_t dex_method_index); ALWAYS_INLINE void Init(const StandardDexFile::CodeItem& code_item); private: diff --git a/runtime/dex/code_item_accessors_test.cc b/runtime/dex/code_item_accessors_test.cc index b29d10b113..2e219562e9 100644 --- a/runtime/dex/code_item_accessors_test.cc +++ b/runtime/dex/code_item_accessors_test.cc @@ -19,6 +19,7 @@ #include <memory> #include "common_runtime_test.h" +#include "art_dex_file_loader.h" #include "dex_file_loader.h" #include "mem_map.h" @@ -44,13 +45,13 @@ std::unique_ptr<const DexFile> CreateFakeDex(bool compact_dex) { StandardDexFile::WriteMagic(map->Begin()); StandardDexFile::WriteCurrentVersion(map->Begin()); } - std::unique_ptr<const DexFile> dex( - DexFileLoader::Open("location", - /*location_checksum*/ 123, - std::move(map), - /*verify*/false, - /*verify_checksum*/false, - &error_msg)); + const ArtDexFileLoader dex_file_loader; + std::unique_ptr<const DexFile> dex(dex_file_loader.Open("location", + /*location_checksum*/ 123, + std::move(map), + /*verify*/false, + /*verify_checksum*/false, + &error_msg)); CHECK(dex != nullptr) << error_msg; return dex; } diff --git a/runtime/dex/compact_dex_debug_info.cc b/runtime/dex/compact_dex_debug_info.cc new file mode 100644 index 0000000000..19495ca92c --- /dev/null +++ b/runtime/dex/compact_dex_debug_info.cc @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2018 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 "compact_dex_debug_info.h" + +#include "compact_dex_utils.h" +#include "leb128.h" + +namespace art { + +constexpr size_t CompactDexDebugInfoOffsetTable::kElementsPerIndex; + +CompactDexDebugInfoOffsetTable::Accessor::Accessor(const uint8_t* data_begin, + uint32_t debug_info_base, + uint32_t debug_info_table_offset) + : table_(reinterpret_cast<const uint32_t*>(data_begin + debug_info_table_offset)), + debug_info_base_(debug_info_base), + data_begin_(data_begin) {} + +uint32_t CompactDexDebugInfoOffsetTable::Accessor::GetDebugInfoOffset(uint32_t method_idx) const { + const uint32_t offset = table_[method_idx / kElementsPerIndex]; + const size_t bit_index = method_idx % kElementsPerIndex; + + const uint8_t* block = data_begin_ + offset; + uint16_t bit_mask = *block; + ++block; + bit_mask = (bit_mask << kBitsPerByte) | *block; + ++block; + if ((bit_mask & (1 << bit_index)) == 0) { + // Bit is not set means the offset is 0 for the debug info. + return 0u; + } + // Trim off the bits above the index we want and count how many bits are set. This is how many + // lebs we need to decode. + size_t count = POPCOUNT(static_cast<uintptr_t>(bit_mask) << (kBitsPerIntPtrT - 1 - bit_index)); + DCHECK_GT(count, 0u); + uint32_t current_offset = debug_info_base_; + do { + current_offset += DecodeUnsignedLeb128(&block); + --count; + } while (count > 0); + return current_offset; +} + +void CompactDexDebugInfoOffsetTable::Build(const std::vector<uint32_t>& debug_info_offsets, + std::vector<uint8_t>* out_data, + uint32_t* out_min_offset, + uint32_t* out_table_offset) { + DCHECK(out_data != nullptr); + DCHECK(out_data->empty()); + // Calculate the base offset and return it. + *out_min_offset = std::numeric_limits<uint32_t>::max(); + for (const uint32_t offset : debug_info_offsets) { + if (offset != 0u) { + *out_min_offset = std::min(*out_min_offset, offset); + } + } + // Write the leb blocks and store the important offsets (each kElementsPerIndex elements). + size_t block_start = 0; + + std::vector<uint32_t> offset_table; + + // Write data first then the table. + while (block_start < debug_info_offsets.size()) { + // Write the offset of the block for each block. + offset_table.push_back(out_data->size()); + + // Block size of up to kElementsPerIndex + const size_t block_size = std::min(debug_info_offsets.size() - block_start, kElementsPerIndex); + + // Calculate bit mask since need to write that first. + uint16_t bit_mask = 0u; + for (size_t i = 0; i < block_size; ++i) { + if (debug_info_offsets[block_start + i] != 0u) { + bit_mask |= 1 << i; + } + } + // Write bit mask. + out_data->push_back(static_cast<uint8_t>(bit_mask >> kBitsPerByte)); + out_data->push_back(static_cast<uint8_t>(bit_mask)); + + // Write debug info offsets relative to the current offset. + uint32_t current_offset = *out_min_offset; + for (size_t i = 0; i < block_size; ++i) { + const uint32_t debug_info_offset = debug_info_offsets[block_start + i]; + if (debug_info_offset != 0u) { + uint32_t delta = debug_info_offset - current_offset; + EncodeUnsignedLeb128(out_data, delta); + current_offset = debug_info_offset; + } + } + + block_start += block_size; + } + + // Write the offset table. + AlignmentPadVector(out_data, alignof(uint32_t)); + *out_table_offset = out_data->size(); + out_data->insert(out_data->end(), + reinterpret_cast<const uint8_t*>(&offset_table[0]), + reinterpret_cast<const uint8_t*>(&offset_table[0] + offset_table.size())); +} + +} // namespace art diff --git a/runtime/dex/compact_dex_debug_info.h b/runtime/dex/compact_dex_debug_info.h new file mode 100644 index 0000000000..1aff75879e --- /dev/null +++ b/runtime/dex/compact_dex_debug_info.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2018 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_RUNTIME_DEX_COMPACT_DEX_DEBUG_INFO_H_ +#define ART_RUNTIME_DEX_COMPACT_DEX_DEBUG_INFO_H_ + +#include <cstdint> +#include <vector> + +namespace art { + +// Debug offset table for compact dex, aims to minimize size while still providing reasonable +// speed (10-20ns average time per lookup on host). +class CompactDexDebugInfoOffsetTable { + public: + // This value is coupled with the leb chunk bitmask. That logic must also be adjusted when the + // integer is modified. + static constexpr size_t kElementsPerIndex = 16; + + // Leb block format: + // [uint16_t] 16 bit mask for what method ids actually have a debug info offset for the chunk. + // [lebs] Up to 16 lebs encoded using leb128, one leb bit. The leb specifies how the offset + // changes compared to the previous index. + + class Accessor { + public: + Accessor(const uint8_t* data_begin, + uint32_t debug_info_base, + uint32_t debug_info_table_offset); + + // Return the debug info for a method index (or 0 if it doesn't have one). + uint32_t GetDebugInfoOffset(uint32_t method_idx) const; + + private: + const uint32_t* const table_; + const uint32_t debug_info_base_; + const uint8_t* const data_begin_; + }; + + // Returned offsets are all relative to debug_info_offsets. + static void Build(const std::vector<uint32_t>& debug_info_offsets, + std::vector<uint8_t>* out_data, + uint32_t* out_min_offset, + uint32_t* out_table_offset); + + // 32 bit aligned for the offset table. + static constexpr size_t kAlignment = sizeof(uint32_t); +}; + +} // namespace art + +#endif // ART_RUNTIME_DEX_COMPACT_DEX_DEBUG_INFO_H_ diff --git a/runtime/dex/compact_dex_debug_info_test.cc b/runtime/dex/compact_dex_debug_info_test.cc new file mode 100644 index 0000000000..02b95e68d7 --- /dev/null +++ b/runtime/dex/compact_dex_debug_info_test.cc @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2018 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 <vector> +#include <sys/mman.h> + +#include "base/logging.h" +#include "dex/compact_dex_debug_info.h" +#include "gtest/gtest.h" +#include "mem_map.h" + +namespace art { + +TEST(CompactDexDebugInfoTest, TestBuildAndAccess) { + MemMap::Init(); + + const size_t kDebugInfoMinOffset = 1234567; + std::vector<uint32_t> offsets = { + 0, 17, 2, 3, 11, 0, 0, 0, 0, 1, 0, 1552, 100, 122, 44, 1234567, 0, 0, + std::numeric_limits<uint32_t>::max() - kDebugInfoMinOffset, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, + }; + // Add some large offset since the debug info section will never be that close to the beginning + // of the file. + for (uint32_t& offset : offsets) { + if (offset != 0u) { + offset += kDebugInfoMinOffset; + } + } + + std::vector<uint8_t> data; + uint32_t base_offset = 0; + uint32_t table_offset = 0; + CompactDexDebugInfoOffsetTable::Build(offsets, + /*out*/ &data, + /*out*/ &base_offset, + /*out*/ &table_offset); + EXPECT_GE(base_offset, kDebugInfoMinOffset); + EXPECT_LT(table_offset, data.size()); + ASSERT_GT(data.size(), 0u); + const size_t before_size = offsets.size() * sizeof(offsets.front()); + EXPECT_LT(data.size(), before_size); + + // Note that the accessor requires the data to be aligned. Use memmap to accomplish this. + std::string error_msg; + // Leave some extra room since we don't copy the table at the start (for testing). + constexpr size_t kExtraOffset = 4 * 128; + std::unique_ptr<MemMap> fake_dex(MemMap::MapAnonymous("fake dex", + nullptr, + data.size() + kExtraOffset, + PROT_READ | PROT_WRITE, + /*low_4gb*/ false, + /*reuse*/ false, + &error_msg)); + ASSERT_TRUE(fake_dex != nullptr) << error_msg; + std::copy(data.begin(), data.end(), fake_dex->Begin() + kExtraOffset); + + CompactDexDebugInfoOffsetTable::Accessor accessor(fake_dex->Begin() + kExtraOffset, + base_offset, + table_offset); + for (size_t i = 0; i < offsets.size(); ++i) { + EXPECT_EQ(offsets[i], accessor.GetDebugInfoOffset(i)); + } + + // Sort to produce a try and produce a smaller table. This happens because the leb diff is smaller + // for sorted increasing order. + std::sort(offsets.begin(), offsets.end()); + std::vector<uint8_t> sorted_data; + CompactDexDebugInfoOffsetTable::Build(offsets, + /*out*/ &sorted_data, + /*out*/ &base_offset, + /*out*/ &table_offset); + EXPECT_LT(sorted_data.size(), data.size()); + { + ScopedLogSeverity sls(LogSeverity::INFO); + LOG(INFO) << "raw size " << before_size + << " table size " << data.size() + << " sorted table size " << sorted_data.size(); + } +} + +} // namespace art diff --git a/runtime/dex/compact_dex_file.cc b/runtime/dex/compact_dex_file.cc index 2d1ee0420e..ff193ffb07 100644 --- a/runtime/dex/compact_dex_file.cc +++ b/runtime/dex/compact_dex_file.cc @@ -63,4 +63,21 @@ uint32_t CompactDexFile::GetCodeItemSize(const DexFile::CodeItem& item) const { reinterpret_cast<uintptr_t>(&item); } +CompactDexFile::CompactDexFile(const uint8_t* base, + size_t size, + const std::string& location, + uint32_t location_checksum, + const OatDexFile* oat_dex_file, + DexFileContainer* container) + : DexFile(base, + size, + location, + location_checksum, + oat_dex_file, + container, + /*is_compact_dex*/ true), + debug_info_offsets_(Begin() + GetHeader().debug_info_offsets_pos_, + GetHeader().debug_info_base_, + GetHeader().debug_info_offsets_table_offset_) {} + } // namespace art diff --git a/runtime/dex/compact_dex_file.h b/runtime/dex/compact_dex_file.h index 280c6f70cc..af782a981a 100644 --- a/runtime/dex/compact_dex_file.h +++ b/runtime/dex/compact_dex_file.h @@ -19,6 +19,7 @@ #include "base/casts.h" #include "dex_file.h" +#include "dex/compact_dex_debug_info.h" namespace art { @@ -41,13 +42,45 @@ class CompactDexFile : public DexFile { private: uint32_t feature_flags_ = 0u; + // Position in the compact dex file for the debug info table data starts. + uint32_t debug_info_offsets_pos_ = 0u; + + // Offset into the debug info table data where the lookup table is. + uint32_t debug_info_offsets_table_offset_ = 0u; + + // Base offset of where debug info starts in the dex file. + uint32_t debug_info_base_ = 0u; + + friend class CompactDexFile; friend class CompactDexWriter; }; + // Like the standard code item except without a debug info offset. struct CodeItem : public DexFile::CodeItem { + static constexpr size_t kAlignment = sizeof(uint32_t); + private: - // TODO: Insert compact dex specific fields here. + CodeItem() = default; + + uint16_t registers_size_; // the number of registers used by this code + // (locals + parameters) + uint16_t ins_size_; // the number of words of incoming arguments to the method + // that this code is for + uint16_t outs_size_; // the number of words of outgoing argument space required + // by this code for method invocation + uint16_t tries_size_; // the number of try_items for this instance. If non-zero, + // then these appear as the tries array just after the + // insns in this instance. + + uint32_t insns_size_in_code_units_; // size of the insns array, in 2 byte code units + uint16_t insns_[1]; // actual array of bytecode. + + ART_FRIEND_TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor); + friend class CodeItemDataAccessor; + friend class CodeItemDebugInfoAccessor; + friend class CodeItemInstructionAccessor; friend class CompactDexFile; + friend class CompactDexWriter; DISALLOW_COPY_AND_ASSIGN(CodeItem); }; @@ -73,25 +106,22 @@ class CompactDexFile : public DexFile { uint32_t GetCodeItemSize(const DexFile::CodeItem& item) const OVERRIDE; + uint32_t GetDebugInfoOffset(uint32_t dex_method_index) const { + return debug_info_offsets_.GetDebugInfoOffset(dex_method_index); + } + private: - // Not supported yet. CompactDexFile(const uint8_t* base, size_t size, const std::string& location, uint32_t location_checksum, const OatDexFile* oat_dex_file, - DexFileContainer* container) - : DexFile(base, - size, - location, - location_checksum, - oat_dex_file, - container, - /*is_compact_dex*/ true) {} + DexFileContainer* container); + + CompactDexDebugInfoOffsetTable::Accessor debug_info_offsets_; friend class DexFile; friend class DexFileLoader; - DISALLOW_COPY_AND_ASSIGN(CompactDexFile); }; diff --git a/runtime/dex/compact_dex_utils.h b/runtime/dex/compact_dex_utils.h new file mode 100644 index 0000000000..1c7e9514fd --- /dev/null +++ b/runtime/dex/compact_dex_utils.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2018 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_RUNTIME_DEX_COMPACT_DEX_UTILS_H_ +#define ART_RUNTIME_DEX_COMPACT_DEX_UTILS_H_ + +#include <vector> + +#include "base/bit_utils.h" + +namespace art { + +// Add padding to the end of the array until the size is aligned. +template <typename T, template<typename> class Allocator> +static inline void AlignmentPadVector(std::vector<T, Allocator<T>>* dest, + size_t alignment) { + while (!IsAlignedParam(dest->size(), alignment)) { + dest->push_back(T()); + } +} + +} // namespace art + +#endif // ART_RUNTIME_DEX_COMPACT_DEX_UTILS_H_ diff --git a/runtime/dex/dex_file.h b/runtime/dex/dex_file.h index c2a36ce01a..183d84e15d 100644 --- a/runtime/dex/dex_file.h +++ b/runtime/dex/dex_file.h @@ -301,43 +301,12 @@ class DexFile { DISALLOW_COPY_AND_ASSIGN(CallSiteIdItem); }; - // Raw code_item. + // Base code_item, compact dex and standard dex have different code item layouts. struct CodeItem { - // Used when quickening / unquickening. - void SetDebugInfoOffset(uint32_t new_offset) { - debug_info_off_ = new_offset; - } - - uint32_t GetDebugInfoOffset() const { - return debug_info_off_; - } - protected: - uint16_t registers_size_; // the number of registers used by this code - // (locals + parameters) - uint16_t ins_size_; // the number of words of incoming arguments to the method - // that this code is for - uint16_t outs_size_; // the number of words of outgoing argument space required - // by this code for method invocation - uint16_t tries_size_; // the number of try_items for this instance. If non-zero, - // then these appear as the tries array just after the - // insns in this instance. - // Normally holds file offset to debug info stream. In case the method has been quickened - // holds an offset in the Vdex file containing both the actual debug_info_off and the - // quickening info offset. - // Don't use this field directly, use OatFile::GetDebugInfoOffset in general ART code, - // or DexFile::GetDebugInfoOffset in code that are not using a Runtime. - uint32_t debug_info_off_; - - uint32_t insns_size_in_code_units_; // size of the insns array, in 2 byte code units - uint16_t insns_[1]; // actual array of bytecode. + CodeItem() = default; private: - ART_FRIEND_TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor); - friend class CodeItemDataAccessor; - friend class CodeItemDebugInfoAccessor; - friend class CodeItemInstructionAccessor; - friend class VdexFile; // TODO: Remove this one when it's cleaned up. DISALLOW_COPY_AND_ASSIGN(CodeItem); }; @@ -348,6 +317,8 @@ class DexFile { uint16_t handler_off_; private: + TryItem() = default; + friend class DexWriter; DISALLOW_COPY_AND_ASSIGN(TryItem); }; @@ -712,15 +683,6 @@ class DexFile { return reinterpret_cast<const CodeItem*>(addr); } - uint32_t GetDebugInfoOffset(const CodeItem* code_item) const { - if (code_item == nullptr) { - return 0; - } - CHECK(oat_dex_file_ == nullptr) - << "Should only use GetDebugInfoOffset in a non runtime setup"; - return code_item->GetDebugInfoOffset(); - } - const char* GetReturnTypeDescriptor(const ProtoId& proto_id) const; // Returns the number of prototype identifiers in the .dex file. diff --git a/runtime/dex/dex_file_loader.cc b/runtime/dex/dex_file_loader.cc index fafd69889d..10aef56125 100644 --- a/runtime/dex/dex_file_loader.cc +++ b/runtime/dex/dex_file_loader.cc @@ -16,72 +16,25 @@ #include "dex_file_loader.h" -#include <sys/mman.h> // For the PROT_* and MAP_* constants. -#include <sys/stat.h> +// #include <sys/mman.h> // For the PROT_* and MAP_* constants. +// #include <sys/stat.h> #include "android-base/stringprintf.h" #include "base/file_magic.h" #include "base/stl_util.h" -#include "base/systrace.h" -#include "base/unix_file/fd_file.h" +// #include "base/systrace.h" +// #include "base/unix_file/fd_file.h" #include "compact_dex_file.h" #include "dex_file.h" #include "dex_file_verifier.h" #include "standard_dex_file.h" -#include "zip_archive.h" +// #include "zip_archive.h" namespace art { -namespace { - -class MemMapContainer : public DexFileContainer { - public: - explicit MemMapContainer(std::unique_ptr<MemMap>&& mem_map) : mem_map_(std::move(mem_map)) { } - virtual ~MemMapContainer() OVERRIDE { } - - int GetPermissions() OVERRIDE { - if (mem_map_.get() == nullptr) { - return 0; - } else { - return mem_map_->GetProtect(); - } - } - - bool IsReadOnly() OVERRIDE { - return GetPermissions() == PROT_READ; - } - - bool EnableWrite() OVERRIDE { - CHECK(IsReadOnly()); - if (mem_map_.get() == nullptr) { - return false; - } else { - return mem_map_->Protect(PROT_READ | PROT_WRITE); - } - } - - bool DisableWrite() OVERRIDE { - CHECK(!IsReadOnly()); - if (mem_map_.get() == nullptr) { - return false; - } else { - return mem_map_->Protect(PROT_READ); - } - } - - private: - std::unique_ptr<MemMap> mem_map_; - DISALLOW_COPY_AND_ASSIGN(MemMapContainer); -}; - -} // namespace - using android::base::StringPrintf; -static constexpr OatDexFile* kNoOatDexFile = nullptr; - - bool DexFileLoader::IsMagicValid(uint32_t magic) { return IsMagicValid(reinterpret_cast<uint8_t*>(&magic)); } @@ -101,63 +54,6 @@ bool DexFileLoader::IsVersionAndMagicValid(const uint8_t* magic) { return false; } -bool DexFileLoader::GetMultiDexChecksums(const char* filename, - std::vector<uint32_t>* checksums, - std::string* error_msg, - int zip_fd) { - CHECK(checksums != nullptr); - uint32_t magic; - - File fd; - if (zip_fd != -1) { - if (ReadMagicAndReset(zip_fd, &magic, error_msg)) { - fd = File(zip_fd, false /* check_usage */); - } - } else { - fd = OpenAndReadMagic(filename, &magic, error_msg); - } - if (fd.Fd() == -1) { - DCHECK(!error_msg->empty()); - return false; - } - if (IsZipMagic(magic)) { - std::unique_ptr<ZipArchive> zip_archive( - ZipArchive::OpenFromFd(fd.Release(), filename, error_msg)); - if (zip_archive.get() == nullptr) { - *error_msg = StringPrintf("Failed to open zip archive '%s' (error msg: %s)", filename, - error_msg->c_str()); - return false; - } - - uint32_t i = 0; - std::string zip_entry_name = GetMultiDexClassesDexName(i++); - std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(zip_entry_name.c_str(), error_msg)); - if (zip_entry.get() == nullptr) { - *error_msg = StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", filename, - zip_entry_name.c_str(), error_msg->c_str()); - return false; - } - - do { - checksums->push_back(zip_entry->GetCrc32()); - zip_entry_name = GetMultiDexClassesDexName(i++); - zip_entry.reset(zip_archive->Find(zip_entry_name.c_str(), error_msg)); - } while (zip_entry.get() != nullptr); - return true; - } - if (IsMagicValid(magic)) { - std::unique_ptr<const DexFile> dex_file( - OpenFile(fd.Release(), filename, false, false, error_msg)); - if (dex_file == nullptr) { - return false; - } - checksums->push_back(dex_file->GetHeader().checksum_); - return true; - } - *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename); - return false; -} - bool DexFileLoader::IsMultiDexLocation(const char* location) { return strrchr(location, kMultiDexSeparator) != nullptr; } @@ -187,326 +83,102 @@ std::string DexFileLoader::GetDexCanonicalLocation(const char* dex_location) { } } -std::unique_ptr<const DexFile> DexFileLoader::Open(const uint8_t* base, - size_t size, - const std::string& location, - 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); - return OpenCommon(base, - size, - location, - location_checksum, - oat_dex_file, - verify, - verify_checksum, - error_msg, - /*container*/ nullptr, - /*verify_result*/ nullptr); -} +// All of the implementations here should be independent of the runtime. +// TODO: implement all the virtual methods. -std::unique_ptr<const DexFile> DexFileLoader::Open(const std::string& location, - uint32_t location_checksum, - std::unique_ptr<MemMap> map, - bool verify, - bool verify_checksum, - std::string* error_msg) { - ScopedTrace trace(std::string("Open dex file from mapped-memory ") + location); - CHECK(map.get() != nullptr); - - if (map->Size() < sizeof(DexFile::Header)) { - *error_msg = StringPrintf( - "DexFile: failed to open dex file '%s' that is too short to have a header", - location.c_str()); - return nullptr; - } - - std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(), - map->Size(), - location, - location_checksum, - kNoOatDexFile, - verify, - verify_checksum, - error_msg, - new MemMapContainer(std::move(map)), - /*verify_result*/ nullptr); - return dex_file; +bool DexFileLoader::GetMultiDexChecksums(const char* filename ATTRIBUTE_UNUSED, + std::vector<uint32_t>* checksums ATTRIBUTE_UNUSED, + std::string* error_msg, + int zip_fd ATTRIBUTE_UNUSED) const { + *error_msg = "UNIMPLEMENTED"; + return false; } -bool DexFileLoader::Open(const char* filename, - const std::string& location, - bool verify, - bool verify_checksum, - std::string* error_msg, - std::vector<std::unique_ptr<const DexFile>>* dex_files) { - ScopedTrace trace(std::string("Open dex file ") + std::string(location)); - DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr"; - uint32_t magic; - File fd = OpenAndReadMagic(filename, &magic, error_msg); - if (fd.Fd() == -1) { - DCHECK(!error_msg->empty()); - return false; - } - if (IsZipMagic(magic)) { - return OpenZip(fd.Release(), location, verify, verify_checksum, error_msg, dex_files); - } - if (IsMagicValid(magic)) { - std::unique_ptr<const DexFile> dex_file(OpenFile(fd.Release(), - location, - verify, - verify_checksum, - error_msg)); - if (dex_file.get() != nullptr) { - dex_files->push_back(std::move(dex_file)); - return true; - } else { - return false; - } - } - *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename); +std::unique_ptr<const DexFile> DexFileLoader::Open(const uint8_t* base ATTRIBUTE_UNUSED, + size_t size ATTRIBUTE_UNUSED, + const std::string& location ATTRIBUTE_UNUSED, + uint32_t location_checksum ATTRIBUTE_UNUSED, + const OatDexFile* oat_dex_file ATTRIBUTE_UNUSED, + bool verify ATTRIBUTE_UNUSED, + bool verify_checksum ATTRIBUTE_UNUSED, + std::string* error_msg) const { + *error_msg = "UNIMPLEMENTED"; + return nullptr; +} + +std::unique_ptr<const DexFile> DexFileLoader::Open(const std::string& location ATTRIBUTE_UNUSED, + uint32_t location_checksum ATTRIBUTE_UNUSED, + std::unique_ptr<MemMap> map ATTRIBUTE_UNUSED, + bool verify ATTRIBUTE_UNUSED, + bool verify_checksum ATTRIBUTE_UNUSED, + std::string* error_msg) const { + *error_msg = "UNIMPLEMENTED"; + return nullptr; +} + +bool DexFileLoader::Open( + const char* filename ATTRIBUTE_UNUSED, + const std::string& location ATTRIBUTE_UNUSED, + bool verify ATTRIBUTE_UNUSED, + bool verify_checksum ATTRIBUTE_UNUSED, + std::string* error_msg, + std::vector<std::unique_ptr<const DexFile>>* dex_files ATTRIBUTE_UNUSED) const { + *error_msg = "UNIMPLEMENTED"; return false; } -std::unique_ptr<const DexFile> DexFileLoader::OpenDex(int fd, - const std::string& location, - bool verify, - bool verify_checksum, - std::string* error_msg) { - ScopedTrace trace("Open dex file " + std::string(location)); - return OpenFile(fd, location, verify, verify_checksum, error_msg); +std::unique_ptr<const DexFile> DexFileLoader::OpenDex( + int fd ATTRIBUTE_UNUSED, + const std::string& location ATTRIBUTE_UNUSED, + bool verify ATTRIBUTE_UNUSED, + bool verify_checksum ATTRIBUTE_UNUSED, + std::string* error_msg) const { + *error_msg = "UNIMPLEMENTED"; + return nullptr; } -bool DexFileLoader::OpenZip(int fd, - const std::string& location, - bool verify, - 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"; - std::unique_ptr<ZipArchive> zip_archive(ZipArchive::OpenFromFd(fd, location.c_str(), error_msg)); - if (zip_archive.get() == nullptr) { - DCHECK(!error_msg->empty()); - return false; - } - return OpenAllDexFilesFromZip( - *zip_archive, location, verify, verify_checksum, error_msg, dex_files); +bool DexFileLoader::OpenZip( + int fd ATTRIBUTE_UNUSED, + const std::string& location ATTRIBUTE_UNUSED, + bool verify ATTRIBUTE_UNUSED, + bool verify_checksum ATTRIBUTE_UNUSED, + std::string* error_msg, + std::vector<std::unique_ptr<const DexFile>>* dex_files ATTRIBUTE_UNUSED) const { + *error_msg = "UNIMPLEMENTED"; + return false; } -std::unique_ptr<const DexFile> DexFileLoader::OpenFile(int fd, - const std::string& location, - bool verify, - bool verify_checksum, - std::string* error_msg) { - ScopedTrace trace(std::string("Open dex file ") + std::string(location)); - CHECK(!location.empty()); - std::unique_ptr<MemMap> map; - { - File delayed_close(fd, /* check_usage */ false); - struct stat sbuf; - memset(&sbuf, 0, sizeof(sbuf)); - if (fstat(fd, &sbuf) == -1) { - *error_msg = StringPrintf("DexFile: fstat '%s' failed: %s", location.c_str(), - strerror(errno)); - return nullptr; - } - if (S_ISDIR(sbuf.st_mode)) { - *error_msg = StringPrintf("Attempt to mmap directory '%s'", location.c_str()); - return nullptr; - } - size_t length = sbuf.st_size; - map.reset(MemMap::MapFile(length, - PROT_READ, - MAP_PRIVATE, - fd, - 0, - /*low_4gb*/false, - location.c_str(), - error_msg)); - if (map == nullptr) { - DCHECK(!error_msg->empty()); - return nullptr; - } - } - - if (map->Size() < sizeof(DexFile::Header)) { - *error_msg = StringPrintf( - "DexFile: failed to open dex file '%s' that is too short to have a header", - location.c_str()); - return nullptr; - } - - const DexFile::Header* dex_header = reinterpret_cast<const DexFile::Header*>(map->Begin()); - - std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(), - map->Size(), - location, - dex_header->checksum_, - kNoOatDexFile, - verify, - verify_checksum, - error_msg, - new MemMapContainer(std::move(map)), - /*verify_result*/ nullptr); - - return dex_file; +std::unique_ptr<const DexFile> DexFileLoader::OpenFile( + int fd ATTRIBUTE_UNUSED, + const std::string& location ATTRIBUTE_UNUSED, + bool verify ATTRIBUTE_UNUSED, + bool verify_checksum ATTRIBUTE_UNUSED, + std::string* error_msg) const { + *error_msg = "UNIMPLEMENTED"; + return nullptr; } std::unique_ptr<const DexFile> DexFileLoader::OpenOneDexFileFromZip( - const ZipArchive& zip_archive, - const char* entry_name, - const std::string& location, - bool verify, - bool verify_checksum, + const ZipArchive& zip_archive ATTRIBUTE_UNUSED, + const char* entry_name ATTRIBUTE_UNUSED, + const std::string& location ATTRIBUTE_UNUSED, + bool verify ATTRIBUTE_UNUSED, + bool verify_checksum ATTRIBUTE_UNUSED, std::string* error_msg, - ZipOpenErrorCode* error_code) { - ScopedTrace trace("Dex file open from Zip Archive " + std::string(location)); - CHECK(!location.empty()); - std::unique_ptr<ZipEntry> zip_entry(zip_archive.Find(entry_name, error_msg)); - if (zip_entry == nullptr) { - *error_code = ZipOpenErrorCode::kEntryNotFound; - return nullptr; - } - if (zip_entry->GetUncompressedLength() == 0) { - *error_msg = StringPrintf("Dex file '%s' has zero length", location.c_str()); - *error_code = ZipOpenErrorCode::kDexFileError; - return nullptr; - } - - std::unique_ptr<MemMap> map; - if (zip_entry->IsUncompressed()) { - if (!zip_entry->IsAlignedTo(alignof(DexFile::Header))) { - // Do not mmap unaligned ZIP entries because - // doing so would fail dex verification which requires 4 byte alignment. - LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; " - << "please zipalign to " << alignof(DexFile::Header) << " bytes. " - << "Falling back to extracting file."; - } else { - // Map uncompressed files within zip as file-backed to avoid a dirty copy. - map.reset(zip_entry->MapDirectlyFromFile(location.c_str(), /*out*/error_msg)); - if (map == nullptr) { - LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; " - << "is your ZIP file corrupted? Falling back to extraction."; - // Try again with Extraction which still has a chance of recovery. - } - } - } - - if (map == nullptr) { - // Default path for compressed ZIP entries, - // and fallback for stored ZIP entries. - map.reset(zip_entry->ExtractToMemMap(location.c_str(), entry_name, error_msg)); - } - - if (map == nullptr) { - *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", entry_name, location.c_str(), - error_msg->c_str()); - *error_code = ZipOpenErrorCode::kExtractToMemoryError; - return nullptr; - } - VerifyResult verify_result; - std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(), - map->Size(), - location, - zip_entry->GetCrc32(), - kNoOatDexFile, - verify, - verify_checksum, - error_msg, - new MemMapContainer(std::move(map)), - &verify_result); - if (dex_file == nullptr) { - if (verify_result == VerifyResult::kVerifyNotAttempted) { - *error_code = ZipOpenErrorCode::kDexFileError; - } else { - *error_code = ZipOpenErrorCode::kVerifyError; - } - return nullptr; - } - if (!dex_file->DisableWrite()) { - *error_msg = StringPrintf("Failed to make dex file '%s' read only", location.c_str()); - *error_code = ZipOpenErrorCode::kMakeReadOnlyError; - return nullptr; - } - CHECK(dex_file->IsReadOnly()) << location; - if (verify_result != VerifyResult::kVerifySucceeded) { - *error_code = ZipOpenErrorCode::kVerifyError; - return nullptr; - } - *error_code = ZipOpenErrorCode::kNoError; - return dex_file; + ZipOpenErrorCode* error_code ATTRIBUTE_UNUSED) const { + *error_msg = "UNIMPLEMENTED"; + return nullptr; } -// Technically we do not have a limitation with respect to the number of dex files that can be in a -// multidex APK. However, it's bad practice, as each dex file requires its own tables for symbols -// (types, classes, methods, ...) and dex caches. So warn the user that we open a zip with what -// seems an excessive number. -static constexpr size_t kWarnOnManyDexFilesThreshold = 100; - -bool DexFileLoader::OpenAllDexFilesFromZip(const ZipArchive& zip_archive, - const std::string& location, - bool verify, - 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(OpenOneDexFileFromZip(zip_archive, - kClassesDex, - location, - verify, - verify_checksum, - error_msg, - &error_code)); - if (dex_file.get() == nullptr) { - return false; - } else { - // Had at least classes.dex. - dex_files->push_back(std::move(dex_file)); - - // Now try some more. - - // We could try to avoid std::string allocations by working on a char array directly. As we - // do not expect a lot of iterations, this seems too involved and brittle. - - 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(OpenOneDexFileFromZip(zip_archive, - name.c_str(), - fake_location, - verify, - verify_checksum, - error_msg, - &error_code)); - if (next_dex_file.get() == nullptr) { - if (error_code != ZipOpenErrorCode::kEntryNotFound) { - LOG(WARNING) << "Zip open failed: " << *error_msg; - } - break; - } else { - dex_files->push_back(std::move(next_dex_file)); - } - - if (i == kWarnOnManyDexFilesThreshold) { - LOG(WARNING) << location << " has in excess of " << kWarnOnManyDexFilesThreshold - << " dex files. Please consider coalescing and shrinking the number to " - " avoid runtime overhead."; - } - - if (i == std::numeric_limits<size_t>::max()) { - LOG(ERROR) << "Overflow in number of dex files!"; - break; - } - } - - return true; - } +bool DexFileLoader::OpenAllDexFilesFromZip( + const ZipArchive& zip_archive ATTRIBUTE_UNUSED, + const std::string& location ATTRIBUTE_UNUSED, + bool verify ATTRIBUTE_UNUSED, + bool verify_checksum ATTRIBUTE_UNUSED, + std::string* error_msg, + std::vector<std::unique_ptr<const DexFile>>* dex_files ATTRIBUTE_UNUSED) const { + *error_msg = "UNIMPLEMENTED"; + return false; } std::unique_ptr<DexFile> DexFileLoader::OpenCommon(const uint8_t* base, diff --git a/runtime/dex/dex_file_loader.h b/runtime/dex/dex_file_loader.h index 7db8d8e08e..6f1afd636f 100644 --- a/runtime/dex/dex_file_loader.h +++ b/runtime/dex/dex_file_loader.h @@ -46,6 +46,8 @@ class DexFileLoader { // Return true if the corresponding version and magic is valid. static bool IsVersionAndMagicValid(const uint8_t* magic); + virtual ~DexFileLoader() { } + // Returns the checksums of a file for comparison with GetLocationChecksum(). // For .dex files, this is the single header checksum. // For zip files, this is the zip entry CRC32 checksum for classes.dex and @@ -55,55 +57,55 @@ class DexFileLoader { // zip_fd is -1, the method will try to open the `filename` and read the // content from it. // Return true if the checksums could be found, false otherwise. - static bool GetMultiDexChecksums(const char* filename, - std::vector<uint32_t>* checksums, - std::string* error_msg, - int zip_fd = -1); + virtual bool GetMultiDexChecksums(const char* filename, + std::vector<uint32_t>* checksums, + std::string* error_msg, + int zip_fd = -1) const; // Check whether a location denotes a multidex dex file. This is a very simple check: returns // whether the string contains the separator character. static bool IsMultiDexLocation(const char* location); // Opens .dex file, backed by existing memory - static std::unique_ptr<const DexFile> Open(const uint8_t* base, - size_t size, - const std::string& location, - uint32_t location_checksum, - const OatDexFile* oat_dex_file, - bool verify, - bool verify_checksum, - std::string* error_msg); + virtual std::unique_ptr<const DexFile> Open(const uint8_t* base, + size_t size, + const std::string& location, + uint32_t location_checksum, + const OatDexFile* oat_dex_file, + bool verify, + bool verify_checksum, + std::string* error_msg) const; // Opens .dex file that has been memory-mapped by the caller. - static std::unique_ptr<const DexFile> Open(const std::string& location, - uint32_t location_checkum, - std::unique_ptr<MemMap> mem_map, - bool verify, - bool verify_checksum, - std::string* error_msg); + virtual std::unique_ptr<const DexFile> Open(const std::string& location, + uint32_t location_checkum, + std::unique_ptr<MemMap> mem_map, + bool verify, + bool verify_checksum, + std::string* error_msg) const; // Opens all .dex files found in the file, guessing the container format based on file extension. - static bool Open(const char* filename, - const std::string& location, - bool verify, - bool verify_checksum, - std::string* error_msg, - std::vector<std::unique_ptr<const DexFile>>* dex_files); + virtual bool Open(const char* filename, + const std::string& location, + bool verify, + bool verify_checksum, + std::string* error_msg, + std::vector<std::unique_ptr<const DexFile>>* dex_files) const; // Open a single dex file from an fd. This function closes the fd. - static std::unique_ptr<const DexFile> OpenDex(int fd, - const std::string& location, - bool verify, - bool verify_checksum, - std::string* error_msg); + virtual std::unique_ptr<const DexFile> OpenDex(int fd, + const std::string& location, + bool verify, + bool verify_checksum, + std::string* error_msg) const; // Opens dex files from within a .jar, .zip, or .apk file - static bool OpenZip(int fd, - const std::string& location, - bool verify, - bool verify_checksum, - std::string* error_msg, - std::vector<std::unique_ptr<const DexFile>>* dex_files); + virtual bool OpenZip(int fd, + const std::string& location, + bool verify, + bool verify_checksum, + std::string* error_msg, + std::vector<std::unique_ptr<const DexFile>>* dex_files) const; // Return the name of the index-th classes.dex in a multidex zip file. This is classes.dex for // index == 0, and classes{index + 1}.dex else. @@ -148,13 +150,7 @@ class DexFileLoader { return (pos == std::string::npos) ? std::string() : location.substr(pos); } - private: - static std::unique_ptr<const DexFile> OpenFile(int fd, - const std::string& location, - bool verify, - bool verify_checksum, - std::string* error_msg); - + protected: enum class ZipOpenErrorCode { kNoError, kEntryNotFound, @@ -164,24 +160,6 @@ class DexFileLoader { kVerifyError }; - // Open all classesXXX.dex files from a zip archive. - static bool OpenAllDexFilesFromZip(const ZipArchive& zip_archive, - const std::string& location, - bool verify, - bool verify_checksum, - std::string* error_msg, - std::vector<std::unique_ptr<const DexFile>>* dex_files); - - // 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> OpenOneDexFileFromZip(const ZipArchive& zip_archive, - const char* entry_name, - const std::string& location, - bool verify, - bool verify_checksum, - std::string* error_msg, - ZipOpenErrorCode* error_code); - enum class VerifyResult { // private kVerifyNotAttempted, kVerifySucceeded, @@ -198,6 +176,31 @@ class DexFileLoader { std::string* error_msg, DexFileContainer* container, VerifyResult* verify_result); + + private: + virtual std::unique_ptr<const DexFile> OpenFile(int fd, + const std::string& location, + bool verify, + bool verify_checksum, + std::string* error_msg) const; + + // Open all classesXXX.dex files from a zip archive. + virtual bool OpenAllDexFilesFromZip(const ZipArchive& zip_archive, + const std::string& location, + bool verify, + bool verify_checksum, + std::string* error_msg, + std::vector<std::unique_ptr<const DexFile>>* dex_files) const; + + // Opens .dex file from the entry_name in a zip archive. error_code is undefined when non-null + // return. + virtual std::unique_ptr<const DexFile> OpenOneDexFileFromZip(const ZipArchive& zip_archive, + const char* entry_name, + const std::string& location, + bool verify, + bool verify_checksum, + std::string* error_msg, + ZipOpenErrorCode* error_code) const; }; } // namespace art diff --git a/runtime/dex/dex_file_test.cc b/runtime/dex/dex_file_test.cc index 3ee115c01b..cb721af754 100644 --- a/runtime/dex/dex_file_test.cc +++ b/runtime/dex/dex_file_test.cc @@ -20,6 +20,7 @@ #include <memory> +#include "art_dex_file_loader.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "code_item_accessors-inl.h" @@ -237,7 +238,8 @@ static bool OpenDexFilesBase64(const char* base64, ScopedObjectAccess soa(Thread::Current()); static constexpr bool kVerifyChecksum = true; std::vector<std::unique_ptr<const DexFile>> tmp; - bool success = DexFileLoader::Open( + const ArtDexFileLoader dex_file_loader; + bool success = dex_file_loader.Open( location, location, /* verify */ true, kVerifyChecksum, error_msg, &tmp); if (success) { for (std::unique_ptr<const DexFile>& dex_file : tmp) { @@ -277,12 +279,13 @@ static std::unique_ptr<const DexFile> OpenDexFileInMemoryBase64(const char* base /* reuse */ false, &error_message)); memcpy(region->Begin(), dex_bytes.data(), dex_bytes.size()); - std::unique_ptr<const DexFile> dex_file(DexFileLoader::Open(location, - location_checksum, - std::move(region), - /* verify */ true, - /* verify_checksum */ true, - &error_message)); + const ArtDexFileLoader dex_file_loader; + std::unique_ptr<const DexFile> dex_file(dex_file_loader.Open(location, + location_checksum, + std::move(region), + /* verify */ true, + /* verify_checksum */ true, + &error_message)); if (expect_success) { CHECK(dex_file != nullptr) << error_message; } else { @@ -368,7 +371,8 @@ TEST_F(DexFileTest, Version40Rejected) { static constexpr bool kVerifyChecksum = true; std::string error_msg; std::vector<std::unique_ptr<const DexFile>> dex_files; - ASSERT_FALSE(DexFileLoader::Open( + const ArtDexFileLoader dex_file_loader; + ASSERT_FALSE(dex_file_loader.Open( location, location, /* verify */ true, kVerifyChecksum, &error_msg, &dex_files)); } @@ -381,7 +385,8 @@ TEST_F(DexFileTest, Version41Rejected) { static constexpr bool kVerifyChecksum = true; std::string error_msg; std::vector<std::unique_ptr<const DexFile>> dex_files; - ASSERT_FALSE(DexFileLoader::Open( + const ArtDexFileLoader dex_file_loader; + ASSERT_FALSE(dex_file_loader.Open( location, location, /* verify */ true, kVerifyChecksum, &error_msg, &dex_files)); } @@ -394,7 +399,8 @@ TEST_F(DexFileTest, ZeroLengthDexRejected) { static constexpr bool kVerifyChecksum = true; std::string error_msg; std::vector<std::unique_ptr<const DexFile>> dex_files; - ASSERT_FALSE(DexFileLoader::Open( + const ArtDexFileLoader dex_file_loader; + ASSERT_FALSE(dex_file_loader.Open( location, location, /* verify */ true, kVerifyChecksum, &error_msg, &dex_files)); } @@ -408,9 +414,10 @@ TEST_F(DexFileTest, GetChecksum) { std::vector<uint32_t> checksums; ScopedObjectAccess soa(Thread::Current()); std::string error_msg; - EXPECT_TRUE(DexFileLoader::GetMultiDexChecksums(GetLibCoreDexFileNames()[0].c_str(), - &checksums, - &error_msg)) + const ArtDexFileLoader dex_file_loader; + EXPECT_TRUE(dex_file_loader.GetMultiDexChecksums(GetLibCoreDexFileNames()[0].c_str(), + &checksums, + &error_msg)) << error_msg; ASSERT_EQ(1U, checksums.size()); EXPECT_EQ(java_lang_dex_file_->GetLocationChecksum(), checksums[0]); @@ -420,9 +427,10 @@ TEST_F(DexFileTest, GetMultiDexChecksums) { std::string error_msg; std::vector<uint32_t> checksums; std::string multidex_file = GetTestDexFileName("MultiDex"); - EXPECT_TRUE(DexFileLoader::GetMultiDexChecksums(multidex_file.c_str(), - &checksums, - &error_msg)) << error_msg; + const ArtDexFileLoader dex_file_loader; + EXPECT_TRUE(dex_file_loader.GetMultiDexChecksums(multidex_file.c_str(), + &checksums, + &error_msg)) << error_msg; std::vector<std::unique_ptr<const DexFile>> dexes = OpenTestDexFiles("MultiDex"); ASSERT_EQ(2U, dexes.size()); @@ -730,8 +738,10 @@ TEST_F(DexFileTest, OpenDexDebugInfoLocalNullType) { std::unique_ptr<const DexFile> raw = OpenDexFileInMemoryBase64( kRawDexDebugInfoLocalNullType, tmp.GetFilename().c_str(), 0xf25f2b38U, true); const DexFile::ClassDef& class_def = raw->GetClassDef(0); - const DexFile::CodeItem* code_item = raw->GetCodeItem(raw->FindCodeItemOffset(class_def, 1)); - CodeItemDebugInfoAccessor accessor(*raw, code_item); + constexpr uint32_t kMethodIdx = 1; + const DexFile::CodeItem* code_item = raw->GetCodeItem(raw->FindCodeItemOffset(class_def, + kMethodIdx)); + CodeItemDebugInfoAccessor accessor(*raw, code_item, kMethodIdx); ASSERT_TRUE(accessor.DecodeDebugLocalInfo(true, 1, Callback, nullptr)); } diff --git a/runtime/dex/dex_file_verifier_test.cc b/runtime/dex/dex_file_verifier_test.cc index d4d912cbfb..9759685961 100644 --- a/runtime/dex/dex_file_verifier_test.cc +++ b/runtime/dex/dex_file_verifier_test.cc @@ -22,6 +22,7 @@ #include <functional> #include <memory> +#include "art_dex_file_loader.h" #include "base/bit_utils.h" #include "base/macros.h" #include "base/unix_file/fd_file.h" @@ -114,7 +115,8 @@ 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 = DexFileLoader::Open( + const ArtDexFileLoader dex_file_loader; + bool success = dex_file_loader.Open( location, location, /* verify */ true, /* verify_checksum */ true, error_msg, &tmp); CHECK(success) << *error_msg; EXPECT_EQ(1U, tmp.size()); diff --git a/runtime/dex/standard_dex_file.h b/runtime/dex/standard_dex_file.h index fb2f720920..6437def4f5 100644 --- a/runtime/dex/standard_dex_file.h +++ b/runtime/dex/standard_dex_file.h @@ -33,8 +33,30 @@ class StandardDexFile : public DexFile { }; struct CodeItem : public DexFile::CodeItem { + static constexpr size_t kAlignment = 4; + private: - // TODO: Insert standard dex specific fields here. + CodeItem() = default; + + uint16_t registers_size_; // the number of registers used by this code + // (locals + parameters) + uint16_t ins_size_; // the number of words of incoming arguments to the method + // that this code is for + uint16_t outs_size_; // the number of words of outgoing argument space required + // by this code for method invocation + uint16_t tries_size_; // the number of try_items for this instance. If non-zero, + // then these appear as the tries array just after the + // insns in this instance. + uint32_t debug_info_off_; // Holds file offset to debug info stream. + + uint32_t insns_size_in_code_units_; // size of the insns array, in 2 byte code units + uint16_t insns_[1]; // actual array of bytecode. + + ART_FRIEND_TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor); + friend class CodeItemDataAccessor; + friend class CodeItemDebugInfoAccessor; + friend class CodeItemInstructionAccessor; + friend class DexWriter; friend class StandardDexFile; DISALLOW_COPY_AND_ASSIGN(CodeItem); }; @@ -80,6 +102,7 @@ class StandardDexFile : public DexFile { friend class DexFileVerifierTest; ART_FRIEND_TEST(ClassLinkerTest, RegisterDexFileName); // for constructor + friend class OptimizingUnitTestHelper; // for constructor DISALLOW_COPY_AND_ASSIGN(StandardDexFile); }; diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h index e459f09e95..20cde530c2 100644 --- a/runtime/dex2oat_environment_test.h +++ b/runtime/dex2oat_environment_test.h @@ -27,6 +27,7 @@ #include "base/stl_util.h" #include "common_runtime_test.h" #include "compiler_callbacks.h" +#include "dex/art_dex_file_loader.h" #include "dex/dex_file_loader.h" #include "exec_utils.h" #include "gc/heap.h" @@ -43,6 +44,7 @@ class Dex2oatEnvironmentTest : public CommonRuntimeTest { public: virtual void SetUp() OVERRIDE { CommonRuntimeTest::SetUp(); + const ArtDexFileLoader dex_file_loader; // Create a scratch directory to work from. @@ -74,7 +76,7 @@ class Dex2oatEnvironmentTest : public CommonRuntimeTest { ASSERT_TRUE(OS::FileExists(GetStrippedDexSrc1().c_str())) << "Expected stripped dex file to be at: " << GetStrippedDexSrc1(); ASSERT_FALSE( - DexFileLoader::GetMultiDexChecksums(GetStrippedDexSrc1().c_str(), &checksums, &error_msg)) + dex_file_loader.GetMultiDexChecksums(GetStrippedDexSrc1().c_str(), &checksums, &error_msg)) << "Expected stripped dex file to be stripped: " << GetStrippedDexSrc1(); ASSERT_TRUE(OS::FileExists(GetDexSrc2().c_str())) << "Expected dex file to be at: " << GetDexSrc2(); @@ -83,21 +85,21 @@ class Dex2oatEnvironmentTest : public CommonRuntimeTest { // GetMultiDexSrc1, but a different secondary dex checksum. static constexpr bool kVerifyChecksum = true; std::vector<std::unique_ptr<const DexFile>> multi1; - ASSERT_TRUE(DexFileLoader::Open(GetMultiDexSrc1().c_str(), - GetMultiDexSrc1().c_str(), - /* verify */ true, - kVerifyChecksum, - &error_msg, - &multi1)) << error_msg; + ASSERT_TRUE(dex_file_loader.Open(GetMultiDexSrc1().c_str(), + GetMultiDexSrc1().c_str(), + /* verify */ true, + kVerifyChecksum, + &error_msg, + &multi1)) << error_msg; ASSERT_GT(multi1.size(), 1u); std::vector<std::unique_ptr<const DexFile>> multi2; - ASSERT_TRUE(DexFileLoader::Open(GetMultiDexSrc2().c_str(), - GetMultiDexSrc2().c_str(), - /* verify */ true, - kVerifyChecksum, - &error_msg, - &multi2)) << error_msg; + ASSERT_TRUE(dex_file_loader.Open(GetMultiDexSrc2().c_str(), + GetMultiDexSrc2().c_str(), + /* verify */ true, + kVerifyChecksum, + &error_msg, + &multi2)) << error_msg; ASSERT_GT(multi2.size(), 1u); ASSERT_EQ(multi1[0]->GetLocationChecksum(), multi2[0]->GetLocationChecksum()); diff --git a/runtime/dex_to_dex_decompiler.cc b/runtime/dex_to_dex_decompiler.cc index e1c07baede..7887191713 100644 --- a/runtime/dex_to_dex_decompiler.cc +++ b/runtime/dex_to_dex_decompiler.cc @@ -36,8 +36,7 @@ class DexDecompiler { const ArrayRef<const uint8_t>& quickened_info, bool decompile_return_instruction) : code_item_accessor_(dex_file, &code_item), - quicken_info_(quickened_info.data()), - quicken_info_number_of_indices_(QuickenInfoTable::NumberOfIndices(quickened_info.size())), + quicken_info_(quickened_info), decompile_return_instruction_(decompile_return_instruction) {} bool Decompile(); @@ -72,7 +71,7 @@ class DexDecompiler { } uint16_t NextIndex() { - DCHECK_LT(quicken_index_, quicken_info_number_of_indices_); + DCHECK_LT(quicken_index_, quicken_info_.NumIndices()); const uint16_t ret = quicken_info_.GetData(quicken_index_); quicken_index_++; return ret; @@ -80,7 +79,6 @@ class DexDecompiler { const CodeItemInstructionAccessor code_item_accessor_; const QuickenInfoTable quicken_info_; - const size_t quicken_info_number_of_indices_; const bool decompile_return_instruction_; size_t quicken_index_ = 0u; @@ -104,7 +102,7 @@ bool DexDecompiler::Decompile() { break; case Instruction::NOP: - if (quicken_info_number_of_indices_ > 0) { + if (quicken_info_.NumIndices() > 0) { // Only try to decompile NOP if there are more than 0 indices. Not having // any index happens when we unquicken a code item that only has // RETURN_VOID_NO_BARRIER as quickened instruction. @@ -181,14 +179,14 @@ bool DexDecompiler::Decompile() { } } - if (quicken_index_ != quicken_info_number_of_indices_) { + if (quicken_index_ != quicken_info_.NumIndices()) { if (quicken_index_ == 0) { LOG(WARNING) << "Failed to use any value in quickening info," << " potentially due to duplicate methods."; } else { LOG(FATAL) << "Failed to use all values in quickening info." << " Actual: " << std::hex << quicken_index_ - << " Expected: " << quicken_info_number_of_indices_; + << " Expected: " << quicken_info_.NumIndices(); return false; } } diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index cf837161e0..a68227e0eb 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -88,7 +88,6 @@ ConcurrentCopying::ConcurrentCopying(Heap* heap, from_space_num_bytes_at_first_pause_(0), mark_stack_mode_(kMarkStackModeOff), weak_ref_access_enabled_(true), - max_peak_num_non_free_regions_(0), skipped_blocks_lock_("concurrent copying bytes blocks lock", kMarkSweepMarkStackLock), measure_read_barrier_slow_path_(measure_read_barrier_slow_path), mark_from_read_barrier_measurements_(false), @@ -1755,8 +1754,6 @@ void ConcurrentCopying::ReclaimPhase() { cumulative_bytes_moved_.FetchAndAddRelaxed(to_bytes); uint64_t to_objects = objects_moved_.LoadSequentiallyConsistent(); cumulative_objects_moved_.FetchAndAddRelaxed(to_objects); - max_peak_num_non_free_regions_ = std::max(max_peak_num_non_free_regions_, - region_space_->GetNumNonFreeRegions()); if (kEnableFromSpaceAccountingCheck) { CHECK_EQ(from_space_num_objects_at_first_pause_, from_objects + unevac_from_objects); CHECK_EQ(from_space_num_bytes_at_first_pause_, from_bytes + unevac_from_bytes); @@ -2269,7 +2266,7 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref, size_t non_moving_space_bytes_allocated = 0U; size_t bytes_allocated = 0U; size_t dummy; - mirror::Object* to_ref = region_space_->AllocNonvirtual<true>( + mirror::Object* to_ref = region_space_->AllocNonvirtual</*kForEvac*/ true>( region_space_alloc_size, ®ion_space_bytes_allocated, nullptr, &dummy); bytes_allocated = region_space_bytes_allocated; if (to_ref != nullptr) { @@ -2341,7 +2338,7 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref, DCHECK(region_space_->IsInToSpace(to_ref)); if (bytes_allocated > space::RegionSpace::kRegionSize) { // Free the large alloc. - region_space_->FreeLarge(to_ref, bytes_allocated); + region_space_->FreeLarge</*kForEvac*/ true>(to_ref, bytes_allocated); } else { // Record the lost copy for later reuse. heap_->num_bytes_allocated_.FetchAndAddSequentiallyConsistent(bytes_allocated); @@ -2696,10 +2693,10 @@ void ConcurrentCopying::DumpPerformanceInfo(std::ostream& os) { os << "Cumulative objects moved " << cumulative_objects_moved_.LoadRelaxed() << "\n"; os << "Peak regions allocated " - << max_peak_num_non_free_regions_ << " (" - << PrettySize(max_peak_num_non_free_regions_ * space::RegionSpace::kRegionSize) - << ") / " << region_space_->GetNumRegions() << " (" - << PrettySize(region_space_->GetNumRegions() * space::RegionSpace::kRegionSize) + << region_space_->GetMaxPeakNumNonFreeRegions() << " (" + << PrettySize(region_space_->GetMaxPeakNumNonFreeRegions() * space::RegionSpace::kRegionSize) + << ") / " << region_space_->GetNumRegions() / 2 << " (" + << PrettySize(region_space_->GetNumRegions() * space::RegionSpace::kRegionSize / 2) << ")\n"; } diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h index 939e7fc8a4..8b4b58e7b1 100644 --- a/runtime/gc/collector/concurrent_copying.h +++ b/runtime/gc/collector/concurrent_copying.h @@ -308,11 +308,6 @@ class ConcurrentCopying : public GarbageCollector { Atomic<uint64_t> cumulative_bytes_moved_; Atomic<uint64_t> cumulative_objects_moved_; - // Maintain the maximum of number of non-free regions collected just before - // reclaim in each GC cycle. At this moment in cycle, highest number of - // regions are in non-free. - size_t max_peak_num_non_free_regions_; - // The skipped blocks are memory blocks/chucks that were copies of // objects that were unused due to lost races (cas failures) at // object copy/forward pointer install. They are reused. diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 251d94ca25..ca5a3eeb17 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -36,6 +36,7 @@ #include "base/stl_util.h" #include "base/systrace.h" #include "base/time_utils.h" +#include "dex/art_dex_file_loader.h" #include "dex/dex_file_loader.h" #include "exec_utils.h" #include "gc/accounting/space_bitmap-inl.h" @@ -1828,6 +1829,7 @@ std::string ImageSpace::GetMultiImageBootClassPath( } bool ImageSpace::ValidateOatFile(const OatFile& oat_file, std::string* error_msg) { + const ArtDexFileLoader dex_file_loader; for (const OatFile::OatDexFile* oat_dex_file : oat_file.GetOatDexFiles()) { const std::string& dex_file_location = oat_dex_file->GetDexFileLocation(); @@ -1838,7 +1840,7 @@ bool ImageSpace::ValidateOatFile(const OatFile& oat_file, std::string* error_msg } std::vector<uint32_t> checksums; - if (!DexFileLoader::GetMultiDexChecksums(dex_file_location.c_str(), &checksums, error_msg)) { + if (!dex_file_loader.GetMultiDexChecksums(dex_file_location.c_str(), &checksums, error_msg)) { *error_msg = StringPrintf("ValidateOatFile failed to get checksums of dex file '%s' " "referenced by oat file %s: %s", dex_file_location.c_str(), diff --git a/runtime/gc/space/region_space-inl.h b/runtime/gc/space/region_space-inl.h index ea2168fe9c..e74e9b169f 100644 --- a/runtime/gc/space/region_space-inl.h +++ b/runtime/gc/space/region_space-inl.h @@ -115,7 +115,7 @@ inline mirror::Object* RegionSpace::Region::Alloc(size_t num_bytes, size_t* byte } template<RegionSpace::RegionType kRegionType> -uint64_t RegionSpace::GetBytesAllocatedInternal() { +inline uint64_t RegionSpace::GetBytesAllocatedInternal() { uint64_t bytes = 0; MutexLock mu(Thread::Current(), region_lock_); for (size_t i = 0; i < num_regions_; ++i) { @@ -150,7 +150,7 @@ uint64_t RegionSpace::GetBytesAllocatedInternal() { } template<RegionSpace::RegionType kRegionType> -uint64_t RegionSpace::GetObjectsAllocatedInternal() { +inline uint64_t RegionSpace::GetObjectsAllocatedInternal() { uint64_t bytes = 0; MutexLock mu(Thread::Current(), region_lock_); for (size_t i = 0; i < num_regions_; ++i) { @@ -185,7 +185,7 @@ uint64_t RegionSpace::GetObjectsAllocatedInternal() { } template<bool kToSpaceOnly, typename Visitor> -void RegionSpace::WalkInternal(Visitor&& visitor) { +inline void RegionSpace::WalkInternal(Visitor&& visitor) { // TODO: MutexLock on region_lock_ won't work due to lock order // issues (the classloader classes lock and the monitor lock). We // call this with threads suspended. @@ -237,9 +237,10 @@ inline mirror::Object* RegionSpace::GetNextObject(mirror::Object* obj) { } template<bool kForEvac> -mirror::Object* RegionSpace::AllocLarge(size_t num_bytes, size_t* bytes_allocated, - size_t* usable_size, - size_t* bytes_tl_bulk_allocated) { +inline mirror::Object* RegionSpace::AllocLarge(size_t num_bytes, + size_t* bytes_allocated, + size_t* usable_size, + size_t* bytes_tl_bulk_allocated) { DCHECK_ALIGNED(num_bytes, kAlignment); DCHECK_GT(num_bytes, kRegionSize); size_t num_regs = RoundUp(num_bytes, kRegionSize) / kRegionSize; @@ -274,7 +275,11 @@ mirror::Object* RegionSpace::AllocLarge(size_t num_bytes, size_t* bytes_allocate Region* first_reg = ®ions_[left]; DCHECK(first_reg->IsFree()); first_reg->UnfreeLarge(this, time_); - ++num_non_free_regions_; + if (kForEvac) { + ++num_evac_regions_; + } else { + ++num_non_free_regions_; + } size_t allocated = num_regs * kRegionSize; // We make 'top' all usable bytes, as the caller of this // allocation may use all of 'usable_size' (see mirror::Array::Alloc). @@ -283,7 +288,11 @@ mirror::Object* RegionSpace::AllocLarge(size_t num_bytes, size_t* bytes_allocate DCHECK_LT(p, num_regions_); DCHECK(regions_[p].IsFree()); regions_[p].UnfreeLargeTail(this, time_); - ++num_non_free_regions_; + if (kForEvac) { + ++num_evac_regions_; + } else { + ++num_non_free_regions_; + } } *bytes_allocated = allocated; if (usable_size != nullptr) { @@ -299,6 +308,35 @@ mirror::Object* RegionSpace::AllocLarge(size_t num_bytes, size_t* bytes_allocate return nullptr; } +template<bool kForEvac> +inline void RegionSpace::FreeLarge(mirror::Object* large_obj, size_t bytes_allocated) { + DCHECK(Contains(large_obj)); + DCHECK_ALIGNED(large_obj, kRegionSize); + MutexLock mu(Thread::Current(), region_lock_); + uint8_t* begin_addr = reinterpret_cast<uint8_t*>(large_obj); + uint8_t* end_addr = AlignUp(reinterpret_cast<uint8_t*>(large_obj) + bytes_allocated, kRegionSize); + CHECK_LT(begin_addr, end_addr); + for (uint8_t* addr = begin_addr; addr < end_addr; addr += kRegionSize) { + Region* reg = RefToRegionLocked(reinterpret_cast<mirror::Object*>(addr)); + if (addr == begin_addr) { + DCHECK(reg->IsLarge()); + } else { + DCHECK(reg->IsLargeTail()); + } + reg->Clear(/*zero_and_release_pages*/true); + if (kForEvac) { + --num_evac_regions_; + } else { + --num_non_free_regions_; + } + } + if (end_addr < Limit()) { + // If we aren't at the end of the space, check that the next region is not a large tail. + Region* following_reg = RefToRegionLocked(reinterpret_cast<mirror::Object*>(end_addr)); + DCHECK(!following_reg->IsLargeTail()); + } +} + inline size_t RegionSpace::Region::BytesAllocated() const { if (IsLarge()) { DCHECK_LT(begin_ + kRegionSize, Top()); diff --git a/runtime/gc/space/region_space.cc b/runtime/gc/space/region_space.cc index a51df7c783..45cfff90cc 100644 --- a/runtime/gc/space/region_space.cc +++ b/runtime/gc/space/region_space.cc @@ -84,14 +84,18 @@ RegionSpace* RegionSpace::Create(const std::string& name, MemMap* mem_map) { RegionSpace::RegionSpace(const std::string& name, MemMap* mem_map) : ContinuousMemMapAllocSpace(name, mem_map, mem_map->Begin(), mem_map->End(), mem_map->End(), kGcRetentionPolicyAlwaysCollect), - region_lock_("Region lock", kRegionSpaceRegionLock), time_(1U) { - size_t mem_map_size = mem_map->Size(); - CHECK_ALIGNED(mem_map_size, kRegionSize); + region_lock_("Region lock", kRegionSpaceRegionLock), + time_(1U), + num_regions_(mem_map->Size() / kRegionSize), + num_non_free_regions_(0U), + num_evac_regions_(0U), + max_peak_num_non_free_regions_(0U), + non_free_region_index_limit_(0U), + current_region_(&full_region_), + evac_region_(nullptr) { + CHECK_ALIGNED(mem_map->Size(), kRegionSize); CHECK_ALIGNED(mem_map->Begin(), kRegionSize); - num_regions_ = mem_map_size / kRegionSize; - num_non_free_regions_ = 0U; DCHECK_GT(num_regions_, 0U); - non_free_region_index_limit_ = 0U; regions_.reset(new Region[num_regions_]); uint8_t* region_addr = mem_map->Begin(); for (size_t i = 0; i < num_regions_; ++i, region_addr += kRegionSize) { @@ -112,8 +116,6 @@ RegionSpace::RegionSpace(const std::string& name, MemMap* mem_map) } DCHECK(!full_region_.IsFree()); DCHECK(full_region_.IsAllocated()); - current_region_ = &full_region_; - evac_region_ = nullptr; size_t ignored; DCHECK(full_region_.Alloc(kAlignment, &ignored, nullptr, &ignored) == nullptr); } @@ -267,6 +269,9 @@ void RegionSpace::ClearFromSpace(uint64_t* cleared_bytes, uint64_t* cleared_obje VerifyNonFreeRegionLimit(); size_t new_non_free_region_index_limit = 0; + // Update max of peak non free region count before reclaiming evacuated regions. + max_peak_num_non_free_regions_ = std::max(max_peak_num_non_free_regions_, + num_non_free_regions_); // Combine zeroing and releasing pages to reduce how often madvise is called. This helps // reduce contention on the mmap semaphore. b/62194020 // clear_region adds a region to the current block. If the region is not adjacent, the @@ -350,6 +355,8 @@ void RegionSpace::ClearFromSpace(uint64_t* cleared_bytes, uint64_t* cleared_obje // Update non_free_region_index_limit_. SetNonFreeRegionLimit(new_non_free_region_index_limit); evac_region_ = nullptr; + num_non_free_regions_ += num_evac_regions_; + num_evac_regions_ = 0; } void RegionSpace::LogFragmentationAllocFailure(std::ostream& os, @@ -411,30 +418,6 @@ void RegionSpace::Dump(std::ostream& os) const { << reinterpret_cast<void*>(Begin()) << "-" << reinterpret_cast<void*>(Limit()); } -void RegionSpace::FreeLarge(mirror::Object* large_obj, size_t bytes_allocated) { - DCHECK(Contains(large_obj)); - DCHECK_ALIGNED(large_obj, kRegionSize); - MutexLock mu(Thread::Current(), region_lock_); - uint8_t* begin_addr = reinterpret_cast<uint8_t*>(large_obj); - uint8_t* end_addr = AlignUp(reinterpret_cast<uint8_t*>(large_obj) + bytes_allocated, kRegionSize); - CHECK_LT(begin_addr, end_addr); - for (uint8_t* addr = begin_addr; addr < end_addr; addr += kRegionSize) { - Region* reg = RefToRegionLocked(reinterpret_cast<mirror::Object*>(addr)); - if (addr == begin_addr) { - DCHECK(reg->IsLarge()); - } else { - DCHECK(reg->IsLargeTail()); - } - reg->Clear(/*zero_and_release_pages*/true); - --num_non_free_regions_; - } - if (end_addr < Limit()) { - // If we aren't at the end of the space, check that the next region is not a large tail. - Region* following_reg = RefToRegionLocked(reinterpret_cast<mirror::Object*>(end_addr)); - DCHECK(!following_reg->IsLargeTail()); - } -} - void RegionSpace::DumpRegions(std::ostream& os) { MutexLock mu(Thread::Current(), region_lock_); for (size_t i = 0; i < num_regions_; ++i) { @@ -572,10 +555,12 @@ RegionSpace::Region* RegionSpace::AllocateRegion(bool for_evac) { Region* r = ®ions_[i]; if (r->IsFree()) { r->Unfree(this, time_); - ++num_non_free_regions_; - if (!for_evac) { + if (for_evac) { + ++num_evac_regions_; // Evac doesn't count as newly allocated. + } else { r->SetNewlyAllocated(); + ++num_non_free_regions_; } return r; } diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h index c9c9136c27..ef8aa52a03 100644 --- a/runtime/gc/space/region_space.h +++ b/runtime/gc/space/region_space.h @@ -64,6 +64,7 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { template<bool kForEvac> mirror::Object* AllocLarge(size_t num_bytes, size_t* bytes_allocated, size_t* usable_size, size_t* bytes_tl_bulk_allocated) REQUIRES(!region_lock_); + template<bool kForEvac> void FreeLarge(mirror::Object* large_obj, size_t bytes_allocated) REQUIRES(!region_lock_); // Return the storage space required by obj. @@ -138,9 +139,8 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { uint64_t GetObjectsAllocatedInUnevacFromSpace() REQUIRES(!region_lock_) { return GetObjectsAllocatedInternal<RegionType::kRegionTypeUnevacFromSpace>(); } - // It is OK to do a racy read here as it's only for performance dump. - size_t GetNumNonFreeRegions() const { - return num_non_free_regions_; + size_t GetMaxPeakNumNonFreeRegions() const { + return max_peak_num_non_free_regions_; } size_t GetNumRegions() const { return num_regions_; @@ -530,8 +530,18 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace { Mutex region_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; uint32_t time_; // The time as the number of collections since the startup. - size_t num_regions_; // The number of regions in this space. - size_t num_non_free_regions_; // The number of non-free regions in this space. + const size_t num_regions_; // The number of regions in this space. + // The number of non-free regions in this space. + size_t num_non_free_regions_ GUARDED_BY(region_lock_); + + // The number of evac regions allocated during collection. 0 when GC not running. + size_t num_evac_regions_ GUARDED_BY(region_lock_); + + // Maintain the maximum of number of non-free regions collected just before + // reclaim in each GC cycle. At this moment in cycle, highest number of + // regions are in non-free. + size_t max_peak_num_non_free_regions_; + std::unique_ptr<Region[]> regions_ GUARDED_BY(region_lock_); // The pointer to the region array. // The upper-bound index of the non-free regions. Used to avoid scanning all regions in diff --git a/runtime/globals.h b/runtime/globals.h index f14d6e95a6..ca4040d777 100644 --- a/runtime/globals.h +++ b/runtime/globals.h @@ -62,6 +62,12 @@ static constexpr bool kIsDebugBuild = GlobalsReturnSelf(false); static constexpr bool kIsDebugBuild = GlobalsReturnSelf(true); #endif +#if defined(ART_PGO_INSTRUMENTATION) +static constexpr bool kIsPGOInstrumentation = true; +#else +static constexpr bool kIsPGOInstrumentation = false; +#endif + // ART_TARGET - Defined for target builds of ART. // ART_TARGET_LINUX - Defined for target Linux builds of ART. // ART_TARGET_ANDROID - Defined for target Android builds of ART. diff --git a/runtime/image.cc b/runtime/image.cc index b9d955c08c..dd0c1487c0 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -26,7 +26,7 @@ namespace art { const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' }; -const uint8_t ImageHeader::kImageVersion[] = { '0', '5', '2', '\0' }; // 4-bit ClassStatus. +const uint8_t ImageHeader::kImageVersion[] = { '0', '5', '3', '\0' }; // ClassStatus in high bits. ImageHeader::ImageHeader(uint32_t image_begin, uint32_t image_size, diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc index 8abf8a6003..55e9c390cd 100644 --- a/runtime/mem_map.cc +++ b/runtime/mem_map.cc @@ -927,9 +927,6 @@ void* MemMap::MapInternal(void* addr, UNUSED(low_4gb); #endif DCHECK_ALIGNED(length, kPageSize); - if (low_4gb) { - DCHECK_EQ(flags & MAP_FIXED, 0); - } // TODO: // A page allocator would be a useful abstraction here, as // 1) It is doubtful that MAP_32BIT on x86_64 is doing the right job for us diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 55c588930e..84b032620f 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -81,9 +81,9 @@ class MANAGED Class FINAL : public Object { template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> ClassStatus GetStatus() REQUIRES_SHARED(Locks::mutator_lock_) { // Avoid including "subtype_check_bits_and_status.h" to get the field. - // The ClassStatus is always in the least-significant bits of status_. + // The ClassStatus is always in the 4 most-significant of status_. return enum_cast<ClassStatus>( - static_cast<uint32_t>(GetField32Volatile<kVerifyFlags>(StatusOffset())) & 0xff); + static_cast<uint32_t>(GetField32Volatile<kVerifyFlags>(StatusOffset())) >> (32 - 4)); } // This is static because 'this' may be moved by GC. diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index a992b5cb5b..0f430874cf 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -27,6 +27,7 @@ #include <class_loader_context.h> #include "common_throws.h" #include "compiler_filter.h" +#include "dex/art_dex_file_loader.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" #include "jni_internal.h" @@ -188,12 +189,13 @@ static const DexFile* CreateDexFile(JNIEnv* env, std::unique_ptr<MemMap> dex_mem dex_mem_map->Begin(), dex_mem_map->End()); std::string error_message; - std::unique_ptr<const DexFile> dex_file(DexFileLoader::Open(location, - 0, - std::move(dex_mem_map), - /* verify */ true, - /* verify_location */ true, - &error_message)); + const ArtDexFileLoader dex_file_loader; + std::unique_ptr<const DexFile> dex_file(dex_file_loader.Open(location, + 0, + std::move(dex_mem_map), + /* verify */ true, + /* verify_location */ true, + &error_message)); if (dex_file == nullptr) { ScopedObjectAccess soa(env); ThrowWrappedIOException("%s", error_message.c_str()); diff --git a/runtime/oat.h b/runtime/oat.h index 6d4f18bdb1..36099b93dc 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,8 +32,8 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - // Last oat version changed reason: 4-bit ClassStatus. - static constexpr uint8_t kOatVersion[] = { '1', '3', '6', '\0' }; + // Last oat version changed reason: ClassStatus in high bits. + static constexpr uint8_t kOatVersion[] = { '1', '3', '7', '\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 df07a191bc..c03dbccbc4 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -43,6 +43,7 @@ #include "base/stl_util.h" #include "base/systrace.h" #include "base/unix_file/fd_file.h" +#include "dex/art_dex_file_loader.h" #include "dex/dex_file_loader.h" #include "dex/dex_file_types.h" #include "dex/standard_dex_file.h" @@ -194,10 +195,6 @@ OatFileBase* OatFileBase::OpenOatFile(const std::string& vdex_filename, ret->PreLoad(); - if (!ret->LoadVdex(vdex_filename, writable, low_4gb, error_msg)) { - return nullptr; - } - if (!ret->Load(elf_filename, oat_file_begin, writable, @@ -211,6 +208,10 @@ OatFileBase* OatFileBase::OpenOatFile(const std::string& vdex_filename, return nullptr; } + if (!ret->LoadVdex(vdex_filename, writable, low_4gb, error_msg)) { + return nullptr; + } + ret->PreSetup(elf_filename); if (!ret->Setup(abs_dex_location, error_msg)) { @@ -234,10 +235,6 @@ OatFileBase* OatFileBase::OpenOatFile(int vdex_fd, std::string* error_msg) { std::unique_ptr<OatFileBase> ret(new kOatFileBaseSubType(oat_location, executable)); - if (!ret->LoadVdex(vdex_fd, vdex_location, writable, low_4gb, error_msg)) { - return nullptr; - } - if (!ret->Load(oat_fd, oat_file_begin, writable, @@ -251,6 +248,10 @@ OatFileBase* OatFileBase::OpenOatFile(int vdex_fd, return nullptr; } + if (!ret->LoadVdex(vdex_fd, vdex_location, writable, low_4gb, error_msg)) { + return nullptr; + } + ret->PreSetup(oat_location); if (!ret->Setup(abs_dex_location, error_msg)) { @@ -264,7 +265,14 @@ bool OatFileBase::LoadVdex(const std::string& vdex_filename, bool writable, bool low_4gb, std::string* error_msg) { - vdex_ = VdexFile::Open(vdex_filename, writable, low_4gb, /* unquicken*/ false, error_msg); + vdex_ = VdexFile::OpenAtAddress(vdex_begin_, + vdex_end_ - vdex_begin_, + vdex_begin_ != nullptr /* mmap_reuse */, + vdex_filename, + writable, + low_4gb, + /* unquicken*/ false, + error_msg); if (vdex_.get() == nullptr) { *error_msg = StringPrintf("Failed to load vdex file '%s' %s", vdex_filename.c_str(), @@ -285,13 +293,16 @@ bool OatFileBase::LoadVdex(int vdex_fd, if (rc == -1) { PLOG(WARNING) << "Failed getting length of vdex file"; } else { - vdex_ = VdexFile::Open(vdex_fd, - s.st_size, - vdex_filename, - writable, - low_4gb, - false /* unquicken */, - error_msg); + vdex_ = VdexFile::OpenAtAddress(vdex_begin_, + vdex_end_ - vdex_begin_, + vdex_begin_ != nullptr /* mmap_reuse */, + vdex_fd, + s.st_size, + vdex_filename, + writable, + low_4gb, + false /* unquicken */, + error_msg); if (vdex_.get() == nullptr) { *error_msg = "Failed opening vdex file."; return false; @@ -339,7 +350,7 @@ bool OatFileBase::ComputeFields(uint8_t* requested_base, } else { bss_end_ = const_cast<uint8_t*>(FindDynamicSymbolAddress("oatbsslastword", &symbol_error_msg)); if (bss_end_ == nullptr) { - *error_msg = StringPrintf("Failed to find oatbasslastword symbol in '%s'", file_path.c_str()); + *error_msg = StringPrintf("Failed to find oatbsslastword symbol in '%s'", file_path.c_str()); return false; } // Readjust to be non-inclusive upper bound. @@ -351,6 +362,20 @@ bool OatFileBase::ComputeFields(uint8_t* requested_base, bss_roots_ = const_cast<uint8_t*>(FindDynamicSymbolAddress("oatbssroots", &symbol_error_msg)); } + vdex_begin_ = const_cast<uint8_t*>(FindDynamicSymbolAddress("oatdex", &symbol_error_msg)); + if (vdex_begin_ == nullptr) { + // No .vdex section. + vdex_end_ = nullptr; + } else { + vdex_end_ = const_cast<uint8_t*>(FindDynamicSymbolAddress("oatdexlastword", &symbol_error_msg)); + if (vdex_end_ == nullptr) { + *error_msg = StringPrintf("Failed to find oatdexlastword symbol in '%s'", file_path.c_str()); + return false; + } + // Readjust to be non-inclusive upper bound. + vdex_end_ += sizeof(uint32_t); + } + return true; } @@ -1441,6 +1466,8 @@ OatFile::OatFile(const std::string& location, bool is_executable) bss_methods_(nullptr), bss_roots_(nullptr), is_executable_(is_executable), + vdex_begin_(nullptr), + vdex_end_(nullptr), secondary_lookup_lock_("OatFile secondary lookup lock", kOatFileSecondaryLookupLock) { CHECK(!location_.empty()); } @@ -1471,6 +1498,14 @@ const uint8_t* OatFile::BssEnd() const { return bss_end_; } +const uint8_t* OatFile::VdexBegin() const { + return vdex_begin_; +} + +const uint8_t* OatFile::VdexEnd() const { + return vdex_end_; +} + const uint8_t* OatFile::DexBegin() const { return vdex_->Begin(); } @@ -1500,21 +1535,6 @@ ArrayRef<GcRoot<mirror::Object>> OatFile::GetBssGcRoots() const { } } -uint32_t OatFile::GetDebugInfoOffset(const DexFile& dex_file, uint32_t debug_info_off) { - // Note that although the specification says that 0 should be used if there - // is no debug information, some applications incorrectly use 0xFFFFFFFF. - // The following check also handles debug_info_off == 0. - if (debug_info_off < dex_file.Size() || debug_info_off == 0xFFFFFFFF) { - return debug_info_off; - } - const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); - if (oat_dex_file == nullptr || (oat_dex_file->GetOatFile() == nullptr)) { - return debug_info_off; - } - return oat_dex_file->GetOatFile()->GetVdexFile()->GetDebugInfoOffset( - dex_file, debug_info_off); -} - const OatFile::OatDexFile* OatFile::GetOatDexFile(const char* dex_location, const uint32_t* dex_location_checksum, std::string* error_msg) const { @@ -1632,14 +1652,15 @@ std::unique_ptr<const DexFile> OatFile::OatDexFile::OpenDexFile(std::string* err ScopedTrace trace(__PRETTY_FUNCTION__); static constexpr bool kVerify = false; static constexpr bool kVerifyChecksum = false; - return DexFileLoader::Open(dex_file_pointer_, - FileSize(), - dex_file_location_, - dex_file_location_checksum_, - this, - kVerify, - kVerifyChecksum, - error_msg); + const ArtDexFileLoader dex_file_loader; + return dex_file_loader.Open(dex_file_pointer_, + FileSize(), + dex_file_location_, + dex_file_location_checksum_, + this, + kVerify, + kVerifyChecksum, + error_msg); } uint32_t OatFile::OatDexFile::GetOatClassOffset(uint16_t class_def_index) const { diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 02318b68b7..bf22e0b88b 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -115,10 +115,6 @@ class OatFile { const char* abs_dex_location, std::string* error_msg); - // Return the actual debug info offset for an offset that might be actually pointing to - // dequickening info. The returned debug info offset is the one originally in the the dex file. - static uint32_t GetDebugInfoOffset(const DexFile& dex_file, uint32_t debug_info_off); - virtual ~OatFile(); bool IsExecutable() const { @@ -279,6 +275,10 @@ class OatFile { return BssEnd() - BssBegin(); } + size_t VdexSize() const { + return VdexEnd() - VdexBegin(); + } + size_t BssMethodsOffset() const { // Note: This is used only for symbolizer and needs to return a valid .bss offset. return (bss_methods_ != nullptr) ? bss_methods_ - BssBegin() : BssRootsOffset(); @@ -299,6 +299,9 @@ class OatFile { const uint8_t* BssBegin() const; const uint8_t* BssEnd() const; + const uint8_t* VdexBegin() const; + const uint8_t* VdexEnd() const; + const uint8_t* DexBegin() const; const uint8_t* DexEnd() const; @@ -358,6 +361,12 @@ class OatFile { // Was this oat_file loaded executable? const bool is_executable_; + // Pointer to the .vdex section, if present, otherwise null. + uint8_t* vdex_begin_; + + // Pointer to the end of the .vdex section, if present, otherwise null. + uint8_t* vdex_end_; + // Owning storage for the OatDexFile objects. std::vector<const OatDexFile*> oat_dex_files_storage_; diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 240030cf5b..73ca19a363 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -28,6 +28,7 @@ #include "base/stl_util.h" #include "class_linker.h" #include "compiler_filter.h" +#include "dex/art_dex_file_loader.h" #include "dex/dex_file_loader.h" #include "exec_utils.h" #include "gc/heap.h" @@ -869,10 +870,11 @@ const std::vector<uint32_t>* OatFileAssistant::GetRequiredDexChecksums() { required_dex_checksums_found_ = false; cached_required_dex_checksums_.clear(); std::string error_msg; - if (DexFileLoader::GetMultiDexChecksums(dex_location_.c_str(), - &cached_required_dex_checksums_, - &error_msg, - zip_fd_)) { + const ArtDexFileLoader dex_file_loader; + if (dex_file_loader.GetMultiDexChecksums(dex_location_.c_str(), + &cached_required_dex_checksums_, + &error_msg, + zip_fd_)) { required_dex_checksums_found_ = true; has_original_dex_files_ = true; } else { diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index 577f8be4d7..9503360167 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -31,6 +31,7 @@ #include "base/systrace.h" #include "class_linker.h" #include "class_loader_context.h" +#include "dex/art_dex_file_loader.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_loader.h" #include "dex/dex_file_tracking_registrar.h" @@ -613,12 +614,13 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( if (oat_file_assistant.HasOriginalDexFiles()) { if (Runtime::Current()->IsDexFileFallbackEnabled()) { static constexpr bool kVerifyChecksum = true; - if (!DexFileLoader::Open(dex_location, - dex_location, - Runtime::Current()->IsVerificationEnabled(), - kVerifyChecksum, - /*out*/ &error_msg, - &dex_files)) { + const ArtDexFileLoader dex_file_loader; + if (!dex_file_loader.Open(dex_location, + dex_location, + Runtime::Current()->IsVerificationEnabled(), + 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/oat_file_test.cc b/runtime/oat_file_test.cc index 7bf0f84596..8d864018ab 100644 --- a/runtime/oat_file_test.cc +++ b/runtime/oat_file_test.cc @@ -21,11 +21,13 @@ #include <gtest/gtest.h> #include "common_runtime_test.h" +#include "dexopt_test.h" #include "scoped_thread_state_change-inl.h" +#include "vdex_file.h" namespace art { -class OatFileTest : public CommonRuntimeTest { +class OatFileTest : public DexoptTest { }; TEST_F(OatFileTest, ResolveRelativeEncodedDexLocation) { @@ -62,4 +64,28 @@ TEST_F(OatFileTest, ResolveRelativeEncodedDexLocation) { "/data/app/foo/base.apk", "o/base.apk")); } +TEST_F(OatFileTest, LoadOat) { + std::string dex_location = GetScratchDir() + "/LoadOat.jar"; + + Copy(GetDexSrc1(), dex_location); + GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); + + std::string oat_location; + std::string error_msg; + ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename( + dex_location, kRuntimeISA, &oat_location, &error_msg)) << error_msg; + std::unique_ptr<OatFile> odex_file(OatFile::Open(oat_location.c_str(), + oat_location.c_str(), + nullptr, + nullptr, + false, + /*low_4gb*/false, + dex_location.c_str(), + &error_msg)); + ASSERT_TRUE(odex_file.get() != nullptr); + + // Check that the vdex file was loaded in the reserved space of odex file. + EXPECT_EQ(odex_file->GetVdexFile()->Begin(), odex_file->VdexBegin()); +} + } // namespace art diff --git a/runtime/obj_ptr.h b/runtime/obj_ptr.h index 70e767acf6..14fdba31d9 100644 --- a/runtime/obj_ptr.h +++ b/runtime/obj_ptr.h @@ -37,7 +37,7 @@ constexpr bool kObjPtrPoisoningValidateOnCopy = false; template<class MirrorType> class ObjPtr { static constexpr size_t kCookieShift = - sizeof(kHeapReferenceSize) * kBitsPerByte - kObjectAlignmentShift; + kHeapReferenceSize * kBitsPerByte - kObjectAlignmentShift; static constexpr size_t kCookieBits = sizeof(uintptr_t) * kBitsPerByte - kCookieShift; static constexpr uintptr_t kCookieMask = (static_cast<uintptr_t>(1u) << kCookieBits) - 1; diff --git a/runtime/quicken_info.h b/runtime/quicken_info.h index ce11f3c19b..52eca61c06 100644 --- a/runtime/quicken_info.h +++ b/runtime/quicken_info.h @@ -17,15 +17,93 @@ #ifndef ART_RUNTIME_QUICKEN_INFO_H_ #define ART_RUNTIME_QUICKEN_INFO_H_ +#include "base/array_ref.h" #include "dex/dex_instruction.h" +#include "leb128.h" namespace art { -// QuickenInfoTable is a table of 16 bit dex indices. There is one slot fo every instruction that is -// possibly dequickenable. +// Table for getting the offset of quicken info. Doesn't have one slot for each index, so a +// combination of iteration and indexing is required to get the quicken info for a given dex method +// index. +class QuickenInfoOffsetTableAccessor { + public: + using TableType = uint32_t; + static constexpr uint32_t kElementsPerIndex = 16; + + class Builder { + public: + explicit Builder(std::vector<uint8_t>* out_data) : out_data_(out_data) {} + + void AddOffset(uint32_t index) { + out_data_->insert(out_data_->end(), + reinterpret_cast<const uint8_t*>(&index), + reinterpret_cast<const uint8_t*>(&index + 1)); + } + + private: + std::vector<uint8_t>* const out_data_; + }; + + // The table only covers every kElementsPerIndex indices. + static bool IsCoveredIndex(uint32_t index) { + return index % kElementsPerIndex == 0; + } + + explicit QuickenInfoOffsetTableAccessor(const uint8_t* data, uint32_t max_index) + : table_(reinterpret_cast<const uint32_t*>(data)), + num_indices_(RoundUp(max_index, kElementsPerIndex) / kElementsPerIndex) {} + + size_t SizeInBytes() const { + return NumIndices() * sizeof(table_[0]); + } + + uint32_t NumIndices() const { + return num_indices_; + } + + // Returns the offset for the index at or before the desired index. If the offset is for an index + // before the desired one, remainder is how many elements to traverse to reach the desired index. + TableType ElementOffset(uint32_t index, uint32_t* remainder) const { + *remainder = index % kElementsPerIndex; + return table_[index / kElementsPerIndex]; + } + + const uint8_t* DataEnd() const { + return reinterpret_cast<const uint8_t*>(table_ + NumIndices()); + } + + static uint32_t Alignment() { + return alignof(TableType); + } + + private: + const TableType* table_; + uint32_t num_indices_; +}; + +// QuickenInfoTable is a table of 16 bit dex indices. There is one slot for every instruction that +// is possibly dequickenable. class QuickenInfoTable { public: - explicit QuickenInfoTable(const uint8_t* data) : data_(data) {} + class Builder { + public: + Builder(std::vector<uint8_t>* out_data, size_t num_elements) : out_data_(out_data) { + EncodeUnsignedLeb128(out_data_, num_elements); + } + + void AddIndex(uint16_t index) { + out_data_->push_back(static_cast<uint8_t>(index)); + out_data_->push_back(static_cast<uint8_t>(index >> 8)); + } + + private: + std::vector<uint8_t>* const out_data_; + }; + + explicit QuickenInfoTable(ArrayRef<const uint8_t> data) + : data_(data.data()), + num_elements_(!data.empty() ? DecodeUnsignedLeb128(&data_) : 0u) {} bool IsNull() const { return data_ == nullptr; @@ -44,8 +122,18 @@ class QuickenInfoTable { return bytes / sizeof(uint16_t); } + static size_t SizeInBytes(ArrayRef<const uint8_t> data) { + QuickenInfoTable table(data); + return table.data_ + table.NumIndices() * 2 - data.data(); + } + + uint32_t NumIndices() const { + return num_elements_; + } + private: - const uint8_t* const data_; + const uint8_t* data_; + const uint32_t num_elements_; DISALLOW_COPY_AND_ASSIGN(QuickenInfoTable); }; diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 1bddb4ef95..377e0a3fca 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -69,6 +69,7 @@ #include "class_linker-inl.h" #include "compiler_callbacks.h" #include "debugger.h" +#include "dex/art_dex_file_loader.h" #include "dex/dex_file_loader.h" #include "elf_file.h" #include "entrypoints/runtime_asm_entrypoints.h" @@ -1041,6 +1042,7 @@ static size_t OpenDexFiles(const std::vector<std::string>& dex_filenames, if (!image_location.empty() && OpenDexFilesFromImage(image_location, dex_files, &failure_count)) { return failure_count; } + const ArtDexFileLoader dex_file_loader; failure_count = 0; for (size_t i = 0; i < dex_filenames.size(); i++) { const char* dex_filename = dex_filenames[i].c_str(); @@ -1051,12 +1053,12 @@ static size_t OpenDexFiles(const std::vector<std::string>& dex_filenames, LOG(WARNING) << "Skipping non-existent dex file '" << dex_filename << "'"; continue; } - if (!DexFileLoader::Open(dex_filename, - dex_location, - Runtime::Current()->IsVerificationEnabled(), - kVerifyChecksum, - &error_msg, - dex_files)) { + if (!dex_file_loader.Open(dex_filename, + dex_location, + Runtime::Current()->IsVerificationEnabled(), + kVerifyChecksum, + &error_msg, + dex_files)) { LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "': " << error_msg; ++failure_count; } diff --git a/runtime/subtype_check_bits.h b/runtime/subtype_check_bits.h index 4305ff849a..462f203978 100644 --- a/runtime/subtype_check_bits.h +++ b/runtime/subtype_check_bits.h @@ -26,22 +26,22 @@ namespace art { /** * The SubtypeCheckBits memory layout (in bits): * - * Variable - * | - * <---- up to 23 bits ----> v +---> 1 bit - * | - * +-------------------------+--------+-----------+---++ - * | Bitstring | | - * +-------------------------+--------+-----------+ | - * | Path To Root | Next | (unused) | OF | - * +---+---------------------+--------+ | | - * | | | | | ... | | (0....0) | | - * +---+---------------------+--------+-----------+----+ - * MSB LSB + * 1 bit Variable + * | | + * v v <---- up to 23 bits ----> + * + * +----+-----------+--------+-------------------------+ + * | | Bitstring | + * + +-----------+--------+-------------------------+ + * | OF | (unused) | Next | Path To Root | + * + | |--------+----+----------+----+----+ + * | | (0....0) | | | ... | | | + * +----+-----------+--------+----+----------+----+----+ + * MSB (most significant bit) LSB * * The bitstring takes up to 23 bits; anything exceeding that is truncated: * - Path To Root is a list of chars, encoded as a BitString: - * starting at the root (in MSB), each character is a sibling index unique to the parent, + * starting at the root (in LSB), each character is a sibling index unique to the parent, * Paths longer than BitString::kCapacity are truncated to fit within the BitString. * - Next is a single BitStringChar (immediatelly following Path To Root) * When new children are assigned paths, they get allocated the parent's Next value. @@ -57,8 +57,8 @@ namespace art { * See subtype_check.h and subtype_check_info.h for more details. */ BITSTRUCT_DEFINE_START(SubtypeCheckBits, /*size*/ BitString::BitStructSizeOf() + 1u) - BitStructUint</*lsb*/0, /*width*/1> overflow_; - BitStructField<BitString, /*lsb*/1> bitstring_; + BitStructField<BitString, /*lsb*/ 0> bitstring_; + BitStructUint</*lsb*/ BitString::BitStructSizeOf(), /*width*/ 1> overflow_; BITSTRUCT_DEFINE_END(SubtypeCheckBits); } // namespace art diff --git a/runtime/subtype_check_bits_and_status.h b/runtime/subtype_check_bits_and_status.h index 11cb9b9d21..321a723985 100644 --- a/runtime/subtype_check_bits_and_status.h +++ b/runtime/subtype_check_bits_and_status.h @@ -19,6 +19,7 @@ #include "base/bit_struct.h" #include "base/bit_utils.h" +#include "base/casts.h" #include "class_status.h" #include "subtype_check_bits.h" @@ -36,13 +37,13 @@ static constexpr size_t NonNumericBitSizeOf() { } /** - * MSB LSB - * +---------------------------------------------------+---------------+ - * | | | - * | SubtypeCheckBits | ClassStatus | - * | | | - * +---------------------------------------------------+---------------+ - * <----- 24 bits -----> <-- 8 bits --> + * MSB (most significant bit) LSB + * +---------------+---------------------------------------------------+ + * | | | + * | ClassStatus | SubtypeCheckBits | + * | | | + * +---------------+---------------------------------------------------+ + * <-- 4 bits --> <----- 28 bits -----> * * Invariants: * @@ -53,20 +54,25 @@ static constexpr size_t NonNumericBitSizeOf() { * This enables a highly efficient path comparison between any two labels: * * src <: target := - * src >> (32 - len(path-to-root(target))) == target >> (32 - len(path-to-root(target)) + * (src & mask) == (target & mask) where mask := (1u << len(path-to-root(target)) - 1u * - * In the above example, the RHS operands are a function of the depth. Since the target - * is known at compile time, it becomes: - * - * src >> #imm_target_shift == #imm + * In the above example, the `len()` (and thus `mask`) is a function of the depth. + * Since the target is known at compile time, it becomes + * (src & #imm_mask) == #imm + * or + * ((src - #imm) << #imm_shift_to_remove_high_bits) == 0 + * or a similar expression chosen for the best performance or code size. * * (This requires that path-to-root in `target` is not truncated, i.e. it is in the Assigned state). */ -static constexpr size_t kClassStatusBitSize = 8u; // NonNumericBitSizeOf<ClassStatus>() +static constexpr size_t kClassStatusBitSize = MinimumBitsToStore(enum_cast<>(ClassStatus::kLast)); +static_assert(kClassStatusBitSize == 4u, "ClassStatus should need 4 bits."); BITSTRUCT_DEFINE_START(SubtypeCheckBitsAndStatus, BitSizeOf<BitString::StorageType>()) - BitStructField<ClassStatus, /*lsb*/0, /*width*/kClassStatusBitSize> status_; - BitStructField<SubtypeCheckBits, /*lsb*/kClassStatusBitSize> subtype_check_info_; - BitStructInt</*lsb*/0, /*width*/BitSizeOf<BitString::StorageType>()> int32_alias_; + BitStructField<SubtypeCheckBits, /*lsb*/ 0> subtype_check_info_; + BitStructField<ClassStatus, + /*lsb*/ SubtypeCheckBits::BitStructSizeOf(), + /*width*/ kClassStatusBitSize> status_; + BitStructInt</*lsb*/ 0, /*width*/ BitSizeOf<BitString::StorageType>()> int32_alias_; BITSTRUCT_DEFINE_END(SubtypeCheckBitsAndStatus); // Use the spare alignment from "ClassStatus" to store all the new SubtypeCheckInfo data. diff --git a/runtime/subtype_check_info.h b/runtime/subtype_check_info.h index 61d590bd59..08db77030e 100644 --- a/runtime/subtype_check_info.h +++ b/runtime/subtype_check_info.h @@ -296,8 +296,7 @@ struct SubtypeCheckInfo { BitString::StorageType GetEncodedPathToRoot() const { BitString::StorageType data = static_cast<BitString::StorageType>(GetPathToRoot()); // Bit strings are logically in the least-significant memory. - // Shift it so the bits are all most-significant. - return data << (BitSizeOf(data) - BitStructSizeOf<BitString>()); + return data; } // Retrieve the path to root bitstring mask as a plain uintN_t that is amenable to @@ -305,17 +304,7 @@ struct SubtypeCheckInfo { BitString::StorageType GetEncodedPathToRootMask() const { size_t num_bitchars = GetSafeDepth(); size_t bitlength = BitString::GetBitLengthTotalAtPosition(num_bitchars); - - BitString::StorageType mask_all = - std::numeric_limits<BitString::StorageType>::max(); - BitString::StorageType mask_lsb = - MaskLeastSignificant<BitString::StorageType>( - BitSizeOf<BitString::StorageType>() - bitlength); - - BitString::StorageType result = mask_all & ~mask_lsb; - - // TODO: refactor above code into MaskMostSignificant? - return result; + return MaskLeastSignificant<BitString::StorageType>(bitlength); } // Get the "Next" bitchar, assuming that there is one to get. diff --git a/runtime/subtype_check_info_test.cc b/runtime/subtype_check_info_test.cc index 338d75a285..91fcc07d65 100644 --- a/runtime/subtype_check_info_test.cc +++ b/runtime/subtype_check_info_test.cc @@ -65,7 +65,7 @@ size_t AsUint(const T& value) { return uint_value; } -// Make max bistring, e.g. BitString[4095,7,255] for {12,3,8} +// Make max bistring, e.g. BitString[4095,15,2047] for {12,4,11} template <size_t kCount = BitString::kCapacity> BitString MakeBitStringMax() { BitString bs{}; @@ -258,60 +258,62 @@ size_t LenForPos() { return BitString::GetBitLengthTotalAtPosition(kPos); } TEST_F(SubtypeCheckInfoTest, EncodedPathToRoot) { using StorageType = BitString::StorageType; - SubtypeCheckInfo io = + SubtypeCheckInfo sci = MakeSubtypeCheckInfo(/*path_to_root*/MakeBitStringMax(), /*next*/BitStringChar{}, /*overflow*/false, /*depth*/BitString::kCapacity); - // 0b11111...000 where MSB == 1, and leading 1s = the maximum bitstring representation. - EXPECT_EQ(MaxInt<StorageType>(LenForPos()) << (BitSizeOf<StorageType>() - LenForPos()), - io.GetEncodedPathToRoot()); - - EXPECT_EQ(MaxInt<StorageType>(LenForPos()) << (BitSizeOf<StorageType>() - LenForPos()), - io.GetEncodedPathToRootMask()); - - // 0b11111...000 where MSB == 1, and leading 1s = the maximum bitstring representation. + // 0b000...111 where LSB == 1, and trailing 1s = the maximum bitstring representation. + EXPECT_EQ(MaxInt<StorageType>(LenForPos()), sci.GetEncodedPathToRoot()); // The rest of this test is written assuming kCapacity == 3 for convenience. // Please update the test if this changes. ASSERT_EQ(3u, BitString::kCapacity); ASSERT_EQ(12u, BitString::kBitSizeAtPosition[0]); - ASSERT_EQ(3u, BitString::kBitSizeAtPosition[1]); - ASSERT_EQ(8u, BitString::kBitSizeAtPosition[2]); + ASSERT_EQ(4u, BitString::kBitSizeAtPosition[1]); + ASSERT_EQ(11u, BitString::kBitSizeAtPosition[2]); - SubtypeCheckInfo io2 = + SubtypeCheckInfo sci2 = MakeSubtypeCheckInfoUnchecked(MakeBitStringMax<2u>(), /*overflow*/false, /*depth*/BitString::kCapacity); -#define MAKE_ENCODED_PATH(pos0, pos1, pos2) (((pos0) << 3u << 8u << 9u) | ((pos1) << 8u << 9u) | ((pos2) << 9u)) +#define MAKE_ENCODED_PATH(pos0, pos1, pos2) \ + (((pos0) << 0) | \ + ((pos1) << BitString::kBitSizeAtPosition[0]) | \ + ((pos2) << (BitString::kBitSizeAtPosition[0] + BitString::kBitSizeAtPosition[1]))) - EXPECT_EQ(MAKE_ENCODED_PATH(MaxInt<BitString::StorageType>(12), 0b111, 0b0), io2.GetEncodedPathToRoot()); - EXPECT_EQ(MAKE_ENCODED_PATH(MaxInt<BitString::StorageType>(12), 0b111, 0b11111111), io2.GetEncodedPathToRootMask()); + EXPECT_EQ(MAKE_ENCODED_PATH(MaxInt<BitString::StorageType>(12), 0b1111, 0b0), + sci2.GetEncodedPathToRoot()); + EXPECT_EQ(MAKE_ENCODED_PATH(MaxInt<BitString::StorageType>(12), 0b1111, 0b11111111111), + sci2.GetEncodedPathToRootMask()); - SubtypeCheckInfo io3 = + SubtypeCheckInfo sci3 = MakeSubtypeCheckInfoUnchecked(MakeBitStringMax<2u>(), /*overflow*/false, /*depth*/BitString::kCapacity - 1u); - EXPECT_EQ(MAKE_ENCODED_PATH(MaxInt<BitString::StorageType>(12), 0b111, 0b0), io3.GetEncodedPathToRoot()); - EXPECT_EQ(MAKE_ENCODED_PATH(MaxInt<BitString::StorageType>(12), 0b111, 0b0), io3.GetEncodedPathToRootMask()); + EXPECT_EQ(MAKE_ENCODED_PATH(MaxInt<BitString::StorageType>(12), 0b1111, 0b0), + sci3.GetEncodedPathToRoot()); + EXPECT_EQ(MAKE_ENCODED_PATH(MaxInt<BitString::StorageType>(12), 0b1111, 0b0), + sci3.GetEncodedPathToRootMask()); - SubtypeCheckInfo io4 = + SubtypeCheckInfo sci4 = MakeSubtypeCheckInfoUnchecked(MakeBitString({0b1010101u}), /*overflow*/false, /*depth*/BitString::kCapacity - 2u); - EXPECT_EQ(MAKE_ENCODED_PATH(0b1010101u, 0b000, 0b0), io4.GetEncodedPathToRoot()); - EXPECT_EQ(MAKE_ENCODED_PATH(MaxInt<BitString::StorageType>(12), 0b000, 0b0), io4.GetEncodedPathToRootMask()); + EXPECT_EQ(MAKE_ENCODED_PATH(0b1010101u, 0b0000, 0b0), sci4.GetEncodedPathToRoot()); + EXPECT_EQ(MAKE_ENCODED_PATH(MaxInt<BitString::StorageType>(12), 0b0000, 0b0), + sci4.GetEncodedPathToRootMask()); } TEST_F(SubtypeCheckInfoTest, NewForRoot) { - SubtypeCheckInfo io = SubtypeCheckInfo::CreateRoot(); - EXPECT_EQ(SubtypeCheckInfo::kAssigned, io.GetState()); // Root is always assigned. - EXPECT_EQ(0u, GetPathToRoot(io).Length()); // Root's path length is 0. - EXPECT_TRUE(HasNext(io)); // Root always has a "Next". - EXPECT_EQ(MakeBitStringChar(1u), io.GetNext()); // Next>=1 to disambiguate from Uninitialized. + SubtypeCheckInfo sci = SubtypeCheckInfo::CreateRoot(); + EXPECT_EQ(SubtypeCheckInfo::kAssigned, sci.GetState()); // Root is always assigned. + EXPECT_EQ(0u, GetPathToRoot(sci).Length()); // Root's path length is 0. + EXPECT_TRUE(HasNext(sci)); // Root always has a "Next". + EXPECT_EQ(MakeBitStringChar(1u), sci.GetNext()); // Next>=1 to disambiguate from Uninitialized. } TEST_F(SubtypeCheckInfoTest, CopyCleared) { diff --git a/runtime/utils.cc b/runtime/utils.cc index bd4175f5fd..79ddcb9bff 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -26,10 +26,10 @@ #include <memory> +#include "android-base/file.h" #include "android-base/stringprintf.h" #include "android-base/strings.h" -#include "base/file_utils.h" #include "dex/dex_file-inl.h" #include "os.h" #include "utf-inl.h" @@ -46,6 +46,7 @@ namespace art { +using android::base::ReadFileToString; using android::base::StringAppendF; using android::base::StringPrintf; @@ -63,6 +64,7 @@ pid_t GetTid() { std::string GetThreadName(pid_t tid) { std::string result; + // TODO: make this less Linux-specific. if (ReadFileToString(StringPrintf("/proc/self/task/%d/comm", tid), &result)) { result.resize(result.size() - 1); // Lose the trailing '\n'. } else { @@ -611,6 +613,7 @@ void SetThreadName(const char* thread_name) { void GetTaskStats(pid_t tid, char* state, int* utime, int* stime, int* task_cpu) { *utime = *stime = *task_cpu = 0; std::string stats; + // TODO: make this less Linux-specific. if (!ReadFileToString(StringPrintf("/proc/self/task/%d/stat", tid), &stats)) { return; } diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc index a53556ffcc..118cffeda6 100644 --- a/runtime/vdex_file.cc +++ b/runtime/vdex_file.cc @@ -25,9 +25,11 @@ #include "base/bit_utils.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" +#include "dex/art_dex_file_loader.h" #include "dex/dex_file.h" #include "dex/dex_file_loader.h" #include "dex_to_dex_decompiler.h" +#include "quicken_info.h" namespace art { @@ -57,11 +59,14 @@ VdexFile::Header::Header(uint32_t number_of_dex_files, DCHECK(IsVersionValid()); } -std::unique_ptr<VdexFile> VdexFile::Open(const std::string& vdex_filename, - bool writable, - bool low_4gb, - bool unquicken, - std::string* error_msg) { +std::unique_ptr<VdexFile> VdexFile::OpenAtAddress(uint8_t* mmap_addr, + size_t mmap_size, + bool mmap_reuse, + const std::string& vdex_filename, + bool writable, + bool low_4gb, + bool unquicken, + std::string* error_msg) { if (!OS::FileExists(vdex_filename.c_str())) { *error_msg = "File " + vdex_filename + " does not exist."; return nullptr; @@ -85,23 +90,43 @@ std::unique_ptr<VdexFile> VdexFile::Open(const std::string& vdex_filename, return nullptr; } - return Open(vdex_file->Fd(), vdex_length, vdex_filename, writable, low_4gb, unquicken, error_msg); + return OpenAtAddress(mmap_addr, + mmap_size, + mmap_reuse, + vdex_file->Fd(), + vdex_length, + vdex_filename, + writable, + low_4gb, + unquicken, + error_msg); } -std::unique_ptr<VdexFile> VdexFile::Open(int file_fd, - size_t vdex_length, - const std::string& vdex_filename, - bool writable, - bool low_4gb, - bool unquicken, - std::string* error_msg) { - std::unique_ptr<MemMap> mmap(MemMap::MapFile( +std::unique_ptr<VdexFile> VdexFile::OpenAtAddress(uint8_t* mmap_addr, + size_t mmap_size, + bool mmap_reuse, + int file_fd, + size_t vdex_length, + const std::string& vdex_filename, + bool writable, + bool low_4gb, + bool unquicken, + std::string* error_msg) { + if (mmap_addr != nullptr && mmap_size < vdex_length) { + LOG(WARNING) << "Insufficient pre-allocated space to mmap vdex."; + mmap_addr = nullptr; + mmap_reuse = false; + } + CHECK(!mmap_reuse || mmap_addr != nullptr); + std::unique_ptr<MemMap> mmap(MemMap::MapFileAtAddress( + mmap_addr, vdex_length, (writable || unquicken) ? PROT_READ | PROT_WRITE : PROT_READ, unquicken ? MAP_PRIVATE : MAP_SHARED, file_fd, 0 /* start offset */, low_4gb, + mmap_reuse, vdex_filename.c_str(), error_msg)); if (mmap == nullptr) { @@ -120,9 +145,8 @@ std::unique_ptr<VdexFile> VdexFile::Open(int file_fd, if (!vdex->OpenAllDexFiles(&unique_ptr_dex_files, error_msg)) { return nullptr; } - Unquicken(MakeNonOwningPointerVector(unique_ptr_dex_files), - vdex->GetQuickeningInfo(), - /* decompile_return_instruction */ false); + vdex->Unquicken(MakeNonOwningPointerVector(unique_ptr_dex_files), + /* decompile_return_instruction */ false); // Update the quickening info size to pretend there isn't any. reinterpret_cast<Header*>(vdex->mmap_->Begin())->quickening_info_size_ = 0; } @@ -135,19 +159,21 @@ const uint8_t* VdexFile::GetNextDexFileData(const uint8_t* cursor) const { DCHECK(cursor == nullptr || (cursor > Begin() && cursor <= End())); if (cursor == nullptr) { // Beginning of the iteration, return the first dex file if there is one. - return HasDexSection() ? DexBegin() : nullptr; + return HasDexSection() ? DexBegin() + sizeof(QuickeningTableOffsetType) : nullptr; } else { // Fetch the next dex file. Return null if there is none. const uint8_t* data = cursor + reinterpret_cast<const DexFile::Header*>(cursor)->file_size_; // Dex files are required to be 4 byte aligned. the OatWriter makes sure they are, see // OatWriter::SeekToDexFiles. data = AlignUp(data, 4); - return (data == DexEnd()) ? nullptr : data; + + return (data == DexEnd()) ? nullptr : data + sizeof(QuickeningTableOffsetType); } } bool VdexFile::OpenAllDexFiles(std::vector<std::unique_ptr<const DexFile>>* dex_files, std::string* error_msg) { + const ArtDexFileLoader dex_file_loader; size_t i = 0; for (const uint8_t* dex_file_start = GetNextDexFileData(nullptr); dex_file_start != nullptr; @@ -156,14 +182,14 @@ bool VdexFile::OpenAllDexFiles(std::vector<std::unique_ptr<const DexFile>>* dex_ // TODO: Supply the location information for a vdex file. static constexpr char kVdexLocation[] = ""; std::string location = DexFileLoader::GetMultiDexLocation(i, kVdexLocation); - std::unique_ptr<const DexFile> dex(DexFileLoader::Open(dex_file_start, - size, - location, - GetLocationChecksum(i), - nullptr /*oat_dex_file*/, - false /*verify*/, - false /*verify_checksum*/, - error_msg)); + std::unique_ptr<const DexFile> dex(dex_file_loader.Open(dex_file_start, + size, + location, + GetLocationChecksum(i), + nullptr /*oat_dex_file*/, + false /*verify*/, + false /*verify_checksum*/, + error_msg)); if (dex == nullptr) { return false; } @@ -172,64 +198,68 @@ bool VdexFile::OpenAllDexFiles(std::vector<std::unique_ptr<const DexFile>>* dex_ return true; } -void VdexFile::Unquicken(const std::vector<const DexFile*>& dex_files, - ArrayRef<const uint8_t> quickening_info, - bool decompile_return_instruction) { - if (quickening_info.size() == 0 && !decompile_return_instruction) { - // Bail early if there is no quickening info and no need to decompile - // RETURN_VOID_NO_BARRIER instructions to RETURN_VOID instructions. - return; - } - - for (uint32_t i = 0; i < dex_files.size(); ++i) { - UnquickenDexFile(*dex_files[i], quickening_info, decompile_return_instruction); +void VdexFile::Unquicken(const std::vector<const DexFile*>& target_dex_files, + bool decompile_return_instruction) const { + const uint8_t* source_dex = GetNextDexFileData(nullptr); + for (const DexFile* target_dex : target_dex_files) { + UnquickenDexFile(*target_dex, source_dex, decompile_return_instruction); + source_dex = GetNextDexFileData(source_dex); } + DCHECK(source_dex == nullptr); } -typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t; - -static uint32_t GetDebugInfoOffsetInternal(const DexFile& dex_file, - uint32_t offset_in_code_item, - const ArrayRef<const uint8_t>& quickening_info) { - if (quickening_info.size() == 0) { - // No quickening info: offset is the right one, return it. - return offset_in_code_item; - } - uint32_t quickening_offset = offset_in_code_item - dex_file.Size(); - return *reinterpret_cast<const unaligned_uint32_t*>(quickening_info.data() + quickening_offset); +uint32_t VdexFile::GetQuickeningInfoTableOffset(const uint8_t* source_dex_begin) const { + DCHECK_GE(source_dex_begin, DexBegin()); + DCHECK_LT(source_dex_begin, DexEnd()); + return reinterpret_cast<const QuickeningTableOffsetType*>(source_dex_begin)[-1]; } -static uint32_t GetQuickeningInfoOffsetFrom(const DexFile& dex_file, - uint32_t offset_in_code_item, - const ArrayRef<const uint8_t>& quickening_info) { - if (offset_in_code_item < dex_file.Size()) { - return VdexFile::kNoQuickeningInfoOffset; - } - if (quickening_info.size() == 0) { - // No quickening info. - return VdexFile::kNoQuickeningInfoOffset; - } - uint32_t quickening_offset = offset_in_code_item - dex_file.Size(); +QuickenInfoOffsetTableAccessor VdexFile::GetQuickenInfoOffsetTable( + const uint8_t* source_dex_begin, + uint32_t num_method_ids, + const ArrayRef<const uint8_t>& quickening_info) const { + // The offset a is in preheader right before the dex file. + const uint32_t offset = GetQuickeningInfoTableOffset(source_dex_begin); + const uint8_t* data_ptr = quickening_info.data() + offset; + return QuickenInfoOffsetTableAccessor(data_ptr, num_method_ids); +} - // Add 2 * sizeof(uint32_t) for the debug info offset and the data offset. - CHECK_LE(quickening_offset + 2 * sizeof(uint32_t), quickening_info.size()); - return *reinterpret_cast<const unaligned_uint32_t*>( - quickening_info.data() + quickening_offset + sizeof(uint32_t)); +QuickenInfoOffsetTableAccessor VdexFile::GetQuickenInfoOffsetTable( + const DexFile& dex_file, + const ArrayRef<const uint8_t>& quickening_info) const { + return GetQuickenInfoOffsetTable(dex_file.Begin(), dex_file.NumMethodIds(), quickening_info); } static ArrayRef<const uint8_t> GetQuickeningInfoAt(const ArrayRef<const uint8_t>& quickening_info, uint32_t quickening_offset) { - return (quickening_offset == VdexFile::kNoQuickeningInfoOffset) - ? ArrayRef<const uint8_t>(nullptr, 0) - : quickening_info.SubArray( - quickening_offset + sizeof(uint32_t), - *reinterpret_cast<const unaligned_uint32_t*>( - quickening_info.data() + quickening_offset)); + ArrayRef<const uint8_t> remaining = quickening_info.SubArray(quickening_offset); + return remaining.SubArray(0u, QuickenInfoTable::SizeInBytes(remaining)); +} + +static uint32_t GetQuickeningInfoOffset(const QuickenInfoOffsetTableAccessor& table, + uint32_t dex_method_index, + const ArrayRef<const uint8_t>& quickening_info) { + DCHECK(!quickening_info.empty()); + uint32_t remainder; + uint32_t offset = table.ElementOffset(dex_method_index, &remainder); + // Decode the sizes for the remainder offsets (not covered by the table). + while (remainder != 0) { + offset += GetQuickeningInfoAt(quickening_info, offset).size(); + --remainder; + } + return offset; } void VdexFile::UnquickenDexFile(const DexFile& target_dex_file, - ArrayRef<const uint8_t> quickening_info, - bool decompile_return_instruction) { + const DexFile& source_dex_file, + bool decompile_return_instruction) const { + UnquickenDexFile(target_dex_file, source_dex_file.Begin(), decompile_return_instruction); +} + +void VdexFile::UnquickenDexFile(const DexFile& target_dex_file, + const uint8_t* source_dex_begin, + bool decompile_return_instruction) const { + ArrayRef<const uint8_t> quickening_info = GetQuickeningInfo(); if (quickening_info.size() == 0 && !decompile_return_instruction) { // Bail early if there is no quickening info and no need to decompile // RETURN_VOID_NO_BARRIER instructions to RETURN_VOID instructions. @@ -244,19 +274,20 @@ void VdexFile::UnquickenDexFile(const DexFile& target_dex_file, class_it.Next()) { if (class_it.IsAtMethod() && class_it.GetMethodCodeItem() != nullptr) { const DexFile::CodeItem* code_item = class_it.GetMethodCodeItem(); - uint32_t quickening_offset = GetQuickeningInfoOffsetFrom( - target_dex_file, code_item->debug_info_off_, quickening_info); - if (quickening_offset != VdexFile::kNoQuickeningInfoOffset) { - // If we have quickening data, put back the original debug_info_off. - const_cast<DexFile::CodeItem*>(code_item)->SetDebugInfoOffset( - GetDebugInfoOffsetInternal(target_dex_file, - code_item->debug_info_off_, - quickening_info)); + ArrayRef<const uint8_t> quicken_data; + if (!quickening_info.empty()) { + const uint32_t quickening_offset = GetQuickeningInfoOffset( + GetQuickenInfoOffsetTable(source_dex_begin, + target_dex_file.NumMethodIds(), + quickening_info), + class_it.GetMemberIndex(), + quickening_info); + quicken_data = GetQuickeningInfoAt(quickening_info, quickening_offset); } optimizer::ArtDecompileDEX( target_dex_file, *code_item, - GetQuickeningInfoAt(quickening_info, quickening_offset), + quicken_data, decompile_return_instruction); } } @@ -264,25 +295,17 @@ void VdexFile::UnquickenDexFile(const DexFile& target_dex_file, } } -uint32_t VdexFile::GetDebugInfoOffset(const DexFile& dex_file, uint32_t offset_in_code_item) const { - return GetDebugInfoOffsetInternal(dex_file, offset_in_code_item, GetQuickeningInfo()); -} - -const uint8_t* VdexFile::GetQuickenedInfoOf(const DexFile& dex_file, - uint32_t code_item_offset) const { +ArrayRef<const uint8_t> VdexFile::GetQuickenedInfoOf(const DexFile& dex_file, + uint32_t dex_method_idx) const { ArrayRef<const uint8_t> quickening_info = GetQuickeningInfo(); - uint32_t quickening_offset = GetQuickeningInfoOffsetFrom( - dex_file, dex_file.GetCodeItem(code_item_offset)->debug_info_off_, quickening_info); - - return GetQuickeningInfoAt(quickening_info, quickening_offset).data(); -} - -bool VdexFile::CanEncodeQuickenedData(const DexFile& dex_file) { - // We are going to use the debug_info_off_ to signal there is - // quickened data, by putting a value greater than dex_file.Size(). So - // make sure we have some room in the offset by checking that we have at least - // half of the range of a uint32_t. - return dex_file.Size() <= (std::numeric_limits<uint32_t>::max() >> 1); + if (quickening_info.empty()) { + return ArrayRef<const uint8_t>(); + } + const uint32_t quickening_offset = GetQuickeningInfoOffset( + GetQuickenInfoOffsetTable(dex_file, quickening_info), + dex_method_idx, + quickening_info); + return GetQuickeningInfoAt(quickening_info, quickening_offset); } } // namespace art diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h index 2d9fcab59c..4687a393e2 100644 --- a/runtime/vdex_file.h +++ b/runtime/vdex_file.h @@ -24,6 +24,7 @@ #include "base/macros.h" #include "mem_map.h" #include "os.h" +#include "quicken_info.h" namespace art { @@ -35,18 +36,17 @@ class DexFile; // File format: // VdexFile::Header fixed-length header // -// DEX[0] array of the input DEX files -// DEX[1] the bytecode may have been quickened +// quicken_table_off[0] offset into QuickeningInfo section for offset table for DEX[0]. +// DEX[0] array of the input DEX files, the bytecode may have been quickened. +// quicken_table_off[1] +// DEX[1] // ... // DEX[D] // VerifierDeps // uint8[D][] verification dependencies // QuickeningInfo // uint8[D][] quickening data -// unaligned_uint32_t[D][2][] table of offsets pair: -// uint32_t[0] contains original CodeItem::debug_info_off_ -// uint32_t[1] contains quickening data offset from the start -// of QuickeningInfo +// uint32[D][] quickening data offset tables class VdexFile { public: @@ -84,8 +84,8 @@ class VdexFile { private: static constexpr uint8_t kVdexMagic[] = { 'v', 'd', 'e', 'x' }; - // Last update: Lookup-friendly encoding for quickening info. - static constexpr uint8_t kVdexVersion[] = { '0', '1', '1', '\0' }; + // Last update: Side table for debug info offsets in compact dex. + static constexpr uint8_t kVdexVersion[] = { '0', '1', '4', '\0' }; uint8_t magic_[4]; uint8_t version_[4]; @@ -98,15 +98,49 @@ class VdexFile { }; typedef uint32_t VdexChecksum; + using QuickeningTableOffsetType = uint32_t; explicit VdexFile(MemMap* mmap) : mmap_(mmap) {} // Returns nullptr if the vdex file cannot be opened or is not valid. + // The mmap_* parameters can be left empty (nullptr/0/false) to allocate at random address. + static std::unique_ptr<VdexFile> OpenAtAddress(uint8_t* mmap_addr, + size_t mmap_size, + bool mmap_reuse, + const std::string& vdex_filename, + bool writable, + bool low_4gb, + bool unquicken, + std::string* error_msg); + + // Returns nullptr if the vdex file cannot be opened or is not valid. + // The mmap_* parameters can be left empty (nullptr/0/false) to allocate at random address. + static std::unique_ptr<VdexFile> OpenAtAddress(uint8_t* mmap_addr, + size_t mmap_size, + bool mmap_reuse, + int file_fd, + size_t vdex_length, + const std::string& vdex_filename, + bool writable, + bool low_4gb, + bool unquicken, + std::string* error_msg); + + // Returns nullptr if the vdex file cannot be opened or is not valid. static std::unique_ptr<VdexFile> Open(const std::string& vdex_filename, bool writable, bool low_4gb, bool unquicken, - std::string* error_msg); + std::string* error_msg) { + return OpenAtAddress(nullptr, + 0, + false, + vdex_filename, + writable, + low_4gb, + unquicken, + error_msg); + } // Returns nullptr if the vdex file cannot be opened or is not valid. static std::unique_ptr<VdexFile> Open(int file_fd, @@ -115,7 +149,18 @@ class VdexFile { bool writable, bool low_4gb, bool unquicken, - std::string* error_msg); + std::string* error_msg) { + return OpenAtAddress(nullptr, + 0, + false, + file_fd, + vdex_length, + vdex_filename, + writable, + low_4gb, + unquicken, + error_msg); + } const uint8_t* Begin() const { return mmap_->Begin(); } const uint8_t* End() const { return mmap_->End(); } @@ -160,29 +205,42 @@ class VdexFile { // `decompile_return_instruction` controls if RETURN_VOID_BARRIER instructions are // decompiled to RETURN_VOID instructions using the slower ClassDataItemIterator // instead of the faster QuickeningInfoIterator. - static void Unquicken(const std::vector<const DexFile*>& dex_files, - ArrayRef<const uint8_t> quickening_info, - bool decompile_return_instruction); + // Always unquickens using the vdex dex files as the source for quicken tables. + void Unquicken(const std::vector<const DexFile*>& target_dex_files, + bool decompile_return_instruction) const; // Fully unquicken `target_dex_file` based on `quickening_info`. - static void UnquickenDexFile(const DexFile& target_dex_file, - ArrayRef<const uint8_t> quickening_info, - bool decompile_return_instruction); + void UnquickenDexFile(const DexFile& target_dex_file, + const DexFile& source_dex_file, + bool decompile_return_instruction) const; - // Return the quickening info of the given code item. - const uint8_t* GetQuickenedInfoOf(const DexFile& dex_file, uint32_t code_item_offset) const; + // Return the quickening info of a given method index (or null if it's empty). + ArrayRef<const uint8_t> GetQuickenedInfoOf(const DexFile& dex_file, + uint32_t dex_method_idx) const; - uint32_t GetDebugInfoOffset(const DexFile& dex_file, uint32_t offset_in_code_item) const; + private: + uint32_t GetQuickeningInfoTableOffset(const uint8_t* source_dex_begin) const; - static bool CanEncodeQuickenedData(const DexFile& dex_file); + // Source dex must be the in the vdex file. + void UnquickenDexFile(const DexFile& target_dex_file, + const uint8_t* source_dex_begin, + bool decompile_return_instruction) const; - static constexpr uint32_t kNoQuickeningInfoOffset = -1; + QuickenInfoOffsetTableAccessor GetQuickenInfoOffsetTable( + const DexFile& dex_file, + const ArrayRef<const uint8_t>& quickening_info) const; + + QuickenInfoOffsetTableAccessor GetQuickenInfoOffsetTable( + const uint8_t* source_dex_begin, + uint32_t num_method_ids, + const ArrayRef<const uint8_t>& quickening_info) const; - private: bool HasDexSection() const { return GetHeader().GetDexSize() != 0; } + bool ContainsDexFile(const DexFile& dex_file) const; + const uint8_t* DexBegin() const { return Begin() + sizeof(Header) + GetHeader().GetSizeOfChecksumsSection(); } @@ -191,8 +249,6 @@ class VdexFile { return DexBegin() + GetHeader().GetDexSize(); } - uint32_t GetDexFileIndex(const DexFile& dex_file) const; - std::unique_ptr<MemMap> mmap_; DISALLOW_COPY_AND_ASSIGN(VdexFile); diff --git a/test/626-checker-arm64-scratch-register/smali/Smali.smali b/test/626-checker-arm64-scratch-register/smali/Smali.smali new file mode 100644 index 0000000000..e6943cf717 --- /dev/null +++ b/test/626-checker-arm64-scratch-register/smali/Smali.smali @@ -0,0 +1,2119 @@ +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +.class public LSmali; +.super Ljava/lang/Object; +.field b00:Z +.field b01:Z +.field b02:Z +.field b03:Z +.field b04:Z +.field b05:Z +.field b06:Z +.field b07:Z +.field b08:Z +.field b09:Z +.field b10:Z +.field b11:Z +.field b12:Z +.field b13:Z +.field b14:Z +.field b15:Z +.field b16:Z +.field b17:Z +.field b18:Z +.field b19:Z +.field b20:Z +.field b21:Z +.field b22:Z +.field b23:Z +.field b24:Z +.field b25:Z +.field b26:Z +.field b27:Z +.field b28:Z +.field b29:Z +.field b30:Z +.field b31:Z +.field b32:Z +.field b33:Z +.field b34:Z +.field b35:Z +.field b36:Z + +.field conditionA:Z +.field conditionB:Z +.field conditionC:Z + +.method public constructor <init>()V + .registers 1 + invoke-direct {p0}, Ljava/lang/Object;-><init>()V + return-void +.end method + +## CHECK-START-ARM64: void Smali.test() register (after) +## CHECK: begin_block +## CHECK: name "B0" +## CHECK: <<This:l\d+>> ParameterValue +## CHECK: end_block +## CHECK: begin_block +## CHECK: successors "<<ThenBlock:B\d+>>" "<<ElseBlock:B\d+>>" +## CHECK: <<CondB:z\d+>> InstanceFieldGet [<<This>>] field_name:Smali.conditionB +## CHECK: If [<<CondB>>] +## CHECK: end_block +## CHECK: begin_block +## CHECK: name "<<ElseBlock>>" +## CHECK: ParallelMove moves:[40(sp)->d0,24(sp)->32(sp),28(sp)->36(sp),d0->d3,d3->d4,d2->d5,d4->d6,d5->d7,d6->d18,d7->d19,d18->d20,d19->d21,d20->d22,d21->d23,d22->d10,d23->d11,16(sp)->24(sp),20(sp)->28(sp),d10->d14,d11->d12,d12->d13,d13->d1,d14->d2,32(sp)->16(sp),36(sp)->20(sp)] +## CHECK: end_block + +## CHECK-START-ARM64: void Smali.test() disassembly (after) +## CHECK: begin_block +## CHECK: name "B0" +## CHECK: <<This:l\d+>> ParameterValue +## CHECK: end_block +## CHECK: begin_block +## CHECK: successors "<<ThenBlock:B\d+>>" "<<ElseBlock:B\d+>>" +## CHECK: <<CondB:z\d+>> InstanceFieldGet [<<This>>] field_name:Smali.conditionB +## CHECK: If [<<CondB>>] +## CHECK: end_block +## CHECK: begin_block +## CHECK: name "<<ElseBlock>>" +## CHECK: ParallelMove moves:[invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid] +## CHECK: fmov d31, d2 +## CHECK: ldr s2, [sp, #36] +## CHECK: ldr w16, [sp, #16] +## CHECK: str w16, [sp, #36] +## CHECK: str s14, [sp, #16] +## CHECK: ldr s14, [sp, #28] +## CHECK: str s1, [sp, #28] +## CHECK: ldr s1, [sp, #32] +## CHECK: str s31, [sp, #32] +## CHECK: ldr s31, [sp, #20] +## CHECK: str s31, [sp, #40] +## CHECK: str s12, [sp, #20] +## CHECK: fmov d12, d11 +## CHECK: fmov d11, d10 +## CHECK: fmov d10, d23 +## CHECK: fmov d23, d22 +## CHECK: fmov d22, d21 +## CHECK: fmov d21, d20 +## CHECK: fmov d20, d19 +## CHECK: fmov d19, d18 +## CHECK: fmov d18, d7 +## CHECK: fmov d7, d6 +## CHECK: fmov d6, d5 +## CHECK: fmov d5, d4 +## CHECK: fmov d4, d3 +## CHECK: fmov d3, d13 +## CHECK: ldr s13, [sp, #24] +## CHECK: str s3, [sp, #24] +## CHECK: ldr s3, pc+{{\d+}} (addr {{0x[0-9a-f]+}}) (100) +## CHECK: end_block +.method public test()V + .registers 45 + + const-string v39, "" + + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b17:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_367 + + const/16 v19, 0x0 + + :goto_c + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b16:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_36b + + const/16 v18, 0x0 + + :goto_16 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b18:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_36f + + const/16 v20, 0x0 + + :goto_20 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b19:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_373 + + const/16 v21, 0x0 + + :goto_2a + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b20:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_377 + + const/16 v22, 0x0 + + :goto_34 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b21:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_37b + + const/16 v23, 0x0 + + :goto_3e + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b15:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_37f + + const/16 v17, 0x0 + + :goto_48 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b00:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_383 + + const/4 v2, 0x0 + + :goto_51 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b22:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_387 + + const/16 v24, 0x0 + + :goto_5b + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b23:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_38b + + const/16 v25, 0x0 + + :goto_65 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b24:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_38f + + const/16 v26, 0x0 + + :goto_6f + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b25:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_393 + + const/16 v27, 0x0 + + :goto_79 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b26:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_397 + + const/16 v28, 0x0 + + :goto_83 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b27:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_39b + + const/16 v29, 0x0 + + :goto_8d + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b29:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_39f + + const/16 v31, 0x0 + + :goto_97 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b28:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3a3 + + const/16 v30, 0x0 + + :goto_a1 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b01:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3a7 + + const/4 v3, 0x0 + + :goto_aa + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b02:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3ab + + const/4 v4, 0x0 + + :goto_b3 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b03:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3af + + const/4 v5, 0x0 + + :goto_bc + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b04:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3b3 + + const/4 v6, 0x0 + + :goto_c5 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b05:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3b7 + + const/4 v7, 0x0 + + :goto_ce + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b07:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3bb + + const/4 v9, 0x0 + + :goto_d7 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b06:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3bf + + const/4 v8, 0x0 + + :goto_e0 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b30:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3c3 + + const/16 v32, 0x0 + + :goto_ea + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b31:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3c7 + + const/16 v33, 0x0 + + :goto_f4 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b32:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3cb + + const/16 v34, 0x0 + + :goto_fe + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b33:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3cf + + const/16 v35, 0x0 + + :goto_108 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b34:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3d3 + + const/16 v36, 0x0 + + :goto_112 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b36:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3d7 + + const/16 v38, 0x0 + + :goto_11c + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b35:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3db + + const/16 v37, 0x0 + + :goto_126 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b08:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3df + + const/4 v10, 0x0 + + :goto_12f + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b09:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3e3 + + const/4 v11, 0x0 + + :goto_138 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b10:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3e7 + + const/4 v12, 0x0 + + :goto_141 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b11:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3eb + + const/4 v13, 0x0 + + :goto_14a + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b12:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3ef + + const/4 v14, 0x0 + + :goto_153 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b14:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3f3 + + const/16 v16, 0x0 + + :goto_15d + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->b13:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_3f7 + + const/4 v15, 0x0 + + :goto_166 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->conditionA:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_202 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v18, v18, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v19, v19, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v20, v20, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v21, v21, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v22, v22, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v23, v23, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v17, v17, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v10, v10, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v11, v11, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v12, v12, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v13, v13, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v14, v14, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v32, v32, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v33, v33, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v34, v34, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v35, v35, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v36, v36, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v3, v3, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v4, v4, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v5, v5, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v6, v6, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v7, v7, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v25, v25, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v26, v26, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v27, v27, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v28, v28, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v29, v29, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v24, v24, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v2, v2, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v16, v16, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v15, v15, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v38, v38, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v37, v37, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v9, v9, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v8, v8, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v31, v31, v42 + + const/high16 v42, 0x447a0000 # 1000.0f + + div-float v30, v30, v42 + + :cond_202 + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->conditionB:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_29e + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v18, v18, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v19, v19, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v20, v20, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v21, v21, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v22, v22, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v23, v23, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v17, v17, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v10, v10, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v11, v11, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v12, v12, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v13, v13, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v14, v14, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v32, v32, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v33, v33, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v34, v34, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v35, v35, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v36, v36, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v3, v3, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v4, v4, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v5, v5, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v6, v6, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v7, v7, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v25, v25, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v26, v26, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v27, v27, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v28, v28, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v29, v29, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v24, v24, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v2, v2, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v16, v16, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v15, v15, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v38, v38, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v37, v37, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v9, v9, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v8, v8, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v31, v31, v42 + + const/high16 v42, 0x42c80000 # 100.0f + + div-float v30, v30, v42 + + :cond_29e + move-object/from16 v0, p0 + + iget-boolean v0, v0, LSmali;->conditionC:Z + + move/from16 v42, v0 + + if-eqz v42, :cond_33a + + const/high16 v42, 0x41400000 # 12.0f + + div-float v18, v18, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v19, v19, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v20, v20, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v21, v21, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v22, v22, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v23, v23, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v17, v17, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v10, v10, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v11, v11, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v12, v12, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v13, v13, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v14, v14, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v32, v32, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v33, v33, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v34, v34, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v35, v35, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v36, v36, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v3, v3, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v4, v4, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v5, v5, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v6, v6, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v7, v7, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v25, v25, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v26, v26, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v27, v27, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v28, v28, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v29, v29, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v24, v24, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v2, v2, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v16, v16, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v15, v15, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v38, v38, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v37, v37, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v9, v9, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v8, v8, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v31, v31, v42 + + const/high16 v42, 0x41400000 # 12.0f + + div-float v30, v30, v42 + + :cond_33a + const/16 v41, 0x0 + + const/high16 v42, 0x42c80000 # 100.0f + + mul-float v42, v42, v41 + + invoke-static/range {v42 .. v42}, Ljava/lang/Math;->round(F)I + + move-result v42 + + move/from16 v0, v42 + + int-to-float v0, v0 + + move/from16 v42, v0 + + const/high16 v43, 0x42c80000 # 100.0f + + div-float v41, v42, v43 + + new-instance v42, Ljava/lang/StringBuilder; + + invoke-direct/range {v42 .. v42}, Ljava/lang/StringBuilder;-><init>()V + + move-object/from16 v0, v42 + + move/from16 v1, v41 + + invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(F)Ljava/lang/StringBuilder; + + move-result-object v42 + + move-object/from16 v0, v42 + + move-object/from16 v1, v39 + + invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + move-result-object v42 + + invoke-virtual/range {v42 .. v42}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; + + move-result-object v40 + + return-void + + :cond_367 + const/high16 v19, 0x3f800000 # 1.0f + + goto/16 :goto_c + + :cond_36b + const/high16 v18, 0x3f800000 # 1.0f + + goto/16 :goto_16 + + :cond_36f + const/high16 v20, 0x3f800000 # 1.0f + + goto/16 :goto_20 + + :cond_373 + const/high16 v21, 0x3f800000 # 1.0f + + goto/16 :goto_2a + + :cond_377 + const/high16 v22, 0x3f800000 # 1.0f + + goto/16 :goto_34 + + :cond_37b + const/high16 v23, 0x3f800000 # 1.0f + + goto/16 :goto_3e + + :cond_37f + const/high16 v17, 0x3f800000 # 1.0f + + goto/16 :goto_48 + + :cond_383 + const/high16 v2, 0x3f800000 # 1.0f + + goto/16 :goto_51 + + :cond_387 + const/high16 v24, 0x3f800000 # 1.0f + + goto/16 :goto_5b + + :cond_38b + const/high16 v25, 0x3f800000 # 1.0f + + goto/16 :goto_65 + + :cond_38f + const/high16 v26, 0x3f800000 # 1.0f + + goto/16 :goto_6f + + :cond_393 + const/high16 v27, 0x3f800000 # 1.0f + + goto/16 :goto_79 + + :cond_397 + const/high16 v28, 0x3f800000 # 1.0f + + goto/16 :goto_83 + + :cond_39b + const/high16 v29, 0x3f800000 # 1.0f + + goto/16 :goto_8d + + :cond_39f + const/high16 v31, 0x3f800000 # 1.0f + + goto/16 :goto_97 + + :cond_3a3 + const/high16 v30, 0x3f800000 # 1.0f + + goto/16 :goto_a1 + + :cond_3a7 + const/high16 v3, 0x3f800000 # 1.0f + + goto/16 :goto_aa + + :cond_3ab + const/high16 v4, 0x3f800000 # 1.0f + + goto/16 :goto_b3 + + :cond_3af + const/high16 v5, 0x3f800000 # 1.0f + + goto/16 :goto_bc + + :cond_3b3 + const/high16 v6, 0x3f800000 # 1.0f + + goto/16 :goto_c5 + + :cond_3b7 + const/high16 v7, 0x3f800000 # 1.0f + + goto/16 :goto_ce + + :cond_3bb + const/high16 v9, 0x3f800000 # 1.0f + + goto/16 :goto_d7 + + :cond_3bf + const/high16 v8, 0x3f800000 # 1.0f + + goto/16 :goto_e0 + + :cond_3c3 + const/high16 v32, 0x3f800000 # 1.0f + + goto/16 :goto_ea + + :cond_3c7 + const/high16 v33, 0x3f800000 # 1.0f + + goto/16 :goto_f4 + + :cond_3cb + const/high16 v34, 0x3f800000 # 1.0f + + goto/16 :goto_fe + + :cond_3cf + const/high16 v35, 0x3f800000 # 1.0f + + goto/16 :goto_108 + + :cond_3d3 + const/high16 v36, 0x3f800000 # 1.0f + + goto/16 :goto_112 + + :cond_3d7 + const/high16 v38, 0x3f800000 # 1.0f + + goto/16 :goto_11c + + :cond_3db + const/high16 v37, 0x3f800000 # 1.0f + + goto/16 :goto_126 + + :cond_3df + const/high16 v10, 0x3f800000 # 1.0f + + goto/16 :goto_12f + + :cond_3e3 + const/high16 v11, 0x3f800000 # 1.0f + + goto/16 :goto_138 + + :cond_3e7 + const/high16 v12, 0x3f800000 # 1.0f + + goto/16 :goto_141 + + :cond_3eb + const/high16 v13, 0x3f800000 # 1.0f + + goto/16 :goto_14a + + :cond_3ef + const/high16 v14, 0x3f800000 # 1.0f + + goto/16 :goto_153 + + :cond_3f3 + const/high16 v16, 0x3f800000 # 1.0f + + goto/16 :goto_15d + + :cond_3f7 + const/high16 v15, 0x3f800000 # 1.0f + + goto/16 :goto_166 +.end method + +## CHECK-START-ARM64: void Smali.testD8() register (after) +## CHECK: begin_block +## CHECK: name "B0" +## CHECK: <<This:l\d+>> ParameterValue +## CHECK: end_block + +## CHECK: begin_block +## CHECK: successors "<<ThenBlockA:B\d+>>" "<<ElseBlockA:B\d+>>" +## CHECK: <<CondA:z\d+>> InstanceFieldGet [<<This>>] field_name:Smali.conditionA +## CHECK: If [<<CondA>>] +## CHECK: end_block + +## CHECK: begin_block +## CHECK: name "<<ThenBlockA>>" +## CHECK: end_block +## CHECK: begin_block +## CHECK: name "<<ElseBlockA>>" +## CHECK: ParallelMove moves:[d2->d0,40(sp)->d17,d20->d26,d19->d27,d27->d1,d26->d2,d14->d20,d13->d19,d17->d14,d0->d13] +## CHECK: end_block + +## CHECK: begin_block +## CHECK: successors "<<ThenBlockB:B\d+>>" "<<ElseBlockB:B\d+>>" +## CHECK: <<CondB:z\d+>> InstanceFieldGet [<<This>>] field_name:Smali.conditionB +## CHECK: If [<<CondB>>] +## CHECK: end_block + +## CHECK: begin_block +## CHECK: name "<<ThenBlockB>>" +## CHECK: end_block +## CHECK: begin_block +## CHECK: name "<<ElseBlockB>>" +## CHECK: ParallelMove moves:[#100->d13,16(sp)->d1,20(sp)->d2,28(sp)->d19,24(sp)->d20,36(sp)->d14,32(sp)->16(sp),d1->20(sp),d2->24(sp),d20->28(sp),d19->32(sp),d14->36(sp),d13->40(sp)] +## CHECK: end_block + +## CHECK-START-ARM64: void Smali.testD8() disassembly (after) +## CHECK: begin_block +## CHECK: name "B0" +## CHECK: <<This:l\d+>> ParameterValue +## CHECK: end_block + +## CHECK: begin_block +## CHECK: successors "<<ThenBlockA:B\d+>>" "<<ElseBlockA:B\d+>>" +## CHECK: <<CondA:z\d+>> InstanceFieldGet [<<This>>] field_name:Smali.conditionA +## CHECK: If [<<CondA>>] +## CHECK: end_block + +## CHECK: begin_block +## CHECK: name "<<ThenBlockA>>" +## CHECK: end_block +## CHECK: begin_block +## CHECK: name "<<ElseBlockA>>" +## CHECK: end_block + +## CHECK: begin_block +## CHECK: successors "<<ThenBlockB:B\d+>>" "<<ElseBlockB:B\d+>>" +## CHECK: <<CondB:z\d+>> InstanceFieldGet [<<This>>] field_name:Smali.conditionB +## CHECK: If [<<CondB>>] +## CHECK: end_block + +## CHECK: begin_block +## CHECK: name "<<ThenBlockB>>" +## CHECK: end_block +## CHECK: begin_block +## CHECK: name "<<ElseBlockB>>" +## CHECK: ParallelMove moves:[invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid] +## CHECK: ldr w16, [sp, #32] +## CHECK: str s19, [sp, #32] +## CHECK: ldr s19, [sp, #28] +## CHECK: str s20, [sp, #28] +## CHECK: ldr s20, [sp, #24] +## CHECK: str s2, [sp, #24] +## CHECK: ldr s2, [sp, #20] +## CHECK: str s1, [sp, #20] +## CHECK: ldr s1, [sp, #16] +## CHECK: str w16, [sp, #16] +## CHECK: fmov d31, d14 +## CHECK: ldr s14, [sp, #36] +## CHECK: str s31, [sp, #36] +## CHECK: str s13, [sp, #40] +## CHECK: ldr s13, pc+580 (addr 0x61c) (100) +## CHECK: end_block +.method public testD8()V + .registers 47 + + move-object/from16 v0, p0 + + const-string v1, "" + + iget-boolean v2, v0, LSmali;->b17:Z + + if-eqz v2, :cond_a + + const/4 v2, 0x0 + + goto :goto_c + + :cond_a + const/high16 v2, 0x3f800000 # 1.0f + + :goto_c + iget-boolean v5, v0, LSmali;->b16:Z + + if-eqz v5, :cond_12 + + const/4 v5, 0x0 + + goto :goto_14 + + :cond_12 + const/high16 v5, 0x3f800000 # 1.0f + + :goto_14 + iget-boolean v6, v0, LSmali;->b18:Z + + if-eqz v6, :cond_1a + + const/4 v6, 0x0 + + goto :goto_1c + + :cond_1a + const/high16 v6, 0x3f800000 # 1.0f + + :goto_1c + iget-boolean v7, v0, LSmali;->b19:Z + + if-eqz v7, :cond_22 + + const/4 v7, 0x0 + + goto :goto_24 + + :cond_22 + const/high16 v7, 0x3f800000 # 1.0f + + :goto_24 + iget-boolean v8, v0, LSmali;->b20:Z + + if-eqz v8, :cond_2a + + const/4 v8, 0x0 + + goto :goto_2c + + :cond_2a + const/high16 v8, 0x3f800000 # 1.0f + + :goto_2c + iget-boolean v9, v0, LSmali;->b21:Z + + if-eqz v9, :cond_32 + + const/4 v9, 0x0 + + goto :goto_34 + + :cond_32 + const/high16 v9, 0x3f800000 # 1.0f + + :goto_34 + iget-boolean v10, v0, LSmali;->b15:Z + + if-eqz v10, :cond_3a + + const/4 v10, 0x0 + + goto :goto_3c + + :cond_3a + const/high16 v10, 0x3f800000 # 1.0f + + :goto_3c + iget-boolean v11, v0, LSmali;->b00:Z + + if-eqz v11, :cond_42 + + const/4 v11, 0x0 + + goto :goto_44 + + :cond_42 + const/high16 v11, 0x3f800000 # 1.0f + + :goto_44 + iget-boolean v12, v0, LSmali;->b22:Z + + if-eqz v12, :cond_4a + + const/4 v12, 0x0 + + goto :goto_4c + + :cond_4a + const/high16 v12, 0x3f800000 # 1.0f + + :goto_4c + iget-boolean v13, v0, LSmali;->b23:Z + + if-eqz v13, :cond_52 + + const/4 v13, 0x0 + + goto :goto_54 + + :cond_52 + const/high16 v13, 0x3f800000 # 1.0f + + :goto_54 + iget-boolean v14, v0, LSmali;->b24:Z + + if-eqz v14, :cond_5a + + const/4 v14, 0x0 + + goto :goto_5c + + :cond_5a + const/high16 v14, 0x3f800000 # 1.0f + + :goto_5c + iget-boolean v15, v0, LSmali;->b25:Z + + if-eqz v15, :cond_62 + + const/4 v15, 0x0 + + goto :goto_64 + + :cond_62 + const/high16 v15, 0x3f800000 # 1.0f + + :goto_64 + iget-boolean v3, v0, LSmali;->b26:Z + + if-eqz v3, :cond_6a + + const/4 v3, 0x0 + + goto :goto_6c + + :cond_6a + const/high16 v3, 0x3f800000 # 1.0f + + :goto_6c + iget-boolean v4, v0, LSmali;->b27:Z + + if-eqz v4, :cond_72 + + const/4 v4, 0x0 + + goto :goto_74 + + :cond_72 + const/high16 v4, 0x3f800000 # 1.0f + + :goto_74 + move-object/from16 v18, v1 + + iget-boolean v1, v0, LSmali;->b29:Z + + if-eqz v1, :cond_7c + + const/4 v1, 0x0 + + goto :goto_7e + + :cond_7c + const/high16 v1, 0x3f800000 # 1.0f + + :goto_7e + move/from16 v19, v1 + + iget-boolean v1, v0, LSmali;->b28:Z + + if-eqz v1, :cond_86 + + const/4 v1, 0x0 + + goto :goto_88 + + :cond_86 + const/high16 v1, 0x3f800000 # 1.0f + + :goto_88 + move/from16 v20, v1 + + iget-boolean v1, v0, LSmali;->b01:Z + + if-eqz v1, :cond_90 + + const/4 v1, 0x0 + + goto :goto_92 + + :cond_90 + const/high16 v1, 0x3f800000 # 1.0f + + :goto_92 + move/from16 v21, v11 + + iget-boolean v11, v0, LSmali;->b02:Z + + if-eqz v11, :cond_9a + + const/4 v11, 0x0 + + goto :goto_9c + + :cond_9a + const/high16 v11, 0x3f800000 # 1.0f + + :goto_9c + move/from16 v22, v12 + + iget-boolean v12, v0, LSmali;->b03:Z + + if-eqz v12, :cond_a4 + + const/4 v12, 0x0 + + goto :goto_a6 + + :cond_a4 + const/high16 v12, 0x3f800000 # 1.0f + + :goto_a6 + move/from16 v23, v4 + + iget-boolean v4, v0, LSmali;->b04:Z + + if-eqz v4, :cond_ae + + const/4 v4, 0x0 + + goto :goto_b0 + + :cond_ae + const/high16 v4, 0x3f800000 # 1.0f + + :goto_b0 + move/from16 v24, v3 + + iget-boolean v3, v0, LSmali;->b05:Z + + if-eqz v3, :cond_b8 + + const/4 v3, 0x0 + + goto :goto_ba + + :cond_b8 + const/high16 v3, 0x3f800000 # 1.0f + + :goto_ba + move/from16 v25, v15 + + iget-boolean v15, v0, LSmali;->b07:Z + + if-eqz v15, :cond_c2 + + const/4 v15, 0x0 + + goto :goto_c4 + + :cond_c2 + const/high16 v15, 0x3f800000 # 1.0f + + :goto_c4 + move/from16 v26, v15 + + iget-boolean v15, v0, LSmali;->b06:Z + + if-eqz v15, :cond_cc + + const/4 v15, 0x0 + + goto :goto_ce + + :cond_cc + const/high16 v15, 0x3f800000 # 1.0f + + :goto_ce + move/from16 v27, v15 + + iget-boolean v15, v0, LSmali;->b30:Z + + if-eqz v15, :cond_d6 + + const/4 v15, 0x0 + + goto :goto_d8 + + :cond_d6 + const/high16 v15, 0x3f800000 # 1.0f + + :goto_d8 + move/from16 v28, v14 + + iget-boolean v14, v0, LSmali;->b31:Z + + if-eqz v14, :cond_e0 + + const/4 v14, 0x0 + + goto :goto_e2 + + :cond_e0 + const/high16 v14, 0x3f800000 # 1.0f + + :goto_e2 + move/from16 v29, v13 + + iget-boolean v13, v0, LSmali;->b32:Z + + if-eqz v13, :cond_ea + + const/4 v13, 0x0 + + goto :goto_ec + + :cond_ea + const/high16 v13, 0x3f800000 # 1.0f + + :goto_ec + move/from16 v30, v3 + + iget-boolean v3, v0, LSmali;->b33:Z + + if-eqz v3, :cond_f4 + + const/4 v3, 0x0 + + goto :goto_f6 + + :cond_f4 + const/high16 v3, 0x3f800000 # 1.0f + + :goto_f6 + move/from16 v31, v4 + + iget-boolean v4, v0, LSmali;->b34:Z + + if-eqz v4, :cond_fe + + const/4 v4, 0x0 + + goto :goto_100 + + :cond_fe + const/high16 v4, 0x3f800000 # 1.0f + + :goto_100 + move/from16 v32, v12 + + iget-boolean v12, v0, LSmali;->b36:Z + + if-eqz v12, :cond_108 + + const/4 v12, 0x0 + + goto :goto_10a + + :cond_108 + const/high16 v12, 0x3f800000 # 1.0f + + :goto_10a + move/from16 v33, v12 + + iget-boolean v12, v0, LSmali;->b35:Z + + if-eqz v12, :cond_112 + + const/4 v12, 0x0 + + goto :goto_114 + + :cond_112 + const/high16 v12, 0x3f800000 # 1.0f + + :goto_114 + move/from16 v34, v12 + + iget-boolean v12, v0, LSmali;->b08:Z + + if-eqz v12, :cond_11c + + const/4 v12, 0x0 + + goto :goto_11e + + :cond_11c + const/high16 v12, 0x3f800000 # 1.0f + + :goto_11e + move/from16 v35, v11 + + iget-boolean v11, v0, LSmali;->b09:Z + + if-eqz v11, :cond_126 + + const/4 v11, 0x0 + + goto :goto_128 + + :cond_126 + const/high16 v11, 0x3f800000 # 1.0f + + :goto_128 + move/from16 v36, v1 + + iget-boolean v1, v0, LSmali;->b10:Z + + if-eqz v1, :cond_130 + + const/4 v1, 0x0 + + goto :goto_132 + + :cond_130 + const/high16 v1, 0x3f800000 # 1.0f + + :goto_132 + move/from16 v37, v4 + + iget-boolean v4, v0, LSmali;->b11:Z + + if-eqz v4, :cond_13a + + const/4 v4, 0x0 + + goto :goto_13c + + :cond_13a + const/high16 v4, 0x3f800000 # 1.0f + + :goto_13c + move/from16 v38, v3 + + iget-boolean v3, v0, LSmali;->b12:Z + + if-eqz v3, :cond_144 + + const/4 v3, 0x0 + + goto :goto_146 + + :cond_144 + const/high16 v3, 0x3f800000 # 1.0f + + :goto_146 + move/from16 v39, v13 + + iget-boolean v13, v0, LSmali;->b14:Z + + if-eqz v13, :cond_14e + + const/4 v13, 0x0 + + goto :goto_150 + + :cond_14e + const/high16 v13, 0x3f800000 # 1.0f + + :goto_150 + move/from16 v40, v13 + + iget-boolean v13, v0, LSmali;->b13:Z + + if-eqz v13, :cond_159 + + const/16 v16, 0x0 + + goto :goto_15b + + :cond_159 + const/high16 v16, 0x3f800000 # 1.0f + + :goto_15b + move/from16 v13, v16 + + move/from16 v41, v13 + + iget-boolean v13, v0, LSmali;->conditionA:Z + + if-eqz v13, :cond_1a2 + + const/high16 v13, 0x447a0000 # 1000.0f + + div-float/2addr v5, v13 + + div-float/2addr v2, v13 + + div-float/2addr v6, v13 + + div-float/2addr v7, v13 + + div-float/2addr v8, v13 + + div-float/2addr v9, v13 + + div-float/2addr v10, v13 + + div-float/2addr v12, v13 + + div-float/2addr v11, v13 + + div-float/2addr v1, v13 + + div-float/2addr v4, v13 + + div-float/2addr v3, v13 + + div-float/2addr v15, v13 + + div-float/2addr v14, v13 + + div-float v16, v39, v13 + + div-float v38, v38, v13 + + div-float v37, v37, v13 + + div-float v36, v36, v13 + + div-float v35, v35, v13 + + div-float v32, v32, v13 + + div-float v31, v31, v13 + + div-float v30, v30, v13 + + div-float v29, v29, v13 + + div-float v28, v28, v13 + + div-float v25, v25, v13 + + div-float v24, v24, v13 + + div-float v23, v23, v13 + + div-float v22, v22, v13 + + div-float v21, v21, v13 + + div-float v39, v40, v13 + + div-float v40, v41, v13 + + div-float v33, v33, v13 + + div-float v34, v34, v13 + + div-float v26, v26, v13 + + div-float v27, v27, v13 + + div-float v19, v19, v13 + + div-float v13, v20, v13 + + goto :goto_1aa + + :cond_1a2 + move/from16 v13, v20 + + move/from16 v16, v39 + + move/from16 v39, v40 + + move/from16 v40, v41 + + :goto_1aa + move/from16 v42, v13 + + iget-boolean v13, v0, LSmali;->conditionB:Z + + const/high16 v20, 0x42c80000 # 100.0f + + if-eqz v13, :cond_1fd + + div-float v5, v5, v20 + + div-float v2, v2, v20 + + div-float v6, v6, v20 + + div-float v7, v7, v20 + + div-float v8, v8, v20 + + div-float v9, v9, v20 + + div-float v10, v10, v20 + + div-float v12, v12, v20 + + div-float v11, v11, v20 + + div-float v1, v1, v20 + + div-float v4, v4, v20 + + div-float v3, v3, v20 + + div-float v15, v15, v20 + + div-float v14, v14, v20 + + div-float v16, v16, v20 + + div-float v38, v38, v20 + + div-float v37, v37, v20 + + div-float v36, v36, v20 + + div-float v35, v35, v20 + + div-float v32, v32, v20 + + div-float v31, v31, v20 + + div-float v30, v30, v20 + + div-float v29, v29, v20 + + div-float v28, v28, v20 + + div-float v25, v25, v20 + + div-float v24, v24, v20 + + div-float v23, v23, v20 + + div-float v22, v22, v20 + + div-float v21, v21, v20 + + div-float v39, v39, v20 + + div-float v40, v40, v20 + + div-float v33, v33, v20 + + div-float v34, v34, v20 + + div-float v26, v26, v20 + + div-float v27, v27, v20 + + div-float v19, v19, v20 + + div-float v13, v42, v20 + + goto :goto_1ff + + :cond_1fd + move/from16 v13, v42 + + :goto_1ff + move/from16 v43, v13 + + iget-boolean v13, v0, LSmali;->conditionC:Z + + if-eqz v13, :cond_244 + + const/high16 v13, 0x41400000 # 12.0f + + div-float/2addr v5, v13 + + div-float/2addr v2, v13 + + div-float/2addr v6, v13 + + div-float/2addr v7, v13 + + div-float/2addr v8, v13 + + div-float/2addr v9, v13 + + div-float/2addr v10, v13 + + div-float/2addr v12, v13 + + div-float/2addr v11, v13 + + div-float/2addr v1, v13 + + div-float/2addr v4, v13 + + div-float/2addr v3, v13 + + div-float/2addr v15, v13 + + div-float/2addr v14, v13 + + div-float v16, v16, v13 + + div-float v38, v38, v13 + + div-float v37, v37, v13 + + div-float v36, v36, v13 + + div-float v35, v35, v13 + + div-float v32, v32, v13 + + div-float v31, v31, v13 + + div-float v30, v30, v13 + + div-float v29, v29, v13 + + div-float v28, v28, v13 + + div-float v25, v25, v13 + + div-float v24, v24, v13 + + div-float v23, v23, v13 + + div-float v22, v22, v13 + + div-float v21, v21, v13 + + div-float v39, v39, v13 + + div-float v40, v40, v13 + + div-float v33, v33, v13 + + div-float v34, v34, v13 + + div-float v26, v26, v13 + + div-float v27, v27, v13 + + div-float v19, v19, v13 + + div-float v13, v43, v13 + + goto :goto_246 + + :cond_244 + move/from16 v13, v43 + + :goto_246 + const/16 v17, 0x0 + + mul-float v0, v20, v17 + + invoke-static {v0}, Ljava/lang/Math;->round(F)I + + move-result v0 + + int-to-float v0, v0 + + div-float v0, v0, v20 + + move/from16 v44, v1 + + new-instance v1, Ljava/lang/StringBuilder; + + invoke-direct {v1}, Ljava/lang/StringBuilder;-><init>()V + + invoke-virtual {v1, v0}, Ljava/lang/StringBuilder;->append(F)Ljava/lang/StringBuilder; + + move/from16 v45, v0 + + move-object/from16 v0, v18 + + invoke-virtual {v1, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; + + invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; + + move-result-object v1 + + return-void +.end method diff --git a/test/626-checker-arm64-scratch-register/src/Main.java b/test/626-checker-arm64-scratch-register/src/Main.java index 139491769e..cf94b87666 100644 --- a/test/626-checker-arm64-scratch-register/src/Main.java +++ b/test/626-checker-arm64-scratch-register/src/Main.java @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; public class Main { @@ -63,14 +65,17 @@ public class Main { /// CHECK: name "B0" /// CHECK: <<This:l\d+>> ParameterValue /// CHECK: end_block + /// CHECK: begin_block - /// CHECK: successors "<<ThenBlock:B\d+>>" "<<ElseBlock:B\d+>>" + /// CHECK: successors "<<ThenBlockA:B\d+>>" "<<ElseBlockA:B\d+>>" + /// CHECK: <<CondA:z\d+>> InstanceFieldGet [<<This>>] field_name:Main.conditionA + /// CHECK: If [<<CondA>>] + /// CHECK: end_block + + /// CHECK: begin_block + /// CHECK: successors "<<ThenBlockB:B\d+>>" "<<ElseBlockB:B\d+>>" /// CHECK: <<CondB:z\d+>> InstanceFieldGet [<<This>>] field_name:Main.conditionB /// CHECK: If [<<CondB>>] - /// CHECK: end_block - /// CHECK: begin_block - /// CHECK: name "<<ElseBlock>>" - /// CHECK: ParallelMove moves:[40(sp)->d0,24(sp)->32(sp),28(sp)->36(sp),d0->d3,d3->d4,d2->d5,d4->d6,d5->d7,d6->d18,d7->d19,d18->d20,d19->d21,d20->d22,d21->d23,d22->d10,d23->d11,16(sp)->24(sp),20(sp)->28(sp),d10->d14,d11->d12,d12->d13,d13->d1,d14->d2,32(sp)->16(sp),36(sp)->20(sp)] /// CHECK: end_block /// CHECK-START-ARM64: void Main.test() disassembly (after) @@ -82,39 +87,6 @@ public class Main { /// CHECK: successors "<<ThenBlock:B\d+>>" "<<ElseBlock:B\d+>>" /// CHECK: <<CondB:z\d+>> InstanceFieldGet [<<This>>] field_name:Main.conditionB /// CHECK: If [<<CondB>>] - /// CHECK: end_block - /// CHECK: begin_block - /// CHECK: name "<<ElseBlock>>" - /// CHECK: ParallelMove moves:[invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid,invalid->invalid] - /// CHECK: fmov d31, d2 - /// CHECK: ldr s2, [sp, #36] - /// CHECK: ldr w16, [sp, #16] - /// CHECK: str w16, [sp, #36] - /// CHECK: str s14, [sp, #16] - /// CHECK: ldr s14, [sp, #28] - /// CHECK: str s1, [sp, #28] - /// CHECK: ldr s1, [sp, #32] - /// CHECK: str s31, [sp, #32] - /// CHECK: ldr s31, [sp, #20] - /// CHECK: str s31, [sp, #40] - /// CHECK: str s12, [sp, #20] - /// CHECK: fmov d12, d11 - /// CHECK: fmov d11, d10 - /// CHECK: fmov d10, d23 - /// CHECK: fmov d23, d22 - /// CHECK: fmov d22, d21 - /// CHECK: fmov d21, d20 - /// CHECK: fmov d20, d19 - /// CHECK: fmov d19, d18 - /// CHECK: fmov d18, d7 - /// CHECK: fmov d7, d6 - /// CHECK: fmov d6, d5 - /// CHECK: fmov d5, d4 - /// CHECK: fmov d4, d3 - /// CHECK: fmov d3, d13 - /// CHECK: ldr s13, [sp, #24] - /// CHECK: str s3, [sp, #24] - /// CHECK: ldr s3, pc+{{\d+}} (addr {{0x[0-9a-f]+}}) (100) /// CHECK: end_block public void test() { @@ -289,9 +261,20 @@ public class Main { String res = s + r; } - public static void main(String[] args) { + public static void main(String[] args) throws Exception { Main main = new Main(); main.test(); + + Class<?> cl = Class.forName("Smali"); + Constructor<?> cons = cl.getConstructor(); + Object o = cons.newInstance(); + + Method test = cl.getMethod("test"); + test.invoke(o); + + Method testD8 = cl.getMethod("testD8"); + testD8.invoke(o); + System.out.println("passed"); } } diff --git a/test/983-source-transform-verify/source_transform.cc b/test/983-source-transform-verify/source_transform.cc index e9cb35e944..c076d1521f 100644 --- a/test/983-source-transform-verify/source_transform.cc +++ b/test/983-source-transform-verify/source_transform.cc @@ -28,6 +28,7 @@ #include "base/macros.h" #include "bytecode_utils.h" #include "dex/code_item_accessors-inl.h" +#include "dex/art_dex_file_loader.h" #include "dex/dex_file.h" #include "dex/dex_file_loader.h" #include "dex/dex_instruction.h" @@ -66,15 +67,16 @@ void JNICALL CheckDexFileHook(jvmtiEnv* jvmti_env ATTRIBUTE_UNUSED, if (IsJVM()) { return; } + const ArtDexFileLoader dex_file_loader; std::string error; - std::unique_ptr<const DexFile> dex(DexFileLoader::Open(class_data, - class_data_len, - "fake_location.dex", - /*location_checksum*/ 0, - /*oat_dex_file*/ nullptr, - /*verify*/ true, - /*verify_checksum*/ true, - &error)); + std::unique_ptr<const DexFile> dex(dex_file_loader.Open(class_data, + class_data_len, + "fake_location.dex", + /*location_checksum*/ 0, + /*oat_dex_file*/ nullptr, + /*verify*/ true, + /*verify_checksum*/ true, + &error)); if (dex.get() == nullptr) { std::cout << "Failed to verify dex file for " << name << " because " << error << std::endl; return; diff --git a/test/knownfailures.json b/test/knownfailures.json index a12510c9dc..9db8e9df0a 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -442,6 +442,7 @@ "957-methodhandle-transforms", "958-methodhandle-stackframe", "959-invoke-polymorphic-accessors", + "979-const-method-handle", "990-method-handle-and-mr" ], "description": [ @@ -647,7 +648,7 @@ }, { "tests": "661-oat-writer-layout", - "variant": "interp-ac | interpreter | jit | no-dex2oat | no-prebuild | no-image | trace", + "variant": "interp-ac | interpreter | jit | no-dex2oat | no-prebuild | no-image | trace | redefine-stress | jvmti-stress", "description": ["Test is designed to only check --compiler-filter=speed"] } diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py index 297ce08bee..2bb407db58 100644 --- a/test/testrunner/target_config.py +++ b/test/testrunner/target_config.py @@ -234,8 +234,8 @@ target_config = { 'env' : { 'ART_USE_READ_BARRIER' : 'false', 'ART_HEAP_POISONING' : 'true', - # Get some extra automated testing coverage for compact dex. - 'ART_DEFAULT_COMPACT_DEX_LEVEL' : 'fast' + # Disable compact dex to get coverage of standard dex file usage. + 'ART_DEFAULT_COMPACT_DEX_LEVEL' : 'none' } }, 'art-preopt' : { @@ -280,8 +280,8 @@ target_config = { 'env': { 'ART_DEFAULT_GC_TYPE' : 'SS', 'ART_USE_READ_BARRIER' : 'false', - # Get some extra automated testing coverage for compact dex. - 'ART_DEFAULT_COMPACT_DEX_LEVEL' : 'fast' + # Disable compact dex to get coverage of standard dex file usage. + 'ART_DEFAULT_COMPACT_DEX_LEVEL' : 'none' } }, 'art-gtest-gss-gc': { |