Merge "Revert "ART: Add StackVisitor accepting a lambda""
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index e2a0a39..e610bb1 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -163,6 +163,16 @@
$(ART_TEST_TARGET_GTEST_VerifierDepsMulti_DEX): $(ART_TEST_GTEST_VerifierDepsMulti_SRC) $(HOST_OUT_EXECUTABLES)/smali
$(HOST_OUT_EXECUTABLES)/smali assemble --output $@ $(filter %.smali,$^)
+ART_TEST_GTEST_VerifySoftFailDuringClinit_SRC := $(abspath $(wildcard $(LOCAL_PATH)/VerifySoftFailDuringClinit/*.smali))
+ART_TEST_HOST_GTEST_VerifySoftFailDuringClinit_DEX := $(dir $(ART_TEST_HOST_GTEST_Main_DEX))$(subst Main,VerifySoftFailDuringClinit,$(basename $(notdir $(ART_TEST_HOST_GTEST_Main_DEX))))$(suffix $(ART_TEST_HOST_GTEST_Main_DEX))
+ART_TEST_TARGET_GTEST_VerifySoftFailDuringClinit_DEX := $(dir $(ART_TEST_TARGET_GTEST_Main_DEX))$(subst Main,VerifySoftFailDuringClinit,$(basename $(notdir $(ART_TEST_TARGET_GTEST_Main_DEX))))$(suffix $(ART_TEST_TARGET_GTEST_Main_DEX))
+
+$(ART_TEST_HOST_GTEST_VerifySoftFailDuringClinit_DEX): $(ART_TEST_GTEST_VerifySoftFailDuringClinit_SRC) $(HOST_OUT_EXECUTABLES)/smali
+ $(HOST_OUT_EXECUTABLES)/smali assemble --output $@ $(filter %.smali,$^)
+
+$(ART_TEST_TARGET_GTEST_VerifySoftFailDuringClinit_DEX): $(ART_TEST_GTEST_VerifySoftFailDuringClinit_SRC) $(HOST_OUT_EXECUTABLES)/smali
+ $(HOST_OUT_EXECUTABLES)/smali assemble --output $@ $(filter %.smali,$^)
+
# Dex file dependencies for each gtest.
ART_GTEST_art_dex_file_loader_test_DEX_DEPS := GetMethodSignature Main Nested MultiDex
ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary MyClassNatives Nested VerifierDeps VerifierDepsMulti
@@ -180,7 +190,7 @@
ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle
ART_GTEST_hiddenapi_test_DEX_DEPS := HiddenApi
ART_GTEST_hidden_api_test_DEX_DEPS := HiddenApiSignatures
-ART_GTEST_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB DefaultMethods
+ART_GTEST_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB DefaultMethods VerifySoftFailDuringClinit
ART_GTEST_imtable_test_DEX_DEPS := IMTA IMTB
ART_GTEST_instrumentation_test_DEX_DEPS := Instrumentation
ART_GTEST_jni_compiler_test_DEX_DEPS := MyClassNatives
@@ -752,5 +762,8 @@
ART_TEST_GTEST_VerifierDeps_SRC :=
ART_TEST_HOST_GTEST_VerifierDeps_DEX :=
ART_TEST_TARGET_GTEST_VerifierDeps_DEX :=
+ART_TEST_GTEST_VerifySoftFailDuringClinit_SRC :=
+ART_TEST_HOST_GTEST_VerifySoftFailDuringClinit_DEX :=
+ART_TEST_TARGET_GTEST_VerifySoftFailDuringClinit_DEX :=
GTEST_DEX_DIRECTORIES :=
LOCAL_PATH :=
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
new file mode 100644
index 0000000..8bddb5d
--- /dev/null
+++ b/build/apex/Android.bp
@@ -0,0 +1,99 @@
+// Android Runtime APEX module.
+
+// Modules listed in LOCAL_REQUIRED_MODULES for module art-runtime in art/Android.mk.
+// - Base requirements (binaries for which both 32- and 64-bit versions are built, if relevant).
+art_runtime_base_binaries_both = [
+ "dalvikvm",
+]
+// - Base requirements (binaries for which a 32-bit version is preferred).
+art_runtime_base_binaries_prefer32 = [
+ "dex2oat",
+ "dexoptanalyzer",
+ "profman",
+]
+// - Base requirements (libraries).
+art_runtime_base_native_shared_libs = [
+ "libart",
+ "libart-compiler",
+ "libopenjdkjvm",
+ "libopenjdkjvmti",
+ "libadbconnection",
+]
+// - Fake library that avoids namespace issues and gives some warnings for nosy apps.
+art_runtime_fake_native_shared_libs = [
+ // FIXME: Does not work as-is, because `libart_fake` is defined in libart_fake/Android.mk,
+ // and because a module defined in a Blueprint file cannot depend on a module defined in a
+ // Makefile. To support `libart_fake` as a dependency of this APEX module, we can either
+ // (probably in that order of preference):
+ // a. translate that logic into Blueprint; or
+ // b. write the whole Android Runtime APEX generation logic in Android.mk; or
+ // c. introduce an `art_apex` module type extending the `apex` module type and write the
+ // corresponding Go logic to handle this extra dependency.
+ //"libart_fake",
+]
+// - Debug variants (binaries for which a 32-bit version is preferred).
+// FIXME: These modules are optional (the built product can decide to include them or not).
+// Should they be moved to another APEX file?
+art_runtime_debug_binaries_prefer32 = [
+ "dex2oatd",
+ "dexoptanalyzerd",
+ "profmand",
+]
+art_runtime_debug_native_shared_libs = [
+ "libartd",
+ "libartd-compiler",
+ "libopenjdkd",
+ "libopenjdkjvmd",
+ "libopenjdkjvmtid",
+ "libadbconnectiond",
+]
+
+// Modules listed in LOCAL_REQUIRED_MODULES for module art-tools in art/Android.mk.
+art_tools_binaries = [
+ "dexdiag",
+ "dexdump",
+ "dexlist",
+ "oatdump",
+]
+
+// Host-only modules listed in LOCAL_REQUIRED_MODULES for module art-tools in art/Android.mk.
+// TODO: Include these modules in the future "host APEX".
+art_tools_host_binaries = [
+ // FIXME: Does not work as-is, because `ahat` is defined in tools/ahat/Android.mk
+ // (same issue as for `libart_fake` above).
+ //"ahat",
+ "hprof-conv",
+ // ...
+]
+
+apex_key {
+ name: "com.android.runtime.key",
+ public_key: "runtime.avbpubkey",
+ private_key: "runtime.pem",
+}
+
+apex {
+ name: "com.android.runtime",
+ compile_multilib: "both",
+ manifest: "manifest.json",
+ native_shared_libs: art_runtime_base_native_shared_libs
+ + art_runtime_fake_native_shared_libs
+ + art_runtime_debug_native_shared_libs,
+ multilib: {
+ both: {
+ // TODO: Add logic to create a `dalvikvm` symlink to `dalvikvm32` or `dalvikvm64`
+ // (see `symlink_preferred_arch` in art/dalvikvm/Android.bp).
+ binaries: art_runtime_base_binaries_both,
+ },
+ prefer32: {
+ binaries: art_runtime_base_binaries_prefer32
+ + art_runtime_debug_binaries_prefer32
+ },
+ first: {
+ binaries: art_tools_binaries,
+ }
+ },
+ key: "com.android.runtime.key",
+ // TODO: Also package a `ld.config.txt` config file (to be placed in `etc/`).
+ // ...
+}
diff --git a/build/apex/manifest.json b/build/apex/manifest.json
new file mode 100644
index 0000000..20a8314
--- /dev/null
+++ b/build/apex/manifest.json
@@ -0,0 +1,4 @@
+{
+ "name": "com.android.runtime",
+ "version": 1
+}
diff --git a/build/apex/runtests.sh b/build/apex/runtests.sh
new file mode 100755
index 0000000..6af2a8b
--- /dev/null
+++ b/build/apex/runtests.sh
@@ -0,0 +1,201 @@
+#!/bin/bash
+
+# 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.
+#
+
+# Run Android Runtime APEX tests.
+
+function say {
+ echo "$0: $*"
+}
+
+function die {
+ echo "$0: $*"
+ exit 1
+}
+
+which guestmount >/dev/null && which guestunmount >/dev/null && which virt-filesystems >/dev/null \
+ || die "This script requires 'guestmount', 'guestunmount',
+and 'virt-filesystems' from libguestfs. On Debian-based systems, these tools
+can be installed with:
+
+ sudo apt-get install libguestfs-tools
+"
+[[ -n "$ANDROID_PRODUCT_OUT" ]] \
+ || die "You need to source and lunch before you can use this script."
+
+# Fail early.
+set -e
+
+build_apex_p=true
+list_image_files_p=false
+
+function usage {
+ cat <<EOF
+Usage: $0 [OPTION]
+Build (optional) and run tests on Android Runtime APEX package (on host).
+
+ -s, --skip-build skip the build step
+ -l, --list-files list the contents of the ext4 image
+ -h, --help display this help and exit
+
+EOF
+ exit
+}
+
+while [[ $# -gt 0 ]]; do
+ case "$1" in
+ (-s|--skip-build) build_apex_p=false;;
+ (-l|--list-files) list_image_files_p=true;;
+ (-h|--help) usage;;
+ (*) die "Unknown option: '$1'
+Try '$0 --help' for more information.";;
+ esac
+ shift
+done
+
+work_dir=$(mktemp -d)
+mount_point="$work_dir/image"
+
+# Garbage collection.
+function finish {
+ # Don't fail early during cleanup.
+ set +e
+ guestunmount "$mount_point"
+ rm -rf "$work_dir"
+}
+
+trap finish EXIT
+
+apex_module="com.android.runtime"
+
+# Build the Android Runtime APEX package (optional).
+$build_apex_p && say "Building package" && make "$apex_module"
+
+system_apexdir="$ANDROID_PRODUCT_OUT/system/apex"
+apex_package="$system_apexdir/$apex_module.apex"
+
+say "Extracting and mounting image"
+
+# Extract the image from the Android Runtime APEX.
+image_filename="image.img"
+unzip -q "$apex_package" "$image_filename" -d "$work_dir"
+mkdir "$mount_point"
+image_file="$work_dir/$image_filename"
+
+# Check filesystems in the image.
+image_filesystems="$work_dir/image_filesystems"
+virt-filesystems -a "$image_file" >"$image_filesystems"
+# We expect a single partition (/dev/sda) in the image.
+partition="/dev/sda"
+echo "$partition" | cmp "$image_filesystems" -
+
+# Mount the image from the Android Runtime APEX.
+guestmount -a "$image_file" -m "$partition" "$mount_point"
+
+# List the contents of the mounted image (optional).
+$list_image_files_p && say "Listing image files" && ls -ld "$mount_point" && tree -ap "$mount_point"
+
+say "Running tests"
+
+# Check that the mounted image contains a manifest.
+[[ -f "$mount_point/manifest.json" ]]
+
+function check_binary {
+ [[ -x "$mount_point/bin/$1" ]] || die "Cannot find binary '$1' in mounted image"
+}
+
+function check_multilib_binary {
+ # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve
+ # the precision of this test?
+ [[ -x "$mount_point/bin/${1}32" ]] || [[ -x "$mount_point/bin/${1}64" ]] \
+ || die "Cannot find binary '$1' in mounted image"
+}
+
+function check_binary_symlink {
+ [[ -h "$mount_point/bin/$1" ]] || die "Cannot find symbolic link '$1' in mounted image"
+}
+
+function check_library {
+ # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve
+ # the precision of this test?
+ [[ -f "$mount_point/lib/$1" ]] || [[ -f "$mount_point/lib64/$1" ]] \
+ || die "Cannot find library '$1' in mounted image"
+}
+
+# Check that the mounted image contains ART base binaries.
+check_multilib_binary dalvikvm
+# TODO: Does not work yet.
+: check_binary_symlink dalvikvm
+check_binary dex2oat
+check_binary dexoptanalyzer
+check_binary profman
+
+# Check that the mounted image contains ART tools binaries.
+check_binary dexdiag
+check_binary dexdump
+check_binary dexlist
+check_binary oatdump
+
+# Check that the mounted image contains ART debug binaries.
+check_binary dex2oatd
+check_binary dexoptanalyzerd
+check_binary profmand
+
+# Check that the mounted image contains ART libraries.
+check_library libart-compiler.so
+check_library libart.so
+check_library libopenjdkjvm.so
+check_library libopenjdkjvmti.so
+check_library libadbconnection.so
+# TODO: Should we check for these libraries too, even if they are not explicitly
+# listed as dependencies in the Android Runtime APEX module rule?
+check_library libartbase.so
+check_library libart-dexlayout.so
+check_library libart-disassembler.so
+check_library libdexfile.so
+check_library libprofile.so
+
+# Check that the mounted image contains ART debug libraries.
+check_library libartd-compiler.so
+check_library libartd.so
+check_library libdexfiled.so
+check_library libopenjdkd.so
+check_library libopenjdkjvmd.so
+check_library libopenjdkjvmtid.so
+check_library libadbconnectiond.so
+# TODO: Should we check for these libraries too, even if they are not explicitly
+# listed as dependencies in the Android Runtime APEX module rule?
+check_library libartbased.so
+check_library libartd-dexlayout.so
+check_library libprofiled.so
+
+# TODO: Should we check for other libraries, such as:
+#
+# libbacktrace.so
+# libbase.so
+# liblog.so
+# libsigchain.so
+# libtombstoned_client.so
+# libunwindstack.so
+# libvixl-arm64.so
+# libvixl-arm.so
+# libvixld-arm64.so
+# libvixld-arm.so
+# ...
+#
+# ?
+
+say "Tests passed"
diff --git a/build/apex/runtime.avbpubkey b/build/apex/runtime.avbpubkey
new file mode 100644
index 0000000..b0ffc9b
--- /dev/null
+++ b/build/apex/runtime.avbpubkey
Binary files differ
diff --git a/build/apex/runtime.pem b/build/apex/runtime.pem
new file mode 100644
index 0000000..4c7ce4b
--- /dev/null
+++ b/build/apex/runtime.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKgIBAAKCAgEAx/VRn+TOZ4Hah9tHkb2Jvw7aQcqurnNamGa1Ta3x09HEV45s
+KTAqeTIPpbagx7aj6LNimiqoJaClV8pFhYfC6y7GLnXBk6PRGb2kPmrWy2aQFRkh
+Z2LBQwu15Rqr3SVbzMPbC5qoXOpUopzZnjRwniR32fnpJedUwpSMxaJwXDxfgBku
+Wm3EHBuTQ33L/z3VGwbVp1Rh/QhI/RfcwT1u6o9XUl0LqiQu/8DLTLNmsjAsQkbA
+8O1ToIBu2l71HaMqXOKRKtVuAYPyAMml5hXSH0dva2ebKkUM8E7FhcsK71QJ5iCs
+L2uC+OmG9f4aSqiIJld7/nDU7nrkiUxjs5bGp2PIxHmuv725XdBMJ+IuqnZGGkA8
+4tF14bY1YX4Tq6ojzATBnbFcZEAU4epJvX13Wu11ktYndMbppUtnVCdhO2vnA/tP
+MpBSOCHMk2Y2Q96LcIN9ANJrcrkrSIGBTQdvCRJ9LtofXlk/ytGIUceCzRtFhmwL
+zWFwJVT7cQX04Pw/EX/zrZyOq7SUYCGDsBwZsUtlZ30Cx92dergtKlZyJFqKnwMv
+hajr55mqRCv4M1dumCgiQaml29ftXWE6wQxqI0jQN8seSVz/HUazjSb3QFXgX16z
+w4VkxqSKu4subqesMcxiyev5McGXUUthkRGDSSFbJwX0L5jNEPyYPUu2nJ0CAwEA
+AQKCAgEAxGKuDin8hjBE3tWAjyTmWp1Nwvw7X96vhaqqOmayceU9vviERlel/24p
+bAnYEw3QIcW8+8kVaA9FFNn2OdVCnRVNU2gX/NcRkQRugVcRKqfKrs4FvrKBOUYR
+Gbh+Py5n4M4jHlyBKvCCu0rteLHsQYVzqMQINk/jMVAQijKlxBEPgpI4slvIFgsH
+MWwlpMOnv2mRAUyhCJDQjrKW/7tEal7p1lzIDgyHlGxXvzcbj7o8XcN7z6RnU+WP
++iz09GzCOIPVK4p/BkH+tsNVioq32jygs44IGRXERWg4GtV2IeQZ7Mj+E3y2H53M
+DWHJlLW9MlsNzrImjypntmkuKr3Uz+ipg/oXD1tv/XJkBkJUsWSQHzGw4DfxRfq7
+eJ9LlIMzrQn8ZJAJTSsckmGuakSyD9amSbtn1kl+fEZge9SvAoZVZelwB1qfGgyS
+qQVAN9x1SP0DCeX33syxT2rxZVOUZgRT8yt01jVcIU3dD66McYRjiUY6uG1aZ3Xb
+p8TD3xKMqPPc7dIN3xcN58S+sIejydmm636LE1ykA0dYPczqxDfIfhbqE/42B5LZ
+grjZdXN1pd97IeEFQLd+DfP8iq80D6k6ojmXxANXCz1ilJXyr2defWUwSSiwsD5v
+HacFeOQ6+KQyYrkdhbpa5XlO6luDIZmxN3B6rx7kqg6UZW9EzYkCggEBAPDNOZ6X
+TIKBIdV5zkr2rvjor/WvPnoWUOBCmxh8zaAZhReE3RitNjtEVz/ns/x8vyyMRdPA
+JDszBrawYlMjoEW9NQe6BYKfwKRl+QzsWEIsdBfYB70vmguwS/VdMbVaU/jWFbS+
+IFB9F88iEJiI8IeH+IomGXinCDxAkXqJztFZRzonaX5+QHC4s8geRyYn9gs6SxHy
+MqOOzifnebZg4dXLCL7jMDGsEa/Fu188FFae407BsOEt4bday37n91xysdilkPg3
+b3mIB3EFrsbnqXypayM/QUfQ/d48Xfa/l+74i1Qpd1MIeHYNndLDxtRes9Oc7Rnv
+oCdI9Lkc+KuR8AcCggEBANSUKb2jz0VfSZSZsgL5gj34Kcfoe5peQvP+pUuJmZhy
+8QkGUUNtq2l86PMJSfJknbUhVLPe0wzT8NG08HTMkVHlw7lve//avugfpnrR7hsZ
+BTWDjW44x+Y8Q8dwTUl3nYtEYn81ycUzmFBmYDEVXjlvyMlXe0HLEz90v2wwtZlp
+IxEXgEgMnLj36JH5iKh7YuLf9c8laok7Jed6u+h5nlXUcbfaSVN6U3K+6UdQKUrr
+TaSQLw2pEsZ6CEt0yGJDkoID7mfTfc1/olNWWGUz0RE9G5eqQYjgEoAiTBZZeSlm
+3Kaun8gydN7wwJ6AjPCPFOwtgV7dUoN4YbWgfsAgnTsCggEBALHOWCWKgqw6vcjr
+0C/6Ruj0qDk51WBA6icuB2flf9AgB+595OQ7wjexFtvRM03UrzUtvsHEtvwfiW2M
+gI3zWH0mYOn7qeXyIEVEJsosGl+Cg5a3pb9ETvMiknPzBKlssWSkcBKt8R59v/7q
+oGaBd1ocRKF90IEOlT4oT0O0Tkq3Kaj/QR5uCxcwy0+RS+gYyc0wlg4CUPIEmKVO
+fsj0cM10xlhtWUDUVZr83oZLzpjHagDVdM5RGsJRAMIMdtKEvl3Co3ElPeL3VsdV
+8uBcXwH1925nXsSwxUQ8PwXcI0wJqpfSppFhR9Gj7E2c0kwuQYqX7VuhXRik/k9R
+3SyS7jECggEBAL7q7m4GL8IjKSdPvgNT6TgUqBmFX3UtkT4nhnbH9u1m1bmANf20
+Ak20RFb6EbKj0Mv7SmJdDfkoY9FDiu2rSBxgmZ7yVFBeOjSpMFCAODOYDgiYxK2o
+S0go+cqlvpPr3M9WNIwBV9xHUVVsDJookb5N+etyKR3W78t+4+ib+oz0Uu0nySts
+QFkTNYncrXJ7lj0iXVaUSRFE0O8LWLYafCyjpxoy7sYNR+L3OPW2Nc+2cr4ITGod
+XeJpeQejs9Ak1fD07OnMlOC576SfGLaTigHMevqEi2UNsS/pHaK46stXOXZtwM0B
+G9uaJ7RyyaHHL0hKOjVj2pZ+yGph4VRWNj8CggEAQlp/QytXhKZtM9OqRy/th+XO
+ctoVEl8codUydwwxMCsKqGYiCXazeyDZQimOjaxSNFXo8hWuf694WGsQJ6TyXCEs
+0JAJbCooI+DI9Z4LbqHtLDg1/S6a1558Nyrc6j6amevvbB5xKS2mKhGl5JgzBsJO
+H3yE0DD1DHaSM3V1rTfdyGoaxNESw45bnpxkAooMrw62OIO/9f502FLUx+sq+koT
+aajw4qQ6rBll3/+PKCORKzncHDMkIbeD6c6sX+ONUz7vxg3pV4eZG7NClWvA24Td
+1sANz3m6EmqG41lBzeUGConWxWRwkEXJgbxmPwMariRKR8aNVOlDVVbDp9Hhxg==
+-----END RSA PRIVATE KEY-----
diff --git a/build/art.go b/build/art.go
index 0b5ee44..1c8be0f 100644
--- a/build/art.go
+++ b/build/art.go
@@ -53,6 +53,9 @@
cflags = append(cflags, "-DART_HEAP_POISONING=1")
asflags = append(asflags, "-DART_HEAP_POISONING=1")
}
+ if envTrue(ctx, "ART_USE_CXX_INTERPRETER") {
+ cflags = append(cflags, "-DART_USE_CXX_INTERPRETER=1")
+ }
if !envFalse(ctx, "ART_USE_READ_BARRIER") && ctx.AConfig().ArtUseReadBarrier() {
// Used to change the read barrier type. Valid values are BAKER, BROOKS,
diff --git a/build/codegen.go b/build/codegen.go
index 8526bf1..d0db78e 100644
--- a/build/codegen.go
+++ b/build/codegen.go
@@ -107,8 +107,9 @@
}
type CodegenCommonArchProperties struct {
- Srcs []string
- Cflags []string
+ Srcs []string
+ Cflags []string
+ Cppflags []string
}
type CodegenLibraryArchProperties struct {
diff --git a/compiler/Android.bp b/compiler/Android.bp
index c2f8e3c..0d92b05 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -157,6 +157,8 @@
"optimizing/code_generator_x86.cc",
"optimizing/code_generator_vector_x86.cc",
"optimizing/intrinsics_x86.cc",
+ "optimizing/instruction_simplifier_x86_shared.cc",
+ "optimizing/instruction_simplifier_x86.cc",
"optimizing/pc_relative_fixups_x86.cc",
"optimizing/x86_memory_gen.cc",
"utils/x86/assembler_x86.cc",
@@ -168,6 +170,7 @@
srcs: [
"jni/quick/x86_64/calling_convention_x86_64.cc",
"optimizing/intrinsics_x86_64.cc",
+ "optimizing/instruction_simplifier_x86_64.cc",
"optimizing/code_generator_x86_64.cc",
"optimizing/code_generator_vector_x86_64.cc",
"utils/x86_64/assembler_x86_64.cc",
@@ -180,7 +183,6 @@
shared_libs: [
"libbase",
"libcutils", // for atrace.
- "liblzma",
],
include_dirs: ["art/disassembler"],
header_libs: [
@@ -196,7 +198,6 @@
static_libs: [
"libbase",
"libcutils",
- "liblzma",
],
}
@@ -230,12 +231,12 @@
// VIXL assembly support for ARM targets.
static: {
whole_static_libs: [
- "libvixl-arm",
+ "libvixl",
],
},
shared: {
shared_libs: [
- "libvixl-arm",
+ "libvixl",
],
},
},
@@ -243,12 +244,12 @@
// VIXL assembly support for ARM64 targets.
static: {
whole_static_libs: [
- "libvixl-arm64",
+ "libvixl",
],
},
shared: {
shared_libs: [
- "libvixl-arm64",
+ "libvixl",
],
},
},
@@ -292,12 +293,12 @@
// VIXL assembly support for ARM targets.
static: {
whole_static_libs: [
- "libvixld-arm",
+ "libvixld",
],
},
shared: {
shared_libs: [
- "libvixld-arm",
+ "libvixld",
],
},
},
@@ -305,12 +306,12 @@
// VIXL assembly support for ARM64 targets.
static: {
whole_static_libs: [
- "libvixld-arm64",
+ "libvixld",
],
},
shared: {
shared_libs: [
- "libvixld-arm64",
+ "libvixld",
],
},
},
@@ -451,8 +452,7 @@
"libprofiled",
"libartd-compiler",
"libartd-simulator-container",
- "libvixld-arm",
- "libvixld-arm64",
+ "libvixld",
"libbacktrace",
"libnativeloader",
@@ -509,7 +509,6 @@
},
shared_libs: [
"libartd-compiler",
- "libvixld-arm",
- "libvixld-arm64",
+ "libvixld",
],
}
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index fc8cd52..599f4aa 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -188,13 +188,13 @@
compiler_options_->compile_pic_ = false; // Non-PIC boot image is a test configuration.
compiler_options_->SetCompilerFilter(GetCompilerFilter());
compiler_options_->image_classes_.swap(*GetImageClasses());
+ compiler_options_->profile_compilation_info_ = GetProfileCompilationInfo();
compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
verification_results_.get(),
compiler_kind_,
&compiler_options_->image_classes_,
number_of_threads_,
- /* swap_fd */ -1,
- GetProfileCompilationInfo()));
+ /* swap_fd */ -1));
}
void CommonCompilerTest::SetUpRuntimeOptions(RuntimeOptions* options) {
diff --git a/compiler/driver/compiled_method_storage_test.cc b/compiler/driver/compiled_method_storage_test.cc
index 5e2f444..8b35bd3 100644
--- a/compiler/driver/compiled_method_storage_test.cc
+++ b/compiler/driver/compiled_method_storage_test.cc
@@ -33,8 +33,7 @@
Compiler::kOptimizing,
/* image_classes */ nullptr,
/* thread_count */ 1u,
- /* swap_fd */ -1,
- /* profile_compilation_info */ nullptr);
+ /* swap_fd */ -1);
CompiledMethodStorage* storage = driver.GetCompiledMethodStorage();
ASSERT_TRUE(storage->DedupeEnabled()); // The default.
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index 294072d..792f508 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -99,25 +99,6 @@
return std::make_pair(fast_get, fast_put);
}
-inline ArtMethod* CompilerDriver::ResolveMethod(
- ScopedObjectAccess& soa,
- Handle<mirror::DexCache> dex_cache,
- Handle<mirror::ClassLoader> class_loader,
- const DexCompilationUnit* mUnit,
- uint32_t method_idx,
- InvokeType invoke_type) {
- DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get());
- ArtMethod* resolved_method =
- mUnit->GetClassLinker()->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>(
- method_idx, dex_cache, class_loader, /* referrer */ nullptr, invoke_type);
- if (UNLIKELY(resolved_method == nullptr)) {
- DCHECK(soa.Self()->IsExceptionPending());
- // Clean up any exception left by type resolution.
- soa.Self()->ClearException();
- }
- return resolved_method;
-}
-
inline VerificationResults* CompilerDriver::GetVerificationResults() const {
DCHECK(Runtime::Current()->IsAotCompiler());
return verification_results_;
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 8c276bb..0d0a7f2 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -248,8 +248,7 @@
Compiler::Kind compiler_kind,
HashSet<std::string>* image_classes,
size_t thread_count,
- int swap_fd,
- const ProfileCompilationInfo* profile_compilation_info)
+ int swap_fd)
: compiler_options_(compiler_options),
verification_results_(verification_results),
compiler_(Compiler::Create(this, compiler_kind)),
@@ -259,9 +258,7 @@
had_hard_verifier_failure_(false),
parallel_thread_count_(thread_count),
stats_(new AOTCompilationStats),
- compiler_context_(nullptr),
compiled_method_storage_(swap_fd),
- profile_compilation_info_(profile_compilation_info),
max_arena_alloc_(0),
dex_to_dex_compiler_(this) {
DCHECK(compiler_options_ != nullptr);
@@ -431,7 +428,6 @@
Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
optimizer::DexToDexCompiler::CompilationLevel dex_to_dex_compilation_level,
- bool compilation_enabled,
Handle<mirror::DexCache> dex_cache,
CompileFn compile_fn) {
DCHECK(driver != nullptr);
@@ -449,7 +445,6 @@
class_loader,
dex_file,
dex_to_dex_compilation_level,
- compilation_enabled,
dex_cache);
if (kTimeCompileMethod) {
@@ -482,7 +477,6 @@
Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
optimizer::DexToDexCompiler::CompilationLevel dex_to_dex_compilation_level,
- bool compilation_enabled,
Handle<mirror::DexCache> dex_cache) {
auto dex_2_dex_fn = [](Thread* self ATTRIBUTE_UNUSED,
CompilerDriver* driver,
@@ -494,7 +488,6 @@
Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
optimizer::DexToDexCompiler::CompilationLevel dex_to_dex_compilation_level,
- bool compilation_enabled ATTRIBUTE_UNUSED,
Handle<mirror::DexCache> dex_cache ATTRIBUTE_UNUSED) -> CompiledMethod* {
DCHECK(driver != nullptr);
MethodReference method_ref(&dex_file, method_idx);
@@ -531,7 +524,6 @@
class_loader,
dex_file,
dex_to_dex_compilation_level,
- compilation_enabled,
dex_cache,
dex_2_dex_fn);
}
@@ -547,7 +539,6 @@
Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
optimizer::DexToDexCompiler::CompilationLevel dex_to_dex_compilation_level,
- bool compilation_enabled,
Handle<mirror::DexCache> dex_cache) {
auto quick_fn = [](
Thread* self,
@@ -560,7 +551,6 @@
Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
optimizer::DexToDexCompiler::CompilationLevel dex_to_dex_compilation_level,
- bool compilation_enabled,
Handle<mirror::DexCache> dex_cache) {
DCHECK(driver != nullptr);
CompiledMethod* compiled_method = nullptr;
@@ -586,7 +576,7 @@
VerificationResults* results = driver->GetVerificationResults();
DCHECK(results != nullptr);
const VerifiedMethod* verified_method = results->GetVerifiedMethod(method_ref);
- bool compile = compilation_enabled &&
+ bool compile =
// Basic checks, e.g., not <clinit>.
results->IsCandidateForCompilation(method_ref, access_flags) &&
// Did not fail to create VerifiedMethod metadata.
@@ -629,7 +619,6 @@
class_loader,
dex_file,
dex_to_dex_compilation_level,
- compilation_enabled,
dex_cache,
quick_fn);
}
@@ -663,7 +652,6 @@
h_class_loader,
dex_file,
dex_to_dex_compilation_level,
- true,
dex_cache);
const size_t num_methods = dex_to_dex_compiler_.NumCodeItemsToQuicken(self);
@@ -679,7 +667,6 @@
h_class_loader,
dex_file,
dex_to_dex_compilation_level,
- true,
dex_cache);
dex_to_dex_compiler_.ClearState();
}
@@ -724,16 +711,14 @@
}
TimingLogger::ScopedTiming t("Resolve const-string Strings", timings);
+ // TODO: Implement a profile-based filter for the boot image. See b/76145463.
for (ClassAccessor accessor : dex_file->GetClasses()) {
- if (!IsClassToCompile(accessor.GetDescriptor())) {
- // Compilation is skipped, do not resolve const-string in code of this class.
- // FIXME: Make sure that inlining honors this. b/26687569
- continue;
- }
+ const ProfileCompilationInfo* profile_compilation_info =
+ GetCompilerOptions().GetProfileCompilationInfo();
const bool is_startup_class =
- profile_compilation_info_ != nullptr &&
- profile_compilation_info_->ContainsClass(*dex_file, accessor.GetClassIdx());
+ profile_compilation_info != nullptr &&
+ profile_compilation_info->ContainsClass(*dex_file, accessor.GetClassIdx());
for (const ClassAccessor::Method& method : accessor.GetMethods()) {
const bool is_clinit = (method.GetAccessFlags() & kAccConstructor) != 0 &&
@@ -741,8 +726,8 @@
const bool is_startup_clinit = is_startup_class && is_clinit;
if (only_startup_strings &&
- profile_compilation_info_ != nullptr &&
- (!profile_compilation_info_->GetMethodHotness(method.GetReference()).IsStartup() &&
+ profile_compilation_info != nullptr &&
+ (!profile_compilation_info->GetMethodHotness(method.GetReference()).IsStartup() &&
!is_startup_clinit)) {
continue;
}
@@ -834,12 +819,6 @@
TimingLogger::ScopedTiming t("Initialize type check bitstrings", timings);
for (ClassAccessor accessor : dex_file->GetClasses()) {
- if (!driver->IsClassToCompile(accessor.GetDescriptor())) {
- // Compilation is skipped, do not look for type checks in code of this class.
- // FIXME: Make sure that inlining honors this. b/26687569
- continue;
- }
-
// Direct and virtual methods.
for (const ClassAccessor::Method& method : accessor.GetMethods()) {
InitializeTypeCheckBitstrings(driver, class_linker, dex_cache, *dex_file, method);
@@ -966,13 +945,6 @@
}
}
-bool CompilerDriver::IsClassToCompile(const char* descriptor) const {
- if (classes_to_compile_ == nullptr) {
- return true;
- }
- return classes_to_compile_->find(StringPiece(descriptor)) != classes_to_compile_->end();
-}
-
bool CompilerDriver::ShouldCompileBasedOnProfile(const MethodReference& method_ref) const {
// Profile compilation info may be null if no profile is passed.
if (!CompilerFilter::DependsOnProfile(compiler_options_->GetCompilerFilter())) {
@@ -981,12 +953,14 @@
return true;
}
// If we are using a profile filter but do not have a profile compilation info, compile nothing.
- if (profile_compilation_info_ == nullptr) {
+ const ProfileCompilationInfo* profile_compilation_info =
+ GetCompilerOptions().GetProfileCompilationInfo();
+ if (profile_compilation_info == nullptr) {
return false;
}
// Compile only hot methods, it is the profile saver's job to decide what startup methods to mark
// as hot.
- bool result = profile_compilation_info_->GetMethodHotness(method_ref).IsHot();
+ bool result = profile_compilation_info->GetMethodHotness(method_ref).IsHot();
if (kDebugProfileGuidedCompilation) {
LOG(INFO) << "[ProfileGuidedCompilation] "
@@ -2610,9 +2584,6 @@
optimizer::DexToDexCompiler::CompilationLevel dex_to_dex_compilation_level =
GetDexToDexCompilationLevel(soa.Self(), *driver, jclass_loader, dex_file, class_def);
-
- const bool compilation_enabled = driver->IsClassToCompile(accessor.GetDescriptor());
-
// Compile direct and virtual methods.
int64_t previous_method_idx = -1;
for (const ClassAccessor::Method& method : accessor.GetMethods()) {
@@ -2633,7 +2604,6 @@
class_loader,
dex_file,
dex_to_dex_compilation_level,
- compilation_enabled,
dex_cache);
}
};
@@ -2644,10 +2614,12 @@
const std::vector<const DexFile*>& dex_files,
TimingLogger* timings) {
if (kDebugProfileGuidedCompilation) {
+ const ProfileCompilationInfo* profile_compilation_info =
+ GetCompilerOptions().GetProfileCompilationInfo();
LOG(INFO) << "[ProfileGuidedCompilation] " <<
- ((profile_compilation_info_ == nullptr)
+ ((profile_compilation_info == nullptr)
? "null"
- : profile_compilation_info_->DumpInfo(dex_files));
+ : profile_compilation_info->DumpInfo(dex_files));
}
dex_to_dex_compiler_.ClearState();
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index f42e555..714b2d1 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -99,8 +99,7 @@
Compiler::Kind compiler_kind,
HashSet<std::string>* image_classes,
size_t thread_count,
- int swap_fd,
- const ProfileCompilationInfo* profile_compilation_info);
+ int swap_fd);
~CompilerDriver();
@@ -146,7 +145,6 @@
bool GetCompiledClass(const ClassReference& ref, ClassStatus* status) const;
CompiledMethod* GetCompiledMethod(MethodReference ref) const;
- size_t GetNonRelativeLinkerPatchCount() const;
// Add a compiled method.
void AddCompiledMethod(const MethodReference& method_ref, CompiledMethod* const compiled_method);
CompiledMethod* RemoveCompiledMethod(const MethodReference& method_ref);
@@ -181,16 +179,6 @@
uint16_t field_idx)
REQUIRES_SHARED(Locks::mutator_lock_);
- // Resolve a method. Returns null on failure, including incompatible class change.
- ArtMethod* ResolveMethod(
- ScopedObjectAccess& soa,
- Handle<mirror::DexCache> dex_cache,
- Handle<mirror::ClassLoader> class_loader,
- const DexCompilationUnit* mUnit,
- uint32_t method_idx,
- InvokeType invoke_type)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
void ProcessedInstanceField(bool resolved);
void ProcessedStaticField(bool resolved, bool local);
@@ -209,14 +197,6 @@
const VerifiedMethod* GetVerifiedMethod(const DexFile* dex_file, uint32_t method_idx) const;
bool IsSafeCast(const DexCompilationUnit* mUnit, uint32_t dex_pc);
- void SetCompilerContext(void* compiler_context) {
- compiler_context_ = compiler_context;
- }
-
- void* GetCompilerContext() const {
- return compiler_context_;
- }
-
size_t GetThreadCount() const {
return parallel_thread_count_;
}
@@ -229,9 +209,6 @@
return compiled_method_storage_.DedupeEnabled();
}
- // Checks whether the provided class should be compiled, i.e., is in classes_to_compile_.
- bool IsClassToCompile(const char* descriptor) const;
-
// Checks whether profile guided compilation is enabled and if the method should be compiled
// according to the profile file.
bool ShouldCompileBasedOnProfile(const MethodReference& method_ref) const;
@@ -266,10 +243,6 @@
return &compiled_method_storage_;
}
- const ProfileCompilationInfo* GetProfileCompilationInfo() const {
- return profile_compilation_info_;
- }
-
// Is `boot_image_filename` the name of a core image (small boot
// image used for ART testing only)?
static bool IsCoreImageFilename(const std::string& boot_image_filename) {
@@ -363,6 +336,12 @@
void FreeThreadPools();
void CheckThreadPools();
+ // Resolve const string literals that are loaded from dex code. If only_startup_strings is
+ // specified, only methods that are marked startup in the profile are resolved.
+ void ResolveConstStrings(const std::vector<const DexFile*>& dex_files,
+ bool only_startup_strings,
+ /*inout*/ TimingLogger* timings);
+
const CompilerOptions* const compiler_options_;
VerificationResults* const verification_results_;
@@ -377,13 +356,6 @@
typedef AtomicDexRefMap<MethodReference, CompiledMethod*> MethodTable;
- private:
- // Resolve const string literals that are loaded from dex code. If only_startup_strings is
- // specified, only methods that are marked startup in the profile are resolved.
- void ResolveConstStrings(const std::vector<const DexFile*>& dex_files,
- bool only_startup_strings,
- /*inout*/ TimingLogger* timings);
-
// All method references that this compiler has compiled.
MethodTable compiled_methods_;
@@ -393,11 +365,6 @@
// Dex2Oat rather than implicitly by CompileAll().
HashSet<std::string>* image_classes_;
- // Specifies the classes that will be compiled. Note that if classes_to_compile_ is null,
- // all classes are eligible for compilation (duplication filters etc. will still apply).
- // This option may be restricted to the boot image, depending on a flag in the implementation.
- std::unique_ptr<HashSet<std::string>> classes_to_compile_;
-
std::atomic<uint32_t> number_of_soft_verifier_failures_;
bool had_hard_verifier_failure_;
@@ -412,16 +379,8 @@
class AOTCompilationStats;
std::unique_ptr<AOTCompilationStats> stats_;
- typedef void (*CompilerCallbackFn)(CompilerDriver& driver);
- typedef MutexLock* (*CompilerMutexLockFn)(CompilerDriver& driver);
-
- void* compiler_context_;
-
CompiledMethodStorage compiled_method_storage_;
- // Info for profile guided compilation.
- const ProfileCompilationInfo* const profile_compilation_info_;
-
size_t max_arena_alloc_;
// Compiler for dex to dex (quickening).
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 6b0e456..685cde3 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -45,8 +45,8 @@
dex_files_for_oat_file_(),
image_classes_(),
boot_image_(false),
- core_image_(false),
app_image_(false),
+ compiling_with_core_image_(false),
baseline_(false),
debuggable_(false),
generate_debug_info_(kDefaultGenerateDebugInfo),
@@ -60,6 +60,7 @@
dump_pass_timings_(false),
dump_stats_(false),
top_k_profile_threshold_(kDefaultTopKProfileThreshold),
+ profile_compilation_info_(nullptr),
verbose_methods_(),
abort_on_hard_verifier_failure_(false),
abort_on_soft_verifier_failure_(false),
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 4a6bbfa..2f4e542 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -42,6 +42,7 @@
class DexFile;
enum class InstructionSet;
class InstructionSetFeatures;
+class ProfileCompilationInfo;
class CompilerOptions final {
public:
@@ -197,13 +198,6 @@
return baseline_;
}
- // Are we compiling a core image (small boot image only used for ART testing)?
- bool IsCoreImage() const {
- // Ensure that `core_image_` => `boot_image_`.
- DCHECK(!core_image_ || boot_image_);
- return core_image_;
- }
-
// Are we compiling an app image?
bool IsAppImage() const {
return app_image_;
@@ -213,11 +207,22 @@
app_image_ = false;
}
+ // Returns whether we are compiling against a "core" image, which
+ // is an indicative we are running tests. The compiler will use that
+ // information for checking invariants.
+ bool CompilingWithCoreImage() const {
+ return compiling_with_core_image_;
+ }
+
// Should the code be compiled as position independent?
bool GetCompilePic() const {
return compile_pic_;
}
+ const ProfileCompilationInfo* GetProfileCompilationInfo() const {
+ return profile_compilation_info_;
+ }
+
bool HasVerboseMethods() const {
return !verbose_methods_.empty();
}
@@ -352,8 +357,8 @@
HashSet<std::string> image_classes_;
bool boot_image_;
- bool core_image_;
bool app_image_;
+ bool compiling_with_core_image_;
bool baseline_;
bool debuggable_;
bool generate_debug_info_;
@@ -370,6 +375,9 @@
// When using a profile file only the top K% of the profiled samples will be compiled.
double top_k_profile_threshold_;
+ // Info for profile guided compilation.
+ const ProfileCompilationInfo* profile_compilation_info_;
+
// Vector of methods to have verbose output enabled for.
std::vector<std::string> verbose_methods_;
diff --git a/compiler/driver/compiler_options_map.def b/compiler/driver/compiler_options_map.def
index 1ec34ec..a593240 100644
--- a/compiler/driver/compiler_options_map.def
+++ b/compiler/driver/compiler_options_map.def
@@ -52,7 +52,7 @@
COMPILER_OPTIONS_KEY (double, TopKProfileThreshold)
COMPILER_OPTIONS_KEY (bool, AbortOnHardVerifierFailure)
COMPILER_OPTIONS_KEY (bool, AbortOnSoftVerifierFailure)
-COMPILER_OPTIONS_KEY (bool, ResolveStartupConstStrings, kIsDebugBuild)
+COMPILER_OPTIONS_KEY (bool, ResolveStartupConstStrings, false)
COMPILER_OPTIONS_KEY (std::string, DumpInitFailures)
COMPILER_OPTIONS_KEY (std::string, DumpCFG)
COMPILER_OPTIONS_KEY (Unit, DumpCFGAppend)
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index bc8641a..bb35065 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -90,10 +90,11 @@
// Special case max code units for inlining, whose default is "unset" (implictly
// meaning no limit). Do this before parsing the actual passed options.
compiler_options_->SetInlineMaxCodeUnits(CompilerOptions::kDefaultInlineMaxCodeUnits);
+ Runtime* runtime = Runtime::Current();
{
std::string error_msg;
- if (!compiler_options_->ParseCompilerOptions(Runtime::Current()->GetCompilerOptions(),
- true /* ignore_unrecognized */,
+ if (!compiler_options_->ParseCompilerOptions(runtime->GetCompilerOptions(),
+ /*ignore_unrecognized=*/ true,
&error_msg)) {
LOG(FATAL) << error_msg;
UNREACHABLE();
@@ -103,7 +104,7 @@
compiler_options_->SetNonPic();
// Set debuggability based on the runtime value.
- compiler_options_->SetDebuggable(Runtime::Current()->IsJavaDebuggable());
+ compiler_options_->SetDebuggable(runtime->IsJavaDebuggable());
const InstructionSet instruction_set = compiler_options_->GetInstructionSet();
if (kRuntimeISA == InstructionSet::kArm) {
@@ -112,7 +113,7 @@
DCHECK_EQ(instruction_set, kRuntimeISA);
}
std::unique_ptr<const InstructionSetFeatures> instruction_set_features;
- for (const StringPiece option : Runtime::Current()->GetCompilerOptions()) {
+ for (const StringPiece option : runtime->GetCompilerOptions()) {
VLOG(compiler) << "JIT compiler option " << option;
std::string error_msg;
if (option.starts_with("--instruction-set-variant=")) {
@@ -144,6 +145,8 @@
instruction_set_features = InstructionSetFeatures::FromCppDefines();
}
compiler_options_->instruction_set_features_ = std::move(instruction_set_features);
+ compiler_options_->compiling_with_core_image_ =
+ CompilerDriver::IsCoreImageFilename(runtime->GetImageLocation());
compiler_driver_.reset(new CompilerDriver(
compiler_options_.get(),
@@ -151,8 +154,7 @@
Compiler::kOptimizing,
/* image_classes */ nullptr,
/* thread_count */ 1,
- /* swap_fd */ -1,
- /* profile_compilation_info */ nullptr));
+ /* swap_fd */ -1));
// Disable dedupe so we can remove compiled methods.
compiler_driver_->SetDedupeEnabled(false);
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index a9acf90..48b50ea 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -2319,9 +2319,10 @@
if (offset >= kReferenceLoadMinFarOffset) {
locations->AddTemp(FixedTempLocation());
}
- } else {
+ } else if (!instruction->GetArray()->IsIntermediateAddress()) {
// We need a non-scratch temporary for the array data pointer in
- // CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier().
+ // CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier() for the case with no
+ // intermediate address.
locations->AddTemp(Location::RequiresRegister());
}
}
@@ -2351,11 +2352,12 @@
MacroAssembler* masm = GetVIXLAssembler();
UseScratchRegisterScope temps(masm);
- // The read barrier instrumentation of object ArrayGet instructions
+ // The non-Baker read barrier instrumentation of object ArrayGet instructions
// does not support the HIntermediateAddress instruction.
DCHECK(!((type == DataType::Type::kReference) &&
instruction->GetArray()->IsIntermediateAddress() &&
- kEmitCompilerReadBarrier));
+ kEmitCompilerReadBarrier &&
+ !kUseBakerReadBarrier));
if (type == DataType::Type::kReference && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
// Object ArrayGet with Baker's read barrier case.
@@ -2363,6 +2365,7 @@
// CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier call.
DCHECK(!instruction->CanDoImplicitNullCheckOn(instruction->InputAt(0)));
if (index.IsConstant()) {
+ DCHECK(!instruction->GetArray()->IsIntermediateAddress());
// Array load with a constant index can be treated as a field load.
offset += Int64FromLocation(index) << DataType::SizeShift(type);
Location maybe_temp =
@@ -2375,9 +2378,8 @@
/* needs_null_check */ false,
/* use_load_acquire */ false);
} else {
- Register temp = WRegisterFrom(locations->GetTemp(0));
codegen_->GenerateArrayLoadWithBakerReadBarrier(
- out, obj.W(), offset, index, temp, /* needs_null_check */ false);
+ instruction, out, obj.W(), offset, index, /* needs_null_check */ false);
}
} else {
// General case.
@@ -2426,8 +2428,8 @@
// input instruction has done it already. See the comment in
// `TryExtractArrayAccessAddress()`.
if (kIsDebugBuild) {
- HIntermediateAddress* tmp = instruction->GetArray()->AsIntermediateAddress();
- DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), offset);
+ HIntermediateAddress* interm_addr = instruction->GetArray()->AsIntermediateAddress();
+ DCHECK_EQ(interm_addr->GetOffset()->AsIntConstant()->GetValueAsUint64(), offset);
}
temp = obj;
} else {
@@ -2539,8 +2541,8 @@
// input instruction has done it already. See the comment in
// `TryExtractArrayAccessAddress()`.
if (kIsDebugBuild) {
- HIntermediateAddress* tmp = instruction->GetArray()->AsIntermediateAddress();
- DCHECK(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64() == offset);
+ HIntermediateAddress* interm_addr = instruction->GetArray()->AsIntermediateAddress();
+ DCHECK(interm_addr->GetOffset()->AsIntConstant()->GetValueAsUint64() == offset);
}
temp = array;
} else {
@@ -5957,11 +5959,11 @@
instruction, ref, obj, src, needs_null_check, use_load_acquire);
}
-void CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier(Location ref,
+void CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier(HArrayGet* instruction,
+ Location ref,
Register obj,
uint32_t data_offset,
Location index,
- Register temp,
bool needs_null_check) {
DCHECK(kEmitCompilerReadBarrier);
DCHECK(kUseBakerReadBarrier);
@@ -6000,9 +6002,24 @@
DCHECK(temps.IsAvailable(ip0));
DCHECK(temps.IsAvailable(ip1));
temps.Exclude(ip0, ip1);
+
+ Register temp;
+ if (instruction->GetArray()->IsIntermediateAddress()) {
+ // We do not need to compute the intermediate address from the array: the
+ // input instruction has done it already. See the comment in
+ // `TryExtractArrayAccessAddress()`.
+ if (kIsDebugBuild) {
+ HIntermediateAddress* interm_addr = instruction->GetArray()->AsIntermediateAddress();
+ DCHECK_EQ(interm_addr->GetOffset()->AsIntConstant()->GetValueAsUint64(), data_offset);
+ }
+ temp = obj;
+ } else {
+ temp = WRegisterFrom(instruction->GetLocations()->GetTemp(0));
+ __ Add(temp.X(), obj.X(), Operand(data_offset));
+ }
+
uint32_t custom_data = EncodeBakerReadBarrierArrayData(temp.GetCode());
- __ Add(temp.X(), obj.X(), Operand(data_offset));
{
ExactAssemblyScope guard(GetVIXLAssembler(),
(kPoisonHeapReferences ? 4u : 3u) * vixl::aarch64::kInstructionSize);
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 1ba58b1..ada5742 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -694,11 +694,11 @@
bool use_load_acquire);
// Fast path implementation of ReadBarrier::Barrier for a heap
// reference array load when Baker's read barriers are used.
- void GenerateArrayLoadWithBakerReadBarrier(Location ref,
+ void GenerateArrayLoadWithBakerReadBarrier(HArrayGet* instruction,
+ Location ref,
vixl::aarch64::Register obj,
uint32_t data_offset,
Location index,
- vixl::aarch64::Register temp,
bool needs_null_check);
// Emit code checking the status of the Marking Register, and
diff --git a/compiler/optimizing/code_generator_vector_arm64.cc b/compiler/optimizing/code_generator_vector_arm64.cc
index e79a96b..5a18c1f 100644
--- a/compiler/optimizing/code_generator_vector_arm64.cc
+++ b/compiler/optimizing/code_generator_vector_arm64.cc
@@ -216,7 +216,7 @@
switch (instruction->GetPackedType()) {
case DataType::Type::kInt32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- switch (instruction->GetKind()) {
+ switch (instruction->GetReductionKind()) {
case HVecReduce::kSum:
__ Addv(dst.S(), src.V4S());
break;
@@ -230,7 +230,7 @@
break;
case DataType::Type::kInt64:
DCHECK_EQ(2u, instruction->GetVectorLength());
- switch (instruction->GetKind()) {
+ switch (instruction->GetReductionKind()) {
case HVecReduce::kSum:
__ Addp(dst.D(), src.V2D());
break;
diff --git a/compiler/optimizing/code_generator_vector_arm_vixl.cc b/compiler/optimizing/code_generator_vector_arm_vixl.cc
index 62b6c4e..b092961 100644
--- a/compiler/optimizing/code_generator_vector_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_vector_arm_vixl.cc
@@ -138,7 +138,7 @@
switch (instruction->GetPackedType()) {
case DataType::Type::kInt32:
DCHECK_EQ(2u, instruction->GetVectorLength());
- switch (instruction->GetKind()) {
+ switch (instruction->GetReductionKind()) {
case HVecReduce::kSum:
__ Vpadd(DataTypeValue::I32, dst, src, src);
break;
diff --git a/compiler/optimizing/code_generator_vector_mips.cc b/compiler/optimizing/code_generator_vector_mips.cc
index 24f4fb2..09e96cc 100644
--- a/compiler/optimizing/code_generator_vector_mips.cc
+++ b/compiler/optimizing/code_generator_vector_mips.cc
@@ -187,7 +187,7 @@
switch (instruction->GetPackedType()) {
case DataType::Type::kInt32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- switch (instruction->GetKind()) {
+ switch (instruction->GetReductionKind()) {
case HVecReduce::kSum:
__ Hadd_sD(tmp, src, src);
__ IlvlD(dst, tmp, tmp);
@@ -209,7 +209,7 @@
break;
case DataType::Type::kInt64:
DCHECK_EQ(2u, instruction->GetVectorLength());
- switch (instruction->GetKind()) {
+ switch (instruction->GetReductionKind()) {
case HVecReduce::kSum:
__ IlvlD(dst, src, src);
__ AddvD(dst, dst, src);
diff --git a/compiler/optimizing/code_generator_vector_mips64.cc b/compiler/optimizing/code_generator_vector_mips64.cc
index 972c49e..b6873b1 100644
--- a/compiler/optimizing/code_generator_vector_mips64.cc
+++ b/compiler/optimizing/code_generator_vector_mips64.cc
@@ -185,7 +185,7 @@
switch (instruction->GetPackedType()) {
case DataType::Type::kInt32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- switch (instruction->GetKind()) {
+ switch (instruction->GetReductionKind()) {
case HVecReduce::kSum:
__ Hadd_sD(tmp, src, src);
__ IlvlD(dst, tmp, tmp);
@@ -207,7 +207,7 @@
break;
case DataType::Type::kInt64:
DCHECK_EQ(2u, instruction->GetVectorLength());
- switch (instruction->GetKind()) {
+ switch (instruction->GetReductionKind()) {
case HVecReduce::kSum:
__ IlvlD(dst, src, src);
__ AddvD(dst, dst, src);
diff --git a/compiler/optimizing/code_generator_vector_x86.cc b/compiler/optimizing/code_generator_vector_x86.cc
index c52ecc7..0ee0035 100644
--- a/compiler/optimizing/code_generator_vector_x86.cc
+++ b/compiler/optimizing/code_generator_vector_x86.cc
@@ -205,8 +205,8 @@
CreateVecUnOpLocations(GetGraph()->GetAllocator(), instruction);
// Long reduction or min/max require a temporary.
if (instruction->GetPackedType() == DataType::Type::kInt64 ||
- instruction->GetKind() == HVecReduce::kMin ||
- instruction->GetKind() == HVecReduce::kMax) {
+ instruction->GetReductionKind() == HVecReduce::kMin ||
+ instruction->GetReductionKind() == HVecReduce::kMax) {
instruction->GetLocations()->AddTemp(Location::RequiresFpuRegister());
}
}
@@ -218,38 +218,23 @@
switch (instruction->GetPackedType()) {
case DataType::Type::kInt32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- switch (instruction->GetKind()) {
+ switch (instruction->GetReductionKind()) {
case HVecReduce::kSum:
__ movaps(dst, src);
__ phaddd(dst, dst);
__ phaddd(dst, dst);
break;
- case HVecReduce::kMin: {
- XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
- __ movaps(tmp, src);
- __ movaps(dst, src);
- __ psrldq(tmp, Immediate(8));
- __ pminsd(dst, tmp);
- __ psrldq(tmp, Immediate(4));
- __ pminsd(dst, tmp);
- break;
- }
- case HVecReduce::kMax: {
- XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
- __ movaps(tmp, src);
- __ movaps(dst, src);
- __ psrldq(tmp, Immediate(8));
- __ pmaxsd(dst, tmp);
- __ psrldq(tmp, Immediate(4));
- __ pmaxsd(dst, tmp);
- break;
- }
+ case HVecReduce::kMin:
+ case HVecReduce::kMax:
+ // Historical note: We've had a broken implementation here. b/117863065
+ // Do not draw on the old code if we ever want to bring MIN/MAX reduction back.
+ LOG(FATAL) << "Unsupported reduction type.";
}
break;
case DataType::Type::kInt64: {
DCHECK_EQ(2u, instruction->GetVectorLength());
XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
- switch (instruction->GetKind()) {
+ switch (instruction->GetReductionKind()) {
case HVecReduce::kSum:
__ movaps(tmp, src);
__ movaps(dst, src);
diff --git a/compiler/optimizing/code_generator_vector_x86_64.cc b/compiler/optimizing/code_generator_vector_x86_64.cc
index 87d0106..9c28827 100644
--- a/compiler/optimizing/code_generator_vector_x86_64.cc
+++ b/compiler/optimizing/code_generator_vector_x86_64.cc
@@ -188,8 +188,8 @@
CreateVecUnOpLocations(GetGraph()->GetAllocator(), instruction);
// Long reduction or min/max require a temporary.
if (instruction->GetPackedType() == DataType::Type::kInt64 ||
- instruction->GetKind() == HVecReduce::kMin ||
- instruction->GetKind() == HVecReduce::kMax) {
+ instruction->GetReductionKind() == HVecReduce::kMin ||
+ instruction->GetReductionKind() == HVecReduce::kMax) {
instruction->GetLocations()->AddTemp(Location::RequiresFpuRegister());
}
}
@@ -201,38 +201,23 @@
switch (instruction->GetPackedType()) {
case DataType::Type::kInt32:
DCHECK_EQ(4u, instruction->GetVectorLength());
- switch (instruction->GetKind()) {
+ switch (instruction->GetReductionKind()) {
case HVecReduce::kSum:
__ movaps(dst, src);
__ phaddd(dst, dst);
__ phaddd(dst, dst);
break;
- case HVecReduce::kMin: {
- XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
- __ movaps(tmp, src);
- __ movaps(dst, src);
- __ psrldq(tmp, Immediate(8));
- __ pminsd(dst, tmp);
- __ psrldq(tmp, Immediate(4));
- __ pminsd(dst, tmp);
- break;
- }
- case HVecReduce::kMax: {
- XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
- __ movaps(tmp, src);
- __ movaps(dst, src);
- __ psrldq(tmp, Immediate(8));
- __ pmaxsd(dst, tmp);
- __ psrldq(tmp, Immediate(4));
- __ pmaxsd(dst, tmp);
- break;
- }
+ case HVecReduce::kMin:
+ case HVecReduce::kMax:
+ // Historical note: We've had a broken implementation here. b/117863065
+ // Do not draw on the old code if we ever want to bring MIN/MAX reduction back.
+ LOG(FATAL) << "Unsupported reduction type.";
}
break;
case DataType::Type::kInt64: {
DCHECK_EQ(2u, instruction->GetVectorLength());
XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
- switch (instruction->GetKind()) {
+ switch (instruction->GetReductionKind()) {
case HVecReduce::kSum:
__ movaps(tmp, src);
__ movaps(dst, src);
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 9f34a51..fba4da6 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -3497,6 +3497,27 @@
}
}
+void InstructionCodeGeneratorX86::RemByPowerOfTwo(HRem* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ Location second = locations->InAt(1);
+
+ Register out = locations->Out().AsRegister<Register>();
+ Register numerator = locations->InAt(0).AsRegister<Register>();
+
+ int32_t imm = Int64FromConstant(second.GetConstant());
+ DCHECK(IsPowerOfTwo(AbsOrMin(imm)));
+ uint32_t abs_imm = static_cast<uint32_t>(AbsOrMin(imm));
+
+ Register tmp = locations->GetTemp(0).AsRegister<Register>();
+ NearLabel done;
+ __ movl(out, numerator);
+ __ andl(out, Immediate(abs_imm-1));
+ __ j(Condition::kZero, &done);
+ __ leal(tmp, Address(out, static_cast<int32_t>(~(abs_imm-1))));
+ __ testl(numerator, numerator);
+ __ cmovl(Condition::kLess, out, tmp);
+ __ Bind(&done);
+}
void InstructionCodeGeneratorX86::DivByPowerOfTwo(HDiv* instruction) {
LocationSummary* locations = instruction->GetLocations();
@@ -3610,8 +3631,12 @@
// Do not generate anything for 0. DivZeroCheck would forbid any generated code.
} else if (imm == 1 || imm == -1) {
DivRemOneOrMinusOne(instruction);
- } else if (is_div && IsPowerOfTwo(AbsOrMin(imm))) {
- DivByPowerOfTwo(instruction->AsDiv());
+ } else if (IsPowerOfTwo(AbsOrMin(imm))) {
+ if (is_div) {
+ DivByPowerOfTwo(instruction->AsDiv());
+ } else {
+ RemByPowerOfTwo(instruction->AsRem());
+ }
} else {
DCHECK(imm <= -2 || imm >= 2);
GenerateDivRemWithAnyConstant(instruction);
@@ -7424,6 +7449,61 @@
}
}
+void LocationsBuilderX86::VisitX86AndNot(HX86AndNot* instruction) {
+ DCHECK(codegen_->GetInstructionSetFeatures().HasAVX2());
+ DCHECK(DataType::IsIntOrLongType(instruction->GetType())) << instruction->GetType();
+ LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorX86::VisitX86AndNot(HX86AndNot* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ Location first = locations->InAt(0);
+ Location second = locations->InAt(1);
+ Location dest = locations->Out();
+ if (instruction->GetResultType() == DataType::Type::kInt32) {
+ __ andn(dest.AsRegister<Register>(),
+ first.AsRegister<Register>(),
+ second.AsRegister<Register>());
+ } else {
+ DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt64);
+ __ andn(dest.AsRegisterPairLow<Register>(),
+ first.AsRegisterPairLow<Register>(),
+ second.AsRegisterPairLow<Register>());
+ __ andn(dest.AsRegisterPairHigh<Register>(),
+ first.AsRegisterPairHigh<Register>(),
+ second.AsRegisterPairHigh<Register>());
+ }
+}
+
+void LocationsBuilderX86::VisitX86MaskOrResetLeastSetBit(HX86MaskOrResetLeastSetBit* instruction) {
+ DCHECK(codegen_->GetInstructionSetFeatures().HasAVX2());
+ DCHECK(instruction->GetType() == DataType::Type::kInt32) << instruction->GetType();
+ LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorX86::VisitX86MaskOrResetLeastSetBit(
+ HX86MaskOrResetLeastSetBit* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ Location src = locations->InAt(0);
+ Location dest = locations->Out();
+ DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
+ switch (instruction->GetOpKind()) {
+ case HInstruction::kAnd:
+ __ blsr(dest.AsRegister<Register>(), src.AsRegister<Register>());
+ break;
+ case HInstruction::kXor:
+ __ blsmsk(dest.AsRegister<Register>(), src.AsRegister<Register>());
+ break;
+ default:
+ LOG(FATAL) << "Unreachable";
+ }
+}
+
void LocationsBuilderX86::VisitAnd(HAnd* instruction) { HandleBitwiseOperation(instruction); }
void LocationsBuilderX86::VisitOr(HOr* instruction) { HandleBitwiseOperation(instruction); }
void LocationsBuilderX86::VisitXor(HXor* instruction) { HandleBitwiseOperation(instruction); }
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 93b0461..deeef88 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -159,6 +159,7 @@
FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
FOR_EACH_CONCRETE_INSTRUCTION_X86(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_X86_COMMON(DECLARE_VISIT_INSTRUCTION)
#undef DECLARE_VISIT_INSTRUCTION
@@ -190,6 +191,7 @@
FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
FOR_EACH_CONCRETE_INSTRUCTION_X86(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_X86_COMMON(DECLARE_VISIT_INSTRUCTION)
#undef DECLARE_VISIT_INSTRUCTION
@@ -216,6 +218,7 @@
void GenerateDivRemIntegral(HBinaryOperation* instruction);
void DivRemOneOrMinusOne(HBinaryOperation* instruction);
void DivByPowerOfTwo(HDiv* instruction);
+ void RemByPowerOfTwo(HRem* instruction);
void GenerateDivRemWithAnyConstant(HBinaryOperation* instruction);
void GenerateRemFP(HRem* rem);
void HandleCondition(HCondition* condition);
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index dac2dba..14cff05 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -3560,7 +3560,40 @@
LOG(FATAL) << "Unexpected type for div by (-)1 " << instruction->GetResultType();
}
}
+void InstructionCodeGeneratorX86_64::RemByPowerOfTwo(HRem* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ Location second = locations->InAt(1);
+ CpuRegister out = locations->Out().AsRegister<CpuRegister>();
+ CpuRegister numerator = locations->InAt(0).AsRegister<CpuRegister>();
+ int64_t imm = Int64FromConstant(second.GetConstant());
+ DCHECK(IsPowerOfTwo(AbsOrMin(imm)));
+ uint64_t abs_imm = AbsOrMin(imm);
+ CpuRegister tmp = locations->GetTemp(0).AsRegister<CpuRegister>();
+ if (instruction->GetResultType() == DataType::Type::kInt32) {
+ NearLabel done;
+ __ movl(out, numerator);
+ __ andl(out, Immediate(abs_imm-1));
+ __ j(Condition::kZero, &done);
+ __ leal(tmp, Address(out, static_cast<int32_t>(~(abs_imm-1))));
+ __ testl(numerator, numerator);
+ __ cmov(Condition::kLess, out, tmp, false);
+ __ Bind(&done);
+ } else {
+ DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt64);
+ codegen_->Load64BitValue(tmp, abs_imm - 1);
+ NearLabel done;
+
+ __ movq(out, numerator);
+ __ andq(out, tmp);
+ __ j(Condition::kZero, &done);
+ __ movq(tmp, numerator);
+ __ sarq(tmp, Immediate(63));
+ __ shlq(tmp, Immediate(WhichPowerOf2(abs_imm)));
+ __ orq(out, tmp);
+ __ Bind(&done);
+ }
+}
void InstructionCodeGeneratorX86_64::DivByPowerOfTwo(HDiv* instruction) {
LocationSummary* locations = instruction->GetLocations();
Location second = locations->InAt(1);
@@ -3737,8 +3770,12 @@
// Do not generate anything. DivZeroCheck would prevent any code to be executed.
} else if (imm == 1 || imm == -1) {
DivRemOneOrMinusOne(instruction);
- } else if (instruction->IsDiv() && IsPowerOfTwo(AbsOrMin(imm))) {
- DivByPowerOfTwo(instruction->AsDiv());
+ } else if (IsPowerOfTwo(AbsOrMin(imm))) {
+ if (is_div) {
+ DivByPowerOfTwo(instruction->AsDiv());
+ } else {
+ RemByPowerOfTwo(instruction->AsRem());
+ }
} else {
DCHECK(imm <= -2 || imm >= 2);
GenerateDivRemWithAnyConstant(instruction);
@@ -6734,6 +6771,48 @@
}
}
+void LocationsBuilderX86_64::VisitX86AndNot(HX86AndNot* instruction) {
+ DCHECK(codegen_->GetInstructionSetFeatures().HasAVX2());
+ DCHECK(DataType::IsIntOrLongType(instruction->GetType())) << instruction->GetType();
+ LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
+ locations->SetInAt(0, Location::RequiresRegister());
+ // There is no immediate variant of negated bitwise and in X86.
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void LocationsBuilderX86_64::VisitX86MaskOrResetLeastSetBit(HX86MaskOrResetLeastSetBit* instruction) {
+ DCHECK(codegen_->GetInstructionSetFeatures().HasAVX2());
+ DCHECK(DataType::IsIntOrLongType(instruction->GetType())) << instruction->GetType();
+ LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorX86_64::VisitX86AndNot(HX86AndNot* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ Location first = locations->InAt(0);
+ Location second = locations->InAt(1);
+ Location dest = locations->Out();
+ __ andn(dest.AsRegister<CpuRegister>(), first.AsRegister<CpuRegister>(), second.AsRegister<CpuRegister>());
+}
+
+void InstructionCodeGeneratorX86_64::VisitX86MaskOrResetLeastSetBit(HX86MaskOrResetLeastSetBit* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ Location src = locations->InAt(0);
+ Location dest = locations->Out();
+ switch (instruction->GetOpKind()) {
+ case HInstruction::kAnd:
+ __ blsr(dest.AsRegister<CpuRegister>(), src.AsRegister<CpuRegister>());
+ break;
+ case HInstruction::kXor:
+ __ blsmsk(dest.AsRegister<CpuRegister>(), src.AsRegister<CpuRegister>());
+ break;
+ default:
+ LOG(FATAL) << "Unreachable";
+ }
+}
+
void LocationsBuilderX86_64::VisitAnd(HAnd* instruction) { HandleBitwiseOperation(instruction); }
void LocationsBuilderX86_64::VisitOr(HOr* instruction) { HandleBitwiseOperation(instruction); }
void LocationsBuilderX86_64::VisitXor(HXor* instruction) { HandleBitwiseOperation(instruction); }
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 1e71397..f74e130 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -161,6 +161,7 @@
FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
FOR_EACH_CONCRETE_INSTRUCTION_X86_64(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_X86_COMMON(DECLARE_VISIT_INSTRUCTION)
#undef DECLARE_VISIT_INSTRUCTION
@@ -192,6 +193,7 @@
FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
FOR_EACH_CONCRETE_INSTRUCTION_X86_64(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_X86_COMMON(DECLARE_VISIT_INSTRUCTION)
#undef DECLARE_VISIT_INSTRUCTION
@@ -213,6 +215,7 @@
void GenerateRemFP(HRem* rem);
void DivRemOneOrMinusOne(HBinaryOperation* instruction);
void DivByPowerOfTwo(HDiv* instruction);
+ void RemByPowerOfTwo(HRem* instruction);
void GenerateDivRemWithAnyConstant(HBinaryOperation* instruction);
void GenerateDivRemIntegral(HBinaryOperation* instruction);
void HandleCondition(HCondition* condition);
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index f56f9cb..45d9167 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -39,7 +39,6 @@
#include "mirror/object_array-alloc-inl.h"
#include "mirror/object_array-inl.h"
#include "nodes.h"
-#include "optimizing_compiler.h"
#include "reference_type_propagation.h"
#include "register_allocator_linear_scan.h"
#include "scoped_thread_state_change-inl.h"
@@ -151,13 +150,13 @@
// If we're compiling with a core image (which is only used for
// test purposes), honor inlining directives in method names:
- // - if a method's name contains the substring "$inline$", ensure
- // that this method is actually inlined;
// - if a method's name contains the substring "$noinline$", do not
- // inline that method.
+ // inline that method;
+ // - if a method's name contains the substring "$inline$", ensure
+ // that this method is actually inlined.
// We limit the latter to AOT compilation, as the JIT may or may not inline
// depending on the state of classes at runtime.
- const bool honor_noinline_directives = IsCompilingWithCoreImage();
+ const bool honor_noinline_directives = codegen_->GetCompilerOptions().CompilingWithCoreImage();
const bool honor_inline_directives =
honor_noinline_directives && Runtime::Current()->IsAotCompiler();
@@ -680,7 +679,7 @@
/*out*/Handle<mirror::ObjectArray<mirror::Class>>* inline_cache)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(Runtime::Current()->IsAotCompiler());
- const ProfileCompilationInfo* pci = compiler_driver_->GetProfileCompilationInfo();
+ const ProfileCompilationInfo* pci = codegen_->GetCompilerOptions().GetProfileCompilationInfo();
if (pci == nullptr) {
return kInlineCacheNoData;
}
@@ -1419,10 +1418,6 @@
static inline bool MayInline(const CompilerOptions& compiler_options,
const DexFile& inlined_from,
const DexFile& inlined_into) {
- if (kIsTargetBuild) {
- return true;
- }
-
// We're not allowed to inline across dex files if we're the no-inline-from dex file.
if (!IsSameDexFile(inlined_from, inlined_into) &&
ContainsElement(compiler_options.GetNoInlineFromDexFile(), &inlined_from)) {
@@ -1647,7 +1642,7 @@
}
}
if (needs_constructor_barrier) {
- // See CompilerDriver::RequiresConstructorBarrier for more details.
+ // See DexCompilationUnit::RequiresConstructorBarrier for more details.
DCHECK(obj != nullptr) << "only non-static methods can have a constructor fence";
HConstructorFence* constructor_fence =
@@ -1737,6 +1732,21 @@
return (object != hint.Get()) ? handles->NewHandle(object) : hint;
}
+static bool CanEncodeInlinedMethodInStackMap(const DexFile& caller_dex_file, ArtMethod* callee)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!Runtime::Current()->IsAotCompiler()) {
+ // JIT can always encode methods in stack maps.
+ return true;
+ }
+ if (IsSameDexFile(caller_dex_file, *callee->GetDexFile())) {
+ return true;
+ }
+ // TODO(ngeoffray): Support more AOT cases for inlining:
+ // - methods in multidex
+ // - methods in boot image for on-device non-PIC compilation.
+ return false;
+}
+
bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction,
ArtMethod* resolved_method,
ReferenceTypeInfo receiver_type,
diff --git a/compiler/optimizing/instruction_simplifier_arm.cc b/compiler/optimizing/instruction_simplifier_arm.cc
index 24fbb6c..f968c19 100644
--- a/compiler/optimizing/instruction_simplifier_arm.cc
+++ b/compiler/optimizing/instruction_simplifier_arm.cc
@@ -202,6 +202,11 @@
return;
}
+ // TODO: Support intermediate address for object arrays on arm.
+ if (type == DataType::Type::kReference) {
+ return;
+ }
+
if (type == DataType::Type::kInt64
|| type == DataType::Type::kFloat32
|| type == DataType::Type::kFloat64) {
diff --git a/compiler/optimizing/instruction_simplifier_shared.cc b/compiler/optimizing/instruction_simplifier_shared.cc
index ccdcb35..0f30f66 100644
--- a/compiler/optimizing/instruction_simplifier_shared.cc
+++ b/compiler/optimizing/instruction_simplifier_shared.cc
@@ -245,11 +245,11 @@
return false;
}
if (kEmitCompilerReadBarrier &&
+ !kUseBakerReadBarrier &&
access->IsArrayGet() &&
access->GetType() == DataType::Type::kReference) {
- // For object arrays, the read barrier instrumentation requires
+ // For object arrays, the non-Baker read barrier instrumentation requires
// the original array pointer.
- // TODO: This can be relaxed for Baker CC.
return false;
}
diff --git a/compiler/optimizing/instruction_simplifier_x86.cc b/compiler/optimizing/instruction_simplifier_x86.cc
new file mode 100644
index 0000000..2d8f94a
--- /dev/null
+++ b/compiler/optimizing/instruction_simplifier_x86.cc
@@ -0,0 +1,88 @@
+/* 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.
+ */
+
+#include "instruction_simplifier_x86.h"
+#include "instruction_simplifier_x86_shared.h"
+#include "code_generator_x86.h"
+
+namespace art {
+
+namespace x86 {
+
+class InstructionSimplifierX86Visitor : public HGraphVisitor {
+ public:
+ InstructionSimplifierX86Visitor(HGraph* graph,
+ CodeGenerator* codegen,
+ OptimizingCompilerStats* stats)
+ : HGraphVisitor(graph),
+ codegen_(down_cast<CodeGeneratorX86*>(codegen)),
+ stats_(stats) {}
+
+ void RecordSimplification() {
+ MaybeRecordStat(stats_, MethodCompilationStat::kInstructionSimplificationsArch);
+ }
+
+ bool HasAVX2() {
+ return (codegen_->GetInstructionSetFeatures().HasAVX2());
+ }
+
+ void VisitBasicBlock(HBasicBlock* block) override {
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ HInstruction* instruction = it.Current();
+ if (instruction->IsInBlock()) {
+ instruction->Accept(this);
+ }
+ }
+ }
+
+ void VisitAnd(HAnd * instruction) override;
+ void VisitXor(HXor* instruction) override;
+
+ private:
+ CodeGeneratorX86* codegen_;
+ OptimizingCompilerStats* stats_;
+};
+
+
+void InstructionSimplifierX86Visitor::VisitAnd(HAnd* instruction) {
+ if (TryCombineAndNot(instruction)) {
+ RecordSimplification();
+ } else if (instruction->GetResultType() == DataType::Type::kInt32) {
+ if (TryGenerateResetLeastSetBit(instruction)) {
+ RecordSimplification();
+ }
+ }
+}
+
+void InstructionSimplifierX86Visitor::VisitXor(HXor* instruction) {
+ if (instruction->GetResultType() == DataType::Type::kInt32) {
+ if (TryGenerateMaskUptoLeastSetBit(instruction)) {
+ RecordSimplification();
+ }
+ }
+}
+
+bool InstructionSimplifierX86::Run() {
+ InstructionSimplifierX86Visitor visitor(graph_, codegen_, stats_);
+ if (visitor.HasAVX2()) {
+ visitor.VisitReversePostOrder();
+ return true;
+ }
+ return false;
+}
+
+} // namespace x86
+} // namespace art
+
diff --git a/compiler/optimizing/instruction_simplifier_x86.h b/compiler/optimizing/instruction_simplifier_x86.h
new file mode 100644
index 0000000..6f10006
--- /dev/null
+++ b/compiler/optimizing/instruction_simplifier_x86.h
@@ -0,0 +1,44 @@
+/*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.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_H_
+#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_H_
+
+#include "nodes.h"
+#include "optimization.h"
+
+namespace art {
+
+class CodeGenerator;
+namespace x86 {
+
+class InstructionSimplifierX86 : public HOptimization {
+ public:
+ InstructionSimplifierX86(HGraph* graph, CodeGenerator* codegen, OptimizingCompilerStats* stats)
+ : HOptimization(graph, kInstructionSimplifierX86PassName, stats),
+ codegen_(codegen) {}
+
+ static constexpr const char* kInstructionSimplifierX86PassName = "instruction_simplifier_x86";
+
+ bool Run() override;
+
+ private:
+ CodeGenerator* codegen_;
+};
+
+} // namespace x86
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_H_
diff --git a/compiler/optimizing/instruction_simplifier_x86_64.cc b/compiler/optimizing/instruction_simplifier_x86_64.cc
new file mode 100644
index 0000000..56c6b41
--- /dev/null
+++ b/compiler/optimizing/instruction_simplifier_x86_64.cc
@@ -0,0 +1,82 @@
+/* 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.
+ */
+
+#include "instruction_simplifier_x86_64.h"
+#include "instruction_simplifier_x86_shared.h"
+#include "code_generator_x86_64.h"
+
+namespace art {
+
+namespace x86_64 {
+
+class InstructionSimplifierX86_64Visitor : public HGraphVisitor {
+ public:
+ InstructionSimplifierX86_64Visitor(HGraph* graph,
+ CodeGenerator* codegen,
+ OptimizingCompilerStats* stats)
+ : HGraphVisitor(graph),
+ codegen_(down_cast<CodeGeneratorX86_64*>(codegen)),
+ stats_(stats) {}
+
+ void RecordSimplification() {
+ MaybeRecordStat(stats_, MethodCompilationStat::kInstructionSimplificationsArch);
+ }
+
+ bool HasAVX2() {
+ return codegen_->GetInstructionSetFeatures().HasAVX2();
+ }
+
+ void VisitBasicBlock(HBasicBlock* block) override {
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ HInstruction* instruction = it.Current();
+ if (instruction->IsInBlock()) {
+ instruction->Accept(this);
+ }
+ }
+ }
+
+ void VisitAnd(HAnd* instruction) override;
+ void VisitXor(HXor* instruction) override;
+
+ private:
+ CodeGeneratorX86_64* codegen_;
+ OptimizingCompilerStats* stats_;
+};
+
+void InstructionSimplifierX86_64Visitor::VisitAnd(HAnd* instruction) {
+ if (TryCombineAndNot(instruction)) {
+ RecordSimplification();
+ } else if (TryGenerateResetLeastSetBit(instruction)) {
+ RecordSimplification();
+ }
+}
+
+
+void InstructionSimplifierX86_64Visitor::VisitXor(HXor* instruction) {
+ if (TryGenerateMaskUptoLeastSetBit(instruction)) {
+ RecordSimplification();
+ }
+}
+
+bool InstructionSimplifierX86_64::Run() {
+ InstructionSimplifierX86_64Visitor visitor(graph_, codegen_, stats_);
+ if (visitor.HasAVX2()) {
+ visitor.VisitReversePostOrder();
+ return true;
+ }
+ return false;
+}
+} // namespace x86_64
+} // namespace art
diff --git a/compiler/optimizing/instruction_simplifier_x86_64.h b/compiler/optimizing/instruction_simplifier_x86_64.h
new file mode 100644
index 0000000..6cae24d
--- /dev/null
+++ b/compiler/optimizing/instruction_simplifier_x86_64.h
@@ -0,0 +1,48 @@
+/* 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.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_64_H_
+#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_64_H_
+
+#include "nodes.h"
+#include "optimization.h"
+
+namespace art {
+
+class CodeGenerator;
+
+namespace x86_64 {
+
+class InstructionSimplifierX86_64 : public HOptimization {
+ public:
+ InstructionSimplifierX86_64(HGraph* graph, CodeGenerator* codegen, OptimizingCompilerStats* stats)
+ : HOptimization(graph, kInstructionSimplifierX86_64PassName, stats),
+ codegen_(codegen) {}
+
+ static constexpr const char* kInstructionSimplifierX86_64PassName =
+ "instruction_simplifier_x86_64";
+
+ bool Run() override;
+
+ private:
+ CodeGenerator* codegen_;
+};
+
+} // namespace x86_64
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_64_H_
+
+
diff --git a/compiler/optimizing/instruction_simplifier_x86_shared.cc b/compiler/optimizing/instruction_simplifier_x86_shared.cc
new file mode 100644
index 0000000..2805abb
--- /dev/null
+++ b/compiler/optimizing/instruction_simplifier_x86_shared.cc
@@ -0,0 +1,137 @@
+/* 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.
+ */
+
+#include "instruction_simplifier_x86_shared.h"
+#include "nodes_x86.h"
+
+namespace art {
+
+bool TryCombineAndNot(HAnd* instruction) {
+ DataType::Type type = instruction->GetType();
+ if (!DataType::IsIntOrLongType(type)) {
+ return false;
+ }
+ // Replace code looking like
+ // Not tmp, y
+ // And dst, x, tmp
+ // with
+ // AndNot dst, x, y
+ HInstruction* left = instruction->GetLeft();
+ HInstruction* right = instruction->GetRight();
+ // Perform simplication only when either left or right
+ // is Not. When both are Not, instruction should be simplified with
+ // DeMorgan's Laws.
+ if (left->IsNot() ^ right->IsNot()) {
+ bool left_is_not = left->IsNot();
+ HInstruction* other_ins = (left_is_not ? right : left);
+ HNot* not_ins = (left_is_not ? left : right)->AsNot();
+ // Only do the simplification if instruction has only one use
+ // and thus can be safely removed.
+ if (not_ins->HasOnlyOneNonEnvironmentUse()) {
+ ArenaAllocator* arena = instruction->GetBlock()->GetGraph()->GetAllocator();
+ HX86AndNot* and_not = new (arena) HX86AndNot(type,
+ not_ins->GetInput(),
+ other_ins,
+ instruction->GetDexPc());
+ instruction->GetBlock()->ReplaceAndRemoveInstructionWith(instruction, and_not);
+ DCHECK(!not_ins->HasUses());
+ not_ins->GetBlock()->RemoveInstruction(not_ins);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool TryGenerateResetLeastSetBit(HAnd* instruction) {
+ DataType::Type type = instruction->GetType();
+ if (!DataType::IsIntOrLongType(type)) {
+ return false;
+ }
+ // Replace code looking like
+ // Add tmp, x, -1 or Sub tmp, x, 1
+ // And dest x, tmp
+ // with
+ // MaskOrResetLeastSetBit dest, x
+ HInstruction* candidate = nullptr;
+ HInstruction* other = nullptr;
+ HInstruction* left = instruction->GetLeft();
+ HInstruction* right = instruction->GetRight();
+ if (AreLeastSetBitInputs(left, right)) {
+ candidate = left;
+ other = right;
+ } else if (AreLeastSetBitInputs(right, left)) {
+ candidate = right;
+ other = left;
+ }
+ if (candidate != nullptr && candidate->HasOnlyOneNonEnvironmentUse()) {
+ ArenaAllocator* arena = instruction->GetBlock()->GetGraph()->GetAllocator();
+ HX86MaskOrResetLeastSetBit* lsb = new (arena) HX86MaskOrResetLeastSetBit(
+ type, HInstruction::kAnd, other, instruction->GetDexPc());
+ instruction->GetBlock()->ReplaceAndRemoveInstructionWith(instruction, lsb);
+ DCHECK(!candidate->HasUses());
+ candidate->GetBlock()->RemoveInstruction(candidate);
+ return true;
+ }
+ return false;
+}
+
+bool TryGenerateMaskUptoLeastSetBit(HXor* instruction) {
+ DataType::Type type = instruction->GetType();
+ if (!DataType::IsIntOrLongType(type)) {
+ return false;
+ }
+ // Replace code looking like
+ // Add tmp, x, -1 or Sub tmp, x, 1
+ // Xor dest x, tmp
+ // with
+ // MaskOrResetLeastSetBit dest, x
+ HInstruction* left = instruction->GetLeft();
+ HInstruction* right = instruction->GetRight();
+ HInstruction* other = nullptr;
+ HInstruction* candidate = nullptr;
+ if (AreLeastSetBitInputs(left, right)) {
+ candidate = left;
+ other = right;
+ } else if (AreLeastSetBitInputs(right, left)) {
+ candidate = right;
+ other = left;
+ }
+ if (candidate != nullptr && candidate->HasOnlyOneNonEnvironmentUse()) {
+ ArenaAllocator* arena = instruction->GetBlock()->GetGraph()->GetAllocator();
+ HX86MaskOrResetLeastSetBit* lsb = new (arena) HX86MaskOrResetLeastSetBit(
+ type, HInstruction::kXor, other, instruction->GetDexPc());
+ instruction->GetBlock()->ReplaceAndRemoveInstructionWith(instruction, lsb);
+ DCHECK(!candidate->HasUses());
+ candidate->GetBlock()->RemoveInstruction(candidate);
+ return true;
+ }
+ return false;
+}
+
+bool AreLeastSetBitInputs(HInstruction* to_test, HInstruction* other) {
+ if (to_test->IsAdd()) {
+ HAdd* add = to_test->AsAdd();
+ HConstant* cst = add->GetConstantRight();
+ return cst != nullptr && cst->IsMinusOne() && other == add->GetLeastConstantLeft();
+ }
+ if (to_test->IsSub()) {
+ HSub* sub = to_test->AsSub();
+ HConstant* cst = sub->GetConstantRight();
+ return cst != nullptr && cst->IsOne() && other == sub->GetLeastConstantLeft();
+ }
+ return false;
+}
+
+} // namespace art
diff --git a/compiler/optimizing/instruction_simplifier_x86_shared.h b/compiler/optimizing/instruction_simplifier_x86_shared.h
new file mode 100644
index 0000000..7f94d7e
--- /dev/null
+++ b/compiler/optimizing/instruction_simplifier_x86_shared.h
@@ -0,0 +1,29 @@
+/* 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.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_SHARED_H_
+#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_SHARED_H_
+
+#include "nodes.h"
+
+namespace art {
+bool TryCombineAndNot(HAnd* instruction);
+bool TryGenerateResetLeastSetBit(HAnd* instruction);
+bool TryGenerateMaskUptoLeastSetBit(HXor* instruction);
+bool AreLeastSetBitInputs(HInstruction* to_test, HInstruction* other);
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_SHARED_H_
+
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 7684dc7..6d04b0e 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -2916,6 +2916,40 @@
void IntrinsicCodeGeneratorARM64::VisitReachabilityFence(HInvoke* invoke ATTRIBUTE_UNUSED) { }
+void IntrinsicLocationsBuilderARM64::VisitCRC32Update(HInvoke* invoke) {
+ if (!codegen_->GetInstructionSetFeatures().HasCRC()) {
+ return;
+ }
+
+ LocationSummary* locations = new (allocator_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister());
+}
+
+// Lower the invoke of CRC32.update(int crc, int b).
+void IntrinsicCodeGeneratorARM64::VisitCRC32Update(HInvoke* invoke) {
+ DCHECK(codegen_->GetInstructionSetFeatures().HasCRC());
+
+ MacroAssembler* masm = GetVIXLAssembler();
+
+ Register crc = InputRegisterAt(invoke, 0);
+ Register val = InputRegisterAt(invoke, 1);
+ Register out = OutputRegister(invoke);
+
+ // The general algorithm of the CRC32 calculation is:
+ // crc = ~crc
+ // result = crc32_for_byte(crc, b)
+ // crc = ~result
+ // It is directly lowered to three instructions.
+ __ Mvn(out, crc);
+ __ Crc32b(out, out, val);
+ __ Mvn(out, out);
+}
+
UNIMPLEMENTED_INTRINSIC(ARM64, ReferenceGetReferent)
UNIMPLEMENTED_INTRINSIC(ARM64, StringStringIndexOf);
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index 38e4c89..4d45a99 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -446,7 +446,7 @@
void IntrinsicCodeGeneratorARMVIXL::VisitMathRint(HInvoke* invoke) {
DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions());
ArmVIXLAssembler* assembler = GetAssembler();
- __ Vrintn(F64, F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
+ __ Vrintn(F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
}
void IntrinsicLocationsBuilderARMVIXL::VisitMathRoundFloat(HInvoke* invoke) {
@@ -481,7 +481,7 @@
// Handle input < 0 cases.
// If input is negative but not a tie, previous result (round to nearest) is valid.
// If input is a negative tie, change rounding direction to positive infinity, out_reg += 1.
- __ Vrinta(F32, F32, temp1, in_reg);
+ __ Vrinta(F32, temp1, in_reg);
__ Vmov(temp2, 0.5);
__ Vsub(F32, temp1, in_reg, temp1);
__ Vcmp(F32, temp1, temp2);
@@ -2945,7 +2945,7 @@
void IntrinsicCodeGeneratorARMVIXL::VisitMathCeil(HInvoke* invoke) {
ArmVIXLAssembler* assembler = GetAssembler();
DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions());
- __ Vrintp(F64, F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
+ __ Vrintp(F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
}
void IntrinsicLocationsBuilderARMVIXL::VisitMathFloor(HInvoke* invoke) {
@@ -2957,7 +2957,7 @@
void IntrinsicCodeGeneratorARMVIXL::VisitMathFloor(HInvoke* invoke) {
ArmVIXLAssembler* assembler = GetAssembler();
DCHECK(codegen_->GetInstructionSetFeatures().HasARMv8AInstructions());
- __ Vrintm(F64, F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
+ __ Vrintm(F64, OutputDRegister(invoke), InputDRegisterAt(invoke, 0));
}
void IntrinsicLocationsBuilderARMVIXL::VisitIntegerValueOf(HInvoke* invoke) {
@@ -3059,6 +3059,7 @@
UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeCASLong) // High register pressure.
UNIMPLEMENTED_INTRINSIC(ARMVIXL, SystemArrayCopyChar)
UNIMPLEMENTED_INTRINSIC(ARMVIXL, ReferenceGetReferent)
+UNIMPLEMENTED_INTRINSIC(ARMVIXL, CRC32Update)
UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOf);
UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOfAfter);
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 6f7f5e4..21fb7d7 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -2696,6 +2696,8 @@
UNIMPLEMENTED_INTRINSIC(MIPS, ReferenceGetReferent)
UNIMPLEMENTED_INTRINSIC(MIPS, SystemArrayCopy)
+UNIMPLEMENTED_INTRINSIC(MIPS, CRC32Update)
+
UNIMPLEMENTED_INTRINSIC(MIPS, StringStringIndexOf);
UNIMPLEMENTED_INTRINSIC(MIPS, StringStringIndexOfAfter);
UNIMPLEMENTED_INTRINSIC(MIPS, StringBufferAppend);
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index 2eb2529..4b86f5d 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -2346,6 +2346,7 @@
UNIMPLEMENTED_INTRINSIC(MIPS64, ReferenceGetReferent)
UNIMPLEMENTED_INTRINSIC(MIPS64, SystemArrayCopy)
+UNIMPLEMENTED_INTRINSIC(MIPS64, CRC32Update)
UNIMPLEMENTED_INTRINSIC(MIPS64, StringStringIndexOf);
UNIMPLEMENTED_INTRINSIC(MIPS64, StringStringIndexOfAfter);
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 3504d7a..6dd4681 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -2967,6 +2967,7 @@
UNIMPLEMENTED_INTRINSIC(X86, LongHighestOneBit)
UNIMPLEMENTED_INTRINSIC(X86, IntegerLowestOneBit)
UNIMPLEMENTED_INTRINSIC(X86, LongLowestOneBit)
+UNIMPLEMENTED_INTRINSIC(X86, CRC32Update)
UNIMPLEMENTED_INTRINSIC(X86, StringStringIndexOf);
UNIMPLEMENTED_INTRINSIC(X86, StringStringIndexOfAfter);
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 96f6eaa..7db26dc 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -2732,6 +2732,7 @@
UNIMPLEMENTED_INTRINSIC(X86_64, ReferenceGetReferent)
UNIMPLEMENTED_INTRINSIC(X86_64, FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(X86_64, DoubleIsInfinite)
+UNIMPLEMENTED_INTRINSIC(X86_64, CRC32Update)
UNIMPLEMENTED_INTRINSIC(X86_64, StringStringIndexOf);
UNIMPLEMENTED_INTRINSIC(X86_64, StringStringIndexOfAfter);
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 2124380..6108522 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1497,6 +1497,14 @@
M(X86PackedSwitch, Instruction)
#endif
+#if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64)
+#define FOR_EACH_CONCRETE_INSTRUCTION_X86_COMMON(M) \
+ M(X86AndNot, Instruction) \
+ M(X86MaskOrResetLeastSetBit, Instruction)
+#else
+#define FOR_EACH_CONCRETE_INSTRUCTION_X86_COMMON(M)
+#endif
+
#define FOR_EACH_CONCRETE_INSTRUCTION_X86_64(M)
#define FOR_EACH_CONCRETE_INSTRUCTION(M) \
@@ -1507,7 +1515,8 @@
FOR_EACH_CONCRETE_INSTRUCTION_MIPS(M) \
FOR_EACH_CONCRETE_INSTRUCTION_MIPS64(M) \
FOR_EACH_CONCRETE_INSTRUCTION_X86(M) \
- FOR_EACH_CONCRETE_INSTRUCTION_X86_64(M)
+ FOR_EACH_CONCRETE_INSTRUCTION_X86_64(M) \
+ FOR_EACH_CONCRETE_INSTRUCTION_X86_COMMON(M)
#define FOR_EACH_ABSTRACT_INSTRUCTION(M) \
M(Condition, BinaryOperation) \
@@ -7766,7 +7775,7 @@
#ifdef ART_ENABLE_CODEGEN_mips
#include "nodes_mips.h"
#endif
-#ifdef ART_ENABLE_CODEGEN_x86
+#if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64)
#include "nodes_x86.h"
#endif
diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h
index 597e399..cd4f45e 100644
--- a/compiler/optimizing/nodes_vector.h
+++ b/compiler/optimizing/nodes_vector.h
@@ -384,21 +384,21 @@
HInstruction* input,
DataType::Type packed_type,
size_t vector_length,
- ReductionKind kind,
+ ReductionKind reduction_kind,
uint32_t dex_pc)
: HVecUnaryOperation(kVecReduce, allocator, input, packed_type, vector_length, dex_pc),
- kind_(kind) {
+ reduction_kind_(reduction_kind) {
DCHECK(HasConsistentPackedTypes(input, packed_type));
}
- ReductionKind GetKind() const { return kind_; }
+ ReductionKind GetReductionKind() const { return reduction_kind_; }
bool CanBeMoved() const override { return true; }
bool InstructionDataEquals(const HInstruction* other) const override {
DCHECK(other->IsVecReduce());
const HVecReduce* o = other->AsVecReduce();
- return HVecOperation::InstructionDataEquals(o) && GetKind() == o->GetKind();
+ return HVecOperation::InstructionDataEquals(o) && GetReductionKind() == o->GetReductionKind();
}
DECLARE_INSTRUCTION(VecReduce);
@@ -407,7 +407,7 @@
DEFAULT_COPY_CONSTRUCTOR(VecReduce);
private:
- const ReductionKind kind_;
+ const ReductionKind reduction_kind_;
};
// Converts every component in the vector,
diff --git a/compiler/optimizing/nodes_vector_test.cc b/compiler/optimizing/nodes_vector_test.cc
index af13449..b0a665d 100644
--- a/compiler/optimizing/nodes_vector_test.cc
+++ b/compiler/optimizing/nodes_vector_test.cc
@@ -401,9 +401,9 @@
EXPECT_TRUE(v2->CanBeMoved());
EXPECT_TRUE(v3->CanBeMoved());
- EXPECT_EQ(HVecReduce::kSum, v1->GetKind());
- EXPECT_EQ(HVecReduce::kMin, v2->GetKind());
- EXPECT_EQ(HVecReduce::kMax, v3->GetKind());
+ EXPECT_EQ(HVecReduce::kSum, v1->GetReductionKind());
+ EXPECT_EQ(HVecReduce::kMin, v2->GetReductionKind());
+ EXPECT_EQ(HVecReduce::kMax, v3->GetReductionKind());
EXPECT_TRUE(v1->Equals(v1));
EXPECT_TRUE(v2->Equals(v2));
diff --git a/compiler/optimizing/nodes_x86.h b/compiler/optimizing/nodes_x86.h
index a551104..8e8fbc1 100644
--- a/compiler/optimizing/nodes_x86.h
+++ b/compiler/optimizing/nodes_x86.h
@@ -128,6 +128,92 @@
const int32_t num_entries_;
};
+class HX86AndNot final : public HBinaryOperation {
+ public:
+ HX86AndNot(DataType::Type result_type,
+ HInstruction* left,
+ HInstruction* right,
+ uint32_t dex_pc = kNoDexPc)
+ : HBinaryOperation(kX86AndNot, result_type, left, right, SideEffects::None(), dex_pc) {
+ }
+
+ bool IsCommutative() const override { return false; }
+
+ template <typename T> static T Compute(T x, T y) { return ~x & y; }
+
+ HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const override {
+ return GetBlock()->GetGraph()->GetIntConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ }
+ HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const override {
+ return GetBlock()->GetGraph()->GetLongConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ }
+ HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED,
+ HFloatConstant* y ATTRIBUTE_UNUSED) const override {
+ LOG(FATAL) << DebugName() << " is not defined for float values";
+ UNREACHABLE();
+ }
+ HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED,
+ HDoubleConstant* y ATTRIBUTE_UNUSED) const override {
+ LOG(FATAL) << DebugName() << " is not defined for double values";
+ UNREACHABLE();
+ }
+
+ DECLARE_INSTRUCTION(X86AndNot);
+
+ protected:
+ DEFAULT_COPY_CONSTRUCTOR(X86AndNot);
+};
+
+class HX86MaskOrResetLeastSetBit final : public HUnaryOperation {
+ public:
+ HX86MaskOrResetLeastSetBit(DataType::Type result_type, InstructionKind op,
+ HInstruction* input, uint32_t dex_pc = kNoDexPc)
+ : HUnaryOperation(kX86MaskOrResetLeastSetBit, result_type, input, dex_pc),
+ op_kind_(op) {
+ DCHECK_EQ(result_type, DataType::Kind(input->GetType()));
+ DCHECK(op == HInstruction::kAnd || op == HInstruction::kXor) << op;
+ }
+ template <typename T>
+ auto Compute(T x) const -> decltype(x & (x-1)) {
+ static_assert(std::is_same<decltype(x & (x-1)), decltype(x ^(x-1))>::value,
+ "Inconsistent bitwise types");
+ switch (op_kind_) {
+ case HInstruction::kAnd:
+ return x & (x-1);
+ case HInstruction::kXor:
+ return x ^ (x-1);
+ default:
+ LOG(FATAL) << "Unreachable";
+ UNREACHABLE();
+ }
+ }
+
+ HConstant* Evaluate(HIntConstant* x) const override {
+ return GetBlock()->GetGraph()->GetIntConstant(Compute(x->GetValue()), GetDexPc());
+ }
+ HConstant* Evaluate(HLongConstant* x) const override {
+ return GetBlock()->GetGraph()->GetLongConstant(Compute(x->GetValue()), GetDexPc());
+ }
+ HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED) const override {
+ LOG(FATAL) << DebugName() << "is not defined for float values";
+ UNREACHABLE();
+ }
+ HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED) const override {
+ LOG(FATAL) << DebugName() << "is not defined for double values";
+ UNREACHABLE();
+ }
+ InstructionKind GetOpKind() const { return op_kind_; }
+
+ DECLARE_INSTRUCTION(X86MaskOrResetLeastSetBit);
+
+ protected:
+ const InstructionKind op_kind_;
+
+ DEFAULT_COPY_CONSTRUCTOR(X86MaskOrResetLeastSetBit);
+};
+
} // namespace art
#endif // ART_COMPILER_OPTIMIZING_NODES_X86_H_
diff --git a/compiler/optimizing/optimization.cc b/compiler/optimizing/optimization.cc
index 4b0941b..0f971e1 100644
--- a/compiler/optimizing/optimization.cc
+++ b/compiler/optimizing/optimization.cc
@@ -28,10 +28,14 @@
#endif
#ifdef ART_ENABLE_CODEGEN_x86
#include "pc_relative_fixups_x86.h"
+#include "instruction_simplifier_x86.h"
#endif
#if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64)
#include "x86_memory_gen.h"
#endif
+#ifdef ART_ENABLE_CODEGEN_x86_64
+#include "instruction_simplifier_x86_64.h"
+#endif
#include "bounds_check_elimination.h"
#include "cha_guard_optimization.h"
@@ -113,6 +117,12 @@
#ifdef ART_ENABLE_CODEGEN_x86
case OptimizationPass::kPcRelativeFixupsX86:
return x86::PcRelativeFixups::kPcRelativeFixupsX86PassName;
+ case OptimizationPass::kInstructionSimplifierX86:
+ return x86::InstructionSimplifierX86::kInstructionSimplifierX86PassName;
+#endif
+#ifdef ART_ENABLE_CODEGEN_x86_64
+ case OptimizationPass::kInstructionSimplifierX86_64:
+ return x86_64::InstructionSimplifierX86_64::kInstructionSimplifierX86_64PassName;
#endif
#if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64)
case OptimizationPass::kX86MemoryOperandGeneration:
@@ -311,6 +321,14 @@
DCHECK(alt_name == nullptr) << "arch-specific pass does not support alternative name";
opt = new (allocator) x86::X86MemoryOperandGeneration(graph, codegen, stats);
break;
+ case OptimizationPass::kInstructionSimplifierX86:
+ opt = new (allocator) x86::InstructionSimplifierX86(graph, codegen, stats);
+ break;
+#endif
+#ifdef ART_ENABLE_CODEGEN_x86_64
+ case OptimizationPass::kInstructionSimplifierX86_64:
+ opt = new (allocator) x86_64::InstructionSimplifierX86_64(graph, codegen, stats);
+ break;
#endif
case OptimizationPass::kNone:
LOG(FATAL) << "kNone does not represent an actual pass";
diff --git a/compiler/optimizing/optimization.h b/compiler/optimizing/optimization.h
index ced383f..490007d 100644
--- a/compiler/optimizing/optimization.h
+++ b/compiler/optimizing/optimization.h
@@ -96,6 +96,10 @@
#endif
#ifdef ART_ENABLE_CODEGEN_x86
kPcRelativeFixupsX86,
+ kInstructionSimplifierX86,
+#endif
+#ifdef ART_ENABLE_CODEGEN_x86_64
+ kInstructionSimplifierX86_64,
#endif
#if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64)
kX86MemoryOperandGeneration,
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 4f495b6..fe6abd4 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -579,6 +579,7 @@
#ifdef ART_ENABLE_CODEGEN_x86
case InstructionSet::kX86: {
OptimizationDef x86_optimizations[] = {
+ OptDef(OptimizationPass::kInstructionSimplifierX86),
OptDef(OptimizationPass::kSideEffectsAnalysis),
OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"),
OptDef(OptimizationPass::kPcRelativeFixupsX86),
@@ -595,6 +596,7 @@
#ifdef ART_ENABLE_CODEGEN_x86_64
case InstructionSet::kX86_64: {
OptimizationDef x86_64_optimizations[] = {
+ OptDef(OptimizationPass::kInstructionSimplifierX86_64),
OptDef(OptimizationPass::kSideEffectsAnalysis),
OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"),
OptDef(OptimizationPass::kX86MemoryOperandGeneration)
@@ -1134,7 +1136,7 @@
}
if (kIsDebugBuild &&
- IsCompilingWithCoreImage() &&
+ compiler_driver->GetCompilerOptions().CompilingWithCoreImage() &&
IsInstructionSetSupported(compiler_driver->GetCompilerOptions().GetInstructionSet())) {
// For testing purposes, we put a special marker on method names
// that should be compiled with this compiler (when the
@@ -1232,30 +1234,11 @@
return new OptimizingCompiler(driver);
}
-bool IsCompilingWithCoreImage() {
- const std::string& image = Runtime::Current()->GetImageLocation();
- return CompilerDriver::IsCoreImageFilename(image);
-}
-
bool EncodeArtMethodInInlineInfo(ArtMethod* method ATTRIBUTE_UNUSED) {
// Note: the runtime is null only for unit testing.
return Runtime::Current() == nullptr || !Runtime::Current()->IsAotCompiler();
}
-bool CanEncodeInlinedMethodInStackMap(const DexFile& caller_dex_file, ArtMethod* callee) {
- if (!Runtime::Current()->IsAotCompiler()) {
- // JIT can always encode methods in stack maps.
- return true;
- }
- if (IsSameDexFile(caller_dex_file, *callee->GetDexFile())) {
- return true;
- }
- // TODO(ngeoffray): Support more AOT cases for inlining:
- // - methods in multidex
- // - methods in boot image for on-device non-PIC compilation.
- return false;
-}
-
bool OptimizingCompiler::JitCompile(Thread* self,
jit::JitCodeCache* code_cache,
ArtMethod* method,
diff --git a/compiler/optimizing/optimizing_compiler.h b/compiler/optimizing/optimizing_compiler.h
index 6ee9c70..f5279e8 100644
--- a/compiler/optimizing/optimizing_compiler.h
+++ b/compiler/optimizing/optimizing_compiler.h
@@ -29,14 +29,7 @@
Compiler* CreateOptimizingCompiler(CompilerDriver* driver);
-// Returns whether we are compiling against a "core" image, which
-// is an indicative we are running tests. The compiler will use that
-// information for checking invariants.
-bool IsCompilingWithCoreImage();
-
bool EncodeArtMethodInInlineInfo(ArtMethod* method);
-bool CanEncodeInlinedMethodInStackMap(const DexFile& caller_dex_file, ArtMethod* callee)
- REQUIRES_SHARED(Locks::mutator_lock_);
} // namespace art
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index c864951..885a08d 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -233,7 +233,7 @@
// Try to assign a type check bitstring.
MutexLock subtype_check_lock(Thread::Current(), *Locks::subtype_check_lock_);
- if ((false) && // FIXME: Inliner does not respect CompilerDriver::IsClassToCompile()
+ if ((false) && // FIXME: Inliner does not respect CompilerDriver::ShouldCompileMethod()
// and we're hitting an unassigned bitstring in dex2oat_image_test. b/26687569
kIsDebugBuild &&
codegen->GetCompilerOptions().IsBootImage() &&
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index 053e202..3d26296 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -125,7 +125,7 @@
// Assemble the .S
snprintf(cmd, sizeof(cmd), "%sas %s -o %s.o", toolsdir.c_str(), filename, filename);
int cmd_result = system(cmd);
- ASSERT_EQ(cmd_result, 0) << strerror(errno);
+ ASSERT_EQ(cmd_result, 0) << cmd << strerror(errno);
// Disassemble.
snprintf(cmd, sizeof(cmd), "%sobjdump -D -M force-thumb --section=.text %s.o | grep '^ *[0-9a-f][0-9a-f]*:'",
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp
index 666db42..8ce96a4 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -71,11 +71,8 @@
generated_sources: ["art_dex2oat_operator_srcs"],
shared_libs: [
"libbase",
- "liblz4",
- "liblzma",
],
include_dirs: [
- "external/lz4/lib",
"external/zlib",
],
export_include_dirs: ["."],
@@ -98,8 +95,6 @@
},
static_libs: [
"libbase",
- "liblz4",
- "liblzma",
],
}
@@ -183,7 +178,7 @@
target: {
android: {
- // Use the 32-bit version of dex2oat on devices
+ // Use the 32-bit version of dex2oat on devices.
compile_multilib: "prefer32",
},
},
@@ -247,7 +242,6 @@
"libdexfile",
"libartbase",
"libbase",
- "liblz4",
"libsigchain",
],
static_libs: [
@@ -285,7 +279,6 @@
"libdexfiled",
"libartbased",
"libbase",
- "liblz4",
"libsigchain",
],
static_libs: [
@@ -315,7 +308,6 @@
],
static_libs: [
"libbase",
- "liblz4",
"libsigchain_dummy",
],
}
@@ -411,11 +403,11 @@
"external/zlib",
],
shared_libs: [
- "libprofiled",
+ "libartbased",
"libartd-compiler",
"libartd-dexlayout",
"libbase",
- "liblz4",
+ "libprofiled",
"libsigchain",
"libziparchive",
],
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index f0f2b3e..dc123e4 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -186,7 +186,8 @@
// Construct the final output.
if (command.size() <= 1U) {
- // It seems only "/system/bin/dex2oat" is left, or not even that. Use a pretty line.
+ // It seems only "/apex/com.android.runtime/bin/dex2oat" is left, or not
+ // even that. Use a pretty line.
return "Starting dex2oat.";
}
return android::base::Join(command, ' ');
@@ -731,11 +732,6 @@
compiler_options_->boot_image_ = !image_filenames_.empty();
compiler_options_->app_image_ = app_image_fd_ != -1 || !app_image_file_name_.empty();
- if (IsBootImage() && image_filenames_.size() == 1) {
- const std::string& boot_image_filename = image_filenames_[0];
- compiler_options_->core_image_ = CompilerDriver::IsCoreImageFilename(boot_image_filename);
- }
-
if (IsAppImage() && IsBootImage()) {
Usage("Can't have both --image and (--app-image-fd or --app-image-file)");
}
@@ -950,6 +946,9 @@
}
}
compiler_options_->passes_to_run_ = passes_to_run_.get();
+ compiler_options_->compiling_with_core_image_ =
+ !boot_image_filename_.empty() &&
+ CompilerDriver::IsCoreImageFilename(boot_image_filename_);
}
static bool SupportsDeterministicCompilation() {
@@ -1045,8 +1044,8 @@
}
void InsertCompileOptions(int argc, char** argv) {
- std::ostringstream oss;
if (!avoid_storing_invocation_) {
+ std::ostringstream oss;
for (int i = 0; i < argc; ++i) {
if (i > 0) {
oss << ' ';
@@ -1054,10 +1053,7 @@
oss << argv[i];
}
key_value_store_->Put(OatHeader::kDex2OatCmdLineKey, oss.str());
- oss.str(""); // Reset.
}
- oss << kRuntimeISA;
- key_value_store_->Put(OatHeader::kDex2OatHostKey, oss.str());
key_value_store_->Put(
OatHeader::kDebuggableKey,
compiler_options_->debuggable_ ? OatHeader::kTrueValue : OatHeader::kFalseValue);
@@ -1513,15 +1509,6 @@
std::vector<gc::space::ImageSpace*> image_spaces =
Runtime::Current()->GetHeap()->GetBootImageSpaces();
image_file_location_oat_checksum_ = image_spaces[0]->GetImageHeader().GetOatChecksum();
- // Store the boot image filename(s).
- std::vector<std::string> image_filenames;
- for (const gc::space::ImageSpace* image_space : image_spaces) {
- image_filenames.push_back(image_space->GetImageFilename());
- }
- std::string image_file_location = android::base::Join(image_filenames, ':');
- if (!image_file_location.empty()) {
- key_value_store_->Put(OatHeader::kImageLocationKey, image_file_location);
- }
} else {
image_file_location_oat_checksum_ = 0u;
}
@@ -1792,14 +1779,14 @@
compiler_options_->no_inline_from_.swap(no_inline_from_dex_files);
}
}
+ compiler_options_->profile_compilation_info_ = profile_compilation_info_.get();
driver_.reset(new CompilerDriver(compiler_options_.get(),
verification_results_.get(),
compiler_kind_,
&compiler_options_->image_classes_,
thread_count_,
- swap_fd_,
- profile_compilation_info_.get()));
+ swap_fd_));
if (!IsBootImage()) {
driver_->SetClasspathDexFiles(class_loader_context_->FlattenOpenedDexFiles());
}
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index d22b301..baeebd9 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -23,6 +23,7 @@
#include <unistd.h>
#include <android-base/logging.h>
+#include <android-base/macros.h>
#include <android-base/stringprintf.h>
#include "common_runtime_test.h"
@@ -90,6 +91,10 @@
args.push_back("--runtime-arg");
args.push_back("-Xnorelocate");
+ // Unless otherwise stated, use a small amount of threads, so that potential aborts are
+ // shorter. This can be overridden with extra_args.
+ args.push_back("-j4");
+
args.insert(args.end(), extra_args.begin(), extra_args.end());
int status = Dex2Oat(args, error_msg);
@@ -99,33 +104,33 @@
return status;
}
- void GenerateOdexForTest(
+ ::testing::AssertionResult GenerateOdexForTest(
const std::string& dex_location,
const std::string& odex_location,
CompilerFilter::Filter filter,
const std::vector<std::string>& extra_args = {},
bool expect_success = true,
- bool use_fd = false) {
- GenerateOdexForTest(dex_location,
- odex_location,
- filter,
- extra_args,
- expect_success,
- use_fd,
- [](const OatFile&) {});
+ bool use_fd = false) WARN_UNUSED {
+ return GenerateOdexForTest(dex_location,
+ odex_location,
+ filter,
+ extra_args,
+ expect_success,
+ use_fd,
+ [](const OatFile&) {});
}
bool test_accepts_odex_file_on_failure = false;
template <typename T>
- void GenerateOdexForTest(
+ ::testing::AssertionResult GenerateOdexForTest(
const std::string& dex_location,
const std::string& odex_location,
CompilerFilter::Filter filter,
const std::vector<std::string>& extra_args,
bool expect_success,
bool use_fd,
- T check_oat) {
+ T check_oat) WARN_UNUSED {
std::string error_msg;
int status = GenerateOdexForTestWithStatus({dex_location},
odex_location,
@@ -135,7 +140,10 @@
use_fd);
bool success = (WIFEXITED(status) && WEXITSTATUS(status) == 0);
if (expect_success) {
- ASSERT_TRUE(success) << error_msg << std::endl << output_;
+ if (!success) {
+ return ::testing::AssertionFailure()
+ << "Failed to compile odex: " << error_msg << std::endl << output_;
+ }
// Verify the odex file was generated as expected.
std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1,
@@ -146,12 +154,16 @@
dex_location.c_str(),
/*reservation=*/ nullptr,
&error_msg));
- ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
+ if (odex_file == nullptr) {
+ return ::testing::AssertionFailure() << "Could not open odex file: " << error_msg;
+ }
CheckFilter(filter, odex_file->GetCompilerFilter());
check_oat(*(odex_file.get()));
} else {
- ASSERT_FALSE(success) << output_;
+ if (success) {
+ return ::testing::AssertionFailure() << "Succeeded to compile odex: " << output_;
+ }
error_msg_ = error_msg;
@@ -165,9 +177,12 @@
dex_location.c_str(),
/*reservation=*/ nullptr,
&error_msg));
- ASSERT_TRUE(odex_file.get() == nullptr);
+ if (odex_file != nullptr) {
+ return ::testing::AssertionFailure() << "Could open odex file: " << error_msg;
+ }
}
}
+ return ::testing::AssertionSuccess();
}
// Check the input compiler filter against the generated oat file's filter. May be overridden
@@ -265,7 +280,7 @@
std::string swap_location = GetOdexDir() + "/Dex2OatSwapTest.odex.swap";
copy.push_back("--swap-file=" + swap_location);
}
- GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, copy);
+ ASSERT_TRUE(GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, copy));
CheckValidity();
ASSERT_TRUE(success_);
@@ -490,7 +505,7 @@
std::vector<std::string> new_args(extra_args);
new_args.push_back("--app-image-file=" + app_image_file);
- GenerateOdexForTest(dex_location, odex_location, filter, new_args);
+ ASSERT_TRUE(GenerateOdexForTest(dex_location, odex_location, filter, new_args));
CheckValidity();
ASSERT_TRUE(success_);
@@ -681,12 +696,12 @@
copy.push_back("--app-image-file=" + app_image_file_name);
}
}
- GenerateOdexForTest(dex_location,
- odex_location,
- CompilerFilter::kSpeedProfile,
- copy,
- expect_success,
- use_fd);
+ ASSERT_TRUE(GenerateOdexForTest(dex_location,
+ odex_location,
+ CompilerFilter::kSpeedProfile,
+ copy,
+ expect_success,
+ use_fd));
if (app_image_file != nullptr) {
ASSERT_EQ(app_image_file->FlushCloseOrErase(), 0) << "Could not flush and close art file";
}
@@ -877,24 +892,24 @@
{
std::string input_vdex = "--input-vdex-fd=-1";
std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_file1->Fd());
- GenerateOdexForTest(dex_location,
- odex_location,
- CompilerFilter::kQuicken,
- { input_vdex, output_vdex },
- /*expect_success=*/ true,
- /*use_fd=*/ true);
+ ASSERT_TRUE(GenerateOdexForTest(dex_location,
+ odex_location,
+ CompilerFilter::kQuicken,
+ { input_vdex, output_vdex },
+ /* expect_success= */ true,
+ /* use_fd= */ true));
EXPECT_GT(vdex_file1->GetLength(), 0u);
}
// Unquicken by running the verify compiler filter on the vdex file.
{
std::string input_vdex = StringPrintf("--input-vdex-fd=%d", vdex_file1->Fd());
std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_file1->Fd());
- GenerateOdexForTest(dex_location,
- odex_location,
- CompilerFilter::kVerify,
- { input_vdex, output_vdex, kDisableCompactDex },
- /*expect_success=*/ true,
- /*use_fd=*/ true);
+ ASSERT_TRUE(GenerateOdexForTest(dex_location,
+ odex_location,
+ CompilerFilter::kVerify,
+ { input_vdex, output_vdex, kDisableCompactDex },
+ /* expect_success= */ true,
+ /* use_fd= */ true));
}
ASSERT_EQ(vdex_file1->FlushCloseOrErase(), 0) << "Could not flush and close vdex file";
CheckResult(dex_location, odex_location);
@@ -918,12 +933,12 @@
{
std::string input_vdex = "--input-vdex-fd=-1";
std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_file1->Fd());
- GenerateOdexForTest(dex_location,
- odex_location,
- CompilerFilter::kQuicken,
- { input_vdex, output_vdex, "--compact-dex-level=fast"},
- /*expect_success=*/ true,
- /*use_fd=*/ true);
+ ASSERT_TRUE(GenerateOdexForTest(dex_location,
+ odex_location,
+ CompilerFilter::kQuicken,
+ { input_vdex, output_vdex, "--compact-dex-level=fast"},
+ /* expect_success= */ true,
+ /* use_fd= */ true));
EXPECT_GT(vdex_file1->GetLength(), 0u);
}
@@ -931,12 +946,12 @@
{
std::string input_vdex = StringPrintf("--input-vdex-fd=%d", vdex_file1->Fd());
std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_file2->Fd());
- GenerateOdexForTest(dex_location,
- odex_location2,
- CompilerFilter::kVerify,
- { input_vdex, output_vdex, "--compact-dex-level=none"},
- /*expect_success=*/ true,
- /*use_fd=*/ true);
+ ASSERT_TRUE(GenerateOdexForTest(dex_location,
+ odex_location2,
+ CompilerFilter::kVerify,
+ { input_vdex, output_vdex, "--compact-dex-level=none"},
+ /* expect_success= */ true,
+ /* use_fd= */ true));
}
ASSERT_EQ(vdex_file1->FlushCloseOrErase(), 0) << "Could not flush and close vdex file";
ASSERT_EQ(vdex_file2->FlushCloseOrErase(), 0) << "Could not flush and close vdex file";
@@ -992,11 +1007,11 @@
std::string swap_location = GetOdexDir() + "/Dex2OatSwapTest.odex.swap";
copy.push_back("--swap-file=" + swap_location);
copy.push_back("-j512"); // Excessive idle threads just slow down dex2oat.
- GenerateOdexForTest(dex_location,
- odex_location,
- CompilerFilter::kSpeed,
- copy,
- expect_success);
+ ASSERT_TRUE(GenerateOdexForTest(dex_location,
+ odex_location,
+ CompilerFilter::kSpeed,
+ copy,
+ expect_success));
}
std::string GetTestDexFileName() {
@@ -1072,13 +1087,13 @@
ASSERT_STREQ(expected_classpath_key, classpath);
};
- GenerateOdexForTest(dex_location,
- odex_location,
- CompilerFilter::kQuicken,
- extra_args,
- expected_success,
- /*use_fd*/ false,
- check_oat);
+ ASSERT_TRUE(GenerateOdexForTest(dex_location,
+ odex_location,
+ CompilerFilter::kQuicken,
+ extra_args,
+ expected_success,
+ /*use_fd*/ false,
+ check_oat));
}
std::string GetUsedDexLocation() {
@@ -1135,11 +1150,11 @@
Copy(GetDexSrc1(), stripped_classpath);
- GenerateOdexForTest(stripped_classpath,
- odex_for_classpath,
- CompilerFilter::kQuicken,
- {},
- true);
+ ASSERT_TRUE(GenerateOdexForTest(stripped_classpath,
+ odex_for_classpath,
+ CompilerFilter::kQuicken,
+ {},
+ true));
// Strip the dex file
Copy(GetStrippedDexSrc1(), stripped_classpath);
@@ -1216,7 +1231,7 @@
CompilerFilter::Filter::kQuicken,
&error_msg,
{"--force-determinism", "--avoid-storing-invocation"});
- EXPECT_EQ(res, 0);
+ ASSERT_EQ(res, 0);
Copy(base_oat_name, unload_oat_name);
Copy(base_vdex_name, unload_vdex_name);
std::unique_ptr<File> unload_oat(OS::OpenFileForReading(unload_oat_name.c_str()));
@@ -1233,7 +1248,7 @@
CompilerFilter::Filter::kQuicken,
&error_msg,
{"--force-determinism", "--avoid-storing-invocation", "--app-image-file=" + app_image_name});
- EXPECT_EQ(res2, 0);
+ ASSERT_EQ(res2, 0);
Copy(base_oat_name, no_unload_oat_name);
Copy(base_vdex_name, no_unload_vdex_name);
std::unique_ptr<File> no_unload_oat(OS::OpenFileForReading(no_unload_oat_name.c_str()));
@@ -1535,26 +1550,26 @@
std::string out_dir = GetScratchDir();
const std::string base_oat_name = out_dir + "/base.oat";
size_t no_dedupe_size = 0;
- GenerateOdexForTest(dex->GetLocation(),
- base_oat_name,
- CompilerFilter::Filter::kSpeed,
- { "--deduplicate-code=false" },
- true, // expect_success
- false, // use_fd
- [&no_dedupe_size](const OatFile& o) {
- no_dedupe_size = o.Size();
- });
+ ASSERT_TRUE(GenerateOdexForTest(dex->GetLocation(),
+ base_oat_name,
+ CompilerFilter::Filter::kSpeed,
+ { "--deduplicate-code=false" },
+ true, // expect_success
+ false, // use_fd
+ [&no_dedupe_size](const OatFile& o) {
+ no_dedupe_size = o.Size();
+ }));
size_t dedupe_size = 0;
- GenerateOdexForTest(dex->GetLocation(),
- base_oat_name,
- CompilerFilter::Filter::kSpeed,
- { "--deduplicate-code=true" },
- true, // expect_success
- false, // use_fd
- [&dedupe_size](const OatFile& o) {
- dedupe_size = o.Size();
- });
+ ASSERT_TRUE(GenerateOdexForTest(dex->GetLocation(),
+ base_oat_name,
+ CompilerFilter::Filter::kSpeed,
+ { "--deduplicate-code=true" },
+ true, // expect_success
+ false, // use_fd
+ [&dedupe_size](const OatFile& o) {
+ dedupe_size = o.Size();
+ }));
EXPECT_LT(dedupe_size, no_dedupe_size);
}
@@ -1563,15 +1578,15 @@
std::unique_ptr<const DexFile> dex(OpenTestDexFile("MainUncompressed"));
std::string out_dir = GetScratchDir();
const std::string base_oat_name = out_dir + "/base.oat";
- GenerateOdexForTest(dex->GetLocation(),
- base_oat_name,
- CompilerFilter::Filter::kQuicken,
- { },
- true, // expect_success
- false, // use_fd
- [](const OatFile& o) {
- CHECK(!o.ContainsDexCode());
- });
+ ASSERT_TRUE(GenerateOdexForTest(dex->GetLocation(),
+ base_oat_name,
+ CompilerFilter::Filter::kQuicken,
+ { },
+ true, // expect_success
+ false, // use_fd
+ [](const OatFile& o) {
+ CHECK(!o.ContainsDexCode());
+ }));
}
TEST_F(Dex2oatTest, EmptyUncompressedDexTest) {
@@ -1667,15 +1682,15 @@
std::string out_dir = GetScratchDir();
const std::string oat_filename = out_dir + "/base.oat";
// The dex won't pass the method verifier, only use the verify filter.
- GenerateOdexForTest(temp_dex.GetFilename(),
- oat_filename,
- CompilerFilter::Filter::kVerify,
- { },
- true, // expect_success
- false, // use_fd
- [](const OatFile& o) {
- CHECK(o.ContainsDexCode());
- });
+ ASSERT_TRUE(GenerateOdexForTest(temp_dex.GetFilename(),
+ oat_filename,
+ CompilerFilter::Filter::kVerify,
+ { },
+ true, // expect_success
+ false, // use_fd
+ [](const OatFile& o) {
+ CHECK(o.ContainsDexCode());
+ }));
// Open our generated oat file.
std::string error_msg;
std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1,
@@ -1718,11 +1733,11 @@
}
const std::string& dex_location = apk_file.GetFilename();
const std::string odex_location = GetOdexDir() + "/output.odex";
- GenerateOdexForTest(dex_location,
- odex_location,
- CompilerFilter::kQuicken,
- { "--compact-dex-level=fast" },
- true);
+ ASSERT_TRUE(GenerateOdexForTest(dex_location,
+ odex_location,
+ CompilerFilter::kQuicken,
+ { "--compact-dex-level=fast" },
+ true));
}
TEST_F(Dex2oatTest, StderrLoggerOutput) {
@@ -1732,11 +1747,11 @@
// Test file doesn't matter.
Copy(GetDexSrc1(), dex_location);
- GenerateOdexForTest(dex_location,
- odex_location,
- CompilerFilter::kQuicken,
- { "--runtime-arg", "-Xuse-stderr-logger" },
- true);
+ ASSERT_TRUE(GenerateOdexForTest(dex_location,
+ odex_location,
+ CompilerFilter::kQuicken,
+ { "--runtime-arg", "-Xuse-stderr-logger" },
+ true));
// Look for some random part of dex2oat logging. With the stderr logger this should be captured,
// even on device.
EXPECT_NE(std::string::npos, output_.find("dex2oat took"));
@@ -1749,11 +1764,11 @@
// Test file doesn't matter.
Copy(GetDexSrc1(), dex_location);
- GenerateOdexForTest(dex_location,
- odex_location,
- CompilerFilter::kVerify,
- { "--compilation-reason=install" },
- true);
+ ASSERT_TRUE(GenerateOdexForTest(dex_location,
+ odex_location,
+ CompilerFilter::kVerify,
+ { "--compilation-reason=install" },
+ true));
std::string error_msg;
std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1,
odex_location.c_str(),
@@ -1774,11 +1789,11 @@
// Test file doesn't matter.
Copy(GetDexSrc1(), dex_location);
- GenerateOdexForTest(dex_location,
- odex_location,
- CompilerFilter::kVerify,
- {},
- true);
+ ASSERT_TRUE(GenerateOdexForTest(dex_location,
+ odex_location,
+ CompilerFilter::kVerify,
+ {},
+ true));
std::string error_msg;
std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1,
odex_location.c_str(),
@@ -1799,14 +1814,13 @@
const std::string dex_location = dex->GetLocation();
const std::string odex_location = out_dir + "/base.oat";
const std::string vdex_location = out_dir + "/base.vdex";
- GenerateOdexForTest(dex_location,
- odex_location,
- CompilerFilter::Filter::kVerify,
- { "--copy-dex-files=false" },
- true, // expect_success
- false, // use_fd
- [](const OatFile&) {
- });
+ ASSERT_TRUE(GenerateOdexForTest(dex_location,
+ odex_location,
+ CompilerFilter::Filter::kVerify,
+ { "--copy-dex-files=false" },
+ true, // expect_success
+ false, // use_fd
+ [](const OatFile&) {}));
{
// Check the vdex doesn't have dex.
std::unique_ptr<VdexFile> vdex(VdexFile::Open(vdex_location.c_str(),
@@ -1856,19 +1870,21 @@
}
auto generate_and_check = [&](CompilerFilter::Filter filter) {
- GenerateOdexForTest(dex_location,
- odex_location,
- filter,
- { "--dump-timings",
- "--dm-file=" + dm_file.GetFilename(),
- // Pass -Xuse-stderr-logger have dex2oat output in output_ on target.
- "--runtime-arg",
- "-Xuse-stderr-logger" },
- true, // expect_success
- false, // use_fd
- [](const OatFile& o) {
- CHECK(o.ContainsDexCode());
- });
+ output_.clear();
+ ASSERT_TRUE(GenerateOdexForTest(dex_location,
+ odex_location,
+ filter,
+ { "--dump-timings",
+ "--dm-file=" + dm_file.GetFilename(),
+ // Pass -Xuse-stderr-logger have dex2oat output in output_ on
+ // target.
+ "--runtime-arg",
+ "-Xuse-stderr-logger" },
+ true, // expect_success
+ false, // use_fd
+ [](const OatFile& o) {
+ CHECK(o.ContainsDexCode());
+ }));
// Check the output for "Fast verify", this is printed from --dump-timings.
std::istringstream iss(output_);
std::string line;
@@ -1916,14 +1932,14 @@
{
std::string input_vdex = "--input-vdex-fd=-1";
std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_output->Fd());
- GenerateOdexForTest(dex_location,
- odex_location,
- CompilerFilter::kQuicken,
- // Disable cdex since we want to compare against the original dex file
- // after unquickening.
- { input_vdex, output_vdex, kDisableCompactDex },
- /*expect_success=*/ true,
- /*use_fd=*/ true);
+ ASSERT_TRUE(GenerateOdexForTest(dex_location,
+ odex_location,
+ CompilerFilter::kQuicken,
+ // Disable cdex since we want to compare against the original
+ // dex file after unquickening.
+ { input_vdex, output_vdex, kDisableCompactDex },
+ /* expect_success= */ true,
+ /* use_fd= */ true));
}
// Unquicken by running the verify compiler filter on the vdex file and verify it matches.
std::string odex_location2 = GetOdexDir() + "/unquickened.odex";
@@ -1932,13 +1948,14 @@
{
std::string input_vdex = StringPrintf("--input-vdex-fd=%d", vdex_output->Fd());
std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_unquickened->Fd());
- GenerateOdexForTest(dex_location,
- odex_location2,
- CompilerFilter::kVerify,
- // Disable cdex to avoid needing to write out the shared section.
- { input_vdex, output_vdex, kDisableCompactDex },
- /*expect_success=*/ true,
- /*use_fd=*/ true);
+ ASSERT_TRUE(GenerateOdexForTest(dex_location,
+ odex_location2,
+ CompilerFilter::kVerify,
+ // Disable cdex to avoid needing to write out the shared
+ // section.
+ { input_vdex, output_vdex, kDisableCompactDex },
+ /* expect_success= */ true,
+ /* use_fd= */ true));
}
ASSERT_EQ(vdex_unquickened->Flush(), 0) << "Could not flush and close vdex file";
ASSERT_TRUE(success_);
@@ -2046,13 +2063,13 @@
ScratchFile app_image_file;
const std::string out_dir = GetScratchDir();
const std::string odex_location = out_dir + "/base.odex";
- GenerateOdexForTest(GetTestDexFileName("ManyMethods"),
- odex_location,
- CompilerFilter::Filter::kSpeedProfile,
- { "--app-image-fd=" + std::to_string(app_image_file.GetFd()) },
- true, // expect_success
- false, // use_fd
- [](const OatFile&) {});
+ ASSERT_TRUE(GenerateOdexForTest(GetTestDexFileName("ManyMethods"),
+ odex_location,
+ CompilerFilter::Filter::kSpeedProfile,
+ { "--app-image-fd=" + std::to_string(app_image_file.GetFd()) },
+ true, // expect_success
+ false, // use_fd
+ [](const OatFile&) {}));
// Open our generated oat file.
std::string error_msg;
std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1,
@@ -2105,15 +2122,15 @@
const std::string out_dir = GetScratchDir();
const std::string odex_location = out_dir + "/base.odex";
const std::string app_image_location = out_dir + "/base.art";
- GenerateOdexForTest(GetTestDexFileName("StringLiterals"),
- odex_location,
- CompilerFilter::Filter::kSpeedProfile,
- { "--app-image-file=" + app_image_location,
- "--resolve-startup-const-strings=true",
- "--profile-file=" + profile_file.GetFilename()},
- /*expect_success=*/ true,
- /*use_fd=*/ false,
- [](const OatFile&) {});
+ ASSERT_TRUE(GenerateOdexForTest(GetTestDexFileName("StringLiterals"),
+ odex_location,
+ CompilerFilter::Filter::kSpeedProfile,
+ { "--app-image-file=" + app_image_location,
+ "--resolve-startup-const-strings=true",
+ "--profile-file=" + profile_file.GetFilename()},
+ /* expect_success= */ true,
+ /* use_fd= */ false,
+ [](const OatFile&) {}));
// Open our generated oat file.
std::string error_msg;
std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/ -1,
@@ -2220,27 +2237,27 @@
}
expected_stored_context += + "]";
// The class path should not be valid and should fail being stored.
- GenerateOdexForTest(GetTestDexFileName("ManyMethods"),
- odex_location,
- CompilerFilter::Filter::kQuicken,
- { "--class-loader-context=" + stored_context },
- true, // expect_success
- false, // use_fd
- [&](const OatFile& oat_file) {
+ EXPECT_TRUE(GenerateOdexForTest(GetTestDexFileName("ManyMethods"),
+ odex_location,
+ CompilerFilter::Filter::kQuicken,
+ { "--class-loader-context=" + stored_context },
+ true, // expect_success
+ false, // use_fd
+ [&](const OatFile& oat_file) {
EXPECT_NE(oat_file.GetClassLoaderContext(), stored_context) << output_;
EXPECT_NE(oat_file.GetClassLoaderContext(), valid_context) << output_;
- });
+ }));
// The stored context should match what we expect even though it's invalid.
- GenerateOdexForTest(GetTestDexFileName("ManyMethods"),
- odex_location,
- CompilerFilter::Filter::kQuicken,
- { "--class-loader-context=" + valid_context,
- "--stored-class-loader-context=" + stored_context },
- true, // expect_success
- false, // use_fd
- [&](const OatFile& oat_file) {
+ EXPECT_TRUE(GenerateOdexForTest(GetTestDexFileName("ManyMethods"),
+ odex_location,
+ CompilerFilter::Filter::kQuicken,
+ { "--class-loader-context=" + valid_context,
+ "--stored-class-loader-context=" + stored_context },
+ true, // expect_success
+ false, // use_fd
+ [&](const OatFile& oat_file) {
EXPECT_EQ(oat_file.GetClassLoaderContext(), expected_stored_context) << output_;
- });
+ }));
}
} // namespace art
diff --git a/dex2oat/linker/image_test.cc b/dex2oat/linker/image_test.cc
index b628c9e..69dac19 100644
--- a/dex2oat/linker/image_test.cc
+++ b/dex2oat/linker/image_test.cc
@@ -99,9 +99,9 @@
TEST_F(ImageTest, TestDefaultMethods) {
CompilationHelper helper;
Compile(ImageHeader::kStorageModeUncompressed,
- helper,
- "DefaultMethods",
- {"LIface;", "LImpl;", "LIterableBase;"});
+ helper,
+ "DefaultMethods",
+ {"LIface;", "LImpl;", "LIterableBase;"});
PointerSize pointer_size = class_linker_->GetImagePointerSize();
Thread* self = Thread::Current();
@@ -152,5 +152,17 @@
ASSERT_TRUE(class_linker_->IsQuickToInterpreterBridge(code));
}
+// Regression test for dex2oat crash for soft verification failure during
+// class initialization check from the transactional interpreter while
+// running the class initializer for another class.
+TEST_F(ImageTest, TestSoftVerificationFailureDuringClassInitialization) {
+ CompilationHelper helper;
+ Compile(ImageHeader::kStorageModeUncompressed,
+ helper,
+ "VerifySoftFailDuringClinit",
+ /*image_classes=*/ {"LClassToInitialize;"},
+ /*image_classes_failing_aot_clinit=*/ {"LClassToInitialize;"});
+}
+
} // namespace linker
} // namespace art
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
index 443ee52..9d1a4e7 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -28,6 +28,7 @@
#include "art_method-inl.h"
#include "base/file_utils.h"
#include "base/hash_set.h"
+#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
#include "base/utils.h"
#include "class_linker-inl.h"
@@ -81,7 +82,8 @@
void Compile(ImageHeader::StorageMode storage_mode,
/*out*/ CompilationHelper& out_helper,
const std::string& extra_dex = "",
- const std::initializer_list<std::string>& image_classes = {});
+ const std::initializer_list<std::string>& image_classes = {},
+ const std::initializer_list<std::string>& image_classes_failing_aot_clinit = {});
void SetUpRuntimeOptions(RuntimeOptions* options) override {
CommonCompilerTest::SetUpRuntimeOptions(options);
@@ -370,10 +372,15 @@
}
}
-inline void ImageTest::Compile(ImageHeader::StorageMode storage_mode,
- CompilationHelper& helper,
- const std::string& extra_dex,
- const std::initializer_list<std::string>& image_classes) {
+inline void ImageTest::Compile(
+ ImageHeader::StorageMode storage_mode,
+ CompilationHelper& helper,
+ const std::string& extra_dex,
+ const std::initializer_list<std::string>& image_classes,
+ const std::initializer_list<std::string>& image_classes_failing_aot_clinit) {
+ for (const std::string& image_class : image_classes_failing_aot_clinit) {
+ ASSERT_TRUE(ContainsElement(image_classes, image_class));
+ }
for (const std::string& image_class : image_classes) {
image_classes_.insert(image_class);
}
@@ -394,7 +401,12 @@
ObjPtr<mirror::Class> klass =
class_linker->FindSystemClass(Thread::Current(), image_class.c_str());
EXPECT_TRUE(klass != nullptr);
- EXPECT_TRUE(klass->IsInitialized());
+ EXPECT_TRUE(klass->IsResolved());
+ if (ContainsElement(image_classes_failing_aot_clinit, image_class)) {
+ EXPECT_FALSE(klass->IsInitialized());
+ } else {
+ EXPECT_TRUE(klass->IsInitialized());
+ }
}
}
}
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index 5ca7f07..e4fbd17 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -613,8 +613,22 @@
CHECK(!oat_filenames.empty());
CHECK_EQ(image_filenames.size(), oat_filenames.size());
+ Thread* self = Thread::Current();
{
- ScopedObjectAccess soa(Thread::Current());
+ // Preload deterministic contents to the dex cache arrays we're going to write.
+ ScopedObjectAccess soa(self);
+ ObjPtr<mirror::ClassLoader> class_loader = GetClassLoader();
+ std::vector<ObjPtr<mirror::DexCache>> dex_caches = FindDexCaches(self);
+ for (ObjPtr<mirror::DexCache> dex_cache : dex_caches) {
+ if (compile_app_image_ && IsInBootImage(dex_cache.Ptr())) {
+ continue; // Boot image DexCache is not written to the app image.
+ }
+ PreloadDexCache(dex_cache, class_loader);
+ }
+ }
+
+ {
+ ScopedObjectAccess soa(self);
for (size_t i = 0; i < oat_filenames.size(); ++i) {
CreateHeader(i);
CopyAndFixupNativeData(i);
@@ -623,7 +637,7 @@
{
// TODO: heap validation can't handle these fix up passes.
- ScopedObjectAccess soa(Thread::Current());
+ ScopedObjectAccess soa(self);
Runtime::Current()->GetHeap()->DisableObjectValidation();
CopyAndFixupObjects();
}
@@ -1409,8 +1423,79 @@
Runtime::Current()->GetClassLinker()->VisitClassLoaders(visitor);
}
-void ImageWriter::PruneAndPreloadDexCache(ObjPtr<mirror::DexCache> dex_cache,
- ObjPtr<mirror::ClassLoader> class_loader) {
+void ImageWriter::PruneDexCache(ObjPtr<mirror::DexCache> dex_cache,
+ ObjPtr<mirror::ClassLoader> class_loader) {
+ Runtime* runtime = Runtime::Current();
+ ClassLinker* class_linker = runtime->GetClassLinker();
+ const DexFile& dex_file = *dex_cache->GetDexFile();
+ // Prune methods.
+ dex::TypeIndex last_class_idx; // Initialized to invalid index.
+ ObjPtr<mirror::Class> last_class = nullptr;
+ mirror::MethodDexCacheType* resolved_methods = dex_cache->GetResolvedMethods();
+ for (size_t slot_idx = 0, num = dex_cache->NumResolvedMethods(); slot_idx != num; ++slot_idx) {
+ auto pair =
+ mirror::DexCache::GetNativePairPtrSize(resolved_methods, slot_idx, target_ptr_size_);
+ uint32_t stored_index = pair.index;
+ ArtMethod* method = pair.object;
+ if (method == nullptr) {
+ continue; // Empty entry.
+ }
+ // Check if the referenced class is in the image. Note that we want to check the referenced
+ // class rather than the declaring class to preserve the semantics, i.e. using a MethodId
+ // results in resolving the referenced class and that can for example throw OOME.
+ const DexFile::MethodId& method_id = dex_file.GetMethodId(stored_index);
+ if (method_id.class_idx_ != last_class_idx) {
+ last_class_idx = method_id.class_idx_;
+ last_class = class_linker->LookupResolvedType(last_class_idx, dex_cache, class_loader);
+ if (last_class != nullptr && !KeepClass(last_class)) {
+ last_class = nullptr;
+ }
+ }
+ if (last_class == nullptr) {
+ dex_cache->ClearResolvedMethod(stored_index, target_ptr_size_);
+ }
+ }
+ // Prune fields.
+ mirror::FieldDexCacheType* resolved_fields = dex_cache->GetResolvedFields();
+ last_class_idx = dex::TypeIndex(); // Initialized to invalid index.
+ last_class = nullptr;
+ for (size_t slot_idx = 0, num = dex_cache->NumResolvedFields(); slot_idx != num; ++slot_idx) {
+ auto pair = mirror::DexCache::GetNativePairPtrSize(resolved_fields, slot_idx, target_ptr_size_);
+ uint32_t stored_index = pair.index;
+ ArtField* field = pair.object;
+ if (field == nullptr) {
+ continue; // Empty entry.
+ }
+ // Check if the referenced class is in the image. Note that we want to check the referenced
+ // class rather than the declaring class to preserve the semantics, i.e. using a FieldId
+ // results in resolving the referenced class and that can for example throw OOME.
+ const DexFile::FieldId& field_id = dex_file.GetFieldId(stored_index);
+ if (field_id.class_idx_ != last_class_idx) {
+ last_class_idx = field_id.class_idx_;
+ last_class = class_linker->LookupResolvedType(last_class_idx, dex_cache, class_loader);
+ if (last_class != nullptr && !KeepClass(last_class)) {
+ last_class = nullptr;
+ }
+ }
+ if (last_class == nullptr) {
+ dex_cache->ClearResolvedField(stored_index, target_ptr_size_);
+ }
+ }
+ // Prune types.
+ for (size_t slot_idx = 0, num = dex_cache->NumResolvedTypes(); slot_idx != num; ++slot_idx) {
+ mirror::TypeDexCachePair pair =
+ dex_cache->GetResolvedTypes()[slot_idx].load(std::memory_order_relaxed);
+ uint32_t stored_index = pair.index;
+ ObjPtr<mirror::Class> klass = pair.object.Read();
+ if (klass != nullptr && !KeepClass(klass)) {
+ dex_cache->ClearResolvedType(dex::TypeIndex(stored_index));
+ }
+ }
+ // Strings do not need pruning.
+}
+
+void ImageWriter::PreloadDexCache(ObjPtr<mirror::DexCache> dex_cache,
+ ObjPtr<mirror::ClassLoader> class_loader) {
// To ensure deterministic contents of the hash-based arrays, each slot shall contain
// the candidate with the lowest index. As we're processing entries in increasing index
// order, this means trying to look up the entry for the current index if the slot is
@@ -1419,7 +1504,7 @@
Runtime* runtime = Runtime::Current();
ClassLinker* class_linker = runtime->GetClassLinker();
const DexFile& dex_file = *dex_cache->GetDexFile();
- // Prune methods.
+ // Preload the methods array and make the contents deterministic.
mirror::MethodDexCacheType* resolved_methods = dex_cache->GetResolvedMethods();
dex::TypeIndex last_class_idx; // Initialized to invalid index.
ObjPtr<mirror::Class> last_class = nullptr;
@@ -1439,27 +1524,20 @@
if (method_id.class_idx_ != last_class_idx) {
last_class_idx = method_id.class_idx_;
last_class = class_linker->LookupResolvedType(last_class_idx, dex_cache, class_loader);
- if (last_class != nullptr && !KeepClass(last_class)) {
- last_class = nullptr;
- }
}
if (method == nullptr || i < stored_index) {
if (last_class != nullptr) {
// Try to resolve the method with the class linker, which will insert
// it into the dex cache if successful.
method = class_linker->FindResolvedMethod(last_class, dex_cache, class_loader, i);
- // If the referenced class is in the image, the defining class must also be there.
- DCHECK(method == nullptr || KeepClass(method->GetDeclaringClass()));
DCHECK(method == nullptr || dex_cache->GetResolvedMethod(i, target_ptr_size_) == method);
}
} else {
DCHECK_EQ(i, stored_index);
- if (last_class == nullptr) {
- dex_cache->ClearResolvedMethod(stored_index, target_ptr_size_);
- }
+ DCHECK(last_class != nullptr);
}
}
- // Prune fields and make the contents of the field array deterministic.
+ // Preload the fields array and make the contents deterministic.
mirror::FieldDexCacheType* resolved_fields = dex_cache->GetResolvedFields();
last_class_idx = dex::TypeIndex(); // Initialized to invalid index.
last_class = nullptr;
@@ -1484,19 +1562,17 @@
}
if (field == nullptr || i < stored_index) {
if (last_class != nullptr) {
+ // Try to resolve the field with the class linker, which will insert
+ // it into the dex cache if successful.
field = class_linker->FindResolvedFieldJLS(last_class, dex_cache, class_loader, i);
- // If the referenced class is in the image, the defining class must also be there.
- DCHECK(field == nullptr || KeepClass(field->GetDeclaringClass()));
DCHECK(field == nullptr || dex_cache->GetResolvedField(i, target_ptr_size_) == field);
}
} else {
DCHECK_EQ(i, stored_index);
- if (last_class == nullptr) {
- dex_cache->ClearResolvedField(stored_index, target_ptr_size_);
- }
+ DCHECK(last_class != nullptr);
}
}
- // Prune types and make the contents of the type array deterministic.
+ // Preload the types array and make the contents deterministic.
// This is done after fields and methods as their lookup can touch the types array.
for (size_t i = 0, end = dex_cache->GetDexFile()->NumTypeIds(); i < end; ++i) {
dex::TypeIndex type_idx(i);
@@ -1507,15 +1583,10 @@
ObjPtr<mirror::Class> klass = pair.object.Read();
if (klass == nullptr || i < stored_index) {
klass = class_linker->LookupResolvedType(type_idx, dex_cache, class_loader);
- if (klass != nullptr) {
- DCHECK_EQ(dex_cache->GetResolvedType(type_idx), klass);
- stored_index = i; // For correct clearing below if not keeping the `klass`.
- }
- } else if (i == stored_index && !KeepClass(klass)) {
- dex_cache->ClearResolvedType(dex::TypeIndex(stored_index));
+ DCHECK(klass == nullptr || dex_cache->GetResolvedType(type_idx) == klass);
}
}
- // Strings do not need pruning, but the contents of the string array must be deterministic.
+ // Preload the strings array and make the contents deterministic.
for (size_t i = 0, end = dex_cache->GetDexFile()->NumStringIds(); i < end; ++i) {
dex::StringIndex string_idx(i);
uint32_t slot_idx = dex_cache->StringSlotIndex(string_idx);
@@ -1564,21 +1635,11 @@
}
// Clear references to removed classes from the DexCaches.
- std::vector<ObjPtr<mirror::DexCache>> dex_caches;
- {
- ReaderMutexLock mu2(self, *Locks::dex_lock_);
- dex_caches.reserve(class_linker->GetDexCachesData().size());
- for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) {
- if (self->IsJWeakCleared(data.weak_root)) {
- continue;
- }
- dex_caches.push_back(self->DecodeJObject(data.weak_root)->AsDexCache());
- }
- }
+ std::vector<ObjPtr<mirror::DexCache>> dex_caches = FindDexCaches(self);
for (ObjPtr<mirror::DexCache> dex_cache : dex_caches) {
// Pass the class loader associated with the DexCache. This can either be
// the app's `class_loader` or `nullptr` if boot class loader.
- PruneAndPreloadDexCache(dex_cache, IsInBootImage(dex_cache.Ptr()) ? nullptr : class_loader);
+ PruneDexCache(dex_cache, IsInBootImage(dex_cache.Ptr()) ? nullptr : class_loader);
}
// Drop the array class cache in the ClassLinker, as these are roots holding those classes live.
@@ -1588,6 +1649,20 @@
prune_class_memo_.clear();
}
+std::vector<ObjPtr<mirror::DexCache>> ImageWriter::FindDexCaches(Thread* self) {
+ std::vector<ObjPtr<mirror::DexCache>> dex_caches;
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ ReaderMutexLock mu2(self, *Locks::dex_lock_);
+ dex_caches.reserve(class_linker->GetDexCachesData().size());
+ for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) {
+ if (self->IsJWeakCleared(data.weak_root)) {
+ continue;
+ }
+ dex_caches.push_back(self->DecodeJObject(data.weak_root)->AsDexCache());
+ }
+ return dex_caches;
+}
+
void ImageWriter::CheckNonImageClassesRemoved() {
auto visitor = [&](Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
if (obj->IsClass() && !IsInBootImage(obj)) {
@@ -2670,10 +2745,8 @@
void ImageWriter::FixupPointerArray(mirror::Object* dst,
mirror::PointerArray* arr,
- mirror::Class* klass,
Bin array_type) {
- CHECK(klass->IsArrayClass());
- CHECK(arr->IsIntArray() || arr->IsLongArray()) << klass->PrettyClass() << " " << arr;
+ CHECK(arr->IsIntArray() || arr->IsLongArray()) << arr->GetClass()->PrettyClass() << " " << arr;
// Fixup int and long pointers for the ArtMethod or ArtField arrays.
const size_t num_elements = arr->GetLength();
CopyAndFixupReference(
@@ -2879,13 +2952,12 @@
if (kUseBakerReadBarrier) {
orig->AssertReadBarrierState();
}
- auto* klass = orig->GetClass();
- if (klass->IsIntArrayClass() || klass->IsLongArrayClass()) {
+ if (orig->IsIntArray() || orig->IsLongArray()) {
// Is this a native pointer array?
auto it = pointer_arrays_.find(down_cast<mirror::PointerArray*>(orig));
if (it != pointer_arrays_.end()) {
// Should only need to fixup every pointer array exactly once.
- FixupPointerArray(copy, down_cast<mirror::PointerArray*>(orig), klass, it->second);
+ FixupPointerArray(copy, down_cast<mirror::PointerArray*>(orig), it->second);
pointer_arrays_.erase(it);
return;
}
@@ -2895,6 +2967,7 @@
} else {
ObjPtr<mirror::ObjectArray<mirror::Class>> class_roots =
Runtime::Current()->GetClassLinker()->GetClassRoots();
+ ObjPtr<mirror::Class> klass = orig->GetClass();
if (klass == GetClassRoot<mirror::Method>(class_roots) ||
klass == GetClassRoot<mirror::Constructor>(class_roots)) {
// Need to go update the ArtMethod.
diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h
index 06c694c..bf89665 100644
--- a/dex2oat/linker/image_writer.h
+++ b/dex2oat/linker/image_writer.h
@@ -460,9 +460,18 @@
// Remove unwanted classes from various roots.
void PruneNonImageClasses() REQUIRES_SHARED(Locks::mutator_lock_);
- // Remove unwanted classes from the DexCache roots and preload deterministic DexCache contents.
- void PruneAndPreloadDexCache(ObjPtr<mirror::DexCache> dex_cache,
- ObjPtr<mirror::ClassLoader> class_loader)
+ // Remove unwanted classes from the DexCache roots.
+ void PruneDexCache(ObjPtr<mirror::DexCache> dex_cache, ObjPtr<mirror::ClassLoader> class_loader)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::classlinker_classes_lock_);
+
+ // Preload deterministic DexCache contents.
+ void PreloadDexCache(ObjPtr<mirror::DexCache> dex_cache, ObjPtr<mirror::ClassLoader> class_loader)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::classlinker_classes_lock_);
+
+ // Find dex caches for pruning or preloading.
+ std::vector<ObjPtr<mirror::DexCache>> FindDexCaches(Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::classlinker_classes_lock_);
@@ -541,7 +550,6 @@
REQUIRES_SHARED(Locks::mutator_lock_);
void FixupPointerArray(mirror::Object* dst,
mirror::PointerArray* arr,
- mirror::Class* klass,
Bin array_type)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index 01c24fc..7f2877f 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -766,10 +766,6 @@
bss_start_ = (bss_size_ != 0u) ? RoundUp(oat_size_, kPageSize) : 0u;
CHECK_EQ(dex_files_->size(), oat_dex_files_.size());
- if (GetCompilerOptions().IsBootImage()) {
- CHECK_EQ(image_writer_ != nullptr,
- oat_header_->GetStoreValueByKey(OatHeader::kImageLocationKey) == nullptr);
- }
write_state_ = WriteState::kWriteRoData;
}
@@ -1504,7 +1500,8 @@
}
ObjPtr<mirror::DexCache> dex_cache = class_linker_->FindDexCache(Thread::Current(), *dex_file);
const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
- mirror::Class* klass = dex_cache->GetResolvedType(class_def.class_idx_);
+ ObjPtr<mirror::Class> klass =
+ class_linker_->LookupResolvedType(class_def.class_idx_, dex_cache, class_loader_);
if (klass != nullptr) {
for (ArtMethod& method : klass->GetCopiedMethods(pointer_size_)) {
// Find origin method. Declaring class and dex_method_idx
@@ -1554,24 +1551,11 @@
ObjPtr<mirror::DexCache> dex_cache = class_linker_->FindDexCache(self, *dex_file_);
ArtMethod* resolved_method;
if (writer_->GetCompilerOptions().IsBootImage()) {
- const InvokeType invoke_type = method.GetInvokeType(
- dex_file_->GetClassDef(class_def_index_).access_flags_);
- // Unchecked as we hold mutator_lock_ on entry.
- ScopedObjectAccessUnchecked soa(self);
- StackHandleScope<1> hs(self);
- resolved_method = class_linker_->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>(
- method.GetIndex(),
- hs.NewHandle(dex_cache),
- ScopedNullHandle<mirror::ClassLoader>(),
- /* referrer */ nullptr,
- invoke_type);
+ resolved_method = class_linker_->LookupResolvedMethod(
+ method.GetIndex(), dex_cache, /*class_loader=*/ nullptr);
if (resolved_method == nullptr) {
- LOG(FATAL_WITHOUT_ABORT) << "Unexpected failure to resolve a method: "
+ LOG(FATAL) << "Unexpected failure to look up a method: "
<< dex_file_->PrettyMethod(method.GetIndex(), true);
- self->AssertPendingException();
- mirror::Throwable* exc = self->GetException();
- std::string dump = exc->Dump();
- LOG(FATAL) << dump;
UNREACHABLE();
}
} else {
diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc
index 83fb17c..af02bfc 100644
--- a/dex2oat/linker/oat_writer_test.cc
+++ b/dex2oat/linker/oat_writer_test.cc
@@ -394,7 +394,6 @@
ScratchFile tmp_base, tmp_oat(tmp_base, ".oat"), tmp_vdex(tmp_base, ".vdex");
SafeMap<std::string, std::string> key_value_store;
- key_value_store.Put(OatHeader::kImageLocationKey, "lue.art");
bool success = WriteElf(tmp_vdex.GetFile(),
tmp_oat.GetFile(),
class_linker->GetBootClassPath(),
@@ -418,7 +417,6 @@
ASSERT_TRUE(oat_header.IsValid());
ASSERT_EQ(class_linker->GetBootClassPath().size(), oat_header.GetDexFileCount()); // core
ASSERT_EQ(42U, oat_header.GetImageFileLocationOatChecksum());
- ASSERT_EQ("lue.art", std::string(oat_header.GetStoreValueByKey(OatHeader::kImageLocationKey)));
ASSERT_TRUE(java_lang_dex_file_ != nullptr);
const DexFile& dex_file = *java_lang_dex_file_;
@@ -517,7 +515,6 @@
ScratchFile tmp_base, tmp_oat(tmp_base, ".oat"), tmp_vdex(tmp_base, ".vdex");
SafeMap<std::string, std::string> key_value_store;
- key_value_store.Put(OatHeader::kImageLocationKey, "test.art");
bool success = WriteElf(tmp_vdex.GetFile(),
tmp_oat.GetFile(),
dex_files,
@@ -584,7 +581,6 @@
ScratchFile tmp_base, tmp_oat(tmp_base, ".oat"), tmp_vdex(tmp_base, ".vdex");
SafeMap<std::string, std::string> key_value_store;
- key_value_store.Put(OatHeader::kImageLocationKey, "test.art");
std::unique_ptr<ProfileCompilationInfo>
profile_compilation_info(use_profile ? new ProfileCompilationInfo() : nullptr);
success = WriteElf(tmp_vdex.GetFile(),
@@ -714,7 +710,6 @@
ASSERT_TRUE(success) << strerror(errno);
SafeMap<std::string, std::string> key_value_store;
- key_value_store.Put(OatHeader::kImageLocationKey, "test.art");
{
// Test using the AddDexFileSource() interface with the zip file.
std::vector<const char*> input_filenames = { zip_file.GetFilename().c_str() };
@@ -831,7 +826,6 @@
ASSERT_TRUE(success) << strerror(errno);
SafeMap<std::string, std::string> key_value_store;
- key_value_store.Put(OatHeader::kImageLocationKey, "test.art");
std::vector<const char*> input_filenames = { zip_file.GetFilename().c_str() };
ScratchFile oat_file, vdex_file(oat_file, ".vdex");
std::unique_ptr<ProfileCompilationInfo> profile_compilation_info(new ProfileCompilationInfo());
diff --git a/dexoptanalyzer/Android.bp b/dexoptanalyzer/Android.bp
index 99a11cd..72896c8 100644
--- a/dexoptanalyzer/Android.bp
+++ b/dexoptanalyzer/Android.bp
@@ -24,6 +24,7 @@
target: {
android: {
+ // Use the 32-bit version of dexoptanalyzer on devices.
compile_multilib: "prefer32",
},
},
diff --git a/disassembler/Android.bp b/disassembler/Android.bp
index 241b191..5aa159e 100644
--- a/disassembler/Android.bp
+++ b/disassembler/Android.bp
@@ -20,11 +20,39 @@
host_supported: true,
srcs: [
"disassembler.cc",
- "disassembler_arm.cc",
- "disassembler_arm64.cc",
"disassembler_mips.cc",
"disassembler_x86.cc",
],
+ codegen: {
+ arm: {
+ srcs: ["disassembler_arm.cc"]
+ },
+ arm64: {
+ srcs: ["disassembler_arm64.cc"]
+ },
+ // TODO: We should also conditionally include the MIPS32/MIPS64 and the
+ // x86/x86-64 disassembler definitions (b/119090273). However, using the
+ // following syntax here:
+ //
+ // mips: {
+ // srcs: ["disassembler_mips.cc"]
+ // },
+ // mips64: {
+ // srcs: ["disassembler_mips.cc"]
+ // },
+ // x86: {
+ // srcs: ["disassembler_x86.cc"]
+ // },
+ // x86_64: {
+ // srcs: ["disassembler_x86.cc"]
+ // },
+ //
+ // does not work, as it generates a file rejected by ninja with this
+ // error message (e.g. on host, where we include all the back ends by
+ // default):
+ //
+ // FAILED: ninja: out/soong/build.ninja:320768: multiple rules generate out/soong/.intermediates/art/disassembler/libart-disassembler/linux_glibc_x86_64_static/obj/art/disassembler/disassembler_mips.o [-w dupbuild=err]
+ },
include_dirs: ["art/runtime"],
shared_libs: [
@@ -41,8 +69,7 @@
defaults: ["libart-disassembler-defaults"],
shared_libs: [
// For disassembler_arm*.
- "libvixl-arm",
- "libvixl-arm64",
+ "libvixl",
],
}
@@ -54,7 +81,6 @@
],
shared_libs: [
// For disassembler_arm*.
- "libvixld-arm",
- "libvixld-arm64",
+ "libvixld",
],
}
diff --git a/disassembler/disassembler.cc b/disassembler/disassembler.cc
index 262e815..aee690e 100644
--- a/disassembler/disassembler.cc
+++ b/disassembler/disassembler.cc
@@ -21,10 +21,21 @@
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
-#include "disassembler_arm.h"
-#include "disassembler_arm64.h"
-#include "disassembler_mips.h"
-#include "disassembler_x86.h"
+#ifdef ART_ENABLE_CODEGEN_arm
+# include "disassembler_arm.h"
+#endif
+
+#ifdef ART_ENABLE_CODEGEN_arm64
+# include "disassembler_arm64.h"
+#endif
+
+#if defined(ART_ENABLE_CODEGEN_mips) || defined(ART_ENABLE_CODEGEN_mips64)
+# include "disassembler_mips.h"
+#endif
+
+#if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64)
+# include "disassembler_x86.h"
+#endif
using android::base::StringPrintf;
@@ -36,21 +47,35 @@
}
Disassembler* Disassembler::Create(InstructionSet instruction_set, DisassemblerOptions* options) {
- if (instruction_set == InstructionSet::kArm || instruction_set == InstructionSet::kThumb2) {
- return new arm::DisassemblerArm(options);
- } else if (instruction_set == InstructionSet::kArm64) {
- return new arm64::DisassemblerArm64(options);
- } else if (instruction_set == InstructionSet::kMips) {
- return new mips::DisassemblerMips(options, /* is_o32_abi= */ true);
- } else if (instruction_set == InstructionSet::kMips64) {
- return new mips::DisassemblerMips(options, /* is_o32_abi= */ false);
- } else if (instruction_set == InstructionSet::kX86) {
- return new x86::DisassemblerX86(options, false);
- } else if (instruction_set == InstructionSet::kX86_64) {
- return new x86::DisassemblerX86(options, true);
- } else {
- UNIMPLEMENTED(FATAL) << static_cast<uint32_t>(instruction_set);
- return nullptr;
+ switch (instruction_set) {
+#ifdef ART_ENABLE_CODEGEN_arm
+ case InstructionSet::kArm:
+ case InstructionSet::kThumb2:
+ return new arm::DisassemblerArm(options);
+#endif
+#ifdef ART_ENABLE_CODEGEN_arm64
+ case InstructionSet::kArm64:
+ return new arm64::DisassemblerArm64(options);
+#endif
+#ifdef ART_ENABLE_CODEGEN_mips
+ case InstructionSet::kMips:
+ return new mips::DisassemblerMips(options, /* is_o32_abi= */ true);
+#endif
+#ifdef ART_ENABLE_CODEGEN_mips64
+ case InstructionSet::kMips64:
+ return new mips::DisassemblerMips(options, /* is_o32_abi= */ false);
+#endif
+#ifdef ART_ENABLE_CODEGEN_x86
+ case InstructionSet::kX86:
+ return new x86::DisassemblerX86(options, /* supports_rex= */ false);
+#endif
+#ifdef ART_ENABLE_CODEGEN_x86_64
+ case InstructionSet::kX86_64:
+ return new x86::DisassemblerX86(options, /* supports_rex= */ true);
+#endif
+ default:
+ UNIMPLEMENTED(FATAL) << static_cast<uint32_t>(instruction_set);
+ return nullptr;
}
}
diff --git a/libartbase/Android.bp b/libartbase/Android.bp
index 5010f68..6a667bc 100644
--- a/libartbase/Android.bp
+++ b/libartbase/Android.bp
@@ -54,6 +54,10 @@
"libziparchive",
"libz",
],
+ // Exclude the version script from Darwin host since it's not
+ // supported by the linker there. That means ASan checks on Darwin
+ // might trigger ODR violations.
+ version_script: "libartbase.map",
},
host: {
shared_libs: [
@@ -61,16 +65,31 @@
"libz",
],
},
+ linux_glibc: {
+ version_script: "libartbase.map",
+ },
+ windows: {
+ version_script: "libartbase.map",
+ },
},
generated_sources: ["art_libartbase_operator_srcs"],
cflags: ["-DBUILDING_LIBART=1"],
shared_libs: [
"liblog",
- // For ashmem.
+ // For ashmem.
"libcutils",
// For common macros.
"libbase",
],
+
+ // Utilities used by various ART libs and tools are linked in statically
+ // here to avoid shared lib dependencies outside the ART APEX. No target
+ // there should depend on these separately.
+ whole_static_libs: [
+ "liblz4",
+ "liblzma",
+ ],
+
export_include_dirs: ["."],
// ART's macros.h depends on libbase's macros.h.
// Note: runtime_options.h depends on cmdline. But we don't really want to export this
diff --git a/libartbase/base/file_utils_test.cc b/libartbase/base/file_utils_test.cc
index f7c9c5e..c917307 100644
--- a/libartbase/base/file_utils_test.cc
+++ b/libartbase/base/file_utils_test.cc
@@ -83,11 +83,15 @@
ASSERT_EQ(0, unsetenv("ANDROID_ROOT"));
std::string android_root3 = GetAndroidRootSafe(&error_msg);
// This should be the same as the other root (modulo realpath), otherwise the test setup is
- // broken.
- UniqueCPtr<char> real_root(realpath(android_root.c_str(), nullptr));
+ // broken. On non-bionic. On bionic we can be running with a different libart that lives outside
+ // of ANDROID_ROOT
UniqueCPtr<char> real_root3(realpath(android_root3.c_str(), nullptr));
+#if !defined(__BIONIC__ ) || defined(__ANDROID__)
+ UniqueCPtr<char> real_root(realpath(android_root.c_str(), nullptr));
EXPECT_STREQ(real_root.get(), real_root3.get());
-
+#else
+ EXPECT_STRNE(real_root3.get(), "");
+#endif
// Reset ANDROID_ROOT, as other things may depend on it.
ASSERT_EQ(0, setenv("ANDROID_ROOT", android_root_env.c_str(), /* overwrite */ 1));
diff --git a/libartbase/base/logging.h b/libartbase/base/logging.h
index d2c0a02..9ded082 100644
--- a/libartbase/base/logging.h
+++ b/libartbase/base/logging.h
@@ -77,12 +77,12 @@
// performed.
extern const char* GetCmdLine();
-// The command used to start the ART runtime, such as "/system/bin/dalvikvm". If InitLogging hasn't
-// been performed then just returns "art"
+// The command used to start the ART runtime, such as "/apex/com.android.runtime/bin/dalvikvm". If
+// InitLogging hasn't been performed then just returns "art".
extern const char* ProgramInvocationName();
// A short version of the command used to start the ART runtime, such as "dalvikvm". If InitLogging
-// hasn't been performed then just returns "art"
+// hasn't been performed then just returns "art".
extern const char* ProgramInvocationShortName();
class LogHelper {
diff --git a/libartbase/base/membarrier.cc b/libartbase/base/membarrier.cc
index def949e..4c86b6b 100644
--- a/libartbase/base/membarrier.cc
+++ b/libartbase/base/membarrier.cc
@@ -25,7 +25,6 @@
#if defined(__BIONIC__)
#include <atomic>
-#include <android/get_device_api_level.h>
#include <linux/membarrier.h>
#define CHECK_MEMBARRIER_CMD(art_value, membarrier_value) \
diff --git a/libartbase/libartbase.map b/libartbase/libartbase.map
new file mode 100644
index 0000000..6249930
--- /dev/null
+++ b/libartbase/libartbase.map
@@ -0,0 +1,15 @@
+# This is used only to hide data symbols that get imported through
+# whole_static_libs, or else they might trigger the ASan odr-violation check.
+# Before adding symbols here, please make sure that it doesn't give rise to a
+# real ODR problem. All these symbols are either in .rodata or .data.rel.ro
+# sections.
+LIBARTBASE {
+ local:
+ PPMD7_kExpEscape;
+ XZ_SIG;
+ g_AlignedAlloc;
+ g_Alloc;
+ g_BigAlloc;
+ g_MidAlloc;
+ k7zSignature;
+};
diff --git a/libdexfile/dex/dex_instruction.h b/libdexfile/dex/dex_instruction.h
index ad8a184..4b38904 100644
--- a/libdexfile/dex/dex_instruction.h
+++ b/libdexfile/dex/dex_instruction.h
@@ -173,7 +173,7 @@
};
enum VerifyFlag : uint32_t {
- kVerifyNone = 0x0000000,
+ kVerifyNothing = 0x0000000,
kVerifyRegA = 0x0000001,
kVerifyRegAWide = 0x0000002,
kVerifyRegB = 0x0000004,
diff --git a/libdexfile/dex/dex_instruction_list.h b/libdexfile/dex/dex_instruction_list.h
index 9f0aba4..b9540a6 100644
--- a/libdexfile/dex/dex_instruction_list.h
+++ b/libdexfile/dex/dex_instruction_list.h
@@ -19,7 +19,7 @@
// V(opcode, instruction_code, name, format, index, flags, extended_flags, verifier_flags);
#define DEX_INSTRUCTION_LIST(V) \
- V(0x00, NOP, "nop", k10x, kIndexNone, kContinue, 0, kVerifyNone) \
+ V(0x00, NOP, "nop", k10x, kIndexNone, kContinue, 0, kVerifyNothing) \
V(0x01, MOVE, "move", k12x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
V(0x02, MOVE_FROM16, "move/from16", k22x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
V(0x03, MOVE_16, "move/16", k32x, kIndexNone, kContinue, 0, kVerifyRegA | kVerifyRegB) \
@@ -33,7 +33,7 @@
V(0x0B, MOVE_RESULT_WIDE, "move-result-wide", k11x, kIndexNone, kContinue, 0, kVerifyRegAWide) \
V(0x0C, MOVE_RESULT_OBJECT, "move-result-object", k11x, kIndexNone, kContinue, 0, kVerifyRegA) \
V(0x0D, MOVE_EXCEPTION, "move-exception", k11x, kIndexNone, kContinue, 0, kVerifyRegA) \
- V(0x0E, RETURN_VOID, "return-void", k10x, kIndexNone, kReturn, 0, kVerifyNone) \
+ V(0x0E, RETURN_VOID, "return-void", k10x, kIndexNone, kReturn, 0, kVerifyNothing) \
V(0x0F, RETURN, "return", k11x, kIndexNone, kReturn, 0, kVerifyRegA) \
V(0x10, RETURN_WIDE, "return-wide", k11x, kIndexNone, kReturn, 0, kVerifyRegAWide) \
V(0x11, RETURN_OBJECT, "return-object", k11x, kIndexNone, kReturn, 0, kVerifyRegA) \
@@ -134,7 +134,7 @@
V(0x70, INVOKE_DIRECT, "invoke-direct", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgNonZero) \
V(0x71, INVOKE_STATIC, "invoke-static", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArg) \
V(0x72, INVOKE_INTERFACE, "invoke-interface", k35c, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgNonZero) \
- V(0x73, RETURN_VOID_NO_BARRIER, "return-void-no-barrier", k10x, kIndexNone, kReturn, 0, kVerifyNone) \
+ V(0x73, RETURN_VOID_NO_BARRIER, "return-void-no-barrier", k10x, kIndexNone, kReturn, 0, kVerifyNothing) \
V(0x74, INVOKE_VIRTUAL_RANGE, "invoke-virtual/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \
V(0x75, INVOKE_SUPER_RANGE, "invoke-super/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \
V(0x76, INVOKE_DIRECT_RANGE, "invoke-direct/range", k3rc, kIndexMethodRef, kContinue | kThrow | kInvoke, 0, kVerifyRegBMethod | kVerifyVarArgRangeNonZero) \
diff --git a/libdexfile/dex/dex_instruction_test.cc b/libdexfile/dex/dex_instruction_test.cc
index 02400f4..3f79abf 100644
--- a/libdexfile/dex/dex_instruction_test.cc
+++ b/libdexfile/dex/dex_instruction_test.cc
@@ -26,7 +26,7 @@
EXPECT_EQ(Instruction::k10x, Instruction::FormatOf(nop));
EXPECT_EQ(Instruction::kIndexNone, Instruction::IndexTypeOf(nop));
EXPECT_EQ(Instruction::kContinue, Instruction::FlagsOf(nop));
- EXPECT_EQ(Instruction::kVerifyNone, Instruction::VerifyFlagsOf(nop));
+ EXPECT_EQ(Instruction::kVerifyNothing, Instruction::VerifyFlagsOf(nop));
}
static void Build45cc(uint8_t num_args, uint16_t method_idx, uint16_t proto_idx,
diff --git a/oatdump/Android.bp b/oatdump/Android.bp
index 596a946..45f853b 100644
--- a/oatdump/Android.bp
+++ b/oatdump/Android.bp
@@ -100,8 +100,7 @@
],
static_libs: [
"libart-disassembler",
- "libvixl-arm",
- "libvixl-arm64",
+ "libvixl",
],
}
@@ -124,8 +123,7 @@
},
static_libs: [
"libartd-disassembler",
- "libvixld-arm",
- "libvixld-arm64",
+ "libvixld",
],
group_static_libs: true,
}
diff --git a/openjdkjvmti/deopt_manager.cc b/openjdkjvmti/deopt_manager.cc
index 8bac38a..d456d83 100644
--- a/openjdkjvmti/deopt_manager.cc
+++ b/openjdkjvmti/deopt_manager.cc
@@ -139,6 +139,9 @@
// OnLoad since the runtime hasn't started up sufficiently. This is only expected to happen
// on userdebug/eng builds.
LOG(INFO) << "Attempting to start jit for openjdkjvmti plugin.";
+ // Note: use rwx allowed = true, because if this is the system server, we will not be
+ // allowed to allocate any JIT code cache, anyways.
+ runtime->CreateJitCodeCache(/*rwx_memory_allowed=*/true);
runtime->CreateJit();
if (runtime->GetJit() == nullptr) {
LOG(WARNING) << "Could not start jit for openjdkjvmti plugin. This process might be "
diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc
index 48df53a..a96436e 100644
--- a/openjdkjvmti/events.cc
+++ b/openjdkjvmti/events.cc
@@ -32,6 +32,7 @@
#include "events-inl.h"
#include <array>
+#include <sys/time.h>
#include "art_field-inl.h"
#include "art_jvmti.h"
@@ -56,6 +57,7 @@
#include "thread-inl.h"
#include "thread_list.h"
#include "ti_phase.h"
+#include "well_known_classes.h"
namespace openjdkjvmti {
@@ -410,14 +412,103 @@
EventHandler* handler_;
};
-static void SetupMonitorListener(art::MonitorCallback* listener, bool enable) {
+class JvmtiParkListener : public art::ParkCallback {
+ public:
+ explicit JvmtiParkListener(EventHandler* handler) : handler_(handler) {}
+
+ void ThreadParkStart(bool is_absolute, int64_t timeout)
+ override REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ if (handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kMonitorWait)) {
+ art::Thread* self = art::Thread::Current();
+ art::JNIEnvExt* jnienv = self->GetJniEnv();
+ art::ArtField* parkBlockerField = art::jni::DecodeArtField(
+ art::WellKnownClasses::java_lang_Thread_parkBlocker);
+ art::ObjPtr<art::mirror::Object> blocker_obj = parkBlockerField->GetObj(self->GetPeer());
+ if (blocker_obj.IsNull()) {
+ blocker_obj = self->GetPeer();
+ }
+ int64_t timeout_ms;
+ if (!is_absolute) {
+ if (timeout == 0) {
+ timeout_ms = 0;
+ } else {
+ timeout_ms = timeout / 1000000;
+ if (timeout_ms == 0) {
+ // If we were instructed to park for a nonzero number of nanoseconds, but not enough
+ // to be a full millisecond, round up to 1 ms. A nonzero park() call will return
+ // soon, but a 0 wait or park call will wait indefinitely.
+ timeout_ms = 1;
+ }
+ }
+ } else {
+ struct timeval tv;
+ gettimeofday(&tv, (struct timezone *) nullptr);
+ int64_t now = tv.tv_sec * 1000LL + tv.tv_usec / 1000;
+ if (now < timeout) {
+ timeout_ms = timeout - now;
+ } else {
+ // Waiting for 0 ms is an indefinite wait; parking until a time in
+ // the past or the current time will return immediately, so emulate
+ // the shortest possible wait event.
+ timeout_ms = 1;
+ }
+ }
+ ScopedLocalRef<jobject> blocker(jnienv, AddLocalRef<jobject>(jnienv, blocker_obj.Ptr()));
+ RunEventCallback<ArtJvmtiEvent::kMonitorWait>(
+ handler_,
+ self,
+ jnienv,
+ blocker.get(),
+ static_cast<jlong>(timeout_ms));
+ }
+ }
+
+
+ // Our interpretation of the spec is that the JVMTI_EVENT_MONITOR_WAITED will be sent immediately
+ // after a thread has woken up from a sleep caused by a call to Object#wait. If the thread will
+ // never go to sleep (due to not having the lock, having bad arguments, or having an exception
+ // propogated from JVMTI_EVENT_MONITOR_WAIT) we will not send this event.
+ //
+ // This does not fully match the RI semantics. Specifically, we will not send the
+ // JVMTI_EVENT_MONITOR_WAITED event in one situation where the RI would, there was an exception in
+ // the JVMTI_EVENT_MONITOR_WAIT event but otherwise the call was fine. In that case the RI would
+ // send this event and return without going to sleep.
+ //
+ // See b/65558434 for more discussion.
+ void ThreadParkFinished(bool timeout) override REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ if (handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kMonitorWaited)) {
+ art::Thread* self = art::Thread::Current();
+ art::JNIEnvExt* jnienv = self->GetJniEnv();
+ art::ArtField* parkBlockerField = art::jni::DecodeArtField(
+ art::WellKnownClasses::java_lang_Thread_parkBlocker);
+ art::ObjPtr<art::mirror::Object> blocker_obj = parkBlockerField->GetObj(self->GetPeer());
+ if (blocker_obj.IsNull()) {
+ blocker_obj = self->GetPeer();
+ }
+ ScopedLocalRef<jobject> blocker(jnienv, AddLocalRef<jobject>(jnienv, blocker_obj.Ptr()));
+ RunEventCallback<ArtJvmtiEvent::kMonitorWaited>(
+ handler_,
+ self,
+ jnienv,
+ blocker.get(),
+ static_cast<jboolean>(timeout));
+ }
+ }
+
+ private:
+ EventHandler* handler_;
+};
+
+static void SetupMonitorListener(art::MonitorCallback* monitor_listener, art::ParkCallback* park_listener, bool enable) {
// We must not hold the mutator lock here, but if we're in FastJNI, for example, we might. For
// now, do a workaround: (possibly) acquire and release.
art::ScopedObjectAccess soa(art::Thread::Current());
if (enable) {
- art::Runtime::Current()->GetRuntimeCallbacks()->AddMonitorCallback(listener);
+ art::Runtime::Current()->GetRuntimeCallbacks()->AddMonitorCallback(monitor_listener);
+ art::Runtime::Current()->GetRuntimeCallbacks()->AddParkCallback(park_listener);
} else {
- art::Runtime::Current()->GetRuntimeCallbacks()->RemoveMonitorCallback(listener);
+ art::Runtime::Current()->GetRuntimeCallbacks()->RemoveMonitorCallback(monitor_listener);
+ art::Runtime::Current()->GetRuntimeCallbacks()->RemoveParkCallback(park_listener);
}
}
@@ -1053,7 +1144,7 @@
case ArtJvmtiEvent::kMonitorWait:
case ArtJvmtiEvent::kMonitorWaited:
if (!OtherMonitorEventsEnabledAnywhere(event)) {
- SetupMonitorListener(monitor_listener_.get(), enable);
+ SetupMonitorListener(monitor_listener_.get(), park_listener_.get(), enable);
}
return;
default:
@@ -1204,6 +1295,7 @@
gc_pause_listener_.reset(new JvmtiGcPauseListener(this));
method_trace_listener_.reset(new JvmtiMethodTraceListener(this));
monitor_listener_.reset(new JvmtiMonitorListener(this));
+ park_listener_.reset(new JvmtiParkListener(this));
}
EventHandler::~EventHandler() {
diff --git a/openjdkjvmti/events.h b/openjdkjvmti/events.h
index 9f91a08..abb15cc 100644
--- a/openjdkjvmti/events.h
+++ b/openjdkjvmti/events.h
@@ -35,6 +35,7 @@
class JvmtiGcPauseListener;
class JvmtiMethodTraceListener;
class JvmtiMonitorListener;
+class JvmtiParkListener;
// an enum for ArtEvents. This differs from the JVMTI events only in that we distinguish between
// retransformation capable and incapable loading
@@ -331,6 +332,7 @@
std::unique_ptr<JvmtiGcPauseListener> gc_pause_listener_;
std::unique_ptr<JvmtiMethodTraceListener> method_trace_listener_;
std::unique_ptr<JvmtiMonitorListener> monitor_listener_;
+ std::unique_ptr<JvmtiParkListener> park_listener_;
// True if frame pop has ever been enabled. Since we store pointers to stack frames we need to
// continue to listen to this event even if it has been disabled.
diff --git a/profman/Android.bp b/profman/Android.bp
index 5aaccb0..89e1f7e 100644
--- a/profman/Android.bp
+++ b/profman/Android.bp
@@ -26,6 +26,7 @@
target: {
android: {
+ // Use the 32-bit version of profman on devices.
compile_multilib: "prefer32",
},
},
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 86ee952..410901e 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -377,7 +377,6 @@
include_dirs: [
"art/sigchainlib",
"external/icu/icu4c/source/common",
- "external/lz4/lib",
"external/zlib",
],
header_libs: [
@@ -390,7 +389,6 @@
"libnativebridge",
"libnativeloader",
"libbacktrace",
- "liblz4",
"liblog",
// For atrace, properties, ashmem, set_sched_policy.
"libcutils",
@@ -422,8 +420,6 @@
"libbase",
"libcutils",
"liblog",
- "liblz4",
- "liblzma",
"libnativebridge",
"libnativeloader",
"libunwindstack",
@@ -661,8 +657,7 @@
],
shared_libs: [
"libartd-compiler",
- "libvixld-arm",
- "libvixld-arm64",
+ "libvixld",
],
}
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index e9c17ee..d8da912 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -402,6 +402,7 @@
case Intrinsics::kUnsafeLoadFence:
case Intrinsics::kUnsafeStoreFence:
case Intrinsics::kUnsafeFullFence:
+ case Intrinsics::kCRC32Update:
// These intrinsics are on the light greylist and will fail a DCHECK in
// SetIntrinsic() if their flags change on the respective dex methods.
// Note that the DCHECK currently won't fail if the dex methods are
diff --git a/runtime/base/mutex-inl.h b/runtime/base/mutex-inl.h
index e775fe4..5daead9 100644
--- a/runtime/base/mutex-inl.h
+++ b/runtime/base/mutex-inl.h
@@ -91,6 +91,15 @@
CheckUnattachedThread(level_);
return;
}
+ LockLevel level = level_;
+ // It would be nice to avoid this condition checking in the non-debug case,
+ // but that would make the various methods that check if a mutex is held not
+ // work properly for thread wait locks. Since the vast majority of lock
+ // acquisitions are not thread wait locks, this check should not be too
+ // expensive.
+ if (UNLIKELY(level == kThreadWaitLock) && self->GetHeldMutex(kThreadWaitLock) != nullptr) {
+ level = kThreadWaitWakeLock;
+ }
if (kDebugLocking) {
// Check if a bad Mutex of this level or lower is held.
bool bad_mutexes_held = false;
@@ -98,13 +107,13 @@
// mutator_lock_ exclusive. This is because we suspending when holding locks at this level is
// not allowed and if we hold the mutator_lock_ exclusive we must unsuspend stuff eventually
// so there are no deadlocks.
- if (level_ == kTopLockLevel &&
+ if (level == kTopLockLevel &&
Locks::mutator_lock_->IsSharedHeld(self) &&
!Locks::mutator_lock_->IsExclusiveHeld(self)) {
LOG(ERROR) << "Lock level violation: holding \"" << Locks::mutator_lock_->name_ << "\" "
<< "(level " << kMutatorLock << " - " << static_cast<int>(kMutatorLock)
<< ") non-exclusive while locking \"" << name_ << "\" "
- << "(level " << level_ << " - " << static_cast<int>(level_) << ") a top level"
+ << "(level " << level << " - " << static_cast<int>(level) << ") a top level"
<< "mutex. This is not allowed.";
bad_mutexes_held = true;
} else if (this == Locks::mutator_lock_ && self->GetHeldMutex(kTopLockLevel) != nullptr) {
@@ -113,10 +122,10 @@
<< "not allowed.";
bad_mutexes_held = true;
}
- for (int i = level_; i >= 0; --i) {
+ for (int i = level; i >= 0; --i) {
LockLevel lock_level_i = static_cast<LockLevel>(i);
BaseMutex* held_mutex = self->GetHeldMutex(lock_level_i);
- if (level_ == kTopLockLevel &&
+ if (level == kTopLockLevel &&
lock_level_i == kMutatorLock &&
Locks::mutator_lock_->IsExclusiveHeld(self)) {
// This is checked above.
@@ -125,7 +134,7 @@
LOG(ERROR) << "Lock level violation: holding \"" << held_mutex->name_ << "\" "
<< "(level " << lock_level_i << " - " << i
<< ") while locking \"" << name_ << "\" "
- << "(level " << level_ << " - " << static_cast<int>(level_) << ")";
+ << "(level " << level << " - " << static_cast<int>(level) << ")";
if (lock_level_i > kAbortLock) {
// Only abort in the check below if this is more than abort level lock.
bad_mutexes_held = true;
@@ -138,8 +147,8 @@
}
// Don't record monitors as they are outside the scope of analysis. They may be inspected off of
// the monitor list.
- if (level_ != kMonitorLock) {
- self->SetHeldMutex(level_, this);
+ if (level != kMonitorLock) {
+ self->SetHeldMutex(level, this);
}
}
@@ -149,10 +158,17 @@
return;
}
if (level_ != kMonitorLock) {
- if (kDebugLocking && gAborting == 0) { // Avoid recursive aborts.
- CHECK(self->GetHeldMutex(level_) == this) << "Unlocking on unacquired mutex: " << name_;
+ auto level = level_;
+ if (UNLIKELY(level == kThreadWaitLock) && self->GetHeldMutex(kThreadWaitWakeLock) == this) {
+ level = kThreadWaitWakeLock;
}
- self->SetHeldMutex(level_, nullptr);
+ if (kDebugLocking && gAborting == 0) { // Avoid recursive aborts.
+ if (level == kThreadWaitWakeLock) {
+ CHECK(self->GetHeldMutex(kThreadWaitLock) != nullptr) << "Held " << kThreadWaitWakeLock << " without " << kThreadWaitLock;;
+ }
+ CHECK(self->GetHeldMutex(level) == this) << "Unlocking on unacquired mutex: " << name_;
+ }
+ self->SetHeldMutex(level, nullptr);
}
}
@@ -214,7 +230,11 @@
if (kDebugLocking) {
// Sanity debug check that if we think it is locked we have it in our held mutexes.
if (result && self != nullptr && level_ != kMonitorLock && !gAborting) {
- CHECK_EQ(self->GetHeldMutex(level_), this);
+ if (level_ == kThreadWaitLock && self->GetHeldMutex(kThreadWaitLock) != this) {
+ CHECK_EQ(self->GetHeldMutex(kThreadWaitWakeLock), this);
+ } else {
+ CHECK_EQ(self->GetHeldMutex(level_), this);
+ }
}
}
return result;
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 7711be9..0c8fe58 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -68,6 +68,14 @@
// A generic lock level for mutexs that should not allow any additional mutexes to be gained after
// acquiring it.
kGenericBottomLock,
+ // Tracks the second acquisition at the same lock level for kThreadWaitLock. This is an exception
+ // to the normal lock ordering, used to implement Monitor::Wait - while holding one kThreadWait
+ // level lock, it is permitted to acquire a second one - with internal safeguards to ensure that
+ // the second lock acquisition does not result in deadlock. This is implemented in the lock
+ // order by treating the second acquisition of a kThreadWaitLock as a kThreadWaitWakeLock
+ // acquisition. Thus, acquiring kThreadWaitWakeLock requires holding kThreadWaitLock.
+ kThreadWaitWakeLock,
+ kThreadWaitLock,
kJdwpAdbStateLock,
kJdwpSocketLock,
kRegionSpaceRegionLock,
diff --git a/runtime/cha.cc b/runtime/cha.cc
index de4aebe..8e06fda 100644
--- a/runtime/cha.cc
+++ b/runtime/cha.cc
@@ -115,7 +115,7 @@
// if they have SingleImplementations methods defined by 'klass'.
// Skip all virtual methods that do not override methods from super class since they cannot be
// SingleImplementations for anything.
- int32_t vtbl_size = super->GetVTableLength<kDefaultVerifyFlags, kWithoutReadBarrier>();
+ int32_t vtbl_size = super->GetVTableLength<kDefaultVerifyFlags>();
ObjPtr<mirror::ClassLoader> loader =
klass->GetClassLoader<kDefaultVerifyFlags, kWithoutReadBarrier>();
for (int vtbl_index = 0; vtbl_index < vtbl_size; ++vtbl_index) {
@@ -131,7 +131,7 @@
// so start with a superclass and move up looking into a corresponding vtbl slot.
for (ObjPtr<mirror::Class> super_it = super;
super_it != nullptr &&
- super_it->GetVTableLength<kDefaultVerifyFlags, kWithoutReadBarrier>() > vtbl_index;
+ super_it->GetVTableLength<kDefaultVerifyFlags>() > vtbl_index;
super_it = super_it->GetSuperClass<kDefaultVerifyFlags, kWithoutReadBarrier>()) {
// Skip superclasses that are also going to be unloaded.
ObjPtr<mirror::ClassLoader> super_loader = super_it->
@@ -158,7 +158,7 @@
// Check all possible interface methods too.
ObjPtr<mirror::IfTable> iftable = klass->GetIfTable<kDefaultVerifyFlags, kWithoutReadBarrier>();
- const size_t ifcount = klass->GetIfTableCount<kDefaultVerifyFlags, kWithoutReadBarrier>();
+ const size_t ifcount = klass->GetIfTableCount<kDefaultVerifyFlags>();
for (size_t i = 0; i < ifcount; ++i) {
ObjPtr<mirror::Class> interface =
iftable->GetInterface<kDefaultVerifyFlags, kWithoutReadBarrier>(i);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index e3dfdb3..7d4bac3 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -965,9 +965,6 @@
runtime->GetOatFileManager().RegisterImageOatFiles(spaces);
DCHECK(!oat_files.empty());
const OatHeader& default_oat_header = oat_files[0]->GetOatHeader();
- const char* image_file_location = oat_files[0]->GetOatHeader().
- GetStoreValueByKey(OatHeader::kImageLocationKey);
- CHECK(image_file_location == nullptr || *image_file_location == 0);
quick_resolution_trampoline_ = default_oat_header.GetQuickResolutionTrampoline();
quick_imt_conflict_trampoline_ = default_oat_header.GetQuickImtConflictTrampoline();
quick_generic_jni_trampoline_ = default_oat_header.GetQuickGenericJniTrampoline();
@@ -1495,18 +1492,44 @@
intern_table->AddImageStringsToTable(space, [&](InternTable::UnorderedSet& interns)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(Locks::intern_table_lock_) {
+ const size_t non_boot_image_strings = intern_table->CountInterns(
+ /*visit_boot_images=*/false,
+ /*visit_non_boot_images=*/true);
VLOG(image) << "AppImage:stringsInInternTableSize = " << interns.size();
- for (auto it = interns.begin(); it != interns.end(); ) {
- ObjPtr<mirror::String> string = it->Read();
- ObjPtr<mirror::String> existing = intern_table->LookupWeakLocked(string);
- if (existing == nullptr) {
- existing = intern_table->LookupStrongLocked(string);
+ VLOG(image) << "AppImage:nonBootImageInternStrings = " << non_boot_image_strings;
+ // Visit the smaller of the two sets to compute the intersection.
+ if (interns.size() < non_boot_image_strings) {
+ for (auto it = interns.begin(); it != interns.end(); ) {
+ ObjPtr<mirror::String> string = it->Read();
+ ObjPtr<mirror::String> existing = intern_table->LookupWeakLocked(string);
+ if (existing == nullptr) {
+ existing = intern_table->LookupStrongLocked(string);
+ }
+ if (existing != nullptr) {
+ intern_remap.Put(string.Ptr(), existing.Ptr());
+ it = interns.erase(it);
+ } else {
+ ++it;
+ }
}
- if (existing != nullptr) {
- intern_remap.Put(string.Ptr(), existing.Ptr());
- it = interns.erase(it);
- } else {
- ++it;
+ } else {
+ intern_table->VisitInterns([&](const GcRoot<mirror::String>& root)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::intern_table_lock_) {
+ auto it = interns.find(root);
+ if (it != interns.end()) {
+ ObjPtr<mirror::String> existing = root.Read();
+ intern_remap.Put(it->Read(), existing.Ptr());
+ it = interns.erase(it);
+ }
+ }, /*visit_boot_images=*/false, /*visit_non_boot_images=*/true);
+ }
+ // Sanity check to ensure correctness.
+ if (kIsDebugBuild) {
+ for (GcRoot<mirror::String>& root : interns) {
+ ObjPtr<mirror::String> string = root.Read();
+ CHECK(intern_table->LookupWeakLocked(string) == nullptr) << string->ToModifiedUtf8();
+ CHECK(intern_table->LookupStrongLocked(string) == nullptr) << string->ToModifiedUtf8();
}
}
});
@@ -1520,26 +1543,6 @@
}
}
-// Update the class loader. Should only be used on classes in the image space.
-class UpdateClassLoaderVisitor {
- public:
- UpdateClassLoaderVisitor(gc::space::ImageSpace* space, ObjPtr<mirror::ClassLoader> class_loader)
- : space_(space),
- class_loader_(class_loader) {}
-
- bool operator()(ObjPtr<mirror::Class> klass) const REQUIRES_SHARED(Locks::mutator_lock_) {
- // Do not update class loader for boot image classes where the app image
- // class loader is only the initiating loader but not the defining loader.
- if (klass->GetClassLoader() != nullptr) {
- klass->SetClassLoader(class_loader_);
- }
- return true;
- }
-
- gc::space::ImageSpace* const space_;
- ObjPtr<mirror::ClassLoader> const class_loader_;
-};
-
static std::unique_ptr<const DexFile> OpenOatDexFile(const OatFile* oat_file,
const char* location,
std::string* error_msg)
@@ -2008,11 +2011,23 @@
}
if (app_image) {
AppImageLoadingHelper::Update(this, space, class_loader, dex_caches, &temp_set);
- // Update class loader and resolved strings. If added_class_table is false, the resolved
- // strings were forwarded UpdateAppImageClassLoadersAndDexCaches.
- UpdateClassLoaderVisitor visitor(space, class_loader.Get());
- for (const ClassTable::TableSlot& root : temp_set) {
- visitor(root.Read());
+
+ {
+ ScopedTrace trace("AppImage:UpdateClassLoaders");
+ // Update class loader and resolved strings. If added_class_table is false, the resolved
+ // strings were forwarded UpdateAppImageClassLoadersAndDexCaches.
+ ObjPtr<mirror::ClassLoader> loader(class_loader.Get());
+ for (const ClassTable::TableSlot& root : temp_set) {
+ // Note: We probably don't need the read barrier unless we copy the app image objects into
+ // the region space.
+ ObjPtr<mirror::Class> klass(root.Read());
+ // Do not update class loader for boot image classes where the app image
+ // class loader is only the initiating loader but not the defining loader.
+ // Avoid read barrier since we are comparing against null.
+ if (klass->GetClassLoader<kDefaultVerifyFlags, kWithoutReadBarrier>() != nullptr) {
+ klass->SetClassLoader</*kCheckTransaction=*/ false>(loader);
+ }
+ }
}
if (kBitstringSubtypeCheckEnabled) {
@@ -3085,7 +3100,7 @@
return (jit == nullptr) || !jit->GetCodeCache()->ContainsPc(quick_code);
}
- if (runtime->IsNativeDebuggable()) {
+ if (runtime->IsNativeDebuggableZygoteOK()) {
DCHECK(runtime->UseJitCompilation() && runtime->GetJit()->JitAtFirstUse());
// If we are doing native debugging, ignore application's AOT code,
// since we want to JIT it (at first use) with extra stackmaps for native
@@ -3985,33 +4000,31 @@
return existing;
}
-ObjPtr<mirror::Class> ClassLinker::FindPrimitiveClass(char type) {
- ObjPtr<mirror::ObjectArray<mirror::Class>> class_roots = GetClassRoots();
+ObjPtr<mirror::Class> ClassLinker::LookupPrimitiveClass(char type) {
+ ClassRoot class_root;
switch (type) {
- case 'B':
- return GetClassRoot(ClassRoot::kPrimitiveByte, class_roots);
- case 'C':
- return GetClassRoot(ClassRoot::kPrimitiveChar, class_roots);
- case 'D':
- return GetClassRoot(ClassRoot::kPrimitiveDouble, class_roots);
- case 'F':
- return GetClassRoot(ClassRoot::kPrimitiveFloat, class_roots);
- case 'I':
- return GetClassRoot(ClassRoot::kPrimitiveInt, class_roots);
- case 'J':
- return GetClassRoot(ClassRoot::kPrimitiveLong, class_roots);
- case 'S':
- return GetClassRoot(ClassRoot::kPrimitiveShort, class_roots);
- case 'Z':
- return GetClassRoot(ClassRoot::kPrimitiveBoolean, class_roots);
- case 'V':
- return GetClassRoot(ClassRoot::kPrimitiveVoid, class_roots);
+ case 'B': class_root = ClassRoot::kPrimitiveByte; break;
+ case 'C': class_root = ClassRoot::kPrimitiveChar; break;
+ case 'D': class_root = ClassRoot::kPrimitiveDouble; break;
+ case 'F': class_root = ClassRoot::kPrimitiveFloat; break;
+ case 'I': class_root = ClassRoot::kPrimitiveInt; break;
+ case 'J': class_root = ClassRoot::kPrimitiveLong; break;
+ case 'S': class_root = ClassRoot::kPrimitiveShort; break;
+ case 'Z': class_root = ClassRoot::kPrimitiveBoolean; break;
+ case 'V': class_root = ClassRoot::kPrimitiveVoid; break;
default:
- break;
+ return nullptr;
}
- std::string printable_type(PrintableChar(type));
- ThrowNoClassDefFoundError("Not a primitive type: %s", printable_type.c_str());
- return nullptr;
+ return GetClassRoot(class_root, this);
+}
+
+ObjPtr<mirror::Class> ClassLinker::FindPrimitiveClass(char type) {
+ ObjPtr<mirror::Class> result = LookupPrimitiveClass(type);
+ if (UNLIKELY(result == nullptr)) {
+ std::string printable_type(PrintableChar(type));
+ ThrowNoClassDefFoundError("Not a primitive type: %s", printable_type.c_str());
+ }
+ return result;
}
ObjPtr<mirror::Class> ClassLinker::InsertClass(const char* descriptor,
@@ -4903,7 +4916,10 @@
} else {
CHECK(Runtime::Current()->IsAotCompiler());
CHECK_EQ(klass->GetStatus(), ClassStatus::kRetryVerificationAtRuntime);
+ self->AssertNoPendingException();
+ self->SetException(Runtime::Current()->GetPreAllocatedNoClassDefFoundError());
}
+ self->AssertPendingException();
return false;
} else {
self->AssertNoPendingException();
@@ -5042,8 +5058,10 @@
ArtField* resolved_field = dex_cache->GetResolvedField(field_idx, image_pointer_size_);
if (resolved_field == nullptr) {
// Populating cache of a dex file which defines `klass` should always be allowed.
- DCHECK_EQ(hiddenapi::GetMemberAction(
- field, class_loader.Get(), dex_cache.Get(), hiddenapi::kNone), hiddenapi::kAllow);
+ DCHECK(!hiddenapi::ShouldDenyAccessToMember(
+ field,
+ hiddenapi::AccessContext(class_loader.Get(), dex_cache.Get()),
+ hiddenapi::AccessMethod::kNone));
dex_cache->SetResolvedField(field_idx, field, image_pointer_size_);
} else {
DCHECK_EQ(field, resolved_field);
@@ -8004,7 +8022,7 @@
if (descriptor[1] == '\0') {
// only the descriptors of primitive types should be 1 character long, also avoid class lookup
// for primitive classes that aren't backed by dex files.
- type = FindPrimitiveClass(descriptor[0]);
+ type = LookupPrimitiveClass(descriptor[0]);
} else {
Thread* const self = Thread::Current();
DCHECK(self != nullptr);
@@ -8074,8 +8092,9 @@
}
DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr);
if (resolved != nullptr &&
- hiddenapi::GetMemberAction(
- resolved, class_loader, dex_cache, hiddenapi::kLinking) == hiddenapi::kDeny) {
+ hiddenapi::ShouldDenyAccessToMember(resolved,
+ hiddenapi::AccessContext(class_loader, dex_cache),
+ hiddenapi::AccessMethod::kLinking)) {
resolved = nullptr;
}
if (resolved != nullptr) {
@@ -8105,11 +8124,9 @@
ObjPtr<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_) {
return method == nullptr ||
- hiddenapi::GetMemberAction(method,
- class_loader,
- dex_cache,
- hiddenapi::kNone) // do not print warnings
- == hiddenapi::kDeny;
+ hiddenapi::ShouldDenyAccessToMember(method,
+ hiddenapi::AccessContext(class_loader, dex_cache),
+ hiddenapi::AccessMethod::kNone); // no warnings
}
ArtMethod* ClassLinker::FindIncompatibleMethod(ObjPtr<mirror::Class> klass,
@@ -8245,8 +8262,10 @@
resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, image_pointer_size_);
}
if (resolved != nullptr &&
- hiddenapi::GetMemberAction(
- resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking) == hiddenapi::kDeny) {
+ hiddenapi::ShouldDenyAccessToMember(
+ resolved,
+ hiddenapi::AccessContext(class_loader.Get(), dex_cache.Get()),
+ hiddenapi::AccessMethod::kLinking)) {
resolved = nullptr;
}
return resolved;
@@ -8345,8 +8364,9 @@
}
if (resolved != nullptr &&
- hiddenapi::GetMemberAction(
- resolved, class_loader, dex_cache, hiddenapi::kLinking) == hiddenapi::kDeny) {
+ hiddenapi::ShouldDenyAccessToMember(resolved,
+ hiddenapi::AccessContext(class_loader, dex_cache),
+ hiddenapi::AccessMethod::kLinking)) {
resolved = nullptr;
}
@@ -8371,8 +8391,9 @@
resolved = mirror::Class::FindField(self, klass, name, type);
if (resolved != nullptr &&
- hiddenapi::GetMemberAction(
- resolved, class_loader, dex_cache, hiddenapi::kLinking) == hiddenapi::kDeny) {
+ hiddenapi::ShouldDenyAccessToMember(resolved,
+ hiddenapi::AccessContext(class_loader, dex_cache),
+ hiddenapi::AccessMethod::kLinking)) {
resolved = nullptr;
}
@@ -8533,7 +8554,7 @@
switch (handle_type) {
case DexFile::MethodHandleType::kStaticPut: {
method_params->Set(0, target_field->ResolveType());
- return_type = hs.NewHandle(FindPrimitiveClass('V'));
+ return_type = hs.NewHandle(GetClassRoot(ClassRoot::kPrimitiveVoid, this));
break;
}
case DexFile::MethodHandleType::kStaticGet: {
@@ -8543,7 +8564,7 @@
case DexFile::MethodHandleType::kInstancePut: {
method_params->Set(0, target_field->GetDeclaringClass());
method_params->Set(1, target_field->ResolveType());
- return_type = hs.NewHandle(FindPrimitiveClass('V'));
+ return_type = hs.NewHandle(GetClassRoot(ClassRoot::kPrimitiveVoid, this));
break;
}
case DexFile::MethodHandleType::kInstanceGet: {
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 7afd575..60e68d5 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -196,6 +196,7 @@
REQUIRES(!Locks::classlinker_classes_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
+ ObjPtr<mirror::Class> LookupPrimitiveClass(char type) REQUIRES_SHARED(Locks::mutator_lock_);
ObjPtr<mirror::Class> FindPrimitiveClass(char type) REQUIRES_SHARED(Locks::mutator_lock_);
void DumpForSigQuit(std::ostream& os) REQUIRES(!Locks::classlinker_classes_lock_);
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index de9fe22..0bae60a 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -338,6 +338,7 @@
// no dex files. So that we can distinguish the real failures...
const ArtDexFileLoader dex_file_loader;
std::vector<ClassLoaderInfo*> work_list;
+ CHECK(class_loader_chain_ != nullptr);
work_list.push_back(class_loader_chain_.get());
while (!work_list.empty()) {
ClassLoaderInfo* info = work_list.back();
@@ -908,7 +909,9 @@
// collision check.
if (expected_context.special_shared_library_) {
// Special case where we are the only entry in the class path.
- if (class_loader_chain_->parent == nullptr && class_loader_chain_->classpath.size() == 0) {
+ if (class_loader_chain_ != nullptr &&
+ class_loader_chain_->parent == nullptr &&
+ class_loader_chain_->classpath.size() == 0) {
return VerificationResult::kVerifies;
}
return VerificationResult::kForcedToSkipChecks;
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index cb3dc65..f3e2ac0 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -735,6 +735,17 @@
ClassLoaderContext::VerificationResult::kMismatch);
}
+TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchSpecial) {
+ std::string context_spec = "&";
+ std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec);
+ // Pretend that we successfully open the dex files to pass the DCHECKS.
+ // (as it's much easier to test all the corner cases without relying on actual dex files).
+ PretendContextOpenedDexFiles(context.get());
+
+ ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_spec),
+ ClassLoaderContext::VerificationResult::kForcedToSkipChecks);
+}
+
TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchWithSL) {
std::string context_spec =
"PCL[a.dex*123:b.dex*456]{PCL[d.dex*321];PCL[e.dex*654]#PCL[f.dex*098:g.dex*999]}"
diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc
index ed3a18d..b11e368 100644
--- a/runtime/dexopt_test.cc
+++ b/runtime/dexopt_test.cc
@@ -66,7 +66,7 @@
}
runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv);
- if (runtime->GetHiddenApiEnforcementPolicy() != hiddenapi::EnforcementPolicy::kNoChecks) {
+ if (runtime->GetHiddenApiEnforcementPolicy() != hiddenapi::EnforcementPolicy::kDisabled) {
argv.push_back("--runtime-arg");
argv.push_back("-Xhidden-api-checks");
}
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index e0bbf43..aba1c5a 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -93,6 +93,8 @@
from_space_num_bytes_at_first_pause_(0),
mark_stack_mode_(kMarkStackModeOff),
weak_ref_access_enabled_(true),
+ copied_live_bytes_ratio_sum_(0.f),
+ gc_count_(0),
young_gen_(young_gen),
skipped_blocks_lock_("concurrent copying bytes blocks lock", kMarkSweepMarkStackLock),
measure_read_barrier_slow_path_(measure_read_barrier_slow_path),
@@ -1658,7 +1660,9 @@
<< " type=" << to_ref->PrettyTypeOf()
<< " young_gen=" << std::boolalpha << young_gen_ << std::noboolalpha
<< " space=" << heap_->DumpSpaceNameFromAddress(to_ref)
- << " region_type=" << rtype;
+ << " region_type=" << rtype
+ // TODO: Temporary; remove this when this is no longer needed (b/116087961).
+ << " runtime->sentinel=" << Runtime::Current()->GetSentinel().Read<kWithoutReadBarrier>();
}
bool add_to_live_bytes = false;
// Invariant: There should be no object from a newly-allocated
@@ -1702,7 +1706,9 @@
<< " type=" << to_ref->PrettyTypeOf()
<< " young_gen=" << std::boolalpha << young_gen_ << std::noboolalpha
<< " space=" << heap_->DumpSpaceNameFromAddress(to_ref)
- << " region_type=" << rtype;
+ << " region_type=" << rtype
+ // TODO: Temporary; remove this when this is no longer needed (b/116087961).
+ << " runtime->sentinel=" << Runtime::Current()->GetSentinel().Read<kWithoutReadBarrier>();
}
#ifdef USE_BAKER_OR_BROOKS_READ_BARRIER
mirror::Object* referent = nullptr;
@@ -2034,6 +2040,11 @@
}
CHECK_LE(to_objects, from_objects);
CHECK_LE(to_bytes, from_bytes);
+ if (from_bytes > 0) {
+ copied_live_bytes_ratio_sum_ += static_cast<float>(to_bytes) / from_bytes;
+ gc_count_++;
+ }
+
// Cleared bytes and objects, populated by the call to RegionSpace::ClearFromSpace below.
uint64_t cleared_bytes;
uint64_t cleared_objects;
@@ -3200,6 +3211,15 @@
if (rb_slow_path_count_gc_total_ > 0) {
os << "GC slow path count " << rb_slow_path_count_gc_total_ << "\n";
}
+ float average_ratio = copied_live_bytes_ratio_sum_ / gc_count_;
+
+ if (young_gen_) {
+ os << "Average minor GC copied live bytes ratio "
+ << average_ratio << " over " << gc_count_ << " minor GCs\n";
+ } else {
+ os << "Average major GC copied live bytes ratio "
+ << average_ratio << " over " << gc_count_ << " major GCs\n";
+ }
os << "Cumulative bytes moved "
<< cumulative_bytes_moved_.load(std::memory_order_relaxed) << "\n";
os << "Cumulative objects moved "
diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h
index 1a7464a..cd086c4 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -359,6 +359,18 @@
Atomic<uint64_t> cumulative_bytes_moved_;
Atomic<uint64_t> cumulative_objects_moved_;
+ // copied_live_bytes_ratio_sum_ and gc_count_ are read and written by CC per
+ // GC, in ReclaimPhase, and are read by DumpPerformanceInfo (potentially from
+ // another thread). However, at present, DumpPerformanceInfo is only called
+ // when the runtime shuts down, so no concurrent access.
+ // The sum of of all copied live bytes ratio (to_bytes/from_bytes)
+ float copied_live_bytes_ratio_sum_;
+ // The number of GC counts, used to calculate the average above. (It doesn't
+ // include GC where from_bytes is zero, IOW, from-space is empty, which is
+ // possible for minor GC if all allocated objects are in non-moving
+ // space.)
+ size_t gc_count_;
+
// Generational "sticky", only trace through dirty objects in region space.
const bool young_gen_;
// If true, the GC thread is done scanning marked objects on dirty and aged
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index b46abfb..0766999 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -832,7 +832,7 @@
reinterpret_cast<uintptr_t>(array) + kObjectAlignment);
// If the bit is not set then the contents have not yet been updated.
if (!visited_->Test(contents_bit)) {
- array->Fixup<kVerifyNone, kWithoutReadBarrier>(array, pointer_size_, visitor);
+ array->Fixup<kVerifyNone>(array, pointer_size_, visitor);
visited_->Set(contents_bit);
}
}
@@ -1884,7 +1884,7 @@
}
auto* iftable = klass->GetIfTable<kVerifyNone, kWithoutReadBarrier>();
if (iftable != nullptr) {
- int32_t ifcount = klass->GetIfTableCount<kVerifyNone, kWithoutReadBarrier>();
+ int32_t ifcount = klass->GetIfTableCount<kVerifyNone>();
for (int32_t i = 0; i != ifcount; ++i) {
mirror::PointerArray* unpatched_ifarray =
iftable->GetMethodArrayOrNull<kVerifyNone, kWithoutReadBarrier>(i);
diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc
index f355276..3b7b938 100644
--- a/runtime/hidden_api.cc
+++ b/runtime/hidden_api.cc
@@ -44,38 +44,53 @@
static inline std::ostream& operator<<(std::ostream& os, AccessMethod value) {
switch (value) {
- case kNone:
+ case AccessMethod::kNone:
LOG(FATAL) << "Internal access to hidden API should not be logged";
UNREACHABLE();
- case kReflection:
+ case AccessMethod::kReflection:
os << "reflection";
break;
- case kJNI:
+ case AccessMethod::kJNI:
os << "JNI";
break;
- case kLinking:
+ case AccessMethod::kLinking:
os << "linking";
break;
}
return os;
}
-static constexpr bool EnumsEqual(EnforcementPolicy policy, hiddenapi::ApiList apiList) {
- return static_cast<int>(policy) == static_cast<int>(apiList);
-}
-
-// GetMemberAction-related static_asserts.
-static_assert(
- EnumsEqual(EnforcementPolicy::kDarkGreyAndBlackList, hiddenapi::ApiList::kDarkGreylist) &&
- EnumsEqual(EnforcementPolicy::kBlacklistOnly, hiddenapi::ApiList::kBlacklist),
- "Mismatch between EnforcementPolicy and ApiList enums");
-static_assert(
- EnforcementPolicy::kJustWarn < EnforcementPolicy::kDarkGreyAndBlackList &&
- EnforcementPolicy::kDarkGreyAndBlackList < EnforcementPolicy::kBlacklistOnly,
- "EnforcementPolicy values ordering not correct");
-
namespace detail {
+// Do not change the values of items in this enum, as they are written to the
+// event log for offline analysis. Any changes will interfere with that analysis.
+enum AccessContextFlags {
+ // Accessed member is a field if this bit is set, else a method
+ kMemberIsField = 1 << 0,
+ // Indicates if access was denied to the member, instead of just printing a warning.
+ kAccessDenied = 1 << 1,
+};
+
+static int32_t GetMaxAllowedSdkVersionForApiList(ApiList api_list) {
+ SdkCodes sdk = SdkCodes::kVersionNone;
+ switch (api_list) {
+ case ApiList::kWhitelist:
+ case ApiList::kLightGreylist:
+ sdk = SdkCodes::kVersionUnlimited;
+ break;
+ case ApiList::kDarkGreylist:
+ sdk = SdkCodes::kVersionO_MR1;
+ break;
+ case ApiList::kBlacklist:
+ sdk = SdkCodes::kVersionNone;
+ break;
+ case ApiList::kNoList:
+ LOG(FATAL) << "Unexpected value";
+ UNREACHABLE();
+ }
+ return static_cast<int32_t>(sdk);
+}
+
MemberSignature::MemberSignature(ArtField* field) {
class_name_ = field->GetDeclaringClass()->GetDescriptor(&tmp_);
member_name_ = field->GetName();
@@ -137,6 +152,7 @@
LOG(WARNING) << "Accessing hidden " << (type_ == kField ? "field " : "method ")
<< Dumpable<MemberSignature>(*this) << " (" << list << ", " << access_method << ")";
}
+
#ifdef ART_TARGET_ANDROID
// Convert an AccessMethod enum to a value for logging from the proto enum.
// This method may look odd (the enum values are current the same), but it
@@ -145,13 +161,13 @@
// future.
inline static int32_t GetEnumValueForLog(AccessMethod access_method) {
switch (access_method) {
- case kNone:
+ case AccessMethod::kNone:
return android::metricslogger::ACCESS_METHOD_NONE;
- case kReflection:
+ case AccessMethod::kReflection:
return android::metricslogger::ACCESS_METHOD_REFLECTION;
- case kJNI:
+ case AccessMethod::kJNI:
return android::metricslogger::ACCESS_METHOD_JNI;
- case kLinking:
+ case AccessMethod::kLinking:
return android::metricslogger::ACCESS_METHOD_LINKING;
default:
DCHECK(false);
@@ -159,9 +175,9 @@
}
#endif
-void MemberSignature::LogAccessToEventLog(AccessMethod access_method, Action action_taken) {
+void MemberSignature::LogAccessToEventLog(AccessMethod access_method, bool access_denied) {
#ifdef ART_TARGET_ANDROID
- if (access_method == kLinking || access_method == kNone) {
+ if (access_method == AccessMethod::kLinking || access_method == AccessMethod::kNone) {
// Linking warnings come from static analysis/compilation of the bytecode
// and can contain false positives (i.e. code that is never run). We choose
// not to log these in the event log.
@@ -170,7 +186,7 @@
}
ComplexEventLogger log_maker(ACTION_HIDDEN_API_ACCESSED);
log_maker.AddTaggedData(FIELD_HIDDEN_API_ACCESS_METHOD, GetEnumValueForLog(access_method));
- if (action_taken == kDeny) {
+ if (access_denied) {
log_maker.AddTaggedData(FIELD_HIDDEN_API_ACCESS_DENIED, 1);
}
const std::string& package_name = Runtime::Current()->GetProcessPackageName();
@@ -183,10 +199,42 @@
log_maker.Record();
#else
UNUSED(access_method);
- UNUSED(action_taken);
+ UNUSED(access_denied);
#endif
}
+void MemberSignature::NotifyHiddenApiListener(AccessMethod access_method) {
+ if (access_method != AccessMethod::kReflection && access_method != AccessMethod::kJNI) {
+ // We can only up-call into Java during reflection and JNI down-calls.
+ return;
+ }
+
+ Runtime* runtime = Runtime::Current();
+ if (!runtime->IsAotCompiler()) {
+ ScopedObjectAccessUnchecked soa(Thread::Current());
+
+ ScopedLocalRef<jobject> consumer_object(soa.Env(),
+ soa.Env()->GetStaticObjectField(
+ WellKnownClasses::dalvik_system_VMRuntime,
+ WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer));
+ // If the consumer is non-null, we call back to it to let it know that we
+ // have encountered an API that's in one of our lists.
+ if (consumer_object != nullptr) {
+ std::ostringstream member_signature_str;
+ Dump(member_signature_str);
+
+ ScopedLocalRef<jobject> signature_str(
+ soa.Env(),
+ soa.Env()->NewStringUTF(member_signature_str.str().c_str()));
+
+ // Call through to Consumer.accept(String memberSignature);
+ soa.Env()->CallVoidMethod(consumer_object.get(),
+ WellKnownClasses::java_util_function_Consumer_accept,
+ signature_str.get());
+ }
+ }
+}
+
static ALWAYS_INLINE bool CanUpdateMemberAccessFlags(ArtField*) {
return true;
}
@@ -205,116 +253,68 @@
}
template<typename T>
-Action GetMemberActionImpl(T* member,
- hiddenapi::ApiList api_list,
- Action action,
- AccessMethod access_method) {
- DCHECK_NE(action, kAllow);
-
- // Get the signature, we need it later.
- MemberSignature member_signature(member);
+bool ShouldDenyAccessToMemberImpl(T* member,
+ hiddenapi::ApiList api_list,
+ AccessMethod access_method) {
+ DCHECK(member != nullptr);
Runtime* runtime = Runtime::Current();
+ EnforcementPolicy policy = runtime->GetHiddenApiEnforcementPolicy();
+
+ const bool deny_access =
+ (policy == EnforcementPolicy::kEnabled) &&
+ (runtime->GetTargetSdkVersion() > GetMaxAllowedSdkVersionForApiList(api_list));
+
+ MemberSignature member_signature(member);
// Check for an exemption first. Exempted APIs are treated as white list.
- // We only do this if we're about to deny, or if the app is debuggable. This is because:
- // - we only print a warning for light greylist violations for debuggable apps
- // - for non-debuggable apps, there is no distinction between light grey & whitelisted APIs.
- // - we want to avoid the overhead of checking for exemptions for light greylisted APIs whenever
- // possible.
- const bool shouldWarn = kLogAllAccesses || runtime->IsJavaDebuggable();
- if (shouldWarn || action == kDeny) {
- if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) {
- action = kAllow;
- // Avoid re-examining the exemption list next time.
- // Note this results in no warning for the member, which seems like what one would expect.
- // Exemptions effectively adds new members to the whitelist.
- MaybeWhitelistMember(runtime, member);
- return kAllow;
- }
+ if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) {
+ // Avoid re-examining the exemption list next time.
+ // Note this results in no warning for the member, which seems like what one would expect.
+ // Exemptions effectively adds new members to the whitelist.
+ MaybeWhitelistMember(runtime, member);
+ return false;
+ }
- if (access_method != kNone) {
- // Print a log message with information about this class member access.
- // We do this if we're about to block access, or the app is debuggable.
+ if (access_method != AccessMethod::kNone) {
+ // Print a log message with information about this class member access.
+ // We do this if we're about to deny access, or the app is debuggable.
+ if (kLogAllAccesses || deny_access || runtime->IsJavaDebuggable()) {
member_signature.WarnAboutAccess(access_method, api_list);
}
- }
- if (kIsTargetBuild && !kIsTargetLinux) {
- uint32_t eventLogSampleRate = runtime->GetHiddenApiEventLogSampleRate();
- // Assert that RAND_MAX is big enough, to ensure sampling below works as expected.
- static_assert(RAND_MAX >= 0xffff, "RAND_MAX too small");
- if (eventLogSampleRate != 0 &&
- (static_cast<uint32_t>(std::rand()) & 0xffff) < eventLogSampleRate) {
- member_signature.LogAccessToEventLog(access_method, action);
+ // If there is a StrictMode listener, notify it about this violation.
+ member_signature.NotifyHiddenApiListener(access_method);
+
+ // If event log sampling is enabled, report this violation.
+ if (kIsTargetBuild && !kIsTargetLinux) {
+ uint32_t eventLogSampleRate = runtime->GetHiddenApiEventLogSampleRate();
+ // Assert that RAND_MAX is big enough, to ensure sampling below works as expected.
+ static_assert(RAND_MAX >= 0xffff, "RAND_MAX too small");
+ if (eventLogSampleRate != 0 &&
+ (static_cast<uint32_t>(std::rand()) & 0xffff) < eventLogSampleRate) {
+ member_signature.LogAccessToEventLog(access_method, deny_access);
+ }
+ }
+
+ // If this access was not denied, move the member into whitelist and skip
+ // the warning the next time the member is accessed.
+ if (!deny_access) {
+ MaybeWhitelistMember(runtime, member);
}
}
- if (action == kDeny) {
- // Block access
- return action;
- }
-
- // Allow access to this member but print a warning.
- DCHECK(action == kAllowButWarn || action == kAllowButWarnAndToast);
-
- if (access_method != kNone) {
- // Depending on a runtime flag, we might move the member into whitelist and
- // skip the warning the next time the member is accessed.
- MaybeWhitelistMember(runtime, member);
-
- // If this action requires a UI warning, set the appropriate flag.
- if (shouldWarn &&
- (action == kAllowButWarnAndToast || runtime->ShouldAlwaysSetHiddenApiWarningFlag())) {
- runtime->SetPendingHiddenApiWarning(true);
- }
- }
-
- return action;
+ return deny_access;
}
// Need to instantiate this.
-template Action GetMemberActionImpl<ArtField>(ArtField* member,
- hiddenapi::ApiList api_list,
- Action action,
- AccessMethod access_method);
-template Action GetMemberActionImpl<ArtMethod>(ArtMethod* member,
- hiddenapi::ApiList api_list,
- Action action,
- AccessMethod access_method);
+template bool ShouldDenyAccessToMemberImpl<ArtField>(ArtField* member,
+ hiddenapi::ApiList api_list,
+ AccessMethod access_method);
+template bool ShouldDenyAccessToMemberImpl<ArtMethod>(ArtMethod* member,
+ hiddenapi::ApiList api_list,
+ AccessMethod access_method);
} // namespace detail
-template<typename T>
-void NotifyHiddenApiListener(T* member) {
- Runtime* runtime = Runtime::Current();
- if (!runtime->IsAotCompiler()) {
- ScopedObjectAccessUnchecked soa(Thread::Current());
-
- ScopedLocalRef<jobject> consumer_object(soa.Env(),
- soa.Env()->GetStaticObjectField(
- WellKnownClasses::dalvik_system_VMRuntime,
- WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer));
- // If the consumer is non-null, we call back to it to let it know that we
- // have encountered an API that's in one of our lists.
- if (consumer_object != nullptr) {
- detail::MemberSignature member_signature(member);
- std::ostringstream member_signature_str;
- member_signature.Dump(member_signature_str);
-
- ScopedLocalRef<jobject> signature_str(
- soa.Env(),
- soa.Env()->NewStringUTF(member_signature_str.str().c_str()));
-
- // Call through to Consumer.accept(String memberSignature);
- soa.Env()->CallVoidMethod(consumer_object.get(),
- WellKnownClasses::java_util_function_Consumer_accept,
- signature_str.get());
- }
- }
-}
-
-template void NotifyHiddenApiListener<ArtMethod>(ArtMethod* member);
-template void NotifyHiddenApiListener<ArtField>(ArtField* member);
-
} // namespace hiddenapi
} // namespace art
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index 57f1a59..ed00e2a 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -32,11 +32,10 @@
// This must be kept in sync with ApplicationInfo.ApiEnforcementPolicy in
// frameworks/base/core/java/android/content/pm/ApplicationInfo.java
enum class EnforcementPolicy {
- kNoChecks = 0,
+ kDisabled = 0,
kJustWarn = 1, // keep checks enabled, but allow everything (enables logging)
- kDarkGreyAndBlackList = 2, // ban dark grey & blacklist
- kBlacklistOnly = 3, // ban blacklist violations only
- kMax = kBlacklistOnly,
+ kEnabled = 2, // ban dark grey & blacklist
+ kMax = kEnabled,
};
inline EnforcementPolicy EnforcementPolicyFromInt(int api_policy_int) {
@@ -45,56 +44,59 @@
return static_cast<EnforcementPolicy>(api_policy_int);
}
-enum Action {
- kAllow,
- kAllowButWarn,
- kAllowButWarnAndToast,
- kDeny
-};
-
-enum AccessMethod {
+enum class AccessMethod {
kNone, // internal test that does not correspond to an actual access by app
kReflection,
kJNI,
kLinking,
};
-// Do not change the values of items in this enum, as they are written to the
-// event log for offline analysis. Any changes will interfere with that analysis.
-enum AccessContextFlags {
- // Accessed member is a field if this bit is set, else a method
- kMemberIsField = 1 << 0,
- // Indicates if access was denied to the member, instead of just printing a warning.
- kAccessDenied = 1 << 1,
+struct AccessContext {
+ public:
+ explicit AccessContext(bool is_trusted) : is_trusted_(is_trusted) {}
+
+ explicit AccessContext(ObjPtr<mirror::Class> klass) : is_trusted_(GetIsTrusted(klass)) {}
+
+ AccessContext(ObjPtr<mirror::ClassLoader> class_loader, ObjPtr<mirror::DexCache> dex_cache)
+ : is_trusted_(GetIsTrusted(class_loader, dex_cache)) {}
+
+ bool IsTrusted() const { return is_trusted_; }
+
+ private:
+ static bool GetIsTrusted(ObjPtr<mirror::ClassLoader> class_loader,
+ ObjPtr<mirror::DexCache> dex_cache)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Trust if the caller is in is boot class loader.
+ if (class_loader.IsNull()) {
+ return true;
+ }
+
+ // Trust if caller is in a platform dex file.
+ if (!dex_cache.IsNull()) {
+ const DexFile* dex_file = dex_cache->GetDexFile();
+ if (dex_file != nullptr && dex_file->IsPlatformDexFile()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ static bool GetIsTrusted(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(!klass.IsNull());
+
+ if (klass->ShouldSkipHiddenApiChecks() && Runtime::Current()->IsJavaDebuggable()) {
+ // Class is known, it is marked trusted and we are in debuggable mode.
+ return true;
+ }
+
+ // Check other aspects of the context.
+ return GetIsTrusted(klass->GetClassLoader(), klass->GetDexCache());
+ }
+
+ bool is_trusted_;
};
-inline Action GetActionFromAccessFlags(ApiList api_list) {
- if (api_list == ApiList::kWhitelist) {
- return kAllow;
- }
-
- EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy();
- if (policy == EnforcementPolicy::kNoChecks) {
- // Exit early. Nothing to enforce.
- return kAllow;
- }
-
- // if policy is "just warn", always warn. We returned above for whitelist APIs.
- if (policy == EnforcementPolicy::kJustWarn) {
- return kAllowButWarn;
- }
- DCHECK(policy >= EnforcementPolicy::kDarkGreyAndBlackList);
- // The logic below relies on equality of values in the enums EnforcementPolicy and
- // ApiList, and their ordering. Assertions are in hidden_api.cc.
- if (static_cast<int>(policy) > static_cast<int>(api_list)) {
- return api_list == ApiList::kDarkGreylist
- ? kAllowButWarnAndToast
- : kAllowButWarn;
- } else {
- return kDeny;
- }
-}
-
class ScopedHiddenApiEnforcementPolicySetting {
public:
explicit ScopedHiddenApiEnforcementPolicySetting(EnforcementPolicy new_policy)
@@ -114,6 +116,13 @@
// Implementation details. DO NOT ACCESS DIRECTLY.
namespace detail {
+enum class SdkCodes {
+ kVersionNone = std::numeric_limits<int32_t>::min(),
+ kVersionUnlimited = std::numeric_limits<int32_t>::max(),
+ kVersionO_MR1 = 27,
+ kVersionP = 28,
+};
+
// Class to encapsulate the signature of a member (ArtField or ArtMethod). This
// is used as a helper when matching prefixes, and when logging the signature.
class MemberSignature {
@@ -146,59 +155,31 @@
void WarnAboutAccess(AccessMethod access_method, ApiList list);
- void LogAccessToEventLog(AccessMethod access_method, Action action_taken);
+ void LogAccessToEventLog(AccessMethod access_method, bool access_denied);
+
+ // Calls back into managed code to notify VMRuntime.nonSdkApiUsageConsumer that
+ // |member| was accessed. This is usually called when an API is on the black,
+ // dark grey or light grey lists. Given that the callback can execute arbitrary
+ // code, a call to this method can result in thread suspension.
+ void NotifyHiddenApiListener(AccessMethod access_method);
};
template<typename T>
-Action GetMemberActionImpl(T* member,
- ApiList api_list,
- Action action,
- AccessMethod access_method)
+bool ShouldDenyAccessToMemberImpl(T* member, ApiList api_list, AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_);
-// Returns true if the caller is either loaded by the boot strap class loader or comes from
-// a dex file located in ${ANDROID_ROOT}/framework/.
-ALWAYS_INLINE
-inline bool IsCallerTrusted(ObjPtr<mirror::Class> caller,
- ObjPtr<mirror::ClassLoader> caller_class_loader,
- ObjPtr<mirror::DexCache> caller_dex_cache)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- if (caller_class_loader.IsNull()) {
- // Boot class loader.
- return true;
- }
-
- if (!caller_dex_cache.IsNull()) {
- const DexFile* caller_dex_file = caller_dex_cache->GetDexFile();
- if (caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile()) {
- // Caller is in a platform dex file.
- return true;
- }
- }
-
- if (!caller.IsNull() &&
- caller->ShouldSkipHiddenApiChecks() &&
- Runtime::Current()->IsJavaDebuggable()) {
- // We are in debuggable mode and this caller has been marked trusted.
- return true;
- }
-
- return false;
-}
-
} // namespace detail
-// Returns true if access to `member` should be denied to the caller of the
-// reflective query. The decision is based on whether the caller is trusted or
-// not. Because different users of this function determine this in a different
-// way, `fn_caller_is_trusted(self)` is called and should return true if the
-// caller is allowed to access the platform.
+// Returns true if access to `member` should be denied in the given context.
+// The decision is based on whether the caller is in a trusted context or not.
+// Because determining the access context can be expensive, a lambda function
+// "fn_get_access_context" is lazily invoked after other criteria have been
+// considered.
// This function might print warnings into the log if the member is hidden.
template<typename T>
-inline Action GetMemberAction(T* member,
- Thread* self,
- std::function<bool(Thread*)> fn_caller_is_trusted,
- AccessMethod access_method)
+inline bool ShouldDenyAccessToMember(T* member,
+ std::function<AccessContext()> fn_get_access_context,
+ AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(member != nullptr);
@@ -210,53 +191,34 @@
// results, e.g. print whitelist warnings (b/78327881).
ApiList api_list = member->GetHiddenApiAccessFlags();
- Action action = GetActionFromAccessFlags(member->GetHiddenApiAccessFlags());
- if (action == kAllow) {
- // Nothing to do.
- return action;
+ // Exit early if member is on the whitelist.
+ if (api_list == ApiList::kWhitelist) {
+ return false;
}
- // Member is hidden. Invoke `fn_caller_in_platform` and find the origin of the access.
+ // Check if caller is exempted from access checks.
// This can be *very* expensive. Save it for last.
- if (fn_caller_is_trusted(self)) {
- // Caller is trusted. Exit.
- return kAllow;
+ if (fn_get_access_context().IsTrusted()) {
+ return false;
}
- // Member is hidden and caller is not in the platform.
- return detail::GetMemberActionImpl(member, api_list, action, access_method);
+ // Member is hidden and caller is not exempted. Enter slow path.
+ return detail::ShouldDenyAccessToMemberImpl(member, api_list, access_method);
}
-inline bool IsCallerTrusted(ObjPtr<mirror::Class> caller) REQUIRES_SHARED(Locks::mutator_lock_) {
- return !caller.IsNull() &&
- detail::IsCallerTrusted(caller, caller->GetClassLoader(), caller->GetDexCache());
-}
-
-// Returns true if access to `member` should be denied to a caller loaded with
-// `caller_class_loader`.
-// This function might print warnings into the log if the member is hidden.
+// Helper method for callers where access context can be determined beforehand.
+// Wraps AccessContext in a lambda and passes it to the real ShouldDenyAccessToMember.
template<typename T>
-inline Action GetMemberAction(T* member,
- ObjPtr<mirror::ClassLoader> caller_class_loader,
- ObjPtr<mirror::DexCache> caller_dex_cache,
- AccessMethod access_method)
+inline bool ShouldDenyAccessToMember(T* member,
+ AccessContext access_context,
+ AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_) {
- bool is_caller_trusted =
- detail::IsCallerTrusted(/* caller= */ nullptr, caller_class_loader, caller_dex_cache);
- return GetMemberAction(member,
- /* thread= */ nullptr,
- [is_caller_trusted] (Thread*) { return is_caller_trusted; },
- access_method);
+ return ShouldDenyAccessToMember(
+ member,
+ [&] () REQUIRES_SHARED(Locks::mutator_lock_) { return access_context; },
+ access_method);
}
-// Calls back into managed code to notify VMRuntime.nonSdkApiUsageConsumer that
-// |member| was accessed. This is usually called when an API is on the black,
-// dark grey or light grey lists. Given that the callback can execute arbitrary
-// code, a call to this method can result in thread suspension.
-template<typename T> void NotifyHiddenApiListener(T* member)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
-
} // namespace hiddenapi
} // namespace art
diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc
index 1727af0..627d9a7 100644
--- a/runtime/hidden_api_test.cc
+++ b/runtime/hidden_api_test.cc
@@ -23,7 +23,7 @@
namespace art {
using hiddenapi::detail::MemberSignature;
-using hiddenapi::GetActionFromAccessFlags;
+using hiddenapi::detail::ShouldDenyAccessToMemberImpl;
class HiddenApiTest : public CommonRuntimeTest {
protected:
@@ -68,6 +68,15 @@
return art_field;
}
+ bool ShouldDenyAccess(hiddenapi::ApiList list) REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Choose parameters such that there are no side effects (AccessMethod::kNone)
+ // and that the member is not on the exemptions list (here we choose one which
+ // is not even in boot class path).
+ return ShouldDenyAccessToMemberImpl(/* member= */ class1_field1_,
+ list,
+ /* access_method= */ hiddenapi::AccessMethod::kNone);
+ }
+
protected:
Thread* self_;
jobject jclass_loader_;
@@ -88,41 +97,33 @@
};
TEST_F(HiddenApiTest, CheckGetActionFromRuntimeFlags) {
- runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kNoChecks);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kWhitelist), hiddenapi::kAllow);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kLightGreylist), hiddenapi::kAllow);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kDarkGreylist), hiddenapi::kAllow);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kBlacklist), hiddenapi::kAllow);
+ ScopedObjectAccess soa(self_);
+
+ runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kDisabled);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kWhitelist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kLightGreylist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kDarkGreylist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kBlacklist), false);
runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kJustWarn);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kWhitelist),
- hiddenapi::kAllow);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kLightGreylist),
- hiddenapi::kAllowButWarn);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kDarkGreylist),
- hiddenapi::kAllowButWarn);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kBlacklist),
- hiddenapi::kAllowButWarn);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kWhitelist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kLightGreylist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kDarkGreylist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kBlacklist), false);
- runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kDarkGreyAndBlackList);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kWhitelist),
- hiddenapi::kAllow);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kLightGreylist),
- hiddenapi::kAllowButWarn);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kDarkGreylist),
- hiddenapi::kDeny);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kBlacklist),
- hiddenapi::kDeny);
+ runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
+ runtime_->SetTargetSdkVersion(static_cast<int32_t>(hiddenapi::detail::SdkCodes::kVersionO_MR1));
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kWhitelist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kLightGreylist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kDarkGreylist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kBlacklist), true);
- runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kBlacklistOnly);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kWhitelist),
- hiddenapi::kAllow);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kLightGreylist),
- hiddenapi::kAllowButWarn);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kDarkGreylist),
- hiddenapi::kAllowButWarnAndToast);
- ASSERT_EQ(GetActionFromAccessFlags(hiddenapi::ApiList::kBlacklist),
- hiddenapi::kDeny);
+ runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
+ runtime_->SetTargetSdkVersion(static_cast<int32_t>(hiddenapi::detail::SdkCodes::kVersionP));
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kWhitelist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kLightGreylist), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kDarkGreylist), true);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kBlacklist), true);
}
TEST_F(HiddenApiTest, CheckMembersRead) {
diff --git a/runtime/image.cc b/runtime/image.cc
index 376742a..59ac283 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -26,7 +26,7 @@
namespace art {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '6', '6', '\0' }; // Add metadata section.
+const uint8_t ImageHeader::kImageVersion[] = { '0', '6', '7', '\0' }; // Added CRC32 intrinsic
ImageHeader::ImageHeader(uint32_t image_begin,
uint32_t image_size,
diff --git a/runtime/intern_table-inl.h b/runtime/intern_table-inl.h
index 3c7da09..6fc53e9 100644
--- a/runtime/intern_table-inl.h
+++ b/runtime/intern_table-inl.h
@@ -77,7 +77,7 @@
bool visit_boot_images,
bool visit_non_boot_images) {
auto visit_tables = [&](std::vector<Table::InternalTable>& tables)
- REQUIRES_SHARED(Locks::mutator_lock_) {
+ NO_THREAD_SAFETY_ANALYSIS {
for (Table::InternalTable& table : tables) {
// Determine if we want to visit the table based on the flags..
const bool visit =
@@ -94,6 +94,26 @@
visit_tables(weak_interns_.tables_);
}
+inline size_t InternTable::CountInterns(bool visit_boot_images,
+ bool visit_non_boot_images) const {
+ size_t ret = 0u;
+ auto visit_tables = [&](const std::vector<Table::InternalTable>& tables)
+ NO_THREAD_SAFETY_ANALYSIS {
+ for (const Table::InternalTable& table : tables) {
+ // Determine if we want to visit the table based on the flags..
+ const bool visit =
+ (visit_boot_images && table.IsBootImage()) ||
+ (visit_non_boot_images && !table.IsBootImage());
+ if (visit) {
+ ret += table.set_.size();
+ }
+ }
+ };
+ visit_tables(strong_interns_.tables_);
+ visit_tables(weak_interns_.tables_);
+ return ret;
+}
+
} // namespace art
#endif // ART_RUNTIME_INTERN_TABLE_INL_H_
diff --git a/runtime/intern_table.h b/runtime/intern_table.h
index baeb1a9..165d56c 100644
--- a/runtime/intern_table.h
+++ b/runtime/intern_table.h
@@ -174,6 +174,10 @@
bool visit_non_boot_images)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_);
+ // Count the number of intern strings in the table.
+ size_t CountInterns(bool visit_boot_images, bool visit_non_boot_images) const
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::intern_table_lock_);
+
void DumpForSigQuit(std::ostream& os) const REQUIRES(!Locks::intern_table_lock_);
void BroadcastForNewInterns();
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 5784b9b..d004d64 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -237,7 +237,11 @@
kMterpImplKind // Assembly interpreter
};
+#if ART_USE_CXX_INTERPRETER
+static constexpr InterpreterImplKind kInterpreterImplKind = kSwitchImplKind;
+#else
static constexpr InterpreterImplKind kInterpreterImplKind = kMterpImplKind;
+#endif
static inline JValue Execute(
Thread* self,
diff --git a/runtime/interpreter/interpreter_intrinsics.cc b/runtime/interpreter/interpreter_intrinsics.cc
index 17b3cd4..24a026a 100644
--- a/runtime/interpreter/interpreter_intrinsics.cc
+++ b/runtime/interpreter/interpreter_intrinsics.cc
@@ -558,6 +558,7 @@
UNIMPLEMENTED_CASE(ReferenceGetReferent /* ()Ljava/lang/Object; */)
UNIMPLEMENTED_CASE(IntegerValueOf /* (I)Ljava/lang/Integer; */)
UNIMPLEMENTED_CASE(ThreadInterrupted /* ()Z */)
+ UNIMPLEMENTED_CASE(CRC32Update /* (II)I */)
INTRINSIC_CASE(VarHandleFullFence)
INTRINSIC_CASE(VarHandleAcquireFence)
INTRINSIC_CASE(VarHandleReleaseFence)
diff --git a/runtime/interpreter/interpreter_switch_impl-inl.h b/runtime/interpreter/interpreter_switch_impl-inl.h
index 48e1728..94cb3de 100644
--- a/runtime/interpreter/interpreter_switch_impl-inl.h
+++ b/runtime/interpreter/interpreter_switch_impl-inl.h
@@ -23,6 +23,7 @@
#include "base/memory_tool.h"
#include "base/quasi_atomic.h"
#include "dex/dex_file_types.h"
+#include "dex/dex_instruction_list.h"
#include "experimental_flags.h"
#include "interpreter_common.h"
#include "jit/jit.h"
@@ -36,237 +37,2547 @@
namespace art {
namespace interpreter {
-#define CHECK_FORCE_RETURN() \
- { \
- if (UNLIKELY(shadow_frame.GetForcePopFrame())) { \
- DCHECK(PrevFrameWillRetry(self, shadow_frame)) \
- << "Pop frame forced without previous frame ready to retry instruction!"; \
- DCHECK(Runtime::Current()->AreNonStandardExitsEnabled()); \
- if (UNLIKELY(NeedsMethodExitEvent(instrumentation))) { \
- SendMethodExitEvents(self, \
- instrumentation, \
- shadow_frame, \
- shadow_frame.GetThisObject(accessor.InsSize()), \
- shadow_frame.GetMethod(), \
- inst->GetDexPc(insns), \
- JValue()); \
- } \
- ctx->result = JValue(); /* Handled in caller. */ \
- return; \
- } \
- } \
- do {} while (false)
-
-#define HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(instr) \
- { \
- DCHECK(self->IsExceptionPending()); \
- self->AllowThreadSuspension(); \
- CHECK_FORCE_RETURN(); \
- if (!MoveToExceptionHandler(self, shadow_frame, instr)) { \
- /* Structured locking is to be enforced for abnormal termination, too. */ \
- DoMonitorCheckOnExit<do_assignability_check>(self, &shadow_frame); \
- if (interpret_one_instruction) { \
- /* Signal mterp to return to caller */ \
- shadow_frame.SetDexPC(dex::kDexNoIndex); \
- } \
- ctx->result = JValue(); /* Handled in caller. */ \
- return; \
- } else { \
- CHECK_FORCE_RETURN(); \
- int32_t displacement = \
- static_cast<int32_t>(shadow_frame.GetDexPC()) - static_cast<int32_t>(dex_pc); \
- inst = inst->RelativeAt(displacement); \
- break; /* Stop executing this opcode and continue in the exception handler. */ \
- } \
- } \
- do {} while (false)
-
-#define HANDLE_PENDING_EXCEPTION() HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(instrumentation)
-
-#define POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_IMPL(_is_exception_pending, _next_function) \
- { \
- if (UNLIKELY(shadow_frame.GetForceRetryInstruction())) { \
- /* Don't need to do anything except clear the flag and exception. We leave the */ \
- /* instruction the same so it will be re-executed on the next go-around. */ \
- DCHECK(inst->IsInvoke()); \
- shadow_frame.SetForceRetryInstruction(false); \
- if (UNLIKELY(_is_exception_pending)) { \
- DCHECK(self->IsExceptionPending()); \
- if (kIsDebugBuild) { \
- LOG(WARNING) << "Suppressing exception for instruction-retry: " \
- << self->GetException()->Dump(); \
- } \
- self->ClearException(); \
- } \
- } else if (UNLIKELY(_is_exception_pending)) { \
- /* Should have succeeded. */ \
- DCHECK(!shadow_frame.GetForceRetryInstruction()); \
- HANDLE_PENDING_EXCEPTION(); \
- } else { \
- inst = inst->_next_function(); \
- } \
- } \
- do {} while (false)
-
-#define POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_POLYMORPHIC(_is_exception_pending) \
- POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_IMPL(_is_exception_pending, Next_4xx)
-#define POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(_is_exception_pending) \
- POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_IMPL(_is_exception_pending, Next_3xx)
-
-#define POSSIBLY_HANDLE_PENDING_EXCEPTION(_is_exception_pending, _next_function) \
- { \
- /* Should only be on invoke instructions. */ \
- DCHECK(!shadow_frame.GetForceRetryInstruction()); \
- if (UNLIKELY(_is_exception_pending)) { \
- HANDLE_PENDING_EXCEPTION(); \
- } else { \
- inst = inst->_next_function(); \
- } \
- } \
- do {} while (false)
-
-#define HANDLE_MONITOR_CHECKS() \
- if (!DoMonitorCheckOnExit<do_assignability_check>(self, &shadow_frame)) { \
- HANDLE_PENDING_EXCEPTION(); \
- }
-
-// Code to run before each dex instruction.
-#define PREAMBLE_SAVE(save_ref) \
- { \
- /* We need to put this before & after the instrumentation to avoid having to put in a */ \
- /* post-script macro. */ \
- CHECK_FORCE_RETURN(); \
- if (UNLIKELY(instrumentation->HasDexPcListeners())) { \
- if (UNLIKELY(!DoDexPcMoveEvent(self, \
- accessor, \
- shadow_frame, \
- dex_pc, \
- instrumentation, \
- save_ref))) { \
- HANDLE_PENDING_EXCEPTION(); \
- break; \
- } \
- CHECK_FORCE_RETURN(); \
- } \
- } \
- do {} while (false)
-
-#define PREAMBLE() PREAMBLE_SAVE(nullptr)
-
-#define BRANCH_INSTRUMENTATION(offset) \
- { \
- if (UNLIKELY(instrumentation->HasBranchListeners())) { \
- instrumentation->Branch(self, shadow_frame.GetMethod(), dex_pc, offset); \
- } \
- JValue result; \
- if (jit::Jit::MaybeDoOnStackReplacement(self, \
- shadow_frame.GetMethod(), \
- dex_pc, \
- offset, \
- &result)) { \
- if (interpret_one_instruction) { \
- /* OSR has completed execution of the method. Signal mterp to return to caller */ \
- shadow_frame.SetDexPC(dex::kDexNoIndex); \
- } \
- ctx->result = result; \
- return; \
- } \
- } \
- do {} while (false)
-
-#define HOTNESS_UPDATE() \
- { \
- if (jit != nullptr) { \
- jit->AddSamples(self, shadow_frame.GetMethod(), 1, /*with_backedges=*/ true); \
- } \
- } \
- do {} while (false)
-
-#define HANDLE_ASYNC_EXCEPTION() \
- if (UNLIKELY(self->ObserveAsyncException())) { \
- HANDLE_PENDING_EXCEPTION(); \
- } \
- do {} while (false)
-
-#define HANDLE_BACKWARD_BRANCH(offset) \
- { \
- if (IsBackwardBranch(offset)) { \
- HOTNESS_UPDATE(); \
- /* Record new dex pc early to have consistent suspend point at loop header. */ \
- shadow_frame.SetDexPC(inst->GetDexPc(insns)); \
- self->AllowThreadSuspension(); \
- } \
- } \
- do {} 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.
-NO_INLINE static bool DoDexPcMoveEvent(Thread* self,
- const CodeItemDataAccessor& accessor,
- 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(accessor.InsSize()),
- 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());
+// Short-lived helper class which executes single DEX bytecode. It is inlined by compiler.
+//
+// The function names must match the names from dex_instruction_list.h and have no arguments.
+//
+// Any relevant execution information is stored in the fields - it should be kept to minimum.
+//
+// Helper methods may return boolean value - in which case 'false' always means
+// "stop executing current opcode" (which does not necessarily exit the interpreter loop).
+//
+template<bool do_access_check, bool transaction_active>
+class InstructionHandler {
+ public:
+ ALWAYS_INLINE WARN_UNUSED bool CheckForceReturn()
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (UNLIKELY(shadow_frame.GetForcePopFrame())) {
+ DCHECK(PrevFrameWillRetry(self, shadow_frame))
+ << "Pop frame forced without previous frame ready to retry instruction!";
+ DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
+ if (UNLIKELY(NeedsMethodExitEvent(instrumentation))) {
+ SendMethodExitEvents(self,
+ instrumentation,
+ shadow_frame,
+ shadow_frame.GetThisObject(Accessor().InsSize()),
+ shadow_frame.GetMethod(),
+ inst->GetDexPc(Insns()),
+ JValue());
+ }
+ ctx->result = JValue(); /* Handled in caller. */
+ exit_interpreter_loop = true;
+ return false;
}
return true;
}
-}
-static bool NeedsMethodExitEvent(const instrumentation::Instrumentation* ins)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- return ins->HasMethodExitListeners() || ins->HasWatchedFramePopListeners();
-}
+ NO_INLINE WARN_UNUSED bool HandlePendingExceptionWithInstrumentationImpl(
+ const instrumentation::Instrumentation* instr)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(self->IsExceptionPending());
+ self->AllowThreadSuspension();
+ if (!CheckForceReturn()) {
+ return false;
+ }
+ if (!MoveToExceptionHandler(self, shadow_frame, instr)) {
+ /* Structured locking is to be enforced for abnormal termination, too. */
+ DoMonitorCheckOnExit<do_assignability_check>(self, &shadow_frame);
+ if (ctx->interpret_one_instruction) {
+ /* Signal mterp to return to caller */
+ shadow_frame.SetDexPC(dex::kDexNoIndex);
+ }
+ ctx->result = JValue(); /* Handled in caller. */
+ exit_interpreter_loop = true;
+ return false; // Return to caller.
+ }
+ if (!CheckForceReturn()) {
+ return false;
+ }
+ int32_t displacement =
+ static_cast<int32_t>(shadow_frame.GetDexPC()) - static_cast<int32_t>(dex_pc);
+ inst = inst->RelativeAt(displacement);
+ return false; // Stop executing this opcode and continue in the exception handler.
+ }
-// Sends the normal method exit event. Returns true if the events succeeded and false if there is a
-// pending exception.
-NO_INLINE static bool SendMethodExitEvents(Thread* self,
- const instrumentation::Instrumentation* instrumentation,
- const ShadowFrame& frame,
- ObjPtr<mirror::Object> thiz,
- ArtMethod* method,
- uint32_t dex_pc,
- const JValue& result)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- bool had_event = false;
- // We don't send method-exit if it's a pop-frame. We still send frame_popped though.
- if (UNLIKELY(instrumentation->HasMethodExitListeners() && !frame.GetForcePopFrame())) {
- had_event = true;
- instrumentation->MethodExitEvent(self, thiz.Ptr(), method, dex_pc, result);
+ // Forwards the call to the NO_INLINE HandlePendingExceptionWithInstrumentationImpl.
+ ALWAYS_INLINE WARN_UNUSED bool HandlePendingExceptionWithInstrumentation(
+ const instrumentation::Instrumentation* instr)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // We need to help the compiler a bit to make the NO_INLINE call efficient.
+ // * All handler fields should be in registers, so we do not want to take the object
+ // address (for 'this' argument). Make a copy of the handler just for the slow path.
+ // * The modifiable fields should also be in registers, so we don't want to store their
+ // address even in the handler copy. Make a copy of them just for the call as well.
+ const Instruction* inst_copy = inst;
+ bool exit_loop_copy = exit_interpreter_loop;
+ InstructionHandler<do_access_check, transaction_active> handler_copy(
+ ctx, instrumentation, self, shadow_frame, dex_pc, inst_copy, inst_data, exit_loop_copy);
+ bool result = handler_copy.HandlePendingExceptionWithInstrumentationImpl(instr);
+ inst = inst_copy;
+ exit_interpreter_loop = exit_loop_copy;
+ return result;
}
- if (UNLIKELY(frame.NeedsNotifyPop() && instrumentation->HasWatchedFramePopListeners())) {
- had_event = true;
- instrumentation->WatchedFramePopped(self, frame);
+
+ ALWAYS_INLINE WARN_UNUSED bool HandlePendingException()
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return HandlePendingExceptionWithInstrumentation(instrumentation);
}
- if (UNLIKELY(had_event)) {
- return !self->IsExceptionPending();
- } else {
+
+ ALWAYS_INLINE WARN_UNUSED bool PossiblyHandlePendingExceptionOnInvokeImpl(
+ bool is_exception_pending,
+ const Instruction* next_inst)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (UNLIKELY(shadow_frame.GetForceRetryInstruction())) {
+ /* Don't need to do anything except clear the flag and exception. We leave the */
+ /* instruction the same so it will be re-executed on the next go-around. */
+ DCHECK(inst->IsInvoke());
+ shadow_frame.SetForceRetryInstruction(false);
+ if (UNLIKELY(is_exception_pending)) {
+ DCHECK(self->IsExceptionPending());
+ if (kIsDebugBuild) {
+ LOG(WARNING) << "Suppressing exception for instruction-retry: "
+ << self->GetException()->Dump();
+ }
+ self->ClearException();
+ }
+ } else if (UNLIKELY(is_exception_pending)) {
+ /* Should have succeeded. */
+ DCHECK(!shadow_frame.GetForceRetryInstruction());
+ if (!HandlePendingException()) {
+ return false;
+ }
+ } else {
+ inst = next_inst;
+ }
return true;
}
-}
+
+ ALWAYS_INLINE WARN_UNUSED bool PossiblyHandlePendingException(
+ bool is_exception_pending,
+ const Instruction* next_inst)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ /* Should only be on invoke instructions. */
+ DCHECK(!shadow_frame.GetForceRetryInstruction());
+ if (UNLIKELY(is_exception_pending)) {
+ if (!HandlePendingException()) {
+ return false;
+ }
+ } else {
+ inst = next_inst;
+ }
+ return true;
+ }
+
+ ALWAYS_INLINE WARN_UNUSED bool HandleMonitorChecks()
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!DoMonitorCheckOnExit<do_assignability_check>(self, &shadow_frame)) {
+ if (!HandlePendingException()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // Code to run before each dex instruction.
+ ALWAYS_INLINE WARN_UNUSED bool Preamble()
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ /* We need to put this before & after the instrumentation to avoid having to put in a */
+ /* post-script macro. */
+ if (!CheckForceReturn()) {
+ return false;
+ }
+ if (UNLIKELY(instrumentation->HasDexPcListeners())) {
+ uint8_t opcode = inst->Opcode(inst_data);
+ bool is_move_result_object = (opcode == Instruction::MOVE_RESULT_OBJECT);
+ JValue* save_ref = is_move_result_object ? &ctx->result_register : nullptr;
+ if (UNLIKELY(!DoDexPcMoveEvent(self,
+ Accessor(),
+ shadow_frame,
+ dex_pc,
+ instrumentation,
+ save_ref))) {
+ if (!HandlePendingException()) {
+ return false;
+ }
+ }
+ if (!CheckForceReturn()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ ALWAYS_INLINE WARN_UNUSED bool BranchInstrumentation(int32_t offset)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (UNLIKELY(instrumentation->HasBranchListeners())) {
+ instrumentation->Branch(self, shadow_frame.GetMethod(), dex_pc, offset);
+ }
+ JValue result;
+ if (jit::Jit::MaybeDoOnStackReplacement(self,
+ shadow_frame.GetMethod(),
+ dex_pc,
+ offset,
+ &result)) {
+ if (ctx->interpret_one_instruction) {
+ /* OSR has completed execution of the method. Signal mterp to return to caller */
+ shadow_frame.SetDexPC(dex::kDexNoIndex);
+ }
+ ctx->result = result;
+ exit_interpreter_loop = true;
+ return false;
+ }
+ return true;
+ }
+
+ ALWAYS_INLINE void HotnessUpdate()
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ if (jit != nullptr) {
+ jit->AddSamples(self, shadow_frame.GetMethod(), 1, /*with_backedges=*/ true);
+ }
+ }
+
+ ALWAYS_INLINE WARN_UNUSED bool HandleAsyncException()
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (UNLIKELY(self->ObserveAsyncException())) {
+ if (!HandlePendingException()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ ALWAYS_INLINE void HandleBackwardBranch(int32_t offset)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (IsBackwardBranch(offset)) {
+ HotnessUpdate();
+ /* Record new dex pc early to have consistent suspend point at loop header. */
+ shadow_frame.SetDexPC(inst->GetDexPc(Insns()));
+ self->AllowThreadSuspension();
+ }
+ }
+
+ // 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.
+ NO_INLINE static bool DoDexPcMoveEvent(Thread* self,
+ const CodeItemDataAccessor& accessor,
+ 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(accessor.InsSize()),
+ 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;
+ }
+ }
+
+ static bool NeedsMethodExitEvent(const instrumentation::Instrumentation* ins)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return ins->HasMethodExitListeners() || ins->HasWatchedFramePopListeners();
+ }
+
+ // Sends the normal method exit event.
+ // Returns true if the events succeeded and false if there is a pending exception.
+ NO_INLINE static bool SendMethodExitEvents(
+ Thread* self,
+ const instrumentation::Instrumentation* instrumentation,
+ const ShadowFrame& frame,
+ ObjPtr<mirror::Object> thiz,
+ ArtMethod* method,
+ uint32_t dex_pc,
+ const JValue& result)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool had_event = false;
+ // We don't send method-exit if it's a pop-frame. We still send frame_popped though.
+ if (UNLIKELY(instrumentation->HasMethodExitListeners() && !frame.GetForcePopFrame())) {
+ had_event = true;
+ instrumentation->MethodExitEvent(self, thiz.Ptr(), method, dex_pc, result);
+ }
+ if (UNLIKELY(frame.NeedsNotifyPop() && instrumentation->HasWatchedFramePopListeners())) {
+ had_event = true;
+ instrumentation->WatchedFramePopped(self, frame);
+ }
+ if (UNLIKELY(had_event)) {
+ return !self->IsExceptionPending();
+ } else {
+ return true;
+ }
+ }
+
+#define BRANCH_INSTRUMENTATION(offset) \
+ if (!BranchInstrumentation(offset)) { \
+ return; \
+ }
+
+#define HANDLE_PENDING_EXCEPTION() \
+ if (!HandlePendingException()) { \
+ return; \
+ }
+
+#define POSSIBLY_HANDLE_PENDING_EXCEPTION(is_exception_pending, next_function) \
+ if (!PossiblyHandlePendingException(is_exception_pending, inst->next_function())) { \
+ return; \
+ }
+
+#define POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_POLYMORPHIC(is_exception_pending) \
+ if (!PossiblyHandlePendingExceptionOnInvokeImpl(is_exception_pending, inst->Next_4xx())) { \
+ return; \
+ }
+
+#define POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(is_exception_pending) \
+ if (!PossiblyHandlePendingExceptionOnInvokeImpl(is_exception_pending, inst->Next_3xx())) { \
+ return; \
+ }
+
+ ALWAYS_INLINE void NOP() REQUIRES_SHARED(Locks::mutator_lock_) {
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void MOVE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void MOVE_FROM16() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_22x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22x()));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void MOVE_16() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_32x(),
+ shadow_frame.GetVReg(inst->VRegB_32x()));
+ inst = inst->Next_3xx();
+ }
+
+ ALWAYS_INLINE void MOVE_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void MOVE_WIDE_FROM16() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegLong(inst->VRegA_22x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_22x()));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void MOVE_WIDE_16() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegLong(inst->VRegA_32x(),
+ shadow_frame.GetVRegLong(inst->VRegB_32x()));
+ inst = inst->Next_3xx();
+ }
+
+ ALWAYS_INLINE void MOVE_OBJECT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegReference(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVRegReference(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void MOVE_OBJECT_FROM16() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegReference(inst->VRegA_22x(inst_data),
+ shadow_frame.GetVRegReference(inst->VRegB_22x()));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void MOVE_OBJECT_16() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegReference(inst->VRegA_32x(),
+ shadow_frame.GetVRegReference(inst->VRegB_32x()));
+ inst = inst->Next_3xx();
+ }
+
+ ALWAYS_INLINE void MOVE_RESULT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_11x(inst_data), ResultRegister()->GetI());
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void MOVE_RESULT_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegLong(inst->VRegA_11x(inst_data), ResultRegister()->GetJ());
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void MOVE_RESULT_OBJECT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), ResultRegister()->GetL());
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void MOVE_EXCEPTION() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Throwable> exception = self->GetException();
+ DCHECK(exception != nullptr) << "No pending exception on MOVE_EXCEPTION instruction";
+ shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), exception);
+ self->ClearException();
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void RETURN_VOID_NO_BARRIER() REQUIRES_SHARED(Locks::mutator_lock_) {
+ JValue result;
+ self->AllowThreadSuspension();
+ if (!HandleMonitorChecks()) {
+ return;
+ }
+ if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
+ !SendMethodExitEvents(self,
+ instrumentation,
+ shadow_frame,
+ shadow_frame.GetThisObject(Accessor().InsSize()),
+ shadow_frame.GetMethod(),
+ inst->GetDexPc(Insns()),
+ result))) {
+ if (!HandlePendingExceptionWithInstrumentation(nullptr)) {
+ return;
+ }
+ }
+ if (ctx->interpret_one_instruction) {
+ /* Signal mterp to return to caller */
+ shadow_frame.SetDexPC(dex::kDexNoIndex);
+ }
+ ctx->result = result;
+ exit_interpreter_loop = true;
+ }
+
+ ALWAYS_INLINE void RETURN_VOID() REQUIRES_SHARED(Locks::mutator_lock_) {
+ QuasiAtomic::ThreadFenceForConstructor();
+ JValue result;
+ self->AllowThreadSuspension();
+ if (!HandleMonitorChecks()) {
+ return;
+ }
+ if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
+ !SendMethodExitEvents(self,
+ instrumentation,
+ shadow_frame,
+ shadow_frame.GetThisObject(Accessor().InsSize()),
+ shadow_frame.GetMethod(),
+ inst->GetDexPc(Insns()),
+ result))) {
+ if (!HandlePendingExceptionWithInstrumentation(nullptr)) {
+ return;
+ }
+ }
+ if (ctx->interpret_one_instruction) {
+ /* Signal mterp to return to caller */
+ shadow_frame.SetDexPC(dex::kDexNoIndex);
+ }
+ ctx->result = result;
+ exit_interpreter_loop = true;
+ }
+
+ ALWAYS_INLINE void RETURN() REQUIRES_SHARED(Locks::mutator_lock_) {
+ JValue result;
+ result.SetJ(0);
+ result.SetI(shadow_frame.GetVReg(inst->VRegA_11x(inst_data)));
+ self->AllowThreadSuspension();
+ if (!HandleMonitorChecks()) {
+ return;
+ }
+ if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
+ !SendMethodExitEvents(self,
+ instrumentation,
+ shadow_frame,
+ shadow_frame.GetThisObject(Accessor().InsSize()),
+ shadow_frame.GetMethod(),
+ inst->GetDexPc(Insns()),
+ result))) {
+ if (!HandlePendingExceptionWithInstrumentation(nullptr)) {
+ return;
+ }
+ }
+ if (ctx->interpret_one_instruction) {
+ /* Signal mterp to return to caller */
+ shadow_frame.SetDexPC(dex::kDexNoIndex);
+ }
+ ctx->result = result;
+ exit_interpreter_loop = true;
+ }
+
+ ALWAYS_INLINE void RETURN_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ JValue result;
+ result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x(inst_data)));
+ self->AllowThreadSuspension();
+ if (!HandleMonitorChecks()) {
+ return;
+ }
+ if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
+ !SendMethodExitEvents(self,
+ instrumentation,
+ shadow_frame,
+ shadow_frame.GetThisObject(Accessor().InsSize()),
+ shadow_frame.GetMethod(),
+ inst->GetDexPc(Insns()),
+ result))) {
+ if (!HandlePendingExceptionWithInstrumentation(nullptr)) {
+ return;
+ }
+ }
+ if (ctx->interpret_one_instruction) {
+ /* Signal mterp to return to caller */
+ shadow_frame.SetDexPC(dex::kDexNoIndex);
+ }
+ ctx->result = result;
+ exit_interpreter_loop = true;
+ }
+
+ ALWAYS_INLINE void RETURN_OBJECT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ JValue result;
+ self->AllowThreadSuspension();
+ if (!HandleMonitorChecks()) {
+ return;
+ }
+ const size_t ref_idx = inst->VRegA_11x(inst_data);
+ ObjPtr<mirror::Object> obj_result = shadow_frame.GetVRegReference(ref_idx);
+ if (do_assignability_check && obj_result != nullptr) {
+ ObjPtr<mirror::Class> return_type = shadow_frame.GetMethod()->ResolveReturnType();
+ // Re-load since it might have moved.
+ obj_result = shadow_frame.GetVRegReference(ref_idx);
+ if (return_type == nullptr) {
+ // Return the pending exception.
+ HANDLE_PENDING_EXCEPTION();
+ }
+ if (!obj_result->VerifierInstanceOf(return_type)) {
+ // This should never happen.
+ std::string temp1, temp2;
+ self->ThrowNewExceptionF("Ljava/lang/InternalError;",
+ "Returning '%s' that is not instance of return type '%s'",
+ obj_result->GetClass()->GetDescriptor(&temp1),
+ return_type->GetDescriptor(&temp2));
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+ result.SetL(obj_result);
+ if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
+ !SendMethodExitEvents(self,
+ instrumentation,
+ shadow_frame,
+ shadow_frame.GetThisObject(Accessor().InsSize()),
+ shadow_frame.GetMethod(),
+ inst->GetDexPc(Insns()),
+ result))) {
+ if (!HandlePendingExceptionWithInstrumentation(nullptr)) {
+ return;
+ }
+ }
+ // Re-load since it might have moved during the MethodExitEvent.
+ result.SetL(shadow_frame.GetVRegReference(ref_idx));
+ if (ctx->interpret_one_instruction) {
+ /* Signal mterp to return to caller */
+ shadow_frame.SetDexPC(dex::kDexNoIndex);
+ }
+ ctx->result = result;
+ exit_interpreter_loop = true;
+ }
+
+ ALWAYS_INLINE void CONST_4() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t dst = inst->VRegA_11n(inst_data);
+ int4_t val = inst->VRegB_11n(inst_data);
+ shadow_frame.SetVReg(dst, val);
+ if (val == 0) {
+ shadow_frame.SetVRegReference(dst, nullptr);
+ }
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void CONST_16() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint8_t dst = inst->VRegA_21s(inst_data);
+ int16_t val = inst->VRegB_21s();
+ shadow_frame.SetVReg(dst, val);
+ if (val == 0) {
+ shadow_frame.SetVRegReference(dst, nullptr);
+ }
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void CONST() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint8_t dst = inst->VRegA_31i(inst_data);
+ int32_t val = inst->VRegB_31i();
+ shadow_frame.SetVReg(dst, val);
+ if (val == 0) {
+ shadow_frame.SetVRegReference(dst, nullptr);
+ }
+ inst = inst->Next_3xx();
+ }
+
+ ALWAYS_INLINE void CONST_HIGH16() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint8_t dst = inst->VRegA_21h(inst_data);
+ int32_t val = static_cast<int32_t>(inst->VRegB_21h() << 16);
+ shadow_frame.SetVReg(dst, val);
+ if (val == 0) {
+ shadow_frame.SetVRegReference(dst, nullptr);
+ }
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void CONST_WIDE_16() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegLong(inst->VRegA_21s(inst_data), inst->VRegB_21s());
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void CONST_WIDE_32() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegLong(inst->VRegA_31i(inst_data), inst->VRegB_31i());
+ inst = inst->Next_3xx();
+ }
+
+ ALWAYS_INLINE void CONST_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegLong(inst->VRegA_51l(inst_data), inst->VRegB_51l());
+ inst = inst->Next_51l();
+ }
+
+ ALWAYS_INLINE void CONST_WIDE_HIGH16() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegLong(inst->VRegA_21h(inst_data),
+ static_cast<uint64_t>(inst->VRegB_21h()) << 48);
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void CONST_STRING() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::String> s = ResolveString(self,
+ shadow_frame,
+ dex::StringIndex(inst->VRegB_21c()));
+ if (UNLIKELY(s == nullptr)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), s);
+ inst = inst->Next_2xx();
+ }
+ }
+
+ ALWAYS_INLINE void CONST_STRING_JUMBO() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::String> s = ResolveString(self,
+ shadow_frame,
+ dex::StringIndex(inst->VRegB_31c()));
+ if (UNLIKELY(s == nullptr)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVRegReference(inst->VRegA_31c(inst_data), s);
+ inst = inst->Next_3xx();
+ }
+ }
+
+ ALWAYS_INLINE void CONST_CLASS() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()),
+ shadow_frame.GetMethod(),
+ self,
+ false,
+ do_access_check);
+ if (UNLIKELY(c == nullptr)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), c);
+ inst = inst->Next_2xx();
+ }
+ }
+
+ ALWAYS_INLINE void CONST_METHOD_HANDLE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ ObjPtr<mirror::MethodHandle> mh = cl->ResolveMethodHandle(self,
+ inst->VRegB_21c(),
+ shadow_frame.GetMethod());
+ if (UNLIKELY(mh == nullptr)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), mh);
+ inst = inst->Next_2xx();
+ }
+ }
+
+ ALWAYS_INLINE void CONST_METHOD_TYPE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ ObjPtr<mirror::MethodType> mt = cl->ResolveMethodType(self,
+ dex::ProtoIndex(inst->VRegB_21c()),
+ shadow_frame.GetMethod());
+ if (UNLIKELY(mt == nullptr)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), mt);
+ inst = inst->Next_2xx();
+ }
+ }
+
+ ALWAYS_INLINE void MONITOR_ENTER() REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!HandleAsyncException()) {
+ return;
+ }
+ ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
+ if (UNLIKELY(obj == nullptr)) {
+ ThrowNullPointerExceptionFromInterpreter();
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ DoMonitorEnter<do_assignability_check>(self, &shadow_frame, obj);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
+ }
+ }
+
+ ALWAYS_INLINE void MONITOR_EXIT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!HandleAsyncException()) {
+ return;
+ }
+ ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
+ if (UNLIKELY(obj == nullptr)) {
+ ThrowNullPointerExceptionFromInterpreter();
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ DoMonitorExit<do_assignability_check>(self, &shadow_frame, obj);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
+ }
+ }
+
+ ALWAYS_INLINE void CHECK_CAST() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()),
+ shadow_frame.GetMethod(),
+ self,
+ false,
+ do_access_check);
+ if (UNLIKELY(c == nullptr)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_21c(inst_data));
+ if (UNLIKELY(obj != nullptr && !obj->InstanceOf(c))) {
+ ThrowClassCastException(c, obj->GetClass());
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ inst = inst->Next_2xx();
+ }
+ }
+ }
+
+ ALWAYS_INLINE void INSTANCE_OF() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegC_22c()),
+ shadow_frame.GetMethod(),
+ self,
+ false,
+ do_access_check);
+ if (UNLIKELY(c == nullptr)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
+ shadow_frame.SetVReg(inst->VRegA_22c(inst_data),
+ (obj != nullptr && obj->InstanceOf(c)) ? 1 : 0);
+ inst = inst->Next_2xx();
+ }
+ }
+
+ ALWAYS_INLINE void ARRAY_LENGTH() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Object> array = shadow_frame.GetVRegReference(inst->VRegB_12x(inst_data));
+ if (UNLIKELY(array == nullptr)) {
+ ThrowNullPointerExceptionFromInterpreter();
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data), array->AsArray()->GetLength());
+ inst = inst->Next_1xx();
+ }
+ }
+
+ ALWAYS_INLINE void NEW_INSTANCE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Object> obj = nullptr;
+ ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()),
+ shadow_frame.GetMethod(),
+ self,
+ false,
+ do_access_check);
+ if (LIKELY(c != nullptr)) {
+ if (UNLIKELY(c->IsStringClass())) {
+ gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
+ obj = mirror::String::AllocEmptyString<true>(self, allocator_type);
+ } else {
+ obj = AllocObjectFromCode<true>(
+ c.Ptr(),
+ self,
+ Runtime::Current()->GetHeap()->GetCurrentAllocator());
+ }
+ }
+ if (UNLIKELY(obj == nullptr)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ obj->GetClass()->AssertInitializedOrInitializingInThread(self);
+ // Don't allow finalizable objects to be allocated during a transaction since these can't
+ // be finalized without a started runtime.
+ if (transaction_active && obj->GetClass()->IsFinalizable()) {
+ AbortTransactionF(self, "Allocating finalizable object in transaction: %s",
+ obj->PrettyTypeOf().c_str());
+ HANDLE_PENDING_EXCEPTION();
+ }
+ shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), obj);
+ inst = inst->Next_2xx();
+ }
+ }
+
+ ALWAYS_INLINE void NEW_ARRAY() REQUIRES_SHARED(Locks::mutator_lock_) {
+ int32_t length = shadow_frame.GetVReg(inst->VRegB_22c(inst_data));
+ ObjPtr<mirror::Object> obj = AllocArrayFromCode<do_access_check, true>(
+ dex::TypeIndex(inst->VRegC_22c()),
+ length,
+ shadow_frame.GetMethod(),
+ self,
+ Runtime::Current()->GetHeap()->GetCurrentAllocator());
+ if (UNLIKELY(obj == nullptr)) {
+ HANDLE_PENDING_EXCEPTION();
+ } else {
+ shadow_frame.SetVRegReference(inst->VRegA_22c(inst_data), obj);
+ inst = inst->Next_2xx();
+ }
+ }
+
+ ALWAYS_INLINE void FILLED_NEW_ARRAY() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success =
+ DoFilledNewArray<false, do_access_check, transaction_active>(inst, shadow_frame, self,
+ ResultRegister());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ }
+
+ ALWAYS_INLINE void FILLED_NEW_ARRAY_RANGE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success =
+ DoFilledNewArray<true, do_access_check, transaction_active>(inst, shadow_frame,
+ self, ResultRegister());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ }
+
+ ALWAYS_INLINE void FILL_ARRAY_DATA() REQUIRES_SHARED(Locks::mutator_lock_) {
+ const uint16_t* payload_addr = reinterpret_cast<const uint16_t*>(inst) + inst->VRegB_31t();
+ const Instruction::ArrayDataPayload* payload =
+ reinterpret_cast<const Instruction::ArrayDataPayload*>(payload_addr);
+ ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_31t(inst_data));
+ bool success = FillArrayData(obj, payload);
+ if (!success) {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ if (transaction_active) {
+ RecordArrayElementsInTransaction(obj->AsArray(), payload->element_count);
+ }
+ inst = inst->Next_3xx();
+ }
+
+ ALWAYS_INLINE void THROW() REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!HandleAsyncException()) {
+ return;
+ }
+ ObjPtr<mirror::Object> exception =
+ shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
+ if (UNLIKELY(exception == nullptr)) {
+ ThrowNullPointerException("throw with null exception");
+ } else if (do_assignability_check && !exception->GetClass()->IsThrowableClass()) {
+ // This should never happen.
+ std::string temp;
+ self->ThrowNewExceptionF("Ljava/lang/InternalError;",
+ "Throwing '%s' that is not instance of Throwable",
+ exception->GetClass()->GetDescriptor(&temp));
+ } else {
+ self->SetException(exception->AsThrowable());
+ }
+ HANDLE_PENDING_EXCEPTION();
+ }
+
+ ALWAYS_INLINE void GOTO() REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!HandleAsyncException()) {
+ return;
+ }
+ int8_t offset = inst->VRegA_10t(inst_data);
+ BRANCH_INSTRUMENTATION(offset);
+ inst = inst->RelativeAt(offset);
+ HandleBackwardBranch(offset);
+ }
+
+ ALWAYS_INLINE void GOTO_16() REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!HandleAsyncException()) {
+ return;
+ }
+ int16_t offset = inst->VRegA_20t();
+ BRANCH_INSTRUMENTATION(offset);
+ inst = inst->RelativeAt(offset);
+ HandleBackwardBranch(offset);
+ }
+
+ ALWAYS_INLINE void GOTO_32() REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!HandleAsyncException()) {
+ return;
+ }
+ int32_t offset = inst->VRegA_30t();
+ BRANCH_INSTRUMENTATION(offset);
+ inst = inst->RelativeAt(offset);
+ HandleBackwardBranch(offset);
+ }
+
+ ALWAYS_INLINE void PACKED_SWITCH() REQUIRES_SHARED(Locks::mutator_lock_) {
+ int32_t offset = DoPackedSwitch(inst, shadow_frame, inst_data);
+ BRANCH_INSTRUMENTATION(offset);
+ inst = inst->RelativeAt(offset);
+ HandleBackwardBranch(offset);
+ }
+
+ ALWAYS_INLINE void SPARSE_SWITCH() REQUIRES_SHARED(Locks::mutator_lock_) {
+ int32_t offset = DoSparseSwitch(inst, shadow_frame, inst_data);
+ BRANCH_INSTRUMENTATION(offset);
+ inst = inst->RelativeAt(offset);
+ HandleBackwardBranch(offset);
+ }
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wfloat-equal"
+
+
+ ALWAYS_INLINE void CMPL_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ float val1 = shadow_frame.GetVRegFloat(inst->VRegB_23x());
+ float val2 = shadow_frame.GetVRegFloat(inst->VRegC_23x());
+ int32_t result;
+ if (val1 > val2) {
+ result = 1;
+ } else if (val1 == val2) {
+ result = 0;
+ } else {
+ result = -1;
+ }
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void CMPG_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ float val1 = shadow_frame.GetVRegFloat(inst->VRegB_23x());
+ float val2 = shadow_frame.GetVRegFloat(inst->VRegC_23x());
+ int32_t result;
+ if (val1 < val2) {
+ result = -1;
+ } else if (val1 == val2) {
+ result = 0;
+ } else {
+ result = 1;
+ }
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void CMPL_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ double val1 = shadow_frame.GetVRegDouble(inst->VRegB_23x());
+ double val2 = shadow_frame.GetVRegDouble(inst->VRegC_23x());
+ int32_t result;
+ if (val1 > val2) {
+ result = 1;
+ } else if (val1 == val2) {
+ result = 0;
+ } else {
+ result = -1;
+ }
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+ inst = inst->Next_2xx();
+ }
+
+
+ ALWAYS_INLINE void CMPG_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ double val1 = shadow_frame.GetVRegDouble(inst->VRegB_23x());
+ double val2 = shadow_frame.GetVRegDouble(inst->VRegC_23x());
+ int32_t result;
+ if (val1 < val2) {
+ result = -1;
+ } else if (val1 == val2) {
+ result = 0;
+ } else {
+ result = 1;
+ }
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+ inst = inst->Next_2xx();
+ }
+
+#pragma clang diagnostic pop
+
+
+ ALWAYS_INLINE void CMP_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+ int64_t val1 = shadow_frame.GetVRegLong(inst->VRegB_23x());
+ int64_t val2 = shadow_frame.GetVRegLong(inst->VRegC_23x());
+ int32_t result;
+ if (val1 > val2) {
+ result = 1;
+ } else if (val1 == val2) {
+ result = 0;
+ } else {
+ result = -1;
+ }
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void IF_EQ() REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) ==
+ shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
+ int16_t offset = inst->VRegC_22t();
+ BRANCH_INSTRUMENTATION(offset);
+ inst = inst->RelativeAt(offset);
+ HandleBackwardBranch(offset);
+ } else {
+ BRANCH_INSTRUMENTATION(2);
+ inst = inst->Next_2xx();
+ }
+ }
+
+ ALWAYS_INLINE void IF_NE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) !=
+ shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
+ int16_t offset = inst->VRegC_22t();
+ BRANCH_INSTRUMENTATION(offset);
+ inst = inst->RelativeAt(offset);
+ HandleBackwardBranch(offset);
+ } else {
+ BRANCH_INSTRUMENTATION(2);
+ inst = inst->Next_2xx();
+ }
+ }
+
+ ALWAYS_INLINE void IF_LT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) <
+ shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
+ int16_t offset = inst->VRegC_22t();
+ BRANCH_INSTRUMENTATION(offset);
+ inst = inst->RelativeAt(offset);
+ HandleBackwardBranch(offset);
+ } else {
+ BRANCH_INSTRUMENTATION(2);
+ inst = inst->Next_2xx();
+ }
+ }
+
+ ALWAYS_INLINE void IF_GE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) >=
+ shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
+ int16_t offset = inst->VRegC_22t();
+ BRANCH_INSTRUMENTATION(offset);
+ inst = inst->RelativeAt(offset);
+ HandleBackwardBranch(offset);
+ } else {
+ BRANCH_INSTRUMENTATION(2);
+ inst = inst->Next_2xx();
+ }
+ }
+
+ ALWAYS_INLINE void IF_GT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) >
+ shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
+ int16_t offset = inst->VRegC_22t();
+ BRANCH_INSTRUMENTATION(offset);
+ inst = inst->RelativeAt(offset);
+ HandleBackwardBranch(offset);
+ } else {
+ BRANCH_INSTRUMENTATION(2);
+ inst = inst->Next_2xx();
+ }
+ }
+
+ ALWAYS_INLINE void IF_LE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) <=
+ shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
+ int16_t offset = inst->VRegC_22t();
+ BRANCH_INSTRUMENTATION(offset);
+ inst = inst->RelativeAt(offset);
+ HandleBackwardBranch(offset);
+ } else {
+ BRANCH_INSTRUMENTATION(2);
+ inst = inst->Next_2xx();
+ }
+ }
+
+ ALWAYS_INLINE void IF_EQZ() REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) == 0) {
+ int16_t offset = inst->VRegB_21t();
+ BRANCH_INSTRUMENTATION(offset);
+ inst = inst->RelativeAt(offset);
+ HandleBackwardBranch(offset);
+ } else {
+ BRANCH_INSTRUMENTATION(2);
+ inst = inst->Next_2xx();
+ }
+ }
+
+ ALWAYS_INLINE void IF_NEZ() REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) != 0) {
+ int16_t offset = inst->VRegB_21t();
+ BRANCH_INSTRUMENTATION(offset);
+ inst = inst->RelativeAt(offset);
+ HandleBackwardBranch(offset);
+ } else {
+ BRANCH_INSTRUMENTATION(2);
+ inst = inst->Next_2xx();
+ }
+ }
+
+ ALWAYS_INLINE void IF_LTZ() REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) < 0) {
+ int16_t offset = inst->VRegB_21t();
+ BRANCH_INSTRUMENTATION(offset);
+ inst = inst->RelativeAt(offset);
+ HandleBackwardBranch(offset);
+ } else {
+ BRANCH_INSTRUMENTATION(2);
+ inst = inst->Next_2xx();
+ }
+ }
+
+ ALWAYS_INLINE void IF_GEZ() REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) >= 0) {
+ int16_t offset = inst->VRegB_21t();
+ BRANCH_INSTRUMENTATION(offset);
+ inst = inst->RelativeAt(offset);
+ HandleBackwardBranch(offset);
+ } else {
+ BRANCH_INSTRUMENTATION(2);
+ inst = inst->Next_2xx();
+ }
+ }
+
+ ALWAYS_INLINE void IF_GTZ() REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) > 0) {
+ int16_t offset = inst->VRegB_21t();
+ BRANCH_INSTRUMENTATION(offset);
+ inst = inst->RelativeAt(offset);
+ HandleBackwardBranch(offset);
+ } else {
+ BRANCH_INSTRUMENTATION(2);
+ inst = inst->Next_2xx();
+ }
+ }
+
+ ALWAYS_INLINE void IF_LEZ() REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) <= 0) {
+ int16_t offset = inst->VRegB_21t();
+ BRANCH_INSTRUMENTATION(offset);
+ inst = inst->RelativeAt(offset);
+ HandleBackwardBranch(offset);
+ } else {
+ BRANCH_INSTRUMENTATION(2);
+ inst = inst->Next_2xx();
+ }
+ }
+
+ ALWAYS_INLINE void AGET_BOOLEAN() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == nullptr)) {
+ ThrowNullPointerExceptionFromInterpreter();
+ HANDLE_PENDING_EXCEPTION();
+ }
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ObjPtr<mirror::BooleanArray> array = a->AsBooleanArray();
+ if (array->CheckIsValidIndex(index)) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+
+ ALWAYS_INLINE void AGET_BYTE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == nullptr)) {
+ ThrowNullPointerExceptionFromInterpreter();
+ HANDLE_PENDING_EXCEPTION();
+ }
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ObjPtr<mirror::ByteArray> array = a->AsByteArray();
+ if (array->CheckIsValidIndex(index)) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+
+ ALWAYS_INLINE void AGET_CHAR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == nullptr)) {
+ ThrowNullPointerExceptionFromInterpreter();
+ HANDLE_PENDING_EXCEPTION();
+ }
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ObjPtr<mirror::CharArray> array = a->AsCharArray();
+ if (array->CheckIsValidIndex(index)) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+
+ ALWAYS_INLINE void AGET_SHORT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == nullptr)) {
+ ThrowNullPointerExceptionFromInterpreter();
+ HANDLE_PENDING_EXCEPTION();
+ }
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ObjPtr<mirror::ShortArray> array = a->AsShortArray();
+ if (array->CheckIsValidIndex(index)) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+
+ ALWAYS_INLINE void AGET() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == nullptr)) {
+ ThrowNullPointerExceptionFromInterpreter();
+ HANDLE_PENDING_EXCEPTION();
+ }
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ DCHECK(a->IsIntArray() || a->IsFloatArray()) << a->PrettyTypeOf();
+ ObjPtr<mirror::IntArray> array = ObjPtr<mirror::IntArray>::DownCast(a);
+ if (array->CheckIsValidIndex(index)) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+
+ ALWAYS_INLINE void AGET_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == nullptr)) {
+ ThrowNullPointerExceptionFromInterpreter();
+ HANDLE_PENDING_EXCEPTION();
+ }
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ DCHECK(a->IsLongArray() || a->IsDoubleArray()) << a->PrettyTypeOf();
+ ObjPtr<mirror::LongArray> array = ObjPtr<mirror::LongArray>::DownCast(a);
+ if (array->CheckIsValidIndex(index)) {
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+
+ ALWAYS_INLINE void AGET_OBJECT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == nullptr)) {
+ ThrowNullPointerExceptionFromInterpreter();
+ HANDLE_PENDING_EXCEPTION();
+ }
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ObjPtr<mirror::ObjectArray<mirror::Object>> array = a->AsObjectArray<mirror::Object>();
+ if (array->CheckIsValidIndex(index)) {
+ shadow_frame.SetVRegReference(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+
+ ALWAYS_INLINE void APUT_BOOLEAN() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == nullptr)) {
+ ThrowNullPointerExceptionFromInterpreter();
+ HANDLE_PENDING_EXCEPTION();
+ }
+ uint8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ObjPtr<mirror::BooleanArray> array = a->AsBooleanArray();
+ if (array->CheckIsValidIndex(index)) {
+ array->SetWithoutChecks<transaction_active>(index, val);
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+
+ ALWAYS_INLINE void APUT_BYTE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == nullptr)) {
+ ThrowNullPointerExceptionFromInterpreter();
+ HANDLE_PENDING_EXCEPTION();
+ }
+ int8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ObjPtr<mirror::ByteArray> array = a->AsByteArray();
+ if (array->CheckIsValidIndex(index)) {
+ array->SetWithoutChecks<transaction_active>(index, val);
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+
+ ALWAYS_INLINE void APUT_CHAR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == nullptr)) {
+ ThrowNullPointerExceptionFromInterpreter();
+ HANDLE_PENDING_EXCEPTION();
+ }
+ uint16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ObjPtr<mirror::CharArray> array = a->AsCharArray();
+ if (array->CheckIsValidIndex(index)) {
+ array->SetWithoutChecks<transaction_active>(index, val);
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+
+ ALWAYS_INLINE void APUT_SHORT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == nullptr)) {
+ ThrowNullPointerExceptionFromInterpreter();
+ HANDLE_PENDING_EXCEPTION();
+ }
+ int16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ObjPtr<mirror::ShortArray> array = a->AsShortArray();
+ if (array->CheckIsValidIndex(index)) {
+ array->SetWithoutChecks<transaction_active>(index, val);
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+
+ ALWAYS_INLINE void APUT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == nullptr)) {
+ ThrowNullPointerExceptionFromInterpreter();
+ HANDLE_PENDING_EXCEPTION();
+ }
+ int32_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ DCHECK(a->IsIntArray() || a->IsFloatArray()) << a->PrettyTypeOf();
+ ObjPtr<mirror::IntArray> array = ObjPtr<mirror::IntArray>::DownCast(a);
+ if (array->CheckIsValidIndex(index)) {
+ array->SetWithoutChecks<transaction_active>(index, val);
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+
+ ALWAYS_INLINE void APUT_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == nullptr)) {
+ ThrowNullPointerExceptionFromInterpreter();
+ HANDLE_PENDING_EXCEPTION();
+ }
+ int64_t val = shadow_frame.GetVRegLong(inst->VRegA_23x(inst_data));
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ DCHECK(a->IsLongArray() || a->IsDoubleArray()) << a->PrettyTypeOf();
+ ObjPtr<mirror::LongArray> array = ObjPtr<mirror::LongArray>::DownCast(a);
+ if (array->CheckIsValidIndex(index)) {
+ array->SetWithoutChecks<transaction_active>(index, val);
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+
+ ALWAYS_INLINE void APUT_OBJECT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
+ if (UNLIKELY(a == nullptr)) {
+ ThrowNullPointerExceptionFromInterpreter();
+ HANDLE_PENDING_EXCEPTION();
+ }
+ int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
+ ObjPtr<mirror::Object> val = shadow_frame.GetVRegReference(inst->VRegA_23x(inst_data));
+ ObjPtr<mirror::ObjectArray<mirror::Object>> array = a->AsObjectArray<mirror::Object>();
+ if (array->CheckIsValidIndex(index) && array->CheckAssignable(val)) {
+ array->SetWithoutChecks<transaction_active>(index, val);
+ inst = inst->Next_2xx();
+ } else {
+ HANDLE_PENDING_EXCEPTION();
+ }
+ }
+
+ ALWAYS_INLINE void IGET_BOOLEAN() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimBoolean, do_access_check>(
+ self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void IGET_BYTE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimByte, do_access_check>(
+ self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void IGET_CHAR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimChar, do_access_check>(
+ self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void IGET_SHORT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimShort, do_access_check>(
+ self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void IGET() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimInt, do_access_check>(
+ self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void IGET_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimLong, do_access_check>(
+ self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void IGET_OBJECT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoFieldGet<InstanceObjectRead, Primitive::kPrimNot, do_access_check>(
+ self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void IGET_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoIGetQuick<Primitive::kPrimInt>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void IGET_WIDE_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoIGetQuick<Primitive::kPrimLong>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void IGET_OBJECT_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoIGetQuick<Primitive::kPrimNot>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void IGET_BOOLEAN_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoIGetQuick<Primitive::kPrimBoolean>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void IGET_BYTE_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoIGetQuick<Primitive::kPrimByte>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void IGET_CHAR_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoIGetQuick<Primitive::kPrimChar>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void IGET_SHORT_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoIGetQuick<Primitive::kPrimShort>(shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void SGET_BOOLEAN() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimBoolean, do_access_check,
+ transaction_active>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void SGET_BYTE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimByte, do_access_check,
+ transaction_active>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void SGET_CHAR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimChar, do_access_check,
+ transaction_active>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void SGET_SHORT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimShort, do_access_check,
+ transaction_active>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void SGET() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimInt, do_access_check,
+ transaction_active>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void SGET_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimLong, do_access_check,
+ transaction_active>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void SGET_OBJECT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoFieldGet<StaticObjectRead, Primitive::kPrimNot, do_access_check,
+ transaction_active>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void IPUT_BOOLEAN() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimBoolean, do_access_check,
+ transaction_active>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void IPUT_BYTE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimByte, do_access_check,
+ transaction_active>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void IPUT_CHAR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimChar, do_access_check,
+ transaction_active>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void IPUT_SHORT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimShort, do_access_check,
+ transaction_active>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void IPUT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimInt, do_access_check,
+ transaction_active>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void IPUT_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimLong, do_access_check,
+ transaction_active>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void IPUT_OBJECT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check,
+ transaction_active>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void IPUT_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoIPutQuick<Primitive::kPrimInt, transaction_active>(
+ shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void IPUT_BOOLEAN_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoIPutQuick<Primitive::kPrimBoolean, transaction_active>(
+ shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void IPUT_BYTE_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoIPutQuick<Primitive::kPrimByte, transaction_active>(
+ shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void IPUT_CHAR_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoIPutQuick<Primitive::kPrimChar, transaction_active>(
+ shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void IPUT_SHORT_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoIPutQuick<Primitive::kPrimShort, transaction_active>(
+ shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void IPUT_WIDE_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoIPutQuick<Primitive::kPrimLong, transaction_active>(
+ shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void IPUT_OBJECT_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoIPutQuick<Primitive::kPrimNot, transaction_active>(
+ shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void SPUT_BOOLEAN() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check,
+ transaction_active>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void SPUT_BYTE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimByte, do_access_check,
+ transaction_active>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void SPUT_CHAR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimChar, do_access_check,
+ transaction_active>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void SPUT_SHORT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimShort, do_access_check,
+ transaction_active>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void SPUT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimInt, do_access_check,
+ transaction_active>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void SPUT_WIDE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimLong, do_access_check,
+ transaction_active>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void SPUT_OBJECT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoFieldPut<StaticObjectWrite, Primitive::kPrimNot, do_access_check,
+ transaction_active>(self, shadow_frame, inst, inst_data);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void INVOKE_VIRTUAL() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoInvoke<kVirtual, false, do_access_check, /*is_mterp=*/ false>(
+ self, shadow_frame, inst, inst_data, ResultRegister());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
+ }
+
+ ALWAYS_INLINE void INVOKE_VIRTUAL_RANGE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoInvoke<kVirtual, true, do_access_check, /*is_mterp=*/ false>(
+ self, shadow_frame, inst, inst_data, ResultRegister());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
+ }
+
+ ALWAYS_INLINE void INVOKE_SUPER() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoInvoke<kSuper, false, do_access_check, /*is_mterp=*/ false>(
+ self, shadow_frame, inst, inst_data, ResultRegister());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
+ }
+
+ ALWAYS_INLINE void INVOKE_SUPER_RANGE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoInvoke<kSuper, true, do_access_check, /*is_mterp=*/ false>(
+ self, shadow_frame, inst, inst_data, ResultRegister());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
+ }
+
+ ALWAYS_INLINE void INVOKE_DIRECT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoInvoke<kDirect, false, do_access_check, /*is_mterp=*/ false>(
+ self, shadow_frame, inst, inst_data, ResultRegister());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
+ }
+
+ ALWAYS_INLINE void INVOKE_DIRECT_RANGE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoInvoke<kDirect, true, do_access_check, /*is_mterp=*/ false>(
+ self, shadow_frame, inst, inst_data, ResultRegister());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
+ }
+
+ ALWAYS_INLINE void INVOKE_INTERFACE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoInvoke<kInterface, false, do_access_check, /*is_mterp=*/ false>(
+ self, shadow_frame, inst, inst_data, ResultRegister());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
+ }
+
+ ALWAYS_INLINE void INVOKE_INTERFACE_RANGE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoInvoke<kInterface, true, do_access_check, /*is_mterp=*/ false>(
+ self, shadow_frame, inst, inst_data, ResultRegister());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
+ }
+
+ ALWAYS_INLINE void INVOKE_STATIC() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoInvoke<kStatic, false, do_access_check, /*is_mterp=*/ false>(
+ self, shadow_frame, inst, inst_data, ResultRegister());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
+ }
+
+ ALWAYS_INLINE void INVOKE_STATIC_RANGE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoInvoke<kStatic, true, do_access_check, /*is_mterp=*/ false>(
+ self, shadow_frame, inst, inst_data, ResultRegister());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
+ }
+
+ ALWAYS_INLINE void INVOKE_VIRTUAL_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoInvoke<kVirtual, false, do_access_check, /*is_mterp=*/ false,
+ /*is_quick=*/ true>(self, shadow_frame, inst, inst_data, ResultRegister());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
+ }
+
+ ALWAYS_INLINE void INVOKE_VIRTUAL_RANGE_QUICK() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoInvoke<kVirtual, true, do_access_check, /*is_mterp=*/ false,
+ /*is_quick=*/ true>(self, shadow_frame, inst, inst_data, ResultRegister());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
+ }
+
+ ALWAYS_INLINE void INVOKE_POLYMORPHIC() REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+ bool success = DoInvokePolymorphic</* is_range= */ false>(
+ self, shadow_frame, inst, inst_data, ResultRegister());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_POLYMORPHIC(!success);
+ }
+
+ ALWAYS_INLINE void INVOKE_POLYMORPHIC_RANGE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+ bool success = DoInvokePolymorphic</* is_range= */ true>(
+ self, shadow_frame, inst, inst_data, ResultRegister());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_POLYMORPHIC(!success);
+ }
+
+ ALWAYS_INLINE void INVOKE_CUSTOM() REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+ bool success = DoInvokeCustom</* is_range= */ false>(
+ self, shadow_frame, inst, inst_data, ResultRegister());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
+ }
+
+ ALWAYS_INLINE void INVOKE_CUSTOM_RANGE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
+ bool success = DoInvokeCustom</* is_range= */ true>(
+ self, shadow_frame, inst, inst_data, ResultRegister());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
+ }
+
+ ALWAYS_INLINE void NEG_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(
+ inst->VRegA_12x(inst_data), -shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void NOT_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(
+ inst->VRegA_12x(inst_data), ~shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void NEG_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegLong(
+ inst->VRegA_12x(inst_data), -shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void NOT_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegLong(
+ inst->VRegA_12x(inst_data), ~shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void NEG_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegFloat(
+ inst->VRegA_12x(inst_data), -shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void NEG_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegDouble(
+ inst->VRegA_12x(inst_data), -shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void INT_TO_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void INT_TO_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void INT_TO_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void LONG_TO_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void LONG_TO_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void LONG_TO_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void FLOAT_TO_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ float val = shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data));
+ int32_t result = art_float_to_integral<int32_t, float>(val);
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data), result);
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void FLOAT_TO_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+ float val = shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data));
+ int64_t result = art_float_to_integral<int64_t, float>(val);
+ shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), result);
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void FLOAT_TO_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void DOUBLE_TO_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ double val = shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data));
+ int32_t result = art_float_to_integral<int32_t, double>(val);
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data), result);
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void DOUBLE_TO_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+ double val = shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data));
+ int64_t result = art_float_to_integral<int64_t, double>(val);
+ shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), result);
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void DOUBLE_TO_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data),
+ shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void INT_TO_BYTE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data), static_cast<int8_t>(
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void INT_TO_CHAR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data), static_cast<uint16_t>(
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void INT_TO_SHORT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_12x(inst_data), static_cast<int16_t>(
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void ADD_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ SafeAdd(shadow_frame.GetVReg(inst->VRegB_23x()),
+ shadow_frame.GetVReg(inst->VRegC_23x())));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void SUB_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ SafeSub(shadow_frame.GetVReg(inst->VRegB_23x()),
+ shadow_frame.GetVReg(inst->VRegC_23x())));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void MUL_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ SafeMul(shadow_frame.GetVReg(inst->VRegB_23x()),
+ shadow_frame.GetVReg(inst->VRegC_23x())));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void DIV_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoIntDivide(shadow_frame, inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()),
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void REM_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoIntRemainder(shadow_frame, inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()),
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void SHL_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) <<
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void SHR_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) >>
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void USHR_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ static_cast<uint32_t>(shadow_frame.GetVReg(inst->VRegB_23x())) >>
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void AND_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) &
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void OR_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) |
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void XOR_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_23x()) ^
+ shadow_frame.GetVReg(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void ADD_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ SafeAdd(shadow_frame.GetVRegLong(inst->VRegB_23x()),
+ shadow_frame.GetVRegLong(inst->VRegC_23x())));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void SUB_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ SafeSub(shadow_frame.GetVRegLong(inst->VRegB_23x()),
+ shadow_frame.GetVRegLong(inst->VRegC_23x())));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void MUL_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ SafeMul(shadow_frame.GetVRegLong(inst->VRegB_23x()),
+ shadow_frame.GetVRegLong(inst->VRegC_23x())));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void DIV_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+ DoLongDivide(shadow_frame, inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()),
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_2xx);
+ }
+
+ ALWAYS_INLINE void REM_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+ DoLongRemainder(shadow_frame, inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()),
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_2xx);
+ }
+
+ ALWAYS_INLINE void AND_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) &
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void OR_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) |
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void XOR_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) ^
+ shadow_frame.GetVRegLong(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void SHL_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) <<
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void SHR_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegLong(inst->VRegB_23x()) >>
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void USHR_LONG() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
+ static_cast<uint64_t>(shadow_frame.GetVRegLong(inst->VRegB_23x())) >>
+ (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void ADD_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegFloat(inst->VRegB_23x()) +
+ shadow_frame.GetVRegFloat(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void SUB_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegFloat(inst->VRegB_23x()) -
+ shadow_frame.GetVRegFloat(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void MUL_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegFloat(inst->VRegB_23x()) *
+ shadow_frame.GetVRegFloat(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void DIV_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegFloat(inst->VRegB_23x()) /
+ shadow_frame.GetVRegFloat(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void REM_FLOAT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
+ fmodf(shadow_frame.GetVRegFloat(inst->VRegB_23x()),
+ shadow_frame.GetVRegFloat(inst->VRegC_23x())));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void ADD_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegDouble(inst->VRegB_23x()) +
+ shadow_frame.GetVRegDouble(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void SUB_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegDouble(inst->VRegB_23x()) -
+ shadow_frame.GetVRegDouble(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void MUL_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegDouble(inst->VRegB_23x()) *
+ shadow_frame.GetVRegDouble(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void DIV_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+ shadow_frame.GetVRegDouble(inst->VRegB_23x()) /
+ shadow_frame.GetVRegDouble(inst->VRegC_23x()));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void REM_DOUBLE() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
+ fmod(shadow_frame.GetVRegDouble(inst->VRegB_23x()),
+ shadow_frame.GetVRegDouble(inst->VRegC_23x())));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void ADD_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA, SafeAdd(shadow_frame.GetVReg(vregA),
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void SUB_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ SafeSub(shadow_frame.GetVReg(vregA),
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void MUL_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ SafeMul(shadow_frame.GetVReg(vregA),
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void DIV_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ bool success = DoIntDivide(shadow_frame, vregA, shadow_frame.GetVReg(vregA),
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_1xx);
+ }
+
+ ALWAYS_INLINE void REM_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ bool success = DoIntRemainder(shadow_frame, vregA, shadow_frame.GetVReg(vregA),
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_1xx);
+ }
+
+ ALWAYS_INLINE void SHL_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) <<
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void SHR_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) >>
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void USHR_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ static_cast<uint32_t>(shadow_frame.GetVReg(vregA)) >>
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void AND_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) &
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void OR_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) |
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void XOR_INT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVReg(vregA,
+ shadow_frame.GetVReg(vregA) ^
+ shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void ADD_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ SafeAdd(shadow_frame.GetVRegLong(vregA),
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void SUB_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ SafeSub(shadow_frame.GetVRegLong(vregA),
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void MUL_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ SafeMul(shadow_frame.GetVRegLong(vregA),
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void DIV_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ DoLongDivide(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA),
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
+ }
+
+ ALWAYS_INLINE void REM_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ DoLongRemainder(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA),
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
+ }
+
+ ALWAYS_INLINE void AND_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) &
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void OR_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) |
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void XOR_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) ^
+ shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void SHL_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) <<
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void SHR_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ shadow_frame.GetVRegLong(vregA) >>
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void USHR_LONG_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegLong(vregA,
+ static_cast<uint64_t>(shadow_frame.GetVRegLong(vregA)) >>
+ (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void ADD_FLOAT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegFloat(vregA,
+ shadow_frame.GetVRegFloat(vregA) +
+ shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void SUB_FLOAT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegFloat(vregA,
+ shadow_frame.GetVRegFloat(vregA) -
+ shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void MUL_FLOAT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegFloat(vregA,
+ shadow_frame.GetVRegFloat(vregA) *
+ shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void DIV_FLOAT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegFloat(vregA,
+ shadow_frame.GetVRegFloat(vregA) /
+ shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void REM_FLOAT_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegFloat(vregA,
+ fmodf(shadow_frame.GetVRegFloat(vregA),
+ shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data))));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void ADD_DOUBLE_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegDouble(vregA,
+ shadow_frame.GetVRegDouble(vregA) +
+ shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void SUB_DOUBLE_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegDouble(vregA,
+ shadow_frame.GetVRegDouble(vregA) -
+ shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void MUL_DOUBLE_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegDouble(vregA,
+ shadow_frame.GetVRegDouble(vregA) *
+ shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void DIV_DOUBLE_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegDouble(vregA,
+ shadow_frame.GetVRegDouble(vregA) /
+ shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void REM_DOUBLE_2ADDR() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint4_t vregA = inst->VRegA_12x(inst_data);
+ shadow_frame.SetVRegDouble(vregA,
+ fmod(shadow_frame.GetVRegDouble(vregA),
+ shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data))));
+ inst = inst->Next_1xx();
+ }
+
+ ALWAYS_INLINE void ADD_INT_LIT16() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ SafeAdd(shadow_frame.GetVReg(inst->VRegB_22s(inst_data)),
+ inst->VRegC_22s()));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void RSUB_INT() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ SafeSub(inst->VRegC_22s(),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data))));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void MUL_INT_LIT16() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ SafeMul(shadow_frame.GetVReg(inst->VRegB_22s(inst_data)),
+ inst->VRegC_22s()));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void DIV_INT_LIT16() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoIntDivide(shadow_frame, inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)),
+ inst->VRegC_22s());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void REM_INT_LIT16() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoIntRemainder(shadow_frame, inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)),
+ inst->VRegC_22s());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void AND_INT_LIT16() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) &
+ inst->VRegC_22s());
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void OR_INT_LIT16() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) |
+ inst->VRegC_22s());
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void XOR_INT_LIT16() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) ^
+ inst->VRegC_22s());
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void ADD_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ SafeAdd(shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b()));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void RSUB_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ SafeSub(inst->VRegC_22b(), shadow_frame.GetVReg(inst->VRegB_22b())));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void MUL_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ SafeMul(shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b()));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void DIV_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoIntDivide(shadow_frame, inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void REM_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool success = DoIntRemainder(shadow_frame, inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b());
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
+ }
+
+ ALWAYS_INLINE void AND_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) &
+ inst->VRegC_22b());
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void OR_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) |
+ inst->VRegC_22b());
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void XOR_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) ^
+ inst->VRegC_22b());
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void SHL_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) <<
+ (inst->VRegC_22b() & 0x1f));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void SHR_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ shadow_frame.GetVReg(inst->VRegB_22b()) >>
+ (inst->VRegC_22b() & 0x1f));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void USHR_INT_LIT8() REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
+ static_cast<uint32_t>(shadow_frame.GetVReg(inst->VRegB_22b())) >>
+ (inst->VRegC_22b() & 0x1f));
+ inst = inst->Next_2xx();
+ }
+
+ ALWAYS_INLINE void UNUSED_3E() REQUIRES_SHARED(Locks::mutator_lock_) {
+ UnexpectedOpcode(inst, shadow_frame);
+ }
+
+ ALWAYS_INLINE void UNUSED_3F() REQUIRES_SHARED(Locks::mutator_lock_) {
+ UnexpectedOpcode(inst, shadow_frame);
+ }
+
+ ALWAYS_INLINE void UNUSED_40() REQUIRES_SHARED(Locks::mutator_lock_) {
+ UnexpectedOpcode(inst, shadow_frame);
+ }
+
+ ALWAYS_INLINE void UNUSED_41() REQUIRES_SHARED(Locks::mutator_lock_) {
+ UnexpectedOpcode(inst, shadow_frame);
+ }
+
+ ALWAYS_INLINE void UNUSED_42() REQUIRES_SHARED(Locks::mutator_lock_) {
+ UnexpectedOpcode(inst, shadow_frame);
+ }
+
+ ALWAYS_INLINE void UNUSED_43() REQUIRES_SHARED(Locks::mutator_lock_) {
+ UnexpectedOpcode(inst, shadow_frame);
+ }
+
+ ALWAYS_INLINE void UNUSED_79() REQUIRES_SHARED(Locks::mutator_lock_) {
+ UnexpectedOpcode(inst, shadow_frame);
+ }
+
+ ALWAYS_INLINE void UNUSED_7A() REQUIRES_SHARED(Locks::mutator_lock_) {
+ UnexpectedOpcode(inst, shadow_frame);
+ }
+
+ ALWAYS_INLINE void UNUSED_F3() REQUIRES_SHARED(Locks::mutator_lock_) {
+ UnexpectedOpcode(inst, shadow_frame);
+ }
+
+ ALWAYS_INLINE void UNUSED_F4() REQUIRES_SHARED(Locks::mutator_lock_) {
+ UnexpectedOpcode(inst, shadow_frame);
+ }
+
+ ALWAYS_INLINE void UNUSED_F5() REQUIRES_SHARED(Locks::mutator_lock_) {
+ UnexpectedOpcode(inst, shadow_frame);
+ }
+
+ ALWAYS_INLINE void UNUSED_F6() REQUIRES_SHARED(Locks::mutator_lock_) {
+ UnexpectedOpcode(inst, shadow_frame);
+ }
+
+ ALWAYS_INLINE void UNUSED_F7() REQUIRES_SHARED(Locks::mutator_lock_) {
+ UnexpectedOpcode(inst, shadow_frame);
+ }
+
+ ALWAYS_INLINE void UNUSED_F8() REQUIRES_SHARED(Locks::mutator_lock_) {
+ UnexpectedOpcode(inst, shadow_frame);
+ }
+
+ ALWAYS_INLINE void UNUSED_F9() REQUIRES_SHARED(Locks::mutator_lock_) {
+ UnexpectedOpcode(inst, shadow_frame);
+ }
+
+ ALWAYS_INLINE InstructionHandler(SwitchImplContext* ctx,
+ const instrumentation::Instrumentation* instrumentation,
+ Thread* self,
+ ShadowFrame& shadow_frame,
+ uint16_t dex_pc,
+ const Instruction*& inst,
+ uint16_t inst_data,
+ bool& exit_interpreter_loop)
+ : ctx(ctx),
+ instrumentation(instrumentation),
+ self(self),
+ shadow_frame(shadow_frame),
+ dex_pc(dex_pc),
+ inst(inst),
+ inst_data(inst_data),
+ exit_interpreter_loop(exit_interpreter_loop) {
+ }
+
+ private:
+ static constexpr bool do_assignability_check = do_access_check;
+
+ const CodeItemDataAccessor& Accessor() { return ctx->accessor; }
+ const uint16_t* Insns() { return ctx->accessor.Insns(); }
+ JValue* ResultRegister() { return &ctx->result_register; }
+
+ SwitchImplContext* const ctx;
+ const instrumentation::Instrumentation* const instrumentation;
+ Thread* const self;
+ ShadowFrame& shadow_frame;
+ uint32_t const dex_pc;
+ const Instruction*& inst;
+ uint16_t const inst_data;
+ bool& exit_interpreter_loop;
+};
+
+#undef BRANCH_INSTRUMENTATION
+#undef POSSIBLY_HANDLE_PENDING_EXCEPTION
+#undef POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE
+#undef POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_POLYMORPHIC
+#undef HANDLE_PENDING_EXCEPTION
// TODO On ASAN builds this function gets a huge stack frame. Since normally we run in the mterp
// this shouldn't cause any problems for stack overflow detection. Remove this once b/117341496 is
@@ -276,9 +2587,6 @@
Thread* self = ctx->self;
const CodeItemDataAccessor& accessor = ctx->accessor;
ShadowFrame& shadow_frame = ctx->shadow_frame;
- JValue result_register = ctx->result_register;
- bool interpret_one_instruction = ctx->interpret_one_instruction;
- constexpr bool do_assignability_check = do_access_check;
if (UNLIKELY(!shadow_frame.HasReferenceArray())) {
LOG(FATAL) << "Invalid shadow frame for interpreter use";
ctx->result = JValue();
@@ -291,2294 +2599,53 @@
const uint16_t* const insns = accessor.Insns();
const Instruction* inst = Instruction::At(insns + dex_pc);
uint16_t inst_data;
- jit::Jit* jit = Runtime::Current()->GetJit();
DCHECK(!shadow_frame.GetForceRetryInstruction())
<< "Entered interpreter from invoke without retry instruction being handled!";
- do {
+ bool const interpret_one_instruction = ctx->interpret_one_instruction;
+ while (true) {
dex_pc = inst->GetDexPc(insns);
shadow_frame.SetDexPC(dex_pc);
TraceExecution(shadow_frame, inst, dex_pc);
inst_data = inst->Fetch16(0);
- switch (inst->Opcode(inst_data)) {
- case Instruction::NOP:
- PREAMBLE();
- inst = inst->Next_1xx();
- break;
- case Instruction::MOVE:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
- shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- case Instruction::MOVE_FROM16:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22x(inst_data),
- shadow_frame.GetVReg(inst->VRegB_22x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::MOVE_16:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_32x(),
- shadow_frame.GetVReg(inst->VRegB_32x()));
- inst = inst->Next_3xx();
- break;
- case Instruction::MOVE_WIDE:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data),
- shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- case Instruction::MOVE_WIDE_FROM16:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_22x(inst_data),
- shadow_frame.GetVRegLong(inst->VRegB_22x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::MOVE_WIDE_16:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_32x(),
- shadow_frame.GetVRegLong(inst->VRegB_32x()));
- inst = inst->Next_3xx();
- break;
- case Instruction::MOVE_OBJECT:
- PREAMBLE();
- shadow_frame.SetVRegReference(inst->VRegA_12x(inst_data),
- shadow_frame.GetVRegReference(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- case Instruction::MOVE_OBJECT_FROM16:
- PREAMBLE();
- shadow_frame.SetVRegReference(inst->VRegA_22x(inst_data),
- shadow_frame.GetVRegReference(inst->VRegB_22x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::MOVE_OBJECT_16:
- PREAMBLE();
- shadow_frame.SetVRegReference(inst->VRegA_32x(),
- shadow_frame.GetVRegReference(inst->VRegB_32x()));
- inst = inst->Next_3xx();
- break;
- case Instruction::MOVE_RESULT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_11x(inst_data), result_register.GetI());
- inst = inst->Next_1xx();
- break;
- case Instruction::MOVE_RESULT_WIDE:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_11x(inst_data), result_register.GetJ());
- inst = inst->Next_1xx();
- break;
- case Instruction::MOVE_RESULT_OBJECT:
- PREAMBLE_SAVE(&result_register);
- shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), result_register.GetL());
- inst = inst->Next_1xx();
- break;
- case Instruction::MOVE_EXCEPTION: {
- PREAMBLE();
- ObjPtr<mirror::Throwable> exception = self->GetException();
- DCHECK(exception != nullptr) << "No pending exception on MOVE_EXCEPTION instruction";
- shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), exception);
- self->ClearException();
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::RETURN_VOID_NO_BARRIER: {
- PREAMBLE();
- JValue result;
- self->AllowThreadSuspension();
- HANDLE_MONITOR_CHECKS();
- if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
- !SendMethodExitEvents(self,
- instrumentation,
- shadow_frame,
- shadow_frame.GetThisObject(accessor.InsSize()),
- shadow_frame.GetMethod(),
- inst->GetDexPc(insns),
- result))) {
- HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
+ {
+ bool exit_loop = false;
+ InstructionHandler<do_access_check, transaction_active> handler(
+ ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, exit_loop);
+ if (!handler.Preamble()) {
+ if (UNLIKELY(exit_loop)) {
+ return;
}
- if (interpret_one_instruction) {
- /* Signal mterp to return to caller */
- shadow_frame.SetDexPC(dex::kDexNoIndex);
- }
- ctx->result = result;
- return;
- }
- case Instruction::RETURN_VOID: {
- PREAMBLE();
- QuasiAtomic::ThreadFenceForConstructor();
- JValue result;
- self->AllowThreadSuspension();
- HANDLE_MONITOR_CHECKS();
- if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
- !SendMethodExitEvents(self,
- instrumentation,
- shadow_frame,
- shadow_frame.GetThisObject(accessor.InsSize()),
- shadow_frame.GetMethod(),
- inst->GetDexPc(insns),
- result))) {
- HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
- }
- if (interpret_one_instruction) {
- /* Signal mterp to return to caller */
- shadow_frame.SetDexPC(dex::kDexNoIndex);
- }
- ctx->result = result;
- return;
- }
- case Instruction::RETURN: {
- PREAMBLE();
- JValue result;
- result.SetJ(0);
- result.SetI(shadow_frame.GetVReg(inst->VRegA_11x(inst_data)));
- self->AllowThreadSuspension();
- HANDLE_MONITOR_CHECKS();
- if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
- !SendMethodExitEvents(self,
- instrumentation,
- shadow_frame,
- shadow_frame.GetThisObject(accessor.InsSize()),
- shadow_frame.GetMethod(),
- inst->GetDexPc(insns),
- result))) {
- HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
- }
- if (interpret_one_instruction) {
- /* Signal mterp to return to caller */
- shadow_frame.SetDexPC(dex::kDexNoIndex);
- }
- ctx->result = result;
- return;
- }
- case Instruction::RETURN_WIDE: {
- PREAMBLE();
- JValue result;
- result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x(inst_data)));
- self->AllowThreadSuspension();
- HANDLE_MONITOR_CHECKS();
- if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
- !SendMethodExitEvents(self,
- instrumentation,
- shadow_frame,
- shadow_frame.GetThisObject(accessor.InsSize()),
- shadow_frame.GetMethod(),
- inst->GetDexPc(insns),
- result))) {
- HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
- }
- if (interpret_one_instruction) {
- /* Signal mterp to return to caller */
- shadow_frame.SetDexPC(dex::kDexNoIndex);
- }
- ctx->result = result;
- return;
- }
- case Instruction::RETURN_OBJECT: {
- PREAMBLE();
- JValue result;
- self->AllowThreadSuspension();
- HANDLE_MONITOR_CHECKS();
- const size_t ref_idx = inst->VRegA_11x(inst_data);
- ObjPtr<mirror::Object> obj_result = shadow_frame.GetVRegReference(ref_idx);
- if (do_assignability_check && obj_result != nullptr) {
- ObjPtr<mirror::Class> return_type = shadow_frame.GetMethod()->ResolveReturnType();
- // Re-load since it might have moved.
- obj_result = shadow_frame.GetVRegReference(ref_idx);
- if (return_type == nullptr) {
- // Return the pending exception.
- HANDLE_PENDING_EXCEPTION();
- }
- if (!obj_result->VerifierInstanceOf(return_type)) {
- // This should never happen.
- std::string temp1, temp2;
- self->ThrowNewExceptionF("Ljava/lang/InternalError;",
- "Returning '%s' that is not instance of return type '%s'",
- obj_result->GetClass()->GetDescriptor(&temp1),
- return_type->GetDescriptor(&temp2));
- HANDLE_PENDING_EXCEPTION();
- }
- }
- result.SetL(obj_result);
- if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
- !SendMethodExitEvents(self,
- instrumentation,
- shadow_frame,
- shadow_frame.GetThisObject(accessor.InsSize()),
- shadow_frame.GetMethod(),
- inst->GetDexPc(insns),
- result))) {
- HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
- }
- // Re-load since it might have moved during the MethodExitEvent.
- result.SetL(shadow_frame.GetVRegReference(ref_idx));
- if (interpret_one_instruction) {
- /* Signal mterp to return to caller */
- shadow_frame.SetDexPC(dex::kDexNoIndex);
- }
- ctx->result = result;
- return;
- }
- case Instruction::CONST_4: {
- PREAMBLE();
- uint4_t dst = inst->VRegA_11n(inst_data);
- int4_t val = inst->VRegB_11n(inst_data);
- shadow_frame.SetVReg(dst, val);
- if (val == 0) {
- shadow_frame.SetVRegReference(dst, nullptr);
- }
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::CONST_16: {
- PREAMBLE();
- uint8_t dst = inst->VRegA_21s(inst_data);
- int16_t val = inst->VRegB_21s();
- shadow_frame.SetVReg(dst, val);
- if (val == 0) {
- shadow_frame.SetVRegReference(dst, nullptr);
- }
- inst = inst->Next_2xx();
- break;
- }
- case Instruction::CONST: {
- PREAMBLE();
- uint8_t dst = inst->VRegA_31i(inst_data);
- int32_t val = inst->VRegB_31i();
- shadow_frame.SetVReg(dst, val);
- if (val == 0) {
- shadow_frame.SetVRegReference(dst, nullptr);
- }
- inst = inst->Next_3xx();
- break;
- }
- case Instruction::CONST_HIGH16: {
- PREAMBLE();
- uint8_t dst = inst->VRegA_21h(inst_data);
- int32_t val = static_cast<int32_t>(inst->VRegB_21h() << 16);
- shadow_frame.SetVReg(dst, val);
- if (val == 0) {
- shadow_frame.SetVRegReference(dst, nullptr);
- }
- inst = inst->Next_2xx();
- break;
- }
- case Instruction::CONST_WIDE_16:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_21s(inst_data), inst->VRegB_21s());
- inst = inst->Next_2xx();
- break;
- case Instruction::CONST_WIDE_32:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_31i(inst_data), inst->VRegB_31i());
- inst = inst->Next_3xx();
- break;
- case Instruction::CONST_WIDE:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_51l(inst_data), inst->VRegB_51l());
- inst = inst->Next_51l();
- break;
- case Instruction::CONST_WIDE_HIGH16:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_21h(inst_data),
- static_cast<uint64_t>(inst->VRegB_21h()) << 48);
- inst = inst->Next_2xx();
- break;
- case Instruction::CONST_STRING: {
- PREAMBLE();
- ObjPtr<mirror::String> s = ResolveString(self,
- shadow_frame,
- dex::StringIndex(inst->VRegB_21c()));
- if (UNLIKELY(s == nullptr)) {
- HANDLE_PENDING_EXCEPTION();
- } else {
- shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), s);
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::CONST_STRING_JUMBO: {
- PREAMBLE();
- ObjPtr<mirror::String> s = ResolveString(self,
- shadow_frame,
- dex::StringIndex(inst->VRegB_31c()));
- if (UNLIKELY(s == nullptr)) {
- HANDLE_PENDING_EXCEPTION();
- } else {
- shadow_frame.SetVRegReference(inst->VRegA_31c(inst_data), s);
- inst = inst->Next_3xx();
- }
- break;
- }
- case Instruction::CONST_CLASS: {
- PREAMBLE();
- ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()),
- shadow_frame.GetMethod(),
- self,
- false,
- do_access_check);
- if (UNLIKELY(c == nullptr)) {
- HANDLE_PENDING_EXCEPTION();
- } else {
- shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), c);
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::CONST_METHOD_HANDLE: {
- PREAMBLE();
- ClassLinker* cl = Runtime::Current()->GetClassLinker();
- ObjPtr<mirror::MethodHandle> mh = cl->ResolveMethodHandle(self,
- inst->VRegB_21c(),
- shadow_frame.GetMethod());
- if (UNLIKELY(mh == nullptr)) {
- HANDLE_PENDING_EXCEPTION();
- } else {
- shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), mh);
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::CONST_METHOD_TYPE: {
- PREAMBLE();
- ClassLinker* cl = Runtime::Current()->GetClassLinker();
- ObjPtr<mirror::MethodType> mt = cl->ResolveMethodType(self,
- dex::ProtoIndex(inst->VRegB_21c()),
- shadow_frame.GetMethod());
- if (UNLIKELY(mt == nullptr)) {
- HANDLE_PENDING_EXCEPTION();
- } else {
- shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), mt);
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::MONITOR_ENTER: {
- PREAMBLE();
- HANDLE_ASYNC_EXCEPTION();
- ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
- if (UNLIKELY(obj == nullptr)) {
- ThrowNullPointerExceptionFromInterpreter();
- HANDLE_PENDING_EXCEPTION();
- } else {
- DoMonitorEnter<do_assignability_check>(self, &shadow_frame, obj);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
- }
- break;
- }
- case Instruction::MONITOR_EXIT: {
- PREAMBLE();
- HANDLE_ASYNC_EXCEPTION();
- ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
- if (UNLIKELY(obj == nullptr)) {
- ThrowNullPointerExceptionFromInterpreter();
- HANDLE_PENDING_EXCEPTION();
- } else {
- DoMonitorExit<do_assignability_check>(self, &shadow_frame, obj);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
- }
- break;
- }
- case Instruction::CHECK_CAST: {
- PREAMBLE();
- ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()),
- shadow_frame.GetMethod(),
- self,
- false,
- do_access_check);
- if (UNLIKELY(c == nullptr)) {
- HANDLE_PENDING_EXCEPTION();
- } else {
- ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_21c(inst_data));
- if (UNLIKELY(obj != nullptr && !obj->InstanceOf(c))) {
- ThrowClassCastException(c, obj->GetClass());
- HANDLE_PENDING_EXCEPTION();
- } else {
- inst = inst->Next_2xx();
- }
- }
- break;
- }
- case Instruction::INSTANCE_OF: {
- PREAMBLE();
- ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegC_22c()),
- shadow_frame.GetMethod(),
- self,
- false,
- do_access_check);
- if (UNLIKELY(c == nullptr)) {
- HANDLE_PENDING_EXCEPTION();
- } else {
- ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
- shadow_frame.SetVReg(inst->VRegA_22c(inst_data),
- (obj != nullptr && obj->InstanceOf(c)) ? 1 : 0);
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::ARRAY_LENGTH: {
- PREAMBLE();
- ObjPtr<mirror::Object> array = shadow_frame.GetVRegReference(inst->VRegB_12x(inst_data));
- if (UNLIKELY(array == nullptr)) {
- ThrowNullPointerExceptionFromInterpreter();
- HANDLE_PENDING_EXCEPTION();
- } else {
- shadow_frame.SetVReg(inst->VRegA_12x(inst_data), array->AsArray()->GetLength());
- inst = inst->Next_1xx();
- }
- break;
- }
- case Instruction::NEW_INSTANCE: {
- PREAMBLE();
- ObjPtr<mirror::Object> obj = nullptr;
- ObjPtr<mirror::Class> c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()),
- shadow_frame.GetMethod(),
- self,
- false,
- do_access_check);
- if (LIKELY(c != nullptr)) {
- if (UNLIKELY(c->IsStringClass())) {
- gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
- obj = mirror::String::AllocEmptyString<true>(self, allocator_type);
- } else {
- obj = AllocObjectFromCode<true>(
- c.Ptr(),
- self,
- Runtime::Current()->GetHeap()->GetCurrentAllocator());
- }
- }
- if (UNLIKELY(obj == nullptr)) {
- HANDLE_PENDING_EXCEPTION();
- } else {
- obj->GetClass()->AssertInitializedOrInitializingInThread(self);
- // Don't allow finalizable objects to be allocated during a transaction since these can't
- // be finalized without a started runtime.
- if (transaction_active && obj->GetClass()->IsFinalizable()) {
- AbortTransactionF(self, "Allocating finalizable object in transaction: %s",
- obj->PrettyTypeOf().c_str());
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- shadow_frame.SetVRegReference(inst->VRegA_21c(inst_data), obj);
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::NEW_ARRAY: {
- PREAMBLE();
- int32_t length = shadow_frame.GetVReg(inst->VRegB_22c(inst_data));
- ObjPtr<mirror::Object> obj = AllocArrayFromCode<do_access_check, true>(
- dex::TypeIndex(inst->VRegC_22c()),
- length,
- shadow_frame.GetMethod(),
- self,
- Runtime::Current()->GetHeap()->GetCurrentAllocator());
- if (UNLIKELY(obj == nullptr)) {
- HANDLE_PENDING_EXCEPTION();
- } else {
- shadow_frame.SetVRegReference(inst->VRegA_22c(inst_data), obj);
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::FILLED_NEW_ARRAY: {
- PREAMBLE();
- bool success =
- DoFilledNewArray<false, do_access_check, transaction_active>(inst, shadow_frame, self,
- &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::FILLED_NEW_ARRAY_RANGE: {
- PREAMBLE();
- bool success =
- DoFilledNewArray<true, do_access_check, transaction_active>(inst, shadow_frame,
- self, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
- break;
- }
- case Instruction::FILL_ARRAY_DATA: {
- PREAMBLE();
- const uint16_t* payload_addr = reinterpret_cast<const uint16_t*>(inst) + inst->VRegB_31t();
- const Instruction::ArrayDataPayload* payload =
- reinterpret_cast<const Instruction::ArrayDataPayload*>(payload_addr);
- ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegA_31t(inst_data));
- bool success = FillArrayData(obj, payload);
- if (!success) {
- HANDLE_PENDING_EXCEPTION();
+ if (UNLIKELY(interpret_one_instruction)) {
break;
}
- if (transaction_active) {
- RecordArrayElementsInTransaction(obj->AsArray(), payload->element_count);
- }
- inst = inst->Next_3xx();
- break;
- }
- case Instruction::THROW: {
- PREAMBLE();
- HANDLE_ASYNC_EXCEPTION();
- ObjPtr<mirror::Object> exception =
- shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
- if (UNLIKELY(exception == nullptr)) {
- ThrowNullPointerException("throw with null exception");
- } else if (do_assignability_check && !exception->GetClass()->IsThrowableClass()) {
- // This should never happen.
- std::string temp;
- self->ThrowNewExceptionF("Ljava/lang/InternalError;",
- "Throwing '%s' that is not instance of Throwable",
- exception->GetClass()->GetDescriptor(&temp));
- } else {
- self->SetException(exception->AsThrowable());
- }
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- case Instruction::GOTO: {
- PREAMBLE();
- HANDLE_ASYNC_EXCEPTION();
- int8_t offset = inst->VRegA_10t(inst_data);
- BRANCH_INSTRUMENTATION(offset);
- inst = inst->RelativeAt(offset);
- HANDLE_BACKWARD_BRANCH(offset);
- break;
- }
- case Instruction::GOTO_16: {
- PREAMBLE();
- HANDLE_ASYNC_EXCEPTION();
- int16_t offset = inst->VRegA_20t();
- BRANCH_INSTRUMENTATION(offset);
- inst = inst->RelativeAt(offset);
- HANDLE_BACKWARD_BRANCH(offset);
- break;
- }
- case Instruction::GOTO_32: {
- PREAMBLE();
- HANDLE_ASYNC_EXCEPTION();
- int32_t offset = inst->VRegA_30t();
- BRANCH_INSTRUMENTATION(offset);
- inst = inst->RelativeAt(offset);
- HANDLE_BACKWARD_BRANCH(offset);
- break;
- }
- case Instruction::PACKED_SWITCH: {
- PREAMBLE();
- int32_t offset = DoPackedSwitch(inst, shadow_frame, inst_data);
- BRANCH_INSTRUMENTATION(offset);
- inst = inst->RelativeAt(offset);
- HANDLE_BACKWARD_BRANCH(offset);
- break;
- }
- case Instruction::SPARSE_SWITCH: {
- PREAMBLE();
- int32_t offset = DoSparseSwitch(inst, shadow_frame, inst_data);
- BRANCH_INSTRUMENTATION(offset);
- inst = inst->RelativeAt(offset);
- HANDLE_BACKWARD_BRANCH(offset);
- break;
- }
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wfloat-equal"
-
- case Instruction::CMPL_FLOAT: {
- PREAMBLE();
- float val1 = shadow_frame.GetVRegFloat(inst->VRegB_23x());
- float val2 = shadow_frame.GetVRegFloat(inst->VRegC_23x());
- int32_t result;
- if (val1 > val2) {
- result = 1;
- } else if (val1 == val2) {
- result = 0;
- } else {
- result = -1;
- }
- shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
- inst = inst->Next_2xx();
- break;
- }
- case Instruction::CMPG_FLOAT: {
- PREAMBLE();
- float val1 = shadow_frame.GetVRegFloat(inst->VRegB_23x());
- float val2 = shadow_frame.GetVRegFloat(inst->VRegC_23x());
- int32_t result;
- if (val1 < val2) {
- result = -1;
- } else if (val1 == val2) {
- result = 0;
- } else {
- result = 1;
- }
- shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
- inst = inst->Next_2xx();
- break;
- }
- case Instruction::CMPL_DOUBLE: {
- PREAMBLE();
- double val1 = shadow_frame.GetVRegDouble(inst->VRegB_23x());
- double val2 = shadow_frame.GetVRegDouble(inst->VRegC_23x());
- int32_t result;
- if (val1 > val2) {
- result = 1;
- } else if (val1 == val2) {
- result = 0;
- } else {
- result = -1;
- }
- shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
- inst = inst->Next_2xx();
- break;
- }
-
- case Instruction::CMPG_DOUBLE: {
- PREAMBLE();
- double val1 = shadow_frame.GetVRegDouble(inst->VRegB_23x());
- double val2 = shadow_frame.GetVRegDouble(inst->VRegC_23x());
- int32_t result;
- if (val1 < val2) {
- result = -1;
- } else if (val1 == val2) {
- result = 0;
- } else {
- result = 1;
- }
- shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
- inst = inst->Next_2xx();
- break;
- }
-
-#pragma clang diagnostic pop
-
- case Instruction::CMP_LONG: {
- PREAMBLE();
- int64_t val1 = shadow_frame.GetVRegLong(inst->VRegB_23x());
- int64_t val2 = shadow_frame.GetVRegLong(inst->VRegC_23x());
- int32_t result;
- if (val1 > val2) {
- result = 1;
- } else if (val1 == val2) {
- result = 0;
- } else {
- result = -1;
- }
- shadow_frame.SetVReg(inst->VRegA_23x(inst_data), result);
- inst = inst->Next_2xx();
- break;
- }
- case Instruction::IF_EQ: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) ==
- shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
- int16_t offset = inst->VRegC_22t();
- BRANCH_INSTRUMENTATION(offset);
- inst = inst->RelativeAt(offset);
- HANDLE_BACKWARD_BRANCH(offset);
- } else {
- BRANCH_INSTRUMENTATION(2);
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_NE: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) !=
- shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
- int16_t offset = inst->VRegC_22t();
- BRANCH_INSTRUMENTATION(offset);
- inst = inst->RelativeAt(offset);
- HANDLE_BACKWARD_BRANCH(offset);
- } else {
- BRANCH_INSTRUMENTATION(2);
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_LT: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) <
- shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
- int16_t offset = inst->VRegC_22t();
- BRANCH_INSTRUMENTATION(offset);
- inst = inst->RelativeAt(offset);
- HANDLE_BACKWARD_BRANCH(offset);
- } else {
- BRANCH_INSTRUMENTATION(2);
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_GE: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) >=
- shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
- int16_t offset = inst->VRegC_22t();
- BRANCH_INSTRUMENTATION(offset);
- inst = inst->RelativeAt(offset);
- HANDLE_BACKWARD_BRANCH(offset);
- } else {
- BRANCH_INSTRUMENTATION(2);
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_GT: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) >
- shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
- int16_t offset = inst->VRegC_22t();
- BRANCH_INSTRUMENTATION(offset);
- inst = inst->RelativeAt(offset);
- HANDLE_BACKWARD_BRANCH(offset);
- } else {
- BRANCH_INSTRUMENTATION(2);
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_LE: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_22t(inst_data)) <=
- shadow_frame.GetVReg(inst->VRegB_22t(inst_data))) {
- int16_t offset = inst->VRegC_22t();
- BRANCH_INSTRUMENTATION(offset);
- inst = inst->RelativeAt(offset);
- HANDLE_BACKWARD_BRANCH(offset);
- } else {
- BRANCH_INSTRUMENTATION(2);
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_EQZ: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) == 0) {
- int16_t offset = inst->VRegB_21t();
- BRANCH_INSTRUMENTATION(offset);
- inst = inst->RelativeAt(offset);
- HANDLE_BACKWARD_BRANCH(offset);
- } else {
- BRANCH_INSTRUMENTATION(2);
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_NEZ: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) != 0) {
- int16_t offset = inst->VRegB_21t();
- BRANCH_INSTRUMENTATION(offset);
- inst = inst->RelativeAt(offset);
- HANDLE_BACKWARD_BRANCH(offset);
- } else {
- BRANCH_INSTRUMENTATION(2);
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_LTZ: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) < 0) {
- int16_t offset = inst->VRegB_21t();
- BRANCH_INSTRUMENTATION(offset);
- inst = inst->RelativeAt(offset);
- HANDLE_BACKWARD_BRANCH(offset);
- } else {
- BRANCH_INSTRUMENTATION(2);
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_GEZ: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) >= 0) {
- int16_t offset = inst->VRegB_21t();
- BRANCH_INSTRUMENTATION(offset);
- inst = inst->RelativeAt(offset);
- HANDLE_BACKWARD_BRANCH(offset);
- } else {
- BRANCH_INSTRUMENTATION(2);
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_GTZ: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) > 0) {
- int16_t offset = inst->VRegB_21t();
- BRANCH_INSTRUMENTATION(offset);
- inst = inst->RelativeAt(offset);
- HANDLE_BACKWARD_BRANCH(offset);
- } else {
- BRANCH_INSTRUMENTATION(2);
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::IF_LEZ: {
- PREAMBLE();
- if (shadow_frame.GetVReg(inst->VRegA_21t(inst_data)) <= 0) {
- int16_t offset = inst->VRegB_21t();
- BRANCH_INSTRUMENTATION(offset);
- inst = inst->RelativeAt(offset);
- HANDLE_BACKWARD_BRANCH(offset);
- } else {
- BRANCH_INSTRUMENTATION(2);
- inst = inst->Next_2xx();
- }
- break;
- }
- case Instruction::AGET_BOOLEAN: {
- PREAMBLE();
- ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == nullptr)) {
- ThrowNullPointerExceptionFromInterpreter();
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- ObjPtr<mirror::BooleanArray> array = a->AsBooleanArray();
- if (array->CheckIsValidIndex(index)) {
- shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::AGET_BYTE: {
- PREAMBLE();
- ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == nullptr)) {
- ThrowNullPointerExceptionFromInterpreter();
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- ObjPtr<mirror::ByteArray> array = a->AsByteArray();
- if (array->CheckIsValidIndex(index)) {
- shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::AGET_CHAR: {
- PREAMBLE();
- ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == nullptr)) {
- ThrowNullPointerExceptionFromInterpreter();
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- ObjPtr<mirror::CharArray> array = a->AsCharArray();
- if (array->CheckIsValidIndex(index)) {
- shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::AGET_SHORT: {
- PREAMBLE();
- ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == nullptr)) {
- ThrowNullPointerExceptionFromInterpreter();
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- ObjPtr<mirror::ShortArray> array = a->AsShortArray();
- if (array->CheckIsValidIndex(index)) {
- shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::AGET: {
- PREAMBLE();
- ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == nullptr)) {
- ThrowNullPointerExceptionFromInterpreter();
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- DCHECK(a->IsIntArray() || a->IsFloatArray()) << a->PrettyTypeOf();
- ObjPtr<mirror::IntArray> array = ObjPtr<mirror::IntArray>::DownCast(a);
- if (array->CheckIsValidIndex(index)) {
- shadow_frame.SetVReg(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::AGET_WIDE: {
- PREAMBLE();
- ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == nullptr)) {
- ThrowNullPointerExceptionFromInterpreter();
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- DCHECK(a->IsLongArray() || a->IsDoubleArray()) << a->PrettyTypeOf();
- ObjPtr<mirror::LongArray> array = ObjPtr<mirror::LongArray>::DownCast(a);
- if (array->CheckIsValidIndex(index)) {
- shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::AGET_OBJECT: {
- PREAMBLE();
- ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == nullptr)) {
- ThrowNullPointerExceptionFromInterpreter();
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- ObjPtr<mirror::ObjectArray<mirror::Object>> array = a->AsObjectArray<mirror::Object>();
- if (array->CheckIsValidIndex(index)) {
- shadow_frame.SetVRegReference(inst->VRegA_23x(inst_data), array->GetWithoutChecks(index));
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::APUT_BOOLEAN: {
- PREAMBLE();
- ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == nullptr)) {
- ThrowNullPointerExceptionFromInterpreter();
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- uint8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- ObjPtr<mirror::BooleanArray> array = a->AsBooleanArray();
- if (array->CheckIsValidIndex(index)) {
- array->SetWithoutChecks<transaction_active>(index, val);
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::APUT_BYTE: {
- PREAMBLE();
- ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == nullptr)) {
- ThrowNullPointerExceptionFromInterpreter();
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- ObjPtr<mirror::ByteArray> array = a->AsByteArray();
- if (array->CheckIsValidIndex(index)) {
- array->SetWithoutChecks<transaction_active>(index, val);
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::APUT_CHAR: {
- PREAMBLE();
- ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == nullptr)) {
- ThrowNullPointerExceptionFromInterpreter();
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- uint16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- ObjPtr<mirror::CharArray> array = a->AsCharArray();
- if (array->CheckIsValidIndex(index)) {
- array->SetWithoutChecks<transaction_active>(index, val);
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::APUT_SHORT: {
- PREAMBLE();
- ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == nullptr)) {
- ThrowNullPointerExceptionFromInterpreter();
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- ObjPtr<mirror::ShortArray> array = a->AsShortArray();
- if (array->CheckIsValidIndex(index)) {
- array->SetWithoutChecks<transaction_active>(index, val);
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::APUT: {
- PREAMBLE();
- ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == nullptr)) {
- ThrowNullPointerExceptionFromInterpreter();
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int32_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- DCHECK(a->IsIntArray() || a->IsFloatArray()) << a->PrettyTypeOf();
- ObjPtr<mirror::IntArray> array = ObjPtr<mirror::IntArray>::DownCast(a);
- if (array->CheckIsValidIndex(index)) {
- array->SetWithoutChecks<transaction_active>(index, val);
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::APUT_WIDE: {
- PREAMBLE();
- ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == nullptr)) {
- ThrowNullPointerExceptionFromInterpreter();
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int64_t val = shadow_frame.GetVRegLong(inst->VRegA_23x(inst_data));
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- DCHECK(a->IsLongArray() || a->IsDoubleArray()) << a->PrettyTypeOf();
- ObjPtr<mirror::LongArray> array = ObjPtr<mirror::LongArray>::DownCast(a);
- if (array->CheckIsValidIndex(index)) {
- array->SetWithoutChecks<transaction_active>(index, val);
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::APUT_OBJECT: {
- PREAMBLE();
- ObjPtr<mirror::Object> a = shadow_frame.GetVRegReference(inst->VRegB_23x());
- if (UNLIKELY(a == nullptr)) {
- ThrowNullPointerExceptionFromInterpreter();
- HANDLE_PENDING_EXCEPTION();
- break;
- }
- int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
- ObjPtr<mirror::Object> val = shadow_frame.GetVRegReference(inst->VRegA_23x(inst_data));
- ObjPtr<mirror::ObjectArray<mirror::Object>> array = a->AsObjectArray<mirror::Object>();
- if (array->CheckIsValidIndex(index) && array->CheckAssignable(val)) {
- array->SetWithoutChecks<transaction_active>(index, val);
- inst = inst->Next_2xx();
- } else {
- HANDLE_PENDING_EXCEPTION();
- }
- break;
- }
- case Instruction::IGET_BOOLEAN: {
- PREAMBLE();
- bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimBoolean, do_access_check>(
- self, shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET_BYTE: {
- PREAMBLE();
- bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimByte, do_access_check>(
- self, shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET_CHAR: {
- PREAMBLE();
- bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimChar, do_access_check>(
- self, shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET_SHORT: {
- PREAMBLE();
- bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimShort, do_access_check>(
- self, shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET: {
- PREAMBLE();
- bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimInt, do_access_check>(
- self, shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET_WIDE: {
- PREAMBLE();
- bool success = DoFieldGet<InstancePrimitiveRead, Primitive::kPrimLong, do_access_check>(
- self, shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET_OBJECT: {
- PREAMBLE();
- bool success = DoFieldGet<InstanceObjectRead, Primitive::kPrimNot, do_access_check>(
- self, shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET_QUICK: {
- PREAMBLE();
- bool success = DoIGetQuick<Primitive::kPrimInt>(shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET_WIDE_QUICK: {
- PREAMBLE();
- bool success = DoIGetQuick<Primitive::kPrimLong>(shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET_OBJECT_QUICK: {
- PREAMBLE();
- bool success = DoIGetQuick<Primitive::kPrimNot>(shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET_BOOLEAN_QUICK: {
- PREAMBLE();
- bool success = DoIGetQuick<Primitive::kPrimBoolean>(shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET_BYTE_QUICK: {
- PREAMBLE();
- bool success = DoIGetQuick<Primitive::kPrimByte>(shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET_CHAR_QUICK: {
- PREAMBLE();
- bool success = DoIGetQuick<Primitive::kPrimChar>(shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IGET_SHORT_QUICK: {
- PREAMBLE();
- bool success = DoIGetQuick<Primitive::kPrimShort>(shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SGET_BOOLEAN: {
- PREAMBLE();
- bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimBoolean, do_access_check,
- transaction_active>(self, shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SGET_BYTE: {
- PREAMBLE();
- bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimByte, do_access_check,
- transaction_active>(self, shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SGET_CHAR: {
- PREAMBLE();
- bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimChar, do_access_check,
- transaction_active>(self, shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SGET_SHORT: {
- PREAMBLE();
- bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimShort, do_access_check,
- transaction_active>(self, shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SGET: {
- PREAMBLE();
- bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimInt, do_access_check,
- transaction_active>(self, shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SGET_WIDE: {
- PREAMBLE();
- bool success = DoFieldGet<StaticPrimitiveRead, Primitive::kPrimLong, do_access_check,
- transaction_active>(self, shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SGET_OBJECT: {
- PREAMBLE();
- bool success = DoFieldGet<StaticObjectRead, Primitive::kPrimNot, do_access_check,
- transaction_active>(self, shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_BOOLEAN: {
- PREAMBLE();
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimBoolean, do_access_check,
- transaction_active>(self, shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_BYTE: {
- PREAMBLE();
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimByte, do_access_check,
- transaction_active>(self, shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_CHAR: {
- PREAMBLE();
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimChar, do_access_check,
- transaction_active>(self, shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_SHORT: {
- PREAMBLE();
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimShort, do_access_check,
- transaction_active>(self, shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT: {
- PREAMBLE();
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimInt, do_access_check,
- transaction_active>(self, shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_WIDE: {
- PREAMBLE();
- bool success = DoFieldPut<InstancePrimitiveWrite, Primitive::kPrimLong, do_access_check,
- transaction_active>(self, shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_OBJECT: {
- PREAMBLE();
- bool success = DoFieldPut<InstanceObjectWrite, Primitive::kPrimNot, do_access_check,
- transaction_active>(self, shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_QUICK: {
- PREAMBLE();
- bool success = DoIPutQuick<Primitive::kPrimInt, transaction_active>(
- shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_BOOLEAN_QUICK: {
- PREAMBLE();
- bool success = DoIPutQuick<Primitive::kPrimBoolean, transaction_active>(
- shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_BYTE_QUICK: {
- PREAMBLE();
- bool success = DoIPutQuick<Primitive::kPrimByte, transaction_active>(
- shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_CHAR_QUICK: {
- PREAMBLE();
- bool success = DoIPutQuick<Primitive::kPrimChar, transaction_active>(
- shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_SHORT_QUICK: {
- PREAMBLE();
- bool success = DoIPutQuick<Primitive::kPrimShort, transaction_active>(
- shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_WIDE_QUICK: {
- PREAMBLE();
- bool success = DoIPutQuick<Primitive::kPrimLong, transaction_active>(
- shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::IPUT_OBJECT_QUICK: {
- PREAMBLE();
- bool success = DoIPutQuick<Primitive::kPrimNot, transaction_active>(
- shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SPUT_BOOLEAN: {
- PREAMBLE();
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimBoolean, do_access_check,
- transaction_active>(self, shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SPUT_BYTE: {
- PREAMBLE();
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimByte, do_access_check,
- transaction_active>(self, shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SPUT_CHAR: {
- PREAMBLE();
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimChar, do_access_check,
- transaction_active>(self, shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SPUT_SHORT: {
- PREAMBLE();
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimShort, do_access_check,
- transaction_active>(self, shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SPUT: {
- PREAMBLE();
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimInt, do_access_check,
- transaction_active>(self, shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SPUT_WIDE: {
- PREAMBLE();
- bool success = DoFieldPut<StaticPrimitiveWrite, Primitive::kPrimLong, do_access_check,
- transaction_active>(self, shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SPUT_OBJECT: {
- PREAMBLE();
- bool success = DoFieldPut<StaticObjectWrite, Primitive::kPrimNot, do_access_check,
- transaction_active>(self, shadow_frame, inst, inst_data);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::INVOKE_VIRTUAL: {
- PREAMBLE();
- bool success = DoInvoke<kVirtual, false, do_access_check, /*is_mterp=*/ false>(
- self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
- break;
- }
- case Instruction::INVOKE_VIRTUAL_RANGE: {
- PREAMBLE();
- bool success = DoInvoke<kVirtual, true, do_access_check, /*is_mterp=*/ false>(
- self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
- break;
- }
- case Instruction::INVOKE_SUPER: {
- PREAMBLE();
- bool success = DoInvoke<kSuper, false, do_access_check, /*is_mterp=*/ false>(
- self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
- break;
- }
- case Instruction::INVOKE_SUPER_RANGE: {
- PREAMBLE();
- bool success = DoInvoke<kSuper, true, do_access_check, /*is_mterp=*/ false>(
- self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
- break;
- }
- case Instruction::INVOKE_DIRECT: {
- PREAMBLE();
- bool success = DoInvoke<kDirect, false, do_access_check, /*is_mterp=*/ false>(
- self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
- break;
- }
- case Instruction::INVOKE_DIRECT_RANGE: {
- PREAMBLE();
- bool success = DoInvoke<kDirect, true, do_access_check, /*is_mterp=*/ false>(
- self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
- break;
- }
- case Instruction::INVOKE_INTERFACE: {
- PREAMBLE();
- bool success = DoInvoke<kInterface, false, do_access_check, /*is_mterp=*/ false>(
- self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
- break;
- }
- case Instruction::INVOKE_INTERFACE_RANGE: {
- PREAMBLE();
- bool success = DoInvoke<kInterface, true, do_access_check, /*is_mterp=*/ false>(
- self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
- break;
- }
- case Instruction::INVOKE_STATIC: {
- PREAMBLE();
- bool success = DoInvoke<kStatic, false, do_access_check, /*is_mterp=*/ false>(
- self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
- break;
- }
- case Instruction::INVOKE_STATIC_RANGE: {
- PREAMBLE();
- bool success = DoInvoke<kStatic, true, do_access_check, /*is_mterp=*/ false>(
- self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
- break;
- }
- case Instruction::INVOKE_VIRTUAL_QUICK: {
- PREAMBLE();
- bool success = DoInvoke<kVirtual, false, do_access_check, /*is_mterp=*/ false,
- /*is_quick=*/ true>(self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
- break;
- }
- case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: {
- PREAMBLE();
- bool success = DoInvoke<kVirtual, true, do_access_check, /*is_mterp=*/ false,
- /*is_quick=*/ true>(self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
- break;
- }
- case Instruction::INVOKE_POLYMORPHIC: {
- PREAMBLE();
- DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
- bool success = DoInvokePolymorphic</* is_range= */ false>(
- self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_POLYMORPHIC(!success);
- break;
- }
- case Instruction::INVOKE_POLYMORPHIC_RANGE: {
- PREAMBLE();
- DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
- bool success = DoInvokePolymorphic</* is_range= */ true>(
- self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_POLYMORPHIC(!success);
- break;
- }
- case Instruction::INVOKE_CUSTOM: {
- PREAMBLE();
- DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
- bool success = DoInvokeCustom</* is_range= */ false>(
- self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
- break;
- }
- case Instruction::INVOKE_CUSTOM_RANGE: {
- PREAMBLE();
- DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
- bool success = DoInvokeCustom</* is_range= */ true>(
- self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
- break;
- }
- case Instruction::NEG_INT:
- PREAMBLE();
- shadow_frame.SetVReg(
- inst->VRegA_12x(inst_data), -shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- case Instruction::NOT_INT:
- PREAMBLE();
- shadow_frame.SetVReg(
- inst->VRegA_12x(inst_data), ~shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- case Instruction::NEG_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(
- inst->VRegA_12x(inst_data), -shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- case Instruction::NOT_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(
- inst->VRegA_12x(inst_data), ~shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- case Instruction::NEG_FLOAT:
- PREAMBLE();
- shadow_frame.SetVRegFloat(
- inst->VRegA_12x(inst_data), -shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- case Instruction::NEG_DOUBLE:
- PREAMBLE();
- shadow_frame.SetVRegDouble(
- inst->VRegA_12x(inst_data), -shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- case Instruction::INT_TO_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data),
- shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- case Instruction::INT_TO_FLOAT:
- PREAMBLE();
- shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data),
- shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- case Instruction::INT_TO_DOUBLE:
- PREAMBLE();
- shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data),
- shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- case Instruction::LONG_TO_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_12x(inst_data),
- shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- case Instruction::LONG_TO_FLOAT:
- PREAMBLE();
- shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data),
- shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- case Instruction::LONG_TO_DOUBLE:
- PREAMBLE();
- shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data),
- shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- case Instruction::FLOAT_TO_INT: {
- PREAMBLE();
- float val = shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data));
- int32_t result = art_float_to_integral<int32_t, float>(val);
- shadow_frame.SetVReg(inst->VRegA_12x(inst_data), result);
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::FLOAT_TO_LONG: {
- PREAMBLE();
- float val = shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data));
- int64_t result = art_float_to_integral<int64_t, float>(val);
- shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), result);
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::FLOAT_TO_DOUBLE:
- PREAMBLE();
- shadow_frame.SetVRegDouble(inst->VRegA_12x(inst_data),
- shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- case Instruction::DOUBLE_TO_INT: {
- PREAMBLE();
- double val = shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data));
- int32_t result = art_float_to_integral<int32_t, double>(val);
- shadow_frame.SetVReg(inst->VRegA_12x(inst_data), result);
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::DOUBLE_TO_LONG: {
- PREAMBLE();
- double val = shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data));
- int64_t result = art_float_to_integral<int64_t, double>(val);
- shadow_frame.SetVRegLong(inst->VRegA_12x(inst_data), result);
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::DOUBLE_TO_FLOAT:
- PREAMBLE();
- shadow_frame.SetVRegFloat(inst->VRegA_12x(inst_data),
- shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- case Instruction::INT_TO_BYTE:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_12x(inst_data), static_cast<int8_t>(
- shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
- inst = inst->Next_1xx();
- break;
- case Instruction::INT_TO_CHAR:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_12x(inst_data), static_cast<uint16_t>(
- shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
- inst = inst->Next_1xx();
- break;
- case Instruction::INT_TO_SHORT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_12x(inst_data), static_cast<int16_t>(
- shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
- inst = inst->Next_1xx();
- break;
- case Instruction::ADD_INT: {
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
- SafeAdd(shadow_frame.GetVReg(inst->VRegB_23x()),
- shadow_frame.GetVReg(inst->VRegC_23x())));
- inst = inst->Next_2xx();
- break;
- }
- case Instruction::SUB_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
- SafeSub(shadow_frame.GetVReg(inst->VRegB_23x()),
- shadow_frame.GetVReg(inst->VRegC_23x())));
- inst = inst->Next_2xx();
- break;
- case Instruction::MUL_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
- SafeMul(shadow_frame.GetVReg(inst->VRegB_23x()),
- shadow_frame.GetVReg(inst->VRegC_23x())));
- inst = inst->Next_2xx();
- break;
- case Instruction::DIV_INT: {
- PREAMBLE();
- bool success = DoIntDivide(shadow_frame, inst->VRegA_23x(inst_data),
- shadow_frame.GetVReg(inst->VRegB_23x()),
- shadow_frame.GetVReg(inst->VRegC_23x()));
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::REM_INT: {
- PREAMBLE();
- bool success = DoIntRemainder(shadow_frame, inst->VRegA_23x(inst_data),
- shadow_frame.GetVReg(inst->VRegB_23x()),
- shadow_frame.GetVReg(inst->VRegC_23x()));
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::SHL_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
- shadow_frame.GetVReg(inst->VRegB_23x()) <<
- (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
- inst = inst->Next_2xx();
- break;
- case Instruction::SHR_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
- shadow_frame.GetVReg(inst->VRegB_23x()) >>
- (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
- inst = inst->Next_2xx();
- break;
- case Instruction::USHR_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
- static_cast<uint32_t>(shadow_frame.GetVReg(inst->VRegB_23x())) >>
- (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x1f));
- inst = inst->Next_2xx();
- break;
- case Instruction::AND_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
- shadow_frame.GetVReg(inst->VRegB_23x()) &
- shadow_frame.GetVReg(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::OR_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
- shadow_frame.GetVReg(inst->VRegB_23x()) |
- shadow_frame.GetVReg(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::XOR_INT:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_23x(inst_data),
- shadow_frame.GetVReg(inst->VRegB_23x()) ^
- shadow_frame.GetVReg(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::ADD_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
- SafeAdd(shadow_frame.GetVRegLong(inst->VRegB_23x()),
- shadow_frame.GetVRegLong(inst->VRegC_23x())));
- inst = inst->Next_2xx();
- break;
- case Instruction::SUB_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
- SafeSub(shadow_frame.GetVRegLong(inst->VRegB_23x()),
- shadow_frame.GetVRegLong(inst->VRegC_23x())));
- inst = inst->Next_2xx();
- break;
- case Instruction::MUL_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
- SafeMul(shadow_frame.GetVRegLong(inst->VRegB_23x()),
- shadow_frame.GetVRegLong(inst->VRegC_23x())));
- inst = inst->Next_2xx();
- break;
- case Instruction::DIV_LONG:
- PREAMBLE();
- DoLongDivide(shadow_frame, inst->VRegA_23x(inst_data),
- shadow_frame.GetVRegLong(inst->VRegB_23x()),
- shadow_frame.GetVRegLong(inst->VRegC_23x()));
- POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_2xx);
- break;
- case Instruction::REM_LONG:
- PREAMBLE();
- DoLongRemainder(shadow_frame, inst->VRegA_23x(inst_data),
- shadow_frame.GetVRegLong(inst->VRegB_23x()),
- shadow_frame.GetVRegLong(inst->VRegC_23x()));
- POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_2xx);
- break;
- case Instruction::AND_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
- shadow_frame.GetVRegLong(inst->VRegB_23x()) &
- shadow_frame.GetVRegLong(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::OR_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
- shadow_frame.GetVRegLong(inst->VRegB_23x()) |
- shadow_frame.GetVRegLong(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::XOR_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
- shadow_frame.GetVRegLong(inst->VRegB_23x()) ^
- shadow_frame.GetVRegLong(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::SHL_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
- shadow_frame.GetVRegLong(inst->VRegB_23x()) <<
- (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
- inst = inst->Next_2xx();
- break;
- case Instruction::SHR_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
- shadow_frame.GetVRegLong(inst->VRegB_23x()) >>
- (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
- inst = inst->Next_2xx();
- break;
- case Instruction::USHR_LONG:
- PREAMBLE();
- shadow_frame.SetVRegLong(inst->VRegA_23x(inst_data),
- static_cast<uint64_t>(shadow_frame.GetVRegLong(inst->VRegB_23x())) >>
- (shadow_frame.GetVReg(inst->VRegC_23x()) & 0x3f));
- inst = inst->Next_2xx();
- break;
- case Instruction::ADD_FLOAT:
- PREAMBLE();
- shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
- shadow_frame.GetVRegFloat(inst->VRegB_23x()) +
- shadow_frame.GetVRegFloat(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::SUB_FLOAT:
- PREAMBLE();
- shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
- shadow_frame.GetVRegFloat(inst->VRegB_23x()) -
- shadow_frame.GetVRegFloat(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::MUL_FLOAT:
- PREAMBLE();
- shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
- shadow_frame.GetVRegFloat(inst->VRegB_23x()) *
- shadow_frame.GetVRegFloat(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::DIV_FLOAT:
- PREAMBLE();
- shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
- shadow_frame.GetVRegFloat(inst->VRegB_23x()) /
- shadow_frame.GetVRegFloat(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::REM_FLOAT:
- PREAMBLE();
- shadow_frame.SetVRegFloat(inst->VRegA_23x(inst_data),
- fmodf(shadow_frame.GetVRegFloat(inst->VRegB_23x()),
- shadow_frame.GetVRegFloat(inst->VRegC_23x())));
- inst = inst->Next_2xx();
- break;
- case Instruction::ADD_DOUBLE:
- PREAMBLE();
- shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
- shadow_frame.GetVRegDouble(inst->VRegB_23x()) +
- shadow_frame.GetVRegDouble(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::SUB_DOUBLE:
- PREAMBLE();
- shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
- shadow_frame.GetVRegDouble(inst->VRegB_23x()) -
- shadow_frame.GetVRegDouble(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::MUL_DOUBLE:
- PREAMBLE();
- shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
- shadow_frame.GetVRegDouble(inst->VRegB_23x()) *
- shadow_frame.GetVRegDouble(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::DIV_DOUBLE:
- PREAMBLE();
- shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
- shadow_frame.GetVRegDouble(inst->VRegB_23x()) /
- shadow_frame.GetVRegDouble(inst->VRegC_23x()));
- inst = inst->Next_2xx();
- break;
- case Instruction::REM_DOUBLE:
- PREAMBLE();
- shadow_frame.SetVRegDouble(inst->VRegA_23x(inst_data),
- fmod(shadow_frame.GetVRegDouble(inst->VRegB_23x()),
- shadow_frame.GetVRegDouble(inst->VRegC_23x())));
- inst = inst->Next_2xx();
- break;
- case Instruction::ADD_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- shadow_frame.SetVReg(vregA, SafeAdd(shadow_frame.GetVReg(vregA),
- shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::SUB_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- shadow_frame.SetVReg(vregA,
- SafeSub(shadow_frame.GetVReg(vregA),
- shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::MUL_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- shadow_frame.SetVReg(vregA,
- SafeMul(shadow_frame.GetVReg(vregA),
- shadow_frame.GetVReg(inst->VRegB_12x(inst_data))));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::DIV_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- bool success = DoIntDivide(shadow_frame, vregA, shadow_frame.GetVReg(vregA),
- shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_1xx);
- break;
- }
- case Instruction::REM_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- bool success = DoIntRemainder(shadow_frame, vregA, shadow_frame.GetVReg(vregA),
- shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_1xx);
- break;
- }
- case Instruction::SHL_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- shadow_frame.SetVReg(vregA,
- shadow_frame.GetVReg(vregA) <<
- (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::SHR_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- shadow_frame.SetVReg(vregA,
- shadow_frame.GetVReg(vregA) >>
- (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::USHR_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- shadow_frame.SetVReg(vregA,
- static_cast<uint32_t>(shadow_frame.GetVReg(vregA)) >>
- (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x1f));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::AND_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- shadow_frame.SetVReg(vregA,
- shadow_frame.GetVReg(vregA) &
- shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::OR_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- shadow_frame.SetVReg(vregA,
- shadow_frame.GetVReg(vregA) |
- shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::XOR_INT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- shadow_frame.SetVReg(vregA,
- shadow_frame.GetVReg(vregA) ^
- shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::ADD_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- shadow_frame.SetVRegLong(vregA,
- SafeAdd(shadow_frame.GetVRegLong(vregA),
- shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::SUB_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- shadow_frame.SetVRegLong(vregA,
- SafeSub(shadow_frame.GetVRegLong(vregA),
- shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::MUL_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- shadow_frame.SetVRegLong(vregA,
- SafeMul(shadow_frame.GetVRegLong(vregA),
- shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data))));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::DIV_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- DoLongDivide(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA),
- shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
- POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
- break;
- }
- case Instruction::REM_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- DoLongRemainder(shadow_frame, vregA, shadow_frame.GetVRegLong(vregA),
- shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
- POSSIBLY_HANDLE_PENDING_EXCEPTION(self->IsExceptionPending(), Next_1xx);
- break;
- }
- case Instruction::AND_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- shadow_frame.SetVRegLong(vregA,
- shadow_frame.GetVRegLong(vregA) &
- shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::OR_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- shadow_frame.SetVRegLong(vregA,
- shadow_frame.GetVRegLong(vregA) |
- shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::XOR_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- shadow_frame.SetVRegLong(vregA,
- shadow_frame.GetVRegLong(vregA) ^
- shadow_frame.GetVRegLong(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::SHL_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- shadow_frame.SetVRegLong(vregA,
- shadow_frame.GetVRegLong(vregA) <<
- (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::SHR_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- shadow_frame.SetVRegLong(vregA,
- shadow_frame.GetVRegLong(vregA) >>
- (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::USHR_LONG_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- shadow_frame.SetVRegLong(vregA,
- static_cast<uint64_t>(shadow_frame.GetVRegLong(vregA)) >>
- (shadow_frame.GetVReg(inst->VRegB_12x(inst_data)) & 0x3f));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::ADD_FLOAT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- shadow_frame.SetVRegFloat(vregA,
- shadow_frame.GetVRegFloat(vregA) +
- shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::SUB_FLOAT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- shadow_frame.SetVRegFloat(vregA,
- shadow_frame.GetVRegFloat(vregA) -
- shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::MUL_FLOAT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- shadow_frame.SetVRegFloat(vregA,
- shadow_frame.GetVRegFloat(vregA) *
- shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::DIV_FLOAT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- shadow_frame.SetVRegFloat(vregA,
- shadow_frame.GetVRegFloat(vregA) /
- shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::REM_FLOAT_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- shadow_frame.SetVRegFloat(vregA,
- fmodf(shadow_frame.GetVRegFloat(vregA),
- shadow_frame.GetVRegFloat(inst->VRegB_12x(inst_data))));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::ADD_DOUBLE_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- shadow_frame.SetVRegDouble(vregA,
- shadow_frame.GetVRegDouble(vregA) +
- shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::SUB_DOUBLE_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- shadow_frame.SetVRegDouble(vregA,
- shadow_frame.GetVRegDouble(vregA) -
- shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::MUL_DOUBLE_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- shadow_frame.SetVRegDouble(vregA,
- shadow_frame.GetVRegDouble(vregA) *
- shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::DIV_DOUBLE_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- shadow_frame.SetVRegDouble(vregA,
- shadow_frame.GetVRegDouble(vregA) /
- shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data)));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::REM_DOUBLE_2ADDR: {
- PREAMBLE();
- uint4_t vregA = inst->VRegA_12x(inst_data);
- shadow_frame.SetVRegDouble(vregA,
- fmod(shadow_frame.GetVRegDouble(vregA),
- shadow_frame.GetVRegDouble(inst->VRegB_12x(inst_data))));
- inst = inst->Next_1xx();
- break;
- }
- case Instruction::ADD_INT_LIT16:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
- SafeAdd(shadow_frame.GetVReg(inst->VRegB_22s(inst_data)),
- inst->VRegC_22s()));
- inst = inst->Next_2xx();
- break;
- case Instruction::RSUB_INT_LIT16:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
- SafeSub(inst->VRegC_22s(),
- shadow_frame.GetVReg(inst->VRegB_22s(inst_data))));
- inst = inst->Next_2xx();
- break;
- case Instruction::MUL_INT_LIT16:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
- SafeMul(shadow_frame.GetVReg(inst->VRegB_22s(inst_data)),
- inst->VRegC_22s()));
- inst = inst->Next_2xx();
- break;
- case Instruction::DIV_INT_LIT16: {
- PREAMBLE();
- bool success = DoIntDivide(shadow_frame, inst->VRegA_22s(inst_data),
- shadow_frame.GetVReg(inst->VRegB_22s(inst_data)),
- inst->VRegC_22s());
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::REM_INT_LIT16: {
- PREAMBLE();
- bool success = DoIntRemainder(shadow_frame, inst->VRegA_22s(inst_data),
- shadow_frame.GetVReg(inst->VRegB_22s(inst_data)),
- inst->VRegC_22s());
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::AND_INT_LIT16:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
- shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) &
- inst->VRegC_22s());
- inst = inst->Next_2xx();
- break;
- case Instruction::OR_INT_LIT16:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
- shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) |
- inst->VRegC_22s());
- inst = inst->Next_2xx();
- break;
- case Instruction::XOR_INT_LIT16:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22s(inst_data),
- shadow_frame.GetVReg(inst->VRegB_22s(inst_data)) ^
- inst->VRegC_22s());
- inst = inst->Next_2xx();
- break;
- case Instruction::ADD_INT_LIT8:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
- SafeAdd(shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b()));
- inst = inst->Next_2xx();
- break;
- case Instruction::RSUB_INT_LIT8:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
- SafeSub(inst->VRegC_22b(), shadow_frame.GetVReg(inst->VRegB_22b())));
- inst = inst->Next_2xx();
- break;
- case Instruction::MUL_INT_LIT8:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
- SafeMul(shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b()));
- inst = inst->Next_2xx();
- break;
- case Instruction::DIV_INT_LIT8: {
- PREAMBLE();
- bool success = DoIntDivide(shadow_frame, inst->VRegA_22b(inst_data),
- shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b());
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
- }
- case Instruction::REM_INT_LIT8: {
- PREAMBLE();
- bool success = DoIntRemainder(shadow_frame, inst->VRegA_22b(inst_data),
- shadow_frame.GetVReg(inst->VRegB_22b()), inst->VRegC_22b());
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_2xx);
- break;
+ continue;
}
- case Instruction::AND_INT_LIT8:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
- shadow_frame.GetVReg(inst->VRegB_22b()) &
- inst->VRegC_22b());
- inst = inst->Next_2xx();
- break;
- case Instruction::OR_INT_LIT8:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
- shadow_frame.GetVReg(inst->VRegB_22b()) |
- inst->VRegC_22b());
- inst = inst->Next_2xx();
- break;
- case Instruction::XOR_INT_LIT8:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
- shadow_frame.GetVReg(inst->VRegB_22b()) ^
- inst->VRegC_22b());
- inst = inst->Next_2xx();
- break;
- case Instruction::SHL_INT_LIT8:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
- shadow_frame.GetVReg(inst->VRegB_22b()) <<
- (inst->VRegC_22b() & 0x1f));
- inst = inst->Next_2xx();
- break;
- case Instruction::SHR_INT_LIT8:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
- shadow_frame.GetVReg(inst->VRegB_22b()) >>
- (inst->VRegC_22b() & 0x1f));
- inst = inst->Next_2xx();
- break;
- case Instruction::USHR_INT_LIT8:
- PREAMBLE();
- shadow_frame.SetVReg(inst->VRegA_22b(inst_data),
- static_cast<uint32_t>(shadow_frame.GetVReg(inst->VRegB_22b())) >>
- (inst->VRegC_22b() & 0x1f));
- inst = inst->Next_2xx();
- break;
- case Instruction::UNUSED_3E ... Instruction::UNUSED_43:
- case Instruction::UNUSED_79 ... Instruction::UNUSED_7A:
- case Instruction::UNUSED_F3 ... Instruction::UNUSED_F9:
- UnexpectedOpcode(inst, shadow_frame);
}
- } while (!interpret_one_instruction);
+ switch (inst->Opcode(inst_data)) {
+#define OPCODE_CASE(OPCODE, OPCODE_NAME, pname, f, i, a, e, v) \
+ case OPCODE: { \
+ bool exit_loop = false; \
+ InstructionHandler<do_access_check, transaction_active> handler( \
+ ctx, instrumentation, self, shadow_frame, dex_pc, inst, inst_data, exit_loop); \
+ handler.OPCODE_NAME(); \
+ /* TODO: Advance 'inst' here, instead of explicitly in each handler */ \
+ if (UNLIKELY(exit_loop)) { \
+ return; \
+ } \
+ break; \
+ }
+DEX_INSTRUCTION_LIST(OPCODE_CASE)
+#undef OPCODE_CASE
+ }
+ if (UNLIKELY(interpret_one_instruction)) {
+ break;
+ }
+ }
// Record where we stopped.
shadow_frame.SetDexPC(inst->GetDexPc(insns));
- ctx->result = result_register;
+ ctx->result = ctx->result_register;
return;
} // NOLINT(readability/fn_size)
diff --git a/runtime/interpreter/mterp/arm/other.S b/runtime/interpreter/mterp/arm/other.S
index 340038c..fcdde1e 100644
--- a/runtime/interpreter/mterp/arm/other.S
+++ b/runtime/interpreter/mterp/arm/other.S
@@ -159,6 +159,9 @@
cmp r0, #0
bne MterpException
FETCH_ADVANCE_INST 1
+ ldr r0, [rSELF, #THREAD_USE_MTERP_OFFSET]
+ cmp r0, #0
+ beq MterpFallback
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
@@ -179,6 +182,9 @@
cmp r0, #0 @ failed?
bne MterpException
FETCH_ADVANCE_INST 1 @ before throw: advance rPC, load rINST
+ ldr r0, [rSELF, #THREAD_USE_MTERP_OFFSET]
+ cmp r0, #0
+ beq MterpFallback
GET_INST_OPCODE ip @ extract opcode from rINST
GOTO_OPCODE ip @ jump to next instruction
diff --git a/runtime/interpreter/mterp/arm64/other.S b/runtime/interpreter/mterp/arm64/other.S
index 024a5c8..f1d0ef3 100644
--- a/runtime/interpreter/mterp/arm64/other.S
+++ b/runtime/interpreter/mterp/arm64/other.S
@@ -146,6 +146,8 @@
bl artLockObjectFromCode
cbnz w0, MterpException
FETCH_ADVANCE_INST 1
+ ldr w0, [xSELF, #THREAD_USE_MTERP_OFFSET]
+ cbz w0, MterpFallback
GET_INST_OPCODE ip // extract opcode from rINST
GOTO_OPCODE ip // jump to next instruction
@@ -165,6 +167,8 @@
bl artUnlockObjectFromCode // w0<- success for unlock(self, obj)
cbnz w0, MterpException
FETCH_ADVANCE_INST 1 // before throw: advance rPC, load rINST
+ ldr w0, [xSELF, #THREAD_USE_MTERP_OFFSET]
+ cbz w0, MterpFallback
GET_INST_OPCODE ip // extract opcode from rINST
GOTO_OPCODE ip // jump to next instruction
diff --git a/runtime/interpreter/mterp/x86/invoke.S b/runtime/interpreter/mterp/x86/invoke.S
index cfb9c7c..06cd904 100644
--- a/runtime/interpreter/mterp/x86/invoke.S
+++ b/runtime/interpreter/mterp/x86/invoke.S
@@ -18,8 +18,7 @@
jz MterpException
ADVANCE_PC 3
movl rSELF, %eax
- movb THREAD_USE_MTERP_OFFSET(%eax), %al
- testb %al, %al
+ cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%eax)
jz MterpFallback
RESTORE_IBASE
FETCH_INST
@@ -45,8 +44,7 @@
jz MterpException
ADVANCE_PC 4
movl rSELF, %eax
- movb THREAD_USE_MTERP_OFFSET(%eax), %al
- testb %al, %al
+ cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%eax)
jz MterpFallback
RESTORE_IBASE
FETCH_INST
diff --git a/runtime/interpreter/mterp/x86/main.S b/runtime/interpreter/mterp/x86/main.S
index b233f2c..0621fb4 100644
--- a/runtime/interpreter/mterp/x86/main.S
+++ b/runtime/interpreter/mterp/x86/main.S
@@ -91,6 +91,8 @@
#include "asm_support.h"
#include "interpreter/cfi_asm_support.h"
+#define LITERAL(value) $$(value)
+
/*
* Handle mac compiler specific
*/
@@ -561,8 +563,7 @@
movl rPC, OFF_FP_DEX_PC_PTR(rFP)
/* Do we need to switch interpreters? */
movl rSELF, %eax
- movb THREAD_USE_MTERP_OFFSET(%eax), %al
- testb %al, %al
+ cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%eax)
jz MterpFallback
/* resume execution at catch block */
REFRESH_IBASE
diff --git a/runtime/interpreter/mterp/x86/other.S b/runtime/interpreter/mterp/x86/other.S
index 5de3381..270ccb6 100644
--- a/runtime/interpreter/mterp/x86/other.S
+++ b/runtime/interpreter/mterp/x86/other.S
@@ -132,7 +132,12 @@
RESTORE_IBASE
testb %al, %al
jnz MterpException
- ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
+ ADVANCE_PC 1
+ movl rSELF, %eax
+ cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%eax)
+ jz MterpFallback
+ FETCH_INST
+ GOTO_NEXT
%def op_monitor_exit():
/*
@@ -152,7 +157,12 @@
RESTORE_IBASE
testb %al, %al
jnz MterpException
- ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
+ ADVANCE_PC 1
+ movl rSELF, %eax
+ cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%eax)
+ jz MterpFallback
+ FETCH_INST
+ GOTO_NEXT
%def op_move(is_object="0"):
/* for move, move-object, long-to-int */
diff --git a/runtime/interpreter/mterp/x86_64/invoke.S b/runtime/interpreter/mterp/x86_64/invoke.S
index f727915..15b48c9 100644
--- a/runtime/interpreter/mterp/x86_64/invoke.S
+++ b/runtime/interpreter/mterp/x86_64/invoke.S
@@ -16,8 +16,7 @@
jz MterpException
ADVANCE_PC 3
movq rSELF, %rax
- movb THREAD_USE_MTERP_OFFSET(%rax), %al
- testb %al, %al
+ cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%rax)
jz MterpFallback
FETCH_INST
GOTO_NEXT
@@ -40,8 +39,7 @@
jz MterpException
ADVANCE_PC 4
movq rSELF, %rax
- movb THREAD_USE_MTERP_OFFSET(%rax), %al
- testb %al, %al
+ cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%rax)
jz MterpFallback
FETCH_INST
GOTO_NEXT
diff --git a/runtime/interpreter/mterp/x86_64/main.S b/runtime/interpreter/mterp/x86_64/main.S
index 75eb00c..4609067 100644
--- a/runtime/interpreter/mterp/x86_64/main.S
+++ b/runtime/interpreter/mterp/x86_64/main.S
@@ -87,6 +87,8 @@
#include "asm_support.h"
#include "interpreter/cfi_asm_support.h"
+#define LITERAL(value) $$(value)
+
/*
* Handle mac compiler specific
*/
@@ -527,8 +529,7 @@
movq rPC, OFF_FP_DEX_PC_PTR(rFP)
/* Do we need to switch interpreters? */
movq rSELF, %rax
- movb THREAD_USE_MTERP_OFFSET(%rax), %al
- testb %al, %al
+ cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%rax)
jz MterpFallback
/* resume execution at catch block */
REFRESH_IBASE
diff --git a/runtime/interpreter/mterp/x86_64/other.S b/runtime/interpreter/mterp/x86_64/other.S
index 849155c..412389f 100644
--- a/runtime/interpreter/mterp/x86_64/other.S
+++ b/runtime/interpreter/mterp/x86_64/other.S
@@ -108,7 +108,12 @@
call SYMBOL(artLockObjectFromCode) # (object, self)
testq %rax, %rax
jnz MterpException
- ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
+ ADVANCE_PC 1
+ movq rSELF, %rax
+ cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%rax)
+ jz MterpFallback
+ FETCH_INST
+ GOTO_NEXT
%def op_monitor_exit():
/*
@@ -125,7 +130,12 @@
call SYMBOL(artUnlockObjectFromCode) # (object, self)
testq %rax, %rax
jnz MterpException
- ADVANCE_PC_FETCH_AND_GOTO_NEXT 1
+ ADVANCE_PC 1
+ movq rSELF, %rax
+ cmpb LITERAL(0), THREAD_USE_MTERP_OFFSET(%rax)
+ jz MterpFallback
+ FETCH_INST
+ GOTO_NEXT
%def op_move(is_object="0"):
/* for move, move-object, long-to-int */
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 9bc2179..e292a76 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -182,15 +182,16 @@
}
template<typename T>
-static ALWAYS_INLINE bool ShouldBlockAccessToMember(T* member, ShadowFrame* frame)
+static ALWAYS_INLINE bool ShouldDenyAccessToMember(T* member, ShadowFrame* frame)
REQUIRES_SHARED(Locks::mutator_lock_) {
// All uses in this file are from reflection
- constexpr hiddenapi::AccessMethod access_method = hiddenapi::kReflection;
- return hiddenapi::GetMemberAction(
+ constexpr hiddenapi::AccessMethod access_method = hiddenapi::AccessMethod::kReflection;
+ return hiddenapi::ShouldDenyAccessToMember(
member,
- frame->GetMethod()->GetDeclaringClass()->GetClassLoader(),
- frame->GetMethod()->GetDeclaringClass()->GetDexCache(),
- access_method) == hiddenapi::kDeny;
+ [&]() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return hiddenapi::AccessContext(frame->GetMethod()->GetDeclaringClass());
+ },
+ access_method);
}
void UnstartedRuntime::UnstartedClassForNameCommon(Thread* self,
@@ -297,7 +298,7 @@
auto* cl = Runtime::Current()->GetClassLinker();
if (cl->EnsureInitialized(self, h_klass, true, true)) {
ArtMethod* cons = h_klass->FindConstructor("()V", cl->GetImagePointerSize());
- if (cons != nullptr && ShouldBlockAccessToMember(cons, shadow_frame)) {
+ if (cons != nullptr && ShouldDenyAccessToMember(cons, shadow_frame)) {
cons = nullptr;
}
if (cons != nullptr) {
@@ -342,7 +343,7 @@
}
}
}
- if (found != nullptr && ShouldBlockAccessToMember(found, shadow_frame)) {
+ if (found != nullptr && ShouldDenyAccessToMember(found, shadow_frame)) {
found = nullptr;
}
if (found == nullptr) {
@@ -407,7 +408,7 @@
self, klass, name, args);
}
}
- if (method != nullptr && ShouldBlockAccessToMember(method->GetArtMethod(), shadow_frame)) {
+ if (method != nullptr && ShouldDenyAccessToMember(method->GetArtMethod(), shadow_frame)) {
method = nullptr;
}
result->SetL(method);
@@ -445,7 +446,7 @@
}
}
if (constructor != nullptr &&
- ShouldBlockAccessToMember(constructor->GetArtMethod(), shadow_frame)) {
+ ShouldDenyAccessToMember(constructor->GetArtMethod(), shadow_frame)) {
constructor = nullptr;
}
result->SetL(constructor);
diff --git a/runtime/intrinsics_list.h b/runtime/intrinsics_list.h
index 2f91f5d..093dd7f 100644
--- a/runtime/intrinsics_list.h
+++ b/runtime/intrinsics_list.h
@@ -219,6 +219,7 @@
V(VarHandleLoadLoadFence, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kNoThrow, "Ljava/lang/invoke/VarHandle;", "loadLoadFence", "()V") \
V(VarHandleStoreStoreFence, kStatic, kNeedsEnvironmentOrCache, kReadSideEffects, kNoThrow, "Ljava/lang/invoke/VarHandle;", "storeStoreFence", "()V") \
V(ReachabilityFence, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kNoThrow, "Ljava/lang/ref/Reference;", "reachabilityFence", "(Ljava/lang/Object;)V") \
+ V(CRC32Update, kStatic, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow, "Ljava/util/zip/CRC32;", "update", "(II)I") \
SIGNATURE_POLYMORPHIC_INTRINSICS_LIST(V)
#endif // ART_RUNTIME_INTRINSICS_LIST_H_
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index e876a1b..d67d9dc 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -60,7 +60,6 @@
void (*Jit::jit_unload_)(void*) = nullptr;
bool (*Jit::jit_compile_method_)(void*, ArtMethod*, Thread*, bool) = nullptr;
void (*Jit::jit_types_loaded_)(void*, mirror::Class**, size_t count) = nullptr;
-bool Jit::generate_debug_info_ = false;
struct StressModeHelper {
DECLARE_RUNTIME_DEBUG_FLAG(kSlowMode);
@@ -176,11 +175,24 @@
lock_("JIT memory use lock") {}
Jit* Jit::Create(JitCodeCache* code_cache, JitOptions* options) {
- CHECK(jit_compiler_handle_ != nullptr) << "Jit::LoadLibrary() needs to be called first";
- std::unique_ptr<Jit> jit(new Jit(code_cache, options));
- if (jit_compiler_handle_ == nullptr) {
+ if (jit_load_ == nullptr) {
+ LOG(WARNING) << "Not creating JIT: library not loaded";
return nullptr;
}
+ bool will_generate_debug_symbols = false;
+ jit_compiler_handle_ = (jit_load_)(&will_generate_debug_symbols);
+ if (jit_compiler_handle_ == nullptr) {
+ LOG(WARNING) << "Not creating JIT: failed to allocate a compiler";
+ return nullptr;
+ }
+ std::unique_ptr<Jit> jit(new Jit(code_cache, options));
+ jit->generate_debug_info_ = will_generate_debug_symbols;
+
+ // With 'perf', we want a 1-1 mapping between an address and a method.
+ // We aren't able to keep method pointers live during the instrumentation method entry trampoline
+ // so we will just disable jit-gc if we are doing that.
+ code_cache->SetGarbageCollectCode(!jit->generate_debug_info_ &&
+ !Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled());
VLOG(jit) << "JIT created with initial_capacity="
<< PrettySize(options->GetCodeCacheInitialCapacity())
@@ -195,7 +207,7 @@
return jit.release();
}
-bool Jit::BindCompilerMethods(std::string* error_msg) {
+bool Jit::LoadCompilerLibrary(std::string* error_msg) {
jit_library_handle_ = dlopen(
kIsDebugBuild ? "libartd-compiler.so" : "libart-compiler.so", RTLD_NOW);
if (jit_library_handle_ == nullptr) {
@@ -234,23 +246,6 @@
return true;
}
-bool Jit::LoadCompiler(std::string* error_msg) {
- if (jit_library_handle_ == nullptr && !BindCompilerMethods(error_msg)) {
- return false;
- }
- bool will_generate_debug_symbols = false;
- VLOG(jit) << "Calling JitLoad interpreter_only="
- << Runtime::Current()->GetInstrumentation()->InterpretOnly();
- jit_compiler_handle_ = (jit_load_)(&will_generate_debug_symbols);
- if (jit_compiler_handle_ == nullptr) {
- dlclose(jit_library_handle_);
- *error_msg = "JIT couldn't load compiler";
- return false;
- }
- generate_debug_info_ = will_generate_debug_symbols;
- return true;
-}
-
bool Jit::CompileMethod(ArtMethod* method, Thread* self, bool osr) {
DCHECK(Runtime::Current()->UseJitCompilation());
DCHECK(!method->IsRuntimeMethod());
@@ -300,11 +295,6 @@
return success;
}
-bool Jit::ShouldGenerateDebugInfo() {
- CHECK(CompilerIsLoaded());
- return generate_debug_info_;
-}
-
void Jit::CreateThreadPool() {
// There is a DCHECK in the 'AddSamples' method to ensure the tread pool
// is not null when we instrument.
@@ -385,7 +375,7 @@
return;
}
jit::Jit* jit = Runtime::Current()->GetJit();
- if (generate_debug_info_) {
+ if (jit->generate_debug_info_) {
DCHECK(jit->jit_types_loaded_ != nullptr);
jit->jit_types_loaded_(jit->jit_compiler_handle_, &type, 1);
}
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index b0ea19b..46b0762 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -282,13 +282,8 @@
JValue* result)
REQUIRES_SHARED(Locks::mutator_lock_);
- // Load and initialize compiler.
- static bool LoadCompiler(std::string* error_msg);
-
- static bool CompilerIsLoaded() { return jit_compiler_handle_ != nullptr; }
-
- // Return whether debug info should be generated. Requires LoadCompiler() to have been called.
- static bool ShouldGenerateDebugInfo();
+ // Load the compiler library.
+ static bool LoadCompilerLibrary(std::string* error_msg);
ThreadPool* GetThreadPool() const {
return thread_pool_.get();
@@ -313,8 +308,8 @@
static bool (*jit_compile_method_)(void*, ArtMethod*, Thread*, bool);
static void (*jit_types_loaded_)(void*, mirror::Class**, size_t count);
- // We make this static to simplify the interaction with libart-compiler.so.
- static bool generate_debug_info_;
+ // Whether we should generate debug info when compiling.
+ bool generate_debug_info_;
// JIT resources owned by runtime.
jit::JitCodeCache* const code_cache_;
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index a15a9be..359f97e 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -436,12 +436,6 @@
SetFootprintLimit(current_capacity_);
}
- // With 'perf', we want a 1-1 mapping between an address and a method.
- // We aren't able to keep method pointers live during the instrumentation method entry trampoline
- // so we will just disable jit-gc if we are doing that.
- garbage_collect_code_ = !Jit::ShouldGenerateDebugInfo() &&
- !Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled();
-
VLOG(jit) << "Created jit code cache: initial data size="
<< PrettySize(initial_data_capacity)
<< ", initial code size="
diff --git a/runtime/jni/jni_internal.cc b/runtime/jni/jni_internal.cc
index 5e01b79..af86cc0 100644
--- a/runtime/jni/jni_internal.cc
+++ b/runtime/jni/jni_internal.cc
@@ -84,20 +84,20 @@
// things not rendering correctly. E.g. b/16858794
static constexpr bool kWarnJniAbort = false;
-static bool IsCallerTrusted(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
- return hiddenapi::IsCallerTrusted(GetCallingClass(self, /* num_frames= */ 1));
-}
-
template<typename T>
-ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)
+ALWAYS_INLINE static bool ShouldDenyAccessToMember(T* member, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
- hiddenapi::Action action = hiddenapi::GetMemberAction(
- member, self, IsCallerTrusted, hiddenapi::kJNI);
- if (action != hiddenapi::kAllow) {
- hiddenapi::NotifyHiddenApiListener(member);
- }
-
- return action == hiddenapi::kDeny;
+ return hiddenapi::ShouldDenyAccessToMember(
+ member,
+ [&]() REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Construct AccessContext from the first calling class on stack.
+ // If the calling class cannot be determined, e.g. unattached threads,
+ // we conservatively assume the caller is trusted.
+ ObjPtr<mirror::Class> caller = GetCallingClass(self, /* num_frames */ 1);
+ return caller.IsNull() ? hiddenapi::AccessContext(/* is_trusted= */ true)
+ : hiddenapi::AccessContext(caller);
+ },
+ hiddenapi::AccessMethod::kJNI);
}
// Helpers to call instrumentation functions for fields. These take jobjects so we don't need to set
@@ -259,7 +259,7 @@
} else {
method = c->FindClassMethod(name, sig, pointer_size);
}
- if (method != nullptr && ShouldBlockAccessToMember(method, soa.Self())) {
+ if (method != nullptr && ShouldDenyAccessToMember(method, soa.Self())) {
method = nullptr;
}
if (method == nullptr || method->IsStatic() != is_static) {
@@ -338,7 +338,7 @@
} else {
field = c->FindInstanceField(name, field_type->GetDescriptor(&temp));
}
- if (field != nullptr && ShouldBlockAccessToMember(field, soa.Self())) {
+ if (field != nullptr && ShouldDenyAccessToMember(field, soa.Self())) {
field = nullptr;
}
if (field == nullptr) {
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index 4427332..a6a5ba2 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -224,16 +224,14 @@
}
}
-template<typename T, VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
+template<typename T, VerifyObjectFlags kVerifyFlags>
inline T PointerArray::GetElementPtrSize(uint32_t idx, PointerSize ptr_size) {
// C style casts here since we sometimes have T be a pointer, or sometimes an integer
// (for stack traces).
if (ptr_size == PointerSize::k64) {
- return (T)static_cast<uintptr_t>(
- AsLongArray<kVerifyFlags, kReadBarrierOption>()->GetWithoutChecks(idx));
+ return (T)static_cast<uintptr_t>(AsLongArray<kVerifyFlags>()->GetWithoutChecks(idx));
}
- return (T)static_cast<uintptr_t>(static_cast<uint32_t>(
- AsIntArray<kVerifyFlags, kReadBarrierOption>()->GetWithoutChecks(idx)));
+ return (T)static_cast<uintptr_t>(AsIntArray<kVerifyFlags>()->GetWithoutChecks(idx));
}
template<bool kTransactionActive, bool kUnchecked>
@@ -255,12 +253,12 @@
ptr_size);
}
-template <VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption, typename Visitor>
+template <VerifyObjectFlags kVerifyFlags, typename Visitor>
inline void PointerArray::Fixup(mirror::PointerArray* dest,
PointerSize pointer_size,
const Visitor& visitor) {
for (size_t i = 0, count = GetLength(); i < count; ++i) {
- void* ptr = GetElementPtrSize<void*, kVerifyFlags, kReadBarrierOption>(i, pointer_size);
+ void* ptr = GetElementPtrSize<void*, kVerifyFlags>(i, pointer_size);
void* new_ptr = visitor(ptr);
if (ptr != new_ptr) {
dest->SetElementPtrSize<false, true>(i, new_ptr, pointer_size);
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 7211f30..8816c61 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -193,9 +193,7 @@
// Either an IntArray or a LongArray.
class PointerArray : public Array {
public:
- template<typename T,
- VerifyObjectFlags kVerifyFlags = kVerifyNone,
- ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+ template<typename T, VerifyObjectFlags kVerifyFlags = kVerifyNone>
T GetElementPtrSize(uint32_t idx, PointerSize ptr_size)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -216,9 +214,7 @@
// Fixup the pointers in the dest arrays by passing our pointers through the visitor. Only copies
// to dest if visitor(source_ptr) != source_ptr.
- template <VerifyObjectFlags kVerifyFlags = kVerifyNone,
- ReadBarrierOption kReadBarrierOption = kWithReadBarrier,
- typename Visitor>
+ template <VerifyObjectFlags kVerifyFlags = kVerifyNone, typename Visitor>
void Fixup(mirror::PointerArray* dest, PointerSize pointer_size, const Visitor& visitor)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 9a4130d..185ae3b 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -294,16 +294,20 @@
}
inline bool Class::HasVTable() {
- return GetVTable() != nullptr || ShouldHaveEmbeddedVTable();
+ // No read barrier is needed for comparing with null.
+ return GetVTable<kDefaultVerifyFlags, kWithoutReadBarrier>() != nullptr ||
+ ShouldHaveEmbeddedVTable();
}
-template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
+template<VerifyObjectFlags kVerifyFlags>
inline int32_t Class::GetVTableLength() {
if (ShouldHaveEmbeddedVTable<kVerifyFlags>()) {
return GetEmbeddedVTableLength();
}
- return GetVTable<kVerifyFlags, kReadBarrierOption>() != nullptr ?
- GetVTable<kVerifyFlags, kReadBarrierOption>()->GetLength() : 0;
+ // We do not need a read barrier here as the length is constant,
+ // both from-space and to-space vtables shall yield the same result.
+ ObjPtr<PointerArray> vtable = GetVTable<kVerifyFlags, kWithoutReadBarrier>();
+ return vtable != nullptr ? vtable->GetLength() : 0;
}
template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
@@ -311,10 +315,9 @@
if (ShouldHaveEmbeddedVTable<kVerifyFlags>()) {
return GetEmbeddedVTableEntry(i, pointer_size);
}
- auto* vtable = GetVTable<kVerifyFlags, kReadBarrierOption>();
+ ObjPtr<PointerArray> vtable = GetVTable<kVerifyFlags, kReadBarrierOption>();
DCHECK(vtable != nullptr);
- return vtable->template GetElementPtrSize<ArtMethod*, kVerifyFlags, kReadBarrierOption>(
- i, pointer_size);
+ return vtable->GetElementPtrSize<ArtMethod*, kVerifyFlags>(i, pointer_size);
}
template<VerifyObjectFlags kVerifyFlags>
@@ -624,9 +627,11 @@
return ret.Ptr();
}
-template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
+template<VerifyObjectFlags kVerifyFlags>
inline int32_t Class::GetIfTableCount() {
- return GetIfTable<kVerifyFlags, kReadBarrierOption>()->Count();
+ // We do not need a read barrier here as the length is constant,
+ // both from-space and to-space iftables shall yield the same result.
+ return GetIfTable<kVerifyFlags, kWithoutReadBarrier>()->Count();
}
inline void Class::SetIfTable(ObjPtr<IfTable> new_iftable) {
@@ -970,7 +975,17 @@
// We do not need a read barrier here as the primitive type is constant,
// both from-space and to-space component type classes shall yield the same result.
ObjPtr<Class> const component_type = GetComponentType<kVerifyFlags, kWithoutReadBarrier>();
- return component_type != nullptr && !component_type->IsPrimitive<kVerifyFlags>();
+ constexpr VerifyObjectFlags kNewFlags = RemoveThisFlags(kVerifyFlags);
+ return component_type != nullptr && !component_type->IsPrimitive<kNewFlags>();
+}
+
+template<VerifyObjectFlags kVerifyFlags>
+bool Class::IsPrimitiveArray() {
+ // We do not need a read barrier here as the primitive type is constant,
+ // both from-space and to-space component type classes shall yield the same result.
+ ObjPtr<Class> const component_type = GetComponentType<kVerifyFlags, kWithoutReadBarrier>();
+ constexpr VerifyObjectFlags kNewFlags = RemoveThisFlags(kVerifyFlags);
+ return component_type != nullptr && component_type->IsPrimitive<kNewFlags>();
}
inline bool Class::IsAssignableFrom(ObjPtr<Class> src) {
@@ -1087,6 +1102,16 @@
return component->IsPrimitive() || component->CannotBeAssignedFromOtherTypes();
}
+template <bool kCheckTransaction>
+inline void Class::SetClassLoader(ObjPtr<ClassLoader> new_class_loader) {
+ if (kCheckTransaction && Runtime::Current()->IsActiveTransaction()) {
+ SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader);
+ } else {
+ DCHECK(!Runtime::Current()->IsActiveTransaction());
+ SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader);
+ }
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index e33e407..83d76a9 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -427,14 +427,6 @@
return GetClassRoot<mirror::Throwable>()->IsAssignableFrom(this);
}
-void Class::SetClassLoader(ObjPtr<ClassLoader> new_class_loader) {
- if (Runtime::Current()->IsActiveTransaction()) {
- SetFieldObject<true>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader);
- } else {
- SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, class_loader_), new_class_loader);
- }
-}
-
template <typename SignatureType>
static inline ArtMethod* FindInterfaceMethodWithSignature(ObjPtr<Class> klass,
const StringPiece& name,
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 74fca54..c38cc86 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -422,13 +422,6 @@
return GetPrimitiveType<kVerifyFlags>() == Primitive::kPrimVoid;
}
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- bool IsPrimitiveArray() REQUIRES_SHARED(Locks::mutator_lock_) {
- return IsArrayClass<kVerifyFlags>() &&
- GetComponentType<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>()->
- IsPrimitive();
- }
-
// Depth of class from java.lang.Object
uint32_t Depth() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -466,7 +459,8 @@
}
bool IsObjectClass() REQUIRES_SHARED(Locks::mutator_lock_) {
- return !IsPrimitive() && GetSuperClass() == nullptr;
+ // No read barrier is needed for comparing with null.
+ return !IsPrimitive() && GetSuperClass<kDefaultVerifyFlags, kWithoutReadBarrier>() == nullptr;
}
bool IsInstantiableNonArray() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -485,18 +479,7 @@
ALWAYS_INLINE bool IsObjectArrayClass() REQUIRES_SHARED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- bool IsIntArrayClass() REQUIRES_SHARED(Locks::mutator_lock_) {
- constexpr auto kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
- auto* component_type = GetComponentType<kVerifyFlags>();
- return component_type != nullptr && component_type->template IsPrimitiveInt<kNewFlags>();
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- bool IsLongArrayClass() REQUIRES_SHARED(Locks::mutator_lock_) {
- constexpr auto kNewFlags = static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis);
- auto* component_type = GetComponentType<kVerifyFlags>();
- return component_type != nullptr && component_type->template IsPrimitiveLong<kNewFlags>();
- }
+ bool IsPrimitiveArray() REQUIRES_SHARED(Locks::mutator_lock_);
// Creates a raw object instance but does not invoke the default constructor.
template<bool kIsInstrumented, bool kCheckAddFinalizer = true>
@@ -633,7 +616,8 @@
void SetSuperClass(ObjPtr<Class> new_super_class) REQUIRES_SHARED(Locks::mutator_lock_);
bool HasSuperClass() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetSuperClass() != nullptr;
+ // No read barrier is needed for comparing with null.
+ return GetSuperClass<kDefaultVerifyFlags, kWithoutReadBarrier>() != nullptr;
}
static constexpr MemberOffset SuperClassOffset() {
@@ -644,6 +628,7 @@
ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
ClassLoader* GetClassLoader() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_);
+ template <bool kCheckTransaction = true>
void SetClassLoader(ObjPtr<ClassLoader> new_cl) REQUIRES_SHARED(Locks::mutator_lock_);
static constexpr MemberOffset DexCacheOffset() {
@@ -812,8 +797,7 @@
static MemberOffset EmbeddedVTableEntryOffset(uint32_t i, PointerSize pointer_size);
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
- ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
int32_t GetVTableLength() REQUIRES_SHARED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
@@ -948,8 +932,7 @@
return (GetAccessFlags() & kAccRecursivelyInitialized) != 0;
}
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
- ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
ALWAYS_INLINE int32_t GetIfTableCount() REQUIRES_SHARED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
@@ -1216,7 +1199,8 @@
// Returns true if the class loader is null, ie the class loader is the boot strap class loader.
bool IsBootStrapClassLoaded() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetClassLoader() == nullptr;
+ // No read barrier is needed for comparing with null.
+ return GetClassLoader<kDefaultVerifyFlags, kWithoutReadBarrier>() == nullptr;
}
static size_t ImTableEntrySize(PointerSize pointer_size) {
diff --git a/runtime/mirror/class_ext-inl.h b/runtime/mirror/class_ext-inl.h
index feaac85..8d68dc9 100644
--- a/runtime/mirror/class_ext-inl.h
+++ b/runtime/mirror/class_ext-inl.h
@@ -32,9 +32,7 @@
}
int32_t len = arr->GetLength();
for (int32_t i = 0; i < len; i++) {
- ArtMethod* method = arr->GetElementPtrSize<ArtMethod*,
- kDefaultVerifyFlags,
- kReadBarrierOption>(i, pointer_size);
+ ArtMethod* method = arr->GetElementPtrSize<ArtMethod*, kDefaultVerifyFlags>(i, pointer_size);
if (method != nullptr) {
method->VisitRoots<kReadBarrierOption>(visitor, pointer_size);
}
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 8ae79a8..2c2ad9b 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -156,7 +156,7 @@
inline bool Object::IsObjectArray() {
// We do not need a read barrier here as the primitive type is constant,
// both from-space and to-space component type classes shall yield the same result.
- constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
+ constexpr VerifyObjectFlags kNewFlags = RemoveThisFlags(kVerifyFlags);
return IsArrayInstance<kVerifyFlags>() &&
!GetClass<kNewFlags, kWithoutReadBarrier>()->
template GetComponentType<kNewFlags, kWithoutReadBarrier>()->IsPrimitive();
@@ -192,113 +192,102 @@
return down_cast<Array*>(this);
}
+template<VerifyObjectFlags kVerifyFlags, Primitive::Type kType>
+ALWAYS_INLINE bool Object::IsSpecificPrimitiveArray() {
+ // We do not need a read barrier here as the primitive type is constant,
+ // both from-space and to-space component type classes shall yield the same result.
+ ObjPtr<Class> klass = GetClass<kVerifyFlags, kWithoutReadBarrier>();
+ constexpr VerifyObjectFlags kNewFlags = RemoveThisFlags(kVerifyFlags);
+ ObjPtr<Class> const component_type = klass->GetComponentType<kNewFlags, kWithoutReadBarrier>();
+ return component_type != nullptr &&
+ component_type->GetPrimitiveType<kNewFlags>() == kType;
+}
+
+template<VerifyObjectFlags kVerifyFlags>
+inline bool Object::IsBooleanArray() {
+ return IsSpecificPrimitiveArray<kVerifyFlags, Primitive::kPrimBoolean>();
+}
+
template<VerifyObjectFlags kVerifyFlags>
inline BooleanArray* Object::AsBooleanArray() {
- constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
- DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
- DCHECK(GetClass<kNewFlags>()->GetComponentType()->IsPrimitiveBoolean());
+ DCHECK(IsBooleanArray<kVerifyFlags>());
return down_cast<BooleanArray*>(this);
}
template<VerifyObjectFlags kVerifyFlags>
+inline bool Object::IsByteArray() {
+ return IsSpecificPrimitiveArray<kVerifyFlags, Primitive::kPrimByte>();
+}
+
+template<VerifyObjectFlags kVerifyFlags>
inline ByteArray* Object::AsByteArray() {
- constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
- DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
- DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveByte());
+ DCHECK(IsByteArray<kVerifyFlags>());
return down_cast<ByteArray*>(this);
}
template<VerifyObjectFlags kVerifyFlags>
-inline ByteArray* Object::AsByteSizedArray() {
- constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
- DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
- DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveByte() ||
- GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveBoolean());
- return down_cast<ByteArray*>(this);
+inline bool Object::IsCharArray() {
+ return IsSpecificPrimitiveArray<kVerifyFlags, Primitive::kPrimChar>();
}
template<VerifyObjectFlags kVerifyFlags>
inline CharArray* Object::AsCharArray() {
- constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
- DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
- DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveChar());
+ DCHECK(IsCharArray<kVerifyFlags>());
return down_cast<CharArray*>(this);
}
template<VerifyObjectFlags kVerifyFlags>
+inline bool Object::IsShortArray() {
+ return IsSpecificPrimitiveArray<kVerifyFlags, Primitive::kPrimShort>();
+}
+
+template<VerifyObjectFlags kVerifyFlags>
inline ShortArray* Object::AsShortArray() {
- constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
- DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
- DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveShort());
+ DCHECK(IsShortArray<kVerifyFlags>());
return down_cast<ShortArray*>(this);
}
template<VerifyObjectFlags kVerifyFlags>
-inline ShortArray* Object::AsShortSizedArray() {
- constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
- DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
- DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveShort() ||
- GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveChar());
- return down_cast<ShortArray*>(this);
-}
-
-template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
inline bool Object::IsIntArray() {
- constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
- ObjPtr<Class> klass = GetClass<kVerifyFlags, kReadBarrierOption>();
- ObjPtr<Class> component_type = klass->GetComponentType<kVerifyFlags, kReadBarrierOption>();
- return component_type != nullptr && component_type->template IsPrimitiveInt<kNewFlags>();
+ return IsSpecificPrimitiveArray<kVerifyFlags, Primitive::kPrimInt>();
}
-template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
+template<VerifyObjectFlags kVerifyFlags>
inline IntArray* Object::AsIntArray() {
- DCHECK((IsIntArray<kVerifyFlags, kReadBarrierOption>()));
+ DCHECK((IsIntArray<kVerifyFlags>()));
return down_cast<IntArray*>(this);
}
-template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
+template<VerifyObjectFlags kVerifyFlags>
inline bool Object::IsLongArray() {
- constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
- ObjPtr<Class> klass = GetClass<kVerifyFlags, kReadBarrierOption>();
- ObjPtr<Class> component_type = klass->GetComponentType<kVerifyFlags, kReadBarrierOption>();
- return component_type != nullptr && component_type->template IsPrimitiveLong<kNewFlags>();
+ return IsSpecificPrimitiveArray<kVerifyFlags, Primitive::kPrimLong>();
}
-template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
+template<VerifyObjectFlags kVerifyFlags>
inline LongArray* Object::AsLongArray() {
- DCHECK((IsLongArray<kVerifyFlags, kReadBarrierOption>()));
+ DCHECK((IsLongArray<kVerifyFlags>()));
return down_cast<LongArray*>(this);
}
template<VerifyObjectFlags kVerifyFlags>
inline bool Object::IsFloatArray() {
- constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
- auto* component_type = GetClass<kVerifyFlags>()->GetComponentType();
- return component_type != nullptr && component_type->template IsPrimitiveFloat<kNewFlags>();
+ return IsSpecificPrimitiveArray<kVerifyFlags, Primitive::kPrimFloat>();
}
template<VerifyObjectFlags kVerifyFlags>
inline FloatArray* Object::AsFloatArray() {
DCHECK(IsFloatArray<kVerifyFlags>());
- constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
- DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
- DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveFloat());
return down_cast<FloatArray*>(this);
}
template<VerifyObjectFlags kVerifyFlags>
inline bool Object::IsDoubleArray() {
- constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
- auto* component_type = GetClass<kVerifyFlags>()->GetComponentType();
- return component_type != nullptr && component_type->template IsPrimitiveDouble<kNewFlags>();
+ return IsSpecificPrimitiveArray<kVerifyFlags, Primitive::kPrimDouble>();
}
template<VerifyObjectFlags kVerifyFlags>
inline DoubleArray* Object::AsDoubleArray() {
DCHECK(IsDoubleArray<kVerifyFlags>());
- constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
- DCHECK(GetClass<kVerifyFlags>()->IsArrayClass());
- DCHECK(GetClass<kNewFlags>()->template GetComponentType<kNewFlags>()->IsPrimitiveDouble());
return down_cast<DoubleArray*>(this);
}
@@ -351,7 +340,7 @@
// values is OK because of that.
static constexpr ReadBarrierOption kRBO = kWithoutReadBarrier;
size_t result;
- constexpr auto kNewFlags = RemoveThisFlags(kVerifyFlags);
+ constexpr VerifyObjectFlags kNewFlags = RemoveThisFlags(kVerifyFlags);
if (IsArrayInstance<kVerifyFlags>()) {
result = AsArray<kNewFlags>()->template SizeOf<kNewFlags, kRBO>();
} else if (IsClass<kNewFlags>()) {
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index 11e8cca..bca7511 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -21,6 +21,7 @@
#include "base/casts.h"
#include "base/enums.h"
#include "base/globals.h"
+#include "dex/primitive.h"
#include "obj_ptr.h"
#include "object_reference.h"
#include "offsets.h"
@@ -199,31 +200,33 @@
Array* AsArray() REQUIRES_SHARED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ bool IsBooleanArray() REQUIRES_SHARED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
BooleanArray* AsBooleanArray() REQUIRES_SHARED(Locks::mutator_lock_);
+
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ bool IsByteArray() REQUIRES_SHARED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
ByteArray* AsByteArray() REQUIRES_SHARED(Locks::mutator_lock_);
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- ByteArray* AsByteSizedArray() REQUIRES_SHARED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ bool IsCharArray() REQUIRES_SHARED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
CharArray* AsCharArray() REQUIRES_SHARED(Locks::mutator_lock_);
+
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ bool IsShortArray() REQUIRES_SHARED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
ShortArray* AsShortArray() REQUIRES_SHARED(Locks::mutator_lock_);
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- ShortArray* AsShortSizedArray() REQUIRES_SHARED(Locks::mutator_lock_);
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
- ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsIntArray() REQUIRES_SHARED(Locks::mutator_lock_);
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
- ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
IntArray* AsIntArray() REQUIRES_SHARED(Locks::mutator_lock_);
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
- ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsLongArray() REQUIRES_SHARED(Locks::mutator_lock_);
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
- ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
LongArray* AsLongArray() REQUIRES_SHARED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
@@ -757,6 +760,9 @@
size_t num_bytes)
REQUIRES_SHARED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags, Primitive::Type kType>
+ bool IsSpecificPrimitiveArray() REQUIRES_SHARED(Locks::mutator_lock_);
+
static Atomic<uint32_t> hash_code_seed;
// The Class representing the type of the object.
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 0f0a378..df2a8e2 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -97,6 +97,7 @@
lock_count_(0),
obj_(GcRoot<mirror::Object>(obj)),
wait_set_(nullptr),
+ wake_set_(nullptr),
hash_code_(hash_code),
locking_method_(nullptr),
locking_dex_pc_(0),
@@ -120,6 +121,7 @@
lock_count_(0),
obj_(GcRoot<mirror::Object>(obj)),
wait_set_(nullptr),
+ wake_set_(nullptr),
hash_code_(hash_code),
locking_method_(nullptr),
locking_dex_pc_(0),
@@ -226,7 +228,8 @@
}
void Monitor::AppendToWaitSet(Thread* thread) {
- DCHECK(owner_ == Thread::Current());
+ // Not checking that the owner is equal to this thread, since we've released
+ // the monitor by the time this method is called.
DCHECK(thread != nullptr);
DCHECK(thread->GetWaitNext() == nullptr) << thread->GetWaitNext();
if (wait_set_ == nullptr) {
@@ -245,24 +248,29 @@
void Monitor::RemoveFromWaitSet(Thread *thread) {
DCHECK(owner_ == Thread::Current());
DCHECK(thread != nullptr);
- if (wait_set_ == nullptr) {
- return;
- }
- if (wait_set_ == thread) {
- wait_set_ = thread->GetWaitNext();
- thread->SetWaitNext(nullptr);
- return;
- }
-
- Thread* t = wait_set_;
- while (t->GetWaitNext() != nullptr) {
- if (t->GetWaitNext() == thread) {
- t->SetWaitNext(thread->GetWaitNext());
- thread->SetWaitNext(nullptr);
- return;
+ auto remove = [&](Thread*& set){
+ if (set != nullptr) {
+ if (set == thread) {
+ set = thread->GetWaitNext();
+ thread->SetWaitNext(nullptr);
+ return true;
+ }
+ Thread* t = set;
+ while (t->GetWaitNext() != nullptr) {
+ if (t->GetWaitNext() == thread) {
+ t->SetWaitNext(thread->GetWaitNext());
+ thread->SetWaitNext(nullptr);
+ return true;
+ }
+ t = t->GetWaitNext();
+ }
}
- t = t->GetWaitNext();
+ return false;
+ };
+ if (remove(wait_set_)) {
+ return;
}
+ remove(wake_set_);
}
void Monitor::SetObject(mirror::Object* object) {
@@ -699,33 +707,81 @@
bool Monitor::Unlock(Thread* self) {
DCHECK(self != nullptr);
uint32_t owner_thread_id = 0u;
- {
- MutexLock mu(self, monitor_lock_);
- Thread* owner = owner_;
- if (owner != nullptr) {
- owner_thread_id = owner->GetThreadId();
- }
- if (owner == self) {
- // We own the monitor, so nobody else can be in here.
- AtraceMonitorUnlock();
- if (lock_count_ == 0) {
- owner_ = nullptr;
- locking_method_ = nullptr;
- locking_dex_pc_ = 0;
- // Wake a contender.
- monitor_contenders_.Signal(self);
- } else {
- --lock_count_;
- }
+ DCHECK(!monitor_lock_.IsExclusiveHeld(self));
+ monitor_lock_.Lock(self);
+ Thread* owner = owner_;
+ if (owner != nullptr) {
+ owner_thread_id = owner->GetThreadId();
+ }
+ if (owner == self) {
+ // We own the monitor, so nobody else can be in here.
+ AtraceMonitorUnlock();
+ if (lock_count_ == 0) {
+ owner_ = nullptr;
+ locking_method_ = nullptr;
+ locking_dex_pc_ = 0;
+ SignalContendersAndReleaseMonitorLock(self);
+ return true;
+ } else {
+ --lock_count_;
+ monitor_lock_.Unlock(self);
return true;
}
}
// We don't own this, so we're not allowed to unlock it.
// The JNI spec says that we should throw IllegalMonitorStateException in this case.
FailedUnlock(GetObject(), self->GetThreadId(), owner_thread_id, this);
+ monitor_lock_.Unlock(self);
return false;
}
+void Monitor::SignalContendersAndReleaseMonitorLock(Thread* self) {
+ // We want to signal one thread to wake up, to acquire the monitor that
+ // we are releasing. This could either be a Thread waiting on its own
+ // ConditionVariable, or a thread waiting on monitor_contenders_.
+ while (wake_set_ != nullptr) {
+ // No risk of waking ourselves here; since monitor_lock_ is not released until we're ready to
+ // return, notify can't move the current thread from wait_set_ to wake_set_ until this
+ // method is done checking wake_set_.
+ Thread* thread = wake_set_;
+ wake_set_ = thread->GetWaitNext();
+ thread->SetWaitNext(nullptr);
+
+ // Check to see if the thread is still waiting.
+ {
+ // In the case of wait(), we'll be acquiring another thread's GetWaitMutex with
+ // self's GetWaitMutex held. This does not risk deadlock, because we only acquire this lock
+ // for threads in the wake_set_. A thread can only enter wake_set_ from Notify or NotifyAll,
+ // and those hold monitor_lock_. Thus, the threads whose wait mutexes we acquire here must
+ // have already been released from wait(), since we have not released monitor_lock_ until
+ // after we've chosen our thread to wake, so there is no risk of the following lock ordering
+ // leading to deadlock:
+ // Thread 1 waits
+ // Thread 2 waits
+ // Thread 3 moves threads 1 and 2 from wait_set_ to wake_set_
+ // Thread 1 enters this block, and attempts to acquire Thread 2's GetWaitMutex to wake it
+ // Thread 2 enters this block, and attempts to acquire Thread 1's GetWaitMutex to wake it
+ //
+ // Since monitor_lock_ is not released until the thread-to-be-woken-up's GetWaitMutex is
+ // acquired, two threads cannot attempt to acquire each other's GetWaitMutex while holding
+ // their own and cause deadlock.
+ MutexLock wait_mu(self, *thread->GetWaitMutex());
+ if (thread->GetWaitMonitor() != nullptr) {
+ // Release the lock, so that a potentially awakened thread will not
+ // immediately contend on it. The lock ordering here is:
+ // monitor_lock_, self->GetWaitMutex, thread->GetWaitMutex
+ monitor_lock_.Unlock(self);
+ thread->GetWaitConditionVariable()->Signal(self);
+ return;
+ }
+ }
+ }
+ // If we didn't wake any threads that were originally waiting on us,
+ // wake a contender.
+ monitor_contenders_.Signal(self);
+ monitor_lock_.Unlock(self);
+}
+
void Monitor::Wait(Thread* self, int64_t ms, int32_t ns,
bool interruptShouldThrow, ThreadState why) {
DCHECK(self != nullptr);
@@ -755,17 +811,9 @@
}
/*
- * Add ourselves to the set of threads waiting on this monitor, and
- * release our hold. We need to let it go even if we're a few levels
+ * Release our hold - we need to let it go even if we're a few levels
* deep in a recursive lock, and we need to restore that later.
- *
- * We append to the wait set ahead of clearing the count and owner
- * fields so the subroutine can check that the calling thread owns
- * the monitor. Aside from that, the order of member updates is
- * not order sensitive as we hold the pthread mutex.
*/
- AppendToWaitSet(self);
- ++num_waiters_;
int prev_lock_count = lock_count_;
lock_count_ = 0;
owner_ = nullptr;
@@ -790,6 +838,17 @@
// Pseudo-atomically wait on self's wait_cond_ and release the monitor lock.
MutexLock mu(self, *self->GetWaitMutex());
+ /*
+ * Add ourselves to the set of threads waiting on this monitor.
+ * It's important that we are only added to the wait set after
+ * acquiring our GetWaitMutex, so that calls to Notify() that occur after we
+ * have released monitor_lock_ will not move us from wait_set_ to wake_set_
+ * until we've signalled contenders on this monitor.
+ */
+ AppendToWaitSet(self);
+ ++num_waiters_;
+
+
// Set wait_monitor_ to the monitor object we will be waiting on. When wait_monitor_ is
// non-null a notifying or interrupting thread must signal the thread's wait_cond_ to wake it
// up.
@@ -797,8 +856,7 @@
self->SetWaitMonitor(this);
// Release the monitor lock.
- monitor_contenders_.Signal(self);
- monitor_lock_.Unlock(self);
+ SignalContendersAndReleaseMonitorLock(self);
// Handle the case where the thread was interrupted before we called wait().
if (self->IsInterrupted()) {
@@ -874,18 +932,12 @@
ThrowIllegalMonitorStateExceptionF("object not locked by thread before notify()");
return;
}
- // Signal the first waiting thread in the wait set.
- while (wait_set_ != nullptr) {
- Thread* thread = wait_set_;
- wait_set_ = thread->GetWaitNext();
- thread->SetWaitNext(nullptr);
-
- // Check to see if the thread is still waiting.
- MutexLock wait_mu(self, *thread->GetWaitMutex());
- if (thread->GetWaitMonitor() != nullptr) {
- thread->GetWaitConditionVariable()->Signal(self);
- return;
- }
+ // Move one thread from waiters to wake set
+ Thread* to_move = wait_set_;
+ if (to_move != nullptr) {
+ wait_set_ = to_move->GetWaitNext();
+ to_move->SetWaitNext(wake_set_);
+ wake_set_ = to_move;
}
}
@@ -897,12 +949,20 @@
ThrowIllegalMonitorStateExceptionF("object not locked by thread before notifyAll()");
return;
}
- // Signal all threads in the wait set.
- while (wait_set_ != nullptr) {
- Thread* thread = wait_set_;
- wait_set_ = thread->GetWaitNext();
- thread->SetWaitNext(nullptr);
- thread->Notify();
+
+ // Move all threads from waiters to wake set
+ Thread* to_move = wait_set_;
+ if (to_move != nullptr) {
+ wait_set_ = nullptr;
+ Thread* move_to = wake_set_;
+ if (move_to == nullptr) {
+ wake_set_ = to_move;
+ return;
+ }
+ while (move_to->GetWaitNext() != nullptr) {
+ move_to = move_to->GetWaitNext();
+ }
+ move_to->SetWaitNext(to_move);
}
}
diff --git a/runtime/monitor.h b/runtime/monitor.h
index 6b7604e..c1f84e9 100644
--- a/runtime/monitor.h
+++ b/runtime/monitor.h
@@ -181,6 +181,8 @@
// this routine.
void RemoveFromWaitSet(Thread* thread) REQUIRES(monitor_lock_);
+ void SignalContendersAndReleaseMonitorLock(Thread* self) RELEASE(monitor_lock_);
+
// Changes the shape of a monitor from thin to fat, preserving the internal lock state. The
// calling thread must own the lock or the owner must be suspended. There's a race with other
// threads inflating the lock, installing hash codes and spurious failures. The caller should
@@ -306,6 +308,9 @@
// Threads currently waiting on this monitor.
Thread* wait_set_ GUARDED_BY(monitor_lock_);
+ // Threads that were waiting on this monitor, but are now contending on it.
+ Thread* wake_set_ GUARDED_BY(monitor_lock_);
+
// Stored object hash code, generated lazily by GetHashCode.
AtomicInteger hash_code_;
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 32b88e6..49b71cd 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -371,7 +371,7 @@
const char* class_name = dex_file->StringByTypeIdx(type_idx);
ClassLinker* linker = Runtime::Current()->GetClassLinker();
ObjPtr<mirror::Class> klass = (class_name[1] == '\0')
- ? linker->FindPrimitiveClass(class_name[0])
+ ? linker->LookupPrimitiveClass(class_name[0])
: linker->LookupClass(self, class_name, nullptr);
if (klass == nullptr) {
return;
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 4d3ad62..e4bc1b7 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -304,8 +304,7 @@
// Our system thread ID, etc, has changed so reset Thread state.
thread->InitAfterFork();
runtime_flags = EnableDebugFeatures(runtime_flags);
- hiddenapi::EnforcementPolicy api_enforcement_policy = hiddenapi::EnforcementPolicy::kNoChecks;
- bool dedupe_hidden_api_warnings = true;
+ hiddenapi::EnforcementPolicy api_enforcement_policy = hiddenapi::EnforcementPolicy::kDisabled;
if ((runtime_flags & DISABLE_VERIFIER) != 0) {
Runtime::Current()->DisableVerifier();
@@ -372,14 +371,14 @@
}
}
- bool do_hidden_api_checks = api_enforcement_policy != hiddenapi::EnforcementPolicy::kNoChecks;
+ bool do_hidden_api_checks = api_enforcement_policy != hiddenapi::EnforcementPolicy::kDisabled;
DCHECK(!(is_system_server && do_hidden_api_checks))
<< "SystemServer should be forked with EnforcementPolicy::kDisable";
DCHECK(!(is_zygote && do_hidden_api_checks))
<< "Child zygote processes should be forked with EnforcementPolicy::kDisable";
Runtime::Current()->SetHiddenApiEnforcementPolicy(api_enforcement_policy);
- Runtime::Current()->SetDedupeHiddenApiWarnings(dedupe_hidden_api_warnings);
- if (api_enforcement_policy != hiddenapi::EnforcementPolicy::kNoChecks &&
+ Runtime::Current()->SetDedupeHiddenApiWarnings(true);
+ if (api_enforcement_policy != hiddenapi::EnforcementPolicy::kDisabled &&
Runtime::Current()->GetHiddenApiEventLogSampleRate() != 0) {
// Hidden API checks are enabled, and we are sampling access for the event log. Initialize the
// random seed, to ensure the sampling is actually random. We do this post-fork, as doing it
@@ -387,9 +386,6 @@
std::srand(static_cast<uint32_t>(NanoTime()));
}
- // Clear the hidden API warning flag, in case it was set.
- Runtime::Current()->SetPendingHiddenApiWarning(false);
-
if (is_zygote) {
// If creating a child-zygote, do not call into the runtime's post-fork logic.
// Doing so would spin up threads for Binder and JDWP. Instead, the Java side
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index c7b8ad4..612a4b3 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -54,11 +54,12 @@
namespace art {
-// Returns true if the first caller outside of the Class class or java.lang.invoke package
-// is in a platform DEX file.
-static bool IsCallerTrusted(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
- // Walk the stack and find the first frame not from java.lang.Class and not from java.lang.invoke.
- // This is very expensive. Save this till the last.
+// Walks the stack, finds the caller of this reflective call and returns
+// a hiddenapi AccessContext formed from its declaring class.
+static hiddenapi::AccessContext GetReflectionCaller(Thread* self)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Walk the stack and find the first frame not from java.lang.Class and not
+ // from java.lang.invoke. This is very expensive. Save this till the last.
struct FirstExternalCallerVisitor : public StackVisitor {
explicit FirstExternalCallerVisitor(Thread* thread)
: StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
@@ -102,50 +103,42 @@
FirstExternalCallerVisitor visitor(self);
visitor.WalkStack();
- return visitor.caller != nullptr &&
- hiddenapi::IsCallerTrusted(visitor.caller->GetDeclaringClass());
-}
-// Returns true if the first non-ClassClass caller up the stack is not allowed to
-// access hidden APIs. This can be *very* expensive. Never call this in a loop.
-ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- hiddenapi::EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy();
- return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerTrusted(self);
+ // Construct AccessContext from the calling class found on the stack.
+ // If the calling class cannot be determined, e.g. unattached threads,
+ // we conservatively assume the caller is trusted.
+ ObjPtr<mirror::Class> caller = (visitor.caller == nullptr)
+ ? nullptr : visitor.caller->GetDeclaringClass();
+ return caller.IsNull() ? hiddenapi::AccessContext(/* is_trusted= */ true)
+ : hiddenapi::AccessContext(caller);
}
// Returns true if the first non-ClassClass caller up the stack should not be
// allowed access to `member`.
template<typename T>
-ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)
+ALWAYS_INLINE static bool ShouldDenyAccessToMember(T* member, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
- hiddenapi::Action action = hiddenapi::GetMemberAction(
- member, self, IsCallerTrusted, hiddenapi::kReflection);
- if (action != hiddenapi::kAllow) {
- hiddenapi::NotifyHiddenApiListener(member);
- }
-
- return action == hiddenapi::kDeny;
+ return hiddenapi::ShouldDenyAccessToMember(
+ member,
+ [&]() REQUIRES_SHARED(Locks::mutator_lock_) { return GetReflectionCaller(self); },
+ hiddenapi::AccessMethod::kReflection);
}
// Returns true if a class member should be discoverable with reflection given
// the criteria. Some reflection calls only return public members
// (public_only == true), some members should be hidden from non-boot class path
-// callers (enforce_hidden_api == true).
+// callers (hiddenapi_context).
template<typename T>
ALWAYS_INLINE static bool IsDiscoverable(bool public_only,
- bool enforce_hidden_api,
+ hiddenapi::AccessContext access_context,
T* member)
REQUIRES_SHARED(Locks::mutator_lock_) {
if (public_only && ((member->GetAccessFlags() & kAccPublic) == 0)) {
return false;
}
- return hiddenapi::GetMemberAction(member,
- nullptr,
- [enforce_hidden_api] (Thread*) { return !enforce_hidden_api; },
- hiddenapi::kNone)
- != hiddenapi::kDeny;
+ return !hiddenapi::ShouldDenyAccessToMember(
+ member, access_context, hiddenapi::AccessMethod::kNone);
}
ALWAYS_INLINE static inline ObjPtr<mirror::Class> DecodeClass(
@@ -266,15 +259,15 @@
IterationRange<StrideIterator<ArtField>> ifields = klass->GetIFields();
IterationRange<StrideIterator<ArtField>> sfields = klass->GetSFields();
size_t array_size = klass->NumInstanceFields() + klass->NumStaticFields();
- bool enforce_hidden_api = ShouldEnforceHiddenApi(self);
+ hiddenapi::AccessContext hiddenapi_context = GetReflectionCaller(self);
// Lets go subtract all the non discoverable fields.
for (ArtField& field : ifields) {
- if (!IsDiscoverable(public_only, enforce_hidden_api, &field)) {
+ if (!IsDiscoverable(public_only, hiddenapi_context, &field)) {
--array_size;
}
}
for (ArtField& field : sfields) {
- if (!IsDiscoverable(public_only, enforce_hidden_api, &field)) {
+ if (!IsDiscoverable(public_only, hiddenapi_context, &field)) {
--array_size;
}
}
@@ -285,7 +278,7 @@
return nullptr;
}
for (ArtField& field : ifields) {
- if (IsDiscoverable(public_only, enforce_hidden_api, &field)) {
+ if (IsDiscoverable(public_only, hiddenapi_context, &field)) {
auto* reflect_field = mirror::Field::CreateFromArtField<kRuntimePointerSize>(self,
&field,
force_resolve);
@@ -300,7 +293,7 @@
}
}
for (ArtField& field : sfields) {
- if (IsDiscoverable(public_only, enforce_hidden_api, &field)) {
+ if (IsDiscoverable(public_only, hiddenapi_context, &field)) {
auto* reflect_field = mirror::Field::CreateFromArtField<kRuntimePointerSize>(self,
&field,
force_resolve);
@@ -459,8 +452,7 @@
StackHandleScope<1> hs(soa.Self());
Handle<mirror::Field> field = hs.NewHandle(GetPublicFieldRecursive(
soa.Self(), DecodeClass(soa, javaThis), name_string));
- if (field.Get() == nullptr ||
- ShouldBlockAccessToMember(field->GetArtField(), soa.Self())) {
+ if (field.Get() == nullptr || ShouldDenyAccessToMember(field->GetArtField(), soa.Self())) {
return nullptr;
}
return soa.AddLocalReference<jobject>(field.Get());
@@ -477,7 +469,7 @@
Handle<mirror::Class> h_klass = hs.NewHandle(DecodeClass(soa, javaThis));
Handle<mirror::Field> result =
hs.NewHandle(GetDeclaredField(soa.Self(), h_klass.Get(), h_string.Get()));
- if (result == nullptr || ShouldBlockAccessToMember(result->GetArtField(), soa.Self())) {
+ if (result == nullptr || ShouldDenyAccessToMember(result->GetArtField(), soa.Self())) {
std::string name_str = h_string->ToModifiedUtf8();
if (name_str == "value" && h_klass->IsStringClass()) {
// We log the error for this specific case, as the user might just swallow the exception.
@@ -509,19 +501,19 @@
soa.Self(),
DecodeClass(soa, javaThis),
soa.Decode<mirror::ObjectArray<mirror::Class>>(args)));
- if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) {
+ if (result == nullptr || ShouldDenyAccessToMember(result->GetArtMethod(), soa.Self())) {
return nullptr;
}
return soa.AddLocalReference<jobject>(result.Get());
}
static ALWAYS_INLINE inline bool MethodMatchesConstructor(
- ArtMethod* m, bool public_only, bool enforce_hidden_api)
+ ArtMethod* m, bool public_only, hiddenapi::AccessContext hiddenapi_context)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(m != nullptr);
return m->IsConstructor() &&
!m->IsStatic() &&
- IsDiscoverable(public_only, enforce_hidden_api, m);
+ IsDiscoverable(public_only, hiddenapi_context, m);
}
static jobjectArray Class_getDeclaredConstructorsInternal(
@@ -529,12 +521,12 @@
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<2> hs(soa.Self());
bool public_only = (publicOnly != JNI_FALSE);
- bool enforce_hidden_api = ShouldEnforceHiddenApi(soa.Self());
+ hiddenapi::AccessContext hiddenapi_context = GetReflectionCaller(soa.Self());
Handle<mirror::Class> h_klass = hs.NewHandle(DecodeClass(soa, javaThis));
size_t constructor_count = 0;
// Two pass approach for speed.
for (auto& m : h_klass->GetDirectMethods(kRuntimePointerSize)) {
- constructor_count += MethodMatchesConstructor(&m, public_only, enforce_hidden_api) ? 1u : 0u;
+ constructor_count += MethodMatchesConstructor(&m, public_only, hiddenapi_context) ? 1u : 0u;
}
auto h_constructors = hs.NewHandle(mirror::ObjectArray<mirror::Constructor>::Alloc(
soa.Self(), GetClassRoot<mirror::ObjectArray<mirror::Constructor>>(), constructor_count));
@@ -544,7 +536,7 @@
}
constructor_count = 0;
for (auto& m : h_klass->GetDirectMethods(kRuntimePointerSize)) {
- if (MethodMatchesConstructor(&m, public_only, enforce_hidden_api)) {
+ if (MethodMatchesConstructor(&m, public_only, hiddenapi_context)) {
DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
DCHECK(!Runtime::Current()->IsActiveTransaction());
ObjPtr<mirror::Constructor> constructor =
@@ -571,7 +563,7 @@
DecodeClass(soa, javaThis),
soa.Decode<mirror::String>(name),
soa.Decode<mirror::ObjectArray<mirror::Class>>(args)));
- if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) {
+ if (result == nullptr || ShouldDenyAccessToMember(result->GetArtMethod(), soa.Self())) {
return nullptr;
}
return soa.AddLocalReference<jobject>(result.Get());
@@ -582,7 +574,7 @@
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<2> hs(soa.Self());
- bool enforce_hidden_api = ShouldEnforceHiddenApi(soa.Self());
+ hiddenapi::AccessContext hiddenapi_context = GetReflectionCaller(soa.Self());
bool public_only = (publicOnly != JNI_FALSE);
Handle<mirror::Class> klass = hs.NewHandle(DecodeClass(soa, javaThis));
@@ -591,7 +583,7 @@
uint32_t modifiers = m.GetAccessFlags();
// Add non-constructor declared methods.
if ((modifiers & kAccConstructor) == 0 &&
- IsDiscoverable(public_only, enforce_hidden_api, &m)) {
+ IsDiscoverable(public_only, hiddenapi_context, &m)) {
++num_methods;
}
}
@@ -605,7 +597,7 @@
for (ArtMethod& m : klass->GetDeclaredMethods(kRuntimePointerSize)) {
uint32_t modifiers = m.GetAccessFlags();
if ((modifiers & kAccConstructor) == 0 &&
- IsDiscoverable(public_only, enforce_hidden_api, &m)) {
+ IsDiscoverable(public_only, hiddenapi_context, &m)) {
DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
DCHECK(!Runtime::Current()->IsActiveTransaction());
ObjPtr<mirror::Method> method =
@@ -819,7 +811,7 @@
soa.Self(),
ScopedNullHandle<mirror::ObjectArray<mirror::Class>>(),
kRuntimePointerSize);
- if (UNLIKELY(constructor == nullptr) || ShouldBlockAccessToMember(constructor, soa.Self())) {
+ if (UNLIKELY(constructor == nullptr) || ShouldDenyAccessToMember(constructor, soa.Self())) {
soa.Self()->ThrowNewExceptionF("Ljava/lang/InstantiationException;",
"%s has no zero argument constructor",
klass->PrettyClass().c_str());
diff --git a/runtime/native/java_lang_System.cc b/runtime/native/java_lang_System.cc
index 2c4184c..e4bc8ce 100644
--- a/runtime/native/java_lang_System.cc
+++ b/runtime/native/java_lang_System.cc
@@ -101,32 +101,36 @@
case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
DCHECK_EQ(Primitive::ComponentSize(dstComponentPrimitiveType), 1U);
- dstArray->AsByteSizedArray()->Memmove(dstPos, srcArray->AsByteSizedArray(), srcPos, count);
+ // Note: Treating BooleanArray as ByteArray.
+ ObjPtr<mirror::ByteArray>::DownCast(dstArray)->Memmove(
+ dstPos, ObjPtr<mirror::ByteArray>::DownCast(srcArray), srcPos, count);
return;
case Primitive::kPrimChar:
case Primitive::kPrimShort:
DCHECK_EQ(Primitive::ComponentSize(dstComponentPrimitiveType), 2U);
- dstArray->AsShortSizedArray()->Memmove(dstPos, srcArray->AsShortSizedArray(), srcPos, count);
+ // Note: Treating CharArray as ShortArray.
+ ObjPtr<mirror::ShortArray>::DownCast(dstArray)->Memmove(
+ dstPos, ObjPtr<mirror::ShortArray>::DownCast(srcArray), srcPos, count);
return;
case Primitive::kPrimInt:
- DCHECK_EQ(Primitive::ComponentSize(dstComponentPrimitiveType), 4U);
- dstArray->AsIntArray()->Memmove(dstPos, srcArray->AsIntArray(), srcPos, count);
- return;
case Primitive::kPrimFloat:
DCHECK_EQ(Primitive::ComponentSize(dstComponentPrimitiveType), 4U);
- dstArray->AsFloatArray()->Memmove(dstPos, srcArray->AsFloatArray(), srcPos, count);
+ // Note: Treating FloatArray as IntArray.
+ ObjPtr<mirror::IntArray>::DownCast(dstArray)->Memmove(
+ dstPos, ObjPtr<mirror::IntArray>::DownCast(srcArray), srcPos, count);
return;
case Primitive::kPrimLong:
- DCHECK_EQ(Primitive::ComponentSize(dstComponentPrimitiveType), 8U);
- dstArray->AsLongArray()->Memmove(dstPos, srcArray->AsLongArray(), srcPos, count);
- return;
case Primitive::kPrimDouble:
DCHECK_EQ(Primitive::ComponentSize(dstComponentPrimitiveType), 8U);
- dstArray->AsDoubleArray()->Memmove(dstPos, srcArray->AsDoubleArray(), srcPos, count);
+ // Note: Treating DoubleArray as LongArray.
+ ObjPtr<mirror::LongArray>::DownCast(dstArray)->Memmove(
+ dstPos, ObjPtr<mirror::LongArray>::DownCast(srcArray), srcPos, count);
return;
case Primitive::kPrimNot: {
- mirror::ObjectArray<mirror::Object>* dstObjArray = dstArray->AsObjectArray<mirror::Object>();
- mirror::ObjectArray<mirror::Object>* srcObjArray = srcArray->AsObjectArray<mirror::Object>();
+ mirror::ObjectArray<mirror::Object>* dstObjArray =
+ dstArray->AsObjectArray<mirror::Object>();
+ mirror::ObjectArray<mirror::Object>* srcObjArray =
+ srcArray->AsObjectArray<mirror::Object>();
dstObjArray->AssignableMemmove(dstPos, srcObjArray, srcPos, count);
return;
}
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index b9ac88d..f21ded9 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -334,14 +334,15 @@
return;
}
ObjPtr<mirror::Class> field_type;
- const char* field_type_desciptor = f->GetArtField()->GetTypeDescriptor();
- Primitive::Type field_prim_type = Primitive::GetType(field_type_desciptor[0]);
+ const char* field_type_descriptor = f->GetArtField()->GetTypeDescriptor();
+ Primitive::Type field_prim_type = Primitive::GetType(field_type_descriptor[0]);
if (field_prim_type == Primitive::kPrimNot) {
field_type = f->GetType();
- DCHECK(field_type != nullptr);
} else {
- field_type = Runtime::Current()->GetClassLinker()->FindPrimitiveClass(field_type_desciptor[0]);
+ field_type =
+ Runtime::Current()->GetClassLinker()->LookupPrimitiveClass(field_type_descriptor[0]);
}
+ DCHECK(field_type != nullptr) << field_type_descriptor;
// We now don't expect suspension unless an exception is thrown.
// Unbox the value, if necessary.
ObjPtr<mirror::Object> boxed_value = soa.Decode<mirror::Object>(javaValue);
diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc
index e021b77..5014f34 100644
--- a/runtime/native/sun_misc_Unsafe.cc
+++ b/runtime/native/sun_misc_Unsafe.cc
@@ -31,8 +31,10 @@
#include "mirror/array.h"
#include "mirror/class-inl.h"
#include "mirror/object-inl.h"
+#include "art_field-inl.h"
#include "native_util.h"
#include "scoped_fast_native_object_access-inl.h"
+#include "well_known_classes.h"
namespace art {
@@ -364,13 +366,17 @@
ObjPtr<mirror::Object> dst = soa.Decode<mirror::Object>(dstObj);
ObjPtr<mirror::Class> component_type = dst->GetClass()->GetComponentType();
if (component_type->IsPrimitiveByte() || component_type->IsPrimitiveBoolean()) {
- copyToArray(srcAddr, MakeObjPtr(dst->AsByteSizedArray()), dst_offset, sz);
+ // Note: Treating BooleanArray as ByteArray.
+ copyToArray(srcAddr, ObjPtr<mirror::ByteArray>::DownCast(dst), dst_offset, sz);
} else if (component_type->IsPrimitiveShort() || component_type->IsPrimitiveChar()) {
- copyToArray(srcAddr, MakeObjPtr(dst->AsShortSizedArray()), dst_offset, sz);
+ // Note: Treating CharArray as ShortArray.
+ copyToArray(srcAddr, ObjPtr<mirror::ShortArray>::DownCast(dst), dst_offset, sz);
} else if (component_type->IsPrimitiveInt() || component_type->IsPrimitiveFloat()) {
- copyToArray(srcAddr, MakeObjPtr(dst->AsIntArray()), dst_offset, sz);
+ // Note: Treating FloatArray as IntArray.
+ copyToArray(srcAddr, ObjPtr<mirror::IntArray>::DownCast(dst), dst_offset, sz);
} else if (component_type->IsPrimitiveLong() || component_type->IsPrimitiveDouble()) {
- copyToArray(srcAddr, MakeObjPtr(dst->AsLongArray()), dst_offset, sz);
+ // Note: Treating DoubleArray as LongArray.
+ copyToArray(srcAddr, ObjPtr<mirror::LongArray>::DownCast(dst), dst_offset, sz);
} else {
ThrowIllegalAccessException("not a primitive array");
}
@@ -395,13 +401,17 @@
ObjPtr<mirror::Object> src = soa.Decode<mirror::Object>(srcObj);
ObjPtr<mirror::Class> component_type = src->GetClass()->GetComponentType();
if (component_type->IsPrimitiveByte() || component_type->IsPrimitiveBoolean()) {
- copyFromArray(dstAddr, MakeObjPtr(src->AsByteSizedArray()), src_offset, sz);
+ // Note: Treating BooleanArray as ByteArray.
+ copyFromArray(dstAddr, ObjPtr<mirror::ByteArray>::DownCast(src), src_offset, sz);
} else if (component_type->IsPrimitiveShort() || component_type->IsPrimitiveChar()) {
- copyFromArray(dstAddr, MakeObjPtr(src->AsShortSizedArray()), src_offset, sz);
+ // Note: Treating CharArray as ShortArray.
+ copyFromArray(dstAddr, ObjPtr<mirror::ShortArray>::DownCast(src), src_offset, sz);
} else if (component_type->IsPrimitiveInt() || component_type->IsPrimitiveFloat()) {
- copyFromArray(dstAddr, MakeObjPtr(src->AsIntArray()), src_offset, sz);
+ // Note: Treating FloatArray as IntArray.
+ copyFromArray(dstAddr, ObjPtr<mirror::IntArray>::DownCast(src), src_offset, sz);
} else if (component_type->IsPrimitiveLong() || component_type->IsPrimitiveDouble()) {
- copyFromArray(dstAddr, MakeObjPtr(src->AsLongArray()), src_offset, sz);
+ // Note: Treating DoubleArray as LongArray.
+ copyFromArray(dstAddr, ObjPtr<mirror::LongArray>::DownCast(src), src_offset, sz);
} else {
ThrowIllegalAccessException("not a primitive array");
}
@@ -504,6 +514,33 @@
std::atomic_thread_fence(std::memory_order_seq_cst);
}
+static void Unsafe_park(JNIEnv* env, jobject, jboolean isAbsolute, jlong time) {
+ ScopedObjectAccess soa(env);
+ Thread::Current()->Park(isAbsolute, time);
+}
+
+static void Unsafe_unpark(JNIEnv* env, jobject, jobject jthread) {
+ art::ScopedFastNativeObjectAccess soa(env);
+ if (jthread == nullptr || !env->IsInstanceOf(jthread, WellKnownClasses::java_lang_Thread)) {
+ ThrowIllegalArgumentException("Argument to unpark() was not a Thread");
+ return;
+ }
+ art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_);
+ art::Thread* thread = art::Thread::FromManagedThread(soa, jthread);
+ if (thread != nullptr) {
+ thread->Unpark();
+ } else {
+ // If thread is null, that means that either the thread is not started yet,
+ // or the thread has already terminated. Setting the field to true will be
+ // respected when the thread does start, and is harmless if the thread has
+ // already terminated.
+ ArtField* unparked =
+ jni::DecodeArtField(WellKnownClasses::java_lang_Thread_unparkedBeforeStart);
+ // JNI must use non transactional mode.
+ unparked->SetBoolean<false>(soa.Decode<mirror::Object>(jthread), JNI_TRUE);
+ }
+}
+
static JNINativeMethod gMethods[] = {
FAST_NATIVE_METHOD(Unsafe, compareAndSwapInt, "(Ljava/lang/Object;JII)Z"),
FAST_NATIVE_METHOD(Unsafe, compareAndSwapLong, "(Ljava/lang/Object;JJJ)Z"),
@@ -546,6 +583,8 @@
FAST_NATIVE_METHOD(Unsafe, putShort, "(Ljava/lang/Object;JS)V"),
FAST_NATIVE_METHOD(Unsafe, putFloat, "(Ljava/lang/Object;JF)V"),
FAST_NATIVE_METHOD(Unsafe, putDouble, "(Ljava/lang/Object;JD)V"),
+ FAST_NATIVE_METHOD(Unsafe, unpark, "(Ljava/lang/Object;)V"),
+ NATIVE_METHOD(Unsafe, park, "(ZJ)V"),
// Each of the getFoo variants are overloaded with a call that operates
// directively on a native pointer.
diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc
index 10f5898..88adad0 100644
--- a/runtime/native_stack_dump.cc
+++ b/runtime/native_stack_dump.cc
@@ -373,7 +373,7 @@
}
os << std::endl;
if (try_addr2line && use_addr2line) {
- Addr2line(it->map.name, it->pc - it->map.start, os, prefix, &addr2line_state);
+ Addr2line(it->map.name, it->rel_pc, os, prefix, &addr2line_state);
}
}
diff --git a/runtime/oat.h b/runtime/oat.h
index 2a6d738..3d6415e 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -34,9 +34,7 @@
// Last oat version changed reason: Remove interpreter alt tables.
static constexpr uint8_t kOatVersion[] = { '1', '6', '3', '\0' };
- static constexpr const char* kImageLocationKey = "image-location";
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
- static constexpr const char* kDex2OatHostKey = "dex2oat-host";
static constexpr const char* kDebuggableKey = "debuggable";
static constexpr const char* kNativeDebuggableKey = "native-debuggable";
static constexpr const char* kCompilerFilter = "compiler-filter";
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 646de75..8011836 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -886,32 +886,31 @@
JValue boxed_value;
ObjPtr<mirror::Class> klass = o->GetClass();
- ObjPtr<mirror::Class> src_class = nullptr;
- ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ Primitive::Type primitive_type;
ArtField* primitive_field = &klass->GetIFieldsPtr()->At(0);
if (klass->DescriptorEquals("Ljava/lang/Boolean;")) {
- src_class = class_linker->FindPrimitiveClass('Z');
+ primitive_type = Primitive::kPrimBoolean;
boxed_value.SetZ(primitive_field->GetBoolean(o));
} else if (klass->DescriptorEquals("Ljava/lang/Byte;")) {
- src_class = class_linker->FindPrimitiveClass('B');
+ primitive_type = Primitive::kPrimByte;
boxed_value.SetB(primitive_field->GetByte(o));
} else if (klass->DescriptorEquals("Ljava/lang/Character;")) {
- src_class = class_linker->FindPrimitiveClass('C');
+ primitive_type = Primitive::kPrimChar;
boxed_value.SetC(primitive_field->GetChar(o));
} else if (klass->DescriptorEquals("Ljava/lang/Float;")) {
- src_class = class_linker->FindPrimitiveClass('F');
+ primitive_type = Primitive::kPrimFloat;
boxed_value.SetF(primitive_field->GetFloat(o));
} else if (klass->DescriptorEquals("Ljava/lang/Double;")) {
- src_class = class_linker->FindPrimitiveClass('D');
+ primitive_type = Primitive::kPrimDouble;
boxed_value.SetD(primitive_field->GetDouble(o));
} else if (klass->DescriptorEquals("Ljava/lang/Integer;")) {
- src_class = class_linker->FindPrimitiveClass('I');
+ primitive_type = Primitive::kPrimInt;
boxed_value.SetI(primitive_field->GetInt(o));
} else if (klass->DescriptorEquals("Ljava/lang/Long;")) {
- src_class = class_linker->FindPrimitiveClass('J');
+ primitive_type = Primitive::kPrimLong;
boxed_value.SetJ(primitive_field->GetLong(o));
} else if (klass->DescriptorEquals("Ljava/lang/Short;")) {
- src_class = class_linker->FindPrimitiveClass('S');
+ primitive_type = Primitive::kPrimShort;
boxed_value.SetS(primitive_field->GetShort(o));
} else {
std::string temp;
@@ -923,7 +922,8 @@
}
return ConvertPrimitiveValue(unbox_for_result,
- src_class->GetPrimitiveType(), dst_class->GetPrimitiveType(),
+ primitive_type,
+ dst_class->GetPrimitiveType(),
boxed_value, unboxed_value);
}
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 34b84f5..ccbc2d9 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -269,10 +269,8 @@
oat_file_manager_(nullptr),
is_low_memory_mode_(false),
safe_mode_(false),
- hidden_api_policy_(hiddenapi::EnforcementPolicy::kNoChecks),
- pending_hidden_api_warning_(false),
+ hidden_api_policy_(hiddenapi::EnforcementPolicy::kDisabled),
dedupe_hidden_api_warnings_(true),
- always_set_hidden_api_warning_flag_(false),
hidden_api_access_event_log_rate_(0),
dump_native_stack_on_sig_quit_(true),
pruned_dalvik_cache_(false),
@@ -786,15 +784,11 @@
// TODO(calin): We use the JIT class as a proxy for JIT compilation and for
// recoding profiles. Maybe we should consider changing the name to be more clear it's
// not only about compiling. b/28295073.
- if (!safe_mode_ && (jit_options_->UseJitCompilation() || jit_options_->GetSaveProfilingInfo())) {
+ if (jit_options_->UseJitCompilation() || jit_options_->GetSaveProfilingInfo()) {
// Try to load compiler pre zygote to reduce PSS. b/27744947
std::string error_msg;
- if (!jit::Jit::LoadCompiler(&error_msg)) {
+ if (!jit::Jit::LoadCompilerLibrary(&error_msg)) {
LOG(WARNING) << "Failed to load JIT compiler with error " << error_msg;
- } else if (!IsZygote()) {
- // If we are the zygote then we need to wait until after forking to create the code cache
- // due to SELinux restrictions on r/w/x memory regions.
- CreateJitCodeCache(/*rwx_memory_allowed=*/true);
}
}
@@ -900,6 +894,11 @@
}
if (jit_ == nullptr) {
+ // The system server's code cache was initialized specially. For other zygote forks or
+ // processes create it now.
+ if (!is_system_server) {
+ CreateJitCodeCache(/*rwx_memory_allowed=*/true);
+ }
// Note that when running ART standalone (not zygote, nor zygote fork),
// the jit may have already been created.
CreateJit();
@@ -1101,6 +1100,10 @@
sentinel_ = GcRoot<mirror::Object>(sentinel);
}
+GcRoot<mirror::Object> Runtime::GetSentinel() {
+ return sentinel_;
+}
+
static inline void CreatePreAllocatedException(Thread* self,
Runtime* runtime,
GcRoot<mirror::Throwable>* exception,
@@ -1230,8 +1233,8 @@
// As is, we're encoding some logic here about which specific policy to use, which would be better
// controlled by the framework.
hidden_api_policy_ = do_hidden_api_checks
- ? hiddenapi::EnforcementPolicy::kDarkGreyAndBlackList
- : hiddenapi::EnforcementPolicy::kNoChecks;
+ ? hiddenapi::EnforcementPolicy::kEnabled
+ : hiddenapi::EnforcementPolicy::kDisabled;
no_sig_chain_ = runtime_options.Exists(Opt::NoSigChain);
force_native_bridge_ = runtime_options.Exists(Opt::ForceNativeBridge);
@@ -2485,7 +2488,7 @@
DCHECK(!jit_options_->UseJitCompilation());
}
- if (safe_mode_ || (!jit_options_->UseJitCompilation() && !jit_options_->GetSaveProfilingInfo())) {
+ if (!jit_options_->UseJitCompilation() && !jit_options_->GetSaveProfilingInfo()) {
return;
}
@@ -2506,7 +2509,16 @@
}
void Runtime::CreateJit() {
+ DCHECK(jit_ == nullptr);
if (jit_code_cache_.get() == nullptr) {
+ if (!IsSafeMode()) {
+ LOG(WARNING) << "Missing code cache, cannot create JIT.";
+ }
+ return;
+ }
+ if (IsSafeMode()) {
+ LOG(INFO) << "Not creating JIT because of SafeMode.";
+ jit_code_cache_.reset();
return;
}
@@ -2515,7 +2527,7 @@
if (jit == nullptr) {
LOG(WARNING) << "Failed to allocate JIT";
// Release JIT code cache resources (several MB of memory).
- jit_code_cache_.reset(nullptr);
+ jit_code_cache_.reset();
}
}
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 4fb0d2e..f6a5634 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -535,10 +535,6 @@
return hidden_api_policy_;
}
- void SetPendingHiddenApiWarning(bool value) {
- pending_hidden_api_warning_ = value;
- }
-
void SetHiddenApiExemptions(const std::vector<std::string>& exemptions) {
hidden_api_exemptions_ = exemptions;
}
@@ -547,10 +543,6 @@
return hidden_api_exemptions_;
}
- bool HasPendingHiddenApiWarning() const {
- return pending_hidden_api_warning_;
- }
-
void SetDedupeHiddenApiWarnings(bool value) {
dedupe_hidden_api_warnings_ = value;
}
@@ -559,14 +551,6 @@
return dedupe_hidden_api_warnings_;
}
- void AlwaysSetHiddenApiWarningFlag() {
- always_set_hidden_api_warning_flag_ = true;
- }
-
- bool ShouldAlwaysSetHiddenApiWarningFlag() const {
- return always_set_hidden_api_warning_flag_;
- }
-
void SetHiddenApiEventLogSampleRate(uint32_t rate) {
hidden_api_access_event_log_rate_ = rate;
}
@@ -650,6 +634,13 @@
void DeoptimizeBootImage();
bool IsNativeDebuggable() const {
+ CHECK(!is_zygote_ || IsAotCompiler());
+ return is_native_debuggable_;
+ }
+
+ // Note: prefer not to use this method, but the checked version above. The separation exists
+ // as the runtime state may change for a zygote child.
+ bool IsNativeDebuggableZygoteOK() const {
return is_native_debuggable_;
}
@@ -691,6 +682,9 @@
// Called from class linker.
void SetSentinel(mirror::Object* sentinel) REQUIRES_SHARED(Locks::mutator_lock_);
+ // For testing purpose only.
+ // TODO: Remove this when this is no longer needed (b/116087961).
+ GcRoot<mirror::Object> GetSentinel() REQUIRES_SHARED(Locks::mutator_lock_);
// Create a normal LinearAlloc or low 4gb version if we are 64 bit AOT compiler.
LinearAlloc* CreateLinearAlloc();
@@ -703,6 +697,11 @@
double GetHashTableMinLoadFactor() const;
double GetHashTableMaxLoadFactor() const;
+ bool IsSafeMode() const {
+ CHECK(!is_zygote_);
+ return safe_mode_;
+ }
+
void SetSafeMode(bool mode) {
safe_mode_ = mode;
}
diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc
index 758917c..bf74816 100644
--- a/runtime/runtime_callbacks.cc
+++ b/runtime/runtime_callbacks.cc
@@ -151,6 +151,26 @@
Remove(cb, &monitor_callbacks_);
}
+void RuntimeCallbacks::ThreadParkStart(bool is_absolute, int64_t timeout) {
+ for (ParkCallback * cb : park_callbacks_) {
+ cb->ThreadParkStart(is_absolute, timeout);
+ }
+}
+
+void RuntimeCallbacks::ThreadParkFinished(bool timeout) {
+ for (ParkCallback * cb : park_callbacks_) {
+ cb->ThreadParkFinished(timeout);
+ }
+}
+
+void RuntimeCallbacks::AddParkCallback(ParkCallback* cb) {
+ park_callbacks_.push_back(cb);
+}
+
+void RuntimeCallbacks::RemoveParkCallback(ParkCallback* cb) {
+ Remove(cb, &park_callbacks_);
+}
+
void RuntimeCallbacks::RemoveThreadLifecycleCallback(ThreadLifecycleCallback* cb) {
Remove(cb, &thread_callbacks_);
}
diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h
index 9f0410d..4cce15e 100644
--- a/runtime/runtime_callbacks.h
+++ b/runtime/runtime_callbacks.h
@@ -115,6 +115,19 @@
virtual ~MonitorCallback() {}
};
+class ParkCallback {
+ public:
+ // Called on entry to the Unsafe.#park method
+ virtual void ThreadParkStart(bool is_absolute, int64_t millis_timeout)
+ REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+
+ // Called just after the thread has woken up from going to sleep for a park(). This will only be
+ // called for Unsafe.park() calls where the thread did (or at least could have) gone to sleep.
+ virtual void ThreadParkFinished(bool timed_out) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+
+ virtual ~ParkCallback() {}
+};
+
// A callback to let parts of the runtime note that they are currently relying on a particular
// method remaining in it's current state. Users should not rely on always being called. If multiple
// callbacks are added the runtime will short-circuit when the first one returns 'true'.
@@ -193,6 +206,11 @@
void AddMonitorCallback(MonitorCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
void RemoveMonitorCallback(MonitorCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
+ void ThreadParkStart(bool is_absolute, int64_t timeout) REQUIRES_SHARED(Locks::mutator_lock_);
+ void ThreadParkFinished(bool timed_out) REQUIRES_SHARED(Locks::mutator_lock_);
+ void AddParkCallback(ParkCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
+ void RemoveParkCallback(ParkCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
+
// Returns true if some MethodInspectionCallback indicates the method is being inspected/depended
// on by some code.
bool IsMethodBeingInspected(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -243,6 +261,8 @@
GUARDED_BY(Locks::mutator_lock_);
std::vector<MonitorCallback*> monitor_callbacks_
GUARDED_BY(Locks::mutator_lock_);
+ std::vector<ParkCallback*> park_callbacks_
+ GUARDED_BY(Locks::mutator_lock_);
std::vector<MethodInspectionCallback*> method_inspection_callbacks_
GUARDED_BY(Locks::mutator_lock_);
std::vector<DdmCallback*> ddm_callbacks_
diff --git a/runtime/thread.cc b/runtime/thread.cc
index dda3b82..e9fed76 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -44,6 +44,7 @@
#include "arch/context.h"
#include "art_field-inl.h"
#include "art_method-inl.h"
+#include "base/atomic.h"
#include "base/bit_utils.h"
#include "base/casts.h"
#include "base/file_utils.h"
@@ -285,6 +286,120 @@
<< "No deoptimization context for thread " << *this;
}
+enum {
+ kPermitAvailable = 0, // Incrementing consumes the permit
+ kNoPermit = 1, // Incrementing marks as waiter waiting
+ kNoPermitWaiterWaiting = 2
+};
+
+void Thread::Park(bool is_absolute, int64_t time) {
+ DCHECK(this == Thread::Current());
+#if ART_USE_FUTEXES
+ // Consume the permit, or mark as waiting. This cannot cause park_state to go
+ // outside of its valid range (0, 1, 2), because in all cases where 2 is
+ // assigned it is set back to 1 before returning, and this method cannot run
+ // concurrently with itself since it operates on the current thread.
+ int old_state = tls32_.park_state_.fetch_add(1, std::memory_order_relaxed);
+ if (old_state == kNoPermit) {
+ // no permit was available. block thread until later.
+ Runtime::Current()->GetRuntimeCallbacks()->ThreadParkStart(is_absolute, time);
+ int result = 0;
+ bool timed_out = false;
+ if (!is_absolute && time == 0) {
+ // Thread.getState() is documented to return waiting for untimed parks.
+ ScopedThreadSuspension sts(this, ThreadState::kWaiting);
+ DCHECK_EQ(NumberOfHeldMutexes(), 0u);
+ result = futex(tls32_.park_state_.Address(),
+ FUTEX_WAIT_PRIVATE,
+ /* sleep if val = */ kNoPermitWaiterWaiting,
+ /* timeout */ nullptr,
+ nullptr,
+ 0);
+ } else if (time > 0) {
+ // Only actually suspend and futex_wait if we're going to wait for some
+ // positive amount of time - the kernel will reject negative times with
+ // EINVAL, and a zero time will just noop.
+
+ // Thread.getState() is documented to return timed wait for timed parks.
+ ScopedThreadSuspension sts(this, ThreadState::kTimedWaiting);
+ DCHECK_EQ(NumberOfHeldMutexes(), 0u);
+ timespec timespec;
+ if (is_absolute) {
+ // Time is millis when scheduled for an absolute time
+ timespec.tv_nsec = (time % 1000) * 1000000;
+ timespec.tv_sec = time / 1000;
+ // This odd looking pattern is recommended by futex documentation to
+ // wait until an absolute deadline, with otherwise identical behavior to
+ // FUTEX_WAIT_PRIVATE. This also allows parkUntil() to return at the
+ // correct time when the system clock changes.
+ result = futex(tls32_.park_state_.Address(),
+ FUTEX_WAIT_BITSET_PRIVATE | FUTEX_CLOCK_REALTIME,
+ /* sleep if val = */ kNoPermitWaiterWaiting,
+ ×pec,
+ nullptr,
+ FUTEX_BITSET_MATCH_ANY);
+ } else {
+ // Time is nanos when scheduled for a relative time
+ timespec.tv_sec = time / 1000000000;
+ timespec.tv_nsec = time % 1000000000;
+ result = futex(tls32_.park_state_.Address(),
+ FUTEX_WAIT_PRIVATE,
+ /* sleep if val = */ kNoPermitWaiterWaiting,
+ ×pec,
+ nullptr,
+ 0);
+ }
+ }
+ if (result == -1) {
+ switch (errno) {
+ case ETIMEDOUT:
+ timed_out = true;
+ FALLTHROUGH_INTENDED;
+ case EAGAIN:
+ case EINTR: break; // park() is allowed to spuriously return
+ default: PLOG(FATAL) << "Failed to park";
+ }
+ }
+ // Mark as no longer waiting, and consume permit if there is one.
+ tls32_.park_state_.store(kNoPermit, std::memory_order_relaxed);
+ // TODO: Call to signal jvmti here
+ Runtime::Current()->GetRuntimeCallbacks()->ThreadParkFinished(timed_out);
+ } else {
+ // the fetch_add has consumed the permit. immediately return.
+ DCHECK_EQ(old_state, kPermitAvailable);
+ }
+#else
+ #pragma clang diagnostic push
+ #pragma clang diagnostic warning "-W#warnings"
+ #warning "LockSupport.park/unpark implemented as noops without FUTEX support."
+ #pragma clang diagnostic pop
+ UNUSED(is_absolute, time);
+ UNIMPLEMENTED(WARNING);
+ sched_yield();
+#endif
+}
+
+void Thread::Unpark() {
+#if ART_USE_FUTEXES
+ // Set permit available; will be consumed either by fetch_add (when the thread
+ // tries to park) or store (when the parked thread is woken up)
+ if (tls32_.park_state_.exchange(kPermitAvailable, std::memory_order_relaxed)
+ == kNoPermitWaiterWaiting) {
+ int result = futex(tls32_.park_state_.Address(),
+ FUTEX_WAKE_PRIVATE,
+ /* number of waiters = */ 1,
+ nullptr,
+ nullptr,
+ 0);
+ if (result == -1) {
+ PLOG(FATAL) << "Failed to unpark";
+ }
+ }
+#else
+ UNIMPLEMENTED(WARNING);
+#endif
+}
+
void Thread::PushStackedShadowFrame(ShadowFrame* sf, StackedShadowFrameType type) {
StackedShadowFrameRecord* record = new StackedShadowFrameRecord(
sf, type, tlsPtr_.stacked_shadow_frame_record);
@@ -489,6 +604,22 @@
runtime->GetRuntimeCallbacks()->ThreadStart(self);
+ // Unpark ourselves if the java peer was unparked before it started (see
+ // b/28845097#comment49 for more information)
+
+ ArtField* unparkedField = jni::DecodeArtField(
+ WellKnownClasses::java_lang_Thread_unparkedBeforeStart);
+ bool should_unpark = false;
+ {
+ // Hold the lock here, so that if another thread calls unpark before the thread starts
+ // we don't observe the unparkedBeforeStart field before the unparker writes to it,
+ // which could cause a lost unpark.
+ art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_);
+ should_unpark = unparkedField->GetBoolean(self->tlsPtr_.opeer) == JNI_TRUE;
+ }
+ if (should_unpark) {
+ self->Unpark();
+ }
// Invoke the 'run' method of our java.lang.Thread.
ObjPtr<mirror::Object> receiver = self->tlsPtr_.opeer;
jmethodID mid = WellKnownClasses::java_lang_Thread_run;
@@ -2123,7 +2254,7 @@
: tls32_(daemon),
wait_monitor_(nullptr),
is_runtime_thread_(false) {
- wait_mutex_ = new Mutex("a thread wait mutex");
+ wait_mutex_ = new Mutex("a thread wait mutex", LockLevel::kThreadWaitLock);
wait_cond_ = new ConditionVariable("a thread wait condition variable", *wait_mutex_);
tlsPtr_.instrumentation_stack = new std::deque<instrumentation::InstrumentationStackFrame>;
tlsPtr_.name = new std::string(kThreadNameDuringStartup);
@@ -2133,6 +2264,9 @@
tls32_.state_and_flags.as_struct.flags = 0;
tls32_.state_and_flags.as_struct.state = kNative;
tls32_.interrupted.store(false, std::memory_order_relaxed);
+ // Initialize with no permit; if the java Thread was unparked before being
+ // started, it will unpark itself before calling into java code.
+ tls32_.park_state_.store(kNoPermit, std::memory_order_relaxed);
memset(&tlsPtr_.held_mutexes[0], 0, sizeof(tlsPtr_.held_mutexes));
std::fill(tlsPtr_.rosalloc_runs,
tlsPtr_.rosalloc_runs + kNumRosAllocThreadLocalSizeBracketsInThread,
@@ -2449,12 +2583,15 @@
}
void Thread::Interrupt(Thread* self) {
- MutexLock mu(self, *wait_mutex_);
- if (tls32_.interrupted.load(std::memory_order_seq_cst)) {
- return;
+ {
+ MutexLock mu(self, *wait_mutex_);
+ if (tls32_.interrupted.load(std::memory_order_seq_cst)) {
+ return;
+ }
+ tls32_.interrupted.store(true, std::memory_order_seq_cst);
+ NotifyLocked(self);
}
- tls32_.interrupted.store(true, std::memory_order_seq_cst);
- NotifyLocked(self);
+ Unpark();
}
void Thread::Notify() {
diff --git a/runtime/thread.h b/runtime/thread.h
index 941867c..b304cef 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -581,6 +581,11 @@
return poison_object_cookie_;
}
+ // Parking for 0ns of relative time means an untimed park, negative (though
+ // should be handled in java code) returns immediately
+ void Park(bool is_absolute, int64_t time) REQUIRES_SHARED(Locks::mutator_lock_);
+ void Unpark();
+
private:
void NotifyLocked(Thread* self) REQUIRES(wait_mutex_);
@@ -1543,6 +1548,8 @@
// Thread "interrupted" status; stays raised until queried or thrown.
Atomic<bool32_t> interrupted;
+ AtomicInteger park_state_;
+
// True if the thread is allowed to access a weak ref (Reference::GetReferent() and system
// weaks) and to potentially mark an object alive/gray. This is used for concurrent reference
// processing of the CC collector only. This is thread local so that we can enable/disable weak
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 206418f..d390611 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -122,12 +122,14 @@
jfieldID WellKnownClasses::dalvik_system_DexPathList_dexElements;
jfieldID WellKnownClasses::dalvik_system_DexPathList__Element_dexFile;
jfieldID WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer;
+jfieldID WellKnownClasses::java_lang_Thread_parkBlocker;
jfieldID WellKnownClasses::java_lang_Thread_daemon;
jfieldID WellKnownClasses::java_lang_Thread_group;
jfieldID WellKnownClasses::java_lang_Thread_lock;
jfieldID WellKnownClasses::java_lang_Thread_name;
jfieldID WellKnownClasses::java_lang_Thread_priority;
jfieldID WellKnownClasses::java_lang_Thread_nativePeer;
+jfieldID WellKnownClasses::java_lang_Thread_unparkedBeforeStart;
jfieldID WellKnownClasses::java_lang_ThreadGroup_groups;
jfieldID WellKnownClasses::java_lang_ThreadGroup_ngroups;
jfieldID WellKnownClasses::java_lang_ThreadGroup_mainThreadGroup;
@@ -291,7 +293,7 @@
void WellKnownClasses::Init(JNIEnv* env) {
hiddenapi::ScopedHiddenApiEnforcementPolicySetting hiddenapi_exemption(
- hiddenapi::EnforcementPolicy::kNoChecks);
+ hiddenapi::EnforcementPolicy::kDisabled);
dalvik_annotation_optimization_CriticalNative =
CacheClass(env, "dalvik/annotation/optimization/CriticalNative");
@@ -370,12 +372,14 @@
dalvik_system_DexPathList_dexElements = CacheField(env, dalvik_system_DexPathList, false, "dexElements", "[Ldalvik/system/DexPathList$Element;");
dalvik_system_DexPathList__Element_dexFile = CacheField(env, dalvik_system_DexPathList__Element, false, "dexFile", "Ldalvik/system/DexFile;");
dalvik_system_VMRuntime_nonSdkApiUsageConsumer = CacheField(env, dalvik_system_VMRuntime, true, "nonSdkApiUsageConsumer", "Ljava/util/function/Consumer;");
+ java_lang_Thread_parkBlocker = CacheField(env, java_lang_Thread, false, "parkBlocker", "Ljava/lang/Object;");
java_lang_Thread_daemon = CacheField(env, java_lang_Thread, false, "daemon", "Z");
java_lang_Thread_group = CacheField(env, java_lang_Thread, false, "group", "Ljava/lang/ThreadGroup;");
java_lang_Thread_lock = CacheField(env, java_lang_Thread, false, "lock", "Ljava/lang/Object;");
java_lang_Thread_name = CacheField(env, java_lang_Thread, false, "name", "Ljava/lang/String;");
java_lang_Thread_priority = CacheField(env, java_lang_Thread, false, "priority", "I");
java_lang_Thread_nativePeer = CacheField(env, java_lang_Thread, false, "nativePeer", "J");
+ java_lang_Thread_unparkedBeforeStart = CacheField(env, java_lang_Thread, false, "unparkedBeforeStart", "Z");
java_lang_ThreadGroup_groups = CacheField(env, java_lang_ThreadGroup, false, "groups", "[Ljava/lang/ThreadGroup;");
java_lang_ThreadGroup_ngroups = CacheField(env, java_lang_ThreadGroup, false, "ngroups", "I");
java_lang_ThreadGroup_mainThreadGroup = CacheField(env, java_lang_ThreadGroup, true, "mainThreadGroup", "Ljava/lang/ThreadGroup;");
@@ -516,6 +520,7 @@
dalvik_system_DexPathList_dexElements = nullptr;
dalvik_system_DexPathList__Element_dexFile = nullptr;
dalvik_system_VMRuntime_nonSdkApiUsageConsumer = nullptr;
+ java_lang_Thread_parkBlocker = nullptr;
java_lang_Thread_daemon = nullptr;
java_lang_Thread_group = nullptr;
java_lang_Thread_lock = nullptr;
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index ce5ab1d..130747c 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -131,12 +131,14 @@
static jfieldID dalvik_system_DexPathList_dexElements;
static jfieldID dalvik_system_DexPathList__Element_dexFile;
static jfieldID dalvik_system_VMRuntime_nonSdkApiUsageConsumer;
+ static jfieldID java_lang_Thread_parkBlocker;
static jfieldID java_lang_Thread_daemon;
static jfieldID java_lang_Thread_group;
static jfieldID java_lang_Thread_lock;
static jfieldID java_lang_Thread_name;
static jfieldID java_lang_Thread_priority;
static jfieldID java_lang_Thread_nativePeer;
+ static jfieldID java_lang_Thread_unparkedBeforeStart;
static jfieldID java_lang_ThreadGroup_groups;
static jfieldID java_lang_ThreadGroup_ngroups;
static jfieldID java_lang_ThreadGroup_mainThreadGroup;
diff --git a/simulator/Android.bp b/simulator/Android.bp
index 8690426..223c891 100644
--- a/simulator/Android.bp
+++ b/simulator/Android.bp
@@ -45,7 +45,7 @@
shared_libs: [
"libart",
"libartbase",
- "libvixl-arm64",
+ "libvixl",
],
}
@@ -58,7 +58,7 @@
shared_libs: [
"libartd",
"libartbased",
- "libvixld-arm64",
+ "libvixld",
],
}
diff --git a/test/004-ThreadStress/src-art/Main.java b/test/004-ThreadStress/src-art/Main.java
index 3a89f4f..e717852 100644
--- a/test/004-ThreadStress/src-art/Main.java
+++ b/test/004-ThreadStress/src-art/Main.java
@@ -26,6 +26,7 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Semaphore;
+import java.util.concurrent.locks.LockSupport;
// Run on host with:
// javac ThreadTest.java && java ThreadStress && rm *.class
@@ -52,6 +53,7 @@
// -sleep:X .......... frequency of Sleep (double)
// -wait:X ........... frequency of Wait (double)
// -timedwait:X ...... frequency of TimedWait (double)
+// -timedpark:X ...... frequency of TimedPark (double)
// -syncandwork:X .... frequency of SyncAndWork (double)
// -queuedwait:X ..... frequency of QueuedWait (double)
@@ -251,6 +253,18 @@
}
}
+ private final static class TimedPark extends Operation {
+ private final static int SLEEP_TIME = 100;
+
+ public TimedPark() {}
+
+ @Override
+ public boolean perform() {
+ LockSupport.parkNanos(this, 100*1000000);
+ return true;
+ }
+ }
+
private final static class SyncAndWork extends Operation {
private final Object lock;
@@ -320,7 +334,8 @@
frequencyMap.put(new NonMovingAlloc(), 0.025); // 5/200
frequencyMap.put(new StackTrace(), 0.1); // 20/200
frequencyMap.put(new Exit(), 0.225); // 45/200
- frequencyMap.put(new Sleep(), 0.125); // 25/200
+ frequencyMap.put(new Sleep(), 0.075); // 15/200
+ frequencyMap.put(new TimedPark(), 0.05); // 10/200
frequencyMap.put(new TimedWait(lock), 0.05); // 10/200
frequencyMap.put(new Wait(lock), 0.075); // 15/200
frequencyMap.put(new QueuedWait(semaphore), 0.05); // 10/200
@@ -341,9 +356,10 @@
private final static Map<Operation, Double> createLockFrequencyMap(Object lock) {
Map<Operation, Double> frequencyMap = new HashMap<Operation, Double>();
frequencyMap.put(new Sleep(), 0.2); // 40/200
- frequencyMap.put(new TimedWait(lock), 0.2); // 40/200
+ frequencyMap.put(new TimedWait(lock), 0.1); // 20/200
frequencyMap.put(new Wait(lock), 0.2); // 40/200
frequencyMap.put(new SyncAndWork(lock), 0.4); // 80/200
+ frequencyMap.put(new TimedPark(), 0.1); // 20/200
return frequencyMap;
}
@@ -389,6 +405,8 @@
op = new Wait(lock);
} else if (split[0].equals("-timedwait")) {
op = new TimedWait(lock);
+ } else if (split[0].equals("-timedpark")) {
+ op = new TimedPark();
} else if (split[0].equals("-syncandwork")) {
op = new SyncAndWork(lock);
} else if (split[0].equals("-queuedwait")) {
@@ -693,7 +711,7 @@
}
// The notifier thread is a daemon just loops forever to wake
- // up threads in operation Wait.
+ // up threads in operations Wait and Park.
if (lock != null) {
Thread notifier = new Thread("Notifier") {
public void run() {
@@ -701,6 +719,11 @@
synchronized (lock) {
lock.notifyAll();
}
+ for (Thread runner : runners) {
+ if (runner != null) {
+ LockSupport.unpark(runner);
+ }
+ }
}
}
};
diff --git a/test/004-UnsafeTest/src/Main.java b/test/004-UnsafeTest/src/Main.java
index d43d374..9176e89 100644
--- a/test/004-UnsafeTest/src/Main.java
+++ b/test/004-UnsafeTest/src/Main.java
@@ -32,6 +32,20 @@
}
}
+ private static void check(float actual, float expected, String msg) {
+ if (actual != expected) {
+ System.out.println(msg + " : " + actual + " != " + expected);
+ System.exit(1);
+ }
+ }
+
+ private static void check(double actual, double expected, String msg) {
+ if (actual != expected) {
+ System.out.println(msg + " : " + actual + " != " + expected);
+ System.exit(1);
+ }
+ }
+
private static void check(Object actual, Object expected, String msg) {
if (actual != expected) {
System.out.println(msg + " : " + actual + " != " + expected);
@@ -54,6 +68,7 @@
testArrayIndexScale(unsafe);
testGetAndPutAndCAS(unsafe);
testGetAndPutVolatile(unsafe);
+ testCopyMemoryPrimitiveArrays(unsafe);
}
private static void testArrayBaseOffset(Unsafe unsafe) {
@@ -237,6 +252,38 @@
"Unsafe.getObjectVolatile(Object, long)");
}
+ // Regression test for "copyMemory" operations hitting a DCHECK() for float/double arrays.
+ private static void testCopyMemoryPrimitiveArrays(Unsafe unsafe) {
+ int size = 4 * 1024;
+ long memory = unsafeTestMalloc(size);
+
+ int floatSize = 4;
+ float[] inputFloats = new float[size / floatSize];
+ for (int i = 0; i != inputFloats.length; ++i) {
+ inputFloats[i] = ((float)i) + 0.5f;
+ }
+ float[] outputFloats = new float[size / floatSize];
+ unsafe.copyMemoryFromPrimitiveArray(inputFloats, 0, memory, size);
+ unsafe.copyMemoryToPrimitiveArray(memory, outputFloats, 0, size);
+ for (int i = 0; i != inputFloats.length; ++i) {
+ check(inputFloats[i], outputFloats[i], "unsafe.copyMemory/float");
+ }
+
+ int doubleSize = 8;
+ double[] inputDoubles = new double[size / doubleSize];
+ for (int i = 0; i != inputDoubles.length; ++i) {
+ inputDoubles[i] = ((double)i) + 0.5;
+ }
+ double[] outputDoubles = new double[size / doubleSize];
+ unsafe.copyMemoryFromPrimitiveArray(inputDoubles, 0, memory, size);
+ unsafe.copyMemoryToPrimitiveArray(memory, outputDoubles, 0, size);
+ for (int i = 0; i != inputDoubles.length; ++i) {
+ check(inputDoubles[i], outputDoubles[i], "unsafe.copyMemory/double");
+ }
+
+ unsafeTestFree(memory);
+ }
+
private static class TestClass {
public int intVar = 0;
public long longVar = 0;
@@ -251,4 +298,6 @@
private static native int vmArrayBaseOffset(Class<?> clazz);
private static native int vmArrayIndexScale(Class<?> clazz);
+ private static native long unsafeTestMalloc(long size);
+ private static native void unsafeTestFree(long memory);
}
diff --git a/test/004-UnsafeTest/unsafe_test.cc b/test/004-UnsafeTest/unsafe_test.cc
index 18d9ea8..e970aaa 100644
--- a/test/004-UnsafeTest/unsafe_test.cc
+++ b/test/004-UnsafeTest/unsafe_test.cc
@@ -15,6 +15,7 @@
*/
#include "art_method-inl.h"
+#include "base/casts.h"
#include "jni.h"
#include "mirror/array.h"
#include "mirror/class-inl.h"
@@ -37,4 +38,16 @@
return Primitive::ComponentSize(klass->GetComponentType()->GetPrimitiveType());
}
+extern "C" JNIEXPORT jlong JNICALL Java_Main_unsafeTestMalloc(JNIEnv*, jclass, jlong size) {
+ void* memory = malloc(dchecked_integral_cast<size_t>(size));
+ CHECK(memory != nullptr);
+ return reinterpret_cast64<jlong>(memory);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_unsafeTestFree(JNIEnv*, jclass, jlong memory) {
+ void* mem = reinterpret_cast64<void*>(memory);
+ CHECK(mem != nullptr);
+ free(mem);
+}
+
} // namespace art
diff --git a/test/099-vmdebug/check b/test/099-vmdebug/check
index d124ce8..6a3fed5 100755
--- a/test/099-vmdebug/check
+++ b/test/099-vmdebug/check
@@ -15,6 +15,6 @@
# limitations under the License.
# Strip the process pids and line numbers from exact error messages.
-sed -e '/^dalvikvm\(\|32\|64\) E.*\] /d' "$2" > "$2.tmp"
+sed -e '/^.*dalvikvm\(\|32\|64\) E.*\] /d' "$2" > "$2.tmp"
diff --strip-trailing-cr -q "$1" "$2.tmp" >/dev/null
diff --git a/test/143-string-value/check b/test/143-string-value/check
index 2a3476c..b5e51ce 100755
--- a/test/143-string-value/check
+++ b/test/143-string-value/check
@@ -15,6 +15,6 @@
# limitations under the License.
# Strip error log messages.
-sed -e '/^dalvikvm\(\|32\|64\) E.*\] /d' "$2" > "$2.tmp"
+sed -e '/^.*dalvikvm\(\|32\|64\) E.*\] /d' "$2" > "$2.tmp"
diff --strip-trailing-cr -q "$1" "$2.tmp" >/dev/null
diff --git a/test/160-read-barrier-stress/src/Main.java b/test/160-read-barrier-stress/src/Main.java
index 5865094..5e49e66 100644
--- a/test/160-read-barrier-stress/src/Main.java
+++ b/test/160-read-barrier-stress/src/Main.java
@@ -121,6 +121,23 @@
assertSameObject(f4444, la[i4444]);
assertDifferentObject(f4999, la[i4998]);
assertSameObject(f4999, la[i4999]);
+
+ la = largeArray;
+ // Group the ArrayGets so they aren't divided by a function call; this will enable
+ // interm. address sharing for arm64.
+ Object tmp1 = la[i0];
+ Object tmp2 = la[i0 + 1];
+ Object tmp3 = la[i0 + 1024];
+ Object tmp4 = la[i0 + 4444];
+ Object tmp5 = la[i0 + 4998];
+ Object tmp6 = la[i0 + 4999];
+
+ assertSameObject(f0000, tmp1);
+ assertDifferentObject(f0000, tmp2);
+ assertSameObject(f1024, tmp3);
+ assertSameObject(f4444, tmp4);
+ assertDifferentObject(f4999, tmp5);
+ assertSameObject(f4999, tmp6);
}
}
diff --git a/test/1931-monitor-events/check b/test/1931-monitor-events/check
new file mode 100644
index 0000000..8a7f844
--- /dev/null
+++ b/test/1931-monitor-events/check
@@ -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.
+
+# Art sends events for park/unpark, and the RI doesn't. Remove it from the expected output.
+if [[ "$TEST_RUNTIME" == "jvm" ]]; then
+ patch -p0 expected.txt < jvm-expected.patch >/dev/null
+fi
+
+./default-check "$@"
diff --git a/test/1931-monitor-events/expected.txt b/test/1931-monitor-events/expected.txt
index 33a9bd3..f368ae2 100644
--- a/test/1931-monitor-events/expected.txt
+++ b/test/1931-monitor-events/expected.txt
@@ -1,6 +1,9 @@
Testing contended locking.
Locker thread 1 for NamedLock[Lock testLock] contended-LOCKING NamedLock[Lock testLock]
Locker thread 1 for NamedLock[Lock testLock] LOCKED NamedLock[Lock testLock]
+Testing park.
+ParkThread start-monitor-wait NamedLock[Parking blocker object] timeout: 1
+ParkThread monitor-waited NamedLock[Parking blocker object] timed_out: true
Testing monitor wait.
Locker thread 2 for NamedLock[Lock testWait] start-monitor-wait NamedLock[Lock testWait] timeout: 0
Locker thread 2 for NamedLock[Lock testWait] monitor-waited NamedLock[Lock testWait] timed_out: false
diff --git a/test/1931-monitor-events/jvm-expected.patch b/test/1931-monitor-events/jvm-expected.patch
new file mode 100644
index 0000000..7595b14
--- /dev/null
+++ b/test/1931-monitor-events/jvm-expected.patch
@@ -0,0 +1,3 @@
+5,6d4
+< ParkThread start-monitor-wait NamedLock[Parking blocker object] timeout: 1
+< ParkThread monitor-waited NamedLock[Parking blocker object] timed_out: true
diff --git a/test/1931-monitor-events/src/art/Test1931.java b/test/1931-monitor-events/src/art/Test1931.java
index ccefede..f549789 100644
--- a/test/1931-monitor-events/src/art/Test1931.java
+++ b/test/1931-monitor-events/src/art/Test1931.java
@@ -23,6 +23,7 @@
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.*;
+import java.util.concurrent.locks.LockSupport;
import java.util.ListIterator;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -67,6 +68,9 @@
System.out.println("Testing contended locking.");
testLock(new Monitors.NamedLock("Lock testLock"));
+ System.out.println("Testing park.");
+ testPark(new Monitors.NamedLock("Parking blocker object"));
+
System.out.println("Testing monitor wait.");
testWait(new Monitors.NamedLock("Lock testWait"));
@@ -88,6 +92,14 @@
testInteruptWait(new Monitors.NamedLock("Lock testInteruptWait"));
}
+ public static void testPark(Object blocker) throws Exception {
+ Thread holder = new Thread(() -> {
+ LockSupport.parkNanos(blocker, 10); // Should round up to one millisecond
+ }, "ParkThread");
+ holder.start();
+ holder.join();
+ }
+
public static void testInteruptWait(final Monitors.NamedLock lk) throws Exception {
final Monitors.LockController controller1 = new Monitors.LockController(lk);
controller1.DoLock();
diff --git a/test/1958-transform-try-jit/expected.txt b/test/1958-transform-try-jit/expected.txt
new file mode 100644
index 0000000..8cfaea2
--- /dev/null
+++ b/test/1958-transform-try-jit/expected.txt
@@ -0,0 +1,2 @@
+Before redefinition: hello
+After redefinition: Goodbye
diff --git a/test/1958-transform-try-jit/info.txt b/test/1958-transform-try-jit/info.txt
new file mode 100644
index 0000000..0bf5ee5
--- /dev/null
+++ b/test/1958-transform-try-jit/info.txt
@@ -0,0 +1,5 @@
+Tests that JVMTI transformation seems to work even when we try to get a method
+inlined by the jit.
+
+Note this test deliberately avoids any internal libart calls so it can be
+included in CTS.
diff --git a/test/1958-transform-try-jit/run b/test/1958-transform-try-jit/run
new file mode 100755
index 0000000..c6e62ae
--- /dev/null
+++ b/test/1958-transform-try-jit/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+./default-run "$@" --jvmti
diff --git a/test/1958-transform-try-jit/src/Main.java b/test/1958-transform-try-jit/src/Main.java
new file mode 100644
index 0000000..7f45c20
--- /dev/null
+++ b/test/1958-transform-try-jit/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.Test1958.run();
+ }
+}
diff --git a/test/1958-transform-try-jit/src/art/Redefinition.java b/test/1958-transform-try-jit/src/art/Redefinition.java
new file mode 100644
index 0000000..56d2938
--- /dev/null
+++ b/test/1958-transform-try-jit/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/1958-transform-try-jit/src/art/Test1958.java b/test/1958-transform-try-jit/src/art/Test1958.java
new file mode 100644
index 0000000..100daa1
--- /dev/null
+++ b/test/1958-transform-try-jit/src/art/Test1958.java
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+
+package art;
+
+import java.lang.reflect.Method;
+import java.util.Base64;
+
+public class Test1958 {
+ static class Runner {
+ public static String doSayHi() {
+ return sayHiHelper(true);
+ }
+
+ public static String dontSayHi() {
+ return sayHiHelper(false);
+ }
+
+ // We are trying to get the definition of Transform.sayHi inlined into this function.
+ public static String sayHiHelper(boolean sayHi) {
+ if (sayHi) {
+ return Transform.sayHi();
+ } else {
+ return "NOPE!";
+ }
+ }
+ }
+
+ static class Transform {
+ public static String sayHi() {
+ // Use lower 'h' to make sure the string will have a different string id
+ // than the transformation (the transformation code is the same except
+ // the actual printed String, which was making the test inacurately passing
+ // in JIT mode when loading the string from the dex cache, as the string ids
+ // of the two different strings were the same).
+ // We know the string ids will be different because lexicographically:
+ // "Goodbye" < "LTransform;" < "hello".
+ return "hello";
+ }
+ }
+
+ /**
+ * base64 encoded class/dex file for
+ * static class Transform {
+ * public static String sayHi() {
+ * return "Goodbye";
+ * }
+ * }
+ */
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADUAFQoABAANCAAOBwAQBwATAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1i" +
+ "ZXJUYWJsZQEABXNheUhpAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAApTb3VyY2VGaWxlAQANVGVz" +
+ "dDE5NTguamF2YQwABQAGAQAHR29vZGJ5ZQcAFAEAFmFydC9UZXN0MTk1OCRUcmFuc2Zvcm0BAAlU" +
+ "cmFuc2Zvcm0BAAxJbm5lckNsYXNzZXMBABBqYXZhL2xhbmcvT2JqZWN0AQAMYXJ0L1Rlc3QxOTU4" +
+ "ACAAAwAEAAAAAAACAAAABQAGAAEABwAAAB0AAQABAAAABSq3AAGxAAAAAQAIAAAABgABAAAABgAJ" +
+ "AAkACgABAAcAAAAbAAEAAAAAAAMSArAAAAABAAgAAAAGAAEAAAAIAAIACwAAAAIADAASAAAACgAB" +
+ "AAMADwARAAg=");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQCRmEaLPLzpKe+CHcDM8YhIJCPWwcFR5yegAwAAcAAAAHhWNBIAAAAAAAAAAPQCAAAR" +
+ "AAAAcAAAAAcAAAC0AAAAAgAAANAAAAAAAAAAAAAAAAMAAADoAAAAAQAAAAABAACAAgAAIAEAAFgB" +
+ "AABgAQAAaQEAAGwBAACGAQAAlgEAALoBAADaAQAA7gEAAAICAAARAgAAHAIAAB8CAAAsAgAAMgIA" +
+ "ADkCAABAAgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACwAAAAIAAAAFAAAAAAAAAAsAAAAGAAAA" +
+ "AAAAAAAAAQAAAAAAAAAAAA4AAAAEAAEAAAAAAAAAAAAAAAAABAAAAAAAAAAJAAAA5AIAAMYCAAAA" +
+ "AAAAAQAAAAAAAABUAQAAAwAAABoAAQARAAAAAQABAAEAAABQAQAABAAAAHAQAgAAAA4ABgAOAAgA" +
+ "DgAGPGluaXQ+AAdHb29kYnllAAFMABhMYXJ0L1Rlc3QxOTU4JFRyYW5zZm9ybTsADkxhcnQvVGVz" +
+ "dDE5NTg7ACJMZGFsdmlrL2Fubm90YXRpb24vRW5jbG9zaW5nQ2xhc3M7AB5MZGFsdmlrL2Fubm90" +
+ "YXRpb24vSW5uZXJDbGFzczsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7" +
+ "AA1UZXN0MTk1OC5qYXZhAAlUcmFuc2Zvcm0AAVYAC2FjY2Vzc0ZsYWdzAARuYW1lAAVzYXlIaQAF" +
+ "dmFsdWUAdX5+RDh7ImNvbXBpbGF0aW9uLW1vZGUiOiJkZWJ1ZyIsIm1pbi1hcGkiOjEsInNoYS0x" +
+ "IjoiNTFjYWNlMWFiZGQwOGIzMjBmMjVmYjgxMTZjMjQzMmIwMmYwOTI5NSIsInZlcnNpb24iOiIx" +
+ "LjQuNS1kZXYifQACAgEPGAECAwIMBAgNFwoAAAIAAICABLgCAQmgAgAAAAACAAAAtwIAAL0CAADY" +
+ "AgAAAAAAAAAAAAAAAAAADgAAAAAAAAABAAAAAAAAAAEAAAARAAAAcAAAAAIAAAAHAAAAtAAAAAMA" +
+ "AAACAAAA0AAAAAUAAAADAAAA6AAAAAYAAAABAAAAAAEAAAEgAAACAAAAIAEAAAMgAAACAAAAUAEA" +
+ "AAIgAAARAAAAWAEAAAQgAAACAAAAtwIAAAAgAAABAAAAxgIAAAMQAAACAAAA1AIAAAYgAAABAAAA" +
+ "5AIAAAAQAAABAAAA9AIAAA==");
+
+ public static void run() throws Exception {
+ Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+ Method doSayHi = Runner.class.getDeclaredMethod("doSayHi");
+ Method dontSayHi = Runner.class.getDeclaredMethod("dontSayHi");
+ // Run the method enough times that the jit thinks it's interesting (default 10000).
+ for (int i = 0; i < 20000; i++) {
+ doSayHi.invoke(null);
+ dontSayHi.invoke(null);
+ }
+ // Sleep for 10 seconds to let the jit finish any work it's doing.
+ Thread.sleep(10 * 1000);
+ // Check what we get right now.
+ System.out.println("Before redefinition: " + doSayHi.invoke(null));
+ Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ System.out.println("After redefinition: " + doSayHi.invoke(null));
+ }
+}
diff --git a/test/411-checker-hdiv-hrem-pow2/src/RemTest.java b/test/411-checker-hdiv-hrem-pow2/src/RemTest.java
index 72725c1..54d7847 100644
--- a/test/411-checker-hdiv-hrem-pow2/src/RemTest.java
+++ b/test/411-checker-hdiv-hrem-pow2/src/RemTest.java
@@ -92,6 +92,17 @@
/// CHECK: cmp w{{\d+}}, #0x0
/// CHECK: and w{{\d+}}, w{{\d+}}, #0x1
/// CHECK: cneg w{{\d+}}, w{{\d+}}, lt
+ /// CHECK-START-X86_64: java.lang.Integer RemTest.$noinline$IntMod2(int) disassembly (after)
+ /// CHECK: Rem [{{i\d+}},{{i\d+}}]
+ /// CHECK-NOT: imul
+ /// CHECK-NOT: shr
+ /// CHECK-NOT: imul
+ /// CHECK: mov
+ /// CHECK: and
+ /// CHECK: jz/eq
+ /// CHECK: lea
+ /// CHECK: test
+ /// CHECK: cmovl/nge
private static Integer $noinline$IntMod2(int v) {
int r = v % 2;
return r;
@@ -101,6 +112,17 @@
/// CHECK: cmp w{{\d+}}, #0x0
/// CHECK: and w{{\d+}}, w{{\d+}}, #0x1
/// CHECK: cneg w{{\d+}}, w{{\d+}}, lt
+ /// CHECK-START-X86_64: java.lang.Integer RemTest.$noinline$IntModMinus2(int) disassembly (after)
+ /// CHECK: Rem [{{i\d+}},{{i\d+}}]
+ /// CHECK-NOT: imul
+ /// CHECK-NOT: shr
+ /// CHECK-NOT: imul
+ /// CHECK: mov
+ /// CHECK: and
+ /// CHECK: jz/eq
+ /// CHECK: lea
+ /// CHECK: test
+ /// CHECK: cmovl/nge
private static Integer $noinline$IntModMinus2(int v) {
int r = v % -2;
return r;
@@ -111,6 +133,17 @@
/// CHECK: and w{{\d+}}, w{{\d+}}, #0xf
/// CHECK: and w{{\d+}}, w{{\d+}}, #0xf
/// CHECK: csneg w{{\d+}}, w{{\d+}}, mi
+ /// CHECK-START-X86_64: java.lang.Integer RemTest.$noinline$IntMod16(int) disassembly (after)
+ /// CHECK: Rem [{{i\d+}},{{i\d+}}]
+ /// CHECK-NOT: imul
+ /// CHECK-NOT: shr
+ /// CHECK-NOT: imul
+ /// CHECK: mov
+ /// CHECK: and
+ /// CHECK: jz/eq
+ /// CHECK: lea
+ /// CHECK: test
+ /// CHECK: cmovl/nge
private static Integer $noinline$IntMod16(int v) {
int r = v % 16;
return r;
@@ -121,6 +154,17 @@
/// CHECK: and w{{\d+}}, w{{\d+}}, #0xf
/// CHECK: and w{{\d+}}, w{{\d+}}, #0xf
/// CHECK: csneg w{{\d+}}, w{{\d+}}, mi
+ /// CHECK-START-X86_64: java.lang.Integer RemTest.$noinline$IntModMinus16(int) disassembly (after)
+ /// CHECK: Rem [{{i\d+}},{{i\d+}}]
+ /// CHECK-NOT: imul
+ /// CHECK-NOT: shr
+ /// CHECK-NOT: imul
+ /// CHECK: mov
+ /// CHECK: and
+ /// CHECK: jz/eq
+ /// CHECK: lea
+ /// CHECK: test
+ /// CHECK: cmovl/nge
private static Integer $noinline$IntModMinus16(int v) {
int r = v % -16;
return r;
@@ -131,6 +175,17 @@
/// CHECK: and w{{\d+}}, w{{\d+}}, #0x7fffffff
/// CHECK: and w{{\d+}}, w{{\d+}}, #0x7fffffff
/// CHECK: csneg w{{\d+}}, w{{\d+}}, mi
+ /// CHECK-START-X86_64: java.lang.Integer RemTest.$noinline$IntModIntMin(int) disassembly (after)
+ /// CHECK: Rem [{{i\d+}},{{i\d+}}]
+ /// CHECK-NOT: imul
+ /// CHECK-NOT: shr
+ /// CHECK-NOT: imul
+ /// CHECK: mov
+ /// CHECK: and
+ /// CHECK: jz/eq
+ /// CHECK: lea
+ /// CHECK: test
+ /// CHECK: cmovl/nge
private static Integer $noinline$IntModIntMin(int v) {
int r = v % Integer.MIN_VALUE;
return r;
@@ -211,6 +266,18 @@
/// CHECK: cmp x{{\d+}}, #0x0
/// CHECK: and x{{\d+}}, x{{\d+}}, #0x1
/// CHECK: cneg x{{\d+}}, x{{\d+}}, lt
+ /// CHECK-START-X86_64: java.lang.Long RemTest.$noinline$LongMod2(long) disassembly (after)
+ /// CHECK: Rem [{{j\d+}},{{j\d+}}]
+ /// CHECK-NOT: imul
+ /// CHECK-NOT: shrq
+ /// CHECK-NOT: imulq
+ /// CHECK: movq
+ /// CHECK: andq
+ /// CHECK: jz/eq
+ /// CHECK: movq
+ /// CHECK: sarq
+ /// CHECK: shlq
+ /// CHECK: orq
private static Long $noinline$LongMod2(long v) {
long r = v % 2;
return r;
@@ -220,6 +287,18 @@
/// CHECK: cmp x{{\d+}}, #0x0
/// CHECK: and x{{\d+}}, x{{\d+}}, #0x1
/// CHECK: cneg x{{\d+}}, x{{\d+}}, lt
+ /// CHECK-START-X86_64: java.lang.Long RemTest.$noinline$LongModMinus2(long) disassembly (after)
+ /// CHECK: Rem [{{j\d+}},{{j\d+}}]
+ /// CHECK-NOT: imul
+ /// CHECK-NOT: shrq
+ /// CHECK-NOT: imulq
+ /// CHECK: movq
+ /// CHECK: andq
+ /// CHECK: jz/eq
+ /// CHECK: movq
+ /// CHECK: sarq
+ /// CHECK: shlq
+ /// CHECK: orq
private static Long $noinline$LongModMinus2(long v) {
long r = v % -2;
return r;
@@ -230,6 +309,19 @@
/// CHECK: and x{{\d+}}, x{{\d+}}, #0xf
/// CHECK: and x{{\d+}}, x{{\d+}}, #0xf
/// CHECK: csneg x{{\d+}}, x{{\d+}}, mi
+
+ /// CHECK-START-X86_64: java.lang.Long RemTest.$noinline$LongMod16(long) disassembly (after)
+ /// CHECK: Rem [{{j\d+}},{{j\d+}}]
+ /// CHECK-NOT: imul
+ /// CHECK-NOT: shrq
+ /// CHECK-NOT: imulq
+ /// CHECK: movq
+ /// CHECK: andq
+ /// CHECK: jz/eq
+ /// CHECK: movq
+ /// CHECK: sarq
+ /// CHECK: shlq
+ /// CHECK: orq
private static Long $noinline$LongMod16(long v) {
long r = v % 16;
return r;
@@ -240,6 +332,18 @@
/// CHECK: and x{{\d+}}, x{{\d+}}, #0xf
/// CHECK: and x{{\d+}}, x{{\d+}}, #0xf
/// CHECK: csneg x{{\d+}}, x{{\d+}}, mi
+ /// CHECK-START-X86_64: java.lang.Long RemTest.$noinline$LongModMinus16(long) disassembly (after)
+ /// CHECK: Rem [{{j\d+}},{{j\d+}}]
+ /// CHECK-NOT: imul
+ /// CHECK-NOT: shrq
+ /// CHECK-NOT: imulq
+ /// CHECK: movq
+ /// CHECK: andq
+ /// CHECK: jz/eq
+ /// CHECK: movq
+ /// CHECK: sarq
+ /// CHECK: shlq
+ /// CHECK: orq
private static Long $noinline$LongModMinus16(long v) {
long r = v % -16;
return r;
@@ -250,6 +354,18 @@
/// CHECK: and x{{\d+}}, x{{\d+}}, #0x7fffffffffffffff
/// CHECK: and x{{\d+}}, x{{\d+}}, #0x7fffffffffffffff
/// CHECK: csneg x{{\d+}}, x{{\d+}}, mi
+ /// CHECK-START-X86_64: java.lang.Long RemTest.$noinline$LongModLongMin(long) disassembly (after)
+ /// CHECK: Rem [{{j\d+}},{{j\d+}}]
+ /// CHECK-NOT: imul
+ /// CHECK-NOT: shrq
+ /// CHECK-NOT: imulq
+ /// CHECK: movq
+ /// CHECK: andq
+ /// CHECK: jz/eq
+ /// CHECK: movq
+ /// CHECK: sarq
+ /// CHECK: shlq
+ /// CHECK: orq
private static Long $noinline$LongModLongMin(long v) {
long r = v % Long.MIN_VALUE;
return r;
diff --git a/test/527-checker-array-access-split/src/Main.java b/test/527-checker-array-access-split/src/Main.java
index f83c924..f39b5e2 100644
--- a/test/527-checker-array-access-split/src/Main.java
+++ b/test/527-checker-array-access-split/src/Main.java
@@ -572,6 +572,75 @@
buf1[end] = 'n';
}
+ //
+ // Check that IntermediateAddress can be shared for object ArrayGets.
+ //
+ /// CHECK-START-ARM64: int Main.checkObjectArrayGet(int, java.lang.Integer[], java.lang.Integer[]) instruction_simplifier_arm64 (before)
+ /// CHECK: <<Parameter:l\d+>> ParameterValue
+ /// CHECK: <<Array:l\d+>> NullCheck [<<Parameter>>]
+ /// CHECK: ArrayGet [<<Array>>,{{i\d+}}]
+ /// CHECK: ArrayGet [<<Array>>,{{i\d+}}]
+ /// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
+ /// CHECK: ArrayGet [<<Array>>,{{i\d+}}]
+ /// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
+ /// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
+
+ /// CHECK-START-ARM64: int Main.checkObjectArrayGet(int, java.lang.Integer[], java.lang.Integer[]) instruction_simplifier_arm64 (after)
+ /// CHECK: <<Parameter:l\d+>> ParameterValue
+ /// CHECK: <<DataOffset:i\d+>> IntConstant 12
+ /// CHECK: <<Array:l\d+>> NullCheck [<<Parameter>>]
+ /// CHECK: <<IntAddr1:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>]
+ /// CHECK: ArrayGet [<<IntAddr1>>,{{i\d+}}]
+ /// CHECK: <<IntAddr2:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>]
+ /// CHECK: ArrayGet [<<IntAddr2>>,{{i\d+}}]
+ /// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
+ /// CHECK: <<IntAddr3:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>]
+ /// CHECK: ArrayGet [<<IntAddr3>>,{{i\d+}}]
+ /// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
+ /// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
+ //
+ /// CHECK-NOT: IntermediateAddress
+
+ /// CHECK-START-ARM64: int Main.checkObjectArrayGet(int, java.lang.Integer[], java.lang.Integer[]) GVN$after_arch (after)
+ /// CHECK: <<Parameter:l\d+>> ParameterValue
+ /// CHECK: <<DataOffset:i\d+>> IntConstant 12
+ /// CHECK: <<Array:l\d+>> NullCheck [<<Parameter>>]
+ /// CHECK: <<IntAddr1:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>]
+ /// CHECK: ArrayGet [<<IntAddr1>>,{{i\d+}}]
+ /// CHECK: ArrayGet [<<IntAddr1>>,{{i\d+}}]
+ /// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
+ /// CHECK: <<IntAddr3:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>]
+ /// CHECK: ArrayGet [<<IntAddr3>>,{{i\d+}}]
+ /// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
+ /// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
+ //
+ /// CHECK-NOT: IntermediateAddress
+ public final static int checkObjectArrayGet(int index, Integer[] a, Integer[] b) {
+ Integer five = Integer.valueOf(5);
+ int tmp1 = a[index];
+ tmp1 += a[index + 1];
+ a[index + 1] = five;
+ tmp1 += a[index + 2];
+ a[index + 2] = five;
+ a[index + 3] = five;
+ return tmp1;
+ }
+
+ /// CHECK-START-ARM64: int Main.testIntAddressObjDisasm(java.lang.Integer[], int) disassembly (after)
+ /// CHECK: <<IntAddr:i\d+>> IntermediateAddress
+ /// CHECK: add w<<AddrReg:\d+>>, {{w\d+}}, #0xc
+ /// CHECK: ArrayGet [<<IntAddr>>,{{i\d+}}]
+ /// CHECK: ldr {{w\d+}}, [x<<AddrReg>>, x{{\d+}}, lsl #2]
+ /// CHECK: ArrayGet [<<IntAddr>>,{{i\d+}}]
+ /// CHECK: ldr {{w\d+}}, [x<<AddrReg>>, x{{\d+}}, lsl #2]
+
+ /// CHECK-START-ARM64: int Main.testIntAddressObjDisasm(java.lang.Integer[], int) disassembly (after)
+ /// CHECK: add {{w\d+}}, {{w\d+}}, #0xc
+ /// CHECK-NOT: add {{w\d+}}, {{w\d+}}, #0xc
+ private int testIntAddressObjDisasm(Integer[] obj, int x) {
+ return obj[x] + obj[x + 1];
+ }
+
public final static int ARRAY_SIZE = 128;
public static void main(String[] args) {
diff --git a/test/552-checker-x86-avx2-bit-manipulation/expected.txt b/test/552-checker-x86-avx2-bit-manipulation/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/552-checker-x86-avx2-bit-manipulation/expected.txt
diff --git a/test/552-checker-x86-avx2-bit-manipulation/info.txt b/test/552-checker-x86-avx2-bit-manipulation/info.txt
new file mode 100644
index 0000000..37bc6dd
--- /dev/null
+++ b/test/552-checker-x86-avx2-bit-manipulation/info.txt
@@ -0,0 +1 @@
+Tests for generating bit manipulation instructions on x86
diff --git a/test/552-checker-x86-avx2-bit-manipulation/src/Main.java b/test/552-checker-x86-avx2-bit-manipulation/src/Main.java
new file mode 100644
index 0000000..b8138dd
--- /dev/null
+++ b/test/552-checker-x86-avx2-bit-manipulation/src/Main.java
@@ -0,0 +1,225 @@
+/*
+ * 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 {
+
+ public static void assertIntEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ public static void assertLongEquals(long expected, long result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ /// CHECK-START-X86_64: long Main.and_not_64(long, long) instruction_simplifier_x86_64 (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: Not loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: And loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Not loop:none
+ /// CHECK-DAG: And loop:none
+
+ // TODO:re-enable when checker supports isa features
+ // CHECK-START-X86_64: long Main.and_not_64(long, long) instruction_simplifier_x86_64 (after)
+ // CHECK-DAG: X86AndNot loop:<<Loop:B\d+>> outer_loop:none
+ // CHECK-DAG: X86AndNot loop:none
+
+ // TODO:re-enable when checker supports isa features
+ // CHECK-START-X86_64: long Main.and_not_64(long, long) instruction_simplifier_x86_64 (after)
+ // CHECK-NOT: Not loop:<<Loop>> outer_loop:none
+ // CHECK-NOT: And loop:<<Loop>> outer_loop:none
+ // CHECK-NOT: Not loop:none
+ // CHECK-NOT: And loop:none
+ public static long and_not_64( long x, long y) {
+ long j = 1;
+ long k = 2;
+ for (long i = -64 ; i < 64; i++ ) {
+ x = x & ~i;
+ y = y | i;
+ }
+ return x & ~y;
+ }
+
+ /// CHECK-START-X86_64: int Main.and_not_32(int, int) instruction_simplifier_x86_64 (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: Not loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: And loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Not loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: And loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Not loop:none
+ /// CHECK-DAG: And loop:none
+
+ // TODO:re-enable when checker supports isa features
+ // CHECK-START-X86_64: int Main.and_not_32(int, int) instruction_simplifier_x86_64 (after)
+ // CHECK-DAG: X86AndNot loop:<<Loop:B\d+>> outer_loop:none
+ // CHECK-DAG: X86AndNot loop:<<Loop>> outer_loop:none
+ // CHECK-DAG: X86AndNot loop:none
+
+ // TODO:re-enable when checker supports isa features
+ // CHECK-START-X86_64: int Main.and_not_32(int, int) instruction_simplifier_x86_64 (after)
+ // CHECK-NOT: Not loop:<<Loop>> outer_loop:none
+ // CHECK-NOT: And loop:<<Loop>> outer_loop:none
+ // CHECK-NOT: Not loop:none
+ // CHECK-NOT: And loop:none
+ public static int and_not_32( int x, int y) {
+ int j = 1;
+ int k = 2;
+ for (int i = -64 ; i < 64; i++ ) {
+ x = x & ~i;
+ y = y | i;
+ }
+ return x & ~y;
+ }
+
+ /// CHECK-START-X86_64: int Main.reset_lowest_set_bit_32(int) instruction_simplifier_x86_64 (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: Add loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: And loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: And loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: And loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: And loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: And loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: And loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: And loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: And loop:<<Loop>> outer_loop:none
+
+
+ // TODO:re-enable when checker supports isa features
+ // CHECK-START-X86_64: int Main.reset_lowest_set_bit_32(int) instruction_simplifier_x86_64 (after)
+ // CHECK-DAG: X86MaskOrResetLeastSetBit loop:<<Loop:B\d+>> outer_loop:none
+ // CHECK-DAG: X86MaskOrResetLeastSetBit loop:<<Loop>> outer_loop:none
+ // CHECK-DAG: X86MaskOrResetLeastSetBit loop:<<Loop>> outer_loop:none
+ // CHECK-DAG: X86MaskOrResetLeastSetBit loop:<<Loop>> outer_loop:none
+ // CHECK-DAG: X86MaskOrResetLeastSetBit loop:<<Loop>> outer_loop:none
+ // CHECK-DAG: X86MaskOrResetLeastSetBit loop:<<Loop>> outer_loop:none
+ // CHECK-DAG: X86MaskOrResetLeastSetBit loop:<<Loop>> outer_loop:none
+ // CHECK-DAG: X86MaskOrResetLeastSetBit loop:<<Loop>> outer_loop:none
+
+ // TODO:re-enable when checker supports isa features
+ // CHECK-START-X86_64: int Main.reset_lowest_set_bit_32(int) instruction_simplifier_x86_64 (after)
+ // CHECK-NOT: And loop:<<Loop>> outer_loop:none
+ public static int reset_lowest_set_bit_32(int x) {
+ int y = x;
+ int j = 5;
+ int k = 10;
+ int l = 20;
+ for (int i = -64 ; i < 64; i++) {
+ y = i & i-1;
+ j += y;
+ j = j & j-1;
+ k +=j;
+ k = k & k-1;
+ l +=k;
+ l = l & l-1;
+ }
+ return y + j + k + l;
+ }
+
+ /// CHECK-START-X86_64: long Main.reset_lowest_set_bit_64(long) instruction_simplifier_x86_64 (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: Sub loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: And loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Sub loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: And loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Sub loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: And loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Sub loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: And loop:<<Loop>> outer_loop:none
+
+ // TODO:re-enable when checker supports isa features
+ // CHECK-START-X86_64: long Main.reset_lowest_set_bit_64(long) instruction_simplifier_x86_64 (after)
+ // CHECK-DAG: X86MaskOrResetLeastSetBit loop:<<Loop:B\d+>> outer_loop:none
+ // CHECK-DAG: X86MaskOrResetLeastSetBit loop:<<Loop>> outer_loop:none
+ // CHECK-DAG: X86MaskOrResetLeastSetBit loop:<<Loop>> outer_loop:none
+ // CHECK-DAG: X86MaskOrResetLeastSetBit loop:<<Loop>> outer_loop:none
+
+ // TODO:re-enable when checker supports isa features
+ // CHECK-START-X86_64: long Main.reset_lowest_set_bit_64(long) instruction_simplifier_x86_64 (after)
+ // CHECK-NOT: And loop:<<Loop>> outer_loop:none
+ // CHECK-NOT: Sub loop:<<Loop>> outer_loop:none
+ public static long reset_lowest_set_bit_64(long x) {
+ long y = x;
+ long j = 5;
+ long k = 10;
+ long l = 20;
+ for (long i = -64 ; i < 64; i++) {
+ y = i & i-1;
+ j += y;
+ j = j & j-1;
+ k +=j;
+ k = k & k-1;
+ l +=k;
+ l = l & l-1;
+ }
+ return y + j + k + l;
+ }
+
+ /// CHECK-START-X86_64: int Main.get_mask_lowest_set_bit_32(int) instruction_simplifier_x86_64 (before)
+ /// CHECK-DAG: Add loop:none
+ /// CHECK-DAG: Xor loop:none
+
+ // TODO:re-enable when checker supports isa features
+ // CHECK-START-X86_64: int Main.get_mask_lowest_set_bit_32(int) instruction_simplifier_x86_64 (after)
+ // CHECK-DAG: X86MaskOrResetLeastSetBit loop:none
+
+ // TODO:re-enable when checker supports isa features
+ // CHECK-START-X86_64: int Main.get_mask_lowest_set_bit_32(int) instruction_simplifier_x86_64 (after)
+ // CHECK-NOT: Add loop:none
+ // CHECK-NOT: Xor loop:none
+ public static int get_mask_lowest_set_bit_32(int x) {
+ return (x-1) ^ x;
+ }
+
+ /// CHECK-START-X86_64: long Main.get_mask_lowest_set_bit_64(long) instruction_simplifier_x86_64 (before)
+ /// CHECK-DAG: Sub loop:none
+ /// CHECK-DAG: Xor loop:none
+
+ // TODO:re-enable when checker supports isa features
+ // CHECK-START-X86_64: long Main.get_mask_lowest_set_bit_64(long) instruction_simplifier_x86_64 (after)
+ // CHECK-DAG: X86MaskOrResetLeastSetBit loop:none
+
+ // TODO:re-enable when checker supports isa features
+ // CHECK-START-X86_64: long Main.get_mask_lowest_set_bit_64(long) instruction_simplifier_x86_64 (after)
+ // CHECK-NOT: Sub loop:none
+ // CHECK-NOT: Xor loop:none
+ public static long get_mask_lowest_set_bit_64(long x) {
+ return (x-1) ^ x;
+ }
+
+ public static void main(String[] args) {
+ int x = 50;
+ int y = x/2;
+ long a = Long.MAX_VALUE;
+ long b = Long.MAX_VALUE/2;
+ assertIntEquals(0,and_not_32(x,y));
+ assertLongEquals(0L, and_not_64(a,b));
+ assertIntEquals(-20502606, reset_lowest_set_bit_32(x));
+ assertLongEquals(-20502606L, reset_lowest_set_bit_64(a));
+ assertLongEquals(-20502606L, reset_lowest_set_bit_64(b));
+ assertIntEquals(1, get_mask_lowest_set_bit_32(y));
+ assertLongEquals(1L, get_mask_lowest_set_bit_64(b));
+ }
+}
diff --git a/test/580-crc32/expected.txt b/test/580-crc32/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/580-crc32/expected.txt
diff --git a/test/580-crc32/info.txt b/test/580-crc32/info.txt
new file mode 100644
index 0000000..24f31e0
--- /dev/null
+++ b/test/580-crc32/info.txt
@@ -0,0 +1 @@
+This test case is used to test java.util.zip.CRC32.
diff --git a/test/580-crc32/src/Main.java b/test/580-crc32/src/Main.java
new file mode 100644
index 0000000..7fc1273
--- /dev/null
+++ b/test/580-crc32/src/Main.java
@@ -0,0 +1,131 @@
+/*
+ * 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.util.zip.CRC32;
+
+/**
+ * The ART compiler can use intrinsics for the java.util.zip.CRC32 method:
+ * private native static int update(int crc, int b)
+ *
+ * As the method is private it is not possible to check the use of intrinsics
+ * for it directly.
+ * The tests check that correct checksums are produced.
+ */
+public class Main {
+ private static CRC32 crc32 = new CRC32();
+
+ public Main() {
+ }
+
+ public static long TestInt(int value) {
+ crc32.reset();
+ crc32.update(value);
+ return crc32.getValue();
+ }
+
+ public static long TestInt(int... values) {
+ crc32.reset();
+ for (int value : values) {
+ crc32.update(value);
+ }
+ return crc32.getValue();
+ }
+
+ public static void assertEqual(long expected, long actual) {
+ if (expected != actual) {
+ throw new Error("Expected: " + expected + ", found: " + actual);
+ }
+ }
+
+ public static void main(String args[]) {
+ // public void update(int b)
+ //
+ // Tests for checksums of the byte 0x0
+ assertEqual(0xD202EF8DL, TestInt(0x0));
+ assertEqual(0xD202EF8DL, TestInt(0x0100));
+ assertEqual(0xD202EF8DL, TestInt(0x010000));
+ assertEqual(0xD202EF8DL, TestInt(0x01000000));
+ assertEqual(0xD202EF8DL, TestInt(0xff00));
+ assertEqual(0xD202EF8DL, TestInt(0xffff00));
+ assertEqual(0xD202EF8DL, TestInt(0xffffff00));
+ assertEqual(0xD202EF8DL, TestInt(0x1200));
+ assertEqual(0xD202EF8DL, TestInt(0x123400));
+ assertEqual(0xD202EF8DL, TestInt(0x12345600));
+ assertEqual(0xD202EF8DL, TestInt(Integer.MIN_VALUE));
+
+ // Tests for checksums of the byte 0x1
+ assertEqual(0xA505DF1BL, TestInt(0x1));
+ assertEqual(0xA505DF1BL, TestInt(0x0101));
+ assertEqual(0xA505DF1BL, TestInt(0x010001));
+ assertEqual(0xA505DF1BL, TestInt(0x01000001));
+ assertEqual(0xA505DF1BL, TestInt(0xff01));
+ assertEqual(0xA505DF1BL, TestInt(0xffff01));
+ assertEqual(0xA505DF1BL, TestInt(0xffffff01));
+ assertEqual(0xA505DF1BL, TestInt(0x1201));
+ assertEqual(0xA505DF1BL, TestInt(0x123401));
+ assertEqual(0xA505DF1BL, TestInt(0x12345601));
+
+ // Tests for checksums of the byte 0x0f
+ assertEqual(0x42BDF21CL, TestInt(0x0f));
+ assertEqual(0x42BDF21CL, TestInt(0x010f));
+ assertEqual(0x42BDF21CL, TestInt(0x01000f));
+ assertEqual(0x42BDF21CL, TestInt(0x0100000f));
+ assertEqual(0x42BDF21CL, TestInt(0xff0f));
+ assertEqual(0x42BDF21CL, TestInt(0xffff0f));
+ assertEqual(0x42BDF21CL, TestInt(0xffffff0f));
+ assertEqual(0x42BDF21CL, TestInt(0x120f));
+ assertEqual(0x42BDF21CL, TestInt(0x12340f));
+ assertEqual(0x42BDF21CL, TestInt(0x1234560f));
+
+ // Tests for checksums of the byte 0xff
+ assertEqual(0xFF000000L, TestInt(0x00ff));
+ assertEqual(0xFF000000L, TestInt(0x01ff));
+ assertEqual(0xFF000000L, TestInt(0x0100ff));
+ assertEqual(0xFF000000L, TestInt(0x010000ff));
+ assertEqual(0xFF000000L, TestInt(0x0000ffff));
+ assertEqual(0xFF000000L, TestInt(0x00ffffff));
+ assertEqual(0xFF000000L, TestInt(0xffffffff));
+ assertEqual(0xFF000000L, TestInt(0x12ff));
+ assertEqual(0xFF000000L, TestInt(0x1234ff));
+ assertEqual(0xFF000000L, TestInt(0x123456ff));
+ assertEqual(0xFF000000L, TestInt(Integer.MAX_VALUE));
+
+ // Tests for sequences
+ assertEqual(0xFF41D912L, TestInt(0, 0, 0));
+ assertEqual(0xFF41D912L, TestInt(0x0100, 0x010000, 0x01000000));
+ assertEqual(0xFF41D912L, TestInt(0xff00, 0xffff00, 0xffffff00));
+ assertEqual(0xFF41D912L, TestInt(0x1200, 0x123400, 0x12345600));
+
+ assertEqual(0x909FB2F2L, TestInt(1, 1, 1));
+ assertEqual(0x909FB2F2L, TestInt(0x0101, 0x010001, 0x01000001));
+ assertEqual(0x909FB2F2L, TestInt(0xff01, 0xffff01, 0xffffff01));
+ assertEqual(0x909FB2F2L, TestInt(0x1201, 0x123401, 0x12345601));
+
+ assertEqual(0xE33A9F71L, TestInt(0x0f, 0x0f, 0x0f));
+ assertEqual(0xE33A9F71L, TestInt(0x010f, 0x01000f, 0x0100000f));
+ assertEqual(0xE33A9F71L, TestInt(0xff0f, 0xffff0f, 0xffffff0f));
+ assertEqual(0xE33A9F71L, TestInt(0x120f, 0x12340f, 0x1234560f));
+
+ assertEqual(0xFFFFFF00L, TestInt(0x0ff, 0x0ff, 0x0ff));
+ assertEqual(0xFFFFFF00L, TestInt(0x01ff, 0x0100ff, 0x010000ff));
+ assertEqual(0xFFFFFF00L, TestInt(0x00ffff, 0x00ffffff, 0xffffffff));
+ assertEqual(0xFFFFFF00L, TestInt(0x12ff, 0x1234ff, 0x123456ff));
+
+ assertEqual(0xB6CC4292L, TestInt(0x01, 0x02));
+
+ assertEqual(0xB2DE047CL, TestInt(0x0, -1, Integer.MIN_VALUE, Integer.MAX_VALUE));
+ }
+}
diff --git a/test/624-checker-stringops/smali/Smali.smali b/test/624-checker-stringops/smali/Smali.smali
index 8600a0a..f8b9275 100644
--- a/test/624-checker-stringops/smali/Smali.smali
+++ b/test/624-checker-stringops/smali/Smali.smali
@@ -34,7 +34,7 @@
invoke-virtual {v0, v1}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer;
move-result-object v1
- const-string v2, "x"
+ const-string v2, "y"
invoke-virtual {v1, v2}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer;
move-result-object v1
@@ -70,7 +70,7 @@
invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v1
- const-string v2, "x"
+ const-string v2, "y"
invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v1
diff --git a/test/624-checker-stringops/src/Main.java b/test/624-checker-stringops/src/Main.java
index 3aa6e56..f52d81a 100644
--- a/test/624-checker-stringops/src/Main.java
+++ b/test/624-checker-stringops/src/Main.java
@@ -120,7 +120,7 @@
/// CHECK-DAG: InvokeVirtual [<<New>>] intrinsic:StringBufferLength
static int bufferLen2() {
StringBuffer s = new StringBuffer();
- return s.append("x").append("x").length();
+ return s.append("x").append("y").length();
}
static int bufferLen2Smali() throws Exception {
@@ -150,7 +150,7 @@
/// CHECK-DAG: InvokeVirtual [<<New>>] intrinsic:StringBuilderLength
static int builderLen2() {
StringBuilder s = new StringBuilder();
- return s.append("x").append("x").length();
+ return s.append("x").append("y").length();
}
static int builderLen2Smali() throws Exception {
diff --git a/test/674-hiddenapi/hiddenapi.cc b/test/674-hiddenapi/hiddenapi.cc
index 96754c3..8e3e4eb 100644
--- a/test/674-hiddenapi/hiddenapi.cc
+++ b/test/674-hiddenapi/hiddenapi.cc
@@ -28,9 +28,9 @@
extern "C" JNIEXPORT void JNICALL Java_Main_init(JNIEnv*, jclass) {
Runtime* runtime = Runtime::Current();
- runtime->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kBlacklistOnly);
+ runtime->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
+ runtime->SetTargetSdkVersion(static_cast<int32_t>(hiddenapi::detail::SdkCodes::kVersionO_MR1));
runtime->SetDedupeHiddenApiWarnings(false);
- runtime->AlwaysSetHiddenApiWarningFlag();
}
extern "C" JNIEXPORT void JNICALL Java_Main_appendToBootClassLoader(
@@ -287,13 +287,5 @@
return static_cast<jint>(kAccHiddenApiBits);
}
-extern "C" JNIEXPORT jboolean JNICALL Java_ChildClass_hasPendingWarning(JNIEnv*, jclass) {
- return Runtime::Current()->HasPendingHiddenApiWarning();
-}
-
-extern "C" JNIEXPORT void JNICALL Java_ChildClass_clearWarning(JNIEnv*, jclass) {
- Runtime::Current()->SetPendingHiddenApiWarning(false);
-}
-
} // namespace Test674HiddenApi
} // namespace art
diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java
index db3ba6d..3427b8e 100644
--- a/test/674-hiddenapi/src-ex/ChildClass.java
+++ b/test/674-hiddenapi/src-ex/ChildClass.java
@@ -98,10 +98,8 @@
expected = Behaviour.Granted;
} else if (hiddenness == Hiddenness.Blacklist) {
expected = Behaviour.Denied;
- } else if (isDebuggable) {
- expected = Behaviour.Warning;
} else {
- expected = Behaviour.Granted;
+ expected = Behaviour.Warning;
}
for (boolean isStatic : booleanValues) {
@@ -145,7 +143,7 @@
}
private static void checkMemberCallback(Class<?> klass, String name,
- boolean isPublic, boolean isField) {
+ boolean isPublic, boolean isField, boolean expectedCallback) {
try {
RecordingConsumer consumer = new RecordingConsumer();
VMRuntime.setNonSdkApiUsageConsumer(consumer);
@@ -168,8 +166,14 @@
// only interested in whether the callback is invoked.
}
- if (consumer.recordedValue == null || !consumer.recordedValue.contains(name)) {
- throw new RuntimeException("No callback for member: " + name);
+ boolean actualCallback = consumer.recordedValue != null &&
+ consumer.recordedValue.contains(name);
+ if (expectedCallback != actualCallback) {
+ if (expectedCallback) {
+ throw new RuntimeException("Expected callback for member: " + name);
+ } else {
+ throw new RuntimeException("Did not expect callback for member: " + name);
+ }
}
} finally {
VMRuntime.setNonSdkApiUsageConsumer(null);
@@ -181,7 +185,7 @@
boolean isPublic = (visibility == Visibility.Public);
boolean canDiscover = (behaviour != Behaviour.Denied);
- boolean setsWarning = (behaviour == Behaviour.Warning);
+ boolean invokesMemberCallback = (behaviour != Behaviour.Granted);
if (klass.isInterface() && (!isStatic || !isPublic)) {
// Interfaces only have public static fields.
@@ -243,8 +247,6 @@
canDiscover);
}
- // Finish here if we could not discover the field.
-
if (canDiscover) {
// Test that modifiers are unaffected.
@@ -254,44 +256,22 @@
// Test getters and setters when meaningful.
- clearWarning();
if (!Reflection.canGetField(klass, name)) {
throwAccessException(klass, name, true, "Field.getInt()");
}
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, name, true, "Field.getInt()", setsWarning);
- }
-
- clearWarning();
if (!Reflection.canSetField(klass, name)) {
throwAccessException(klass, name, true, "Field.setInt()");
}
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, name, true, "Field.setInt()", setsWarning);
- }
-
- clearWarning();
if (!JNI.canGetField(klass, name, isStatic)) {
throwAccessException(klass, name, true, "getIntField");
}
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, name, true, "getIntField", setsWarning);
- }
-
- clearWarning();
if (!JNI.canSetField(klass, name, isStatic)) {
throwAccessException(klass, name, true, "setIntField");
}
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, name, true, "setIntField", setsWarning);
- }
}
// Test that callbacks are invoked correctly.
- clearWarning();
- if (setsWarning || !canDiscover) {
- checkMemberCallback(klass, name, isPublic, true /* isField */);
- }
+ checkMemberCallback(klass, name, isPublic, true /* isField */, invokesMemberCallback);
}
private static void checkMethod(Class<?> klass, String name, boolean isStatic,
@@ -304,7 +284,7 @@
}
boolean canDiscover = (behaviour != Behaviour.Denied);
- boolean setsWarning = (behaviour == Behaviour.Warning);
+ boolean invokesMemberCallback = (behaviour != Behaviour.Granted);
// Test discovery with reflection.
@@ -354,39 +334,21 @@
}
// Test whether we can invoke the method. This skips non-static interface methods.
-
if (!klass.isInterface() || isStatic) {
- clearWarning();
if (!Reflection.canInvokeMethod(klass, name)) {
throwAccessException(klass, name, false, "invoke()");
}
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, name, false, "invoke()", setsWarning);
- }
-
- clearWarning();
if (!JNI.canInvokeMethodA(klass, name, isStatic)) {
throwAccessException(klass, name, false, "CallMethodA");
}
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, name, false, "CallMethodA()", setsWarning);
- }
-
- clearWarning();
if (!JNI.canInvokeMethodV(klass, name, isStatic)) {
throwAccessException(klass, name, false, "CallMethodV");
}
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, name, false, "CallMethodV()", setsWarning);
- }
}
}
// Test that callbacks are invoked correctly.
- clearWarning();
- if (setsWarning || !canDiscover) {
- checkMemberCallback(klass, name, isPublic, false /* isField */);
- }
+ checkMemberCallback(klass, name, isPublic, false /* isField */, invokesMemberCallback);
}
private static void checkConstructor(Class<?> klass, Visibility visibility, Hiddenness hiddenness,
@@ -403,7 +365,6 @@
MethodType methodType = MethodType.methodType(void.class, args);
boolean canDiscover = (behaviour != Behaviour.Denied);
- boolean setsWarning = (behaviour == Behaviour.Warning);
// Test discovery with reflection.
@@ -446,70 +407,41 @@
canDiscover);
}
- // Finish here if we could not discover the constructor.
+ if (canDiscover) {
+ // Test whether we can invoke the constructor.
- if (!canDiscover) {
- return;
- }
-
- // Test whether we can invoke the constructor.
-
- clearWarning();
- if (!Reflection.canInvokeConstructor(klass, args, initargs)) {
- throwAccessException(klass, fullName, false, "invoke()");
- }
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, fullName, false, "invoke()", setsWarning);
- }
-
- clearWarning();
- if (!JNI.canInvokeConstructorA(klass, signature)) {
- throwAccessException(klass, fullName, false, "NewObjectA");
- }
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, fullName, false, "NewObjectA", setsWarning);
- }
-
- clearWarning();
- if (!JNI.canInvokeConstructorV(klass, signature)) {
- throwAccessException(klass, fullName, false, "NewObjectV");
- }
- if (hasPendingWarning() != setsWarning) {
- throwWarningException(klass, fullName, false, "NewObjectV", setsWarning);
+ if (!Reflection.canInvokeConstructor(klass, args, initargs)) {
+ throwAccessException(klass, fullName, false, "invoke()");
+ }
+ if (!JNI.canInvokeConstructorA(klass, signature)) {
+ throwAccessException(klass, fullName, false, "NewObjectA");
+ }
+ if (!JNI.canInvokeConstructorV(klass, signature)) {
+ throwAccessException(klass, fullName, false, "NewObjectV");
+ }
}
}
private static void checkNullaryConstructor(Class<?> klass, Behaviour behaviour)
throws Exception {
boolean canAccess = (behaviour != Behaviour.Denied);
- boolean setsWarning = (behaviour == Behaviour.Warning);
- clearWarning();
if (Reflection.canUseNewInstance(klass) != canAccess) {
throw new RuntimeException("Expected to " + (canAccess ? "" : "not ") +
"be able to construct " + klass.getName() + ". " +
"isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
}
- if (canAccess && hasPendingWarning() != setsWarning) {
- throwWarningException(klass, "nullary constructor", false, "newInstance", setsWarning);
- }
}
private static void checkLinking(String className, boolean takesParameter, Behaviour behaviour)
throws Exception {
boolean canAccess = (behaviour != Behaviour.Denied);
- boolean setsWarning = (behaviour == Behaviour.Warning);
- clearWarning();
if (Linking.canAccess(className, takesParameter) != canAccess) {
throw new RuntimeException("Expected to " + (canAccess ? "" : "not ") +
"be able to verify " + className + "." +
"isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
}
- if (canAccess && hasPendingWarning() != setsWarning) {
- throwWarningException(
- Class.forName(className), "access", false, "static linking", setsWarning);
- }
}
private static void throwDiscoveryException(Class<?> klass, String name, boolean isField,
@@ -528,15 +460,6 @@
"everythingWhitelisted = " + everythingWhitelisted);
}
- private static void throwWarningException(Class<?> klass, String name, boolean isField,
- String fn, boolean setsWarning) {
- throw new RuntimeException("Expected access to " + (isField ? "field " : "method ") +
- klass.getName() + "." + name + " using " + fn + " to " + (setsWarning ? "" : "not ") +
- "set the warning flag. " +
- "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot + ", " +
- "everythingWhitelisted = " + everythingWhitelisted);
- }
-
private static void throwModifiersException(Class<?> klass, String name, boolean isField) {
throw new RuntimeException("Expected " + (isField ? "field " : "method ") + klass.getName() +
"." + name + " to not expose hidden modifiers");
@@ -545,7 +468,4 @@
private static boolean isParentInBoot;
private static boolean isChildInBoot;
private static boolean everythingWhitelisted;
-
- private static native boolean hasPendingWarning();
- private static native void clearWarning();
}
diff --git a/test/911-get-stack-trace/expected.txt b/test/911-get-stack-trace/expected.txt
index b0a400a..8dd49aa 100644
--- a/test/911-get-stack-trace/expected.txt
+++ b/test/911-get-stack-trace/expected.txt
@@ -21,7 +21,7 @@
baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
bar (IIILart/ControlData;)J 0 26
foo (IIILart/ControlData;)I 0 21
- doTest ()V 33 25
+ doTest ()V 31 25
run ()V 0 25
---------
print (Ljava/lang/Thread;II)V 0 38
@@ -41,7 +41,7 @@
baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
bar (IIILart/ControlData;)J 0 26
foo (IIILart/ControlData;)I 0 21
- doTest ()V 37 26
+ doTest ()V 35 26
run ()V 0 25
---------
getStackTrace (Ljava/lang/Thread;II)[[Ljava/lang/String; -1 -2
@@ -62,7 +62,7 @@
baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
bar (IIILart/ControlData;)J 0 26
foo (IIILart/ControlData;)I 0 21
- doTest ()V 60 32
+ doTest ()V 58 32
run ()V 0 25
---------
bar (IIILart/ControlData;)J 0 26
@@ -388,7 +388,7 @@
Test911
getAllStackTraces (I)[[Ljava/lang/Object; -1 -2
printAll (I)V 0 75
- doTest ()V 120 59
+ doTest ()V 118 59
run ()V 24 37
---------
@@ -643,7 +643,7 @@
Test911
getAllStackTraces (I)[[Ljava/lang/Object; -1 -2
printAll (I)V 0 75
- doTest ()V 125 61
+ doTest ()V 123 61
run ()V 24 37
---------
@@ -675,7 +675,7 @@
Test911
getThreadListStackTraces ([Ljava/lang/Thread;I)[[Ljava/lang/Object; -1 -2
printList ([Ljava/lang/Thread;I)V 0 68
- doTest ()V 110 54
+ doTest ()V 108 54
run ()V 32 41
---------
@@ -732,7 +732,7 @@
Test911
getThreadListStackTraces ([Ljava/lang/Thread;I)[[Ljava/lang/Object; -1 -2
printList ([Ljava/lang/Thread;I)V 0 68
- doTest ()V 115 56
+ doTest ()V 113 56
run ()V 32 41
---------
@@ -857,7 +857,7 @@
4
JVMTI_ERROR_ILLEGAL_ARGUMENT
[public static native java.lang.Object[] art.Frames.getFrameLocation(java.lang.Thread,int), ffffffff]
-[public static void art.Frames.doTestSameThread(), 40]
+[public static void art.Frames.doTestSameThread(), 3e]
[public static void art.Frames.doTest() throws java.lang.Exception, 0]
[public void art.Test911$1.run(), 28]
JVMTI_ERROR_NO_MORE_FRAMES
diff --git a/test/911-get-stack-trace/src/art/PrintThread.java b/test/911-get-stack-trace/src/art/PrintThread.java
index d8b3cbc..798db06 100644
--- a/test/911-get-stack-trace/src/art/PrintThread.java
+++ b/test/911-get-stack-trace/src/art/PrintThread.java
@@ -42,7 +42,7 @@
// may not exist depending on the environment.
public final static String IGNORE_THREAD_NAME_REGEX =
"Binder:|RenderThread|hwuiTask|Jit thread pool worker|Instr:|JDWP|Profile Saver|main|" +
- "queued-work-looper|InstrumentationConnectionThread";
+ "queued-work-looper|InstrumentationConnectionThread|intel_svc_streamer_thread";
public final static Matcher IGNORE_THREADS =
Pattern.compile(IGNORE_THREAD_NAME_REGEX).matcher("");
diff --git a/test/913-heaps/expected.txt b/test/913-heaps/expected.txt
index 065b854..1bd56d1 100644
--- a/test/913-heaps/expected.txt
+++ b/test/913-heaps/expected.txt
@@ -1,9 +1,9 @@
---
true true
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1]
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1]
+root@root --(thread)--> 3000@0 [size=124, length=-1]
1001@0 --(superclass)--> 1000@0 [size=123456780000, length=-1]
1002@0 --(interface)--> 2001@0 [size=123456780004, length=-1]
1002@0 --(superclass)--> 1001@0 [size=123456780001, length=-1]
@@ -44,14 +44,14 @@
---
root@root --(jni-global)--> 1@1000 [size=16, length=-1]
root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1]
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1]
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 8])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 20])--> 1@1000 [size=16, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1]
root@root --(thread)--> 1@1000 [size=16, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(thread)--> 3000@0 [size=124, length=-1]
1001@0 --(superclass)--> 1000@0 [size=123456780005, length=-1]
1002@0 --(interface)--> 2001@0 [size=123456780009, length=-1]
1002@0 --(superclass)--> 1001@0 [size=123456780006, length=-1]
@@ -90,18 +90,18 @@
5@1002 --(field@9)--> 6@1000 [size=16, length=-1]
6@1000 --(class)--> 1000@0 [size=123456780005, length=-1]
---
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(thread)--> 3000@0 [size=124, length=-1]
---
3@1001 --(class)--> 1001@0 [size=123456780011, length=-1]
---
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(thread)--> 3000@0 [size=124, length=-1]
---
3@1001 --(class)--> 1001@0 [size=123456780016, length=-1]
---
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1]
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1]
+root@root --(thread)--> 3000@0 [size=124, length=-1]
---
1001@0 --(superclass)--> 1000@0 [size=123456780020, length=-1]
3@1001 --(class)--> 1001@0 [size=123456780021, length=-1]
@@ -110,14 +110,14 @@
---
root@root --(jni-global)--> 1@1000 [size=16, length=-1]
root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1]
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1]
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 8])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 20])--> 1@1000 [size=16, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1]
root@root --(thread)--> 1@1000 [size=16, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(thread)--> 3000@0 [size=124, length=-1]
---
1001@0 --(superclass)--> 1000@0 [size=123456780025, length=-1]
3@1001 --(class)--> 1001@0 [size=123456780026, length=-1]
@@ -198,10 +198,10 @@
---
---
---- untagged objects
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1]
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1]
+root@root --(thread)--> 3000@0 [size=124, length=-1]
1001@0 --(superclass)--> 1000@0 [size=123456780050, length=-1]
1002@0 --(interface)--> 2001@0 [size=123456780054, length=-1]
1002@0 --(superclass)--> 1001@0 [size=123456780051, length=-1]
@@ -242,14 +242,14 @@
---
root@root --(jni-global)--> 1@1000 [size=16, length=-1]
root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1]
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1]
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 8])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 20])--> 1@1000 [size=16, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1]
root@root --(thread)--> 1@1000 [size=16, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(thread)--> 3000@0 [size=124, length=-1]
1001@0 --(superclass)--> 1000@0 [size=123456780055, length=-1]
1002@0 --(interface)--> 2001@0 [size=123456780059, length=-1]
1002@0 --(superclass)--> 1001@0 [size=123456780056, length=-1]
@@ -289,9 +289,9 @@
6@1000 --(class)--> 1000@0 [size=123456780055, length=-1]
---
---- tagged classes
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1]
+root@root --(thread)--> 3000@0 [size=124, length=-1]
1001@0 --(superclass)--> 1000@0 [size=123456780060, length=-1]
1002@0 --(interface)--> 2001@0 [size=123456780064, length=-1]
1002@0 --(superclass)--> 1001@0 [size=123456780061, length=-1]
@@ -316,9 +316,9 @@
5@1002 --(field@8)--> 500@0 [size=20, length=2]
6@1000 --(class)--> 1000@0 [size=123456780060, length=-1]
---
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=132, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=132, length=-1]
-root@root --(thread)--> 3000@0 [size=132, length=-1]
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=124, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=124, length=-1]
+root@root --(thread)--> 3000@0 [size=124, length=-1]
1001@0 --(superclass)--> 1000@0 [size=123456780065, length=-1]
1002@0 --(interface)--> 2001@0 [size=123456780069, length=-1]
1002@0 --(superclass)--> 1001@0 [size=123456780066, length=-1]
diff --git a/test/Android.bp b/test/Android.bp
index 561f95e..d85e2a6 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -59,8 +59,7 @@
shared_libs: [
"libartd",
"libartd-disassembler",
- "libvixld-arm",
- "libvixld-arm64",
+ "libvixld",
"libart-gtest",
"libdexfiled",
"libprofiled",
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 64c1d4f..148aea4 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -59,6 +59,7 @@
ART_TEST_HOST_RUN_TEST_DEPENDENCIES := \
$(ART_HOST_EXECUTABLES) \
$(HOST_OUT_EXECUTABLES)/hprof-conv \
+ $(HOST_OUT_EXECUTABLES)/timeout_dumper \
$(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libtiagent) \
$(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libtiagentd) \
$(OUT_DIR)/$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libtistress) \
diff --git a/test/VerifySoftFailDuringClinit/ClassToInitialize.smali b/test/VerifySoftFailDuringClinit/ClassToInitialize.smali
new file mode 100644
index 0000000..0d12ec8
--- /dev/null
+++ b/test/VerifySoftFailDuringClinit/ClassToInitialize.smali
@@ -0,0 +1,22 @@
+# 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.
+
+.class public LClassToInitialize;
+.super Ljava/lang/Object;
+
+.method public static constructor <clinit>()V
+ .registers 0
+ invoke-static {}, LVerifySoftFail;->empty()V
+ return-void
+.end method
diff --git a/test/VerifySoftFailDuringClinit/VerifySoftFail.smali b/test/VerifySoftFailDuringClinit/VerifySoftFail.smali
new file mode 100644
index 0000000..e0f4946
--- /dev/null
+++ b/test/VerifySoftFailDuringClinit/VerifySoftFail.smali
@@ -0,0 +1,27 @@
+# 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.
+
+.class public LVerifySoftFail;
+.super Ljava/lang/Object;
+
+.method public static empty()V
+ .registers 0
+ return-void
+.end method
+
+.method public static softFail()V
+ .registers 0
+ invoke-static {}, LMissingClass;->test()V
+ return-void
+.end method
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 900b1d7..6488d24 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -31,6 +31,8 @@
GDB_SERVER="gdbserver"
HAVE_IMAGE="y"
HOST="n"
+BIONIC="n"
+CREATE_ANDROID_ROOT="n"
INTERPRETER="n"
JIT="n"
INVOKE_WITH=""
@@ -209,6 +211,13 @@
HOST="y"
ANDROID_ROOT="$ANDROID_HOST_OUT"
shift
+ elif [ "x$1" = "x--bionic" ]; then
+ BIONIC="y"
+ # We need to create an ANDROID_ROOT because currently we cannot create
+ # the frameworks/libcore with linux_bionic so we need to use the normal
+ # host ones which are in a different location.
+ CREATE_ANDROID_ROOT="y"
+ shift
elif [ "x$1" = "x--no-prebuild" ]; then
PREBUILD="n"
shift
@@ -374,6 +383,10 @@
done
fi
+if [ "$CREATE_ANDROID_ROOT" = "y" ]; then
+ ANDROID_ROOT=$DEX_LOCATION/android-root
+fi
+
if [ "x$1" = "x" ] ; then
MAIN="Main"
else
@@ -644,6 +657,15 @@
fi
fi
+if [ "$BIONIC" = "y" ]; then
+ # This is the location that soong drops linux_bionic builds. Despite being
+ # called linux_bionic-x86 the build is actually amd64 (x86_64) only.
+ if [ ! -e "$OUT_DIR/soong/host/linux_bionic-x86" ]; then
+ echo "linux_bionic-x86 target doesn't seem to have been built!" >&2
+ exit 1
+ fi
+fi
+
# Prevent test from silently falling back to interpreter in no-prebuild mode. This happens
# when DEX_LOCATION path is too long, because vdex/odex filename is constructed by taking
# full path to dex, stripping leading '/', appending '@classes.vdex' and changing every
@@ -671,6 +693,21 @@
mkdir_locations="${DEX_LOCATION}/dalvik-cache/$ISA"
strip_cmdline="true"
sync_cmdline="true"
+linkroot_cmdline="true"
+linkroot_overlay_cmdline="true"
+
+linkdirs() {
+ find "$1" -maxdepth 1 -mindepth 1 -type d | xargs -i ln -sf '{}' "$2"
+}
+
+if [ "$CREATE_ANDROID_ROOT" = "y" ]; then
+ mkdir_locations="${mkdir_locations} ${ANDROID_ROOT}"
+ linkroot_cmdline="linkdirs ${ANDROID_HOST_OUT} ${ANDROID_ROOT}"
+ if [ "${BIONIC}" = "y" ]; then
+ # TODO Make this overlay more generic.
+ linkroot_overlay_cmdline="linkdirs $OUT_DIR/soong/host/linux_bionic-x86 ${ANDROID_ROOT}"
+ fi
+fi
# PROFILE takes precedence over RANDOM_PROFILE, since PROFILE tests require a
# specific profile to run properly.
@@ -942,14 +979,15 @@
# Note: We first send SIGRTMIN+2 (usually 36) to ART, which will induce a full thread dump
# before abort. However, dumping threads might deadlock, so we also use the "-k"
# option to definitely kill the child.
- cmdline="timeout -k 120s -s SIGRTMIN+2 ${TIME_OUT_VALUE}s $cmdline"
+ cmdline="timeout -k 120s -s SIGRTMIN+2 ${TIME_OUT_VALUE}s timeout_dumper $cmdline"
fi
if [ "$DEV_MODE" = "y" ]; then
for var in ANDROID_PRINTF_LOG ANDROID_DATA ANDROID_ROOT LD_LIBRARY_PATH DYLD_LIBRARY_PATH PATH LD_USE_LOAD_BIAS; do
echo EXPORT $var=${!var}
done
- echo "mkdir -p ${mkdir_locations} && $profman_cmdline && $dex2oat_cmdline && $dm_cmdline && $vdex_cmdline && $strip_cmdline && $sync_cmdline && $cmdline"
+ echo "$(declare -f linkdirs)"
+ echo "mkdir -p ${mkdir_locations} && $linkroot_cmdline && $linkroot_overlay_cmdline && $profman_cmdline && $dex2oat_cmdline && $dm_cmdline && $vdex_cmdline && $strip_cmdline && $sync_cmdline && $cmdline"
fi
cd $ANDROID_BUILD_TOP
@@ -963,6 +1001,8 @@
export ASAN_OPTIONS=$RUN_TEST_ASAN_OPTIONS
mkdir -p ${mkdir_locations} || exit 1
+ $linkroot_cmdline || { echo "create symlink android-root failed." >&2 ; exit 2; }
+ $linkroot_overlay_cmdline || { echo "overlay android-root failed." >&2 ; exit 2; }
$profman_cmdline || { echo "Profman failed." >&2 ; exit 2; }
$dex2oat_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
$dm_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
diff --git a/test/run-test b/test/run-test
index 229e201..2363152 100755
--- a/test/run-test
+++ b/test/run-test
@@ -88,6 +88,8 @@
export ANDROID_HOST_OUT=${OUT_DIR}/host/linux-x86
fi
+host_lib_root=${ANDROID_HOST_OUT}
+
# Allow changing DESUGAR script to something else, or to disable it with DESUGAR=false.
if [ -z "$DESUGAR" ]; then
export DESUGAR="$ANDROID_BUILD_TOP/art/tools/desugar.sh"
@@ -385,6 +387,14 @@
run_args="${run_args} --64"
suffix64="64"
shift
+ elif [ "x$1" = "x--bionic" ]; then
+ # soong linux_bionic builds are 64bit only.
+ run_args="${run_args} --bionic --host --64"
+ suffix64="64"
+ target_mode="no"
+ DEX_LOCATION=$tmp_dir
+ host_lib_root=$OUT_DIR/soong/host/linux_bionic-x86
+ shift
elif [ "x$1" = "x--trace" ]; then
trace="true"
shift
@@ -584,7 +594,7 @@
if [ "$target_mode" = "no" ]; then
guess_host_arch_name
run_args="${run_args} --boot ${ANDROID_HOST_OUT}/framework/core${image_suffix}.art"
- run_args="${run_args} --runtime-option -Djava.library.path=${ANDROID_HOST_OUT}/lib${suffix64}:${ANDROID_HOST_OUT}/nativetest${suffix64}"
+ run_args="${run_args} --runtime-option -Djava.library.path=${host_lib_root}/lib${suffix64}:${host_lib_root}/nativetest${suffix64}"
else
guess_target_arch_name
run_args="${run_args} --runtime-option -Djava.library.path=/data/nativetest${suffix64}/art/${target_arch_name}"
@@ -711,6 +721,7 @@
echo " --output-path [path] Location where to store the build" \
"files."
echo " --64 Run the test in 64-bit mode"
+ echo " --bionic Use the (host, 64-bit only) linux_bionic libc runtime"
echo " --trace Run with method tracing"
echo " --strace Run with syscall tracing from strace."
echo " --stream Run method tracing in streaming mode (requires --trace)"
diff --git a/test/testrunner/run_build_test_target.py b/test/testrunner/run_build_test_target.py
index 14287b0..b4a4ada 100755
--- a/test/testrunner/run_build_test_target.py
+++ b/test/testrunner/run_build_test_target.py
@@ -62,6 +62,18 @@
print(custom_env)
os.environ.update(custom_env)
+# build is just a binary/script that is directly executed to build any artifacts needed for the
+# test.
+if 'build' in target:
+ build_command = target.get('build').format(
+ ANDROID_BUILD_TOP = env.ANDROID_BUILD_TOP,
+ MAKE_OPTIONS='DX= -j{threads}'.format(threads = n_threads))
+ sys.stdout.write(str(build_command) + '\n')
+ sys.stdout.flush()
+ if subprocess.call(build_command.split()):
+ sys.exit(1)
+
+# make runs soong/kati to build the target listed in the entry.
if 'make' in target:
build_command = 'build/soong/soong_ui.bash --make-mode'
build_command += ' DX='
@@ -107,7 +119,8 @@
run_test_command += ['--host']
run_test_command += ['--dex2oat-jobs']
run_test_command += ['4']
- run_test_command += ['-b']
+ if '--no-build-dependencies' not in test_flags:
+ run_test_command += ['-b']
run_test_command += ['--verbose']
sys.stdout.write(str(run_test_command) + '\n')
diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py
index 978e9cb3..077129f 100644
--- a/test/testrunner/target_config.py
+++ b/test/testrunner/target_config.py
@@ -40,6 +40,12 @@
'art-interpreter' : {
'run-test' : ['--interpreter']
},
+ 'art-interpreter-cxx' : {
+ 'run-test' : ['--interpreter'],
+ 'env' : {
+ 'ART_USE_CXX_INTERPRETER' : 'true'
+ }
+ },
'art-interpreter-access-checks' : {
'run-test' : ['--interp-ac']
},
@@ -323,4 +329,11 @@
'art-golem-linux-x64': {
'golem' : 'linux-x64'
},
+ 'art-linux-bionic-x64': {
+ 'build': '{ANDROID_BUILD_TOP}/art/tools/build_linux_bionic_tests.sh {MAKE_OPTIONS}',
+ 'run-test': ['--run-test-option=--bionic',
+ '--host',
+ '--64',
+ '--no-build-dependencies'],
+ },
}
diff --git a/tools/build_linux_bionic_tests.sh b/tools/build_linux_bionic_tests.sh
new file mode 100755
index 0000000..dba2d50
--- /dev/null
+++ b/tools/build_linux_bionic_tests.sh
@@ -0,0 +1,101 @@
+#!/bin/bash
+#
+# 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.
+
+
+if [[ -z $ANDROID_BUILD_TOP ]]; then
+ pushd .
+else
+ pushd $ANDROID_BUILD_TOP
+fi
+
+if [ ! -d art ]; then
+ echo "Script needs to be run at the root of the android tree"
+ exit 1
+fi
+
+source build/envsetup.sh >&/dev/null # for get_build_var
+
+out_dir=$(get_build_var OUT_DIR)
+
+# TODO(b/31559095) Figure out a better way to do this.
+#
+# There is no good way to force soong to generate host-bionic builds currently
+# so this is a hacky workaround.
+
+# First build all the targets still in .mk files (also build normal glibc host
+# targets so we know what's needed to run the tests).
+build/soong/soong_ui.bash --make-mode "$@" test-art-host-run-test-dependencies build-art-host-tests
+if [ $? != 0 ]; then
+ exit 1
+fi
+
+tmp_soong_var=$(mktemp --tmpdir soong.variables.bak.XXXXXX)
+
+echo "Saving soong.variables to " $tmp_soong_var
+cat $out_dir/soong/soong.variables > ${tmp_soong_var}
+python3 <<END - ${tmp_soong_var} ${out_dir}/soong/soong.variables
+import json
+import sys
+x = json.load(open(sys.argv[1]))
+x['Allow_missing_dependencies'] = True
+x['HostArch'] = 'x86_64'
+x['CrossHost'] = 'linux_bionic'
+x['CrossHostArch'] = 'x86_64'
+if 'CrossHostSecondaryArch' in x:
+ del x['CrossHostSecondaryArch']
+json.dump(x, open(sys.argv[2], mode='w'))
+END
+if [ $? != 0 ]; then
+ mv $tmp_soong_var $out_dir/soong/soong.variables
+ exit 2
+fi
+
+soong_out=$out_dir/soong/host/linux_bionic-x86
+declare -a bionic_targets
+# These are the binaries actually used in tests. Since some of the files are
+# java targets or 32 bit we cannot just do the same find for the bin files.
+#
+# We look at what the earlier build generated to figure out what to ask soong to
+# build since we cannot use the .mk defined phony targets.
+bionic_targets=(
+ $soong_out/bin/dalvikvm
+ $soong_out/bin/dalvikvm64
+ $soong_out/bin/dex2oat
+ $soong_out/bin/dex2oatd
+ $soong_out/bin/profman
+ $soong_out/bin/profmand
+ $soong_out/bin/hiddenapi
+ $soong_out/bin/hprof-conv
+ $(find $ANDROID_HOST_OUT/lib64 -type f | sed "s:$ANDROID_HOST_OUT:$soong_out:g")
+ $(find $ANDROID_HOST_OUT/nativetest64 -type f | sed "s:$ANDROID_HOST_OUT:$soong_out:g"))
+
+echo building ${bionic_targets[*]}
+
+build/soong/soong_ui.bash --make-mode --skip-make "$@" ${bionic_targets[*]}
+ret=$?
+
+mv $tmp_soong_var $out_dir/soong/soong.variables
+
+# Having built with host-bionic confuses soong somewhat by making it think the
+# linux_bionic targets are needed for art phony targets like
+# test-art-host-run-test-dependencies. To work around this blow away all
+# ninja files in OUT_DIR. The build system is smart enough to not need to
+# rebuild stuff so this should be fine.
+rm -f $OUT_DIR/*.ninja $OUT_DIR/soong/*.ninja
+
+popd
+
+exit $ret
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index 3d70087..da463d5 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -61,7 +61,7 @@
done
# Allow to build successfully in master-art.
-extra_args=SOONG_ALLOW_MISSING_DEPENDENCIES=true
+extra_args="SOONG_ALLOW_MISSING_DEPENDENCIES=true TEMPORARY_DISABLE_PATH_RESTRICTIONS=true"
if [[ $mode == "host" ]]; then
make_command="make $j_arg $extra_args $showcommands build-art-host-tests $common_targets"
@@ -81,6 +81,8 @@
# These targets are needed for the chroot environment.
make_command+=" crash_dump event-log-tags"
fi
+ # Build the Runtime APEX.
+ make_command+=" com.android.runtime"
mode_suffix="-target"
fi
@@ -92,4 +94,5 @@
echo "Executing $make_command"
+# Disable path restrictions to enable luci builds using vpython.
bash -c "$make_command"
diff --git a/tools/hiddenapi/hiddenapi.cc b/tools/hiddenapi/hiddenapi.cc
index f61b3e8..65a4945 100644
--- a/tools/hiddenapi/hiddenapi.cc
+++ b/tools/hiddenapi/hiddenapi.cc
@@ -813,6 +813,7 @@
// Create a new MapItem entry with new MapList details.
DexFile::MapItem new_item;
new_item.type_ = old_item.type_;
+ new_item.unused_ = 0u; // initialize to ensure dex output is deterministic (b/119308882)
new_item.size_ = old_item.size_;
new_item.offset_ = new_map_offset;
diff --git a/tools/libcore_gcstress_failures.txt b/tools/libcore_gcstress_failures.txt
index fff1c70..eec45fa 100644
--- a/tools/libcore_gcstress_failures.txt
+++ b/tools/libcore_gcstress_failures.txt
@@ -27,12 +27,14 @@
description: "Timeouts.",
result: EXEC_FAILED,
modes: [device],
- names: ["libcore.java.lang.StringTest#testFastPathString_wellFormedUtf8Sequence",
+ names: ["jsr166.TimeUnitTest#testConvert",
+ "libcore.java.lang.StringTest#testFastPathString_wellFormedUtf8Sequence",
+ "libcore.java.text.DecimalFormatTest#testCurrencySymbolSpacing",
+ "libcore.java.text.SimpleDateFormatTest#testLocales",
"org.apache.harmony.tests.java.lang.ref.ReferenceQueueTest#test_remove",
"org.apache.harmony.tests.java.text.DateFormatTest#test_getAvailableLocales",
+ "org.apache.harmony.tests.java.lang.String2Test#test_getBytes",
"org.apache.harmony.tests.java.util.TimerTest#testOverdueTaskExecutesImmediately",
- "org.apache.harmony.tests.java.util.WeakHashMapTest#test_keySet_hasNext",
- "libcore.java.text.DecimalFormatTest#testCurrencySymbolSpacing",
- "libcore.java.text.SimpleDateFormatTest#testLocales"]
+ "org.apache.harmony.tests.java.util.WeakHashMapTest#test_keySet_hasNext"]
}
]
diff --git a/tools/timeout_dumper/Android.bp b/tools/timeout_dumper/Android.bp
new file mode 100644
index 0000000..dfd5442
--- /dev/null
+++ b/tools/timeout_dumper/Android.bp
@@ -0,0 +1,39 @@
+//
+// 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.
+//
+
+art_cc_binary {
+ name: "timeout_dumper",
+
+ host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+ device_supported: false,
+
+ defaults: ["art_defaults"],
+
+ srcs: ["timeout_dumper.cc"],
+
+ shared_libs: [
+ "libbacktrace",
+ "libbase",
+ ],
+ sanitize: {
+ address: true,
+ },
+}
diff --git a/tools/timeout_dumper/timeout_dumper.cc b/tools/timeout_dumper/timeout_dumper.cc
new file mode 100644
index 0000000..30e194c
--- /dev/null
+++ b/tools/timeout_dumper/timeout_dumper.cc
@@ -0,0 +1,609 @@
+/*
+ * 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.
+ */
+
+#include <dirent.h>
+#include <poll.h>
+#include <sys/prctl.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <csignal>
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+#include <thread>
+#include <memory>
+#include <set>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+namespace art {
+namespace {
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+constexpr bool kUseAddr2line = true;
+
+namespace timeout_signal {
+
+class SignalSet {
+ public:
+ SignalSet() {
+ if (sigemptyset(&set_) == -1) {
+ PLOG(FATAL) << "sigemptyset failed";
+ }
+ }
+
+ void Add(int signal) {
+ if (sigaddset(&set_, signal) == -1) {
+ PLOG(FATAL) << "sigaddset " << signal << " failed";
+ }
+ }
+
+ void Block() {
+ if (pthread_sigmask(SIG_BLOCK, &set_, nullptr) != 0) {
+ PLOG(FATAL) << "pthread_sigmask failed";
+ }
+ }
+
+ int Wait() {
+ // Sleep in sigwait() until a signal arrives. gdb causes EINTR failures.
+ int signal_number;
+ int rc = TEMP_FAILURE_RETRY(sigwait(&set_, &signal_number));
+ if (rc != 0) {
+ PLOG(FATAL) << "sigwait failed";
+ }
+ return signal_number;
+ }
+
+ private:
+ sigset_t set_;
+};
+
+int GetTimeoutSignal() {
+ return SIGRTMIN + 2;
+}
+
+} // namespace timeout_signal
+
+namespace addr2line {
+
+constexpr const char* kAddr2linePath =
+ "/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/bin/x86_64-linux-addr2line";
+
+std::unique_ptr<std::string> FindAddr2line() {
+ const char* env_value = getenv("ANDROID_BUILD_TOP");
+ if (env_value != nullptr) {
+ std::string path = std::string(env_value) + kAddr2linePath;
+ if (access(path.c_str(), X_OK) == 0) {
+ return std::make_unique<std::string>(path);
+ }
+ }
+
+ std::string path = std::string(".") + kAddr2linePath;
+ if (access(path.c_str(), X_OK) == 0) {
+ return std::make_unique<std::string>(path);
+ }
+
+ constexpr const char* kHostAddr2line = "/usr/bin/addr2line";
+ if (access(kHostAddr2line, F_OK) == 0) {
+ return std::make_unique<std::string>(kHostAddr2line);
+ }
+
+ return nullptr;
+}
+
+// The state of an open pipe to addr2line. In "server" mode, addr2line takes input on stdin
+// and prints the result to stdout. This struct keeps the state of the open connection.
+struct Addr2linePipe {
+ Addr2linePipe(int in_fd, int out_fd, const std::string& file_name, pid_t pid)
+ : in(in_fd), out(out_fd), file(file_name), child_pid(pid), odd(true) {}
+
+ ~Addr2linePipe() {
+ kill(child_pid, SIGKILL);
+ }
+
+ unique_fd in; // The file descriptor that is connected to the output of addr2line.
+ unique_fd out; // The file descriptor that is connected to the input of addr2line.
+
+ const std::string file; // The file addr2line is working on, so that we know when to close
+ // and restart.
+ const pid_t child_pid; // The pid of the child, which we should kill when we're done.
+ bool odd; // Print state for indentation of lines.
+};
+
+std::unique_ptr<Addr2linePipe> Connect(const std::string& name, const char* args[]) {
+ int caller_to_addr2line[2];
+ int addr2line_to_caller[2];
+
+ if (pipe(caller_to_addr2line) == -1) {
+ return nullptr;
+ }
+ if (pipe(addr2line_to_caller) == -1) {
+ close(caller_to_addr2line[0]);
+ close(caller_to_addr2line[1]);
+ return nullptr;
+ }
+
+ pid_t pid = fork();
+ if (pid == -1) {
+ close(caller_to_addr2line[0]);
+ close(caller_to_addr2line[1]);
+ close(addr2line_to_caller[0]);
+ close(addr2line_to_caller[1]);
+ return nullptr;
+ }
+
+ if (pid == 0) {
+ dup2(caller_to_addr2line[0], STDIN_FILENO);
+ dup2(addr2line_to_caller[1], STDOUT_FILENO);
+
+ close(caller_to_addr2line[0]);
+ close(caller_to_addr2line[1]);
+ close(addr2line_to_caller[0]);
+ close(addr2line_to_caller[1]);
+
+ execv(args[0], const_cast<char* const*>(args));
+ exit(1);
+ } else {
+ close(caller_to_addr2line[0]);
+ close(addr2line_to_caller[1]);
+ return std::make_unique<Addr2linePipe>(addr2line_to_caller[0],
+ caller_to_addr2line[1],
+ name,
+ pid);
+ }
+}
+
+void WritePrefix(std::ostream& os, const char* prefix, bool odd) {
+ if (prefix != nullptr) {
+ os << prefix;
+ }
+ os << " ";
+ if (!odd) {
+ os << " ";
+ }
+}
+
+void Drain(size_t expected,
+ const char* prefix,
+ std::unique_ptr<Addr2linePipe>* pipe /* inout */,
+ std::ostream& os) {
+ DCHECK(pipe != nullptr);
+ DCHECK(pipe->get() != nullptr);
+ int in = pipe->get()->in.get();
+ DCHECK_GE(in, 0);
+
+ bool prefix_written = false;
+
+ for (;;) {
+ constexpr uint32_t kWaitTimeExpectedMilli = 500;
+ constexpr uint32_t kWaitTimeUnexpectedMilli = 50;
+
+ int timeout = expected > 0 ? kWaitTimeExpectedMilli : kWaitTimeUnexpectedMilli;
+ struct pollfd read_fd{in, POLLIN, 0};
+ int retval = TEMP_FAILURE_RETRY(poll(&read_fd, 1, timeout));
+ if (retval == -1) {
+ // An error occurred.
+ pipe->reset();
+ return;
+ }
+
+ if (retval == 0) {
+ // Timeout.
+ return;
+ }
+
+ if (!(read_fd.revents & POLLIN)) {
+ // addr2line call exited.
+ pipe->reset();
+ return;
+ }
+
+ constexpr size_t kMaxBuffer = 128; // Relatively small buffer. Should be OK as we're on an
+ // alt stack, but just to be sure...
+ char buffer[kMaxBuffer];
+ memset(buffer, 0, kMaxBuffer);
+ int bytes_read = TEMP_FAILURE_RETRY(read(in, buffer, kMaxBuffer - 1));
+ if (bytes_read <= 0) {
+ // This should not really happen...
+ pipe->reset();
+ return;
+ }
+ buffer[bytes_read] = '\0';
+
+ char* tmp = buffer;
+ while (*tmp != 0) {
+ if (!prefix_written) {
+ WritePrefix(os, prefix, (*pipe)->odd);
+ prefix_written = true;
+ }
+ char* new_line = strchr(tmp, '\n');
+ if (new_line == nullptr) {
+ os << tmp;
+
+ break;
+ } else {
+ os << std::string(tmp, new_line - tmp + 1);
+
+ tmp = new_line + 1;
+ prefix_written = false;
+ (*pipe)->odd = !(*pipe)->odd;
+
+ if (expected > 0) {
+ expected--;
+ }
+ }
+ }
+ }
+}
+
+void Addr2line(const std::string& addr2line,
+ const std::string& map_src,
+ uintptr_t offset,
+ std::ostream& os,
+ const char* prefix,
+ std::unique_ptr<Addr2linePipe>* pipe /* inout */) {
+ DCHECK(pipe != nullptr);
+
+ if (map_src == "[vdso]" || android::base::EndsWith(map_src, ".vdex")) {
+ // addr2line will not work on the vdso.
+ // vdex files are special frames injected for the interpreter
+ // so they don't have any line number information available.
+ return;
+ }
+
+ if (*pipe == nullptr || (*pipe)->file != map_src) {
+ if (*pipe != nullptr) {
+ Drain(0, prefix, pipe, os);
+ }
+ pipe->reset(); // Close early.
+
+ const char* args[] = {
+ addr2line.c_str(),
+ "--functions",
+ "--inlines",
+ "--demangle",
+ "-e",
+ map_src.c_str(),
+ nullptr
+ };
+ *pipe = Connect(map_src, args);
+ }
+
+ Addr2linePipe* pipe_ptr = pipe->get();
+ if (pipe_ptr == nullptr) {
+ // Failed...
+ return;
+ }
+
+ // Send the offset.
+ const std::string hex_offset = StringPrintf("%zx\n", offset);
+
+ if (!android::base::WriteFully(pipe_ptr->out.get(), hex_offset.data(), hex_offset.length())) {
+ // Error. :-(
+ pipe->reset();
+ return;
+ }
+
+ // Now drain (expecting two lines).
+ Drain(2U, prefix, pipe, os);
+}
+
+} // namespace addr2line
+
+namespace ptrace {
+
+std::set<pid_t> PtraceSiblings(pid_t pid) {
+ std::set<pid_t> ret;
+ std::string task_path = android::base::StringPrintf("/proc/%d/task", pid);
+
+ std::unique_ptr<DIR, int (*)(DIR*)> d(opendir(task_path.c_str()), closedir);
+
+ // Bail early if the task directory cannot be opened.
+ if (d == nullptr) {
+ PLOG(ERROR) << "Failed to scan task folder";
+ return ret;
+ }
+
+ struct dirent* de;
+ while ((de = readdir(d.get())) != nullptr) {
+ // Ignore "." and "..".
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
+ continue;
+ }
+
+ char* end;
+ pid_t tid = strtoul(de->d_name, &end, 10);
+ if (*end) {
+ continue;
+ }
+
+ if (tid == pid) {
+ continue;
+ }
+
+ if (::ptrace(PTRACE_ATTACH, tid, 0, 0) != 0) {
+ PLOG(ERROR) << "Failed to attach to tid " << tid;
+ continue;
+ }
+
+ ret.insert(tid);
+ }
+ return ret;
+}
+
+} // namespace ptrace
+
+template <typename T>
+bool WaitLoop(uint32_t max_wait_micros, const T& handler) {
+ constexpr uint32_t kWaitMicros = 10;
+ const size_t kMaxLoopCount = max_wait_micros / kWaitMicros;
+
+ for (size_t loop_count = 1; loop_count <= kMaxLoopCount; ++loop_count) {
+ bool ret;
+ if (handler(&ret)) {
+ return ret;
+ }
+ usleep(kWaitMicros);
+ }
+ return false;
+}
+
+bool WaitForMainSigStop(const std::atomic<bool>& saw_wif_stopped_for_main) {
+ auto handler = [&](bool* res) {
+ if (saw_wif_stopped_for_main) {
+ *res = true;
+ return true;
+ }
+ return false;
+ };
+ constexpr uint32_t kMaxWaitMicros = 30 * 1000 * 1000; // 30s wait.
+ return WaitLoop(kMaxWaitMicros, handler);
+}
+
+bool WaitForSigStopped(pid_t pid, uint32_t max_wait_micros) {
+ auto handler = [&](bool* res) {
+ int status;
+ pid_t rc = TEMP_FAILURE_RETRY(waitpid(pid, &status, WNOHANG));
+ if (rc == -1) {
+ PLOG(ERROR) << "Failed to waitpid for " << pid;
+ *res = false;
+ return true;
+ }
+ if (rc == pid) {
+ if (!(WIFSTOPPED(status))) {
+ LOG(ERROR) << "Did not get expected stopped signal for " << pid;
+ *res = false;
+ } else {
+ *res = true;
+ }
+ return true;
+ }
+ return false;
+ };
+ return WaitLoop(max_wait_micros, handler);
+}
+
+#ifdef __LP64__
+constexpr bool kIs64Bit = true;
+#else
+constexpr bool kIs64Bit = false;
+#endif
+
+void DumpThread(pid_t pid,
+ pid_t tid,
+ const std::string* addr2line_path,
+ const char* prefix,
+ BacktraceMap* map) {
+ // Use std::cerr to avoid the LOG prefix.
+ std::cerr << std::endl << "=== pid: " << pid << " tid: " << tid << " ===" << std::endl;
+
+ constexpr uint32_t kMaxWaitMicros = 1000 * 1000; // 1s.
+ if (pid != tid && !WaitForSigStopped(tid, kMaxWaitMicros)) {
+ LOG(ERROR) << "Failed to wait for sigstop on " << tid;
+ }
+
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map));
+ if (backtrace == nullptr) {
+ LOG(ERROR) << prefix << "(failed to create Backtrace for thread " << tid << ")";
+ return;
+ }
+ backtrace->SetSkipFrames(0);
+ if (!backtrace->Unwind(0, nullptr)) {
+ LOG(ERROR) << prefix << "(backtrace::Unwind failed for thread " << tid
+ << ": " << backtrace->GetErrorString(backtrace->GetError()) << ")";
+ return;
+ }
+ if (backtrace->NumFrames() == 0) {
+ LOG(ERROR) << prefix << "(no native stack frames for thread " << tid << ")";
+ return;
+ }
+
+ std::unique_ptr<addr2line::Addr2linePipe> addr2line_state;
+
+ for (Backtrace::const_iterator it = backtrace->begin();
+ it != backtrace->end(); ++it) {
+ std::ostringstream oss;
+ oss << prefix << StringPrintf("#%02zu pc ", it->num);
+ bool try_addr2line = false;
+ if (!BacktraceMap::IsValid(it->map)) {
+ oss << StringPrintf(kIs64Bit ? "%016" PRIx64 " ???" : "%08" PRIx64 " ???", it->pc);
+ } else {
+ oss << StringPrintf(kIs64Bit ? "%016" PRIx64 " " : "%08" PRIx64 " ", it->rel_pc);
+ if (it->map.name.empty()) {
+ oss << StringPrintf("<anonymous:%" PRIx64 ">", it->map.start);
+ } else {
+ oss << it->map.name;
+ }
+ if (it->map.offset != 0) {
+ oss << StringPrintf(" (offset %" PRIx64 ")", it->map.offset);
+ }
+ oss << " (";
+ if (!it->func_name.empty()) {
+ oss << it->func_name;
+ if (it->func_offset != 0) {
+ oss << "+" << it->func_offset;
+ }
+ // Functions found using the gdb jit interface will be in an empty
+ // map that cannot be found using addr2line.
+ if (!it->map.name.empty()) {
+ try_addr2line = true;
+ }
+ } else {
+ oss << "???";
+ }
+ oss << ")";
+ }
+ std::cerr << oss.str() << std::endl;
+ if (try_addr2line && addr2line_path != nullptr) {
+ addr2line::Addr2line(*addr2line_path,
+ it->map.name,
+ it->rel_pc,
+ std::cerr,
+ prefix,
+ &addr2line_state);
+ }
+ }
+
+ if (addr2line_state != nullptr) {
+ addr2line::Drain(0, prefix, &addr2line_state, std::cerr);
+ }
+}
+
+void DumpProcess(pid_t forked_pid, const std::atomic<bool>& saw_wif_stopped_for_main) {
+ CHECK_EQ(0, ::ptrace(PTRACE_ATTACH, forked_pid, 0, 0));
+ std::set<pid_t> tids = ptrace::PtraceSiblings(forked_pid);
+ tids.insert(forked_pid);
+
+ // Check whether we have and should use addr2line.
+ std::unique_ptr<std::string> addr2line_path = addr2line::FindAddr2line();
+ if (addr2line_path != nullptr) {
+ LOG(ERROR) << "Found addr2line at " << *addr2line_path;
+ } else {
+ LOG(ERROR) << "Did not find usable addr2line";
+ }
+ bool use_addr2line = kUseAddr2line && addr2line_path != nullptr;
+ LOG(ERROR) << (use_addr2line ? "U" : "Not u") << "sing addr2line";
+
+ if (!WaitForMainSigStop(saw_wif_stopped_for_main)) {
+ LOG(ERROR) << "Did not receive SIGSTOP for pid " << forked_pid;
+ }
+
+ std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(forked_pid));
+ if (backtrace_map == nullptr) {
+ LOG(ERROR) << "Could not create BacktraceMap";
+ return;
+ }
+
+ for (pid_t tid : tids) {
+ DumpThread(forked_pid,
+ tid,
+ use_addr2line ? addr2line_path.get() : nullptr,
+ " ",
+ backtrace_map.get());
+ }
+}
+
+[[noreturn]]
+void WaitMainLoop(pid_t forked_pid, std::atomic<bool>* saw_wif_stopped_for_main) {
+ for (;;) {
+ // Consider switching to waitid to not get woken up for WIFSTOPPED.
+ int status;
+ pid_t res = TEMP_FAILURE_RETRY(waitpid(forked_pid, &status, 0));
+ if (res == -1) {
+ PLOG(FATAL) << "Failure during waitpid";
+ __builtin_unreachable();
+ }
+
+ if (WIFEXITED(status)) {
+ _exit(WEXITSTATUS(status));
+ __builtin_unreachable();
+ }
+ if (WIFSIGNALED(status)) {
+ _exit(1);
+ __builtin_unreachable();
+ }
+ if (WIFSTOPPED(status)) {
+ *saw_wif_stopped_for_main = true;
+ continue;
+ }
+ if (WIFCONTINUED(status)) {
+ continue;
+ }
+
+ LOG(FATAL) << "Unknown status " << std::hex << status;
+ }
+}
+
+[[noreturn]]
+void SetupAndWait(pid_t forked_pid) {
+ timeout_signal::SignalSet signals;
+ signals.Add(timeout_signal::GetTimeoutSignal());
+ signals.Block();
+
+ std::atomic<bool> saw_wif_stopped_for_main(false);
+
+ std::thread signal_catcher([&]() {
+ signals.Block();
+ int sig = signals.Wait();
+ CHECK_EQ(sig, timeout_signal::GetTimeoutSignal());
+
+ DumpProcess(forked_pid, saw_wif_stopped_for_main);
+
+ // Don't clean up. Just kill the child and exit.
+ kill(forked_pid, SIGKILL);
+ _exit(1);
+ });
+
+ WaitMainLoop(forked_pid, &saw_wif_stopped_for_main);
+}
+
+} // namespace
+} // namespace art
+
+int main(int argc ATTRIBUTE_UNUSED, char** argv) {
+ pid_t orig_ppid = getpid();
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ if (prctl(PR_SET_PDEATHSIG, SIGTERM) == -1) {
+ _exit(1);
+ }
+
+ if (getppid() != orig_ppid) {
+ _exit(2);
+ }
+
+ execvp(argv[1], &argv[1]);
+
+ _exit(3);
+ __builtin_unreachable();
+ }
+
+ art::SetupAndWait(pid);
+ __builtin_unreachable();
+}