Add "kind" argument to Get/SetVReg.
In order to determine where a register is promoted its necessary to know
the kind of use of the register.
Extend notion of precise-ness to numeric verifier register types.
Dump verifier output in oatdump.
Dump vregs with their location or constant value.
Introduce indenting ostream utility.
Change-Id: Ia3d29497877976bc24465484743bca08236e1768
diff --git a/build/Android.common.mk b/build/Android.common.mk
index d200874..d86a785 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -366,6 +366,7 @@
src/gtest_test.cc \
src/heap_test.cc \
src/image_test.cc \
+ src/indenter_test.cc \
src/indirect_reference_table_test.cc \
src/intern_table_test.cc \
src/jni_internal_test.cc \
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 689b928..9a354e1 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -683,6 +683,11 @@
void ClassLinker::RegisterOatFileLocked(const OatFile& oat_file) {
dex_lock_.AssertHeld(Thread::Current());
+#ifndef NDEBUG
+ for (size_t i = 0; i < oat_files_.size(); ++i) {
+ CHECK_NE(&oat_file, oat_files_[i]) << oat_file.GetLocation();
+ }
+#endif
oat_files_.push_back(&oat_file);
}
@@ -955,7 +960,6 @@
if (oat_file == NULL) {
return NULL;
}
- CHECK(oat_file != NULL) << oat_location;
return oat_file;
}
@@ -1840,13 +1844,27 @@
DexCache* ClassLinker::FindDexCache(const DexFile& dex_file) const {
MutexLock mu(Thread::Current(), dex_lock_);
+ // Search assuming unique-ness of dex file.
for (size_t i = 0; i != dex_caches_.size(); ++i) {
DexCache* dex_cache = dex_caches_[i];
if (dex_cache->GetDexFile() == &dex_file) {
return dex_cache;
}
}
- LOG(FATAL) << "Failed to find DexCache for DexFile " << dex_file.GetLocation();
+ // Search matching by location name.
+ std::string location(dex_file.GetLocation());
+ for (size_t i = 0; i != dex_caches_.size(); ++i) {
+ DexCache* dex_cache = dex_caches_[i];
+ if (dex_cache->GetDexFile()->GetLocation() == location) {
+ return dex_cache;
+ }
+ }
+ // Failure, dump diagnostic and abort.
+ for (size_t i = 0; i != dex_caches_.size(); ++i) {
+ DexCache* dex_cache = dex_caches_[i];
+ LOG(ERROR) << "Registered dex file " << i << " = " << dex_cache->GetDexFile()->GetLocation();
+ }
+ LOG(FATAL) << "Failed to find DexCache for DexFile " << location;
return NULL;
}
diff --git a/src/debugger.cc b/src/debugger.cc
index 1d8b997..42c0c7f 100644
--- a/src/debugger.cc
+++ b/src/debugger.cc
@@ -25,9 +25,7 @@
#include "dex_instruction.h"
#include "gc/large_object_space.h"
#include "gc/space.h"
-#if !defined(ART_USE_LLVM_COMPILER)
-#include "oat/runtime/context.h" // For VmapTable
-#endif
+#include "oat/runtime/context.h"
#include "object_utils.h"
#include "safe_map.h"
#include "ScopedLocalRef.h"
@@ -1691,7 +1689,7 @@
this_object = NULL;
} else {
uint16_t reg = DemangleSlot(0, m);
- this_object = reinterpret_cast<Object*>(GetVReg(m, reg));
+ this_object = reinterpret_cast<Object*>(GetVReg(m, reg, kReferenceVReg));
}
return false;
}
@@ -1759,7 +1757,7 @@
case JDWP::JT_BOOLEAN:
{
CHECK_EQ(width_, 1U);
- uint32_t intVal = GetVReg(m, reg);
+ uint32_t intVal = GetVReg(m, reg, kIntVReg);
VLOG(jdwp) << "get boolean local " << reg << " = " << intVal;
JDWP::Set1(buf_+1, intVal != 0);
}
@@ -1767,7 +1765,7 @@
case JDWP::JT_BYTE:
{
CHECK_EQ(width_, 1U);
- uint32_t intVal = GetVReg(m, reg);
+ uint32_t intVal = GetVReg(m, reg, kIntVReg);
VLOG(jdwp) << "get byte local " << reg << " = " << intVal;
JDWP::Set1(buf_+1, intVal);
}
@@ -1776,16 +1774,23 @@
case JDWP::JT_CHAR:
{
CHECK_EQ(width_, 2U);
- uint32_t intVal = GetVReg(m, reg);
+ uint32_t intVal = GetVReg(m, reg, kIntVReg);
VLOG(jdwp) << "get short/char local " << reg << " = " << intVal;
JDWP::Set2BE(buf_+1, intVal);
}
break;
case JDWP::JT_INT:
+ {
+ CHECK_EQ(width_, 4U);
+ uint32_t intVal = GetVReg(m, reg, kIntVReg);
+ VLOG(jdwp) << "get int local " << reg << " = " << intVal;
+ JDWP::Set4BE(buf_+1, intVal);
+ }
+ break;
case JDWP::JT_FLOAT:
{
CHECK_EQ(width_, 4U);
- uint32_t intVal = GetVReg(m, reg);
+ uint32_t intVal = GetVReg(m, reg, kFloatVReg);
VLOG(jdwp) << "get int/float local " << reg << " = " << intVal;
JDWP::Set4BE(buf_+1, intVal);
}
@@ -1793,7 +1798,7 @@
case JDWP::JT_ARRAY:
{
CHECK_EQ(width_, sizeof(JDWP::ObjectId));
- Object* o = reinterpret_cast<Object*>(GetVReg(m, reg));
+ Object* o = reinterpret_cast<Object*>(GetVReg(m, reg, kReferenceVReg));
VLOG(jdwp) << "get array local " << reg << " = " << o;
if (!Runtime::Current()->GetHeap()->IsHeapAddress(o)) {
LOG(FATAL) << "Register " << reg << " expected to hold array: " << o;
@@ -1809,7 +1814,7 @@
case JDWP::JT_THREAD_GROUP:
{
CHECK_EQ(width_, sizeof(JDWP::ObjectId));
- Object* o = reinterpret_cast<Object*>(GetVReg(m, reg));
+ Object* o = reinterpret_cast<Object*>(GetVReg(m, reg, kReferenceVReg));
VLOG(jdwp) << "get object local " << reg << " = " << o;
if (!Runtime::Current()->GetHeap()->IsHeapAddress(o)) {
LOG(FATAL) << "Register " << reg << " expected to hold object: " << o;
@@ -1819,11 +1824,20 @@
}
break;
case JDWP::JT_DOUBLE:
+ {
+ CHECK_EQ(width_, 8U);
+ uint32_t lo = GetVReg(m, reg, kDoubleLoVReg);
+ uint64_t hi = GetVReg(m, reg + 1, kDoubleHiVReg);
+ uint64_t longVal = (hi << 32) | lo;
+ VLOG(jdwp) << "get double/long local " << hi << ":" << lo << " = " << longVal;
+ JDWP::Set8BE(buf_+1, longVal);
+ }
+ break;
case JDWP::JT_LONG:
{
CHECK_EQ(width_, 8U);
- uint32_t lo = GetVReg(m, reg);
- uint64_t hi = GetVReg(m, reg + 1);
+ uint32_t lo = GetVReg(m, reg, kLongLoVReg);
+ uint64_t hi = GetVReg(m, reg + 1, kLongHiVReg);
uint64_t longVal = (hi << 32) | lo;
VLOG(jdwp) << "get double/long local " << hi << ":" << lo << " = " << longVal;
JDWP::Set8BE(buf_+1, longVal);
@@ -1878,17 +1892,20 @@
case JDWP::JT_BOOLEAN:
case JDWP::JT_BYTE:
CHECK_EQ(width_, 1U);
- SetVReg(m, reg, static_cast<uint32_t>(value_));
+ SetVReg(m, reg, static_cast<uint32_t>(value_), kIntVReg);
break;
case JDWP::JT_SHORT:
case JDWP::JT_CHAR:
CHECK_EQ(width_, 2U);
- SetVReg(m, reg, static_cast<uint32_t>(value_));
+ SetVReg(m, reg, static_cast<uint32_t>(value_), kIntVReg);
break;
case JDWP::JT_INT:
+ CHECK_EQ(width_, 4U);
+ SetVReg(m, reg, static_cast<uint32_t>(value_), kIntVReg);
+ break;
case JDWP::JT_FLOAT:
CHECK_EQ(width_, 4U);
- SetVReg(m, reg, static_cast<uint32_t>(value_));
+ SetVReg(m, reg, static_cast<uint32_t>(value_), kFloatVReg);
break;
case JDWP::JT_ARRAY:
case JDWP::JT_OBJECT:
@@ -1899,14 +1916,18 @@
if (o == kInvalidObject) {
UNIMPLEMENTED(FATAL) << "return an error code when given an invalid object to store";
}
- SetVReg(m, reg, static_cast<uint32_t>(reinterpret_cast<uintptr_t>(o)));
+ SetVReg(m, reg, static_cast<uint32_t>(reinterpret_cast<uintptr_t>(o)), kReferenceVReg);
}
break;
case JDWP::JT_DOUBLE:
+ CHECK_EQ(width_, 8U);
+ SetVReg(m, reg, static_cast<uint32_t>(value_), kDoubleLoVReg);
+ SetVReg(m, reg + 1, static_cast<uint32_t>(value_ >> 32), kDoubleHiVReg);
+ break;
case JDWP::JT_LONG:
CHECK_EQ(width_, 8U);
- SetVReg(m, reg, static_cast<uint32_t>(value_));
- SetVReg(m, reg + 1, static_cast<uint32_t>(value_ >> 32));
+ SetVReg(m, reg, static_cast<uint32_t>(value_), kLongLoVReg);
+ SetVReg(m, reg + 1, static_cast<uint32_t>(value_ >> 32), kLongHiVReg);
break;
default:
LOG(FATAL) << "Unknown tag " << tag_;
diff --git a/src/dex_instruction.cc b/src/dex_instruction.cc
index 201a8e6..d3aa238 100644
--- a/src/dex_instruction.cc
+++ b/src/dex_instruction.cc
@@ -289,8 +289,8 @@
switch (insn.opcode) {
case CONST_STRING:
if (file != NULL) {
- os << StringPrintf("const-string v%d, \"%s\" // string@%d", insn.vA,
- file->StringDataByIdx(insn.vB), insn.vB);
+ os << StringPrintf("const-string v%d, %s // string@%d", insn.vA,
+ PrintableString(file->StringDataByIdx(insn.vB)).c_str(), insn.vB);
break;
} // else fall-through
case CHECK_CAST:
diff --git a/src/disassembler_arm.cc b/src/disassembler_arm.cc
index d047f0e..15480b9 100644
--- a/src/disassembler_arm.cc
+++ b/src/disassembler_arm.cc
@@ -275,7 +275,7 @@
opcode += kConditionCodeNames[cond];
opcode += suffixes;
// TODO: a more complete ARM disassembler could generate wider opcodes.
- os << StringPrintf("\t\t\t%p: %08x\t%-7s ", instr_ptr, instruction, opcode.c_str()) << args.str() << '\n';
+ os << StringPrintf("%p: %08x\t%-7s ", instr_ptr, instruction, opcode.c_str()) << args.str() << '\n';
}
size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) {
@@ -854,7 +854,7 @@
it_conditions_.pop_back();
}
- os << StringPrintf("\t\t\t%p: %08x\t%-7s ", instr_ptr, instr, opcode.str().c_str()) << args.str() << '\n';
+ os << StringPrintf("%p: %08x\t%-7s ", instr_ptr, instr, opcode.str().c_str()) << args.str() << '\n';
return 4;
}
@@ -1157,7 +1157,7 @@
it_conditions_.pop_back();
}
- os << StringPrintf("\t\t\t%p: %04x \t%-7s ", instr_ptr, instr, opcode.str().c_str()) << args.str() << '\n';
+ os << StringPrintf("%p: %04x \t%-7s ", instr_ptr, instr, opcode.str().c_str()) << args.str() << '\n';
}
return 2;
}
diff --git a/src/disassembler_mips.cc b/src/disassembler_mips.cc
index 86a661c..0efcb0b 100644
--- a/src/disassembler_mips.cc
+++ b/src/disassembler_mips.cc
@@ -254,7 +254,7 @@
}
}
- os << StringPrintf("\t\t\t%p: %08x\t%-7s ", instr_ptr, instruction, opcode.c_str()) << args.str() << '\n';
+ os << StringPrintf("%p: %08x\t%-7s ", instr_ptr, instruction, opcode.c_str()) << args.str() << '\n';
}
DisassemblerMips::DisassemblerMips() {
diff --git a/src/disassembler_x86.cc b/src/disassembler_x86.cc
index 35593a3..bb187e1 100644
--- a/src/disassembler_x86.cc
+++ b/src/disassembler_x86.cc
@@ -731,7 +731,7 @@
for (size_t i = 0; begin_instr + i < instr; ++i) {
hex << StringPrintf("%02X", begin_instr[i]);
}
- os << StringPrintf("\t\t\t%p: %22s \t%-7s ", begin_instr, hex.str().c_str(), opcode.str().c_str()) << args.str() << '\n';
+ os << StringPrintf("%p: %22s \t%-7s ", begin_instr, hex.str().c_str(), opcode.str().c_str()) << args.str() << '\n';
return instr - begin_instr;
}
diff --git a/src/gc/mark_sweep.cc b/src/gc/mark_sweep.cc
index d6c44db..1ccceaa 100644
--- a/src/gc/mark_sweep.cc
+++ b/src/gc/mark_sweep.cc
@@ -272,8 +272,7 @@
LOG(ERROR) << "Found invalid root: " << root;
LOG(ERROR) << "VReg: " << vreg;
if (method != NULL) {
- LOG(ERROR) << "In method " << PrettyMethod(method, true) << "\nVerifier output:\n";
- verifier::MethodVerifier::VerifyMethodAndDump(const_cast<AbstractMethod*>(method));
+ LOG(ERROR) << "In method " << PrettyMethod(method, true);
}
}
}
diff --git a/src/indenter.h b/src/indenter.h
new file mode 100644
index 0000000..f66df08
--- /dev/null
+++ b/src/indenter.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2012 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_SRC_INDENTER_H_
+#define ART_SRC_INDENTER_H_
+
+#include "macros.h"
+#include <streambuf>
+
+const char kIndentChar =' ';
+const size_t kIndentBy1Count = 2;
+
+class Indenter : public std::streambuf {
+ public:
+ Indenter(std::streambuf* out, char text, size_t count)
+ : indent_next_(true), out_sbuf_(out), text_(text), count_(count) {}
+
+ private:
+ int_type overflow(int_type c) {
+ if (c != std::char_traits<char>::eof()) {
+ if (indent_next_) {
+ for (size_t i = 0; i < count_; ++i) {
+ out_sbuf_->sputc(text_);
+ }
+ }
+ out_sbuf_->sputc(c);
+ indent_next_ = (c == '\n');
+ }
+ return std::char_traits<char>::not_eof(c);
+ }
+
+ int sync() {
+ return out_sbuf_->pubsync();
+ }
+
+ bool indent_next_;
+
+ // Buffer to write output to.
+ std::streambuf* const out_sbuf_;
+
+ // Text output as indent.
+ const char text_;
+
+ // Number of times text is output.
+ const size_t count_;
+
+ DISALLOW_COPY_AND_ASSIGN(Indenter);
+};
+
+#endif // ART_SRC_INDENTER_H_
diff --git a/src/indenter_test.cc b/src/indenter_test.cc
new file mode 100644
index 0000000..1919e3d
--- /dev/null
+++ b/src/indenter_test.cc
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011 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 "gtest/gtest.h"
+#include "indenter.h"
+
+TEST(IndenterTest, MultiLineTest) {
+ std::ostringstream output;
+ Indenter indent_filter(output.rdbuf(), '\t', 2);
+ std::ostream input(&indent_filter);
+
+ EXPECT_EQ(output.str(), "");
+
+ input << "hello";
+ EXPECT_EQ(output.str(), "\t\thello");
+
+ input << "\nhello again";
+ EXPECT_EQ(output.str(), "\t\thello\n\t\thello again");
+
+ input << "\n";
+ EXPECT_EQ(output.str(), "\t\thello\n\t\thello again\n");
+}
diff --git a/src/monitor.cc b/src/monitor.cc
index 7c433f7..1fbda87 100644
--- a/src/monitor.cc
+++ b/src/monitor.cc
@@ -932,7 +932,8 @@
}
uint16_t monitor_register = ((monitor_enter_instruction >> 8) & 0xff);
- Object* o = reinterpret_cast<Object*>(stack_visitor->GetVReg(m, monitor_register));
+ Object* o = reinterpret_cast<Object*>(stack_visitor->GetVReg(m, monitor_register,
+ kReferenceVReg));
DumpLockedObject(os, o);
}
}
diff --git a/src/oat/runtime/context.h b/src/oat/runtime/context.h
index 7002f6a..317030f 100644
--- a/src/oat/runtime/context.h
+++ b/src/oat/runtime/context.h
@@ -57,54 +57,6 @@
};
};
-class VmapTable {
- public:
- explicit VmapTable(const uint16_t* table) : table_(table) {
- }
-
- uint16_t operator[](size_t i) const {
- return table_[i + 1];
- }
-
- size_t size() const {
- return table_[0];
- }
-
- /*
- * WARNING: This code should be changed or renamed. The "reg"
- * argument is a Dalvik virtual register number, but the way
- * the vmap and register promotion works a Dalvik vReg can have
- * neither, one or both of core register and floating point register
- * identities. The "INVALID_VREG" marker of 0xffff below separates the
- * core promoted registers from the floating point promoted registers,
- * and thus terminates the search before reaching the fp section.
- * This is likely the desired behavior for GC, as references won't
- * ever be promoted to float registers - but we'll probably want to
- * rework this shared code to make it useful for the debugger as well.
- */
- // Is register 'reg' in the context or on the stack?
- bool IsInContext(size_t reg, uint32_t& vmap_offset) const {
- vmap_offset = 0xEBAD0FF5;
- // TODO: take advantage of the registers being ordered
- for (size_t i = 0; i < size(); ++i) {
- // Stop if we find what we are are looking for...
- if (table_[i + 1] == reg) {
- vmap_offset = i;
- return true;
- }
- // ...or the INVALID_VREG that marks lr.
- // TODO: x86?
- if (table_[i + 1] == 0xffff) {
- break;
- }
- }
- return false;
- }
-
- private:
- const uint16_t* table_;
-};
-
} // namespace art
#endif // ART_SRC_OAT_RUNTIME_CONTEXT_H_
diff --git a/src/oatdump.cc b/src/oatdump.cc
index a371d28..45d936a 100644
--- a/src/oatdump.cc
+++ b/src/oatdump.cc
@@ -26,17 +26,18 @@
#include "dex_instruction.h"
#include "disassembler.h"
#include "file.h"
+#include "gc_map.h"
#include "gc/large_object_space.h"
#include "gc/space.h"
#include "image.h"
-#include "oat/runtime/context.h" // For VmapTable
+#include "indenter.h"
#include "object_utils.h"
#include "os.h"
#include "runtime.h"
#include "safe_map.h"
#include "scoped_thread_state_change.h"
#include "stringpiece.h"
-#include "gc_map.h"
+#include "verifier/method_verifier.h"
namespace art {
@@ -251,7 +252,9 @@
// TODO: JACK CLASS ACCESS (HACK TO BE REMOVED)
<< ( (class_def.access_flags_ & kAccClassJack) == kAccClassJack ? " (Jack)" : "" )
<< "\n";
- DumpOatClass(os, *oat_class.get(), *(dex_file.get()), class_def);
+ Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
+ std::ostream indented_os(&indent_filter);
+ DumpOatClass(indented_os, *oat_class.get(), *(dex_file.get()), class_def);
}
os << std::flush;
@@ -274,73 +277,105 @@
}
ClassDataItemIterator it(dex_file, class_data);
SkipAllFields(it);
-
- uint32_t class_method_index = 0;
+ uint32_t class_def_idx = dex_file.GetIndexForClassDef(class_def);
+ uint32_t class_method_idx = 0;
while (it.HasNextDirectMethod()) {
- const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_index);
- DumpOatMethod(os, class_method_index, oat_method, dex_file,
- it.GetMemberIndex(), it.GetMethodCodeItem());
- class_method_index++;
+ const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_idx);
+ DumpOatMethod(os, class_def_idx, class_method_idx, oat_method, dex_file,
+ it.GetMemberIndex(), it.GetMethodCodeItem(), it.GetMemberAccessFlags());
+ class_method_idx++;
it.Next();
}
while (it.HasNextVirtualMethod()) {
- const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_index);
- DumpOatMethod(os, class_method_index, oat_method, dex_file,
- it.GetMemberIndex(), it.GetMethodCodeItem());
- class_method_index++;
+ const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_idx);
+ DumpOatMethod(os, class_def_idx, class_method_idx, oat_method, dex_file,
+ it.GetMemberIndex(), it.GetMethodCodeItem(), it.GetMemberAccessFlags());
+ class_method_idx++;
it.Next();
}
DCHECK(!it.HasNext());
os << std::flush;
}
- void DumpOatMethod(std::ostream& os, uint32_t class_method_index,
+ void DumpOatMethod(std::ostream& os, uint32_t class_def_idx, uint32_t class_method_index,
const OatFile::OatMethod& oat_method, const DexFile& dex_file,
- uint32_t dex_method_idx, const DexFile::CodeItem* code_item) {
- os << StringPrintf("\t%d: %s (dex_method_idx=%d)\n",
+ uint32_t dex_method_idx, const DexFile::CodeItem* code_item,
+ uint32_t method_access_flags) {
+ os << StringPrintf("%d: %s (dex_method_idx=%d)\n",
class_method_index, PrettyMethod(dex_method_idx, dex_file, true).c_str(),
dex_method_idx);
- os << StringPrintf("\t\tframe_size_in_bytes: %zd\n", oat_method.GetFrameSizeInBytes());
- os << StringPrintf("\t\tcore_spill_mask: 0x%08x\n", oat_method.GetCoreSpillMask());
- DumpSpillMask(os, oat_method.GetCoreSpillMask(), false);
- os << StringPrintf("\n\t\tfp_spill_mask: 0x%08x\n", oat_method.GetFpSpillMask());
- DumpSpillMask(os, oat_method.GetFpSpillMask(), true);
- os << StringPrintf("\t\tvmap_table: %p (offset=0x%08x)\n",
- oat_method.GetVmapTable(), oat_method.GetVmapTableOffset());
- DumpVmap(os, oat_method);
- const bool kDumpRawMappingTable = false;
- if (kDumpRawMappingTable) {
- os << StringPrintf("\t\tmapping_table: %p (offset=0x%08x)\n",
- oat_method.GetMappingTable(), oat_method.GetMappingTableOffset());
- DumpMappingTable(os, oat_method);
+ Indenter indent1_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
+ std::ostream indent1_os(&indent1_filter);
+ {
+ indent1_os << "DEX CODE:\n";
+ Indenter indent2_filter(indent1_os.rdbuf(), kIndentChar, kIndentBy1Count);
+ std::ostream indent2_os(&indent2_filter);
+ DumpDexCode(indent2_os, dex_file, code_item);
}
- const bool kDumpRawGcMap = false;
- if (kDumpRawGcMap) {
- os << StringPrintf("\t\tgc_map: %p (offset=0x%08x)\n",
- oat_method.GetNativeGcMap(), oat_method.GetNativeGcMapOffset());
- DumpGcMap(os, oat_method, code_item);
+ if (Runtime::Current() != NULL) {
+ indent1_os << "VERIFIER TYPE ANALYSIS:\n";
+ Indenter indent2_filter(indent1_os.rdbuf(), kIndentChar, kIndentBy1Count);
+ std::ostream indent2_os(&indent2_filter);
+ DumpVerifier(indent2_os, dex_method_idx, &dex_file, class_def_idx, code_item, method_access_flags);
}
- os << "\t\tDEX CODE:\n";
- DumpDexCode(os, dex_file, code_item);
- os << StringPrintf("\t\tCODE: %p (offset=0x%08x size=%d)%s\n",
- oat_method.GetCode(),
- oat_method.GetCodeOffset(),
- oat_method.GetCodeSize(),
- oat_method.GetCode() != NULL ? "..." : "");
- DumpCode(os, oat_method, code_item);
- os << StringPrintf("\t\tINVOKE STUB: %p (offset=0x%08x size=%d)%s\n",
- oat_method.GetInvokeStub(),
- oat_method.GetInvokeStubOffset(),
- oat_method.GetInvokeStubSize(),
- oat_method.GetInvokeStub() != NULL ? "..." : "");
- DumpInvokeStub(os, oat_method);
+ {
+ indent1_os << "OAT DATA:\n";
+ Indenter indent2_filter(indent1_os.rdbuf(), kIndentChar, kIndentBy1Count);
+ std::ostream indent2_os(&indent2_filter);
+
+ indent2_os << StringPrintf("frame_size_in_bytes: %zd\n", oat_method.GetFrameSizeInBytes());
+ indent2_os << StringPrintf("core_spill_mask: 0x%08x ", oat_method.GetCoreSpillMask());
+ DumpSpillMask(indent2_os, oat_method.GetCoreSpillMask(), false);
+ indent2_os << StringPrintf("\nfp_spill_mask: 0x%08x ", oat_method.GetFpSpillMask());
+ DumpSpillMask(indent2_os, oat_method.GetFpSpillMask(), true);
+ indent2_os << StringPrintf("\nvmap_table: %p (offset=0x%08x)\n",
+ oat_method.GetVmapTable(), oat_method.GetVmapTableOffset());
+ DumpVmap(indent2_os, oat_method);
+ indent2_os << StringPrintf("mapping_table: %p (offset=0x%08x)\n",
+ oat_method.GetMappingTable(), oat_method.GetMappingTableOffset());
+ const bool kDumpRawMappingTable = false;
+ if (kDumpRawMappingTable) {
+ Indenter indent3_filter(indent2_os.rdbuf(), kIndentChar, kIndentBy1Count);
+ std::ostream indent3_os(&indent3_filter);
+ DumpMappingTable(indent3_os, oat_method);
+ }
+ indent2_os << StringPrintf("gc_map: %p (offset=0x%08x)\n",
+ oat_method.GetNativeGcMap(), oat_method.GetNativeGcMapOffset());
+ const bool kDumpRawGcMap = false;
+ if (kDumpRawGcMap) {
+ Indenter indent3_filter(indent2_os.rdbuf(), kIndentChar, kIndentBy1Count);
+ std::ostream indent3_os(&indent3_filter);
+ DumpGcMap(indent3_os, oat_method, code_item);
+ }
+ }
+ {
+ indent1_os << StringPrintf("CODE: %p (offset=0x%08x size=%d)%s\n",
+ oat_method.GetCode(),
+ oat_method.GetCodeOffset(),
+ oat_method.GetCodeSize(),
+ oat_method.GetCode() != NULL ? "..." : "");
+ Indenter indent2_filter(indent1_os.rdbuf(), kIndentChar, kIndentBy1Count);
+ std::ostream indent2_os(&indent2_filter);
+ DumpCode(indent2_os, oat_method, dex_method_idx, &dex_file, class_def_idx, code_item,
+ method_access_flags);
+ }
+ {
+ indent1_os << StringPrintf("INVOKE STUB: %p (offset=0x%08x size=%d)%s\n",
+ oat_method.GetInvokeStub(),
+ oat_method.GetInvokeStubOffset(),
+ oat_method.GetInvokeStubSize(),
+ oat_method.GetInvokeStub() != NULL ? "..." : "");
+ Indenter indent2_filter(indent1_os.rdbuf(), kIndentChar, kIndentBy1Count);
+ std::ostream indent2_os(&indent2_filter);
+ DumpInvokeStub(indent2_os, oat_method);
+ }
}
void DumpSpillMask(std::ostream& os, uint32_t spill_mask, bool is_float) {
if (spill_mask == 0) {
return;
}
- os << " (";
+ os << "(";
for (size_t i = 0; i < 32; i++) {
if ((spill_mask & (1 << i)) != 0) {
if (is_float) {
@@ -366,57 +401,38 @@
}
const VmapTable vmap_table(raw_table);
bool first = true;
- os << "\t\t\t";
+ bool processing_fp = false;
+ uint32_t spill_mask = oat_method.GetCoreSpillMask();
for (size_t i = 0; i < vmap_table.size(); i++) {
uint16_t dex_reg = vmap_table[i];
- size_t matches = 0;
- size_t spill_shifts = 0;
- uint32_t spill_mask = oat_method.GetCoreSpillMask();
- bool processing_fp = false;
- while (matches != (i + 1)) {
- if (spill_mask == 0) {
- CHECK(!processing_fp);
- spill_mask = oat_method.GetFpSpillMask();
- processing_fp = true;
- }
- matches += spill_mask & 1; // Add 1 if the low bit is set
- spill_mask >>= 1;
- spill_shifts++;
- }
- size_t arm_reg = spill_shifts - 1; // wind back one as we want the last match
+ uint32_t cpu_reg = vmap_table.ComputeRegister(spill_mask, i,
+ processing_fp ? kFloatVReg : kIntVReg);
os << (first ? "v" : ", v") << dex_reg;
- if (arm_reg < 16) {
- os << "/r" << arm_reg;
+ if (!processing_fp) {
+ os << "/r" << cpu_reg;
} else {
- os << "/fr" << (arm_reg - 16);
+ os << "/fr" << cpu_reg;
}
- if (first) {
- first = false;
+ first = false;
+ if (!processing_fp && dex_reg == 0xFFFF) {
+ processing_fp = true;
+ spill_mask = oat_method.GetFpSpillMask();
}
}
os << "\n";
}
void DescribeVReg(std::ostream& os, const OatFile::OatMethod& oat_method,
- const DexFile::CodeItem* code_item, size_t reg) {
+ const DexFile::CodeItem* code_item, size_t reg, VRegKind kind) {
const uint16_t* raw_table = oat_method.GetVmapTable();
if (raw_table != NULL) {
const VmapTable vmap_table(raw_table);
uint32_t vmap_offset;
- if (vmap_table.IsInContext(reg, vmap_offset)) {
- // Compute the register we need to load from the context
- uint32_t spill_mask = oat_method.GetCoreSpillMask();
- CHECK_LT(vmap_offset, static_cast<uint32_t>(__builtin_popcount(spill_mask)));
- uint32_t matches = 0;
- uint32_t spill_shifts = 0;
- while (matches != (vmap_offset + 1)) {
- DCHECK_NE(spill_mask, 0u);
- matches += spill_mask & 1; // Add 1 if the low bit is set
- spill_mask >>= 1;
- spill_shifts++;
- }
- spill_shifts--; // wind back one as we want the last match
- os << "r" << spill_shifts;
+ if (vmap_table.IsInContext(reg, vmap_offset, kind)) {
+ bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg);
+ uint32_t spill_mask = is_float ? oat_method.GetFpSpillMask()
+ : oat_method.GetCoreSpillMask();
+ os << (is_float ? "fr" : "r") << vmap_table.ComputeRegister(spill_mask, vmap_offset, kind);
} else {
uint32_t offset = StackVisitor::GetVRegOffset(code_item, oat_method.GetCoreSpillMask(),
oat_method.GetFpSpillMask(),
@@ -437,7 +453,7 @@
for (size_t entry = 0; entry < map.NumEntries(); entry++) {
const uint8_t* native_pc = reinterpret_cast<const uint8_t*>(code) +
map.GetNativePcOffset(entry);
- os << StringPrintf("\t\t\t%p", native_pc);
+ os << StringPrintf("%p", native_pc);
size_t num_regs = map.RegWidth() * 8;
const uint8_t* reg_bitmap = map.GetBitMap(entry);
bool first = true;
@@ -445,12 +461,12 @@
if (((reg_bitmap[reg / 8] >> (reg % 8)) & 0x01) != 0) {
if (first) {
os << " v" << reg << " (";
- DescribeVReg(os, oat_method, code_item, reg);
+ DescribeVReg(os, oat_method, code_item, reg, kReferenceVReg);
os << ")";
first = false;
} else {
os << ", v" << reg << " (";
- DescribeVReg(os, oat_method, code_item, reg);
+ DescribeVReg(os, oat_method, code_item, reg, kReferenceVReg);
os << ")";
}
}
@@ -469,26 +485,33 @@
++raw_table;
uint32_t length = *raw_table;
++raw_table;
+ if (length == 0) {
+ return;
+ }
uint32_t pc_to_dex_entries = *raw_table;
++raw_table;
-
- os << "\t\tsuspend point mappings {";
+ if (pc_to_dex_entries != 0) {
+ os << "suspend point mappings {\n";
+ } else {
+ os << "catch entry mappings {\n";
+ }
+ Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
+ std::ostream indent_os(&indent_filter);
for (size_t i = 0; i < length; i += 2) {
const uint8_t* native_pc = reinterpret_cast<const uint8_t*>(code) + raw_table[i];
uint32_t dex_pc = raw_table[i + 1];
- os << StringPrintf("%p -> 0x%04x", native_pc, dex_pc);
- if (i + 2 == pc_to_dex_entries) {
+ indent_os << StringPrintf("%p -> 0x%04x\n", native_pc, dex_pc);
+ if (i + 2 == pc_to_dex_entries && pc_to_dex_entries != length) {
// Separate the pc -> dex from dex -> pc sections
- os << "}\n\t\tcatch entry mappings {";
- } else if (i + 2 < length) {
- os << ", ";
+ indent_os << std::flush;
+ os << "}\ncatch entry mappings {\n";
}
}
os << "}\n";
}
- void DumpMappingAtOffset(std::ostream& os, const OatFile::OatMethod& oat_method, size_t offset,
- bool suspend_point_mapping) {
+ uint32_t DumpMappingAtOffset(std::ostream& os, const OatFile::OatMethod& oat_method, size_t offset,
+ bool suspend_point_mapping) {
const uint32_t* raw_table = oat_method.GetMappingTable();
if (raw_table != NULL) {
++raw_table;
@@ -506,37 +529,39 @@
}
for (size_t i = start; i < end; i += 2) {
if (offset == raw_table[i]) {
+ uint32_t dex_pc = raw_table[i + 1];
if (suspend_point_mapping) {
- os << "\t\t\tsuspend point dex PC: 0x";
+ os << "suspend point dex PC: 0x";
} else {
- os << "\t\t\tcatch entry dex PC: 0x";
+ os << "catch entry dex PC: 0x";
}
- os << std::hex << raw_table[i + 1] << std::dec << "\n";
- return;
+ os << std::hex << dex_pc << std::dec << "\n";
+ return dex_pc;
}
}
}
+ return DexFile::kDexNoIndex;
}
- void DumpGcMapAtOffset(std::ostream& os, const OatFile::OatMethod& oat_method,
- const DexFile::CodeItem* code_item, size_t offset) {
+ void DumpGcMapAtNativePcOffset(std::ostream& os, const OatFile::OatMethod& oat_method,
+ const DexFile::CodeItem* code_item, size_t native_pc_offset) {
const uint8_t* gc_map_raw = oat_method.GetNativeGcMap();
if (gc_map_raw != NULL) {
NativePcOffsetToReferenceMap map(gc_map_raw);
- if (map.HasEntry(offset)) {
+ if (map.HasEntry(native_pc_offset)) {
size_t num_regs = map.RegWidth() * 8;
- const uint8_t* reg_bitmap = map.FindBitMap(offset);
+ const uint8_t* reg_bitmap = map.FindBitMap(native_pc_offset);
bool first = true;
for (size_t reg = 0; reg < num_regs; reg++) {
if (((reg_bitmap[reg / 8] >> (reg % 8)) & 0x01) != 0) {
if (first) {
- os << "\t\t\tGC map objects: v" << reg << " (";
- DescribeVReg(os, oat_method, code_item, reg);
+ os << "GC map objects: v" << reg << " (";
+ DescribeVReg(os, oat_method, code_item, reg, kReferenceVReg);
os << ")";
first = false;
} else {
os << ", v" << reg << " (";
- DescribeVReg(os, oat_method, code_item, reg);
+ DescribeVReg(os, oat_method, code_item, reg, kReferenceVReg);
os << ")";
}
}
@@ -548,32 +573,97 @@
}
}
+ void DumpVRegsAtDexPc(std::ostream& os, const OatFile::OatMethod& oat_method,
+ uint32_t dex_method_idx, const DexFile* dex_file,
+ uint32_t class_def_idx, const DexFile::CodeItem* code_item,
+ uint32_t method_access_flags, uint32_t dex_pc) {
+ bool first = true;
+ ScopedObjectAccess soa(Thread::Current());
+ DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(*dex_file);
+ ClassLoader* class_loader = NULL;
+ std::vector<int32_t> kinds =
+ verifier::MethodVerifier::DescribeVRegs(dex_method_idx, dex_file, dex_cache,
+ class_loader, class_def_idx, code_item, NULL,
+ method_access_flags, dex_pc);
+ for (size_t reg = 0; reg < code_item->registers_size_; reg++) {
+ VRegKind kind = static_cast<VRegKind>(kinds.at(reg * 2));
+ if (kind != kUndefined) {
+ if (first) {
+ os << "VRegs: v";
+ first = false;
+ } else {
+ os << ", v";
+ }
+ os << reg << " (";
+ switch (kind) {
+ case kImpreciseConstant:
+ os << "Imprecise Constant: " << kinds.at((reg * 2) + 1) << ", ";
+ DescribeVReg(os, oat_method, code_item, reg, kind);
+ break;
+ case kConstant:
+ os << "Constant: " << kinds.at((reg * 2) + 1);
+ break;
+ default:
+ DescribeVReg(os, oat_method, code_item, reg, kind);
+ break;
+ }
+ os << ")";
+ }
+ }
+ if (!first) {
+ os << "\n";
+ }
+ }
+
+
void DumpDexCode(std::ostream& os, const DexFile& dex_file, const DexFile::CodeItem* code_item) {
if (code_item != NULL) {
size_t i = 0;
while (i < code_item->insns_size_in_code_units_) {
const Instruction* instruction = Instruction::At(&code_item->insns_[i]);
- os << StringPrintf("\t\t\t0x%04zx: %s\n", i, instruction->DumpString(&dex_file).c_str());
+ os << StringPrintf("0x%04zx: %s\n", i, instruction->DumpString(&dex_file).c_str());
i += instruction->SizeInCodeUnits();
}
}
}
- void DumpCode(std::ostream& os, const OatFile::OatMethod& oat_method,
- const DexFile::CodeItem* code_item) {
+ void DumpVerifier(std::ostream& os, uint32_t dex_method_idx, const DexFile* dex_file,
+ uint32_t class_def_idx, const DexFile::CodeItem* code_item,
+ uint32_t method_access_flags) {
+ if ((method_access_flags & kAccNative) == 0) {
+ ScopedObjectAccess soa(Thread::Current());
+ DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(*dex_file);
+ ClassLoader* class_loader = NULL;
+ verifier::MethodVerifier::VerifyMethodAndDump(os, dex_method_idx, dex_file, dex_cache,
+ class_loader, class_def_idx, code_item, NULL,
+ method_access_flags);
+ }
+ }
+
+ void DumpCode(std::ostream& os, const OatFile::OatMethod& oat_method,
+ uint32_t dex_method_idx, const DexFile* dex_file,
+ uint32_t class_def_idx, const DexFile::CodeItem* code_item,
+ uint32_t method_access_flags) {
const void* code = oat_method.GetCode();
size_t code_size = oat_method.GetCodeSize();
if (code == NULL || code_size == 0) {
- os << "\t\t\tNO CODE!\n";
+ os << "NO CODE!\n";
return;
}
const uint8_t* native_pc = reinterpret_cast<const uint8_t*>(code);
size_t offset = 0;
+ const bool kDumpVRegs = (Runtime::Current() != NULL);
while (offset < code_size) {
DumpMappingAtOffset(os, oat_method, offset, false);
offset += disassembler_->Dump(os, native_pc + offset);
- DumpMappingAtOffset(os, oat_method, offset, true);
- DumpGcMapAtOffset(os, oat_method, code_item, offset);
+ uint32_t dex_pc = DumpMappingAtOffset(os, oat_method, offset, true);
+ if (dex_pc != DexFile::kDexNoIndex) {
+ DumpGcMapAtNativePcOffset(os, oat_method, code_item, offset);
+ if (kDumpVRegs) {
+ DumpVRegsAtDexPc(os, oat_method, dex_method_idx, dex_file, class_def_idx, code_item,
+ method_access_flags, dex_pc);
+ }
+ }
}
}
@@ -592,72 +682,70 @@
class ImageDumper {
public:
- explicit ImageDumper(std::ostream& os, const std::string& image_filename,
+ explicit ImageDumper(std::ostream* os, const std::string& image_filename,
const std::string& host_prefix, Space& image_space,
const ImageHeader& image_header)
: os_(os), image_filename_(image_filename), host_prefix_(host_prefix),
image_space_(image_space), image_header_(image_header) {}
void Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- os_ << "MAGIC:\n";
- os_ << image_header_.GetMagic() << "\n\n";
+ std::ostream& os = *os_;
+ os << "MAGIC: " << image_header_.GetMagic() << "\n\n";
- os_ << "IMAGE BEGIN:\n";
- os_ << reinterpret_cast<void*>(image_header_.GetImageBegin()) << "\n\n";
+ os << "IMAGE BEGIN: " << reinterpret_cast<void*>(image_header_.GetImageBegin()) << "\n\n";
- os_ << "OAT CHECKSUM:\n";
- os_ << StringPrintf("0x%08x\n\n", image_header_.GetOatChecksum());
+ os << "OAT CHECKSUM: " << StringPrintf("0x%08x\n\n", image_header_.GetOatChecksum());
- os_ << "OAT BEGIN:\n";
- os_ << reinterpret_cast<void*>(image_header_.GetOatBegin()) << "\n\n";
+ os << "OAT BEGIN:" << reinterpret_cast<void*>(image_header_.GetOatBegin()) << "\n\n";
- os_ << "OAT END:\n";
- os_ << reinterpret_cast<void*>(image_header_.GetOatEnd()) << "\n\n";
+ os << "OAT END:" << reinterpret_cast<void*>(image_header_.GetOatEnd()) << "\n\n";
- os_ << "ROOTS:\n";
- os_ << reinterpret_cast<void*>(image_header_.GetImageRoots()) << "\n";
- CHECK_EQ(arraysize(image_roots_descriptions_), size_t(ImageHeader::kImageRootsMax));
- for (int i = 0; i < ImageHeader::kImageRootsMax; i++) {
- ImageHeader::ImageRoot image_root = static_cast<ImageHeader::ImageRoot>(i);
- const char* image_root_description = image_roots_descriptions_[i];
- Object* image_root_object = image_header_.GetImageRoot(image_root);
- os_ << StringPrintf("%s: %p\n", image_root_description, image_root_object);
- if (image_root_object->IsObjectArray()) {
- // TODO: replace down_cast with AsObjectArray (g++ currently has a problem with this)
- ObjectArray<Object>* image_root_object_array
- = down_cast<ObjectArray<Object>*>(image_root_object);
- // = image_root_object->AsObjectArray<Object>();
- for (int i = 0; i < image_root_object_array->GetLength(); i++) {
- Object* value = image_root_object_array->Get(i);
- if (value != NULL) {
- os_ << "\t" << i << ": ";
- std::string summary;
- PrettyObjectValue(summary, value->GetClass(), value);
- os_ << summary;
- } else {
- os_ << StringPrintf("\t%d: null\n", i);
+ {
+ os << "ROOTS: " << reinterpret_cast<void*>(image_header_.GetImageRoots()) << "\n";
+ Indenter indent1_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
+ std::ostream indent1_os(&indent1_filter);
+ CHECK_EQ(arraysize(image_roots_descriptions_), size_t(ImageHeader::kImageRootsMax));
+ for (int i = 0; i < ImageHeader::kImageRootsMax; i++) {
+ ImageHeader::ImageRoot image_root = static_cast<ImageHeader::ImageRoot>(i);
+ const char* image_root_description = image_roots_descriptions_[i];
+ Object* image_root_object = image_header_.GetImageRoot(image_root);
+ indent1_os << StringPrintf("%s: %p\n", image_root_description, image_root_object);
+ if (image_root_object->IsObjectArray()) {
+ Indenter indent2_filter(indent1_os.rdbuf(), kIndentChar, kIndentBy1Count);
+ std::ostream indent2_os(&indent2_filter);
+ // TODO: replace down_cast with AsObjectArray (g++ currently has a problem with this)
+ ObjectArray<Object>* image_root_object_array
+ = down_cast<ObjectArray<Object>*>(image_root_object);
+ // = image_root_object->AsObjectArray<Object>();
+ for (int i = 0; i < image_root_object_array->GetLength(); i++) {
+ Object* value = image_root_object_array->Get(i);
+ if (value != NULL) {
+ indent2_os << i << ": ";
+ PrettyObjectValue(indent2_os, value->GetClass(), value);
+ } else {
+ indent2_os << i << ": null\n";
+ }
}
}
}
}
- os_ << "\n";
+ os << "\n";
- os_ << "OAT LOCATION:\n" << std::flush;
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
Object* oat_location_object = image_header_.GetImageRoot(ImageHeader::kOatLocation);
std::string oat_location(oat_location_object->AsString()->ToModifiedUtf8());
- os_ << oat_location;
+ os << "OAT LOCATION: " << oat_location;
if (!host_prefix_.empty()) {
oat_location = host_prefix_ + oat_location;
- os_ << " (" << oat_location << ")";
+ os << " (" << oat_location << ")";
}
- os_ << "\n";
+ os << "\n";
const OatFile* oat_file = class_linker->FindOatFileFromOatLocation(oat_location);
if (oat_file == NULL) {
- os_ << "NOT FOUND\n";
+ os << "NOT FOUND\n";
return;
}
- os_ << "\n";
+ os << "\n";
stats_.oat_file_bytes = oat_file->Size();
@@ -672,7 +760,7 @@
stats_.oat_dex_file_sizes.push_back(entry);
}
- os_ << "OBJECTS:\n" << std::flush;
+ os << "OBJECTS:\n" << std::flush;
// Loop through all the image spaces and dump their objects.
Heap* heap = Runtime::Current()->GetHeap();
@@ -682,98 +770,105 @@
WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
heap->FlushAllocStack();
}
- ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
- // TODO: C++0x auto
- for (Spaces::const_iterator it = spaces.begin(); it != spaces.end(); ++it) {
- Space* space = *it;
- if (space->IsImageSpace()) {
- ImageSpace* image_space = space->AsImageSpace();
- image_space->GetLiveBitmap()->Walk(ImageDumper::Callback, this);
- os_ << "\n";
+ {
+ std::ostream* saved_os = os_;
+ Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
+ std::ostream indent_os(&indent_filter);
+ os_ = &indent_os;
+ ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
+ // TODO: C++0x auto
+ for (Spaces::const_iterator it = spaces.begin(); it != spaces.end(); ++it) {
+ Space* space = *it;
+ if (space->IsImageSpace()) {
+ ImageSpace* image_space = space->AsImageSpace();
+ image_space->GetLiveBitmap()->Walk(ImageDumper::Callback, this);
+ indent_os << "\n";
+ }
}
+ // Dump the large objects separately.
+ heap->GetLargeObjectsSpace()->GetLiveObjects()->Walk(ImageDumper::Callback, this);
+ indent_os << "\n";
+ os_ = saved_os;
}
- // Dump the large objects separately.
- heap->GetLargeObjectsSpace()->GetLiveObjects()->Walk(ImageDumper::Callback, this);
- os_ << "\n";
-
- os_ << "STATS:\n" << std::flush;
+ os << "STATS:\n" << std::flush;
UniquePtr<File> file(OS::OpenFile(image_filename_.c_str(), false));
stats_.file_bytes = file->Length();
size_t header_bytes = sizeof(ImageHeader);
stats_.header_bytes = header_bytes;
size_t alignment_bytes = RoundUp(header_bytes, kObjectAlignment) - header_bytes;
stats_.alignment_bytes += alignment_bytes;
- stats_.Dump(os_);
- os_ << "\n";
+ stats_.Dump(os);
+ os << "\n";
- os_ << std::flush;
+ os << std::flush;
- oat_dumper_->Dump(os_);
+ oat_dumper_->Dump(os);
}
private:
- static void PrettyObjectValue(std::string& summary, Class* type, Object* value)
+ static void PrettyObjectValue(std::ostream& os, Class* type, Object* value)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
CHECK(type != NULL);
if (value == NULL) {
- StringAppendF(&summary, "null %s\n", PrettyDescriptor(type).c_str());
+ os << StringPrintf("null %s\n", PrettyDescriptor(type).c_str());
} else if (type->IsStringClass()) {
String* string = value->AsString();
- StringAppendF(&summary, "%p String: \"%s\"\n", string, string->ToModifiedUtf8().c_str());
+ os << StringPrintf("%p String: %s\n", string,
+ PrintableString(string->ToModifiedUtf8()).c_str());
} else if (type->IsClassClass()) {
Class* klass = value->AsClass();
- StringAppendF(&summary, "%p Class: %s\n", klass, PrettyDescriptor(klass).c_str());
+ os << StringPrintf("%p Class: %s\n", klass, PrettyDescriptor(klass).c_str());
} else if (type->IsFieldClass()) {
Field* field = value->AsField();
- StringAppendF(&summary, "%p Field: %s\n", field, PrettyField(field).c_str());
+ os << StringPrintf("%p Field: %s\n", field, PrettyField(field).c_str());
} else if (type->IsMethodClass()) {
AbstractMethod* method = value->AsMethod();
- StringAppendF(&summary, "%p Method: %s\n", method, PrettyMethod(method).c_str());
+ os << StringPrintf("%p Method: %s\n", method, PrettyMethod(method).c_str());
} else {
- StringAppendF(&summary, "%p %s\n", value, PrettyDescriptor(type).c_str());
+ os << StringPrintf("%p %s\n", value, PrettyDescriptor(type).c_str());
}
}
- static void PrintField(std::string& summary, Field* field, Object* obj)
+ static void PrintField(std::ostream& os, Field* field, Object* obj)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
FieldHelper fh(field);
const char* descriptor = fh.GetTypeDescriptor();
- StringAppendF(&summary, "\t%s: ", fh.GetName());
+ os << StringPrintf("%s: ", fh.GetName());
if (descriptor[0] != 'L' && descriptor[0] != '[') {
Class* type = fh.GetType();
if (type->IsPrimitiveLong()) {
- StringAppendF(&summary, "%lld (0x%llx)\n", field->Get64(obj), field->Get64(obj));
+ os << StringPrintf("%lld (0x%llx)\n", field->Get64(obj), field->Get64(obj));
} else if (type->IsPrimitiveDouble()) {
- StringAppendF(&summary, "%f (%a)\n", field->GetDouble(obj), field->GetDouble(obj));
+ os << StringPrintf("%f (%a)\n", field->GetDouble(obj), field->GetDouble(obj));
} else if (type->IsPrimitiveFloat()) {
- StringAppendF(&summary, "%f (%a)\n", field->GetFloat(obj), field->GetFloat(obj));
+ os << StringPrintf("%f (%a)\n", field->GetFloat(obj), field->GetFloat(obj));
} else {
DCHECK(type->IsPrimitive());
- StringAppendF(&summary, "%d (0x%x)\n", field->Get32(obj), field->Get32(obj));
+ os << StringPrintf("%d (0x%x)\n", field->Get32(obj), field->Get32(obj));
}
} else {
// Get the value, don't compute the type unless it is non-null as we don't want
// to cause class loading.
Object* value = field->GetObj(obj);
if (value == NULL) {
- StringAppendF(&summary, "null %s\n", PrettyDescriptor(descriptor).c_str());
+ os << StringPrintf("null %s\n", PrettyDescriptor(descriptor).c_str());
} else {
- PrettyObjectValue(summary, fh.GetType(), value);
+ PrettyObjectValue(os, fh.GetType(), value);
}
}
}
- static void DumpFields(std::string& summary, Object* obj, Class* klass)
+ static void DumpFields(std::ostream& os, Object* obj, Class* klass)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
Class* super = klass->GetSuperClass();
if (super != NULL) {
- DumpFields(summary, obj, super);
+ DumpFields(os, obj, super);
}
ObjectArray<Field>* fields = klass->GetIFields();
if (fields != NULL) {
for (int32_t i = 0; i < fields->GetLength(); i++) {
Field* field = fields->Get(i);
- PrintField(summary, field, obj);
+ PrintField(os, field, obj);
}
}
}
@@ -827,31 +922,30 @@
state->stats_.object_bytes += object_bytes;
state->stats_.alignment_bytes += alignment_bytes;
- std::string summary;
+ std::ostream& os = *state->os_;
Class* obj_class = obj->GetClass();
if (obj_class->IsArrayClass()) {
- StringAppendF(&summary, "%p: %s length:%d\n", obj, PrettyDescriptor(obj_class).c_str(),
- obj->AsArray()->GetLength());
+ os << StringPrintf("%p: %s length:%d\n", obj, PrettyDescriptor(obj_class).c_str(),
+ obj->AsArray()->GetLength());
} else if (obj->IsClass()) {
Class* klass = obj->AsClass();
- StringAppendF(&summary, "%p: java.lang.Class \"%s\" (", obj,
- PrettyDescriptor(klass).c_str());
- std::ostringstream ss;
- ss << klass->GetStatus() << ")\n";
- summary += ss.str();
+ os << StringPrintf("%p: java.lang.Class \"%s\" (", obj, PrettyDescriptor(klass).c_str())
+ << klass->GetStatus() << ")\n";
} else if (obj->IsField()) {
- StringAppendF(&summary, "%p: java.lang.reflect.Field %s\n", obj,
- PrettyField(obj->AsField()).c_str());
+ os << StringPrintf("%p: java.lang.reflect.Field %s\n", obj,
+ PrettyField(obj->AsField()).c_str());
} else if (obj->IsMethod()) {
- StringAppendF(&summary, "%p: java.lang.reflect.Method %s\n", obj,
- PrettyMethod(obj->AsMethod()).c_str());
+ os << StringPrintf("%p: java.lang.reflect.Method %s\n", obj,
+ PrettyMethod(obj->AsMethod()).c_str());
} else if (obj_class->IsStringClass()) {
- StringAppendF(&summary, "%p: java.lang.String %s\n", obj,
- PrintableString(obj->AsString()->ToModifiedUtf8()).c_str());
+ os << StringPrintf("%p: java.lang.String %s\n", obj,
+ PrintableString(obj->AsString()->ToModifiedUtf8()).c_str());
} else {
- StringAppendF(&summary, "%p: %s\n", obj, PrettyDescriptor(obj_class).c_str());
+ os << StringPrintf("%p: %s\n", obj, PrettyDescriptor(obj_class).c_str());
}
- DumpFields(summary, obj, obj_class);
+ Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
+ std::ostream indent_os(&indent_filter);
+ DumpFields(indent_os, obj, obj_class);
if (obj->IsObjectArray()) {
ObjectArray<Object>* obj_array = obj->AsObjectArray<Object>();
int32_t length = obj_array->GetLength();
@@ -866,21 +960,23 @@
}
}
if (run == 0) {
- StringAppendF(&summary, "\t%d: ", i);
+ indent_os << StringPrintf("%d: ", i);
} else {
- StringAppendF(&summary, "\t%d to %zd: ", i, i + run);
+ indent_os << StringPrintf("%d to %zd: ", i, i + run);
i = i + run;
}
Class* value_class = value == NULL ? obj_class->GetComponentType() : value->GetClass();
- PrettyObjectValue(summary, value_class, value);
+ PrettyObjectValue(indent_os, value_class, value);
}
} else if (obj->IsClass()) {
ObjectArray<Field>* sfields = obj->AsClass()->GetSFields();
if (sfields != NULL) {
- summary += "\t\tSTATICS:\n";
+ indent_os << "STATICS:\n";
+ Indenter indent2_filter(indent_os.rdbuf(), kIndentChar, kIndentBy1Count);
+ std::ostream indent2_os(&indent2_filter);
for (int32_t i = 0; i < sfields->GetLength(); i++) {
Field* field = sfields->Get(i);
- PrintField(summary, field, field->GetDeclaringClass());
+ PrintField(indent2_os, field, field->GetDeclaringClass());
}
}
} else if (obj->IsMethod()) {
@@ -901,7 +997,7 @@
state->stats_.native_to_managed_code_bytes += oat_code_size;
}
if (oat_code != method->GetCode()) {
- StringAppendF(&summary, "\t\tOAT CODE: %p\n", oat_code);
+ indent_os << StringPrintf("OAT CODE: %p\n", oat_code);
}
} else if (method->IsAbstract() || method->IsCalleeSaveMethod() ||
method->IsResolutionMethod()) {
@@ -958,9 +1054,9 @@
}
state->stats_.managed_code_bytes_ignoring_deduplication += oat_code_size;
- StringAppendF(&summary, "\t\tOAT CODE: %p-%p\n", oat_code_begin, oat_code_end);
- StringAppendF(&summary, "\t\tSIZE: Dex Instructions=%zd GC=%zd Mapping=%zd\n",
- dex_instruction_bytes, gc_map_bytes, pc_mapping_table_bytes);
+ indent_os << StringPrintf("OAT CODE: %p-%p\n", oat_code_begin, oat_code_end);
+ indent_os << StringPrintf("SIZE: Dex Instructions=%zd GC=%zd Mapping=%zd\n",
+ dex_instruction_bytes, gc_map_bytes, pc_mapping_table_bytes);
size_t total_size = dex_instruction_bytes + gc_map_bytes + pc_mapping_table_bytes +
vmap_table_bytes + invoke_stub_size + oat_code_size + object_bytes;
@@ -971,8 +1067,6 @@
}
}
state->stats_.Update(ClassHelper(obj_class).GetDescriptor(), object_bytes);
-
- state->os_ << summary << std::flush;
}
std::set<const void*> already_seen_;
@@ -1115,7 +1209,7 @@
os << "\nBig methods (size > " << i << " standard deviations the norm):\n";
first = false;
}
- os << "\t" << PrettyMethod(method_outlier[j]) << " requires storage of "
+ os << PrettyMethod(method_outlier[j]) << " requires storage of "
<< PrettySize(cur_size) << "\n";
method_outlier_size[j] = 0; // don't consider this method again
dumped_values++;
@@ -1125,7 +1219,7 @@
}
}
if (skipped_values > 0) {
- os << "\t... skipped " << skipped_values
+ os << "... skipped " << skipped_values
<< " methods with size > 1 standard deviation from the norm\n";
}
os << std::flush;
@@ -1155,7 +1249,7 @@
<< " standard deviations the norm):\n";
first = false;
}
- os << "\t" << PrettyMethod(method_outlier[j]) << " expanded code by "
+ os << PrettyMethod(method_outlier[j]) << " expanded code by "
<< cur_expansion << "\n";
method_outlier_expansion[j] = 0.0; // don't consider this method again
dumped_values++;
@@ -1165,33 +1259,36 @@
}
}
if (skipped_values > 0) {
- os << "\t... skipped " << skipped_values
+ os << "... skipped " << skipped_values
<< " methods with expansion > 1 standard deviation from the norm\n";
}
os << "\n" << std::flush;
}
void Dump(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- os << "\tart_file_bytes = " << PrettySize(file_bytes) << "\n\n"
- << "\tart_file_bytes = header_bytes + object_bytes + alignment_bytes\n"
- << StringPrintf("\theader_bytes = %8zd (%2.0f%% of art file bytes)\n"
- "\tobject_bytes = %8zd (%2.0f%% of art file bytes)\n"
- "\talignment_bytes = %8zd (%2.0f%% of art file bytes)\n\n",
- header_bytes, PercentOfFileBytes(header_bytes),
- object_bytes, PercentOfFileBytes(object_bytes),
- alignment_bytes, PercentOfFileBytes(alignment_bytes))
- << std::flush;
+ {
+ os << "art_file_bytes = " << PrettySize(file_bytes) << "\n\n"
+ << "art_file_bytes = header_bytes + object_bytes + alignment_bytes\n";
+ Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
+ std::ostream indent_os(&indent_filter);
+ indent_os << StringPrintf("header_bytes = %8zd (%2.0f%% of art file bytes)\n"
+ "object_bytes = %8zd (%2.0f%% of art file bytes)\n"
+ "alignment_bytes = %8zd (%2.0f%% of art file bytes)\n\n",
+ header_bytes, PercentOfFileBytes(header_bytes),
+ object_bytes, PercentOfFileBytes(object_bytes),
+ alignment_bytes, PercentOfFileBytes(alignment_bytes))
+ << std::flush;
+ CHECK_EQ(file_bytes, header_bytes + object_bytes + alignment_bytes);
+ }
- CHECK_EQ(file_bytes, header_bytes + object_bytes + alignment_bytes);
-
- os << "\tobject_bytes breakdown:\n";
+ os << "object_bytes breakdown:\n";
size_t object_bytes_total = 0;
typedef SizeAndCountTable::const_iterator It; // TODO: C++0x auto
for (It it = sizes_and_counts.begin(), end = sizes_and_counts.end(); it != end; ++it) {
const std::string& descriptor(it->first);
double average = static_cast<double>(it->second.bytes) / static_cast<double>(it->second.count);
double percent = PercentOfObjectBytes(it->second.bytes);
- os << StringPrintf("\t%32s %8zd bytes %6zd instances "
+ os << StringPrintf("%32s %8zd bytes %6zd instances "
"(%4.0f bytes/instance) %2.0f%% of object_bytes\n",
descriptor.c_str(), it->second.bytes, it->second.count,
average, percent);
@@ -1200,13 +1297,13 @@
os << "\n" << std::flush;
CHECK_EQ(object_bytes, object_bytes_total);
- os << StringPrintf("\toat_file_bytes = %8zd\n"
- "\tmanaged_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
- "\tmanaged_to_native_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
- "\tnative_to_managed_code_bytes = %8zd (%2.0f%% of oat file bytes)\n\n"
- "\tclass_initializer_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
- "\tlarge_initializer_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
- "\tlarge_method_code_bytes = %8zd (%2.0f%% of oat file bytes)\n\n",
+ os << StringPrintf("oat_file_bytes = %8zd\n"
+ "managed_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
+ "managed_to_native_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
+ "native_to_managed_code_bytes = %8zd (%2.0f%% of oat file bytes)\n\n"
+ "class_initializer_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
+ "large_initializer_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
+ "large_method_code_bytes = %8zd (%2.0f%% of oat file bytes)\n\n",
oat_file_bytes,
managed_code_bytes, PercentOfOatBytes(managed_code_bytes),
managed_to_native_code_bytes, PercentOfOatBytes(managed_to_native_code_bytes),
@@ -1214,27 +1311,24 @@
class_initializer_code_bytes, PercentOfOatBytes(class_initializer_code_bytes),
large_initializer_code_bytes, PercentOfOatBytes(large_initializer_code_bytes),
large_method_code_bytes, PercentOfOatBytes(large_method_code_bytes))
- << std::flush;
-
- os << "\tDexFile sizes:\n";
+ << "DexFile sizes:\n";
typedef std::vector<std::pair<std::string, size_t> >::const_iterator It2;
for (It2 it = oat_dex_file_sizes.begin(); it != oat_dex_file_sizes.end();
++it) {
- os << StringPrintf("\t%s = %zd (%2.0f%% of oat file bytes)\n",
- it->first.c_str(), it->second,
- PercentOfOatBytes(it->second));
+ os << StringPrintf("%s = %zd (%2.0f%% of oat file bytes)\n",
+ it->first.c_str(), it->second, PercentOfOatBytes(it->second));
}
- os << "\n" << StringPrintf("\tgc_map_bytes = %7zd (%2.0f%% of oat file bytes)\n"
- "\tpc_mapping_table_bytes = %7zd (%2.0f%% of oat file bytes)\n"
- "\tvmap_table_bytes = %7zd (%2.0f%% of oat file bytes)\n\n",
+ os << "\n" << StringPrintf("gc_map_bytes = %7zd (%2.0f%% of oat file bytes)\n"
+ "pc_mapping_table_bytes = %7zd (%2.0f%% of oat file bytes)\n"
+ "vmap_table_bytes = %7zd (%2.0f%% of oat file bytes)\n\n",
gc_map_bytes, PercentOfOatBytes(gc_map_bytes),
pc_mapping_table_bytes, PercentOfOatBytes(pc_mapping_table_bytes),
vmap_table_bytes, PercentOfOatBytes(vmap_table_bytes))
<< std::flush;
- os << StringPrintf("\tdex_instruction_bytes = %zd\n", dex_instruction_bytes)
- << StringPrintf("\tmanaged_code_bytes expansion = %.2f (ignoring deduplication %.2f)\n\n",
+ os << StringPrintf("dex_instruction_bytes = %zd\n", dex_instruction_bytes)
+ << StringPrintf("managed_code_bytes expansion = %.2f (ignoring deduplication %.2f)\n\n",
static_cast<double>(managed_code_bytes) / static_cast<double>(dex_instruction_bytes),
static_cast<double>(managed_code_bytes_ignoring_deduplication) /
static_cast<double>(dex_instruction_bytes))
@@ -1254,7 +1348,7 @@
kLargeMethodDexBytes = 16000
};
UniquePtr<OatDumper> oat_dumper_;
- std::ostream& os_;
+ std::ostream* os_;
const std::string image_filename_;
const std::string host_prefix_;
Space& image_space_;
@@ -1376,7 +1470,7 @@
fprintf(stderr, "Invalid image header %s\n", image_filename);
return EXIT_FAILURE;
}
- ImageDumper image_dumper(*os, image_filename, *host_prefix.get(), *image_space, image_header);
+ ImageDumper image_dumper(os, image_filename, *host_prefix.get(), *image_space, image_header);
image_dumper.Dump();
return EXIT_SUCCESS;
}
diff --git a/src/stack.cc b/src/stack.cc
index 8a741c6..f712652 100644
--- a/src/stack.cc
+++ b/src/stack.cc
@@ -64,49 +64,38 @@
return GetMethod()->NativePcOffset(cur_quick_frame_pc_);
}
-
-uint32_t StackVisitor::GetVReg(AbstractMethod* m, int vreg) const {
- DCHECK(m == GetMethod());
+uint32_t StackVisitor::GetVReg(AbstractMethod* m, uint16_t vreg, VRegKind kind) const {
if (cur_quick_frame_ != NULL) {
DCHECK(context_ != NULL); // You can't reliably read registers without a context.
- uint32_t core_spills = m->GetCoreSpillMask();
+ DCHECK(m == GetMethod());
const VmapTable vmap_table(m->GetVmapTableRaw());
uint32_t vmap_offset;
// TODO: IsInContext stops before spotting floating point registers.
- if (vmap_table.IsInContext(vreg, vmap_offset)) {
- // Compute the register we need to load from the context.
- uint32_t spill_mask = core_spills;
- CHECK_LT(vmap_offset, static_cast<uint32_t>(__builtin_popcount(spill_mask)));
- uint32_t matches = 0;
- uint32_t spill_shifts = 0;
- while (matches != (vmap_offset + 1)) {
- DCHECK_NE(spill_mask, 0u);
- matches += spill_mask & 1; // Add 1 if the low bit is set.
- spill_mask >>= 1;
- spill_shifts++;
- }
- spill_shifts--; // Wind back one as we want the last match.
- return GetGPR(spill_shifts);
+ if (vmap_table.IsInContext(vreg, vmap_offset, kind)) {
+ bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg);
+ uint32_t spill_mask = is_float ? m->GetFpSpillMask()
+ : m->GetCoreSpillMask();
+ return GetGPR(vmap_table.ComputeRegister(spill_mask, vmap_offset, kind));
} else {
const DexFile::CodeItem* code_item = MethodHelper(m).GetCodeItem();
DCHECK(code_item != NULL) << PrettyMethod(m); // Can't be NULL or how would we compile its instructions?
- uint32_t fp_spills = m->GetFpSpillMask();
size_t frame_size = m->GetFrameSizeInBytes();
- return GetVReg(cur_quick_frame_, code_item, core_spills, fp_spills, frame_size, vreg);
+ return GetVReg(cur_quick_frame_, code_item, m->GetCoreSpillMask(), m->GetFpSpillMask(),
+ frame_size, vreg);
}
} else {
return cur_shadow_frame_->GetVReg(vreg);
}
}
-void StackVisitor::SetVReg(AbstractMethod* m, int vreg, uint32_t new_value) {
+void StackVisitor::SetVReg(AbstractMethod* m, uint16_t vreg, uint32_t new_value, VRegKind kind) {
if (cur_quick_frame_ != NULL) {
DCHECK(context_ != NULL); // You can't reliably write registers without a context.
DCHECK(m == GetMethod());
const VmapTable vmap_table(m->GetVmapTableRaw());
uint32_t vmap_offset;
// TODO: IsInContext stops before spotting floating point registers.
- if (vmap_table.IsInContext(vreg, vmap_offset)) {
+ if (vmap_table.IsInContext(vreg, vmap_offset, kind)) {
UNIMPLEMENTED(FATAL);
}
const DexFile::CodeItem* code_item = MethodHelper(m).GetCodeItem();
@@ -118,7 +107,7 @@
byte* vreg_addr = reinterpret_cast<byte*>(GetCurrentQuickFrame()) + offset;
*reinterpret_cast<uint32_t*>(vreg_addr) = new_value;
} else {
- LOG(FATAL) << "Unimplemented - shadow frame SetVReg";
+ return cur_shadow_frame_->SetVReg(vreg, new_value);
}
}
@@ -129,7 +118,7 @@
uintptr_t StackVisitor::GetReturnPc() const {
AbstractMethod** sp = GetCurrentQuickFrame();
- CHECK(sp != NULL);
+ DCHECK(sp != NULL);
byte* pc_addr = reinterpret_cast<byte*>(sp) + GetMethod()->GetReturnPcOffsetInBytes();
return *reinterpret_cast<uintptr_t*>(pc_addr);
}
diff --git a/src/stack.h b/src/stack.h
index fd2fd15..5b5609c 100644
--- a/src/stack.h
+++ b/src/stack.h
@@ -29,12 +29,27 @@
namespace art {
class AbstractMethod;
+class Context;
class Object;
class ShadowFrame;
class StackIndirectReferenceTable;
class ScopedObjectAccess;
class Thread;
+// The kind of vreg being accessed in calls to Set/GetVReg.
+enum VRegKind {
+ kReferenceVReg,
+ kIntVReg,
+ kFloatVReg,
+ kLongLoVReg,
+ kLongHiVReg,
+ kDoubleLoVReg,
+ kDoubleHiVReg,
+ kConstant,
+ kImpreciseConstant,
+ kUndefined,
+};
+
class ShadowFrame {
public:
static ShadowFrame* Create(uint16_t num_refs, uint16_t num_vregs, ShadowFrame* link,
@@ -366,15 +381,17 @@
return num_frames_;
}
- uint32_t GetVReg(AbstractMethod* m, int vreg) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ uint32_t GetVReg(AbstractMethod* m, uint16_t vreg, VRegKind kind) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- void SetVReg(AbstractMethod* m, int vreg, uint32_t new_value)
+ void SetVReg(AbstractMethod* m, uint16_t vreg, uint32_t new_value, VRegKind kind)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
uintptr_t GetGPR(uint32_t reg) const;
uint32_t GetVReg(AbstractMethod** cur_quick_frame, const DexFile::CodeItem* code_item,
- uint32_t core_spills, uint32_t fp_spills, size_t frame_size, int vreg) const {
+ uint32_t core_spills, uint32_t fp_spills, size_t frame_size,
+ uint16_t vreg) const {
int offset = GetVRegOffset(code_item, core_spills, fp_spills, frame_size, vreg);
DCHECK_EQ(cur_quick_frame, GetCurrentQuickFrame());
byte* vreg_addr = reinterpret_cast<byte*>(cur_quick_frame) + offset;
@@ -482,6 +499,77 @@
Context* const context_;
};
+class VmapTable {
+ public:
+ explicit VmapTable(const uint16_t* table) : table_(table) {
+ }
+
+ uint16_t operator[](size_t i) const {
+ return table_[i + 1];
+ }
+
+ size_t size() const {
+ return table_[0];
+ }
+
+ // Is the dex register 'vreg' in the context or on the stack? Should not be called when the
+ // 'kind' is unknown or constant.
+ bool IsInContext(size_t vreg, uint32_t& vmap_offset, VRegKind kind) const {
+ DCHECK(kind == kReferenceVReg || kind == kIntVReg || kind == kFloatVReg ||
+ kind == kLongLoVReg || kind == kLongHiVReg || kind == kDoubleLoVReg ||
+ kind == kDoubleHiVReg || kind == kImpreciseConstant);
+ vmap_offset = 0xEBAD0FF5;
+ // TODO: take advantage of the registers being ordered
+ // TODO: we treat kImpreciseConstant as an integer below, need to ensure that such values
+ // are never promoted to floating point registers.
+ bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg);
+ bool in_floats = false;
+ for (size_t i = 0; i < size(); ++i) {
+ // Stop if we find what we are are looking for.
+ if ((table_[i + 1] == vreg) && (in_floats == is_float)) {
+ vmap_offset = i;
+ return true;
+ }
+ // 0xffff is the marker for LR (return PC on x86), following it are spilled float registers.
+ if (table_[i + 1] == 0xffff) {
+ in_floats = true;
+ }
+ }
+ return false;
+ }
+
+ // Compute the register number that corresponds to the entry in the vmap (vmap_offset, computed
+ // by IsInContext above). If the kind is floating point then the result will be a floating point
+ // register number, otherwise it will be an integer register number.
+ uint32_t ComputeRegister(uint32_t spill_mask, uint32_t vmap_offset, VRegKind kind) const {
+ // Compute the register we need to load from the context.
+ DCHECK(kind == kReferenceVReg || kind == kIntVReg || kind == kFloatVReg ||
+ kind == kLongLoVReg || kind == kLongHiVReg || kind == kDoubleLoVReg ||
+ kind == kDoubleHiVReg || kind == kImpreciseConstant);
+ // TODO: we treat kImpreciseConstant as an integer below, need to ensure that such values
+ // are never promoted to floating point registers.
+ bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg);
+ uint32_t matches = 0;
+ if (is_float) {
+ while (table_[matches] != 0xffff) {
+ matches++;
+ }
+ }
+ CHECK_LT(vmap_offset - matches, static_cast<uint32_t>(__builtin_popcount(spill_mask)));
+ uint32_t spill_shifts = 0;
+ while (matches != (vmap_offset + 1)) {
+ DCHECK_NE(spill_mask, 0u);
+ matches += spill_mask & 1; // Add 1 if the low bit is set
+ spill_mask >>= 1;
+ spill_shifts++;
+ }
+ spill_shifts--; // wind back one as we want the last match
+ return spill_shifts;
+ }
+ private:
+ const uint16_t* table_;
+};
+
} // namespace art
#endif // ART_SRC_STACK_H_
diff --git a/src/thread.cc b/src/thread.cc
index 0d06e42..943fdcb 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -1883,20 +1883,10 @@
if (TestBitmap(reg, reg_bitmap)) {
uint32_t vmap_offset;
Object* ref;
- if (vmap_table.IsInContext(reg, vmap_offset)) {
- // Compute the register we need to load from the context
- uint32_t spill_mask = core_spills;
- CHECK_LT(vmap_offset, static_cast<uint32_t>(__builtin_popcount(spill_mask)));
- uint32_t matches = 0;
- uint32_t spill_shifts = 0;
- while (matches != (vmap_offset + 1)) {
- DCHECK_NE(spill_mask, 0u);
- matches += spill_mask & 1; // Add 1 if the low bit is set
- spill_mask >>= 1;
- spill_shifts++;
- }
- spill_shifts--; // wind back one as we want the last match
- ref = reinterpret_cast<Object*>(GetGPR(spill_shifts));
+ if (vmap_table.IsInContext(reg, vmap_offset, kReferenceVReg)) {
+ uintptr_t val = GetGPR(vmap_table.ComputeRegister(core_spills, vmap_offset,
+ kReferenceVReg));
+ ref = reinterpret_cast<Object*>(val);
} else {
ref = reinterpret_cast<Object*>(GetVReg(cur_quick_frame, code_item, core_spills,
fp_spills, frame_size, reg));
diff --git a/src/verifier/method_verifier.cc b/src/verifier/method_verifier.cc
index 2149490..d6cd665dd 100644
--- a/src/verifier/method_verifier.cc
+++ b/src/verifier/method_verifier.cc
@@ -25,6 +25,7 @@
#include "dex_instruction.h"
#include "dex_instruction_visitor.h"
#include "verifier/dex_gc_map.h"
+#include "indenter.h"
#include "intern_table.h"
#include "leb128.h"
#include "logging.h"
@@ -324,22 +325,37 @@
return result;
}
-void MethodVerifier::VerifyMethodAndDump(AbstractMethod* method) {
- CHECK(method != NULL);
- MethodHelper mh(method);
- MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(),
- mh.GetClassDefIndex(), mh.GetCodeItem(), method->GetDexMethodIndex(),
- method, method->GetAccessFlags());
+void MethodVerifier::VerifyMethodAndDump(std::ostream& os, uint32_t dex_method_idx,
+ const DexFile* dex_file, DexCache* dex_cache,
+ ClassLoader* class_loader, uint32_t class_def_idx,
+ const DexFile::CodeItem* code_item, AbstractMethod* method,
+ uint32_t method_access_flags) {
+ MethodVerifier verifier(dex_file, dex_cache, class_loader, class_def_idx, code_item,
+ dex_method_idx, method, method_access_flags);
verifier.Verify();
- verifier.DumpFailures(LOG(INFO) << "Dump of method " << PrettyMethod(method) << "\n")
- << verifier.info_messages_.str() << MutatorLockedDumpable<MethodVerifier>(verifier);
+ verifier.DumpFailures(os);
+ os << verifier.info_messages_.str();
+ verifier.Dump(os);
+}
+
+std::vector<int32_t> MethodVerifier::DescribeVRegs(uint32_t dex_method_idx,
+ const DexFile* dex_file, DexCache* dex_cache,
+ ClassLoader* class_loader,
+ uint32_t class_def_idx,
+ const DexFile::CodeItem* code_item,
+ AbstractMethod* method,
+ uint32_t method_access_flags, uint32_t dex_pc) {
+ MethodVerifier verifier(dex_file, dex_cache, class_loader, class_def_idx, code_item,
+ dex_method_idx, method, method_access_flags);
+ verifier.Verify();
+ return verifier.DescribeVRegs(dex_pc);
}
MethodVerifier::MethodVerifier(const DexFile* dex_file, DexCache* dex_cache,
ClassLoader* class_loader, uint32_t class_def_idx, const DexFile::CodeItem* code_item,
- uint32_t method_idx, AbstractMethod* method, uint32_t method_access_flags)
+ uint32_t dex_method_idx, AbstractMethod* method, uint32_t method_access_flags)
: work_insn_idx_(-1),
- method_idx_(method_idx),
+ dex_method_idx_(dex_method_idx),
foo_method_(method),
method_access_flags_(method_access_flags),
dex_file_(dex_file),
@@ -355,7 +371,8 @@
monitor_enter_count_(0) {
}
-void MethodVerifier::FindLocksAtDexPc(AbstractMethod* m, uint32_t dex_pc, std::vector<uint32_t>& monitor_enter_dex_pcs) {
+void MethodVerifier::FindLocksAtDexPc(AbstractMethod* m, uint32_t dex_pc,
+ std::vector<uint32_t>& monitor_enter_dex_pcs) {
MethodHelper mh(m);
MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(),
mh.GetClassDefIndex(), mh.GetCodeItem(), m->GetDexMethodIndex(),
@@ -446,7 +463,7 @@
}
}
failures_.push_back(error);
- std::string location(StringPrintf("%s: [0x%X]", PrettyMethod(method_idx_, *dex_file_).c_str(),
+ std::string location(StringPrintf("%s: [0x%X]", PrettyMethod(dex_method_idx_, *dex_file_).c_str(),
work_insn_idx_));
std::ostringstream* failure_message = new std::ostringstream(location);
failure_messages_.push_back(failure_message);
@@ -1000,7 +1017,7 @@
if (!SetTypesFromSignature()) {
DCHECK_NE(failures_.size(), 0U);
std::string prepend("Bad signature in ");
- prepend += PrettyMethod(method_idx_, *dex_file_);
+ prepend += PrettyMethod(dex_method_idx_, *dex_file_);
PrependToLastFailMessage(prepend);
return false;
}
@@ -1010,7 +1027,7 @@
return false;
}
- Compiler::MethodReference ref(dex_file_, method_idx_);
+ Compiler::MethodReference ref(dex_file_, dex_method_idx_);
#if !defined(ART_USE_LLVM_COMPILER)
@@ -1054,17 +1071,28 @@
os << "Native method\n";
return;
}
- reg_types_.Dump(os);
+ {
+ os << "Register Types:\n";
+ Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
+ std::ostream indent_os(&indent_filter);
+ reg_types_.Dump(indent_os);
+ }
os << "Dumping instructions and register lines:\n";
+ Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
+ std::ostream indent_os(&indent_filter);
const Instruction* inst = Instruction::At(code_item_->insns_);
for (size_t dex_pc = 0; dex_pc < code_item_->insns_size_in_code_units_;
dex_pc += insn_flags_[dex_pc].GetLengthInCodeUnits()) {
- os << StringPrintf("0x%04zx", dex_pc) << ": " << insn_flags_[dex_pc].Dump()
- << " " << inst->DumpHex(5) << " " << inst->DumpString(dex_file_) << "\n";
RegisterLine* reg_line = reg_table_.GetLine(dex_pc);
if (reg_line != NULL) {
- os << reg_line->Dump() << "\n";
+ indent_os << reg_line->Dump() << "\n";
}
+ indent_os << StringPrintf("0x%04zx", dex_pc) << ": " << insn_flags_[dex_pc].Dump() << " ";
+ const bool kDumpHexOfInstruction = false;
+ if (kDumpHexOfInstruction) {
+ indent_os << inst->DumpHex(5) << " ";
+ }
+ indent_os << inst->DumpString(dex_file_) << "\n";
inst = inst->Next();
}
}
@@ -1108,7 +1136,7 @@
}
const DexFile::ProtoId& proto_id =
- dex_file_->GetMethodPrototype(dex_file_->GetMethodId(method_idx_));
+ dex_file_->GetMethodPrototype(dex_file_->GetMethodId(dex_method_idx_));
DexFileParameterIterator iterator(*dex_file_, proto_id);
for (; iterator.HasNext(); iterator.Next()) {
@@ -1153,8 +1181,9 @@
break;
case 'J':
case 'D': {
- const RegType& low_half = descriptor[0] == 'J' ? reg_types_.Long() : reg_types_.Double();
- reg_line->SetRegisterType(arg_start + cur_arg, low_half); // implicitly sets high-register
+ const RegType& lo_half = descriptor[0] == 'J' ? reg_types_.LongLo() : reg_types_.DoubleLo();
+ const RegType& hi_half = descriptor[0] == 'J' ? reg_types_.LongHi() : reg_types_.DoubleHi();
+ reg_line->SetRegisterTypeWide(arg_start + cur_arg, lo_half, hi_half);
cur_arg++;
break;
}
@@ -1250,7 +1279,7 @@
if (work_line_->CompareLine(register_line) != 0) {
Dump(std::cout);
std::cout << info_messages_.str();
- LOG(FATAL) << "work_line diverged in " << PrettyMethod(method_idx_, *dex_file_)
+ LOG(FATAL) << "work_line diverged in " << PrettyMethod(dex_method_idx_, *dex_file_)
<< "@" << reinterpret_cast<void*>(work_insn_idx_) << "\n"
<< " work_line=" << *work_line_ << "\n"
<< " expected=" << *register_line;
@@ -1259,7 +1288,7 @@
#endif
}
if (!CodeFlowVerifyInstruction(&start_guess)) {
- std::string prepend(PrettyMethod(method_idx_, *dex_file_));
+ std::string prepend(PrettyMethod(dex_method_idx_, *dex_file_));
prepend += " failed to verify: ";
PrependToLastFailMessage(prepend);
return false;
@@ -1500,32 +1529,51 @@
}
break;
- case Instruction::CONST_4:
/* could be boolean, int, float, or a null reference */
+ case Instruction::CONST_4:
work_line_->SetRegisterType(dec_insn.vA,
- reg_types_.FromCat1Const((dec_insn.vB << 28) >> 28));
+ reg_types_.FromCat1Const(static_cast<int32_t>(dec_insn.vB << 28) >> 28, true));
break;
case Instruction::CONST_16:
- /* could be boolean, int, float, or a null reference */
work_line_->SetRegisterType(dec_insn.vA,
- reg_types_.FromCat1Const(static_cast<int16_t>(dec_insn.vB)));
+ reg_types_.FromCat1Const(static_cast<int16_t>(dec_insn.vB), true));
break;
case Instruction::CONST:
- /* could be boolean, int, float, or a null reference */
- work_line_->SetRegisterType(dec_insn.vA, reg_types_.FromCat1Const(dec_insn.vB));
+ work_line_->SetRegisterType(dec_insn.vA, reg_types_.FromCat1Const(dec_insn.vB, true));
break;
case Instruction::CONST_HIGH16:
- /* could be boolean, int, float, or a null reference */
work_line_->SetRegisterType(dec_insn.vA,
- reg_types_.FromCat1Const(dec_insn.vB << 16));
+ reg_types_.FromCat1Const(dec_insn.vB << 16, true));
break;
- case Instruction::CONST_WIDE_16:
- case Instruction::CONST_WIDE_32:
- case Instruction::CONST_WIDE:
- case Instruction::CONST_WIDE_HIGH16:
/* could be long or double; resolved upon use */
- work_line_->SetRegisterType(dec_insn.vA, reg_types_.ConstLo());
+ case Instruction::CONST_WIDE_16: {
+ int64_t val = static_cast<int16_t>(dec_insn.vB);
+ const RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true);
+ const RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
+ work_line_->SetRegisterTypeWide(dec_insn.vA, lo, hi);
break;
+ }
+ case Instruction::CONST_WIDE_32: {
+ int64_t val = static_cast<int32_t>(dec_insn.vB);
+ const RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true);
+ const RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
+ work_line_->SetRegisterTypeWide(dec_insn.vA, lo, hi);
+ break;
+ }
+ case Instruction::CONST_WIDE: {
+ int64_t val = dec_insn.vB_wide;
+ const RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true);
+ const RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
+ work_line_->SetRegisterTypeWide(dec_insn.vA, lo, hi);
+ break;
+ }
+ case Instruction::CONST_WIDE_HIGH16: {
+ int64_t val = static_cast<uint64_t>(dec_insn.vB) << 48;
+ const RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true);
+ const RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
+ work_line_->SetRegisterTypeWide(dec_insn.vA, lo, hi);
+ break;
+ }
case Instruction::CONST_STRING:
case Instruction::CONST_STRING_JUMBO:
work_line_->SetRegisterType(dec_insn.vA, reg_types_.JavaLangString());
@@ -1658,19 +1706,23 @@
break;
case Instruction::CMPL_DOUBLE:
case Instruction::CMPG_DOUBLE:
- if (!work_line_->VerifyRegisterType(dec_insn.vB, reg_types_.Double())) {
+ if (!work_line_->VerifyRegisterTypeWide(dec_insn.vB, reg_types_.DoubleLo(),
+ reg_types_.DoubleHi())) {
break;
}
- if (!work_line_->VerifyRegisterType(dec_insn.vC, reg_types_.Double())) {
+ if (!work_line_->VerifyRegisterTypeWide(dec_insn.vC, reg_types_.DoubleLo(),
+ reg_types_.DoubleHi())) {
break;
}
work_line_->SetRegisterType(dec_insn.vA, reg_types_.Integer());
break;
case Instruction::CMP_LONG:
- if (!work_line_->VerifyRegisterType(dec_insn.vB, reg_types_.Long())) {
+ if (!work_line_->VerifyRegisterTypeWide(dec_insn.vB, reg_types_.LongLo(),
+ reg_types_.LongHi())) {
break;
}
- if (!work_line_->VerifyRegisterType(dec_insn.vC, reg_types_.Long())) {
+ if (!work_line_->VerifyRegisterTypeWide(dec_insn.vC, reg_types_.LongLo(),
+ reg_types_.LongHi())) {
break;
}
work_line_->SetRegisterType(dec_insn.vA, reg_types_.Integer());
@@ -1792,7 +1844,7 @@
VerifyAGet(dec_insn, reg_types_.Integer(), true);
break;
case Instruction::AGET_WIDE:
- VerifyAGet(dec_insn, reg_types_.Long(), true);
+ VerifyAGet(dec_insn, reg_types_.LongLo(), true);
break;
case Instruction::AGET_OBJECT:
VerifyAGet(dec_insn, reg_types_.JavaLangObject(false), false);
@@ -1814,7 +1866,7 @@
VerifyAPut(dec_insn, reg_types_.Integer(), true);
break;
case Instruction::APUT_WIDE:
- VerifyAPut(dec_insn, reg_types_.Long(), true);
+ VerifyAPut(dec_insn, reg_types_.LongLo(), true);
break;
case Instruction::APUT_OBJECT:
VerifyAPut(dec_insn, reg_types_.JavaLangObject(false), false);
@@ -1836,7 +1888,7 @@
VerifyISGet(dec_insn, reg_types_.Integer(), true, false);
break;
case Instruction::IGET_WIDE:
- VerifyISGet(dec_insn, reg_types_.Long(), true, false);
+ VerifyISGet(dec_insn, reg_types_.LongLo(), true, false);
break;
case Instruction::IGET_OBJECT:
VerifyISGet(dec_insn, reg_types_.JavaLangObject(false), false, false);
@@ -1858,7 +1910,7 @@
VerifyISPut(dec_insn, reg_types_.Integer(), true, false);
break;
case Instruction::IPUT_WIDE:
- VerifyISPut(dec_insn, reg_types_.Long(), true, false);
+ VerifyISPut(dec_insn, reg_types_.LongLo(), true, false);
break;
case Instruction::IPUT_OBJECT:
VerifyISPut(dec_insn, reg_types_.JavaLangObject(false), false, false);
@@ -1880,7 +1932,7 @@
VerifyISGet(dec_insn, reg_types_.Integer(), true, true);
break;
case Instruction::SGET_WIDE:
- VerifyISGet(dec_insn, reg_types_.Long(), true, true);
+ VerifyISGet(dec_insn, reg_types_.LongLo(), true, true);
break;
case Instruction::SGET_OBJECT:
VerifyISGet(dec_insn, reg_types_.JavaLangObject(false), false, true);
@@ -1902,7 +1954,7 @@
VerifyISPut(dec_insn, reg_types_.Integer(), true, true);
break;
case Instruction::SPUT_WIDE:
- VerifyISPut(dec_insn, reg_types_.Long(), true, true);
+ VerifyISPut(dec_insn, reg_types_.LongLo(), true, true);
break;
case Instruction::SPUT_OBJECT:
VerifyISPut(dec_insn, reg_types_.JavaLangObject(false), false, true);
@@ -1927,7 +1979,11 @@
descriptor = MethodHelper(called_method).GetReturnTypeDescriptor();
}
const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor, false);
- work_line_->SetResultRegisterType(return_type);
+ if (!return_type.IsLowHalf()) {
+ work_line_->SetResultRegisterType(return_type);
+ } else {
+ work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(®_types_));
+ }
just_set_result = true;
break;
}
@@ -1989,7 +2045,11 @@
}
const RegType& return_type = reg_types_.FromDescriptor(class_loader_, return_type_descriptor,
false);
- work_line_->SetResultRegisterType(return_type);
+ if (!return_type.IsLowHalf()) {
+ work_line_->SetResultRegisterType(return_type);
+ } else {
+ work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(®_types_));
+ }
just_set_result = true;
break;
}
@@ -2007,7 +2067,11 @@
descriptor = MethodHelper(called_method).GetReturnTypeDescriptor();
}
const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor, false);
- work_line_->SetResultRegisterType(return_type);
+ if (!return_type.IsLowHalf()) {
+ work_line_->SetResultRegisterType(return_type);
+ } else {
+ work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(®_types_));
+ }
just_set_result = true;
}
break;
@@ -2057,8 +2121,11 @@
descriptor = MethodHelper(abs_method).GetReturnTypeDescriptor();
}
const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor, false);
- work_line_->SetResultRegisterType(return_type);
- work_line_->SetResultRegisterType(return_type);
+ if (!return_type.IsLowHalf()) {
+ work_line_->SetResultRegisterType(return_type);
+ } else {
+ work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(®_types_));
+ }
just_set_result = true;
break;
}
@@ -2068,49 +2135,61 @@
break;
case Instruction::NEG_LONG:
case Instruction::NOT_LONG:
- work_line_->CheckUnaryOp(dec_insn, reg_types_.Long(), reg_types_.Long());
+ work_line_->CheckUnaryOpWide(dec_insn, reg_types_.LongLo(), reg_types_.LongHi(),
+ reg_types_.LongLo(), reg_types_.LongHi());
break;
case Instruction::NEG_FLOAT:
work_line_->CheckUnaryOp(dec_insn, reg_types_.Float(), reg_types_.Float());
break;
case Instruction::NEG_DOUBLE:
- work_line_->CheckUnaryOp(dec_insn, reg_types_.Double(), reg_types_.Double());
+ work_line_->CheckUnaryOpWide(dec_insn, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
+ reg_types_.DoubleLo(), reg_types_.DoubleHi());
break;
case Instruction::INT_TO_LONG:
- work_line_->CheckUnaryOp(dec_insn, reg_types_.Long(), reg_types_.Integer());
+ work_line_->CheckUnaryOpToWide(dec_insn, reg_types_.LongLo(), reg_types_.LongHi(),
+ reg_types_.Integer());
break;
case Instruction::INT_TO_FLOAT:
work_line_->CheckUnaryOp(dec_insn, reg_types_.Float(), reg_types_.Integer());
break;
case Instruction::INT_TO_DOUBLE:
- work_line_->CheckUnaryOp(dec_insn, reg_types_.Double(), reg_types_.Integer());
+ work_line_->CheckUnaryOpToWide(dec_insn, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
+ reg_types_.Integer());
break;
case Instruction::LONG_TO_INT:
- work_line_->CheckUnaryOp(dec_insn, reg_types_.Integer(), reg_types_.Long());
+ work_line_->CheckUnaryOpFromWide(dec_insn, reg_types_.Integer(),
+ reg_types_.LongLo(), reg_types_.LongHi());
break;
case Instruction::LONG_TO_FLOAT:
- work_line_->CheckUnaryOp(dec_insn, reg_types_.Float(), reg_types_.Long());
+ work_line_->CheckUnaryOpFromWide(dec_insn, reg_types_.Float(),
+ reg_types_.LongLo(), reg_types_.LongHi());
break;
case Instruction::LONG_TO_DOUBLE:
- work_line_->CheckUnaryOp(dec_insn, reg_types_.Double(), reg_types_.Long());
+ work_line_->CheckUnaryOpWide(dec_insn, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
+ reg_types_.LongLo(), reg_types_.LongHi());
break;
case Instruction::FLOAT_TO_INT:
work_line_->CheckUnaryOp(dec_insn, reg_types_.Integer(), reg_types_.Float());
break;
case Instruction::FLOAT_TO_LONG:
- work_line_->CheckUnaryOp(dec_insn, reg_types_.Long(), reg_types_.Float());
+ work_line_->CheckUnaryOpToWide(dec_insn, reg_types_.LongLo(), reg_types_.LongHi(),
+ reg_types_.Float());
break;
case Instruction::FLOAT_TO_DOUBLE:
- work_line_->CheckUnaryOp(dec_insn, reg_types_.Double(), reg_types_.Float());
+ work_line_->CheckUnaryOpToWide(dec_insn, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
+ reg_types_.Float());
break;
case Instruction::DOUBLE_TO_INT:
- work_line_->CheckUnaryOp(dec_insn, reg_types_.Integer(), reg_types_.Double());
+ work_line_->CheckUnaryOpFromWide(dec_insn, reg_types_.Integer(),
+ reg_types_.DoubleLo(), reg_types_.DoubleHi());
break;
case Instruction::DOUBLE_TO_LONG:
- work_line_->CheckUnaryOp(dec_insn, reg_types_.Long(), reg_types_.Double());
+ work_line_->CheckUnaryOpWide(dec_insn, reg_types_.LongLo(), reg_types_.LongHi(),
+ reg_types_.DoubleLo(), reg_types_.DoubleHi());
break;
case Instruction::DOUBLE_TO_FLOAT:
- work_line_->CheckUnaryOp(dec_insn, reg_types_.Float(), reg_types_.Double());
+ work_line_->CheckUnaryOpFromWide(dec_insn, reg_types_.Float(),
+ reg_types_.DoubleLo(), reg_types_.DoubleHi());
break;
case Instruction::INT_TO_BYTE:
work_line_->CheckUnaryOp(dec_insn, reg_types_.Byte(), reg_types_.Integer());
@@ -2130,12 +2209,14 @@
case Instruction::SHL_INT:
case Instruction::SHR_INT:
case Instruction::USHR_INT:
- work_line_->CheckBinaryOp(dec_insn, reg_types_.Integer(), reg_types_.Integer(), reg_types_.Integer(), false);
+ work_line_->CheckBinaryOp(dec_insn, reg_types_.Integer(), reg_types_.Integer(),
+ reg_types_.Integer(), false);
break;
case Instruction::AND_INT:
case Instruction::OR_INT:
case Instruction::XOR_INT:
- work_line_->CheckBinaryOp(dec_insn, reg_types_.Integer(), reg_types_.Integer(), reg_types_.Integer(), true);
+ work_line_->CheckBinaryOp(dec_insn, reg_types_.Integer(), reg_types_.Integer(),
+ reg_types_.Integer(), true);
break;
case Instruction::ADD_LONG:
case Instruction::SUB_LONG:
@@ -2145,13 +2226,16 @@
case Instruction::AND_LONG:
case Instruction::OR_LONG:
case Instruction::XOR_LONG:
- work_line_->CheckBinaryOp(dec_insn, reg_types_.Long(), reg_types_.Long(), reg_types_.Long(), false);
+ work_line_->CheckBinaryOpWide(dec_insn, reg_types_.LongLo(), reg_types_.LongHi(),
+ reg_types_.LongLo(), reg_types_.LongHi(),
+ reg_types_.LongLo(), reg_types_.LongHi());
break;
case Instruction::SHL_LONG:
case Instruction::SHR_LONG:
case Instruction::USHR_LONG:
/* shift distance is Int, making these different from other binary operations */
- work_line_->CheckBinaryOp(dec_insn, reg_types_.Long(), reg_types_.Long(), reg_types_.Integer(), false);
+ work_line_->CheckBinaryOpWideShift(dec_insn, reg_types_.LongLo(), reg_types_.LongHi(),
+ reg_types_.Integer());
break;
case Instruction::ADD_FLOAT:
case Instruction::SUB_FLOAT:
@@ -2165,7 +2249,9 @@
case Instruction::MUL_DOUBLE:
case Instruction::DIV_DOUBLE:
case Instruction::REM_DOUBLE:
- work_line_->CheckBinaryOp(dec_insn, reg_types_.Double(), reg_types_.Double(), reg_types_.Double(), false);
+ work_line_->CheckBinaryOpWide(dec_insn, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
+ reg_types_.DoubleLo(), reg_types_.DoubleHi(),
+ reg_types_.DoubleLo(), reg_types_.DoubleHi());
break;
case Instruction::ADD_INT_2ADDR:
case Instruction::SUB_INT_2ADDR:
@@ -2192,12 +2278,15 @@
case Instruction::AND_LONG_2ADDR:
case Instruction::OR_LONG_2ADDR:
case Instruction::XOR_LONG_2ADDR:
- work_line_->CheckBinaryOp2addr(dec_insn, reg_types_.Long(), reg_types_.Long(), reg_types_.Long(), false);
+ work_line_->CheckBinaryOp2addrWide(dec_insn, reg_types_.LongLo(), reg_types_.LongHi(),
+ reg_types_.LongLo(), reg_types_.LongHi(),
+ reg_types_.LongLo(), reg_types_.LongHi());
break;
case Instruction::SHL_LONG_2ADDR:
case Instruction::SHR_LONG_2ADDR:
case Instruction::USHR_LONG_2ADDR:
- work_line_->CheckBinaryOp2addr(dec_insn, reg_types_.Long(), reg_types_.Long(), reg_types_.Integer(), false);
+ work_line_->CheckBinaryOp2addrWideShift(dec_insn, reg_types_.LongLo(), reg_types_.LongHi(),
+ reg_types_.Integer());
break;
case Instruction::ADD_FLOAT_2ADDR:
case Instruction::SUB_FLOAT_2ADDR:
@@ -2211,7 +2300,9 @@
case Instruction::MUL_DOUBLE_2ADDR:
case Instruction::DIV_DOUBLE_2ADDR:
case Instruction::REM_DOUBLE_2ADDR:
- work_line_->CheckBinaryOp2addr(dec_insn, reg_types_.Double(), reg_types_.Double(), reg_types_.Double(), false);
+ work_line_->CheckBinaryOp2addrWide(dec_insn, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
+ reg_types_.DoubleLo(), reg_types_.DoubleHi(),
+ reg_types_.DoubleLo(), reg_types_.DoubleHi());
break;
case Instruction::ADD_INT_LIT16:
case Instruction::RSUB_INT:
@@ -2290,7 +2381,7 @@
} // end - switch (dec_insn.opcode)
if (have_pending_hard_failure_) {
- if (!Runtime::Current()->IsStarted()) {
+ if (Runtime::Current()->IsCompiler()) {
/* When compiling, check that the last failure is a hard failure */
CHECK_EQ(failures_[failures_.size() - 1], VERIFY_ERROR_BAD_CLASS_HARD);
}
@@ -2645,7 +2736,7 @@
const RegType& super = GetDeclaringClass().GetSuperClass(®_types_);
if (super.IsUnresolvedTypes()) {
Fail(VERIFY_ERROR_NO_METHOD) << "unknown super class in invoke-super from "
- << PrettyMethod(method_idx_, *dex_file_)
+ << PrettyMethod(dex_method_idx_, *dex_file_)
<< " to super " << PrettyMethod(res_method);
return NULL;
}
@@ -2653,7 +2744,7 @@
if (res_method->GetMethodIndex() >= super_klass->GetVTable()->GetLength()) {
MethodHelper mh(res_method);
Fail(VERIFY_ERROR_NO_METHOD) << "invalid invoke-super from "
- << PrettyMethod(method_idx_, *dex_file_)
+ << PrettyMethod(dex_method_idx_, *dex_file_)
<< " to super " << super
<< "." << mh.GetName()
<< mh.GetSignature();
@@ -2768,7 +2859,7 @@
}
void MethodVerifier::VerifyAGet(const DecodedInstruction& dec_insn,
- const RegType& insn_type, bool is_primitive) {
+ const RegType& insn_type, bool is_primitive) {
const RegType& index_type = work_line_->GetRegisterType(dec_insn.vC);
if (!index_type.IsArrayIndexTypes()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Invalid reg type for array index (" << index_type << ")";
@@ -2782,7 +2873,8 @@
work_line_->SetRegisterType(dec_insn.vA, reg_types_.Zero());
} else {
// Category 2
- work_line_->SetRegisterType(dec_insn.vA, reg_types_.ConstLo());
+ work_line_->SetRegisterTypeWide(dec_insn.vA, reg_types_.FromCat2ConstLo(0, false),
+ reg_types_.FromCat2ConstHi(0, false));
}
} else if (!array_type.IsArrayTypes()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "not array type " << array_type << " with aget";
@@ -2797,14 +2889,19 @@
<< " source for category 1 aget";
} else if (is_primitive && !insn_type.Equals(component_type) &&
!((insn_type.IsInteger() && component_type.IsFloat()) ||
- (insn_type.IsLong() && component_type.IsDouble()))) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "array type " << array_type
- << " incompatible with aget of type " << insn_type;
+ (insn_type.IsLong() && component_type.IsDouble()))) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "array type " << array_type
+ << " incompatible with aget of type " << insn_type;
} else {
- // Use knowledge of the field type which is stronger than the type inferred from the
- // instruction, which can't differentiate object types and ints from floats, longs from
- // doubles.
+ // Use knowledge of the field type which is stronger than the type inferred from the
+ // instruction, which can't differentiate object types and ints from floats, longs from
+ // doubles.
+ if (!component_type.IsLowHalf()) {
work_line_->SetRegisterType(dec_insn.vA, component_type);
+ } else {
+ work_line_->SetRegisterTypeWide(dec_insn.vA, component_type,
+ component_type.HighHalf(®_types_));
+ }
}
}
}
@@ -2925,7 +3022,7 @@
// the field is declared in this class
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "cannot access instance field " << PrettyField(field)
<< " of a not fully initialized object within the context of "
- << PrettyMethod(method_idx_, *dex_file_);
+ << PrettyMethod(dex_method_idx_, *dex_file_);
return NULL;
} else if (!field_klass.IsAssignableFrom(obj_type)) {
// Trying to access C1.field1 using reference of type C2, which is neither C1 or a sub-class
@@ -2986,7 +3083,11 @@
return;
}
}
- work_line_->SetRegisterType(dec_insn.vA, field_type);
+ if (!field_type.IsLowHalf()) {
+ work_line_->SetRegisterType(dec_insn.vA, field_type);
+ } else {
+ work_line_->SetRegisterTypeWide(dec_insn.vA, field_type, field_type.HighHalf(®_types_));
+ }
}
void MethodVerifier::VerifyISPut(const DecodedInstruction& dec_insn,
@@ -3113,7 +3214,7 @@
}
const RegType& MethodVerifier::GetMethodReturnType() {
- const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx_);
+ const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_);
const DexFile::ProtoId& proto_id = dex_file_->GetMethodPrototype(method_id);
uint16_t return_type_idx = proto_id.return_type_idx_;
const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(return_type_idx));
@@ -3125,7 +3226,7 @@
Class* klass = foo_method_->GetDeclaringClass();
return reg_types_.FromClass(klass, klass->IsFinal());
} else {
- const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx_);
+ const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_);
const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(method_id.class_idx_));
return reg_types_.FromDescriptor(class_loader_, descriptor, false);
}
@@ -3265,6 +3366,50 @@
return it->second;
}
+std::vector<int32_t> MethodVerifier::DescribeVRegs(uint32_t dex_pc) {
+ RegisterLine* line = reg_table_.GetLine(dex_pc);
+ std::vector<int32_t> result;
+ for (size_t i = 0; i < line->NumRegs(); ++i) {
+ const RegType& type = line->GetRegisterType(i);
+ if (type.IsConstant()) {
+ result.push_back(type.IsPreciseConstant() ? kConstant : kImpreciseConstant);
+ result.push_back(type.ConstantValue());
+ } else if (type.IsConstantLo()) {
+ result.push_back(type.IsPreciseConstantLo() ? kConstant : kImpreciseConstant);
+ result.push_back(type.ConstantValueLo());
+ } else if (type.IsConstantHi()) {
+ result.push_back(type.IsPreciseConstantHi() ? kConstant : kImpreciseConstant);
+ result.push_back(type.ConstantValueHi());
+ } else if (type.IsIntegralTypes()) {
+ result.push_back(kIntVReg);
+ result.push_back(0);
+ } else if (type.IsFloat()) {
+ result.push_back(kFloatVReg);
+ result.push_back(0);
+ } else if (type.IsLong()) {
+ result.push_back(kLongLoVReg);
+ result.push_back(0);
+ result.push_back(kLongHiVReg);
+ result.push_back(0);
+ ++i;
+ } else if (type.IsDouble()) {
+ result.push_back(kDoubleLoVReg);
+ result.push_back(0);
+ result.push_back(kDoubleHiVReg);
+ result.push_back(0);
+ ++i;
+ } else if (type.IsUndefined() || type.IsConflict() || type.IsHighHalf()) {
+ result.push_back(kUndefined);
+ result.push_back(0);
+ } else {
+ CHECK(type.IsNonZeroReferenceTypes()) << type;
+ result.push_back(kReferenceVReg);
+ result.push_back(0);
+ }
+ }
+ return result;
+}
+
Mutex* MethodVerifier::dex_gc_maps_lock_ = NULL;
MethodVerifier::DexGcMapTable* MethodVerifier::dex_gc_maps_ = NULL;
diff --git a/src/verifier/method_verifier.h b/src/verifier/method_verifier.h
index 42283a2..95d7905 100644
--- a/src/verifier/method_verifier.h
+++ b/src/verifier/method_verifier.h
@@ -113,6 +113,8 @@
kTrackRegsAll,
};
+// A mapping from a dex pc to the register line statuses as they are immediately prior to the
+// execution of that instruction.
class PcToRegisterLineTable {
public:
PcToRegisterLineTable() {}
@@ -137,7 +139,6 @@
private:
typedef SafeMap<int32_t, RegisterLine*> Table;
- // Map from a dex pc to the register status associated with it
Table pc_to_register_line_;
};
@@ -162,7 +163,19 @@
std::string& error)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- static void VerifyMethodAndDump(AbstractMethod* method)
+ static void VerifyMethodAndDump(std::ostream& os, uint32_t method_idx, const DexFile* dex_file,
+ DexCache* dex_cache, ClassLoader* class_loader,
+ uint32_t class_def_idx, const DexFile::CodeItem* code_item,
+ AbstractMethod* method, uint32_t method_access_flags)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ static std::vector<int32_t> DescribeVRegs(uint32_t dex_method_idx,
+ const DexFile* dex_file, DexCache* dex_cache,
+ ClassLoader* class_loader,
+ uint32_t class_def_idx,
+ const DexFile::CodeItem* code_item,
+ AbstractMethod* method,
+ uint32_t method_access_flags, uint32_t dex_pc)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
uint8_t EncodePcToReferenceMapData() const;
@@ -180,7 +193,7 @@
// Log for verification information.
std::ostream& LogVerifyInfo() {
- return info_messages_ << "VFY: " << PrettyMethod(method_idx_, *dex_file_)
+ return info_messages_ << "VFY: " << PrettyMethod(dex_method_idx_, *dex_file_)
<< '[' << reinterpret_cast<void*>(work_insn_idx_) << "] : ";
}
@@ -576,6 +589,9 @@
// Compute sizes for GC map data
void ComputeGcMapSizes(size_t* gc_points, size_t* ref_bitmap_bits, size_t* log2_max_gc_pc);
+ // Describe VRegs at the given dex pc.
+ std::vector<int32_t> DescribeVRegs(uint32_t dex_pc);
+
InsnFlags* CurrentInsnFlags();
// All the GC maps that the verifier has created
@@ -617,7 +633,7 @@
// Storage for the register status we're saving for later.
UniquePtr<RegisterLine> saved_line_;
- uint32_t method_idx_; // The method we're working on.
+ uint32_t dex_method_idx_; // The method we're working on.
// Its object representation if known.
AbstractMethod* foo_method_ GUARDED_BY(Locks::mutator_lock_);
uint32_t method_access_flags_; // Method's access flags.
diff --git a/src/verifier/reg_type.cc b/src/verifier/reg_type.cc
index e02fbf4..dc41ceb 100644
--- a/src/verifier/reg_type.cc
+++ b/src/verifier/reg_type.cc
@@ -25,19 +25,22 @@
static const char* type_strings[] = {
"Undefined",
"Conflict",
- "Boolean",
- "Byte",
- "Short",
- "Char",
- "Integer",
- "Float",
- "Long (Low Half)",
- "Long (High Half)",
- "Double (Low Half)",
- "Double (High Half)",
- "64-bit Constant (Low Half)",
- "64-bit Constant (High Half)",
- "32-bit Constant",
+ "boolean",
+ "byte",
+ "short",
+ "char",
+ "int",
+ "float",
+ "long (Low Half)",
+ "long (High Half)",
+ "double (Low Half)",
+ "double (High Half)",
+ "Precise 32-bit Constant",
+ "Imprecise 32-bit Constant",
+ "Precise 64-bit Constant (Low Half)",
+ "Precise 64-bit Constant (High Half)",
+ "Imprecise 64-bit Constant (Low Half)",
+ "Imprecise 64-bit Constant (High Half)",
"Unresolved Reference",
"Uninitialized Reference",
"Uninitialized This Reference",
@@ -81,14 +84,34 @@
} else if (IsConstant()) {
uint32_t val = ConstantValue();
if (val == 0) {
- result = "Zero";
+ CHECK(IsPreciseConstant());
+ result = "Zero/null";
} else {
+ result = IsPreciseConstant() ? "Precise " : "Imprecise ";
if (IsConstantShort()) {
- result = StringPrintf("32-bit Constant: %d", val);
+ result += StringPrintf("Constant: %d", val);
} else {
- result = StringPrintf("32-bit Constant: 0x%x", val);
+ result += StringPrintf("Constant: 0x%x", val);
}
}
+ } else if (IsConstantLo()) {
+ int32_t val = ConstantValueLo();
+ result = IsPreciseConstantLo() ? "Precise " : "Imprecise ";
+ if (val >= std::numeric_limits<jshort>::min() &&
+ val <= std::numeric_limits<jshort>::max()) {
+ result += StringPrintf("Low-half Constant: %d", val);
+ } else {
+ result += StringPrintf("Low-half Constant: 0x%x", val);
+ }
+ } else if (IsConstantHi()) {
+ int32_t val = ConstantValueHi();
+ result = IsPreciseConstantHi() ? "Precise " : "Imprecise ";
+ if (val >= std::numeric_limits<jshort>::min() &&
+ val <= std::numeric_limits<jshort>::max()) {
+ result += StringPrintf("High-half Constant: %d", val);
+ } else {
+ result += StringPrintf("High-half Constant: 0x%x", val);
+ }
} else {
result = type_strings[type_];
if (IsReferenceTypes()) {
@@ -104,13 +127,14 @@
}
const RegType& RegType::HighHalf(RegTypeCache* cache) const {
- CHECK(IsLowHalf());
+ DCHECK(IsLowHalf());
if (type_ == kRegTypeLongLo) {
return cache->FromType(kRegTypeLongHi);
} else if (type_ == kRegTypeDoubleLo) {
return cache->FromType(kRegTypeDoubleHi);
} else {
- return cache->FromType(kRegTypeConstHi);
+ DCHECK_EQ(type_, kRegTypeImpreciseConstLo);
+ return cache->FromType(kRegTypeImpreciseConstHi);
}
}
@@ -244,16 +268,32 @@
if (val1 >= 0 && val2 >= 0) {
// +ve1 MERGE +ve2 => MAX(+ve1, +ve2)
if (val1 >= val2) {
- return *this;
+ if (!IsPreciseConstant()) {
+ return *this;
+ } else {
+ return reg_types->FromCat1Const(val1, false);
+ }
} else {
- return incoming_type;
+ if (!incoming_type.IsPreciseConstant()) {
+ return incoming_type;
+ } else {
+ return reg_types->FromCat1Const(val2, false);
+ }
}
} else if (val1 < 0 && val2 < 0) {
// -ve1 MERGE -ve2 => MIN(-ve1, -ve2)
if (val1 <= val2) {
- return *this;
+ if (!IsPreciseConstant()) {
+ return *this;
+ } else {
+ return reg_types->FromCat1Const(val1, false);
+ }
} else {
- return incoming_type;
+ if (!incoming_type.IsPreciseConstant()) {
+ return incoming_type;
+ } else {
+ return reg_types->FromCat1Const(val2, false);
+ }
}
} else {
// Values are +ve and -ve, choose smallest signed type in which they both fit
@@ -275,6 +315,14 @@
return reg_types->IntConstant();
}
}
+ } else if (IsConstantLo() && incoming_type.IsConstantLo()) {
+ int32_t val1 = ConstantValueLo();
+ int32_t val2 = incoming_type.ConstantValueLo();
+ return reg_types->FromCat2ConstLo(val1 | val2, false);
+ } else if (IsConstantHi() && incoming_type.IsConstantHi()) {
+ int32_t val1 = ConstantValueHi();
+ int32_t val2 = incoming_type.ConstantValueHi();
+ return reg_types->FromCat2ConstHi(val1 | val2, false);
} else if (IsIntegralTypes() && incoming_type.IsIntegralTypes()) {
if (IsBooleanTypes() && incoming_type.IsBooleanTypes()) {
return reg_types->Boolean(); // boolean MERGE boolean => boolean
diff --git a/src/verifier/reg_type.h b/src/verifier/reg_type.h
index 205867d..5daaf60 100644
--- a/src/verifier/reg_type.h
+++ b/src/verifier/reg_type.h
@@ -52,10 +52,13 @@
kRegTypeLongHi,
kRegTypeDoubleLo, // D.
kRegTypeDoubleHi,
- kRegTypeConstLo, // Const derived wide, lower half - could be long or double.
- kRegTypeConstHi, // Const derived wide, upper half - could be long or double.
- kRegTypeLastFixedLocation = kRegTypeConstHi,
- kRegTypeConst, // 32-bit constant derived value - could be float or int.
+ kRegTypeLastFixedLocation = kRegTypeDoubleHi,
+ kRegTypePreciseConst, // 32-bit constant - could be float or int.
+ kRegTypeImpreciseConst, // 32-bit constant derived value - could be float or int.
+ kRegTypePreciseConstLo, // Const wide, lower half - could be long or double.
+ kRegTypePreciseConstHi, // Const wide, upper half - could be long or double.
+ kRegTypeImpreciseConstLo, // Const derived wide, lower half - could be long or double.
+ kRegTypeImpreciseConstHi, // Const derived wide, upper half - could be long or double.
kRegTypeUnresolvedReference, // Reference type that couldn't be resolved.
kRegTypeUninitializedReference, // Freshly allocated reference type.
kRegTypeUninitializedThisReference, // Freshly allocated reference passed as "this".
@@ -105,38 +108,85 @@
IsUnresolvedAndUninitializedThisReference() || IsUnresolvedMergedReference() ||
IsUnresolvedSuperClass();
}
- bool IsLowHalf() const { return type_ == kRegTypeLongLo ||
- type_ == kRegTypeDoubleLo ||
- type_ == kRegTypeConstLo; }
- bool IsHighHalf() const { return type_ == kRegTypeLongHi ||
- type_ == kRegTypeDoubleHi ||
- type_ == kRegTypeConstHi; }
+ bool IsLowHalf() const {
+ return type_ == kRegTypeLongLo ||
+ type_ == kRegTypeDoubleLo ||
+ type_ == kRegTypePreciseConstLo ||
+ type_ == kRegTypeImpreciseConstLo;
+ }
+ bool IsHighHalf() const {
+ return type_ == kRegTypeLongHi ||
+ type_ == kRegTypeDoubleHi ||
+ type_ == kRegTypePreciseConstHi ||
+ type_ == kRegTypeImpreciseConstHi;
+ }
bool IsLongOrDoubleTypes() const { return IsLowHalf(); }
// Check this is the low half, and that type_h is its matching high-half
bool CheckWidePair(const RegType& type_h) const {
- return IsLowHalf() && (type_h.type_ == type_ + 1);
+ if (IsLowHalf()) {
+ return (type_h.type_ == type_ + 1) ||
+ (IsPreciseConstantLo() && !type_h.IsPreciseConstantHi()) ||
+ (!IsPreciseConstantLo() && type_h.IsPreciseConstantHi());
+ }
+ return false;
}
// The high half that corresponds to this low half
const RegType& HighHalf(RegTypeCache* cache) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool IsConstant() const { return type_ == kRegTypeConst; }
- bool IsLongConstant() const { return type_ == kRegTypeConstLo; }
- bool IsLongConstantHigh() const { return type_ == kRegTypeConstHi; }
+ bool IsPreciseConstant() const {
+ return type_ == kRegTypePreciseConst;
+ }
+ bool IsPreciseConstantLo() const {
+ bool result = (type_ == kRegTypePreciseConstLo);
+ DCHECK(result || (type_ == kRegTypeImpreciseConstLo));
+ return result;
+ }
+ bool IsPreciseConstantHi() const {
+ bool result = (type_ == kRegTypePreciseConstHi);
+ DCHECK(result || (type_ == kRegTypeImpreciseConstHi));
+ return result;
+ }
+ bool IsConstant() const {
+ return type_ == kRegTypePreciseConst || type_ == kRegTypeImpreciseConst;
+ }
- // If this is a 32-bit constant, what is the value? This value may just
- // approximate to the actual constant value by virtue of merging.
+ bool IsConstantLo() const {
+ return type_ == kRegTypePreciseConstLo || type_ == kRegTypeImpreciseConstLo;
+ }
+ bool IsLongConstant() const {
+ return IsConstantLo();
+ }
+ bool IsConstantHi() const {
+ return type_ == kRegTypePreciseConstHi || type_ == kRegTypeImpreciseConstHi;
+ }
+ bool IsLongConstantHigh() const {
+ return IsConstantHi();
+ }
+
+ // If this is a 32-bit constant, what is the value? This value may be imprecise in which case
+ // the value represents part of the integer range of values that may be held in the register.
int32_t ConstantValue() const {
DCHECK(IsConstant());
return allocation_pc_or_constant_or_merged_types_;
}
+ int32_t ConstantValueLo() const {
+ DCHECK(IsConstantLo());
+ return allocation_pc_or_constant_or_merged_types_;
+ }
+ int32_t ConstantValueHi() const {
+ DCHECK(IsConstantHi());
+ return allocation_pc_or_constant_or_merged_types_;
+ }
- bool IsZero() const { return IsConstant() && ConstantValue() == 0; }
- bool IsOne() const { return IsConstant() && ConstantValue() == 1; }
- bool IsConstantBoolean() const { return IsZero() || IsOne(); }
+ bool IsZero() const { return IsPreciseConstant() && ConstantValue() == 0; }
+ bool IsOne() const { return IsPreciseConstant() && ConstantValue() == 1; }
+ bool IsConstantBoolean() const {
+ return IsConstant() && ConstantValue() >= 0 && ConstantValue() <= 1;
+ }
bool IsConstantByte() const {
return IsConstant() &&
ConstantValue() >= std::numeric_limits<jbyte>::min() &&
@@ -187,13 +237,17 @@
return IsLong() || IsLongConstant();
}
bool IsLongHighTypes() const {
- return type_ == kRegTypeLongHi || type_ == kRegTypeConstHi;
+ return type_ == kRegTypeLongHi ||
+ type_ == kRegTypePreciseConstHi ||
+ type_ == kRegTypeImpreciseConstHi;
}
bool IsDoubleTypes() const {
return IsDouble() || IsLongConstant();
}
bool IsDoubleHighTypes() const {
- return type_ == kRegTypeDoubleHi || type_ == kRegTypeConstHi;
+ return type_ == kRegTypeDoubleHi ||
+ type_ == kRegTypePreciseConstHi ||
+ type_ == kRegTypeImpreciseConstHi;
}
uint32_t GetAllocationPc() const {
@@ -351,8 +405,9 @@
: type_(type), klass_or_descriptor_(klass_or_descriptor),
allocation_pc_or_constant_or_merged_types_(allocation_pc_or_constant_or_merged_types),
cache_id_(cache_id) {
- DCHECK(IsConstant() || IsUninitializedTypes() || IsUnresolvedMergedReference() ||
- IsUnresolvedSuperClass() || allocation_pc_or_constant_or_merged_types == 0);
+ DCHECK(IsConstant() || IsConstantLo() || IsConstantHi() ||
+ IsUninitializedTypes() || IsUnresolvedMergedReference() || IsUnresolvedSuperClass() ||
+ allocation_pc_or_constant_or_merged_types == 0);
if (!IsConstant() && !IsLongConstant() && !IsLongConstantHigh() && !IsUndefined() &&
!IsConflict() && !IsUnresolvedMergedReference() && !IsUnresolvedSuperClass()) {
DCHECK(klass_or_descriptor != NULL);
diff --git a/src/verifier/reg_type_cache.cc b/src/verifier/reg_type_cache.cc
index 847cde9..1b91321 100644
--- a/src/verifier/reg_type_cache.cc
+++ b/src/verifier/reg_type_cache.cc
@@ -312,14 +312,41 @@
}
}
-const RegType& RegTypeCache::FromCat1Const(int32_t value) {
+const RegType& RegTypeCache::FromCat1Const(int32_t value, bool precise) {
+ RegType::Type wanted_type = precise ? RegType::kRegTypePreciseConst : RegType::kRegTypeImpreciseConst;
for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) {
RegType* cur_entry = entries_[i];
- if (cur_entry->IsConstant() && cur_entry->ConstantValue() == value) {
+ if (cur_entry->GetType() == wanted_type && cur_entry->ConstantValue() == value) {
return *cur_entry;
}
}
- RegType* entry = new RegType(RegType::kRegTypeConst, NULL, value, entries_.size());
+ RegType* entry = new RegType(wanted_type, NULL, value, entries_.size());
+ entries_.push_back(entry);
+ return *entry;
+}
+
+const RegType& RegTypeCache::FromCat2ConstLo(int32_t value, bool precise) {
+ RegType::Type wanted_type = precise ? RegType::kRegTypePreciseConstLo : RegType::kRegTypeImpreciseConstLo;
+ for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) {
+ RegType* cur_entry = entries_[i];
+ if (cur_entry->GetType() == wanted_type && cur_entry->ConstantValueLo() == value) {
+ return *cur_entry;
+ }
+ }
+ RegType* entry = new RegType(wanted_type, NULL, value, entries_.size());
+ entries_.push_back(entry);
+ return *entry;
+}
+
+const RegType& RegTypeCache::FromCat2ConstHi(int32_t value, bool precise) {
+ RegType::Type wanted_type = precise ? RegType::kRegTypePreciseConstHi : RegType::kRegTypeImpreciseConstHi;
+ for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) {
+ RegType* cur_entry = entries_[i];
+ if (cur_entry->GetType() == wanted_type && cur_entry->ConstantValueHi() == value) {
+ return *cur_entry;
+ }
+ }
+ RegType* entry = new RegType(wanted_type, NULL, value, entries_.size());
entries_.push_back(entry);
return *entry;
}
@@ -337,11 +364,10 @@
}
void RegTypeCache::Dump(std::ostream& os) {
- os << "Register Types:\n";
for (size_t i = 0; i < entries_.size(); i++) {
RegType* cur_entry = entries_[i];
if (cur_entry != NULL) {
- os << "\t" << i << ": " << cur_entry->Dump() << "\n";
+ os << i << ": " << cur_entry->Dump() << "\n";
}
}
}
diff --git a/src/verifier/reg_type_cache.h b/src/verifier/reg_type_cache.h
index 36fd2c7..bf0c5b1 100644
--- a/src/verifier/reg_type_cache.h
+++ b/src/verifier/reg_type_cache.h
@@ -44,7 +44,10 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const RegType& FromClass(Class* klass, bool precise)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- const RegType& FromCat1Const(int32_t value);
+ const RegType& FromCat1Const(int32_t value, bool precise)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ const RegType& FromCat2ConstLo(int32_t value, bool precise);
+ const RegType& FromCat2ConstHi(int32_t value, bool precise);
const RegType& FromDescriptor(ClassLoader* loader, const char* descriptor, bool precise)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const RegType& FromType(RegType::Type)
@@ -70,12 +73,18 @@
const RegType& Float() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return FromType(RegType::kRegTypeFloat);
}
- const RegType& Long() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const RegType& LongLo() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return FromType(RegType::kRegTypeLongLo);
}
- const RegType& Double() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ const RegType& LongHi() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return FromType(RegType::kRegTypeLongHi);
+ }
+ const RegType& DoubleLo() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return FromType(RegType::kRegTypeDoubleLo);
}
+ const RegType& DoubleHi() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return FromType(RegType::kRegTypeDoubleHi);
+ }
const RegType& JavaLangClass(bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return From(precise ? RegType::kRegTypeReference
@@ -103,11 +112,8 @@
const RegType& Conflict() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return FromType(RegType::kRegTypeConflict);
}
- const RegType& ConstLo() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return FromType(RegType::kRegTypeConstLo);
- }
const RegType& Zero() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return FromCat1Const(0);
+ return FromCat1Const(0, true);
}
const RegType& Uninitialized(const RegType& type, uint32_t allocation_pc);
@@ -118,9 +124,15 @@
// Representatives of various constant types. When merging constants we can't infer a type,
// (an int may later be used as a float) so we select these representative values meaning future
// merges won't know the exact constant value but have some notion of its size.
- const RegType& ByteConstant() { return FromCat1Const(std::numeric_limits<jbyte>::min()); }
- const RegType& ShortConstant() { return FromCat1Const(std::numeric_limits<jshort>::min()); }
- const RegType& IntConstant() { return FromCat1Const(std::numeric_limits<jint>::max()); }
+ const RegType& ByteConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return FromCat1Const(std::numeric_limits<jbyte>::min(), false);
+ }
+ const RegType& ShortConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return FromCat1Const(std::numeric_limits<jshort>::min(), false);
+ }
+ const RegType& IntConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return FromCat1Const(std::numeric_limits<jint>::max(), false);
+ }
const RegType& GetComponentType(const RegType& array, ClassLoader* loader)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/src/verifier/reg_type_test.cc b/src/verifier/reg_type_test.cc
index 6bdf886..18e9a65 100644
--- a/src/verifier/reg_type_test.cc
+++ b/src/verifier/reg_type_test.cc
@@ -184,7 +184,7 @@
EXPECT_FALSE(int_reg_type.IsDoubleTypes());
EXPECT_TRUE(int_reg_type.IsArrayIndexTypes());
- const RegType& long_reg_type = cache.Long();
+ const RegType& long_reg_type = cache.LongLo();
EXPECT_FALSE(long_reg_type.IsUndefined());
EXPECT_FALSE(long_reg_type.IsConflict());
EXPECT_FALSE(long_reg_type.IsZero());
@@ -246,7 +246,7 @@
EXPECT_FALSE(float_reg_type.IsDoubleTypes());
EXPECT_FALSE(float_reg_type.IsArrayIndexTypes());
- const RegType& double_reg_type = cache.Double();
+ const RegType& double_reg_type = cache.DoubleLo();
EXPECT_FALSE(double_reg_type.IsUndefined());
EXPECT_FALSE(double_reg_type.IsConflict());
EXPECT_FALSE(double_reg_type.IsZero());
diff --git a/src/verifier/register_line.cc b/src/verifier/register_line.cc
index 4882740..afd2eff 100644
--- a/src/verifier/register_line.cc
+++ b/src/verifier/register_line.cc
@@ -35,12 +35,9 @@
bool RegisterLine::SetRegisterType(uint32_t vdst, const RegType& new_type) {
DCHECK_LT(vdst, num_regs_);
- if (new_type.IsLowHalf()) {
- line_[vdst] = new_type.GetId();
- line_[vdst + 1] = new_type.HighHalf(verifier_->GetRegTypeCache()).GetId();
- } else if (new_type.IsHighHalf()) {
- /* should never set these explicitly */
- verifier_->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Explicit set of high register type";
+ if (new_type.IsLowHalf() || new_type.IsHighHalf()) {
+ verifier_->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Expected category1 register type not '"
+ << new_type << "'";
return false;
} else if (new_type.IsConflict()) { // should only be set during a merge
verifier_->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Set register to unknown type " << new_type;
@@ -58,13 +55,33 @@
return true;
}
+bool RegisterLine::SetRegisterTypeWide(uint32_t vdst, const RegType& new_type1,
+ const RegType& new_type2) {
+ DCHECK_LT(vdst, num_regs_);
+ if (!new_type1.CheckWidePair(new_type2)) {
+ verifier_->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Invalid wide pair '"
+ << new_type1 << "' '" << new_type2 << "'";
+ return false;
+ } else {
+ line_[vdst] = new_type1.GetId();
+ line_[vdst + 1] = new_type2.GetId();
+ }
+ // Clear the monitor entry bits for this register.
+ ClearAllRegToLockDepths(vdst);
+ ClearAllRegToLockDepths(vdst + 1);
+ return true;
+}
+
void RegisterLine::SetResultTypeToUnknown() {
result_[0] = RegType::kRegTypeUndefined;
result_[1] = RegType::kRegTypeUndefined;
}
void RegisterLine::SetResultRegisterType(const RegType& new_type) {
+ DCHECK(!new_type.IsLowHalf());
+ DCHECK(!new_type.IsHighHalf());
result_[0] = new_type.GetId();
+ result_[1] = RegType::kRegTypeUndefined;
if (new_type.IsLowHalf()) {
DCHECK_EQ(new_type.HighHalf(verifier_->GetRegTypeCache()).GetId(), new_type.GetId() + 1);
result_[1] = new_type.GetId() + 1;
@@ -73,6 +90,12 @@
}
}
+void RegisterLine::SetResultRegisterTypeWide(const RegType& new_type1, const RegType& new_type2) {
+ DCHECK(new_type1.CheckWidePair(new_type2));
+ result_[0] = new_type1.GetId();
+ result_[1] = new_type2.GetId();
+}
+
const RegType& RegisterLine::GetRegisterType(uint32_t vsrc) const {
// The register index was validated during the static pass, so we don't need to check it here.
DCHECK_LT(vsrc, num_regs_);
@@ -121,6 +144,29 @@
return true;
}
+bool RegisterLine::VerifyRegisterTypeWide(uint32_t vsrc, const RegType& check_type1,
+ const RegType& check_type2) {
+ DCHECK(check_type1.CheckWidePair(check_type2));
+ // Verify the src register type against the check type refining the type of the register
+ const RegType& src_type = GetRegisterType(vsrc);
+ if (!check_type1.IsAssignableFrom(src_type)) {
+ verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "register v" << vsrc << " has type " << src_type
+ << " but expected " << check_type1;
+ return false;
+ }
+ const RegType& src_type_h = GetRegisterType(vsrc + 1);
+ if (!src_type.CheckWidePair(src_type_h)) {
+ verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "wide register v" << vsrc << " has type "
+ << src_type << "/" << src_type_h;
+ return false;
+ }
+ // The register at vsrc has a defined type, we know the lower-upper-bound, but this is less
+ // precise than the subtype in vsrc so leave it for reference types. For primitive types
+ // if they are a defined type then they are as precise as we can get, however, for constant
+ // types we may wish to refine them. Unfortunately constant propagation has rendered this useless.
+ return true;
+}
+
void RegisterLine::MarkRefsAsInitialized(const RegType& uninit_type) {
DCHECK(uninit_type.IsUninitializedTypes());
const RegType& init_type = verifier_->GetRegTypeCache()->FromUninitialized(uninit_type);
@@ -180,7 +226,7 @@
verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "copy2 v" << vdst << "<-v" << vsrc
<< " type=" << type_l << "/" << type_h;
} else {
- SetRegisterType(vdst, type_l); // implicitly sets the second half
+ SetRegisterTypeWide(vdst, type_l, type_h);
}
}
@@ -209,7 +255,7 @@
<< "copyRes2 v" << vdst << "<- result0" << " type=" << type_l;
} else {
DCHECK(type_l.CheckWidePair(type_h)); // Set should never allow this case
- SetRegisterType(vdst, type_l); // also sets the high
+ SetRegisterTypeWide(vdst, type_l, type_h); // also sets the high
result_[0] = RegType::kRegTypeUndefined;
result_[1] = RegType::kRegTypeUndefined;
}
@@ -222,6 +268,30 @@
}
}
+void RegisterLine::CheckUnaryOpWide(const DecodedInstruction& dec_insn,
+ const RegType& dst_type1, const RegType& dst_type2,
+ const RegType& src_type1, const RegType& src_type2) {
+ if (VerifyRegisterTypeWide(dec_insn.vB, src_type1, src_type2)) {
+ SetRegisterTypeWide(dec_insn.vA, dst_type1, dst_type2);
+ }
+}
+
+void RegisterLine::CheckUnaryOpToWide(const DecodedInstruction& dec_insn,
+ const RegType& dst_type1, const RegType& dst_type2,
+ const RegType& src_type) {
+ if (VerifyRegisterType(dec_insn.vB, src_type)) {
+ SetRegisterTypeWide(dec_insn.vA, dst_type1, dst_type2);
+ }
+}
+
+void RegisterLine::CheckUnaryOpFromWide(const DecodedInstruction& dec_insn,
+ const RegType& dst_type,
+ const RegType& src_type1, const RegType& src_type2) {
+ if (VerifyRegisterTypeWide(dec_insn.vB, src_type1, src_type2)) {
+ SetRegisterType(dec_insn.vA, dst_type);
+ }
+}
+
void RegisterLine::CheckBinaryOp(const DecodedInstruction& dec_insn,
const RegType& dst_type,
const RegType& src_type1, const RegType& src_type2,
@@ -240,6 +310,25 @@
}
}
+void RegisterLine::CheckBinaryOpWide(const DecodedInstruction& dec_insn,
+ const RegType& dst_type1, const RegType& dst_type2,
+ const RegType& src_type1_1, const RegType& src_type1_2,
+ const RegType& src_type2_1, const RegType& src_type2_2) {
+ if (VerifyRegisterTypeWide(dec_insn.vB, src_type1_1, src_type1_2) &&
+ VerifyRegisterTypeWide(dec_insn.vC, src_type2_1, src_type2_2)) {
+ SetRegisterTypeWide(dec_insn.vA, dst_type1, dst_type2);
+ }
+}
+
+void RegisterLine::CheckBinaryOpWideShift(const DecodedInstruction& dec_insn,
+ const RegType& long_lo_type, const RegType& long_hi_type,
+ const RegType& int_type) {
+ if (VerifyRegisterTypeWide(dec_insn.vB, long_lo_type, long_hi_type) &&
+ VerifyRegisterType(dec_insn.vC, int_type)) {
+ SetRegisterTypeWide(dec_insn.vA, long_lo_type, long_hi_type);
+ }
+}
+
void RegisterLine::CheckBinaryOp2addr(const DecodedInstruction& dec_insn,
const RegType& dst_type, const RegType& src_type1,
const RegType& src_type2, bool check_boolean_op) {
@@ -257,6 +346,25 @@
}
}
+void RegisterLine::CheckBinaryOp2addrWide(const DecodedInstruction& dec_insn,
+ const RegType& dst_type1, const RegType& dst_type2,
+ const RegType& src_type1_1, const RegType& src_type1_2,
+ const RegType& src_type2_1, const RegType& src_type2_2) {
+ if (VerifyRegisterTypeWide(dec_insn.vA, src_type1_1, src_type1_2) &&
+ VerifyRegisterTypeWide(dec_insn.vB, src_type2_1, src_type2_2)) {
+ SetRegisterTypeWide(dec_insn.vA, dst_type1, dst_type2);
+ }
+}
+
+void RegisterLine::CheckBinaryOp2addrWideShift(const DecodedInstruction& dec_insn,
+ const RegType& long_lo_type, const RegType& long_hi_type,
+ const RegType& int_type) {
+ if (VerifyRegisterTypeWide(dec_insn.vA, long_lo_type, long_hi_type) &&
+ VerifyRegisterType(dec_insn.vB, int_type)) {
+ SetRegisterTypeWide(dec_insn.vA, long_lo_type, long_hi_type);
+ }
+}
+
void RegisterLine::CheckLiteralOp(const DecodedInstruction& dec_insn,
const RegType& dst_type, const RegType& src_type,
bool check_boolean_op) {
diff --git a/src/verifier/register_line.h b/src/verifier/register_line.h
index 9f0fcb0..c6c19f8 100644
--- a/src/verifier/register_line.h
+++ b/src/verifier/register_line.h
@@ -48,9 +48,6 @@
// During verification, we associate one of these with every "interesting" instruction. We track
// the status of all registers, and (if the method has any monitor-enter instructions) maintain a
// stack of entered monitors (identified by code unit offset).
-// If live-precise register maps are enabled, the "liveRegs" vector will be populated. Unlike the
-// other lists of registers here, we do not track the liveness of the method result register
-// (which is not visible to the GC).
class RegisterLine {
public:
RegisterLine(size_t num_regs, MethodVerifier* verifier)
@@ -88,16 +85,25 @@
bool SetRegisterType(uint32_t vdst, const RegType& new_type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool SetRegisterTypeWide(uint32_t vdst, const RegType& new_type1, const RegType& new_type2)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
/* Set the type of the "result" register. */
void SetResultRegisterType(const RegType& new_type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void SetResultRegisterTypeWide(const RegType& new_type1, const RegType& new_type2)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Get the type of register vsrc.
const RegType& GetRegisterType(uint32_t vsrc) const;
bool VerifyRegisterType(uint32_t vsrc, const RegType& check_type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ bool VerifyRegisterTypeWide(uint32_t vsrc, const RegType& check_type1, const RegType& check_type2)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
void CopyFromLine(const RegisterLine* src) {
DCHECK_EQ(num_regs_, src->num_regs_);
memcpy(line_.get(), src->line_.get(), num_regs_ * sizeof(uint16_t));
@@ -171,6 +177,21 @@
const RegType& dst_type, const RegType& src_type)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void CheckUnaryOpWide(const DecodedInstruction& dec_insn,
+ const RegType& dst_type1, const RegType& dst_type2,
+ const RegType& src_type1, const RegType& src_type2)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void CheckUnaryOpToWide(const DecodedInstruction& dec_insn,
+ const RegType& dst_type1, const RegType& dst_type2,
+ const RegType& src_type)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void CheckUnaryOpFromWide(const DecodedInstruction& dec_insn,
+ const RegType& dst_type,
+ const RegType& src_type1, const RegType& src_type2)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
/*
* Verify types for a simple three-register instruction (e.g. "add-int").
* "dst_type" is stored into vA, and "src_type1"/"src_type2" are verified
@@ -181,6 +202,17 @@
bool check_boolean_op)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void CheckBinaryOpWide(const DecodedInstruction& dec_insn,
+ const RegType& dst_type1, const RegType& dst_type2,
+ const RegType& src_type1_1, const RegType& src_type1_2,
+ const RegType& src_type2_1, const RegType& src_type2_2)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void CheckBinaryOpWideShift(const DecodedInstruction& dec_insn,
+ const RegType& long_lo_type, const RegType& long_hi_type,
+ const RegType& int_type)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
/*
* Verify types for a binary "2addr" operation. "src_type1"/"src_type2"
* are verified against vA/vB, then "dst_type" is stored into vA.
@@ -191,6 +223,17 @@
bool check_boolean_op)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void CheckBinaryOp2addrWide(const DecodedInstruction& dec_insn,
+ const RegType& dst_type1, const RegType& dst_type2,
+ const RegType& src_type1_1, const RegType& src_type1_2,
+ const RegType& src_type2_1, const RegType& src_type2_2)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void CheckBinaryOp2addrWideShift(const DecodedInstruction& dec_insn,
+ const RegType& long_lo_type, const RegType& long_hi_type,
+ const RegType& int_type)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
/*
* Verify types for A two-register instruction with a literal constant (e.g. "add-int/lit8").
* "dst_type" is stored into vA, and "src_type" is verified against vB.
diff --git a/test/ReferenceMap/stack_walk_refmap_jni.cc b/test/ReferenceMap/stack_walk_refmap_jni.cc
index 31bd57f..e276897 100644
--- a/test/ReferenceMap/stack_walk_refmap_jni.cc
+++ b/test/ReferenceMap/stack_walk_refmap_jni.cc
@@ -62,11 +62,6 @@
return true;
}
- // Enable this to dump reference map to LOG(INFO)
- if (false) {
- ScopedObjectAccess ts(Thread::Current());
- art::verifier::MethodVerifier::VerifyMethodAndDump(m);
- }
const uint8_t* ref_bitmap = NULL;
MethodHelper mh(m);
std::string m_name(mh.GetName());