[automerger skipped] Merge commit '67c932ce050e80b9bf23b15b24d9aed1a2ee209d' into art-file-create am: 7b1b6673e3 am: 89cb524ea7 -s ours
am: d55574c730 -s ours
am skip reason: change_id Ic4dae399a5bfe862aff3d8614c45b38044d805db with SHA1 d224e964bd is in history
Change-Id: I95af592d7c996a9faff51204f29e42f6913cdc0b
diff --git a/.gitignore b/.gitignore
index 4e806c6..803c297 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,6 @@
JIT_ART
**/__pycache__/**
+**/.idea
+**/*.iml
+**/*.pyc
+**/*.swn
diff --git a/Android.bp b/Android.bp
index d0e22fb..c1c3049 100644
--- a/Android.bp
+++ b/Android.bp
@@ -11,13 +11,17 @@
"libbacktrace",
"libcutils",
"libunwindbacktrace",
+ "libunwind",
+ "libunwindstack",
"libutils",
"libbase",
"liblz4",
"liblzma",
+ "libmetricslogger_static",
]
subdirs = [
+ "adbconnection",
"benchmark",
"build",
"cmdline",
@@ -29,13 +33,25 @@
"dexlist",
"dexoptanalyzer",
"disassembler",
+ "dt_fd_forward",
+ "dt_fd_forward/export",
"imgdiag",
+ "libartbase",
+ "libdexfile",
"oatdump",
+ "openjdkjvm",
+ "openjdkjvmti",
"patchoat",
"profman",
"runtime",
"sigchainlib",
+ "simulator",
"test",
+ "tools",
+ "tools/breakpoint-logger",
"tools/cpp-define-generator",
"tools/dmtracedump",
+ "tools/hiddenapi",
+ "tools/titrace",
+ "tools/wrapagentproperties",
]
diff --git a/Android.mk b/Android.mk
index 8735d7c..e4f4e74 100644
--- a/Android.mk
+++ b/Android.mk
@@ -25,18 +25,6 @@
include $(art_path)/build/Android.common_path.mk
include $(art_path)/build/Android.oat.mk
-# Following the example of build's dont_bother for clean targets.
-art_dont_bother := false
-ifneq (,$(filter clean-oat%,$(MAKECMDGOALS)))
- art_dont_bother := true
-endif
-
-# Don't bother with tests unless there is a test-art*, build-art*, or related target.
-art_test_bother := false
-ifneq (,$(filter tests test-art% valgrind-test-art% build-art% checkbuild,$(MAKECMDGOALS)))
- art_test_bother := true
-endif
-
.PHONY: clean-oat
clean-oat: clean-oat-host clean-oat-target
@@ -66,8 +54,6 @@
endif
adb shell rm -rf data/run-test/test-*/dalvik-cache/*
-ifneq ($(art_dont_bother),true)
-
########################################################################
# cpplint rules to style check art source files
@@ -79,7 +65,9 @@
include $(art_path)/oatdump/Android.mk
include $(art_path)/tools/Android.mk
include $(art_path)/tools/ahat/Android.mk
+include $(art_path)/tools/amm/Android.mk
include $(art_path)/tools/dexfuzz/Android.mk
+include $(art_path)/tools/veridex/Android.mk
include $(art_path)/libart_fake/Android.mk
ART_HOST_DEPENDENCIES := \
@@ -103,8 +91,6 @@
########################################################################
# test rules
-ifeq ($(art_test_bother),true)
-
# All the dependencies that must be built ahead of sync-ing them onto the target device.
TEST_ART_TARGET_SYNC_DEPS :=
@@ -135,11 +121,11 @@
else
test-art-target-sync: $(TEST_ART_TARGET_SYNC_DEPS)
$(TEST_ART_ADB_ROOT_AND_REMOUNT)
- adb wait-for-device push $(ANDROID_PRODUCT_OUT)/system $(ART_TEST_ANDROID_ROOT)
+ adb wait-for-device push $(PRODUCT_OUT)/system $(ART_TEST_ANDROID_ROOT)
# Push the contents of the `data` dir into `/data` on the device. If
# `/data` already exists on the device, it is not overwritten, but its
# contents are updated.
- adb push $(ANDROID_PRODUCT_OUT)/data /
+ adb push $(PRODUCT_OUT)/data /
endif
endif
@@ -348,7 +334,6 @@
valgrind-test-art-target64: valgrind-test-art-target-gtest64
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
-endif # art_test_bother
#######################
# Fake packages for ART
@@ -370,6 +355,7 @@
libopenjdkjvmti \
patchoat \
profman \
+ libadbconnection \
# For nosy apps, we provide a fake library that avoids namespace issues and gives some warnings.
LOCAL_REQUIRED_MODULES += libart_fake
@@ -395,6 +381,7 @@
libopenjdkjvmtid \
patchoatd \
profmand \
+ libadbconnectiond \
endif
endif
@@ -445,6 +432,19 @@
include $(BUILD_PHONY_PACKAGE)
endif
+# Create dummy hidden API lists which are normally generated by the framework
+# but which we do not have in the master-art manifest.
+# We need to execute this now to ensure Makefile rules depending on these files can
+# be constructed.
+define build-art-hiddenapi
+$(shell if [ ! -d frameworks/base ]; then \
+ mkdir -p ${TARGET_OUT_COMMON_INTERMEDIATES}/PACKAGING; \
+ touch ${TARGET_OUT_COMMON_INTERMEDIATES}/PACKAGING/hiddenapi-{blacklist,dark-greylist,light-greylist}.txt; \
+ fi;)
+endef
+
+$(eval $(call build-art-hiddenapi))
+
########################################################################
# "m build-art" for quick minimal build
.PHONY: build-art
@@ -457,7 +457,8 @@
build-art-target: $(TARGET_OUT_EXECUTABLES)/art $(ART_TARGET_DEPENDENCIES) $(TARGET_CORE_IMG_OUTS)
########################################################################
-# Phony target for only building what go/lem requires on target.
+# Phony target for only building what go/lem requires for pushing ART on /data.
+
.PHONY: build-art-target-golem
# Also include libartbenchmark, we always include it when running golem.
# libstdc++ is needed when building for ART_TARGET_LINUX.
@@ -470,8 +471,10 @@
$(ART_TARGET_SHARED_LIBRARY_BENCHMARK) \
$(TARGET_CORE_IMG_OUT_BASE).art \
$(TARGET_CORE_IMG_OUT_BASE)-interpreter.art
+ # remove libartd.so and libdexfiled.so from public.libraries.txt because golem builds
+ # won't have it.
sed -i '/libartd.so/d' $(TARGET_OUT)/etc/public.libraries.txt
- # remove libartd.so from public.libraries.txt because golem builds won't have it.
+ sed -i '/libdexfiled.so/d' $(TARGET_OUT)/etc/public.libraries.txt
########################################################################
# Phony target for building what go/lem requires on host.
@@ -482,6 +485,11 @@
$(ART_HOST_SHARED_LIBRARY_BENCHMARK)
########################################################################
+# Phony target for building what go/lem requires for syncing /system to target.
+.PHONY: build-art-unbundled-golem
+build-art-unbundled-golem: art-runtime linker oatdump $(TARGET_CORE_JARS) crash_dump
+
+########################################################################
# Rules for building all dependencies for tests.
.PHONY: build-art-host-tests
@@ -582,11 +590,7 @@
########################################################################
-endif # !art_dont_bother
-
# Clear locally used variables.
-art_dont_bother :=
-art_test_bother :=
TEST_ART_TARGET_SYNC_DEPS :=
# Helper target that depends on boot image creation.
diff --git a/CPPLINT.cfg b/CPPLINT.cfg
new file mode 100644
index 0000000..8328842
--- /dev/null
+++ b/CPPLINT.cfg
@@ -0,0 +1,33 @@
+#
+# 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.
+#
+
+# Don't search for additional CPPLINT.cfg in parent directories.
+set noparent
+
+# Use 'ART_' as the cpp header guard prefix (e.g. #ifndef ART_PATH_TO_FILE_H_).
+root=..
+
+# Limit line length.
+linelength=100
+
+# Ignore the following categories of errors, as specified by the filter:
+# (the filter settings are concatenated together)
+filter=-build/c++11
+filter=-build/include
+filter=-readability/function,-readability/streams,-readability/todo
+filter=-runtime/printf,-runtime/references,-runtime/sizeof,-runtime/threadsafe_fn
+# TODO: this should be re-enabled.
+filter=-whitespace/line_length
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 8a8df36..7e492c7 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,4 +1,10 @@
[Hook Scripts]
check_generated_files_up_to_date = tools/cpp-define-generator/presubmit-check-files-up-to-date
check_generated_tests_up_to_date = tools/test_presubmit.py
-check_cpplint_on_changed_files = tools/cpplint_presubmit.py
+
+[Builtin Hooks]
+cpplint = true
+
+[Builtin Hooks Options]
+# Cpplint prints nothing unless there were errors.
+cpplint = --quiet ${PREUPLOAD_FILES}
diff --git a/adbconnection/Android.bp b/adbconnection/Android.bp
new file mode 100644
index 0000000..441b706
--- /dev/null
+++ b/adbconnection/Android.bp
@@ -0,0 +1,80 @@
+//
+// 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.
+//
+
+// Build variants {target,host} x {debug,ndebug} x {32,64}
+
+cc_defaults {
+ name: "adbconnection-defaults",
+ host_supported: true,
+ srcs: ["adbconnection.cc"],
+ defaults: ["art_defaults"],
+
+ // Note that this tool needs to be built for both 32-bit and 64-bit since it requires
+ // to be same ISA as what it is attached to.
+ compile_multilib: "both",
+
+ shared_libs: [
+ "libbase",
+ ],
+ target: {
+ android: {
+ shared_libs: [
+ "libcutils",
+ ],
+ },
+ host: {
+ },
+ darwin: {
+ enabled: false,
+ },
+ },
+ header_libs: [
+ "libnativehelper_header_only",
+ "dt_fd_forward_export",
+ ],
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+ symlink_preferred_arch: true,
+ required: [
+ "libjdwp",
+ "libdt_fd_forward",
+ ],
+}
+
+art_cc_library {
+ name: "libadbconnection",
+ defaults: ["adbconnection-defaults"],
+ shared_libs: [
+ "libart",
+ ],
+}
+
+art_cc_library {
+ name: "libadbconnectiond",
+ defaults: [
+ "art_debug_defaults",
+ "adbconnection-defaults",
+ ],
+ shared_libs: [
+ "libartd",
+ ],
+}
diff --git a/adbconnection/adbconnection.cc b/adbconnection/adbconnection.cc
new file mode 100644
index 0000000..4c2d4d7
--- /dev/null
+++ b/adbconnection/adbconnection.cc
@@ -0,0 +1,907 @@
+/*
+ * 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.
+ */
+
+#include <array>
+
+#include "adbconnection.h"
+
+#include "android-base/endian.h"
+#include "android-base/stringprintf.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "java_vm_ext.h"
+#include "jni_env_ext.h"
+#include "mirror/throwable.h"
+#include "nativehelper/ScopedLocalRef.h"
+#include "runtime-inl.h"
+#include "runtime_callbacks.h"
+#include "scoped_thread_state_change-inl.h"
+#include "well_known_classes.h"
+
+#include "jdwp/jdwp_priv.h"
+
+#include "fd_transport.h"
+
+#include "poll.h"
+
+#ifdef ART_TARGET_ANDROID
+#include "cutils/sockets.h"
+#endif
+
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/eventfd.h>
+#include <jni.h>
+
+namespace adbconnection {
+
+// Messages sent from the transport
+using dt_fd_forward::kListenStartMessage;
+using dt_fd_forward::kListenEndMessage;
+using dt_fd_forward::kAcceptMessage;
+using dt_fd_forward::kCloseMessage;
+
+// Messages sent to the transport
+using dt_fd_forward::kPerformHandshakeMessage;
+using dt_fd_forward::kSkipHandshakeMessage;
+
+using android::base::StringPrintf;
+
+static constexpr const char kJdwpHandshake[14] = {
+ 'J', 'D', 'W', 'P', '-', 'H', 'a', 'n', 'd', 's', 'h', 'a', 'k', 'e'
+};
+
+static constexpr int kEventfdLocked = 0;
+static constexpr int kEventfdUnlocked = 1;
+static constexpr int kControlSockSendTimeout = 10;
+
+static constexpr size_t kPacketHeaderLen = 11;
+static constexpr off_t kPacketSizeOff = 0;
+static constexpr off_t kPacketIdOff = 4;
+static constexpr off_t kPacketCommandSetOff = 9;
+static constexpr off_t kPacketCommandOff = 10;
+
+static constexpr uint8_t kDdmCommandSet = 199;
+static constexpr uint8_t kDdmChunkCommand = 1;
+
+static AdbConnectionState* gState;
+
+static bool IsDebuggingPossible() {
+ return art::Dbg::IsJdwpAllowed();
+}
+
+// Begin running the debugger.
+void AdbConnectionDebuggerController::StartDebugger() {
+ if (IsDebuggingPossible()) {
+ connection_->StartDebuggerThreads();
+ } else {
+ LOG(ERROR) << "Not starting debugger since process cannot load the jdwp agent.";
+ }
+}
+
+// The debugger should begin shutting down since the runtime is ending. We don't actually do
+// anything here. The real shutdown has already happened as far as the agent is concerned.
+void AdbConnectionDebuggerController::StopDebugger() { }
+
+bool AdbConnectionDebuggerController::IsDebuggerConfigured() {
+ return IsDebuggingPossible() && !art::Runtime::Current()->GetJdwpOptions().empty();
+}
+
+void AdbConnectionDdmCallback::DdmPublishChunk(uint32_t type,
+ const art::ArrayRef<const uint8_t>& data) {
+ connection_->PublishDdmData(type, data);
+}
+
+class ScopedEventFdLock {
+ public:
+ explicit ScopedEventFdLock(int fd) : fd_(fd), data_(0) {
+ TEMP_FAILURE_RETRY(read(fd_, &data_, sizeof(data_)));
+ }
+
+ ~ScopedEventFdLock() {
+ TEMP_FAILURE_RETRY(write(fd_, &data_, sizeof(data_)));
+ }
+
+ private:
+ int fd_;
+ uint64_t data_;
+};
+
+AdbConnectionState::AdbConnectionState(const std::string& agent_name)
+ : agent_name_(agent_name),
+ controller_(this),
+ ddm_callback_(this),
+ sleep_event_fd_(-1),
+ control_sock_(-1),
+ local_agent_control_sock_(-1),
+ remote_agent_control_sock_(-1),
+ adb_connection_socket_(-1),
+ adb_write_event_fd_(-1),
+ shutting_down_(false),
+ agent_loaded_(false),
+ agent_listening_(false),
+ agent_has_socket_(false),
+ sent_agent_fds_(false),
+ performed_handshake_(false),
+ notified_ddm_active_(false),
+ next_ddm_id_(1),
+ started_debugger_threads_(false) {
+ // Setup the addr.
+ control_addr_.controlAddrUn.sun_family = AF_UNIX;
+ control_addr_len_ = sizeof(control_addr_.controlAddrUn.sun_family) + sizeof(kJdwpControlName) - 1;
+ memcpy(control_addr_.controlAddrUn.sun_path, kJdwpControlName, sizeof(kJdwpControlName) - 1);
+
+ // Add the startup callback.
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::Runtime::Current()->GetRuntimeCallbacks()->AddDebuggerControlCallback(&controller_);
+}
+
+static jobject CreateAdbConnectionThread(art::Thread* thr) {
+ JNIEnv* env = thr->GetJniEnv();
+ // Move to native state to talk with the jnienv api.
+ art::ScopedThreadStateChange stsc(thr, art::kNative);
+ ScopedLocalRef<jstring> thr_name(env, env->NewStringUTF(kAdbConnectionThreadName));
+ ScopedLocalRef<jobject> thr_group(
+ env,
+ env->GetStaticObjectField(art::WellKnownClasses::java_lang_ThreadGroup,
+ art::WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup));
+ return env->NewObject(art::WellKnownClasses::java_lang_Thread,
+ art::WellKnownClasses::java_lang_Thread_init,
+ thr_group.get(),
+ thr_name.get(),
+ /*Priority*/ 0,
+ /*Daemon*/ true);
+}
+
+struct CallbackData {
+ AdbConnectionState* this_;
+ jobject thr_;
+};
+
+static void* CallbackFunction(void* vdata) {
+ std::unique_ptr<CallbackData> data(reinterpret_cast<CallbackData*>(vdata));
+ CHECK(data->this_ == gState);
+ art::Thread* self = art::Thread::Attach(kAdbConnectionThreadName,
+ true,
+ data->thr_);
+ CHECK(self != nullptr) << "threads_being_born_ should have ensured thread could be attached.";
+ // The name in Attach() is only for logging. Set the thread name. This is important so
+ // that the thread is no longer seen as starting up.
+ {
+ art::ScopedObjectAccess soa(self);
+ self->SetThreadName(kAdbConnectionThreadName);
+ }
+
+ // Release the peer.
+ JNIEnv* env = self->GetJniEnv();
+ env->DeleteGlobalRef(data->thr_);
+ data->thr_ = nullptr;
+ {
+ // The StartThreadBirth was called in the parent thread. We let the runtime know we are up
+ // before going into the provided code.
+ art::MutexLock mu(self, *art::Locks::runtime_shutdown_lock_);
+ art::Runtime::Current()->EndThreadBirth();
+ }
+ data->this_->RunPollLoop(self);
+ int detach_result = art::Runtime::Current()->GetJavaVM()->DetachCurrentThread();
+ CHECK_EQ(detach_result, 0);
+
+ // Get rid of the connection
+ gState = nullptr;
+ delete data->this_;
+
+ return nullptr;
+}
+
+void AdbConnectionState::StartDebuggerThreads() {
+ // First do all the final setup we need.
+ CHECK_EQ(adb_write_event_fd_.get(), -1);
+ CHECK_EQ(sleep_event_fd_.get(), -1);
+ CHECK_EQ(local_agent_control_sock_.get(), -1);
+ CHECK_EQ(remote_agent_control_sock_.get(), -1);
+
+ sleep_event_fd_.reset(eventfd(kEventfdLocked, EFD_CLOEXEC));
+ CHECK_NE(sleep_event_fd_.get(), -1) << "Unable to create wakeup eventfd.";
+ adb_write_event_fd_.reset(eventfd(kEventfdUnlocked, EFD_CLOEXEC));
+ CHECK_NE(adb_write_event_fd_.get(), -1) << "Unable to create write-lock eventfd.";
+
+ {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::Runtime::Current()->GetRuntimeCallbacks()->AddDdmCallback(&ddm_callback_);
+ }
+ // Setup the socketpair we use to talk to the agent.
+ bool has_sockets;
+ do {
+ has_sockets = android::base::Socketpair(AF_UNIX,
+ SOCK_SEQPACKET | SOCK_CLOEXEC,
+ 0,
+ &local_agent_control_sock_,
+ &remote_agent_control_sock_);
+ } while (!has_sockets && errno == EINTR);
+ if (!has_sockets) {
+ PLOG(FATAL) << "Unable to create socketpair for agent control!";
+ }
+
+ // Next start the threads.
+ art::Thread* self = art::Thread::Current();
+ art::ScopedObjectAccess soa(self);
+ {
+ art::Runtime* runtime = art::Runtime::Current();
+ art::MutexLock mu(self, *art::Locks::runtime_shutdown_lock_);
+ if (runtime->IsShuttingDownLocked()) {
+ // The runtime is shutting down so we cannot create new threads. This shouldn't really happen.
+ LOG(ERROR) << "The runtime is shutting down when we are trying to start up the debugger!";
+ return;
+ }
+ runtime->StartThreadBirth();
+ }
+ ScopedLocalRef<jobject> thr(soa.Env(), CreateAdbConnectionThread(soa.Self()));
+ pthread_t pthread;
+ std::unique_ptr<CallbackData> data(new CallbackData { this, soa.Env()->NewGlobalRef(thr.get()) });
+ started_debugger_threads_ = true;
+ int pthread_create_result = pthread_create(&pthread,
+ nullptr,
+ &CallbackFunction,
+ data.get());
+ if (pthread_create_result != 0) {
+ started_debugger_threads_ = false;
+ // If the create succeeded the other thread will call EndThreadBirth.
+ art::Runtime* runtime = art::Runtime::Current();
+ soa.Env()->DeleteGlobalRef(data->thr_);
+ LOG(ERROR) << "Failed to create thread for adb-jdwp connection manager!";
+ art::MutexLock mu(art::Thread::Current(), *art::Locks::runtime_shutdown_lock_);
+ runtime->EndThreadBirth();
+ return;
+ }
+ data.release();
+}
+
+static bool FlagsSet(int16_t data, int16_t flags) {
+ return (data & flags) == flags;
+}
+
+void AdbConnectionState::CloseFds() {
+ {
+ // Lock the write_event_fd so that concurrent PublishDdms will see that the connection is
+ // closed.
+ ScopedEventFdLock lk(adb_write_event_fd_);
+ // shutdown(adb_connection_socket_, SHUT_RDWR);
+ adb_connection_socket_.reset();
+ }
+
+ // If we didn't load anything we will need to do the handshake again.
+ performed_handshake_ = false;
+
+ // If the agent isn't loaded we might need to tell ddms code the connection is closed.
+ if (!agent_loaded_ && notified_ddm_active_) {
+ NotifyDdms(/*active*/false);
+ }
+}
+
+void AdbConnectionState::NotifyDdms(bool active) {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ DCHECK_NE(notified_ddm_active_, active);
+ notified_ddm_active_ = active;
+ if (active) {
+ art::Dbg::DdmConnected();
+ } else {
+ art::Dbg::DdmDisconnected();
+ }
+}
+
+uint32_t AdbConnectionState::NextDdmId() {
+ // Just have a normal counter but always set the sign bit.
+ return (next_ddm_id_++) | 0x80000000;
+}
+
+void AdbConnectionState::PublishDdmData(uint32_t type, const art::ArrayRef<const uint8_t>& data) {
+ SendDdmPacket(NextDdmId(), DdmPacketType::kCmd, type, data);
+}
+
+void AdbConnectionState::SendDdmPacket(uint32_t id,
+ DdmPacketType packet_type,
+ uint32_t type,
+ art::ArrayRef<const uint8_t> data) {
+ // Get the write_event early to fail fast.
+ ScopedEventFdLock lk(adb_write_event_fd_);
+ if (adb_connection_socket_ == -1) {
+ VLOG(jdwp) << "Not sending ddms data of type "
+ << StringPrintf("%c%c%c%c",
+ static_cast<char>(type >> 24),
+ static_cast<char>(type >> 16),
+ static_cast<char>(type >> 8),
+ static_cast<char>(type)) << " due to no connection!";
+ // Adb is not connected.
+ return;
+ }
+
+ // the adb_write_event_fd_ will ensure that the adb_connection_socket_ will not go away until
+ // after we have sent our data.
+ static constexpr uint32_t kDdmPacketHeaderSize =
+ kJDWPHeaderLen // jdwp command packet size
+ + sizeof(uint32_t) // Type
+ + sizeof(uint32_t); // length
+ alignas(sizeof(uint32_t)) std::array<uint8_t, kDdmPacketHeaderSize> pkt;
+ uint8_t* pkt_data = pkt.data();
+
+ // Write the length first.
+ *reinterpret_cast<uint32_t*>(pkt_data) = htonl(kDdmPacketHeaderSize + data.size());
+ pkt_data += sizeof(uint32_t);
+
+ // Write the id next;
+ *reinterpret_cast<uint32_t*>(pkt_data) = htonl(id);
+ pkt_data += sizeof(uint32_t);
+
+ // next the flags. (0 for cmd packet because DDMS).
+ *(pkt_data++) = static_cast<uint8_t>(packet_type);
+ switch (packet_type) {
+ case DdmPacketType::kCmd: {
+ // Now the cmd-set
+ *(pkt_data++) = kJDWPDdmCmdSet;
+ // Now the command
+ *(pkt_data++) = kJDWPDdmCmd;
+ break;
+ }
+ case DdmPacketType::kReply: {
+ // This is the error code bytes which are all 0
+ *(pkt_data++) = 0;
+ *(pkt_data++) = 0;
+ }
+ }
+
+ // These are at unaligned addresses so we need to do them manually.
+ // now the type.
+ uint32_t net_type = htonl(type);
+ memcpy(pkt_data, &net_type, sizeof(net_type));
+ pkt_data += sizeof(uint32_t);
+
+ // Now the data.size()
+ uint32_t net_len = htonl(data.size());
+ memcpy(pkt_data, &net_len, sizeof(net_len));
+ pkt_data += sizeof(uint32_t);
+
+ static uint32_t constexpr kIovSize = 2;
+ struct iovec iovs[kIovSize] = {
+ { pkt.data(), pkt.size() },
+ { const_cast<uint8_t*>(data.data()), data.size() },
+ };
+ // now pkt_header has the header.
+ // use writev to send the actual data.
+ ssize_t res = TEMP_FAILURE_RETRY(writev(adb_connection_socket_, iovs, kIovSize));
+ if (static_cast<size_t>(res) != (kDdmPacketHeaderSize + data.size())) {
+ PLOG(ERROR) << StringPrintf("Failed to send DDMS packet %c%c%c%c to debugger (%zd of %zu)",
+ static_cast<char>(type >> 24),
+ static_cast<char>(type >> 16),
+ static_cast<char>(type >> 8),
+ static_cast<char>(type),
+ res, data.size() + kDdmPacketHeaderSize);
+ } else {
+ VLOG(jdwp) << StringPrintf("sent DDMS packet %c%c%c%c to debugger %zu",
+ static_cast<char>(type >> 24),
+ static_cast<char>(type >> 16),
+ static_cast<char>(type >> 8),
+ static_cast<char>(type),
+ data.size() + kDdmPacketHeaderSize);
+ }
+}
+
+void AdbConnectionState::SendAgentFds(bool require_handshake) {
+ DCHECK(!sent_agent_fds_);
+ const char* message = require_handshake ? kPerformHandshakeMessage : kSkipHandshakeMessage;
+ union {
+ cmsghdr cm;
+ char buffer[CMSG_SPACE(dt_fd_forward::FdSet::kDataLength)];
+ } cm_un;
+ iovec iov;
+ iov.iov_base = const_cast<char*>(message);
+ iov.iov_len = strlen(message) + 1;
+
+ msghdr msg;
+ msg.msg_name = nullptr;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_flags = 0;
+ msg.msg_control = cm_un.buffer;
+ msg.msg_controllen = sizeof(cm_un.buffer);
+
+ cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(dt_fd_forward::FdSet::kDataLength);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+
+ // Duplicate the fds before sending them.
+ android::base::unique_fd read_fd(dup(adb_connection_socket_));
+ CHECK_NE(read_fd.get(), -1) << "Failed to dup read_fd_: " << strerror(errno);
+ android::base::unique_fd write_fd(dup(adb_connection_socket_));
+ CHECK_NE(write_fd.get(), -1) << "Failed to dup write_fd: " << strerror(errno);
+ android::base::unique_fd write_lock_fd(dup(adb_write_event_fd_));
+ CHECK_NE(write_lock_fd.get(), -1) << "Failed to dup write_lock_fd: " << strerror(errno);
+
+ dt_fd_forward::FdSet {
+ read_fd.get(), write_fd.get(), write_lock_fd.get()
+ }.WriteData(CMSG_DATA(cmsg));
+
+ int res = TEMP_FAILURE_RETRY(sendmsg(local_agent_control_sock_, &msg, MSG_EOR));
+ if (res < 0) {
+ PLOG(ERROR) << "Failed to send agent adb connection fds.";
+ } else {
+ sent_agent_fds_ = true;
+ VLOG(jdwp) << "Fds have been sent to jdwp agent!";
+ }
+}
+
+android::base::unique_fd AdbConnectionState::ReadFdFromAdb() {
+ // We don't actually care about the data that is sent. We do need to receive something though.
+ char dummy = '!';
+ union {
+ cmsghdr cm;
+ char buffer[CMSG_SPACE(sizeof(int))];
+ } cm_un;
+
+ iovec iov;
+ iov.iov_base = &dummy;
+ iov.iov_len = 1;
+
+ msghdr msg;
+ msg.msg_name = nullptr;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_flags = 0;
+ msg.msg_control = cm_un.buffer;
+ msg.msg_controllen = sizeof(cm_un.buffer);
+
+ cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = msg.msg_controllen;
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ (reinterpret_cast<int*>(CMSG_DATA(cmsg)))[0] = -1;
+
+ int rc = TEMP_FAILURE_RETRY(recvmsg(control_sock_, &msg, 0));
+
+ if (rc <= 0) {
+ PLOG(WARNING) << "Receiving file descriptor from ADB failed (socket " << control_sock_ << ")";
+ return android::base::unique_fd(-1);
+ } else {
+ VLOG(jdwp) << "Fds have been received from ADB!";
+ }
+
+ return android::base::unique_fd((reinterpret_cast<int*>(CMSG_DATA(cmsg)))[0]);
+}
+
+bool AdbConnectionState::SetupAdbConnection() {
+ int sleep_ms = 500;
+ const int sleep_max_ms = 2*1000;
+
+ android::base::unique_fd sock(socket(AF_UNIX, SOCK_SEQPACKET, 0));
+ if (sock < 0) {
+ PLOG(ERROR) << "Could not create ADB control socket";
+ return false;
+ }
+ struct timeval timeout;
+ timeout.tv_sec = kControlSockSendTimeout;
+ timeout.tv_usec = 0;
+ setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
+ int32_t pid = getpid();
+
+ while (!shutting_down_) {
+ // If adbd isn't running, because USB debugging was disabled or
+ // perhaps the system is restarting it for "adb root", the
+ // connect() will fail. We loop here forever waiting for it
+ // to come back.
+ //
+ // Waking up and polling every couple of seconds is generally a
+ // bad thing to do, but we only do this if the application is
+ // debuggable *and* adbd isn't running. Still, for the sake
+ // of battery life, we should consider timing out and giving
+ // up after a few minutes in case somebody ships an app with
+ // the debuggable flag set.
+ int ret = connect(sock, &control_addr_.controlAddrPlain, control_addr_len_);
+ if (ret == 0) {
+ bool trusted = sock >= 0;
+#ifdef ART_TARGET_ANDROID
+ // Needed for socket_peer_is_trusted.
+ trusted = trusted && socket_peer_is_trusted(sock);
+#endif
+ if (!trusted) {
+ LOG(ERROR) << "adb socket is not trusted. Aborting connection.";
+ if (sock >= 0 && shutdown(sock, SHUT_RDWR)) {
+ PLOG(ERROR) << "trouble shutting down socket";
+ }
+ return false;
+ }
+ /* now try to send our pid to the ADB daemon */
+ ret = TEMP_FAILURE_RETRY(send(sock, &pid, sizeof(pid), 0));
+ if (ret == sizeof(pid)) {
+ VLOG(jdwp) << "PID " << pid << " sent to adb";
+ control_sock_ = std::move(sock);
+ return true;
+ } else {
+ PLOG(ERROR) << "Weird, can't send JDWP process pid to ADB. Aborting connection.";
+ return false;
+ }
+ } else {
+ if (VLOG_IS_ON(jdwp)) {
+ PLOG(ERROR) << "Can't connect to ADB control socket. Will retry.";
+ }
+
+ usleep(sleep_ms * 1000);
+
+ sleep_ms += (sleep_ms >> 1);
+ if (sleep_ms > sleep_max_ms) {
+ sleep_ms = sleep_max_ms;
+ }
+ }
+ }
+ return false;
+}
+
+void AdbConnectionState::RunPollLoop(art::Thread* self) {
+ CHECK_NE(agent_name_, "");
+ CHECK_EQ(self->GetState(), art::kNative);
+ // TODO: Clang prebuilt for r316199 produces bogus thread safety analysis warning for holding both
+ // exclusive and shared lock in the same scope. Remove the assertion as a temporary workaround.
+ // http://b/71769596
+ // art::Locks::mutator_lock_->AssertNotHeld(self);
+ self->SetState(art::kWaitingInMainDebuggerLoop);
+ // shutting_down_ set by StopDebuggerThreads
+ while (!shutting_down_) {
+ // First get the control_sock_ from adb if we don't have one. We only need to do this once.
+ if (control_sock_ == -1 && !SetupAdbConnection()) {
+ LOG(ERROR) << "Failed to setup adb connection.";
+ return;
+ }
+ while (!shutting_down_ && control_sock_ != -1) {
+ bool should_listen_on_connection = !agent_has_socket_ && !sent_agent_fds_;
+ struct pollfd pollfds[4] = {
+ { sleep_event_fd_, POLLIN, 0 },
+ // -1 as an fd causes it to be ignored by poll
+ { (agent_loaded_ ? local_agent_control_sock_ : -1), POLLIN, 0 },
+ // Check for the control_sock_ actually going away. Only do this if we don't have an active
+ // connection.
+ { (adb_connection_socket_ == -1 ? control_sock_ : -1), POLLIN | POLLRDHUP, 0 },
+ // if we have not loaded the agent either the adb_connection_socket_ is -1 meaning we don't
+ // have a real connection yet or the socket through adb needs to be listened to for incoming
+ // data that the agent or this plugin can handle.
+ { should_listen_on_connection ? adb_connection_socket_ : -1, POLLIN | POLLRDHUP, 0 }
+ };
+ int res = TEMP_FAILURE_RETRY(poll(pollfds, 4, -1));
+ if (res < 0) {
+ PLOG(ERROR) << "Failed to poll!";
+ return;
+ }
+ // We don't actually care about doing this we just use it to wake us up.
+ // const struct pollfd& sleep_event_poll = pollfds[0];
+ const struct pollfd& agent_control_sock_poll = pollfds[1];
+ const struct pollfd& control_sock_poll = pollfds[2];
+ const struct pollfd& adb_socket_poll = pollfds[3];
+ if (FlagsSet(agent_control_sock_poll.revents, POLLIN)) {
+ DCHECK(agent_loaded_);
+ char buf[257];
+ res = TEMP_FAILURE_RETRY(recv(local_agent_control_sock_, buf, sizeof(buf) - 1, 0));
+ if (res < 0) {
+ PLOG(ERROR) << "Failed to read message from agent control socket! Retrying";
+ continue;
+ } else {
+ buf[res + 1] = '\0';
+ VLOG(jdwp) << "Local agent control sock has data: " << static_cast<const char*>(buf);
+ }
+ if (memcmp(kListenStartMessage, buf, sizeof(kListenStartMessage)) == 0) {
+ agent_listening_ = true;
+ if (adb_connection_socket_ != -1) {
+ SendAgentFds(/*require_handshake*/ !performed_handshake_);
+ }
+ } else if (memcmp(kListenEndMessage, buf, sizeof(kListenEndMessage)) == 0) {
+ agent_listening_ = false;
+ } else if (memcmp(kCloseMessage, buf, sizeof(kCloseMessage)) == 0) {
+ CloseFds();
+ agent_has_socket_ = false;
+ } else if (memcmp(kAcceptMessage, buf, sizeof(kAcceptMessage)) == 0) {
+ agent_has_socket_ = true;
+ sent_agent_fds_ = false;
+ // We will only ever do the handshake once so reset this.
+ performed_handshake_ = false;
+ } else {
+ LOG(ERROR) << "Unknown message received from debugger! '" << std::string(buf) << "'";
+ }
+ } else if (FlagsSet(control_sock_poll.revents, POLLIN)) {
+ bool maybe_send_fds = false;
+ {
+ // Hold onto this lock so that concurrent ddm publishes don't try to use an illegal fd.
+ ScopedEventFdLock sefdl(adb_write_event_fd_);
+ android::base::unique_fd new_fd(ReadFdFromAdb());
+ if (new_fd == -1) {
+ // Something went wrong. We need to retry getting the control socket.
+ PLOG(ERROR) << "Something went wrong getting fds from adb. Retry!";
+ control_sock_.reset();
+ break;
+ } else if (adb_connection_socket_ != -1) {
+ // We already have a connection.
+ VLOG(jdwp) << "Ignoring second debugger. Accept then drop!";
+ if (new_fd >= 0) {
+ new_fd.reset();
+ }
+ } else {
+ VLOG(jdwp) << "Adb connection established with fd " << new_fd;
+ adb_connection_socket_ = std::move(new_fd);
+ maybe_send_fds = true;
+ }
+ }
+ if (maybe_send_fds && agent_loaded_ && agent_listening_) {
+ VLOG(jdwp) << "Sending fds as soon as we received them.";
+ // The agent was already loaded so this must be after a disconnection. Therefore have the
+ // transport perform the handshake.
+ SendAgentFds(/*require_handshake*/ true);
+ }
+ } else if (FlagsSet(control_sock_poll.revents, POLLRDHUP)) {
+ // The other end of the adb connection just dropped it.
+ // Reset the connection since we don't have an active socket through the adb server.
+ DCHECK(!agent_has_socket_) << "We shouldn't be doing anything if there is already a "
+ << "connection active";
+ control_sock_.reset();
+ break;
+ } else if (FlagsSet(adb_socket_poll.revents, POLLIN)) {
+ DCHECK(!agent_has_socket_);
+ if (!agent_loaded_) {
+ HandleDataWithoutAgent(self);
+ } else if (agent_listening_ && !sent_agent_fds_) {
+ VLOG(jdwp) << "Sending agent fds again on data.";
+ // Agent was already loaded so it can deal with the handshake.
+ SendAgentFds(/*require_handshake*/ true);
+ }
+ } else if (FlagsSet(adb_socket_poll.revents, POLLRDHUP)) {
+ DCHECK(!agent_has_socket_);
+ CloseFds();
+ } else {
+ VLOG(jdwp) << "Woke up poll without anything to do!";
+ }
+ }
+ }
+}
+
+static uint32_t ReadUint32AndAdvance(/*in-out*/uint8_t** in) {
+ uint32_t res;
+ memcpy(&res, *in, sizeof(uint32_t));
+ *in = (*in) + sizeof(uint32_t);
+ return ntohl(res);
+}
+
+void AdbConnectionState::HandleDataWithoutAgent(art::Thread* self) {
+ DCHECK(!agent_loaded_);
+ DCHECK(!agent_listening_);
+ // TODO Should we check in some other way if we are userdebug/eng?
+ CHECK(art::Dbg::IsJdwpAllowed());
+ // We try to avoid loading the agent which is expensive. First lets just perform the handshake.
+ if (!performed_handshake_) {
+ PerformHandshake();
+ return;
+ }
+ // Read the packet header to figure out if it is one we can handle. We only 'peek' into the stream
+ // to see if it's one we can handle. This doesn't change the state of the socket.
+ alignas(sizeof(uint32_t)) uint8_t packet_header[kPacketHeaderLen];
+ ssize_t res = TEMP_FAILURE_RETRY(recv(adb_connection_socket_.get(),
+ packet_header,
+ sizeof(packet_header),
+ MSG_PEEK));
+ // We want to be very careful not to change the socket state until we know we succeeded. This will
+ // let us fall-back to just loading the agent and letting it deal with everything.
+ if (res <= 0) {
+ // Close the socket. We either hit EOF or an error.
+ if (res < 0) {
+ PLOG(ERROR) << "Unable to peek into adb socket due to error. Closing socket.";
+ }
+ CloseFds();
+ return;
+ } else if (res < static_cast<int>(kPacketHeaderLen)) {
+ LOG(ERROR) << "Unable to peek into adb socket. Loading agent to handle this. Only read " << res;
+ AttachJdwpAgent(self);
+ return;
+ }
+ uint32_t full_len = ntohl(*reinterpret_cast<uint32_t*>(packet_header + kPacketSizeOff));
+ uint32_t pkt_id = ntohl(*reinterpret_cast<uint32_t*>(packet_header + kPacketIdOff));
+ uint8_t pkt_cmd_set = packet_header[kPacketCommandSetOff];
+ uint8_t pkt_cmd = packet_header[kPacketCommandOff];
+ if (pkt_cmd_set != kDdmCommandSet ||
+ pkt_cmd != kDdmChunkCommand ||
+ full_len < kPacketHeaderLen) {
+ VLOG(jdwp) << "Loading agent due to jdwp packet that cannot be handled by adbconnection.";
+ AttachJdwpAgent(self);
+ return;
+ }
+ uint32_t avail = -1;
+ res = TEMP_FAILURE_RETRY(ioctl(adb_connection_socket_.get(), FIONREAD, &avail));
+ if (res < 0) {
+ PLOG(ERROR) << "Failed to determine amount of readable data in socket! Closing connection";
+ CloseFds();
+ return;
+ } else if (avail < full_len) {
+ LOG(WARNING) << "Unable to handle ddm command in adbconnection due to insufficent data. "
+ << "Expected " << full_len << " bytes but only " << avail << " are readable. "
+ << "Loading jdwp agent to deal with this.";
+ AttachJdwpAgent(self);
+ return;
+ }
+ // Actually read the data.
+ std::vector<uint8_t> full_pkt;
+ full_pkt.resize(full_len);
+ res = TEMP_FAILURE_RETRY(recv(adb_connection_socket_.get(), full_pkt.data(), full_len, 0));
+ if (res < 0) {
+ PLOG(ERROR) << "Failed to recv data from adb connection. Closing connection";
+ CloseFds();
+ return;
+ }
+ DCHECK_EQ(memcmp(full_pkt.data(), packet_header, sizeof(packet_header)), 0);
+ size_t data_size = full_len - kPacketHeaderLen;
+ if (data_size < (sizeof(uint32_t) * 2)) {
+ // This is an error (the data isn't long enough) but to match historical behavior we need to
+ // ignore it.
+ return;
+ }
+ uint8_t* ddm_data = full_pkt.data() + kPacketHeaderLen;
+ uint32_t ddm_type = ReadUint32AndAdvance(&ddm_data);
+ uint32_t ddm_len = ReadUint32AndAdvance(&ddm_data);
+ if (ddm_len > data_size - (2 * sizeof(uint32_t))) {
+ // This is an error (the data isn't long enough) but to match historical behavior we need to
+ // ignore it.
+ return;
+ }
+
+ if (!notified_ddm_active_) {
+ NotifyDdms(/*active*/ true);
+ }
+ uint32_t reply_type;
+ std::vector<uint8_t> reply;
+ if (!art::Dbg::DdmHandleChunk(self->GetJniEnv(),
+ ddm_type,
+ art::ArrayRef<const jbyte>(reinterpret_cast<const jbyte*>(ddm_data),
+ ddm_len),
+ /*out*/&reply_type,
+ /*out*/&reply)) {
+ // To match historical behavior we don't send any response when there is no data to reply with.
+ return;
+ }
+ SendDdmPacket(pkt_id,
+ DdmPacketType::kReply,
+ reply_type,
+ art::ArrayRef<const uint8_t>(reply));
+}
+
+void AdbConnectionState::PerformHandshake() {
+ CHECK(!performed_handshake_);
+ // Check to make sure we are able to read the whole handshake.
+ uint32_t avail = -1;
+ int res = TEMP_FAILURE_RETRY(ioctl(adb_connection_socket_.get(), FIONREAD, &avail));
+ if (res < 0 || avail < sizeof(kJdwpHandshake)) {
+ if (res < 0) {
+ PLOG(ERROR) << "Failed to determine amount of readable data for handshake!";
+ }
+ LOG(WARNING) << "Closing connection to broken client.";
+ CloseFds();
+ return;
+ }
+ // Perform the handshake.
+ char handshake_msg[sizeof(kJdwpHandshake)];
+ res = TEMP_FAILURE_RETRY(recv(adb_connection_socket_.get(),
+ handshake_msg,
+ sizeof(handshake_msg),
+ MSG_DONTWAIT));
+ if (res < static_cast<int>(sizeof(kJdwpHandshake)) ||
+ strncmp(handshake_msg, kJdwpHandshake, sizeof(kJdwpHandshake)) != 0) {
+ if (res < 0) {
+ PLOG(ERROR) << "Failed to read handshake!";
+ }
+ LOG(WARNING) << "Handshake failed!";
+ CloseFds();
+ return;
+ }
+ // Send the handshake back.
+ res = TEMP_FAILURE_RETRY(send(adb_connection_socket_.get(),
+ kJdwpHandshake,
+ sizeof(kJdwpHandshake),
+ 0));
+ if (res < static_cast<int>(sizeof(kJdwpHandshake))) {
+ PLOG(ERROR) << "Failed to send jdwp-handshake response.";
+ CloseFds();
+ return;
+ }
+ performed_handshake_ = true;
+}
+
+void AdbConnectionState::AttachJdwpAgent(art::Thread* self) {
+ art::Runtime* runtime = art::Runtime::Current();
+ self->AssertNoPendingException();
+ runtime->AttachAgent(/* JNIEnv */ nullptr,
+ MakeAgentArg(),
+ /* classloader */ nullptr);
+ if (self->IsExceptionPending()) {
+ LOG(ERROR) << "Failed to load agent " << agent_name_;
+ art::ScopedObjectAccess soa(self);
+ self->GetException()->Dump();
+ self->ClearException();
+ return;
+ }
+ agent_loaded_ = true;
+}
+
+bool ContainsArgument(const std::string& opts, const char* arg) {
+ return opts.find(arg) != std::string::npos;
+}
+
+bool ValidateJdwpOptions(const std::string& opts) {
+ bool res = true;
+ // The adbconnection plugin requires that the jdwp agent be configured as a 'server' because that
+ // is what adb expects and otherwise we will hit a deadlock as the poll loop thread stops waiting
+ // for the fd's to be passed down.
+ if (ContainsArgument(opts, "server=n")) {
+ res = false;
+ LOG(ERROR) << "Cannot start jdwp debugging with server=n from adbconnection.";
+ }
+ // We don't start the jdwp agent until threads are already running. It is far too late to suspend
+ // everything.
+ if (ContainsArgument(opts, "suspend=y")) {
+ res = false;
+ LOG(ERROR) << "Cannot use suspend=y with late-init jdwp.";
+ }
+ return res;
+}
+
+std::string AdbConnectionState::MakeAgentArg() {
+ const std::string& opts = art::Runtime::Current()->GetJdwpOptions();
+ DCHECK(ValidateJdwpOptions(opts));
+ // TODO Get agent_name_ from something user settable?
+ return agent_name_ + "=" + opts + (opts.empty() ? "" : ",") +
+ "ddm_already_active=" + (notified_ddm_active_ ? "y" : "n") + "," +
+ // See the comment above for why we need to be server=y. Since the agent defaults to server=n
+ // we will add it if it wasn't already present for the convenience of the user.
+ (ContainsArgument(opts, "server=y") ? "" : "server=y,") +
+ // See the comment above for why we need to be suspend=n. Since the agent defaults to
+ // suspend=y we will add it if it wasn't already present.
+ (ContainsArgument(opts, "suspend=n") ? "" : "suspend=n") +
+ "transport=dt_fd_forward,address=" + std::to_string(remote_agent_control_sock_);
+}
+
+void AdbConnectionState::StopDebuggerThreads() {
+ // The regular agent system will take care of unloading the agent (if needed).
+ shutting_down_ = true;
+ // Wakeup the poll loop.
+ uint64_t data = 1;
+ if (sleep_event_fd_ != -1) {
+ TEMP_FAILURE_RETRY(write(sleep_event_fd_, &data, sizeof(data)));
+ }
+}
+
+// The plugin initialization function.
+extern "C" bool ArtPlugin_Initialize() REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ DCHECK(art::Runtime::Current()->GetJdwpProvider() == art::JdwpProvider::kAdbConnection);
+ // TODO Provide some way for apps to set this maybe?
+ DCHECK(gState == nullptr);
+ gState = new AdbConnectionState(kDefaultJdwpAgentName);
+ return ValidateJdwpOptions(art::Runtime::Current()->GetJdwpOptions());
+}
+
+extern "C" bool ArtPlugin_Deinitialize() {
+ gState->StopDebuggerThreads();
+ if (!gState->DebuggerThreadsStarted()) {
+ // If debugger threads were started then those threads will delete the state once they are done.
+ delete gState;
+ }
+ return true;
+}
+
+} // namespace adbconnection
diff --git a/adbconnection/adbconnection.h b/adbconnection/adbconnection.h
new file mode 100644
index 0000000..04e39bf
--- /dev/null
+++ b/adbconnection/adbconnection.h
@@ -0,0 +1,182 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_ADBCONNECTION_ADBCONNECTION_H_
+#define ART_ADBCONNECTION_ADBCONNECTION_H_
+
+#include <stdint.h>
+#include <vector>
+#include <limits>
+
+#include "android-base/unique_fd.h"
+
+#include "base/mutex.h"
+#include "base/array_ref.h"
+#include "runtime_callbacks.h"
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <jni.h>
+
+namespace adbconnection {
+
+static constexpr char kJdwpControlName[] = "\0jdwp-control";
+static constexpr char kAdbConnectionThreadName[] = "ADB-JDWP Connection Control Thread";
+
+// The default jdwp agent name.
+static constexpr char kDefaultJdwpAgentName[] = "libjdwp.so";
+
+class AdbConnectionState;
+
+struct AdbConnectionDebuggerController : public art::DebuggerControlCallback {
+ explicit AdbConnectionDebuggerController(AdbConnectionState* connection)
+ : connection_(connection) {}
+
+ // Begin running the debugger.
+ void StartDebugger() OVERRIDE;
+
+ // The debugger should begin shutting down since the runtime is ending.
+ void StopDebugger() OVERRIDE;
+
+ bool IsDebuggerConfigured() OVERRIDE;
+
+ private:
+ AdbConnectionState* connection_;
+};
+
+enum class DdmPacketType : uint8_t { kReply = 0x80, kCmd = 0x00, };
+
+struct AdbConnectionDdmCallback : public art::DdmCallback {
+ explicit AdbConnectionDdmCallback(AdbConnectionState* connection) : connection_(connection) {}
+
+ void DdmPublishChunk(uint32_t type,
+ const art::ArrayRef<const uint8_t>& data)
+ REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+ private:
+ AdbConnectionState* connection_;
+};
+
+class AdbConnectionState {
+ public:
+ explicit AdbConnectionState(const std::string& name);
+
+ // Called on the listening thread to start dealing with new input. thr is used to attach the new
+ // thread to the runtime.
+ void RunPollLoop(art::Thread* self);
+
+ // Sends ddms data over the socket, if there is one. This data is sent even if we haven't finished
+ // hand-shaking yet.
+ void PublishDdmData(uint32_t type, const art::ArrayRef<const uint8_t>& data);
+
+ // Stops debugger threads during shutdown.
+ void StopDebuggerThreads();
+
+ // If StartDebuggerThreads was called successfully.
+ bool DebuggerThreadsStarted() {
+ return started_debugger_threads_;
+ }
+
+ private:
+ uint32_t NextDdmId();
+
+ void StartDebuggerThreads();
+
+ // Tell adbd about the new runtime.
+ bool SetupAdbConnection();
+
+ std::string MakeAgentArg();
+
+ android::base::unique_fd ReadFdFromAdb();
+
+ void SendAgentFds(bool require_handshake);
+
+ void CloseFds();
+
+ void HandleDataWithoutAgent(art::Thread* self);
+
+ void PerformHandshake();
+
+ void AttachJdwpAgent(art::Thread* self);
+
+ void NotifyDdms(bool active);
+
+ void SendDdmPacket(uint32_t id,
+ DdmPacketType type,
+ uint32_t ddm_type,
+ art::ArrayRef<const uint8_t> data);
+
+ std::string agent_name_;
+
+ AdbConnectionDebuggerController controller_;
+ AdbConnectionDdmCallback ddm_callback_;
+
+ // Eventfd used to allow the StopDebuggerThreads function to wake up sleeping threads
+ android::base::unique_fd sleep_event_fd_;
+
+ // Socket that we use to talk to adbd.
+ android::base::unique_fd control_sock_;
+
+ // Socket that we use to talk to the agent (if it's loaded).
+ android::base::unique_fd local_agent_control_sock_;
+
+ // The fd of the socket the agent uses to talk to us. We need to keep it around in order to clean
+ // it up when the runtime goes away.
+ android::base::unique_fd remote_agent_control_sock_;
+
+ // The fd that is forwarded through adb to the client. This is guarded by the
+ // adb_write_event_fd_.
+ android::base::unique_fd adb_connection_socket_;
+
+ // The fd we send to the agent to let us synchronize access to the shared adb_connection_socket_.
+ // This is also used as a general lock for the adb_connection_socket_ on any threads other than
+ // the poll thread.
+ android::base::unique_fd adb_write_event_fd_;
+
+ std::atomic<bool> shutting_down_;
+
+ // True if we have loaded the agent library.
+ std::atomic<bool> agent_loaded_;
+
+ // True if the dt_fd_forward transport is listening for a new communication channel.
+ std::atomic<bool> agent_listening_;
+
+ // True if the dt_fd_forward transport has the socket. If so we don't do anything to the agent or
+ // the adb connection socket until connection goes away.
+ std::atomic<bool> agent_has_socket_;
+
+ std::atomic<bool> sent_agent_fds_;
+
+ bool performed_handshake_;
+
+ bool notified_ddm_active_;
+
+ std::atomic<uint32_t> next_ddm_id_;
+
+ bool started_debugger_threads_;
+
+ socklen_t control_addr_len_;
+ union {
+ sockaddr_un controlAddrUn;
+ sockaddr controlAddrPlain;
+ } control_addr_;
+
+ friend struct AdbConnectionDebuggerController;
+};
+
+} // namespace adbconnection
+
+#endif // ART_ADBCONNECTION_ADBCONNECTION_H_
diff --git a/benchmark/Android.bp b/benchmark/Android.bp
index 606734b..3995ca2 100644
--- a/benchmark/Android.bp
+++ b/benchmark/Android.bp
@@ -17,7 +17,7 @@
art_cc_library {
name: "libartbenchmark",
host_supported: true,
- defaults: ["art_defaults" ],
+ defaults: ["art_defaults"],
srcs: [
"jni_loader.cc",
"jobject-benchmark/jobject_benchmark.cc",
@@ -31,15 +31,6 @@
"libbase",
"libnativehelper",
],
- clang: true,
- target: {
- android: {
- shared_libs: ["libdl"],
- },
- host: {
- host_ldlibs: ["-ldl", "-lpthread"],
- },
- },
cflags: [
"-Wno-frame-larger-than=",
],
@@ -49,7 +40,10 @@
name: "libartbenchmark-micronative-host",
host_supported: true,
device_supported: false,
- defaults: ["art_debug_defaults", "art_defaults" ],
+ defaults: [
+ "art_debug_defaults",
+ "art_defaults",
+ ],
srcs: [
"jni_loader.cc",
"micro-native/micro_native.cc",
@@ -60,12 +54,6 @@
],
header_libs: ["jni_headers"],
stl: "libc++_static",
- clang: true,
- target: {
- host: {
- host_ldlibs: ["-ldl", "-lpthread"],
- },
- },
cflags: [
"-Wno-frame-larger-than=",
],
diff --git a/benchmark/micro-native/micro_native.cc b/benchmark/micro-native/micro_native.cc
index d366d9d..dffbf3b 100644
--- a/benchmark/micro-native/micro_native.cc
+++ b/benchmark/micro-native/micro_native.cc
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include <stdio.h>
#include <jni.h>
+#include <stdio.h>
#ifndef NATIVE_METHOD
#define NATIVE_METHOD(className, functionName, signature) \
diff --git a/benchmark/type-check/info.txt b/benchmark/type-check/info.txt
new file mode 100644
index 0000000..d14fb96
--- /dev/null
+++ b/benchmark/type-check/info.txt
@@ -0,0 +1 @@
+Benchmarks for repeating check-cast and instance-of instructions in a loop.
diff --git a/benchmark/type-check/src/TypeCheckBenchmark.java b/benchmark/type-check/src/TypeCheckBenchmark.java
new file mode 100644
index 0000000..96904d9
--- /dev/null
+++ b/benchmark/type-check/src/TypeCheckBenchmark.java
@@ -0,0 +1,147 @@
+/*
+ * 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 TypeCheckBenchmark {
+ public void timeCheckCastLevel1ToLevel1(int count) {
+ Object[] arr = arr1;
+ for (int i = 0; i < count; ++i) {
+ Level1 l1 = (Level1) arr[i & 1023];
+ }
+ }
+
+ public void timeCheckCastLevel2ToLevel1(int count) {
+ Object[] arr = arr2;
+ for (int i = 0; i < count; ++i) {
+ Level1 l1 = (Level1) arr[i & 1023];
+ }
+ }
+
+ public void timeCheckCastLevel3ToLevel1(int count) {
+ Object[] arr = arr3;
+ for (int i = 0; i < count; ++i) {
+ Level1 l1 = (Level1) arr[i & 1023];
+ }
+ }
+
+ public void timeCheckCastLevel9ToLevel1(int count) {
+ Object[] arr = arr9;
+ for (int i = 0; i < count; ++i) {
+ Level1 l1 = (Level1) arr[i & 1023];
+ }
+ }
+
+ public void timeCheckCastLevel9ToLevel2(int count) {
+ Object[] arr = arr9;
+ for (int i = 0; i < count; ++i) {
+ Level2 l2 = (Level2) arr[i & 1023];
+ }
+ }
+
+ public void timeInstanceOfLevel1ToLevel1(int count) {
+ int sum = 0;
+ Object[] arr = arr1;
+ for (int i = 0; i < count; ++i) {
+ if (arr[i & 1023] instanceof Level1) {
+ ++sum;
+ }
+ }
+ result = sum;
+ }
+
+ public void timeInstanceOfLevel2ToLevel1(int count) {
+ int sum = 0;
+ Object[] arr = arr2;
+ for (int i = 0; i < count; ++i) {
+ if (arr[i & 1023] instanceof Level1) {
+ ++sum;
+ }
+ }
+ result = sum;
+ }
+
+ public void timeInstanceOfLevel3ToLevel1(int count) {
+ int sum = 0;
+ Object[] arr = arr3;
+ for (int i = 0; i < count; ++i) {
+ if (arr[i & 1023] instanceof Level1) {
+ ++sum;
+ }
+ }
+ result = sum;
+ }
+
+ public void timeInstanceOfLevel9ToLevel1(int count) {
+ int sum = 0;
+ Object[] arr = arr9;
+ for (int i = 0; i < count; ++i) {
+ if (arr[i & 1023] instanceof Level1) {
+ ++sum;
+ }
+ }
+ result = sum;
+ }
+
+ public void timeInstanceOfLevel9ToLevel2(int count) {
+ int sum = 0;
+ Object[] arr = arr9;
+ for (int i = 0; i < count; ++i) {
+ if (arr[i & 1023] instanceof Level2) {
+ ++sum;
+ }
+ }
+ result = sum;
+ }
+
+ public static Object[] createArray(int level) {
+ try {
+ Class<?>[] ls = {
+ null,
+ Level1.class,
+ Level2.class,
+ Level3.class,
+ Level4.class,
+ Level5.class,
+ Level6.class,
+ Level7.class,
+ Level8.class,
+ Level9.class,
+ };
+ Class<?> l = ls[level];
+ Object[] array = new Object[1024];
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = l.newInstance();
+ }
+ return array;
+ } catch (Exception unexpected) {
+ throw new Error("Initialization failure!");
+ }
+ }
+ Object[] arr1 = createArray(1);
+ Object[] arr2 = createArray(2);
+ Object[] arr3 = createArray(3);
+ Object[] arr9 = createArray(9);
+ int result;
+}
+
+class Level1 { }
+class Level2 extends Level1 { }
+class Level3 extends Level2 { }
+class Level4 extends Level3 { }
+class Level5 extends Level4 { }
+class Level6 extends Level5 { }
+class Level7 extends Level6 { }
+class Level8 extends Level7 { }
+class Level9 extends Level8 { }
diff --git a/build/Android.bp b/build/Android.bp
index c5ff486..2a5598f 100644
--- a/build/Android.bp
+++ b/build/Android.bp
@@ -4,6 +4,7 @@
deps: [
"blueprint",
"blueprint-pathtools",
+ "blueprint-proptools",
"soong",
"soong-android",
"soong-cc",
@@ -20,7 +21,6 @@
// Additional flags are computed by art.go
name: "art_defaults",
- clang: true,
cflags: [
// Base set of cflags used by all things ART.
"-fno-rtti",
@@ -68,10 +68,6 @@
cflags: [
"-DART_TARGET",
- // Enable missing-noreturn only on non-Mac. As lots of things are not implemented
- // for Apple, it's a pain.
- "-Wmissing-noreturn",
-
// To use oprofile_android --callgraph, uncomment this and recompile with
// mmma -j art
// "-fno-omit-frame-pointer",
@@ -90,19 +86,21 @@
// Apple, it's a pain.
"-Wmissing-noreturn",
],
- host_ldlibs: [
- "-lrt",
- ],
},
host: {
cflags: [
// Bug: 15446488. We don't omit the frame pointer to work around
// clang/libunwind bugs that cause SEGVs in run-test-004-ThreadStress.
"-fno-omit-frame-pointer",
- ],
- host_ldlibs: [
- "-ldl",
- "-lpthread",
+ // The build assumes that all our x86/x86_64 hosts (such as buildbots and developer
+ // desktops) support at least sse4.2/popcount. This firstly implies that the ART
+ // runtime binary itself may exploit these features. Secondly, this implies that
+ // the ART runtime passes these feature flags to dex2oat and JIT by calling the
+ // method InstructionSetFeatures::FromCppDefines(). Since invoking dex2oat directly
+ // does not pick up these flags, cross-compiling from a x86/x86_64 host to a
+ // x86/x86_64 target should not be affected.
+ "-msse4.2",
+ "-mpopcnt",
],
},
},
@@ -129,13 +127,9 @@
},
include_dirs: [
- "external/icu/icu4c/source/common",
- "external/lz4/lib",
"external/valgrind/include",
"external/valgrind",
"external/vixl/src",
- "external/zlib",
- "libnativehelper/platform_include"
],
tidy_checks: [
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index f5a95fa..0896252 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -49,6 +49,11 @@
# Enable the read barrier by default.
ART_USE_READ_BARRIER ?= true
+# Default compact dex level to none.
+ifeq ($(ART_DEFAULT_COMPACT_DEX_LEVEL),)
+ART_DEFAULT_COMPACT_DEX_LEVEL := none
+endif
+
ART_CPP_EXTENSION := .cc
ifndef LIBART_IMG_HOST_BASE_ADDRESS
diff --git a/build/Android.common_path.mk b/build/Android.common_path.mk
index f4f8d49..3247e54 100644
--- a/build/Android.common_path.mk
+++ b/build/Android.common_path.mk
@@ -84,12 +84,6 @@
HOST_CORE_DEX_FILES := $(foreach jar,$(HOST_CORE_JARS), $(call intermediates-dir-for,JAVA_LIBRARIES,$(jar),t,COMMON)/javalib.jar)
TARGET_CORE_DEX_FILES := $(foreach jar,$(TARGET_CORE_JARS),$(call intermediates-dir-for,JAVA_LIBRARIES,$(jar), ,COMMON)/javalib.jar)
-# Classpath for Jack compilation: we only need core-libart.
-HOST_JACK_CLASSPATH_DEPENDENCIES := $(call intermediates-dir-for,JAVA_LIBRARIES,core-oj-hostdex,t,COMMON)/classes.jack $(call intermediates-dir-for,JAVA_LIBRARIES,core-libart-hostdex,t,COMMON)/classes.jack
-HOST_JACK_CLASSPATH := $(abspath $(call intermediates-dir-for,JAVA_LIBRARIES,core-oj-hostdex,t,COMMON)/classes.jack):$(abspath $(call intermediates-dir-for,JAVA_LIBRARIES,core-libart-hostdex,t,COMMON)/classes.jack)
-TARGET_JACK_CLASSPATH_DEPENDENCIES := $(call intermediates-dir-for,JAVA_LIBRARIES,core-oj, ,COMMON)/classes.jack $(call intermediates-dir-for,JAVA_LIBRARIES,core-libart, ,COMMON)/classes.jack
-TARGET_JACK_CLASSPATH := $(abspath $(call intermediates-dir-for,JAVA_LIBRARIES,core-oj, ,COMMON)/classes.jack):$(abspath $(call intermediates-dir-for,JAVA_LIBRARIES,core-libart, ,COMMON)/classes.jack)
-
ART_HOST_DEX_DEPENDENCIES := $(foreach jar,$(HOST_CORE_JARS),$(HOST_OUT_JAVA_LIBRARIES)/$(jar).jar)
ART_TARGET_DEX_DEPENDENCIES := $(foreach jar,$(TARGET_CORE_JARS),$(TARGET_OUT_JAVA_LIBRARIES)/$(jar).jar)
diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk
index 1ae79ac..c508fe7 100644
--- a/build/Android.common_test.mk
+++ b/build/Android.common_test.mk
@@ -20,26 +20,16 @@
include art/build/Android.common_path.mk
# Directory used for temporary test files on the host.
-ifneq ($(TMPDIR),)
-ART_HOST_TEST_DIR := $(TMPDIR)/test-art-$(shell echo $$PPID)
-else
-# Use a BSD checksum calculated from ANDROID_BUILD_TOP and USER as one of the
-# path components for the test output. This should allow us to run tests from multiple
-# repositories at the same time.
-ART_HOST_TEST_DIR := /tmp/test-art-$(shell echo ${ANDROID_BUILD_TOP}-${USER} | sum | cut -d ' ' -f1)
-endif
+# Use a BSD checksum calculated from CWD and USER as one of the path
+# components for the test output. This should allow us to run tests from
+# multiple repositories at the same time.
+ART_TMPDIR := $(if $(TMPDIR),$(TMPDIR),/tmp)
+ART_HOST_TEST_DIR := $(ART_TMPDIR)/test-art-$(shell echo $$CWD-${USER} | sum | cut -d ' ' -f1)
# List of known broken tests that we won't attempt to execute. The test name must be the full
# rule name such as test-art-host-oat-optimizing-HelloWorld64.
ART_TEST_KNOWN_BROKEN :=
-# List of run-tests to skip running in any configuration. This needs to be the full name of the
-# run-test such as '457-regs'.
-ART_TEST_RUN_TEST_SKIP ?=
-
-# Failing valgrind tests.
-# Note: *all* 64b tests involving the runtime do not work currently. b/15170219.
-
# List of known failing tests that when executed won't cause test execution to not finish.
# The test name must be the full rule name such as test-art-host-oat-optimizing-HelloWorld64.
ART_TEST_KNOWN_FAILING :=
@@ -47,85 +37,9 @@
# Keep going after encountering a test failure?
ART_TEST_KEEP_GOING ?= true
-# Do you want all tests, even those that are time consuming?
-ART_TEST_FULL ?= false
-
# Do you want run-test to be quieter? run-tests will only show output if they fail.
ART_TEST_QUIET ?= true
-# Do you want interpreter tests run?
-ART_TEST_INTERPRETER ?= true
-ART_TEST_INTERPRETER_ACCESS_CHECKS ?= true
-
-# Do you want JIT tests run?
-ART_TEST_JIT ?= true
-
-# Do you want optimizing compiler tests run?
-ART_TEST_OPTIMIZING ?= true
-
-# Do you want to test the optimizing compiler with graph coloring register allocation?
-ART_TEST_OPTIMIZING_GRAPH_COLOR ?= $(ART_TEST_FULL)
-
-# Do you want to do run-tests with profiles?
-ART_TEST_SPEED_PROFILE ?= $(ART_TEST_FULL)
-
-# Do we want to test PIC-compiled tests ("apps")?
-ART_TEST_PIC_TEST ?= $(ART_TEST_FULL)
-
-# Do you want tracing tests run?
-ART_TEST_TRACE ?= $(ART_TEST_FULL)
-
-# Do you want tracing tests (streaming mode) run?
-ART_TEST_TRACE_STREAM ?= $(ART_TEST_FULL)
-
-# Do you want tests with GC verification enabled run?
-ART_TEST_GC_VERIFY ?= $(ART_TEST_FULL)
-
-# Do you want tests with the GC stress mode enabled run?
-ART_TEST_GC_STRESS ?= $(ART_TEST_FULL)
-
-# Do you want tests with the JNI forcecopy mode enabled run?
-ART_TEST_JNI_FORCECOPY ?= $(ART_TEST_FULL)
-
-# Do you want run-tests with relocation enabled run?
-ART_TEST_RUN_TEST_RELOCATE ?= $(ART_TEST_FULL)
-
-# Do you want run-tests with prebuilding?
-ART_TEST_RUN_TEST_PREBUILD ?= true
-
-# Do you want run-tests with no prebuilding enabled run?
-ART_TEST_RUN_TEST_NO_PREBUILD ?= $(ART_TEST_FULL)
-
-# Do you want run-tests with a pregenerated core.art?
-ART_TEST_RUN_TEST_IMAGE ?= true
-
-# Do you want run-tests without a pregenerated core.art?
-ART_TEST_RUN_TEST_NO_IMAGE ?= $(ART_TEST_FULL)
-
-# Do you want run-tests with relocation enabled but patchoat failing?
-ART_TEST_RUN_TEST_RELOCATE_NO_PATCHOAT ?= $(ART_TEST_FULL)
-
-# Do you want run-tests without a dex2oat?
-ART_TEST_RUN_TEST_NO_DEX2OAT ?= $(ART_TEST_FULL)
-
-# Do you want run-tests with libartd.so?
-ART_TEST_RUN_TEST_DEBUG ?= true
-
-# Do you want run-tests with libart.so?
-ART_TEST_RUN_TEST_NDEBUG ?= $(ART_TEST_FULL)
-
-# Do you want run-tests with the host/target's second arch?
-ART_TEST_RUN_TEST_2ND_ARCH ?= true
-
-# Do you want failed tests to have their artifacts cleaned up?
-ART_TEST_RUN_TEST_ALWAYS_CLEAN ?= true
-
-# Do you want run-tests with the --debuggable flag
-ART_TEST_RUN_TEST_DEBUGGABLE ?= $(ART_TEST_FULL)
-
-# Do you want to test multi-part boot-image functionality?
-ART_TEST_RUN_TEST_MULTI_IMAGE ?= $(ART_TEST_FULL)
-
# Define the command run on test failure. $(1) is the name of the test. Executed by the shell.
# If the test was a top-level make target (e.g. `test-art-host-gtest-codegen_test64`), the command
# fails with exit status 1 (returned by the last `grep` statement below).
@@ -202,9 +116,9 @@
# $(5): a make variable used to collate target dependencies, e.g ART_TEST_TARGET_OAT_HelloWorld_DEX
# $(6): a make variable used to collate host dependencies, e.g ART_TEST_HOST_OAT_HelloWorld_DEX
#
-# If the input test directory contains a file called main.list and main.jpp,
+# If the input test directory contains a file called main.list,
# then a multi-dex file is created passing main.list as the --main-dex-list
-# argument to dx and main.jpp for Jack.
+# argument to dx.
define build-art-test-dex
ifeq ($(ART_BUILD_TARGET),true)
include $(CLEAR_VARS)
@@ -219,7 +133,6 @@
LOCAL_DEX_PREOPT_IMAGE_LOCATION := $(TARGET_CORE_IMG_OUT)
ifneq ($(wildcard $(LOCAL_PATH)/$(2)/main.list),)
LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(LOCAL_PATH)/$(2)/main.list --minimal-main-dex
- LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true -D jack.preprocessor.file=$(LOCAL_PATH)/$(2)/main.jpp
endif
include $(BUILD_JAVA_LIBRARY)
$(5) := $$(LOCAL_INSTALLED_MODULE)
@@ -235,7 +148,6 @@
LOCAL_DEX_PREOPT_IMAGE := $(HOST_CORE_IMG_LOCATION)
ifneq ($(wildcard $(LOCAL_PATH)/$(2)/main.list),)
LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(LOCAL_PATH)/$(2)/main.list --minimal-main-dex
- LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true -D jack.preprocessor.file=$(LOCAL_PATH)/$(2)/main.jpp
endif
include $(BUILD_HOST_DALVIK_JAVA_LIBRARY)
$(6) := $$(LOCAL_INSTALLED_MODULE)
diff --git a/build/Android.cpplint.mk b/build/Android.cpplint.mk
index 66ac897..964a4c8 100644
--- a/build/Android.cpplint.mk
+++ b/build/Android.cpplint.mk
@@ -16,40 +16,46 @@
include art/build/Android.common_build.mk
-ART_CPPLINT := $(LOCAL_PATH)/tools/cpplint.py
-ART_CPPLINT_FILTER := --filter=-whitespace/line_length,-build/include,-readability/function,-readability/streams,-readability/todo,-runtime/references,-runtime/sizeof,-runtime/threadsafe_fn,-runtime/printf
-ART_CPPLINT_FLAGS := --root=$(TOP)
-ART_CPPLINT_QUIET := --quiet
-ART_CPPLINT_INGORED := \
- runtime/elf.h \
- runtime/openjdkjvmti/include/jvmti.h
+# Use upstream cpplint (toolpath from .repo/manifests/GLOBAL-PREUPLOAD.cfg).
+ART_CPPLINT := external/google-styleguide/cpplint/cpplint.py
-# This:
-# 1) Gets a list of all .h & .cc files in the art directory.
+# This file previously configured many cpplint settings.
+# Everything that could be moved to CPPLINT.cfg has moved there.
+# Please add new settings to CPPLINT.cfg over adding new flags in this file.
+
+ART_CPPLINT_FLAGS :=
+# No output when there are no errors.
+ART_CPPLINT_QUIET := --quiet
+
+# 1) Get list of all .h & .cc files in the art directory.
# 2) Prepends 'art/' to each of them to make the full name.
-# 3) removes art/runtime/elf.h from the list.
-ART_CPPLINT_SRC := $(filter-out $(patsubst %,$(LOCAL_PATH)/%,$(ART_CPPLINT_INGORED)), $(addprefix $(LOCAL_PATH)/, $(call all-subdir-named-files,*.h) $(call all-subdir-named-files,*$(ART_CPP_EXTENSION))))
+ART_CPPLINT_SRC := $(addprefix $(LOCAL_PATH)/, $(call all-subdir-named-files,*.h) $(call all-subdir-named-files,*$(ART_CPP_EXTENSION)))
+
+# 1) Get list of all CPPLINT.cfg files in the art directory.
+# 2) Prepends 'art/' to each of them to make the full name.
+ART_CPPLINT_CFG := $(addprefix $(LOCAL_PATH)/, $(call all-subdir-named-files,CPPLINT.cfg))
# "mm cpplint-art" to verify we aren't regressing
+# - files not touched since the last build are skipped (quite fast).
.PHONY: cpplint-art
-cpplint-art:
- $(ART_CPPLINT) $(ART_CPPLINT_FLAGS) $(ART_CPPLINT_FILTER) $(ART_CPPLINT_SRC)
+cpplint-art: cpplint-art-phony
-# "mm cpplint-art-all" to see all warnings
+# "mm cpplint-art-all" to manually execute cpplint.py on all files (very slow).
.PHONY: cpplint-art-all
cpplint-art-all:
$(ART_CPPLINT) $(ART_CPPLINT_FLAGS) $(ART_CPPLINT_SRC)
OUT_CPPLINT := $(TARGET_COMMON_OUT_ROOT)/cpplint
+# Build up the list of all targets for linting the ART source files.
ART_CPPLINT_TARGETS :=
define declare-art-cpplint-target
art_cpplint_file := $(1)
art_cpplint_touch := $$(OUT_CPPLINT)/$$(subst /,__,$$(art_cpplint_file))
-$$(art_cpplint_touch): $$(art_cpplint_file) $(ART_CPPLINT) art/build/Android.cpplint.mk
- $(hide) $(ART_CPPLINT) $(ART_CPPLINT_QUIET) $(ART_CPPLINT_FLAGS) $(ART_CPPLINT_FILTER) $$<
+$$(art_cpplint_touch): $$(art_cpplint_file) $(ART_CPPLINT) $(ART_CPPLINT_CFG) art/build/Android.cpplint.mk
+ $(hide) $(ART_CPPLINT) $(ART_CPPLINT_QUIET) $(ART_CPPLINT_FLAGS) $$<
$(hide) mkdir -p $$(dir $$@)
$(hide) touch $$@
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 571c91a..b483e5f 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -36,6 +36,8 @@
ForClassLoaderD \
ExceptionHandle \
GetMethodSignature \
+ HiddenApi \
+ HiddenApiSignatures \
ImageLayoutA \
ImageLayoutB \
IMTA \
@@ -72,6 +74,21 @@
ART_TEST_HOST_GTEST_MainStripped_DEX := $(basename $(ART_TEST_HOST_GTEST_Main_DEX))Stripped$(suffix $(ART_TEST_HOST_GTEST_Main_DEX))
ART_TEST_TARGET_GTEST_MainStripped_DEX := $(basename $(ART_TEST_TARGET_GTEST_Main_DEX))Stripped$(suffix $(ART_TEST_TARGET_GTEST_Main_DEX))
+# Create rules for MainUncompressed, a copy of Main with the classes.dex uncompressed
+# for the dex2oat tests.
+ART_TEST_HOST_GTEST_MainUncompressed_DEX := $(basename $(ART_TEST_HOST_GTEST_Main_DEX))Uncompressed$(suffix $(ART_TEST_HOST_GTEST_Main_DEX))
+ART_TEST_TARGET_GTEST_MainUncompressed_DEX := $(basename $(ART_TEST_TARGET_GTEST_Main_DEX))Uncompressed$(suffix $(ART_TEST_TARGET_GTEST_Main_DEX))
+
+# Create rules for UncompressedEmpty, a classes.dex that is empty and uncompressed
+# for the dex2oat tests.
+ART_TEST_HOST_GTEST_EmptyUncompressed_DEX := $(basename $(ART_TEST_HOST_GTEST_Main_DEX))EmptyUncompressed$(suffix $(ART_TEST_HOST_GTEST_Main_DEX))
+ART_TEST_TARGET_GTEST_EmptyUncompressed_DEX := $(basename $(ART_TEST_TARGET_GTEST_Main_DEX))EmptyUncompressed$(suffix $(ART_TEST_TARGET_GTEST_Main_DEX))
+
+# Create rules for MultiDexUncompressed, a copy of MultiDex with the classes.dex uncompressed
+# for the OatFile tests.
+ART_TEST_HOST_GTEST_MultiDexUncompressed_DEX := $(basename $(ART_TEST_HOST_GTEST_MultiDex_DEX))Uncompressed$(suffix $(ART_TEST_HOST_GTEST_MultiDex_DEX))
+ART_TEST_TARGET_GTEST_MultiDexUncompressed_DEX := $(basename $(ART_TEST_TARGET_GTEST_MultiDex_DEX))Uncompressed$(suffix $(ART_TEST_TARGET_GTEST_MultiDex_DEX))
+
$(ART_TEST_HOST_GTEST_MainStripped_DEX): $(ART_TEST_HOST_GTEST_Main_DEX)
cp $< $@
$(call dexpreopt-remove-classes.dex,$@)
@@ -80,6 +97,36 @@
cp $< $@
$(call dexpreopt-remove-classes.dex,$@)
+$(ART_TEST_HOST_GTEST_MainUncompressed_DEX): $(ART_TEST_HOST_GTEST_Main_DEX) $(ZIPALIGN)
+ cp $< $@
+ $(call uncompress-dexs, $@)
+ $(call align-package, $@)
+
+$(ART_TEST_TARGET_GTEST_MainUncompressed_DEX): $(ART_TEST_TARGET_GTEST_Main_DEX) $(ZIPALIGN)
+ cp $< $@
+ $(call uncompress-dexs, $@)
+ $(call align-package, $@)
+
+$(ART_TEST_HOST_GTEST_EmptyUncompressed_DEX): $(ZIPALIGN)
+ touch $(dir $@)classes.dex
+ zip -j -qD -X -0 $@ $(dir $@)classes.dex
+ rm $(dir $@)classes.dex
+
+$(ART_TEST_TARGET_GTEST_EmptyUncompressed_DEX): $(ZIPALIGN)
+ touch $(dir $@)classes.dex
+ zip -j -qD -X -0 $@ $(dir $@)classes.dex
+ rm $(dir $@)classes.dex
+
+$(ART_TEST_HOST_GTEST_MultiDexUncompressed_DEX): $(ART_TEST_HOST_GTEST_MultiDex_DEX) $(ZIPALIGN)
+ cp $< $@
+ $(call uncompress-dexs, $@)
+ $(call align-package, $@)
+
+$(ART_TEST_TARGET_GTEST_MultiDexUncompressed_DEX): $(ART_TEST_TARGET_GTEST_MultiDex_DEX) $(ZIPALIGN)
+ cp $< $@
+ $(call uncompress-dexs, $@)
+ $(call align-package, $@)
+
ART_TEST_GTEST_VerifierDeps_SRC := $(abspath $(wildcard $(LOCAL_PATH)/VerifierDeps/*.smali))
ART_TEST_GTEST_VerifierDepsMulti_SRC := $(abspath $(wildcard $(LOCAL_PATH)/VerifierDepsMulti/*.smali))
ART_TEST_HOST_GTEST_VerifierDeps_DEX := $(dir $(ART_TEST_HOST_GTEST_Main_DEX))$(subst Main,VerifierDeps,$(basename $(notdir $(ART_TEST_HOST_GTEST_Main_DEX))))$(suffix $(ART_TEST_HOST_GTEST_Main_DEX))
@@ -100,7 +147,8 @@
$(HOST_OUT_EXECUTABLES)/smali assemble --output $@ $(filter %.smali,$^)
# Dex file dependencies for each gtest.
-ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested
+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
ART_GTEST_atomic_dex_ref_map_test_DEX_DEPS := Interfaces
ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB ErroneousInit ForClassLoaderA ForClassLoaderB ForClassLoaderC ForClassLoaderD Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode
@@ -108,11 +156,12 @@
ART_GTEST_class_table_test_DEX_DEPS := XandY
ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex
ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes
-ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested MultiDex
ART_GTEST_dexlayout_test_DEX_DEPS := ManyMethods
-ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics VerifierDeps
+ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) ManyMethods Statics VerifierDeps MainUncompressed EmptyUncompressed
ART_GTEST_dex2oat_image_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics VerifierDeps
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_imtable_test_DEX_DEPS := IMTA IMTB
ART_GTEST_instrumentation_test_DEX_DEPS := Instrumentation
@@ -121,9 +170,11 @@
ART_GTEST_oat_file_assistant_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
ART_GTEST_dexoptanalyzer_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
ART_GTEST_image_space_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
-ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex
+ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex MainUncompressed MultiDexUncompressed
ART_GTEST_oat_test_DEX_DEPS := Main
+ART_GTEST_oat_writer_test_DEX_DEPS := Main
ART_GTEST_object_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY
+ART_GTEST_patchoat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
ART_GTEST_proxy_test_DEX_DEPS := Interfaces
ART_GTEST_reflection_test_DEX_DEPS := Main NonStaticLeafMethods StaticLeafMethods
ART_GTEST_profile_assistant_test_DEX_DEPS := ProfileTestMultiDex
@@ -136,6 +187,7 @@
ART_GTEST_heap_verification_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY
ART_GTEST_verifier_deps_test_DEX_DEPS := VerifierDeps VerifierDepsMulti MultiDex
ART_GTEST_dex_to_dex_decompiler_test_DEX_DEPS := VerifierDeps DexToDexDecompiler
+ART_GTEST_oatdump_app_test_DEX_DEPS := ProfileTestMultiDex
# The elf writer test has dependencies on core.oat.
ART_GTEST_elf_writer_test_HOST_DEPS := $(HOST_CORE_IMAGE_DEFAULT_64) $(HOST_CORE_IMAGE_DEFAULT_32)
@@ -146,13 +198,18 @@
$(HOST_CORE_IMAGE_optimizing_32) \
$(HOST_CORE_IMAGE_interpreter_64) \
$(HOST_CORE_IMAGE_interpreter_32) \
- $(HOST_OUT_EXECUTABLES)/patchoatd
+ patchoatd-host
ART_GTEST_dex2oat_environment_tests_TARGET_DEPS := \
$(TARGET_CORE_IMAGE_optimizing_64) \
$(TARGET_CORE_IMAGE_optimizing_32) \
$(TARGET_CORE_IMAGE_interpreter_64) \
$(TARGET_CORE_IMAGE_interpreter_32) \
- $(TARGET_OUT_EXECUTABLES)/patchoatd
+ patchoatd-target
+
+ART_GTEST_oat_file_test_HOST_DEPS := \
+ $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS)
+ART_GTEST_oat_file_test_TARGET_DEPS := \
+ $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS)
ART_GTEST_oat_file_assistant_test_HOST_DEPS := \
$(ART_GTEST_dex2oat_environment_tests_HOST_DEPS)
@@ -161,10 +218,10 @@
ART_GTEST_dexoptanalyzer_test_HOST_DEPS := \
$(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) \
- $(HOST_OUT_EXECUTABLES)/dexoptanalyzerd
+ dexoptanalyzerd-host
ART_GTEST_dexoptanalyzer_test_TARGET_DEPS := \
$(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) \
- dexoptanalyzerd
+ dexoptanalyzerd-target
ART_GTEST_image_space_test_HOST_DEPS := \
$(ART_GTEST_dex2oat_environment_tests_HOST_DEPS)
@@ -172,57 +229,59 @@
$(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS)
ART_GTEST_dex2oat_test_HOST_DEPS := \
- $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS)
+ $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) \
+ dex2oatd-host
ART_GTEST_dex2oat_test_TARGET_DEPS := \
- $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS)
+ $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) \
+ dex2oatd-target
ART_GTEST_dex2oat_image_test_HOST_DEPS := \
- $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS)
+ $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) \
+ dex2oatd-host
ART_GTEST_dex2oat_image_test_TARGET_DEPS := \
- $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS)
+ $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) \
+ dex2oatd-target
# TODO: document why this is needed.
ART_GTEST_proxy_test_HOST_DEPS := $(HOST_CORE_IMAGE_DEFAULT_64) $(HOST_CORE_IMAGE_DEFAULT_32)
# The dexdiag test requires the dexdiag utility.
-ART_GTEST_dexdiag_test_HOST_DEPS := \
- $(HOST_OUT_EXECUTABLES)/dexdiag
-ART_GTEST_dexdiag_test_TARGET_DEPS := \
- dexdiag
+ART_GTEST_dexdiag_test_HOST_DEPS := dexdiag-host
+ART_GTEST_dexdiag_test_TARGET_DEPS := dexdiag-target
# The dexdump test requires an image and the dexdump utility.
# TODO: rename into dexdump when migration completes
ART_GTEST_dexdump_test_HOST_DEPS := \
$(HOST_CORE_IMAGE_DEFAULT_64) \
$(HOST_CORE_IMAGE_DEFAULT_32) \
- $(HOST_OUT_EXECUTABLES)/dexdump2
+ dexdump2-host
ART_GTEST_dexdump_test_TARGET_DEPS := \
$(TARGET_CORE_IMAGE_DEFAULT_64) \
$(TARGET_CORE_IMAGE_DEFAULT_32) \
- dexdump2
+ dexdump2-target
# The dexlayout test requires an image and the dexlayout utility.
# TODO: rename into dexdump when migration completes
ART_GTEST_dexlayout_test_HOST_DEPS := \
$(HOST_CORE_IMAGE_DEFAULT_64) \
$(HOST_CORE_IMAGE_DEFAULT_32) \
- $(HOST_OUT_EXECUTABLES)/dexlayout \
- $(HOST_OUT_EXECUTABLES)/dexdump2
+ dexlayoutd-host \
+ dexdump2-host
ART_GTEST_dexlayout_test_TARGET_DEPS := \
$(TARGET_CORE_IMAGE_DEFAULT_64) \
$(TARGET_CORE_IMAGE_DEFAULT_32) \
- dexlayout \
- dexdump2
+ dexlayoutd-target \
+ dexdump2-target
# The dexlist test requires an image and the dexlist utility.
ART_GTEST_dexlist_test_HOST_DEPS := \
$(HOST_CORE_IMAGE_DEFAULT_64) \
$(HOST_CORE_IMAGE_DEFAULT_32) \
- $(HOST_OUT_EXECUTABLES)/dexlist
+ dexlist-host
ART_GTEST_dexlist_test_TARGET_DEPS := \
$(TARGET_CORE_IMAGE_DEFAULT_64) \
$(TARGET_CORE_IMAGE_DEFAULT_32) \
- dexlist
+ dexlist-target
# The imgdiag test has dependencies on core.oat since it needs to load it during the test.
# For the host, also add the installed tool (in the base size, that should suffice). For the
@@ -230,30 +289,45 @@
ART_GTEST_imgdiag_test_HOST_DEPS := \
$(HOST_CORE_IMAGE_DEFAULT_64) \
$(HOST_CORE_IMAGE_DEFAULT_32) \
- $(HOST_OUT_EXECUTABLES)/imgdiagd
+ imgdiagd-host
ART_GTEST_imgdiag_test_TARGET_DEPS := \
$(TARGET_CORE_IMAGE_DEFAULT_64) \
$(TARGET_CORE_IMAGE_DEFAULT_32) \
- imgdiagd
+ imgdiagd-target
# Oatdump test requires an image and oatfile to dump.
ART_GTEST_oatdump_test_HOST_DEPS := \
$(HOST_CORE_IMAGE_DEFAULT_64) \
$(HOST_CORE_IMAGE_DEFAULT_32) \
- $(HOST_OUT_EXECUTABLES)/oatdumpd \
- $(HOST_OUT_EXECUTABLES)/oatdumpds
+ oatdumpd-host \
+ oatdumpds-host \
+ dexdump2-host
ART_GTEST_oatdump_test_TARGET_DEPS := \
$(TARGET_CORE_IMAGE_DEFAULT_64) \
$(TARGET_CORE_IMAGE_DEFAULT_32) \
- oatdump
+ oatdumpd-target \
+ dexdump2-target
ART_GTEST_oatdump_image_test_HOST_DEPS := $(ART_GTEST_oatdump_test_HOST_DEPS)
ART_GTEST_oatdump_image_test_TARGET_DEPS := $(ART_GTEST_oatdump_test_TARGET_DEPS)
+ART_GTEST_oatdump_app_test_HOST_DEPS := $(ART_GTEST_oatdump_test_HOST_DEPS) \
+ dex2oatd-host \
+ dex2oatds-host
+ART_GTEST_oatdump_app_test_TARGET_DEPS := $(ART_GTEST_oatdump_test_TARGET_DEPS) \
+ dex2oatd-target
+
+ART_GTEST_patchoat_test_HOST_DEPS := \
+ $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS)
+ART_GTEST_patchoat_test_TARGET_DEPS := \
+ $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS)
# Profile assistant tests requires profman utility.
-ART_GTEST_profile_assistant_test_HOST_DEPS := \
- $(HOST_OUT_EXECUTABLES)/profmand
-ART_GTEST_profile_assistant_test_TARGET_DEPS := \
- profman
+ART_GTEST_profile_assistant_test_HOST_DEPS := profmand-host
+ART_GTEST_profile_assistant_test_TARGET_DEPS := profmand-target
+
+ART_GTEST_hiddenapi_test_HOST_DEPS := \
+ $(HOST_CORE_IMAGE_DEFAULT_64) \
+ $(HOST_CORE_IMAGE_DEFAULT_32) \
+ hiddenapid-host
# The path for which all the source files are relative, not actually the current directory.
LOCAL_PATH := art
@@ -268,11 +342,16 @@
art_dexlayout_tests \
art_dexlist_tests \
art_dexoptanalyzer_tests \
+ art_hiddenapi_tests \
art_imgdiag_tests \
+ art_libartbase_tests \
+ art_libdexfile_tests \
art_oatdump_tests \
+ art_patchoat_tests \
art_profman_tests \
art_runtime_tests \
art_runtime_compiler_tests \
+ art_sigchain_tests \
ART_TARGET_GTEST_FILES := $(foreach m,$(ART_TEST_MODULES),\
$(ART_TEST_LIST_device_$(TARGET_ARCH)_$(m)))
@@ -310,19 +389,31 @@
ART_GTEST_TARGET_ANDROID_ROOT := $(ART_TEST_ANDROID_ROOT)
endif
-ART_VALGRIND_TARGET_DEPENDENCIES := \
+ART_VALGRIND_TARGET_DEPENDENCIES :=
+
+# Has to match list in external/valgrind/Android.build_one.mk
+ART_VALGRIND_SUPPORTED_ARCH := arm arm64 x86_64
+
+# Valgrind is not supported for x86
+ifneq (,$(filter $(ART_VALGRIND_SUPPORTED_ARCH),$(TARGET_ARCH)))
+art_vg_arch := $(if $(filter x86_64,$(TARGET_ARCH)),amd64,$(TARGET_ARCH))
+ART_VALGRIND_TARGET_DEPENDENCIES += \
$(TARGET_OUT_EXECUTABLES)/valgrind \
- $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/memcheck-$(TARGET_ARCH)-linux \
- $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_core-$(TARGET_ARCH)-linux.so \
- $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_memcheck-$(TARGET_ARCH)-linux.so \
+ $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/memcheck-$(art_vg_arch)-linux \
+ $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_core-$(art_vg_arch)-linux.so \
+ $(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_memcheck-$(art_vg_arch)-linux.so \
$(TARGET_OUT_SHARED_LIBRARIES)/valgrind/default.supp
+art_vg_arch :=
+endif
ifdef TARGET_2ND_ARCH
+ifneq (,$(filter $(ART_VALGRIND_SUPPORTED_ARCH),$(TARGET_2ND_ARCH)))
ART_VALGRIND_TARGET_DEPENDENCIES += \
$(TARGET_OUT_SHARED_LIBRARIES)/valgrind/memcheck-$(TARGET_2ND_ARCH)-linux \
$(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_core-$(TARGET_2ND_ARCH)-linux.so \
$(TARGET_OUT_SHARED_LIBRARIES)/valgrind/vgpreload_memcheck-$(TARGET_2ND_ARCH)-linux.so
endif
+endif
include $(CLEAR_VARS)
LOCAL_MODULE := valgrind-target-suppressions.txt
@@ -362,11 +453,12 @@
$(hide) adb shell rm $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID
$(hide) adb shell chmod 755 $$(PRIVATE_TARGET_EXE)
$(hide) $$(call ART_TEST_SKIP,$$@) && \
- (adb shell "$(GCOV_ENV) LD_LIBRARY_PATH=$(4) ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) \
- $$(PRIVATE_TARGET_EXE) && touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID" \
- && (adb pull $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID /tmp/ \
- && $$(call ART_TEST_PASSED,$$@)) \
- || $$(call ART_TEST_FAILED,$$@))
+ (adb shell "env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \
+ ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) $$(PRIVATE_TARGET_EXE) \
+ && touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID" \
+ && (adb pull $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID /tmp/ \
+ && $$(call ART_TEST_PASSED,$$@)) \
+ || $$(call ART_TEST_FAILED,$$@))
$(hide) rm -f /tmp/$$@-$$$$PPID
ART_TEST_TARGET_GTEST$($(3)ART_PHONY_TEST_TARGET_SUFFIX)_RULES += $$(gtest_rule)
@@ -379,17 +471,20 @@
$(hide) adb shell rm $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID
$(hide) adb shell chmod 755 $$(PRIVATE_TARGET_EXE)
$(hide) $$(call ART_TEST_SKIP,$$@) && \
- (adb shell "$(GCOV_ENV) LD_LIBRARY_PATH=$(4) ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) \
- valgrind --leak-check=full --error-exitcode=1 --workaround-gcc296-bugs=yes \
- --suppressions=$(ART_TARGET_TEST_DIR)/valgrind-target-suppressions.txt \
- --num-callers=50 --show-mismatched-frees=no \
- $$(PRIVATE_TARGET_EXE) && touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID" \
- && (adb pull $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID /tmp/ \
- && $$(call ART_TEST_PASSED,$$@)) \
- || $$(call ART_TEST_FAILED,$$@))
+ (adb shell "env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \
+ ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) \
+ $$$$ANDROID_ROOT/bin/valgrind \
+ --leak-check=full --error-exitcode=1 --workaround-gcc296-bugs=yes \
+ --suppressions=$(ART_TARGET_TEST_DIR)/valgrind-target-suppressions.txt \
+ --num-callers=50 --show-mismatched-frees=no $$(PRIVATE_TARGET_EXE) \
+ && touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID" \
+ && (adb pull $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID /tmp/ \
+ && $$(call ART_TEST_PASSED,$$@)) \
+ || $$(call ART_TEST_FAILED,$$@))
$(hide) rm -f /tmp/$$@-$$$$PPID
- ART_TEST_TARGET_VALGRIND_GTEST$$($(3)ART_PHONY_TEST_TARGET_SUFFIX)_RULES += valgrind-$$(gtest_rule)
+ ART_TEST_TARGET_VALGRIND_GTEST$$($(3)ART_PHONY_TEST_TARGET_SUFFIX)_RULES += \
+ valgrind-$$(gtest_rule)
ART_TEST_TARGET_VALGRIND_GTEST_RULES += valgrind-$$(gtest_rule)
ART_TEST_TARGET_VALGRIND_GTEST_$(1)_RULES += valgrind-$$(gtest_rule)
@@ -424,16 +519,37 @@
$$(gtest_exe) \
$$(ART_GTEST_$(1)_HOST_DEPS) \
$(foreach file,$(ART_GTEST_$(1)_DEX_DEPS),$(ART_TEST_HOST_GTEST_$(file)_DEX))
+ ifneq (,$(DIST_DIR))
+ gtest_xml_output := --gtest_output=xml:$(DIST_DIR)/gtest/$(1)$$($(3)ART_PHONY_TEST_HOST_SUFFIX).xml
+ else
+ gtest_xml_output :=
+ endif
ART_TEST_HOST_GTEST_DEPENDENCIES += $$(gtest_deps)
+.PHONY: $$(gtest_rule)
+ifeq (,$(SANITIZE_HOST))
+$$(gtest_rule): PRIVATE_XML_OUTPUT := $$(gtest_xml_output)
+$$(gtest_rule): $$(gtest_exe) $$(gtest_deps)
+ $(hide) ($$(call ART_TEST_SKIP,$$@) && $$< $$(PRIVATE_XML_OUTPUT) && \
+ $$(call ART_TEST_PASSED,$$@)) || $$(call ART_TEST_FAILED,$$@)
+else
# Note: envsetup currently exports ASAN_OPTIONS=detect_leaks=0 to suppress leak detection, as some
# build tools (e.g., ninja) intentionally leak. We want leak checks when we run our tests, so
# override ASAN_OPTIONS. b/37751350
-.PHONY: $$(gtest_rule)
+# Note 2: Under sanitization, also capture the output, and run it through the stack tool on failure
+# (with the x86-64 ABI, as this allows symbolization of both x86 and x86-64). We don't do this in
+# general as it loses all the color output, and we have our own symbolization step when not running
+# under ASAN.
+$$(gtest_rule): PRIVATE_XML_OUTPUT := $$(gtest_xml_output)
$$(gtest_rule): $$(gtest_exe) $$(gtest_deps)
- $(hide) ($$(call ART_TEST_SKIP,$$@) && ASAN_OPTIONS=detect_leaks=1 $$< && \
- $$(call ART_TEST_PASSED,$$@)) || $$(call ART_TEST_FAILED,$$@)
+ $(hide) ($$(call ART_TEST_SKIP,$$@) && set -o pipefail && \
+ ASAN_OPTIONS=detect_leaks=1 $$< $$(PRIVATE_XML_OUTPUT) 2>&1 | tee $$<.tmp.out >&2 && \
+ { $$(call ART_TEST_PASSED,$$@) ; rm $$<.tmp.out ; }) || \
+ ( grep -q AddressSanitizer $$<.tmp.out && export ANDROID_BUILD_TOP=`pwd` && \
+ { echo "ABI: 'x86_64'" | cat - $$<.tmp.out | development/scripts/stack | tail -n 3000 ; } ; \
+ rm $$<.tmp.out ; $$(call ART_TEST_FAILED,$$@))
+endif
ART_TEST_HOST_GTEST$$($(3)ART_PHONY_TEST_HOST_SUFFIX)_RULES += $$(gtest_rule)
ART_TEST_HOST_GTEST_RULES += $$(gtest_rule)
@@ -604,7 +720,7 @@
endif
.PHONY: $$(rule_name)
-$$(rule_name): $$(dependencies)
+$$(rule_name): $$(dependencies) dx d8-compat-dx desugar
$(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@)
# Clear locally defined variables.
@@ -679,6 +795,9 @@
ART_GTEST_dex2oat_image_test_HOST_DEPS :=
ART_GTEST_dex2oat_image_test_TARGET_DEPS :=
ART_GTEST_object_test_DEX_DEPS :=
+ART_GTEST_patchoat_test_DEX_DEPS :=
+ART_GTEST_patchoat_test_HOST_DEPS :=
+ART_GTEST_patchoat_test_TARGET_DEPS :=
ART_GTEST_proxy_test_DEX_DEPS :=
ART_GTEST_reflection_test_DEX_DEPS :=
ART_GTEST_stub_test_DEX_DEPS :=
@@ -692,6 +811,10 @@
$(foreach dir,$(GTEST_DEX_DIRECTORIES), $(eval ART_TEST_HOST_GTEST_$(dir)_DEX :=))
ART_TEST_HOST_GTEST_MainStripped_DEX :=
ART_TEST_TARGET_GTEST_MainStripped_DEX :=
+ART_TEST_HOST_GTEST_MainUncompressed_DEX :=
+ART_TEST_TARGET_GTEST_MainUncompressed_DEX :=
+ART_TEST_HOST_GTEST_EmptyUncompressed_DEX :=
+ART_TEST_TARGET_GTEST_EmptyUncompressed_DEX :=
ART_TEST_GTEST_VerifierDeps_SRC :=
ART_TEST_HOST_GTEST_VerifierDeps_DEX :=
ART_TEST_TARGET_GTEST_VerifierDeps_DEX :=
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index 3f9ea15..517ac5c 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -111,6 +111,7 @@
$$(LOCAL_$(2)DEX2OAT_HOST_INSTRUCTION_SET_FEATURES_OPTION) \
--host --android-root=$$(HOST_OUT) \
--generate-debug-info --generate-build-id --compile-pic \
+ --runtime-arg -XX:SlowDebug=true \
$$(PRIVATE_CORE_MULTI_PARAM) $$(PRIVATE_CORE_COMPILE_OPTIONS)
$$(core_oat_name): $$(core_image_name)
@@ -214,6 +215,7 @@
--instruction-set-features=$$($(2)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) \
--android-root=$$(PRODUCT_OUT)/system \
--generate-debug-info --generate-build-id --compile-pic \
+ --runtime-arg -XX:SlowDebug=true \
$$(PRIVATE_CORE_COMPILE_OPTIONS) || (rm $$(PRIVATE_CORE_OAT_NAME); exit 1)
$$(core_oat_name): $$(core_image_name)
diff --git a/build/art.go b/build/art.go
index 6c9aa89..59480a0 100644
--- a/build/art.go
+++ b/build/art.go
@@ -19,6 +19,8 @@
"android/soong/cc"
"fmt"
"sync"
+
+ "github.com/google/blueprint/proptools"
)
var supportedArches = []string{"arm", "arm64", "mips", "mips64", "x86", "x86_64"}
@@ -44,10 +46,6 @@
cflags = append(cflags, "-DART_USE_TLAB=1")
}
- if !envFalse(ctx, "ART_ENABLE_VDEX") {
- cflags = append(cflags, "-DART_ENABLE_VDEX")
- }
-
imtSize := envDefault(ctx, "ART_IMT_SIZE", "43")
cflags = append(cflags, "-DIMT_SIZE="+imtSize)
@@ -68,6 +66,9 @@
"-DART_READ_BARRIER_TYPE_IS_"+barrierType+"=1")
}
+ cdexLevel := envDefault(ctx, "ART_DEFAULT_COMPACT_DEX_LEVEL", "fast")
+ cflags = append(cflags, "-DART_DEFAULT_COMPACT_DEX_LEVEL="+cdexLevel)
+
// We need larger stack overflow guards for ASAN, as the compiled code will have
// larger frame sizes. For simplicity, just use global not-target-specific cflags.
// Note: We increase this for both debug and non-debug, as the overflow gap will
@@ -97,6 +98,15 @@
asflags = append(asflags, "-DART_ENABLE_ADDRESS_SANITIZER=1")
}
+ if envTrue(ctx, "ART_MIPS32_CHECK_ALIGNMENT") {
+ // Enable the use of MIPS32 CHECK_ALIGNMENT macro for debugging purposes
+ asflags = append(asflags, "-DART_MIPS32_CHECK_ALIGNMENT")
+ }
+
+ if envTrueOrDefault(ctx, "USE_D8_DESUGAR") {
+ cflags = append(cflags, "-DUSE_D8_DESUGAR=1")
+ }
+
return cflags, asflags
}
@@ -153,6 +163,11 @@
cflags = append(cflags, "-DART_BASE_ADDRESS_MIN_DELTA="+minDelta)
cflags = append(cflags, "-DART_BASE_ADDRESS_MAX_DELTA="+maxDelta)
+ if len(ctx.AConfig().SanitizeHost()) > 0 && !envFalse(ctx, "ART_ENABLE_ADDRESS_SANITIZER") {
+ // We enable full sanitization on the host by default.
+ cflags = append(cflags, "-DART_ENABLE_ADDRESS_SANITIZER=1")
+ }
+
return cflags
}
@@ -200,31 +215,33 @@
func customLinker(ctx android.LoadHookContext) {
linker := envDefault(ctx, "CUSTOM_TARGET_LINKER", "")
- if linker != "" {
- type props struct {
- DynamicLinker string
- }
-
- p := &props{}
- p.DynamicLinker = linker
- ctx.AppendProperties(p)
+ type props struct {
+ DynamicLinker string
}
+
+ p := &props{}
+ if linker != "" {
+ p.DynamicLinker = linker
+ }
+
+ ctx.AppendProperties(p)
}
func prefer32Bit(ctx android.LoadHookContext) {
- if envTrue(ctx, "HOST_PREFER_32_BIT") {
- type props struct {
- Target struct {
- Host struct {
- Compile_multilib string
- }
+ type props struct {
+ Target struct {
+ Host struct {
+ Compile_multilib *string
}
}
-
- p := &props{}
- p.Target.Host.Compile_multilib = "prefer32"
- ctx.AppendProperties(p)
}
+
+ p := &props{}
+ if envTrue(ctx, "HOST_PREFER_32_BIT") {
+ p.Target.Host.Compile_multilib = proptools.StringPtr("prefer32")
+ }
+
+ ctx.AppendProperties(p)
}
func testMap(config android.Config) map[string][]string {
@@ -256,6 +273,7 @@
func init() {
android.RegisterModuleType("art_cc_library", artLibrary)
+ android.RegisterModuleType("art_cc_static_library", artStaticLibrary)
android.RegisterModuleType("art_cc_binary", artBinary)
android.RegisterModuleType("art_cc_test", artTest)
android.RegisterModuleType("art_cc_test_library", artTestLibrary)
@@ -287,8 +305,18 @@
}
func artLibrary() android.Module {
- library, _ := cc.NewLibrary(android.HostAndDeviceSupported)
- module := library.Init()
+ m, _ := cc.NewLibrary(android.HostAndDeviceSupported)
+ module := m.Init()
+
+ installCodegenCustomizer(module, true)
+
+ return module
+}
+
+func artStaticLibrary() android.Module {
+ m, library := cc.NewLibrary(android.HostAndDeviceSupported)
+ library.BuildOnlyStatic()
+ module := m.Init()
installCodegenCustomizer(module, true)
@@ -342,3 +370,7 @@
func envFalse(ctx android.BaseContext, key string) bool {
return ctx.AConfig().Getenv(key) == "false"
}
+
+func envTrueOrDefault(ctx android.BaseContext, key string) bool {
+ return ctx.AConfig().Getenv(key) != "false"
+}
diff --git a/cmdline/Android.bp b/cmdline/Android.bp
index c811cbd..b46e987 100644
--- a/cmdline/Android.bp
+++ b/cmdline/Android.bp
@@ -14,6 +14,13 @@
// limitations under the License.
//
+// TODO: this header library depends on libart. Find a way to express that.
+cc_library_headers {
+ name: "art_cmdlineparser_headers",
+ host_supported: true,
+ export_include_dirs: ["."],
+}
+
art_cc_test {
name: "art_cmdline_tests",
defaults: [
diff --git a/cmdline/cmdline.h b/cmdline/cmdline.h
index 18ca944..95ab123 100644
--- a/cmdline/cmdline.h
+++ b/cmdline/cmdline.h
@@ -26,7 +26,9 @@
#include "android-base/stringprintf.h"
+#include "base/file_utils.h"
#include "base/logging.h"
+#include "base/mutex.h"
#include "base/stringpiece.h"
#include "noop_compiler_callbacks.h"
#include "runtime.h"
@@ -78,6 +80,7 @@
*filename = cache_filename;
return true;
} else {
+ *filename = system_filename;
return false;
}
}
@@ -146,7 +149,7 @@
} else if (option.starts_with("--instruction-set=")) {
StringPiece instruction_set_str = option.substr(strlen("--instruction-set=")).data();
instruction_set_ = GetInstructionSetFromString(instruction_set_str.data());
- if (instruction_set_ == kNone) {
+ if (instruction_set_ == InstructionSet::kNone) {
fprintf(stderr, "Unsupported instruction set %s\n", instruction_set_str.data());
PrintUsage();
return false;
@@ -217,7 +220,7 @@
// Specified by --boot-image.
const char* boot_image_location_ = nullptr;
// Specified by --instruction-set.
- InstructionSet instruction_set_ = kRuntimeISA;
+ InstructionSet instruction_set_ = InstructionSet::kNone;
// Specified by --output.
std::ostream* os_ = &std::cout;
std::unique_ptr<std::ofstream> out_; // If something besides cout is used
@@ -230,6 +233,10 @@
*error_msg = "--boot-image must be specified";
return false;
}
+ if (instruction_set_ == InstructionSet::kNone) {
+ LOG(WARNING) << "No instruction set given, assuming " << GetInstructionSetString(kRuntimeISA);
+ instruction_set_ = kRuntimeISA;
+ }
DBG_LOG << "boot image location: " << boot_image_location_;
@@ -257,7 +264,7 @@
DBG_LOG << "boot_image_location parent_dir_name was " << parent_dir_name;
- if (GetInstructionSetFromString(parent_dir_name.c_str()) != kNone) {
+ if (GetInstructionSetFromString(parent_dir_name.c_str()) != InstructionSet::kNone) {
*error_msg = "Do not specify the architecture as part of the boot image location";
return false;
}
@@ -266,8 +273,10 @@
// Check that the boot image location points to a valid file name.
std::string file_name;
if (!LocationToFilename(boot_image_location, instruction_set_, &file_name)) {
- *error_msg = android::base::StringPrintf("No corresponding file for location '%s' exists",
- boot_image_location.c_str());
+ *error_msg = android::base::StringPrintf(
+ "No corresponding file for location '%s' (filename '%s') exists",
+ boot_image_location.c_str(),
+ file_name.c_str());
return false;
}
@@ -295,6 +304,7 @@
template <typename Args = CmdlineArgs>
struct CmdlineMain {
int Main(int argc, char** argv) {
+ Locks::Init();
InitLogging(argv, Runtime::Abort);
std::unique_ptr<Args> args = std::unique_ptr<Args>(CreateArguments());
args_ = args.get();
diff --git a/cmdline/cmdline_parser.h b/cmdline/cmdline_parser.h
index 32480dd..82c04e7 100644
--- a/cmdline/cmdline_parser.h
+++ b/cmdline/cmdline_parser.h
@@ -19,20 +19,20 @@
#define CMDLINE_NDEBUG 1 // Do not output any debugging information for parsing.
-#include "cmdline/detail/cmdline_parser_detail.h"
-#include "cmdline/detail/cmdline_parse_argument_detail.h"
-#include "cmdline/detail/cmdline_debug_detail.h"
+#include "detail/cmdline_debug_detail.h"
+#include "detail/cmdline_parse_argument_detail.h"
+#include "detail/cmdline_parser_detail.h"
-#include "cmdline_type_parser.h"
-#include "token_range.h"
-#include "cmdline_types.h"
-#include "cmdline_result.h"
#include "cmdline_parse_result.h"
+#include "cmdline_result.h"
+#include "cmdline_type_parser.h"
+#include "cmdline_types.h"
+#include "token_range.h"
-#include "runtime/base/variant_map.h"
+#include "base/variant_map.h"
-#include <vector>
#include <memory>
+#include <vector>
namespace art {
// Build a parser for command line arguments with a small domain specific language.
@@ -340,7 +340,7 @@
typename std::enable_if<std::is_same<TArg, Unit>::value>::type
InitializeTypedBuilder(ArgumentBuilder<TArg>* arg_builder) {
// Every Unit argument implicitly maps to a runtime value of Unit{}
- std::vector<Unit> values(names_.size(), Unit{}); // NOLINT [whitespace/braces] [5]
+ std::vector<Unit> values(names_.size(), Unit{});
arg_builder->SetValuesInternal(std::move(values));
}
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index d957869..235a2aa 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -15,14 +15,18 @@
*/
#include "cmdline_parser.h"
-#include "runtime/runtime_options.h"
-#include "runtime/parsed_options.h"
-#include "utils.h"
#include <numeric>
+
#include "gtest/gtest.h"
-#include "runtime/experimental_flags.h"
-#include "runtime/runtime.h"
+
+#include "base/mutex.h"
+#include "base/utils.h"
+#include "jdwp_provider.h"
+#include "experimental_flags.h"
+#include "parsed_options.h"
+#include "runtime.h"
+#include "runtime_options.h"
#define EXPECT_NULL(expected) EXPECT_EQ(reinterpret_cast<const void*>(expected), \
reinterpret_cast<void*>(nullptr));
@@ -123,6 +127,7 @@
using RuntimeParser = ParsedOptions::RuntimeParser;
static void SetUpTestCase() {
+ art::Locks::Init();
art::InitLogging(nullptr, art::Runtime::Abort); // argv = null
}
@@ -188,7 +193,7 @@
#define EXPECT_SINGLE_PARSE_VALUE(expected, argv, key) \
_EXPECT_SINGLE_PARSE_EXISTS(argv, key); \
EXPECT_KEY_VALUE(args, key, expected); \
- } while (false) // NOLINT [readability/namespace] [5]
+ } while (false)
#define EXPECT_SINGLE_PARSE_VALUE_STR(expected, argv, key) \
EXPECT_SINGLE_PARSE_VALUE(std::string(expected), argv, key)
@@ -242,7 +247,7 @@
{
const char* log_args = "-verbose:"
"class,compiler,gc,heap,jdwp,jni,monitor,profiler,signals,simulator,startup,"
- "third-party-jni,threads,verifier";
+ "third-party-jni,threads,verifier,verifier-debug";
LogVerbosity log_verbosity = LogVerbosity();
log_verbosity.class_linker = true;
@@ -259,6 +264,7 @@
log_verbosity.third_party_jni = true;
log_verbosity.threads = true;
log_verbosity.verifier = true;
+ log_verbosity.verifier_debug = true;
EXPECT_SINGLE_PARSE_VALUE(log_verbosity, log_args, M::Verbose);
}
@@ -316,7 +322,7 @@
* Test success
*/
{
- XGcOption option_all_true{}; // NOLINT [readability/braces] [4]
+ XGcOption option_all_true{};
option_all_true.collector_type_ = gc::CollectorType::kCollectorTypeCMS;
option_all_true.verify_pre_gc_heap_ = true;
option_all_true.verify_pre_sweeping_heap_ = true;
@@ -333,7 +339,7 @@
EXPECT_SINGLE_PARSE_VALUE(option_all_true, xgc_args_all_true, M::GcOption);
- XGcOption option_all_false{}; // NOLINT [readability/braces] [4]
+ XGcOption option_all_false{};
option_all_false.collector_type_ = gc::CollectorType::kCollectorTypeMS;
option_all_false.verify_pre_gc_heap_ = false;
option_all_false.verify_pre_sweeping_heap_ = false;
@@ -348,7 +354,7 @@
EXPECT_SINGLE_PARSE_VALUE(option_all_false, xgc_args_all_false, M::GcOption);
- XGcOption option_all_default{}; // NOLINT [readability/braces] [4]
+ XGcOption option_all_default{};
const char* xgc_args_blank = "-Xgc:";
EXPECT_SINGLE_PARSE_VALUE(option_all_default, xgc_args_blank, M::GcOption);
@@ -361,48 +367,40 @@
} // TEST_F
/*
- * {"-Xrunjdwp:_", "-agentlib:jdwp=_"}
+ * { "-XjdwpProvider:_" }
*/
-TEST_F(CmdlineParserTest, TestJdwpOptions) {
- /*
- * Test success
- */
+TEST_F(CmdlineParserTest, TestJdwpProviderEmpty) {
{
- /*
- * "Example: -Xrunjdwp:transport=dt_socket,address=8000,server=y\n"
- */
- JDWP::JdwpOptions opt = JDWP::JdwpOptions();
- opt.transport = JDWP::JdwpTransportType::kJdwpTransportSocket;
- opt.port = 8000;
- opt.server = true;
-
- const char *opt_args = "-Xrunjdwp:transport=dt_socket,address=8000,server=y";
-
- EXPECT_SINGLE_PARSE_VALUE(opt, opt_args, M::JdwpOptions);
+ EXPECT_SINGLE_PARSE_DEFAULT_VALUE(JdwpProvider::kNone, "", M::JdwpProvider);
}
+} // TEST_F
- {
- /*
- * "Example: -agentlib:jdwp=transport=dt_socket,address=localhost:6500,server=n\n");
- */
- JDWP::JdwpOptions opt = JDWP::JdwpOptions();
- opt.transport = JDWP::JdwpTransportType::kJdwpTransportSocket;
- opt.host = "localhost";
- opt.port = 6500;
- opt.server = false;
+TEST_F(CmdlineParserTest, TestJdwpProviderDefault) {
+ const char* opt_args = "-XjdwpProvider:default";
+ EXPECT_SINGLE_PARSE_VALUE(JdwpProvider::kDefaultJdwpProvider, opt_args, M::JdwpProvider);
+} // TEST_F
- const char *opt_args = "-agentlib:jdwp=transport=dt_socket,address=localhost:6500,server=n";
+TEST_F(CmdlineParserTest, TestJdwpProviderInternal) {
+ const char* opt_args = "-XjdwpProvider:internal";
+ EXPECT_SINGLE_PARSE_VALUE(JdwpProvider::kInternal, opt_args, M::JdwpProvider);
+} // TEST_F
- EXPECT_SINGLE_PARSE_VALUE(opt, opt_args, M::JdwpOptions);
- }
+TEST_F(CmdlineParserTest, TestJdwpProviderNone) {
+ const char* opt_args = "-XjdwpProvider:none";
+ EXPECT_SINGLE_PARSE_VALUE(JdwpProvider::kNone, opt_args, M::JdwpProvider);
+} // TEST_F
- /*
- * Test failures
- */
- EXPECT_SINGLE_PARSE_FAIL("-Xrunjdwp:help", CmdlineResult::kUsage); // usage for help only
- EXPECT_SINGLE_PARSE_FAIL("-Xrunjdwp:blabla", CmdlineResult::kFailure); // invalid subarg
- EXPECT_SINGLE_PARSE_FAIL("-agentlib:jdwp=help", CmdlineResult::kUsage); // usage for help only
- EXPECT_SINGLE_PARSE_FAIL("-agentlib:jdwp=blabla", CmdlineResult::kFailure); // invalid subarg
+TEST_F(CmdlineParserTest, TestJdwpProviderAdbconnection) {
+ const char* opt_args = "-XjdwpProvider:adbconnection";
+ EXPECT_SINGLE_PARSE_VALUE(JdwpProvider::kAdbConnection, opt_args, M::JdwpProvider);
+} // TEST_F
+
+TEST_F(CmdlineParserTest, TestJdwpProviderHelp) {
+ EXPECT_SINGLE_PARSE_FAIL("-XjdwpProvider:help", CmdlineResult::kUsage);
+} // TEST_F
+
+TEST_F(CmdlineParserTest, TestJdwpProviderFail) {
+ EXPECT_SINGLE_PARSE_FAIL("-XjdwpProvider:blablabla", CmdlineResult::kFailure);
} // TEST_F
/*
@@ -564,10 +562,10 @@
auto&& map = parser_->ReleaseArgumentsMap();
EXPECT_EQ(5u, map.Size());
- EXPECT_KEY_VALUE(map, M::Help, Unit{}); // NOLINT [whitespace/braces] [5]
+ EXPECT_KEY_VALUE(map, M::Help, Unit{});
EXPECT_KEY_VALUE(map, M::ForegroundHeapGrowthMultiplier, 0.5);
EXPECT_KEY_VALUE(map, M::Dex2Oat, false);
- EXPECT_KEY_VALUE(map, M::MethodTrace, Unit{}); // NOLINT [whitespace/braces] [5]
+ EXPECT_KEY_VALUE(map, M::MethodTrace, Unit{});
EXPECT_KEY_VALUE(map, M::LargeObjectSpace, gc::space::LargeObjectSpaceType::kMap);
} // TEST_F
} // namespace art
diff --git a/cmdline/cmdline_result.h b/cmdline/cmdline_result.h
index 963dfc1..0ae1145 100644
--- a/cmdline/cmdline_result.h
+++ b/cmdline/cmdline_result.h
@@ -18,86 +18,85 @@
#define ART_CMDLINE_CMDLINE_RESULT_H_
#include <assert.h>
-#include <utils.h>
+#include "base/utils.h"
namespace art {
- // Result of an attempt to process the command line arguments. If fails, specifies
- // the specific error code and an error message.
- // Use the value-carrying CmdlineParseResult<T> to get an additional value out in a success case.
- struct CmdlineResult {
- enum Status {
- kSuccess,
- // Error codes:
- kUsage,
- kFailure,
- kOutOfRange,
- kUnknown,
- };
-
- // Short-hand for checking if the result was successful.
- operator bool() const {
- return IsSuccess();
- }
-
- // Check if the operation has succeeded.
- bool IsSuccess() const { return status_ == kSuccess; }
- // Check if the operation was not a success.
- bool IsError() const { return status_ != kSuccess; }
- // Get the specific status, regardless of whether it's failure or success.
- Status GetStatus() const { return status_; }
-
- // Get the error message, *must* only be called for error status results.
- const std::string& GetMessage() const { assert(IsError()); return message_; }
-
- // Constructor any status. No message.
- explicit CmdlineResult(Status status) : status_(status) {}
-
- // Constructor with an error status, copying the message.
- CmdlineResult(Status status, const std::string& message)
- : status_(status), message_(message) {
- assert(status != kSuccess);
- }
-
- // Constructor with an error status, taking over the message.
- CmdlineResult(Status status, std::string&& message)
- : status_(status), message_(message) {
- assert(status != kSuccess);
- }
-
- // Make sure copying exists
- CmdlineResult(const CmdlineResult&) = default;
- // Make sure moving is cheap
- CmdlineResult(CmdlineResult&&) = default;
-
- private:
- const Status status_;
- const std::string message_;
+// Result of an attempt to process the command line arguments. If fails, specifies
+// the specific error code and an error message.
+// Use the value-carrying CmdlineParseResult<T> to get an additional value out in a success case.
+struct CmdlineResult {
+ enum Status {
+ kSuccess,
+ // Error codes:
+ kUsage,
+ kFailure,
+ kOutOfRange,
+ kUnknown,
};
- // TODO: code-generate this
- static inline std::ostream& operator<<(std::ostream& stream, CmdlineResult::Status status) {
- switch (status) {
- case CmdlineResult::kSuccess:
- stream << "kSuccess";
- break;
- case CmdlineResult::kUsage:
- stream << "kUsage";
- break;
- case CmdlineResult::kFailure:
- stream << "kFailure";
- break;
- case CmdlineResult::kOutOfRange:
- stream << "kOutOfRange";
- break;
- case CmdlineResult::kUnknown:
- stream << "kUnknown";
- break;
- default:
- UNREACHABLE();
- }
- return stream;
+ // Short-hand for checking if the result was successful.
+ operator bool() const {
+ return IsSuccess();
}
+ // Check if the operation has succeeded.
+ bool IsSuccess() const { return status_ == kSuccess; }
+ // Check if the operation was not a success.
+ bool IsError() const { return status_ != kSuccess; }
+ // Get the specific status, regardless of whether it's failure or success.
+ Status GetStatus() const { return status_; }
+
+ // Get the error message, *must* only be called for error status results.
+ const std::string& GetMessage() const { assert(IsError()); return message_; }
+
+ // Constructor any status. No message.
+ explicit CmdlineResult(Status status) : status_(status) {}
+
+ // Constructor with an error status, copying the message.
+ CmdlineResult(Status status, const std::string& message)
+ : status_(status), message_(message) {
+ assert(status != kSuccess);
+ }
+
+ // Constructor with an error status, taking over the message.
+ CmdlineResult(Status status, std::string&& message)
+ : status_(status), message_(message) {
+ assert(status != kSuccess);
+ }
+
+ // Make sure copying exists
+ CmdlineResult(const CmdlineResult&) = default;
+ // Make sure moving is cheap
+ CmdlineResult(CmdlineResult&&) = default;
+
+ private:
+ const Status status_;
+ const std::string message_;
+};
+
+// TODO: code-generate this
+static inline std::ostream& operator<<(std::ostream& stream, CmdlineResult::Status status) {
+ switch (status) {
+ case CmdlineResult::kSuccess:
+ stream << "kSuccess";
+ break;
+ case CmdlineResult::kUsage:
+ stream << "kUsage";
+ break;
+ case CmdlineResult::kFailure:
+ stream << "kFailure";
+ break;
+ case CmdlineResult::kOutOfRange:
+ stream << "kOutOfRange";
+ break;
+ case CmdlineResult::kUnknown:
+ stream << "kUnknown";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ return stream;
+}
} // namespace art
#endif // ART_CMDLINE_CMDLINE_RESULT_H_
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index 4de8a48..cb3140a 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -20,9 +20,9 @@
#include <list>
-#include "memory_representation.h"
-#include "detail/cmdline_debug_detail.h"
#include "cmdline_type_parser.h"
+#include "detail/cmdline_debug_detail.h"
+#include "memory_representation.h"
#include "android-base/strings.h"
@@ -34,8 +34,10 @@
#include "gc/collector_type.h"
#include "gc/space/large_object_space.h"
#include "jdwp/jdwp.h"
+#include "jdwp_provider.h"
#include "jit/profile_saver_options.h"
#include "plugin.h"
+#include "read_barrier_config.h"
#include "ti/agent.h"
#include "unit.h"
@@ -56,130 +58,38 @@
struct CmdlineType<Unit> : CmdlineTypeParser<Unit> {
Result Parse(const std::string& args) {
if (args == "") {
- return Result::Success(Unit{}); // NOLINT [whitespace/braces] [5]
+ return Result::Success(Unit{});
}
return Result::Failure("Unexpected extra characters " + args);
}
};
template <>
-struct CmdlineType<JDWP::JdwpOptions> : CmdlineTypeParser<JDWP::JdwpOptions> {
+struct CmdlineType<JdwpProvider> : CmdlineTypeParser<JdwpProvider> {
/*
- * Handle one of the JDWP name/value pairs.
- *
- * JDWP options are:
- * help: if specified, show help message and bail
- * transport: may be dt_socket or dt_shmem
- * address: for dt_socket, "host:port", or just "port" when listening
- * server: if "y", wait for debugger to attach; if "n", attach to debugger
- * timeout: how long to wait for debugger to connect / listen
- *
- * Useful with server=n (these aren't supported yet):
- * onthrow=<exception-name>: connect to debugger when exception thrown
- * onuncaught=y|n: connect to debugger when uncaught exception thrown
- * launch=<command-line>: launch the debugger itself
- *
- * The "transport" option is required, as is "address" if server=n.
+ * Handle a single JDWP provider name. Must be either 'internal', 'default', or the file name of
+ * an agent. A plugin will make use of this and the jdwpOptions to set up jdwp when appropriate.
*/
- Result Parse(const std::string& options) {
- VLOG(jdwp) << "ParseJdwpOptions: " << options;
-
- if (options == "help") {
+ Result Parse(const std::string& option) {
+ if (option == "help") {
return Result::Usage(
- "Example: -Xrunjdwp:transport=dt_socket,address=8000,server=y\n"
- "Example: -Xrunjdwp:transport=dt_socket,address=localhost:6500,server=n\n");
- }
-
- const std::string s;
-
- std::vector<std::string> pairs;
- Split(options, ',', &pairs);
-
- JDWP::JdwpOptions jdwp_options;
-
- for (const std::string& jdwp_option : pairs) {
- std::string::size_type equals_pos = jdwp_option.find('=');
- if (equals_pos == std::string::npos) {
- return Result::Failure(s +
- "Can't parse JDWP option '" + jdwp_option + "' in '" + options + "'");
- }
-
- Result parse_attempt = ParseJdwpOption(jdwp_option.substr(0, equals_pos),
- jdwp_option.substr(equals_pos + 1),
- &jdwp_options);
- if (parse_attempt.IsError()) {
- // We fail to parse this JDWP option.
- return parse_attempt;
- }
- }
-
- if (jdwp_options.transport == JDWP::kJdwpTransportUnknown) {
- return Result::Failure(s + "Must specify JDWP transport: " + options);
- }
- if (!jdwp_options.server && (jdwp_options.host.empty() || jdwp_options.port == 0)) {
- return Result::Failure(s + "Must specify JDWP host and port when server=n: " + options);
- }
-
- return Result::Success(std::move(jdwp_options));
- }
-
- Result ParseJdwpOption(const std::string& name, const std::string& value,
- JDWP::JdwpOptions* jdwp_options) {
- if (name == "transport") {
- if (value == "dt_socket") {
- jdwp_options->transport = JDWP::kJdwpTransportSocket;
- } else if (value == "dt_android_adb") {
- jdwp_options->transport = JDWP::kJdwpTransportAndroidAdb;
- } else {
- return Result::Failure("JDWP transport not supported: " + value);
- }
- } else if (name == "server") {
- if (value == "n") {
- jdwp_options->server = false;
- } else if (value == "y") {
- jdwp_options->server = true;
- } else {
- return Result::Failure("JDWP option 'server' must be 'y' or 'n'");
- }
- } else if (name == "suspend") {
- if (value == "n") {
- jdwp_options->suspend = false;
- } else if (value == "y") {
- jdwp_options->suspend = true;
- } else {
- return Result::Failure("JDWP option 'suspend' must be 'y' or 'n'");
- }
- } else if (name == "address") {
- /* this is either <port> or <host>:<port> */
- std::string port_string;
- jdwp_options->host.clear();
- std::string::size_type colon = value.find(':');
- if (colon != std::string::npos) {
- jdwp_options->host = value.substr(0, colon);
- port_string = value.substr(colon + 1);
- } else {
- port_string = value;
- }
- if (port_string.empty()) {
- return Result::Failure("JDWP address missing port: " + value);
- }
- char* end;
- uint64_t port = strtoul(port_string.c_str(), &end, 10);
- if (*end != '\0' || port > 0xffff) {
- return Result::Failure("JDWP address has junk in port field: " + value);
- }
- jdwp_options->port = port;
- } else if (name == "launch" || name == "onthrow" || name == "oncaught" || name == "timeout") {
- /* valid but unsupported */
- LOG(INFO) << "Ignoring JDWP option '" << name << "'='" << value << "'";
+ "Example: -XjdwpProvider:none to disable JDWP\n"
+ "Example: -XjdwpProvider:internal for internal jdwp implementation\n"
+ "Example: -XjdwpProvider:adbconnection for adb connection mediated jdwp implementation\n"
+ "Example: -XjdwpProvider:default for the default jdwp implementation\n");
+ } else if (option == "default") {
+ return Result::Success(JdwpProvider::kDefaultJdwpProvider);
+ } else if (option == "internal") {
+ return Result::Success(JdwpProvider::kInternal);
+ } else if (option == "adbconnection") {
+ return Result::Success(JdwpProvider::kAdbConnection);
+ } else if (option == "none") {
+ return Result::Success(JdwpProvider::kNone);
} else {
- LOG(INFO) << "Ignoring unrecognized JDWP option '" << name << "'='" << value << "'";
+ return Result::Failure(std::string("not a valid jdwp provider: ") + option);
}
-
- return Result::SuccessNoValue();
}
-
- static const char* Name() { return "JdwpOptions"; }
+ static const char* Name() { return "JdwpProvider"; }
};
template <size_t Divisor>
@@ -289,26 +199,42 @@
static const char* Name() { return "double"; }
};
+template <typename T>
+static inline CmdlineParseResult<T> ParseNumeric(const std::string& str) {
+ static_assert(sizeof(T) < sizeof(long long int), // NOLINT [runtime/int] [4]
+ "Current support is restricted.");
+
+ const char* begin = str.c_str();
+ char* end;
+
+ // Parse into a larger type (long long) because we can't use strtoul
+ // since it silently converts negative values into unsigned long and doesn't set errno.
+ errno = 0;
+ long long int result = strtoll(begin, &end, 10); // NOLINT [runtime/int] [4]
+ if (begin == end || *end != '\0' || errno == EINVAL) {
+ return CmdlineParseResult<T>::Failure("Failed to parse integer from " + str);
+ } else if ((errno == ERANGE) || // NOLINT [runtime/int] [4]
+ result < std::numeric_limits<T>::min() || result > std::numeric_limits<T>::max()) {
+ return CmdlineParseResult<T>::OutOfRange(
+ "Failed to parse integer from " + str + "; out of range");
+ }
+
+ return CmdlineParseResult<T>::Success(static_cast<T>(result));
+}
+
template <>
struct CmdlineType<unsigned int> : CmdlineTypeParser<unsigned int> {
Result Parse(const std::string& str) {
- const char* begin = str.c_str();
- char* end;
+ return ParseNumeric<unsigned int>(str);
+ }
- // Parse into a larger type (long long) because we can't use strtoul
- // since it silently converts negative values into unsigned long and doesn't set errno.
- errno = 0;
- long long int result = strtoll(begin, &end, 10); // NOLINT [runtime/int] [4]
- if (begin == end || *end != '\0' || errno == EINVAL) {
- return Result::Failure("Failed to parse integer from " + str);
- } else if ((errno == ERANGE) || // NOLINT [runtime/int] [4]
- result < std::numeric_limits<int>::min()
- || result > std::numeric_limits<unsigned int>::max() || result < 0) {
- return Result::OutOfRange(
- "Failed to parse integer from " + str + "; out of unsigned int range");
- }
+ static const char* Name() { return "unsigned integer"; }
+};
- return Result::Success(static_cast<unsigned int>(result));
+template <>
+struct CmdlineType<int> : CmdlineTypeParser<int> {
+ Result Parse(const std::string& str) {
+ return ParseNumeric<int>(str);
}
static const char* Name() { return "unsigned integer"; }
@@ -403,19 +329,19 @@
};
template <>
-struct CmdlineType<std::list<ti::Agent>> : CmdlineTypeParser<std::list<ti::Agent>> {
+struct CmdlineType<std::list<ti::AgentSpec>> : CmdlineTypeParser<std::list<ti::AgentSpec>> {
Result Parse(const std::string& args) {
assert(false && "Use AppendValues() for an Agent list type");
return Result::Failure("Unconditional failure: Agent list must be appended: " + args);
}
Result ParseAndAppend(const std::string& args,
- std::list<ti::Agent>& existing_value) {
+ std::list<ti::AgentSpec>& existing_value) {
existing_value.emplace_back(args);
return Result::SuccessNoValue();
}
- static const char* Name() { return "std::list<ti::Agent>"; }
+ static const char* Name() { return "std::list<ti::AgentSpec>"; }
};
template <>
@@ -515,7 +441,7 @@
template <>
struct CmdlineType<XGcOption> : CmdlineTypeParser<XGcOption> {
Result Parse(const std::string& option) { // -Xgc: already stripped
- XGcOption xgc{}; // NOLINT [readability/braces] [4]
+ XGcOption xgc{};
std::vector<std::string> gc_options;
Split(option, ',', &gc_options);
@@ -652,6 +578,8 @@
log_verbosity.threads = true;
} else if (verbose_options[j] == "verifier") {
log_verbosity.verifier = true;
+ } else if (verbose_options[j] == "verifier-debug") {
+ log_verbosity.verifier_debug = true;
} else if (verbose_options[j] == "image") {
log_verbosity.image = true;
} else if (verbose_options[j] == "systrace-locks") {
@@ -717,6 +645,16 @@
return Result::SuccessNoValue();
}
+ if (option == "profile-aot-code") {
+ existing.profile_aot_code_ = true;
+ return Result::SuccessNoValue();
+ }
+
+ if (option == "save-without-jit-notifications") {
+ existing.wait_for_jit_notifications_to_save_ = false;
+ return Result::SuccessNoValue();
+ }
+
// The rest of these options are always the wildcard from '-Xps-*'
std::string suffix = RemovePrefix(option);
diff --git a/cmdline/detail/cmdline_debug_detail.h b/cmdline/detail/cmdline_debug_detail.h
index 79028f4..e69d5dc 100644
--- a/cmdline/detail/cmdline_debug_detail.h
+++ b/cmdline/detail/cmdline_debug_detail.h
@@ -25,16 +25,16 @@
#endif
namespace art {
- // Implementation details for some template querying. Don't look inside if you hate templates.
- namespace detail {
- struct debug_log_ignore {
- // Ignore most of the normal operator<< usage.
- template <typename T>
- debug_log_ignore& operator<<(const T&) { return *this; }
- // Ignore std::endl and the like.
- debug_log_ignore& operator<<(std::ostream& (*)(std::ostream&) ) { return *this; }
- };
- } // namespace detail // NOLINT [readability/namespace] [5]
+// Implementation details for some template querying. Don't look inside if you hate templates.
+namespace detail {
+struct debug_log_ignore {
+ // Ignore most of the normal operator<< usage.
+ template <typename T>
+ debug_log_ignore& operator<<(const T&) { return *this; }
+ // Ignore std::endl and the like.
+ debug_log_ignore& operator<<(std::ostream& (*)(std::ostream&) ) { return *this; }
+};
+} // namespace detail // NOLINT [readability/namespace] [5]
} // namespace art
#endif // ART_CMDLINE_DETAIL_CMDLINE_DEBUG_DETAIL_H_
diff --git a/cmdline/detail/cmdline_parse_argument_detail.h b/cmdline/detail/cmdline_parse_argument_detail.h
index da03c21..2fee27c 100644
--- a/cmdline/detail/cmdline_parse_argument_detail.h
+++ b/cmdline/detail/cmdline_parse_argument_detail.h
@@ -17,13 +17,13 @@
#ifndef ART_CMDLINE_DETAIL_CMDLINE_PARSE_ARGUMENT_DETAIL_H_
#define ART_CMDLINE_DETAIL_CMDLINE_PARSE_ARGUMENT_DETAIL_H_
-#include <type_traits>
#include <assert.h>
-#include <functional>
-#include <vector>
#include <algorithm>
-#include <numeric>
+#include <functional>
#include <memory>
+#include <numeric>
+#include <type_traits>
+#include <vector>
#include "android-base/strings.h"
@@ -33,473 +33,473 @@
#include "unit.h"
namespace art {
- // Implementation details for the parser. Do not look inside if you hate templates.
- namespace detail {
- // A non-templated base class for argument parsers. Used by the general parser
- // to parse arguments, without needing to know the argument type at compile time.
- //
- // This is an application of the type erasure idiom.
- struct CmdlineParseArgumentAny {
- virtual ~CmdlineParseArgumentAny() {}
+// Implementation details for the parser. Do not look inside if you hate templates.
+namespace detail {
+// A non-templated base class for argument parsers. Used by the general parser
+// to parse arguments, without needing to know the argument type at compile time.
+//
+// This is an application of the type erasure idiom.
+struct CmdlineParseArgumentAny {
+ virtual ~CmdlineParseArgumentAny() {}
- // Attempt to parse this argument starting at arguments[position].
- // If the parsing succeeds, the parsed value will be saved as a side-effect.
- //
- // In most situations, the parsing will not match by returning kUnknown. In this case,
- // no tokens were consumed and the position variable will not be updated.
- //
- // At other times, parsing may fail due to validation but the initial token was still matched
- // (for example an out of range value, or passing in a string where an int was expected).
- // In this case the tokens are still consumed, and the position variable will get incremented
- // by all the consumed tokens.
- //
- // The # of tokens consumed by the parse attempt will be set as an out-parameter into
- // consumed_tokens. The parser should skip this many tokens before parsing the next
- // argument.
- virtual CmdlineResult ParseArgument(const TokenRange& arguments, size_t* consumed_tokens) = 0;
- // How many tokens should be taken off argv for parsing this argument.
- // For example "--help" is just 1, "-compiler-option _" would be 2 (since there's a space).
- //
- // A [min,max] range is returned to represent argument definitions with multiple
- // value tokens. (e.g. {"-h", "-h " } would return [1,2]).
- virtual std::pair<size_t, size_t> GetNumTokens() const = 0;
- // Get the run-time typename of the argument type.
- virtual const char* GetTypeName() const = 0;
- // Try to do a close match, returning how many tokens were matched against this argument
- // definition. More tokens is better.
- //
- // Do a quick match token-by-token, and see if they match.
- // Any tokens with a wildcard in them are only matched up until the wildcard.
- // If this is true, then the wildcard matching later on can still fail, so this is not
- // a guarantee that the argument is correct, it's more of a strong hint that the
- // user-provided input *probably* was trying to match this argument.
- //
- // Returns how many tokens were either matched (or ignored because there was a
- // wildcard present). 0 means no match. If the Size() tokens are returned.
- virtual size_t MaybeMatches(const TokenRange& tokens) = 0;
- };
+ // Attempt to parse this argument starting at arguments[position].
+ // If the parsing succeeds, the parsed value will be saved as a side-effect.
+ //
+ // In most situations, the parsing will not match by returning kUnknown. In this case,
+ // no tokens were consumed and the position variable will not be updated.
+ //
+ // At other times, parsing may fail due to validation but the initial token was still matched
+ // (for example an out of range value, or passing in a string where an int was expected).
+ // In this case the tokens are still consumed, and the position variable will get incremented
+ // by all the consumed tokens.
+ //
+ // The # of tokens consumed by the parse attempt will be set as an out-parameter into
+ // consumed_tokens. The parser should skip this many tokens before parsing the next
+ // argument.
+ virtual CmdlineResult ParseArgument(const TokenRange& arguments, size_t* consumed_tokens) = 0;
+ // How many tokens should be taken off argv for parsing this argument.
+ // For example "--help" is just 1, "-compiler-option _" would be 2 (since there's a space).
+ //
+ // A [min,max] range is returned to represent argument definitions with multiple
+ // value tokens. (e.g. {"-h", "-h " } would return [1,2]).
+ virtual std::pair<size_t, size_t> GetNumTokens() const = 0;
+ // Get the run-time typename of the argument type.
+ virtual const char* GetTypeName() const = 0;
+ // Try to do a close match, returning how many tokens were matched against this argument
+ // definition. More tokens is better.
+ //
+ // Do a quick match token-by-token, and see if they match.
+ // Any tokens with a wildcard in them are only matched up until the wildcard.
+ // If this is true, then the wildcard matching later on can still fail, so this is not
+ // a guarantee that the argument is correct, it's more of a strong hint that the
+ // user-provided input *probably* was trying to match this argument.
+ //
+ // Returns how many tokens were either matched (or ignored because there was a
+ // wildcard present). 0 means no match. If the Size() tokens are returned.
+ virtual size_t MaybeMatches(const TokenRange& tokens) = 0;
+};
- template <typename T>
- using EnableIfNumeric = std::enable_if<std::is_arithmetic<T>::value>;
+template <typename T>
+using EnableIfNumeric = std::enable_if<std::is_arithmetic<T>::value>;
- template <typename T>
- using DisableIfNumeric = std::enable_if<!std::is_arithmetic<T>::value>;
+template <typename T>
+using DisableIfNumeric = std::enable_if<!std::is_arithmetic<T>::value>;
- // Argument definition information, created by an ArgumentBuilder and an UntypedArgumentBuilder.
- template <typename TArg>
- struct CmdlineParserArgumentInfo {
- // This version will only be used if TArg is arithmetic and thus has the <= operators.
- template <typename T = TArg> // Necessary to get SFINAE to kick in.
- bool CheckRange(const TArg& value, typename EnableIfNumeric<T>::type* = 0) {
- if (has_range_) {
- return min_ <= value && value <= max_;
- }
- return true;
+// Argument definition information, created by an ArgumentBuilder and an UntypedArgumentBuilder.
+template <typename TArg>
+struct CmdlineParserArgumentInfo {
+ // This version will only be used if TArg is arithmetic and thus has the <= operators.
+ template <typename T = TArg> // Necessary to get SFINAE to kick in.
+ bool CheckRange(const TArg& value, typename EnableIfNumeric<T>::type* = 0) {
+ if (has_range_) {
+ return min_ <= value && value <= max_;
+ }
+ return true;
+ }
+
+ // This version will be used at other times when TArg is not arithmetic.
+ template <typename T = TArg>
+ bool CheckRange(const TArg&, typename DisableIfNumeric<T>::type* = 0) {
+ assert(!has_range_);
+ return true;
+ }
+
+ // Do a quick match token-by-token, and see if they match.
+ // Any tokens with a wildcard in them only match the prefix up until the wildcard.
+ //
+ // If this is true, then the wildcard matching later on can still fail, so this is not
+ // a guarantee that the argument is correct, it's more of a strong hint that the
+ // user-provided input *probably* was trying to match this argument.
+ size_t MaybeMatches(const TokenRange& token_list) const {
+ auto best_match = FindClosestMatch(token_list);
+
+ return best_match.second;
+ }
+
+ // Attempt to find the closest match (see MaybeMatches).
+ //
+ // Returns the token range that was the closest match and the # of tokens that
+ // this range was matched up until.
+ std::pair<const TokenRange*, size_t> FindClosestMatch(const TokenRange& token_list) const {
+ const TokenRange* best_match_ptr = nullptr;
+
+ size_t best_match = 0;
+ for (auto&& token_range : tokenized_names_) {
+ size_t this_match = token_range.MaybeMatches(token_list, std::string("_"));
+
+ if (this_match > best_match) {
+ best_match_ptr = &token_range;
+ best_match = this_match;
+ }
+ }
+
+ return std::make_pair(best_match_ptr, best_match);
+ }
+
+ // Mark the argument definition as completed, do not mutate the object anymore after this
+ // call is done.
+ //
+ // Performs several sanity checks and token calculations.
+ void CompleteArgument() {
+ assert(names_.size() >= 1);
+ assert(!is_completed_);
+
+ is_completed_ = true;
+
+ size_t blank_count = 0;
+ size_t token_count = 0;
+
+ size_t global_blank_count = 0;
+ size_t global_token_count = 0;
+ for (auto&& name : names_) {
+ std::string s(name);
+
+ size_t local_blank_count = std::count(s.begin(), s.end(), '_');
+ size_t local_token_count = std::count(s.begin(), s.end(), ' ');
+
+ if (global_blank_count != 0) {
+ assert(local_blank_count == global_blank_count
+ && "Every argument descriptor string must have same amount of blanks (_)");
}
- // This version will be used at other times when TArg is not arithmetic.
- template <typename T = TArg>
- bool CheckRange(const TArg&, typename DisableIfNumeric<T>::type* = 0) {
- assert(!has_range_);
- return true;
+ if (local_blank_count != 0) {
+ global_blank_count = local_blank_count;
+ blank_count++;
+
+ assert(local_blank_count == 1 && "More than one blank is not supported");
+ assert(s.back() == '_' && "The blank character must only be at the end of the string");
}
- // Do a quick match token-by-token, and see if they match.
- // Any tokens with a wildcard in them only match the prefix up until the wildcard.
- //
- // If this is true, then the wildcard matching later on can still fail, so this is not
- // a guarantee that the argument is correct, it's more of a strong hint that the
- // user-provided input *probably* was trying to match this argument.
- size_t MaybeMatches(const TokenRange& token_list) const {
- auto best_match = FindClosestMatch(token_list);
-
- return best_match.second;
+ if (global_token_count != 0) {
+ assert(local_token_count == global_token_count
+ && "Every argument descriptor string must have same amount of tokens (spaces)");
}
- // Attempt to find the closest match (see MaybeMatches).
- //
- // Returns the token range that was the closest match and the # of tokens that
- // this range was matched up until.
- std::pair<const TokenRange*, size_t> FindClosestMatch(const TokenRange& token_list) const {
- const TokenRange* best_match_ptr = nullptr;
-
- size_t best_match = 0;
- for (auto&& token_range : tokenized_names_) {
- size_t this_match = token_range.MaybeMatches(token_list, std::string("_"));
-
- if (this_match > best_match) {
- best_match_ptr = &token_range;
- best_match = this_match;
- }
- }
-
- return std::make_pair(best_match_ptr, best_match);
+ if (local_token_count != 0) {
+ global_token_count = local_token_count;
+ token_count++;
}
- // Mark the argument definition as completed, do not mutate the object anymore after this
- // call is done.
- //
- // Performs several sanity checks and token calculations.
- void CompleteArgument() {
- assert(names_.size() >= 1);
- assert(!is_completed_);
-
- is_completed_ = true;
-
- size_t blank_count = 0;
- size_t token_count = 0;
-
- size_t global_blank_count = 0;
- size_t global_token_count = 0;
- for (auto&& name : names_) {
- std::string s(name);
-
- size_t local_blank_count = std::count(s.begin(), s.end(), '_');
- size_t local_token_count = std::count(s.begin(), s.end(), ' ');
-
- if (global_blank_count != 0) {
- assert(local_blank_count == global_blank_count
- && "Every argument descriptor string must have same amount of blanks (_)");
- }
-
- if (local_blank_count != 0) {
- global_blank_count = local_blank_count;
- blank_count++;
-
- assert(local_blank_count == 1 && "More than one blank is not supported");
- assert(s.back() == '_' && "The blank character must only be at the end of the string");
- }
-
- if (global_token_count != 0) {
- assert(local_token_count == global_token_count
- && "Every argument descriptor string must have same amount of tokens (spaces)");
- }
-
- if (local_token_count != 0) {
- global_token_count = local_token_count;
- token_count++;
- }
-
- // Tokenize every name, turning it from a string to a token list.
- tokenized_names_.clear();
- for (auto&& name1 : names_) {
- // Split along ' ' only, removing any duplicated spaces.
- tokenized_names_.push_back(
- TokenRange::Split(name1, {' '}).RemoveToken(" "));
- }
-
- // remove the _ character from each of the token ranges
- // we will often end up with an empty token (i.e. ["-XX", "_"] -> ["-XX", ""]
- // and this is OK because we still need an empty token to simplify
- // range comparisons
- simple_names_.clear();
-
- for (auto&& tokenized_name : tokenized_names_) {
- simple_names_.push_back(tokenized_name.RemoveCharacter('_'));
- }
- }
-
- if (token_count != 0) {
- assert(("Every argument descriptor string must have equal amount of tokens (spaces)" &&
- token_count == names_.size()));
- }
-
- if (blank_count != 0) {
- assert(("Every argument descriptor string must have an equal amount of blanks (_)" &&
- blank_count == names_.size()));
- }
-
- using_blanks_ = blank_count > 0;
- {
- size_t smallest_name_token_range_size =
- std::accumulate(tokenized_names_.begin(), tokenized_names_.end(), ~(0u),
- [](size_t min, const TokenRange& cur) {
- return std::min(min, cur.Size());
- });
- size_t largest_name_token_range_size =
- std::accumulate(tokenized_names_.begin(), tokenized_names_.end(), 0u,
- [](size_t max, const TokenRange& cur) {
- return std::max(max, cur.Size());
- });
-
- token_range_size_ = std::make_pair(smallest_name_token_range_size,
- largest_name_token_range_size);
- }
-
- if (has_value_list_) {
- assert(names_.size() == value_list_.size()
- && "Number of arg descriptors must match number of values");
- assert(!has_value_map_);
- }
- if (has_value_map_) {
- if (!using_blanks_) {
- assert(names_.size() == value_map_.size() &&
- "Since no blanks were specified, each arg is mapped directly into a mapped "
- "value without parsing; sizes must match");
- }
-
- assert(!has_value_list_);
- }
-
- if (!using_blanks_ && !CmdlineType<TArg>::kCanParseBlankless) {
- assert((has_value_map_ || has_value_list_) &&
- "Arguments without a blank (_) must provide either a value map or a value list");
- }
-
- TypedCheck();
+ // Tokenize every name, turning it from a string to a token list.
+ tokenized_names_.clear();
+ for (auto&& name1 : names_) {
+ // Split along ' ' only, removing any duplicated spaces.
+ tokenized_names_.push_back(
+ TokenRange::Split(name1, {' '}).RemoveToken(" "));
}
- // List of aliases for a single argument definition, e.g. {"-Xdex2oat", "-Xnodex2oat"}.
- std::vector<const char*> names_;
- // Is there at least 1 wildcard '_' in the argument definition?
- bool using_blanks_ = false;
- // [min, max] token counts in each arg def
- std::pair<size_t, size_t> token_range_size_;
+ // remove the _ character from each of the token ranges
+ // we will often end up with an empty token (i.e. ["-XX", "_"] -> ["-XX", ""]
+ // and this is OK because we still need an empty token to simplify
+ // range comparisons
+ simple_names_.clear();
- // contains all the names in a tokenized form, i.e. as a space-delimited list
- std::vector<TokenRange> tokenized_names_;
+ for (auto&& tokenized_name : tokenized_names_) {
+ simple_names_.push_back(tokenized_name.RemoveCharacter('_'));
+ }
+ }
- // contains the tokenized names, but with the _ character stripped
- std::vector<TokenRange> simple_names_;
+ if (token_count != 0) {
+ assert(("Every argument descriptor string must have equal amount of tokens (spaces)" &&
+ token_count == names_.size()));
+ }
- // For argument definitions created with '.AppendValues()'
- // Meaning that parsing should mutate the existing value in-place if possible.
- bool appending_values_ = false;
+ if (blank_count != 0) {
+ assert(("Every argument descriptor string must have an equal amount of blanks (_)" &&
+ blank_count == names_.size()));
+ }
- // For argument definitions created with '.WithRange(min, max)'
- bool has_range_ = false;
- TArg min_;
- TArg max_;
+ using_blanks_ = blank_count > 0;
+ {
+ size_t smallest_name_token_range_size =
+ std::accumulate(tokenized_names_.begin(), tokenized_names_.end(), ~(0u),
+ [](size_t min, const TokenRange& cur) {
+ return std::min(min, cur.Size());
+ });
+ size_t largest_name_token_range_size =
+ std::accumulate(tokenized_names_.begin(), tokenized_names_.end(), 0u,
+ [](size_t max, const TokenRange& cur) {
+ return std::max(max, cur.Size());
+ });
- // For argument definitions created with '.WithValueMap'
- bool has_value_map_ = false;
- std::vector<std::pair<const char*, TArg>> value_map_;
+ token_range_size_ = std::make_pair(smallest_name_token_range_size,
+ largest_name_token_range_size);
+ }
- // For argument definitions created with '.WithValues'
- bool has_value_list_ = false;
- std::vector<TArg> value_list_;
-
- // Make sure there's a default constructor.
- CmdlineParserArgumentInfo() = default;
-
- // Ensure there's a default move constructor.
- CmdlineParserArgumentInfo(CmdlineParserArgumentInfo&&) = default;
-
- private:
- // Perform type-specific checks at runtime.
- template <typename T = TArg>
- void TypedCheck(typename std::enable_if<std::is_same<Unit, T>::value>::type* = 0) {
- assert(!using_blanks_ &&
- "Blanks are not supported in Unit arguments; since a Unit has no parse-able value");
+ if (has_value_list_) {
+ assert(names_.size() == value_list_.size()
+ && "Number of arg descriptors must match number of values");
+ assert(!has_value_map_);
+ }
+ if (has_value_map_) {
+ if (!using_blanks_) {
+ assert(names_.size() == value_map_.size() &&
+ "Since no blanks were specified, each arg is mapped directly into a mapped "
+ "value without parsing; sizes must match");
}
- void TypedCheck() {}
+ assert(!has_value_list_);
+ }
- bool is_completed_ = false;
- };
+ if (!using_blanks_ && !CmdlineType<TArg>::kCanParseBlankless) {
+ assert((has_value_map_ || has_value_list_) &&
+ "Arguments without a blank (_) must provide either a value map or a value list");
+ }
- // A virtual-implementation of the necessary argument information in order to
- // be able to parse arguments.
- template <typename TArg>
- struct CmdlineParseArgument : CmdlineParseArgumentAny {
- CmdlineParseArgument(CmdlineParserArgumentInfo<TArg>&& argument_info,
- std::function<void(TArg&)>&& save_argument,
- std::function<TArg&(void)>&& load_argument)
- : argument_info_(std::forward<decltype(argument_info)>(argument_info)),
- save_argument_(std::forward<decltype(save_argument)>(save_argument)),
- load_argument_(std::forward<decltype(load_argument)>(load_argument)) {
- }
+ TypedCheck();
+ }
- using UserTypeInfo = CmdlineType<TArg>;
+ // List of aliases for a single argument definition, e.g. {"-Xdex2oat", "-Xnodex2oat"}.
+ std::vector<const char*> names_;
+ // Is there at least 1 wildcard '_' in the argument definition?
+ bool using_blanks_ = false;
+ // [min, max] token counts in each arg def
+ std::pair<size_t, size_t> token_range_size_;
- virtual CmdlineResult ParseArgument(const TokenRange& arguments, size_t* consumed_tokens) {
- assert(arguments.Size() > 0);
- assert(consumed_tokens != nullptr);
+ // contains all the names in a tokenized form, i.e. as a space-delimited list
+ std::vector<TokenRange> tokenized_names_;
- auto closest_match_res = argument_info_.FindClosestMatch(arguments);
- size_t best_match_size = closest_match_res.second;
- const TokenRange* best_match_arg_def = closest_match_res.first;
+ // contains the tokenized names, but with the _ character stripped
+ std::vector<TokenRange> simple_names_;
- if (best_match_size > arguments.Size()) {
- // The best match has more tokens than were provided.
- // Shouldn't happen in practice since the outer parser does this check.
- return CmdlineResult(CmdlineResult::kUnknown, "Size mismatch");
- }
+ // For argument definitions created with '.AppendValues()'
+ // Meaning that parsing should mutate the existing value in-place if possible.
+ bool appending_values_ = false;
- assert(best_match_arg_def != nullptr);
- *consumed_tokens = best_match_arg_def->Size();
+ // For argument definitions created with '.WithRange(min, max)'
+ bool has_range_ = false;
+ TArg min_;
+ TArg max_;
- if (!argument_info_.using_blanks_) {
- return ParseArgumentSingle(arguments.Join(' '));
- }
+ // For argument definitions created with '.WithValueMap'
+ bool has_value_map_ = false;
+ std::vector<std::pair<const char*, TArg>> value_map_;
- // Extract out the blank value from arguments
- // e.g. for a def of "foo:_" and input "foo:bar", blank_value == "bar"
- std::string blank_value = "";
- size_t idx = 0;
- for (auto&& def_token : *best_match_arg_def) {
- auto&& arg_token = arguments[idx];
+ // For argument definitions created with '.WithValues'
+ bool has_value_list_ = false;
+ std::vector<TArg> value_list_;
- // Does this definition-token have a wildcard in it?
- if (def_token.find('_') == std::string::npos) {
- // No, regular token. Match 1:1 against the argument token.
- bool token_match = def_token == arg_token;
+ // Make sure there's a default constructor.
+ CmdlineParserArgumentInfo() = default;
- if (!token_match) {
- return CmdlineResult(CmdlineResult::kFailure,
- std::string("Failed to parse ") + best_match_arg_def->GetToken(0)
- + " at token " + std::to_string(idx));
- }
- } else {
- // This is a wild-carded token.
- TokenRange def_split_wildcards = TokenRange::Split(def_token, {'_'});
+ // Ensure there's a default move constructor.
+ CmdlineParserArgumentInfo(CmdlineParserArgumentInfo&&) = default;
- // Extract the wildcard contents out of the user-provided arg_token.
- std::unique_ptr<TokenRange> arg_matches =
- def_split_wildcards.MatchSubstrings(arg_token, "_");
- if (arg_matches == nullptr) {
- return CmdlineResult(CmdlineResult::kFailure,
- std::string("Failed to parse ") + best_match_arg_def->GetToken(0)
- + ", with a wildcard pattern " + def_token
- + " at token " + std::to_string(idx));
- }
+ private:
+ // Perform type-specific checks at runtime.
+ template <typename T = TArg>
+ void TypedCheck(typename std::enable_if<std::is_same<Unit, T>::value>::type* = 0) {
+ assert(!using_blanks_ &&
+ "Blanks are not supported in Unit arguments; since a Unit has no parse-able value");
+ }
- // Get the corresponding wildcard tokens from arg_matches,
- // and concatenate it to blank_value.
- for (size_t sub_idx = 0;
- sub_idx < def_split_wildcards.Size() && sub_idx < arg_matches->Size(); ++sub_idx) {
- if (def_split_wildcards[sub_idx] == "_") {
- blank_value += arg_matches->GetToken(sub_idx);
- }
- }
- }
+ void TypedCheck() {}
- ++idx;
- }
+ bool is_completed_ = false;
+};
- return ParseArgumentSingle(blank_value);
- }
+// A virtual-implementation of the necessary argument information in order to
+// be able to parse arguments.
+template <typename TArg>
+struct CmdlineParseArgument : CmdlineParseArgumentAny {
+ CmdlineParseArgument(CmdlineParserArgumentInfo<TArg>&& argument_info,
+ std::function<void(TArg&)>&& save_argument,
+ std::function<TArg&(void)>&& load_argument)
+ : argument_info_(std::forward<decltype(argument_info)>(argument_info)),
+ save_argument_(std::forward<decltype(save_argument)>(save_argument)),
+ load_argument_(std::forward<decltype(load_argument)>(load_argument)) {
+ }
- private:
- virtual CmdlineResult ParseArgumentSingle(const std::string& argument) {
- // TODO: refactor to use LookupValue for the value lists/maps
+ using UserTypeInfo = CmdlineType<TArg>;
- // Handle the 'WithValueMap(...)' argument definition
- if (argument_info_.has_value_map_) {
- for (auto&& value_pair : argument_info_.value_map_) {
- const char* name = value_pair.first;
+ virtual CmdlineResult ParseArgument(const TokenRange& arguments, size_t* consumed_tokens) {
+ assert(arguments.Size() > 0);
+ assert(consumed_tokens != nullptr);
- if (argument == name) {
- return SaveArgument(value_pair.second);
- }
- }
+ auto closest_match_res = argument_info_.FindClosestMatch(arguments);
+ size_t best_match_size = closest_match_res.second;
+ const TokenRange* best_match_arg_def = closest_match_res.first;
- // Error case: Fail, telling the user what the allowed values were.
- std::vector<std::string> allowed_values;
- for (auto&& value_pair : argument_info_.value_map_) {
- const char* name = value_pair.first;
- allowed_values.push_back(name);
- }
+ if (best_match_size > arguments.Size()) {
+ // The best match has more tokens than were provided.
+ // Shouldn't happen in practice since the outer parser does this check.
+ return CmdlineResult(CmdlineResult::kUnknown, "Size mismatch");
+ }
- std::string allowed_values_flat = android::base::Join(allowed_values, ',');
+ assert(best_match_arg_def != nullptr);
+ *consumed_tokens = best_match_arg_def->Size();
+
+ if (!argument_info_.using_blanks_) {
+ return ParseArgumentSingle(arguments.Join(' '));
+ }
+
+ // Extract out the blank value from arguments
+ // e.g. for a def of "foo:_" and input "foo:bar", blank_value == "bar"
+ std::string blank_value = "";
+ size_t idx = 0;
+ for (auto&& def_token : *best_match_arg_def) {
+ auto&& arg_token = arguments[idx];
+
+ // Does this definition-token have a wildcard in it?
+ if (def_token.find('_') == std::string::npos) {
+ // No, regular token. Match 1:1 against the argument token.
+ bool token_match = def_token == arg_token;
+
+ if (!token_match) {
return CmdlineResult(CmdlineResult::kFailure,
- "Argument value '" + argument + "' does not match any of known valid"
- "values: {" + allowed_values_flat + "}");
+ std::string("Failed to parse ") + best_match_arg_def->GetToken(0)
+ + " at token " + std::to_string(idx));
}
+ } else {
+ // This is a wild-carded token.
+ TokenRange def_split_wildcards = TokenRange::Split(def_token, {'_'});
- // Handle the 'WithValues(...)' argument definition
- if (argument_info_.has_value_list_) {
- size_t arg_def_idx = 0;
- for (auto&& value : argument_info_.value_list_) {
- auto&& arg_def_token = argument_info_.names_[arg_def_idx];
-
- if (arg_def_token == argument) {
- return SaveArgument(value);
- }
- ++arg_def_idx;
- }
-
- assert(arg_def_idx + 1 == argument_info_.value_list_.size() &&
- "Number of named argument definitions must match number of values defined");
-
- // Error case: Fail, telling the user what the allowed values were.
- std::vector<std::string> allowed_values;
- for (auto&& arg_name : argument_info_.names_) {
- allowed_values.push_back(arg_name);
- }
-
- std::string allowed_values_flat = android::base::Join(allowed_values, ',');
+ // Extract the wildcard contents out of the user-provided arg_token.
+ std::unique_ptr<TokenRange> arg_matches =
+ def_split_wildcards.MatchSubstrings(arg_token, "_");
+ if (arg_matches == nullptr) {
return CmdlineResult(CmdlineResult::kFailure,
- "Argument value '" + argument + "' does not match any of known valid"
- "values: {" + allowed_values_flat + "}");
+ std::string("Failed to parse ") + best_match_arg_def->GetToken(0)
+ + ", with a wildcard pattern " + def_token
+ + " at token " + std::to_string(idx));
}
- // Handle the regular case where we parsed an unknown value from a blank.
- UserTypeInfo type_parser;
-
- if (argument_info_.appending_values_) {
- TArg& existing = load_argument_();
- CmdlineParseResult<TArg> result = type_parser.ParseAndAppend(argument, existing);
-
- assert(!argument_info_.has_range_);
-
- return result;
- }
-
- CmdlineParseResult<TArg> result = type_parser.Parse(argument);
-
- if (result.IsSuccess()) {
- TArg& value = result.GetValue();
-
- // Do a range check for 'WithRange(min,max)' argument definition.
- if (!argument_info_.CheckRange(value)) {
- return CmdlineParseResult<TArg>::OutOfRange(
- value, argument_info_.min_, argument_info_.max_);
+ // Get the corresponding wildcard tokens from arg_matches,
+ // and concatenate it to blank_value.
+ for (size_t sub_idx = 0;
+ sub_idx < def_split_wildcards.Size() && sub_idx < arg_matches->Size(); ++sub_idx) {
+ if (def_split_wildcards[sub_idx] == "_") {
+ blank_value += arg_matches->GetToken(sub_idx);
}
+ }
+ }
+ ++idx;
+ }
+
+ return ParseArgumentSingle(blank_value);
+ }
+
+ private:
+ virtual CmdlineResult ParseArgumentSingle(const std::string& argument) {
+ // TODO: refactor to use LookupValue for the value lists/maps
+
+ // Handle the 'WithValueMap(...)' argument definition
+ if (argument_info_.has_value_map_) {
+ for (auto&& value_pair : argument_info_.value_map_) {
+ const char* name = value_pair.first;
+
+ if (argument == name) {
+ return SaveArgument(value_pair.second);
+ }
+ }
+
+ // Error case: Fail, telling the user what the allowed values were.
+ std::vector<std::string> allowed_values;
+ for (auto&& value_pair : argument_info_.value_map_) {
+ const char* name = value_pair.first;
+ allowed_values.push_back(name);
+ }
+
+ std::string allowed_values_flat = android::base::Join(allowed_values, ',');
+ return CmdlineResult(CmdlineResult::kFailure,
+ "Argument value '" + argument + "' does not match any of known valid"
+ "values: {" + allowed_values_flat + "}");
+ }
+
+ // Handle the 'WithValues(...)' argument definition
+ if (argument_info_.has_value_list_) {
+ size_t arg_def_idx = 0;
+ for (auto&& value : argument_info_.value_list_) {
+ auto&& arg_def_token = argument_info_.names_[arg_def_idx];
+
+ if (arg_def_token == argument) {
return SaveArgument(value);
}
-
- // Some kind of type-specific parse error. Pass the result as-is.
- CmdlineResult raw_result = std::move(result);
- return raw_result;
+ ++arg_def_idx;
}
- public:
- virtual const char* GetTypeName() const {
- // TODO: Obviate the need for each type specialization to hardcode the type name
- return UserTypeInfo::Name();
+ assert(arg_def_idx + 1 == argument_info_.value_list_.size() &&
+ "Number of named argument definitions must match number of values defined");
+
+ // Error case: Fail, telling the user what the allowed values were.
+ std::vector<std::string> allowed_values;
+ for (auto&& arg_name : argument_info_.names_) {
+ allowed_values.push_back(arg_name);
}
- // How many tokens should be taken off argv for parsing this argument.
- // For example "--help" is just 1, "-compiler-option _" would be 2 (since there's a space).
- //
- // A [min,max] range is returned to represent argument definitions with multiple
- // value tokens. (e.g. {"-h", "-h " } would return [1,2]).
- virtual std::pair<size_t, size_t> GetNumTokens() const {
- return argument_info_.token_range_size_;
+ std::string allowed_values_flat = android::base::Join(allowed_values, ',');
+ return CmdlineResult(CmdlineResult::kFailure,
+ "Argument value '" + argument + "' does not match any of known valid"
+ "values: {" + allowed_values_flat + "}");
+ }
+
+ // Handle the regular case where we parsed an unknown value from a blank.
+ UserTypeInfo type_parser;
+
+ if (argument_info_.appending_values_) {
+ TArg& existing = load_argument_();
+ CmdlineParseResult<TArg> result = type_parser.ParseAndAppend(argument, existing);
+
+ assert(!argument_info_.has_range_);
+
+ return result;
+ }
+
+ CmdlineParseResult<TArg> result = type_parser.Parse(argument);
+
+ if (result.IsSuccess()) {
+ TArg& value = result.GetValue();
+
+ // Do a range check for 'WithRange(min,max)' argument definition.
+ if (!argument_info_.CheckRange(value)) {
+ return CmdlineParseResult<TArg>::OutOfRange(
+ value, argument_info_.min_, argument_info_.max_);
}
- // See if this token range might begin the same as the argument definition.
- virtual size_t MaybeMatches(const TokenRange& tokens) {
- return argument_info_.MaybeMatches(tokens);
- }
+ return SaveArgument(value);
+ }
- private:
- CmdlineResult SaveArgument(const TArg& value) {
- assert(!argument_info_.appending_values_
- && "If the values are being appended, then the updated parse value is "
- "updated by-ref as a side effect and shouldn't be stored directly");
- TArg val = value;
- save_argument_(val);
- return CmdlineResult(CmdlineResult::kSuccess);
- }
+ // Some kind of type-specific parse error. Pass the result as-is.
+ CmdlineResult raw_result = std::move(result);
+ return raw_result;
+ }
- CmdlineParserArgumentInfo<TArg> argument_info_;
- std::function<void(TArg&)> save_argument_;
- std::function<TArg&(void)> load_argument_;
- };
- } // namespace detail // NOLINT [readability/namespace] [5]
+ public:
+ virtual const char* GetTypeName() const {
+ // TODO: Obviate the need for each type specialization to hardcode the type name
+ return UserTypeInfo::Name();
+ }
+
+ // How many tokens should be taken off argv for parsing this argument.
+ // For example "--help" is just 1, "-compiler-option _" would be 2 (since there's a space).
+ //
+ // A [min,max] range is returned to represent argument definitions with multiple
+ // value tokens. (e.g. {"-h", "-h " } would return [1,2]).
+ virtual std::pair<size_t, size_t> GetNumTokens() const {
+ return argument_info_.token_range_size_;
+ }
+
+ // See if this token range might begin the same as the argument definition.
+ virtual size_t MaybeMatches(const TokenRange& tokens) {
+ return argument_info_.MaybeMatches(tokens);
+ }
+
+ private:
+ CmdlineResult SaveArgument(const TArg& value) {
+ assert(!argument_info_.appending_values_
+ && "If the values are being appended, then the updated parse value is "
+ "updated by-ref as a side effect and shouldn't be stored directly");
+ TArg val = value;
+ save_argument_(val);
+ return CmdlineResult(CmdlineResult::kSuccess);
+ }
+
+ CmdlineParserArgumentInfo<TArg> argument_info_;
+ std::function<void(TArg&)> save_argument_;
+ std::function<TArg&(void)> load_argument_;
+};
+} // namespace detail // NOLINT [readability/namespace] [5]
} // namespace art
#endif // ART_CMDLINE_DETAIL_CMDLINE_PARSE_ARGUMENT_DETAIL_H_
diff --git a/cmdline/detail/cmdline_parser_detail.h b/cmdline/detail/cmdline_parser_detail.h
index 24dbca2..4c26ba3 100644
--- a/cmdline/detail/cmdline_parser_detail.h
+++ b/cmdline/detail/cmdline_parser_detail.h
@@ -17,112 +17,112 @@
#ifndef ART_CMDLINE_DETAIL_CMDLINE_PARSER_DETAIL_H_
#define ART_CMDLINE_DETAIL_CMDLINE_PARSER_DETAIL_H_
-#include <string>
#include <sstream>
+#include <string>
#include <vector>
namespace art {
- // Implementation details for some template querying. Don't look inside if you hate templates.
- namespace detail {
- template <typename T>
- typename std::remove_reference<T>::type& FakeReference();
+// Implementation details for some template querying. Don't look inside if you hate templates.
+namespace detail {
+template <typename T>
+typename std::remove_reference<T>::type& FakeReference();
- // SupportsInsertionOperator<T, TStream>::value will evaluate to a boolean,
- // whose value is true if the TStream class supports the << operator against T,
- // and false otherwise.
- template <typename T2, typename TStream2 = std::ostream>
- struct SupportsInsertionOperator {
- private:
- template <typename TStream, typename T>
- static std::true_type InsertionOperatorTest(TStream& os, const T& value,
- std::remove_reference<decltype(os << value)>* = 0); // NOLINT [whitespace/operators] [3]
+// SupportsInsertionOperator<T, TStream>::value will evaluate to a boolean,
+// whose value is true if the TStream class supports the << operator against T,
+// and false otherwise.
+template <typename T2, typename TStream2 = std::ostream>
+struct SupportsInsertionOperator {
+ private:
+ template <typename TStream, typename T>
+ static std::true_type InsertionOperatorTest(TStream& os, const T& value,
+ std::remove_reference<decltype(os << value)>* = 0); // NOLINT [whitespace/operators] [3]
- template <typename TStream, typename ... T>
- static std::false_type InsertionOperatorTest(TStream& os, const T& ... args);
+ template <typename TStream, typename ... T>
+ static std::false_type InsertionOperatorTest(TStream& os, const T& ... args);
- public:
- static constexpr bool value =
- decltype(InsertionOperatorTest(FakeReference<TStream2>(), std::declval<T2>()))::value;
- };
+ public:
+ static constexpr bool value =
+ decltype(InsertionOperatorTest(FakeReference<TStream2>(), std::declval<T2>()))::value;
+};
- template <typename TLeft, typename TRight = TLeft, bool IsFloatingPoint = false>
- struct SupportsEqualityOperatorImpl;
+template <typename TLeft, typename TRight = TLeft, bool IsFloatingPoint = false>
+struct SupportsEqualityOperatorImpl;
- template <typename TLeft, typename TRight>
- struct SupportsEqualityOperatorImpl<TLeft, TRight, false> {
- private:
- template <typename TL, typename TR>
- static std::true_type EqualityOperatorTest(const TL& left, const TR& right,
- std::remove_reference<decltype(left == right)>* = 0); // NOLINT [whitespace/operators] [3]
+template <typename TLeft, typename TRight>
+struct SupportsEqualityOperatorImpl<TLeft, TRight, false> {
+ private:
+ template <typename TL, typename TR>
+ static std::true_type EqualityOperatorTest(const TL& left, const TR& right,
+ std::remove_reference<decltype(left == right)>* = 0); // NOLINT [whitespace/operators] [3]
- template <typename TL, typename ... T>
- static std::false_type EqualityOperatorTest(const TL& left, const T& ... args);
+ template <typename TL, typename ... T>
+ static std::false_type EqualityOperatorTest(const TL& left, const T& ... args);
- public:
- static constexpr bool value =
- decltype(EqualityOperatorTest(std::declval<TLeft>(), std::declval<TRight>()))::value;
- };
+ public:
+ static constexpr bool value =
+ decltype(EqualityOperatorTest(std::declval<TLeft>(), std::declval<TRight>()))::value;
+};
- // Partial specialization when TLeft/TRight are both floating points.
- // This is a work-around because decltype(floatvar1 == floatvar2)
- // will not compile with clang:
- // error: comparing floating point with == or != is unsafe [-Werror,-Wfloat-equal]
- template <typename TLeft, typename TRight>
- struct SupportsEqualityOperatorImpl<TLeft, TRight, true> {
- static constexpr bool value = true;
- };
+// Partial specialization when TLeft/TRight are both floating points.
+// This is a work-around because decltype(floatvar1 == floatvar2)
+// will not compile with clang:
+// error: comparing floating point with == or != is unsafe [-Werror,-Wfloat-equal]
+template <typename TLeft, typename TRight>
+struct SupportsEqualityOperatorImpl<TLeft, TRight, true> {
+ static constexpr bool value = true;
+};
- // SupportsEqualityOperatorImpl<T1, T2>::value will evaluate to a boolean,
- // whose value is true if T1 can be compared against T2 with ==,
- // and false otherwise.
- template <typename TLeft, typename TRight = TLeft>
- struct SupportsEqualityOperator :
- SupportsEqualityOperatorImpl<TLeft, TRight,
- std::is_floating_point<TLeft>::value
- && std::is_floating_point<TRight>::value> {
- };
+// SupportsEqualityOperatorImpl<T1, T2>::value will evaluate to a boolean,
+// whose value is true if T1 can be compared against T2 with ==,
+// and false otherwise.
+template <typename TLeft, typename TRight = TLeft>
+struct SupportsEqualityOperator : // NOLINT [whitespace/labels] [4]
+ SupportsEqualityOperatorImpl<TLeft, TRight,
+ std::is_floating_point<TLeft>::value
+ && std::is_floating_point<TRight>::value> {
+};
- // Convert any kind of type to an std::string, even if there's no
- // serialization support for it. Unknown types get converted to an
- // an arbitrary value.
- //
- // Meant for printing user-visible errors or unit test failures only.
- template <typename T>
- std::string ToStringAny(const T& value,
- typename std::enable_if<
- SupportsInsertionOperator<T>::value>::type* = 0) {
- std::stringstream stream;
- stream << value;
- return stream.str();
+// Convert any kind of type to an std::string, even if there's no
+// serialization support for it. Unknown types get converted to an
+// an arbitrary value.
+//
+// Meant for printing user-visible errors or unit test failures only.
+template <typename T>
+std::string ToStringAny(const T& value,
+ typename std::enable_if<
+ SupportsInsertionOperator<T>::value>::type* = 0) {
+ std::stringstream stream;
+ stream << value;
+ return stream.str();
+}
+
+template <typename T>
+std::string ToStringAny(const std::vector<T> value,
+ typename std::enable_if<
+ SupportsInsertionOperator<T>::value>::type* = 0) {
+ std::stringstream stream;
+ stream << "vector{";
+
+ for (size_t i = 0; i < value.size(); ++i) {
+ stream << ToStringAny(value[i]);
+
+ if (i != value.size() - 1) {
+ stream << ',';
}
+ }
- template <typename T>
- std::string ToStringAny(const std::vector<T> value,
- typename std::enable_if<
- SupportsInsertionOperator<T>::value>::type* = 0) {
- std::stringstream stream;
- stream << "vector{";
+ stream << "}";
+ return stream.str();
+}
- for (size_t i = 0; i < value.size(); ++i) {
- stream << ToStringAny(value[i]);
-
- if (i != value.size() - 1) {
- stream << ',';
- }
- }
-
- stream << "}";
- return stream.str();
- }
-
- template <typename T>
- std::string ToStringAny(const T&,
- typename std::enable_if<
- !SupportsInsertionOperator<T>::value>::type* = 0
- ) {
- return std::string("(unknown type [no operator<< implemented] for )");
- }
- } // namespace detail // NOLINT [readability/namespace] [5]
+template <typename T>
+std::string ToStringAny(const T&,
+ typename std::enable_if<
+ !SupportsInsertionOperator<T>::value>::type* = 0
+) {
+ return std::string("(unknown type [no operator<< implemented] for )");
+}
+} // namespace detail // NOLINT [readability/namespace] [5]
} // namespace art
#endif // ART_CMDLINE_DETAIL_CMDLINE_PARSER_DETAIL_H_
diff --git a/cmdline/memory_representation.h b/cmdline/memory_representation.h
index 2619c31..8db68bc 100644
--- a/cmdline/memory_representation.h
+++ b/cmdline/memory_representation.h
@@ -17,9 +17,9 @@
#ifndef ART_CMDLINE_MEMORY_REPRESENTATION_H_
#define ART_CMDLINE_MEMORY_REPRESENTATION_H_
-#include <string>
#include <assert.h>
#include <ostream>
+#include <string>
#include "base/bit_utils.h"
diff --git a/cmdline/token_range.h b/cmdline/token_range.h
index c22d6c8..642bb1d 100644
--- a/cmdline/token_range.h
+++ b/cmdline/token_range.h
@@ -18,10 +18,10 @@
#define ART_CMDLINE_TOKEN_RANGE_H_
#include <assert.h>
-#include <vector>
-#include <string>
#include <algorithm>
#include <memory>
+#include <string>
+#include <vector>
#include "android-base/strings.h"
diff --git a/compiler/Android.bp b/compiler/Android.bp
index b721d21..32e42bc 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -23,7 +23,6 @@
name: "libart-compiler-defaults",
defaults: ["art_defaults"],
host_supported: true,
- clang: true,
srcs: [
"compiled_method.cc",
"debug/elf_debug_writer.cc",
@@ -38,7 +37,6 @@
"driver/dex_compilation_unit.cc",
"linker/buffered_output_stream.cc",
"linker/file_output_stream.cc",
- "linker/multi_oat_relative_patcher.cc",
"linker/output_stream.cc",
"linker/vector_output_stream.cc",
"linker/relative_patcher.cc",
@@ -54,6 +52,8 @@
"optimizing/code_generator_utils.cc",
"optimizing/code_sinking.cc",
"optimizing/constant_folding.cc",
+ "optimizing/constructor_fence_redundancy_elimination.cc",
+ "optimizing/data_type.cc",
"optimizing/dead_code_elimination.cc",
"optimizing/escape.cc",
"optimizing/graph_checker.cc",
@@ -89,15 +89,12 @@
"optimizing/ssa_liveness_analysis.cc",
"optimizing/ssa_phi_elimination.cc",
"optimizing/stack_map_stream.cc",
+ "optimizing/superblock_cloner.cc",
"trampolines/trampoline_compiler.cc",
"utils/assembler.cc",
"utils/jni_macro_assembler.cc",
"utils/swap_space.cc",
"compiler.cc",
- "elf_writer.cc",
- "elf_writer_quick.cc",
- "image_writer.cc",
- "oat_writer.cc",
],
codegen: {
@@ -139,6 +136,7 @@
"linker/mips/relative_patcher_mips.cc",
"optimizing/code_generator_mips.cc",
"optimizing/code_generator_vector_mips.cc",
+ "optimizing/instruction_simplifier_mips.cc",
"optimizing/intrinsics_mips.cc",
"optimizing/pc_relative_fixups_mips.cc",
"utils/mips/assembler_mips.cc",
@@ -184,45 +182,32 @@
],
},
},
- target: {
- host: {
- // For compiler driver TLS.
- host_ldlibs: ["-lpthread"],
- },
- android: {
- // For atrace.
- shared_libs: ["libcutils"],
- },
- },
generated_sources: ["art_compiler_operator_srcs"],
shared_libs: [
"libbase",
- "liblz4",
+ "libcutils", // for atrace.
"liblzma",
],
include_dirs: ["art/disassembler"],
- export_include_dirs: ["."],
+ header_libs: [
+ "art_cmdlineparser_headers", // For compiler_options.
+ "libnativehelper_header_only",
+ ],
- // For SHA-1 checksumming of build ID
- static: {
- whole_static_libs: ["libcrypto"],
- },
- shared: {
- shared_libs: ["libcrypto"],
- },
+ export_include_dirs: ["."],
}
gensrcs {
name: "art_compiler_operator_srcs",
- cmd: "$(location generate-operator-out.py) art/compiler $(in) > $(out)",
- tool_files: ["generate-operator-out.py"],
+ cmd: "$(location generate_operator_out) art/compiler $(in) > $(out)",
+ tools: ["generate_operator_out"],
srcs: [
- "compiled_method.h",
"dex/dex_to_dex_compiler.h",
"driver/compiler_driver.h",
"driver/compiler_options.h",
- "image_writer.h",
+ "linker/linker_patch.h",
"optimizing/locations.h",
+ "optimizing/optimizing_compiler_stats.h",
"utils/arm/constants_arm.h",
"utils/mips/assembler_mips.h",
@@ -233,7 +218,10 @@
art_cc_library {
name: "libart-compiler",
- defaults: ["libart-compiler-defaults"],
+ defaults: [
+ "libart-compiler-defaults",
+ "dex2oat-pgo-defaults",
+ ],
codegen: {
arm: {
// VIXL assembly support for ARM targets.
@@ -264,8 +252,16 @@
},
shared_libs: [
"libart",
- "libart-dexlayout",
+ "libdexfile",
],
+
+ target: {
+ android: {
+ lto: {
+ thin: true,
+ },
+ },
+ },
}
art_cc_library {
@@ -304,7 +300,7 @@
},
shared_libs: [
"libartd",
- "libartd-dexlayout"
+ "libdexfiled",
],
}
@@ -314,6 +310,7 @@
srcs: ["common_compiler_test.cc"],
shared_libs: [
"libartd-compiler",
+ "libartd-disassembler",
"libart-runtime-gtest",
"libbase",
],
@@ -325,21 +322,18 @@
"art_gtest_defaults",
],
srcs: [
- "compiled_method_test.cc",
"debug/dwarf/dwarf_test.cc",
+ "debug/src_map_elem_test.cc",
"dex/dex_to_dex_decompiler_test.cc",
"driver/compiled_method_storage_test.cc",
"driver/compiler_driver_test.cc",
- "elf_writer_test.cc",
"exception_test.cc",
- "image_test.cc",
- "image_write_read_test.cc",
"jni/jni_compiler_test.cc",
- "linker/method_bss_mapping_encoder_test.cc",
- "linker/multi_oat_relative_patcher_test.cc",
+ "linker/linker_patch_test.cc",
"linker/output_stream_test.cc",
- "oat_test.cc",
"optimizing/bounds_check_elimination_test.cc",
+ "optimizing/superblock_cloner_test.cc",
+ "optimizing/data_type_test.cc",
"optimizing/dominator_test.cc",
"optimizing/find_loops_test.cc",
"optimizing/graph_checker_test.cc",
@@ -363,9 +357,7 @@
"utils/atomic_dex_ref_map_test.cc",
"utils/dedupe_set_test.cc",
"utils/intrusive_forward_list_test.cc",
- "utils/string_reference_test.cc",
"utils/swap_space_test.cc",
- "utils/test_dex_file_builder_test.cc",
"verifier_deps_test.cc",
"jni/jni_cfi_test.cc",
@@ -423,8 +415,14 @@
},
},
+ header_libs: [
+ "libart_simulator_headers",
+ "libnativehelper_header_only",
+ ],
+
shared_libs: [
"libartd-compiler",
+ "libartd-simulator-container",
"libvixld-arm",
"libvixld-arm64",
@@ -432,6 +430,10 @@
"libnativeloader",
],
+ include_dirs: [
+ "external/zlib",
+ ],
+
target: {
host: {
shared_libs: [
diff --git a/compiler/cfi_test.h b/compiler/cfi_test.h
index c754e55..29ff235 100644
--- a/compiler/cfi_test.h
+++ b/compiler/cfi_test.h
@@ -17,16 +17,16 @@
#ifndef ART_COMPILER_CFI_TEST_H_
#define ART_COMPILER_CFI_TEST_H_
-#include <vector>
#include <memory>
#include <sstream>
+#include <vector>
#include "arch/instruction_set.h"
#include "base/enums.h"
#include "debug/dwarf/dwarf_constants.h"
#include "debug/dwarf/dwarf_test.h"
#include "debug/dwarf/headers.h"
-#include "disassembler/disassembler.h"
+#include "disassembler.h"
#include "gtest/gtest.h"
#include "thread.h"
@@ -68,7 +68,7 @@
: &Thread::DumpThreadOffset<PointerSize::k32>);
std::unique_ptr<Disassembler> disasm(Disassembler::Create(isa, opts));
std::stringstream stream;
- const uint8_t* base = actual_asm.data() + (isa == kThumb2 ? 1 : 0);
+ const uint8_t* base = actual_asm.data() + (isa == InstructionSet::kThumb2 ? 1 : 0);
disasm->Dump(stream, base, base + actual_asm.size());
ReformatAsm(&stream, &lines);
// Print CFI and assembly interleaved.
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 07bfe31..d3e3a51 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -21,21 +21,22 @@
#include "art_method-inl.h"
#include "base/callee_save_type.h"
#include "base/enums.h"
+#include "base/utils.h"
#include "class_linker.h"
-#include "compiled_method.h"
+#include "compiled_method-inl.h"
+#include "dex/descriptors_names.h"
#include "dex/quick_compiler_callbacks.h"
#include "dex/verification_results.h"
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
#include "interpreter/interpreter.h"
-#include "mirror/class_loader.h"
#include "mirror/class-inl.h"
+#include "mirror/class_loader.h"
#include "mirror/dex_cache.h"
#include "mirror/object-inl.h"
#include "oat_quick_method_header.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
-#include "utils.h"
namespace art {
@@ -95,7 +96,7 @@
const void* method_code = CompiledMethod::CodePointer(code_ptr,
compiled_method->GetInstructionSet());
LOG(INFO) << "MakeExecutable " << method->PrettyMethod() << " code=" << method_code;
- class_linker_->SetEntryPointsToCompiledCode(method, method_code);
+ method->SetEntryPointFromQuickCompiledCode(method_code);
} else {
// No code? You must mean to go into the interpreter.
// Or the generic JNI...
@@ -174,7 +175,6 @@
}
}
- timer_.reset(new CumulativeLogger("Compilation times"));
CreateCompilerDriver(compiler_kind_, instruction_set);
}
}
@@ -193,9 +193,6 @@
GetCompiledClasses(),
GetCompiledMethods(),
number_of_threads,
- /* dump_stats */ true,
- /* dump_passes */ true,
- timer_.get(),
/* swap_fd */ -1,
GetProfileCompilationInfo()));
// We typically don't generate an image in unit tests, disable this optimization by default.
@@ -227,7 +224,6 @@
}
void CommonCompilerTest::TearDown() {
- timer_.reset();
compiler_driver_.reset();
callbacks_.reset();
verification_results_.reset();
diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h
index 0683577..8af29d4 100644
--- a/compiler/common_compiler_test.h
+++ b/compiler/common_compiler_test.h
@@ -23,17 +23,17 @@
#include "common_runtime_test.h"
#include "compiler.h"
-#include "jit/profile_compilation_info.h"
#include "oat_file.h"
namespace art {
namespace mirror {
- class ClassLoader;
+class ClassLoader;
} // namespace mirror
class CompilerDriver;
class CompilerOptions;
class CumulativeLogger;
+class ProfileCompilationInfo;
class VerificationResults;
template<class T> class Handle;
@@ -106,7 +106,6 @@
std::unique_ptr<CompilerOptions> compiler_options_;
std::unique_ptr<VerificationResults> verification_results_;
std::unique_ptr<CompilerDriver> compiler_driver_;
- std::unique_ptr<CumulativeLogger> timer_;
std::unique_ptr<const InstructionSetFeatures> instruction_set_features_;
diff --git a/compiler/compiled_method-inl.h b/compiler/compiled_method-inl.h
new file mode 100644
index 0000000..c432747
--- /dev/null
+++ b/compiler/compiled_method-inl.h
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_COMPILER_COMPILED_METHOD_INL_H_
+#define ART_COMPILER_COMPILED_METHOD_INL_H_
+
+#include "compiled_method.h"
+
+#include "base/array_ref.h"
+#include "base/length_prefixed_array.h"
+#include "linker/linker_patch.h"
+
+namespace art {
+
+inline ArrayRef<const uint8_t> CompiledCode::GetQuickCode() const {
+ return GetArray(quick_code_);
+}
+
+template <typename T>
+inline ArrayRef<const T> CompiledCode::GetArray(const LengthPrefixedArray<T>* array) {
+ if (array == nullptr) {
+ return ArrayRef<const T>();
+ }
+ DCHECK_NE(array->size(), 0u);
+ return ArrayRef<const T>(&array->At(0), array->size());
+}
+
+inline ArrayRef<const uint8_t> CompiledMethod::GetMethodInfo() const {
+ return GetArray(method_info_);
+}
+
+inline ArrayRef<const uint8_t> CompiledMethod::GetVmapTable() const {
+ return GetArray(vmap_table_);
+}
+
+inline ArrayRef<const uint8_t> CompiledMethod::GetCFIInfo() const {
+ return GetArray(cfi_info_);
+}
+
+inline ArrayRef<const linker::LinkerPatch> CompiledMethod::GetPatches() const {
+ return GetArray(patches_);
+}
+
+} // namespace art
+
+#endif // ART_COMPILER_COMPILED_METHOD_INL_H_
diff --git a/compiler/compiled_method.cc b/compiler/compiled_method.cc
index 0d9021f..e413718 100644
--- a/compiler/compiled_method.cc
+++ b/compiler/compiled_method.cc
@@ -22,11 +22,12 @@
namespace art {
-CompiledCode::CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set,
+CompiledCode::CompiledCode(CompilerDriver* compiler_driver,
+ InstructionSet instruction_set,
const ArrayRef<const uint8_t>& quick_code)
: compiler_driver_(compiler_driver),
- instruction_set_(instruction_set),
- quick_code_(compiler_driver_->GetCompiledMethodStorage()->DeduplicateCode(quick_code)) {
+ quick_code_(compiler_driver_->GetCompiledMethodStorage()->DeduplicateCode(quick_code)),
+ packed_fields_(InstructionSetField::Encode(instruction_set)) {
}
CompiledCode::~CompiledCode() {
@@ -47,7 +48,7 @@
}
size_t CompiledCode::AlignCode(size_t offset) const {
- return AlignCode(offset, instruction_set_);
+ return AlignCode(offset, GetInstructionSet());
}
size_t CompiledCode::AlignCode(size_t offset, InstructionSet instruction_set) {
@@ -55,19 +56,19 @@
}
size_t CompiledCode::CodeDelta() const {
- return CodeDelta(instruction_set_);
+ return CodeDelta(GetInstructionSet());
}
size_t CompiledCode::CodeDelta(InstructionSet instruction_set) {
switch (instruction_set) {
- case kArm:
- case kArm64:
- case kMips:
- case kMips64:
- case kX86:
- case kX86_64:
+ case InstructionSet::kArm:
+ case InstructionSet::kArm64:
+ case InstructionSet::kMips:
+ case InstructionSet::kMips64:
+ case InstructionSet::kX86:
+ case InstructionSet::kX86_64:
return 0;
- case kThumb2: {
+ case InstructionSet::kThumb2: {
// +1 to set the low-order bit so a BLX will switch to Thumb mode
return 1;
}
@@ -77,17 +78,16 @@
}
}
-const void* CompiledCode::CodePointer(const void* code_pointer,
- InstructionSet instruction_set) {
+const void* CompiledCode::CodePointer(const void* code_pointer, InstructionSet instruction_set) {
switch (instruction_set) {
- case kArm:
- case kArm64:
- case kMips:
- case kMips64:
- case kX86:
- case kX86_64:
+ case InstructionSet::kArm:
+ case InstructionSet::kArm64:
+ case InstructionSet::kMips:
+ case InstructionSet::kMips64:
+ case InstructionSet::kX86:
+ case InstructionSet::kX86_64:
return code_pointer;
- case kThumb2: {
+ case InstructionSet::kThumb2: {
uintptr_t address = reinterpret_cast<uintptr_t>(code_pointer);
// Set the low-order bit so a BLX will switch to Thumb mode
address |= 0x1;
@@ -108,7 +108,7 @@
const ArrayRef<const uint8_t>& method_info,
const ArrayRef<const uint8_t>& vmap_table,
const ArrayRef<const uint8_t>& cfi_info,
- const ArrayRef<const LinkerPatch>& patches)
+ const ArrayRef<const linker::LinkerPatch>& patches)
: CompiledCode(driver, instruction_set, quick_code),
frame_size_in_bytes_(frame_size_in_bytes),
core_spill_mask_(core_spill_mask),
@@ -129,7 +129,7 @@
const ArrayRef<const uint8_t>& method_info,
const ArrayRef<const uint8_t>& vmap_table,
const ArrayRef<const uint8_t>& cfi_info,
- const ArrayRef<const LinkerPatch>& patches) {
+ const ArrayRef<const linker::LinkerPatch>& patches) {
SwapAllocator<CompiledMethod> alloc(driver->GetCompiledMethodStorage()->GetSwapSpaceAllocator());
CompiledMethod* ret = alloc.allocate(1);
alloc.construct(ret,
diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h
index 761e9e1..acdce26 100644
--- a/compiler/compiled_method.h
+++ b/compiler/compiled_method.h
@@ -18,37 +18,38 @@
#define ART_COMPILER_COMPILED_METHOD_H_
#include <memory>
-#include <iosfwd>
#include <string>
#include <vector>
#include "arch/instruction_set.h"
-#include "base/array_ref.h"
+#include "base/bit_field.h"
#include "base/bit_utils.h"
-#include "base/length_prefixed_array.h"
-#include "dex_file_types.h"
-#include "method_reference.h"
namespace art {
+template <typename T> class ArrayRef;
class CompilerDriver;
class CompiledMethodStorage;
+template<typename T> class LengthPrefixedArray;
+
+namespace linker {
+class LinkerPatch;
+} // namespace linker
class CompiledCode {
public:
// For Quick to supply an code blob
- CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set,
+ CompiledCode(CompilerDriver* compiler_driver,
+ InstructionSet instruction_set,
const ArrayRef<const uint8_t>& quick_code);
virtual ~CompiledCode();
InstructionSet GetInstructionSet() const {
- return instruction_set_;
+ return GetPackedField<InstructionSetField>();
}
- ArrayRef<const uint8_t> GetQuickCode() const {
- return GetArray(quick_code_);
- }
+ ArrayRef<const uint8_t> GetQuickCode() const;
bool operator==(const CompiledCode& rhs) const;
@@ -66,294 +67,43 @@
// Returns a pointer suitable for invoking the code at the argument
// code_pointer address. Mainly to cope with kThumb2 where the
// lower bit must be set to indicate Thumb mode.
- static const void* CodePointer(const void* code_pointer,
- InstructionSet instruction_set);
+ static const void* CodePointer(const void* code_pointer, InstructionSet instruction_set);
protected:
+ static constexpr size_t kInstructionSetFieldSize =
+ MinimumBitsToStore(static_cast<size_t>(InstructionSet::kLast));
+ static constexpr size_t kNumberOfCompiledCodePackedBits = kInstructionSetFieldSize;
+ static constexpr size_t kMaxNumberOfPackedBits = sizeof(uint32_t) * kBitsPerByte;
+
template <typename T>
- static ArrayRef<const T> GetArray(const LengthPrefixedArray<T>* array) {
- if (array == nullptr) {
- return ArrayRef<const T>();
- }
- DCHECK_NE(array->size(), 0u);
- return ArrayRef<const T>(&array->At(0), array->size());
- }
+ static ArrayRef<const T> GetArray(const LengthPrefixedArray<T>* array);
CompilerDriver* GetCompilerDriver() {
return compiler_driver_;
}
+ template <typename BitFieldType>
+ typename BitFieldType::value_type GetPackedField() const {
+ return BitFieldType::Decode(packed_fields_);
+ }
+
+ template <typename BitFieldType>
+ void SetPackedField(typename BitFieldType::value_type value) {
+ DCHECK(IsUint<BitFieldType::size>(static_cast<uintptr_t>(value)));
+ packed_fields_ = BitFieldType::Update(value, packed_fields_);
+ }
+
private:
+ using InstructionSetField = BitField<InstructionSet, 0u, kInstructionSetFieldSize>;
+
CompilerDriver* const compiler_driver_;
- const InstructionSet instruction_set_;
-
- // Used to store the PIC code for Quick.
+ // Used to store the compiled code.
const LengthPrefixedArray<uint8_t>* const quick_code_;
+
+ uint32_t packed_fields_;
};
-class SrcMapElem {
- public:
- uint32_t from_;
- int32_t to_;
-};
-
-inline bool operator<(const SrcMapElem& lhs, const SrcMapElem& rhs) {
- if (lhs.from_ != rhs.from_) {
- return lhs.from_ < rhs.from_;
- }
- return lhs.to_ < rhs.to_;
-}
-
-inline bool operator==(const SrcMapElem& lhs, const SrcMapElem& rhs) {
- return lhs.from_ == rhs.from_ && lhs.to_ == rhs.to_;
-}
-
-class LinkerPatch {
- public:
- // Note: We explicitly specify the underlying type of the enum because GCC
- // would otherwise select a bigger underlying type and then complain that
- // 'art::LinkerPatch::patch_type_' is too small to hold all
- // values of 'enum class art::LinkerPatch::Type'
- // which is ridiculous given we have only a handful of values here. If we
- // choose to squeeze the Type into fewer than 8 bits, we'll have to declare
- // patch_type_ as an uintN_t and do explicit static_cast<>s.
- enum class Type : uint8_t {
- kMethodRelative, // NOTE: Actual patching is instruction_set-dependent.
- kMethodBssEntry, // NOTE: Actual patching is instruction_set-dependent.
- kCall,
- kCallRelative, // NOTE: Actual patching is instruction_set-dependent.
- kTypeRelative, // NOTE: Actual patching is instruction_set-dependent.
- kTypeBssEntry, // NOTE: Actual patching is instruction_set-dependent.
- kStringRelative, // NOTE: Actual patching is instruction_set-dependent.
- kStringBssEntry, // NOTE: Actual patching is instruction_set-dependent.
- kBakerReadBarrierBranch, // NOTE: Actual patching is instruction_set-dependent.
- };
-
- static LinkerPatch RelativeMethodPatch(size_t literal_offset,
- const DexFile* target_dex_file,
- uint32_t pc_insn_offset,
- uint32_t target_method_idx) {
- LinkerPatch patch(literal_offset, Type::kMethodRelative, target_dex_file);
- patch.method_idx_ = target_method_idx;
- patch.pc_insn_offset_ = pc_insn_offset;
- return patch;
- }
-
- static LinkerPatch MethodBssEntryPatch(size_t literal_offset,
- const DexFile* target_dex_file,
- uint32_t pc_insn_offset,
- uint32_t target_method_idx) {
- LinkerPatch patch(literal_offset, Type::kMethodBssEntry, target_dex_file);
- patch.method_idx_ = target_method_idx;
- patch.pc_insn_offset_ = pc_insn_offset;
- return patch;
- }
-
- static LinkerPatch CodePatch(size_t literal_offset,
- const DexFile* target_dex_file,
- uint32_t target_method_idx) {
- LinkerPatch patch(literal_offset, Type::kCall, target_dex_file);
- patch.method_idx_ = target_method_idx;
- return patch;
- }
-
- static LinkerPatch RelativeCodePatch(size_t literal_offset,
- const DexFile* target_dex_file,
- uint32_t target_method_idx) {
- LinkerPatch patch(literal_offset, Type::kCallRelative, target_dex_file);
- patch.method_idx_ = target_method_idx;
- return patch;
- }
-
- static LinkerPatch RelativeTypePatch(size_t literal_offset,
- const DexFile* target_dex_file,
- uint32_t pc_insn_offset,
- uint32_t target_type_idx) {
- LinkerPatch patch(literal_offset, Type::kTypeRelative, target_dex_file);
- patch.type_idx_ = target_type_idx;
- patch.pc_insn_offset_ = pc_insn_offset;
- return patch;
- }
-
- static LinkerPatch TypeBssEntryPatch(size_t literal_offset,
- const DexFile* target_dex_file,
- uint32_t pc_insn_offset,
- uint32_t target_type_idx) {
- LinkerPatch patch(literal_offset, Type::kTypeBssEntry, target_dex_file);
- patch.type_idx_ = target_type_idx;
- patch.pc_insn_offset_ = pc_insn_offset;
- return patch;
- }
-
- static LinkerPatch RelativeStringPatch(size_t literal_offset,
- const DexFile* target_dex_file,
- uint32_t pc_insn_offset,
- uint32_t target_string_idx) {
- LinkerPatch patch(literal_offset, Type::kStringRelative, target_dex_file);
- patch.string_idx_ = target_string_idx;
- patch.pc_insn_offset_ = pc_insn_offset;
- return patch;
- }
-
- static LinkerPatch StringBssEntryPatch(size_t literal_offset,
- const DexFile* target_dex_file,
- uint32_t pc_insn_offset,
- uint32_t target_string_idx) {
- LinkerPatch patch(literal_offset, Type::kStringBssEntry, target_dex_file);
- patch.string_idx_ = target_string_idx;
- patch.pc_insn_offset_ = pc_insn_offset;
- return patch;
- }
-
- static LinkerPatch BakerReadBarrierBranchPatch(size_t literal_offset,
- uint32_t custom_value1 = 0u,
- uint32_t custom_value2 = 0u) {
- LinkerPatch patch(literal_offset, Type::kBakerReadBarrierBranch, nullptr);
- patch.baker_custom_value1_ = custom_value1;
- patch.baker_custom_value2_ = custom_value2;
- return patch;
- }
-
- LinkerPatch(const LinkerPatch& other) = default;
- LinkerPatch& operator=(const LinkerPatch& other) = default;
-
- size_t LiteralOffset() const {
- return literal_offset_;
- }
-
- Type GetType() const {
- return patch_type_;
- }
-
- bool IsPcRelative() const {
- switch (GetType()) {
- case Type::kMethodRelative:
- case Type::kMethodBssEntry:
- case Type::kCallRelative:
- case Type::kTypeRelative:
- case Type::kTypeBssEntry:
- case Type::kStringRelative:
- case Type::kStringBssEntry:
- case Type::kBakerReadBarrierBranch:
- return true;
- default:
- return false;
- }
- }
-
- MethodReference TargetMethod() const {
- DCHECK(patch_type_ == Type::kMethodRelative ||
- patch_type_ == Type::kMethodBssEntry ||
- patch_type_ == Type::kCall ||
- patch_type_ == Type::kCallRelative);
- return MethodReference(target_dex_file_, method_idx_);
- }
-
- const DexFile* TargetTypeDexFile() const {
- DCHECK(patch_type_ == Type::kTypeRelative ||
- patch_type_ == Type::kTypeBssEntry);
- return target_dex_file_;
- }
-
- dex::TypeIndex TargetTypeIndex() const {
- DCHECK(patch_type_ == Type::kTypeRelative ||
- patch_type_ == Type::kTypeBssEntry);
- return dex::TypeIndex(type_idx_);
- }
-
- const DexFile* TargetStringDexFile() const {
- DCHECK(patch_type_ == Type::kStringRelative ||
- patch_type_ == Type::kStringBssEntry);
- return target_dex_file_;
- }
-
- dex::StringIndex TargetStringIndex() const {
- DCHECK(patch_type_ == Type::kStringRelative ||
- patch_type_ == Type::kStringBssEntry);
- return dex::StringIndex(string_idx_);
- }
-
- uint32_t PcInsnOffset() const {
- DCHECK(patch_type_ == Type::kMethodRelative ||
- patch_type_ == Type::kMethodBssEntry ||
- patch_type_ == Type::kTypeRelative ||
- patch_type_ == Type::kTypeBssEntry ||
- patch_type_ == Type::kStringRelative ||
- patch_type_ == Type::kStringBssEntry);
- return pc_insn_offset_;
- }
-
- uint32_t GetBakerCustomValue1() const {
- DCHECK(patch_type_ == Type::kBakerReadBarrierBranch);
- return baker_custom_value1_;
- }
-
- uint32_t GetBakerCustomValue2() const {
- DCHECK(patch_type_ == Type::kBakerReadBarrierBranch);
- return baker_custom_value2_;
- }
-
- private:
- LinkerPatch(size_t literal_offset, Type patch_type, const DexFile* target_dex_file)
- : target_dex_file_(target_dex_file),
- literal_offset_(literal_offset),
- patch_type_(patch_type) {
- cmp1_ = 0u;
- cmp2_ = 0u;
- // The compiler rejects methods that are too big, so the compiled code
- // of a single method really shouln't be anywhere close to 16MiB.
- DCHECK(IsUint<24>(literal_offset));
- }
-
- const DexFile* target_dex_file_;
- // TODO: Clean up naming. Some patched locations are literals but others are not.
- uint32_t literal_offset_ : 24; // Method code size up to 16MiB.
- Type patch_type_ : 8;
- union {
- uint32_t cmp1_; // Used for relational operators.
- uint32_t method_idx_; // Method index for Call/Method patches.
- uint32_t type_idx_; // Type index for Type patches.
- uint32_t string_idx_; // String index for String patches.
- uint32_t baker_custom_value1_;
- static_assert(sizeof(method_idx_) == sizeof(cmp1_), "needed by relational operators");
- static_assert(sizeof(type_idx_) == sizeof(cmp1_), "needed by relational operators");
- static_assert(sizeof(string_idx_) == sizeof(cmp1_), "needed by relational operators");
- static_assert(sizeof(baker_custom_value1_) == sizeof(cmp1_), "needed by relational operators");
- };
- union {
- // Note: To avoid uninitialized padding on 64-bit systems, we use `size_t` for `cmp2_`.
- // This allows a hashing function to treat an array of linker patches as raw memory.
- size_t cmp2_; // Used for relational operators.
- // Literal offset of the insn loading PC (same as literal_offset if it's the same insn,
- // may be different if the PC-relative addressing needs multiple insns).
- uint32_t pc_insn_offset_;
- uint32_t baker_custom_value2_;
- static_assert(sizeof(pc_insn_offset_) <= sizeof(cmp2_), "needed by relational operators");
- static_assert(sizeof(baker_custom_value2_) <= sizeof(cmp2_), "needed by relational operators");
- };
-
- friend bool operator==(const LinkerPatch& lhs, const LinkerPatch& rhs);
- friend bool operator<(const LinkerPatch& lhs, const LinkerPatch& rhs);
-};
-std::ostream& operator<<(std::ostream& os, const LinkerPatch::Type& type);
-
-inline bool operator==(const LinkerPatch& lhs, const LinkerPatch& rhs) {
- return lhs.literal_offset_ == rhs.literal_offset_ &&
- lhs.patch_type_ == rhs.patch_type_ &&
- lhs.target_dex_file_ == rhs.target_dex_file_ &&
- lhs.cmp1_ == rhs.cmp1_ &&
- lhs.cmp2_ == rhs.cmp2_;
-}
-
-inline bool operator<(const LinkerPatch& lhs, const LinkerPatch& rhs) {
- return (lhs.literal_offset_ != rhs.literal_offset_) ? lhs.literal_offset_ < rhs.literal_offset_
- : (lhs.patch_type_ != rhs.patch_type_) ? lhs.patch_type_ < rhs.patch_type_
- : (lhs.target_dex_file_ != rhs.target_dex_file_) ? lhs.target_dex_file_ < rhs.target_dex_file_
- : (lhs.cmp1_ != rhs.cmp1_) ? lhs.cmp1_ < rhs.cmp1_
- : lhs.cmp2_ < rhs.cmp2_;
-}
-
class CompiledMethod FINAL : public CompiledCode {
public:
// Constructs a CompiledMethod.
@@ -368,7 +118,7 @@
const ArrayRef<const uint8_t>& method_info,
const ArrayRef<const uint8_t>& vmap_table,
const ArrayRef<const uint8_t>& cfi_info,
- const ArrayRef<const LinkerPatch>& patches);
+ const ArrayRef<const linker::LinkerPatch>& patches);
virtual ~CompiledMethod();
@@ -382,10 +132,22 @@
const ArrayRef<const uint8_t>& method_info,
const ArrayRef<const uint8_t>& vmap_table,
const ArrayRef<const uint8_t>& cfi_info,
- const ArrayRef<const LinkerPatch>& patches);
+ const ArrayRef<const linker::LinkerPatch>& patches);
static void ReleaseSwapAllocatedCompiledMethod(CompilerDriver* driver, CompiledMethod* m);
+ bool IsIntrinsic() const {
+ return GetPackedField<IsIntrinsicField>();
+ }
+
+ // Marks the compiled method as being generated using an intrinsic codegen.
+ // Such methods have no relationships to their code items.
+ // This affects debug information generated at link time.
+ void MarkAsIntrinsic() {
+ DCHECK(!IsIntrinsic());
+ SetPackedField<IsIntrinsicField>(/* value */ true);
+ }
+
size_t GetFrameSizeInBytes() const {
return frame_size_in_bytes_;
}
@@ -398,23 +160,23 @@
return fp_spill_mask_;
}
- ArrayRef<const uint8_t> GetMethodInfo() const {
- return GetArray(method_info_);
- }
+ ArrayRef<const uint8_t> GetMethodInfo() const;
- ArrayRef<const uint8_t> GetVmapTable() const {
- return GetArray(vmap_table_);
- }
+ ArrayRef<const uint8_t> GetVmapTable() const;
- ArrayRef<const uint8_t> GetCFIInfo() const {
- return GetArray(cfi_info_);
- }
+ ArrayRef<const uint8_t> GetCFIInfo() const;
- ArrayRef<const LinkerPatch> GetPatches() const {
- return GetArray(patches_);
- }
+ ArrayRef<const linker::LinkerPatch> GetPatches() const;
private:
+ static constexpr size_t kIsIntrinsicLsb = kNumberOfCompiledCodePackedBits;
+ static constexpr size_t kIsIntrinsicSize = 1u;
+ static constexpr size_t kNumberOfCompiledMethodPackedBits = kIsIntrinsicLsb + kIsIntrinsicSize;
+ static_assert(kNumberOfCompiledMethodPackedBits <= CompiledCode::kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+
+ using IsIntrinsicField = BitField<bool, kIsIntrinsicLsb, kIsIntrinsicSize>;
+
// For quick code, the size of the activation used by the code.
const size_t frame_size_in_bytes_;
// For quick code, a bit mask describing spilled GPR callee-save registers.
@@ -428,7 +190,7 @@
// For quick code, a FDE entry for the debug_frame section.
const LengthPrefixedArray<uint8_t>* const cfi_info_;
// For quick code, linker patches needed by the method.
- const LengthPrefixedArray<LinkerPatch>* const patches_;
+ const LengthPrefixedArray<linker::LinkerPatch>* const patches_;
};
} // namespace art
diff --git a/compiler/compiled_method_test.cc b/compiler/compiled_method_test.cc
deleted file mode 100644
index f4a72cf..0000000
--- a/compiler/compiled_method_test.cc
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright (C) 2015 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 <gtest/gtest.h>
-
-#include "compiled_method.h"
-
-namespace art {
-
-TEST(CompiledMethod, SrcMapElemOperators) {
- SrcMapElem elems[] = {
- { 1u, -1 },
- { 1u, 0 },
- { 1u, 1 },
- { 2u, -1 },
- { 2u, 0 }, // Index 4.
- { 2u, 1 },
- { 2u, 0u }, // Index 6: Arbitrarily add identical SrcMapElem with index 4.
- };
-
- for (size_t i = 0; i != arraysize(elems); ++i) {
- for (size_t j = 0; j != arraysize(elems); ++j) {
- bool expected = (i != 6u ? i : 4u) == (j != 6u ? j : 4u);
- EXPECT_EQ(expected, elems[i] == elems[j]) << i << " " << j;
- }
- }
-
- for (size_t i = 0; i != arraysize(elems); ++i) {
- for (size_t j = 0; j != arraysize(elems); ++j) {
- bool expected = (i != 6u ? i : 4u) < (j != 6u ? j : 4u);
- EXPECT_EQ(expected, elems[i] < elems[j]) << i << " " << j;
- }
- }
-}
-
-TEST(CompiledMethod, LinkerPatchOperators) {
- const DexFile* dex_file1 = reinterpret_cast<const DexFile*>(1);
- const DexFile* dex_file2 = reinterpret_cast<const DexFile*>(2);
- LinkerPatch patches[] = {
- LinkerPatch::RelativeMethodPatch(16u, dex_file1, 3000u, 1000u),
- LinkerPatch::RelativeMethodPatch(16u, dex_file1, 3001u, 1000u),
- LinkerPatch::RelativeMethodPatch(16u, dex_file1, 3000u, 1001u),
- LinkerPatch::RelativeMethodPatch(16u, dex_file1, 3001u, 1001u), // Index 3.
- LinkerPatch::RelativeMethodPatch(16u, dex_file2, 3000u, 1000u),
- LinkerPatch::RelativeMethodPatch(16u, dex_file2, 3001u, 1000u),
- LinkerPatch::RelativeMethodPatch(16u, dex_file2, 3000u, 1001u),
- LinkerPatch::RelativeMethodPatch(16u, dex_file2, 3001u, 1001u),
- LinkerPatch::MethodBssEntryPatch(16u, dex_file1, 3000u, 1000u),
- LinkerPatch::MethodBssEntryPatch(16u, dex_file1, 3001u, 1000u),
- LinkerPatch::MethodBssEntryPatch(16u, dex_file1, 3000u, 1001u),
- LinkerPatch::MethodBssEntryPatch(16u, dex_file1, 3001u, 1001u),
- LinkerPatch::MethodBssEntryPatch(16u, dex_file2, 3000u, 1000u),
- LinkerPatch::MethodBssEntryPatch(16u, dex_file2, 3001u, 1000u),
- LinkerPatch::MethodBssEntryPatch(16u, dex_file2, 3000u, 1001u),
- LinkerPatch::MethodBssEntryPatch(16u, dex_file2, 3001u, 1001u),
- LinkerPatch::CodePatch(16u, dex_file1, 1000u),
- LinkerPatch::CodePatch(16u, dex_file1, 1001u),
- LinkerPatch::CodePatch(16u, dex_file2, 1000u),
- LinkerPatch::CodePatch(16u, dex_file2, 1001u),
- LinkerPatch::RelativeCodePatch(16u, dex_file1, 1000u),
- LinkerPatch::RelativeCodePatch(16u, dex_file1, 1001u),
- LinkerPatch::RelativeCodePatch(16u, dex_file2, 1000u),
- LinkerPatch::RelativeCodePatch(16u, dex_file2, 1001u),
- LinkerPatch::RelativeTypePatch(16u, dex_file1, 3000u, 1000u),
- LinkerPatch::RelativeTypePatch(16u, dex_file1, 3001u, 1000u),
- LinkerPatch::RelativeTypePatch(16u, dex_file1, 3000u, 1001u),
- LinkerPatch::RelativeTypePatch(16u, dex_file1, 3001u, 1001u),
- LinkerPatch::RelativeTypePatch(16u, dex_file2, 3000u, 1000u),
- LinkerPatch::RelativeTypePatch(16u, dex_file2, 3001u, 1000u),
- LinkerPatch::RelativeTypePatch(16u, dex_file2, 3000u, 1001u),
- LinkerPatch::RelativeTypePatch(16u, dex_file2, 3001u, 1001u),
- LinkerPatch::TypeBssEntryPatch(16u, dex_file1, 3000u, 1000u),
- LinkerPatch::TypeBssEntryPatch(16u, dex_file1, 3001u, 1000u),
- LinkerPatch::TypeBssEntryPatch(16u, dex_file1, 3000u, 1001u),
- LinkerPatch::TypeBssEntryPatch(16u, dex_file1, 3001u, 1001u),
- LinkerPatch::TypeBssEntryPatch(16u, dex_file2, 3000u, 1000u),
- LinkerPatch::TypeBssEntryPatch(16u, dex_file2, 3001u, 1000u),
- LinkerPatch::TypeBssEntryPatch(16u, dex_file2, 3000u, 1001u),
- LinkerPatch::TypeBssEntryPatch(16u, dex_file2, 3001u, 1001u),
- LinkerPatch::RelativeStringPatch(16u, dex_file1, 3000u, 1000u),
- LinkerPatch::RelativeStringPatch(16u, dex_file1, 3001u, 1000u),
- LinkerPatch::RelativeStringPatch(16u, dex_file1, 3000u, 1001u),
- LinkerPatch::RelativeStringPatch(16u, dex_file1, 3001u, 1001u),
- LinkerPatch::RelativeStringPatch(16u, dex_file2, 3000u, 1000u),
- LinkerPatch::RelativeStringPatch(16u, dex_file2, 3001u, 1000u),
- LinkerPatch::RelativeStringPatch(16u, dex_file2, 3000u, 1001u),
- LinkerPatch::RelativeStringPatch(16u, dex_file2, 3001u, 1001u),
- LinkerPatch::StringBssEntryPatch(16u, dex_file1, 3000u, 1000u),
- LinkerPatch::StringBssEntryPatch(16u, dex_file1, 3001u, 1000u),
- LinkerPatch::StringBssEntryPatch(16u, dex_file1, 3000u, 1001u),
- LinkerPatch::StringBssEntryPatch(16u, dex_file1, 3001u, 1001u),
- LinkerPatch::StringBssEntryPatch(16u, dex_file2, 3000u, 1000u),
- LinkerPatch::StringBssEntryPatch(16u, dex_file2, 3001u, 1000u),
- LinkerPatch::StringBssEntryPatch(16u, dex_file2, 3000u, 1001u),
- LinkerPatch::StringBssEntryPatch(16u, dex_file2, 3001u, 1001u),
- LinkerPatch::BakerReadBarrierBranchPatch(16u, 0u, 0u),
- LinkerPatch::BakerReadBarrierBranchPatch(16u, 0u, 1u),
- LinkerPatch::BakerReadBarrierBranchPatch(16u, 1u, 0u),
- LinkerPatch::BakerReadBarrierBranchPatch(16u, 1u, 1u),
-
- LinkerPatch::RelativeMethodPatch(32u, dex_file1, 3000u, 1000u),
- LinkerPatch::RelativeMethodPatch(32u, dex_file1, 3001u, 1000u),
- LinkerPatch::RelativeMethodPatch(32u, dex_file1, 3000u, 1001u),
- LinkerPatch::RelativeMethodPatch(32u, dex_file1, 3001u, 1001u),
- LinkerPatch::RelativeMethodPatch(32u, dex_file2, 3000u, 1000u),
- LinkerPatch::RelativeMethodPatch(32u, dex_file2, 3001u, 1000u),
- LinkerPatch::RelativeMethodPatch(32u, dex_file2, 3000u, 1001u),
- LinkerPatch::RelativeMethodPatch(32u, dex_file2, 3001u, 1001u),
- LinkerPatch::MethodBssEntryPatch(32u, dex_file1, 3000u, 1000u),
- LinkerPatch::MethodBssEntryPatch(32u, dex_file1, 3001u, 1000u),
- LinkerPatch::MethodBssEntryPatch(32u, dex_file1, 3000u, 1001u),
- LinkerPatch::MethodBssEntryPatch(32u, dex_file1, 3001u, 1001u),
- LinkerPatch::MethodBssEntryPatch(32u, dex_file2, 3000u, 1000u),
- LinkerPatch::MethodBssEntryPatch(32u, dex_file2, 3001u, 1000u),
- LinkerPatch::MethodBssEntryPatch(32u, dex_file2, 3000u, 1001u),
- LinkerPatch::MethodBssEntryPatch(32u, dex_file2, 3001u, 1001u),
- LinkerPatch::CodePatch(32u, dex_file1, 1000u),
- LinkerPatch::CodePatch(32u, dex_file1, 1001u),
- LinkerPatch::CodePatch(32u, dex_file2, 1000u),
- LinkerPatch::CodePatch(32u, dex_file2, 1001u),
- LinkerPatch::RelativeCodePatch(32u, dex_file1, 1000u),
- LinkerPatch::RelativeCodePatch(32u, dex_file1, 1001u),
- LinkerPatch::RelativeCodePatch(32u, dex_file2, 1000u),
- LinkerPatch::RelativeCodePatch(32u, dex_file2, 1001u),
- LinkerPatch::RelativeTypePatch(32u, dex_file1, 3000u, 1000u),
- LinkerPatch::RelativeTypePatch(32u, dex_file1, 3001u, 1000u),
- LinkerPatch::RelativeTypePatch(32u, dex_file1, 3000u, 1001u),
- LinkerPatch::RelativeTypePatch(32u, dex_file1, 3001u, 1001u),
- LinkerPatch::RelativeTypePatch(32u, dex_file2, 3000u, 1000u),
- LinkerPatch::RelativeTypePatch(32u, dex_file2, 3001u, 1000u),
- LinkerPatch::RelativeTypePatch(32u, dex_file2, 3000u, 1001u),
- LinkerPatch::RelativeTypePatch(32u, dex_file2, 3001u, 1001u),
- LinkerPatch::TypeBssEntryPatch(32u, dex_file1, 3000u, 1000u),
- LinkerPatch::TypeBssEntryPatch(32u, dex_file1, 3001u, 1000u),
- LinkerPatch::TypeBssEntryPatch(32u, dex_file1, 3000u, 1001u),
- LinkerPatch::TypeBssEntryPatch(32u, dex_file1, 3001u, 1001u),
- LinkerPatch::TypeBssEntryPatch(32u, dex_file2, 3000u, 1000u),
- LinkerPatch::TypeBssEntryPatch(32u, dex_file2, 3001u, 1000u),
- LinkerPatch::TypeBssEntryPatch(32u, dex_file2, 3000u, 1001u),
- LinkerPatch::TypeBssEntryPatch(32u, dex_file2, 3001u, 1001u),
- LinkerPatch::RelativeStringPatch(32u, dex_file1, 3000u, 1000u),
- LinkerPatch::RelativeStringPatch(32u, dex_file1, 3001u, 1000u),
- LinkerPatch::RelativeStringPatch(32u, dex_file1, 3000u, 1001u),
- LinkerPatch::RelativeStringPatch(32u, dex_file1, 3001u, 1001u),
- LinkerPatch::RelativeStringPatch(32u, dex_file2, 3000u, 1000u),
- LinkerPatch::RelativeStringPatch(32u, dex_file2, 3001u, 1000u),
- LinkerPatch::RelativeStringPatch(32u, dex_file2, 3000u, 1001u),
- LinkerPatch::RelativeStringPatch(32u, dex_file2, 3001u, 1001u),
- LinkerPatch::StringBssEntryPatch(32u, dex_file1, 3000u, 1000u),
- LinkerPatch::StringBssEntryPatch(32u, dex_file1, 3001u, 1000u),
- LinkerPatch::StringBssEntryPatch(32u, dex_file1, 3000u, 1001u),
- LinkerPatch::StringBssEntryPatch(32u, dex_file1, 3001u, 1001u),
- LinkerPatch::StringBssEntryPatch(32u, dex_file2, 3000u, 1000u),
- LinkerPatch::StringBssEntryPatch(32u, dex_file2, 3001u, 1000u),
- LinkerPatch::StringBssEntryPatch(32u, dex_file2, 3000u, 1001u),
- LinkerPatch::StringBssEntryPatch(32u, dex_file2, 3001u, 1001u),
- LinkerPatch::BakerReadBarrierBranchPatch(32u, 0u, 0u),
- LinkerPatch::BakerReadBarrierBranchPatch(32u, 0u, 1u),
- LinkerPatch::BakerReadBarrierBranchPatch(32u, 1u, 0u),
- LinkerPatch::BakerReadBarrierBranchPatch(32u, 1u, 1u),
-
- LinkerPatch::RelativeMethodPatch(16u, dex_file1, 3001u, 1001u), // Same as patch at index 3.
- };
- constexpr size_t last_index = arraysize(patches) - 1u;
-
- for (size_t i = 0; i != arraysize(patches); ++i) {
- for (size_t j = 0; j != arraysize(patches); ++j) {
- bool expected = (i != last_index ? i : 3u) == (j != last_index ? j : 3u);
- EXPECT_EQ(expected, patches[i] == patches[j]) << i << " " << j;
- }
- }
-
- for (size_t i = 0; i != arraysize(patches); ++i) {
- for (size_t j = 0; j != arraysize(patches); ++j) {
- bool expected = (i != last_index ? i : 3u) < (j != last_index ? j : 3u);
- EXPECT_EQ(expected, patches[i] < patches[j]) << i << " " << j;
- }
- }
-}
-
-} // namespace art
diff --git a/compiler/compiler.cc b/compiler/compiler.cc
index c500921..646040f 100644
--- a/compiler/compiler.cc
+++ b/compiler/compiler.cc
@@ -16,10 +16,13 @@
#include "compiler.h"
-#include "base/logging.h"
+#include <android-base/logging.h>
+
+#include "base/macros.h"
+#include "base/utils.h"
+#include "dex/code_item_accessors-inl.h"
#include "driver/compiler_driver.h"
#include "optimizing/optimizing_compiler.h"
-#include "utils.h"
namespace art {
@@ -44,15 +47,16 @@
* Dalvik uses 16-bit uints for instruction and register counts. We'll limit to a quarter
* of that, which also guarantees we cannot overflow our 16-bit internal Quick SSA name space.
*/
- if (code_item.insns_size_in_code_units_ >= UINT16_MAX / 4) {
+ CodeItemDataAccessor accessor(dex_file, &code_item);
+ if (accessor.InsnsSizeInCodeUnits() >= UINT16_MAX / 4) {
LOG(INFO) << "Method exceeds compiler instruction limit: "
- << code_item.insns_size_in_code_units_
+ << accessor.InsnsSizeInCodeUnits()
<< " in " << dex_file.PrettyMethod(method_idx);
return true;
}
- if (code_item.registers_size_ >= UINT16_MAX / 4) {
+ if (accessor.RegistersSize() >= UINT16_MAX / 4) {
LOG(INFO) << "Method exceeds compiler virtual register limit: "
- << code_item.registers_size_ << " in " << dex_file.PrettyMethod(method_idx);
+ << accessor.RegistersSize() << " in " << dex_file.PrettyMethod(method_idx);
return true;
}
return false;
diff --git a/compiler/compiler.h b/compiler/compiler.h
index ba89cb1..f2ec3a9 100644
--- a/compiler/compiler.h
+++ b/compiler/compiler.h
@@ -17,19 +17,19 @@
#ifndef ART_COMPILER_COMPILER_H_
#define ART_COMPILER_COMPILER_H_
-#include "dex_file.h"
#include "base/mutex.h"
-#include "os.h"
+#include "base/os.h"
+#include "dex/dex_file.h"
namespace art {
namespace jit {
- class JitCodeCache;
- class JitLogger;
+class JitCodeCache;
+class JitLogger;
} // namespace jit
namespace mirror {
- class ClassLoader;
- class DexCache;
+class ClassLoader;
+class DexCache;
} // namespace mirror
class ArtMethod;
@@ -39,6 +39,12 @@
class OatWriter;
class Thread;
+enum class CopyOption {
+ kNever,
+ kAlways,
+ kOnlyIfCompressed
+};
+
class Compiler {
public:
enum Kind {
@@ -46,12 +52,6 @@
kOptimizing
};
- enum JniOptimizationFlags {
- kNone = 0x0,
- kFastNative = 0x1,
- kCriticalNative = 0x2,
- };
-
static Compiler* Create(CompilerDriver* driver, Kind kind);
virtual void Init() = 0;
@@ -72,7 +72,7 @@
virtual CompiledMethod* JniCompile(uint32_t access_flags,
uint32_t method_idx,
const DexFile& dex_file,
- JniOptimizationFlags optimization_flags) const = 0;
+ Handle<mirror::DexCache> dex_cache) const = 0;
virtual bool JitCompile(Thread* self ATTRIBUTE_UNUSED,
jit::JitCodeCache* code_cache ATTRIBUTE_UNUSED,
diff --git a/compiler/debug/debug_info.h b/compiler/debug/debug_info.h
new file mode 100644
index 0000000..04c6991
--- /dev/null
+++ b/compiler/debug/debug_info.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_COMPILER_DEBUG_DEBUG_INFO_H_
+#define ART_COMPILER_DEBUG_DEBUG_INFO_H_
+
+#include <map>
+
+#include "base/array_ref.h"
+#include "method_debug_info.h"
+
+namespace art {
+class DexFile;
+
+namespace debug {
+
+// References inputs for all debug information which can be written into the ELF file.
+struct DebugInfo {
+ // Describes compiled code in the .text section.
+ ArrayRef<const MethodDebugInfo> compiled_methods;
+
+ // Describes dex-files in the .dex section.
+ std::map<uint32_t, const DexFile*> dex_files; // Offset in section -> dex file content.
+
+ bool Empty() const {
+ return compiled_methods.empty() && dex_files.empty();
+ }
+};
+
+} // namespace debug
+} // namespace art
+
+#endif // ART_COMPILER_DEBUG_DEBUG_INFO_H_
diff --git a/compiler/debug/dwarf/debug_abbrev_writer.h b/compiler/debug/dwarf/debug_abbrev_writer.h
index 0fc843c..cccca25 100644
--- a/compiler/debug/dwarf/debug_abbrev_writer.h
+++ b/compiler/debug/dwarf/debug_abbrev_writer.h
@@ -22,10 +22,10 @@
#include <unordered_map>
#include "base/casts.h"
+#include "base/leb128.h"
#include "base/stl_util.h"
#include "debug/dwarf/dwarf_constants.h"
#include "debug/dwarf/writer.h"
-#include "leb128.h"
namespace art {
namespace dwarf {
diff --git a/compiler/debug/dwarf/debug_info_entry_writer.h b/compiler/debug/dwarf/debug_info_entry_writer.h
index 85f021e..89d16f2 100644
--- a/compiler/debug/dwarf/debug_info_entry_writer.h
+++ b/compiler/debug/dwarf/debug_info_entry_writer.h
@@ -21,11 +21,11 @@
#include <unordered_map>
#include "base/casts.h"
+#include "base/leb128.h"
#include "debug/dwarf/debug_abbrev_writer.h"
#include "debug/dwarf/dwarf_constants.h"
#include "debug/dwarf/expression.h"
#include "debug/dwarf/writer.h"
-#include "leb128.h"
namespace art {
namespace dwarf {
diff --git a/compiler/debug/dwarf/dwarf_test.cc b/compiler/debug/dwarf/dwarf_test.cc
index 866bf4394..933034f 100644
--- a/compiler/debug/dwarf/dwarf_test.cc
+++ b/compiler/debug/dwarf/dwarf_test.cc
@@ -125,7 +125,7 @@
WriteCIE(is64bit, Reg(is64bit ? 16 : 8),
initial_opcodes, kCFIFormat, &debug_frame_data_);
std::vector<uintptr_t> debug_frame_patches;
- std::vector<uintptr_t> expected_patches { 28 }; // NOLINT
+ std::vector<uintptr_t> expected_patches = { 28 };
WriteFDE(is64bit, 0, 0, 0x01000000, 0x01000000, ArrayRef<const uint8_t>(*opcodes.data()),
kCFIFormat, 0, &debug_frame_data_, &debug_frame_patches);
@@ -140,7 +140,7 @@
initial_opcodes, kCFIFormat, &debug_frame_data_);
DebugFrameOpCodeWriter<> opcodes;
std::vector<uintptr_t> debug_frame_patches;
- std::vector<uintptr_t> expected_patches { 32 }; // NOLINT
+ std::vector<uintptr_t> expected_patches = { 32 };
WriteFDE(is64bit, 0, 0, 0x0100000000000000, 0x0200000000000000,
ArrayRef<const uint8_t>(*opcodes.data()),
kCFIFormat, 0, &debug_frame_data_, &debug_frame_patches);
@@ -237,7 +237,7 @@
DW_CHECK_NEXT("1\t0\t1000\t2000\tfile.c");
std::vector<uintptr_t> debug_line_patches;
- std::vector<uintptr_t> expected_patches { 87 }; // NOLINT
+ std::vector<uintptr_t> expected_patches = { 87 };
WriteDebugLineTable(include_directories, files, opcodes,
0, &debug_line_data_, &debug_line_patches);
@@ -275,7 +275,7 @@
EXPECT_LT(opcodes.data()->size(), num_rows * 3);
std::vector<std::string> directories;
- std::vector<FileEntry> files { { "file.c", 0, 1000, 2000 } }; // NOLINT
+ std::vector<FileEntry> files = { { "file.c", 0, 1000, 2000 } };
std::vector<uintptr_t> debug_line_patches;
WriteDebugLineTable(directories, files, opcodes,
0, &debug_line_data_, &debug_line_patches);
@@ -333,7 +333,7 @@
DW_CHECK("3 DW_TAG_compile_unit [no children]");
std::vector<uintptr_t> debug_info_patches;
- std::vector<uintptr_t> expected_patches { 16, 20, 29, 33, 42, 46 }; // NOLINT
+ std::vector<uintptr_t> expected_patches = { 16, 20, 29, 33, 42, 46 };
dwarf::WriteDebugInfoCU(0 /* debug_abbrev_offset */, info,
0, &debug_info_data_, &debug_info_patches);
diff --git a/compiler/debug/dwarf/dwarf_test.h b/compiler/debug/dwarf/dwarf_test.h
index e2f0a65..9a7c604 100644
--- a/compiler/debug/dwarf/dwarf_test.h
+++ b/compiler/debug/dwarf/dwarf_test.h
@@ -17,20 +17,21 @@
#ifndef ART_COMPILER_DEBUG_DWARF_DWARF_TEST_H_
#define ART_COMPILER_DEBUG_DWARF_DWARF_TEST_H_
-#include <cstring>
#include <dirent.h>
-#include <memory>
-#include <set>
#include <stdio.h>
-#include <string>
#include <sys/types.h>
+#include <cstring>
+#include <memory>
+#include <set>
+#include <string>
+
+#include "base/os.h"
#include "base/unix_file/fd_file.h"
#include "common_runtime_test.h"
-#include "elf_builder.h"
#include "gtest/gtest.h"
+#include "linker/elf_builder.h"
#include "linker/file_output_stream.h"
-#include "os.h"
namespace art {
namespace dwarf {
@@ -59,10 +60,11 @@
template<typename ElfTypes>
std::vector<std::string> Objdump(const char* args) {
// Write simple elf file with just the DWARF sections.
- InstructionSet isa = (sizeof(typename ElfTypes::Addr) == 8) ? kX86_64 : kX86;
+ InstructionSet isa =
+ (sizeof(typename ElfTypes::Addr) == 8) ? InstructionSet::kX86_64 : InstructionSet::kX86;
ScratchFile file;
- FileOutputStream output_stream(file.GetFile());
- ElfBuilder<ElfTypes> builder(isa, nullptr, &output_stream);
+ linker::FileOutputStream output_stream(file.GetFile());
+ linker::ElfBuilder<ElfTypes> builder(isa, nullptr, &output_stream);
builder.Start();
if (!debug_info_data_.empty()) {
builder.WriteSection(".debug_info", &debug_info_data_);
diff --git a/compiler/debug/dwarf/writer.h b/compiler/debug/dwarf/writer.h
index 95912ad..c09d97a 100644
--- a/compiler/debug/dwarf/writer.h
+++ b/compiler/debug/dwarf/writer.h
@@ -19,9 +19,11 @@
#include <type_traits>
#include <vector>
+
+#include <android-base/logging.h>
+
#include "base/bit_utils.h"
-#include "base/logging.h"
-#include "leb128.h"
+#include "base/leb128.h"
namespace art {
namespace dwarf {
diff --git a/compiler/debug/elf_debug_frame_writer.h b/compiler/debug/elf_debug_frame_writer.h
index f9d33c1..27b70c8 100644
--- a/compiler/debug/elf_debug_frame_writer.h
+++ b/compiler/debug/elf_debug_frame_writer.h
@@ -24,7 +24,7 @@
#include "debug/dwarf/dwarf_constants.h"
#include "debug/dwarf/headers.h"
#include "debug/method_debug_info.h"
-#include "elf_builder.h"
+#include "linker/elf_builder.h"
namespace art {
namespace debug {
@@ -37,8 +37,8 @@
// debugger that its value in the previous frame is not recoverable.
bool is64bit = Is64BitInstructionSet(isa);
switch (isa) {
- case kArm:
- case kThumb2: {
+ case InstructionSet::kArm:
+ case InstructionSet::kThumb2: {
dwarf::DebugFrameOpCodeWriter<> opcodes;
opcodes.DefCFA(Reg::ArmCore(13), 0); // R13(SP).
// core registers.
@@ -61,7 +61,7 @@
WriteCIE(is64bit, return_reg, opcodes, format, buffer);
return;
}
- case kArm64: {
+ case InstructionSet::kArm64: {
dwarf::DebugFrameOpCodeWriter<> opcodes;
opcodes.DefCFA(Reg::Arm64Core(31), 0); // R31(SP).
// core registers.
@@ -84,8 +84,8 @@
WriteCIE(is64bit, return_reg, opcodes, format, buffer);
return;
}
- case kMips:
- case kMips64: {
+ case InstructionSet::kMips:
+ case InstructionSet::kMips64: {
dwarf::DebugFrameOpCodeWriter<> opcodes;
opcodes.DefCFA(Reg::MipsCore(29), 0); // R29(SP).
// core registers.
@@ -108,7 +108,7 @@
WriteCIE(is64bit, return_reg, opcodes, format, buffer);
return;
}
- case kX86: {
+ case InstructionSet::kX86: {
// FIXME: Add fp registers once libunwind adds support for them. Bug: 20491296
constexpr bool generate_opcodes_for_x86_fp = false;
dwarf::DebugFrameOpCodeWriter<> opcodes;
@@ -134,7 +134,7 @@
WriteCIE(is64bit, return_reg, opcodes, format, buffer);
return;
}
- case kX86_64: {
+ case InstructionSet::kX86_64: {
dwarf::DebugFrameOpCodeWriter<> opcodes;
opcodes.DefCFA(Reg::X86_64Core(4), 8); // R4(RSP).
opcodes.Offset(Reg::X86_64Core(16), -8); // R16(RIP).
@@ -160,7 +160,7 @@
WriteCIE(is64bit, return_reg, opcodes, format, buffer);
return;
}
- case kNone:
+ case InstructionSet::kNone:
break;
}
LOG(FATAL) << "Cannot write CIE frame for ISA " << isa;
@@ -168,7 +168,7 @@
}
template<typename ElfTypes>
-void WriteCFISection(ElfBuilder<ElfTypes>* builder,
+void WriteCFISection(linker::ElfBuilder<ElfTypes>* builder,
const ArrayRef<const MethodDebugInfo>& method_infos,
dwarf::CFIFormat format,
bool write_oat_patches) {
@@ -207,13 +207,12 @@
}
// Write .eh_frame/.debug_frame section.
- auto* cfi_section = (format == dwarf::DW_DEBUG_FRAME_FORMAT
- ? builder->GetDebugFrame()
- : builder->GetEhFrame());
+ const bool is_debug_frame = format == dwarf::DW_DEBUG_FRAME_FORMAT;
+ auto* cfi_section = (is_debug_frame ? builder->GetDebugFrame() : builder->GetEhFrame());
{
cfi_section->Start();
const bool is64bit = Is64BitInstructionSet(builder->GetIsa());
- const Elf_Addr cfi_address = cfi_section->GetAddress();
+ const Elf_Addr cfi_address = (is_debug_frame ? 0 : cfi_section->GetAddress());
const Elf_Addr cie_address = cfi_address;
Elf_Addr buffer_address = cfi_address;
std::vector<uint8_t> buffer; // Small temporary buffer.
diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h
index de32351..893cad2 100644
--- a/compiler/debug/elf_debug_info_writer.h
+++ b/compiler/debug/elf_debug_info_writer.h
@@ -27,13 +27,16 @@
#include "debug/elf_compilation_unit.h"
#include "debug/elf_debug_loc_writer.h"
#include "debug/method_debug_info.h"
-#include "dex_file-inl.h"
-#include "dex_file.h"
-#include "elf_builder.h"
+#include "dex/code_item_accessors-inl.h"
+#include "dex/dex_file-inl.h"
+#include "dex/dex_file.h"
+#include "heap_poisoning.h"
#include "linear_alloc.h"
+#include "linker/elf_builder.h"
#include "mirror/array.h"
#include "mirror/class-inl.h"
#include "mirror/class.h"
+#include "oat_file.h"
namespace art {
namespace debug {
@@ -46,9 +49,10 @@
static std::vector<const char*> GetParamNames(const MethodDebugInfo* mi) {
std::vector<const char*> names;
- if (mi->code_item != nullptr) {
+ CodeItemDebugInfoAccessor accessor(*mi->dex_file, mi->code_item, mi->dex_method_index);
+ if (accessor.HasCodeItem()) {
DCHECK(mi->dex_file != nullptr);
- const uint8_t* stream = mi->dex_file->GetDebugInfoStream(mi->code_item);
+ const uint8_t* stream = mi->dex_file->GetDebugInfoStream(accessor.DebugInfoOffset());
if (stream != nullptr) {
DecodeUnsignedLeb128(&stream); // line.
uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
@@ -67,7 +71,7 @@
using Elf_Addr = typename ElfTypes::Addr;
public:
- explicit ElfDebugInfoWriter(ElfBuilder<ElfTypes>* builder)
+ explicit ElfDebugInfoWriter(linker::ElfBuilder<ElfTypes>* builder)
: builder_(builder),
debug_abbrev_(&debug_abbrev_buffer_) {
}
@@ -92,7 +96,7 @@
}
private:
- ElfBuilder<ElfTypes>* builder_;
+ linker::ElfBuilder<ElfTypes>* builder_;
std::vector<uintptr_t> debug_info_patches_;
std::vector<uint8_t> debug_abbrev_buffer_;
dwarf::DebugAbbrevWriter<> debug_abbrev_;
@@ -122,22 +126,44 @@
const Elf_Addr base_address = compilation_unit.is_code_address_text_relative
? owner_->builder_->GetText()->GetAddress()
: 0;
- const uint64_t cu_size = compilation_unit.code_end - compilation_unit.code_address;
+ const bool is64bit = Is64BitInstructionSet(owner_->builder_->GetIsa());
using namespace dwarf; // NOLINT. For easy access to DWARF constants.
info_.StartTag(DW_TAG_compile_unit);
info_.WriteString(DW_AT_producer, "Android dex2oat");
info_.WriteData1(DW_AT_language, DW_LANG_Java);
info_.WriteString(DW_AT_comp_dir, "$JAVA_SRC_ROOT");
+ // The low_pc acts as base address for several other addresses/ranges.
info_.WriteAddr(DW_AT_low_pc, base_address + compilation_unit.code_address);
- info_.WriteUdata(DW_AT_high_pc, dchecked_integral_cast<uint32_t>(cu_size));
info_.WriteSecOffset(DW_AT_stmt_list, compilation_unit.debug_line_offset);
+ // Write .debug_ranges entries covering code ranges of the whole compilation unit.
+ dwarf::Writer<> debug_ranges(&owner_->debug_ranges_);
+ info_.WriteSecOffset(DW_AT_ranges, owner_->debug_ranges_.size());
+ for (auto mi : compilation_unit.methods) {
+ uint64_t low_pc = mi->code_address - compilation_unit.code_address;
+ uint64_t high_pc = low_pc + mi->code_size;
+ if (is64bit) {
+ debug_ranges.PushUint64(low_pc);
+ debug_ranges.PushUint64(high_pc);
+ } else {
+ debug_ranges.PushUint32(low_pc);
+ debug_ranges.PushUint32(high_pc);
+ }
+ }
+ if (is64bit) {
+ debug_ranges.PushUint64(0); // End of list.
+ debug_ranges.PushUint64(0);
+ } else {
+ debug_ranges.PushUint32(0); // End of list.
+ debug_ranges.PushUint32(0);
+ }
+
const char* last_dex_class_desc = nullptr;
for (auto mi : compilation_unit.methods) {
DCHECK(mi->dex_file != nullptr);
const DexFile* dex = mi->dex_file;
- const DexFile::CodeItem* dex_code = mi->code_item;
+ CodeItemDebugInfoAccessor accessor(*dex, mi->code_item, mi->dex_method_index);
const DexFile::MethodId& dex_method = dex->GetMethodId(mi->dex_method_index);
const DexFile::ProtoId& dex_proto = dex->GetMethodPrototype(dex_method);
const DexFile::TypeList* dex_params = dex->GetProtoParameters(dex_proto);
@@ -179,13 +205,13 @@
// Decode dex register locations for all stack maps.
// It might be expensive, so do it just once and reuse the result.
std::vector<DexRegisterMap> dex_reg_maps;
- if (mi->code_info != nullptr) {
+ if (accessor.HasCodeItem() && mi->code_info != nullptr) {
const CodeInfo code_info(mi->code_info);
CodeInfoEncoding encoding = code_info.ExtractEncoding();
for (size_t s = 0; s < code_info.GetNumberOfStackMaps(encoding); ++s) {
const StackMap& stack_map = code_info.GetStackMapAt(s, encoding);
dex_reg_maps.push_back(code_info.GetDexRegisterMapOf(
- stack_map, encoding, dex_code->registers_size_));
+ stack_map, encoding, accessor.RegistersSize()));
}
}
@@ -199,9 +225,9 @@
WriteName("this");
info_.WriteFlagPresent(DW_AT_artificial);
WriteLazyType(dex_class_desc);
- if (dex_code != nullptr) {
+ if (accessor.HasCodeItem()) {
// Write the stack location of the parameter.
- const uint32_t vreg = dex_code->registers_size_ - dex_code->ins_size_ + arg_reg;
+ const uint32_t vreg = accessor.RegistersSize() - accessor.InsSize() + arg_reg;
const bool is64bitValue = false;
WriteRegLocation(mi, dex_reg_maps, vreg, is64bitValue, compilation_unit.code_address);
}
@@ -219,28 +245,27 @@
const char* type_desc = dex->StringByTypeIdx(dex_params->GetTypeItem(i).type_idx_);
WriteLazyType(type_desc);
const bool is64bitValue = type_desc[0] == 'D' || type_desc[0] == 'J';
- if (dex_code != nullptr) {
+ if (accessor.HasCodeItem()) {
// Write the stack location of the parameter.
- const uint32_t vreg = dex_code->registers_size_ - dex_code->ins_size_ + arg_reg;
+ const uint32_t vreg = accessor.RegistersSize() - accessor.InsSize() + arg_reg;
WriteRegLocation(mi, dex_reg_maps, vreg, is64bitValue, compilation_unit.code_address);
}
arg_reg += is64bitValue ? 2 : 1;
info_.EndTag();
}
- if (dex_code != nullptr) {
- DCHECK_EQ(arg_reg, dex_code->ins_size_);
+ if (accessor.HasCodeItem()) {
+ DCHECK_EQ(arg_reg, accessor.InsSize());
}
}
// Write local variables.
LocalInfos local_infos;
- if (dex->DecodeDebugLocalInfo(dex_code,
- is_static,
- mi->dex_method_index,
- LocalInfoCallback,
- &local_infos)) {
+ if (accessor.DecodeDebugLocalInfo(is_static,
+ mi->dex_method_index,
+ LocalInfoCallback,
+ &local_infos)) {
for (const DexFile::LocalInfo& var : local_infos) {
- if (var.reg_ < dex_code->registers_size_ - dex_code->ins_size_) {
+ if (var.reg_ < accessor.RegistersSize() - accessor.InsSize()) {
info_.StartTag(DW_TAG_variable);
WriteName(var.name_);
WriteLazyType(var.descriptor_);
@@ -269,7 +294,7 @@
CHECK_EQ(info_.Depth(), 0);
std::vector<uint8_t> buffer;
buffer.reserve(info_.data()->size() + KB);
- const size_t offset = owner_->builder_->GetDebugInfo()->GetSize();
+ const size_t offset = owner_->builder_->GetDebugInfo()->GetPosition();
// All compilation units share single table which is at the start of .debug_abbrev.
const size_t debug_abbrev_offset = 0;
WriteDebugInfoCU(debug_abbrev_offset, info_, offset, &buffer, &owner_->debug_info_patches_);
@@ -434,7 +459,7 @@
CHECK_EQ(info_.Depth(), 0);
std::vector<uint8_t> buffer;
buffer.reserve(info_.data()->size() + KB);
- const size_t offset = owner_->builder_->GetDebugInfo()->GetSize();
+ const size_t offset = owner_->builder_->GetDebugInfo()->GetPosition();
// All compilation units share single table which is at the start of .debug_abbrev.
const size_t debug_abbrev_offset = 0;
WriteDebugInfoCU(debug_abbrev_offset, info_, offset, &buffer, &owner_->debug_info_patches_);
diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h
index cdd1e53..44504c1 100644
--- a/compiler/debug/elf_debug_line_writer.h
+++ b/compiler/debug/elf_debug_line_writer.h
@@ -20,12 +20,13 @@
#include <unordered_set>
#include <vector>
-#include "compiled_method.h"
#include "debug/dwarf/debug_line_opcode_writer.h"
#include "debug/dwarf/headers.h"
#include "debug/elf_compilation_unit.h"
-#include "dex_file-inl.h"
-#include "elf_builder.h"
+#include "debug/src_map_elem.h"
+#include "dex/dex_file-inl.h"
+#include "linker/elf_builder.h"
+#include "oat_file.h"
#include "stack_map.h"
namespace art {
@@ -43,7 +44,7 @@
using Elf_Addr = typename ElfTypes::Addr;
public:
- explicit ElfDebugLineWriter(ElfBuilder<ElfTypes>* builder) : builder_(builder) {
+ explicit ElfDebugLineWriter(linker::ElfBuilder<ElfTypes>* builder) : builder_(builder) {
}
void Start() {
@@ -59,7 +60,7 @@
? builder_->GetText()->GetAddress()
: 0;
- compilation_unit.debug_line_offset = builder_->GetDebugLine()->GetSize();
+ compilation_unit.debug_line_offset = builder_->GetDebugLine()->GetPosition();
std::vector<dwarf::FileEntry> files;
std::unordered_map<std::string, size_t> files_map;
@@ -68,19 +69,19 @@
int code_factor_bits_ = 0;
int dwarf_isa = -1;
switch (isa) {
- case kArm: // arm actually means thumb2.
- case kThumb2:
+ case InstructionSet::kArm: // arm actually means thumb2.
+ case InstructionSet::kThumb2:
code_factor_bits_ = 1; // 16-bit instuctions
dwarf_isa = 1; // DW_ISA_ARM_thumb.
break;
- case kArm64:
- case kMips:
- case kMips64:
+ case InstructionSet::kArm64:
+ case InstructionSet::kMips:
+ case InstructionSet::kMips64:
code_factor_bits_ = 2; // 32-bit instructions
break;
- case kNone:
- case kX86:
- case kX86_64:
+ case InstructionSet::kNone:
+ case InstructionSet::kX86:
+ case InstructionSet::kX86_64:
break;
}
std::unordered_set<uint64_t> seen_addresses(compilation_unit.methods.size());
@@ -158,7 +159,9 @@
PositionInfos dex2line_map;
DCHECK(mi->dex_file != nullptr);
const DexFile* dex = mi->dex_file;
- if (!dex->DecodeDebugPositionInfo(mi->code_item, PositionInfoCallback, &dex2line_map)) {
+ CodeItemDebugInfoAccessor accessor(*dex, mi->code_item, mi->dex_method_index);
+ const uint32_t debug_info_offset = accessor.DebugInfoOffset();
+ if (!dex->DecodeDebugPositionInfo(debug_info_offset, PositionInfoCallback, &dex2line_map)) {
continue;
}
@@ -265,7 +268,7 @@
}
std::vector<uint8_t> buffer;
buffer.reserve(opcodes.data()->size() + KB);
- size_t offset = builder_->GetDebugLine()->GetSize();
+ size_t offset = builder_->GetDebugLine()->GetPosition();
WriteDebugLineTable(directories, files, opcodes, offset, &buffer, &debug_line_patches_);
builder_->GetDebugLine()->WriteFully(buffer.data(), buffer.size());
return buffer.size();
@@ -280,7 +283,7 @@
}
private:
- ElfBuilder<ElfTypes>* builder_;
+ linker::ElfBuilder<ElfTypes>* builder_;
std::vector<uintptr_t> debug_line_patches_;
};
diff --git a/compiler/debug/elf_debug_loc_writer.h b/compiler/debug/elf_debug_loc_writer.h
index bf47e8f..9ea9f01 100644
--- a/compiler/debug/elf_debug_loc_writer.h
+++ b/compiler/debug/elf_debug_loc_writer.h
@@ -33,20 +33,20 @@
static Reg GetDwarfCoreReg(InstructionSet isa, int machine_reg) {
switch (isa) {
- case kArm:
- case kThumb2:
+ case InstructionSet::kArm:
+ case InstructionSet::kThumb2:
return Reg::ArmCore(machine_reg);
- case kArm64:
+ case InstructionSet::kArm64:
return Reg::Arm64Core(machine_reg);
- case kX86:
+ case InstructionSet::kX86:
return Reg::X86Core(machine_reg);
- case kX86_64:
+ case InstructionSet::kX86_64:
return Reg::X86_64Core(machine_reg);
- case kMips:
+ case InstructionSet::kMips:
return Reg::MipsCore(machine_reg);
- case kMips64:
+ case InstructionSet::kMips64:
return Reg::Mips64Core(machine_reg);
- case kNone:
+ case InstructionSet::kNone:
LOG(FATAL) << "No instruction set";
}
UNREACHABLE();
@@ -54,20 +54,20 @@
static Reg GetDwarfFpReg(InstructionSet isa, int machine_reg) {
switch (isa) {
- case kArm:
- case kThumb2:
+ case InstructionSet::kArm:
+ case InstructionSet::kThumb2:
return Reg::ArmFp(machine_reg);
- case kArm64:
+ case InstructionSet::kArm64:
return Reg::Arm64Fp(machine_reg);
- case kX86:
+ case InstructionSet::kX86:
return Reg::X86Fp(machine_reg);
- case kX86_64:
+ case InstructionSet::kX86_64:
return Reg::X86_64Fp(machine_reg);
- case kMips:
+ case InstructionSet::kMips:
return Reg::MipsFp(machine_reg);
- case kMips64:
+ case InstructionSet::kMips64:
return Reg::Mips64Fp(machine_reg);
- case kNone:
+ case InstructionSet::kNone:
LOG(FATAL) << "No instruction set";
}
UNREACHABLE();
@@ -149,11 +149,12 @@
DCHECK_LT(stack_map_index, dex_register_maps.size());
DexRegisterMap dex_register_map = dex_register_maps[stack_map_index];
DCHECK(dex_register_map.IsValid());
+ CodeItemDataAccessor accessor(*method_info->dex_file, method_info->code_item);
reg_lo = dex_register_map.GetDexRegisterLocation(
- vreg, method_info->code_item->registers_size_, code_info, encoding);
+ vreg, accessor.RegistersSize(), code_info, encoding);
if (is64bitValue) {
reg_hi = dex_register_map.GetDexRegisterLocation(
- vreg + 1, method_info->code_item->registers_size_, code_info, encoding);
+ vreg + 1, accessor.RegistersSize(), code_info, encoding);
}
// Add location entry for this address range.
@@ -230,7 +231,7 @@
break; // the high word is correctly implied by the low word.
}
} else if (kind == Kind::kInFpuRegister) {
- if ((isa == kArm || isa == kThumb2) &&
+ if ((isa == InstructionSet::kArm || isa == InstructionSet::kThumb2) &&
piece == 0 && reg_hi.GetKind() == Kind::kInFpuRegister &&
reg_hi.GetValue() == value + 1 && value % 2 == 0) {
// Translate S register pair to D register (e.g. S4+S5 to D2).
@@ -251,7 +252,10 @@
// kInStackLargeOffset and kConstantLargeValue are hidden by GetKind().
// kInRegisterHigh and kInFpuRegisterHigh should be handled by
// the special cases above and they should not occur alone.
- LOG(ERROR) << "Unexpected register location kind: " << kind;
+ LOG(WARNING) << "Unexpected register location: " << kind
+ << " (This can indicate either a bug in the dexer when generating"
+ << " local variable information, or a bug in ART compiler."
+ << " Please file a bug at go/art-bug)";
break;
}
if (is64bitValue) {
diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc
index 7fa6e14..59a080f 100644
--- a/compiler/debug/elf_debug_writer.cc
+++ b/compiler/debug/elf_debug_writer.cc
@@ -17,6 +17,7 @@
#include "elf_debug_writer.h"
#include <vector>
+#include <unordered_map>
#include "base/array_ref.h"
#include "debug/dwarf/dwarf_constants.h"
@@ -28,7 +29,7 @@
#include "debug/elf_gnu_debugdata_writer.h"
#include "debug/elf_symtab_writer.h"
#include "debug/method_debug_info.h"
-#include "elf_builder.h"
+#include "linker/elf_builder.h"
#include "linker/vector_output_stream.h"
#include "oat.h"
@@ -36,37 +37,52 @@
namespace debug {
template <typename ElfTypes>
-void WriteDebugInfo(ElfBuilder<ElfTypes>* builder,
- const ArrayRef<const MethodDebugInfo>& method_infos,
+void WriteDebugInfo(linker::ElfBuilder<ElfTypes>* builder,
+ const DebugInfo& debug_info,
dwarf::CFIFormat cfi_format,
bool write_oat_patches) {
// Write .strtab and .symtab.
- WriteDebugSymbols(builder, method_infos, true /* with_signature */);
+ WriteDebugSymbols(builder, false /* mini-debug-info */, debug_info);
// Write .debug_frame.
- WriteCFISection(builder, method_infos, cfi_format, write_oat_patches);
+ WriteCFISection(builder, debug_info.compiled_methods, cfi_format, write_oat_patches);
- // Group the methods into compilation units based on source file.
- std::vector<ElfCompilationUnit> compilation_units;
- const char* last_source_file = nullptr;
- for (const MethodDebugInfo& mi : method_infos) {
+ // Group the methods into compilation units based on class.
+ std::unordered_map<const DexFile::ClassDef*, ElfCompilationUnit> class_to_compilation_unit;
+ for (const MethodDebugInfo& mi : debug_info.compiled_methods) {
if (mi.dex_file != nullptr) {
auto& dex_class_def = mi.dex_file->GetClassDef(mi.class_def_index);
- const char* source_file = mi.dex_file->GetSourceFile(dex_class_def);
- if (compilation_units.empty() || source_file != last_source_file) {
- compilation_units.push_back(ElfCompilationUnit());
- }
- ElfCompilationUnit& cu = compilation_units.back();
+ ElfCompilationUnit& cu = class_to_compilation_unit[&dex_class_def];
cu.methods.push_back(&mi);
// All methods must have the same addressing mode otherwise the min/max below does not work.
DCHECK_EQ(cu.methods.front()->is_code_address_text_relative, mi.is_code_address_text_relative);
cu.is_code_address_text_relative = mi.is_code_address_text_relative;
cu.code_address = std::min(cu.code_address, mi.code_address);
cu.code_end = std::max(cu.code_end, mi.code_address + mi.code_size);
- last_source_file = source_file;
}
}
+ // Sort compilation units to make the compiler output deterministic.
+ std::vector<ElfCompilationUnit> compilation_units;
+ compilation_units.reserve(class_to_compilation_unit.size());
+ for (auto& it : class_to_compilation_unit) {
+ // The .debug_line section requires the methods to be sorted by code address.
+ std::stable_sort(it.second.methods.begin(),
+ it.second.methods.end(),
+ [](const MethodDebugInfo* a, const MethodDebugInfo* b) {
+ return a->code_address < b->code_address;
+ });
+ compilation_units.push_back(std::move(it.second));
+ }
+ std::sort(compilation_units.begin(),
+ compilation_units.end(),
+ [](ElfCompilationUnit& a, ElfCompilationUnit& b) {
+ // Sort by index of the first method within the method_infos array.
+ // This assumes that the order of method_infos is deterministic.
+ // Code address is not good for sorting due to possible duplicates.
+ return a.methods.front() < b.methods.front();
+ });
+
// Write .debug_line section.
if (!compilation_units.empty()) {
ElfDebugLineWriter<ElfTypes> line_writer(builder);
@@ -92,52 +108,94 @@
std::vector<uint8_t> MakeMiniDebugInfo(
InstructionSet isa,
const InstructionSetFeatures* features,
- size_t rodata_size,
- size_t text_size,
- const ArrayRef<const MethodDebugInfo>& method_infos) {
+ uint64_t text_section_address,
+ size_t text_section_size,
+ uint64_t dex_section_address,
+ size_t dex_section_size,
+ const DebugInfo& debug_info) {
if (Is64BitInstructionSet(isa)) {
return MakeMiniDebugInfoInternal<ElfTypes64>(isa,
features,
- rodata_size,
- text_size,
- method_infos);
+ text_section_address,
+ text_section_size,
+ dex_section_address,
+ dex_section_size,
+ debug_info);
} else {
return MakeMiniDebugInfoInternal<ElfTypes32>(isa,
features,
- rodata_size,
- text_size,
- method_infos);
+ text_section_address,
+ text_section_size,
+ dex_section_address,
+ dex_section_size,
+ debug_info);
}
}
template <typename ElfTypes>
-static std::vector<uint8_t> WriteDebugElfFileForMethodsInternal(
+static std::vector<uint8_t> MakeElfFileForJITInternal(
InstructionSet isa,
const InstructionSetFeatures* features,
- const ArrayRef<const MethodDebugInfo>& method_infos) {
+ bool mini_debug_info,
+ ArrayRef<const MethodDebugInfo> method_infos) {
+ CHECK_GT(method_infos.size(), 0u);
+ uint64_t min_address = std::numeric_limits<uint64_t>::max();
+ uint64_t max_address = 0;
+ for (const MethodDebugInfo& mi : method_infos) {
+ CHECK_EQ(mi.is_code_address_text_relative, false);
+ min_address = std::min(min_address, mi.code_address);
+ max_address = std::max(max_address, mi.code_address + mi.code_size);
+ }
+ DebugInfo debug_info{};
+ debug_info.compiled_methods = method_infos;
std::vector<uint8_t> buffer;
buffer.reserve(KB);
- VectorOutputStream out("Debug ELF file", &buffer);
- std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, features, &out));
+ linker::VectorOutputStream out("Debug ELF file", &buffer);
+ std::unique_ptr<linker::ElfBuilder<ElfTypes>> builder(
+ new linker::ElfBuilder<ElfTypes>(isa, features, &out));
// No program headers since the ELF file is not linked and has no allocated sections.
builder->Start(false /* write_program_headers */);
- WriteDebugInfo(builder.get(),
- method_infos,
- dwarf::DW_DEBUG_FRAME_FORMAT,
- false /* write_oat_patches */);
+ if (mini_debug_info) {
+ if (method_infos.size() > 1) {
+ std::vector<uint8_t> mdi = MakeMiniDebugInfo(isa,
+ features,
+ min_address,
+ max_address - min_address,
+ /* dex_section_address */ 0,
+ /* dex_section_size */ 0,
+ debug_info);
+ builder->WriteSection(".gnu_debugdata", &mdi);
+ } else {
+ // The compression is great help for multiple methods but it is not worth it for a
+ // single method due to the overheads so skip the compression here for performance.
+ builder->GetText()->AllocateVirtualMemory(min_address, max_address - min_address);
+ WriteDebugSymbols(builder.get(), true /* mini-debug-info */, debug_info);
+ WriteCFISection(builder.get(),
+ debug_info.compiled_methods,
+ dwarf::DW_DEBUG_FRAME_FORMAT,
+ false /* write_oat_paches */);
+ }
+ } else {
+ builder->GetText()->AllocateVirtualMemory(min_address, max_address - min_address);
+ WriteDebugInfo(builder.get(),
+ debug_info,
+ dwarf::DW_DEBUG_FRAME_FORMAT,
+ false /* write_oat_patches */);
+ }
builder->End();
CHECK(builder->Good());
return buffer;
}
-std::vector<uint8_t> WriteDebugElfFileForMethods(
+std::vector<uint8_t> MakeElfFileForJIT(
InstructionSet isa,
const InstructionSetFeatures* features,
- const ArrayRef<const MethodDebugInfo>& method_infos) {
+ bool mini_debug_info,
+ ArrayRef<const MethodDebugInfo> method_infos) {
if (Is64BitInstructionSet(isa)) {
- return WriteDebugElfFileForMethodsInternal<ElfTypes64>(isa, features, method_infos);
+ return MakeElfFileForJITInternal<ElfTypes64>(isa, features, mini_debug_info, method_infos);
} else {
- return WriteDebugElfFileForMethodsInternal<ElfTypes32>(isa, features, method_infos);
+ return MakeElfFileForJITInternal<ElfTypes32>(isa, features, mini_debug_info, method_infos);
}
}
@@ -149,8 +207,9 @@
REQUIRES_SHARED(Locks::mutator_lock_) {
std::vector<uint8_t> buffer;
buffer.reserve(KB);
- VectorOutputStream out("Debug ELF file", &buffer);
- std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, features, &out));
+ linker::VectorOutputStream out("Debug ELF file", &buffer);
+ std::unique_ptr<linker::ElfBuilder<ElfTypes>> builder(
+ new linker::ElfBuilder<ElfTypes>(isa, features, &out));
// No program headers since the ELF file is not linked and has no allocated sections.
builder->Start(false /* write_program_headers */);
ElfDebugInfoWriter<ElfTypes> info_writer(builder.get());
@@ -174,40 +233,15 @@
}
}
-std::vector<MethodDebugInfo> MakeTrampolineInfos(const OatHeader& header) {
- std::map<const char*, uint32_t> trampolines = {
- { "interpreterToInterpreterBridge", header.GetInterpreterToInterpreterBridgeOffset() },
- { "interpreterToCompiledCodeBridge", header.GetInterpreterToCompiledCodeBridgeOffset() },
- { "jniDlsymLookup", header.GetJniDlsymLookupOffset() },
- { "quickGenericJniTrampoline", header.GetQuickGenericJniTrampolineOffset() },
- { "quickImtConflictTrampoline", header.GetQuickImtConflictTrampolineOffset() },
- { "quickResolutionTrampoline", header.GetQuickResolutionTrampolineOffset() },
- { "quickToInterpreterBridge", header.GetQuickToInterpreterBridgeOffset() },
- };
- std::vector<MethodDebugInfo> result;
- for (const auto& it : trampolines) {
- if (it.second != 0) {
- MethodDebugInfo info = MethodDebugInfo();
- info.trampoline_name = it.first;
- info.isa = header.GetInstructionSet();
- info.is_code_address_text_relative = true;
- info.code_address = it.second - header.GetExecutableOffset();
- info.code_size = 0; // The symbol lasts until the next symbol.
- result.push_back(std::move(info));
- }
- }
- return result;
-}
-
// Explicit instantiations
template void WriteDebugInfo<ElfTypes32>(
- ElfBuilder<ElfTypes32>* builder,
- const ArrayRef<const MethodDebugInfo>& method_infos,
+ linker::ElfBuilder<ElfTypes32>* builder,
+ const DebugInfo& debug_info,
dwarf::CFIFormat cfi_format,
bool write_oat_patches);
template void WriteDebugInfo<ElfTypes64>(
- ElfBuilder<ElfTypes64>* builder,
- const ArrayRef<const MethodDebugInfo>& method_infos,
+ linker::ElfBuilder<ElfTypes64>* builder,
+ const DebugInfo& debug_info,
dwarf::CFIFormat cfi_format,
bool write_oat_patches);
diff --git a/compiler/debug/elf_debug_writer.h b/compiler/debug/elf_debug_writer.h
index 5d68810..e442e00 100644
--- a/compiler/debug/elf_debug_writer.h
+++ b/compiler/debug/elf_debug_writer.h
@@ -23,7 +23,8 @@
#include "base/macros.h"
#include "base/mutex.h"
#include "debug/dwarf/dwarf_constants.h"
-#include "elf_builder.h"
+#include "debug/debug_info.h"
+#include "linker/elf_builder.h"
namespace art {
class OatHeader;
@@ -35,22 +36,25 @@
template <typename ElfTypes>
void WriteDebugInfo(
- ElfBuilder<ElfTypes>* builder,
- const ArrayRef<const MethodDebugInfo>& method_infos,
+ linker::ElfBuilder<ElfTypes>* builder,
+ const DebugInfo& debug_info,
dwarf::CFIFormat cfi_format,
bool write_oat_patches);
std::vector<uint8_t> MakeMiniDebugInfo(
InstructionSet isa,
const InstructionSetFeatures* features,
- size_t rodata_section_size,
+ uint64_t text_section_address,
size_t text_section_size,
- const ArrayRef<const MethodDebugInfo>& method_infos);
+ uint64_t dex_section_address,
+ size_t dex_section_size,
+ const DebugInfo& debug_info);
-std::vector<uint8_t> WriteDebugElfFileForMethods(
+std::vector<uint8_t> MakeElfFileForJIT(
InstructionSet isa,
const InstructionSetFeatures* features,
- const ArrayRef<const MethodDebugInfo>& method_infos);
+ bool mini_debug_info,
+ ArrayRef<const MethodDebugInfo> method_infos);
std::vector<uint8_t> WriteDebugElfFileForClasses(
InstructionSet isa,
@@ -58,8 +62,6 @@
const ArrayRef<mirror::Class*>& types)
REQUIRES_SHARED(Locks::mutator_lock_);
-std::vector<MethodDebugInfo> MakeTrampolineInfos(const OatHeader& oat_header);
-
} // namespace debug
} // namespace art
diff --git a/compiler/debug/elf_gnu_debugdata_writer.h b/compiler/debug/elf_gnu_debugdata_writer.h
index fb63d62..a88c5cb 100644
--- a/compiler/debug/elf_gnu_debugdata_writer.h
+++ b/compiler/debug/elf_gnu_debugdata_writer.h
@@ -20,7 +20,7 @@
#include <vector>
#include "arch/instruction_set.h"
-#include "elf_builder.h"
+#include "linker/elf_builder.h"
#include "linker/vector_output_stream.h"
// liblzma.
@@ -80,21 +80,25 @@
static std::vector<uint8_t> MakeMiniDebugInfoInternal(
InstructionSet isa,
const InstructionSetFeatures* features,
- size_t rodata_section_size,
+ typename ElfTypes::Addr text_section_address,
size_t text_section_size,
- const ArrayRef<const MethodDebugInfo>& method_infos) {
+ typename ElfTypes::Addr dex_section_address,
+ size_t dex_section_size,
+ const DebugInfo& debug_info) {
std::vector<uint8_t> buffer;
buffer.reserve(KB);
- VectorOutputStream out("Mini-debug-info ELF file", &buffer);
- std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, features, &out));
- builder->Start();
- // Mirror .rodata and .text as NOBITS sections.
- // It is needed to detected relocations after compression.
- builder->GetRoData()->WriteNoBitsSection(rodata_section_size);
- builder->GetText()->WriteNoBitsSection(text_section_size);
- WriteDebugSymbols(builder.get(), method_infos, false /* with_signature */);
+ linker::VectorOutputStream out("Mini-debug-info ELF file", &buffer);
+ std::unique_ptr<linker::ElfBuilder<ElfTypes>> builder(
+ new linker::ElfBuilder<ElfTypes>(isa, features, &out));
+ builder->Start(false /* write_program_headers */);
+ // Mirror ELF sections as NOBITS since the added symbols will reference them.
+ builder->GetText()->AllocateVirtualMemory(text_section_address, text_section_size);
+ if (dex_section_size != 0) {
+ builder->GetDex()->AllocateVirtualMemory(dex_section_address, dex_section_size);
+ }
+ WriteDebugSymbols(builder.get(), true /* mini-debug-info */, debug_info);
WriteCFISection(builder.get(),
- method_infos,
+ debug_info.compiled_methods,
dwarf::DW_DEBUG_FRAME_FORMAT,
false /* write_oat_paches */);
builder->End();
diff --git a/compiler/debug/elf_symtab_writer.h b/compiler/debug/elf_symtab_writer.h
index af9f091..7a8e291 100644
--- a/compiler/debug/elf_symtab_writer.h
+++ b/compiler/debug/elf_symtab_writer.h
@@ -17,11 +17,15 @@
#ifndef ART_COMPILER_DEBUG_ELF_SYMTAB_WRITER_H_
#define ART_COMPILER_DEBUG_ELF_SYMTAB_WRITER_H_
+#include <map>
#include <unordered_set>
+#include "base/utils.h"
+#include "debug/debug_info.h"
#include "debug/method_debug_info.h"
-#include "elf_builder.h"
-#include "utils.h"
+#include "dex/dex_file-inl.h"
+#include "dex/code_item_accessors.h"
+#include "linker/elf_builder.h"
namespace art {
namespace debug {
@@ -33,76 +37,83 @@
// exist, but it will still work well without them.
// However, these extra symbols take space, so let's just generate
// one symbol which marks the whole .text section as code.
-constexpr bool kGenerateSingleArmMappingSymbol = true;
+// Note that ARM's Streamline requires it to match function symbol.
+constexpr bool kGenerateArmMappingSymbol = true;
+
+// Magic name for .symtab symbols which enumerate dex files used
+// by this ELF file (currently mmapped inside the .dex section).
+constexpr const char* kDexFileSymbolName = "$dexfile";
template <typename ElfTypes>
-static void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder,
- const ArrayRef<const MethodDebugInfo>& method_infos,
- bool with_signature) {
+static void WriteDebugSymbols(linker::ElfBuilder<ElfTypes>* builder,
+ bool mini_debug_info,
+ const DebugInfo& debug_info) {
uint64_t mapping_symbol_address = std::numeric_limits<uint64_t>::max();
+ const auto* text = builder->GetText();
auto* strtab = builder->GetStrTab();
auto* symtab = builder->GetSymTab();
- if (method_infos.empty()) {
+ if (debug_info.Empty()) {
return;
}
// Find all addresses which contain deduped methods.
// The first instance of method is not marked deduped_, but the rest is.
std::unordered_set<uint64_t> deduped_addresses;
- for (const MethodDebugInfo& info : method_infos) {
+ for (const MethodDebugInfo& info : debug_info.compiled_methods) {
if (info.deduped) {
deduped_addresses.insert(info.code_address);
}
+ if (kGenerateArmMappingSymbol && info.isa == InstructionSet::kThumb2) {
+ uint64_t address = info.code_address;
+ address += info.is_code_address_text_relative ? text->GetAddress() : 0;
+ mapping_symbol_address = std::min(mapping_symbol_address, address);
+ }
}
strtab->Start();
strtab->Write(""); // strtab should start with empty string.
- std::string last_name;
- size_t last_name_offset = 0;
- for (const MethodDebugInfo& info : method_infos) {
+ // Generate ARM mapping symbols. ELF local symbols must be added first.
+ if (mapping_symbol_address != std::numeric_limits<uint64_t>::max()) {
+ symtab->Add(strtab->Write("$t"), text, mapping_symbol_address, 0, STB_LOCAL, STT_NOTYPE);
+ }
+ // Add symbols for compiled methods.
+ for (const MethodDebugInfo& info : debug_info.compiled_methods) {
if (info.deduped) {
continue; // Add symbol only for the first instance.
}
size_t name_offset;
- if (info.trampoline_name != nullptr) {
- name_offset = strtab->Write(info.trampoline_name);
+ if (!info.custom_name.empty()) {
+ name_offset = strtab->Write(info.custom_name);
} else {
DCHECK(info.dex_file != nullptr);
- std::string name = info.dex_file->PrettyMethod(info.dex_method_index, with_signature);
+ std::string name = info.dex_file->PrettyMethod(info.dex_method_index, !mini_debug_info);
if (deduped_addresses.find(info.code_address) != deduped_addresses.end()) {
name += " [DEDUPED]";
}
- // If we write method names without signature, we might see the same name multiple times.
- name_offset = (name == last_name ? last_name_offset : strtab->Write(name));
- last_name = std::move(name);
- last_name_offset = name_offset;
+ name_offset = strtab->Write(name);
}
- const auto* text = info.is_code_address_text_relative ? builder->GetText() : nullptr;
- uint64_t address = info.code_address + (text != nullptr ? text->GetAddress() : 0);
+ uint64_t address = info.code_address;
+ address += info.is_code_address_text_relative ? text->GetAddress() : 0;
// Add in code delta, e.g., thumb bit 0 for Thumb2 code.
address += CompiledMethod::CodeDelta(info.isa);
symtab->Add(name_offset, text, address, info.code_size, STB_GLOBAL, STT_FUNC);
-
- // Conforming to aaelf, add $t mapping symbol to indicate start of a sequence of thumb2
- // instructions, so that disassembler tools can correctly disassemble.
- // Note that even if we generate just a single mapping symbol, ARM's Streamline
- // requires it to match function symbol. Just address 0 does not work.
- if (info.isa == kThumb2) {
- if (address < mapping_symbol_address || !kGenerateSingleArmMappingSymbol) {
- symtab->Add(strtab->Write("$t"), text, address & ~1, 0, STB_LOCAL, STT_NOTYPE);
- mapping_symbol_address = address;
- }
+ }
+ // Add symbols for dex files.
+ if (!debug_info.dex_files.empty() && builder->GetDex()->Exists()) {
+ auto dex = builder->GetDex();
+ for (auto it : debug_info.dex_files) {
+ uint64_t dex_address = dex->GetAddress() + it.first /* offset within the section */;
+ const DexFile* dex_file = it.second;
+ typename ElfTypes::Word dex_name = strtab->Write(kDexFileSymbolName);
+ symtab->Add(dex_name, dex, dex_address, dex_file->Size(), STB_GLOBAL, STT_FUNC);
}
}
strtab->End();
// Symbols are buffered and written after names (because they are smaller).
- // We could also do two passes in this function to avoid the buffering.
- symtab->Start();
- symtab->Write();
- symtab->End();
+ symtab->WriteCachedSection();
}
} // namespace debug
diff --git a/compiler/debug/method_debug_info.h b/compiler/debug/method_debug_info.h
index ed1da2c..d0b03ec 100644
--- a/compiler/debug/method_debug_info.h
+++ b/compiler/debug/method_debug_info.h
@@ -17,14 +17,17 @@
#ifndef ART_COMPILER_DEBUG_METHOD_DEBUG_INFO_H_
#define ART_COMPILER_DEBUG_METHOD_DEBUG_INFO_H_
-#include "compiled_method.h"
-#include "dex_file.h"
+#include <string>
+
+#include "arch/instruction_set.h"
+#include "base/array_ref.h"
+#include "dex/dex_file.h"
namespace art {
namespace debug {
struct MethodDebugInfo {
- const char* trampoline_name;
+ std::string custom_name;
const DexFile* dex_file; // Native methods (trampolines) do not reference dex file.
size_t class_def_index;
uint32_t dex_method_index;
diff --git a/compiler/debug/src_map_elem.h b/compiler/debug/src_map_elem.h
new file mode 100644
index 0000000..5286b8c
--- /dev/null
+++ b/compiler/debug/src_map_elem.h
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_COMPILER_DEBUG_SRC_MAP_ELEM_H_
+#define ART_COMPILER_DEBUG_SRC_MAP_ELEM_H_
+
+#include <stdint.h>
+
+namespace art {
+
+class SrcMapElem {
+ public:
+ uint32_t from_;
+ int32_t to_;
+};
+
+inline bool operator<(const SrcMapElem& lhs, const SrcMapElem& rhs) {
+ if (lhs.from_ != rhs.from_) {
+ return lhs.from_ < rhs.from_;
+ }
+ return lhs.to_ < rhs.to_;
+}
+
+inline bool operator==(const SrcMapElem& lhs, const SrcMapElem& rhs) {
+ return lhs.from_ == rhs.from_ && lhs.to_ == rhs.to_;
+}
+
+} // namespace art
+
+#endif // ART_COMPILER_DEBUG_SRC_MAP_ELEM_H_
diff --git a/compiler/debug/src_map_elem_test.cc b/compiler/debug/src_map_elem_test.cc
new file mode 100644
index 0000000..ceaa53f
--- /dev/null
+++ b/compiler/debug/src_map_elem_test.cc
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include "src_map_elem.h"
+
+#include "base/macros.h"
+
+namespace art {
+namespace debug {
+
+TEST(SrcMapElem, Operators) {
+ SrcMapElem elems[] = {
+ { 1u, -1 },
+ { 1u, 0 },
+ { 1u, 1 },
+ { 2u, -1 },
+ { 2u, 0 }, // Index 4.
+ { 2u, 1 },
+ { 2u, 0u }, // Index 6: Arbitrarily add identical SrcMapElem with index 4.
+ };
+
+ for (size_t i = 0; i != arraysize(elems); ++i) {
+ for (size_t j = 0; j != arraysize(elems); ++j) {
+ bool expected = (i != 6u ? i : 4u) == (j != 6u ? j : 4u);
+ EXPECT_EQ(expected, elems[i] == elems[j]) << i << " " << j;
+ }
+ }
+
+ for (size_t i = 0; i != arraysize(elems); ++i) {
+ for (size_t j = 0; j != arraysize(elems); ++j) {
+ bool expected = (i != 6u ? i : 4u) < (j != 6u ? j : 4u);
+ EXPECT_EQ(expected, elems[i] < elems[j]) << i << " " << j;
+ }
+ }
+}
+
+} // namespace debug
+} // namespace art
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index 9d57b96..be8641f 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -16,16 +16,19 @@
#include "dex_to_dex_compiler.h"
-#include "android-base/stringprintf.h"
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
#include "art_field-inl.h"
#include "art_method-inl.h"
-#include "base/logging.h"
+#include "base/logging.h" // For VLOG
+#include "base/macros.h"
#include "base/mutex.h"
-#include "bytecode_utils.h"
#include "compiled_method.h"
-#include "dex_file-inl.h"
-#include "dex_instruction-inl.h"
+#include "dex/bytecode_utils.h"
+#include "dex/dex_file-inl.h"
+#include "dex/dex_instruction-inl.h"
+#include "dex_to_dex_decompiler.h"
#include "driver/compiler_driver.h"
#include "driver/dex_compilation_unit.h"
#include "mirror/dex_cache.h"
@@ -42,34 +45,30 @@
// Control check-cast elision.
const bool kEnableCheckCastEllision = true;
-struct QuickenedInfo {
- QuickenedInfo(uint32_t pc, uint16_t index) : dex_pc(pc), dex_member_index(index) {}
+// Holds the state for compiling a single method.
+struct DexToDexCompiler::CompilationState {
+ struct QuickenedInfo {
+ QuickenedInfo(uint32_t pc, uint16_t index) : dex_pc(pc), dex_member_index(index) {}
- uint32_t dex_pc;
- uint16_t dex_member_index;
-};
+ uint32_t dex_pc;
+ uint16_t dex_member_index;
+ };
-class DexCompiler {
- public:
- DexCompiler(art::CompilerDriver& compiler,
- const DexCompilationUnit& unit,
- DexToDexCompilationLevel dex_to_dex_compilation_level)
- : driver_(compiler),
- unit_(unit),
- dex_to_dex_compilation_level_(dex_to_dex_compilation_level) {}
-
- ~DexCompiler() {}
-
- void Compile();
+ CompilationState(DexToDexCompiler* compiler,
+ const DexCompilationUnit& unit,
+ const CompilationLevel compilation_level,
+ const std::vector<uint8_t>* quicken_data);
const std::vector<QuickenedInfo>& GetQuickenedInfo() const {
return quickened_info_;
}
- private:
- const DexFile& GetDexFile() const {
- return *unit_.GetDexFile();
- }
+ // Returns the quickening info, or an empty array if it was not quickened.
+ // If already_quickened is true, then don't change anything but still return what the quicken
+ // data would have been.
+ std::vector<uint8_t> Compile();
+
+ const DexFile& GetDexFile() const;
// Compiles a RETURN-VOID into a RETURN-VOID-BARRIER within a constructor where
// a barrier is required.
@@ -91,30 +90,133 @@
// Compiles a virtual method invocation into a quick virtual method invocation.
// The method index is replaced by the vtable index where the corresponding
- // Executable can be found. Therefore, this does not involve any resolution
+ // executable can be found. Therefore, this does not involve any resolution
// at runtime.
// Since the method index is encoded with 16 bits, we can replace it only if the
// vtable index can be encoded with 16 bits too.
void CompileInvokeVirtual(Instruction* inst, uint32_t dex_pc,
Instruction::Code new_opcode, bool is_range);
+ // Return the next index.
+ uint16_t NextIndex();
+
+ // Returns the dequickened index if an instruction is quickened, otherwise return index.
+ uint16_t GetIndexForInstruction(const Instruction* inst, uint32_t index);
+
+ DexToDexCompiler* const compiler_;
CompilerDriver& driver_;
const DexCompilationUnit& unit_;
- const DexToDexCompilationLevel dex_to_dex_compilation_level_;
+ const CompilationLevel compilation_level_;
// Filled by the compiler when quickening, in order to encode that information
// in the .oat file. The runtime will use that information to get to the original
// opcodes.
std::vector<QuickenedInfo> quickened_info_;
- DISALLOW_COPY_AND_ASSIGN(DexCompiler);
+ // True if we optimized a return void to a return void no barrier.
+ bool optimized_return_void_ = false;
+
+ // If the code item was already quickened previously.
+ const bool already_quickened_;
+ const QuickenInfoTable existing_quicken_info_;
+ uint32_t quicken_index_ = 0u;
+
+ DISALLOW_COPY_AND_ASSIGN(CompilationState);
};
-void DexCompiler::Compile() {
- DCHECK_EQ(dex_to_dex_compilation_level_, DexToDexCompilationLevel::kOptimize);
- for (CodeItemIterator it(*unit_.GetCodeItem()); !it.Done(); it.Advance()) {
- Instruction* inst = const_cast<Instruction*>(&it.CurrentInstruction());
- const uint32_t dex_pc = it.CurrentDexPc();
+DexToDexCompiler::DexToDexCompiler(CompilerDriver* driver)
+ : driver_(driver),
+ lock_("Quicken lock", kDexToDexCompilerLock) {
+ DCHECK(driver != nullptr);
+}
+
+void DexToDexCompiler::ClearState() {
+ MutexLock lock(Thread::Current(), lock_);
+ active_dex_file_ = nullptr;
+ active_bit_vector_ = nullptr;
+ should_quicken_.clear();
+ shared_code_item_quicken_info_.clear();
+}
+
+size_t DexToDexCompiler::NumCodeItemsToQuicken(Thread* self) const {
+ MutexLock lock(self, lock_);
+ return num_code_items_;
+}
+
+BitVector* DexToDexCompiler::GetOrAddBitVectorForDex(const DexFile* dex_file) {
+ if (active_dex_file_ != dex_file) {
+ active_dex_file_ = dex_file;
+ auto inserted = should_quicken_.emplace(dex_file,
+ BitVector(dex_file->NumMethodIds(),
+ /*expandable*/ false,
+ Allocator::GetMallocAllocator()));
+ active_bit_vector_ = &inserted.first->second;
+ }
+ return active_bit_vector_;
+}
+
+void DexToDexCompiler::MarkForCompilation(Thread* self,
+ const MethodReference& method_ref) {
+ MutexLock lock(self, lock_);
+ BitVector* const bitmap = GetOrAddBitVectorForDex(method_ref.dex_file);
+ DCHECK(bitmap != nullptr);
+ DCHECK(!bitmap->IsBitSet(method_ref.index));
+ bitmap->SetBit(method_ref.index);
+ ++num_code_items_;
+}
+
+DexToDexCompiler::CompilationState::CompilationState(DexToDexCompiler* compiler,
+ const DexCompilationUnit& unit,
+ const CompilationLevel compilation_level,
+ const std::vector<uint8_t>* quicken_data)
+ : compiler_(compiler),
+ driver_(*compiler->GetDriver()),
+ unit_(unit),
+ compilation_level_(compilation_level),
+ already_quickened_(quicken_data != nullptr),
+ existing_quicken_info_(already_quickened_
+ ? ArrayRef<const uint8_t>(*quicken_data) : ArrayRef<const uint8_t>()) {}
+
+uint16_t DexToDexCompiler::CompilationState::NextIndex() {
+ DCHECK(already_quickened_);
+ if (kIsDebugBuild && quicken_index_ >= existing_quicken_info_.NumIndices()) {
+ for (const DexInstructionPcPair& pair : unit_.GetCodeItemAccessor()) {
+ LOG(ERROR) << pair->DumpString(nullptr);
+ }
+ LOG(FATAL) << "Mismatched number of quicken slots.";
+ }
+ const uint16_t ret = existing_quicken_info_.GetData(quicken_index_);
+ quicken_index_++;
+ return ret;
+}
+
+uint16_t DexToDexCompiler::CompilationState::GetIndexForInstruction(const Instruction* inst,
+ uint32_t index) {
+ if (UNLIKELY(already_quickened_)) {
+ return inst->IsQuickened() ? NextIndex() : index;
+ }
+ DCHECK(!inst->IsQuickened());
+ return index;
+}
+
+bool DexToDexCompiler::ShouldCompileMethod(const MethodReference& ref) {
+ // TODO: It's probably safe to avoid the lock here if the active_dex_file_ matches since we only
+ // only call ShouldCompileMethod on one dex at a time.
+ MutexLock lock(Thread::Current(), lock_);
+ return GetOrAddBitVectorForDex(ref.dex_file)->IsBitSet(ref.index);
+}
+
+std::vector<uint8_t> DexToDexCompiler::CompilationState::Compile() {
+ DCHECK_EQ(compilation_level_, CompilationLevel::kOptimize);
+ const CodeItemDataAccessor& instructions = unit_.GetCodeItemAccessor();
+ for (DexInstructionIterator it = instructions.begin(); it != instructions.end(); ++it) {
+ const uint32_t dex_pc = it.DexPc();
+ Instruction* inst = const_cast<Instruction*>(&it.Inst());
+
+ if (!already_quickened_) {
+ DCHECK(!inst->IsQuickened());
+ }
+
switch (inst->Opcode()) {
case Instruction::RETURN_VOID:
CompileReturnVoid(inst, dex_pc);
@@ -125,89 +227,150 @@
if (inst->Opcode() == Instruction::NOP) {
// We turned the CHECK_CAST into two NOPs, avoid visiting the second NOP twice since this
// would add 2 quickening info entries.
- it.Advance();
+ ++it;
}
break;
case Instruction::IGET:
+ case Instruction::IGET_QUICK:
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_QUICK, false);
break;
case Instruction::IGET_WIDE:
+ case Instruction::IGET_WIDE_QUICK:
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_WIDE_QUICK, false);
break;
case Instruction::IGET_OBJECT:
+ case Instruction::IGET_OBJECT_QUICK:
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_OBJECT_QUICK, false);
break;
case Instruction::IGET_BOOLEAN:
+ case Instruction::IGET_BOOLEAN_QUICK:
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_BOOLEAN_QUICK, false);
break;
case Instruction::IGET_BYTE:
+ case Instruction::IGET_BYTE_QUICK:
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_BYTE_QUICK, false);
break;
case Instruction::IGET_CHAR:
+ case Instruction::IGET_CHAR_QUICK:
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_CHAR_QUICK, false);
break;
case Instruction::IGET_SHORT:
+ case Instruction::IGET_SHORT_QUICK:
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IGET_SHORT_QUICK, false);
break;
case Instruction::IPUT:
+ case Instruction::IPUT_QUICK:
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_QUICK, true);
break;
case Instruction::IPUT_BOOLEAN:
+ case Instruction::IPUT_BOOLEAN_QUICK:
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_BOOLEAN_QUICK, true);
break;
case Instruction::IPUT_BYTE:
+ case Instruction::IPUT_BYTE_QUICK:
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_BYTE_QUICK, true);
break;
case Instruction::IPUT_CHAR:
+ case Instruction::IPUT_CHAR_QUICK:
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_CHAR_QUICK, true);
break;
case Instruction::IPUT_SHORT:
+ case Instruction::IPUT_SHORT_QUICK:
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_SHORT_QUICK, true);
break;
case Instruction::IPUT_WIDE:
+ case Instruction::IPUT_WIDE_QUICK:
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_WIDE_QUICK, true);
break;
case Instruction::IPUT_OBJECT:
+ case Instruction::IPUT_OBJECT_QUICK:
CompileInstanceFieldAccess(inst, dex_pc, Instruction::IPUT_OBJECT_QUICK, true);
break;
case Instruction::INVOKE_VIRTUAL:
+ case Instruction::INVOKE_VIRTUAL_QUICK:
CompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_QUICK, false);
break;
case Instruction::INVOKE_VIRTUAL_RANGE:
+ case Instruction::INVOKE_VIRTUAL_RANGE_QUICK:
CompileInvokeVirtual(inst, dex_pc, Instruction::INVOKE_VIRTUAL_RANGE_QUICK, true);
break;
case Instruction::NOP:
- // We need to differentiate between check cast inserted NOP and normal NOP, put an invalid
- // index in the map for normal nops. This should be rare in real code.
- quickened_info_.push_back(QuickenedInfo(dex_pc, DexFile::kDexNoIndex16));
+ if (already_quickened_) {
+ const uint16_t reference_index = NextIndex();
+ quickened_info_.push_back(QuickenedInfo(dex_pc, reference_index));
+ if (reference_index == DexFile::kDexNoIndex16) {
+ // This means it was a normal nop and not a check-cast.
+ break;
+ }
+ const uint16_t type_index = NextIndex();
+ if (driver_.IsSafeCast(&unit_, dex_pc)) {
+ quickened_info_.push_back(QuickenedInfo(dex_pc, type_index));
+ }
+ ++it;
+ } else {
+ // We need to differentiate between check cast inserted NOP and normal NOP, put an invalid
+ // index in the map for normal nops. This should be rare in real code.
+ quickened_info_.push_back(QuickenedInfo(dex_pc, DexFile::kDexNoIndex16));
+ }
break;
default:
- DCHECK(!inst->IsQuickened());
// Nothing to do.
break;
}
}
+
+ if (already_quickened_) {
+ DCHECK_EQ(quicken_index_, existing_quicken_info_.NumIndices());
+ }
+
+ // Even if there are no indicies, generate an empty quicken info so that we know the method was
+ // quickened.
+
+ std::vector<uint8_t> quicken_data;
+ if (kIsDebugBuild) {
+ // Double check that the counts line up with the size of the quicken info.
+ size_t quicken_count = 0;
+ for (const DexInstructionPcPair& pair : instructions) {
+ if (QuickenInfoTable::NeedsIndexForInstruction(&pair.Inst())) {
+ ++quicken_count;
+ }
+ }
+ CHECK_EQ(quicken_count, GetQuickenedInfo().size());
+ }
+
+ QuickenInfoTable::Builder builder(&quicken_data, GetQuickenedInfo().size());
+ // Length is encoded by the constructor.
+ for (const CompilationState::QuickenedInfo& info : GetQuickenedInfo()) {
+ // Dex pc is not serialized, only used for checking the instructions. Since we access the
+ // array based on the index of the quickened instruction, the indexes must line up perfectly.
+ // The reader side uses the NeedsIndexForInstruction function too.
+ const Instruction& inst = instructions.InstructionAt(info.dex_pc);
+ CHECK(QuickenInfoTable::NeedsIndexForInstruction(&inst)) << inst.Opcode();
+ builder.AddIndex(info.dex_member_index);
+ }
+ DCHECK(!quicken_data.empty());
+ return quicken_data;
}
-void DexCompiler::CompileReturnVoid(Instruction* inst, uint32_t dex_pc) {
+void DexToDexCompiler::CompilationState::CompileReturnVoid(Instruction* inst, uint32_t dex_pc) {
DCHECK_EQ(inst->Opcode(), Instruction::RETURN_VOID);
if (unit_.IsConstructor()) {
// Are we compiling a non clinit constructor which needs a barrier ?
@@ -223,9 +386,11 @@
<< " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
<< GetDexFile().PrettyMethod(unit_.GetDexMethodIndex(), true);
inst->SetOpcode(Instruction::RETURN_VOID_NO_BARRIER);
+ optimized_return_void_ = true;
}
-Instruction* DexCompiler::CompileCheckCast(Instruction* inst, uint32_t dex_pc) {
+Instruction* DexToDexCompiler::CompilationState::CompileCheckCast(Instruction* inst,
+ uint32_t dex_pc) {
if (!kEnableCheckCastEllision) {
return inst;
}
@@ -242,27 +407,30 @@
<< " by replacing it with 2 NOPs at dex pc "
<< StringPrintf("0x%x", dex_pc) << " in method "
<< GetDexFile().PrettyMethod(unit_.GetDexMethodIndex(), true);
- quickened_info_.push_back(QuickenedInfo(dex_pc, inst->VRegA_21c()));
- quickened_info_.push_back(QuickenedInfo(dex_pc, inst->VRegB_21c()));
- // We are modifying 4 consecutive bytes.
- inst->SetOpcode(Instruction::NOP);
- inst->SetVRegA_10x(0u); // keep compliant with verifier.
- // Get to next instruction which is the second half of check-cast and replace
- // it by a NOP.
- inst = const_cast<Instruction*>(inst->Next());
- inst->SetOpcode(Instruction::NOP);
- inst->SetVRegA_10x(0u); // keep compliant with verifier.
+ if (!already_quickened_) {
+ quickened_info_.push_back(QuickenedInfo(dex_pc, inst->VRegA_21c()));
+ quickened_info_.push_back(QuickenedInfo(dex_pc, inst->VRegB_21c()));
+
+ // We are modifying 4 consecutive bytes.
+ inst->SetOpcode(Instruction::NOP);
+ inst->SetVRegA_10x(0u); // keep compliant with verifier.
+ // Get to next instruction which is the second half of check-cast and replace
+ // it by a NOP.
+ inst = const_cast<Instruction*>(inst->Next());
+ inst->SetOpcode(Instruction::NOP);
+ inst->SetVRegA_10x(0u); // keep compliant with verifier.
+ }
return inst;
}
-void DexCompiler::CompileInstanceFieldAccess(Instruction* inst,
- uint32_t dex_pc,
- Instruction::Code new_opcode,
- bool is_put) {
+void DexToDexCompiler::CompilationState::CompileInstanceFieldAccess(Instruction* inst,
+ uint32_t dex_pc,
+ Instruction::Code new_opcode,
+ bool is_put) {
if (!kEnableQuickening) {
return;
}
- uint32_t field_idx = inst->VRegC_22c();
+ uint32_t field_idx = GetIndexForInstruction(inst, inst->VRegC_22c());
MemberOffset field_offset(0u);
bool is_volatile;
bool fast_path = driver_.ComputeInstanceFieldInfo(field_idx, &unit_, is_put,
@@ -274,26 +442,34 @@
<< " by field offset " << field_offset.Int32Value()
<< " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
<< GetDexFile().PrettyMethod(unit_.GetDexMethodIndex(), true);
- // We are modifying 4 consecutive bytes.
- inst->SetOpcode(new_opcode);
- // Replace field index by field offset.
- inst->SetVRegC_22c(static_cast<uint16_t>(field_offset.Int32Value()));
+ if (!already_quickened_) {
+ // We are modifying 4 consecutive bytes.
+ inst->SetOpcode(new_opcode);
+ // Replace field index by field offset.
+ inst->SetVRegC_22c(static_cast<uint16_t>(field_offset.Int32Value()));
+ }
quickened_info_.push_back(QuickenedInfo(dex_pc, field_idx));
}
}
-void DexCompiler::CompileInvokeVirtual(Instruction* inst, uint32_t dex_pc,
- Instruction::Code new_opcode, bool is_range) {
+const DexFile& DexToDexCompiler::CompilationState::GetDexFile() const {
+ return *unit_.GetDexFile();
+}
+
+void DexToDexCompiler::CompilationState::CompileInvokeVirtual(Instruction* inst,
+ uint32_t dex_pc,
+ Instruction::Code new_opcode,
+ bool is_range) {
if (!kEnableQuickening) {
return;
}
- uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
+ uint32_t method_idx = GetIndexForInstruction(inst,
+ is_range ? inst->VRegB_3rc() : inst->VRegB_35c());
ScopedObjectAccess soa(Thread::Current());
ClassLinker* class_linker = unit_.GetClassLinker();
ArtMethod* resolved_method =
class_linker->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>(
- GetDexFile(),
method_idx,
unit_.GetDexCache(),
unit_.GetClassLoader(),
@@ -315,19 +491,20 @@
<< " by vtable index " << vtable_idx
<< " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method "
<< GetDexFile().PrettyMethod(unit_.GetDexMethodIndex(), true);
- // We are modifying 4 consecutive bytes.
- inst->SetOpcode(new_opcode);
- // Replace method index by vtable index.
- if (is_range) {
- inst->SetVRegB_3rc(static_cast<uint16_t>(vtable_idx));
- } else {
- inst->SetVRegB_35c(static_cast<uint16_t>(vtable_idx));
+ if (!already_quickened_) {
+ // We are modifying 4 consecutive bytes.
+ inst->SetOpcode(new_opcode);
+ // Replace method index by vtable index.
+ if (is_range) {
+ inst->SetVRegB_3rc(static_cast<uint16_t>(vtable_idx));
+ } else {
+ inst->SetVRegB_35c(static_cast<uint16_t>(vtable_idx));
+ }
}
quickened_info_.push_back(QuickenedInfo(dex_pc, method_idx));
}
-CompiledMethod* ArtCompileDEX(
- CompilerDriver* driver,
+CompiledMethod* DexToDexCompiler::CompileMethod(
const DexFile::CodeItem* code_item,
uint32_t access_flags,
InvokeType invoke_type ATTRIBUTE_UNUSED,
@@ -335,69 +512,175 @@
uint32_t method_idx,
Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
- DexToDexCompilationLevel dex_to_dex_compilation_level) {
- DCHECK(driver != nullptr);
- if (dex_to_dex_compilation_level != DexToDexCompilationLevel::kDontDexToDexCompile) {
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<1> hs(soa.Self());
- ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
- art::DexCompilationUnit unit(
- class_loader,
- class_linker,
- dex_file,
- code_item,
- class_def_idx,
- method_idx,
- access_flags,
- driver->GetVerifiedMethod(&dex_file, method_idx),
- hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file)));
- art::optimizer::DexCompiler dex_compiler(*driver, unit, dex_to_dex_compilation_level);
- dex_compiler.Compile();
- if (dex_compiler.GetQuickenedInfo().empty()) {
- // No need to create a CompiledMethod if there are no quickened opcodes.
+ CompilationLevel compilation_level) {
+ if (compilation_level == CompilationLevel::kDontDexToDexCompile) {
+ return nullptr;
+ }
+
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<1> hs(soa.Self());
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ art::DexCompilationUnit unit(
+ class_loader,
+ class_linker,
+ dex_file,
+ code_item,
+ class_def_idx,
+ method_idx,
+ access_flags,
+ driver_->GetVerifiedMethod(&dex_file, method_idx),
+ hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file)));
+
+ std::vector<uint8_t> quicken_data;
+ // If the code item is shared with multiple different method ids, make sure that we quicken only
+ // once and verify that all the dequicken maps match.
+ if (UNLIKELY(shared_code_items_.find(code_item) != shared_code_items_.end())) {
+ // Avoid quickening the shared code items for now because the existing conflict detection logic
+ // does not currently handle cases where the code item is quickened in one place but
+ // compiled in another.
+ static constexpr bool kAvoidQuickeningSharedCodeItems = true;
+ if (kAvoidQuickeningSharedCodeItems) {
return nullptr;
}
+ // For shared code items, use a lock to prevent races.
+ MutexLock mu(soa.Self(), lock_);
+ auto existing = shared_code_item_quicken_info_.find(code_item);
+ QuickenState* existing_data = nullptr;
+ std::vector<uint8_t>* existing_quicken_data = nullptr;
+ if (existing != shared_code_item_quicken_info_.end()) {
+ existing_data = &existing->second;
+ if (existing_data->conflict_) {
+ return nullptr;
+ }
+ existing_quicken_data = &existing_data->quicken_data_;
+ }
+ bool optimized_return_void;
+ {
+ CompilationState state(this, unit, compilation_level, existing_quicken_data);
+ quicken_data = state.Compile();
+ optimized_return_void = state.optimized_return_void_;
+ }
- // Create a `CompiledMethod`, with the quickened information in the vmap table.
+ // Already quickened, check that the data matches what was previously seen.
+ MethodReference method_ref(&dex_file, method_idx);
+ if (existing_data != nullptr) {
+ if (*existing_quicken_data != quicken_data ||
+ existing_data->optimized_return_void_ != optimized_return_void) {
+ VLOG(compiler) << "Quicken data mismatch, for method "
+ << dex_file.PrettyMethod(method_idx);
+ // Mark the method as a conflict to never attempt to quicken it in the future.
+ existing_data->conflict_ = true;
+ }
+ existing_data->methods_.push_back(method_ref);
+ } else {
+ QuickenState new_state;
+ new_state.methods_.push_back(method_ref);
+ new_state.quicken_data_ = quicken_data;
+ new_state.optimized_return_void_ = optimized_return_void;
+ bool inserted = shared_code_item_quicken_info_.emplace(code_item, new_state).second;
+ CHECK(inserted) << "Failed to insert " << dex_file.PrettyMethod(method_idx);
+ }
+
+ // Easy sanity check is to check that the existing stuff matches by re-quickening using the
+ // newly produced quicken data.
+ // Note that this needs to be behind the lock for this case since we may unquicken in another
+ // thread.
if (kIsDebugBuild) {
- // Double check that the counts line up with the size of the quicken info.
- size_t quicken_count = 0;
- for (CodeItemIterator it(*code_item); !it.Done(); it.Advance()) {
- if (QuickenInfoTable::NeedsIndexForInstruction(&it.CurrentInstruction())) {
- ++quicken_count;
+ CompilationState state2(this, unit, compilation_level, &quicken_data);
+ std::vector<uint8_t> new_data = state2.Compile();
+ CHECK(new_data == quicken_data) << "Mismatch producing new quicken data";
+ }
+ } else {
+ CompilationState state(this, unit, compilation_level, /*quicken_data*/ nullptr);
+ quicken_data = state.Compile();
+
+ // Easy sanity check is to check that the existing stuff matches by re-quickening using the
+ // newly produced quicken data.
+ if (kIsDebugBuild) {
+ CompilationState state2(this, unit, compilation_level, &quicken_data);
+ std::vector<uint8_t> new_data = state2.Compile();
+ CHECK(new_data == quicken_data) << "Mismatch producing new quicken data";
+ }
+ }
+
+ if (quicken_data.empty()) {
+ return nullptr;
+ }
+
+ // Create a `CompiledMethod`, with the quickened information in the vmap table.
+ InstructionSet instruction_set = driver_->GetInstructionSet();
+ if (instruction_set == InstructionSet::kThumb2) {
+ // Don't use the thumb2 instruction set to avoid the one off code delta.
+ instruction_set = InstructionSet::kArm;
+ }
+ CompiledMethod* ret = CompiledMethod::SwapAllocCompiledMethod(
+ driver_,
+ instruction_set,
+ ArrayRef<const uint8_t>(), // no code
+ 0,
+ 0,
+ 0,
+ ArrayRef<const uint8_t>(), // method_info
+ ArrayRef<const uint8_t>(quicken_data), // vmap_table
+ ArrayRef<const uint8_t>(), // cfi data
+ ArrayRef<const linker::LinkerPatch>());
+ DCHECK(ret != nullptr);
+ return ret;
+}
+
+void DexToDexCompiler::SetDexFiles(const std::vector<const DexFile*>& dex_files) {
+ // Record what code items are already seen to detect when multiple methods have the same code
+ // item.
+ std::unordered_set<const DexFile::CodeItem*> seen_code_items;
+ for (const DexFile* dex_file : dex_files) {
+ for (size_t i = 0; i < dex_file->NumClassDefs(); ++i) {
+ const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
+ const uint8_t* class_data = dex_file->GetClassData(class_def);
+ if (class_data == nullptr) {
+ continue;
+ }
+ ClassDataItemIterator it(*dex_file, class_data);
+ it.SkipAllFields();
+ for (; it.HasNextMethod(); it.Next()) {
+ const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
+ // Detect the shared code items.
+ if (!seen_code_items.insert(code_item).second) {
+ shared_code_items_.insert(code_item);
}
}
- CHECK_EQ(quicken_count, dex_compiler.GetQuickenedInfo().size());
}
- std::vector<uint8_t> quicken_data;
- for (QuickenedInfo info : dex_compiler.GetQuickenedInfo()) {
- // Dex pc is not serialized, only used for checking the instructions. Since we access the
- // array based on the index of the quickened instruction, the indexes must line up perfectly.
- // The reader side uses the NeedsIndexForInstruction function too.
- const Instruction* inst = Instruction::At(code_item->insns_ + info.dex_pc);
- CHECK(QuickenInfoTable::NeedsIndexForInstruction(inst)) << inst->Opcode();
- // Add the index.
- quicken_data.push_back(static_cast<uint8_t>(info.dex_member_index >> 0));
- quicken_data.push_back(static_cast<uint8_t>(info.dex_member_index >> 8));
- }
- InstructionSet instruction_set = driver->GetInstructionSet();
- if (instruction_set == kThumb2) {
- // Don't use the thumb2 instruction set to avoid the one off code delta.
- instruction_set = kArm;
- }
- return CompiledMethod::SwapAllocCompiledMethod(
- driver,
- instruction_set,
- ArrayRef<const uint8_t>(), // no code
- 0,
- 0,
- 0,
- ArrayRef<const uint8_t>(), // method_info
- ArrayRef<const uint8_t>(quicken_data), // vmap_table
- ArrayRef<const uint8_t>(), // cfi data
- ArrayRef<const LinkerPatch>());
}
- return nullptr;
+ VLOG(compiler) << "Shared code items " << shared_code_items_.size();
+}
+
+void DexToDexCompiler::UnquickenConflictingMethods() {
+ MutexLock mu(Thread::Current(), lock_);
+ size_t unquicken_count = 0;
+ for (const auto& pair : shared_code_item_quicken_info_) {
+ const DexFile::CodeItem* code_item = pair.first;
+ const QuickenState& state = pair.second;
+ CHECK_GE(state.methods_.size(), 1u);
+ if (state.conflict_) {
+ // Unquicken using the existing quicken data.
+ // TODO: Do we really need to pass a dex file in?
+ optimizer::ArtDecompileDEX(*state.methods_[0].dex_file,
+ *code_item,
+ ArrayRef<const uint8_t>(state.quicken_data_),
+ /* decompile_return_instruction*/ true);
+ ++unquicken_count;
+ // Go clear the vmaps for all the methods that were already quickened to avoid writing them
+ // out during oat writing.
+ for (const MethodReference& ref : state.methods_) {
+ CompiledMethod* method = driver_->RemoveCompiledMethod(ref);
+ if (method != nullptr) {
+ // There is up to one compiled method for each method ref. Releasing it leaves the
+ // deduped data intact, this means its safe to do even when other threads might be
+ // compiling.
+ CompiledMethod::ReleaseSwapAllocatedCompiledMethod(driver_, method);
+ }
+ }
+ }
+ }
}
} // namespace optimizer
diff --git a/compiler/dex/dex_to_dex_compiler.h b/compiler/dex/dex_to_dex_compiler.h
index 87ddb39..7536c31 100644
--- a/compiler/dex/dex_to_dex_compiler.h
+++ b/compiler/dex/dex_to_dex_compiler.h
@@ -17,14 +17,22 @@
#ifndef ART_COMPILER_DEX_DEX_TO_DEX_COMPILER_H_
#define ART_COMPILER_DEX_DEX_TO_DEX_COMPILER_H_
-#include "dex_file.h"
+#include <set>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "base/bit_vector.h"
+#include "dex/dex_file.h"
+#include "dex/invoke_type.h"
+#include "dex/method_reference.h"
#include "handle.h"
-#include "invoke_type.h"
+#include "quicken_info.h"
namespace art {
class CompiledMethod;
class CompilerDriver;
+class DexCompilationUnit;
namespace mirror {
class ClassLoader;
@@ -32,21 +40,79 @@
namespace optimizer {
-enum class DexToDexCompilationLevel {
- kDontDexToDexCompile, // Only meaning wrt image time interpretation.
- kOptimize // Perform peep-hole optimizations.
-};
-std::ostream& operator<<(std::ostream& os, const DexToDexCompilationLevel& rhs);
+class DexToDexCompiler {
+ public:
+ enum class CompilationLevel {
+ kDontDexToDexCompile, // Only meaning wrt image time interpretation.
+ kOptimize // Perform peep-hole optimizations.
+ };
-CompiledMethod* ArtCompileDEX(CompilerDriver* driver,
- const DexFile::CodeItem* code_item,
- uint32_t access_flags,
- InvokeType invoke_type,
- uint16_t class_def_idx,
- uint32_t method_idx,
- Handle<mirror::ClassLoader> class_loader,
- const DexFile& dex_file,
- DexToDexCompilationLevel dex_to_dex_compilation_level);
+ explicit DexToDexCompiler(CompilerDriver* driver);
+
+ CompiledMethod* CompileMethod(const DexFile::CodeItem* code_item,
+ uint32_t access_flags,
+ InvokeType invoke_type,
+ uint16_t class_def_idx,
+ uint32_t method_idx,
+ Handle<mirror::ClassLoader> class_loader,
+ const DexFile& dex_file,
+ const CompilationLevel compilation_level) WARN_UNUSED;
+
+ void MarkForCompilation(Thread* self,
+ const MethodReference& method_ref);
+
+ void ClearState();
+
+ // Unquicken all methods that have conflicting quicken info. This is not done during the
+ // quickening process to avoid race conditions.
+ void UnquickenConflictingMethods();
+
+ CompilerDriver* GetDriver() {
+ return driver_;
+ }
+
+ bool ShouldCompileMethod(const MethodReference& ref);
+
+ // Return the number of code items to quicken.
+ size_t NumCodeItemsToQuicken(Thread* self) const;
+
+ void SetDexFiles(const std::vector<const DexFile*>& dex_files);
+
+ private:
+ // Holds the state for compiling a single method.
+ struct CompilationState;
+
+ // Quicken state for a code item, may be referenced by multiple methods.
+ struct QuickenState {
+ std::vector<MethodReference> methods_;
+ std::vector<uint8_t> quicken_data_;
+ bool optimized_return_void_ = false;
+ bool conflict_ = false;
+ };
+
+ BitVector* GetOrAddBitVectorForDex(const DexFile* dex_file) REQUIRES(lock_);
+
+ CompilerDriver* const driver_;
+
+ // State for adding methods (should this be in its own class?).
+ const DexFile* active_dex_file_ = nullptr;
+ BitVector* active_bit_vector_ = nullptr;
+
+ // Lock that guards duplicate code items and the bitmap.
+ mutable Mutex lock_;
+ // Record what method references are going to get quickened.
+ std::unordered_map<const DexFile*, BitVector> should_quicken_;
+ // Guarded by lock_ during writing, accessed without a lock during quickening.
+ // This is safe because no thread is adding to the shared code items during the quickening phase.
+ std::unordered_set<const DexFile::CodeItem*> shared_code_items_;
+ // Blacklisted code items are unquickened in UnquickenConflictingMethods.
+ std::unordered_map<const DexFile::CodeItem*, QuickenState> shared_code_item_quicken_info_
+ GUARDED_BY(lock_);
+ // Number of added code items.
+ size_t num_code_items_ GUARDED_BY(lock_) = 0u;
+};
+
+std::ostream& operator<<(std::ostream& os, const DexToDexCompiler::CompilationLevel& rhs);
} // namespace optimizer
diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc
index 8e416b0..19b1900 100644
--- a/compiler/dex/dex_to_dex_decompiler_test.cc
+++ b/compiler/dex/dex_to_dex_decompiler_test.cc
@@ -17,14 +17,13 @@
#include "dex_to_dex_decompiler.h"
#include "class_linker.h"
-#include "compiler/common_compiler_test.h"
-#include "compiler/compiled_method.h"
-#include "compiler/driver/compiler_options.h"
-#include "compiler/driver/compiler_driver.h"
+#include "common_compiler_test.h"
+#include "compiled_method-inl.h"
#include "compiler_callbacks.h"
-#include "dex_file.h"
+#include "dex/dex_file.h"
+#include "driver/compiler_driver.h"
+#include "driver/compiler_options.h"
#include "handle_scope-inl.h"
-#include "verifier/method_verifier-inl.h"
#include "mirror/class_loader.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
@@ -92,7 +91,7 @@
it.SkipAllFields();
// Unquicken each method.
- while (it.HasNextDirectMethod()) {
+ while (it.HasNextMethod()) {
uint32_t method_idx = it.GetMemberIndex();
CompiledMethod* compiled_method =
compiler_driver_->GetCompiledMethod(MethodReference(updated_dex_file, method_idx));
@@ -100,20 +99,10 @@
if (compiled_method != nullptr) {
table = compiled_method->GetVmapTable();
}
- optimizer::ArtDecompileDEX(
- *it.GetMethodCodeItem(), table, /* decompile_return_instruction */ true);
- it.Next();
- }
- while (it.HasNextVirtualMethod()) {
- uint32_t method_idx = it.GetMemberIndex();
- CompiledMethod* compiled_method =
- compiler_driver_->GetCompiledMethod(MethodReference(updated_dex_file, method_idx));
- ArrayRef<const uint8_t> table;
- if (compiled_method != nullptr) {
- table = compiled_method->GetVmapTable();
- }
- optimizer::ArtDecompileDEX(
- *it.GetMethodCodeItem(), table, /* decompile_return_instruction */ true);
+ optimizer::ArtDecompileDEX(*updated_dex_file,
+ *it.GetMethodCodeItem(),
+ table,
+ /* decompile_return_instruction */ true);
it.Next();
}
DCHECK(!it.HasNext());
diff --git a/compiler/dex/inline_method_analyser.cc b/compiler/dex/inline_method_analyser.cc
index e5ff7fc..dc044c1 100644
--- a/compiler/dex/inline_method_analyser.cc
+++ b/compiler/dex/inline_method_analyser.cc
@@ -20,10 +20,11 @@
#include "art_method-inl.h"
#include "base/enums.h"
#include "class_linker-inl.h"
-#include "dex_file-inl.h"
-#include "dex_instruction.h"
-#include "dex_instruction-inl.h"
-#include "dex_instruction_utils.h"
+#include "dex/code_item_accessors-inl.h"
+#include "dex/dex_file-inl.h"
+#include "dex/dex_instruction-inl.h"
+#include "dex/dex_instruction.h"
+#include "dex/dex_instruction_utils.h"
#include "mirror/class-inl.h"
#include "mirror/dex_cache-inl.h"
@@ -43,7 +44,7 @@
typedef bool MatchFn(Matcher* matcher);
template <size_t size>
- static bool Match(const DexFile::CodeItem* code_item, MatchFn* const (&pattern)[size]);
+ static bool Match(const CodeItemDataAccessor* code_item, MatchFn* const (&pattern)[size]);
// Match and advance.
@@ -62,22 +63,20 @@
bool IPutOnThis();
private:
- explicit Matcher(const DexFile::CodeItem* code_item)
+ explicit Matcher(const CodeItemDataAccessor* code_item)
: code_item_(code_item),
- instruction_(Instruction::At(code_item->insns_)),
- pos_(0u),
- mark_(0u) { }
+ instruction_(code_item->begin()) {}
- static bool DoMatch(const DexFile::CodeItem* code_item, MatchFn* const* pattern, size_t size);
+ static bool DoMatch(const CodeItemDataAccessor* code_item, MatchFn* const* pattern, size_t size);
- const DexFile::CodeItem* const code_item_;
- const Instruction* instruction_;
- size_t pos_;
- size_t mark_;
+ const CodeItemDataAccessor* const code_item_;
+ DexInstructionIterator instruction_;
+ size_t pos_ = 0u;
+ size_t mark_ = 0u;
};
template <size_t size>
-bool Matcher::Match(const DexFile::CodeItem* code_item, MatchFn* const (&pattern)[size]) {
+bool Matcher::Match(const CodeItemDataAccessor* code_item, MatchFn* const (&pattern)[size]) {
return DoMatch(code_item, pattern, size);
}
@@ -93,7 +92,7 @@
return false;
}
matcher->pos_ += 1u;
- matcher->instruction_ = matcher->instruction_->Next();
+ ++matcher->instruction_;
return true;
}
@@ -105,7 +104,7 @@
return true;
}
matcher->pos_ = matcher->mark_;
- matcher->instruction_ = matcher->instruction_->Next();
+ ++matcher->instruction_;
return true;
}
@@ -122,12 +121,12 @@
}
bool Matcher::IPutOnThis() {
- DCHECK_NE(code_item_->ins_size_, 0u);
+ DCHECK_NE(code_item_->InsSize(), 0u);
return IsInstructionIPut(instruction_->Opcode()) &&
- instruction_->VRegB_22c() == code_item_->registers_size_ - code_item_->ins_size_;
+ instruction_->VRegB_22c() == code_item_->RegistersSize() - code_item_->InsSize();
}
-bool Matcher::DoMatch(const DexFile::CodeItem* code_item, MatchFn* const* pattern, size_t size) {
+bool Matcher::DoMatch(const CodeItemDataAccessor* code_item, MatchFn* const* pattern, size_t size) {
Matcher matcher(code_item);
while (matcher.pos_ != size) {
if (!pattern[matcher.pos_](&matcher)) {
@@ -142,8 +141,11 @@
ArtMethod* GetTargetConstructor(ArtMethod* method, const Instruction* invoke_direct)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK_EQ(invoke_direct->Opcode(), Instruction::INVOKE_DIRECT);
- DCHECK_EQ(invoke_direct->VRegC_35c(),
- method->GetCodeItem()->registers_size_ - method->GetCodeItem()->ins_size_);
+ if (kIsDebugBuild) {
+ CodeItemDataAccessor accessor(method->DexInstructionData());
+ DCHECK_EQ(invoke_direct->VRegC_35c(),
+ accessor.RegistersSize() - accessor.InsSize());
+ }
uint32_t method_index = invoke_direct->VRegB_35c();
ArtMethod* target_method = Runtime::Current()->GetClassLinker()->LookupResolvedMethod(
method_index, method->GetDexCache(), method->GetClassLoader());
@@ -158,7 +160,7 @@
// Return the forwarded arguments and check that all remaining arguments are zero.
// If the check fails, return static_cast<size_t>(-1).
-size_t CountForwardedConstructorArguments(const DexFile::CodeItem* code_item,
+size_t CountForwardedConstructorArguments(const CodeItemDataAccessor* code_item,
const Instruction* invoke_direct,
uint16_t zero_vreg_mask) {
DCHECK_EQ(invoke_direct->Opcode(), Instruction::INVOKE_DIRECT);
@@ -167,7 +169,7 @@
uint32_t args[Instruction::kMaxVarArgRegs];
invoke_direct->GetVarArgs(args);
uint16_t this_vreg = args[0];
- DCHECK_EQ(this_vreg, code_item->registers_size_ - code_item->ins_size_); // Checked by verifier.
+ DCHECK_EQ(this_vreg, code_item->RegistersSize() - code_item->InsSize()); // Checked by verifier.
size_t forwarded = 1u;
while (forwarded < number_of_args &&
args[forwarded] == this_vreg + forwarded &&
@@ -249,7 +251,7 @@
return true;
}
-bool DoAnalyseConstructor(const DexFile::CodeItem* code_item,
+bool DoAnalyseConstructor(const CodeItemDataAccessor* code_item,
ArtMethod* method,
/*inout*/ ConstructorIPutData (&iputs)[kMaxConstructorIPuts])
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -292,42 +294,43 @@
DCHECK(method->IsConstructor());
DCHECK(code_item != nullptr);
if (!method->GetDeclaringClass()->IsVerified() ||
- code_item->insns_size_in_code_units_ > kMaxCodeUnits ||
- code_item->registers_size_ > kMaxVRegs ||
+ code_item->InsnsSizeInCodeUnits() > kMaxCodeUnits ||
+ code_item->RegistersSize() > kMaxVRegs ||
!Matcher::Match(code_item, kConstructorPattern)) {
return false;
}
// Verify the invoke, prevent a few odd cases and collect IPUTs.
- uint16_t this_vreg = code_item->registers_size_ - code_item->ins_size_;
+ uint16_t this_vreg = code_item->RegistersSize() - code_item->InsSize();
uint16_t zero_vreg_mask = 0u;
- for (const Instruction* instruction = Instruction::At(code_item->insns_);
- instruction->Opcode() != Instruction::RETURN_VOID;
- instruction = instruction->Next()) {
- if (instruction->Opcode() == Instruction::INVOKE_DIRECT) {
- ArtMethod* target_method = GetTargetConstructor(method, instruction);
+
+ for (const DexInstructionPcPair& pair : *code_item) {
+ const Instruction& instruction = pair.Inst();
+ if (instruction.Opcode() == Instruction::RETURN_VOID) {
+ break;
+ } else if (instruction.Opcode() == Instruction::INVOKE_DIRECT) {
+ ArtMethod* target_method = GetTargetConstructor(method, &instruction);
if (target_method == nullptr) {
return false;
}
// We allow forwarding constructors only if they pass more arguments
// to prevent infinite recursion.
if (target_method->GetDeclaringClass() == method->GetDeclaringClass() &&
- instruction->VRegA_35c() <= code_item->ins_size_) {
+ instruction.VRegA_35c() <= code_item->InsSize()) {
return false;
}
- size_t forwarded = CountForwardedConstructorArguments(code_item, instruction, zero_vreg_mask);
+ size_t forwarded = CountForwardedConstructorArguments(code_item, &instruction, zero_vreg_mask);
if (forwarded == static_cast<size_t>(-1)) {
return false;
}
if (target_method->GetDeclaringClass()->IsObjectClass()) {
- DCHECK_EQ(Instruction::At(target_method->GetCodeItem()->insns_)->Opcode(),
- Instruction::RETURN_VOID);
+ DCHECK_EQ(target_method->DexInstructionData().begin()->Opcode(), Instruction::RETURN_VOID);
} else {
- const DexFile::CodeItem* target_code_item = target_method->GetCodeItem();
- if (target_code_item == nullptr) {
+ CodeItemDataAccessor target_code_item(target_method->DexInstructionData());
+ if (!target_code_item.HasCodeItem()) {
return false; // Native constructor?
}
- if (!DoAnalyseConstructor(target_code_item, target_method, iputs)) {
+ if (!DoAnalyseConstructor(&target_code_item, target_method, iputs)) {
return false;
}
// Prune IPUTs with zero input.
@@ -345,15 +348,15 @@
return false;
}
}
- } else if (IsInstructionDirectConst(instruction->Opcode())) {
- zero_vreg_mask |= GetZeroVRegMask(instruction);
+ } else if (IsInstructionDirectConst(instruction.Opcode())) {
+ zero_vreg_mask |= GetZeroVRegMask(&instruction);
if ((zero_vreg_mask & (1u << this_vreg)) != 0u) {
return false; // Overwriting `this` is unsupported.
}
} else {
- DCHECK(IsInstructionIPut(instruction->Opcode()));
- DCHECK_EQ(instruction->VRegB_22c(), this_vreg);
- if (!RecordConstructorIPut(method, instruction, this_vreg, zero_vreg_mask, iputs)) {
+ DCHECK(IsInstructionIPut(instruction.Opcode()));
+ DCHECK_EQ(instruction.VRegB_22c(), this_vreg);
+ if (!RecordConstructorIPut(method, &instruction, this_vreg, zero_vreg_mask, iputs)) {
return false;
}
}
@@ -363,7 +366,7 @@
} // anonymous namespace
-bool AnalyseConstructor(const DexFile::CodeItem* code_item,
+bool AnalyseConstructor(const CodeItemDataAccessor* code_item,
ArtMethod* method,
InlineMethod* result)
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -427,28 +430,27 @@
InlineMethodAnalyser::IPutVariant(Instruction::IPUT_SHORT), "iget/iput_short variant");
bool InlineMethodAnalyser::AnalyseMethodCode(ArtMethod* method, InlineMethod* result) {
- const DexFile::CodeItem* code_item = method->GetCodeItem();
- if (code_item == nullptr) {
+ CodeItemDataAccessor code_item(method->DexInstructionData());
+ if (!code_item.HasCodeItem()) {
// Native or abstract.
return false;
}
- return AnalyseMethodCode(code_item,
+ return AnalyseMethodCode(&code_item,
MethodReference(method->GetDexFile(), method->GetDexMethodIndex()),
method->IsStatic(),
method,
result);
}
-bool InlineMethodAnalyser::AnalyseMethodCode(const DexFile::CodeItem* code_item,
+bool InlineMethodAnalyser::AnalyseMethodCode(const CodeItemDataAccessor* code_item,
const MethodReference& method_ref,
bool is_static,
ArtMethod* method,
InlineMethod* result) {
// We currently support only plain return or 2-instruction methods.
- DCHECK_NE(code_item->insns_size_in_code_units_, 0u);
- const Instruction* instruction = Instruction::At(code_item->insns_);
- Instruction::Code opcode = instruction->Opcode();
+ DCHECK_NE(code_item->InsnsSizeInCodeUnits(), 0u);
+ Instruction::Code opcode = code_item->begin()->Opcode();
switch (opcode) {
case Instruction::RETURN_VOID:
@@ -509,7 +511,7 @@
}
bool InlineMethodAnalyser::IsSyntheticAccessor(MethodReference ref) {
- const DexFile::MethodId& method_id = ref.dex_file->GetMethodId(ref.dex_method_index);
+ const DexFile::MethodId& method_id = ref.dex_file->GetMethodId(ref.index);
const char* method_name = ref.dex_file->GetMethodName(method_id);
// javac names synthetic accessors "access$nnn",
// jack names them "-getN", "-putN", "-wrapN".
@@ -517,15 +519,15 @@
strncmp(method_name, "-", strlen("-")) == 0;
}
-bool InlineMethodAnalyser::AnalyseReturnMethod(const DexFile::CodeItem* code_item,
+bool InlineMethodAnalyser::AnalyseReturnMethod(const CodeItemDataAccessor* code_item,
InlineMethod* result) {
- const Instruction* return_instruction = Instruction::At(code_item->insns_);
+ DexInstructionIterator return_instruction = code_item->begin();
Instruction::Code return_opcode = return_instruction->Opcode();
uint32_t reg = return_instruction->VRegA_11x();
- uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_;
+ uint32_t arg_start = code_item->RegistersSize() - code_item->InsSize();
DCHECK_GE(reg, arg_start);
DCHECK_LT((return_opcode == Instruction::RETURN_WIDE) ? reg + 1 : reg,
- code_item->registers_size_);
+ code_item->RegistersSize());
if (result != nullptr) {
result->opcode = kInlineOpReturnArg;
@@ -539,9 +541,9 @@
return true;
}
-bool InlineMethodAnalyser::AnalyseConstMethod(const DexFile::CodeItem* code_item,
+bool InlineMethodAnalyser::AnalyseConstMethod(const CodeItemDataAccessor* code_item,
InlineMethod* result) {
- const Instruction* instruction = Instruction::At(code_item->insns_);
+ DexInstructionIterator instruction = code_item->begin();
const Instruction* return_instruction = instruction->Next();
Instruction::Code return_opcode = return_instruction->Opcode();
if (return_opcode != Instruction::RETURN &&
@@ -550,13 +552,13 @@
}
int32_t return_reg = return_instruction->VRegA_11x();
- DCHECK_LT(return_reg, code_item->registers_size_);
+ DCHECK_LT(return_reg, code_item->RegistersSize());
int32_t const_value = instruction->VRegB();
if (instruction->Opcode() == Instruction::CONST_HIGH16) {
const_value <<= 16;
}
- DCHECK_LT(instruction->VRegA(), code_item->registers_size_);
+ DCHECK_LT(instruction->VRegA(), code_item->RegistersSize());
if (instruction->VRegA() != return_reg) {
return false; // Not returning the value set by const?
}
@@ -570,12 +572,12 @@
return true;
}
-bool InlineMethodAnalyser::AnalyseIGetMethod(const DexFile::CodeItem* code_item,
+bool InlineMethodAnalyser::AnalyseIGetMethod(const CodeItemDataAccessor* code_item,
const MethodReference& method_ref,
bool is_static,
ArtMethod* method,
InlineMethod* result) {
- const Instruction* instruction = Instruction::At(code_item->insns_);
+ DexInstructionIterator instruction = code_item->begin();
Instruction::Code opcode = instruction->Opcode();
DCHECK(IsInstructionIGet(opcode));
@@ -590,17 +592,17 @@
uint32_t return_reg = return_instruction->VRegA_11x();
DCHECK_LT(return_opcode == Instruction::RETURN_WIDE ? return_reg + 1 : return_reg,
- code_item->registers_size_);
+ code_item->RegistersSize());
uint32_t dst_reg = instruction->VRegA_22c();
uint32_t object_reg = instruction->VRegB_22c();
uint32_t field_idx = instruction->VRegC_22c();
- uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_;
+ uint32_t arg_start = code_item->RegistersSize() - code_item->InsSize();
DCHECK_GE(object_reg, arg_start);
- DCHECK_LT(object_reg, code_item->registers_size_);
+ DCHECK_LT(object_reg, code_item->RegistersSize());
uint32_t object_arg = object_reg - arg_start;
- DCHECK_LT(opcode == Instruction::IGET_WIDE ? dst_reg + 1 : dst_reg, code_item->registers_size_);
+ DCHECK_LT(opcode == Instruction::IGET_WIDE ? dst_reg + 1 : dst_reg, code_item->RegistersSize());
if (dst_reg != return_reg) {
return false; // Not returning the value retrieved by IGET?
}
@@ -634,18 +636,18 @@
return true;
}
-bool InlineMethodAnalyser::AnalyseIPutMethod(const DexFile::CodeItem* code_item,
+bool InlineMethodAnalyser::AnalyseIPutMethod(const CodeItemDataAccessor* code_item,
const MethodReference& method_ref,
bool is_static,
ArtMethod* method,
InlineMethod* result) {
- const Instruction* instruction = Instruction::At(code_item->insns_);
+ DexInstructionIterator instruction = code_item->begin();
Instruction::Code opcode = instruction->Opcode();
DCHECK(IsInstructionIPut(opcode));
const Instruction* return_instruction = instruction->Next();
Instruction::Code return_opcode = return_instruction->Opcode();
- uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_;
+ uint32_t arg_start = code_item->RegistersSize() - code_item->InsSize();
uint16_t return_arg_plus1 = 0u;
if (return_opcode != Instruction::RETURN_VOID) {
if (return_opcode != Instruction::RETURN &&
@@ -657,7 +659,7 @@
uint32_t return_reg = return_instruction->VRegA_11x();
DCHECK_GE(return_reg, arg_start);
DCHECK_LT(return_opcode == Instruction::RETURN_WIDE ? return_reg + 1u : return_reg,
- code_item->registers_size_);
+ code_item->RegistersSize());
return_arg_plus1 = return_reg - arg_start + 1u;
}
@@ -665,9 +667,9 @@
uint32_t object_reg = instruction->VRegB_22c();
uint32_t field_idx = instruction->VRegC_22c();
DCHECK_GE(object_reg, arg_start);
- DCHECK_LT(object_reg, code_item->registers_size_);
+ DCHECK_LT(object_reg, code_item->RegistersSize());
DCHECK_GE(src_reg, arg_start);
- DCHECK_LT(opcode == Instruction::IPUT_WIDE ? src_reg + 1 : src_reg, code_item->registers_size_);
+ DCHECK_LT(opcode == Instruction::IPUT_WIDE ? src_reg + 1 : src_reg, code_item->RegistersSize());
uint32_t object_arg = object_reg - arg_start;
uint32_t src_arg = src_reg - arg_start;
diff --git a/compiler/dex/inline_method_analyser.h b/compiler/dex/inline_method_analyser.h
index a35e97f..e1d652a 100644
--- a/compiler/dex/inline_method_analyser.h
+++ b/compiler/dex/inline_method_analyser.h
@@ -19,9 +19,9 @@
#include "base/macros.h"
#include "base/mutex.h"
-#include "dex_file.h"
-#include "dex_instruction.h"
-#include "method_reference.h"
+#include "dex/dex_file.h"
+#include "dex/dex_instruction.h"
+#include "dex/method_reference.h"
/*
* NOTE: This code is part of the quick compiler. It lives in the runtime
@@ -30,6 +30,8 @@
namespace art {
+class CodeItemDataAccessor;
+
namespace verifier {
class MethodVerifier;
} // namespace verifier
@@ -121,21 +123,21 @@
static bool IsSyntheticAccessor(MethodReference ref);
private:
- static bool AnalyseMethodCode(const DexFile::CodeItem* code_item,
+ static bool AnalyseMethodCode(const CodeItemDataAccessor* code_item,
const MethodReference& method_ref,
bool is_static,
ArtMethod* method,
InlineMethod* result)
REQUIRES_SHARED(Locks::mutator_lock_);
- static bool AnalyseReturnMethod(const DexFile::CodeItem* code_item, InlineMethod* result);
- static bool AnalyseConstMethod(const DexFile::CodeItem* code_item, InlineMethod* result);
- static bool AnalyseIGetMethod(const DexFile::CodeItem* code_item,
+ static bool AnalyseReturnMethod(const CodeItemDataAccessor* code_item, InlineMethod* result);
+ static bool AnalyseConstMethod(const CodeItemDataAccessor* code_item, InlineMethod* result);
+ static bool AnalyseIGetMethod(const CodeItemDataAccessor* code_item,
const MethodReference& method_ref,
bool is_static,
ArtMethod* method,
InlineMethod* result)
REQUIRES_SHARED(Locks::mutator_lock_);
- static bool AnalyseIPutMethod(const DexFile::CodeItem* code_item,
+ static bool AnalyseIPutMethod(const CodeItemDataAccessor* code_item,
const MethodReference& method_ref,
bool is_static,
ArtMethod* method,
diff --git a/compiler/dex/quick_compiler_callbacks.cc b/compiler/dex/quick_compiler_callbacks.cc
index 1a240bd..baf97a8 100644
--- a/compiler/dex/quick_compiler_callbacks.cc
+++ b/compiler/dex/quick_compiler_callbacks.cc
@@ -17,8 +17,12 @@
#include "quick_compiler_callbacks.h"
#include "driver/compiler_driver.h"
-#include "verifier/method_verifier-inl.h"
+#include "mirror/class-inl.h"
+#include "mirror/object.h"
+#include "obj_ptr-inl.h"
+#include "thread-current-inl.h"
#include "verification_results.h"
+#include "verifier/method_verifier-inl.h"
namespace art {
@@ -34,17 +38,35 @@
}
}
-bool QuickCompilerCallbacks::CanAssumeVerified(ClassReference ref) {
+ClassStatus QuickCompilerCallbacks::GetPreviousClassState(ClassReference ref) {
// If we don't have class unloading enabled in the compiler, we will never see class that were
// previously verified. Return false to avoid overhead from the lookup in the compiler driver.
if (!does_class_unloading_) {
- return false;
+ return ClassStatus::kNotReady;
}
DCHECK(compiler_driver_ != nullptr);
// In the case of the quicken filter: avoiding verification of quickened instructions, which the
// verifier doesn't currently support.
// In the case of the verify filter, avoiding verifiying twice.
- return compiler_driver_->CanAssumeVerified(ref);
+ return compiler_driver_->GetClassStatus(ref);
+}
+
+void QuickCompilerCallbacks::UpdateClassState(ClassReference ref, ClassStatus status) {
+ // Driver is null when bootstrapping the runtime.
+ if (compiler_driver_ != nullptr) {
+ compiler_driver_->RecordClassStatus(ref, status);
+ }
+}
+
+bool QuickCompilerCallbacks::CanUseOatStatusForVerification(mirror::Class* klass) {
+ // No dex files: conservatively false.
+ if (dex_files_ == nullptr) {
+ return false;
+ }
+
+ // If the class isn't from one of the dex files, accept oat file data.
+ const DexFile* dex_file = &klass->GetDexFile();
+ return std::find(dex_files_->begin(), dex_files_->end(), dex_file) == dex_files_->end();
}
} // namespace art
diff --git a/compiler/dex/quick_compiler_callbacks.h b/compiler/dex/quick_compiler_callbacks.h
index 578aff4..8a07e9c 100644
--- a/compiler/dex/quick_compiler_callbacks.h
+++ b/compiler/dex/quick_compiler_callbacks.h
@@ -23,51 +23,62 @@
namespace art {
class CompilerDriver;
+class DexFile;
class VerificationResults;
class QuickCompilerCallbacks FINAL : public CompilerCallbacks {
- public:
- explicit QuickCompilerCallbacks(CompilerCallbacks::CallbackMode mode)
- : CompilerCallbacks(mode) {}
+ public:
+ explicit QuickCompilerCallbacks(CompilerCallbacks::CallbackMode mode)
+ : CompilerCallbacks(mode), dex_files_(nullptr) {}
- ~QuickCompilerCallbacks() { }
+ ~QuickCompilerCallbacks() { }
- void MethodVerified(verifier::MethodVerifier* verifier)
- REQUIRES_SHARED(Locks::mutator_lock_) OVERRIDE;
+ void MethodVerified(verifier::MethodVerifier* verifier)
+ REQUIRES_SHARED(Locks::mutator_lock_) OVERRIDE;
- void ClassRejected(ClassReference ref) OVERRIDE;
+ void ClassRejected(ClassReference ref) OVERRIDE;
- // We are running in an environment where we can call patchoat safely so we should.
- bool IsRelocationPossible() OVERRIDE {
- return true;
- }
+ // We are running in an environment where we can call patchoat safely so we should.
+ bool IsRelocationPossible() OVERRIDE {
+ return true;
+ }
- verifier::VerifierDeps* GetVerifierDeps() const OVERRIDE {
- return verifier_deps_.get();
- }
+ verifier::VerifierDeps* GetVerifierDeps() const OVERRIDE {
+ return verifier_deps_.get();
+ }
- void SetVerifierDeps(verifier::VerifierDeps* deps) OVERRIDE {
- verifier_deps_.reset(deps);
- }
+ void SetVerifierDeps(verifier::VerifierDeps* deps) OVERRIDE {
+ verifier_deps_.reset(deps);
+ }
- void SetVerificationResults(VerificationResults* verification_results) {
- verification_results_ = verification_results;
- }
+ void SetVerificationResults(VerificationResults* verification_results) {
+ verification_results_ = verification_results;
+ }
- bool CanAssumeVerified(ClassReference ref) OVERRIDE;
+ ClassStatus GetPreviousClassState(ClassReference ref) OVERRIDE;
- void SetDoesClassUnloading(bool does_class_unloading, CompilerDriver* compiler_driver)
- OVERRIDE {
- does_class_unloading_ = does_class_unloading;
- compiler_driver_ = compiler_driver;
- DCHECK(!does_class_unloading || compiler_driver_ != nullptr);
- }
+ void SetDoesClassUnloading(bool does_class_unloading, CompilerDriver* compiler_driver)
+ OVERRIDE {
+ does_class_unloading_ = does_class_unloading;
+ compiler_driver_ = compiler_driver;
+ DCHECK(!does_class_unloading || compiler_driver_ != nullptr);
+ }
- private:
- VerificationResults* verification_results_ = nullptr;
- bool does_class_unloading_ = false;
- CompilerDriver* compiler_driver_ = nullptr;
- std::unique_ptr<verifier::VerifierDeps> verifier_deps_;
+ void UpdateClassState(ClassReference ref, ClassStatus state) OVERRIDE;
+
+ bool CanUseOatStatusForVerification(mirror::Class* klass) OVERRIDE
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ void SetDexFiles(const std::vector<const DexFile*>* dex_files) {
+ dex_files_ = dex_files;
+ }
+
+ private:
+ VerificationResults* verification_results_ = nullptr;
+ bool does_class_unloading_ = false;
+ CompilerDriver* compiler_driver_ = nullptr;
+ std::unique_ptr<verifier::VerifierDeps> verifier_deps_;
+ const std::vector<const DexFile*>* dex_files_;
};
} // namespace art
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index beb3439..1e0b94d 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -16,14 +16,15 @@
#include "verification_results.h"
-#include "base/logging.h"
+#include <android-base/logging.h>
+
#include "base/mutex-inl.h"
#include "base/stl_util.h"
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
#include "runtime.h"
-#include "thread.h"
#include "thread-current-inl.h"
+#include "thread.h"
#include "utils/atomic_dex_ref_map-inl.h"
#include "verified_method.h"
#include "verifier/method_verifier-inl.h"
@@ -46,28 +47,22 @@
void VerificationResults::ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) {
DCHECK(method_verifier != nullptr);
- if (!compiler_options_->IsAnyCompilationEnabled()) {
- // Verified methods are only required for quickening and compilation.
- return;
- }
MethodReference ref = method_verifier->GetMethodReference();
std::unique_ptr<const VerifiedMethod> verified_method(VerifiedMethod::Create(method_verifier));
if (verified_method == nullptr) {
// We'll punt this later.
return;
}
- AtomicMap::InsertResult result = atomic_verified_methods_.Insert(
- DexFileReference(ref.dex_file, ref.dex_method_index),
- /*expected*/ nullptr,
- verified_method.get());
+ AtomicMap::InsertResult result = atomic_verified_methods_.Insert(ref,
+ /*expected*/ nullptr,
+ verified_method.get());
const VerifiedMethod* existing = nullptr;
bool inserted;
if (result != AtomicMap::kInsertResultInvalidDexFile) {
inserted = (result == AtomicMap::kInsertResultSuccess);
if (!inserted) {
// Rare case.
- CHECK(atomic_verified_methods_.Get(DexFileReference(ref.dex_file, ref.dex_method_index),
- &existing));
+ CHECK(atomic_verified_methods_.Get(ref, &existing));
CHECK_NE(verified_method.get(), existing);
}
} else {
@@ -104,8 +99,7 @@
const VerifiedMethod* VerificationResults::GetVerifiedMethod(MethodReference ref) {
const VerifiedMethod* ret = nullptr;
- DCHECK(compiler_options_->IsAnyCompilationEnabled());
- if (atomic_verified_methods_.Get(DexFileReference(ref.dex_file, ref.dex_method_index), &ret)) {
+ if (atomic_verified_methods_.Get(ref, &ret)) {
return ret;
}
ReaderMutexLock mu(Thread::Current(), verified_methods_lock_);
@@ -119,7 +113,7 @@
// at runtime.
std::unique_ptr<VerifiedMethod> verified_method = std::make_unique<VerifiedMethod>(
/* encountered_error_types */ 0, /* has_runtime_throw */ false);
- if (atomic_verified_methods_.Insert(DexFileReference(ref.dex_file, ref.dex_method_index),
+ if (atomic_verified_methods_.Insert(ref,
/*expected*/ nullptr,
verified_method.get()) ==
AtomicMap::InsertResult::kInsertResultSuccess) {
@@ -154,7 +148,7 @@
}
void VerificationResults::AddDexFile(const DexFile* dex_file) {
- atomic_verified_methods_.AddDexFile(dex_file, dex_file->NumMethodIds());
+ atomic_verified_methods_.AddDexFile(dex_file);
WriterMutexLock mu(Thread::Current(), verified_methods_lock_);
// There can be some verified methods that are already registered for the dex_file since we set
// up well known classes earlier. Remove these and put them in the array so that we don't
@@ -162,9 +156,7 @@
for (auto it = verified_methods_.begin(); it != verified_methods_.end(); ) {
MethodReference ref = it->first;
if (ref.dex_file == dex_file) {
- CHECK(atomic_verified_methods_.Insert(DexFileReference(ref.dex_file, ref.dex_method_index),
- nullptr,
- it->second) ==
+ CHECK(atomic_verified_methods_.Insert(ref, nullptr, it->second) ==
AtomicMap::kInsertResultSuccess);
it = verified_methods_.erase(it);
} else {
diff --git a/compiler/dex/verification_results.h b/compiler/dex/verification_results.h
index 5a03599..56f0030 100644
--- a/compiler/dex/verification_results.h
+++ b/compiler/dex/verification_results.h
@@ -23,9 +23,9 @@
#include "base/dchecked_vector.h"
#include "base/macros.h"
#include "base/mutex.h"
-#include "class_reference.h"
-#include "method_reference.h"
-#include "safe_map.h"
+#include "base/safe_map.h"
+#include "dex/class_reference.h"
+#include "dex/method_reference.h"
#include "utils/atomic_dex_ref_map.h"
namespace art {
@@ -64,10 +64,8 @@
private:
// Verified methods. The method array is fixed to avoid needing a lock to extend it.
- using AtomicMap = AtomicDexRefMap<const VerifiedMethod*>;
- using VerifiedMethodMap = SafeMap<MethodReference,
- const VerifiedMethod*,
- MethodReferenceComparator>;
+ using AtomicMap = AtomicDexRefMap<MethodReference, const VerifiedMethod*>;
+ using VerifiedMethodMap = SafeMap<MethodReference, const VerifiedMethod*>;
VerifiedMethodMap verified_methods_ GUARDED_BY(verified_methods_lock_);
const CompilerOptions* const compiler_options_;
diff --git a/compiler/dex/verified_method.cc b/compiler/dex/verified_method.cc
index e46dc59..f2da3ff 100644
--- a/compiler/dex/verified_method.cc
+++ b/compiler/dex/verified_method.cc
@@ -19,9 +19,11 @@
#include <algorithm>
#include <memory>
-#include "base/logging.h"
-#include "dex_file.h"
-#include "dex_instruction-inl.h"
+#include <android-base/logging.h>
+
+#include "dex/code_item_accessors-inl.h"
+#include "dex/dex_file.h"
+#include "dex/dex_instruction-inl.h"
#include "runtime.h"
#include "verifier/method_verifier-inl.h"
#include "verifier/reg_type-inl.h"
@@ -64,24 +66,20 @@
if (method_verifier->HasFailures()) {
return;
}
- const DexFile::CodeItem* code_item = method_verifier->CodeItem();
- const Instruction* inst = Instruction::At(code_item->insns_);
- const Instruction* end = Instruction::At(code_item->insns_ +
- code_item->insns_size_in_code_units_);
-
- for (; inst < end; inst = inst->Next()) {
- Instruction::Code code = inst->Opcode();
+ for (const DexInstructionPcPair& pair : method_verifier->CodeItem()) {
+ const Instruction& inst = pair.Inst();
+ const Instruction::Code code = inst.Opcode();
if (code == Instruction::CHECK_CAST) {
- uint32_t dex_pc = inst->GetDexPc(code_item->insns_);
+ const uint32_t dex_pc = pair.DexPc();
if (!method_verifier->GetInstructionFlags(dex_pc).IsVisited()) {
// Do not attempt to quicken this instruction, it's unreachable anyway.
continue;
}
const verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc);
const verifier::RegType& reg_type(line->GetRegisterType(method_verifier,
- inst->VRegA_21c()));
+ inst.VRegA_21c()));
const verifier::RegType& cast_type =
- method_verifier->ResolveCheckedClass(dex::TypeIndex(inst->VRegB_21c()));
+ method_verifier->ResolveCheckedClass(dex::TypeIndex(inst.VRegB_21c()));
// Pass null for the method verifier to not record the VerifierDeps dependency
// if the types are not assignable.
if (cast_type.IsStrictlyAssignableFrom(reg_type, /* method_verifier */ nullptr)) {
diff --git a/compiler/dex/verified_method.h b/compiler/dex/verified_method.h
index 64b3f44..f04392d 100644
--- a/compiler/dex/verified_method.h
+++ b/compiler/dex/verified_method.h
@@ -20,9 +20,9 @@
#include <vector>
#include "base/mutex.h"
-#include "dex_file.h"
-#include "method_reference.h"
-#include "safe_map.h"
+#include "base/safe_map.h"
+#include "dex/dex_file.h"
+#include "dex/method_reference.h"
namespace art {
diff --git a/compiler/driver/compiled_method_storage.cc b/compiler/driver/compiled_method_storage.cc
index 528b0a2..a26a985 100644
--- a/compiler/driver/compiled_method_storage.cc
+++ b/compiler/driver/compiled_method_storage.cc
@@ -19,10 +19,12 @@
#include "compiled_method_storage.h"
-#include "base/logging.h"
+#include <android-base/logging.h>
+
+#include "base/utils.h"
#include "compiled_method.h"
+#include "linker/linker_patch.h"
#include "thread-current-inl.h"
-#include "utils.h"
#include "utils/dedupe_set-inl.h"
#include "utils/swap_space.h"
@@ -135,16 +137,7 @@
return hash;
} else {
- size_t hash = 0x811c9dc5;
- for (uint32_t i = 0; i < len; ++i) {
- hash = (hash * 16777619) ^ data[i];
- }
- hash += hash << 13;
- hash ^= hash >> 7;
- hash += hash << 3;
- hash ^= hash >> 17;
- hash += hash << 5;
- return hash;
+ return HashBytes(data, len);
}
}
};
@@ -178,7 +171,7 @@
LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())),
dedupe_cfi_info_("dedupe cfi info", LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())),
dedupe_linker_patches_("dedupe cfi info",
- LengthPrefixedArrayAlloc<LinkerPatch>(swap_space_.get())) {
+ LengthPrefixedArrayAlloc<linker::LinkerPatch>(swap_space_.get())) {
}
CompiledMethodStorage::~CompiledMethodStorage() {
@@ -234,13 +227,13 @@
ReleaseArrayIfNotDeduplicated(cfi_info);
}
-const LengthPrefixedArray<LinkerPatch>* CompiledMethodStorage::DeduplicateLinkerPatches(
- const ArrayRef<const LinkerPatch>& linker_patches) {
+const LengthPrefixedArray<linker::LinkerPatch>* CompiledMethodStorage::DeduplicateLinkerPatches(
+ const ArrayRef<const linker::LinkerPatch>& linker_patches) {
return AllocateOrDeduplicateArray(linker_patches, &dedupe_linker_patches_);
}
void CompiledMethodStorage::ReleaseLinkerPatches(
- const LengthPrefixedArray<LinkerPatch>* linker_patches) {
+ const LengthPrefixedArray<linker::LinkerPatch>* linker_patches) {
ReleaseArrayIfNotDeduplicated(linker_patches);
}
diff --git a/compiler/driver/compiled_method_storage.h b/compiler/driver/compiled_method_storage.h
index 27011e8..249f06c 100644
--- a/compiler/driver/compiled_method_storage.h
+++ b/compiler/driver/compiled_method_storage.h
@@ -28,7 +28,9 @@
namespace art {
+namespace linker {
class LinkerPatch;
+} // namespace linker
class CompiledMethodStorage {
public:
@@ -61,9 +63,9 @@
const LengthPrefixedArray<uint8_t>* DeduplicateCFIInfo(const ArrayRef<const uint8_t>& cfi_info);
void ReleaseCFIInfo(const LengthPrefixedArray<uint8_t>* cfi_info);
- const LengthPrefixedArray<LinkerPatch>* DeduplicateLinkerPatches(
- const ArrayRef<const LinkerPatch>& linker_patches);
- void ReleaseLinkerPatches(const LengthPrefixedArray<LinkerPatch>* linker_patches);
+ const LengthPrefixedArray<linker::LinkerPatch>* DeduplicateLinkerPatches(
+ const ArrayRef<const linker::LinkerPatch>& linker_patches);
+ void ReleaseLinkerPatches(const LengthPrefixedArray<linker::LinkerPatch>* linker_patches);
private:
template <typename T, typename DedupeSetType>
@@ -98,7 +100,7 @@
ArrayDedupeSet<uint8_t> dedupe_method_info_;
ArrayDedupeSet<uint8_t> dedupe_vmap_table_;
ArrayDedupeSet<uint8_t> dedupe_cfi_info_;
- ArrayDedupeSet<LinkerPatch> dedupe_linker_patches_;
+ ArrayDedupeSet<linker::LinkerPatch> dedupe_linker_patches_;
DISALLOW_COPY_AND_ASSIGN(CompiledMethodStorage);
};
diff --git a/compiler/driver/compiled_method_storage_test.cc b/compiler/driver/compiled_method_storage_test.cc
index bbd28b2..0769561 100644
--- a/compiler/driver/compiled_method_storage_test.cc
+++ b/compiler/driver/compiled_method_storage_test.cc
@@ -14,10 +14,11 @@
* limitations under the License.
*/
+#include "compiled_method_storage.h"
+
#include <gtest/gtest.h>
-#include "compiled_method_storage.h"
-#include "compiled_method.h"
+#include "compiled_method-inl.h"
#include "compiler_driver.h"
#include "compiler_options.h"
#include "dex/verification_results.h"
@@ -30,15 +31,12 @@
CompilerDriver driver(&compiler_options,
&verification_results,
Compiler::kOptimizing,
- /* instruction_set_ */ kNone,
+ /* instruction_set_ */ InstructionSet::kNone,
/* instruction_set_features */ nullptr,
/* image_classes */ nullptr,
/* compiled_classes */ nullptr,
/* compiled_methods */ nullptr,
/* thread_count */ 1u,
- /* dump_stats */ false,
- /* dump_passes */ false,
- /* timer */ nullptr,
/* swap_fd */ -1,
/* profile_compilation_info */ nullptr);
CompiledMethodStorage* storage = driver.GetCompiledMethodStorage();
@@ -69,17 +67,17 @@
ArrayRef<const uint8_t>(raw_cfi_info1),
ArrayRef<const uint8_t>(raw_cfi_info2),
};
- const LinkerPatch raw_patches1[] = {
- LinkerPatch::CodePatch(0u, nullptr, 1u),
- LinkerPatch::RelativeMethodPatch(4u, nullptr, 0u, 1u),
+ const linker::LinkerPatch raw_patches1[] = {
+ linker::LinkerPatch::CodePatch(0u, nullptr, 1u),
+ linker::LinkerPatch::RelativeMethodPatch(4u, nullptr, 0u, 1u),
};
- const LinkerPatch raw_patches2[] = {
- LinkerPatch::CodePatch(0u, nullptr, 1u),
- LinkerPatch::RelativeMethodPatch(4u, nullptr, 0u, 2u),
+ const linker::LinkerPatch raw_patches2[] = {
+ linker::LinkerPatch::CodePatch(0u, nullptr, 1u),
+ linker::LinkerPatch::RelativeMethodPatch(4u, nullptr, 0u, 2u),
};
- ArrayRef<const LinkerPatch> patches[] = {
- ArrayRef<const LinkerPatch>(raw_patches1),
- ArrayRef<const LinkerPatch>(raw_patches2),
+ ArrayRef<const linker::LinkerPatch> patches[] = {
+ ArrayRef<const linker::LinkerPatch>(raw_patches1),
+ ArrayRef<const linker::LinkerPatch>(raw_patches2),
};
std::vector<CompiledMethod*> compiled_methods;
@@ -90,7 +88,7 @@
for (auto&& f : cfi_info) {
for (auto&& p : patches) {
compiled_methods.push_back(CompiledMethod::SwapAllocCompiledMethod(
- &driver, kNone, c, 0u, 0u, 0u, s, v, f, p));
+ &driver, InstructionSet::kNone, c, 0u, 0u, 0u, s, v, f, p));
}
}
}
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index b043929..294072d 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -32,14 +32,16 @@
namespace art {
-inline mirror::Class* CompilerDriver::ResolveClass(
- const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
- Handle<mirror::ClassLoader> class_loader, dex::TypeIndex cls_index,
+inline ObjPtr<mirror::Class> CompilerDriver::ResolveClass(
+ const ScopedObjectAccess& soa,
+ Handle<mirror::DexCache> dex_cache,
+ Handle<mirror::ClassLoader> class_loader,
+ dex::TypeIndex cls_index,
const DexCompilationUnit* mUnit) {
DCHECK_EQ(dex_cache->GetDexFile(), mUnit->GetDexFile());
DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get());
- mirror::Class* cls = mUnit->GetClassLinker()->ResolveType(
- *mUnit->GetDexFile(), cls_index, dex_cache, class_loader);
+ ObjPtr<mirror::Class> cls =
+ mUnit->GetClassLinker()->ResolveType(cls_index, dex_cache, class_loader);
DCHECK_EQ(cls == nullptr, soa.Self()->IsExceptionPending());
if (UNLIKELY(cls == nullptr)) {
// Clean up any exception left by type resolution.
@@ -48,9 +50,11 @@
return cls;
}
-inline mirror::Class* CompilerDriver::ResolveCompilingMethodsClass(
- const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
- Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit) {
+inline ObjPtr<mirror::Class> CompilerDriver::ResolveCompilingMethodsClass(
+ const ScopedObjectAccess& soa,
+ Handle<mirror::DexCache> dex_cache,
+ Handle<mirror::ClassLoader> class_loader,
+ const DexCompilationUnit* mUnit) {
DCHECK_EQ(dex_cache->GetDexFile(), mUnit->GetDexFile());
DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get());
const DexFile::MethodId& referrer_method_id =
@@ -58,13 +62,13 @@
return ResolveClass(soa, dex_cache, class_loader, referrer_method_id.class_idx_, mUnit);
}
-inline ArtField* CompilerDriver::ResolveFieldWithDexFile(
- const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
- Handle<mirror::ClassLoader> class_loader, const DexFile* dex_file,
- uint32_t field_idx, bool is_static) {
- DCHECK_EQ(dex_cache->GetDexFile(), dex_file);
+inline ArtField* CompilerDriver::ResolveField(const ScopedObjectAccess& soa,
+ Handle<mirror::DexCache> dex_cache,
+ Handle<mirror::ClassLoader> class_loader,
+ uint32_t field_idx,
+ bool is_static) {
ArtField* resolved_field = Runtime::Current()->GetClassLinker()->ResolveField(
- *dex_file, field_idx, dex_cache, class_loader, is_static);
+ field_idx, dex_cache, class_loader, is_static);
DCHECK_EQ(resolved_field == nullptr, soa.Self()->IsExceptionPending());
if (UNLIKELY(resolved_field == nullptr)) {
// Clean up any exception left by type resolution.
@@ -79,18 +83,11 @@
return resolved_field;
}
-inline ArtField* CompilerDriver::ResolveField(
- const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
- Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit,
- uint32_t field_idx, bool is_static) {
- DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get());
- return ResolveFieldWithDexFile(soa, dex_cache, class_loader, mUnit->GetDexFile(), field_idx,
- is_static);
-}
-
inline std::pair<bool, bool> CompilerDriver::IsFastInstanceField(
- mirror::DexCache* dex_cache, mirror::Class* referrer_class,
- ArtField* resolved_field, uint16_t field_idx) {
+ ObjPtr<mirror::DexCache> dex_cache,
+ ObjPtr<mirror::Class> referrer_class,
+ ArtField* resolved_field,
+ uint16_t field_idx) {
DCHECK(!resolved_field->IsStatic());
ObjPtr<mirror::Class> fields_class = resolved_field->GetDeclaringClass();
bool fast_get = referrer_class != nullptr &&
@@ -112,7 +109,7 @@
DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get());
ArtMethod* resolved_method =
mUnit->GetClassLinker()->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>(
- *dex_cache->GetDexFile(), method_idx, dex_cache, class_loader, nullptr, invoke_type);
+ 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.
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index e48b82d..5360476 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -16,9 +16,9 @@
#include "compiler_driver.h"
+#include <unistd.h>
#include <unordered_set>
#include <vector>
-#include <unistd.h>
#ifndef __APPLE__
#include <malloc.h> // For mallinfo
@@ -32,21 +32,24 @@
#include "base/array_ref.h"
#include "base/bit_vector.h"
#include "base/enums.h"
+#include "base/logging.h" // For VLOG
#include "base/stl_util.h"
#include "base/systrace.h"
#include "base/time_utils.h"
#include "base/timing_logger.h"
#include "class_linker-inl.h"
-#include "compiled_method.h"
+#include "compiled_method-inl.h"
#include "compiler.h"
#include "compiler_callbacks.h"
#include "compiler_driver-inl.h"
+#include "dex/descriptors_names.h"
+#include "dex/dex_file-inl.h"
+#include "dex/dex_file_annotations.h"
+#include "dex/dex_instruction-inl.h"
#include "dex/dex_to_dex_compiler.h"
#include "dex/verification_results.h"
#include "dex/verified_method.h"
#include "dex_compilation_unit.h"
-#include "dex_file-inl.h"
-#include "dex_instruction-inl.h"
#include "driver/compiler_options.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/accounting/heap_bitmap.h"
@@ -54,7 +57,9 @@
#include "gc/space/space.h"
#include "handle_scope-inl.h"
#include "intrinsics_enum.h"
+#include "jit/profile_compilation_info.h"
#include "jni_internal.h"
+#include "linker/linker_patch.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
#include "mirror/dex_cache-inl.h"
@@ -65,6 +70,7 @@
#include "nativehelper/ScopedLocalRef.h"
#include "object_lock.h"
#include "runtime.h"
+#include "runtime_intrinsics.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
#include "thread_list.h"
@@ -251,24 +257,6 @@
DISALLOW_COPY_AND_ASSIGN(AOTCompilationStats);
};
-class CompilerDriver::DexFileMethodSet {
- public:
- explicit DexFileMethodSet(const DexFile& dex_file)
- : dex_file_(dex_file),
- method_indexes_(dex_file.NumMethodIds(), false, Allocator::GetMallocAllocator()) {
- }
- DexFileMethodSet(DexFileMethodSet&& other) = default;
-
- const DexFile& GetDexFile() const { return dex_file_; }
-
- BitVector& GetMethodIndexes() { return method_indexes_; }
- const BitVector& GetMethodIndexes() const { return method_indexes_; }
-
- private:
- const DexFile& dex_file_;
- BitVector method_indexes_;
-};
-
CompilerDriver::CompilerDriver(
const CompilerOptions* compiler_options,
VerificationResults* verification_results,
@@ -279,36 +267,30 @@
std::unordered_set<std::string>* compiled_classes,
std::unordered_set<std::string>* compiled_methods,
size_t thread_count,
- bool dump_stats,
- bool dump_passes,
- CumulativeLogger* timer,
int swap_fd,
const ProfileCompilationInfo* profile_compilation_info)
: compiler_options_(compiler_options),
verification_results_(verification_results),
compiler_(Compiler::Create(this, compiler_kind)),
compiler_kind_(compiler_kind),
- instruction_set_(instruction_set == kArm ? kThumb2 : instruction_set),
+ instruction_set_(
+ instruction_set == InstructionSet::kArm ? InstructionSet::kThumb2 : instruction_set),
instruction_set_features_(instruction_set_features),
requires_constructor_barrier_lock_("constructor barrier lock"),
non_relative_linker_patch_count_(0u),
image_classes_(image_classes),
classes_to_compile_(compiled_classes),
methods_to_compile_(compiled_methods),
+ number_of_soft_verifier_failures_(0),
had_hard_verifier_failure_(false),
parallel_thread_count_(thread_count),
stats_(new AOTCompilationStats),
- dump_stats_(dump_stats),
- dump_passes_(dump_passes),
- timings_logger_(timer),
compiler_context_(nullptr),
support_boot_image_fixup_(true),
compiled_method_storage_(swap_fd),
profile_compilation_info_(profile_compilation_info),
max_arena_alloc_(0),
- dex_to_dex_references_lock_("dex-to-dex references lock"),
- dex_to_dex_references_(),
- current_dex_to_dex_methods_(nullptr) {
+ dex_to_dex_compiler_(this) {
DCHECK(compiler_options_ != nullptr);
compiler_->Init();
@@ -316,6 +298,8 @@
if (GetCompilerOptions().IsBootImage()) {
CHECK(image_classes_.get() != nullptr) << "Expected image classes for boot image";
}
+
+ compiled_method_storage_.SetDedupeEnabled(compiler_options_->DeduplicateCode());
}
CompilerDriver::~CompilerDriver() {
@@ -363,28 +347,6 @@
}
#undef CREATE_TRAMPOLINE
-static void SetupIntrinsic(Thread* self,
- Intrinsics intrinsic,
- InvokeType invoke_type,
- const char* class_name,
- const char* method_name,
- const char* signature)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- PointerSize image_size = class_linker->GetImagePointerSize();
- ObjPtr<mirror::Class> cls = class_linker->FindSystemClass(self, class_name);
- if (cls == nullptr) {
- LOG(FATAL) << "Could not find class of intrinsic " << class_name;
- }
- ArtMethod* method = cls->FindClassMethod(method_name, signature, image_size);
- if (method == nullptr || method->GetDeclaringClass() != cls) {
- LOG(FATAL) << "Could not find method of intrinsic "
- << class_name << " " << method_name << " " << signature;
- }
- DCHECK_EQ(method->GetInvokeType(), invoke_type);
- method->SetIntrinsic(static_cast<uint32_t>(intrinsic));
-}
-
void CompilerDriver::CompileAll(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
TimingLogger* timings) {
@@ -403,14 +365,7 @@
// We don't need to setup the intrinsics for non boot image compilation, as
// those compilations will pick up a boot image that have the ArtMethod already
// set with the intrinsics flag.
- ScopedObjectAccess soa(Thread::Current());
-#define SETUP_INTRINSICS(Name, InvokeType, NeedsEnvironmentOrCache, SideEffects, Exceptions, \
- ClassName, MethodName, Signature) \
- SetupIntrinsic(soa.Self(), Intrinsics::k##Name, InvokeType, ClassName, MethodName, Signature);
-#include "intrinsics_list.h"
-INTRINSICS_LIST(SETUP_INTRINSICS)
-#undef INTRINSICS_LIST
-#undef SETUP_INTRINSICS
+ InitializeIntrinsics();
}
// Compile:
// 1) Compile all classes and methods enabled for compilation. May fall back to dex-to-dex
@@ -418,17 +373,23 @@
if (GetCompilerOptions().IsAnyCompilationEnabled()) {
Compile(class_loader, dex_files, timings);
}
- if (dump_stats_) {
+ if (GetCompilerOptions().GetDumpStats()) {
stats_->Dump();
}
FreeThreadPools();
}
-static optimizer::DexToDexCompilationLevel GetDexToDexCompilationLevel(
+static optimizer::DexToDexCompiler::CompilationLevel GetDexToDexCompilationLevel(
Thread* self, const CompilerDriver& driver, Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file, const DexFile::ClassDef& class_def)
REQUIRES_SHARED(Locks::mutator_lock_) {
+ // When the dex file is uncompressed in the APK, we do not generate a copy in the .vdex
+ // file. As a result, dex2oat will map the dex file read-only, and we only need to check
+ // that to know if we can do quickening.
+ if (dex_file.GetContainer() != nullptr && dex_file.GetContainer()->IsReadOnly()) {
+ return optimizer::DexToDexCompiler::CompilationLevel::kDontDexToDexCompile;
+ }
auto* const runtime = Runtime::Current();
DCHECK(driver.GetCompilerOptions().IsQuickeningCompilationEnabled());
const char* descriptor = dex_file.GetClassDescriptor(class_def);
@@ -437,7 +398,7 @@
if (klass == nullptr) {
CHECK(self->IsExceptionPending());
self->ClearException();
- return optimizer::DexToDexCompilationLevel::kDontDexToDexCompile;
+ return optimizer::DexToDexCompiler::CompilationLevel::kDontDexToDexCompile;
}
// DexToDex at the kOptimize level may introduce quickened opcodes, which replace symbolic
// references with actual offsets. We cannot re-verify such instructions.
@@ -445,22 +406,23 @@
// We store the verification information in the class status in the oat file, which the linker
// can validate (checksums) and use to skip load-time verification. It is thus safe to
// optimize when a class has been fully verified before.
- optimizer::DexToDexCompilationLevel max_level = optimizer::DexToDexCompilationLevel::kOptimize;
+ optimizer::DexToDexCompiler::CompilationLevel max_level =
+ optimizer::DexToDexCompiler::CompilationLevel::kOptimize;
if (driver.GetCompilerOptions().GetDebuggable()) {
// We are debuggable so definitions of classes might be changed. We don't want to do any
// optimizations that could break that.
- max_level = optimizer::DexToDexCompilationLevel::kDontDexToDexCompile;
+ max_level = optimizer::DexToDexCompiler::CompilationLevel::kDontDexToDexCompile;
}
if (klass->IsVerified()) {
// Class is verified so we can enable DEX-to-DEX compilation for performance.
return max_level;
} else {
// Class verification has failed: do not run DEX-to-DEX optimizations.
- return optimizer::DexToDexCompilationLevel::kDontDexToDexCompile;
+ return optimizer::DexToDexCompiler::CompilationLevel::kDontDexToDexCompile;
}
}
-static optimizer::DexToDexCompilationLevel GetDexToDexCompilationLevel(
+static optimizer::DexToDexCompiler::CompilationLevel GetDexToDexCompilationLevel(
Thread* self,
const CompilerDriver& driver,
jobject jclass_loader,
@@ -477,136 +439,50 @@
// GetQuickGenericJniStub allowing down calls that aren't compiled using a JNI compiler?
static bool InstructionSetHasGenericJniStub(InstructionSet isa) {
switch (isa) {
- case kArm:
- case kArm64:
- case kThumb2:
- case kMips:
- case kMips64:
- case kX86:
- case kX86_64: return true;
+ case InstructionSet::kArm:
+ case InstructionSet::kArm64:
+ case InstructionSet::kThumb2:
+ case InstructionSet::kMips:
+ case InstructionSet::kMips64:
+ case InstructionSet::kX86:
+ case InstructionSet::kX86_64: return true;
default: return false;
}
}
-static void CompileMethod(Thread* self,
- CompilerDriver* driver,
- const DexFile::CodeItem* code_item,
- uint32_t access_flags,
- InvokeType invoke_type,
- uint16_t class_def_idx,
- uint32_t method_idx,
- Handle<mirror::ClassLoader> class_loader,
- const DexFile& dex_file,
- optimizer::DexToDexCompilationLevel dex_to_dex_compilation_level,
- bool compilation_enabled,
- Handle<mirror::DexCache> dex_cache) {
+template <typename CompileFn>
+static void CompileMethodHarness(
+ Thread* self,
+ CompilerDriver* driver,
+ const DexFile::CodeItem* code_item,
+ uint32_t access_flags,
+ InvokeType invoke_type,
+ uint16_t class_def_idx,
+ uint32_t method_idx,
+ 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);
- CompiledMethod* compiled_method = nullptr;
+ CompiledMethod* compiled_method;
uint64_t start_ns = kTimeCompileMethod ? NanoTime() : 0;
MethodReference method_ref(&dex_file, method_idx);
- if (driver->GetCurrentDexToDexMethods() != nullptr) {
- // This is the second pass when we dex-to-dex compile previously marked methods.
- // TODO: Refactor the compilation to avoid having to distinguish the two passes
- // here. That should be done on a higher level. http://b/29089975
- if (driver->GetCurrentDexToDexMethods()->IsBitSet(method_idx)) {
- VerificationResults* results = driver->GetVerificationResults();
- DCHECK(results != nullptr);
- const VerifiedMethod* verified_method = results->GetVerifiedMethod(method_ref);
- // Do not optimize if a VerifiedMethod is missing. SafeCast elision,
- // for example, relies on it.
- compiled_method = optimizer::ArtCompileDEX(
- driver,
- code_item,
- access_flags,
- invoke_type,
- class_def_idx,
- method_idx,
- class_loader,
- dex_file,
- (verified_method != nullptr)
- ? dex_to_dex_compilation_level
- : optimizer::DexToDexCompilationLevel::kDontDexToDexCompile);
- }
- } else if ((access_flags & kAccNative) != 0) {
- // Are we extracting only and have support for generic JNI down calls?
- if (!driver->GetCompilerOptions().IsJniCompilationEnabled() &&
- InstructionSetHasGenericJniStub(driver->GetInstructionSet())) {
- // Leaving this empty will trigger the generic JNI version
- } else {
- // Look-up the ArtMethod associated with this code_item (if any)
- // -- It is later used to lookup any [optimization] annotations for this method.
- ScopedObjectAccess soa(self);
+ compiled_method = compile_fn(self,
+ driver,
+ code_item,
+ access_flags,
+ invoke_type,
+ class_def_idx,
+ method_idx,
+ class_loader,
+ dex_file,
+ dex_to_dex_compilation_level,
+ compilation_enabled,
+ dex_cache);
- // TODO: Lookup annotation from DexFile directly without resolving method.
- ArtMethod* method =
- Runtime::Current()->GetClassLinker()->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>(
- dex_file,
- method_idx,
- dex_cache,
- class_loader,
- /* referrer */ nullptr,
- invoke_type);
-
- // Query any JNI optimization annotations such as @FastNative or @CriticalNative.
- Compiler::JniOptimizationFlags optimization_flags = Compiler::kNone;
- if (UNLIKELY(method == nullptr)) {
- // Failed method resolutions happen very rarely, e.g. ancestor class cannot be resolved.
- DCHECK(self->IsExceptionPending());
- self->ClearException();
- } else if (method->IsAnnotatedWithFastNative()) {
- // TODO: Will no longer need this CHECK once we have verifier checking this.
- CHECK(!method->IsAnnotatedWithCriticalNative());
- optimization_flags = Compiler::kFastNative;
- } else if (method->IsAnnotatedWithCriticalNative()) {
- // TODO: Will no longer need this CHECK once we have verifier checking this.
- CHECK(!method->IsAnnotatedWithFastNative());
- optimization_flags = Compiler::kCriticalNative;
- }
-
- compiled_method = driver->GetCompiler()->JniCompile(access_flags,
- method_idx,
- dex_file,
- optimization_flags);
- CHECK(compiled_method != nullptr);
- }
- } else if ((access_flags & kAccAbstract) != 0) {
- // Abstract methods don't have code.
- } else {
- VerificationResults* results = driver->GetVerificationResults();
- DCHECK(results != nullptr);
- const VerifiedMethod* verified_method = results->GetVerifiedMethod(method_ref);
- bool compile = compilation_enabled &&
- // Basic checks, e.g., not <clinit>.
- results->IsCandidateForCompilation(method_ref, access_flags) &&
- // Did not fail to create VerifiedMethod metadata.
- verified_method != nullptr &&
- // Do not have failures that should punt to the interpreter.
- !verified_method->HasRuntimeThrow() &&
- (verified_method->GetEncounteredVerificationFailures() &
- (verifier::VERIFY_ERROR_FORCE_INTERPRETER | verifier::VERIFY_ERROR_LOCKING)) == 0 &&
- // Is eligable for compilation by methods-to-compile filter.
- driver->IsMethodToCompile(method_ref) &&
- driver->ShouldCompileBasedOnProfile(method_ref);
-
- if (compile) {
- // NOTE: if compiler declines to compile this method, it will return null.
- compiled_method = driver->GetCompiler()->Compile(code_item,
- access_flags,
- invoke_type,
- class_def_idx,
- method_idx,
- class_loader,
- dex_file,
- dex_cache);
- }
- if (compiled_method == nullptr &&
- dex_to_dex_compilation_level != optimizer::DexToDexCompilationLevel::kDontDexToDexCompile) {
- DCHECK(!Runtime::Current()->UseJitCompilation());
- // TODO: add a command-line option to disable DEX-to-DEX compilation ?
- driver->MarkForDexToDexCompilation(self, method_ref);
- }
- }
if (kTimeCompileMethod) {
uint64_t duration_ns = NanoTime() - start_ns;
if (duration_ns > MsToNs(driver->GetCompiler()->GetMaximumCompilationTimeBeforeWarning())) {
@@ -618,7 +494,7 @@
if (compiled_method != nullptr) {
// Count non-relative linker patches.
size_t non_relative_linker_patch_count = 0u;
- for (const LinkerPatch& patch : compiled_method->GetPatches()) {
+ for (const linker::LinkerPatch& patch : compiled_method->GetPatches()) {
if (!patch.IsPcRelative()) {
++non_relative_linker_patch_count;
}
@@ -637,6 +513,170 @@
}
}
+static void CompileMethodDex2Dex(
+ Thread* self,
+ CompilerDriver* driver,
+ const DexFile::CodeItem* code_item,
+ uint32_t access_flags,
+ InvokeType invoke_type,
+ uint16_t class_def_idx,
+ uint32_t method_idx,
+ 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,
+ const DexFile::CodeItem* code_item,
+ uint32_t access_flags,
+ InvokeType invoke_type,
+ uint16_t class_def_idx,
+ uint32_t method_idx,
+ 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);
+
+ optimizer::DexToDexCompiler* const compiler = &driver->GetDexToDexCompiler();
+
+ if (compiler->ShouldCompileMethod(method_ref)) {
+ VerificationResults* results = driver->GetVerificationResults();
+ DCHECK(results != nullptr);
+ const VerifiedMethod* verified_method = results->GetVerifiedMethod(method_ref);
+ // Do not optimize if a VerifiedMethod is missing. SafeCast elision,
+ // for example, relies on it.
+ return compiler->CompileMethod(
+ code_item,
+ access_flags,
+ invoke_type,
+ class_def_idx,
+ method_idx,
+ class_loader,
+ dex_file,
+ (verified_method != nullptr)
+ ? dex_to_dex_compilation_level
+ : optimizer::DexToDexCompiler::CompilationLevel::kDontDexToDexCompile);
+ }
+ return nullptr;
+ };
+ CompileMethodHarness(self,
+ driver,
+ code_item,
+ access_flags,
+ invoke_type,
+ class_def_idx,
+ method_idx,
+ class_loader,
+ dex_file,
+ dex_to_dex_compilation_level,
+ compilation_enabled,
+ dex_cache,
+ dex_2_dex_fn);
+}
+
+static void CompileMethodQuick(
+ Thread* self,
+ CompilerDriver* driver,
+ const DexFile::CodeItem* code_item,
+ uint32_t access_flags,
+ InvokeType invoke_type,
+ uint16_t class_def_idx,
+ uint32_t method_idx,
+ 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,
+ CompilerDriver* driver,
+ const DexFile::CodeItem* code_item,
+ uint32_t access_flags,
+ InvokeType invoke_type,
+ uint16_t class_def_idx,
+ uint32_t method_idx,
+ 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;
+ MethodReference method_ref(&dex_file, method_idx);
+
+ if ((access_flags & kAccNative) != 0) {
+ // Are we extracting only and have support for generic JNI down calls?
+ if (!driver->GetCompilerOptions().IsJniCompilationEnabled() &&
+ InstructionSetHasGenericJniStub(driver->GetInstructionSet())) {
+ // Leaving this empty will trigger the generic JNI version
+ } else {
+ // Query any JNI optimization annotations such as @FastNative or @CriticalNative.
+ access_flags |= annotations::GetNativeMethodAnnotationAccessFlags(
+ dex_file, dex_file.GetClassDef(class_def_idx), method_idx);
+
+ compiled_method = driver->GetCompiler()->JniCompile(
+ access_flags, method_idx, dex_file, dex_cache);
+ CHECK(compiled_method != nullptr);
+ }
+ } else if ((access_flags & kAccAbstract) != 0) {
+ // Abstract methods don't have code.
+ } else {
+ VerificationResults* results = driver->GetVerificationResults();
+ DCHECK(results != nullptr);
+ const VerifiedMethod* verified_method = results->GetVerifiedMethod(method_ref);
+ bool compile = compilation_enabled &&
+ // Basic checks, e.g., not <clinit>.
+ results->IsCandidateForCompilation(method_ref, access_flags) &&
+ // Did not fail to create VerifiedMethod metadata.
+ verified_method != nullptr &&
+ // Do not have failures that should punt to the interpreter.
+ !verified_method->HasRuntimeThrow() &&
+ (verified_method->GetEncounteredVerificationFailures() &
+ (verifier::VERIFY_ERROR_FORCE_INTERPRETER | verifier::VERIFY_ERROR_LOCKING)) == 0 &&
+ // Is eligable for compilation by methods-to-compile filter.
+ driver->IsMethodToCompile(method_ref) &&
+ driver->ShouldCompileBasedOnProfile(method_ref);
+
+ if (compile) {
+ // NOTE: if compiler declines to compile this method, it will return null.
+ compiled_method = driver->GetCompiler()->Compile(code_item,
+ access_flags,
+ invoke_type,
+ class_def_idx,
+ method_idx,
+ class_loader,
+ dex_file,
+ dex_cache);
+ }
+ if (compiled_method == nullptr &&
+ dex_to_dex_compilation_level !=
+ optimizer::DexToDexCompiler::CompilationLevel::kDontDexToDexCompile) {
+ DCHECK(!Runtime::Current()->UseJitCompilation());
+ // TODO: add a command-line option to disable DEX-to-DEX compilation ?
+ driver->GetDexToDexCompiler().MarkForCompilation(self, method_ref);
+ }
+ }
+ return compiled_method;
+ };
+ CompileMethodHarness(self,
+ driver,
+ code_item,
+ access_flags,
+ invoke_type,
+ class_def_idx,
+ method_idx,
+ class_loader,
+ dex_file,
+ dex_to_dex_compilation_level,
+ compilation_enabled,
+ dex_cache,
+ quick_fn);
+}
+
void CompilerDriver::CompileOne(Thread* self, ArtMethod* method, TimingLogger* timings) {
DCHECK(!Runtime::Current()->IsStarted());
jobject jclass_loader;
@@ -671,53 +711,42 @@
PreCompile(jclass_loader, dex_files, timings);
// Can we run DEX-to-DEX compiler on this class ?
- optimizer::DexToDexCompilationLevel dex_to_dex_compilation_level =
+ optimizer::DexToDexCompiler::CompilationLevel dex_to_dex_compilation_level =
GetDexToDexCompilationLevel(self,
*this,
jclass_loader,
*dex_file,
dex_file->GetClassDef(class_def_idx));
- DCHECK(current_dex_to_dex_methods_ == nullptr);
- CompileMethod(self,
- this,
- code_item,
- access_flags,
- invoke_type,
- class_def_idx,
- method_idx,
- class_loader,
- *dex_file,
- dex_to_dex_compilation_level,
- true,
- dex_cache);
+ CompileMethodQuick(self,
+ this,
+ code_item,
+ access_flags,
+ invoke_type,
+ class_def_idx,
+ method_idx,
+ class_loader,
+ *dex_file,
+ dex_to_dex_compilation_level,
+ true,
+ dex_cache);
- ArrayRef<DexFileMethodSet> dex_to_dex_references;
- {
- // From this point on, we shall not modify dex_to_dex_references_, so
- // just grab a reference to it that we use without holding the mutex.
- MutexLock lock(Thread::Current(), dex_to_dex_references_lock_);
- dex_to_dex_references = ArrayRef<DexFileMethodSet>(dex_to_dex_references_);
- }
- if (!dex_to_dex_references.empty()) {
- DCHECK_EQ(dex_to_dex_references.size(), 1u);
- DCHECK(&dex_to_dex_references[0].GetDexFile() == dex_file);
- current_dex_to_dex_methods_ = &dex_to_dex_references.front().GetMethodIndexes();
- DCHECK(current_dex_to_dex_methods_->IsBitSet(method_idx));
- DCHECK_EQ(current_dex_to_dex_methods_->NumSetBits(), 1u);
- CompileMethod(self,
- this,
- code_item,
- access_flags,
- invoke_type,
- class_def_idx,
- method_idx,
- class_loader,
- *dex_file,
- dex_to_dex_compilation_level,
- true,
- dex_cache);
- current_dex_to_dex_methods_ = nullptr;
+ const size_t num_methods = dex_to_dex_compiler_.NumCodeItemsToQuicken(self);
+ if (num_methods != 0) {
+ DCHECK_EQ(num_methods, 1u);
+ CompileMethodDex2Dex(self,
+ this,
+ code_item,
+ access_flags,
+ invoke_type,
+ class_def_idx,
+ method_idx,
+ class_loader,
+ *dex_file,
+ dex_to_dex_compilation_level,
+ true,
+ dex_cache);
+ dex_to_dex_compiler_.ClearState();
}
FreeThreadPools();
@@ -761,19 +790,15 @@
return;
}
- const uint16_t* code_ptr = code_item->insns_;
- const uint16_t* code_end = code_item->insns_ + code_item->insns_size_in_code_units_;
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
-
- while (code_ptr < code_end) {
- const Instruction* inst = Instruction::At(code_ptr);
+ for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(dex_file, code_item)) {
switch (inst->Opcode()) {
case Instruction::CONST_STRING:
case Instruction::CONST_STRING_JUMBO: {
dex::StringIndex string_index((inst->Opcode() == Instruction::CONST_STRING)
? inst->VRegB_21c()
: inst->VRegB_31c());
- mirror::String* string = class_linker->ResolveString(dex_file, string_index, dex_cache);
+ ObjPtr<mirror::String> string = class_linker->ResolveString(string_index, dex_cache);
CHECK(string != nullptr) << "Could not allocate a string when forcing determinism";
break;
}
@@ -781,8 +806,6 @@
default:
break;
}
-
- code_ptr += inst->SizeInCodeUnits();
}
}
@@ -819,31 +842,17 @@
continue;
}
- // Direct methods.
- int64_t previous_direct_method_idx = -1;
- while (it.HasNextDirectMethod()) {
+ // Direct and virtual methods.
+ int64_t previous_method_idx = -1;
+ while (it.HasNextMethod()) {
uint32_t method_idx = it.GetMemberIndex();
- if (method_idx == previous_direct_method_idx) {
+ if (method_idx == previous_method_idx) {
// smali can create dex files with two encoded_methods sharing the same method_idx
// http://code.google.com/p/smali/issues/detail?id=119
it.Next();
continue;
}
- previous_direct_method_idx = method_idx;
- ResolveConstStrings(dex_cache, *dex_file, it.GetMethodCodeItem());
- it.Next();
- }
- // Virtual methods.
- int64_t previous_virtual_method_idx = -1;
- while (it.HasNextVirtualMethod()) {
- uint32_t method_idx = it.GetMemberIndex();
- if (method_idx == previous_virtual_method_idx) {
- // smali can create dex files with two encoded_methods sharing the same method_idx
- // http://code.google.com/p/smali/issues/detail?id=119
- it.Next();
- continue;
- }
- previous_virtual_method_idx = method_idx;
+ previous_method_idx = method_idx;
ResolveConstStrings(dex_cache, *dex_file, it.GetMethodCodeItem());
it.Next();
}
@@ -896,7 +905,7 @@
for (const DexFile* dex_file : dex_files) {
// Can be already inserted if the caller is CompileOne. This happens for gtests.
if (!compiled_methods_.HaveDexFile(dex_file)) {
- compiled_methods_.AddDexFile(dex_file, dex_file->NumMethodIds());
+ compiled_methods_.AddDexFile(dex_file);
}
}
// Resolve eagerly to prepare for compilation.
@@ -923,8 +932,17 @@
VLOG(compiler) << "Verify: " << GetMemoryUsageString(false);
if (had_hard_verifier_failure_ && GetCompilerOptions().AbortOnHardVerifierFailure()) {
- LOG(FATAL) << "Had a hard failure verifying all classes, and was asked to abort in such "
- << "situations. Please check the log.";
+ // Avoid dumping threads. Even if we shut down the thread pools, there will still be three
+ // instances of this thread's stack.
+ LOG(FATAL_WITHOUT_ABORT) << "Had a hard failure verifying all classes, and was asked to abort "
+ << "in such situations. Please check the log.";
+ _exit(1);
+ } else if (number_of_soft_verifier_failures_ > 0 &&
+ GetCompilerOptions().AbortOnSoftVerifierFailure()) {
+ LOG(FATAL_WITHOUT_ABORT) << "Had " << number_of_soft_verifier_failures_ << " soft failure(s) "
+ << "verifying all classes, and was asked to abort in such situations. "
+ << "Please check the log.";
+ _exit(1);
}
if (compiler_options_->IsAnyCompilationEnabled()) {
@@ -961,7 +979,7 @@
return true;
}
- std::string tmp = method_ref.dex_file->PrettyMethod(method_ref.dex_method_index, true);
+ std::string tmp = method_ref.PrettyMethod();
return methods_to_compile_->find(tmp.c_str()) != methods_to_compile_->end();
}
@@ -982,8 +1000,7 @@
if (kDebugProfileGuidedCompilation) {
LOG(INFO) << "[ProfileGuidedCompilation] "
- << (result ? "Compiled" : "Skipped") << " method:"
- << method_ref.dex_file->PrettyMethod(method_ref.dex_method_index, true);
+ << (result ? "Compiled" : "Skipped") << " method:" << method_ref.PrettyMethod(true);
}
return result;
}
@@ -1013,14 +1030,14 @@
ArtMethod* method,
std::set<std::pair<dex::TypeIndex, const DexFile*>>* exceptions_to_resolve)
REQUIRES_SHARED(Locks::mutator_lock_) {
- const DexFile::CodeItem* code_item = method->GetCodeItem();
- if (code_item == nullptr) {
+ if (method->GetCodeItem() == nullptr) {
return; // native or abstract method
}
- if (code_item->tries_size_ == 0) {
+ CodeItemDataAccessor accessor(method->DexInstructionData());
+ if (accessor.TriesSize() == 0) {
return; // nothing to process
}
- const uint8_t* encoded_catch_handler_list = DexFile::GetCatchHandlerData(*code_item, 0);
+ const uint8_t* encoded_catch_handler_list = accessor.GetCatchHandlerData();
size_t num_encoded_catch_handlers = DecodeUnsignedLeb128(&encoded_catch_handler_list);
for (size_t i = 0; i < num_encoded_catch_handlers; i++) {
int32_t encoded_catch_handler_size = DecodeSignedLeb128(&encoded_catch_handler_list);
@@ -1112,22 +1129,21 @@
for (const auto& exception_type : unresolved_exception_types) {
dex::TypeIndex exception_type_idx = exception_type.first;
const DexFile* dex_file = exception_type.second;
- StackHandleScope<2> hs2(self);
+ StackHandleScope<1> hs2(self);
Handle<mirror::DexCache> dex_cache(hs2.NewHandle(class_linker->RegisterDexFile(*dex_file,
nullptr)));
- Handle<mirror::Class> klass(hs2.NewHandle(
+ ObjPtr<mirror::Class> klass =
(dex_cache != nullptr)
- ? class_linker->ResolveType(*dex_file,
- exception_type_idx,
+ ? class_linker->ResolveType(exception_type_idx,
dex_cache,
ScopedNullHandle<mirror::ClassLoader>())
- : nullptr));
+ : nullptr;
if (klass == nullptr) {
const DexFile::TypeId& type_id = dex_file->GetTypeId(exception_type_idx);
const char* descriptor = dex_file->GetTypeDescriptor(type_id);
LOG(FATAL) << "Failed to resolve class " << descriptor;
}
- DCHECK(java_lang_Throwable->IsAssignableFrom(klass.Get()));
+ DCHECK(java_lang_Throwable->IsAssignableFrom(klass));
}
// Resolving exceptions may load classes that reference more exceptions, iterate until no
// more are found
@@ -1348,17 +1364,6 @@
return IsImageClass(descriptor);
}
-void CompilerDriver::MarkForDexToDexCompilation(Thread* self, const MethodReference& method_ref) {
- MutexLock lock(self, dex_to_dex_references_lock_);
- // Since we're compiling one dex file at a time, we need to look for the
- // current dex file entry only at the end of dex_to_dex_references_.
- if (dex_to_dex_references_.empty() ||
- &dex_to_dex_references_.back().GetDexFile() != method_ref.dex_file) {
- dex_to_dex_references_.emplace_back(*method_ref.dex_file);
- }
- dex_to_dex_references_.back().GetMethodIndexes().SetBit(method_ref.dex_method_index);
-}
-
bool CompilerDriver::CanAccessTypeWithoutChecks(ObjPtr<mirror::Class> referrer_class,
ObjPtr<mirror::Class> resolved_class) {
if (resolved_class == nullptr) {
@@ -1431,17 +1436,18 @@
}
ArtField* CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx,
- const DexCompilationUnit* mUnit, bool is_put,
+ const DexCompilationUnit* mUnit,
+ bool is_put,
const ScopedObjectAccess& soa) {
// Try to resolve the field and compiling method's class.
ArtField* resolved_field;
- mirror::Class* referrer_class;
+ ObjPtr<mirror::Class> referrer_class;
Handle<mirror::DexCache> dex_cache(mUnit->GetDexCache());
{
- Handle<mirror::ClassLoader> class_loader_handle = mUnit->GetClassLoader();
- resolved_field = ResolveField(soa, dex_cache, class_loader_handle, mUnit, field_idx, false);
+ Handle<mirror::ClassLoader> class_loader = mUnit->GetClassLoader();
+ resolved_field = ResolveField(soa, dex_cache, class_loader, field_idx, /* is_static */ false);
referrer_class = resolved_field != nullptr
- ? ResolveCompilingMethodsClass(soa, dex_cache, class_loader_handle, mUnit) : nullptr;
+ ? ResolveCompilingMethodsClass(soa, dex_cache, class_loader, mUnit) : nullptr;
}
bool can_link = false;
if (resolved_field != nullptr && referrer_class != nullptr) {
@@ -1539,13 +1545,19 @@
void ForAll(size_t begin, size_t end, CompilationVisitor* visitor, size_t work_units)
REQUIRES(!*Locks::mutator_lock_) {
+ ForAllLambda(begin, end, [visitor](size_t index) { visitor->Visit(index); }, work_units);
+ }
+
+ template <typename Fn>
+ void ForAllLambda(size_t begin, size_t end, Fn fn, size_t work_units)
+ REQUIRES(!*Locks::mutator_lock_) {
Thread* self = Thread::Current();
self->AssertNoPendingException();
CHECK_GT(work_units, 0U);
index_.StoreRelaxed(begin);
for (size_t i = 0; i < work_units; ++i) {
- thread_pool_->AddTask(self, new ForAllClosure(this, end, visitor));
+ thread_pool_->AddTask(self, new ForAllClosureLambda<Fn>(this, end, fn));
}
thread_pool_->StartWorkers(self);
@@ -1565,32 +1577,33 @@
}
private:
- class ForAllClosure : public Task {
+ template <typename Fn>
+ class ForAllClosureLambda : public Task {
public:
- ForAllClosure(ParallelCompilationManager* manager, size_t end, CompilationVisitor* visitor)
+ ForAllClosureLambda(ParallelCompilationManager* manager, size_t end, Fn fn)
: manager_(manager),
end_(end),
- visitor_(visitor) {}
+ fn_(fn) {}
- virtual void Run(Thread* self) {
+ void Run(Thread* self) OVERRIDE {
while (true) {
const size_t index = manager_->NextIndex();
if (UNLIKELY(index >= end_)) {
break;
}
- visitor_->Visit(index);
+ fn_(index);
self->AssertNoPendingException();
}
}
- virtual void Finalize() {
+ void Finalize() OVERRIDE {
delete this;
}
private:
ParallelCompilationManager* const manager_;
const size_t end_;
- CompilationVisitor* const visitor_;
+ Fn fn_;
};
AtomicInteger index_;
@@ -1606,7 +1619,7 @@
// A fast version of SkipClass above if the class pointer is available
// that avoids the expensive FindInClassPath search.
-static bool SkipClass(jobject class_loader, const DexFile& dex_file, mirror::Class* klass)
+static bool SkipClass(jobject class_loader, const DexFile& dex_file, ObjPtr<mirror::Class> klass)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(klass != nullptr);
const DexFile& original_dex_file = *klass->GetDexCache()->GetDexFile();
@@ -1674,7 +1687,7 @@
: manager_(manager) {}
void Visit(size_t class_def_index) OVERRIDE REQUIRES(!Locks::mutator_lock_) {
- ATRACE_CALL();
+ ScopedTrace trace(__FUNCTION__);
Thread* const self = Thread::Current();
jobject jclass_loader = manager_->GetClassLoader();
const DexFile& dex_file = *manager_->GetDexFile();
@@ -1700,8 +1713,8 @@
Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(
soa.Self(), dex_file)));
// Resolve the class.
- mirror::Class* klass = class_linker->ResolveType(dex_file, class_def.class_idx_, dex_cache,
- class_loader);
+ ObjPtr<mirror::Class> klass =
+ class_linker->ResolveType(class_def.class_idx_, dex_cache, class_loader);
bool resolve_fields_and_methods;
if (klass == nullptr) {
// Class couldn't be resolved, for example, super-class is in a different dex file. Don't
@@ -1727,8 +1740,8 @@
ClassDataItemIterator it(dex_file, class_data);
while (it.HasNextStaticField()) {
if (resolve_fields_and_methods) {
- ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(),
- dex_cache, class_loader, true);
+ ArtField* field = class_linker->ResolveField(
+ it.GetMemberIndex(), dex_cache, class_loader, /* is_static */ true);
if (field == nullptr) {
CheckAndClearResolveException(soa.Self());
}
@@ -1742,8 +1755,8 @@
requires_constructor_barrier = true;
}
if (resolve_fields_and_methods) {
- ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(),
- dex_cache, class_loader, false);
+ ArtField* field = class_linker->ResolveField(
+ it.GetMemberIndex(), dex_cache, class_loader, /* is_static */ false);
if (field == nullptr) {
CheckAndClearResolveException(soa.Self());
}
@@ -1751,18 +1764,12 @@
it.Next();
}
if (resolve_fields_and_methods) {
- while (it.HasNextDirectMethod()) {
+ while (it.HasNextMethod()) {
ArtMethod* method = class_linker->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>(
- dex_file, it.GetMemberIndex(), dex_cache, class_loader, nullptr,
- it.GetMethodInvokeType(class_def));
- if (method == nullptr) {
- CheckAndClearResolveException(soa.Self());
- }
- it.Next();
- }
- while (it.HasNextVirtualMethod()) {
- ArtMethod* method = class_linker->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>(
- dex_file, it.GetMemberIndex(), dex_cache, class_loader, nullptr,
+ it.GetMemberIndex(),
+ dex_cache,
+ class_loader,
+ /* referrer */ nullptr,
it.GetMethodInvokeType(class_def));
if (method == nullptr) {
CheckAndClearResolveException(soa.Self());
@@ -1798,7 +1805,7 @@
dex_file,
class_loader.Get())));
ObjPtr<mirror::Class> klass = (dex_cache != nullptr)
- ? class_linker->ResolveType(dex_file, dex::TypeIndex(type_idx), dex_cache, class_loader)
+ ? class_linker->ResolveType(dex::TypeIndex(type_idx), dex_cache, class_loader)
: nullptr;
if (klass == nullptr) {
@@ -1869,12 +1876,7 @@
ClassDataItemIterator it(dex_file, class_data);
it.SkipAllFields();
- while (it.HasNextDirectMethod()) {
- verification_results->CreateVerifiedMethodFor(MethodReference(&dex_file, it.GetMemberIndex()));
- it.Next();
- }
-
- while (it.HasNextVirtualMethod()) {
+ while (it.HasNextMethod()) {
verification_results->CreateVerifiedMethodFor(MethodReference(&dex_file, it.GetMemberIndex()));
it.Next();
}
@@ -1883,7 +1885,7 @@
static void LoadAndUpdateStatus(const DexFile& dex_file,
const DexFile::ClassDef& class_def,
- mirror::Class::Status status,
+ ClassStatus status,
Handle<mirror::ClassLoader> class_loader,
Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -1941,17 +1943,17 @@
if (compiler_only_verifies) {
// Just update the compiled_classes_ map. The compiler doesn't need to resolve
// the type.
- DexFileReference ref(dex_file, i);
- mirror::Class::Status existing = mirror::Class::kStatusNotReady;
+ ClassReference ref(dex_file, i);
+ ClassStatus existing = ClassStatus::kNotReady;
DCHECK(compiled_classes_.Get(ref, &existing)) << ref.dex_file->GetLocation();
ClassStateTable::InsertResult result =
- compiled_classes_.Insert(ref, existing, mirror::Class::kStatusVerified);
+ compiled_classes_.Insert(ref, existing, ClassStatus::kVerified);
CHECK_EQ(result, ClassStateTable::kInsertResultSuccess);
} else {
// Update the class status, so later compilation stages know they don't need to verify
// the class.
LoadAndUpdateStatus(
- *dex_file, class_def, mirror::Class::kStatusVerified, class_loader, soa.Self());
+ *dex_file, class_def, ClassStatus::kVerified, class_loader, soa.Self());
// Create `VerifiedMethod`s for each methods, the compiler expects one for
// quickening or compiling.
// Note that this means:
@@ -1965,7 +1967,7 @@
// this class again.
LoadAndUpdateStatus(*dex_file,
class_def,
- mirror::Class::kStatusRetryVerificationAtRuntime,
+ ClassStatus::kRetryVerificationAtRuntime,
class_loader,
soa.Self());
}
@@ -2033,7 +2035,7 @@
: manager_(manager), log_level_(log_level) {}
virtual void Visit(size_t class_def_index) REQUIRES(!Locks::mutator_lock_) OVERRIDE {
- ATRACE_CALL();
+ ScopedTrace trace(__FUNCTION__);
ScopedObjectAccess soa(Thread::Current());
const DexFile& dex_file = *manager_->GetDexFile();
const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
@@ -2072,13 +2074,13 @@
LOG(ERROR) << "Verification failed on class " << PrettyDescriptor(descriptor)
<< " because: " << error_msg;
manager_->GetCompiler()->SetHadHardVerifierFailure();
+ } else if (failure_kind == verifier::FailureKind::kSoftFailure) {
+ manager_->GetCompiler()->AddSoftVerifierFailure();
} else {
// Force a soft failure for the VerifierDeps. This is a sanity measure, as
// the vdex file already records that the class hasn't been resolved. It avoids
// trying to do future verification optimizations when processing the vdex file.
- DCHECK(failure_kind == verifier::FailureKind::kSoftFailure ||
- failure_kind == verifier::FailureKind::kNoFailure)
- << failure_kind;
+ DCHECK(failure_kind == verifier::FailureKind::kNoFailure) << failure_kind;
failure_kind = verifier::FailureKind::kSoftFailure;
}
} else if (!SkipClass(jclass_loader, dex_file, klass.Get())) {
@@ -2090,6 +2092,8 @@
CHECK(soa.Self()->IsExceptionPending());
soa.Self()->ClearException();
manager_->GetCompiler()->SetHadHardVerifierFailure();
+ } else if (failure_kind == verifier::FailureKind::kSoftFailure) {
+ manager_->GetCompiler()->AddSoftVerifierFailure();
}
CHECK(klass->ShouldVerifyAtRuntime() || klass->IsVerified() || klass->IsErroneous())
@@ -2099,16 +2103,20 @@
ClassReference ref(manager_->GetDexFile(), class_def_index);
manager_->GetCompiler()->RecordClassStatus(ref, klass->GetStatus());
- // It is *very* problematic if there are verification errors in the boot classpath. For example,
- // we rely on things working OK without verification when the decryption dialog is brought up.
- // So abort in a debug build if we find this violated.
+ // It is *very* problematic if there are resolution errors in the boot classpath.
+ //
+ // It is also bad if classes fail verification. For example, we rely on things working
+ // OK without verification when the decryption dialog is brought up. It is thus highly
+ // recommended to compile the boot classpath with
+ // --abort-on-hard-verifier-error --abort-on-soft-verifier-error
+ // which is the default build system configuration.
if (kIsDebugBuild) {
- // TODO(narayan): Remove this special case for signature polymorphic
- // invokes once verifier support is fully implemented.
- if (manager_->GetCompiler()->GetCompilerOptions().IsBootImage() &&
- !android::base::StartsWith(descriptor, "Ljava/lang/invoke/")) {
- DCHECK(klass->IsVerified()) << "Boot classpath class " << klass->PrettyClass()
- << " failed to fully verify: state= " << klass->GetStatus();
+ if (manager_->GetCompiler()->GetCompilerOptions().IsBootImage()) {
+ if (!klass->IsResolved() || klass->IsErroneous()) {
+ LOG(FATAL) << "Boot classpath class " << klass->PrettyClass()
+ << " failed to resolve/is erroneous: state= " << klass->GetStatus();
+ UNREACHABLE();
+ }
}
if (klass->IsVerified()) {
DCHECK_EQ(failure_kind, verifier::FailureKind::kNoFailure);
@@ -2142,7 +2150,9 @@
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
ParallelCompilationManager context(class_linker, class_loader, this, &dex_file, dex_files,
thread_pool);
- verifier::HardFailLogMode log_level = GetCompilerOptions().AbortOnHardVerifierFailure()
+ bool abort_on_verifier_failures = GetCompilerOptions().AbortOnHardVerifierFailure()
+ || GetCompilerOptions().AbortOnSoftVerifierFailure();
+ verifier::HardFailLogMode log_level = abort_on_verifier_failures
? verifier::HardFailLogMode::kLogInternalFatal
: verifier::HardFailLogMode::kLogWarning;
VerifyClassVisitor visitor(&context, log_level);
@@ -2154,7 +2164,7 @@
explicit SetVerifiedClassVisitor(const ParallelCompilationManager* manager) : manager_(manager) {}
virtual void Visit(size_t class_def_index) REQUIRES(!Locks::mutator_lock_) OVERRIDE {
- ATRACE_CALL();
+ ScopedTrace trace(__FUNCTION__);
ScopedObjectAccess soa(Thread::Current());
const DexFile& dex_file = *manager_->GetDexFile();
const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
@@ -2171,10 +2181,10 @@
// Only do this if the class is resolved. If even resolution fails, quickening will go very,
// very wrong.
if (klass->IsResolved() && !klass->IsErroneousResolved()) {
- if (klass->GetStatus() < mirror::Class::kStatusVerified) {
+ if (klass->GetStatus() < ClassStatus::kVerified) {
ObjectLock<mirror::Class> lock(soa.Self(), klass);
// Set class status to verified.
- mirror::Class::SetStatus(klass, mirror::Class::kStatusVerified, soa.Self());
+ mirror::Class::SetStatus(klass, ClassStatus::kVerified, soa.Self());
// Mark methods as pre-verified. If we don't do this, the interpreter will run with
// access checks.
klass->SetSkipAccessChecksFlagOnAllMethods(
@@ -2204,7 +2214,7 @@
TimingLogger* timings) {
TimingLogger::ScopedTiming t("Verify Dex File", timings);
if (!compiled_classes_.HaveDexFile(&dex_file)) {
- compiled_classes_.AddDexFile(&dex_file, dex_file.NumClassDefs());
+ compiled_classes_.AddDexFile(&dex_file);
}
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
ParallelCompilationManager context(class_linker, class_loader, this, &dex_file, dex_files,
@@ -2218,7 +2228,7 @@
explicit InitializeClassVisitor(const ParallelCompilationManager* manager) : manager_(manager) {}
void Visit(size_t class_def_index) OVERRIDE {
- ATRACE_CALL();
+ ScopedTrace trace(__FUNCTION__);
jobject jclass_loader = manager_->GetClassLoader();
const DexFile& dex_file = *manager_->GetDexFile();
const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
@@ -2251,7 +2261,7 @@
const bool is_boot_image = manager_->GetCompiler()->GetCompilerOptions().IsBootImage();
const bool is_app_image = manager_->GetCompiler()->GetCompilerOptions().IsAppImage();
- mirror::Class::Status old_status = klass->GetStatus();
+ ClassStatus old_status = klass->GetStatus();
// Don't initialize classes in boot space when compiling app image
if (is_app_image && klass->IsBootStrapClassLoaded()) {
// Also return early and don't store the class status in the recorded class status.
@@ -2307,6 +2317,7 @@
// The boot image case doesn't need to recursively initialize the dependencies with
// special logic since the class linker already does this.
can_init_static_fields =
+ ClassLinker::kAppImageMayContainStrings &&
!soa.Self()->IsExceptionPending() &&
is_superclass_initialized &&
NoClinitInDependency(klass, soa.Self(), &class_loader);
@@ -2323,10 +2334,8 @@
// a ReaderWriterMutex but we're holding the mutator lock so we fail mutex sanity
// checks in Thread::AssertThreadSuspensionIsAllowable.
Runtime* const runtime = Runtime::Current();
- Transaction transaction;
-
// Run the class initializer in transaction mode.
- runtime->EnterTransactionMode(&transaction);
+ runtime->EnterTransactionMode(is_app_image, klass.Get());
bool success = manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, true,
true);
// TODO we detach transaction from runtime to indicate we quit the transactional
@@ -2335,7 +2344,11 @@
{
ScopedAssertNoThreadSuspension ants("Transaction end");
- runtime->ExitTransactionMode();
+
+ if (success) {
+ runtime->ExitTransactionMode();
+ DCHECK(!runtime->IsActiveTransaction());
+ }
if (!success) {
CHECK(soa.Self()->IsExceptionPending());
@@ -2349,7 +2362,7 @@
*file_log << exception->Dump() << "\n";
}
soa.Self()->ClearException();
- transaction.Rollback();
+ runtime->RollbackAllTransactions();
CHECK_EQ(old_status, klass->GetStatus()) << "Previous class status not restored";
} else if (is_boot_image) {
// For boot image, we want to put the updated status in the oat class since we can't
@@ -2374,7 +2387,7 @@
// would do so they can be skipped at runtime.
if (!klass->IsInitialized() &&
manager_->GetClassLinker()->ValidateSuperClassDescriptors(klass)) {
- old_status = mirror::Class::kStatusSuperclassValidated;
+ old_status = ClassStatus::kSuperclassValidated;
} else {
soa.Self()->ClearException();
}
@@ -2396,22 +2409,20 @@
DCHECK(!klass->IsInitialized());
StackHandleScope<1> hs(Thread::Current());
- Handle<mirror::DexCache> h_dex_cache = hs.NewHandle(klass->GetDexCache());
- const DexFile* dex_file = manager_->GetDexFile();
+ Handle<mirror::DexCache> dex_cache = hs.NewHandle(klass->GetDexCache());
const DexFile::ClassDef* class_def = klass->GetClassDef();
ClassLinker* class_linker = manager_->GetClassLinker();
// Check encoded final field values for strings and intern.
- annotations::RuntimeEncodedStaticFieldValueIterator value_it(*dex_file,
- &h_dex_cache,
- &class_loader,
+ annotations::RuntimeEncodedStaticFieldValueIterator value_it(dex_cache,
+ class_loader,
manager_->GetClassLinker(),
*class_def);
for ( ; value_it.HasNext(); value_it.Next()) {
if (value_it.GetValueType() == annotations::RuntimeEncodedStaticFieldValueIterator::kString) {
// Resolve the string. This will intern the string.
art::ObjPtr<mirror::String> resolved = class_linker->ResolveString(
- *dex_file, dex::StringIndex(value_it.GetJavaValue().i), h_dex_cache);
+ dex::StringIndex(value_it.GetJavaValue().i), dex_cache);
CHECK(resolved != nullptr);
}
}
@@ -2419,30 +2430,24 @@
// Intern strings seen in <clinit>.
ArtMethod* clinit = klass->FindClassInitializer(class_linker->GetImagePointerSize());
if (clinit != nullptr) {
- const DexFile::CodeItem* code_item = clinit->GetCodeItem();
- DCHECK(code_item != nullptr);
- const Instruction* inst = Instruction::At(code_item->insns_);
-
- const uint32_t insns_size = code_item->insns_size_in_code_units_;
- for (uint32_t dex_pc = 0; dex_pc < insns_size;) {
+ for (const DexInstructionPcPair& inst : clinit->DexInstructions()) {
if (inst->Opcode() == Instruction::CONST_STRING) {
ObjPtr<mirror::String> s = class_linker->ResolveString(
- *dex_file, dex::StringIndex(inst->VRegB_21c()), h_dex_cache);
+ dex::StringIndex(inst->VRegB_21c()), dex_cache);
CHECK(s != nullptr);
} else if (inst->Opcode() == Instruction::CONST_STRING_JUMBO) {
ObjPtr<mirror::String> s = class_linker->ResolveString(
- *dex_file, dex::StringIndex(inst->VRegB_31c()), h_dex_cache);
+ dex::StringIndex(inst->VRegB_31c()), dex_cache);
CHECK(s != nullptr);
}
- dex_pc += inst->SizeInCodeUnits();
- inst = inst->Next();
}
}
}
bool ResolveTypesOfMethods(Thread* self, ArtMethod* m)
REQUIRES_SHARED(Locks::mutator_lock_) {
- auto rtn_type = m->GetReturnType(true); // return value is discarded because resolve will be done internally.
+ // Return value of ResolveReturnType() is discarded because resolve will be done internally.
+ ObjPtr<mirror::Class> rtn_type = m->ResolveReturnType();
if (rtn_type == nullptr) {
self->ClearException();
return false;
@@ -2451,7 +2456,7 @@
if (types != nullptr) {
for (uint32_t i = 0; i < types->Size(); ++i) {
dex::TypeIndex param_type_idx = types->GetTypeItem(i).type_idx_;
- auto param_type = m->GetClassFromTypeIndex(param_type_idx, true);
+ ObjPtr<mirror::Class> param_type = m->ResolveClassFromTypeIndex(param_type_idx);
if (param_type == nullptr) {
self->ClearException();
return false;
@@ -2674,76 +2679,37 @@
}
if (GetCompilerOptions().IsBootImage()) {
// Prune garbage objects created during aborted transactions.
- Runtime::Current()->GetHeap()->CollectGarbage(true);
+ Runtime::Current()->GetHeap()->CollectGarbage(/* clear_soft_references */ true);
}
}
-void CompilerDriver::Compile(jobject class_loader,
- const std::vector<const DexFile*>& dex_files,
- TimingLogger* timings) {
- if (kDebugProfileGuidedCompilation) {
- LOG(INFO) << "[ProfileGuidedCompilation] " <<
- ((profile_compilation_info_ == nullptr)
- ? "null"
- : profile_compilation_info_->DumpInfo(&dex_files));
- }
+template <typename CompileFn>
+static void CompileDexFile(CompilerDriver* driver,
+ jobject class_loader,
+ const DexFile& dex_file,
+ const std::vector<const DexFile*>& dex_files,
+ ThreadPool* thread_pool,
+ size_t thread_count,
+ TimingLogger* timings,
+ const char* timing_name,
+ CompileFn compile_fn) {
+ TimingLogger::ScopedTiming t(timing_name, timings);
+ ParallelCompilationManager context(Runtime::Current()->GetClassLinker(),
+ class_loader,
+ driver,
+ &dex_file,
+ dex_files,
+ thread_pool);
- current_dex_to_dex_methods_ = nullptr;
- Thread* const self = Thread::Current();
- {
- // Clear in case we aren't the first call to Compile.
- MutexLock mu(self, dex_to_dex_references_lock_);
- dex_to_dex_references_.clear();
- }
-
- for (const DexFile* dex_file : dex_files) {
- CHECK(dex_file != nullptr);
- CompileDexFile(class_loader,
- *dex_file,
- dex_files,
- parallel_thread_pool_.get(),
- parallel_thread_count_,
- timings);
- const ArenaPool* const arena_pool = Runtime::Current()->GetArenaPool();
- const size_t arena_alloc = arena_pool->GetBytesAllocated();
- max_arena_alloc_ = std::max(arena_alloc, max_arena_alloc_);
- Runtime::Current()->ReclaimArenaPoolMemory();
- }
-
- ArrayRef<DexFileMethodSet> dex_to_dex_references;
- {
- // From this point on, we shall not modify dex_to_dex_references_, so
- // just grab a reference to it that we use without holding the mutex.
- MutexLock lock(self, dex_to_dex_references_lock_);
- dex_to_dex_references = ArrayRef<DexFileMethodSet>(dex_to_dex_references_);
- }
- for (const auto& method_set : dex_to_dex_references) {
- current_dex_to_dex_methods_ = &method_set.GetMethodIndexes();
- CompileDexFile(class_loader,
- method_set.GetDexFile(),
- dex_files,
- parallel_thread_pool_.get(),
- parallel_thread_count_,
- timings);
- }
- current_dex_to_dex_methods_ = nullptr;
-
- VLOG(compiler) << "Compile: " << GetMemoryUsageString(false);
-}
-
-class CompileClassVisitor : public CompilationVisitor {
- public:
- explicit CompileClassVisitor(const ParallelCompilationManager* manager) : manager_(manager) {}
-
- virtual void Visit(size_t class_def_index) REQUIRES(!Locks::mutator_lock_) OVERRIDE {
- ATRACE_CALL();
- const DexFile& dex_file = *manager_->GetDexFile();
+ auto compile = [&context, &compile_fn](size_t class_def_index) {
+ ScopedTrace trace(__FUNCTION__);
+ const DexFile& dex_file = *context.GetDexFile();
const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
- ClassLinker* class_linker = manager_->GetClassLinker();
- jobject jclass_loader = manager_->GetClassLoader();
+ ClassLinker* class_linker = context.GetClassLinker();
+ jobject jclass_loader = context.GetClassLoader();
ClassReference ref(&dex_file, class_def_index);
// Skip compiling classes with generic verifier failures since they will still fail at runtime
- if (manager_->GetCompiler()->verification_results_->IsClassRejected(ref)) {
+ if (context.GetCompiler()->GetVerificationResults()->IsClassRejected(ref)) {
return;
}
// Use a scoped object access to perform to the quick SkipClass check.
@@ -2774,10 +2740,10 @@
// Go to native so that we don't block GC during compilation.
ScopedThreadSuspension sts(soa.Self(), kNative);
- CompilerDriver* const driver = manager_->GetCompiler();
+ CompilerDriver* const driver = context.GetCompiler();
// Can we run DEX-to-DEX compiler on this class ?
- optimizer::DexToDexCompilationLevel dex_to_dex_compilation_level =
+ optimizer::DexToDexCompiler::CompilationLevel dex_to_dex_compilation_level =
GetDexToDexCompilationLevel(soa.Self(), *driver, jclass_loader, dex_file, class_def);
ClassDataItemIterator it(dex_file, class_data);
@@ -2786,132 +2752,159 @@
bool compilation_enabled = driver->IsClassToCompile(
dex_file.StringByTypeIdx(class_def.class_idx_));
- // Compile direct methods
- int64_t previous_direct_method_idx = -1;
- while (it.HasNextDirectMethod()) {
+ // Compile direct and virtual methods.
+ int64_t previous_method_idx = -1;
+ while (it.HasNextMethod()) {
uint32_t method_idx = it.GetMemberIndex();
- if (method_idx == previous_direct_method_idx) {
+ if (method_idx == previous_method_idx) {
// smali can create dex files with two encoded_methods sharing the same method_idx
// http://code.google.com/p/smali/issues/detail?id=119
it.Next();
continue;
}
- previous_direct_method_idx = method_idx;
- CompileMethod(soa.Self(),
- driver,
- it.GetMethodCodeItem(),
- it.GetMethodAccessFlags(),
- it.GetMethodInvokeType(class_def),
- class_def_index,
- method_idx,
- class_loader,
- dex_file,
- dex_to_dex_compilation_level,
- compilation_enabled,
- dex_cache);
- it.Next();
- }
- // Compile virtual methods
- int64_t previous_virtual_method_idx = -1;
- while (it.HasNextVirtualMethod()) {
- uint32_t method_idx = it.GetMemberIndex();
- if (method_idx == previous_virtual_method_idx) {
- // smali can create dex files with two encoded_methods sharing the same method_idx
- // http://code.google.com/p/smali/issues/detail?id=119
- it.Next();
- continue;
- }
- previous_virtual_method_idx = method_idx;
- CompileMethod(soa.Self(),
- driver, it.GetMethodCodeItem(),
- it.GetMethodAccessFlags(),
- it.GetMethodInvokeType(class_def),
- class_def_index,
- method_idx,
- class_loader,
- dex_file,
- dex_to_dex_compilation_level,
- compilation_enabled,
- dex_cache);
+ previous_method_idx = method_idx;
+ compile_fn(soa.Self(),
+ driver,
+ it.GetMethodCodeItem(),
+ it.GetMethodAccessFlags(),
+ it.GetMethodInvokeType(class_def),
+ class_def_index,
+ method_idx,
+ class_loader,
+ dex_file,
+ dex_to_dex_compilation_level,
+ compilation_enabled,
+ dex_cache);
it.Next();
}
DCHECK(!it.HasNext());
+ };
+ context.ForAllLambda(0, dex_file.NumClassDefs(), compile, thread_count);
+}
+
+void CompilerDriver::Compile(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings) {
+ if (kDebugProfileGuidedCompilation) {
+ LOG(INFO) << "[ProfileGuidedCompilation] " <<
+ ((profile_compilation_info_ == nullptr)
+ ? "null"
+ : profile_compilation_info_->DumpInfo(&dex_files));
}
- private:
- const ParallelCompilationManager* const manager_;
-};
+ dex_to_dex_compiler_.ClearState();
+ for (const DexFile* dex_file : dex_files) {
+ CHECK(dex_file != nullptr);
+ CompileDexFile(this,
+ class_loader,
+ *dex_file,
+ dex_files,
+ parallel_thread_pool_.get(),
+ parallel_thread_count_,
+ timings,
+ "Compile Dex File Quick",
+ CompileMethodQuick);
+ const ArenaPool* const arena_pool = Runtime::Current()->GetArenaPool();
+ const size_t arena_alloc = arena_pool->GetBytesAllocated();
+ max_arena_alloc_ = std::max(arena_alloc, max_arena_alloc_);
+ Runtime::Current()->ReclaimArenaPoolMemory();
+ }
-void CompilerDriver::CompileDexFile(jobject class_loader,
- const DexFile& dex_file,
- const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool,
- size_t thread_count,
- TimingLogger* timings) {
- TimingLogger::ScopedTiming t("Compile Dex File", timings);
- ParallelCompilationManager context(Runtime::Current()->GetClassLinker(), class_loader, this,
- &dex_file, dex_files, thread_pool);
- CompileClassVisitor visitor(&context);
- context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count);
+ if (dex_to_dex_compiler_.NumCodeItemsToQuicken(Thread::Current()) > 0u) {
+ // TODO: Not visit all of the dex files, its probably rare that only one would have quickened
+ // methods though.
+ for (const DexFile* dex_file : dex_files) {
+ CompileDexFile(this,
+ class_loader,
+ *dex_file,
+ dex_files,
+ parallel_thread_pool_.get(),
+ parallel_thread_count_,
+ timings,
+ "Compile Dex File Dex2Dex",
+ CompileMethodDex2Dex);
+ }
+ dex_to_dex_compiler_.ClearState();
+ }
+
+ VLOG(compiler) << "Compile: " << GetMemoryUsageString(false);
}
void CompilerDriver::AddCompiledMethod(const MethodReference& method_ref,
CompiledMethod* const compiled_method,
size_t non_relative_linker_patch_count) {
- DCHECK(GetCompiledMethod(method_ref) == nullptr)
- << method_ref.dex_file->PrettyMethod(method_ref.dex_method_index);
- MethodTable::InsertResult result = compiled_methods_.Insert(
- DexFileReference(method_ref.dex_file, method_ref.dex_method_index),
- /*expected*/ nullptr,
- compiled_method);
+ DCHECK(GetCompiledMethod(method_ref) == nullptr) << method_ref.PrettyMethod();
+ MethodTable::InsertResult result = compiled_methods_.Insert(method_ref,
+ /*expected*/ nullptr,
+ compiled_method);
CHECK(result == MethodTable::kInsertResultSuccess);
non_relative_linker_patch_count_.FetchAndAddRelaxed(non_relative_linker_patch_count);
- DCHECK(GetCompiledMethod(method_ref) != nullptr)
- << method_ref.dex_file->PrettyMethod(method_ref.dex_method_index);
+ DCHECK(GetCompiledMethod(method_ref) != nullptr) << method_ref.PrettyMethod();
}
-bool CompilerDriver::GetCompiledClass(ClassReference ref, mirror::Class::Status* status) const {
+CompiledMethod* CompilerDriver::RemoveCompiledMethod(const MethodReference& method_ref) {
+ CompiledMethod* ret = nullptr;
+ CHECK(compiled_methods_.Remove(method_ref, &ret));
+ return ret;
+}
+
+bool CompilerDriver::GetCompiledClass(const ClassReference& ref, ClassStatus* status) const {
DCHECK(status != nullptr);
// The table doesn't know if something wasn't inserted. For this case it will return
- // kStatusNotReady. To handle this, just assume anything we didn't try to verify is not compiled.
- if (!compiled_classes_.Get(DexFileReference(ref.first, ref.second), status) ||
- *status < mirror::Class::kStatusRetryVerificationAtRuntime) {
+ // ClassStatus::kNotReady. To handle this, just assume anything we didn't try to verify
+ // is not compiled.
+ if (!compiled_classes_.Get(ref, status) ||
+ *status < ClassStatus::kRetryVerificationAtRuntime) {
return false;
}
return true;
}
-void CompilerDriver::RecordClassStatus(ClassReference ref, mirror::Class::Status status) {
+ClassStatus CompilerDriver::GetClassStatus(const ClassReference& ref) const {
+ ClassStatus status = ClassStatus::kNotReady;
+ if (!GetCompiledClass(ref, &status)) {
+ classpath_classes_.Get(ref, &status);
+ }
+ return status;
+}
+
+void CompilerDriver::RecordClassStatus(const ClassReference& ref, ClassStatus status) {
switch (status) {
- case mirror::Class::kStatusErrorResolved:
- case mirror::Class::kStatusErrorUnresolved:
- case mirror::Class::kStatusNotReady:
- case mirror::Class::kStatusResolved:
- case mirror::Class::kStatusRetryVerificationAtRuntime:
- case mirror::Class::kStatusVerified:
- case mirror::Class::kStatusSuperclassValidated:
- case mirror::Class::kStatusInitialized:
+ case ClassStatus::kErrorResolved:
+ case ClassStatus::kErrorUnresolved:
+ case ClassStatus::kNotReady:
+ case ClassStatus::kResolved:
+ case ClassStatus::kRetryVerificationAtRuntime:
+ case ClassStatus::kVerified:
+ case ClassStatus::kSuperclassValidated:
+ case ClassStatus::kInitialized:
break; // Expected states.
default:
LOG(FATAL) << "Unexpected class status for class "
- << PrettyDescriptor(ref.first->GetClassDescriptor(ref.first->GetClassDef(ref.second)))
+ << PrettyDescriptor(
+ ref.dex_file->GetClassDescriptor(ref.dex_file->GetClassDef(ref.index)))
<< " of " << status;
}
ClassStateTable::InsertResult result;
+ ClassStateTable* table = &compiled_classes_;
do {
- DexFileReference dex_ref(ref.first, ref.second);
- mirror::Class::Status existing = mirror::Class::kStatusNotReady;
- if (!compiled_classes_.Get(dex_ref, &existing)) {
- // Probably a uses library class, bail.
+ ClassStatus existing = ClassStatus::kNotReady;
+ if (!table->Get(ref, &existing)) {
+ // A classpath class.
if (kIsDebugBuild) {
// Check to make sure it's not a dex file for an oat file we are compiling since these
// should always succeed. These do not include classes in for used libraries.
for (const DexFile* dex_file : GetDexFilesForOatFile()) {
- CHECK_NE(dex_ref.dex_file, dex_file) << dex_ref.dex_file->GetLocation();
+ CHECK_NE(ref.dex_file, dex_file) << ref.dex_file->GetLocation();
}
}
- return;
+ if (!classpath_classes_.HaveDexFile(ref.dex_file)) {
+ // Boot classpath dex file.
+ return;
+ }
+ table = &classpath_classes_;
+ table->Get(ref, &existing);
}
if (existing >= status) {
// Existing status is already better than we expect, break.
@@ -2919,14 +2912,14 @@
}
// Update the status if we now have a greater one. This happens with vdex,
// which records a class is verified, but does not resolve it.
- result = compiled_classes_.Insert(dex_ref, existing, status);
- CHECK(result != ClassStateTable::kInsertResultInvalidDexFile);
+ result = table->Insert(ref, existing, status);
+ CHECK(result != ClassStateTable::kInsertResultInvalidDexFile) << ref.dex_file->GetLocation();
} while (result != ClassStateTable::kInsertResultSuccess);
}
CompiledMethod* CompilerDriver::GetCompiledMethod(MethodReference ref) const {
CompiledMethod* compiled_method = nullptr;
- compiled_methods_.Get(DexFileReference(ref.dex_file, ref.dex_method_index), &compiled_method);
+ compiled_methods_.Get(ref, &compiled_method);
return compiled_method;
}
@@ -3027,17 +3020,12 @@
void CompilerDriver::SetDexFilesForOatFile(const std::vector<const DexFile*>& dex_files) {
dex_files_for_oat_file_ = dex_files;
- for (const DexFile* dex_file : dex_files) {
- if (!compiled_classes_.HaveDexFile(dex_file)) {
- compiled_classes_.AddDexFile(dex_file, dex_file->NumClassDefs());
- }
- }
+ compiled_classes_.AddDexFiles(dex_files);
+ dex_to_dex_compiler_.SetDexFiles(dex_files);
}
-bool CompilerDriver::CanAssumeVerified(ClassReference ref) const {
- mirror::Class::Status existing = mirror::Class::kStatusNotReady;
- compiled_classes_.Get(DexFileReference(ref.first, ref.second), &existing);
- return existing >= mirror::Class::kStatusVerified;
+void CompilerDriver::SetClasspathDexFiles(const std::vector<const DexFile*>& dex_files) {
+ classpath_classes_.AddDexFiles(dex_files);
}
} // namespace art
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 5771b19..a5462ee 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -17,27 +17,30 @@
#ifndef ART_COMPILER_DRIVER_COMPILER_DRIVER_H_
#define ART_COMPILER_DRIVER_COMPILER_DRIVER_H_
+#include <atomic>
#include <set>
#include <string>
#include <unordered_set>
#include <vector>
+#include "android-base/strings.h"
+
#include "arch/instruction_set.h"
#include "base/array_ref.h"
#include "base/bit_utils.h"
#include "base/mutex.h"
+#include "base/os.h"
+#include "base/quasi_atomic.h"
+#include "base/safe_map.h"
#include "base/timing_logger.h"
-#include "class_reference.h"
+#include "class_status.h"
#include "compiler.h"
-#include "dex_file.h"
-#include "dex_file_types.h"
+#include "dex/class_reference.h"
+#include "dex/dex_file.h"
+#include "dex/dex_file_types.h"
+#include "dex/dex_to_dex_compiler.h"
+#include "dex/method_reference.h"
#include "driver/compiled_method_storage.h"
-#include "jit/profile_compilation_info.h"
-#include "invoke_type.h"
-#include "method_reference.h"
-#include "mirror/class.h" // For mirror::Class::Status.
-#include "os.h"
-#include "safe_map.h"
#include "thread_pool.h"
#include "utils/atomic_dex_ref_map.h"
#include "utils/dex_cache_arrays_layout.h"
@@ -45,6 +48,7 @@
namespace art {
namespace mirror {
+class Class;
class DexCache;
} // namespace mirror
@@ -53,17 +57,22 @@
class VerifierDepsTest;
} // namespace verifier
+class ArtField;
class BitVector;
class CompiledMethod;
class CompilerOptions;
class DexCompilationUnit;
+template<class T> class Handle;
struct InlineIGetIPutData;
class InstructionSetFeatures;
class InternTable;
+enum InvokeType : uint32_t;
+class MemberOffset;
+template<class MirrorType> class ObjPtr;
class ParallelCompilationManager;
+class ProfileCompilationInfo;
class ScopedObjectAccess;
template <class Allocator> class SrcMap;
-template<class T> class Handle;
class TimingLogger;
class VdexFile;
class VerificationResults;
@@ -94,18 +103,18 @@
std::unordered_set<std::string>* compiled_classes,
std::unordered_set<std::string>* compiled_methods,
size_t thread_count,
- bool dump_stats,
- bool dump_passes,
- CumulativeLogger* timer,
int swap_fd,
const ProfileCompilationInfo* profile_compilation_info);
~CompilerDriver();
- // Set dex files that will be stored in the oat file after being compiled.
+ // Set dex files associated with the oat file being compiled.
void SetDexFilesForOatFile(const std::vector<const DexFile*>& dex_files);
- // Get dex file that will be stored in the oat file after being compiled.
+ // Set dex files classpath.
+ void SetClasspathDexFiles(const std::vector<const DexFile*>& dex_files);
+
+ // Get dex files associated with the the oat file being compiled.
ArrayRef<const DexFile* const> GetDexFilesForOatFile() const {
return ArrayRef<const DexFile* const>(dex_files_for_oat_file_);
}
@@ -113,12 +122,11 @@
void CompileAll(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
TimingLogger* timings)
- REQUIRES(!Locks::mutator_lock_, !dex_to_dex_references_lock_);
+ REQUIRES(!Locks::mutator_lock_);
// Compile a single Method.
void CompileOne(Thread* self, ArtMethod* method, TimingLogger* timings)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!dex_to_dex_references_lock_);
+ REQUIRES_SHARED(Locks::mutator_lock_);
VerificationResults* GetVerificationResults() const;
@@ -149,7 +157,8 @@
std::unique_ptr<const std::vector<uint8_t>> CreateQuickResolutionTrampoline() const;
std::unique_ptr<const std::vector<uint8_t>> CreateQuickToInterpreterBridge() const;
- bool GetCompiledClass(ClassReference ref, mirror::Class::Status* status) const;
+ ClassStatus GetClassStatus(const ClassReference& ref) const;
+ bool GetCompiledClass(const ClassReference& ref, ClassStatus* status) const;
CompiledMethod* GetCompiledMethod(MethodReference ref) const;
size_t GetNonRelativeLinkerPatchCount() const;
@@ -157,6 +166,7 @@
void AddCompiledMethod(const MethodReference& method_ref,
CompiledMethod* const compiled_method,
size_t non_relative_linker_patch_count);
+ CompiledMethod* RemoveCompiledMethod(const MethodReference& method_ref);
void SetRequiresConstructorBarrier(Thread* self,
const DexFile* dex_file,
@@ -215,36 +225,33 @@
REQUIRES_SHARED(Locks::mutator_lock_);
// Resolve compiling method's class. Returns null on failure.
- mirror::Class* ResolveCompilingMethodsClass(
- const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
- Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit)
+ ObjPtr<mirror::Class> ResolveCompilingMethodsClass(const ScopedObjectAccess& soa,
+ Handle<mirror::DexCache> dex_cache,
+ Handle<mirror::ClassLoader> class_loader,
+ const DexCompilationUnit* mUnit)
REQUIRES_SHARED(Locks::mutator_lock_);
- mirror::Class* ResolveClass(
- const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
- Handle<mirror::ClassLoader> class_loader, dex::TypeIndex type_index,
- const DexCompilationUnit* mUnit)
+ ObjPtr<mirror::Class> ResolveClass(const ScopedObjectAccess& soa,
+ Handle<mirror::DexCache> dex_cache,
+ Handle<mirror::ClassLoader> class_loader,
+ dex::TypeIndex type_index,
+ const DexCompilationUnit* mUnit)
REQUIRES_SHARED(Locks::mutator_lock_);
// Resolve a field. Returns null on failure, including incompatible class change.
// NOTE: Unlike ClassLinker's ResolveField(), this method enforces is_static.
- ArtField* ResolveField(
- const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
- Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit,
- uint32_t field_idx, bool is_static)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
- // Resolve a field with a given dex file.
- ArtField* ResolveFieldWithDexFile(
- const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
- Handle<mirror::ClassLoader> class_loader, const DexFile* dex_file,
- uint32_t field_idx, bool is_static)
+ ArtField* ResolveField(const ScopedObjectAccess& soa,
+ Handle<mirror::DexCache> dex_cache,
+ Handle<mirror::ClassLoader> class_loader,
+ uint32_t field_idx,
+ bool is_static)
REQUIRES_SHARED(Locks::mutator_lock_);
// Can we fast-path an IGET/IPUT access to an instance field? If yes, compute the field offset.
- std::pair<bool, bool> IsFastInstanceField(
- mirror::DexCache* dex_cache, mirror::Class* referrer_class,
- ArtField* resolved_field, uint16_t field_idx)
+ std::pair<bool, bool> IsFastInstanceField(ObjPtr<mirror::DexCache> dex_cache,
+ ObjPtr<mirror::Class> referrer_class,
+ ArtField* resolved_field,
+ uint16_t field_idx)
REQUIRES_SHARED(Locks::mutator_lock_);
// Resolve a method. Returns null on failure, including incompatible class change.
@@ -266,9 +273,9 @@
REQUIRES(!Locks::mutator_lock_);
ArtField* ComputeInstanceFieldInfo(uint32_t field_idx,
- const DexCompilationUnit* mUnit,
- bool is_put,
- const ScopedObjectAccess& soa)
+ const DexCompilationUnit* mUnit,
+ bool is_put,
+ const ScopedObjectAccess& soa)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -295,18 +302,6 @@
return parallel_thread_count_;
}
- bool GetDumpStats() const {
- return dump_stats_;
- }
-
- bool GetDumpPasses() const {
- return dump_passes_;
- }
-
- CumulativeLogger* GetTimingsLogger() const {
- return timings_logger_;
- }
-
void SetDedupeEnabled(bool dedupe_enabled) {
compiled_method_storage_.SetDedupeEnabled(dedupe_enabled);
}
@@ -332,7 +327,7 @@
// according to the profile file.
bool ShouldVerifyClassBasedOnProfile(const DexFile& dex_file, uint16_t class_idx) const;
- void RecordClassStatus(ClassReference ref, mirror::Class::Status status);
+ void RecordClassStatus(const ClassReference& ref, ClassStatus status);
// Checks if the specified method has been verified without failures. Returns
// false if the method is not in the verification results (GetVerificationResults).
@@ -346,6 +341,9 @@
void SetHadHardVerifierFailure() {
had_hard_verifier_failure_ = true;
}
+ void AddSoftVerifierFailure() {
+ number_of_soft_verifier_failures_++;
+ }
Compiler::Kind GetCompilerKind() {
return compiler_kind_;
@@ -366,18 +364,30 @@
return true;
}
- void MarkForDexToDexCompilation(Thread* self, const MethodReference& method_ref)
- REQUIRES(!dex_to_dex_references_lock_);
-
- const BitVector* GetCurrentDexToDexMethods() const {
- return current_dex_to_dex_methods_;
- }
-
const ProfileCompilationInfo* GetProfileCompilationInfo() const {
return profile_compilation_info_;
}
- bool CanAssumeVerified(ClassReference ref) const;
+ // 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) {
+ // Look for "core.art" or "core-*.art".
+ if (android::base::EndsWith(boot_image_filename, "core.art")) {
+ return true;
+ }
+ if (!android::base::EndsWith(boot_image_filename, ".art")) {
+ return false;
+ }
+ size_t slash_pos = boot_image_filename.rfind('/');
+ if (slash_pos == std::string::npos) {
+ return android::base::StartsWith(boot_image_filename, "core-");
+ }
+ return boot_image_filename.compare(slash_pos + 1, 5u, "core-") == 0;
+ }
+
+ optimizer::DexToDexCompiler& GetDexToDexCompiler() {
+ return dex_to_dex_compiler_;
+ }
private:
void PreCompile(jobject class_loader,
@@ -445,14 +455,7 @@
void Compile(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
- TimingLogger* timings) REQUIRES(!dex_to_dex_references_lock_);
- void CompileDexFile(jobject class_loader,
- const DexFile& dex_file,
- const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool,
- size_t thread_count,
- TimingLogger* timings)
- REQUIRES(!Locks::mutator_lock_);
+ TimingLogger* timings);
bool MayInlineInternal(const DexFile* inlined_from, const DexFile* inlined_into) const;
@@ -478,10 +481,12 @@
GUARDED_BY(requires_constructor_barrier_lock_);
// All class references that this compiler has compiled. Indexed by class defs.
- using ClassStateTable = AtomicDexRefMap<mirror::Class::Status>;
+ using ClassStateTable = AtomicDexRefMap<ClassReference, ClassStatus>;
ClassStateTable compiled_classes_;
+ // All class references that are in the classpath. Indexed by class defs.
+ ClassStateTable classpath_classes_;
- typedef AtomicDexRefMap<CompiledMethod*> MethodTable;
+ typedef AtomicDexRefMap<MethodReference, CompiledMethod*> MethodTable;
private:
// All method references that this compiler has compiled.
@@ -505,6 +510,7 @@
// This option may be restricted to the boot image, depending on a flag in the implementation.
std::unique_ptr<std::unordered_set<std::string>> methods_to_compile_;
+ std::atomic<uint32_t> number_of_soft_verifier_failures_;
bool had_hard_verifier_failure_;
// A thread pool that can (potentially) run tasks in parallel.
@@ -517,11 +523,6 @@
class AOTCompilationStats;
std::unique_ptr<AOTCompilationStats> stats_;
- bool dump_stats_;
- const bool dump_passes_;
-
- CumulativeLogger* const timings_logger_;
-
typedef void (*CompilerCallbackFn)(CompilerDriver& driver);
typedef MutexLock* (*CompilerMutexLockFn)(CompilerDriver& driver);
@@ -529,7 +530,7 @@
bool support_boot_image_fixup_;
- // List of dex files that will be stored in the oat file.
+ // List of dex files associates with the oat file.
std::vector<const DexFile*> dex_files_for_oat_file_;
CompiledMethodStorage compiled_method_storage_;
@@ -539,14 +540,8 @@
size_t max_arena_alloc_;
- // Data for delaying dex-to-dex compilation.
- Mutex dex_to_dex_references_lock_;
- // In the first phase, dex_to_dex_references_ collects methods for dex-to-dex compilation.
- class DexFileMethodSet;
- std::vector<DexFileMethodSet> dex_to_dex_references_ GUARDED_BY(dex_to_dex_references_lock_);
- // In the second phase, current_dex_to_dex_methods_ points to the BitVector with method
- // indexes for dex-to-dex compilation in the current dex file.
- const BitVector* current_dex_to_dex_methods_;
+ // Compiler for dex to dex (quickening).
+ optimizer::DexToDexCompiler dex_to_dex_compiler_;
friend class CompileClassVisitor;
friend class DexToDexDecompilerTest;
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index 37e17b2..162904c 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -16,24 +16,26 @@
#include "driver/compiler_driver.h"
+#include <limits>
#include <stdint.h>
#include <stdio.h>
#include <memory>
#include "art_method-inl.h"
+#include "base/casts.h"
#include "class_linker-inl.h"
#include "common_compiler_test.h"
#include "compiler_callbacks.h"
-#include "dex_file.h"
-#include "dex_file_types.h"
+#include "dex/dex_file.h"
+#include "dex/dex_file_types.h"
#include "gc/heap.h"
+#include "handle_scope-inl.h"
+#include "jit/profile_compilation_info.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
#include "mirror/dex_cache-inl.h"
-#include "mirror/object_array-inl.h"
#include "mirror/object-inl.h"
-#include "handle_scope-inl.h"
-#include "jit/profile_compilation_info.h"
+#include "mirror/object_array-inl.h"
#include "scoped_thread_state_change-inl.h"
namespace art {
@@ -344,11 +346,11 @@
ASSERT_NE(klass, nullptr);
EXPECT_TRUE(klass->IsVerified());
- mirror::Class::Status status;
+ ClassStatus status;
bool found = compiler_driver_->GetCompiledClass(
ClassReference(&klass->GetDexFile(), klass->GetDexTypeIndex().index_), &status);
ASSERT_TRUE(found);
- EXPECT_EQ(status, mirror::Class::kStatusVerified);
+ EXPECT_EQ(status, ClassStatus::kVerified);
}
};
@@ -367,10 +369,8 @@
CheckVerifiedClass(class_loader, "LSecond;");
}
-// Test that a class of status kStatusRetryVerificationAtRuntime is indeed recorded that way in the
-// driver.
-// Test that checks that classes can be assumed as verified if unloading mode is enabled and
-// the class status is at least verified.
+// Test that a class of status ClassStatus::kRetryVerificationAtRuntime is indeed
+// recorded that way in the driver.
TEST_F(CompilerDriverVerifyTest, RetryVerifcationStatusCheckVerified) {
Thread* const self = Thread::Current();
jobject class_loader;
@@ -388,25 +388,21 @@
callbacks_->SetDoesClassUnloading(true, compiler_driver_.get());
ClassReference ref(dex_file, 0u);
// Test that the status is read from the compiler driver as expected.
- for (size_t i = mirror::Class::kStatusRetryVerificationAtRuntime;
- i < mirror::Class::kStatusMax;
- ++i) {
- const mirror::Class::Status expected_status = static_cast<mirror::Class::Status>(i);
+ static_assert(enum_cast<size_t>(ClassStatus::kLast) < std::numeric_limits<size_t>::max(),
+ "Make sure incrementing the class status does not overflow.");
+ for (size_t i = enum_cast<size_t>(ClassStatus::kRetryVerificationAtRuntime);
+ i <= enum_cast<size_t>(ClassStatus::kLast);
+ ++i) {
+ const ClassStatus expected_status = enum_cast<ClassStatus>(i);
// Skip unsupported status that are not supposed to be ever recorded.
- if (expected_status == mirror::Class::kStatusVerifyingAtRuntime ||
- expected_status == mirror::Class::kStatusInitializing) {
+ if (expected_status == ClassStatus::kVerifyingAtRuntime ||
+ expected_status == ClassStatus::kInitializing) {
continue;
}
compiler_driver_->RecordClassStatus(ref, expected_status);
- mirror::Class::Status status = {};
+ ClassStatus status = {};
ASSERT_TRUE(compiler_driver_->GetCompiledClass(ref, &status));
EXPECT_EQ(status, expected_status);
-
- // Check that we can assume verified if we are a status that is at least verified.
- if (status >= mirror::Class::kStatusVerified) {
- // Check that the class can be assumed as verified in the compiler driver.
- EXPECT_TRUE(callbacks_->CanAssumeVerified(ref)) << status;
- }
}
}
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 76f0ae9..2d82d79 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -18,6 +18,15 @@
#include <fstream>
+#include "android-base/stringprintf.h"
+
+#include "base/runtime_debug.h"
+#include "base/variant_map.h"
+#include "cmdline_parser.h"
+#include "compiler_options_map-inl.h"
+#include "runtime.h"
+#include "simple_compiler_options_map.h"
+
namespace art {
CompilerOptions::CompilerOptions()
@@ -30,6 +39,7 @@
inline_max_code_units_(kUnsetInlineMaxCodeUnits),
no_inline_from_(nullptr),
boot_image_(false),
+ core_image_(false),
app_image_(false),
top_k_profile_threshold_(kDefaultTopKProfileThreshold),
debuggable_(false),
@@ -40,12 +50,17 @@
implicit_so_checks_(true),
implicit_suspend_checks_(false),
compile_pic_(false),
+ dump_timings_(false),
+ dump_stats_(false),
verbose_methods_(),
abort_on_hard_verifier_failure_(false),
+ abort_on_soft_verifier_failure_(false),
init_failure_output_(nullptr),
dump_cfg_file_name_(""),
dump_cfg_append_(false),
force_determinism_(false),
+ deduplicate_code_(true),
+ count_hotness_in_compiled_code_(false),
register_allocation_strategy_(RegisterAllocator::kRegisterAllocatorDefault),
passes_to_run_(nullptr) {
}
@@ -55,113 +70,62 @@
// because we don't want to include the PassManagerOptions definition from the header file.
}
-void CompilerOptions::ParseHugeMethodMax(const StringPiece& option, UsageFn Usage) {
- ParseUintOption(option, "--huge-method-max", &huge_method_threshold_, Usage);
+namespace {
+
+bool kEmitRuntimeReadBarrierChecks = kIsDebugBuild &&
+ RegisterRuntimeDebugFlag(&kEmitRuntimeReadBarrierChecks);
+
+} // namespace
+
+bool CompilerOptions::EmitRunTimeChecksInDebugMode() const {
+ // Run-time checks (e.g. Marking Register checks) are only emitted in slow-debug mode.
+ return kEmitRuntimeReadBarrierChecks;
}
-void CompilerOptions::ParseLargeMethodMax(const StringPiece& option, UsageFn Usage) {
- ParseUintOption(option, "--large-method-max", &large_method_threshold_, Usage);
-}
-
-void CompilerOptions::ParseSmallMethodMax(const StringPiece& option, UsageFn Usage) {
- ParseUintOption(option, "--small-method-max", &small_method_threshold_, Usage);
-}
-
-void CompilerOptions::ParseTinyMethodMax(const StringPiece& option, UsageFn Usage) {
- ParseUintOption(option, "--tiny-method-max", &tiny_method_threshold_, Usage);
-}
-
-void CompilerOptions::ParseNumDexMethods(const StringPiece& option, UsageFn Usage) {
- ParseUintOption(option, "--num-dex-methods", &num_dex_methods_threshold_, Usage);
-}
-
-void CompilerOptions::ParseInlineMaxCodeUnits(const StringPiece& option, UsageFn Usage) {
- ParseUintOption(option, "--inline-max-code-units", &inline_max_code_units_, Usage);
-}
-
-void CompilerOptions::ParseDumpInitFailures(const StringPiece& option,
- UsageFn Usage ATTRIBUTE_UNUSED) {
- DCHECK(option.starts_with("--dump-init-failures="));
- std::string file_name = option.substr(strlen("--dump-init-failures=")).data();
- init_failure_output_.reset(new std::ofstream(file_name));
+bool CompilerOptions::ParseDumpInitFailures(const std::string& option, std::string* error_msg) {
+ init_failure_output_.reset(new std::ofstream(option));
if (init_failure_output_.get() == nullptr) {
- LOG(ERROR) << "Failed to allocate ofstream";
+ *error_msg = "Failed to construct std::ofstream";
+ return false;
} else if (init_failure_output_->fail()) {
- LOG(ERROR) << "Failed to open " << file_name << " for writing the initialization "
- << "failures.";
+ *error_msg = android::base::StringPrintf(
+ "Failed to open %s for writing the initialization failures.", option.c_str());
init_failure_output_.reset();
- }
-}
-
-void CompilerOptions::ParseRegisterAllocationStrategy(const StringPiece& option,
- UsageFn Usage) {
- DCHECK(option.starts_with("--register-allocation-strategy="));
- StringPiece choice = option.substr(strlen("--register-allocation-strategy=")).data();
- if (choice == "linear-scan") {
- register_allocation_strategy_ = RegisterAllocator::Strategy::kRegisterAllocatorLinearScan;
- } else if (choice == "graph-color") {
- register_allocation_strategy_ = RegisterAllocator::Strategy::kRegisterAllocatorGraphColor;
- } else {
- Usage("Unrecognized register allocation strategy. Try linear-scan, or graph-color.");
- }
-}
-
-bool CompilerOptions::ParseCompilerOption(const StringPiece& option, UsageFn Usage) {
- if (option.starts_with("--compiler-filter=")) {
- const char* compiler_filter_string = option.substr(strlen("--compiler-filter=")).data();
- if (!CompilerFilter::ParseCompilerFilter(compiler_filter_string, &compiler_filter_)) {
- Usage("Unknown --compiler-filter value %s", compiler_filter_string);
- }
- } else if (option == "--compile-pic") {
- compile_pic_ = true;
- } else if (option.starts_with("--huge-method-max=")) {
- ParseHugeMethodMax(option, Usage);
- } else if (option.starts_with("--large-method-max=")) {
- ParseLargeMethodMax(option, Usage);
- } else if (option.starts_with("--small-method-max=")) {
- ParseSmallMethodMax(option, Usage);
- } else if (option.starts_with("--tiny-method-max=")) {
- ParseTinyMethodMax(option, Usage);
- } else if (option.starts_with("--num-dex-methods=")) {
- ParseNumDexMethods(option, Usage);
- } else if (option.starts_with("--inline-max-code-units=")) {
- ParseInlineMaxCodeUnits(option, Usage);
- } else if (option == "--generate-debug-info" || option == "-g") {
- generate_debug_info_ = true;
- } else if (option == "--no-generate-debug-info") {
- generate_debug_info_ = false;
- } else if (option == "--generate-mini-debug-info") {
- generate_mini_debug_info_ = true;
- } else if (option == "--no-generate-mini-debug-info") {
- generate_mini_debug_info_ = false;
- } else if (option == "--generate-build-id") {
- generate_build_id_ = true;
- } else if (option == "--no-generate-build-id") {
- generate_build_id_ = false;
- } else if (option == "--debuggable") {
- debuggable_ = true;
- } else if (option.starts_with("--top-k-profile-threshold=")) {
- ParseDouble(option.data(), '=', 0.0, 100.0, &top_k_profile_threshold_, Usage);
- } else if (option == "--abort-on-hard-verifier-error") {
- abort_on_hard_verifier_failure_ = true;
- } else if (option.starts_with("--dump-init-failures=")) {
- ParseDumpInitFailures(option, Usage);
- } else if (option.starts_with("--dump-cfg=")) {
- dump_cfg_file_name_ = option.substr(strlen("--dump-cfg=")).data();
- } else if (option == "--dump-cfg-append") {
- dump_cfg_append_ = true;
- } else if (option.starts_with("--register-allocation-strategy=")) {
- ParseRegisterAllocationStrategy(option, Usage);
- } else if (option.starts_with("--verbose-methods=")) {
- // TODO: rather than switch off compiler logging, make all VLOG(compiler) messages
- // conditional on having verbose methods.
- gLogVerbosity.compiler = false;
- Split(option.substr(strlen("--verbose-methods=")).ToString(), ',', &verbose_methods_);
- } else {
- // Option not recognized.
return false;
}
return true;
}
+bool CompilerOptions::ParseRegisterAllocationStrategy(const std::string& option,
+ std::string* error_msg) {
+ if (option == "linear-scan") {
+ register_allocation_strategy_ = RegisterAllocator::Strategy::kRegisterAllocatorLinearScan;
+ } else if (option == "graph-color") {
+ register_allocation_strategy_ = RegisterAllocator::Strategy::kRegisterAllocatorGraphColor;
+ } else {
+ *error_msg = "Unrecognized register allocation strategy. Try linear-scan, or graph-color.";
+ return false;
+ }
+ return true;
+}
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wframe-larger-than="
+
+bool CompilerOptions::ParseCompilerOptions(const std::vector<std::string>& options,
+ bool ignore_unrecognized,
+ std::string* error_msg) {
+ auto parser = CreateSimpleParser(ignore_unrecognized);
+ CmdlineResult parse_result = parser.Parse(options);
+ if (!parse_result.IsSuccess()) {
+ *error_msg = parse_result.GetMessage();
+ return false;
+ }
+
+ SimpleParseArgumentMap args = parser.ReleaseArgumentsMap();
+ return ReadCompilerOptions(args, this, error_msg);
+}
+
+#pragma GCC diagnostic pop
+
} // namespace art
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 0a2d542..05d8805 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -22,15 +22,15 @@
#include <vector>
#include "base/macros.h"
+#include "base/utils.h"
#include "compiler_filter.h"
#include "globals.h"
#include "optimizing/register_allocator.h"
-#include "utils.h"
namespace art {
namespace verifier {
- class VerifierDepsTest;
+class VerifierDepsTest;
} // namespace verifier
class DexFile;
@@ -161,6 +161,9 @@
return generate_mini_debug_info_;
}
+ // Should run-time checks be emitted in debug mode?
+ bool EmitRunTimeChecksInDebugMode() const;
+
bool GetGenerateBuildId() const {
return generate_build_id_;
}
@@ -177,10 +180,19 @@
return implicit_suspend_checks_;
}
+ // Are we compiling a boot image?
bool IsBootImage() const {
return boot_image_;
}
+ // 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_;
}
@@ -214,12 +226,17 @@
bool AbortOnHardVerifierFailure() const {
return abort_on_hard_verifier_failure_;
}
+ bool AbortOnSoftVerifierFailure() const {
+ return abort_on_soft_verifier_failure_;
+ }
const std::vector<const DexFile*>* GetNoInlineFromDexFile() const {
return no_inline_from_;
}
- bool ParseCompilerOption(const StringPiece& option, UsageFn Usage);
+ bool ParseCompilerOptions(const std::vector<std::string>& options,
+ bool ignore_unrecognized,
+ std::string* error_msg);
void SetNonPic() {
compile_pic_ = false;
@@ -237,6 +254,10 @@
return force_determinism_;
}
+ bool DeduplicateCode() const {
+ return deduplicate_code_;
+ }
+
RegisterAllocator::Strategy GetRegisterAllocationStrategy() const {
return register_allocation_strategy_;
}
@@ -245,8 +266,20 @@
return passes_to_run_;
}
+ bool GetDumpTimings() const {
+ return dump_timings_;
+ }
+
+ bool GetDumpStats() const {
+ return dump_stats_;
+ }
+
+ bool CountHotnessInCompiledCode() const {
+ return count_hotness_in_compiled_code_;
+ }
+
private:
- void ParseDumpInitFailures(const StringPiece& option, UsageFn Usage);
+ bool ParseDumpInitFailures(const std::string& option, std::string* error_msg);
void ParseDumpCfgPasses(const StringPiece& option, UsageFn Usage);
void ParseInlineMaxCodeUnits(const StringPiece& option, UsageFn Usage);
void ParseNumDexMethods(const StringPiece& option, UsageFn Usage);
@@ -254,7 +287,7 @@
void ParseSmallMethodMax(const StringPiece& option, UsageFn Usage);
void ParseLargeMethodMax(const StringPiece& option, UsageFn Usage);
void ParseHugeMethodMax(const StringPiece& option, UsageFn Usage);
- void ParseRegisterAllocationStrategy(const StringPiece& option, UsageFn Usage);
+ bool ParseRegisterAllocationStrategy(const std::string& option, std::string* error_msg);
CompilerFilter::Filter compiler_filter_;
size_t huge_method_threshold_;
@@ -270,6 +303,7 @@
const std::vector<const DexFile*>* no_inline_from_;
bool boot_image_;
+ bool core_image_;
bool app_image_;
// When using a profile file only the top K% of the profiled samples will be compiled.
double top_k_profile_threshold_;
@@ -281,6 +315,8 @@
bool implicit_so_checks_;
bool implicit_suspend_checks_;
bool compile_pic_;
+ bool dump_timings_;
+ bool dump_stats_;
// Vector of methods to have verbose output enabled for.
std::vector<std::string> verbose_methods_;
@@ -288,6 +324,8 @@
// Abort compilation with an error if we find a class that fails verification with a hard
// failure.
bool abort_on_hard_verifier_failure_;
+ // Same for soft failures.
+ bool abort_on_soft_verifier_failure_;
// Log initialization of initialization failures to this stream if not null.
std::unique_ptr<std::ostream> init_failure_output_;
@@ -299,6 +337,13 @@
// outcomes.
bool force_determinism_;
+ // Whether code should be deduplicated.
+ bool deduplicate_code_;
+
+ // Whether compiled code should increment the hotness count of ArtMethod. Note that the increments
+ // won't be atomic for performance reasons, so we accept races, just like in interpreter.
+ bool count_hotness_in_compiled_code_;
+
RegisterAllocator::Strategy register_allocation_strategy_;
// If not null, specifies optimization passes which will be run instead of defaults.
@@ -314,6 +359,9 @@
friend class CommonCompilerTest;
friend class verifier::VerifierDepsTest;
+ template <class Base>
+ friend bool ReadCompilerOptions(Base& map, CompilerOptions* options, std::string* error_msg);
+
DISALLOW_COPY_AND_ASSIGN(CompilerOptions);
};
diff --git a/compiler/driver/compiler_options_map-inl.h b/compiler/driver/compiler_options_map-inl.h
new file mode 100644
index 0000000..3b18db0
--- /dev/null
+++ b/compiler/driver/compiler_options_map-inl.h
@@ -0,0 +1,189 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_INL_H_
+#define ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_INL_H_
+
+#include "compiler_options_map.h"
+
+#include <memory>
+
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+#include "android-base/stringprintf.h"
+
+#include "base/macros.h"
+#include "cmdline_parser.h"
+#include "compiler_options.h"
+
+namespace art {
+
+template <class Base>
+inline bool ReadCompilerOptions(Base& map, CompilerOptions* options, std::string* error_msg) {
+ if (map.Exists(Base::CompilerFilter)) {
+ CompilerFilter::Filter compiler_filter;
+ if (!CompilerFilter::ParseCompilerFilter(map.Get(Base::CompilerFilter)->c_str(),
+ &compiler_filter)) {
+ *error_msg = android::base::StringPrintf("Unknown --compiler-filter value %s",
+ map.Get(Base::CompilerFilter)->c_str());
+ return false;
+ }
+ options->SetCompilerFilter(compiler_filter);
+ }
+ if (map.Exists(Base::PIC)) {
+ options->compile_pic_ = true;
+ }
+ map.AssignIfExists(Base::HugeMethodMaxThreshold, &options->huge_method_threshold_);
+ map.AssignIfExists(Base::LargeMethodMaxThreshold, &options->large_method_threshold_);
+ map.AssignIfExists(Base::SmallMethodMaxThreshold, &options->small_method_threshold_);
+ map.AssignIfExists(Base::TinyMethodMaxThreshold, &options->tiny_method_threshold_);
+ map.AssignIfExists(Base::NumDexMethodsThreshold, &options->num_dex_methods_threshold_);
+ map.AssignIfExists(Base::InlineMaxCodeUnitsThreshold, &options->inline_max_code_units_);
+ map.AssignIfExists(Base::GenerateDebugInfo, &options->generate_debug_info_);
+ map.AssignIfExists(Base::GenerateMiniDebugInfo, &options->generate_mini_debug_info_);
+ map.AssignIfExists(Base::GenerateBuildID, &options->generate_build_id_);
+ if (map.Exists(Base::Debuggable)) {
+ options->debuggable_ = true;
+ }
+ map.AssignIfExists(Base::TopKProfileThreshold, &options->top_k_profile_threshold_);
+ map.AssignIfExists(Base::AbortOnHardVerifierFailure, &options->abort_on_hard_verifier_failure_);
+ map.AssignIfExists(Base::AbortOnSoftVerifierFailure, &options->abort_on_soft_verifier_failure_);
+ if (map.Exists(Base::DumpInitFailures)) {
+ if (!options->ParseDumpInitFailures(*map.Get(Base::DumpInitFailures), error_msg)) {
+ return false;
+ }
+ }
+ map.AssignIfExists(Base::DumpCFG, &options->dump_cfg_file_name_);
+ if (map.Exists(Base::DumpCFGAppend)) {
+ options->dump_cfg_append_ = true;
+ }
+ if (map.Exists(Base::RegisterAllocationStrategy)) {
+ if (!options->ParseRegisterAllocationStrategy(*map.Get(Base::DumpInitFailures), error_msg)) {
+ return false;
+ }
+ }
+ map.AssignIfExists(Base::VerboseMethods, &options->verbose_methods_);
+ options->deduplicate_code_ = map.GetOrDefault(Base::DeduplicateCode);
+ if (map.Exists(Base::CountHotnessInCompiledCode)) {
+ options->count_hotness_in_compiled_code_ = true;
+ }
+
+ if (map.Exists(Base::DumpTimings)) {
+ options->dump_timings_ = true;
+ }
+
+ if (map.Exists(Base::DumpStats)) {
+ options->dump_stats_ = true;
+ }
+
+ return true;
+}
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wframe-larger-than="
+
+template <typename Map, typename Builder>
+inline void AddCompilerOptionsArgumentParserOptions(Builder& b) {
+ b.
+ Define("--compiler-filter=_")
+ .template WithType<std::string>()
+ .IntoKey(Map::CompilerFilter)
+
+ .Define("--compile-pic")
+ .IntoKey(Map::PIC)
+
+ .Define("--huge-method-max=_")
+ .template WithType<unsigned int>()
+ .IntoKey(Map::HugeMethodMaxThreshold)
+ .Define("--large-method-max=_")
+ .template WithType<unsigned int>()
+ .IntoKey(Map::LargeMethodMaxThreshold)
+ .Define("--small-method-max=_")
+ .template WithType<unsigned int>()
+ .IntoKey(Map::SmallMethodMaxThreshold)
+ .Define("--tiny-method-max=_")
+ .template WithType<unsigned int>()
+ .IntoKey(Map::TinyMethodMaxThreshold)
+ .Define("--num-dex-methods=_")
+ .template WithType<unsigned int>()
+ .IntoKey(Map::NumDexMethodsThreshold)
+ .Define("--inline-max-code-units=_")
+ .template WithType<unsigned int>()
+ .IntoKey(Map::InlineMaxCodeUnitsThreshold)
+
+ .Define({"--generate-debug-info", "-g", "--no-generate-debug-info"})
+ .WithValues({true, true, false})
+ .IntoKey(Map::GenerateDebugInfo)
+ .Define({"--generate-mini-debug-info", "--no-generate-mini-debug-info"})
+ .WithValues({true, false})
+ .IntoKey(Map::GenerateMiniDebugInfo)
+
+ .Define({"--generate-build-id", "--no-generate-build-id"})
+ .WithValues({true, false})
+ .IntoKey(Map::GenerateBuildID)
+
+ .Define({"--deduplicate-code=_"})
+ .template WithType<bool>()
+ .WithValueMap({{"false", false}, {"true", true}})
+ .IntoKey(Map::DeduplicateCode)
+
+ .Define({"--count-hotness-in-compiled-code"})
+ .IntoKey(Map::CountHotnessInCompiledCode)
+
+ .Define({"--dump-timings"})
+ .IntoKey(Map::DumpTimings)
+
+ .Define({"--dump-stats"})
+ .IntoKey(Map::DumpStats)
+
+ .Define("--debuggable")
+ .IntoKey(Map::Debuggable)
+
+ .Define("--top-k-profile-threshold=_")
+ .template WithType<double>().WithRange(0.0, 100.0)
+ .IntoKey(Map::TopKProfileThreshold)
+
+ .Define({"--abort-on-hard-verifier-error", "--no-abort-on-hard-verifier-error"})
+ .WithValues({true, false})
+ .IntoKey(Map::AbortOnHardVerifierFailure)
+ .Define({"--abort-on-soft-verifier-error", "--no-abort-on-soft-verifier-error"})
+ .WithValues({true, false})
+ .IntoKey(Map::AbortOnSoftVerifierFailure)
+
+ .Define("--dump-init-failures=_")
+ .template WithType<std::string>()
+ .IntoKey(Map::DumpInitFailures)
+
+ .Define("--dump-cfg=_")
+ .template WithType<std::string>()
+ .IntoKey(Map::DumpCFG)
+ .Define("--dump-cfg-append")
+ .IntoKey(Map::DumpCFGAppend)
+
+ .Define("--register-allocation-strategy=_")
+ .template WithType<std::string>()
+ .IntoKey(Map::RegisterAllocationStrategy)
+
+ .Define("--verbose-methods=_")
+ .template WithType<ParseStringList<','>>()
+ .IntoKey(Map::VerboseMethods);
+}
+
+#pragma GCC diagnostic pop
+
+} // namespace art
+
+#endif // ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_INL_H_
diff --git a/compiler/driver/compiler_options_map-storage.h b/compiler/driver/compiler_options_map-storage.h
new file mode 100644
index 0000000..01f32e0
--- /dev/null
+++ b/compiler/driver/compiler_options_map-storage.h
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_STORAGE_H_
+#define ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_STORAGE_H_
+
+// Assumes:
+// * #include "compiler_options_map.h"
+// * namespace art
+//
+// Usage:
+// #define COMPILER_OPTIONS_MAP_TYPE TheTypeOfTheMap
+// #define COMPILER_OPTIONS_MAP_KEY_TYPE TheTypeOfTheMapsKey
+// #include "driver/compiler_options_map-storage.h
+
+#ifndef COMPILER_OPTIONS_MAP_TYPE
+#error "Expected COMPILER_OPTIONS_MAP_TYPE"
+#endif
+
+#ifndef COMPILER_OPTIONS_MAP_KEY_TYPE
+#error "Expected COMPILER_OPTIONS_MAP_KEY_TYPE"
+#endif
+
+#define COMPILER_OPTIONS_KEY(Type, Name, ...) \
+ template <typename Base, template <typename TV> class KeyType> \
+ const KeyType<Type> CompilerOptionsMap<Base, KeyType>::Name {__VA_ARGS__};
+#include <driver/compiler_options_map.def>
+
+template struct CompilerOptionsMap<COMPILER_OPTIONS_MAP_TYPE, COMPILER_OPTIONS_MAP_KEY_TYPE>;
+
+#undef COMPILER_OPTIONS_MAP_TYPE
+#undef COMPILER_OPTIONS_MAP_KEY_TYPE
+
+#endif // ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_STORAGE_H_
+#undef ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_STORAGE_H_ // Guard is only for cpplint
diff --git a/compiler/driver/compiler_options_map.def b/compiler/driver/compiler_options_map.def
new file mode 100644
index 0000000..acddae7
--- /dev/null
+++ b/compiler/driver/compiler_options_map.def
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+#ifndef COMPILER_OPTIONS_KEY
+#error "Please #define COMPILER_OPTIONS_KEY before #including this file"
+#define COMPILER_OPTIONS_KEY(...) // Don't display errors in this file in IDEs.
+#endif
+
+// This file defines the list of keys for CompilerOptionsMap.
+// These can be used with CompilerOptionsMap.Get/Set/etc, once that template class has been
+// instantiated.
+//
+// Column Descriptions:
+// <<Type>> <<Key Name>> (<<Default Value>>)
+//
+// Default values are only used by Map::GetOrDefault(K<T>).
+// If a default value is omitted here, T{} is used as the default value, which is
+// almost-always the value of the type as if it was memset to all 0.
+//
+// Please keep the columns aligned if possible when adding new rows.
+//
+
+// Parse-able keys from the command line.
+
+// TODO: Add type parser.
+COMPILER_OPTIONS_KEY (std::string, CompilerFilter)
+COMPILER_OPTIONS_KEY (Unit, PIC)
+COMPILER_OPTIONS_KEY (unsigned int, HugeMethodMaxThreshold)
+COMPILER_OPTIONS_KEY (unsigned int, LargeMethodMaxThreshold)
+COMPILER_OPTIONS_KEY (unsigned int, SmallMethodMaxThreshold)
+COMPILER_OPTIONS_KEY (unsigned int, TinyMethodMaxThreshold)
+COMPILER_OPTIONS_KEY (unsigned int, NumDexMethodsThreshold)
+COMPILER_OPTIONS_KEY (unsigned int, InlineMaxCodeUnitsThreshold)
+COMPILER_OPTIONS_KEY (bool, GenerateDebugInfo)
+COMPILER_OPTIONS_KEY (bool, GenerateMiniDebugInfo)
+COMPILER_OPTIONS_KEY (bool, GenerateBuildID)
+COMPILER_OPTIONS_KEY (Unit, Debuggable)
+COMPILER_OPTIONS_KEY (double, TopKProfileThreshold)
+COMPILER_OPTIONS_KEY (bool, AbortOnHardVerifierFailure)
+COMPILER_OPTIONS_KEY (bool, AbortOnSoftVerifierFailure)
+COMPILER_OPTIONS_KEY (std::string, DumpInitFailures)
+COMPILER_OPTIONS_KEY (std::string, DumpCFG)
+COMPILER_OPTIONS_KEY (Unit, DumpCFGAppend)
+// TODO: Add type parser.
+COMPILER_OPTIONS_KEY (std::string, RegisterAllocationStrategy)
+COMPILER_OPTIONS_KEY (ParseStringList<','>, VerboseMethods)
+COMPILER_OPTIONS_KEY (bool, DeduplicateCode, true)
+COMPILER_OPTIONS_KEY (Unit, CountHotnessInCompiledCode)
+COMPILER_OPTIONS_KEY (Unit, DumpTimings)
+COMPILER_OPTIONS_KEY (Unit, DumpStats)
+
+#undef COMPILER_OPTIONS_KEY
diff --git a/compiler/driver/compiler_options_map.h b/compiler/driver/compiler_options_map.h
new file mode 100644
index 0000000..b9bc8b6
--- /dev/null
+++ b/compiler/driver/compiler_options_map.h
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_H_
+#define ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_H_
+
+#include <string>
+#include <vector>
+
+#include "base/variant_map.h"
+#include "cmdline_types.h"
+
+namespace art {
+
+// Defines a type-safe heterogeneous key->value map. This is to be used as the base for
+// an extended map.
+template <typename Base, template <typename TV> class KeyType>
+struct CompilerOptionsMap : VariantMap<Base, KeyType> {
+ // Make the next many usages of Key slightly shorter to type.
+ template <typename TValue>
+ using Key = KeyType<TValue>;
+
+ // List of key declarations, shorthand for 'static const Key<T> Name'
+#define COMPILER_OPTIONS_KEY(Type, Name, ...) static const Key<Type> (Name);
+#include "compiler_options_map.def"
+};
+
+#undef DECLARE_KEY
+
+} // namespace art
+
+#endif // ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_H_
diff --git a/compiler/driver/dex_compilation_unit.cc b/compiler/driver/dex_compilation_unit.cc
index 7e8e812..c90c37d 100644
--- a/compiler/driver/dex_compilation_unit.cc
+++ b/compiler/driver/dex_compilation_unit.cc
@@ -16,8 +16,10 @@
#include "dex_compilation_unit.h"
+#include "base/utils.h"
+#include "dex/code_item_accessors-inl.h"
+#include "dex/descriptors_names.h"
#include "mirror/dex_cache.h"
-#include "utils.h"
namespace art {
@@ -38,8 +40,8 @@
dex_method_idx_(method_idx),
access_flags_(access_flags),
verified_method_(verified_method),
- dex_cache_(dex_cache) {
-}
+ dex_cache_(dex_cache),
+ code_item_accessor_(dex_file, code_item) {}
const std::string& DexCompilationUnit::GetSymbol() {
if (symbol_.empty()) {
diff --git a/compiler/driver/dex_compilation_unit.h b/compiler/driver/dex_compilation_unit.h
index 24a9a5b..c1ae3c9 100644
--- a/compiler/driver/dex_compilation_unit.h
+++ b/compiler/driver/dex_compilation_unit.h
@@ -20,7 +20,8 @@
#include <stdint.h>
#include "base/arena_object.h"
-#include "dex_file.h"
+#include "dex/code_item_accessors.h"
+#include "dex/dex_file.h"
#include "handle.h"
#include "jni.h"
@@ -112,6 +113,10 @@
return dex_cache_;
}
+ const CodeItemDataAccessor& GetCodeItemAccessor() const {
+ return code_item_accessor_;
+ }
+
private:
const Handle<mirror::ClassLoader> class_loader_;
@@ -127,6 +132,8 @@
const Handle<mirror::DexCache> dex_cache_;
+ const CodeItemDataAccessor code_item_accessor_;
+
std::string symbol_;
};
diff --git a/compiler/driver/simple_compiler_options_map.h b/compiler/driver/simple_compiler_options_map.h
new file mode 100644
index 0000000..3860da9
--- /dev/null
+++ b/compiler/driver/simple_compiler_options_map.h
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+// This file declares a completion of the CompilerOptionsMap and should be included into a
+// .cc file, only.
+
+#ifndef ART_COMPILER_DRIVER_SIMPLE_COMPILER_OPTIONS_MAP_H_
+#define ART_COMPILER_DRIVER_SIMPLE_COMPILER_OPTIONS_MAP_H_
+
+#include <memory>
+
+#include "compiler_options_map-inl.h"
+#include "base/variant_map.h"
+
+namespace art {
+
+template <typename TValue>
+struct SimpleParseArgumentMapKey : VariantMapKey<TValue> {
+ SimpleParseArgumentMapKey() {}
+ explicit SimpleParseArgumentMapKey(TValue default_value)
+ : VariantMapKey<TValue>(std::move(default_value)) {}
+ // Don't ODR-use constexpr default values, which means that Struct::Fields
+ // that are declared 'static constexpr T Name = Value' don't need to have a matching definition.
+};
+
+struct SimpleParseArgumentMap : CompilerOptionsMap<SimpleParseArgumentMap,
+ SimpleParseArgumentMapKey> {
+ // This 'using' line is necessary to inherit the variadic constructor.
+ using CompilerOptionsMap<SimpleParseArgumentMap, SimpleParseArgumentMapKey>::CompilerOptionsMap;
+};
+
+#define COMPILER_OPTIONS_MAP_TYPE SimpleParseArgumentMap
+#define COMPILER_OPTIONS_MAP_KEY_TYPE SimpleParseArgumentMapKey
+#include "compiler_options_map-storage.h"
+
+using Parser = CmdlineParser<SimpleParseArgumentMap, SimpleParseArgumentMapKey>;
+
+static inline Parser CreateSimpleParser(bool ignore_unrecognized) {
+ std::unique_ptr<Parser::Builder> parser_builder =
+ std::unique_ptr<Parser::Builder>(new Parser::Builder());
+
+ AddCompilerOptionsArgumentParserOptions<SimpleParseArgumentMap>(*parser_builder);
+
+ parser_builder->IgnoreUnrecognized(ignore_unrecognized);
+
+ return parser_builder->Build();
+}
+
+} // namespace art
+
+#endif // ART_COMPILER_DRIVER_SIMPLE_COMPILER_OPTIONS_MAP_H_
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h
deleted file mode 100644
index 2ef9fa1..0000000
--- a/compiler/elf_builder.h
+++ /dev/null
@@ -1,1026 +0,0 @@
-/*
- * Copyright (C) 2015 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_ELF_BUILDER_H_
-#define ART_COMPILER_ELF_BUILDER_H_
-
-#include <vector>
-
-#include "arch/instruction_set.h"
-#include "arch/mips/instruction_set_features_mips.h"
-#include "base/array_ref.h"
-#include "base/bit_utils.h"
-#include "base/casts.h"
-#include "base/unix_file/fd_file.h"
-#include "elf_utils.h"
-#include "leb128.h"
-#include "linker/error_delaying_output_stream.h"
-
-namespace art {
-
-// Writes ELF file.
-//
-// The basic layout of the elf file:
-// Elf_Ehdr - The ELF header.
-// Elf_Phdr[] - Program headers for the linker.
-// .note.gnu.build-id - Optional build ID section (SHA-1 digest).
-// .rodata - DEX files and oat metadata.
-// .text - Compiled code.
-// .bss - Zero-initialized writeable section.
-// .MIPS.abiflags - MIPS specific section.
-// .dynstr - Names for .dynsym.
-// .dynsym - A few oat-specific dynamic symbols.
-// .hash - Hash-table for .dynsym.
-// .dynamic - Tags which let the linker locate .dynsym.
-// .strtab - Names for .symtab.
-// .symtab - Debug symbols.
-// .eh_frame - Unwind information (CFI).
-// .eh_frame_hdr - Index of .eh_frame.
-// .debug_frame - Unwind information (CFI).
-// .debug_frame.oat_patches - Addresses for relocation.
-// .debug_info - Debug information.
-// .debug_info.oat_patches - Addresses for relocation.
-// .debug_abbrev - Decoding information for .debug_info.
-// .debug_str - Strings for .debug_info.
-// .debug_line - Line number tables.
-// .debug_line.oat_patches - Addresses for relocation.
-// .text.oat_patches - Addresses for relocation.
-// .shstrtab - Names of ELF sections.
-// Elf_Shdr[] - Section headers.
-//
-// Some section are optional (the debug sections in particular).
-//
-// We try write the section data directly into the file without much
-// in-memory buffering. This means we generally write sections based on the
-// dependency order (e.g. .dynamic points to .dynsym which points to .text).
-//
-// In the cases where we need to buffer, we write the larger section first
-// and buffer the smaller one (e.g. .strtab is bigger than .symtab).
-//
-// The debug sections are written last for easier stripping.
-//
-template <typename ElfTypes>
-class ElfBuilder FINAL {
- public:
- static constexpr size_t kMaxProgramHeaders = 16;
- // SHA-1 digest. Not using SHA_DIGEST_LENGTH from openssl/sha.h to avoid
- // spreading this header dependency for just this single constant.
- static constexpr size_t kBuildIdLen = 20;
-
- using Elf_Addr = typename ElfTypes::Addr;
- using Elf_Off = typename ElfTypes::Off;
- using Elf_Word = typename ElfTypes::Word;
- using Elf_Sword = typename ElfTypes::Sword;
- using Elf_Ehdr = typename ElfTypes::Ehdr;
- using Elf_Shdr = typename ElfTypes::Shdr;
- using Elf_Sym = typename ElfTypes::Sym;
- using Elf_Phdr = typename ElfTypes::Phdr;
- using Elf_Dyn = typename ElfTypes::Dyn;
-
- // Base class of all sections.
- class Section : public OutputStream {
- public:
- Section(ElfBuilder<ElfTypes>* owner,
- const std::string& name,
- Elf_Word type,
- Elf_Word flags,
- const Section* link,
- Elf_Word info,
- Elf_Word align,
- Elf_Word entsize)
- : OutputStream(name),
- owner_(owner),
- header_(),
- section_index_(0),
- name_(name),
- link_(link),
- started_(false),
- finished_(false),
- phdr_flags_(PF_R),
- phdr_type_(0) {
- DCHECK_GE(align, 1u);
- header_.sh_type = type;
- header_.sh_flags = flags;
- header_.sh_info = info;
- header_.sh_addralign = align;
- header_.sh_entsize = entsize;
- }
-
- // Start writing of this section.
- void Start() {
- CHECK(!started_);
- CHECK(!finished_);
- started_ = true;
- auto& sections = owner_->sections_;
- // Check that the previous section is complete.
- CHECK(sections.empty() || sections.back()->finished_);
- // The first ELF section index is 1. Index 0 is reserved for NULL.
- section_index_ = sections.size() + 1;
- // Page-align if we switch between allocated and non-allocated sections,
- // or if we change the type of allocation (e.g. executable vs non-executable).
- if (!sections.empty()) {
- if (header_.sh_flags != sections.back()->header_.sh_flags) {
- header_.sh_addralign = kPageSize;
- }
- }
- // Align file position.
- if (header_.sh_type != SHT_NOBITS) {
- header_.sh_offset = owner_->AlignFileOffset(header_.sh_addralign);
- } else {
- header_.sh_offset = 0;
- }
- // Align virtual memory address.
- if ((header_.sh_flags & SHF_ALLOC) != 0) {
- header_.sh_addr = owner_->AlignVirtualAddress(header_.sh_addralign);
- } else {
- header_.sh_addr = 0;
- }
- // Push this section on the list of written sections.
- sections.push_back(this);
- }
-
- // Finish writing of this section.
- void End() {
- CHECK(started_);
- CHECK(!finished_);
- finished_ = true;
- if (header_.sh_type == SHT_NOBITS) {
- CHECK_GT(header_.sh_size, 0u);
- } else {
- // Use the current file position to determine section size.
- off_t file_offset = owner_->stream_.Seek(0, kSeekCurrent);
- CHECK_GE(file_offset, (off_t)header_.sh_offset);
- header_.sh_size = file_offset - header_.sh_offset;
- }
- if ((header_.sh_flags & SHF_ALLOC) != 0) {
- owner_->virtual_address_ += header_.sh_size;
- }
- }
-
- // Get the location of this section in virtual memory.
- Elf_Addr GetAddress() const {
- CHECK(started_);
- return header_.sh_addr;
- }
-
- // Returns the size of the content of this section.
- Elf_Word GetSize() const {
- if (finished_) {
- return header_.sh_size;
- } else {
- CHECK(started_);
- CHECK_NE(header_.sh_type, (Elf_Word)SHT_NOBITS);
- return owner_->stream_.Seek(0, kSeekCurrent) - header_.sh_offset;
- }
- }
-
- // Write this section as "NOBITS" section. (used for the .bss section)
- // This means that the ELF file does not contain the initial data for this section
- // and it will be zero-initialized when the ELF file is loaded in the running program.
- void WriteNoBitsSection(Elf_Word size) {
- DCHECK_NE(header_.sh_flags & SHF_ALLOC, 0u);
- header_.sh_type = SHT_NOBITS;
- Start();
- header_.sh_size = size;
- End();
- }
-
- // This function always succeeds to simplify code.
- // Use builder's Good() to check the actual status.
- bool WriteFully(const void* buffer, size_t byte_count) OVERRIDE {
- CHECK(started_);
- CHECK(!finished_);
- return owner_->stream_.WriteFully(buffer, byte_count);
- }
-
- // This function always succeeds to simplify code.
- // Use builder's Good() to check the actual status.
- off_t Seek(off_t offset, Whence whence) OVERRIDE {
- // Forward the seek as-is and trust the caller to use it reasonably.
- return owner_->stream_.Seek(offset, whence);
- }
-
- // This function flushes the output and returns whether it succeeded.
- // If there was a previous failure, this does nothing and returns false, i.e. failed.
- bool Flush() OVERRIDE {
- return owner_->stream_.Flush();
- }
-
- Elf_Word GetSectionIndex() const {
- DCHECK(started_);
- DCHECK_NE(section_index_, 0u);
- return section_index_;
- }
-
- private:
- ElfBuilder<ElfTypes>* owner_;
- Elf_Shdr header_;
- Elf_Word section_index_;
- const std::string name_;
- const Section* const link_;
- bool started_;
- bool finished_;
- Elf_Word phdr_flags_;
- Elf_Word phdr_type_;
-
- friend class ElfBuilder;
-
- DISALLOW_COPY_AND_ASSIGN(Section);
- };
-
- class CachedSection : public Section {
- public:
- CachedSection(ElfBuilder<ElfTypes>* owner,
- const std::string& name,
- Elf_Word type,
- Elf_Word flags,
- const Section* link,
- Elf_Word info,
- Elf_Word align,
- Elf_Word entsize)
- : Section(owner, name, type, flags, link, info, align, entsize), cache_() { }
-
- Elf_Word Add(const void* data, size_t length) {
- Elf_Word offset = cache_.size();
- const uint8_t* d = reinterpret_cast<const uint8_t*>(data);
- cache_.insert(cache_.end(), d, d + length);
- return offset;
- }
-
- Elf_Word GetCacheSize() {
- return cache_.size();
- }
-
- void Write() {
- this->WriteFully(cache_.data(), cache_.size());
- cache_.clear();
- cache_.shrink_to_fit();
- }
-
- void WriteCachedSection() {
- this->Start();
- Write();
- this->End();
- }
-
- private:
- std::vector<uint8_t> cache_;
- };
-
- // Writer of .dynstr section.
- class CachedStringSection FINAL : public CachedSection {
- public:
- CachedStringSection(ElfBuilder<ElfTypes>* owner,
- const std::string& name,
- Elf_Word flags,
- Elf_Word align)
- : CachedSection(owner,
- name,
- SHT_STRTAB,
- flags,
- /* link */ nullptr,
- /* info */ 0,
- align,
- /* entsize */ 0) { }
-
- Elf_Word Add(const std::string& name) {
- if (CachedSection::GetCacheSize() == 0u) {
- DCHECK(name.empty());
- }
- return CachedSection::Add(name.c_str(), name.length() + 1);
- }
- };
-
- // Writer of .strtab and .shstrtab sections.
- class StringSection FINAL : public Section {
- public:
- StringSection(ElfBuilder<ElfTypes>* owner,
- const std::string& name,
- Elf_Word flags,
- Elf_Word align)
- : Section(owner,
- name,
- SHT_STRTAB,
- flags,
- /* link */ nullptr,
- /* info */ 0,
- align,
- /* entsize */ 0),
- current_offset_(0) {
- }
-
- Elf_Word Write(const std::string& name) {
- if (current_offset_ == 0) {
- DCHECK(name.empty());
- }
- Elf_Word offset = current_offset_;
- this->WriteFully(name.c_str(), name.length() + 1);
- current_offset_ += name.length() + 1;
- return offset;
- }
-
- private:
- Elf_Word current_offset_;
- };
-
- // Writer of .dynsym and .symtab sections.
- class SymbolSection FINAL : public CachedSection {
- public:
- SymbolSection(ElfBuilder<ElfTypes>* owner,
- const std::string& name,
- Elf_Word type,
- Elf_Word flags,
- Section* strtab)
- : CachedSection(owner,
- name,
- type,
- flags,
- strtab,
- /* info */ 0,
- sizeof(Elf_Off),
- sizeof(Elf_Sym)) {
- // The symbol table always has to start with NULL symbol.
- Elf_Sym null_symbol = Elf_Sym();
- CachedSection::Add(&null_symbol, sizeof(null_symbol));
- }
-
- // Buffer symbol for this section. It will be written later.
- // If the symbol's section is null, it will be considered absolute (SHN_ABS).
- // (we use this in JIT to reference code which is stored outside the debug ELF file)
- void Add(Elf_Word name,
- const Section* section,
- Elf_Addr addr,
- Elf_Word size,
- uint8_t binding,
- uint8_t type) {
- Elf_Word section_index;
- if (section != nullptr) {
- DCHECK_LE(section->GetAddress(), addr);
- DCHECK_LE(addr, section->GetAddress() + section->GetSize());
- section_index = section->GetSectionIndex();
- } else {
- section_index = static_cast<Elf_Word>(SHN_ABS);
- }
- Add(name, section_index, addr, size, binding, type);
- }
-
- void Add(Elf_Word name,
- Elf_Word section_index,
- Elf_Addr addr,
- Elf_Word size,
- uint8_t binding,
- uint8_t type) {
- Elf_Sym sym = Elf_Sym();
- sym.st_name = name;
- sym.st_value = addr;
- sym.st_size = size;
- sym.st_other = 0;
- sym.st_shndx = section_index;
- sym.st_info = (binding << 4) + (type & 0xf);
- CachedSection::Add(&sym, sizeof(sym));
- }
- };
-
- class AbiflagsSection FINAL : public Section {
- public:
- // Section with Mips abiflag info.
- static constexpr uint8_t MIPS_AFL_REG_NONE = 0; // no registers
- static constexpr uint8_t MIPS_AFL_REG_32 = 1; // 32-bit registers
- static constexpr uint8_t MIPS_AFL_REG_64 = 2; // 64-bit registers
- static constexpr uint32_t MIPS_AFL_FLAGS1_ODDSPREG = 1; // Uses odd single-prec fp regs
- static constexpr uint8_t MIPS_ABI_FP_DOUBLE = 1; // -mdouble-float
- static constexpr uint8_t MIPS_ABI_FP_XX = 5; // -mfpxx
- static constexpr uint8_t MIPS_ABI_FP_64A = 7; // -mips32r* -mfp64 -mno-odd-spreg
-
- AbiflagsSection(ElfBuilder<ElfTypes>* owner,
- const std::string& name,
- Elf_Word type,
- Elf_Word flags,
- const Section* link,
- Elf_Word info,
- Elf_Word align,
- Elf_Word entsize,
- InstructionSet isa,
- const InstructionSetFeatures* features)
- : Section(owner, name, type, flags, link, info, align, entsize) {
- if (isa == kMips || isa == kMips64) {
- bool fpu32 = false; // assume mips64 values
- uint8_t isa_rev = 6; // assume mips64 values
- if (isa == kMips) {
- // adjust for mips32 values
- fpu32 = features->AsMipsInstructionSetFeatures()->Is32BitFloatingPoint();
- isa_rev = features->AsMipsInstructionSetFeatures()->IsR6()
- ? 6
- : features->AsMipsInstructionSetFeatures()->IsMipsIsaRevGreaterThanEqual2()
- ? (fpu32 ? 2 : 5)
- : 1;
- }
- abiflags_.version = 0; // version of flags structure
- abiflags_.isa_level = (isa == kMips) ? 32 : 64;
- abiflags_.isa_rev = isa_rev;
- abiflags_.gpr_size = (isa == kMips) ? MIPS_AFL_REG_32 : MIPS_AFL_REG_64;
- abiflags_.cpr1_size = fpu32 ? MIPS_AFL_REG_32 : MIPS_AFL_REG_64;
- abiflags_.cpr2_size = MIPS_AFL_REG_NONE;
- // Set the fp_abi to MIPS_ABI_FP_64A for mips32 with 64-bit FPUs (ie: mips32 R5 and R6).
- // Otherwise set to MIPS_ABI_FP_DOUBLE.
- abiflags_.fp_abi = (isa == kMips && !fpu32) ? MIPS_ABI_FP_64A : MIPS_ABI_FP_DOUBLE;
- abiflags_.isa_ext = 0;
- abiflags_.ases = 0;
- // To keep the code simple, we are not using odd FP reg for single floats for both
- // mips32 and mips64 ART. Therefore we are not setting the MIPS_AFL_FLAGS1_ODDSPREG bit.
- abiflags_.flags1 = 0;
- abiflags_.flags2 = 0;
- }
- }
-
- Elf_Word GetSize() const {
- return sizeof(abiflags_);
- }
-
- void Write() {
- this->WriteFully(&abiflags_, sizeof(abiflags_));
- }
-
- private:
- struct {
- uint16_t version; // version of this structure
- uint8_t isa_level, isa_rev, gpr_size, cpr1_size, cpr2_size;
- uint8_t fp_abi;
- uint32_t isa_ext, ases, flags1, flags2;
- } abiflags_;
- };
-
- class BuildIdSection FINAL : public Section {
- public:
- BuildIdSection(ElfBuilder<ElfTypes>* owner,
- const std::string& name,
- Elf_Word type,
- Elf_Word flags,
- const Section* link,
- Elf_Word info,
- Elf_Word align,
- Elf_Word entsize)
- : Section(owner, name, type, flags, link, info, align, entsize),
- digest_start_(-1) {
- }
-
- void Write() {
- // The size fields are 32-bit on both 32-bit and 64-bit systems, confirmed
- // with the 64-bit linker and libbfd code. The size of name and desc must
- // be a multiple of 4 and it currently is.
- this->WriteUint32(4); // namesz.
- this->WriteUint32(kBuildIdLen); // descsz.
- this->WriteUint32(3); // type = NT_GNU_BUILD_ID.
- this->WriteFully("GNU", 4); // name.
- digest_start_ = this->Seek(0, kSeekCurrent);
- static_assert(kBuildIdLen % 4 == 0, "expecting a mutliple of 4 for build ID length");
- this->WriteFully(std::string(kBuildIdLen, '\0').c_str(), kBuildIdLen); // desc.
- }
-
- off_t GetDigestStart() {
- CHECK_GT(digest_start_, 0);
- return digest_start_;
- }
-
- private:
- bool WriteUint32(uint32_t v) {
- return this->WriteFully(&v, sizeof(v));
- }
-
- // File offset where the build ID digest starts.
- // Populated with zeros first, then updated with the actual value as the
- // very last thing in the output file creation.
- off_t digest_start_;
- };
-
- ElfBuilder(InstructionSet isa, const InstructionSetFeatures* features, OutputStream* output)
- : isa_(isa),
- features_(features),
- stream_(output),
- rodata_(this, ".rodata", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0),
- text_(this, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, nullptr, 0, kPageSize, 0),
- bss_(this, ".bss", SHT_NOBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0),
- dynstr_(this, ".dynstr", SHF_ALLOC, kPageSize),
- dynsym_(this, ".dynsym", SHT_DYNSYM, SHF_ALLOC, &dynstr_),
- hash_(this, ".hash", SHT_HASH, SHF_ALLOC, &dynsym_, 0, sizeof(Elf_Word), sizeof(Elf_Word)),
- dynamic_(this, ".dynamic", SHT_DYNAMIC, SHF_ALLOC, &dynstr_, 0, kPageSize, sizeof(Elf_Dyn)),
- eh_frame_(this, ".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0),
- eh_frame_hdr_(this, ".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, 4, 0),
- strtab_(this, ".strtab", 0, 1),
- symtab_(this, ".symtab", SHT_SYMTAB, 0, &strtab_),
- debug_frame_(this, ".debug_frame", SHT_PROGBITS, 0, nullptr, 0, sizeof(Elf_Addr), 0),
- debug_info_(this, ".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0),
- debug_line_(this, ".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0),
- shstrtab_(this, ".shstrtab", 0, 1),
- abiflags_(this, ".MIPS.abiflags", SHT_MIPS_ABIFLAGS, SHF_ALLOC, nullptr, 0, kPageSize, 0,
- isa, features),
- build_id_(this, ".note.gnu.build-id", SHT_NOTE, SHF_ALLOC, nullptr, 0, 4, 0),
- started_(false),
- write_program_headers_(false),
- loaded_size_(0u),
- virtual_address_(0) {
- text_.phdr_flags_ = PF_R | PF_X;
- bss_.phdr_flags_ = PF_R | PF_W;
- dynamic_.phdr_flags_ = PF_R | PF_W;
- dynamic_.phdr_type_ = PT_DYNAMIC;
- eh_frame_hdr_.phdr_type_ = PT_GNU_EH_FRAME;
- abiflags_.phdr_type_ = PT_MIPS_ABIFLAGS;
- build_id_.phdr_type_ = PT_NOTE;
- }
- ~ElfBuilder() {}
-
- InstructionSet GetIsa() { return isa_; }
- Section* GetRoData() { return &rodata_; }
- Section* GetText() { return &text_; }
- Section* GetBss() { return &bss_; }
- StringSection* GetStrTab() { return &strtab_; }
- SymbolSection* GetSymTab() { return &symtab_; }
- Section* GetEhFrame() { return &eh_frame_; }
- Section* GetEhFrameHdr() { return &eh_frame_hdr_; }
- Section* GetDebugFrame() { return &debug_frame_; }
- Section* GetDebugInfo() { return &debug_info_; }
- Section* GetDebugLine() { return &debug_line_; }
-
- // Encode patch locations as LEB128 list of deltas between consecutive addresses.
- // (exposed publicly for tests)
- static void EncodeOatPatches(const ArrayRef<const uintptr_t>& locations,
- std::vector<uint8_t>* buffer) {
- buffer->reserve(buffer->size() + locations.size() * 2); // guess 2 bytes per ULEB128.
- uintptr_t address = 0; // relative to start of section.
- for (uintptr_t location : locations) {
- DCHECK_GE(location, address) << "Patch locations are not in sorted order";
- EncodeUnsignedLeb128(buffer, dchecked_integral_cast<uint32_t>(location - address));
- address = location;
- }
- }
-
- void WritePatches(const char* name, const ArrayRef<const uintptr_t>& patch_locations) {
- std::vector<uint8_t> buffer;
- EncodeOatPatches(patch_locations, &buffer);
- std::unique_ptr<Section> s(new Section(this, name, SHT_OAT_PATCH, 0, nullptr, 0, 1, 0));
- s->Start();
- s->WriteFully(buffer.data(), buffer.size());
- s->End();
- other_sections_.push_back(std::move(s));
- }
-
- void WriteSection(const char* name, const std::vector<uint8_t>* buffer) {
- std::unique_ptr<Section> s(new Section(this, name, SHT_PROGBITS, 0, nullptr, 0, 1, 0));
- s->Start();
- s->WriteFully(buffer->data(), buffer->size());
- s->End();
- other_sections_.push_back(std::move(s));
- }
-
- // Reserve space for ELF header and program headers.
- // We do not know the number of headers until later, so
- // it is easiest to just reserve a fixed amount of space.
- // Program headers are required for loading by the linker.
- // It is possible to omit them for ELF files used for debugging.
- void Start(bool write_program_headers = true) {
- int size = sizeof(Elf_Ehdr);
- if (write_program_headers) {
- size += sizeof(Elf_Phdr) * kMaxProgramHeaders;
- }
- stream_.Seek(size, kSeekSet);
- started_ = true;
- virtual_address_ += size;
- write_program_headers_ = write_program_headers;
- }
-
- void End() {
- DCHECK(started_);
-
- // Note: loaded_size_ == 0 for tests that don't write .rodata, .text, .bss,
- // .dynstr, dynsym, .hash and .dynamic. These tests should not read loaded_size_.
- // TODO: Either refactor the .eh_frame creation so that it counts towards loaded_size_,
- // or remove all support for .eh_frame. (The currently unused .eh_frame counts towards
- // the virtual_address_ but we don't consider it for loaded_size_.)
- CHECK(loaded_size_ == 0 || loaded_size_ == RoundUp(virtual_address_, kPageSize))
- << loaded_size_ << " " << virtual_address_;
-
- // Write section names and finish the section headers.
- shstrtab_.Start();
- shstrtab_.Write("");
- for (auto* section : sections_) {
- section->header_.sh_name = shstrtab_.Write(section->name_);
- if (section->link_ != nullptr) {
- section->header_.sh_link = section->link_->GetSectionIndex();
- }
- }
- shstrtab_.End();
-
- // Write section headers at the end of the ELF file.
- std::vector<Elf_Shdr> shdrs;
- shdrs.reserve(1u + sections_.size());
- shdrs.push_back(Elf_Shdr()); // NULL at index 0.
- for (auto* section : sections_) {
- shdrs.push_back(section->header_);
- }
- Elf_Off section_headers_offset;
- section_headers_offset = AlignFileOffset(sizeof(Elf_Off));
- stream_.WriteFully(shdrs.data(), shdrs.size() * sizeof(shdrs[0]));
-
- // Flush everything else before writing the program headers. This should prevent
- // the OS from reordering writes, so that we don't end up with valid headers
- // and partially written data if we suddenly lose power, for example.
- stream_.Flush();
-
- // The main ELF header.
- Elf_Ehdr elf_header = MakeElfHeader(isa_, features_);
- elf_header.e_shoff = section_headers_offset;
- elf_header.e_shnum = shdrs.size();
- elf_header.e_shstrndx = shstrtab_.GetSectionIndex();
-
- // Program headers (i.e. mmap instructions).
- std::vector<Elf_Phdr> phdrs;
- if (write_program_headers_) {
- phdrs = MakeProgramHeaders();
- CHECK_LE(phdrs.size(), kMaxProgramHeaders);
- elf_header.e_phoff = sizeof(Elf_Ehdr);
- elf_header.e_phnum = phdrs.size();
- }
-
- stream_.Seek(0, kSeekSet);
- stream_.WriteFully(&elf_header, sizeof(elf_header));
- stream_.WriteFully(phdrs.data(), phdrs.size() * sizeof(phdrs[0]));
- stream_.Flush();
- }
-
- // The running program does not have access to section headers
- // and the loader is not supposed to use them either.
- // The dynamic sections therefore replicates some of the layout
- // information like the address and size of .rodata and .text.
- // It also contains other metadata like the SONAME.
- // The .dynamic section is found using the PT_DYNAMIC program header.
- void PrepareDynamicSection(const std::string& elf_file_path,
- Elf_Word rodata_size,
- Elf_Word text_size,
- Elf_Word bss_size,
- Elf_Word bss_methods_offset,
- Elf_Word bss_roots_offset) {
- std::string soname(elf_file_path);
- size_t directory_separator_pos = soname.rfind('/');
- if (directory_separator_pos != std::string::npos) {
- soname = soname.substr(directory_separator_pos + 1);
- }
-
- // Calculate addresses of .text, .bss and .dynstr.
- DCHECK_EQ(rodata_.header_.sh_addralign, static_cast<Elf_Word>(kPageSize));
- DCHECK_EQ(text_.header_.sh_addralign, static_cast<Elf_Word>(kPageSize));
- DCHECK_EQ(bss_.header_.sh_addralign, static_cast<Elf_Word>(kPageSize));
- DCHECK_EQ(dynstr_.header_.sh_addralign, static_cast<Elf_Word>(kPageSize));
- Elf_Word rodata_address = rodata_.GetAddress();
- Elf_Word text_address = RoundUp(rodata_address + rodata_size, kPageSize);
- Elf_Word bss_address = RoundUp(text_address + text_size, kPageSize);
- Elf_Word abiflags_address = RoundUp(bss_address + bss_size, kPageSize);
- Elf_Word abiflags_size = 0;
- if (isa_ == kMips || isa_ == kMips64) {
- abiflags_size = abiflags_.GetSize();
- }
- Elf_Word dynstr_address = RoundUp(abiflags_address + abiflags_size, kPageSize);
-
- // Cache .dynstr, .dynsym and .hash data.
- dynstr_.Add(""); // dynstr should start with empty string.
- Elf_Word rodata_index = rodata_.GetSectionIndex();
- Elf_Word oatdata = dynstr_.Add("oatdata");
- dynsym_.Add(oatdata, rodata_index, rodata_address, rodata_size, STB_GLOBAL, STT_OBJECT);
- if (text_size != 0u) {
- Elf_Word text_index = rodata_index + 1u;
- Elf_Word oatexec = dynstr_.Add("oatexec");
- dynsym_.Add(oatexec, text_index, text_address, text_size, STB_GLOBAL, STT_OBJECT);
- Elf_Word oatlastword = dynstr_.Add("oatlastword");
- Elf_Word oatlastword_address = text_address + text_size - 4;
- dynsym_.Add(oatlastword, text_index, oatlastword_address, 4, STB_GLOBAL, STT_OBJECT);
- } else if (rodata_size != 0) {
- // rodata_ can be size 0 for dwarf_test.
- Elf_Word oatlastword = dynstr_.Add("oatlastword");
- Elf_Word oatlastword_address = rodata_address + rodata_size - 4;
- dynsym_.Add(oatlastword, rodata_index, oatlastword_address, 4, STB_GLOBAL, STT_OBJECT);
- }
- DCHECK_LE(bss_roots_offset, bss_size);
- if (bss_size != 0u) {
- Elf_Word bss_index = rodata_index + 1u + (text_size != 0 ? 1u : 0u);
- Elf_Word oatbss = dynstr_.Add("oatbss");
- dynsym_.Add(oatbss, bss_index, bss_address, bss_roots_offset, STB_GLOBAL, STT_OBJECT);
- DCHECK_LE(bss_methods_offset, bss_roots_offset);
- DCHECK_LE(bss_roots_offset, bss_size);
- // Add a symbol marking the start of the methods part of the .bss, if not empty.
- if (bss_methods_offset != bss_roots_offset) {
- Elf_Word bss_methods_address = bss_address + bss_methods_offset;
- Elf_Word bss_methods_size = bss_roots_offset - bss_methods_offset;
- Elf_Word oatbssroots = dynstr_.Add("oatbssmethods");
- dynsym_.Add(
- oatbssroots, bss_index, bss_methods_address, bss_methods_size, STB_GLOBAL, STT_OBJECT);
- }
- // Add a symbol marking the start of the GC roots part of the .bss, if not empty.
- if (bss_roots_offset != bss_size) {
- Elf_Word bss_roots_address = bss_address + bss_roots_offset;
- Elf_Word bss_roots_size = bss_size - bss_roots_offset;
- Elf_Word oatbssroots = dynstr_.Add("oatbssroots");
- dynsym_.Add(
- oatbssroots, bss_index, bss_roots_address, bss_roots_size, STB_GLOBAL, STT_OBJECT);
- }
- Elf_Word oatbsslastword = dynstr_.Add("oatbsslastword");
- Elf_Word bsslastword_address = bss_address + bss_size - 4;
- dynsym_.Add(oatbsslastword, bss_index, bsslastword_address, 4, STB_GLOBAL, STT_OBJECT);
- }
- Elf_Word soname_offset = dynstr_.Add(soname);
-
- // We do not really need a hash-table since there is so few entries.
- // However, the hash-table is the only way the linker can actually
- // determine the number of symbols in .dynsym so it is required.
- int count = dynsym_.GetCacheSize() / sizeof(Elf_Sym); // Includes NULL.
- std::vector<Elf_Word> hash;
- hash.push_back(1); // Number of buckets.
- hash.push_back(count); // Number of chains.
- // Buckets. Having just one makes it linear search.
- hash.push_back(1); // Point to first non-NULL symbol.
- // Chains. This creates linked list of symbols.
- hash.push_back(0); // Dummy entry for the NULL symbol.
- for (int i = 1; i < count - 1; i++) {
- hash.push_back(i + 1); // Each symbol points to the next one.
- }
- hash.push_back(0); // Last symbol terminates the chain.
- hash_.Add(hash.data(), hash.size() * sizeof(hash[0]));
-
- // Calculate addresses of .dynsym, .hash and .dynamic.
- DCHECK_EQ(dynstr_.header_.sh_flags, dynsym_.header_.sh_flags);
- DCHECK_EQ(dynsym_.header_.sh_flags, hash_.header_.sh_flags);
- Elf_Word dynsym_address =
- RoundUp(dynstr_address + dynstr_.GetCacheSize(), dynsym_.header_.sh_addralign);
- Elf_Word hash_address =
- RoundUp(dynsym_address + dynsym_.GetCacheSize(), hash_.header_.sh_addralign);
- DCHECK_EQ(dynamic_.header_.sh_addralign, static_cast<Elf_Word>(kPageSize));
- Elf_Word dynamic_address = RoundUp(hash_address + dynsym_.GetCacheSize(), kPageSize);
-
- Elf_Dyn dyns[] = {
- { DT_HASH, { hash_address } },
- { DT_STRTAB, { dynstr_address } },
- { DT_SYMTAB, { dynsym_address } },
- { DT_SYMENT, { sizeof(Elf_Sym) } },
- { DT_STRSZ, { dynstr_.GetCacheSize() } },
- { DT_SONAME, { soname_offset } },
- { DT_NULL, { 0 } },
- };
- dynamic_.Add(&dyns, sizeof(dyns));
-
- loaded_size_ = RoundUp(dynamic_address + dynamic_.GetCacheSize(), kPageSize);
- }
-
- void WriteDynamicSection() {
- dynstr_.WriteCachedSection();
- dynsym_.WriteCachedSection();
- hash_.WriteCachedSection();
- dynamic_.WriteCachedSection();
-
- CHECK_EQ(loaded_size_, RoundUp(dynamic_.GetAddress() + dynamic_.GetSize(), kPageSize));
- }
-
- Elf_Word GetLoadedSize() {
- CHECK_NE(loaded_size_, 0u);
- return loaded_size_;
- }
-
- void WriteMIPSabiflagsSection() {
- abiflags_.Start();
- abiflags_.Write();
- abiflags_.End();
- }
-
- void WriteBuildIdSection() {
- build_id_.Start();
- build_id_.Write();
- build_id_.End();
- }
-
- void WriteBuildId(uint8_t build_id[kBuildIdLen]) {
- stream_.Seek(build_id_.GetDigestStart(), kSeekSet);
- stream_.WriteFully(build_id, kBuildIdLen);
- }
-
- // Returns true if all writes and seeks on the output stream succeeded.
- bool Good() {
- return stream_.Good();
- }
-
- // Returns the builder's internal stream.
- OutputStream* GetStream() {
- return &stream_;
- }
-
- off_t AlignFileOffset(size_t alignment) {
- return stream_.Seek(RoundUp(stream_.Seek(0, kSeekCurrent), alignment), kSeekSet);
- }
-
- Elf_Addr AlignVirtualAddress(size_t alignment) {
- return virtual_address_ = RoundUp(virtual_address_, alignment);
- }
-
- private:
- static Elf_Ehdr MakeElfHeader(InstructionSet isa, const InstructionSetFeatures* features) {
- Elf_Ehdr elf_header = Elf_Ehdr();
- switch (isa) {
- case kArm:
- // Fall through.
- case kThumb2: {
- elf_header.e_machine = EM_ARM;
- elf_header.e_flags = EF_ARM_EABI_VER5;
- break;
- }
- case kArm64: {
- elf_header.e_machine = EM_AARCH64;
- elf_header.e_flags = 0;
- break;
- }
- case kX86: {
- elf_header.e_machine = EM_386;
- elf_header.e_flags = 0;
- break;
- }
- case kX86_64: {
- elf_header.e_machine = EM_X86_64;
- elf_header.e_flags = 0;
- break;
- }
- case kMips: {
- elf_header.e_machine = EM_MIPS;
- elf_header.e_flags = (EF_MIPS_NOREORDER |
- EF_MIPS_PIC |
- EF_MIPS_CPIC |
- EF_MIPS_ABI_O32 |
- (features->AsMipsInstructionSetFeatures()->IsR6()
- ? EF_MIPS_ARCH_32R6
- : EF_MIPS_ARCH_32R2));
- break;
- }
- case kMips64: {
- elf_header.e_machine = EM_MIPS;
- elf_header.e_flags = (EF_MIPS_NOREORDER |
- EF_MIPS_PIC |
- EF_MIPS_CPIC |
- EF_MIPS_ARCH_64R6);
- break;
- }
- case kNone: {
- LOG(FATAL) << "No instruction set";
- break;
- }
- default: {
- LOG(FATAL) << "Unknown instruction set " << isa;
- }
- }
-
- elf_header.e_ident[EI_MAG0] = ELFMAG0;
- elf_header.e_ident[EI_MAG1] = ELFMAG1;
- elf_header.e_ident[EI_MAG2] = ELFMAG2;
- elf_header.e_ident[EI_MAG3] = ELFMAG3;
- elf_header.e_ident[EI_CLASS] = (sizeof(Elf_Addr) == sizeof(Elf32_Addr))
- ? ELFCLASS32 : ELFCLASS64;
- elf_header.e_ident[EI_DATA] = ELFDATA2LSB;
- elf_header.e_ident[EI_VERSION] = EV_CURRENT;
- elf_header.e_ident[EI_OSABI] = ELFOSABI_LINUX;
- elf_header.e_ident[EI_ABIVERSION] = 0;
- elf_header.e_type = ET_DYN;
- elf_header.e_version = 1;
- elf_header.e_entry = 0;
- elf_header.e_ehsize = sizeof(Elf_Ehdr);
- elf_header.e_phentsize = sizeof(Elf_Phdr);
- elf_header.e_shentsize = sizeof(Elf_Shdr);
- elf_header.e_phoff = sizeof(Elf_Ehdr);
- return elf_header;
- }
-
- // Create program headers based on written sections.
- std::vector<Elf_Phdr> MakeProgramHeaders() {
- CHECK(!sections_.empty());
- std::vector<Elf_Phdr> phdrs;
- {
- // The program headers must start with PT_PHDR which is used in
- // loaded process to determine the number of program headers.
- Elf_Phdr phdr = Elf_Phdr();
- phdr.p_type = PT_PHDR;
- phdr.p_flags = PF_R;
- phdr.p_offset = phdr.p_vaddr = phdr.p_paddr = sizeof(Elf_Ehdr);
- phdr.p_filesz = phdr.p_memsz = 0; // We need to fill this later.
- phdr.p_align = sizeof(Elf_Off);
- phdrs.push_back(phdr);
- // Tell the linker to mmap the start of file to memory.
- Elf_Phdr load = Elf_Phdr();
- load.p_type = PT_LOAD;
- load.p_flags = PF_R;
- load.p_offset = load.p_vaddr = load.p_paddr = 0;
- load.p_filesz = load.p_memsz = sizeof(Elf_Ehdr) + sizeof(Elf_Phdr) * kMaxProgramHeaders;
- load.p_align = kPageSize;
- phdrs.push_back(load);
- }
- // Create program headers for sections.
- for (auto* section : sections_) {
- const Elf_Shdr& shdr = section->header_;
- if ((shdr.sh_flags & SHF_ALLOC) != 0 && shdr.sh_size != 0) {
- // PT_LOAD tells the linker to mmap part of the file.
- // The linker can only mmap page-aligned sections.
- // Single PT_LOAD may contain several ELF sections.
- Elf_Phdr& prev = phdrs.back();
- Elf_Phdr load = Elf_Phdr();
- load.p_type = PT_LOAD;
- load.p_flags = section->phdr_flags_;
- load.p_offset = shdr.sh_offset;
- load.p_vaddr = load.p_paddr = shdr.sh_addr;
- load.p_filesz = (shdr.sh_type != SHT_NOBITS ? shdr.sh_size : 0u);
- load.p_memsz = shdr.sh_size;
- load.p_align = shdr.sh_addralign;
- if (prev.p_type == load.p_type &&
- prev.p_flags == load.p_flags &&
- prev.p_filesz == prev.p_memsz && // Do not merge .bss
- load.p_filesz == load.p_memsz) { // Do not merge .bss
- // Merge this PT_LOAD with the previous one.
- Elf_Word size = shdr.sh_offset + shdr.sh_size - prev.p_offset;
- prev.p_filesz = size;
- prev.p_memsz = size;
- } else {
- // If we are adding new load, it must be aligned.
- CHECK_EQ(shdr.sh_addralign, (Elf_Word)kPageSize);
- phdrs.push_back(load);
- }
- }
- }
- for (auto* section : sections_) {
- const Elf_Shdr& shdr = section->header_;
- if ((shdr.sh_flags & SHF_ALLOC) != 0 && shdr.sh_size != 0) {
- // Other PT_* types allow the program to locate interesting
- // parts of memory at runtime. They must overlap with PT_LOAD.
- if (section->phdr_type_ != 0) {
- Elf_Phdr phdr = Elf_Phdr();
- phdr.p_type = section->phdr_type_;
- phdr.p_flags = section->phdr_flags_;
- phdr.p_offset = shdr.sh_offset;
- phdr.p_vaddr = phdr.p_paddr = shdr.sh_addr;
- phdr.p_filesz = phdr.p_memsz = shdr.sh_size;
- phdr.p_align = shdr.sh_addralign;
- phdrs.push_back(phdr);
- }
- }
- }
- // Set the size of the initial PT_PHDR.
- CHECK_EQ(phdrs[0].p_type, (Elf_Word)PT_PHDR);
- phdrs[0].p_filesz = phdrs[0].p_memsz = phdrs.size() * sizeof(Elf_Phdr);
-
- return phdrs;
- }
-
- InstructionSet isa_;
- const InstructionSetFeatures* features_;
-
- ErrorDelayingOutputStream stream_;
-
- Section rodata_;
- Section text_;
- Section bss_;
- CachedStringSection dynstr_;
- SymbolSection dynsym_;
- CachedSection hash_;
- CachedSection dynamic_;
- Section eh_frame_;
- Section eh_frame_hdr_;
- StringSection strtab_;
- SymbolSection symtab_;
- Section debug_frame_;
- Section debug_info_;
- Section debug_line_;
- StringSection shstrtab_;
- AbiflagsSection abiflags_;
- BuildIdSection build_id_;
- std::vector<std::unique_ptr<Section>> other_sections_;
-
- // List of used section in the order in which they were written.
- std::vector<Section*> sections_;
-
- bool started_;
- bool write_program_headers_;
-
- // The size of the memory taken by the ELF file when loaded.
- size_t loaded_size_;
-
- // Used for allocation of virtual address space.
- Elf_Addr virtual_address_;
-
- DISALLOW_COPY_AND_ASSIGN(ElfBuilder);
-};
-
-} // namespace art
-
-#endif // ART_COMPILER_ELF_BUILDER_H_
diff --git a/compiler/elf_writer.cc b/compiler/elf_writer.cc
deleted file mode 100644
index 37e4f11..0000000
--- a/compiler/elf_writer.cc
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2012 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 "elf_writer.h"
-
-#include "base/unix_file/fd_file.h"
-#include "elf_file.h"
-
-namespace art {
-
-uintptr_t ElfWriter::GetOatDataAddress(ElfFile* elf_file) {
- uintptr_t oatdata_address = elf_file->FindSymbolAddress(SHT_DYNSYM,
- "oatdata",
- false);
- CHECK_NE(0U, oatdata_address);
- return oatdata_address;
-}
-
-void ElfWriter::GetOatElfInformation(File* file,
- size_t* oat_loaded_size,
- size_t* oat_data_offset) {
- std::string error_msg;
- std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file,
- false,
- false,
- /*low_4gb*/false,
- &error_msg));
- CHECK(elf_file.get() != nullptr) << error_msg;
-
- bool success = elf_file->GetLoadedSize(oat_loaded_size, &error_msg);
- CHECK(success) << error_msg;
- CHECK_NE(0U, *oat_loaded_size);
- *oat_data_offset = GetOatDataAddress(elf_file.get());
- CHECK_NE(0U, *oat_data_offset);
-}
-
-bool ElfWriter::Fixup(File* file, uintptr_t oat_data_begin) {
- std::string error_msg;
- std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file, true, false, /*low_4gb*/false, &error_msg));
- CHECK(elf_file.get() != nullptr) << error_msg;
-
- // Lookup "oatdata" symbol address.
- uintptr_t oatdata_address = ElfWriter::GetOatDataAddress(elf_file.get());
- uintptr_t base_address = oat_data_begin - oatdata_address;
-
- return elf_file->Fixup(base_address);
-}
-
-} // namespace art
diff --git a/compiler/elf_writer.h b/compiler/elf_writer.h
deleted file mode 100644
index a8a5bc3..0000000
--- a/compiler/elf_writer.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2012 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_ELF_WRITER_H_
-#define ART_COMPILER_ELF_WRITER_H_
-
-#include <stdint.h>
-#include <cstddef>
-#include <string>
-#include <vector>
-
-#include "base/array_ref.h"
-#include "base/macros.h"
-#include "base/mutex.h"
-#include "os.h"
-
-namespace art {
-
-class ElfFile;
-class OutputStream;
-
-namespace debug {
-struct MethodDebugInfo;
-} // namespace debug
-
-class ElfWriter {
- public:
- // Looks up information about location of oat file in elf file container.
- // Used for ImageWriter to perform memory layout.
- static void GetOatElfInformation(File* file,
- size_t* oat_loaded_size,
- size_t* oat_data_offset);
-
- // Returns runtime oat_data runtime address for an opened ElfFile.
- static uintptr_t GetOatDataAddress(ElfFile* elf_file);
-
- static bool Fixup(File* file, uintptr_t oat_data_begin);
-
- virtual ~ElfWriter() {}
-
- virtual void Start() = 0;
- virtual void PrepareDynamicSection(size_t rodata_size,
- size_t text_size,
- size_t bss_size,
- size_t bss_methods_offset,
- size_t bss_roots_offset) = 0;
- virtual void PrepareDebugInfo(const ArrayRef<const debug::MethodDebugInfo>& method_infos) = 0;
- virtual OutputStream* StartRoData() = 0;
- virtual void EndRoData(OutputStream* rodata) = 0;
- virtual OutputStream* StartText() = 0;
- virtual void EndText(OutputStream* text) = 0;
- virtual void WriteDynamicSection() = 0;
- virtual void WriteDebugInfo(const ArrayRef<const debug::MethodDebugInfo>& method_infos) = 0;
- virtual bool End() = 0;
-
- // Get the ELF writer's stream. This stream can be used for writing data directly
- // to a section after the section has been finished. When that's done, the user
- // should Seek() back to the position where the stream was before this operation.
- virtual OutputStream* GetStream() = 0;
-
- // Get the size that the loaded ELF file will occupy in memory.
- virtual size_t GetLoadedSize() = 0;
-
- protected:
- ElfWriter() = default;
-};
-
-} // namespace art
-
-#endif // ART_COMPILER_ELF_WRITER_H_
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
deleted file mode 100644
index 5d6dd2e..0000000
--- a/compiler/elf_writer_quick.cc
+++ /dev/null
@@ -1,318 +0,0 @@
-/*
- * Copyright (C) 2012 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 "elf_writer_quick.h"
-
-#include <openssl/sha.h>
-#include <unordered_map>
-#include <unordered_set>
-
-#include "base/casts.h"
-#include "base/logging.h"
-#include "compiled_method.h"
-#include "debug/elf_debug_writer.h"
-#include "debug/method_debug_info.h"
-#include "driver/compiler_options.h"
-#include "elf.h"
-#include "elf_builder.h"
-#include "elf_utils.h"
-#include "globals.h"
-#include "leb128.h"
-#include "linker/buffered_output_stream.h"
-#include "linker/file_output_stream.h"
-#include "thread-current-inl.h"
-#include "thread_pool.h"
-#include "utils.h"
-
-namespace art {
-
-// .eh_frame and .debug_frame are almost identical.
-// Except for some minor formatting differences, the main difference
-// is that .eh_frame is allocated within the running program because
-// it is used by C++ exception handling (which we do not use so we
-// can choose either). C++ compilers generally tend to use .eh_frame
-// because if they need it sometimes, they might as well always use it.
-// Let's use .debug_frame because it is easier to strip or compress.
-constexpr dwarf::CFIFormat kCFIFormat = dwarf::DW_DEBUG_FRAME_FORMAT;
-
-class DebugInfoTask : public Task {
- public:
- DebugInfoTask(InstructionSet isa,
- const InstructionSetFeatures* features,
- size_t rodata_section_size,
- size_t text_section_size,
- const ArrayRef<const debug::MethodDebugInfo>& method_infos)
- : isa_(isa),
- instruction_set_features_(features),
- rodata_section_size_(rodata_section_size),
- text_section_size_(text_section_size),
- method_infos_(method_infos) {
- }
-
- void Run(Thread*) {
- result_ = debug::MakeMiniDebugInfo(isa_,
- instruction_set_features_,
- rodata_section_size_,
- text_section_size_,
- method_infos_);
- }
-
- std::vector<uint8_t>* GetResult() {
- return &result_;
- }
-
- private:
- InstructionSet isa_;
- const InstructionSetFeatures* instruction_set_features_;
- size_t rodata_section_size_;
- size_t text_section_size_;
- const ArrayRef<const debug::MethodDebugInfo> method_infos_;
- std::vector<uint8_t> result_;
-};
-
-template <typename ElfTypes>
-class ElfWriterQuick FINAL : public ElfWriter {
- public:
- ElfWriterQuick(InstructionSet instruction_set,
- const InstructionSetFeatures* features,
- const CompilerOptions* compiler_options,
- File* elf_file);
- ~ElfWriterQuick();
-
- void Start() OVERRIDE;
- void PrepareDynamicSection(size_t rodata_size,
- size_t text_size,
- size_t bss_size,
- size_t bss_methods_offset,
- size_t bss_roots_offset) OVERRIDE;
- void PrepareDebugInfo(const ArrayRef<const debug::MethodDebugInfo>& method_infos) OVERRIDE;
- OutputStream* StartRoData() OVERRIDE;
- void EndRoData(OutputStream* rodata) OVERRIDE;
- OutputStream* StartText() OVERRIDE;
- void EndText(OutputStream* text) OVERRIDE;
- void WriteDynamicSection() OVERRIDE;
- void WriteDebugInfo(const ArrayRef<const debug::MethodDebugInfo>& method_infos) OVERRIDE;
- bool End() OVERRIDE;
-
- virtual OutputStream* GetStream() OVERRIDE;
-
- size_t GetLoadedSize() OVERRIDE;
-
- static void EncodeOatPatches(const std::vector<uintptr_t>& locations,
- std::vector<uint8_t>* buffer);
-
- private:
- const InstructionSetFeatures* instruction_set_features_;
- const CompilerOptions* const compiler_options_;
- File* const elf_file_;
- size_t rodata_size_;
- size_t text_size_;
- size_t bss_size_;
- std::unique_ptr<BufferedOutputStream> output_stream_;
- std::unique_ptr<ElfBuilder<ElfTypes>> builder_;
- std::unique_ptr<DebugInfoTask> debug_info_task_;
- std::unique_ptr<ThreadPool> debug_info_thread_pool_;
-
- void ComputeFileBuildId(uint8_t (*build_id)[ElfBuilder<ElfTypes>::kBuildIdLen]);
-
- DISALLOW_IMPLICIT_CONSTRUCTORS(ElfWriterQuick);
-};
-
-std::unique_ptr<ElfWriter> CreateElfWriterQuick(InstructionSet instruction_set,
- const InstructionSetFeatures* features,
- const CompilerOptions* compiler_options,
- File* elf_file) {
- if (Is64BitInstructionSet(instruction_set)) {
- return std::make_unique<ElfWriterQuick<ElfTypes64>>(instruction_set,
- features,
- compiler_options,
- elf_file);
- } else {
- return std::make_unique<ElfWriterQuick<ElfTypes32>>(instruction_set,
- features,
- compiler_options,
- elf_file);
- }
-}
-
-template <typename ElfTypes>
-ElfWriterQuick<ElfTypes>::ElfWriterQuick(InstructionSet instruction_set,
- const InstructionSetFeatures* features,
- const CompilerOptions* compiler_options,
- File* elf_file)
- : ElfWriter(),
- instruction_set_features_(features),
- compiler_options_(compiler_options),
- elf_file_(elf_file),
- rodata_size_(0u),
- text_size_(0u),
- bss_size_(0u),
- output_stream_(
- std::make_unique<BufferedOutputStream>(std::make_unique<FileOutputStream>(elf_file))),
- builder_(new ElfBuilder<ElfTypes>(instruction_set, features, output_stream_.get())) {}
-
-template <typename ElfTypes>
-ElfWriterQuick<ElfTypes>::~ElfWriterQuick() {}
-
-template <typename ElfTypes>
-void ElfWriterQuick<ElfTypes>::Start() {
- builder_->Start();
- if (compiler_options_->GetGenerateBuildId()) {
- builder_->WriteBuildIdSection();
- }
-}
-
-template <typename ElfTypes>
-void ElfWriterQuick<ElfTypes>::PrepareDynamicSection(size_t rodata_size,
- size_t text_size,
- size_t bss_size,
- size_t bss_methods_offset,
- size_t bss_roots_offset) {
- DCHECK_EQ(rodata_size_, 0u);
- rodata_size_ = rodata_size;
- DCHECK_EQ(text_size_, 0u);
- text_size_ = text_size;
- DCHECK_EQ(bss_size_, 0u);
- bss_size_ = bss_size;
- builder_->PrepareDynamicSection(elf_file_->GetPath(),
- rodata_size_,
- text_size_,
- bss_size_,
- bss_methods_offset,
- bss_roots_offset);
-}
-
-template <typename ElfTypes>
-OutputStream* ElfWriterQuick<ElfTypes>::StartRoData() {
- auto* rodata = builder_->GetRoData();
- rodata->Start();
- return rodata;
-}
-
-template <typename ElfTypes>
-void ElfWriterQuick<ElfTypes>::EndRoData(OutputStream* rodata) {
- CHECK_EQ(builder_->GetRoData(), rodata);
- builder_->GetRoData()->End();
-}
-
-template <typename ElfTypes>
-OutputStream* ElfWriterQuick<ElfTypes>::StartText() {
- auto* text = builder_->GetText();
- text->Start();
- return text;
-}
-
-template <typename ElfTypes>
-void ElfWriterQuick<ElfTypes>::EndText(OutputStream* text) {
- CHECK_EQ(builder_->GetText(), text);
- builder_->GetText()->End();
-}
-
-template <typename ElfTypes>
-void ElfWriterQuick<ElfTypes>::WriteDynamicSection() {
- if (bss_size_ != 0u) {
- builder_->GetBss()->WriteNoBitsSection(bss_size_);
- }
- if (builder_->GetIsa() == kMips || builder_->GetIsa() == kMips64) {
- builder_->WriteMIPSabiflagsSection();
- }
- builder_->WriteDynamicSection();
-}
-
-template <typename ElfTypes>
-void ElfWriterQuick<ElfTypes>::PrepareDebugInfo(
- const ArrayRef<const debug::MethodDebugInfo>& method_infos) {
- if (!method_infos.empty() && compiler_options_->GetGenerateMiniDebugInfo()) {
- // Prepare the mini-debug-info in background while we do other I/O.
- Thread* self = Thread::Current();
- debug_info_task_ = std::unique_ptr<DebugInfoTask>(
- new DebugInfoTask(builder_->GetIsa(),
- instruction_set_features_,
- rodata_size_,
- text_size_,
- method_infos));
- debug_info_thread_pool_ = std::unique_ptr<ThreadPool>(
- new ThreadPool("Mini-debug-info writer", 1));
- debug_info_thread_pool_->AddTask(self, debug_info_task_.get());
- debug_info_thread_pool_->StartWorkers(self);
- }
-}
-
-template <typename ElfTypes>
-void ElfWriterQuick<ElfTypes>::WriteDebugInfo(
- const ArrayRef<const debug::MethodDebugInfo>& method_infos) {
- if (!method_infos.empty()) {
- if (compiler_options_->GetGenerateDebugInfo()) {
- // Generate all the debug information we can.
- debug::WriteDebugInfo(builder_.get(), method_infos, kCFIFormat, true /* write_oat_patches */);
- }
- if (compiler_options_->GetGenerateMiniDebugInfo()) {
- // Wait for the mini-debug-info generation to finish and write it to disk.
- Thread* self = Thread::Current();
- DCHECK(debug_info_thread_pool_ != nullptr);
- debug_info_thread_pool_->Wait(self, true, false);
- builder_->WriteSection(".gnu_debugdata", debug_info_task_->GetResult());
- }
- }
-}
-
-template <typename ElfTypes>
-bool ElfWriterQuick<ElfTypes>::End() {
- builder_->End();
- if (compiler_options_->GetGenerateBuildId()) {
- uint8_t build_id[ElfBuilder<ElfTypes>::kBuildIdLen];
- ComputeFileBuildId(&build_id);
- builder_->WriteBuildId(build_id);
- }
- return builder_->Good();
-}
-
-template <typename ElfTypes>
-void ElfWriterQuick<ElfTypes>::ComputeFileBuildId(
- uint8_t (*build_id)[ElfBuilder<ElfTypes>::kBuildIdLen]) {
- constexpr int kBufSize = 8192;
- std::vector<char> buffer(kBufSize);
- int64_t offset = 0;
- SHA_CTX ctx;
- SHA1_Init(&ctx);
- while (true) {
- int64_t bytes_read = elf_file_->Read(buffer.data(), kBufSize, offset);
- CHECK_GE(bytes_read, 0);
- if (bytes_read == 0) {
- // End of file.
- break;
- }
- SHA1_Update(&ctx, buffer.data(), bytes_read);
- offset += bytes_read;
- }
- SHA1_Final(*build_id, &ctx);
-}
-
-template <typename ElfTypes>
-OutputStream* ElfWriterQuick<ElfTypes>::GetStream() {
- return builder_->GetStream();
-}
-
-template <typename ElfTypes>
-size_t ElfWriterQuick<ElfTypes>::GetLoadedSize() {
- return builder_->GetLoadedSize();
-}
-
-// Explicit instantiations
-template class ElfWriterQuick<ElfTypes32>;
-template class ElfWriterQuick<ElfTypes64>;
-
-} // namespace art
diff --git a/compiler/elf_writer_quick.h b/compiler/elf_writer_quick.h
deleted file mode 100644
index 3d5dd39..0000000
--- a/compiler/elf_writer_quick.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2012 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_ELF_WRITER_QUICK_H_
-#define ART_COMPILER_ELF_WRITER_QUICK_H_
-
-#include <memory>
-
-#include "arch/instruction_set.h"
-#include "elf_writer.h"
-#include "os.h"
-
-namespace art {
-
-class CompilerOptions;
-class InstructionSetFeatures;
-
-std::unique_ptr<ElfWriter> CreateElfWriterQuick(InstructionSet instruction_set,
- const InstructionSetFeatures* features,
- const CompilerOptions* compiler_options,
- File* elf_file);
-
-} // namespace art
-
-#endif // ART_COMPILER_ELF_WRITER_QUICK_H_
diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc
deleted file mode 100644
index fa2d78d..0000000
--- a/compiler/elf_writer_test.cc
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2011 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