Merge "ART: Arm32 optimizing compiler backend should honor sdiv"
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 257406a..1d0aad5 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -180,7 +180,6 @@
callbacks_.reset(new QuickCompilerCallbacks(verification_results_.get(),
method_inliner_map_.get(),
CompilerCallbacks::CallbackMode::kCompileApp));
- options->push_back(std::make_pair("compilercallbacks", callbacks_.get()));
}
void CommonCompilerTest::TearDown() {
diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h
index 9cffbc8..d7b210d 100644
--- a/compiler/common_compiler_test.h
+++ b/compiler/common_compiler_test.h
@@ -78,7 +78,6 @@
std::unique_ptr<CompilerOptions> compiler_options_;
std::unique_ptr<VerificationResults> verification_results_;
std::unique_ptr<DexFileToMethodInlinerMap> method_inliner_map_;
- std::unique_ptr<CompilerCallbacks> callbacks_;
std::unique_ptr<CompilerDriver> compiler_driver_;
std::unique_ptr<CumulativeLogger> timer_;
std::unique_ptr<const InstructionSetFeatures> instruction_set_features_;
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index d59ad6c..afd39e8 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -85,9 +85,6 @@
compiler_options_.reset(new CompilerOptions);
verification_results_.reset(new VerificationResults(compiler_options_.get()));
method_inliner_map_.reset(new DexFileToMethodInlinerMap);
- callbacks_.reset(new QuickCompilerCallbacks(verification_results_.get(),
- method_inliner_map_.get(),
- CompilerCallbacks::CallbackMode::kCompileApp));
timer_.reset(new CumulativeLogger("Compilation times"));
compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
verification_results_.get(),
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 5ce73ba..b2f9c65 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -583,8 +583,13 @@
if (method != nullptr) {
return method;
}
- return delegate_->Compile(code_item, access_flags, invoke_type, class_def_idx, method_idx,
- class_loader, dex_file);
+ method = delegate_->Compile(code_item, access_flags, invoke_type, class_def_idx, method_idx,
+ class_loader, dex_file);
+
+ if (method != nullptr) {
+ compilation_stats_.RecordStat(MethodCompilationStat::kCompiledQuick);
+ }
+ return method;
}
Compiler* CreateOptimizingCompiler(CompilerDriver* driver) {
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index 22ec2a5..b97a667 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -28,6 +28,7 @@
kAttemptCompilation = 0,
kCompiledBaseline,
kCompiledOptimized,
+ kCompiledQuick,
kInlinedInvoke,
kNotCompiledUnsupportedIsa,
kNotCompiledPathological,
@@ -65,16 +66,22 @@
compile_stats_[kCompiledBaseline] * 100 / compile_stats_[kAttemptCompilation];
size_t optimized_percent =
compile_stats_[kCompiledOptimized] * 100 / compile_stats_[kAttemptCompilation];
+ size_t quick_percent =
+ compile_stats_[kCompiledQuick] * 100 / compile_stats_[kAttemptCompilation];
std::ostringstream oss;
- oss << "Attempted compilation of " << compile_stats_[kAttemptCompilation] << " methods: "
- << unoptimized_percent << "% (" << compile_stats_[kCompiledBaseline] << ") unoptimized, "
- << optimized_percent << "% (" << compile_stats_[kCompiledOptimized] << ") optimized.";
+ oss << "Attempted compilation of " << compile_stats_[kAttemptCompilation] << " methods: ";
+
+ oss << unoptimized_percent << "% (" << compile_stats_[kCompiledBaseline] << ") unoptimized, ";
+ oss << optimized_percent << "% (" << compile_stats_[kCompiledOptimized] << ") optimized, ";
+ oss << quick_percent << "% (" << compile_stats_[kCompiledQuick] << ") quick.";
+
+ LOG(INFO) << oss.str();
+
for (int i = 0; i < kLastStat; i++) {
if (compile_stats_[i] != 0) {
- oss << "\n" << PrintMethodCompilationStat(i) << ": " << compile_stats_[i];
+ VLOG(compiler) << PrintMethodCompilationStat(i) << ": " << compile_stats_[i];
}
}
- LOG(INFO) << oss.str();
}
}
@@ -84,6 +91,7 @@
case kAttemptCompilation : return "kAttemptCompilation";
case kCompiledBaseline : return "kCompiledBaseline";
case kCompiledOptimized : return "kCompiledOptimized";
+ case kCompiledQuick : return "kCompiledQuick";
case kInlinedInvoke : return "kInlinedInvoke";
case kNotCompiledUnsupportedIsa : return "kNotCompiledUnsupportedIsa";
case kNotCompiledPathological : return "kNotCompiledPathological";
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index cecc210..cf38bd3 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -213,7 +213,7 @@
LiveInterval* interval =
LiveInterval::MakeTempInterval(allocator_, Primitive::kPrimInt);
temp_intervals_.Add(interval);
- interval->AddRange(position, position + 1);
+ interval->AddTempUse(instruction, i);
unhandled_core_intervals_.Add(interval);
break;
}
@@ -222,7 +222,7 @@
LiveInterval* interval =
LiveInterval::MakeTempInterval(allocator_, Primitive::kPrimDouble);
temp_intervals_.Add(interval);
- interval->AddRange(position, position + 1);
+ interval->AddTempUse(instruction, i);
if (codegen_->NeedsTwoRegisters(Primitive::kPrimDouble)) {
interval->AddHighInterval(true);
LiveInterval* high = interval->GetHighInterval();
@@ -851,6 +851,23 @@
return false;
}
+bool RegisterAllocator::PotentiallyRemoveOtherHalf(LiveInterval* interval,
+ GrowableArray<LiveInterval*>* intervals,
+ size_t index) {
+ if (interval->IsLowInterval()) {
+ DCHECK_EQ(intervals->Get(index), interval->GetHighInterval());
+ intervals->DeleteAt(index);
+ return true;
+ } else if (interval->IsHighInterval()) {
+ DCHECK_GT(index, 0u);
+ DCHECK_EQ(intervals->Get(index - 1), interval->GetLowInterval());
+ intervals->DeleteAt(index - 1);
+ return true;
+ } else {
+ return false;
+ }
+}
+
// Find the register that is used the last, and spill the interval
// that holds it. If the first use of `current` is after that register
// we spill `current` instead.
@@ -974,33 +991,17 @@
if (active->GetRegister() == reg) {
DCHECK(!active->IsFixed());
LiveInterval* split = Split(active, current->GetStart());
- active_.DeleteAt(i);
if (split != active) {
handled_.Add(active);
}
+ active_.DeleteAt(i);
+ PotentiallyRemoveOtherHalf(active, &active_, i);
AddSorted(unhandled_, split);
-
- if (active->IsLowInterval() || active->IsHighInterval()) {
- LiveInterval* other_half = active->IsLowInterval()
- ? active->GetHighInterval()
- : active->GetLowInterval();
- // We also need to remove the other half from the list of actives.
- bool found = false;
- for (size_t j = 0; j < active_.Size(); ++j) {
- if (active_.Get(j) == other_half) {
- found = true;
- active_.DeleteAt(j);
- handled_.Add(other_half);
- break;
- }
- }
- DCHECK(found);
- }
break;
}
}
- for (size_t i = 0, e = inactive_.Size(); i < e; ++i) {
+ for (size_t i = 0; i < inactive_.Size(); ++i) {
LiveInterval* inactive = inactive_.Get(i);
if (inactive->GetRegister() == reg) {
if (!current->IsSplit() && !inactive->IsFixed()) {
@@ -1024,29 +1025,14 @@
// If it's inactive, it must start before the current interval.
DCHECK_NE(split, inactive);
inactive_.DeleteAt(i);
+ if (PotentiallyRemoveOtherHalf(inactive, &inactive_, i) && inactive->IsHighInterval()) {
+ // We have removed an entry prior to `inactive`. So we need to decrement.
+ --i;
+ }
+ // Decrement because we have removed `inactive` from the list.
--i;
- --e;
handled_.Add(inactive);
AddSorted(unhandled_, split);
-
- if (inactive->IsLowInterval() || inactive->IsHighInterval()) {
- LiveInterval* other_half = inactive->IsLowInterval()
- ? inactive->GetHighInterval()
- : inactive->GetLowInterval();
-
- // We also need to remove the other half from the list of inactives.
- bool found = false;
- for (size_t j = 0; j < inactive_.Size(); ++j) {
- if (inactive_.Get(j) == other_half) {
- found = true;
- inactive_.DeleteAt(j);
- --e;
- handled_.Add(other_half);
- break;
- }
- }
- DCHECK(found);
- }
}
}
}
@@ -1695,8 +1681,6 @@
}
// Assign temp locations.
- HInstruction* current = nullptr;
- size_t temp_index = 0;
for (size_t i = 0; i < temp_intervals_.Size(); ++i) {
LiveInterval* temp = temp_intervals_.Get(i);
if (temp->IsHighInterval()) {
@@ -1704,25 +1688,20 @@
continue;
}
HInstruction* at = liveness_.GetTempUser(temp);
- if (at != current) {
- temp_index = 0;
- current = at;
- }
+ size_t temp_index = liveness_.GetTempIndex(temp);
LocationSummary* locations = at->GetLocations();
switch (temp->GetType()) {
case Primitive::kPrimInt:
- locations->SetTempAt(
- temp_index++, Location::RegisterLocation(temp->GetRegister()));
+ locations->SetTempAt(temp_index, Location::RegisterLocation(temp->GetRegister()));
break;
case Primitive::kPrimDouble:
if (codegen_->NeedsTwoRegisters(Primitive::kPrimDouble)) {
Location location = Location::FpuRegisterPairLocation(
temp->GetRegister(), temp->GetHighInterval()->GetRegister());
- locations->SetTempAt(temp_index++, location);
+ locations->SetTempAt(temp_index, location);
} else {
- locations->SetTempAt(
- temp_index++, Location::FpuRegisterLocation(temp->GetRegister()));
+ locations->SetTempAt(temp_index, Location::FpuRegisterLocation(temp->GetRegister()));
}
break;
diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h
index fcc6112..717be75 100644
--- a/compiler/optimizing/register_allocator.h
+++ b/compiler/optimizing/register_allocator.h
@@ -144,6 +144,13 @@
size_t first_register_use,
size_t* next_use);
+ // If `interval` has another half, remove it from the list of `intervals`.
+ // `index` holds the index at which `interval` is in `intervals`.
+ // Returns whether there is another half.
+ bool PotentiallyRemoveOtherHalf(LiveInterval* interval,
+ GrowableArray<LiveInterval*>* intervals,
+ size_t index);
+
ArenaAllocator* const allocator_;
CodeGenerator* const codegen_;
const SsaLivenessAnalysis& liveness_;
diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc
index 56ccd71..0f3973e 100644
--- a/compiler/optimizing/ssa_liveness_analysis.cc
+++ b/compiler/optimizing/ssa_liveness_analysis.cc
@@ -318,6 +318,8 @@
int LiveInterval::FindFirstRegisterHint(size_t* free_until) const {
DCHECK(!IsHighInterval());
+ if (IsTemp()) return kNoRegister;
+
if (GetParent() == this && defined_by_ != nullptr) {
// This is the first interval for the instruction. Try to find
// a register based on its definition.
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index b57029d..bc78dc2 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -180,6 +180,15 @@
// This interval is the result of a split.
bool IsSplit() const { return parent_ != this; }
+ void AddTempUse(HInstruction* instruction, size_t temp_index) {
+ DCHECK(IsTemp());
+ DCHECK(first_use_ == nullptr) << "A temporary can only have one user";
+ size_t position = instruction->GetLifetimePosition();
+ first_use_ = new (allocator_) UsePosition(
+ instruction, temp_index, /* is_environment */ false, position, first_use_);
+ AddRange(position, position + 1);
+ }
+
void AddUse(HInstruction* instruction, size_t input_index, bool is_environment) {
// Set the use within the instruction.
size_t position = instruction->GetLifetimePosition() + 1;
@@ -856,7 +865,15 @@
HInstruction* GetTempUser(LiveInterval* temp) const {
// A temporary shares the same lifetime start as the instruction that requires it.
DCHECK(temp->IsTemp());
- return GetInstructionFromPosition(temp->GetStart() / 2);
+ HInstruction* user = GetInstructionFromPosition(temp->GetStart() / 2);
+ DCHECK_EQ(user, temp->GetFirstUse()->GetUser());
+ return user;
+ }
+
+ size_t GetTempIndex(LiveInterval* temp) const {
+ // We use the input index to store the index of the temporary in the user's temporary list.
+ DCHECK(temp->IsTemp());
+ return temp->GetFirstUse()->GetInputIndex();
}
size_t GetMaxLifetimePosition() const {
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 4104509..d400010 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -220,7 +220,6 @@
std::string min_heap_string(StringPrintf("-Xms%zdm", gc::Heap::kDefaultInitialSize / MB));
std::string max_heap_string(StringPrintf("-Xmx%zdm", gc::Heap::kDefaultMaximumSize / MB));
- callbacks_.reset(new NoopCompilerCallbacks());
RuntimeOptions options;
std::string boot_class_path_string = "-Xbootclasspath:" + GetLibCoreDexFileName();
@@ -228,9 +227,16 @@
options.push_back(std::make_pair("-Xcheck:jni", nullptr));
options.push_back(std::make_pair(min_heap_string, nullptr));
options.push_back(std::make_pair(max_heap_string, nullptr));
- options.push_back(std::make_pair("compilercallbacks", callbacks_.get()));
+
+ callbacks_.reset(new NoopCompilerCallbacks());
+
SetUpRuntimeOptions(&options);
+ // Install compiler-callbacks if SetupRuntimeOptions hasn't deleted them.
+ if (callbacks_.get() != nullptr) {
+ options.push_back(std::make_pair("compilercallbacks", callbacks_.get()));
+ }
+
PreRuntimeCreate();
if (!Runtime::Create(options, false)) {
LOG(FATAL) << "Failed to create runtime";
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index a29487f..5fbc2ee 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -140,10 +140,11 @@
// Get the first dex file from a PathClassLoader. Will abort if it is null.
const DexFile* GetFirstDexFile(jobject jclass_loader);
+ std::unique_ptr<CompilerCallbacks> callbacks_;
+
private:
static std::string GetCoreFileLocation(const char* suffix);
- std::unique_ptr<CompilerCallbacks> callbacks_;
std::vector<std::unique_ptr<const DexFile>> loaded_dex_files_;
};
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 5ea0187..a767cf0 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -1451,22 +1451,31 @@
* Circularly shifts registers so that arguments come last. Reverts
* slots to dex style argument placement.
*/
-static uint16_t DemangleSlot(uint16_t slot, mirror::ArtMethod* m)
+static uint16_t DemangleSlot(uint16_t slot, mirror::ArtMethod* m, JDWP::JdwpError* error)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
const DexFile::CodeItem* code_item = m->GetCodeItem();
if (code_item == nullptr) {
// We should not get here for a method without code (native, proxy or abstract). Log it and
// return the slot as is since all registers are arguments.
LOG(WARNING) << "Trying to demangle slot for method without code " << PrettyMethod(m);
- return slot;
- }
- uint16_t ins_size = code_item->ins_size_;
- uint16_t locals_size = code_item->registers_size_ - ins_size;
- if (slot < ins_size) {
- return slot + locals_size;
+ uint16_t vreg_count = mirror::ArtMethod::NumArgRegisters(m->GetShorty());
+ if (slot < vreg_count) {
+ *error = JDWP::ERR_NONE;
+ return slot;
+ }
} else {
- return slot - ins_size;
+ if (slot < code_item->registers_size_) {
+ uint16_t ins_size = code_item->ins_size_;
+ uint16_t locals_size = code_item->registers_size_ - ins_size;
+ *error = JDWP::ERR_NONE;
+ return (slot < ins_size) ? slot + locals_size : slot - ins_size;
+ }
}
+
+ // Slot is invalid in the method.
+ LOG(ERROR) << "Invalid local slot " << slot << " for method " << PrettyMethod(m);
+ *error = JDWP::ERR_INVALID_SLOT;
+ return DexFile::kDexNoIndex16;
}
JDWP::JdwpError Dbg::OutputDeclaredFields(JDWP::RefTypeId class_id, bool with_generic, JDWP::ExpandBuf* pReply) {
@@ -2427,6 +2436,9 @@
if (error != JDWP::ERR_NONE) {
return error;
}
+ if (!IsSuspendedForDebugger(soa, thread)) {
+ return JDWP::ERR_THREAD_NOT_SUSPENDED;
+ }
}
// Find the frame with the given frame_id.
std::unique_ptr<Context> context(Context::Create());
@@ -2455,73 +2467,81 @@
return JDWP::ERR_NONE;
}
+constexpr JDWP::JdwpError kStackFrameLocalAccessError = JDWP::ERR_ABSENT_INFORMATION;
+
+static std::string GetStackContextAsString(const StackVisitor& visitor)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return StringPrintf(" at DEX pc 0x%08x in method %s", visitor.GetDexPc(false),
+ PrettyMethod(visitor.GetMethod()).c_str());
+}
+
+static JDWP::JdwpError FailGetLocalValue(const StackVisitor& visitor, uint16_t vreg,
+ JDWP::JdwpTag tag)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ LOG(ERROR) << "Failed to read " << tag << " local from register v" << vreg
+ << GetStackContextAsString(visitor);
+ return kStackFrameLocalAccessError;
+}
+
JDWP::JdwpError Dbg::GetLocalValue(const StackVisitor& visitor, ScopedObjectAccessUnchecked& soa,
int slot, JDWP::JdwpTag tag, uint8_t* buf, size_t width) {
mirror::ArtMethod* m = visitor.GetMethod();
- uint16_t reg = DemangleSlot(slot, m);
+ JDWP::JdwpError error = JDWP::ERR_NONE;
+ uint16_t vreg = DemangleSlot(slot, m, &error);
+ if (error != JDWP::ERR_NONE) {
+ return error;
+ }
// TODO: check that the tag is compatible with the actual type of the slot!
- // TODO: check slot is valid for this method or return INVALID_SLOT error.
- constexpr JDWP::JdwpError kFailureErrorCode = JDWP::ERR_ABSENT_INFORMATION;
switch (tag) {
case JDWP::JT_BOOLEAN: {
CHECK_EQ(width, 1U);
uint32_t intVal;
- if (visitor.GetVReg(m, reg, kIntVReg, &intVal)) {
- VLOG(jdwp) << "get boolean local " << reg << " = " << intVal;
- JDWP::Set1(buf + 1, intVal != 0);
- } else {
- VLOG(jdwp) << "failed to get boolean local " << reg;
- return kFailureErrorCode;
+ if (!visitor.GetVReg(m, vreg, kIntVReg, &intVal)) {
+ return FailGetLocalValue(visitor, vreg, tag);
}
+ VLOG(jdwp) << "get boolean local " << vreg << " = " << intVal;
+ JDWP::Set1(buf + 1, intVal != 0);
break;
}
case JDWP::JT_BYTE: {
CHECK_EQ(width, 1U);
uint32_t intVal;
- if (visitor.GetVReg(m, reg, kIntVReg, &intVal)) {
- VLOG(jdwp) << "get byte local " << reg << " = " << intVal;
- JDWP::Set1(buf + 1, intVal);
- } else {
- VLOG(jdwp) << "failed to get byte local " << reg;
- return kFailureErrorCode;
+ if (!visitor.GetVReg(m, vreg, kIntVReg, &intVal)) {
+ return FailGetLocalValue(visitor, vreg, tag);
}
+ VLOG(jdwp) << "get byte local " << vreg << " = " << intVal;
+ JDWP::Set1(buf + 1, intVal);
break;
}
case JDWP::JT_SHORT:
case JDWP::JT_CHAR: {
CHECK_EQ(width, 2U);
uint32_t intVal;
- if (visitor.GetVReg(m, reg, kIntVReg, &intVal)) {
- VLOG(jdwp) << "get short/char local " << reg << " = " << intVal;
- JDWP::Set2BE(buf + 1, intVal);
- } else {
- VLOG(jdwp) << "failed to get short/char local " << reg;
- return kFailureErrorCode;
+ if (!visitor.GetVReg(m, vreg, kIntVReg, &intVal)) {
+ return FailGetLocalValue(visitor, vreg, tag);
}
+ VLOG(jdwp) << "get short/char local " << vreg << " = " << intVal;
+ JDWP::Set2BE(buf + 1, intVal);
break;
}
case JDWP::JT_INT: {
CHECK_EQ(width, 4U);
uint32_t intVal;
- if (visitor.GetVReg(m, reg, kIntVReg, &intVal)) {
- VLOG(jdwp) << "get int local " << reg << " = " << intVal;
- JDWP::Set4BE(buf + 1, intVal);
- } else {
- VLOG(jdwp) << "failed to get int local " << reg;
- return kFailureErrorCode;
+ if (!visitor.GetVReg(m, vreg, kIntVReg, &intVal)) {
+ return FailGetLocalValue(visitor, vreg, tag);
}
+ VLOG(jdwp) << "get int local " << vreg << " = " << intVal;
+ JDWP::Set4BE(buf + 1, intVal);
break;
}
case JDWP::JT_FLOAT: {
CHECK_EQ(width, 4U);
uint32_t intVal;
- if (visitor.GetVReg(m, reg, kFloatVReg, &intVal)) {
- VLOG(jdwp) << "get float local " << reg << " = " << intVal;
- JDWP::Set4BE(buf + 1, intVal);
- } else {
- VLOG(jdwp) << "failed to get float local " << reg;
- return kFailureErrorCode;
+ if (!visitor.GetVReg(m, vreg, kFloatVReg, &intVal)) {
+ return FailGetLocalValue(visitor, vreg, tag);
}
+ VLOG(jdwp) << "get float local " << vreg << " = " << intVal;
+ JDWP::Set4BE(buf + 1, intVal);
break;
}
case JDWP::JT_ARRAY:
@@ -2533,47 +2553,44 @@
case JDWP::JT_THREAD_GROUP: {
CHECK_EQ(width, sizeof(JDWP::ObjectId));
uint32_t intVal;
- if (visitor.GetVReg(m, reg, kReferenceVReg, &intVal)) {
- mirror::Object* o = reinterpret_cast<mirror::Object*>(intVal);
- VLOG(jdwp) << "get " << tag << " object local " << reg << " = " << o;
- if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) {
- LOG(FATAL) << "Register " << reg << " expected to hold " << tag << " object: " << o;
- }
- tag = TagFromObject(soa, o);
- JDWP::SetObjectId(buf + 1, gRegistry->Add(o));
- } else {
- VLOG(jdwp) << "failed to get " << tag << " object local " << reg;
- return kFailureErrorCode;
+ if (!visitor.GetVReg(m, vreg, kReferenceVReg, &intVal)) {
+ return FailGetLocalValue(visitor, vreg, tag);
}
+ mirror::Object* o = reinterpret_cast<mirror::Object*>(intVal);
+ VLOG(jdwp) << "get " << tag << " object local " << vreg << " = " << o;
+ if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) {
+ LOG(FATAL) << StringPrintf("Found invalid object %#" PRIxPTR " in register v%u",
+ reinterpret_cast<uintptr_t>(o), vreg)
+ << GetStackContextAsString(visitor);
+ UNREACHABLE();
+ }
+ tag = TagFromObject(soa, o);
+ JDWP::SetObjectId(buf + 1, gRegistry->Add(o));
break;
}
case JDWP::JT_DOUBLE: {
CHECK_EQ(width, 8U);
uint64_t longVal;
- if (visitor.GetVRegPair(m, reg, kDoubleLoVReg, kDoubleHiVReg, &longVal)) {
- VLOG(jdwp) << "get double local " << reg << " = " << longVal;
- JDWP::Set8BE(buf + 1, longVal);
- } else {
- VLOG(jdwp) << "failed to get double local " << reg;
- return kFailureErrorCode;
+ if (!visitor.GetVRegPair(m, vreg, kDoubleLoVReg, kDoubleHiVReg, &longVal)) {
+ return FailGetLocalValue(visitor, vreg, tag);
}
+ VLOG(jdwp) << "get double local " << vreg << " = " << longVal;
+ JDWP::Set8BE(buf + 1, longVal);
break;
}
case JDWP::JT_LONG: {
CHECK_EQ(width, 8U);
uint64_t longVal;
- if (visitor.GetVRegPair(m, reg, kLongLoVReg, kLongHiVReg, &longVal)) {
- VLOG(jdwp) << "get long local " << reg << " = " << longVal;
- JDWP::Set8BE(buf + 1, longVal);
- } else {
- VLOG(jdwp) << "failed to get long local " << reg;
- return kFailureErrorCode;
+ if (!visitor.GetVRegPair(m, vreg, kLongLoVReg, kLongHiVReg, &longVal)) {
+ return FailGetLocalValue(visitor, vreg, tag);
}
+ VLOG(jdwp) << "get long local " << vreg << " = " << longVal;
+ JDWP::Set8BE(buf + 1, longVal);
break;
}
default:
LOG(FATAL) << "Unknown tag " << tag;
- break;
+ UNREACHABLE();
}
// Prepend tag, which may have been updated.
@@ -2594,6 +2611,9 @@
if (error != JDWP::ERR_NONE) {
return error;
}
+ if (!IsSuspendedForDebugger(soa, thread)) {
+ return JDWP::ERR_THREAD_NOT_SUSPENDED;
+ }
}
// Find the frame with the given frame_id.
std::unique_ptr<Context> context(Context::Create());
@@ -2620,46 +2640,50 @@
return JDWP::ERR_NONE;
}
+template<typename T>
+static JDWP::JdwpError FailSetLocalValue(const StackVisitor& visitor, uint16_t vreg,
+ JDWP::JdwpTag tag, T value)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ LOG(ERROR) << "Failed to write " << tag << " local " << value
+ << " (0x" << std::hex << value << ") into register v" << vreg
+ << GetStackContextAsString(visitor);
+ return kStackFrameLocalAccessError;
+}
+
JDWP::JdwpError Dbg::SetLocalValue(StackVisitor& visitor, int slot, JDWP::JdwpTag tag,
uint64_t value, size_t width) {
mirror::ArtMethod* m = visitor.GetMethod();
- uint16_t reg = DemangleSlot(slot, m);
+ JDWP::JdwpError error = JDWP::ERR_NONE;
+ uint16_t vreg = DemangleSlot(slot, m, &error);
+ if (error != JDWP::ERR_NONE) {
+ return error;
+ }
// TODO: check that the tag is compatible with the actual type of the slot!
- // TODO: check slot is valid for this method or return INVALID_SLOT error.
- constexpr JDWP::JdwpError kFailureErrorCode = JDWP::ERR_ABSENT_INFORMATION;
switch (tag) {
case JDWP::JT_BOOLEAN:
case JDWP::JT_BYTE:
CHECK_EQ(width, 1U);
- if (!visitor.SetVReg(m, reg, static_cast<uint32_t>(value), kIntVReg)) {
- VLOG(jdwp) << "failed to set boolean/byte local " << reg << " = "
- << static_cast<uint32_t>(value);
- return kFailureErrorCode;
+ if (!visitor.SetVReg(m, vreg, static_cast<uint32_t>(value), kIntVReg)) {
+ return FailSetLocalValue(visitor, vreg, tag, static_cast<uint32_t>(value));
}
break;
case JDWP::JT_SHORT:
case JDWP::JT_CHAR:
CHECK_EQ(width, 2U);
- if (!visitor.SetVReg(m, reg, static_cast<uint32_t>(value), kIntVReg)) {
- VLOG(jdwp) << "failed to set short/char local " << reg << " = "
- << static_cast<uint32_t>(value);
- return kFailureErrorCode;
+ if (!visitor.SetVReg(m, vreg, static_cast<uint32_t>(value), kIntVReg)) {
+ return FailSetLocalValue(visitor, vreg, tag, static_cast<uint32_t>(value));
}
break;
case JDWP::JT_INT:
CHECK_EQ(width, 4U);
- if (!visitor.SetVReg(m, reg, static_cast<uint32_t>(value), kIntVReg)) {
- VLOG(jdwp) << "failed to set int local " << reg << " = "
- << static_cast<uint32_t>(value);
- return kFailureErrorCode;
+ if (!visitor.SetVReg(m, vreg, static_cast<uint32_t>(value), kIntVReg)) {
+ return FailSetLocalValue(visitor, vreg, tag, static_cast<uint32_t>(value));
}
break;
case JDWP::JT_FLOAT:
CHECK_EQ(width, 4U);
- if (!visitor.SetVReg(m, reg, static_cast<uint32_t>(value), kFloatVReg)) {
- VLOG(jdwp) << "failed to set float local " << reg << " = "
- << static_cast<uint32_t>(value);
- return kFailureErrorCode;
+ if (!visitor.SetVReg(m, vreg, static_cast<uint32_t>(value), kFloatVReg)) {
+ return FailSetLocalValue(visitor, vreg, tag, static_cast<uint32_t>(value));
}
break;
case JDWP::JT_ARRAY:
@@ -2670,38 +2694,35 @@
case JDWP::JT_THREAD:
case JDWP::JT_THREAD_GROUP: {
CHECK_EQ(width, sizeof(JDWP::ObjectId));
- JDWP::JdwpError error;
mirror::Object* o = gRegistry->Get<mirror::Object*>(static_cast<JDWP::ObjectId>(value),
&error);
if (error != JDWP::ERR_NONE) {
VLOG(jdwp) << tag << " object " << o << " is an invalid object";
return JDWP::ERR_INVALID_OBJECT;
- } else if (!visitor.SetVReg(m, reg, static_cast<uint32_t>(reinterpret_cast<uintptr_t>(o)),
- kReferenceVReg)) {
- VLOG(jdwp) << "failed to set " << tag << " object local " << reg << " = " << o;
- return kFailureErrorCode;
+ }
+ if (!visitor.SetVReg(m, vreg, static_cast<uint32_t>(reinterpret_cast<uintptr_t>(o)),
+ kReferenceVReg)) {
+ return FailSetLocalValue(visitor, vreg, tag, reinterpret_cast<uintptr_t>(o));
}
break;
}
case JDWP::JT_DOUBLE: {
CHECK_EQ(width, 8U);
- if (!visitor.SetVRegPair(m, reg, value, kDoubleLoVReg, kDoubleHiVReg)) {
- VLOG(jdwp) << "failed to set double local " << reg << " = " << value;
- return kFailureErrorCode;
+ if (!visitor.SetVRegPair(m, vreg, value, kDoubleLoVReg, kDoubleHiVReg)) {
+ return FailSetLocalValue(visitor, vreg, tag, value);
}
break;
}
case JDWP::JT_LONG: {
CHECK_EQ(width, 8U);
- if (!visitor.SetVRegPair(m, reg, value, kLongLoVReg, kLongHiVReg)) {
- VLOG(jdwp) << "failed to set double local " << reg << " = " << value;
- return kFailureErrorCode;
+ if (!visitor.SetVRegPair(m, vreg, value, kLongLoVReg, kLongHiVReg)) {
+ return FailSetLocalValue(visitor, vreg, tag, value);
}
break;
}
default:
LOG(FATAL) << "Unknown tag " << tag;
- break;
+ UNREACHABLE();
}
return JDWP::ERR_NONE;
}
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index b2798d3..0422fcd 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -27,6 +27,7 @@
#include "class_linker.h"
#include "common_runtime_test.h"
+#include "compiler_callbacks.h"
#include "mem_map.h"
#include "os.h"
#include "thread-inl.h"
@@ -77,11 +78,7 @@
nullptr));
// Make sure compilercallbacks are not set so that relocation will be
// enabled.
- for (std::pair<std::string, const void*>& pair : *options) {
- if (pair.first == "compilercallbacks") {
- pair.second = nullptr;
- }
- }
+ callbacks_.reset();
}
virtual void PreRuntimeCreate() {
diff --git a/test/467-regalloc-pair/expected.txt b/test/467-regalloc-pair/expected.txt
new file mode 100644
index 0000000..da39d9d
--- /dev/null
+++ b/test/467-regalloc-pair/expected.txt
@@ -0,0 +1 @@
+In interface
diff --git a/test/467-regalloc-pair/info.txt b/test/467-regalloc-pair/info.txt
new file mode 100644
index 0000000..882a29c
--- /dev/null
+++ b/test/467-regalloc-pair/info.txt
@@ -0,0 +1,2 @@
+Regression test for optimizing's register allocator
+that used to trip when compiling TestCase.testCase on x86.
diff --git a/test/467-regalloc-pair/smali/TestCase.smali b/test/467-regalloc-pair/smali/TestCase.smali
new file mode 100644
index 0000000..a3101fe
--- /dev/null
+++ b/test/467-regalloc-pair/smali/TestCase.smali
@@ -0,0 +1,59 @@
+# Copyright (C) 2015 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.
+
+.class public LTestCase;
+
+.super Ljava/lang/Object;
+
+.method public static testCase([BLMain;)V
+ .registers 12
+ const/4 v2, 0
+ array-length v0, v10
+ div-int/lit8 v0, v0, 7
+ invoke-static {v2, v0}, Ljava/lang/Math;->max(II)I
+ move-result v7
+ move v6, v2
+ move v3, v2
+ :label5
+ if-ge v6, v7, :label1
+ const-wide/16 v0, 0
+ move-wide v4, v0
+ move v1, v2
+ move v0, v3
+ :label4
+ const/4 v3, 6
+ if-ge v1, v3, :label2
+ const/16 v3, 8
+ shl-long/2addr v4, v3
+ add-int/lit8 v3, v0, 1
+ aget-byte v0, v10, v0
+ if-gez v0, :label3
+ add-int/lit16 v0, v0, 256
+ :label3
+ int-to-long v8, v0
+ or-long/2addr v4, v8
+ add-int/lit8 v0, v1, 1
+ move v1, v0
+ move v0, v3
+ goto :label4
+ :label2
+ add-int/lit8 v3, v0, 1
+ aget-byte v0, v10, v0
+ invoke-interface {v11, v4, v5, v0}, LItf;->invokeInterface(JI)V
+ add-int/lit8 v0, v6, 1
+ move v6, v0
+ goto :label5
+ :label1
+ return-void
+.end method
diff --git a/test/467-regalloc-pair/src/Main.java b/test/467-regalloc-pair/src/Main.java
new file mode 100644
index 0000000..aac07fd
--- /dev/null
+++ b/test/467-regalloc-pair/src/Main.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 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 java.lang.reflect.Method;
+
+interface Itf {
+ public void invokeInterface(long l, int i);
+}
+
+public class Main implements Itf {
+
+ // Workaround for b/18051191.
+ class InnerClass {}
+
+ public static void main(String[] args) throws Exception {
+ Class<?> c = Class.forName("TestCase");
+ Method m = c.getMethod("testCase", byte[].class, Main.class);
+ m.invoke(null, new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 }, new Main());
+ }
+
+ public void invokeInterface(long l, int i) {
+ System.out.println("In interface");
+ }
+}
diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh
index c51bd20..7d3030e 100755
--- a/tools/run-jdwp-tests.sh
+++ b/tools/run-jdwp-tests.sh
@@ -19,6 +19,11 @@
exit 1
fi
+if [[ $ANDROID_SERIAL == 03a79ae90ae5889b ]] || [[ $ANDROID_SERIAL == HT4CTJT03670 ]] || [[ $ANDROID_SERIAL == HT49CJT00070 ]]; then
+ echo "Not run because of localhost failures. Investigating."
+ exit 0
+fi
+
# Jar containing all the tests.
test_jar=out/host/linux-x86/framework/apache-harmony-jdwp-tests-hostdex.jar
junit_jar=out/host/linux-x86/framework/junit.jar
@@ -35,13 +40,19 @@
args=$@
debuggee_args="-Xcompiler-option --compiler-backend=Optimizing -Xcompiler-option --debuggable"
device_dir="--device-dir=/data/local/tmp"
+# We use the art script on target to ensure the runner and the debuggee share the same
+# image.
+vm_command="--vm-command=$art"
while true; do
if [[ "$1" == "--mode=host" ]]; then
art="art"
# We force generation of a new image to avoid build-time and run-time classpath differences.
image="-Ximage:/system/non/existent"
+ # We do not need a device directory on host.
device_dir=""
+ # Vogar knows which VM to use on host.
+ vm_command=""
shift
elif [[ $1 == -Ximage:* ]]; then
image="$1"
@@ -54,8 +65,9 @@
done
# Run the tests using vogar.
-vogar --vm-command=$art \
+vogar $vm_command \
--vm-arg $image \
+ --verbose \
$args \
$device_dir \
--vm-arg -Ximage-compiler-option \