summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/art.go32
-rw-r--r--compiler/driver/compiler_options.cc59
-rw-r--r--compiler/driver/compiler_options.h34
-rw-r--r--compiler/jit/jit_compiler.cc28
-rw-r--r--compiler/optimizing/code_generator_arm.cc9
-rw-r--r--compiler/optimizing/code_generator_arm64.cc7
-rw-r--r--compiler/optimizing/code_generator_arm_vixl.cc9
-rw-r--r--compiler/optimizing/graph_visualizer.cc12
-rw-r--r--compiler/optimizing/induction_var_range.cc10
-rw-r--r--compiler/optimizing/induction_var_range.h1
-rw-r--r--compiler/optimizing/induction_var_range_test.cc12
-rw-r--r--compiler/optimizing/loop_optimization.cc291
-rw-r--r--compiler/optimizing/loop_optimization.h28
-rw-r--r--compiler/optimizing/nodes.cc1
-rw-r--r--compiler/optimizing/nodes.h14
-rw-r--r--compiler/optimizing/scheduler.cc8
-rw-r--r--compiler/optimizing/ssa_liveness_analysis.cc2
-rw-r--r--dex2oat/dex2oat.cc8
-rw-r--r--oatdump/oatdump.cc2
-rw-r--r--runtime/arch/arm/context_arm.h2
-rw-r--r--runtime/arch/arm64/context_arm64.h2
-rw-r--r--runtime/arch/x86/context_x86.h2
-rw-r--r--runtime/arch/x86_64/context_x86_64.h2
-rw-r--r--runtime/art_method.cc29
-rw-r--r--runtime/art_method.h11
-rw-r--r--runtime/class_linker.cc16
-rw-r--r--runtime/debugger.cc24
-rw-r--r--runtime/dex_file_annotations.cc58
-rw-r--r--runtime/dex_file_annotations.h11
-rw-r--r--runtime/entrypoints/quick/quick_trampoline_entrypoints.cc28
-rw-r--r--runtime/gc/gc_cause.cc1
-rw-r--r--runtime/gc/gc_cause.h2
-rw-r--r--runtime/gc/heap.cc8
-rw-r--r--runtime/gc/heap.h5
-rw-r--r--runtime/gc/heap_verification_test.cc5
-rw-r--r--runtime/gc/space/region_space-inl.h60
-rw-r--r--runtime/gc/space/region_space.cc89
-rw-r--r--runtime/gc/space/region_space.h44
-rw-r--r--runtime/gc/verification.cc54
-rw-r--r--runtime/gc/verification.h11
-rw-r--r--runtime/interpreter/interpreter_switch_impl.cc60
-rw-r--r--runtime/jit/profile_saver.cc59
-rw-r--r--runtime/openjdkjvmti/Android.bp1
-rw-r--r--runtime/openjdkjvmti/OpenjdkJvmTi.cc14
-rw-r--r--runtime/openjdkjvmti/art_jvmti.h12
-rw-r--r--runtime/openjdkjvmti/events-inl.h27
-rw-r--r--runtime/openjdkjvmti/events.cc44
-rw-r--r--runtime/openjdkjvmti/ti_breakpoint.cc114
-rw-r--r--runtime/openjdkjvmti/ti_breakpoint.h94
-rw-r--r--runtime/openjdkjvmti/ti_redefine.cc8
-rw-r--r--runtime/openjdkjvmti/ti_redefine.h9
-rw-r--r--runtime/thread.cc39
-rwxr-xr-xtest/442-checker-constant-folding/build23
-rw-r--r--test/442-checker-constant-folding/src/Main.java540
-rwxr-xr-xtest/450-checker-types/build23
-rw-r--r--test/450-checker-types/smali/SmaliTests.smali120
-rw-r--r--test/450-checker-types/src/Main.java56
-rwxr-xr-xtest/458-checker-instruct-simplification/build23
-rw-r--r--test/458-checker-instruct-simplification/smali/SmaliTests.smali141
-rw-r--r--test/458-checker-instruct-simplification/src/Main.java103
-rwxr-xr-xtest/463-checker-boolean-simplifier/build23
-rw-r--r--test/463-checker-boolean-simplifier/smali/BooleanNotDx.smali65
-rw-r--r--test/463-checker-boolean-simplifier/src/Main.java64
-rwxr-xr-xtest/536-checker-intrinsic-optimization/build23
-rw-r--r--test/536-checker-intrinsic-optimization/smali/SmaliTests.smali64
-rw-r--r--test/536-checker-intrinsic-optimization/src/Main.java21
-rwxr-xr-xtest/537-checker-inline-and-unverified/build23
-rw-r--r--test/537-checker-inline-and-unverified/src/Main.java6
-rwxr-xr-xtest/565-checker-doublenegbitwise/build23
-rw-r--r--test/565-checker-doublenegbitwise/smali/SmaliTests.smali405
-rw-r--r--test/565-checker-doublenegbitwise/src/Main.java303
-rwxr-xr-xtest/586-checker-null-array-get/build23
-rw-r--r--test/586-checker-null-array-get/smali/SmaliTests.smali96
-rw-r--r--test/586-checker-null-array-get/src/Main.java27
-rwxr-xr-xtest/593-checker-boolean-2-integral-conv/build23
-rw-r--r--test/593-checker-boolean-2-integral-conv/smali/SmaliTests.smali119
-rw-r--r--test/593-checker-boolean-2-integral-conv/src/Main.java97
-rw-r--r--test/593-checker-shift-and-simplifier/expected.txt1
-rw-r--r--test/593-checker-shift-and-simplifier/smali/SmaliTests.smali58
-rw-r--r--test/593-checker-shift-and-simplifier/src/Main.java32
-rwxr-xr-xtest/633-checker-rtp-getclass/build23
-rw-r--r--test/633-checker-rtp-getclass/smali/SmaliTests.smali65
-rw-r--r--test/633-checker-rtp-getclass/src/Main.java41
-rwxr-xr-xtest/652-deopt-intrinsic/run18
-rwxr-xr-xtest/656-annotation-lookup-generic-jni/check21
-rw-r--r--test/656-annotation-lookup-generic-jni/expected.txt3
-rw-r--r--test/656-annotation-lookup-generic-jni/info.txt7
-rw-r--r--test/656-annotation-lookup-generic-jni/src-art/Main.java76
-rw-r--r--test/656-annotation-lookup-generic-jni/src-ex/DummyAnnotation.java17
-rw-r--r--test/656-annotation-lookup-generic-jni/src-ex/Test.java28
-rw-r--r--test/656-annotation-lookup-generic-jni/test.cc28
-rw-r--r--test/656-checker-simd-opt/expected.txt1
-rw-r--r--test/656-checker-simd-opt/info.txt1
-rw-r--r--test/656-checker-simd-opt/src/Main.java112
-rw-r--r--test/656-loop-deopt/src/Main.java37
-rw-r--r--test/657-branches/expected.txt1
-rw-r--r--test/657-branches/info.txt2
-rw-r--r--test/657-branches/src/Main.java47
-rwxr-xr-xtest/961-default-iface-resolution-gen/build2
-rwxr-xr-xtest/964-default-iface-init-gen/build2
-rw-r--r--test/988-method-trace/expected.txt44
-rw-r--r--test/988-method-trace/src/art/Test988.java23
-rw-r--r--test/988-method-trace/src/art/Trace.java11
-rw-r--r--test/989-method-trace-throw/src/art/Trace.java11
-rw-r--r--test/990-field-trace/src/art/Trace.java11
-rw-r--r--test/991-field-trace-2/src/art/Trace.java11
-rw-r--r--test/992-source-data/expected.txt2
-rw-r--r--test/992-source-data/src/art/Target2.java (renamed from test/992-source-data/src/art/Test2.java)2
-rw-r--r--test/992-source-data/src/art/Test992.java2
-rw-r--r--test/993-breakpoints/breakpoints.cc69
-rw-r--r--test/993-breakpoints/expected.txt613
-rw-r--r--test/993-breakpoints/info.txt7
-rwxr-xr-xtest/993-breakpoints/run18
-rw-r--r--test/993-breakpoints/src/Main.java21
-rw-r--r--test/993-breakpoints/src/art/Breakpoint.java202
-rw-r--r--test/993-breakpoints/src/art/Test993.java498
-rw-r--r--test/994-breakpoint-line/expected.txt34
-rw-r--r--test/994-breakpoint-line/info.txt5
-rwxr-xr-xtest/994-breakpoint-line/run18
-rw-r--r--test/994-breakpoint-line/src/Main.java21
-rw-r--r--test/994-breakpoint-line/src/art/Breakpoint.java202
-rw-r--r--test/994-breakpoint-line/src/art/Test994.java72
-rw-r--r--test/995-breakpoints-throw/expected.txt34
-rw-r--r--test/995-breakpoints-throw/info.txt6
-rwxr-xr-xtest/995-breakpoints-throw/run18
-rw-r--r--test/995-breakpoints-throw/src/Main.java21
-rw-r--r--test/995-breakpoints-throw/src/art/Breakpoint.java202
-rw-r--r--test/995-breakpoints-throw/src/art/Test995.java136
-rw-r--r--test/996-breakpoint-obsolete/expected.txt14
-rw-r--r--test/996-breakpoint-obsolete/info.txt4
-rw-r--r--test/996-breakpoint-obsolete/obsolete_breakpoints.cc76
-rwxr-xr-xtest/996-breakpoint-obsolete/run18
-rw-r--r--test/996-breakpoint-obsolete/src/Main.java21
-rw-r--r--test/996-breakpoint-obsolete/src/art/Breakpoint.java202
-rw-r--r--test/996-breakpoint-obsolete/src/art/Redefinition.java91
-rw-r--r--test/996-breakpoint-obsolete/src/art/Test996.java152
-rw-r--r--test/997-single-step/expected.txt12
-rw-r--r--test/997-single-step/info.txt3
-rwxr-xr-xtest/997-single-step/run18
-rw-r--r--test/997-single-step/src/Main.java21
-rw-r--r--test/997-single-step/src/art/Breakpoint.java202
-rw-r--r--test/997-single-step/src/art/Test997.java82
-rw-r--r--test/997-single-step/src/art/Trace.java56
-rw-r--r--test/Android.bp3
-rw-r--r--test/Android.run-test-jvmti-java-library.mk12
-rwxr-xr-xtest/etc/run-test-jar8
-rw-r--r--test/knownfailures.json21
-rwxr-xr-xtest/run-test10
-rwxr-xr-xtest/testrunner/testrunner.py6
-rw-r--r--test/ti-agent/common_helper.cc259
-rw-r--r--test/ti-stress/stress.cc27
-rwxr-xr-xtools/buildbot-build.sh2
-rw-r--r--tools/dexfuzz/README2
-rw-r--r--tools/dexfuzz/src/dexfuzz/DexFuzz.java2
-rw-r--r--tools/dexfuzz/src/dexfuzz/program/Program.java4
-rw-r--r--tools/dexfuzz/src/dexfuzz/program/mutators/IfBranchChanger.java158
-rw-r--r--tools/dexfuzz/src/dexfuzz/program/mutators/OppositeBranchChanger.java72
-rw-r--r--tools/dexfuzz/src/dexfuzz/program/mutators/RandomBranchChanger.java70
158 files changed, 7241 insertions, 1270 deletions
diff --git a/build/art.go b/build/art.go
index f52c63525a..db626fd19c 100644
--- a/build/art.go
+++ b/build/art.go
@@ -83,20 +83,20 @@ func globalFlags(ctx android.BaseContext) ([]string, []string) {
// the debug version. So make the gap consistent (and adjust for the worst).
if len(ctx.AConfig().SanitizeDevice()) > 0 || len(ctx.AConfig().SanitizeHost()) > 0 {
cflags = append(cflags,
- "-DART_STACK_OVERFLOW_GAP_arm=8192",
- "-DART_STACK_OVERFLOW_GAP_arm64=8192",
- "-DART_STACK_OVERFLOW_GAP_mips=16384",
- "-DART_STACK_OVERFLOW_GAP_mips64=16384",
- "-DART_STACK_OVERFLOW_GAP_x86=16384",
- "-DART_STACK_OVERFLOW_GAP_x86_64=20480")
+ "-DART_STACK_OVERFLOW_GAP_arm=8192",
+ "-DART_STACK_OVERFLOW_GAP_arm64=8192",
+ "-DART_STACK_OVERFLOW_GAP_mips=16384",
+ "-DART_STACK_OVERFLOW_GAP_mips64=16384",
+ "-DART_STACK_OVERFLOW_GAP_x86=16384",
+ "-DART_STACK_OVERFLOW_GAP_x86_64=20480")
} else {
cflags = append(cflags,
- "-DART_STACK_OVERFLOW_GAP_arm=8192",
- "-DART_STACK_OVERFLOW_GAP_arm64=8192",
- "-DART_STACK_OVERFLOW_GAP_mips=16384",
- "-DART_STACK_OVERFLOW_GAP_mips64=16384",
- "-DART_STACK_OVERFLOW_GAP_x86=8192",
- "-DART_STACK_OVERFLOW_GAP_x86_64=8192")
+ "-DART_STACK_OVERFLOW_GAP_arm=8192",
+ "-DART_STACK_OVERFLOW_GAP_arm64=8192",
+ "-DART_STACK_OVERFLOW_GAP_mips=16384",
+ "-DART_STACK_OVERFLOW_GAP_mips64=16384",
+ "-DART_STACK_OVERFLOW_GAP_x86=8192",
+ "-DART_STACK_OVERFLOW_GAP_x86_64=8192")
}
return cflags, asflags
@@ -168,10 +168,10 @@ func globalDefaults(ctx android.LoadHookContext) {
Cflags []string
}
}
- Cflags []string
- Asflags []string
+ Cflags []string
+ Asflags []string
Sanitize struct {
- Recover []string
+ Recover []string
}
}
@@ -182,7 +182,7 @@ func globalDefaults(ctx android.LoadHookContext) {
if envTrue(ctx, "ART_DEX_FILE_ACCESS_TRACKING") {
p.Cflags = append(p.Cflags, "-DART_DEX_FILE_ACCESS_TRACKING")
- p.Sanitize.Recover = []string {
+ p.Sanitize.Recover = []string{
"address",
}
}
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index a4e2083fe4..76f0ae9202 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -40,7 +40,7 @@ CompilerOptions::CompilerOptions()
implicit_so_checks_(true),
implicit_suspend_checks_(false),
compile_pic_(false),
- verbose_methods_(nullptr),
+ verbose_methods_(),
abort_on_hard_verifier_failure_(false),
init_failure_output_(nullptr),
dump_cfg_file_name_(""),
@@ -55,58 +55,6 @@ CompilerOptions::~CompilerOptions() {
// because we don't want to include the PassManagerOptions definition from the header file.
}
-CompilerOptions::CompilerOptions(CompilerFilter::Filter compiler_filter,
- size_t huge_method_threshold,
- size_t large_method_threshold,
- size_t small_method_threshold,
- size_t tiny_method_threshold,
- size_t num_dex_methods_threshold,
- size_t inline_max_code_units,
- const std::vector<const DexFile*>* no_inline_from,
- double top_k_profile_threshold,
- bool debuggable,
- bool generate_debug_info,
- bool implicit_null_checks,
- bool implicit_so_checks,
- bool implicit_suspend_checks,
- bool compile_pic,
- const std::vector<std::string>* verbose_methods,
- std::ostream* init_failure_output,
- bool abort_on_hard_verifier_failure,
- const std::string& dump_cfg_file_name,
- bool dump_cfg_append,
- bool force_determinism,
- RegisterAllocator::Strategy regalloc_strategy,
- const std::vector<std::string>* passes_to_run)
- : compiler_filter_(compiler_filter),
- huge_method_threshold_(huge_method_threshold),
- large_method_threshold_(large_method_threshold),
- small_method_threshold_(small_method_threshold),
- tiny_method_threshold_(tiny_method_threshold),
- num_dex_methods_threshold_(num_dex_methods_threshold),
- inline_max_code_units_(inline_max_code_units),
- no_inline_from_(no_inline_from),
- boot_image_(false),
- app_image_(false),
- top_k_profile_threshold_(top_k_profile_threshold),
- debuggable_(debuggable),
- generate_debug_info_(generate_debug_info),
- generate_mini_debug_info_(kDefaultGenerateMiniDebugInfo),
- generate_build_id_(false),
- implicit_null_checks_(implicit_null_checks),
- implicit_so_checks_(implicit_so_checks),
- implicit_suspend_checks_(implicit_suspend_checks),
- compile_pic_(compile_pic),
- verbose_methods_(verbose_methods),
- abort_on_hard_verifier_failure_(abort_on_hard_verifier_failure),
- init_failure_output_(init_failure_output),
- dump_cfg_file_name_(dump_cfg_file_name),
- dump_cfg_append_(dump_cfg_append),
- force_determinism_(force_determinism),
- register_allocation_strategy_(regalloc_strategy),
- passes_to_run_(passes_to_run) {
-}
-
void CompilerOptions::ParseHugeMethodMax(const StringPiece& option, UsageFn Usage) {
ParseUintOption(option, "--huge-method-max", &huge_method_threshold_, Usage);
}
@@ -204,6 +152,11 @@ bool CompilerOptions::ParseCompilerOption(const StringPiece& option, UsageFn Usa
dump_cfg_append_ = true;
} else if (option.starts_with("--register-allocation-strategy=")) {
ParseRegisterAllocationStrategy(option, Usage);
+ } else if (option.starts_with("--verbose-methods=")) {
+ // TODO: rather than switch off compiler logging, make all VLOG(compiler) messages
+ // conditional on having verbose methods.
+ gLogVerbosity.compiler = false;
+ Split(option.substr(strlen("--verbose-methods=")).ToString(), ',', &verbose_methods_);
} else {
// Option not recognized.
return false;
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 89c2537476..b99263db0e 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -52,30 +52,6 @@ class CompilerOptions FINAL {
CompilerOptions();
~CompilerOptions();
- CompilerOptions(CompilerFilter::Filter compiler_filter,
- size_t huge_method_threshold,
- size_t large_method_threshold,
- size_t small_method_threshold,
- size_t tiny_method_threshold,
- size_t num_dex_methods_threshold,
- size_t inline_max_code_units,
- const std::vector<const DexFile*>* no_inline_from,
- double top_k_profile_threshold,
- bool debuggable,
- bool generate_debug_info,
- bool implicit_null_checks,
- bool implicit_so_checks,
- bool implicit_suspend_checks,
- bool compile_pic,
- const std::vector<std::string>* verbose_methods,
- std::ostream* init_failure_output,
- bool abort_on_hard_verifier_failure,
- const std::string& dump_cfg_file_name,
- bool dump_cfg_append,
- bool force_determinism,
- RegisterAllocator::Strategy regalloc_strategy,
- const std::vector<std::string>* passes_to_run);
-
CompilerFilter::Filter GetCompilerFilter() const {
return compiler_filter_;
}
@@ -163,6 +139,10 @@ class CompilerOptions FINAL {
return debuggable_;
}
+ void SetDebuggable(bool value) {
+ debuggable_ = value;
+ }
+
bool GetNativeDebuggable() const {
return GetDebuggable() && GetGenerateDebugInfo();
}
@@ -211,11 +191,11 @@ class CompilerOptions FINAL {
}
bool HasVerboseMethods() const {
- return verbose_methods_ != nullptr && !verbose_methods_->empty();
+ return !verbose_methods_.empty();
}
bool IsVerboseMethod(const std::string& pretty_method) const {
- for (const std::string& cur_method : *verbose_methods_) {
+ for (const std::string& cur_method : verbose_methods_) {
if (pretty_method.find(cur_method) != std::string::npos) {
return true;
}
@@ -299,7 +279,7 @@ class CompilerOptions FINAL {
bool compile_pic_;
// Vector of methods to have verbose output enabled for.
- const std::vector<std::string>* verbose_methods_;
+ std::vector<std::string> verbose_methods_;
// Abort compilation with an error if we find a class that fails verification with a hard
// failure.
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 66135414f7..715d97379e 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -90,36 +90,16 @@ NO_RETURN static void Usage(const char* fmt, ...) {
}
JitCompiler::JitCompiler() {
- compiler_options_.reset(new CompilerOptions(
- CompilerFilter::kDefaultCompilerFilter,
- CompilerOptions::kDefaultHugeMethodThreshold,
- CompilerOptions::kDefaultLargeMethodThreshold,
- CompilerOptions::kDefaultSmallMethodThreshold,
- CompilerOptions::kDefaultTinyMethodThreshold,
- CompilerOptions::kDefaultNumDexMethodsThreshold,
- CompilerOptions::kDefaultInlineMaxCodeUnits,
- /* no_inline_from */ nullptr,
- CompilerOptions::kDefaultTopKProfileThreshold,
- Runtime::Current()->IsJavaDebuggable(),
- CompilerOptions::kDefaultGenerateDebugInfo,
- /* implicit_null_checks */ true,
- /* implicit_so_checks */ true,
- /* implicit_suspend_checks */ false,
- /* pic */ false,
- /* verbose_methods */ nullptr,
- /* init_failure_output */ nullptr,
- /* abort_on_hard_verifier_failure */ false,
- /* dump_cfg_file_name */ "",
- /* dump_cfg_append */ false,
- /* force_determinism */ false,
- RegisterAllocator::kRegisterAllocatorDefault,
- /* passes_to_run */ nullptr));
+ compiler_options_.reset(new CompilerOptions());
for (const std::string& argument : Runtime::Current()->GetCompilerOptions()) {
compiler_options_->ParseCompilerOption(argument, Usage);
}
// JIT is never PIC, no matter what the runtime compiler options specify.
compiler_options_->SetNonPic();
+ // Set debuggability based on the runtime value.
+ compiler_options_->SetDebuggable(Runtime::Current()->IsJavaDebuggable());
+
const InstructionSet instruction_set = kRuntimeISA;
for (const StringPiece option : Runtime::Current()->GetCompilerOptions()) {
VLOG(compiler) << "JIT compiler option " << option;
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 0b3ac204ff..6b9f232e8f 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -2875,21 +2875,28 @@ void InstructionCodeGeneratorARM::GenerateCompareTestAndBranch(HCondition* condi
if (CanGenerateTest(condition, codegen_->GetAssembler())) {
Label* non_fallthrough_target;
bool invert;
+ bool emit_both_branches;
if (true_target_in == nullptr) {
+ // The true target is fallthrough.
DCHECK(false_target_in != nullptr);
non_fallthrough_target = false_target_in;
invert = true;
+ emit_both_branches = false;
} else {
+ // Either the false target is fallthrough, or there is no fallthrough
+ // and both branches must be emitted.
non_fallthrough_target = true_target_in;
invert = false;
+ emit_both_branches = (false_target_in != nullptr);
}
const auto cond = GenerateTest(condition, invert, codegen_);
__ b(non_fallthrough_target, cond.first);
- if (false_target_in != nullptr && false_target_in != non_fallthrough_target) {
+ if (emit_both_branches) {
+ // No target falls through, we need to branch.
__ b(false_target_in);
}
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 34397e66bc..2561ed0762 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -3608,9 +3608,6 @@ void InstructionCodeGeneratorARM64::GenerateTestAndBranch(HInstruction* instruct
size_t condition_input_index,
vixl::aarch64::Label* true_target,
vixl::aarch64::Label* false_target) {
- // FP branching requires both targets to be explicit. If either of the targets
- // is nullptr (fallthrough) use and bind `fallthrough_target` instead.
- vixl::aarch64::Label fallthrough_target;
HInstruction* cond = instruction->InputAt(condition_input_index);
if (true_target == nullptr && false_target == nullptr) {
@@ -3711,10 +3708,6 @@ void InstructionCodeGeneratorARM64::GenerateTestAndBranch(HInstruction* instruct
if (true_target != nullptr && false_target != nullptr) {
__ B(false_target);
}
-
- if (fallthrough_target.IsLinked()) {
- __ Bind(&fallthrough_target);
- }
}
void LocationsBuilderARM64::VisitIf(HIf* if_instr) {
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index a8b00c358b..9a2402be04 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -2964,21 +2964,28 @@ void InstructionCodeGeneratorARMVIXL::GenerateCompareTestAndBranch(HCondition* c
if (CanGenerateTest(condition, codegen_->GetAssembler())) {
vixl32::Label* non_fallthrough_target;
bool invert;
+ bool emit_both_branches;
if (true_target_in == nullptr) {
+ // The true target is fallthrough.
DCHECK(false_target_in != nullptr);
non_fallthrough_target = false_target_in;
invert = true;
+ emit_both_branches = false;
} else {
non_fallthrough_target = true_target_in;
invert = false;
+ // Either the false target is fallthrough, or there is no fallthrough
+ // and both branches must be emitted.
+ emit_both_branches = (false_target_in != nullptr);
}
const auto cond = GenerateTest(condition, invert, codegen_);
__ B(cond.first, non_fallthrough_target, is_far_target);
- if (false_target_in != nullptr && false_target_in != non_fallthrough_target) {
+ if (emit_both_branches) {
+ // No target falls through, we need to branch.
__ B(false_target_in);
}
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 7dcf2440b2..a20ec3c0db 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -451,8 +451,16 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
void VisitInvoke(HInvoke* invoke) OVERRIDE {
StartAttributeStream("dex_file_index") << invoke->GetDexMethodIndex();
- StartAttributeStream("method_name") << GetGraph()->GetDexFile().PrettyMethod(
- invoke->GetDexMethodIndex(), /* with_signature */ false);
+ ArtMethod* method = invoke->GetResolvedMethod();
+ // We don't print signatures, which conflict with c1visualizer format.
+ static constexpr bool kWithSignature = false;
+ // Note that we can only use the graph's dex file for the unresolved case. The
+ // other invokes might be coming from inlined methods.
+ ScopedObjectAccess soa(Thread::Current());
+ std::string method_name = (method == nullptr)
+ ? GetGraph()->GetDexFile().PrettyMethod(invoke->GetDexMethodIndex(), kWithSignature)
+ : method->PrettyMethod(kWithSignature);
+ StartAttributeStream("method_name") << method_name;
}
void VisitInvokeUnresolved(HInvokeUnresolved* invoke) OVERRIDE {
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index c0ec58f824..f35aace3a9 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -373,21 +373,23 @@ bool InductionVarRange::IsFinite(HLoopInformation* loop, /*out*/ int64_t* tc) co
bool InductionVarRange::IsUnitStride(HInstruction* context,
HInstruction* instruction,
+ HGraph* graph,
/*out*/ HInstruction** offset) const {
HLoopInformation* loop = nullptr;
HInductionVarAnalysis::InductionInfo* info = nullptr;
HInductionVarAnalysis::InductionInfo* trip = nullptr;
if (HasInductionInfo(context, instruction, &loop, &info, &trip)) {
if (info->induction_class == HInductionVarAnalysis::kLinear &&
- info->op_b->operation == HInductionVarAnalysis::kFetch &&
!HInductionVarAnalysis::IsNarrowingLinear(info)) {
int64_t stride_value = 0;
if (IsConstant(info->op_a, kExact, &stride_value) && stride_value == 1) {
int64_t off_value = 0;
- if (IsConstant(info->op_b, kExact, &off_value) && off_value == 0) {
- *offset = nullptr;
- } else {
+ if (IsConstant(info->op_b, kExact, &off_value)) {
+ *offset = graph->GetConstant(info->op_b->type, off_value);
+ } else if (info->op_b->operation == HInductionVarAnalysis::kFetch) {
*offset = info->op_b->fetch;
+ } else {
+ return false;
}
return true;
}
diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h
index a8ee829d08..ab1772bf15 100644
--- a/compiler/optimizing/induction_var_range.h
+++ b/compiler/optimizing/induction_var_range.h
@@ -163,6 +163,7 @@ class InductionVarRange {
*/
bool IsUnitStride(HInstruction* context,
HInstruction* instruction,
+ HGraph* graph,
/*out*/ HInstruction** offset) const;
/**
diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc
index d01d3146fc..67d2093829 100644
--- a/compiler/optimizing/induction_var_range_test.cc
+++ b/compiler/optimizing/induction_var_range_test.cc
@@ -770,8 +770,8 @@ TEST_F(InductionVarRangeTest, ConstantTripCountUp) {
EXPECT_TRUE(range_.IsFinite(loop_header_->GetLoopInformation(), &tc));
EXPECT_EQ(1000, tc);
HInstruction* offset = nullptr;
- EXPECT_TRUE(range_.IsUnitStride(phi, phi, &offset));
- EXPECT_TRUE(offset == nullptr);
+ EXPECT_TRUE(range_.IsUnitStride(phi, phi, graph_, &offset));
+ ExpectInt(0, offset);
HInstruction* tce = range_.GenerateTripCount(
loop_header_->GetLoopInformation(), graph_, loop_preheader_);
ASSERT_TRUE(tce != nullptr);
@@ -826,7 +826,7 @@ TEST_F(InductionVarRangeTest, ConstantTripCountDown) {
EXPECT_TRUE(range_.IsFinite(loop_header_->GetLoopInformation(), &tc));
EXPECT_EQ(1000, tc);
HInstruction* offset = nullptr;
- EXPECT_FALSE(range_.IsUnitStride(phi, phi, &offset));
+ EXPECT_FALSE(range_.IsUnitStride(phi, phi, graph_, &offset));
HInstruction* tce = range_.GenerateTripCount(
loop_header_->GetLoopInformation(), graph_, loop_preheader_);
ASSERT_TRUE(tce != nullptr);
@@ -908,8 +908,8 @@ TEST_F(InductionVarRangeTest, SymbolicTripCountUp) {
EXPECT_TRUE(range_.IsFinite(loop_header_->GetLoopInformation(), &tc));
EXPECT_EQ(0, tc); // unknown
HInstruction* offset = nullptr;
- EXPECT_TRUE(range_.IsUnitStride(phi, phi, &offset));
- EXPECT_TRUE(offset == nullptr);
+ EXPECT_TRUE(range_.IsUnitStride(phi, phi, graph_, &offset));
+ ExpectInt(0, offset);
HInstruction* tce = range_.GenerateTripCount(
loop_header_->GetLoopInformation(), graph_, loop_preheader_);
ASSERT_TRUE(tce != nullptr);
@@ -994,7 +994,7 @@ TEST_F(InductionVarRangeTest, SymbolicTripCountDown) {
EXPECT_TRUE(range_.IsFinite(loop_header_->GetLoopInformation(), &tc));
EXPECT_EQ(0, tc); // unknown
HInstruction* offset = nullptr;
- EXPECT_FALSE(range_.IsUnitStride(phi, phi, &offset));
+ EXPECT_FALSE(range_.IsUnitStride(phi, phi, graph_, &offset));
HInstruction* tce = range_.GenerateTripCount(
loop_header_->GetLoopInformation(), graph_, loop_preheader_);
ASSERT_TRUE(tce != nullptr);
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index d2493137fe..b61d7b80d1 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -31,6 +31,9 @@ namespace art {
// Enables vectorization (SIMDization) in the loop optimizer.
static constexpr bool kEnableVectorization = true;
+// All current SIMD targets want 16-byte alignment.
+static constexpr size_t kAlignedBase = 16;
+
// Remove the instruction from the graph. A bit more elaborate than the usual
// instruction removal, since there may be a cycle in the use structure.
static void RemoveFromCycle(HInstruction* instruction) {
@@ -283,6 +286,9 @@ HLoopOptimization::HLoopOptimization(HGraph* graph,
simplified_(false),
vector_length_(0),
vector_refs_(nullptr),
+ vector_peeling_candidate_(nullptr),
+ vector_runtime_test_a_(nullptr),
+ vector_runtime_test_b_(nullptr),
vector_map_(nullptr) {
}
@@ -422,23 +428,6 @@ void HLoopOptimization::TraverseLoopsInnerToOuter(LoopNode* node) {
// Optimization.
//
-bool HLoopOptimization::CanRemoveCycle() {
- for (HInstruction* i : *iset_) {
- // We can never remove instructions that have environment
- // uses when we compile 'debuggable'.
- if (i->HasEnvironmentUses() && graph_->IsDebuggable()) {
- return false;
- }
- // A deoptimization should never have an environment input removed.
- for (const HUseListNode<HEnvironment*>& use : i->GetEnvUses()) {
- if (use.GetUser()->GetHolder()->IsDeoptimize()) {
- return false;
- }
- }
- }
- return true;
-}
-
void HLoopOptimization::SimplifyInduction(LoopNode* node) {
HBasicBlock* header = node->loop_info->GetHeader();
HBasicBlock* preheader = node->loop_info->GetPreHeader();
@@ -565,7 +554,7 @@ void HLoopOptimization::OptimizeInnerLoop(LoopNode* node) {
if (kEnableVectorization) {
iset_->clear(); // prepare phi induction
if (TrySetSimpleLoopHeader(header) &&
- CanVectorize(node, body, trip_count) &&
+ ShouldVectorize(node, body, trip_count) &&
TryAssignLastValue(node->loop_info, phi, preheader, /*collect_loop_uses*/ true)) {
Vectorize(node, body, exit, trip_count);
graph_->SetHasSIMD(true); // flag SIMD usage
@@ -580,10 +569,11 @@ void HLoopOptimization::OptimizeInnerLoop(LoopNode* node) {
// Intel Press, June, 2004 (http://www.aartbik.com/).
//
-bool HLoopOptimization::CanVectorize(LoopNode* node, HBasicBlock* block, int64_t trip_count) {
+bool HLoopOptimization::ShouldVectorize(LoopNode* node, HBasicBlock* block, int64_t trip_count) {
// Reset vector bookkeeping.
vector_length_ = 0;
vector_refs_->clear();
+ vector_peeling_candidate_ = nullptr;
vector_runtime_test_a_ =
vector_runtime_test_b_= nullptr;
@@ -600,12 +590,9 @@ bool HLoopOptimization::CanVectorize(LoopNode* node, HBasicBlock* block, int64_t
}
}
- // Heuristics. Does vectorization seem profitable?
- // TODO: refine
- if (vector_length_ == 0) {
- return false; // nothing found
- } else if (0 < trip_count && trip_count < vector_length_) {
- return false; // insufficient iterations
+ // Does vectorization seem profitable?
+ if (!IsVectorizationProfitable(trip_count)) {
+ return false;
}
// Data dependence analysis. Find each pair of references with same type, where
@@ -633,18 +620,24 @@ bool HLoopOptimization::CanVectorize(LoopNode* node, HBasicBlock* block, int64_t
// Conservatively assume a potential loop-carried data dependence otherwise, avoided by
// generating an explicit a != b disambiguation runtime test on the two references.
if (x != y) {
- // For now, we reject after one test to avoid excessive overhead.
- if (vector_runtime_test_a_ != nullptr) {
- return false;
+ // To avoid excessive overhead, we only accept one a != b test.
+ if (vector_runtime_test_a_ == nullptr) {
+ // First test found.
+ vector_runtime_test_a_ = a;
+ vector_runtime_test_b_ = b;
+ } else if ((vector_runtime_test_a_ != a || vector_runtime_test_b_ != b) &&
+ (vector_runtime_test_a_ != b || vector_runtime_test_b_ != a)) {
+ return false; // second test would be needed
}
- vector_runtime_test_a_ = a;
- vector_runtime_test_b_ = b;
}
}
}
}
}
+ // Consider dynamic loop peeling for alignment.
+ SetPeelingCandidate(trip_count);
+
// Success!
return true;
}
@@ -657,28 +650,52 @@ void HLoopOptimization::Vectorize(LoopNode* node,
HBasicBlock* header = node->loop_info->GetHeader();
HBasicBlock* preheader = node->loop_info->GetPreHeader();
- // A cleanup is needed for any unknown trip count or for a known trip count
- // with remainder iterations after vectorization.
- bool needs_cleanup = trip_count == 0 || (trip_count % vector_length_) != 0;
+ // Pick a loop unrolling factor for the vector loop.
+ uint32_t unroll = GetUnrollingFactor(block, trip_count);
+ uint32_t chunk = vector_length_ * unroll;
+
+ // A cleanup loop is needed, at least, for any unknown trip count or
+ // for a known trip count with remainder iterations after vectorization.
+ bool needs_cleanup = trip_count == 0 || (trip_count % chunk) != 0;
// Adjust vector bookkeeping.
iset_->clear(); // prepare phi induction
bool is_simple_loop_header = TrySetSimpleLoopHeader(header); // fills iset_
DCHECK(is_simple_loop_header);
+ vector_header_ = header;
+ vector_body_ = block;
+
+ // Generate dynamic loop peeling trip count, if needed:
+ // ptc = <peeling-needed-for-candidate>
+ HInstruction* ptc = nullptr;
+ if (vector_peeling_candidate_ != nullptr) {
+ DCHECK_LT(vector_length_, trip_count) << "dynamic peeling currently requires known trip count";
+ //
+ // TODO: Implement this. Compute address of first access memory location and
+ // compute peeling factor to obtain kAlignedBase alignment.
+ //
+ needs_cleanup = true;
+ }
- // Generate preheader:
+ // Generate loop control:
// stc = <trip-count>;
- // vtc = stc - stc % VL;
+ // vtc = stc - (stc - ptc) % chunk;
+ // i = 0;
HInstruction* stc = induction_range_.GenerateTripCount(node->loop_info, graph_, preheader);
HInstruction* vtc = stc;
if (needs_cleanup) {
- DCHECK(IsPowerOfTwo(vector_length_));
+ DCHECK(IsPowerOfTwo(chunk));
+ HInstruction* diff = stc;
+ if (ptc != nullptr) {
+ diff = Insert(preheader, new (global_allocator_) HSub(induc_type, stc, ptc));
+ }
HInstruction* rem = Insert(
preheader, new (global_allocator_) HAnd(induc_type,
- stc,
- graph_->GetIntConstant(vector_length_ - 1)));
+ diff,
+ graph_->GetIntConstant(chunk - 1)));
vtc = Insert(preheader, new (global_allocator_) HSub(induc_type, stc, rem));
}
+ vector_index_ = graph_->GetIntConstant(0);
// Generate runtime disambiguation test:
// vtc = a != b ? vtc : 0;
@@ -691,16 +708,31 @@ void HLoopOptimization::Vectorize(LoopNode* node,
needs_cleanup = true;
}
- // Generate vector loop:
- // for (i = 0; i < vtc; i += VL)
+ // Generate dynamic peeling loop for alignment, if needed:
+ // for ( ; i < ptc; i += 1)
+ // <loop-body>
+ if (ptc != nullptr) {
+ vector_mode_ = kSequential;
+ GenerateNewLoop(node,
+ block,
+ graph_->TransformLoopForVectorization(vector_header_, vector_body_, exit),
+ vector_index_,
+ ptc,
+ graph_->GetIntConstant(1),
+ /*unroll*/ 1);
+ }
+
+ // Generate vector loop, possibly further unrolled:
+ // for ( ; i < vtc; i += chunk)
// <vectorized-loop-body>
vector_mode_ = kVector;
GenerateNewLoop(node,
block,
- graph_->TransformLoopForVectorization(header, block, exit),
- graph_->GetIntConstant(0),
+ graph_->TransformLoopForVectorization(vector_header_, vector_body_, exit),
+ vector_index_,
vtc,
- graph_->GetIntConstant(vector_length_));
+ graph_->GetIntConstant(vector_length_), // increment per unroll
+ unroll);
HLoopInformation* vloop = vector_header_->GetLoopInformation();
// Generate cleanup loop, if needed:
@@ -711,9 +743,10 @@ void HLoopOptimization::Vectorize(LoopNode* node,
GenerateNewLoop(node,
block,
graph_->TransformLoopForVectorization(vector_header_, vector_body_, exit),
- vector_phi_,
+ vector_index_,
stc,
- graph_->GetIntConstant(1));
+ graph_->GetIntConstant(1),
+ /*unroll*/ 1);
}
// Remove the original loop by disconnecting the body block
@@ -722,8 +755,9 @@ void HLoopOptimization::Vectorize(LoopNode* node,
while (!header->GetFirstInstruction()->IsGoto()) {
header->RemoveInstruction(header->GetFirstInstruction());
}
- // Update loop hierarchy: the old header now resides in the
- // same outer loop as the old preheader.
+ // Update loop hierarchy: the old header now resides in the same outer loop
+ // as the old preheader. Note that we don't bother putting sequential
+ // loops back in the hierarchy at this point.
header->SetLoopInformation(preheader->GetLoopInformation()); // outward
node->loop_info = vloop;
}
@@ -733,44 +767,64 @@ void HLoopOptimization::GenerateNewLoop(LoopNode* node,
HBasicBlock* new_preheader,
HInstruction* lo,
HInstruction* hi,
- HInstruction* step) {
+ HInstruction* step,
+ uint32_t unroll) {
+ DCHECK(unroll == 1 || vector_mode_ == kVector);
Primitive::Type induc_type = Primitive::kPrimInt;
// Prepare new loop.
- vector_map_->clear();
vector_preheader_ = new_preheader,
vector_header_ = vector_preheader_->GetSingleSuccessor();
vector_body_ = vector_header_->GetSuccessors()[1];
- vector_phi_ = new (global_allocator_) HPhi(global_allocator_,
- kNoRegNumber,
- 0,
- HPhi::ToPhiType(induc_type));
+ HPhi* phi = new (global_allocator_) HPhi(global_allocator_,
+ kNoRegNumber,
+ 0,
+ HPhi::ToPhiType(induc_type));
// Generate header and prepare body.
// for (i = lo; i < hi; i += step)
// <loop-body>
- HInstruction* cond = new (global_allocator_) HAboveOrEqual(vector_phi_, hi);
- vector_header_->AddPhi(vector_phi_);
+ HInstruction* cond = new (global_allocator_) HAboveOrEqual(phi, hi);
+ vector_header_->AddPhi(phi);
vector_header_->AddInstruction(cond);
vector_header_->AddInstruction(new (global_allocator_) HIf(cond));
- for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
- bool vectorized_def = VectorizeDef(node, it.Current(), /*generate_code*/ true);
- DCHECK(vectorized_def);
- }
- // Generate body from the instruction map, but in original program order.
- HEnvironment* env = vector_header_->GetFirstInstruction()->GetEnvironment();
- for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
- auto i = vector_map_->find(it.Current());
- if (i != vector_map_->end() && !i->second->IsInBlock()) {
- Insert(vector_body_, i->second);
- // Deal with instructions that need an environment, such as the scalar intrinsics.
- if (i->second->NeedsEnvironment()) {
- i->second->CopyEnvironmentFromWithLoopPhiAdjustment(env, vector_header_);
+ vector_index_ = phi;
+ for (uint32_t u = 0; u < unroll; u++) {
+ // Clear map, leaving loop invariants setup during unrolling.
+ if (u == 0) {
+ vector_map_->clear();
+ } else {
+ for (auto i = vector_map_->begin(); i != vector_map_->end(); ) {
+ if (i->second->IsVecReplicateScalar()) {
+ DCHECK(node->loop_info->IsDefinedOutOfTheLoop(i->first));
+ ++i;
+ } else {
+ i = vector_map_->erase(i);
+ }
+ }
+ }
+ // Generate instruction map.
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ bool vectorized_def = VectorizeDef(node, it.Current(), /*generate_code*/ true);
+ DCHECK(vectorized_def);
+ }
+ // Generate body from the instruction map, but in original program order.
+ HEnvironment* env = vector_header_->GetFirstInstruction()->GetEnvironment();
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ auto i = vector_map_->find(it.Current());
+ if (i != vector_map_->end() && !i->second->IsInBlock()) {
+ Insert(vector_body_, i->second);
+ // Deal with instructions that need an environment, such as the scalar intrinsics.
+ if (i->second->NeedsEnvironment()) {
+ i->second->CopyEnvironmentFromWithLoopPhiAdjustment(env, vector_header_);
+ }
}
}
+ vector_index_ = new (global_allocator_) HAdd(induc_type, vector_index_, step);
+ Insert(vector_body_, vector_index_);
}
- // Finalize increment and phi.
- HInstruction* inc = new (global_allocator_) HAdd(induc_type, vector_phi_, step);
- vector_phi_->AddInput(lo);
- vector_phi_->AddInput(Insert(vector_body_, inc));
+ // Finalize phi for the loop index.
+ phi->AddInput(lo);
+ phi->AddInput(vector_index_);
+ vector_index_ = phi;
}
// TODO: accept reductions at left-hand-side, mixed-type store idioms, etc.
@@ -791,11 +845,11 @@ bool HLoopOptimization::VectorizeDef(LoopNode* node,
HInstruction* offset = nullptr;
if (TrySetVectorType(type, &restrictions) &&
node->loop_info->IsDefinedOutOfTheLoop(base) &&
- induction_range_.IsUnitStride(instruction, index, &offset) &&
+ induction_range_.IsUnitStride(instruction, index, graph_, &offset) &&
VectorizeUse(node, value, generate_code, type, restrictions)) {
if (generate_code) {
GenerateVecSub(index, offset);
- GenerateVecMem(instruction, vector_map_->Get(index), vector_map_->Get(value), type);
+ GenerateVecMem(instruction, vector_map_->Get(index), vector_map_->Get(value), offset, type);
} else {
vector_refs_->insert(ArrayReference(base, offset, type, /*lhs*/ true));
}
@@ -849,10 +903,10 @@ bool HLoopOptimization::VectorizeUse(LoopNode* node,
HInstruction* offset = nullptr;
if (type == instruction->GetType() &&
node->loop_info->IsDefinedOutOfTheLoop(base) &&
- induction_range_.IsUnitStride(instruction, index, &offset)) {
+ induction_range_.IsUnitStride(instruction, index, graph_, &offset)) {
if (generate_code) {
GenerateVecSub(index, offset);
- GenerateVecMem(instruction, vector_map_->Get(index), nullptr, type);
+ GenerateVecMem(instruction, vector_map_->Get(index), nullptr, offset, type);
} else {
vector_refs_->insert(ArrayReference(base, offset, type, /*lhs*/ false));
}
@@ -1164,8 +1218,9 @@ void HLoopOptimization::GenerateVecInv(HInstruction* org, Primitive::Type type)
void HLoopOptimization::GenerateVecSub(HInstruction* org, HInstruction* offset) {
if (vector_map_->find(org) == vector_map_->end()) {
- HInstruction* subscript = vector_phi_;
- if (offset != nullptr) {
+ HInstruction* subscript = vector_index_;
+ int64_t value = 0;
+ if (!IsInt64AndGet(offset, &value) || value != 0) {
subscript = new (global_allocator_) HAdd(Primitive::kPrimInt, subscript, offset);
if (org->IsPhi()) {
Insert(vector_body_, subscript); // lacks layout placeholder
@@ -1178,17 +1233,27 @@ void HLoopOptimization::GenerateVecSub(HInstruction* org, HInstruction* offset)
void HLoopOptimization::GenerateVecMem(HInstruction* org,
HInstruction* opa,
HInstruction* opb,
+ HInstruction* offset,
Primitive::Type type) {
HInstruction* vector = nullptr;
if (vector_mode_ == kVector) {
// Vector store or load.
+ HInstruction* base = org->InputAt(0);
if (opb != nullptr) {
vector = new (global_allocator_) HVecStore(
- global_allocator_, org->InputAt(0), opa, opb, type, vector_length_);
+ global_allocator_, base, opa, opb, type, vector_length_);
} else {
bool is_string_char_at = org->AsArrayGet()->IsStringCharAt();
vector = new (global_allocator_) HVecLoad(
- global_allocator_, org->InputAt(0), opa, type, vector_length_, is_string_char_at);
+ global_allocator_, base, opa, type, vector_length_, is_string_char_at);
+ }
+ // Known dynamically enforced alignment?
+ // TODO: detect offset + constant differences.
+ // TODO: long run, static alignment analysis?
+ if (vector_peeling_candidate_ != nullptr &&
+ vector_peeling_candidate_->base == base &&
+ vector_peeling_candidate_->offset == offset) {
+ vector->AsVecMemoryOperation()->SetAlignment(Alignment(kAlignedBase, 0));
}
} else {
// Scalar store or load.
@@ -1444,10 +1509,57 @@ bool HLoopOptimization::VectorizeHalvingAddIdiom(LoopNode* node,
}
//
+// Vectorization heuristics.
+//
+
+bool HLoopOptimization::IsVectorizationProfitable(int64_t trip_count) {
+ // Current heuristic: non-empty body with sufficient number
+ // of iterations (if known).
+ // TODO: refine by looking at e.g. operation count, alignment, etc.
+ if (vector_length_ == 0) {
+ return false; // nothing found
+ } else if (0 < trip_count && trip_count < vector_length_) {
+ return false; // insufficient iterations
+ }
+ return true;
+}
+
+void HLoopOptimization::SetPeelingCandidate(int64_t trip_count ATTRIBUTE_UNUSED) {
+ // Current heuristic: none.
+ // TODO: implement
+}
+
+uint32_t HLoopOptimization::GetUnrollingFactor(HBasicBlock* block, int64_t trip_count) {
+ // Current heuristic: unroll by 2 on ARM64/X86 for large known trip
+ // counts and small loop bodies.
+ // TODO: refine with operation count, remaining iterations, etc.
+ // Artem had some really cool ideas for this already.
+ switch (compiler_driver_->GetInstructionSet()) {
+ case kArm64:
+ case kX86:
+ case kX86_64: {
+ size_t num_instructions = block->GetInstructions().CountSize();
+ if (num_instructions <= 10 && trip_count >= 4 * vector_length_) {
+ return 2;
+ }
+ return 1;
+ }
+ default:
+ return 1;
+ }
+}
+
+//
// Helpers.
//
bool HLoopOptimization::TrySetPhiInduction(HPhi* phi, bool restrict_uses) {
+ // Special case Phis that have equivalent in a debuggable setup. Our graph checker isn't
+ // smart enough to follow strongly connected components (and it's probably not worth
+ // it to make it so). See b/33775412.
+ if (graph_->IsDebuggable() && phi->HasEquivalentPhi()) {
+ return false;
+ }
DCHECK(iset_->empty());
ArenaSet<HInstruction*>* set = induction_range_.LookupCycle(phi);
if (set != nullptr) {
@@ -1576,8 +1688,8 @@ bool HLoopOptimization::TryReplaceWithLastValue(HLoopInformation* loop_info,
size_t index = it->GetIndex();
++it; // increment before replacing
if (iset_->find(user->GetHolder()) == iset_->end()) { // not excluded?
- HLoopInformation* other_loop_info = user->GetHolder()->GetBlock()->GetLoopInformation();
// Only update environment uses after the loop.
+ HLoopInformation* other_loop_info = user->GetHolder()->GetBlock()->GetLoopInformation();
if (other_loop_info == nullptr || !other_loop_info->IsIn(*loop_info)) {
user->RemoveAsUserOfInput(index);
user->SetRawEnvAt(index, replacement);
@@ -1614,4 +1726,21 @@ void HLoopOptimization::RemoveDeadInstructions(const HInstructionList& list) {
}
}
+bool HLoopOptimization::CanRemoveCycle() {
+ for (HInstruction* i : *iset_) {
+ // We can never remove instructions that have environment
+ // uses when we compile 'debuggable'.
+ if (i->HasEnvironmentUses() && graph_->IsDebuggable()) {
+ return false;
+ }
+ // A deoptimization should never have an environment input removed.
+ for (const HUseListNode<HEnvironment*>& use : i->GetEnvUses()) {
+ if (use.GetUser()->GetHolder()->IsDeoptimize()) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
} // namespace art
diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h
index cc6343aeb5..de4bd85fc8 100644
--- a/compiler/optimizing/loop_optimization.h
+++ b/compiler/optimizing/loop_optimization.h
@@ -116,14 +116,15 @@ class HLoopOptimization : public HOptimization {
void OptimizeInnerLoop(LoopNode* node);
// Vectorization analysis and synthesis.
- bool CanVectorize(LoopNode* node, HBasicBlock* block, int64_t trip_count);
+ bool ShouldVectorize(LoopNode* node, HBasicBlock* block, int64_t trip_count);
void Vectorize(LoopNode* node, HBasicBlock* block, HBasicBlock* exit, int64_t trip_count);
void GenerateNewLoop(LoopNode* node,
HBasicBlock* block,
HBasicBlock* new_preheader,
HInstruction* lo,
HInstruction* hi,
- HInstruction* step);
+ HInstruction* step,
+ uint32_t unroll);
bool VectorizeDef(LoopNode* node, HInstruction* instruction, bool generate_code);
bool VectorizeUse(LoopNode* node,
HInstruction* instruction,
@@ -133,10 +134,11 @@ class HLoopOptimization : public HOptimization {
bool TrySetVectorType(Primitive::Type type, /*out*/ uint64_t* restrictions);
bool TrySetVectorLength(uint32_t length);
void GenerateVecInv(HInstruction* org, Primitive::Type type);
- void GenerateVecSub(HInstruction* org, HInstruction* off);
+ void GenerateVecSub(HInstruction* org, HInstruction* offset);
void GenerateVecMem(HInstruction* org,
HInstruction* opa,
HInstruction* opb,
+ HInstruction* offset,
Primitive::Type type);
void GenerateVecOp(HInstruction* org,
HInstruction* opa,
@@ -151,6 +153,11 @@ class HLoopOptimization : public HOptimization {
Primitive::Type type,
uint64_t restrictions);
+ // Vectorization heuristics.
+ bool IsVectorizationProfitable(int64_t trip_count);
+ void SetPeelingCandidate(int64_t trip_count);
+ uint32_t GetUnrollingFactor(HBasicBlock* block, int64_t trip_count);
+
// Helpers.
bool TrySetPhiInduction(HPhi* phi, bool restrict_uses);
bool TrySetSimpleLoopHeader(HBasicBlock* block);
@@ -208,20 +215,25 @@ class HLoopOptimization : public HOptimization {
// Contents reside in phase-local heap memory.
ArenaSet<ArrayReference>* vector_refs_;
+ // Dynamic loop peeling candidate for alignment.
+ const ArrayReference* vector_peeling_candidate_;
+
+ // Dynamic data dependence test of the form a != b.
+ HInstruction* vector_runtime_test_a_;
+ HInstruction* vector_runtime_test_b_;
+
// Mapping used during vectorization synthesis for both the scalar peeling/cleanup
- // loop (simd_ is false) and the actual vector loop (simd_ is true). The data
+ // loop (mode is kSequential) and the actual vector loop (mode is kVector). The data
// structure maps original instructions into the new instructions.
// Contents reside in phase-local heap memory.
ArenaSafeMap<HInstruction*, HInstruction*>* vector_map_;
// Temporary vectorization bookkeeping.
+ VectorMode vector_mode_; // synthesis mode
HBasicBlock* vector_preheader_; // preheader of the new loop
HBasicBlock* vector_header_; // header of the new loop
HBasicBlock* vector_body_; // body of the new loop
- HInstruction* vector_runtime_test_a_;
- HInstruction* vector_runtime_test_b_; // defines a != b runtime test
- HPhi* vector_phi_; // the Phi representing the normalized loop index
- VectorMode vector_mode_; // selects synthesis mode
+ HInstruction* vector_index_; // normalized index of the new loop
friend class LoopOptimizationTest;
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index d0047c54f2..4ca833707b 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -967,6 +967,7 @@ void HInstructionList::AddInstruction(HInstruction* instruction) {
DCHECK(last_instruction_ == nullptr);
first_instruction_ = last_instruction_ = instruction;
} else {
+ DCHECK(last_instruction_ != nullptr);
last_instruction_->next_ = instruction;
instruction->previous_ = last_instruction_;
last_instruction_ = instruction;
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index ffa16dd787..5e072cdb67 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -421,7 +421,7 @@ class HGraph : public ArenaObject<kArenaAllocGraph> {
void SimplifyLoop(HBasicBlock* header);
int32_t GetNextInstructionId() {
- DCHECK_NE(current_instruction_id_, INT32_MAX);
+ CHECK_NE(current_instruction_id_, INT32_MAX);
return current_instruction_id_++;
}
@@ -430,7 +430,7 @@ class HGraph : public ArenaObject<kArenaAllocGraph> {
}
void SetCurrentInstructionId(int32_t id) {
- DCHECK_GE(id, current_instruction_id_);
+ CHECK_GE(id, current_instruction_id_);
current_instruction_id_ = id;
}
@@ -2612,6 +2612,16 @@ class HPhi FINAL : public HVariableInputSizeInstruction {
&& other->AsPhi()->GetRegNumber() == GetRegNumber();
}
+ bool HasEquivalentPhi() const {
+ if (GetPrevious() != nullptr && GetPrevious()->AsPhi()->GetRegNumber() == GetRegNumber()) {
+ return true;
+ }
+ if (GetNext() != nullptr && GetNext()->AsPhi()->GetRegNumber() == GetRegNumber()) {
+ return true;
+ }
+ return false;
+ }
+
// Returns the next equivalent phi (starting from the current one) or null if there is none.
// An equivalent phi is a phi having the same dex register and type.
// It assumes that phis with the same dex register are adjacent.
diff --git a/compiler/optimizing/scheduler.cc b/compiler/optimizing/scheduler.cc
index 320f01a727..147fa1c05a 100644
--- a/compiler/optimizing/scheduler.cc
+++ b/compiler/optimizing/scheduler.cc
@@ -109,6 +109,10 @@ void SchedulingGraph::AddDependencies(HInstruction* instruction, bool is_schedul
// barrier depend on it.
for (HInstruction* other = instruction->GetNext(); other != nullptr; other = other->GetNext()) {
SchedulingNode* other_node = GetNode(other);
+ CHECK(other_node != nullptr)
+ << other->DebugName()
+ << " is in block " << other->GetBlock()->GetBlockId()
+ << ", and expected in block " << instruction->GetBlock()->GetBlockId();
bool other_is_barrier = other_node->IsSchedulingBarrier();
if (is_scheduling_barrier || other_is_barrier) {
AddOtherDependency(other_node, instruction_node);
@@ -377,6 +381,10 @@ void HScheduler::Schedule(HBasicBlock* block) {
scheduling_graph_.Clear();
for (HBackwardInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
HInstruction* instruction = it.Current();
+ CHECK_EQ(instruction->GetBlock(), block)
+ << instruction->DebugName()
+ << " is in block " << instruction->GetBlock()->GetBlockId()
+ << ", and expected in block " << block->GetBlockId();
SchedulingNode* node = scheduling_graph_.AddNode(instruction, IsSchedulingBarrier(instruction));
CalculateLatency(node);
scheduling_nodes.push_back(node);
diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc
index 7b7495bf3b..185303bc8c 100644
--- a/compiler/optimizing/ssa_liveness_analysis.cc
+++ b/compiler/optimizing/ssa_liveness_analysis.cc
@@ -197,7 +197,7 @@ void SsaLivenessAnalysis::ComputeLiveRanges() {
HInstruction* instruction = environment->GetInstructionAt(i);
bool should_be_live = ShouldBeLiveForEnvironment(current, instruction);
if (should_be_live) {
- DCHECK(instruction->HasSsaIndex());
+ CHECK(instruction->HasSsaIndex()) << instruction->DebugName();
live_in->SetBit(instruction->GetSsaIndex());
}
if (instruction != nullptr) {
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 92d60b252b..ca0bae1c8e 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -925,8 +925,6 @@ class Dex2Oat FINAL {
break;
}
- compiler_options_->verbose_methods_ = verbose_methods_.empty() ? nullptr : &verbose_methods_;
-
if (!IsBootImage() && multi_image_) {
Usage("--multi-image can only be used when creating boot images");
}
@@ -1262,11 +1260,6 @@ class Dex2Oat FINAL {
app_image_file_name_ = option.substr(strlen("--app-image-file=")).data();
} else if (option.starts_with("--app-image-fd=")) {
ParseUintOption(option, "--app-image-fd", &app_image_fd_, Usage);
- } else if (option.starts_with("--verbose-methods=")) {
- // TODO: rather than switch off compiler logging, make all VLOG(compiler) messages
- // conditional on having verbost methods.
- gLogVerbosity.compiler = false;
- Split(option.substr(strlen("--verbose-methods=")).ToString(), ',', &verbose_methods_);
} else if (option == "--multi-image") {
multi_image_ = true;
} else if (option.starts_with("--no-inline-from=")) {
@@ -2804,7 +2797,6 @@ class Dex2Oat FINAL {
std::vector<const DexFile*> no_inline_from_dex_files_;
- std::vector<std::string> verbose_methods_;
bool dump_stats_;
bool dump_passes_;
bool dump_timing_;
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index d8bafc011a..066c66ac93 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -3466,7 +3466,7 @@ struct OatdumpArgs : public CmdlineArgs {
" Example: --image=/system/framework/boot.art\n"
"\n"
" --app-image=<file.art>: specifies an input app image. Must also have a specified\n"
- " boot image and app oat file.\n"
+ " boot image (with --image) and app oat file (with --app-oat).\n"
" Example: --app-image=app.art\n"
"\n"
" --app-oat=<file.odex>: specifies an input app oat.\n"
diff --git a/runtime/arch/arm/context_arm.h b/runtime/arch/arm/context_arm.h
index 2623ee9315..fa9aa46d4d 100644
--- a/runtime/arch/arm/context_arm.h
+++ b/runtime/arch/arm/context_arm.h
@@ -25,7 +25,7 @@
namespace art {
namespace arm {
-class ArmContext : public Context {
+class ArmContext FINAL : public Context {
public:
ArmContext() {
Reset();
diff --git a/runtime/arch/arm64/context_arm64.h b/runtime/arch/arm64/context_arm64.h
index 105e78461d..36aded07c4 100644
--- a/runtime/arch/arm64/context_arm64.h
+++ b/runtime/arch/arm64/context_arm64.h
@@ -25,7 +25,7 @@
namespace art {
namespace arm64 {
-class Arm64Context : public Context {
+class Arm64Context FINAL : public Context {
public:
Arm64Context() {
Reset();
diff --git a/runtime/arch/x86/context_x86.h b/runtime/arch/x86/context_x86.h
index f482d9ffcb..303dfe361c 100644
--- a/runtime/arch/x86/context_x86.h
+++ b/runtime/arch/x86/context_x86.h
@@ -25,7 +25,7 @@
namespace art {
namespace x86 {
-class X86Context : public Context {
+class X86Context FINAL : public Context {
public:
X86Context() {
Reset();
diff --git a/runtime/arch/x86_64/context_x86_64.h b/runtime/arch/x86_64/context_x86_64.h
index 46f2b63848..f8e2845983 100644
--- a/runtime/arch/x86_64/context_x86_64.h
+++ b/runtime/arch/x86_64/context_x86_64.h
@@ -25,7 +25,7 @@
namespace art {
namespace x86_64 {
-class X86_64Context : public Context {
+class X86_64Context FINAL : public Context {
public:
X86_64Context() {
Reset();
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index ac433dd403..3e7ed9799d 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -61,6 +61,19 @@ DEFINE_RUNTIME_DEBUG_FLAG(ArtMethod, kCheckDeclaringClassState);
static_assert(ArtMethod::kRuntimeMethodDexMethodIndex == DexFile::kDexNoIndex,
"Wrong runtime-method dex method index");
+ArtMethod* ArtMethod::GetCanonicalMethod(PointerSize pointer_size) {
+ if (LIKELY(!IsDefault())) {
+ return this;
+ } else {
+ mirror::Class* declaring_class = GetDeclaringClass();
+ ArtMethod* ret = declaring_class->FindDeclaredVirtualMethod(declaring_class->GetDexCache(),
+ GetDexMethodIndex(),
+ pointer_size);
+ DCHECK(ret != nullptr);
+ return ret;
+ }
+}
+
ArtMethod* ArtMethod::GetNonObsoleteMethod() {
DCHECK_EQ(kRuntimePointerSize, Runtime::Current()->GetClassLinker()->GetImagePointerSize());
if (LIKELY(!IsObsolete())) {
@@ -405,15 +418,19 @@ bool ArtMethod::IsOverridableByDefaultMethod() {
bool ArtMethod::IsAnnotatedWithFastNative() {
return IsAnnotatedWith(WellKnownClasses::dalvik_annotation_optimization_FastNative,
- DexFile::kDexVisibilityBuild);
+ DexFile::kDexVisibilityBuild,
+ /* lookup_in_resolved_boot_classes */ true);
}
bool ArtMethod::IsAnnotatedWithCriticalNative() {
return IsAnnotatedWith(WellKnownClasses::dalvik_annotation_optimization_CriticalNative,
- DexFile::kDexVisibilityBuild);
+ DexFile::kDexVisibilityBuild,
+ /* lookup_in_resolved_boot_classes */ true);
}
-bool ArtMethod::IsAnnotatedWith(jclass klass, uint32_t visibility) {
+bool ArtMethod::IsAnnotatedWith(jclass klass,
+ uint32_t visibility,
+ bool lookup_in_resolved_boot_classes) {
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
StackHandleScope<1> shs(self);
@@ -422,10 +439,8 @@ bool ArtMethod::IsAnnotatedWith(jclass klass, uint32_t visibility) {
DCHECK(annotation->IsAnnotation());
Handle<mirror::Class> annotation_handle(shs.NewHandle(annotation));
- // Note: Resolves any method annotations' classes as a side-effect.
- // -- This seems allowed by the spec since it says we can preload any classes
- // referenced by another classes's constant pool table.
- return annotations::IsMethodAnnotationPresent(this, annotation_handle, visibility);
+ return annotations::IsMethodAnnotationPresent(
+ this, annotation_handle, visibility, lookup_in_resolved_boot_classes);
}
static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file,
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 396c8784a3..4b3e8efdad 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -483,6 +483,12 @@ class ArtMethod FINAL {
}
}
+ // Takes a method and returns a 'canonical' one if the method is default (and therefore
+ // potentially copied from some other class). For example, this ensures that the debugger does not
+ // get confused as to which method we are in.
+ ArtMethod* GetCanonicalMethod(PointerSize pointer_size = kRuntimePointerSize)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
ArtMethod* GetSingleImplementation(PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -723,7 +729,10 @@ class ArtMethod FINAL {
private:
uint16_t FindObsoleteDexClassDefIndex() REQUIRES_SHARED(Locks::mutator_lock_);
- bool IsAnnotatedWith(jclass klass, uint32_t visibility);
+ // If `lookup_in_resolved_boot_classes` is true, look up any of the
+ // method's annotations' classes in the bootstrap class loader's
+ // resolved types; otherwise, resolve them as a side effect.
+ bool IsAnnotatedWith(jclass klass, uint32_t visibility, bool lookup_in_resolved_boot_classes);
static constexpr size_t PtrSizedFieldsOffset(PointerSize pointer_size) {
// Round up to pointer size for padding field. Tested in art_method.cc.
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 141df1ec1a..10e0bd28c3 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -4064,7 +4064,10 @@ verifier::FailureKind ClassLinker::VerifyClass(
while (old_status == mirror::Class::kStatusVerifying ||
old_status == mirror::Class::kStatusVerifyingAtRuntime) {
lock.WaitIgnoringInterrupts();
- CHECK(klass->IsErroneous() || (klass->GetStatus() > old_status))
+ // WaitIgnoringInterrupts can still receive an interrupt and return early, in this
+ // case we may see the same status again. b/62912904. This is why the check is
+ // greater or equal.
+ CHECK(klass->IsErroneous() || (klass->GetStatus() >= old_status))
<< "Class '" << klass->PrettyClass()
<< "' performed an illegal verification state transition from " << old_status
<< " to " << klass->GetStatus();
@@ -4107,6 +4110,10 @@ verifier::FailureKind ClassLinker::VerifyClass(
}
}
+ VLOG(class_linker) << "Beginning verification for class: "
+ << klass->PrettyDescriptor()
+ << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8();
+
// Verify super class.
StackHandleScope<2> hs(self);
MutableHandle<mirror::Class> supertype(hs.NewHandle(klass->GetSuperClass()));
@@ -4161,6 +4168,13 @@ verifier::FailureKind ClassLinker::VerifyClass(
const DexFile& dex_file = *klass->GetDexCache()->GetDexFile();
mirror::Class::Status oat_file_class_status(mirror::Class::kStatusNotReady);
bool preverified = VerifyClassUsingOatFile(dex_file, klass.Get(), oat_file_class_status);
+
+ VLOG(class_linker) << "Class preverified status for class "
+ << klass->PrettyDescriptor()
+ << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8()
+ << ": "
+ << preverified;
+
// If the oat file says the class had an error, re-run the verifier. That way we will get a
// precise error message. To ensure a rerun, test:
// mirror::Class::IsErroneous(oat_file_class_status) => !preverified
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 12bdb32fec..cc12439074 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -77,25 +77,10 @@ static uint16_t CappedAllocRecordCount(size_t alloc_record_count) {
return alloc_record_count;
}
-// Takes a method and returns a 'canonical' one if the method is default (and therefore potentially
-// copied from some other class). This ensures that the debugger does not get confused as to which
-// method we are in.
-static ArtMethod* GetCanonicalMethod(ArtMethod* m)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- if (LIKELY(!m->IsDefault())) {
- return m;
- } else {
- mirror::Class* declaring_class = m->GetDeclaringClass();
- return declaring_class->FindDeclaredVirtualMethod(declaring_class->GetDexCache(),
- m->GetDexMethodIndex(),
- kRuntimePointerSize);
- }
-}
-
class Breakpoint : public ValueObject {
public:
Breakpoint(ArtMethod* method, uint32_t dex_pc, DeoptimizationRequest::Kind deoptimization_kind)
- : method_(GetCanonicalMethod(method)),
+ : method_(method->GetCanonicalMethod(kRuntimePointerSize)),
dex_pc_(dex_pc),
deoptimization_kind_(deoptimization_kind) {
CHECK(deoptimization_kind_ == DeoptimizationRequest::kNothing ||
@@ -125,7 +110,7 @@ class Breakpoint : public ValueObject {
// Returns true if the method of this breakpoint and the passed in method should be considered the
// same. That is, they are either the same method or they are copied from the same method.
bool IsInMethod(ArtMethod* m) const REQUIRES_SHARED(Locks::mutator_lock_) {
- return method_ == GetCanonicalMethod(m);
+ return method_ == m->GetCanonicalMethod(kRuntimePointerSize);
}
private:
@@ -1367,7 +1352,8 @@ JDWP::FieldId Dbg::ToFieldId(const ArtField* f) {
static JDWP::MethodId ToMethodId(ArtMethod* m)
REQUIRES_SHARED(Locks::mutator_lock_) {
- return static_cast<JDWP::MethodId>(reinterpret_cast<uintptr_t>(GetCanonicalMethod(m)));
+ return static_cast<JDWP::MethodId>(
+ reinterpret_cast<uintptr_t>(m->GetCanonicalMethod(kRuntimePointerSize)));
}
static ArtField* FromFieldId(JDWP::FieldId fid)
@@ -2887,7 +2873,7 @@ static void SetEventLocation(JDWP::EventLocation* location, ArtMethod* m, uint32
if (m == nullptr) {
memset(location, 0, sizeof(*location));
} else {
- location->method = GetCanonicalMethod(m);
+ location->method = m->GetCanonicalMethod(kRuntimePointerSize);
location->dex_pc = (m->IsNative() || m->IsProxyMethod()) ? static_cast<uint32_t>(-1) : dex_pc;
}
}
diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc
index f21f1a2704..2b81f0a99a 100644
--- a/runtime/dex_file_annotations.cc
+++ b/runtime/dex_file_annotations.cc
@@ -751,7 +751,8 @@ const DexFile::AnnotationItem* GetAnnotationItemFromAnnotationSet(
const ClassData& klass,
const DexFile::AnnotationSetItem* annotation_set,
uint32_t visibility,
- Handle<mirror::Class> annotation_class)
+ Handle<mirror::Class> annotation_class,
+ bool lookup_in_resolved_boot_classes = false)
REQUIRES_SHARED(Locks::mutator_lock_) {
const DexFile& dex_file = klass.GetDexFile();
for (uint32_t i = 0; i < annotation_set->size_; ++i) {
@@ -761,19 +762,37 @@ const DexFile::AnnotationItem* GetAnnotationItemFromAnnotationSet(
}
const uint8_t* annotation = annotation_item->annotation_;
uint32_t type_index = DecodeUnsignedLeb128(&annotation);
- StackHandleScope<2> hs(Thread::Current());
- mirror::Class* resolved_class = Runtime::Current()->GetClassLinker()->ResolveType(
- klass.GetDexFile(),
- dex::TypeIndex(type_index),
- hs.NewHandle(klass.GetDexCache()),
- hs.NewHandle(klass.GetClassLoader()));
- if (resolved_class == nullptr) {
- std::string temp;
- LOG(WARNING) << StringPrintf("Unable to resolve %s annotation class %d",
- klass.GetRealClass()->GetDescriptor(&temp), type_index);
- CHECK(Thread::Current()->IsExceptionPending());
- Thread::Current()->ClearException();
- continue;
+ mirror::Class* resolved_class;
+ if (lookup_in_resolved_boot_classes) {
+ ObjPtr<mirror::Class> looked_up_class =
+ Runtime::Current()->GetClassLinker()->LookupResolvedType(
+ klass.GetDexFile(),
+ dex::TypeIndex(type_index),
+ klass.GetDexCache(),
+ // Force the use of the bootstrap class loader.
+ static_cast<mirror::ClassLoader*>(nullptr));
+ resolved_class = looked_up_class.Ptr();
+ if (resolved_class == nullptr) {
+ // If `resolved_class` is null, this is fine: just ignore that
+ // annotation item. We expect this to happen, as we do not
+ // attempt to resolve the annotation's class in this code path.
+ continue;
+ }
+ } else {
+ StackHandleScope<2> hs(Thread::Current());
+ resolved_class = Runtime::Current()->GetClassLinker()->ResolveType(
+ klass.GetDexFile(),
+ dex::TypeIndex(type_index),
+ hs.NewHandle(klass.GetDexCache()),
+ hs.NewHandle(klass.GetClassLoader()));
+ if (resolved_class == nullptr) {
+ std::string temp;
+ LOG(WARNING) << StringPrintf("Unable to resolve %s annotation class %d",
+ klass.GetRealClass()->GetDescriptor(&temp), type_index);
+ CHECK(Thread::Current()->IsExceptionPending());
+ Thread::Current()->ClearException();
+ continue;
+ }
}
if (resolved_class == annotation_class.Get()) {
return annotation_item;
@@ -1200,15 +1219,20 @@ mirror::ObjectArray<mirror::String>* GetSignatureAnnotationForMethod(ArtMethod*
return GetSignatureValue(ClassData(method), annotation_set);
}
-bool IsMethodAnnotationPresent(ArtMethod* method, Handle<mirror::Class> annotation_class,
- uint32_t visibility /* = DexFile::kDexVisibilityRuntime */) {
+bool IsMethodAnnotationPresent(ArtMethod* method,
+ Handle<mirror::Class> annotation_class,
+ uint32_t visibility /* = DexFile::kDexVisibilityRuntime */,
+ bool lookup_in_resolved_boot_classes /* = false */) {
const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(method);
if (annotation_set == nullptr) {
return false;
}
const DexFile::AnnotationItem* annotation_item =
GetAnnotationItemFromAnnotationSet(ClassData(method),
- annotation_set, visibility, annotation_class);
+ annotation_set,
+ visibility,
+ annotation_class,
+ lookup_in_resolved_boot_classes);
return annotation_item != nullptr;
}
diff --git a/runtime/dex_file_annotations.h b/runtime/dex_file_annotations.h
index 651c9844eb..e1088823c3 100644
--- a/runtime/dex_file_annotations.h
+++ b/runtime/dex_file_annotations.h
@@ -65,8 +65,15 @@ bool GetParametersMetadataForMethod(ArtMethod* method,
REQUIRES_SHARED(Locks::mutator_lock_);
mirror::ObjectArray<mirror::String>* GetSignatureAnnotationForMethod(ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_);
-bool IsMethodAnnotationPresent(ArtMethod* method, Handle<mirror::Class> annotation_class,
- uint32_t visibility = DexFile::kDexVisibilityRuntime)
+// Check whether `method` is annotated with `annotation_class`.
+// If `lookup_in_resolved_boot_classes` is true, look up any of the
+// method's annotations' classes in the bootstrap class loader's
+// resolved types; if it is false (default value), resolve them as a
+// side effect.
+bool IsMethodAnnotationPresent(ArtMethod* method,
+ Handle<mirror::Class> annotation_class,
+ uint32_t visibility = DexFile::kDexVisibilityRuntime,
+ bool lookup_in_resolved_boot_classes = false)
REQUIRES_SHARED(Locks::mutator_lock_);
// Class annotations.
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index b7cd39f107..2c99aeba88 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -2181,11 +2181,39 @@ extern "C" TwoWordReturn artQuickGenericJniTrampoline(Thread* self, ArtMethod**
REQUIRES_SHARED(Locks::mutator_lock_) {
ArtMethod* called = *sp;
DCHECK(called->IsNative()) << called->PrettyMethod(true);
+ // Fix up a callee-save frame at the bottom of the stack (at `*sp`,
+ // above the alloca region) while we check for optimization
+ // annotations, thus allowing stack walking until the completion of
+ // the JNI frame creation.
+ //
+ // Note however that the Generic JNI trampoline does not expect
+ // exception being thrown at that stage.
+ *sp = Runtime::Current()->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs);
+ self->SetTopOfStack(sp);
uint32_t shorty_len = 0;
const char* shorty = called->GetShorty(&shorty_len);
+ // Optimization annotations lookup does not try to resolve classes,
+ // as this may throw an exception, which is not supported by the
+ // Generic JNI trampoline at this stage; instead, method's
+ // annotations' classes are looked up in the bootstrap class
+ // loader's resolved types (which won't trigger an exception).
bool critical_native = called->IsAnnotatedWithCriticalNative();
+ // ArtMethod::IsAnnotatedWithCriticalNative should not throw
+ // an exception; clear it if it happened anyway.
+ // TODO: Revisit this code path and turn this into a CHECK(!self->IsExceptionPending()).
+ if (self->IsExceptionPending()) {
+ self->ClearException();
+ }
bool fast_native = called->IsAnnotatedWithFastNative();
+ // ArtMethod::IsAnnotatedWithFastNative should not throw
+ // an exception; clear it if it happened anyway.
+ // TODO: Revisit this code path and turn this into a CHECK(!self->IsExceptionPending()).
+ if (self->IsExceptionPending()) {
+ self->ClearException();
+ }
bool normal_native = !critical_native && !fast_native;
+ // Restore the initial ArtMethod pointer at `*sp`.
+ *sp = called;
// Run the visitor and update sp.
BuildGenericJniFrameVisitor visitor(self,
diff --git a/runtime/gc/gc_cause.cc b/runtime/gc/gc_cause.cc
index 39b5e3952d..a3a2051934 100644
--- a/runtime/gc/gc_cause.cc
+++ b/runtime/gc/gc_cause.cc
@@ -25,6 +25,7 @@ namespace gc {
const char* PrettyCause(GcCause cause) {
switch (cause) {
+ case kGcCauseNone: return "None";
case kGcCauseForAlloc: return "Alloc";
case kGcCauseBackground: return "Background";
case kGcCauseExplicit: return "Explicit";
diff --git a/runtime/gc/gc_cause.h b/runtime/gc/gc_cause.h
index b2b3a91645..78496f3ead 100644
--- a/runtime/gc/gc_cause.h
+++ b/runtime/gc/gc_cause.h
@@ -24,6 +24,8 @@ namespace gc {
// What caused the GC?
enum GcCause {
+ // Invalid GC cause used as a placeholder.
+ kGcCauseNone,
// GC triggered by a failed allocation. Thread doing allocation is blocked waiting for GC before
// retrying allocation.
kGcCauseForAlloc,
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index d944ce4904..880b2d40bd 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -214,6 +214,7 @@ Heap::Heap(size_t initial_size,
disable_thread_flip_count_(0),
thread_flip_running_(false),
collector_type_running_(kCollectorTypeNone),
+ last_gc_cause_(kGcCauseNone),
thread_running_gc_(nullptr),
last_gc_type_(collector::kGcTypeNone),
next_gc_type_(collector::kGcTypePartial),
@@ -1458,6 +1459,7 @@ void Heap::StartGC(Thread* self, GcCause cause, CollectorType collector_type) {
// Ensure there is only one GC at a time.
WaitForGcToCompleteLocked(cause, self);
collector_type_running_ = collector_type;
+ last_gc_cause_ = cause;
thread_running_gc_ = self;
}
@@ -3537,6 +3539,7 @@ collector::GcType Heap::WaitForGcToComplete(GcCause cause, Thread* self) {
collector::GcType Heap::WaitForGcToCompleteLocked(GcCause cause, Thread* self) {
collector::GcType last_gc_type = collector::kGcTypeNone;
+ GcCause last_gc_cause = kGcCauseNone;
uint64_t wait_start = NanoTime();
while (collector_type_running_ != kCollectorTypeNone) {
if (self != task_processor_->GetRunningThread()) {
@@ -3551,12 +3554,13 @@ collector::GcType Heap::WaitForGcToCompleteLocked(GcCause cause, Thread* self) {
// We must wait, change thread state then sleep on gc_complete_cond_;
gc_complete_cond_->Wait(self);
last_gc_type = last_gc_type_;
+ last_gc_cause = last_gc_cause_;
}
uint64_t wait_time = NanoTime() - wait_start;
total_wait_time_ += wait_time;
if (wait_time > long_pause_log_threshold_) {
- LOG(INFO) << "WaitForGcToComplete blocked for " << PrettyDuration(wait_time)
- << " for cause " << cause;
+ LOG(INFO) << "WaitForGcToComplete blocked " << cause << " on " << last_gc_cause << " for "
+ << PrettyDuration(wait_time);
}
if (self != task_processor_->GetRunningThread()) {
// The current thread is about to run a collection. If the thread
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 0289250966..3484e0297d 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -1189,9 +1189,12 @@ class Heap {
// Task processor, proxies heap trim requests to the daemon threads.
std::unique_ptr<TaskProcessor> task_processor_;
- // True while the garbage collector is running.
+ // Collector type of the running GC.
volatile CollectorType collector_type_running_ GUARDED_BY(gc_complete_lock_);
+ // Cause of the last running GC.
+ volatile GcCause last_gc_cause_ GUARDED_BY(gc_complete_lock_);
+
// The thread currently running the GC.
volatile Thread* thread_running_gc_ GUARDED_BY(gc_complete_lock_);
diff --git a/runtime/gc/heap_verification_test.cc b/runtime/gc/heap_verification_test.cc
index 8ea0459c89..40ee86ce79 100644
--- a/runtime/gc/heap_verification_test.cc
+++ b/runtime/gc/heap_verification_test.cc
@@ -54,6 +54,11 @@ TEST_F(VerificationTest, IsValidHeapObjectAddress) {
Handle<mirror::String> string(
hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "test")));
EXPECT_TRUE(v->IsValidHeapObjectAddress(string.Get()));
+ // Address in the heap that isn't aligned.
+ const void* unaligned_address =
+ reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(string.Get()) + 1);
+ EXPECT_TRUE(v->IsAddressInHeapSpace(unaligned_address));
+ EXPECT_FALSE(v->IsValidHeapObjectAddress(unaligned_address));
EXPECT_TRUE(v->IsValidHeapObjectAddress(string->GetClass()));
const uintptr_t uint_klass = reinterpret_cast<uintptr_t>(string->GetClass());
// Not actually a valid object but the verification can't know that. Guaranteed to be inside a
diff --git a/runtime/gc/space/region_space-inl.h b/runtime/gc/space/region_space-inl.h
index fc24fc2974..82e8f20154 100644
--- a/runtime/gc/space/region_space-inl.h
+++ b/runtime/gc/space/region_space-inl.h
@@ -48,58 +48,32 @@ inline mirror::Object* RegionSpace::AllocNonvirtual(size_t num_bytes, size_t* by
mirror::Object* obj;
if (LIKELY(num_bytes <= kRegionSize)) {
// Non-large object.
- if (!kForEvac) {
- obj = current_region_->Alloc(num_bytes, bytes_allocated, usable_size,
- bytes_tl_bulk_allocated);
- } else {
- DCHECK(evac_region_ != nullptr);
- obj = evac_region_->Alloc(num_bytes, bytes_allocated, usable_size,
- bytes_tl_bulk_allocated);
- }
+ obj = (kForEvac ? evac_region_ : current_region_)->Alloc(num_bytes,
+ bytes_allocated,
+ usable_size,
+ bytes_tl_bulk_allocated);
if (LIKELY(obj != nullptr)) {
return obj;
}
MutexLock mu(Thread::Current(), region_lock_);
// Retry with current region since another thread may have updated it.
- if (!kForEvac) {
- obj = current_region_->Alloc(num_bytes, bytes_allocated, usable_size,
- bytes_tl_bulk_allocated);
- } else {
- obj = evac_region_->Alloc(num_bytes, bytes_allocated, usable_size,
- bytes_tl_bulk_allocated);
- }
+ obj = (kForEvac ? evac_region_ : current_region_)->Alloc(num_bytes,
+ bytes_allocated,
+ usable_size,
+ bytes_tl_bulk_allocated);
if (LIKELY(obj != nullptr)) {
return obj;
}
- if (!kForEvac) {
- // Retain sufficient free regions for full evacuation.
- if ((num_non_free_regions_ + 1) * 2 > num_regions_) {
- return nullptr;
- }
- for (size_t i = 0; i < num_regions_; ++i) {
- Region* r = &regions_[i];
- if (r->IsFree()) {
- r->Unfree(this, time_);
- r->SetNewlyAllocated();
- ++num_non_free_regions_;
- obj = r->Alloc(num_bytes, bytes_allocated, usable_size, bytes_tl_bulk_allocated);
- CHECK(obj != nullptr);
- current_region_ = r;
- return obj;
- }
- }
- } else {
- for (size_t i = 0; i < num_regions_; ++i) {
- Region* r = &regions_[i];
- if (r->IsFree()) {
- r->Unfree(this, time_);
- ++num_non_free_regions_;
- obj = r->Alloc(num_bytes, bytes_allocated, usable_size, bytes_tl_bulk_allocated);
- CHECK(obj != nullptr);
- evac_region_ = r;
- return obj;
- }
+ Region* r = AllocateRegion(kForEvac);
+ if (LIKELY(r != nullptr)) {
+ if (kForEvac) {
+ evac_region_ = r;
+ } else {
+ current_region_ = r;
}
+ obj = r->Alloc(num_bytes, bytes_allocated, usable_size, bytes_tl_bulk_allocated);
+ CHECK(obj != nullptr);
+ return obj;
}
} else {
// Large object.
diff --git a/runtime/gc/space/region_space.cc b/runtime/gc/space/region_space.cc
index 8d8c4885ef..1f7bd09b3f 100644
--- a/runtime/gc/space/region_space.cc
+++ b/runtime/gc/space/region_space.cc
@@ -29,6 +29,10 @@ namespace space {
// value of the region size, evaculate the region.
static constexpr uint kEvaculateLivePercentThreshold = 75U;
+// If we protect the cleared regions.
+// Only protect for target builds to prevent flaky test failures (b/63131961).
+static constexpr bool kProtectClearedRegions = kIsTargetBuild;
+
MemMap* RegionSpace::CreateMemMap(const std::string& name, size_t capacity,
uint8_t* requested_begin) {
CHECK_ALIGNED(capacity, kRegionSize);
@@ -449,21 +453,14 @@ bool RegionSpace::AllocNewTlab(Thread* self, size_t min_bytes) {
MutexLock mu(self, region_lock_);
RevokeThreadLocalBuffersLocked(self);
// Retain sufficient free regions for full evacuation.
- if ((num_non_free_regions_ + 1) * 2 > num_regions_) {
- return false;
- }
- for (size_t i = 0; i < num_regions_; ++i) {
- Region* r = &regions_[i];
- if (r->IsFree()) {
- r->Unfree(this, time_);
- ++num_non_free_regions_;
- r->SetNewlyAllocated();
- r->SetTop(r->End());
- r->is_a_tlab_ = true;
- r->thread_ = self;
- self->SetTlab(r->Begin(), r->Begin() + min_bytes, r->End());
- return true;
- }
+
+ Region* r = AllocateRegion(/*for_evac*/ false);
+ if (r != nullptr) {
+ r->is_a_tlab_ = true;
+ r->thread_ = self;
+ r->SetTop(r->End());
+ self->SetTlab(r->Begin(), r->Begin() + min_bytes, r->End());
+ return true;
}
return false;
}
@@ -543,6 +540,68 @@ size_t RegionSpace::AllocationSizeNonvirtual(mirror::Object* obj, size_t* usable
return num_bytes;
}
+void RegionSpace::Region::Clear(bool zero_and_release_pages) {
+ top_.StoreRelaxed(begin_);
+ state_ = RegionState::kRegionStateFree;
+ type_ = RegionType::kRegionTypeNone;
+ objects_allocated_.StoreRelaxed(0);
+ alloc_time_ = 0;
+ live_bytes_ = static_cast<size_t>(-1);
+ if (zero_and_release_pages) {
+ ZeroAndReleasePages(begin_, end_ - begin_);
+ }
+ if (kProtectClearedRegions) {
+ mprotect(begin_, end_ - begin_, PROT_NONE);
+ }
+ is_newly_allocated_ = false;
+ is_a_tlab_ = false;
+ thread_ = nullptr;
+}
+
+RegionSpace::Region* RegionSpace::AllocateRegion(bool for_evac) {
+ if (!for_evac && (num_non_free_regions_ + 1) * 2 > num_regions_) {
+ return nullptr;
+ }
+ for (size_t i = 0; i < num_regions_; ++i) {
+ Region* r = &regions_[i];
+ if (r->IsFree()) {
+ r->Unfree(this, time_);
+ ++num_non_free_regions_;
+ if (!for_evac) {
+ // Evac doesn't count as newly allocated.
+ r->SetNewlyAllocated();
+ }
+ return r;
+ }
+ }
+ return nullptr;
+}
+
+void RegionSpace::Region::MarkAsAllocated(RegionSpace* region_space, uint32_t alloc_time) {
+ DCHECK(IsFree());
+ alloc_time_ = alloc_time;
+ region_space->AdjustNonFreeRegionLimit(idx_);
+ type_ = RegionType::kRegionTypeToSpace;
+ if (kProtectClearedRegions) {
+ mprotect(Begin(), kRegionSize, PROT_READ | PROT_WRITE);
+ }
+}
+
+void RegionSpace::Region::Unfree(RegionSpace* region_space, uint32_t alloc_time) {
+ MarkAsAllocated(region_space, alloc_time);
+ state_ = RegionState::kRegionStateAllocated;
+}
+
+void RegionSpace::Region::UnfreeLarge(RegionSpace* region_space, uint32_t alloc_time) {
+ MarkAsAllocated(region_space, alloc_time);
+ state_ = RegionState::kRegionStateLarge;
+}
+
+void RegionSpace::Region::UnfreeLargeTail(RegionSpace* region_space, uint32_t alloc_time) {
+ MarkAsAllocated(region_space, alloc_time);
+ state_ = RegionState::kRegionStateLargeTail;
+}
+
} // namespace space
} // namespace gc
} // namespace art
diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h
index 323ccdbd74..8907b07bf2 100644
--- a/runtime/gc/space/region_space.h
+++ b/runtime/gc/space/region_space.h
@@ -284,20 +284,7 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace {
return type_;
}
- void Clear(bool zero_and_release_pages) {
- top_.StoreRelaxed(begin_);
- state_ = RegionState::kRegionStateFree;
- type_ = RegionType::kRegionTypeNone;
- objects_allocated_.StoreRelaxed(0);
- alloc_time_ = 0;
- live_bytes_ = static_cast<size_t>(-1);
- if (zero_and_release_pages) {
- ZeroAndReleasePages(begin_, end_ - begin_);
- }
- is_newly_allocated_ = false;
- is_a_tlab_ = false;
- thread_ = nullptr;
- }
+ void Clear(bool zero_and_release_pages);
ALWAYS_INLINE mirror::Object* Alloc(size_t num_bytes, size_t* bytes_allocated,
size_t* usable_size,
@@ -315,31 +302,16 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace {
// Given a free region, declare it non-free (allocated).
void Unfree(RegionSpace* region_space, uint32_t alloc_time)
- REQUIRES(region_space->region_lock_) {
- DCHECK(IsFree());
- state_ = RegionState::kRegionStateAllocated;
- type_ = RegionType::kRegionTypeToSpace;
- alloc_time_ = alloc_time;
- region_space->AdjustNonFreeRegionLimit(idx_);
- }
+ REQUIRES(region_space->region_lock_);
void UnfreeLarge(RegionSpace* region_space, uint32_t alloc_time)
- REQUIRES(region_space->region_lock_) {
- DCHECK(IsFree());
- state_ = RegionState::kRegionStateLarge;
- type_ = RegionType::kRegionTypeToSpace;
- alloc_time_ = alloc_time;
- region_space->AdjustNonFreeRegionLimit(idx_);
- }
+ REQUIRES(region_space->region_lock_);
void UnfreeLargeTail(RegionSpace* region_space, uint32_t alloc_time)
- REQUIRES(region_space->region_lock_) {
- DCHECK(IsFree());
- state_ = RegionState::kRegionStateLargeTail;
- type_ = RegionType::kRegionTypeToSpace;
- alloc_time_ = alloc_time;
- region_space->AdjustNonFreeRegionLimit(idx_);
- }
+ REQUIRES(region_space->region_lock_);
+
+ void MarkAsAllocated(RegionSpace* region_space, uint32_t alloc_time)
+ REQUIRES(region_space->region_lock_);
void SetNewlyAllocated() {
is_newly_allocated_ = true;
@@ -539,6 +511,8 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace {
}
}
+ Region* AllocateRegion(bool for_evac) REQUIRES(region_lock_);
+
Mutex region_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
uint32_t time_; // The time as the number of collections since the startup.
diff --git a/runtime/gc/verification.cc b/runtime/gc/verification.cc
index 03b26a0a6b..beb43dfcf5 100644
--- a/runtime/gc/verification.cc
+++ b/runtime/gc/verification.cc
@@ -26,6 +26,28 @@
namespace art {
namespace gc {
+std::string Verification::DumpRAMAroundAddress(uintptr_t addr, uintptr_t bytes) const {
+ const uintptr_t dump_start = addr - bytes;
+ const uintptr_t dump_end = addr + bytes;
+ std::ostringstream oss;
+ if (dump_start < dump_end &&
+ IsAddressInHeapSpace(reinterpret_cast<const void*>(dump_start)) &&
+ IsAddressInHeapSpace(reinterpret_cast<const void*>(dump_end - 1))) {
+ oss << " adjacent_ram=";
+ for (uintptr_t p = dump_start; p < dump_end; ++p) {
+ if (p == addr) {
+ // Marker of where the address is.
+ oss << "|";
+ }
+ uint8_t* ptr = reinterpret_cast<uint8_t*>(p);
+ oss << std::hex << std::setfill('0') << std::setw(2) << static_cast<uintptr_t>(*ptr);
+ }
+ } else {
+ oss << " <invalid address>";
+ }
+ return oss.str();
+}
+
std::string Verification::DumpObjectInfo(const void* addr, const char* tag) const {
std::ostringstream oss;
oss << tag << "=" << addr;
@@ -51,23 +73,7 @@ std::string Verification::DumpObjectInfo(const void* addr, const char* tag) cons
card_table->GetCard(reinterpret_cast<const mirror::Object*>(addr)));
}
// Dump adjacent RAM.
- const uintptr_t uint_addr = reinterpret_cast<uintptr_t>(addr);
- static constexpr size_t kBytesBeforeAfter = 2 * kObjectAlignment;
- const uintptr_t dump_start = uint_addr - kBytesBeforeAfter;
- const uintptr_t dump_end = uint_addr + kBytesBeforeAfter;
- if (dump_start < dump_end &&
- IsValidHeapObjectAddress(reinterpret_cast<const void*>(dump_start)) &&
- IsValidHeapObjectAddress(reinterpret_cast<const void*>(dump_end - kObjectAlignment))) {
- oss << " adjacent_ram=";
- for (uintptr_t p = dump_start; p < dump_end; ++p) {
- if (p == uint_addr) {
- // Marker of where the object is.
- oss << "|";
- }
- uint8_t* ptr = reinterpret_cast<uint8_t*>(p);
- oss << std::hex << std::setfill('0') << std::setw(2) << static_cast<uintptr_t>(*ptr);
- }
- }
+ oss << DumpRAMAroundAddress(reinterpret_cast<uintptr_t>(addr), 4 * kObjectAlignment);
} else {
oss << " <invalid address>";
}
@@ -91,12 +97,15 @@ void Verification::LogHeapCorruption(ObjPtr<mirror::Object> holder,
if (holder != nullptr) {
mirror::Class* holder_klass = holder->GetClass<kVerifyNone, kWithoutReadBarrier>();
if (IsValidClass(holder_klass)) {
- oss << "field_offset=" << offset.Uint32Value();
+ oss << " field_offset=" << offset.Uint32Value();
ArtField* field = holder->FindFieldByOffset(offset);
if (field != nullptr) {
oss << " name=" << field->GetName();
}
}
+ mirror::HeapReference<mirror::Object>* addr = holder->GetFieldObjectReferenceAddr(offset);
+ oss << " reference addr"
+ << DumpRAMAroundAddress(reinterpret_cast<uintptr_t>(addr), 4 * kObjectAlignment);
}
if (fatal) {
@@ -106,10 +115,7 @@ void Verification::LogHeapCorruption(ObjPtr<mirror::Object> holder,
}
}
-bool Verification::IsValidHeapObjectAddress(const void* addr, space::Space** out_space) const {
- if (!IsAligned<kObjectAlignment>(addr)) {
- return false;
- }
+bool Verification::IsAddressInHeapSpace(const void* addr, space::Space** out_space) const {
space::Space* const space = heap_->FindSpaceFromAddress(addr);
if (space != nullptr) {
if (out_space != nullptr) {
@@ -120,6 +126,10 @@ bool Verification::IsValidHeapObjectAddress(const void* addr, space::Space** out
return false;
}
+bool Verification::IsValidHeapObjectAddress(const void* addr, space::Space** out_space) const {
+ return IsAligned<kObjectAlignment>(addr) && IsAddressInHeapSpace(addr, out_space);
+}
+
bool Verification::IsValidClass(const void* addr) const {
if (!IsValidHeapObjectAddress(addr)) {
return false;
diff --git a/runtime/gc/verification.h b/runtime/gc/verification.h
index 903e159c5a..6b456fd349 100644
--- a/runtime/gc/verification.h
+++ b/runtime/gc/verification.h
@@ -49,11 +49,10 @@ class Verification {
mirror::Object* ref,
bool fatal) const REQUIRES_SHARED(Locks::mutator_lock_);
-
// Return true if the klass is likely to be a valid mirror::Class.
bool IsValidClass(const void* klass) const REQUIRES_SHARED(Locks::mutator_lock_);
- // Does not allow null.
+ // Does not allow null, checks alignment.
bool IsValidHeapObjectAddress(const void* addr, space::Space** out_space = nullptr) const
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -62,6 +61,14 @@ class Verification {
std::string FirstPathFromRootSet(ObjPtr<mirror::Object> target) const
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Does not check alignment, used by DumpRAMAroundAddress.
+ bool IsAddressInHeapSpace(const void* addr, space::Space** out_space = nullptr) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Dump bytes of RAM before and after an address.
+ std::string DumpRAMAroundAddress(uintptr_t addr, uintptr_t bytes) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
private:
gc::Heap* const heap_;
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 45788e7617..0a2705d5f7 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -64,13 +64,22 @@ namespace interpreter {
}
// Code to run before each dex instruction.
-#define PREAMBLE() \
- do { \
- if (UNLIKELY(instrumentation->HasDexPcListeners())) { \
- instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), \
- shadow_frame.GetMethod(), dex_pc); \
+#define PREAMBLE_SAVE(save_ref) \
+ { \
+ if (UNLIKELY(instrumentation->HasDexPcListeners()) && \
+ UNLIKELY(!DoDexPcMoveEvent(self, \
+ code_item, \
+ shadow_frame, \
+ dex_pc, \
+ instrumentation, \
+ save_ref))) { \
+ HANDLE_PENDING_EXCEPTION(); \
+ break; \
} \
- } while (false)
+ } \
+ do {} while (false)
+
+#define PREAMBLE() PREAMBLE_SAVE(nullptr)
#define BRANCH_INSTRUMENTATION(offset) \
do { \
@@ -104,6 +113,43 @@ namespace interpreter {
} \
} while (false)
+// Unlike most other events the DexPcMovedEvent can be sent when there is a pending exception (if
+// the next instruction is MOVE_EXCEPTION). This means it needs to be handled carefully to be able
+// to detect exceptions thrown by the DexPcMovedEvent itself. These exceptions could be thrown by
+// jvmti-agents while handling breakpoint or single step events. We had to move this into its own
+// function because it was making ExecuteSwitchImpl have too large a stack.
+NO_INLINE static bool DoDexPcMoveEvent(Thread* self,
+ const DexFile::CodeItem* code_item,
+ const ShadowFrame& shadow_frame,
+ uint32_t dex_pc,
+ const instrumentation::Instrumentation* instrumentation,
+ JValue* save_ref)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(instrumentation->HasDexPcListeners());
+ StackHandleScope<2> hs(self);
+ Handle<mirror::Throwable> thr(hs.NewHandle(self->GetException()));
+ mirror::Object* null_obj = nullptr;
+ HandleWrapper<mirror::Object> h(
+ hs.NewHandleWrapper(LIKELY(save_ref == nullptr) ? &null_obj : save_ref->GetGCRoot()));
+ self->ClearException();
+ instrumentation->DexPcMovedEvent(self,
+ shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(),
+ dex_pc);
+ if (UNLIKELY(self->IsExceptionPending())) {
+ // We got a new exception in the dex-pc-moved event. We just let this exception replace the old
+ // one.
+ // TODO It would be good to add the old exception to the suppressed exceptions of the new one if
+ // possible.
+ return false;
+ } else {
+ if (UNLIKELY(!thr.IsNull())) {
+ self->SetException(thr.Get());
+ }
+ return true;
+ }
+}
+
template<bool do_access_check, bool transaction_active>
JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame, JValue result_register,
@@ -198,7 +244,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
inst = inst->Next_1xx();
break;
case Instruction::MOVE_RESULT_OBJECT:
- PREAMBLE();
+ PREAMBLE_SAVE(&result_register);
shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), result_register.GetL());
inst = inst->Next_1xx();
break;
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index b41bc78170..10dddaefc8 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -43,6 +43,33 @@ namespace art {
ProfileSaver* ProfileSaver::instance_ = nullptr;
pthread_t ProfileSaver::profiler_pthread_ = 0U;
+// At what priority to schedule the saver threads. 9 is the lowest foreground priority on device.
+static constexpr int kProfileSaverPthreadPriority = 9;
+
+static void SetProfileSaverThreadPriority(pthread_t thread, int priority) {
+#if defined(ART_TARGET_ANDROID)
+ int result = setpriority(PRIO_PROCESS, pthread_gettid_np(thread), priority);
+ if (result != 0) {
+ LOG(ERROR) << "Failed to setpriority to :" << priority;
+ }
+#else
+ UNUSED(thread);
+ UNUSED(priority);
+#endif
+}
+
+static int GetDefaultThreadPriority() {
+#if defined(ART_TARGET_ANDROID)
+ pthread_attr_t attr;
+ sched_param param;
+ pthread_attr_init(&attr);
+ pthread_attr_getschedparam(&attr, &param);
+ return param.sched_priority;
+#else
+ return 0;
+#endif
+}
+
ProfileSaver::ProfileSaver(const ProfileSaverOptions& options,
const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
@@ -241,6 +268,20 @@ class GetClassesAndMethodsVisitor : public ClassVisitor {
const bool profile_boot_class_path_;
};
+class ScopedDefaultPriority {
+ public:
+ explicit ScopedDefaultPriority(pthread_t thread) : thread_(thread) {
+ SetProfileSaverThreadPriority(thread_, GetDefaultThreadPriority());
+ }
+
+ ~ScopedDefaultPriority() {
+ SetProfileSaverThreadPriority(thread_, kProfileSaverPthreadPriority);
+ }
+
+ private:
+ const pthread_t thread_;
+};
+
void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() {
ScopedTrace trace(__PRETTY_FUNCTION__);
const uint64_t start_time = NanoTime();
@@ -257,7 +298,15 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() {
TypeReferenceCollection resolved_classes(allocator.Adapter(), allocator.Adapter());
const bool is_low_ram = Runtime::Current()->GetHeap()->IsLowMemoryMode();
const size_t hot_threshold = options_.GetHotStartupMethodSamples(is_low_ram);
+ pthread_t profiler_pthread;
{
+ MutexLock mu(self, *Locks::profiler_lock_);
+ profiler_pthread = profiler_pthread_;
+ }
+ {
+ // Restore profile saver thread priority during the GC critical section. This helps prevent
+ // priority inversions blocking the GC for long periods of time.
+ ScopedDefaultPriority sdp(profiler_pthread);
ScopedObjectAccess soa(self);
gc::ScopedGCCriticalSection sgcs(self,
gc::kGcCauseProfileSaver,
@@ -543,15 +592,7 @@ void ProfileSaver::Start(const ProfileSaverOptions& options,
(&profiler_pthread_, nullptr, &RunProfileSaverThread, reinterpret_cast<void*>(instance_)),
"Profile saver thread");
-#if defined(ART_TARGET_ANDROID)
- // At what priority to schedule the saver threads. 9 is the lowest foreground priority on device.
- static constexpr int kProfileSaverPthreadPriority = 9;
- int result = setpriority(
- PRIO_PROCESS, pthread_gettid_np(profiler_pthread_), kProfileSaverPthreadPriority);
- if (result != 0) {
- PLOG(ERROR) << "Failed to setpriority to :" << kProfileSaverPthreadPriority;
- }
-#endif
+ SetProfileSaverThreadPriority(profiler_pthread_, kProfileSaverPthreadPriority);
}
void ProfileSaver::Stop(bool dump_info) {
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp
index e38f265c5a..619a49aa71 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/runtime/openjdkjvmti/Android.bp
@@ -27,6 +27,7 @@ cc_defaults {
"fixed_up_dex_file.cc",
"object_tagging.cc",
"OpenjdkJvmTi.cc",
+ "ti_breakpoint.cc",
"ti_class.cc",
"ti_class_definition.cc",
"ti_class_loader.cc",
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 0896210f1c..e3768b358f 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -48,6 +48,7 @@
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
#include "thread_list.h"
+#include "ti_breakpoint.h"
#include "ti_class.h"
#include "ti_dump.h"
#include "ti_field.h"
@@ -619,20 +620,17 @@ class JvmtiFunctions {
return ERR(NOT_IMPLEMENTED);
}
- static jvmtiError SetBreakpoint(jvmtiEnv* env,
- jmethodID method ATTRIBUTE_UNUSED,
- jlocation location ATTRIBUTE_UNUSED) {
+
+ static jvmtiError SetBreakpoint(jvmtiEnv* env, jmethodID method, jlocation location) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_generate_breakpoint_events);
- return ERR(NOT_IMPLEMENTED);
+ return BreakpointUtil::SetBreakpoint(env, method, location);
}
- static jvmtiError ClearBreakpoint(jvmtiEnv* env,
- jmethodID method ATTRIBUTE_UNUSED,
- jlocation location ATTRIBUTE_UNUSED) {
+ static jvmtiError ClearBreakpoint(jvmtiEnv* env, jmethodID method, jlocation location) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_generate_breakpoint_events);
- return ERR(NOT_IMPLEMENTED);
+ return BreakpointUtil::ClearBreakpoint(env, method, location);
}
static jvmtiError SetFieldAccessWatch(jvmtiEnv* env, jclass klass, jfieldID field) {
diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h
index b5f12191e6..2d5d527e68 100644
--- a/runtime/openjdkjvmti/art_jvmti.h
+++ b/runtime/openjdkjvmti/art_jvmti.h
@@ -34,6 +34,7 @@
#include <memory>
#include <type_traits>
+#include <unordered_map>
#include <unordered_set>
#include <jni.h>
@@ -46,10 +47,12 @@
#include "java_vm_ext.h"
#include "jni_env_ext.h"
#include "jvmti.h"
+#include "ti_breakpoint.h"
namespace art {
class ArtField;
-}
+class ArtMethod;
+} // namespace art
namespace openjdkjvmti {
@@ -76,6 +79,9 @@ struct ArtJvmTiEnv : public jvmtiEnv {
std::unordered_set<art::ArtField*> access_watched_fields;
std::unordered_set<art::ArtField*> modify_watched_fields;
+ // Set of breakpoints is unique to each jvmtiEnv.
+ std::unordered_set<Breakpoint> breakpoints;
+
ArtJvmTiEnv(art::JavaVMExt* runtime, EventHandler* event_handler);
static ArtJvmTiEnv* AsArtJvmTiEnv(jvmtiEnv* env) {
@@ -223,10 +229,10 @@ const jvmtiCapabilities kPotentialCapabilities = {
.can_get_source_debug_extension = 1,
.can_access_local_variables = 0,
.can_maintain_original_method_order = 0,
- .can_generate_single_step_events = 0,
+ .can_generate_single_step_events = 1,
.can_generate_exception_events = 0,
.can_generate_frame_pop_events = 0,
- .can_generate_breakpoint_events = 0,
+ .can_generate_breakpoint_events = 1,
.can_suspend = 0,
.can_redefine_any_class = 0,
.can_get_current_thread_cpu_time = 0,
diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h
index af99233f90..f30d7cecb3 100644
--- a/runtime/openjdkjvmti/events-inl.h
+++ b/runtime/openjdkjvmti/events-inl.h
@@ -22,6 +22,7 @@
#include "events.h"
#include "jni_internal.h"
#include "ScopedLocalRef.h"
+#include "ti_breakpoint.h"
#include "art_jvmti.h"
@@ -217,6 +218,32 @@ inline void EventHandler::DispatchEvent(ArtJvmTiEnv* env, art::Thread* thread, A
}
}
+// Need to give custom specializations for Breakpoint since it needs to filter out which particular
+// methods/dex_pcs agents get notified on.
+template <>
+inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kBreakpoint>(art::Thread* thread,
+ JNIEnv* jnienv,
+ jthread jni_thread,
+ jmethodID jmethod,
+ jlocation location) const {
+ art::ArtMethod* method = art::jni::DecodeArtMethod(jmethod);
+ for (ArtJvmTiEnv* env : envs) {
+ // Search for a breakpoint on this particular method and location.
+ if (env != nullptr &&
+ ShouldDispatch<ArtJvmtiEvent::kBreakpoint>(env, thread) &&
+ env->breakpoints.find({method, location}) != env->breakpoints.end()) {
+ // We temporarily clear any pending exceptions so the event can call back into java code.
+ ScopedLocalRef<jthrowable> thr(jnienv, jnienv->ExceptionOccurred());
+ jnienv->ExceptionClear();
+ auto callback = impl::GetCallback<ArtJvmtiEvent::kBreakpoint>(env);
+ (*callback)(env, jnienv, jni_thread, jmethod, location);
+ if (thr.get() != nullptr && !jnienv->ExceptionCheck()) {
+ jnienv->Throw(thr.get());
+ }
+ }
+ }
+}
+
// Need to give custom specializations for FieldAccess and FieldModification since they need to
// filter out which particular fields agents want to get notified on.
// TODO The spec allows us to do shortcuts like only allow one agent to ever set these watches. This
diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc
index 989b9af591..f749daa918 100644
--- a/runtime/openjdkjvmti/events.cc
+++ b/runtime/openjdkjvmti/events.cc
@@ -423,14 +423,30 @@ class JvmtiMethodTraceListener FINAL : public art::instrumentation::Instrumentat
}
}
- // Call-back for when the dex pc moves in a method. We don't currently have any events associated
- // with this.
- void DexPcMoved(art::Thread* self ATTRIBUTE_UNUSED,
+ // Call-back for when the dex pc moves in a method.
+ void DexPcMoved(art::Thread* self,
art::Handle<art::mirror::Object> this_object ATTRIBUTE_UNUSED,
- art::ArtMethod* method ATTRIBUTE_UNUSED,
- uint32_t new_dex_pc ATTRIBUTE_UNUSED)
+ art::ArtMethod* method,
+ uint32_t new_dex_pc)
REQUIRES_SHARED(art::Locks::mutator_lock_) OVERRIDE {
- return;
+ DCHECK(!method->IsRuntimeMethod());
+ // Default methods might be copied to multiple classes. We need to get the canonical version of
+ // this method so that we can check for breakpoints correctly.
+ // TODO We should maybe do this on other events to ensure that we are consistent WRT default
+ // methods. This could interact with obsolete methods if we ever let interface redefinition
+ // happen though.
+ method = method->GetCanonicalMethod();
+ art::JNIEnvExt* jnienv = self->GetJniEnv();
+ jmethodID jmethod = art::jni::EncodeArtMethod(method);
+ jlocation location = static_cast<jlocation>(new_dex_pc);
+ // Step event is reported first according to the spec.
+ if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kSingleStep)) {
+ RunEventCallback<ArtJvmtiEvent::kSingleStep>(self, jnienv, jmethod, location);
+ }
+ // Next we do the Breakpoint events. The Dispatch code will filter the individual
+ if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kBreakpoint)) {
+ RunEventCallback<ArtJvmtiEvent::kBreakpoint>(self, jnienv, jmethod, location);
+ }
}
// Call-back for when we read from a field.
@@ -563,6 +579,9 @@ static uint32_t GetInstrumentationEventsFor(ArtJvmtiEvent event) {
return art::instrumentation::Instrumentation::kFieldWritten;
case ArtJvmtiEvent::kFieldAccess:
return art::instrumentation::Instrumentation::kFieldRead;
+ case ArtJvmtiEvent::kBreakpoint:
+ case ArtJvmtiEvent::kSingleStep:
+ return art::instrumentation::Instrumentation::kDexPcMoved;
default:
LOG(FATAL) << "Unknown event ";
return 0;
@@ -580,6 +599,8 @@ static void SetupTraceListener(JvmtiMethodTraceListener* listener,
art::gc::kCollectorTypeInstrumentation);
art::ScopedSuspendAll ssa("jvmti method tracing installation");
if (enable) {
+ // TODO Depending on the features being used we should be able to avoid deoptimizing everything
+ // like we do here.
if (!instr->AreAllMethodsDeoptimized()) {
instr->EnableMethodTracing("jvmti-tracing", /*needs_interpreter*/true);
}
@@ -601,6 +622,17 @@ void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) {
SetupGcPauseTracking(gc_pause_listener_.get(), event, enable);
return;
+ case ArtJvmtiEvent::kBreakpoint:
+ case ArtJvmtiEvent::kSingleStep: {
+ ArtJvmtiEvent other = (event == ArtJvmtiEvent::kBreakpoint) ? ArtJvmtiEvent::kSingleStep
+ : ArtJvmtiEvent::kBreakpoint;
+ // We only need to do anything if there isn't already a listener installed/held-on by the
+ // other jvmti event that uses DexPcMoved.
+ if (!IsEventEnabledAnywhere(other)) {
+ SetupTraceListener(method_trace_listener_.get(), event, enable);
+ }
+ return;
+ }
case ArtJvmtiEvent::kMethodEntry:
case ArtJvmtiEvent::kMethodExit:
case ArtJvmtiEvent::kFieldAccess:
diff --git a/runtime/openjdkjvmti/ti_breakpoint.cc b/runtime/openjdkjvmti/ti_breakpoint.cc
new file mode 100644
index 0000000000..6d0e2c60c1
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_breakpoint.cc
@@ -0,0 +1,114 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <functional>
+
+#include "ti_breakpoint.h"
+
+#include "art_jvmti.h"
+#include "art_method-inl.h"
+#include "base/enums.h"
+#include "dex_file_annotations.h"
+#include "events-inl.h"
+#include "jni_internal.h"
+#include "mirror/class-inl.h"
+#include "mirror/object_array-inl.h"
+#include "modifiers.h"
+#include "runtime_callbacks.h"
+#include "scoped_thread_state_change-inl.h"
+#include "ScopedLocalRef.h"
+#include "thread-current-inl.h"
+#include "thread_list.h"
+#include "ti_phase.h"
+
+namespace openjdkjvmti {
+
+size_t Breakpoint::hash() const {
+ return std::hash<uintptr_t> {}(reinterpret_cast<uintptr_t>(method_))
+ ^ std::hash<jlocation> {}(location_);
+}
+
+Breakpoint::Breakpoint(art::ArtMethod* m, jlocation loc) : method_(m), location_(loc) {
+ DCHECK(!m->IsDefault() || !m->IsCopied() || !m->IsInvokable())
+ << "Flags are: 0x" << std::hex << m->GetAccessFlags();
+}
+
+void BreakpointUtil::RemoveBreakpointsInClass(ArtJvmTiEnv* env, art::mirror::Class* klass) {
+ std::vector<Breakpoint> to_remove;
+ for (const Breakpoint& b : env->breakpoints) {
+ if (b.GetMethod()->GetDeclaringClass() == klass) {
+ to_remove.push_back(b);
+ }
+ }
+ for (const Breakpoint& b : to_remove) {
+ auto it = env->breakpoints.find(b);
+ DCHECK(it != env->breakpoints.end());
+ env->breakpoints.erase(it);
+ }
+}
+
+jvmtiError BreakpointUtil::SetBreakpoint(jvmtiEnv* jenv, jmethodID method, jlocation location) {
+ ArtJvmTiEnv* env = ArtJvmTiEnv::AsArtJvmTiEnv(jenv);
+ if (method == nullptr) {
+ return ERR(INVALID_METHODID);
+ }
+ // Need to get mutator_lock_ so we can find the interface version of any default methods.
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ArtMethod* art_method = art::jni::DecodeArtMethod(method)->GetCanonicalMethod();
+ if (location < 0 || static_cast<uint32_t>(location) >=
+ art_method->GetCodeItem()->insns_size_in_code_units_) {
+ return ERR(INVALID_LOCATION);
+ }
+ auto res_pair = env->breakpoints.insert(/* Breakpoint */ {art_method, location});
+ if (!res_pair.second) {
+ // Didn't get inserted because it's already present!
+ return ERR(DUPLICATE);
+ }
+ return OK;
+}
+
+jvmtiError BreakpointUtil::ClearBreakpoint(jvmtiEnv* jenv, jmethodID method, jlocation location) {
+ ArtJvmTiEnv* env = ArtJvmTiEnv::AsArtJvmTiEnv(jenv);
+ if (method == nullptr) {
+ return ERR(INVALID_METHODID);
+ }
+ // Need to get mutator_lock_ so we can find the interface version of any default methods.
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ auto pos = env->breakpoints.find(
+ /* Breakpoint */ {art::jni::DecodeArtMethod(method)->GetCanonicalMethod(), location});
+ if (pos == env->breakpoints.end()) {
+ return ERR(NOT_FOUND);
+ }
+ env->breakpoints.erase(pos);
+ return OK;
+}
+
+} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_breakpoint.h b/runtime/openjdkjvmti/ti_breakpoint.h
new file mode 100644
index 0000000000..c3dbef7baf
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_breakpoint.h
@@ -0,0 +1,94 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_BREAKPOINT_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_BREAKPOINT_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+#include "base/mutex.h"
+
+namespace art {
+class ArtMethod;
+namespace mirror {
+class Class;
+} // namespace mirror
+} // namespace art
+
+namespace openjdkjvmti {
+
+struct ArtJvmTiEnv;
+
+class Breakpoint {
+ public:
+ Breakpoint(art::ArtMethod* m, jlocation loc);
+
+ // Get the hash code of this breakpoint.
+ size_t hash() const;
+
+ bool operator==(const Breakpoint& other) const {
+ return method_ == other.method_ && location_ == other.location_;
+ }
+
+ art::ArtMethod* GetMethod() const {
+ return method_;
+ }
+
+ jlocation GetLocation() const {
+ return location_;
+ }
+
+ private:
+ art::ArtMethod* method_;
+ jlocation location_;
+};
+
+class BreakpointUtil {
+ public:
+ static jvmtiError SetBreakpoint(jvmtiEnv* env, jmethodID method, jlocation location);
+ static jvmtiError ClearBreakpoint(jvmtiEnv* env, jmethodID method, jlocation location);
+ // Used by class redefinition to remove breakpoints on redefined classes.
+ static void RemoveBreakpointsInClass(ArtJvmTiEnv* env, art::mirror::Class* klass)
+ REQUIRES(art::Locks::mutator_lock_);
+};
+
+} // namespace openjdkjvmti
+
+namespace std {
+template<> struct hash<openjdkjvmti::Breakpoint> {
+ size_t operator()(const openjdkjvmti::Breakpoint& b) const {
+ return b.hash();
+ }
+};
+
+} // namespace std
+#endif // ART_RUNTIME_OPENJDKJVMTI_TI_BREAKPOINT_H_
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index 5422f48664..debee913ee 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -64,6 +64,7 @@
#include "object_lock.h"
#include "runtime.h"
#include "ScopedLocalRef.h"
+#include "ti_breakpoint.h"
#include "ti_class_loader.h"
#include "transform.h"
#include "verifier/method_verifier.h"
@@ -380,7 +381,7 @@ jvmtiError Redefiner::RedefineClassesDirect(ArtJvmTiEnv* env,
art::jit::ScopedJitSuspend suspend_jit;
// Get shared mutator lock so we can lock all the classes.
art::ScopedObjectAccess soa(self);
- Redefiner r(runtime, self, error_msg);
+ Redefiner r(env, runtime, self, error_msg);
for (const ArtClassDefinition& def : definitions) {
// Only try to transform classes that have been modified.
if (def.IsModified()) {
@@ -1200,6 +1201,10 @@ bool Redefiner::ClassRedefinition::FinishRemainingAllocations(
return true;
}
+void Redefiner::ClassRedefinition::UnregisterJvmtiBreakpoints() {
+ BreakpointUtil::RemoveBreakpointsInClass(driver_->env_, GetMirrorClass());
+}
+
void Redefiner::ClassRedefinition::UnregisterBreakpoints() {
DCHECK(art::Dbg::IsDebuggerActive());
art::JDWP::JdwpState* state = art::Dbg::GetJdwpState();
@@ -1342,6 +1347,7 @@ jvmtiError Redefiner::Run() {
// TODO Rewrite so we don't do a stack walk for each and every class.
redef.FindAndAllocateObsoleteMethods(klass);
redef.UpdateClass(klass, data.GetNewDexCache(), data.GetOriginalDexFile());
+ redef.UnregisterJvmtiBreakpoints();
}
RestoreObsoleteMethodMapsIfUnneeded(holder);
// TODO We should check for if any of the redefined methods are intrinsic methods here and, if any
diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h
index ec4a8b2789..27d7c3d726 100644
--- a/runtime/openjdkjvmti/ti_redefine.h
+++ b/runtime/openjdkjvmti/ti_redefine.h
@@ -199,6 +199,8 @@ class Redefiner {
void ReleaseDexFile() REQUIRES_SHARED(art::Locks::mutator_lock_);
void UnregisterBreakpoints() REQUIRES_SHARED(art::Locks::mutator_lock_);
+ // This should be done with all threads suspended.
+ void UnregisterJvmtiBreakpoints() REQUIRES(art::Locks::mutator_lock_);
private:
Redefiner* driver_;
@@ -208,6 +210,7 @@ class Redefiner {
art::ArrayRef<const unsigned char> original_dex_file_;
};
+ ArtJvmTiEnv* env_;
jvmtiError result_;
art::Runtime* runtime_;
art::Thread* self_;
@@ -216,10 +219,12 @@ class Redefiner {
// mirror::Class difficult and confusing.
std::string* error_msg_;
- Redefiner(art::Runtime* runtime,
+ Redefiner(ArtJvmTiEnv* env,
+ art::Runtime* runtime,
art::Thread* self,
std::string* error_msg)
- : result_(ERR(INTERNAL)),
+ : env_(env),
+ result_(ERR(INTERNAL)),
runtime_(runtime),
self_(self),
redefinitions_(),
diff --git a/runtime/thread.cc b/runtime/thread.cc
index be1614b3cc..5edd071675 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -549,27 +549,40 @@ void Thread::InstallImplicitProtection() {
//
// We map in the stack by reading every page from the stack bottom (highest address)
// to the stack top. (We then madvise this away.) This must be done by reading from the
- // current stack pointer downwards. Any access more than a page below the current SP
- // might cause a segv.
- // TODO: This comment may be out of date. It seems possible to speed this up. As
- // this is normally done once in the zygote on startup, ignore for now.
+ // current stack pointer downwards.
//
- // AddressSanitizer does not like the part of this functions that reads every stack page.
- // Looks a lot like an out-of-bounds access.
+ // Accesses too far below the current machine register corresponding to the stack pointer (e.g.,
+ // ESP on x86[-32], SP on ARM) might cause a SIGSEGV (at least on x86 with newer kernels). We
+ // thus have to move the stack pointer. We do this portably by using a recursive function with a
+ // large stack frame size.
- // (Defensively) first remove the protection on the protected region as will want to read
+ // (Defensively) first remove the protection on the protected region as we'll want to read
// and write it. Ignore errors.
UnprotectStack();
VLOG(threads) << "Need to map in stack for thread at " << std::hex <<
static_cast<void*>(pregion);
- // Read every page from the high address to the low.
- volatile uint8_t dont_optimize_this;
- UNUSED(dont_optimize_this);
- for (uint8_t* p = stack_top; p >= pregion; p -= kPageSize) {
- dont_optimize_this = *p;
- }
+ struct RecurseDownStack {
+ // This function has an intentionally large stack size.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wframe-larger-than="
+ NO_INLINE
+ static void Touch(uintptr_t target) {
+ volatile size_t zero = 0;
+ // Use a large local volatile array to ensure a large frame size. Do not use anything close
+ // to a full page for ASAN. It would be nice to ensure the frame size is at most a page, but
+ // there is no pragma support for this.
+ volatile char space[kPageSize - 256];
+ char sink ATTRIBUTE_UNUSED = space[zero];
+ if (reinterpret_cast<uintptr_t>(space) >= target + kPageSize) {
+ Touch(target);
+ }
+ zero *= 2; // Try to avoid tail recursion.
+ }
+#pragma GCC diagnostic pop
+ };
+ RecurseDownStack::Touch(reinterpret_cast<uintptr_t>(pregion));
VLOG(threads) << "(again) installing stack protected region at " << std::hex <<
static_cast<void*>(pregion) << " to " <<
diff --git a/test/442-checker-constant-folding/build b/test/442-checker-constant-folding/build
new file mode 100755
index 0000000000..49292c9ac1
--- /dev/null
+++ b/test/442-checker-constant-folding/build
@@ -0,0 +1,23 @@
+#!/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.
+
+# This checker test is incompatible with jack bytecode output,
+# so force it to use javac/dx.
+export USE_JACK=false
+# Also disable desugar because it is missing in jack platform builds.
+export DESUGAR=false
+
+./default-build "$@"
diff --git a/test/442-checker-constant-folding/src/Main.java b/test/442-checker-constant-folding/src/Main.java
index 64180d5273..eba5137f6f 100644
--- a/test/442-checker-constant-folding/src/Main.java
+++ b/test/442-checker-constant-folding/src/Main.java
@@ -27,12 +27,6 @@ public class Main {
}
}
- public static void assertTrue(boolean condition) {
- if (!condition) {
- throw new Error();
- }
- }
-
public static void assertIntEquals(int expected, int result) {
if (expected != result) {
throw new Error("Expected: " + expected + ", found: " + result);
@@ -57,21 +51,6 @@ public class Main {
}
}
- private static int $inline$int(int x) {
- return x;
- }
-
- private static long $inline$long(long x) {
- return x;
- }
-
- private static float $inline$float(float x) {
- return x;
- }
-
- private static double $inline$double(double x) {
- return x;
- }
// Wrappers around methods located in file TestCmp.smali.
@@ -215,119 +194,121 @@ public class Main {
return y;
}
+
/**
* Exercise constant folding on addition.
*/
- /// CHECK-START: int Main.IntAddition1() constant_folding$after_inlining (before)
+ /// CHECK-START: int Main.IntAddition1() constant_folding (before)
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
/// CHECK-DAG: <<Add:i\d+>> Add [<<Const1>>,<<Const2>>]
/// CHECK-DAG: Return [<<Add>>]
- /// CHECK-START: int Main.IntAddition1() constant_folding$after_inlining (after)
+ /// CHECK-START: int Main.IntAddition1() constant_folding (after)
/// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
/// CHECK-DAG: Return [<<Const3>>]
- /// CHECK-START: int Main.IntAddition1() constant_folding$after_inlining (after)
+ /// CHECK-START: int Main.IntAddition1() constant_folding (after)
/// CHECK-NOT: Add
public static int IntAddition1() {
int a, b, c;
- a = $inline$int(1);
- b = $inline$int(2);
+ a = 1;
+ b = 2;
c = a + b;
return c;
}
- /// CHECK-START: int Main.IntAddition2() constant_folding$after_inlining (before)
+ /// CHECK-START: int Main.IntAddition2() constant_folding (before)
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
/// CHECK-DAG: <<Const5:i\d+>> IntConstant 5
/// CHECK-DAG: <<Const6:i\d+>> IntConstant 6
+ /// CHECK-DAG: <<Const11:i\d+>> IntConstant 11
/// CHECK-DAG: <<Add1:i\d+>> Add [<<Const1>>,<<Const2>>]
- /// CHECK-DAG: <<Add2:i\d+>> Add [<<Const5>>,<<Const6>>]
- /// CHECK-DAG: <<Add3:i\d+>> Add [<<Add1>>,<<Add2>>]
+ /// CHECK-DAG: Add [<<Const5>>,<<Const6>>]
+ /// CHECK-DAG: <<Add3:i\d+>> Add [<<Add1>>,<<Const11>>]
/// CHECK-DAG: Return [<<Add3>>]
- /// CHECK-START: int Main.IntAddition2() constant_folding$after_inlining (after)
+ /// CHECK-START: int Main.IntAddition2() constant_folding (after)
/// CHECK-DAG: <<Const14:i\d+>> IntConstant 14
/// CHECK-DAG: Return [<<Const14>>]
- /// CHECK-START: int Main.IntAddition2() constant_folding$after_inlining (after)
+ /// CHECK-START: int Main.IntAddition2() constant_folding (after)
/// CHECK-NOT: Add
public static int IntAddition2() {
int a, b, c;
- a = $inline$int(1);
- b = $inline$int(2);
+ a = 1;
+ b = 2;
a += b;
- b = $inline$int(5);
- c = $inline$int(6);
+ b = 5;
+ c = 6;
b += c;
c = a + b;
return c;
}
- /// CHECK-START: long Main.LongAddition() constant_folding$after_inlining (before)
+ /// CHECK-START: long Main.LongAddition() constant_folding (before)
/// CHECK-DAG: <<Const1:j\d+>> LongConstant 1
/// CHECK-DAG: <<Const2:j\d+>> LongConstant 2
/// CHECK-DAG: <<Add:j\d+>> Add [<<Const1>>,<<Const2>>]
/// CHECK-DAG: Return [<<Add>>]
- /// CHECK-START: long Main.LongAddition() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.LongAddition() constant_folding (after)
/// CHECK-DAG: <<Const3:j\d+>> LongConstant 3
/// CHECK-DAG: Return [<<Const3>>]
- /// CHECK-START: long Main.LongAddition() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.LongAddition() constant_folding (after)
/// CHECK-NOT: Add
public static long LongAddition() {
long a, b, c;
- a = $inline$long(1L);
- b = $inline$long(2L);
+ a = 1L;
+ b = 2L;
c = a + b;
return c;
}
- /// CHECK-START: float Main.FloatAddition() constant_folding$after_inlining (before)
+ /// CHECK-START: float Main.FloatAddition() constant_folding (before)
/// CHECK-DAG: <<Const1:f\d+>> FloatConstant 1
/// CHECK-DAG: <<Const2:f\d+>> FloatConstant 2
/// CHECK-DAG: <<Add:f\d+>> Add [<<Const1>>,<<Const2>>]
/// CHECK-DAG: Return [<<Add>>]
- /// CHECK-START: float Main.FloatAddition() constant_folding$after_inlining (after)
+ /// CHECK-START: float Main.FloatAddition() constant_folding (after)
/// CHECK-DAG: <<Const3:f\d+>> FloatConstant 3
/// CHECK-DAG: Return [<<Const3>>]
- /// CHECK-START: float Main.FloatAddition() constant_folding$after_inlining (after)
+ /// CHECK-START: float Main.FloatAddition() constant_folding (after)
/// CHECK-NOT: Add
public static float FloatAddition() {
float a, b, c;
- a = $inline$float(1F);
- b = $inline$float(2F);
+ a = 1F;
+ b = 2F;
c = a + b;
return c;
}
- /// CHECK-START: double Main.DoubleAddition() constant_folding$after_inlining (before)
+ /// CHECK-START: double Main.DoubleAddition() constant_folding (before)
/// CHECK-DAG: <<Const1:d\d+>> DoubleConstant 1
/// CHECK-DAG: <<Const2:d\d+>> DoubleConstant 2
/// CHECK-DAG: <<Add:d\d+>> Add [<<Const1>>,<<Const2>>]
/// CHECK-DAG: Return [<<Add>>]
- /// CHECK-START: double Main.DoubleAddition() constant_folding$after_inlining (after)
+ /// CHECK-START: double Main.DoubleAddition() constant_folding (after)
/// CHECK-DAG: <<Const3:d\d+>> DoubleConstant 3
/// CHECK-DAG: Return [<<Const3>>]
- /// CHECK-START: double Main.DoubleAddition() constant_folding$after_inlining (after)
+ /// CHECK-START: double Main.DoubleAddition() constant_folding (after)
/// CHECK-NOT: Add
public static double DoubleAddition() {
double a, b, c;
- a = $inline$double(1D);
- b = $inline$double(2D);
+ a = 1D;
+ b = 2D;
c = a + b;
return c;
}
@@ -337,86 +318,86 @@ public class Main {
* Exercise constant folding on subtraction.
*/
- /// CHECK-START: int Main.IntSubtraction() constant_folding$after_inlining (before)
+ /// CHECK-START: int Main.IntSubtraction() constant_folding (before)
/// CHECK-DAG: <<Const6:i\d+>> IntConstant 6
/// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
/// CHECK-DAG: <<Sub:i\d+>> Sub [<<Const6>>,<<Const2>>]
/// CHECK-DAG: Return [<<Sub>>]
- /// CHECK-START: int Main.IntSubtraction() constant_folding$after_inlining (after)
+ /// CHECK-START: int Main.IntSubtraction() constant_folding (after)
/// CHECK-DAG: <<Const4:i\d+>> IntConstant 4
/// CHECK-DAG: Return [<<Const4>>]
- /// CHECK-START: int Main.IntSubtraction() constant_folding$after_inlining (after)
+ /// CHECK-START: int Main.IntSubtraction() constant_folding (after)
/// CHECK-NOT: Sub
public static int IntSubtraction() {
int a, b, c;
- a = $inline$int(6);
- b = $inline$int(2);
+ a = 6;
+ b = 2;
c = a - b;
return c;
}
- /// CHECK-START: long Main.LongSubtraction() constant_folding$after_inlining (before)
+ /// CHECK-START: long Main.LongSubtraction() constant_folding (before)
/// CHECK-DAG: <<Const6:j\d+>> LongConstant 6
/// CHECK-DAG: <<Const2:j\d+>> LongConstant 2
/// CHECK-DAG: <<Sub:j\d+>> Sub [<<Const6>>,<<Const2>>]
/// CHECK-DAG: Return [<<Sub>>]
- /// CHECK-START: long Main.LongSubtraction() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.LongSubtraction() constant_folding (after)
/// CHECK-DAG: <<Const4:j\d+>> LongConstant 4
/// CHECK-DAG: Return [<<Const4>>]
- /// CHECK-START: long Main.LongSubtraction() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.LongSubtraction() constant_folding (after)
/// CHECK-NOT: Sub
public static long LongSubtraction() {
long a, b, c;
- a = $inline$long(6L);
- b = $inline$long(2L);
+ a = 6L;
+ b = 2L;
c = a - b;
return c;
}
- /// CHECK-START: float Main.FloatSubtraction() constant_folding$after_inlining (before)
+ /// CHECK-START: float Main.FloatSubtraction() constant_folding (before)
/// CHECK-DAG: <<Const6:f\d+>> FloatConstant 6
/// CHECK-DAG: <<Const2:f\d+>> FloatConstant 2
/// CHECK-DAG: <<Sub:f\d+>> Sub [<<Const6>>,<<Const2>>]
/// CHECK-DAG: Return [<<Sub>>]
- /// CHECK-START: float Main.FloatSubtraction() constant_folding$after_inlining (after)
+ /// CHECK-START: float Main.FloatSubtraction() constant_folding (after)
/// CHECK-DAG: <<Const4:f\d+>> FloatConstant 4
/// CHECK-DAG: Return [<<Const4>>]
- /// CHECK-START: float Main.FloatSubtraction() constant_folding$after_inlining (after)
+ /// CHECK-START: float Main.FloatSubtraction() constant_folding (after)
/// CHECK-NOT: Sub
public static float FloatSubtraction() {
float a, b, c;
- a = $inline$float(6F);
- b = $inline$float(2F);
+ a = 6F;
+ b = 2F;
c = a - b;
return c;
}
- /// CHECK-START: double Main.DoubleSubtraction() constant_folding$after_inlining (before)
+ /// CHECK-START: double Main.DoubleSubtraction() constant_folding (before)
/// CHECK-DAG: <<Const6:d\d+>> DoubleConstant 6
/// CHECK-DAG: <<Const2:d\d+>> DoubleConstant 2
/// CHECK-DAG: <<Sub:d\d+>> Sub [<<Const6>>,<<Const2>>]
/// CHECK-DAG: Return [<<Sub>>]
- /// CHECK-START: double Main.DoubleSubtraction() constant_folding$after_inlining (after)
+ /// CHECK-START: double Main.DoubleSubtraction() constant_folding (after)
/// CHECK-DAG: <<Const4:d\d+>> DoubleConstant 4
/// CHECK-DAG: Return [<<Const4>>]
- /// CHECK-START: double Main.DoubleSubtraction() constant_folding$after_inlining (after)
+ /// CHECK-START: double Main.DoubleSubtraction() constant_folding (after)
/// CHECK-NOT: Sub
public static double DoubleSubtraction() {
double a, b, c;
- a = $inline$double(6D);
- b = $inline$double(2D);
+ a = 6D;
+ b = 2D;
c = a - b;
return c;
}
@@ -426,86 +407,86 @@ public class Main {
* Exercise constant folding on multiplication.
*/
- /// CHECK-START: int Main.IntMultiplication() constant_folding$after_inlining (before)
+ /// CHECK-START: int Main.IntMultiplication() constant_folding (before)
/// CHECK-DAG: <<Const7:i\d+>> IntConstant 7
/// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
/// CHECK-DAG: <<Mul:i\d+>> Mul [<<Const7>>,<<Const3>>]
/// CHECK-DAG: Return [<<Mul>>]
- /// CHECK-START: int Main.IntMultiplication() constant_folding$after_inlining (after)
+ /// CHECK-START: int Main.IntMultiplication() constant_folding (after)
/// CHECK-DAG: <<Const21:i\d+>> IntConstant 21
/// CHECK-DAG: Return [<<Const21>>]
- /// CHECK-START: int Main.IntMultiplication() constant_folding$after_inlining (after)
+ /// CHECK-START: int Main.IntMultiplication() constant_folding (after)
/// CHECK-NOT: Mul
public static int IntMultiplication() {
int a, b, c;
- a = $inline$int(7);
- b = $inline$int(3);
+ a = 7;
+ b = 3;
c = a * b;
return c;
}
- /// CHECK-START: long Main.LongMultiplication() constant_folding$after_inlining (before)
+ /// CHECK-START: long Main.LongMultiplication() constant_folding (before)
/// CHECK-DAG: <<Const7:j\d+>> LongConstant 7
/// CHECK-DAG: <<Const3:j\d+>> LongConstant 3
/// CHECK-DAG: <<Mul:j\d+>> Mul [<<Const7>>,<<Const3>>]
/// CHECK-DAG: Return [<<Mul>>]
- /// CHECK-START: long Main.LongMultiplication() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.LongMultiplication() constant_folding (after)
/// CHECK-DAG: <<Const21:j\d+>> LongConstant 21
/// CHECK-DAG: Return [<<Const21>>]
- /// CHECK-START: long Main.LongMultiplication() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.LongMultiplication() constant_folding (after)
/// CHECK-NOT: Mul
public static long LongMultiplication() {
long a, b, c;
- a = $inline$long(7L);
- b = $inline$long(3L);
+ a = 7L;
+ b = 3L;
c = a * b;
return c;
}
- /// CHECK-START: float Main.FloatMultiplication() constant_folding$after_inlining (before)
+ /// CHECK-START: float Main.FloatMultiplication() constant_folding (before)
/// CHECK-DAG: <<Const7:f\d+>> FloatConstant 7
/// CHECK-DAG: <<Const3:f\d+>> FloatConstant 3
/// CHECK-DAG: <<Mul:f\d+>> Mul [<<Const7>>,<<Const3>>]
/// CHECK-DAG: Return [<<Mul>>]
- /// CHECK-START: float Main.FloatMultiplication() constant_folding$after_inlining (after)
+ /// CHECK-START: float Main.FloatMultiplication() constant_folding (after)
/// CHECK-DAG: <<Const21:f\d+>> FloatConstant 21
/// CHECK-DAG: Return [<<Const21>>]
- /// CHECK-START: float Main.FloatMultiplication() constant_folding$after_inlining (after)
+ /// CHECK-START: float Main.FloatMultiplication() constant_folding (after)
/// CHECK-NOT: Mul
public static float FloatMultiplication() {
float a, b, c;
- a = $inline$float(7F);
- b = $inline$float(3F);
+ a = 7F;
+ b = 3F;
c = a * b;
return c;
}
- /// CHECK-START: double Main.DoubleMultiplication() constant_folding$after_inlining (before)
+ /// CHECK-START: double Main.DoubleMultiplication() constant_folding (before)
/// CHECK-DAG: <<Const7:d\d+>> DoubleConstant 7
/// CHECK-DAG: <<Const3:d\d+>> DoubleConstant 3
/// CHECK-DAG: <<Mul:d\d+>> Mul [<<Const7>>,<<Const3>>]
/// CHECK-DAG: Return [<<Mul>>]
- /// CHECK-START: double Main.DoubleMultiplication() constant_folding$after_inlining (after)
+ /// CHECK-START: double Main.DoubleMultiplication() constant_folding (after)
/// CHECK-DAG: <<Const21:d\d+>> DoubleConstant 21
/// CHECK-DAG: Return [<<Const21>>]
- /// CHECK-START: double Main.DoubleMultiplication() constant_folding$after_inlining (after)
+ /// CHECK-START: double Main.DoubleMultiplication() constant_folding (after)
/// CHECK-NOT: Mul
public static double DoubleMultiplication() {
double a, b, c;
- a = $inline$double(7D);
- b = $inline$double(3D);
+ a = 7D;
+ b = 3D;
c = a * b;
return c;
}
@@ -515,90 +496,90 @@ public class Main {
* Exercise constant folding on division.
*/
- /// CHECK-START: int Main.IntDivision() constant_folding$after_inlining (before)
+ /// CHECK-START: int Main.IntDivision() constant_folding (before)
/// CHECK-DAG: <<Const8:i\d+>> IntConstant 8
/// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
/// CHECK-DAG: <<Div0Chk:i\d+>> DivZeroCheck [<<Const3>>]
/// CHECK-DAG: <<Div:i\d+>> Div [<<Const8>>,<<Div0Chk>>]
/// CHECK-DAG: Return [<<Div>>]
- /// CHECK-START: int Main.IntDivision() constant_folding$after_inlining (after)
+ /// CHECK-START: int Main.IntDivision() constant_folding (after)
/// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
/// CHECK-DAG: Return [<<Const2>>]
- /// CHECK-START: int Main.IntDivision() constant_folding$after_inlining (after)
+ /// CHECK-START: int Main.IntDivision() constant_folding (after)
/// CHECK-NOT: DivZeroCheck
/// CHECK-NOT: Div
public static int IntDivision() {
int a, b, c;
- a = $inline$int(8);
- b = $inline$int(3);
+ a = 8;
+ b = 3;
c = a / b;
return c;
}
- /// CHECK-START: long Main.LongDivision() constant_folding$after_inlining (before)
+ /// CHECK-START: long Main.LongDivision() constant_folding (before)
/// CHECK-DAG: <<Const8:j\d+>> LongConstant 8
/// CHECK-DAG: <<Const3:j\d+>> LongConstant 3
/// CHECK-DAG: <<Div0Chk:j\d+>> DivZeroCheck [<<Const3>>]
/// CHECK-DAG: <<Div:j\d+>> Div [<<Const8>>,<<Div0Chk>>]
/// CHECK-DAG: Return [<<Div>>]
- /// CHECK-START: long Main.LongDivision() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.LongDivision() constant_folding (after)
/// CHECK-DAG: <<Const2:j\d+>> LongConstant 2
/// CHECK-DAG: Return [<<Const2>>]
- /// CHECK-START: long Main.LongDivision() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.LongDivision() constant_folding (after)
/// CHECK-NOT: DivZeroCheck
/// CHECK-NOT: Div
public static long LongDivision() {
long a, b, c;
- a = $inline$long(8L);
- b = $inline$long(3L);
+ a = 8L;
+ b = 3L;
c = a / b;
return c;
}
- /// CHECK-START: float Main.FloatDivision() constant_folding$after_inlining (before)
+ /// CHECK-START: float Main.FloatDivision() constant_folding (before)
/// CHECK-DAG: <<Const8:f\d+>> FloatConstant 8
/// CHECK-DAG: <<Const2P5:f\d+>> FloatConstant 2.5
/// CHECK-DAG: <<Div:f\d+>> Div [<<Const8>>,<<Const2P5>>]
/// CHECK-DAG: Return [<<Div>>]
- /// CHECK-START: float Main.FloatDivision() constant_folding$after_inlining (after)
+ /// CHECK-START: float Main.FloatDivision() constant_folding (after)
/// CHECK-DAG: <<Const3P2:f\d+>> FloatConstant 3.2
/// CHECK-DAG: Return [<<Const3P2>>]
- /// CHECK-START: float Main.FloatDivision() constant_folding$after_inlining (after)
+ /// CHECK-START: float Main.FloatDivision() constant_folding (after)
/// CHECK-NOT: Div
public static float FloatDivision() {
float a, b, c;
- a = $inline$float(8F);
- b = $inline$float(2.5F);
+ a = 8F;
+ b = 2.5F;
c = a / b;
return c;
}
- /// CHECK-START: double Main.DoubleDivision() constant_folding$after_inlining (before)
+ /// CHECK-START: double Main.DoubleDivision() constant_folding (before)
/// CHECK-DAG: <<Const8:d\d+>> DoubleConstant 8
/// CHECK-DAG: <<Const2P5:d\d+>> DoubleConstant 2.5
/// CHECK-DAG: <<Div:d\d+>> Div [<<Const8>>,<<Const2P5>>]
/// CHECK-DAG: Return [<<Div>>]
- /// CHECK-START: double Main.DoubleDivision() constant_folding$after_inlining (after)
+ /// CHECK-START: double Main.DoubleDivision() constant_folding (after)
/// CHECK-DAG: <<Const3P2:d\d+>> DoubleConstant 3.2
/// CHECK-DAG: Return [<<Const3P2>>]
- /// CHECK-START: double Main.DoubleDivision() constant_folding$after_inlining (after)
+ /// CHECK-START: double Main.DoubleDivision() constant_folding (after)
/// CHECK-NOT: Div
public static double DoubleDivision() {
double a, b, c;
- a = $inline$double(8D);
- b = $inline$double(2.5D);
+ a = 8D;
+ b = 2.5D;
c = a / b;
return c;
}
@@ -608,90 +589,90 @@ public class Main {
* Exercise constant folding on remainder.
*/
- /// CHECK-START: int Main.IntRemainder() constant_folding$after_inlining (before)
+ /// CHECK-START: int Main.IntRemainder() constant_folding (before)
/// CHECK-DAG: <<Const8:i\d+>> IntConstant 8
/// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
/// CHECK-DAG: <<Div0Chk:i\d+>> DivZeroCheck [<<Const3>>]
/// CHECK-DAG: <<Rem:i\d+>> Rem [<<Const8>>,<<Div0Chk>>]
/// CHECK-DAG: Return [<<Rem>>]
- /// CHECK-START: int Main.IntRemainder() constant_folding$after_inlining (after)
+ /// CHECK-START: int Main.IntRemainder() constant_folding (after)
/// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
/// CHECK-DAG: Return [<<Const2>>]
- /// CHECK-START: int Main.IntRemainder() constant_folding$after_inlining (after)
+ /// CHECK-START: int Main.IntRemainder() constant_folding (after)
/// CHECK-NOT: DivZeroCheck
/// CHECK-NOT: Rem
public static int IntRemainder() {
int a, b, c;
- a = $inline$int(8);
- b = $inline$int(3);
+ a = 8;
+ b = 3;
c = a % b;
return c;
}
- /// CHECK-START: long Main.LongRemainder() constant_folding$after_inlining (before)
+ /// CHECK-START: long Main.LongRemainder() constant_folding (before)
/// CHECK-DAG: <<Const8:j\d+>> LongConstant 8
/// CHECK-DAG: <<Const3:j\d+>> LongConstant 3
/// CHECK-DAG: <<Div0Chk:j\d+>> DivZeroCheck [<<Const3>>]
/// CHECK-DAG: <<Rem:j\d+>> Rem [<<Const8>>,<<Div0Chk>>]
/// CHECK-DAG: Return [<<Rem>>]
- /// CHECK-START: long Main.LongRemainder() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.LongRemainder() constant_folding (after)
/// CHECK-DAG: <<Const2:j\d+>> LongConstant 2
/// CHECK-DAG: Return [<<Const2>>]
- /// CHECK-START: long Main.LongRemainder() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.LongRemainder() constant_folding (after)
/// CHECK-NOT: DivZeroCheck
/// CHECK-NOT: Rem
public static long LongRemainder() {
long a, b, c;
- a = $inline$long(8L);
- b = $inline$long(3L);
+ a = 8L;
+ b = 3L;
c = a % b;
return c;
}
- /// CHECK-START: float Main.FloatRemainder() constant_folding$after_inlining (before)
+ /// CHECK-START: float Main.FloatRemainder() constant_folding (before)
/// CHECK-DAG: <<Const8:f\d+>> FloatConstant 8
/// CHECK-DAG: <<Const2P5:f\d+>> FloatConstant 2.5
/// CHECK-DAG: <<Rem:f\d+>> Rem [<<Const8>>,<<Const2P5>>]
/// CHECK-DAG: Return [<<Rem>>]
- /// CHECK-START: float Main.FloatRemainder() constant_folding$after_inlining (after)
+ /// CHECK-START: float Main.FloatRemainder() constant_folding (after)
/// CHECK-DAG: <<Const0P5:f\d+>> FloatConstant 0.5
/// CHECK-DAG: Return [<<Const0P5>>]
- /// CHECK-START: float Main.FloatRemainder() constant_folding$after_inlining (after)
+ /// CHECK-START: float Main.FloatRemainder() constant_folding (after)
/// CHECK-NOT: Rem
public static float FloatRemainder() {
float a, b, c;
- a = $inline$float(8F);
- b = $inline$float(2.5F);
+ a = 8F;
+ b = 2.5F;
c = a % b;
return c;
}
- /// CHECK-START: double Main.DoubleRemainder() constant_folding$after_inlining (before)
+ /// CHECK-START: double Main.DoubleRemainder() constant_folding (before)
/// CHECK-DAG: <<Const8:d\d+>> DoubleConstant 8
/// CHECK-DAG: <<Const2P5:d\d+>> DoubleConstant 2.5
/// CHECK-DAG: <<Rem:d\d+>> Rem [<<Const8>>,<<Const2P5>>]
/// CHECK-DAG: Return [<<Rem>>]
- /// CHECK-START: double Main.DoubleRemainder() constant_folding$after_inlining (after)
+ /// CHECK-START: double Main.DoubleRemainder() constant_folding (after)
/// CHECK-DAG: <<Const0P5:d\d+>> DoubleConstant 0.5
/// CHECK-DAG: Return [<<Const0P5>>]
- /// CHECK-START: double Main.DoubleRemainder() constant_folding$after_inlining (after)
+ /// CHECK-START: double Main.DoubleRemainder() constant_folding (after)
/// CHECK-NOT: Rem
public static double DoubleRemainder() {
double a, b, c;
- a = $inline$double(8D);
- b = $inline$double(2.5D);
+ a = 8D;
+ b = 2.5D;
c = a % b;
return c;
}
@@ -701,42 +682,42 @@ public class Main {
* Exercise constant folding on left shift.
*/
- /// CHECK-START: int Main.ShlIntLong() constant_folding$after_inlining (before)
+ /// CHECK-START: int Main.ShlIntLong() constant_folding (before)
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Const2L:j\d+>> LongConstant 2
/// CHECK-DAG: <<TypeConv:i\d+>> TypeConversion [<<Const2L>>]
/// CHECK-DAG: <<Shl:i\d+>> Shl [<<Const1>>,<<TypeConv>>]
/// CHECK-DAG: Return [<<Shl>>]
- /// CHECK-START: int Main.ShlIntLong() constant_folding$after_inlining (after)
+ /// CHECK-START: int Main.ShlIntLong() constant_folding (after)
/// CHECK-DAG: <<Const4:i\d+>> IntConstant 4
/// CHECK-DAG: Return [<<Const4>>]
- /// CHECK-START: int Main.ShlIntLong() constant_folding$after_inlining (after)
+ /// CHECK-START: int Main.ShlIntLong() constant_folding (after)
/// CHECK-NOT: Shl
public static int ShlIntLong() {
- int lhs = $inline$int(1);
- long rhs = $inline$long(2L);
+ int lhs = 1;
+ long rhs = 2;
return lhs << rhs;
}
- /// CHECK-START: long Main.ShlLongInt() constant_folding$after_inlining (before)
+ /// CHECK-START: long Main.ShlLongInt() constant_folding (before)
/// CHECK-DAG: <<Const3L:j\d+>> LongConstant 3
/// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
/// CHECK-DAG: <<Shl:j\d+>> Shl [<<Const3L>>,<<Const2>>]
/// CHECK-DAG: Return [<<Shl>>]
- /// CHECK-START: long Main.ShlLongInt() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.ShlLongInt() constant_folding (after)
/// CHECK-DAG: <<Const12L:j\d+>> LongConstant 12
/// CHECK-DAG: Return [<<Const12L>>]
- /// CHECK-START: long Main.ShlLongInt() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.ShlLongInt() constant_folding (after)
/// CHECK-NOT: Shl
public static long ShlLongInt() {
- long lhs = $inline$long(3L);
- int rhs = $inline$int(2);
+ long lhs = 3;
+ int rhs = 2;
return lhs << rhs;
}
@@ -745,42 +726,42 @@ public class Main {
* Exercise constant folding on right shift.
*/
- /// CHECK-START: int Main.ShrIntLong() constant_folding$after_inlining (before)
+ /// CHECK-START: int Main.ShrIntLong() constant_folding (before)
/// CHECK-DAG: <<Const7:i\d+>> IntConstant 7
/// CHECK-DAG: <<Const2L:j\d+>> LongConstant 2
/// CHECK-DAG: <<TypeConv:i\d+>> TypeConversion [<<Const2L>>]
/// CHECK-DAG: <<Shr:i\d+>> Shr [<<Const7>>,<<TypeConv>>]
/// CHECK-DAG: Return [<<Shr>>]
- /// CHECK-START: int Main.ShrIntLong() constant_folding$after_inlining (after)
+ /// CHECK-START: int Main.ShrIntLong() constant_folding (after)
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
/// CHECK-DAG: Return [<<Const1>>]
- /// CHECK-START: int Main.ShrIntLong() constant_folding$after_inlining (after)
+ /// CHECK-START: int Main.ShrIntLong() constant_folding (after)
/// CHECK-NOT: Shr
public static int ShrIntLong() {
- int lhs = $inline$int(7);
- long rhs = $inline$long(2L);
+ int lhs = 7;
+ long rhs = 2;
return lhs >> rhs;
}
- /// CHECK-START: long Main.ShrLongInt() constant_folding$after_inlining (before)
+ /// CHECK-START: long Main.ShrLongInt() constant_folding (before)
/// CHECK-DAG: <<Const9L:j\d+>> LongConstant 9
/// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
/// CHECK-DAG: <<Shr:j\d+>> Shr [<<Const9L>>,<<Const2>>]
/// CHECK-DAG: Return [<<Shr>>]
- /// CHECK-START: long Main.ShrLongInt() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.ShrLongInt() constant_folding (after)
/// CHECK-DAG: <<Const2L:j\d+>> LongConstant 2
/// CHECK-DAG: Return [<<Const2L>>]
- /// CHECK-START: long Main.ShrLongInt() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.ShrLongInt() constant_folding (after)
/// CHECK-NOT: Shr
public static long ShrLongInt() {
- long lhs = $inline$long(9);
- int rhs = $inline$int(2);
+ long lhs = 9;
+ int rhs = 2;
return lhs >> rhs;
}
@@ -789,42 +770,42 @@ public class Main {
* Exercise constant folding on unsigned right shift.
*/
- /// CHECK-START: int Main.UShrIntLong() constant_folding$after_inlining (before)
+ /// CHECK-START: int Main.UShrIntLong() constant_folding (before)
/// CHECK-DAG: <<ConstM7:i\d+>> IntConstant -7
/// CHECK-DAG: <<Const2L:j\d+>> LongConstant 2
/// CHECK-DAG: <<TypeConv:i\d+>> TypeConversion [<<Const2L>>]
/// CHECK-DAG: <<UShr:i\d+>> UShr [<<ConstM7>>,<<TypeConv>>]
/// CHECK-DAG: Return [<<UShr>>]
- /// CHECK-START: int Main.UShrIntLong() constant_folding$after_inlining (after)
+ /// CHECK-START: int Main.UShrIntLong() constant_folding (after)
/// CHECK-DAG: <<ConstRes:i\d+>> IntConstant 1073741822
/// CHECK-DAG: Return [<<ConstRes>>]
- /// CHECK-START: int Main.UShrIntLong() constant_folding$after_inlining (after)
+ /// CHECK-START: int Main.UShrIntLong() constant_folding (after)
/// CHECK-NOT: UShr
public static int UShrIntLong() {
- int lhs = $inline$int(-7);
- long rhs = $inline$long(2L);
+ int lhs = -7;
+ long rhs = 2;
return lhs >>> rhs;
}
- /// CHECK-START: long Main.UShrLongInt() constant_folding$after_inlining (before)
+ /// CHECK-START: long Main.UShrLongInt() constant_folding (before)
/// CHECK-DAG: <<ConstM9L:j\d+>> LongConstant -9
/// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
/// CHECK-DAG: <<UShr:j\d+>> UShr [<<ConstM9L>>,<<Const2>>]
/// CHECK-DAG: Return [<<UShr>>]
- /// CHECK-START: long Main.UShrLongInt() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.UShrLongInt() constant_folding (after)
/// CHECK-DAG: <<ConstRes:j\d+>> LongConstant 4611686018427387901
/// CHECK-DAG: Return [<<ConstRes>>]
- /// CHECK-START: long Main.UShrLongInt() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.UShrLongInt() constant_folding (after)
/// CHECK-NOT: UShr
public static long UShrLongInt() {
- long lhs = $inline$long(-9);
- int rhs = $inline$int(2);
+ long lhs = -9;
+ int rhs = 2;
return lhs >>> rhs;
}
@@ -833,43 +814,43 @@ public class Main {
* Exercise constant folding on logical and.
*/
- /// CHECK-START: long Main.AndIntLong() constant_folding$after_inlining (before)
+ /// CHECK-START: long Main.AndIntLong() constant_folding (before)
/// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
/// CHECK-DAG: <<Const3L:j\d+>> LongConstant 3
/// CHECK-DAG: <<TypeConv:j\d+>> TypeConversion [<<Const10>>]
/// CHECK-DAG: <<And:j\d+>> And [<<TypeConv>>,<<Const3L>>]
/// CHECK-DAG: Return [<<And>>]
- /// CHECK-START: long Main.AndIntLong() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.AndIntLong() constant_folding (after)
/// CHECK-DAG: <<Const2:j\d+>> LongConstant 2
/// CHECK-DAG: Return [<<Const2>>]
- /// CHECK-START: long Main.AndIntLong() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.AndIntLong() constant_folding (after)
/// CHECK-NOT: And
public static long AndIntLong() {
- int lhs = $inline$int(10);
- long rhs = $inline$long(3L);
+ int lhs = 10;
+ long rhs = 3;
return lhs & rhs;
}
- /// CHECK-START: long Main.AndLongInt() constant_folding$after_inlining (before)
+ /// CHECK-START: long Main.AndLongInt() constant_folding (before)
/// CHECK-DAG: <<Const10L:j\d+>> LongConstant 10
/// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
/// CHECK-DAG: <<TypeConv:j\d+>> TypeConversion [<<Const3>>]
/// CHECK-DAG: <<And:j\d+>> And [<<TypeConv>>,<<Const10L>>]
/// CHECK-DAG: Return [<<And>>]
- /// CHECK-START: long Main.AndLongInt() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.AndLongInt() constant_folding (after)
/// CHECK-DAG: <<Const2:j\d+>> LongConstant 2
/// CHECK-DAG: Return [<<Const2>>]
- /// CHECK-START: long Main.AndLongInt() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.AndLongInt() constant_folding (after)
/// CHECK-NOT: And
public static long AndLongInt() {
- long lhs = $inline$long(10L);
- int rhs = $inline$int(3);
+ long lhs = 10;
+ int rhs = 3;
return lhs & rhs;
}
@@ -878,43 +859,43 @@ public class Main {
* Exercise constant folding on logical or.
*/
- /// CHECK-START: long Main.OrIntLong() constant_folding$after_inlining (before)
+ /// CHECK-START: long Main.OrIntLong() constant_folding (before)
/// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
/// CHECK-DAG: <<Const3L:j\d+>> LongConstant 3
/// CHECK-DAG: <<TypeConv:j\d+>> TypeConversion [<<Const10>>]
/// CHECK-DAG: <<Or:j\d+>> Or [<<TypeConv>>,<<Const3L>>]
/// CHECK-DAG: Return [<<Or>>]
- /// CHECK-START: long Main.OrIntLong() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.OrIntLong() constant_folding (after)
/// CHECK-DAG: <<Const11:j\d+>> LongConstant 11
/// CHECK-DAG: Return [<<Const11>>]
- /// CHECK-START: long Main.OrIntLong() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.OrIntLong() constant_folding (after)
/// CHECK-NOT: Or
public static long OrIntLong() {
- int lhs = $inline$int(10);
- long rhs = $inline$long(3L);
+ int lhs = 10;
+ long rhs = 3;
return lhs | rhs;
}
- /// CHECK-START: long Main.OrLongInt() constant_folding$after_inlining (before)
+ /// CHECK-START: long Main.OrLongInt() constant_folding (before)
/// CHECK-DAG: <<Const10L:j\d+>> LongConstant 10
/// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
/// CHECK-DAG: <<TypeConv:j\d+>> TypeConversion [<<Const3>>]
/// CHECK-DAG: <<Or:j\d+>> Or [<<TypeConv>>,<<Const10L>>]
/// CHECK-DAG: Return [<<Or>>]
- /// CHECK-START: long Main.OrLongInt() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.OrLongInt() constant_folding (after)
/// CHECK-DAG: <<Const11:j\d+>> LongConstant 11
/// CHECK-DAG: Return [<<Const11>>]
- /// CHECK-START: long Main.OrLongInt() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.OrLongInt() constant_folding (after)
/// CHECK-NOT: Or
public static long OrLongInt() {
- long lhs = $inline$long(10L);
- int rhs = $inline$int(3);
+ long lhs = 10;
+ int rhs = 3;
return lhs | rhs;
}
@@ -923,43 +904,43 @@ public class Main {
* Exercise constant folding on logical exclusive or.
*/
- /// CHECK-START: long Main.XorIntLong() constant_folding$after_inlining (before)
+ /// CHECK-START: long Main.XorIntLong() constant_folding (before)
/// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
/// CHECK-DAG: <<Const3L:j\d+>> LongConstant 3
/// CHECK-DAG: <<TypeConv:j\d+>> TypeConversion [<<Const10>>]
/// CHECK-DAG: <<Xor:j\d+>> Xor [<<TypeConv>>,<<Const3L>>]
/// CHECK-DAG: Return [<<Xor>>]
- /// CHECK-START: long Main.XorIntLong() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.XorIntLong() constant_folding (after)
/// CHECK-DAG: <<Const9:j\d+>> LongConstant 9
/// CHECK-DAG: Return [<<Const9>>]
- /// CHECK-START: long Main.XorIntLong() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.XorIntLong() constant_folding (after)
/// CHECK-NOT: Xor
public static long XorIntLong() {
- int lhs = $inline$int(10);
- long rhs = $inline$long(3L);
+ int lhs = 10;
+ long rhs = 3;
return lhs ^ rhs;
}
- /// CHECK-START: long Main.XorLongInt() constant_folding$after_inlining (before)
+ /// CHECK-START: long Main.XorLongInt() constant_folding (before)
/// CHECK-DAG: <<Const10L:j\d+>> LongConstant 10
/// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
/// CHECK-DAG: <<TypeConv:j\d+>> TypeConversion [<<Const3>>]
/// CHECK-DAG: <<Xor:j\d+>> Xor [<<TypeConv>>,<<Const10L>>]
/// CHECK-DAG: Return [<<Xor>>]
- /// CHECK-START: long Main.XorLongInt() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.XorLongInt() constant_folding (after)
/// CHECK-DAG: <<Const9:j\d+>> LongConstant 9
/// CHECK-DAG: Return [<<Const9>>]
- /// CHECK-START: long Main.XorLongInt() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.XorLongInt() constant_folding (after)
/// CHECK-NOT: Xor
public static long XorLongInt() {
- long lhs = $inline$long(10L);
- int rhs = $inline$int(3);
+ long lhs = 10;
+ int rhs = 3;
return lhs ^ rhs;
}
@@ -968,23 +949,23 @@ public class Main {
* Exercise constant folding on constant (static) condition.
*/
- /// CHECK-START: int Main.StaticCondition() constant_folding$after_inlining (before)
+ /// CHECK-START: int Main.StaticCondition() constant_folding (before)
/// CHECK-DAG: <<Const7:i\d+>> IntConstant 7
/// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
/// CHECK-DAG: <<Cond:z\d+>> GreaterThanOrEqual [<<Const7>>,<<Const2>>]
- /// CHECK-DAG: Select [{{i\d+}},{{i\d+}},<<Cond>>]
+ /// CHECK-DAG: If [<<Cond>>]
- /// CHECK-START: int Main.StaticCondition() constant_folding$after_inlining (after)
+ /// CHECK-START: int Main.StaticCondition() constant_folding (after)
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
- /// CHECK-DAG: Select [{{i\d+}},{{i\d+}},<<Const1>>]
+ /// CHECK-DAG: If [<<Const1>>]
- /// CHECK-START: int Main.StaticCondition() constant_folding$after_inlining (after)
+ /// CHECK-START: int Main.StaticCondition() constant_folding (after)
/// CHECK-NOT: GreaterThanOrEqual
public static int StaticCondition() {
int a, b, c;
- a = $inline$int(7);
- b = $inline$int(2);
+ a = 7;
+ b = 2;
if (a < b)
c = a + b;
else
@@ -1029,30 +1010,28 @@ public class Main {
* (forward) post-order traversal of the the dominator tree.
*/
- /// CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding$after_inlining (before)
- /// CHECK-DAG: <<Cond:z\d+>> ParameterValue
+ /// CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding (before)
/// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
/// CHECK-DAG: <<Const5:i\d+>> IntConstant 5
/// CHECK-DAG: <<Add:i\d+>> Add [<<Const5>>,<<Const2>>]
/// CHECK-DAG: <<Sub:i\d+>> Sub [<<Const5>>,<<Const2>>]
- /// CHECK-DAG: <<Phi:i\d+>> Select [<<Sub>>,<<Add>>,<<Cond>>]
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Add>>,<<Sub>>]
/// CHECK-DAG: Return [<<Phi>>]
- /// CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding$after_inlining (after)
- /// CHECK-DAG: <<Cond:z\d+>> ParameterValue
+ /// CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding (after)
/// CHECK-DAG: <<Const3:i\d+>> IntConstant 3
/// CHECK-DAG: <<Const7:i\d+>> IntConstant 7
- /// CHECK-DAG: <<Phi:i\d+>> Select [<<Const3>>,<<Const7>>,<<Cond>>]
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Const7>>,<<Const3>>]
/// CHECK-DAG: Return [<<Phi>>]
- /// CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding$after_inlining (after)
+ /// CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding (after)
/// CHECK-NOT: Add
/// CHECK-NOT: Sub
public static int JumpsAndConditionals(boolean cond) {
int a, b, c;
- a = $inline$int(5);
- b = $inline$int(2);
+ a = 5;
+ b = 2;
if (cond)
c = a + b;
else
@@ -1328,259 +1307,207 @@ public class Main {
/**
- * Test optimizations of comparisons with null yielding a constant result.
- */
-
- /// CHECK-START: boolean Main.ConstStringEqualsNull() constant_folding$after_inlining (before)
- /// CHECK-DAG: <<ConstStr:l\d+>> LoadString
- /// CHECK-DAG: <<Null:l\d+>> NullConstant
- /// CHECK-DAG: <<Eq:z\d+>> Equal [<<ConstStr>>,<<Null>>]
- /// CHECK-DAG: If [<<Eq>>]
-
- /// CHECK-START: boolean Main.ConstStringEqualsNull() constant_folding$after_inlining (after)
- /// CHECK-DAG: <<False:i\d+>> IntConstant 0
- /// CHECK-DAG: If [<<False>>]
-
- /// CHECK-START: boolean Main.ConstStringEqualsNull() constant_folding$after_inlining (after)
- /// CHECK-NOT: Equal
-
- public static boolean ConstStringEqualsNull() {
- // Due to Jack emitting code using the opposite condition, use != to generate Equal.
- if ($inline$ConstString() != null) {
- return false;
- } else {
- return true;
- }
- }
-
- /// CHECK-START: boolean Main.ConstStringNotEqualsNull() constant_folding$after_inlining (before)
- /// CHECK-DAG: <<ConstStr:l\d+>> LoadString
- /// CHECK-DAG: <<Null:l\d+>> NullConstant
- /// CHECK-DAG: <<Ne:z\d+>> NotEqual [<<ConstStr>>,<<Null>>]
- /// CHECK-DAG: If [<<Ne>>]
-
- /// CHECK-START: boolean Main.ConstStringNotEqualsNull() constant_folding$after_inlining (after)
- /// CHECK-DAG: <<True:i\d+>> IntConstant 1
- /// CHECK-DAG: If [<<True>>]
-
- /// CHECK-START: boolean Main.ConstStringNotEqualsNull() constant_folding$after_inlining (after)
- /// CHECK-NOT: NotEqual
-
- public static boolean ConstStringNotEqualsNull() {
- // Due to Jack emitting code using the opposite condition, use == to generate NotEqual.
- if ($inline$ConstString() == null) {
- return false;
- } else {
- return true;
- }
- }
-
- public static String $inline$ConstString() {
- return "";
- }
-
- /**
* Exercise constant folding on type conversions.
*/
- /// CHECK-START: int Main.ReturnInt33() constant_folding$after_inlining (before)
+ /// CHECK-START: int Main.ReturnInt33() constant_folding (before)
/// CHECK-DAG: <<Const33:j\d+>> LongConstant 33
/// CHECK-DAG: <<Convert:i\d+>> TypeConversion [<<Const33>>]
/// CHECK-DAG: Return [<<Convert>>]
- /// CHECK-START: int Main.ReturnInt33() constant_folding$after_inlining (after)
+ /// CHECK-START: int Main.ReturnInt33() constant_folding (after)
/// CHECK-DAG: <<Const33:i\d+>> IntConstant 33
/// CHECK-DAG: Return [<<Const33>>]
- /// CHECK-START: int Main.ReturnInt33() constant_folding$after_inlining (after)
+ /// CHECK-START: int Main.ReturnInt33() constant_folding (after)
/// CHECK-NOT: TypeConversion
public static int ReturnInt33() {
- long imm = $inline$long(33L);
+ long imm = 33L;
return (int) imm;
}
- /// CHECK-START: int Main.ReturnIntMax() constant_folding$after_inlining (before)
+ /// CHECK-START: int Main.ReturnIntMax() constant_folding (before)
/// CHECK-DAG: <<ConstMax:f\d+>> FloatConstant 1e+34
/// CHECK-DAG: <<Convert:i\d+>> TypeConversion [<<ConstMax>>]
/// CHECK-DAG: Return [<<Convert>>]
- /// CHECK-START: int Main.ReturnIntMax() constant_folding$after_inlining (after)
+ /// CHECK-START: int Main.ReturnIntMax() constant_folding (after)
/// CHECK-DAG: <<ConstMax:i\d+>> IntConstant 2147483647
/// CHECK-DAG: Return [<<ConstMax>>]
- /// CHECK-START: int Main.ReturnIntMax() constant_folding$after_inlining (after)
+ /// CHECK-START: int Main.ReturnIntMax() constant_folding (after)
/// CHECK-NOT: TypeConversion
public static int ReturnIntMax() {
- float imm = $inline$float(1.0e34f);
+ float imm = 1.0e34f;
return (int) imm;
}
- /// CHECK-START: int Main.ReturnInt0() constant_folding$after_inlining (before)
+ /// CHECK-START: int Main.ReturnInt0() constant_folding (before)
/// CHECK-DAG: <<ConstNaN:d\d+>> DoubleConstant nan
/// CHECK-DAG: <<Convert:i\d+>> TypeConversion [<<ConstNaN>>]
/// CHECK-DAG: Return [<<Convert>>]
- /// CHECK-START: int Main.ReturnInt0() constant_folding$after_inlining (after)
+ /// CHECK-START: int Main.ReturnInt0() constant_folding (after)
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
/// CHECK-DAG: Return [<<Const0>>]
- /// CHECK-START: int Main.ReturnInt0() constant_folding$after_inlining (after)
+ /// CHECK-START: int Main.ReturnInt0() constant_folding (after)
/// CHECK-NOT: TypeConversion
public static int ReturnInt0() {
- double imm = $inline$double(Double.NaN);
+ double imm = Double.NaN;
return (int) imm;
}
- /// CHECK-START: long Main.ReturnLong33() constant_folding$after_inlining (before)
+ /// CHECK-START: long Main.ReturnLong33() constant_folding (before)
/// CHECK-DAG: <<Const33:i\d+>> IntConstant 33
/// CHECK-DAG: <<Convert:j\d+>> TypeConversion [<<Const33>>]
/// CHECK-DAG: Return [<<Convert>>]
- /// CHECK-START: long Main.ReturnLong33() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.ReturnLong33() constant_folding (after)
/// CHECK-DAG: <<Const33:j\d+>> LongConstant 33
/// CHECK-DAG: Return [<<Const33>>]
- /// CHECK-START: long Main.ReturnLong33() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.ReturnLong33() constant_folding (after)
/// CHECK-NOT: TypeConversion
public static long ReturnLong33() {
- int imm = $inline$int(33);
+ int imm = 33;
return (long) imm;
}
- /// CHECK-START: long Main.ReturnLong34() constant_folding$after_inlining (before)
+ /// CHECK-START: long Main.ReturnLong34() constant_folding (before)
/// CHECK-DAG: <<Const34:f\d+>> FloatConstant 34
/// CHECK-DAG: <<Convert:j\d+>> TypeConversion [<<Const34>>]
/// CHECK-DAG: Return [<<Convert>>]
- /// CHECK-START: long Main.ReturnLong34() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.ReturnLong34() constant_folding (after)
/// CHECK-DAG: <<Const34:j\d+>> LongConstant 34
/// CHECK-DAG: Return [<<Const34>>]
- /// CHECK-START: long Main.ReturnLong34() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.ReturnLong34() constant_folding (after)
/// CHECK-NOT: TypeConversion
public static long ReturnLong34() {
- float imm = $inline$float(34.0f);
+ float imm = 34.0f;
return (long) imm;
}
- /// CHECK-START: long Main.ReturnLong0() constant_folding$after_inlining (before)
+ /// CHECK-START: long Main.ReturnLong0() constant_folding (before)
/// CHECK-DAG: <<ConstNaN:d\d+>> DoubleConstant nan
/// CHECK-DAG: <<Convert:j\d+>> TypeConversion [<<ConstNaN>>]
/// CHECK-DAG: Return [<<Convert>>]
- /// CHECK-START: long Main.ReturnLong0() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.ReturnLong0() constant_folding (after)
/// CHECK-DAG: <<Const0:j\d+>> LongConstant 0
/// CHECK-DAG: Return [<<Const0>>]
- /// CHECK-START: long Main.ReturnLong0() constant_folding$after_inlining (after)
+ /// CHECK-START: long Main.ReturnLong0() constant_folding (after)
/// CHECK-NOT: TypeConversion
public static long ReturnLong0() {
- double imm = $inline$double(-Double.NaN);
+ double imm = -Double.NaN;
return (long) imm;
}
- /// CHECK-START: float Main.ReturnFloat33() constant_folding$after_inlining (before)
+ /// CHECK-START: float Main.ReturnFloat33() constant_folding (before)
/// CHECK-DAG: <<Const33:i\d+>> IntConstant 33
/// CHECK-DAG: <<Convert:f\d+>> TypeConversion [<<Const33>>]
/// CHECK-DAG: Return [<<Convert>>]
- /// CHECK-START: float Main.ReturnFloat33() constant_folding$after_inlining (after)
+ /// CHECK-START: float Main.ReturnFloat33() constant_folding (after)
/// CHECK-DAG: <<Const33:f\d+>> FloatConstant 33
/// CHECK-DAG: Return [<<Const33>>]
- /// CHECK-START: float Main.ReturnFloat33() constant_folding$after_inlining (after)
+ /// CHECK-START: float Main.ReturnFloat33() constant_folding (after)
/// CHECK-NOT: TypeConversion
public static float ReturnFloat33() {
- int imm = $inline$int(33);
+ int imm = 33;
return (float) imm;
}
- /// CHECK-START: float Main.ReturnFloat34() constant_folding$after_inlining (before)
+ /// CHECK-START: float Main.ReturnFloat34() constant_folding (before)
/// CHECK-DAG: <<Const34:j\d+>> LongConstant 34
/// CHECK-DAG: <<Convert:f\d+>> TypeConversion [<<Const34>>]
/// CHECK-DAG: Return [<<Convert>>]
- /// CHECK-START: float Main.ReturnFloat34() constant_folding$after_inlining (after)
+ /// CHECK-START: float Main.ReturnFloat34() constant_folding (after)
/// CHECK-DAG: <<Const34:f\d+>> FloatConstant 34
/// CHECK-DAG: Return [<<Const34>>]
- /// CHECK-START: float Main.ReturnFloat34() constant_folding$after_inlining (after)
+ /// CHECK-START: float Main.ReturnFloat34() constant_folding (after)
/// CHECK-NOT: TypeConversion
public static float ReturnFloat34() {
- long imm = $inline$long(34L);
+ long imm = 34L;
return (float) imm;
}
- /// CHECK-START: float Main.ReturnFloat99P25() constant_folding$after_inlining (before)
+ /// CHECK-START: float Main.ReturnFloat99P25() constant_folding (before)
/// CHECK-DAG: <<Const:d\d+>> DoubleConstant 99.25
/// CHECK-DAG: <<Convert:f\d+>> TypeConversion [<<Const>>]
/// CHECK-DAG: Return [<<Convert>>]
- /// CHECK-START: float Main.ReturnFloat99P25() constant_folding$after_inlining (after)
+ /// CHECK-START: float Main.ReturnFloat99P25() constant_folding (after)
/// CHECK-DAG: <<Const:f\d+>> FloatConstant 99.25
/// CHECK-DAG: Return [<<Const>>]
- /// CHECK-START: float Main.ReturnFloat99P25() constant_folding$after_inlining (after)
+ /// CHECK-START: float Main.ReturnFloat99P25() constant_folding (after)
/// CHECK-NOT: TypeConversion
public static float ReturnFloat99P25() {
- double imm = $inline$double(99.25);
+ double imm = 99.25;
return (float) imm;
}
- /// CHECK-START: double Main.ReturnDouble33() constant_folding$after_inlining (before)
+ /// CHECK-START: double Main.ReturnDouble33() constant_folding (before)
/// CHECK-DAG: <<Const33:i\d+>> IntConstant 33
/// CHECK-DAG: <<Convert:d\d+>> TypeConversion [<<Const33>>]
/// CHECK-DAG: Return [<<Convert>>]
- /// CHECK-START: double Main.ReturnDouble33() constant_folding$after_inlining (after)
+ /// CHECK-START: double Main.ReturnDouble33() constant_folding (after)
/// CHECK-DAG: <<Const33:d\d+>> DoubleConstant 33
/// CHECK-DAG: Return [<<Const33>>]
public static double ReturnDouble33() {
- int imm = $inline$int(33);
+ int imm = 33;
return (double) imm;
}
- /// CHECK-START: double Main.ReturnDouble34() constant_folding$after_inlining (before)
+ /// CHECK-START: double Main.ReturnDouble34() constant_folding (before)
/// CHECK-DAG: <<Const34:j\d+>> LongConstant 34
/// CHECK-DAG: <<Convert:d\d+>> TypeConversion [<<Const34>>]
/// CHECK-DAG: Return [<<Convert>>]
- /// CHECK-START: double Main.ReturnDouble34() constant_folding$after_inlining (after)
+ /// CHECK-START: double Main.ReturnDouble34() constant_folding (after)
/// CHECK-DAG: <<Const34:d\d+>> DoubleConstant 34
/// CHECK-DAG: Return [<<Const34>>]
- /// CHECK-START: double Main.ReturnDouble34() constant_folding$after_inlining (after)
+ /// CHECK-START: double Main.ReturnDouble34() constant_folding (after)
/// CHECK-NOT: TypeConversion
public static double ReturnDouble34() {
- long imm = $inline$long(34L);
+ long imm = 34L;
return (double) imm;
}
- /// CHECK-START: double Main.ReturnDouble99P25() constant_folding$after_inlining (before)
+ /// CHECK-START: double Main.ReturnDouble99P25() constant_folding (before)
/// CHECK-DAG: <<Const:f\d+>> FloatConstant 99.25
/// CHECK-DAG: <<Convert:d\d+>> TypeConversion [<<Const>>]
/// CHECK-DAG: Return [<<Convert>>]
- /// CHECK-START: double Main.ReturnDouble99P25() constant_folding$after_inlining (after)
+ /// CHECK-START: double Main.ReturnDouble99P25() constant_folding (after)
/// CHECK-DAG: <<Const:d\d+>> DoubleConstant 99.25
/// CHECK-DAG: Return [<<Const>>]
- /// CHECK-START: double Main.ReturnDouble99P25() constant_folding$after_inlining (after)
+ /// CHECK-START: double Main.ReturnDouble99P25() constant_folding (after)
/// CHECK-NOT: TypeConversion
public static double ReturnDouble99P25() {
- float imm = $inline$float(99.25f);
+ float imm = 99.25f;
return (double) imm;
}
@@ -1659,9 +1586,6 @@ public class Main {
assertFalse(CmpFloatGreaterThanNaN(arbitrary));
assertFalse(CmpDoubleLessThanNaN(arbitrary));
- assertFalse(ConstStringEqualsNull());
- assertTrue(ConstStringNotEqualsNull());
-
Main main = new Main();
assertIntEquals(1, main.smaliCmpLongConstants());
assertIntEquals(-1, main.smaliCmpGtFloatConstants());
diff --git a/test/450-checker-types/build b/test/450-checker-types/build
new file mode 100755
index 0000000000..49292c9ac1
--- /dev/null
+++ b/test/450-checker-types/build
@@ -0,0 +1,23 @@
+#!/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.
+
+# This checker test is incompatible with jack bytecode output,
+# so force it to use javac/dx.
+export USE_JACK=false
+# Also disable desugar because it is missing in jack platform builds.
+export DESUGAR=false
+
+./default-build "$@"
diff --git a/test/450-checker-types/smali/SmaliTests.smali b/test/450-checker-types/smali/SmaliTests.smali
deleted file mode 100644
index 6a3122e41b..0000000000
--- a/test/450-checker-types/smali/SmaliTests.smali
+++ /dev/null
@@ -1,120 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-.class public LSmaliTests;
-.super Ljava/lang/Object;
-
-## CHECK-START: void SmaliTests.testInstanceOf_EQ0_NotInlined(java.lang.Object) builder (after)
-## CHECK-DAG: <<Cst0:i\d+>> IntConstant 0
-## CHECK-DAG: <<IOf:z\d+>> InstanceOf
-## CHECK-DAG: Equal [<<IOf>>,<<Cst0>>]
-
-## CHECK-START: void SmaliTests.testInstanceOf_EQ0_NotInlined(java.lang.Object) instruction_simplifier (before)
-## CHECK: CheckCast
-
-## CHECK-START: void SmaliTests.testInstanceOf_EQ0_NotInlined(java.lang.Object) instruction_simplifier (after)
-## CHECK-NOT: CheckCast
-
-.method public static testInstanceOf_EQ0_NotInlined(Ljava/lang/Object;)V
- .registers 3
-
- const v0, 0x0
- instance-of v1, p0, LSubclassC;
- if-eq v1, v0, :return
-
- check-cast p0, LSubclassC;
- invoke-virtual {p0}, LSubclassC;->$noinline$g()V
-
- :return
- return-void
-
-.end method
-
-## CHECK-START: void SmaliTests.testInstanceOf_EQ1_NotInlined(java.lang.Object) builder (after)
-## CHECK-DAG: <<Cst1:i\d+>> IntConstant 1
-## CHECK-DAG: <<IOf:z\d+>> InstanceOf
-## CHECK-DAG: Equal [<<IOf>>,<<Cst1>>]
-
-## CHECK-START: void SmaliTests.testInstanceOf_EQ1_NotInlined(java.lang.Object) instruction_simplifier (before)
-## CHECK: CheckCast
-
-## CHECK-START: void SmaliTests.testInstanceOf_EQ1_NotInlined(java.lang.Object) instruction_simplifier (after)
-## CHECK-NOT: CheckCast
-
-.method public static testInstanceOf_EQ1_NotInlined(Ljava/lang/Object;)V
- .registers 3
-
- const v0, 0x1
- instance-of v1, p0, LSubclassC;
- if-eq v1, v0, :invoke
- return-void
-
- :invoke
- check-cast p0, LSubclassC;
- invoke-virtual {p0}, LSubclassC;->$noinline$g()V
- return-void
-
-.end method
-
-## CHECK-START: void SmaliTests.testInstanceOf_NE0_NotInlined(java.lang.Object) builder (after)
-## CHECK-DAG: <<Cst0:i\d+>> IntConstant 0
-## CHECK-DAG: <<IOf:z\d+>> InstanceOf
-## CHECK-DAG: NotEqual [<<IOf>>,<<Cst0>>]
-
-## CHECK-START: void SmaliTests.testInstanceOf_NE0_NotInlined(java.lang.Object) instruction_simplifier (before)
-## CHECK: CheckCast
-
-## CHECK-START: void SmaliTests.testInstanceOf_NE0_NotInlined(java.lang.Object) instruction_simplifier (after)
-## CHECK-NOT: CheckCast
-
-.method public static testInstanceOf_NE0_NotInlined(Ljava/lang/Object;)V
- .registers 3
-
- const v0, 0x0
- instance-of v1, p0, LSubclassC;
- if-ne v1, v0, :invoke
- return-void
-
- :invoke
- check-cast p0, LSubclassC;
- invoke-virtual {p0}, LSubclassC;->$noinline$g()V
- return-void
-
-.end method
-
-## CHECK-START: void SmaliTests.testInstanceOf_NE1_NotInlined(java.lang.Object) builder (after)
-## CHECK-DAG: <<Cst1:i\d+>> IntConstant 1
-## CHECK-DAG: <<IOf:z\d+>> InstanceOf
-## CHECK-DAG: NotEqual [<<IOf>>,<<Cst1>>]
-
-## CHECK-START: void SmaliTests.testInstanceOf_NE1_NotInlined(java.lang.Object) instruction_simplifier (before)
-## CHECK: CheckCast
-
-## CHECK-START: void SmaliTests.testInstanceOf_NE1_NotInlined(java.lang.Object) instruction_simplifier (after)
-## CHECK-NOT: CheckCast
-
-.method public static testInstanceOf_NE1_NotInlined(Ljava/lang/Object;)V
- .registers 3
-
- const v0, 0x1
- instance-of v1, p0, LSubclassC;
- if-ne v1, v0, :return
-
- check-cast p0, LSubclassC;
- invoke-virtual {p0}, LSubclassC;->$noinline$g()V
-
- :return
- return-void
-
-.end method
diff --git a/test/450-checker-types/src/Main.java b/test/450-checker-types/src/Main.java
index ea8609ee5e..ae0fdbe576 100644
--- a/test/450-checker-types/src/Main.java
+++ b/test/450-checker-types/src/Main.java
@@ -210,6 +210,58 @@ public class Main {
public static boolean $inline$InstanceofSubclassB(Object o) { return o instanceof SubclassB; }
public static boolean $inline$InstanceofSubclassC(Object o) { return o instanceof SubclassC; }
+ /// CHECK-START: void Main.testInstanceOf_NotInlined(java.lang.Object) builder (after)
+ /// CHECK-DAG: <<Cst0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Cst1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<IOf1:z\d+>> InstanceOf
+ /// CHECK-DAG: NotEqual [<<IOf1>>,<<Cst1>>]
+ /// CHECK-DAG: <<IOf2:z\d+>> InstanceOf
+ /// CHECK-DAG: Equal [<<IOf2>>,<<Cst0>>]
+
+ /// CHECK-START: void Main.testInstanceOf_NotInlined(java.lang.Object) instruction_simplifier (before)
+ /// CHECK: CheckCast
+ /// CHECK: CheckCast
+ /// CHECK-NOT: CheckCast
+
+ /// CHECK-START: void Main.testInstanceOf_NotInlined(java.lang.Object) instruction_simplifier (after)
+ /// CHECK-NOT: CheckCast
+ public void testInstanceOf_NotInlined(Object o) {
+ if ((o instanceof SubclassC) == true) {
+ ((SubclassC)o).$noinline$g();
+ }
+ if ((o instanceof SubclassB) != false) {
+ ((SubclassB)o).$noinline$g();
+ }
+ }
+
+ /// CHECK-START: void Main.testNotInstanceOf_NotInlined(java.lang.Object) builder (after)
+ /// CHECK-DAG: <<Cst0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Cst1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<IOf1:z\d+>> InstanceOf
+ /// CHECK-DAG: Equal [<<IOf1>>,<<Cst1>>]
+ /// CHECK-DAG: <<IOf2:z\d+>> InstanceOf
+ /// CHECK-DAG: NotEqual [<<IOf2>>,<<Cst0>>]
+
+ /// CHECK-START: void Main.testNotInstanceOf_NotInlined(java.lang.Object) instruction_simplifier (before)
+ /// CHECK: CheckCast
+ /// CHECK: CheckCast
+ /// CHECK-NOT: CheckCast
+
+ /// CHECK-START: void Main.testNotInstanceOf_NotInlined(java.lang.Object) instruction_simplifier (after)
+ /// CHECK-NOT: CheckCast
+ public void testNotInstanceOf_NotInlined(Object o) {
+ if ((o instanceof SubclassC) != true) {
+ // Empty branch to flip the condition.
+ } else {
+ ((SubclassC)o).$noinline$g();
+ }
+ if ((o instanceof SubclassB) == false) {
+ // Empty branch to flip the condition.
+ } else {
+ ((SubclassB)o).$noinline$g();
+ }
+ }
+
/// CHECK-START: void Main.testInstanceOf_Inlined(java.lang.Object) inliner (after)
/// CHECK-DAG: <<IOf:z\d+>> InstanceOf
/// CHECK-DAG: If [<<IOf>>]
@@ -634,7 +686,7 @@ public class Main {
/// CHECK-DAG: <<Null:l\d+>> NullConstant
/// CHECK-DAG: <<Phi:l\d+>> Phi [<<Arg>>,<<Null>>] klass:SubclassA
/// CHECK-DAG: <<NCPhi:l\d+>> NullCheck [<<Phi>>]
- /// CHECK-DAG: InvokeVirtual [<<NCPhi>>] method_name:Super.hashCode
+ /// CHECK-DAG: InvokeVirtual [<<NCPhi>>] method_name:java.lang.Object.hashCode
public void testThisArgumentMoreSpecific(boolean cond) {
// Inlining method from Super will build it with `this` typed as Super.
@@ -655,7 +707,7 @@ public class Main {
/// CHECK-START: void Main.testExplicitArgumentMoreSpecific(SubclassA) inliner (after)
/// CHECK-DAG: <<Arg:l\d+>> ParameterValue klass:SubclassA
/// CHECK-DAG: <<NCArg:l\d+>> NullCheck [<<Arg>>] klass:SubclassA
- /// CHECK-DAG: InvokeVirtual [<<NCArg>>] method_name:Super.hashCode
+ /// CHECK-DAG: InvokeVirtual [<<NCArg>>] method_name:java.lang.Object.hashCode
public void testExplicitArgumentMoreSpecific(SubclassA obj) {
// Inlining a method will build it with reference types from its signature,
diff --git a/test/458-checker-instruct-simplification/build b/test/458-checker-instruct-simplification/build
new file mode 100755
index 0000000000..49292c9ac1
--- /dev/null
+++ b/test/458-checker-instruct-simplification/build
@@ -0,0 +1,23 @@
+#!/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.
+
+# This checker test is incompatible with jack bytecode output,
+# so force it to use javac/dx.
+export USE_JACK=false
+# Also disable desugar because it is missing in jack platform builds.
+export DESUGAR=false
+
+./default-build "$@"
diff --git a/test/458-checker-instruct-simplification/smali/SmaliTests.smali b/test/458-checker-instruct-simplification/smali/SmaliTests.smali
index 6845961f39..a8d7d94e46 100644
--- a/test/458-checker-instruct-simplification/smali/SmaliTests.smali
+++ b/test/458-checker-instruct-simplification/smali/SmaliTests.smali
@@ -327,3 +327,144 @@
return v0
.end method
+
+# Test simplification of the `~~var` pattern.
+# The transformation tested is implemented in `InstructionSimplifierVisitor::VisitNot`.
+
+## CHECK-START: long SmaliTests.NotNot1(long) instruction_simplifier (before)
+## CHECK-DAG: <<Arg:j\d+>> ParameterValue
+## CHECK-DAG: <<Not1:j\d+>> Not [<<Arg>>]
+## CHECK-DAG: <<Not2:j\d+>> Not [<<Not1>>]
+## CHECK-DAG: Return [<<Not2>>]
+
+## CHECK-START: long SmaliTests.NotNot1(long) instruction_simplifier (after)
+## CHECK-DAG: <<Arg:j\d+>> ParameterValue
+## CHECK-DAG: Return [<<Arg>>]
+
+## CHECK-START: long SmaliTests.NotNot1(long) instruction_simplifier (after)
+## CHECK-NOT: Not
+
+.method public static NotNot1(J)J
+ .registers 4
+ .param p0, "arg" # J
+
+ .prologue
+ sget-boolean v0, LMain;->doThrow:Z
+
+ # if (doThrow) throw new Error();
+ if-eqz v0, :cond_a
+ new-instance v0, Ljava/lang/Error;
+ invoke-direct {v0}, Ljava/lang/Error;-><init>()V
+ throw v0
+
+ :cond_a
+ # return ~~arg
+ not-long v0, p0
+ not-long v0, v0
+ return-wide v0
+.end method
+
+## CHECK-START: int SmaliTests.NotNot2(int) instruction_simplifier (before)
+## CHECK-DAG: <<Arg:i\d+>> ParameterValue
+## CHECK-DAG: <<Not1:i\d+>> Not [<<Arg>>]
+## CHECK-DAG: <<Not2:i\d+>> Not [<<Not1>>]
+## CHECK-DAG: <<Add:i\d+>> Add [<<Not2>>,<<Not1>>]
+## CHECK-DAG: Return [<<Add>>]
+
+## CHECK-START: int SmaliTests.NotNot2(int) instruction_simplifier (after)
+## CHECK-DAG: <<Arg:i\d+>> ParameterValue
+## CHECK-DAG: <<Not:i\d+>> Not [<<Arg>>]
+## CHECK-DAG: <<Add:i\d+>> Add [<<Arg>>,<<Not>>]
+## CHECK-DAG: Return [<<Add>>]
+
+## CHECK-START: int SmaliTests.NotNot2(int) instruction_simplifier (after)
+## CHECK: Not
+## CHECK-NOT: Not
+
+.method public static NotNot2(I)I
+ .registers 3
+ .param p0, "arg" # I
+
+ .prologue
+ sget-boolean v1, LMain;->doThrow:Z
+
+ # if (doThrow) throw new Error();
+ if-eqz v1, :cond_a
+ new-instance v1, Ljava/lang/Error;
+ invoke-direct {v1}, Ljava/lang/Error;-><init>()V
+ throw v1
+
+ :cond_a
+ # temp = ~arg; return temp + ~temp;
+ not-int v0, p0
+ not-int v1, v0
+ add-int/2addr v1, v0
+
+ return v1
+.end method
+
+# Test simplification of double Boolean negation. Note that sometimes
+# both negations can be removed but we only expect the simplifier to
+# remove the second.
+
+## CHECK-START: boolean SmaliTests.NotNotBool(boolean) instruction_simplifier (before)
+## CHECK-DAG: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+## CHECK-DAG: <<Result:z\d+>> InvokeStaticOrDirect
+## CHECK-DAG: <<NotResult:i\d+>> Xor [<<Result>>,<<Const1>>]
+## CHECK-DAG: Return [<<NotResult>>]
+
+## CHECK-START: boolean SmaliTests.NotNotBool(boolean) instruction_simplifier (after)
+## CHECK-DAG: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: <<Result:z\d+>> InvokeStaticOrDirect
+## CHECK-DAG: <<NotResult:z\d+>> BooleanNot [<<Result>>]
+## CHECK-DAG: Return [<<NotResult>>]
+
+## CHECK-START: boolean SmaliTests.NotNotBool(boolean) instruction_simplifier$after_inlining (before)
+## CHECK-DAG: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>]
+## CHECK-DAG: <<NotNotArg:z\d+>> BooleanNot [<<NotArg>>]
+## CHECK-DAG: Return [<<NotNotArg>>]
+
+## CHECK-START: boolean SmaliTests.NotNotBool(boolean) instruction_simplifier$after_inlining (after)
+## CHECK-DAG: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>]
+## CHECK-DAG: Return [<<Arg>>]
+
+## CHECK-START: boolean SmaliTests.NotNotBool(boolean) dead_code_elimination$final (after)
+## CHECK-DAG: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: Return [<<Arg>>]
+
+.method public static NegateValue(Z)Z
+ .registers 2
+ .param p0, "arg" # Z
+
+ .prologue
+
+ # return !arg
+ xor-int/lit8 v0, p0, 0x1
+ return v0
+.end method
+
+
+.method public static NotNotBool(Z)Z
+ .registers 2
+ .param p0, "arg" # Z
+
+ .prologue
+ sget-boolean v0, LMain;->doThrow:Z
+
+ # if (doThrow) throw new Error();
+ if-eqz v0, :cond_a
+ new-instance v0, Ljava/lang/Error;
+ invoke-direct {v0}, Ljava/lang/Error;-><init>()V
+ throw v0
+
+ :cond_a
+ # return !Negate(arg)
+ invoke-static {p0}, LSmaliTests;->NegateValue(Z)Z
+ move-result v0
+ xor-int/lit8 v0, v0, 0x1
+
+ return v0
+.end method
diff --git a/test/458-checker-instruct-simplification/src/Main.java b/test/458-checker-instruct-simplification/src/Main.java
index 529ea5ba9c..5c36ce9982 100644
--- a/test/458-checker-instruct-simplification/src/Main.java
+++ b/test/458-checker-instruct-simplification/src/Main.java
@@ -982,17 +982,18 @@ public class Main {
*/
/// CHECK-START: long Main.$noinline$NotNot1(long) instruction_simplifier (before)
- /// CHECK-DAG: <<Arg:j\d+>> ParameterValue
- /// CHECK-DAG: <<Not1:j\d+>> Not [<<Arg>>]
- /// CHECK-DAG: <<Not2:j\d+>> Not [<<Not1>>]
- /// CHECK-DAG: Return [<<Not2>>]
+ /// CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ /// CHECK-DAG: <<ConstNeg1:j\d+>> LongConstant -1
+ /// CHECK-DAG: <<Not1:j\d+>> Xor [<<Arg>>,<<ConstNeg1>>]
+ /// CHECK-DAG: <<Not2:j\d+>> Xor [<<Not1>>,<<ConstNeg1>>]
+ /// CHECK-DAG: Return [<<Not2>>]
/// CHECK-START: long Main.$noinline$NotNot1(long) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: Return [<<Arg>>]
/// CHECK-START: long Main.$noinline$NotNot1(long) instruction_simplifier (after)
- /// CHECK-NOT: Not
+ /// CHECK-NOT: Xor
public static long $noinline$NotNot1(long arg) {
if (doThrow) { throw new Error(); }
@@ -1000,11 +1001,12 @@ public class Main {
}
/// CHECK-START: int Main.$noinline$NotNot2(int) instruction_simplifier (before)
- /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
- /// CHECK-DAG: <<Not1:i\d+>> Not [<<Arg>>]
- /// CHECK-DAG: <<Not2:i\d+>> Not [<<Not1>>]
- /// CHECK-DAG: <<Add:i\d+>> Add [<<Not2>>,<<Not1>>]
- /// CHECK-DAG: Return [<<Add>>]
+ /// CHECK-DAG: <<Arg:i\d+>> ParameterValue
+ /// CHECK-DAG: <<ConstNeg1:i\d+>> IntConstant -1
+ /// CHECK-DAG: <<Not1:i\d+>> Xor [<<Arg>>,<<ConstNeg1>>]
+ /// CHECK-DAG: <<Not2:i\d+>> Xor [<<Not1>>,<<ConstNeg1>>]
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Not2>>,<<Not1>>]
+ /// CHECK-DAG: Return [<<Add>>]
/// CHECK-START: int Main.$noinline$NotNot2(int) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
@@ -1016,6 +1018,9 @@ public class Main {
/// CHECK: Not
/// CHECK-NOT: Not
+ /// CHECK-START: int Main.$noinline$NotNot2(int) instruction_simplifier (after)
+ /// CHECK-NOT: Xor
+
public static int $noinline$NotNot2(int arg) {
if (doThrow) { throw new Error(); }
int temp = ~arg;
@@ -1180,30 +1185,46 @@ public class Main {
/// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
- /// CHECK-DAG: <<Result:z\d+>> InvokeStaticOrDirect
- /// CHECK-DAG: <<NotResult:i\d+>> Xor [<<Result>>,<<Const1>>]
- /// CHECK-DAG: Return [<<NotResult>>]
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Result:z\d+>> InvokeStaticOrDirect method_name:Main.NegateValue
+ /// CHECK-DAG: <<NotResult:z\d+>> NotEqual [<<Result>>,<<Const1>>]
+ /// CHECK-DAG: If [<<NotResult>>]
+
+ /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier (after)
+ /// CHECK-NOT: NotEqual
/// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier (after)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<Result:z\d+>> InvokeStaticOrDirect
- /// CHECK-DAG: <<NotResult:z\d+>> BooleanNot [<<Result>>]
- /// CHECK-DAG: Return [<<NotResult>>]
+ /// CHECK-DAG: <<Result:z\d+>> InvokeStaticOrDirect method_name:Main.NegateValue
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Const1>>,<<Const0>>]
+ /// CHECK-DAG: Return [<<Phi>>]
/// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier$after_inlining (before)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>]
- /// CHECK-DAG: <<NotNotArg:z\d+>> BooleanNot [<<NotArg>>]
- /// CHECK-DAG: Return [<<NotNotArg>>]
+ /// CHECK-NOT: BooleanNot [<<Arg>>]
+ /// CHECK-NOT: Phi
+
+ /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Sel:i\d+>> Select [<<Const1>>,<<Const0>>,<<Arg>>]
+ /// CHECK-DAG: <<Sel2:i\d+>> Select [<<Const1>>,<<Const0>>,<<Sel>>]
+ /// CHECK-DAG: Return [<<Sel2>>]
/// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier$after_inlining (after)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>]
- /// CHECK-DAG: Return [<<Arg>>]
+ /// CHECK: BooleanNot [<<Arg>>]
+ /// CHECK-NEXT: Goto
+
+ /// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) instruction_simplifier$after_inlining (after)
+ /// CHECK-NOT: Select
/// CHECK-START: boolean Main.$noinline$NotNotBool(boolean) dead_code_elimination$final (after)
/// CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ /// CHECK-NOT: BooleanNot [<<Arg>>]
/// CHECK-DAG: Return [<<Arg>>]
public static boolean NegateValue(boolean arg) {
@@ -1882,7 +1903,18 @@ public class Main {
}
}
- public static int $noinline$runSmaliTestConst(String name, int arg) {
+ public static boolean $noinline$runSmaliTestBoolean(String name, boolean input) {
+ if (doThrow) { throw new Error(); }
+ try {
+ Class<?> c = Class.forName("SmaliTests");
+ Method m = c.getMethod(name, boolean.class);
+ return (Boolean) m.invoke(null, input);
+ } catch (Exception ex) {
+ throw new Error(ex);
+ }
+ }
+
+ public static int $noinline$runSmaliTestInt(String name, int arg) {
if (doThrow) { throw new Error(); }
try {
Class<?> c = Class.forName("SmaliTests");
@@ -1893,6 +1925,17 @@ public class Main {
}
}
+ public static long $noinline$runSmaliTestLong(String name, long arg) {
+ if (doThrow) { throw new Error(); }
+ try {
+ Class<?> c = Class.forName("SmaliTests");
+ Method m = c.getMethod(name, long.class);
+ return (Long) m.invoke(null, arg);
+ } catch (Exception ex) {
+ throw new Error(ex);
+ }
+ }
+
/// CHECK-START: int Main.$noinline$intUnnecessaryShiftMasking(int, int) instruction_simplifier (before)
/// CHECK: <<Value:i\d+>> ParameterValue
/// CHECK: <<Shift:i\d+>> ParameterValue
@@ -2188,7 +2231,9 @@ public class Main {
assertIntEquals(1, $noinline$NegSub1(arg, arg + 1));
assertIntEquals(1, $noinline$NegSub2(arg, arg + 1));
assertLongEquals(arg, $noinline$NotNot1(arg));
+ assertLongEquals(arg, $noinline$runSmaliTestLong("NotNot1", arg));
assertIntEquals(-1, $noinline$NotNot2(arg));
+ assertIntEquals(-1, $noinline$runSmaliTestInt("NotNot2", arg));
assertIntEquals(-(arg + arg + 1), $noinline$SubNeg1(arg, arg + 1));
assertIntEquals(-(arg + arg + 1), $noinline$SubNeg2(arg, arg + 1));
assertLongEquals(-(2 * arg + 1), $noinline$SubNeg3(arg, arg + 1));
@@ -2197,7 +2242,9 @@ public class Main {
assertBooleanEquals(false, $noinline$NotEqualBoolVsIntConst(false));
assertBooleanEquals(false, $noinline$NotEqualBoolVsIntConst(false));
assertBooleanEquals(true, $noinline$NotNotBool(true));
+ assertBooleanEquals(true, $noinline$runSmaliTestBoolean("NotNotBool", true));
assertBooleanEquals(false, $noinline$NotNotBool(false));
+ assertBooleanEquals(false, $noinline$runSmaliTestBoolean("NotNotBool", false));
assertFloatEquals(50.0f, $noinline$Div2(100.0f));
assertDoubleEquals(75.0, $noinline$Div2(150.0));
assertFloatEquals(-400.0f, $noinline$DivMP25(100.0f));
@@ -2309,11 +2356,11 @@ public class Main {
}
}
- assertIntEquals(0, $noinline$runSmaliTestConst("AddSubConst", 1));
- assertIntEquals(3, $noinline$runSmaliTestConst("SubAddConst", 2));
- assertIntEquals(-16, $noinline$runSmaliTestConst("SubSubConst1", 3));
- assertIntEquals(-5, $noinline$runSmaliTestConst("SubSubConst2", 4));
- assertIntEquals(26, $noinline$runSmaliTestConst("SubSubConst3", 5));
+ assertIntEquals(0, $noinline$runSmaliTestInt("AddSubConst", 1));
+ assertIntEquals(3, $noinline$runSmaliTestInt("SubAddConst", 2));
+ assertIntEquals(-16, $noinline$runSmaliTestInt("SubSubConst1", 3));
+ assertIntEquals(-5, $noinline$runSmaliTestInt("SubSubConst2", 4));
+ assertIntEquals(26, $noinline$runSmaliTestInt("SubSubConst3", 5));
assertIntEquals(0x5e6f7808, $noinline$intUnnecessaryShiftMasking(0xabcdef01, 3));
assertIntEquals(0x5e6f7808, $noinline$intUnnecessaryShiftMasking(0xabcdef01, 3 + 32));
assertLongEquals(0xffffffffffffeaf3L, $noinline$longUnnecessaryShiftMasking(0xabcdef0123456789L, 50));
diff --git a/test/463-checker-boolean-simplifier/build b/test/463-checker-boolean-simplifier/build
new file mode 100755
index 0000000000..49292c9ac1
--- /dev/null
+++ b/test/463-checker-boolean-simplifier/build
@@ -0,0 +1,23 @@
+#!/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.
+
+# This checker test is incompatible with jack bytecode output,
+# so force it to use javac/dx.
+export USE_JACK=false
+# Also disable desugar because it is missing in jack platform builds.
+export DESUGAR=false
+
+./default-build "$@"
diff --git a/test/463-checker-boolean-simplifier/smali/BooleanNotDx.smali b/test/463-checker-boolean-simplifier/smali/BooleanNotDx.smali
deleted file mode 100644
index 765d0eb663..0000000000
--- a/test/463-checker-boolean-simplifier/smali/BooleanNotDx.smali
+++ /dev/null
@@ -1,65 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-.class public LBooleanNotSmali;
-.super Ljava/lang/Object;
-
-#
-# Elementary test negating a boolean. Verifies that blocks are merged and
-# empty branches removed.
-#
-
-## CHECK-START: boolean BooleanNotSmali.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 [<<Const0>>,<<Const1>>]
-## CHECK-DAG: Return [<<Phi>>]
-
-## CHECK-START: boolean BooleanNotSmali.BooleanNot(boolean) select_generator (before)
-## CHECK: Goto
-## CHECK: Goto
-## CHECK: Goto
-## CHECK-NOT: Goto
-
-## CHECK-START: boolean BooleanNotSmali.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 BooleanNotSmali.BooleanNot(boolean) select_generator (after)
-## CHECK-NOT: If
-## CHECK-NOT: Phi
-
-## CHECK-START: boolean BooleanNotSmali.BooleanNot(boolean) select_generator (after)
-## CHECK: Goto
-## CHECK-NOT: Goto
-
-.method public static BooleanNot(Z)Z
- .registers 2
-
- if-eqz v1, :true_start
- const/4 v0, 0x0
-
-:return_start
- return v0
-
-:true_start
- const/4 v0, 0x1
- goto :return_start
-
-.end method
diff --git a/test/463-checker-boolean-simplifier/src/Main.java b/test/463-checker-boolean-simplifier/src/Main.java
index 9368488056..d1d02cdfee 100644
--- a/test/463-checker-boolean-simplifier/src/Main.java
+++ b/test/463-checker-boolean-simplifier/src/Main.java
@@ -32,14 +32,42 @@ public class Main {
}
}
- // Invoke a method written in smali that implements the boolean ! operator. This method
- // uses the if/else pattern generated by dx (while Jack generates a different pattern).
- // Since this method is in a smali-generated class, we invoke it through reflection.
- public static boolean SmaliBooleanNot(boolean x) throws Exception {
- Class<?> c = Class.forName("BooleanNotSmali");
- java.lang.reflect.Method method = c.getMethod("BooleanNot", boolean.class);
- Object retValue = method.invoke(null, new Object[] { Boolean.valueOf(x) });
- return ((Boolean) retValue).booleanValue();
+ /*
+ * 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;
}
/*
@@ -157,7 +185,11 @@ public class Main {
/// CHECK-NOT: BooleanNot
public static int NegatedCondition(boolean x) {
- return (x != false) ? 42 : 43;
+ if (x != false) {
+ return 42;
+ } else {
+ return 43;
+ }
}
/// CHECK-START: int Main.SimpleTrueBlock(boolean, int) select_generator (after)
@@ -221,7 +253,13 @@ public class Main {
/// CHECK-DAG: Return [<<Select123>>]
public static int ThreeBlocks(boolean x, boolean y) {
- return x ? 1 : (y ? 2 : 3);
+ if (x) {
+ return 1;
+ } else if (y) {
+ return 2;
+ } else {
+ return 3;
+ }
}
/// CHECK-START: int Main.MultiplePhis() select_generator (before)
@@ -254,10 +292,8 @@ public class Main {
while (y++ < 10) {
if (y > 1) {
x = 13;
- continue;
} else {
x = 42;
- continue;
}
}
return x;
@@ -330,8 +366,8 @@ public class Main {
}
public static void main(String[] args) throws Exception {
- assertBoolEquals(false, SmaliBooleanNot(true));
- assertBoolEquals(true, SmaliBooleanNot(false));
+ assertBoolEquals(false, BooleanNot(true));
+ assertBoolEquals(true, BooleanNot(false));
assertBoolEquals(true, GreaterThan(10, 5));
assertBoolEquals(false, GreaterThan(10, 10));
assertBoolEquals(false, GreaterThan(5, 10));
diff --git a/test/536-checker-intrinsic-optimization/build b/test/536-checker-intrinsic-optimization/build
new file mode 100755
index 0000000000..49292c9ac1
--- /dev/null
+++ b/test/536-checker-intrinsic-optimization/build
@@ -0,0 +1,23 @@
+#!/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.
+
+# This checker test is incompatible with jack bytecode output,
+# so force it to use javac/dx.
+export USE_JACK=false
+# Also disable desugar because it is missing in jack platform builds.
+export DESUGAR=false
+
+./default-build "$@"
diff --git a/test/536-checker-intrinsic-optimization/smali/SmaliTests.smali b/test/536-checker-intrinsic-optimization/smali/SmaliTests.smali
new file mode 100644
index 0000000000..6612faeb77
--- /dev/null
+++ b/test/536-checker-intrinsic-optimization/smali/SmaliTests.smali
@@ -0,0 +1,64 @@
+# 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.
+
+.class public LSmaliTests;
+.super Ljava/lang/Object;
+
+## CHECK-START: char SmaliTests.stringCharAtCatch(java.lang.String, int) instruction_simplifier (before)
+## CHECK-DAG: <<Char:c\d+>> InvokeVirtual intrinsic:StringCharAt
+## CHECK-DAG: Return [<<Char>>]
+
+## CHECK-START: char SmaliTests.stringCharAtCatch(java.lang.String, int) instruction_simplifier (after)
+## CHECK-DAG: <<String:l\d+>> ParameterValue
+## CHECK-DAG: <<Pos:i\d+>> ParameterValue
+## CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<String>>]
+## CHECK-DAG: <<Length:i\d+>> ArrayLength [<<NullCk>>] is_string_length:true
+## CHECK-DAG: <<Bounds:i\d+>> BoundsCheck [<<Pos>>,<<Length>>] is_string_char_at:true
+## CHECK-DAG: <<Char:c\d+>> ArrayGet [<<NullCk>>,<<Bounds>>] is_string_char_at:true
+## CHECK-DAG: Return [<<Char>>]
+
+## CHECK-START: char SmaliTests.stringCharAtCatch(java.lang.String, int) instruction_simplifier (after)
+## CHECK-NOT: InvokeVirtual intrinsic:StringCharAt
+.method public static stringCharAtCatch(Ljava/lang/String;I)C
+ .registers 4
+ .param p0, "s" # Ljava/lang/String;
+ .param p1, "pos" # I
+
+ .prologue
+
+ # if (doThrow) { throw new Error(); }
+ sget-boolean v1, LMain;->doThrow:Z
+ if-eqz v1, :doThrow_false
+ new-instance v1, Ljava/lang/Error;
+ invoke-direct {v1}, Ljava/lang/Error;-><init>()V
+ throw v1
+
+ :doThrow_false
+ :try_start
+ # tmp = s.charAt(pos)
+ invoke-virtual {p0, p1}, Ljava/lang/String;->charAt(I)C
+ :try_end
+ .catch Ljava/lang/StringIndexOutOfBoundsException; {:try_start .. :try_end} :catch
+
+ # return tmp
+ move-result v1
+ return v1
+
+ :catch
+ # return '\0'
+ move-exception v0
+ const/4 v1, 0x0
+ return v1
+.end method
+
diff --git a/test/536-checker-intrinsic-optimization/src/Main.java b/test/536-checker-intrinsic-optimization/src/Main.java
index e395e283e0..3dce23fb31 100644
--- a/test/536-checker-intrinsic-optimization/src/Main.java
+++ b/test/536-checker-intrinsic-optimization/src/Main.java
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+import java.lang.reflect.Method;
public class Main {
public static boolean doThrow = false;
@@ -83,7 +84,9 @@ public class Main {
}
assertCharEquals('7', $opt$noinline$stringCharAtCatch("0123456789", 7));
+ assertCharEquals('7', $noinline$runSmaliTest("stringCharAtCatch", "0123456789", 7));
assertCharEquals('\0', $opt$noinline$stringCharAtCatch("0123456789", 10));
+ assertCharEquals('\0', $noinline$runSmaliTest("stringCharAtCatch","0123456789", 10));
assertIntEquals('a' + 'b' + 'c', $opt$noinline$stringSumChars("abc"));
assertIntEquals('a' + 'b' + 'c', $opt$noinline$stringSumLeadingChars("abcdef", 3));
@@ -166,17 +169,21 @@ public class Main {
}
/// CHECK-START: char Main.$opt$noinline$stringCharAtCatch(java.lang.String, int) instruction_simplifier (before)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 0
/// CHECK-DAG: <<Char:c\d+>> InvokeVirtual intrinsic:StringCharAt
- /// CHECK-DAG: Return [<<Char>>]
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Char>>,<<Int>>]
+ /// CHECK-DAG: Return [<<Phi>>]
/// CHECK-START: char Main.$opt$noinline$stringCharAtCatch(java.lang.String, int) instruction_simplifier (after)
/// CHECK-DAG: <<String:l\d+>> ParameterValue
/// CHECK-DAG: <<Pos:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 0
/// CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<String>>]
/// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<NullCk>>] is_string_length:true
/// CHECK-DAG: <<Bounds:i\d+>> BoundsCheck [<<Pos>>,<<Length>>] is_string_char_at:true
/// CHECK-DAG: <<Char:c\d+>> ArrayGet [<<NullCk>>,<<Bounds>>] is_string_char_at:true
- /// CHECK-DAG: Return [<<Char>>]
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Char>>,<<Int>>]
+ /// CHECK-DAG: Return [<<Phi>>]
/// CHECK-START: char Main.$opt$noinline$stringCharAtCatch(java.lang.String, int) instruction_simplifier (after)
/// CHECK-NOT: InvokeVirtual intrinsic:StringCharAt
@@ -421,4 +428,14 @@ public class Main {
static String myString;
static Object myObject;
+
+ public static char $noinline$runSmaliTest(String name, String str, int pos) {
+ try {
+ Class<?> c = Class.forName("SmaliTests");
+ Method m = c.getMethod(name, String.class, int.class);
+ return (Character) m.invoke(null, str, pos);
+ } catch (Exception ex) {
+ throw new Error(ex);
+ }
+ }
}
diff --git a/test/537-checker-inline-and-unverified/build b/test/537-checker-inline-and-unverified/build
new file mode 100755
index 0000000000..49292c9ac1
--- /dev/null
+++ b/test/537-checker-inline-and-unverified/build
@@ -0,0 +1,23 @@
+#!/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.
+
+# This checker test is incompatible with jack bytecode output,
+# so force it to use javac/dx.
+export USE_JACK=false
+# Also disable desugar because it is missing in jack platform builds.
+export DESUGAR=false
+
+./default-build "$@"
diff --git a/test/537-checker-inline-and-unverified/src/Main.java b/test/537-checker-inline-and-unverified/src/Main.java
index b9d5fc98cc..bdc14b027c 100644
--- a/test/537-checker-inline-and-unverified/src/Main.java
+++ b/test/537-checker-inline-and-unverified/src/Main.java
@@ -45,14 +45,12 @@ public class Main {
}
public static boolean $opt$noinline$testNoInline() {
- boolean result = true;
try {
- result = (null instanceof InaccessibleClass);
- throw new Error("Unreachable");
+ return null instanceof InaccessibleClass;
} catch (IllegalAccessError e) {
// expected
}
- return result;
+ return false;
}
public static boolean $opt$inline$testInline() {
diff --git a/test/565-checker-doublenegbitwise/build b/test/565-checker-doublenegbitwise/build
new file mode 100755
index 0000000000..49292c9ac1
--- /dev/null
+++ b/test/565-checker-doublenegbitwise/build
@@ -0,0 +1,23 @@
+#!/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.
+
+# This checker test is incompatible with jack bytecode output,
+# so force it to use javac/dx.
+export USE_JACK=false
+# Also disable desugar because it is missing in jack platform builds.
+export DESUGAR=false
+
+./default-build "$@"
diff --git a/test/565-checker-doublenegbitwise/smali/SmaliTests.smali b/test/565-checker-doublenegbitwise/smali/SmaliTests.smali
new file mode 100644
index 0000000000..2e0802276e
--- /dev/null
+++ b/test/565-checker-doublenegbitwise/smali/SmaliTests.smali
@@ -0,0 +1,405 @@
+# 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.
+
+.class public LSmaliTests;
+.super Ljava/lang/Object;
+
+#
+# Test transformation of Not/Not/And into Or/Not.
+#
+
+## CHECK-START: int SmaliTests.$opt$noinline$andToOr(int, int) instruction_simplifier (before)
+## CHECK-DAG: <<P1:i\d+>> ParameterValue
+## CHECK-DAG: <<P2:i\d+>> ParameterValue
+## CHECK-DAG: <<Not1:i\d+>> Not [<<P1>>]
+## CHECK-DAG: <<Not2:i\d+>> Not [<<P2>>]
+## CHECK-DAG: <<And:i\d+>> And [<<Not1>>,<<Not2>>]
+## CHECK-DAG: Return [<<And>>]
+
+## CHECK-START: int SmaliTests.$opt$noinline$andToOr(int, int) instruction_simplifier (after)
+## CHECK-DAG: <<P1:i\d+>> ParameterValue
+## CHECK-DAG: <<P2:i\d+>> ParameterValue
+## CHECK-DAG: <<Or:i\d+>> Or [<<P1>>,<<P2>>]
+## CHECK-DAG: <<Not:i\d+>> Not [<<Or>>]
+## CHECK-DAG: Return [<<Not>>]
+
+## CHECK-START: int SmaliTests.$opt$noinline$andToOr(int, int) instruction_simplifier (after)
+## CHECK-DAG: Not
+## CHECK-NOT: Not
+
+## CHECK-START: int SmaliTests.$opt$noinline$andToOr(int, int) instruction_simplifier (after)
+## CHECK-NOT: And
+.method public static $opt$noinline$andToOr(II)I
+ .registers 4
+ .param p0, "a" # I
+ .param p1, "b" # I
+
+ .prologue
+ sget-boolean v0, LSmaliTests;->doThrow:Z
+ if-eqz v0, :cond_a
+ new-instance v0, Ljava/lang/Error;
+ invoke-direct {v0}, Ljava/lang/Error;-><init>()V
+ throw v0
+
+ :cond_a
+ # return ~a & ~b;
+ not-int v0, p0
+ not-int v1, p1
+ and-int/2addr v0, v1
+
+ return v0
+.end method
+
+# Test transformation of Not/Not/And into Or/Not for boolean negations.
+# Note that the graph before this instruction simplification pass does not
+# contain `HBooleanNot` instructions. This is because this transformation
+# follows the optimization of `HSelect` to `HBooleanNot` occurring in the
+# same pass.
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier (before)
+## CHECK-DAG: <<P1:z\d+>> ParameterValue
+## CHECK-DAG: <<P2:z\d+>> ParameterValue
+## CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+## CHECK-DAG: <<NotP1:i\d+>> Xor [<<P1>>,<<Const1>>]
+## CHECK-DAG: <<NotP2:i\d+>> Xor [<<P2>>,<<Const1>>]
+## CHECK-DAG: <<And:i\d+>> And [<<NotP1>>,<<NotP2>>]
+## CHECK-DAG: Return [<<And>>]
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier (after)
+## CHECK-DAG: <<Cond1:z\d+>> ParameterValue
+## CHECK-DAG: <<Cond2:z\d+>> ParameterValue
+## CHECK-DAG: <<Or:i\d+>> Or [<<Cond1>>,<<Cond2>>]
+## CHECK-DAG: <<BooleanNot:z\d+>> BooleanNot [<<Or>>]
+## CHECK-DAG: Return [<<BooleanNot>>]
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_bce (after)
+## CHECK-DAG: BooleanNot
+## CHECK-NOT: BooleanNot
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_bce (after)
+## CHECK-NOT: And
+.method public static $opt$noinline$booleanAndToOr(ZZ)Z
+ .registers 4
+ .param p0, "a" # Z
+ .param p1, "b" # Z
+
+ .prologue
+ sget-boolean v0, LSmaliTests;->doThrow:Z
+ if-eqz v0, :cond_a
+ new-instance v0, Ljava/lang/Error;
+ invoke-direct {v0}, Ljava/lang/Error;-><init>()V
+ throw v0
+
+ :cond_a
+ # return !a & !b;
+ xor-int/lit8 v0, p0, 0x1
+ xor-int/lit8 v1, p1, 0x1
+ and-int/2addr v0, v1
+ return v0
+.end method
+
+# Test transformation of Not/Not/Or into And/Not.
+
+## CHECK-START: long SmaliTests.$opt$noinline$orToAnd(long, long) instruction_simplifier (before)
+## CHECK-DAG: <<P1:j\d+>> ParameterValue
+## CHECK-DAG: <<P2:j\d+>> ParameterValue
+## CHECK-DAG: <<Not1:j\d+>> Not [<<P1>>]
+## CHECK-DAG: <<Not2:j\d+>> Not [<<P2>>]
+## CHECK-DAG: <<Or:j\d+>> Or [<<Not1>>,<<Not2>>]
+## CHECK-DAG: Return [<<Or>>]
+
+## CHECK-START: long SmaliTests.$opt$noinline$orToAnd(long, long) instruction_simplifier (after)
+## CHECK-DAG: <<P1:j\d+>> ParameterValue
+## CHECK-DAG: <<P2:j\d+>> ParameterValue
+## CHECK-DAG: <<And:j\d+>> And [<<P1>>,<<P2>>]
+## CHECK-DAG: <<Not:j\d+>> Not [<<And>>]
+## CHECK-DAG: Return [<<Not>>]
+
+## CHECK-START: long SmaliTests.$opt$noinline$orToAnd(long, long) instruction_simplifier (after)
+## CHECK-DAG: Not
+## CHECK-NOT: Not
+
+## CHECK-START: long SmaliTests.$opt$noinline$orToAnd(long, long) instruction_simplifier (after)
+## CHECK-NOT: Or
+.method public static $opt$noinline$orToAnd(JJ)J
+ .registers 8
+ .param p0, "a" # J
+ .param p2, "b" # J
+
+ .prologue
+ sget-boolean v0, LSmaliTests;->doThrow:Z
+ if-eqz v0, :cond_a
+ new-instance v0, Ljava/lang/Error;
+ invoke-direct {v0}, Ljava/lang/Error;-><init>()V
+ throw v0
+
+ :cond_a
+ # return ~a | ~b;
+ not-long v0, p0
+ not-long v2, p2
+ or-long/2addr v0, v2
+ return-wide v0
+.end method
+
+# Test transformation of Not/Not/Or into Or/And for boolean negations.
+# Note that the graph before this instruction simplification pass does not
+# contain `HBooleanNot` instructions. This is because this transformation
+# follows the optimization of `HSelect` to `HBooleanNot` occurring in the
+# same pass.
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier (before)
+## CHECK-DAG: <<P1:z\d+>> ParameterValue
+## CHECK-DAG: <<P2:z\d+>> ParameterValue
+## CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+## CHECK-DAG: <<NotP1:i\d+>> Xor [<<P1>>,<<Const1>>]
+## CHECK-DAG: <<NotP2:i\d+>> Xor [<<P2>>,<<Const1>>]
+## CHECK-DAG: <<Or:i\d+>> Or [<<NotP1>>,<<NotP2>>]
+## CHECK-DAG: Return [<<Or>>]
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier (after)
+## CHECK-DAG: <<Cond1:z\d+>> ParameterValue
+## CHECK-DAG: <<Cond2:z\d+>> ParameterValue
+## CHECK-DAG: <<And:i\d+>> And [<<Cond1>>,<<Cond2>>]
+## CHECK-DAG: <<BooleanNot:z\d+>> BooleanNot [<<And>>]
+## CHECK-DAG: Return [<<BooleanNot>>]
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_bce (after)
+## CHECK-DAG: BooleanNot
+## CHECK-NOT: BooleanNot
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_bce (after)
+## CHECK-NOT: Or
+.method public static $opt$noinline$booleanOrToAnd(ZZ)Z
+ .registers 4
+ .param p0, "a" # Z
+ .param p1, "b" # Z
+
+ .prologue
+ sget-boolean v0, LSmaliTests;->doThrow:Z
+ if-eqz v0, :cond_a
+ new-instance v0, Ljava/lang/Error;
+ invoke-direct {v0}, Ljava/lang/Error;-><init>()V
+ throw v0
+
+ :cond_a
+ # return !a | !b;
+ xor-int/lit8 v0, p0, 0x1
+ xor-int/lit8 v1, p1, 0x1
+ or-int/2addr v0, v1
+ return v0
+.end method
+
+# Test that the transformation copes with inputs being separated from the
+# bitwise operations.
+# This is a regression test. The initial logic was inserting the new bitwise
+# operation incorrectly.
+
+## CHECK-START: int SmaliTests.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (before)
+## CHECK-DAG: <<P1:i\d+>> ParameterValue
+## CHECK-DAG: <<P2:i\d+>> ParameterValue
+## CHECK-DAG: <<Cst1:i\d+>> IntConstant 1
+## CHECK-DAG: <<AddP1:i\d+>> Add [<<P1>>,<<Cst1>>]
+## CHECK-DAG: <<Not1:i\d+>> Not [<<AddP1>>]
+## CHECK-DAG: <<AddP2:i\d+>> Add [<<P2>>,<<Cst1>>]
+## CHECK-DAG: <<Not2:i\d+>> Not [<<AddP2>>]
+## CHECK-DAG: <<Or:i\d+>> Or [<<Not1>>,<<Not2>>]
+## CHECK-DAG: Return [<<Or>>]
+
+## CHECK-START: int SmaliTests.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after)
+## CHECK-DAG: <<P1:i\d+>> ParameterValue
+## CHECK-DAG: <<P2:i\d+>> ParameterValue
+## CHECK-DAG: <<Cst1:i\d+>> IntConstant 1
+## CHECK-DAG: <<AddP1:i\d+>> Add [<<P1>>,<<Cst1>>]
+## CHECK-DAG: <<AddP2:i\d+>> Add [<<P2>>,<<Cst1>>]
+## CHECK-DAG: <<And:i\d+>> And [<<AddP1>>,<<AddP2>>]
+## CHECK-DAG: <<Not:i\d+>> Not [<<And>>]
+## CHECK-DAG: Return [<<Not>>]
+
+## CHECK-START: int SmaliTests.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after)
+## CHECK-DAG: Not
+## CHECK-NOT: Not
+
+## CHECK-START: int SmaliTests.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after)
+## CHECK-NOT: Or
+.method public static $opt$noinline$regressInputsAway(II)I
+ .registers 7
+ .param p0, "a" # I
+ .param p1, "b" # I
+
+ .prologue
+ sget-boolean v4, LSmaliTests;->doThrow:Z
+ if-eqz v4, :cond_a
+ new-instance v4, Ljava/lang/Error;
+ invoke-direct {v4}, Ljava/lang/Error;-><init>()V
+ throw v4
+
+ :cond_a
+ # int a1 = a + 1;
+ add-int/lit8 v0, p0, 0x1
+ # int not_a1 = ~a1;
+ not-int v2, v0
+ # int b1 = b + 1;
+ add-int/lit8 v1, p1, 0x1
+ # int not_b1 = ~b1;
+ not-int v3, v1
+
+ # return not_a1 | not_b1
+ or-int v4, v2, v3
+ return v4
+.end method
+
+# Test transformation of Not/Not/Xor into Xor.
+
+# See first note above.
+## CHECK-START: int SmaliTests.$opt$noinline$notXorToXor(int, int) instruction_simplifier (before)
+## CHECK-DAG: <<P1:i\d+>> ParameterValue
+## CHECK-DAG: <<P2:i\d+>> ParameterValue
+## CHECK-DAG: <<Not1:i\d+>> Not [<<P1>>]
+## CHECK-DAG: <<Not2:i\d+>> Not [<<P2>>]
+## CHECK-DAG: <<Xor:i\d+>> Xor [<<Not1>>,<<Not2>>]
+## CHECK-DAG: Return [<<Xor>>]
+
+## CHECK-START: int SmaliTests.$opt$noinline$notXorToXor(int, int) instruction_simplifier (after)
+## CHECK-DAG: <<P1:i\d+>> ParameterValue
+## CHECK-DAG: <<P2:i\d+>> ParameterValue
+## CHECK-DAG: <<Xor:i\d+>> Xor [<<P1>>,<<P2>>]
+## CHECK-DAG: Return [<<Xor>>]
+
+## CHECK-START: int SmaliTests.$opt$noinline$notXorToXor(int, int) instruction_simplifier (after)
+## CHECK-NOT: Not
+.method public static $opt$noinline$notXorToXor(II)I
+ .registers 4
+ .param p0, "a" # I
+ .param p1, "b" # I
+
+ .prologue
+ sget-boolean v0, LSmaliTests;->doThrow:Z
+ if-eqz v0, :cond_a
+ new-instance v0, Ljava/lang/Error;
+ invoke-direct {v0}, Ljava/lang/Error;-><init>()V
+ throw v0
+
+ :cond_a
+ # return ~a ^ ~b;
+ not-int v0, p0
+ not-int v1, p1
+ xor-int/2addr v0, v1
+ return v0
+.end method
+
+# Test transformation of Not/Not/Xor into Xor for boolean negations.
+# Note that the graph before this instruction simplification pass does not
+# contain `HBooleanNot` instructions. This is because this transformation
+# follows the optimization of `HSelect` to `HBooleanNot` occurring in the
+# same pass.
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier (before)
+## CHECK-DAG: <<P1:z\d+>> ParameterValue
+## CHECK-DAG: <<P2:z\d+>> ParameterValue
+## CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+## CHECK-DAG: <<NotP1:i\d+>> Xor [<<P1>>,<<Const1>>]
+## CHECK-DAG: <<NotP2:i\d+>> Xor [<<P2>>,<<Const1>>]
+## CHECK-DAG: <<Xor:i\d+>> Xor [<<NotP1>>,<<NotP2>>]
+## CHECK-DAG: Return [<<Xor>>]
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier (after)
+## CHECK-DAG: <<Cond1:z\d+>> ParameterValue
+## CHECK-DAG: <<Cond2:z\d+>> ParameterValue
+## CHECK-DAG: <<Xor:i\d+>> Xor [<<Cond1>>,<<Cond2>>]
+## CHECK-DAG: Return [<<Xor>>]
+
+## CHECK-START: boolean SmaliTests.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_bce (after)
+## CHECK-NOT: BooleanNot
+.method public static $opt$noinline$booleanNotXorToXor(ZZ)Z
+ .registers 4
+ .param p0, "a" # Z
+ .param p1, "b" # Z
+
+ .prologue
+ sget-boolean v0, LSmaliTests;->doThrow:Z
+ if-eqz v0, :cond_a
+ new-instance v0, Ljava/lang/Error;
+ invoke-direct {v0}, Ljava/lang/Error;-><init>()V
+ throw v0
+
+ :cond_a
+ # return !a ^ !b;
+ xor-int/lit8 v0, p0, 0x1
+ xor-int/lit8 v1, p1, 0x1
+ xor-int/2addr v0, v1
+ return v0
+.end method
+
+# Check that no transformation is done when one Not has multiple uses.
+
+## CHECK-START: int SmaliTests.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (before)
+## CHECK-DAG: <<P1:i\d+>> ParameterValue
+## CHECK-DAG: <<P2:i\d+>> ParameterValue
+## CHECK-DAG: <<One:i\d+>> IntConstant 1
+## CHECK-DAG: <<Not2:i\d+>> Not [<<P2>>]
+## CHECK-DAG: <<And2:i\d+>> And [<<Not2>>,<<One>>]
+## CHECK-DAG: <<Not1:i\d+>> Not [<<P1>>]
+## CHECK-DAG: <<And1:i\d+>> And [<<Not1>>,<<Not2>>]
+## CHECK-DAG: <<Add:i\d+>> Add [<<And2>>,<<And1>>]
+## CHECK-DAG: Return [<<Add>>]
+
+## CHECK-START: int SmaliTests.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (after)
+## CHECK-DAG: <<P1:i\d+>> ParameterValue
+## CHECK-DAG: <<P2:i\d+>> ParameterValue
+## CHECK-DAG: <<One:i\d+>> IntConstant 1
+## CHECK-DAG: <<Not2:i\d+>> Not [<<P2>>]
+## CHECK-DAG: <<And2:i\d+>> And [<<Not2>>,<<One>>]
+## CHECK-DAG: <<Not1:i\d+>> Not [<<P1>>]
+## CHECK-DAG: <<And1:i\d+>> And [<<Not1>>,<<Not2>>]
+## CHECK-DAG: <<Add:i\d+>> Add [<<And2>>,<<And1>>]
+## CHECK-DAG: Return [<<Add>>]
+
+## CHECK-START: int SmaliTests.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (after)
+## CHECK-NOT: Or
+.method public static $opt$noinline$notMultipleUses(II)I
+ .registers 5
+ .param p0, "a" # I
+ .param p1, "b" # I
+
+ .prologue
+ sget-boolean v1, LSmaliTests;->doThrow:Z
+ if-eqz v1, :cond_a
+ new-instance v1, Ljava/lang/Error;
+ invoke-direct {v1}, Ljava/lang/Error;-><init>()V
+ throw v1
+
+ :cond_a
+ # int tmp = ~b;
+ not-int v0, p1
+ # return (tmp & 0x1) + (~a & tmp);
+ and-int/lit8 v1, v0, 0x1
+ not-int v2, p0
+ and-int/2addr v2, v0
+ add-int/2addr v1, v2
+ return v1
+.end method
+
+# static fields
+.field static doThrow:Z # boolean
+
+# direct methods
+.method static constructor <clinit>()V
+ .registers 1
+
+ .prologue
+ # doThrow = false
+ const/4 v0, 0x0
+ sput-boolean v0, LSmaliTests;->doThrow:Z
+ return-void
+.end method
diff --git a/test/565-checker-doublenegbitwise/src/Main.java b/test/565-checker-doublenegbitwise/src/Main.java
index 5ccc648076..816cafc2ba 100644
--- a/test/565-checker-doublenegbitwise/src/Main.java
+++ b/test/565-checker-doublenegbitwise/src/Main.java
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+import java.lang.reflect.Method;
+
public class Main {
// A dummy value to defeat inlining of these routines.
@@ -31,31 +33,53 @@ public class Main {
}
}
+ public static void assertEquals(boolean expected, boolean result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ public static <T> T $noinline$runSmaliTest(String name, Class<T> klass, T input1, T input2) {
+ if (doThrow) { throw new Error(); }
+
+ Class<T> inputKlass = (Class<T>)input1.getClass();
+ try {
+ Class<?> c = Class.forName("SmaliTests");
+ Method m = c.getMethod(name, klass, klass);
+ return inputKlass.cast(m.invoke(null, input1, input2));
+ } catch (Exception ex) {
+ throw new Error(ex);
+ }
+ }
+
/**
* Test transformation of Not/Not/And into Or/Not.
*/
+ // Note: before the instruction_simplifier pass, Xor's are used instead of
+ // Not's (the simplification happens during the same pass).
/// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (before)
- /// CHECK: <<P1:i\d+>> ParameterValue
- /// CHECK: <<P2:i\d+>> ParameterValue
- /// CHECK: <<Not1:i\d+>> Not [<<P1>>]
- /// CHECK: <<Not2:i\d+>> Not [<<P2>>]
- /// CHECK: <<And:i\d+>> And [<<Not1>>,<<Not2>>]
- /// CHECK: Return [<<And>>]
+ /// CHECK-DAG: <<P1:i\d+>> ParameterValue
+ /// CHECK-DAG: <<P2:i\d+>> ParameterValue
+ /// CHECK-DAG: <<CstM1:i\d+>> IntConstant -1
+ /// CHECK-DAG: <<Not1:i\d+>> Xor [<<P1>>,<<CstM1>>]
+ /// CHECK-DAG: <<Not2:i\d+>> Xor [<<P2>>,<<CstM1>>]
+ /// CHECK-DAG: <<And:i\d+>> And [<<Not1>>,<<Not2>>]
+ /// CHECK-DAG: Return [<<And>>]
/// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (after)
- /// CHECK: <<P1:i\d+>> ParameterValue
- /// CHECK: <<P2:i\d+>> ParameterValue
- /// CHECK: <<Or:i\d+>> Or [<<P1>>,<<P2>>]
- /// CHECK: <<Not:i\d+>> Not [<<Or>>]
- /// CHECK: Return [<<Not>>]
+ /// CHECK-DAG: <<P1:i\d+>> ParameterValue
+ /// CHECK-DAG: <<P2:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Or:i\d+>> Or [<<P1>>,<<P2>>]
+ /// CHECK-DAG: <<Not:i\d+>> Not [<<Or>>]
+ /// CHECK-DAG: Return [<<Not>>]
/// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (after)
- /// CHECK: Not
- /// CHECK-NOT: Not
+ /// CHECK-DAG: Not
+ /// CHECK-NOT: Not
/// CHECK-START: int Main.$opt$noinline$andToOr(int, int) instruction_simplifier (after)
- /// CHECK-NOT: And
+ /// CHECK-NOT: And
public static int $opt$noinline$andToOr(int a, int b) {
if (doThrow) throw new Error();
@@ -70,28 +94,29 @@ public class Main {
* same pass.
*/
- /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier (before)
- /// CHECK: <<P1:z\d+>> ParameterValue
- /// CHECK: <<P2:z\d+>> ParameterValue
- /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
- /// CHECK-DAG: <<NotP1:i\d+>> Xor [<<P1>>,<<Const1>>]
- /// CHECK-DAG: <<NotP2:i\d+>> Xor [<<P2>>,<<Const1>>]
- /// CHECK: <<And:i\d+>> And [<<NotP1>>,<<NotP2>>]
- /// CHECK: Return [<<And>>]
-
- /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier (after)
- /// CHECK: <<Cond1:z\d+>> ParameterValue
- /// CHECK: <<Cond2:z\d+>> ParameterValue
- /// CHECK: <<Or:i\d+>> Or [<<Cond1>>,<<Cond2>>]
- /// CHECK: <<BooleanNot:z\d+>> BooleanNot [<<Or>>]
- /// CHECK: Return [<<BooleanNot>>]
+ /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<P1:z\d+>> ParameterValue
+ /// CHECK-DAG: <<P2:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Select1:i\d+>> Select [<<Const1>>,<<Const0>>,<<P1>>]
+ /// CHECK-DAG: <<Select2:i\d+>> Select [<<Const1>>,<<Const0>>,<<P2>>]
+ /// CHECK-DAG: <<And:i\d+>> And [<<Select2>>,<<Select1>>]
+ /// CHECK-DAG: Return [<<And>>]
+
+ /// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Cond1:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Cond2:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Or:i\d+>> Or [<<Cond2>>,<<Cond1>>]
+ /// CHECK-DAG: <<BooleanNot:z\d+>> BooleanNot [<<Or>>]
+ /// CHECK-DAG: Return [<<BooleanNot>>]
/// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_bce (after)
- /// CHECK: BooleanNot
- /// CHECK-NOT: BooleanNot
+ /// CHECK-DAG: BooleanNot
+ /// CHECK-NOT: BooleanNot
/// CHECK-START: boolean Main.$opt$noinline$booleanAndToOr(boolean, boolean) instruction_simplifier$after_bce (after)
- /// CHECK-NOT: And
+ /// CHECK-NOT: And
public static boolean $opt$noinline$booleanAndToOr(boolean a, boolean b) {
if (doThrow) throw new Error();
@@ -102,27 +127,30 @@ public class Main {
* Test transformation of Not/Not/Or into And/Not.
*/
+ // See note above.
+ // The second Xor has its arguments reversed for no obvious reason.
/// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (before)
- /// CHECK: <<P1:j\d+>> ParameterValue
- /// CHECK: <<P2:j\d+>> ParameterValue
- /// CHECK: <<Not1:j\d+>> Not [<<P1>>]
- /// CHECK: <<Not2:j\d+>> Not [<<P2>>]
- /// CHECK: <<Or:j\d+>> Or [<<Not1>>,<<Not2>>]
- /// CHECK: Return [<<Or>>]
+ /// CHECK-DAG: <<P1:j\d+>> ParameterValue
+ /// CHECK-DAG: <<P2:j\d+>> ParameterValue
+ /// CHECK-DAG: <<CstM1:j\d+>> LongConstant -1
+ /// CHECK-DAG: <<Not1:j\d+>> Xor [<<P1>>,<<CstM1>>]
+ /// CHECK-DAG: <<Not2:j\d+>> Xor [<<CstM1>>,<<P2>>]
+ /// CHECK-DAG: <<Or:j\d+>> Or [<<Not1>>,<<Not2>>]
+ /// CHECK-DAG: Return [<<Or>>]
/// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (after)
- /// CHECK: <<P1:j\d+>> ParameterValue
- /// CHECK: <<P2:j\d+>> ParameterValue
- /// CHECK: <<And:j\d+>> And [<<P1>>,<<P2>>]
- /// CHECK: <<Not:j\d+>> Not [<<And>>]
- /// CHECK: Return [<<Not>>]
+ /// CHECK-DAG: <<P1:j\d+>> ParameterValue
+ /// CHECK-DAG: <<P2:j\d+>> ParameterValue
+ /// CHECK-DAG: <<And:j\d+>> And [<<P1>>,<<P2>>]
+ /// CHECK-DAG: <<Not:j\d+>> Not [<<And>>]
+ /// CHECK-DAG: Return [<<Not>>]
/// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (after)
- /// CHECK: Not
- /// CHECK-NOT: Not
+ /// CHECK-DAG: Not
+ /// CHECK-NOT: Not
/// CHECK-START: long Main.$opt$noinline$orToAnd(long, long) instruction_simplifier (after)
- /// CHECK-NOT: Or
+ /// CHECK-NOT: Or
public static long $opt$noinline$orToAnd(long a, long b) {
if (doThrow) throw new Error();
@@ -137,28 +165,29 @@ public class Main {
* same pass.
*/
- /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier (before)
- /// CHECK: <<P1:z\d+>> ParameterValue
- /// CHECK: <<P2:z\d+>> ParameterValue
- /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
- /// CHECK: <<NotP1:i\d+>> Xor [<<P1>>,<<Const1>>]
- /// CHECK: <<NotP2:i\d+>> Xor [<<P2>>,<<Const1>>]
- /// CHECK: <<Or:i\d+>> Or [<<NotP1>>,<<NotP2>>]
- /// CHECK: Return [<<Or>>]
-
- /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier (after)
- /// CHECK: <<Cond1:z\d+>> ParameterValue
- /// CHECK: <<Cond2:z\d+>> ParameterValue
- /// CHECK: <<And:i\d+>> And [<<Cond1>>,<<Cond2>>]
- /// CHECK: <<BooleanNot:z\d+>> BooleanNot [<<And>>]
- /// CHECK: Return [<<BooleanNot>>]
+ /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<P1:z\d+>> ParameterValue
+ /// CHECK-DAG: <<P2:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Select1:i\d+>> Select [<<Const1>>,<<Const0>>,<<P1>>]
+ /// CHECK-DAG: <<Select2:i\d+>> Select [<<Const1>>,<<Const0>>,<<P2>>]
+ /// CHECK-DAG: <<Or:i\d+>> Or [<<Select2>>,<<Select1>>]
+ /// CHECK-DAG: Return [<<Or>>]
+
+ /// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Cond1:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Cond2:z\d+>> ParameterValue
+ /// CHECK-DAG: <<And:i\d+>> And [<<Cond2>>,<<Cond1>>]
+ /// CHECK-DAG: <<BooleanNot:z\d+>> BooleanNot [<<And>>]
+ /// CHECK-DAG: Return [<<BooleanNot>>]
/// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_bce (after)
- /// CHECK: BooleanNot
- /// CHECK-NOT: BooleanNot
+ /// CHECK-DAG: BooleanNot
+ /// CHECK-NOT: BooleanNot
/// CHECK-START: boolean Main.$opt$noinline$booleanOrToAnd(boolean, boolean) instruction_simplifier$after_bce (after)
- /// CHECK-NOT: Or
+ /// CHECK-NOT: Or
public static boolean $opt$noinline$booleanOrToAnd(boolean a, boolean b) {
if (doThrow) throw new Error();
@@ -173,32 +202,33 @@ public class Main {
*/
/// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (before)
- /// CHECK: <<P1:i\d+>> ParameterValue
- /// CHECK: <<P2:i\d+>> ParameterValue
- /// CHECK: <<Cst1:i\d+>> IntConstant 1
- /// CHECK: <<AddP1:i\d+>> Add [<<P1>>,<<Cst1>>]
- /// CHECK: <<Not1:i\d+>> Not [<<AddP1>>]
- /// CHECK: <<AddP2:i\d+>> Add [<<P2>>,<<Cst1>>]
- /// CHECK: <<Not2:i\d+>> Not [<<AddP2>>]
- /// CHECK: <<Or:i\d+>> Or [<<Not1>>,<<Not2>>]
- /// CHECK: Return [<<Or>>]
+ /// CHECK-DAG: <<P1:i\d+>> ParameterValue
+ /// CHECK-DAG: <<P2:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Cst1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<CstM1:i\d+>> IntConstant -1
+ /// CHECK-DAG: <<AddP1:i\d+>> Add [<<P1>>,<<Cst1>>]
+ /// CHECK-DAG: <<Not1:i\d+>> Xor [<<AddP1>>,<<CstM1>>]
+ /// CHECK-DAG: <<AddP2:i\d+>> Add [<<P2>>,<<Cst1>>]
+ /// CHECK-DAG: <<Not2:i\d+>> Xor [<<AddP2>>,<<CstM1>>]
+ /// CHECK-DAG: <<Or:i\d+>> Or [<<Not1>>,<<Not2>>]
+ /// CHECK-DAG: Return [<<Or>>]
/// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after)
- /// CHECK: <<P1:i\d+>> ParameterValue
- /// CHECK: <<P2:i\d+>> ParameterValue
- /// CHECK: <<Cst1:i\d+>> IntConstant 1
- /// CHECK: <<AddP1:i\d+>> Add [<<P1>>,<<Cst1>>]
- /// CHECK: <<AddP2:i\d+>> Add [<<P2>>,<<Cst1>>]
- /// CHECK: <<And:i\d+>> And [<<AddP1>>,<<AddP2>>]
- /// CHECK: <<Not:i\d+>> Not [<<And>>]
- /// CHECK: Return [<<Not>>]
+ /// CHECK-DAG: <<P1:i\d+>> ParameterValue
+ /// CHECK-DAG: <<P2:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Cst1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<AddP1:i\d+>> Add [<<P1>>,<<Cst1>>]
+ /// CHECK-DAG: <<AddP2:i\d+>> Add [<<P2>>,<<Cst1>>]
+ /// CHECK-DAG: <<And:i\d+>> And [<<AddP1>>,<<AddP2>>]
+ /// CHECK-DAG: <<Not:i\d+>> Not [<<And>>]
+ /// CHECK-DAG: Return [<<Not>>]
/// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after)
- /// CHECK: Not
- /// CHECK-NOT: Not
+ /// CHECK-DAG: Not
+ /// CHECK-NOT: Not
/// CHECK-START: int Main.$opt$noinline$regressInputsAway(int, int) instruction_simplifier (after)
- /// CHECK-NOT: Or
+ /// CHECK-NOT: Or
public static int $opt$noinline$regressInputsAway(int a, int b) {
if (doThrow) throw new Error();
@@ -215,21 +245,22 @@ public class Main {
// See first note above.
/// CHECK-START: int Main.$opt$noinline$notXorToXor(int, int) instruction_simplifier (before)
- /// CHECK: <<P1:i\d+>> ParameterValue
- /// CHECK: <<P2:i\d+>> ParameterValue
- /// CHECK: <<Not1:i\d+>> Not [<<P1>>]
- /// CHECK: <<Not2:i\d+>> Not [<<P2>>]
- /// CHECK: <<Xor:i\d+>> Xor [<<Not1>>,<<Not2>>]
- /// CHECK: Return [<<Xor>>]
+ /// CHECK-DAG: <<P1:i\d+>> ParameterValue
+ /// CHECK-DAG: <<P2:i\d+>> ParameterValue
+ /// CHECK-DAG: <<CstM1:i\d+>> IntConstant -1
+ /// CHECK-DAG: <<Not1:i\d+>> Xor [<<P1>>,<<CstM1>>]
+ /// CHECK-DAG: <<Not2:i\d+>> Xor [<<P2>>,<<CstM1>>]
+ /// CHECK-DAG: <<Xor:i\d+>> Xor [<<Not1>>,<<Not2>>]
+ /// CHECK-DAG: Return [<<Xor>>]
/// CHECK-START: int Main.$opt$noinline$notXorToXor(int, int) instruction_simplifier (after)
- /// CHECK: <<P1:i\d+>> ParameterValue
- /// CHECK: <<P2:i\d+>> ParameterValue
- /// CHECK: <<Xor:i\d+>> Xor [<<P1>>,<<P2>>]
- /// CHECK: Return [<<Xor>>]
+ /// CHECK-DAG: <<P1:i\d+>> ParameterValue
+ /// CHECK-DAG: <<P2:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Xor:i\d+>> Xor [<<P1>>,<<P2>>]
+ /// CHECK-DAG: Return [<<Xor>>]
/// CHECK-START: int Main.$opt$noinline$notXorToXor(int, int) instruction_simplifier (after)
- /// CHECK-NOT: Not
+ /// CHECK-NOT: Not
public static int $opt$noinline$notXorToXor(int a, int b) {
if (doThrow) throw new Error();
@@ -244,23 +275,24 @@ public class Main {
* same pass.
*/
- /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier (before)
- /// CHECK: <<P1:z\d+>> ParameterValue
- /// CHECK: <<P2:z\d+>> ParameterValue
- /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
- /// CHECK: <<NotP1:i\d+>> Xor [<<P1>>,<<Const1>>]
- /// CHECK: <<NotP2:i\d+>> Xor [<<P2>>,<<Const1>>]
- /// CHECK: <<Xor:i\d+>> Xor [<<NotP1>>,<<NotP2>>]
- /// CHECK: Return [<<Xor>>]
-
- /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier (after)
- /// CHECK: <<Cond1:z\d+>> ParameterValue
- /// CHECK: <<Cond2:z\d+>> ParameterValue
- /// CHECK: <<Xor:i\d+>> Xor [<<Cond1>>,<<Cond2>>]
- /// CHECK: Return [<<Xor>>]
+ /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_inlining (before)
+ /// CHECK-DAG: <<P1:z\d+>> ParameterValue
+ /// CHECK-DAG: <<P2:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Select1:i\d+>> Select [<<Const1>>,<<Const0>>,<<P1>>]
+ /// CHECK-DAG: <<Select2:i\d+>> Select [<<Const1>>,<<Const0>>,<<P2>>]
+ /// CHECK-DAG: <<Xor:i\d+>> Xor [<<Select2>>,<<Select1>>]
+ /// CHECK-DAG: Return [<<Xor>>]
+
+ /// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_inlining (after)
+ /// CHECK-DAG: <<Cond1:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Cond2:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Xor:i\d+>> Xor [<<Cond2>>,<<Cond1>>]
+ /// CHECK-DAG: Return [<<Xor>>]
/// CHECK-START: boolean Main.$opt$noinline$booleanNotXorToXor(boolean, boolean) instruction_simplifier$after_bce (after)
- /// CHECK-NOT: BooleanNot
+ /// CHECK-NOT: BooleanNot
public static boolean $opt$noinline$booleanNotXorToXor(boolean a, boolean b) {
if (doThrow) throw new Error();
@@ -272,29 +304,30 @@ public class Main {
*/
/// CHECK-START: int Main.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (before)
- /// CHECK: <<P1:i\d+>> ParameterValue
- /// CHECK: <<P2:i\d+>> ParameterValue
- /// CHECK: <<One:i\d+>> IntConstant 1
- /// CHECK: <<Not2:i\d+>> Not [<<P2>>]
- /// CHECK: <<And2:i\d+>> And [<<Not2>>,<<One>>]
- /// CHECK: <<Not1:i\d+>> Not [<<P1>>]
- /// CHECK: <<And1:i\d+>> And [<<Not1>>,<<Not2>>]
- /// CHECK: <<Add:i\d+>> Add [<<And2>>,<<And1>>]
- /// CHECK: Return [<<Add>>]
+ /// CHECK-DAG: <<P1:i\d+>> ParameterValue
+ /// CHECK-DAG: <<P2:i\d+>> ParameterValue
+ /// CHECK-DAG: <<CstM1:i\d+>> IntConstant -1
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Not2:i\d+>> Xor [<<P2>>,<<CstM1>>]
+ /// CHECK-DAG: <<And2:i\d+>> And [<<Not2>>,<<One>>]
+ /// CHECK-DAG: <<Not1:i\d+>> Xor [<<P1>>,<<CstM1>>]
+ /// CHECK-DAG: <<And1:i\d+>> And [<<Not1>>,<<Not2>>]
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<And2>>,<<And1>>]
+ /// CHECK-DAG: Return [<<Add>>]
/// CHECK-START: int Main.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (after)
- /// CHECK: <<P1:i\d+>> ParameterValue
- /// CHECK: <<P2:i\d+>> ParameterValue
- /// CHECK: <<One:i\d+>> IntConstant 1
- /// CHECK: <<Not2:i\d+>> Not [<<P2>>]
- /// CHECK: <<And2:i\d+>> And [<<Not2>>,<<One>>]
- /// CHECK: <<Not1:i\d+>> Not [<<P1>>]
- /// CHECK: <<And1:i\d+>> And [<<Not1>>,<<Not2>>]
- /// CHECK: <<Add:i\d+>> Add [<<And2>>,<<And1>>]
- /// CHECK: Return [<<Add>>]
+ /// CHECK-DAG: <<P1:i\d+>> ParameterValue
+ /// CHECK-DAG: <<P2:i\d+>> ParameterValue
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Not2:i\d+>> Not [<<P2>>]
+ /// CHECK-DAG: <<And2:i\d+>> And [<<Not2>>,<<One>>]
+ /// CHECK-DAG: <<Not1:i\d+>> Not [<<P1>>]
+ /// CHECK-DAG: <<And1:i\d+>> And [<<Not1>>,<<Not2>>]
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<And2>>,<<And1>>]
+ /// CHECK-DAG: Return [<<Add>>]
/// CHECK-START: int Main.$opt$noinline$notMultipleUses(int, int) instruction_simplifier (after)
- /// CHECK-NOT: Or
+ /// CHECK-NOT: Or
public static int $opt$noinline$notMultipleUses(int a, int b) {
if (doThrow) throw new Error();
@@ -304,8 +337,20 @@ public class Main {
public static void main(String[] args) {
assertIntEquals(~0xff, $opt$noinline$andToOr(0xf, 0xff));
+ assertIntEquals(~0xff, $noinline$runSmaliTest("$opt$noinline$andToOr", int.class, 0xf, 0xff));
+ assertEquals(true, $opt$noinline$booleanAndToOr(false, false));
+ assertEquals(true, $noinline$runSmaliTest("$opt$noinline$booleanAndToOr", boolean.class, false, false));
assertLongEquals(~0xf, $opt$noinline$orToAnd(0xf, 0xff));
+ assertLongEquals(~0xf, $noinline$runSmaliTest("$opt$noinline$orToAnd", long.class, 0xfL, 0xffL));
+ assertEquals(false, $opt$noinline$booleanOrToAnd(true, true));
+ assertEquals(false, $noinline$runSmaliTest("$opt$noinline$booleanOrToAnd", boolean.class, true, true));
+ assertIntEquals(-1, $opt$noinline$regressInputsAway(0xf, 0xff));
+ assertIntEquals(-1, $noinline$runSmaliTest("$opt$noinline$regressInputsAway", int.class, 0xf, 0xff));
assertIntEquals(0xf0, $opt$noinline$notXorToXor(0xf, 0xff));
+ assertIntEquals(0xf0, $noinline$runSmaliTest("$opt$noinline$notXorToXor", int.class, 0xf, 0xff));
+ assertEquals(true, $opt$noinline$booleanNotXorToXor(true, false));
+ assertEquals(true, $noinline$runSmaliTest("$opt$noinline$booleanNotXorToXor", boolean.class, true, false));
assertIntEquals(~0xff, $opt$noinline$notMultipleUses(0xf, 0xff));
+ assertIntEquals(~0xff, $noinline$runSmaliTest("$opt$noinline$notMultipleUses", int.class, 0xf, 0xff));
}
}
diff --git a/test/586-checker-null-array-get/build b/test/586-checker-null-array-get/build
new file mode 100755
index 0000000000..49292c9ac1
--- /dev/null
+++ b/test/586-checker-null-array-get/build
@@ -0,0 +1,23 @@
+#!/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.
+
+# This checker test is incompatible with jack bytecode output,
+# so force it to use javac/dx.
+export USE_JACK=false
+# Also disable desugar because it is missing in jack platform builds.
+export DESUGAR=false
+
+./default-build "$@"
diff --git a/test/586-checker-null-array-get/smali/SmaliTests.smali b/test/586-checker-null-array-get/smali/SmaliTests.smali
new file mode 100644
index 0000000000..f58af360fc
--- /dev/null
+++ b/test/586-checker-null-array-get/smali/SmaliTests.smali
@@ -0,0 +1,96 @@
+# 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.
+
+.class public LSmaliTests;
+.super Ljava/lang/Object;
+
+## CHECK-START: void SmaliTests.bar() load_store_elimination (after)
+## CHECK-DAG: <<Null:l\d+>> NullConstant
+## CHECK-DAG: <<BoundType:l\d+>> BoundType [<<Null>>]
+## CHECK-DAG: <<CheckL:l\d+>> NullCheck [<<BoundType>>]
+## CHECK-DAG: <<GetL0:l\d+>> ArrayGet [<<CheckL>>,{{i\d+}}]
+## CHECK-DAG: <<GetL1:l\d+>> ArrayGet [<<CheckL>>,{{i\d+}}]
+## CHECK-DAG: <<GetL2:l\d+>> ArrayGet [<<CheckL>>,{{i\d+}}]
+## CHECK-DAG: <<GetL3:l\d+>> ArrayGet [<<CheckL>>,{{i\d+}}]
+## CHECK-DAG: <<CheckJ:l\d+>> NullCheck [<<Null>>]
+## CHECK-DAG: <<GetJ0:j\d+>> ArrayGet [<<CheckJ>>,{{i\d+}}]
+## CHECK-DAG: <<GetJ1:j\d+>> ArrayGet [<<CheckJ>>,{{i\d+}}]
+## CHECK-DAG: <<GetJ2:j\d+>> ArrayGet [<<CheckJ>>,{{i\d+}}]
+## CHECK-DAG: <<GetJ3:j\d+>> ArrayGet [<<CheckJ>>,{{i\d+}}]
+.method public static bar()V
+ .registers 7
+
+ .prologue
+ const/4 v6, 0x3
+ const/4 v5, 0x2
+ const/4 v4, 0x1
+ const/4 v3, 0x0
+
+ # We create multiple accesses that will lead the bounds check
+ # elimination pass to add a HDeoptimize. Not having the bounds check helped
+ # the load store elimination think it could merge two ArrayGet with different
+ # types.
+
+ # String[] array = (String[])getNull();
+ invoke-static {}, LMain;->getNull()Ljava/lang/Object;
+ move-result-object v0
+ check-cast v0, [Ljava/lang/String;
+
+ # objectField = array[0];
+ aget-object v2, v0, v3
+ sput-object v2, LMain;->objectField:Ljava/lang/Object;
+ # objectField = array[1];
+ aget-object v2, v0, v4
+ sput-object v2, LMain;->objectField:Ljava/lang/Object;
+ # objectField = array[2];
+ aget-object v2, v0, v5
+ sput-object v2, LMain;->objectField:Ljava/lang/Object;
+ # objectField = array[3];
+ aget-object v2, v0, v6
+ sput-object v2, LMain;->objectField:Ljava/lang/Object;
+
+ # long[] longArray = getLongArray();
+ invoke-static {}, LMain;->getLongArray()[J
+ move-result-object v1
+
+ # longField = longArray[0];
+ aget-wide v2, v1, v3
+ sput-wide v2, LMain;->longField:J
+ # longField = longArray[1];
+ aget-wide v2, v1, v4
+ sput-wide v2, LMain;->longField:J
+ # longField = longArray[2];
+ aget-wide v2, v1, v5
+ sput-wide v2, LMain;->longField:J
+ # longField = longArray[3];
+ aget-wide v2, v1, v6
+ sput-wide v2, LMain;->longField:J
+
+ return-void
+.end method
+
+
+# static fields
+.field static doThrow:Z # boolean
+
+# direct methods
+.method static constructor <clinit>()V
+ .registers 1
+
+ .prologue
+ # doThrow = false
+ const/4 v0, 0x0
+ sput-boolean v0, LSmaliTests;->doThrow:Z
+ return-void
+.end method
diff --git a/test/586-checker-null-array-get/src/Main.java b/test/586-checker-null-array-get/src/Main.java
index 0ea7d34043..09ebff16c2 100644
--- a/test/586-checker-null-array-get/src/Main.java
+++ b/test/586-checker-null-array-get/src/Main.java
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
class Test1 {
int[] iarr;
}
@@ -29,6 +32,18 @@ public class Main {
public static Test1 getNullTest1() { return null; }
public static Test2 getNullTest2() { return null; }
+ public static void $noinline$runSmaliTest(String name) throws Throwable {
+ try {
+ Class<?> c = Class.forName("SmaliTests");
+ Method m = c.getMethod(name);
+ m.invoke(null);
+ } catch (InvocationTargetException ex) {
+ throw ex.getCause(); // re-raise expected exception.
+ } catch (Exception ex) {
+ throw new Error(ex);
+ }
+ }
+
public static void main(String[] args) {
try {
foo();
@@ -43,6 +58,15 @@ public class Main {
// Expected.
}
try {
+ $noinline$runSmaliTest("bar");
+ throw new Error("Expected NullPointerException");
+ } catch (NullPointerException e) {
+ // Expected.
+ } catch (Throwable t) {
+ throw new Error("Unexpected Throwable", t);
+ }
+
+ try {
test1();
throw new Error("Expected NullPointerException");
} catch (NullPointerException e) {
@@ -62,7 +86,8 @@ public class Main {
/// CHECK-START: void Main.bar() load_store_elimination (after)
/// CHECK-DAG: <<Null:l\d+>> NullConstant
- /// CHECK-DAG: <<BoundType:l\d+>> BoundType [<<Null>>]
+ /// CHECK-DAG: <<BoundFirst:l\d+>> BoundType [<<Null>>]
+ /// CHECK-DAG: <<BoundType:l\d+>> BoundType [<<BoundFirst>>]
/// CHECK-DAG: <<CheckL:l\d+>> NullCheck [<<BoundType>>]
/// CHECK-DAG: <<GetL0:l\d+>> ArrayGet [<<CheckL>>,{{i\d+}}]
/// CHECK-DAG: <<GetL1:l\d+>> ArrayGet [<<CheckL>>,{{i\d+}}]
diff --git a/test/593-checker-boolean-2-integral-conv/build b/test/593-checker-boolean-2-integral-conv/build
new file mode 100755
index 0000000000..49292c9ac1
--- /dev/null
+++ b/test/593-checker-boolean-2-integral-conv/build
@@ -0,0 +1,23 @@
+#!/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.
+
+# This checker test is incompatible with jack bytecode output,
+# so force it to use javac/dx.
+export USE_JACK=false
+# Also disable desugar because it is missing in jack platform builds.
+export DESUGAR=false
+
+./default-build "$@"
diff --git a/test/593-checker-boolean-2-integral-conv/smali/SmaliTests.smali b/test/593-checker-boolean-2-integral-conv/smali/SmaliTests.smali
new file mode 100644
index 0000000000..00ebaaf451
--- /dev/null
+++ b/test/593-checker-boolean-2-integral-conv/smali/SmaliTests.smali
@@ -0,0 +1,119 @@
+# 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.
+
+.class public LSmaliTests;
+.super Ljava/lang/Object;
+
+# static fields
+.field public static booleanField:Z
+
+.method static constructor <clinit>()V
+ .registers 1
+
+ .prologue
+ const/4 v0, 0x1
+
+ # booleanField = true
+ sput-boolean v0, LSmaliTests;->booleanField:Z
+
+ return-void
+.end method
+
+## CHECK-START: long SmaliTests.booleanToLong(boolean) builder (after)
+## CHECK-DAG: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+## CHECK-DAG: <<One:i\d+>> IntConstant 1
+## CHECK-DAG: <<Cond:z\d+>> Equal [<<Arg>>,<<Zero>>]
+## CHECK-DAG: If [<<Cond>>]
+## CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<Zero>>]
+## CHECK-DAG: <<IToJ:j\d+>> TypeConversion [<<Phi>>]
+## CHECK-DAG: Return [<<IToJ>>]
+
+## CHECK-START: long SmaliTests.booleanToLong(boolean) select_generator (after)
+## CHECK-DAG: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+## CHECK-DAG: <<One:i\d+>> IntConstant 1
+## CHECK-DAG: <<Sel:i\d+>> Select [<<Zero>>,<<One>>,<<Arg>>]
+## CHECK-DAG: <<IToJ:j\d+>> TypeConversion [<<Sel>>]
+## CHECK-DAG: Return [<<IToJ>>]
+
+## CHECK-START: long SmaliTests.booleanToLong(boolean) instruction_simplifier$after_bce (after)
+## CHECK-DAG: <<Arg:z\d+>> ParameterValue
+## CHECK-DAG: <<ZToJ:j\d+>> TypeConversion [<<Arg>>]
+## CHECK-DAG: Return [<<ZToJ>>]
+.method public static booleanToLong(Z)J
+ .registers 3
+ .param p0, "b" # Z
+ .prologue
+
+ # return b ? 1 : 0;
+ if-eqz p0, :b_is_zero
+
+# :b_is_one
+ const/4 v0, 0x1
+
+ :l_return
+ int-to-long v0, v0
+ return-wide v0
+
+ :b_is_zero
+ const/4 v0, 0x0
+ goto :l_return
+.end method
+
+## CHECK-START: int SmaliTests.longToIntOfBoolean() builder (after)
+## CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
+## CHECK-DAG: <<Sget:z\d+>> StaticFieldGet
+## CHECK-DAG: <<ZToJ:j\d+>> InvokeStaticOrDirect [<<Sget>>,<<Method>>]
+## CHECK-DAG: <<JToI:i\d+>> TypeConversion [<<ZToJ>>]
+## CHECK-DAG: Return [<<JToI>>]
+
+## CHECK-START: int SmaliTests.longToIntOfBoolean() inliner (after)
+## CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
+## CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+## CHECK-DAG: <<One:i\d+>> IntConstant 1
+## CHECK-DAG: <<Sget:z\d+>> StaticFieldGet
+## CHECK-DAG: If [<<Sget>>]
+## CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<Zero>>]
+## CHECK-DAG: <<IToJ:j\d+>> TypeConversion [<<Phi>>]
+## CHECK-DAG: <<JToI:i\d+>> TypeConversion [<<IToJ>>]
+## CHECK-DAG: Return [<<JToI>>]
+
+## CHECK-START: int SmaliTests.longToIntOfBoolean() select_generator (after)
+## CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
+## CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+## CHECK-DAG: <<One:i\d+>> IntConstant 1
+## CHECK-DAG: <<Sget:z\d+>> StaticFieldGet
+## CHECK-DAG: <<Sel:i\d+>> Select [<<Zero>>,<<One>>,<<Sget>>]
+## CHECK-DAG: <<IToJ:j\d+>> TypeConversion [<<Sel>>]
+## CHECK-DAG: <<JToI:i\d+>> TypeConversion [<<IToJ>>]
+## CHECK-DAG: Return [<<JToI>>]
+
+## CHECK-START: int SmaliTests.longToIntOfBoolean() instruction_simplifier$after_bce (after)
+## CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
+## CHECK-DAG: <<Sget:z\d+>> StaticFieldGet
+## CHECK-DAG: Return [<<Sget>>]
+.method public static longToIntOfBoolean()I
+ .registers 3
+ .prologue
+
+ # long l = booleanToLong(booleanField);
+ sget-boolean v2, LSmaliTests;->booleanField:Z
+ invoke-static {v2}, LSmaliTests;->booleanToLong(Z)J
+ move-result-wide v0
+
+ # return (int) l;
+ long-to-int v2, v0
+ return v2
+.end method
diff --git a/test/593-checker-boolean-2-integral-conv/src/Main.java b/test/593-checker-boolean-2-integral-conv/src/Main.java
index b4c91c8db6..3503b2e877 100644
--- a/test/593-checker-boolean-2-integral-conv/src/Main.java
+++ b/test/593-checker-boolean-2-integral-conv/src/Main.java
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+import java.lang.reflect.Method;
+
public class Main {
public static void main(String args[]) {
@@ -22,8 +24,10 @@ public class Main {
expectEqualsChar((char)1, booleanToChar(true));
expectEqualsInt(1, booleanToInt(true));
expectEqualsLong(1L, booleanToLong(true));
+ expectEqualsLong(1L, $noinline$runSmaliTest("booleanToLong", true));
expectEqualsInt(1, longToIntOfBoolean());
+ expectEqualsInt(1, $noinline$runSmaliTest("longToIntOfBoolean"));
System.out.println("passed");
}
@@ -132,26 +136,34 @@ public class Main {
/// CHECK-START: long Main.booleanToLong(boolean) builder (after)
/// CHECK: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
- /// CHECK-DAG: <<One:i\d+>> IntConstant 1
- /// CHECK-DAG: <<Cond:z\d+>> Equal [<<Arg>>,<<Zero>>]
+ /// CHECK-DAG: <<IZero:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Zero:j\d+>> LongConstant 0
+ /// CHECK-DAG: <<One:j\d+>> LongConstant 1
+ /// CHECK-DAG: <<Cond:z\d+>> Equal [<<Arg>>,<<IZero>>]
/// CHECK-DAG: If [<<Cond>>]
- /// CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<Zero>>]
- /// CHECK-DAG: <<IToJ:j\d+>> TypeConversion [<<Phi>>]
- /// CHECK-DAG: Return [<<IToJ>>]
+ /// CHECK-DAG: <<Phi:j\d+>> Phi [<<One>>,<<Zero>>]
+ /// CHECK-DAG: Return [<<Phi>>]
/// CHECK-START: long Main.booleanToLong(boolean) select_generator (after)
- /// CHECK: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
- /// CHECK-DAG: <<One:i\d+>> IntConstant 1
- /// CHECK-DAG: <<Sel:i\d+>> Select [<<Zero>>,<<One>>,<<Arg>>]
- /// CHECK-DAG: <<IToJ:j\d+>> TypeConversion [<<Sel>>]
- /// CHECK-DAG: Return [<<IToJ>>]
+ /// CHECK-NOT: IntConstant
+ /// CHECK-NOT: Equal
+ /// CHECK-NOT: If
+ /// CHECK-NOT: Phi
- /// CHECK-START: long Main.booleanToLong(boolean) instruction_simplifier$after_bce (after)
+ /// CHECK-START: long Main.booleanToLong(boolean) select_generator (after)
/// CHECK: <<Arg:z\d+>> ParameterValue
- /// CHECK-DAG: <<ZToJ:j\d+>> TypeConversion [<<Arg>>]
- /// CHECK-DAG: Return [<<ZToJ>>]
+ /// CHECK-DAG: <<Zero:j\d+>> LongConstant 0
+ /// CHECK-DAG: <<One:j\d+>> LongConstant 1
+ /// CHECK-DAG: <<Sel:j\d+>> Select [<<Zero>>,<<One>>,<<Arg>>]
+ /// CHECK-DAG: Return [<<Sel>>]
+
+ // As of now, the code is not optimized any further than the above.
+ // TODO: Re-enable checks below after simplifier is updated to handle this pattern: b/63064517
+
+ // CHECK-START: long Main.booleanToLong(boolean) instruction_simplifier$after_bce (after)
+ // CHECK: <<Arg:z\d+>> ParameterValue
+ // CHECK-DAG: <<ZToJ:j\d+>> TypeConversion [<<Arg>>]
+ // CHECK-DAG: Return [<<ZToJ>>]
static long booleanToLong(boolean b) {
return b ? 1 : 0;
@@ -166,29 +178,36 @@ public class Main {
/// CHECK-START: int Main.longToIntOfBoolean() inliner (after)
/// CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
- /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
- /// CHECK-DAG: <<One:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Zero:j\d+>> LongConstant 0
+ /// CHECK-DAG: <<One:j\d+>> LongConstant 1
/// CHECK-DAG: <<Sget:z\d+>> StaticFieldGet
/// CHECK-DAG: If [<<Sget>>]
- /// CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<Zero>>]
- /// CHECK-DAG: <<IToJ:j\d+>> TypeConversion [<<Phi>>]
- /// CHECK-DAG: <<JToI:i\d+>> TypeConversion [<<IToJ>>]
+ /// CHECK-DAG: <<Phi:j\d+>> Phi [<<One>>,<<Zero>>]
+ /// CHECK-DAG: <<JToI:i\d+>> TypeConversion [<<Phi>>]
/// CHECK-DAG: Return [<<JToI>>]
+ /// CHECK-START: long Main.booleanToLong(boolean) select_generator (after)
+ /// CHECK-NOT: IntConstant
+ /// CHECK-NOT: Equal
+ /// CHECK-NOT: If
+ /// CHECK-NOT: Phi
+
/// CHECK-START: int Main.longToIntOfBoolean() select_generator (after)
/// CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
- /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
- /// CHECK-DAG: <<One:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Zero:j\d+>> LongConstant 0
+ /// CHECK-DAG: <<One:j\d+>> LongConstant 1
/// CHECK-DAG: <<Sget:z\d+>> StaticFieldGet
- /// CHECK-DAG: <<Sel:i\d+>> Select [<<Zero>>,<<One>>,<<Sget>>]
- /// CHECK-DAG: <<IToJ:j\d+>> TypeConversion [<<Sel>>]
- /// CHECK-DAG: <<JToI:i\d+>> TypeConversion [<<IToJ>>]
+ /// CHECK-DAG: <<Sel:j\d+>> Select [<<Zero>>,<<One>>,<<Sget>>]
+ /// CHECK-DAG: <<JToI:i\d+>> TypeConversion [<<Sel>>]
/// CHECK-DAG: Return [<<JToI>>]
- /// CHECK-START: int Main.longToIntOfBoolean() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
- /// CHECK-DAG: <<Sget:z\d+>> StaticFieldGet
- /// CHECK-DAG: Return [<<Sget>>]
+ // As of now, the code is not optimized any further than the above.
+ // TODO: Re-enable checks below after simplifier is updated to handle this pattern: b/63064517
+
+ // CHECK-START: int Main.longToIntOfBoolean() instruction_simplifier$after_bce (after)
+ // CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
+ // CHECK-DAG: <<Sget:z\d+>> StaticFieldGet
+ // CHECK-DAG: Return [<<Sget>>]
static int longToIntOfBoolean() {
long l = booleanToLong(booleanField);
@@ -226,6 +245,26 @@ public class Main {
}
}
+ public static long $noinline$runSmaliTest(String name, boolean input) {
+ try {
+ Class<?> c = Class.forName("SmaliTests");
+ Method m = c.getMethod(name, boolean.class);
+ return (Long) m.invoke(null, input);
+ } catch (Exception ex) {
+ throw new Error(ex);
+ }
+ }
+
+ public static int $noinline$runSmaliTest(String name) {
+ try {
+ Class<?> c = Class.forName("SmaliTests");
+ Method m = c.getMethod(name);
+ return (Integer) m.invoke(null);
+ } catch (Exception ex) {
+ throw new Error(ex);
+ }
+ }
+
public static boolean booleanField = true;
diff --git a/test/593-checker-shift-and-simplifier/expected.txt b/test/593-checker-shift-and-simplifier/expected.txt
index b0aad4deb5..f8d85db6ac 100644
--- a/test/593-checker-shift-and-simplifier/expected.txt
+++ b/test/593-checker-shift-and-simplifier/expected.txt
@@ -1 +1,2 @@
passed
+passed
diff --git a/test/593-checker-shift-and-simplifier/smali/SmaliTests.smali b/test/593-checker-shift-and-simplifier/smali/SmaliTests.smali
new file mode 100644
index 0000000000..6b0d68306b
--- /dev/null
+++ b/test/593-checker-shift-and-simplifier/smali/SmaliTests.smali
@@ -0,0 +1,58 @@
+# 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.
+
+.class public LSmaliTests;
+.super Ljava/lang/Object;
+
+# A very particular set of operations that caused a double removal by the
+# ARM64 simplifier doing "forward" removals (b/27851582).
+
+## CHECK-START-ARM: int SmaliTests.operations() instruction_simplifier_arm (before)
+## CHECK-DAG: <<Get:i\d+>> ArrayGet
+## CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
+## CHECK-DAG: <<Shl:i\d+>> Shl [<<Get>>,i{{\d+}}]
+## CHECK-DAG: And [<<Not>>,<<Shl>>]
+
+## CHECK-START-ARM: int SmaliTests.operations() instruction_simplifier_arm (after)
+## CHECK-DAG: <<Get:i\d+>> ArrayGet
+## CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
+## CHECK-DAG: DataProcWithShifterOp [<<Not>>,<<Get>>] kind:And+LSL shift:2
+
+## CHECK-START-ARM64: int SmaliTests.operations() instruction_simplifier_arm64 (before)
+## CHECK-DAG: <<Get:i\d+>> ArrayGet
+## CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
+## CHECK-DAG: <<Shl:i\d+>> Shl [<<Get>>,i{{\d+}}]
+## CHECK-DAG: And [<<Not>>,<<Shl>>]
+
+## CHECK-START-ARM64: int SmaliTests.operations() instruction_simplifier_arm64 (after)
+## CHECK-DAG: <<Get:i\d+>> ArrayGet
+## CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
+## CHECK-DAG: DataProcWithShifterOp [<<Not>>,<<Get>>] kind:And+LSL shift:2
+.method public static operations()I
+ .registers 6
+ .prologue
+
+ # int r = a[0];
+ sget-object v4, LMain;->a:[I
+ const/4 v5, 0x0
+ aget v2, v4, v5
+ # int n = ~r;
+ not-int v1, v2
+ # int s = r << 2;
+ shl-int/lit8 v3, v2, 0x2
+ # int a = s & n;
+ and-int v0, v3, v1
+ # return a
+ return v0
+.end method
diff --git a/test/593-checker-shift-and-simplifier/src/Main.java b/test/593-checker-shift-and-simplifier/src/Main.java
index c9826bc546..f0ef0e6949 100644
--- a/test/593-checker-shift-and-simplifier/src/Main.java
+++ b/test/593-checker-shift-and-simplifier/src/Main.java
@@ -14,30 +14,20 @@
* limitations under the License.
*/
+import java.lang.reflect.Method;
+
public class Main {
- private static int[] a = { 10 };
+ static int[] a = { 10 };
// A very particular set of operations that caused a double removal by the
// ARM64 simplifier doing "forward" removals (b/27851582).
- /// CHECK-START-ARM: int Main.operations() instruction_simplifier_arm (before)
- /// CHECK-DAG: <<Get:i\d+>> ArrayGet
- /// CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
- /// CHECK-DAG: <<Shl:i\d+>> Shl [<<Get>>,i{{\d+}}]
- /// CHECK-DAG: And [<<Not>>,<<Shl>>]
- //
/// CHECK-START-ARM: int Main.operations() instruction_simplifier_arm (after)
/// CHECK-DAG: <<Get:i\d+>> ArrayGet
/// CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
/// CHECK-DAG: DataProcWithShifterOp [<<Not>>,<<Get>>] kind:And+LSL shift:2
- /// CHECK-START-ARM64: int Main.operations() instruction_simplifier_arm64 (before)
- /// CHECK-DAG: <<Get:i\d+>> ArrayGet
- /// CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
- /// CHECK-DAG: <<Shl:i\d+>> Shl [<<Get>>,i{{\d+}}]
- /// CHECK-DAG: And [<<Not>>,<<Shl>>]
- //
/// CHECK-START-ARM64: int Main.operations() instruction_simplifier_arm64 (after)
/// CHECK-DAG: <<Get:i\d+>> ArrayGet
/// CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
@@ -56,5 +46,21 @@ public class Main {
} else {
System.out.println("passed");
}
+
+ if ($noinline$runSmaliTest("operations") != 32) {
+ System.out.println("failed");
+ } else {
+ System.out.println("passed");
+ }
+ }
+
+ public static int $noinline$runSmaliTest(String name) {
+ try {
+ Class<?> c = Class.forName("SmaliTests");
+ Method m = c.getMethod(name);
+ return (Integer) m.invoke(null);
+ } catch (Exception ex) {
+ throw new Error(ex);
+ }
}
}
diff --git a/test/633-checker-rtp-getclass/build b/test/633-checker-rtp-getclass/build
new file mode 100755
index 0000000000..49292c9ac1
--- /dev/null
+++ b/test/633-checker-rtp-getclass/build
@@ -0,0 +1,23 @@
+#!/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.
+
+# This checker test is incompatible with jack bytecode output,
+# so force it to use javac/dx.
+export USE_JACK=false
+# Also disable desugar because it is missing in jack platform builds.
+export DESUGAR=false
+
+./default-build "$@"
diff --git a/test/633-checker-rtp-getclass/smali/SmaliTests.smali b/test/633-checker-rtp-getclass/smali/SmaliTests.smali
new file mode 100644
index 0000000000..9ea04498d7
--- /dev/null
+++ b/test/633-checker-rtp-getclass/smali/SmaliTests.smali
@@ -0,0 +1,65 @@
+# 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.
+
+.class public LSmaliTests;
+.super Ljava/lang/Object;
+
+# Checker test to make sure the only inlined instruction is SubMain.bar.
+
+## CHECK-START: int SmaliTests.$opt$noinline$foo(Main) inliner (after)
+## CHECK-DAG: InvokeVirtual method_name:Main.foo
+## CHECK-DAG: <<Const:i\d+>> IntConstant 3
+## CHECK: begin_block
+## CHECK: BoundType klass:SubMain
+## CHECK: Return [<<Const>>]
+## CHECK-NOT: begin_block
+## CHECK: end_block
+.method public static $opt$noinline$foo(LMain;)I
+ .registers 3
+ .param p0, "o" # LMain;
+ .prologue
+
+ # if (doThrow) { throw new Error(); }
+ sget-boolean v0, LMain;->doThrow:Z
+ if-eqz v0, :doThrow_false
+ new-instance v0, Ljava/lang/Error;
+ invoke-direct {v0}, Ljava/lang/Error;-><init>()V
+ throw v0
+
+ :doThrow_false
+ # if (o.getClass() == Main.class || o.getClass() != SubMain.class)
+ invoke-virtual {p0}, LMain;->getClass()Ljava/lang/Class;
+ move-result-object v0
+ const-class v1, LMain;
+ if-eq v0, v1, :class_is_Main_and_not_SubMain
+
+ invoke-virtual {p0}, LMain;->getClass()Ljava/lang/Class;
+ move-result-object v0
+ const-class v1, LSubMain;
+ if-eq v0, v1, :else
+
+ :class_is_Main_and_not_SubMain
+ # return o.foo()
+ invoke-virtual {p0}, LMain;->foo()I
+ move-result v0
+ return v0
+
+ :else
+ # return o.bar()
+ invoke-virtual {p0}, LMain;->bar()I
+ move-result v0
+ return v0
+.end method
+
+
diff --git a/test/633-checker-rtp-getclass/src/Main.java b/test/633-checker-rtp-getclass/src/Main.java
index f29c139f63..d1145af2a5 100644
--- a/test/633-checker-rtp-getclass/src/Main.java
+++ b/test/633-checker-rtp-getclass/src/Main.java
@@ -14,34 +14,13 @@
* limitations under the License.
*/
+import java.lang.reflect.Method;
+
public class Main {
public static void main(String[] args) {
- System.out.println($opt$noinline$foo(new Main()));
- System.out.println($opt$noinline$foo(new SubMain()));
- System.out.println($opt$noinline$foo(new SubSubMain()));
- }
-
-
- // Checker test to make sure the only inlined instruction is
- // SubMain.bar.
- /// CHECK-START: int Main.$opt$noinline$foo(Main) inliner (after)
- /// CHECK-DAG: InvokeVirtual method_name:Main.foo
- /// CHECK-DAG: <<Const:i\d+>> IntConstant 3
- /// CHECK: begin_block
- /// CHECK: BoundType klass:SubMain
- /// CHECK: Return [<<Const>>]
- /// CHECK-NOT: begin_block
- /// CHECK: end_block
- public static int $opt$noinline$foo(Main o) {
- if (doThrow) { throw new Error(); }
- // To exercise the bug on Jack, we need two getClass compares.
- if (o.getClass() == Main.class || o.getClass() != SubMain.class) {
- return o.foo();
- } else {
- // We used to wrongly bound the type of o to `Main` here and then realize that's
- // impossible and mark this branch as dead.
- return o.bar();
- }
+ System.out.println($noinline$runSmaliTest("$opt$noinline$foo", new Main()));
+ System.out.println($noinline$runSmaliTest("$opt$noinline$foo", new SubMain()));
+ System.out.println($noinline$runSmaliTest("$opt$noinline$foo", new SubSubMain()));
}
public int bar() {
@@ -53,6 +32,16 @@ public class Main {
}
public static boolean doThrow = false;
+
+ public static int $noinline$runSmaliTest(String name, Main input) {
+ try {
+ Class<?> c = Class.forName("SmaliTests");
+ Method m = c.getMethod(name, Main.class);
+ return (Integer) m.invoke(null, input);
+ } catch (Exception ex) {
+ throw new Error(ex);
+ }
+ }
}
class SubMain extends Main {
diff --git a/test/652-deopt-intrinsic/run b/test/652-deopt-intrinsic/run
new file mode 100755
index 0000000000..97d1ff16bb
--- /dev/null
+++ b/test/652-deopt-intrinsic/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# 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.
+
+# Ensure this test is not subject to code collection.
+exec ${RUN} "$@" --runtime-option -Xjitinitialsize:32M
diff --git a/test/656-annotation-lookup-generic-jni/check b/test/656-annotation-lookup-generic-jni/check
new file mode 100755
index 0000000000..39a52d5297
--- /dev/null
+++ b/test/656-annotation-lookup-generic-jni/check
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# 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.
+
+# On gcstress configurations, an extra "JNI_OnUnload called" line may
+# be emitted. If so, remove it.
+sed -e '${/^JNI_OnUnload called$/d;}' "$2" > "$2.tmp"
+
+./default-check "$1" "$2.tmp"
diff --git a/test/656-annotation-lookup-generic-jni/expected.txt b/test/656-annotation-lookup-generic-jni/expected.txt
new file mode 100644
index 0000000000..4519c7e442
--- /dev/null
+++ b/test/656-annotation-lookup-generic-jni/expected.txt
@@ -0,0 +1,3 @@
+JNI_OnLoad called
+Java_Test_nativeMethodWithAnnotation
+passed
diff --git a/test/656-annotation-lookup-generic-jni/info.txt b/test/656-annotation-lookup-generic-jni/info.txt
new file mode 100644
index 0000000000..9049bfcf80
--- /dev/null
+++ b/test/656-annotation-lookup-generic-jni/info.txt
@@ -0,0 +1,7 @@
+Non-regression test for b/38454151, where the invocation of a native
+method with an annotation (to be found in a custom class loader)
+through Generic JNI would crash the Generic JNI trampoline because it
+would throw an exception (that should eventually be caught) and walk a
+stack with an unexpected layout when trying to resolve the method's
+annotation classes (during the CriticalNative/FastNative optimization
+annotation lookup).
diff --git a/test/656-annotation-lookup-generic-jni/src-art/Main.java b/test/656-annotation-lookup-generic-jni/src-art/Main.java
new file mode 100644
index 0000000000..01b288a900
--- /dev/null
+++ b/test/656-annotation-lookup-generic-jni/src-art/Main.java
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+import dalvik.system.InMemoryDexClassLoader;
+
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+public class Main {
+
+ public static void main(String[] args) throws Exception {
+ // Extract Dex file contents from the secondary Jar file.
+ String jarFilename =
+ System.getenv("DEX_LOCATION") + "/656-annotation-lookup-generic-jni-ex.jar";
+ ZipFile zipFile = new ZipFile(jarFilename);
+ ZipEntry zipEntry = zipFile.getEntry("classes.dex");
+ InputStream inputStream = zipFile.getInputStream(zipEntry);
+ int dexFileSize = (int) zipEntry.getSize();
+ byte[] dexFileContents = new byte[dexFileSize];
+ inputStream.read(dexFileContents, 0, dexFileSize);
+
+ // Create class loader from secondary Dex file.
+ ByteBuffer dexBuffer = ByteBuffer.wrap(dexFileContents);
+ ClassLoader classLoader = createUnquickenedDexClassLoader(dexBuffer);
+
+ // Load and initialize the Test class.
+ Class<?> testClass = classLoader.loadClass("Test");
+ Method initialize = testClass.getMethod("initialize", String.class);
+ initialize.invoke(null, args[0]);
+
+ // Invoke Test.nativeMethodWithAnnotation().
+ Method nativeMethodWithAnnotation = testClass.getMethod("nativeMethodWithAnnotation");
+ // Invoking the native method Test.nativeMethodWithAnnotation used
+ // to crash the Generic JNI trampoline during the resolution of
+ // the method's annotations (DummyAnnotation) (see b/38454151).
+ nativeMethodWithAnnotation.invoke(null);
+
+ zipFile.close();
+ System.out.println("passed");
+ }
+
+ // Create a class loader loading a Dex file in memory
+ // *without creating an Oat file*. This way, the Dex file won't be
+ // quickened and JNI stubs won't be compiled, thus forcing the use
+ // of Generic JNI when invoking the native method
+ // Test.nativeMethodWithAnnotation.
+ static ClassLoader createUnquickenedDexClassLoader(ByteBuffer dexBuffer) {
+ InMemoryDexClassLoader cl = new InMemoryDexClassLoader(dexBuffer, getBootClassLoader());
+ return cl;
+ }
+
+ static ClassLoader getBootClassLoader() {
+ ClassLoader cl = Main.class.getClassLoader();
+ while (cl.getParent() != null) {
+ cl = cl.getParent();
+ }
+ return cl;
+ }
+
+}
diff --git a/test/656-annotation-lookup-generic-jni/src-ex/DummyAnnotation.java b/test/656-annotation-lookup-generic-jni/src-ex/DummyAnnotation.java
new file mode 100644
index 0000000000..6caac6685e
--- /dev/null
+++ b/test/656-annotation-lookup-generic-jni/src-ex/DummyAnnotation.java
@@ -0,0 +1,17 @@
+/*
+ * 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.
+ */
+
+public @interface DummyAnnotation {}
diff --git a/test/656-annotation-lookup-generic-jni/src-ex/Test.java b/test/656-annotation-lookup-generic-jni/src-ex/Test.java
new file mode 100644
index 0000000000..838b4fe0d6
--- /dev/null
+++ b/test/656-annotation-lookup-generic-jni/src-ex/Test.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+public class Test {
+
+ public static void initialize(String libname) {
+ // Load test native library to get access to the implementation of
+ // Test.nativeMethodWithAnnotation.
+ System.loadLibrary(libname);
+ }
+
+ @DummyAnnotation
+ public static native void nativeMethodWithAnnotation();
+
+}
diff --git a/test/656-annotation-lookup-generic-jni/test.cc b/test/656-annotation-lookup-generic-jni/test.cc
new file mode 100644
index 0000000000..c8aa2af921
--- /dev/null
+++ b/test/656-annotation-lookup-generic-jni/test.cc
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+#include "jni.h"
+
+#include <iostream>
+
+namespace art {
+
+// Native method annotated with `DummyAnnotation` in Java source.
+extern "C" JNIEXPORT void JNICALL Java_Test_nativeMethodWithAnnotation(JNIEnv*, jclass) {
+ std::cout << "Java_Test_nativeMethodWithAnnotation" << std::endl;
+}
+
+} // namespace art
diff --git a/test/656-checker-simd-opt/expected.txt b/test/656-checker-simd-opt/expected.txt
new file mode 100644
index 0000000000..b0aad4deb5
--- /dev/null
+++ b/test/656-checker-simd-opt/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/656-checker-simd-opt/info.txt b/test/656-checker-simd-opt/info.txt
new file mode 100644
index 0000000000..185d2b1b95
--- /dev/null
+++ b/test/656-checker-simd-opt/info.txt
@@ -0,0 +1 @@
+Tests around optimizations of SIMD code.
diff --git a/test/656-checker-simd-opt/src/Main.java b/test/656-checker-simd-opt/src/Main.java
new file mode 100644
index 0000000000..794c9b6c0d
--- /dev/null
+++ b/test/656-checker-simd-opt/src/Main.java
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+/**
+ * Tests for SIMD related optimizations.
+ */
+public class Main {
+
+ /// CHECK-START: void Main.unroll(float[], float[]) loop_optimization (before)
+ /// CHECK-DAG: <<Cons:f\d+>> FloatConstant 2.5 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:f\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Mul:f\d+>> Mul [<<Get>>,<<Cons>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Mul>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.unroll(float[], float[]) loop_optimization (after)
+ /// CHECK-DAG: <<Cons:f\d+>> FloatConstant 2.5 loop:none
+ /// CHECK-DAG: <<Incr:i\d+>> IntConstant 4 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Cons>>] loop:none
+ /// CHECK-NOT: VecReplicateScalar
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Mul1:d\d+>> VecMul [<<Get1>>,<<Repl>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Mul1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Phi>>,<<Incr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Add>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Mul2:d\d+>> VecMul [<<Get2>>,<<Repl>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Add>>,<<Mul2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Add>>,<<Incr>>] loop:<<Loop>> outer_loop:none
+ private static void unroll(float[] x, float[] y) {
+ for (int i = 0; i < 100; i++) {
+ x[i] = y[i] * 2.5f;
+ }
+ }
+
+ /// CHECK-START: void Main.stencil(int[], int[], int) loop_optimization (before)
+ /// CHECK-DAG: <<CP1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<CM1:i\d+>> IntConstant -1 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Add1:i\d+>> Add [<<Phi>>,<<CM1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get1:i\d+>> ArrayGet [{{l\d+}},<<Add1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:i\d+>> ArrayGet [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add2:i\d+>> Add [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add3:i\d+>> Add [<<Phi>>,<<CP1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get3:i\d+>> ArrayGet [{{l\d+}},<<Add3>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add4:i\d+>> Add [<<Add2>>,<<Get3>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Add4>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.stencil(int[], int[], int) loop_optimization (after)
+ /// CHECK-DAG: <<CP1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<CP2:i\d+>> IntConstant 2 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Add1:i\d+>> Add [<<Phi>>,<<CP1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Add1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add2:d\d+>> VecAdd [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add3:i\d+>> Add [<<Phi>>,<<CP2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get3:d\d+>> VecLoad [{{l\d+}},<<Add3>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add4:d\d+>> VecAdd [<<Add2>>,<<Get3>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Add1>>,<<Add4>>] loop:<<Loop>> outer_loop:none
+ private static void stencil(int[] a, int[] b, int n) {
+ for (int i = 1; i < n - 1; i++) {
+ a[i] = b[i - 1] + b[i] + b[i + 1];
+ }
+ }
+
+ public static void main(String[] args) {
+ float[] x = new float[100];
+ float[] y = new float[100];
+ for (int i = 0; i < 100; i++) {
+ x[i] = 0.0f;
+ y[i] = 2.0f;
+ }
+ unroll(x, y);
+ for (int i = 0; i < 100; i++) {
+ expectEquals(5.0f, x[i]);
+ expectEquals(2.0f, y[i]);
+ }
+ int[] a = new int[100];
+ int[] b = new int[100];
+ for (int i = 0; i < 100; i++) {
+ a[i] = 0;
+ b[i] = i;
+ }
+ stencil(a, b, 100);
+ for (int i = 1; i < 99; i++) {
+ int e = i + i + i;
+ expectEquals(e, a[i]);
+ expectEquals(i, b[i]);
+ }
+ System.out.println("passed");
+ }
+
+ private static void expectEquals(float expected, float result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
diff --git a/test/656-loop-deopt/src/Main.java b/test/656-loop-deopt/src/Main.java
index c99cccf4f1..20e6d723d1 100644
--- a/test/656-loop-deopt/src/Main.java
+++ b/test/656-loop-deopt/src/Main.java
@@ -32,6 +32,15 @@ public class Main {
$noinline$loopIncrement(new Main());
ensureJitCompiled(Main.class, "$noinline$loopIncrement");
$noinline$loopIncrement(new SubMain());
+
+ $noinline$objectReturned(new Main());
+ ensureJitCompiled(Main.class, "$noinline$objectReturned");
+ Object o = $noinline$objectReturned(new SubMain());
+ // We used to get 0xebadde09 in 'o' here and therefore crash
+ // both interpreter and compiled code.
+ if (o instanceof Cloneable) {
+ System.out.println("Unexpected object type " + o.getClass());
+ }
}
public boolean doCheck() {
@@ -59,7 +68,7 @@ public class Main {
public static void $noinline$objectUpdate(Main m) {
Object o = new Object();
// We used to kill 'o' when the inline cache of 'doCheck' only
- // contains 'Main' (which makes the only branch using 'a' dead).
+ // contains 'Main' (which makes the only branch using 'o' dead).
// So the deoptimization at the inline cache was incorrectly assuming
// 'o' was dead.
// This lead to a NPE on the 'toString' call just after deoptimizing.
@@ -82,8 +91,8 @@ public class Main {
// 'k' was 5000.
for (int i = 0; i < 5000; i++, k++) {
if (m.doCheck()) {
- // We make this branch the only true user of the 'a' phi. All other uses
- // of 'a' are phi updates.
+ // We make this branch the only true user of the 'k' phi. All other uses
+ // of 'k' are phi updates.
myIntStatic = k;
}
}
@@ -92,6 +101,28 @@ public class Main {
}
}
+ public static Object $noinline$objectReturned(Main m) {
+ Object o = new Object();
+ // We used to kill 'o' when the inline cache of 'doCheck' only
+ // contains 'Main' (which makes the only branch using 'o' dead).
+ // So the deoptimization at the inline cache was incorrectly assuming
+ // 'o' was dead.
+ // We also need to make 'o' escape through a return instruction, as mterp
+ // executes the same code for return and return-object, and the 0xebadde09
+ // sentinel for dead value is only pushed to non-object dex registers.
+ Object myReturnValue = null;
+ for (int i = 0; i < 5000; i++) {
+ if (m.doCheck()) {
+ // We make this branch the only true user of the 'o' phi. All other uses
+ // of 'o' are phi updates.
+ myReturnValue = o;
+ } else if (myIntStatic == 42) {
+ o = m;
+ }
+ }
+ return myReturnValue;
+ }
+
public static int myIntStatic = 0;
public static native void ensureJitCompiled(Class<?> itf, String name);
diff --git a/test/657-branches/expected.txt b/test/657-branches/expected.txt
new file mode 100644
index 0000000000..d9fd864709
--- /dev/null
+++ b/test/657-branches/expected.txt
@@ -0,0 +1 @@
+Hello World: 4.0
diff --git a/test/657-branches/info.txt b/test/657-branches/info.txt
new file mode 100644
index 0000000000..84a2bb908c
--- /dev/null
+++ b/test/657-branches/info.txt
@@ -0,0 +1,2 @@
+Regression test for the ARM backend, which used to have a bug
+handling branches fallthrough.
diff --git a/test/657-branches/src/Main.java b/test/657-branches/src/Main.java
new file mode 100644
index 0000000000..2b62c5faa1
--- /dev/null
+++ b/test/657-branches/src/Main.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+public class Main {
+
+ public static void foo(float f) {
+ // The reason this used to break:
+ // 1) We inline the 'foo' call, so blocks now only contain HLoadClass instructions.
+ // 2) We then run the select_generator pass, which cannot change the
+ // if/else because blocks contain instructions.
+ // 3) We run GVN which will remove the HLoadClass instructions in the blocks.
+ // 4) At code generation, we are in the unlikely situation that a diamond shape
+ // contains no instruction (usually removed by select_generator). This used
+ // to trip the ARM code generators.
+ if (f < 1.2f) {
+ foo(Main.class, Object.class);
+ if (f < 0.2f) {
+ foo(Main.class, Object.class);
+ } else {
+ foo(Main.class, Object.class);
+ }
+ } else {
+ System.out.println("Hello World: " + f);
+ }
+ }
+
+ public static void foo(Object a, Object b) {}
+
+ public static void main(String[] args) {
+ foo(0f);
+ foo(4f);
+ foo(0.1f);
+ }
+}
diff --git a/test/961-default-iface-resolution-gen/build b/test/961-default-iface-resolution-gen/build
index f2c222524e..d719a9ffe9 100755
--- a/test/961-default-iface-resolution-gen/build
+++ b/test/961-default-iface-resolution-gen/build
@@ -23,4 +23,4 @@ mkdir -p ./src
./util-src/generate_java.py ./src ./expected.txt
# dx runs out of memory with default 256M, give it more memory.
-./default-build "$@" --experimental default-methods --dx-vm-option -JXmx512M
+./default-build "$@" --experimental default-methods --dx-vm-option -JXmx1024M
diff --git a/test/964-default-iface-init-gen/build b/test/964-default-iface-init-gen/build
index a800151670..e504690043 100755
--- a/test/964-default-iface-init-gen/build
+++ b/test/964-default-iface-init-gen/build
@@ -23,4 +23,4 @@ mkdir -p ./src
./util-src/generate_java.py ./src ./expected.txt
# dx runs out of memory with just 256m, so increase it.
-./default-build "$@" --experimental default-methods --dx-vm-option -JXmx512M
+./default-build "$@" --experimental default-methods --dx-vm-option -JXmx1024M
diff --git a/test/988-method-trace/expected.txt b/test/988-method-trace/expected.txt
index 30ad532f6c..8e42a48865 100644
--- a/test/988-method-trace/expected.txt
+++ b/test/988-method-trace/expected.txt
@@ -1,4 +1,4 @@
-.<= public static native void art.Trace.enableTracing(java.lang.Class,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.Thread) -> <null: null>
+.<= public static native void art.Trace.enableTracing(java.lang.Class,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.Thread) -> <null: null>
<= public static void art.Trace.enableMethodTracing(java.lang.Class,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.Thread) -> <null: null>
=> art.Test988$IterOp()
.=> public java.lang.Object()
@@ -143,11 +143,11 @@ fibonacci(5)=5
......=> private static native java.lang.Object java.lang.Throwable.nativeFillInStackTrace()
......<= private static native java.lang.Object java.lang.Throwable.nativeFillInStackTrace() -> <class [Ljava.lang.Object;: <non-deterministic>>
.....<= public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace() -> <class java.lang.Error: java.lang.Error: Bad argument: -19 < 0
- at art.Test988.iter_fibonacci(Test988.java:209)
- at art.Test988$IterOp.applyAsInt(Test988.java:204)
- at art.Test988.doFibTest(Test988.java:297)
- at art.Test988.run(Test988.java:267)
- at Main.main(Main.java:19)
+ art.Test988.iter_fibonacci(Test988.java:228)
+ art.Test988$IterOp.applyAsInt(Test988.java:223)
+ art.Test988.doFibTest(Test988.java:316)
+ art.Test988.run(Test988.java:286)
+ <additional hidden frames>
>
....<= public java.lang.Throwable(java.lang.String) -> <null: null>
...<= public java.lang.Error(java.lang.String) -> <null: null>
@@ -163,11 +163,11 @@ fibonacci(5)=5
...<= private void java.util.ArrayList.ensureExplicitCapacity(int) -> <null: null>
..<= private void java.util.ArrayList.ensureCapacityInternal(int) -> <null: null>
fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0
- at art.Test988.iter_fibonacci(Test988.java:209)
- at art.Test988$IterOp.applyAsInt(Test988.java:204)
- at art.Test988.doFibTest(Test988.java:297)
- at art.Test988.run(Test988.java:267)
- at Main.main(Main.java:19)
+ art.Test988.iter_fibonacci(Test988.java:228)
+ art.Test988$IterOp.applyAsInt(Test988.java:223)
+ art.Test988.doFibTest(Test988.java:316)
+ art.Test988.run(Test988.java:286)
+ <additional hidden frames>
.<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
<= public static void art.Test988.doFibTest(int,java.util.function.IntUnaryOperator) -> <null: null>
@@ -244,11 +244,11 @@ fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0
......=> private static native java.lang.Object java.lang.Throwable.nativeFillInStackTrace()
......<= private static native java.lang.Object java.lang.Throwable.nativeFillInStackTrace() -> <class [Ljava.lang.Object;: <non-deterministic>>
.....<= public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace() -> <class java.lang.Error: java.lang.Error: Bad argument: -19 < 0
- at art.Test988.fibonacci(Test988.java:231)
- at art.Test988$RecurOp.applyAsInt(Test988.java:226)
- at art.Test988.doFibTest(Test988.java:297)
- at art.Test988.run(Test988.java:268)
- at Main.main(Main.java:19)
+ art.Test988.fibonacci(Test988.java:250)
+ art.Test988$RecurOp.applyAsInt(Test988.java:245)
+ art.Test988.doFibTest(Test988.java:316)
+ art.Test988.run(Test988.java:287)
+ <additional hidden frames>
>
....<= public java.lang.Throwable(java.lang.String) -> <null: null>
...<= public java.lang.Error(java.lang.String) -> <null: null>
@@ -264,14 +264,14 @@ fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0
...<= private void java.util.ArrayList.ensureExplicitCapacity(int) -> <null: null>
..<= private void java.util.ArrayList.ensureCapacityInternal(int) -> <null: null>
fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0
- at art.Test988.fibonacci(Test988.java:231)
- at art.Test988$RecurOp.applyAsInt(Test988.java:226)
- at art.Test988.doFibTest(Test988.java:297)
- at art.Test988.run(Test988.java:268)
- at Main.main(Main.java:19)
+ art.Test988.fibonacci(Test988.java:250)
+ art.Test988$RecurOp.applyAsInt(Test988.java:245)
+ art.Test988.doFibTest(Test988.java:316)
+ art.Test988.run(Test988.java:287)
+ <additional hidden frames>
.<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
<= public static void art.Test988.doFibTest(int,java.util.function.IntUnaryOperator) -> <null: null>
=> public static native java.lang.Thread java.lang.Thread.currentThread()
-<= public static native java.lang.Thread java.lang.Thread.currentThread() -> <class java.lang.Thread: <non-deterministic>>
+<= public static native java.lang.Thread java.lang.Thread.currentThread() -> <<non-deterministic>: <non-deterministic>>
=> public static native void art.Trace.disableTracing(java.lang.Thread)
diff --git a/test/988-method-trace/src/art/Test988.java b/test/988-method-trace/src/art/Test988.java
index 6a45c0eaa2..e40c612851 100644
--- a/test/988-method-trace/src/art/Test988.java
+++ b/test/988-method-trace/src/art/Test988.java
@@ -31,6 +31,7 @@ public class Test988 {
// Methods with non-deterministic output that should not be printed.
static Set<Method> NON_DETERMINISTIC_OUTPUT_METHODS = new HashSet<>();
+ static Set<Method> NON_DETERMINISTIC_OUTPUT_TYPE_METHODS = new HashSet<>();
static {
try {
@@ -39,6 +40,7 @@ public class Test988 {
} catch (Exception e) {}
try {
NON_DETERMINISTIC_OUTPUT_METHODS.add(Thread.class.getDeclaredMethod("currentThread"));
+ NON_DETERMINISTIC_OUTPUT_TYPE_METHODS.add(Thread.class.getDeclaredMethod("currentThread"));
} catch (Exception e) {}
}
@@ -66,7 +68,16 @@ public class Test988 {
return arrayToString(val);
} else if (val instanceof Throwable) {
StringWriter w = new StringWriter();
- ((Throwable) val).printStackTrace(new PrintWriter(w));
+ Throwable thr = ((Throwable) val);
+ w.write(thr.getClass().getName() + ": " + thr.getMessage() + "\n");
+ for (StackTraceElement e : thr.getStackTrace()) {
+ if (e.getClassName().startsWith("art.")) {
+ w.write("\t" + e + "\n");
+ } else {
+ w.write("\t<additional hidden frames>\n");
+ break;
+ }
+ }
return w.toString();
} else {
return val.toString();
@@ -134,8 +145,16 @@ public class Test988 {
if (val != null) {
klass = val.getClass();
}
+ String klass_print;
+ if (klass == null) {
+ klass_print = "null";
+ } else if (NON_DETERMINISTIC_OUTPUT_TYPE_METHODS.contains(m)) {
+ klass_print = "<non-deterministic>";
+ } else {
+ klass_print = klass.toString();
+ }
System.out.println(
- whitespace(cnt) + "<= " + m + " -> <" + klass + ": " + print + ">");
+ whitespace(cnt) + "<= " + m + " -> <" + klass_print + ": " + print + ">");
}
}
diff --git a/test/988-method-trace/src/art/Trace.java b/test/988-method-trace/src/art/Trace.java
index 9c27c9f69e..ba3d397b0b 100644
--- a/test/988-method-trace/src/art/Trace.java
+++ b/test/988-method-trace/src/art/Trace.java
@@ -25,6 +25,7 @@ public class Trace {
Method exitMethod,
Method fieldAccess,
Method fieldModify,
+ Method singleStep,
Thread thr);
public static native void disableTracing(Thread thr);
@@ -32,14 +33,20 @@ public class Trace {
Method fieldAccess,
Method fieldModify,
Thread thr) {
- enableTracing(methodClass, null, null, fieldAccess, fieldModify, thr);
+ enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr);
}
public static void enableMethodTracing(Class<?> methodClass,
Method entryMethod,
Method exitMethod,
Thread thr) {
- enableTracing(methodClass, entryMethod, exitMethod, null, null, thr);
+ enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr);
+ }
+
+ public static void enableSingleStepTracing(Class<?> methodClass,
+ Method singleStep,
+ Thread thr) {
+ enableTracing(methodClass, null, null, null, null, singleStep, thr);
}
public static native void watchFieldAccess(Field f);
diff --git a/test/989-method-trace-throw/src/art/Trace.java b/test/989-method-trace-throw/src/art/Trace.java
index 9c27c9f69e..ba3d397b0b 100644
--- a/test/989-method-trace-throw/src/art/Trace.java
+++ b/test/989-method-trace-throw/src/art/Trace.java
@@ -25,6 +25,7 @@ public class Trace {
Method exitMethod,
Method fieldAccess,
Method fieldModify,
+ Method singleStep,
Thread thr);
public static native void disableTracing(Thread thr);
@@ -32,14 +33,20 @@ public class Trace {
Method fieldAccess,
Method fieldModify,
Thread thr) {
- enableTracing(methodClass, null, null, fieldAccess, fieldModify, thr);
+ enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr);
}
public static void enableMethodTracing(Class<?> methodClass,
Method entryMethod,
Method exitMethod,
Thread thr) {
- enableTracing(methodClass, entryMethod, exitMethod, null, null, thr);
+ enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr);
+ }
+
+ public static void enableSingleStepTracing(Class<?> methodClass,
+ Method singleStep,
+ Thread thr) {
+ enableTracing(methodClass, null, null, null, null, singleStep, thr);
}
public static native void watchFieldAccess(Field f);
diff --git a/test/990-field-trace/src/art/Trace.java b/test/990-field-trace/src/art/Trace.java
index 9c27c9f69e..ba3d397b0b 100644
--- a/test/990-field-trace/src/art/Trace.java
+++ b/test/990-field-trace/src/art/Trace.java
@@ -25,6 +25,7 @@ public class Trace {
Method exitMethod,
Method fieldAccess,
Method fieldModify,
+ Method singleStep,
Thread thr);
public static native void disableTracing(Thread thr);
@@ -32,14 +33,20 @@ public class Trace {
Method fieldAccess,
Method fieldModify,
Thread thr) {
- enableTracing(methodClass, null, null, fieldAccess, fieldModify, thr);
+ enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr);
}
public static void enableMethodTracing(Class<?> methodClass,
Method entryMethod,
Method exitMethod,
Thread thr) {
- enableTracing(methodClass, entryMethod, exitMethod, null, null, thr);
+ enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr);
+ }
+
+ public static void enableSingleStepTracing(Class<?> methodClass,
+ Method singleStep,
+ Thread thr) {
+ enableTracing(methodClass, null, null, null, null, singleStep, thr);
}
public static native void watchFieldAccess(Field f);
diff --git a/test/991-field-trace-2/src/art/Trace.java b/test/991-field-trace-2/src/art/Trace.java
index 9c27c9f69e..ba3d397b0b 100644
--- a/test/991-field-trace-2/src/art/Trace.java
+++ b/test/991-field-trace-2/src/art/Trace.java
@@ -25,6 +25,7 @@ public class Trace {
Method exitMethod,
Method fieldAccess,
Method fieldModify,
+ Method singleStep,
Thread thr);
public static native void disableTracing(Thread thr);
@@ -32,14 +33,20 @@ public class Trace {
Method fieldAccess,
Method fieldModify,
Thread thr) {
- enableTracing(methodClass, null, null, fieldAccess, fieldModify, thr);
+ enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr);
}
public static void enableMethodTracing(Class<?> methodClass,
Method entryMethod,
Method exitMethod,
Thread thr) {
- enableTracing(methodClass, entryMethod, exitMethod, null, null, thr);
+ enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr);
+ }
+
+ public static void enableSingleStepTracing(Class<?> methodClass,
+ Method singleStep,
+ Thread thr) {
+ enableTracing(methodClass, null, null, null, null, singleStep, thr);
}
public static native void watchFieldAccess(Field f);
diff --git a/test/992-source-data/expected.txt b/test/992-source-data/expected.txt
index 480d8a4fe7..4db8df0ada 100644
--- a/test/992-source-data/expected.txt
+++ b/test/992-source-data/expected.txt
@@ -1,6 +1,6 @@
class art.Test992 is defined in file "Test992.java"
class art.Test992$Target1 is defined in file "Test992.java"
-class art.Test2 is defined in file "Test2.java"
+class art.Target2 is defined in file "Target2.java"
int does not have a known source file because java.lang.RuntimeException: JVMTI_ERROR_ABSENT_INFORMATION
class java.lang.Integer is defined in file "Integer.java"
class java.lang.Object is defined in file "Object.java"
diff --git a/test/992-source-data/src/art/Test2.java b/test/992-source-data/src/art/Target2.java
index dbb1089c5e..7c29d88871 100644
--- a/test/992-source-data/src/art/Test2.java
+++ b/test/992-source-data/src/art/Target2.java
@@ -16,4 +16,4 @@
package art;
-public class Test2 {}
+public class Target2 {}
diff --git a/test/992-source-data/src/art/Test992.java b/test/992-source-data/src/art/Test992.java
index db6ea73856..d9ab112726 100644
--- a/test/992-source-data/src/art/Test992.java
+++ b/test/992-source-data/src/art/Test992.java
@@ -25,7 +25,7 @@ public class Test992 {
public static void run() {
doTest(Test992.class);
doTest(Target1.class);
- doTest(Test2.class);
+ doTest(Target2.class);
doTest(Integer.TYPE);
doTest(Integer.class);
doTest(Object.class);
diff --git a/test/993-breakpoints/breakpoints.cc b/test/993-breakpoints/breakpoints.cc
new file mode 100644
index 0000000000..129207098d
--- /dev/null
+++ b/test/993-breakpoints/breakpoints.cc
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2013 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 <inttypes.h>
+#include <memory>
+#include <stdio.h>
+
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+
+#include "jni.h"
+#include "jvmti.h"
+#include "scoped_local_ref.h"
+
+// Test infrastructure
+#include "jni_binder.h"
+#include "jni_helper.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
+#include "ti_macros.h"
+
+namespace art {
+namespace Test993Breakpoints {
+
+extern "C" JNIEXPORT
+jobject JNICALL Java_art_Test993_constructNative(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jobject target,
+ jclass clazz) {
+ jmethodID method = env->FromReflectedMethod(target);
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+ return env->NewObject(clazz, method);
+}
+
+extern "C" JNIEXPORT
+void JNICALL Java_art_Test993_invokeNative(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jobject target,
+ jclass clazz,
+ jobject thizz) {
+ jmethodID method = env->FromReflectedMethod(target);
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ if (thizz == nullptr) {
+ env->CallStaticVoidMethod(clazz, method);
+ } else {
+ env->CallVoidMethod(thizz, method);
+ }
+}
+
+} // namespace Test993Breakpoints
+} // namespace art
+
diff --git a/test/993-breakpoints/expected.txt b/test/993-breakpoints/expected.txt
new file mode 100644
index 0000000000..962154734b
--- /dev/null
+++ b/test/993-breakpoints/expected.txt
@@ -0,0 +1,613 @@
+Running static invoke
+ Breaking on []
+ Native invoking: public static void art.Test993.breakpoint() args: [this: null]
+ Reflective invoking: public static void art.Test993.breakpoint() args: [this: null]
+ Invoking "Test993::breakpoint"
+ Breaking on [public static void art.Test993.breakpoint() @ 41]
+ Native invoking: public static void art.Test993.breakpoint() args: [this: null]
+ Breakpoint: public static void art.Test993.breakpoint() @ line=41
+ Reflective invoking: public static void art.Test993.breakpoint() args: [this: null]
+ Breakpoint: public static void art.Test993.breakpoint() @ line=41
+ Invoking "Test993::breakpoint"
+ Breakpoint: public static void art.Test993.breakpoint() @ line=41
+Running private static invoke
+ Breaking on []
+ Native invoking: private static void art.Test993.privateBreakpoint() args: [this: null]
+ Invoking "Test993::privateBreakpoint"
+ Breaking on [private static void art.Test993.privateBreakpoint() @ 45]
+ Native invoking: private static void art.Test993.privateBreakpoint() args: [this: null]
+ Breakpoint: private static void art.Test993.privateBreakpoint() @ line=45
+ Invoking "Test993::privateBreakpoint"
+ Breakpoint: private static void art.Test993.privateBreakpoint() @ line=45
+Running interface static invoke
+ Breaking on []
+ Native invoking: public static void art.Test993$Breakable.iBreakpoint() args: [this: null]
+ Reflective invoking: public static void art.Test993$Breakable.iBreakpoint() args: [this: null]
+ Invoking "Breakable::iBreakpoint"
+ Breaking on [public static void art.Test993$Breakable.iBreakpoint() @ 51]
+ Native invoking: public static void art.Test993$Breakable.iBreakpoint() args: [this: null]
+ Breakpoint: public static void art.Test993$Breakable.iBreakpoint() @ line=51
+ Reflective invoking: public static void art.Test993$Breakable.iBreakpoint() args: [this: null]
+ Breakpoint: public static void art.Test993$Breakable.iBreakpoint() @ line=51
+ Invoking "Breakable::iBreakpoint"
+ Breakpoint: public static void art.Test993$Breakable.iBreakpoint() @ line=51
+Running TestClass1 invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1]
+ Invoking "((Breakable)new TestClass1()).breakit()"
+ Invoking "new TestClass1().breakit()"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass1()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass1().breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+Running TestClass1ext invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Native invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Reflective invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Invoking "((Breakable)new TestClass1ext()).breakit()"
+ Invoking "((TestClass1)new TestClass1ext()).breakit()"
+ Invoking "new TestClass1ext().breakit()"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass1ext()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass1)new TestClass1ext()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass1ext().breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public void art.Test993$TestClass1ext.breakit() @ 74]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Native invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Reflective invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Invoking "((Breakable)new TestClass1ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Invoking "((TestClass1)new TestClass1ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Invoking "new TestClass1ext().breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass1ext.breakit() @ 74]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass1ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass1)new TestClass1ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass1ext().breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+Running TestClass2 invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Invoking "((Breakable)new TestClass2()).breakit()"
+ Invoking "new TestClass2().breakit()"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Invoking "((Breakable)new TestClass2()).breakit()"
+ Invoking "new TestClass2().breakit()"
+ Breaking on [public void art.Test993$TestClass2.breakit() @ 83]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2().breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass2.breakit() @ 83]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2().breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+Running TestClass2ext invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Invoking "new TestClass2ext().breakit())"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Invoking "new TestClass2ext().breakit())"
+ Breaking on [public void art.Test993$TestClass2.breakit() @ 83]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Breaking on [public void art.Test993$TestClass2ext.breakit() @ 91]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass2.breakit() @ 83]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass2ext.breakit() @ 91]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breaking on [public void art.Test993$TestClass2.breakit() @ 83, public void art.Test993$TestClass2ext.breakit() @ 91]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass2.breakit() @ 83, public void art.Test993$TestClass2ext.breakit() @ 91]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+Running TestClass3 invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Invoking "((Breakable)new TestClass3()).breakit()"
+ Invoking "new TestClass3().breakit())"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3().breakit())"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public void art.Test993$TestClass3.breakit() @ 99]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((Breakable)new TestClass3()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "new TestClass3().breakit())"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass3.breakit() @ 99]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3().breakit())"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+Running TestClass3ext invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Invoking "new TestClass3ext().breakit())"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public void art.Test993$TestClass3.breakit() @ 99]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breaking on [public void art.Test993$TestClass3ext.breakit() @ 108]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass3.breakit() @ 99]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass3ext.breakit() @ 108]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public void art.Test993$TestClass3.breakit() @ 99, public void art.Test993$TestClass3ext.breakit() @ 108]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass3.breakit() @ 99, public void art.Test993$TestClass3ext.breakit() @ 108]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+Running private instance invoke
+ Breaking on []
+ Native invoking: private void art.Test993$TestClass4.privateMethod() args: [this: TestClass4]
+ Invoking "new TestClass4().callPrivateMethod()"
+ Breaking on [private void art.Test993$TestClass4.privateMethod() @ 118]
+ Native invoking: private void art.Test993$TestClass4.privateMethod() args: [this: TestClass4]
+ Breakpoint: private void art.Test993$TestClass4.privateMethod() @ line=118
+ Invoking "new TestClass4().callPrivateMethod()"
+ Breakpoint: private void art.Test993$TestClass4.privateMethod() @ line=118
+Running TestClass1 constructor
+ Breaking on []
+ Native constructor: public art.Test993$TestClass1(), type: class art.Test993$TestClass1
+ Created: TestClass1
+ Reflective constructor: public art.Test993$TestClass1()
+ Created: TestClass1
+ Constructing: new TestClass1()
+ Created: TestClass1
+ Breaking on [public art.Test993$TestClass1() @ 62]
+ Native constructor: public art.Test993$TestClass1(), type: class art.Test993$TestClass1
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1
+ Reflective constructor: public art.Test993$TestClass1()
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1
+ Constructing: new TestClass1()
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1
+Running TestClass1ext constructor
+ Breaking on []
+ Native constructor: public art.Test993$TestClass1ext(), type: class art.Test993$TestClass1ext
+ Created: TestClass1Ext
+ Reflective constructor: public art.Test993$TestClass1ext()
+ Created: TestClass1Ext
+ Constructing: new TestClass1ext()
+ Created: TestClass1Ext
+ Breaking on [public art.Test993$TestClass1() @ 62]
+ Native constructor: public art.Test993$TestClass1ext(), type: class art.Test993$TestClass1ext
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Reflective constructor: public art.Test993$TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Constructing: new TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Breaking on [public art.Test993$TestClass1ext() @ 70]
+ Native constructor: public art.Test993$TestClass1ext(), type: class art.Test993$TestClass1ext
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Created: TestClass1Ext
+ Reflective constructor: public art.Test993$TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Created: TestClass1Ext
+ Constructing: new TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Created: TestClass1Ext
+ Breaking on [public art.Test993$TestClass1() @ 62, public art.Test993$TestClass1ext() @ 70]
+ Native constructor: public art.Test993$TestClass1ext(), type: class art.Test993$TestClass1ext
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Reflective constructor: public art.Test993$TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Constructing: new TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
diff --git a/test/993-breakpoints/info.txt b/test/993-breakpoints/info.txt
new file mode 100644
index 0000000000..b5eb5464b7
--- /dev/null
+++ b/test/993-breakpoints/info.txt
@@ -0,0 +1,7 @@
+Test basic JVMTI breakpoint functionality.
+
+This test places a breakpoint on the first instruction of a number of functions
+that are entered in every way possible for the given class of method.
+
+It also tests that breakpoints don't interfere with each other by having
+multiple breakpoints be set at once.
diff --git a/test/993-breakpoints/run b/test/993-breakpoints/run
new file mode 100755
index 0000000000..51875a7e86
--- /dev/null
+++ b/test/993-breakpoints/run
@@ -0,0 +1,18 @@
+#!/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.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/993-breakpoints/src/Main.java b/test/993-breakpoints/src/Main.java
new file mode 100644
index 0000000000..b11f6f8174
--- /dev/null
+++ b/test/993-breakpoints/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test993.run();
+ }
+}
diff --git a/test/993-breakpoints/src/art/Breakpoint.java b/test/993-breakpoints/src/art/Breakpoint.java
new file mode 100644
index 0000000000..2a370ebd40
--- /dev/null
+++ b/test/993-breakpoints/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * 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.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/993-breakpoints/src/art/Test993.java b/test/993-breakpoints/src/art/Test993.java
new file mode 100644
index 0000000000..781ebffc0f
--- /dev/null
+++ b/test/993-breakpoints/src/art/Test993.java
@@ -0,0 +1,498 @@
+/*
+ * 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.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Set;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.Collection;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.function.IntUnaryOperator;
+import java.util.function.Supplier;
+
+public class Test993 {
+
+ public static final Breakpoint.Manager MANAGER = new Breakpoint.Manager();
+
+ // A function we can use as a start breakpoint.
+ public static void breakpoint() {
+ return;
+ }
+
+ private static void privateBreakpoint() {
+ return;
+ }
+
+ // An interface with a default method we can break on.
+ static interface Breakable {
+ public static void iBreakpoint() {
+ return;
+ }
+
+ public default void breakit() {
+ return;
+ }
+ }
+
+ // A class that has a default method we breakpoint on.
+ public static class TestClass1 implements Breakable {
+ public TestClass1() {
+ super();
+ }
+ public String toString() { return "TestClass1"; }
+ }
+
+ // A class that overrides a default method that we can breakpoint on and calls super.
+ public static class TestClass1ext extends TestClass1 {
+ public TestClass1ext() {
+ super();
+ }
+ public String toString() { return "TestClass1Ext"; }
+ public void breakit() {
+ super.breakit();
+ }
+ }
+
+
+ // A class that overrides a default method that we can breakpoint on.
+ public static class TestClass2 implements Breakable {
+ public String toString() { return "TestClass2"; }
+ public void breakit() {
+ return;
+ }
+ }
+
+ // A class that overrides a default method that we can breakpoint on and calls super.
+ public static class TestClass2ext extends TestClass2 {
+ public String toString() { return "TestClass2ext"; }
+ public void breakit() {
+ super.breakit();
+ }
+ }
+
+ // A class that overrides a default method and calls it directly with interface invoke-super
+ public static class TestClass3 implements Breakable {
+ public String toString() { return "TestClass3"; }
+ public void breakit() {
+ Breakable.super.breakit();
+ }
+ }
+
+ // A class that overrides a default method that we can breakpoint on and calls super to a class
+ // that uses interface-invoke-super.
+ public static class TestClass3ext extends TestClass3 {
+ public String toString() { return "TestClass3ext"; }
+ public void breakit() {
+ super.breakit();
+ }
+ }
+
+ public static class TestClass4 {
+ public String toString() { return "TestClass4"; }
+ public void callPrivateMethod() {
+ privateMethod();
+ }
+ private void privateMethod() {
+ return;
+ }
+ }
+
+ public static void notifyBreakpointReached(Thread thr, Executable e, long loc) {
+ System.out.println("\t\t\tBreakpoint: " + e + " @ line=" + Breakpoint.locationToLine(e, loc));
+ }
+
+ public static interface ThrowRunnable extends Runnable {
+ public default void run() {
+ try {
+ runThrow();
+ } catch (Exception e) {
+ throw new Error("Caught error while running " + this, e);
+ }
+ }
+ public void runThrow() throws Exception;
+ }
+
+ public static class InvokeDirect implements Runnable {
+ String msg;
+ Runnable r;
+ public InvokeDirect(String msg, Runnable r) {
+ this.msg = msg;
+ this.r = r;
+ }
+ @Override
+ public void run() {
+ System.out.println("\t\tInvoking \"" + msg + "\"");
+ r.run();
+ }
+ }
+
+ public static class InvokeReflect implements ThrowRunnable {
+ Method m;
+ Object this_arg;
+ public InvokeReflect(Method m, Object this_arg) {
+ this.m = m;
+ this.this_arg = this_arg;
+ }
+
+ @Override
+ public void runThrow() throws Exception {
+ System.out.println("\t\tReflective invoking: " + m + " args: [this: " + this_arg + "]");
+ m.invoke(this_arg);
+ }
+ }
+
+ public static class InvokeNative implements Runnable {
+ Method m;
+ Object this_arg;
+ public InvokeNative(Method m, Object this_arg) {
+ this.m = m;
+ this.this_arg = this_arg;
+ }
+
+ @Override
+ public void run() {
+ System.out.println("\t\tNative invoking: " + m + " args: [this: " + this_arg + "]");
+ invokeNative(m, m.getDeclaringClass(), this_arg);
+ }
+ }
+
+ public static native void invokeNative(Method m, Class<?> clazz, Object thizz);
+
+ public static class ConstructDirect implements Runnable {
+ String msg;
+ Supplier<Object> s;
+ public ConstructDirect(String msg, Supplier<Object> s) {
+ this.msg = msg;
+ this.s = s;
+ }
+
+ @Override
+ public void run() {
+ System.out.println("\t\tConstructing: " + msg);
+ System.out.println("\t\t\tCreated: " + s.get());
+ }
+ }
+
+ public static class ConstructReflect implements ThrowRunnable {
+ Constructor<?> m;
+ public ConstructReflect(Constructor<?> m) {
+ this.m = m;
+ }
+
+ @Override
+ public void runThrow() throws Exception {
+ System.out.println("\t\tReflective constructor: " + m);
+ System.out.println("\t\t\tCreated: " + m.newInstance());
+ }
+ }
+
+ public static class ConstructNative implements Runnable {
+ Constructor<?> m;
+ Class type;
+ public ConstructNative(Constructor<?> m) {
+ this.m = m;
+ this.type = m.getDeclaringClass();
+ }
+
+ @Override
+ public void run() {
+ System.out.println("\t\tNative constructor: " + m + ", type: " + type);
+ System.out.println("\t\t\tCreated: " + constructNative(m, type));
+ }
+ }
+
+ public static native Object constructNative(Constructor m, Class<?> clazz);
+
+ private static <T> List<List<T>> combinations(List<T> items, int len) {
+ if (len > items.size()) {
+ throw new Error("Bad length" + len + " " + items);
+ }
+ if (len == 1) {
+ List<List<T>> out = new ArrayList<>();
+ for (T t : items) {
+ out.add(Arrays.asList(t));
+ }
+ return out;
+ }
+ List<List<T>> out = new ArrayList<>();
+ for (int rem = 0; rem <= items.size() - len; rem++) {
+ for (List<T> others : combinations(items.subList(rem + 1, items.size()), len - 1)) {
+ List<T> newone = new ArrayList<>();
+ newone.add(items.get(rem));
+ newone.addAll(others);
+ out.add(newone);
+ }
+ }
+ return out;
+ }
+
+ private static <T> List<List<T>> allCombinations(List<T> items) {
+ List<List<T>> out = new ArrayList<List<T>>();
+ out.add(new ArrayList<>());
+ for (int i = 0; i < items.size(); i++) {
+ out.addAll(combinations(items, i + 1));
+ }
+ return out;
+ }
+
+ private static Breakpoint.Manager.BP BP(Executable m) {
+ return new Breakpoint.Manager.BP(m);
+ }
+
+ public static void run() throws Exception {
+ // Set up breakpoints
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ Breakpoint.startBreakpointWatch(
+ Test993.class,
+ Test993.class.getDeclaredMethod("notifyBreakpointReached",
+ Thread.class, Executable.class, Long.TYPE),
+ Thread.currentThread());
+
+ runMethodTests();
+ runConstructorTests();
+
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ }
+
+ public static void runConstructorTests() throws Exception {
+ // The constructors we will be breaking on.
+ Constructor<?> tc1_construct = TestClass1.class.getConstructor();
+ Constructor<?> tc1ext_construct = TestClass1ext.class.getConstructor();
+
+ Runnable[] tc1_constructors = new Runnable[] {
+ new ConstructNative(tc1_construct),
+ new ConstructReflect(tc1_construct),
+ new ConstructDirect("new TestClass1()", TestClass1::new),
+ };
+ Breakpoint.Manager.BP[] tc1_bps = new Breakpoint.Manager.BP[] {
+ BP(tc1_construct),
+ };
+ runTestGroups("TestClass1 constructor", tc1_constructors, tc1_bps);
+
+ Runnable[] tc1ext_constructors = new Runnable[] {
+ new ConstructNative(tc1ext_construct),
+ new ConstructReflect(tc1ext_construct),
+ new ConstructDirect("new TestClass1ext()", TestClass1ext::new),
+ };
+ Breakpoint.Manager.BP[] tc1ext_bps = new Breakpoint.Manager.BP[] {
+ BP(tc1_construct), BP(tc1ext_construct),
+ };
+ runTestGroups("TestClass1ext constructor", tc1ext_constructors, tc1ext_bps);
+ }
+
+ public static void runMethodTests() throws Exception {
+ // The methods we will be breaking on.
+ Method breakpoint_method = Test993.class.getDeclaredMethod("breakpoint");
+ Method private_breakpoint_method = Test993.class.getDeclaredMethod("privateBreakpoint");
+ Method i_breakpoint_method = Breakable.class.getDeclaredMethod("iBreakpoint");
+ Method breakit_method = Breakable.class.getDeclaredMethod("breakit");
+ Method breakit_method_tc1ext = TestClass1ext.class.getDeclaredMethod("breakit");
+ Method breakit_method_tc2 = TestClass2.class.getDeclaredMethod("breakit");
+ Method breakit_method_tc2ext = TestClass2ext.class.getDeclaredMethod("breakit");
+ Method breakit_method_tc3 = TestClass3.class.getDeclaredMethod("breakit");
+ Method breakit_method_tc3ext = TestClass3ext.class.getDeclaredMethod("breakit");
+ Method private_method = TestClass4.class.getDeclaredMethod("privateMethod");
+
+ // Static class function
+ Runnable[] static_invokes = new Runnable[] {
+ new InvokeNative(breakpoint_method, null),
+
+ new InvokeReflect(breakpoint_method, null),
+
+ new InvokeDirect("Test993::breakpoint", Test993::breakpoint),
+ };
+ Breakpoint.Manager.BP[] static_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(breakpoint_method)
+ };
+ runTestGroups("static invoke", static_invokes, static_breakpoints);
+
+ // Static private class function
+ Runnable[] private_static_invokes = new Runnable[] {
+ new InvokeNative(private_breakpoint_method, null),
+
+ new InvokeDirect("Test993::privateBreakpoint", Test993::privateBreakpoint),
+ };
+ Breakpoint.Manager.BP[] private_static_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(private_breakpoint_method)
+ };
+ runTestGroups("private static invoke", private_static_invokes, private_static_breakpoints);
+
+ // Static interface function.
+ Runnable[] i_static_invokes = new Runnable[] {
+ new InvokeNative(i_breakpoint_method, null),
+
+ new InvokeReflect(i_breakpoint_method, null),
+
+ new InvokeDirect("Breakable::iBreakpoint", Breakable::iBreakpoint),
+ };
+ Breakpoint.Manager.BP[] i_static_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(i_breakpoint_method)
+ };
+ runTestGroups("interface static invoke", i_static_invokes, i_static_breakpoints);
+
+ // Call default method through a class.
+ Runnable[] tc1_invokes = new Runnable[] {
+ new InvokeNative(breakit_method, new TestClass1()),
+
+ new InvokeReflect(breakit_method, new TestClass1()),
+
+ new InvokeDirect("((Breakable)new TestClass1()).breakit()",
+ () -> ((Breakable)new TestClass1()).breakit()),
+ new InvokeDirect("new TestClass1().breakit()",
+ () -> new TestClass1().breakit()),
+ };
+ Breakpoint.Manager.BP[] tc1_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(breakit_method)
+ };
+ runTestGroups("TestClass1 invokes", tc1_invokes, tc1_breakpoints);
+
+ // Call default method through an override and normal invoke-super
+ Runnable[] tc1ext_invokes = new Runnable[] {
+ new InvokeNative(breakit_method, new TestClass1ext()),
+ new InvokeNative(breakit_method_tc1ext, new TestClass1ext()),
+
+ new InvokeReflect(breakit_method, new TestClass1ext()),
+ new InvokeReflect(breakit_method_tc1ext, new TestClass1ext()),
+
+ new InvokeDirect("((Breakable)new TestClass1ext()).breakit()",
+ () -> ((Breakable)new TestClass1ext()).breakit()),
+ new InvokeDirect("((TestClass1)new TestClass1ext()).breakit()",
+ () -> ((TestClass1)new TestClass1ext()).breakit()),
+ new InvokeDirect("new TestClass1ext().breakit()",
+ () -> new TestClass1ext().breakit()),
+ };
+ Breakpoint.Manager.BP[] tc1ext_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(breakit_method), BP(breakit_method_tc1ext)
+ };
+ runTestGroups("TestClass1ext invokes", tc1ext_invokes, tc1ext_breakpoints);
+
+ // Override default/interface method.
+ Runnable[] tc2_invokes = new Runnable[] {
+ new InvokeNative(breakit_method, new TestClass2()),
+ new InvokeNative(breakit_method_tc2, new TestClass2()),
+
+ new InvokeReflect(breakit_method, new TestClass2()),
+ new InvokeReflect(breakit_method_tc2, new TestClass2()),
+
+ new InvokeDirect("((Breakable)new TestClass2()).breakit()",
+ () -> ((Breakable)new TestClass2()).breakit()),
+ new InvokeDirect("new TestClass2().breakit()",
+ () -> new TestClass2().breakit()),
+ };
+ Breakpoint.Manager.BP[] tc2_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(breakit_method), BP(breakit_method_tc2)
+ };
+ runTestGroups("TestClass2 invokes", tc2_invokes, tc2_breakpoints);
+
+ // Call overridden method using invoke-super
+ Runnable[] tc2ext_invokes = new Runnable[] {
+ new InvokeNative(breakit_method, new TestClass2ext()),
+ new InvokeNative(breakit_method_tc2, new TestClass2ext()),
+ new InvokeNative(breakit_method_tc2ext, new TestClass2ext()),
+
+ new InvokeReflect(breakit_method, new TestClass2ext()),
+ new InvokeReflect(breakit_method_tc2, new TestClass2ext()),
+ new InvokeReflect(breakit_method_tc2ext, new TestClass2ext()),
+
+ new InvokeDirect("((Breakable)new TestClass2ext()).breakit()",
+ () -> ((Breakable)new TestClass2ext()).breakit()),
+ new InvokeDirect("((TestClass2)new TestClass2ext()).breakit()",
+ () -> ((TestClass2)new TestClass2ext()).breakit()),
+ new InvokeDirect("new TestClass2ext().breakit())",
+ () -> new TestClass2ext().breakit()),
+ };
+ Breakpoint.Manager.BP[] tc2ext_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(breakit_method), BP(breakit_method_tc2), BP(breakit_method_tc2ext)
+ };
+ runTestGroups("TestClass2ext invokes", tc2ext_invokes, tc2ext_breakpoints);
+
+ // Override default method and call it using interface-invoke-super
+ Runnable[] tc3_invokes = new Runnable[] {
+ new InvokeNative(breakit_method, new TestClass3()),
+ new InvokeNative(breakit_method_tc3, new TestClass3()),
+
+ new InvokeReflect(breakit_method, new TestClass3()),
+ new InvokeReflect(breakit_method_tc3, new TestClass3()),
+
+ new InvokeDirect("((Breakable)new TestClass3()).breakit()",
+ () -> ((Breakable)new TestClass3()).breakit()),
+ new InvokeDirect("new TestClass3().breakit())",
+ () -> new TestClass3().breakit()),
+ };
+ Breakpoint.Manager.BP[] tc3_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(breakit_method), BP(breakit_method_tc3)
+ };
+ runTestGroups("TestClass3 invokes", tc3_invokes, tc3_breakpoints);
+
+ // Call overridden method using invoke-super
+ Runnable[] tc3ext_invokes = new Runnable[] {
+ new InvokeNative(breakit_method, new TestClass3ext()),
+ new InvokeNative(breakit_method_tc3, new TestClass3ext()),
+ new InvokeNative(breakit_method_tc3ext, new TestClass3ext()),
+
+ new InvokeReflect(breakit_method, new TestClass3ext()),
+ new InvokeReflect(breakit_method_tc3, new TestClass3ext()),
+ new InvokeReflect(breakit_method_tc3ext, new TestClass3ext()),
+
+ new InvokeDirect("((Breakable)new TestClass3ext()).breakit()",
+ () -> ((Breakable)new TestClass3ext()).breakit()),
+ new InvokeDirect("((TestClass3)new TestClass3ext()).breakit()",
+ () -> ((TestClass3)new TestClass3ext()).breakit()),
+ new InvokeDirect("new TestClass3ext().breakit())",
+ () -> new TestClass3ext().breakit()),
+ };
+ Breakpoint.Manager.BP[] tc3ext_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(breakit_method), BP(breakit_method_tc3), BP(breakit_method_tc3ext)
+ };
+ runTestGroups("TestClass3ext invokes", tc3ext_invokes, tc3ext_breakpoints);
+
+ // private instance method.
+ Runnable[] private_instance_invokes = new Runnable[] {
+ new InvokeNative(private_method, new TestClass4()),
+
+ new InvokeDirect("new TestClass4().callPrivateMethod()",
+ () -> new TestClass4().callPrivateMethod()),
+ };
+ Breakpoint.Manager.BP[] private_instance_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(private_method)
+ };
+ runTestGroups(
+ "private instance invoke", private_instance_invokes, private_instance_breakpoints);
+ }
+
+ private static void runTestGroups(String name,
+ Runnable[] invokes,
+ Breakpoint.Manager.BP[] breakpoints) throws Exception {
+ System.out.println("Running " + name);
+ for (List<Breakpoint.Manager.BP> bps : allCombinations(Arrays.asList(breakpoints))) {
+ System.out.println("\tBreaking on " + bps);
+ for (Runnable test : invokes) {
+ MANAGER.clearAllBreakpoints();
+ MANAGER.setBreakpoints(bps.toArray(new Breakpoint.Manager.BP[0]));
+ test.run();
+ }
+ }
+ }
+}
diff --git a/test/994-breakpoint-line/expected.txt b/test/994-breakpoint-line/expected.txt
new file mode 100644
index 0000000000..5899659b3c
--- /dev/null
+++ b/test/994-breakpoint-line/expected.txt
@@ -0,0 +1,34 @@
+Breaking on line: 29 calling with arg: true
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=29
+ argument was true
+Breaking on line: 29 calling with arg: false
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=29
+ argument was false
+Breaking on line: 30 calling with arg: true
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=30
+ argument was true
+Breaking on line: 30 calling with arg: false
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=30
+ argument was false
+Breaking on line: 31 calling with arg: true
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=31
+ argument was true
+Breaking on line: 31 calling with arg: false
+ argument was false
+Breaking on line: 33 calling with arg: true
+ argument was true
+Breaking on line: 33 calling with arg: false
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=33
+ argument was false
+Breaking on line: 35 calling with arg: true
+ argument was true
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=35
+Breaking on line: 35 calling with arg: false
+ argument was false
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=35
+Breaking on line: 36 calling with arg: true
+ argument was true
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=36
+Breaking on line: 36 calling with arg: false
+ argument was false
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=36
diff --git a/test/994-breakpoint-line/info.txt b/test/994-breakpoint-line/info.txt
new file mode 100644
index 0000000000..210dea0471
--- /dev/null
+++ b/test/994-breakpoint-line/info.txt
@@ -0,0 +1,5 @@
+Test basic JVMTI breakpoint functionality.
+
+This test ensures we can place breakpoints on particular lines of a method. It
+sets breakpoints on each line in turn of a function with multiple execution
+paths and then runs the function, receiving the breakpoint events.
diff --git a/test/994-breakpoint-line/run b/test/994-breakpoint-line/run
new file mode 100755
index 0000000000..51875a7e86
--- /dev/null
+++ b/test/994-breakpoint-line/run
@@ -0,0 +1,18 @@
+#!/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.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/994-breakpoint-line/src/Main.java b/test/994-breakpoint-line/src/Main.java
new file mode 100644
index 0000000000..39cfeb3ee4
--- /dev/null
+++ b/test/994-breakpoint-line/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test994.run();
+ }
+}
diff --git a/test/994-breakpoint-line/src/art/Breakpoint.java b/test/994-breakpoint-line/src/art/Breakpoint.java
new file mode 100644
index 0000000000..bbb89f707f
--- /dev/null
+++ b/test/994-breakpoint-line/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * 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.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/994-breakpoint-line/src/art/Test994.java b/test/994-breakpoint-line/src/art/Test994.java
new file mode 100644
index 0000000000..6a1c354b39
--- /dev/null
+++ b/test/994-breakpoint-line/src/art/Test994.java
@@ -0,0 +1,72 @@
+/*
+ * 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.Arrays;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+
+public class Test994 {
+ public static final Breakpoint.Manager MANAGER = new Breakpoint.Manager();
+ public static void doNothing() {}
+
+ // Method with multiple paths we can break on.
+ public static void doMultiPath(boolean bit) {
+ doNothing();
+ if (bit) {
+ System.out.println("\targument was true");
+ } else {
+ System.out.println("\targument was false");
+ }
+ doNothing();
+ }
+
+ public static void notifyBreakpointReached(Thread thr, Executable e, long loc) {
+ System.out.println(
+ "\tBreakpoint reached: " + e + " @ line=" + Breakpoint.locationToLine(e, loc));
+ }
+
+ public static void run() throws Exception {
+ // Set up breakpoints
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ Breakpoint.startBreakpointWatch(
+ Test994.class,
+ Test994.class.getDeclaredMethod(
+ "notifyBreakpointReached", Thread.class, Executable.class, Long.TYPE),
+ Thread.currentThread());
+
+ Method multipath_method = Test994.class.getDeclaredMethod("doMultiPath", Boolean.TYPE);
+
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(multipath_method);
+
+ // Make sure everything is in the same order.
+ Arrays.sort(lines);
+
+ boolean[] values = new boolean[] { true, false };
+
+ for (Breakpoint.LineNumber line : lines) {
+ MANAGER.clearAllBreakpoints();
+ MANAGER.setBreakpoint(multipath_method, line.location);
+ for (boolean arg : values) {
+ System.out.println("Breaking on line: " + line.line + " calling with arg: " + arg);
+ doMultiPath(arg);
+ }
+ }
+
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ }
+}
diff --git a/test/995-breakpoints-throw/expected.txt b/test/995-breakpoints-throw/expected.txt
new file mode 100644
index 0000000000..a565b7cf14
--- /dev/null
+++ b/test/995-breakpoints-throw/expected.txt
@@ -0,0 +1,34 @@
+Test "call Test995::breakpoint": Running breakpoint with handler "do nothing"
+ Breakpoint: public static void art.Test995.breakpoint() @ line=34
+Test "call Test995::breakpoint": No error caught with handler "do nothing"
+Test "call Test995::breakpoint": Finished running with handler "do nothing"
+Test "call Test995::breakpointCatch": Running breakpoint with handler "do nothing"
+ Breakpoint: public static void art.Test995.breakpointCatch() @ line=48
+Test "call Test995::breakpointCatch": No error caught with handler "do nothing"
+Test "call Test995::breakpointCatch": Finished running with handler "do nothing"
+Test "call Test995::breakpointCatchLate": Running breakpoint with handler "do nothing"
+ Breakpoint: public static void art.Test995.breakpointCatchLate() @ line=38
+Test "call Test995::breakpointCatchLate": No error caught with handler "do nothing"
+Test "call Test995::breakpointCatchLate": Finished running with handler "do nothing"
+Test "catch subroutine Test995::breakpoint": Running breakpoint with handler "do nothing"
+ Breakpoint: public static void art.Test995.breakpoint() @ line=34
+Test "catch subroutine Test995::breakpoint": No error caught with handler "do nothing"
+Test "catch subroutine Test995::breakpoint": Finished running with handler "do nothing"
+Test "call Test995::breakpoint": Running breakpoint with handler "throw"
+ Breakpoint: public static void art.Test995.breakpoint() @ line=34
+Test "call Test995::breakpoint": Caught error java.lang.Error:"throwing error!" with handler "throw"
+Test "call Test995::breakpoint": Finished running with handler "throw"
+Test "call Test995::breakpointCatch": Running breakpoint with handler "throw"
+ Breakpoint: public static void art.Test995.breakpointCatch() @ line=48
+Caught java.lang.Error: "throwing error!"
+Test "call Test995::breakpointCatch": No error caught with handler "throw"
+Test "call Test995::breakpointCatch": Finished running with handler "throw"
+Test "call Test995::breakpointCatchLate": Running breakpoint with handler "throw"
+ Breakpoint: public static void art.Test995.breakpointCatchLate() @ line=38
+Test "call Test995::breakpointCatchLate": Caught error java.lang.Error:"throwing error!" with handler "throw"
+Test "call Test995::breakpointCatchLate": Finished running with handler "throw"
+Test "catch subroutine Test995::breakpoint": Running breakpoint with handler "throw"
+ Breakpoint: public static void art.Test995.breakpoint() @ line=34
+Caught java.lang.Error:"throwing error!"
+Test "catch subroutine Test995::breakpoint": No error caught with handler "throw"
+Test "catch subroutine Test995::breakpoint": Finished running with handler "throw"
diff --git a/test/995-breakpoints-throw/info.txt b/test/995-breakpoints-throw/info.txt
new file mode 100644
index 0000000000..80f9cf94bf
--- /dev/null
+++ b/test/995-breakpoints-throw/info.txt
@@ -0,0 +1,6 @@
+Test basic JVMTI breakpoint functionality.
+
+Tests that it is possible to throw exceptions while handling breakpoint events
+and that they are handled appropriately. This includes checking that it is
+possible for the method being breakpointed to catch exceptions thrown by the
+handler.
diff --git a/test/995-breakpoints-throw/run b/test/995-breakpoints-throw/run
new file mode 100755
index 0000000000..51875a7e86
--- /dev/null
+++ b/test/995-breakpoints-throw/run
@@ -0,0 +1,18 @@
+#!/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.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/995-breakpoints-throw/src/Main.java b/test/995-breakpoints-throw/src/Main.java
new file mode 100644
index 0000000000..6f80b43255
--- /dev/null
+++ b/test/995-breakpoints-throw/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test995.run();
+ }
+}
diff --git a/test/995-breakpoints-throw/src/art/Breakpoint.java b/test/995-breakpoints-throw/src/art/Breakpoint.java
new file mode 100644
index 0000000000..bbb89f707f
--- /dev/null
+++ b/test/995-breakpoints-throw/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * 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.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/995-breakpoints-throw/src/art/Test995.java b/test/995-breakpoints-throw/src/art/Test995.java
new file mode 100644
index 0000000000..a4023fb80a
--- /dev/null
+++ b/test/995-breakpoints-throw/src/art/Test995.java
@@ -0,0 +1,136 @@
+/*
+ * 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.Arrays;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+
+public class Test995 {
+ public static final Breakpoint.Manager MANAGER = new Breakpoint.Manager();
+ public static BreakpointHandler HANDLER = null;
+
+ public static void doNothing() { }
+
+ public static interface BreakpointHandler {
+ public void breakpointReached(Executable e, long loc);
+ }
+
+ public static void breakpoint() {
+ return;
+ }
+
+ public static void breakpointCatchLate() {
+ doNothing();
+ try {
+ doNothing();
+ } catch (Throwable t) {
+ System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
+ }
+ }
+
+ public static void breakpointCatch() {
+ try {
+ doNothing();
+ } catch (Throwable t) {
+ System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
+ }
+ }
+
+ public static void notifyBreakpointReached(Thread thr, Executable e, long loc) {
+ System.out.println("\tBreakpoint: " + e + " @ line=" + Breakpoint.locationToLine(e, loc));
+ HANDLER.breakpointReached(e, loc);
+ }
+
+
+ public static BreakpointHandler makeHandler(String name, BreakpointHandler h) {
+ return new BreakpointHandler() {
+ public String toString() {
+ return name;
+ }
+ public void breakpointReached(Executable e, long loc) {
+ h.breakpointReached(e, loc);
+ }
+ };
+ }
+
+ public static Runnable makeTest(String name, Runnable test) {
+ return new Runnable() {
+ public String toString() { return name; }
+ public void run() { test.run(); }
+ };
+ }
+
+ public static void run() throws Exception {
+ // Set up breakpoints
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ Breakpoint.startBreakpointWatch(
+ Test995.class,
+ Test995.class.getDeclaredMethod(
+ "notifyBreakpointReached", Thread.class, Executable.class, Long.TYPE),
+ Thread.currentThread());
+
+ Method breakpoint_method = Test995.class.getDeclaredMethod("breakpoint");
+ Method breakpoint_catch_method = Test995.class.getDeclaredMethod("breakpointCatch");
+ Method breakpoint_catch_late_method = Test995.class.getDeclaredMethod("breakpointCatchLate");
+ MANAGER.setBreakpoint(breakpoint_method, Breakpoint.getStartLocation(breakpoint_method));
+ MANAGER.setBreakpoint(
+ breakpoint_catch_method, Breakpoint.getStartLocation(breakpoint_catch_method));
+ MANAGER.setBreakpoint(
+ breakpoint_catch_late_method, Breakpoint.getStartLocation(breakpoint_catch_late_method));
+
+ BreakpointHandler[] handlers = new BreakpointHandler[] {
+ makeHandler("do nothing", (e, l) -> {}),
+ makeHandler("throw", (e, l) -> { throw new Error("throwing error!"); }),
+ };
+
+ Runnable[] tests = new Runnable[] {
+ makeTest("call Test995::breakpoint", Test995::breakpoint),
+ makeTest("call Test995::breakpointCatch", Test995::breakpointCatch),
+ makeTest("call Test995::breakpointCatchLate", Test995::breakpointCatchLate),
+ makeTest("catch subroutine Test995::breakpoint",
+ () -> {
+ try {
+ breakpoint();
+ } catch (Throwable t) {
+ System.out.printf("Caught %s:\"%s\"\n", t.getClass().getName(), t.getMessage());
+ }
+ }),
+ };
+
+ for (BreakpointHandler handler : handlers) {
+ for (Runnable test : tests) {
+ try {
+ HANDLER = handler;
+ System.out.printf("Test \"%s\": Running breakpoint with handler \"%s\"\n",
+ test, handler);
+ test.run();
+ System.out.printf("Test \"%s\": No error caught with handler \"%s\"\n",
+ test, handler);
+ } catch (Throwable e) {
+ System.out.printf("Test \"%s\": Caught error %s:\"%s\" with handler \"%s\"\n",
+ test, e.getClass().getName(), e.getMessage(), handler);
+ }
+ System.out.printf("Test \"%s\": Finished running with handler \"%s\"\n", test, handler);
+ HANDLER = null;
+ }
+ }
+
+ MANAGER.clearAllBreakpoints();
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ }
+}
diff --git a/test/996-breakpoint-obsolete/expected.txt b/test/996-breakpoint-obsolete/expected.txt
new file mode 100644
index 0000000000..e0d419e3f5
--- /dev/null
+++ b/test/996-breakpoint-obsolete/expected.txt
@@ -0,0 +1,14 @@
+Initially setting breakpoint to line 42
+Running transform without redefinition.
+Should be after first breakpoint.
+Breakpoint reached: public void art.Test996$Transform.run(java.lang.Runnable) @ line=42
+Running transform with redefinition.
+Redefining calling function!
+Setting breakpoint on now obsolete method to line 40
+Breakpoint reached: public void art.Test996$Transform.run(java.lang.Runnable) @ line=40
+Should be after first breakpoint.
+Running transform post redefinition. Should not hit any breakpoints.
+Doing nothing transformed
+Setting initial breakpoint on redefined method.
+Doing nothing transformed
+Breakpoint reached: public void art.Test996$Transform.run(java.lang.Runnable) @ line=8
diff --git a/test/996-breakpoint-obsolete/info.txt b/test/996-breakpoint-obsolete/info.txt
new file mode 100644
index 0000000000..58536acece
--- /dev/null
+++ b/test/996-breakpoint-obsolete/info.txt
@@ -0,0 +1,4 @@
+Test JVMTI breakpoint/obsolete method interaction.
+
+This checks that redefining a class will clear breakpoints on the class's
+methods and that it is possible to set breakpoints on obsolete methods.
diff --git a/test/996-breakpoint-obsolete/obsolete_breakpoints.cc b/test/996-breakpoint-obsolete/obsolete_breakpoints.cc
new file mode 100644
index 0000000000..b6a67e4a08
--- /dev/null
+++ b/test/996-breakpoint-obsolete/obsolete_breakpoints.cc
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2013 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 <inttypes.h>
+#include <memory>
+#include <stdio.h>
+
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+
+#include "jni.h"
+#include "jvmti.h"
+#include "scoped_local_ref.h"
+
+// Test infrastructure
+#include "jni_binder.h"
+#include "jni_helper.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
+#include "ti_macros.h"
+
+namespace art {
+namespace Test996ObsoleteBreakpoints {
+
+static constexpr jint kNumFrames = 10;
+
+static jmethodID GetFirstObsoleteMethod(JNIEnv* env, jvmtiEnv* jvmti_env) {
+ jint frame_count;
+ jvmtiFrameInfo frames[kNumFrames];
+ if (JvmtiErrorToException(env, jvmti_env,
+ jvmti_env->GetStackTrace(nullptr, // current thread
+ 0,
+ kNumFrames,
+ frames,
+ &frame_count))) {
+ return nullptr;
+ }
+ for (jint i = 0; i < frame_count; i++) {
+ jboolean is_obsolete = false;
+ if (JvmtiErrorToException(env, jvmti_env,
+ jvmti_env->IsMethodObsolete(frames[i].method, &is_obsolete))) {
+ return nullptr;
+ }
+ if (is_obsolete) {
+ return frames[i].method;
+ }
+ }
+ ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+ env->ThrowNew(rt_exception.get(), "Unable to find obsolete method!");
+ return nullptr;
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test996_setBreakpointOnObsoleteMethod(
+ JNIEnv* env, jclass k ATTRIBUTE_UNUSED, jlong loc) {
+ jmethodID method = GetFirstObsoleteMethod(env, jvmti_env);
+ if (method == nullptr) {
+ return;
+ }
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->SetBreakpoint(method, loc));
+}
+
+} // namespace Test996ObsoleteBreakpoints
+} // namespace art
diff --git a/test/996-breakpoint-obsolete/run b/test/996-breakpoint-obsolete/run
new file mode 100755
index 0000000000..51875a7e86
--- /dev/null
+++ b/test/996-breakpoint-obsolete/run
@@ -0,0 +1,18 @@
+#!/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.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/996-breakpoint-obsolete/src/Main.java b/test/996-breakpoint-obsolete/src/Main.java
new file mode 100644
index 0000000000..1b9b0a9b4b
--- /dev/null
+++ b/test/996-breakpoint-obsolete/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test996.run();
+ }
+}
diff --git a/test/996-breakpoint-obsolete/src/art/Breakpoint.java b/test/996-breakpoint-obsolete/src/art/Breakpoint.java
new file mode 100644
index 0000000000..bbb89f707f
--- /dev/null
+++ b/test/996-breakpoint-obsolete/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * 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.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/996-breakpoint-obsolete/src/art/Redefinition.java b/test/996-breakpoint-obsolete/src/art/Redefinition.java
new file mode 100644
index 0000000000..56d2938a01
--- /dev/null
+++ b/test/996-breakpoint-obsolete/src/art/Redefinition.java
@@ -0,0 +1,91 @@
+/*
+ * 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/996-breakpoint-obsolete/src/art/Test996.java b/test/996-breakpoint-obsolete/src/art/Test996.java
new file mode 100644
index 0000000000..f3166c33c7
--- /dev/null
+++ b/test/996-breakpoint-obsolete/src/art/Test996.java
@@ -0,0 +1,152 @@
+/*
+ * 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.lang.reflect.Executable;
+import java.lang.reflect.Method;
+import java.util.Base64;
+
+public class Test996 {
+ // The line we are going to break on. This should be the println in the Transform class. We set a
+ // breakpoint here after we have redefined the class.
+ public static final int TRANSFORM_BREAKPOINT_REDEFINED_LINE = 40;
+
+ // The line we initially set a breakpoint on. This should be the doNothing call. This should be
+ // cleared by the redefinition and should only be caught on the initial run.
+ public static final int TRANSFORM_BREAKPOINT_INITIAL_LINE = 42;
+
+ // A function that doesn't do anything. Used for giving places to break on in a function.
+ public static void doNothing() {}
+
+ public static final class Transform {
+ public void run(Runnable r) {
+ r.run();
+ // Make sure we don't change anything above this line to keep all the breakpoint stuff
+ // working. We will be putting a breakpoint before this line in the runnable.
+ System.out.println("Should be after first breakpoint.");
+ // This is set as a breakpoint prior to redefinition. It should not be hit.
+ doNothing();
+ }
+ }
+
+ /* ******************************************************************************************** */
+ // Try to keep all edits to this file below the above line. If edits need to be made above this
+ // line be sure to update the TRANSFORM_BREAKPOINT_REDEFINED_LINE and
+ // TRANSFORM_BREAKPOINT_INITIAL_LINE to their appropriate values.
+
+ public static final int TRANSFORM_BREAKPOINT_POST_REDEFINITION_LINE = 8;
+
+ // The base64 encoding of the following class. The redefined 'run' method should have the same
+ // instructions as the original. This means that the locations of each line should stay the same
+ // and the set of valid locations will not change. We use this to ensure that breakpoints are
+ // removed from the redefined method.
+ // public static final class Transform {
+ // public void run(Runnable r) {
+ // r.run();
+ // System.out.println("Doing nothing transformed");
+ // doNothing(); // try to catch non-removed breakpoints
+ // }
+ // }
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAKAoACAARCwASABMJABQAFQgAFgoAFwAYCgAZABoHABsHAB4BAAY8aW5pdD4BAAMo" +
+ "KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQADcnVuAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
+ "KVYBAApTb3VyY2VGaWxlAQAMVGVzdDk5Ni5qYXZhDAAJAAoHAB8MAA0ACgcAIAwAIQAiAQAZRG9p" +
+ "bmcgbm90aGluZyB0cmFuc2Zvcm1lZAcAIwwAJAAlBwAmDAAnAAoBABVhcnQvVGVzdDk5NiRUcmFu" +
+ "c2Zvcm0BAAlUcmFuc2Zvcm0BAAxJbm5lckNsYXNzZXMBABBqYXZhL2xhbmcvT2JqZWN0AQASamF2" +
+ "YS9sYW5nL1J1bm5hYmxlAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50" +
+ "U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3Ry" +
+ "aW5nOylWAQALYXJ0L1Rlc3Q5OTYBAAlkb05vdGhpbmcAMQAHAAgAAAAAAAIAAQAJAAoAAQALAAAA" +
+ "HQABAAEAAAAFKrcAAbEAAAABAAwAAAAGAAEAAAAEAAEADQAOAAEACwAAADYAAgACAAAAEiu5AAIB" +
+ "ALIAAxIEtgAFuAAGsQAAAAEADAAAABIABAAAAAYABgAHAA4ACAARAAkAAgAPAAAAAgAQAB0AAAAK" +
+ "AAEABwAZABwAGQ==");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQBzn3TiKGAiM0fubj25v816W0k+niqj+SQcBAAAcAAAAHhWNBIAAAAAAAAAAFgDAAAW" +
+ "AAAAcAAAAAoAAADIAAAAAwAAAPAAAAABAAAAFAEAAAYAAAAcAQAAAQAAAEwBAACwAgAAbAEAANoB" +
+ "AADiAQAA/QEAABYCAAAlAgAASQIAAGkCAACAAgAAlAIAAKoCAAC+AgAA0gIAAOACAADrAgAA7gIA" +
+ "APICAAD/AgAACgMAABADAAAVAwAAHgMAACMDAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAA" +
+ "CQAAAAoAAAANAAAADQAAAAkAAAAAAAAADgAAAAkAAADMAQAADgAAAAkAAADUAQAACAAEABIAAAAA" +
+ "AAAAAAAAAAAAAQAUAAAAAQAAABAAAAAEAAIAEwAAAAUAAAAAAAAABgAAABQAAAAAAAAAEQAAAAUA" +
+ "AAAAAAAACwAAALwBAABHAwAAAAAAAAIAAAA4AwAAPgMAAAEAAQABAAAAKgMAAAQAAABwEAQAAAAO" +
+ "AAQAAgACAAAALwMAAA4AAAByEAUAAwBiAAAAGgEBAG4gAwAQAHEAAgAAAA4AbAEAAAAAAAAAAAAA" +
+ "AAAAAAEAAAAGAAAAAQAAAAcABjxpbml0PgAZRG9pbmcgbm90aGluZyB0cmFuc2Zvcm1lZAAXTGFy" +
+ "dC9UZXN0OTk2JFRyYW5zZm9ybTsADUxhcnQvVGVzdDk5NjsAIkxkYWx2aWsvYW5ub3RhdGlvbi9F" +
+ "bmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwAVTGphdmEvaW8v" +
+ "UHJpbnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAFExqYXZhL2xhbmcvUnVubmFibGU7ABJM" +
+ "amF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVtOwAMVGVzdDk5Ni5qYXZhAAlUcmFu" +
+ "c2Zvcm0AAVYAAlZMAAthY2Nlc3NGbGFncwAJZG9Ob3RoaW5nAARuYW1lAANvdXQAB3ByaW50bG4A" +
+ "A3J1bgAFdmFsdWUABAAHDgAGAQAHDjx4PAACAgEVGAECAwIPBBkRFwwAAAEBAIGABPgCAQGQAwAA" +
+ "ABAAAAAAAAAAAQAAAAAAAAABAAAAFgAAAHAAAAACAAAACgAAAMgAAAADAAAAAwAAAPAAAAAEAAAA" +
+ "AQAAABQBAAAFAAAABgAAABwBAAAGAAAAAQAAAEwBAAADEAAAAQAAAGwBAAABIAAAAgAAAHgBAAAG" +
+ "IAAAAQAAALwBAAABEAAAAgAAAMwBAAACIAAAFgAAANoBAAADIAAAAgAAACoDAAAEIAAAAgAAADgD" +
+ "AAAAIAAAAQAAAEcDAAAAEAAAAQAAAFgDAAA=");
+
+ public static void notifyBreakpointReached(Thread thr, Executable e, long loc) {
+ int line = Breakpoint.locationToLine(e, loc);
+ if (line == -1 && e.getName().equals("run") && e.getDeclaringClass().equals(Transform.class)) {
+ // RI always reports line = -1 for obsolete methods. Just replace it with the real line for
+ // consistency.
+ line = TRANSFORM_BREAKPOINT_REDEFINED_LINE;
+ }
+ System.out.println("Breakpoint reached: " + e + " @ line=" + line);
+ }
+
+ public static void run() throws Exception {
+ // Set up breakpoints
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ Breakpoint.startBreakpointWatch(
+ Test996.class,
+ Test996.class.getDeclaredMethod(
+ "notifyBreakpointReached", Thread.class, Executable.class, Long.TYPE),
+ Thread.currentThread());
+
+ Transform t = new Transform();
+ Method non_obsolete_run_method = Transform.class.getDeclaredMethod("run", Runnable.class);
+ final long obsolete_breakpoint_location =
+ Breakpoint.lineToLocation(non_obsolete_run_method, TRANSFORM_BREAKPOINT_REDEFINED_LINE);
+
+ System.out.println("Initially setting breakpoint to line " + TRANSFORM_BREAKPOINT_INITIAL_LINE);
+ long initial_breakpoint_location =
+ Breakpoint.lineToLocation(non_obsolete_run_method, TRANSFORM_BREAKPOINT_INITIAL_LINE);
+ Breakpoint.setBreakpoint(non_obsolete_run_method, initial_breakpoint_location);
+
+ System.out.println("Running transform without redefinition.");
+ t.run(() -> {});
+
+ System.out.println("Running transform with redefinition.");
+ t.run(() -> {
+ System.out.println("Redefining calling function!");
+ // This should clear the breakpoint set to TRANSFORM_BREAKPOINT_INITIAL_LINE
+ Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ System.out.println("Setting breakpoint on now obsolete method to line " +
+ TRANSFORM_BREAKPOINT_REDEFINED_LINE);
+ setBreakpointOnObsoleteMethod(obsolete_breakpoint_location);
+ });
+ System.out.println("Running transform post redefinition. Should not hit any breakpoints.");
+ t.run(() -> {});
+
+ System.out.println("Setting initial breakpoint on redefined method.");
+ long final_breakpoint_location =
+ Breakpoint.lineToLocation(non_obsolete_run_method,
+ TRANSFORM_BREAKPOINT_POST_REDEFINITION_LINE);
+ Breakpoint.setBreakpoint(non_obsolete_run_method, final_breakpoint_location);
+ t.run(() -> {});
+
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ }
+
+ public static native void setBreakpointOnObsoleteMethod(long location);
+}
diff --git a/test/997-single-step/expected.txt b/test/997-single-step/expected.txt
new file mode 100644
index 0000000000..69c554ca7f
--- /dev/null
+++ b/test/997-single-step/expected.txt
@@ -0,0 +1,12 @@
+Stepping through doMultiPath(true)
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=41
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=42
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=43
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=47
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=48
+Stepping through doMultiPath(false)
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=41
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=42
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=45
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=47
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=48
diff --git a/test/997-single-step/info.txt b/test/997-single-step/info.txt
new file mode 100644
index 0000000000..e4a584e46f
--- /dev/null
+++ b/test/997-single-step/info.txt
@@ -0,0 +1,3 @@
+Test basic JVMTI single step functionality.
+
+Ensures that we can receive single step events from JVMTI.
diff --git a/test/997-single-step/run b/test/997-single-step/run
new file mode 100755
index 0000000000..51875a7e86
--- /dev/null
+++ b/test/997-single-step/run
@@ -0,0 +1,18 @@
+#!/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.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/997-single-step/src/Main.java b/test/997-single-step/src/Main.java
new file mode 100644
index 0000000000..1927f04d50
--- /dev/null
+++ b/test/997-single-step/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test997.run();
+ }
+}
diff --git a/test/997-single-step/src/art/Breakpoint.java b/test/997-single-step/src/art/Breakpoint.java
new file mode 100644
index 0000000000..bbb89f707f
--- /dev/null
+++ b/test/997-single-step/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * 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.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/997-single-step/src/art/Test997.java b/test/997-single-step/src/art/Test997.java
new file mode 100644
index 0000000000..a7a522dcca
--- /dev/null
+++ b/test/997-single-step/src/art/Test997.java
@@ -0,0 +1,82 @@
+/*
+ * 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.Arrays;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+
+public class Test997 {
+ static final int NO_LAST_LINE_NUMBER = -1;
+ static int LAST_LINE_NUMBER = NO_LAST_LINE_NUMBER;
+ static Method DO_MULTIPATH_METHOD;
+
+ static {
+ try {
+ DO_MULTIPATH_METHOD = Test997.class.getDeclaredMethod("doMultiPath", Boolean.TYPE);
+ } catch (Exception e) {
+ throw new Error("could not find method doMultiPath", e);
+ }
+ }
+
+ // Function that acts simply to ensure there are multiple lines.
+ public static void doNothing() {}
+
+ // Method with multiple paths we can break on.
+ public static void doMultiPath(boolean bit) {
+ doNothing();
+ if (bit) {
+ doNothing();
+ } else {
+ doNothing();
+ }
+ doNothing();
+ }
+
+ public static void notifySingleStep(Thread thr, Executable e, long loc) {
+ if (!e.equals(DO_MULTIPATH_METHOD)) {
+ // Only report steps in doMultiPath
+ return;
+ }
+ int cur_line = Breakpoint.locationToLine(e, loc);
+ // Only report anything when the line number changes. This is so we can run this test against
+ // both the RI and ART and also to prevent front-end compiler changes from affecting output.
+ if (LAST_LINE_NUMBER == NO_LAST_LINE_NUMBER || LAST_LINE_NUMBER != cur_line) {
+ LAST_LINE_NUMBER = cur_line;
+ System.out.println("Single step: " + e + " @ line=" + cur_line);
+ }
+ }
+
+ public static void resetTest() {
+ LAST_LINE_NUMBER = NO_LAST_LINE_NUMBER;
+ }
+
+ public static void run() throws Exception {
+ boolean[] values = new boolean[] { true, false };
+ Trace.enableSingleStepTracing(Test997.class,
+ Test997.class.getDeclaredMethod(
+ "notifySingleStep", Thread.class, Executable.class, Long.TYPE),
+ Thread.currentThread());
+ for (boolean arg : values) {
+ System.out.println("Stepping through doMultiPath(" + arg + ")");
+ resetTest();
+ doMultiPath(arg);
+ }
+
+ Trace.disableTracing(Thread.currentThread());
+ }
+}
diff --git a/test/997-single-step/src/art/Trace.java b/test/997-single-step/src/art/Trace.java
new file mode 100644
index 0000000000..ba3d397b0b
--- /dev/null
+++ b/test/997-single-step/src/art/Trace.java
@@ -0,0 +1,56 @@
+/*
+ * 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.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public class Trace {
+ public static native void enableTracing(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Method fieldAccess,
+ Method fieldModify,
+ Method singleStep,
+ Thread thr);
+ public static native void disableTracing(Thread thr);
+
+ public static void enableFieldTracing(Class<?> methodClass,
+ Method fieldAccess,
+ Method fieldModify,
+ Thread thr) {
+ enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr);
+ }
+
+ public static void enableMethodTracing(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Thread thr) {
+ enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr);
+ }
+
+ public static void enableSingleStepTracing(Class<?> methodClass,
+ Method singleStep,
+ Thread thr) {
+ enableTracing(methodClass, null, null, null, null, singleStep, thr);
+ }
+
+ public static native void watchFieldAccess(Field f);
+ public static native void watchFieldModification(Field f);
+ public static native void watchAllFieldAccesses();
+ public static native void watchAllFieldModifications();
+}
diff --git a/test/Android.bp b/test/Android.bp
index 9e6ecffe79..0dff01b6cf 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -281,6 +281,8 @@ art_cc_defaults {
"989-method-trace-throw/method_trace.cc",
"991-field-trace-2/field_trace.cc",
"992-source-data/source_file.cc",
+ "993-breakpoints/breakpoints.cc",
+ "996-breakpoint-obsolete/obsolete_breakpoints.cc",
],
shared_libs: [
"libbase",
@@ -396,6 +398,7 @@ cc_defaults {
"626-const-class-linking/clear_dex_cache_types.cc",
"642-fp-callees/fp_callees.cc",
"647-jni-get-field-id/get_field_id.cc",
+ "656-annotation-lookup-generic-jni/test.cc",
"708-jit-cache-churn/jit.cc"
],
shared_libs: [
diff --git a/test/Android.run-test-jvmti-java-library.mk b/test/Android.run-test-jvmti-java-library.mk
index 60ce6c7003..753fe9a330 100644
--- a/test/Android.run-test-jvmti-java-library.mk
+++ b/test/Android.run-test-jvmti-java-library.mk
@@ -22,6 +22,7 @@ include $(CLEAR_VARS)
LOCAL_SHIM_CLASSES := \
902-hello-transformation/src/art/Redefinition.java \
903-hello-tagging/src/art/Main.java \
+ 989-method-trace-throw/src/art/Trace.java \
LOCAL_SRC_FILES := $(LOCAL_SHIM_CLASSES)
@@ -77,6 +78,12 @@ LOCAL_SRC_FILES += \
984-obsolete-invoke/src/art/Test984.java \
985-re-obsolete/src/art/Test985.java \
986-native-method-bind/src/art/Test986.java \
+ 988-method-trace/src/art/Test988.java \
+ 989-method-trace-throw/src/art/Test989.java \
+ 990-field-trace/src/art/Test990.java \
+ 991-field-trace-2/src/art/Test991.java \
+ 992-source-data/src/art/Test992.java \
+ 992-source-data/src/art/Target2.java \
JVMTI_RUN_TEST_GENERATED_NUMBERS := \
901 \
@@ -119,6 +126,11 @@ JVMTI_RUN_TEST_GENERATED_NUMBERS := \
984 \
985 \
986 \
+ 988 \
+ 989 \
+ 990 \
+ 991 \
+ 992 \
# Try to enforce that the directories correspond to the Java files we pull in.
JVMTI_RUN_TEST_DIR_CHECK := $(sort $(foreach DIR,$(JVMTI_RUN_TEST_GENERATED_NUMBERS), \
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index d24edccd8b..ede485a81b 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -63,6 +63,7 @@ TEST_VDEX="n"
TEST_IS_NDEBUG="n"
APP_IMAGE="y"
JVMTI_STRESS="n"
+JVMTI_STEP_STRESS="n"
JVMTI_FIELD_STRESS="n"
JVMTI_TRACE_STRESS="n"
JVMTI_REDEFINE_STRESS="n"
@@ -163,6 +164,10 @@ while true; do
JVMTI_STRESS="y"
JVMTI_REDEFINE_STRESS="y"
shift
+ elif [ "x$1" = "x--jvmti-step-stress" ]; then
+ JVMTI_STRESS="y"
+ JVMTI_STEP_STRESS="y"
+ shift
elif [ "x$1" = "x--jvmti-field-stress" ]; then
JVMTI_STRESS="y"
JVMTI_FIELD_STRESS="y"
@@ -426,6 +431,9 @@ if [[ "$JVMTI_STRESS" = "y" ]]; then
if [[ "$JVMTI_FIELD_STRESS" = "y" ]]; then
agent_args="${agent_args},field"
fi
+ if [[ "$JVMTI_STEP_STRESS" = "y" ]]; then
+ agent_args="${agent_args},step"
+ fi
if [[ "$JVMTI_TRACE_STRESS" = "y" ]]; then
agent_args="${agent_args},trace"
fi
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 7bfa417691..d3d92c651e 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -502,7 +502,7 @@
"645-checker-abs-simd",
"706-checker-scheduler"],
"description": ["Checker tests are not compatible with jvmti."],
- "variant": "jvmti-stress | redefine-stress | trace-stress | field-stress"
+ "variant": "jvmti-stress | redefine-stress | trace-stress | field-stress | step-stress"
},
{
"tests": [
@@ -510,7 +510,7 @@
"964-default-iface-init-gen"
],
"description": ["Tests that just take too long with jvmti-stress"],
- "variant": "jvmti-stress | redefine-stress | trace-stress"
+ "variant": "jvmti-stress | redefine-stress | trace-stress | step-stress"
},
{
"tests": [
@@ -541,7 +541,7 @@
"981-dedup-original-dex"
],
"description": ["Tests that require exact knowledge of the number of plugins and agents."],
- "variant": "jvmti-stress | redefine-stress | trace-stress | field-stress"
+ "variant": "jvmti-stress | redefine-stress | trace-stress | field-stress | step-stress"
},
{
"tests": [
@@ -579,7 +579,7 @@
"004-ThreadStress"
],
"description": "The thread stress test just takes too long with field-stress",
- "variant": "jvmti-stress | field-stress"
+ "variant": "jvmti-stress | field-stress | step-stress"
},
{
"tests": [
@@ -594,11 +594,11 @@
},
{
"tests": [
- "953-invoke-polymorphic-compiler"
+ "567-checker-compare"
],
- "description": "Test throws VerifyError when run with --build-with-javac-dx.",
+ "description": "Checker tests failing when run with --build-with-javac-dx.",
"env_vars": {"ANDROID_COMPILE_WITH_JACK": "false"},
- "bug": "b/62722425"
+ "bug": "b/62950048"
},
{
"tests": [
@@ -665,5 +665,12 @@
"description": [ "Flake on gcstress" ],
"bug": "b/62562923",
"variant": "gcstress & jit & target"
+ },
+ {
+ "tests": ["004-JniTest"],
+ "description": [ "Tests failing with --build-with-javac-dx since the new annotation",
+ "lookup changes" ],
+ "bug": "b/63089991",
+ "env_vars": {"ANDROID_COMPILE_WITH_JACK": "false"}
}
]
diff --git a/test/run-test b/test/run-test
index e6c2480ed0..486b465a89 100755
--- a/test/run-test
+++ b/test/run-test
@@ -145,6 +145,7 @@ gc_verify="false"
gc_stress="false"
jvmti_trace_stress="false"
jvmti_field_stress="false"
+jvmti_step_stress="false"
jvmti_redefine_stress="false"
strace="false"
always_clean="no"
@@ -242,6 +243,9 @@ while true; do
basic_verify="true"
gc_stress="true"
shift
+ elif [ "x$1" = "x--jvmti-step-stress" ]; then
+ jvmti_step_stress="true"
+ shift
elif [ "x$1" = "x--jvmti-redefine-stress" ]; then
jvmti_redefine_stress="true"
shift
@@ -464,6 +468,9 @@ fi
if [ "$jvmti_redefine_stress" = "true" ]; then
run_args="${run_args} --no-app-image --jvmti-redefine-stress"
fi
+if [ "$jvmti_step_stress" = "true" ]; then
+ run_args="${run_args} --no-app-image --jvmti-step-stress"
+fi
if [ "$jvmti_field_stress" = "true" ]; then
run_args="${run_args} --no-app-image --jvmti-field-stress"
fi
@@ -679,6 +686,7 @@ if [ "$usage" = "yes" ]; then
echo " --gcstress Run with gc stress testing"
echo " --gcverify Run with gc verification"
echo " --jvmti-trace-stress Run with jvmti method tracing stress testing"
+ echo " --jvmti-step-stress Run with jvmti single step stress testing"
echo " --jvmti-redefine-stress"
echo " Run with jvmti method redefinition stress testing"
echo " --always-clean Delete the test files even if the test fails."
@@ -749,7 +757,7 @@ export TEST_NAME=`basename ${test_dir}`
# Tests named '<number>-checker-*' will also have their CFGs verified with
# Checker when compiled with Optimizing on host.
if [[ "$TEST_NAME" =~ ^[0-9]+-checker- ]]; then
- if [ "$runtime" = "art" -a "$image_suffix" = "" -a "$USE_JACK" = "true" ]; then
+ if [ "$runtime" = "art" -a "$image_suffix" = "" ]; then
# In no-prebuild or no-image mode, the compiler only quickens so disable the checker.
if [ "$prebuild_mode" = "yes" -a "$have_image" = "yes" ]; then
run_checker="yes"
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index b6a5963cf4..68e1856adb 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -148,7 +148,7 @@ def gather_test_info():
VARIANT_TYPE_DICT['jni'] = {'jni', 'forcecopy', 'checkjni'}
VARIANT_TYPE_DICT['address_sizes'] = {'64', '32'}
VARIANT_TYPE_DICT['jvmti'] = {'no-jvmti', 'jvmti-stress', 'redefine-stress', 'trace-stress',
- 'field-stress'}
+ 'field-stress', 'step-stress'}
VARIANT_TYPE_DICT['compiler'] = {'interp-ac', 'interpreter', 'jit', 'optimizing',
'regalloc_gc', 'speed-profile'}
@@ -445,6 +445,8 @@ def run_tests(tests):
options_test += ' --jvmti-trace-stress'
elif jvmti == 'redefine-stress':
options_test += ' --jvmti-redefine-stress'
+ elif jvmti == 'step-stress':
+ options_test += ' --jvmti-step-stress'
if address_size == '64':
options_test += ' --64'
@@ -965,6 +967,8 @@ def parse_option():
JVMTI_TYPES.add('redefine-stress')
if options['field_stress']:
JVMTI_TYPES.add('field-stress')
+ if options['step_stress']:
+ JVMTI_TYPES.add('step-stress')
if options['trace_stress']:
JVMTI_TYPES.add('trace-stress')
if options['no_jvmti']:
diff --git a/test/ti-agent/common_helper.cc b/test/ti-agent/common_helper.cc
index 4fe58db169..0eb71f8371 100644
--- a/test/ti-agent/common_helper.cc
+++ b/test/ti-agent/common_helper.cc
@@ -38,6 +38,9 @@ static void SetupCommonRetransform();
static void SetupCommonRedefine();
static void SetupCommonTransform();
+// Taken from art/runtime/modifiers.h
+static constexpr uint32_t kAccStatic = 0x0008; // field, method, ic
+
template <bool is_redefine>
static void throwCommonRedefinitionError(jvmtiEnv* jvmti,
JNIEnv* env,
@@ -69,22 +72,6 @@ static void throwCommonRedefinitionError(jvmtiEnv* jvmti,
env->ThrowNew(env->FindClass("java/lang/Exception"), message.c_str());
}
-namespace common_trace {
-
-// Taken from art/runtime/modifiers.h
-static constexpr uint32_t kAccStatic = 0x0008; // field, method, ic
-
-struct TraceData {
- jclass test_klass;
- jmethodID enter_method;
- jmethodID exit_method;
- jmethodID field_access;
- jmethodID field_modify;
- bool in_callback;
- bool access_watch_on_load;
- bool modify_watch_on_load;
-};
-
static jobject GetJavaField(jvmtiEnv* jvmti, JNIEnv* env, jclass field_klass, jfieldID f) {
jint mods = 0;
if (JvmtiErrorToException(env, jvmti, jvmti->GetFieldModifiers(field_klass, f, &mods))) {
@@ -175,6 +162,221 @@ static jobject GetJavaValue(jvmtiEnv* jvmtienv,
return GetJavaValueByType(env, type[0], value);
}
+namespace common_breakpoint {
+
+struct BreakpointData {
+ jclass test_klass;
+ jmethodID breakpoint_method;
+ bool in_callback;
+ bool allow_recursive;
+};
+
+extern "C" void breakpointCB(jvmtiEnv* jvmti,
+ JNIEnv* jnienv,
+ jthread thread,
+ jmethodID method,
+ jlocation location) {
+ BreakpointData* data = nullptr;
+ if (JvmtiErrorToException(jnienv, jvmti,
+ jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ if (data->in_callback && !data->allow_recursive) {
+ return;
+ }
+ data->in_callback = true;
+ jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
+ jnienv->CallStaticVoidMethod(data->test_klass,
+ data->breakpoint_method,
+ thread,
+ method_arg,
+ static_cast<jlong>(location));
+ jnienv->DeleteLocalRef(method_arg);
+ data->in_callback = false;
+}
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Breakpoint_getLineNumberTableNative(
+ JNIEnv* env,
+ jclass k ATTRIBUTE_UNUSED,
+ jobject target) {
+ jmethodID method = env->FromReflectedMethod(target);
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+ jint nlines;
+ jvmtiLineNumberEntry* lines = nullptr;
+ if (JvmtiErrorToException(env, jvmti_env,
+ jvmti_env->GetLineNumberTable(method, &nlines, &lines))) {
+ return nullptr;
+ }
+ jintArray lines_array = env->NewIntArray(nlines);
+ if (env->ExceptionCheck()) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(lines));
+ return nullptr;
+ }
+ jlongArray locs_array = env->NewLongArray(nlines);
+ if (env->ExceptionCheck()) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(lines));
+ return nullptr;
+ }
+ ScopedLocalRef<jclass> object_class(env, env->FindClass("java/lang/Object"));
+ if (env->ExceptionCheck()) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(lines));
+ return nullptr;
+ }
+ jobjectArray ret = env->NewObjectArray(2, object_class.get(), nullptr);
+ if (env->ExceptionCheck()) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(lines));
+ return nullptr;
+ }
+ jint* temp_lines = env->GetIntArrayElements(lines_array, /*isCopy*/nullptr);
+ jlong* temp_locs = env->GetLongArrayElements(locs_array, /*isCopy*/nullptr);
+ for (jint i = 0; i < nlines; i++) {
+ temp_lines[i] = lines[i].line_number;
+ temp_locs[i] = lines[i].start_location;
+ }
+ env->ReleaseIntArrayElements(lines_array, temp_lines, 0);
+ env->ReleaseLongArrayElements(locs_array, temp_locs, 0);
+ env->SetObjectArrayElement(ret, 0, locs_array);
+ env->SetObjectArrayElement(ret, 1, lines_array);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(lines));
+ return ret;
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_art_Breakpoint_getStartLocation(JNIEnv* env,
+ jclass k ATTRIBUTE_UNUSED,
+ jobject target) {
+ jmethodID method = env->FromReflectedMethod(target);
+ if (env->ExceptionCheck()) {
+ return 0;
+ }
+ jlong start = 0;
+ jlong end = end;
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->GetMethodLocation(method, &start, &end));
+ return start;
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Breakpoint_clearBreakpoint(JNIEnv* env,
+ jclass k ATTRIBUTE_UNUSED,
+ jobject target,
+ jlocation location) {
+ jmethodID method = env->FromReflectedMethod(target);
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->ClearBreakpoint(method, location));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Breakpoint_setBreakpoint(JNIEnv* env,
+ jclass k ATTRIBUTE_UNUSED,
+ jobject target,
+ jlocation location) {
+ jmethodID method = env->FromReflectedMethod(target);
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->SetBreakpoint(method, location));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Breakpoint_startBreakpointWatch(
+ JNIEnv* env,
+ jclass k ATTRIBUTE_UNUSED,
+ jclass method_klass,
+ jobject method,
+ jboolean allow_recursive,
+ jthread thr) {
+ BreakpointData* data = nullptr;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->Allocate(sizeof(BreakpointData),
+ reinterpret_cast<unsigned char**>(&data)))) {
+ return;
+ }
+ memset(data, 0, sizeof(BreakpointData));
+ data->test_klass = reinterpret_cast<jclass>(env->NewGlobalRef(method_klass));
+ data->breakpoint_method = env->FromReflectedMethod(method);
+ data->in_callback = false;
+ data->allow_recursive = allow_recursive;
+
+ void* old_data = nullptr;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(&old_data))) {
+ return;
+ } else if (old_data != nullptr) {
+ ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+ env->ThrowNew(rt_exception.get(), "Environment already has local storage set!");
+ return;
+ }
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data))) {
+ return;
+ }
+ jvmtiEventCallbacks cb;
+ memset(&cb, 0, sizeof(cb));
+ cb.Breakpoint = breakpointCB;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) {
+ return;
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_BREAKPOINT,
+ thr))) {
+ return;
+ }
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Breakpoint_stopBreakpointWatch(
+ JNIEnv* env,
+ jclass k ATTRIBUTE_UNUSED,
+ jthread thr) {
+ if (JvmtiErrorToException(env, jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_BREAKPOINT,
+ thr))) {
+ return;
+ }
+}
+
+} // namespace common_breakpoint
+
+namespace common_trace {
+
+struct TraceData {
+ jclass test_klass;
+ jmethodID enter_method;
+ jmethodID exit_method;
+ jmethodID field_access;
+ jmethodID field_modify;
+ jmethodID single_step;
+ bool in_callback;
+ bool access_watch_on_load;
+ bool modify_watch_on_load;
+};
+
+static void singleStepCB(jvmtiEnv* jvmti,
+ JNIEnv* jnienv,
+ jthread thread,
+ jmethodID method,
+ jlocation location) {
+ TraceData* data = nullptr;
+ if (JvmtiErrorToException(jnienv, jvmti,
+ jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ if (data->in_callback) {
+ return;
+ }
+ CHECK(data->single_step != nullptr);
+ data->in_callback = true;
+ jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
+ jnienv->CallStaticVoidMethod(data->test_klass,
+ data->single_step,
+ thread,
+ method_arg,
+ static_cast<jlong>(location));
+ jnienv->DeleteLocalRef(method_arg);
+ data->in_callback = false;
+}
+
static void fieldAccessCB(jvmtiEnv* jvmti,
JNIEnv* jnienv,
jthread thr ATTRIBUTE_UNUSED,
@@ -481,6 +683,7 @@ extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing(
jobject exit,
jobject field_access,
jobject field_modify,
+ jobject single_step,
jthread thr) {
TraceData* data = nullptr;
if (JvmtiErrorToException(env,
@@ -495,8 +698,17 @@ extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing(
data->exit_method = exit != nullptr ? env->FromReflectedMethod(exit) : nullptr;
data->field_access = field_access != nullptr ? env->FromReflectedMethod(field_access) : nullptr;
data->field_modify = field_modify != nullptr ? env->FromReflectedMethod(field_modify) : nullptr;
+ data->single_step = single_step != nullptr ? env->FromReflectedMethod(single_step) : nullptr;
data->in_callback = false;
+ void* old_data = nullptr;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(&old_data))) {
+ return;
+ } else if (old_data != nullptr) {
+ ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+ env->ThrowNew(rt_exception.get(), "Environment already has local storage set!");
+ return;
+ }
if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data))) {
return;
}
@@ -508,6 +720,7 @@ extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing(
cb.FieldAccess = fieldAccessCB;
cb.FieldModification = fieldModificationCB;
cb.ClassPrepare = classPrepareCB;
+ cb.SingleStep = singleStepCB;
if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) {
return;
}
@@ -543,6 +756,14 @@ extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing(
thr))) {
return;
}
+ if (single_step != nullptr &&
+ JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_SINGLE_STEP,
+ thr))) {
+ return;
+ }
}
extern "C" JNIEXPORT void JNICALL Java_art_Trace_disableTracing(
@@ -571,6 +792,12 @@ extern "C" JNIEXPORT void JNICALL Java_art_Trace_disableTracing(
thr))) {
return;
}
+ if (JvmtiErrorToException(env, jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_SINGLE_STEP,
+ thr))) {
+ return;
+ }
}
} // namespace common_trace
diff --git a/test/ti-stress/stress.cc b/test/ti-stress/stress.cc
index 40fcc4f11d..d197acd216 100644
--- a/test/ti-stress/stress.cc
+++ b/test/ti-stress/stress.cc
@@ -17,6 +17,7 @@
#include <jni.h>
#include <stdio.h>
#include <iostream>
+#include <iomanip>
#include <fstream>
#include <memory>
#include <stdio.h>
@@ -40,6 +41,7 @@ struct StressData {
bool trace_stress;
bool redefine_stress;
bool field_stress;
+ bool step_stress;
};
static void WriteToFile(const std::string& fname, jint data_len, const unsigned char* data) {
@@ -586,6 +588,21 @@ void JNICALL ClassPrepareHook(jvmtiEnv* jvmtienv,
}
}
+void JNICALL SingleStepHook(jvmtiEnv* jvmtienv,
+ JNIEnv* env,
+ jthread thread,
+ jmethodID method,
+ jlocation location) {
+ ScopedThreadInfo info(jvmtienv, env, thread);
+ ScopedMethodInfo method_info(jvmtienv, env, method);
+ if (!method_info.Init()) {
+ LOG(ERROR) << "Unable to get method info!";
+ return;
+ }
+ LOG(INFO) << "Single step at location: 0x" << std::setw(8) << std::setfill('0') << std::hex
+ << location << " in method " << method_info << " thread: " << info.GetName();
+}
+
// The hook we are using.
void JNICALL ClassFileLoadHookSecretNoOp(jvmtiEnv* jvmti,
JNIEnv* jni_env ATTRIBUTE_UNUSED,
@@ -645,6 +662,8 @@ static void ReadOptions(StressData* data, char* options) {
std::string cur = GetOption(ops);
if (cur == "trace") {
data->trace_stress = true;
+ } else if (cur == "step") {
+ data->step_stress = true;
} else if (cur == "field") {
data->field_stress = true;
} else if (cur == "redefine") {
@@ -776,6 +795,7 @@ extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm,
cb.FieldAccess = FieldAccessHook;
cb.FieldModification = FieldModificationHook;
cb.ClassPrepare = ClassPrepareHook;
+ cb.SingleStep = SingleStepHook;
if (jvmti->SetEventCallbacks(&cb, sizeof(cb)) != JVMTI_ERROR_NONE) {
LOG(ERROR) << "Unable to set class file load hook cb!";
return 1;
@@ -837,6 +857,13 @@ extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm,
return 1;
}
}
+ if (data->step_stress) {
+ if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_SINGLE_STEP,
+ nullptr) != JVMTI_ERROR_NONE) {
+ return 1;
+ }
+ }
return 0;
}
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index bf7692ab15..75694c340c 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -68,7 +68,7 @@ if $using_jack; then
fi
if [[ $mode == "host" ]]; then
- make_command="make $j_arg $showcommands build-art-host-tests $common_targets"
+ make_command="make $j_arg $showcommands build-art-host-tests $common_targets dx-tests"
make_command+=" ${out_dir}/host/linux-x86/lib/libjavacoretests.so "
make_command+=" ${out_dir}/host/linux-x86/lib64/libjavacoretests.so"
elif [[ $mode == "target" ]]; then
diff --git a/tools/dexfuzz/README b/tools/dexfuzz/README
index 78f73f5c11..3c0c65e7e2 100644
--- a/tools/dexfuzz/README
+++ b/tools/dexfuzz/README
@@ -139,7 +139,9 @@ InstructionDuplicator 80
InstructionSwapper 80
NewMethodCaller 10
NonsenseStringPrinter 10
+OppositeBranchChanger 40
PoolIndexChanger 30
+RandomBranchChanger 30
RandomInstructionGenerator 30
SwitchBranchShifter 30
TryBlockShifter 40
diff --git a/tools/dexfuzz/src/dexfuzz/DexFuzz.java b/tools/dexfuzz/src/dexfuzz/DexFuzz.java
index 3b2875482e..41ce7b2706 100644
--- a/tools/dexfuzz/src/dexfuzz/DexFuzz.java
+++ b/tools/dexfuzz/src/dexfuzz/DexFuzz.java
@@ -34,7 +34,7 @@ import dexfuzz.listeners.UpdatingConsoleListener;
*/
public class DexFuzz {
private static int majorVersion = 1;
- private static int minorVersion = 1;
+ private static int minorVersion = 2;
private static int seedChangeVersion = 0;
/**
diff --git a/tools/dexfuzz/src/dexfuzz/program/Program.java b/tools/dexfuzz/src/dexfuzz/program/Program.java
index 286fe5221e..e550d30308 100644
--- a/tools/dexfuzz/src/dexfuzz/program/Program.java
+++ b/tools/dexfuzz/src/dexfuzz/program/Program.java
@@ -32,7 +32,9 @@ import dexfuzz.program.mutators.InstructionDuplicator;
import dexfuzz.program.mutators.InstructionSwapper;
import dexfuzz.program.mutators.NewMethodCaller;
import dexfuzz.program.mutators.NonsenseStringPrinter;
+import dexfuzz.program.mutators.OppositeBranchChanger;
import dexfuzz.program.mutators.PoolIndexChanger;
+import dexfuzz.program.mutators.RandomBranchChanger;
import dexfuzz.program.mutators.RandomInstructionGenerator;
import dexfuzz.program.mutators.SwitchBranchShifter;
import dexfuzz.program.mutators.TryBlockShifter;
@@ -199,7 +201,9 @@ public class Program {
registerMutator(new InstructionSwapper(rng, mutationStats, mutations));
registerMutator(new NewMethodCaller(rng, mutationStats, mutations));
registerMutator(new NonsenseStringPrinter(rng, mutationStats, mutations));
+ registerMutator(new OppositeBranchChanger(rng, mutationStats, mutations));
registerMutator(new PoolIndexChanger(rng, mutationStats, mutations));
+ registerMutator(new RandomBranchChanger(rng, mutationStats, mutations));
registerMutator(new RandomInstructionGenerator(rng, mutationStats, mutations));
registerMutator(new SwitchBranchShifter(rng, mutationStats, mutations));
registerMutator(new TryBlockShifter(rng, mutationStats, mutations));
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/IfBranchChanger.java b/tools/dexfuzz/src/dexfuzz/program/mutators/IfBranchChanger.java
new file mode 100644
index 0000000000..872b29730c
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/IfBranchChanger.java
@@ -0,0 +1,158 @@
+/*
+ * 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 dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * This class mutates the comparison operator of the if
+ * statements by taking in a random instruction, checking whether
+ * it is an if statement and, if so, changing the comparison
+ * operator. The inheriting classes implement the way comparison
+ * operator changes. For example, by choosing the opposite
+ * comparison operator or by choosing a random comparison operator.
+ */
+public abstract class IfBranchChanger extends CodeMutator {
+ /**
+ * Every CodeMutator has an AssociatedMutation, representing the
+ * mutation that this CodeMutator can perform, to allow separate
+ * generateMutation() and applyMutation() phases, allowing serialization.
+ */
+ public static class AssociatedMutation extends Mutation {
+ public int ifBranchInsnIdx;
+
+ @Override
+ public String getString() {
+ return Integer.toString(ifBranchInsnIdx);
+ }
+
+ @Override
+ public void parseString(String[] elements) {
+ ifBranchInsnIdx = Integer.parseInt(elements[2]);
+ }
+ }
+
+ // The following two methods are here for the benefit of MutationSerializer,
+ // so it can create a CodeMutator and get the correct associated Mutation, as it
+ // reads in mutations from a dump of mutations.
+ @Override
+ public Mutation getNewMutation() {
+ return new AssociatedMutation();
+ }
+
+ public IfBranchChanger() { }
+
+ public IfBranchChanger(Random rng, MutationStats stats, List<Mutation> mutations) {
+ super(rng, stats, mutations);
+ }
+
+ // A cache that should only exist between generateMutation() and applyMutation(),
+ // or be created at the start of applyMutation(), if we're reading in mutations from
+ // a file.
+ private List<MInsn> ifBranchInsns = null;
+
+ private void generateCachedifBranchInsns(MutatableCode mutatableCode) {
+ if (ifBranchInsns != null) {
+ return;
+ }
+
+ ifBranchInsns = new ArrayList<MInsn>();
+
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (isIfBranchOperation(mInsn)) {
+ ifBranchInsns.add(mInsn);
+ }
+ }
+ }
+
+ @Override
+ protected boolean canMutate(MutatableCode mutatableCode) {
+ for (MInsn mInsn : mutatableCode.getInstructions()) {
+ if (isIfBranchOperation(mInsn)) {
+ return true;
+ }
+ }
+
+ Log.debug("No if branch operation, skipping...");
+ return false;
+ }
+
+ @Override
+ protected Mutation generateMutation(MutatableCode mutatableCode) {
+ generateCachedifBranchInsns(mutatableCode);
+
+ int ifBranchInsnIdx = rng.nextInt(ifBranchInsns.size());
+
+ AssociatedMutation mutation = new AssociatedMutation();
+ mutation.setup(this.getClass(), mutatableCode);
+ mutation.ifBranchInsnIdx = ifBranchInsnIdx;
+ return mutation;
+ }
+
+ @Override
+ protected void applyMutation(Mutation uncastMutation) {
+ AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+ MutatableCode mutatableCode = mutation.mutatableCode;
+
+ generateCachedifBranchInsns(mutatableCode);
+
+ MInsn ifBranchInsn = ifBranchInsns.get(mutation.ifBranchInsnIdx);
+
+ String oldInsnString = ifBranchInsn.toString();
+
+ Opcode newOpcode = getModifiedOpcode(ifBranchInsn);
+
+ ifBranchInsn.insn.info = Instruction.getOpcodeInfo(newOpcode);
+
+ Log.info("Changed " + oldInsnString + " to " + ifBranchInsn);
+
+ stats.incrementStat("Changed if branch operator to " + getMutationTag() + " operator");
+
+ // Clear cache.
+ ifBranchInsns = null;
+ }
+
+ /**
+ * Get a different if branch instruction.
+ * @return opcode of the new comparison operator.
+ */
+ protected abstract Opcode getModifiedOpcode(MInsn mInsn);
+
+ /**
+ * Get the tag of the mutation that fired.
+ * @return string tag of the type of mutation used
+ */
+ protected abstract String getMutationTag();
+
+ private boolean isIfBranchOperation(MInsn mInsn) {
+ Opcode opcode = mInsn.insn.info.opcode;
+ if (Opcode.isBetween(opcode, Opcode.IF_EQ, Opcode.IF_LEZ)) {
+ return true;
+ }
+ return false;
+ }
+} \ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/OppositeBranchChanger.java b/tools/dexfuzz/src/dexfuzz/program/mutators/OppositeBranchChanger.java
new file mode 100644
index 0000000000..cb25b641cc
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/OppositeBranchChanger.java
@@ -0,0 +1,72 @@
+/*
+ * 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 dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Opcode;
+import java.util.List;
+import java.util.Random;
+
+public class OppositeBranchChanger extends IfBranchChanger {
+
+ public OppositeBranchChanger(Random rng, MutationStats stats, List<Mutation> mutations) {
+ super(rng, stats, mutations);
+ likelihood = 40;
+ }
+
+ @Override
+ protected Opcode getModifiedOpcode(MInsn mInsn) {
+ Opcode opcode = mInsn.insn.info.opcode;
+ switch (opcode) {
+ case IF_EQ:
+ return Opcode.IF_NE;
+ case IF_NE:
+ return Opcode.IF_EQ;
+ case IF_LT:
+ return Opcode.IF_GE;
+ case IF_GT:
+ return Opcode.IF_LE;
+ case IF_GE:
+ return Opcode.IF_LT;
+ case IF_LE:
+ return Opcode.IF_GT;
+ case IF_EQZ:
+ return Opcode.IF_NEZ;
+ case IF_NEZ:
+ return Opcode.IF_EQZ;
+ case IF_LTZ:
+ return Opcode.IF_GEZ;
+ case IF_GTZ:
+ return Opcode.IF_LEZ;
+ case IF_GEZ:
+ return Opcode.IF_LTZ;
+ case IF_LEZ:
+ return Opcode.IF_GTZ;
+ default:
+ Log.errorAndQuit("Could not find if branch.");
+ return opcode;
+ }
+ }
+
+ @Override
+ protected String getMutationTag() {
+ return "opposite";
+ }
+} \ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/RandomBranchChanger.java b/tools/dexfuzz/src/dexfuzz/program/mutators/RandomBranchChanger.java
new file mode 100644
index 0000000000..fc42c2ed6f
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/RandomBranchChanger.java
@@ -0,0 +1,70 @@
+/*
+ * 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 dexfuzz.program.mutators;
+
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Opcode;
+import java.util.List;
+import java.util.Random;
+
+public class RandomBranchChanger extends IfBranchChanger {
+
+ private static final Opcode[] EQUALITY_CMP_OP_LIST = {
+ Opcode.IF_EQ,
+ Opcode.IF_NE,
+ Opcode.IF_LT,
+ Opcode.IF_GE,
+ Opcode.IF_GT,
+ Opcode.IF_LE
+ };
+
+ private static final Opcode[] ZERO_CMP_OP_LIST = {
+ Opcode.IF_EQZ,
+ Opcode.IF_NEZ,
+ Opcode.IF_LTZ,
+ Opcode.IF_GEZ,
+ Opcode.IF_GTZ,
+ Opcode.IF_LEZ
+ };
+
+ public RandomBranchChanger(Random rng, MutationStats stats, List<Mutation> mutations) {
+ super(rng, stats, mutations);
+ likelihood = 30;
+ }
+
+ @Override
+ protected Opcode getModifiedOpcode(MInsn mInsn) {
+ Opcode opcode = mInsn.insn.info.opcode;
+ if (Opcode.isBetween(opcode, Opcode.IF_EQ, Opcode.IF_LE)) {
+ int index = opcode.ordinal() - Opcode.IF_EQ.ordinal();
+ int length = EQUALITY_CMP_OP_LIST.length;
+ return EQUALITY_CMP_OP_LIST[(index + 1 + rng.nextInt(length - 1)) % length];
+ } else if (Opcode.isBetween(opcode, Opcode.IF_EQZ, Opcode.IF_LEZ)) {
+ int index = opcode.ordinal() - Opcode.IF_EQZ.ordinal();
+ int length = ZERO_CMP_OP_LIST.length;
+ return ZERO_CMP_OP_LIST[(index + 1 + rng.nextInt(length - 1)) % length];
+ }
+ return opcode;
+ }
+
+ @Override
+ protected String getMutationTag() {
+ return "random";
+ }
+} \ No newline at end of file