summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/dex/dex_to_dex_compiler.cc16
-rw-r--r--compiler/driver/compiler_driver.cc316
-rw-r--r--compiler/optimizing/nodes.cc17
-rw-r--r--compiler/optimizing/nodes.h5
-rw-r--r--compiler/optimizing/superblock_cloner.cc154
-rw-r--r--compiler/optimizing/superblock_cloner.h31
-rw-r--r--compiler/verifier_deps_test.cc63
-rw-r--r--libdexfile/dex/class_accessor-inl.h30
-rw-r--r--libdexfile/dex/class_accessor.h95
-rw-r--r--libdexfile/dex/dex_instruction_iterator.h2
-rwxr-xr-xtest/463-checker-boolean-simplifier/build20
-rw-r--r--test/463-checker-boolean-simplifier/smali/Main2.smali308
-rw-r--r--test/463-checker-boolean-simplifier/src/Main.java174
-rw-r--r--test/530-checker-peel-unroll/src/Main.java235
-rw-r--r--test/913-heaps/check2
-rw-r--r--test/980-redefine-object/redef_object.cc143
-rw-r--r--test/980-redefine-object/src/Main.java273
-rw-r--r--test/980-redefine-object/src/art/Redefinition.java91
-rw-r--r--test/Android.bp5
-rwxr-xr-xtest/run-test5
-rwxr-xr-xtest/testrunner/testrunner.py5
-rw-r--r--tools/build/var_list1
-rwxr-xr-xtools/buildbot-build.sh6
-rwxr-xr-xtools/cleanup-buildbot-device.sh4
-rwxr-xr-xtools/common/common.py8
-rw-r--r--tools/dexanalyze/dexanalyze_experiments.cc6
-rwxr-xr-xtools/golem/build-target.sh2
-rw-r--r--tools/jfuzz/README.md10
-rwxr-xr-xtools/jfuzz/run_dex_fuzz_test.py14
-rwxr-xr-xtools/jfuzz/run_jfuzz_test.py8
-rwxr-xr-xtools/setup-buildbot-device.sh5
-rwxr-xr-xtools/teardown-buildbot-device.sh70
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