Add dequickening support.
Both vdex and BCI require full dequickening support. This
change implements dequickening by using the existing encoded
quickening, and adds the checkcast quickening to that encoding.
bug: 30937355
bug: 32369913
Test: dex_to_dex_decompiler_test.cc test-art-host
Change-Id: Ie95f46946d59b28157d6e47dcf4a859be032d1c3
diff --git a/compiler/dex/dex_to_dex_decompiler.cc b/compiler/dex/dex_to_dex_decompiler.cc
new file mode 100644
index 0000000..051125e
--- /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