Merge "Fix rosalloc issues with valgrind."
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index eb4915b..6f9dd6d 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -1679,9 +1679,7 @@
if (opcode == Instruction::NEW_INSTANCE) {
uint32_t type_idx = mir->dalvikInsn.vB;
if (cu_->compiler_driver->IsStringTypeIndex(type_idx, cu_->dex_file)) {
- // Change NEW_INSTANCE into CONST_4 of 0
- mir->dalvikInsn.opcode = Instruction::CONST_4;
- mir->dalvikInsn.vB = 0;
+ LOG(FATAL) << "Quick cannot compile String allocations";
}
} else if ((opcode == Instruction::INVOKE_DIRECT) ||
(opcode == Instruction::INVOKE_DIRECT_RANGE)) {
@@ -1689,52 +1687,13 @@
DexFileMethodInliner* inliner =
cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file);
if (inliner->IsStringInitMethodIndex(method_idx)) {
- bool is_range = (opcode == Instruction::INVOKE_DIRECT_RANGE);
- uint32_t orig_this_reg = is_range ? mir->dalvikInsn.vC : mir->dalvikInsn.arg[0];
- // Remove this pointer from string init and change to static call.
- mir->dalvikInsn.vA--;
- if (!is_range) {
- mir->dalvikInsn.opcode = Instruction::INVOKE_STATIC;
- for (uint32_t i = 0; i < mir->dalvikInsn.vA; i++) {
- mir->dalvikInsn.arg[i] = mir->dalvikInsn.arg[i + 1];
- }
- } else {
- mir->dalvikInsn.opcode = Instruction::INVOKE_STATIC_RANGE;
- mir->dalvikInsn.vC++;
- }
- // Insert a move-result instruction to the original this pointer reg.
- MIR* move_result_mir = static_cast<MIR *>(arena_->Alloc(sizeof(MIR), kArenaAllocMIR));
- move_result_mir->dalvikInsn.opcode = Instruction::MOVE_RESULT_OBJECT;
- move_result_mir->dalvikInsn.vA = orig_this_reg;
- move_result_mir->offset = mir->offset;
- move_result_mir->m_unit_index = mir->m_unit_index;
- bb->InsertMIRAfter(mir, move_result_mir);
- // Add additional moves if this pointer was copied to other registers.
- const VerifiedMethod* verified_method =
- cu_->compiler_driver->GetVerifiedMethod(cu_->dex_file, cu_->method_idx);
- DCHECK(verified_method != nullptr);
- const SafeMap<uint32_t, std::set<uint32_t>>& string_init_map =
- verified_method->GetStringInitPcRegMap();
- auto map_it = string_init_map.find(mir->offset);
- if (map_it != string_init_map.end()) {
- const std::set<uint32_t>& reg_set = map_it->second;
- for (auto set_it = reg_set.begin(); set_it != reg_set.end(); ++set_it) {
- MIR* move_mir = static_cast<MIR *>(arena_->Alloc(sizeof(MIR), kArenaAllocMIR));
- move_mir->dalvikInsn.opcode = Instruction::MOVE_OBJECT;
- move_mir->dalvikInsn.vA = *set_it;
- move_mir->dalvikInsn.vB = orig_this_reg;
- move_mir->offset = mir->offset;
- move_mir->m_unit_index = mir->m_unit_index;
- bb->InsertMIRAfter(move_result_mir, move_mir);
- }
- }
+ LOG(FATAL) << "Quick cannot compile String allocations";
}
}
}
}
}
-
bool MIRGraph::EliminateSuspendChecksGate() {
if (kLeafOptimization || // Incompatible (could create loops without suspend checks).
(cu_->disable_opt & (1 << kSuspendCheckElimination)) != 0 || // Disabled.
diff --git a/compiler/dex/quick/quick_compiler.cc b/compiler/dex/quick/quick_compiler.cc
index 027290f..49768de 100644
--- a/compiler/dex/quick/quick_compiler.cc
+++ b/compiler/dex/quick/quick_compiler.cc
@@ -509,7 +509,8 @@
}
bool QuickCompiler::CanCompileInstruction(const MIR* mir,
- const DexFile& dex_file) const {
+ const DexFile& dex_file,
+ CompilationUnit* cu) const {
switch (mir->dalvikInsn.opcode) {
// Quick compiler won't support new instruction semantics to invoke-super into an interface
// method
@@ -522,6 +523,13 @@
// False if we are an interface i.e. !(java_access_flags & kAccInterface)
return class_def != nullptr && ((class_def->GetJavaAccessFlags() & kAccInterface) == 0);
}
+ case Instruction::NEW_INSTANCE: {
+ uint32_t type_idx = mir->dalvikInsn.vB;
+ if (cu->compiler_driver->IsStringTypeIndex(type_idx, cu->dex_file)) {
+ return false;
+ }
+ return true;
+ }
default:
return true;
}
@@ -567,7 +575,7 @@
<< MIRGraph::extended_mir_op_names_[opcode - kMirOpFirst];
}
return false;
- } else if (!CanCompileInstruction(mir, dex_file)) {
+ } else if (!CanCompileInstruction(mir, dex_file, cu)) {
VLOG(compiler) << "Cannot compile dalvik opcode : " << mir->dalvikInsn.opcode;
return false;
}
diff --git a/compiler/dex/quick/quick_compiler.h b/compiler/dex/quick/quick_compiler.h
index 55f45f1..f32cf86 100644
--- a/compiler/dex/quick/quick_compiler.h
+++ b/compiler/dex/quick/quick_compiler.h
@@ -75,7 +75,7 @@
explicit QuickCompiler(CompilerDriver* driver);
private:
- bool CanCompileInstruction(const MIR* mir, const DexFile& dex_file) const;
+ bool CanCompileInstruction(const MIR* mir, const DexFile& dex_file, CompilationUnit* cu) const;
std::unique_ptr<PassManager> pre_opt_pass_manager_;
std::unique_ptr<PassManager> post_opt_pass_manager_;
diff --git a/compiler/dex/verified_method.cc b/compiler/dex/verified_method.cc
index 0355f11..9ae2164 100644
--- a/compiler/dex/verified_method.cc
+++ b/compiler/dex/verified_method.cc
@@ -37,20 +37,16 @@
namespace art {
-VerifiedMethod::VerifiedMethod(uint32_t encountered_error_types,
- bool has_runtime_throw,
- const SafeMap<uint32_t, std::set<uint32_t>>& string_init_pc_reg_map)
+VerifiedMethod::VerifiedMethod(uint32_t encountered_error_types, bool has_runtime_throw)
: encountered_error_types_(encountered_error_types),
- has_runtime_throw_(has_runtime_throw),
- string_init_pc_reg_map_(string_init_pc_reg_map) {
+ has_runtime_throw_(has_runtime_throw) {
}
const VerifiedMethod* VerifiedMethod::Create(verifier::MethodVerifier* method_verifier,
bool compile) {
std::unique_ptr<VerifiedMethod> verified_method(
new VerifiedMethod(method_verifier->GetEncounteredFailureTypes(),
- method_verifier->HasInstructionThatWillThrow(),
- method_verifier->GetStringInitPcRegMap()));
+ method_verifier->HasInstructionThatWillThrow()));
if (compile) {
/* Generate a register map. */
diff --git a/compiler/dex/verified_method.h b/compiler/dex/verified_method.h
index 74fcb07..12d0219 100644
--- a/compiler/dex/verified_method.h
+++ b/compiler/dex/verified_method.h
@@ -83,14 +83,8 @@
return has_runtime_throw_;
}
- const SafeMap<uint32_t, std::set<uint32_t>>& GetStringInitPcRegMap() const {
- return string_init_pc_reg_map_;
- }
-
private:
- VerifiedMethod(uint32_t encountered_error_types,
- bool has_runtime_throw,
- const SafeMap<uint32_t, std::set<uint32_t>>& string_init_pc_reg_map);
+ VerifiedMethod(uint32_t encountered_error_types, bool has_runtime_throw);
/*
* Generate the GC map for a method that has just been verified (i.e. we're doing this as part of
@@ -129,10 +123,6 @@
const uint32_t encountered_error_types_;
const bool has_runtime_throw_;
-
- // 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.
- const SafeMap<uint32_t, std::set<uint32_t>> string_init_pc_reg_map_;
};
} // namespace art
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index 0d65bc7..3cb63e7 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -186,13 +186,7 @@
} else {
// Search dex file for localized ssb index, may fail if member's class is a parent
// of the class mentioned in the dex file and there is no dex cache entry.
- std::string temp;
- const DexFile::TypeId* type_id =
- dex_file->FindTypeId(resolved_member->GetDeclaringClass()->GetDescriptor(&temp));
- if (type_id != nullptr) {
- // medium path, needs check of static storage base being initialized
- storage_idx = dex_file->GetIndexForTypeId(*type_id);
- }
+ storage_idx = resolved_member->GetDeclaringClass()->FindTypeIndexInOtherDexFile(*dex_file);
}
if (storage_idx != DexFile::kDexNoIndex) {
*storage_index = storage_idx;
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 73574ba..a8de86d 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -907,10 +907,10 @@
mirror::DexCache::GetElementPtrSize(resolved_methods, i, target_ptr_size_);
DCHECK(method != nullptr) << "Expected resolution method instead of null method";
mirror::Class* declaring_class = method->GetDeclaringClass();
- // Miranda methods may be held live by a class which was not an image class but have a
+ // Copied methods may be held live by a class which was not an image class but have a
// declaring class which is an image class. Set it to the resolution method to be safe and
// prevent dangling pointers.
- if (method->IsMiranda() || !KeepClass(declaring_class)) {
+ if (method->MightBeCopied() || !KeepClass(declaring_class)) {
mirror::DexCache::SetElementPtrSize(resolved_methods,
i,
resolution_method,
@@ -1820,12 +1820,16 @@
}
template <typename T>
-T* ImageWriter::NativeLocationInImage(T* obj, const char* oat_filename) {
+T* ImageWriter::NativeLocationInImage(T* obj) {
if (obj == nullptr || IsInBootImage(obj)) {
return obj;
} else {
- ImageInfo& image_info = GetImageInfo(oat_filename);
- return reinterpret_cast<T*>(image_info.image_begin_ + NativeOffsetInImage(obj));
+ auto it = native_object_relocations_.find(obj);
+ CHECK(it != native_object_relocations_.end()) << obj << " spaces "
+ << Runtime::Current()->GetHeap()->DumpSpaces();
+ const NativeObjectRelocation& relocation = it->second;
+ ImageInfo& image_info = GetImageInfo(relocation.oat_filename);
+ return reinterpret_cast<T*>(image_info.image_begin_ + relocation.offset);
}
}
@@ -1842,33 +1846,19 @@
class NativeLocationVisitor {
public:
- explicit NativeLocationVisitor(ImageWriter* image_writer, const char* oat_filename)
- : image_writer_(image_writer), oat_filename_(oat_filename) {}
+ explicit NativeLocationVisitor(ImageWriter* image_writer) : image_writer_(image_writer) {}
template <typename T>
T* operator()(T* ptr) const SHARED_REQUIRES(Locks::mutator_lock_) {
- return image_writer_->NativeLocationInImage(ptr, oat_filename_);
- }
-
- ArtMethod* operator()(ArtMethod* method) const SHARED_REQUIRES(Locks::mutator_lock_) {
- const char* oat_filename = method->IsRuntimeMethod() ? image_writer_->GetDefaultOatFilename() :
- image_writer_->GetOatFilenameForDexCache(method->GetDexCache());
- return image_writer_->NativeLocationInImage(method, oat_filename);
- }
-
- ArtField* operator()(ArtField* field) const SHARED_REQUIRES(Locks::mutator_lock_) {
- const char* oat_filename = image_writer_->GetOatFilenameForDexCache(field->GetDexCache());
- return image_writer_->NativeLocationInImage(field, oat_filename);
+ return image_writer_->NativeLocationInImage(ptr);
}
private:
ImageWriter* const image_writer_;
- const char* oat_filename_;
};
void ImageWriter::FixupClass(mirror::Class* orig, mirror::Class* copy) {
- const char* oat_filename = GetOatFilename(orig);
- orig->FixupNativePointers(copy, target_ptr_size_, NativeLocationVisitor(this, oat_filename));
+ orig->FixupNativePointers(copy, target_ptr_size_, NativeLocationVisitor(this));
FixupClassVisitor visitor(this, copy);
static_cast<mirror::Object*>(orig)->VisitReferences(visitor, visitor);
@@ -1952,11 +1942,10 @@
// 64-bit values here, clearing the top 32 bits for 32-bit targets. The zero-extension is
// done by casting to the unsigned type uintptr_t before casting to int64_t, i.e.
// static_cast<int64_t>(reinterpret_cast<uintptr_t>(image_begin_ + offset))).
- const char* oat_filename = GetOatFilenameForDexCache(orig_dex_cache);
GcRoot<mirror::String>* orig_strings = orig_dex_cache->GetStrings();
if (orig_strings != nullptr) {
copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::StringsOffset(),
- NativeLocationInImage(orig_strings, oat_filename),
+ NativeLocationInImage(orig_strings),
/*pointer size*/8u);
orig_dex_cache->FixupStrings(NativeCopyLocation(orig_strings, orig_dex_cache),
ImageAddressVisitor(this));
@@ -1964,7 +1953,7 @@
GcRoot<mirror::Class>* orig_types = orig_dex_cache->GetResolvedTypes();
if (orig_types != nullptr) {
copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedTypesOffset(),
- NativeLocationInImage(orig_types, oat_filename),
+ NativeLocationInImage(orig_types),
/*pointer size*/8u);
orig_dex_cache->FixupResolvedTypes(NativeCopyLocation(orig_types, orig_dex_cache),
ImageAddressVisitor(this));
@@ -1972,32 +1961,25 @@
ArtMethod** orig_methods = orig_dex_cache->GetResolvedMethods();
if (orig_methods != nullptr) {
copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedMethodsOffset(),
- NativeLocationInImage(orig_methods, oat_filename),
+ NativeLocationInImage(orig_methods),
/*pointer size*/8u);
ArtMethod** copy_methods = NativeCopyLocation(orig_methods, orig_dex_cache);
for (size_t i = 0, num = orig_dex_cache->NumResolvedMethods(); i != num; ++i) {
ArtMethod* orig = mirror::DexCache::GetElementPtrSize(orig_methods, i, target_ptr_size_);
- const char* method_oat_filename;
- if (orig == nullptr || orig->IsRuntimeMethod()) {
- method_oat_filename = default_oat_filename_;
- } else {
- method_oat_filename = GetOatFilenameForDexCache(orig->GetDexCache());
- }
- ArtMethod* copy = NativeLocationInImage(orig, method_oat_filename);
+ // NativeLocationInImage also handles runtime methods since these have relocation info.
+ ArtMethod* copy = NativeLocationInImage(orig);
mirror::DexCache::SetElementPtrSize(copy_methods, i, copy, target_ptr_size_);
}
}
ArtField** orig_fields = orig_dex_cache->GetResolvedFields();
if (orig_fields != nullptr) {
copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedFieldsOffset(),
- NativeLocationInImage(orig_fields, oat_filename),
+ NativeLocationInImage(orig_fields),
/*pointer size*/8u);
ArtField** copy_fields = NativeCopyLocation(orig_fields, orig_dex_cache);
for (size_t i = 0, num = orig_dex_cache->NumResolvedFields(); i != num; ++i) {
ArtField* orig = mirror::DexCache::GetElementPtrSize(orig_fields, i, target_ptr_size_);
- const char* field_oat_filename =
- orig == nullptr ? default_oat_filename_ : GetOatFilenameForDexCache(orig->GetDexCache());
- ArtField* copy = NativeLocationInImage(orig, field_oat_filename);
+ ArtField* copy = NativeLocationInImage(orig);
mirror::DexCache::SetElementPtrSize(copy_fields, i, copy, target_ptr_size_);
}
}
@@ -2089,20 +2071,10 @@
copy->SetDeclaringClass(GetImageAddress(orig->GetDeclaringClassUnchecked()));
- const char* oat_filename;
- if (orig->IsRuntimeMethod() || compile_app_image_) {
- oat_filename = default_oat_filename_;
- } else {
- auto it = dex_file_oat_filename_map_.find(orig->GetDexFile());
- DCHECK(it != dex_file_oat_filename_map_.end()) << orig->GetDexFile()->GetLocation();
- oat_filename = it->second;
- }
ArtMethod** orig_resolved_methods = orig->GetDexCacheResolvedMethods(target_ptr_size_);
- copy->SetDexCacheResolvedMethods(NativeLocationInImage(orig_resolved_methods, oat_filename),
- target_ptr_size_);
+ copy->SetDexCacheResolvedMethods(NativeLocationInImage(orig_resolved_methods), target_ptr_size_);
GcRoot<mirror::Class>* orig_resolved_types = orig->GetDexCacheResolvedTypes(target_ptr_size_);
- copy->SetDexCacheResolvedTypes(NativeLocationInImage(orig_resolved_types, oat_filename),
- target_ptr_size_);
+ copy->SetDexCacheResolvedTypes(NativeLocationInImage(orig_resolved_types), target_ptr_size_);
// OatWriter replaces the code_ with an offset value. Here we re-adjust to a pointer relative to
// oat_begin_
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index 9371d9f..b227c44 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -428,7 +428,7 @@
// Location of where the object will be when the image is loaded at runtime.
template <typename T>
- T* NativeLocationInImage(T* obj, const char* oat_filename) SHARED_REQUIRES(Locks::mutator_lock_);
+ T* NativeLocationInImage(T* obj) SHARED_REQUIRES(Locks::mutator_lock_);
// Location of where the temporary copy of the object currently is.
template <typename T>
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 894d29e..d3b404a 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -415,7 +415,9 @@
size_t visited_virtuals = 0;
// TODO We should also check copied methods in this test.
for (auto& m : klass->GetDeclaredVirtualMethods(pointer_size)) {
- EXPECT_FALSE(m.IsMiranda());
+ if (!klass->IsInterface()) {
+ EXPECT_FALSE(m.MightBeCopied());
+ }
CheckMethod(&m, oat_class.GetOatMethod(method_index), dex_file);
++method_index;
++visited_virtuals;
diff --git a/compiler/optimizing/constant_folding.cc b/compiler/optimizing/constant_folding.cc
index 014353d..7ddabde 100644
--- a/compiler/optimizing/constant_folding.cc
+++ b/compiler/optimizing/constant_folding.cc
@@ -18,8 +18,28 @@
namespace art {
-// This visitor tries to simplify operations that yield a constant. For example
-// `input * 0` is replaced by a null constant.
+// This visitor tries to simplify instructions that can be evaluated
+// as constants.
+class HConstantFoldingVisitor : public HGraphDelegateVisitor {
+ public:
+ explicit HConstantFoldingVisitor(HGraph* graph)
+ : HGraphDelegateVisitor(graph) {}
+
+ private:
+ void VisitBasicBlock(HBasicBlock* block) OVERRIDE;
+
+ void VisitUnaryOperation(HUnaryOperation* inst) OVERRIDE;
+ void VisitBinaryOperation(HBinaryOperation* inst) OVERRIDE;
+
+ void VisitTypeConversion(HTypeConversion* inst) OVERRIDE;
+ void VisitDivZeroCheck(HDivZeroCheck* inst) OVERRIDE;
+
+ DISALLOW_COPY_AND_ASSIGN(HConstantFoldingVisitor);
+};
+
+// This visitor tries to simplify operations with an absorbing input,
+// yielding a constant. For example `input * 0` is replaced by a
+// null constant.
class InstructionWithAbsorbingInputSimplifier : public HGraphVisitor {
public:
explicit InstructionWithAbsorbingInputSimplifier(HGraph* graph) : HGraphVisitor(graph) {}
@@ -44,59 +64,69 @@
void VisitXor(HXor* instruction) OVERRIDE;
};
+
void HConstantFolding::Run() {
- InstructionWithAbsorbingInputSimplifier simplifier(graph_);
+ HConstantFoldingVisitor visitor(graph_);
// Process basic blocks in reverse post-order in the dominator tree,
// so that an instruction turned into a constant, used as input of
// another instruction, may possibly be used to turn that second
// instruction into a constant as well.
- for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
- HBasicBlock* block = it.Current();
- // Traverse this block's instructions in (forward) order and
- // replace the ones that can be statically evaluated by a
- // compile-time counterpart.
- for (HInstructionIterator inst_it(block->GetInstructions());
- !inst_it.Done(); inst_it.Advance()) {
- HInstruction* inst = inst_it.Current();
- if (inst->IsBinaryOperation()) {
- // Constant folding: replace `op(a, b)' with a constant at
- // compile time if `a' and `b' are both constants.
- HConstant* constant = inst->AsBinaryOperation()->TryStaticEvaluation();
- if (constant != nullptr) {
- inst->ReplaceWith(constant);
- inst->GetBlock()->RemoveInstruction(inst);
- } else {
- inst->Accept(&simplifier);
- }
- } else if (inst->IsUnaryOperation()) {
- // Constant folding: replace `op(a)' with a constant at compile
- // time if `a' is a constant.
- HConstant* constant = inst->AsUnaryOperation()->TryStaticEvaluation();
- if (constant != nullptr) {
- inst->ReplaceWith(constant);
- inst->GetBlock()->RemoveInstruction(inst);
- }
- } else if (inst->IsTypeConversion()) {
- // Constant folding: replace `TypeConversion(a)' with a constant at
- // compile time if `a' is a constant.
- HConstant* constant = inst->AsTypeConversion()->TryStaticEvaluation();
- if (constant != nullptr) {
- inst->ReplaceWith(constant);
- inst->GetBlock()->RemoveInstruction(inst);
- }
- } else if (inst->IsDivZeroCheck()) {
- // We can safely remove the check if the input is a non-null constant.
- HDivZeroCheck* check = inst->AsDivZeroCheck();
- HInstruction* check_input = check->InputAt(0);
- if (check_input->IsConstant() && !check_input->AsConstant()->IsZero()) {
- check->ReplaceWith(check_input);
- check->GetBlock()->RemoveInstruction(check);
- }
- }
- }
+ visitor.VisitReversePostOrder();
+}
+
+
+void HConstantFoldingVisitor::VisitBasicBlock(HBasicBlock* block) {
+ // Traverse this block's instructions (phis don't need to be
+ // processed) in (forward) order and replace the ones that can be
+ // statically evaluated by a compile-time counterpart.
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ it.Current()->Accept(this);
}
}
+void HConstantFoldingVisitor::VisitUnaryOperation(HUnaryOperation* inst) {
+ // Constant folding: replace `op(a)' with a constant at compile
+ // time if `a' is a constant.
+ HConstant* constant = inst->TryStaticEvaluation();
+ if (constant != nullptr) {
+ inst->ReplaceWith(constant);
+ inst->GetBlock()->RemoveInstruction(inst);
+ }
+}
+
+void HConstantFoldingVisitor::VisitBinaryOperation(HBinaryOperation* inst) {
+ // Constant folding: replace `op(a, b)' with a constant at
+ // compile time if `a' and `b' are both constants.
+ HConstant* constant = inst->TryStaticEvaluation();
+ if (constant != nullptr) {
+ inst->ReplaceWith(constant);
+ inst->GetBlock()->RemoveInstruction(inst);
+ } else {
+ InstructionWithAbsorbingInputSimplifier simplifier(GetGraph());
+ inst->Accept(&simplifier);
+ }
+}
+
+void HConstantFoldingVisitor::VisitTypeConversion(HTypeConversion* inst) {
+ // Constant folding: replace `TypeConversion(a)' with a constant at
+ // compile time if `a' is a constant.
+ HConstant* constant = inst->AsTypeConversion()->TryStaticEvaluation();
+ if (constant != nullptr) {
+ inst->ReplaceWith(constant);
+ inst->GetBlock()->RemoveInstruction(inst);
+ }
+}
+
+void HConstantFoldingVisitor::VisitDivZeroCheck(HDivZeroCheck* inst) {
+ // We can safely remove the check if the input is a non-null constant.
+ HInstruction* check_input = inst->InputAt(0);
+ if (check_input->IsConstant() && !check_input->AsConstant()->IsZero()) {
+ inst->ReplaceWith(check_input);
+ inst->GetBlock()->RemoveInstruction(inst);
+ }
+}
+
+
void InstructionWithAbsorbingInputSimplifier::VisitShift(HBinaryOperation* instruction) {
DCHECK(instruction->IsShl() || instruction->IsShr() || instruction->IsUShr());
HInstruction* left = instruction->GetLeft();
diff --git a/compiler/optimizing/constant_folding.h b/compiler/optimizing/constant_folding.h
index 2698b2d..e10b1d6 100644
--- a/compiler/optimizing/constant_folding.h
+++ b/compiler/optimizing/constant_folding.h
@@ -26,13 +26,20 @@
* Optimization pass performing a simple constant-expression
* evaluation on the SSA form.
*
+ * Note that graph simplifications producing a constant should be
+ * implemented in art::HConstantFolding, while graph simplifications
+ * not producing constants should be implemented in
+ * art::InstructionSimplifier. (This convention is a choice that was
+ * made during the development of these parts of the compiler and is
+ * not bound by any technical requirement.)
+ *
* This class is named art::HConstantFolding to avoid name
* clashes with the art::ConstantPropagation class defined in
* compiler/dex/post_opt_passes.h.
*/
class HConstantFolding : public HOptimization {
public:
- explicit HConstantFolding(HGraph* graph, const char* name = kConstantFoldingPassName)
+ HConstantFolding(HGraph* graph, const char* name = kConstantFoldingPassName)
: HOptimization(graph, name) {}
void Run() OVERRIDE;
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index e6e9177..4a49c83 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -593,8 +593,9 @@
HBasicBlock* predecessor = loop_header->GetPredecessors()[i];
if (!loop_information->IsBackEdge(*predecessor)) {
AddError(StringPrintf(
- "Loop header %d has multiple incoming (non back edge) blocks.",
- id));
+ "Loop header %d has multiple incoming (non back edge) blocks: %d.",
+ id,
+ predecessor->GetBlockId()));
}
}
}
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index a5acab8..f39699e 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -190,28 +190,34 @@
}
}
-static uint32_t FindClassIndexIn(mirror::Class* cls, const DexFile& dex_file)
+static uint32_t FindClassIndexIn(mirror::Class* cls,
+ const DexFile& dex_file,
+ Handle<mirror::DexCache> dex_cache)
SHARED_REQUIRES(Locks::mutator_lock_) {
+ uint32_t index = DexFile::kDexNoIndex;
if (cls->GetDexCache() == nullptr) {
- DCHECK(cls->IsArrayClass());
- // TODO: find the class in `dex_file`.
- return DexFile::kDexNoIndex;
+ DCHECK(cls->IsArrayClass()) << PrettyClass(cls);
+ index = cls->FindTypeIndexInOtherDexFile(dex_file);
} else if (cls->GetDexTypeIndex() == DexFile::kDexNoIndex16) {
+ DCHECK(cls->IsProxyClass()) << PrettyClass(cls);
// TODO: deal with proxy classes.
- return DexFile::kDexNoIndex;
} else if (IsSameDexFile(cls->GetDexFile(), dex_file)) {
+ index = cls->GetDexTypeIndex();
+ } else {
+ index = cls->FindTypeIndexInOtherDexFile(dex_file);
+ }
+
+ if (index != DexFile::kDexNoIndex) {
// Update the dex cache to ensure the class is in. The generated code will
// consider it is. We make it safe by updating the dex cache, as other
// dex files might also load the class, and there is no guarantee the dex
// cache of the dex file of the class will be updated.
- if (cls->GetDexCache()->GetResolvedType(cls->GetDexTypeIndex()) == nullptr) {
- cls->GetDexCache()->SetResolvedType(cls->GetDexTypeIndex(), cls);
+ if (dex_cache->GetResolvedType(index) == nullptr) {
+ dex_cache->SetResolvedType(index, cls);
}
- return cls->GetDexTypeIndex();
- } else {
- // TODO: find the class in `dex_file`.
- return DexFile::kDexNoIndex;
}
+
+ return index;
}
bool HInliner::TryInline(HInvoke* invoke_instruction) {
@@ -303,7 +309,7 @@
uint32_t dex_pc) const {
ArtField* field = class_linker->GetClassRoot(ClassLinker::kJavaLangObject)->GetInstanceField(0);
DCHECK_EQ(std::string(field->GetName()), "shadow$_klass_");
- return new (graph_->GetArena()) HInstanceFieldGet(
+ HInstanceFieldGet* result = new (graph_->GetArena()) HInstanceFieldGet(
receiver,
Primitive::kPrimNot,
field->GetOffset(),
@@ -313,6 +319,9 @@
*field->GetDexFile(),
handles_->NewHandle(field->GetDexCache()),
dex_pc);
+ // The class of a field is effectively final, and does not have any memory dependencies.
+ result->SetSideEffects(SideEffects::None());
+ return result;
}
bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction,
@@ -322,7 +331,8 @@
<< invoke_instruction->DebugName();
const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
- uint32_t class_index = FindClassIndexIn(ic.GetMonomorphicType(), caller_dex_file);
+ uint32_t class_index = FindClassIndexIn(
+ ic.GetMonomorphicType(), caller_dex_file, caller_compilation_unit_.GetDexCache());
if (class_index == DexFile::kDexNoIndex) {
VLOG(compiler) << "Call to " << PrettyMethod(resolved_method)
<< " from inline cache is not inlined because its class is not"
@@ -350,32 +360,15 @@
}
// We successfully inlined, now add a guard.
- HInstanceFieldGet* receiver_class = BuildGetReceiverClass(
- class_linker, receiver, invoke_instruction->GetDexPc());
-
bool is_referrer =
(ic.GetMonomorphicType() == outermost_graph_->GetArtMethod()->GetDeclaringClass());
- HLoadClass* load_class = new (graph_->GetArena()) HLoadClass(graph_->GetCurrentMethod(),
- class_index,
- caller_dex_file,
- is_referrer,
- invoke_instruction->GetDexPc(),
- /* needs_access_check */ false,
- /* is_in_dex_cache */ true);
-
- HNotEqual* compare = new (graph_->GetArena()) HNotEqual(load_class, receiver_class);
- HDeoptimize* deoptimize = new (graph_->GetArena()) HDeoptimize(
- compare, invoke_instruction->GetDexPc());
- // TODO: Extend reference type propagation to understand the guard.
- if (cursor != nullptr) {
- bb_cursor->InsertInstructionAfter(receiver_class, cursor);
- } else {
- bb_cursor->InsertInstructionBefore(receiver_class, bb_cursor->GetFirstInstruction());
- }
- bb_cursor->InsertInstructionAfter(load_class, receiver_class);
- bb_cursor->InsertInstructionAfter(compare, load_class);
- bb_cursor->InsertInstructionAfter(deoptimize, compare);
- deoptimize->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
+ AddTypeGuard(receiver,
+ cursor,
+ bb_cursor,
+ class_index,
+ is_referrer,
+ invoke_instruction,
+ /* with_deoptimization */ true);
// Run type propagation to get the guard typed, and eventually propagate the
// type of the receiver.
@@ -386,11 +379,219 @@
return true;
}
+HInstruction* HInliner::AddTypeGuard(HInstruction* receiver,
+ HInstruction* cursor,
+ HBasicBlock* bb_cursor,
+ uint32_t class_index,
+ bool is_referrer,
+ HInstruction* invoke_instruction,
+ bool with_deoptimization) {
+ ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
+ HInstanceFieldGet* receiver_class = BuildGetReceiverClass(
+ class_linker, receiver, invoke_instruction->GetDexPc());
+
+ const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
+ // Note that we will just compare the classes, so we don't need Java semantics access checks.
+ // Also, the caller of `AddTypeGuard` must have guaranteed that the class is in the dex cache.
+ HLoadClass* load_class = new (graph_->GetArena()) HLoadClass(graph_->GetCurrentMethod(),
+ class_index,
+ caller_dex_file,
+ is_referrer,
+ invoke_instruction->GetDexPc(),
+ /* needs_access_check */ false,
+ /* is_in_dex_cache */ true);
+
+ HNotEqual* compare = new (graph_->GetArena()) HNotEqual(load_class, receiver_class);
+ // TODO: Extend reference type propagation to understand the guard.
+ if (cursor != nullptr) {
+ bb_cursor->InsertInstructionAfter(receiver_class, cursor);
+ } else {
+ bb_cursor->InsertInstructionBefore(receiver_class, bb_cursor->GetFirstInstruction());
+ }
+ bb_cursor->InsertInstructionAfter(load_class, receiver_class);
+ bb_cursor->InsertInstructionAfter(compare, load_class);
+ if (with_deoptimization) {
+ HDeoptimize* deoptimize = new (graph_->GetArena()) HDeoptimize(
+ compare, invoke_instruction->GetDexPc());
+ bb_cursor->InsertInstructionAfter(deoptimize, compare);
+ deoptimize->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
+ }
+ return compare;
+}
+
bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction,
ArtMethod* resolved_method,
const InlineCache& ic) {
DCHECK(invoke_instruction->IsInvokeVirtual() || invoke_instruction->IsInvokeInterface())
<< invoke_instruction->DebugName();
+
+ if (TryInlinePolymorphicCallToSameTarget(invoke_instruction, resolved_method, ic)) {
+ return true;
+ }
+
+ ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
+ size_t pointer_size = class_linker->GetImagePointerSize();
+ const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
+
+ bool all_targets_inlined = true;
+ bool one_target_inlined = false;
+ for (size_t i = 0; i < InlineCache::kIndividualCacheSize; ++i) {
+ if (ic.GetTypeAt(i) == nullptr) {
+ break;
+ }
+ ArtMethod* method = nullptr;
+ if (invoke_instruction->IsInvokeInterface()) {
+ method = ic.GetTypeAt(i)->FindVirtualMethodForInterface(
+ resolved_method, pointer_size);
+ } else {
+ DCHECK(invoke_instruction->IsInvokeVirtual());
+ method = ic.GetTypeAt(i)->FindVirtualMethodForVirtual(
+ resolved_method, pointer_size);
+ }
+
+ HInstruction* receiver = invoke_instruction->InputAt(0);
+ HInstruction* cursor = invoke_instruction->GetPrevious();
+ HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
+
+ uint32_t class_index = FindClassIndexIn(
+ ic.GetTypeAt(i), caller_dex_file, caller_compilation_unit_.GetDexCache());
+ HInstruction* return_replacement = nullptr;
+ if (class_index == DexFile::kDexNoIndex ||
+ !TryBuildAndInline(invoke_instruction, method, &return_replacement)) {
+ all_targets_inlined = false;
+ } else {
+ one_target_inlined = true;
+ bool is_referrer = (ic.GetTypeAt(i) == outermost_graph_->GetArtMethod()->GetDeclaringClass());
+
+ // If we have inlined all targets before, and this receiver is the last seen,
+ // we deoptimize instead of keeping the original invoke instruction.
+ bool deoptimize = all_targets_inlined &&
+ (i != InlineCache::kIndividualCacheSize - 1) &&
+ (ic.GetTypeAt(i + 1) == nullptr);
+ HInstruction* compare = AddTypeGuard(
+ receiver, cursor, bb_cursor, class_index, is_referrer, invoke_instruction, deoptimize);
+ if (deoptimize) {
+ if (return_replacement != nullptr) {
+ invoke_instruction->ReplaceWith(return_replacement);
+ }
+ invoke_instruction->GetBlock()->RemoveInstruction(invoke_instruction);
+ // Because the inline cache data can be populated concurrently, we force the end of the
+ // iteration. Otherhwise, we could see a new receiver type.
+ break;
+ } else {
+ CreateDiamondPatternForPolymorphicInline(compare, return_replacement, invoke_instruction);
+ }
+ }
+ }
+
+ if (!one_target_inlined) {
+ VLOG(compiler) << "Call to " << PrettyMethod(resolved_method)
+ << " from inline cache is not inlined because none"
+ << " of its targets could be inlined";
+ return false;
+ }
+ MaybeRecordStat(kInlinedPolymorphicCall);
+
+ // Run type propagation to get the guards typed.
+ ReferenceTypePropagation rtp_fixup(graph_, handles_, /* is_first_run */ false);
+ rtp_fixup.Run();
+ return true;
+}
+
+void HInliner::CreateDiamondPatternForPolymorphicInline(HInstruction* compare,
+ HInstruction* return_replacement,
+ HInstruction* invoke_instruction) {
+ uint32_t dex_pc = invoke_instruction->GetDexPc();
+ HBasicBlock* cursor_block = compare->GetBlock();
+ HBasicBlock* original_invoke_block = invoke_instruction->GetBlock();
+ ArenaAllocator* allocator = graph_->GetArena();
+
+ // Spit the block after the compare: `cursor_block` will now be the start of the diamond,
+ // and the returned block is the start of the then branch (that could contain multiple blocks).
+ HBasicBlock* then = cursor_block->SplitAfterForInlining(compare);
+
+ // Split the block containing the invoke before and after the invoke. The returned block
+ // of the split before will contain the invoke and will be the otherwise branch of
+ // the diamond. The returned block of the split after will be the merge block
+ // of the diamond.
+ HBasicBlock* end_then = invoke_instruction->GetBlock();
+ HBasicBlock* otherwise = end_then->SplitBeforeForInlining(invoke_instruction);
+ HBasicBlock* merge = otherwise->SplitAfterForInlining(invoke_instruction);
+
+ // If the methods we are inlining return a value, we create a phi in the merge block
+ // that will have the `invoke_instruction and the `return_replacement` as inputs.
+ if (return_replacement != nullptr) {
+ HPhi* phi = new (allocator) HPhi(
+ allocator, kNoRegNumber, 0, HPhi::ToPhiType(invoke_instruction->GetType()), dex_pc);
+ merge->AddPhi(phi);
+ invoke_instruction->ReplaceWith(phi);
+ phi->AddInput(return_replacement);
+ phi->AddInput(invoke_instruction);
+ }
+
+ // Add the control flow instructions.
+ otherwise->AddInstruction(new (allocator) HGoto(dex_pc));
+ end_then->AddInstruction(new (allocator) HGoto(dex_pc));
+ cursor_block->AddInstruction(new (allocator) HIf(compare, dex_pc));
+
+ // Add the newly created blocks to the graph.
+ graph_->AddBlock(then);
+ graph_->AddBlock(otherwise);
+ graph_->AddBlock(merge);
+
+ // Set up successor (and implictly predecessor) relations.
+ cursor_block->AddSuccessor(otherwise);
+ cursor_block->AddSuccessor(then);
+ end_then->AddSuccessor(merge);
+ otherwise->AddSuccessor(merge);
+
+ // Set up dominance information.
+ then->SetDominator(cursor_block);
+ cursor_block->AddDominatedBlock(then);
+ otherwise->SetDominator(cursor_block);
+ cursor_block->AddDominatedBlock(otherwise);
+ merge->SetDominator(cursor_block);
+ cursor_block->AddDominatedBlock(merge);
+
+ // Update the revert post order.
+ size_t index = IndexOfElement(graph_->reverse_post_order_, cursor_block);
+ MakeRoomFor(&graph_->reverse_post_order_, 1, index);
+ graph_->reverse_post_order_[++index] = then;
+ index = IndexOfElement(graph_->reverse_post_order_, end_then);
+ MakeRoomFor(&graph_->reverse_post_order_, 2, index);
+ graph_->reverse_post_order_[++index] = otherwise;
+ graph_->reverse_post_order_[++index] = merge;
+
+ // Set the loop information of the newly created blocks.
+ HLoopInformation* loop_info = cursor_block->GetLoopInformation();
+ if (loop_info != nullptr) {
+ then->SetLoopInformation(cursor_block->GetLoopInformation());
+ merge->SetLoopInformation(cursor_block->GetLoopInformation());
+ otherwise->SetLoopInformation(cursor_block->GetLoopInformation());
+ for (HLoopInformationOutwardIterator loop_it(*cursor_block);
+ !loop_it.Done();
+ loop_it.Advance()) {
+ loop_it.Current()->Add(then);
+ loop_it.Current()->Add(merge);
+ loop_it.Current()->Add(otherwise);
+ }
+ // In case the original invoke location was a back edge, we need to update
+ // the loop to now have the merge block as a back edge.
+ if (loop_info->IsBackEdge(*original_invoke_block)) {
+ loop_info->RemoveBackEdge(original_invoke_block);
+ loop_info->AddBackEdge(merge);
+ }
+ }
+
+ // Set the try/catch information of the newly created blocks.
+ then->SetTryCatchInformation(cursor_block->GetTryCatchInformation());
+ merge->SetTryCatchInformation(cursor_block->GetTryCatchInformation());
+ otherwise->SetTryCatchInformation(cursor_block->GetTryCatchInformation());
+}
+
+bool HInliner::TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction,
+ ArtMethod* resolved_method,
+ const InlineCache& ic) {
// This optimization only works under JIT for now.
DCHECK(Runtime::Current()->UseJit());
if (graph_->GetInstructionSet() == kMips64) {
@@ -883,7 +1084,7 @@
HConstantFolding fold(callee_graph);
HSharpening sharpening(callee_graph, codegen_, dex_compilation_unit, compiler_driver_);
InstructionSimplifier simplify(callee_graph, stats_);
- IntrinsicsRecognizer intrinsics(callee_graph, compiler_driver_);
+ IntrinsicsRecognizer intrinsics(callee_graph, compiler_driver_, stats_);
HOptimization* optimizations[] = {
&intrinsics,
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index 9dd9bf5..cdb2167 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -101,12 +101,18 @@
const InlineCache& ic)
SHARED_REQUIRES(Locks::mutator_lock_);
- // Try to inline targets of a polymorphic call. Currently unimplemented.
+ // Try to inline targets of a polymorphic call.
bool TryInlinePolymorphicCall(HInvoke* invoke_instruction,
ArtMethod* resolved_method,
const InlineCache& ic)
SHARED_REQUIRES(Locks::mutator_lock_);
+ bool TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction,
+ ArtMethod* resolved_method,
+ const InlineCache& ic)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+
HInstanceFieldGet* BuildGetReceiverClass(ClassLinker* class_linker,
HInstruction* receiver,
uint32_t dex_pc) const
@@ -118,6 +124,57 @@
bool do_rtp)
SHARED_REQUIRES(Locks::mutator_lock_);
+ // Add a type guard on the given `receiver`. This will add to the graph:
+ // i0 = HFieldGet(receiver, klass)
+ // i1 = HLoadClass(class_index, is_referrer)
+ // i2 = HNotEqual(i0, i1)
+ //
+ // And if `with_deoptimization` is true:
+ // HDeoptimize(i2)
+ //
+ // The method returns the `HNotEqual`, that will be used for polymorphic inlining.
+ HInstruction* AddTypeGuard(HInstruction* receiver,
+ HInstruction* cursor,
+ HBasicBlock* bb_cursor,
+ uint32_t class_index,
+ bool is_referrer,
+ HInstruction* invoke_instruction,
+ bool with_deoptimization)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
+ /*
+ * Ad-hoc implementation for implementing a diamond pattern in the graph for
+ * polymorphic inlining:
+ * 1) `compare` becomes the input of the new `HIf`.
+ * 2) Everything up until `invoke_instruction` is in the then branch (could
+ * contain multiple blocks).
+ * 3) `invoke_instruction` is moved to the otherwise block.
+ * 4) If `return_replacement` is not null, the merge block will have
+ * a phi whose inputs are `return_replacement` and `invoke_instruction`.
+ *
+ * Before:
+ * Block1
+ * compare
+ * ...
+ * invoke_instruction
+ *
+ * After:
+ * Block1
+ * compare
+ * if
+ * / \
+ * / \
+ * Then block Otherwise block
+ * ... invoke_instruction
+ * \ /
+ * \ /
+ * Merge block
+ * phi(return_replacement, invoke_instruction)
+ */
+ void CreateDiamondPatternForPolymorphicInline(HInstruction* compare,
+ HInstruction* return_replacement,
+ HInstruction* invoke_instruction);
+
HGraph* const outermost_graph_;
const DexCompilationUnit& outer_compilation_unit_;
const DexCompilationUnit& caller_compilation_unit_;
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index a48d06f..13d3f75 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -92,6 +92,7 @@
void SimplifySystemArrayCopy(HInvoke* invoke);
void SimplifyStringEquals(HInvoke* invoke);
void SimplifyCompare(HInvoke* invoke, bool has_zero_op);
+ void SimplifyIsNaN(HInvoke* invoke);
OptimizingCompilerStats* stats_;
bool simplification_occurred_ = false;
@@ -1551,6 +1552,16 @@
invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, compare);
}
+void InstructionSimplifierVisitor::SimplifyIsNaN(HInvoke* invoke) {
+ DCHECK(invoke->IsInvokeStaticOrDirect());
+ uint32_t dex_pc = invoke->GetDexPc();
+ // IsNaN(x) is the same as x != x.
+ HInstruction* x = invoke->InputAt(0);
+ HCondition* condition = new (GetGraph()->GetArena()) HNotEqual(x, x, dex_pc);
+ condition->SetBias(ComparisonBias::kLtBias);
+ invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, condition);
+}
+
void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) {
if (instruction->GetIntrinsic() == Intrinsics::kStringEquals) {
SimplifyStringEquals(instruction);
@@ -1568,6 +1579,9 @@
} else if (instruction->GetIntrinsic() == Intrinsics::kIntegerSignum ||
instruction->GetIntrinsic() == Intrinsics::kLongSignum) {
SimplifyCompare(instruction, /* is_signum */ true);
+ } else if (instruction->GetIntrinsic() == Intrinsics::kFloatIsNaN ||
+ instruction->GetIntrinsic() == Intrinsics::kDoubleIsNaN) {
+ SimplifyIsNaN(instruction);
}
}
diff --git a/compiler/optimizing/instruction_simplifier.h b/compiler/optimizing/instruction_simplifier.h
index cc4b6f6..7905104 100644
--- a/compiler/optimizing/instruction_simplifier.h
+++ b/compiler/optimizing/instruction_simplifier.h
@@ -25,6 +25,13 @@
/**
* Implements optimizations specific to each instruction.
+ *
+ * Note that graph simplifications producing a constant should be
+ * implemented in art::HConstantFolding, while graph simplifications
+ * not producing constants should be implemented in
+ * art::InstructionSimplifier. (This convention is a choice that was
+ * made during the development of these parts of the compiler and is
+ * not bound by any technical requirement.)
*/
class InstructionSimplifier : public HOptimization {
public:
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index db39bc8..316e86b 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -570,6 +570,7 @@
NeedsEnvironmentOrCache(intrinsic),
GetSideEffects(intrinsic),
GetExceptions(intrinsic));
+ MaybeRecordStat(MethodCompilationStat::kIntrinsicRecognized);
}
}
}
diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h
index 3bf3f7f..2ab50bb 100644
--- a/compiler/optimizing/intrinsics.h
+++ b/compiler/optimizing/intrinsics.h
@@ -33,8 +33,8 @@
// Recognize intrinsics from HInvoke nodes.
class IntrinsicsRecognizer : public HOptimization {
public:
- IntrinsicsRecognizer(HGraph* graph, CompilerDriver* driver)
- : HOptimization(graph, kIntrinsicsRecognizerPassName),
+ IntrinsicsRecognizer(HGraph* graph, CompilerDriver* driver, OptimizingCompilerStats* stats)
+ : HOptimization(graph, kIntrinsicsRecognizerPassName, stats),
driver_(driver) {}
void Run() OVERRIDE;
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index 00a158b..ea8669f 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -1858,8 +1858,6 @@
UNIMPLEMENTED_INTRINSIC(FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite)
-UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
-UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit)
UNIMPLEMENTED_INTRINSIC(LongHighestOneBit)
@@ -1867,6 +1865,8 @@
UNIMPLEMENTED_INTRINSIC(LongLowestOneBit)
// Handled as HIR instructions.
+UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
+UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
UNIMPLEMENTED_INTRINSIC(LongRotateLeft)
UNIMPLEMENTED_INTRINSIC(IntegerRotateRight)
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 4140d94..8741fd2 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -1618,8 +1618,6 @@
UNIMPLEMENTED_INTRINSIC(FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite)
-UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
-UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit)
UNIMPLEMENTED_INTRINSIC(LongHighestOneBit)
@@ -1627,6 +1625,8 @@
UNIMPLEMENTED_INTRINSIC(LongLowestOneBit)
// Handled as HIR instructions.
+UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
+UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
UNIMPLEMENTED_INTRINSIC(LongRotateLeft)
UNIMPLEMENTED_INTRINSIC(IntegerRotateRight)
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 5d6e8c2..c862964 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -1220,8 +1220,6 @@
UNIMPLEMENTED_INTRINSIC(FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite)
-UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
-UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit)
UNIMPLEMENTED_INTRINSIC(LongHighestOneBit)
@@ -1229,6 +1227,8 @@
UNIMPLEMENTED_INTRINSIC(LongLowestOneBit)
// Handled as HIR instructions.
+UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
+UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerCompare)
UNIMPLEMENTED_INTRINSIC(LongCompare)
UNIMPLEMENTED_INTRINSIC(IntegerSignum)
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index ac28503..cf3a365 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -1764,8 +1764,6 @@
UNIMPLEMENTED_INTRINSIC(FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite)
-UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
-UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit)
UNIMPLEMENTED_INTRINSIC(LongHighestOneBit)
@@ -1773,6 +1771,8 @@
UNIMPLEMENTED_INTRINSIC(LongLowestOneBit)
// Handled as HIR instructions.
+UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
+UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerCompare)
UNIMPLEMENTED_INTRINSIC(LongCompare)
UNIMPLEMENTED_INTRINSIC(IntegerSignum)
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 22cefe8..260a877 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -2635,8 +2635,6 @@
UNIMPLEMENTED_INTRINSIC(FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite)
-UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
-UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerHighestOneBit)
UNIMPLEMENTED_INTRINSIC(LongHighestOneBit)
@@ -2644,6 +2642,8 @@
UNIMPLEMENTED_INTRINSIC(LongLowestOneBit)
// Handled as HIR instructions.
+UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
+UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
UNIMPLEMENTED_INTRINSIC(LongRotateLeft)
UNIMPLEMENTED_INTRINSIC(IntegerRotateRight)
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index c9a4344..93e8c00 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -2717,10 +2717,10 @@
UNIMPLEMENTED_INTRINSIC(FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite)
-UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
-UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
// Handled as HIR instructions.
+UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
+UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
UNIMPLEMENTED_INTRINSIC(LongRotateLeft)
UNIMPLEMENTED_INTRINSIC(IntegerRotateRight)
diff --git a/compiler/optimizing/licm.cc b/compiler/optimizing/licm.cc
index a6b4078..33bb2e8 100644
--- a/compiler/optimizing/licm.cc
+++ b/compiler/optimizing/licm.cc
@@ -141,6 +141,7 @@
DCHECK(!instruction->HasEnvironment());
}
instruction->MoveBefore(pre_header->GetLastInstruction());
+ MaybeRecordStat(MethodCompilationStat::kLoopInvariantMoved);
} else if (instruction->CanThrow()) {
// If `instruction` can throw, we cannot move further instructions
// that can throw as well.
diff --git a/compiler/optimizing/licm.h b/compiler/optimizing/licm.h
index 0b5a0f1..bf56f53 100644
--- a/compiler/optimizing/licm.h
+++ b/compiler/optimizing/licm.h
@@ -26,8 +26,9 @@
class LICM : public HOptimization {
public:
- LICM(HGraph* graph, const SideEffectsAnalysis& side_effects)
- : HOptimization(graph, kLoopInvariantCodeMotionPassName), side_effects_(side_effects) {}
+ LICM(HGraph* graph, const SideEffectsAnalysis& side_effects, OptimizingCompilerStats* stats)
+ : HOptimization(graph, kLoopInvariantCodeMotionPassName, stats),
+ side_effects_(side_effects) {}
void Run() OVERRIDE;
diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc
index 9fb32f4..d446539 100644
--- a/compiler/optimizing/licm_test.cc
+++ b/compiler/optimizing/licm_test.cc
@@ -79,7 +79,7 @@
graph_->BuildDominatorTree();
SideEffectsAnalysis side_effects(graph_);
side_effects.Run();
- LICM(graph_, side_effects).Run();
+ LICM(graph_, side_effects, nullptr).Run();
}
// General building fields.
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index b26ce0a..f36dc6e 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -1420,7 +1420,38 @@
}
}
-HBasicBlock* HBasicBlock::SplitAfter(HInstruction* cursor) {
+HBasicBlock* HBasicBlock::SplitBeforeForInlining(HInstruction* cursor) {
+ DCHECK_EQ(cursor->GetBlock(), this);
+
+ HBasicBlock* new_block = new (GetGraph()->GetArena()) HBasicBlock(GetGraph(),
+ cursor->GetDexPc());
+ new_block->instructions_.first_instruction_ = cursor;
+ new_block->instructions_.last_instruction_ = instructions_.last_instruction_;
+ instructions_.last_instruction_ = cursor->previous_;
+ if (cursor->previous_ == nullptr) {
+ instructions_.first_instruction_ = nullptr;
+ } else {
+ cursor->previous_->next_ = nullptr;
+ cursor->previous_ = nullptr;
+ }
+
+ new_block->instructions_.SetBlockOfInstructions(new_block);
+
+ for (HBasicBlock* successor : GetSuccessors()) {
+ new_block->successors_.push_back(successor);
+ successor->predecessors_[successor->GetPredecessorIndexOf(this)] = new_block;
+ }
+ successors_.clear();
+
+ for (HBasicBlock* dominated : GetDominatedBlocks()) {
+ dominated->dominator_ = new_block;
+ new_block->dominated_blocks_.push_back(dominated);
+ }
+ dominated_blocks_.clear();
+ return new_block;
+}
+
+HBasicBlock* HBasicBlock::SplitAfterForInlining(HInstruction* cursor) {
DCHECK(!cursor->IsControlFlow());
DCHECK_NE(instructions_.last_instruction_, cursor);
DCHECK_EQ(cursor->GetBlock(), this);
@@ -1573,6 +1604,20 @@
}
}
+void HInstructionList::AddBefore(HInstruction* cursor, const HInstructionList& instruction_list) {
+ DCHECK(Contains(cursor));
+ if (!instruction_list.IsEmpty()) {
+ if (cursor == first_instruction_) {
+ first_instruction_ = instruction_list.first_instruction_;
+ } else {
+ cursor->previous_->next_ = instruction_list.first_instruction_;
+ }
+ instruction_list.last_instruction_->next_ = cursor;
+ instruction_list.first_instruction_->previous_ = cursor->previous_;
+ cursor->previous_ = instruction_list.last_instruction_;
+ }
+}
+
void HInstructionList::Add(const HInstructionList& instruction_list) {
if (IsEmpty()) {
first_instruction_ = instruction_list.first_instruction_;
@@ -1815,18 +1860,6 @@
graph_ = nullptr;
}
-// Create space in `blocks` for adding `number_of_new_blocks` entries
-// starting at location `at`. Blocks after `at` are moved accordingly.
-static void MakeRoomFor(ArenaVector<HBasicBlock*>* blocks,
- size_t number_of_new_blocks,
- size_t after) {
- DCHECK_LT(after, blocks->size());
- size_t old_size = blocks->size();
- size_t new_size = old_size + number_of_new_blocks;
- blocks->resize(new_size);
- std::copy_backward(blocks->begin() + after + 1u, blocks->begin() + old_size, blocks->end());
-}
-
void HGraph::DeleteDeadEmptyBlock(HBasicBlock* block) {
DCHECK_EQ(block->GetGraph(), this);
DCHECK(block->GetSuccessors().empty());
@@ -1880,7 +1913,8 @@
DCHECK(!body->IsInLoop());
HInstruction* last = body->GetLastInstruction();
- invoke->GetBlock()->instructions_.AddAfter(invoke, body->GetInstructions());
+ // Note that we add instructions before the invoke only to simplify polymorphic inlining.
+ invoke->GetBlock()->instructions_.AddBefore(invoke, body->GetInstructions());
body->GetInstructions().SetBlockOfInstructions(invoke->GetBlock());
// Replace the invoke with the return value of the inlined graph.
@@ -1898,7 +1932,8 @@
// with the second half.
ArenaAllocator* allocator = outer_graph->GetArena();
HBasicBlock* at = invoke->GetBlock();
- HBasicBlock* to = at->SplitAfter(invoke);
+ // Note that we split before the invoke only to simplify polymorphic inlining.
+ HBasicBlock* to = at->SplitBeforeForInlining(invoke);
HBasicBlock* first = entry_block_->GetSuccessors()[0];
DCHECK(!first->IsInLoop());
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 01ba704..399afab 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -131,6 +131,7 @@
void SetBlockOfInstructions(HBasicBlock* block) const;
void AddAfter(HInstruction* cursor, const HInstructionList& instruction_list);
+ void AddBefore(HInstruction* cursor, const HInstructionList& instruction_list);
void Add(const HInstructionList& instruction_list);
// Return the number of instructions in the list. This is an expensive operation.
@@ -618,6 +619,7 @@
friend class SsaBuilder; // For caching constants.
friend class SsaLivenessAnalysis; // For the linear order.
+ friend class HInliner; // For the reverse post order.
ART_FRIEND_TEST(GraphTest, IfSuccessorSimpleJoinBlock1);
DISALLOW_COPY_AND_ASSIGN(HGraph);
};
@@ -972,12 +974,15 @@
// loop and try/catch information.
HBasicBlock* SplitBefore(HInstruction* cursor);
- // Split the block into two blocks just after `cursor`. Returns the newly
+ // Split the block into two blocks just before `cursor`. Returns the newly
// created block. Note that this method just updates raw block information,
// like predecessors, successors, dominators, and instruction list. It does not
// update the graph, reverse post order, loop information, nor make sure the
// blocks are consistent (for example ending with a control flow instruction).
- HBasicBlock* SplitAfter(HInstruction* cursor);
+ HBasicBlock* SplitBeforeForInlining(HInstruction* cursor);
+
+ // Similar to `SplitBeforeForInlining` but does it after `cursor`.
+ HBasicBlock* SplitAfterForInlining(HInstruction* cursor);
// Split catch block into two blocks after the original move-exception bytecode
// instruction, or at the beginning if not present. Returns the newly created,
@@ -2063,6 +2068,7 @@
}
SideEffects GetSideEffects() const { return side_effects_; }
+ void SetSideEffects(SideEffects other) { side_effects_ = other; }
void AddSideEffects(SideEffects other) { side_effects_.Add(other); }
size_t GetLifetimePosition() const { return lifetime_position_; }
@@ -2101,7 +2107,6 @@
protected:
virtual const HUserRecord<HInstruction*> InputRecordAt(size_t i) const = 0;
virtual void SetRawInputRecordAt(size_t index, const HUserRecord<HInstruction*>& input) = 0;
- void SetSideEffects(SideEffects other) { side_effects_ = other; }
private:
void RemoveEnvironmentUser(HUseListNode<HEnvironment*>* use_node) { env_uses_.Remove(use_node); }
@@ -6370,6 +6375,18 @@
DISALLOW_COPY_AND_ASSIGN(SwitchTable);
};
+// Create space in `blocks` for adding `number_of_new_blocks` entries
+// starting at location `at`. Blocks after `at` are moved accordingly.
+inline void MakeRoomFor(ArenaVector<HBasicBlock*>* blocks,
+ size_t number_of_new_blocks,
+ size_t after) {
+ DCHECK_LT(after, blocks->size());
+ size_t old_size = blocks->size();
+ size_t new_size = old_size + number_of_new_blocks;
+ blocks->resize(new_size);
+ std::copy_backward(blocks->begin() + after + 1u, blocks->begin() + old_size, blocks->end());
+}
+
} // namespace art
#endif // ART_COMPILER_OPTIMIZING_NODES_H_
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 12b748b..ae638df 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -505,12 +505,12 @@
graph, stats, HDeadCodeElimination::kFinalDeadCodeEliminationPassName);
HConstantFolding* fold1 = new (arena) HConstantFolding(graph);
InstructionSimplifier* simplify1 = new (arena) InstructionSimplifier(graph, stats);
- HSelectGenerator* select_generator = new (arena) HSelectGenerator(graph);
+ HSelectGenerator* select_generator = new (arena) HSelectGenerator(graph, stats);
HConstantFolding* fold2 = new (arena) HConstantFolding(graph, "constant_folding_after_inlining");
HConstantFolding* fold3 = new (arena) HConstantFolding(graph, "constant_folding_after_bce");
SideEffectsAnalysis* side_effects = new (arena) SideEffectsAnalysis(graph);
GVNOptimization* gvn = new (arena) GVNOptimization(graph, *side_effects);
- LICM* licm = new (arena) LICM(graph, *side_effects);
+ LICM* licm = new (arena) LICM(graph, *side_effects, stats);
LoadStoreElimination* lse = new (arena) LoadStoreElimination(graph, *side_effects);
HInductionVarAnalysis* induction = new (arena) HInductionVarAnalysis(graph);
BoundsCheckElimination* bce = new (arena) BoundsCheckElimination(graph, *side_effects, induction);
@@ -519,7 +519,7 @@
graph, stats, "instruction_simplifier_after_bce");
InstructionSimplifier* simplify3 = new (arena) InstructionSimplifier(
graph, stats, "instruction_simplifier_before_codegen");
- IntrinsicsRecognizer* intrinsics = new (arena) IntrinsicsRecognizer(graph, driver);
+ IntrinsicsRecognizer* intrinsics = new (arena) IntrinsicsRecognizer(graph, driver, stats);
HOptimization* optimizations1[] = {
intrinsics,
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index 52a7b10..179004b 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -56,6 +56,10 @@
kMonomorphicCall,
kPolymorphicCall,
kMegamorphicCall,
+ kBooleanSimplified,
+ kIntrinsicRecognized,
+ kLoopInvariantMoved,
+ kSelectGenerated,
kLastStat
};
@@ -124,7 +128,11 @@
case kInlinedPolymorphicCall: name = "InlinedPolymorphicCall"; break;
case kMonomorphicCall: name = "MonomorphicCall"; break;
case kPolymorphicCall: name = "PolymorphicCall"; break;
- case kMegamorphicCall: name = "kMegamorphicCall"; break;
+ case kMegamorphicCall: name = "MegamorphicCall"; break;
+ case kBooleanSimplified : name = "BooleanSimplified"; break;
+ case kIntrinsicRecognized : name = "IntrinsicRecognized"; break;
+ case kLoopInvariantMoved : name = "LoopInvariantMoved"; break;
+ case kSelectGenerated : name = "SelectGenerated"; break;
case kLastStat:
LOG(FATAL) << "invalid stat "
diff --git a/compiler/optimizing/select_generator.cc b/compiler/optimizing/select_generator.cc
index 105b30a..e52476e 100644
--- a/compiler/optimizing/select_generator.cc
+++ b/compiler/optimizing/select_generator.cc
@@ -141,6 +141,8 @@
block->MergeWith(merge_block);
}
+ MaybeRecordStat(MethodCompilationStat::kSelectGenerated);
+
// No need to update 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
diff --git a/compiler/optimizing/select_generator.h b/compiler/optimizing/select_generator.h
index f9d6d4d..c6dca58 100644
--- a/compiler/optimizing/select_generator.h
+++ b/compiler/optimizing/select_generator.h
@@ -47,8 +47,8 @@
class HSelectGenerator : public HOptimization {
public:
- explicit HSelectGenerator(HGraph* graph)
- : HOptimization(graph, kSelectGeneratorPassName) {}
+ HSelectGenerator(HGraph* graph, OptimizingCompilerStats* stats)
+ : HOptimization(graph, kSelectGeneratorPassName, stats) {}
void Run() OVERRIDE;
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index 43f2499..09ca8b7 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -422,6 +422,34 @@
return true;
}
+static bool HasAliasInEnvironments(HInstruction* instruction) {
+ for (HUseIterator<HEnvironment*> use_it(instruction->GetEnvUses());
+ !use_it.Done();
+ use_it.Advance()) {
+ HEnvironment* use = use_it.Current()->GetUser();
+ HUseListNode<HEnvironment*>* next = use_it.Current()->GetNext();
+ if (next != nullptr && next->GetUser() == use) {
+ return true;
+ }
+ }
+
+ if (kIsDebugBuild) {
+ // Do a quadratic search to ensure same environment uses are next
+ // to each other.
+ for (HUseIterator<HEnvironment*> use_it(instruction->GetEnvUses());
+ !use_it.Done();
+ use_it.Advance()) {
+ HUseListNode<HEnvironment*>* current = use_it.Current();
+ HUseListNode<HEnvironment*>* next = current->GetNext();
+ while (next != nullptr) {
+ DCHECK(next->GetUser() != current->GetUser());
+ next = next->GetNext();
+ }
+ }
+ }
+ return false;
+}
+
void SsaBuilder::RemoveRedundantUninitializedStrings() {
if (GetGraph()->IsDebuggable()) {
// Do not perform the optimization for consistency with the interpreter
@@ -433,7 +461,7 @@
// 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()) {
+ if (!new_instance->HasNonEnvironmentUses() && !HasAliasInEnvironments(new_instance)) {
new_instance->ReplaceWith(GetGraph()->GetNullConstant());
new_instance->GetBlock()->RemoveInstruction(new_instance);
diff --git a/runtime/Android.mk b/runtime/Android.mk
index e9f7add..947de8a 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -238,6 +238,7 @@
# (empty) body is called.
JIT_DEBUG_REGISTER_CODE_LDFLAGS := -Wl,--keep-unique,__jit_debug_register_code
LIBART_TARGET_LDFLAGS_arm := $(JIT_DEBUG_REGISTER_CODE_LDFLAGS)
+LIBART_TARGET_LDFLAGS_arm64 := $(JIT_DEBUG_REGISTER_CODE_LDFLAGS)
LIBART_TARGET_LDFLAGS_x86 := $(JIT_DEBUG_REGISTER_CODE_LDFLAGS)
LIBART_TARGET_LDFLAGS_x86_64 := $(JIT_DEBUG_REGISTER_CODE_LDFLAGS)
JIT_DEBUG_REGISTER_CODE_LDFLAGS :=
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 078a978..f3e8d6b 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -132,6 +132,11 @@
return (GetAccessFlags() & kAccFinal) != 0;
}
+ // Returns true if this method might be copied from another class.
+ bool MightBeCopied() {
+ return IsMiranda() || IsDefault() || IsDefaultConflicting();
+ }
+
bool IsMiranda() {
return (GetAccessFlags() & kAccMiranda) != 0;
}
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 82a5f96..6972b3e 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -1009,10 +1009,6 @@
DCHECK(alloc_tracker_lock_ == nullptr);
alloc_tracker_lock_ = new Mutex("AllocTracker lock", current_lock_level);
- UPDATE_CURRENT_LOCK_LEVEL(kInterpreterStringInitMapLock);
- DCHECK(interpreter_string_init_map_lock_ == nullptr);
- interpreter_string_init_map_lock_ = new Mutex("Interpreter String initializer reference map lock", current_lock_level);
-
UPDATE_CURRENT_LOCK_LEVEL(kThreadListLock);
DCHECK(thread_list_lock_ == nullptr);
thread_list_lock_ = new Mutex("thread list lock", current_lock_level);
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index f674a6f..e72f2a2 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -102,7 +102,6 @@
kMonitorListLock,
kJniLoadLibraryLock,
kThreadListLock,
- kInterpreterStringInitMapLock,
kAllocTrackerLock,
kDeoptimizationLock,
kProfilerLock,
diff --git a/runtime/base/scoped_arena_containers.h b/runtime/base/scoped_arena_containers.h
index 9b56856..bd19d00 100644
--- a/runtime/base/scoped_arena_containers.h
+++ b/runtime/base/scoped_arena_containers.h
@@ -201,11 +201,12 @@
template <typename T>
class ArenaDelete {
static constexpr uint8_t kMagicFill = 0xCE;
+
protected:
// Used for variable sized objects such as RegisterLine.
ALWAYS_INLINE void ProtectMemory(T* ptr, size_t size) const {
if (RUNNING_ON_MEMORY_TOOL > 0) {
- // Writing to the memory will fail if it we already destroyed the pointer with
+ // Writing to the memory will fail ift we already destroyed the pointer with
// DestroyOnlyDelete since we make it no access.
memset(ptr, kMagicFill, size);
MEMORY_TOOL_MAKE_NOACCESS(ptr, size);
@@ -220,8 +221,10 @@
public:
void operator()(T* ptr) const {
- ptr->~T();
- ProtectMemory(ptr, sizeof(T));
+ if (ptr != nullptr) {
+ ptr->~T();
+ ProtectMemory(ptr, sizeof(T));
+ }
}
};
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 5278d1b..936c988 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -759,7 +759,7 @@
SHARED_REQUIRES(Locks::mutator_lock_) {
if (m->IsRuntimeMethod()) {
CHECK(m->GetDeclaringClass() == nullptr) << PrettyMethod(m);
- } else if (m->IsMiranda()) {
+ } else if (m->MightBeCopied()) {
CHECK(m->GetDeclaringClass() != nullptr) << PrettyMethod(m);
} else if (expected_class != nullptr) {
CHECK_EQ(m->GetDeclaringClassUnchecked(), expected_class) << PrettyMethod(m);
@@ -1137,18 +1137,18 @@
virtual void Visit(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_) {
GcRoot<mirror::Class>* resolved_types = method->GetDexCacheResolvedTypes(sizeof(void*));
- const bool is_miranda = method->IsMiranda();
+ const bool maybe_copied = method->MightBeCopied();
if (resolved_types != nullptr) {
bool in_image_space = false;
- if (kIsDebugBuild || is_miranda) {
+ if (kIsDebugBuild || maybe_copied) {
in_image_space = header_.GetImageSection(ImageHeader::kSectionDexCacheArrays).Contains(
reinterpret_cast<const uint8_t*>(resolved_types) - header_.GetImageBegin());
}
// Must be in image space for non-miranda method.
- DCHECK(is_miranda || in_image_space)
+ DCHECK(maybe_copied || in_image_space)
<< resolved_types << " is not in image starting at "
<< reinterpret_cast<void*>(header_.GetImageBegin());
- if (!is_miranda || in_image_space) {
+ if (!maybe_copied || in_image_space) {
// Go through the array so that we don't need to do a slow map lookup.
method->SetDexCacheResolvedTypes(*reinterpret_cast<GcRoot<mirror::Class>**>(resolved_types),
sizeof(void*));
@@ -1157,15 +1157,15 @@
ArtMethod** resolved_methods = method->GetDexCacheResolvedMethods(sizeof(void*));
if (resolved_methods != nullptr) {
bool in_image_space = false;
- if (kIsDebugBuild || is_miranda) {
+ if (kIsDebugBuild || maybe_copied) {
in_image_space = header_.GetImageSection(ImageHeader::kSectionDexCacheArrays).Contains(
reinterpret_cast<const uint8_t*>(resolved_methods) - header_.GetImageBegin());
}
// Must be in image space for non-miranda method.
- DCHECK(is_miranda || in_image_space)
+ DCHECK(maybe_copied || in_image_space)
<< resolved_methods << " is not in image starting at "
<< reinterpret_cast<void*>(header_.GetImageBegin());
- if (!is_miranda || in_image_space) {
+ if (!maybe_copied || in_image_space) {
// Go through the array so that we don't need to do a slow map lookup.
method->SetDexCacheResolvedMethods(*reinterpret_cast<ArtMethod***>(resolved_methods),
sizeof(void*));
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 3a0f3e5..4d528ed 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -263,7 +263,7 @@
for (ArtMethod& method : klass->GetCopiedMethods(sizeof(void*))) {
AssertMethod(&method);
EXPECT_FALSE(method.IsDirect());
- EXPECT_TRUE(method.IsMiranda() || method.IsDefault() || method.IsDefaultConflicting());
+ EXPECT_TRUE(method.MightBeCopied());
EXPECT_TRUE(method.GetDeclaringClass()->IsInterface())
<< "declaring class: " << PrettyClass(method.GetDeclaringClass());
EXPECT_TRUE(method.GetDeclaringClass()->IsAssignableFrom(klass.Get()))
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 8e1b7f4..d393f0b 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -1622,7 +1622,9 @@
inline void ConcurrentCopying::Scan(mirror::Object* to_ref) {
DCHECK(!region_space_->IsInFromSpace(to_ref));
ConcurrentCopyingRefFieldsVisitor visitor(this);
- to_ref->VisitReferences(visitor, visitor);
+ // Disable the read barrier for a performance reason.
+ to_ref->VisitReferences</*kVisitNativeRoots*/true, kDefaultVerifyFlags, kWithoutReadBarrier>(
+ visitor, visitor);
}
// Process a field.
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index cbaa817..3453abc 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -733,39 +733,21 @@
}
if (string_init && !self->IsExceptionPending()) {
- // Set the new string result of the StringFactory.
- shadow_frame.SetVRegReference(string_init_vreg_this, result->GetL());
- // Overwrite all potential copies of the original result of the new-instance of string with the
- // new result of the StringFactory. Use the verifier to find this set of registers.
- ArtMethod* method = shadow_frame.GetMethod();
- MethodReference method_ref = method->ToMethodReference();
- SafeMap<uint32_t, std::set<uint32_t>>* string_init_map_ptr = nullptr;
- MethodRefToStringInitRegMap& method_to_string_init_map = Runtime::Current()->GetStringInitMap();
- {
- MutexLock mu(self, *Locks::interpreter_string_init_map_lock_);
- auto it = method_to_string_init_map.find(method_ref);
- if (it != method_to_string_init_map.end()) {
- string_init_map_ptr = &it->second;
- }
- }
- if (string_init_map_ptr == nullptr) {
- SafeMap<uint32_t, std::set<uint32_t>> string_init_map =
- verifier::MethodVerifier::FindStringInitMap(method);
- MutexLock mu(self, *Locks::interpreter_string_init_map_lock_);
- auto it = method_to_string_init_map.lower_bound(method_ref);
- if (it == method_to_string_init_map.end() ||
- method_to_string_init_map.key_comp()(method_ref, it->first)) {
- it = method_to_string_init_map.PutBefore(it, method_ref, std::move(string_init_map));
- }
- string_init_map_ptr = &it->second;
- }
- if (string_init_map_ptr->size() != 0) {
- uint32_t dex_pc = shadow_frame.GetDexPC();
- auto map_it = string_init_map_ptr->find(dex_pc);
- if (map_it != string_init_map_ptr->end()) {
- const std::set<uint32_t>& reg_set = map_it->second;
- for (auto set_it = reg_set.begin(); set_it != reg_set.end(); ++set_it) {
- shadow_frame.SetVRegReference(*set_it, result->GetL());
+ mirror::Object* existing = shadow_frame.GetVRegReference(string_init_vreg_this);
+ if (existing == nullptr) {
+ // If it's null, we come from compiled code that was deoptimized. Nothing to do,
+ // as the compiler verified there was no alias.
+ // Set the new string result of the StringFactory.
+ shadow_frame.SetVRegReference(string_init_vreg_this, result->GetL());
+ } else {
+ // Replace the fake string that was allocated with the StringFactory result.
+ for (uint32_t i = 0; i < shadow_frame.NumberOfVRegs(); ++i) {
+ if (shadow_frame.GetVRegReference(i) == existing) {
+ DCHECK_EQ(shadow_frame.GetVRegReference(i),
+ reinterpret_cast<mirror::Object*>(shadow_frame.GetVReg(i)));
+ shadow_frame.SetVRegReference(i, result->GetL());
+ DCHECK_EQ(shadow_frame.GetVRegReference(i),
+ reinterpret_cast<mirror::Object*>(shadow_frame.GetVReg(i)));
}
}
}
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 422832e..3f806d3 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -532,8 +532,9 @@
return GetFieldPtr<LengthPrefixedArray<ArtField>*>(OFFSET_OF_OBJECT_MEMBER(Class, ifields_));
}
+template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
inline MemberOffset Class::GetFirstReferenceInstanceFieldOffset() {
- Class* super_class = GetSuperClass();
+ Class* super_class = GetSuperClass<kVerifyFlags, kReadBarrierOption>();
return (super_class != nullptr)
? MemberOffset(RoundUp(super_class->GetObjectSize(),
sizeof(mirror::HeapReference<mirror::Object>)))
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index cdc6204..9190e44 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -1048,5 +1048,11 @@
return depth;
}
+uint32_t Class::FindTypeIndexInOtherDexFile(const DexFile& dex_file) {
+ std::string temp;
+ const DexFile::TypeId* type_id = dex_file.FindTypeId(GetDescriptor(&temp));
+ return (type_id == nullptr) ? DexFile::kDexNoIndex : dex_file.GetIndexForTypeId(*type_id);
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 388a231..6e3463c 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -1006,6 +1006,8 @@
SHARED_REQUIRES(Locks::mutator_lock_);
// Get the offset of the first reference instance field. Other reference instance fields follow.
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
+ ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
MemberOffset GetFirstReferenceInstanceFieldOffset()
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -1119,6 +1121,9 @@
SetField32<false>(OFFSET_OF_OBJECT_MEMBER(Class, dex_type_idx_), type_idx);
}
+ uint32_t FindTypeIndexInOtherDexFile(const DexFile& dex_file)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
static Class* GetJavaLangClass() SHARED_REQUIRES(Locks::mutator_lock_) {
DCHECK(HasJavaLangClass());
return java_lang_Class_.Read();
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index eb391be4..76a36ac 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -1068,7 +1068,7 @@
MemberOffset field_offset = kIsStatic
? klass->GetFirstReferenceStaticFieldOffset<kVerifyFlags, kReadBarrierOption>(
Runtime::Current()->GetClassLinker()->GetImagePointerSize())
- : klass->GetFirstReferenceInstanceFieldOffset();
+ : klass->GetFirstReferenceInstanceFieldOffset<kVerifyFlags, kReadBarrierOption>();
for (size_t i = 0u; i < num_reference_fields; ++i) {
// TODO: Do a simpler check?
if (field_offset.Uint32Value() != ClassOffset().Uint32Value()) {
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 1956bae..cbb3e89 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -94,8 +94,6 @@
class Transaction;
typedef std::vector<std::pair<std::string, const void*>> RuntimeOptions;
-typedef SafeMap<MethodReference, SafeMap<uint32_t, std::set<uint32_t>>,
- MethodReferenceComparator> MethodRefToStringInitRegMap;
// Not all combinations of flags are valid. You may not visit all roots as well as the new roots
// (no logical reason to do this). You also may not start logging new roots and stop logging new
@@ -574,10 +572,6 @@
return jit_options_.get();
}
- MethodRefToStringInitRegMap& GetStringInitMap() {
- return method_ref_string_init_reg_map_;
- }
-
bool IsDebuggable() const;
// Returns the build fingerprint, if set. Otherwise an empty string is returned.
@@ -803,8 +797,6 @@
// Experimental opcodes should not be used by other production code.
ExperimentalFlags experimental_flags_;
- MethodRefToStringInitRegMap method_ref_string_init_reg_map_;
-
// Contains the build fingerprint, if given as a parameter.
std::string fingerprint_;
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index a6cf9ea..0c6060e 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -617,23 +617,6 @@
return GetQuickInvokedMethod(inst, register_line, is_range, false);
}
-SafeMap<uint32_t, std::set<uint32_t>> MethodVerifier::FindStringInitMap(ArtMethod* m) {
- Thread* self = Thread::Current();
- StackHandleScope<2> hs(self);
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(m->GetDexCache()));
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(m->GetClassLoader()));
- MethodVerifier verifier(self, m->GetDexFile(), dex_cache, class_loader, &m->GetClassDef(),
- m->GetCodeItem(), m->GetDexMethodIndex(), m, m->GetAccessFlags(),
- true, true, false, true);
- // Avoid copying: The map is moved out of the verifier before the verifier is destroyed.
- return std::move(verifier.FindStringInitMap());
-}
-
-SafeMap<uint32_t, std::set<uint32_t>>& MethodVerifier::FindStringInitMap() {
- Verify();
- return GetStringInitPcRegMap();
-}
-
bool MethodVerifier::Verify() {
// Some older code doesn't correctly mark constructors as such. Test for this case by looking at
// the name.
@@ -2865,8 +2848,7 @@
* Replace the uninitialized reference with an initialized one. We need to do this for all
* registers that have the same object instance in them, not just the "this" register.
*/
- const uint32_t this_reg = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c();
- work_line_->MarkRefsAsInitialized(this, this_type, this_reg, work_insn_idx_);
+ work_line_->MarkRefsAsInitialized(this, this_type);
}
if (return_type == nullptr) {
return_type = ®_types_.FromDescriptor(GetClassLoader(), return_type_descriptor, false);
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index b53a45c..6d8e1ab 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -213,9 +213,6 @@
static ArtMethod* FindInvokedMethodAtDexPc(ArtMethod* m, uint32_t dex_pc)
SHARED_REQUIRES(Locks::mutator_lock_);
- static SafeMap<uint32_t, std::set<uint32_t>> FindStringInitMap(ArtMethod* m)
- SHARED_REQUIRES(Locks::mutator_lock_);
-
static void Init() SHARED_REQUIRES(Locks::mutator_lock_);
static void Shutdown();
@@ -294,10 +291,6 @@
ArtField* GetQuickFieldAccess(const Instruction* inst, RegisterLine* reg_line)
SHARED_REQUIRES(Locks::mutator_lock_);
- SafeMap<uint32_t, std::set<uint32_t>>& GetStringInitPcRegMap() {
- return string_init_pc_reg_map_;
- }
-
uint32_t GetEncounteredFailureTypes() {
return encountered_failure_types_;
}
@@ -875,11 +868,6 @@
friend class art::Thread;
- // Map of dex pcs of invocations of java.lang.String.<init> to the set of other registers that
- // contain the uninitialized this pointer to that invoke. Will contain no entry if there are
- // no other registers.
- SafeMap<uint32_t, std::set<uint32_t>> string_init_pc_reg_map_;
-
DISALLOW_COPY_AND_ASSIGN(MethodVerifier);
};
std::ostream& operator<<(std::ostream& os, const MethodVerifier::FailureKind& rhs);
diff --git a/runtime/verifier/register_line-inl.h b/runtime/verifier/register_line-inl.h
index bfbb78f..29d87c4 100644
--- a/runtime/verifier/register_line-inl.h
+++ b/runtime/verifier/register_line-inl.h
@@ -204,9 +204,10 @@
}
inline void RegisterLineArenaDelete::operator()(RegisterLine* ptr) const {
- const size_t size = ptr != nullptr ? RegisterLine::ComputeSize(ptr->NumRegs()) : 0u;
- ptr->~RegisterLine();
- ProtectMemory(ptr, size);
+ if (ptr != nullptr) {
+ ptr->~RegisterLine();
+ ProtectMemory(ptr, RegisterLine::ComputeSize(ptr->NumRegs()));
+ }
}
} // namespace verifier
diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc
index b7cde99..82c371d 100644
--- a/runtime/verifier/register_line.cc
+++ b/runtime/verifier/register_line.cc
@@ -91,25 +91,14 @@
return true;
}
-void RegisterLine::MarkRefsAsInitialized(MethodVerifier* verifier, const RegType& uninit_type,
- uint32_t this_reg, uint32_t dex_pc) {
+void RegisterLine::MarkRefsAsInitialized(MethodVerifier* verifier, const RegType& uninit_type) {
DCHECK(uninit_type.IsUninitializedTypes());
- bool is_string = !uninit_type.IsUnresolvedTypes() && uninit_type.GetClass()->IsStringClass();
const RegType& init_type = verifier->GetRegTypeCache()->FromUninitialized(uninit_type);
size_t changed = 0;
for (uint32_t i = 0; i < num_regs_; i++) {
if (GetRegisterType(verifier, i).Equals(uninit_type)) {
line_[i] = init_type.GetId();
changed++;
- if (is_string && i != this_reg) {
- auto it = verifier->GetStringInitPcRegMap().find(dex_pc);
- if (it != verifier->GetStringInitPcRegMap().end()) {
- it->second.insert(i);
- } else {
- std::set<uint32_t> reg_set = { i };
- verifier->GetStringInitPcRegMap().Put(dex_pc, reg_set);
- }
- }
}
}
// Is this initializing "this"?
diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h
index d508454..15ae202 100644
--- a/runtime/verifier/register_line.h
+++ b/runtime/verifier/register_line.h
@@ -99,11 +99,14 @@
// available now. An example is sharpening types after a check-cast. Note that when given kKeep,
// the new_type is dchecked to be a reference type.
template <LockOp kLockOp>
- ALWAYS_INLINE bool SetRegisterType(MethodVerifier* verifier, uint32_t vdst,
+ ALWAYS_INLINE bool SetRegisterType(MethodVerifier* verifier,
+ uint32_t vdst,
const RegType& new_type)
SHARED_REQUIRES(Locks::mutator_lock_);
- bool SetRegisterTypeWide(MethodVerifier* verifier, uint32_t vdst, const RegType& new_type1,
+ bool SetRegisterTypeWide(MethodVerifier* verifier,
+ uint32_t vdst,
+ const RegType& new_type1,
const RegType& new_type2)
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -117,11 +120,14 @@
// Get the type of register vsrc.
const RegType& GetRegisterType(MethodVerifier* verifier, uint32_t vsrc) const;
- ALWAYS_INLINE bool VerifyRegisterType(MethodVerifier* verifier, uint32_t vsrc,
+ ALWAYS_INLINE bool VerifyRegisterType(MethodVerifier* verifier,
+ uint32_t vsrc,
const RegType& check_type)
SHARED_REQUIRES(Locks::mutator_lock_);
- bool VerifyRegisterTypeWide(MethodVerifier* verifier, uint32_t vsrc, const RegType& check_type1,
+ bool VerifyRegisterTypeWide(MethodVerifier* verifier,
+ uint32_t vsrc,
+ const RegType& check_type1,
const RegType& check_type2)
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -155,8 +161,7 @@
* reference type. This is called when an appropriate constructor is invoked -- all copies of
* the reference must be marked as initialized.
*/
- void MarkRefsAsInitialized(MethodVerifier* verifier, const RegType& uninit_type,
- uint32_t this_reg, uint32_t dex_pc)
+ void MarkRefsAsInitialized(MethodVerifier* verifier, const RegType& uninit_type)
SHARED_REQUIRES(Locks::mutator_lock_);
/*
@@ -210,31 +215,42 @@
* allow_failure will return Conflict() instead of causing a verification failure if there is an
* error.
*/
- const RegType& GetInvocationThis(MethodVerifier* verifier, const Instruction* inst,
- bool is_range, bool allow_failure = false)
+ const RegType& GetInvocationThis(MethodVerifier* verifier,
+ const Instruction* inst,
+ bool is_range,
+ bool allow_failure = false)
SHARED_REQUIRES(Locks::mutator_lock_);
/*
* Verify types for a simple two-register instruction (e.g. "neg-int").
* "dst_type" is stored into vA, and "src_type" is verified against vB.
*/
- void CheckUnaryOp(MethodVerifier* verifier, const Instruction* inst, const RegType& dst_type,
+ void CheckUnaryOp(MethodVerifier* verifier,
+ const Instruction* inst,
+ const RegType& dst_type,
const RegType& src_type)
SHARED_REQUIRES(Locks::mutator_lock_);
- void CheckUnaryOpWide(MethodVerifier* verifier, const Instruction* inst,
- const RegType& dst_type1, const RegType& dst_type2,
- const RegType& src_type1, const RegType& src_type2)
+ void CheckUnaryOpWide(MethodVerifier* verifier,
+ const Instruction* inst,
+ const RegType& dst_type1,
+ const RegType& dst_type2,
+ const RegType& src_type1,
+ const RegType& src_type2)
SHARED_REQUIRES(Locks::mutator_lock_);
- void CheckUnaryOpToWide(MethodVerifier* verifier, const Instruction* inst,
- const RegType& dst_type1, const RegType& dst_type2,
+ void CheckUnaryOpToWide(MethodVerifier* verifier,
+ const Instruction* inst,
+ const RegType& dst_type1,
+ const RegType& dst_type2,
const RegType& src_type)
SHARED_REQUIRES(Locks::mutator_lock_);
- void CheckUnaryOpFromWide(MethodVerifier* verifier, const Instruction* inst,
+ void CheckUnaryOpFromWide(MethodVerifier* verifier,
+ const Instruction* inst,
const RegType& dst_type,
- const RegType& src_type1, const RegType& src_type2)
+ const RegType& src_type1,
+ const RegType& src_type2)
SHARED_REQUIRES(Locks::mutator_lock_);
/*
@@ -242,19 +258,28 @@
* "dst_type" is stored into vA, and "src_type1"/"src_type2" are verified
* against vB/vC.
*/
- void CheckBinaryOp(MethodVerifier* verifier, const Instruction* inst,
- const RegType& dst_type, const RegType& src_type1, const RegType& src_type2,
+ void CheckBinaryOp(MethodVerifier* verifier,
+ const Instruction* inst,
+ const RegType& dst_type,
+ const RegType& src_type1,
+ const RegType& src_type2,
bool check_boolean_op)
SHARED_REQUIRES(Locks::mutator_lock_);
- void CheckBinaryOpWide(MethodVerifier* verifier, const Instruction* inst,
- const RegType& dst_type1, const RegType& dst_type2,
- const RegType& src_type1_1, const RegType& src_type1_2,
- const RegType& src_type2_1, const RegType& src_type2_2)
+ void CheckBinaryOpWide(MethodVerifier* verifier,
+ const Instruction* inst,
+ const RegType& dst_type1,
+ const RegType& dst_type2,
+ const RegType& src_type1_1,
+ const RegType& src_type1_2,
+ const RegType& src_type2_1,
+ const RegType& src_type2_2)
SHARED_REQUIRES(Locks::mutator_lock_);
- void CheckBinaryOpWideShift(MethodVerifier* verifier, const Instruction* inst,
- const RegType& long_lo_type, const RegType& long_hi_type,
+ void CheckBinaryOpWideShift(MethodVerifier* verifier,
+ const Instruction* inst,
+ const RegType& long_lo_type,
+ const RegType& long_hi_type,
const RegType& int_type)
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -262,20 +287,28 @@
* Verify types for a binary "2addr" operation. "src_type1"/"src_type2"
* are verified against vA/vB, then "dst_type" is stored into vA.
*/
- void CheckBinaryOp2addr(MethodVerifier* verifier, const Instruction* inst,
+ void CheckBinaryOp2addr(MethodVerifier* verifier,
+ const Instruction* inst,
const RegType& dst_type,
- const RegType& src_type1, const RegType& src_type2,
+ const RegType& src_type1,
+ const RegType& src_type2,
bool check_boolean_op)
SHARED_REQUIRES(Locks::mutator_lock_);
- void CheckBinaryOp2addrWide(MethodVerifier* verifier, const Instruction* inst,
- const RegType& dst_type1, const RegType& dst_type2,
- const RegType& src_type1_1, const RegType& src_type1_2,
- const RegType& src_type2_1, const RegType& src_type2_2)
+ void CheckBinaryOp2addrWide(MethodVerifier* verifier,
+ const Instruction* inst,
+ const RegType& dst_type1,
+ const RegType& dst_type2,
+ const RegType& src_type1_1,
+ const RegType& src_type1_2,
+ const RegType& src_type2_1,
+ const RegType& src_type2_2)
SHARED_REQUIRES(Locks::mutator_lock_);
- void CheckBinaryOp2addrWideShift(MethodVerifier* verifier, const Instruction* inst,
- const RegType& long_lo_type, const RegType& long_hi_type,
+ void CheckBinaryOp2addrWideShift(MethodVerifier* verifier,
+ const Instruction* inst,
+ const RegType& long_lo_type,
+ const RegType& long_hi_type,
const RegType& int_type)
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -285,9 +318,12 @@
*
* If "check_boolean_op" is set, we use the constant value in vC.
*/
- void CheckLiteralOp(MethodVerifier* verifier, const Instruction* inst,
- const RegType& dst_type, const RegType& src_type,
- bool check_boolean_op, bool is_lit16)
+ void CheckLiteralOp(MethodVerifier* verifier,
+ const Instruction* inst,
+ const RegType& dst_type,
+ const RegType& src_type,
+ bool check_boolean_op,
+ bool is_lit16)
SHARED_REQUIRES(Locks::mutator_lock_);
// Verify/push monitor onto the monitor stack, locking the value in reg_idx at location insn_idx.
@@ -409,8 +445,6 @@
void operator()(RegisterLine* ptr) const;
};
-
-
} // namespace verifier
} // namespace art
diff --git a/test/575-checker-isnan/expected.txt b/test/575-checker-isnan/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/575-checker-isnan/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/575-checker-isnan/info.txt b/test/575-checker-isnan/info.txt
new file mode 100644
index 0000000..5c48a6a
--- /dev/null
+++ b/test/575-checker-isnan/info.txt
@@ -0,0 +1 @@
+Unit test for float/double isNaN() operation.
diff --git a/test/575-checker-isnan/src/Main.java b/test/575-checker-isnan/src/Main.java
new file mode 100644
index 0000000..cc71e5e
--- /dev/null
+++ b/test/575-checker-isnan/src/Main.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+public class Main {
+
+ /// CHECK-START: boolean Main.isNaN32(float) instruction_simplifier (before)
+ /// CHECK-DAG: <<Result:z\d+>> InvokeStaticOrDirect
+ /// CHECK-DAG: Return [<<Result>>]
+ //
+ /// CHECK-START: boolean Main.isNaN32(float) instruction_simplifier (after)
+ /// CHECK-DAG: <<Result:z\d+>> NotEqual
+ /// CHECK-DAG: Return [<<Result>>]
+ //
+ /// CHECK-START: boolean Main.isNaN32(float) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ private static boolean isNaN32(float x) {
+ return Float.isNaN(x);
+ }
+
+ /// CHECK-START: boolean Main.isNaN64(double) instruction_simplifier (before)
+ /// CHECK-DAG: <<Result:z\d+>> InvokeStaticOrDirect
+ /// CHECK-DAG: Return [<<Result>>]
+ //
+ /// CHECK-START: boolean Main.isNaN64(double) instruction_simplifier (after)
+ /// CHECK-DAG: <<Result:z\d+>> NotEqual
+ /// CHECK-DAG: Return [<<Result>>]
+ //
+ /// CHECK-START: boolean Main.isNaN64(double) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+ private static boolean isNaN64(double x) {
+ return Double.isNaN(x);
+ }
+
+ public static void main(String args[]) {
+ // A few distinct numbers.
+ expectFalse(isNaN32(Float.NEGATIVE_INFINITY));
+ expectFalse(isNaN32(-1.0f));
+ expectFalse(isNaN32(-0.0f));
+ expectFalse(isNaN32(0.0f));
+ expectFalse(isNaN32(1.0f));
+ expectFalse(isNaN32(Float.POSITIVE_INFINITY));
+
+ // A few distinct subnormal numbers.
+ expectFalse(isNaN32(Float.intBitsToFloat(0x00400000)));
+ expectFalse(isNaN32(Float.intBitsToFloat(0x80400000)));
+ expectFalse(isNaN32(Float.intBitsToFloat(0x00000001)));
+ expectFalse(isNaN32(Float.intBitsToFloat(0x80000001)));
+
+ // A few NaN numbers.
+ expectTrue(isNaN32(Float.NaN));
+ expectTrue(isNaN32(0.0f / 0.0f));
+ expectTrue(isNaN32((float)Math.sqrt(-1.0f)));
+ float[] fvals = {
+ Float.intBitsToFloat(0x7f800001),
+ Float.intBitsToFloat(0x7fa00000),
+ Float.intBitsToFloat(0x7fc00000),
+ Float.intBitsToFloat(0x7fffffff),
+ Float.intBitsToFloat(0xff800001),
+ Float.intBitsToFloat(0xffa00000),
+ Float.intBitsToFloat(0xffc00000),
+ Float.intBitsToFloat(0xffffffff)
+ };
+ for (int i = 0; i < fvals.length; i++) {
+ expectTrue(isNaN32(fvals[i]));
+ }
+
+ // A few distinct numbers.
+ expectFalse(isNaN64(Double.NEGATIVE_INFINITY));
+ expectFalse(isNaN32(-1.0f));
+ expectFalse(isNaN64(-0.0d));
+ expectFalse(isNaN64(0.0d));
+ expectFalse(isNaN64(1.0d));
+ expectFalse(isNaN64(Double.POSITIVE_INFINITY));
+
+ // A few distinct subnormal numbers.
+ expectFalse(isNaN64(Double.longBitsToDouble(0x0008000000000000l)));
+ expectFalse(isNaN64(Double.longBitsToDouble(0x8008000000000000l)));
+ expectFalse(isNaN64(Double.longBitsToDouble(0x0000000000000001l)));
+ expectFalse(isNaN64(Double.longBitsToDouble(0x8000000000000001l)));
+
+ // A few NaN numbers.
+ expectTrue(isNaN64(Double.NaN));
+ expectTrue(isNaN64(0.0d / 0.0d));
+ expectTrue(isNaN64(Math.sqrt(-1.0d)));
+ double[] dvals = {
+ Double.longBitsToDouble(0x7ff0000000000001L),
+ Double.longBitsToDouble(0x7ff4000000000000L),
+ Double.longBitsToDouble(0x7ff8000000000000L),
+ Double.longBitsToDouble(0x7fffffffffffffffL),
+ Double.longBitsToDouble(0xfff0000000000001L),
+ Double.longBitsToDouble(0xfff4000000000000L),
+ Double.longBitsToDouble(0xfff8000000000000L),
+ Double.longBitsToDouble(0xffffffffffffffffL)
+ };
+ for (int i = 0; i < dvals.length; i++) {
+ expectTrue(isNaN64(dvals[i]));
+ }
+
+ System.out.println("passed");
+ }
+
+ private static void expectTrue(boolean value) {
+ if (!value) {
+ throw new Error("Expected True");
+ }
+ }
+
+ private static void expectFalse(boolean value) {
+ if (value) {
+ throw new Error("Expected False");
+ }
+ }
+}
diff --git a/test/575-checker-string-init-alias/expected.txt b/test/575-checker-string-init-alias/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/575-checker-string-init-alias/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/575-checker-string-init-alias/info.txt b/test/575-checker-string-init-alias/info.txt
new file mode 100644
index 0000000..a91ea64
--- /dev/null
+++ b/test/575-checker-string-init-alias/info.txt
@@ -0,0 +1,2 @@
+Test for the String.<init> change and deoptimization: make
+sure the compiler knows how to handle dex aliases.
diff --git a/test/575-checker-string-init-alias/smali/TestCase.smali b/test/575-checker-string-init-alias/smali/TestCase.smali
new file mode 100644
index 0000000..ff04b27
--- /dev/null
+++ b/test/575-checker-string-init-alias/smali/TestCase.smali
@@ -0,0 +1,72 @@
+# Copyright (C) 2016 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;
+
+.field public static staticField:Ljava/lang/String;
+
+## CHECK-START: void TestCase.testNoAlias(int[], java.lang.String) register (after)
+## CHECK: <<Null:l\d+>> NullConstant
+## CHECK: Deoptimize env:[[<<Null>>,{{.*]]}}
+## CHECK: InvokeStaticOrDirect method_name:java.lang.String.<init>
+.method public static testNoAlias([ILjava/lang/String;)V
+ .registers 6
+ const v1, 0
+ const v2, 1
+ new-instance v0, Ljava/lang/String;
+
+ # Will deoptimize.
+ aget v3, p0, v1
+
+ # Check that we're being executed by the interpreter.
+ invoke-static {}, LMain;->assertIsInterpreted()V
+
+ invoke-direct {v0, p1}, Ljava/lang/String;-><init>(Ljava/lang/String;)V
+
+ sput-object v0, LTestCase;->staticField:Ljava/lang/String;
+
+ # Will throw AIOOBE.
+ aget v3, p0, v2
+
+ return-void
+.end method
+
+## CHECK-START: void TestCase.testAlias(int[], java.lang.String) register (after)
+## CHECK: <<New:l\d+>> NewInstance
+## CHECK: Deoptimize env:[[<<New>>,<<New>>,{{.*]]}}
+## CHECK: InvokeStaticOrDirect method_name:java.lang.String.<init>
+.method public static testAlias([ILjava/lang/String;)V
+ .registers 7
+ const v2, 0
+ const v3, 1
+ new-instance v0, Ljava/lang/String;
+ move-object v1, v0
+
+ # Will deoptimize.
+ aget v4, p0, v2
+
+ # Check that we're being executed by the interpreter.
+ invoke-static {}, LMain;->assertIsInterpreted()V
+
+ invoke-direct {v1, p1}, Ljava/lang/String;-><init>(Ljava/lang/String;)V
+
+ sput-object v1, LTestCase;->staticField:Ljava/lang/String;
+
+ # Will throw AIOOBE.
+ aget v4, p0, v3
+
+ return-void
+.end method
diff --git a/test/575-checker-string-init-alias/src/Main.java b/test/575-checker-string-init-alias/src/Main.java
new file mode 100644
index 0000000..1ab3207
--- /dev/null
+++ b/test/575-checker-string-init-alias/src/Main.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 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.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+public class Main {
+ // Workaround for b/18051191.
+ class Inner {}
+
+ public static native void assertIsInterpreted();
+
+ private static void assertEqual(String expected, String actual) {
+ if (!expected.equals(actual)) {
+ throw new Error("Assertion failed: " + expected + " != " + actual);
+ }
+ }
+
+ public static void main(String[] args) throws Throwable {
+ System.loadLibrary(args[0]);
+ Class<?> c = Class.forName("TestCase");
+ int[] array = new int[1];
+
+ {
+ Method m = c.getMethod("testNoAlias", int[].class, String.class);
+ try {
+ m.invoke(null, new Object[] { array , "foo" });
+ throw new Error("Expected AIOOBE");
+ } catch (InvocationTargetException e) {
+ if (!(e.getCause() instanceof ArrayIndexOutOfBoundsException)) {
+ throw new Error("Expected AIOOBE");
+ }
+ // Ignore
+ }
+ Field field = c.getField("staticField");
+ assertEqual("foo", (String)field.get(null));
+ }
+
+ {
+ Method m = c.getMethod("testAlias", int[].class, String.class);
+ try {
+ m.invoke(null, new Object[] { array, "bar" });
+ throw new Error("Expected AIOOBE");
+ } catch (InvocationTargetException e) {
+ if (!(e.getCause() instanceof ArrayIndexOutOfBoundsException)) {
+ throw new Error("Expected AIOOBE");
+ }
+ // Ignore
+ }
+ Field field = c.getField("staticField");
+ assertEqual("bar", (String)field.get(null));
+ }
+ }
+}
diff --git a/test/576-polymorphic-inlining/expected.txt b/test/576-polymorphic-inlining/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/576-polymorphic-inlining/expected.txt
diff --git a/test/576-polymorphic-inlining/info.txt b/test/576-polymorphic-inlining/info.txt
new file mode 100644
index 0000000..b3ef0c8
--- /dev/null
+++ b/test/576-polymorphic-inlining/info.txt
@@ -0,0 +1 @@
+Test for polymorphic inlining.
diff --git a/test/576-polymorphic-inlining/src/Main.java b/test/576-polymorphic-inlining/src/Main.java
new file mode 100644
index 0000000..d8d09af
--- /dev/null
+++ b/test/576-polymorphic-inlining/src/Main.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+public class Main {
+ public static void main(String[] args) {
+ for (int i = 0; i < 20000; ++i) {
+ $noinline$testVoid(new Main());
+ $noinline$testVoid(new SubMain());
+ $noinline$testVoid(new SubSubMain());
+
+ $noinline$testWithReturnValue(new Main());
+ $noinline$testWithReturnValue(new SubMain());
+ $noinline$testWithReturnValue(new SubSubMain());
+
+ $noinline$testWithBackEdge(new Main());
+ $noinline$testWithBackEdge(new SubMain());
+ $noinline$testWithBackEdge(new SubSubMain());
+ }
+ }
+
+ public static void assertIdentical(Object expected, Object actual) {
+ if (expected != actual) {
+ throw new Error("Expected " + expected + ", got " + actual);
+ }
+ }
+
+ public static void $noinline$testVoid(Main m) {
+ if (doThrow) throw new Error("");
+ m.willInlineVoid();
+ m.willOnlyInlineForMainVoid();
+ }
+
+ public static void $noinline$testWithReturnValue(Main m) {
+ if (doThrow) throw new Error("");
+ assertIdentical(m.getClass(), m.willInlineWithReturnValue());
+ assertIdentical(m.getClass(), m.willOnlyInlineForMainWithReturnValue());
+ }
+
+ public static void $noinline$testWithBackEdge(Main m) {
+ if (doThrow) throw new Error("");
+ for (int i = 0; i < 10; ++i) {
+ m.willInlineVoid();
+ }
+ for (int i = 0; i < 10; ++i) {
+ m.willOnlyInlineForMainVoid();
+ }
+ }
+
+ public void willInlineVoid() {
+ }
+
+ public void willOnlyInlineForMainVoid() {
+ }
+
+ public Class willInlineWithReturnValue() {
+ return Main.class;
+ }
+
+ public Class willOnlyInlineForMainWithReturnValue() {
+ return Main.class;
+ }
+ public static boolean doThrow;
+}
+
+class SubMain extends Main {
+ public void willOnlyInlineForMainVoid() {
+ if (doThrow) throw new Error("");
+ }
+
+ public void willInlineVoid() {
+ }
+
+ public Class willInlineWithReturnValue() {
+ return SubMain.class;
+ }
+
+ public Class willOnlyInlineForMainWithReturnValue() {
+ return SubMain.class;
+ }
+}
+
+class SubSubMain extends SubMain {
+ public Class willInlineWithReturnValue() {
+ return SubSubMain.class;
+ }
+
+ public Class willOnlyInlineForMainWithReturnValue() {
+ return SubSubMain.class;
+ }
+}