Merge "Regression test for array aliasing bug."
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index bd530ac..6e087c5 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -2879,9 +2879,9 @@
bool CompilerDriver::GetCompiledClass(ClassReference ref, mirror::Class::Status* status) const {
DCHECK(status != nullptr);
// The table doesn't know if something wasn't inserted. For this case it will return
- // kStatusNotReady. To handle this, just assume anything not verified is not compiled.
+ // kStatusNotReady. To handle this, just assume anything we didn't try to verify is not compiled.
if (!compiled_classes_.Get(DexFileReference(ref.first, ref.second), status) ||
- *status < mirror::Class::kStatusVerified) {
+ *status < mirror::Class::kStatusRetryVerificationAtRuntime) {
return false;
}
return true;
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index fee6afb..4da3e0d 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -366,6 +366,40 @@
CheckVerifiedClass(class_loader, "LSecond;");
}
+// Test that a class of status kStatusRetryVerificationAtRuntime is indeed recorded that way in the
+// driver.
+TEST_F(CompilerDriverVerifyTest, RetryVerifcationStatus) {
+ Thread* const self = Thread::Current();
+ jobject class_loader;
+ std::vector<const DexFile*> dex_files;
+ const DexFile* dex_file = nullptr;
+ {
+ ScopedObjectAccess soa(self);
+ class_loader = LoadDex("ProfileTestMultiDex");
+ ASSERT_NE(class_loader, nullptr);
+ dex_files = GetDexFiles(class_loader);
+ ASSERT_GT(dex_files.size(), 0u);
+ dex_file = dex_files.front();
+ }
+ compiler_driver_->SetDexFilesForOatFile(dex_files);
+ ClassReference ref(dex_file, 0u);
+ // Test that the status is read from the compiler driver as expected.
+ for (size_t i = mirror::Class::kStatusRetryVerificationAtRuntime;
+ i < mirror::Class::kStatusMax;
+ ++i) {
+ const mirror::Class::Status expected_status = static_cast<mirror::Class::Status>(i);
+ // Skip unsupported status that are not supposed to be ever recorded.
+ if (expected_status == mirror::Class::kStatusVerifyingAtRuntime ||
+ expected_status == mirror::Class::kStatusInitializing) {
+ continue;
+ }
+ compiler_driver_->RecordClassStatus(ref, expected_status);
+ mirror::Class::Status status = {};
+ ASSERT_TRUE(compiler_driver_->GetCompiledClass(ref, &status));
+ EXPECT_EQ(status, expected_status);
+ }
+}
+
// TODO: need check-cast test (when stub complete & we can throw/catch
} // namespace art
diff --git a/compiler/optimizing/code_generator_vector_arm64.cc b/compiler/optimizing/code_generator_vector_arm64.cc
index f422b9f..9095ecd 100644
--- a/compiler/optimizing/code_generator_vector_arm64.cc
+++ b/compiler/optimizing/code_generator_vector_arm64.cc
@@ -15,7 +15,9 @@
*/
#include "code_generator_arm64.h"
+
#include "mirror/array-inl.h"
+#include "mirror/string.h"
using namespace vixl::aarch64; // NOLINT(build/namespaces)
diff --git a/compiler/optimizing/code_generator_vector_x86.cc b/compiler/optimizing/code_generator_vector_x86.cc
index 14782d7..e7aec76 100644
--- a/compiler/optimizing/code_generator_vector_x86.cc
+++ b/compiler/optimizing/code_generator_vector_x86.cc
@@ -15,7 +15,9 @@
*/
#include "code_generator_x86.h"
+
#include "mirror/array-inl.h"
+#include "mirror/string.h"
namespace art {
namespace x86 {
diff --git a/compiler/optimizing/code_generator_vector_x86_64.cc b/compiler/optimizing/code_generator_vector_x86_64.cc
index 246044e..c7ee81c 100644
--- a/compiler/optimizing/code_generator_vector_x86_64.cc
+++ b/compiler/optimizing/code_generator_vector_x86_64.cc
@@ -15,7 +15,9 @@
*/
#include "code_generator_x86_64.h"
+
#include "mirror/array-inl.h"
+#include "mirror/string.h"
namespace art {
namespace x86_64 {
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 4cea6df..2669d97 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -22,6 +22,7 @@
#include "entrypoints/quick/quick_entrypoints.h"
#include "intrinsics.h"
#include "mirror/array-inl.h"
+#include "mirror/object_array-inl.h"
#include "mirror/string.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index d785567..74be954 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -22,6 +22,7 @@
#include "entrypoints/quick/quick_entrypoints.h"
#include "intrinsics.h"
#include "mirror/array-inl.h"
+#include "mirror/object_array-inl.h"
#include "mirror/string.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
diff --git a/compiler/optimizing/load_store_analysis.h b/compiler/optimizing/load_store_analysis.h
index 86fb8e0..a2c1794 100644
--- a/compiler/optimizing/load_store_analysis.h
+++ b/compiler/optimizing/load_store_analysis.h
@@ -466,6 +466,11 @@
CreateReferenceInfoForReferenceType(new_instance);
}
+ void VisitNewArray(HNewArray* new_array) OVERRIDE {
+ // Any references appearing in the ref_info_array_ so far cannot alias with new_array.
+ CreateReferenceInfoForReferenceType(new_array);
+ }
+
void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* instruction) OVERRIDE {
CreateReferenceInfoForReferenceType(instruction);
}
@@ -478,6 +483,22 @@
CreateReferenceInfoForReferenceType(instruction);
}
+ void VisitInvokeUnresolved(HInvokeUnresolved* instruction) OVERRIDE {
+ CreateReferenceInfoForReferenceType(instruction);
+ }
+
+ void VisitInvokePolymorphic(HInvokePolymorphic* instruction) OVERRIDE {
+ CreateReferenceInfoForReferenceType(instruction);
+ }
+
+ void VisitLoadString(HLoadString* instruction) OVERRIDE {
+ CreateReferenceInfoForReferenceType(instruction);
+ }
+
+ void VisitPhi(HPhi* instruction) OVERRIDE {
+ CreateReferenceInfoForReferenceType(instruction);
+ }
+
void VisitParameterValue(HParameterValue* instruction) OVERRIDE {
CreateReferenceInfoForReferenceType(instruction);
}
diff --git a/compiler/optimizing/loop_optimization_test.cc b/compiler/optimizing/loop_optimization_test.cc
index 5b93506..b5b03d8 100644
--- a/compiler/optimizing/loop_optimization_test.cc
+++ b/compiler/optimizing/loop_optimization_test.cc
@@ -195,4 +195,44 @@
EXPECT_EQ("[[[[[[[[[[][][][][][][][][][]]]]]]]]]]", LoopStructure());
}
+// Check that SimplifyLoop() doesn't invalidate data flow when ordering loop headers'
+// predecessors.
+TEST_F(LoopOptimizationTest, SimplifyLoop) {
+ // Can't use AddLoop as we want special order for blocks predecessors.
+ HBasicBlock* header = new (&allocator_) HBasicBlock(graph_);
+ HBasicBlock* body = new (&allocator_) HBasicBlock(graph_);
+ graph_->AddBlock(header);
+ graph_->AddBlock(body);
+
+ // Control flow: make a loop back edge first in the list of predecessors.
+ entry_block_->RemoveSuccessor(return_block_);
+ body->AddSuccessor(header);
+ entry_block_->AddSuccessor(header);
+ header->AddSuccessor(body);
+ header->AddSuccessor(return_block_);
+ DCHECK(header->GetSuccessors()[1] == return_block_);
+
+ // Data flow.
+ header->AddInstruction(new (&allocator_) HIf(parameter_));
+ body->AddInstruction(new (&allocator_) HGoto());
+
+ HPhi* phi = new (&allocator_) HPhi(&allocator_, 0, 0, Primitive::kPrimInt);
+ HInstruction* add = new (&allocator_) HAdd(Primitive::kPrimInt, phi, parameter_);
+ header->AddPhi(phi);
+ body->AddInstruction(add);
+
+ phi->AddInput(add);
+ phi->AddInput(parameter_);
+
+ graph_->ClearLoopInformation();
+ graph_->ClearDominanceInformation();
+ graph_->BuildDominatorTree();
+
+ // Check that after optimizations in BuildDominatorTree()/SimplifyCFG() phi inputs
+ // are still mapped correctly to the block predecessors.
+ for (size_t i = 0, e = phi->InputCount(); i < e; i++) {
+ HInstruction* input = phi->InputAt(i);
+ ASSERT_TRUE(input->GetBlock()->Dominates(header->GetPredecessors()[i]));
+ }
+}
} // namespace art
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 3a1864b..ddd798b 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -358,6 +358,35 @@
}
}
+// Reorder phi inputs to match reordering of the block's predecessors.
+static void FixPhisAfterPredecessorsReodering(HBasicBlock* block, size_t first, size_t second) {
+ for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+ HPhi* phi = it.Current()->AsPhi();
+ HInstruction* first_instr = phi->InputAt(first);
+ HInstruction* second_instr = phi->InputAt(second);
+ phi->ReplaceInput(first_instr, second);
+ phi->ReplaceInput(second_instr, first);
+ }
+}
+
+// Make sure that the first predecessor of a loop header is the incoming block.
+void HGraph::OrderLoopHeaderPredecessors(HBasicBlock* header) {
+ DCHECK(header->IsLoopHeader());
+ HLoopInformation* info = header->GetLoopInformation();
+ if (info->IsBackEdge(*header->GetPredecessors()[0])) {
+ HBasicBlock* to_swap = header->GetPredecessors()[0];
+ for (size_t pred = 1, e = header->GetPredecessors().size(); pred < e; ++pred) {
+ HBasicBlock* predecessor = header->GetPredecessors()[pred];
+ if (!info->IsBackEdge(*predecessor)) {
+ header->predecessors_[pred] = to_swap;
+ header->predecessors_[0] = predecessor;
+ FixPhisAfterPredecessorsReodering(header, 0, pred);
+ break;
+ }
+ }
+ }
+}
+
void HGraph::SimplifyLoop(HBasicBlock* header) {
HLoopInformation* info = header->GetLoopInformation();
@@ -381,18 +410,7 @@
pre_header->AddSuccessor(header);
}
- // Make sure the first predecessor of a loop header is the incoming block.
- if (info->IsBackEdge(*header->GetPredecessors()[0])) {
- HBasicBlock* to_swap = header->GetPredecessors()[0];
- for (size_t pred = 1, e = header->GetPredecessors().size(); pred < e; ++pred) {
- HBasicBlock* predecessor = header->GetPredecessors()[pred];
- if (!info->IsBackEdge(*predecessor)) {
- header->predecessors_[pred] = to_swap;
- header->predecessors_[0] = predecessor;
- break;
- }
- }
- }
+ OrderLoopHeaderPredecessors(header);
HInstruction* first_instruction = header->GetFirstInstruction();
if (first_instruction != nullptr && first_instruction->IsSuspendCheck()) {
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index e443142..488d472 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -418,6 +418,7 @@
HBasicBlock* SplitEdge(HBasicBlock* block, HBasicBlock* successor);
void SplitCriticalEdge(HBasicBlock* block, HBasicBlock* successor);
+ void OrderLoopHeaderPredecessors(HBasicBlock* header);
void SimplifyLoop(HBasicBlock* header);
int32_t GetNextInstructionId() {
diff --git a/compiler/optimizing/scheduler_arm.cc b/compiler/optimizing/scheduler_arm.cc
index ea15790..d6eb6e3 100644
--- a/compiler/optimizing/scheduler_arm.cc
+++ b/compiler/optimizing/scheduler_arm.cc
@@ -20,6 +20,7 @@
#include "code_generator_utils.h"
#include "common_arm.h"
#include "mirror/array-inl.h"
+#include "mirror/string.h"
namespace art {
namespace arm {
diff --git a/compiler/optimizing/scheduler_arm64.cc b/compiler/optimizing/scheduler_arm64.cc
index f54d3f3..510619f 100644
--- a/compiler/optimizing/scheduler_arm64.cc
+++ b/compiler/optimizing/scheduler_arm64.cc
@@ -18,6 +18,7 @@
#include "code_generator_utils.h"
#include "mirror/array-inl.h"
+#include "mirror/string.h"
namespace art {
namespace arm64 {
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index e0ad649..ff193e9 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -355,6 +355,9 @@
UsageError("");
UsageError(" --debuggable: Produce code debuggable with Java debugger.");
UsageError("");
+ UsageError(" --avoid-storing-invocation: Avoid storing the invocation args in the key value");
+ UsageError(" store. Used to test determinism with different args.");
+ UsageError("");
UsageError(" --runtime-arg <argument>: used to specify various arguments for the runtime,");
UsageError(" such as initial heap size, maximum heap size, and verbose output.");
UsageError(" Use a separate --runtime-arg switch for each argument.");
@@ -611,6 +614,7 @@
dump_passes_(false),
dump_timing_(false),
dump_slow_timing_(kIsDebugBuild),
+ avoid_storing_invocation_(false),
swap_fd_(kInvalidFd),
app_image_fd_(kInvalidFd),
profile_file_fd_(kInvalidFd),
@@ -1133,14 +1137,16 @@
void InsertCompileOptions(int argc, char** argv) {
std::ostringstream oss;
- for (int i = 0; i < argc; ++i) {
- if (i > 0) {
- oss << ' ';
+ if (!avoid_storing_invocation_) {
+ for (int i = 0; i < argc; ++i) {
+ if (i > 0) {
+ oss << ' ';
+ }
+ oss << argv[i];
}
- oss << argv[i];
+ key_value_store_->Put(OatHeader::kDex2OatCmdLineKey, oss.str());
+ oss.str(""); // Reset.
}
- key_value_store_->Put(OatHeader::kDex2OatCmdLineKey, oss.str());
- oss.str(""); // Reset.
oss << kRuntimeISA;
key_value_store_->Put(OatHeader::kDex2OatHostKey, oss.str());
key_value_store_->Put(
@@ -1271,6 +1277,8 @@
dump_passes_ = true;
} else if (option == "--dump-stats") {
dump_stats_ = true;
+ } else if (option == "--avoid-storing-invocation") {
+ avoid_storing_invocation_ = true;
} else if (option.starts_with("--swap-file=")) {
swap_file_name_ = option.substr(strlen("--swap-file=")).data();
} else if (option.starts_with("--swap-fd=")) {
@@ -1308,7 +1316,7 @@
} else if (option.starts_with("--class-loader-context=")) {
class_loader_context_ = ClassLoaderContext::Create(
option.substr(strlen("--class-loader-context=")).data());
- if (class_loader_context_== nullptr) {
+ if (class_loader_context_ == nullptr) {
Usage("Option --class-loader-context has an incorrect format: %s", option.data());
}
} else if (option.starts_with("--dirty-image-objects=")) {
@@ -1576,20 +1584,12 @@
}
// Open dex files for class path.
+
if (class_loader_context_ == nullptr) {
- // TODO(calin): Temporary workaround while we transition to use
- // --class-loader-context instead of --runtime-arg -cp
- if (runtime_->GetClassPathString().empty()) {
- class_loader_context_ = std::unique_ptr<ClassLoaderContext>(
- new ClassLoaderContext());
- } else {
- std::string spec = runtime_->GetClassPathString() == OatFile::kSpecialSharedLibrary
- ? OatFile::kSpecialSharedLibrary
- : "PCL[" + runtime_->GetClassPathString() + "]";
- class_loader_context_ = ClassLoaderContext::Create(spec);
- }
+ // If no context was specified use the default one (which is an empty PathClassLoader).
+ class_loader_context_ = std::unique_ptr<ClassLoaderContext>(ClassLoaderContext::Default());
}
- CHECK(class_loader_context_ != nullptr);
+
DCHECK_EQ(oat_writers_.size(), 1u);
// Note: Ideally we would reject context where the source dex files are also
@@ -2899,6 +2899,7 @@
bool dump_passes_;
bool dump_timing_;
bool dump_slow_timing_;
+ bool avoid_storing_invocation_;
std::string swap_file_name_;
int swap_fd_;
size_t min_dex_files_for_swap_ = kDefaultMinDexFilesForSwap;
diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc
index 0c73ce7..eb8ced0 100644
--- a/runtime/base/unix_file/fd_file.cc
+++ b/runtime/base/unix_file/fd_file.cc
@@ -438,4 +438,30 @@
return true;
}
+int FdFile::Compare(FdFile* other) {
+ int64_t length = GetLength();
+ int64_t length2 = other->GetLength();
+ if (length != length2) {
+ return length < length2 ? -1 : 1;
+ }
+ static const size_t kBufferSize = 4096;
+ std::unique_ptr<uint8_t[]> buffer1(new uint8_t[kBufferSize]);
+ std::unique_ptr<uint8_t[]> buffer2(new uint8_t[kBufferSize]);
+ while (length > 0) {
+ size_t len = std::min(kBufferSize, static_cast<size_t>(length));
+ if (!ReadFully(&buffer1[0], len)) {
+ return -1;
+ }
+ if (!other->ReadFully(&buffer2[0], len)) {
+ return 1;
+ }
+ int result = memcmp(&buffer1[0], &buffer2[0], len);
+ if (result != 0) {
+ return result;
+ }
+ length -= len;
+ }
+ return 0;
+}
+
} // namespace unix_file
diff --git a/runtime/base/unix_file/fd_file.h b/runtime/base/unix_file/fd_file.h
index e07c3fd..91b08bc 100644
--- a/runtime/base/unix_file/fd_file.h
+++ b/runtime/base/unix_file/fd_file.h
@@ -145,6 +145,11 @@
// WARNING: Only use this when you know what you're doing!
void MarkUnchecked();
+ // Compare against another file. Returns 0 if the files are equivalent, otherwise returns -1 or 1
+ // depending on if the lenghts are different. If the lengths are the same, the function returns
+ // the difference of the first byte that differs.
+ int Compare(FdFile* other);
+
protected:
// If the guard state indicates checking (!=kNoCheck), go to the target state "target". Print the
// given warning if the current state is or exceeds warn_threshold.
diff --git a/runtime/base/unix_file/fd_file_test.cc b/runtime/base/unix_file/fd_file_test.cc
index 6aef348..8b1a115 100644
--- a/runtime/base/unix_file/fd_file_test.cc
+++ b/runtime/base/unix_file/fd_file_test.cc
@@ -220,4 +220,58 @@
EXPECT_FALSE(art::OS::FileExists(filename.c_str())) << filename;
}
+TEST_F(FdFileTest, Compare) {
+ std::vector<uint8_t> buffer;
+ constexpr int64_t length = 17 * art::KB;
+ for (size_t i = 0; i < length; ++i) {
+ buffer.push_back(static_cast<uint8_t>(i));
+ }
+
+ auto reset_compare = [&](art::ScratchFile& a, art::ScratchFile& b) {
+ a.GetFile()->ResetOffset();
+ b.GetFile()->ResetOffset();
+ return a.GetFile()->Compare(b.GetFile());
+ };
+
+ art::ScratchFile tmp;
+ EXPECT_TRUE(tmp.GetFile()->WriteFully(&buffer[0], length));
+ EXPECT_EQ(tmp.GetFile()->GetLength(), length);
+
+ art::ScratchFile tmp2;
+ EXPECT_TRUE(tmp2.GetFile()->WriteFully(&buffer[0], length));
+ EXPECT_EQ(tmp2.GetFile()->GetLength(), length);
+
+ // Basic equality check.
+ tmp.GetFile()->ResetOffset();
+ tmp2.GetFile()->ResetOffset();
+ // Files should be the same.
+ EXPECT_EQ(reset_compare(tmp, tmp2), 0);
+
+ // Change a byte near the start.
+ ++buffer[2];
+ art::ScratchFile tmp3;
+ EXPECT_TRUE(tmp3.GetFile()->WriteFully(&buffer[0], length));
+ --buffer[2];
+ EXPECT_NE(reset_compare(tmp, tmp3), 0);
+
+ // Change a byte near the middle.
+ ++buffer[length / 2];
+ art::ScratchFile tmp4;
+ EXPECT_TRUE(tmp4.GetFile()->WriteFully(&buffer[0], length));
+ --buffer[length / 2];
+ EXPECT_NE(reset_compare(tmp, tmp4), 0);
+
+ // Change a byte near the end.
+ ++buffer[length - 5];
+ art::ScratchFile tmp5;
+ EXPECT_TRUE(tmp5.GetFile()->WriteFully(&buffer[0], length));
+ --buffer[length - 5];
+ EXPECT_NE(reset_compare(tmp, tmp5), 0);
+
+ // Reference check
+ art::ScratchFile tmp6;
+ EXPECT_TRUE(tmp6.GetFile()->WriteFully(&buffer[0], length));
+ EXPECT_EQ(reset_compare(tmp, tmp6), 0);
+}
+
} // namespace unix_file
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index b50aec0..e7051b3 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -68,6 +68,10 @@
}
}
+std::unique_ptr<ClassLoaderContext> ClassLoaderContext::Default() {
+ return Create("");
+}
+
std::unique_ptr<ClassLoaderContext> ClassLoaderContext::Create(const std::string& spec) {
std::unique_ptr<ClassLoaderContext> result(new ClassLoaderContext());
if (result->Parse(spec)) {
diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h
index 85299d4..9afa880 100644
--- a/runtime/class_loader_context.h
+++ b/runtime/class_loader_context.h
@@ -34,9 +34,6 @@
// Utility class which holds the class loader context used during compilation/verification.
class ClassLoaderContext {
public:
- // Creates an empty context (with no class loaders).
- ClassLoaderContext();
-
~ClassLoaderContext();
// Opens requested class path files and appends them to ClassLoaderInfo::opened_dex_files.
@@ -126,6 +123,10 @@
static std::unique_ptr<ClassLoaderContext> CreateContextForClassLoader(jobject class_loader,
jobjectArray dex_elements);
+ // Returns the default class loader context to be used when none is specified.
+ // This will return a context with a single and empty PathClassLoader.
+ static std::unique_ptr<ClassLoaderContext> Default();
+
private:
enum ClassLoaderType {
kInvalidClassLoader = 0,
@@ -151,6 +152,9 @@
explicit ClassLoaderInfo(ClassLoaderType cl_type) : type(cl_type) {}
};
+ // Creates an empty context (with no class loaders).
+ ClassLoaderContext();
+
// Constructs an empty context.
// `owns_the_dex_files` specifies whether or not the context will own the opened dex files
// present in the class loader chain. If `owns_the_dex_files` is true then OpenDexFiles cannot
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 9d672b1..9b24885 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -2431,24 +2431,31 @@
// Non-immune non-moving space. Use the mark bitmap.
accounting::ContinuousSpaceBitmap* mark_bitmap =
heap_mark_bitmap_->GetContinuousSpaceBitmap(from_ref);
- accounting::LargeObjectBitmap* los_bitmap =
- heap_mark_bitmap_->GetLargeObjectBitmap(from_ref);
- CHECK(los_bitmap != nullptr) << "LOS bitmap covers the entire address range";
bool is_los = mark_bitmap == nullptr;
if (!is_los && mark_bitmap->Test(from_ref)) {
// Already marked.
to_ref = from_ref;
- } else if (is_los && los_bitmap->Test(from_ref)) {
- // Already marked in LOS.
- to_ref = from_ref;
} else {
- // Not marked.
- if (IsOnAllocStack(from_ref)) {
- // If on the allocation stack, it's considered marked.
+ accounting::LargeObjectBitmap* los_bitmap =
+ heap_mark_bitmap_->GetLargeObjectBitmap(from_ref);
+ // We may not have a large object space for dex2oat, don't assume it exists.
+ if (los_bitmap == nullptr) {
+ CHECK(heap_->GetLargeObjectsSpace() == nullptr)
+ << "LOS bitmap covers the entire address range " << from_ref
+ << " " << heap_->DumpSpaces();
+ }
+ if (los_bitmap != nullptr && is_los && los_bitmap->Test(from_ref)) {
+ // Already marked in LOS.
to_ref = from_ref;
} else {
// Not marked.
- to_ref = nullptr;
+ if (IsOnAllocStack(from_ref)) {
+ // If on the allocation stack, it's considered marked.
+ to_ref = from_ref;
+ } else {
+ // Not marked.
+ to_ref = nullptr;
+ }
}
}
}
diff --git a/runtime/mirror/accessible_object.h b/runtime/mirror/accessible_object.h
index a217193..d489f14 100644
--- a/runtime/mirror/accessible_object.h
+++ b/runtime/mirror/accessible_object.h
@@ -17,11 +17,8 @@
#ifndef ART_RUNTIME_MIRROR_ACCESSIBLE_OBJECT_H_
#define ART_RUNTIME_MIRROR_ACCESSIBLE_OBJECT_H_
-#include "class.h"
-#include "gc_root.h"
#include "object.h"
#include "read_barrier_option.h"
-#include "thread.h"
namespace art {
@@ -34,12 +31,6 @@
return OFFSET_OF_OBJECT_MEMBER(AccessibleObject, flag_);
}
- template<bool kTransactionActive>
- void SetAccessible(bool value) REQUIRES_SHARED(Locks::mutator_lock_) {
- UNUSED(padding_);
- return SetFieldBoolean<kTransactionActive>(FlagOffset(), value ? 1u : 0u);
- }
-
bool IsAccessible() REQUIRES_SHARED(Locks::mutator_lock_) {
return GetFieldBoolean(FlagOffset());
}
@@ -47,7 +38,7 @@
private:
uint8_t flag_;
// Padding required for correct alignment of subclasses like Executable, Field, etc.
- uint8_t padding_[1];
+ uint8_t padding_[1] ATTRIBUTE_UNUSED;
DISALLOW_IMPLICIT_CONSTRUCTORS(AccessibleObject);
};
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index 63142d5..2281245 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -27,8 +27,7 @@
#include "class.h"
#include "gc/heap-inl.h"
#include "obj_ptr-inl.h"
-#include "object-inl.h"
-#include "thread.h"
+#include "thread-current-inl.h"
namespace art {
namespace mirror {
diff --git a/runtime/mirror/field-inl.h b/runtime/mirror/field-inl.h
index d33df5c..ad48202 100644
--- a/runtime/mirror/field-inl.h
+++ b/runtime/mirror/field-inl.h
@@ -20,7 +20,8 @@
#include "field.h"
#include "art_field-inl.h"
-#include "mirror/dex_cache-inl.h"
+#include "class-inl.h"
+#include "dex_cache-inl.h"
namespace art {
@@ -87,6 +88,10 @@
SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(Field, type_), type);
}
+inline Primitive::Type Field::GetTypeAsPrimitiveType() {
+ return GetType()->GetPrimitiveType();
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/field.h b/runtime/mirror/field.h
index 40186a6..6845575 100644
--- a/runtime/mirror/field.h
+++ b/runtime/mirror/field.h
@@ -20,8 +20,10 @@
#include "accessible_object.h"
#include "base/enums.h"
#include "gc_root.h"
+#include "modifiers.h"
#include "obj_ptr.h"
#include "object.h"
+#include "primitive.h"
#include "read_barrier_option.h"
namespace art {
@@ -69,10 +71,7 @@
return (GetAccessFlags() & kAccVolatile) != 0;
}
- ALWAYS_INLINE Primitive::Type GetTypeAsPrimitiveType()
- REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetType()->GetPrimitiveType();
- }
+ ALWAYS_INLINE Primitive::Type GetTypeAsPrimitiveType() REQUIRES_SHARED(Locks::mutator_lock_);
mirror::Class* GetType() REQUIRES_SHARED(Locks::mutator_lock_) {
return GetFieldObject<mirror::Class>(OFFSET_OF_OBJECT_MEMBER(Field, type_));
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 086925b..4f8952d 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -393,14 +393,6 @@
}
template<VerifyObjectFlags kVerifyFlags, bool kIsVolatile>
-inline uint8_t Object::GetFieldBoolean(MemberOffset field_offset) {
- if (kVerifyFlags & kVerifyThis) {
- VerifyObject(this);
- }
- return GetField<uint8_t, kIsVolatile>(field_offset);
-}
-
-template<VerifyObjectFlags kVerifyFlags, bool kIsVolatile>
inline int8_t Object::GetFieldByte(MemberOffset field_offset) {
if (kVerifyFlags & kVerifyThis) {
VerifyObject(this);
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index 886780f..aedcd66 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -380,7 +380,12 @@
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
ALWAYS_INLINE uint8_t GetFieldBoolean(MemberOffset field_offset)
- REQUIRES_SHARED(Locks::mutator_lock_);
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (kVerifyFlags & kVerifyThis) {
+ VerifyObject(this);
+ }
+ return GetField<uint8_t, kIsVolatile>(field_offset);
+ }
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
ALWAYS_INLINE int8_t GetFieldByte(MemberOffset field_offset)
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index 9f59a1f..2e4dd8a 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -27,7 +27,7 @@
#include "dex_file_annotations.h"
#include "jni_internal.h"
#include "mirror/class-inl.h"
-#include "mirror/field.h"
+#include "mirror/field-inl.h"
#include "native_util.h"
#include "reflection-inl.h"
#include "scoped_fast_native_object_access-inl.h"
diff --git a/runtime/thread.h b/runtime/thread.h
index 776096a..7540fd2 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -1705,8 +1705,13 @@
class SCOPED_CAPABILITY ScopedAssertNoThreadSuspension {
public:
- ALWAYS_INLINE explicit ScopedAssertNoThreadSuspension(const char* cause)
- ACQUIRE(Roles::uninterruptible_) {
+ ALWAYS_INLINE ScopedAssertNoThreadSuspension(const char* cause,
+ bool enabled = true)
+ ACQUIRE(Roles::uninterruptible_)
+ : enabled_(enabled) {
+ if (!enabled_) {
+ return;
+ }
if (kIsDebugBuild) {
self_ = Thread::Current();
old_cause_ = self_->StartAssertNoThreadSuspension(cause);
@@ -1715,6 +1720,9 @@
}
}
ALWAYS_INLINE ~ScopedAssertNoThreadSuspension() RELEASE(Roles::uninterruptible_) {
+ if (!enabled_) {
+ return;
+ }
if (kIsDebugBuild) {
self_->EndAssertNoThreadSuspension(old_cause_);
} else {
@@ -1724,6 +1732,7 @@
private:
Thread* self_;
+ const bool enabled_;
const char* old_cause_;
};
diff --git a/test/1338-gc-no-los/expected.txt b/test/1338-gc-no-los/expected.txt
new file mode 100644
index 0000000..36bec43
--- /dev/null
+++ b/test/1338-gc-no-los/expected.txt
@@ -0,0 +1 @@
+131072 200 true
diff --git a/test/1338-gc-no-los/info.txt b/test/1338-gc-no-los/info.txt
new file mode 100644
index 0000000..2e27357
--- /dev/null
+++ b/test/1338-gc-no-los/info.txt
@@ -0,0 +1 @@
+Test that the GC works with no large object space. Regression test for b/64393515.
\ No newline at end of file
diff --git a/test/1338-gc-no-los/run b/test/1338-gc-no-los/run
new file mode 100755
index 0000000..f3c43fb
--- /dev/null
+++ b/test/1338-gc-no-los/run
@@ -0,0 +1,16 @@
+#!/bin/bash
+#
+# Copyright 2017 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.
+./default-run "$@" --runtime-option -XX:LargeObjectSpace=disabled
diff --git a/test/1338-gc-no-los/src-art/Main.java b/test/1338-gc-no-los/src-art/Main.java
new file mode 100644
index 0000000..662fa7e
--- /dev/null
+++ b/test/1338-gc-no-los/src-art/Main.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+import dalvik.system.VMRuntime;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Method;
+
+public class Main {
+ public static void main(String[] args) {
+ try {
+ // Allocate a large object.
+ byte[] arr = new byte[128 * 1024];
+ // Allocate a non movable object.
+ byte[] arr2 = (byte[])VMRuntime.getRuntime().newNonMovableArray(Byte.TYPE, 200);
+ // Put the array in a weak reference so that IsMarked is called by the GC.
+ WeakReference weakRef = new WeakReference(arr2);
+ // Do a GC.
+ Runtime.getRuntime().gc();
+ arr[0] = 1;
+ arr2[0] = 1;
+ System.out.println(arr.length + " " + arr2.length + " " + (weakRef.get() != null));
+ } catch (Exception e) {
+ System.out.println(e);
+ }
+ }
+}
diff --git a/test/knownfailures.json b/test/knownfailures.json
index f89fd0a..a8191bb 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -713,7 +713,9 @@
"--no-dex2oat do not create"]
},
{
- "tests": "961-default-iface-resolution-gen",
+ "tests": ["961-default-iface-resolution-gen",
+ "964-default-iface-init-gen",
+ "968-default-partial-compile-gen"],
"env_vars": {"SANITIZE_HOST": "address"},
"description": ["Test hits dex2oat watchdog timeout (60sec) on art-asan"]
},
diff --git a/test/ti-agent/trace_helper.cc b/test/ti-agent/trace_helper.cc
index 7a9d1e0..1f8ceff 100644
--- a/test/ti-agent/trace_helper.cc
+++ b/test/ti-agent/trace_helper.cc
@@ -388,10 +388,12 @@
data->single_step = single_step != nullptr ? env->FromReflectedMethod(single_step) : nullptr;
data->in_callback = false;
- void* old_data = nullptr;
- if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(&old_data))) {
+ TraceData* old_data = nullptr;
+ if (JvmtiErrorToException(env, jvmti_env,
+ jvmti_env->GetEnvironmentLocalStorage(
+ reinterpret_cast<void**>(&old_data)))) {
return;
- } else if (old_data != nullptr) {
+ } else if (old_data != nullptr && old_data->test_klass != nullptr) {
ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
env->ThrowNew(rt_exception.get(), "Environment already has local storage set!");
return;
@@ -455,6 +457,21 @@
extern "C" JNIEXPORT void JNICALL Java_art_Trace_disableTracing(
JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thr) {
+ TraceData* data = nullptr;
+ if (JvmtiErrorToException(
+ env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ // If data is null then we haven't ever enabled tracing so we don't need to do anything.
+ if (data == nullptr || data->test_klass == nullptr) {
+ return;
+ }
+ env->DeleteGlobalRef(data->test_klass);
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ // Clear test_klass so we know this isn't being used
+ data->test_klass = nullptr;
if (JvmtiErrorToException(env, jvmti_env,
jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
JVMTI_EVENT_FIELD_ACCESS,
diff --git a/tools/art b/tools/art
index 18c5c84..dea7ebd 100644
--- a/tools/art
+++ b/tools/art
@@ -119,6 +119,71 @@
env "$@"
}
+# Parse a colon-separated list into an array (e.g. "foo.dex:bar.dex" -> (foo.dex bar.dex))
+PARSE_CLASSPATH_RESULT=() # Return value will be here due to shell limitations.
+parse_classpath() {
+ local cp="$1"
+ local oldifs=$IFS
+
+ local cp_array
+ cp_array=()
+
+ IFS=":"
+ for part in $cp; do
+ cp_array+=("$part")
+ done
+ IFS=$oldifs
+
+ PARSE_CLASSPATH_RESULT=("${cp_array[@]}")
+}
+
+# Sets 'PARSE_CLASSPATH_RESULT' to an array of class path dex files.
+# e.g. (-cp foo/classes.dex:bar/classes.dex) -> (foo/classes.dex bar/classes.dex)
+find_cp_in_args() {
+ local found="false"
+ local index=0
+ local what
+
+ while [[ $# -gt 0 ]]; do
+ case "$1" in
+ -cp|-classpath)
+ parse_classpath "$2"
+ # Sets 'PARSE_CLASSPATH_RESULT' to an array of class path dex files.
+ # Subsequent parses will overwrite the preceding.
+ shift
+ ;;
+ esac
+ shift
+ done
+}
+
+# Delete the 'oat' directories relative to the classpath's dex files.
+# e.g. (foo/classes.dex bar/classes.dex) would delete (foo/oat bar/oat) directories.
+cleanup_oat_directory() {
+ local classpath
+ classpath=("$@")
+
+ local dirpath
+
+ for path in "${classpath[@]}"; do
+ dirpath="$(dirname "$path")"
+ [[ -d "$dirpath" ]] && verbose_run rm -rf "$dirpath/oat"
+ done
+}
+
+# Parse -cp <CP>, -classpath <CP>, and $CLASSPATH to find the dex files.
+# Each dex file's directory will have an 'oat' file directory, delete it.
+# Input: Command line arguments to the art script.
+# e.g. -cp foo/classes.dex:bar/classes.dex would delete (foo/oat bar/oat) directories.
+cleanup_oat_directory_for_classpath() {
+ # First try: Use $CLASSPATH environment variable.
+ parse_classpath "$CLASSPATH"
+ # Second try: Look for latest -cp or -classpath arg which will take precedence.
+ find_cp_in_args "$@"
+
+ cleanup_oat_directory "${PARSE_CLASSPATH_RESULT[@]}"
+}
+
# Attempt to find $ANDROID_ROOT/framework/<isa>/core.art' without knowing what <isa> is.
function check_if_boot_image_file_exists() {
local image_location_dir="$1"
@@ -151,9 +216,15 @@
echo "$image_location"
}
+# Runs dalvikvm, returns its exit code.
+# (Oat directories are cleaned up in between runs)
function run_art() {
local image_location="$(detect_boot_image_location)"
+ local ret
+ # First cleanup any left-over 'oat' files from the last time dalvikvm was run.
+ cleanup_oat_directory_for_classpath "$@"
+ # Run dalvikvm.
verbose_run ANDROID_DATA=$ANDROID_DATA \
ANDROID_ROOT=$ANDROID_ROOT \
LD_LIBRARY_PATH=$LD_LIBRARY_PATH \
@@ -164,6 +235,13 @@
-Xnorelocate \
-Ximage:"$image_location" \
"$@"
+ ret=$?
+
+ # Avoid polluting disk with 'oat' files after dalvikvm has finished.
+ cleanup_oat_directory_for_classpath "$@"
+
+ # Forward exit code of dalvikvm.
+ return $ret
}
while [[ "$1" = "-"* ]]; do
diff --git a/tools/libcore_gcstress_debug_failures.txt b/tools/libcore_gcstress_debug_failures.txt
index 66bc252..d4a0423 100644
--- a/tools/libcore_gcstress_debug_failures.txt
+++ b/tools/libcore_gcstress_debug_failures.txt
@@ -11,6 +11,7 @@
names: ["libcore.icu.TransliteratorTest#testAll",
"libcore.icu.RelativeDateTimeFormatterTest#test_bug25821045",
"libcore.java.lang.ref.ReferenceQueueTest#testRemoveWithDelayedResultAndTimeout",
+ "libcore.java.lang.ref.ReferenceQueueTest#testRemoveWithDelayedResultAndNoTimeout",
"libcore.java.util.TimeZoneTest#testSetDefaultDeadlock",
"org.apache.harmony.tests.java.util.TimerTest#testThrowingTaskKillsTimerThread"]
}