diff options
| author | 2016-11-18 13:14:10 +0000 | |
|---|---|---|
| committer | 2016-11-18 13:14:11 +0000 | |
| commit | 2dae2f256bce26690a29af498c50cc2d6440d15b (patch) | |
| tree | 5c9a44da1911d455310d88c78fa6871e2397a41d /compiler | |
| parent | 67eb3914d2555e9c191337a4fe42ecef419b0330 (diff) | |
| parent | 01b70e8bca5e824ee3958ffbdd7727334e81eb0a (diff) | |
Merge "Add dequickening support."
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/Android.bp | 2 | ||||
| -rw-r--r-- | compiler/dex/dex_to_dex_compiler.cc | 2 | ||||
| -rw-r--r-- | compiler/dex/dex_to_dex_compiler.h | 2 | ||||
| -rw-r--r-- | compiler/dex/dex_to_dex_decompiler.cc | 198 | ||||
| -rw-r--r-- | compiler/dex/dex_to_dex_decompiler.h | 38 | ||||
| -rw-r--r-- | compiler/dex/dex_to_dex_decompiler_test.cc | 136 | ||||
| -rw-r--r-- | compiler/driver/compiler_driver.h | 1 | ||||
| -rw-r--r-- | compiler/driver/compiler_options.h | 1 | ||||
| -rw-r--r-- | compiler/optimizing/instruction_builder.cc | 5 |
9 files changed, 382 insertions, 3 deletions
diff --git a/compiler/Android.bp b/compiler/Android.bp index 6edb639a1a..e2a450d171 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -28,6 +28,7 @@ art_cc_defaults { "compiled_method.cc", "debug/elf_debug_writer.cc", "dex/dex_to_dex_compiler.cc", + "dex/dex_to_dex_decompiler.cc", "dex/verified_method.cc", "dex/verification_results.cc", "dex/quick_compiler_callbacks.cc", @@ -312,6 +313,7 @@ art_cc_test { srcs: [ "compiled_method_test.cc", "debug/dwarf/dwarf_test.cc", + "dex/dex_to_dex_decompiler_test.cc", "driver/compiled_method_storage_test.cc", "driver/compiler_driver_test.cc", "elf_writer_test.cc", diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index 9c1d72b9be..cf69f469a0 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -233,6 +233,8 @@ Instruction* DexCompiler::CompileCheckCast(Instruction* inst, uint32_t dex_pc) { << " by replacing it with 2 NOPs at dex pc " << StringPrintf("0x%x", dex_pc) << " in method " << GetDexFile().PrettyMethod(unit_.GetDexMethodIndex(), true); + quickened_info_.push_back(QuickenedInfo(dex_pc, inst->VRegA_21c())); + quickened_info_.push_back(QuickenedInfo(dex_pc, inst->VRegB_21c())); // We are modifying 4 consecutive bytes. inst->SetOpcode(Instruction::NOP); inst->SetVRegA_10x(0u); // keep compliant with verifier. diff --git a/compiler/dex/dex_to_dex_compiler.h b/compiler/dex/dex_to_dex_compiler.h index 3fad6d4c95..0a00d45297 100644 --- a/compiler/dex/dex_to_dex_compiler.h +++ b/compiler/dex/dex_to_dex_compiler.h @@ -17,8 +17,6 @@ #ifndef ART_COMPILER_DEX_DEX_TO_DEX_COMPILER_H_ #define ART_COMPILER_DEX_DEX_TO_DEX_COMPILER_H_ -#include "jni.h" - #include "dex_file.h" #include "invoke_type.h" diff --git a/compiler/dex/dex_to_dex_decompiler.cc b/compiler/dex/dex_to_dex_decompiler.cc new file mode 100644 index 0000000000..051125eeaa --- /dev/null +++ b/compiler/dex/dex_to_dex_decompiler.cc @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "dex_to_dex_decompiler.h" + +#include "base/logging.h" +#include "base/mutex.h" +#include "dex_file-inl.h" +#include "dex_instruction-inl.h" +#include "optimizing/bytecode_utils.h" + +namespace art { +namespace optimizer { + +class DexDecompiler { + public: + DexDecompiler(const DexFile::CodeItem& code_item, const ArrayRef<const uint8_t>& quickened_info) + : code_item_(code_item), + quickened_info_ptr_(quickened_info.data()), + quickened_info_end_(quickened_info.data() + quickened_info.size()) {} + + bool Decompile(); + + private: + void DecompileInstanceFieldAccess(Instruction* inst, + uint32_t dex_pc, + Instruction::Code new_opcode) { + uint16_t index = GetIndexAt(dex_pc); + inst->SetOpcode(new_opcode); + inst->SetVRegC_22c(index); + } + + void DecompileInvokeVirtual(Instruction* inst, + uint32_t dex_pc, + Instruction::Code new_opcode, + bool is_range) { + uint16_t index = GetIndexAt(dex_pc); + inst->SetOpcode(new_opcode); + if (is_range) { + inst->SetVRegB_3rc(index); + } else { + inst->SetVRegB_35c(index); + } + } + + void DecompileNop(Instruction* inst, uint32_t dex_pc) { + if (quickened_info_ptr_ == quickened_info_end_) { + return; + } + const uint8_t* temporary_pointer = quickened_info_ptr_; + uint32_t quickened_pc = DecodeUnsignedLeb128(&temporary_pointer); + if (quickened_pc != dex_pc) { + return; + } + uint16_t reference_index = GetIndexAt(dex_pc); + uint16_t type_index = GetIndexAt(dex_pc); + inst->SetOpcode(Instruction::CHECK_CAST); + inst->SetVRegA_21c(reference_index); + inst->SetVRegB_21c(type_index); + } + + uint16_t GetIndexAt(uint32_t dex_pc) { + // Note that as a side effect, DecodeUnsignedLeb128 update the given pointer + // to the new position in the buffer. + DCHECK_LT(quickened_info_ptr_, quickened_info_end_); + uint32_t quickened_pc = DecodeUnsignedLeb128(&quickened_info_ptr_); + DCHECK_LT(quickened_info_ptr_, quickened_info_end_); + uint16_t index = DecodeUnsignedLeb128(&quickened_info_ptr_); + DCHECK_LE(quickened_info_ptr_, quickened_info_end_); + DCHECK_EQ(quickened_pc, dex_pc); + return index; + } + + const DexFile::CodeItem& code_item_; + const uint8_t* quickened_info_ptr_; + const uint8_t* const quickened_info_end_; + + DISALLOW_COPY_AND_ASSIGN(DexDecompiler); +}; + +bool DexDecompiler::Decompile() { + // We need to iterate over the code item, and not over the quickening data, + // because the RETURN_VOID quickening is not encoded in the quickening data. Because + // unquickening is a rare need and not performance sensitive, it is not worth the + // added storage to also add the RETURN_VOID quickening in the quickened data. + for (CodeItemIterator it(code_item_); !it.Done(); it.Advance()) { + uint32_t dex_pc = it.CurrentDexPc(); + Instruction* inst = const_cast<Instruction*>(&it.CurrentInstruction()); + + switch (inst->Opcode()) { + case Instruction::RETURN_VOID_NO_BARRIER: + inst->SetOpcode(Instruction::RETURN_VOID); + break; + + case Instruction::NOP: + DecompileNop(inst, dex_pc); + break; + + case Instruction::IGET_QUICK: + DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET); + break; + + case Instruction::IGET_WIDE_QUICK: + DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_WIDE); + break; + + case Instruction::IGET_OBJECT_QUICK: + DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_OBJECT); + break; + + case Instruction::IGET_BOOLEAN_QUICK: + DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_BOOLEAN); + break; + + case Instruction::IGET_BYTE_QUICK: + DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_BYTE); + break; + + case Instruction::IGET_CHAR_QUICK: + DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_CHAR); + break; + + case Instruction::IGET_SHORT_QUICK: + DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_SHORT); + break; + + case Instruction::IPUT_QUICK: + DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT); + break; + + case Instruction::IPUT_BOOLEAN_QUICK: + DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_BOOLEAN); + break; + + case Instruction::IPUT_BYTE_QUICK: + DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_BYTE); + break; + + case Instruction::IPUT_CHAR_QUICK: + DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_CHAR); + break; + + case Instruction::IPUT_SHORT_QUICK: + DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_SHORT); + break; + + case Instruction::IPUT_WIDE_QUICK: + DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_WIDE); + break; + + case Instruction::IPUT_OBJECT_QUICK: + DecompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_OBJECT); + break; + + case Instruction::INVOKE_VIRTUAL_QUICK: + DecompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL, false); + break; + + case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: + DecompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_RANGE, true); + break; + + default: + break; + } + } + + if (quickened_info_ptr_ != quickened_info_end_) { + LOG(ERROR) << "Failed to use all values in quickening info." + << " Actual: " << std::hex << quickened_info_ptr_ + << " Expected: " << quickened_info_end_; + return false; + } + + return true; +} + +bool ArtDecompileDEX(const DexFile::CodeItem& code_item, + const ArrayRef<const uint8_t>& quickened_info) { + DexDecompiler decompiler(code_item, quickened_info); + return decompiler.Decompile(); +} + +} // namespace optimizer +} // namespace art diff --git a/compiler/dex/dex_to_dex_decompiler.h b/compiler/dex/dex_to_dex_decompiler.h new file mode 100644 index 0000000000..5502ca2d92 --- /dev/null +++ b/compiler/dex/dex_to_dex_decompiler.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_DEX_DEX_TO_DEX_DECOMPILER_H_ +#define ART_COMPILER_DEX_DEX_TO_DEX_DECOMPILER_H_ + +#include "base/array_ref.h" +#include "dex_file.h" + +namespace art { +namespace optimizer { + +// "Decompile", that is unquicken, the code item provided, given the +// associated quickening data. +// TODO: code_item isn't really a const element, but changing it +// to non-const has too many repercussions on the code base. We make it +// consistent with DexToDexCompiler, but we should really change it to +// DexFile::CodeItem*. +bool ArtDecompileDEX(const DexFile::CodeItem& code_item, + const ArrayRef<const uint8_t>& quickened_data); + +} // namespace optimizer +} // namespace art + +#endif // ART_COMPILER_DEX_DEX_TO_DEX_DECOMPILER_H_ diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc new file mode 100644 index 0000000000..ea6c7a2e09 --- /dev/null +++ b/compiler/dex/dex_to_dex_decompiler_test.cc @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "dex/dex_to_dex_decompiler.h" + +#include "class_linker.h" +#include "compiler/common_compiler_test.h" +#include "compiler/compiled_method.h" +#include "compiler/driver/compiler_options.h" +#include "compiler/driver/compiler_driver.h" +#include "compiler_callbacks.h" +#include "dex_file.h" +#include "handle_scope-inl.h" +#include "verifier/method_verifier-inl.h" +#include "mirror/class_loader.h" +#include "runtime.h" +#include "thread.h" +#include "scoped_thread_state_change-inl.h" + +namespace art { + +class DexToDexDecompilerTest : public CommonCompilerTest { + public: + void CompileAll(jobject class_loader) REQUIRES(!Locks::mutator_lock_) { + TimingLogger timings("CompilerDriverTest::CompileAll", false, false); + TimingLogger::ScopedTiming t(__FUNCTION__, &timings); + compiler_options_->boot_image_ = false; + compiler_options_->SetCompilerFilter(CompilerFilter::kInterpretOnly); + compiler_driver_->CompileAll(class_loader, + GetDexFiles(class_loader), + /* verifier_deps */ nullptr, + &timings); + } + + void RunTest(const char* dex_name) { + Thread* self = Thread::Current(); + // First load the original dex file. + jobject original_class_loader; + { + ScopedObjectAccess soa(self); + original_class_loader = LoadDex(dex_name); + } + const DexFile* original_dex_file = GetDexFiles(original_class_loader)[0]; + + // Load the dex file again and make it writable to quicken them. + jobject class_loader; + const DexFile* updated_dex_file = nullptr; + { + ScopedObjectAccess soa(self); + class_loader = LoadDex(dex_name); + updated_dex_file = GetDexFiles(class_loader)[0]; + Runtime::Current()->GetClassLinker()->RegisterDexFile( + *updated_dex_file, soa.Decode<mirror::ClassLoader>(class_loader).Ptr()); + } + // The dex files should be identical. + int cmp = memcmp(original_dex_file->Begin(), + updated_dex_file->Begin(), + updated_dex_file->Size()); + ASSERT_EQ(0, cmp); + + updated_dex_file->EnableWrite(); + CompileAll(class_loader); + // The dex files should be different after quickening. + cmp = memcmp(original_dex_file->Begin(), updated_dex_file->Begin(), updated_dex_file->Size()); + ASSERT_NE(0, cmp); + + // Unquicken the dex file. + for (uint32_t i = 0; i < updated_dex_file->NumClassDefs(); ++i) { + const DexFile::ClassDef& class_def = updated_dex_file->GetClassDef(i); + const uint8_t* class_data = updated_dex_file->GetClassData(class_def); + if (class_data == nullptr) { + continue; + } + ClassDataItemIterator it(*updated_dex_file, class_data); + // Skip fields + while (it.HasNextStaticField()) { + it.Next(); + } + while (it.HasNextInstanceField()) { + it.Next(); + } + + // Unquicken each method. + while (it.HasNextDirectMethod()) { + uint32_t method_idx = it.GetMemberIndex(); + CompiledMethod* compiled_method = + compiler_driver_->GetCompiledMethod(MethodReference(updated_dex_file, method_idx)); + ArrayRef<const uint8_t> table; + if (compiled_method != nullptr) { + table = compiled_method->GetVmapTable(); + } + optimizer::ArtDecompileDEX(*it.GetMethodCodeItem(), table); + it.Next(); + } + while (it.HasNextVirtualMethod()) { + uint32_t method_idx = it.GetMemberIndex(); + CompiledMethod* compiled_method = + compiler_driver_->GetCompiledMethod(MethodReference(updated_dex_file, method_idx)); + ArrayRef<const uint8_t> table; + if (compiled_method != nullptr) { + table = compiled_method->GetVmapTable(); + } + optimizer::ArtDecompileDEX(*it.GetMethodCodeItem(), table); + it.Next(); + } + DCHECK(!it.HasNext()); + } + + // Make sure after unquickening we go back to the same contents as the original dex file. + cmp = memcmp(original_dex_file->Begin(), updated_dex_file->Begin(), updated_dex_file->Size()); + ASSERT_EQ(0, cmp); + } +}; + +TEST_F(DexToDexDecompilerTest, VerifierDeps) { + RunTest("VerifierDeps"); +} + +TEST_F(DexToDexDecompilerTest, DexToDexDecompiler) { + RunTest("DexToDexDecompiler"); +} + +} // namespace art diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index c8d6cb0d4d..1bd354658a 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -584,6 +584,7 @@ class CompilerDriver { const BitVector* current_dex_to_dex_methods_; friend class CompileClassVisitor; + friend class DexToDexDecompilerTest; friend class verifier::VerifierDepsTest; DISALLOW_COPY_AND_ASSIGN(CompilerDriver); }; diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index 56b632d6b4..9c62f80339 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -341,6 +341,7 @@ class CompilerOptions FINAL { const std::vector<std::string>* passes_to_run_; friend class Dex2Oat; + friend class DexToDexDecompilerTest; friend class CommonCompilerTest; friend class verifier::VerifierDepsTest; diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index c8c4ca76fd..b44137d138 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -1723,7 +1723,10 @@ uint16_t HInstructionBuilder::LookupQuickenedInfo(uint32_t dex_pc) { if (dex_pc_in_map == dex_pc) { return value_in_map; } else { - skipped_interpreter_metadata_.Put(dex_pc_in_map, value_in_map); + // Overwrite and not Put, as quickened CHECK-CAST has two entries with + // the same dex_pc. This is OK, because the compiler does not care about those + // entries. + skipped_interpreter_metadata_.Overwrite(dex_pc_in_map, value_in_map); } } } |