Merge "mem_map_test: perform null check before dereferencing a pointer."
diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc
index 32d7518..4617668 100644
--- a/compiler/dex/quick/dex_file_method_inliner.cc
+++ b/compiler/dex/quick/dex_file_method_inliner.cc
@@ -110,9 +110,9 @@
static_assert(kIntrinsicIsStatic[kIntrinsicAbsFloat], "AbsFloat must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicAbsDouble], "AbsDouble must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicMinMaxInt], "MinMaxInt must be static");
-static_assert(kIntrinsicIsStatic[kIntrinsicMinMaxLong], "MinMaxLong_must_be_static");
-static_assert(kIntrinsicIsStatic[kIntrinsicMinMaxFloat], "MinMaxFloat_must_be_static");
-static_assert(kIntrinsicIsStatic[kIntrinsicMinMaxDouble], "MinMaxDouble_must_be_static");
+static_assert(kIntrinsicIsStatic[kIntrinsicMinMaxLong], "MinMaxLong must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicMinMaxFloat], "MinMaxFloat must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicMinMaxDouble], "MinMaxDouble must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicCos], "Cos must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicSin], "Sin must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicAcos], "Acos must be static");
@@ -153,7 +153,7 @@
static_assert(kIntrinsicIsStatic[kIntrinsicPeek], "Peek must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicPoke], "Poke must be static");
static_assert(!kIntrinsicIsStatic[kIntrinsicCas], "Cas must not be static");
-static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGet], "UnsafeGet_must_not_be_static");
+static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafeGet], "UnsafeGet must not be static");
static_assert(!kIntrinsicIsStatic[kIntrinsicUnsafePut], "UnsafePut must not be static");
static_assert(kIntrinsicIsStatic[kIntrinsicSystemArrayCopyCharArray],
"SystemArrayCopyCharArray must be static");
diff --git a/compiler/dwarf/register.h b/compiler/dwarf/register.h
index b67e8dd..35b3e15 100644
--- a/compiler/dwarf/register.h
+++ b/compiler/dwarf/register.h
@@ -29,7 +29,7 @@
// TODO: Arm S0–S31 register mapping is obsolescent.
// We should use VFP-v3/Neon D0-D31 mapping instead.
// However, D0 is aliased to pair of S0 and S1, so using that
- // mapping we can not easily say S0 is spilled and S1 is not.
+ // mapping we cannot easily say S0 is spilled and S1 is not.
// There are ways around this in DWARF but they are complex.
// It would be much simpler to always spill whole D registers.
// Arm64 mapping is correct since we already do this there.
diff --git a/compiler/elf_writer_debug.cc b/compiler/elf_writer_debug.cc
index 3e64762..e03614f 100644
--- a/compiler/elf_writer_debug.cc
+++ b/compiler/elf_writer_debug.cc
@@ -212,7 +212,7 @@
case kNone:
break;
}
- LOG(FATAL) << "Can not write CIE frame for ISA " << isa;
+ LOG(FATAL) << "Cannot write CIE frame for ISA " << isa;
UNREACHABLE();
}
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 2bf8404..2725792 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -5483,7 +5483,7 @@
case TypeCheckKind::kUnresolvedCheck:
case TypeCheckKind::kInterfaceCheck: {
// Note that we indeed only call on slow path, but we always go
- // into the slow path for the unresolved & interface check
+ // into the slow path for the unresolved and interface check
// cases.
//
// We cannot directly call the InstanceofNonTrivial runtime
@@ -5693,8 +5693,8 @@
case TypeCheckKind::kUnresolvedCheck:
case TypeCheckKind::kInterfaceCheck:
- // We always go into the type check slow path for the unresolved &
- // interface check cases.
+ // We always go into the type check slow path for the unresolved
+ // and interface check cases.
//
// We cannot directly call the CheckCast runtime entry point
// without resorting to a type checking slow path here (i.e. by
@@ -5980,6 +5980,7 @@
new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARM(instruction, root, root);
codegen_->AddSlowPath(slow_path);
+ // IP = Thread::Current()->GetIsGcMarking()
__ LoadFromOffset(
kLoadWord, IP, TR, Thread::IsGcMarkingOffset<kArmWordSize>().Int32Value());
__ CompareAndBranchIfNonZero(IP, slow_path->GetEntryLabel());
@@ -6058,11 +6059,8 @@
// }
//
// Note: the original implementation in ReadBarrier::Barrier is
- // slightly more complex as:
- // - it implements the load-load fence using a data dependency on
- // the high-bits of rb_state, which are expected to be all zeroes;
- // - it performs additional checks that we do not do here for
- // performance reasons.
+ // slightly more complex as it performs additional checks that we do
+ // not do here for performance reasons.
Register ref_reg = ref.AsRegister<Register>();
Register temp_reg = temp.AsRegister<Register>();
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 1183dda..d45ea97 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -442,7 +442,7 @@
// Fast path implementation of ReadBarrier::Barrier for a heap
// reference field load when Baker's read barriers are used.
void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
- Location out,
+ Location ref,
Register obj,
uint32_t offset,
Location temp,
@@ -450,7 +450,7 @@
// Fast path implementation of ReadBarrier::Barrier for a heap
// reference array load when Baker's read barriers are used.
void GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
- Location out,
+ Location ref,
Register obj,
uint32_t data_offset,
Location index,
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index f95cb30..f7ccdd8 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -6009,7 +6009,7 @@
case TypeCheckKind::kUnresolvedCheck:
case TypeCheckKind::kInterfaceCheck: {
// Note that we indeed only call on slow path, but we always go
- // into the slow path for the unresolved & interface check
+ // into the slow path for the unresolved and interface check
// cases.
//
// We cannot directly call the InstanceofNonTrivial runtime
@@ -6240,8 +6240,8 @@
case TypeCheckKind::kUnresolvedCheck:
case TypeCheckKind::kInterfaceCheck:
- // We always go into the type check slow path for the unresolved &
- // interface check cases.
+ // We always go into the type check slow path for the unresolved
+ // and interface check cases.
//
// We cannot directly call the CheckCast runtime entry point
// without resorting to a type checking slow path here (i.e. by
@@ -6520,6 +6520,8 @@
// Plain GC root load with no read barrier.
// /* GcRoot<mirror::Object> */ root = *(obj + offset)
__ movl(root_reg, Address(obj, offset));
+ // Note that GC roots are not affected by heap poisoning, thus we
+ // do not have to unpoison `root_reg` here.
}
}
@@ -6582,7 +6584,9 @@
// Note: the original implementation in ReadBarrier::Barrier is
// slightly more complex as:
// - it implements the load-load fence using a data dependency on
- // the high-bits of rb_state, which are expected to be all zeroes;
+ // the high-bits of rb_state, which are expected to be all zeroes
+ // (we use CodeGeneratorX86::GenerateMemoryBarrier instead here,
+ // which is a no-op thanks to the x86 memory model);
// - it performs additional checks that we do not do here for
// performance reasons.
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 34f983f..43e9543 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -451,7 +451,7 @@
// Fast path implementation of ReadBarrier::Barrier for a heap
// reference field load when Baker's read barriers are used.
void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
- Location out,
+ Location ref,
Register obj,
uint32_t offset,
Location temp,
@@ -459,7 +459,7 @@
// Fast path implementation of ReadBarrier::Barrier for a heap
// reference array load when Baker's read barriers are used.
void GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
- Location out,
+ Location ref,
Register obj,
uint32_t data_offset,
Location index,
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 31f3660..2ce2d91 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -4663,13 +4663,13 @@
bool needs_write_barrier =
CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
- bool may_need_runtime_call = instruction->NeedsTypeCheck();
+ bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
bool object_array_set_with_read_barrier =
kEmitCompilerReadBarrier && (value_type == Primitive::kPrimNot);
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(
instruction,
- (may_need_runtime_call || object_array_set_with_read_barrier) ?
+ (may_need_runtime_call_for_type_check || object_array_set_with_read_barrier) ?
LocationSummary::kCallOnSlowPath :
LocationSummary::kNoCall);
@@ -4698,7 +4698,7 @@
Location index = locations->InAt(1);
Location value = locations->InAt(2);
Primitive::Type value_type = instruction->GetComponentType();
- bool may_need_runtime_call = instruction->NeedsTypeCheck();
+ bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
bool needs_write_barrier =
CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
@@ -4750,7 +4750,7 @@
__ movl(address, Immediate(0));
codegen_->MaybeRecordImplicitNullCheck(instruction);
DCHECK(!needs_write_barrier);
- DCHECK(!may_need_runtime_call);
+ DCHECK(!may_need_runtime_call_for_type_check);
break;
}
@@ -4759,7 +4759,7 @@
NearLabel done, not_null, do_put;
SlowPathCode* slow_path = nullptr;
CpuRegister temp = locations->GetTemp(0).AsRegister<CpuRegister>();
- if (may_need_runtime_call) {
+ if (may_need_runtime_call_for_type_check) {
slow_path = new (GetGraph()->GetArena()) ArraySetSlowPathX86_64(instruction);
codegen_->AddSlowPath(slow_path);
if (instruction->GetValueCanBeNull()) {
@@ -4837,7 +4837,7 @@
} else {
__ movl(address, register_value);
}
- if (!may_need_runtime_call) {
+ if (!may_need_runtime_call_for_type_check) {
codegen_->MaybeRecordImplicitNullCheck(instruction);
}
@@ -5626,7 +5626,7 @@
case TypeCheckKind::kUnresolvedCheck:
case TypeCheckKind::kInterfaceCheck: {
// Note that we indeed only call on slow path, but we always go
- // into the slow path for the unresolved & interface check
+ // into the slow path for the unresolved and interface check
// cases.
//
// We cannot directly call the InstanceofNonTrivial runtime
@@ -5857,8 +5857,8 @@
case TypeCheckKind::kUnresolvedCheck:
case TypeCheckKind::kInterfaceCheck:
- // We always go into the type check slow path for the unresolved &
- // interface check cases.
+ // We always go into the type check slow path for the unresolved
+ // and interface check cases.
//
// We cannot directly call the CheckCast runtime entry point
// without resorting to a type checking slow path here (i.e. by
@@ -6120,6 +6120,8 @@
// Plain GC root load with no read barrier.
// /* GcRoot<mirror::Object> */ root = *(obj + offset)
__ movl(root_reg, Address(obj, offset));
+ // Note that GC roots are not affected by heap poisoning, thus we
+ // do not have to unpoison `root_reg` here.
}
}
@@ -6182,7 +6184,9 @@
// Note: the original implementation in ReadBarrier::Barrier is
// slightly more complex as:
// - it implements the load-load fence using a data dependency on
- // the high-bits of rb_state, which are expected to be all zeroes;
+ // the high-bits of rb_state, which are expected to be all zeroes
+ // (we use CodeGeneratorX86_64::GenerateMemoryBarrier instead
+ // here, which is a no-op thanks to the x86-64 memory model);
// - it performs additional checks that we do not do here for
// performance reasons.
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 9de9d9e..82aabb0 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -400,7 +400,7 @@
// Fast path implementation of ReadBarrier::Barrier for a heap
// reference field load when Baker's read barriers are used.
void GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
- Location out,
+ Location ref,
CpuRegister obj,
uint32_t offset,
Location temp,
@@ -408,7 +408,7 @@
// Fast path implementation of ReadBarrier::Barrier for a heap
// reference array load when Baker's read barriers are used.
void GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
- Location out,
+ Location ref,
CpuRegister obj,
uint32_t data_offset,
Location index,
diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc
index 86a695b..e170e37 100644
--- a/compiler/optimizing/dead_code_elimination.cc
+++ b/compiler/optimizing/dead_code_elimination.cc
@@ -89,15 +89,18 @@
}
void HDeadCodeElimination::RemoveDeadBlocks() {
+ if (graph_->HasIrreducibleLoops()) {
+ // Do not eliminate dead blocks if the graph has irreducible loops. We could
+ // support it, but that would require changes in our loop representation to handle
+ // multiple entry points. We decided it was not worth the complexity.
+ return;
+ }
// Classify blocks as reachable/unreachable.
ArenaAllocator* allocator = graph_->GetArena();
ArenaBitVector live_blocks(allocator, graph_->GetBlocks().size(), false);
MarkReachableBlocks(graph_, &live_blocks);
bool removed_one_or_more_blocks = false;
- // If the graph has irreducible loops we need to reset all graph analysis we have done
- // before: the irreducible loop can be turned into a reducible one.
- // For simplicity, we do the full computation regardless of the type of the loops.
bool rerun_dominance_and_loop_analysis = false;
// Remove all dead blocks. Iterate in post order because removal needs the
@@ -105,9 +108,6 @@
// inside out.
for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
HBasicBlock* block = it.Current();
- if (block->IsLoopHeader() && block->GetLoopInformation()->IsIrreducible()) {
- rerun_dominance_and_loop_analysis = true;
- }
int id = block->GetBlockId();
if (!live_blocks.IsBitSet(id)) {
MaybeRecordDeadBlock(block);
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 9439ba0..3113677 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -484,6 +484,18 @@
loop_information->GetPreHeader()->GetSuccessors().size()));
}
+ if (loop_information->GetSuspendCheck() == nullptr) {
+ AddError(StringPrintf(
+ "Loop with header %d does not have a suspend check.",
+ loop_header->GetBlockId()));
+ }
+
+ if (loop_information->GetSuspendCheck() != loop_header->GetFirstInstructionDisregardMoves()) {
+ AddError(StringPrintf(
+ "Loop header %d does not have the loop suspend check as the first instruction.",
+ loop_header->GetBlockId()));
+ }
+
// Ensure the loop header has only one incoming branch and the remaining
// predecessors are back edges.
size_t num_preds = loop_header->GetPredecessors().size();
@@ -589,6 +601,14 @@
}
}
+ if (instruction->NeedsEnvironment() && !instruction->HasEnvironment()) {
+ AddError(StringPrintf("Instruction %s:%d in block %d requires an environment "
+ "but does not have one.",
+ instruction->DebugName(),
+ instruction->GetId(),
+ current_block_->GetBlockId()));
+ }
+
// Ensure an instruction having an environment is dominated by the
// instructions contained in the environment.
for (HEnvironment* environment = instruction->GetEnvironment();
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 854d92a..2eabadf 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -167,11 +167,7 @@
void HGraph::ClearLoopInformation() {
SetHasIrreducibleLoops(false);
for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) {
- HBasicBlock* current = it.Current();
- if (current->IsLoopHeader()) {
- current->RemoveInstruction(current->GetLoopInformation()->GetSuspendCheck());
- }
- current->SetLoopInformation(nullptr);
+ it.Current()->SetLoopInformation(nullptr);
}
}
@@ -180,6 +176,14 @@
dominator_ = nullptr;
}
+HInstruction* HBasicBlock::GetFirstInstructionDisregardMoves() const {
+ HInstruction* instruction = GetFirstInstruction();
+ while (instruction->IsParallelMove()) {
+ instruction = instruction->GetNext();
+ }
+ return instruction;
+}
+
void HGraph::ComputeDominanceInformation() {
DCHECK(reverse_post_order_.empty());
reverse_post_order_.reserve(blocks_.size());
@@ -457,6 +461,10 @@
}
if (block->IsLoopHeader()) {
SimplifyLoop(block);
+ } else if (!block->IsEntryBlock() && block->GetFirstInstruction()->IsSuspendCheck()) {
+ // We are being called by the dead code elimiation pass, and what used to be
+ // a loop got dismantled. Just remove the suspend check.
+ block->RemoveInstruction(block->GetFirstInstruction());
}
}
}
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 859d570..e222ef7 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -860,6 +860,8 @@
HInstruction* GetLastPhi() const { return phis_.last_instruction_; }
const HInstructionList& GetPhis() const { return phis_; }
+ HInstruction* GetFirstInstructionDisregardMoves() const;
+
void AddSuccessor(HBasicBlock* block) {
successors_.push_back(block);
block->predecessors_.push_back(this);
diff --git a/compiler/optimizing/parallel_move_resolver.cc b/compiler/optimizing/parallel_move_resolver.cc
index 9d136f3..be470cc 100644
--- a/compiler/optimizing/parallel_move_resolver.cc
+++ b/compiler/optimizing/parallel_move_resolver.cc
@@ -504,7 +504,7 @@
void ParallelMoveResolverNoSwap::UpdateMoveSource(Location from, Location to) {
// This function is used to reduce the dependencies in the graph after
// (from -> to) has been performed. Since we ensure there is no move with the same
- // destination, (to -> X) can not be blocked while (from -> X) might still be
+ // destination, (to -> X) cannot be blocked while (from -> X) might still be
// blocked. Consider for example the moves (0 -> 1) (1 -> 2) (1 -> 3). After
// (1 -> 2) has been performed, the moves left are (0 -> 1) and (1 -> 3). There is
// a dependency between the two. If we update the source location from 1 to 2, we
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index 7494e33..c0011cd 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -422,6 +422,34 @@
return true;
}
+void SsaBuilder::RemoveRedundantUninitializedStrings() {
+ if (GetGraph()->IsDebuggable()) {
+ // Do not perform the optimization for consistency with the interpreter
+ // which always allocates an object for new-instance of String.
+ return;
+ }
+
+ for (HNewInstance* new_instance : uninitialized_strings_) {
+ DCHECK(new_instance->IsStringAlloc());
+
+ // Replace NewInstance of String with NullConstant if not used prior to
+ // calling StringFactory. In case of deoptimization, the interpreter is
+ // expected to skip null check on the `this` argument of the StringFactory call.
+ if (!new_instance->HasNonEnvironmentUses()) {
+ new_instance->ReplaceWith(GetGraph()->GetNullConstant());
+ new_instance->GetBlock()->RemoveInstruction(new_instance);
+
+ // Remove LoadClass if not needed any more.
+ HLoadClass* load_class = new_instance->InputAt(0)->AsLoadClass();
+ DCHECK(load_class != nullptr);
+ DCHECK(!load_class->NeedsAccessCheck()) << "String class is always accessible";
+ if (!load_class->HasUses()) {
+ load_class->GetBlock()->RemoveInstruction(load_class);
+ }
+ }
+ }
+}
+
GraphAnalysisResult SsaBuilder::BuildSsa() {
// 1) Visit in reverse post order. We need to have all predecessors of a block
// visited (with the exception of loops) in order to create the right environment
@@ -487,7 +515,15 @@
// input types.
dead_phi_elimimation.EliminateDeadPhis();
- // 11) Clear locals.
+ // 11) Step 1) replaced uses of NewInstances of String with the results of
+ // their corresponding StringFactory calls. Unless the String objects are used
+ // before they are initialized, they can be replaced with NullConstant.
+ // Note that this optimization is valid only if unsimplified code does not use
+ // the uninitialized value because we assume execution can be deoptimized at
+ // any safepoint. We must therefore perform it before any other optimizations.
+ RemoveRedundantUninitializedStrings();
+
+ // 12) Clear locals.
for (HInstructionIterator it(GetGraph()->GetEntryBlock()->GetInstructions());
!it.Done();
it.Advance()) {
@@ -894,6 +930,10 @@
HNewInstance* new_instance = invoke->GetThisArgumentOfStringInit();
invoke->RemoveThisArgumentOfStringInit();
+ // Replacing the NewInstance might render it redundant. Keep a list of these
+ // to be visited once it is clear whether it is has remaining uses.
+ uninitialized_strings_.push_back(new_instance);
+
// Walk over all vregs and replace any occurrence of `new_instance` with `invoke`.
for (size_t vreg = 0, e = current_locals_->size(); vreg < e; ++vreg) {
if ((*current_locals_)[vreg] == new_instance) {
diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h
index 28eef6a..ccef8ea 100644
--- a/compiler/optimizing/ssa_builder.h
+++ b/compiler/optimizing/ssa_builder.h
@@ -57,6 +57,7 @@
loop_headers_(graph->GetArena()->Adapter(kArenaAllocSsaBuilder)),
ambiguous_agets_(graph->GetArena()->Adapter(kArenaAllocSsaBuilder)),
ambiguous_asets_(graph->GetArena()->Adapter(kArenaAllocSsaBuilder)),
+ uninitialized_strings_(graph->GetArena()->Adapter(kArenaAllocSsaBuilder)),
locals_for_(graph->GetBlocks().size(),
ArenaVector<HInstruction*>(graph->GetArena()->Adapter(kArenaAllocSsaBuilder)),
graph->GetArena()->Adapter(kArenaAllocSsaBuilder)) {
@@ -105,6 +106,8 @@
HPhi* GetFloatDoubleOrReferenceEquivalentOfPhi(HPhi* phi, Primitive::Type type);
HArrayGet* GetFloatOrDoubleEquivalentOfArrayGet(HArrayGet* aget);
+ void RemoveRedundantUninitializedStrings();
+
StackHandleScopeCollection* const handles_;
// True if types of ambiguous ArrayGets have been resolved.
@@ -119,6 +122,7 @@
ArenaVector<HArrayGet*> ambiguous_agets_;
ArenaVector<HArraySet*> ambiguous_asets_;
+ ArenaVector<HNewInstance*> uninitialized_strings_;
// HEnvironment for each block.
ArenaVector<ArenaVector<HInstruction*>> locals_for_;
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index e89c5af..3df9101 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -232,7 +232,7 @@
}
if (founddir.empty()) {
- ADD_FAILURE() << "Can not find Android tools directory.";
+ ADD_FAILURE() << "Cannot find Android tools directory.";
}
return founddir;
}
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 8a3db6c..108b8d2 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -1094,11 +1094,11 @@
int32_t GetLineNumFromPC(ArtMethod* method, uint32_t rel_pc) const
SHARED_REQUIRES(Locks::mutator_lock_);
- // Returns false if there is no debugging information or if it can not be decoded.
+ // Returns false if there is no debugging information or if it cannot be decoded.
bool DecodeDebugLocalInfo(const CodeItem* code_item, bool is_static, uint32_t method_idx,
DexDebugNewLocalCb local_cb, void* context) const;
- // Returns false if there is no debugging information or if it can not be decoded.
+ // Returns false if there is no debugging information or if it cannot be decoded.
bool DecodeDebugPositionInfo(const CodeItem* code_item, DexDebugNewPositionCb position_cb,
void* context) const;
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 9a9f42b..0663b7e 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -193,10 +193,10 @@
return nullptr;
}
gc::Heap* heap = Runtime::Current()->GetHeap();
- // Pass in false since the object can not be finalizable.
+ // Pass in false since the object cannot be finalizable.
return klass->Alloc<kInstrumented, false>(self, heap->GetCurrentAllocator());
}
- // Pass in false since the object can not be finalizable.
+ // Pass in false since the object cannot be finalizable.
return klass->Alloc<kInstrumented, false>(self, allocator_type);
}
@@ -207,7 +207,7 @@
Thread* self,
gc::AllocatorType allocator_type) {
DCHECK(klass != nullptr);
- // Pass in false since the object can not be finalizable.
+ // Pass in false since the object cannot be finalizable.
return klass->Alloc<kInstrumented, false>(self, allocator_type);
}
@@ -410,10 +410,19 @@
DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
return nullptr; // Failure.
} else if (UNLIKELY(*this_object == nullptr && type != kStatic)) {
- // Maintain interpreter-like semantics where NullPointerException is thrown
- // after potential NoSuchMethodError from class linker.
- ThrowNullPointerExceptionForMethodAccess(method_idx, type);
- return nullptr; // Failure.
+ if (UNLIKELY(resolved_method->GetDeclaringClass()->IsStringClass() &&
+ resolved_method->IsConstructor())) {
+ // Hack for String init:
+ //
+ // We assume that the input of String.<init> in verified code is always
+ // an unitialized reference. If it is a null constant, it must have been
+ // optimized out by the compiler. Do not throw NullPointerException.
+ } else {
+ // Maintain interpreter-like semantics where NullPointerException is thrown
+ // after potential NoSuchMethodError from class linker.
+ ThrowNullPointerExceptionForMethodAccess(method_idx, type);
+ return nullptr; // Failure.
+ }
} else if (access_check) {
mirror::Class* methods_class = resolved_method->GetDeclaringClass();
bool can_access_resolved_method =
diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc
index ce6467a..7727b2d 100644
--- a/runtime/gc/collector/mark_compact.cc
+++ b/runtime/gc/collector/mark_compact.cc
@@ -180,7 +180,7 @@
t.NewTiming("ProcessCards");
// Process dirty cards and add dirty cards to mod-union tables.
heap_->ProcessCards(GetTimings(), false, false, true);
- // Clear the whole card table since we can not Get any additional dirty cards during the
+ // Clear the whole card table since we cannot get any additional dirty cards during the
// paused GC. This saves memory but only works for pause the world collectors.
t.NewTiming("ClearCardTable");
heap_->GetCardTable()->ClearCardTable();
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index 99e98bb..2784693 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -227,7 +227,7 @@
BindBitmaps();
// Process dirty cards and add dirty cards to mod-union tables.
heap_->ProcessCards(GetTimings(), kUseRememberedSet && generational_, false, true);
- // Clear the whole card table since we can not Get any additional dirty cards during the
+ // Clear the whole card table since we cannot get any additional dirty cards during the
// paused GC. This saves memory but only works for pause the world collectors.
t.NewTiming("ClearCardTable");
heap_->GetCardTable()->ClearCardTable();
diff --git a/runtime/gc/collector_type.h b/runtime/gc/collector_type.h
index 416510d..c8e913c 100644
--- a/runtime/gc/collector_type.h
+++ b/runtime/gc/collector_type.h
@@ -34,7 +34,7 @@
kCollectorTypeSS,
// A generational variant of kCollectorTypeSS.
kCollectorTypeGSS,
- // Mark compact colector.
+ // Mark compact collector.
kCollectorTypeMC,
// Heap trimming collector, doesn't do any actual collecting.
kCollectorTypeHeapTrim,
diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc
index 39ba743..5e7f1a2 100644
--- a/runtime/gc/reference_processor.cc
+++ b/runtime/gc/reference_processor.cc
@@ -86,7 +86,7 @@
// it to the mutator as long as the GC is not preserving references.
if (LIKELY(collector_ != nullptr)) {
// If it's null it means not marked, but it could become marked if the referent is reachable
- // by finalizer referents. So we can not return in this case and must block. Otherwise, we
+ // by finalizer referents. So we cannot return in this case and must block. Otherwise, we
// can return it to the mutator as long as the GC is not preserving references, in which
// case only black nodes can be safely returned. If the GC is preserving references, the
// mutator could take a white field from a grey or white node and move it somewhere else
diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc
index 2798b21..e70fe21 100644
--- a/runtime/gc/space/large_object_space.cc
+++ b/runtime/gc/space/large_object_space.cc
@@ -521,7 +521,7 @@
num_bytes_allocated_ += allocation_size;
total_bytes_allocated_ += allocation_size;
mirror::Object* obj = reinterpret_cast<mirror::Object*>(GetAddressForAllocationInfo(new_info));
- // We always put our object at the start of the free block, there can not be another free block
+ // We always put our object at the start of the free block, there cannot be another free block
// before it.
if (kIsDebugBuild) {
mprotect(obj, allocation_size, PROT_READ | PROT_WRITE);
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 7d60264..7d55e8c 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -931,7 +931,7 @@
ArtMethod* caller,
uint32_t dex_pc,
ArtMethod* callee) const {
- // We can not have thread suspension since that would cause the this_object parameter to
+ // We cannot have thread suspension since that would cause the this_object parameter to
// potentially become a dangling pointer. An alternative could be to put it in a handle instead.
ScopedAssertNoThreadSuspension ants(thread, __FUNCTION__);
for (InstrumentationListener* listener : invoke_virtual_or_interface_listeners_) {
diff --git a/runtime/intern_table.h b/runtime/intern_table.h
index 8f715a3..2b2176e 100644
--- a/runtime/intern_table.h
+++ b/runtime/intern_table.h
@@ -61,7 +61,7 @@
SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
// Only used by image writer. Special version that may not cause thread suspension since the GC
- // can not be running while we are doing image writing. Maybe be called while while holding a
+ // cannot be running while we are doing image writing. Maybe be called while while holding a
// lock since there will not be thread suspension.
mirror::String* InternStrongImageString(mirror::String* s)
SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 18fb0d8..ecd4de9 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -592,6 +592,10 @@
//
// (at this point the ArtMethod has already been replaced,
// so we just need to fix-up the arguments)
+ //
+ // Note that FindMethodFromCode in entrypoint_utils-inl.h was also special-cased
+ // to handle the compiler optimization of replacing `this` with null without
+ // throwing NullPointerException.
uint32_t string_init_vreg_this = is_range ? vregC : arg[0];
if (UNLIKELY(string_init)) {
DCHECK_GT(num_regs, 0u); // As the method is an instance method, there should be at least 1.
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 83e594b..d2f563b 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -627,7 +627,7 @@
if (dl_iterate_phdr(dl_iterate_context::callback, &context) == 0) {
PrintFileToLog("/proc/self/maps", LogSeverity::WARNING);
- LOG(ERROR) << "File " << elf_filename << " loaded with dlopen but can not find its mmaps.";
+ LOG(ERROR) << "File " << elf_filename << " loaded with dlopen but cannot find its mmaps.";
}
#endif
}
diff --git a/runtime/prebuilt_tools_test.cc b/runtime/prebuilt_tools_test.cc
index a7f7bcd..eb226d4 100644
--- a/runtime/prebuilt_tools_test.cc
+++ b/runtime/prebuilt_tools_test.cc
@@ -34,7 +34,7 @@
struct stat exec_st;
std::string exec_path = tools_dir + tool;
if (stat(exec_path.c_str(), &exec_st) != 0) {
- ADD_FAILURE() << "Can not find " << tool << " in " << tools_dir;
+ ADD_FAILURE() << "Cannot find " << tool << " in " << tools_dir;
}
}
}
@@ -42,7 +42,7 @@
TEST_F(PrebuiltToolsTest, CheckHostTools) {
std::string tools_dir = GetAndroidHostToolsDir();
if (tools_dir.empty()) {
- ADD_FAILURE() << "Can not find Android tools directory for host";
+ ADD_FAILURE() << "Cannot find Android tools directory for host";
} else {
CheckToolsExist(tools_dir);
}
@@ -54,7 +54,7 @@
for (InstructionSet isa : isas) {
std::string tools_dir = GetAndroidTargetToolsDir(isa);
if (tools_dir.empty()) {
- ADD_FAILURE() << "Can not find Android tools directory for " << isa;
+ ADD_FAILURE() << "Cannot find Android tools directory for " << isa;
} else {
CheckToolsExist(tools_dir);
}
diff --git a/runtime/utf_test.cc b/runtime/utf_test.cc
index 5239e40..c67879b 100644
--- a/runtime/utf_test.cc
+++ b/runtime/utf_test.cc
@@ -353,7 +353,7 @@
if (codePoint <= 0xffff) {
if (codePoint >= 0xd800 && codePoint <= 0xdfff) {
// According to the Unicode standard, no character will ever
- // be assigned to these code points, and they can not be encoded
+ // be assigned to these code points, and they cannot be encoded
// into either utf-16 or utf-8.
continue;
}
diff --git a/test/127-checker-secondarydex/src/Test.java b/test/127-checker-secondarydex/src/Test.java
index ae8bac9..266ed19 100644
--- a/test/127-checker-secondarydex/src/Test.java
+++ b/test/127-checker-secondarydex/src/Test.java
@@ -23,8 +23,12 @@
System.out.println("Test");
}
- /// CHECK-START: java.lang.String Test.toString() ssa_builder (after)
- /// CHECK: LoadClass needs_access_check:false klass:java.lang.String
+ /// CHECK-START: java.lang.Integer Test.toInteger() ssa_builder (after)
+ /// CHECK: LoadClass needs_access_check:false klass:java.lang.Integer
+
+ public Integer toInteger() {
+ return new Integer(42);
+ }
public String toString() {
return new String("Test");
diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc
index 9bfe429..77301d2 100644
--- a/test/137-cfi/cfi.cc
+++ b/test/137-cfi/cfi.cc
@@ -76,7 +76,7 @@
}
}
- printf("Can not find %s in backtrace:\n", seq[cur_search_index].c_str());
+ printf("Cannot find %s in backtrace:\n", seq[cur_search_index].c_str());
for (Backtrace::const_iterator it = bt->begin(); it != bt->end(); ++it) {
if (BacktraceMap::IsValid(it->map)) {
printf(" %s\n", it->func_name.c_str());
@@ -112,7 +112,7 @@
std::unique_ptr<Backtrace> bt(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, GetTid()));
if (!bt->Unwind(0, nullptr)) {
- printf("Can not unwind in process.\n");
+ printf("Cannot unwind in process.\n");
return JNI_FALSE;
} else if (bt->NumFrames() == 0) {
printf("No frames for unwind in process.\n");
@@ -205,7 +205,7 @@
std::unique_ptr<Backtrace> bt(Backtrace::Create(pid, BACKTRACE_CURRENT_THREAD));
bool result = true;
if (!bt->Unwind(0, nullptr)) {
- printf("Can not unwind other process.\n");
+ printf("Cannot unwind other process.\n");
result = false;
} else if (bt->NumFrames() == 0) {
printf("No frames for unwind of other process.\n");
diff --git a/test/141-class-unload/src/Main.java b/test/141-class-unload/src/Main.java
index 0640b36..bcb697a 100644
--- a/test/141-class-unload/src/Main.java
+++ b/test/141-class-unload/src/Main.java
@@ -79,7 +79,7 @@
private static void testUnloadClass(Constructor constructor) throws Exception {
WeakReference<Class> klass = setUpUnloadClass(constructor);
- // No strong refernces to class loader, should get unloaded.
+ // No strong references to class loader, should get unloaded.
Runtime.getRuntime().gc();
WeakReference<Class> klass2 = setUpUnloadClass(constructor);
Runtime.getRuntime().gc();
@@ -91,7 +91,7 @@
private static void testUnloadLoader(Constructor constructor)
throws Exception {
WeakReference<ClassLoader> loader = setUpUnloadLoader(constructor, true);
- // No strong refernces to class loader, should get unloaded.
+ // No strong references to class loader, should get unloaded.
Runtime.getRuntime().gc();
// If the weak reference is cleared, then it was unloaded.
System.out.println(loader.get());
@@ -109,7 +109,7 @@
private static void testLoadAndUnloadLibrary(Constructor constructor) throws Exception {
WeakReference<ClassLoader> loader = setUpLoadLibrary(constructor);
- // No strong refernces to class loader, should get unloaded.
+ // No strong references to class loader, should get unloaded.
Runtime.getRuntime().gc();
// If the weak reference is cleared, then it was unloaded.
System.out.println(loader.get());
diff --git a/test/559-checker-irreducible-loop/smali/IrreducibleLoop.smali b/test/559-checker-irreducible-loop/smali/IrreducibleLoop.smali
index 30a648d..971ad84 100644
--- a/test/559-checker-irreducible-loop/smali/IrreducibleLoop.smali
+++ b/test/559-checker-irreducible-loop/smali/IrreducibleLoop.smali
@@ -91,9 +91,7 @@
goto :other_loop_entry
.end method
-# Check that if a irreducible loop entry is dead, the loop can become
-# natural.
-# We start with:
+# Check that dce does not apply for irreducible loops.
#
# entry
# / \
@@ -106,18 +104,8 @@
## CHECK-START: int IrreducibleLoop.dce(int) dead_code_elimination (before)
## CHECK: irreducible:true
-# And end with:
-#
-# entry
-# /
-# /
-# loop_entry
-# / \-
-# exit \-
-# other_loop_entry
-
## CHECK-START: int IrreducibleLoop.dce(int) dead_code_elimination (after)
-## CHECK-NOT: irreducible:true
+## CHECK: irreducible:true
.method public static dce(I)I
.registers 3
const/16 v0, 42
diff --git a/test/563-checker-fakestring/smali/TestCase.smali b/test/563-checker-fakestring/smali/TestCase.smali
index cd52f3d..4bd804d 100644
--- a/test/563-checker-fakestring/smali/TestCase.smali
+++ b/test/563-checker-fakestring/smali/TestCase.smali
@@ -64,9 +64,15 @@
.end method
-# Test deoptimization between String's allocation and initialization.
+# Test deoptimization between String's allocation and initialization. When not
+# compiling --debuggable, the NewInstance will be optimized out.
## CHECK-START: int TestCase.deoptimizeNewInstance(int[], byte[]) register (after)
+## CHECK: <<Null:l\d+>> NullConstant
+## CHECK: Deoptimize env:[[<<Null>>,{{.*]]}}
+## CHECK: InvokeStaticOrDirect method_name:java.lang.String.<init>
+
+## CHECK-START-DEBUGGABLE: int TestCase.deoptimizeNewInstance(int[], byte[]) register (after)
## CHECK: <<String:l\d+>> NewInstance
## CHECK: Deoptimize env:[[<<String>>,{{.*]]}}
## CHECK: InvokeStaticOrDirect method_name:java.lang.String.<init>
@@ -98,3 +104,23 @@
return v2
.end method
+
+# Test that a redundant NewInstance is removed if not used and not compiling
+# --debuggable.
+
+## CHECK-START: java.lang.String TestCase.removeNewInstance(byte[]) register (after)
+## CHECK-NOT: NewInstance
+## CHECK-NOT: LoadClass
+
+## CHECK-START-DEBUGGABLE: java.lang.String TestCase.removeNewInstance(byte[]) register (after)
+## CHECK: NewInstance
+
+.method public static removeNewInstance([B)Ljava/lang/String;
+ .registers 5
+
+ new-instance v0, Ljava/lang/String;
+ const-string v1, "UTF8"
+ invoke-direct {v0, p0, v1}, Ljava/lang/String;-><init>([BLjava/lang/String;)V
+ return-object v0
+
+.end method
diff --git a/test/563-checker-fakestring/src/Main.java b/test/563-checker-fakestring/src/Main.java
index 750f718..04df0f6 100644
--- a/test/563-checker-fakestring/src/Main.java
+++ b/test/563-checker-fakestring/src/Main.java
@@ -57,5 +57,11 @@
}
}
}
+
+ {
+ Method m = c.getMethod("removeNewInstance", byte[].class);
+ String result = (String) m.invoke(null, new Object[] { testData });
+ assertEqual(testString, result);
+ }
}
}