diff options
32 files changed, 1239 insertions, 885 deletions
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index be8641fd86..68155d844a 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -26,6 +26,7 @@ #include "base/mutex.h" #include "compiled_method.h" #include "dex/bytecode_utils.h" +#include "dex/class_accessor-inl.h" #include "dex/dex_file-inl.h" #include "dex/dex_instruction-inl.h" #include "dex_to_dex_decompiler.h" @@ -633,21 +634,14 @@ void DexToDexCompiler::SetDexFiles(const std::vector<const DexFile*>& dex_files) // item. std::unordered_set<const DexFile::CodeItem*> seen_code_items; for (const DexFile* dex_file : dex_files) { - for (size_t i = 0; i < dex_file->NumClassDefs(); ++i) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); - const uint8_t* class_data = dex_file->GetClassData(class_def); - if (class_data == nullptr) { - continue; - } - ClassDataItemIterator it(*dex_file, class_data); - it.SkipAllFields(); - for (; it.HasNextMethod(); it.Next()) { - const DexFile::CodeItem* code_item = it.GetMethodCodeItem(); + for (ClassAccessor accessor : dex_file->GetClasses()) { + accessor.VisitMethods([&](const ClassAccessor::Method& method) { + const DexFile::CodeItem* code_item = method.GetCodeItem(); // Detect the shared code items. if (!seen_code_items.insert(code_item).second) { shared_code_items_.insert(code_item); } - } + }); } } VLOG(compiler) << "Shared code items " << shared_code_items_.size(); diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 7dc44fa44b..1b809d232a 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -42,6 +42,7 @@ #include "compiler.h" #include "compiler_callbacks.h" #include "compiler_driver-inl.h" +#include "dex/class_accessor-inl.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_annotations.h" @@ -771,39 +772,6 @@ void CompilerDriver::Resolve(jobject class_loader, } } -// Resolve const-strings in the code. Done to have deterministic allocation behavior. Right now -// this is single-threaded for simplicity. -// TODO: Collect the relevant string indices in parallel, then allocate them sequentially in a -// stable order. - -static void ResolveConstStrings(ClassLinker* class_linker, - Handle<mirror::DexCache> dex_cache, - const DexFile& dex_file, - const DexFile::CodeItem* code_item) - REQUIRES_SHARED(Locks::mutator_lock_) { - if (code_item == nullptr) { - // Abstract or native method. - return; - } - - for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(dex_file, code_item)) { - switch (inst->Opcode()) { - case Instruction::CONST_STRING: - case Instruction::CONST_STRING_JUMBO: { - dex::StringIndex string_index((inst->Opcode() == Instruction::CONST_STRING) - ? inst->VRegB_21c() - : inst->VRegB_31c()); - ObjPtr<mirror::String> string = class_linker->ResolveString(string_index, dex_cache); - CHECK(string != nullptr) << "Could not allocate a string when forcing determinism"; - break; - } - - default: - break; - } - } -} - static void ResolveConstStrings(CompilerDriver* driver, const std::vector<const DexFile*>& dex_files, TimingLogger* timings) { @@ -816,33 +784,35 @@ static void ResolveConstStrings(CompilerDriver* driver, dex_cache.Assign(class_linker->FindDexCache(soa.Self(), *dex_file)); TimingLogger::ScopedTiming t("Resolve const-string Strings", timings); - size_t class_def_count = dex_file->NumClassDefs(); - for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); - - const uint8_t* class_data = dex_file->GetClassData(class_def); - if (class_data == nullptr) { - // empty class, probably a marker interface - continue; - } - - ClassDataItemIterator it(*dex_file, class_data); - it.SkipAllFields(); - - bool compilation_enabled = driver->IsClassToCompile( - dex_file->StringByTypeIdx(class_def.class_idx_)); - if (!compilation_enabled) { + for (ClassAccessor accessor : dex_file->GetClasses()) { + if (!driver->IsClassToCompile(accessor.GetDescriptor())) { // Compilation is skipped, do not resolve const-string in code of this class. // FIXME: Make sure that inlining honors this. b/26687569 continue; } + accessor.VisitMethods([&](const ClassAccessor::Method& method) + REQUIRES_SHARED(Locks::mutator_lock_) { + // Resolve const-strings in the code. Done to have deterministic allocation behavior. Right + // now this is single-threaded for simplicity. + // TODO: Collect the relevant string indices in parallel, then allocate them sequentially + // in a stable order. + for (const DexInstructionPcPair& inst : method.GetInstructions()) { + switch (inst->Opcode()) { + case Instruction::CONST_STRING: + case Instruction::CONST_STRING_JUMBO: { + dex::StringIndex string_index((inst->Opcode() == Instruction::CONST_STRING) + ? inst->VRegB_21c() + : inst->VRegB_31c()); + ObjPtr<mirror::String> string = class_linker->ResolveString(string_index, dex_cache); + CHECK(string != nullptr) << "Could not allocate a string when forcing determinism"; + break; + } - // Direct and virtual methods. - while (it.HasNextMethod()) { - ResolveConstStrings(class_linker, dex_cache, *dex_file, it.GetMethodCodeItem()); - it.Next(); - } - DCHECK(!it.HasNext()); + default: + break; + } + } + }); } } } @@ -856,14 +826,9 @@ static void InitializeTypeCheckBitstrings(CompilerDriver* driver, ClassLinker* class_linker, Handle<mirror::DexCache> dex_cache, const DexFile& dex_file, - const DexFile::CodeItem* code_item) + const ClassAccessor::Method& method) REQUIRES_SHARED(Locks::mutator_lock_) { - if (code_item == nullptr) { - // Abstract or native method. - return; - } - - for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(dex_file, code_item)) { + for (const DexInstructionPcPair& inst : method.GetInstructions()) { switch (inst->Opcode()) { case Instruction::CHECK_CAST: case Instruction::INSTANCE_OF: { @@ -907,34 +872,18 @@ static void InitializeTypeCheckBitstrings(CompilerDriver* driver, dex_cache.Assign(class_linker->FindDexCache(soa.Self(), *dex_file)); TimingLogger::ScopedTiming t("Initialize type check bitstrings", timings); - size_t class_def_count = dex_file->NumClassDefs(); - for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); - - const uint8_t* class_data = dex_file->GetClassData(class_def); - if (class_data == nullptr) { - // empty class, probably a marker interface - continue; - } - - ClassDataItemIterator it(*dex_file, class_data); - it.SkipAllFields(); - - bool compilation_enabled = driver->IsClassToCompile( - dex_file->StringByTypeIdx(class_def.class_idx_)); - if (!compilation_enabled) { + for (ClassAccessor accessor : dex_file->GetClasses()) { + if (!driver->IsClassToCompile(accessor.GetDescriptor())) { // Compilation is skipped, do not look for type checks in code of this class. // FIXME: Make sure that inlining honors this. b/26687569 continue; } // Direct and virtual methods. - while (it.HasNextMethod()) { - InitializeTypeCheckBitstrings( - driver, class_linker, dex_cache, *dex_file, it.GetMethodCodeItem()); - it.Next(); - } - DCHECK(!it.HasNext()); + accessor.VisitMethods([&](const ClassAccessor::Method& method) + REQUIRES_SHARED(Locks::mutator_lock_) { + InitializeTypeCheckBitstrings(driver, class_linker, dex_cache, *dex_file, method); + }); } } } @@ -954,10 +903,8 @@ static void EnsureVerifiedOrVerifyAtRuntime(jobject jclass_loader, ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); for (const DexFile* dex_file : dex_files) { - for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); - const char* descriptor = dex_file->GetClassDescriptor(class_def); - cls.Assign(class_linker->FindClass(soa.Self(), descriptor, class_loader)); + for (ClassAccessor accessor : dex_file->GetClasses()) { + cls.Assign(class_linker->FindClass(soa.Self(), accessor.GetDescriptor(), class_loader)); if (cls == nullptr) { soa.Self()->ClearException(); } else if (&cls->GetDexFile() == dex_file) { @@ -1740,22 +1687,16 @@ static void CheckAndClearResolveException(Thread* self) bool CompilerDriver::RequiresConstructorBarrier(const DexFile& dex_file, uint16_t class_def_idx) const { - const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_idx); - const uint8_t* class_data = dex_file.GetClassData(class_def); - if (class_data == nullptr) { - // Empty class such as a marker interface. - return false; - } - ClassDataItemIterator it(dex_file, class_data); - it.SkipStaticFields(); + ClassAccessor accessor(dex_file, dex_file.GetClassDef(class_def_idx)); + bool has_is_final = false; // We require a constructor barrier if there are final instance fields. - while (it.HasNextInstanceField()) { - if (it.MemberIsFinal()) { - return true; + accessor.VisitFields(/*static*/ VoidFunctor(), + [&](const ClassAccessor::Field& field) { + if (field.IsFinal()) { + has_is_final = true; } - it.Next(); - } - return false; + }); + return has_is_final; } class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { @@ -1770,11 +1711,6 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { const DexFile& dex_file = *manager_->GetDexFile(); ClassLinker* class_linker = manager_->GetClassLinker(); - // If an instance field is final then we need to have a barrier on the return, static final - // fields are assigned within the lock held for class initialization. Conservatively assume - // constructor barriers are always required. - bool requires_constructor_barrier = true; - // Method and Field are the worst. We can't resolve without either // context from the code use (to disambiguate virtual vs direct // method and instance vs static field) or from class @@ -1806,56 +1742,53 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { // We want to resolve the methods and fields eagerly. resolve_fields_and_methods = true; } - // Note the class_data pointer advances through the headers, - // static fields, instance fields, direct methods, and virtual - // methods. - const uint8_t* class_data = dex_file.GetClassData(class_def); - if (class_data == nullptr) { - // Empty class such as a marker interface. - requires_constructor_barrier = false; - } else { - ClassDataItemIterator it(dex_file, class_data); - while (it.HasNextStaticField()) { - if (resolve_fields_and_methods) { - ArtField* field = class_linker->ResolveField( - it.GetMemberIndex(), dex_cache, class_loader, /* is_static */ true); - if (field == nullptr) { - CheckAndClearResolveException(soa.Self()); - } + // If an instance field is final then we need to have a barrier on the return, static final + // fields are assigned within the lock held for class initialization. + bool requires_constructor_barrier = false; + + ClassAccessor accessor(dex_file, class_def); + // Optionally resolve fields and methods and figure out if we need a constructor barrier. + auto method_visitor = [&](const ClassAccessor::Method& method) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (resolve_fields_and_methods) { + ArtMethod* resolved = class_linker->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( + method.GetIndex(), + dex_cache, + class_loader, + /* referrer */ nullptr, + method.GetInvokeType(class_def.access_flags_)); + if (resolved == nullptr) { + CheckAndClearResolveException(soa.Self()); } - it.Next(); } - // We require a constructor barrier if there are final instance fields. - requires_constructor_barrier = false; - while (it.HasNextInstanceField()) { - if (it.MemberIsFinal()) { - requires_constructor_barrier = true; - } - if (resolve_fields_and_methods) { - ArtField* field = class_linker->ResolveField( - it.GetMemberIndex(), dex_cache, class_loader, /* is_static */ false); - if (field == nullptr) { - CheckAndClearResolveException(soa.Self()); + }; + accessor.VisitFieldsAndMethods( + // static fields + [&](ClassAccessor::Field& field) REQUIRES_SHARED(Locks::mutator_lock_) { + if (resolve_fields_and_methods) { + ArtField* resolved = class_linker->ResolveField( + field.GetIndex(), dex_cache, class_loader, /* is_static */ true); + if (resolved == nullptr) { + CheckAndClearResolveException(soa.Self()); + } } - } - it.Next(); - } - if (resolve_fields_and_methods) { - while (it.HasNextMethod()) { - ArtMethod* method = class_linker->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( - it.GetMemberIndex(), - dex_cache, - class_loader, - /* referrer */ nullptr, - it.GetMethodInvokeType(class_def)); - if (method == nullptr) { - CheckAndClearResolveException(soa.Self()); + }, + // instance fields + [&](ClassAccessor::Field& field) REQUIRES_SHARED(Locks::mutator_lock_) { + if (field.IsFinal()) { + // We require a constructor barrier if there are final instance fields. + requires_constructor_barrier = true; } - it.Next(); - } - DCHECK(!it.HasNext()); - } - } + if (resolve_fields_and_methods) { + ArtField* resolved = class_linker->ResolveField( + field.GetIndex(), dex_cache, class_loader, /* is_static */ false); + if (resolved == nullptr) { + CheckAndClearResolveException(soa.Self()); + } + } + }, + /*direct methods*/ method_visitor, + /*virtual methods*/ method_visitor); manager_->GetCompiler()->SetRequiresConstructorBarrier(self, &dex_file, class_def_index, @@ -1942,32 +1875,13 @@ void CompilerDriver::SetVerified(jobject class_loader, } } -static void PopulateVerifiedMethods(const DexFile& dex_file, - uint32_t class_def_index, - VerificationResults* verification_results) { - const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); - const uint8_t* class_data = dex_file.GetClassData(class_def); - if (class_data == nullptr) { - return; - } - ClassDataItemIterator it(dex_file, class_data); - it.SkipAllFields(); - - while (it.HasNextMethod()) { - verification_results->CreateVerifiedMethodFor(MethodReference(&dex_file, it.GetMemberIndex())); - it.Next(); - } - DCHECK(!it.HasNext()); -} - -static void LoadAndUpdateStatus(const DexFile& dex_file, - const DexFile::ClassDef& class_def, +static void LoadAndUpdateStatus(const ClassAccessor& accessor, ClassStatus status, Handle<mirror::ClassLoader> class_loader, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { StackHandleScope<1> hs(self); - const char* descriptor = dex_file.GetClassDescriptor(class_def); + const char* descriptor = accessor.GetDescriptor(); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Handle<mirror::Class> cls(hs.NewHandle<mirror::Class>( class_linker->FindClass(self, descriptor, class_loader))); @@ -1975,7 +1889,7 @@ static void LoadAndUpdateStatus(const DexFile& dex_file, // Check that the class is resolved with the current dex file. We might get // a boot image class, or a class in a different dex file for multidex, and // we should not update the status in that case. - if (&cls->GetDexFile() == &dex_file) { + if (&cls->GetDexFile() == &accessor.GetDexFile()) { ObjectLock<mirror::Class> lock(self, cls); mirror::Class::SetStatus(cls, status, self); } @@ -2014,13 +1928,13 @@ bool CompilerDriver::FastVerify(jobject jclass_loader, // Fetch the list of unverified classes. const std::set<dex::TypeIndex>& unverified_classes = verifier_deps->GetUnverifiedClasses(*dex_file); - for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); - if (unverified_classes.find(class_def.class_idx_) == unverified_classes.end()) { + uint32_t class_def_idx = 0u; + for (ClassAccessor accessor : dex_file->GetClasses()) { + if (unverified_classes.find(accessor.GetClassIdx()) == unverified_classes.end()) { if (compiler_only_verifies) { // Just update the compiled_classes_ map. The compiler doesn't need to resolve // the type. - ClassReference ref(dex_file, i); + ClassReference ref(dex_file, class_def_idx); ClassStatus existing = ClassStatus::kNotReady; DCHECK(compiled_classes_.Get(ref, &existing)) << ref.dex_file->GetLocation(); ClassStateTable::InsertResult result = @@ -2029,26 +1943,27 @@ bool CompilerDriver::FastVerify(jobject jclass_loader, } else { // Update the class status, so later compilation stages know they don't need to verify // the class. - LoadAndUpdateStatus( - *dex_file, class_def, ClassStatus::kVerified, class_loader, soa.Self()); + LoadAndUpdateStatus(accessor, ClassStatus::kVerified, class_loader, soa.Self()); // Create `VerifiedMethod`s for each methods, the compiler expects one for // quickening or compiling. // Note that this means: // - We're only going to compile methods that did verify. // - Quickening will not do checkcast ellision. // TODO(ngeoffray): Reconsider this once we refactor compiler filters. - PopulateVerifiedMethods(*dex_file, i, verification_results_); + accessor.VisitMethods([&](const ClassAccessor::Method& method) { + verification_results_->CreateVerifiedMethodFor(method.GetReference()); + }); } } else if (!compiler_only_verifies) { // Make sure later compilation stages know they should not try to verify // this class again. - LoadAndUpdateStatus(*dex_file, - class_def, + LoadAndUpdateStatus(accessor, ClassStatus::kRetryVerificationAtRuntime, class_loader, soa.Self()); } } + ++class_def_idx; } return true; } @@ -2784,22 +2699,22 @@ static void CompileDexFile(CompilerDriver* driver, auto compile = [&context, &compile_fn](size_t class_def_index) { ScopedTrace trace(__FUNCTION__); const DexFile& dex_file = *context.GetDexFile(); - const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); ClassLinker* class_linker = context.GetClassLinker(); jobject jclass_loader = context.GetClassLoader(); ClassReference ref(&dex_file, class_def_index); + const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); + ClassAccessor accessor(dex_file, class_def); // Skip compiling classes with generic verifier failures since they will still fail at runtime if (context.GetCompiler()->GetVerificationResults()->IsClassRejected(ref)) { return; } // Use a scoped object access to perform to the quick SkipClass check. - const char* descriptor = dex_file.GetClassDescriptor(class_def); ScopedObjectAccess soa(Thread::Current()); StackHandleScope<3> hs(soa.Self()); Handle<mirror::ClassLoader> class_loader( hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader))); Handle<mirror::Class> klass( - hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor, class_loader))); + hs.NewHandle(class_linker->FindClass(soa.Self(), accessor.GetDescriptor(), class_loader))); Handle<mirror::DexCache> dex_cache; if (klass == nullptr) { soa.Self()->AssertPendingException(); @@ -2814,9 +2729,8 @@ static void CompileDexFile(CompilerDriver* driver, dex_cache = hs.NewHandle(klass->GetDexCache()); } - const uint8_t* class_data = dex_file.GetClassData(class_def); - if (class_data == nullptr) { - // empty class, probably a marker interface + // Avoid suspension if there are no methods to compile. + if (accessor.NumDirectMethods() + accessor.NumVirtualMethods() == 0) { return; } @@ -2829,28 +2743,24 @@ static void CompileDexFile(CompilerDriver* driver, optimizer::DexToDexCompiler::CompilationLevel dex_to_dex_compilation_level = GetDexToDexCompilationLevel(soa.Self(), *driver, jclass_loader, dex_file, class_def); - ClassDataItemIterator it(dex_file, class_data); - it.SkipAllFields(); - bool compilation_enabled = driver->IsClassToCompile( - dex_file.StringByTypeIdx(class_def.class_idx_)); + const bool compilation_enabled = driver->IsClassToCompile(accessor.GetDescriptor()); // Compile direct and virtual methods. int64_t previous_method_idx = -1; - while (it.HasNextMethod()) { - uint32_t method_idx = it.GetMemberIndex(); + accessor.VisitMethods([&](const ClassAccessor::Method& method) { + const uint32_t method_idx = method.GetIndex(); if (method_idx == previous_method_idx) { // smali can create dex files with two encoded_methods sharing the same method_idx // http://code.google.com/p/smali/issues/detail?id=119 - it.Next(); - continue; + return; } previous_method_idx = method_idx; compile_fn(soa.Self(), driver, - it.GetMethodCodeItem(), - it.GetMethodAccessFlags(), - it.GetMethodInvokeType(class_def), + method.GetCodeItem(), + method.GetAccessFlags(), + method.GetInvokeType(class_def.access_flags_), class_def_index, method_idx, class_loader, @@ -2858,9 +2768,7 @@ static void CompileDexFile(CompilerDriver* driver, dex_to_dex_compilation_level, compilation_enabled, dex_cache); - it.Next(); - } - DCHECK(!it.HasNext()); + }); }; context.ForAllLambda(0, dex_file.NumClassDefs(), compile, thread_count); } diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 7f78dc257e..55496602d2 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -1121,6 +1121,23 @@ void HEnvironment::RemoveAsUserOfInput(size_t index) const { user->FixUpUserRecordsAfterEnvUseRemoval(before_env_use_node); } +void HEnvironment::ReplaceInput(HInstruction* replacement, size_t index) { + const HUserRecord<HEnvironment*>& env_use_record = vregs_[index]; + HInstruction* orig_instr = env_use_record.GetInstruction(); + + DCHECK(orig_instr != replacement); + + HUseList<HEnvironment*>::iterator before_use_node = env_use_record.GetBeforeUseNode(); + // Note: fixup_end remains valid across splice_after(). + auto fixup_end = replacement->env_uses_.empty() ? replacement->env_uses_.begin() + : ++replacement->env_uses_.begin(); + replacement->env_uses_.splice_after(replacement->env_uses_.before_begin(), + env_use_record.GetInstruction()->env_uses_, + before_use_node); + replacement->FixUpUserRecordsAfterEnvUseInsertion(fixup_end); + orig_instr->FixUpUserRecordsAfterEnvUseRemoval(before_use_node); +} + HInstruction* HInstruction::GetNextDisregardingMoves() const { HInstruction* next = GetNext(); while (next != nullptr && next->IsParallelMove()) { diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 09d9c57a33..3fd5b6b02d 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1909,6 +1909,11 @@ class HEnvironment : public ArenaObject<kArenaAllocEnvironment> { void RemoveAsUserOfInput(size_t index) const; + // Replaces the input at the position 'index' with the replacement; the replacement and old + // input instructions' env_uses_ lists are adjusted. The function works similar to + // HInstruction::ReplaceInput. + void ReplaceInput(HInstruction* replacement, size_t index); + size_t Size() const { return vregs_.size(); } HEnvironment* GetParent() const { return parent_; } diff --git a/compiler/optimizing/superblock_cloner.cc b/compiler/optimizing/superblock_cloner.cc index fad7729956..1b43618538 100644 --- a/compiler/optimizing/superblock_cloner.cc +++ b/compiler/optimizing/superblock_cloner.cc @@ -409,7 +409,7 @@ void SuperblockCloner::ResolvePhi(HPhi* phi) { // Main algorithm methods. // -void SuperblockCloner::SearchForSubgraphExits(ArenaVector<HBasicBlock*>* exits) { +void SuperblockCloner::SearchForSubgraphExits(ArenaVector<HBasicBlock*>* exits) const { DCHECK(exits->empty()); for (uint32_t block_id : orig_bb_set_.Indexes()) { HBasicBlock* block = GetBlockById(block_id); @@ -521,6 +521,113 @@ void SuperblockCloner::ResolveDataFlow() { } // +// Helpers for live-outs processing and Subgraph-closed SSA. +// + +bool SuperblockCloner::CollectLiveOutsAndCheckClonable(HInstructionMap* live_outs) const { + DCHECK(live_outs->empty()); + for (uint32_t idx : orig_bb_set_.Indexes()) { + HBasicBlock* block = GetBlockById(idx); + + for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { + HInstruction* instr = it.Current(); + DCHECK(instr->IsClonable()); + + if (IsUsedOutsideRegion(instr, orig_bb_set_)) { + live_outs->FindOrAdd(instr, instr); + } + } + + for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { + HInstruction* instr = it.Current(); + if (!instr->IsClonable()) { + return false; + } + + if (IsUsedOutsideRegion(instr, orig_bb_set_)) { + // TODO: Investigate why HNewInstance, HCheckCast has a requirement for the input. + if (instr->IsLoadClass()) { + return false; + } + live_outs->FindOrAdd(instr, instr); + } + } + } + return true; +} + +void SuperblockCloner::ConstructSubgraphClosedSSA() { + if (live_outs_.empty()) { + return; + } + + ArenaVector<HBasicBlock*> exits(arena_->Adapter(kArenaAllocSuperblockCloner)); + SearchForSubgraphExits(&exits); + if (exits.empty()) { + DCHECK(live_outs_.empty()); + return; + } + + DCHECK_EQ(exits.size(), 1u); + HBasicBlock* exit_block = exits[0]; + // There should be no critical edges. + DCHECK_EQ(exit_block->GetPredecessors().size(), 1u); + DCHECK(exit_block->GetPhis().IsEmpty()); + + // For each live-out value insert a phi into the loop exit and replace all the value's uses + // external to the loop with this phi. The phi will have the original value as its only input; + // after copying is done FixSubgraphClosedSSAAfterCloning will add a corresponding copy of the + // original value as the second input thus merging data flow from the original and copy parts of + // the subgraph. Also update the record in the live_outs_ map from (value, value) to + // (value, new_phi). + for (auto live_out_it = live_outs_.begin(); live_out_it != live_outs_.end(); ++live_out_it) { + HInstruction* value = live_out_it->first; + HPhi* phi = new (arena_) HPhi(arena_, kNoRegNumber, 0, value->GetType()); + + if (value->GetType() == DataType::Type::kReference) { + phi->SetReferenceTypeInfo(value->GetReferenceTypeInfo()); + } + + exit_block->AddPhi(phi); + live_out_it->second = phi; + + const HUseList<HInstruction*>& uses = value->GetUses(); + for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) { + HInstruction* user = it->GetUser(); + size_t index = it->GetIndex(); + // Increment `it` now because `*it` may disappear thanks to user->ReplaceInput(). + ++it; + if (!IsInOrigBBSet(user->GetBlock())) { + user->ReplaceInput(phi, index); + } + } + + const HUseList<HEnvironment*>& env_uses = value->GetEnvUses(); + for (auto it = env_uses.begin(), e = env_uses.end(); it != e; /* ++it below */) { + HEnvironment* env = it->GetUser(); + size_t index = it->GetIndex(); + ++it; + if (!IsInOrigBBSet(env->GetHolder()->GetBlock())) { + env->ReplaceInput(phi, index); + } + } + + phi->AddInput(value); + } +} + +void SuperblockCloner::FixSubgraphClosedSSAAfterCloning() { + for (auto it : live_outs_) { + DCHECK(it.first != it.second); + HInstruction* orig_value = it.first; + HPhi* phi = it.second->AsPhi(); + HInstruction* copy_value = GetInstrCopy(orig_value); + // Copy edges are inserted after the original so we can just add new input to the phi. + phi->AddInput(copy_value); + } +} + +// // Debug and logging methods. // @@ -644,7 +751,6 @@ void DumpBBSet(const ArenaBitVector* set) { } void SuperblockCloner::DumpInputSets() { - std::cout << graph_->PrettyMethod() << "\n"; std::cout << "orig_bb_set:\n"; for (uint32_t idx : orig_bb_set_.Indexes()) { std::cout << idx << "\n"; @@ -680,7 +786,9 @@ SuperblockCloner::SuperblockCloner(HGraph* graph, bb_map_(bb_map), hir_map_(hir_map), outer_loop_(nullptr), - outer_loop_bb_set_(arena_, orig_bb_set->GetSizeOf(), true, kArenaAllocSuperblockCloner) { + outer_loop_bb_set_(arena_, orig_bb_set->GetSizeOf(), true, kArenaAllocSuperblockCloner), + live_outs_(std::less<HInstruction*>(), + graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)) { orig_bb_set_.Copy(orig_bb_set); } @@ -699,26 +807,19 @@ bool SuperblockCloner::IsSubgraphClonable() const { return false; } - // Check that there are no instructions defined in the subgraph and used outside. - // TODO: Improve this by accepting graph with such uses but only one exit. - for (uint32_t idx : orig_bb_set_.Indexes()) { - HBasicBlock* block = GetBlockById(idx); + HInstructionMap live_outs( + std::less<HInstruction*>(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); - for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { - HInstruction* instr = it.Current(); - if (!instr->IsClonable() || - IsUsedOutsideRegion(instr, orig_bb_set_)) { - return false; - } - } + if (!CollectLiveOutsAndCheckClonable(&live_outs)) { + return false; + } - for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { - HInstruction* instr = it.Current(); - if (!instr->IsClonable() || - IsUsedOutsideRegion(instr, orig_bb_set_)) { - return false; - } - } + ArenaVector<HBasicBlock*> exits(arena_->Adapter(kArenaAllocSuperblockCloner)); + SearchForSubgraphExits(&exits); + + // The only loops with live-outs which are currently supported are loops with a single exit. + if (!live_outs.empty() && exits.size() != 1) { + return false; } return true; @@ -794,8 +895,10 @@ void SuperblockCloner::Run() { DumpInputSets(); } + CollectLiveOutsAndCheckClonable(&live_outs_); // Find an area in the graph for which control flow information should be adjusted. FindAndSetLocalAreaForAdjustments(); + ConstructSubgraphClosedSSA(); // Clone the basic blocks from the orig_bb_set_; data flow is invalid after the call and is to be // adjusted. CloneBasicBlocks(); @@ -819,6 +922,7 @@ void SuperblockCloner::Run() { AdjustControlFlowInfo(); // Fix data flow of the graph. ResolveDataFlow(); + FixSubgraphClosedSSAAfterCloning(); } void SuperblockCloner::CleanUp() { @@ -985,8 +1089,14 @@ HBasicBlock* PeelUnrollHelper::DoPeelUnrollImpl(bool to_unroll) { HBasicBlock* loop_header = loop_info_->GetHeader(); // Check that loop info is up-to-date. DCHECK(loop_info_ == loop_header->GetLoopInformation()); - HGraph* graph = loop_header->GetGraph(); + + if (kSuperblockClonerLogging) { + std::cout << "Method: " << graph->PrettyMethod() << std::endl; + std::cout << "Scalar loop " << (to_unroll ? "unrolling" : "peeling") << + " was applied to the loop <" << loop_header->GetBlockId() << ">." << std::endl; + } + ArenaAllocator allocator(graph->GetAllocator()->GetArenaPool()); HEdgeSet remap_orig_internal(graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); diff --git a/compiler/optimizing/superblock_cloner.h b/compiler/optimizing/superblock_cloner.h index e0931674cb..f21172131b 100644 --- a/compiler/optimizing/superblock_cloner.h +++ b/compiler/optimizing/superblock_cloner.h @@ -218,7 +218,7 @@ class SuperblockCloner : public ValueObject { private: // Fills the 'exits' vector with the subgraph exits. - void SearchForSubgraphExits(ArenaVector<HBasicBlock*>* exits); + void SearchForSubgraphExits(ArenaVector<HBasicBlock*>* exits) const; // Finds and records information about the area in the graph for which control flow (back edges, // loops, dominators) needs to be adjusted. @@ -240,6 +240,33 @@ class SuperblockCloner : public ValueObject { void ResolveDataFlow(); // + // Helpers for live-outs processing and Subgraph-closed SSA. + // + // - live-outs - values which are defined inside the subgraph and have uses outside. + // - Subgraph-closed SSA - SSA form for which all the values defined inside the subgraph + // have no outside uses except for the phi-nodes in the subgraph exits. + // + // Note: now if the subgraph has live-outs it is only clonable if it has a single exit; this + // makes the subgraph-closed SSA form construction much easier. + // + // TODO: Support subgraphs with live-outs and multiple exits. + // + + // For each live-out value 'val' in the region puts a record <val, val> into the map. + // Returns whether all of the instructions in the subgraph are clonable. + bool CollectLiveOutsAndCheckClonable(HInstructionMap* live_outs_) const; + + // Constructs Subgraph-closed SSA; precondition - a subgraph has a single exit. + // + // For each live-out 'val' in 'live_outs_' map inserts a HPhi 'phi' into the exit node, updates + // the record in the map to <val, phi> and replaces all outside uses with this phi. + void ConstructSubgraphClosedSSA(); + + // Fixes the data flow for the live-out 'val' by adding a 'copy_val' input to the corresponding + // (<val, phi>) phi after the cloning is done. + void FixSubgraphClosedSSAAfterCloning(); + + // // Helpers for CloneBasicBlock. // @@ -316,6 +343,8 @@ class SuperblockCloner : public ValueObject { HLoopInformation* outer_loop_; HBasicBlockSet outer_loop_bb_set_; + HInstructionMap live_outs_; + ART_FRIEND_TEST(SuperblockClonerTest, AdjustControlFlowInfo); ART_FRIEND_TEST(SuperblockClonerTest, IsGraphConnected); diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc index 06f0bcdec7..103862beff 100644 --- a/compiler/verifier_deps_test.cc +++ b/compiler/verifier_deps_test.cc @@ -22,6 +22,8 @@ #include "class_linker.h" #include "common_compiler_test.h" #include "compiler_callbacks.h" +#include "dex/class_accessor-inl.h" +#include "dex/class_iterator.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_types.h" #include "dex/verification_results.h" @@ -148,48 +150,45 @@ class VerifierDepsTest : public CommonCompilerTest { Handle<mirror::DexCache> dex_cache_handle(hs.NewHandle(klass_Main_->GetDexCache())); const DexFile::ClassDef* class_def = klass_Main_->GetClassDef(); - const uint8_t* class_data = primary_dex_file_->GetClassData(*class_def); - CHECK(class_data != nullptr); + ClassAccessor accessor(*primary_dex_file_, *class_def); - ClassDataItemIterator it(*primary_dex_file_, class_data); - it.SkipAllFields(); + bool has_failures = true; + bool found_method = false; - ArtMethod* method = nullptr; - while (it.HasNextDirectMethod()) { + accessor.VisitMethods([&](const ClassAccessor::Method& method) + REQUIRES_SHARED(Locks::mutator_lock_) { ArtMethod* resolved_method = class_linker_->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( - it.GetMemberIndex(), + method.GetIndex(), dex_cache_handle, class_loader_handle, /* referrer */ nullptr, - it.GetMethodInvokeType(*class_def)); + method.GetInvokeType(class_def->access_flags_)); CHECK(resolved_method != nullptr); if (method_name == resolved_method->GetName()) { - method = resolved_method; - break; + soa.Self()->SetVerifierDeps(callbacks_->GetVerifierDeps()); + MethodVerifier verifier(soa.Self(), + primary_dex_file_, + dex_cache_handle, + class_loader_handle, + *class_def, + method.GetCodeItem(), + method.GetIndex(), + resolved_method, + method.GetAccessFlags(), + true /* can_load_classes */, + true /* allow_soft_failures */, + true /* need_precise_constants */, + false /* verify to dump */, + true /* allow_thread_suspension */); + verifier.Verify(); + soa.Self()->SetVerifierDeps(nullptr); + has_failures = verifier.HasFailures(); + found_method = true; } - it.Next(); - } - CHECK(method != nullptr); - - Thread::Current()->SetVerifierDeps(callbacks_->GetVerifierDeps()); - MethodVerifier verifier(Thread::Current(), - primary_dex_file_, - dex_cache_handle, - class_loader_handle, - *class_def, - it.GetMethodCodeItem(), - it.GetMemberIndex(), - method, - it.GetMethodAccessFlags(), - true /* can_load_classes */, - true /* allow_soft_failures */, - true /* need_precise_constants */, - false /* verify to dump */, - true /* allow_thread_suspension */); - verifier.Verify(); - Thread::Current()->SetVerifierDeps(nullptr); - return !verifier.HasFailures(); + }); + CHECK(found_method) << "Expected to find method " << method_name; + return !has_failures; } void VerifyDexFile(const char* multidex = nullptr) { diff --git a/libdexfile/dex/class_accessor-inl.h b/libdexfile/dex/class_accessor-inl.h index 5cfbcaa359..a082142366 100644 --- a/libdexfile/dex/class_accessor-inl.h +++ b/libdexfile/dex/class_accessor-inl.h @@ -38,14 +38,14 @@ inline ClassAccessor::ClassAccessor(const DexFile& dex_file, const DexFile::Clas num_virtual_methods_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u) {} inline const uint8_t* ClassAccessor::Method::Read(const uint8_t* ptr) { - method_idx_ += DecodeUnsignedLeb128(&ptr); + index_ += DecodeUnsignedLeb128(&ptr); access_flags_ = DecodeUnsignedLeb128(&ptr); code_off_ = DecodeUnsignedLeb128(&ptr); return ptr; } inline const uint8_t* ClassAccessor::Field::Read(const uint8_t* ptr) { - field_idx_ += DecodeUnsignedLeb128(&ptr); + index_ += DecodeUnsignedLeb128(&ptr); access_flags_ = DecodeUnsignedLeb128(&ptr); return ptr; } @@ -54,7 +54,7 @@ template <typename StaticFieldVisitor, typename InstanceFieldVisitor, typename DirectMethodVisitor, typename VirtualMethodVisitor> -inline void ClassAccessor::VisitMethodsAndFields( +inline void ClassAccessor::VisitFieldsAndMethods( const StaticFieldVisitor& static_field_visitor, const InstanceFieldVisitor& instance_field_visitor, const DirectMethodVisitor& direct_method_visitor, @@ -75,14 +75,14 @@ inline void ClassAccessor::VisitMethodsAndFields( } } { - Method data(dex_file_); + Method data(dex_file_, /*is_static_or_direct*/ true); for (size_t i = 0; i < num_direct_methods_; ++i) { ptr = data.Read(ptr); direct_method_visitor(data); } } { - Method data(dex_file_); + Method data(dex_file_, /*is_static_or_direct*/ false); for (size_t i = 0; i < num_virtual_methods_; ++i) { ptr = data.Read(ptr); virtual_method_visitor(data); @@ -94,12 +94,22 @@ template <typename DirectMethodVisitor, typename VirtualMethodVisitor> inline void ClassAccessor::VisitMethods(const DirectMethodVisitor& direct_method_visitor, const VirtualMethodVisitor& virtual_method_visitor) const { - VisitMethodsAndFields(VoidFunctor(), + VisitFieldsAndMethods(VoidFunctor(), VoidFunctor(), direct_method_visitor, virtual_method_visitor); } +template <typename StaticFieldVisitor, + typename InstanceFieldVisitor> +inline void ClassAccessor::VisitFields(const StaticFieldVisitor& static_field_visitor, + const InstanceFieldVisitor& instance_field_visitor) const { + VisitFieldsAndMethods(static_field_visitor, + instance_field_visitor, + VoidFunctor(), + VoidFunctor()); +} + // Visit direct and virtual methods. template <typename MethodVisitor> inline void ClassAccessor::VisitMethods(const MethodVisitor& method_visitor) const { @@ -114,6 +124,14 @@ inline CodeItemInstructionAccessor ClassAccessor::Method::GetInstructions() cons return CodeItemInstructionAccessor(dex_file_, dex_file_.GetCodeItem(GetCodeItemOffset())); } +inline const char* ClassAccessor::GetDescriptor() const { + return dex_file_.StringByTypeIdx(descriptor_index_); +} + +inline const DexFile::CodeItem* ClassAccessor::Method::GetCodeItem() const { + return dex_file_.GetCodeItem(code_off_); +} + } // namespace art #endif // ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_INL_H_ diff --git a/libdexfile/dex/class_accessor.h b/libdexfile/dex/class_accessor.h index 835c4e2eb7..72bc50b98c 100644 --- a/libdexfile/dex/class_accessor.h +++ b/libdexfile/dex/class_accessor.h @@ -20,6 +20,9 @@ #include "base/utils.h" #include "code_item_accessors.h" #include "dex_file.h" +#include "invoke_type.h" +#include "method_reference.h" +#include "modifiers.h" namespace art { @@ -27,56 +30,82 @@ class ClassIteratorData; // Classes to access Dex data. class ClassAccessor { - public: - // Class method data. - class Method { + private: + class BaseItem { public: uint32_t GetIndex() const { - return method_idx_; + return index_; } uint32_t GetAccessFlags() const { return access_flags_; } + bool IsFinal() const { + return (GetAccessFlags() & kAccFinal) != 0; + } + + public: + uint32_t index_ = 0u; + uint32_t access_flags_ = 0u; + }; + + public: + // A decoded version of the method of a class_data_item. + class Method : public BaseItem { + public: uint32_t GetCodeItemOffset() const { return code_off_; } + InvokeType GetInvokeType(uint32_t class_access_flags) const { + return is_static_or_direct_ + ? GetDirectMethodInvokeType() + : GetVirtualMethodInvokeType(class_access_flags); + } + + MethodReference GetReference() const { + return MethodReference(&dex_file_, GetIndex()); + } + CodeItemInstructionAccessor GetInstructions() const; + const DexFile::CodeItem* GetCodeItem() const; + private: - explicit Method(const DexFile& dex_file) : dex_file_(dex_file) {} + explicit Method(const DexFile& dex_file, bool is_static_or_direct) + : dex_file_(dex_file), + is_static_or_direct_(is_static_or_direct) {} const uint8_t* Read(const uint8_t* ptr); - // A decoded version of the method of a class_data_item. + InvokeType GetDirectMethodInvokeType() const { + return (GetAccessFlags() & kAccStatic) != 0 ? kStatic : kDirect; + } + + InvokeType GetVirtualMethodInvokeType(uint32_t class_access_flags) const { + DCHECK_EQ(GetAccessFlags() & kAccStatic, 0U); + if ((class_access_flags & kAccInterface) != 0) { + return kInterface; + } else if ((GetAccessFlags() & kAccConstructor) != 0) { + return kSuper; + } else { + return kVirtual; + } + } + const DexFile& dex_file_; - uint32_t method_idx_ = 0u; - uint32_t access_flags_ = 0u; + const bool is_static_or_direct_; uint32_t code_off_ = 0u; friend class ClassAccessor; }; - // Class field data. - class Field { - public: - uint32_t GetIndex() const { - return field_idx_; - } - - uint32_t GetAccessFlags() const { - return access_flags_; - } - + // A decoded version of the field of a class_data_item. + class Field : public BaseItem { private: const uint8_t* Read(const uint8_t* ptr); - // A decoded version of the field of a class_data_item. - uint32_t field_idx_ = 0u; - uint32_t access_flags_ = 0u; - friend class ClassAccessor; }; @@ -89,20 +118,27 @@ class ClassAccessor { const DexFile::CodeItem* GetCodeItem(const Method& method) const; // Iterator data is not very iterator friendly, use visitors to get around this. + // No thread safety analysis since the visitor may require capabilities. template <typename StaticFieldVisitor, typename InstanceFieldVisitor, typename DirectMethodVisitor, typename VirtualMethodVisitor> - void VisitMethodsAndFields(const StaticFieldVisitor& static_field_visitor, + void VisitFieldsAndMethods(const StaticFieldVisitor& static_field_visitor, const InstanceFieldVisitor& instance_field_visitor, const DirectMethodVisitor& direct_method_visitor, - const VirtualMethodVisitor& virtual_method_visitor) const; + const VirtualMethodVisitor& virtual_method_visitor) const + NO_THREAD_SAFETY_ANALYSIS; template <typename DirectMethodVisitor, typename VirtualMethodVisitor> void VisitMethods(const DirectMethodVisitor& direct_method_visitor, const VirtualMethodVisitor& virtual_method_visitor) const; + template <typename StaticFieldVisitor, + typename InstanceFieldVisitor> + void VisitFields(const StaticFieldVisitor& static_field_visitor, + const InstanceFieldVisitor& instance_field_visitor) const; + // Visit direct and virtual methods. template <typename MethodVisitor> void VisitMethods(const MethodVisitor& method_visitor) const; @@ -123,11 +159,16 @@ class ClassAccessor { return num_virtual_methods_; } - // TODO: Deprecate - dex::TypeIndex GetDescriptorIndex() const { + const char* GetDescriptor() const; + + dex::TypeIndex GetClassIdx() const { return descriptor_index_; } + const DexFile& GetDexFile() const { + return dex_file_; + } + protected: const DexFile& dex_file_; const dex::TypeIndex descriptor_index_ = {}; diff --git a/libdexfile/dex/dex_instruction_iterator.h b/libdexfile/dex/dex_instruction_iterator.h index db3ff95e02..b75a95bf5c 100644 --- a/libdexfile/dex/dex_instruction_iterator.h +++ b/libdexfile/dex/dex_instruction_iterator.h @@ -123,7 +123,7 @@ class DexInstructionIterator : public DexInstructionIteratorBase { using DexInstructionIteratorBase::DexInstructionIteratorBase; explicit DexInstructionIterator(const uint16_t* inst, uint32_t dex_pc) - : DexInstructionIteratorBase(Instruction::At(inst), dex_pc) {} + : DexInstructionIteratorBase(inst != nullptr ? Instruction::At(inst) : nullptr, dex_pc) {} explicit DexInstructionIterator(const DexInstructionPcPair& pair) : DexInstructionIterator(pair.Instructions(), pair.DexPc()) {} diff --git a/test/463-checker-boolean-simplifier/build b/test/463-checker-boolean-simplifier/build deleted file mode 100755 index 10ffcc537d..0000000000 --- a/test/463-checker-boolean-simplifier/build +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -# -# Copyright 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# See b/65168732 -export USE_D8=false - -./default-build "$@" diff --git a/test/463-checker-boolean-simplifier/smali/Main2.smali b/test/463-checker-boolean-simplifier/smali/Main2.smali new file mode 100644 index 0000000000..5fc553ea36 --- /dev/null +++ b/test/463-checker-boolean-simplifier/smali/Main2.smali @@ -0,0 +1,308 @@ +# Copyright (C) 2018 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 LMain2; +.super Ljava/lang/Object; +.source "Main2.java" + + +# direct methods +.method constructor <init>()V + .registers 1 + + .prologue + .line 17 + invoke-direct {p0}, Ljava/lang/Object;-><init>()V + + return-void +.end method + +# Elementary test negating a boolean. Verifies that blocks are merged and +# empty branches removed. + +## CHECK-START: boolean Main2.BooleanNot(boolean) select_generator (before) +## CHECK-DAG: <<Param:z\d+>> ParameterValue +## CHECK-DAG: <<Const0:i\d+>> IntConstant 0 +## CHECK-DAG: <<Const1:i\d+>> IntConstant 1 +## CHECK-DAG: If [<<Param>>] +## CHECK-DAG: <<Phi:i\d+>> Phi [<<Const1>>,<<Const0>>] +## CHECK-DAG: Return [<<Phi>>] + +## CHECK-START: boolean Main2.BooleanNot(boolean) select_generator (before) +## CHECK: Goto +## CHECK: Goto +## CHECK: Goto +## CHECK-NOT: Goto + +## CHECK-START: boolean Main2.BooleanNot(boolean) select_generator (after) +## CHECK-DAG: <<Param:z\d+>> ParameterValue +## CHECK-DAG: <<Const0:i\d+>> IntConstant 0 +## CHECK-DAG: <<Const1:i\d+>> IntConstant 1 +## CHECK-DAG: <<NotParam:i\d+>> Select [<<Const1>>,<<Const0>>,<<Param>>] +## CHECK-DAG: Return [<<NotParam>>] + +## CHECK-START: boolean Main2.BooleanNot(boolean) select_generator (after) +## CHECK-NOT: If +## CHECK-NOT: Phi + +## CHECK-START: boolean Main2.BooleanNot(boolean) select_generator (after) +## CHECK: Goto +## CHECK-NOT: Goto + +# The original java source of this method: +# +# return !x; +# +.method public static BooleanNot(Z)Z + .registers 2 + .param p0, "x" # Z + + .prologue + .line 70 + if-nez p0, :cond_4 + + const/4 v0, 0x1 + + :goto_3 + return v0 + + :cond_4 + const/4 v0, 0x0 + + goto :goto_3 +.end method + +# Program which further uses negated conditions. +# Note that Phis are discovered retrospectively. + +## CHECK-START: boolean Main2.ValuesOrdered(int, int, int) select_generator (before) +## CHECK-DAG: <<ParamX:i\d+>> ParameterValue +## CHECK-DAG: <<ParamY:i\d+>> ParameterValue +## CHECK-DAG: <<ParamZ:i\d+>> ParameterValue +## CHECK-DAG: <<Const0:i\d+>> IntConstant 0 +## CHECK-DAG: <<Const1:i\d+>> IntConstant 1 +## CHECK-DAG: <<CondXY:z\d+>> GreaterThan [<<ParamX>>,<<ParamY>>] +## CHECK-DAG: If [<<CondXY>>] +## CHECK-DAG: <<CondYZ:z\d+>> GreaterThan [<<ParamY>>,<<ParamZ>>] +## CHECK-DAG: If [<<CondYZ>>] +## CHECK-DAG: <<CondXYZ:z\d+>> NotEqual [<<PhiXY:i\d+>>,<<PhiYZ:i\d+>>] +## CHECK-DAG: If [<<CondXYZ>>] +## CHECK-DAG: Return [<<PhiXYZ:i\d+>>] +## CHECK-DAG: <<PhiXY>> Phi [<<Const1>>,<<Const0>>] +## CHECK-DAG: <<PhiYZ>> Phi [<<Const1>>,<<Const0>>] +## CHECK-DAG: <<PhiXYZ>> Phi [<<Const1>>,<<Const0>>] + +## CHECK-START: boolean Main2.ValuesOrdered(int, int, int) select_generator (after) +## CHECK-DAG: <<ParamX:i\d+>> ParameterValue +## CHECK-DAG: <<ParamY:i\d+>> ParameterValue +## CHECK-DAG: <<ParamZ:i\d+>> ParameterValue +## CHECK-DAG: <<Const0:i\d+>> IntConstant 0 +## CHECK-DAG: <<Const1:i\d+>> IntConstant 1 +## CHECK-DAG: <<CmpXY:z\d+>> GreaterThan [<<ParamX>>,<<ParamY>>] +## CHECK-DAG: <<SelXY:i\d+>> Select [<<Const1>>,<<Const0>>,<<CmpXY>>] +## CHECK-DAG: <<CmpYZ:z\d+>> GreaterThan [<<ParamY>>,<<ParamZ>>] +## CHECK-DAG: <<SelYZ:i\d+>> Select [<<Const1>>,<<Const0>>,<<CmpYZ>>] +## CHECK-DAG: <<CmpXYZ:z\d+>> NotEqual [<<SelXY>>,<<SelYZ>>] +## CHECK-DAG: <<SelXYZ:i\d+>> Select [<<Const1>>,<<Const0>>,<<CmpXYZ>>] +## CHECK-DAG: Return [<<SelXYZ>>] + +# The original java source of this method: +# +# return (x <= y) == (y <= z); +# +.method public static ValuesOrdered(III)Z + .registers 7 + .param p0, "x" # I + .param p1, "y" # I + .param p2, "z" # I + + .prologue + const/4 v0, 0x1 + + const/4 v1, 0x0 + + .line 166 + if-gt p0, p1, :cond_b + + move v3, v0 + + :goto_5 + if-gt p1, p2, :cond_d + + move v2, v0 + + :goto_8 + if-ne v3, v2, :cond_f + + :goto_a + return v0 + + :cond_b + move v3, v1 + + goto :goto_5 + + :cond_d + move v2, v1 + + goto :goto_8 + + :cond_f + move v0, v1 + + goto :goto_a +.end method + +## CHECK-START: int Main2.NegatedCondition(boolean) select_generator (before) +## CHECK-DAG: <<Param:z\d+>> ParameterValue +## CHECK-DAG: <<Const42:i\d+>> IntConstant 42 +## CHECK-DAG: <<Const43:i\d+>> IntConstant 43 +## CHECK-DAG: If [<<Param>>] +## CHECK-DAG: <<Phi:i\d+>> Phi [<<Const42>>,<<Const43>>] +## CHECK-DAG: Return [<<Phi>>] + +## CHECK-START: int Main2.NegatedCondition(boolean) select_generator (after) +## CHECK-DAG: <<Param:z\d+>> ParameterValue +## CHECK-DAG: <<Const42:i\d+>> IntConstant 42 +## CHECK-DAG: <<Const43:i\d+>> IntConstant 43 +## CHECK-DAG: <<Select:i\d+>> Select [<<Const43>>,<<Const42>>,<<Param>>] +## CHECK-DAG: Return [<<Select>>] + +## CHECK-START: int Main2.NegatedCondition(boolean) select_generator (after) +## CHECK-NOT: BooleanNot + +# The original java source of this method: +# +# if (x != false) { +# return 42; +# } else { +# return 43; +# } +# +.method public static NegatedCondition(Z)I + .registers 2 + .param p0, "x" # Z + + .prologue + .line 188 + if-eqz p0, :cond_5 + + .line 189 + const/16 v0, 0x2a + + .line 191 + :goto_4 + return v0 + + :cond_5 + const/16 v0, 0x2b + + goto :goto_4 +.end method + +## CHECK-START: int Main2.MultiplePhis() select_generator (before) +## CHECK-DAG: <<Const0:i\d+>> IntConstant 0 +## CHECK-DAG: <<Const1:i\d+>> IntConstant 1 +## CHECK-DAG: <<Const13:i\d+>> IntConstant 13 +## CHECK-DAG: <<Const42:i\d+>> IntConstant 42 +## CHECK-DAG: <<PhiX:i\d+>> Phi [<<Const0>>,<<Const13>>,<<Const42>>] +## CHECK-DAG: <<PhiY:i\d+>> Phi [<<Const1>>,<<Add:i\d+>>,<<Add>>] +## CHECK-DAG: <<Add>> Add [<<PhiY>>,<<Const1>>] +## CHECK-DAG: <<Cond:z\d+>> LessThanOrEqual [<<Add>>,<<Const1>>] +## CHECK-DAG: If [<<Cond>>] +## CHECK-DAG: Return [<<PhiX>>] + +## CHECK-START: int Main2.MultiplePhis() select_generator (after) +## CHECK-DAG: <<Const0:i\d+>> IntConstant 0 +## CHECK-DAG: <<Const1:i\d+>> IntConstant 1 +## CHECK-DAG: <<Const13:i\d+>> IntConstant 13 +## CHECK-DAG: <<Const42:i\d+>> IntConstant 42 +## CHECK-DAG: <<PhiX:i\d+>> Phi [<<Const0>>,<<Select:i\d+>>] +## CHECK-DAG: <<PhiY:i\d+>> Phi [<<Const1>>,<<Add:i\d+>>] +## CHECK-DAG: <<Add>> Add [<<PhiY>>,<<Const1>>] +## CHECK-DAG: <<Cond:z\d+>> LessThanOrEqual [<<Add>>,<<Const1>>] +## CHECK-DAG: <<Select>> Select [<<Const13>>,<<Const42>>,<<Cond>>] +## CHECK-DAG: Return [<<PhiX>>] + +# The original java source of this method: +# +# int x = 0; +# int y = 1; +# while (y++ < 10) { +# if (y > 1) { +# x = 13; +# } else { +# x = 42; +# } +# } +# return x; +# +.method public static MultiplePhis()I + .registers 4 + + .prologue + .line 290 + const/4 v0, 0x0 + + .line 291 + .local v0, "x":I + const/4 v1, 0x1 + + .local v1, "y":I + move v2, v1 + + .line 292 + .end local v1 # "y":I + .local v2, "y":I + :goto_3 + add-int/lit8 v1, v2, 0x1 + + .end local v2 # "y":I + .restart local v1 # "y":I + const/16 v3, 0xa + + if-ge v2, v3, :cond_14 + + .line 293 + const/4 v3, 0x1 + + if-le v1, v3, :cond_10 + + .line 294 + const/16 v0, 0xd + + move v2, v1 + + .end local v1 # "y":I + .restart local v2 # "y":I + goto :goto_3 + + .line 296 + .end local v2 # "y":I + .restart local v1 # "y":I + :cond_10 + const/16 v0, 0x2a + + move v2, v1 + + .end local v1 # "y":I + .restart local v2 # "y":I + goto :goto_3 + + .line 299 + .end local v2 # "y":I + .restart local v1 # "y":I + :cond_14 + return v0 +.end method diff --git a/test/463-checker-boolean-simplifier/src/Main.java b/test/463-checker-boolean-simplifier/src/Main.java index d1d02cdfee..2c759ed6f9 100644 --- a/test/463-checker-boolean-simplifier/src/Main.java +++ b/test/463-checker-boolean-simplifier/src/Main.java @@ -14,6 +14,8 @@ * limitations under the License. */ +import java.lang.reflect.Method; + public class Main { // Note #1: `javac` flips the conditions of If statements. @@ -33,44 +35,6 @@ public class Main { } /* - * Elementary test negating a boolean. Verifies that blocks are merged and - * empty branches removed. - */ - - /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (before) - /// CHECK-DAG: <<Param:z\d+>> ParameterValue - /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 - /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 - /// CHECK-DAG: If [<<Param>>] - /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Const1>>,<<Const0>>] - /// CHECK-DAG: Return [<<Phi>>] - - /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (before) - /// CHECK: Goto - /// CHECK: Goto - /// CHECK: Goto - /// CHECK-NOT: Goto - - /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (after) - /// CHECK-DAG: <<Param:z\d+>> ParameterValue - /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 - /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 - /// CHECK-DAG: <<NotParam:i\d+>> Select [<<Const1>>,<<Const0>>,<<Param>>] - /// CHECK-DAG: Return [<<NotParam>>] - - /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (after) - /// CHECK-NOT: If - /// CHECK-NOT: Phi - - /// CHECK-START: boolean Main.BooleanNot(boolean) select_generator (after) - /// CHECK: Goto - /// CHECK-NOT: Goto - - public static boolean BooleanNot(boolean x) { - return !x; - } - - /* * Program which only delegates the condition, i.e. returns 1 when True * and 0 when False. */ @@ -126,72 +90,6 @@ public class Main { return (x < y) ? true : false; } - /* - * Program which further uses negated conditions. - * Note that Phis are discovered retrospectively. - */ - - /// CHECK-START: boolean Main.ValuesOrdered(int, int, int) select_generator (before) - /// CHECK-DAG: <<ParamX:i\d+>> ParameterValue - /// CHECK-DAG: <<ParamY:i\d+>> ParameterValue - /// CHECK-DAG: <<ParamZ:i\d+>> ParameterValue - /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 - /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 - /// CHECK-DAG: <<CondXY:z\d+>> GreaterThan [<<ParamX>>,<<ParamY>>] - /// CHECK-DAG: If [<<CondXY>>] - /// CHECK-DAG: <<CondYZ:z\d+>> GreaterThan [<<ParamY>>,<<ParamZ>>] - /// CHECK-DAG: If [<<CondYZ>>] - /// CHECK-DAG: <<CondXYZ:z\d+>> NotEqual [<<PhiXY:i\d+>>,<<PhiYZ:i\d+>>] - /// CHECK-DAG: If [<<CondXYZ>>] - /// CHECK-DAG: Return [<<PhiXYZ:i\d+>>] - /// CHECK-DAG: <<PhiXY>> Phi [<<Const1>>,<<Const0>>] - /// CHECK-DAG: <<PhiYZ>> Phi [<<Const1>>,<<Const0>>] - /// CHECK-DAG: <<PhiXYZ>> Phi [<<Const1>>,<<Const0>>] - - /// CHECK-START: boolean Main.ValuesOrdered(int, int, int) select_generator (after) - /// CHECK-DAG: <<ParamX:i\d+>> ParameterValue - /// CHECK-DAG: <<ParamY:i\d+>> ParameterValue - /// CHECK-DAG: <<ParamZ:i\d+>> ParameterValue - /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 - /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 - /// CHECK-DAG: <<CmpXY:z\d+>> GreaterThan [<<ParamX>>,<<ParamY>>] - /// CHECK-DAG: <<SelXY:i\d+>> Select [<<Const1>>,<<Const0>>,<<CmpXY>>] - /// CHECK-DAG: <<CmpYZ:z\d+>> GreaterThan [<<ParamY>>,<<ParamZ>>] - /// CHECK-DAG: <<SelYZ:i\d+>> Select [<<Const1>>,<<Const0>>,<<CmpYZ>>] - /// CHECK-DAG: <<CmpXYZ:z\d+>> NotEqual [<<SelXY>>,<<SelYZ>>] - /// CHECK-DAG: <<SelXYZ:i\d+>> Select [<<Const1>>,<<Const0>>,<<CmpXYZ>>] - /// CHECK-DAG: Return [<<SelXYZ>>] - - public static boolean ValuesOrdered(int x, int y, int z) { - return (x <= y) == (y <= z); - } - - /// CHECK-START: int Main.NegatedCondition(boolean) select_generator (before) - /// CHECK-DAG: <<Param:z\d+>> ParameterValue - /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42 - /// CHECK-DAG: <<Const43:i\d+>> IntConstant 43 - /// CHECK-DAG: If [<<Param>>] - /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Const42>>,<<Const43>>] - /// CHECK-DAG: Return [<<Phi>>] - - /// CHECK-START: int Main.NegatedCondition(boolean) select_generator (after) - /// CHECK-DAG: <<Param:z\d+>> ParameterValue - /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42 - /// CHECK-DAG: <<Const43:i\d+>> IntConstant 43 - /// CHECK-DAG: <<Select:i\d+>> Select [<<Const43>>,<<Const42>>,<<Param>>] - /// CHECK-DAG: Return [<<Select>>] - - /// CHECK-START: int Main.NegatedCondition(boolean) select_generator (after) - /// CHECK-NOT: BooleanNot - - public static int NegatedCondition(boolean x) { - if (x != false) { - return 42; - } else { - return 43; - } - } - /// CHECK-START: int Main.SimpleTrueBlock(boolean, int) select_generator (after) /// CHECK-DAG: <<ParamX:z\d+>> ParameterValue /// CHECK-DAG: <<ParamY:i\d+>> ParameterValue @@ -262,43 +160,6 @@ public class Main { } } - /// CHECK-START: int Main.MultiplePhis() select_generator (before) - /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 - /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 - /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13 - /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42 - /// CHECK-DAG: <<PhiX:i\d+>> Phi [<<Const0>>,<<Const13>>,<<Const42>>] - /// CHECK-DAG: <<PhiY:i\d+>> Phi [<<Const1>>,<<Add:i\d+>>,<<Add>>] - /// CHECK-DAG: <<Add>> Add [<<PhiY>>,<<Const1>>] - /// CHECK-DAG: <<Cond:z\d+>> LessThanOrEqual [<<Add>>,<<Const1>>] - /// CHECK-DAG: If [<<Cond>>] - /// CHECK-DAG: Return [<<PhiX>>] - - /// CHECK-START: int Main.MultiplePhis() select_generator (after) - /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 - /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 - /// CHECK-DAG: <<Const13:i\d+>> IntConstant 13 - /// CHECK-DAG: <<Const42:i\d+>> IntConstant 42 - /// CHECK-DAG: <<PhiX:i\d+>> Phi [<<Const0>>,<<Select:i\d+>>] - /// CHECK-DAG: <<PhiY:i\d+>> Phi [<<Const1>>,<<Add:i\d+>>] - /// CHECK-DAG: <<Add>> Add [<<PhiY>>,<<Const1>>] - /// CHECK-DAG: <<Cond:z\d+>> LessThanOrEqual [<<Add>>,<<Const1>>] - /// CHECK-DAG: <<Select>> Select [<<Const13>>,<<Const42>>,<<Cond>>] - /// CHECK-DAG: Return [<<PhiX>>] - - public static int MultiplePhis() { - int x = 0; - int y = 1; - while (y++ < 10) { - if (y > 1) { - x = 13; - } else { - x = 42; - } - } - return x; - } - /// CHECK-START: int Main.TrueBlockWithTooManyInstructions(boolean) select_generator (before) /// CHECK-DAG: <<This:l\d+>> ParameterValue /// CHECK-DAG: <<Cond:z\d+>> ParameterValue @@ -366,23 +227,30 @@ public class Main { } public static void main(String[] args) throws Exception { - assertBoolEquals(false, BooleanNot(true)); - assertBoolEquals(true, BooleanNot(false)); + Class main2 = Class.forName("Main2"); + Method booleanNot = main2.getMethod("BooleanNot", boolean.class); + Method valuesOrdered = main2.getMethod("ValuesOrdered", int.class, int.class, int.class); + Method negatedCondition = main2.getMethod("NegatedCondition", boolean.class); + Method multiplePhis = main2.getMethod("MultiplePhis"); + + assertBoolEquals(false, (boolean)booleanNot.invoke(null, true)); + assertBoolEquals(true, (boolean)booleanNot.invoke(null, false)); assertBoolEquals(true, GreaterThan(10, 5)); assertBoolEquals(false, GreaterThan(10, 10)); assertBoolEquals(false, GreaterThan(5, 10)); assertBoolEquals(true, LessThan(5, 10)); assertBoolEquals(false, LessThan(10, 10)); assertBoolEquals(false, LessThan(10, 5)); - assertBoolEquals(true, ValuesOrdered(1, 3, 5)); - assertBoolEquals(true, ValuesOrdered(5, 3, 1)); - assertBoolEquals(false, ValuesOrdered(1, 3, 2)); - assertBoolEquals(false, ValuesOrdered(2, 3, 1)); - assertBoolEquals(true, ValuesOrdered(3, 3, 3)); - assertBoolEquals(true, ValuesOrdered(3, 3, 5)); - assertBoolEquals(false, ValuesOrdered(5, 5, 3)); - assertIntEquals(42, NegatedCondition(true)); - assertIntEquals(43, NegatedCondition(false)); + + assertBoolEquals(true, (boolean)valuesOrdered.invoke(null, 1, 3, 5)); + assertBoolEquals(true, (boolean)valuesOrdered.invoke(null, 5, 3, 1)); + assertBoolEquals(false, (boolean)valuesOrdered.invoke(null, 1, 3, 2)); + assertBoolEquals(false, (boolean)valuesOrdered.invoke(null, 2, 3, 1)); + assertBoolEquals(true, (boolean)valuesOrdered.invoke(null, 3, 3, 3)); + assertBoolEquals(true, (boolean)valuesOrdered.invoke(null, 3, 3, 5)); + assertBoolEquals(false, (boolean)valuesOrdered.invoke(null, 5, 5, 3)); + assertIntEquals(42, (int)negatedCondition.invoke(null, true)); + assertIntEquals(43, (int)negatedCondition.invoke(null, false)); assertIntEquals(46, SimpleTrueBlock(true, 4)); assertIntEquals(43, SimpleTrueBlock(false, 4)); assertIntEquals(42, SimpleFalseBlock(true, 7)); @@ -393,7 +261,7 @@ public class Main { assertIntEquals(1, ThreeBlocks(true, false)); assertIntEquals(2, ThreeBlocks(false, true)); assertIntEquals(3, ThreeBlocks(false, false)); - assertIntEquals(13, MultiplePhis()); + assertIntEquals(13, (int)multiplePhis.invoke(null)); Main m = new Main(); assertIntEquals(42, m.TrueBlockWithTooManyInstructions(true)); diff --git a/test/530-checker-peel-unroll/src/Main.java b/test/530-checker-peel-unroll/src/Main.java index 2051b47afe..804c9fe916 100644 --- a/test/530-checker-peel-unroll/src/Main.java +++ b/test/530-checker-peel-unroll/src/Main.java @@ -455,6 +455,235 @@ public class Main { } } + /// CHECK-START: int Main.unrollingSimpleLiveOuts(int[]) loop_optimization (before) + /// CHECK-DAG: <<Array:l\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2 loop:none + /// CHECK-DAG: <<Limit:i\d+>> IntConstant 4094 loop:none + /// CHECK-DAG: <<PhiS:i\d+>> Phi [<<Const1>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<PhiT:i\d+>> Phi [<<Const2>>,{{i\d+}}] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<PhiI:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Check:z\d+>> GreaterThanOrEqual [<<PhiI>>,<<Limit>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<Check>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddI:i\d+>> Add [<<PhiI>>,<<Const1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Get0:i\d+>> ArrayGet [<<Array>>,<<AddI>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddS:i\d+>> Add [<<PhiS>>,<<Get0>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddT:i\d+>> Mul [<<PhiT>>,<<Get0>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Get1:i\d+>> ArrayGet [<<Array>>,<<PhiI>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddArr:i\d+>> Add [<<AddS>>,<<Get1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [<<Array>>,<<PhiI>>,<<AddArr>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-DAG: <<STAdd:i\d+>> Add [<<PhiS>>,<<PhiT>>] loop:none + /// CHECK-DAG: <<ZCheck:i\d+>> DivZeroCheck [<<STAdd>>] env:[[<<PhiS>>,<<PhiT>>,<<STAdd>>,<<Const1>>,_,<<Array>>]] loop:none + /// CHECK-DAG: <<Div:i\d+>> Div [<<Const1>>,<<ZCheck>>] loop:none + /// CHECK-DAG: Return [<<Div>>] loop:none + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + + /// CHECK-START: int Main.unrollingSimpleLiveOuts(int[]) loop_optimization (after) + /// CHECK-DAG: <<Array:l\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2 loop:none + /// CHECK-DAG: <<Limit:i\d+>> IntConstant 4094 loop:none + /// CHECK-DAG: <<PhiS:i\d+>> Phi [<<Const1>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<PhiT:i\d+>> Phi [<<Const2>>,{{i\d+}}] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<PhiI:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Check:z\d+>> GreaterThanOrEqual [<<PhiI>>,<<Limit>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<Check>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddI:i\d+>> Add [<<PhiI>>,<<Const1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Get0:i\d+>> ArrayGet [<<Array>>,<<AddI>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddS:i\d+>> Add [<<PhiS>>,<<Get0>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddT:i\d+>> Mul [<<PhiT>>,<<Get0>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Get1:i\d+>> ArrayGet [<<Array>>,<<PhiI>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddArr:i\d+>> Add [<<AddS>>,<<Get1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [<<Array>>,<<PhiI>>,<<AddArr>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-DAG: GreaterThanOrEqual [<<AddI>>,<<Limit>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<Const0>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddIA:i\d+>> Add [<<AddI>>,<<Const1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Get0A:i\d+>> ArrayGet [<<Array>>,<<AddIA>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddSA:i\d+>> Add [<<AddS>>,<<Get0A>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddTA:i\d+>> Mul [<<AddT>>,<<Get0A>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Get1A:i\d+>> ArrayGet [<<Array>>,<<AddI>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddArrA:i\d+>> Add [<<AddSA>>,<<Get1A>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [<<Array>>,<<AddI>>,<<AddArrA>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-DAG: <<RetPhiS:i\d+>> Phi [<<PhiS>>,<<AddS>>] loop:none + /// CHECK-DAG: <<RetPhiT:i\d+>> Phi [<<PhiT>>,<<AddT>>] loop:none + /// CHECK-DAG: <<STAdd:i\d+>> Add [<<RetPhiS>>,<<RetPhiT>>] loop:none + /// CHECK-DAG: <<ZCheck:i\d+>> DivZeroCheck [<<STAdd>>] env:[[<<RetPhiS>>,<<RetPhiT>>,<<STAdd>>,<<Const1>>,_,<<Array>>]] loop:none + /// CHECK-DAG: <<Div:i\d+>> Div [<<Const1>>,<<ZCheck>>] loop:none + /// CHECK-DAG: Return [<<Div>>] loop:none + // + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + private static final int unrollingSimpleLiveOuts(int[] a) { + int s = 1; + int t = 2; + for (int i = 0; i < LENGTH - 2; i++) { + int temp = a[i + 1]; + s += temp; + t *= temp; + a[i] += s; + } + + return 1 / (s + t); + } + + /// CHECK-START: int Main.unrollingWhileLiveOuts(int[]) loop_optimization (before) + /// CHECK-DAG: <<Array:l\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2 loop:none + /// CHECK-DAG: <<Const128:i\d+>> IntConstant 128 loop:none + /// CHECK-DAG: <<Limit:i\d+>> IntConstant 4094 loop:none + /// CHECK-DAG: <<PhiI:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<PhiS:i\d+>> Phi [<<Const128>>,{{i\d+}}] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddI:i\d+>> Add [<<PhiI>>,<<Const1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Check:z\d+>> GreaterThanOrEqual [<<PhiI>>,<<Limit>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<If:v\d+>> If [<<Check>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Rem:i\d+>> Rem [<<AddI>>,<<Const2>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<NE:z\d+>> NotEqual [<<Rem>>,<<Const0>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<NE>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddS:i\d+>> Add [<<PhiS>>,<<Const1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Phi [<<PhiS>>,<<AddS>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + + /// CHECK-START: int Main.unrollingWhileLiveOuts(int[]) loop_optimization (after) + /// CHECK-DAG: <<Array:l\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2 loop:none + /// CHECK-DAG: <<Const128:i\d+>> IntConstant 128 loop:none + /// CHECK-DAG: <<Limit:i\d+>> IntConstant 4094 loop:none + /// CHECK-DAG: <<PhiI:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<PhiS:i\d+>> Phi [<<Const128>>,{{i\d+}}] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddI:i\d+>> Add [<<PhiI>>,<<Const1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Check:z\d+>> GreaterThanOrEqual [<<PhiI>>,<<Limit>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<If:v\d+>> If [<<Check>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Rem:i\d+>> Rem [<<AddI>>,<<Const2>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<NE:z\d+>> NotEqual [<<Rem>>,<<Const0>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<NE>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddS:i\d+>> Add [<<PhiS>>,<<Const1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<PhiS>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<PhiSM:i\d+>> Phi [<<PhiS>>,<<AddS>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-DAG: <<AddIA:i\d+>> Add [<<AddI>>,<<Const1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<CheckA:z\d+>> GreaterThanOrEqual [<<AddI>>,<<Limit>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<IfA:v\d+>> If [<<Const0>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<RemA:i\d+>> Rem [<<AddIA>>,<<Const2>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<NEA:z\d+>> NotEqual [<<RemA>>,<<Const0>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: If [<<NEA>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<AddSA:i\d+>> Add [<<PhiSM>>,<<Const1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<<PhiSM>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: Phi [<<AddSA>>,<<PhiSM>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-DAG: <<RetPhi:i\d+>> Phi [<<PhiS>>,<<PhiSM>>] loop:none + /// CHECK-DAG: Return [<<RetPhi>>] loop:none + // + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + private static final int unrollingWhileLiveOuts(int[] a) { + int i = 0; + int s = 128; + while (i++ < LENGTH - 2) { + if (i % 2 == 0) { + a[i] = s++; + } + } + return s; + } + + /// CHECK-START: int Main.unrollingLiveOutsNested(int[]) loop_optimization (before) + /// CHECK-DAG: <<Array:l\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2 loop:none + /// CHECK-DAG: <<Limit:i\d+>> IntConstant 4094 loop:none + // + /// CHECK-DAG: <<OutPhiJ:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop0:B\d+>> outer_loop:none + /// CHECK-DAG: <<OutPhiS:i\d+>> Phi [<<Const1>>,{{i\d+}}] loop:<<Loop0>> outer_loop:none + /// CHECK-DAG: <<OutPhiT:i\d+>> Phi [<<Const2>>,{{i\d+}}] loop:<<Loop0>> outer_loop:none + // + /// CHECK-DAG: <<PhiI:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop1:B\d+>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<PhiS:i\d+>> Phi [<<OutPhiS>>,{{i\d+}}] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<PhiT:i\d+>> Phi [<<OutPhiT>>,{{i\d+}}] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<Check:z\d+>> GreaterThanOrEqual [<<PhiI>>,<<Limit>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: If [<<Check>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<AddI:i\d+>> Add [<<PhiI>>,<<Const1>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<Get0:i\d+>> ArrayGet [<<Array>>,<<AddI>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<AddS:i\d+>> Add [<<PhiS>>,<<Get0>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<AddT:i\d+>> Mul [<<PhiT>>,<<Get0>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<Get1:i\d+>> ArrayGet [<<Array>>,<<PhiI>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<AddArr:i\d+>> Add [<<AddS>>,<<Get1>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: ArraySet [<<Array>>,<<PhiI>>,<<AddArr>>] loop:<<Loop1>> outer_loop:<<Loop0>> + // + /// CHECK-DAG: Add [<<OutPhiJ>>,<<Const1>>] loop:<<Loop0>> outer_loop:none + // + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + + /// CHECK-START: int Main.unrollingLiveOutsNested(int[]) loop_optimization (after) + /// CHECK-DAG: <<Array:l\d+>> ParameterValue loop:none + /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none + /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2 loop:none + /// CHECK-DAG: <<Limit:i\d+>> IntConstant 4094 loop:none + // + /// CHECK-DAG: <<OutPhiJ:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop0:B\d+>> outer_loop:none + /// CHECK-DAG: <<OutPhiS:i\d+>> Phi [<<Const1>>,{{i\d+}}] loop:<<Loop0>> outer_loop:none + /// CHECK-DAG: <<OutPhiT:i\d+>> Phi [<<Const2>>,{{i\d+}}] loop:<<Loop0>> outer_loop:none + // + /// CHECK-DAG: <<PhiI:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop1:B\d+>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<PhiS:i\d+>> Phi [<<OutPhiS>>,{{i\d+}}] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<PhiT:i\d+>> Phi [<<OutPhiT>>,{{i\d+}}] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<Check:z\d+>> GreaterThanOrEqual [<<PhiI>>,<<Limit>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: If [<<Check>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<AddI:i\d+>> Add [<<PhiI>>,<<Const1>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<Get0:i\d+>> ArrayGet [<<Array>>,<<AddI>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<AddS:i\d+>> Add [<<PhiS>>,<<Get0>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<AddT:i\d+>> Mul [<<PhiT>>,<<Get0>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<Get1:i\d+>> ArrayGet [<<Array>>,<<PhiI>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<AddArr:i\d+>> Add [<<AddS>>,<<Get1>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: ArraySet [<<Array>>,<<PhiI>>,<<AddArr>>] loop:<<Loop1>> outer_loop:<<Loop0>> + // + /// CHECK-DAG: If [<<Const0>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<AddIA:i\d+>> Add [<<AddI>>,<<Const1>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<Get0A:i\d+>> ArrayGet [<<Array>>,<<AddIA>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<AddSA:i\d+>> Add [<<AddS>>,<<Get0A>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<AddTA:i\d+>> Mul [<<AddT>>,<<Get0A>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<Get1A:i\d+>> ArrayGet [<<Array>>,<<AddI>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: <<AddArrA:i\d+>> Add [<<AddSA>>,<<Get1A>>] loop:<<Loop1>> outer_loop:<<Loop0>> + /// CHECK-DAG: ArraySet [<<Array>>,<<AddI>>,<<AddArrA>>] loop:<<Loop1>> outer_loop:<<Loop0>> + // + /// CHECK-DAG: <<RetPhiS:i\d+>> Phi [<<PhiS>>,<<AddS>>] loop:<<Loop0>> outer_loop:none + /// CHECK-DAG: <<RetPhiT:i\d+>> Phi [<<PhiT>>,<<AddT>>] loop:<<Loop0>> outer_loop:none + /// CHECK-DAG: Add [<<OutPhiJ>>,<<Const1>>] loop:<<Loop0>> outer_loop:none + // + /// CHECK-DAG: <<RetAdd:i\d+>> Add [<<OutPhiS>>,<<OutPhiT>>] loop:none + /// CHECK-DAG: Return [<<RetAdd>>] loop:none + // + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + private static final int unrollingLiveOutsNested(int[] a) { + int s = 1; + int t = 2; + for (int j = 0; j < 16; j++) { + for (int i = 0; i < LENGTH - 2; i++) { + int temp = a[i + 1]; + s += temp; + t *= temp; + a[i] += s; + } + } + return s + t; + } + /// CHECK-START: void Main.noUnrollingOddTripCount(int[]) loop_optimization (before) /// CHECK-DAG: <<Array:l\d+>> ParameterValue loop:none /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none @@ -802,7 +1031,11 @@ public class Main { peelingBreakFromNest(a, false); peelingBreakFromNest(a, true); - int expected = 141312; + unrollingSimpleLiveOuts(a); + unrollingWhileLiveOuts(a); + unrollingLiveOutsNested(a); + + int expected = 51565978; int found = 0; for (int i = 0; i < a.length; i++) { found += a[i]; diff --git a/test/913-heaps/check b/test/913-heaps/check index c3b47f56ac..f7f8dab8cd 100644 --- a/test/913-heaps/check +++ b/test/913-heaps/check @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Jack/D8 has a different set of bytecode offsets/method IDs in the expected.txt +# D8 has a different set of bytecode offsets/method IDs in the expected.txt if [[ "$USE_D8" == true ]]; then patch -p0 expected.txt < expected_d8.diff fi diff --git a/test/980-redefine-object/redef_object.cc b/test/980-redefine-object/redef_object.cc new file mode 100644 index 0000000000..b4d82ad76d --- /dev/null +++ b/test/980-redefine-object/redef_object.cc @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2018 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. + */ + +#include <limits> +#include <memory> + +#include "jni.h" +#include "jvmti.h" + +// Test infrastructure +#include "jvmti_helper.h" +#include "test_env.h" + +// Slicer's headers have code that triggers these warnings. b/65298177 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wsign-compare" +#pragma clang diagnostic ignored "-Wunused-parameter" +#include "slicer/instrumentation.h" +#include "slicer/reader.h" +#include "slicer/writer.h" +#pragma clang diagnostic pop + +namespace art { +namespace Test980RedefineObject { + +static void JNICALL RedefineObjectHook(jvmtiEnv *jvmti_env, + JNIEnv* env, + jclass class_being_redefined ATTRIBUTE_UNUSED, + jobject loader ATTRIBUTE_UNUSED, + const char* name, + jobject protection_domain ATTRIBUTE_UNUSED, + jint class_data_len, + const unsigned char* class_data, + jint* new_class_data_len, + unsigned char** new_class_data) { + if (strcmp(name, "java/lang/Object") != 0) { + return; + } + + dex::Reader reader(class_data, class_data_len); + dex::u4 class_index = reader.FindClassIndex("Ljava/lang/Object;"); + if (class_index == dex::kNoIndex) { + env->ThrowNew(env->FindClass("java/lang/RuntimeException"), + "Failed to find object in dex file!"); + return; + } + + reader.CreateClassIr(class_index); + auto dex_ir = reader.GetIr(); + + slicer::MethodInstrumenter mi(dex_ir); + mi.AddTransformation<slicer::EntryHook>(ir::MethodId("Lart/test/TestWatcher;", + "NotifyConstructed"), + /*this_as_object*/ true); + if (!mi.InstrumentMethod(ir::MethodId("Ljava/lang/Object;", + "<init>", + "()V"))) { + env->ThrowNew(env->FindClass("java/lang/RuntimeException"), + "Failed to find Object;-><init>()V in dex file!"); + return; + } + + + dex::Writer writer(dex_ir); + + class JvmtiAllocator : public dex::Writer::Allocator { + public: + explicit JvmtiAllocator(jvmtiEnv* jvmti) : jvmti_(jvmti) {} + + void* Allocate(size_t size) { + unsigned char* res = nullptr; + jvmti_->Allocate(size, &res); + return res; + } + + void Free(void* ptr) { + jvmti_->Deallocate(reinterpret_cast<unsigned char*>(ptr)); + } + + private: + jvmtiEnv* jvmti_; + }; + JvmtiAllocator allocator(jvmti_env); + size_t new_size; + *new_class_data = writer.CreateImage(&allocator, &new_size); + if (new_size > std::numeric_limits<jint>::max()) { + *new_class_data = nullptr; + env->ThrowNew(env->FindClass("java/lang/RuntimeException"), + "transform result is too large!"); + return; + } + *new_class_data_len = static_cast<jint>(new_size); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_addMemoryTrackingCall(JNIEnv* env, + jclass klass ATTRIBUTE_UNUSED, + jclass obj_class, + jthread thr) { + jvmtiCapabilities caps {.can_retransform_classes = 1}; + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->AddCapabilities(&caps))) { + return; + } + jvmtiEventCallbacks cb {.ClassFileLoadHook = RedefineObjectHook }; + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) { + return; + } + if (JvmtiErrorToException(env, + jvmti_env, + jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, + JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, + thr))) { + return; + } + if (JvmtiErrorToException(env, + jvmti_env, + jvmti_env->RetransformClasses(1, &obj_class))) { + return; + } + if (JvmtiErrorToException(env, + jvmti_env, + jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, + JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, + thr))) { + return; + } +} + +} // namespace Test980RedefineObject +} // namespace art + diff --git a/test/980-redefine-object/src/Main.java b/test/980-redefine-object/src/Main.java index 2428b55a4e..efbc75f6c5 100644 --- a/test/980-redefine-object/src/Main.java +++ b/test/980-redefine-object/src/Main.java @@ -14,278 +14,16 @@ * limitations under the License. */ -import static art.Redefinition.doCommonClassRedefinition; - import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Base64; import java.util.LinkedList; public class Main { - - // TODO We should make this run on the RI. - /** - * This test cannot be run on the RI. - */ - private static final byte[] CLASS_BYTES = new byte[0]; - - // TODO It might be a good idea to replace this hard-coded Object definition with a - // retransformation based test. /** - * Base64 encoding of the following smali file. - * - * .class public Ljava/lang/Object; - * .source "Object.java" - * # instance fields - * .field private transient shadow$_klass_:Ljava/lang/Class; - * .annotation system Ldalvik/annotation/Signature; - * value = { - * "Ljava/lang/Class", - * "<*>;" - * } - * .end annotation - * .end field - * - * .field private transient shadow$_monitor_:I - * # direct methods - * .method public constructor <init>()V - * .registers 1 - * .prologue - * invoke-static {p0}, Lart/test/TestWatcher;->NotifyConstructed(Ljava/lang/Object;)V - * return-void - * .end method - * - * .method static identityHashCode(Ljava/lang/Object;)I - * .registers 7 - * .prologue - * iget v0, p0, Ljava/lang/Object;->shadow$_monitor_:I - * const/high16 v3, -0x40000000 # -2.0f - * const/high16 v2, -0x80000000 - * const v1, 0xfffffff - * const/high16 v4, -0x40000000 # -2.0f - * and-int/2addr v4, v0 - * const/high16 v5, -0x80000000 - * if-ne v4, v5, :cond_15 - * const v4, 0xfffffff - * and-int/2addr v4, v0 - * return v4 - * :cond_15 - * invoke-static {p0}, Ljava/lang/Object;->identityHashCodeNative(Ljava/lang/Object;)I - * move-result v4 - * return v4 - * .end method - * - * .method private static native identityHashCodeNative(Ljava/lang/Object;)I - * .annotation build Ldalvik/annotation/optimization/FastNative; - * .end annotation - * .end method - * - * .method private native internalClone()Ljava/lang/Object; - * .annotation build Ldalvik/annotation/optimization/FastNative; - * .end annotation - * .end method - * - * - * # virtual methods - * .method protected clone()Ljava/lang/Object; - * .registers 4 - * .annotation system Ldalvik/annotation/Throws; - * value = { - * Ljava/lang/CloneNotSupportedException; - * } - * .end annotation - * - * .prologue - * instance-of v0, p0, Ljava/lang/Cloneable; - * if-nez v0, :cond_2d - * new-instance v0, Ljava/lang/CloneNotSupportedException; - * new-instance v1, Ljava/lang/StringBuilder; - * invoke-direct {v1}, Ljava/lang/StringBuilder;-><init>()V - * const-string/jumbo v2, "Class " - * invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; - * move-result-object v1 - * invoke-virtual {p0}, Ljava/lang/Object;->getClass()Ljava/lang/Class; - * move-result-object v2 - * invoke-virtual {v2}, Ljava/lang/Class;->getName()Ljava/lang/String; - * move-result-object v2 - * invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; - * move-result-object v1 - * const-string/jumbo v2, " doesn\'t implement Cloneable" - * invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; - * move-result-object v1 - * invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; - * move-result-object v1 - * invoke-direct {v0, v1}, Ljava/lang/CloneNotSupportedException;-><init>(Ljava/lang/String;)V - * throw v0 - * :cond_2d - * invoke-direct {p0}, Ljava/lang/Object;->internalClone()Ljava/lang/Object; - * move-result-object v0 - * return-object v0 - * .end method - * - * .method public equals(Ljava/lang/Object;)Z - * .registers 3 - * .prologue - * if-ne p0, p1, :cond_4 - * const/4 v0, 0x1 - * :goto_3 - * return v0 - * :cond_4 - * const/4 v0, 0x0 - * goto :goto_3 - * .end method - * - * .method protected finalize()V - * .registers 1 - * .annotation system Ldalvik/annotation/Throws; - * value = { - * Ljava/lang/Throwable; - * } - * .end annotation - * .prologue - * return-void - * .end method - * - * .method public final getClass()Ljava/lang/Class; - * .registers 2 - * .annotation system Ldalvik/annotation/Signature; - * value = { - * "()", - * "Ljava/lang/Class", - * "<*>;" - * } - * .end annotation - * .prologue - * iget-object v0, p0, Ljava/lang/Object;->shadow$_klass_:Ljava/lang/Class; - * return-object v0 - * .end method - * - * .method public hashCode()I - * .registers 2 - * .prologue - * invoke-static {p0}, Ljava/lang/Object;->identityHashCode(Ljava/lang/Object;)I - * move-result v0 - * return v0 - * .end method - * - * .method public final native notify()V - * .annotation build Ldalvik/annotation/optimization/FastNative; - * .end annotation - * .end method - * - * .method public final native notifyAll()V - * .annotation build Ldalvik/annotation/optimization/FastNative; - * .end annotation - * .end method - * - * .method public toString()Ljava/lang/String; - * .registers 3 - * .prologue - * new-instance v0, Ljava/lang/StringBuilder; - * invoke-direct {v0}, Ljava/lang/StringBuilder;-><init>()V - * invoke-virtual {p0}, Ljava/lang/Object;->getClass()Ljava/lang/Class; - * move-result-object v1 - * invoke-virtual {v1}, Ljava/lang/Class;->getName()Ljava/lang/String; - * move-result-object v1 - * invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; - * move-result-object v0 - * const-string/jumbo v1, "@" - * invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; - * move-result-object v0 - * invoke-virtual {p0}, Ljava/lang/Object;->hashCode()I - * move-result v1 - * invoke-static {v1}, Ljava/lang/Integer;->toHexString(I)Ljava/lang/String; - * move-result-object v1 - * invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; - * move-result-object v0 - * invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; - * move-result-object v0 - * return-object v0 - * .end method - * - * .method public final native wait()V - * .annotation system Ldalvik/annotation/Throws; - * value = { - * Ljava/lang/InterruptedException; - * } - * .end annotation - * - * .annotation build Ldalvik/annotation/optimization/FastNative; - * .end annotation - * .end method - * - * .method public final wait(J)V - * .registers 4 - * .annotation system Ldalvik/annotation/Throws; - * value = { - * Ljava/lang/InterruptedException; - * } - * .end annotation - * .prologue - * const/4 v0, 0x0 - * invoke-virtual {p0, p1, p2, v0}, Ljava/lang/Object;->wait(JI)V - * return-void - * .end method - * - * .method public final native wait(JI)V - * .annotation system Ldalvik/annotation/Throws; - * value = { - * Ljava/lang/InterruptedException; - * } - * .end annotation - * - * .annotation build Ldalvik/annotation/optimization/FastNative; - * .end annotation - * .end method + * NB This test cannot be run on the RI. + * TODO We should make this run on the RI. */ - private static final byte[] DEX_BYTES = Base64.getDecoder().decode( - "ZGV4CjAzNQDUlMR9j03MYuOKekKs2p7zJzu2IfDb7RlMCgAAcAAAAHhWNBIAAAAAAAAAAIgJAAA6" + - "AAAAcAAAABEAAABYAQAADQAAAJwBAAACAAAAOAIAABYAAABIAgAAAQAAAPgCAAA0BwAAGAMAABgD" + - "AAA2AwAAOgMAAEADAABIAwAASwMAAFMDAABWAwAAWgMAAF0DAABgAwAAZAMAAGgDAACAAwAAnwMA" + - "ALsDAADoAwAA+gMAAA0EAAA1BAAATAQAAGEEAACDBAAAlwQAAKsEAADGBAAA3QQAAPAEAAD9BAAA" + - "AAUAAAQFAAAJBQAADQUAABAFAAAUBQAAHAUAACMFAAArBQAANQUAAD8FAABIBQAAUgUAAGQFAAB8" + - "BQAAiwUAAJUFAACnBQAAugUAAM0FAADVBQAA3QUAAOgFAADtBQAA/QUAAA8GAAAcBgAAJgYAAC0G" + - "AAAGAAAACAAAAAwAAAANAAAADgAAAA8AAAARAAAAEgAAABMAAAAUAAAAFQAAABYAAAAXAAAAGAAA" + - "ABkAAAAcAAAAIAAAAAYAAAAAAAAAAAAAAAcAAAAAAAAAPAYAAAkAAAAGAAAAAAAAAAkAAAALAAAA" + - "AAAAAAkAAAAMAAAAAAAAAAoAAAAMAAAARAYAAAsAAAANAAAAVAYAABwAAAAPAAAAAAAAAB0AAAAP" + - "AAAATAYAAB4AAAAPAAAANAYAAB8AAAAPAAAAPAYAAB8AAAAPAAAAVAYAACEAAAAQAAAAPAYAAAsA" + - "BgA0AAAACwAAADUAAAACAAoAGgAAAAYABAAnAAAABwALAAMAAAAJAAUANgAAAAsABwADAAAACwAD" + - "ACMAAAALAAwAJAAAAAsABwAlAAAACwACACYAAAALAAAAKAAAAAsAAQApAAAACwABACoAAAALAAMA" + - "KwAAAAsABwAxAAAACwAHADIAAAALAAQANwAAAAsABwA5AAAACwAIADkAAAALAAkAOQAAAA0ABwAD" + - "AAAADQAGACIAAAANAAQANwAAAAsAAAABAAAA/////wAAAAAbAAAA0AYAAD4JAAAAAAAAHCBkb2Vz" + - "bid0IGltcGxlbWVudCBDbG9uZWFibGUAAigpAAQ8Kj47AAY8aW5pdD4AAUAABkNsYXNzIAABSQAC" + - "SUwAAUoAAUwAAkxJAAJMTAAWTGFydC90ZXN0L1Rlc3RXYXRjaGVyOwAdTGRhbHZpay9hbm5vdGF0" + - "aW9uL1NpZ25hdHVyZTsAGkxkYWx2aWsvYW5ub3RhdGlvbi9UaHJvd3M7ACtMZGFsdmlrL2Fubm90" + - "YXRpb24vb3B0aW1pemF0aW9uL0Zhc3ROYXRpdmU7ABBMamF2YS9sYW5nL0NsYXNzABFMamF2YS9s" + - "YW5nL0NsYXNzOwAmTGphdmEvbGFuZy9DbG9uZU5vdFN1cHBvcnRlZEV4Y2VwdGlvbjsAFUxqYXZh" + - "L2xhbmcvQ2xvbmVhYmxlOwATTGphdmEvbGFuZy9JbnRlZ2VyOwAgTGphdmEvbGFuZy9JbnRlcnJ1" + - "cHRlZEV4Y2VwdGlvbjsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7ABlM" + - "amF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7ABVMamF2YS9sYW5nL1Rocm93YWJsZTsAEU5vdGlmeUNv" + - "bnN0cnVjdGVkAAtPYmplY3QuamF2YQABVgACVkoAA1ZKSQACVkwAAVoAAlpMAAZhcHBlbmQABWNs" + - "b25lAAZlcXVhbHMACGZpbmFsaXplAAhnZXRDbGFzcwAHZ2V0TmFtZQAIaGFzaENvZGUAEGlkZW50" + - "aXR5SGFzaENvZGUAFmlkZW50aXR5SGFzaENvZGVOYXRpdmUADWludGVybmFsQ2xvbmUACGxvY2tX" + - "b3JkABBsb2NrV29yZEhhc2hNYXNrABFsb2NrV29yZFN0YXRlSGFzaAARbG9ja1dvcmRTdGF0ZU1h" + - "c2sABm1pbGxpcwAGbm90aWZ5AAlub3RpZnlBbGwAA29iagAOc2hhZG93JF9rbGFzc18AEHNoYWRv" + - "dyRfbW9uaXRvcl8AC3RvSGV4U3RyaW5nAAh0b1N0cmluZwAFdmFsdWUABHdhaXQAAAIAAAABAAAA" + - "AQAAAAsAAAABAAAAAAAAAAEAAAABAAAAAQAAAAwAAgQBOBwBGAcCBAE4HAEYCgIDATgcAhcQFwIC" + - "BAE4HAEYDgAFAAIDATgcAxcBFxAXAgAAAAAAAAAAAAEAAABaBgAAAgAAAGIGAAB8BgAAAQAAAGIG" + - "AAABAAAAagYAAAEAAAB0BgAAAQAAAHwGAAABAAAAfwYAAAAAAAABAAAACgAAAAAAAAAAAAAAsAYA" + - "AAUAAACUBgAABwAAALgGAAAIAAAAyAYAAAsAAADABgAADAAAAMAGAAANAAAAwAYAAA4AAADABgAA" + - "EAAAAJwGAAARAAAAqAYAABIAAACcBgAAKAAHDgBwATQHDi0DAC0BLQMDMAEtAwIvATwDAS4BeFsA" + - "7AEABw5LARoPOsYArAEBNAcOAMUEAAcOAEEABw4AaAAHDgCRAgAHDgCmAwExBw5LAAAAAQABAAEA" + - "AAA4BwAABAAAAHEQAAAAAA4ABwABAAEAAAA9BwAAGgAAAFJgAQAVAwDAFQIAgBQB////DxUEAMC1" + - "BBUFAIAzVAcAFAT///8PtQQPBHEQCwAGAAoEDwQEAAEAAgAAAFkHAAAyAAAAIDAIADkAKwAiAAcA" + - "IgENAHAQEwABABsCBQAAAG4gFAAhAAwBbhAIAAMADAJuEAEAAgAMAm4gFAAhAAwBGwIAAAAAbiAU" + - "ACEADAFuEBUAAQAMAXAgAgAQACcAcBAMAAMADAARAAMAAgAAAAAAZQcAAAYAAAAzIQQAEhAPABIA" + - "KP4BAAEAAAAAAGwHAAABAAAADgAAAAIAAQAAAAAAcgcAAAMAAABUEAAAEQAAAAIAAQABAAAAdwcA" + - "AAUAAABxEAoAAQAKAA8AAAADAAEAAgAAAHwHAAApAAAAIgANAHAQEwAAAG4QCAACAAwBbhABAAEA" + - "DAFuIBQAEAAMABsBBAAAAG4gFAAQAAwAbhAJAAIACgFxEAMAAQAMAW4gFAAQAAwAbhAVAAAADAAR" + - "AAAABAADAAQAAACCBwAABQAAABIAbkASACEDDgAAAgQLAIIBAYIBBIGABIwPBgikDwGKAgABggIA" + - "BQToDwEB3BABBPgQARGMEQEBpBEEkQIAAZECAAEBwBEBkQIAARGkEgGRAgAAABAAAAAAAAAAAQAA" + - "AAAAAAABAAAAOgAAAHAAAAACAAAAEQAAAFgBAAADAAAADQAAAJwBAAAEAAAAAgAAADgCAAAFAAAA" + - "FgAAAEgCAAAGAAAAAQAAAPgCAAACIAAAOgAAABgDAAABEAAABQAAADQGAAAEIAAABgAAAFoGAAAD" + - "EAAACQAAAIwGAAAGIAAAAQAAANAGAAADIAAACQAAADgHAAABIAAACQAAAIwHAAAAIAAAAQAAAD4J" + - "AAAAEAAAAQAAAIgJAAA="); private static final String LISTENER_LOCATION = System.getenv("DEX_LOCATION") + "/980-redefine-object-ex.jar"; @@ -361,7 +99,7 @@ public class Main { // Redefine the Object Class. safePrintln("Redefining the Object class to add a hook into the <init> method"); - doCommonClassRedefinition(Object.class, CLASS_BYTES, DEX_BYTES); + addMemoryTrackingCall(Object.class, Thread.currentThread()); safePrintln("Allocating an j.l.Object after redefining Object class"); Object o2 = new Object(); @@ -407,5 +145,10 @@ public class Main { safePrintln("Finishing test!"); } + // This is from 929-search/search.cc private static native void addToBootClassLoader(String s); + // This is from 980-redefine-object/redef_object.cc + // It will add a call to Lart/test/TestWatcher;->NotifyConstructed()V in the Object <init>()V + // function. + private static native void addMemoryTrackingCall(Class c, Thread thr); } diff --git a/test/980-redefine-object/src/art/Redefinition.java b/test/980-redefine-object/src/art/Redefinition.java deleted file mode 100644 index 56d2938a01..0000000000 --- a/test/980-redefine-object/src/art/Redefinition.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package art; - -import java.util.ArrayList; -// Common Redefinition functions. Placed here for use by CTS -public class Redefinition { - public static final class CommonClassDefinition { - public final Class<?> target; - public final byte[] class_file_bytes; - public final byte[] dex_file_bytes; - - public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) { - this.target = target; - this.class_file_bytes = class_file_bytes; - this.dex_file_bytes = dex_file_bytes; - } - } - - // A set of possible test configurations. Test should set this if they need to. - // This must be kept in sync with the defines in ti-agent/common_helper.cc - public static enum Config { - COMMON_REDEFINE(0), - COMMON_RETRANSFORM(1), - COMMON_TRANSFORM(2); - - private final int val; - private Config(int val) { - this.val = val; - } - } - - public static void setTestConfiguration(Config type) { - nativeSetTestConfiguration(type.val); - } - - private static native void nativeSetTestConfiguration(int type); - - // Transforms the class - public static native void doCommonClassRedefinition(Class<?> target, - byte[] classfile, - byte[] dexfile); - - public static void doMultiClassRedefinition(CommonClassDefinition... defs) { - ArrayList<Class<?>> classes = new ArrayList<>(); - ArrayList<byte[]> class_files = new ArrayList<>(); - ArrayList<byte[]> dex_files = new ArrayList<>(); - - for (CommonClassDefinition d : defs) { - classes.add(d.target); - class_files.add(d.class_file_bytes); - dex_files.add(d.dex_file_bytes); - } - doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]), - class_files.toArray(new byte[0][]), - dex_files.toArray(new byte[0][])); - } - - public static void addMultiTransformationResults(CommonClassDefinition... defs) { - for (CommonClassDefinition d : defs) { - addCommonTransformationResult(d.target.getCanonicalName(), - d.class_file_bytes, - d.dex_file_bytes); - } - } - - public static native void doCommonMultiClassRedefinition(Class<?>[] targets, - byte[][] classfiles, - byte[][] dexfiles); - public static native void doCommonClassRetransformation(Class<?>... target); - public static native void setPopRetransformations(boolean pop); - public static native void popTransformationFor(String name); - public static native void enableCommonRetransformation(boolean enable); - public static native void addCommonTransformationResult(String target_name, - byte[] class_bytes, - byte[] dex_bytes); -} diff --git a/test/Android.bp b/test/Android.bp index 7909bf897a..e0ec286314 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -314,10 +314,15 @@ art_cc_defaults { "909-attach-agent/attach.cc", "912-classes/classes_art.cc", "936-search-onload/search_onload.cc", + "980-redefine-object/redef_object.cc", "983-source-transform-verify/source_transform_art.cc", "1940-ddms-ext/ddm_ext.cc", "1944-sudden-exit/sudden_exit.cc", ], + static_libs: [ + "libz", + "slicer", + ], } art_cc_test_library { diff --git a/test/run-test b/test/run-test index 8e012d13fb..5bd8b3b348 100755 --- a/test/run-test +++ b/test/run-test @@ -441,6 +441,11 @@ while true; do fi done +if [ "$usage" = "no" -a "x$1" = "x" ]; then + echo "missing test to run" 1>&2 + usage="yes" +fi + # The DEX_LOCATION with the chroot prefix, if any. chroot_dex_location="$chroot$DEX_LOCATION" diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py index 09b9b210fc..be7e225e05 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -411,8 +411,9 @@ def run_tests(tests): elif prebuild == 'no-dex2oat': options_test += ' --no-prebuild --no-dex2oat' - # Add option and remove the cdex- prefix. - options_test += ' --compact-dex-level ' + cdex_level.replace('cdex-','') + if cdex_level: + # Add option and remove the cdex- prefix. + options_test += ' --compact-dex-level ' + cdex_level.replace('cdex-','') if compiler == 'optimizing': options_test += ' --optimizing' diff --git a/tools/build/var_list b/tools/build/var_list index adcb066f7c..bb005cf77c 100644 --- a/tools/build/var_list +++ b/tools/build/var_list @@ -33,5 +33,4 @@ TARGET_ARCH HOST_PREFER_32_BIT HOST_OUT_EXECUTABLES ANDROID_JAVA_TOOLCHAIN -ANDROID_COMPILE_WITH_JACK diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh index 10eb9360af..830505124e 100755 --- a/tools/buildbot-build.sh +++ b/tools/buildbot-build.sh @@ -32,8 +32,6 @@ else out_dir=${OUT_DIR} fi -using_jack=$(get_build_var ANDROID_COMPILE_WITH_JACK) - java_libraries_dir=${out_dir}/target/common/obj/JAVA_LIBRARIES common_targets="vogar core-tests apache-harmony-jdwp-tests-hostdex jsr166-tests mockito-target" mode="target" @@ -62,10 +60,6 @@ while true; do fi done -if [[ $using_jack == "true" ]]; then - common_targets="$common_targets ${out_dir}/host/linux-x86/bin/jack" -fi - # Allow to build successfully in master-art. extra_args=SOONG_ALLOW_MISSING_DEPENDENCIES=true diff --git a/tools/cleanup-buildbot-device.sh b/tools/cleanup-buildbot-device.sh index 8c28828261..2144b02c2f 100755 --- a/tools/cleanup-buildbot-device.sh +++ b/tools/cleanup-buildbot-device.sh @@ -40,10 +40,6 @@ if [[ -n "$ART_TEST_CHROOT" ]]; then # # TODO: Reorder ART Buildbot steps so that "device cleanup" happens # before "setup device" and remove this special case. - # - # TODO: Also consider adding a "tear down device" step on the ART - # Buildbot (at the very end of a build) undoing (some of) the work - # done in the "device setup" step. adb shell test -f "$ART_TEST_CHROOT/system" \ "&&" find "$ART_TEST_CHROOT/system" \ ! -path "$ART_TEST_CHROOT/system/etc/selinux/plat_property_contexts" \ diff --git a/tools/common/common.py b/tools/common/common.py index 735bbaa4a4..b728e8d927 100755 --- a/tools/common/common.py +++ b/tools/common/common.py @@ -116,14 +116,6 @@ def GetEnvVariableOrError(variable_name): return top -def GetJackClassPath(): - """Returns Jack's classpath.""" - top = GetEnvVariableOrError('ANDROID_BUILD_TOP') - libdir = top + '/out/host/common/obj/JAVA_LIBRARIES' - return libdir + '/core-libart-hostdex_intermediates/classes.jack:' \ - + libdir + '/core-oj-hostdex_intermediates/classes.jack' - - def _DexArchCachePaths(android_data_path): """Returns paths to architecture specific caches. diff --git a/tools/dexanalyze/dexanalyze_experiments.cc b/tools/dexanalyze/dexanalyze_experiments.cc index 427a465caf..312a7b3159 100644 --- a/tools/dexanalyze/dexanalyze_experiments.cc +++ b/tools/dexanalyze/dexanalyze_experiments.cc @@ -150,7 +150,7 @@ void CountDexIndices::ProcessDexFile(const DexFile& dex_file) { case Instruction::INVOKE_VIRTUAL_RANGE: { bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE); uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); - if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetDescriptorIndex()) { + if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) { ++same_class_virtual_; } else { ++other_class_virtual_; @@ -162,7 +162,7 @@ void CountDexIndices::ProcessDexFile(const DexFile& dex_file) { case Instruction::INVOKE_DIRECT_RANGE: { bool is_range = (inst->Opcode() == Instruction::INVOKE_DIRECT_RANGE); uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); - if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetDescriptorIndex()) { + if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) { ++same_class_direct_; } else { ++other_class_direct_; @@ -174,7 +174,7 @@ void CountDexIndices::ProcessDexFile(const DexFile& dex_file) { case Instruction::INVOKE_STATIC_RANGE: { bool is_range = (inst->Opcode() == Instruction::INVOKE_STATIC_RANGE); uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); - if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetDescriptorIndex()) { + if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) { ++same_class_static_; } else { ++other_class_static_; diff --git a/tools/golem/build-target.sh b/tools/golem/build-target.sh index 4ca2722ac9..921a8cbe36 100755 --- a/tools/golem/build-target.sh +++ b/tools/golem/build-target.sh @@ -267,8 +267,6 @@ if [[ $mode == "golem" ]]; then execute 'source' build/envsetup.sh # Build generic targets (as opposed to something specific like aosp_angler-eng). execute lunch "$lunch_target" - setenv JACK_SERVER false - setenv_escape JACK_REPOSITORY "$PWD/prebuilts/sdk/tools/jacks" '$PWD/prebuilts/sdk/tools/jacks' # Golem uses master-art repository which is missing a lot of other libraries. setenv SOONG_ALLOW_MISSING_DEPENDENCIES true # Golem may be missing tools such as javac from its path. diff --git a/tools/jfuzz/README.md b/tools/jfuzz/README.md index eb0e71f53d..f32cf561d6 100644 --- a/tools/jfuzz/README.md +++ b/tools/jfuzz/README.md @@ -37,8 +37,10 @@ The current version of JFuzz sends all output to stdout, and uses a fixed testing class named Test. So a typical test run looks as follows. jfuzz > Test.java - jack -cp ${JACK_CLASSPATH} --output-dex . Test.java - art -classpath classes.dex Test + mkdir classes + javac -d classes Test.java + dx --dex --output=classes.dex classes + art -cp classes.dex Test How to start JFuzz testing ========================== @@ -67,7 +69,7 @@ where --report_script : path to script called for each divergence --jfuzz_arg : argument for jfuzz --true_divergence : don't bisect timeout divergences - --dexer=DEXER : use either dx, d8, or jack to obtain dex files + --dexer=DEXER : use either dx or d8 to obtain dex files --debug_info : include debugging info How to start JFuzz nightly testing @@ -97,7 +99,7 @@ where --num_tests : number of tests to run (10000 by default) --num_inputs : number of JFuzz programs to generate --device : target device serial number (passed to adb -s) - --dexer=DEXER : use either dx, d8, or jack to obtain dex files + --dexer=DEXER : use either dx or d8 to obtain dex files --debug_info : include debugging info Background diff --git a/tools/jfuzz/run_dex_fuzz_test.py b/tools/jfuzz/run_dex_fuzz_test.py index 47fe072b6d..64bf23405b 100755 --- a/tools/jfuzz/run_dex_fuzz_test.py +++ b/tools/jfuzz/run_dex_fuzz_test.py @@ -28,7 +28,6 @@ sys.path.append(os.path.dirname(os.path.dirname( from common.common import FatalError from common.common import GetEnvVariableOrError -from common.common import GetJackClassPath from common.common import RetCode from common.common import RunCommand @@ -106,7 +105,7 @@ class DexFuzzTester(object): self.RunDexFuzz() def CompileOnHost(self): - """Compiles Test.java into classes.dex using either javac/dx,d8 or jack. + """Compiles Test.java into classes.dex using either javac/dx or d8. Raises: FatalError: error when compilation fails @@ -128,15 +127,6 @@ class DexFuzzTester(object): os.unlink(cfile) os.unlink('jerr.txt') os.unlink('dxerr.txt') - - elif self._dexer == 'jack': - jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.', 'Test.java'] - if RunCommand(['jack'] + jack_args, out=None, err='jackerr.txt', - timeout=30) != RetCode.SUCCESS: - print('Unexpected error while running Jack') - raise FatalError('Unexpected error while running Jack') - # Cleanup on success (nothing to see). - os.unlink('jackerr.txt') else: raise FatalError('Unknown dexer: ' + self._dexer) @@ -188,7 +178,7 @@ def main(): help='number of JFuzz program to generate (default: 10)') parser.add_argument('--device', help='target device serial number') parser.add_argument('--dexer', default='dx', type=str, - help='defines dexer as dx, d8, or jack (default: dx)') + help='defines dexer as dx or d8 (default: dx)') parser.add_argument('--debug_info', default=False, action='store_true', help='include debugging info') args = parser.parse_args() diff --git a/tools/jfuzz/run_jfuzz_test.py b/tools/jfuzz/run_jfuzz_test.py index 3ff9f450a1..f8bfd8dda7 100755 --- a/tools/jfuzz/run_jfuzz_test.py +++ b/tools/jfuzz/run_jfuzz_test.py @@ -33,7 +33,6 @@ sys.path.append(os.path.dirname(os.path.dirname( from common.common import RetCode from common.common import CommandListToCommandString from common.common import FatalError -from common.common import GetJackClassPath from common.common import GetEnvVariableOrError from common.common import RunCommand from common.common import RunCommandForOutput @@ -127,8 +126,6 @@ class TestRunnerWithHostCompilation(TestRunner): """ self._dexer = dexer self._debug_info = debug_info - self._jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.', - 'Test.java'] def CompileOnHost(self): if self._dexer == 'dx' or self._dexer == 'd8': @@ -140,9 +137,6 @@ class TestRunnerWithHostCompilation(TestRunner): out=None, err='dxerr.txt', timeout=30) else: retc = RetCode.NOTCOMPILED - elif self._dexer == 'jack': - retc = RunCommand(['jack'] + self._jack_args, - out=None, err='jackerr.txt', timeout=30) else: raise FatalError('Unknown dexer: ' + self._dexer) return retc @@ -632,7 +626,7 @@ def main(): parser.add_argument('--true_divergence', default=False, action='store_true', help='do not bisect timeout divergences') parser.add_argument('--dexer', default='dx', type=str, - help='defines dexer as dx, d8, or jack (default: dx)') + help='defines dexer as dx or d8 (default: dx)') parser.add_argument('--debug_info', default=False, action='store_true', help='include debugging info') args = parser.parse_args() diff --git a/tools/setup-buildbot-device.sh b/tools/setup-buildbot-device.sh index f71d973925..f89197580d 100755 --- a/tools/setup-buildbot-device.sh +++ b/tools/setup-buildbot-device.sh @@ -14,10 +14,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +# The work does by this script is (mostly) undone by tools/teardown-buildbot-device.sh. +# Make sure to keep these files in sync. + green='\033[0;32m' nc='\033[0m' -# Setup as root, as some actions performed here (e.g. setting the date) requires it. +# Setup as root, as some actions performed here require it. adb root adb wait-for-device diff --git a/tools/teardown-buildbot-device.sh b/tools/teardown-buildbot-device.sh new file mode 100755 index 0000000000..df239a28bc --- /dev/null +++ b/tools/teardown-buildbot-device.sh @@ -0,0 +1,70 @@ +#!/bin/bash +# +# Copyright (C) 2018 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. + +# This script undoes (most of) the work done by tools/setup-buildbot-device.sh. +# Make sure to keep these files in sync. + +green='\033[0;32m' +nc='\033[0m' + +# Setup as root, as some actions performed here require it. +adb root +adb wait-for-device + +if [[ -n "$ART_TEST_CHROOT" ]]; then + # Tear down the chroot dir. + echo -e "${green}Tear down the chroot dir in $ART_TEST_CHROOT${nc}" + + # Check that ART_TEST_CHROOT is correctly defined. + [[ "x$ART_TEST_CHROOT" = x/* ]] || { echo "$ART_TEST_CHROOT is not an absolute path"; exit 1; } + + # Remove /dev from chroot. + adb shell mount | grep -q "^tmpfs on $ART_TEST_CHROOT/dev type tmpfs " \ + && adb shell umount "$ART_TEST_CHROOT/dev" \ + && adb shell rmdir "$ART_TEST_CHROOT/dev" + + # Remove /sys/kernel/debug from chroot. + adb shell mount | grep -q "^debugfs on $ART_TEST_CHROOT/sys/kernel/debug type debugfs " \ + && adb shell umount "$ART_TEST_CHROOT/sys/kernel/debug" + # Remove /sys from chroot. + adb shell mount | grep -q "^sysfs on $ART_TEST_CHROOT/sys type sysfs " \ + && adb shell umount "$ART_TEST_CHROOT/sys" \ + && adb shell rmdir "$ART_TEST_CHROOT/sys" + + # Remove /proc from chroot. + adb shell mount | grep -q "^proc on $ART_TEST_CHROOT/proc type proc " \ + && adb shell umount "$ART_TEST_CHROOT/proc" \ + && adb shell rmdir "$ART_TEST_CHROOT/proc" + + # Remove /etc from chroot. + adb shell rm -f "$ART_TEST_CHROOT/etc" + adb shell rm -rf "$ART_TEST_CHROOT/system/etc" + + # Remove directories used for ART testing in chroot. + adb shell rm -rf "$ART_TEST_CHROOT/data/local/tmp" + adb shell rm -rf "$ART_TEST_CHROOT/data/dalvik-cache" + adb shell rm -rf "$ART_TEST_CHROOT/tmp" + + # Remove property_contexts file(s) from chroot. + property_context_files="/property_contexts \ + /system/etc/selinux/plat_property_contexts \ + /vendor/etc/selinux/nonplat_property_context \ + /plat_property_contexts \ + /nonplat_property_contexts" + for f in $property_context_files; do + adb shell test -f "$f" "&&" rm -f "$ART_TEST_CHROOT$f" + done +fi |