merge in mnc-release history after reset to mnc-dev
diff --git a/compiler/dex/quick/quick_compiler.cc b/compiler/dex/quick/quick_compiler.cc
index 58236e2..ff4659a 100644
--- a/compiler/dex/quick/quick_compiler.cc
+++ b/compiler/dex/quick/quick_compiler.cc
@@ -33,6 +33,7 @@
#include "dex/pass_driver_me_post_opt.h"
#include "dex/pass_manager.h"
#include "dex/quick/mir_to_lir.h"
+#include "dex/verified_method.h"
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
#include "elf_writer_quick.h"
@@ -624,6 +625,10 @@
return nullptr;
}
+ if (driver->GetVerifiedMethod(&dex_file, method_idx)->HasRuntimeThrow()) {
+ return nullptr;
+ }
+
DCHECK(driver->GetCompilerOptions().IsCompilationEnabled());
Runtime* const runtime = Runtime::Current();
diff --git a/compiler/dex/verified_method.cc b/compiler/dex/verified_method.cc
index 9b141cc..2bc8042 100644
--- a/compiler/dex/verified_method.cc
+++ b/compiler/dex/verified_method.cc
@@ -41,6 +41,7 @@
bool compile) {
std::unique_ptr<VerifiedMethod> verified_method(new VerifiedMethod);
verified_method->has_verification_failures_ = method_verifier->HasFailures();
+ verified_method->has_runtime_throw_ = method_verifier->HasInstructionThatWillThrow();
if (compile) {
/* Generate a register map. */
if (!verified_method->GenerateGcMap(method_verifier)) {
diff --git a/compiler/dex/verified_method.h b/compiler/dex/verified_method.h
index 242e3df..bad4495 100644
--- a/compiler/dex/verified_method.h
+++ b/compiler/dex/verified_method.h
@@ -75,6 +75,10 @@
return has_verification_failures_;
}
+ bool HasRuntimeThrow() const {
+ return has_runtime_throw_;
+ }
+
void SetStringInitPcRegMap(SafeMap<uint32_t, std::set<uint32_t>>& string_init_pc_reg_map) {
string_init_pc_reg_map_ = string_init_pc_reg_map;
}
@@ -121,6 +125,7 @@
SafeCastSet safe_cast_set_;
bool has_verification_failures_;
+ bool has_runtime_throw_ = false;
// Copy of mapping generated by verifier of dex PCs of string init invocations
// to the set of other registers that the receiver has been copied into.
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index e98e572..f3bda2f 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -165,6 +165,7 @@
void StackArgsIntsFirstImpl();
void StackArgsFloatsFirstImpl();
void StackArgsMixedImpl();
+ void StackArgsSignExtendedMips64Impl();
JNIEnv* env_;
jmethodID jmethod_;
@@ -1715,4 +1716,49 @@
JNI_TEST(StackArgsMixed)
+void Java_MyClassNatives_stackArgsSignExtendedMips64(JNIEnv*, jclass, jint i1, jint i2, jint i3,
+ jint i4, jint i5, jint i6, jint i7, jint i8) {
+ EXPECT_EQ(i1, 1);
+ EXPECT_EQ(i2, 2);
+ EXPECT_EQ(i3, 3);
+ EXPECT_EQ(i4, 4);
+ EXPECT_EQ(i5, 5);
+ EXPECT_EQ(i6, 6);
+ EXPECT_EQ(i7, 7);
+ EXPECT_EQ(i8, -8);
+
+#if defined(__mips__) && defined(__LP64__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+ // Mips64 ABI requires that arguments passed through stack be sign-extended 8B slots.
+ // First 8 arguments are passed through registers, check i7 and i8.
+ uint32_t stack1_high = *(&i7 + 1);
+ uint32_t stack2_high = *(&i8 + 1);
+
+ EXPECT_EQ(stack1_high, static_cast<uint32_t>(0));
+ EXPECT_EQ(stack2_high, static_cast<uint32_t>(0xffffffff));
+#else
+ LOG(INFO) << "Skipping stackArgsSignExtendedMips64 as there is nothing to be done on "
+ << kRuntimeISA;
+ // Force-print to std::cout so it's also outside the logcat.
+ std::cout << "Skipping stackArgsSignExtendedMips64 as there is nothing to be done on "
+ << kRuntimeISA << std::endl;
+#endif
+}
+
+void JniCompilerTest::StackArgsSignExtendedMips64Impl() {
+ SetUpForTest(true, "stackArgsSignExtendedMips64", "(IIIIIIII)V",
+ reinterpret_cast<void*>(&Java_MyClassNatives_stackArgsSignExtendedMips64));
+ jint i1 = 1;
+ jint i2 = 2;
+ jint i3 = 3;
+ jint i4 = 4;
+ jint i5 = 5;
+ jint i6 = 6;
+ jint i7 = 7;
+ jint i8 = -8;
+
+ env_->CallStaticVoidMethod(jklass_, jmethod_, i1, i2, i3, i4, i5, i6, i7, i8);
+}
+
+JNI_TEST(StackArgsSignExtendedMips64)
+
} // namespace art
diff --git a/compiler/optimizing/boolean_simplifier.cc b/compiler/optimizing/boolean_simplifier.cc
index 8100a29..daf7d67 100644
--- a/compiler/optimizing/boolean_simplifier.cc
+++ b/compiler/optimizing/boolean_simplifier.cc
@@ -141,6 +141,12 @@
block->MergeWith(false_block);
block->MergeWith(merge_block);
+ // No need to update any dominance information, as we are simplifying
+ // a simple diamond shape, where the join block is merged with the
+ // entry block. Any following blocks would have had the join block
+ // as a dominator, and `MergeWith` handles changing that to the
+ // entry block.
+
// Remove the original condition if it is now unused.
if (!if_condition->HasUses()) {
if_condition->GetBlock()->RemoveInstructionOrPhi(if_condition);
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index 900dabe..ebc0adc 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -1180,9 +1180,7 @@
}
}
ValueRange* narrowed_range = existing_range->Narrow(range);
- if (narrowed_range != nullptr) {
- GetValueRangeMap(successor)->Overwrite(instruction->GetId(), narrowed_range);
- }
+ GetValueRangeMap(successor)->Overwrite(instruction->GetId(), narrowed_range);
}
// Special case that we may simultaneously narrow two MonotonicValueRange's to
@@ -1388,7 +1386,7 @@
if (array_length->IsPhi()) {
// Input 1 of the phi contains the real array.length once the loop body is
// entered. That value will be used for bound analysis. The graph is still
- // strickly in SSA form.
+ // strictly in SSA form.
array_length = array_length->AsPhi()->InputAt(1)->AsArrayLength();
}
@@ -1730,6 +1728,10 @@
ValueBound upper = ValueBound(new_array, -right_const);
ValueRange* range = new (GetGraph()->GetArena())
ValueRange(GetGraph()->GetArena(), lower, upper);
+ ValueRange* existing_range = LookupValueRange(left, new_array->GetBlock());
+ if (existing_range != nullptr) {
+ range = existing_range->Narrow(range);
+ }
GetValueRangeMap(new_array->GetBlock())->Overwrite(left->GetId(), range);
}
}
@@ -1780,7 +1782,13 @@
it != first_constant_index_bounds_check_map_.end();
++it) {
HBoundsCheck* bounds_check = it->second;
- HArrayLength* array_length = bounds_check->InputAt(1)->AsArrayLength();
+ HInstruction* array_length = bounds_check->InputAt(1);
+ if (!array_length->IsArrayLength()) {
+ // Prior deoptimizations may have changed the array length to a phi.
+ // TODO(mingyao): propagate the range to the phi?
+ DCHECK(array_length->IsPhi()) << array_length->DebugName();
+ continue;
+ }
HIntConstant* lower_bound_const_instr = nullptr;
int32_t lower_bound_const = INT_MIN;
size_t counter = 0;
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index ed39923..b564aca 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -2062,8 +2062,13 @@
case Instruction::MOVE_RESULT:
case Instruction::MOVE_RESULT_WIDE:
case Instruction::MOVE_RESULT_OBJECT:
- UpdateLocal(instruction.VRegA(), latest_result_);
- latest_result_ = nullptr;
+ if (latest_result_ == nullptr) {
+ // Only dead code can lead to this situation, where the verifier
+ // does not reject the method.
+ } else {
+ UpdateLocal(instruction.VRegA(), latest_result_);
+ latest_result_ = nullptr;
+ }
break;
case Instruction::CMP_LONG: {
diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc
index bfed1a8..4db3b43 100644
--- a/compiler/optimizing/codegen_test.cc
+++ b/compiler/optimizing/codegen_test.cc
@@ -180,7 +180,10 @@
std::function<void(HGraph*)> hook_before_codegen,
bool has_result,
Expected expected) {
- graph->BuildDominatorTree();
+ // Tests may have already computed it.
+ if (graph->GetReversePostOrder().IsEmpty()) {
+ graph->BuildDominatorTree();
+ }
SsaLivenessAnalysis liveness(graph, codegen);
liveness.Analyze();
diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc
index 6fbe75e..2362cc1 100644
--- a/compiler/optimizing/dead_code_elimination.cc
+++ b/compiler/optimizing/dead_code_elimination.cc
@@ -67,6 +67,7 @@
ArenaBitVector affected_loops(allocator, graph_->GetBlocks().Size(), false);
MarkReachableBlocks(graph_->GetEntryBlock(), &live_blocks);
+ bool removed_one_or_more_blocks = false;
// Remove all dead blocks. Iterate in post order because removal needs the
// block's chain of dominators and nested loops need to be updated from the
@@ -83,9 +84,17 @@
MaybeRecordDeadBlock(block);
MarkLoopHeadersContaining(*block, &affected_loops);
block->DisconnectAndDelete();
+ removed_one_or_more_blocks = true;
}
}
+ // If we removed at least one block, we need to recompute the full
+ // dominator tree.
+ if (removed_one_or_more_blocks) {
+ graph_->ClearDominanceInformation();
+ graph_->ComputeDominanceInformation();
+ }
+
// Connect successive blocks created by dead branches. Order does not matter.
for (HReversePostOrderIterator it(*graph_); !it.Done();) {
HBasicBlock* block = it.Current();
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index c01364a..88490d0 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -116,9 +116,24 @@
// dominators and the reverse post order.
SimplifyCFG();
- // (5) Compute the immediate dominator of each block. We visit
- // the successors of a block only when all its forward branches
- // have been processed.
+ // (5) Compute the dominance information and the reverse post order.
+ ComputeDominanceInformation();
+}
+
+void HGraph::ClearDominanceInformation() {
+ for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) {
+ it.Current()->ClearDominanceInformation();
+ }
+ reverse_post_order_.Reset();
+}
+
+void HBasicBlock::ClearDominanceInformation() {
+ dominated_blocks_.Reset();
+ dominator_ = nullptr;
+}
+
+void HGraph::ComputeDominanceInformation() {
+ DCHECK(reverse_post_order_.IsEmpty());
GrowableArray<size_t> visits(arena_, blocks_.Size());
visits.SetSize(blocks_.Size());
reverse_post_order_.Add(entry_block_);
@@ -1037,8 +1052,7 @@
}
predecessors_.Reset();
- // Disconnect the block from its successors and update their dominators
- // and phis.
+ // Disconnect the block from its successors and update their phis.
for (size_t i = 0, e = successors_.Size(); i < e; ++i) {
HBasicBlock* successor = successors_.Get(i);
// Delete this block from the list of predecessors.
@@ -1049,19 +1063,6 @@
// dominator of `successor` which violates the order DCHECKed at the top.
DCHECK(!successor->predecessors_.IsEmpty());
- // Recompute the successor's dominator.
- HBasicBlock* old_dominator = successor->GetDominator();
- HBasicBlock* new_dominator = successor->predecessors_.Get(0);
- for (size_t j = 1, f = successor->predecessors_.Size(); j < f; ++j) {
- new_dominator = graph_->FindCommonDominator(
- new_dominator, successor->predecessors_.Get(j));
- }
- if (old_dominator != new_dominator) {
- successor->SetDominator(new_dominator);
- old_dominator->RemoveDominatedBlock(successor);
- new_dominator->AddDominatedBlock(successor);
- }
-
// Remove this block's entries in the successor's phis.
if (successor->predecessors_.Size() == 1u) {
// The successor has just one predecessor left. Replace phis with the only
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 394e3fc..b36d9b8 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -170,6 +170,9 @@
return true;
}
+ void ComputeDominanceInformation();
+ void ClearDominanceInformation();
+
void BuildDominatorTree();
void TransformToSsa();
void SimplifyCFG();
@@ -547,11 +550,10 @@
LOG(FATAL) << "Unreachable";
UNREACHABLE();
}
+ void ClearDominanceInformation();
int NumberOfBackEdges() const {
- return loop_information_ == nullptr
- ? 0
- : loop_information_->NumberOfBackEdges();
+ return IsLoopHeader() ? loop_information_->NumberOfBackEdges() : 0;
}
HInstruction* GetFirstInstruction() const { return instructions_.first_instruction_; }
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 810b4f8..8958932 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -618,7 +618,8 @@
const DexFile& dex_file) const {
CompilerDriver* compiler_driver = GetCompilerDriver();
CompiledMethod* method = nullptr;
- if (compiler_driver->IsMethodVerifiedWithoutFailures(method_idx, class_def_idx, dex_file)) {
+ if (compiler_driver->IsMethodVerifiedWithoutFailures(method_idx, class_def_idx, dex_file) &&
+ !compiler_driver->GetVerifiedMethod(&dex_file, method_idx)->HasRuntimeThrow()) {
method = TryCompile(code_item, access_flags, invoke_type, class_def_idx,
method_idx, jclass_loader, dex_file);
} else {
diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc
index a8b55d1..5e49b93 100644
--- a/compiler/utils/mips64/assembler_mips64.cc
+++ b/compiler/utils/mips64/assembler_mips64.cc
@@ -839,7 +839,7 @@
CHECK(size == 4 || size == 8) << size;
if (size == 4) {
LoadFromOffset(kLoadWord, scratch.AsGpuRegister(), SP, src.Int32Value());
- StoreToOffset(kStoreWord, scratch.AsGpuRegister(), SP, dest.Int32Value());
+ StoreToOffset(kStoreDoubleword, scratch.AsGpuRegister(), SP, dest.Int32Value());
} else if (size == 8) {
LoadFromOffset(kLoadDoubleword, scratch.AsGpuRegister(), SP, src.Int32Value());
StoreToOffset(kStoreDoubleword, scratch.AsGpuRegister(), SP, dest.Int32Value());
@@ -855,7 +855,7 @@
if (size == 4) {
LoadFromOffset(kLoadWord, scratch, src_base.AsMips64().AsGpuRegister(),
src_offset.Int32Value());
- StoreToOffset(kStoreWord, scratch, SP, dest.Int32Value());
+ StoreToOffset(kStoreDoubleword, scratch, SP, dest.Int32Value());
} else if (size == 8) {
LoadFromOffset(kLoadDoubleword, scratch, src_base.AsMips64().AsGpuRegister(),
src_offset.Int32Value());
@@ -871,7 +871,7 @@
CHECK(size == 4 || size == 8) << size;
if (size == 4) {
LoadFromOffset(kLoadWord, scratch, SP, src.Int32Value());
- StoreToOffset(kStoreWord, scratch, dest_base.AsMips64().AsGpuRegister(),
+ StoreToOffset(kStoreDoubleword, scratch, dest_base.AsMips64().AsGpuRegister(),
dest_offset.Int32Value());
} else if (size == 8) {
LoadFromOffset(kLoadDoubleword, scratch, SP, src.Int32Value());
@@ -894,7 +894,7 @@
CHECK(size == 4 || size == 8) << size;
if (size == 4) {
LoadFromOffset(kLoadWord, scratch, src.AsMips64().AsGpuRegister(), src_offset.Int32Value());
- StoreToOffset(kStoreWord, scratch, dest.AsMips64().AsGpuRegister(), dest_offset.Int32Value());
+ StoreToOffset(kStoreDoubleword, scratch, dest.AsMips64().AsGpuRegister(), dest_offset.Int32Value());
} else if (size == 8) {
LoadFromOffset(kLoadDoubleword, scratch, src.AsMips64().AsGpuRegister(),
src_offset.Int32Value());
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 5ae291a..d02ab14 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -1052,9 +1052,12 @@
.cfi_adjust_cfa_offset 8
.cfi_rel_offset r0, 0
.cfi_rel_offset r1, 4
- sub sp, #8 @ space for return value argument
+ vpush {d0} @ save fp return value
.cfi_adjust_cfa_offset 8
- strd r0, [sp] @ r0/r1 -> [sp] for fpr_res
+ sub sp, #8 @ space for return value argument. Note: AAPCS stack alignment is 8B, no
+ @ need to align by 16.
+ .cfi_adjust_cfa_offset 8
+ vstr d0, [sp] @ d0 -> [sp] for fpr_res
mov r2, r0 @ pass return value as gpr_res
mov r3, r1
mov r0, r9 @ pass Thread::Current
@@ -1065,6 +1068,8 @@
mov r2, r0 @ link register saved by instrumentation
mov lr, r1 @ r1 is holding link register if we're to bounce to deoptimize
+ vpop {d0} @ restore fp return value
+ .cfi_adjust_cfa_offset -8
pop {r0, r1} @ restore return value
.cfi_adjust_cfa_offset -8
.cfi_restore r0
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index 7f68c6f..b662821 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -1373,7 +1373,7 @@
dla $t9, art_quick_invoke_interface_trampoline
.cpreturn
jalr $zero, $t9
- lwu $a0, MIRROR_LONG_ARRAY_DATA_OFFSET($a0) # load the target method
+ ld $a0, MIRROR_LONG_ARRAY_DATA_OFFSET($a0) # load the target method
END art_quick_imt_conflict_trampoline
.extern artQuickResolutionTrampoline
diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc
index 549eac2..45fb9c4 100644
--- a/runtime/check_jni.cc
+++ b/runtime/check_jni.cc
@@ -1188,7 +1188,7 @@
* Create an over-sized buffer to hold the contents of "buf". Copy it in,
* filling in the area around it with guard data.
*/
- static void* Create(const void* original_buf, size_t len, bool mod_okay) {
+ static void* Create(void* original_buf, size_t len, bool mod_okay) {
const size_t new_len = LengthIncludingRedZones(len);
uint8_t* const new_buf = DebugAlloc(new_len);
@@ -1227,13 +1227,14 @@
* Create a guarded copy of a primitive array. Modifications to the copied
* data are allowed. Returns a pointer to the copied data.
*/
- static void* CreateGuardedPACopy(JNIEnv* env, const jarray java_array, jboolean* is_copy) {
+ static void* CreateGuardedPACopy(JNIEnv* env, const jarray java_array, jboolean* is_copy,
+ void* original_ptr) {
ScopedObjectAccess soa(env);
mirror::Array* a = soa.Decode<mirror::Array*>(java_array);
size_t component_size = a->GetClass()->GetComponentSize();
size_t byte_count = a->GetLength() * component_size;
- void* result = Create(a->GetRawData(component_size, 0), byte_count, true);
+ void* result = Create(original_ptr, byte_count, true);
if (is_copy != nullptr) {
*is_copy = JNI_TRUE;
}
@@ -1244,22 +1245,22 @@
* Perform the array "release" operation, which may or may not copy data
* back into the managed heap, and may or may not release the underlying storage.
*/
- static void* ReleaseGuardedPACopy(const char* function_name, JNIEnv* env, jarray java_array,
- void* embedded_buf, int mode) {
+ static void* ReleaseGuardedPACopy(const char* function_name, JNIEnv* env,
+ jarray java_array ATTRIBUTE_UNUSED, void* embedded_buf,
+ int mode) {
ScopedObjectAccess soa(env);
- mirror::Array* a = soa.Decode<mirror::Array*>(java_array);
-
if (!GuardedCopy::Check(function_name, embedded_buf, true)) {
return nullptr;
}
+ GuardedCopy* const copy = FromEmbedded(embedded_buf);
+ void* original_ptr = copy->original_ptr_;
if (mode != JNI_ABORT) {
- size_t len = FromEmbedded(embedded_buf)->original_length_;
- memcpy(a->GetRawData(a->GetClass()->GetComponentSize(), 0), embedded_buf, len);
+ memcpy(original_ptr, embedded_buf, copy->original_length_);
}
if (mode != JNI_COMMIT) {
- return Destroy(embedded_buf);
+ Destroy(embedded_buf);
}
- return embedded_buf;
+ return original_ptr;
}
@@ -1286,7 +1287,7 @@
}
private:
- GuardedCopy(const void* original_buf, size_t len, uLong adler) :
+ GuardedCopy(void* original_buf, size_t len, uLong adler) :
magic_(kGuardMagic), adler_(adler), original_ptr_(original_buf), original_length_(len) {
}
@@ -1414,7 +1415,7 @@
const uint32_t magic_;
const uLong adler_;
- const void* const original_ptr_;
+ void* const original_ptr_;
const size_t original_length_;
};
const char* const GuardedCopy::kCanary = "JNI BUFFER RED ZONE";
@@ -2307,10 +2308,11 @@
JniValueType args[3] = {{.E = env}, {.a = array}, {.p = is_copy}};
if (sc.Check(soa, true, "Eap", args)) {
JniValueType result;
- result.p = baseEnv(env)->GetPrimitiveArrayCritical(env, array, is_copy);
- if (result.p != nullptr && soa.ForceCopy()) {
- result.p = GuardedCopy::CreateGuardedPACopy(env, array, is_copy);
+ void* ptr = baseEnv(env)->GetPrimitiveArrayCritical(env, array, is_copy);
+ if (ptr != nullptr && soa.ForceCopy()) {
+ ptr = GuardedCopy::CreateGuardedPACopy(env, array, is_copy, ptr);
}
+ result.p = ptr;
if (sc.Check(soa, false, "p", &result)) {
return const_cast<void*>(result.p);
}
@@ -2325,7 +2327,7 @@
JniValueType args[4] = {{.E = env}, {.a = array}, {.p = carray}, {.r = mode}};
if (sc.Check(soa, true, "Eapr", args)) {
if (soa.ForceCopy()) {
- GuardedCopy::ReleaseGuardedPACopy(__FUNCTION__, env, array, carray, mode);
+ carray = GuardedCopy::ReleaseGuardedPACopy(__FUNCTION__, env, array, carray, mode);
}
baseEnv(env)->ReleasePrimitiveArrayCritical(env, array, carray, mode);
JniValueType result;
@@ -3062,26 +3064,26 @@
JniValueType args[3] = {{.E = env}, {.s = string}, {.p = is_copy}};
if (sc.Check(soa, true, "Esp", args)) {
JniValueType result;
+ void* ptr;
if (utf) {
CHECK(!critical);
- result.u = baseEnv(env)->GetStringUTFChars(env, string, is_copy);
+ ptr = const_cast<char*>(baseEnv(env)->GetStringUTFChars(env, string, is_copy));
+ result.u = reinterpret_cast<char*>(ptr);
} else {
- if (critical) {
- result.p = baseEnv(env)->GetStringCritical(env, string, is_copy);
- } else {
- result.p = baseEnv(env)->GetStringChars(env, string, is_copy);
- }
+ ptr = const_cast<jchar*>(critical ? baseEnv(env)->GetStringCritical(env, string, is_copy) :
+ baseEnv(env)->GetStringChars(env, string, is_copy));
+ result.p = ptr;
}
// TODO: could we be smarter about not copying when local_is_copy?
- if (result.p != nullptr && soa.ForceCopy()) {
+ if (ptr != nullptr && soa.ForceCopy()) {
if (utf) {
size_t length_in_bytes = strlen(result.u) + 1;
result.u =
- reinterpret_cast<const char*>(GuardedCopy::Create(result.u, length_in_bytes, false));
+ reinterpret_cast<const char*>(GuardedCopy::Create(ptr, length_in_bytes, false));
} else {
size_t length_in_bytes = baseEnv(env)->GetStringLength(env, string) * 2;
result.p =
- reinterpret_cast<const jchar*>(GuardedCopy::Create(result.p, length_in_bytes, false));
+ reinterpret_cast<const jchar*>(GuardedCopy::Create(ptr, length_in_bytes, false));
}
if (is_copy != nullptr) {
*is_copy = JNI_TRUE;
@@ -3175,47 +3177,43 @@
JniValueType args[3] = {{.E = env}, {.a = array}, {.p = is_copy}};
if (sc.Check(soa, true, "Eap", args) && sc.CheckPrimitiveArrayType(soa, array, type)) {
JniValueType result;
+ void* ptr = nullptr;
switch (type) {
case Primitive::kPrimBoolean:
- result.p = baseEnv(env)->GetBooleanArrayElements(env, down_cast<jbooleanArray>(array),
- is_copy);
+ ptr = baseEnv(env)->GetBooleanArrayElements(env, down_cast<jbooleanArray>(array),
+ is_copy);
break;
case Primitive::kPrimByte:
- result.p = baseEnv(env)->GetByteArrayElements(env, down_cast<jbyteArray>(array),
- is_copy);
+ ptr = baseEnv(env)->GetByteArrayElements(env, down_cast<jbyteArray>(array), is_copy);
break;
case Primitive::kPrimChar:
- result.p = baseEnv(env)->GetCharArrayElements(env, down_cast<jcharArray>(array),
- is_copy);
+ ptr = baseEnv(env)->GetCharArrayElements(env, down_cast<jcharArray>(array), is_copy);
break;
case Primitive::kPrimShort:
- result.p = baseEnv(env)->GetShortArrayElements(env, down_cast<jshortArray>(array),
- is_copy);
+ ptr = baseEnv(env)->GetShortArrayElements(env, down_cast<jshortArray>(array), is_copy);
break;
case Primitive::kPrimInt:
- result.p = baseEnv(env)->GetIntArrayElements(env, down_cast<jintArray>(array), is_copy);
+ ptr = baseEnv(env)->GetIntArrayElements(env, down_cast<jintArray>(array), is_copy);
break;
case Primitive::kPrimLong:
- result.p = baseEnv(env)->GetLongArrayElements(env, down_cast<jlongArray>(array),
- is_copy);
+ ptr = baseEnv(env)->GetLongArrayElements(env, down_cast<jlongArray>(array), is_copy);
break;
case Primitive::kPrimFloat:
- result.p = baseEnv(env)->GetFloatArrayElements(env, down_cast<jfloatArray>(array),
- is_copy);
+ ptr = baseEnv(env)->GetFloatArrayElements(env, down_cast<jfloatArray>(array), is_copy);
break;
case Primitive::kPrimDouble:
- result.p = baseEnv(env)->GetDoubleArrayElements(env, down_cast<jdoubleArray>(array),
- is_copy);
+ ptr = baseEnv(env)->GetDoubleArrayElements(env, down_cast<jdoubleArray>(array), is_copy);
break;
default:
LOG(FATAL) << "Unexpected primitive type: " << type;
}
- if (result.p != nullptr && soa.ForceCopy()) {
- result.p = GuardedCopy::CreateGuardedPACopy(env, array, is_copy);
+ if (ptr != nullptr && soa.ForceCopy()) {
+ ptr = GuardedCopy::CreateGuardedPACopy(env, array, is_copy, ptr);
if (is_copy != nullptr) {
*is_copy = JNI_TRUE;
}
}
+ result.p = ptr;
if (sc.Check(soa, false, "p", &result)) {
return const_cast<void*>(result.p);
}
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 57557e2..d428267 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -745,7 +745,7 @@
void Heap::DecrementDisableMovingGC(Thread* self) {
MutexLock mu(self, *gc_complete_lock_);
- CHECK_GE(disable_moving_gc_count_, 0U);
+ CHECK_GT(disable_moving_gc_count_, 0U);
--disable_moving_gc_count_;
}
diff --git a/runtime/thread_linux.cc b/runtime/thread_linux.cc
index 0731f30..9d54eba 100644
--- a/runtime/thread_linux.cc
+++ b/runtime/thread_linux.cc
@@ -36,11 +36,11 @@
}
// The default SIGSTKSZ on linux is 8K. If we do any logging in a signal
-// handler this is too small. We allocate 16K instead or the minimum signal
-// stack size.
+// handler or do a stack unwind, this is too small. We allocate 32K
+// instead of the minimum signal stack size.
// TODO: We shouldn't do logging (with locks) in signal handlers.
static constexpr int kHostAltSigStackSize =
- 16 * KB < MINSIGSTKSZ ? MINSIGSTKSZ : 16 * KB;
+ 32 * KB < MINSIGSTKSZ ? MINSIGSTKSZ : 32 * KB;
void Thread::SetUpAlternateSignalStack() {
// Create and set an alternate signal stack.
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 5436100..bcad9b6 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -3728,6 +3728,7 @@
} else {
const RegType& array_type = work_line_->GetRegisterType(this, inst->VRegB_23x());
if (array_type.IsZero()) {
+ have_pending_runtime_throw_failure_ = true;
// Null array class; this code path will fail at runtime. Infer a merge-able type from the
// instruction type. TODO: have a proper notion of bottom here.
if (!is_primitive || insn_type.IsCategory1Types()) {
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index cd691a3..204b18e 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -244,6 +244,10 @@
bool HasCheckCasts() const;
bool HasVirtualOrInterfaceInvokes() const;
bool HasFailures() const;
+ bool HasInstructionThatWillThrow() const {
+ return have_pending_runtime_throw_failure_;
+ }
+
const RegType& ResolveCheckedClass(uint32_t class_idx)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Returns the method of a quick invoke or null if it cannot be found.
diff --git a/test/401-optimizing-compiler/src/Main.java b/test/401-optimizing-compiler/src/Main.java
index 7c3fd25..a1e62b3 100644
--- a/test/401-optimizing-compiler/src/Main.java
+++ b/test/401-optimizing-compiler/src/Main.java
@@ -110,6 +110,11 @@
if (result != 42) {
throw new Error("Unexpected result: " + result);
}
+
+ String s = $opt$StringInit();
+ if (!s.equals("hello world")) {
+ throw new Error("Unexpected string: " + s);
+ }
}
public static void invokePrivate() {
@@ -230,5 +235,9 @@
return array.length;
}
+ public static String $opt$StringInit() {
+ return new String("hello world");
+ }
+
Object o;
}
diff --git a/test/499-bce-phi-array-length/src/Main.java b/test/499-bce-phi-array-length/src/Main.java
index c8c84a1..e917bc1 100644
--- a/test/499-bce-phi-array-length/src/Main.java
+++ b/test/499-bce-phi-array-length/src/Main.java
@@ -32,11 +32,33 @@
return result;
}
+ public static int bar(int start, int[] array) {
+ int result = 0;
+ for (int i = start; i < 3; i++) {
+ result += array[i];
+ for (int j = 0; j < 2; ++j) {
+ result += array[j];
+ // The following operations would lead to BCE wanting to add another
+ // deoptimization, but it crashed assuming the input of a `HBoundsCheck`
+ // must be a `HArrayLength`.
+ result += array[0];
+ result += array[1];
+ result += array[2];
+ }
+ }
+ return result;
+ }
+
public static void main(String[] args) {
int[] a = new int[] { 1, 2, 3, 4, 5 };
int result = foo(1, a);
if (result != 11) {
throw new Error("Got " + result + ", expected " + 11);
}
+
+ result = bar(1, a);
+ if (result != 35) {
+ throw new Error("Got " + result + ", expected " + 35);
+ }
}
}
diff --git a/test/513-array-deopt/src/Main.java b/test/513-array-deopt/src/Main.java
index a0ae4c3..5ee4d55 100644
--- a/test/513-array-deopt/src/Main.java
+++ b/test/513-array-deopt/src/Main.java
@@ -19,19 +19,36 @@
a[0] = 0;
a[1] = 0;
a[2] = 0;
- // Up to this point, we record that the lower bound is 2.
+ // Up to this point, we record that the lower bound (inclusive) is 3.
// The next instruction will record that the lower bound is 5.
// The deoptimization code used to assume the lower bound has
- // to be check it will add for the deoptimization (here, it
- // would be 2).
+ // to be the one it will add for the deoptimization check (here, it
+ // would be 3).
return new int[a.length - 5];
}
+ public static int[] foo(int[] a) {
+ a[0] = 0;
+ a[1] = 0;
+ a[2] = 0;
+ // Up to this point, we record that the lower bound (inclusive) is 3.
+ // The next instruction will record that the lower bound is 1.
+ // The deoptimization code used to assume the lower bound has
+ // to be the one it will add for the deoptimization check (here, it
+ // would be 3).
+ return new int[a.length - 1];
+ }
+
public static void main(String[] args) {
int[] a = new int[5];
- a = bar(a);
- if (a.length != 0) {
- throw new Error("Expected 0, got " + a.length);
+ int[] result = bar(a);
+ if (result.length != 0) {
+ throw new Error("Expected 0, got " + result.length);
+ }
+
+ result = foo(a);
+ if (result.length != 4) {
+ throw new Error("Expected 5, got " + result.length);
}
}
}
diff --git a/test/515-dce-dominator/expected.txt b/test/515-dce-dominator/expected.txt
new file mode 100644
index 0000000..ccaf6f8
--- /dev/null
+++ b/test/515-dce-dominator/expected.txt
@@ -0,0 +1 @@
+Enter
diff --git a/test/515-dce-dominator/info.txt b/test/515-dce-dominator/info.txt
new file mode 100644
index 0000000..af706e0
--- /dev/null
+++ b/test/515-dce-dominator/info.txt
@@ -0,0 +1,3 @@
+Regression test for the DCE phase of optimizing, where
+we need to recompute the full dominance information of
+the graph when blocks get removed.
diff --git a/test/515-dce-dominator/smali/Dominator.smali b/test/515-dce-dominator/smali/Dominator.smali
new file mode 100644
index 0000000..a504aba
--- /dev/null
+++ b/test/515-dce-dominator/smali/Dominator.smali
@@ -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.
+
+.class public LDominator;
+
+.super Ljava/lang/Object;
+
+.method public static method(I)I
+ .registers 2
+ const/4 v0, 0
+ :b1
+ if-ne v0, v0, :b3
+ :b2
+ if-eq v0, p0, :b4
+ :b5
+ if-eq v0, p0, :b2
+ goto :b6
+ :b4
+ goto :b7
+ :b3
+ goto :b6
+ :b6
+ goto :b7
+ :b7
+ return v1
+.end method
diff --git a/test/515-dce-dominator/src/Main.java b/test/515-dce-dominator/src/Main.java
new file mode 100644
index 0000000..bf9ee25
--- /dev/null
+++ b/test/515-dce-dominator/src/Main.java
@@ -0,0 +1,28 @@
+/*
+ * 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;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ // Workaround for b/18051191.
+ System.out.println("Enter");
+ Class<?> c = Class.forName("Dominator");
+ Method m = c.getMethod("method", int.class);
+ Object[] arguments = { 5 };
+ m.invoke(null, arguments);
+ }
+}
diff --git a/test/516-dead-move-result/expected.txt b/test/516-dead-move-result/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/516-dead-move-result/expected.txt
diff --git a/test/516-dead-move-result/info.txt b/test/516-dead-move-result/info.txt
new file mode 100644
index 0000000..49d9489
--- /dev/null
+++ b/test/516-dead-move-result/info.txt
@@ -0,0 +1,3 @@
+Regression test for the graph builder in optimizing,
+where a move-result was bogus, but it passed the verifier
+because it was dead code.
diff --git a/test/516-dead-move-result/smali/MoveResult.smali b/test/516-dead-move-result/smali/MoveResult.smali
new file mode 100644
index 0000000..9650b58
--- /dev/null
+++ b/test/516-dead-move-result/smali/MoveResult.smali
@@ -0,0 +1,25 @@
+# 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 LMoveResult;
+
+.super Ljava/lang/Object;
+
+.method public static method()V
+ .registers 1
+ goto :b1
+ move-result v0
+ :b1
+ return-void
+.end method
diff --git a/test/516-dead-move-result/src/Main.java b/test/516-dead-move-result/src/Main.java
new file mode 100644
index 0000000..90580a8
--- /dev/null
+++ b/test/516-dead-move-result/src/Main.java
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+public class Main {
+ // Workaround for b/18051191.
+ class InnerClass {}
+
+ public static void main(String[] args) throws Exception {
+ Class<?> c = Class.forName("MoveResult");
+ Method m = c.getMethod("method");
+ Object[] arguments = { };
+ m.invoke(null, arguments);
+ }
+}
diff --git a/test/518-null-array-get/expected.txt b/test/518-null-array-get/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/518-null-array-get/expected.txt
diff --git a/test/518-null-array-get/info.txt b/test/518-null-array-get/info.txt
new file mode 100644
index 0000000..407f590
--- /dev/null
+++ b/test/518-null-array-get/info.txt
@@ -0,0 +1,3 @@
+Regression test for Quick and Optimizing that used
+to crash on an aget-object + int-to-byte sequence
+(accepted by the verifier in the case the array was null).
diff --git a/test/518-null-array-get/smali/NullArray.smali b/test/518-null-array-get/smali/NullArray.smali
new file mode 100644
index 0000000..52abc38
--- /dev/null
+++ b/test/518-null-array-get/smali/NullArray.smali
@@ -0,0 +1,26 @@
+# 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 LNullArray;
+
+.super Ljava/lang/Object;
+
+.method public static method()B
+ .registers 2
+ const/4 v0, 0
+ const/4 v1, 0
+ aget-object v0, v0, v1
+ int-to-byte v0, v0
+ return v0
+.end method
diff --git a/test/518-null-array-get/src/Main.java b/test/518-null-array-get/src/Main.java
new file mode 100644
index 0000000..66e50aa
--- /dev/null
+++ b/test/518-null-array-get/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.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class Main {
+ // Workaround for b/18051191.
+ class InnerClass {}
+
+ public static void main(String[] args) throws Exception {
+ Class<?> c = Class.forName("NullArray");
+ Method m = c.getMethod("method");
+ Object[] arguments = { };
+ try {
+ m.invoke(null, arguments);
+ throw new Error("Expected an InvocationTargetException");
+ } catch (InvocationTargetException e) {
+ if (!(e.getCause() instanceof NullPointerException)) {
+ throw new Error("Expected a NullPointerException");
+ }
+ }
+ }
+}
diff --git a/test/MyClassNatives/MyClassNatives.java b/test/MyClassNatives/MyClassNatives.java
index 8b4a9a4..19c13f7 100644
--- a/test/MyClassNatives/MyClassNatives.java
+++ b/test/MyClassNatives/MyClassNatives.java
@@ -94,6 +94,9 @@
float f4, int i5, float f5, int i6, float f6, int i7, float f7, int i8, float f8, int i9,
float f9, int i10, float f10);
+ native static void stackArgsSignExtendedMips64(int i1, int i2, int i3, int i4, int i5, int i6,
+ int i7, int i8);
+
static native double logD(double d);
static native float logF(float f);
static native boolean returnTrue();