diff options
-rw-r--r-- | libdexfile/dex/dex_file_verifier.cc | 7 | ||||
-rw-r--r-- | runtime/jit/jit.cc | 26 | ||||
-rwxr-xr-x | test/716-jli-jit-samples/build | 20 | ||||
-rw-r--r-- | test/716-jli-jit-samples/expected.txt | 3 | ||||
-rw-r--r-- | test/716-jli-jit-samples/info.txt | 2 | ||||
-rw-r--r-- | test/716-jli-jit-samples/src-art/Main.java | 142 | ||||
-rw-r--r-- | test/common/runtime_state.cc | 22 | ||||
-rw-r--r-- | test/knownfailures.json | 1 |
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", |