summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/art.go32
-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/loop_optimization.cc271
-rw-r--r--compiler/optimizing/loop_optimization.h28
-rw-r--r--compiler/optimizing/nodes.cc1
-rw-r--r--compiler/optimizing/nodes.h10
-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.cc194
-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.cc88
-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/monitor.cc102
-rw-r--r--runtime/monitor.h4
-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/parsed_options.cc3
-rw-r--r--runtime/runtime.cc3
-rw-r--r--runtime/runtime_options.def1
-rw-r--r--runtime/thread.cc19
-rw-r--r--test/004-ThreadStress/expected.txt11
-rwxr-xr-xtest/004-ThreadStress/run22
-rw-r--r--test/004-ThreadStress/src/Main.java2
-rwxr-xr-xtest/131-structural-change/build39
-rw-r--r--test/131-structural-change/expected.txt3
-rw-r--r--test/131-structural-change/info.txt1
-rw-r--r--test/131-structural-change/src/Main.java54
-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
-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/run (renamed from test/131-structural-change/run)6
-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.java (renamed from test/131-structural-change/src-ex/A.java)7
-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.java69
-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.txt2
-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/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.java (renamed from test/131-structural-change/src/A.java)15
-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.java (renamed from test/131-structural-change/src-ex/B.java)10
-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
-rwxr-xr-xtest/etc/run-test-jar8
-rw-r--r--test/knownfailures.json32
-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/bootjars.sh6
-rwxr-xr-xtools/buildbot-build.sh2
147 files changed, 6751 insertions, 1427 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/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/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index d2493137fe..32f40024d3 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
@@ -645,6 +632,9 @@ bool HLoopOptimization::CanVectorize(LoopNode* node, HBasicBlock* block, int64_t
}
}
+ // Consider dynamic loop peeling for alignment.
+ SetPeelingCandidate(trip_count);
+
// Success!
return true;
}
@@ -657,28 +647,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 +705,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 +740,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 +752,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 +764,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.
@@ -795,7 +846,7 @@ bool HLoopOptimization::VectorizeDef(LoopNode* node,
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));
}
@@ -852,7 +903,7 @@ bool HLoopOptimization::VectorizeUse(LoopNode* node,
induction_range_.IsUnitStride(instruction, index, &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,7 +1215,7 @@ 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_;
+ HInstruction* subscript = vector_index_;
if (offset != nullptr) {
subscript = new (global_allocator_) HAdd(Primitive::kPrimInt, subscript, offset);
if (org->IsPhi()) {
@@ -1178,17 +1229,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 +1505,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 +1684,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 +1722,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..b21c4a5aa7 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -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/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 71558e1820..928645ac0f 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -4107,6 +4107,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 +4165,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
@@ -5440,184 +5451,6 @@ bool ClassLinker::LinkClass(Thread* self,
return true;
}
-static void CountMethodsAndFields(ClassDataItemIterator& dex_data,
- size_t* virtual_methods,
- size_t* direct_methods,
- size_t* static_fields,
- size_t* instance_fields) {
- *static_fields = dex_data.NumStaticFields();
- *instance_fields = dex_data.NumInstanceFields();
- *direct_methods = dex_data.NumDirectMethods();
- *virtual_methods = dex_data.NumVirtualMethods();
-}
-
-static void DumpClass(std::ostream& os,
- const DexFile& dex_file, const DexFile::ClassDef& dex_class_def,
- const char* suffix) {
- ClassDataItemIterator dex_data(dex_file, dex_file.GetClassData(dex_class_def));
- os << dex_file.GetClassDescriptor(dex_class_def) << suffix << ":\n";
- os << " Static fields:\n";
- while (dex_data.HasNextStaticField()) {
- const DexFile::FieldId& id = dex_file.GetFieldId(dex_data.GetMemberIndex());
- os << " " << dex_file.GetFieldTypeDescriptor(id) << " " << dex_file.GetFieldName(id) << "\n";
- dex_data.Next();
- }
- os << " Instance fields:\n";
- while (dex_data.HasNextInstanceField()) {
- const DexFile::FieldId& id = dex_file.GetFieldId(dex_data.GetMemberIndex());
- os << " " << dex_file.GetFieldTypeDescriptor(id) << " " << dex_file.GetFieldName(id) << "\n";
- dex_data.Next();
- }
- os << " Direct methods:\n";
- while (dex_data.HasNextDirectMethod()) {
- const DexFile::MethodId& id = dex_file.GetMethodId(dex_data.GetMemberIndex());
- os << " " << dex_file.GetMethodName(id) << dex_file.GetMethodSignature(id).ToString() << "\n";
- dex_data.Next();
- }
- os << " Virtual methods:\n";
- while (dex_data.HasNextVirtualMethod()) {
- const DexFile::MethodId& id = dex_file.GetMethodId(dex_data.GetMemberIndex());
- os << " " << dex_file.GetMethodName(id) << dex_file.GetMethodSignature(id).ToString() << "\n";
- dex_data.Next();
- }
-}
-
-static std::string DumpClasses(const DexFile& dex_file1,
- const DexFile::ClassDef& dex_class_def1,
- const DexFile& dex_file2,
- const DexFile::ClassDef& dex_class_def2) {
- std::ostringstream os;
- DumpClass(os, dex_file1, dex_class_def1, " (Compile time)");
- DumpClass(os, dex_file2, dex_class_def2, " (Runtime)");
- return os.str();
-}
-
-
-// Very simple structural check on whether the classes match. Only compares the number of
-// methods and fields.
-static bool SimpleStructuralCheck(const DexFile& dex_file1,
- const DexFile::ClassDef& dex_class_def1,
- const DexFile& dex_file2,
- const DexFile::ClassDef& dex_class_def2,
- std::string* error_msg) {
- ClassDataItemIterator dex_data1(dex_file1, dex_file1.GetClassData(dex_class_def1));
- ClassDataItemIterator dex_data2(dex_file2, dex_file2.GetClassData(dex_class_def2));
-
- // Counters for current dex file.
- size_t dex_virtual_methods1, dex_direct_methods1, dex_static_fields1, dex_instance_fields1;
- CountMethodsAndFields(dex_data1,
- &dex_virtual_methods1,
- &dex_direct_methods1,
- &dex_static_fields1,
- &dex_instance_fields1);
- // Counters for compile-time dex file.
- size_t dex_virtual_methods2, dex_direct_methods2, dex_static_fields2, dex_instance_fields2;
- CountMethodsAndFields(dex_data2,
- &dex_virtual_methods2,
- &dex_direct_methods2,
- &dex_static_fields2,
- &dex_instance_fields2);
-
- if (dex_virtual_methods1 != dex_virtual_methods2) {
- std::string class_dump = DumpClasses(dex_file1, dex_class_def1, dex_file2, dex_class_def2);
- *error_msg = StringPrintf("Virtual method count off: %zu vs %zu\n%s",
- dex_virtual_methods1,
- dex_virtual_methods2,
- class_dump.c_str());
- return false;
- }
- if (dex_direct_methods1 != dex_direct_methods2) {
- std::string class_dump = DumpClasses(dex_file1, dex_class_def1, dex_file2, dex_class_def2);
- *error_msg = StringPrintf("Direct method count off: %zu vs %zu\n%s",
- dex_direct_methods1,
- dex_direct_methods2,
- class_dump.c_str());
- return false;
- }
- if (dex_static_fields1 != dex_static_fields2) {
- std::string class_dump = DumpClasses(dex_file1, dex_class_def1, dex_file2, dex_class_def2);
- *error_msg = StringPrintf("Static field count off: %zu vs %zu\n%s",
- dex_static_fields1,
- dex_static_fields2,
- class_dump.c_str());
- return false;
- }
- if (dex_instance_fields1 != dex_instance_fields2) {
- std::string class_dump = DumpClasses(dex_file1, dex_class_def1, dex_file2, dex_class_def2);
- *error_msg = StringPrintf("Instance field count off: %zu vs %zu\n%s",
- dex_instance_fields1,
- dex_instance_fields2,
- class_dump.c_str());
- return false;
- }
-
- return true;
-}
-
-// Checks whether a the super-class changed from what we had at compile-time. This would
-// invalidate quickening.
-static bool CheckSuperClassChange(Handle<mirror::Class> klass,
- const DexFile& dex_file,
- const DexFile::ClassDef& class_def,
- ObjPtr<mirror::Class> super_class)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- // Check for unexpected changes in the superclass.
- // Quick check 1) is the super_class class-loader the boot class loader? This always has
- // precedence.
- if (super_class->GetClassLoader() != nullptr &&
- // Quick check 2) different dex cache? Breaks can only occur for different dex files,
- // which is implied by different dex cache.
- klass->GetDexCache() != super_class->GetDexCache()) {
- // Now comes the expensive part: things can be broken if (a) the klass' dex file has a
- // definition for the super-class, and (b) the files are in separate oat files. The oat files
- // are referenced from the dex file, so do (b) first. Only relevant if we have oat files.
- const OatDexFile* class_oat_dex_file = dex_file.GetOatDexFile();
- const OatFile* class_oat_file = nullptr;
- if (class_oat_dex_file != nullptr) {
- class_oat_file = class_oat_dex_file->GetOatFile();
- }
-
- if (class_oat_file != nullptr) {
- const OatDexFile* loaded_super_oat_dex_file = super_class->GetDexFile().GetOatDexFile();
- const OatFile* loaded_super_oat_file = nullptr;
- if (loaded_super_oat_dex_file != nullptr) {
- loaded_super_oat_file = loaded_super_oat_dex_file->GetOatFile();
- }
-
- if (loaded_super_oat_file != nullptr && class_oat_file != loaded_super_oat_file) {
- // Now check (a).
- const DexFile::ClassDef* super_class_def = dex_file.FindClassDef(class_def.superclass_idx_);
- if (super_class_def != nullptr) {
- // Uh-oh, we found something. Do our check.
- std::string error_msg;
- if (!SimpleStructuralCheck(dex_file, *super_class_def,
- super_class->GetDexFile(), *super_class->GetClassDef(),
- &error_msg)) {
- // Print a warning to the log. This exception might be caught, e.g., as common in test
- // drivers. When the class is later tried to be used, we re-throw a new instance, as we
- // only save the type of the exception.
- LOG(WARNING) << "Incompatible structural change detected: " <<
- StringPrintf(
- "Structural change of %s is hazardous (%s at compile time, %s at runtime): %s",
- dex_file.PrettyType(super_class_def->class_idx_).c_str(),
- class_oat_file->GetLocation().c_str(),
- loaded_super_oat_file->GetLocation().c_str(),
- error_msg.c_str());
- ThrowIncompatibleClassChangeError(klass.Get(),
- "Structural change of %s is hazardous (%s at compile time, %s at runtime): %s",
- dex_file.PrettyType(super_class_def->class_idx_).c_str(),
- class_oat_file->GetLocation().c_str(),
- loaded_super_oat_file->GetLocation().c_str(),
- error_msg.c_str());
- return false;
- }
- }
- }
- }
- }
- return true;
-}
-
bool ClassLinker::LoadSuperAndInterfaces(Handle<mirror::Class> klass, const DexFile& dex_file) {
CHECK_EQ(mirror::Class::kStatusIdx, klass->GetStatus());
const DexFile::ClassDef& class_def = dex_file.GetClassDef(klass->GetDexClassDefIndex());
@@ -5650,11 +5483,6 @@ bool ClassLinker::LoadSuperAndInterfaces(Handle<mirror::Class> klass, const DexF
}
CHECK(super_class->IsResolved());
klass->SetSuperClass(super_class);
-
- if (!CheckSuperClassChange(klass, dex_file, class_def, super_class)) {
- DCHECK(Thread::Current()->IsExceptionPending());
- return false;
- }
}
const DexFile::TypeList* interfaces = dex_file.GetInterfacesList(class_def);
if (interfaces != nullptr) {
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..26b7282a0a 100644
--- a/runtime/gc/space/region_space.cc
+++ b/runtime/gc/space/region_space.cc
@@ -29,6 +29,9 @@ namespace space {
// value of the region size, evaculate the region.
static constexpr uint kEvaculateLivePercentThreshold = 75U;
+// If we protect the cleared regions.
+static constexpr bool kProtectClearedRegions = true;
+
MemMap* RegionSpace::CreateMemMap(const std::string& name, size_t capacity,
uint8_t* requested_begin) {
CHECK_ALIGNED(capacity, kRegionSize);
@@ -449,21 +452,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 +539,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..a7a2d1264f 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.
+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/monitor.cc b/runtime/monitor.cc
index a617818c3f..940afc8448 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -73,9 +73,12 @@ static constexpr uint64_t kLongWaitMs = 100;
*/
uint32_t Monitor::lock_profiling_threshold_ = 0;
+uint32_t Monitor::stack_dump_lock_profiling_threshold_ = 0;
-void Monitor::Init(uint32_t lock_profiling_threshold) {
+void Monitor::Init(uint32_t lock_profiling_threshold,
+ uint32_t stack_dump_lock_profiling_threshold) {
lock_profiling_threshold_ = lock_profiling_threshold;
+ stack_dump_lock_profiling_threshold_ = stack_dump_lock_profiling_threshold;
}
Monitor::Monitor(Thread* self, Thread* owner, mirror::Object* obj, int32_t hash_code)
@@ -401,32 +404,81 @@ void Monitor::Lock(Thread* self) {
if (original_owner_thread_id != 0u) {
// Woken from contention.
if (log_contention) {
- uint32_t original_owner_tid = 0;
- std::string original_owner_name;
- {
- MutexLock mu2(Thread::Current(), *Locks::thread_list_lock_);
- // Re-find the owner in case the thread got killed.
- Thread* original_owner = Runtime::Current()->GetThreadList()->FindThreadByThreadId(
- original_owner_thread_id);
- // Do not do any work that requires the mutator lock.
- if (original_owner != nullptr) {
- original_owner_tid = original_owner->GetTid();
- original_owner->GetThreadName(original_owner_name);
- }
+ uint64_t wait_ms = MilliTime() - wait_start_ms;
+ uint32_t sample_percent;
+ if (wait_ms >= lock_profiling_threshold_) {
+ sample_percent = 100;
+ } else {
+ sample_percent = 100 * wait_ms / lock_profiling_threshold_;
}
-
- if (original_owner_tid != 0u) {
- uint64_t wait_ms = MilliTime() - wait_start_ms;
- uint32_t sample_percent;
- if (wait_ms >= lock_profiling_threshold_) {
- sample_percent = 100;
- } else {
- sample_percent = 100 * wait_ms / lock_profiling_threshold_;
+ if (sample_percent != 0 && (static_cast<uint32_t>(rand() % 100) < sample_percent)) {
+ // Reacquire mutator_lock_ for logging.
+ ScopedObjectAccess soa(self);
+
+ bool owner_alive = false;
+ pid_t original_owner_tid = 0;
+ std::string original_owner_name;
+
+ const bool should_dump_stacks = stack_dump_lock_profiling_threshold_ > 0 &&
+ wait_ms > stack_dump_lock_profiling_threshold_;
+ std::string owner_stack_dump;
+
+ // Acquire thread-list lock to find thread and keep it from dying until we've got all
+ // the info we need.
+ {
+ MutexLock mu2(Thread::Current(), *Locks::thread_list_lock_);
+
+ // Re-find the owner in case the thread got killed.
+ Thread* original_owner = Runtime::Current()->GetThreadList()->FindThreadByThreadId(
+ original_owner_thread_id);
+
+ if (original_owner != nullptr) {
+ owner_alive = true;
+ original_owner_tid = original_owner->GetTid();
+ original_owner->GetThreadName(original_owner_name);
+
+ if (should_dump_stacks) {
+ // Very long contention. Dump stacks.
+ struct CollectStackTrace : public Closure {
+ void Run(art::Thread* thread) OVERRIDE
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ thread->DumpJavaStack(oss);
+ }
+
+ std::ostringstream oss;
+ };
+ CollectStackTrace owner_trace;
+ original_owner->RequestSynchronousCheckpoint(&owner_trace);
+ owner_stack_dump = owner_trace.oss.str();
+ }
+ }
+ // This is all the data we need. Now drop the thread-list lock, it's OK for the
+ // owner to go away now.
}
- if (sample_percent != 0 && (static_cast<uint32_t>(rand() % 100) < sample_percent)) {
- // Reacquire mutator_lock_ for logging.
- ScopedObjectAccess soa(self);
- if (wait_ms > kLongWaitMs && owners_method != nullptr) {
+
+ // If we found the owner (and thus have owner data), go and log now.
+ if (owner_alive) {
+ // Give the detailed traces for really long contention.
+ if (should_dump_stacks) {
+ // This must be here (and not above) because we cannot hold the thread-list lock
+ // while running the checkpoint.
+ std::ostringstream self_trace_oss;
+ self->DumpJavaStack(self_trace_oss);
+
+ uint32_t pc;
+ ArtMethod* m = self->GetCurrentMethod(&pc);
+
+ LOG(WARNING) << "Long "
+ << PrettyContentionInfo(original_owner_name,
+ original_owner_tid,
+ owners_method,
+ owners_dex_pc,
+ num_waiters)
+ << " in " << ArtMethod::PrettyMethod(m) << " for "
+ << PrettyDuration(MsToNs(wait_ms)) << "\n"
+ << "Current owner stack:\n" << owner_stack_dump
+ << "Contender stack:\n" << self_trace_oss.str();
+ } else if (wait_ms > kLongWaitMs && owners_method != nullptr) {
uint32_t pc;
ArtMethod* m = self->GetCurrentMethod(&pc);
// TODO: We should maybe check that original_owner is still a live thread.
diff --git a/runtime/monitor.h b/runtime/monitor.h
index 96c5a5b7cc..09faeba9a6 100644
--- a/runtime/monitor.h
+++ b/runtime/monitor.h
@@ -55,7 +55,7 @@ class Monitor {
~Monitor();
- static void Init(uint32_t lock_profiling_threshold);
+ static void Init(uint32_t lock_profiling_threshold, uint32_t stack_dump_lock_profiling_threshold);
// Return the thread id of the lock owner or 0 when there is no owner.
static uint32_t GetLockOwnerThreadId(mirror::Object* obj)
@@ -228,7 +228,6 @@ class Monitor {
ArtMethod* owners_method,
uint32_t owners_dex_pc,
size_t num_waiters)
- REQUIRES(!Locks::thread_list_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
// Wait on a monitor until timeout, interrupt, or notification. Used for Object.wait() and
@@ -274,6 +273,7 @@ class Monitor {
ALWAYS_INLINE static void AtraceMonitorUnlock();
static uint32_t lock_profiling_threshold_;
+ static uint32_t stack_dump_lock_profiling_threshold_;
Mutex monitor_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
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/parsed_options.cc b/runtime/parsed_options.cc
index b1eb506209..1d524fd5e6 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -239,6 +239,9 @@ std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognize
.Define("-Xlockprofthreshold:_")
.WithType<unsigned int>()
.IntoKey(M::LockProfThreshold)
+ .Define("-Xstackdumplockprofthreshold:_")
+ .WithType<unsigned int>()
+ .IntoKey(M::StackDumpLockProfThreshold)
.Define("-Xusetombstonedtraces")
.WithValue(true)
.IntoKey(M::UseTombstonedTraces)
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 46b4392ef4..bf9e4051ae 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1064,7 +1064,8 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
oat_file_manager_ = new OatFileManager;
Thread::SetSensitiveThreadHook(runtime_options.GetOrDefault(Opt::HookIsSensitiveThread));
- Monitor::Init(runtime_options.GetOrDefault(Opt::LockProfThreshold));
+ Monitor::Init(runtime_options.GetOrDefault(Opt::LockProfThreshold),
+ runtime_options.GetOrDefault(Opt::StackDumpLockProfThreshold));
boot_class_path_string_ = runtime_options.ReleaseOrDefault(Opt::BootClassPath);
class_path_string_ = runtime_options.ReleaseOrDefault(Opt::ClassPath);
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 3d23e20e1e..09a200afc4 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -100,6 +100,7 @@ RUNTIME_OPTIONS_KEY (Unit, NoSigChain)
RUNTIME_OPTIONS_KEY (Unit, ForceNativeBridge)
RUNTIME_OPTIONS_KEY (LogVerbosity, Verbose)
RUNTIME_OPTIONS_KEY (unsigned int, LockProfThreshold)
+RUNTIME_OPTIONS_KEY (unsigned int, StackDumpLockProfThreshold)
RUNTIME_OPTIONS_KEY (bool, UseTombstonedTraces, false)
RUNTIME_OPTIONS_KEY (std::string, StackTraceFile)
RUNTIME_OPTIONS_KEY (Unit, MethodTrace)
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 9f8c90acc3..be1614b3cc 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1467,11 +1467,13 @@ bool Thread::RequestSynchronousCheckpoint(Closure* function) {
{
ScopedThreadListLockUnlock stllu(self);
- ScopedThreadSuspension sts(self, ThreadState::kWaiting);
- while (GetState() == ThreadState::kRunnable) {
- // We became runnable again. Wait till the suspend triggered in ModifySuspendCount
- // moves us to suspended.
- sched_yield();
+ {
+ ScopedThreadSuspension sts(self, ThreadState::kWaiting);
+ while (GetState() == ThreadState::kRunnable) {
+ // We became runnable again. Wait till the suspend triggered in ModifySuspendCount
+ // moves us to suspended.
+ sched_yield();
+ }
}
function->Run(this);
@@ -1485,6 +1487,13 @@ bool Thread::RequestSynchronousCheckpoint(Closure* function) {
DCHECK(updated);
}
+ {
+ // Imitate ResumeAll, the thread may be waiting on Thread::resume_cond_ since we raised its
+ // suspend count. Now the suspend_count_ is lowered so we must do the broadcast.
+ MutexLock mu2(self, *Locks::thread_suspend_count_lock_);
+ Thread::resume_cond_->Broadcast(self);
+ }
+
return true; // We're done, break out of the loop.
}
}
diff --git a/test/004-ThreadStress/expected.txt b/test/004-ThreadStress/expected.txt
index 772faf6e9f..9eaab24ede 100644
--- a/test/004-ThreadStress/expected.txt
+++ b/test/004-ThreadStress/expected.txt
@@ -9,3 +9,14 @@ Finishing worker
Finishing worker
Finishing worker
Finishing worker
+JNI_OnLoad called
+Starting worker for N
+Starting worker for N
+Starting worker for N
+Starting worker for N
+Starting worker for N
+Finishing worker
+Finishing worker
+Finishing worker
+Finishing worker
+Finishing worker
diff --git a/test/004-ThreadStress/run b/test/004-ThreadStress/run
new file mode 100755
index 0000000000..3851d739ea
--- /dev/null
+++ b/test/004-ThreadStress/run
@@ -0,0 +1,22 @@
+#!/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.
+
+# Enable lock contention logging.
+${RUN} --runtime-option -Xlockprofthreshold:10 "${@}"
+
+# Run locks-only mode with stack-dump lock profiling. Reduce the number of total operations from
+# the default 1000 to 100.
+${RUN} --runtime-option -Xlockprofthreshold:10 --runtime-option -Xstackdumplockprofthreshold:20 "${@}" Main --locks-only -o 100
diff --git a/test/004-ThreadStress/src/Main.java b/test/004-ThreadStress/src/Main.java
index 5cae3983bf..95d164978b 100644
--- a/test/004-ThreadStress/src/Main.java
+++ b/test/004-ThreadStress/src/Main.java
@@ -232,7 +232,7 @@ public class Main implements Runnable {
public boolean perform() {
synchronized (lock) {
try {
- Thread.sleep((int)(Math.random()*10));
+ Thread.sleep((int)(Math.random() * 50 + 50));
} catch (InterruptedException ignored) {
}
}
diff --git a/test/131-structural-change/build b/test/131-structural-change/build
deleted file mode 100755
index ff0da2098d..0000000000
--- a/test/131-structural-change/build
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2015 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.
-
-# Stop if something fails.
-set -e
-
-if [ ${USE_JACK} = "true" ]; then
- ${JACK} --output-dex . src
- zip $TEST_NAME.jar classes.dex
-
- ${JACK} --output-dex . src-ex
- zip ${TEST_NAME}-ex.jar classes.dex
-else
- mkdir classes
- ${JAVAC} -d classes `find src -name '*.java'`
-
- mkdir classes-ex
- ${JAVAC} -d classes-ex `find src-ex -name '*.java'`
-
- if [ ${NEED_DEX} = "true" ]; then
- ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes
- zip $TEST_NAME.jar classes.dex
- ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes.dex --dump-width=1000 classes-ex
- zip ${TEST_NAME}-ex.jar classes.dex
- fi
-fi
diff --git a/test/131-structural-change/expected.txt b/test/131-structural-change/expected.txt
deleted file mode 100644
index 1d19278f1e..0000000000
--- a/test/131-structural-change/expected.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-JNI_OnLoad called
-Should really reach here.
-Done.
diff --git a/test/131-structural-change/info.txt b/test/131-structural-change/info.txt
deleted file mode 100644
index 6d5817bcea..0000000000
--- a/test/131-structural-change/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-Check whether a structural change in a (non-native) multi-dex scenario is detected.
diff --git a/test/131-structural-change/src/Main.java b/test/131-structural-change/src/Main.java
deleted file mode 100644
index c7488992df..0000000000
--- a/test/131-structural-change/src/Main.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import java.io.File;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-
-/**
- * Structural hazard test.
- */
-public class Main {
- public static void main(String[] args) {
- System.loadLibrary(args[0]);
- new Main().run();
- }
-
- private void run() {
- try {
- Class<?> bClass = getClass().getClassLoader().loadClass("A");
- System.out.println("Should really reach here.");
- } catch (Exception e) {
- e.printStackTrace(System.out);
- }
-
- boolean haveOatFile = hasOatFile();
- boolean gotError = false;
- try {
- Class<?> bClass = getClass().getClassLoader().loadClass("B");
- } catch (IncompatibleClassChangeError icce) {
- gotError = true;
- } catch (Exception e) {
- e.printStackTrace(System.out);
- }
- if (haveOatFile ^ gotError) {
- System.out.println("Did not get expected error. " + haveOatFile + " " + gotError);
- }
- System.out.println("Done.");
- }
-
- private native static boolean hasOatFile();
-}
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/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/131-structural-change/run b/test/652-deopt-intrinsic/run
index 63fdb8c749..97d1ff16bb 100755
--- a/test/131-structural-change/run
+++ b/test/652-deopt-intrinsic/run
@@ -1,6 +1,6 @@
#!/bin/bash
#
-# Copyright (C) 2015 The Android Open Source Project
+# 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.
@@ -14,5 +14,5 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Use secondary switch to add secondary dex file to class path.
-exec ${RUN} "${@}" --secondary
+# 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/131-structural-change/src-ex/A.java b/test/656-annotation-lookup-generic-jni/src-ex/DummyAnnotation.java
index 800347b716..6caac6685e 100644
--- a/test/131-structural-change/src-ex/A.java
+++ b/test/656-annotation-lookup-generic-jni/src-ex/DummyAnnotation.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -14,7 +14,4 @@
* limitations under the License.
*/
-public class A {
- public void bar() {
- }
-}
+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..0d0885c85a
--- /dev/null
+++ b/test/656-checker-simd-opt/src/Main.java
@@ -0,0 +1,69 @@
+/*
+ * 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;
+ }
+ }
+
+ 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]);
+ }
+ 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 63343b8fb4..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()
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/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/131-structural-change/src/A.java b/test/993-breakpoints/src/Main.java
index b07de581d8..b11f6f8174 100644
--- a/test/131-structural-change/src/A.java
+++ b/test/993-breakpoints/src/Main.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -14,13 +14,8 @@
* limitations under the License.
*/
-public class A {
- public void foo() {
- }
-
- public void bar() {
- }
-
- public void baz() {
- }
+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/131-structural-change/src-ex/B.java b/test/994-breakpoint-line/src/Main.java
index 61369db32d..39cfeb3ee4 100644
--- a/test/131-structural-change/src-ex/B.java
+++ b/test/994-breakpoint-line/src/Main.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-public class B extends A {
- public void test() {
- System.out.println("Should not reach this!");
- }
+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/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 8bdaeaaf5a..a8d492bf4d 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -143,15 +143,6 @@
"complete test) JDWP must be set up."]
},
{
- "tests": "131-structural-change",
- "variant": "debug",
- "description": ["131 is an old test. The functionality has been",
- "implemented at an earlier stage and is checked",
- "in tests 138. Blacklisted for debug builds since",
- "these builds have duplicate classes checks which",
- "punt to interpreter"]
- },
- {
"tests": "138-duplicate-classes-check",
"variant": "ndebug",
"description": ["Turned on for debug builds since debug builds have",
@@ -511,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": [
@@ -519,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": [
@@ -550,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": [
@@ -588,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": [
@@ -611,6 +602,14 @@
},
{
"tests": [
+ "567-checker-compare"
+ ],
+ "description": "Checker tests failing when run with --build-with-javac-dx.",
+ "env_vars": {"ANDROID_COMPILE_WITH_JACK": "false"},
+ "bug": "b/62950048"
+ },
+ {
+ "tests": [
"536-checker-needs-access-check",
"537-checker-inline-and-unverified",
"569-checker-pattern-replacement",
@@ -674,5 +673,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/bootjars.sh b/tools/bootjars.sh
index bb47e55183..f710de99bb 100755
--- a/tools/bootjars.sh
+++ b/tools/bootjars.sh
@@ -79,8 +79,10 @@ fi
# Print the file path (relative to $TOP) to the classes.jar of each boot jar in the intermediates directory.
intermediates_dir=$(get_build_var "$intermediates_env_var")
-# turn the file path into an absolute path
-intermediates_dir=$(readlink -f $TOP/$intermediates_dir)
+# turn the file path into an absolute path if needed
+pushd "$TOP" >/dev/null
+intermediates_dir=$(readlink -m "$intermediates_dir")
+popd >/dev/null
for jar in $boot_jars_list; do
echo "$intermediates_dir/JAVA_LIBRARIES/${jar}_intermediates/classes.jar"
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