diff options
| -rw-r--r-- | compiler/optimizing/graph_visualizer.cc | 5 | ||||
| -rw-r--r-- | runtime/interpreter/interpreter.cc | 13 | ||||
| -rw-r--r-- | test/680-checker-deopt-dex-pc-0/expected.txt | 2 | ||||
| -rw-r--r-- | test/680-checker-deopt-dex-pc-0/info.txt | 2 | ||||
| -rw-r--r-- | test/680-checker-deopt-dex-pc-0/src/Main.java | 59 |
5 files changed, 78 insertions, 3 deletions
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 5ff31cead5..719904d780 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -576,6 +576,11 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { } StartAttributeStream() << input_list; } + if (instruction->GetDexPc() != kNoDexPc) { + StartAttributeStream("dex_pc") << instruction->GetDexPc(); + } else { + StartAttributeStream("dex_pc") << "n/a"; + } instruction->Accept(this); if (instruction->HasEnvironment()) { StringList envs; diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 735c0e815a..f23304c391 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -243,11 +243,13 @@ static inline JValue Execute( const CodeItemDataAccessor& accessor, ShadowFrame& shadow_frame, JValue result_register, - bool stay_in_interpreter = false) REQUIRES_SHARED(Locks::mutator_lock_) { + bool stay_in_interpreter = false, + bool from_deoptimize = false) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(!shadow_frame.GetMethod()->IsAbstract()); DCHECK(!shadow_frame.GetMethod()->IsNative()); - if (LIKELY(shadow_frame.GetDexPC() == 0)) { // Entering the method, but not via deoptimization. + if (LIKELY(!from_deoptimize)) { // Entering the method, but not via deoptimization. if (kIsDebugBuild) { + CHECK_EQ(shadow_frame.GetDexPC(), 0u); self->AssertNoPendingException(); } instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); @@ -568,7 +570,12 @@ void EnterInterpreterFromDeoptimize(Thread* self, } if (new_dex_pc != dex::kDexNoIndex) { shadow_frame->SetDexPC(new_dex_pc); - value = Execute(self, accessor, *shadow_frame, value); + value = Execute(self, + accessor, + *shadow_frame, + value, + /* stay_in_interpreter */ true, + /* from_deoptimize */ true); } ShadowFrame* old_frame = shadow_frame; shadow_frame = shadow_frame->GetLink(); diff --git a/test/680-checker-deopt-dex-pc-0/expected.txt b/test/680-checker-deopt-dex-pc-0/expected.txt new file mode 100644 index 0000000000..805857dc65 --- /dev/null +++ b/test/680-checker-deopt-dex-pc-0/expected.txt @@ -0,0 +1,2 @@ +JNI_OnLoad called +passed diff --git a/test/680-checker-deopt-dex-pc-0/info.txt b/test/680-checker-deopt-dex-pc-0/info.txt new file mode 100644 index 0000000000..8eae156b22 --- /dev/null +++ b/test/680-checker-deopt-dex-pc-0/info.txt @@ -0,0 +1,2 @@ +Regression test for deoptimization at dex pc 0 causing infinite recursion +for JIT-at-first-use. diff --git a/test/680-checker-deopt-dex-pc-0/src/Main.java b/test/680-checker-deopt-dex-pc-0/src/Main.java new file mode 100644 index 0000000000..d5a6a9015c --- /dev/null +++ b/test/680-checker-deopt-dex-pc-0/src/Main.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + // We run this test for AOT to verify that there is a HDeoptimize with dex pc 0. + /// CHECK-START: int Main.$noinline$getInt(byte[], int) BCE (after) + /// CHECK: Deoptimize dex_pc:0 + public static int $noinline$getInt(byte[] array, int offset) { + // The aget for `array[offset]` is at dex pc 0, so the Deoptimize + // from dynamic BCE shall also be at dex pc 0. + return ((array[offset ] & 0xFF) << 0) + + ((array[offset + 1] & 0xFF) << 8) + + ((array[offset + 2] & 0xFF) << 16) + + ((array[offset + 3] & 0xFF) << 24); + } + + public static void main(String[] args) { + System.loadLibrary(args[0]); + if (hasJit()) { + byte[] array = { 0, 1, 2, 3 }; + while (!hasJitCompiledEntrypoint(Main.class, "$noinline$getInt")) { + for (int i = 0; i < 10000; ++i) { + if ($noinline$getInt(array, 0) != 0x03020100) { + throw new Error(); + } + } + try { + Thread.sleep(200); + } catch (InterruptedException ignored) {} + } + try { + // The HDeoptimize at dex pc 0 was previously handled poorly as the dex pc 0 + // was used to detect whether we entered the method. This meant that the + // instrumentation would have reported MethodEnteredEvent and we would have + // told JIT that the method was entered. With JIT-on-first-use we would also + // immediatelly recompile the method and run the compiled code leading to + // a an infinite deoptimization recursion, yielding StackOverflowError. + $noinline$getInt(array, 1); + } catch (ArrayIndexOutOfBoundsException ignored) {} + } + System.out.println("passed"); + } + + public static native boolean hasJit(); + public native static boolean hasJitCompiledEntrypoint(Class<?> cls, String methodName); +} |