summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libdexfile/dex/dex_file_verifier.cc7
-rw-r--r--runtime/jit/jit.cc26
-rwxr-xr-xtest/716-jli-jit-samples/build20
-rw-r--r--test/716-jli-jit-samples/expected.txt3
-rw-r--r--test/716-jli-jit-samples/info.txt2
-rw-r--r--test/716-jli-jit-samples/src-art/Main.java142
-rw-r--r--test/common/runtime_state.cc22
-rw-r--r--test/knownfailures.json1
8 files changed, 209 insertions, 14 deletions
diff --git a/libdexfile/dex/dex_file_verifier.cc b/libdexfile/dex/dex_file_verifier.cc
index 2380f48d3a..ba65fc9b28 100644
--- a/libdexfile/dex/dex_file_verifier.cc
+++ b/libdexfile/dex/dex_file_verifier.cc
@@ -615,7 +615,7 @@ bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx,
uint32_t code_offset,
ClassDataItemIterator* direct_it,
bool expect_direct) {
- DCHECK(expect_direct || direct_it != nullptr);
+ DCHECK_EQ(expect_direct, direct_it == nullptr);
// Check for overflow.
if (!CheckIndex(idx, header_->method_ids_size_, "class_data_item method_idx")) {
return false;
@@ -1252,9 +1252,10 @@ bool DexFileVerifier::CheckIntraCodeItem() {
uint32_t* handler_offsets;
constexpr size_t kAllocaMaxSize = 1024;
if (handlers_size < kAllocaMaxSize/sizeof(uint32_t)) {
+ // Note: Clang does not specify alignment guarantees for alloca. So align by hand.
handler_offsets =
- AlignDown(reinterpret_cast<uint32_t*>(alloca((handlers_size + 1) * sizeof(uint32_t))),
- alignof(uint32_t[]));
+ AlignUp(reinterpret_cast<uint32_t*>(alloca((handlers_size + 1) * sizeof(uint32_t))),
+ alignof(uint32_t[]));
} else {
handler_offsets_uptr.reset(new uint32_t[handlers_size]);
handler_offsets = handler_offsets_uptr.get();
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index d20f760c17..2c0fbadc1d 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -29,6 +29,8 @@
#include "interpreter/interpreter.h"
#include "java_vm_ext.h"
#include "jit_code_cache.h"
+#include "mirror/method_handle_impl.h"
+#include "mirror/var_handle.h"
#include "oat_file_manager.h"
#include "oat_quick_method_header.h"
#include "profile/profile_compilation_info.h"
@@ -638,15 +640,33 @@ class JitCompileTask FINAL : public Task {
DISALLOW_IMPLICIT_CONSTRUCTORS(JitCompileTask);
};
+static bool IgnoreSamplesForMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (method->IsClassInitializer() || !method->IsCompilable()) {
+ // We do not want to compile such methods.
+ return true;
+ }
+ if (method->IsNative()) {
+ ObjPtr<mirror::Class> klass = method->GetDeclaringClass();
+ if (klass == mirror::MethodHandle::StaticClass() || klass == mirror::VarHandle::StaticClass()) {
+ // MethodHandle and VarHandle invocation methods are required to throw an
+ // UnsupportedOperationException if invoked reflectively. We achieve this by having native
+ // implementations that arise the exception. We need to disable JIT compilation of these JNI
+ // methods as it can lead to transitioning between JIT compiled JNI stubs and generic JNI
+ // stubs. Since these stubs have different stack representations we can then crash in stack
+ // walking (b/78151261).
+ return true;
+ }
+ }
+ return false;
+}
+
void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_backedges) {
if (thread_pool_ == nullptr) {
// Should only see this when shutting down.
DCHECK(Runtime::Current()->IsShuttingDown(self));
return;
}
-
- if (method->IsClassInitializer() || !method->IsCompilable()) {
- // We do not want to compile such methods.
+ if (IgnoreSamplesForMethod(method)) {
return;
}
if (hot_method_threshold_ == 0) {
diff --git a/test/716-jli-jit-samples/build b/test/716-jli-jit-samples/build
new file mode 100755
index 0000000000..730a8a14bb
--- /dev/null
+++ b/test/716-jli-jit-samples/build
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+# make us exit on a failure
+set -e
+
+./default-build "$@" --experimental var-handles
diff --git a/test/716-jli-jit-samples/expected.txt b/test/716-jli-jit-samples/expected.txt
new file mode 100644
index 0000000000..dc533f3689
--- /dev/null
+++ b/test/716-jli-jit-samples/expected.txt
@@ -0,0 +1,3 @@
+JNI_OnLoad called
+MethodHandle OK
+VarHandle OK
diff --git a/test/716-jli-jit-samples/info.txt b/test/716-jli-jit-samples/info.txt
new file mode 100644
index 0000000000..81a76f64da
--- /dev/null
+++ b/test/716-jli-jit-samples/info.txt
@@ -0,0 +1,2 @@
+Test MethodHandle and VarHandle invokes do not accumulate JIT samples
+(regression test for b/78151261).
diff --git a/test/716-jli-jit-samples/src-art/Main.java b/test/716-jli-jit-samples/src-art/Main.java
new file mode 100644
index 0000000000..def6b9f669
--- /dev/null
+++ b/test/716-jli-jit-samples/src-art/Main.java
@@ -0,0 +1,142 @@
+/*
+ * 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.
+ */
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.VarHandle;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class Main {
+ private static final int ITERATIONS = 100;
+
+ private static final VarHandle widgetIdVarHandle;
+
+ public static native int getHotnessCounter(Class<?> cls, String methodName);
+
+ public static class Widget {
+ public Widget(int id) {
+ this.id = id;
+ }
+
+ int getId() {
+ return id;
+ }
+
+ int id;
+ }
+
+ static {
+ try {
+ widgetIdVarHandle = MethodHandles.lookup().findVarHandle(Widget.class, "id", int.class);
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+
+ private static void assertEquals(int i1, int i2) {
+ if (i1 == i2) {
+ return;
+ }
+ throw new AssertionError("assertEquals i1: " + i1 + ", i2: " + i2);
+ }
+
+ private static void assertEquals(Object o, Object p) {
+ if (o == p) {
+ return;
+ }
+ if (o != null && p != null && o.equals(p)) {
+ return;
+ }
+ throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p);
+ }
+
+ private static void fail() {
+ System.out.println("fail");
+ Thread.dumpStack();
+ }
+
+ private static void fail(String message) {
+ System.out.println("fail: " + message);
+ Thread.dumpStack();
+ }
+
+ private static void testMethodHandleCounters() throws Throwable {
+ for (int i = 0; i < ITERATIONS; ++i) {
+ // Regular MethodHandle invocations
+ MethodHandle mh =
+ MethodHandles.lookup()
+ .findConstructor(
+ Widget.class, MethodType.methodType(void.class, int.class));
+ Widget w = (Widget) mh.invoke(3);
+ w = (Widget) mh.invokeExact(3);
+ assertEquals(0, getHotnessCounter(MethodHandle.class, "invoke"));
+ assertEquals(0, getHotnessCounter(MethodHandle.class, "invokeExact"));
+
+ // Reflective MethodHandle invocations
+ String[] methodNames = {"invoke", "invokeExact"};
+ for (String methodName : methodNames) {
+ Method invokeMethod = MethodHandle.class.getMethod(methodName, Object[].class);
+ MethodHandle instance =
+ MethodHandles.lookup()
+ .findVirtual(
+ Widget.class, "getId", MethodType.methodType(int.class));
+ try {
+ invokeMethod.invoke(instance, new Object[] {new Object[] {}});
+ fail();
+ } catch (InvocationTargetException ite) {
+ assertEquals(ite.getCause().getClass(), UnsupportedOperationException.class);
+ }
+ }
+ assertEquals(0, getHotnessCounter(MethodHandle.class, "invoke"));
+ assertEquals(0, getHotnessCounter(MethodHandle.class, "invokeExact"));
+ }
+
+ System.out.println("MethodHandle OK");
+ }
+
+ private static void testVarHandleCounters() throws Throwable {
+ Widget w = new Widget(0);
+ for (int i = 0; i < ITERATIONS; ++i) {
+ // Regular accessor invocations
+ widgetIdVarHandle.set(w, i);
+ assertEquals(i, widgetIdVarHandle.get(w));
+ assertEquals(0, getHotnessCounter(VarHandle.class, "set"));
+ assertEquals(0, getHotnessCounter(VarHandle.class, "get"));
+
+ // Reflective accessor invocations
+ for (String accessorName : new String[] {"get", "set"}) {
+ Method setMethod = VarHandle.class.getMethod(accessorName, Object[].class);
+ try {
+ setMethod.invoke(widgetIdVarHandle, new Object[] {new Object[0]});
+ fail();
+ } catch (InvocationTargetException ite) {
+ assertEquals(ite.getCause().getClass(), UnsupportedOperationException.class);
+ }
+ }
+ assertEquals(0, getHotnessCounter(VarHandle.class, "set"));
+ assertEquals(0, getHotnessCounter(VarHandle.class, "get"));
+ }
+ System.out.println("VarHandle OK");
+ }
+
+ public static void main(String[] args) throws Throwable {
+ System.loadLibrary(args[0]);
+ testMethodHandleCounters();
+ testVarHandleCounters();
+ }
+}
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index a55cc79ef9..f89888bb99 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -258,17 +258,23 @@ extern "C" JNIEXPORT int JNICALL Java_Main_getHotnessCounter(JNIEnv* env,
jclass,
jclass cls,
jstring method_name) {
- ArtMethod* method = nullptr;
- {
- ScopedObjectAccess soa(Thread::Current());
+ ScopedObjectAccess soa(Thread::Current());
+ ScopedUtfChars chars(env, method_name);
+ CHECK(chars.c_str() != nullptr);
+ ArtMethod* method =
+ soa.Decode<mirror::Class>(cls)->FindDeclaredDirectMethodByName(chars.c_str(),
+ kRuntimePointerSize);
+ if (method != nullptr) {
+ return method->GetCounter();
+ }
- ScopedUtfChars chars(env, method_name);
- CHECK(chars.c_str() != nullptr);
- method = soa.Decode<mirror::Class>(cls)->FindDeclaredDirectMethodByName(
- chars.c_str(), kRuntimePointerSize);
+ method = soa.Decode<mirror::Class>(cls)->FindDeclaredVirtualMethodByName(chars.c_str(),
+ kRuntimePointerSize);
+ if (method != nullptr) {
+ return method->GetCounter();
}
- return method->GetCounter();
+ return std::numeric_limits<int32_t>::min();
}
extern "C" JNIEXPORT int JNICALL Java_Main_numberOfDeoptimizations(JNIEnv*, jclass) {
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 369f927270..e109bf5a8f 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -883,6 +883,7 @@
"706-checker-scheduler",
"707-checker-invalid-profile",
"714-invoke-custom-lambda-metafactory",
+ "716-jli-jit-samples",
"800-smali",
"801-VoidCheckCast",
"802-deoptimization",