summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Gampe <agampe@google.com>2015-08-10 23:09:50 +0000
committerAndroid Git Automerger <android-git-automerger@android.com>2015-08-10 23:09:50 +0000
commitab3764e251eef3e515e353a9a20381c5c8dd2694 (patch)
treeaead903f2740b4dd4aa3d3b286c0fa25a4c4f8ff
parenta76c5ff153e1ef788c7eb2abf722a98f8b9b9bac (diff)
parentf695a009725c8c840d916d01c14998f5c5f816d2 (diff)
downloadart-ab3764e251eef3e515e353a9a20381c5c8dd2694.tar.gz
art-ab3764e251eef3e515e353a9a20381c5c8dd2694.tar.bz2
art-ab3764e251eef3e515e353a9a20381c5c8dd2694.zip
am f695a009: ART: Change UnresolvedMergedType internal representation
* commit 'f695a009725c8c840d916d01c14998f5c5f816d2': ART: Change UnresolvedMergedType internal representation
-rw-r--r--compiler/dex/gvn_dead_code_elimination.cc2
-rw-r--r--runtime/base/bit_vector.cc37
-rw-r--r--runtime/base/bit_vector.h13
-rw-r--r--runtime/base/bit_vector_test.cc4
-rw-r--r--runtime/verifier/reg_type.cc61
-rw-r--r--runtime/verifier/reg_type.h51
-rw-r--r--runtime/verifier/reg_type_cache.cc57
-rw-r--r--runtime/verifier/reg_type_test.cc10
-rw-r--r--test/800-smali/expected.txt1
-rw-r--r--test/800-smali/smali/b_22881413.smali291
-rw-r--r--test/800-smali/src/Main.java1
-rwxr-xr-xtest/etc/run-test-jar14
12 files changed, 452 insertions, 90 deletions
diff --git a/compiler/dex/gvn_dead_code_elimination.cc b/compiler/dex/gvn_dead_code_elimination.cc
index 044989e2a9..d29b865ce9 100644
--- a/compiler/dex/gvn_dead_code_elimination.cc
+++ b/compiler/dex/gvn_dead_code_elimination.cc
@@ -74,7 +74,7 @@ inline void GvnDeadCodeElimination::MIRData::RemovePrevChange(int v_reg, MIRData
GvnDeadCodeElimination::VRegChains::VRegChains(uint32_t num_vregs, ScopedArenaAllocator* alloc)
: num_vregs_(num_vregs),
vreg_data_(alloc->AllocArray<VRegValue>(num_vregs, kArenaAllocMisc)),
- vreg_high_words_(num_vregs, false, Allocator::GetNoopAllocator(),
+ vreg_high_words_(false, Allocator::GetNoopAllocator(),
BitVector::BitsToWords(num_vregs),
alloc->AllocArray<uint32_t>(BitVector::BitsToWords(num_vregs))),
mir_data_(alloc->Adapter()) {
diff --git a/runtime/base/bit_vector.cc b/runtime/base/bit_vector.cc
index 39ce0d2cbe..cfd3d24aad 100644
--- a/runtime/base/bit_vector.cc
+++ b/runtime/base/bit_vector.cc
@@ -24,11 +24,7 @@
namespace art {
-// TODO: replace excessive argument defaulting when we are at gcc 4.7
-// or later on host with delegating constructor support. Specifically,
-// starts_bits and storage_size/storage are mutually exclusive.
-BitVector::BitVector(uint32_t start_bits,
- bool expandable,
+BitVector::BitVector(bool expandable,
Allocator* allocator,
uint32_t storage_size,
uint32_t* storage)
@@ -36,12 +32,31 @@ BitVector::BitVector(uint32_t start_bits,
storage_size_(storage_size),
allocator_(allocator),
expandable_(expandable) {
+ DCHECK(storage_ != nullptr);
+
static_assert(sizeof(*storage_) == kWordBytes, "word bytes");
static_assert(sizeof(*storage_) * 8u == kWordBits, "word bits");
- if (storage_ == nullptr) {
- storage_size_ = BitsToWords(start_bits);
- storage_ = static_cast<uint32_t*>(allocator_->Alloc(storage_size_ * kWordBytes));
- }
+}
+
+BitVector::BitVector(uint32_t start_bits,
+ bool expandable,
+ Allocator* allocator)
+ : BitVector(expandable,
+ allocator,
+ BitsToWords(start_bits),
+ static_cast<uint32_t*>(allocator->Alloc(BitsToWords(start_bits) * kWordBytes))) {
+}
+
+
+BitVector::BitVector(const BitVector& src,
+ bool expandable,
+ Allocator* allocator)
+ : BitVector(expandable,
+ allocator,
+ src.storage_size_,
+ static_cast<uint32_t*>(allocator->Alloc(src.storage_size_ * kWordBytes))) {
+ // Direct memcpy would be faster, but this should be fine too and is cleaner.
+ Copy(&src);
}
BitVector::~BitVector() {
@@ -357,4 +372,8 @@ void BitVector::EnsureSize(uint32_t idx) {
}
}
+Allocator* BitVector::GetAllocator() const {
+ return allocator_;
+}
+
} // namespace art
diff --git a/runtime/base/bit_vector.h b/runtime/base/bit_vector.h
index 17835f5610..237bc90c85 100644
--- a/runtime/base/bit_vector.h
+++ b/runtime/base/bit_vector.h
@@ -112,9 +112,16 @@ class BitVector {
BitVector(uint32_t start_bits,
bool expandable,
+ Allocator* allocator);
+
+ BitVector(bool expandable,
Allocator* allocator,
- uint32_t storage_size = 0,
- uint32_t* storage = nullptr);
+ uint32_t storage_size,
+ uint32_t* storage);
+
+ BitVector(const BitVector& src,
+ bool expandable,
+ Allocator* allocator);
virtual ~BitVector();
@@ -231,6 +238,8 @@ class BitVector {
void Dump(std::ostream& os, const char* prefix) const;
+ Allocator* GetAllocator() const;
+
private:
/**
* @brief Dump the bitvector into buffer in a 00101..01 format.
diff --git a/runtime/base/bit_vector_test.cc b/runtime/base/bit_vector_test.cc
index c51b9b0570..76095c28be 100644
--- a/runtime/base/bit_vector_test.cc
+++ b/runtime/base/bit_vector_test.cc
@@ -71,7 +71,7 @@ TEST(BitVector, NoopAllocator) {
uint32_t bits[kWords];
memset(bits, 0, sizeof(bits));
- BitVector bv(0U, false, Allocator::GetNoopAllocator(), kWords, bits);
+ BitVector bv(false, Allocator::GetNoopAllocator(), kWords, bits);
EXPECT_EQ(kWords, bv.GetStorageSize());
EXPECT_EQ(kWords * sizeof(uint32_t), bv.GetSizeOf());
EXPECT_EQ(bits, bv.GetRawStorage());
@@ -128,7 +128,7 @@ TEST(BitVector, SetInitialBits) {
uint32_t bits[kWords];
memset(bits, 0, sizeof(bits));
- BitVector bv(0U, false, Allocator::GetNoopAllocator(), kWords, bits);
+ BitVector bv(false, Allocator::GetNoopAllocator(), kWords, bits);
bv.SetInitialBits(0u);
EXPECT_EQ(0u, bv.NumSetBits());
bv.SetInitialBits(1u);
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index 1435607e48..2cdb73d2f1 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -16,6 +16,7 @@
#include "reg_type-inl.h"
+#include "base/bit_vector-inl.h"
#include "base/casts.h"
#include "class_linker-inl.h"
#include "dex_file-inl.h"
@@ -307,13 +308,17 @@ PreciseReferenceType::PreciseReferenceType(mirror::Class* klass, const std::stri
std::string UnresolvedMergedType::Dump() const {
std::stringstream result;
- std::set<uint16_t> types = GetMergedTypes();
- result << "UnresolvedMergedReferences(";
- auto it = types.begin();
- result << reg_type_cache_->GetFromId(*it).Dump();
- for (++it; it != types.end(); ++it) {
- result << ", ";
- result << reg_type_cache_->GetFromId(*it).Dump();
+ result << "UnresolvedMergedReferences(" << GetResolvedPart().Dump() << " | ";
+ const BitVector& types = GetUnresolvedTypes();
+
+ bool first = true;
+ for (uint32_t idx : types.Indexes()) {
+ if (!first) {
+ result << ", ";
+ } else {
+ first = false;
+ }
+ result << reg_type_cache_->GetFromId(idx).Dump();
}
result << ")";
return result.str();
@@ -490,32 +495,6 @@ bool UnresolvedType::IsNonZeroReferenceTypes() const {
return true;
}
-std::set<uint16_t> UnresolvedMergedType::GetMergedTypes() const {
- std::pair<uint16_t, uint16_t> refs = GetTopMergedTypes();
- const RegType& left = reg_type_cache_->GetFromId(refs.first);
- const RegType& right = reg_type_cache_->GetFromId(refs.second);
-
- std::set<uint16_t> types;
- if (left.IsUnresolvedMergedReference()) {
- types = down_cast<const UnresolvedMergedType*>(&left)->GetMergedTypes();
- } else {
- types.insert(refs.first);
- }
- if (right.IsUnresolvedMergedReference()) {
- std::set<uint16_t> right_types =
- down_cast<const UnresolvedMergedType*>(&right)->GetMergedTypes();
- types.insert(right_types.begin(), right_types.end());
- } else {
- types.insert(refs.second);
- }
- if (kIsDebugBuild) {
- for (const auto& type : types) {
- CHECK(!reg_type_cache_->GetFromId(type).IsUnresolvedMergedReference());
- }
- }
- return types;
-}
-
const RegType& RegType::GetSuperClass(RegTypeCache* cache) const {
if (!IsUnresolvedTypes()) {
mirror::Class* super_klass = GetClass()->GetSuperClass();
@@ -805,12 +784,24 @@ void UnresolvedUninitializedRefType::CheckInvariants() const {
CHECK(klass_.IsNull()) << *this;
}
+UnresolvedMergedType::UnresolvedMergedType(const RegType& resolved,
+ const BitVector& unresolved,
+ const RegTypeCache* reg_type_cache,
+ uint16_t cache_id)
+ : UnresolvedType("", cache_id),
+ reg_type_cache_(reg_type_cache),
+ resolved_part_(resolved),
+ unresolved_types_(unresolved, false, unresolved.GetAllocator()) {
+ if (kIsDebugBuild) {
+ CheckInvariants();
+ }
+}
void UnresolvedMergedType::CheckInvariants() const {
// Unresolved merged types: merged types should be defined.
CHECK(descriptor_.empty()) << *this;
CHECK(klass_.IsNull()) << *this;
- CHECK_NE(merged_types_.first, 0U) << *this;
- CHECK_NE(merged_types_.second, 0U) << *this;
+ CHECK(resolved_part_.IsReferenceTypes());
+ CHECK(!resolved_part_.IsUnresolvedTypes());
}
void UnresolvedReferenceType::CheckInvariants() const {
diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h
index d08c937a64..2af0eadb71 100644
--- a/runtime/verifier/reg_type.h
+++ b/runtime/verifier/reg_type.h
@@ -22,6 +22,7 @@
#include <set>
#include <string>
+#include "base/bit_vector.h"
#include "base/macros.h"
#include "base/mutex.h"
#include "gc_root.h"
@@ -230,6 +231,14 @@ class RegType {
// from another.
const RegType& Merge(const RegType& incoming_type, RegTypeCache* reg_types) const
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Same as above, but also handles the case where incoming_type == this.
+ const RegType& SafeMerge(const RegType& incoming_type, RegTypeCache* reg_types) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ if (Equals(incoming_type)) {
+ return *this;
+ }
+ return Merge(incoming_type, reg_types);
+ }
/*
* A basic Join operation on classes. For a pair of types S and T the Join,
@@ -868,30 +877,23 @@ class UnresolvedSuperClass FINAL : public UnresolvedType {
const RegTypeCache* const reg_type_cache_;
};
-// A merge of two unresolved types. If the types were resolved this may be
-// Conflict or another
-// known ReferenceType.
+// A merge of unresolved (and resolved) types. If the types were resolved this may be
+// Conflict or another known ReferenceType.
class UnresolvedMergedType FINAL : public UnresolvedType {
public:
- UnresolvedMergedType(uint16_t left_id, uint16_t right_id,
+ // Note: the constructor will copy the unresolved BitVector, not use it directly.
+ UnresolvedMergedType(const RegType& resolved, const BitVector& unresolved,
const RegTypeCache* reg_type_cache, uint16_t cache_id)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : UnresolvedType("", cache_id),
- reg_type_cache_(reg_type_cache),
- merged_types_(left_id, right_id) {
- if (kIsDebugBuild) {
- CheckInvariants();
- }
- }
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- // The top of a tree of merged types.
- std::pair<uint16_t, uint16_t> GetTopMergedTypes() const {
- DCHECK(IsUnresolvedMergedReference());
- return merged_types_;
+ // The resolved part. See description below.
+ const RegType& GetResolvedPart() const {
+ return resolved_part_;
+ }
+ // The unresolved part.
+ const BitVector& GetUnresolvedTypes() const {
+ return unresolved_types_;
}
-
- // The complete set of merged types.
- std::set<uint16_t> GetMergedTypes() const;
bool IsUnresolvedMergedReference() const OVERRIDE { return true; }
@@ -903,7 +905,16 @@ class UnresolvedMergedType FINAL : public UnresolvedType {
void CheckInvariants() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
const RegTypeCache* const reg_type_cache_;
- const std::pair<uint16_t, uint16_t> merged_types_;
+
+ // The original implementation of merged types was a binary tree. Collection of the flattened
+ // types ("leaves") can be expensive, so we store the expanded list now, as two components:
+ // 1) A resolved component. We use Zero when there is no resolved component, as that will be
+ // an identity merge.
+ // 2) A bitvector of the unresolved reference types. A bitvector was chosen with the assumption
+ // that there should not be too many types in flight in practice. (We also bias the index
+ // against the index of Zero, which is one of the later default entries in any cache.)
+ const RegType& resolved_part_;
+ const BitVector unresolved_types_;
};
std::ostream& operator<<(std::ostream& os, const RegType& rhs)
diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc
index b371d7e391..a597c8fdf9 100644
--- a/runtime/verifier/reg_type_cache.cc
+++ b/runtime/verifier/reg_type_cache.cc
@@ -317,39 +317,62 @@ void RegTypeCache::CreatePrimitiveAndSmallConstantTypes() {
}
const RegType& RegTypeCache::FromUnresolvedMerge(const RegType& left, const RegType& right) {
- std::set<uint16_t> types;
+ BitVector types(1, // Allocate at least a word.
+ true, // Is expandable.
+ Allocator::GetMallocAllocator()); // TODO: Arenas in the verifier.
+ const RegType* left_resolved;
if (left.IsUnresolvedMergedReference()) {
- RegType& non_const(const_cast<RegType&>(left));
- types = (down_cast<UnresolvedMergedType*>(&non_const))->GetMergedTypes();
+ const UnresolvedMergedType* left_merge = down_cast<const UnresolvedMergedType*>(&left);
+ types.Copy(&left_merge->GetUnresolvedTypes());
+ left_resolved = &left_merge->GetResolvedPart();
+ } else if (left.IsUnresolvedReference()) {
+ types.SetBit(left.GetId());
+ left_resolved = &Zero();
} else {
- types.insert(left.GetId());
+ left_resolved = &left;
}
+
+ const RegType* right_resolved;
if (right.IsUnresolvedMergedReference()) {
- RegType& non_const(const_cast<RegType&>(right));
- std::set<uint16_t> right_types = (down_cast<UnresolvedMergedType*>(&non_const))->GetMergedTypes();
- types.insert(right_types.begin(), right_types.end());
+ const UnresolvedMergedType* right_merge = down_cast<const UnresolvedMergedType*>(&right);
+ types.Union(&right_merge->GetUnresolvedTypes());
+ right_resolved = &right_merge->GetResolvedPart();
+ } else if (right.IsUnresolvedReference()) {
+ types.SetBit(right.GetId());
+ right_resolved = &Zero();
} else {
- types.insert(right.GetId());
+ right_resolved = &right;
+ }
+
+ // Merge the resolved parts. Left and right might be equal, so use SafeMerge.
+ const RegType& resolved_parts_merged = left_resolved->SafeMerge(*right_resolved, this);
+ // If we get a conflict here, the merge result is a conflict, not an unresolved merge type.
+ if (resolved_parts_merged.IsConflict()) {
+ return Conflict();
}
+
// Check if entry already exists.
for (size_t i = primitive_count_; i < entries_.size(); i++) {
const RegType* cur_entry = entries_[i];
if (cur_entry->IsUnresolvedMergedReference()) {
- std::set<uint16_t> cur_entry_types =
- (down_cast<const UnresolvedMergedType*>(cur_entry))->GetMergedTypes();
- if (cur_entry_types == types) {
+ const UnresolvedMergedType* cmp_type = down_cast<const UnresolvedMergedType*>(cur_entry);
+ const RegType& resolved_part = cmp_type->GetResolvedPart();
+ const BitVector& unresolved_part = cmp_type->GetUnresolvedTypes();
+ // Use SameBitsSet. "types" is expandable to allow merging in the components, but the
+ // BitVector in the final RegType will be made non-expandable.
+ if (&resolved_part == &resolved_parts_merged &&
+ types.SameBitsSet(&unresolved_part)) {
return *cur_entry;
}
}
}
+
// Create entry.
- RegType* entry = new UnresolvedMergedType(left.GetId(), right.GetId(), this, entries_.size());
+ RegType* entry = new UnresolvedMergedType(resolved_parts_merged,
+ types,
+ this,
+ entries_.size());
AddEntry(entry);
- if (kIsDebugBuild) {
- UnresolvedMergedType* tmp_entry = down_cast<UnresolvedMergedType*>(entry);
- std::set<uint16_t> check_types = tmp_entry->GetMergedTypes();
- CHECK(check_types == types);
- }
return *entry;
}
diff --git a/runtime/verifier/reg_type_test.cc b/runtime/verifier/reg_type_test.cc
index 2fecc8b25f..971b1f5d0f 100644
--- a/runtime/verifier/reg_type_test.cc
+++ b/runtime/verifier/reg_type_test.cc
@@ -18,6 +18,7 @@
#include <set>
+#include "base/bit_vector.h"
#include "base/casts.h"
#include "common_runtime_test.h"
#include "reg_type_cache-inl.h"
@@ -421,7 +422,7 @@ TEST_F(RegTypeReferenceTest, Dump) {
EXPECT_EQ(expected, resolved_unintialiesd.Dump());
expected = "Unresolved And Uninitialized Reference: java.lang.DoesNotExist Allocation PC: 12";
EXPECT_EQ(expected, unresolved_unintialized.Dump());
- expected = "UnresolvedMergedReferences(Unresolved Reference: java.lang.DoesNotExist, Unresolved Reference: java.lang.DoesNotExistEither)";
+ expected = "UnresolvedMergedReferences(Zero/null | Unresolved Reference: java.lang.DoesNotExist, Unresolved Reference: java.lang.DoesNotExistEither)";
EXPECT_EQ(expected, unresolved_merged.Dump());
}
@@ -477,9 +478,10 @@ TEST_F(RegTypeReferenceTest, Merging) {
EXPECT_TRUE(merged.IsUnresolvedMergedReference());
RegType& merged_nonconst = const_cast<RegType&>(merged);
- std::set<uint16_t> merged_ids = (down_cast<UnresolvedMergedType*>(&merged_nonconst))->GetMergedTypes();
- EXPECT_EQ(ref_type_0.GetId(), *(merged_ids.begin()));
- EXPECT_EQ(ref_type_1.GetId(), *((++merged_ids.begin())));
+ const BitVector& unresolved_parts =
+ down_cast<UnresolvedMergedType*>(&merged_nonconst)->GetUnresolvedTypes();
+ EXPECT_TRUE(unresolved_parts.IsBitSet(ref_type_0.GetId()));
+ EXPECT_TRUE(unresolved_parts.IsBitSet(ref_type_1.GetId()));
}
TEST_F(RegTypeTest, MergingFloat) {
diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt
index 659f1044aa..77668daadd 100644
--- a/test/800-smali/expected.txt
+++ b/test/800-smali/expected.txt
@@ -27,4 +27,5 @@ b/22244733
b/22331663
b/22331663 (pass)
b/22331663 (fail)
+b/22881413
Done!
diff --git a/test/800-smali/smali/b_22881413.smali b/test/800-smali/smali/b_22881413.smali
new file mode 100644
index 0000000000..f624734353
--- /dev/null
+++ b/test/800-smali/smali/b_22881413.smali
@@ -0,0 +1,291 @@
+.class public LB22881413;
+.super Ljava/lang/Object;
+
+# A couple of fields to allow "loading" resolved and unresolved types. Use non-final classes to
+# avoid automatically getting precise reference types.
+.field private static res1:Ljava/lang/Number;
+.field private static res2:Ljava/lang/ClassLoader;
+.field private static res3:Ljava/lang/Package;
+.field private static res4:Ljava/lang/RuntimeException;
+.field private static res5:Ljava/lang/Exception;
+.field private static res6:Ljava/util/ArrayList;
+.field private static res7:Ljava/util/LinkedList;
+.field private static res8:Ljava/lang/Thread;
+.field private static res9:Ljava/lang/ThreadGroup;
+.field private static res10:Ljava/lang/Runtime;
+
+.field private static unres1:La/b/c/d1;
+.field private static unres2:La/b/c/d2;
+.field private static unres3:La/b/c/d3;
+.field private static unres4:La/b/c/d4;
+.field private static unres5:La/b/c/d5;
+.field private static unres6:La/b/c/d6;
+.field private static unres7:La/b/c/d7;
+.field private static unres8:La/b/c/d8;
+.field private static unres9:La/b/c/d9;
+.field private static unres10:La/b/c/d10;
+
+.field private static unresBase0:La/b/c/dBase0;
+.field private static unresBase1:La/b/c/dBase1;
+.field private static unresBase2:La/b/c/dBase2;
+.field private static unresBase3:La/b/c/dBase3;
+.field private static unresBase4:La/b/c/dBase4;
+.field private static unresBase5:La/b/c/dBase5;
+.field private static unresBase6:La/b/c/dBase6;
+.field private static unresBase7:La/b/c/dBase7;
+.field private static unresBase8:La/b/c/dBase8;
+
+# Empty, ignore this. We want to see if the other method can be verified in a reasonable amount of
+# time.
+.method public static run()V
+.registers 2
+ return-void
+.end method
+
+.method public static foo(IZZ) V
+.registers 11
+ # v8 = int, v9 = boolean, v10 = boolean
+
+ sget-object v0, LB22881413;->unresBase0:La/b/c/dBase0;
+ const v1, 0
+ const v2, 0
+
+# We're trying to create something like this (with more loops to amplify things).
+#
+# v0 = Unresolved1
+# while (something) {
+#
+# [Repeatedly]
+# if (cond) {
+# v0 = ResolvedX;
+# } else {
+# v0 = UnresolvedX;
+# }
+#
+# v0 = Unresolved2
+# };
+#
+# Important points:
+# 1) Use a while, so that the end of the loop is a goto. That way, the merging of outer-loop
+# unresolved classes is postponed.
+# 2) Put the else cases after all if cases. That way there are backward gotos that will lead
+# to stabilization loops in the body.
+#
+
+:Loop1
+
+ const v6, 0
+ add-int/lit16 v8, v8, -1
+ if-ge v8, v6, :Loop1End
+
+:Loop2
+
+ const v6, 0
+ add-int/lit16 v8, v8, -1
+ if-ge v8, v6, :Loop2End
+
+:Loop3
+
+ const v6, 0
+ add-int/lit16 v8, v8, -1
+ if-ge v8, v6, :Loop3End
+
+:Loop4
+
+ const v6, 0
+ add-int/lit16 v8, v8, -1
+ if-ge v8, v6, :Loop4End
+
+:Loop5
+
+ const v6, 0
+ add-int/lit16 v8, v8, -1
+ if-ge v8, v6, :Loop5End
+
+:Loop6
+
+ const v6, 0
+ add-int/lit16 v8, v8, -1
+ if-ge v8, v6, :Loop6End
+
+:Loop7
+
+ const v6, 0
+ add-int/lit16 v8, v8, -1
+ if-ge v8, v6, :Loop7End
+
+:Loop8
+
+ const v6, 0
+ add-int/lit16 v8, v8, -1
+ if-ge v8, v6, :Loop8End
+
+# Prototype:
+#
+# if-eqz v9, :ElseX
+# sget-object v0, LB22881413;->res1:Ljava/lang/Number;
+#:JoinX
+#
+# And somewhere at the end
+#
+#:ElseX
+# sget-object v0, LB22881413;->unresX:La/b/c/dX;
+# goto :JoinX
+#
+#
+
+ if-eqz v10, :Join1
+ if-eqz v9, :Else1
+ sget-object v0, LB22881413;->res1:Ljava/lang/Number;
+:Join1
+
+
+ if-eqz v10, :Join2
+ if-eqz v9, :Else2
+ sget-object v0, LB22881413;->res2:Ljava/lang/ClassLoader;
+:Join2
+
+
+ if-eqz v10, :Join3
+ if-eqz v9, :Else3
+ sget-object v0, LB22881413;->res3:Ljava/lang/Package;
+:Join3
+
+
+ if-eqz v10, :Join4
+ if-eqz v9, :Else4
+ sget-object v0, LB22881413;->res4:Ljava/lang/RuntimeException;
+:Join4
+
+
+ if-eqz v10, :Join5
+ if-eqz v9, :Else5
+ sget-object v0, LB22881413;->res5:Ljava/lang/Exception;
+:Join5
+
+
+ if-eqz v10, :Join6
+ if-eqz v9, :Else6
+ sget-object v0, LB22881413;->res6:Ljava/util/ArrayList;
+:Join6
+
+
+ if-eqz v10, :Join7
+ if-eqz v9, :Else7
+ sget-object v0, LB22881413;->res7:Ljava/util/LinkedList;
+:Join7
+
+
+ if-eqz v10, :Join8
+ if-eqz v9, :Else8
+ sget-object v0, LB22881413;->res8:Ljava/lang/Thread;
+:Join8
+
+
+ if-eqz v10, :Join9
+ if-eqz v9, :Else9
+ sget-object v0, LB22881413;->res9:Ljava/lang/ThreadGroup;
+:Join9
+
+
+ if-eqz v10, :Join10
+ if-eqz v9, :Else10
+ sget-object v0, LB22881413;->res10:Ljava/lang/Runtime;
+:Join10
+
+
+ goto :InnerMostLoopEnd
+
+:Else1
+ sget-object v0, LB22881413;->unres1:La/b/c/d1;
+ goto :Join1
+
+:Else2
+ sget-object v0, LB22881413;->unres2:La/b/c/d2;
+ goto :Join2
+
+:Else3
+ sget-object v0, LB22881413;->unres3:La/b/c/d3;
+ goto :Join3
+
+:Else4
+ sget-object v0, LB22881413;->unres4:La/b/c/d4;
+ goto :Join4
+
+:Else5
+ sget-object v0, LB22881413;->unres5:La/b/c/d5;
+ goto :Join5
+
+:Else6
+ sget-object v0, LB22881413;->unres6:La/b/c/d6;
+ goto :Join6
+
+:Else7
+ sget-object v0, LB22881413;->unres7:La/b/c/d7;
+ goto :Join7
+
+:Else8
+ sget-object v0, LB22881413;->unres8:La/b/c/d8;
+ goto :Join8
+
+:Else9
+ sget-object v0, LB22881413;->unres9:La/b/c/d9;
+ goto :Join9
+
+:Else10
+ sget-object v0, LB22881413;->unres10:La/b/c/d10;
+ goto :Join10
+
+:InnerMostLoopEnd
+
+ # Loop 8 end of body.
+ sget-object v0, LB22881413;->unresBase8:La/b/c/dBase8;
+ goto :Loop8
+
+:Loop8End
+
+ # Loop 7 end of body.
+ sget-object v0, LB22881413;->unresBase7:La/b/c/dBase7;
+ goto :Loop7
+
+:Loop7End
+
+ # Loop 6 end of body.
+ sget-object v0, LB22881413;->unresBase6:La/b/c/dBase6;
+ goto :Loop6
+
+:Loop6End
+
+ # Loop 5 end of body
+ sget-object v0, LB22881413;->unresBase5:La/b/c/dBase5;
+ goto :Loop5
+
+:Loop5End
+
+ # Loop 4 end of body
+ sget-object v0, LB22881413;->unresBase4:La/b/c/dBase4;
+ goto :Loop4
+
+:Loop4End
+
+ # Loop 3 end of body
+ sget-object v0, LB22881413;->unresBase3:La/b/c/dBase3;
+ goto :Loop3
+
+:Loop3End
+
+ # Loop 2 end of body
+ sget-object v0, LB22881413;->unresBase2:La/b/c/dBase2;
+ goto :Loop2
+
+:Loop2End
+
+ # Loop 1 end of body
+ sget-object v0, LB22881413;->unresBase1:La/b/c/dBase1;
+ goto :Loop1
+
+:Loop1End
+
+ return-void
+
+.end method
diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java
index 709c7f66a9..7ee1e45312 100644
--- a/test/800-smali/src/Main.java
+++ b/test/800-smali/src/Main.java
@@ -101,6 +101,7 @@ public class Main {
new Object[] { false }, null, null));
testCases.add(new TestCase("b/22331663 (fail)", "B22331663Fail", "run",
new Object[] { false }, new VerifyError(), null));
+ testCases.add(new TestCase("b/22881413", "B22881413", "run", null, null, null));
}
public void runTests() {
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 240ed41ff2..1a8853a559 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -316,6 +316,20 @@ if [ "$PREBUILD" = "y" ]; then
--dex-file=$DEX_LOCATION/$TEST_NAME.jar \
--oat-file=$DEX_LOCATION/dalvik-cache/$ISA/$(echo $DEX_LOCATION/$TEST_NAME.jar/classes.dex | cut -d/ -f 2- | sed "s:/:@:g") \
--instruction-set=$ISA"
+ if [ "x$INSTRUCTION_SET_FEATURES" != "x" ] ; then
+ dex2oat_cmdline="${dex2oat_cmdline} --instruction-set-features=${INSTRUCTION_SET_FEATURES}"
+ fi
+
+ # Add in a timeout. This is important for testing the compilation/verification time of
+ # pathological cases.
+ # Note: as we don't know how decent targets are (e.g., emulator), only do this on the host for
+ # now. We should try to improve this.
+ # The current value is rather arbitrary. run-tests should compile quickly.
+ if [ "$HOST" != "n" ]; then
+ # Use SIGRTMIN+2 to try to dump threads.
+ # Use -k 1m to SIGKILL it a minute later if it hasn't ended.
+ dex2oat_cmdline="timeout -k 1m -s SIGRTMIN+2 1m ${dex2oat_cmdline}"
+ fi
fi
dalvikvm_cmdline="$INVOKE_WITH $GDB $ANDROID_ROOT/bin/$DALVIKVM \