Fix image bug around clinit and nterp.
nterp trampoline doesn't do clinit checks, so we should install the
resolution stub if a method needs a clinit check before being entered.
Test: 818-clinit-nterp
Bug: 178992601
Change-Id: I9d6b023a6c6620adfcd2a6ed5cd1e042a7cb449b
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index 440b677..0e5914b 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -3175,18 +3175,27 @@
quick_code = GetOatAddressForOffset(quick_oat_code_offset, image_info);
}
+ bool needs_clinit_check = NeedsClinitCheckBeforeCall(method) &&
+ !method->GetDeclaringClass()->IsVisiblyInitialized();
+
if (quick_code == nullptr) {
- // If we don't have code, use generic jni / interpreter bridge.
- // Both perform class initialization check if needed.
+ // If we don't have code, use generic jni / interpreter.
if (method->IsNative()) {
+ // The generic JNI trampolines performs class initialization check if needed.
quick_code = GetOatAddress(StubType::kQuickGenericJNITrampoline);
} else if (CanMethodUseNterp(method, compiler_options_.GetInstructionSet())) {
- quick_code = GetOatAddress(StubType::kNterpTrampoline);
+ // The nterp trampoline doesn't do initialization checks, so install the
+ // resolution stub if needed.
+ if (needs_clinit_check) {
+ quick_code = GetOatAddress(StubType::kQuickResolutionTrampoline);
+ } else {
+ quick_code = GetOatAddress(StubType::kNterpTrampoline);
+ }
} else {
+ // The interpreter brige performs class initialization check if needed.
quick_code = GetOatAddress(StubType::kQuickToInterpreterBridge);
}
- } else if (NeedsClinitCheckBeforeCall(method) &&
- !method->GetDeclaringClass()->IsVisiblyInitialized()) {
+ } else if (needs_clinit_check) {
// If we do have code but the method needs a class initialization check before calling
// that code, install the resolution stub that will perform the check.
quick_code = GetOatAddress(StubType::kQuickResolutionTrampoline);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 9cde319..8080eca 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1979,6 +1979,8 @@
// nterp entry point.
if (method.GetEntryPointFromQuickCompiledCode() == nterp_trampoline_) {
if (can_use_nterp) {
+ DCHECK(!NeedsClinitCheckBeforeCall(&method) ||
+ method.GetDeclaringClass()->IsVisiblyInitialized());
method.SetEntryPointFromQuickCompiledCode(interpreter::GetNterpEntryPoint());
} else {
method.SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
diff --git a/runtime/image.cc b/runtime/image.cc
index 97bb0ad..57a2972 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -29,8 +29,8 @@
namespace art {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-// Last change: Rewrite ClassLinker::LinkFields().
-const uint8_t ImageHeader::kImageVersion[] = { '0', '9', '2', '\0' };
+// Last change: Fix clinit with nterp.
+const uint8_t ImageHeader::kImageVersion[] = { '0', '9', '3', '\0' };
ImageHeader::ImageHeader(uint32_t image_reservation_size,
uint32_t component_count,
diff --git a/test/818-clinit-nterp/expected-stderr.txt b/test/818-clinit-nterp/expected-stderr.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/818-clinit-nterp/expected-stderr.txt
diff --git a/test/818-clinit-nterp/expected-stdout.txt b/test/818-clinit-nterp/expected-stdout.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/818-clinit-nterp/expected-stdout.txt
diff --git a/test/818-clinit-nterp/info.txt b/test/818-clinit-nterp/info.txt
new file mode 100644
index 0000000..7313189
--- /dev/null
+++ b/test/818-clinit-nterp/info.txt
@@ -0,0 +1,2 @@
+Regression test for image creation, which used to install the nterp trampoline
+on methods that required a clinit check.
diff --git a/test/818-clinit-nterp/profile b/test/818-clinit-nterp/profile
new file mode 100644
index 0000000..df4346e
--- /dev/null
+++ b/test/818-clinit-nterp/profile
@@ -0,0 +1,2 @@
+LMain;
+LClinit;
diff --git a/test/818-clinit-nterp/run b/test/818-clinit-nterp/run
new file mode 100644
index 0000000..52d2b5f
--- /dev/null
+++ b/test/818-clinit-nterp/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (C) 2019 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.
+
+exec ${RUN} $@ --profile
diff --git a/test/818-clinit-nterp/src/Main.java b/test/818-clinit-nterp/src/Main.java
new file mode 100644
index 0000000..5342cec
--- /dev/null
+++ b/test/818-clinit-nterp/src/Main.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2021 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) {
+ Clinit.run();
+ if (!clinitDidRun) {
+ throw new Error("Expected Clinit.<clinit> to have run");
+ }
+ }
+ static boolean clinitDidRun = false;
+}
+
+class Clinit {
+ public static void run() {
+ }
+
+ static {
+ Main.clinitDidRun = true;
+ }
+}