diff options
author | 2012-11-08 10:39:18 -0800 | |
---|---|---|
committer | 2012-11-16 14:01:34 -0800 | |
commit | 2bcb4a496b7aa00d996df3a070524f7568fb35a1 (patch) | |
tree | 8422ab8d65b7422008094b2eaadec0dad87b2df3 | |
parent | efc6369224b036a1fb77849f7ae65b3492c832c0 (diff) |
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
-rw-r--r-- | build/Android.common.mk | 1 | ||||
-rw-r--r-- | src/class_linker.cc | 22 | ||||
-rw-r--r-- | src/debugger.cc | 57 | ||||
-rw-r--r-- | src/dex_instruction.cc | 4 | ||||
-rw-r--r-- | src/disassembler_arm.cc | 6 | ||||
-rw-r--r-- | src/disassembler_mips.cc | 2 | ||||
-rw-r--r-- | src/disassembler_x86.cc | 2 | ||||
-rw-r--r-- | src/gc/mark_sweep.cc | 3 | ||||
-rw-r--r-- | src/indenter.h | 63 | ||||
-rw-r--r-- | src/indenter_test.cc | 35 | ||||
-rw-r--r-- | src/monitor.cc | 3 | ||||
-rw-r--r-- | src/oat/runtime/context.h | 48 | ||||
-rw-r--r-- | src/oatdump.cc | 616 | ||||
-rw-r--r-- | src/stack.cc | 37 | ||||
-rw-r--r-- | src/stack.h | 94 | ||||
-rw-r--r-- | src/thread.cc | 18 | ||||
-rw-r--r-- | src/verifier/method_verifier.cc | 317 | ||||
-rw-r--r-- | src/verifier/method_verifier.h | 24 | ||||
-rw-r--r-- | src/verifier/reg_type.cc | 92 | ||||
-rw-r--r-- | src/verifier/reg_type.h | 101 | ||||
-rw-r--r-- | src/verifier/reg_type_cache.cc | 36 | ||||
-rw-r--r-- | src/verifier/reg_type_cache.h | 32 | ||||
-rw-r--r-- | src/verifier/reg_type_test.cc | 4 | ||||
-rw-r--r-- | src/verifier/register_line.cc | 124 | ||||
-rw-r--r-- | src/verifier/register_line.h | 49 | ||||
-rw-r--r-- | test/ReferenceMap/stack_walk_refmap_jni.cc | 5 |
26 files changed, 1247 insertions, 548 deletions
diff --git a/build/Android.common.mk b/build/Android.common.mk index d20087416a..d86a785e76 100644 --- a/build/Android.common.mk +++ b/build/Android.common.mk @@ -366,6 +366,7 @@ TEST_COMMON_SRC_FILES := \ 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 689b92893e..9a354e1653 100644 --- a/src/class_linker.cc +++ b/src/class_linker.cc @@ -683,6 +683,11 @@ void ClassLinker::RegisterOatFile(const OatFile& oat_file) { 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 @@ const OatFile* ClassLinker::FindOatFileFromOatLocationLocked(const std::string& if (oat_file == NULL) { return NULL; } - CHECK(oat_file != NULL) << oat_location; return oat_file; } @@ -1840,13 +1844,27 @@ void ClassLinker::RegisterDexFile(const DexFile& dex_file, SirtRef<DexCache>& de 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 1d8b997b32..42c0c7fde4 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 @@ struct GetThisVisitor : public StackVisitor { 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 @@ void Dbg::GetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot 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 @@ void Dbg::GetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot 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 @@ void Dbg::GetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot 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 @@ void Dbg::GetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot 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 @@ void Dbg::GetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot 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 @@ void Dbg::GetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot } 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 @@ void Dbg::SetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot 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 @@ void Dbg::SetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot 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 201a8e6fcb..d3aa2389ef 100644 --- a/src/dex_instruction.cc +++ b/src/dex_instruction.cc @@ -289,8 +289,8 @@ std::string Instruction::DumpString(const DexFile* file) const { 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 d047f0e726..15480b9dd9 100644 --- a/src/disassembler_arm.cc +++ b/src/disassembler_arm.cc @@ -275,7 +275,7 @@ void DisassemblerArm::DumpArm(std::ostream& os, const uint8_t* instr_ptr) { 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 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) 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 @@ size_t DisassemblerArm::DumpThumb16(std::ostream& os, const uint8_t* instr_ptr) 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 86a661cbae..0efcb0bbcf 100644 --- a/src/disassembler_mips.cc +++ b/src/disassembler_mips.cc @@ -254,7 +254,7 @@ static void DumpMips(std::ostream& os, const uint8_t* instr_ptr) { } } - 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 35593a3e1c..bb187e153b 100644 --- a/src/disassembler_x86.cc +++ b/src/disassembler_x86.cc @@ -731,7 +731,7 @@ DISASSEMBLER_ENTRY(cmp, 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 d6c44db2b2..1ccceaa824 100644 --- a/src/gc/mark_sweep.cc +++ b/src/gc/mark_sweep.cc @@ -272,8 +272,7 @@ void MarkSweep::VerifyRoot(const Object* root, size_t vreg, const AbstractMethod 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 0000000000..f66df0897a --- /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 0000000000..1919e3d18e --- /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 7c433f7ece..1fbda876ba 100644 --- a/src/monitor.cc +++ b/src/monitor.cc @@ -932,7 +932,8 @@ void Monitor::DescribeLocks(std::ostream& os, StackVisitor* stack_visitor) { } 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 7002f6a658..317030ff54 100644 --- a/src/oat/runtime/context.h +++ b/src/oat/runtime/context.h @@ -57,54 +57,6 @@ class 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]; - } - - /* - * 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 a371d284a7..45d936a0a3 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 @@ class OatDumper { // 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 @@ class OatDumper { } 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); - } - 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); - } - 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); + 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); + } + 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); + } + { + 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 @@ class OatDumper { } 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 @@ class OatDumper { 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 @@ class OatDumper { 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 @@ class OatDumper { ++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 @@ class OatDumper { } 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 @@ class OatDumper { } } + 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 OatDumper { 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"; - - os_ << "IMAGE BEGIN:\n"; - os_ << reinterpret_cast<void*>(image_header_.GetImageBegin()) << "\n\n"; - - os_ << "OAT CHECKSUM:\n"; - os_ << StringPrintf("0x%08x\n\n", image_header_.GetOatChecksum()); - - os_ << "OAT BEGIN:\n"; - os_ << reinterpret_cast<void*>(image_header_.GetOatBegin()) << "\n\n"; - - os_ << "OAT END:\n"; - os_ << 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); + std::ostream& os = *os_; + os << "MAGIC: " << image_header_.GetMagic() << "\n\n"; + + os << "IMAGE BEGIN: " << reinterpret_cast<void*>(image_header_.GetImageBegin()) << "\n\n"; + + os << "OAT CHECKSUM: " << StringPrintf("0x%08x\n\n", image_header_.GetOatChecksum()); + + os << "OAT BEGIN:" << reinterpret_cast<void*>(image_header_.GetOatBegin()) << "\n\n"; + + os << "OAT END:" << reinterpret_cast<void*>(image_header_.GetOatEnd()) << "\n\n"; + + { + 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 @@ class ImageDumper { 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 @@ class ImageDumper { 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 @@ class ImageDumper { 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 @@ class ImageDumper { } } 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 @@ class ImageDumper { 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 @@ class ImageDumper { } 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 @@ class ImageDumper { } } state->stats_.Update(ClassHelper(obj_class).GetDescriptor(), object_bytes); - - state->os_ << summary << std::flush; } std::set<const void*> already_seen_; @@ -1115,7 +1209,7 @@ class ImageDumper { 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 @@ class ImageDumper { } } 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 @@ class ImageDumper { << " 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 @@ class ImageDumper { } } 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; - - CHECK_EQ(file_bytes, header_bytes + object_bytes + alignment_bytes); + { + 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); + } - 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 @@ class ImageDumper { 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 ImageDumper { 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 @@ class ImageDumper { 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 @@ static int oatdump(int argc, char** argv) { 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 8a741c6150..f7126526be 100644 --- a/src/stack.cc +++ b/src/stack.cc @@ -64,49 +64,38 @@ size_t StackVisitor::GetNativePcOffset() const { 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 @@ void StackVisitor::SetVReg(AbstractMethod* m, int vreg, uint32_t new_value) { 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::GetGPR(uint32_t reg) const { 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 fd2fd155da..5b5609c2c6 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 @@ class StackVisitor { 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 @@ class StackVisitor { 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 0d06e4298b..943fdcbf82 100644 --- a/src/thread.cc +++ b/src/thread.cc @@ -1883,20 +1883,10 @@ class ReferenceMapVisitor : public StackVisitor { 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 2149490a8c..d6cd665dd6 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 @@ MethodVerifier::FailureKind MethodVerifier::VerifyMethod(uint32_t method_idx, co 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(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(); - verifier.DumpFailures(LOG(INFO) << "Dump of method " << PrettyMethod(method) << "\n") - << verifier.info_messages_.str() << MutatorLockedDumpable<MethodVerifier>(verifier); + 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 @@ MethodVerifier::MethodVerifier(const DexFile* dex_file, DexCache* dex_cache, 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 @@ std::ostream& MethodVerifier::Fail(VerifyError error) { } } 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 @@ bool MethodVerifier::VerifyCodeFlow() { 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 @@ bool MethodVerifier::VerifyCodeFlow() { 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 @@ void MethodVerifier::Dump(std::ostream& os) { 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 @@ bool MethodVerifier::SetTypesFromSignature() { } 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 @@ bool MethodVerifier::SetTypesFromSignature() { 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 @@ bool MethodVerifier::CodeFlowVerifyMethod() { 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 @@ bool MethodVerifier::CodeFlowVerifyMethod() { #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 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } 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 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { 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 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { 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 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { 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 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { 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 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { 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 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { 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 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { 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 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { 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 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } 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 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { 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 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { 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 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { 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 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { 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 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { 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 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { 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 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { 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 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { 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 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } // 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 @@ AbstractMethod* MethodVerifier::VerifyInvocationArgs(const DecodedInstruction& d 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 @@ AbstractMethod* MethodVerifier::VerifyInvocationArgs(const DecodedInstruction& d 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::VerifyNewArray(const DecodedInstruction& dec_insn, bool is_ } 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 @@ void MethodVerifier::VerifyAGet(const DecodedInstruction& dec_insn, 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 @@ void MethodVerifier::VerifyAGet(const DecodedInstruction& dec_insn, << " 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 @@ Field* MethodVerifier::GetInstanceField(const RegType& obj_type, int field_idx) // 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 @@ void MethodVerifier::VerifyISGet(const DecodedInstruction& dec_insn, 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 @@ InsnFlags* MethodVerifier::CurrentInsnFlags() { } 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 @@ const RegType& MethodVerifier::GetDeclaringClass() { 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 @@ const std::vector<uint8_t>* MethodVerifier::GetDexGcMap(Compiler::MethodReferenc 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 42283a2e19..95d79054fe 100644 --- a/src/verifier/method_verifier.h +++ b/src/verifier/method_verifier.h @@ -113,6 +113,8 @@ enum RegisterTrackingMode { 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 @@ class PcToRegisterLineTable { 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 @@ class MethodVerifier { 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 @@ class MethodVerifier { // 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 @@ class MethodVerifier { // 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 @@ class MethodVerifier { // 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 e02fbf47d0..dc41cebc56 100644 --- a/src/verifier/reg_type.cc +++ b/src/verifier/reg_type.cc @@ -25,19 +25,22 @@ namespace verifier { 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 @@ std::string RegType::Dump(const RegTypeCache* reg_types) const { } 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 @@ std::string RegType::Dump(const RegTypeCache* reg_types) const { } 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 @@ const RegType& RegType::Merge(const RegType& incoming_type, RegTypeCache* reg_ty 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 @@ const RegType& RegType::Merge(const RegType& incoming_type, RegTypeCache* reg_ty 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 205867d4d1..5daaf6013c 100644 --- a/src/verifier/reg_type.h +++ b/src/verifier/reg_type.h @@ -52,10 +52,13 @@ class RegType { 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 @@ class RegType { 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; + } + + 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 just - // approximate to the actual constant value by virtue of merging. + // 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 @@ class RegType { 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 @@ class RegType { : 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 847cde90af..1b91321b13 100644 --- a/src/verifier/reg_type_cache.cc +++ b/src/verifier/reg_type_cache.cc @@ -312,14 +312,41 @@ const RegType& RegTypeCache::FromType(RegType::Type type) { } } -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 @@ const RegType& RegTypeCache::GetComponentType(const RegType& array, ClassLoader* } 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 36fd2c75bc..bf0c5b1cbb 100644 --- a/src/verifier/reg_type_cache.h +++ b/src/verifier/reg_type_cache.h @@ -44,7 +44,10 @@ class RegTypeCache { 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 @@ class RegTypeCache { 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 @@ class RegTypeCache { 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 @@ class RegTypeCache { // 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 6bdf886569..18e9a65cf4 100644 --- a/src/verifier/reg_type_test.cc +++ b/src/verifier/reg_type_test.cc @@ -184,7 +184,7 @@ TEST_F(RegTypeTest, Primitives) { 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 @@ TEST_F(RegTypeTest, Primitives) { 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 4882740dcc..afd2eff077 100644 --- a/src/verifier/register_line.cc +++ b/src/verifier/register_line.cc @@ -35,12 +35,9 @@ bool RegisterLine::CheckConstructorReturn() const { 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 @@ bool RegisterLine::SetRegisterType(uint32_t vdst, const RegType& new_type) { 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::SetResultRegisterType(const RegType& new_type) { } } +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 @@ bool RegisterLine::VerifyRegisterType(uint32_t vsrc, const RegType& check_type) 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 @@ void RegisterLine::CopyRegister2(uint32_t vdst, uint32_t vsrc) { 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 @@ void RegisterLine::CopyResultRegister2(uint32_t vdst) { << "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::CheckUnaryOp(const DecodedInstruction& dec_insn, } } +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::CheckBinaryOp(const DecodedInstruction& dec_insn, } } +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::CheckBinaryOp2addr(const DecodedInstruction& dec_insn, } } +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 9f0fcb05ae..c6c19f8ded 100644 --- a/src/verifier/register_line.h +++ b/src/verifier/register_line.h @@ -48,9 +48,6 @@ enum TypeCategory { // 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 @@ class RegisterLine { 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 @@ class RegisterLine { 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 @@ class RegisterLine { 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 @@ class RegisterLine { 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 31bd57fad2..e2768979e6 100644 --- a/test/ReferenceMap/stack_walk_refmap_jni.cc +++ b/test/ReferenceMap/stack_walk_refmap_jni.cc @@ -62,11 +62,6 @@ struct ReferenceMap2Visitor : public StackVisitor { 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()); |