diff --git a/Android.bp b/Android.bp index 295ae4c5569d1c8ba5c93267da5f1bb44b663333..197860694b785632409e2bc40f670c4dee532c86 100644 --- a/Android.bp +++ b/Android.bp @@ -20,6 +20,7 @@ art_static_dependencies = [ ] subdirs = [ + "adbconnection", "benchmark", "build", "cmdline", @@ -31,6 +32,8 @@ subdirs = [ "dexlist", "dexoptanalyzer", "disassembler", + "dt_fd_forward", + "dt_fd_forward/export", "imgdiag", "oatdump", "openjdkjvm", diff --git a/Android.mk b/Android.mk index 7081f7bec5e447e5ade6de31235c2071fe59e2b7..cb0b709107e9667ebb0d89bc03be4cf9d8c19393 100644 --- a/Android.mk +++ b/Android.mk @@ -370,6 +370,7 @@ LOCAL_REQUIRED_MODULES := \ 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 +396,7 @@ LOCAL_REQUIRED_MODULES += \ libopenjdkjvmtid \ patchoatd \ profmand \ + libadbconnectiond \ endif endif @@ -484,7 +486,7 @@ build-art-host-golem: build-art-host \ ######################################################################## # 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) +build-art-unbundled-golem: art-runtime linker oatdump $(TARGET_CORE_JARS) crash_dump ######################################################################## # Rules for building all dependencies for tests. diff --git a/CPPLINT.cfg b/CPPLINT.cfg new file mode 100644 index 0000000000000000000000000000000000000000..83288421e2626177a3a2f61a376bc3279e45f41b --- /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 8a8df361011f3c3db0c9427d8f0992f7d79e7c8f..7e492c74530214fa286b17e80d906139e9cea108 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 0000000000000000000000000000000000000000..441b706556a63e80a1b3de75e27d4473d1e60e62 --- /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 0000000000000000000000000000000000000000..2a9982a6e47313f604f182083dbe0224032daa66 --- /dev/null +++ b/adbconnection/adbconnection.cc @@ -0,0 +1,639 @@ +/* + * 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 + +#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 +#include +#include +#include + +namespace adbconnection { + +using dt_fd_forward::kListenStartMessage; +using dt_fd_forward::kListenEndMessage; +using dt_fd_forward::kAcceptMessage; +using dt_fd_forward::kCloseMessage; + +using android::base::StringPrintf; + +static constexpr int kEventfdLocked = 0; +static constexpr int kEventfdUnlocked = 1; +static constexpr int kControlSockSendTimeout = 10; + +static AdbConnectionState* gState; + +static bool IsDebuggingPossible() { + // TODO We need to do this on IsJdwpAllowed not IsDebuggable in order to support userdebug + // workloads. For now we will only allow it when we are debuggable so that testing is easier. + return art::Runtime::Current()->IsJavaDebuggable() && 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& 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), + next_ddm_id_(1) { + // 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 thr_name(env, env->NewStringUTF(kAdbConnectionThreadName)); + ScopedLocalRef 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 data(reinterpret_cast(vdata)); + 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); + + 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 thr(soa.Env(), CreateAdbConnectionThread(soa.Self())); + pthread_t pthread; + std::unique_ptr data(new CallbackData { this, soa.Env()->NewGlobalRef(thr.get()) }); + int pthread_create_result = pthread_create(&pthread, + nullptr, + &CallbackFunction, + data.get()); + if (pthread_create_result != 0) { + // 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(); +} + +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& data) { + // Get the write_event early to fail fast. + ScopedEventFdLock lk(adb_write_event_fd_); + if (adb_connection_socket_ == -1) { + LOG(WARNING) << "Not sending ddms data of type " + << StringPrintf("%c%c%c%c", + static_cast(type >> 24), + static_cast(type >> 16), + static_cast(type >> 8), + static_cast(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 + std::array pkt; + uint8_t* pkt_data = pkt.data(); + + // Write the length first. + *reinterpret_cast(pkt_data) = htonl(kDdmPacketHeaderSize + data.size()); + pkt_data += sizeof(uint32_t); + + // Write the id next; + *reinterpret_cast(pkt_data) = htonl(NextDdmId()); + pkt_data += sizeof(uint32_t); + + // next the flags. (0 for cmd packet because DDMS). + *(pkt_data++) = 0; + // Now the cmd-set + *(pkt_data++) = kJDWPDdmCmdSet; + // Now the command + *(pkt_data++) = kJDWPDdmCmd; + + // now the type. + *reinterpret_cast(pkt_data) = htonl(type); + pkt_data += sizeof(uint32_t); + + // Now the data.size() + *reinterpret_cast(pkt_data) = htonl(data.size()); + pkt_data += sizeof(uint32_t); + + static uint32_t constexpr kIovSize = 2; + struct iovec iovs[kIovSize] = { + { pkt.data(), pkt.size() }, + { const_cast(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(res) != (kDdmPacketHeaderSize + data.size())) { + PLOG(ERROR) << StringPrintf("Failed to send DDMS packet %c%c%c%c to debugger (%zd of %zu)", + static_cast(type >> 24), + static_cast(type >> 16), + static_cast(type >> 8), + static_cast(type), + res, data.size() + kDdmPacketHeaderSize); + } else { + VLOG(jdwp) << StringPrintf("sent DDMS packet %c%c%c%c to debugger %zu", + static_cast(type >> 24), + static_cast(type >> 16), + static_cast(type >> 8), + static_cast(type), + data.size() + kDdmPacketHeaderSize); + } +} + +void AdbConnectionState::SendAgentFds() { + // TODO + DCHECK(!sent_agent_fds_); + char dummy = '!'; + union { + cmsghdr cm; + char buffer[CMSG_SPACE(dt_fd_forward::FdSet::kDataLength)]; + } 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 = 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(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(CMSG_DATA(cmsg)))[0]); +} + +bool AdbConnectionState::SetupAdbConnection() { + int sleep_ms = 500; + const int sleep_max_ms = 2*1000; + char buff[sizeof(pid_t) + 1]; + + 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)); + snprintf(buff, sizeof(buff), "%04x", getpid()); + buff[sizeof(pid_t)] = 0; + + 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, buff, sizeof(pid_t), 0)); + if (ret == sizeof(pid_t)) { + LOG(INFO) << "PID " << getpid() << " send 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 { + 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_EQ(self->GetState(), art::kNative); + 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) { + 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 can handle. + { ((!agent_has_socket_ && !sent_agent_fds_) ? adb_connection_socket_ : -1), POLLIN, 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(buf); + } + if (memcmp(kListenStartMessage, buf, sizeof(kListenStartMessage)) == 0) { + agent_listening_ = true; + if (adb_connection_socket_ != -1) { + SendAgentFds(); + } + } 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; + } 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."; + SendAgentFds(); + } + } 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_) { + DCHECK(!agent_listening_); + // Load the agent now! + self->AssertNoPendingException(); + art::Runtime::Current()->AttachAgent(MakeAgentArg()); + if (self->IsExceptionPending()) { + LOG(ERROR) << "Failed to load agent " << agent_name_; + art::ScopedObjectAccess soa(self); + self->GetException()->Dump(); + self->ClearException(); + return; + } + agent_loaded_ = true; + } else if (agent_listening_ && !sent_agent_fds_) { + VLOG(jdwp) << "Sending agent fds again on data."; + SendAgentFds(); + } + } else { + VLOG(jdwp) << "Woke up poll without anything to do!"; + } + } + } +} + +std::string AdbConnectionState::MakeAgentArg() { + // TODO Get this from something user settable? + const std::string& opts = art::Runtime::Current()->GetJdwpOptions(); + return agent_name_ + "=" + opts + (opts.empty() ? "" : ",") + + "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; + 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? + gState = new AdbConnectionState(kDefaultJdwpAgentName); + CHECK(gState != nullptr); + return true; +} + +extern "C" bool ArtPlugin_Deinitialize() { + CHECK(gState != nullptr); + // Just do this a second time? + // TODO I don't think this should be needed. + gState->StopDebuggerThreads(); + delete gState; + return true; +} + +} // namespace adbconnection diff --git a/adbconnection/adbconnection.h b/adbconnection/adbconnection.h new file mode 100644 index 0000000000000000000000000000000000000000..28a5a05af305075d46edae01d1305d9250f2e2a0 --- /dev/null +++ b/adbconnection/adbconnection.h @@ -0,0 +1,155 @@ +/* + * 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 +#include +#include + +#include "android-base/unique_fd.h" + +#include "base/mutex.h" +#include "runtime_callbacks.h" + +#include +#include +#include + +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_; +}; + +struct AdbConnectionDdmCallback : public art::DdmCallback { + explicit AdbConnectionDdmCallback(AdbConnectionState* connection) : connection_(connection) {} + + void DdmPublishChunk(uint32_t type, + const art::ArrayRef& data) + REQUIRES_SHARED(art::Locks::mutator_lock_); + + private: + AdbConnectionState* connection_; +}; + +class AdbConnectionState { + public: + explicit AdbConnectionState(const std::string& agent_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& data); + + // Stops debugger threads during shutdown. + void StopDebuggerThreads(); + + private: + uint32_t NextDdmId(); + + void StartDebuggerThreads(); + + // Tell adbd about the new runtime. + bool SetupAdbConnection(); + + std::string MakeAgentArg(); + + android::base::unique_fd ReadFdFromAdb(); + + void SendAgentFds(); + + void CloseFds(); + + 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 shutting_down_; + + // True if we have loaded the agent library. + std::atomic agent_loaded_; + + // True if the dt_fd_forward transport is listening for a new communication channel. + std::atomic 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 agent_has_socket_; + + std::atomic sent_agent_fds_; + + std::atomic next_ddm_id_; + + 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/build/Android.common_build.mk b/build/Android.common_build.mk index f5a95fa0cfc41ce90940593a544db81b1ea42957..08962526ddd57814107c9bd0307ce8f4007a7248 100644 --- a/build/Android.common_build.mk +++ b/build/Android.common_build.mk @@ -49,6 +49,11 @@ endif # 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_test.mk b/build/Android.common_test.mk index 37e6d4230d9a8cf579e1a6f4248f2286bb2c68e5..0f29f2901baf0da63ce9815ca62b962784897944 100644 --- a/build/Android.common_test.mk +++ b/build/Android.common_test.mk @@ -33,13 +33,6 @@ endif # 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 +40,9 @@ ART_TEST_KNOWN_FAILING := # 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). diff --git a/build/Android.cpplint.mk b/build/Android.cpplint.mk index 247f4e347039d3870895eb167271966b2df577b8..964a4c896f6591d23f0484c74313635d606496e5 100644 --- a/build/Android.cpplint.mk +++ b/build/Android.cpplint.mk @@ -16,42 +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 -# Use `pwd` instead of $TOP for root, $TOP is always . and --root doesn't seem -# to work with a relative path (b/34787652). -ART_CPPLINT_FLAGS := --root=`pwd` +# Use upstream cpplint (toolpath from .repo/manifests/GLOBAL-PREUPLOAD.cfg). +ART_CPPLINT := external/google-styleguide/cpplint/cpplint.py + +# 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 -ART_CPPLINT_INGORED := \ - runtime/elf.h \ - openjdkjvmti/include/jvmti.h -# This: -# 1) Gets a list of all .h & .cc files in the art directory. +# 1) Get list of all .h & .cc files in the art directory. +# 2) Prepends 'art/' to each of them to make the full name. +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. -# 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_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 7769aad1df33eece4bc60957ddbb34fd23491489..230b2665e6f2158b484f824160f34e709b9e273f 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -124,6 +124,7 @@ ART_GTEST_image_space_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex ART_GTEST_oat_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 @@ -146,13 +147,13 @@ ART_GTEST_dex2oat_environment_tests_HOST_DEPS := \ $(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_assistant_test_HOST_DEPS := \ $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) @@ -161,10 +162,10 @@ ART_GTEST_oat_file_assistant_test_TARGET_DEPS := \ 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 +173,59 @@ ART_GTEST_image_space_test_TARGET_DEPS := \ $(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 +233,33 @@ ART_GTEST_dexlist_test_TARGET_DEPS := \ 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 ART_GTEST_oatdump_test_TARGET_DEPS := \ $(TARGET_CORE_IMAGE_DEFAULT_64) \ $(TARGET_CORE_IMAGE_DEFAULT_32) \ - oatdump + oatdumpd-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_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 # The path for which all the source files are relative, not actually the current directory. LOCAL_PATH := art @@ -270,6 +276,7 @@ ART_TEST_MODULES := \ art_dexoptanalyzer_tests \ art_imgdiag_tests \ art_oatdump_tests \ + art_patchoat_tests \ art_profman_tests \ art_runtime_tests \ art_runtime_compiler_tests \ @@ -608,7 +615,7 @@ define define-test-art-gtest-combination endif .PHONY: $$(rule_name) -$$(rule_name): $$(dependencies) dx d8 +$$(rule_name): $$(dependencies) dx d8-compat-dx $(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@) # Clear locally defined variables. @@ -683,6 +690,9 @@ ART_GTEST_dex2oat_image_test_DEX_DEPS := 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 := diff --git a/build/art.go b/build/art.go index 4e48d2d932d7b5b9500b84cd34800bf7dda826d1..58df11ca5eec353107a57dd82dee393ec9133045 100644 --- a/build/art.go +++ b/build/art.go @@ -46,10 +46,6 @@ func globalFlags(ctx android.BaseContext) ([]string, []string) { 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) @@ -70,6 +66,9 @@ func globalFlags(ctx android.BaseContext) ([]string, []string) { "-DART_READ_BARRIER_TYPE_IS_"+barrierType+"=1") } + cdexLevel := envDefault(ctx, "ART_DEFAULT_COMPACT_DEX_LEVEL", "none") + 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 @@ -104,6 +103,10 @@ func globalFlags(ctx android.BaseContext) ([]string, []string) { asflags = append(asflags, "-DART_MIPS32_CHECK_ALIGNMENT") } + if envTrue(ctx, "USE_D8_DESUGAR") { + cflags = append(cflags, "-DUSE_D8_DESUGAR=1") + } + return cflags, asflags } diff --git a/cmdline/cmdline_parser.h b/cmdline/cmdline_parser.h index fef39ad93097b8d87266bd6444879e146609e7cf..82c04e70f5e21361183a121945cf7f6a7c218261 100644 --- a/cmdline/cmdline_parser.h +++ b/cmdline/cmdline_parser.h @@ -340,7 +340,7 @@ struct CmdlineParser { typename std::enable_if::value>::type InitializeTypedBuilder(ArgumentBuilder* arg_builder) { // Every Unit argument implicitly maps to a runtime value of Unit{} - std::vector values(names_.size(), Unit{}); // NOLINT [whitespace/braces] [5] + std::vector 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 34aea55f5b2806c6d28cdc9b58f0197e02acdb87..5d672061dfbd83cf35fb371c5668cd50a171085a 100644 --- a/cmdline/cmdline_parser_test.cc +++ b/cmdline/cmdline_parser_test.cc @@ -20,6 +20,7 @@ #include "gtest/gtest.h" +#include "jdwp_provider.h" #include "experimental_flags.h" #include "parsed_options.h" #include "runtime.h" @@ -190,7 +191,7 @@ class CmdlineParserTest : public ::testing::Test { #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) @@ -244,7 +245,7 @@ TEST_F(CmdlineParserTest, TestLogVerbosity) { { 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; @@ -261,6 +262,7 @@ TEST_F(CmdlineParserTest, TestLogVerbosity) { 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); } @@ -318,7 +320,7 @@ TEST_F(CmdlineParserTest, DISABLED_TestXGcOption) { * 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; @@ -335,7 +337,7 @@ TEST_F(CmdlineParserTest, DISABLED_TestXGcOption) { 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; @@ -350,7 +352,7 @@ TEST_F(CmdlineParserTest, DISABLED_TestXGcOption) { 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); @@ -363,48 +365,40 @@ TEST_F(CmdlineParserTest, DISABLED_TestXGcOption) { } // 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; + EXPECT_SINGLE_PARSE_DEFAULT_VALUE(JdwpProvider::kInternal, "", M::JdwpProvider); + } +} // TEST_F - const char *opt_args = "-Xrunjdwp:transport=dt_socket,address=8000,server=y"; +TEST_F(CmdlineParserTest, TestJdwpProviderDefault) { + const char* opt_args = "-XjdwpProvider:default"; + EXPECT_SINGLE_PARSE_VALUE(JdwpProvider::kInternal, opt_args, M::JdwpProvider); +} // TEST_F - EXPECT_SINGLE_PARSE_VALUE(opt, opt_args, M::JdwpOptions); - } +TEST_F(CmdlineParserTest, TestJdwpProviderInternal) { + const char* opt_args = "-XjdwpProvider:internal"; + EXPECT_SINGLE_PARSE_VALUE(JdwpProvider::kInternal, opt_args, 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; - - const char *opt_args = "-agentlib:jdwp=transport=dt_socket,address=localhost:6500,server=n"; - - 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 /* @@ -566,10 +560,10 @@ TEST_F(CmdlineParserTest, MultipleArguments) { 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_types.h b/cmdline/cmdline_types.h index 87bf1c4d431ac1274bb42c0d5ee493af2f359905..529fe2bcdbaaa8e59b6d8097363f086aeb93fd5b 100644 --- a/cmdline/cmdline_types.h +++ b/cmdline/cmdline_types.h @@ -34,6 +34,7 @@ #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" @@ -57,130 +58,37 @@ template <> struct CmdlineType : CmdlineTypeParser { 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 : CmdlineTypeParser { +struct CmdlineType : CmdlineTypeParser { /* - * 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=: connect to debugger when exception thrown - * onuncaught=y|n: connect to debugger when uncaught exception thrown - * launch=: 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 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 or : */ - 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" + " (currently internal)\n"); + } else if (option == "internal" || option == "default") { + 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 @@ -532,7 +440,7 @@ struct XGcOption { template <> struct CmdlineType : CmdlineTypeParser { Result Parse(const std::string& option) { // -Xgc: already stripped - XGcOption xgc{}; // NOLINT [readability/braces] [4] + XGcOption xgc{}; std::vector gc_options; Split(option, ',', &gc_options); @@ -669,6 +577,8 @@ struct CmdlineType : CmdlineTypeParser { 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") { diff --git a/compiler/Android.bp b/compiler/Android.bp index 859947108e14b46cde069c19424c0bf7d94dfbb5..164f9c1e8f1ab0fc35f2d4a9e3abd5da6e944f99 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -181,15 +181,10 @@ art_cc_defaults { ], }, }, - target: { - android: { - // For atrace. - shared_libs: ["libcutils"], - }, - }, generated_sources: ["art_compiler_operator_srcs"], shared_libs: [ "libbase", + "libcutils", // for atrace. "liblzma", ], include_dirs: ["art/disassembler"], @@ -211,6 +206,7 @@ gensrcs { "driver/compiler_options.h", "linker/linker_patch.h", "optimizing/locations.h", + "optimizing/optimizing_compiler_stats.h", "utils/arm/constants_arm.h", "utils/mips/assembler_mips.h", @@ -320,9 +316,9 @@ art_cc_test { "exception_test.cc", "jni/jni_compiler_test.cc", "linker/linker_patch_test.cc", - "linker/method_bss_mapping_encoder_test.cc", "linker/output_stream_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", diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc index 500fc4ae9a112257c4c21143fc32e217922ffd10..40a5370ec727245faa47e22d5d49d9739f0d220e 100644 --- a/compiler/common_compiler_test.cc +++ b/compiler/common_compiler_test.cc @@ -174,7 +174,6 @@ void CommonCompilerTest::SetUp() { } } - timer_.reset(new CumulativeLogger("Compilation times")); CreateCompilerDriver(compiler_kind_, instruction_set); } } @@ -193,9 +192,6 @@ void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind, 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 +223,6 @@ InstructionSet CommonCompilerTest::GetInstructionSet() const { } 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 bcda41a9b826ad27d2930922682926d05aa91a56..05fdc97e07246d27eaa7edb352800c462e0682f7 100644 --- a/compiler/common_compiler_test.h +++ b/compiler/common_compiler_test.h @@ -106,7 +106,6 @@ class CommonCompilerTest : public CommonRuntimeTest { std::unique_ptr compiler_options_; std::unique_ptr verification_results_; std::unique_ptr compiler_driver_; - std::unique_ptr timer_; std::unique_ptr instruction_set_features_; diff --git a/compiler/compiled_method.cc b/compiler/compiled_method.cc index fc6a717aa6f5229ba82df225f068b9e5fd10f7d2..e41371855ddfafdb37f314d77aebf3d3580cab40 100644 --- a/compiler/compiled_method.cc +++ b/compiler/compiled_method.cc @@ -26,8 +26,8 @@ CompiledCode::CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set, const ArrayRef& 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() { @@ -48,7 +48,7 @@ bool CompiledCode::operator==(const CompiledCode& rhs) const { } 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) { @@ -56,7 +56,7 @@ size_t CompiledCode::AlignCode(size_t offset, InstructionSet instruction_set) { } size_t CompiledCode::CodeDelta() const { - return CodeDelta(instruction_set_); + return CodeDelta(GetInstructionSet()); } size_t CompiledCode::CodeDelta(InstructionSet instruction_set) { diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h index 892bc592db080000d888e4b4cebfdc02718df9d1..acdce260e511f992d39b673d176d103be82c05c0 100644 --- a/compiler/compiled_method.h +++ b/compiler/compiled_method.h @@ -22,6 +22,8 @@ #include #include "arch/instruction_set.h" +#include "base/bit_field.h" +#include "base/bit_utils.h" namespace art { @@ -44,7 +46,7 @@ class CompiledCode { virtual ~CompiledCode(); InstructionSet GetInstructionSet() const { - return instruction_set_; + return GetPackedField(); } ArrayRef GetQuickCode() const; @@ -68,6 +70,11 @@ class CompiledCode { static const void* CodePointer(const void* code_pointer, InstructionSet instruction_set); protected: + static constexpr size_t kInstructionSetFieldSize = + MinimumBitsToStore(static_cast(InstructionSet::kLast)); + static constexpr size_t kNumberOfCompiledCodePackedBits = kInstructionSetFieldSize; + static constexpr size_t kMaxNumberOfPackedBits = sizeof(uint32_t) * kBitsPerByte; + template static ArrayRef GetArray(const LengthPrefixedArray* array); @@ -75,13 +82,26 @@ class CompiledCode { return compiler_driver_; } + template + typename BitFieldType::value_type GetPackedField() const { + return BitFieldType::Decode(packed_fields_); + } + + template + void SetPackedField(typename BitFieldType::value_type value) { + DCHECK(IsUint(static_cast(value))); + packed_fields_ = BitFieldType::Update(value, packed_fields_); + } + private: - CompilerDriver* const compiler_driver_; + using InstructionSetField = BitField; - const InstructionSet instruction_set_; + CompilerDriver* const compiler_driver_; - // Used to store the PIC code for Quick. + // Used to store the compiled code. const LengthPrefixedArray* const quick_code_; + + uint32_t packed_fields_; }; class CompiledMethod FINAL : public CompiledCode { @@ -116,6 +136,18 @@ class CompiledMethod FINAL : public CompiledCode { static void ReleaseSwapAllocatedCompiledMethod(CompilerDriver* driver, CompiledMethod* m); + bool IsIntrinsic() const { + return GetPackedField(); + } + + // 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(/* value */ true); + } + size_t GetFrameSizeInBytes() const { return frame_size_in_bytes_; } @@ -137,6 +169,14 @@ class CompiledMethod FINAL : public CompiledCode { ArrayRef 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; + // 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. diff --git a/compiler/compiler.cc b/compiler/compiler.cc index c500921ab3f89b42987769b7cd2d2b2abec25347..47f44ff3bcacc0b8b19654295d12f6a4ff952dd3 100644 --- a/compiler/compiler.cc +++ b/compiler/compiler.cc @@ -16,7 +16,10 @@ #include "compiler.h" -#include "base/logging.h" +#include + +#include "base/macros.h" +#include "code_item_accessors-inl.h" #include "driver/compiler_driver.h" #include "optimizing/optimizing_compiler.h" #include "utils.h" @@ -44,15 +47,16 @@ bool Compiler::IsPathologicalCase(const DexFile::CodeItem& code_item, * 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 cfed6d5a8e1d104471f7e04fb3e7f58d0016f0ed..85abd6654c22c5fab5070b8bf1f33ce3c0056f40 100644 --- a/compiler/compiler.h +++ b/compiler/compiler.h @@ -46,12 +46,6 @@ class Compiler { kOptimizing }; - enum JniOptimizationFlags { - kNone = 0x0, - kFastNative = 0x1, - kCriticalNative = 0x2, - }; - static Compiler* Create(CompilerDriver* driver, Kind kind); virtual void Init() = 0; @@ -72,7 +66,7 @@ class Compiler { virtual CompiledMethod* JniCompile(uint32_t access_flags, uint32_t method_idx, const DexFile& dex_file, - JniOptimizationFlags optimization_flags) const = 0; + Handle dex_cache) const = 0; virtual bool JitCompile(Thread* self ATTRIBUTE_UNUSED, jit::JitCodeCache* code_cache ATTRIBUTE_UNUSED, diff --git a/compiler/debug/dwarf/dwarf_test.cc b/compiler/debug/dwarf/dwarf_test.cc index 866bf4394d01884bc5c1d7f091f88db59e4ce3af..933034f5934869839fd85273e236dde2c5cdb609 100644 --- a/compiler/debug/dwarf/dwarf_test.cc +++ b/compiler/debug/dwarf/dwarf_test.cc @@ -125,7 +125,7 @@ TEST_F(DwarfTest, DebugFrame) { WriteCIE(is64bit, Reg(is64bit ? 16 : 8), initial_opcodes, kCFIFormat, &debug_frame_data_); std::vector debug_frame_patches; - std::vector expected_patches { 28 }; // NOLINT + std::vector expected_patches = { 28 }; WriteFDE(is64bit, 0, 0, 0x01000000, 0x01000000, ArrayRef(*opcodes.data()), kCFIFormat, 0, &debug_frame_data_, &debug_frame_patches); @@ -140,7 +140,7 @@ TEST_F(DwarfTest, DebugFrame64) { initial_opcodes, kCFIFormat, &debug_frame_data_); DebugFrameOpCodeWriter<> opcodes; std::vector debug_frame_patches; - std::vector expected_patches { 32 }; // NOLINT + std::vector expected_patches = { 32 }; WriteFDE(is64bit, 0, 0, 0x0100000000000000, 0x0200000000000000, ArrayRef(*opcodes.data()), kCFIFormat, 0, &debug_frame_data_, &debug_frame_patches); @@ -237,7 +237,7 @@ TEST_F(DwarfTest, DebugLine) { DW_CHECK_NEXT("1\t0\t1000\t2000\tfile.c"); std::vector debug_line_patches; - std::vector expected_patches { 87 }; // NOLINT + std::vector expected_patches = { 87 }; WriteDebugLineTable(include_directories, files, opcodes, 0, &debug_line_data_, &debug_line_patches); @@ -275,7 +275,7 @@ TEST_F(DwarfTest, DebugLineSpecialOpcodes) { EXPECT_LT(opcodes.data()->size(), num_rows * 3); std::vector directories; - std::vector files { { "file.c", 0, 1000, 2000 } }; // NOLINT + std::vector files = { { "file.c", 0, 1000, 2000 } }; std::vector debug_line_patches; WriteDebugLineTable(directories, files, opcodes, 0, &debug_line_data_, &debug_line_patches); @@ -333,7 +333,7 @@ TEST_F(DwarfTest, DebugInfo) { DW_CHECK("3 DW_TAG_compile_unit [no children]"); std::vector debug_info_patches; - std::vector expected_patches { 16, 20, 29, 33, 42, 46 }; // NOLINT + std::vector 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/writer.h b/compiler/debug/dwarf/writer.h index 95912ad6c96bf9644ce97420be817c3dd03c7361..afeb9803523722db409b0b556fc74e193c944bd0 100644 --- a/compiler/debug/dwarf/writer.h +++ b/compiler/debug/dwarf/writer.h @@ -19,8 +19,10 @@ #include #include + +#include + #include "base/bit_utils.h" -#include "base/logging.h" #include "leb128.h" namespace art { diff --git a/compiler/debug/elf_debug_frame_writer.h b/compiler/debug/elf_debug_frame_writer.h index d0c98a7b793a5d1918d547de817378687cd8edaa..27b70c8caa27e61d98c9e54f47f937928f2f1ed4 100644 --- a/compiler/debug/elf_debug_frame_writer.h +++ b/compiler/debug/elf_debug_frame_writer.h @@ -207,13 +207,12 @@ void WriteCFISection(linker::ElfBuilder* builder, } // 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 buffer; // Small temporary buffer. diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h index 2b617273b5621c9de020d9c0e9d23e7bf98a44f5..0e11e3298748180e6c740a3008bc0f43caf6031e 100644 --- a/compiler/debug/elf_debug_info_writer.h +++ b/compiler/debug/elf_debug_info_writer.h @@ -22,6 +22,7 @@ #include #include "art_field-inl.h" +#include "code_item_accessors-inl.h" #include "debug/dwarf/debug_abbrev_writer.h" #include "debug/dwarf/debug_info_entry_writer.h" #include "debug/elf_compilation_unit.h" @@ -35,6 +36,7 @@ #include "mirror/array.h" #include "mirror/class-inl.h" #include "mirror/class.h" +#include "oat_file.h" namespace art { namespace debug { @@ -47,9 +49,10 @@ static void LocalInfoCallback(void* ctx, const DexFile::LocalInfo& entry) { static std::vector GetParamNames(const MethodDebugInfo* mi) { std::vector names; - if (mi->code_item != nullptr) { + CodeItemDebugInfoAccessor accessor(mi->dex_file, mi->code_item); + 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); @@ -160,7 +163,7 @@ class ElfCompilationUnitWriter { 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); 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); @@ -202,13 +205,13 @@ class ElfCompilationUnitWriter { // Decode dex register locations for all stack maps. // It might be expensive, so do it just once and reuse the result. std::vector 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())); } } @@ -222,9 +225,9 @@ class ElfCompilationUnitWriter { 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); } @@ -242,28 +245,27 @@ class ElfCompilationUnitWriter { 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_); @@ -292,7 +294,7 @@ class ElfCompilationUnitWriter { CHECK_EQ(info_.Depth(), 0); std::vector 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_); @@ -457,7 +459,7 @@ class ElfCompilationUnitWriter { CHECK_EQ(info_.Depth(), 0); std::vector 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 6e72b46174ec21d80893e0d2e23a43fde0b2bf98..d7fd52448c4adc9acbf8cbc9b5d8184041549e43 100644 --- a/compiler/debug/elf_debug_line_writer.h +++ b/compiler/debug/elf_debug_line_writer.h @@ -26,6 +26,7 @@ #include "debug/src_map_elem.h" #include "dex_file-inl.h" #include "linker/elf_builder.h" +#include "oat_file.h" #include "stack_map.h" namespace art { @@ -59,7 +60,7 @@ class ElfDebugLineWriter { ? builder_->GetText()->GetAddress() : 0; - compilation_unit.debug_line_offset = builder_->GetDebugLine()->GetSize(); + compilation_unit.debug_line_offset = builder_->GetDebugLine()->GetPosition(); std::vector files; std::unordered_map files_map; @@ -158,7 +159,9 @@ class ElfDebugLineWriter { 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); + const uint32_t debug_info_offset = accessor.DebugInfoOffset(); + if (!dex->DecodeDebugPositionInfo(debug_info_offset, PositionInfoCallback, &dex2line_map)) { continue; } @@ -265,7 +268,7 @@ class ElfDebugLineWriter { } std::vector 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(); diff --git a/compiler/debug/elf_debug_loc_writer.h b/compiler/debug/elf_debug_loc_writer.h index bb856b29f4d1b3bd3069ab20526f244e51e361c3..34c2919a2116ebffde629fa5db5b4ea4db1381b2 100644 --- a/compiler/debug/elf_debug_loc_writer.h +++ b/compiler/debug/elf_debug_loc_writer.h @@ -149,11 +149,12 @@ static std::vector GetVariableLocations( 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. @@ -251,7 +252,10 @@ static void WriteDebugLocEntry(const MethodDebugInfo* method_info, // 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 33c46d7e1fd5fdcb2130930dbcbc3b3bc229ab51..a6267292bfae490f144ce1654406b6df7fd6764f 100644 --- a/compiler/debug/elf_debug_writer.cc +++ b/compiler/debug/elf_debug_writer.cc @@ -108,29 +108,32 @@ void WriteDebugInfo(linker::ElfBuilder* builder, std::vector MakeMiniDebugInfo( InstructionSet isa, const InstructionSetFeatures* features, - size_t rodata_size, + uint64_t text_address, size_t text_size, const ArrayRef& method_infos) { if (Is64BitInstructionSet(isa)) { return MakeMiniDebugInfoInternal(isa, features, - rodata_size, + text_address, text_size, method_infos); } else { return MakeMiniDebugInfoInternal(isa, features, - rodata_size, + text_address, text_size, method_infos); } } template -static std::vector WriteDebugElfFileForMethodsInternal( +static std::vector MakeElfFileForJITInternal( InstructionSet isa, const InstructionSetFeatures* features, - const ArrayRef& method_infos) { + bool mini_debug_info, + const MethodDebugInfo& mi) { + CHECK_EQ(mi.is_code_address_text_relative, false); + ArrayRef method_infos(&mi, 1); std::vector buffer; buffer.reserve(KB); linker::VectorOutputStream out("Debug ELF file", &buffer); @@ -138,23 +141,34 @@ static std::vector WriteDebugElfFileForMethodsInternal( new linker::ElfBuilder(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) { + std::vector mdi = MakeMiniDebugInfo(isa, + features, + mi.code_address, + mi.code_size, + method_infos); + builder->WriteSection(".gnu_debugdata", &mdi); + } else { + builder->GetText()->AllocateVirtualMemory(mi.code_address, mi.code_size); + WriteDebugInfo(builder.get(), + method_infos, + dwarf::DW_DEBUG_FRAME_FORMAT, + false /* write_oat_patches */); + } builder->End(); CHECK(builder->Good()); return buffer; } -std::vector WriteDebugElfFileForMethods( +std::vector MakeElfFileForJIT( InstructionSet isa, const InstructionSetFeatures* features, - const ArrayRef& method_infos) { + bool mini_debug_info, + const MethodDebugInfo& method_info) { if (Is64BitInstructionSet(isa)) { - return WriteDebugElfFileForMethodsInternal(isa, features, method_infos); + return MakeElfFileForJITInternal(isa, features, mini_debug_info, method_info); } else { - return WriteDebugElfFileForMethodsInternal(isa, features, method_infos); + return MakeElfFileForJITInternal(isa, features, mini_debug_info, method_info); } } diff --git a/compiler/debug/elf_debug_writer.h b/compiler/debug/elf_debug_writer.h index d24ca9b203f8a7e187d2f170005b53637afb2213..a47bf076b9f2d6d7f89e598ad3736f5cf0b48cf6 100644 --- a/compiler/debug/elf_debug_writer.h +++ b/compiler/debug/elf_debug_writer.h @@ -43,14 +43,15 @@ void WriteDebugInfo( std::vector MakeMiniDebugInfo( InstructionSet isa, const InstructionSetFeatures* features, - size_t rodata_section_size, + uint64_t text_section_address, size_t text_section_size, const ArrayRef& method_infos); -std::vector WriteDebugElfFileForMethods( +std::vector MakeElfFileForJIT( InstructionSet isa, const InstructionSetFeatures* features, - const ArrayRef& method_infos); + bool mini_debug_info, + const MethodDebugInfo& method_info); std::vector WriteDebugElfFileForClasses( InstructionSet isa, diff --git a/compiler/debug/elf_gnu_debugdata_writer.h b/compiler/debug/elf_gnu_debugdata_writer.h index 1cdf6b0ad1cb9464161f0da45f1eeb4a3932d70d..78b8e2780c200d5432fbd05aa6903447a51219e2 100644 --- a/compiler/debug/elf_gnu_debugdata_writer.h +++ b/compiler/debug/elf_gnu_debugdata_writer.h @@ -80,7 +80,7 @@ template static std::vector MakeMiniDebugInfoInternal( InstructionSet isa, const InstructionSetFeatures* features, - size_t rodata_section_size, + typename ElfTypes::Addr text_section_address, size_t text_section_size, const ArrayRef& method_infos) { std::vector buffer; @@ -88,11 +88,9 @@ static std::vector MakeMiniDebugInfoInternal( linker::VectorOutputStream out("Mini-debug-info ELF file", &buffer); std::unique_ptr> builder( new linker::ElfBuilder(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); + builder->Start(false /* write_program_headers */); + // Mirror .text as NOBITS section since the added symbols will reference it. + builder->GetText()->AllocateVirtualMemory(text_section_address, text_section_size); WriteDebugSymbols(builder.get(), method_infos, false /* with_signature */); WriteCFISection(builder.get(), method_infos, diff --git a/compiler/debug/elf_symtab_writer.h b/compiler/debug/elf_symtab_writer.h index 0907e102a0f16ba90ac89befa556e32a62555dfb..57e010f2323c63879d8685471e9b0d01aa07321f 100644 --- a/compiler/debug/elf_symtab_writer.h +++ b/compiler/debug/elf_symtab_writer.h @@ -79,8 +79,9 @@ static void WriteDebugSymbols(linker::ElfBuilder* builder, last_name_offset = name_offset; } - const auto* text = info.is_code_address_text_relative ? builder->GetText() : nullptr; - uint64_t address = info.code_address + (text != nullptr ? text->GetAddress() : 0); + const auto* text = builder->GetText(); + 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); diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index cc452fc1ae9e6ab320140b38a2c2c492b4cc52a2..ead909af9a742abd40cc2e343e45b1e633b3a578 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -16,11 +16,13 @@ #include "dex_to_dex_compiler.h" -#include "android-base/stringprintf.h" +#include +#include #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" @@ -294,7 +296,6 @@ void DexCompiler::CompileInvokeVirtual(Instruction* inst, uint32_t dex_pc, ClassLinker* class_linker = unit_.GetClassLinker(); ArtMethod* resolved_method = class_linker->ResolveMethod( - GetDexFile(), method_idx, unit_.GetDexCache(), unit_.GetClassLoader(), diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc index 6637be28111347cd824db024f17416973114c93e..979c4c4ce2498ceb8b4bc5bbb682b41c271c1404 100644 --- a/compiler/dex/dex_to_dex_decompiler_test.cc +++ b/compiler/dex/dex_to_dex_decompiler_test.cc @@ -91,19 +91,7 @@ class DexToDexDecompilerTest : public CommonCompilerTest { it.SkipAllFields(); // Unquicken each method. - while (it.HasNextDirectMethod()) { - uint32_t method_idx = it.GetMemberIndex(); - CompiledMethod* compiled_method = - compiler_driver_->GetCompiledMethod(MethodReference(updated_dex_file, method_idx)); - ArrayRef table; - if (compiled_method != nullptr) { - table = compiled_method->GetVmapTable(); - } - optimizer::ArtDecompileDEX( - *it.GetMethodCodeItem(), table, /* decompile_return_instruction */ true); - it.Next(); - } - while (it.HasNextVirtualMethod()) { + while (it.HasNextMethod()) { uint32_t method_idx = it.GetMemberIndex(); CompiledMethod* compiled_method = compiler_driver_->GetCompiledMethod(MethodReference(updated_dex_file, method_idx)); diff --git a/compiler/dex/inline_method_analyser.cc b/compiler/dex/inline_method_analyser.cc index 518b0ece73ea86629fe520a6c36e249421171302..80677b934b598e6ce52ac0b172465d117ba5e504 100644 --- a/compiler/dex/inline_method_analyser.cc +++ b/compiler/dex/inline_method_analyser.cc @@ -20,6 +20,7 @@ #include "art_method-inl.h" #include "base/enums.h" #include "class_linker-inl.h" +#include "code_item_accessors-inl.h" #include "dex_file-inl.h" #include "dex_instruction-inl.h" #include "dex_instruction.h" @@ -43,7 +44,7 @@ class Matcher { typedef bool MatchFn(Matcher* matcher); template - 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 @@ class Matcher { bool IPutOnThis(); private: - explicit Matcher(const DexFile::CodeItem* code_item) + explicit Matcher(const CodeItemDataAccessor* code_item) : code_item_(code_item), - instruction_(code_item->Instructions().begin()), - 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 CodeItemDataAccessor* const code_item_; DexInstructionIterator instruction_; - size_t pos_; - size_t mark_; + size_t pos_ = 0u; + size_t mark_ = 0u; }; template -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); } @@ -122,12 +121,12 @@ bool Matcher::Const0() { } 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 @@ bool Matcher::DoMatch(const DexFile::CodeItem* code_item, MatchFn* const* patter 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); + 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 @@ ArtMethod* GetTargetConstructor(ArtMethod* method, const Instruction* invoke_dir // Return the forwarded arguments and check that all remaining arguments are zero. // If the check fails, return static_cast(-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 @@ size_t CountForwardedConstructorArguments(const DexFile::CodeItem* code_item, 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 @@ bool RecordConstructorIPut(ArtMethod* method, 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,17 +294,17 @@ bool DoAnalyseConstructor(const DexFile::CodeItem* code_item, 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 DexInstructionPcPair& pair : code_item->Instructions()) { + for (const DexInstructionPcPair& pair : *code_item) { const Instruction& instruction = pair.Inst(); if (instruction.Opcode() == Instruction::RETURN_VOID) { break; @@ -314,7 +316,7 @@ bool DoAnalyseConstructor(const DexFile::CodeItem* code_item, // 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); @@ -322,14 +324,13 @@ bool DoAnalyseConstructor(const DexFile::CodeItem* code_item, return false; } if (target_method->GetDeclaringClass()->IsObjectClass()) { - DCHECK_EQ(target_method->GetCodeItem()->Instructions().begin()->Opcode(), - Instruction::RETURN_VOID); + DCHECK_EQ(CodeItemDataAccessor(target_method).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); + 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. @@ -365,7 +366,7 @@ bool DoAnalyseConstructor(const DexFile::CodeItem* code_item, } // anonymous namespace -bool AnalyseConstructor(const DexFile::CodeItem* code_item, +bool AnalyseConstructor(const CodeItemDataAccessor* code_item, ArtMethod* method, InlineMethod* result) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -429,27 +430,27 @@ static_assert(InlineMethodAnalyser::IGetVariant(Instruction::IGET_SHORT) == 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); + 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); - Instruction::Code opcode = code_item->Instructions().begin()->Opcode(); + DCHECK_NE(code_item->InsnsSizeInCodeUnits(), 0u); + Instruction::Code opcode = code_item->begin()->Opcode(); switch (opcode) { case Instruction::RETURN_VOID: @@ -518,15 +519,15 @@ bool InlineMethodAnalyser::IsSyntheticAccessor(MethodReference ref) { strncmp(method_name, "-", strlen("-")) == 0; } -bool InlineMethodAnalyser::AnalyseReturnMethod(const DexFile::CodeItem* code_item, +bool InlineMethodAnalyser::AnalyseReturnMethod(const CodeItemDataAccessor* code_item, InlineMethod* result) { - DexInstructionIterator return_instruction = code_item->Instructions().begin(); + 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; @@ -540,9 +541,9 @@ bool InlineMethodAnalyser::AnalyseReturnMethod(const DexFile::CodeItem* code_ite return true; } -bool InlineMethodAnalyser::AnalyseConstMethod(const DexFile::CodeItem* code_item, +bool InlineMethodAnalyser::AnalyseConstMethod(const CodeItemDataAccessor* code_item, InlineMethod* result) { - DexInstructionIterator instruction = code_item->Instructions().begin(); + DexInstructionIterator instruction = code_item->begin(); const Instruction* return_instruction = instruction->Next(); Instruction::Code return_opcode = return_instruction->Opcode(); if (return_opcode != Instruction::RETURN && @@ -551,13 +552,13 @@ bool InlineMethodAnalyser::AnalyseConstMethod(const DexFile::CodeItem* code_item } 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? } @@ -571,12 +572,12 @@ bool InlineMethodAnalyser::AnalyseConstMethod(const DexFile::CodeItem* code_item 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) { - DexInstructionIterator instruction = code_item->Instructions().begin(); + DexInstructionIterator instruction = code_item->begin(); Instruction::Code opcode = instruction->Opcode(); DCHECK(IsInstructionIGet(opcode)); @@ -591,17 +592,17 @@ bool InlineMethodAnalyser::AnalyseIGetMethod(const DexFile::CodeItem* code_item, 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? } @@ -635,18 +636,18 @@ bool InlineMethodAnalyser::AnalyseIGetMethod(const DexFile::CodeItem* code_item, 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) { - DexInstructionIterator instruction = code_item->Instructions().begin(); + 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 && @@ -658,7 +659,7 @@ bool InlineMethodAnalyser::AnalyseIPutMethod(const DexFile::CodeItem* code_item, 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; } @@ -666,9 +667,9 @@ bool InlineMethodAnalyser::AnalyseIPutMethod(const DexFile::CodeItem* code_item, 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 a35e97fa1156e4d5de3ce02d5df6e16cfc950b6b..cde21479957f09214db3ce3246a4def3d07dded7 100644 --- a/compiler/dex/inline_method_analyser.h +++ b/compiler/dex/inline_method_analyser.h @@ -30,6 +30,8 @@ namespace art { +class CodeItemDataAccessor; + namespace verifier { class MethodVerifier; } // namespace verifier @@ -121,21 +123,21 @@ class InlineMethodAnalyser { 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/verification_results.cc b/compiler/dex/verification_results.cc index 03c90d82c80b95d6e201652e46cdb30efb8a6bd6..1e0b94de8141f10dd2d8e873d4f57dcdb5b7a9c7 100644 --- a/compiler/dex/verification_results.cc +++ b/compiler/dex/verification_results.cc @@ -16,7 +16,8 @@ #include "verification_results.h" -#include "base/logging.h" +#include + #include "base/mutex-inl.h" #include "base/stl_util.h" #include "driver/compiler_driver.h" diff --git a/compiler/dex/verified_method.cc b/compiler/dex/verified_method.cc index df75e07c3fac8a8794529a78229858b9ca47aa9a..8934201b106f7fce8f6dab6f212067a187fc5f35 100644 --- a/compiler/dex/verified_method.cc +++ b/compiler/dex/verified_method.cc @@ -19,7 +19,9 @@ #include #include -#include "base/logging.h" +#include + +#include "code_item_accessors-inl.h" #include "dex_file.h" #include "dex_instruction-inl.h" #include "runtime.h" @@ -64,7 +66,7 @@ void VerifiedMethod::GenerateSafeCastSet(verifier::MethodVerifier* method_verifi if (method_verifier->HasFailures()) { return; } - for (const DexInstructionPcPair& pair : method_verifier->CodeItem()->Instructions()) { + for (const DexInstructionPcPair& pair : method_verifier->CodeItem()) { const Instruction& inst = pair.Inst(); const Instruction::Code code = inst.Opcode(); if (code == Instruction::CHECK_CAST) { diff --git a/compiler/driver/compiled_method_storage.cc b/compiler/driver/compiled_method_storage.cc index c739333cee3e83548edd5f4af8388d701e35ccd3..c8c2b6998f569c769aac51ebac8a4a0a26ca0e73 100644 --- a/compiler/driver/compiled_method_storage.cc +++ b/compiler/driver/compiled_method_storage.cc @@ -19,7 +19,8 @@ #include "compiled_method_storage.h" -#include "base/logging.h" +#include + #include "compiled_method.h" #include "linker/linker_patch.h" #include "thread-current-inl.h" diff --git a/compiler/driver/compiled_method_storage_test.cc b/compiler/driver/compiled_method_storage_test.cc index de481caf07865cf0130ce3272aa527c8506b3412..0769561d0ed6e2fb8fe12566147152fb7e1f9ac3 100644 --- a/compiler/driver/compiled_method_storage_test.cc +++ b/compiler/driver/compiled_method_storage_test.cc @@ -37,9 +37,6 @@ TEST(CompiledMethodStorage, Deduplicate) { /* 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(); diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h index b04392918d9dcca0140894e08ae670ab91e8a178..294072d7e724f38f1c64affff50998647f097b80 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 dex_cache, - Handle class_loader, dex::TypeIndex cls_index, +inline ObjPtr CompilerDriver::ResolveClass( + const ScopedObjectAccess& soa, + Handle dex_cache, + Handle 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 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 @@ inline mirror::Class* CompilerDriver::ResolveClass( return cls; } -inline mirror::Class* CompilerDriver::ResolveCompilingMethodsClass( - const ScopedObjectAccess& soa, Handle dex_cache, - Handle class_loader, const DexCompilationUnit* mUnit) { +inline ObjPtr CompilerDriver::ResolveCompilingMethodsClass( + const ScopedObjectAccess& soa, + Handle dex_cache, + Handle 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 @@ inline mirror::Class* CompilerDriver::ResolveCompilingMethodsClass( return ResolveClass(soa, dex_cache, class_loader, referrer_method_id.class_idx_, mUnit); } -inline ArtField* CompilerDriver::ResolveFieldWithDexFile( - const ScopedObjectAccess& soa, Handle dex_cache, - Handle 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 dex_cache, + Handle 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 @@ inline ArtField* CompilerDriver::ResolveFieldWithDexFile( return resolved_field; } -inline ArtField* CompilerDriver::ResolveField( - const ScopedObjectAccess& soa, Handle dex_cache, - Handle 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 CompilerDriver::IsFastInstanceField( - mirror::DexCache* dex_cache, mirror::Class* referrer_class, - ArtField* resolved_field, uint16_t field_idx) { + ObjPtr dex_cache, + ObjPtr referrer_class, + ArtField* resolved_field, + uint16_t field_idx) { DCHECK(!resolved_field->IsStatic()); ObjPtr fields_class = resolved_field->GetDeclaringClass(); bool fast_get = referrer_class != nullptr && @@ -112,7 +109,7 @@ inline ArtMethod* CompilerDriver::ResolveMethod( DCHECK_EQ(class_loader.Get(), mUnit->GetClassLoader().Get()); ArtMethod* resolved_method = mUnit->GetClassLinker()->ResolveMethod( - *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 a9d27ef0cc2caa843fcdaf5a7384963c053e3bf1..68f963e3ab37dd4b922a440ac31f388591b3aacc 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -32,6 +32,7 @@ #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" @@ -46,6 +47,7 @@ #include "dex/verified_method.h" #include "dex_compilation_unit.h" #include "dex_file-inl.h" +#include "dex_file_annotations.h" #include "dex_instruction-inl.h" #include "driver/compiler_options.h" #include "gc/accounting/card_table-inl.h" @@ -281,9 +283,6 @@ CompilerDriver::CompilerDriver( std::unordered_set* compiled_classes, std::unordered_set* 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), @@ -302,9 +301,6 @@ CompilerDriver::CompilerDriver( 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), @@ -395,7 +391,7 @@ void CompilerDriver::CompileAll(jobject class_loader, if (GetCompilerOptions().IsAnyCompilationEnabled()) { Compile(class_loader, dex_files, timings); } - if (dump_stats_) { + if (GetCompilerOptions().GetDumpStats()) { stats_->Dump(); } @@ -428,6 +424,10 @@ static optimizer::DexToDexCompilationLevel GetDexToDexCompilationLevel( // optimizations that could break that. max_level = optimizer::DexToDexCompilationLevel::kDontDexToDexCompile; } + if (!VdexFile::CanEncodeQuickenedData(dex_file)) { + // Don't do any dex level optimizations if we cannot encode the quickening. + return optimizer::DexToDexCompilationLevel::kDontDexToDexCompile; + } if (klass->IsVerified()) { // Class is verified so we can enable DEX-to-DEX compilation for performance. return max_level; @@ -511,40 +511,12 @@ static void CompileMethod(Thread* self, 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); - - // TODO: Lookup annotation from DexFile directly without resolving method. - ArtMethod* method = - Runtime::Current()->GetClassLinker()->ResolveMethod( - 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; - } + 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, - optimization_flags); + compiled_method = driver->GetCompiler()->JniCompile( + access_flags, method_idx, dex_file, dex_cache); CHECK(compiled_method != nullptr); } } else if ((access_flags & kAccAbstract) != 0) { @@ -730,7 +702,6 @@ void CompilerDriver::Resolve(jobject class_loader, // stable order. static void ResolveConstStrings(Handle dex_cache, - const DexFile& dex_file, const DexFile::CodeItem* code_item) REQUIRES_SHARED(Locks::mutator_lock_) { if (code_item == nullptr) { @@ -746,7 +717,7 @@ static void ResolveConstStrings(Handle dex_cache, 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 string = class_linker->ResolveString(string_index, dex_cache); CHECK(string != nullptr) << "Could not allocate a string when forcing determinism"; break; } @@ -790,32 +761,18 @@ static void ResolveConstStrings(CompilerDriver* driver, continue; } - // Direct methods. - int64_t previous_direct_method_idx = -1; - while (it.HasNextDirectMethod()) { - uint32_t method_idx = it.GetMemberIndex(); - if (method_idx == previous_direct_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()) { + // Direct and virtual methods. + int64_t previous_method_idx = -1; + while (it.HasNextMethod()) { uint32_t method_idx = it.GetMemberIndex(); - if (method_idx == previous_virtual_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_virtual_method_idx = method_idx; - ResolveConstStrings(dex_cache, *dex_file, it.GetMethodCodeItem()); + previous_method_idx = method_idx; + ResolveConstStrings(dex_cache, it.GetMethodCodeItem()); it.Next(); } DCHECK(!it.HasNext()); @@ -992,14 +949,14 @@ class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor { ArtMethod* method, std::set>* 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); + 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); @@ -1091,22 +1048,21 @@ void CompilerDriver::LoadImageClasses(TimingLogger* timings) { 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 dex_cache(hs2.NewHandle(class_linker->RegisterDexFile(*dex_file, nullptr))); - Handle klass(hs2.NewHandle( + ObjPtr klass = (dex_cache != nullptr) - ? class_linker->ResolveType(*dex_file, - exception_type_idx, + ? class_linker->ResolveType(exception_type_idx, dex_cache, ScopedNullHandle()) - : 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 @@ -1410,17 +1366,18 @@ void CompilerDriver::ProcessedStaticField(bool resolved, bool local) { } 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 referrer_class; Handle dex_cache(mUnit->GetDexCache()); { - Handle class_loader_handle = mUnit->GetClassLoader(); - resolved_field = ResolveField(soa, dex_cache, class_loader_handle, mUnit, field_idx, false); + Handle 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) { @@ -1585,7 +1542,7 @@ class ParallelCompilationManager { // 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 klass) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(klass != nullptr); const DexFile& original_dex_file = *klass->GetDexCache()->GetDexFile(); @@ -1653,7 +1610,7 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { : 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(); @@ -1679,8 +1636,8 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { Handle 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 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 @@ -1706,8 +1663,8 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { 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()); } @@ -1721,8 +1678,8 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { 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()); } @@ -1730,18 +1687,12 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { it.Next(); } if (resolve_fields_and_methods) { - while (it.HasNextDirectMethod()) { - ArtMethod* method = class_linker->ResolveMethod( - dex_file, it.GetMemberIndex(), dex_cache, class_loader, nullptr, - it.GetMethodInvokeType(class_def)); - if (method == nullptr) { - CheckAndClearResolveException(soa.Self()); - } - it.Next(); - } - while (it.HasNextVirtualMethod()) { + while (it.HasNextMethod()) { ArtMethod* method = class_linker->ResolveMethod( - 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()); @@ -1777,7 +1728,7 @@ class ResolveTypeVisitor : public CompilationVisitor { dex_file, class_loader.Get()))); ObjPtr 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) { @@ -1848,12 +1799,7 @@ static void PopulateVerifiedMethods(const DexFile& dex_file, 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(); } @@ -2012,7 +1958,7 @@ class VerifyClassVisitor : public CompilationVisitor { : 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); @@ -2080,28 +2026,19 @@ class VerifyClassVisitor : public CompilationVisitor { 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) { if (manager_->GetCompiler()->GetCompilerOptions().IsBootImage()) { - if (!klass->IsVerified()) { - // Re-run verification to get all failure messages if it soft-failed. - if (!klass->IsErroneous()) { - gLogVerbosity.verifier = true; - // Note: We can't call ClassLinker::VerifyClass, as it will elide the second - // verification. - Runtime* runtime = Runtime::Current(); - std::string v_error; - verifier::MethodVerifier::VerifyClass(soa.Self(), - klass.Get(), - runtime->GetCompilerCallbacks(), - runtime->IsAotCompiler(), - verifier::HardFailLogMode::kLogInternalFatal, - &v_error); - } + if (!klass->IsResolved() || klass->IsErroneous()) { LOG(FATAL) << "Boot classpath class " << klass->PrettyClass() - << " failed to fully verify: state= " << klass->GetStatus(); + << " failed to resolve/is erroneous: state= " << klass->GetStatus(); + UNREACHABLE(); } } if (klass->IsVerified()) { @@ -2150,7 +2087,7 @@ class SetVerifiedClassVisitor : public CompilationVisitor { 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); @@ -2214,7 +2151,7 @@ class InitializeClassVisitor : public CompilationVisitor { 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); @@ -2394,22 +2331,20 @@ class InitializeClassVisitor : public CompilationVisitor { DCHECK(!klass->IsInitialized()); StackHandleScope<1> hs(Thread::Current()); - Handle h_dex_cache = hs.NewHandle(klass->GetDexCache()); - const DexFile* dex_file = manager_->GetDexFile(); + Handle 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 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); } } @@ -2422,11 +2357,11 @@ class InitializeClassVisitor : public CompilationVisitor { for (const DexInstructionPcPair& inst : code_item->Instructions()) { if (inst->Opcode() == Instruction::CONST_STRING) { ObjPtr 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 s = class_linker->ResolveString( - *dex_file, dex::StringIndex(inst->VRegB_31c()), h_dex_cache); + dex::StringIndex(inst->VRegB_31c()), dex_cache); CHECK(s != nullptr); } } @@ -2730,7 +2665,7 @@ class CompileClassVisitor : public CompilationVisitor { explicit CompileClassVisitor(const ParallelCompilationManager* manager) : manager_(manager) {} virtual void Visit(size_t class_def_index) REQUIRES(!Locks::mutator_lock_) OVERRIDE { - ATRACE_CALL(); + ScopedTrace trace(__FUNCTION__); const DexFile& dex_file = *manager_->GetDexFile(); const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); ClassLinker* class_linker = manager_->GetClassLinker(); @@ -2780,17 +2715,17 @@ class CompileClassVisitor : public CompilationVisitor { 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; + previous_method_idx = method_idx; CompileMethod(soa.Self(), driver, it.GetMethodCodeItem(), @@ -2805,30 +2740,6 @@ class CompileClassVisitor : public CompilationVisitor { 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); - it.Next(); - } DCHECK(!it.HasNext()); } diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index da4a580bf2330f8e23d52f41c385e2afa48a1405..e001726c953e75b38d03c28d54e1734016ef8476 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -97,9 +97,6 @@ class CompilerDriver { std::unordered_set* compiled_classes, std::unordered_set* compiled_methods, size_t thread_count, - bool dump_stats, - bool dump_passes, - CumulativeLogger* timer, int swap_fd, const ProfileCompilationInfo* profile_compilation_info); @@ -222,36 +219,33 @@ class CompilerDriver { REQUIRES_SHARED(Locks::mutator_lock_); // Resolve compiling method's class. Returns null on failure. - mirror::Class* ResolveCompilingMethodsClass( - const ScopedObjectAccess& soa, Handle dex_cache, - Handle class_loader, const DexCompilationUnit* mUnit) + ObjPtr ResolveCompilingMethodsClass(const ScopedObjectAccess& soa, + Handle dex_cache, + Handle class_loader, + const DexCompilationUnit* mUnit) REQUIRES_SHARED(Locks::mutator_lock_); - mirror::Class* ResolveClass( - const ScopedObjectAccess& soa, Handle dex_cache, - Handle class_loader, dex::TypeIndex type_index, - const DexCompilationUnit* mUnit) + ObjPtr ResolveClass(const ScopedObjectAccess& soa, + Handle dex_cache, + Handle 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 dex_cache, - Handle 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 dex_cache, - Handle class_loader, const DexFile* dex_file, - uint32_t field_idx, bool is_static) + ArtField* ResolveField(const ScopedObjectAccess& soa, + Handle dex_cache, + Handle 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 IsFastInstanceField( - mirror::DexCache* dex_cache, mirror::Class* referrer_class, - ArtField* resolved_field, uint16_t field_idx) + std::pair IsFastInstanceField(ObjPtr dex_cache, + ObjPtr 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. @@ -273,9 +267,9 @@ class CompilerDriver { 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_); @@ -302,18 +296,6 @@ class CompilerDriver { 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); } @@ -536,11 +518,6 @@ class CompilerDriver { class AOTCompilationStats; std::unique_ptr stats_; - bool dump_stats_; - const bool dump_passes_; - - CumulativeLogger* const timings_logger_; - typedef void (*CompilerCallbackFn)(CompilerDriver& driver); typedef MutexLock* (*CompilerMutexLockFn)(CompilerDriver& driver); diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc index 032763cdff7e7958869ce42cfb3ce3793b8ab915..c0a9a05aa6b050d02bf17cbe8fd864c162d4853b 100644 --- a/compiler/driver/compiler_options.cc +++ b/compiler/driver/compiler_options.cc @@ -49,6 +49,8 @@ CompilerOptions::CompilerOptions() 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), diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index a71f61a9e34fc491b70d14eefd8835c34df52fe7..3f660293d29b51c5eb363edd6a1d639578e41e27 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -266,6 +266,14 @@ class CompilerOptions FINAL { return passes_to_run_; } + bool GetDumpTimings() const { + return dump_timings_; + } + + bool GetDumpStats() const { + return dump_stats_; + } + private: bool ParseDumpInitFailures(const std::string& option, std::string* error_msg); void ParseDumpCfgPasses(const StringPiece& option, UsageFn Usage); @@ -303,6 +311,8 @@ class CompilerOptions FINAL { 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 verbose_methods_; diff --git a/compiler/driver/compiler_options_map-inl.h b/compiler/driver/compiler_options_map-inl.h index e28d49974a9773c77e2eef315477c4edca8c90c4..f97ab08600b58b57222d14333d1c4c002964deff 100644 --- a/compiler/driver/compiler_options_map-inl.h +++ b/compiler/driver/compiler_options_map-inl.h @@ -78,6 +78,14 @@ inline bool ReadCompilerOptions(Base& map, CompilerOptions* options, std::string map.AssignIfExists(Base::VerboseMethods, &options->verbose_methods_); options->deduplicate_code_ = map.GetOrDefault(Base::DeduplicateCode); + if (map.Exists(Base::DumpTimings)) { + options->dump_timings_ = true; + } + + if (map.Exists(Base::DumpStats)) { + options->dump_stats_ = true; + } + return true; } @@ -129,6 +137,12 @@ inline void AddCompilerOptionsArgumentParserOptions(Builder& b) { .WithValueMap({{"false", false}, {"true", true}}) .IntoKey(Map::DeduplicateCode) + .Define({"--dump-timings"}) + .IntoKey(Map::DumpTimings) + + .Define({"--dump-stats"}) + .IntoKey(Map::DumpStats) + .Define("--debuggable") .IntoKey(Map::Debuggable) diff --git a/compiler/driver/compiler_options_map-storage.h b/compiler/driver/compiler_options_map-storage.h index 756598de05da084fb4d20a4242211014eaff83bc..01f32e0c9c9ba556d3701b419e9f6c08407525be 100644 --- a/compiler/driver/compiler_options_map-storage.h +++ b/compiler/driver/compiler_options_map-storage.h @@ -36,7 +36,7 @@ #define COMPILER_OPTIONS_KEY(Type, Name, ...) \ template class KeyType> \ - const KeyType CompilerOptionsMap::Name {__VA_ARGS__}; // NOLINT [readability/braces] [4] + const KeyType CompilerOptionsMap::Name {__VA_ARGS__}; #include template struct CompilerOptionsMap; diff --git a/compiler/driver/compiler_options_map.def b/compiler/driver/compiler_options_map.def index cccd6184c6fc03097dcbbc021a9c91da7a275d80..2c56fd7974881dcf22c0d0e071eea536882a3d39 100644 --- a/compiler/driver/compiler_options_map.def +++ b/compiler/driver/compiler_options_map.def @@ -58,5 +58,7 @@ COMPILER_OPTIONS_KEY (Unit, DumpCFGAppend) COMPILER_OPTIONS_KEY (std::string, RegisterAllocationStrategy) COMPILER_OPTIONS_KEY (ParseStringList<','>, VerboseMethods) COMPILER_OPTIONS_KEY (bool, DeduplicateCode, true) +COMPILER_OPTIONS_KEY (Unit, DumpTimings) +COMPILER_OPTIONS_KEY (Unit, DumpStats) #undef COMPILER_OPTIONS_KEY diff --git a/compiler/exception_test.cc b/compiler/exception_test.cc index 897b50bdacd11fd22abb81c188867dc1d708bc83..4dbef0d799ffb0dafa23844a2fc1fa78ab834b97 100644 --- a/compiler/exception_test.cc +++ b/compiler/exception_test.cc @@ -20,6 +20,7 @@ #include "base/callee_save_type.h" #include "base/enums.h" #include "class_linker.h" +#include "code_item_accessors-inl.h" #include "common_runtime_test.h" #include "dex_file-inl.h" #include "dex_file.h" @@ -129,11 +130,12 @@ class ExceptionTest : public CommonRuntimeTest { TEST_F(ExceptionTest, FindCatchHandler) { ScopedObjectAccess soa(Thread::Current()); const DexFile::CodeItem* code_item = dex_->GetCodeItem(method_f_->GetCodeItemOffset()); + CodeItemDataAccessor accessor(dex_, code_item); ASSERT_TRUE(code_item != nullptr); - ASSERT_EQ(2u, code_item->tries_size_); - ASSERT_NE(0u, code_item->insns_size_in_code_units_); + ASSERT_EQ(2u, accessor.TriesSize()); + ASSERT_NE(0u, accessor.InsnsSizeInCodeUnits()); const DexFile::TryItem *t0, *t1; t0 = dex_->GetTryItems(*code_item, 0); diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index 5c89869e006235db3d867aa3de7e2aec0e4b3815..88e3e5b2300fa5d7eb0397eafcf6935681e55d5b 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -21,7 +21,9 @@ #include "arch/instruction_set.h" #include "arch/instruction_set_features.h" #include "art_method-inl.h" +#include "base/logging.h" // For VLOG #include "base/stringpiece.h" +#include "base/systrace.h" #include "base/time_utils.h" #include "base/timing_logger.h" #include "base/unix_file/fd_file.h" @@ -80,6 +82,9 @@ extern "C" void jit_types_loaded(void* handle, mirror::Class** types, size_t cou JitCompiler::JitCompiler() { compiler_options_.reset(new CompilerOptions()); + // Special case max code units for inlining, whose default is "unset" (implictly + // meaning no limit). Do this before parsing the actuall passed options. + compiler_options_->SetInlineMaxCodeUnits(CompilerOptions::kDefaultInlineMaxCodeUnits); { std::string error_msg; if (!compiler_options_->ParseCompilerOptions(Runtime::Current()->GetCompilerOptions(), @@ -95,10 +100,6 @@ JitCompiler::JitCompiler() { // Set debuggability based on the runtime value. compiler_options_->SetDebuggable(Runtime::Current()->IsJavaDebuggable()); - // Special case max code units for inlining, whose default is "unset" (implictly - // meaning no limit). - compiler_options_->SetInlineMaxCodeUnits(CompilerOptions::kDefaultInlineMaxCodeUnits); - const InstructionSet instruction_set = kRuntimeISA; for (const StringPiece option : Runtime::Current()->GetCompilerOptions()) { VLOG(compiler) << "JIT compiler option " << option; @@ -131,7 +132,6 @@ JitCompiler::JitCompiler() { if (instruction_set_features_ == nullptr) { instruction_set_features_ = InstructionSetFeatures::FromCppDefines(); } - cumulative_logger_.reset(new CumulativeLogger("jit times")); compiler_driver_.reset(new CompilerDriver( compiler_options_.get(), /* verification_results */ nullptr, @@ -142,9 +142,6 @@ JitCompiler::JitCompiler() { /* compiled_classes */ nullptr, /* compiled_methods */ nullptr, /* thread_count */ 1, - /* dump_stats */ false, - /* dump_passes */ false, - cumulative_logger_.get(), /* swap_fd */ -1, /* profile_compilation_info */ nullptr)); // Disable dedupe so we can remove compiled methods. @@ -167,6 +164,8 @@ JitCompiler::~JitCompiler() { } bool JitCompiler::CompileMethod(Thread* self, ArtMethod* method, bool osr) { + SCOPED_TRACE << "JIT compiling " << method->PrettyMethod(); + DCHECK(!method->IsProxyMethod()); DCHECK(method->GetDeclaringClass()->IsResolved()); diff --git a/compiler/jit/jit_compiler.h b/compiler/jit/jit_compiler.h index 1e1838efd50be9990fb0fe6ad12175d3251143cb..31dc9e2fe5a2c6f8773c472aaeb15afc8571432d 100644 --- a/compiler/jit/jit_compiler.h +++ b/compiler/jit/jit_compiler.h @@ -48,7 +48,6 @@ class JitCompiler { private: std::unique_ptr compiler_options_; - std::unique_ptr cumulative_logger_; std::unique_ptr compiler_driver_; std::unique_ptr instruction_set_features_; std::unique_ptr jit_logger_; diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc index 3460efe474be734d0386e6975089708743e961b1..daf64d1298f7061c8036b7bdf34e7720245f9d01 100644 --- a/compiler/jni/jni_compiler_test.cc +++ b/compiler/jni/jni_compiler_test.cc @@ -55,10 +55,10 @@ extern "C" JNIEXPORT jint JNICALL Java_MyClassNatives_sbar(JNIEnv*, jclass, jint namespace art { enum class JniKind { - kNormal = Compiler::kNone, // Regular kind of un-annotated natives. - kFast = Compiler::kFastNative, // Native method annotated with @FastNative. - kCritical = Compiler::kCriticalNative, // Native method annotated with @CriticalNative. - kCount = Compiler::kCriticalNative + 1 // How many different types of JNIs we can have. + kNormal, // Regular kind of un-annotated natives. + kFast, // Native method annotated with @FastNative. + kCritical, // Native method annotated with @CriticalNative. + kCount // How many different types of JNIs we can have. }; // Used to initialize array sizes that want to have different state per current jni. @@ -2205,8 +2205,8 @@ void JniCompilerTest::NormalNativeImpl() { ArtMethod* method = jni::DecodeArtMethod(jmethod_); ASSERT_TRUE(method != nullptr); - EXPECT_FALSE(method->IsAnnotatedWithCriticalNative()); - EXPECT_FALSE(method->IsAnnotatedWithFastNative()); + EXPECT_FALSE(method->IsCriticalNative()); + EXPECT_FALSE(method->IsFastNative()); } // TODO: just rename the java functions to the standard convention and remove duplicated tests @@ -2227,8 +2227,8 @@ void JniCompilerTest::FastNativeImpl() { ArtMethod* method = jni::DecodeArtMethod(jmethod_); ASSERT_TRUE(method != nullptr); - EXPECT_FALSE(method->IsAnnotatedWithCriticalNative()); - EXPECT_TRUE(method->IsAnnotatedWithFastNative()); + EXPECT_FALSE(method->IsCriticalNative()); + EXPECT_TRUE(method->IsFastNative()); } // TODO: just rename the java functions to the standard convention and remove duplicated tests @@ -2256,8 +2256,8 @@ void JniCompilerTest::CriticalNativeImpl() { ArtMethod* method = jni::DecodeArtMethod(jmethod_); ASSERT_TRUE(method != nullptr); - EXPECT_TRUE(method->IsAnnotatedWithCriticalNative()); - EXPECT_FALSE(method->IsAnnotatedWithFastNative()); + EXPECT_TRUE(method->IsCriticalNative()); + EXPECT_FALSE(method->IsFastNative()); EXPECT_EQ(0, gJava_myClassNatives_criticalNative_calls[gCurrentJni]); env_->CallStaticVoidMethod(jklass_, jmethod_); diff --git a/compiler/jni/quick/arm/calling_convention_arm.cc b/compiler/jni/quick/arm/calling_convention_arm.cc index 3e637bcf43e513d925637c7871b9765a4553ed56..54f193b5511ef505a1feb01311cd66ab69a08035 100644 --- a/compiler/jni/quick/arm/calling_convention_arm.cc +++ b/compiler/jni/quick/arm/calling_convention_arm.cc @@ -16,7 +16,9 @@ #include "calling_convention_arm.h" -#include "base/logging.h" +#include + +#include "base/macros.h" #include "handle_scope-inl.h" #include "utils/arm/managed_register_arm.h" diff --git a/compiler/jni/quick/arm64/calling_convention_arm64.cc b/compiler/jni/quick/arm64/calling_convention_arm64.cc index 3afd7011cad45d85ea66ace9f1a316aded15dd98..328ecbbc5cbece8cb7a310da50fda79dc22c974c 100644 --- a/compiler/jni/quick/arm64/calling_convention_arm64.cc +++ b/compiler/jni/quick/arm64/calling_convention_arm64.cc @@ -16,7 +16,8 @@ #include "calling_convention_arm64.h" -#include "base/logging.h" +#include + #include "handle_scope-inl.h" #include "utils/arm64/managed_register_arm64.h" diff --git a/compiler/jni/quick/calling_convention.cc b/compiler/jni/quick/calling_convention.cc index 55c27d1a6aa22b5450fc31156b3c869c48d5cd35..ff814c8a6b9e463c0ac78f1ce5f2d2877b1107b3 100644 --- a/compiler/jni/quick/calling_convention.cc +++ b/compiler/jni/quick/calling_convention.cc @@ -16,7 +16,7 @@ #include "calling_convention.h" -#include "base/logging.h" +#include #ifdef ART_ENABLE_CODEGEN_arm #include "jni/quick/arm/calling_convention_arm.h" diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc index b3177aa4715804ca8a8070f4e37700cadd198770..136e3db062aed0f6a426b0d807ba98d27f4250ba 100644 --- a/compiler/jni/quick/jni_compiler.cc +++ b/compiler/jni/quick/jni_compiler.cc @@ -25,11 +25,10 @@ #include "art_method.h" #include "base/arena_allocator.h" #include "base/enums.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/macros.h" #include "calling_convention.h" #include "class_linker.h" -#include "compiled_method.h" #include "debug/dwarf/debug_frame_opcode_writer.h" #include "dex_file-inl.h" #include "driver/compiler_driver.h" @@ -52,8 +51,6 @@ namespace art { -using JniOptimizationFlags = Compiler::JniOptimizationFlags; - template static void CopyParameter(JNIMacroAssembler* jni_asm, ManagedRuntimeCallingConvention* mr_conv, @@ -117,11 +114,10 @@ static ThreadOffset GetJniEntrypointThreadOffset(JniEntrypoint whi // convention. // template -static CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver* driver, - uint32_t access_flags, - uint32_t method_idx, - const DexFile& dex_file, - JniOptimizationFlags optimization_flags) { +static JniCompiledMethod ArtJniCompileMethodInternal(CompilerDriver* driver, + uint32_t access_flags, + uint32_t method_idx, + const DexFile& dex_file) { const bool is_native = (access_flags & kAccNative) != 0; CHECK(is_native); const bool is_static = (access_flags & kAccStatic) != 0; @@ -131,10 +127,10 @@ static CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver* driver, const InstructionSetFeatures* instruction_set_features = driver->GetInstructionSetFeatures(); // i.e. if the method was annotated with @FastNative - const bool is_fast_native = (optimization_flags == Compiler::kFastNative); + const bool is_fast_native = (access_flags & kAccFastNative) != 0u; // i.e. if the method was annotated with @CriticalNative - bool is_critical_native = (optimization_flags == Compiler::kCriticalNative); + bool is_critical_native = (access_flags & kAccCriticalNative) != 0u; VLOG(jni) << "JniCompile: Method :: " << dex_file.PrettyMethod(method_idx, /* with signature */ true) @@ -660,16 +656,12 @@ static CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver* driver, MemoryRegion code(&managed_code[0], managed_code.size()); __ FinalizeInstructions(code); - return CompiledMethod::SwapAllocCompiledMethod(driver, - instruction_set, - ArrayRef(managed_code), - frame_size, - main_jni_conv->CoreSpillMask(), - main_jni_conv->FpSpillMask(), - /* method_info */ ArrayRef(), - /* vmap_table */ ArrayRef(), - ArrayRef(*jni_asm->cfi().data()), - ArrayRef()); + return JniCompiledMethod(instruction_set, + std::move(managed_code), + frame_size, + main_jni_conv->CoreSpillMask(), + main_jni_conv->FpSpillMask(), + ArrayRef(*jni_asm->cfi().data())); } // Copy a single parameter from the managed to the JNI calling convention. @@ -778,17 +770,16 @@ static void SetNativeParameter(JNIMacroAssembler* jni_asm, } } -CompiledMethod* ArtQuickJniCompileMethod(CompilerDriver* compiler, - uint32_t access_flags, - uint32_t method_idx, - const DexFile& dex_file, - Compiler::JniOptimizationFlags optimization_flags) { +JniCompiledMethod ArtQuickJniCompileMethod(CompilerDriver* compiler, + uint32_t access_flags, + uint32_t method_idx, + const DexFile& dex_file) { if (Is64BitInstructionSet(compiler->GetInstructionSet())) { return ArtJniCompileMethodInternal( - compiler, access_flags, method_idx, dex_file, optimization_flags); + compiler, access_flags, method_idx, dex_file); } else { return ArtJniCompileMethodInternal( - compiler, access_flags, method_idx, dex_file, optimization_flags); + compiler, access_flags, method_idx, dex_file); } } diff --git a/compiler/jni/quick/jni_compiler.h b/compiler/jni/quick/jni_compiler.h index 26c32a31b8cc164386724716acee7aec44ed34a1..11419947a0ed652fcea2e98523577dcc404f2f2a 100644 --- a/compiler/jni/quick/jni_compiler.h +++ b/compiler/jni/quick/jni_compiler.h @@ -17,19 +17,55 @@ #ifndef ART_COMPILER_JNI_QUICK_JNI_COMPILER_H_ #define ART_COMPILER_JNI_QUICK_JNI_COMPILER_H_ -#include "compiler.h" -#include "dex_file.h" +#include + +#include "arch/instruction_set.h" +#include "base/array_ref.h" namespace art { +class ArtMethod; class CompilerDriver; -class CompiledMethod; +class DexFile; + +class JniCompiledMethod { + public: + JniCompiledMethod(InstructionSet instruction_set, + std::vector&& code, + uint32_t frame_size, + uint32_t core_spill_mask, + uint32_t fp_spill_mask, + ArrayRef cfi) + : instruction_set_(instruction_set), + code_(std::move(code)), + frame_size_(frame_size), + core_spill_mask_(core_spill_mask), + fp_spill_mask_(fp_spill_mask), + cfi_(cfi.begin(), cfi.end()) {} + + JniCompiledMethod(JniCompiledMethod&& other) = default; + ~JniCompiledMethod() = default; + + InstructionSet GetInstructionSet() const { return instruction_set_; } + ArrayRef GetCode() const { return ArrayRef(code_); } + uint32_t GetFrameSize() const { return frame_size_; } + uint32_t GetCoreSpillMask() const { return core_spill_mask_; } + uint32_t GetFpSpillMask() const { return fp_spill_mask_; } + ArrayRef GetCfi() const { return ArrayRef(cfi_); } + + private: + InstructionSet instruction_set_; + std::vector code_; + uint32_t frame_size_; + uint32_t core_spill_mask_; + uint32_t fp_spill_mask_; + std::vector cfi_; +}; -CompiledMethod* ArtQuickJniCompileMethod(CompilerDriver* compiler, - uint32_t access_flags, - uint32_t method_idx, - const DexFile& dex_file, - Compiler::JniOptimizationFlags optimization_flags); +JniCompiledMethod ArtQuickJniCompileMethod(CompilerDriver* compiler, + uint32_t access_flags, + uint32_t method_idx, + const DexFile& dex_file); } // namespace art diff --git a/compiler/jni/quick/mips/calling_convention_mips.cc b/compiler/jni/quick/mips/calling_convention_mips.cc index 0e0716e911220200683db03370f45e6dac475b0c..5ec1addcb9c5cadaec562983ea60c0bdd3e8ae4a 100644 --- a/compiler/jni/quick/mips/calling_convention_mips.cc +++ b/compiler/jni/quick/mips/calling_convention_mips.cc @@ -16,7 +16,8 @@ #include "calling_convention_mips.h" -#include "base/logging.h" +#include + #include "handle_scope-inl.h" #include "utils/mips/managed_register_mips.h" diff --git a/compiler/jni/quick/mips64/calling_convention_mips64.cc b/compiler/jni/quick/mips64/calling_convention_mips64.cc index afe6a762eb35c106988014342c4428bd9cd3717f..a7012aefa85f3ea585cfc5793cd9c8c5b3c17bed 100644 --- a/compiler/jni/quick/mips64/calling_convention_mips64.cc +++ b/compiler/jni/quick/mips64/calling_convention_mips64.cc @@ -16,7 +16,8 @@ #include "calling_convention_mips64.h" -#include "base/logging.h" +#include + #include "handle_scope-inl.h" #include "utils/mips64/managed_register_mips64.h" diff --git a/compiler/jni/quick/x86/calling_convention_x86.cc b/compiler/jni/quick/x86/calling_convention_x86.cc index 0bfcc3fb4d3b1ac76d4eb5bb1fb97ec56c083673..ad58e3820dafc0182f52fdce17a646dbc22ce275 100644 --- a/compiler/jni/quick/x86/calling_convention_x86.cc +++ b/compiler/jni/quick/x86/calling_convention_x86.cc @@ -16,7 +16,8 @@ #include "calling_convention_x86.h" -#include "base/logging.h" +#include + #include "handle_scope-inl.h" #include "utils/x86/managed_register_x86.h" diff --git a/compiler/jni/quick/x86_64/calling_convention_x86_64.cc b/compiler/jni/quick/x86_64/calling_convention_x86_64.cc index ba654f4750c018b82ee84392129cc3c947a1fa4f..e5e96d01fcf5b47249b2d7dbad417be1b4233d58 100644 --- a/compiler/jni/quick/x86_64/calling_convention_x86_64.cc +++ b/compiler/jni/quick/x86_64/calling_convention_x86_64.cc @@ -16,8 +16,9 @@ #include "calling_convention_x86_64.h" +#include + #include "base/bit_utils.h" -#include "base/logging.h" #include "handle_scope-inl.h" #include "utils/x86_64/managed_register_x86_64.h" diff --git a/compiler/linker/arm/relative_patcher_thumb2.cc b/compiler/linker/arm/relative_patcher_thumb2.cc index 48747fc379e19a36a9e2d5f71ff7a079506e71e6..78755176e43fed209627a9528cdb8edb4a2c151d 100644 --- a/compiler/linker/arm/relative_patcher_thumb2.cc +++ b/compiler/linker/arm/relative_patcher_thumb2.cc @@ -16,6 +16,8 @@ #include "linker/arm/relative_patcher_thumb2.h" +#include + #include "arch/arm/asm_support_arm.h" #include "art_method.h" #include "base/bit_utils.h" diff --git a/compiler/linker/elf_builder.h b/compiler/linker/elf_builder.h index b30b55e9b4ca563a5d692a61da879db8da69abfb..aa3cd98595b169b096b36acfe9da29d77ec11201 100644 --- a/compiler/linker/elf_builder.h +++ b/compiler/linker/elf_builder.h @@ -108,8 +108,6 @@ class ElfBuilder FINAL { section_index_(0), name_(name), link_(link), - started_(false), - finished_(false), phdr_flags_(PF_R), phdr_type_(0) { DCHECK_GE(align, 1u); @@ -120,90 +118,62 @@ class ElfBuilder FINAL { 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); + // Allocate chunk of virtual memory for this section from the owning ElfBuilder. + // This must be done at the start for all SHF_ALLOC sections (i.e. mmaped by linker). + // It is fine to allocate section but never call Start/End() (e.g. the .bss section). + void AllocateVirtualMemory(Elf_Word size) { + AllocateVirtualMemory(owner_->virtual_address_, size); } - // 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; - } + void AllocateVirtualMemory(Elf_Addr addr, Elf_Word size) { + CHECK_NE(header_.sh_flags & SHF_ALLOC, 0u); + Elf_Word align = AddSection(); + CHECK_EQ(header_.sh_addr, 0u); + header_.sh_addr = RoundUp(addr, align); + CHECK(header_.sh_size == 0u || header_.sh_size == size); + header_.sh_size = size; + CHECK_LE(owner_->virtual_address_, header_.sh_addr); + owner_->virtual_address_ = header_.sh_addr + header_.sh_size; } - // Get the location of this section in virtual memory. - Elf_Addr GetAddress() const { - CHECK(started_); - return header_.sh_addr; + // Start writing file data of this section. + void Start() { + CHECK(owner_->current_section_ == nullptr); + Elf_Word align = AddSection(); + CHECK_EQ(header_.sh_offset, 0u); + header_.sh_offset = owner_->AlignFileOffset(align); + owner_->current_section_ = this; } - // 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; - } + // Finish writing file data of this section. + void End() { + CHECK(owner_->current_section_ == this); + Elf_Word position = GetPosition(); + CHECK(header_.sh_size == 0u || header_.sh_size == position); + header_.sh_size = position; + owner_->current_section_ = nullptr; + } + + // Get the number of bytes written so far. + // Only valid while writing the section. + Elf_Word GetPosition() const { + CHECK(owner_->current_section_ == this); + off_t file_offset = owner_->stream_.Seek(0, kSeekCurrent); + DCHECK_GE(file_offset, (off_t)header_.sh_offset); + return file_offset - 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) { + // Get the location of this section in virtual memory. + Elf_Addr GetAddress() const { DCHECK_NE(header_.sh_flags & SHF_ALLOC, 0u); - header_.sh_type = SHT_NOBITS; - Start(); - header_.sh_size = size; - End(); + DCHECK_NE(header_.sh_addr, 0u); + return header_.sh_addr; } // 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_); + CHECK(owner_->current_section_ == this); return owner_->stream_.WriteFully(buffer, byte_count); } @@ -221,19 +191,32 @@ class ElfBuilder FINAL { } Elf_Word GetSectionIndex() const { - DCHECK(started_); DCHECK_NE(section_index_, 0u); return section_index_; } private: + // Add this section to the list of generated ELF sections (if not there already). + // It also ensures the alignment is sufficient to generate valid program headers, + // since that depends on the previous section. It returns the required alignment. + Elf_Word AddSection() { + if (section_index_ == 0) { + std::vector& sections = owner_->sections_; + Elf_Word last = sections.empty() ? PF_R : sections.back()->phdr_flags_; + if (phdr_flags_ != last) { + header_.sh_addralign = kPageSize; // Page-align if R/W/X flags changed. + } + sections.push_back(this); + section_index_ = sections.size(); // First ELF section has index 1. + } + return owner_->write_program_headers_ ? header_.sh_addralign : 1; + } + ElfBuilder* 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_; @@ -370,7 +353,7 @@ class ElfBuilder FINAL { Elf_Word section_index; if (section != nullptr) { DCHECK_LE(section->GetAddress(), addr); - DCHECK_LE(addr, section->GetAddress() + section->GetSize()); + DCHECK_LE(addr, section->GetAddress() + section->header_.sh_size); section_index = section->GetSectionIndex(); } else { section_index = static_cast(SHN_ABS); @@ -479,6 +462,10 @@ class ElfBuilder FINAL { digest_start_(-1) { } + Elf_Word GetSize() { + return 16 + kBuildIdLen; + } + 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 @@ -490,6 +477,7 @@ class ElfBuilder FINAL { 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. + DCHECK_EQ(this->GetPosition(), GetSize()); } off_t GetDigestStart() { @@ -530,6 +518,7 @@ class ElfBuilder FINAL { 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), + current_section_(nullptr), started_(false), write_program_headers_(false), loaded_size_(0u), @@ -545,6 +534,7 @@ class ElfBuilder FINAL { ~ElfBuilder() {} InstructionSet GetIsa() { return isa_; } + BuildIdSection* GetBuildId() { return &build_id_; } Section* GetRoData() { return &rodata_; } Section* GetText() { return &text_; } Section* GetBss() { return &bss_; } @@ -622,6 +612,9 @@ class ElfBuilder FINAL { if (section->link_ != nullptr) { section->header_.sh_link = section->link_->GetSectionIndex(); } + if (section->header_.sh_offset == 0) { + section->header_.sh_type = SHT_NOBITS; + } } shstrtab_.End(); @@ -680,65 +673,57 @@ class ElfBuilder FINAL { soname = soname.substr(directory_separator_pos + 1); } - // Calculate addresses of .text, .bss and .dynstr. - DCHECK_EQ(rodata_.header_.sh_addralign, static_cast(kPageSize)); - DCHECK_EQ(text_.header_.sh_addralign, static_cast(kPageSize)); - DCHECK_EQ(bss_.header_.sh_addralign, static_cast(kPageSize)); - DCHECK_EQ(dynstr_.header_.sh_addralign, static_cast(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; + // Allocate all pre-dynamic sections. + rodata_.AllocateVirtualMemory(rodata_size); + text_.AllocateVirtualMemory(text_size); + if (bss_size != 0) { + bss_.AllocateVirtualMemory(bss_size); + } if (isa_ == InstructionSet::kMips || isa_ == InstructionSet::kMips64) { - abiflags_size = abiflags_.GetSize(); + abiflags_.AllocateVirtualMemory(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); + dynsym_.Add(oatdata, &rodata_, rodata_.GetAddress(), 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); + dynsym_.Add(oatexec, &text_, text_.GetAddress(), 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); + Elf_Word oatlastword_address = text_.GetAddress() + text_size - 4; + dynsym_.Add(oatlastword, &text_, 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); + Elf_Word oatlastword_address = rodata_.GetAddress() + rodata_size - 4; + dynsym_.Add(oatlastword, &rodata_, 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); + dynsym_.Add(oatbss, &bss_, bss_.GetAddress(), 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_address = bss_.GetAddress() + 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); + oatbssroots, &bss_, 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_address = bss_.GetAddress() + 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); + oatbssroots, &bss_, 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 bsslastword_address = bss_.GetAddress() + bss_size - 4; + dynsym_.Add(oatbsslastword, &bss_, bsslastword_address, 4, STB_GLOBAL, STT_OBJECT); } Elf_Word soname_offset = dynstr_.Add(soname); @@ -759,28 +744,24 @@ class ElfBuilder FINAL { 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(kPageSize)); - Elf_Word dynamic_address = RoundUp(hash_address + dynsym_.GetCacheSize(), kPageSize); + // Allocate all remaining sections. + dynstr_.AllocateVirtualMemory(dynstr_.GetCacheSize()); + dynsym_.AllocateVirtualMemory(dynsym_.GetCacheSize()); + hash_.AllocateVirtualMemory(hash_.GetCacheSize()); Elf_Dyn dyns[] = { - { DT_HASH, { hash_address } }, - { DT_STRTAB, { dynstr_address } }, - { DT_SYMTAB, { dynsym_address } }, + { DT_HASH, { hash_.GetAddress() } }, + { DT_STRTAB, { dynstr_.GetAddress() } }, + { DT_SYMTAB, { dynsym_.GetAddress() } }, { DT_SYMENT, { sizeof(Elf_Sym) } }, { DT_STRSZ, { dynstr_.GetCacheSize() } }, { DT_SONAME, { soname_offset } }, { DT_NULL, { 0 } }, }; dynamic_.Add(&dyns, sizeof(dyns)); + dynamic_.AllocateVirtualMemory(dynamic_.GetCacheSize()); - loaded_size_ = RoundUp(dynamic_address + dynamic_.GetCacheSize(), kPageSize); + loaded_size_ = RoundUp(virtual_address_, kPageSize); } void WriteDynamicSection() { @@ -788,8 +769,6 @@ class ElfBuilder FINAL { dynsym_.WriteCachedSection(); hash_.WriteCachedSection(); dynamic_.WriteCachedSection(); - - CHECK_EQ(loaded_size_, RoundUp(dynamic_.GetAddress() + dynamic_.GetSize(), kPageSize)); } Elf_Word GetLoadedSize() { @@ -828,10 +807,6 @@ class ElfBuilder FINAL { 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(); @@ -902,7 +877,6 @@ class ElfBuilder FINAL { 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; } @@ -933,6 +907,7 @@ class ElfBuilder FINAL { for (auto* section : sections_) { const Elf_Shdr& shdr = section->header_; if ((shdr.sh_flags & SHF_ALLOC) != 0 && shdr.sh_size != 0) { + DCHECK(shdr.sh_addr != 0u) << "Allocate virtual memory for the section"; // 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. @@ -1010,6 +985,7 @@ class ElfBuilder FINAL { // List of used section in the order in which they were written. std::vector sections_; + Section* current_section_; // The section which is currently being written. bool started_; bool write_program_headers_; diff --git a/compiler/linker/error_delaying_output_stream.h b/compiler/linker/error_delaying_output_stream.h index 33e6b5ab2329bc612995169aa31526ff23a8e595..659f1dc0937a846824872c569cde194b9430248e 100644 --- a/compiler/linker/error_delaying_output_stream.h +++ b/compiler/linker/error_delaying_output_stream.h @@ -19,7 +19,9 @@ #include "output_stream.h" -#include "base/logging.h" +#include + +#include "base/macros.h" namespace art { namespace linker { diff --git a/compiler/linker/linker_patch.h b/compiler/linker/linker_patch.h index 0ac149029a7e3e433b89e224b2fa9e1de7043fe1..6f4e7746a6c62bd4f0dbf4f03a37f607d2ba0fd5 100644 --- a/compiler/linker/linker_patch.h +++ b/compiler/linker/linker_patch.h @@ -20,8 +20,9 @@ #include #include +#include + #include "base/bit_utils.h" -#include "base/logging.h" #include "method_reference.h" namespace art { diff --git a/compiler/linker/method_bss_mapping_encoder.h b/compiler/linker/method_bss_mapping_encoder.h deleted file mode 100644 index b2922ec6d2165bd80caa64c876ab2f70b819cbb5..0000000000000000000000000000000000000000 --- a/compiler/linker/method_bss_mapping_encoder.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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_LINKER_METHOD_BSS_MAPPING_ENCODER_H_ -#define ART_COMPILER_LINKER_METHOD_BSS_MAPPING_ENCODER_H_ - -#include "base/enums.h" -#include "base/logging.h" -#include "dex_file.h" -#include "method_bss_mapping.h" - -namespace art { -namespace linker { - -// Helper class for encoding compressed MethodBssMapping. -class MethodBssMappingEncoder { - public: - explicit MethodBssMappingEncoder(PointerSize pointer_size) - : pointer_size_(static_cast(pointer_size)) { - entry_.method_index = DexFile::kDexNoIndex16; - entry_.index_mask = 0u; - entry_.bss_offset = static_cast(-1); - } - - // Try to merge the next method_index -> bss_offset mapping into the current entry. - // Return true on success, false on failure. - bool TryMerge(uint32_t method_index, uint32_t bss_offset) { - DCHECK_NE(method_index, entry_.method_index); - if (entry_.bss_offset + pointer_size_ != bss_offset) { - return false; - } - uint32_t diff = method_index - entry_.method_index; - if (diff > 16u) { - return false; - } - if ((entry_.index_mask & ~(static_cast(-1) << diff)) != 0u) { - return false; - } - entry_.method_index = method_index; - // Insert the bit indicating the method index we've just overwritten - // and shift bits indicating method indexes before that. - entry_.index_mask = dchecked_integral_cast( - (static_cast(entry_.index_mask) | 0x10000u) >> diff); - entry_.bss_offset = bss_offset; - return true; - } - - void Reset(uint32_t method_index, uint32_t bss_offset) { - entry_.method_index = method_index; - entry_.index_mask = 0u; - entry_.bss_offset = bss_offset; - } - - MethodBssMappingEntry GetEntry() { - return entry_; - } - - private: - size_t pointer_size_; - MethodBssMappingEntry entry_; -}; - -} // namespace linker -} // namespace art - -#endif // ART_COMPILER_LINKER_METHOD_BSS_MAPPING_ENCODER_H_ diff --git a/compiler/linker/method_bss_mapping_encoder_test.cc b/compiler/linker/method_bss_mapping_encoder_test.cc deleted file mode 100644 index 1240389bef978b58dc1a73eed81ad8b999bc464e..0000000000000000000000000000000000000000 --- a/compiler/linker/method_bss_mapping_encoder_test.cc +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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 "method_bss_mapping_encoder.h" - -#include "gtest/gtest.h" - -namespace art { -namespace linker { - -TEST(MethodBssMappingEncoder, TryMerge) { - for (PointerSize pointer_size : {PointerSize::k32, PointerSize::k64}) { - size_t raw_pointer_size = static_cast(pointer_size); - MethodBssMappingEncoder encoder(pointer_size); - encoder.Reset(1u, 0u); - ASSERT_FALSE(encoder.TryMerge(5u, raw_pointer_size + 1)); // Wrong bss_offset difference. - ASSERT_FALSE(encoder.TryMerge(18u, raw_pointer_size)); // Method index out of range. - ASSERT_TRUE(encoder.TryMerge(5u, raw_pointer_size)); - ASSERT_TRUE(encoder.GetEntry().CoversIndex(1u)); - ASSERT_TRUE(encoder.GetEntry().CoversIndex(5u)); - ASSERT_FALSE(encoder.GetEntry().CoversIndex(17u)); - ASSERT_FALSE(encoder.TryMerge(17u, 2 * raw_pointer_size + 1)); // Wrong bss_offset difference. - ASSERT_FALSE(encoder.TryMerge(18u, 2 * raw_pointer_size)); // Method index out of range. - ASSERT_TRUE(encoder.TryMerge(17u, 2 * raw_pointer_size)); - ASSERT_TRUE(encoder.GetEntry().CoversIndex(1u)); - ASSERT_TRUE(encoder.GetEntry().CoversIndex(5u)); - ASSERT_TRUE(encoder.GetEntry().CoversIndex(17u)); - ASSERT_EQ(0u, encoder.GetEntry().GetBssOffset(1u, raw_pointer_size)); - ASSERT_EQ(raw_pointer_size, encoder.GetEntry().GetBssOffset(5u, raw_pointer_size)); - ASSERT_EQ(2 * raw_pointer_size, encoder.GetEntry().GetBssOffset(17u, raw_pointer_size)); - ASSERT_EQ(0x0011u, encoder.GetEntry().index_mask); - ASSERT_FALSE(encoder.TryMerge(18u, 2 * raw_pointer_size)); // Method index out of range. - } -} - -} // namespace linker -} // namespace art diff --git a/compiler/linker/output_stream_test.cc b/compiler/linker/output_stream_test.cc index ad298406be62d7ed1d6947f6c5983d80e5b9813b..f93ea7a7094ca279efc93c0e2d3047f17e97c6fe 100644 --- a/compiler/linker/output_stream_test.cc +++ b/compiler/linker/output_stream_test.cc @@ -17,7 +17,9 @@ #include "file_output_stream.h" #include "vector_output_stream.h" -#include "base/logging.h" +#include + +#include "base/macros.h" #include "base/unix_file/fd_file.h" #include "buffered_output_stream.h" #include "common_runtime_test.h" diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h index 6297dd048140fbdfe27f8b00e83ee28335dea6c0..9e9d14af9e51233b0f5d360921528088198943eb 100644 --- a/compiler/linker/relative_patcher_test.h +++ b/compiler/linker/relative_patcher_test.h @@ -52,9 +52,6 @@ class RelativePatcherTest : public testing::Test { /* compiled_classes */ nullptr, /* compiled_methods */ nullptr, /* thread_count */ 1u, - /* dump_stats */ false, - /* dump_passes */ false, - /* timer */ nullptr, /* swap_fd */ -1, /* profile_compilation_info */ nullptr), error_msg_(), diff --git a/compiler/linker/vector_output_stream.cc b/compiler/linker/vector_output_stream.cc index 75f90e5f94a3b85a3a4712005a34bba040f5a292..f2cae5b1d518cccd5c2e2dd01a114883974114e2 100644 --- a/compiler/linker/vector_output_stream.cc +++ b/compiler/linker/vector_output_stream.cc @@ -16,7 +16,7 @@ #include "vector_output_stream.h" -#include "base/logging.h" +#include namespace art { namespace linker { diff --git a/compiler/optimizing/block_builder.cc b/compiler/optimizing/block_builder.cc index 595dd4d22660932377e3d27be2c14440093c3c83..c505efafe2fc7dc57d5f08cfc011c8ab7f18b3bf 100644 --- a/compiler/optimizing/block_builder.cc +++ b/compiler/optimizing/block_builder.cc @@ -16,11 +16,33 @@ #include "block_builder.h" +#include "base/logging.h" // FOR VLOG. #include "bytecode_utils.h" +#include "code_item_accessors-inl.h" #include "quicken_info.h" namespace art { +HBasicBlockBuilder::HBasicBlockBuilder(HGraph* graph, + const DexFile* const dex_file, + const CodeItemDebugInfoAccessor& accessor, + ScopedArenaAllocator* local_allocator) + : allocator_(graph->GetAllocator()), + graph_(graph), + dex_file_(dex_file), + code_item_accessor_(accessor), + local_allocator_(local_allocator), + branch_targets_(code_item_accessor_.HasCodeItem() + ? code_item_accessor_.InsnsSizeInCodeUnits() + : /* fake dex_pc=0 for intrinsic graph */ 1u, + nullptr, + local_allocator->Adapter(kArenaAllocGraphBuilder)), + throwing_blocks_(kDefaultNumberOfThrowingBlocks, + local_allocator->Adapter(kArenaAllocGraphBuilder)), + number_of_branches_(0u), + quicken_index_for_dex_pc_(std::less(), + local_allocator->Adapter(kArenaAllocGraphBuilder)) {} + HBasicBlock* HBasicBlockBuilder::MaybeCreateBlockAt(uint32_t dex_pc) { return MaybeCreateBlockAt(dex_pc, dex_pc); } @@ -40,30 +62,30 @@ bool HBasicBlockBuilder::CreateBranchTargets() { // Create the first block for the dex instructions, single successor of the entry block. MaybeCreateBlockAt(0u); - if (code_item_.tries_size_ != 0) { + if (code_item_accessor_.TriesSize() != 0) { // Create branch targets at the start/end of the TryItem range. These are // places where the program might fall through into/out of the a block and // where TryBoundary instructions will be inserted later. Other edges which // enter/exit the try blocks are a result of branches/switches. - for (size_t idx = 0; idx < code_item_.tries_size_; ++idx) { - const DexFile::TryItem* try_item = DexFile::GetTryItems(code_item_, idx); - uint32_t dex_pc_start = try_item->start_addr_; - uint32_t dex_pc_end = dex_pc_start + try_item->insn_count_; + for (const DexFile::TryItem& try_item : code_item_accessor_.TryItems()) { + uint32_t dex_pc_start = try_item.start_addr_; + uint32_t dex_pc_end = dex_pc_start + try_item.insn_count_; MaybeCreateBlockAt(dex_pc_start); - if (dex_pc_end < code_item_.insns_size_in_code_units_) { + if (dex_pc_end < code_item_accessor_.InsnsSizeInCodeUnits()) { // TODO: Do not create block if the last instruction cannot fall through. MaybeCreateBlockAt(dex_pc_end); - } else if (dex_pc_end == code_item_.insns_size_in_code_units_) { + } else if (dex_pc_end == code_item_accessor_.InsnsSizeInCodeUnits()) { // The TryItem spans until the very end of the CodeItem and therefore // cannot have any code afterwards. } else { // The TryItem spans beyond the end of the CodeItem. This is invalid code. + VLOG(compiler) << "Not compiled: TryItem spans beyond the end of the CodeItem"; return false; } } // Create branch targets for exception handlers. - const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(code_item_, 0); + const uint8_t* handlers_ptr = code_item_accessor_.GetCatchHandlerData(); uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr); for (uint32_t idx = 0; idx < handlers_size; ++idx) { CatchHandlerIterator iterator(handlers_ptr); @@ -76,8 +98,7 @@ bool HBasicBlockBuilder::CreateBranchTargets() { // Iterate over all instructions and find branching instructions. Create blocks for // the locations these instructions branch to. - IterationRange instructions = code_item_.Instructions(); - for (const DexInstructionPcPair& pair : instructions) { + for (const DexInstructionPcPair& pair : code_item_accessor_) { const uint32_t dex_pc = pair.DexPc(); const Instruction& instruction = pair.Inst(); @@ -107,9 +128,10 @@ bool HBasicBlockBuilder::CreateBranchTargets() { if (instruction.CanFlowThrough()) { DexInstructionIterator next(std::next(DexInstructionIterator(pair))); - if (next == instructions.end()) { + if (next == code_item_accessor_.end()) { // In the normal case we should never hit this but someone can artificially forge a dex // file to fall-through out the method code. In this case we bail out compilation. + VLOG(compiler) << "Not compiled: Fall-through beyond the CodeItem"; return false; } MaybeCreateBlockAt(next.DexPc()); @@ -127,7 +149,7 @@ void HBasicBlockBuilder::ConnectBasicBlocks() { bool is_throwing_block = false; // Calculate the qucikening index here instead of CreateBranchTargets since it's easier to // calculate in dex_pc order. - for (const DexInstructionPcPair& pair : code_item_.Instructions()) { + for (const DexInstructionPcPair& pair : code_item_accessor_) { const uint32_t dex_pc = pair.DexPc(); const Instruction& instruction = pair.Inst(); @@ -210,10 +232,12 @@ static const DexFile::TryItem* GetTryItem( // successors matches the order in which runtime exception delivery searches // for a handler. static void LinkToCatchBlocks(HTryBoundary* try_boundary, - const DexFile::CodeItem& code_item, + const CodeItemDataAccessor& accessor, const DexFile::TryItem* try_item, const ScopedArenaSafeMap& catch_blocks) { - for (CatchHandlerIterator it(code_item, *try_item); it.HasNext(); it.Next()) { + for (CatchHandlerIterator it(accessor.GetCatchHandlerData(try_item->handler_off_)); + it.HasNext(); + it.Next()) { try_boundary->AddExceptionHandler(catch_blocks.Get(it.GetHandlerAddress())); } } @@ -229,7 +253,7 @@ bool HBasicBlockBuilder::MightHaveLiveNormalPredecessors(HBasicBlock* catch_bloc } } - const Instruction& first = code_item_.InstructionAt(catch_block->GetDexPc()); + const Instruction& first = code_item_accessor_.InstructionAt(catch_block->GetDexPc()); if (first.Opcode() == Instruction::MOVE_EXCEPTION) { // Verifier guarantees that if a catch block begins with MOVE_EXCEPTION then // it has no live normal predecessors. @@ -247,7 +271,7 @@ bool HBasicBlockBuilder::MightHaveLiveNormalPredecessors(HBasicBlock* catch_bloc } void HBasicBlockBuilder::InsertTryBoundaryBlocks() { - if (code_item_.tries_size_ == 0) { + if (code_item_accessor_.TriesSize() == 0) { return; } @@ -269,10 +293,10 @@ void HBasicBlockBuilder::InsertTryBoundaryBlocks() { // loop for synchronized blocks. if (ContainsElement(throwing_blocks_, block)) { // Try to find a TryItem covering the block. - const int32_t try_item_idx = DexFile::FindTryItem(code_item_, block->GetDexPc()); - if (try_item_idx != -1) { + const DexFile::TryItem* try_item = code_item_accessor_.FindTryItem(block->GetDexPc()); + if (try_item != nullptr) { // Block throwing and in a TryItem. Store the try block information. - try_block_info.Put(block->GetBlockId(), DexFile::GetTryItems(code_item_, try_item_idx)); + try_block_info.Put(block->GetBlockId(), try_item); } } } @@ -283,7 +307,7 @@ void HBasicBlockBuilder::InsertTryBoundaryBlocks() { // Iterate over catch blocks, create artifical landing pads if necessary to // simplify the CFG, and set metadata. - const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(code_item_, 0); + const uint8_t* handlers_ptr = code_item_accessor_.GetCatchHandlerData(); uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr); for (uint32_t idx = 0; idx < handlers_size; ++idx) { CatchHandlerIterator iterator(handlers_ptr); @@ -331,7 +355,7 @@ void HBasicBlockBuilder::InsertTryBoundaryBlocks() { HTryBoundary* try_entry = new (allocator_) HTryBoundary( HTryBoundary::BoundaryKind::kEntry, try_block->GetDexPc()); try_block->CreateImmediateDominator()->AddInstruction(try_entry); - LinkToCatchBlocks(try_entry, code_item_, try_item, catch_blocks); + LinkToCatchBlocks(try_entry, code_item_accessor_, try_item, catch_blocks); break; } } @@ -359,12 +383,13 @@ void HBasicBlockBuilder::InsertTryBoundaryBlocks() { HTryBoundary* try_exit = new (allocator_) HTryBoundary(HTryBoundary::BoundaryKind::kExit, successor->GetDexPc()); graph_->SplitEdge(try_block, successor)->AddInstruction(try_exit); - LinkToCatchBlocks(try_exit, code_item_, try_item, catch_blocks); + LinkToCatchBlocks(try_exit, code_item_accessor_, try_item, catch_blocks); } } } bool HBasicBlockBuilder::Build() { + DCHECK(code_item_accessor_.HasCodeItem()); DCHECK(graph_->GetBlocks().empty()); graph_->SetEntryBlock(new (allocator_) HBasicBlock(graph_, kNoDexPc)); @@ -381,6 +406,27 @@ bool HBasicBlockBuilder::Build() { return true; } +void HBasicBlockBuilder::BuildIntrinsic() { + DCHECK(!code_item_accessor_.HasCodeItem()); + DCHECK(graph_->GetBlocks().empty()); + + // Create blocks. + HBasicBlock* entry_block = new (allocator_) HBasicBlock(graph_, kNoDexPc); + HBasicBlock* exit_block = new (allocator_) HBasicBlock(graph_, kNoDexPc); + HBasicBlock* body = MaybeCreateBlockAt(/* semantic_dex_pc */ kNoDexPc, /* store_dex_pc */ 0u); + + // Add blocks to the graph. + graph_->AddBlock(entry_block); + graph_->AddBlock(body); + graph_->AddBlock(exit_block); + graph_->SetEntryBlock(entry_block); + graph_->SetExitBlock(exit_block); + + // Connect blocks. + entry_block->AddSuccessor(body); + body->AddSuccessor(exit_block); +} + size_t HBasicBlockBuilder::GetQuickenIndex(uint32_t dex_pc) const { return quicken_index_for_dex_pc_.Get(dex_pc); } diff --git a/compiler/optimizing/block_builder.h b/compiler/optimizing/block_builder.h index 79f7a7bc8124dde88278e118531258a35743b893..e68b95c46fbf2ad2388ae55c35b1ca7de62d72a7 100644 --- a/compiler/optimizing/block_builder.h +++ b/compiler/optimizing/block_builder.h @@ -19,6 +19,7 @@ #include "base/scoped_arena_allocator.h" #include "base/scoped_arena_containers.h" +#include "code_item_accessors.h" #include "dex_file.h" #include "nodes.h" @@ -28,21 +29,8 @@ class HBasicBlockBuilder : public ValueObject { public: HBasicBlockBuilder(HGraph* graph, const DexFile* const dex_file, - const DexFile::CodeItem& code_item, - ScopedArenaAllocator* local_allocator) - : allocator_(graph->GetAllocator()), - graph_(graph), - dex_file_(dex_file), - code_item_(code_item), - local_allocator_(local_allocator), - branch_targets_(code_item.insns_size_in_code_units_, - nullptr, - local_allocator->Adapter(kArenaAllocGraphBuilder)), - throwing_blocks_(kDefaultNumberOfThrowingBlocks, - local_allocator->Adapter(kArenaAllocGraphBuilder)), - number_of_branches_(0u), - quicken_index_for_dex_pc_(std::less(), - local_allocator->Adapter(kArenaAllocGraphBuilder)) {} + const CodeItemDebugInfoAccessor& accessor, + ScopedArenaAllocator* local_allocator); // Creates basic blocks in `graph_` at branch target dex_pc positions of the // `code_item_`. Blocks are connected but left unpopulated with instructions. @@ -50,6 +38,9 @@ class HBasicBlockBuilder : public ValueObject { // exits a try block. bool Build(); + // Creates basic blocks in `graph_` for compiling an intrinsic. + void BuildIntrinsic(); + size_t GetNumberOfBranches() const { return number_of_branches_; } HBasicBlock* GetBlockAt(uint32_t dex_pc) const { return branch_targets_[dex_pc]; } @@ -79,7 +70,7 @@ class HBasicBlockBuilder : public ValueObject { HGraph* const graph_; const DexFile* const dex_file_; - const DexFile::CodeItem& code_item_; + CodeItemDataAccessor code_item_accessor_; // null code item for intrinsic graph. ScopedArenaAllocator* const local_allocator_; ScopedArenaVector branch_targets_; diff --git a/compiler/optimizing/bounds_check_elimination.h b/compiler/optimizing/bounds_check_elimination.h index 6dc53207ead84f350caf96a84583d637b4819664..79c67a8c7ade8dde7775f5ed44a9f71466030a5e 100644 --- a/compiler/optimizing/bounds_check_elimination.h +++ b/compiler/optimizing/bounds_check_elimination.h @@ -28,8 +28,9 @@ class BoundsCheckElimination : public HOptimization { public: BoundsCheckElimination(HGraph* graph, const SideEffectsAnalysis& side_effects, - HInductionVarAnalysis* induction_analysis) - : HOptimization(graph, kBoundsCheckEliminationPassName), + HInductionVarAnalysis* induction_analysis, + const char* name = kBoundsCheckEliminationPassName) + : HOptimization(graph, name), side_effects_(side_effects), induction_analysis_(induction_analysis) {} diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 4ed1612220514829445fd4b4872c4ccb89f4fb50..af537dd653a8a7c55be66c405ccc895d0623e915 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -24,6 +24,7 @@ #include "data_type-inl.h" #include "dex/verified_method.h" #include "driver/compiler_options.h" +#include "driver/dex_compilation_unit.h" #include "instruction_builder.h" #include "mirror/class_loader.h" #include "mirror/dex_cache.h" @@ -36,6 +37,7 @@ namespace art { HGraphBuilder::HGraphBuilder(HGraph* graph, + const CodeItemDebugInfoAccessor& accessor, const DexCompilationUnit* dex_compilation_unit, const DexCompilationUnit* outer_compilation_unit, CompilerDriver* driver, @@ -45,7 +47,7 @@ HGraphBuilder::HGraphBuilder(HGraph* graph, VariableSizedHandleScope* handles) : graph_(graph), dex_file_(&graph->GetDexFile()), - code_item_(*dex_compilation_unit->GetCodeItem()), + code_item_accessor_(accessor), dex_compilation_unit_(dex_compilation_unit), outer_compilation_unit_(outer_compilation_unit), compiler_driver_(driver), @@ -55,6 +57,23 @@ HGraphBuilder::HGraphBuilder(HGraph* graph, handles_(handles), return_type_(DataType::FromShorty(dex_compilation_unit_->GetShorty()[0])) {} +HGraphBuilder::HGraphBuilder(HGraph* graph, + const DexCompilationUnit* dex_compilation_unit, + const CodeItemDebugInfoAccessor& accessor, + VariableSizedHandleScope* handles, + DataType::Type return_type) + : graph_(graph), + dex_file_(&graph->GetDexFile()), + code_item_accessor_(accessor), + dex_compilation_unit_(dex_compilation_unit), + outer_compilation_unit_(nullptr), + compiler_driver_(nullptr), + code_generator_(nullptr), + compilation_stats_(nullptr), + interpreter_metadata_(nullptr), + handles_(handles), + return_type_(return_type) {} + bool HGraphBuilder::SkipCompilation(size_t number_of_branches) { if (compiler_driver_ == nullptr) { // Note that the compiler driver is null when unit testing. @@ -67,23 +86,21 @@ bool HGraphBuilder::SkipCompilation(size_t number_of_branches) { return false; } - if (compiler_options.IsHugeMethod(code_item_.insns_size_in_code_units_)) { + const uint32_t code_units = code_item_accessor_.InsnsSizeInCodeUnits(); + if (compiler_options.IsHugeMethod(code_units)) { VLOG(compiler) << "Skip compilation of huge method " << dex_file_->PrettyMethod(dex_compilation_unit_->GetDexMethodIndex()) - << ": " << code_item_.insns_size_in_code_units_ << " code units"; - MaybeRecordStat(compilation_stats_, - MethodCompilationStat::kNotCompiledHugeMethod); + << ": " << code_units << " code units"; + MaybeRecordStat(compilation_stats_, MethodCompilationStat::kNotCompiledHugeMethod); return true; } // If it's large and contains no branches, it's likely to be machine generated initialization. - if (compiler_options.IsLargeMethod(code_item_.insns_size_in_code_units_) - && (number_of_branches == 0)) { + if (compiler_options.IsLargeMethod(code_units) && (number_of_branches == 0)) { VLOG(compiler) << "Skip compilation of large method with no branch " << dex_file_->PrettyMethod(dex_compilation_unit_->GetDexMethodIndex()) - << ": " << code_item_.insns_size_in_code_units_ << " code units"; - MaybeRecordStat(compilation_stats_, - MethodCompilationStat::kNotCompiledLargeMethodNoBranches); + << ": " << code_units << " code units"; + MaybeRecordStat(compilation_stats_, MethodCompilationStat::kNotCompiledLargeMethodNoBranches); return true; } @@ -91,16 +108,17 @@ bool HGraphBuilder::SkipCompilation(size_t number_of_branches) { } GraphAnalysisResult HGraphBuilder::BuildGraph() { + DCHECK(code_item_accessor_.HasCodeItem()); DCHECK(graph_->GetBlocks().empty()); - graph_->SetNumberOfVRegs(code_item_.registers_size_); - graph_->SetNumberOfInVRegs(code_item_.ins_size_); - graph_->SetMaximumNumberOfOutVRegs(code_item_.outs_size_); - graph_->SetHasTryCatch(code_item_.tries_size_ != 0); + graph_->SetNumberOfVRegs(code_item_accessor_.RegistersSize()); + graph_->SetNumberOfInVRegs(code_item_accessor_.InsSize()); + graph_->SetMaximumNumberOfOutVRegs(code_item_accessor_.OutsSize()); + graph_->SetHasTryCatch(code_item_accessor_.TriesSize() != 0); // Use ScopedArenaAllocator for all local allocations. ScopedArenaAllocator local_allocator(graph_->GetArenaStack()); - HBasicBlockBuilder block_builder(graph_, dex_file_, code_item_, &local_allocator); + HBasicBlockBuilder block_builder(graph_, dex_file_, code_item_accessor_, &local_allocator); SsaBuilder ssa_builder(graph_, dex_compilation_unit_->GetClassLoader(), dex_compilation_unit_->GetDexCache(), @@ -110,7 +128,7 @@ GraphAnalysisResult HGraphBuilder::BuildGraph() { &block_builder, &ssa_builder, dex_file_, - code_item_, + code_item_accessor_, return_type_, dex_compilation_unit_, outer_compilation_unit_, @@ -148,4 +166,64 @@ GraphAnalysisResult HGraphBuilder::BuildGraph() { return ssa_builder.BuildSsa(); } +void HGraphBuilder::BuildIntrinsicGraph(ArtMethod* method) { + DCHECK(!code_item_accessor_.HasCodeItem()); + DCHECK(graph_->GetBlocks().empty()); + + // Determine the number of arguments and associated vregs. + uint32_t method_idx = dex_compilation_unit_->GetDexMethodIndex(); + const char* shorty = dex_file_->GetMethodShorty(dex_file_->GetMethodId(method_idx)); + size_t num_args = strlen(shorty + 1); + size_t num_wide_args = std::count(shorty + 1, shorty + 1 + num_args, 'J') + + std::count(shorty + 1, shorty + 1 + num_args, 'D'); + size_t num_arg_vregs = num_args + num_wide_args + (dex_compilation_unit_->IsStatic() ? 0u : 1u); + + // For simplicity, reserve 2 vregs (the maximum) for return value regardless of the return type. + size_t return_vregs = 2u; + graph_->SetNumberOfVRegs(return_vregs + num_arg_vregs); + graph_->SetNumberOfInVRegs(num_arg_vregs); + graph_->SetMaximumNumberOfOutVRegs(num_arg_vregs); + graph_->SetHasTryCatch(false); + + // Use ScopedArenaAllocator for all local allocations. + ScopedArenaAllocator local_allocator(graph_->GetArenaStack()); + HBasicBlockBuilder block_builder(graph_, + dex_file_, + CodeItemDebugInfoAccessor(), + &local_allocator); + SsaBuilder ssa_builder(graph_, + dex_compilation_unit_->GetClassLoader(), + dex_compilation_unit_->GetDexCache(), + handles_, + &local_allocator); + HInstructionBuilder instruction_builder(graph_, + &block_builder, + &ssa_builder, + dex_file_, + CodeItemDebugInfoAccessor(), + return_type_, + dex_compilation_unit_, + outer_compilation_unit_, + compiler_driver_, + code_generator_, + interpreter_metadata_, + compilation_stats_, + handles_, + &local_allocator); + + // 1) Create basic blocks for the intrinsic and link them together. + block_builder.BuildIntrinsic(); + + // 2) Build the trivial dominator tree. + GraphAnalysisResult bdt_result = graph_->BuildDominatorTree(); + DCHECK_EQ(bdt_result, kAnalysisSuccess); + + // 3) Populate basic blocks with instructions for the intrinsic. + instruction_builder.BuildIntrinsic(method); + + // 4) Type the graph (no dead/redundant phis to eliminate). + GraphAnalysisResult build_ssa_result = ssa_builder.BuildSsa(); + DCHECK_EQ(build_ssa_result, kAnalysisSuccess); +} + } // namespace art diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index 5a860f1e43d4976ccafa366a53da74fe061d74a6..c40e0b4e6ad20f959fa39e210978f287728293e1 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -18,20 +18,23 @@ #define ART_COMPILER_OPTIMIZING_BUILDER_H_ #include "base/arena_object.h" +#include "code_item_accessors.h" #include "dex_file-inl.h" #include "dex_file.h" #include "driver/compiler_driver.h" -#include "driver/dex_compilation_unit.h" #include "nodes.h" namespace art { +class ArtMethod; class CodeGenerator; +class DexCompilationUnit; class OptimizingCompilerStats; class HGraphBuilder : public ValueObject { public: HGraphBuilder(HGraph* graph, + const CodeItemDebugInfoAccessor& accessor, const DexCompilationUnit* dex_compilation_unit, const DexCompilationUnit* outer_compilation_unit, CompilerDriver* driver, @@ -43,22 +46,12 @@ class HGraphBuilder : public ValueObject { // Only for unit testing. HGraphBuilder(HGraph* graph, const DexCompilationUnit* dex_compilation_unit, - const DexFile::CodeItem& code_item, + const CodeItemDebugInfoAccessor& accessor, VariableSizedHandleScope* handles, - DataType::Type return_type = DataType::Type::kInt32) - : graph_(graph), - dex_file_(dex_compilation_unit->GetDexFile()), - code_item_(code_item), - dex_compilation_unit_(dex_compilation_unit), - outer_compilation_unit_(nullptr), - compiler_driver_(nullptr), - code_generator_(nullptr), - compilation_stats_(nullptr), - interpreter_metadata_(nullptr), - handles_(handles), - return_type_(return_type) {} + DataType::Type return_type = DataType::Type::kInt32); GraphAnalysisResult BuildGraph(); + void BuildIntrinsicGraph(ArtMethod* method); static constexpr const char* kBuilderPassName = "builder"; @@ -67,7 +60,7 @@ class HGraphBuilder : public ValueObject { HGraph* const graph_; const DexFile* const dex_file_; - const DexFile::CodeItem& code_item_; + const CodeItemDebugInfoAccessor code_item_accessor_; // null for intrinsic graph. // The compilation unit of the current method being compiled. Note that // it can be an inlined method. diff --git a/compiler/optimizing/cha_guard_optimization.h b/compiler/optimizing/cha_guard_optimization.h index ba0cdb81fdf6c33b0c555158ee0222d6c2acbc5a..f14e07bd6ccb0273ade8833f52404e7d4c93c848 100644 --- a/compiler/optimizing/cha_guard_optimization.h +++ b/compiler/optimizing/cha_guard_optimization.h @@ -26,8 +26,9 @@ namespace art { */ class CHAGuardOptimization : public HOptimization { public: - explicit CHAGuardOptimization(HGraph* graph) - : HOptimization(graph, kCHAGuardOptimizationPassName) {} + explicit CHAGuardOptimization(HGraph* graph, + const char* name = kCHAGuardOptimizationPassName) + : HOptimization(graph, name) {} void Run() OVERRIDE; diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 015a6a04d3fb85d15a1694d2982bb20011a86684..dee74e96dc1d8d107b112608f7194b0766e761dc 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -295,15 +295,6 @@ void CodeGenerator::EmitJitRootPatches(uint8_t* code ATTRIBUTE_UNUSED, DCHECK_EQ(code_generation_data_->GetNumberOfJitClassRoots(), 0u); } -size_t CodeGenerator::GetCacheOffset(uint32_t index) { - return sizeof(GcRoot) * index; -} - -size_t CodeGenerator::GetCachePointerOffset(uint32_t index) { - PointerSize pointer_size = InstructionSetPointerSize(GetInstructionSet()); - return static_cast(pointer_size) * index; -} - uint32_t CodeGenerator::GetArrayLengthOffset(HArrayLength* array_length) { return array_length->IsStringLength() ? mirror::String::CountOffset().Uint32Value() @@ -946,12 +937,12 @@ static void CheckLoopEntriesCanBeUsedForOsr(const HGraph& graph, void CodeGenerator::BuildStackMaps(MemoryRegion stack_map_region, MemoryRegion method_info_region, - const DexFile::CodeItem& code_item) { + const DexFile::CodeItem* code_item_for_osr_check) { StackMapStream* stack_map_stream = GetStackMapStream(); stack_map_stream->FillInCodeInfo(stack_map_region); stack_map_stream->FillInMethodInfo(method_info_region); - if (kIsDebugBuild) { - CheckLoopEntriesCanBeUsedForOsr(*graph_, CodeInfo(stack_map_region), code_item); + if (kIsDebugBuild && code_item_for_osr_check != nullptr) { + CheckLoopEntriesCanBeUsedForOsr(*graph_, CodeInfo(stack_map_region), *code_item_for_osr_check); } } @@ -981,21 +972,6 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, } } - uint32_t outer_dex_pc = dex_pc; - uint32_t outer_environment_size = 0; - uint32_t inlining_depth = 0; - if (instruction != nullptr) { - for (HEnvironment* environment = instruction->GetEnvironment(); - environment != nullptr; - environment = environment->GetParent()) { - outer_dex_pc = environment->GetDexPc(); - outer_environment_size = environment->Size(); - if (environment != instruction->GetEnvironment()) { - inlining_depth++; - } - } - } - // Collect PC infos for the mapping table. uint32_t native_pc = GetAssembler()->CodePosition(); @@ -1003,12 +979,12 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, if (instruction == nullptr) { // For stack overflow checks and native-debug-info entries without dex register // mapping (i.e. start of basic block or start of slow path). - stack_map_stream->BeginStackMapEntry(outer_dex_pc, native_pc, 0, 0, 0, 0); + stack_map_stream->BeginStackMapEntry(dex_pc, native_pc, 0, 0, 0, 0); stack_map_stream->EndStackMapEntry(); return; } - LocationSummary* locations = instruction->GetLocations(); + LocationSummary* locations = instruction->GetLocations(); uint32_t register_mask = locations->GetRegisterMask(); DCHECK_EQ(register_mask & ~locations->GetLiveRegisters()->GetCoreRegisters(), 0u); if (locations->OnlyCallsOnSlowPath()) { @@ -1023,22 +999,33 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, // The register mask must be a subset of callee-save registers. DCHECK_EQ(register_mask & core_callee_save_mask_, register_mask); } + + uint32_t outer_dex_pc = dex_pc; + uint32_t outer_environment_size = 0u; + uint32_t inlining_depth = 0; + HEnvironment* const environment = instruction->GetEnvironment(); + if (environment != nullptr) { + HEnvironment* outer_environment = environment; + while (outer_environment->GetParent() != nullptr) { + outer_environment = outer_environment->GetParent(); + ++inlining_depth; + } + outer_dex_pc = outer_environment->GetDexPc(); + outer_environment_size = outer_environment->Size(); + } stack_map_stream->BeginStackMapEntry(outer_dex_pc, native_pc, register_mask, locations->GetStackMask(), outer_environment_size, inlining_depth); - - HEnvironment* const environment = instruction->GetEnvironment(); EmitEnvironment(environment, slow_path); // Record invoke info, the common case for the trampoline is super and static invokes. Only // record these to reduce oat file size. if (kEnableDexLayoutOptimizations) { - if (environment != nullptr && - instruction->IsInvoke() && - instruction->IsInvokeStaticOrDirect()) { - HInvoke* const invoke = instruction->AsInvoke(); + if (instruction->IsInvokeStaticOrDirect()) { + HInvoke* const invoke = instruction->AsInvokeStaticOrDirect(); + DCHECK(environment != nullptr); stack_map_stream->AddInvoke(invoke->GetInvokeType(), invoke->GetDexMethodIndex()); } } @@ -1411,10 +1398,10 @@ LocationSummary* CodeGenerator::CreateThrowingSlowPathLocations(HInstruction* in void CodeGenerator::GenerateNullCheck(HNullCheck* instruction) { if (compiler_options_.GetImplicitNullChecks()) { - MaybeRecordStat(stats_, kImplicitNullCheckGenerated); + MaybeRecordStat(stats_, MethodCompilationStat::kImplicitNullCheckGenerated); GenerateImplicitNullCheck(instruction); } else { - MaybeRecordStat(stats_, kExplicitNullCheckGenerated); + MaybeRecordStat(stats_, MethodCompilationStat::kExplicitNullCheckGenerated); GenerateExplicitNullCheck(instruction); } } diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 18ad60db87eb6adbde25923e2c7e3f1d2f862631..3c5a37f958276efe0ff51ebc3452930f6b95cc60 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -346,7 +346,7 @@ class CodeGenerator : public DeletableArenaObject { void BuildStackMaps(MemoryRegion stack_map_region, MemoryRegion method_info_region, - const DexFile::CodeItem& code_item); + const DexFile::CodeItem* code_item_for_osr_check); void ComputeStackMapAndMethodInfoSize(size_t* stack_map_size, size_t* method_info_size); size_t GetNumberOfJitRoots() const; @@ -388,13 +388,6 @@ class CodeGenerator : public DeletableArenaObject { bool IsBlockedCoreRegister(size_t i) { return blocked_core_registers_[i]; } bool IsBlockedFloatingPointRegister(size_t i) { return blocked_fpu_registers_[i]; } - // Helper that returns the pointer offset of an index in an object array. - // Note: this method assumes we always have the same pointer size, regardless - // of the architecture. - static size_t GetCacheOffset(uint32_t index); - // Pointer variant for ArtMethod and ArtField arrays. - size_t GetCachePointerOffset(uint32_t index); - // Helper that returns the offset of the array's length field. // Note: Besides the normal arrays, we also use the HArrayLength for // accessing the String's `count` field in String intrinsics. @@ -412,6 +405,50 @@ class CodeGenerator : public DeletableArenaObject { Location to2, DataType::Type type2); + static bool InstanceOfNeedsReadBarrier(HInstanceOf* instance_of) { + // Used only for kExactCheck, kAbstractClassCheck, kClassHierarchyCheck and kArrayObjectCheck. + DCHECK(instance_of->GetTypeCheckKind() == TypeCheckKind::kExactCheck || + instance_of->GetTypeCheckKind() == TypeCheckKind::kAbstractClassCheck || + instance_of->GetTypeCheckKind() == TypeCheckKind::kClassHierarchyCheck || + instance_of->GetTypeCheckKind() == TypeCheckKind::kArrayObjectCheck) + << instance_of->GetTypeCheckKind(); + // If the target class is in the boot image, it's non-moveable and it doesn't matter + // if we compare it with a from-space or to-space reference, the result is the same. + // It's OK to traverse a class hierarchy jumping between from-space and to-space. + return kEmitCompilerReadBarrier && !instance_of->GetTargetClass()->IsInBootImage(); + } + + static ReadBarrierOption ReadBarrierOptionForInstanceOf(HInstanceOf* instance_of) { + return InstanceOfNeedsReadBarrier(instance_of) ? kWithReadBarrier : kWithoutReadBarrier; + } + + static bool IsTypeCheckSlowPathFatal(HCheckCast* check_cast) { + switch (check_cast->GetTypeCheckKind()) { + case TypeCheckKind::kExactCheck: + case TypeCheckKind::kAbstractClassCheck: + case TypeCheckKind::kClassHierarchyCheck: + case TypeCheckKind::kArrayObjectCheck: + case TypeCheckKind::kInterfaceCheck: { + bool needs_read_barrier = + kEmitCompilerReadBarrier && !check_cast->GetTargetClass()->IsInBootImage(); + // We do not emit read barriers for HCheckCast, so we can get false negatives + // and the slow path shall re-check and simply return if the cast is actually OK. + return !needs_read_barrier; + } + case TypeCheckKind::kArrayCheck: + case TypeCheckKind::kUnresolvedCheck: + return false; + } + LOG(FATAL) << "Unreachable"; + UNREACHABLE(); + } + + static LocationSummary::CallKind GetCheckCastCallKind(HCheckCast* check_cast) { + return (IsTypeCheckSlowPathFatal(check_cast) && !check_cast->CanThrowIntoCatchBlock()) + ? LocationSummary::kNoCall // In fact, call on a fatal (non-returning) slow path. + : LocationSummary::kCallOnSlowPath; + } + static bool StoreNeedsWriteBarrier(DataType::Type type, HInstruction* value) { // Check that null value is not represented as an integer constant. DCHECK(type != DataType::Type::kReference || !value->IsIntConstant()); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index a0cb43ee01b76ae1390280c1cecba2be709e23f2..f9dcb5d6ef4ab175a2b623c6dbf79e34056e2382 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -311,40 +311,23 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 { LoadClassSlowPathARM64(HLoadClass* cls, HInstruction* at, uint32_t dex_pc, - bool do_clinit, - vixl::aarch64::Register bss_entry_temp = vixl::aarch64::Register(), - vixl::aarch64::Label* bss_entry_adrp_label = nullptr) + bool do_clinit) : SlowPathCodeARM64(at), cls_(cls), dex_pc_(dex_pc), - do_clinit_(do_clinit), - bss_entry_temp_(bss_entry_temp), - bss_entry_adrp_label_(bss_entry_adrp_label) { + do_clinit_(do_clinit) { DCHECK(at->IsLoadClass() || at->IsClinitCheck()); } void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); Location out = locations->Out(); - constexpr bool call_saves_everything_except_r0_ip0 = (!kUseReadBarrier || kUseBakerReadBarrier); CodeGeneratorARM64* arm64_codegen = down_cast(codegen); - InvokeRuntimeCallingConvention calling_convention; - // For HLoadClass/kBssEntry/kSaveEverything, the page address of the entry is in a temp - // register, make sure it's not clobbered by the call or by saving/restoring registers. - DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_); - bool is_load_class_bss_entry = - (cls_ == instruction_) && (cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry); - if (is_load_class_bss_entry) { - DCHECK(bss_entry_temp_.IsValid()); - DCHECK(!bss_entry_temp_.Is(calling_convention.GetRegisterAt(0))); - DCHECK( - !UseScratchRegisterScope(arm64_codegen->GetVIXLAssembler()).IsAvailable(bss_entry_temp_)); - } - __ Bind(GetEntryLabel()); SaveLiveRegisters(codegen, locations); + InvokeRuntimeCallingConvention calling_convention; dex::TypeIndex type_index = cls_->GetTypeIndex(); __ Mov(calling_convention.GetRegisterAt(0).W(), type_index.index_); QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage @@ -363,26 +346,6 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 { arm64_codegen->MoveLocation(out, calling_convention.GetReturnLocation(type), type); } RestoreLiveRegisters(codegen, locations); - // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry. - if (is_load_class_bss_entry) { - DCHECK(out.IsValid()); - const DexFile& dex_file = cls_->GetDexFile(); - if (call_saves_everything_except_r0_ip0) { - // The class entry page address was preserved in bss_entry_temp_ thanks to kSaveEverything. - } else { - // For non-Baker read barrier, we need to re-calculate the address of the class entry page. - bss_entry_adrp_label_ = arm64_codegen->NewBssEntryTypePatch(dex_file, type_index); - arm64_codegen->EmitAdrpPlaceholder(bss_entry_adrp_label_, bss_entry_temp_); - } - vixl::aarch64::Label* strp_label = - arm64_codegen->NewBssEntryTypePatch(dex_file, type_index, bss_entry_adrp_label_); - { - SingleEmissionCheckScope guard(arm64_codegen->GetVIXLAssembler()); - __ Bind(strp_label); - __ str(RegisterFrom(locations->Out(), DataType::Type::kReference), - MemOperand(bss_entry_temp_, /* offset placeholder */ 0)); - } - } __ B(GetExitLabel()); } @@ -398,34 +361,23 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 { // Whether to initialize the class. const bool do_clinit_; - // For HLoadClass/kBssEntry, the temp register and the label of the ADRP where it was loaded. - vixl::aarch64::Register bss_entry_temp_; - vixl::aarch64::Label* bss_entry_adrp_label_; - DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARM64); }; class LoadStringSlowPathARM64 : public SlowPathCodeARM64 { public: - LoadStringSlowPathARM64(HLoadString* instruction, Register temp, vixl::aarch64::Label* adrp_label) - : SlowPathCodeARM64(instruction), - temp_(temp), - adrp_label_(adrp_label) {} + explicit LoadStringSlowPathARM64(HLoadString* instruction) + : SlowPathCodeARM64(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg())); CodeGeneratorARM64* arm64_codegen = down_cast(codegen); - InvokeRuntimeCallingConvention calling_convention; - // Make sure `temp_` is not clobbered by the call or by saving/restoring registers. - DCHECK(temp_.IsValid()); - DCHECK(!temp_.Is(calling_convention.GetRegisterAt(0))); - DCHECK(!UseScratchRegisterScope(arm64_codegen->GetVIXLAssembler()).IsAvailable(temp_)); - __ Bind(GetEntryLabel()); SaveLiveRegisters(codegen, locations); + InvokeRuntimeCallingConvention calling_convention; const dex::StringIndex string_index = instruction_->AsLoadString()->GetStringIndex(); __ Mov(calling_convention.GetRegisterAt(0).W(), string_index.index_); arm64_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this); @@ -435,33 +387,12 @@ class LoadStringSlowPathARM64 : public SlowPathCodeARM64 { RestoreLiveRegisters(codegen, locations); - // Store the resolved String to the BSS entry. - const DexFile& dex_file = instruction_->AsLoadString()->GetDexFile(); - if (!kUseReadBarrier || kUseBakerReadBarrier) { - // The string entry page address was preserved in temp_ thanks to kSaveEverything. - } else { - // For non-Baker read barrier, we need to re-calculate the address of the string entry page. - adrp_label_ = arm64_codegen->NewStringBssEntryPatch(dex_file, string_index); - arm64_codegen->EmitAdrpPlaceholder(adrp_label_, temp_); - } - vixl::aarch64::Label* strp_label = - arm64_codegen->NewStringBssEntryPatch(dex_file, string_index, adrp_label_); - { - SingleEmissionCheckScope guard(arm64_codegen->GetVIXLAssembler()); - __ Bind(strp_label); - __ str(RegisterFrom(locations->Out(), DataType::Type::kReference), - MemOperand(temp_, /* offset placeholder */ 0)); - } - __ B(GetExitLabel()); } const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathARM64"; } private: - const Register temp_; - vixl::aarch64::Label* adrp_label_; - DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARM64); }; @@ -547,7 +478,7 @@ class TypeCheckSlowPathARM64 : public SlowPathCodeARM64 { __ Bind(GetEntryLabel()); - if (!is_fatal_) { + if (!is_fatal_ || instruction_->CanThrowIntoCatchBlock()) { SaveLiveRegisters(codegen, locations); } @@ -3891,11 +3822,12 @@ void LocationsBuilderARM64::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kExactCheck: case TypeCheckKind::kAbstractClassCheck: case TypeCheckKind::kClassHierarchyCheck: - case TypeCheckKind::kArrayObjectCheck: - call_kind = - kEmitCompilerReadBarrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall; - baker_read_barrier_slow_path = kUseBakerReadBarrier; + case TypeCheckKind::kArrayObjectCheck: { + bool needs_read_barrier = CodeGenerator::InstanceOfNeedsReadBarrier(instruction); + call_kind = needs_read_barrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall; + baker_read_barrier_slow_path = kUseBakerReadBarrier && needs_read_barrier; break; + } case TypeCheckKind::kArrayCheck: case TypeCheckKind::kUnresolvedCheck: case TypeCheckKind::kInterfaceCheck: @@ -3944,13 +3876,15 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { switch (type_check_kind) { case TypeCheckKind::kExactCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); __ Cmp(out, cls); __ Cset(out, eq); if (zero.IsLinked()) { @@ -3960,13 +3894,15 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { } case TypeCheckKind::kAbstractClassCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); // If the class is abstract, we eagerly fetch the super class of the // object to avoid doing a comparison we know will fail. vixl::aarch64::Label loop, success; @@ -3976,7 +3912,7 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { out_loc, super_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); // If `out` is null, we use it for the result, and jump to `done`. __ Cbz(out, &done); __ Cmp(out, cls); @@ -3989,13 +3925,15 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { } case TypeCheckKind::kClassHierarchyCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); // Walk over the class hierarchy to find a match. vixl::aarch64::Label loop, success; __ Bind(&loop); @@ -4006,7 +3944,7 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { out_loc, super_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); __ Cbnz(out, &loop); // If `out` is null, we use it for the result, and jump to `done`. __ B(&done); @@ -4019,13 +3957,15 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { } case TypeCheckKind::kArrayObjectCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); // Do an exact check. vixl::aarch64::Label exact_check; __ Cmp(out, cls); @@ -4036,7 +3976,7 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { out_loc, component_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); // If `out` is null, we use it for the result, and jump to `done`. __ Cbz(out, &done); __ Ldrh(out, HeapOperand(out, primitive_offset)); @@ -4117,26 +4057,8 @@ void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) { } void LocationsBuilderARM64::VisitCheckCast(HCheckCast* instruction) { - LocationSummary::CallKind call_kind = LocationSummary::kNoCall; - bool throws_into_catch = instruction->CanThrowIntoCatchBlock(); - TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); - switch (type_check_kind) { - case TypeCheckKind::kExactCheck: - case TypeCheckKind::kAbstractClassCheck: - case TypeCheckKind::kClassHierarchyCheck: - case TypeCheckKind::kArrayObjectCheck: - call_kind = (throws_into_catch || kEmitCompilerReadBarrier) ? - LocationSummary::kCallOnSlowPath : - LocationSummary::kNoCall; // In fact, call on a fatal (non-returning) slow path. - break; - case TypeCheckKind::kArrayCheck: - case TypeCheckKind::kUnresolvedCheck: - case TypeCheckKind::kInterfaceCheck: - call_kind = LocationSummary::kCallOnSlowPath; - break; - } - + LocationSummary::CallKind call_kind = CodeGenerator::GetCheckCastCallKind(instruction); LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); @@ -4167,18 +4089,7 @@ void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) { const uint32_t object_array_data_offset = mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value(); - bool is_type_check_slow_path_fatal = false; - // Always false for read barriers since we may need to go to the entrypoint for non-fatal cases - // from false negatives. The false negatives may come from avoiding read barriers below. Avoiding - // read barriers is done for performance and code size reasons. - if (!kEmitCompilerReadBarrier) { - is_type_check_slow_path_fatal = - (type_check_kind == TypeCheckKind::kExactCheck || - type_check_kind == TypeCheckKind::kAbstractClassCheck || - type_check_kind == TypeCheckKind::kClassHierarchyCheck || - type_check_kind == TypeCheckKind::kArrayObjectCheck) && - !instruction->CanThrowIntoCatchBlock(); - } + bool is_type_check_slow_path_fatal = CodeGenerator::IsTypeCheckSlowPathFatal(instruction); SlowPathCodeARM64* type_check_slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathARM64( instruction, is_type_check_slow_path_fatal); @@ -4883,7 +4794,6 @@ void LocationsBuilderARM64::VisitLoadClass(HLoadClass* cls) { if (cls->GetLoadKind() == HLoadClass::LoadKind::kBssEntry) { if (!kUseReadBarrier || kUseBakerReadBarrier) { // Rely on the type resolution or initialization and marking to save everything we need. - locations->AddTemp(FixedTempLocation()); RegisterSet caller_saves = RegisterSet::Empty(); InvokeRuntimeCallingConvention calling_convention; caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0).GetCode())); @@ -4910,8 +4820,6 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA Location out_loc = cls->GetLocations()->Out(); Register out = OutputRegister(cls); - Register bss_entry_temp; - vixl::aarch64::Label* bss_entry_adrp_label = nullptr; const ReadBarrierOption read_barrier_option = cls->IsInBootImage() ? kWithoutReadBarrier @@ -4975,16 +4883,16 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA // Add ADRP with its PC-relative Class .bss entry patch. const DexFile& dex_file = cls->GetDexFile(); dex::TypeIndex type_index = cls->GetTypeIndex(); - bss_entry_temp = XRegisterFrom(cls->GetLocations()->GetTemp(0)); - bss_entry_adrp_label = codegen_->NewBssEntryTypePatch(dex_file, type_index); - codegen_->EmitAdrpPlaceholder(bss_entry_adrp_label, bss_entry_temp); + vixl::aarch64::Register temp = XRegisterFrom(out_loc); + vixl::aarch64::Label* adrp_label = codegen_->NewBssEntryTypePatch(dex_file, type_index); + codegen_->EmitAdrpPlaceholder(adrp_label, temp); // Add LDR with its PC-relative Class patch. vixl::aarch64::Label* ldr_label = - codegen_->NewBssEntryTypePatch(dex_file, type_index, bss_entry_adrp_label); + codegen_->NewBssEntryTypePatch(dex_file, type_index, adrp_label); // /* GcRoot */ out = *(base_address + offset) /* PC-relative */ GenerateGcRootFieldLoad(cls, out_loc, - bss_entry_temp, + temp, /* offset placeholder */ 0u, ldr_label, read_barrier_option); @@ -5013,7 +4921,7 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA if (generate_null_check || do_clinit) { DCHECK(cls->CanCallRuntime()); SlowPathCodeARM64* slow_path = new (codegen_->GetScopedAllocator()) LoadClassSlowPathARM64( - cls, cls, cls->GetDexPc(), do_clinit, bss_entry_temp, bss_entry_adrp_label); + cls, cls, cls->GetDexPc(), do_clinit); codegen_->AddSlowPath(slow_path); if (generate_null_check) { __ Cbz(out, slow_path->GetEntryLabel()); @@ -5078,7 +4986,6 @@ void LocationsBuilderARM64::VisitLoadString(HLoadString* load) { if (load->GetLoadKind() == HLoadString::LoadKind::kBssEntry) { if (!kUseReadBarrier || kUseBakerReadBarrier) { // Rely on the pResolveString and marking to save everything we need. - locations->AddTemp(FixedTempLocation()); RegisterSet caller_saves = RegisterSet::Empty(); InvokeRuntimeCallingConvention calling_convention; caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0).GetCode())); @@ -5138,7 +5045,7 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) NO_THREAD const DexFile& dex_file = load->GetDexFile(); const dex::StringIndex string_index = load->GetStringIndex(); DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); - Register temp = XRegisterFrom(load->GetLocations()->GetTemp(0)); + Register temp = XRegisterFrom(out_loc); vixl::aarch64::Label* adrp_label = codegen_->NewStringBssEntryPatch(dex_file, string_index); codegen_->EmitAdrpPlaceholder(adrp_label, temp); // Add LDR with its .bss entry String patch. @@ -5152,7 +5059,7 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) NO_THREAD ldr_label, kCompilerReadBarrierOption); SlowPathCodeARM64* slow_path = - new (codegen_->GetScopedAllocator()) LoadStringSlowPathARM64(load, temp, adrp_label); + new (codegen_->GetScopedAllocator()) LoadStringSlowPathARM64(load); codegen_->AddSlowPath(slow_path); __ Cbz(out.X(), slow_path->GetEntryLabel()); __ Bind(slow_path->GetExitLabel()); diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 9e7455d488267374e43baed68960f0eeb8d1a2b8..017598d484f253a1cbf133a12a29e69fdeb41ec8 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -532,29 +532,12 @@ class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL { void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); Location out = locations->Out(); - constexpr bool call_saves_everything_except_r0 = (!kUseReadBarrier || kUseBakerReadBarrier); CodeGeneratorARMVIXL* arm_codegen = down_cast(codegen); __ Bind(GetEntryLabel()); SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConventionARMVIXL calling_convention; - // For HLoadClass/kBssEntry/kSaveEverything, make sure we preserve the address of the entry. - DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_); - bool is_load_class_bss_entry = - (cls_ == instruction_) && (cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry); - vixl32::Register entry_address; - if (is_load_class_bss_entry && call_saves_everything_except_r0) { - vixl32::Register temp = RegisterFrom(locations->GetTemp(0)); - // In the unlucky case that the `temp` is R0, we preserve the address in `out` across - // the kSaveEverything call. - bool temp_is_r0 = temp.Is(calling_convention.GetRegisterAt(0)); - entry_address = temp_is_r0 ? RegisterFrom(out) : temp; - DCHECK(!entry_address.Is(calling_convention.GetRegisterAt(0))); - if (temp_is_r0) { - __ Mov(entry_address, temp); - } - } dex::TypeIndex type_index = cls_->GetTypeIndex(); __ Mov(calling_convention.GetRegisterAt(0), type_index.index_); QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage @@ -566,22 +549,6 @@ class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL { CheckEntrypointTypes(); } - // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry. - if (is_load_class_bss_entry) { - if (call_saves_everything_except_r0) { - // The class entry address was preserved in `entry_address` thanks to kSaveEverything. - __ Str(r0, MemOperand(entry_address)); - } else { - // For non-Baker read barrier, we need to re-calculate the address of the string entry. - UseScratchRegisterScope temps( - down_cast(codegen)->GetVIXLAssembler()); - vixl32::Register temp = temps.Acquire(); - CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = - arm_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index); - arm_codegen->EmitMovwMovtPlaceholder(labels, temp); - __ Str(r0, MemOperand(temp)); - } - } // Move the class to the desired location. if (out.IsValid()) { DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg())); @@ -616,48 +583,17 @@ class LoadStringSlowPathARMVIXL : public SlowPathCodeARMVIXL { DCHECK_EQ(instruction_->AsLoadString()->GetLoadKind(), HLoadString::LoadKind::kBssEntry); LocationSummary* locations = instruction_->GetLocations(); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg())); - HLoadString* load = instruction_->AsLoadString(); - const dex::StringIndex string_index = load->GetStringIndex(); - vixl32::Register out = OutputRegister(load); - constexpr bool call_saves_everything_except_r0 = (!kUseReadBarrier || kUseBakerReadBarrier); + const dex::StringIndex string_index = instruction_->AsLoadString()->GetStringIndex(); CodeGeneratorARMVIXL* arm_codegen = down_cast(codegen); __ Bind(GetEntryLabel()); SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConventionARMVIXL calling_convention; - // In the unlucky case that the `temp` is R0, we preserve the address in `out` across - // the kSaveEverything call. - vixl32::Register entry_address; - if (call_saves_everything_except_r0) { - vixl32::Register temp = RegisterFrom(locations->GetTemp(0)); - bool temp_is_r0 = (temp.Is(calling_convention.GetRegisterAt(0))); - entry_address = temp_is_r0 ? out : temp; - DCHECK(!entry_address.Is(calling_convention.GetRegisterAt(0))); - if (temp_is_r0) { - __ Mov(entry_address, temp); - } - } - __ Mov(calling_convention.GetRegisterAt(0), string_index.index_); arm_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this); CheckEntrypointTypes(); - // Store the resolved String to the .bss entry. - if (call_saves_everything_except_r0) { - // The string entry address was preserved in `entry_address` thanks to kSaveEverything. - __ Str(r0, MemOperand(entry_address)); - } else { - // For non-Baker read barrier, we need to re-calculate the address of the string entry. - UseScratchRegisterScope temps( - down_cast(codegen)->GetVIXLAssembler()); - vixl32::Register temp = temps.Acquire(); - CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = - arm_codegen->NewStringBssEntryPatch(load->GetDexFile(), string_index); - arm_codegen->EmitMovwMovtPlaceholder(labels, temp); - __ Str(r0, MemOperand(temp)); - } - arm_codegen->Move32(locations->Out(), LocationFrom(r0)); RestoreLiveRegisters(codegen, locations); @@ -683,7 +619,7 @@ class TypeCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL { CodeGeneratorARMVIXL* arm_codegen = down_cast(codegen); __ Bind(GetEntryLabel()); - if (!is_fatal_) { + if (!is_fatal_ || instruction_->CanThrowIntoCatchBlock()) { SaveLiveRegisters(codegen, locations); } @@ -7104,9 +7040,6 @@ void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) { if (load_kind == HLoadClass::LoadKind::kBssEntry) { if (!kUseReadBarrier || kUseBakerReadBarrier) { // Rely on the type resolution or initialization and marking to save everything we need. - // Note that IP may be clobbered by saving/restoring the live register (only one thanks - // to the custom calling convention) or by marking, so we request a different temp. - locations->AddTemp(Location::RequiresRegister()); RegisterSet caller_saves = RegisterSet::Empty(); InvokeRuntimeCallingConventionARMVIXL calling_convention; caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0))); @@ -7189,13 +7122,10 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_ break; } case HLoadClass::LoadKind::kBssEntry: { - vixl32::Register temp = (!kUseReadBarrier || kUseBakerReadBarrier) - ? RegisterFrom(locations->GetTemp(0)) - : out; CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex()); - codegen_->EmitMovwMovtPlaceholder(labels, temp); - GenerateGcRootFieldLoad(cls, out_loc, temp, /* offset */ 0, read_barrier_option); + codegen_->EmitMovwMovtPlaceholder(labels, out); + GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option); generate_null_check = true; break; } @@ -7296,9 +7226,6 @@ void LocationsBuilderARMVIXL::VisitLoadString(HLoadString* load) { if (load_kind == HLoadString::LoadKind::kBssEntry) { if (!kUseReadBarrier || kUseBakerReadBarrier) { // Rely on the pResolveString and marking to save everything we need, including temps. - // Note that IP may be clobbered by saving/restoring the live register (only one thanks - // to the custom calling convention) or by marking, so we request a different temp. - locations->AddTemp(Location::RequiresRegister()); RegisterSet caller_saves = RegisterSet::Empty(); InvokeRuntimeCallingConventionARMVIXL calling_convention; caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0))); @@ -7348,13 +7275,10 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THRE } case HLoadString::LoadKind::kBssEntry: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); - vixl32::Register temp = (!kUseReadBarrier || kUseBakerReadBarrier) - ? RegisterFrom(locations->GetTemp(0)) - : out; CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = codegen_->NewStringBssEntryPatch(load->GetDexFile(), load->GetStringIndex()); - codegen_->EmitMovwMovtPlaceholder(labels, temp); - GenerateGcRootFieldLoad(load, out_loc, temp, /* offset */ 0, kCompilerReadBarrierOption); + codegen_->EmitMovwMovtPlaceholder(labels, out); + GenerateGcRootFieldLoad(load, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption); LoadStringSlowPathARMVIXL* slow_path = new (codegen_->GetScopedAllocator()) LoadStringSlowPathARMVIXL(load); codegen_->AddSlowPath(slow_path); @@ -7453,11 +7377,12 @@ void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kExactCheck: case TypeCheckKind::kAbstractClassCheck: case TypeCheckKind::kClassHierarchyCheck: - case TypeCheckKind::kArrayObjectCheck: - call_kind = - kEmitCompilerReadBarrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall; - baker_read_barrier_slow_path = kUseBakerReadBarrier; + case TypeCheckKind::kArrayObjectCheck: { + bool needs_read_barrier = CodeGenerator::InstanceOfNeedsReadBarrier(instruction); + call_kind = needs_read_barrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall; + baker_read_barrier_slow_path = kUseBakerReadBarrier && needs_read_barrier; break; + } case TypeCheckKind::kArrayCheck: case TypeCheckKind::kUnresolvedCheck: case TypeCheckKind::kInterfaceCheck: @@ -7510,13 +7435,15 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) switch (type_check_kind) { case TypeCheckKind::kExactCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); // Classes must be equal for the instanceof to succeed. __ Cmp(out, cls); // We speculatively set the result to false without changing the condition @@ -7543,13 +7470,15 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) } case TypeCheckKind::kAbstractClassCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); // If the class is abstract, we eagerly fetch the super class of the // object to avoid doing a comparison we know will fail. vixl32::Label loop; @@ -7559,7 +7488,7 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) out_loc, super_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); // If `out` is null, we use it for the result, and jump to the final label. __ CompareAndBranchIfZero(out, final_label, /* far_target */ false); __ Cmp(out, cls); @@ -7569,13 +7498,15 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) } case TypeCheckKind::kClassHierarchyCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); // Walk over the class hierarchy to find a match. vixl32::Label loop, success; __ Bind(&loop); @@ -7586,7 +7517,7 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) out_loc, super_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); // This is essentially a null check, but it sets the condition flags to the // proper value for the code that follows the loop, i.e. not `eq`. __ Cmp(out, 1); @@ -7623,13 +7554,15 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) } case TypeCheckKind::kArrayObjectCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); // Do an exact check. vixl32::Label exact_check; __ Cmp(out, cls); @@ -7640,7 +7573,7 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) out_loc, component_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); // If `out` is null, we use it for the result, and jump to the final label. __ CompareAndBranchIfZero(out, final_label, /* far_target */ false); GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset); @@ -7730,26 +7663,8 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) } void LocationsBuilderARMVIXL::VisitCheckCast(HCheckCast* instruction) { - LocationSummary::CallKind call_kind = LocationSummary::kNoCall; - bool throws_into_catch = instruction->CanThrowIntoCatchBlock(); - TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); - switch (type_check_kind) { - case TypeCheckKind::kExactCheck: - case TypeCheckKind::kAbstractClassCheck: - case TypeCheckKind::kClassHierarchyCheck: - case TypeCheckKind::kArrayObjectCheck: - call_kind = (throws_into_catch || kEmitCompilerReadBarrier) ? - LocationSummary::kCallOnSlowPath : - LocationSummary::kNoCall; // In fact, call on a fatal (non-returning) slow path. - break; - case TypeCheckKind::kArrayCheck: - case TypeCheckKind::kUnresolvedCheck: - case TypeCheckKind::kInterfaceCheck: - call_kind = LocationSummary::kCallOnSlowPath; - break; - } - + LocationSummary::CallKind call_kind = CodeGenerator::GetCheckCastCallKind(instruction); LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); @@ -7778,18 +7693,7 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { const uint32_t object_array_data_offset = mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value(); - // Always false for read barriers since we may need to go to the entrypoint for non-fatal cases - // from false negatives. The false negatives may come from avoiding read barriers below. Avoiding - // read barriers is done for performance and code size reasons. - bool is_type_check_slow_path_fatal = false; - if (!kEmitCompilerReadBarrier) { - is_type_check_slow_path_fatal = - (type_check_kind == TypeCheckKind::kExactCheck || - type_check_kind == TypeCheckKind::kAbstractClassCheck || - type_check_kind == TypeCheckKind::kClassHierarchyCheck || - type_check_kind == TypeCheckKind::kArrayObjectCheck) && - !instruction->CanThrowIntoCatchBlock(); - } + bool is_type_check_slow_path_fatal = CodeGenerator::IsTypeCheckSlowPathFatal(instruction); SlowPathCodeARMVIXL* type_check_slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathARMVIXL( instruction, is_type_check_slow_path_fatal); diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index f9f5a4da5646d4b690a55d55e14b2bd8bbc999f3..9f4c2349e700962ec90062c75c88f1dcfe531a0b 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -220,13 +220,11 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS { LoadClassSlowPathMIPS(HLoadClass* cls, HInstruction* at, uint32_t dex_pc, - bool do_clinit, - const CodeGeneratorMIPS::PcRelativePatchInfo* bss_info_high = nullptr) + bool do_clinit) : SlowPathCodeMIPS(at), cls_(cls), dex_pc_(dex_pc), - do_clinit_(do_clinit), - bss_info_high_(bss_info_high) { + do_clinit_(do_clinit) { DCHECK(at->IsLoadClass() || at->IsClinitCheck()); } @@ -234,28 +232,11 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS { LocationSummary* locations = instruction_->GetLocations(); Location out = locations->Out(); CodeGeneratorMIPS* mips_codegen = down_cast(codegen); - const bool baker_or_no_read_barriers = (!kUseReadBarrier || kUseBakerReadBarrier); InvokeRuntimeCallingConvention calling_convention; DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_); - const bool is_load_class_bss_entry = - (cls_ == instruction_) && (cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry); __ Bind(GetEntryLabel()); SaveLiveRegisters(codegen, locations); - // For HLoadClass/kBssEntry/kSaveEverything, make sure we preserve the address of the entry. - Register entry_address = kNoRegister; - if (is_load_class_bss_entry && baker_or_no_read_barriers) { - Register temp = locations->GetTemp(0).AsRegister(); - bool temp_is_a0 = (temp == calling_convention.GetRegisterAt(0)); - // In the unlucky case that `temp` is A0, we preserve the address in `out` across the - // kSaveEverything call. - entry_address = temp_is_a0 ? out.AsRegister() : temp; - DCHECK_NE(entry_address, calling_convention.GetRegisterAt(0)); - if (temp_is_a0) { - __ Move(entry_address, temp); - } - } - dex::TypeIndex type_index = cls_->GetTypeIndex(); __ LoadConst32(calling_convention.GetRegisterAt(0), type_index.index_); QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage @@ -267,18 +248,6 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS { CheckEntrypointTypes(); } - // For HLoadClass/kBssEntry, store the resolved class to the BSS entry. - if (is_load_class_bss_entry && baker_or_no_read_barriers) { - // The class entry address was preserved in `entry_address` thanks to kSaveEverything. - DCHECK(bss_info_high_); - CodeGeneratorMIPS::PcRelativePatchInfo* info_low = - mips_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index, bss_info_high_); - __ Sw(calling_convention.GetRegisterAt(0), - entry_address, - /* placeholder */ 0x5678, - &info_low->label); - } - // Move the class to the desired location. if (out.IsValid()) { DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg())); @@ -289,21 +258,6 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS { } RestoreLiveRegisters(codegen, locations); - // For HLoadClass/kBssEntry, store the resolved class to the BSS entry. - if (is_load_class_bss_entry && !baker_or_no_read_barriers) { - // For non-Baker read barriers we need to re-calculate the address of - // the class entry. - const bool isR6 = mips_codegen->GetInstructionSetFeatures().IsR6(); - const bool has_irreducible_loops = codegen->GetGraph()->HasIrreducibleLoops(); - Register base = - (isR6 || has_irreducible_loops) ? ZERO : locations->InAt(0).AsRegister(); - CodeGeneratorMIPS::PcRelativePatchInfo* info_high = - mips_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index); - CodeGeneratorMIPS::PcRelativePatchInfo* info_low = - mips_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index, info_high); - mips_codegen->EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, base); - __ Sw(out.AsRegister(), TMP, /* placeholder */ 0x5678, &info_low->label); - } __ B(GetExitLabel()); } @@ -319,92 +273,41 @@ class LoadClassSlowPathMIPS : public SlowPathCodeMIPS { // Whether to initialize the class. const bool do_clinit_; - // Pointer to the high half PC-relative patch info for HLoadClass/kBssEntry. - const CodeGeneratorMIPS::PcRelativePatchInfo* bss_info_high_; - DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathMIPS); }; class LoadStringSlowPathMIPS : public SlowPathCodeMIPS { public: - explicit LoadStringSlowPathMIPS(HLoadString* instruction, - const CodeGeneratorMIPS::PcRelativePatchInfo* bss_info_high) - : SlowPathCodeMIPS(instruction), bss_info_high_(bss_info_high) {} + explicit LoadStringSlowPathMIPS(HLoadString* instruction) + : SlowPathCodeMIPS(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { DCHECK(instruction_->IsLoadString()); DCHECK_EQ(instruction_->AsLoadString()->GetLoadKind(), HLoadString::LoadKind::kBssEntry); LocationSummary* locations = instruction_->GetLocations(); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg())); - HLoadString* load = instruction_->AsLoadString(); - const dex::StringIndex string_index = load->GetStringIndex(); - Register out = locations->Out().AsRegister(); + const dex::StringIndex string_index = instruction_->AsLoadString()->GetStringIndex(); CodeGeneratorMIPS* mips_codegen = down_cast(codegen); - const bool baker_or_no_read_barriers = (!kUseReadBarrier || kUseBakerReadBarrier); InvokeRuntimeCallingConvention calling_convention; __ Bind(GetEntryLabel()); SaveLiveRegisters(codegen, locations); - // For HLoadString/kBssEntry/kSaveEverything, make sure we preserve the address of the entry. - Register entry_address = kNoRegister; - if (baker_or_no_read_barriers) { - Register temp = locations->GetTemp(0).AsRegister(); - bool temp_is_a0 = (temp == calling_convention.GetRegisterAt(0)); - // In the unlucky case that `temp` is A0, we preserve the address in `out` across the - // kSaveEverything call. - entry_address = temp_is_a0 ? out : temp; - DCHECK_NE(entry_address, calling_convention.GetRegisterAt(0)); - if (temp_is_a0) { - __ Move(entry_address, temp); - } - } - __ LoadConst32(calling_convention.GetRegisterAt(0), string_index.index_); mips_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this); CheckEntrypointTypes(); - // Store the resolved string to the BSS entry. - if (baker_or_no_read_barriers) { - // The string entry address was preserved in `entry_address` thanks to kSaveEverything. - DCHECK(bss_info_high_); - CodeGeneratorMIPS::PcRelativePatchInfo* info_low = - mips_codegen->NewStringBssEntryPatch(load->GetDexFile(), string_index, bss_info_high_); - __ Sw(calling_convention.GetRegisterAt(0), - entry_address, - /* placeholder */ 0x5678, - &info_low->label); - } - DataType::Type type = instruction_->GetType(); mips_codegen->MoveLocation(locations->Out(), Location::RegisterLocation(calling_convention.GetRegisterAt(0)), type); RestoreLiveRegisters(codegen, locations); - // Store the resolved string to the BSS entry. - if (!baker_or_no_read_barriers) { - // For non-Baker read barriers we need to re-calculate the address of - // the string entry. - const bool isR6 = mips_codegen->GetInstructionSetFeatures().IsR6(); - const bool has_irreducible_loops = codegen->GetGraph()->HasIrreducibleLoops(); - Register base = - (isR6 || has_irreducible_loops) ? ZERO : locations->InAt(0).AsRegister(); - CodeGeneratorMIPS::PcRelativePatchInfo* info_high = - mips_codegen->NewStringBssEntryPatch(load->GetDexFile(), string_index); - CodeGeneratorMIPS::PcRelativePatchInfo* info_low = - mips_codegen->NewStringBssEntryPatch(load->GetDexFile(), string_index, info_high); - mips_codegen->EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, base); - __ Sw(out, TMP, /* placeholder */ 0x5678, &info_low->label); - } __ B(GetExitLabel()); } const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathMIPS"; } private: - // Pointer to the high half PC-relative patch info. - const CodeGeneratorMIPS::PcRelativePatchInfo* bss_info_high_; - DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathMIPS); }; @@ -1192,17 +1095,23 @@ void ParallelMoveResolverMIPS::EmitSwap(size_t index) { __ Move(r2, r1); __ Move(r1, TMP); } else if (loc1.IsFpuRegister() && loc2.IsFpuRegister()) { - FRegister f1 = loc1.AsFpuRegister(); - FRegister f2 = loc2.AsFpuRegister(); - if (type == DataType::Type::kFloat32) { - __ MovS(FTMP, f2); - __ MovS(f2, f1); - __ MovS(f1, FTMP); + if (codegen_->GetGraph()->HasSIMD()) { + __ MoveV(static_cast(FTMP), VectorRegisterFrom(loc1)); + __ MoveV(VectorRegisterFrom(loc1), VectorRegisterFrom(loc2)); + __ MoveV(VectorRegisterFrom(loc2), static_cast(FTMP)); } else { - DCHECK_EQ(type, DataType::Type::kFloat64); - __ MovD(FTMP, f2); - __ MovD(f2, f1); - __ MovD(f1, FTMP); + FRegister f1 = loc1.AsFpuRegister(); + FRegister f2 = loc2.AsFpuRegister(); + if (type == DataType::Type::kFloat32) { + __ MovS(FTMP, f2); + __ MovS(f2, f1); + __ MovS(f1, FTMP); + } else { + DCHECK_EQ(type, DataType::Type::kFloat64); + __ MovD(FTMP, f2); + __ MovD(f2, f1); + __ MovD(f1, FTMP); + } } } else if ((loc1.IsRegister() && loc2.IsFpuRegister()) || (loc1.IsFpuRegister() && loc2.IsRegister())) { @@ -1249,6 +1158,8 @@ void ParallelMoveResolverMIPS::EmitSwap(size_t index) { Exchange(loc1.GetStackIndex(), loc2.GetStackIndex(), /* double_slot */ false); } else if (loc1.IsDoubleStackSlot() && loc2.IsDoubleStackSlot()) { Exchange(loc1.GetStackIndex(), loc2.GetStackIndex(), /* double_slot */ true); + } else if (loc1.IsSIMDStackSlot() && loc2.IsSIMDStackSlot()) { + ExchangeQuadSlots(loc1.GetStackIndex(), loc2.GetStackIndex()); } else if ((loc1.IsRegister() && loc2.IsStackSlot()) || (loc1.IsStackSlot() && loc2.IsRegister())) { Register reg = loc1.IsRegister() ? loc1.AsRegister() : loc2.AsRegister(); @@ -1271,6 +1182,13 @@ void ParallelMoveResolverMIPS::EmitSwap(size_t index) { __ Move(TMP, reg_h); __ LoadFromOffset(kLoadWord, reg_h, SP, offset_h); __ StoreToOffset(kStoreWord, TMP, SP, offset_h); + } else if ((loc1.IsFpuRegister() && loc2.IsSIMDStackSlot()) || + (loc1.IsSIMDStackSlot() && loc2.IsFpuRegister())) { + Location fp_loc = loc1.IsFpuRegister() ? loc1 : loc2; + intptr_t offset = loc1.IsFpuRegister() ? loc2.GetStackIndex() : loc1.GetStackIndex(); + __ MoveV(static_cast(FTMP), VectorRegisterFrom(fp_loc)); + __ LoadQFromOffset(fp_loc.AsFpuRegister(), SP, offset); + __ StoreQToOffset(FTMP, SP, offset); } else if (loc1.IsFpuRegister() || loc2.IsFpuRegister()) { FRegister reg = loc1.IsFpuRegister() ? loc1.AsFpuRegister() : loc2.AsFpuRegister(); @@ -1322,6 +1240,13 @@ void ParallelMoveResolverMIPS::Exchange(int index1, int index2, bool double_slot } } +void ParallelMoveResolverMIPS::ExchangeQuadSlots(int index1, int index2) { + __ LoadQFromOffset(FTMP, SP, index1); + __ LoadQFromOffset(FTMP2, SP, index2); + __ StoreQToOffset(FTMP, SP, index2); + __ StoreQToOffset(FTMP2, SP, index1); +} + void CodeGeneratorMIPS::ComputeSpillMask() { core_spill_mask_ = allocated_registers_.GetCoreRegisters() & core_callee_save_mask_; fpu_spill_mask_ = allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_; @@ -1887,6 +1812,11 @@ void CodeGeneratorMIPS::SetupBlockedRegisters() const { blocked_core_registers_[TMP] = true; blocked_fpu_registers_[FTMP] = true; + if (GetInstructionSetFeatures().HasMsa()) { + // To be used just for MSA instructions. + blocked_fpu_registers_[FTMP2] = true; + } + // Reserve suspend and thread registers. blocked_core_registers_[S0] = true; blocked_core_registers_[TR] = true; @@ -2038,6 +1968,7 @@ void LocationsBuilderMIPS::HandleBinaryOp(HBinaryOperation* instruction) { DCHECK_EQ(instruction->InputCount(), 2U); LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction); DataType::Type type = instruction->GetResultType(); + bool isR6 = codegen_->GetInstructionSetFeatures().IsR6(); switch (type) { case DataType::Type::kInt32: { locations->SetInAt(0, Location::RequiresRegister()); @@ -2047,11 +1978,22 @@ void LocationsBuilderMIPS::HandleBinaryOp(HBinaryOperation* instruction) { int32_t imm = CodeGenerator::GetInt32ValueOf(right->AsConstant()); if (instruction->IsAnd() || instruction->IsOr() || instruction->IsXor()) { can_use_imm = IsUint<16>(imm); - } else if (instruction->IsAdd()) { - can_use_imm = IsInt<16>(imm); } else { - DCHECK(instruction->IsSub()); - can_use_imm = IsInt<16>(-imm); + DCHECK(instruction->IsSub() || instruction->IsAdd()); + if (instruction->IsSub()) { + imm = -imm; + } + if (isR6) { + bool single_use = right->GetUses().HasExactlyOneElement(); + int16_t imm_high = High16Bits(imm); + int16_t imm_low = Low16Bits(imm); + if (imm_low < 0) { + imm_high += 1; + } + can_use_imm = !((imm_high != 0) && (imm_low != 0)) || single_use; + } else { + can_use_imm = IsInt<16>(imm); + } } } if (can_use_imm) @@ -2085,6 +2027,7 @@ void LocationsBuilderMIPS::HandleBinaryOp(HBinaryOperation* instruction) { void InstructionCodeGeneratorMIPS::HandleBinaryOp(HBinaryOperation* instruction) { DataType::Type type = instruction->GetType(); LocationSummary* locations = instruction->GetLocations(); + bool isR6 = codegen_->GetInstructionSetFeatures().IsR6(); switch (type) { case DataType::Type::kInt32: { @@ -2116,17 +2059,32 @@ void InstructionCodeGeneratorMIPS::HandleBinaryOp(HBinaryOperation* instruction) __ Xori(dst, lhs, rhs_imm); else __ Xor(dst, lhs, rhs_reg); - } else if (instruction->IsAdd()) { - if (use_imm) - __ Addiu(dst, lhs, rhs_imm); - else - __ Addu(dst, lhs, rhs_reg); } else { - DCHECK(instruction->IsSub()); - if (use_imm) - __ Addiu(dst, lhs, -rhs_imm); - else + DCHECK(instruction->IsAdd() || instruction->IsSub()); + if (use_imm) { + if (instruction->IsSub()) { + rhs_imm = -rhs_imm; + } + if (IsInt<16>(rhs_imm)) { + __ Addiu(dst, lhs, rhs_imm); + } else { + DCHECK(isR6); + int16_t rhs_imm_high = High16Bits(rhs_imm); + int16_t rhs_imm_low = Low16Bits(rhs_imm); + if (rhs_imm_low < 0) { + rhs_imm_high += 1; + } + __ Aui(dst, lhs, rhs_imm_high); + if (rhs_imm_low != 0) { + __ Addiu(dst, dst, rhs_imm_low); + } + } + } else if (instruction->IsAdd()) { + __ Addu(dst, lhs, rhs_reg); + } else { + DCHECK(instruction->IsSub()); __ Subu(dst, lhs, rhs_reg); + } } break; } @@ -2474,6 +2432,7 @@ void InstructionCodeGeneratorMIPS::HandleShift(HBinaryOperation* instr) { } } } else { + const bool isR6 = codegen_->GetInstructionSetFeatures().IsR6(); MipsLabel done; if (instr->IsShl()) { __ Sllv(dst_low, lhs_low, rhs_reg); @@ -2483,9 +2442,14 @@ void InstructionCodeGeneratorMIPS::HandleShift(HBinaryOperation* instr) { __ Sllv(dst_high, lhs_high, rhs_reg); __ Or(dst_high, dst_high, TMP); __ Andi(TMP, rhs_reg, kMipsBitsPerWord); - __ Beqz(TMP, &done); - __ Move(dst_high, dst_low); - __ Move(dst_low, ZERO); + if (isR6) { + __ Beqzc(TMP, &done, /* is_bare */ true); + __ Move(dst_high, dst_low); + __ Move(dst_low, ZERO); + } else { + __ Movn(dst_high, dst_low, TMP); + __ Movn(dst_low, ZERO, TMP); + } } else if (instr->IsShr()) { __ Srav(dst_high, lhs_high, rhs_reg); __ Nor(AT, ZERO, rhs_reg); @@ -2494,9 +2458,15 @@ void InstructionCodeGeneratorMIPS::HandleShift(HBinaryOperation* instr) { __ Srlv(dst_low, lhs_low, rhs_reg); __ Or(dst_low, dst_low, TMP); __ Andi(TMP, rhs_reg, kMipsBitsPerWord); - __ Beqz(TMP, &done); - __ Move(dst_low, dst_high); - __ Sra(dst_high, dst_high, 31); + if (isR6) { + __ Beqzc(TMP, &done, /* is_bare */ true); + __ Move(dst_low, dst_high); + __ Sra(dst_high, dst_high, 31); + } else { + __ Sra(AT, dst_high, 31); + __ Movn(dst_low, dst_high, TMP); + __ Movn(dst_high, AT, TMP); + } } else if (instr->IsUShr()) { __ Srlv(dst_high, lhs_high, rhs_reg); __ Nor(AT, ZERO, rhs_reg); @@ -2505,10 +2475,15 @@ void InstructionCodeGeneratorMIPS::HandleShift(HBinaryOperation* instr) { __ Srlv(dst_low, lhs_low, rhs_reg); __ Or(dst_low, dst_low, TMP); __ Andi(TMP, rhs_reg, kMipsBitsPerWord); - __ Beqz(TMP, &done); - __ Move(dst_low, dst_high); - __ Move(dst_high, ZERO); - } else { + if (isR6) { + __ Beqzc(TMP, &done, /* is_bare */ true); + __ Move(dst_low, dst_high); + __ Move(dst_high, ZERO); + } else { + __ Movn(dst_low, dst_high, TMP); + __ Movn(dst_high, ZERO, TMP); + } + } else { // Rotate. __ Nor(AT, ZERO, rhs_reg); __ Srlv(TMP, lhs_low, rhs_reg); __ Sll(dst_low, lhs_high, 1); @@ -2519,10 +2494,16 @@ void InstructionCodeGeneratorMIPS::HandleShift(HBinaryOperation* instr) { __ Sllv(dst_high, dst_high, AT); __ Or(dst_high, dst_high, TMP); __ Andi(TMP, rhs_reg, kMipsBitsPerWord); - __ Beqz(TMP, &done); - __ Move(TMP, dst_high); - __ Move(dst_high, dst_low); - __ Move(dst_low, TMP); + if (isR6) { + __ Beqzc(TMP, &done, /* is_bare */ true); + __ Move(TMP, dst_high); + __ Move(dst_high, dst_low); + __ Move(dst_low, TMP); + } else { + __ Movn(AT, dst_high, TMP); + __ Movn(dst_high, dst_low, TMP); + __ Movn(dst_low, AT, TMP); + } } __ Bind(&done); } @@ -3177,23 +3158,92 @@ void LocationsBuilderMIPS::VisitBoundsCheck(HBoundsCheck* instruction) { caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(1))); LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + + HInstruction* index = instruction->InputAt(0); + HInstruction* length = instruction->InputAt(1); + + bool const_index = false; + bool const_length = false; + + if (index->IsConstant()) { + if (length->IsConstant()) { + const_index = true; + const_length = true; + } else { + int32_t index_value = index->AsIntConstant()->GetValue(); + if (index_value < 0 || IsInt<16>(index_value + 1)) { + const_index = true; + } + } + } else if (length->IsConstant()) { + int32_t length_value = length->AsIntConstant()->GetValue(); + if (IsUint<15>(length_value)) { + const_length = true; + } + } + + locations->SetInAt(0, const_index + ? Location::ConstantLocation(index->AsConstant()) + : Location::RequiresRegister()); + locations->SetInAt(1, const_length + ? Location::ConstantLocation(length->AsConstant()) + : Location::RequiresRegister()); } void InstructionCodeGeneratorMIPS::VisitBoundsCheck(HBoundsCheck* instruction) { LocationSummary* locations = instruction->GetLocations(); - BoundsCheckSlowPathMIPS* slow_path = - new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS(instruction); - codegen_->AddSlowPath(slow_path); - - Register index = locations->InAt(0).AsRegister(); - Register length = locations->InAt(1).AsRegister(); + Location index_loc = locations->InAt(0); + Location length_loc = locations->InAt(1); + + if (length_loc.IsConstant()) { + int32_t length = length_loc.GetConstant()->AsIntConstant()->GetValue(); + if (index_loc.IsConstant()) { + int32_t index = index_loc.GetConstant()->AsIntConstant()->GetValue(); + if (index < 0 || index >= length) { + BoundsCheckSlowPathMIPS* slow_path = + new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS(instruction); + codegen_->AddSlowPath(slow_path); + __ B(slow_path->GetEntryLabel()); + } else { + // Nothing to be done. + } + return; + } - // length is limited by the maximum positive signed 32-bit integer. - // Unsigned comparison of length and index checks for index < 0 - // and for length <= index simultaneously. - __ Bgeu(index, length, slow_path->GetEntryLabel()); + BoundsCheckSlowPathMIPS* slow_path = + new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS(instruction); + codegen_->AddSlowPath(slow_path); + Register index = index_loc.AsRegister(); + if (length == 0) { + __ B(slow_path->GetEntryLabel()); + } else if (length == 1) { + __ Bnez(index, slow_path->GetEntryLabel()); + } else { + DCHECK(IsUint<15>(length)) << length; + __ Sltiu(TMP, index, length); + __ Beqz(TMP, slow_path->GetEntryLabel()); + } + } else { + Register length = length_loc.AsRegister(); + BoundsCheckSlowPathMIPS* slow_path = + new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS(instruction); + codegen_->AddSlowPath(slow_path); + if (index_loc.IsConstant()) { + int32_t index = index_loc.GetConstant()->AsIntConstant()->GetValue(); + if (index < 0) { + __ B(slow_path->GetEntryLabel()); + } else if (index == 0) { + __ Blez(length, slow_path->GetEntryLabel()); + } else { + DCHECK(IsInt<16>(index + 1)) << index; + __ Sltiu(TMP, length, index + 1); + __ Bnez(TMP, slow_path->GetEntryLabel()); + } + } else { + Register index = index_loc.AsRegister(); + __ Bgeu(index, length, slow_path->GetEntryLabel()); + } + } } // Temp is used for read barrier. @@ -7713,8 +7763,6 @@ void LocationsBuilderMIPS::VisitLoadClass(HLoadClass* cls) { if (load_kind == HLoadClass::LoadKind::kBssEntry) { if (!kUseReadBarrier || kUseBakerReadBarrier) { // Rely on the type resolution or initialization and marking to save everything we need. - // Request a temp to hold the BSS entry location for the slow path. - locations->AddTemp(Location::RequiresRegister()); RegisterSet caller_saves = RegisterSet::Empty(); InvokeRuntimeCallingConvention calling_convention; caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); @@ -7763,7 +7811,6 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAF ? kWithoutReadBarrier : kCompilerReadBarrierOption; bool generate_null_check = false; - CodeGeneratorMIPS::PcRelativePatchInfo* bss_info_high = nullptr; switch (load_kind) { case HLoadClass::LoadKind::kReferrersClass: { DCHECK(!cls->CanCallRuntime()); @@ -7822,17 +7869,16 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAF break; } case HLoadClass::LoadKind::kBssEntry: { - bss_info_high = codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex()); + CodeGeneratorMIPS::PcRelativePatchInfo* bss_info_high = + codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex()); CodeGeneratorMIPS::PcRelativePatchInfo* info_low = codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex(), bss_info_high); - constexpr bool non_baker_read_barrier = kUseReadBarrier && !kUseBakerReadBarrier; - Register temp = non_baker_read_barrier ? out : locations->GetTemp(0).AsRegister(); codegen_->EmitPcRelativeAddressPlaceholderHigh(bss_info_high, - temp, + out, base_or_current_method_reg); GenerateGcRootFieldLoad(cls, out_loc, - temp, + out, /* placeholder */ 0x5678, read_barrier_option, &info_low->label); @@ -7864,7 +7910,7 @@ void InstructionCodeGeneratorMIPS::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAF if (generate_null_check || cls->MustGenerateClinitCheck()) { DCHECK(cls->CanCallRuntime()); SlowPathCodeMIPS* slow_path = new (codegen_->GetScopedAllocator()) LoadClassSlowPathMIPS( - cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck(), bss_info_high); + cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck()); codegen_->AddSlowPath(slow_path); if (generate_null_check) { __ Beqz(out, slow_path->GetEntryLabel()); @@ -7937,8 +7983,6 @@ void LocationsBuilderMIPS::VisitLoadString(HLoadString* load) { if (load_kind == HLoadString::LoadKind::kBssEntry) { if (!kUseReadBarrier || kUseBakerReadBarrier) { // Rely on the pResolveString and marking to save everything we need. - // Request a temp to hold the BSS entry location for the slow path. - locations->AddTemp(Location::RequiresRegister()); RegisterSet caller_saves = RegisterSet::Empty(); InvokeRuntimeCallingConvention calling_convention; caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); @@ -8018,19 +8062,17 @@ void InstructionCodeGeneratorMIPS::VisitLoadString(HLoadString* load) NO_THREAD_ codegen_->NewStringBssEntryPatch(load->GetDexFile(), load->GetStringIndex()); CodeGeneratorMIPS::PcRelativePatchInfo* info_low = codegen_->NewStringBssEntryPatch(load->GetDexFile(), load->GetStringIndex(), info_high); - constexpr bool non_baker_read_barrier = kUseReadBarrier && !kUseBakerReadBarrier; - Register temp = non_baker_read_barrier ? out : locations->GetTemp(0).AsRegister(); codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, - temp, + out, base_or_current_method_reg); GenerateGcRootFieldLoad(load, out_loc, - temp, + out, /* placeholder */ 0x5678, kCompilerReadBarrierOption, &info_low->label); SlowPathCodeMIPS* slow_path = - new (codegen_->GetScopedAllocator()) LoadStringSlowPathMIPS(load, info_high); + new (codegen_->GetScopedAllocator()) LoadStringSlowPathMIPS(load); codegen_->AddSlowPath(slow_path); __ Beqz(out, slow_path->GetEntryLabel()); __ Bind(slow_path->GetExitLabel()); diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index 7845e312cb727a227020033557515a4b88dbf2c2..cf8e7a373a1c80200fae01e62271deea6bd37e26 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -145,6 +145,7 @@ class ParallelMoveResolverMIPS : public ParallelMoveResolverWithSwap { void RestoreScratch(int reg) OVERRIDE; void Exchange(int index1, int index2, bool double_slot); + void ExchangeQuadSlots(int index1, int index2); MipsAssembler* GetAssembler() const; diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 0a6d9159d1259f20034422b70ecb244f5045c613..eb64f1be2319f7724ebaf018fd37e8366c1cf74c 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -175,13 +175,11 @@ class LoadClassSlowPathMIPS64 : public SlowPathCodeMIPS64 { LoadClassSlowPathMIPS64(HLoadClass* cls, HInstruction* at, uint32_t dex_pc, - bool do_clinit, - const CodeGeneratorMIPS64::PcRelativePatchInfo* bss_info_high = nullptr) + bool do_clinit) : SlowPathCodeMIPS64(at), cls_(cls), dex_pc_(dex_pc), - do_clinit_(do_clinit), - bss_info_high_(bss_info_high) { + do_clinit_(do_clinit) { DCHECK(at->IsLoadClass() || at->IsClinitCheck()); } @@ -189,28 +187,11 @@ class LoadClassSlowPathMIPS64 : public SlowPathCodeMIPS64 { LocationSummary* locations = instruction_->GetLocations(); Location out = locations->Out(); CodeGeneratorMIPS64* mips64_codegen = down_cast(codegen); - const bool baker_or_no_read_barriers = (!kUseReadBarrier || kUseBakerReadBarrier); InvokeRuntimeCallingConvention calling_convention; DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_); - const bool is_load_class_bss_entry = - (cls_ == instruction_) && (cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry); __ Bind(GetEntryLabel()); SaveLiveRegisters(codegen, locations); - // For HLoadClass/kBssEntry/kSaveEverything, make sure we preserve the address of the entry. - GpuRegister entry_address = kNoGpuRegister; - if (is_load_class_bss_entry && baker_or_no_read_barriers) { - GpuRegister temp = locations->GetTemp(0).AsRegister(); - bool temp_is_a0 = (temp == calling_convention.GetRegisterAt(0)); - // In the unlucky case that `temp` is A0, we preserve the address in `out` across the - // kSaveEverything call. - entry_address = temp_is_a0 ? out.AsRegister() : temp; - DCHECK_NE(entry_address, calling_convention.GetRegisterAt(0)); - if (temp_is_a0) { - __ Move(entry_address, temp); - } - } - dex::TypeIndex type_index = cls_->GetTypeIndex(); __ LoadConst32(calling_convention.GetRegisterAt(0), type_index.index_); QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage @@ -222,19 +203,6 @@ class LoadClassSlowPathMIPS64 : public SlowPathCodeMIPS64 { CheckEntrypointTypes(); } - // For HLoadClass/kBssEntry, store the resolved class to the BSS entry. - if (is_load_class_bss_entry && baker_or_no_read_barriers) { - // The class entry address was preserved in `entry_address` thanks to kSaveEverything. - DCHECK(bss_info_high_); - CodeGeneratorMIPS64::PcRelativePatchInfo* info_low = - mips64_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index, bss_info_high_); - __ Bind(&info_low->label); - __ StoreToOffset(kStoreWord, - calling_convention.GetRegisterAt(0), - entry_address, - /* placeholder */ 0x5678); - } - // Move the class to the desired location. if (out.IsValid()) { DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg())); @@ -245,17 +213,6 @@ class LoadClassSlowPathMIPS64 : public SlowPathCodeMIPS64 { } RestoreLiveRegisters(codegen, locations); - // For HLoadClass/kBssEntry, store the resolved class to the BSS entry. - if (is_load_class_bss_entry && !baker_or_no_read_barriers) { - // For non-Baker read barriers we need to re-calculate the address of - // the class entry. - CodeGeneratorMIPS64::PcRelativePatchInfo* info_high = - mips64_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index); - CodeGeneratorMIPS64::PcRelativePatchInfo* info_low = - mips64_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index, info_high); - mips64_codegen->EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, info_low); - __ StoreToOffset(kStoreWord, out.AsRegister(), TMP, /* placeholder */ 0x5678); - } __ Bc(GetExitLabel()); } @@ -271,46 +228,25 @@ class LoadClassSlowPathMIPS64 : public SlowPathCodeMIPS64 { // Whether to initialize the class. const bool do_clinit_; - // Pointer to the high half PC-relative patch info for HLoadClass/kBssEntry. - const CodeGeneratorMIPS64::PcRelativePatchInfo* bss_info_high_; - DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathMIPS64); }; class LoadStringSlowPathMIPS64 : public SlowPathCodeMIPS64 { public: - explicit LoadStringSlowPathMIPS64(HLoadString* instruction, - const CodeGeneratorMIPS64::PcRelativePatchInfo* bss_info_high) - : SlowPathCodeMIPS64(instruction), bss_info_high_(bss_info_high) {} + explicit LoadStringSlowPathMIPS64(HLoadString* instruction) + : SlowPathCodeMIPS64(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { DCHECK(instruction_->IsLoadString()); DCHECK_EQ(instruction_->AsLoadString()->GetLoadKind(), HLoadString::LoadKind::kBssEntry); LocationSummary* locations = instruction_->GetLocations(); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg())); - HLoadString* load = instruction_->AsLoadString(); - const dex::StringIndex string_index = load->GetStringIndex(); - GpuRegister out = locations->Out().AsRegister(); + const dex::StringIndex string_index = instruction_->AsLoadString()->GetStringIndex(); CodeGeneratorMIPS64* mips64_codegen = down_cast(codegen); - const bool baker_or_no_read_barriers = (!kUseReadBarrier || kUseBakerReadBarrier); InvokeRuntimeCallingConvention calling_convention; __ Bind(GetEntryLabel()); SaveLiveRegisters(codegen, locations); - // For HLoadString/kBssEntry/kSaveEverything, make sure we preserve the address of the entry. - GpuRegister entry_address = kNoGpuRegister; - if (baker_or_no_read_barriers) { - GpuRegister temp = locations->GetTemp(0).AsRegister(); - bool temp_is_a0 = (temp == calling_convention.GetRegisterAt(0)); - // In the unlucky case that `temp` is A0, we preserve the address in `out` across the - // kSaveEverything call. - entry_address = temp_is_a0 ? out : temp; - DCHECK_NE(entry_address, calling_convention.GetRegisterAt(0)); - if (temp_is_a0) { - __ Move(entry_address, temp); - } - } - __ LoadConst32(calling_convention.GetRegisterAt(0), string_index.index_); mips64_codegen->InvokeRuntime(kQuickResolveString, instruction_, @@ -318,47 +254,18 @@ class LoadStringSlowPathMIPS64 : public SlowPathCodeMIPS64 { this); CheckEntrypointTypes(); - // Store the resolved string to the BSS entry. - if (baker_or_no_read_barriers) { - // The string entry address was preserved in `entry_address` thanks to kSaveEverything. - DCHECK(bss_info_high_); - CodeGeneratorMIPS64::PcRelativePatchInfo* info_low = - mips64_codegen->NewStringBssEntryPatch(load->GetDexFile(), - string_index, - bss_info_high_); - __ Bind(&info_low->label); - __ StoreToOffset(kStoreWord, - calling_convention.GetRegisterAt(0), - entry_address, - /* placeholder */ 0x5678); - } - DataType::Type type = instruction_->GetType(); mips64_codegen->MoveLocation(locations->Out(), Location::RegisterLocation(calling_convention.GetRegisterAt(0)), type); RestoreLiveRegisters(codegen, locations); - // Store the resolved string to the BSS entry. - if (!baker_or_no_read_barriers) { - // For non-Baker read barriers we need to re-calculate the address of - // the string entry. - CodeGeneratorMIPS64::PcRelativePatchInfo* info_high = - mips64_codegen->NewStringBssEntryPatch(load->GetDexFile(), string_index); - CodeGeneratorMIPS64::PcRelativePatchInfo* info_low = - mips64_codegen->NewStringBssEntryPatch(load->GetDexFile(), string_index, info_high); - mips64_codegen->EmitPcRelativeAddressPlaceholderHigh(info_high, TMP, info_low); - __ StoreToOffset(kStoreWord, out, TMP, /* placeholder */ 0x5678); - } __ Bc(GetExitLabel()); } const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathMIPS64"; } private: - // Pointer to the high half PC-relative patch info. - const CodeGeneratorMIPS64::PcRelativePatchInfo* bss_info_high_; - DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathMIPS64); }; @@ -1154,6 +1061,13 @@ void ParallelMoveResolverMIPS64::Exchange(int index1, int index2, bool double_sl __ StoreToOffset(store_type, TMP, SP, index1 + stack_offset); } +void ParallelMoveResolverMIPS64::ExchangeQuadSlots(int index1, int index2) { + __ LoadFpuFromOffset(kLoadQuadword, FTMP, SP, index1); + __ LoadFpuFromOffset(kLoadQuadword, FTMP2, SP, index2); + __ StoreFpuToOffset(kStoreQuadword, FTMP, SP, index2); + __ StoreFpuToOffset(kStoreQuadword, FTMP2, SP, index1); +} + static dwarf::Reg DWARFReg(GpuRegister reg) { return dwarf::Reg::Mips64Core(static_cast(reg)); } @@ -1463,6 +1377,8 @@ void CodeGeneratorMIPS64::SwapLocations(Location loc1, Location loc2, DataType:: bool is_slot1 = loc1.IsStackSlot() || loc1.IsDoubleStackSlot(); bool is_slot2 = loc2.IsStackSlot() || loc2.IsDoubleStackSlot(); + bool is_simd1 = loc1.IsSIMDStackSlot(); + bool is_simd2 = loc2.IsSIMDStackSlot(); bool is_fp_reg1 = loc1.IsFpuRegister(); bool is_fp_reg2 = loc2.IsFpuRegister(); @@ -1475,17 +1391,23 @@ void CodeGeneratorMIPS64::SwapLocations(Location loc1, Location loc2, DataType:: __ Move(r1, TMP); } else if (is_fp_reg2 && is_fp_reg1) { // Swap 2 FPRs - FpuRegister r1 = loc1.AsFpuRegister(); - FpuRegister r2 = loc2.AsFpuRegister(); - if (type == DataType::Type::kFloat32) { - __ MovS(FTMP, r1); - __ MovS(r1, r2); - __ MovS(r2, FTMP); + if (GetGraph()->HasSIMD()) { + __ MoveV(static_cast(FTMP), VectorRegisterFrom(loc1)); + __ MoveV(VectorRegisterFrom(loc1), VectorRegisterFrom(loc2)); + __ MoveV(VectorRegisterFrom(loc2), static_cast(FTMP)); } else { - DCHECK_EQ(type, DataType::Type::kFloat64); - __ MovD(FTMP, r1); - __ MovD(r1, r2); - __ MovD(r2, FTMP); + FpuRegister r1 = loc1.AsFpuRegister(); + FpuRegister r2 = loc2.AsFpuRegister(); + if (type == DataType::Type::kFloat32) { + __ MovS(FTMP, r1); + __ MovS(r1, r2); + __ MovS(r2, FTMP); + } else { + DCHECK_EQ(type, DataType::Type::kFloat64); + __ MovD(FTMP, r1); + __ MovD(r1, r2); + __ MovD(r2, FTMP); + } } } else if (is_slot1 != is_slot2) { // Swap GPR/FPR and stack slot @@ -1514,6 +1436,17 @@ void CodeGeneratorMIPS64::SwapLocations(Location loc1, Location loc2, DataType:: move_resolver_.Exchange(loc1.GetStackIndex(), loc2.GetStackIndex(), loc1.IsDoubleStackSlot()); + } else if (is_simd1 && is_simd2) { + move_resolver_.ExchangeQuadSlots(loc1.GetStackIndex(), loc2.GetStackIndex()); + } else if ((is_fp_reg1 && is_simd2) || (is_fp_reg2 && is_simd1)) { + Location fp_reg_loc = is_fp_reg1 ? loc1 : loc2; + Location mem_loc = is_fp_reg1 ? loc2 : loc1; + __ LoadFpuFromOffset(kLoadQuadword, FTMP, SP, mem_loc.GetStackIndex()); + __ StoreFpuToOffset(kStoreQuadword, + fp_reg_loc.AsFpuRegister(), + SP, + mem_loc.GetStackIndex()); + __ MoveV(VectorRegisterFrom(fp_reg_loc), static_cast(FTMP)); } else { LOG(FATAL) << "Unimplemented swap between locations " << loc1 << " and " << loc2; } @@ -1746,6 +1679,11 @@ void CodeGeneratorMIPS64::SetupBlockedRegisters() const { blocked_core_registers_[TMP2] = true; blocked_fpu_registers_[FTMP] = true; + if (GetInstructionSetFeatures().HasMsa()) { + // To be used just for MSA instructions. + blocked_fpu_registers_[FTMP2] = true; + } + // Reserve suspend and thread registers. blocked_core_registers_[S0] = true; blocked_core_registers_[TR] = true; @@ -1886,11 +1824,19 @@ void LocationsBuilderMIPS64::HandleBinaryOp(HBinaryOperation* instruction) { int64_t imm = CodeGenerator::GetInt64ValueOf(right->AsConstant()); if (instruction->IsAnd() || instruction->IsOr() || instruction->IsXor()) { can_use_imm = IsUint<16>(imm); - } else if (instruction->IsAdd()) { - can_use_imm = IsInt<16>(imm); } else { - DCHECK(instruction->IsSub()); - can_use_imm = IsInt<16>(-imm); + DCHECK(instruction->IsAdd() || instruction->IsSub()); + bool single_use = right->GetUses().HasExactlyOneElement(); + if (instruction->IsSub()) { + if (!(type == DataType::Type::kInt32 && imm == INT32_MIN)) { + imm = -imm; + } + } + if (type == DataType::Type::kInt32) { + can_use_imm = IsInt<16>(imm) || (Low16Bits(imm) == 0) || single_use; + } else { + can_use_imm = IsInt<16>(imm) || (IsInt<32>(imm) && (Low16Bits(imm) == 0)) || single_use; + } } } if (can_use_imm) @@ -1948,30 +1894,90 @@ void InstructionCodeGeneratorMIPS64::HandleBinaryOp(HBinaryOperation* instructio __ Xori(dst, lhs, rhs_imm); else __ Xor(dst, lhs, rhs_reg); - } else if (instruction->IsAdd()) { - if (type == DataType::Type::kInt32) { - if (use_imm) - __ Addiu(dst, lhs, rhs_imm); - else - __ Addu(dst, lhs, rhs_reg); - } else { - if (use_imm) - __ Daddiu(dst, lhs, rhs_imm); - else - __ Daddu(dst, lhs, rhs_reg); + } else if (instruction->IsAdd() || instruction->IsSub()) { + if (instruction->IsSub()) { + rhs_imm = -rhs_imm; } - } else { - DCHECK(instruction->IsSub()); if (type == DataType::Type::kInt32) { - if (use_imm) - __ Addiu(dst, lhs, -rhs_imm); - else - __ Subu(dst, lhs, rhs_reg); + if (use_imm) { + if (IsInt<16>(rhs_imm)) { + __ Addiu(dst, lhs, rhs_imm); + } else { + int16_t rhs_imm_high = High16Bits(rhs_imm); + int16_t rhs_imm_low = Low16Bits(rhs_imm); + if (rhs_imm_low < 0) { + rhs_imm_high += 1; + } + __ Aui(dst, lhs, rhs_imm_high); + if (rhs_imm_low != 0) { + __ Addiu(dst, dst, rhs_imm_low); + } + } + } else { + if (instruction->IsAdd()) { + __ Addu(dst, lhs, rhs_reg); + } else { + DCHECK(instruction->IsSub()); + __ Subu(dst, lhs, rhs_reg); + } + } } else { - if (use_imm) - __ Daddiu(dst, lhs, -rhs_imm); - else + if (use_imm) { + if (IsInt<16>(rhs_imm)) { + __ Daddiu(dst, lhs, rhs_imm); + } else if (IsInt<32>(rhs_imm)) { + int16_t rhs_imm_high = High16Bits(rhs_imm); + int16_t rhs_imm_low = Low16Bits(rhs_imm); + bool overflow_hi16 = false; + if (rhs_imm_low < 0) { + rhs_imm_high += 1; + overflow_hi16 = (rhs_imm_high == -32768); + } + __ Daui(dst, lhs, rhs_imm_high); + if (rhs_imm_low != 0) { + __ Daddiu(dst, dst, rhs_imm_low); + } + if (overflow_hi16) { + __ Dahi(dst, 1); + } + } else { + int16_t rhs_imm_low = Low16Bits(Low32Bits(rhs_imm)); + if (rhs_imm_low < 0) { + rhs_imm += (INT64_C(1) << 16); + } + int16_t rhs_imm_upper = High16Bits(Low32Bits(rhs_imm)); + if (rhs_imm_upper < 0) { + rhs_imm += (INT64_C(1) << 32); + } + int16_t rhs_imm_high = Low16Bits(High32Bits(rhs_imm)); + if (rhs_imm_high < 0) { + rhs_imm += (INT64_C(1) << 48); + } + int16_t rhs_imm_top = High16Bits(High32Bits(rhs_imm)); + GpuRegister tmp = lhs; + if (rhs_imm_low != 0) { + __ Daddiu(dst, tmp, rhs_imm_low); + tmp = dst; + } + // Dahi and Dati must use the same input and output register, so we have to initialize + // the dst register using Daddiu or Daui, even when the intermediate value is zero: + // Daui(dst, lhs, 0). + if ((rhs_imm_upper != 0) || (rhs_imm_low == 0)) { + __ Daui(dst, tmp, rhs_imm_upper); + } + if (rhs_imm_high != 0) { + __ Dahi(dst, rhs_imm_high); + } + if (rhs_imm_top != 0) { + __ Dati(dst, rhs_imm_top); + } + } + } else if (instruction->IsAdd()) { + __ Daddu(dst, lhs, rhs_reg); + } else { + DCHECK(instruction->IsSub()); __ Dsubu(dst, lhs, rhs_reg); + } } } break; @@ -2707,23 +2713,92 @@ void LocationsBuilderMIPS64::VisitBoundsCheck(HBoundsCheck* instruction) { caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(1))); LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves); - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + + HInstruction* index = instruction->InputAt(0); + HInstruction* length = instruction->InputAt(1); + + bool const_index = false; + bool const_length = false; + + if (index->IsConstant()) { + if (length->IsConstant()) { + const_index = true; + const_length = true; + } else { + int32_t index_value = index->AsIntConstant()->GetValue(); + if (index_value < 0 || IsInt<16>(index_value + 1)) { + const_index = true; + } + } + } else if (length->IsConstant()) { + int32_t length_value = length->AsIntConstant()->GetValue(); + if (IsUint<15>(length_value)) { + const_length = true; + } + } + + locations->SetInAt(0, const_index + ? Location::ConstantLocation(index->AsConstant()) + : Location::RequiresRegister()); + locations->SetInAt(1, const_length + ? Location::ConstantLocation(length->AsConstant()) + : Location::RequiresRegister()); } void InstructionCodeGeneratorMIPS64::VisitBoundsCheck(HBoundsCheck* instruction) { LocationSummary* locations = instruction->GetLocations(); - BoundsCheckSlowPathMIPS64* slow_path = - new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS64(instruction); - codegen_->AddSlowPath(slow_path); - - GpuRegister index = locations->InAt(0).AsRegister(); - GpuRegister length = locations->InAt(1).AsRegister(); + Location index_loc = locations->InAt(0); + Location length_loc = locations->InAt(1); + + if (length_loc.IsConstant()) { + int32_t length = length_loc.GetConstant()->AsIntConstant()->GetValue(); + if (index_loc.IsConstant()) { + int32_t index = index_loc.GetConstant()->AsIntConstant()->GetValue(); + if (index < 0 || index >= length) { + BoundsCheckSlowPathMIPS64* slow_path = + new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS64(instruction); + codegen_->AddSlowPath(slow_path); + __ Bc(slow_path->GetEntryLabel()); + } else { + // Nothing to be done. + } + return; + } - // length is limited by the maximum positive signed 32-bit integer. - // Unsigned comparison of length and index checks for index < 0 - // and for length <= index simultaneously. - __ Bgeuc(index, length, slow_path->GetEntryLabel()); + BoundsCheckSlowPathMIPS64* slow_path = + new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS64(instruction); + codegen_->AddSlowPath(slow_path); + GpuRegister index = index_loc.AsRegister(); + if (length == 0) { + __ Bc(slow_path->GetEntryLabel()); + } else if (length == 1) { + __ Bnezc(index, slow_path->GetEntryLabel()); + } else { + DCHECK(IsUint<15>(length)) << length; + __ Sltiu(TMP, index, length); + __ Beqzc(TMP, slow_path->GetEntryLabel()); + } + } else { + GpuRegister length = length_loc.AsRegister(); + BoundsCheckSlowPathMIPS64* slow_path = + new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathMIPS64(instruction); + codegen_->AddSlowPath(slow_path); + if (index_loc.IsConstant()) { + int32_t index = index_loc.GetConstant()->AsIntConstant()->GetValue(); + if (index < 0) { + __ Bc(slow_path->GetEntryLabel()); + } else if (index == 0) { + __ Blezc(length, slow_path->GetEntryLabel()); + } else { + DCHECK(IsInt<16>(index + 1)) << index; + __ Sltiu(TMP, length, index + 1); + __ Bnezc(TMP, slow_path->GetEntryLabel()); + } + } else { + GpuRegister index = index_loc.AsRegister(); + __ Bgeuc(index, length, slow_path->GetEntryLabel()); + } + } } // Temp is used for read barrier. @@ -5979,8 +6054,6 @@ void LocationsBuilderMIPS64::VisitLoadClass(HLoadClass* cls) { if (load_kind == HLoadClass::LoadKind::kBssEntry) { if (!kUseReadBarrier || kUseBakerReadBarrier) { // Rely on the type resolution or initialization and marking to save everything we need. - // Request a temp to hold the BSS entry location for the slow path. - locations->AddTemp(Location::RequiresRegister()); RegisterSet caller_saves = RegisterSet::Empty(); InvokeRuntimeCallingConvention calling_convention; caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); @@ -6014,7 +6087,6 @@ void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) NO_THREAD_S ? kWithoutReadBarrier : kCompilerReadBarrierOption; bool generate_null_check = false; - CodeGeneratorMIPS64::PcRelativePatchInfo* bss_info_high = nullptr; switch (load_kind) { case HLoadClass::LoadKind::kReferrersClass: DCHECK(!cls->CanCallRuntime()); @@ -6064,17 +6136,14 @@ void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) NO_THREAD_S break; } case HLoadClass::LoadKind::kBssEntry: { - bss_info_high = codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex()); + CodeGeneratorMIPS64::PcRelativePatchInfo* bss_info_high = + codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex()); CodeGeneratorMIPS64::PcRelativePatchInfo* info_low = codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex(), bss_info_high); - constexpr bool non_baker_read_barrier = kUseReadBarrier && !kUseBakerReadBarrier; - GpuRegister temp = non_baker_read_barrier - ? out - : locations->GetTemp(0).AsRegister(); - codegen_->EmitPcRelativeAddressPlaceholderHigh(bss_info_high, temp); + codegen_->EmitPcRelativeAddressPlaceholderHigh(bss_info_high, out); GenerateGcRootFieldLoad(cls, out_loc, - temp, + out, /* placeholder */ 0x5678, read_barrier_option, &info_low->label); @@ -6098,7 +6167,7 @@ void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) NO_THREAD_S if (generate_null_check || cls->MustGenerateClinitCheck()) { DCHECK(cls->CanCallRuntime()); SlowPathCodeMIPS64* slow_path = new (codegen_->GetScopedAllocator()) LoadClassSlowPathMIPS64( - cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck(), bss_info_high); + cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck()); codegen_->AddSlowPath(slow_path); if (generate_null_check) { __ Beqzc(out, slow_path->GetEntryLabel()); @@ -6146,8 +6215,6 @@ void LocationsBuilderMIPS64::VisitLoadString(HLoadString* load) { if (load_kind == HLoadString::LoadKind::kBssEntry) { if (!kUseReadBarrier || kUseBakerReadBarrier) { // Rely on the pResolveString and marking to save everything we need. - // Request a temp to hold the BSS entry location for the slow path. - locations->AddTemp(Location::RequiresRegister()); RegisterSet caller_saves = RegisterSet::Empty(); InvokeRuntimeCallingConvention calling_convention; caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); @@ -6203,19 +6270,15 @@ void InstructionCodeGeneratorMIPS64::VisitLoadString(HLoadString* load) NO_THREA codegen_->NewStringBssEntryPatch(load->GetDexFile(), load->GetStringIndex()); CodeGeneratorMIPS64::PcRelativePatchInfo* info_low = codegen_->NewStringBssEntryPatch(load->GetDexFile(), load->GetStringIndex(), info_high); - constexpr bool non_baker_read_barrier = kUseReadBarrier && !kUseBakerReadBarrier; - GpuRegister temp = non_baker_read_barrier - ? out - : locations->GetTemp(0).AsRegister(); - codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, temp); + codegen_->EmitPcRelativeAddressPlaceholderHigh(info_high, out); GenerateGcRootFieldLoad(load, out_loc, - temp, + out, /* placeholder */ 0x5678, kCompilerReadBarrierOption, &info_low->label); SlowPathCodeMIPS64* slow_path = - new (codegen_->GetScopedAllocator()) LoadStringSlowPathMIPS64(load, info_high); + new (codegen_->GetScopedAllocator()) LoadStringSlowPathMIPS64(load); codegen_->AddSlowPath(slow_path); __ Beqzc(out, slow_path->GetEntryLabel()); __ Bind(slow_path->GetExitLabel()); diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index 2a95b3775df5a4d55128cf40f198190c802b67f2..d479410f077ba627263e994a7b0e5fdc02b66760 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -142,6 +142,7 @@ class ParallelMoveResolverMIPS64 : public ParallelMoveResolverWithSwap { void RestoreScratch(int reg) OVERRIDE; void Exchange(int index1, int index2, bool double_slot); + void ExchangeQuadSlots(int index1, int index2); Mips64Assembler* GetAssembler() const; diff --git a/compiler/optimizing/code_generator_utils.cc b/compiler/optimizing/code_generator_utils.cc index 96fe2a17e6081897bfcbf798b588ceec8a229061..dd47a1fc6ccac705e208e53093915de1c2b87ce3 100644 --- a/compiler/optimizing/code_generator_utils.cc +++ b/compiler/optimizing/code_generator_utils.cc @@ -15,9 +15,10 @@ */ #include "code_generator_utils.h" -#include "nodes.h" -#include "base/logging.h" +#include + +#include "nodes.h" namespace art { diff --git a/compiler/optimizing/code_generator_vector_mips.cc b/compiler/optimizing/code_generator_vector_mips.cc index 384b6421457e91fa347209c2d08b0c7bced78400..3cf150a6b81cdb8d53889ef86886f4787c95b318 100644 --- a/compiler/optimizing/code_generator_vector_mips.cc +++ b/compiler/optimizing/code_generator_vector_mips.cc @@ -1071,11 +1071,195 @@ void InstructionCodeGeneratorMIPS::VisitVecMultiplyAccumulate(HVecMultiplyAccumu void LocationsBuilderMIPS::VisitVecSADAccumulate(HVecSADAccumulate* instruction) { CreateVecAccumLocations(GetGraph()->GetAllocator(), instruction); + LocationSummary* locations = instruction->GetLocations(); + // All conversions require at least one temporary register. + locations->AddTemp(Location::RequiresFpuRegister()); + // Some conversions require a second temporary register. + HVecOperation* a = instruction->InputAt(1)->AsVecOperation(); + HVecOperation* b = instruction->InputAt(2)->AsVecOperation(); + DCHECK_EQ(HVecOperation::ToSignedType(a->GetPackedType()), + HVecOperation::ToSignedType(b->GetPackedType())); + switch (a->GetPackedType()) { + case DataType::Type::kInt32: + if (instruction->GetPackedType() == DataType::Type::kInt32) { + break; + } + FALLTHROUGH_INTENDED; + case DataType::Type::kUint8: + case DataType::Type::kInt8: + case DataType::Type::kUint16: + case DataType::Type::kInt16: + locations->AddTemp(Location::RequiresFpuRegister()); + break; + default: + break; + } } void InstructionCodeGeneratorMIPS::VisitVecSADAccumulate(HVecSADAccumulate* instruction) { - LOG(FATAL) << "No SIMD for " << instruction->GetId(); - // TODO: implement this, location helper already filled out (shared with MulAcc). + LocationSummary* locations = instruction->GetLocations(); + VectorRegister acc = VectorRegisterFrom(locations->InAt(0)); + VectorRegister left = VectorRegisterFrom(locations->InAt(1)); + VectorRegister right = VectorRegisterFrom(locations->InAt(2)); + VectorRegister tmp = static_cast(FTMP); + VectorRegister tmp1 = VectorRegisterFrom(locations->GetTemp(0)); + + DCHECK(locations->InAt(0).Equals(locations->Out())); + + // Handle all feasible acc_T += sad(a_S, b_S) type combinations (T x S). + HVecOperation* a = instruction->InputAt(1)->AsVecOperation(); + HVecOperation* b = instruction->InputAt(2)->AsVecOperation(); + DCHECK_EQ(HVecOperation::ToSignedType(a->GetPackedType()), + HVecOperation::ToSignedType(b->GetPackedType())); + switch (a->GetPackedType()) { + case DataType::Type::kUint8: + case DataType::Type::kInt8: + DCHECK_EQ(16u, a->GetVectorLength()); + switch (instruction->GetPackedType()) { + case DataType::Type::kUint16: + case DataType::Type::kInt16: { + DCHECK_EQ(8u, instruction->GetVectorLength()); + VectorRegister tmp2 = VectorRegisterFrom(locations->GetTemp(1)); + __ FillB(tmp, ZERO); + __ Hadd_sH(tmp1, left, tmp); + __ Hadd_sH(tmp2, right, tmp); + __ Asub_sH(tmp1, tmp1, tmp2); + __ AddvH(acc, acc, tmp1); + __ Hadd_sH(tmp1, tmp, left); + __ Hadd_sH(tmp2, tmp, right); + __ Asub_sH(tmp1, tmp1, tmp2); + __ AddvH(acc, acc, tmp1); + break; + } + case DataType::Type::kInt32: { + DCHECK_EQ(4u, instruction->GetVectorLength()); + VectorRegister tmp2 = VectorRegisterFrom(locations->GetTemp(1)); + __ FillB(tmp, ZERO); + __ Hadd_sH(tmp1, left, tmp); + __ Hadd_sH(tmp2, right, tmp); + __ Asub_sH(tmp1, tmp1, tmp2); + __ Hadd_sW(tmp1, tmp1, tmp1); + __ AddvW(acc, acc, tmp1); + __ Hadd_sH(tmp1, tmp, left); + __ Hadd_sH(tmp2, tmp, right); + __ Asub_sH(tmp1, tmp1, tmp2); + __ Hadd_sW(tmp1, tmp1, tmp1); + __ AddvW(acc, acc, tmp1); + break; + } + case DataType::Type::kInt64: { + DCHECK_EQ(2u, instruction->GetVectorLength()); + VectorRegister tmp2 = VectorRegisterFrom(locations->GetTemp(1)); + __ FillB(tmp, ZERO); + __ Hadd_sH(tmp1, left, tmp); + __ Hadd_sH(tmp2, right, tmp); + __ Asub_sH(tmp1, tmp1, tmp2); + __ Hadd_sW(tmp1, tmp1, tmp1); + __ Hadd_sD(tmp1, tmp1, tmp1); + __ AddvD(acc, acc, tmp1); + __ Hadd_sH(tmp1, tmp, left); + __ Hadd_sH(tmp2, tmp, right); + __ Asub_sH(tmp1, tmp1, tmp2); + __ Hadd_sW(tmp1, tmp1, tmp1); + __ Hadd_sD(tmp1, tmp1, tmp1); + __ AddvD(acc, acc, tmp1); + break; + } + default: + LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); + } + break; + case DataType::Type::kUint16: + case DataType::Type::kInt16: + DCHECK_EQ(8u, a->GetVectorLength()); + switch (instruction->GetPackedType()) { + case DataType::Type::kInt32: { + DCHECK_EQ(4u, instruction->GetVectorLength()); + VectorRegister tmp2 = VectorRegisterFrom(locations->GetTemp(1)); + __ FillH(tmp, ZERO); + __ Hadd_sW(tmp1, left, tmp); + __ Hadd_sW(tmp2, right, tmp); + __ Asub_sW(tmp1, tmp1, tmp2); + __ AddvW(acc, acc, tmp1); + __ Hadd_sW(tmp1, tmp, left); + __ Hadd_sW(tmp2, tmp, right); + __ Asub_sW(tmp1, tmp1, tmp2); + __ AddvW(acc, acc, tmp1); + break; + } + case DataType::Type::kInt64: { + DCHECK_EQ(2u, instruction->GetVectorLength()); + VectorRegister tmp2 = VectorRegisterFrom(locations->GetTemp(1)); + __ FillH(tmp, ZERO); + __ Hadd_sW(tmp1, left, tmp); + __ Hadd_sW(tmp2, right, tmp); + __ Asub_sW(tmp1, tmp1, tmp2); + __ Hadd_sD(tmp1, tmp1, tmp1); + __ AddvD(acc, acc, tmp1); + __ Hadd_sW(tmp1, tmp, left); + __ Hadd_sW(tmp2, tmp, right); + __ Asub_sW(tmp1, tmp1, tmp2); + __ Hadd_sD(tmp1, tmp1, tmp1); + __ AddvD(acc, acc, tmp1); + break; + } + default: + LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); + } + break; + case DataType::Type::kInt32: + DCHECK_EQ(4u, a->GetVectorLength()); + switch (instruction->GetPackedType()) { + case DataType::Type::kInt32: { + DCHECK_EQ(4u, instruction->GetVectorLength()); + __ FillW(tmp, ZERO); + __ SubvW(tmp1, left, right); + __ Add_aW(tmp1, tmp1, tmp); + __ AddvW(acc, acc, tmp1); + break; + } + case DataType::Type::kInt64: { + DCHECK_EQ(2u, instruction->GetVectorLength()); + VectorRegister tmp2 = VectorRegisterFrom(locations->GetTemp(1)); + __ FillW(tmp, ZERO); + __ Hadd_sD(tmp1, left, tmp); + __ Hadd_sD(tmp2, right, tmp); + __ Asub_sD(tmp1, tmp1, tmp2); + __ AddvD(acc, acc, tmp1); + __ Hadd_sD(tmp1, tmp, left); + __ Hadd_sD(tmp2, tmp, right); + __ Asub_sD(tmp1, tmp1, tmp2); + __ AddvD(acc, acc, tmp1); + break; + } + default: + LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); + } + break; + case DataType::Type::kInt64: { + DCHECK_EQ(2u, a->GetVectorLength()); + switch (instruction->GetPackedType()) { + case DataType::Type::kInt64: { + DCHECK_EQ(2u, instruction->GetVectorLength()); + __ FillW(tmp, ZERO); + __ SubvD(tmp1, left, right); + __ Add_aD(tmp1, tmp1, tmp); + __ AddvD(acc, acc, tmp1); + break; + } + default: + LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); + } + break; + } + default: + LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); + } } // Helper to set up locations for vector memory operations. diff --git a/compiler/optimizing/code_generator_vector_mips64.cc b/compiler/optimizing/code_generator_vector_mips64.cc index 0c59b7344a449f084794ca7313da5b0113576548..2d69533f21e3d6440899e498005eb17de35461c2 100644 --- a/compiler/optimizing/code_generator_vector_mips64.cc +++ b/compiler/optimizing/code_generator_vector_mips64.cc @@ -1069,11 +1069,195 @@ void InstructionCodeGeneratorMIPS64::VisitVecMultiplyAccumulate(HVecMultiplyAccu void LocationsBuilderMIPS64::VisitVecSADAccumulate(HVecSADAccumulate* instruction) { CreateVecAccumLocations(GetGraph()->GetAllocator(), instruction); + LocationSummary* locations = instruction->GetLocations(); + // All conversions require at least one temporary register. + locations->AddTemp(Location::RequiresFpuRegister()); + // Some conversions require a second temporary register. + HVecOperation* a = instruction->InputAt(1)->AsVecOperation(); + HVecOperation* b = instruction->InputAt(2)->AsVecOperation(); + DCHECK_EQ(HVecOperation::ToSignedType(a->GetPackedType()), + HVecOperation::ToSignedType(b->GetPackedType())); + switch (a->GetPackedType()) { + case DataType::Type::kInt32: + if (instruction->GetPackedType() == DataType::Type::kInt32) { + break; + } + FALLTHROUGH_INTENDED; + case DataType::Type::kUint8: + case DataType::Type::kInt8: + case DataType::Type::kUint16: + case DataType::Type::kInt16: + locations->AddTemp(Location::RequiresFpuRegister()); + break; + default: + break; + } } void InstructionCodeGeneratorMIPS64::VisitVecSADAccumulate(HVecSADAccumulate* instruction) { - LOG(FATAL) << "No SIMD for " << instruction->GetId(); - // TODO: implement this, location helper already filled out (shared with MulAcc). + LocationSummary* locations = instruction->GetLocations(); + VectorRegister acc = VectorRegisterFrom(locations->InAt(0)); + VectorRegister left = VectorRegisterFrom(locations->InAt(1)); + VectorRegister right = VectorRegisterFrom(locations->InAt(2)); + VectorRegister tmp = static_cast(FTMP); + VectorRegister tmp1 = VectorRegisterFrom(locations->GetTemp(0)); + + DCHECK(locations->InAt(0).Equals(locations->Out())); + + // Handle all feasible acc_T += sad(a_S, b_S) type combinations (T x S). + HVecOperation* a = instruction->InputAt(1)->AsVecOperation(); + HVecOperation* b = instruction->InputAt(2)->AsVecOperation(); + DCHECK_EQ(HVecOperation::ToSignedType(a->GetPackedType()), + HVecOperation::ToSignedType(b->GetPackedType())); + switch (a->GetPackedType()) { + case DataType::Type::kUint8: + case DataType::Type::kInt8: + DCHECK_EQ(16u, a->GetVectorLength()); + switch (instruction->GetPackedType()) { + case DataType::Type::kUint16: + case DataType::Type::kInt16: { + DCHECK_EQ(8u, instruction->GetVectorLength()); + VectorRegister tmp2 = VectorRegisterFrom(locations->GetTemp(1)); + __ FillB(tmp, ZERO); + __ Hadd_sH(tmp1, left, tmp); + __ Hadd_sH(tmp2, right, tmp); + __ Asub_sH(tmp1, tmp1, tmp2); + __ AddvH(acc, acc, tmp1); + __ Hadd_sH(tmp1, tmp, left); + __ Hadd_sH(tmp2, tmp, right); + __ Asub_sH(tmp1, tmp1, tmp2); + __ AddvH(acc, acc, tmp1); + break; + } + case DataType::Type::kInt32: { + DCHECK_EQ(4u, instruction->GetVectorLength()); + VectorRegister tmp2 = VectorRegisterFrom(locations->GetTemp(1)); + __ FillB(tmp, ZERO); + __ Hadd_sH(tmp1, left, tmp); + __ Hadd_sH(tmp2, right, tmp); + __ Asub_sH(tmp1, tmp1, tmp2); + __ Hadd_sW(tmp1, tmp1, tmp1); + __ AddvW(acc, acc, tmp1); + __ Hadd_sH(tmp1, tmp, left); + __ Hadd_sH(tmp2, tmp, right); + __ Asub_sH(tmp1, tmp1, tmp2); + __ Hadd_sW(tmp1, tmp1, tmp1); + __ AddvW(acc, acc, tmp1); + break; + } + case DataType::Type::kInt64: { + DCHECK_EQ(2u, instruction->GetVectorLength()); + VectorRegister tmp2 = VectorRegisterFrom(locations->GetTemp(1)); + __ FillB(tmp, ZERO); + __ Hadd_sH(tmp1, left, tmp); + __ Hadd_sH(tmp2, right, tmp); + __ Asub_sH(tmp1, tmp1, tmp2); + __ Hadd_sW(tmp1, tmp1, tmp1); + __ Hadd_sD(tmp1, tmp1, tmp1); + __ AddvD(acc, acc, tmp1); + __ Hadd_sH(tmp1, tmp, left); + __ Hadd_sH(tmp2, tmp, right); + __ Asub_sH(tmp1, tmp1, tmp2); + __ Hadd_sW(tmp1, tmp1, tmp1); + __ Hadd_sD(tmp1, tmp1, tmp1); + __ AddvD(acc, acc, tmp1); + break; + } + default: + LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); + } + break; + case DataType::Type::kUint16: + case DataType::Type::kInt16: + DCHECK_EQ(8u, a->GetVectorLength()); + switch (instruction->GetPackedType()) { + case DataType::Type::kInt32: { + DCHECK_EQ(4u, instruction->GetVectorLength()); + VectorRegister tmp2 = VectorRegisterFrom(locations->GetTemp(1)); + __ FillH(tmp, ZERO); + __ Hadd_sW(tmp1, left, tmp); + __ Hadd_sW(tmp2, right, tmp); + __ Asub_sW(tmp1, tmp1, tmp2); + __ AddvW(acc, acc, tmp1); + __ Hadd_sW(tmp1, tmp, left); + __ Hadd_sW(tmp2, tmp, right); + __ Asub_sW(tmp1, tmp1, tmp2); + __ AddvW(acc, acc, tmp1); + break; + } + case DataType::Type::kInt64: { + DCHECK_EQ(2u, instruction->GetVectorLength()); + VectorRegister tmp2 = VectorRegisterFrom(locations->GetTemp(1)); + __ FillH(tmp, ZERO); + __ Hadd_sW(tmp1, left, tmp); + __ Hadd_sW(tmp2, right, tmp); + __ Asub_sW(tmp1, tmp1, tmp2); + __ Hadd_sD(tmp1, tmp1, tmp1); + __ AddvD(acc, acc, tmp1); + __ Hadd_sW(tmp1, tmp, left); + __ Hadd_sW(tmp2, tmp, right); + __ Asub_sW(tmp1, tmp1, tmp2); + __ Hadd_sD(tmp1, tmp1, tmp1); + __ AddvD(acc, acc, tmp1); + break; + } + default: + LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); + } + break; + case DataType::Type::kInt32: + DCHECK_EQ(4u, a->GetVectorLength()); + switch (instruction->GetPackedType()) { + case DataType::Type::kInt32: { + DCHECK_EQ(4u, instruction->GetVectorLength()); + __ FillW(tmp, ZERO); + __ SubvW(tmp1, left, right); + __ Add_aW(tmp1, tmp1, tmp); + __ AddvW(acc, acc, tmp1); + break; + } + case DataType::Type::kInt64: { + DCHECK_EQ(2u, instruction->GetVectorLength()); + VectorRegister tmp2 = VectorRegisterFrom(locations->GetTemp(1)); + __ FillW(tmp, ZERO); + __ Hadd_sD(tmp1, left, tmp); + __ Hadd_sD(tmp2, right, tmp); + __ Asub_sD(tmp1, tmp1, tmp2); + __ AddvD(acc, acc, tmp1); + __ Hadd_sD(tmp1, tmp, left); + __ Hadd_sD(tmp2, tmp, right); + __ Asub_sD(tmp1, tmp1, tmp2); + __ AddvD(acc, acc, tmp1); + break; + } + default: + LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); + } + break; + case DataType::Type::kInt64: { + DCHECK_EQ(2u, a->GetVectorLength()); + switch (instruction->GetPackedType()) { + case DataType::Type::kInt64: { + DCHECK_EQ(2u, instruction->GetVectorLength()); + __ FillD(tmp, ZERO); + __ SubvD(tmp1, left, right); + __ Add_aD(tmp1, tmp1, tmp); + __ AddvD(acc, acc, tmp1); + break; + } + default: + LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); + } + break; + } + default: + LOG(FATAL) << "Unsupported SIMD type"; + UNREACHABLE(); + } } // Helper to set up locations for vector memory operations. diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index ad0e71aaf4e5278654eb2e1d89c8524bbcf02bae..ba222fe532883e067bde2da3ae4cc9f0fc4fea6d 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -240,13 +240,6 @@ class LoadStringSlowPathX86 : public SlowPathCode { x86_codegen->Move32(locations->Out(), Location::RegisterLocation(EAX)); RestoreLiveRegisters(codegen, locations); - // Store the resolved String to the BSS entry. - Register method_address = locations->InAt(0).AsRegister(); - __ movl(Address(method_address, CodeGeneratorX86::kDummy32BitOffset), - locations->Out().AsRegister()); - Label* fixup_label = x86_codegen->NewStringBssEntryPatch(instruction_->AsLoadString()); - __ Bind(fixup_label); - __ jmp(GetExitLabel()); } @@ -293,16 +286,6 @@ class LoadClassSlowPathX86 : public SlowPathCode { x86_codegen->Move32(out, Location::RegisterLocation(EAX)); } RestoreLiveRegisters(codegen, locations); - // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry. - DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_); - if (cls_ == instruction_ && cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry) { - DCHECK(out.IsValid()); - Register method_address = locations->InAt(0).AsRegister(); - __ movl(Address(method_address, CodeGeneratorX86::kDummy32BitOffset), - locations->Out().AsRegister()); - Label* fixup_label = x86_codegen->NewTypeBssEntryPatch(cls_); - __ Bind(fixup_label); - } __ jmp(GetExitLabel()); } @@ -334,7 +317,14 @@ class TypeCheckSlowPathX86 : public SlowPathCode { CodeGeneratorX86* x86_codegen = down_cast(codegen); __ Bind(GetEntryLabel()); - if (!is_fatal_) { + if (kPoisonHeapReferences && + instruction_->IsCheckCast() && + instruction_->AsCheckCast()->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck) { + // First, unpoison the `cls` reference that was poisoned for direct memory comparison. + __ UnpoisonHeapReference(locations->InAt(1).AsRegister()); + } + + if (!is_fatal_ || instruction_->CanThrowIntoCatchBlock()) { SaveLiveRegisters(codegen, locations); } @@ -5749,24 +5739,18 @@ X86Assembler* ParallelMoveResolverX86::GetAssembler() const { return codegen_->GetAssembler(); } -void ParallelMoveResolverX86::MoveMemoryToMemory32(int dst, int src) { +void ParallelMoveResolverX86::MoveMemoryToMemory(int dst, int src, int number_of_words) { ScratchRegisterScope ensure_scratch( this, kNoRegister, EAX, codegen_->GetNumberOfCoreRegisters()); Register temp_reg = static_cast(ensure_scratch.GetRegister()); int stack_offset = ensure_scratch.IsSpilled() ? kX86WordSize : 0; - __ movl(temp_reg, Address(ESP, src + stack_offset)); - __ movl(Address(ESP, dst + stack_offset), temp_reg); -} -void ParallelMoveResolverX86::MoveMemoryToMemory64(int dst, int src) { - ScratchRegisterScope ensure_scratch( - this, kNoRegister, EAX, codegen_->GetNumberOfCoreRegisters()); - Register temp_reg = static_cast(ensure_scratch.GetRegister()); - int stack_offset = ensure_scratch.IsSpilled() ? kX86WordSize : 0; - __ movl(temp_reg, Address(ESP, src + stack_offset)); - __ movl(Address(ESP, dst + stack_offset), temp_reg); - __ movl(temp_reg, Address(ESP, src + stack_offset + kX86WordSize)); - __ movl(Address(ESP, dst + stack_offset + kX86WordSize), temp_reg); + // Now that temp register is available (possibly spilled), move blocks of memory. + for (int i = 0; i < number_of_words; i++) { + __ movl(temp_reg, Address(ESP, src + stack_offset)); + __ movl(Address(ESP, dst + stack_offset), temp_reg); + stack_offset += kX86WordSize; + } } void ParallelMoveResolverX86::EmitMove(size_t index) { @@ -5817,7 +5801,7 @@ void ParallelMoveResolverX86::EmitMove(size_t index) { __ movss(destination.AsFpuRegister(), Address(ESP, source.GetStackIndex())); } else { DCHECK(destination.IsStackSlot()); - MoveMemoryToMemory32(destination.GetStackIndex(), source.GetStackIndex()); + MoveMemoryToMemory(destination.GetStackIndex(), source.GetStackIndex(), 1); } } else if (source.IsDoubleStackSlot()) { if (destination.IsRegisterPair()) { @@ -5828,11 +5812,15 @@ void ParallelMoveResolverX86::EmitMove(size_t index) { __ movsd(destination.AsFpuRegister(), Address(ESP, source.GetStackIndex())); } else { DCHECK(destination.IsDoubleStackSlot()) << destination; - MoveMemoryToMemory64(destination.GetStackIndex(), source.GetStackIndex()); + MoveMemoryToMemory(destination.GetStackIndex(), source.GetStackIndex(), 2); } } else if (source.IsSIMDStackSlot()) { - DCHECK(destination.IsFpuRegister()); - __ movups(destination.AsFpuRegister(), Address(ESP, source.GetStackIndex())); + if (destination.IsFpuRegister()) { + __ movups(destination.AsFpuRegister(), Address(ESP, source.GetStackIndex())); + } else { + DCHECK(destination.IsSIMDStackSlot()); + MoveMemoryToMemory(destination.GetStackIndex(), source.GetStackIndex(), 4); + } } else if (source.IsConstant()) { HConstant* constant = source.GetConstant(); if (constant->IsIntConstant() || constant->IsNullConstant()) { @@ -5932,7 +5920,16 @@ void ParallelMoveResolverX86::Exchange32(XmmRegister reg, int mem) { __ movd(reg, temp_reg); } -void ParallelMoveResolverX86::Exchange(int mem1, int mem2) { +void ParallelMoveResolverX86::Exchange128(XmmRegister reg, int mem) { + size_t extra_slot = 4 * kX86WordSize; + __ subl(ESP, Immediate(extra_slot)); + __ movups(Address(ESP, 0), XmmRegister(reg)); + ExchangeMemory(0, mem + extra_slot, 4); + __ movups(XmmRegister(reg), Address(ESP, 0)); + __ addl(ESP, Immediate(extra_slot)); +} + +void ParallelMoveResolverX86::ExchangeMemory(int mem1, int mem2, int number_of_words) { ScratchRegisterScope ensure_scratch1( this, kNoRegister, EAX, codegen_->GetNumberOfCoreRegisters()); @@ -5942,10 +5939,15 @@ void ParallelMoveResolverX86::Exchange(int mem1, int mem2) { int stack_offset = ensure_scratch1.IsSpilled() ? kX86WordSize : 0; stack_offset += ensure_scratch2.IsSpilled() ? kX86WordSize : 0; - __ movl(static_cast(ensure_scratch1.GetRegister()), Address(ESP, mem1 + stack_offset)); - __ movl(static_cast(ensure_scratch2.GetRegister()), Address(ESP, mem2 + stack_offset)); - __ movl(Address(ESP, mem2 + stack_offset), static_cast(ensure_scratch1.GetRegister())); - __ movl(Address(ESP, mem1 + stack_offset), static_cast(ensure_scratch2.GetRegister())); + + // Now that temp registers are available (possibly spilled), exchange blocks of memory. + for (int i = 0; i < number_of_words; i++) { + __ movl(static_cast(ensure_scratch1.GetRegister()), Address(ESP, mem1 + stack_offset)); + __ movl(static_cast(ensure_scratch2.GetRegister()), Address(ESP, mem2 + stack_offset)); + __ movl(Address(ESP, mem2 + stack_offset), static_cast(ensure_scratch1.GetRegister())); + __ movl(Address(ESP, mem1 + stack_offset), static_cast(ensure_scratch2.GetRegister())); + stack_offset += kX86WordSize; + } } void ParallelMoveResolverX86::EmitSwap(size_t index) { @@ -5964,7 +5966,7 @@ void ParallelMoveResolverX86::EmitSwap(size_t index) { } else if (source.IsStackSlot() && destination.IsRegister()) { Exchange(destination.AsRegister(), source.GetStackIndex()); } else if (source.IsStackSlot() && destination.IsStackSlot()) { - Exchange(destination.GetStackIndex(), source.GetStackIndex()); + ExchangeMemory(destination.GetStackIndex(), source.GetStackIndex(), 1); } else if (source.IsFpuRegister() && destination.IsFpuRegister()) { // Use XOR Swap algorithm to avoid a temporary. DCHECK_NE(source.reg(), destination.reg()); @@ -6000,8 +6002,13 @@ void ParallelMoveResolverX86::EmitSwap(size_t index) { // Move the high double to the low double. __ psrldq(reg, Immediate(8)); } else if (destination.IsDoubleStackSlot() && source.IsDoubleStackSlot()) { - Exchange(destination.GetStackIndex(), source.GetStackIndex()); - Exchange(destination.GetHighStackIndex(kX86WordSize), source.GetHighStackIndex(kX86WordSize)); + ExchangeMemory(destination.GetStackIndex(), source.GetStackIndex(), 2); + } else if (source.IsSIMDStackSlot() && destination.IsSIMDStackSlot()) { + ExchangeMemory(destination.GetStackIndex(), source.GetStackIndex(), 4); + } else if (source.IsFpuRegister() && destination.IsSIMDStackSlot()) { + Exchange128(source.AsFpuRegister(), destination.GetStackIndex()); + } else if (destination.IsFpuRegister() && source.IsSIMDStackSlot()) { + Exchange128(destination.AsFpuRegister(), source.GetStackIndex()); } else { LOG(FATAL) << "Unimplemented: source: " << source << ", destination: " << destination; } @@ -6386,7 +6393,7 @@ static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) { // interface pointer, one for loading the current interface. // The other checks have one temp for loading the object's class. static size_t NumberOfCheckCastTemps(TypeCheckKind type_check_kind) { - if (type_check_kind == TypeCheckKind::kInterfaceCheck && !kPoisonHeapReferences) { + if (type_check_kind == TypeCheckKind::kInterfaceCheck) { return 2; } return 1 + NumberOfInstanceOfTemps(type_check_kind); @@ -6400,11 +6407,12 @@ void LocationsBuilderX86::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kExactCheck: case TypeCheckKind::kAbstractClassCheck: case TypeCheckKind::kClassHierarchyCheck: - case TypeCheckKind::kArrayObjectCheck: - call_kind = - kEmitCompilerReadBarrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall; - baker_read_barrier_slow_path = kUseBakerReadBarrier; + case TypeCheckKind::kArrayObjectCheck: { + bool needs_read_barrier = CodeGenerator::InstanceOfNeedsReadBarrier(instruction); + call_kind = needs_read_barrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall; + baker_read_barrier_slow_path = kUseBakerReadBarrier && needs_read_barrier; break; + } case TypeCheckKind::kArrayCheck: case TypeCheckKind::kUnresolvedCheck: case TypeCheckKind::kInterfaceCheck: @@ -6452,12 +6460,14 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { switch (type_check_kind) { case TypeCheckKind::kExactCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, - kCompilerReadBarrierOption); + read_barrier_option); if (cls.IsRegister()) { __ cmpl(out, cls.AsRegister()); } else { @@ -6473,12 +6483,14 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { } case TypeCheckKind::kAbstractClassCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, - kCompilerReadBarrierOption); + read_barrier_option); // If the class is abstract, we eagerly fetch the super class of the // object to avoid doing a comparison we know will fail. NearLabel loop; @@ -6488,7 +6500,7 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { out_loc, super_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); __ testl(out, out); // If `out` is null, we use it for the result, and jump to `done`. __ j(kEqual, &done); @@ -6507,12 +6519,14 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { } case TypeCheckKind::kClassHierarchyCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, - kCompilerReadBarrierOption); + read_barrier_option); // Walk over the class hierarchy to find a match. NearLabel loop, success; __ Bind(&loop); @@ -6528,7 +6542,7 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { out_loc, super_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); __ testl(out, out); __ j(kNotEqual, &loop); // If `out` is null, we use it for the result, and jump to `done`. @@ -6542,12 +6556,14 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { } case TypeCheckKind::kArrayObjectCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, - kCompilerReadBarrierOption); + read_barrier_option); // Do an exact check. NearLabel exact_check; if (cls.IsRegister()) { @@ -6563,7 +6579,7 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { out_loc, component_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); __ testl(out, out); // If `out` is null, we use it for the result, and jump to `done`. __ j(kEqual, &done); @@ -6647,30 +6663,9 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) { } } -static bool IsTypeCheckSlowPathFatal(TypeCheckKind type_check_kind, bool throws_into_catch) { - switch (type_check_kind) { - case TypeCheckKind::kExactCheck: - case TypeCheckKind::kAbstractClassCheck: - case TypeCheckKind::kClassHierarchyCheck: - case TypeCheckKind::kArrayObjectCheck: - return !throws_into_catch && !kEmitCompilerReadBarrier; - case TypeCheckKind::kInterfaceCheck: - return !throws_into_catch && !kEmitCompilerReadBarrier && !kPoisonHeapReferences; - case TypeCheckKind::kArrayCheck: - case TypeCheckKind::kUnresolvedCheck: - return false; - } - LOG(FATAL) << "Unreachable"; - UNREACHABLE(); -} - void LocationsBuilderX86::VisitCheckCast(HCheckCast* instruction) { - bool throws_into_catch = instruction->CanThrowIntoCatchBlock(); TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); - LocationSummary::CallKind call_kind = - IsTypeCheckSlowPathFatal(type_check_kind, throws_into_catch) - ? LocationSummary::kNoCall - : LocationSummary::kCallOnSlowPath; + LocationSummary::CallKind call_kind = CodeGenerator::GetCheckCastCallKind(instruction); LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); @@ -6708,12 +6703,7 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) { const uint32_t object_array_data_offset = mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value(); - // Always false for read barriers since we may need to go to the entrypoint for non-fatal cases - // from false negatives. The false negatives may come from avoiding read barriers below. Avoiding - // read barriers is done for performance and code size reasons. - bool is_type_check_slow_path_fatal = - IsTypeCheckSlowPathFatal(type_check_kind, instruction->CanThrowIntoCatchBlock()); - + bool is_type_check_slow_path_fatal = CodeGenerator::IsTypeCheckSlowPathFatal(instruction); SlowPathCode* type_check_slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathX86( instruction, is_type_check_slow_path_fatal); @@ -6866,44 +6856,40 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) { break; case TypeCheckKind::kInterfaceCheck: { - // Fast path for the interface check. Since we compare with a memory location in the inner - // loop we would need to have cls poisoned. However unpoisoning cls would reset the - // conditional flags and cause the conditional jump to be incorrect. Therefore we just jump - // to the slow path if we are running under poisoning. - if (!kPoisonHeapReferences) { - // Try to avoid read barriers to improve the fast path. We can not get false positives by - // doing this. - // /* HeapReference */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - temp_loc, - obj_loc, - class_offset, - kWithoutReadBarrier); - - // /* HeapReference */ temp = temp->iftable_ - GenerateReferenceLoadTwoRegisters(instruction, - temp_loc, - temp_loc, - iftable_offset, - kWithoutReadBarrier); - // Iftable is never null. - __ movl(maybe_temp2_loc.AsRegister(), Address(temp, array_length_offset)); - // Loop through the iftable and check if any class matches. - NearLabel start_loop; - __ Bind(&start_loop); - // Need to subtract first to handle the empty array case. - __ subl(maybe_temp2_loc.AsRegister(), Immediate(2)); - __ j(kNegative, type_check_slow_path->GetEntryLabel()); - // Go to next interface if the classes do not match. - __ cmpl(cls.AsRegister(), - CodeGeneratorX86::ArrayAddress(temp, - maybe_temp2_loc, - TIMES_4, - object_array_data_offset)); - __ j(kNotEqual, &start_loop); - } else { - __ jmp(type_check_slow_path->GetEntryLabel()); - } + // Fast path for the interface check. Try to avoid read barriers to improve the fast path. + // We can not get false positives by doing this. + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + kWithoutReadBarrier); + + // /* HeapReference */ temp = temp->iftable_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + temp_loc, + iftable_offset, + kWithoutReadBarrier); + // Iftable is never null. + __ movl(maybe_temp2_loc.AsRegister(), Address(temp, array_length_offset)); + // Maybe poison the `cls` for direct comparison with memory. + __ MaybePoisonHeapReference(cls.AsRegister()); + // Loop through the iftable and check if any class matches. + NearLabel start_loop; + __ Bind(&start_loop); + // Need to subtract first to handle the empty array case. + __ subl(maybe_temp2_loc.AsRegister(), Immediate(2)); + __ j(kNegative, type_check_slow_path->GetEntryLabel()); + // Go to next interface if the classes do not match. + __ cmpl(cls.AsRegister(), + CodeGeneratorX86::ArrayAddress(temp, + maybe_temp2_loc, + TIMES_4, + object_array_data_offset)); + __ j(kNotEqual, &start_loop); + // If `cls` was poisoned above, unpoison it. + __ MaybeUnpoisonHeapReference(cls.AsRegister()); break; } } diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 176e4dfda02578ce2d9b2d00e954e1ce8c02761d..40b7e3c54fae14e639c35ed9fad4fbc45904e22e 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -139,10 +139,10 @@ class ParallelMoveResolverX86 : public ParallelMoveResolverWithSwap { private: void Exchange(Register reg, int mem); - void Exchange(int mem1, int mem2); void Exchange32(XmmRegister reg, int mem); - void MoveMemoryToMemory32(int dst, int src); - void MoveMemoryToMemory64(int dst, int src); + void Exchange128(XmmRegister reg, int mem); + void ExchangeMemory(int mem1, int mem2, int number_of_words); + void MoveMemoryToMemory(int dst, int src, int number_of_words); CodeGeneratorX86* const codegen_; diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index d64a49704e94be16078008d502f2ae6b42ae71e9..caad7885bd42f08a9d99696f9c422b1e1f489ae4 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -273,15 +273,6 @@ class LoadClassSlowPathX86_64 : public SlowPathCode { } RestoreLiveRegisters(codegen, locations); - // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry. - DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_); - if (cls_ == instruction_ && cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry) { - DCHECK(out.IsValid()); - __ movl(Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, /* no_rip */ false), - locations->Out().AsRegister()); - Label* fixup_label = x86_64_codegen->NewTypeBssEntryPatch(cls_); - __ Bind(fixup_label); - } __ jmp(GetExitLabel()); } @@ -323,12 +314,6 @@ class LoadStringSlowPathX86_64 : public SlowPathCode { x86_64_codegen->Move(locations->Out(), Location::RegisterLocation(RAX)); RestoreLiveRegisters(codegen, locations); - // Store the resolved String to the BSS entry. - __ movl(Address::Absolute(CodeGeneratorX86_64::kDummy32BitOffset, /* no_rip */ false), - locations->Out().AsRegister()); - Label* fixup_label = x86_64_codegen->NewStringBssEntryPatch(instruction_->AsLoadString()); - __ Bind(fixup_label); - __ jmp(GetExitLabel()); } @@ -352,7 +337,14 @@ class TypeCheckSlowPathX86_64 : public SlowPathCode { CodeGeneratorX86_64* x86_64_codegen = down_cast(codegen); __ Bind(GetEntryLabel()); - if (!is_fatal_) { + if (kPoisonHeapReferences && + instruction_->IsCheckCast() && + instruction_->AsCheckCast()->GetTypeCheckKind() == TypeCheckKind::kInterfaceCheck) { + // First, unpoison the `cls` reference that was poisoned for direct memory comparison. + __ UnpoisonHeapReference(locations->InAt(1).AsRegister()); + } + + if (!is_fatal_ || instruction_->CanThrowIntoCatchBlock()) { SaveLiveRegisters(codegen, locations); } @@ -5235,9 +5227,17 @@ void ParallelMoveResolverX86_64::EmitMove(size_t index) { __ movq(Address(CpuRegister(RSP), destination.GetStackIndex()), CpuRegister(TMP)); } } else if (source.IsSIMDStackSlot()) { - DCHECK(destination.IsFpuRegister()); - __ movups(destination.AsFpuRegister(), - Address(CpuRegister(RSP), source.GetStackIndex())); + if (destination.IsFpuRegister()) { + __ movups(destination.AsFpuRegister(), + Address(CpuRegister(RSP), source.GetStackIndex())); + } else { + DCHECK(destination.IsSIMDStackSlot()); + size_t high = kX86_64WordSize; + __ movq(CpuRegister(TMP), Address(CpuRegister(RSP), source.GetStackIndex())); + __ movq(Address(CpuRegister(RSP), destination.GetStackIndex()), CpuRegister(TMP)); + __ movq(CpuRegister(TMP), Address(CpuRegister(RSP), source.GetStackIndex() + high)); + __ movq(Address(CpuRegister(RSP), destination.GetStackIndex() + high), CpuRegister(TMP)); + } } else if (source.IsConstant()) { HConstant* constant = source.GetConstant(); if (constant->IsIntConstant() || constant->IsNullConstant()) { @@ -5305,19 +5305,6 @@ void ParallelMoveResolverX86_64::Exchange32(CpuRegister reg, int mem) { __ movl(reg, CpuRegister(TMP)); } -void ParallelMoveResolverX86_64::Exchange32(int mem1, int mem2) { - ScratchRegisterScope ensure_scratch( - this, TMP, RAX, codegen_->GetNumberOfCoreRegisters()); - - int stack_offset = ensure_scratch.IsSpilled() ? kX86_64WordSize : 0; - __ movl(CpuRegister(TMP), Address(CpuRegister(RSP), mem1 + stack_offset)); - __ movl(CpuRegister(ensure_scratch.GetRegister()), - Address(CpuRegister(RSP), mem2 + stack_offset)); - __ movl(Address(CpuRegister(RSP), mem2 + stack_offset), CpuRegister(TMP)); - __ movl(Address(CpuRegister(RSP), mem1 + stack_offset), - CpuRegister(ensure_scratch.GetRegister())); -} - void ParallelMoveResolverX86_64::Exchange64(CpuRegister reg1, CpuRegister reg2) { __ movq(CpuRegister(TMP), reg1); __ movq(reg1, reg2); @@ -5330,19 +5317,6 @@ void ParallelMoveResolverX86_64::Exchange64(CpuRegister reg, int mem) { __ movq(reg, CpuRegister(TMP)); } -void ParallelMoveResolverX86_64::Exchange64(int mem1, int mem2) { - ScratchRegisterScope ensure_scratch( - this, TMP, RAX, codegen_->GetNumberOfCoreRegisters()); - - int stack_offset = ensure_scratch.IsSpilled() ? kX86_64WordSize : 0; - __ movq(CpuRegister(TMP), Address(CpuRegister(RSP), mem1 + stack_offset)); - __ movq(CpuRegister(ensure_scratch.GetRegister()), - Address(CpuRegister(RSP), mem2 + stack_offset)); - __ movq(Address(CpuRegister(RSP), mem2 + stack_offset), CpuRegister(TMP)); - __ movq(Address(CpuRegister(RSP), mem1 + stack_offset), - CpuRegister(ensure_scratch.GetRegister())); -} - void ParallelMoveResolverX86_64::Exchange32(XmmRegister reg, int mem) { __ movl(CpuRegister(TMP), Address(CpuRegister(RSP), mem)); __ movss(Address(CpuRegister(RSP), mem), reg); @@ -5355,6 +5329,48 @@ void ParallelMoveResolverX86_64::Exchange64(XmmRegister reg, int mem) { __ movd(reg, CpuRegister(TMP)); } +void ParallelMoveResolverX86_64::Exchange128(XmmRegister reg, int mem) { + size_t extra_slot = 2 * kX86_64WordSize; + __ subq(CpuRegister(RSP), Immediate(extra_slot)); + __ movups(Address(CpuRegister(RSP), 0), XmmRegister(reg)); + ExchangeMemory64(0, mem + extra_slot, 2); + __ movups(XmmRegister(reg), Address(CpuRegister(RSP), 0)); + __ addq(CpuRegister(RSP), Immediate(extra_slot)); +} + +void ParallelMoveResolverX86_64::ExchangeMemory32(int mem1, int mem2) { + ScratchRegisterScope ensure_scratch( + this, TMP, RAX, codegen_->GetNumberOfCoreRegisters()); + + int stack_offset = ensure_scratch.IsSpilled() ? kX86_64WordSize : 0; + __ movl(CpuRegister(TMP), Address(CpuRegister(RSP), mem1 + stack_offset)); + __ movl(CpuRegister(ensure_scratch.GetRegister()), + Address(CpuRegister(RSP), mem2 + stack_offset)); + __ movl(Address(CpuRegister(RSP), mem2 + stack_offset), CpuRegister(TMP)); + __ movl(Address(CpuRegister(RSP), mem1 + stack_offset), + CpuRegister(ensure_scratch.GetRegister())); +} + +void ParallelMoveResolverX86_64::ExchangeMemory64(int mem1, int mem2, int num_of_qwords) { + ScratchRegisterScope ensure_scratch( + this, TMP, RAX, codegen_->GetNumberOfCoreRegisters()); + + int stack_offset = ensure_scratch.IsSpilled() ? kX86_64WordSize : 0; + + // Now that temp registers are available (possibly spilled), exchange blocks of memory. + for (int i = 0; i < num_of_qwords; i++) { + __ movq(CpuRegister(TMP), + Address(CpuRegister(RSP), mem1 + stack_offset)); + __ movq(CpuRegister(ensure_scratch.GetRegister()), + Address(CpuRegister(RSP), mem2 + stack_offset)); + __ movq(Address(CpuRegister(RSP), mem2 + stack_offset), + CpuRegister(TMP)); + __ movq(Address(CpuRegister(RSP), mem1 + stack_offset), + CpuRegister(ensure_scratch.GetRegister())); + stack_offset += kX86_64WordSize; + } +} + void ParallelMoveResolverX86_64::EmitSwap(size_t index) { MoveOperands* move = moves_[index]; Location source = move->GetSource(); @@ -5367,13 +5383,13 @@ void ParallelMoveResolverX86_64::EmitSwap(size_t index) { } else if (source.IsStackSlot() && destination.IsRegister()) { Exchange32(destination.AsRegister(), source.GetStackIndex()); } else if (source.IsStackSlot() && destination.IsStackSlot()) { - Exchange32(destination.GetStackIndex(), source.GetStackIndex()); + ExchangeMemory32(destination.GetStackIndex(), source.GetStackIndex()); } else if (source.IsRegister() && destination.IsDoubleStackSlot()) { Exchange64(source.AsRegister(), destination.GetStackIndex()); } else if (source.IsDoubleStackSlot() && destination.IsRegister()) { Exchange64(destination.AsRegister(), source.GetStackIndex()); } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) { - Exchange64(destination.GetStackIndex(), source.GetStackIndex()); + ExchangeMemory64(destination.GetStackIndex(), source.GetStackIndex(), 1); } else if (source.IsFpuRegister() && destination.IsFpuRegister()) { __ movd(CpuRegister(TMP), source.AsFpuRegister()); __ movaps(source.AsFpuRegister(), destination.AsFpuRegister()); @@ -5386,6 +5402,12 @@ void ParallelMoveResolverX86_64::EmitSwap(size_t index) { Exchange64(source.AsFpuRegister(), destination.GetStackIndex()); } else if (source.IsDoubleStackSlot() && destination.IsFpuRegister()) { Exchange64(destination.AsFpuRegister(), source.GetStackIndex()); + } else if (source.IsSIMDStackSlot() && destination.IsSIMDStackSlot()) { + ExchangeMemory64(destination.GetStackIndex(), source.GetStackIndex(), 2); + } else if (source.IsFpuRegister() && destination.IsSIMDStackSlot()) { + Exchange128(source.AsFpuRegister(), destination.GetStackIndex()); + } else if (destination.IsFpuRegister() && source.IsSIMDStackSlot()) { + Exchange128(destination.AsFpuRegister(), source.GetStackIndex()); } else { LOG(FATAL) << "Unimplemented swap between " << source << " and " << destination; } @@ -5744,7 +5766,7 @@ void InstructionCodeGeneratorX86_64::VisitThrow(HThrow* instruction) { } static bool CheckCastTypeCheckNeedsATemporary(TypeCheckKind type_check_kind) { - if (type_check_kind == TypeCheckKind::kInterfaceCheck && !kPoisonHeapReferences) { + if (type_check_kind == TypeCheckKind::kInterfaceCheck) { // We need a temporary for holding the iftable length. return true; } @@ -5771,11 +5793,12 @@ void LocationsBuilderX86_64::VisitInstanceOf(HInstanceOf* instruction) { case TypeCheckKind::kExactCheck: case TypeCheckKind::kAbstractClassCheck: case TypeCheckKind::kClassHierarchyCheck: - case TypeCheckKind::kArrayObjectCheck: - call_kind = - kEmitCompilerReadBarrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall; - baker_read_barrier_slow_path = kUseBakerReadBarrier; + case TypeCheckKind::kArrayObjectCheck: { + bool needs_read_barrier = CodeGenerator::InstanceOfNeedsReadBarrier(instruction); + call_kind = needs_read_barrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall; + baker_read_barrier_slow_path = kUseBakerReadBarrier && needs_read_barrier; break; + } case TypeCheckKind::kArrayCheck: case TypeCheckKind::kUnresolvedCheck: case TypeCheckKind::kInterfaceCheck: @@ -5826,12 +5849,14 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { switch (type_check_kind) { case TypeCheckKind::kExactCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, - kCompilerReadBarrierOption); + read_barrier_option); if (cls.IsRegister()) { __ cmpl(out, cls.AsRegister()); } else { @@ -5852,12 +5877,14 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { } case TypeCheckKind::kAbstractClassCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, - kCompilerReadBarrierOption); + read_barrier_option); // If the class is abstract, we eagerly fetch the super class of the // object to avoid doing a comparison we know will fail. NearLabel loop, success; @@ -5867,7 +5894,7 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { out_loc, super_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); __ testl(out, out); // If `out` is null, we use it for the result, and jump to `done`. __ j(kEqual, &done); @@ -5886,12 +5913,14 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { } case TypeCheckKind::kClassHierarchyCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, - kCompilerReadBarrierOption); + read_barrier_option); // Walk over the class hierarchy to find a match. NearLabel loop, success; __ Bind(&loop); @@ -5907,7 +5936,7 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { out_loc, super_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); __ testl(out, out); __ j(kNotEqual, &loop); // If `out` is null, we use it for the result, and jump to `done`. @@ -5921,12 +5950,14 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { } case TypeCheckKind::kArrayObjectCheck: { + ReadBarrierOption read_barrier_option = + CodeGenerator::ReadBarrierOptionForInstanceOf(instruction); // /* HeapReference */ out = obj->klass_ GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, - kCompilerReadBarrierOption); + read_barrier_option); // Do an exact check. NearLabel exact_check; if (cls.IsRegister()) { @@ -5942,7 +5973,7 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { out_loc, component_offset, maybe_temp_loc, - kCompilerReadBarrierOption); + read_barrier_option); __ testl(out, out); // If `out` is null, we use it for the result, and jump to `done`. __ j(kEqual, &done); @@ -6026,30 +6057,9 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) { } } -static bool IsTypeCheckSlowPathFatal(TypeCheckKind type_check_kind, bool throws_into_catch) { - switch (type_check_kind) { - case TypeCheckKind::kExactCheck: - case TypeCheckKind::kAbstractClassCheck: - case TypeCheckKind::kClassHierarchyCheck: - case TypeCheckKind::kArrayObjectCheck: - return !throws_into_catch && !kEmitCompilerReadBarrier; - case TypeCheckKind::kInterfaceCheck: - return !throws_into_catch && !kEmitCompilerReadBarrier && !kPoisonHeapReferences; - case TypeCheckKind::kArrayCheck: - case TypeCheckKind::kUnresolvedCheck: - return false; - } - LOG(FATAL) << "Unreachable"; - UNREACHABLE(); -} - void LocationsBuilderX86_64::VisitCheckCast(HCheckCast* instruction) { - bool throws_into_catch = instruction->CanThrowIntoCatchBlock(); TypeCheckKind type_check_kind = instruction->GetTypeCheckKind(); - bool is_fatal_slow_path = IsTypeCheckSlowPathFatal(type_check_kind, throws_into_catch); - LocationSummary::CallKind call_kind = is_fatal_slow_path - ? LocationSummary::kNoCall - : LocationSummary::kCallOnSlowPath; + LocationSummary::CallKind call_kind = CodeGenerator::GetCheckCastCallKind(instruction); LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RequiresRegister()); @@ -6090,11 +6100,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { const uint32_t object_array_data_offset = mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value(); - // Always false for read barriers since we may need to go to the entrypoint for non-fatal cases - // from false negatives. The false negatives may come from avoiding read barriers below. Avoiding - // read barriers is done for performance and code size reasons. - bool is_type_check_slow_path_fatal = - IsTypeCheckSlowPathFatal(type_check_kind, instruction->CanThrowIntoCatchBlock()); + bool is_type_check_slow_path_fatal = CodeGenerator::IsTypeCheckSlowPathFatal(instruction); SlowPathCode* type_check_slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathX86_64( instruction, is_type_check_slow_path_fatal); @@ -6248,42 +6254,40 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) { } case TypeCheckKind::kInterfaceCheck: - // Fast path for the interface check. We always go slow path for heap poisoning since - // unpoisoning cls would require an extra temp. - if (!kPoisonHeapReferences) { - // Try to avoid read barriers to improve the fast path. We can not get false positives by - // doing this. - // /* HeapReference */ temp = obj->klass_ - GenerateReferenceLoadTwoRegisters(instruction, - temp_loc, - obj_loc, - class_offset, - kWithoutReadBarrier); - - // /* HeapReference */ temp = temp->iftable_ - GenerateReferenceLoadTwoRegisters(instruction, - temp_loc, - temp_loc, - iftable_offset, - kWithoutReadBarrier); - // Iftable is never null. - __ movl(maybe_temp2_loc.AsRegister(), Address(temp, array_length_offset)); - // Loop through the iftable and check if any class matches. - NearLabel start_loop; - __ Bind(&start_loop); - // Need to subtract first to handle the empty array case. - __ subl(maybe_temp2_loc.AsRegister(), Immediate(2)); - __ j(kNegative, type_check_slow_path->GetEntryLabel()); - // Go to next interface if the classes do not match. - __ cmpl(cls.AsRegister(), - CodeGeneratorX86_64::ArrayAddress(temp, - maybe_temp2_loc, - TIMES_4, - object_array_data_offset)); - __ j(kNotEqual, &start_loop); // Return if same class. - } else { - __ jmp(type_check_slow_path->GetEntryLabel()); - } + // Fast path for the interface check. Try to avoid read barriers to improve the fast path. + // We can not get false positives by doing this. + // /* HeapReference */ temp = obj->klass_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + obj_loc, + class_offset, + kWithoutReadBarrier); + + // /* HeapReference */ temp = temp->iftable_ + GenerateReferenceLoadTwoRegisters(instruction, + temp_loc, + temp_loc, + iftable_offset, + kWithoutReadBarrier); + // Iftable is never null. + __ movl(maybe_temp2_loc.AsRegister(), Address(temp, array_length_offset)); + // Maybe poison the `cls` for direct comparison with memory. + __ MaybePoisonHeapReference(cls.AsRegister()); + // Loop through the iftable and check if any class matches. + NearLabel start_loop; + __ Bind(&start_loop); + // Need to subtract first to handle the empty array case. + __ subl(maybe_temp2_loc.AsRegister(), Immediate(2)); + __ j(kNegative, type_check_slow_path->GetEntryLabel()); + // Go to next interface if the classes do not match. + __ cmpl(cls.AsRegister(), + CodeGeneratorX86_64::ArrayAddress(temp, + maybe_temp2_loc, + TIMES_4, + object_array_data_offset)); + __ j(kNotEqual, &start_loop); // Return if same class. + // If `cls` was poisoned above, unpoison it. + __ MaybeUnpoisonHeapReference(cls.AsRegister()); break; } diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 00c5c27470817877c23ae33db7ca42690c1e69d8..e86123ef01a2523dbc80c43b2c8015728afe2517 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -139,11 +139,12 @@ class ParallelMoveResolverX86_64 : public ParallelMoveResolverWithSwap { private: void Exchange32(CpuRegister reg, int mem); void Exchange32(XmmRegister reg, int mem); - void Exchange32(int mem1, int mem2); void Exchange64(CpuRegister reg1, CpuRegister reg2); void Exchange64(CpuRegister reg, int mem); void Exchange64(XmmRegister reg, int mem); - void Exchange64(int mem1, int mem2); + void Exchange128(XmmRegister reg, int mem); + void ExchangeMemory32(int mem1, int mem2); + void ExchangeMemory64(int mem1, int mem2, int num_of_qwords); CodeGeneratorX86_64* const codegen_; diff --git a/compiler/optimizing/code_sinking.h b/compiler/optimizing/code_sinking.h index 59cda52a8cbccc8733f4f75a40a24ce0515228ce..836d9d4f67c05f80ab8dbfcc8da76a2de2d439f7 100644 --- a/compiler/optimizing/code_sinking.h +++ b/compiler/optimizing/code_sinking.h @@ -28,8 +28,10 @@ namespace art { */ class CodeSinking : public HOptimization { public: - CodeSinking(HGraph* graph, OptimizingCompilerStats* stats) - : HOptimization(graph, kCodeSinkingPassName, stats) {} + CodeSinking(HGraph* graph, + OptimizingCompilerStats* stats, + const char* name = kCodeSinkingPassName) + : HOptimization(graph, name, stats) {} void Run() OVERRIDE; diff --git a/compiler/optimizing/constant_folding.cc b/compiler/optimizing/constant_folding.cc index bb586bf096e1e8740aa7c1d7e33853a16df735bb..6f11e628eef22ff6487ee6742322dfacc817e0e4 100644 --- a/compiler/optimizing/constant_folding.cc +++ b/compiler/optimizing/constant_folding.cc @@ -113,7 +113,7 @@ void HConstantFoldingVisitor::VisitBinaryOperation(HBinaryOperation* inst) { void HConstantFoldingVisitor::VisitTypeConversion(HTypeConversion* inst) { // Constant folding: replace `TypeConversion(a)' with a constant at // compile time if `a' is a constant. - HConstant* constant = inst->AsTypeConversion()->TryStaticEvaluation(); + HConstant* constant = inst->TryStaticEvaluation(); if (constant != nullptr) { inst->ReplaceWith(constant); inst->GetBlock()->RemoveInstruction(inst); diff --git a/compiler/optimizing/constructor_fence_redundancy_elimination.h b/compiler/optimizing/constructor_fence_redundancy_elimination.h index d89210cd1c83f8a7ab28754c66b7a5888e951a5d..f4b06d5544cd1be9ae83d5d70145fc67fddf35a3 100644 --- a/compiler/optimizing/constructor_fence_redundancy_elimination.h +++ b/compiler/optimizing/constructor_fence_redundancy_elimination.h @@ -48,12 +48,13 @@ namespace art { class ConstructorFenceRedundancyElimination : public HOptimization { public: ConstructorFenceRedundancyElimination(HGraph* graph, - OptimizingCompilerStats* stats) - : HOptimization(graph, kPassName, stats) {} + OptimizingCompilerStats* stats, + const char* name = kCFREPassName) + : HOptimization(graph, name, stats) {} void Run() OVERRIDE; - static constexpr const char* kPassName = "constructor_fence_redundancy_elimination"; + static constexpr const char* kCFREPassName = "constructor_fence_redundancy_elimination"; private: DISALLOW_COPY_AND_ASSIGN(ConstructorFenceRedundancyElimination); diff --git a/compiler/optimizing/data_type.h b/compiler/optimizing/data_type.h index 75a7fbe6ca6683f275000a45cada55d75a246af1..548fe28cee9c66cd6111c893aadaf61b0cde27ce 100644 --- a/compiler/optimizing/data_type.h +++ b/compiler/optimizing/data_type.h @@ -19,7 +19,8 @@ #include -#include "base/logging.h" +#include + #include "base/bit_utils.h" namespace art { @@ -186,6 +187,7 @@ class DataType { } static bool IsTypeConversionImplicit(Type input_type, Type result_type); + static bool IsTypeConversionImplicit(int64_t value, Type result_type); static const char* PrettyDescriptor(Type type); @@ -213,6 +215,18 @@ inline bool DataType::IsTypeConversionImplicit(Type input_type, Type result_type MaxValueOfIntegralType(input_type) <= MaxValueOfIntegralType(result_type)); } +inline bool DataType::IsTypeConversionImplicit(int64_t value, Type result_type) { + if (IsIntegralType(result_type) && result_type != Type::kInt64) { + // If the constant value falls in the range of the result_type, type + // conversion isn't needed. + return value >= MinValueOfIntegralType(result_type) && + value <= MaxValueOfIntegralType(result_type); + } + // Conversion isn't implicit if it's into non-integer types, or 64-bit int + // which may have different number of registers. + return false; +} + } // namespace art #endif // ART_COMPILER_OPTIMIZING_DATA_TYPE_H_ diff --git a/compiler/optimizing/escape.cc b/compiler/optimizing/escape.cc index 0a92703bd4098b07eb719f04885f2836c002d9cd..2b578c1cc88ecbc14b2caaeb5c6291b5507d0727 100644 --- a/compiler/optimizing/escape.cc +++ b/compiler/optimizing/escape.cc @@ -57,7 +57,9 @@ void CalculateEscape(HInstruction* reference, *is_singleton_and_not_returned = false; *is_singleton_and_not_deopt_visible = false; return; - } else if (user->IsPhi() || user->IsSelect() || user->IsInvoke() || + } else if (user->IsPhi() || + user->IsSelect() || + (user->IsInvoke() && user->GetSideEffects().DoesAnyWrite()) || (user->IsInstanceFieldSet() && (reference == user->InputAt(1))) || (user->IsUnresolvedInstanceFieldSet() && (reference == user->InputAt(1))) || (user->IsStaticFieldSet() && (reference == user->InputAt(1))) || diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc index 813772e9afb82ce57e9cdd601484ab733a6cc4af..71c394ec1f0de6e6715d1f1ca23c27ffee5afc93 100644 --- a/compiler/optimizing/gvn.cc +++ b/compiler/optimizing/gvn.cc @@ -301,8 +301,11 @@ class ValueSet : public ArenaObject { // Pure instructions are put into odd buckets to speed up deletion. Note that in the // case of irreducible loops, we don't put pure instructions in odd buckets, as we // need to delete them when entering the loop. - if (instruction->GetSideEffects().HasDependencies() || - instruction->GetBlock()->GetGraph()->HasIrreducibleLoops()) { + // ClinitCheck is treated as a pure instruction since it's only executed + // once. + bool pure = !instruction->GetSideEffects().HasDependencies() || + instruction->IsClinitCheck(); + if (!pure || instruction->GetBlock()->GetGraph()->HasIrreducibleLoops()) { return (hash_code << 1) | 0; } else { return (hash_code << 1) | 1; diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc index e2747afd85e3c009710b5f9c11cbbf476d2cf008..d270c6a28e23deb4626cecf63734de02514a9b88 100644 --- a/compiler/optimizing/induction_var_analysis.cc +++ b/compiler/optimizing/induction_var_analysis.cc @@ -93,12 +93,142 @@ static DataType::Type ImplicitConversion(DataType::Type type) { } } +/** + * Returns true if loop is guarded by "a cmp b" on entry. + */ +static bool IsGuardedBy(HLoopInformation* loop, + IfCondition cmp, + HInstruction* a, + HInstruction* b) { + // Chase back through straightline code to the first potential + // block that has a control dependence. + // guard: if (x) bypass + // | + // entry: straightline code + // | + // preheader + // | + // header + HBasicBlock* guard = loop->GetPreHeader(); + HBasicBlock* entry = loop->GetHeader(); + while (guard->GetPredecessors().size() == 1 && + guard->GetSuccessors().size() == 1) { + entry = guard; + guard = guard->GetSinglePredecessor(); + } + // Find guard. + HInstruction* control = guard->GetLastInstruction(); + if (!control->IsIf()) { + return false; + } + HIf* ifs = control->AsIf(); + HInstruction* if_expr = ifs->InputAt(0); + if (if_expr->IsCondition()) { + IfCondition other_cmp = ifs->IfTrueSuccessor() == entry + ? if_expr->AsCondition()->GetCondition() + : if_expr->AsCondition()->GetOppositeCondition(); + if (if_expr->InputAt(0) == a && if_expr->InputAt(1) == b) { + return cmp == other_cmp; + } else if (if_expr->InputAt(1) == a && if_expr->InputAt(0) == b) { + switch (cmp) { + case kCondLT: return other_cmp == kCondGT; + case kCondLE: return other_cmp == kCondGE; + case kCondGT: return other_cmp == kCondLT; + case kCondGE: return other_cmp == kCondLE; + default: LOG(FATAL) << "unexpected cmp: " << cmp; + } + } + } + return false; +} + +/* Finds first loop header phi use. */ +HInstruction* FindFirstLoopHeaderPhiUse(HLoopInformation* loop, HInstruction* instruction) { + for (const HUseListNode& use : instruction->GetUses()) { + if (use.GetUser()->GetBlock() == loop->GetHeader() && + use.GetUser()->IsPhi() && + use.GetUser()->InputAt(1) == instruction) { + return use.GetUser(); + } + } + return nullptr; +} + +/** + * Relinks the Phi structure after break-loop rewriting. + */ +bool FixOutsideUse(HLoopInformation* loop, + HInstruction* instruction, + HInstruction* replacement, + bool rewrite) { + // Deal with regular uses. + const HUseList& uses = instruction->GetUses(); + for (auto it = uses.begin(), end = uses.end(); it != end; ) { + HInstruction* user = it->GetUser(); + size_t index = it->GetIndex(); + ++it; // increment prior to potential removal + if (user->GetBlock()->GetLoopInformation() != loop) { + if (replacement == nullptr) { + return false; + } else if (rewrite) { + user->ReplaceInput(replacement, index); + } + } + } + // Deal with environment uses. + const HUseList& env_uses = instruction->GetEnvUses(); + for (auto it = env_uses.begin(), end = env_uses.end(); it != end;) { + HEnvironment* user = it->GetUser(); + size_t index = it->GetIndex(); + ++it; // increment prior to potential removal + if (user->GetHolder()->GetBlock()->GetLoopInformation() != loop) { + if (replacement == nullptr) { + return false; + } else if (rewrite) { + user->RemoveAsUserOfInput(index); + user->SetRawEnvAt(index, replacement); + replacement->AddEnvUseAt(user, index); + } + } + } + return true; +} + +/** + * Test and rewrite the loop body of a break-loop. Returns true on success. + */ +bool RewriteBreakLoopBody(HLoopInformation* loop, + HBasicBlock* body, + HInstruction* cond, + HInstruction* index, + HInstruction* upper, + bool rewrite) { + // Deal with Phis. Outside use prohibited, except for index (which gets exit value). + for (HInstructionIterator it(loop->GetHeader()->GetPhis()); !it.Done(); it.Advance()) { + HInstruction* exit_value = it.Current() == index ? upper : nullptr; + if (!FixOutsideUse(loop, it.Current(), exit_value, rewrite)) { + return false; + } + } + // Deal with other statements in header. + for (HInstruction* m = cond->GetPrevious(), *p = nullptr; m && !m->IsSuspendCheck(); m = p) { + p = m->GetPrevious(); + if (rewrite) { + m->MoveBefore(body->GetFirstInstruction(), false); + } + if (!FixOutsideUse(loop, m, FindFirstLoopHeaderPhiUse(loop, m), rewrite)) { + return false; + } + } + return true; +} + // // Class methods. // -HInductionVarAnalysis::HInductionVarAnalysis(HGraph* graph) - : HOptimization(graph, kInductionPassName), +HInductionVarAnalysis::HInductionVarAnalysis(HGraph* graph, const char* name) + : HOptimization(graph, name), global_depth_(0), stack_(graph->GetAllocator()->Adapter(kArenaAllocInductionVarAnalysis)), map_(std::less(), @@ -754,6 +884,10 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveConversion( return nullptr; } +// +// Loop trip count analysis methods. +// + void HInductionVarAnalysis::VisitControl(HLoopInformation* loop) { HInstruction* control = loop->GetHeader()->GetLastInstruction(); if (control->IsIf()) { @@ -774,15 +908,16 @@ void HInductionVarAnalysis::VisitControl(HLoopInformation* loop) { if (a == nullptr || b == nullptr) { return; // Loop control is not a sequence. } else if (if_true->GetLoopInformation() != loop && if_false->GetLoopInformation() == loop) { - VisitCondition(loop, a, b, type, condition->GetOppositeCondition()); + VisitCondition(loop, if_false, a, b, type, condition->GetOppositeCondition()); } else if (if_true->GetLoopInformation() == loop && if_false->GetLoopInformation() != loop) { - VisitCondition(loop, a, b, type, condition->GetCondition()); + VisitCondition(loop, if_true, a, b, type, condition->GetCondition()); } } } } void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop, + HBasicBlock* body, InductionInfo* a, InductionInfo* b, DataType::Type type, @@ -790,11 +925,11 @@ void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop, if (a->induction_class == kInvariant && b->induction_class == kLinear) { // Swap condition if induction is at right-hand-side (e.g. U > i is same as i < U). switch (cmp) { - case kCondLT: VisitCondition(loop, b, a, type, kCondGT); break; - case kCondLE: VisitCondition(loop, b, a, type, kCondGE); break; - case kCondGT: VisitCondition(loop, b, a, type, kCondLT); break; - case kCondGE: VisitCondition(loop, b, a, type, kCondLE); break; - case kCondNE: VisitCondition(loop, b, a, type, kCondNE); break; + case kCondLT: VisitCondition(loop, body, b, a, type, kCondGT); break; + case kCondLE: VisitCondition(loop, body, b, a, type, kCondGE); break; + case kCondGT: VisitCondition(loop, body, b, a, type, kCondLT); break; + case kCondGE: VisitCondition(loop, body, b, a, type, kCondLE); break; + case kCondNE: VisitCondition(loop, body, b, a, type, kCondNE); break; default: break; } } else if (a->induction_class == kLinear && b->induction_class == kInvariant) { @@ -802,24 +937,30 @@ void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop, InductionInfo* lower_expr = a->op_b; InductionInfo* upper_expr = b; InductionInfo* stride_expr = a->op_a; - // Constant stride? + // Test for constant stride and integral condition. int64_t stride_value = 0; if (!IsExact(stride_expr, &stride_value)) { - return; + return; // unknown stride + } else if (type != DataType::Type::kInt32 && type != DataType::Type::kInt64) { + return; // not integral } - // Rewrite condition i != U into strict end condition i < U or i > U if this end condition - // is reached exactly (tested by verifying if the loop has a unit stride and the non-strict - // condition would be always taken). + // Since loops with a i != U condition will not be normalized by the method below, first + // try to rewrite a break-loop with terminating condition i != U into an equivalent loop + // with non-strict end condition i <= U or i >= U if such a rewriting is possible and safe. + if (cmp == kCondNE && RewriteBreakLoop(loop, body, stride_value, type)) { + cmp = stride_value > 0 ? kCondLE : kCondGE; + } + // If this rewriting failed, try to rewrite condition i != U into strict end condition i < U + // or i > U if this end condition is reached exactly (tested by verifying if the loop has a + // unit stride and the non-strict condition would be always taken). if (cmp == kCondNE && ((stride_value == +1 && IsTaken(lower_expr, upper_expr, kCondLE)) || (stride_value == -1 && IsTaken(lower_expr, upper_expr, kCondGE)))) { cmp = stride_value > 0 ? kCondLT : kCondGT; } - // Only accept integral condition. A mismatch between the type of condition and the induction - // is only allowed if the, necessarily narrower, induction range fits the narrower control. - if (type != DataType::Type::kInt32 && type != DataType::Type::kInt64) { - return; // not integral - } else if (type != a->type && - !FitsNarrowerControl(lower_expr, upper_expr, stride_value, a->type, cmp)) { + // A mismatch between the type of condition and the induction is only allowed if the, + // necessarily narrower, induction range fits the narrower control. + if (type != a->type && + !FitsNarrowerControl(lower_expr, upper_expr, stride_value, a->type, cmp)) { return; // mismatched type } // Normalize a linear loop control with a nonzero stride: @@ -984,6 +1125,69 @@ bool HInductionVarAnalysis::FitsNarrowerControl(InductionInfo* lower_expr, IsAtMost(upper_expr, &value) && value <= max; } +bool HInductionVarAnalysis::RewriteBreakLoop(HLoopInformation* loop, + HBasicBlock* body, + int64_t stride_value, + DataType::Type type) { + // Only accept unit stride. + if (std::abs(stride_value) != 1) { + return false; + } + // Simple terminating i != U condition, used nowhere else. + HIf* ifs = loop->GetHeader()->GetLastInstruction()->AsIf(); + HInstruction* cond = ifs->InputAt(0); + if (ifs->GetPrevious() != cond || !cond->HasOnlyOneNonEnvironmentUse()) { + return false; + } + int c = LookupInfo(loop, cond->InputAt(0))->induction_class == kLinear ? 0 : 1; + HInstruction* index = cond->InputAt(c); + HInstruction* upper = cond->InputAt(1 - c); + // Safe to rewrite into i <= U? + IfCondition cmp = stride_value > 0 ? kCondLE : kCondGE; + if (!index->IsPhi() || !IsFinite(LookupInfo(loop, upper), stride_value, type, cmp)) { + return false; + } + // Body consists of update to index i only, used nowhere else. + if (body->GetSuccessors().size() != 1 || + body->GetSingleSuccessor() != loop->GetHeader() || + !body->GetPhis().IsEmpty() || + body->GetInstructions().IsEmpty() || + body->GetFirstInstruction() != index->InputAt(1) || + !body->GetFirstInstruction()->HasOnlyOneNonEnvironmentUse() || + !body->GetFirstInstruction()->GetNext()->IsGoto()) { + return false; + } + // Always taken or guarded by enclosing condition. + if (!IsTaken(LookupInfo(loop, index)->op_b, LookupInfo(loop, upper), cmp) && + !IsGuardedBy(loop, cmp, index->InputAt(0), upper)) { + return false; + } + // Test if break-loop body can be written, and do so on success. + if (RewriteBreakLoopBody(loop, body, cond, index, upper, /*rewrite*/ false)) { + RewriteBreakLoopBody(loop, body, cond, index, upper, /*rewrite*/ true); + } else { + return false; + } + // Rewrite condition in HIR. + if (ifs->IfTrueSuccessor() != body) { + cmp = (cmp == kCondLE) ? kCondGT : kCondLT; + } + HInstruction* rep = nullptr; + switch (cmp) { + case kCondLT: rep = new (graph_->GetAllocator()) HLessThan(index, upper); break; + case kCondGT: rep = new (graph_->GetAllocator()) HGreaterThan(index, upper); break; + case kCondLE: rep = new (graph_->GetAllocator()) HLessThanOrEqual(index, upper); break; + case kCondGE: rep = new (graph_->GetAllocator()) HGreaterThanOrEqual(index, upper); break; + default: LOG(FATAL) << cmp; UNREACHABLE(); + } + loop->GetHeader()->ReplaceAndRemoveInstructionWith(cond, rep); + return true; +} + +// +// Helper methods. +// + void HInductionVarAnalysis::AssignInfo(HLoopInformation* loop, HInstruction* instruction, InductionInfo* info) { diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h index a2d302ae81c5c18576dbbf9ac8e014067d7a6cb2..acad77d35fb5f52e9c7aeb9ea8b8a927215bc5cb 100644 --- a/compiler/optimizing/induction_var_analysis.h +++ b/compiler/optimizing/induction_var_analysis.h @@ -35,7 +35,7 @@ namespace art { */ class HInductionVarAnalysis : public HOptimization { public: - explicit HInductionVarAnalysis(HGraph* graph); + explicit HInductionVarAnalysis(HGraph* graph, const char* name = kInductionPassName); void Run() OVERRIDE; @@ -195,9 +195,14 @@ class HInductionVarAnalysis : public HOptimization { HInstruction* entry_phi, HTypeConversion* conversion); + // + // Loop trip count analysis methods. + // + // Trip count information. void VisitControl(HLoopInformation* loop); void VisitCondition(HLoopInformation* loop, + HBasicBlock* body, InductionInfo* a, InductionInfo* b, DataType::Type type, @@ -219,6 +224,14 @@ class HInductionVarAnalysis : public HOptimization { int64_t stride_value, DataType::Type type, IfCondition cmp); + bool RewriteBreakLoop(HLoopInformation* loop, + HBasicBlock* body, + int64_t stride_value, + DataType::Type type); + + // + // Helper methods. + // // Assign and lookup. void AssignInfo(HLoopInformation* loop, HInstruction* instruction, InductionInfo* info); diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc index f87b46d4ee1ce0143239e856a8f6b938c42be411..4c11ad46435de98df1fb3e7ac47f5f473f8e0d27 100644 --- a/compiler/optimizing/induction_var_analysis_test.cc +++ b/compiler/optimizing/induction_var_analysis_test.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include // NOLINT [build/c++11] [5] +#include #include "base/arena_allocator.h" #include "builder.h" diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 4d846fa4ed9209efabb66d9bfcadad88a6bea067..7a66d807cfb391594fff948f03029034acd2ce49 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -294,7 +294,7 @@ static dex::TypeIndex FindClassIndexIn(mirror::Class* cls, // as there may be different class loaders. So only return the index if it's // the right class already resolved with the class loader. if (index.IsValid()) { - ObjPtr resolved = ClassLinker::LookupResolvedType( + ObjPtr resolved = compilation_unit.GetClassLinker()->LookupResolvedType( index, compilation_unit.GetDexCache().Get(), compilation_unit.GetClassLoader().Get()); if (resolved != cls) { index = dex::TypeIndex::Invalid(); @@ -441,9 +441,9 @@ bool HInliner::TryInline(HInvoke* invoke_instruction) { // Add dependency due to devirtulization. We've assumed resolved_method // has single implementation. outermost_graph_->AddCHASingleImplementationDependency(resolved_method); - MaybeRecordStat(stats_, kCHAInline); + MaybeRecordStat(stats_, MethodCompilationStat::kCHAInline); } else { - MaybeRecordStat(stats_, kInlinedInvokeVirtualOrInterface); + MaybeRecordStat(stats_, MethodCompilationStat::kInlinedInvokeVirtualOrInterface); } } return result; @@ -533,7 +533,7 @@ bool HInliner::TryInlineFromInlineCache(const DexFile& caller_dex_file, } case kInlineCacheMonomorphic: { - MaybeRecordStat(stats_, kMonomorphicCall); + MaybeRecordStat(stats_, MethodCompilationStat::kMonomorphicCall); if (UseOnlyPolymorphicInliningWithNoDeopt()) { return TryInlinePolymorphicCall(invoke_instruction, resolved_method, inline_cache); } else { @@ -542,7 +542,7 @@ bool HInliner::TryInlineFromInlineCache(const DexFile& caller_dex_file, } case kInlineCachePolymorphic: { - MaybeRecordStat(stats_, kPolymorphicCall); + MaybeRecordStat(stats_, MethodCompilationStat::kPolymorphicCall); return TryInlinePolymorphicCall(invoke_instruction, resolved_method, inline_cache); } @@ -551,7 +551,7 @@ bool HInliner::TryInlineFromInlineCache(const DexFile& caller_dex_file, << "Interface or virtual call to " << caller_dex_file.PrettyMethod(invoke_instruction->GetDexMethodIndex()) << " is megamorphic and not inlined"; - MaybeRecordStat(stats_, kMegamorphicCall); + MaybeRecordStat(stats_, MethodCompilationStat::kMegamorphicCall); return false; } @@ -682,7 +682,7 @@ HInliner::InlineCacheType HInliner::ExtractClassesFromOfflineProfile( << "is invalid in location" << dex_cache->GetDexFile()->GetLocation(); return kInlineCacheNoData; } - ObjPtr clazz = ClassLinker::LookupResolvedType( + ObjPtr clazz = caller_compilation_unit_.GetClassLinker()->LookupResolvedType( class_ref.type_index, dex_cache, caller_compilation_unit_.GetClassLoader().Get()); @@ -755,7 +755,7 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction, dex::TypeIndex class_index = FindClassIndexIn( GetMonomorphicType(classes), caller_compilation_unit_); if (!class_index.IsValid()) { - LOG_FAIL(stats_, kNotInlinedDexCache) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedDexCache) << "Call to " << ArtMethod::PrettyMethod(resolved_method) << " from inline cache is not inlined because its class is not" << " accessible to the caller"; @@ -804,7 +804,7 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction, /* is_first_run */ false); rtp_fixup.Run(); - MaybeRecordStat(stats_, kInlinedMonomorphicCall); + MaybeRecordStat(stats_, MethodCompilationStat::kInlinedMonomorphicCall); return true; } @@ -876,9 +876,9 @@ HInstruction* HInliner::AddTypeGuard(HInstruction* receiver, load_class, codegen_, compiler_driver_, caller_compilation_unit_); DCHECK(kind != HLoadClass::LoadKind::kInvalid) << "We should always be able to reference a class for inline caches"; - // Insert before setting the kind, as setting the kind affects the inputs. - bb_cursor->InsertInstructionAfter(load_class, receiver_class); + // Load kind must be set before inserting the instruction into the graph. load_class->SetLoadKind(kind); + bb_cursor->InsertInstructionAfter(load_class, receiver_class); // In AOT mode, we will most likely load the class from BSS, which will involve a call // to the runtime. In this case, the load instruction will need an environment so copy // it from the invoke instruction. @@ -994,7 +994,7 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction, return false; } - MaybeRecordStat(stats_, kInlinedPolymorphicCall); + MaybeRecordStat(stats_, MethodCompilationStat::kInlinedPolymorphicCall); // Run type propagation to get the guards typed. ReferenceTypePropagation rtp_fixup(graph_, @@ -1200,7 +1200,7 @@ bool HInliner::TryInlinePolymorphicCallToSameTarget( /* is_first_run */ false); rtp_fixup.Run(); - MaybeRecordStat(stats_, kInlinedPolymorphicCall); + MaybeRecordStat(stats_, MethodCompilationStat::kInlinedPolymorphicCall); LOG_SUCCESS() << "Inlined same polymorphic target " << actual_method->PrettyMethod(); return true; @@ -1211,11 +1211,49 @@ bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction, ReferenceTypeInfo receiver_type, bool do_rtp, bool cha_devirtualize) { + DCHECK(!invoke_instruction->IsIntrinsic()); HInstruction* return_replacement = nullptr; uint32_t dex_pc = invoke_instruction->GetDexPc(); HInstruction* cursor = invoke_instruction->GetPrevious(); HBasicBlock* bb_cursor = invoke_instruction->GetBlock(); - if (!TryBuildAndInline(invoke_instruction, method, receiver_type, &return_replacement)) { + bool should_remove_invoke_instruction = false; + + // If invoke_instruction is devirtualized to a different method, give intrinsics + // another chance before we try to inline it. + bool wrong_invoke_type = false; + if (invoke_instruction->GetResolvedMethod() != method && + IntrinsicsRecognizer::Recognize(invoke_instruction, method, &wrong_invoke_type)) { + MaybeRecordStat(stats_, MethodCompilationStat::kIntrinsicRecognized); + if (invoke_instruction->IsInvokeInterface()) { + // We don't intrinsify an invoke-interface directly. + // Replace the invoke-interface with an invoke-virtual. + HInvokeVirtual* new_invoke = new (graph_->GetAllocator()) HInvokeVirtual( + graph_->GetAllocator(), + invoke_instruction->GetNumberOfArguments(), + invoke_instruction->GetType(), + invoke_instruction->GetDexPc(), + invoke_instruction->GetDexMethodIndex(), // Use interface method's dex method index. + method, + method->GetMethodIndex()); + HInputsRef inputs = invoke_instruction->GetInputs(); + for (size_t index = 0; index != inputs.size(); ++index) { + new_invoke->SetArgumentAt(index, inputs[index]); + } + invoke_instruction->GetBlock()->InsertInstructionBefore(new_invoke, invoke_instruction); + new_invoke->CopyEnvironmentFrom(invoke_instruction->GetEnvironment()); + if (invoke_instruction->GetType() == DataType::Type::kReference) { + new_invoke->SetReferenceTypeInfo(invoke_instruction->GetReferenceTypeInfo()); + } + // Run intrinsic recognizer again to set new_invoke's intrinsic. + IntrinsicsRecognizer::Recognize(new_invoke, method, &wrong_invoke_type); + DCHECK_NE(new_invoke->GetIntrinsic(), Intrinsics::kNone); + return_replacement = new_invoke; + // invoke_instruction is replaced with new_invoke. + should_remove_invoke_instruction = true; + } else { + // invoke_instruction is intrinsified and stays. + } + } else if (!TryBuildAndInline(invoke_instruction, method, receiver_type, &return_replacement)) { if (invoke_instruction->IsInvokeInterface()) { DCHECK(!method->IsProxyMethod()); // Turn an invoke-interface into an invoke-virtual. An invoke-virtual is always @@ -1258,19 +1296,27 @@ bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction, new_invoke->SetReferenceTypeInfo(invoke_instruction->GetReferenceTypeInfo()); } return_replacement = new_invoke; + // invoke_instruction is replaced with new_invoke. + should_remove_invoke_instruction = true; } else { // TODO: Consider sharpening an invoke virtual once it is not dependent on the // compiler driver. return false; } + } else { + // invoke_instruction is inlined. + should_remove_invoke_instruction = true; } + if (cha_devirtualize) { AddCHAGuard(invoke_instruction, dex_pc, cursor, bb_cursor); } if (return_replacement != nullptr) { invoke_instruction->ReplaceWith(return_replacement); } - invoke_instruction->GetBlock()->RemoveInstruction(invoke_instruction); + if (should_remove_invoke_instruction) { + invoke_instruction->GetBlock()->RemoveInstruction(invoke_instruction); + } FixUpReturnReferenceType(method, return_replacement); if (do_rtp && ReturnTypeMoreSpecific(invoke_instruction, return_replacement)) { // Actual return value has a more specific type than the method's declared @@ -1301,14 +1347,14 @@ bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction, ReferenceTypeInfo receiver_type, HInstruction** return_replacement) { if (method->IsProxyMethod()) { - LOG_FAIL(stats_, kNotInlinedProxy) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedProxy) << "Method " << method->PrettyMethod() << " is not inlined because of unimplemented inline support for proxy methods."; return false; } if (CountRecursiveCallsOf(method) > kMaximumNumberOfRecursiveCalls) { - LOG_FAIL(stats_, kNotInlinedRecursiveBudget) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedRecursiveBudget) << "Method " << method->PrettyMethod() << " is not inlined because it has reached its recursive call budget."; @@ -1322,10 +1368,10 @@ bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction, if (TryPatternSubstitution(invoke_instruction, method, return_replacement)) { LOG_SUCCESS() << "Successfully replaced pattern of invoke " << method->PrettyMethod(); - MaybeRecordStat(stats_, kReplacedInvokeWithSimplePattern); + MaybeRecordStat(stats_, MethodCompilationStat::kReplacedInvokeWithSimplePattern); return true; } - LOG_FAIL(stats_, kNotInlinedWont) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedWont) << "Won't inline " << method->PrettyMethod() << " in " << outer_compilation_unit_.GetDexFile()->GetLocation() << " (" << caller_compilation_unit_.GetDexFile()->GetLocation() << ") from " @@ -1335,33 +1381,33 @@ bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction, bool same_dex_file = IsSameDexFile(*outer_compilation_unit_.GetDexFile(), *method->GetDexFile()); - const DexFile::CodeItem* code_item = method->GetCodeItem(); + CodeItemDataAccessor accessor(method); - if (code_item == nullptr) { + if (!accessor.HasCodeItem()) { LOG_FAIL_NO_STAT() << "Method " << method->PrettyMethod() << " is not inlined because it is native"; return false; } size_t inline_max_code_units = compiler_driver_->GetCompilerOptions().GetInlineMaxCodeUnits(); - if (code_item->insns_size_in_code_units_ > inline_max_code_units) { - LOG_FAIL(stats_, kNotInlinedCodeItem) + if (accessor.InsnsSizeInCodeUnits() > inline_max_code_units) { + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedCodeItem) << "Method " << method->PrettyMethod() << " is not inlined because its code item is too big: " - << code_item->insns_size_in_code_units_ + << accessor.InsnsSizeInCodeUnits() << " > " << inline_max_code_units; return false; } - if (code_item->tries_size_ != 0) { - LOG_FAIL(stats_, kNotInlinedTryCatch) + if (accessor.TriesSize() != 0) { + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedTryCatch) << "Method " << method->PrettyMethod() << " is not inlined because of try block"; return false; } if (!method->IsCompilable()) { - LOG_FAIL(stats_, kNotInlinedNotVerified) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedNotVerified) << "Method " << method->PrettyMethod() << " has soft failures un-handled by the compiler, so it cannot be inlined"; } @@ -1371,7 +1417,7 @@ bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction, if (Runtime::Current()->UseJitCompilation() || !compiler_driver_->IsMethodVerifiedWithoutFailures( method->GetDexMethodIndex(), class_def_idx, *method->GetDexFile())) { - LOG_FAIL(stats_, kNotInlinedNotVerified) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedNotVerified) << "Method " << method->PrettyMethod() << " couldn't be verified, so it cannot be inlined"; return false; @@ -1382,9 +1428,10 @@ bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction, invoke_instruction->AsInvokeStaticOrDirect()->IsStaticWithImplicitClinitCheck()) { // Case of a static method that cannot be inlined because it implicitly // requires an initialization check of its declaring class. - LOG_FAIL(stats_, kNotInlinedDexCache) << "Method " << method->PrettyMethod() - << " is not inlined because it is static and requires a clinit" - << " check that cannot be emitted due to Dex cache limitations"; + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedDexCache) + << "Method " << method->PrettyMethod() + << " is not inlined because it is static and requires a clinit" + << " check that cannot be emitted due to Dex cache limitations"; return false; } @@ -1394,7 +1441,7 @@ bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction, } LOG_SUCCESS() << method->PrettyMethod(); - MaybeRecordStat(stats_, kInlinedInvoke); + MaybeRecordStat(stats_, MethodCompilationStat::kInlinedInvoke); return true; } @@ -1613,6 +1660,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, const DexFile::CodeItem* code_item = resolved_method->GetCodeItem(); const DexFile& callee_dex_file = *resolved_method->GetDexFile(); uint32_t method_index = resolved_method->GetDexMethodIndex(); + CodeItemDebugInfoAccessor code_item_accessor(&callee_dex_file, code_item); ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker(); Handle dex_cache = NewHandleIfDifferent(resolved_method->GetDexCache(), caller_compilation_unit_.GetDexCache(), @@ -1667,16 +1715,17 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, } } HGraphBuilder builder(callee_graph, + code_item_accessor, &dex_compilation_unit, &outer_compilation_unit_, compiler_driver_, codegen_, inline_stats_, - resolved_method->GetQuickenedInfo(class_linker->GetImagePointerSize()), + resolved_method->GetQuickenedInfo(), handles_); if (builder.BuildGraph() != kAnalysisSuccess) { - LOG_FAIL(stats_, kNotInlinedCannotBuild) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedCannotBuild) << "Method " << callee_dex_file.PrettyMethod(method_index) << " could not be built, so cannot be inlined"; return false; @@ -1684,7 +1733,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, if (!RegisterAllocator::CanAllocateRegistersFor(*callee_graph, compiler_driver_->GetInstructionSet())) { - LOG_FAIL(stats_, kNotInlinedRegisterAllocator) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedRegisterAllocator) << "Method " << callee_dex_file.PrettyMethod(method_index) << " cannot be inlined because of the register allocator"; return false; @@ -1737,7 +1786,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, HBasicBlock* exit_block = callee_graph->GetExitBlock(); if (exit_block == nullptr) { - LOG_FAIL(stats_, kNotInlinedInfiniteLoop) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedInfiniteLoop) << "Method " << callee_dex_file.PrettyMethod(method_index) << " could not be inlined because it has an infinite loop"; return false; @@ -1748,14 +1797,14 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, if (predecessor->GetLastInstruction()->IsThrow()) { if (invoke_instruction->GetBlock()->IsTryBlock()) { // TODO(ngeoffray): Support adding HTryBoundary in Hgraph::InlineInto. - LOG_FAIL(stats_, kNotInlinedTryCatch) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedTryCatch) << "Method " << callee_dex_file.PrettyMethod(method_index) << " could not be inlined because one branch always throws and" << " caller is in a try/catch block"; return false; } else if (graph_->GetExitBlock() == nullptr) { // TODO(ngeoffray): Support adding HExit in the caller graph. - LOG_FAIL(stats_, kNotInlinedInfiniteLoop) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedInfiniteLoop) << "Method " << callee_dex_file.PrettyMethod(method_index) << " could not be inlined because one branch always throws and" << " caller does not have an exit block"; @@ -1774,7 +1823,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, } if (!has_one_return) { - LOG_FAIL(stats_, kNotInlinedAlwaysThrows) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedAlwaysThrows) << "Method " << callee_dex_file.PrettyMethod(method_index) << " could not be inlined because it always throws"; return false; @@ -1787,7 +1836,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, if (block->GetLoopInformation()->IsIrreducible()) { // Don't inline methods with irreducible loops, they could prevent some // optimizations to run. - LOG_FAIL(stats_, kNotInlinedIrreducibleLoop) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedIrreducibleLoop) << "Method " << callee_dex_file.PrettyMethod(method_index) << " could not be inlined because it contains an irreducible loop"; return false; @@ -1796,7 +1845,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, // Don't inline methods with loops without exit, since they cause the // loop information to be computed incorrectly when updating after // inlining. - LOG_FAIL(stats_, kNotInlinedLoopWithoutExit) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedLoopWithoutExit) << "Method " << callee_dex_file.PrettyMethod(method_index) << " could not be inlined because it contains a loop with no exit"; return false; @@ -1807,7 +1856,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, !instr_it.Done(); instr_it.Advance()) { if (++number_of_instructions >= inlining_budget_) { - LOG_FAIL(stats_, kNotInlinedInstructionBudget) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedInstructionBudget) << "Method " << callee_dex_file.PrettyMethod(method_index) << " is not inlined because the outer method has reached" << " its instruction budget limit."; @@ -1816,7 +1865,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, HInstruction* current = instr_it.Current(); if (current->NeedsEnvironment() && (total_number_of_dex_registers_ >= kMaximumNumberOfCumulatedDexRegisters)) { - LOG_FAIL(stats_, kNotInlinedEnvironmentBudget) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedEnvironmentBudget) << "Method " << callee_dex_file.PrettyMethod(method_index) << " is not inlined because its caller has reached" << " its environment budget limit."; @@ -1826,7 +1875,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, if (current->NeedsEnvironment() && !CanEncodeInlinedMethodInStackMap(*caller_compilation_unit_.GetDexFile(), resolved_method)) { - LOG_FAIL(stats_, kNotInlinedStackMaps) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedStackMaps) << "Method " << callee_dex_file.PrettyMethod(method_index) << " could not be inlined because " << current->DebugName() << " needs an environment, is in a different dex file" @@ -1835,7 +1884,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, } if (!same_dex_file && current->NeedsDexCacheOfDeclaringClass()) { - LOG_FAIL(stats_, kNotInlinedDexCache) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedDexCache) << "Method " << callee_dex_file.PrettyMethod(method_index) << " could not be inlined because " << current->DebugName() << " it is in a different dex file and requires access to the dex cache"; @@ -1847,7 +1896,7 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, current->IsUnresolvedStaticFieldSet() || current->IsUnresolvedInstanceFieldSet()) { // Entrypoint for unresolved fields does not handle inlined frames. - LOG_FAIL(stats_, kNotInlinedUnresolvedEntrypoint) + LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedUnresolvedEntrypoint) << "Method " << callee_dex_file.PrettyMethod(method_index) << " could not be inlined because it is using an unresolved" << " entrypoint"; @@ -1884,7 +1933,7 @@ void HInliner::RunOptimizations(HGraph* callee_graph, // optimization that could lead to a HDeoptimize. The following optimizations do not. HDeadCodeElimination dce(callee_graph, inline_stats_, "dead_code_elimination$inliner"); HConstantFolding fold(callee_graph, "constant_folding$inliner"); - HSharpening sharpening(callee_graph, codegen_, dex_compilation_unit, compiler_driver_, handles_); + HSharpening sharpening(callee_graph, codegen_, compiler_driver_); InstructionSimplifier simplify(callee_graph, codegen_, compiler_driver_, inline_stats_); IntrinsicsRecognizer intrinsics(callee_graph, inline_stats_); @@ -1919,6 +1968,7 @@ void HInliner::RunOptimizations(HGraph* callee_graph, return; } + CodeItemDataAccessor accessor(&callee_graph->GetDexFile(), code_item); HInliner inliner(callee_graph, outermost_graph_, codegen_, @@ -1927,7 +1977,7 @@ void HInliner::RunOptimizations(HGraph* callee_graph, compiler_driver_, handles_, inline_stats_, - total_number_of_dex_registers_ + code_item->registers_size_, + total_number_of_dex_registers_ + accessor.RegistersSize(), total_number_of_instructions_ + number_of_instructions, this, depth_ + 1); diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h index c4b3a32d91084205f5c09981650d3226151d1e90..042eee3204fad70aa573cd12acafa185f9913b7a 100644 --- a/compiler/optimizing/inliner.h +++ b/compiler/optimizing/inliner.h @@ -44,8 +44,9 @@ class HInliner : public HOptimization { size_t total_number_of_dex_registers, size_t total_number_of_instructions, HInliner* parent, - size_t depth = 0) - : HOptimization(outer_graph, kInlinerPassName, stats), + size_t depth = 0, + const char* name = kInlinerPassName) + : HOptimization(outer_graph, name, stats), outermost_graph_(outermost_graph), outer_compilation_unit_(outer_compilation_unit), caller_compilation_unit_(caller_compilation_unit), diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 8e9b8187226b0128b81a807b75398f09741141e3..3f5923fa3f9d50412a210b5a6200f7e778d8c2ce 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -29,6 +29,7 @@ #include "driver/compiler_options.h" #include "imtable-inl.h" #include "mirror/dex_cache.h" +#include "oat_file.h" #include "optimizing_compiler_stats.h" #include "quicken_info.h" #include "scoped_thread_state_change-inl.h" @@ -38,6 +39,44 @@ namespace art { +HInstructionBuilder::HInstructionBuilder(HGraph* graph, + HBasicBlockBuilder* block_builder, + SsaBuilder* ssa_builder, + const DexFile* dex_file, + const CodeItemDebugInfoAccessor& accessor, + DataType::Type return_type, + const DexCompilationUnit* dex_compilation_unit, + const DexCompilationUnit* outer_compilation_unit, + CompilerDriver* compiler_driver, + CodeGenerator* code_generator, + const uint8_t* interpreter_metadata, + OptimizingCompilerStats* compiler_stats, + VariableSizedHandleScope* handles, + ScopedArenaAllocator* local_allocator) + : allocator_(graph->GetAllocator()), + graph_(graph), + handles_(handles), + dex_file_(dex_file), + code_item_accessor_(accessor), + return_type_(return_type), + block_builder_(block_builder), + ssa_builder_(ssa_builder), + compiler_driver_(compiler_driver), + code_generator_(code_generator), + dex_compilation_unit_(dex_compilation_unit), + outer_compilation_unit_(outer_compilation_unit), + quicken_info_(interpreter_metadata), + compilation_stats_(compiler_stats), + local_allocator_(local_allocator), + locals_for_(local_allocator->Adapter(kArenaAllocGraphBuilder)), + current_block_(nullptr), + current_locals_(nullptr), + latest_result_(nullptr), + current_this_parameter_(nullptr), + loop_headers_(local_allocator->Adapter(kArenaAllocGraphBuilder)) { + loop_headers_.reserve(kDefaultNumberOfLoops); +} + HBasicBlock* HInstructionBuilder::FindBlockStartingAt(uint32_t dex_pc) const { return block_builder_->GetBlockAt(dex_pc); } @@ -272,6 +311,7 @@ static bool IsBlockPopulated(HBasicBlock* block) { } bool HInstructionBuilder::Build() { + DCHECK(code_item_accessor_.HasCodeItem()); locals_for_.resize( graph_->GetBlocks().size(), ScopedArenaVector(local_allocator_->Adapter(kArenaAllocGraphBuilder))); @@ -321,7 +361,7 @@ bool HInstructionBuilder::Build() { quicken_index = block_builder_->GetQuickenIndex(block_dex_pc); } - for (const DexInstructionPcPair& pair : code_item_.Instructions(block_dex_pc)) { + for (const DexInstructionPcPair& pair : code_item_accessor_.InstructionsFrom(block_dex_pc)) { if (current_block_ == nullptr) { // The previous instruction ended this block. break; @@ -364,6 +404,73 @@ bool HInstructionBuilder::Build() { return true; } +void HInstructionBuilder::BuildIntrinsic(ArtMethod* method) { + DCHECK(!code_item_accessor_.HasCodeItem()); + DCHECK(method->IsIntrinsic()); + + locals_for_.resize( + graph_->GetBlocks().size(), + ScopedArenaVector(local_allocator_->Adapter(kArenaAllocGraphBuilder))); + + // Fill the entry block. Do not add suspend check, we do not want a suspend + // check in intrinsics; intrinsic methods are supposed to be fast. + current_block_ = graph_->GetEntryBlock(); + InitializeBlockLocals(); + InitializeParameters(); + AppendInstruction(new (allocator_) HGoto(0u)); + + // Fill the body. + current_block_ = current_block_->GetSingleSuccessor(); + InitializeBlockLocals(); + DCHECK(!IsBlockPopulated(current_block_)); + + // Add the invoke and return instruction. Use HInvokeStaticOrDirect even + // for methods that would normally use an HInvokeVirtual (sharpen the call). + size_t in_vregs = graph_->GetNumberOfInVRegs(); + size_t number_of_arguments = + in_vregs - std::count(current_locals_->end() - in_vregs, current_locals_->end(), nullptr); + uint32_t method_idx = dex_compilation_unit_->GetDexMethodIndex(); + MethodReference target_method(dex_file_, method_idx); + HInvokeStaticOrDirect::DispatchInfo dispatch_info = { + HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall, + HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod, + /* method_load_data */ 0u + }; + InvokeType invoke_type = dex_compilation_unit_->IsStatic() ? kStatic : kDirect; + HInvokeStaticOrDirect* invoke = new (allocator_) HInvokeStaticOrDirect( + allocator_, + number_of_arguments, + return_type_, + kNoDexPc, + method_idx, + method, + dispatch_info, + invoke_type, + target_method, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + HandleInvoke(invoke, + in_vregs, + /* args */ nullptr, + graph_->GetNumberOfVRegs() - in_vregs, + /* is_range */ true, + dex_file_->GetMethodShorty(method_idx), + /* clinit_check */ nullptr, + /* is_unresolved */ false); + + // Add the return instruction. + if (return_type_ == DataType::Type::kVoid) { + AppendInstruction(new (allocator_) HReturnVoid()); + } else { + AppendInstruction(new (allocator_) HReturn(invoke)); + } + + // Fill the exit block. + DCHECK_EQ(current_block_->GetSingleSuccessor(), graph_->GetExitBlock()); + current_block_ = graph_->GetExitBlock(); + InitializeBlockLocals(); + AppendInstruction(new (allocator_) HExit()); +} + ArenaBitVector* HInstructionBuilder::FindNativeDebugInfoLocations() { // The callback gets called when the line number changes. // In other words, it marks the start of new java statement. @@ -373,16 +480,16 @@ ArenaBitVector* HInstructionBuilder::FindNativeDebugInfoLocations() { return false; } }; - const uint32_t num_instructions = code_item_.insns_size_in_code_units_; ArenaBitVector* locations = ArenaBitVector::Create(local_allocator_, - num_instructions, + code_item_accessor_.InsnsSizeInCodeUnits(), /* expandable */ false, kArenaAllocGraphBuilder); locations->ClearAllBits(); - dex_file_->DecodeDebugPositionInfo(&code_item_, Callback::Position, locations); + dex_file_->DecodeDebugPositionInfo(code_item_accessor_.DebugInfoOffset(), + Callback::Position, + locations); // Instruction-specific tweaks. - IterationRange instructions = code_item_.Instructions(); - for (const DexInstructionPcPair& inst : instructions) { + for (const DexInstructionPcPair& inst : code_item_accessor_) { switch (inst->Opcode()) { case Instruction::MOVE_EXCEPTION: { // Stop in native debugger after the exception has been moved. @@ -391,7 +498,7 @@ ArenaBitVector* HInstructionBuilder::FindNativeDebugInfoLocations() { locations->ClearBit(inst.DexPc()); DexInstructionIterator next = std::next(DexInstructionIterator(inst)); DCHECK(next.DexPc() != inst.DexPc()); - if (next != instructions.end()) { + if (next != code_item_accessor_.end()) { locations->SetBit(next.DexPc()); } break; @@ -726,7 +833,6 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in ArtMethod* resolved_method = class_linker->ResolveMethod( - *dex_compilation_unit_->GetDexFile(), method_idx, dex_compilation_unit_->GetDexCache(), class_loader, @@ -761,7 +867,6 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in return nullptr; } ObjPtr referenced_class = class_linker->LookupResolvedType( - *dex_compilation_unit_->GetDexFile(), dex_compilation_unit_->GetDexFile()->GetMethodId(method_idx).class_idx_, dex_compilation_unit_->GetDexCache().Get(), class_loader.Get()); @@ -1058,7 +1163,7 @@ void HInstructionBuilder::BuildConstructorFenceForAllocation(HInstruction* alloc MethodCompilationStat::kConstructorFenceGeneratedNew); } -static bool IsSubClass(mirror::Class* to_test, mirror::Class* super_class) +static bool IsSubClass(ObjPtr to_test, ObjPtr super_class) REQUIRES_SHARED(Locks::mutator_lock_) { return to_test != nullptr && !to_test->IsInterface() && to_test->IsSubClass(super_class); } @@ -1281,6 +1386,8 @@ bool HInstructionBuilder::BuildInstanceFieldAccess(const Instruction& instructio uint16_t field_index; if (instruction.IsQuickened()) { if (!CanDecodeQuickenedInfo()) { + VLOG(compiler) << "Not compiled: Could not decode quickened instruction " + << instruction.Opcode(); return false; } field_index = LookupQuickenedInfo(quicken_index); @@ -1352,8 +1459,8 @@ bool HInstructionBuilder::BuildInstanceFieldAccess(const Instruction& instructio return true; } -static mirror::Class* GetClassFrom(CompilerDriver* driver, - const DexCompilationUnit& compilation_unit) { +static ObjPtr GetClassFrom(CompilerDriver* driver, + const DexCompilationUnit& compilation_unit) { ScopedObjectAccess soa(Thread::Current()); Handle class_loader = compilation_unit.GetClassLoader(); Handle dex_cache = compilation_unit.GetDexCache(); @@ -1361,11 +1468,11 @@ static mirror::Class* GetClassFrom(CompilerDriver* driver, return driver->ResolveCompilingMethodsClass(soa, dex_cache, class_loader, &compilation_unit); } -mirror::Class* HInstructionBuilder::GetOutermostCompilingClass() const { +ObjPtr HInstructionBuilder::GetOutermostCompilingClass() const { return GetClassFrom(compiler_driver_, *outer_compilation_unit_); } -mirror::Class* HInstructionBuilder::GetCompilingClass() const { +ObjPtr HInstructionBuilder::GetCompilingClass() const { return GetClassFrom(compiler_driver_, *dex_compilation_unit_); } @@ -1412,12 +1519,10 @@ ArtField* HInstructionBuilder::ResolveField(uint16_t field_idx, bool is_static, Handle class_loader = dex_compilation_unit_->GetClassLoader(); Handle compiling_class(hs.NewHandle(GetCompilingClass())); - ArtField* resolved_field = class_linker->ResolveField(*dex_compilation_unit_->GetDexFile(), - field_idx, + ArtField* resolved_field = class_linker->ResolveField(field_idx, dex_compilation_unit_->GetDexCache(), class_loader, is_static); - if (UNLIKELY(resolved_field == nullptr)) { // Clean up any exception left by type resolution. soa.Self()->ClearException(); @@ -1453,7 +1558,7 @@ ArtField* HInstructionBuilder::ResolveField(uint16_t field_idx, bool is_static, return resolved_field; } -bool HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, +void HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put) { uint32_t source_or_dest_reg = instruction.VRegA_21c(); @@ -1467,7 +1572,7 @@ bool HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, MethodCompilationStat::kUnresolvedField); DataType::Type field_type = GetFieldAccessType(*dex_file_, field_index); BuildUnresolvedStaticFieldAccess(instruction, dex_pc, is_put, field_type); - return true; + return; } DataType::Type field_type = GetFieldAccessType(*dex_file_, field_index); @@ -1485,7 +1590,7 @@ bool HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, MaybeRecordStat(compilation_stats_, MethodCompilationStat::kUnresolvedFieldNotAFastAccess); BuildUnresolvedStaticFieldAccess(instruction, dex_pc, is_put, field_type); - return true; + return; } HInstruction* cls = constant; @@ -1521,7 +1626,6 @@ bool HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, dex_pc)); UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction()); } - return true; } void HInstructionBuilder::BuildCheckedDivRem(uint16_t out_vreg, @@ -1641,7 +1745,8 @@ void HInstructionBuilder::BuildFillArrayData(const Instruction& instruction, uin int32_t payload_offset = instruction.VRegB_31t() + dex_pc; const Instruction::ArrayDataPayload* payload = - reinterpret_cast(code_item_.insns_ + payload_offset); + reinterpret_cast( + code_item_accessor_.Insns() + payload_offset); const uint8_t* data = payload->data; uint32_t element_count = payload->element_count; @@ -1729,6 +1834,17 @@ static TypeCheckKind ComputeTypeCheckKind(Handle cls) } } +void HInstructionBuilder::BuildLoadString(dex::StringIndex string_index, uint32_t dex_pc) { + HLoadString* load_string = + new (allocator_) HLoadString(graph_->GetCurrentMethod(), string_index, *dex_file_, dex_pc); + HSharpening::ProcessLoadString(load_string, + code_generator_, + compiler_driver_, + *dex_compilation_unit_, + handles_); + AppendInstruction(load_string); +} + HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, uint32_t dex_pc) { ScopedObjectAccess soa(Thread::Current()); const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); @@ -1741,7 +1857,7 @@ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, uint3 if (klass->IsPublic()) { needs_access_check = false; } else { - mirror::Class* compiling_class = GetCompilingClass(); + ObjPtr compiling_class = GetCompilingClass(); if (compiling_class != nullptr && compiling_class->CanAccess(klass.Get())) { needs_access_check = false; } @@ -1786,9 +1902,9 @@ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, // We actually cannot reference this class, we're forced to bail. return nullptr; } - // Append the instruction first, as setting the load kind affects the inputs. - AppendInstruction(load_class); + // Load kind must be set before inserting the instruction into the graph. load_class->SetLoadKind(load_kind); + AppendInstruction(load_class); return load_class; } @@ -1988,6 +2104,8 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, uint16_t method_idx; if (instruction.Opcode() == Instruction::INVOKE_VIRTUAL_QUICK) { if (!CanDecodeQuickenedInfo()) { + VLOG(compiler) << "Not compiled: Could not decode quickened instruction " + << instruction.Opcode(); return false; } method_idx = LookupQuickenedInfo(quicken_index); @@ -2013,6 +2131,8 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, uint16_t method_idx; if (instruction.Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK) { if (!CanDecodeQuickenedInfo()) { + VLOG(compiler) << "Not compiled: Could not decode quickened instruction " + << instruction.Opcode(); return false; } method_idx = LookupQuickenedInfo(quicken_index); @@ -2688,7 +2808,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::IGET_CHAR_QUICK: case Instruction::IGET_SHORT: case Instruction::IGET_SHORT_QUICK: { - if (!BuildInstanceFieldAccess(instruction, dex_pc, false, quicken_index)) { + if (!BuildInstanceFieldAccess(instruction, dex_pc, /* is_put */ false, quicken_index)) { return false; } break; @@ -2708,7 +2828,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::IPUT_CHAR_QUICK: case Instruction::IPUT_SHORT: case Instruction::IPUT_SHORT_QUICK: { - if (!BuildInstanceFieldAccess(instruction, dex_pc, true, quicken_index)) { + if (!BuildInstanceFieldAccess(instruction, dex_pc, /* is_put */ true, quicken_index)) { return false; } break; @@ -2721,9 +2841,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::SGET_BYTE: case Instruction::SGET_CHAR: case Instruction::SGET_SHORT: { - if (!BuildStaticFieldAccess(instruction, dex_pc, false)) { - return false; - } + BuildStaticFieldAccess(instruction, dex_pc, /* is_put */ false); break; } @@ -2734,9 +2852,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::SPUT_BYTE: case Instruction::SPUT_CHAR: case Instruction::SPUT_SHORT: { - if (!BuildStaticFieldAccess(instruction, dex_pc, true)) { - return false; - } + BuildStaticFieldAccess(instruction, dex_pc, /* is_put */ true); break; } @@ -2767,20 +2883,14 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::CONST_STRING: { dex::StringIndex string_index(instruction.VRegB_21c()); - AppendInstruction(new (allocator_) HLoadString(graph_->GetCurrentMethod(), - string_index, - *dex_file_, - dex_pc)); + BuildLoadString(string_index, dex_pc); UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction()); break; } case Instruction::CONST_STRING_JUMBO: { dex::StringIndex string_index(instruction.VRegB_31c()); - AppendInstruction(new (allocator_) HLoadString(graph_->GetCurrentMethod(), - string_index, - *dex_file_, - dex_pc)); + BuildLoadString(string_index, dex_pc); UpdateLocal(instruction.VRegA_31c(), current_block_->GetLastInstruction()); break; } @@ -2860,7 +2970,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, ObjPtr HInstructionBuilder::LookupResolvedType( dex::TypeIndex type_index, const DexCompilationUnit& compilation_unit) const { - return ClassLinker::LookupResolvedType( + return compilation_unit.GetClassLinker()->LookupResolvedType( type_index, compilation_unit.GetDexCache().Get(), compilation_unit.GetClassLoader().Get()); } diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index 058b71168714bf7b319693a6a0c73353da84a747..b4e30516ab225717e185baa031615fb4670ab081 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -19,6 +19,7 @@ #include "base/scoped_arena_allocator.h" #include "base/scoped_arena_containers.h" +#include "code_item_accessors.h" #include "data_type.h" #include "dex_file.h" #include "dex_file_types.h" @@ -50,7 +51,7 @@ class HInstructionBuilder : public ValueObject { HBasicBlockBuilder* block_builder, SsaBuilder* ssa_builder, const DexFile* dex_file, - const DexFile::CodeItem& code_item, + const CodeItemDebugInfoAccessor& accessor, DataType::Type return_type, const DexCompilationUnit* dex_compilation_unit, const DexCompilationUnit* outer_compilation_unit, @@ -59,32 +60,10 @@ class HInstructionBuilder : public ValueObject { const uint8_t* interpreter_metadata, OptimizingCompilerStats* compiler_stats, VariableSizedHandleScope* handles, - ScopedArenaAllocator* local_allocator) - : allocator_(graph->GetAllocator()), - graph_(graph), - handles_(handles), - dex_file_(dex_file), - code_item_(code_item), - return_type_(return_type), - block_builder_(block_builder), - ssa_builder_(ssa_builder), - compiler_driver_(compiler_driver), - code_generator_(code_generator), - dex_compilation_unit_(dex_compilation_unit), - outer_compilation_unit_(outer_compilation_unit), - quicken_info_(interpreter_metadata), - compilation_stats_(compiler_stats), - local_allocator_(local_allocator), - locals_for_(local_allocator->Adapter(kArenaAllocGraphBuilder)), - current_block_(nullptr), - current_locals_(nullptr), - latest_result_(nullptr), - current_this_parameter_(nullptr), - loop_headers_(local_allocator->Adapter(kArenaAllocGraphBuilder)) { - loop_headers_.reserve(kDefaultNumberOfLoops); - } + ScopedArenaAllocator* local_allocator); bool Build(); + void BuildIntrinsic(ArtMethod* method); private: void InitializeBlockLocals(); @@ -174,8 +153,8 @@ class HInstructionBuilder : public ValueObject { uint32_t dex_pc, bool is_put, DataType::Type field_type); - // Builds a static field access node and returns whether the instruction is supported. - bool BuildStaticFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put); + // Builds a static field access node. + void BuildStaticFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put); void BuildArrayAccess(const Instruction& instruction, uint32_t dex_pc, @@ -239,9 +218,10 @@ class HInstructionBuilder : public ValueObject { // Builds an instruction sequence for a switch statement. void BuildSwitch(const Instruction& instruction, uint32_t dex_pc); - // Builds a `HLoadClass` loading the given `type_index`. If `outer` is true, - // this method will use the outer class's dex file to lookup the type at - // `type_index`. + // Builds a `HLoadString` loading the given `string_index`. + void BuildLoadString(dex::StringIndex string_index, uint32_t dex_pc); + + // Builds a `HLoadClass` loading the given `type_index`. HLoadClass* BuildLoadClass(dex::TypeIndex type_index, uint32_t dex_pc); HLoadClass* BuildLoadClass(dex::TypeIndex type_index, @@ -252,10 +232,10 @@ class HInstructionBuilder : public ValueObject { REQUIRES_SHARED(Locks::mutator_lock_); // Returns the outer-most compiling method's class. - mirror::Class* GetOutermostCompilingClass() const; + ObjPtr GetOutermostCompilingClass() const; // Returns the class whose method is being compiled. - mirror::Class* GetCompilingClass() const; + ObjPtr GetCompilingClass() const; // Returns whether `type_index` points to the outer-most compiling method's class. bool IsOutermostCompilingClass(dex::TypeIndex type_index) const; @@ -327,7 +307,7 @@ class HInstructionBuilder : public ValueObject { // The dex file where the method being compiled is, and the bytecode data. const DexFile* const dex_file_; - const DexFile::CodeItem& code_item_; + const CodeItemDebugInfoAccessor code_item_accessor_; // null for intrinsic graph. // The return type of the method being compiled. const DataType::Type return_type_; diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 2bd2d5f0a1ae8d2c3b098d2fba823335a4fac647..a42a85dc1d28af42cb8544e1ec8e5256763fbec0 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -27,6 +27,10 @@ namespace art { +// Whether to run an exhaustive test of individual HInstructions cloning when each instruction +// is replaced with its copy if it is clonable. +static constexpr bool kTestInstructionClonerExhaustively = false; + class InstructionSimplifierVisitor : public HGraphDelegateVisitor { public: InstructionSimplifierVisitor(HGraph* graph, @@ -44,7 +48,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { void RecordSimplification() { simplification_occurred_ = true; simplifications_at_current_position_++; - MaybeRecordStat(stats_, kInstructionSimplifications); + MaybeRecordStat(stats_, MethodCompilationStat::kInstructionSimplifications); } bool ReplaceRotateWithRor(HBinaryOperation* op, HUShr* ushr, HShl* shl); @@ -130,6 +134,11 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { }; void InstructionSimplifier::Run() { + if (kTestInstructionClonerExhaustively) { + CloneAndReplaceInstructionVisitor visitor(graph_); + visitor.VisitReversePostOrder(); + } + InstructionSimplifierVisitor visitor(graph_, codegen_, compiler_driver_, stats_); visitor.Run(); } @@ -654,7 +663,7 @@ void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) { HGraph* graph = GetGraph(); if (object->IsNullConstant()) { - MaybeRecordStat(stats_, kRemovedInstanceOf); + MaybeRecordStat(stats_, MethodCompilationStat::kRemovedInstanceOf); instruction->ReplaceWith(graph->GetIntConstant(0)); instruction->GetBlock()->RemoveInstruction(instruction); RecordSimplification(); @@ -665,7 +674,7 @@ void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) { // the return value check with the `outcome` check, b/27651442 . bool outcome = false; if (TypeCheckHasKnownOutcome(load_class, object, &outcome)) { - MaybeRecordStat(stats_, kRemovedInstanceOf); + MaybeRecordStat(stats_, MethodCompilationStat::kRemovedInstanceOf); if (outcome && can_be_null) { // Type test will succeed, we just need a null test. HNotEqual* test = new (graph->GetAllocator()) HNotEqual(graph->GetNullConstant(), object); @@ -1072,6 +1081,58 @@ static inline bool TryReplaceFieldOrArrayGetType(HInstruction* maybe_get, DataTy } } +// The type conversion is only used for storing into a field/element of the +// same/narrower size. +static bool IsTypeConversionForStoringIntoNoWiderFieldOnly(HTypeConversion* type_conversion) { + if (type_conversion->HasEnvironmentUses()) { + return false; + } + DataType::Type input_type = type_conversion->GetInputType(); + DataType::Type result_type = type_conversion->GetResultType(); + if (!DataType::IsIntegralType(input_type) || + !DataType::IsIntegralType(result_type) || + input_type == DataType::Type::kInt64 || + result_type == DataType::Type::kInt64) { + // Type conversion is needed if non-integer types are involved, or 64-bit + // types are involved, which may use different number of registers. + return false; + } + if (DataType::Size(input_type) >= DataType::Size(result_type)) { + // Type conversion is not necessary when storing to a field/element of the + // same/smaller size. + } else { + // We do not handle this case here. + return false; + } + + // Check if the converted value is only used for storing into heap. + for (const HUseListNode& use : type_conversion->GetUses()) { + HInstruction* instruction = use.GetUser(); + if (instruction->IsInstanceFieldSet() && + instruction->AsInstanceFieldSet()->GetFieldType() == result_type) { + DCHECK_EQ(instruction->AsInstanceFieldSet()->GetValue(), type_conversion); + continue; + } + if (instruction->IsStaticFieldSet() && + instruction->AsStaticFieldSet()->GetFieldType() == result_type) { + DCHECK_EQ(instruction->AsStaticFieldSet()->GetValue(), type_conversion); + continue; + } + if (instruction->IsArraySet() && + instruction->AsArraySet()->GetComponentType() == result_type && + // not index use. + instruction->AsArraySet()->GetIndex() != type_conversion) { + DCHECK_EQ(instruction->AsArraySet()->GetValue(), type_conversion); + continue; + } + // The use is not as a store value, or the field/element type is not the + // same as the result_type, keep the type conversion. + return false; + } + // Codegen automatically handles the type conversion during the store. + return true; +} + void InstructionSimplifierVisitor::VisitTypeConversion(HTypeConversion* instruction) { HInstruction* input = instruction->GetInput(); DataType::Type input_type = input->GetType(); @@ -1160,6 +1221,13 @@ void InstructionSimplifierVisitor::VisitTypeConversion(HTypeConversion* instruct return; } } + + if (IsTypeConversionForStoringIntoNoWiderFieldOnly(instruction)) { + instruction->ReplaceWith(input); + instruction->GetBlock()->RemoveInstruction(instruction); + RecordSimplification(); + return; + } } void InstructionSimplifierVisitor::VisitAdd(HAdd* instruction) { @@ -2024,6 +2092,22 @@ void InstructionSimplifierVisitor::SimplifyStringEquals(HInvoke* instruction) { ReferenceTypeInfo argument_rti = argument->GetReferenceTypeInfo(); if (argument_rti.IsValid() && argument_rti.IsStringClass()) { optimizations.SetArgumentIsString(); + } else if (kUseReadBarrier) { + DCHECK(instruction->GetResolvedMethod() != nullptr); + DCHECK(instruction->GetResolvedMethod()->GetDeclaringClass()->IsStringClass() || + // Object.equals() can be devirtualized to String.equals(). + instruction->GetResolvedMethod()->GetDeclaringClass()->IsObjectClass()); + Runtime* runtime = Runtime::Current(); + // For AOT, we always assume that the boot image shall contain the String.class and + // we do not need a read barrier for boot image classes as they are non-moveable. + // For JIT, check if we actually have a boot image; if we do, the String.class + // should also be non-moveable. + if (runtime->IsAotCompiler() || runtime->GetHeap()->HasBootImageSpace()) { + DCHECK(runtime->IsAotCompiler() || + !runtime->GetHeap()->IsMovableObject( + instruction->GetResolvedMethod()->GetDeclaringClass())); + optimizations.SetNoReadBarrierForStringClass(); + } } } } @@ -2243,7 +2327,7 @@ void InstructionSimplifierVisitor::SimplifyStringCharAt(HInvoke* invoke) { HArrayLength* length = new (allocator) HArrayLength(str, dex_pc, /* is_string_length */ true); invoke->GetBlock()->InsertInstructionBefore(length, invoke); HBoundsCheck* bounds_check = new (allocator) HBoundsCheck( - index, length, dex_pc, invoke->GetDexMethodIndex()); + index, length, dex_pc, /* is_string_char_at */ true); invoke->GetBlock()->InsertInstructionBefore(bounds_check, invoke); HArrayGet* array_get = new (allocator) HArrayGet(str, bounds_check, diff --git a/compiler/optimizing/instruction_simplifier_arm.cc b/compiler/optimizing/instruction_simplifier_arm.cc index d41e49a0f3bc8faa784307630be7105deb4dd740..92081e30b1085191c66beae7904bd6f5c7855091 100644 --- a/compiler/optimizing/instruction_simplifier_arm.cc +++ b/compiler/optimizing/instruction_simplifier_arm.cc @@ -37,9 +37,7 @@ class InstructionSimplifierArmVisitor : public HGraphVisitor { private: void RecordSimplification() { - if (stats_ != nullptr) { - stats_->RecordStat(kInstructionSimplificationsArch); - } + MaybeRecordStat(stats_, MethodCompilationStat::kInstructionSimplificationsArch); } bool TryMergeIntoUsersShifterOperand(HInstruction* instruction); diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc index 69e1463ac42c8562cca48400d8e06100a3eabc75..1c44e5ac49af0005b67ca4e60685df74415c2b94 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.cc +++ b/compiler/optimizing/instruction_simplifier_arm64.cc @@ -37,9 +37,7 @@ class InstructionSimplifierArm64Visitor : public HGraphVisitor { private: void RecordSimplification() { - if (stats_ != nullptr) { - stats_->RecordStat(kInstructionSimplificationsArch); - } + MaybeRecordStat(stats_, MethodCompilationStat::kInstructionSimplificationsArch); } bool TryMergeIntoUsersShifterOperand(HInstruction* instruction); diff --git a/compiler/optimizing/instruction_simplifier_mips.cc b/compiler/optimizing/instruction_simplifier_mips.cc index 6a0d8a60c4f2dfd395d22f6811fe89e3d2a3c408..fa97401a0c90d0379e800c38d9ee09386801edb6 100644 --- a/compiler/optimizing/instruction_simplifier_mips.cc +++ b/compiler/optimizing/instruction_simplifier_mips.cc @@ -33,9 +33,7 @@ class InstructionSimplifierMipsVisitor : public HGraphVisitor { private: void RecordSimplification() { - if (stats_ != nullptr) { - stats_->RecordStat(kInstructionSimplificationsArch); - } + MaybeRecordStat(stats_, MethodCompilationStat::kInstructionSimplificationsArch); } bool TryExtractArrayAccessIndex(HInstruction* access, diff --git a/compiler/optimizing/instruction_simplifier_mips.h b/compiler/optimizing/instruction_simplifier_mips.h index 22cc2efc1ae1b96d5ab3a3624fd2fb67f28703f5..6cb8affe85c0190699a3c1874d71beacb2870a68 100644 --- a/compiler/optimizing/instruction_simplifier_mips.h +++ b/compiler/optimizing/instruction_simplifier_mips.h @@ -30,7 +30,7 @@ namespace mips { class InstructionSimplifierMips : public HOptimization { public: InstructionSimplifierMips(HGraph* graph, CodeGenerator* codegen, OptimizingCompilerStats* stats) - : HOptimization(graph, "instruction_simplifier_mips", stats), + : HOptimization(graph, kInstructionSimplifierMipsPassName, stats), codegen_(down_cast(codegen)) {} static constexpr const char* kInstructionSimplifierMipsPassName = "instruction_simplifier_mips"; diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index dfae53455589d63958a5fc51383820b39271b005..6928b70df7b2081eacc9d30b528dbba74a62524b 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -104,7 +104,8 @@ static inline IntrinsicExceptions GetExceptions(Intrinsics i) { return kCanThrow; } -static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) { +static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) + REQUIRES_SHARED(Locks::mutator_lock_) { // Whenever the intrinsic is marked as static, report an error if we find an InvokeVirtual. // // Whenever the intrinsic is marked as direct and we find an InvokeVirtual, a devirtualization @@ -130,18 +131,51 @@ static bool CheckInvokeType(Intrinsics intrinsic, HInvoke* invoke) { } if (invoke_type == kVirtual) { ArtMethod* art_method = invoke->GetResolvedMethod(); - ScopedObjectAccess soa(Thread::Current()); return (art_method->IsFinal() || art_method->GetDeclaringClass()->IsFinal()); } return false; case kVirtual: // Call might be devirtualized. - return (invoke_type == kVirtual || invoke_type == kDirect); + return (invoke_type == kVirtual || invoke_type == kDirect || invoke_type == kInterface); - default: + case kSuper: + case kInterface: + case kPolymorphic: return false; } + LOG(FATAL) << "Unknown intrinsic invoke type: " << intrinsic_type; + UNREACHABLE(); +} + +bool IntrinsicsRecognizer::Recognize(HInvoke* invoke, + ArtMethod* art_method, + /*out*/ bool* wrong_invoke_type) { + if (art_method == nullptr) { + art_method = invoke->GetResolvedMethod(); + } + *wrong_invoke_type = false; + if (art_method == nullptr || !art_method->IsIntrinsic()) { + return false; + } + + // TODO: b/65872996 The intent is that polymorphic signature methods should + // be compiler intrinsics. At present, they are only interpreter intrinsics. + if (art_method->IsPolymorphicSignature()) { + return false; + } + + Intrinsics intrinsic = static_cast(art_method->GetIntrinsic()); + if (CheckInvokeType(intrinsic, invoke) == false) { + *wrong_invoke_type = true; + return false; + } + + invoke->SetIntrinsic(intrinsic, + NeedsEnvironmentOrCache(intrinsic), + GetSideEffects(intrinsic), + GetExceptions(intrinsic)); + return true; } void IntrinsicsRecognizer::Run() { @@ -151,23 +185,14 @@ void IntrinsicsRecognizer::Run() { inst_it.Advance()) { HInstruction* inst = inst_it.Current(); if (inst->IsInvoke()) { - HInvoke* invoke = inst->AsInvoke(); - ArtMethod* art_method = invoke->GetResolvedMethod(); - if (art_method != nullptr && art_method->IsIntrinsic()) { - Intrinsics intrinsic = static_cast(art_method->GetIntrinsic()); - if (!CheckInvokeType(intrinsic, invoke)) { - LOG(WARNING) << "Found an intrinsic with unexpected invoke type: " - << static_cast(intrinsic) << " for " - << art_method->PrettyMethod() - << invoke->DebugName(); - } else { - invoke->SetIntrinsic(intrinsic, - NeedsEnvironmentOrCache(intrinsic), - GetSideEffects(intrinsic), - GetExceptions(intrinsic)); - MaybeRecordStat(stats_, - MethodCompilationStat::kIntrinsicRecognized); - } + bool wrong_invoke_type = false; + if (Recognize(inst->AsInvoke(), /* art_method */ nullptr, &wrong_invoke_type)) { + MaybeRecordStat(stats_, MethodCompilationStat::kIntrinsicRecognized); + } else if (wrong_invoke_type) { + LOG(WARNING) + << "Found an intrinsic with unexpected invoke type: " + << inst->AsInvoke()->GetResolvedMethod()->PrettyMethod() << " " + << inst->DebugName(); } } } diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h index bdeb261dbe456fd9c32c2a6a1a9e46714a692a4b..62991435c7ab12db5d42bd769117205ce8a351b8 100644 --- a/compiler/optimizing/intrinsics.h +++ b/compiler/optimizing/intrinsics.h @@ -37,11 +37,19 @@ static constexpr uint64_t kNanDouble = 0x7ff8000000000000; // Recognize intrinsics from HInvoke nodes. class IntrinsicsRecognizer : public HOptimization { public: - IntrinsicsRecognizer(HGraph* graph, OptimizingCompilerStats* stats) - : HOptimization(graph, kIntrinsicsRecognizerPassName, stats) {} + IntrinsicsRecognizer(HGraph* graph, + OptimizingCompilerStats* stats, + const char* name = kIntrinsicsRecognizerPassName) + : HOptimization(graph, name, stats) {} void Run() OVERRIDE; + // Static helper that recognizes intrinsic call. Returns true on success. + // If it fails due to invoke type mismatch, wrong_invoke_type is set. + // Useful to recognize intrinsics on individual calls outside this full pass. + static bool Recognize(HInvoke* invoke, ArtMethod* method, /*out*/ bool* wrong_invoke_type) + REQUIRES_SHARED(Locks::mutator_lock_); + static constexpr const char* kIntrinsicsRecognizerPassName = "intrinsics_recognition"; private: @@ -203,6 +211,7 @@ class StringEqualsOptimizations : public IntrinsicOptimizations { INTRINSIC_OPTIMIZATION(ArgumentNotNull, 0); INTRINSIC_OPTIMIZATION(ArgumentIsString, 1); + INTRINSIC_OPTIMIZATION(NoReadBarrierForStringClass, 2); private: DISALLOW_COPY_AND_ASSIGN(StringEqualsOptimizations); diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index ef85f9ccc41115cca5dc055a8359e95e3133621a..ca1b451e6bb289f75e2d5665bf63928218e37daa 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -1519,6 +1519,13 @@ static const char* GetConstString(HInstruction* candidate, uint32_t* utf16_lengt } void IntrinsicLocationsBuilderARM64::VisitStringEquals(HInvoke* invoke) { + if (kEmitCompilerReadBarrier && + !StringEqualsOptimizations(invoke).GetArgumentIsString() && + !StringEqualsOptimizations(invoke).GetNoReadBarrierForStringClass()) { + // No support for this odd case (String class is moveable, not in the boot image). + return; + } + LocationSummary* locations = new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); locations->SetInAt(0, Location::RequiresRegister()); diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc index e0874d954966e241f443a98e0ea240f3017de10d..1d8ea092a4e221b3ce131f47a7face111f38f860 100644 --- a/compiler/optimizing/intrinsics_arm_vixl.cc +++ b/compiler/optimizing/intrinsics_arm_vixl.cc @@ -1723,6 +1723,13 @@ static const char* GetConstString(HInstruction* candidate, uint32_t* utf16_lengt } void IntrinsicLocationsBuilderARMVIXL::VisitStringEquals(HInvoke* invoke) { + if (kEmitCompilerReadBarrier && + !StringEqualsOptimizations(invoke).GetArgumentIsString() && + !StringEqualsOptimizations(invoke).GetNoReadBarrierForStringClass()) { + // No support for this odd case (String class is moveable, not in the boot image). + return; + } + LocationSummary* locations = new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); InvokeRuntimeCallingConventionARMVIXL calling_convention; diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 98ccce79e4ae54b3a733f1450dbd1090c521ffc5..113c9de5a25a358b0fa85c346f1a57f082c87962 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -2062,6 +2062,13 @@ void IntrinsicCodeGeneratorMIPS::VisitStringCompareTo(HInvoke* invoke) { // boolean java.lang.String.equals(Object anObject) void IntrinsicLocationsBuilderMIPS::VisitStringEquals(HInvoke* invoke) { + if (kEmitCompilerReadBarrier && + !StringEqualsOptimizations(invoke).GetArgumentIsString() && + !StringEqualsOptimizations(invoke).GetNoReadBarrierForStringClass()) { + // No support for this odd case (String class is moveable, not in the boot image). + return; + } + LocationSummary* locations = new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); locations->SetInAt(0, Location::RequiresRegister()); diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index f62913430ef641b9be49d4141a26e7b6b35536e6..521bad27e2f062744e201a7dee9c58e7783d1866 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -1637,6 +1637,13 @@ void IntrinsicCodeGeneratorMIPS64::VisitStringCompareTo(HInvoke* invoke) { // boolean java.lang.String.equals(Object anObject) void IntrinsicLocationsBuilderMIPS64::VisitStringEquals(HInvoke* invoke) { + if (kEmitCompilerReadBarrier && + !StringEqualsOptimizations(invoke).GetArgumentIsString() && + !StringEqualsOptimizations(invoke).GetNoReadBarrierForStringClass()) { + // No support for this odd case (String class is moveable, not in the boot image). + return; + } + LocationSummary* locations = new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); locations->SetInAt(0, Location::RequiresRegister()); diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 8a0b6aeb0ee9d46e4004d763d2ae762853335db3..baa410b884edf2888ff7698a91ab985fefd46abf 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -1345,6 +1345,13 @@ void IntrinsicCodeGeneratorX86::VisitStringCompareTo(HInvoke* invoke) { } void IntrinsicLocationsBuilderX86::VisitStringEquals(HInvoke* invoke) { + if (kEmitCompilerReadBarrier && + !StringEqualsOptimizations(invoke).GetArgumentIsString() && + !StringEqualsOptimizations(invoke).GetNoReadBarrierForStringClass()) { + // No support for this odd case (String class is moveable, not in the boot image). + return; + } + LocationSummary* locations = new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); locations->SetInAt(0, Location::RequiresRegister()); diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 92ffda427b6b36d1916377bdd2d50f3e4b2a17d7..6dd8b8e1f5632be231088ce17e61a26401b6fbc8 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -1520,6 +1520,13 @@ void IntrinsicCodeGeneratorX86_64::VisitStringCompareTo(HInvoke* invoke) { } void IntrinsicLocationsBuilderX86_64::VisitStringEquals(HInvoke* invoke) { + if (kEmitCompilerReadBarrier && + !StringEqualsOptimizations(invoke).GetArgumentIsString() && + !StringEqualsOptimizations(invoke).GetNoReadBarrierForStringClass()) { + // No support for this odd case (String class is moveable, not in the boot image). + return; + } + LocationSummary* locations = new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); locations->SetInAt(0, Location::RequiresRegister()); diff --git a/compiler/optimizing/licm.cc b/compiler/optimizing/licm.cc index 7af1a20f9894fbd3fca6f50b50226c380b55b3f0..d3a0376e9c4ace06f6789ee646fe27a71b97cdf5 100644 --- a/compiler/optimizing/licm.cc +++ b/compiler/optimizing/licm.cc @@ -129,10 +129,25 @@ void LICM::Run() { !inst_it.Done(); inst_it.Advance()) { HInstruction* instruction = inst_it.Current(); - if (instruction->CanBeMoved() - && (!instruction->CanThrow() || !found_first_non_hoisted_visible_instruction_in_loop) - && !instruction->GetSideEffects().MayDependOn(loop_effects) - && InputsAreDefinedBeforeLoop(instruction)) { + bool can_move = false; + if (instruction->CanBeMoved() && InputsAreDefinedBeforeLoop(instruction)) { + if (instruction->CanThrow()) { + if (!found_first_non_hoisted_visible_instruction_in_loop) { + DCHECK(instruction->GetBlock()->IsLoopHeader()); + if (instruction->IsClinitCheck()) { + // clinit is only done once, and since all visible instructions + // in the loop header so far have been hoisted out, we can hoist + // the clinit check out also. + can_move = true; + } else if (!instruction->GetSideEffects().MayDependOn(loop_effects)) { + can_move = true; + } + } + } else if (!instruction->GetSideEffects().MayDependOn(loop_effects)) { + can_move = true; + } + } + if (can_move) { // We need to update the environment if the instruction has a loop header // phi in it. if (instruction->NeedsEnvironment()) { @@ -142,7 +157,9 @@ void LICM::Run() { } instruction->MoveBefore(pre_header->GetLastInstruction()); MaybeRecordStat(stats_, MethodCompilationStat::kLoopInvariantMoved); - } else if (instruction->CanThrow() || instruction->DoesAnyWrite()) { + } + + if (!can_move && (instruction->CanThrow() || instruction->DoesAnyWrite())) { // If `instruction` can do something visible (throw or write), // we cannot move further instructions that can throw. found_first_non_hoisted_visible_instruction_in_loop = true; diff --git a/compiler/optimizing/licm.h b/compiler/optimizing/licm.h index bf56f53d461b92f85fd46ad0ff803a49eebcc168..ee567aeb20e2a45a441696a862ae79e89d6a3991 100644 --- a/compiler/optimizing/licm.h +++ b/compiler/optimizing/licm.h @@ -26,8 +26,11 @@ class SideEffectsAnalysis; class LICM : public HOptimization { public: - LICM(HGraph* graph, const SideEffectsAnalysis& side_effects, OptimizingCompilerStats* stats) - : HOptimization(graph, kLoopInvariantCodeMotionPassName, stats), + LICM(HGraph* graph, + const SideEffectsAnalysis& side_effects, + OptimizingCompilerStats* stats, + const char* name = kLoopInvariantCodeMotionPassName) + : HOptimization(graph, name, stats), side_effects_(side_effects) {} void Run() OVERRIDE; diff --git a/compiler/optimizing/load_store_analysis.cc b/compiler/optimizing/load_store_analysis.cc index 5a8ac5919523533f5e7808aed81587b2e02bd8bf..8b1812a6de627ccf20e548e1d4e75148d5afdef7 100644 --- a/compiler/optimizing/load_store_analysis.cc +++ b/compiler/optimizing/load_store_analysis.cc @@ -22,111 +22,130 @@ namespace art { // The number of heap locations for most of the methods stays below this threshold. constexpr size_t kMaxNumberOfHeapLocations = 32; -// Check if array indices array[idx1 +/- CONST] and array[idx2] MAY alias. -static bool BinaryOpAndIndexMayAlias(const HBinaryOperation* idx1, const HInstruction* idx2) { - DCHECK(idx1 != nullptr); - DCHECK(idx2 != nullptr); +// Test if two integer ranges [l1,h1] and [l2,h2] overlap. +// Note that the ranges are inclusive on both ends. +// l1|------|h1 +// l2|------|h2 +static bool CanIntegerRangesOverlap(int64_t l1, int64_t h1, int64_t l2, int64_t h2) { + return std::max(l1, l2) <= std::min(h1, h2); +} - if (!idx1->IsAdd() && !idx1->IsSub()) { +static bool IsAddOrSub(const HInstruction* instruction) { + return instruction->IsAdd() || instruction->IsSub(); +} + +static bool CanBinaryOpAndIndexAlias(const HBinaryOperation* idx1, + const size_t vector_length1, + const HInstruction* idx2, + const size_t vector_length2) { + if (!IsAddOrSub(idx1)) { // We currently only support Add and Sub operations. return true; } - - HConstant* cst = idx1->GetConstantRight(); - if (cst == nullptr || cst->IsArithmeticZero()) { + if (idx1->AsBinaryOperation()->GetLeastConstantLeft() != idx2) { + // Cannot analyze [i+CONST1] and [j]. return true; } - - if (idx1->GetLeastConstantLeft() == idx2) { - // for example, array[idx1 + 1] and array[idx1] - return false; + if (!idx1->GetConstantRight()->IsIntConstant()) { + return true; } - return true; + // Since 'i' are the same in [i+CONST] and [i], + // further compare [CONST] and [0]. + int64_t l1 = idx1->IsAdd() ? + idx1->GetConstantRight()->AsIntConstant()->GetValue() : + -idx1->GetConstantRight()->AsIntConstant()->GetValue(); + int64_t l2 = 0; + int64_t h1 = l1 + (vector_length1 - 1); + int64_t h2 = l2 + (vector_length2 - 1); + return CanIntegerRangesOverlap(l1, h1, l2, h2); } -// Check if Add and Sub MAY alias when used as indices in arrays. -static bool BinaryOpsMayAlias(const HBinaryOperation* idx1, const HBinaryOperation* idx2) { - DCHECK(idx1!= nullptr); - DCHECK(idx2 != nullptr); - - HConstant* idx1_cst = idx1->GetConstantRight(); - HInstruction* idx1_other = idx1->GetLeastConstantLeft(); - HConstant* idx2_cst = idx2->GetConstantRight(); - HInstruction* idx2_other = idx2->GetLeastConstantLeft(); - - if (idx1_cst == nullptr || idx1_other == nullptr || - idx2_cst == nullptr || idx2_other == nullptr) { - // We only analyze patterns like [i +/- CONST]. +static bool CanBinaryOpsAlias(const HBinaryOperation* idx1, + const size_t vector_length1, + const HBinaryOperation* idx2, + const size_t vector_length2) { + if (!IsAddOrSub(idx1) || !IsAddOrSub(idx2)) { + // We currently only support Add and Sub operations. return true; } - - if (idx1_other != idx2_other) { - // For example, [j+1] and [k+1] MAY alias. + if (idx1->AsBinaryOperation()->GetLeastConstantLeft() != + idx2->AsBinaryOperation()->GetLeastConstantLeft()) { + // Cannot analyze [i+CONST1] and [j+CONST2]. return true; } - - if ((idx1->IsAdd() && idx2->IsAdd()) || - (idx1->IsSub() && idx2->IsSub())) { - return idx1_cst->AsIntConstant()->GetValue() == idx2_cst->AsIntConstant()->GetValue(); - } - - if ((idx1->IsAdd() && idx2->IsSub()) || - (idx1->IsSub() && idx2->IsAdd())) { - // [i + CONST1] and [i - CONST2] MAY alias iff CONST1 == -CONST2. - // By checking CONST1 == -CONST2, following cases are handled: - // - Zero constants case [i+0] and [i-0] is handled. - // - Overflow cases are handled, for example: - // [i+0x80000000] and [i-0x80000000]; - // [i+0x10] and [i-0xFFFFFFF0]. - // - Other cases [i+CONST1] and [i-CONST2] without any overflow is handled. - return idx1_cst->AsIntConstant()->GetValue() == -(idx2_cst->AsIntConstant()->GetValue()); + if (!idx1->GetConstantRight()->IsIntConstant() || + !idx2->GetConstantRight()->IsIntConstant()) { + return true; } - // All other cases, MAY alias. - return true; + // Since 'i' are the same in [i+CONST1] and [i+CONST2], + // further compare [CONST1] and [CONST2]. + int64_t l1 = idx1->IsAdd() ? + idx1->GetConstantRight()->AsIntConstant()->GetValue() : + -idx1->GetConstantRight()->AsIntConstant()->GetValue(); + int64_t l2 = idx2->IsAdd() ? + idx2->GetConstantRight()->AsIntConstant()->GetValue() : + -idx2->GetConstantRight()->AsIntConstant()->GetValue(); + int64_t h1 = l1 + (vector_length1 - 1); + int64_t h2 = l2 + (vector_length2 - 1); + return CanIntegerRangesOverlap(l1, h1, l2, h2); } -// The following array index cases are handled: -// [i] and [i] -// [CONST1] and [CONST2] -// [i] and [i+CONST] -// [i] and [i-CONST] -// [i+CONST1] and [i+CONST2] -// [i-CONST1] and [i-CONST2] -// [i+CONST1] and [i-CONST2] -// [i-CONST1] and [i+CONST2] -// For other complicated cases, we rely on other passes like GVN and simpilfier -// to optimize these cases before this pass. -// For example: [i+j+k+10] and [i+k+10+j] shall be optimized to [i7+10] and [i7+10]. -bool HeapLocationCollector::CanArrayIndicesAlias(const HInstruction* idx1, - const HInstruction* idx2) const { +bool HeapLocationCollector::CanArrayElementsAlias(const HInstruction* idx1, + const size_t vector_length1, + const HInstruction* idx2, + const size_t vector_length2) const { DCHECK(idx1 != nullptr); DCHECK(idx2 != nullptr); + DCHECK_GE(vector_length1, HeapLocation::kScalar); + DCHECK_GE(vector_length2, HeapLocation::kScalar); + // [i] and [i]. if (idx1 == idx2) { - // [i] and [i] return true; } - if (idx1->IsIntConstant() && idx2->IsIntConstant()) { - // [CONST1] and [CONST2] - return idx1->AsIntConstant()->GetValue() == idx2->AsIntConstant()->GetValue(); - } - - if (idx1->IsBinaryOperation() && !BinaryOpAndIndexMayAlias(idx1->AsBinaryOperation(), idx2)) { - // [i] and [i+/-CONST] - return false; - } - if (idx2->IsBinaryOperation() && !BinaryOpAndIndexMayAlias(idx2->AsBinaryOperation(), idx1)) { - // [i+/-CONST] and [i] - return false; - } - if (idx1->IsBinaryOperation() && idx2->IsBinaryOperation()) { - // [i+/-CONST1] and [i+/-CONST2] - if (!BinaryOpsMayAlias(idx1->AsBinaryOperation(), idx2->AsBinaryOperation())) { - return false; - } + // [CONST1] and [CONST2]. + if (idx1->IsIntConstant() && idx2->IsIntConstant()) { + int64_t l1 = idx1->AsIntConstant()->GetValue(); + int64_t l2 = idx2->AsIntConstant()->GetValue(); + // To avoid any overflow in following CONST+vector_length calculation, + // use int64_t instead of int32_t. + int64_t h1 = l1 + (vector_length1 - 1); + int64_t h2 = l2 + (vector_length2 - 1); + return CanIntegerRangesOverlap(l1, h1, l2, h2); + } + + // [i+CONST] and [i]. + if (idx1->IsBinaryOperation() && + idx1->AsBinaryOperation()->GetConstantRight() != nullptr && + idx1->AsBinaryOperation()->GetLeastConstantLeft() == idx2) { + return CanBinaryOpAndIndexAlias(idx1->AsBinaryOperation(), + vector_length1, + idx2, + vector_length2); + } + + // [i] and [i+CONST]. + if (idx2->IsBinaryOperation() && + idx2->AsBinaryOperation()->GetConstantRight() != nullptr && + idx2->AsBinaryOperation()->GetLeastConstantLeft() == idx1) { + return CanBinaryOpAndIndexAlias(idx2->AsBinaryOperation(), + vector_length2, + idx1, + vector_length1); + } + + // [i+CONST1] and [i+CONST2]. + if (idx1->IsBinaryOperation() && + idx1->AsBinaryOperation()->GetConstantRight() != nullptr && + idx2->IsBinaryOperation() && + idx2->AsBinaryOperation()->GetConstantRight() != nullptr) { + return CanBinaryOpsAlias(idx1->AsBinaryOperation(), + vector_length1, + idx2->AsBinaryOperation(), + vector_length2); } // By default, MAY alias. diff --git a/compiler/optimizing/load_store_analysis.h b/compiler/optimizing/load_store_analysis.h index 5a1df45914442715e96643536d9994245c97f3d7..437e6be41842c123632f520698dbe6e15210a65f 100644 --- a/compiler/optimizing/load_store_analysis.h +++ b/compiler/optimizing/load_store_analysis.h @@ -32,8 +32,7 @@ class ReferenceInfo : public ArenaObject { position_(pos), is_singleton_(true), is_singleton_and_not_returned_(true), - is_singleton_and_not_deopt_visible_(true), - has_index_aliasing_(false) { + is_singleton_and_not_deopt_visible_(true) { CalculateEscape(reference_, nullptr, &is_singleton_, @@ -70,16 +69,6 @@ class ReferenceInfo : public ArenaObject { (!is_singleton_and_not_returned_ || !is_singleton_and_not_deopt_visible_); } - bool HasIndexAliasing() { - return has_index_aliasing_; - } - - void SetHasIndexAliasing(bool has_index_aliasing) { - // Only allow setting to true. - DCHECK(has_index_aliasing); - has_index_aliasing_ = has_index_aliasing; - } - private: HInstruction* const reference_; const size_t position_; // position in HeapLocationCollector's ref_info_array_. @@ -90,9 +79,6 @@ class ReferenceInfo : public ArenaObject { bool is_singleton_and_not_returned_; // Is singleton and not used as an environment local of HDeoptimize. bool is_singleton_and_not_deopt_visible_; - // Some heap locations with reference_ have array index aliasing, - // e.g. arr[i] and arr[j] may be the same location. - bool has_index_aliasing_; DISALLOW_COPY_AND_ASSIGN(ReferenceInfo); }; @@ -102,23 +88,27 @@ class ReferenceInfo : public ArenaObject { class HeapLocation : public ArenaObject { public: static constexpr size_t kInvalidFieldOffset = -1; - + // Default value for heap locations which are not vector data. + static constexpr size_t kScalar = 1; // TODO: more fine-grained array types. static constexpr int16_t kDeclaringClassDefIndexForArrays = -1; HeapLocation(ReferenceInfo* ref_info, size_t offset, HInstruction* index, + size_t vector_length, int16_t declaring_class_def_index) : ref_info_(ref_info), offset_(offset), index_(index), + vector_length_(vector_length), declaring_class_def_index_(declaring_class_def_index), - value_killed_by_loop_side_effects_(true) { + value_killed_by_loop_side_effects_(true), + has_aliased_locations_(false) { DCHECK(ref_info != nullptr); DCHECK((offset == kInvalidFieldOffset && index != nullptr) || (offset != kInvalidFieldOffset && index == nullptr)); - if (ref_info->IsSingleton() && !IsArrayElement()) { + if (ref_info->IsSingleton() && !IsArray()) { // Assume this location's value cannot be killed by loop side effects // until proven otherwise. value_killed_by_loop_side_effects_ = false; @@ -128,6 +118,7 @@ class HeapLocation : public ArenaObject { ReferenceInfo* GetReferenceInfo() const { return ref_info_; } size_t GetOffset() const { return offset_; } HInstruction* GetIndex() const { return index_; } + size_t GetVectorLength() const { return vector_length_; } // Returns the definition of declaring class' dex index. // It's kDeclaringClassDefIndexForArrays for an array element. @@ -135,7 +126,7 @@ class HeapLocation : public ArenaObject { return declaring_class_def_index_; } - bool IsArrayElement() const { + bool IsArray() const { return index_ != nullptr; } @@ -147,16 +138,40 @@ class HeapLocation : public ArenaObject { value_killed_by_loop_side_effects_ = val; } + bool HasAliasedLocations() const { + return has_aliased_locations_; + } + + void SetHasAliasedLocations(bool val) { + has_aliased_locations_ = val; + } + private: - ReferenceInfo* const ref_info_; // reference for instance/static field or array access. - const size_t offset_; // offset of static/instance field. - HInstruction* const index_; // index of an array element. - const int16_t declaring_class_def_index_; // declaring class's def's dex index. - bool value_killed_by_loop_side_effects_; // value of this location may be killed by loop - // side effects because this location is stored - // into inside a loop. This gives - // better info on whether a singleton's location - // value may be killed by loop side effects. + // Reference for instance/static field, array element or vector data. + ReferenceInfo* const ref_info_; + // Offset of static/instance field. + // Invalid when this HeapLocation is not field. + const size_t offset_; + // Index of an array element or starting index of vector data. + // Invalid when this HeapLocation is not array. + HInstruction* const index_; + // Vector length of vector data. + // When this HeapLocation is not vector data, it's value is kScalar. + const size_t vector_length_; + // Declaring class's def's dex index. + // Invalid when this HeapLocation is not field access. + const int16_t declaring_class_def_index_; + + // Value of this location may be killed by loop side effects + // because this location is stored into inside a loop. + // This gives better info on whether a singleton's location + // value may be killed by loop side effects. + bool value_killed_by_loop_side_effects_; + + // Has aliased heap locations in the method, due to either the + // reference is aliased or the array element is aliased via different + // index names. + bool has_aliased_locations_; DISALLOW_COPY_AND_ASSIGN(HeapLocation); }; @@ -218,14 +233,26 @@ class HeapLocationCollector : public HGraphVisitor { return nullptr; } - size_t GetArrayAccessHeapLocation(HInstruction* array, HInstruction* index) const { + size_t GetFieldHeapLocation(HInstruction* object, const FieldInfo* field) const { + DCHECK(object != nullptr); + DCHECK(field != nullptr); + return FindHeapLocationIndex(FindReferenceInfoOf(HuntForOriginalReference(object)), + field->GetFieldOffset().SizeValue(), + nullptr, + HeapLocation::kScalar, + field->GetDeclaringClassDefIndex()); + } + + size_t GetArrayHeapLocation(HInstruction* array, + HInstruction* index, + size_t vector_length = HeapLocation::kScalar) const { DCHECK(array != nullptr); DCHECK(index != nullptr); - HInstruction* original_ref = HuntForOriginalReference(array); - ReferenceInfo* ref_info = FindReferenceInfoOf(original_ref); - return FindHeapLocationIndex(ref_info, + DCHECK_GE(vector_length, HeapLocation::kScalar); + return FindHeapLocationIndex(FindReferenceInfoOf(HuntForOriginalReference(array)), HeapLocation::kInvalidFieldOffset, index, + vector_length, HeapLocation::kDeclaringClassDefIndexForArrays); } @@ -242,15 +269,26 @@ class HeapLocationCollector : public HGraphVisitor { } // Find and return the heap location index in heap_locations_. + // NOTE: When heap locations are created, potentially aliasing/overlapping + // accesses are given different indexes. This find function also + // doesn't take aliasing/overlapping into account. For example, + // this function returns three different indexes for: + // - ref_info=array, index=i, vector_length=kScalar; + // - ref_info=array, index=i, vector_length=2; + // - ref_info=array, index=i, vector_length=4; + // In later analysis, ComputeMayAlias() and MayAlias() compute and tell whether + // these indexes alias. size_t FindHeapLocationIndex(ReferenceInfo* ref_info, size_t offset, HInstruction* index, + size_t vector_length, int16_t declaring_class_def_index) const { for (size_t i = 0; i < heap_locations_.size(); i++) { HeapLocation* loc = heap_locations_[i]; if (loc->GetReferenceInfo() == ref_info && loc->GetOffset() == offset && loc->GetIndex() == index && + loc->GetVectorLength() == vector_length && loc->GetDeclaringClassDefIndex() == declaring_class_def_index) { return i; } @@ -315,7 +353,10 @@ class HeapLocationCollector : public HGraphVisitor { return true; } - bool CanArrayIndicesAlias(const HInstruction* i1, const HInstruction* i2) const; + bool CanArrayElementsAlias(const HInstruction* idx1, + const size_t vector_length1, + const HInstruction* idx2, + const size_t vector_length2) const; // `index1` and `index2` are indices in the array of collected heap locations. // Returns the position in the bit vector that tracks whether the two heap @@ -336,11 +377,12 @@ class HeapLocationCollector : public HGraphVisitor { // Compute if two locations may alias to each other. bool ComputeMayAlias(size_t index1, size_t index2) const { + DCHECK_NE(index1, index2); HeapLocation* loc1 = heap_locations_[index1]; HeapLocation* loc2 = heap_locations_[index2]; if (loc1->GetOffset() != loc2->GetOffset()) { // Either two different instance fields, or one is an instance - // field and the other is an array element. + // field and the other is an array data. return false; } if (loc1->GetDeclaringClassDefIndex() != loc2->GetDeclaringClassDefIndex()) { @@ -350,15 +392,17 @@ class HeapLocationCollector : public HGraphVisitor { if (!CanReferencesAlias(loc1->GetReferenceInfo(), loc2->GetReferenceInfo())) { return false; } - if (loc1->IsArrayElement() && loc2->IsArrayElement()) { - HInstruction* array_index1 = loc1->GetIndex(); - HInstruction* array_index2 = loc2->GetIndex(); - if (!CanArrayIndicesAlias(array_index1, array_index2)) { + if (loc1->IsArray() && loc2->IsArray()) { + HInstruction* idx1 = loc1->GetIndex(); + HInstruction* idx2 = loc2->GetIndex(); + size_t vector_length1 = loc1->GetVectorLength(); + size_t vector_length2 = loc2->GetVectorLength(); + if (!CanArrayElementsAlias(idx1, vector_length1, idx2, vector_length2)) { return false; } - ReferenceInfo* ref_info = loc1->GetReferenceInfo(); - ref_info->SetHasIndexAliasing(true); } + loc1->SetHasAliasedLocations(true); + loc2->SetHasAliasedLocations(true); return true; } @@ -383,14 +427,15 @@ class HeapLocationCollector : public HGraphVisitor { HeapLocation* GetOrCreateHeapLocation(HInstruction* ref, size_t offset, HInstruction* index, + size_t vector_length, int16_t declaring_class_def_index) { HInstruction* original_ref = HuntForOriginalReference(ref); ReferenceInfo* ref_info = GetOrCreateReferenceInfo(original_ref); size_t heap_location_idx = FindHeapLocationIndex( - ref_info, offset, index, declaring_class_def_index); + ref_info, offset, index, vector_length, declaring_class_def_index); if (heap_location_idx == kHeapLocationNotFound) { HeapLocation* heap_loc = new (GetGraph()->GetAllocator()) - HeapLocation(ref_info, offset, index, declaring_class_def_index); + HeapLocation(ref_info, offset, index, vector_length, declaring_class_def_index); heap_locations_.push_back(heap_loc); return heap_loc; } @@ -403,12 +448,19 @@ class HeapLocationCollector : public HGraphVisitor { } const uint16_t declaring_class_def_index = field_info.GetDeclaringClassDefIndex(); const size_t offset = field_info.GetFieldOffset().SizeValue(); - return GetOrCreateHeapLocation(ref, offset, nullptr, declaring_class_def_index); + return GetOrCreateHeapLocation(ref, + offset, + nullptr, + HeapLocation::kScalar, + declaring_class_def_index); } - void VisitArrayAccess(HInstruction* array, HInstruction* index) { - GetOrCreateHeapLocation(array, HeapLocation::kInvalidFieldOffset, - index, HeapLocation::kDeclaringClassDefIndexForArrays); + void VisitArrayAccess(HInstruction* array, HInstruction* index, size_t vector_length) { + GetOrCreateHeapLocation(array, + HeapLocation::kInvalidFieldOffset, + index, + vector_length, + HeapLocation::kDeclaringClassDefIndexForArrays); } void VisitInstanceFieldGet(HInstanceFieldGet* instruction) OVERRIDE { @@ -456,12 +508,30 @@ class HeapLocationCollector : public HGraphVisitor { // since we cannot accurately track the fields. void VisitArrayGet(HArrayGet* instruction) OVERRIDE { - VisitArrayAccess(instruction->InputAt(0), instruction->InputAt(1)); + HInstruction* array = instruction->InputAt(0); + HInstruction* index = instruction->InputAt(1); + VisitArrayAccess(array, index, HeapLocation::kScalar); CreateReferenceInfoForReferenceType(instruction); } void VisitArraySet(HArraySet* instruction) OVERRIDE { - VisitArrayAccess(instruction->InputAt(0), instruction->InputAt(1)); + HInstruction* array = instruction->InputAt(0); + HInstruction* index = instruction->InputAt(1); + VisitArrayAccess(array, index, HeapLocation::kScalar); + has_heap_stores_ = true; + } + + void VisitVecLoad(HVecLoad* instruction) OVERRIDE { + HInstruction* array = instruction->InputAt(0); + HInstruction* index = instruction->InputAt(1); + VisitArrayAccess(array, index, instruction->GetVectorLength()); + CreateReferenceInfoForReferenceType(instruction); + } + + void VisitVecStore(HVecStore* instruction) OVERRIDE { + HInstruction* array = instruction->InputAt(0); + HInstruction* index = instruction->InputAt(1); + VisitArrayAccess(array, index, instruction->GetVectorLength()); has_heap_stores_ = true; } @@ -494,8 +564,8 @@ class HeapLocationCollector : public HGraphVisitor { class LoadStoreAnalysis : public HOptimization { public: - explicit LoadStoreAnalysis(HGraph* graph) - : HOptimization(graph, kLoadStoreAnalysisPassName), + explicit LoadStoreAnalysis(HGraph* graph, const char* name = kLoadStoreAnalysisPassName) + : HOptimization(graph, name), heap_location_collector_(graph) {} const HeapLocationCollector& GetHeapLocationCollector() const { diff --git a/compiler/optimizing/load_store_analysis_test.cc b/compiler/optimizing/load_store_analysis_test.cc index b41e1e4d00556f789b00ddbfa8b40159610e67c6..56361a8c9002bb693d00128cbab81cc0120e1fb4 100644 --- a/compiler/optimizing/load_store_analysis_test.cc +++ b/compiler/optimizing/load_store_analysis_test.cc @@ -78,11 +78,12 @@ TEST_F(LoadStoreAnalysisTest, ArrayHeapLocations) { // Test queries on HeapLocationCollector's ref info and index records. ReferenceInfo* ref = heap_location_collector.FindReferenceInfoOf(array); - size_t field_off = HeapLocation::kInvalidFieldOffset; + size_t field = HeapLocation::kInvalidFieldOffset; + size_t vec = HeapLocation::kScalar; size_t class_def = HeapLocation::kDeclaringClassDefIndexForArrays; - size_t loc1 = heap_location_collector.FindHeapLocationIndex(ref, field_off, c1, class_def); - size_t loc2 = heap_location_collector.FindHeapLocationIndex(ref, field_off, c2, class_def); - size_t loc3 = heap_location_collector.FindHeapLocationIndex(ref, field_off, index, class_def); + size_t loc1 = heap_location_collector.FindHeapLocationIndex(ref, field, c1, vec, class_def); + size_t loc2 = heap_location_collector.FindHeapLocationIndex(ref, field, c2, vec, class_def); + size_t loc3 = heap_location_collector.FindHeapLocationIndex(ref, field, index, vec, class_def); // must find this reference info for array in HeapLocationCollector. ASSERT_TRUE(ref != nullptr); // must find these heap locations; @@ -167,10 +168,8 @@ TEST_F(LoadStoreAnalysisTest, FieldHeapLocations) { // Test queries on HeapLocationCollector's ref info and index records. ReferenceInfo* ref = heap_location_collector.FindReferenceInfoOf(object); - size_t loc1 = heap_location_collector.FindHeapLocationIndex( - ref, 10, nullptr, kUnknownClassDefIndex); - size_t loc2 = heap_location_collector.FindHeapLocationIndex( - ref, 20, nullptr, kUnknownClassDefIndex); + size_t loc1 = heap_location_collector.GetFieldHeapLocation(object, &get_field10->GetFieldInfo()); + size_t loc2 = heap_location_collector.GetFieldHeapLocation(object, &get_field20->GetFieldInfo()); // must find references info for object and in HeapLocationCollector. ASSERT_TRUE(ref != nullptr); // must find these heap locations. @@ -247,31 +246,236 @@ TEST_F(LoadStoreAnalysisTest, ArrayIndexAliasingTest) { size_t loc2 = HeapLocationCollector::kHeapLocationNotFound; // Test alias: array[0] and array[1] - loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, c0); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, c1); + loc1 = heap_location_collector.GetArrayHeapLocation(array, c0); + loc2 = heap_location_collector.GetArrayHeapLocation(array, c1); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+0] and array[i-0] - loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, add0); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, sub0); + loc1 = heap_location_collector.GetArrayHeapLocation(array, add0); + loc2 = heap_location_collector.GetArrayHeapLocation(array, sub0); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+1] and array[i-1] - loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, add1); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, sub1); + loc1 = heap_location_collector.GetArrayHeapLocation(array, add1); + loc2 = heap_location_collector.GetArrayHeapLocation(array, sub1); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+1] and array[1-i] - loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, add1); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, rev_sub1); + loc1 = heap_location_collector.GetArrayHeapLocation(array, add1); + loc2 = heap_location_collector.GetArrayHeapLocation(array, rev_sub1); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+1] and array[i-(-1)] - loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, add1); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, sub_neg1); + loc1 = heap_location_collector.GetArrayHeapLocation(array, add1); + loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_neg1); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); } +TEST_F(LoadStoreAnalysisTest, ArrayAliasingTest) { + HBasicBlock* entry = new (GetAllocator()) HBasicBlock(graph_); + graph_->AddBlock(entry); + graph_->SetEntryBlock(entry); + graph_->BuildDominatorTree(); + + HInstruction* array = new (GetAllocator()) HParameterValue( + graph_->GetDexFile(), dex::TypeIndex(0), 0, DataType::Type::kReference); + HInstruction* index = new (GetAllocator()) HParameterValue( + graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kInt32); + HInstruction* c0 = graph_->GetIntConstant(0); + HInstruction* c1 = graph_->GetIntConstant(1); + HInstruction* c6 = graph_->GetIntConstant(6); + HInstruction* c8 = graph_->GetIntConstant(8); + + HInstruction* arr_set_0 = new (GetAllocator()) HArraySet(array, + c0, + c0, + DataType::Type::kInt32, + 0); + HInstruction* arr_set_1 = new (GetAllocator()) HArraySet(array, + c1, + c0, + DataType::Type::kInt32, + 0); + HInstruction* arr_set_i = new (GetAllocator()) HArraySet(array, + index, + c0, + DataType::Type::kInt32, + 0); + + HVecOperation* v1 = new (GetAllocator()) HVecReplicateScalar(GetAllocator(), + c1, + DataType::Type::kInt32, + 4, + kNoDexPc); + HVecOperation* v2 = new (GetAllocator()) HVecReplicateScalar(GetAllocator(), + c1, + DataType::Type::kInt32, + 2, + kNoDexPc); + HInstruction* i_add6 = new (GetAllocator()) HAdd(DataType::Type::kInt32, index, c6); + HInstruction* i_add8 = new (GetAllocator()) HAdd(DataType::Type::kInt32, index, c8); + + HInstruction* vstore_0 = new (GetAllocator()) HVecStore( + GetAllocator(), + array, + c0, + v1, + DataType::Type::kInt32, + SideEffects::ArrayWriteOfType(DataType::Type::kInt32), + 4, + kNoDexPc); + HInstruction* vstore_1 = new (GetAllocator()) HVecStore( + GetAllocator(), + array, + c1, + v1, + DataType::Type::kInt32, + SideEffects::ArrayWriteOfType(DataType::Type::kInt32), + 4, + kNoDexPc); + HInstruction* vstore_8 = new (GetAllocator()) HVecStore( + GetAllocator(), + array, + c8, + v1, + DataType::Type::kInt32, + SideEffects::ArrayWriteOfType(DataType::Type::kInt32), + 4, + kNoDexPc); + HInstruction* vstore_i = new (GetAllocator()) HVecStore( + GetAllocator(), + array, + index, + v1, + DataType::Type::kInt32, + SideEffects::ArrayWriteOfType(DataType::Type::kInt32), + 4, + kNoDexPc); + HInstruction* vstore_i_add6 = new (GetAllocator()) HVecStore( + GetAllocator(), + array, + i_add6, + v1, + DataType::Type::kInt32, + SideEffects::ArrayWriteOfType(DataType::Type::kInt32), + 4, + kNoDexPc); + HInstruction* vstore_i_add8 = new (GetAllocator()) HVecStore( + GetAllocator(), + array, + i_add8, + v1, + DataType::Type::kInt32, + SideEffects::ArrayWriteOfType(DataType::Type::kInt32), + 4, + kNoDexPc); + HInstruction* vstore_i_add6_vlen2 = new (GetAllocator()) HVecStore( + GetAllocator(), + array, + i_add6, + v2, + DataType::Type::kInt32, + SideEffects::ArrayWriteOfType(DataType::Type::kInt32), + 2, + kNoDexPc); + + entry->AddInstruction(array); + entry->AddInstruction(index); + + entry->AddInstruction(arr_set_0); + entry->AddInstruction(arr_set_1); + entry->AddInstruction(arr_set_i); + entry->AddInstruction(v1); + entry->AddInstruction(v2); + entry->AddInstruction(i_add6); + entry->AddInstruction(i_add8); + entry->AddInstruction(vstore_0); + entry->AddInstruction(vstore_1); + entry->AddInstruction(vstore_8); + entry->AddInstruction(vstore_i); + entry->AddInstruction(vstore_i_add6); + entry->AddInstruction(vstore_i_add8); + entry->AddInstruction(vstore_i_add6_vlen2); + + LoadStoreAnalysis lsa(graph_); + lsa.Run(); + const HeapLocationCollector& heap_location_collector = lsa.GetHeapLocationCollector(); + + // LSA/HeapLocationCollector should see those instructions. + ASSERT_EQ(heap_location_collector.GetNumberOfHeapLocations(), 10U); + ASSERT_TRUE(heap_location_collector.HasHeapStores()); + + // Test queries on HeapLocationCollector's aliasing matrix after load store analysis. + size_t loc1, loc2; + + // Test alias: array[0] and array[0,1,2,3] + loc1 = heap_location_collector.GetArrayHeapLocation(array, c0); + loc2 = heap_location_collector.GetArrayHeapLocation(array, c0, 4); + ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); + + // Test alias: array[0] and array[8,9,10,11] + loc1 = heap_location_collector.GetArrayHeapLocation(array, c0); + loc2 = heap_location_collector.GetArrayHeapLocation(array, c8, 4); + ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); + + // Test alias: array[1] and array[8,9,10,11] + loc1 = heap_location_collector.GetArrayHeapLocation(array, c1); + loc2 = heap_location_collector.GetArrayHeapLocation(array, c8, 4); + ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); + + // Test alias: array[1] and array[0,1,2,3] + loc1 = heap_location_collector.GetArrayHeapLocation(array, c1); + loc2 = heap_location_collector.GetArrayHeapLocation(array, c0, 4); + ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); + + // Test alias: array[0,1,2,3] and array[8,9,10,11] + loc1 = heap_location_collector.GetArrayHeapLocation(array, c0, 4); + loc2 = heap_location_collector.GetArrayHeapLocation(array, c8, 4); + ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); + + // Test alias: array[0,1,2,3] and array[1,2,3,4] + loc1 = heap_location_collector.GetArrayHeapLocation(array, c1, 4); + loc2 = heap_location_collector.GetArrayHeapLocation(array, c0, 4); + ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); + + // Test alias: array[0] and array[i,i+1,i+2,i+3] + loc1 = heap_location_collector.GetArrayHeapLocation(array, c0); + loc2 = heap_location_collector.GetArrayHeapLocation(array, index, 4); + ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); + + // Test alias: array[i] and array[0,1,2,3] + loc1 = heap_location_collector.GetArrayHeapLocation(array, index); + loc2 = heap_location_collector.GetArrayHeapLocation(array, c0, 4); + ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); + + // Test alias: array[i] and array[i,i+1,i+2,i+3] + loc1 = heap_location_collector.GetArrayHeapLocation(array, index); + loc2 = heap_location_collector.GetArrayHeapLocation(array, index, 4); + ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); + + // Test alias: array[i] and array[i+8,i+9,i+10,i+11] + loc1 = heap_location_collector.GetArrayHeapLocation(array, index); + loc2 = heap_location_collector.GetArrayHeapLocation(array, i_add8, 4); + ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); + + // Test alias: array[i+6,i+7,i+8,i+9] and array[i+8,i+9,i+10,i+11] + // Test partial overlap. + loc1 = heap_location_collector.GetArrayHeapLocation(array, i_add6, 4); + loc2 = heap_location_collector.GetArrayHeapLocation(array, i_add8, 4); + ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); + + // Test alias: array[i+6,i+7] and array[i,i+1,i+2,i+3] + // Test different vector lengths. + loc1 = heap_location_collector.GetArrayHeapLocation(array, i_add6, 2); + loc2 = heap_location_collector.GetArrayHeapLocation(array, index, 4); + ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); + + // Test alias: array[i+6,i+7] and array[i+8,i+9,i+10,i+11] + loc1 = heap_location_collector.GetArrayHeapLocation(array, i_add6, 2); + loc2 = heap_location_collector.GetArrayHeapLocation(array, i_add8, 4); + ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); +} + TEST_F(LoadStoreAnalysisTest, ArrayIndexCalculationOverflowTest) { HBasicBlock* entry = new (GetAllocator()) HBasicBlock(graph_); graph_->AddBlock(entry); @@ -359,33 +563,33 @@ TEST_F(LoadStoreAnalysisTest, ArrayIndexCalculationOverflowTest) { size_t loc2 = HeapLocationCollector::kHeapLocationNotFound; // Test alias: array[i+0x80000000] and array[i-0x80000000] - loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, add_0x80000000); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, sub_0x80000000); + loc1 = heap_location_collector.GetArrayHeapLocation(array, add_0x80000000); + loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0x80000000); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+0x10] and array[i-0xFFFFFFF0] - loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, add_0x10); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, sub_0xFFFFFFF0); + loc1 = heap_location_collector.GetArrayHeapLocation(array, add_0x10); + loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0xFFFFFFF0); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+0x7FFFFFFF] and array[i-0x80000001] - loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, add_0x7FFFFFFF); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, sub_0x80000001); + loc1 = heap_location_collector.GetArrayHeapLocation(array, add_0x7FFFFFFF); + loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0x80000001); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Test alias: array[i+0] and array[i-0] - loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, add_0); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, sub_0); + loc1 = heap_location_collector.GetArrayHeapLocation(array, add_0); + loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); // Should not alias: - loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, sub_0x80000000); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, sub_0x80000001); + loc1 = heap_location_collector.GetArrayHeapLocation(array, sub_0x80000000); + loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0x80000001); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); // Should not alias: - loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, add_0); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(array, sub_0x80000000); + loc1 = heap_location_collector.GetArrayHeapLocation(array, add_0); + loc2 = heap_location_collector.GetArrayHeapLocation(array, sub_0x80000000); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); } @@ -443,10 +647,10 @@ TEST_F(LoadStoreAnalysisTest, TestHuntOriginalRef) { // times the original reference has been transformed by BoundType, // NullCheck, IntermediateAddress, etc. ASSERT_EQ(heap_location_collector.GetNumberOfHeapLocations(), 1U); - size_t loc1 = heap_location_collector.GetArrayAccessHeapLocation(array, c1); - size_t loc2 = heap_location_collector.GetArrayAccessHeapLocation(bound_type, c1); - size_t loc3 = heap_location_collector.GetArrayAccessHeapLocation(null_check, c1); - size_t loc4 = heap_location_collector.GetArrayAccessHeapLocation(inter_addr, c1); + size_t loc1 = heap_location_collector.GetArrayHeapLocation(array, c1); + size_t loc2 = heap_location_collector.GetArrayHeapLocation(bound_type, c1); + size_t loc3 = heap_location_collector.GetArrayHeapLocation(null_check, c1); + size_t loc4 = heap_location_collector.GetArrayHeapLocation(inter_addr, c1); ASSERT_TRUE(loc1 != HeapLocationCollector::kHeapLocationNotFound); ASSERT_EQ(loc1, loc2); ASSERT_EQ(loc1, loc3); diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index a16fdc2a6711a62576f223495301ff2e099432ef..88326d321b9747c070b762dd3b0431e7edb14bfe 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -74,25 +74,116 @@ class LSEVisitor : public HGraphDelegateVisitor { HGraphVisitor::VisitBasicBlock(block); } + HTypeConversion* AddTypeConversionIfNecessary(HInstruction* instruction, + HInstruction* value, + DataType::Type expected_type) { + HTypeConversion* type_conversion = nullptr; + // Should never add type conversion into boolean value. + if (expected_type != DataType::Type::kBool && + !DataType::IsTypeConversionImplicit(value->GetType(), expected_type)) { + type_conversion = new (GetGraph()->GetAllocator()) HTypeConversion( + expected_type, value, instruction->GetDexPc()); + instruction->GetBlock()->InsertInstructionBefore(type_conversion, instruction); + } + return type_conversion; + } + + // Find an instruction's substitute if it should be removed. + // Return the same instruction if it should not be removed. + HInstruction* FindSubstitute(HInstruction* instruction) { + size_t size = removed_loads_.size(); + for (size_t i = 0; i < size; i++) { + if (removed_loads_[i] == instruction) { + return substitute_instructions_for_loads_[i]; + } + } + return instruction; + } + + void AddRemovedLoad(HInstruction* load, HInstruction* heap_value) { + DCHECK_EQ(FindSubstitute(heap_value), heap_value) << + "Unexpected heap_value that has a substitute " << heap_value->DebugName(); + removed_loads_.push_back(load); + substitute_instructions_for_loads_.push_back(heap_value); + } + + // Scan the list of removed loads to see if we can reuse `type_conversion`, if + // the other removed load has the same substitute and type and is dominated + // by `type_conversioni`. + void TryToReuseTypeConversion(HInstruction* type_conversion, size_t index) { + size_t size = removed_loads_.size(); + HInstruction* load = removed_loads_[index]; + HInstruction* substitute = substitute_instructions_for_loads_[index]; + for (size_t j = index + 1; j < size; j++) { + HInstruction* load2 = removed_loads_[j]; + HInstruction* substitute2 = substitute_instructions_for_loads_[j]; + if (load2 == nullptr) { + DCHECK(substitute2->IsTypeConversion()); + continue; + } + DCHECK(load2->IsInstanceFieldGet() || + load2->IsStaticFieldGet() || + load2->IsArrayGet()); + DCHECK(substitute2 != nullptr); + if (substitute2 == substitute && + load2->GetType() == load->GetType() && + type_conversion->GetBlock()->Dominates(load2->GetBlock()) && + // Don't share across irreducible loop headers. + // TODO: can be more fine-grained than this by testing each dominator. + (load2->GetBlock() == type_conversion->GetBlock() || + !GetGraph()->HasIrreducibleLoops())) { + // The removed_loads_ are added in reverse post order. + DCHECK(type_conversion->StrictlyDominates(load2)); + load2->ReplaceWith(type_conversion); + load2->GetBlock()->RemoveInstruction(load2); + removed_loads_[j] = nullptr; + substitute_instructions_for_loads_[j] = type_conversion; + } + } + } + // Remove recorded instructions that should be eliminated. void RemoveInstructions() { size_t size = removed_loads_.size(); DCHECK_EQ(size, substitute_instructions_for_loads_.size()); for (size_t i = 0; i < size; i++) { HInstruction* load = removed_loads_[i]; - DCHECK(load != nullptr); + if (load == nullptr) { + // The load has been handled in the scan for type conversion below. + DCHECK(substitute_instructions_for_loads_[i]->IsTypeConversion()); + continue; + } DCHECK(load->IsInstanceFieldGet() || load->IsStaticFieldGet() || load->IsArrayGet()); HInstruction* substitute = substitute_instructions_for_loads_[i]; DCHECK(substitute != nullptr); - // Keep tracing substitute till one that's not removed. - HInstruction* sub_sub = FindSubstitute(substitute); - while (sub_sub != substitute) { - substitute = sub_sub; - sub_sub = FindSubstitute(substitute); + // We proactively retrieve the substitute for a removed load, so + // a load that has a substitute should not be observed as a heap + // location value. + DCHECK_EQ(FindSubstitute(substitute), substitute); + + // The load expects to load the heap value as type load->GetType(). + // However the tracked heap value may not be of that type. An explicit + // type conversion may be needed. + // There are actually three types involved here: + // (1) tracked heap value's type (type A) + // (2) heap location (field or element)'s type (type B) + // (3) load's type (type C) + // We guarantee that type A stored as type B and then fetched out as + // type C is the same as casting from type A to type C directly, since + // type B and type C will have the same size which is guarenteed in + // HInstanceFieldGet/HStaticFieldGet/HArrayGet's SetType(). + // So we only need one type conversion from type A to type C. + HTypeConversion* type_conversion = AddTypeConversionIfNecessary( + load, substitute, load->GetType()); + if (type_conversion != nullptr) { + TryToReuseTypeConversion(type_conversion, i); + load->ReplaceWith(type_conversion); + substitute_instructions_for_loads_[i] = type_conversion; + } else { + load->ReplaceWith(substitute); } - load->ReplaceWith(substitute); load->GetBlock()->RemoveInstruction(load); } @@ -199,6 +290,12 @@ class LSEVisitor : public HGraphDelegateVisitor { if (predecessors.size() == 0) { return; } + if (block->IsExitBlock()) { + // Exit block doesn't really merge values since the control flow ends in + // its predecessors. Each predecessor needs to make sure stores are kept + // if necessary. + return; + } ScopedArenaVector& heap_values = heap_values_for_[block->GetBlockId()]; for (size_t i = 0; i < heap_values.size(); i++) { @@ -233,15 +330,23 @@ class LSEVisitor : public HGraphDelegateVisitor { } } - if (merged_value == kUnknownHeapValue || ref_info->IsSingletonAndNonRemovable()) { - // There are conflicting heap values from different predecessors, - // or the heap value may be needed after method return or deoptimization. - // Keep the last store in each predecessor since future loads cannot be eliminated. - for (HBasicBlock* predecessor : predecessors) { - ScopedArenaVector& pred_values = - heap_values_for_[predecessor->GetBlockId()]; - KeepIfIsStore(pred_values[i]); + if (ref_info->IsSingleton()) { + if (ref_info->IsSingletonAndNonRemovable() || + (merged_value == kUnknownHeapValue && + !block->IsSingleReturnOrReturnVoidAllowingPhis())) { + // The heap value may be needed after method return or deoptimization, + // or there are conflicting heap values from different predecessors and + // this block is not a single return, + // keep the last store in each predecessor since future loads may not + // be eliminated. + for (HBasicBlock* predecessor : predecessors) { + ScopedArenaVector& pred_values = + heap_values_for_[predecessor->GetBlockId()]; + KeepIfIsStore(pred_values[i]); + } } + } else { + // Currenctly we don't eliminate stores to non-singletons. } if ((merged_value == nullptr) || !from_all_predecessors) { @@ -302,19 +407,19 @@ class LSEVisitor : public HGraphDelegateVisitor { HInstruction* ref, size_t offset, HInstruction* index, + size_t vector_length, int16_t declaring_class_def_index) { HInstruction* original_ref = heap_location_collector_.HuntForOriginalReference(ref); ReferenceInfo* ref_info = heap_location_collector_.FindReferenceInfoOf(original_ref); size_t idx = heap_location_collector_.FindHeapLocationIndex( - ref_info, offset, index, declaring_class_def_index); + ref_info, offset, index, vector_length, declaring_class_def_index); DCHECK_NE(idx, HeapLocationCollector::kHeapLocationNotFound); ScopedArenaVector& heap_values = heap_values_for_[instruction->GetBlock()->GetBlockId()]; HInstruction* heap_value = heap_values[idx]; if (heap_value == kDefaultHeapValue) { HInstruction* constant = GetDefaultValue(instruction->GetType()); - removed_loads_.push_back(instruction); - substitute_instructions_for_loads_.push_back(constant); + AddRemovedLoad(instruction, constant); heap_values[idx] = constant; return; } @@ -327,6 +432,8 @@ class LSEVisitor : public HGraphDelegateVisitor { DCHECK(ref_info->IsSingleton()); // Get the real heap value of the store. heap_value = heap_value->IsInstanceFieldSet() ? store->InputAt(1) : store->InputAt(2); + // heap_value may already have a substitute. + heap_value = FindSubstitute(heap_value); } } if (heap_value == kUnknownHeapValue) { @@ -347,8 +454,7 @@ class LSEVisitor : public HGraphDelegateVisitor { } return; } - removed_loads_.push_back(instruction); - substitute_instructions_for_loads_.push_back(heap_value); + AddRemovedLoad(instruction, heap_value); TryRemovingNullCheck(instruction); } } @@ -367,12 +473,15 @@ class LSEVisitor : public HGraphDelegateVisitor { HInstruction* ref, size_t offset, HInstruction* index, + size_t vector_length, int16_t declaring_class_def_index, HInstruction* value) { + // value may already have a substitute. + value = FindSubstitute(value); HInstruction* original_ref = heap_location_collector_.HuntForOriginalReference(ref); ReferenceInfo* ref_info = heap_location_collector_.FindReferenceInfoOf(original_ref); size_t idx = heap_location_collector_.FindHeapLocationIndex( - ref_info, offset, index, declaring_class_def_index); + ref_info, offset, index, vector_length, declaring_class_def_index); DCHECK_NE(idx, HeapLocationCollector::kHeapLocationNotFound); ScopedArenaVector& heap_values = heap_values_for_[instruction->GetBlock()->GetBlockId()]; @@ -382,8 +491,10 @@ class LSEVisitor : public HGraphDelegateVisitor { if (Equal(heap_value, value)) { // Store into the heap location with the same value. same_value = true; - } else if (index != nullptr && ref_info->HasIndexAliasing()) { - // For array element, don't eliminate stores if the index can be aliased. + } else if (index != nullptr && + heap_location_collector_.GetHeapLocation(idx)->HasAliasedLocations()) { + // For array element, don't eliminate stores if the location can be aliased + // (due to either ref or index aliasing). } else if (ref_info->IsSingleton()) { // Store into a field/element of a singleton. The value cannot be killed due to // aliasing/invocation. It can be redundant since future loads can @@ -446,7 +557,12 @@ class LSEVisitor : public HGraphDelegateVisitor { HInstruction* obj = instruction->InputAt(0); size_t offset = instruction->GetFieldInfo().GetFieldOffset().SizeValue(); int16_t declaring_class_def_index = instruction->GetFieldInfo().GetDeclaringClassDefIndex(); - VisitGetLocation(instruction, obj, offset, nullptr, declaring_class_def_index); + VisitGetLocation(instruction, + obj, + offset, + nullptr, + HeapLocation::kScalar, + declaring_class_def_index); } void VisitInstanceFieldSet(HInstanceFieldSet* instruction) OVERRIDE { @@ -454,14 +570,25 @@ class LSEVisitor : public HGraphDelegateVisitor { size_t offset = instruction->GetFieldInfo().GetFieldOffset().SizeValue(); int16_t declaring_class_def_index = instruction->GetFieldInfo().GetDeclaringClassDefIndex(); HInstruction* value = instruction->InputAt(1); - VisitSetLocation(instruction, obj, offset, nullptr, declaring_class_def_index, value); + VisitSetLocation(instruction, + obj, + offset, + nullptr, + HeapLocation::kScalar, + declaring_class_def_index, + value); } void VisitStaticFieldGet(HStaticFieldGet* instruction) OVERRIDE { HInstruction* cls = instruction->InputAt(0); size_t offset = instruction->GetFieldInfo().GetFieldOffset().SizeValue(); int16_t declaring_class_def_index = instruction->GetFieldInfo().GetDeclaringClassDefIndex(); - VisitGetLocation(instruction, cls, offset, nullptr, declaring_class_def_index); + VisitGetLocation(instruction, + cls, + offset, + nullptr, + HeapLocation::kScalar, + declaring_class_def_index); } void VisitStaticFieldSet(HStaticFieldSet* instruction) OVERRIDE { @@ -469,7 +596,13 @@ class LSEVisitor : public HGraphDelegateVisitor { size_t offset = instruction->GetFieldInfo().GetFieldOffset().SizeValue(); int16_t declaring_class_def_index = instruction->GetFieldInfo().GetDeclaringClassDefIndex(); HInstruction* value = instruction->InputAt(1); - VisitSetLocation(instruction, cls, offset, nullptr, declaring_class_def_index, value); + VisitSetLocation(instruction, + cls, + offset, + nullptr, + HeapLocation::kScalar, + declaring_class_def_index, + value); } void VisitArrayGet(HArrayGet* instruction) OVERRIDE { @@ -479,6 +612,7 @@ class LSEVisitor : public HGraphDelegateVisitor { array, HeapLocation::kInvalidFieldOffset, index, + HeapLocation::kScalar, HeapLocation::kDeclaringClassDefIndexForArrays); } @@ -490,6 +624,7 @@ class LSEVisitor : public HGraphDelegateVisitor { array, HeapLocation::kInvalidFieldOffset, index, + HeapLocation::kScalar, HeapLocation::kDeclaringClassDefIndexForArrays, value); } @@ -521,15 +656,46 @@ class LSEVisitor : public HGraphDelegateVisitor { } } - void HandleInvoke(HInstruction* invoke) { + // Keep necessary stores before exiting a method via return/throw. + void HandleExit(HBasicBlock* block) { + const ScopedArenaVector& heap_values = + heap_values_for_[block->GetBlockId()]; + for (size_t i = 0; i < heap_values.size(); i++) { + HInstruction* heap_value = heap_values[i]; + ReferenceInfo* ref_info = heap_location_collector_.GetHeapLocation(i)->GetReferenceInfo(); + if (!ref_info->IsSingletonAndRemovable()) { + KeepIfIsStore(heap_value); + } + } + } + + void VisitReturn(HReturn* instruction) OVERRIDE { + HandleExit(instruction->GetBlock()); + } + + void VisitReturnVoid(HReturnVoid* return_void) OVERRIDE { + HandleExit(return_void->GetBlock()); + } + + void VisitThrow(HThrow* throw_instruction) OVERRIDE { + HandleExit(throw_instruction->GetBlock()); + } + + void HandleInvoke(HInstruction* instruction) { + SideEffects side_effects = instruction->GetSideEffects(); ScopedArenaVector& heap_values = - heap_values_for_[invoke->GetBlock()->GetBlockId()]; + heap_values_for_[instruction->GetBlock()->GetBlockId()]; for (size_t i = 0; i < heap_values.size(); i++) { ReferenceInfo* ref_info = heap_location_collector_.GetHeapLocation(i)->GetReferenceInfo(); if (ref_info->IsSingleton()) { // Singleton references cannot be seen by the callee. } else { - heap_values[i] = kUnknownHeapValue; + if (side_effects.DoesAnyRead()) { + KeepIfIsStore(heap_values[i]); + } + if (side_effects.DoesAnyWrite()) { + heap_values[i] = kUnknownHeapValue; + } } } } @@ -606,18 +772,6 @@ class LSEVisitor : public HGraphDelegateVisitor { } } - // Find an instruction's substitute if it should be removed. - // Return the same instruction if it should not be removed. - HInstruction* FindSubstitute(HInstruction* instruction) { - size_t size = removed_loads_.size(); - for (size_t i = 0; i < size; i++) { - if (removed_loads_[i] == instruction) { - return substitute_instructions_for_loads_[i]; - } - } - return instruction; - } - const HeapLocationCollector& heap_location_collector_; const SideEffectsAnalysis& side_effects_; diff --git a/compiler/optimizing/load_store_elimination.h b/compiler/optimizing/load_store_elimination.h index 20a8a769c037c7ad761bd7b00c20171dd3ad6bc7..7153541bafc2a904de55f98de00e80502b1079e4 100644 --- a/compiler/optimizing/load_store_elimination.h +++ b/compiler/optimizing/load_store_elimination.h @@ -29,8 +29,9 @@ class LoadStoreElimination : public HOptimization { LoadStoreElimination(HGraph* graph, const SideEffectsAnalysis& side_effects, const LoadStoreAnalysis& lsa, - OptimizingCompilerStats* stats) - : HOptimization(graph, kLoadStoreEliminationPassName, stats), + OptimizingCompilerStats* stats, + const char* name = kLoadStoreEliminationPassName) + : HOptimization(graph, name, stats), side_effects_(side_effects), lsa_(lsa) {} diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc index 69c58275b40c02ac832081bb0e4eb78e40652732..3dc1ef7534c625a0f003dcab93ab5119dc5083e2 100644 --- a/compiler/optimizing/loop_optimization.cc +++ b/compiler/optimizing/loop_optimization.cc @@ -448,8 +448,9 @@ static bool CheckInductionSetFullyRemoved(ScopedArenaSet* iset) { HLoopOptimization::HLoopOptimization(HGraph* graph, CompilerDriver* compiler_driver, HInductionVarAnalysis* induction_analysis, - OptimizingCompilerStats* stats) - : HOptimization(graph, kLoopOptimizationPassName, stats), + OptimizingCompilerStats* stats, + const char* name) + : HOptimization(graph, name, stats), compiler_driver_(compiler_driver), induction_range_(induction_analysis), loop_allocator_(nullptr), @@ -1512,17 +1513,17 @@ bool HLoopOptimization::TrySetVectorType(DataType::Type type, uint64_t* restrict case DataType::Type::kBool: case DataType::Type::kUint8: case DataType::Type::kInt8: - *restrictions |= kNoDiv | kNoReduction | kNoSAD; + *restrictions |= kNoDiv; return TrySetVectorLength(16); case DataType::Type::kUint16: case DataType::Type::kInt16: - *restrictions |= kNoDiv | kNoStringCharAt | kNoReduction | kNoSAD; + *restrictions |= kNoDiv | kNoStringCharAt; return TrySetVectorLength(8); case DataType::Type::kInt32: - *restrictions |= kNoDiv | kNoSAD; + *restrictions |= kNoDiv; return TrySetVectorLength(4); case DataType::Type::kInt64: - *restrictions |= kNoDiv | kNoSAD; + *restrictions |= kNoDiv; return TrySetVectorLength(2); case DataType::Type::kFloat32: *restrictions |= kNoMinMax | kNoReduction; // min/max(x, NaN) @@ -1541,17 +1542,17 @@ bool HLoopOptimization::TrySetVectorType(DataType::Type type, uint64_t* restrict case DataType::Type::kBool: case DataType::Type::kUint8: case DataType::Type::kInt8: - *restrictions |= kNoDiv | kNoReduction | kNoSAD; + *restrictions |= kNoDiv; return TrySetVectorLength(16); case DataType::Type::kUint16: case DataType::Type::kInt16: - *restrictions |= kNoDiv | kNoStringCharAt | kNoReduction | kNoSAD; + *restrictions |= kNoDiv | kNoStringCharAt; return TrySetVectorLength(8); case DataType::Type::kInt32: - *restrictions |= kNoDiv | kNoSAD; + *restrictions |= kNoDiv; return TrySetVectorLength(4); case DataType::Type::kInt64: - *restrictions |= kNoDiv | kNoSAD; + *restrictions |= kNoDiv; return TrySetVectorLength(2); case DataType::Type::kFloat32: *restrictions |= kNoMinMax | kNoReduction; // min/max(x, NaN) @@ -1748,7 +1749,8 @@ void HLoopOptimization::GenerateVecReductionPhiInputs(HPhi* phi, HInstruction* r HInstruction* HLoopOptimization::ReduceAndExtractIfNeeded(HInstruction* instruction) { if (instruction->IsPhi()) { HInstruction* input = instruction->InputAt(1); - if (input->IsVecOperation() && !input->IsVecExtractScalar()) { + if (HVecOperation::ReturnsSIMDValue(input)) { + DCHECK(!input->IsPhi()); HVecOperation* input_vector = input->AsVecOperation(); uint32_t vector_length = input_vector->GetVectorLength(); DataType::Type type = input_vector->GetPackedType(); diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h index 51e0a986b826c49c93e3d693585454663b920a5b..a707ad13580fc341de6ed5d1eb4327822189b146 100644 --- a/compiler/optimizing/loop_optimization.h +++ b/compiler/optimizing/loop_optimization.h @@ -37,7 +37,8 @@ class HLoopOptimization : public HOptimization { HLoopOptimization(HGraph* graph, CompilerDriver* compiler_driver, HInductionVarAnalysis* induction_analysis, - OptimizingCompilerStats* stats); + OptimizingCompilerStats* stats, + const char* name = kLoopOptimizationPassName); void Run() OVERRIDE; diff --git a/compiler/optimizing/loop_optimization_test.cc b/compiler/optimizing/loop_optimization_test.cc index 4e1857df5b572263fd8a910afee7c57f4148c3cd..db8368986c96a66aaa9bb920295055005ef3e9ec 100644 --- a/compiler/optimizing/loop_optimization_test.cc +++ b/compiler/optimizing/loop_optimization_test.cc @@ -194,7 +194,9 @@ TEST_F(LoopOptimizationTest, LoopNestWithSequence) { // Check that SimplifyLoop() doesn't invalidate data flow when ordering loop headers' // predecessors. -TEST_F(LoopOptimizationTest, SimplifyLoop) { +// +// This is a test for nodes.cc functionality - HGraph::SimplifyLoop. +TEST_F(LoopOptimizationTest, SimplifyLoopReoderPredecessors) { // Can't use AddLoop as we want special order for blocks predecessors. HBasicBlock* header = new (GetAllocator()) HBasicBlock(graph_); HBasicBlock* body = new (GetAllocator()) HBasicBlock(graph_); @@ -232,4 +234,83 @@ TEST_F(LoopOptimizationTest, SimplifyLoop) { ASSERT_TRUE(input->GetBlock()->Dominates(header->GetPredecessors()[i])); } } + +// Test that SimplifyLoop() processes the multiple-preheaders loops correctly. +// +// This is a test for nodes.cc functionality - HGraph::SimplifyLoop. +TEST_F(LoopOptimizationTest, SimplifyLoopSinglePreheader) { + HBasicBlock* header = AddLoop(entry_block_, return_block_); + + header->InsertInstructionBefore( + new (GetAllocator()) HSuspendCheck(), header->GetLastInstruction()); + + // Insert an if construct before the loop so it will have two preheaders. + HBasicBlock* if_block = new (GetAllocator()) HBasicBlock(graph_); + HBasicBlock* preheader0 = new (GetAllocator()) HBasicBlock(graph_); + HBasicBlock* preheader1 = new (GetAllocator()) HBasicBlock(graph_); + + graph_->AddBlock(if_block); + graph_->AddBlock(preheader0); + graph_->AddBlock(preheader1); + + // Fix successors/predecessors. + entry_block_->ReplaceSuccessor(header, if_block); + if_block->AddSuccessor(preheader0); + if_block->AddSuccessor(preheader1); + preheader0->AddSuccessor(header); + preheader1->AddSuccessor(header); + + if_block->AddInstruction(new (GetAllocator()) HIf(parameter_)); + preheader0->AddInstruction(new (GetAllocator()) HGoto()); + preheader1->AddInstruction(new (GetAllocator()) HGoto()); + + HBasicBlock* body = header->GetSuccessors()[0]; + DCHECK(body != return_block_); + + // Add some data flow. + HIntConstant* const_0 = graph_->GetIntConstant(0); + HIntConstant* const_1 = graph_->GetIntConstant(1); + HIntConstant* const_2 = graph_->GetIntConstant(2); + + HAdd* preheader0_add = new (GetAllocator()) HAdd(DataType::Type::kInt32, parameter_, const_0); + preheader0->AddInstruction(preheader0_add); + HAdd* preheader1_add = new (GetAllocator()) HAdd(DataType::Type::kInt32, parameter_, const_1); + preheader1->AddInstruction(preheader1_add); + + HPhi* header_phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32); + header->AddPhi(header_phi); + + HAdd* body_add = new (GetAllocator()) HAdd(DataType::Type::kInt32, parameter_, const_2); + body->AddInstruction(body_add); + + DCHECK(header->GetPredecessors()[0] == body); + DCHECK(header->GetPredecessors()[1] == preheader0); + DCHECK(header->GetPredecessors()[2] == preheader1); + + header_phi->AddInput(body_add); + header_phi->AddInput(preheader0_add); + header_phi->AddInput(preheader1_add); + + graph_->ClearLoopInformation(); + graph_->ClearDominanceInformation(); + graph_->BuildDominatorTree(); + + EXPECT_EQ(header->GetPredecessors().size(), 2u); + EXPECT_EQ(header->GetPredecessors()[1], body); + + HBasicBlock* new_preheader = header->GetLoopInformation()->GetPreHeader(); + EXPECT_EQ(preheader0->GetSingleSuccessor(), new_preheader); + EXPECT_EQ(preheader1->GetSingleSuccessor(), new_preheader); + + EXPECT_EQ(new_preheader->GetPhis().CountSize(), 1u); + HPhi* new_preheader_phi = new_preheader->GetFirstPhi()->AsPhi(); + EXPECT_EQ(new_preheader_phi->InputCount(), 2u); + EXPECT_EQ(new_preheader_phi->InputAt(0), preheader0_add); + EXPECT_EQ(new_preheader_phi->InputAt(1), preheader1_add); + + EXPECT_EQ(header_phi->InputCount(), 2u); + EXPECT_EQ(header_phi->InputAt(0), new_preheader_phi); + EXPECT_EQ(header_phi->InputAt(1), body_add); +} + } // namespace art diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index f4f6434678caade5c454afdee7484615bc11ec54..727431a49379731a0b69cb4808f550338802cc6e 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -397,6 +397,117 @@ void HGraph::OrderLoopHeaderPredecessors(HBasicBlock* header) { } } +// Transform control flow of the loop to a single preheader format (don't touch the data flow). +// New_preheader can be already among the header predecessors - this situation will be correctly +// processed. +static void FixControlForNewSinglePreheader(HBasicBlock* header, HBasicBlock* new_preheader) { + HLoopInformation* loop_info = header->GetLoopInformation(); + for (size_t pred = 0; pred < header->GetPredecessors().size(); ++pred) { + HBasicBlock* predecessor = header->GetPredecessors()[pred]; + if (!loop_info->IsBackEdge(*predecessor) && predecessor != new_preheader) { + predecessor->ReplaceSuccessor(header, new_preheader); + pred--; + } + } +} + +// == Before == == After == +// _________ _________ _________ _________ +// | B0 | | B1 | (old preheaders) | B0 | | B1 | +// |=========| |=========| |=========| |=========| +// | i0 = .. | | i1 = .. | | i0 = .. | | i1 = .. | +// |_________| |_________| |_________| |_________| +// \ / \ / +// \ / ___v____________v___ +// \ / (new preheader) | B20 <- B0, B1 | +// | | |====================| +// | | | i20 = phi(i0, i1) | +// | | |____________________| +// | | | +// /\ | | /\ /\ | /\ +// / v_______v_________v_______v \ / v___________v_____________v \ +// | | B10 <- B0, B1, B2, B3 | | | | B10 <- B20, B2, B3 | | +// | |===========================| | (header) | |===========================| | +// | | i10 = phi(i0, i1, i2, i3) | | | | i10 = phi(i20, i2, i3) | | +// | |___________________________| | | |___________________________| | +// | / \ | | / \ | +// | ... ... | | ... ... | +// | _________ _________ | | _________ _________ | +// | | B2 | | B3 | | | | B2 | | B3 | | +// | |=========| |=========| | (back edges) | |=========| |=========| | +// | | i2 = .. | | i3 = .. | | | | i2 = .. | | i3 = .. | | +// | |_________| |_________| | | |_________| |_________| | +// \ / \ / \ / \ / +// \___/ \___/ \___/ \___/ +// +void HGraph::TransformLoopToSinglePreheaderFormat(HBasicBlock* header) { + HLoopInformation* loop_info = header->GetLoopInformation(); + + HBasicBlock* preheader = new (allocator_) HBasicBlock(this, header->GetDexPc()); + AddBlock(preheader); + preheader->AddInstruction(new (allocator_) HGoto(header->GetDexPc())); + + // If the old header has no Phis then we only need to fix the control flow. + if (header->GetPhis().IsEmpty()) { + FixControlForNewSinglePreheader(header, preheader); + preheader->AddSuccessor(header); + return; + } + + // Find the first non-back edge block in the header's predecessors list. + size_t first_nonbackedge_pred_pos = 0; + bool found = false; + for (size_t pred = 0; pred < header->GetPredecessors().size(); ++pred) { + HBasicBlock* predecessor = header->GetPredecessors()[pred]; + if (!loop_info->IsBackEdge(*predecessor)) { + first_nonbackedge_pred_pos = pred; + found = true; + break; + } + } + + DCHECK(found); + + // Fix the data-flow. + for (HInstructionIterator it(header->GetPhis()); !it.Done(); it.Advance()) { + HPhi* header_phi = it.Current()->AsPhi(); + + HPhi* preheader_phi = new (GetAllocator()) HPhi(GetAllocator(), + header_phi->GetRegNumber(), + 0, + header_phi->GetType()); + if (header_phi->GetType() == DataType::Type::kReference) { + preheader_phi->SetReferenceTypeInfo(header_phi->GetReferenceTypeInfo()); + } + preheader->AddPhi(preheader_phi); + + HInstruction* orig_input = header_phi->InputAt(first_nonbackedge_pred_pos); + header_phi->ReplaceInput(preheader_phi, first_nonbackedge_pred_pos); + preheader_phi->AddInput(orig_input); + + for (size_t input_pos = first_nonbackedge_pred_pos + 1; + input_pos < header_phi->InputCount(); + input_pos++) { + HInstruction* input = header_phi->InputAt(input_pos); + HBasicBlock* pred_block = header->GetPredecessors()[input_pos]; + + if (loop_info->Contains(*pred_block)) { + DCHECK(loop_info->IsBackEdge(*pred_block)); + } else { + preheader_phi->AddInput(input); + header_phi->RemoveInputAt(input_pos); + input_pos--; + } + } + } + + // Fix the control-flow. + HBasicBlock* first_pred = header->GetPredecessors()[first_nonbackedge_pred_pos]; + preheader->InsertBetween(first_pred, header); + + FixControlForNewSinglePreheader(header, preheader); +} + void HGraph::SimplifyLoop(HBasicBlock* header) { HLoopInformation* info = header->GetLoopInformation(); @@ -406,18 +517,7 @@ void HGraph::SimplifyLoop(HBasicBlock* header) { // this graph. size_t number_of_incomings = header->GetPredecessors().size() - info->NumberOfBackEdges(); if (number_of_incomings != 1 || (GetEntryBlock()->GetSingleSuccessor() == header)) { - HBasicBlock* pre_header = new (allocator_) HBasicBlock(this, header->GetDexPc()); - AddBlock(pre_header); - pre_header->AddInstruction(new (allocator_) HGoto(header->GetDexPc())); - - for (size_t pred = 0; pred < header->GetPredecessors().size(); ++pred) { - HBasicBlock* predecessor = header->GetPredecessors()[pred]; - if (!info->IsBackEdge(*predecessor)) { - predecessor->ReplaceSuccessor(header, pre_header); - pred--; - } - } - pre_header->AddSuccessor(header); + TransformLoopToSinglePreheaderFormat(header); } OrderLoopHeaderPredecessors(header); @@ -507,6 +607,7 @@ GraphAnalysisResult HGraph::AnalyzeLoops() const { if (block->IsCatchBlock()) { // TODO: Dealing with exceptional back edges could be tricky because // they only approximate the real control flow. Bail out for now. + VLOG(compiler) << "Not compiled: Exceptional back edges"; return kAnalysisFailThrowCatchLoop; } block->GetLoopInformation()->Populate(); @@ -845,6 +946,13 @@ static void UpdateInputsUsers(HInstruction* instruction) { DCHECK(!instruction->HasEnvironment()); } +void HBasicBlock::ReplaceAndRemovePhiWith(HPhi* initial, HPhi* replacement) { + DCHECK(initial->GetBlock() == this); + InsertPhiAfter(replacement, initial); + initial->ReplaceWith(replacement); + RemovePhi(initial); +} + void HBasicBlock::ReplaceAndRemoveInstructionWith(HInstruction* initial, HInstruction* replacement) { DCHECK(initial->GetBlock() == this); @@ -1396,6 +1504,14 @@ HConstant* HTypeConversion::TryStaticEvaluation() const { if (GetInput()->IsIntConstant()) { int32_t value = GetInput()->AsIntConstant()->GetValue(); switch (GetResultType()) { + case DataType::Type::kInt8: + return graph->GetIntConstant(static_cast(value), GetDexPc()); + case DataType::Type::kUint8: + return graph->GetIntConstant(static_cast(value), GetDexPc()); + case DataType::Type::kInt16: + return graph->GetIntConstant(static_cast(value), GetDexPc()); + case DataType::Type::kUint16: + return graph->GetIntConstant(static_cast(value), GetDexPc()); case DataType::Type::kInt64: return graph->GetLongConstant(static_cast(value), GetDexPc()); case DataType::Type::kFloat32: @@ -1408,6 +1524,14 @@ HConstant* HTypeConversion::TryStaticEvaluation() const { } else if (GetInput()->IsLongConstant()) { int64_t value = GetInput()->AsLongConstant()->GetValue(); switch (GetResultType()) { + case DataType::Type::kInt8: + return graph->GetIntConstant(static_cast(value), GetDexPc()); + case DataType::Type::kUint8: + return graph->GetIntConstant(static_cast(value), GetDexPc()); + case DataType::Type::kInt16: + return graph->GetIntConstant(static_cast(value), GetDexPc()); + case DataType::Type::kUint16: + return graph->GetIntConstant(static_cast(value), GetDexPc()); case DataType::Type::kInt32: return graph->GetIntConstant(static_cast(value), GetDexPc()); case DataType::Type::kFloat32: @@ -1810,6 +1934,11 @@ bool HBasicBlock::IsSingleReturn() const { return HasOnlyOneInstruction(*this) && GetLastInstruction()->IsReturn(); } +bool HBasicBlock::IsSingleReturnOrReturnVoidAllowingPhis() const { + return (GetFirstInstruction() == GetLastInstruction()) && + (GetLastInstruction()->IsReturn() || GetLastInstruction()->IsReturnVoid()); +} + bool HBasicBlock::IsSingleTryBoundary() const { return HasOnlyOneInstruction(*this) && GetLastInstruction()->IsTryBoundary(); } @@ -2802,21 +2931,6 @@ bool HLoadClass::InstructionDataEquals(const HInstruction* other) const { } } -void HLoadClass::SetLoadKind(LoadKind load_kind) { - SetPackedField(load_kind); - - if (load_kind != LoadKind::kRuntimeCall && - load_kind != LoadKind::kReferrersClass) { - RemoveAsUserOfInput(0u); - SetRawInputAt(0u, nullptr); - } - - if (!NeedsEnvironment()) { - RemoveEnvironment(); - SetSideEffects(SideEffects::None()); - } -} - std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs) { switch (rhs) { case HLoadClass::LoadKind::kReferrersClass: @@ -2859,21 +2973,6 @@ bool HLoadString::InstructionDataEquals(const HInstruction* other) const { } } -void HLoadString::SetLoadKind(LoadKind load_kind) { - // Once sharpened, the load kind should not be changed again. - DCHECK_EQ(GetLoadKind(), LoadKind::kRuntimeCall); - SetPackedField(load_kind); - - if (load_kind != LoadKind::kRuntimeCall) { - RemoveAsUserOfInput(0u); - SetRawInputAt(0u, nullptr); - } - if (!NeedsEnvironment()) { - RemoveEnvironment(); - SetSideEffects(SideEffects::None()); - } -} - std::ostream& operator<<(std::ostream& os, HLoadString::LoadKind rhs) { switch (rhs) { case HLoadString::LoadKind::kBootImageLinkTimePcRelative: @@ -2902,6 +3001,28 @@ void HInstruction::RemoveEnvironmentUsers() { env_uses_.clear(); } +HInstruction* ReplaceInstrOrPhiByClone(HInstruction* instr) { + HInstruction* clone = instr->Clone(instr->GetBlock()->GetGraph()->GetAllocator()); + HBasicBlock* block = instr->GetBlock(); + + if (instr->IsPhi()) { + HPhi* phi = instr->AsPhi(); + DCHECK(!phi->HasEnvironment()); + HPhi* phi_clone = clone->AsPhi(); + block->ReplaceAndRemovePhiWith(phi, phi_clone); + } else { + block->ReplaceAndRemoveInstructionWith(instr, clone); + if (instr->HasEnvironment()) { + clone->CopyEnvironmentFrom(instr->GetEnvironment()); + HLoopInformation* loop_info = block->GetLoopInformation(); + if (instr->IsSuspendCheck() && loop_info != nullptr) { + loop_info->SetSuspendCheck(clone->AsSuspendCheck()); + } + } + } + return clone; +} + // Returns an instruction with the opposite Boolean value from 'cond'. HInstruction* HGraph::InsertOppositeCondition(HInstruction* cond, HInstruction* cursor) { ArenaAllocator* allocator = GetAllocator(); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 29c78a1e34a2448b0743c045311eeea2b692b9c8..d13f5b42bfecdbbbda2867b11e9eac02e834ade6 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -423,6 +423,17 @@ class HGraph : public ArenaObject { void SplitCriticalEdge(HBasicBlock* block, HBasicBlock* successor); void OrderLoopHeaderPredecessors(HBasicBlock* header); + + // Transform a loop into a format with a single preheader. + // + // Each phi in the header should be split: original one in the header should only hold + // inputs reachable from the back edges and a single input from the preheader. The newly created + // phi in the preheader should collate the inputs from the original multiple incoming blocks. + // + // Loops in the graph typically have a single preheader, so this method is used to "repair" loops + // that no longer have this property. + void TransformLoopToSinglePreheaderFormat(HBasicBlock* header); + void SimplifyLoop(HBasicBlock* header); int32_t GetNextInstructionId() { @@ -968,6 +979,7 @@ class HBasicBlock : public ArenaObject { bool IsSingleGoto() const; bool IsSingleReturn() const; + bool IsSingleReturnOrReturnVoidAllowingPhis() const; bool IsSingleTryBoundary() const; // Returns true if this block emits nothing but a jump. @@ -1159,6 +1171,8 @@ class HBasicBlock : public ArenaObject { // Insert `instruction` before/after an existing instruction `cursor`. void InsertInstructionBefore(HInstruction* instruction, HInstruction* cursor); void InsertInstructionAfter(HInstruction* instruction, HInstruction* cursor); + // Replace phi `initial` with `replacement` within this block. + void ReplaceAndRemovePhiWith(HPhi* initial, HPhi* replacement); // Replace instruction `initial` with `replacement` within this block. void ReplaceAndRemoveInstructionWith(HInstruction* initial, HInstruction* replacement); @@ -1479,18 +1493,31 @@ FOR_EACH_INSTRUCTION(FORWARD_DECLARATION) #undef FORWARD_DECLARATION #define DECLARE_INSTRUCTION(type) \ + private: \ + H##type& operator=(const H##type&) = delete; \ + public: \ InstructionKind GetKindInternal() const OVERRIDE { return k##type; } \ const char* DebugName() const OVERRIDE { return #type; } \ bool InstructionTypeEquals(const HInstruction* other) const OVERRIDE { \ return other->Is##type(); \ } \ + HInstruction* Clone(ArenaAllocator* arena) const OVERRIDE { \ + DCHECK(IsClonable()); \ + return new (arena) H##type(*this->As##type()); \ + } \ void Accept(HGraphVisitor* visitor) OVERRIDE #define DECLARE_ABSTRACT_INSTRUCTION(type) \ + private: \ + H##type& operator=(const H##type&) = delete; \ + public: \ bool Is##type() const { return As##type() != nullptr; } \ const H##type* As##type() const { return this; } \ H##type* As##type() { return this; } +#define DEFAULT_COPY_CONSTRUCTOR(type) \ + explicit H##type(const H##type& other) = default; + template class HUseListNode : public ArenaObject, public IntrusiveForwardListNode> { @@ -2181,6 +2208,25 @@ class HInstruction : public ArenaObject { FOR_EACH_ABSTRACT_INSTRUCTION(INSTRUCTION_TYPE_CHECK) #undef INSTRUCTION_TYPE_CHECK + // Return a clone of the instruction if it is clonable (shallow copy by default, custom copy + // if a custom copy-constructor is provided for a particular type). If IsClonable() is false for + // the instruction then the behaviour of this function is undefined. + // + // Note: It is semantically valid to create a clone of the instruction only until + // prepare_for_register_allocator phase as lifetime, intervals and codegen info are not + // copied. + // + // Note: HEnvironment and some other fields are not copied and are set to default values, see + // 'explicit HInstruction(const HInstruction& other)' for details. + virtual HInstruction* Clone(ArenaAllocator* arena ATTRIBUTE_UNUSED) const { + LOG(FATAL) << "Cloning is not implemented for the instruction " << + DebugName() << " " << GetId(); + UNREACHABLE(); + } + + // Return whether instruction can be cloned (copied). + virtual bool IsClonable() const { return false; } + // Returns whether the instruction can be moved within the graph. // TODO: this method is used by LICM and GVN with possibly different // meanings? split and rename? @@ -2297,6 +2343,30 @@ class HInstruction : public ArenaObject { packed_fields_ = BitFieldType::Update(value, packed_fields_); } + // Copy construction for the instruction (used for Clone function). + // + // Fields (e.g. lifetime, intervals and codegen info) associated with phases starting from + // prepare_for_register_allocator are not copied (set to default values). + // + // Copy constructors must be provided for every HInstruction type; default copy constructor is + // fine for most of them. However for some of the instructions a custom copy constructor must be + // specified (when instruction has non-trivially copyable fields and must have a special behaviour + // for copying them). + explicit HInstruction(const HInstruction& other) + : previous_(nullptr), + next_(nullptr), + block_(nullptr), + dex_pc_(other.dex_pc_), + id_(-1), + ssa_index_(-1), + packed_fields_(other.packed_fields_), + environment_(nullptr), + locations_(nullptr), + live_interval_(nullptr), + lifetime_position_(kNoLifetime), + side_effects_(other.side_effects_), + reference_type_handle_(other.reference_type_handle_) {} + private: void FixUpUserRecordsAfterUseInsertion(HUseList::iterator fixup_end) { auto before_use_node = uses_.before_begin(); @@ -2386,8 +2456,6 @@ class HInstruction : public ArenaObject { friend class HEnvironment; friend class HGraph; friend class HInstructionList; - - DISALLOW_COPY_AND_ASSIGN(HInstruction); }; std::ostream& operator<<(std::ostream& os, const HInstruction::InstructionKind& rhs); @@ -2483,10 +2551,9 @@ class HVariableInputSizeInstruction : public HInstruction { : HInstruction(side_effects, dex_pc), inputs_(number_of_inputs, allocator->Adapter(kind)) {} - ArenaVector> inputs_; + DEFAULT_COPY_CONSTRUCTOR(VariableInputSizeInstruction); - private: - DISALLOW_COPY_AND_ASSIGN(HVariableInputSizeInstruction); + ArenaVector> inputs_; }; template @@ -2501,6 +2568,9 @@ class HTemplateInstruction: public HInstruction { return ArrayRef>(inputs_); } + protected: + DEFAULT_COPY_CONSTRUCTOR(TemplateInstruction); + private: std::array, N> inputs_; @@ -2521,6 +2591,9 @@ class HTemplateInstruction<0>: public HInstruction { return ArrayRef>(); } + protected: + DEFAULT_COPY_CONSTRUCTOR(TemplateInstruction<0>); + private: friend class SsaBuilder; }; @@ -2546,6 +2619,7 @@ class HExpression : public HTemplateInstruction { static_assert(kNumberOfExpressionPackedBits <= HInstruction::kMaxNumberOfPackedBits, "Too many packed fields."); using TypeField = BitField; + DEFAULT_COPY_CONSTRUCTOR(Expression); }; // Represents dex's RETURN_VOID opcode. A HReturnVoid is a control flow @@ -2559,8 +2633,8 @@ class HReturnVoid FINAL : public HTemplateInstruction<0> { DECLARE_INSTRUCTION(ReturnVoid); - private: - DISALLOW_COPY_AND_ASSIGN(HReturnVoid); + protected: + DEFAULT_COPY_CONSTRUCTOR(ReturnVoid); }; // Represents dex's RETURN opcodes. A HReturn is a control flow @@ -2576,8 +2650,8 @@ class HReturn FINAL : public HTemplateInstruction<1> { DECLARE_INSTRUCTION(Return); - private: - DISALLOW_COPY_AND_ASSIGN(HReturn); + protected: + DEFAULT_COPY_CONSTRUCTOR(Return); }; class HPhi FINAL : public HVariableInputSizeInstruction { @@ -2603,6 +2677,8 @@ class HPhi FINAL : public HVariableInputSizeInstruction { SetPackedFlag(true); } + bool IsClonable() const OVERRIDE { return true; } + // Returns a type equivalent to the given `type`, but that a `HPhi` can hold. static DataType::Type ToPhiType(DataType::Type type) { return DataType::Kind(type); @@ -2665,6 +2741,9 @@ class HPhi FINAL : public HVariableInputSizeInstruction { DECLARE_INSTRUCTION(Phi); + protected: + DEFAULT_COPY_CONSTRUCTOR(Phi); + private: static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits; static constexpr size_t kFieldTypeSize = @@ -2676,8 +2755,6 @@ class HPhi FINAL : public HVariableInputSizeInstruction { using TypeField = BitField; const uint32_t reg_number_; - - DISALLOW_COPY_AND_ASSIGN(HPhi); }; // The exit instruction is the only instruction of the exit block. @@ -2691,8 +2768,8 @@ class HExit FINAL : public HTemplateInstruction<0> { DECLARE_INSTRUCTION(Exit); - private: - DISALLOW_COPY_AND_ASSIGN(HExit); + protected: + DEFAULT_COPY_CONSTRUCTOR(Exit); }; // Jumps from one block to another. @@ -2700,6 +2777,7 @@ class HGoto FINAL : public HTemplateInstruction<0> { public: explicit HGoto(uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::None(), dex_pc) {} + bool IsClonable() const OVERRIDE { return true; } bool IsControlFlow() const OVERRIDE { return true; } HBasicBlock* GetSuccessor() const { @@ -2708,8 +2786,8 @@ class HGoto FINAL : public HTemplateInstruction<0> { DECLARE_INSTRUCTION(Goto); - private: - DISALLOW_COPY_AND_ASSIGN(HGoto); + protected: + DEFAULT_COPY_CONSTRUCTOR(Goto); }; class HConstant : public HExpression<0> { @@ -2732,8 +2810,8 @@ class HConstant : public HExpression<0> { DECLARE_ABSTRACT_INSTRUCTION(Constant); - private: - DISALLOW_COPY_AND_ASSIGN(HConstant); + protected: + DEFAULT_COPY_CONSTRUCTOR(Constant); }; class HNullConstant FINAL : public HConstant { @@ -2751,12 +2829,14 @@ class HNullConstant FINAL : public HConstant { DECLARE_INSTRUCTION(NullConstant); + protected: + DEFAULT_COPY_CONSTRUCTOR(NullConstant); + private: explicit HNullConstant(uint32_t dex_pc = kNoDexPc) : HConstant(DataType::Type::kReference, dex_pc) {} friend class HGraph; - DISALLOW_COPY_AND_ASSIGN(HNullConstant); }; // Constants of the type int. Those can be from Dex instructions, or @@ -2788,6 +2868,9 @@ class HIntConstant FINAL : public HConstant { DECLARE_INSTRUCTION(IntConstant); + protected: + DEFAULT_COPY_CONSTRUCTOR(IntConstant); + private: explicit HIntConstant(int32_t value, uint32_t dex_pc = kNoDexPc) : HConstant(DataType::Type::kInt32, dex_pc), value_(value) {} @@ -2799,7 +2882,6 @@ class HIntConstant FINAL : public HConstant { friend class HGraph; ART_FRIEND_TEST(GraphTest, InsertInstructionBefore); ART_FRIEND_TYPED_TEST(ParallelMoveTest, ConstantLast); - DISALLOW_COPY_AND_ASSIGN(HIntConstant); }; class HLongConstant FINAL : public HConstant { @@ -2822,6 +2904,9 @@ class HLongConstant FINAL : public HConstant { DECLARE_INSTRUCTION(LongConstant); + protected: + DEFAULT_COPY_CONSTRUCTOR(LongConstant); + private: explicit HLongConstant(int64_t value, uint32_t dex_pc = kNoDexPc) : HConstant(DataType::Type::kInt64, dex_pc), value_(value) {} @@ -2829,7 +2914,6 @@ class HLongConstant FINAL : public HConstant { const int64_t value_; friend class HGraph; - DISALLOW_COPY_AND_ASSIGN(HLongConstant); }; class HFloatConstant FINAL : public HConstant { @@ -2871,6 +2955,9 @@ class HFloatConstant FINAL : public HConstant { DECLARE_INSTRUCTION(FloatConstant); + protected: + DEFAULT_COPY_CONSTRUCTOR(FloatConstant); + private: explicit HFloatConstant(float value, uint32_t dex_pc = kNoDexPc) : HConstant(DataType::Type::kFloat32, dex_pc), value_(value) {} @@ -2882,7 +2969,6 @@ class HFloatConstant FINAL : public HConstant { // Only the SsaBuilder and HGraph can create floating-point constants. friend class SsaBuilder; friend class HGraph; - DISALLOW_COPY_AND_ASSIGN(HFloatConstant); }; class HDoubleConstant FINAL : public HConstant { @@ -2922,6 +3008,9 @@ class HDoubleConstant FINAL : public HConstant { DECLARE_INSTRUCTION(DoubleConstant); + protected: + DEFAULT_COPY_CONSTRUCTOR(DoubleConstant); + private: explicit HDoubleConstant(double value, uint32_t dex_pc = kNoDexPc) : HConstant(DataType::Type::kFloat64, dex_pc), value_(value) {} @@ -2933,7 +3022,6 @@ class HDoubleConstant FINAL : public HConstant { // Only the SsaBuilder and HGraph can create floating-point constants. friend class SsaBuilder; friend class HGraph; - DISALLOW_COPY_AND_ASSIGN(HDoubleConstant); }; // Conditional branch. A block ending with an HIf instruction must have @@ -2945,6 +3033,7 @@ class HIf FINAL : public HTemplateInstruction<1> { SetRawInputAt(0, input); } + bool IsClonable() const OVERRIDE { return true; } bool IsControlFlow() const OVERRIDE { return true; } HBasicBlock* IfTrueSuccessor() const { @@ -2957,8 +3046,8 @@ class HIf FINAL : public HTemplateInstruction<1> { DECLARE_INSTRUCTION(If); - private: - DISALLOW_COPY_AND_ASSIGN(HIf); + protected: + DEFAULT_COPY_CONSTRUCTOR(If); }; @@ -3011,6 +3100,9 @@ class HTryBoundary FINAL : public HTemplateInstruction<0> { DECLARE_INSTRUCTION(TryBoundary); + protected: + DEFAULT_COPY_CONSTRUCTOR(TryBoundary); + private: static constexpr size_t kFieldBoundaryKind = kNumberOfGenericPackedBits; static constexpr size_t kFieldBoundaryKindSize = @@ -3020,8 +3112,6 @@ class HTryBoundary FINAL : public HTemplateInstruction<0> { static_assert(kNumberOfTryBoundaryPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); using BoundaryKindField = BitField; - - DISALLOW_COPY_AND_ASSIGN(HTryBoundary); }; // Deoptimize to interpreter, upon checking a condition. @@ -3044,6 +3134,8 @@ class HDeoptimize FINAL : public HVariableInputSizeInstruction { SetRawInputAt(0, cond); } + bool IsClonable() const OVERRIDE { return true; } + // Use this constructor when the `HDeoptimize` guards an instruction, and any user // that relies on the deoptimization to pass should have its input be the `HDeoptimize` // instead of `guard`. @@ -3097,6 +3189,9 @@ class HDeoptimize FINAL : public HVariableInputSizeInstruction { DECLARE_INSTRUCTION(Deoptimize); + protected: + DEFAULT_COPY_CONSTRUCTOR(Deoptimize); + private: static constexpr size_t kFieldCanBeMoved = kNumberOfGenericPackedBits; static constexpr size_t kFieldDeoptimizeKind = kNumberOfGenericPackedBits + 1; @@ -3108,8 +3203,6 @@ class HDeoptimize FINAL : public HVariableInputSizeInstruction { "Too many packed fields."); using DeoptimizeKindField = BitField; - - DISALLOW_COPY_AND_ASSIGN(HDeoptimize); }; // Represents a should_deoptimize flag. Currently used for CHA-based devirtualization. @@ -3135,8 +3228,8 @@ class HShouldDeoptimizeFlag FINAL : public HVariableInputSizeInstruction { DECLARE_INSTRUCTION(ShouldDeoptimizeFlag); - private: - DISALLOW_COPY_AND_ASSIGN(HShouldDeoptimizeFlag); + protected: + DEFAULT_COPY_CONSTRUCTOR(ShouldDeoptimizeFlag); }; // Represents the ArtMethod that was passed as a first argument to @@ -3149,8 +3242,8 @@ class HCurrentMethod FINAL : public HExpression<0> { DECLARE_INSTRUCTION(CurrentMethod); - private: - DISALLOW_COPY_AND_ASSIGN(HCurrentMethod); + protected: + DEFAULT_COPY_CONSTRUCTOR(CurrentMethod); }; // Fetches an ArtMethod from the virtual table or the interface method table @@ -3173,6 +3266,7 @@ class HClassTableGet FINAL : public HExpression<1> { SetRawInputAt(0, cls); } + bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other) const OVERRIDE { return other->AsClassTableGet()->GetIndex() == index_ && @@ -3184,6 +3278,9 @@ class HClassTableGet FINAL : public HExpression<1> { DECLARE_INSTRUCTION(ClassTableGet); + protected: + DEFAULT_COPY_CONSTRUCTOR(ClassTableGet); + private: static constexpr size_t kFieldTableKind = kNumberOfExpressionPackedBits; static constexpr size_t kFieldTableKindSize = @@ -3195,8 +3292,6 @@ class HClassTableGet FINAL : public HExpression<1> { // The index of the ArtMethod in the table. const size_t index_; - - DISALLOW_COPY_AND_ASSIGN(HClassTableGet); }; // PackedSwitch (jump table). A block ending with a PackedSwitch instruction will @@ -3214,6 +3309,8 @@ class HPackedSwitch FINAL : public HTemplateInstruction<1> { SetRawInputAt(0, input); } + bool IsClonable() const OVERRIDE { return true; } + bool IsControlFlow() const OVERRIDE { return true; } int32_t GetStartValue() const { return start_value_; } @@ -3226,11 +3323,12 @@ class HPackedSwitch FINAL : public HTemplateInstruction<1> { } DECLARE_INSTRUCTION(PackedSwitch); + protected: + DEFAULT_COPY_CONSTRUCTOR(PackedSwitch); + private: const int32_t start_value_; const uint32_t num_entries_; - - DISALLOW_COPY_AND_ASSIGN(HPackedSwitch); }; class HUnaryOperation : public HExpression<1> { @@ -3240,6 +3338,9 @@ class HUnaryOperation : public HExpression<1> { SetRawInputAt(0, input); } + // All of the UnaryOperation instructions are clonable. + bool IsClonable() const OVERRIDE { return true; } + HInstruction* GetInput() const { return InputAt(0); } DataType::Type GetResultType() const { return GetType(); } @@ -3261,8 +3362,8 @@ class HUnaryOperation : public HExpression<1> { DECLARE_ABSTRACT_INSTRUCTION(UnaryOperation); - private: - DISALLOW_COPY_AND_ASSIGN(HUnaryOperation); + protected: + DEFAULT_COPY_CONSTRUCTOR(UnaryOperation); }; class HBinaryOperation : public HExpression<2> { @@ -3277,6 +3378,9 @@ class HBinaryOperation : public HExpression<2> { SetRawInputAt(1, right); } + // All of the BinaryOperation instructions are clonable. + bool IsClonable() const OVERRIDE { return true; } + HInstruction* GetLeft() const { return InputAt(0); } HInstruction* GetRight() const { return InputAt(1); } DataType::Type GetResultType() const { return GetType(); } @@ -3351,8 +3455,8 @@ class HBinaryOperation : public HExpression<2> { DECLARE_ABSTRACT_INSTRUCTION(BinaryOperation); - private: - DISALLOW_COPY_AND_ASSIGN(HBinaryOperation); + protected: + DEFAULT_COPY_CONSTRUCTOR(BinaryOperation); }; // The comparison bias applies for floating point operations and indicates how NaN @@ -3442,8 +3546,7 @@ class HCondition : public HBinaryOperation { return GetBlock()->GetGraph()->GetIntConstant(value, dex_pc); } - private: - DISALLOW_COPY_AND_ASSIGN(HCondition); + DEFAULT_COPY_CONSTRUCTOR(Condition); }; // Instruction to check if two inputs are equal to each other. @@ -3485,10 +3588,11 @@ class HEqual FINAL : public HCondition { return kCondNE; } + protected: + DEFAULT_COPY_CONSTRUCTOR(Equal); + private: template static bool Compute(T x, T y) { return x == y; } - - DISALLOW_COPY_AND_ASSIGN(HEqual); }; class HNotEqual FINAL : public HCondition { @@ -3528,10 +3632,11 @@ class HNotEqual FINAL : public HCondition { return kCondEQ; } + protected: + DEFAULT_COPY_CONSTRUCTOR(NotEqual); + private: template static bool Compute(T x, T y) { return x != y; } - - DISALLOW_COPY_AND_ASSIGN(HNotEqual); }; class HLessThan FINAL : public HCondition { @@ -3565,10 +3670,11 @@ class HLessThan FINAL : public HCondition { return kCondGE; } + protected: + DEFAULT_COPY_CONSTRUCTOR(LessThan); + private: template static bool Compute(T x, T y) { return x < y; } - - DISALLOW_COPY_AND_ASSIGN(HLessThan); }; class HLessThanOrEqual FINAL : public HCondition { @@ -3602,10 +3708,11 @@ class HLessThanOrEqual FINAL : public HCondition { return kCondGT; } + protected: + DEFAULT_COPY_CONSTRUCTOR(LessThanOrEqual); + private: template static bool Compute(T x, T y) { return x <= y; } - - DISALLOW_COPY_AND_ASSIGN(HLessThanOrEqual); }; class HGreaterThan FINAL : public HCondition { @@ -3639,10 +3746,11 @@ class HGreaterThan FINAL : public HCondition { return kCondLE; } + protected: + DEFAULT_COPY_CONSTRUCTOR(GreaterThan); + private: template static bool Compute(T x, T y) { return x > y; } - - DISALLOW_COPY_AND_ASSIGN(HGreaterThan); }; class HGreaterThanOrEqual FINAL : public HCondition { @@ -3676,10 +3784,11 @@ class HGreaterThanOrEqual FINAL : public HCondition { return kCondLT; } + protected: + DEFAULT_COPY_CONSTRUCTOR(GreaterThanOrEqual); + private: template static bool Compute(T x, T y) { return x >= y; } - - DISALLOW_COPY_AND_ASSIGN(HGreaterThanOrEqual); }; class HBelow FINAL : public HCondition { @@ -3714,12 +3823,13 @@ class HBelow FINAL : public HCondition { return kCondAE; } + protected: + DEFAULT_COPY_CONSTRUCTOR(Below); + private: template static bool Compute(T x, T y) { return MakeUnsigned(x) < MakeUnsigned(y); } - - DISALLOW_COPY_AND_ASSIGN(HBelow); }; class HBelowOrEqual FINAL : public HCondition { @@ -3754,12 +3864,13 @@ class HBelowOrEqual FINAL : public HCondition { return kCondA; } + protected: + DEFAULT_COPY_CONSTRUCTOR(BelowOrEqual); + private: template static bool Compute(T x, T y) { return MakeUnsigned(x) <= MakeUnsigned(y); } - - DISALLOW_COPY_AND_ASSIGN(HBelowOrEqual); }; class HAbove FINAL : public HCondition { @@ -3794,12 +3905,13 @@ class HAbove FINAL : public HCondition { return kCondBE; } + protected: + DEFAULT_COPY_CONSTRUCTOR(Above); + private: template static bool Compute(T x, T y) { return MakeUnsigned(x) > MakeUnsigned(y); } - - DISALLOW_COPY_AND_ASSIGN(HAbove); }; class HAboveOrEqual FINAL : public HCondition { @@ -3834,12 +3946,13 @@ class HAboveOrEqual FINAL : public HCondition { return kCondB; } + protected: + DEFAULT_COPY_CONSTRUCTOR(AboveOrEqual); + private: template static bool Compute(T x, T y) { return MakeUnsigned(x) >= MakeUnsigned(y); } - - DISALLOW_COPY_AND_ASSIGN(HAboveOrEqual); }; // Instruction to check how two inputs compare to each other. @@ -3929,8 +4042,7 @@ class HCompare FINAL : public HBinaryOperation { return GetBlock()->GetGraph()->GetIntConstant(value, dex_pc); } - private: - DISALLOW_COPY_AND_ASSIGN(HCompare); + DEFAULT_COPY_CONSTRUCTOR(Compare); }; class HNewInstance FINAL : public HExpression<1> { @@ -3949,6 +4061,8 @@ class HNewInstance FINAL : public HExpression<1> { SetRawInputAt(0, cls); } + bool IsClonable() const OVERRIDE { return true; } + dex::TypeIndex GetTypeIndex() const { return type_index_; } const DexFile& GetDexFile() const { return dex_file_; } @@ -3985,6 +4099,9 @@ class HNewInstance FINAL : public HExpression<1> { DECLARE_INSTRUCTION(NewInstance); + protected: + DEFAULT_COPY_CONSTRUCTOR(NewInstance); + private: static constexpr size_t kFlagFinalizable = kNumberOfExpressionPackedBits; static constexpr size_t kNumberOfNewInstancePackedBits = kFlagFinalizable + 1; @@ -3994,8 +4111,6 @@ class HNewInstance FINAL : public HExpression<1> { const dex::TypeIndex type_index_; const DexFile& dex_file_; QuickEntrypointEnum entrypoint_; - - DISALLOW_COPY_AND_ASSIGN(HNewInstance); }; enum IntrinsicNeedsEnvironmentOrCache { @@ -4113,6 +4228,8 @@ class HInvoke : public HVariableInputSizeInstruction { SetPackedFlag(true); } + DEFAULT_COPY_CONSTRUCTOR(Invoke); + uint32_t number_of_arguments_; ArtMethod* resolved_method_; const uint32_t dex_method_index_; @@ -4120,9 +4237,6 @@ class HInvoke : public HVariableInputSizeInstruction { // A magic word holding optimizations for intrinsics. See intrinsics.h. uint32_t intrinsic_optimizations_; - - private: - DISALLOW_COPY_AND_ASSIGN(HInvoke); }; class HInvokeUnresolved FINAL : public HInvoke { @@ -4143,10 +4257,12 @@ class HInvokeUnresolved FINAL : public HInvoke { invoke_type) { } + bool IsClonable() const OVERRIDE { return true; } + DECLARE_INSTRUCTION(InvokeUnresolved); - private: - DISALLOW_COPY_AND_ASSIGN(HInvokeUnresolved); + protected: + DEFAULT_COPY_CONSTRUCTOR(InvokeUnresolved); }; class HInvokePolymorphic FINAL : public HInvoke { @@ -4165,10 +4281,12 @@ class HInvokePolymorphic FINAL : public HInvoke { nullptr, kVirtual) {} + bool IsClonable() const OVERRIDE { return true; } + DECLARE_INSTRUCTION(InvokePolymorphic); - private: - DISALLOW_COPY_AND_ASSIGN(HInvokePolymorphic); + protected: + DEFAULT_COPY_CONSTRUCTOR(InvokePolymorphic); }; class HInvokeStaticOrDirect FINAL : public HInvoke { @@ -4255,6 +4373,8 @@ class HInvokeStaticOrDirect FINAL : public HInvoke { SetPackedField(clinit_check_requirement); } + bool IsClonable() const OVERRIDE { return true; } + void SetDispatchInfo(const DispatchInfo& dispatch_info) { bool had_current_method_input = HasCurrentMethodInput(); bool needs_current_method_input = NeedsCurrentMethodInput(dispatch_info.method_load_kind); @@ -4400,6 +4520,9 @@ class HInvokeStaticOrDirect FINAL : public HInvoke { DECLARE_INSTRUCTION(InvokeStaticOrDirect); + protected: + DEFAULT_COPY_CONSTRUCTOR(InvokeStaticOrDirect); + private: static constexpr size_t kFieldClinitCheckRequirement = kNumberOfInvokePackedBits; static constexpr size_t kFieldClinitCheckRequirementSize = @@ -4415,8 +4538,6 @@ class HInvokeStaticOrDirect FINAL : public HInvoke { // Cached values of the resolved method, to avoid needing the mutator lock. MethodReference target_method_; DispatchInfo dispatch_info_; - - DISALLOW_COPY_AND_ASSIGN(HInvokeStaticOrDirect); }; std::ostream& operator<<(std::ostream& os, HInvokeStaticOrDirect::MethodLoadKind rhs); std::ostream& operator<<(std::ostream& os, HInvokeStaticOrDirect::ClinitCheckRequirement rhs); @@ -4440,6 +4561,8 @@ class HInvokeVirtual FINAL : public HInvoke { kVirtual), vtable_index_(vtable_index) {} + bool IsClonable() const OVERRIDE { return true; } + bool CanBeNull() const OVERRIDE { switch (GetIntrinsic()) { case Intrinsics::kThreadCurrentThread: @@ -4462,11 +4585,12 @@ class HInvokeVirtual FINAL : public HInvoke { DECLARE_INSTRUCTION(InvokeVirtual); + protected: + DEFAULT_COPY_CONSTRUCTOR(InvokeVirtual); + private: // Cached value of the resolved method, to avoid needing the mutator lock. const uint32_t vtable_index_; - - DISALLOW_COPY_AND_ASSIGN(HInvokeVirtual); }; class HInvokeInterface FINAL : public HInvoke { @@ -4488,6 +4612,8 @@ class HInvokeInterface FINAL : public HInvoke { kInterface), imt_index_(imt_index) {} + bool IsClonable() const OVERRIDE { return true; } + bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE { // TODO: Add implicit null checks in intrinsics. return (obj == InputAt(0)) && !GetLocations()->Intrinsified(); @@ -4499,15 +4625,15 @@ class HInvokeInterface FINAL : public HInvoke { } uint32_t GetImtIndex() const { return imt_index_; } - uint32_t GetDexMethodIndex() const { return dex_method_index_; } DECLARE_INSTRUCTION(InvokeInterface); + protected: + DEFAULT_COPY_CONSTRUCTOR(InvokeInterface); + private: // Cached value of the resolved method, to avoid needing the mutator lock. const uint32_t imt_index_; - - DISALLOW_COPY_AND_ASSIGN(HInvokeInterface); }; class HNeg FINAL : public HUnaryOperation { @@ -4534,8 +4660,8 @@ class HNeg FINAL : public HUnaryOperation { DECLARE_INSTRUCTION(Neg); - private: - DISALLOW_COPY_AND_ASSIGN(HNeg); + protected: + DEFAULT_COPY_CONSTRUCTOR(Neg); }; class HNewArray FINAL : public HExpression<2> { @@ -4546,6 +4672,8 @@ class HNewArray FINAL : public HExpression<2> { SetRawInputAt(1, length); } + bool IsClonable() const OVERRIDE { return true; } + // Calls runtime so needs an environment. bool NeedsEnvironment() const OVERRIDE { return true; } @@ -4565,8 +4693,8 @@ class HNewArray FINAL : public HExpression<2> { DECLARE_INSTRUCTION(NewArray); - private: - DISALLOW_COPY_AND_ASSIGN(HNewArray); + protected: + DEFAULT_COPY_CONSTRUCTOR(NewArray); }; class HAdd FINAL : public HBinaryOperation { @@ -4600,8 +4728,8 @@ class HAdd FINAL : public HBinaryOperation { DECLARE_INSTRUCTION(Add); - private: - DISALLOW_COPY_AND_ASSIGN(HAdd); + protected: + DEFAULT_COPY_CONSTRUCTOR(Add); }; class HSub FINAL : public HBinaryOperation { @@ -4633,8 +4761,8 @@ class HSub FINAL : public HBinaryOperation { DECLARE_INSTRUCTION(Sub); - private: - DISALLOW_COPY_AND_ASSIGN(HSub); + protected: + DEFAULT_COPY_CONSTRUCTOR(Sub); }; class HMul FINAL : public HBinaryOperation { @@ -4668,8 +4796,8 @@ class HMul FINAL : public HBinaryOperation { DECLARE_INSTRUCTION(Mul); - private: - DISALLOW_COPY_AND_ASSIGN(HMul); + protected: + DEFAULT_COPY_CONSTRUCTOR(Mul); }; class HDiv FINAL : public HBinaryOperation { @@ -4715,8 +4843,8 @@ class HDiv FINAL : public HBinaryOperation { DECLARE_INSTRUCTION(Div); - private: - DISALLOW_COPY_AND_ASSIGN(HDiv); + protected: + DEFAULT_COPY_CONSTRUCTOR(Div); }; class HRem FINAL : public HBinaryOperation { @@ -4762,8 +4890,8 @@ class HRem FINAL : public HBinaryOperation { DECLARE_INSTRUCTION(Rem); - private: - DISALLOW_COPY_AND_ASSIGN(HRem); + protected: + DEFAULT_COPY_CONSTRUCTOR(Rem); }; class HDivZeroCheck FINAL : public HExpression<1> { @@ -4788,8 +4916,8 @@ class HDivZeroCheck FINAL : public HExpression<1> { DECLARE_INSTRUCTION(DivZeroCheck); - private: - DISALLOW_COPY_AND_ASSIGN(HDivZeroCheck); + protected: + DEFAULT_COPY_CONSTRUCTOR(DivZeroCheck); }; class HShl FINAL : public HBinaryOperation { @@ -4834,8 +4962,8 @@ class HShl FINAL : public HBinaryOperation { DECLARE_INSTRUCTION(Shl); - private: - DISALLOW_COPY_AND_ASSIGN(HShl); + protected: + DEFAULT_COPY_CONSTRUCTOR(Shl); }; class HShr FINAL : public HBinaryOperation { @@ -4880,8 +5008,8 @@ class HShr FINAL : public HBinaryOperation { DECLARE_INSTRUCTION(Shr); - private: - DISALLOW_COPY_AND_ASSIGN(HShr); + protected: + DEFAULT_COPY_CONSTRUCTOR(Shr); }; class HUShr FINAL : public HBinaryOperation { @@ -4928,8 +5056,8 @@ class HUShr FINAL : public HBinaryOperation { DECLARE_INSTRUCTION(UShr); - private: - DISALLOW_COPY_AND_ASSIGN(HUShr); + protected: + DEFAULT_COPY_CONSTRUCTOR(UShr); }; class HAnd FINAL : public HBinaryOperation { @@ -4965,8 +5093,8 @@ class HAnd FINAL : public HBinaryOperation { DECLARE_INSTRUCTION(And); - private: - DISALLOW_COPY_AND_ASSIGN(HAnd); + protected: + DEFAULT_COPY_CONSTRUCTOR(And); }; class HOr FINAL : public HBinaryOperation { @@ -5002,8 +5130,8 @@ class HOr FINAL : public HBinaryOperation { DECLARE_INSTRUCTION(Or); - private: - DISALLOW_COPY_AND_ASSIGN(HOr); + protected: + DEFAULT_COPY_CONSTRUCTOR(Or); }; class HXor FINAL : public HBinaryOperation { @@ -5039,8 +5167,8 @@ class HXor FINAL : public HBinaryOperation { DECLARE_INSTRUCTION(Xor); - private: - DISALLOW_COPY_AND_ASSIGN(HXor); + protected: + DEFAULT_COPY_CONSTRUCTOR(Xor); }; class HRor FINAL : public HBinaryOperation { @@ -5090,8 +5218,8 @@ class HRor FINAL : public HBinaryOperation { DECLARE_INSTRUCTION(Ror); - private: - DISALLOW_COPY_AND_ASSIGN(HRor); + protected: + DEFAULT_COPY_CONSTRUCTOR(Ror); }; // The value of a parameter in this method. Its location depends on @@ -5121,6 +5249,9 @@ class HParameterValue FINAL : public HExpression<0> { DECLARE_INSTRUCTION(ParameterValue); + protected: + DEFAULT_COPY_CONSTRUCTOR(ParameterValue); + private: // Whether or not the parameter value corresponds to 'this' argument. static constexpr size_t kFlagIsThis = kNumberOfExpressionPackedBits; @@ -5134,8 +5265,6 @@ class HParameterValue FINAL : public HExpression<0> { // The index of this parameter in the parameters list. Must be less // than HGraph::number_of_in_vregs_. const uint8_t index_; - - DISALLOW_COPY_AND_ASSIGN(HParameterValue); }; class HNot FINAL : public HUnaryOperation { @@ -5167,8 +5296,8 @@ class HNot FINAL : public HUnaryOperation { DECLARE_INSTRUCTION(Not); - private: - DISALLOW_COPY_AND_ASSIGN(HNot); + protected: + DEFAULT_COPY_CONSTRUCTOR(Not); }; class HBooleanNot FINAL : public HUnaryOperation { @@ -5204,8 +5333,8 @@ class HBooleanNot FINAL : public HUnaryOperation { DECLARE_INSTRUCTION(BooleanNot); - private: - DISALLOW_COPY_AND_ASSIGN(HBooleanNot); + protected: + DEFAULT_COPY_CONSTRUCTOR(BooleanNot); }; class HTypeConversion FINAL : public HExpression<1> { @@ -5233,8 +5362,8 @@ class HTypeConversion FINAL : public HExpression<1> { DECLARE_INSTRUCTION(TypeConversion); - private: - DISALLOW_COPY_AND_ASSIGN(HTypeConversion); + protected: + DEFAULT_COPY_CONSTRUCTOR(TypeConversion); }; static constexpr uint32_t kNoRegNumber = -1; @@ -5248,6 +5377,7 @@ class HNullCheck FINAL : public HExpression<1> { SetRawInputAt(0, value); } + bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; @@ -5259,11 +5389,10 @@ class HNullCheck FINAL : public HExpression<1> { bool CanBeNull() const OVERRIDE { return false; } - DECLARE_INSTRUCTION(NullCheck); - private: - DISALLOW_COPY_AND_ASSIGN(HNullCheck); + protected: + DEFAULT_COPY_CONSTRUCTOR(NullCheck); }; // Embeds an ArtField and all the information required by the compiler. We cache @@ -5325,6 +5454,7 @@ class HInstanceFieldGet FINAL : public HExpression<1> { SetRawInputAt(0, value); } + bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return !IsVolatile(); } bool InstructionDataEquals(const HInstruction* other) const OVERRIDE { @@ -5354,10 +5484,11 @@ class HInstanceFieldGet FINAL : public HExpression<1> { DECLARE_INSTRUCTION(InstanceFieldGet); + protected: + DEFAULT_COPY_CONSTRUCTOR(InstanceFieldGet); + private: const FieldInfo field_info_; - - DISALLOW_COPY_AND_ASSIGN(HInstanceFieldGet); }; class HInstanceFieldSet FINAL : public HTemplateInstruction<2> { @@ -5385,6 +5516,8 @@ class HInstanceFieldSet FINAL : public HTemplateInstruction<2> { SetRawInputAt(1, value); } + bool IsClonable() const OVERRIDE { return true; } + bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE { return (obj == InputAt(0)) && art::CanDoImplicitNullCheckOn(GetFieldOffset().Uint32Value()); } @@ -5399,6 +5532,9 @@ class HInstanceFieldSet FINAL : public HTemplateInstruction<2> { DECLARE_INSTRUCTION(InstanceFieldSet); + protected: + DEFAULT_COPY_CONSTRUCTOR(InstanceFieldSet); + private: static constexpr size_t kFlagValueCanBeNull = kNumberOfGenericPackedBits; static constexpr size_t kNumberOfInstanceFieldSetPackedBits = kFlagValueCanBeNull + 1; @@ -5406,8 +5542,6 @@ class HInstanceFieldSet FINAL : public HTemplateInstruction<2> { "Too many packed fields."); const FieldInfo field_info_; - - DISALLOW_COPY_AND_ASSIGN(HInstanceFieldSet); }; class HArrayGet FINAL : public HExpression<2> { @@ -5435,6 +5569,7 @@ class HArrayGet FINAL : public HExpression<2> { SetRawInputAt(1, index); } + bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; @@ -5484,6 +5619,9 @@ class HArrayGet FINAL : public HExpression<2> { DECLARE_INSTRUCTION(ArrayGet); + protected: + DEFAULT_COPY_CONSTRUCTOR(ArrayGet); + private: // We treat a String as an array, creating the HArrayGet from String.charAt() // intrinsic in the instruction simplifier. We can always determine whether @@ -5494,8 +5632,6 @@ class HArrayGet FINAL : public HExpression<2> { static constexpr size_t kNumberOfArrayGetPackedBits = kFlagIsStringCharAt + 1; static_assert(kNumberOfArrayGetPackedBits <= HInstruction::kMaxNumberOfPackedBits, "Too many packed fields."); - - DISALLOW_COPY_AND_ASSIGN(HArrayGet); }; class HArraySet FINAL : public HTemplateInstruction<3> { @@ -5529,6 +5665,8 @@ class HArraySet FINAL : public HTemplateInstruction<3> { SetRawInputAt(2, value); } + bool IsClonable() const OVERRIDE { return true; } + bool NeedsEnvironment() const OVERRIDE { // We call a runtime method to throw ArrayStoreException. return NeedsTypeCheck(); @@ -5594,6 +5732,9 @@ class HArraySet FINAL : public HTemplateInstruction<3> { DECLARE_INSTRUCTION(ArraySet); + protected: + DEFAULT_COPY_CONSTRUCTOR(ArraySet); + private: static constexpr size_t kFieldExpectedComponentType = kNumberOfGenericPackedBits; static constexpr size_t kFieldExpectedComponentTypeSize = @@ -5609,8 +5750,6 @@ class HArraySet FINAL : public HTemplateInstruction<3> { static_assert(kNumberOfArraySetPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); using ExpectedComponentTypeField = BitField; - - DISALLOW_COPY_AND_ASSIGN(HArraySet); }; class HArrayLength FINAL : public HExpression<1> { @@ -5623,6 +5762,7 @@ class HArrayLength FINAL : public HExpression<1> { SetRawInputAt(0, array); } + bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; @@ -5635,6 +5775,9 @@ class HArrayLength FINAL : public HExpression<1> { DECLARE_INSTRUCTION(ArrayLength); + protected: + DEFAULT_COPY_CONSTRUCTOR(ArrayLength); + private: // We treat a String as an array, creating the HArrayLength from String.length() // or String.isEmpty() intrinsic in the instruction simplifier. We can always @@ -5645,8 +5788,6 @@ class HArrayLength FINAL : public HExpression<1> { static constexpr size_t kNumberOfArrayLengthPackedBits = kFlagIsStringLength + 1; static_assert(kNumberOfArrayLengthPackedBits <= HInstruction::kMaxNumberOfPackedBits, "Too many packed fields."); - - DISALLOW_COPY_AND_ASSIGN(HArrayLength); }; class HBoundsCheck FINAL : public HExpression<2> { @@ -5656,14 +5797,15 @@ class HBoundsCheck FINAL : public HExpression<2> { HBoundsCheck(HInstruction* index, HInstruction* length, uint32_t dex_pc, - bool string_char_at = false) + bool is_string_char_at = false) : HExpression(index->GetType(), SideEffects::CanTriggerGC(), dex_pc) { DCHECK_EQ(DataType::Type::kInt32, DataType::Kind(index->GetType())); - SetPackedFlag(string_char_at); + SetPackedFlag(is_string_char_at); SetRawInputAt(0, index); SetRawInputAt(1, length); } + bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; @@ -5679,10 +5821,11 @@ class HBoundsCheck FINAL : public HExpression<2> { DECLARE_INSTRUCTION(BoundsCheck); + protected: + DEFAULT_COPY_CONSTRUCTOR(BoundsCheck); + private: static constexpr size_t kFlagIsStringCharAt = kNumberOfExpressionPackedBits; - - DISALLOW_COPY_AND_ASSIGN(HBoundsCheck); }; class HSuspendCheck FINAL : public HTemplateInstruction<0> { @@ -5690,6 +5833,8 @@ class HSuspendCheck FINAL : public HTemplateInstruction<0> { explicit HSuspendCheck(uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc), slow_path_(nullptr) {} + bool IsClonable() const OVERRIDE { return true; } + bool NeedsEnvironment() const OVERRIDE { return true; } @@ -5699,12 +5844,13 @@ class HSuspendCheck FINAL : public HTemplateInstruction<0> { DECLARE_INSTRUCTION(SuspendCheck); + protected: + DEFAULT_COPY_CONSTRUCTOR(SuspendCheck); + private: // Only used for code generation, in order to share the same slow path between back edges // of a same loop. SlowPathCode* slow_path_; - - DISALLOW_COPY_AND_ASSIGN(HSuspendCheck); }; // Pseudo-instruction which provides the native debugger with mapping information. @@ -5720,8 +5866,8 @@ class HNativeDebugInfo : public HTemplateInstruction<0> { DECLARE_INSTRUCTION(NativeDebugInfo); - private: - DISALLOW_COPY_AND_ASSIGN(HNativeDebugInfo); + protected: + DEFAULT_COPY_CONSTRUCTOR(NativeDebugInfo); }; /** @@ -5787,6 +5933,8 @@ class HLoadClass FINAL : public HInstruction { SetPackedFlag(false); } + bool IsClonable() const OVERRIDE { return true; } + void SetLoadKind(LoadKind load_kind); LoadKind GetLoadKind() const { @@ -5878,6 +6026,9 @@ class HLoadClass FINAL : public HInstruction { DECLARE_INSTRUCTION(LoadClass); + protected: + DEFAULT_COPY_CONSTRUCTOR(LoadClass); + private: static constexpr size_t kFlagNeedsAccessCheck = kNumberOfGenericPackedBits; static constexpr size_t kFlagIsInBootImage = kFlagNeedsAccessCheck + 1; @@ -5917,11 +6068,23 @@ class HLoadClass FINAL : public HInstruction { Handle klass_; ReferenceTypeInfo loaded_class_rti_; - - DISALLOW_COPY_AND_ASSIGN(HLoadClass); }; std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs); +// Note: defined outside class to see operator<<(., HLoadClass::LoadKind). +inline void HLoadClass::SetLoadKind(LoadKind load_kind) { + // The load kind should be determined before inserting the instruction to the graph. + DCHECK(GetBlock() == nullptr); + DCHECK(GetEnvironment() == nullptr); + SetPackedField(load_kind); + if (load_kind != LoadKind::kRuntimeCall && load_kind != LoadKind::kReferrersClass) { + special_input_ = HUserRecord(nullptr); + } + if (!NeedsEnvironment()) { + SetSideEffects(SideEffects::None()); + } +} + // Note: defined outside class to see operator<<(., HLoadClass::LoadKind). inline void HLoadClass::AddSpecialInput(HInstruction* special_input) { // The special input is used for PC-relative loads on some architectures, @@ -5976,6 +6139,8 @@ class HLoadString FINAL : public HInstruction { SetPackedField(LoadKind::kRuntimeCall); } + bool IsClonable() const OVERRIDE { return true; } + void SetLoadKind(LoadKind load_kind); LoadKind GetLoadKind() const { @@ -6042,6 +6207,9 @@ class HLoadString FINAL : public HInstruction { DECLARE_INSTRUCTION(LoadString); + protected: + DEFAULT_COPY_CONSTRUCTOR(LoadString); + private: static constexpr size_t kFieldLoadKind = kNumberOfGenericPackedBits; static constexpr size_t kFieldLoadKindSize = @@ -6061,11 +6229,24 @@ class HLoadString FINAL : public HInstruction { const DexFile& dex_file_; Handle string_; - - DISALLOW_COPY_AND_ASSIGN(HLoadString); }; std::ostream& operator<<(std::ostream& os, HLoadString::LoadKind rhs); +// Note: defined outside class to see operator<<(., HLoadString::LoadKind). +inline void HLoadString::SetLoadKind(LoadKind load_kind) { + // The load kind should be determined before inserting the instruction to the graph. + DCHECK(GetBlock() == nullptr); + DCHECK(GetEnvironment() == nullptr); + DCHECK_EQ(GetLoadKind(), LoadKind::kRuntimeCall); + SetPackedField(load_kind); + if (load_kind != LoadKind::kRuntimeCall) { + special_input_ = HUserRecord(nullptr); + } + if (!NeedsEnvironment()) { + SetSideEffects(SideEffects::None()); + } +} + // Note: defined outside class to see operator<<(., HLoadString::LoadKind). inline void HLoadString::AddSpecialInput(HInstruction* special_input) { // The special input is used for PC-relative loads on some architectures, @@ -6089,11 +6270,12 @@ class HClinitCheck FINAL : public HExpression<1> { HClinitCheck(HLoadClass* constant, uint32_t dex_pc) : HExpression( DataType::Type::kReference, - SideEffects::AllChanges(), // Assume write/read on all fields/arrays. + SideEffects::AllExceptGCDependency(), // Assume write/read on all fields/arrays. dex_pc) { SetRawInputAt(0, constant); } + bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; @@ -6113,8 +6295,9 @@ class HClinitCheck FINAL : public HExpression<1> { DECLARE_INSTRUCTION(ClinitCheck); - private: - DISALLOW_COPY_AND_ASSIGN(HClinitCheck); + + protected: + DEFAULT_COPY_CONSTRUCTOR(ClinitCheck); }; class HStaticFieldGet FINAL : public HExpression<1> { @@ -6140,6 +6323,7 @@ class HStaticFieldGet FINAL : public HExpression<1> { } + bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return !IsVolatile(); } bool InstructionDataEquals(const HInstruction* other) const OVERRIDE { @@ -6165,10 +6349,11 @@ class HStaticFieldGet FINAL : public HExpression<1> { DECLARE_INSTRUCTION(StaticFieldGet); + protected: + DEFAULT_COPY_CONSTRUCTOR(StaticFieldGet); + private: const FieldInfo field_info_; - - DISALLOW_COPY_AND_ASSIGN(HStaticFieldGet); }; class HStaticFieldSet FINAL : public HTemplateInstruction<2> { @@ -6196,6 +6381,7 @@ class HStaticFieldSet FINAL : public HTemplateInstruction<2> { SetRawInputAt(1, value); } + bool IsClonable() const OVERRIDE { return true; } const FieldInfo& GetFieldInfo() const { return field_info_; } MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); } DataType::Type GetFieldType() const { return field_info_.GetFieldType(); } @@ -6207,6 +6393,9 @@ class HStaticFieldSet FINAL : public HTemplateInstruction<2> { DECLARE_INSTRUCTION(StaticFieldSet); + protected: + DEFAULT_COPY_CONSTRUCTOR(StaticFieldSet); + private: static constexpr size_t kFlagValueCanBeNull = kNumberOfGenericPackedBits; static constexpr size_t kNumberOfStaticFieldSetPackedBits = kFlagValueCanBeNull + 1; @@ -6214,8 +6403,6 @@ class HStaticFieldSet FINAL : public HTemplateInstruction<2> { "Too many packed fields."); const FieldInfo field_info_; - - DISALLOW_COPY_AND_ASSIGN(HStaticFieldSet); }; class HUnresolvedInstanceFieldGet FINAL : public HExpression<1> { @@ -6229,6 +6416,7 @@ class HUnresolvedInstanceFieldGet FINAL : public HExpression<1> { SetRawInputAt(0, obj); } + bool IsClonable() const OVERRIDE { return true; } bool NeedsEnvironment() const OVERRIDE { return true; } bool CanThrow() const OVERRIDE { return true; } @@ -6237,10 +6425,11 @@ class HUnresolvedInstanceFieldGet FINAL : public HExpression<1> { DECLARE_INSTRUCTION(UnresolvedInstanceFieldGet); + protected: + DEFAULT_COPY_CONSTRUCTOR(UnresolvedInstanceFieldGet); + private: const uint32_t field_index_; - - DISALLOW_COPY_AND_ASSIGN(HUnresolvedInstanceFieldGet); }; class HUnresolvedInstanceFieldSet FINAL : public HTemplateInstruction<2> { @@ -6258,6 +6447,7 @@ class HUnresolvedInstanceFieldSet FINAL : public HTemplateInstruction<2> { SetRawInputAt(1, value); } + bool IsClonable() const OVERRIDE { return true; } bool NeedsEnvironment() const OVERRIDE { return true; } bool CanThrow() const OVERRIDE { return true; } @@ -6266,6 +6456,9 @@ class HUnresolvedInstanceFieldSet FINAL : public HTemplateInstruction<2> { DECLARE_INSTRUCTION(UnresolvedInstanceFieldSet); + protected: + DEFAULT_COPY_CONSTRUCTOR(UnresolvedInstanceFieldSet); + private: static constexpr size_t kFieldFieldType = HInstruction::kNumberOfGenericPackedBits; static constexpr size_t kFieldFieldTypeSize = @@ -6277,8 +6470,6 @@ class HUnresolvedInstanceFieldSet FINAL : public HTemplateInstruction<2> { using FieldTypeField = BitField; const uint32_t field_index_; - - DISALLOW_COPY_AND_ASSIGN(HUnresolvedInstanceFieldSet); }; class HUnresolvedStaticFieldGet FINAL : public HExpression<0> { @@ -6290,6 +6481,7 @@ class HUnresolvedStaticFieldGet FINAL : public HExpression<0> { field_index_(field_index) { } + bool IsClonable() const OVERRIDE { return true; } bool NeedsEnvironment() const OVERRIDE { return true; } bool CanThrow() const OVERRIDE { return true; } @@ -6298,10 +6490,11 @@ class HUnresolvedStaticFieldGet FINAL : public HExpression<0> { DECLARE_INSTRUCTION(UnresolvedStaticFieldGet); + protected: + DEFAULT_COPY_CONSTRUCTOR(UnresolvedStaticFieldGet); + private: const uint32_t field_index_; - - DISALLOW_COPY_AND_ASSIGN(HUnresolvedStaticFieldGet); }; class HUnresolvedStaticFieldSet FINAL : public HTemplateInstruction<1> { @@ -6317,6 +6510,7 @@ class HUnresolvedStaticFieldSet FINAL : public HTemplateInstruction<1> { SetRawInputAt(0, value); } + bool IsClonable() const OVERRIDE { return true; } bool NeedsEnvironment() const OVERRIDE { return true; } bool CanThrow() const OVERRIDE { return true; } @@ -6325,6 +6519,9 @@ class HUnresolvedStaticFieldSet FINAL : public HTemplateInstruction<1> { DECLARE_INSTRUCTION(UnresolvedStaticFieldSet); + protected: + DEFAULT_COPY_CONSTRUCTOR(UnresolvedStaticFieldSet); + private: static constexpr size_t kFieldFieldType = HInstruction::kNumberOfGenericPackedBits; static constexpr size_t kFieldFieldTypeSize = @@ -6336,8 +6533,6 @@ class HUnresolvedStaticFieldSet FINAL : public HTemplateInstruction<1> { using FieldTypeField = BitField; const uint32_t field_index_; - - DISALLOW_COPY_AND_ASSIGN(HUnresolvedStaticFieldSet); }; // Implement the move-exception DEX instruction. @@ -6350,8 +6545,8 @@ class HLoadException FINAL : public HExpression<0> { DECLARE_INSTRUCTION(LoadException); - private: - DISALLOW_COPY_AND_ASSIGN(HLoadException); + protected: + DEFAULT_COPY_CONSTRUCTOR(LoadException); }; // Implicit part of move-exception which clears thread-local exception storage. @@ -6363,8 +6558,8 @@ class HClearException FINAL : public HTemplateInstruction<0> { DECLARE_INSTRUCTION(ClearException); - private: - DISALLOW_COPY_AND_ASSIGN(HClearException); + protected: + DEFAULT_COPY_CONSTRUCTOR(ClearException); }; class HThrow FINAL : public HTemplateInstruction<1> { @@ -6380,11 +6575,10 @@ class HThrow FINAL : public HTemplateInstruction<1> { bool CanThrow() const OVERRIDE { return true; } - DECLARE_INSTRUCTION(Throw); - private: - DISALLOW_COPY_AND_ASSIGN(HThrow); + protected: + DEFAULT_COPY_CONSTRUCTOR(Throw); }; /** @@ -6407,7 +6601,7 @@ std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs); class HInstanceOf FINAL : public HExpression<2> { public: HInstanceOf(HInstruction* object, - HLoadClass* constant, + HLoadClass* target_class, TypeCheckKind check_kind, uint32_t dex_pc) : HExpression(DataType::Type::kBool, @@ -6416,9 +6610,16 @@ class HInstanceOf FINAL : public HExpression<2> { SetPackedField(check_kind); SetPackedFlag(true); SetRawInputAt(0, object); - SetRawInputAt(1, constant); + SetRawInputAt(1, target_class); + } + + HLoadClass* GetTargetClass() const { + HInstruction* load_class = InputAt(1); + DCHECK(load_class->IsLoadClass()); + return load_class->AsLoadClass(); } + bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { @@ -6446,6 +6647,9 @@ class HInstanceOf FINAL : public HExpression<2> { DECLARE_INSTRUCTION(InstanceOf); + protected: + DEFAULT_COPY_CONSTRUCTOR(InstanceOf); + private: static constexpr size_t kFieldTypeCheckKind = kNumberOfExpressionPackedBits; static constexpr size_t kFieldTypeCheckKindSize = @@ -6454,8 +6658,6 @@ class HInstanceOf FINAL : public HExpression<2> { static constexpr size_t kNumberOfInstanceOfPackedBits = kFlagMustDoNullCheck + 1; static_assert(kNumberOfInstanceOfPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); using TypeCheckKindField = BitField; - - DISALLOW_COPY_AND_ASSIGN(HInstanceOf); }; class HBoundType FINAL : public HExpression<1> { @@ -6469,6 +6671,8 @@ class HBoundType FINAL : public HExpression<1> { SetRawInputAt(0, input); } + bool IsClonable() const OVERRIDE { return true; } + // {Get,Set}Upper* should only be used in reference type propagation. const ReferenceTypeInfo& GetUpperBound() const { return upper_bound_; } bool GetUpperCanBeNull() const { return GetPackedFlag(); } @@ -6483,6 +6687,9 @@ class HBoundType FINAL : public HExpression<1> { DECLARE_INSTRUCTION(BoundType); + protected: + DEFAULT_COPY_CONSTRUCTOR(BoundType); + private: // Represents the top constraint that can_be_null_ cannot exceed (i.e. if this // is false then CanBeNull() cannot be true). @@ -6498,23 +6705,28 @@ class HBoundType FINAL : public HExpression<1> { // // uper_bound_ will be ClassX // } ReferenceTypeInfo upper_bound_; - - DISALLOW_COPY_AND_ASSIGN(HBoundType); }; class HCheckCast FINAL : public HTemplateInstruction<2> { public: HCheckCast(HInstruction* object, - HLoadClass* constant, + HLoadClass* target_class, TypeCheckKind check_kind, uint32_t dex_pc) : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc) { SetPackedField(check_kind); SetPackedFlag(true); SetRawInputAt(0, object); - SetRawInputAt(1, constant); + SetRawInputAt(1, target_class); + } + + HLoadClass* GetTargetClass() const { + HInstruction* load_class = InputAt(1); + DCHECK(load_class->IsLoadClass()); + return load_class->AsLoadClass(); } + bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { @@ -6535,6 +6747,9 @@ class HCheckCast FINAL : public HTemplateInstruction<2> { DECLARE_INSTRUCTION(CheckCast); + protected: + DEFAULT_COPY_CONSTRUCTOR(CheckCast); + private: static constexpr size_t kFieldTypeCheckKind = kNumberOfGenericPackedBits; static constexpr size_t kFieldTypeCheckKindSize = @@ -6543,8 +6758,6 @@ class HCheckCast FINAL : public HTemplateInstruction<2> { static constexpr size_t kNumberOfCheckCastPackedBits = kFlagMustDoNullCheck + 1; static_assert(kNumberOfCheckCastPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); using TypeCheckKindField = BitField; - - DISALLOW_COPY_AND_ASSIGN(HCheckCast); }; /** @@ -6581,10 +6794,15 @@ class HMemoryBarrier FINAL : public HTemplateInstruction<0> { SetPackedField(barrier_kind); } + bool IsClonable() const OVERRIDE { return true; } + MemBarrierKind GetBarrierKind() { return GetPackedField(); } DECLARE_INSTRUCTION(MemoryBarrier); + protected: + DEFAULT_COPY_CONSTRUCTOR(MemoryBarrier); + private: static constexpr size_t kFieldBarrierKind = HInstruction::kNumberOfGenericPackedBits; static constexpr size_t kFieldBarrierKindSize = @@ -6594,8 +6812,6 @@ class HMemoryBarrier FINAL : public HTemplateInstruction<0> { static_assert(kNumberOfMemoryBarrierPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); using BarrierKindField = BitField; - - DISALLOW_COPY_AND_ASSIGN(HMemoryBarrier); }; // A constructor fence orders all prior stores to fields that could be accessed via a final field of @@ -6746,8 +6962,8 @@ class HConstructorFence FINAL : public HVariableInputSizeInstruction { DECLARE_INSTRUCTION(ConstructorFence); - private: - DISALLOW_COPY_AND_ASSIGN(HConstructorFence); + protected: + DEFAULT_COPY_CONSTRUCTOR(ConstructorFence); }; class HMonitorOperation FINAL : public HTemplateInstruction<1> { @@ -6781,6 +6997,9 @@ class HMonitorOperation FINAL : public HTemplateInstruction<1> { DECLARE_INSTRUCTION(MonitorOperation); + protected: + DEFAULT_COPY_CONSTRUCTOR(MonitorOperation); + private: static constexpr size_t kFieldOperationKind = HInstruction::kNumberOfGenericPackedBits; static constexpr size_t kFieldOperationKindSize = @@ -6790,9 +7009,6 @@ class HMonitorOperation FINAL : public HTemplateInstruction<1> { static_assert(kNumberOfMonitorOperationPackedBits <= HInstruction::kMaxNumberOfPackedBits, "Too many packed fields."); using OperationKindField = BitField; - - private: - DISALLOW_COPY_AND_ASSIGN(HMonitorOperation); }; class HSelect FINAL : public HExpression<3> { @@ -6813,6 +7029,7 @@ class HSelect FINAL : public HExpression<3> { SetRawInputAt(2, condition); } + bool IsClonable() const OVERRIDE { return true; } HInstruction* GetFalseValue() const { return InputAt(0); } HInstruction* GetTrueValue() const { return InputAt(1); } HInstruction* GetCondition() const { return InputAt(2); } @@ -6828,8 +7045,8 @@ class HSelect FINAL : public HExpression<3> { DECLARE_INSTRUCTION(Select); - private: - DISALLOW_COPY_AND_ASSIGN(HSelect); + protected: + DEFAULT_COPY_CONSTRUCTOR(Select); }; class MoveOperands : public ArenaObject { @@ -6960,10 +7177,11 @@ class HParallelMove FINAL : public HTemplateInstruction<0> { DECLARE_INSTRUCTION(ParallelMove); + protected: + DEFAULT_COPY_CONSTRUCTOR(ParallelMove); + private: ArenaVector moves_; - - DISALLOW_COPY_AND_ASSIGN(HParallelMove); }; // This instruction computes an intermediate address pointing in the 'middle' of an object. The @@ -6982,6 +7200,7 @@ class HIntermediateAddress FINAL : public HExpression<2> { SetRawInputAt(1, offset); } + bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; @@ -6993,8 +7212,8 @@ class HIntermediateAddress FINAL : public HExpression<2> { DECLARE_INSTRUCTION(IntermediateAddress); - private: - DISALLOW_COPY_AND_ASSIGN(HIntermediateAddress); + protected: + DEFAULT_COPY_CONSTRUCTOR(IntermediateAddress); }; @@ -7069,6 +7288,33 @@ class HGraphDelegateVisitor : public HGraphVisitor { DISALLOW_COPY_AND_ASSIGN(HGraphDelegateVisitor); }; +// Create a clone of the instruction, insert it into the graph; replace the old one with a new +// and remove the old instruction. +HInstruction* ReplaceInstrOrPhiByClone(HInstruction* instr); + +// Create a clone for each clonable instructions/phis and replace the original with the clone. +// +// Used for testing individual instruction cloner. +class CloneAndReplaceInstructionVisitor : public HGraphDelegateVisitor { + public: + explicit CloneAndReplaceInstructionVisitor(HGraph* graph) + : HGraphDelegateVisitor(graph), instr_replaced_by_clones_count(0) {} + + void VisitInstruction(HInstruction* instruction) OVERRIDE { + if (instruction->IsClonable()) { + ReplaceInstrOrPhiByClone(instruction); + instr_replaced_by_clones_count++; + } + } + + size_t GetInstrReplacedByClonesCount() const { return instr_replaced_by_clones_count; } + + private: + size_t instr_replaced_by_clones_count; + + DISALLOW_COPY_AND_ASSIGN(CloneAndReplaceInstructionVisitor); +}; + // Iterator over the blocks that art part of the loop. Includes blocks part // of an inner loop. The order in which the blocks are iterated is on their // block id. diff --git a/compiler/optimizing/nodes_mips.h b/compiler/optimizing/nodes_mips.h index ef388c30d508076ba1fe5ffff7af55ac3af1b2b9..2c0595e3d880c6aaa1b280f721c48ee15f36d901 100644 --- a/compiler/optimizing/nodes_mips.h +++ b/compiler/optimizing/nodes_mips.h @@ -30,8 +30,8 @@ class HMipsComputeBaseMethodAddress : public HExpression<0> { DECLARE_INSTRUCTION(MipsComputeBaseMethodAddress); - private: - DISALLOW_COPY_AND_ASSIGN(HMipsComputeBaseMethodAddress); + protected: + DEFAULT_COPY_CONSTRUCTOR(MipsComputeBaseMethodAddress); }; // Mips version of HPackedSwitch that holds a pointer to the base method address. @@ -62,11 +62,12 @@ class HMipsPackedSwitch FINAL : public HTemplateInstruction<2> { DECLARE_INSTRUCTION(MipsPackedSwitch); + protected: + DEFAULT_COPY_CONSTRUCTOR(MipsPackedSwitch); + private: const int32_t start_value_; const int32_t num_entries_; - - DISALLOW_COPY_AND_ASSIGN(HMipsPackedSwitch); }; // This instruction computes part of the array access offset (index offset). @@ -105,8 +106,8 @@ class HIntermediateArrayAddressIndex FINAL : public HExpression<2> { DECLARE_INSTRUCTION(IntermediateArrayAddressIndex); - private: - DISALLOW_COPY_AND_ASSIGN(HIntermediateArrayAddressIndex); + protected: + DEFAULT_COPY_CONSTRUCTOR(IntermediateArrayAddressIndex); }; } // namespace art diff --git a/compiler/optimizing/nodes_shared.h b/compiler/optimizing/nodes_shared.h index 7b4f5f7cbb3733a3110e2152feb4713a080f9ee3..e837f1e7e0caaab94a5642e9175e3d36fd3f6f1a 100644 --- a/compiler/optimizing/nodes_shared.h +++ b/compiler/optimizing/nodes_shared.h @@ -38,6 +38,8 @@ class HMultiplyAccumulate FINAL : public HExpression<3> { SetRawInputAt(kInputMulRightIndex, mul_right); } + bool IsClonable() const OVERRIDE { return true; } + static constexpr int kInputAccumulatorIndex = 0; static constexpr int kInputMulLeftIndex = 1; static constexpr int kInputMulRightIndex = 2; @@ -51,11 +53,12 @@ class HMultiplyAccumulate FINAL : public HExpression<3> { DECLARE_INSTRUCTION(MultiplyAccumulate); + protected: + DEFAULT_COPY_CONSTRUCTOR(MultiplyAccumulate); + private: // Indicates if this is a MADD or MSUB. const InstructionKind op_kind_; - - DISALLOW_COPY_AND_ASSIGN(HMultiplyAccumulate); }; class HBitwiseNegatedRight FINAL : public HBinaryOperation { @@ -111,11 +114,12 @@ class HBitwiseNegatedRight FINAL : public HBinaryOperation { DECLARE_INSTRUCTION(BitwiseNegatedRight); + protected: + DEFAULT_COPY_CONSTRUCTOR(BitwiseNegatedRight); + private: // Specifies the bitwise operation, which will be then negated. const InstructionKind op_kind_; - - DISALLOW_COPY_AND_ASSIGN(HBitwiseNegatedRight); }; // This instruction computes part of the array access offset (data and index offset). @@ -145,6 +149,7 @@ class HIntermediateAddressIndex FINAL : public HExpression<3> { SetRawInputAt(2, shift); } + bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; @@ -157,8 +162,8 @@ class HIntermediateAddressIndex FINAL : public HExpression<3> { DECLARE_INSTRUCTION(IntermediateAddressIndex); - private: - DISALLOW_COPY_AND_ASSIGN(HIntermediateAddressIndex); + protected: + DEFAULT_COPY_CONSTRUCTOR(IntermediateAddressIndex); }; class HDataProcWithShifterOp FINAL : public HExpression<2> { @@ -198,6 +203,7 @@ class HDataProcWithShifterOp FINAL : public HExpression<2> { SetRawInputAt(1, right); } + bool IsClonable() const OVERRIDE { return true; } bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(const HInstruction* other_instr) const OVERRIDE { const HDataProcWithShifterOp* other = other_instr->AsDataProcWithShifterOp(); @@ -225,14 +231,15 @@ class HDataProcWithShifterOp FINAL : public HExpression<2> { DECLARE_INSTRUCTION(DataProcWithShifterOp); + protected: + DEFAULT_COPY_CONSTRUCTOR(DataProcWithShifterOp); + private: InstructionKind instr_kind_; OpKind op_kind_; int shift_amount_; friend std::ostream& operator<<(std::ostream& os, OpKind op); - - DISALLOW_COPY_AND_ASSIGN(HDataProcWithShifterOp); }; std::ostream& operator<<(std::ostream& os, const HDataProcWithShifterOp::OpKind op); diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h index 17540b9770411cd948676f464ed73a30fa9820d4..87dff8403b6bd9cf6d4265395374c8859e913aa2 100644 --- a/compiler/optimizing/nodes_vector.h +++ b/compiler/optimizing/nodes_vector.h @@ -109,6 +109,16 @@ class HVecOperation : public HVariableInputSizeInstruction { // Assumes vector nodes cannot be moved by default. Each concrete implementation // that can be moved should override this method and return true. + // + // Note: similar approach is used for instruction scheduling (if it is turned on for the target): + // by default HScheduler::IsSchedulable returns false for a particular HVecOperation. + // HScheduler${ARCH}::IsSchedulable can be overridden to return true for an instruction (see + // scheduler_arm64.h for example) if it is safe to schedule it; in this case one *must* also + // look at/update HScheduler${ARCH}::IsSchedulingBarrier for this instruction. + // + // Note: For newly introduced vector instructions HScheduler${ARCH}::IsSchedulingBarrier must be + // altered to return true if the instruction might reside outside the SIMD loop body since SIMD + // registers are not kept alive across vector loop boundaries (yet). bool CanBeMoved() const OVERRIDE { return false; } // Tests if all data of a vector node (vector length and packed type) is equal. @@ -150,6 +160,19 @@ class HVecOperation : public HVariableInputSizeInstruction { } } + // Helper method to determine if an instruction returns a SIMD value. + // TODO: This method is needed until we introduce SIMD as proper type. + static bool ReturnsSIMDValue(HInstruction* instruction) { + if (instruction->IsVecOperation()) { + return !instruction->IsVecExtractScalar(); // only scalar returning vec op + } else if (instruction->IsPhi()) { + return + instruction->GetType() == kSIMDType && + instruction->InputAt(1)->IsVecOperation(); // vectorizer does not go deeper + } + return false; + } + DECLARE_ABSTRACT_INSTRUCTION(VecOperation); protected: @@ -161,10 +184,10 @@ class HVecOperation : public HVariableInputSizeInstruction { static_assert(kNumberOfVectorOpPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); using TypeField = BitField; + DEFAULT_COPY_CONSTRUCTOR(VecOperation); + private: const size_t vector_length_; - - DISALLOW_COPY_AND_ASSIGN(HVecOperation); }; // Abstraction of a unary vector operation. @@ -188,8 +211,8 @@ class HVecUnaryOperation : public HVecOperation { DECLARE_ABSTRACT_INSTRUCTION(VecUnaryOperation); - private: - DISALLOW_COPY_AND_ASSIGN(HVecUnaryOperation); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecUnaryOperation); }; // Abstraction of a binary vector operation. @@ -216,8 +239,8 @@ class HVecBinaryOperation : public HVecOperation { DECLARE_ABSTRACT_INSTRUCTION(VecBinaryOperation); - private: - DISALLOW_COPY_AND_ASSIGN(HVecBinaryOperation); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecBinaryOperation); }; // Abstraction of a vector operation that references memory, with an alignment. @@ -255,10 +278,11 @@ class HVecMemoryOperation : public HVecOperation { DECLARE_ABSTRACT_INSTRUCTION(VecMemoryOperation); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecMemoryOperation); + private: Alignment alignment_; - - DISALLOW_COPY_AND_ASSIGN(HVecMemoryOperation); }; // Packed type consistency checker ("same vector length" integral types may mix freely). @@ -296,8 +320,8 @@ class HVecReplicateScalar FINAL : public HVecUnaryOperation { DECLARE_INSTRUCTION(VecReplicateScalar); - private: - DISALLOW_COPY_AND_ASSIGN(HVecReplicateScalar); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecReplicateScalar); }; // Extracts a particular scalar from the given vector, @@ -329,8 +353,8 @@ class HVecExtractScalar FINAL : public HVecUnaryOperation { DECLARE_INSTRUCTION(VecExtractScalar); - private: - DISALLOW_COPY_AND_ASSIGN(HVecExtractScalar); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecExtractScalar); }; // Reduces the given vector into the first element as sum/min/max, @@ -367,10 +391,11 @@ class HVecReduce FINAL : public HVecUnaryOperation { DECLARE_INSTRUCTION(VecReduce); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecReduce); + private: const ReductionKind kind_; - - DISALLOW_COPY_AND_ASSIGN(HVecReduce); }; // Converts every component in the vector, @@ -394,8 +419,8 @@ class HVecCnv FINAL : public HVecUnaryOperation { DECLARE_INSTRUCTION(VecCnv); - private: - DISALLOW_COPY_AND_ASSIGN(HVecCnv); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecCnv); }; // Negates every component in the vector, @@ -415,8 +440,8 @@ class HVecNeg FINAL : public HVecUnaryOperation { DECLARE_INSTRUCTION(VecNeg); - private: - DISALLOW_COPY_AND_ASSIGN(HVecNeg); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecNeg); }; // Takes absolute value of every component in the vector, @@ -437,8 +462,8 @@ class HVecAbs FINAL : public HVecUnaryOperation { DECLARE_INSTRUCTION(VecAbs); - private: - DISALLOW_COPY_AND_ASSIGN(HVecAbs); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecAbs); }; // Bitwise- or boolean-nots every component in the vector, @@ -459,8 +484,8 @@ class HVecNot FINAL : public HVecUnaryOperation { DECLARE_INSTRUCTION(VecNot); - private: - DISALLOW_COPY_AND_ASSIGN(HVecNot); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecNot); }; // @@ -486,8 +511,8 @@ class HVecAdd FINAL : public HVecBinaryOperation { DECLARE_INSTRUCTION(VecAdd); - private: - DISALLOW_COPY_AND_ASSIGN(HVecAdd); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecAdd); }; // Performs halving add on every component in the two vectors, viz. @@ -531,14 +556,15 @@ class HVecHalvingAdd FINAL : public HVecBinaryOperation { DECLARE_INSTRUCTION(VecHalvingAdd); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecHalvingAdd); + private: // Additional packed bits. static constexpr size_t kFieldHAddIsUnsigned = HVecOperation::kNumberOfVectorOpPackedBits; static constexpr size_t kFieldHAddIsRounded = kFieldHAddIsUnsigned + 1; static constexpr size_t kNumberOfHAddPackedBits = kFieldHAddIsRounded + 1; static_assert(kNumberOfHAddPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); - - DISALLOW_COPY_AND_ASSIGN(HVecHalvingAdd); }; // Subtracts every component in the two vectors, @@ -560,8 +586,8 @@ class HVecSub FINAL : public HVecBinaryOperation { DECLARE_INSTRUCTION(VecSub); - private: - DISALLOW_COPY_AND_ASSIGN(HVecSub); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecSub); }; // Multiplies every component in the two vectors, @@ -583,8 +609,8 @@ class HVecMul FINAL : public HVecBinaryOperation { DECLARE_INSTRUCTION(VecMul); - private: - DISALLOW_COPY_AND_ASSIGN(HVecMul); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecMul); }; // Divides every component in the two vectors, @@ -606,8 +632,8 @@ class HVecDiv FINAL : public HVecBinaryOperation { DECLARE_INSTRUCTION(VecDiv); - private: - DISALLOW_COPY_AND_ASSIGN(HVecDiv); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecDiv); }; // Takes minimum of every component in the two vectors, @@ -645,13 +671,14 @@ class HVecMin FINAL : public HVecBinaryOperation { DECLARE_INSTRUCTION(VecMin); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecMin); + private: // Additional packed bits. static constexpr size_t kFieldMinOpIsUnsigned = HVecOperation::kNumberOfVectorOpPackedBits; static constexpr size_t kNumberOfMinOpPackedBits = kFieldMinOpIsUnsigned + 1; static_assert(kNumberOfMinOpPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); - - DISALLOW_COPY_AND_ASSIGN(HVecMin); }; // Takes maximum of every component in the two vectors, @@ -689,13 +716,14 @@ class HVecMax FINAL : public HVecBinaryOperation { DECLARE_INSTRUCTION(VecMax); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecMax); + private: // Additional packed bits. static constexpr size_t kFieldMaxOpIsUnsigned = HVecOperation::kNumberOfVectorOpPackedBits; static constexpr size_t kNumberOfMaxOpPackedBits = kFieldMaxOpIsUnsigned + 1; static_assert(kNumberOfMaxOpPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); - - DISALLOW_COPY_AND_ASSIGN(HVecMax); }; // Bitwise-ands every component in the two vectors, @@ -716,8 +744,8 @@ class HVecAnd FINAL : public HVecBinaryOperation { DECLARE_INSTRUCTION(VecAnd); - private: - DISALLOW_COPY_AND_ASSIGN(HVecAnd); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecAnd); }; // Bitwise-and-nots every component in the two vectors, @@ -738,8 +766,8 @@ class HVecAndNot FINAL : public HVecBinaryOperation { DECLARE_INSTRUCTION(VecAndNot); - private: - DISALLOW_COPY_AND_ASSIGN(HVecAndNot); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecAndNot); }; // Bitwise-ors every component in the two vectors, @@ -760,8 +788,8 @@ class HVecOr FINAL : public HVecBinaryOperation { DECLARE_INSTRUCTION(VecOr); - private: - DISALLOW_COPY_AND_ASSIGN(HVecOr); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecOr); }; // Bitwise-xors every component in the two vectors, @@ -782,8 +810,8 @@ class HVecXor FINAL : public HVecBinaryOperation { DECLARE_INSTRUCTION(VecXor); - private: - DISALLOW_COPY_AND_ASSIGN(HVecXor); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecXor); }; // Logically shifts every component in the vector left by the given distance, @@ -804,8 +832,8 @@ class HVecShl FINAL : public HVecBinaryOperation { DECLARE_INSTRUCTION(VecShl); - private: - DISALLOW_COPY_AND_ASSIGN(HVecShl); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecShl); }; // Arithmetically shifts every component in the vector right by the given distance, @@ -826,8 +854,8 @@ class HVecShr FINAL : public HVecBinaryOperation { DECLARE_INSTRUCTION(VecShr); - private: - DISALLOW_COPY_AND_ASSIGN(HVecShr); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecShr); }; // Logically shifts every component in the vector right by the given distance, @@ -848,8 +876,8 @@ class HVecUShr FINAL : public HVecBinaryOperation { DECLARE_INSTRUCTION(VecUShr); - private: - DISALLOW_COPY_AND_ASSIGN(HVecUShr); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecUShr); }; // @@ -874,7 +902,7 @@ class HVecSetScalars FINAL : public HVecOperation { vector_length, dex_pc) { for (size_t i = 0; i < number_of_scalars; i++) { - DCHECK(!scalars[i]->IsVecOperation() || scalars[i]->IsVecExtractScalar()); + DCHECK(!ReturnsSIMDValue(scalars[i])); SetRawInputAt(0, scalars[i]); } } @@ -885,8 +913,8 @@ class HVecSetScalars FINAL : public HVecOperation { DECLARE_INSTRUCTION(VecSetScalars); - private: - DISALLOW_COPY_AND_ASSIGN(HVecSetScalars); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecSetScalars); }; // Multiplies every component in the two vectors, adds the result vector to the accumulator vector, @@ -929,11 +957,12 @@ class HVecMultiplyAccumulate FINAL : public HVecOperation { DECLARE_INSTRUCTION(VecMultiplyAccumulate); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecMultiplyAccumulate); + private: // Indicates if this is a MADD or MSUB. const InstructionKind op_kind_; - - DISALLOW_COPY_AND_ASSIGN(HVecMultiplyAccumulate); }; // Takes the absolute difference of two vectors, and adds the results to @@ -968,8 +997,8 @@ class HVecSADAccumulate FINAL : public HVecOperation { DECLARE_INSTRUCTION(VecSADAccumulate); - private: - DISALLOW_COPY_AND_ASSIGN(HVecSADAccumulate); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecSADAccumulate); }; // Loads a vector from memory, viz. load(mem, 1) @@ -1007,13 +1036,14 @@ class HVecLoad FINAL : public HVecMemoryOperation { DECLARE_INSTRUCTION(VecLoad); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecLoad); + private: // Additional packed bits. static constexpr size_t kFieldIsStringCharAt = HVecOperation::kNumberOfVectorOpPackedBits; static constexpr size_t kNumberOfVecLoadPackedBits = kFieldIsStringCharAt + 1; static_assert(kNumberOfVecLoadPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); - - DISALLOW_COPY_AND_ASSIGN(HVecLoad); }; // Stores a vector to memory, viz. store(m, 1, [x1, .. , xn] ) @@ -1045,8 +1075,8 @@ class HVecStore FINAL : public HVecMemoryOperation { DECLARE_INSTRUCTION(VecStore); - private: - DISALLOW_COPY_AND_ASSIGN(HVecStore); + protected: + DEFAULT_COPY_CONSTRUCTOR(VecStore) }; } // namespace art diff --git a/compiler/optimizing/nodes_x86.h b/compiler/optimizing/nodes_x86.h index 22e92eab31f081a1ba52316ead3a5add79c9016d..6326065fe24f112cde688671b0e2f4c03db9b27e 100644 --- a/compiler/optimizing/nodes_x86.h +++ b/compiler/optimizing/nodes_x86.h @@ -30,8 +30,8 @@ class HX86ComputeBaseMethodAddress FINAL : public HExpression<0> { DECLARE_INSTRUCTION(X86ComputeBaseMethodAddress); - private: - DISALLOW_COPY_AND_ASSIGN(HX86ComputeBaseMethodAddress); + protected: + DEFAULT_COPY_CONSTRUCTOR(X86ComputeBaseMethodAddress); }; // Load a constant value from the constant table. @@ -54,8 +54,8 @@ class HX86LoadFromConstantTable FINAL : public HExpression<2> { DECLARE_INSTRUCTION(X86LoadFromConstantTable); - private: - DISALLOW_COPY_AND_ASSIGN(HX86LoadFromConstantTable); + protected: + DEFAULT_COPY_CONSTRUCTOR(X86LoadFromConstantTable); }; // Version of HNeg with access to the constant table for FP types. @@ -77,8 +77,8 @@ class HX86FPNeg FINAL : public HExpression<2> { DECLARE_INSTRUCTION(X86FPNeg); - private: - DISALLOW_COPY_AND_ASSIGN(HX86FPNeg); + protected: + DEFAULT_COPY_CONSTRUCTOR(X86FPNeg); }; // X86 version of HPackedSwitch that holds a pointer to the base method address. @@ -113,11 +113,12 @@ class HX86PackedSwitch FINAL : public HTemplateInstruction<2> { DECLARE_INSTRUCTION(X86PackedSwitch); + protected: + DEFAULT_COPY_CONSTRUCTOR(X86PackedSwitch); + private: const int32_t start_value_; const int32_t num_entries_; - - DISALLOW_COPY_AND_ASSIGN(HX86PackedSwitch); }; } // namespace art diff --git a/compiler/optimizing/optimization.cc b/compiler/optimizing/optimization.cc index 1e68ca2802589fb5056f10a3c62e80c260ce45fc..d8ac696d1e292df0529ce8d304903fb5302964f6 100644 --- a/compiler/optimizing/optimization.cc +++ b/compiler/optimizing/optimization.cc @@ -16,5 +16,318 @@ #include "optimization.h" +#ifdef ART_ENABLE_CODEGEN_arm +#include "instruction_simplifier_arm.h" +#endif +#ifdef ART_ENABLE_CODEGEN_arm64 +#include "instruction_simplifier_arm64.h" +#endif +#ifdef ART_ENABLE_CODEGEN_mips +#include "instruction_simplifier_mips.h" +#include "pc_relative_fixups_mips.h" +#endif +#ifdef ART_ENABLE_CODEGEN_x86 +#include "pc_relative_fixups_x86.h" +#endif +#if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64) +#include "x86_memory_gen.h" +#endif + +#include "bounds_check_elimination.h" +#include "cha_guard_optimization.h" +#include "code_item_accessors-inl.h" +#include "code_sinking.h" +#include "constant_folding.h" +#include "constructor_fence_redundancy_elimination.h" +#include "dead_code_elimination.h" +#include "driver/dex_compilation_unit.h" +#include "gvn.h" +#include "induction_var_analysis.h" +#include "inliner.h" +#include "instruction_simplifier.h" +#include "intrinsics.h" +#include "licm.h" +#include "load_store_analysis.h" +#include "load_store_elimination.h" +#include "loop_optimization.h" +#include "scheduler.h" +#include "select_generator.h" +#include "sharpening.h" +#include "side_effects_analysis.h" + +// Decide between default or alternative pass name. + namespace art { + +const char* OptimizationPassName(OptimizationPass pass) { + switch (pass) { + case OptimizationPass::kSideEffectsAnalysis: + return SideEffectsAnalysis::kSideEffectsAnalysisPassName; + case OptimizationPass::kInductionVarAnalysis: + return HInductionVarAnalysis::kInductionPassName; + case OptimizationPass::kLoadStoreAnalysis: + return LoadStoreAnalysis::kLoadStoreAnalysisPassName; + case OptimizationPass::kGlobalValueNumbering: + return GVNOptimization::kGlobalValueNumberingPassName; + case OptimizationPass::kInvariantCodeMotion: + return LICM::kLoopInvariantCodeMotionPassName; + case OptimizationPass::kLoopOptimization: + return HLoopOptimization::kLoopOptimizationPassName; + case OptimizationPass::kBoundsCheckElimination: + return BoundsCheckElimination::kBoundsCheckEliminationPassName; + case OptimizationPass::kLoadStoreElimination: + return LoadStoreElimination::kLoadStoreEliminationPassName; + case OptimizationPass::kConstantFolding: + return HConstantFolding::kConstantFoldingPassName; + case OptimizationPass::kDeadCodeElimination: + return HDeadCodeElimination::kDeadCodeEliminationPassName; + case OptimizationPass::kInliner: + return HInliner::kInlinerPassName; + case OptimizationPass::kSharpening: + return HSharpening::kSharpeningPassName; + case OptimizationPass::kSelectGenerator: + return HSelectGenerator::kSelectGeneratorPassName; + case OptimizationPass::kInstructionSimplifier: + return InstructionSimplifier::kInstructionSimplifierPassName; + case OptimizationPass::kIntrinsicsRecognizer: + return IntrinsicsRecognizer::kIntrinsicsRecognizerPassName; + case OptimizationPass::kCHAGuardOptimization: + return CHAGuardOptimization::kCHAGuardOptimizationPassName; + case OptimizationPass::kCodeSinking: + return CodeSinking::kCodeSinkingPassName; + case OptimizationPass::kConstructorFenceRedundancyElimination: + return ConstructorFenceRedundancyElimination::kCFREPassName; + case OptimizationPass::kScheduling: + return HInstructionScheduling::kInstructionSchedulingPassName; +#ifdef ART_ENABLE_CODEGEN_arm + case OptimizationPass::kInstructionSimplifierArm: + return arm::InstructionSimplifierArm::kInstructionSimplifierArmPassName; +#endif +#ifdef ART_ENABLE_CODEGEN_arm64 + case OptimizationPass::kInstructionSimplifierArm64: + return arm64::InstructionSimplifierArm64::kInstructionSimplifierArm64PassName; +#endif +#ifdef ART_ENABLE_CODEGEN_mips + case OptimizationPass::kPcRelativeFixupsMips: + return mips::PcRelativeFixups::kPcRelativeFixupsMipsPassName; + case OptimizationPass::kInstructionSimplifierMips: + return mips::InstructionSimplifierMips::kInstructionSimplifierMipsPassName; +#endif +#ifdef ART_ENABLE_CODEGEN_x86 + case OptimizationPass::kPcRelativeFixupsX86: + return x86::PcRelativeFixups::kPcRelativeFixupsX86PassName; +#endif +#if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64) + case OptimizationPass::kX86MemoryOperandGeneration: + return x86::X86MemoryOperandGeneration::kX86MemoryOperandGenerationPassName; +#endif + } +} + +#define X(x) if (name == OptimizationPassName((x))) return (x) + +OptimizationPass OptimizationPassByName(const std::string& name) { + X(OptimizationPass::kBoundsCheckElimination); + X(OptimizationPass::kCHAGuardOptimization); + X(OptimizationPass::kCodeSinking); + X(OptimizationPass::kConstantFolding); + X(OptimizationPass::kConstructorFenceRedundancyElimination); + X(OptimizationPass::kDeadCodeElimination); + X(OptimizationPass::kGlobalValueNumbering); + X(OptimizationPass::kInductionVarAnalysis); + X(OptimizationPass::kInliner); + X(OptimizationPass::kInstructionSimplifier); + X(OptimizationPass::kIntrinsicsRecognizer); + X(OptimizationPass::kInvariantCodeMotion); + X(OptimizationPass::kLoadStoreAnalysis); + X(OptimizationPass::kLoadStoreElimination); + X(OptimizationPass::kLoopOptimization); + X(OptimizationPass::kScheduling); + X(OptimizationPass::kSelectGenerator); + X(OptimizationPass::kSharpening); + X(OptimizationPass::kSideEffectsAnalysis); +#ifdef ART_ENABLE_CODEGEN_arm + X(OptimizationPass::kInstructionSimplifierArm); +#endif +#ifdef ART_ENABLE_CODEGEN_arm64 + X(OptimizationPass::kInstructionSimplifierArm64); +#endif +#ifdef ART_ENABLE_CODEGEN_mips + X(OptimizationPass::kPcRelativeFixupsMips); + X(OptimizationPass::kInstructionSimplifierMips); +#endif +#ifdef ART_ENABLE_CODEGEN_x86 + X(OptimizationPass::kPcRelativeFixupsX86); + X(OptimizationPass::kX86MemoryOperandGeneration); +#endif + LOG(FATAL) << "Cannot find optimization " << name; + UNREACHABLE(); +} + +#undef X + +ArenaVector ConstructOptimizations( + const OptimizationDef definitions[], + size_t length, + ArenaAllocator* allocator, + HGraph* graph, + OptimizingCompilerStats* stats, + CodeGenerator* codegen, + CompilerDriver* driver, + const DexCompilationUnit& dex_compilation_unit, + VariableSizedHandleScope* handles) { + ArenaVector optimizations(allocator->Adapter()); + + // Some optimizations require SideEffectsAnalysis or HInductionVarAnalysis + // instances. This method uses the nearest instance preceeding it in the pass + // name list or fails fatally if no such analysis can be found. + SideEffectsAnalysis* most_recent_side_effects = nullptr; + HInductionVarAnalysis* most_recent_induction = nullptr; + LoadStoreAnalysis* most_recent_lsa = nullptr; + + // Loop over the requested optimizations. + for (size_t i = 0; i < length; i++) { + OptimizationPass pass = definitions[i].first; + const char* alt_name = definitions[i].second; + const char* name = alt_name != nullptr + ? alt_name + : OptimizationPassName(pass); + HOptimization* opt = nullptr; + + switch (pass) { + // + // Analysis passes (kept in most recent for subsequent passes). + // + case OptimizationPass::kSideEffectsAnalysis: + opt = most_recent_side_effects = new (allocator) SideEffectsAnalysis(graph, name); + break; + case OptimizationPass::kInductionVarAnalysis: + opt = most_recent_induction = new (allocator) HInductionVarAnalysis(graph, name); + break; + case OptimizationPass::kLoadStoreAnalysis: + opt = most_recent_lsa = new (allocator) LoadStoreAnalysis(graph, name); + break; + // + // Passes that need prior analysis. + // + case OptimizationPass::kGlobalValueNumbering: + CHECK(most_recent_side_effects != nullptr); + opt = new (allocator) GVNOptimization(graph, *most_recent_side_effects, name); + break; + case OptimizationPass::kInvariantCodeMotion: + CHECK(most_recent_side_effects != nullptr); + opt = new (allocator) LICM(graph, *most_recent_side_effects, stats, name); + break; + case OptimizationPass::kLoopOptimization: + CHECK(most_recent_induction != nullptr); + opt = new (allocator) HLoopOptimization(graph, driver, most_recent_induction, stats, name); + break; + case OptimizationPass::kBoundsCheckElimination: + CHECK(most_recent_side_effects != nullptr && most_recent_induction != nullptr); + opt = new (allocator) BoundsCheckElimination( + graph, *most_recent_side_effects, most_recent_induction, name); + break; + case OptimizationPass::kLoadStoreElimination: + CHECK(most_recent_side_effects != nullptr && most_recent_induction != nullptr); + opt = new (allocator) LoadStoreElimination( + graph, *most_recent_side_effects, *most_recent_lsa, stats, name); + break; + // + // Regular passes. + // + case OptimizationPass::kConstantFolding: + opt = new (allocator) HConstantFolding(graph, name); + break; + case OptimizationPass::kDeadCodeElimination: + opt = new (allocator) HDeadCodeElimination(graph, stats, name); + break; + case OptimizationPass::kInliner: { + CodeItemDataAccessor accessor(dex_compilation_unit.GetDexFile(), + dex_compilation_unit.GetCodeItem()); + opt = new (allocator) HInliner(graph, // outer_graph + graph, // outermost_graph + codegen, + dex_compilation_unit, // outer_compilation_unit + dex_compilation_unit, // outermost_compilation_unit + driver, + handles, + stats, + accessor.RegistersSize(), + /* total_number_of_instructions */ 0, + /* parent */ nullptr, + /* depth */ 0, + name); + break; + } + case OptimizationPass::kSharpening: + opt = new (allocator) HSharpening(graph, codegen, driver, name); + break; + case OptimizationPass::kSelectGenerator: + opt = new (allocator) HSelectGenerator(graph, handles, stats, name); + break; + case OptimizationPass::kInstructionSimplifier: + opt = new (allocator) InstructionSimplifier(graph, codegen, driver, stats, name); + break; + case OptimizationPass::kIntrinsicsRecognizer: + opt = new (allocator) IntrinsicsRecognizer(graph, stats, name); + break; + case OptimizationPass::kCHAGuardOptimization: + opt = new (allocator) CHAGuardOptimization(graph, name); + break; + case OptimizationPass::kCodeSinking: + opt = new (allocator) CodeSinking(graph, stats, name); + break; + case OptimizationPass::kConstructorFenceRedundancyElimination: + opt = new (allocator) ConstructorFenceRedundancyElimination(graph, stats, name); + break; + case OptimizationPass::kScheduling: + opt = new (allocator) HInstructionScheduling( + graph, driver->GetInstructionSet(), codegen, name); + break; + // + // Arch-specific passes. + // +#ifdef ART_ENABLE_CODEGEN_arm + case OptimizationPass::kInstructionSimplifierArm: + DCHECK(alt_name == nullptr) << "arch-specific pass does not support alternative name"; + opt = new (allocator) arm::InstructionSimplifierArm(graph, stats); + break; +#endif +#ifdef ART_ENABLE_CODEGEN_arm64 + case OptimizationPass::kInstructionSimplifierArm64: + DCHECK(alt_name == nullptr) << "arch-specific pass does not support alternative name"; + opt = new (allocator) arm64::InstructionSimplifierArm64(graph, stats); + break; +#endif +#ifdef ART_ENABLE_CODEGEN_mips + case OptimizationPass::kPcRelativeFixupsMips: + DCHECK(alt_name == nullptr) << "arch-specific pass does not support alternative name"; + opt = new (allocator) mips::PcRelativeFixups(graph, codegen, stats); + break; + case OptimizationPass::kInstructionSimplifierMips: + DCHECK(alt_name == nullptr) << "arch-specific pass does not support alternative name"; + opt = new (allocator) mips::InstructionSimplifierMips(graph, codegen, stats); + break; +#endif +#ifdef ART_ENABLE_CODEGEN_x86 + case OptimizationPass::kPcRelativeFixupsX86: + DCHECK(alt_name == nullptr) << "arch-specific pass does not support alternative name"; + opt = new (allocator) x86::PcRelativeFixups(graph, codegen, stats); + break; + case OptimizationPass::kX86MemoryOperandGeneration: + DCHECK(alt_name == nullptr) << "arch-specific pass does not support alternative name"; + opt = new (allocator) x86::X86MemoryOperandGeneration(graph, codegen, stats); + break; +#endif + } // switch + + // Add each next optimization to result vector. + CHECK(opt != nullptr); + DCHECK_STREQ(name, opt->GetPassName()); // sanity + optimizations.push_back(opt); + } + + return optimizations; +} + } // namespace art diff --git a/compiler/optimizing/optimization.h b/compiler/optimizing/optimization.h index ce41a2e5128b41a430f9b5cbaa360b55bfcdaecc..c170f155fac1ee74ecffc82914315776d7978aeb 100644 --- a/compiler/optimizing/optimization.h +++ b/compiler/optimizing/optimization.h @@ -23,6 +23,10 @@ namespace art { +class CodeGenerator; +class CompilerDriver; +class DexCompilationUnit; + /** * Abstraction to implement an optimization pass. */ @@ -58,6 +62,81 @@ class HOptimization : public ArenaObject { DISALLOW_COPY_AND_ASSIGN(HOptimization); }; +// Optimization passes that can be constructed by the helper method below. An enum +// field is preferred over a string lookup at places where performance matters. +// TODO: generate this table and lookup methods below automatically? +enum class OptimizationPass { + kBoundsCheckElimination, + kCHAGuardOptimization, + kCodeSinking, + kConstantFolding, + kConstructorFenceRedundancyElimination, + kDeadCodeElimination, + kGlobalValueNumbering, + kInductionVarAnalysis, + kInliner, + kInstructionSimplifier, + kIntrinsicsRecognizer, + kInvariantCodeMotion, + kLoadStoreAnalysis, + kLoadStoreElimination, + kLoopOptimization, + kScheduling, + kSelectGenerator, + kSharpening, + kSideEffectsAnalysis, +#ifdef ART_ENABLE_CODEGEN_arm + kInstructionSimplifierArm, +#endif +#ifdef ART_ENABLE_CODEGEN_arm64 + kInstructionSimplifierArm64, +#endif +#ifdef ART_ENABLE_CODEGEN_mips + kPcRelativeFixupsMips, + kInstructionSimplifierMips, +#endif +#ifdef ART_ENABLE_CODEGEN_x86 + kPcRelativeFixupsX86, +#endif +#if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64) + kX86MemoryOperandGeneration, +#endif +}; + +// Lookup name of optimization pass. +const char* OptimizationPassName(OptimizationPass pass); + +// Lookup optimization pass by name. +OptimizationPass OptimizationPassByName(const std::string& name); + +// Optimization definition consisting of an optimization pass +// and an optional alternative name (nullptr denotes default). +typedef std::pair OptimizationDef; + +// Helper method for optimization definition array entries. +inline OptimizationDef OptDef(OptimizationPass pass, const char* name = nullptr) { + return std::make_pair(pass, name); +} + +// Helper method to construct series of optimization passes. +// The array should consist of the requested optimizations +// and optional alternative names for repeated passes. +// Example: +// { OptPass(kConstantFolding), +// OptPass(Inliner), +// OptPass(kConstantFolding, "constant_folding$after_inlining") +// } +ArenaVector ConstructOptimizations( + const OptimizationDef definitions[], + size_t length, + ArenaAllocator* allocator, + HGraph* graph, + OptimizingCompilerStats* stats, + CodeGenerator* codegen, + CompilerDriver* driver, + const DexCompilationUnit& dex_compilation_unit, + VariableSizedHandleScope* handles); + } // namespace art #endif // ART_COMPILER_OPTIMIZING_OPTIMIZATION_H_ diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 9233eb5bafc55d6710c9cb26979914d1f8ed4d5a..9d04dd83435d843f70b32960b19c92b04f07c251 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -22,23 +22,6 @@ #include -#ifdef ART_ENABLE_CODEGEN_arm64 -#include "instruction_simplifier_arm64.h" -#endif - -#ifdef ART_ENABLE_CODEGEN_mips -#include "instruction_simplifier_mips.h" -#include "pc_relative_fixups_mips.h" -#endif - -#ifdef ART_ENABLE_CODEGEN_x86 -#include "pc_relative_fixups_x86.h" -#endif - -#if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64) -#include "x86_memory_gen.h" -#endif - #include "art_method-inl.h" #include "base/arena_allocator.h" #include "base/arena_containers.h" @@ -47,16 +30,10 @@ #include "base/mutex.h" #include "base/scoped_arena_allocator.h" #include "base/timing_logger.h" -#include "bounds_check_elimination.h" #include "builder.h" -#include "cha_guard_optimization.h" #include "code_generator.h" -#include "code_sinking.h" #include "compiled_method.h" #include "compiler.h" -#include "constant_folding.h" -#include "constructor_fence_redundancy_elimination.h" -#include "dead_code_elimination.h" #include "debug/elf_debug_writer.h" #include "debug/method_debug_info.h" #include "dex/verification_results.h" @@ -67,31 +44,19 @@ #include "driver/dex_compilation_unit.h" #include "graph_checker.h" #include "graph_visualizer.h" -#include "gvn.h" -#include "induction_var_analysis.h" #include "inliner.h" -#include "instruction_simplifier.h" -#include "instruction_simplifier_arm.h" -#include "intrinsics.h" #include "jit/debugger_interface.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" #include "jit/jit_logger.h" #include "jni/quick/jni_compiler.h" -#include "licm.h" #include "linker/linker_patch.h" -#include "load_store_analysis.h" -#include "load_store_elimination.h" -#include "loop_optimization.h" #include "nodes.h" #include "oat_quick_method_header.h" #include "prepare_for_register_allocation.h" #include "reference_type_propagation.h" #include "register_allocator_linear_scan.h" -#include "scheduler.h" #include "select_generator.h" -#include "sharpening.h" -#include "side_effects_analysis.h" #include "ssa_builder.h" #include "ssa_liveness_analysis.h" #include "ssa_phi_elimination.h" @@ -147,7 +112,7 @@ class PassObserver : public ValueObject { Mutex& dump_mutex) : graph_(graph), cached_method_name_(), - timing_logger_enabled_(compiler_driver->GetDumpPasses()), + timing_logger_enabled_(compiler_driver->GetCompilerOptions().GetDumpTimings()), timing_logger_(timing_logger_enabled_ ? GetMethodName() : "", true, true), disasm_info_(graph->GetAllocator()), visualizer_oss_(), @@ -312,13 +277,7 @@ class OptimizingCompiler FINAL : public Compiler { CompiledMethod* JniCompile(uint32_t access_flags, uint32_t method_idx, const DexFile& dex_file, - JniOptimizationFlags optimization_flags) const OVERRIDE { - return ArtQuickJniCompileMethod(GetCompilerDriver(), - access_flags, - method_idx, - dex_file, - optimization_flags); - } + Handle dex_cache) const OVERRIDE; uintptr_t GetEntryPointOf(ArtMethod* method) const OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { @@ -341,21 +300,52 @@ class OptimizingCompiler FINAL : public Compiler { private: void RunOptimizations(HGraph* graph, CodeGenerator* codegen, - CompilerDriver* driver, const DexCompilationUnit& dex_compilation_unit, PassObserver* pass_observer, - VariableSizedHandleScope* handles) const; + VariableSizedHandleScope* handles, + const OptimizationDef definitions[], + size_t length) const { + // Convert definitions to optimization passes. + ArenaVector optimizations = ConstructOptimizations( + definitions, + length, + graph->GetAllocator(), + graph, + compilation_stats_.get(), + codegen, + GetCompilerDriver(), + dex_compilation_unit, + handles); + DCHECK_EQ(length, optimizations.size()); + // Run the optimization passes one by one. + for (size_t i = 0; i < length; ++i) { + PassScope scope(optimizations[i]->GetPassName(), pass_observer); + optimizations[i]->Run(); + } + } + + template void RunOptimizations( + HGraph* graph, + CodeGenerator* codegen, + const DexCompilationUnit& dex_compilation_unit, + PassObserver* pass_observer, + VariableSizedHandleScope* handles, + const OptimizationDef (&definitions)[length]) const { + RunOptimizations( + graph, codegen, dex_compilation_unit, pass_observer, handles, definitions, length); + } - void RunOptimizations(HOptimization* optimizations[], - size_t length, - PassObserver* pass_observer) const; + void RunOptimizations(HGraph* graph, + CodeGenerator* codegen, + const DexCompilationUnit& dex_compilation_unit, + PassObserver* pass_observer, + VariableSizedHandleScope* handles) const; private: // Create a 'CompiledMethod' for an optimized graph. CompiledMethod* Emit(ArenaAllocator* allocator, CodeVectorAllocator* code_allocator, CodeGenerator* codegen, - CompilerDriver* driver, const DexFile::CodeItem* item) const; // Try compiling a method and return the code generator used for @@ -368,29 +358,29 @@ class OptimizingCompiler FINAL : public Compiler { CodeGenerator* TryCompile(ArenaAllocator* allocator, ArenaStack* arena_stack, CodeVectorAllocator* code_allocator, - const DexFile::CodeItem* code_item, - uint32_t access_flags, - InvokeType invoke_type, - uint16_t class_def_idx, - uint32_t method_idx, - Handle class_loader, - const DexFile& dex_file, - Handle dex_cache, + const DexCompilationUnit& dex_compilation_unit, ArtMethod* method, bool osr, VariableSizedHandleScope* handles) const; + CodeGenerator* TryCompileIntrinsic(ArenaAllocator* allocator, + ArenaStack* arena_stack, + CodeVectorAllocator* code_allocator, + const DexCompilationUnit& dex_compilation_unit, + ArtMethod* method, + VariableSizedHandleScope* handles) const; + void MaybeRunInliner(HGraph* graph, CodeGenerator* codegen, - CompilerDriver* driver, const DexCompilationUnit& dex_compilation_unit, PassObserver* pass_observer, VariableSizedHandleScope* handles) const; - void RunArchOptimizations(InstructionSet instruction_set, - HGraph* graph, + void RunArchOptimizations(HGraph* graph, CodeGenerator* codegen, - PassObserver* pass_observer) const; + const DexCompilationUnit& dex_compilation_unit, + PassObserver* pass_observer, + VariableSizedHandleScope* handles) const; std::unique_ptr compilation_stats_; @@ -417,7 +407,7 @@ void OptimizingCompiler::Init() { driver->GetCompilerOptions().GetDumpCfgAppend() ? std::ofstream::app : std::ofstream::out; visualizer_output_.reset(new std::ofstream(cfg_file_name, cfg_file_mode)); } - if (driver->GetDumpStats()) { + if (driver->GetCompilerOptions().GetDumpStats()) { compilation_stats_.reset(new OptimizingCompilerStats()); } } @@ -446,299 +436,130 @@ static bool IsInstructionSetSupported(InstructionSet instruction_set) { || instruction_set == InstructionSet::kX86_64; } -// Strip pass name suffix to get optimization name. -static std::string ConvertPassNameToOptimizationName(const std::string& pass_name) { - size_t pos = pass_name.find(kPassNameSeparator); - return pos == std::string::npos ? pass_name : pass_name.substr(0, pos); -} - -static HOptimization* BuildOptimization( - const std::string& pass_name, - ArenaAllocator* allocator, - HGraph* graph, - OptimizingCompilerStats* stats, - CodeGenerator* codegen, - CompilerDriver* driver, - const DexCompilationUnit& dex_compilation_unit, - VariableSizedHandleScope* handles, - SideEffectsAnalysis* most_recent_side_effects, - HInductionVarAnalysis* most_recent_induction, - LoadStoreAnalysis* most_recent_lsa) { - std::string opt_name = ConvertPassNameToOptimizationName(pass_name); - if (opt_name == BoundsCheckElimination::kBoundsCheckEliminationPassName) { - CHECK(most_recent_side_effects != nullptr && most_recent_induction != nullptr); - return new (allocator) BoundsCheckElimination(graph, - *most_recent_side_effects, - most_recent_induction); - } else if (opt_name == GVNOptimization::kGlobalValueNumberingPassName) { - CHECK(most_recent_side_effects != nullptr); - return new (allocator) GVNOptimization(graph, *most_recent_side_effects, pass_name.c_str()); - } else if (opt_name == HConstantFolding::kConstantFoldingPassName) { - return new (allocator) HConstantFolding(graph, pass_name.c_str()); - } else if (opt_name == HDeadCodeElimination::kDeadCodeEliminationPassName) { - return new (allocator) HDeadCodeElimination(graph, stats, pass_name.c_str()); - } else if (opt_name == HInliner::kInlinerPassName) { - size_t number_of_dex_registers = dex_compilation_unit.GetCodeItem()->registers_size_; - return new (allocator) HInliner(graph, // outer_graph - graph, // outermost_graph - codegen, - dex_compilation_unit, // outer_compilation_unit - dex_compilation_unit, // outermost_compilation_unit - driver, - handles, - stats, - number_of_dex_registers, - /* total_number_of_instructions */ 0, - /* parent */ nullptr); - } else if (opt_name == HSharpening::kSharpeningPassName) { - return new (allocator) HSharpening(graph, codegen, dex_compilation_unit, driver, handles); - } else if (opt_name == HSelectGenerator::kSelectGeneratorPassName) { - return new (allocator) HSelectGenerator(graph, handles, stats); - } else if (opt_name == HInductionVarAnalysis::kInductionPassName) { - return new (allocator) HInductionVarAnalysis(graph); - } else if (opt_name == InstructionSimplifier::kInstructionSimplifierPassName) { - return new (allocator) InstructionSimplifier(graph, codegen, driver, stats, pass_name.c_str()); - } else if (opt_name == IntrinsicsRecognizer::kIntrinsicsRecognizerPassName) { - return new (allocator) IntrinsicsRecognizer(graph, stats); - } else if (opt_name == LICM::kLoopInvariantCodeMotionPassName) { - CHECK(most_recent_side_effects != nullptr); - return new (allocator) LICM(graph, *most_recent_side_effects, stats); - } else if (opt_name == LoadStoreAnalysis::kLoadStoreAnalysisPassName) { - return new (allocator) LoadStoreAnalysis(graph); - } else if (opt_name == LoadStoreElimination::kLoadStoreEliminationPassName) { - CHECK(most_recent_side_effects != nullptr); - CHECK(most_recent_lsa != nullptr); - return new (allocator) LoadStoreElimination(graph, - *most_recent_side_effects, - *most_recent_lsa, stats); - } else if (opt_name == SideEffectsAnalysis::kSideEffectsAnalysisPassName) { - return new (allocator) SideEffectsAnalysis(graph); - } else if (opt_name == HLoopOptimization::kLoopOptimizationPassName) { - return new (allocator) HLoopOptimization(graph, driver, most_recent_induction, stats); - } else if (opt_name == CHAGuardOptimization::kCHAGuardOptimizationPassName) { - return new (allocator) CHAGuardOptimization(graph); - } else if (opt_name == CodeSinking::kCodeSinkingPassName) { - return new (allocator) CodeSinking(graph, stats); - } else if (opt_name == ConstructorFenceRedundancyElimination::kPassName) { - return new (allocator) ConstructorFenceRedundancyElimination(graph, stats); -#ifdef ART_ENABLE_CODEGEN_arm - } else if (opt_name == arm::InstructionSimplifierArm::kInstructionSimplifierArmPassName) { - return new (allocator) arm::InstructionSimplifierArm(graph, stats); -#endif -#ifdef ART_ENABLE_CODEGEN_arm64 - } else if (opt_name == arm64::InstructionSimplifierArm64::kInstructionSimplifierArm64PassName) { - return new (allocator) arm64::InstructionSimplifierArm64(graph, stats); -#endif -#ifdef ART_ENABLE_CODEGEN_mips - } else if (opt_name == mips::PcRelativeFixups::kPcRelativeFixupsMipsPassName) { - return new (allocator) mips::PcRelativeFixups(graph, codegen, stats); - } else if (opt_name == mips::InstructionSimplifierMips::kInstructionSimplifierMipsPassName) { - return new (allocator) mips::InstructionSimplifierMips(graph, codegen, stats); -#endif -#ifdef ART_ENABLE_CODEGEN_x86 - } else if (opt_name == x86::PcRelativeFixups::kPcRelativeFixupsX86PassName) { - return new (allocator) x86::PcRelativeFixups(graph, codegen, stats); - } else if (opt_name == x86::X86MemoryOperandGeneration::kX86MemoryOperandGenerationPassName) { - return new (allocator) x86::X86MemoryOperandGeneration(graph, codegen, stats); -#endif - } - return nullptr; -} - -static ArenaVector BuildOptimizations( - const std::vector& pass_names, - ArenaAllocator* allocator, - HGraph* graph, - OptimizingCompilerStats* stats, - CodeGenerator* codegen, - CompilerDriver* driver, - const DexCompilationUnit& dex_compilation_unit, - VariableSizedHandleScope* handles) { - // Few HOptimizations constructors require SideEffectsAnalysis or HInductionVarAnalysis - // instances. This method assumes that each of them expects the nearest instance preceeding it - // in the pass name list. - SideEffectsAnalysis* most_recent_side_effects = nullptr; - HInductionVarAnalysis* most_recent_induction = nullptr; - LoadStoreAnalysis* most_recent_lsa = nullptr; - ArenaVector ret(allocator->Adapter()); - for (const std::string& pass_name : pass_names) { - HOptimization* opt = BuildOptimization( - pass_name, - allocator, - graph, - stats, - codegen, - driver, - dex_compilation_unit, - handles, - most_recent_side_effects, - most_recent_induction, - most_recent_lsa); - CHECK(opt != nullptr) << "Couldn't build optimization: \"" << pass_name << "\""; - ret.push_back(opt); - - std::string opt_name = ConvertPassNameToOptimizationName(pass_name); - if (opt_name == SideEffectsAnalysis::kSideEffectsAnalysisPassName) { - most_recent_side_effects = down_cast(opt); - } else if (opt_name == HInductionVarAnalysis::kInductionPassName) { - most_recent_induction = down_cast(opt); - } else if (opt_name == LoadStoreAnalysis::kLoadStoreAnalysisPassName) { - most_recent_lsa = down_cast(opt); - } - } - return ret; -} - -void OptimizingCompiler::RunOptimizations(HOptimization* optimizations[], - size_t length, - PassObserver* pass_observer) const { - for (size_t i = 0; i < length; ++i) { - PassScope scope(optimizations[i]->GetPassName(), pass_observer); - optimizations[i]->Run(); - } -} - void OptimizingCompiler::MaybeRunInliner(HGraph* graph, CodeGenerator* codegen, - CompilerDriver* driver, const DexCompilationUnit& dex_compilation_unit, PassObserver* pass_observer, VariableSizedHandleScope* handles) const { - OptimizingCompilerStats* stats = compilation_stats_.get(); - const CompilerOptions& compiler_options = driver->GetCompilerOptions(); + const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions(); bool should_inline = (compiler_options.GetInlineMaxCodeUnits() > 0); if (!should_inline) { return; } - size_t number_of_dex_registers = dex_compilation_unit.GetCodeItem()->registers_size_; - HInliner* inliner = new (graph->GetAllocator()) HInliner( - graph, // outer_graph - graph, // outermost_graph - codegen, - dex_compilation_unit, // outer_compilation_unit - dex_compilation_unit, // outermost_compilation_unit - driver, - handles, - stats, - number_of_dex_registers, - /* total_number_of_instructions */ 0, - /* parent */ nullptr); - HOptimization* optimizations[] = { inliner }; - - RunOptimizations(optimizations, arraysize(optimizations), pass_observer); + OptimizationDef optimizations[] = { + OptDef(OptimizationPass::kInliner) + }; + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + optimizations); } -void OptimizingCompiler::RunArchOptimizations(InstructionSet instruction_set, - HGraph* graph, +void OptimizingCompiler::RunArchOptimizations(HGraph* graph, CodeGenerator* codegen, - PassObserver* pass_observer) const { - UNUSED(codegen); // To avoid compilation error when compiling for svelte - OptimizingCompilerStats* stats = compilation_stats_.get(); - ArenaAllocator* allocator = graph->GetAllocator(); - switch (instruction_set) { + const DexCompilationUnit& dex_compilation_unit, + PassObserver* pass_observer, + VariableSizedHandleScope* handles) const { + switch (GetCompilerDriver()->GetInstructionSet()) { #if defined(ART_ENABLE_CODEGEN_arm) case InstructionSet::kThumb2: case InstructionSet::kArm: { - arm::InstructionSimplifierArm* simplifier = - new (allocator) arm::InstructionSimplifierArm(graph, stats); - SideEffectsAnalysis* side_effects = new (allocator) SideEffectsAnalysis(graph); - GVNOptimization* gvn = - new (allocator) GVNOptimization(graph, *side_effects, "GVN$after_arch"); - HInstructionScheduling* scheduling = - new (allocator) HInstructionScheduling(graph, instruction_set, codegen); - HOptimization* arm_optimizations[] = { - simplifier, - side_effects, - gvn, - scheduling, + OptimizationDef arm_optimizations[] = { + OptDef(OptimizationPass::kInstructionSimplifierArm), + OptDef(OptimizationPass::kSideEffectsAnalysis), + OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"), + OptDef(OptimizationPass::kScheduling) }; - RunOptimizations(arm_optimizations, arraysize(arm_optimizations), pass_observer); + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + arm_optimizations); break; } #endif #ifdef ART_ENABLE_CODEGEN_arm64 case InstructionSet::kArm64: { - arm64::InstructionSimplifierArm64* simplifier = - new (allocator) arm64::InstructionSimplifierArm64(graph, stats); - SideEffectsAnalysis* side_effects = new (allocator) SideEffectsAnalysis(graph); - GVNOptimization* gvn = - new (allocator) GVNOptimization(graph, *side_effects, "GVN$after_arch"); - HInstructionScheduling* scheduling = - new (allocator) HInstructionScheduling(graph, instruction_set); - HOptimization* arm64_optimizations[] = { - simplifier, - side_effects, - gvn, - scheduling, + OptimizationDef arm64_optimizations[] = { + OptDef(OptimizationPass::kInstructionSimplifierArm64), + OptDef(OptimizationPass::kSideEffectsAnalysis), + OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"), + OptDef(OptimizationPass::kScheduling) }; - RunOptimizations(arm64_optimizations, arraysize(arm64_optimizations), pass_observer); + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + arm64_optimizations); break; } #endif #ifdef ART_ENABLE_CODEGEN_mips case InstructionSet::kMips: { - mips::InstructionSimplifierMips* simplifier = - new (allocator) mips::InstructionSimplifierMips(graph, codegen, stats); - SideEffectsAnalysis* side_effects = new (allocator) SideEffectsAnalysis(graph); - GVNOptimization* gvn = - new (allocator) GVNOptimization(graph, *side_effects, "GVN$after_arch"); - mips::PcRelativeFixups* pc_relative_fixups = - new (allocator) mips::PcRelativeFixups(graph, codegen, stats); - HOptimization* mips_optimizations[] = { - simplifier, - side_effects, - gvn, - pc_relative_fixups, + OptimizationDef mips_optimizations[] = { + OptDef(OptimizationPass::kInstructionSimplifierMips), + OptDef(OptimizationPass::kSideEffectsAnalysis), + OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"), + OptDef(OptimizationPass::kPcRelativeFixupsMips) }; - RunOptimizations(mips_optimizations, arraysize(mips_optimizations), pass_observer); + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + mips_optimizations); break; } #endif #ifdef ART_ENABLE_CODEGEN_mips64 case InstructionSet::kMips64: { - SideEffectsAnalysis* side_effects = new (allocator) SideEffectsAnalysis(graph); - GVNOptimization* gvn = - new (allocator) GVNOptimization(graph, *side_effects, "GVN$after_arch"); - HOptimization* mips64_optimizations[] = { - side_effects, - gvn, + OptimizationDef mips64_optimizations[] = { + OptDef(OptimizationPass::kSideEffectsAnalysis), + OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch") }; - RunOptimizations(mips64_optimizations, arraysize(mips64_optimizations), pass_observer); + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + mips64_optimizations); break; } #endif #ifdef ART_ENABLE_CODEGEN_x86 case InstructionSet::kX86: { - SideEffectsAnalysis* side_effects = new (allocator) SideEffectsAnalysis(graph); - GVNOptimization* gvn = - new (allocator) GVNOptimization(graph, *side_effects, "GVN$after_arch"); - x86::PcRelativeFixups* pc_relative_fixups = - new (allocator) x86::PcRelativeFixups(graph, codegen, stats); - x86::X86MemoryOperandGeneration* memory_gen = - new (allocator) x86::X86MemoryOperandGeneration(graph, codegen, stats); - HOptimization* x86_optimizations[] = { - side_effects, - gvn, - pc_relative_fixups, - memory_gen + OptimizationDef x86_optimizations[] = { + OptDef(OptimizationPass::kSideEffectsAnalysis), + OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"), + OptDef(OptimizationPass::kPcRelativeFixupsX86), + OptDef(OptimizationPass::kX86MemoryOperandGeneration) }; - RunOptimizations(x86_optimizations, arraysize(x86_optimizations), pass_observer); + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + x86_optimizations); break; } #endif #ifdef ART_ENABLE_CODEGEN_x86_64 case InstructionSet::kX86_64: { - SideEffectsAnalysis* side_effects = new (allocator) SideEffectsAnalysis(graph); - GVNOptimization* gvn = - new (allocator) GVNOptimization(graph, *side_effects, "GVN$after_arch"); - x86::X86MemoryOperandGeneration* memory_gen = - new (allocator) x86::X86MemoryOperandGeneration(graph, codegen, stats); - HOptimization* x86_64_optimizations[] = { - side_effects, - gvn, - memory_gen + OptimizationDef x86_64_optimizations[] = { + OptDef(OptimizationPass::kSideEffectsAnalysis), + OptDef(OptimizationPass::kGlobalValueNumbering, "GVN$after_arch"), + OptDef(OptimizationPass::kX86MemoryOperandGeneration) }; - RunOptimizations(x86_64_optimizations, arraysize(x86_64_optimizations), pass_observer); + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + x86_64_optimizations); break; } #endif @@ -774,110 +595,93 @@ static void AllocateRegisters(HGraph* graph, } } +// Strip pass name suffix to get optimization name. +static std::string ConvertPassNameToOptimizationName(const std::string& pass_name) { + size_t pos = pass_name.find(kPassNameSeparator); + return pos == std::string::npos ? pass_name : pass_name.substr(0, pos); +} + void OptimizingCompiler::RunOptimizations(HGraph* graph, CodeGenerator* codegen, - CompilerDriver* driver, const DexCompilationUnit& dex_compilation_unit, PassObserver* pass_observer, VariableSizedHandleScope* handles) const { - OptimizingCompilerStats* stats = compilation_stats_.get(); - ArenaAllocator* allocator = graph->GetAllocator(); - if (driver->GetCompilerOptions().GetPassesToRun() != nullptr) { - ArenaVector optimizations = BuildOptimizations( - *driver->GetCompilerOptions().GetPassesToRun(), - allocator, - graph, - stats, - codegen, - driver, - dex_compilation_unit, - handles); - RunOptimizations(&optimizations[0], optimizations.size(), pass_observer); + const std::vector* pass_names = + GetCompilerDriver()->GetCompilerOptions().GetPassesToRun(); + if (pass_names != nullptr) { + // If passes were defined on command-line, build the optimization + // passes and run these instead of the built-in optimizations. + const size_t length = pass_names->size(); + std::vector optimizations; + for (const std::string& pass_name : *pass_names) { + std::string opt_name = ConvertPassNameToOptimizationName(pass_name); + optimizations.push_back(OptDef(OptimizationPassByName(opt_name.c_str()), pass_name.c_str())); + } + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + optimizations.data(), + length); return; } - HDeadCodeElimination* dce1 = new (allocator) HDeadCodeElimination( - graph, stats, "dead_code_elimination$initial"); - HDeadCodeElimination* dce2 = new (allocator) HDeadCodeElimination( - graph, stats, "dead_code_elimination$after_inlining"); - HDeadCodeElimination* dce3 = new (allocator) HDeadCodeElimination( - graph, stats, "dead_code_elimination$final"); - HConstantFolding* fold1 = new (allocator) HConstantFolding(graph, "constant_folding"); - InstructionSimplifier* simplify1 = new (allocator) InstructionSimplifier( - graph, codegen, driver, stats); - HSelectGenerator* select_generator = new (allocator) HSelectGenerator(graph, handles, stats); - HConstantFolding* fold2 = new (allocator) HConstantFolding( - graph, "constant_folding$after_inlining"); - HConstantFolding* fold3 = new (allocator) HConstantFolding(graph, "constant_folding$after_bce"); - SideEffectsAnalysis* side_effects1 = new (allocator) SideEffectsAnalysis( - graph, "side_effects$before_gvn"); - SideEffectsAnalysis* side_effects2 = new (allocator) SideEffectsAnalysis( - graph, "side_effects$before_lse"); - GVNOptimization* gvn = new (allocator) GVNOptimization(graph, *side_effects1); - LICM* licm = new (allocator) LICM(graph, *side_effects1, stats); - HInductionVarAnalysis* induction = new (allocator) HInductionVarAnalysis(graph); - BoundsCheckElimination* bce = - new (allocator) BoundsCheckElimination(graph, *side_effects1, induction); - HLoopOptimization* loop = new (allocator) HLoopOptimization(graph, driver, induction, stats); - LoadStoreAnalysis* lsa = new (allocator) LoadStoreAnalysis(graph); - LoadStoreElimination* lse = - new (allocator) LoadStoreElimination(graph, *side_effects2, *lsa, stats); - HSharpening* sharpening = new (allocator) HSharpening( - graph, codegen, dex_compilation_unit, driver, handles); - InstructionSimplifier* simplify2 = new (allocator) InstructionSimplifier( - graph, codegen, driver, stats, "instruction_simplifier$after_inlining"); - InstructionSimplifier* simplify3 = new (allocator) InstructionSimplifier( - graph, codegen, driver, stats, "instruction_simplifier$after_bce"); - InstructionSimplifier* simplify4 = new (allocator) InstructionSimplifier( - graph, codegen, driver, stats, "instruction_simplifier$before_codegen"); - IntrinsicsRecognizer* intrinsics = new (allocator) IntrinsicsRecognizer(graph, stats); - CHAGuardOptimization* cha_guard = new (allocator) CHAGuardOptimization(graph); - CodeSinking* code_sinking = new (allocator) CodeSinking(graph, stats); - ConstructorFenceRedundancyElimination* cfre = - new (allocator) ConstructorFenceRedundancyElimination(graph, stats); - - HOptimization* optimizations1[] = { - intrinsics, - sharpening, - fold1, - simplify1, - dce1, + OptimizationDef optimizations1[] = { + OptDef(OptimizationPass::kIntrinsicsRecognizer), + OptDef(OptimizationPass::kSharpening), + OptDef(OptimizationPass::kConstantFolding), + OptDef(OptimizationPass::kInstructionSimplifier), + OptDef(OptimizationPass::kDeadCodeElimination, "dead_code_elimination$initial") }; - RunOptimizations(optimizations1, arraysize(optimizations1), pass_observer); + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + optimizations1); - MaybeRunInliner(graph, codegen, driver, dex_compilation_unit, pass_observer, handles); + MaybeRunInliner(graph, codegen, dex_compilation_unit, pass_observer, handles); - HOptimization* optimizations2[] = { + OptimizationDef optimizations2[] = { // SelectGenerator depends on the InstructionSimplifier removing // redundant suspend checks to recognize empty blocks. - select_generator, - fold2, // TODO: if we don't inline we can also skip fold2. - simplify2, - dce2, - side_effects1, - gvn, - licm, - induction, - bce, - loop, - fold3, // evaluates code generated by dynamic bce - simplify3, - side_effects2, - lsa, - lse, - cha_guard, - dce3, - code_sinking, + OptDef(OptimizationPass::kSelectGenerator), + // TODO: if we don't inline we can also skip fold2. + OptDef(OptimizationPass::kConstantFolding, "constant_folding$after_inlining"), + OptDef(OptimizationPass::kInstructionSimplifier, "instruction_simplifier$after_inlining"), + OptDef(OptimizationPass::kDeadCodeElimination, "dead_code_elimination$after_inlining"), + OptDef(OptimizationPass::kSideEffectsAnalysis, "side_effects$before_gvn"), + OptDef(OptimizationPass::kGlobalValueNumbering), + OptDef(OptimizationPass::kInvariantCodeMotion), + OptDef(OptimizationPass::kInductionVarAnalysis), + OptDef(OptimizationPass::kBoundsCheckElimination), + OptDef(OptimizationPass::kLoopOptimization), + // Evaluates code generated by dynamic bce. + OptDef(OptimizationPass::kConstantFolding, "constant_folding$after_bce"), + OptDef(OptimizationPass::kInstructionSimplifier, "instruction_simplifier$after_bce"), + OptDef(OptimizationPass::kSideEffectsAnalysis, "side_effects$before_lse"), + OptDef(OptimizationPass::kLoadStoreAnalysis), + OptDef(OptimizationPass::kLoadStoreElimination), + OptDef(OptimizationPass::kCHAGuardOptimization), + OptDef(OptimizationPass::kDeadCodeElimination, "dead_code_elimination$final"), + OptDef(OptimizationPass::kCodeSinking), // The codegen has a few assumptions that only the instruction simplifier // can satisfy. For example, the code generator does not expect to see a // HTypeConversion from a type to the same type. - simplify4, - cfre, // Eliminate constructor fences after code sinking to avoid - // complicated sinking logic to split a fence with many inputs. + OptDef(OptimizationPass::kInstructionSimplifier, "instruction_simplifier$before_codegen"), + // Eliminate constructor fences after code sinking to avoid + // complicated sinking logic to split a fence with many inputs. + OptDef(OptimizationPass::kConstructorFenceRedundancyElimination) }; - RunOptimizations(optimizations2, arraysize(optimizations2), pass_observer); + RunOptimizations(graph, + codegen, + dex_compilation_unit, + pass_observer, + handles, + optimizations2); - RunArchOptimizations(driver->GetInstructionSet(), graph, codegen, pass_observer); + RunArchOptimizations(graph, codegen, dex_compilation_unit, pass_observer, handles); } static ArenaVector EmitAndSortLinkerPatches(CodeGenerator* codegen) { @@ -896,8 +700,7 @@ static ArenaVector EmitAndSortLinkerPatches(CodeGenerator* CompiledMethod* OptimizingCompiler::Emit(ArenaAllocator* allocator, CodeVectorAllocator* code_allocator, CodeGenerator* codegen, - CompilerDriver* compiler_driver, - const DexFile::CodeItem* code_item) const { + const DexFile::CodeItem* code_item_for_osr_check) const { ArenaVector linker_patches = EmitAndSortLinkerPatches(codegen); ArenaVector stack_map(allocator->Adapter(kArenaAllocStackMaps)); ArenaVector method_info(allocator->Adapter(kArenaAllocStackMaps)); @@ -908,10 +711,10 @@ CompiledMethod* OptimizingCompiler::Emit(ArenaAllocator* allocator, method_info.resize(method_info_size); codegen->BuildStackMaps(MemoryRegion(stack_map.data(), stack_map.size()), MemoryRegion(method_info.data(), method_info.size()), - *code_item); + code_item_for_osr_check); CompiledMethod* compiled_method = CompiledMethod::SwapAllocCompiledMethod( - compiler_driver, + GetCompilerDriver(), codegen->GetInstructionSet(), ArrayRef(code_allocator->GetMemory()), // Follow Quick's behavior and set the frame size to zero if it is @@ -931,21 +734,16 @@ CompiledMethod* OptimizingCompiler::Emit(ArenaAllocator* allocator, CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, ArenaStack* arena_stack, CodeVectorAllocator* code_allocator, - const DexFile::CodeItem* code_item, - uint32_t access_flags, - InvokeType invoke_type, - uint16_t class_def_idx, - uint32_t method_idx, - Handle class_loader, - const DexFile& dex_file, - Handle dex_cache, + const DexCompilationUnit& dex_compilation_unit, ArtMethod* method, bool osr, VariableSizedHandleScope* handles) const { - MaybeRecordStat(compilation_stats_.get(), - MethodCompilationStat::kAttemptCompilation); + MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kAttemptBytecodeCompilation); CompilerDriver* compiler_driver = GetCompilerDriver(); InstructionSet instruction_set = compiler_driver->GetInstructionSet(); + const DexFile& dex_file = *dex_compilation_unit.GetDexFile(); + uint32_t method_idx = dex_compilation_unit.GetDexMethodIndex(); + const DexFile::CodeItem* code_item = dex_compilation_unit.GetCodeItem(); // Always use the Thumb-2 assembler: some runtime functionality // (like implicit stack overflow checks) assume Thumb-2. @@ -959,8 +757,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, } if (Compiler::IsPathologicalCase(*code_item, method_idx, dex_file)) { - MaybeRecordStat(compilation_stats_.get(), - MethodCompilationStat::kNotCompiledPathological); + MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kNotCompiledPathological); return nullptr; } @@ -969,24 +766,13 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, static constexpr size_t kSpaceFilterOptimizingThreshold = 128; const CompilerOptions& compiler_options = compiler_driver->GetCompilerOptions(); if ((compiler_options.GetCompilerFilter() == CompilerFilter::kSpace) - && (code_item->insns_size_in_code_units_ > kSpaceFilterOptimizingThreshold)) { - MaybeRecordStat(compilation_stats_.get(), - MethodCompilationStat::kNotCompiledSpaceFilter); + && (CodeItemInstructionAccessor(&dex_file, code_item).InsnsSizeInCodeUnits() > + kSpaceFilterOptimizingThreshold)) { + MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kNotCompiledSpaceFilter); return nullptr; } - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - DexCompilationUnit dex_compilation_unit( - class_loader, - class_linker, - dex_file, - code_item, - class_def_idx, - method_idx, - access_flags, - /* verified_method */ nullptr, - dex_cache); - + CodeItemDebugInfoAccessor code_item_accessor(&dex_file, code_item); HGraph* graph = new (allocator) HGraph( allocator, arena_stack, @@ -998,18 +784,13 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, osr); const uint8_t* interpreter_metadata = nullptr; - if (method == nullptr) { - ScopedObjectAccess soa(Thread::Current()); - method = compiler_driver->ResolveMethod( - soa, dex_cache, class_loader, &dex_compilation_unit, method_idx, invoke_type); - } // For AOT compilation, we may not get a method, for example if its class is erroneous. // JIT should always have a method. DCHECK(Runtime::Current()->IsAotCompiler() || method != nullptr); if (method != nullptr) { graph->SetArtMethod(method); ScopedObjectAccess soa(Thread::Current()); - interpreter_metadata = method->GetQuickenedInfo(class_linker->GetImagePointerSize()); + interpreter_metadata = method->GetQuickenedInfo(); } std::unique_ptr codegen( @@ -1019,8 +800,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, compiler_driver->GetCompilerOptions(), compilation_stats_.get())); if (codegen.get() == nullptr) { - MaybeRecordStat(compilation_stats_.get(), - MethodCompilationStat::kNotCompiledNoCodegen); + MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kNotCompiledNoCodegen); return nullptr; } codegen->GetAssembler()->cfi().SetEnabled( @@ -1036,6 +816,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, VLOG(compiler) << "Building " << pass_observer.GetMethodName(); PassScope scope(HGraphBuilder::kBuilderPassName, &pass_observer); HGraphBuilder builder(graph, + code_item_accessor, &dex_compilation_unit, &dex_compilation_unit, compiler_driver, @@ -1076,7 +857,6 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, RunOptimizations(graph, codegen.get(), - compiler_driver, dex_compilation_unit, &pass_observer, handles); @@ -1092,6 +872,111 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, codegen->Compile(code_allocator); pass_observer.DumpDisassembly(); + MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kCompiledBytecode); + return codegen.release(); +} + +CodeGenerator* OptimizingCompiler::TryCompileIntrinsic( + ArenaAllocator* allocator, + ArenaStack* arena_stack, + CodeVectorAllocator* code_allocator, + const DexCompilationUnit& dex_compilation_unit, + ArtMethod* method, + VariableSizedHandleScope* handles) const { + MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kAttemptIntrinsicCompilation); + CompilerDriver* compiler_driver = GetCompilerDriver(); + InstructionSet instruction_set = compiler_driver->GetInstructionSet(); + const DexFile& dex_file = *dex_compilation_unit.GetDexFile(); + uint32_t method_idx = dex_compilation_unit.GetDexMethodIndex(); + + // Always use the Thumb-2 assembler: some runtime functionality + // (like implicit stack overflow checks) assume Thumb-2. + DCHECK_NE(instruction_set, InstructionSet::kArm); + + // Do not attempt to compile on architectures we do not support. + if (!IsInstructionSetSupported(instruction_set)) { + return nullptr; + } + + HGraph* graph = new (allocator) HGraph( + allocator, + arena_stack, + dex_file, + method_idx, + compiler_driver->GetInstructionSet(), + kInvalidInvokeType, + compiler_driver->GetCompilerOptions().GetDebuggable(), + /* osr */ false); + + DCHECK(Runtime::Current()->IsAotCompiler()); + DCHECK(method != nullptr); + graph->SetArtMethod(method); + + std::unique_ptr codegen( + CodeGenerator::Create(graph, + instruction_set, + *compiler_driver->GetInstructionSetFeatures(), + compiler_driver->GetCompilerOptions(), + compilation_stats_.get())); + if (codegen.get() == nullptr) { + return nullptr; + } + codegen->GetAssembler()->cfi().SetEnabled( + compiler_driver->GetCompilerOptions().GenerateAnyDebugInfo()); + + PassObserver pass_observer(graph, + codegen.get(), + visualizer_output_.get(), + compiler_driver, + dump_mutex_); + + { + VLOG(compiler) << "Building intrinsic graph " << pass_observer.GetMethodName(); + PassScope scope(HGraphBuilder::kBuilderPassName, &pass_observer); + HGraphBuilder builder(graph, + CodeItemDebugInfoAccessor(), // Null code item. + &dex_compilation_unit, + &dex_compilation_unit, + compiler_driver, + codegen.get(), + compilation_stats_.get(), + /* interpreter_metadata */ nullptr, + handles); + builder.BuildIntrinsicGraph(method); + } + + OptimizationDef optimizations[] = { + OptDef(OptimizationPass::kIntrinsicsRecognizer), + // Some intrinsics are converted to HIR by the simplifier and the codegen also + // has a few assumptions that only the instruction simplifier can satisfy. + OptDef(OptimizationPass::kInstructionSimplifier), + }; + RunOptimizations(graph, + codegen.get(), + dex_compilation_unit, + &pass_observer, + handles, + optimizations); + + RunArchOptimizations(graph, codegen.get(), dex_compilation_unit, &pass_observer, handles); + + AllocateRegisters(graph, + codegen.get(), + &pass_observer, + compiler_driver->GetCompilerOptions().GetRegisterAllocationStrategy(), + compilation_stats_.get()); + if (!codegen->IsLeafMethod()) { + VLOG(compiler) << "Intrinsic method is not leaf: " << method->GetIntrinsic() + << " " << graph->PrettyMethod(); + return nullptr; + } + + codegen->Compile(code_allocator); + pass_observer.DumpDisassembly(); + + VLOG(compiler) << "Compiled intrinsic: " << method->GetIntrinsic() + << " " << graph->PrettyMethod(); + MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kCompiledIntrinsic); return codegen.release(); } @@ -1104,42 +989,68 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item, const DexFile& dex_file, Handle dex_cache) const { CompilerDriver* compiler_driver = GetCompilerDriver(); - CompiledMethod* method = nullptr; - DCHECK(Runtime::Current()->IsAotCompiler()); + CompiledMethod* compiled_method = nullptr; + Runtime* runtime = Runtime::Current(); + DCHECK(runtime->IsAotCompiler()); const VerifiedMethod* verified_method = compiler_driver->GetVerifiedMethod(&dex_file, method_idx); DCHECK(!verified_method->HasRuntimeThrow()); if (compiler_driver->IsMethodVerifiedWithoutFailures(method_idx, class_def_idx, dex_file) || verifier::CanCompilerHandleVerificationFailure( verified_method->GetEncounteredVerificationFailures())) { - ArenaAllocator allocator(Runtime::Current()->GetArenaPool()); - ArenaStack arena_stack(Runtime::Current()->GetArenaPool()); + ArenaAllocator allocator(runtime->GetArenaPool()); + ArenaStack arena_stack(runtime->GetArenaPool()); CodeVectorAllocator code_allocator(&allocator); std::unique_ptr codegen; + bool compiled_intrinsic = false; { + DexCompilationUnit dex_compilation_unit( + jclass_loader, + runtime->GetClassLinker(), + dex_file, + code_item, + class_def_idx, + method_idx, + access_flags, + /* verified_method */ nullptr, // Not needed by the Optimizing compiler. + dex_cache); ScopedObjectAccess soa(Thread::Current()); + ArtMethod* method = compiler_driver->ResolveMethod( + soa, dex_cache, jclass_loader, &dex_compilation_unit, method_idx, invoke_type); VariableSizedHandleScope handles(soa.Self()); // Go to native so that we don't block GC during compilation. ScopedThreadSuspension sts(soa.Self(), kNative); - codegen.reset( - TryCompile(&allocator, - &arena_stack, - &code_allocator, - code_item, - access_flags, - invoke_type, - class_def_idx, - method_idx, - jclass_loader, - dex_file, - dex_cache, - nullptr, - /* osr */ false, - &handles)); + if (method != nullptr && UNLIKELY(method->IsIntrinsic())) { + DCHECK(compiler_driver->GetCompilerOptions().IsBootImage()); + codegen.reset( + TryCompileIntrinsic(&allocator, + &arena_stack, + &code_allocator, + dex_compilation_unit, + method, + &handles)); + if (codegen != nullptr) { + compiled_intrinsic = true; + } + } + if (codegen == nullptr) { + codegen.reset( + TryCompile(&allocator, + &arena_stack, + &code_allocator, + dex_compilation_unit, + method, + /* osr */ false, + &handles)); + } } if (codegen.get() != nullptr) { - MaybeRecordStat(compilation_stats_.get(), - MethodCompilationStat::kCompiled); - method = Emit(&allocator, &code_allocator, codegen.get(), compiler_driver, code_item); + compiled_method = Emit(&allocator, + &code_allocator, + codegen.get(), + compiled_intrinsic ? nullptr : code_item); + if (compiled_intrinsic) { + compiled_method->MarkAsIntrinsic(); + } if (kArenaAllocatorCountAllocations) { codegen.reset(); // Release codegen's ScopedArenaAllocator for memory accounting. @@ -1173,10 +1084,71 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item, // regressing. std::string method_name = dex_file.PrettyMethod(method_idx); bool shouldCompile = method_name.find("$opt$") != std::string::npos; - DCHECK((method != nullptr) || !shouldCompile) << "Didn't compile " << method_name; + DCHECK((compiled_method != nullptr) || !shouldCompile) << "Didn't compile " << method_name; + } + + return compiled_method; +} + +CompiledMethod* OptimizingCompiler::JniCompile(uint32_t access_flags, + uint32_t method_idx, + const DexFile& dex_file, + Handle dex_cache) const { + if (GetCompilerDriver()->GetCompilerOptions().IsBootImage()) { + ScopedObjectAccess soa(Thread::Current()); + Runtime* runtime = Runtime::Current(); + ArtMethod* method = runtime->GetClassLinker()->LookupResolvedMethod( + method_idx, dex_cache.Get(), /* class_loader */ nullptr); + if (method != nullptr && UNLIKELY(method->IsIntrinsic())) { + ScopedNullHandle class_loader; // null means boot class path loader. + DexCompilationUnit dex_compilation_unit( + class_loader, + runtime->GetClassLinker(), + dex_file, + /* code_item */ nullptr, + /* class_def_idx */ DexFile::kDexNoIndex16, + method_idx, + access_flags, + /* verified_method */ nullptr, + dex_cache); + ArenaAllocator allocator(runtime->GetArenaPool()); + ArenaStack arena_stack(runtime->GetArenaPool()); + CodeVectorAllocator code_allocator(&allocator); + VariableSizedHandleScope handles(soa.Self()); + // Go to native so that we don't block GC during compilation. + ScopedThreadSuspension sts(soa.Self(), kNative); + std::unique_ptr codegen( + TryCompileIntrinsic(&allocator, + &arena_stack, + &code_allocator, + dex_compilation_unit, + method, + &handles)); + if (codegen != nullptr) { + CompiledMethod* compiled_method = Emit(&allocator, + &code_allocator, + codegen.get(), + /* code_item_for_osr_check */ nullptr); + compiled_method->MarkAsIntrinsic(); + return compiled_method; + } + } } - return method; + JniCompiledMethod jni_compiled_method = ArtQuickJniCompileMethod( + GetCompilerDriver(), access_flags, method_idx, dex_file); + MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kCompiledNativeStub); + return CompiledMethod::SwapAllocCompiledMethod( + GetCompilerDriver(), + jni_compiled_method.GetInstructionSet(), + jni_compiled_method.GetCode(), + jni_compiled_method.GetFrameSize(), + jni_compiled_method.GetCoreSpillMask(), + jni_compiled_method.GetFpSpillMask(), + /* method_info */ ArrayRef(), + /* vmap_table */ ArrayRef(), + jni_compiled_method.GetCfi(), + /* patches */ ArrayRef()); } Compiler* CreateOptimizingCompiler(CompilerDriver* driver) { @@ -1223,29 +1195,98 @@ bool OptimizingCompiler::JitCompile(Thread* self, const DexFile::CodeItem* code_item = dex_file->GetCodeItem(method->GetCodeItemOffset()); const uint32_t method_idx = method->GetDexMethodIndex(); const uint32_t access_flags = method->GetAccessFlags(); - const InvokeType invoke_type = method->GetInvokeType(); - ArenaAllocator allocator(Runtime::Current()->GetJitArenaPool()); - ArenaStack arena_stack(Runtime::Current()->GetJitArenaPool()); + Runtime* runtime = Runtime::Current(); + ArenaAllocator allocator(runtime->GetJitArenaPool()); + + if (UNLIKELY(method->IsNative())) { + JniCompiledMethod jni_compiled_method = ArtQuickJniCompileMethod( + GetCompilerDriver(), access_flags, method_idx, *dex_file); + ScopedNullHandle> roots; + ArenaSet> cha_single_implementation_list( + allocator.Adapter(kArenaAllocCHA)); + const void* code = code_cache->CommitCode( + self, + method, + /* stack_map_data */ nullptr, + /* method_info_data */ nullptr, + /* roots_data */ nullptr, + jni_compiled_method.GetFrameSize(), + jni_compiled_method.GetCoreSpillMask(), + jni_compiled_method.GetFpSpillMask(), + jni_compiled_method.GetCode().data(), + jni_compiled_method.GetCode().size(), + /* data_size */ 0u, + osr, + roots, + /* has_should_deoptimize_flag */ false, + cha_single_implementation_list); + if (code == nullptr) { + return false; + } + + const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions(); + if (compiler_options.GenerateAnyDebugInfo()) { + const auto* method_header = reinterpret_cast(code); + const uintptr_t code_address = reinterpret_cast(method_header->GetCode()); + debug::MethodDebugInfo info = {}; + DCHECK(info.trampoline_name.empty()); + info.dex_file = dex_file; + info.class_def_index = class_def_idx; + info.dex_method_index = method_idx; + info.access_flags = access_flags; + info.code_item = code_item; + info.isa = jni_compiled_method.GetInstructionSet(); + info.deduped = false; + info.is_native_debuggable = compiler_options.GetNativeDebuggable(); + info.is_optimized = true; + info.is_code_address_text_relative = false; + info.code_address = code_address; + info.code_size = jni_compiled_method.GetCode().size(); + info.frame_size_in_bytes = method_header->GetFrameSizeInBytes(); + info.code_info = nullptr; + info.cfi = jni_compiled_method.GetCfi(); + // If both flags are passed, generate full debug info. + const bool mini_debug_info = !compiler_options.GetGenerateDebugInfo(); + std::vector elf_file = debug::MakeElfFileForJIT( + GetCompilerDriver()->GetInstructionSet(), + GetCompilerDriver()->GetInstructionSetFeatures(), + mini_debug_info, + info); + CreateJITCodeEntryForAddress(code_address, std::move(elf_file)); + } + + Runtime::Current()->GetJit()->AddMemoryUsage(method, allocator.BytesUsed()); + if (jit_logger != nullptr) { + jit_logger->WriteLog(code, jni_compiled_method.GetCode().size(), method); + } + return true; + } + + ArenaStack arena_stack(runtime->GetJitArenaPool()); CodeVectorAllocator code_allocator(&allocator); VariableSizedHandleScope handles(self); std::unique_ptr codegen; { + DexCompilationUnit dex_compilation_unit( + class_loader, + runtime->GetClassLinker(), + *dex_file, + code_item, + class_def_idx, + method_idx, + access_flags, + /* verified_method */ nullptr, + dex_cache); + // Go to native so that we don't block GC during compilation. ScopedThreadSuspension sts(self, kNative); codegen.reset( TryCompile(&allocator, &arena_stack, &code_allocator, - code_item, - access_flags, - invoke_type, - class_def_idx, - method_idx, - class_loader, - *dex_file, - dex_cache, + dex_compilation_unit, method, osr, &handles)); @@ -1267,6 +1308,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, self, class_linker->GetClassRoot(ClassLinker::kObjectArrayClass), number_of_roots))); if (roots == nullptr) { // Out of memory, just clear the exception to avoid any Java exception uncaught problems. + MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kJitOutOfMemoryForCommit); DCHECK(self->IsExceptionPending()); self->ClearException(); return false; @@ -1283,12 +1325,12 @@ bool OptimizingCompiler::JitCompile(Thread* self, &method_info_data, &roots_data); if (stack_map_data == nullptr || roots_data == nullptr) { + MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kJitOutOfMemoryForCommit); return false; } - MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kCompiled); codegen->BuildStackMaps(MemoryRegion(stack_map_data, stack_map_size), MemoryRegion(method_info_data, method_info_size), - *code_item); + code_item); codegen->EmitJitRoots(code_allocator.GetData(), roots, roots_data); const void* code = code_cache->CommitCode( @@ -1309,12 +1351,13 @@ bool OptimizingCompiler::JitCompile(Thread* self, codegen->GetGraph()->GetCHASingleImplementationList()); if (code == nullptr) { + MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kJitOutOfMemoryForCommit); code_cache->ClearData(self, stack_map_data, roots_data); return false; } const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions(); - if (compiler_options.GetGenerateDebugInfo()) { + if (compiler_options.GenerateAnyDebugInfo()) { const auto* method_header = reinterpret_cast(code); const uintptr_t code_address = reinterpret_cast(method_header->GetCode()); debug::MethodDebugInfo info = {}; @@ -1334,10 +1377,13 @@ bool OptimizingCompiler::JitCompile(Thread* self, info.frame_size_in_bytes = method_header->GetFrameSizeInBytes(); info.code_info = stack_map_size == 0 ? nullptr : stack_map_data; info.cfi = ArrayRef(*codegen->GetAssembler()->cfi().data()); - std::vector elf_file = debug::WriteDebugElfFileForMethods( + // If both flags are passed, generate full debug info. + const bool mini_debug_info = !compiler_options.GetGenerateDebugInfo(); + std::vector elf_file = debug::MakeElfFileForJIT( GetCompilerDriver()->GetInstructionSet(), GetCompilerDriver()->GetInstructionSetFeatures(), - ArrayRef(&info, 1)); + mini_debug_info, + info); CreateJITCodeEntryForAddress(code_address, std::move(elf_file)); } diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index 07f9635abaddb8f3189cd72277abf2e7803cc629..32a94ab5e4b7c431f07aacedeeea106cc3dd1bf4 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -23,14 +23,18 @@ #include #include "atomic.h" +#include "base/logging.h" // For VLOG_IS_ON. #include "globals.h" namespace art { -enum MethodCompilationStat { - kAttemptCompilation = 0, +enum class MethodCompilationStat { + kAttemptBytecodeCompilation = 0, + kAttemptIntrinsicCompilation, + kCompiledNativeStub, + kCompiledIntrinsic, + kCompiledBytecode, kCHAInline, - kCompiled, kInlinedInvoke, kReplacedInvokeWithSimplePattern, kInstructionSimplifications, @@ -94,8 +98,10 @@ enum MethodCompilationStat { kConstructorFenceRemovedLSE, kConstructorFenceRemovedPFRA, kConstructorFenceRemovedCFRE, + kJitOutOfMemoryForCommit, kLastStat }; +std::ostream& operator<<(std::ostream& os, const MethodCompilationStat& rhs); class OptimizingCompilerStats { public: @@ -105,7 +111,15 @@ class OptimizingCompilerStats { } void RecordStat(MethodCompilationStat stat, uint32_t count = 1) { - compile_stats_[stat] += count; + size_t stat_index = static_cast(stat); + DCHECK_LT(stat_index, arraysize(compile_stats_)); + compile_stats_[stat_index] += count; + } + + uint32_t GetStat(MethodCompilationStat stat) const { + size_t stat_index = static_cast(stat); + DCHECK_LT(stat_index, arraysize(compile_stats_)); + return compile_stats_[stat_index]; } void Log() const { @@ -114,18 +128,29 @@ class OptimizingCompilerStats { return; } - if (compile_stats_[kAttemptCompilation] == 0) { + uint32_t compiled_intrinsics = GetStat(MethodCompilationStat::kCompiledIntrinsic); + uint32_t compiled_native_stubs = GetStat(MethodCompilationStat::kCompiledNativeStub); + uint32_t bytecode_attempts = + GetStat(MethodCompilationStat::kAttemptBytecodeCompilation); + if (compiled_intrinsics == 0u && compiled_native_stubs == 0u && bytecode_attempts == 0u) { LOG(INFO) << "Did not compile any method."; } else { - float compiled_percent = - compile_stats_[kCompiled] * 100.0f / compile_stats_[kAttemptCompilation]; - LOG(INFO) << "Attempted compilation of " << compile_stats_[kAttemptCompilation] - << " methods: " << std::fixed << std::setprecision(2) - << compiled_percent << "% (" << compile_stats_[kCompiled] << ") compiled."; - - for (size_t i = 0; i < kLastStat; i++) { + uint32_t compiled_bytecode_methods = + GetStat(MethodCompilationStat::kCompiledBytecode); + // Successful intrinsic compilation preempts other compilation attempts but failed intrinsic + // compilation shall still count towards bytecode or native stub compilation attempts. + uint32_t num_compilation_attempts = + compiled_intrinsics + compiled_native_stubs + bytecode_attempts; + uint32_t num_successful_compilations = + compiled_intrinsics + compiled_native_stubs + compiled_bytecode_methods; + float compiled_percent = num_successful_compilations * 100.0f / num_compilation_attempts; + LOG(INFO) << "Attempted compilation of " + << num_compilation_attempts << " methods: " << std::fixed << std::setprecision(2) + << compiled_percent << "% (" << num_successful_compilations << ") compiled."; + + for (size_t i = 0; i < arraysize(compile_stats_); ++i) { if (compile_stats_[i] != 0) { - LOG(INFO) << PrintMethodCompilationStat(static_cast(i)) << ": " + LOG(INFO) << "OptStat#" << static_cast(i) << ": " << compile_stats_[i]; } } @@ -133,7 +158,7 @@ class OptimizingCompilerStats { } void AddTo(OptimizingCompilerStats* other_stats) { - for (size_t i = 0; i != kLastStat; ++i) { + for (size_t i = 0; i != arraysize(compile_stats_); ++i) { uint32_t count = compile_stats_[i]; if (count != 0) { other_stats->RecordStat(static_cast(i), count); @@ -142,91 +167,13 @@ class OptimizingCompilerStats { } void Reset() { - for (size_t i = 0; i != kLastStat; ++i) { - compile_stats_[i] = 0u; + for (std::atomic& stat : compile_stats_) { + stat = 0u; } } private: - std::string PrintMethodCompilationStat(MethodCompilationStat stat) const { - std::string name; - switch (stat) { - case kAttemptCompilation : name = "AttemptCompilation"; break; - case kCHAInline : name = "CHAInline"; break; - case kCompiled : name = "Compiled"; break; - case kInlinedInvoke : name = "InlinedInvoke"; break; - case kReplacedInvokeWithSimplePattern: name = "ReplacedInvokeWithSimplePattern"; break; - case kInstructionSimplifications: name = "InstructionSimplifications"; break; - case kInstructionSimplificationsArch: name = "InstructionSimplificationsArch"; break; - case kUnresolvedMethod : name = "UnresolvedMethod"; break; - case kUnresolvedField : name = "UnresolvedField"; break; - case kUnresolvedFieldNotAFastAccess : name = "UnresolvedFieldNotAFastAccess"; break; - case kRemovedCheckedCast: name = "RemovedCheckedCast"; break; - case kRemovedDeadInstruction: name = "RemovedDeadInstruction"; break; - case kRemovedNullCheck: name = "RemovedNullCheck"; break; - case kNotCompiledSkipped: name = "NotCompiledSkipped"; break; - case kNotCompiledInvalidBytecode: name = "NotCompiledInvalidBytecode"; break; - case kNotCompiledThrowCatchLoop : name = "NotCompiledThrowCatchLoop"; break; - case kNotCompiledAmbiguousArrayOp : name = "NotCompiledAmbiguousArrayOp"; break; - case kNotCompiledHugeMethod : name = "NotCompiledHugeMethod"; break; - case kNotCompiledLargeMethodNoBranches : name = "NotCompiledLargeMethodNoBranches"; break; - case kNotCompiledMalformedOpcode : name = "NotCompiledMalformedOpcode"; break; - case kNotCompiledNoCodegen : name = "NotCompiledNoCodegen"; break; - case kNotCompiledPathological : name = "NotCompiledPathological"; break; - case kNotCompiledSpaceFilter : name = "NotCompiledSpaceFilter"; break; - case kNotCompiledUnhandledInstruction : name = "NotCompiledUnhandledInstruction"; break; - case kNotCompiledUnsupportedIsa : name = "NotCompiledUnsupportedIsa"; break; - case kNotCompiledVerificationError : name = "NotCompiledVerificationError"; break; - case kNotCompiledVerifyAtRuntime : name = "NotCompiledVerifyAtRuntime"; break; - case kInlinedMonomorphicCall: name = "InlinedMonomorphicCall"; break; - case kInlinedPolymorphicCall: name = "InlinedPolymorphicCall"; break; - case kMonomorphicCall: name = "MonomorphicCall"; break; - case kPolymorphicCall: name = "PolymorphicCall"; break; - case kMegamorphicCall: name = "MegamorphicCall"; break; - case kBooleanSimplified : name = "BooleanSimplified"; break; - case kIntrinsicRecognized : name = "IntrinsicRecognized"; break; - case kLoopInvariantMoved : name = "LoopInvariantMoved"; break; - case kLoopVectorized : name = "LoopVectorized"; break; - case kLoopVectorizedIdiom : name = "LoopVectorizedIdiom"; break; - case kSelectGenerated : name = "SelectGenerated"; break; - case kRemovedInstanceOf: name = "RemovedInstanceOf"; break; - case kInlinedInvokeVirtualOrInterface: name = "InlinedInvokeVirtualOrInterface"; break; - case kImplicitNullCheckGenerated: name = "ImplicitNullCheckGenerated"; break; - case kExplicitNullCheckGenerated: name = "ExplicitNullCheckGenerated"; break; - case kSimplifyIf: name = "SimplifyIf"; break; - case kInstructionSunk: name = "InstructionSunk"; break; - case kNotInlinedUnresolvedEntrypoint: name = "NotInlinedUnresolvedEntrypoint"; break; - case kNotInlinedDexCache: name = "NotInlinedDexCache"; break; - case kNotInlinedStackMaps: name = "NotInlinedStackMaps"; break; - case kNotInlinedEnvironmentBudget: name = "NotInlinedEnvironmentBudget"; break; - case kNotInlinedInstructionBudget: name = "NotInlinedInstructionBudget"; break; - case kNotInlinedLoopWithoutExit: name = "NotInlinedLoopWithoutExit"; break; - case kNotInlinedIrreducibleLoop: name = "NotInlinedIrreducibleLoop"; break; - case kNotInlinedAlwaysThrows: name = "NotInlinedAlwaysThrows"; break; - case kNotInlinedInfiniteLoop: name = "NotInlinedInfiniteLoop"; break; - case kNotInlinedTryCatch: name = "NotInlinedTryCatch"; break; - case kNotInlinedRegisterAllocator: name = "NotInlinedRegisterAllocator"; break; - case kNotInlinedCannotBuild: name = "NotInlinedCannotBuild"; break; - case kNotInlinedNotVerified: name = "NotInlinedNotVerified"; break; - case kNotInlinedCodeItem: name = "NotInlinedCodeItem"; break; - case kNotInlinedWont: name = "NotInlinedWont"; break; - case kNotInlinedRecursiveBudget: name = "NotInlinedRecursiveBudget"; break; - case kNotInlinedProxy: name = "NotInlinedProxy"; break; - case kConstructorFenceGeneratedNew: name = "ConstructorFenceGeneratedNew"; break; - case kConstructorFenceGeneratedFinal: name = "ConstructorFenceGeneratedFinal"; break; - case kConstructorFenceRemovedLSE: name = "ConstructorFenceRemovedLSE"; break; - case kConstructorFenceRemovedPFRA: name = "ConstructorFenceRemovedPFRA"; break; - case kConstructorFenceRemovedCFRE: name = "ConstructorFenceRemovedCFRE"; break; - - case kLastStat: - LOG(FATAL) << "invalid stat " - << static_cast::type>(stat); - UNREACHABLE(); - } - return "OptStat#" + name; - } - - std::atomic compile_stats_[kLastStat]; + std::atomic compile_stats_[static_cast(MethodCompilationStat::kLastStat)]; DISALLOW_COPY_AND_ASSIGN(OptimizingCompilerStats); }; diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h index e90c30d5ca55a0aa7d1d6b3bd100dd73b72b1aff..7d05262b10021f2b1b5c8080c9675559e1c6137c 100644 --- a/compiler/optimizing/optimizing_unit_test.h +++ b/compiler/optimizing/optimizing_unit_test.h @@ -19,9 +19,11 @@ #include "base/scoped_arena_allocator.h" #include "builder.h" +#include "code_item_accessors-inl.h" #include "common_compiler_test.h" #include "dex_file.h" #include "dex_instruction.h" +#include "driver/dex_compilation_unit.h" #include "handle_scope-inl.h" #include "mirror/class_loader.h" #include "mirror/dex_cache.h" @@ -133,19 +135,19 @@ class OptimizingUnitTest : public CommonCompilerTest { if (handles_ == nullptr) { handles_.reset(new VariableSizedHandleScope(soa.Self())); } - const DexFile* dex_file = graph->GetAllocator()->Alloc(); const DexCompilationUnit* dex_compilation_unit = new (graph->GetAllocator()) DexCompilationUnit( handles_->NewHandle(nullptr), /* class_linker */ nullptr, - *dex_file, + graph->GetDexFile(), code_item, /* class_def_index */ DexFile::kDexNoIndex16, /* method_idx */ dex::kDexNoIndex, /* access_flags */ 0u, /* verified_method */ nullptr, handles_->NewHandle(nullptr)); - HGraphBuilder builder(graph, dex_compilation_unit, *code_item, handles_.get(), return_type); + CodeItemDebugInfoAccessor accessor(&graph->GetDexFile(), code_item); + HGraphBuilder builder(graph, dex_compilation_unit, accessor, handles_.get(), return_type); bool graph_built = (builder.BuildGraph() == kAnalysisSuccess); return graph_built ? graph : nullptr; } diff --git a/compiler/optimizing/pc_relative_fixups_mips.h b/compiler/optimizing/pc_relative_fixups_mips.h index 5a7397bf9de3d6ee3b88c5f84c7e53732635fe24..ec2c711f8d4afafcc49bd82959086d5f47ed57ee 100644 --- a/compiler/optimizing/pc_relative_fixups_mips.h +++ b/compiler/optimizing/pc_relative_fixups_mips.h @@ -29,7 +29,7 @@ namespace mips { class PcRelativeFixups : public HOptimization { public: PcRelativeFixups(HGraph* graph, CodeGenerator* codegen, OptimizingCompilerStats* stats) - : HOptimization(graph, "pc_relative_fixups_mips", stats), + : HOptimization(graph, kPcRelativeFixupsMipsPassName, stats), codegen_(codegen) {} static constexpr const char* kPcRelativeFixupsMipsPassName = "pc_relative_fixups_mips"; diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc index fe98aa95619917478f986c7f818b6a000210ad73..1ed190d328ee1be3167818301c7a20b72824efb6 100644 --- a/compiler/optimizing/prepare_for_register_allocation.cc +++ b/compiler/optimizing/prepare_for_register_allocation.cc @@ -53,16 +53,18 @@ void PrepareForRegisterAllocation::VisitDeoptimize(HDeoptimize* deoptimize) { void PrepareForRegisterAllocation::VisitBoundsCheck(HBoundsCheck* check) { check->ReplaceWith(check->InputAt(0)); if (check->IsStringCharAt()) { - // Add a fake environment for String.charAt() inline info as we want - // the exception to appear as being thrown from there. + // Add a fake environment for String.charAt() inline info as we want the exception + // to appear as being thrown from there. Skip if we're compiling String.charAt() itself. ArtMethod* char_at_method = jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt); - ArenaAllocator* allocator = GetGraph()->GetAllocator(); - HEnvironment* environment = new (allocator) HEnvironment(allocator, - /* number_of_vregs */ 0u, - char_at_method, - /* dex_pc */ dex::kDexNoIndex, - check); - check->InsertRawEnvironment(environment); + if (GetGraph()->GetArtMethod() != char_at_method) { + ArenaAllocator* allocator = GetGraph()->GetAllocator(); + HEnvironment* environment = new (allocator) HEnvironment(allocator, + /* number_of_vregs */ 0u, + char_at_method, + /* dex_pc */ dex::kDexNoIndex, + check); + check->InsertRawEnvironment(environment); + } } } diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index 7246129e25fb0d1fdf8f685b891de7de95abb06d..8bb124e066503a574f2b90759574ee0a225cf963 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -544,7 +544,7 @@ void ReferenceTypePropagation::RTPVisitor::SetClassAsTypeInfo(HInstruction* inst // the method is from the String class, the null loader is good enough. Handle loader(hs.NewHandle(nullptr)); ArtMethod* method = cl->ResolveMethod( - dex_file, invoke->GetDexMethodIndex(), dex_cache, loader, nullptr, kDirect); + invoke->GetDexMethodIndex(), dex_cache, loader, /* referrer */ nullptr, kDirect); DCHECK(method != nullptr); mirror::Class* declaring_class = method->GetDeclaringClass(); DCHECK(declaring_class != nullptr); @@ -576,8 +576,8 @@ void ReferenceTypePropagation::RTPVisitor::UpdateReferenceTypeInfo(HInstruction* ScopedObjectAccess soa(Thread::Current()); ObjPtr dex_cache = FindDexCacheWithHint(soa.Self(), dex_file, hint_dex_cache_); - ObjPtr klass = - ClassLinker::LookupResolvedType(type_idx, dex_cache, class_loader_.Get()); + ObjPtr klass = Runtime::Current()->GetClassLinker()->LookupResolvedType( + type_idx, dex_cache, class_loader_.Get()); SetClassAsTypeInfo(instr, klass, is_exact); } @@ -612,7 +612,7 @@ void ReferenceTypePropagation::RTPVisitor::UpdateFieldAccessTypeInfo(HInstructio // The field is unknown only during tests. if (info.GetField() != nullptr) { - klass = info.GetField()->LookupType(); + klass = info.GetField()->LookupResolvedType(); } SetClassAsTypeInfo(instr, klass, /* is_exact */ false); diff --git a/compiler/optimizing/scheduler.cc b/compiler/optimizing/scheduler.cc index 8cc376c3a61ed1b25258abb9f9eee181c084a219..bb28d50b5693465d307eca672744b8f50134217e 100644 --- a/compiler/optimizing/scheduler.cc +++ b/compiler/optimizing/scheduler.cc @@ -72,7 +72,7 @@ static bool MayHaveReorderingDependency(SideEffects node, SideEffects other) { size_t SchedulingGraph::ArrayAccessHeapLocation(HInstruction* array, HInstruction* index) const { DCHECK(heap_location_collector_ != nullptr); - size_t heap_loc = heap_location_collector_->GetArrayAccessHeapLocation(array, index); + size_t heap_loc = heap_location_collector_->GetArrayHeapLocation(array, index); // This array access should be analyzed and added to HeapLocationCollector before. DCHECK(heap_loc != HeapLocationCollector::kHeapLocationNotFound); return heap_loc; @@ -153,12 +153,7 @@ size_t SchedulingGraph::FieldAccessHeapLocation(HInstruction* obj, const FieldIn DCHECK(field != nullptr); DCHECK(heap_location_collector_ != nullptr); - size_t heap_loc = heap_location_collector_->FindHeapLocationIndex( - heap_location_collector_->FindReferenceInfoOf( - heap_location_collector_->HuntForOriginalReference(obj)), - field->GetFieldOffset().SizeValue(), - nullptr, - field->GetDeclaringClassDefIndex()); + size_t heap_loc = heap_location_collector_->GetFieldHeapLocation(obj, field); // This field access should be analyzed and added to HeapLocationCollector before. DCHECK(heap_loc != HeapLocationCollector::kHeapLocationNotFound); diff --git a/compiler/optimizing/scheduler.h b/compiler/optimizing/scheduler.h index a6e160379b66249002d7624ecc8bf7274cccec59..dfa077f7deac31f7499f00c8605f5f227007ef01 100644 --- a/compiler/optimizing/scheduler.h +++ b/compiler/optimizing/scheduler.h @@ -462,6 +462,11 @@ class HScheduler { // containing basic block from being scheduled. // This method is used to restrict scheduling to instructions that we know are // safe to handle. + // + // For newly introduced instructions by default HScheduler::IsSchedulable returns false. + // HScheduler${ARCH}::IsSchedulable can be overridden to return true for an instruction (see + // scheduler_arm64.h for example) if it is safe to schedule it; in this case one *must* also + // look at/update HScheduler${ARCH}::IsSchedulingBarrier for this instruction. virtual bool IsSchedulable(const HInstruction* instruction) const; bool IsSchedulable(const HBasicBlock* block) const; @@ -495,8 +500,11 @@ inline bool SchedulingGraph::IsSchedulingBarrier(const HInstruction* instruction class HInstructionScheduling : public HOptimization { public: - HInstructionScheduling(HGraph* graph, InstructionSet instruction_set, CodeGenerator* cg = nullptr) - : HOptimization(graph, kInstructionScheduling), + HInstructionScheduling(HGraph* graph, + InstructionSet instruction_set, + CodeGenerator* cg = nullptr, + const char* name = kInstructionSchedulingPassName) + : HOptimization(graph, name), codegen_(cg), instruction_set_(instruction_set) {} @@ -505,7 +513,7 @@ class HInstructionScheduling : public HOptimization { } void Run(bool only_optimize_loop_blocks, bool schedule_randomly); - static constexpr const char* kInstructionScheduling = "scheduler"; + static constexpr const char* kInstructionSchedulingPassName = "scheduler"; private: CodeGenerator* const codegen_; diff --git a/compiler/optimizing/scheduler_arm64.h b/compiler/optimizing/scheduler_arm64.h index 32f161f26ae157e83d884b8d24192eba5a581472..f71cb5b78408ae5082a076a2d603aec237c4ebaa 100644 --- a/compiler/optimizing/scheduler_arm64.h +++ b/compiler/optimizing/scheduler_arm64.h @@ -151,6 +151,20 @@ class HSchedulerARM64 : public HScheduler { #undef CASE_INSTRUCTION_KIND } + // Treat as scheduling barriers those vector instructions whose live ranges exceed the vectorized + // loop boundaries. This is a workaround for the lack of notion of SIMD register in the compiler; + // around a call we have to save/restore all live SIMD&FP registers (only lower 64 bits of + // SIMD&FP registers are callee saved) so don't reorder such vector instructions. + // + // TODO: remove this when a proper support of SIMD registers is introduced to the compiler. + bool IsSchedulingBarrier(const HInstruction* instr) const OVERRIDE { + return HScheduler::IsSchedulingBarrier(instr) || + instr->IsVecReduce() || + instr->IsVecExtractScalar() || + instr->IsVecSetScalars() || + instr->IsVecReplicateScalar(); + } + private: SchedulingLatencyVisitorARM64 arm64_latency_visitor_; DISALLOW_COPY_AND_ASSIGN(HSchedulerARM64); diff --git a/compiler/optimizing/scheduler_test.cc b/compiler/optimizing/scheduler_test.cc index 75dce81550b4cde5be6844abeb12adb4918389dd..104ebc79c275c398b5f860757d02b03ac4dbf22a 100644 --- a/compiler/optimizing/scheduler_test.cc +++ b/compiler/optimizing/scheduler_test.cc @@ -294,38 +294,38 @@ class SchedulerTest : public OptimizingUnitTest { size_t loc2 = HeapLocationCollector::kHeapLocationNotFound; // Test side effect dependency: array[0] and array[1] - loc1 = heap_location_collector.GetArrayAccessHeapLocation(arr, c0); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(arr, c1); + loc1 = heap_location_collector.GetArrayHeapLocation(arr, c0); + loc2 = heap_location_collector.GetArrayHeapLocation(arr, c1); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); ASSERT_FALSE(scheduling_graph.HasImmediateOtherDependency(arr_set_1, arr_set_0)); // Test side effect dependency based on LSA analysis: array[i] and array[j] - loc1 = heap_location_collector.GetArrayAccessHeapLocation(arr, i); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(arr, j); + loc1 = heap_location_collector.GetArrayHeapLocation(arr, i); + loc2 = heap_location_collector.GetArrayHeapLocation(arr, j); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(arr_set_j, arr_set_i)); // Test side effect dependency based on LSA analysis: array[i] and array[i+0] - loc1 = heap_location_collector.GetArrayAccessHeapLocation(arr, i); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(arr, add0); + loc1 = heap_location_collector.GetArrayHeapLocation(arr, i); + loc2 = heap_location_collector.GetArrayHeapLocation(arr, add0); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(arr_set_add0, arr_set_i)); // Test side effect dependency based on LSA analysis: array[i] and array[i-0] - loc1 = heap_location_collector.GetArrayAccessHeapLocation(arr, i); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(arr, sub0); + loc1 = heap_location_collector.GetArrayHeapLocation(arr, i); + loc2 = heap_location_collector.GetArrayHeapLocation(arr, sub0); ASSERT_TRUE(heap_location_collector.MayAlias(loc1, loc2)); ASSERT_TRUE(scheduling_graph.HasImmediateOtherDependency(arr_set_sub0, arr_set_i)); // Test side effect dependency based on LSA analysis: array[i] and array[i+1] - loc1 = heap_location_collector.GetArrayAccessHeapLocation(arr, i); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(arr, add1); + loc1 = heap_location_collector.GetArrayHeapLocation(arr, i); + loc2 = heap_location_collector.GetArrayHeapLocation(arr, add1); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); ASSERT_FALSE(scheduling_graph.HasImmediateOtherDependency(arr_set_add1, arr_set_i)); // Test side effect dependency based on LSA analysis: array[i+1] and array[i-1] - loc1 = heap_location_collector.GetArrayAccessHeapLocation(arr, add1); - loc2 = heap_location_collector.GetArrayAccessHeapLocation(arr, sub1); + loc1 = heap_location_collector.GetArrayHeapLocation(arr, add1); + loc2 = heap_location_collector.GetArrayHeapLocation(arr, sub1); ASSERT_FALSE(heap_location_collector.MayAlias(loc1, loc2)); ASSERT_FALSE(scheduling_graph.HasImmediateOtherDependency(arr_set_sub1, arr_set_add1)); diff --git a/compiler/optimizing/select_generator.cc b/compiler/optimizing/select_generator.cc index 77ec9a6285c614628dc7828392b92a18d47c8143..66e51421ca3a1af9eb7c9b494d32b74c678c07a9 100644 --- a/compiler/optimizing/select_generator.cc +++ b/compiler/optimizing/select_generator.cc @@ -24,8 +24,9 @@ static constexpr size_t kMaxInstructionsInBranch = 1u; HSelectGenerator::HSelectGenerator(HGraph* graph, VariableSizedHandleScope* handles, - OptimizingCompilerStats* stats) - : HOptimization(graph, kSelectGeneratorPassName, stats), + OptimizingCompilerStats* stats, + const char* name) + : HOptimization(graph, name, stats), handle_scope_(handles) { } diff --git a/compiler/optimizing/select_generator.h b/compiler/optimizing/select_generator.h index f8cf00e35a8a951c3e6a6dd145a78220b28e5326..bda57fd5c87a0f3bcdea9ab5391c0d854e8690b0 100644 --- a/compiler/optimizing/select_generator.h +++ b/compiler/optimizing/select_generator.h @@ -65,7 +65,8 @@ class HSelectGenerator : public HOptimization { public: HSelectGenerator(HGraph* graph, VariableSizedHandleScope* handles, - OptimizingCompilerStats* stats); + OptimizingCompilerStats* stats, + const char* name = kSelectGeneratorPassName); void Run() OVERRIDE; diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index e46c9a7081873a3efce8ad69652e6123dfd3ad34..1e49411c72deac45156306fb6757ac09096c54bc 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -45,8 +45,6 @@ void HSharpening::Run() { SharpenInvokeStaticOrDirect(instruction->AsInvokeStaticOrDirect(), codegen_, compiler_driver_); - } else if (instruction->IsLoadString()) { - ProcessLoadString(instruction->AsLoadString()); } // TODO: Move the sharpening of invoke-virtual/-interface/-super from HGraphBuilder // here. Rewrite it to avoid the CompilerDriver's reliance on verifier data @@ -147,10 +145,11 @@ void HSharpening::SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, invoke->SetDispatchInfo(dispatch_info); } -HLoadClass::LoadKind HSharpening::ComputeLoadClassKind(HLoadClass* load_class, - CodeGenerator* codegen, - CompilerDriver* compiler_driver, - const DexCompilationUnit& dex_compilation_unit) { +HLoadClass::LoadKind HSharpening::ComputeLoadClassKind( + HLoadClass* load_class, + CodeGenerator* codegen, + CompilerDriver* compiler_driver, + const DexCompilationUnit& dex_compilation_unit) { Handle klass = load_class->GetClass(); DCHECK(load_class->GetLoadKind() == HLoadClass::LoadKind::kRuntimeCall || load_class->GetLoadKind() == HLoadClass::LoadKind::kReferrersClass) @@ -237,7 +236,12 @@ HLoadClass::LoadKind HSharpening::ComputeLoadClassKind(HLoadClass* load_class, return load_kind; } -void HSharpening::ProcessLoadString(HLoadString* load_string) { +void HSharpening::ProcessLoadString( + HLoadString* load_string, + CodeGenerator* codegen, + CompilerDriver* compiler_driver, + const DexCompilationUnit& dex_compilation_unit, + VariableSizedHandleScope* handles) { DCHECK_EQ(load_string->GetLoadKind(), HLoadString::LoadKind::kRuntimeCall); const DexFile& dex_file = load_string->GetDexFile(); @@ -249,27 +253,27 @@ void HSharpening::ProcessLoadString(HLoadString* load_string) { ClassLinker* class_linker = runtime->GetClassLinker(); ScopedObjectAccess soa(Thread::Current()); StackHandleScope<1> hs(soa.Self()); - Handle dex_cache = IsSameDexFile(dex_file, *compilation_unit_.GetDexFile()) - ? compilation_unit_.GetDexCache() + Handle dex_cache = IsSameDexFile(dex_file, *dex_compilation_unit.GetDexFile()) + ? dex_compilation_unit.GetDexCache() : hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file)); - mirror::String* string = nullptr; + ObjPtr string = nullptr; - if (codegen_->GetCompilerOptions().IsBootImage()) { + if (codegen->GetCompilerOptions().IsBootImage()) { // Compiling boot image. Resolve the string and allocate it if needed, to ensure // the string will be added to the boot image. DCHECK(!runtime->UseJitCompilation()); - string = class_linker->ResolveString(dex_file, string_index, dex_cache); + string = class_linker->ResolveString(string_index, dex_cache); CHECK(string != nullptr); - if (compiler_driver_->GetSupportBootImageFixup()) { - DCHECK(ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &dex_file)); + if (compiler_driver->GetSupportBootImageFixup()) { + DCHECK(ContainsElement(compiler_driver->GetDexFilesForOatFile(), &dex_file)); desired_load_kind = HLoadString::LoadKind::kBootImageLinkTimePcRelative; } else { // compiler_driver_test. Do not sharpen. desired_load_kind = HLoadString::LoadKind::kRuntimeCall; } } else if (runtime->UseJitCompilation()) { - DCHECK(!codegen_->GetCompilerOptions().GetCompilePic()); - string = class_linker->LookupString(dex_file, string_index, dex_cache.Get()); + DCHECK(!codegen->GetCompilerOptions().GetCompilePic()); + string = class_linker->LookupString(string_index, dex_cache.Get()); if (string != nullptr) { if (runtime->GetHeap()->ObjectIsInBootImageSpace(string)) { desired_load_kind = HLoadString::LoadKind::kBootImageAddress; @@ -281,9 +285,9 @@ void HSharpening::ProcessLoadString(HLoadString* load_string) { } } else { // AOT app compilation. Try to lookup the string without allocating if not found. - string = class_linker->LookupString(dex_file, string_index, dex_cache.Get()); + string = class_linker->LookupString(string_index, dex_cache.Get()); if (string != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(string)) { - if (codegen_->GetCompilerOptions().GetCompilePic()) { + if (codegen->GetCompilerOptions().GetCompilePic()) { desired_load_kind = HLoadString::LoadKind::kBootImageInternTable; } else { desired_load_kind = HLoadString::LoadKind::kBootImageAddress; @@ -293,12 +297,12 @@ void HSharpening::ProcessLoadString(HLoadString* load_string) { } } if (string != nullptr) { - load_string->SetString(handles_->NewHandle(string)); + load_string->SetString(handles->NewHandle(string)); } } DCHECK_NE(desired_load_kind, static_cast(-1)); - HLoadString::LoadKind load_kind = codegen_->GetSupportedLoadStringKind(desired_load_kind); + HLoadString::LoadKind load_kind = codegen->GetSupportedLoadStringKind(desired_load_kind); load_string->SetLoadKind(load_kind); } diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h index f74b0afdbf555bb85dbb9c4ca4edfa5dd6a2c3cc..6df7d6d91ed705db13635981f7b2455fdc3e3bf8 100644 --- a/compiler/optimizing/sharpening.h +++ b/compiler/optimizing/sharpening.h @@ -34,25 +34,29 @@ class HSharpening : public HOptimization { public: HSharpening(HGraph* graph, CodeGenerator* codegen, - const DexCompilationUnit& compilation_unit, CompilerDriver* compiler_driver, - VariableSizedHandleScope* handles) - : HOptimization(graph, kSharpeningPassName), + const char* name = kSharpeningPassName) + : HOptimization(graph, name), codegen_(codegen), - compilation_unit_(compilation_unit), - compiler_driver_(compiler_driver), - handles_(handles) { } + compiler_driver_(compiler_driver) { } void Run() OVERRIDE; static constexpr const char* kSharpeningPassName = "sharpening"; + // Used by the builder. + static void ProcessLoadString(HLoadString* load_string, + CodeGenerator* codegen, + CompilerDriver* compiler_driver, + const DexCompilationUnit& dex_compilation_unit, + VariableSizedHandleScope* handles); + // Used by the builder and the inliner. static HLoadClass::LoadKind ComputeLoadClassKind(HLoadClass* load_class, CodeGenerator* codegen, CompilerDriver* compiler_driver, const DexCompilationUnit& dex_compilation_unit) - REQUIRES_SHARED(Locks::mutator_lock_); + REQUIRES_SHARED(Locks::mutator_lock_); // Used by Sharpening and InstructionSimplifier. static void SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, @@ -60,12 +64,8 @@ class HSharpening : public HOptimization { CompilerDriver* compiler_driver); private: - void ProcessLoadString(HLoadString* load_string); - CodeGenerator* codegen_; - const DexCompilationUnit& compilation_unit_; CompilerDriver* compiler_driver_; - VariableSizedHandleScope* handles_; }; } // namespace art diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc index e4edbfdc24087ba1ed25de0abb922624cd376f7a..cb384768b7dd4ae0efa829aba36bcb032669e16c 100644 --- a/compiler/optimizing/ssa_builder.cc +++ b/compiler/optimizing/ssa_builder.cc @@ -328,6 +328,8 @@ bool SsaBuilder::FixAmbiguousArrayOps() { HInstruction* array = aget_int->GetArray(); if (!array->GetReferenceTypeInfo().IsPrimitiveArrayClass()) { // RTP did not type the input array. Bail. + VLOG(compiler) << "Not compiled: Could not infer an array type for array operation at " + << aget_int->GetDexPc(); return false; } @@ -368,6 +370,8 @@ bool SsaBuilder::FixAmbiguousArrayOps() { HInstruction* array = aset->GetArray(); if (!array->GetReferenceTypeInfo().IsPrimitiveArrayClass()) { // RTP did not type the input array. Bail. + VLOG(compiler) << "Not compiled: Could not infer an array type for array operation at " + << aset->GetDexPc(); return false; } diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc index 9ab7a89b333a101ae509b8920930f14d4fa50b28..f6bd05269e1e27ebe56d633e2e9e6f6f93c44b1f 100644 --- a/compiler/optimizing/ssa_liveness_analysis.cc +++ b/compiler/optimizing/ssa_liveness_analysis.cc @@ -474,9 +474,10 @@ size_t LiveInterval::NumberOfSpillSlotsNeeded() const { // For a SIMD operation, compute the number of needed spill slots. // TODO: do through vector type? HInstruction* definition = GetParent()->GetDefinedBy(); - if (definition != nullptr && - definition->IsVecOperation() && - !definition->IsVecExtractScalar()) { + if (definition != nullptr && HVecOperation::ReturnsSIMDValue(definition)) { + if (definition->IsPhi()) { + definition = definition->InputAt(1); // SIMD always appears on back-edge + } return definition->AsVecOperation()->GetVectorNumberOfBytes() / kVRegSize; } // Return number of needed spill slots based on type. diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index 9bc80457a3c9858f099ce9f0fa5edf82d1a903d7..4f43eb374c1545e6c2c9600d1fb22e74b24cd060 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -32,7 +32,6 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, uint32_t num_dex_registers, uint8_t inlining_depth) { DCHECK_EQ(0u, current_entry_.dex_pc) << "EndStackMapEntry not called after BeginStackMapEntry"; - DCHECK_NE(dex_pc, static_cast(-1)) << "invalid dex_pc"; current_entry_.dex_pc = dex_pc; current_entry_.native_pc_code_offset = CodeOffset::FromOffset(native_pc_offset, instruction_set_); current_entry_.register_mask = register_mask; @@ -56,7 +55,10 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, number_of_stack_maps_with_inline_info_++; } - dex_pc_max_ = std::max(dex_pc_max_, dex_pc); + // Note: dex_pc can be kNoDexPc for native method intrinsics. + if (dex_pc != dex::kDexNoIndex && (dex_pc_max_ == dex::kDexNoIndex || dex_pc_max_ < dex_pc)) { + dex_pc_max_ = dex_pc; + } register_mask_max_ = std::max(register_mask_max_, register_mask); current_dex_register_ = 0; } diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index e126609dbad42772958a06b24bd88587eea93e76..579aabdb5f50c409f5dc6b9ebfad7e5664d97210 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -73,7 +73,7 @@ class StackMapStream : public ValueObject { method_indices_(allocator->Adapter(kArenaAllocStackMapStream)), dex_register_entries_(allocator->Adapter(kArenaAllocStackMapStream)), stack_mask_max_(-1), - dex_pc_max_(0), + dex_pc_max_(kNoDexPc), register_mask_max_(0), number_of_stack_maps_with_inline_info_(0), dex_map_hash_to_stack_map_indices_(std::less(), diff --git a/compiler/optimizing/superblock_cloner_test.cc b/compiler/optimizing/superblock_cloner_test.cc new file mode 100644 index 0000000000000000000000000000000000000000..fd77eb81fc899e44ab7526df7df493ea688bcd7b --- /dev/null +++ b/compiler/optimizing/superblock_cloner_test.cc @@ -0,0 +1,185 @@ +/* + * 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 "graph_checker.h" +#include "nodes.h" +#include "optimizing_unit_test.h" + +#include "gtest/gtest.h" + +namespace art { + +// This class provides methods and helpers for testing various cloning and copying routines: +// individual instruction cloning and cloning of the more coarse-grain structures. +class SuperblockClonerTest : public OptimizingUnitTest { + public: + SuperblockClonerTest() + : graph_(CreateGraph()), entry_block_(nullptr), exit_block_(nullptr), parameter_(nullptr) {} + + void CreateBasicLoopControlFlow(/* out */ HBasicBlock** header_p, + /* out */ HBasicBlock** body_p) { + entry_block_ = new (GetAllocator()) HBasicBlock(graph_); + graph_->AddBlock(entry_block_); + graph_->SetEntryBlock(entry_block_); + + HBasicBlock* loop_preheader = new (GetAllocator()) HBasicBlock(graph_); + HBasicBlock* loop_header = new (GetAllocator()) HBasicBlock(graph_); + HBasicBlock* loop_body = new (GetAllocator()) HBasicBlock(graph_); + HBasicBlock* loop_exit = new (GetAllocator()) HBasicBlock(graph_); + + graph_->AddBlock(loop_preheader); + graph_->AddBlock(loop_header); + graph_->AddBlock(loop_body); + graph_->AddBlock(loop_exit); + + exit_block_ = new (GetAllocator()) HBasicBlock(graph_); + graph_->AddBlock(exit_block_); + graph_->SetExitBlock(exit_block_); + + entry_block_->AddSuccessor(loop_preheader); + loop_preheader->AddSuccessor(loop_header); + // Loop exit first to have a proper exit condition/target for HIf. + loop_header->AddSuccessor(loop_exit); + loop_header->AddSuccessor(loop_body); + loop_body->AddSuccessor(loop_header); + loop_exit->AddSuccessor(exit_block_); + + *header_p = loop_header; + *body_p = loop_body; + + parameter_ = new (GetAllocator()) HParameterValue(graph_->GetDexFile(), + dex::TypeIndex(0), + 0, + DataType::Type::kInt32); + entry_block_->AddInstruction(parameter_); + loop_exit->AddInstruction(new (GetAllocator()) HReturnVoid()); + exit_block_->AddInstruction(new (GetAllocator()) HExit()); + } + + void CreateBasicLoopDataFlow(HBasicBlock* loop_header, HBasicBlock* loop_body) { + uint32_t dex_pc = 0; + + // Entry block. + HIntConstant* const_0 = graph_->GetIntConstant(0); + HIntConstant* const_1 = graph_->GetIntConstant(1); + HIntConstant* const_128 = graph_->GetIntConstant(128); + + // Header block. + HPhi* phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32); + HInstruction* suspend_check = new (GetAllocator()) HSuspendCheck(); + + loop_header->AddPhi(phi); + loop_header->AddInstruction(suspend_check); + loop_header->AddInstruction(new (GetAllocator()) HGreaterThanOrEqual(phi, const_128)); + loop_header->AddInstruction(new (GetAllocator()) HIf(parameter_)); + + // Loop body block. + HInstruction* null_check = new (GetAllocator()) HNullCheck(parameter_, dex_pc); + HInstruction* array_length = new (GetAllocator()) HArrayLength(null_check, dex_pc); + HInstruction* bounds_check = new (GetAllocator()) HBoundsCheck(phi, array_length, dex_pc); + HInstruction* array_get = + new (GetAllocator()) HArrayGet(null_check, bounds_check, DataType::Type::kInt32, dex_pc); + HInstruction* add = new (GetAllocator()) HAdd(DataType::Type::kInt32, array_get, const_1); + HInstruction* array_set = + new (GetAllocator()) HArraySet(null_check, bounds_check, add, DataType::Type::kInt32, dex_pc); + HInstruction* induction_inc = new (GetAllocator()) HAdd(DataType::Type::kInt32, phi, const_1); + + loop_body->AddInstruction(null_check); + loop_body->AddInstruction(array_length); + loop_body->AddInstruction(bounds_check); + loop_body->AddInstruction(array_get); + loop_body->AddInstruction(add); + loop_body->AddInstruction(array_set); + loop_body->AddInstruction(induction_inc); + loop_body->AddInstruction(new (GetAllocator()) HGoto()); + + phi->AddInput(const_0); + phi->AddInput(induction_inc); + + graph_->SetHasBoundsChecks(true); + + // Adjust HEnvironment for each instruction which require that. + ArenaVector current_locals({phi, const_128, parameter_}, + GetAllocator()->Adapter(kArenaAllocInstruction)); + + HEnvironment* env = ManuallyBuildEnvFor(suspend_check, ¤t_locals); + null_check->CopyEnvironmentFrom(env); + bounds_check->CopyEnvironmentFrom(env); + } + + HEnvironment* ManuallyBuildEnvFor(HInstruction* instruction, + ArenaVector* current_locals) { + HEnvironment* environment = new (GetAllocator()) HEnvironment( + (GetAllocator()), + current_locals->size(), + graph_->GetArtMethod(), + instruction->GetDexPc(), + instruction); + + environment->CopyFrom(ArrayRef(*current_locals)); + instruction->SetRawEnvironment(environment); + return environment; + } + + bool CheckGraph() { + GraphChecker checker(graph_); + checker.Run(); + if (!checker.IsValid()) { + for (const std::string& error : checker.GetErrors()) { + std::cout << error << std::endl; + } + return false; + } + return true; + } + + HGraph* graph_; + + HBasicBlock* entry_block_; + HBasicBlock* exit_block_; + + HInstruction* parameter_; +}; + +TEST_F(SuperblockClonerTest, IndividualInstrCloner) { + HBasicBlock* header = nullptr; + HBasicBlock* loop_body = nullptr; + + CreateBasicLoopControlFlow(&header, &loop_body); + CreateBasicLoopDataFlow(header, loop_body); + graph_->BuildDominatorTree(); + ASSERT_TRUE(CheckGraph()); + + HSuspendCheck* old_suspend_check = header->GetLoopInformation()->GetSuspendCheck(); + CloneAndReplaceInstructionVisitor visitor(graph_); + // Do instruction cloning and replacement twice with different visiting order. + + visitor.VisitInsertionOrder(); + size_t instr_replaced_by_clones_count = visitor.GetInstrReplacedByClonesCount(); + EXPECT_EQ(instr_replaced_by_clones_count, 12u); + EXPECT_TRUE(CheckGraph()); + + visitor.VisitReversePostOrder(); + instr_replaced_by_clones_count = visitor.GetInstrReplacedByClonesCount(); + EXPECT_EQ(instr_replaced_by_clones_count, 24u); + EXPECT_TRUE(CheckGraph()); + + HSuspendCheck* new_suspend_check = header->GetLoopInformation()->GetSuspendCheck(); + EXPECT_NE(new_suspend_check, old_suspend_check); + EXPECT_NE(new_suspend_check, nullptr); +} + +} // namespace art diff --git a/compiler/utils/arm/assembler_arm_vixl.h b/compiler/utils/arm/assembler_arm_vixl.h index 0e73e6bf9e517e220f5c9006f664ec01bf74705b..1377e6407359847a6348f2a5ae0d45b2b7d80695 100644 --- a/compiler/utils/arm/assembler_arm_vixl.h +++ b/compiler/utils/arm/assembler_arm_vixl.h @@ -17,8 +17,10 @@ #ifndef ART_COMPILER_UTILS_ARM_ASSEMBLER_ARM_VIXL_H_ #define ART_COMPILER_UTILS_ARM_ASSEMBLER_ARM_VIXL_H_ +#include + #include "base/arena_containers.h" -#include "base/logging.h" +#include "base/macros.h" #include "constants_arm.h" #include "offsets.h" #include "utils/arm/assembler_arm_shared.h" diff --git a/compiler/utils/arm/constants_arm.h b/compiler/utils/arm/constants_arm.h index 5b87e3e7f8f038e6c634354bfb1cd99be9fc0cc7..66252bed865260a3fe604bf6b5c6912ad06924b5 100644 --- a/compiler/utils/arm/constants_arm.h +++ b/compiler/utils/arm/constants_arm.h @@ -21,9 +21,10 @@ #include +#include + #include "arch/arm/registers_arm.h" #include "base/casts.h" -#include "base/logging.h" #include "globals.h" namespace art { diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.h b/compiler/utils/arm/jni_macro_assembler_arm_vixl.h index c13c9af8190837fc72ad643030cdd6b1a9e44539..4bc5d69f4dc86732a83e21e4552b0c7126ed14ab 100644 --- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.h +++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.h @@ -17,8 +17,10 @@ #ifndef ART_COMPILER_UTILS_ARM_JNI_MACRO_ASSEMBLER_ARM_VIXL_H_ #define ART_COMPILER_UTILS_ARM_JNI_MACRO_ASSEMBLER_ARM_VIXL_H_ +#include + #include "base/arena_containers.h" -#include "base/logging.h" +#include "base/macros.h" #include "constants_arm.h" #include "offsets.h" #include "utils/arm/assembler_arm_shared.h" diff --git a/compiler/utils/arm/managed_register_arm.h b/compiler/utils/arm/managed_register_arm.h index 2be2d5638e5b7a8965feeb25a0b8270ee9eacc84..26f23b2ed6c6cdadbea670db1735ae96a885068c 100644 --- a/compiler/utils/arm/managed_register_arm.h +++ b/compiler/utils/arm/managed_register_arm.h @@ -17,7 +17,8 @@ #ifndef ART_COMPILER_UTILS_ARM_MANAGED_REGISTER_ARM_H_ #define ART_COMPILER_UTILS_ARM_MANAGED_REGISTER_ARM_H_ -#include "base/logging.h" +#include + #include "constants_arm.h" #include "debug/dwarf/register.h" #include "utils/managed_register.h" diff --git a/compiler/utils/arm64/assembler_arm64.cc b/compiler/utils/arm64/assembler_arm64.cc index bb989588d68c4b6a1ac382f83df7b49531c8ae8e..c83fd4404aec9ff09a1a01e2fdb564475df5d76c 100644 --- a/compiler/utils/arm64/assembler_arm64.cc +++ b/compiler/utils/arm64/assembler_arm64.cc @@ -15,7 +15,6 @@ */ #include "assembler_arm64.h" -#include "base/logging.h" #include "entrypoints/quick/quick_entrypoints.h" #include "heap_poisoning.h" #include "offsets.h" diff --git a/compiler/utils/arm64/assembler_arm64.h b/compiler/utils/arm64/assembler_arm64.h index e5ec24add07fd46b279dfb8a21a791bd9be20f30..8983af2677464e09e8107128f4ad7110d62776d9 100644 --- a/compiler/utils/arm64/assembler_arm64.h +++ b/compiler/utils/arm64/assembler_arm64.h @@ -21,8 +21,10 @@ #include #include +#include + #include "base/arena_containers.h" -#include "base/logging.h" +#include "base/macros.h" #include "offsets.h" #include "utils/arm64/managed_register_arm64.h" #include "utils/assembler.h" diff --git a/compiler/utils/arm64/jni_macro_assembler_arm64.cc b/compiler/utils/arm64/jni_macro_assembler_arm64.cc index 573bb6d4bea95f3b3b6c2dfe74c89943e7c561fd..a5aa1c12b3c0d5097d22e8e60727bc8327d7c360 100644 --- a/compiler/utils/arm64/jni_macro_assembler_arm64.cc +++ b/compiler/utils/arm64/jni_macro_assembler_arm64.cc @@ -16,7 +16,6 @@ #include "jni_macro_assembler_arm64.h" -#include "base/logging.h" #include "entrypoints/quick/quick_entrypoints.h" #include "managed_register_arm64.h" #include "offsets.h" diff --git a/compiler/utils/arm64/jni_macro_assembler_arm64.h b/compiler/utils/arm64/jni_macro_assembler_arm64.h index ce39a136927ad14e42fc7b053fe57dd7f1e953fe..f531b2aa513a7b0a023d21de678e584d5afb3e32 100644 --- a/compiler/utils/arm64/jni_macro_assembler_arm64.h +++ b/compiler/utils/arm64/jni_macro_assembler_arm64.h @@ -21,10 +21,12 @@ #include #include +#include + #include "assembler_arm64.h" #include "base/arena_containers.h" #include "base/enums.h" -#include "base/logging.h" +#include "base/macros.h" #include "offsets.h" #include "utils/assembler.h" #include "utils/jni_macro_assembler.h" diff --git a/compiler/utils/arm64/managed_register_arm64.h b/compiler/utils/arm64/managed_register_arm64.h index 7378a0a081e1758cca02963eed628d9ec3bb23e3..9ce7ec9a977b9c4834509c6535ad8e9a8d6a5984 100644 --- a/compiler/utils/arm64/managed_register_arm64.h +++ b/compiler/utils/arm64/managed_register_arm64.h @@ -17,8 +17,9 @@ #ifndef ART_COMPILER_UTILS_ARM64_MANAGED_REGISTER_ARM64_H_ #define ART_COMPILER_UTILS_ARM64_MANAGED_REGISTER_ARM64_H_ +#include + #include "arch/arm64/registers_arm64.h" -#include "base/logging.h" #include "debug/dwarf/register.h" #include "utils/managed_register.h" diff --git a/compiler/utils/assembler.h b/compiler/utils/assembler.h index e0cef859e1f25854671db3d3c911e354078565ed..5b0cd6baa8d206d9b392486f7f3effdd06089d85 100644 --- a/compiler/utils/assembler.h +++ b/compiler/utils/assembler.h @@ -19,6 +19,8 @@ #include +#include + #include "arch/instruction_set.h" #include "arch/instruction_set_features.h" #include "arm/constants_arm.h" @@ -26,7 +28,6 @@ #include "base/arena_object.h" #include "base/array_ref.h" #include "base/enums.h" -#include "base/logging.h" #include "base/macros.h" #include "debug/dwarf/debug_frame_opcode_writer.h" #include "label.h" diff --git a/compiler/utils/intrusive_forward_list.h b/compiler/utils/intrusive_forward_list.h index 5a358ac2c44ef97db74eb2dd1021a4ff78c6f88d..ccdd32aad4f8220de97f0bce315d71154135dbc0 100644 --- a/compiler/utils/intrusive_forward_list.h +++ b/compiler/utils/intrusive_forward_list.h @@ -23,8 +23,9 @@ #include #include +#include + #include "base/casts.h" -#include "base/logging.h" #include "base/macros.h" namespace art { diff --git a/compiler/utils/intrusive_forward_list_test.cc b/compiler/utils/intrusive_forward_list_test.cc index 939676cdc85fd1493373a43e3c02941c749504ef..e97c3044d0b7b1aadd70476b169266559e70401a 100644 --- a/compiler/utils/intrusive_forward_list_test.cc +++ b/compiler/utils/intrusive_forward_list_test.cc @@ -574,11 +574,11 @@ void IntrusiveForwardListTest::Remove() { ref.remove(4); ifl.remove(4); ASSERT_LISTS_EQUAL(ref, ifl); - auto odd = [](ValueType value) { return (value.value & 1) != 0; }; // NOLINT(readability/braces) + auto odd = [](ValueType value) { return (value.value & 1) != 0; }; ref.remove_if(odd); ifl.remove_if(odd); ASSERT_LISTS_EQUAL(ref, ifl); - auto all = [](ValueType value ATTRIBUTE_UNUSED) { return true; }; // NOLINT(readability/braces) + auto all = [](ValueType value ATTRIBUTE_UNUSED) { return true; }; ref.remove_if(all); ifl.remove_if(all); ASSERT_LISTS_EQUAL(ref, ifl); @@ -721,7 +721,7 @@ void IntrusiveForwardListTest::ModifyValue() { ListType ifl(storage.begin(), storage.end()); ASSERT_LISTS_EQUAL(ref, ifl); - auto add1 = [](const ValueType& value) { return value.value + 1; }; // NOLINT [readability/braces] + auto add1 = [](const ValueType& value) { return value.value + 1; }; std::transform(ref.begin(), ref.end(), ref.begin(), add1); std::transform(ifl.begin(), ifl.end(), ifl.begin(), add1); ASSERT_LISTS_EQUAL(ref, ifl); diff --git a/compiler/utils/jni_macro_assembler.h b/compiler/utils/jni_macro_assembler.h index 0fc1353bf57129f324dfc94feb4f16f7ce2363c8..f5df926749d40a4bb7c1168e043cd0b60f0f32f9 100644 --- a/compiler/utils/jni_macro_assembler.h +++ b/compiler/utils/jni_macro_assembler.h @@ -19,12 +19,13 @@ #include +#include + #include "arch/instruction_set.h" #include "base/arena_allocator.h" #include "base/arena_object.h" #include "base/array_ref.h" #include "base/enums.h" -#include "base/logging.h" #include "base/macros.h" #include "managed_register.h" #include "offsets.h" diff --git a/compiler/utils/label.h b/compiler/utils/label.h index b9d4e9c521927da74b4a102a4dcb22b170d3030f..3c91b2ffd1e94050c16f4298b3a63e13f9862365 100644 --- a/compiler/utils/label.h +++ b/compiler/utils/label.h @@ -17,8 +17,8 @@ #ifndef ART_COMPILER_UTILS_LABEL_H_ #define ART_COMPILER_UTILS_LABEL_H_ -#include "base/logging.h" -#include "base/macros.h" +#include +#include namespace art { diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc index eb75f8b67c8826637f243692d6d617cd929ef065..2218ef9af29e07a880480fa5e937b6f5b9dceaed 100644 --- a/compiler/utils/mips/assembler_mips.cc +++ b/compiler/utils/mips/assembler_mips.cc @@ -42,26 +42,13 @@ std::ostream& operator<<(std::ostream& os, const DRegister& rhs) { MipsAssembler::DelaySlot::DelaySlot() : instruction_(0), - gpr_outs_mask_(0), - gpr_ins_mask_(0), - fpr_outs_mask_(0), - fpr_ins_mask_(0), - cc_outs_mask_(0), - cc_ins_mask_(0), patcher_label_(nullptr) {} -void MipsAssembler::DsFsmInstr(uint32_t instruction, - uint32_t gpr_outs_mask, - uint32_t gpr_ins_mask, - uint32_t fpr_outs_mask, - uint32_t fpr_ins_mask, - uint32_t cc_outs_mask, - uint32_t cc_ins_mask, - MipsLabel* patcher_label) { +InOutRegMasks& MipsAssembler::DsFsmInstr(uint32_t instruction, MipsLabel* patcher_label) { if (!reordering_) { CHECK_EQ(ds_fsm_state_, kExpectingLabel); CHECK_EQ(delay_slot_.instruction_, 0u); - return; + return delay_slot_.masks_; } switch (ds_fsm_state_) { case kExpectingLabel: @@ -92,13 +79,9 @@ void MipsAssembler::DsFsmInstr(uint32_t instruction, break; } delay_slot_.instruction_ = instruction; - delay_slot_.gpr_outs_mask_ = gpr_outs_mask & ~1u; // Ignore register ZERO. - delay_slot_.gpr_ins_mask_ = gpr_ins_mask & ~1u; // Ignore register ZERO. - delay_slot_.fpr_outs_mask_ = fpr_outs_mask; - delay_slot_.fpr_ins_mask_ = fpr_ins_mask; - delay_slot_.cc_outs_mask_ = cc_outs_mask; - delay_slot_.cc_ins_mask_ = cc_ins_mask; + delay_slot_.masks_ = InOutRegMasks(); delay_slot_.patcher_label_ = patcher_label; + return delay_slot_.masks_; } void MipsAssembler::DsFsmLabel() { @@ -167,73 +150,7 @@ size_t MipsAssembler::CodePosition() { } void MipsAssembler::DsFsmInstrNop(uint32_t instruction ATTRIBUTE_UNUSED) { - DsFsmInstr(0, 0, 0, 0, 0, 0, 0); -} - -void MipsAssembler::DsFsmInstrRrr(uint32_t instruction, - Register out, - Register in1, - Register in2, - MipsLabel* patcher_label) { - DsFsmInstr(instruction, (1u << out), (1u << in1) | (1u << in2), 0, 0, 0, 0, patcher_label); -} - -void MipsAssembler::DsFsmInstrRrrr(uint32_t instruction, - Register in1_out, - Register in2, - Register in3) { - DsFsmInstr(instruction, (1u << in1_out), (1u << in1_out) | (1u << in2) | (1u << in3), 0, 0, 0, 0); -} - -void MipsAssembler::DsFsmInstrFff(uint32_t instruction, - FRegister out, - FRegister in1, - FRegister in2) { - DsFsmInstr(instruction, 0, 0, (1u << out), (1u << in1) | (1u << in2), 0, 0); -} - -void MipsAssembler::DsFsmInstrFfff(uint32_t instruction, - FRegister in1_out, - FRegister in2, - FRegister in3) { - DsFsmInstr(instruction, 0, 0, (1u << in1_out), (1u << in1_out) | (1u << in2) | (1u << in3), 0, 0); -} - -void MipsAssembler::DsFsmInstrFffr(uint32_t instruction, - FRegister in1_out, - FRegister in2, - Register in3) { - DsFsmInstr(instruction, 0, (1u << in3), (1u << in1_out), (1u << in1_out) | (1u << in2), 0, 0); -} - -void MipsAssembler::DsFsmInstrRf(uint32_t instruction, Register out, FRegister in) { - DsFsmInstr(instruction, (1u << out), 0, 0, (1u << in), 0, 0); -} - -void MipsAssembler::DsFsmInstrFr(uint32_t instruction, FRegister out, Register in) { - DsFsmInstr(instruction, 0, (1u << in), (1u << out), 0, 0, 0); -} - -void MipsAssembler::DsFsmInstrFR(uint32_t instruction, FRegister in1, Register in2) { - DsFsmInstr(instruction, 0, (1u << in2), 0, (1u << in1), 0, 0); -} - -void MipsAssembler::DsFsmInstrCff(uint32_t instruction, int cc_out, FRegister in1, FRegister in2) { - DsFsmInstr(instruction, 0, 0, 0, (1u << in1) | (1u << in2), (1 << cc_out), 0); -} - -void MipsAssembler::DsFsmInstrRrrc(uint32_t instruction, - Register in1_out, - Register in2, - int cc_in) { - DsFsmInstr(instruction, (1u << in1_out), (1u << in1_out) | (1u << in2), 0, 0, 0, (1 << cc_in)); -} - -void MipsAssembler::DsFsmInstrFffc(uint32_t instruction, - FRegister in1_out, - FRegister in2, - int cc_in) { - DsFsmInstr(instruction, 0, 0, (1u << in1_out), (1u << in1_out) | (1u << in2), 0, (1 << cc_in)); + DsFsmInstr(0); } void MipsAssembler::FinalizeCode() { @@ -535,14 +452,14 @@ uint32_t MipsAssembler::EmitMsa2RF(int operation, } void MipsAssembler::Addu(Register rd, Register rs, Register rt) { - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x21), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x21)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::Addiu(Register rt, Register rs, uint16_t imm16, MipsLabel* patcher_label) { if (patcher_label != nullptr) { Bind(patcher_label); } - DsFsmInstrRrr(EmitI(0x9, rs, rt, imm16), rt, rs, rs, patcher_label); + DsFsmInstr(EmitI(0x9, rs, rt, imm16), patcher_label).GprOuts(rt).GprIns(rs); } void MipsAssembler::Addiu(Register rt, Register rs, uint16_t imm16) { @@ -550,32 +467,32 @@ void MipsAssembler::Addiu(Register rt, Register rs, uint16_t imm16) { } void MipsAssembler::Subu(Register rd, Register rs, Register rt) { - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x23), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x23)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::MultR2(Register rs, Register rt) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitR(0, rs, rt, static_cast(0), 0, 0x18), ZERO, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, static_cast(0), 0, 0x18)).GprIns(rs, rt); } void MipsAssembler::MultuR2(Register rs, Register rt) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitR(0, rs, rt, static_cast(0), 0, 0x19), ZERO, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, static_cast(0), 0, 0x19)).GprIns(rs, rt); } void MipsAssembler::DivR2(Register rs, Register rt) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitR(0, rs, rt, static_cast(0), 0, 0x1a), ZERO, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, static_cast(0), 0, 0x1a)).GprIns(rs, rt); } void MipsAssembler::DivuR2(Register rs, Register rt) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitR(0, rs, rt, static_cast(0), 0, 0x1b), ZERO, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, static_cast(0), 0, 0x1b)).GprIns(rs, rt); } void MipsAssembler::MulR2(Register rd, Register rs, Register rt) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitR(0x1c, rs, rt, rd, 0, 2), rd, rs, rt); + DsFsmInstr(EmitR(0x1c, rs, rt, rd, 0, 2)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::DivR2(Register rd, Register rs, Register rt) { @@ -604,179 +521,181 @@ void MipsAssembler::ModuR2(Register rd, Register rs, Register rt) { void MipsAssembler::MulR6(Register rd, Register rs, Register rt) { CHECK(IsR6()); - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 2, 0x18), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 2, 0x18)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::MuhR6(Register rd, Register rs, Register rt) { CHECK(IsR6()); - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 3, 0x18), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 3, 0x18)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::MuhuR6(Register rd, Register rs, Register rt) { CHECK(IsR6()); - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 3, 0x19), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 3, 0x19)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::DivR6(Register rd, Register rs, Register rt) { CHECK(IsR6()); - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 2, 0x1a), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 2, 0x1a)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::ModR6(Register rd, Register rs, Register rt) { CHECK(IsR6()); - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 3, 0x1a), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 3, 0x1a)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::DivuR6(Register rd, Register rs, Register rt) { CHECK(IsR6()); - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 2, 0x1b), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 2, 0x1b)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::ModuR6(Register rd, Register rs, Register rt) { CHECK(IsR6()); - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 3, 0x1b), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 3, 0x1b)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::And(Register rd, Register rs, Register rt) { - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x24), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x24)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::Andi(Register rt, Register rs, uint16_t imm16) { - DsFsmInstrRrr(EmitI(0xc, rs, rt, imm16), rt, rs, rs); + DsFsmInstr(EmitI(0xc, rs, rt, imm16)).GprOuts(rt).GprIns(rs); } void MipsAssembler::Or(Register rd, Register rs, Register rt) { - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x25), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x25)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::Ori(Register rt, Register rs, uint16_t imm16) { - DsFsmInstrRrr(EmitI(0xd, rs, rt, imm16), rt, rs, rs); + DsFsmInstr(EmitI(0xd, rs, rt, imm16)).GprOuts(rt).GprIns(rs); } void MipsAssembler::Xor(Register rd, Register rs, Register rt) { - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x26), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x26)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::Xori(Register rt, Register rs, uint16_t imm16) { - DsFsmInstrRrr(EmitI(0xe, rs, rt, imm16), rt, rs, rs); + DsFsmInstr(EmitI(0xe, rs, rt, imm16)).GprOuts(rt).GprIns(rs); } void MipsAssembler::Nor(Register rd, Register rs, Register rt) { - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x27), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x27)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::Movz(Register rd, Register rs, Register rt) { CHECK(!IsR6()); - DsFsmInstrRrrr(EmitR(0, rs, rt, rd, 0, 0x0A), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x0A)).GprInOuts(rd).GprIns(rs, rt); } void MipsAssembler::Movn(Register rd, Register rs, Register rt) { CHECK(!IsR6()); - DsFsmInstrRrrr(EmitR(0, rs, rt, rd, 0, 0x0B), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x0B)).GprInOuts(rd).GprIns(rs, rt); } void MipsAssembler::Seleqz(Register rd, Register rs, Register rt) { CHECK(IsR6()); - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x35), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x35)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::Selnez(Register rd, Register rs, Register rt) { CHECK(IsR6()); - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x37), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x37)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::ClzR6(Register rd, Register rs) { CHECK(IsR6()); - DsFsmInstrRrr(EmitR(0, rs, static_cast(0), rd, 0x01, 0x10), rd, rs, rs); + DsFsmInstr(EmitR(0, rs, static_cast(0), rd, 0x01, 0x10)).GprOuts(rd).GprIns(rs); } void MipsAssembler::ClzR2(Register rd, Register rs) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitR(0x1C, rs, rd, rd, 0, 0x20), rd, rs, rs); + DsFsmInstr(EmitR(0x1C, rs, rd, rd, 0, 0x20)).GprOuts(rd).GprIns(rs); } void MipsAssembler::CloR6(Register rd, Register rs) { CHECK(IsR6()); - DsFsmInstrRrr(EmitR(0, rs, static_cast(0), rd, 0x01, 0x11), rd, rs, rs); + DsFsmInstr(EmitR(0, rs, static_cast(0), rd, 0x01, 0x11)).GprOuts(rd).GprIns(rs); } void MipsAssembler::CloR2(Register rd, Register rs) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitR(0x1C, rs, rd, rd, 0, 0x21), rd, rs, rs); + DsFsmInstr(EmitR(0x1C, rs, rd, rd, 0, 0x21)).GprOuts(rd).GprIns(rs); } void MipsAssembler::Seb(Register rd, Register rt) { - DsFsmInstrRrr(EmitR(0x1f, static_cast(0), rt, rd, 0x10, 0x20), rd, rt, rt); + DsFsmInstr(EmitR(0x1f, static_cast(0), rt, rd, 0x10, 0x20)).GprOuts(rd).GprIns(rt); } void MipsAssembler::Seh(Register rd, Register rt) { - DsFsmInstrRrr(EmitR(0x1f, static_cast(0), rt, rd, 0x18, 0x20), rd, rt, rt); + DsFsmInstr(EmitR(0x1f, static_cast(0), rt, rd, 0x18, 0x20)).GprOuts(rd).GprIns(rt); } void MipsAssembler::Wsbh(Register rd, Register rt) { - DsFsmInstrRrr(EmitR(0x1f, static_cast(0), rt, rd, 2, 0x20), rd, rt, rt); + DsFsmInstr(EmitR(0x1f, static_cast(0), rt, rd, 2, 0x20)).GprOuts(rd).GprIns(rt); } void MipsAssembler::Bitswap(Register rd, Register rt) { CHECK(IsR6()); - DsFsmInstrRrr(EmitR(0x1f, static_cast(0), rt, rd, 0x0, 0x20), rd, rt, rt); + DsFsmInstr(EmitR(0x1f, static_cast(0), rt, rd, 0x0, 0x20)).GprOuts(rd).GprIns(rt); } void MipsAssembler::Sll(Register rd, Register rt, int shamt) { CHECK(IsUint<5>(shamt)) << shamt; - DsFsmInstrRrr(EmitR(0, static_cast(0), rt, rd, shamt, 0x00), rd, rt, rt); + DsFsmInstr(EmitR(0, static_cast(0), rt, rd, shamt, 0x00)).GprOuts(rd).GprIns(rt); } void MipsAssembler::Srl(Register rd, Register rt, int shamt) { CHECK(IsUint<5>(shamt)) << shamt; - DsFsmInstrRrr(EmitR(0, static_cast(0), rt, rd, shamt, 0x02), rd, rt, rt); + DsFsmInstr(EmitR(0, static_cast(0), rt, rd, shamt, 0x02)).GprOuts(rd).GprIns(rt); } void MipsAssembler::Rotr(Register rd, Register rt, int shamt) { CHECK(IsUint<5>(shamt)) << shamt; - DsFsmInstrRrr(EmitR(0, static_cast(1), rt, rd, shamt, 0x02), rd, rt, rt); + DsFsmInstr(EmitR(0, static_cast(1), rt, rd, shamt, 0x02)).GprOuts(rd).GprIns(rt); } void MipsAssembler::Sra(Register rd, Register rt, int shamt) { CHECK(IsUint<5>(shamt)) << shamt; - DsFsmInstrRrr(EmitR(0, static_cast(0), rt, rd, shamt, 0x03), rd, rt, rt); + DsFsmInstr(EmitR(0, static_cast(0), rt, rd, shamt, 0x03)).GprOuts(rd).GprIns(rt); } void MipsAssembler::Sllv(Register rd, Register rt, Register rs) { - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x04), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x04)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::Srlv(Register rd, Register rt, Register rs) { - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x06), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x06)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::Rotrv(Register rd, Register rt, Register rs) { - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 1, 0x06), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 1, 0x06)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::Srav(Register rd, Register rt, Register rs) { - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x07), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x07)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::Ext(Register rd, Register rt, int pos, int size) { CHECK(IsUint<5>(pos)) << pos; CHECK(0 < size && size <= 32) << size; CHECK(0 < pos + size && pos + size <= 32) << pos << " + " << size; - DsFsmInstrRrr(EmitR(0x1f, rt, rd, static_cast(size - 1), pos, 0x00), rd, rt, rt); + DsFsmInstr(EmitR(0x1f, rt, rd, static_cast(size - 1), pos, 0x00)) + .GprOuts(rd).GprIns(rt); } void MipsAssembler::Ins(Register rd, Register rt, int pos, int size) { CHECK(IsUint<5>(pos)) << pos; CHECK(0 < size && size <= 32) << size; CHECK(0 < pos + size && pos + size <= 32) << pos << " + " << size; - DsFsmInstrRrr(EmitR(0x1f, rt, rd, static_cast(pos + size - 1), pos, 0x04), rd, rd, rt); + DsFsmInstr(EmitR(0x1f, rt, rd, static_cast(pos + size - 1), pos, 0x04)) + .GprInOuts(rd).GprIns(rt); } void MipsAssembler::Lsa(Register rd, Register rs, Register rt, int saPlusOne) { CHECK(IsR6() || HasMsa()); CHECK(1 <= saPlusOne && saPlusOne <= 4) << saPlusOne; int sa = saPlusOne - 1; - DsFsmInstrRrr(EmitR(0x0, rs, rt, rd, sa, 0x05), rd, rs, rt); + DsFsmInstr(EmitR(0x0, rs, rt, rd, sa, 0x05)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::ShiftAndAdd(Register dst, @@ -798,18 +717,18 @@ void MipsAssembler::ShiftAndAdd(Register dst, } void MipsAssembler::Lb(Register rt, Register rs, uint16_t imm16) { - DsFsmInstrRrr(EmitI(0x20, rs, rt, imm16), rt, rs, rs); + DsFsmInstr(EmitI(0x20, rs, rt, imm16)).GprOuts(rt).GprIns(rs); } void MipsAssembler::Lh(Register rt, Register rs, uint16_t imm16) { - DsFsmInstrRrr(EmitI(0x21, rs, rt, imm16), rt, rs, rs); + DsFsmInstr(EmitI(0x21, rs, rt, imm16)).GprOuts(rt).GprIns(rs); } void MipsAssembler::Lw(Register rt, Register rs, uint16_t imm16, MipsLabel* patcher_label) { if (patcher_label != nullptr) { Bind(patcher_label); } - DsFsmInstrRrr(EmitI(0x23, rs, rt, imm16), rt, rs, rs, patcher_label); + DsFsmInstr(EmitI(0x23, rs, rt, imm16), patcher_label).GprOuts(rt).GprIns(rs); } void MipsAssembler::Lw(Register rt, Register rs, uint16_t imm16) { @@ -818,20 +737,20 @@ void MipsAssembler::Lw(Register rt, Register rs, uint16_t imm16) { void MipsAssembler::Lwl(Register rt, Register rs, uint16_t imm16) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitI(0x22, rs, rt, imm16), rt, rt, rs); + DsFsmInstr(EmitI(0x22, rs, rt, imm16)).GprInOuts(rt).GprIns(rs); } void MipsAssembler::Lwr(Register rt, Register rs, uint16_t imm16) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitI(0x26, rs, rt, imm16), rt, rt, rs); + DsFsmInstr(EmitI(0x26, rs, rt, imm16)).GprInOuts(rt).GprIns(rs); } void MipsAssembler::Lbu(Register rt, Register rs, uint16_t imm16) { - DsFsmInstrRrr(EmitI(0x24, rs, rt, imm16), rt, rs, rs); + DsFsmInstr(EmitI(0x24, rs, rt, imm16)).GprOuts(rt).GprIns(rs); } void MipsAssembler::Lhu(Register rt, Register rs, uint16_t imm16) { - DsFsmInstrRrr(EmitI(0x25, rs, rt, imm16), rt, rs, rs); + DsFsmInstr(EmitI(0x25, rs, rt, imm16)).GprOuts(rt).GprIns(rs); } void MipsAssembler::Lwpc(Register rs, uint32_t imm19) { @@ -841,12 +760,12 @@ void MipsAssembler::Lwpc(Register rs, uint32_t imm19) { } void MipsAssembler::Lui(Register rt, uint16_t imm16) { - DsFsmInstrRrr(EmitI(0xf, static_cast(0), rt, imm16), rt, ZERO, ZERO); + DsFsmInstr(EmitI(0xf, static_cast(0), rt, imm16)).GprOuts(rt); } void MipsAssembler::Aui(Register rt, Register rs, uint16_t imm16) { CHECK(IsR6()); - DsFsmInstrRrr(EmitI(0xf, rs, rt, imm16), rt, rt, rs); + DsFsmInstr(EmitI(0xf, rs, rt, imm16)).GprOuts(rt).GprIns(rs); } void MipsAssembler::AddUpper(Register rt, Register rs, uint16_t imm16, Register tmp) { @@ -871,27 +790,27 @@ void MipsAssembler::Sync(uint32_t stype) { void MipsAssembler::Mfhi(Register rd) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitR(0, ZERO, ZERO, rd, 0, 0x10), rd, ZERO, ZERO); + DsFsmInstr(EmitR(0, ZERO, ZERO, rd, 0, 0x10)).GprOuts(rd); } void MipsAssembler::Mflo(Register rd) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitR(0, ZERO, ZERO, rd, 0, 0x12), rd, ZERO, ZERO); + DsFsmInstr(EmitR(0, ZERO, ZERO, rd, 0, 0x12)).GprOuts(rd); } void MipsAssembler::Sb(Register rt, Register rs, uint16_t imm16) { - DsFsmInstrRrr(EmitI(0x28, rs, rt, imm16), ZERO, rt, rs); + DsFsmInstr(EmitI(0x28, rs, rt, imm16)).GprIns(rt, rs); } void MipsAssembler::Sh(Register rt, Register rs, uint16_t imm16) { - DsFsmInstrRrr(EmitI(0x29, rs, rt, imm16), ZERO, rt, rs); + DsFsmInstr(EmitI(0x29, rs, rt, imm16)).GprIns(rt, rs); } void MipsAssembler::Sw(Register rt, Register rs, uint16_t imm16, MipsLabel* patcher_label) { if (patcher_label != nullptr) { Bind(patcher_label); } - DsFsmInstrRrr(EmitI(0x2b, rs, rt, imm16), ZERO, rt, rs, patcher_label); + DsFsmInstr(EmitI(0x2b, rs, rt, imm16), patcher_label).GprIns(rt, rs); } void MipsAssembler::Sw(Register rt, Register rs, uint16_t imm16) { @@ -900,50 +819,50 @@ void MipsAssembler::Sw(Register rt, Register rs, uint16_t imm16) { void MipsAssembler::Swl(Register rt, Register rs, uint16_t imm16) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitI(0x2a, rs, rt, imm16), ZERO, rt, rs); + DsFsmInstr(EmitI(0x2a, rs, rt, imm16)).GprIns(rt, rs); } void MipsAssembler::Swr(Register rt, Register rs, uint16_t imm16) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitI(0x2e, rs, rt, imm16), ZERO, rt, rs); + DsFsmInstr(EmitI(0x2e, rs, rt, imm16)).GprIns(rt, rs); } void MipsAssembler::LlR2(Register rt, Register base, int16_t imm16) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitI(0x30, base, rt, imm16), rt, base, base); + DsFsmInstr(EmitI(0x30, base, rt, imm16)).GprOuts(rt).GprIns(base); } void MipsAssembler::ScR2(Register rt, Register base, int16_t imm16) { CHECK(!IsR6()); - DsFsmInstrRrr(EmitI(0x38, base, rt, imm16), rt, rt, base); + DsFsmInstr(EmitI(0x38, base, rt, imm16)).GprInOuts(rt).GprIns(base); } void MipsAssembler::LlR6(Register rt, Register base, int16_t imm9) { CHECK(IsR6()); CHECK(IsInt<9>(imm9)); - DsFsmInstrRrr(EmitI(0x1f, base, rt, ((imm9 & 0x1ff) << 7) | 0x36), rt, base, base); + DsFsmInstr(EmitI(0x1f, base, rt, ((imm9 & 0x1ff) << 7) | 0x36)).GprOuts(rt).GprIns(base); } void MipsAssembler::ScR6(Register rt, Register base, int16_t imm9) { CHECK(IsR6()); CHECK(IsInt<9>(imm9)); - DsFsmInstrRrr(EmitI(0x1f, base, rt, ((imm9 & 0x1ff) << 7) | 0x26), rt, rt, base); + DsFsmInstr(EmitI(0x1f, base, rt, ((imm9 & 0x1ff) << 7) | 0x26)).GprInOuts(rt).GprIns(base); } void MipsAssembler::Slt(Register rd, Register rs, Register rt) { - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x2a), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x2a)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::Sltu(Register rd, Register rs, Register rt) { - DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x2b), rd, rs, rt); + DsFsmInstr(EmitR(0, rs, rt, rd, 0, 0x2b)).GprOuts(rd).GprIns(rs, rt); } void MipsAssembler::Slti(Register rt, Register rs, uint16_t imm16) { - DsFsmInstrRrr(EmitI(0xa, rs, rt, imm16), rt, rs, rs); + DsFsmInstr(EmitI(0xa, rs, rt, imm16)).GprOuts(rt).GprIns(rs); } void MipsAssembler::Sltiu(Register rt, Register rs, uint16_t imm16) { - DsFsmInstrRrr(EmitI(0xb, rs, rt, imm16), rt, rs, rs); + DsFsmInstr(EmitI(0xb, rs, rt, imm16)).GprOuts(rt).GprIns(rs); } void MipsAssembler::B(uint16_t imm16) { @@ -1021,8 +940,8 @@ void MipsAssembler::Jalr(Register rd, Register rs) { uint32_t last_instruction = delay_slot_.instruction_; MipsLabel* patcher_label = delay_slot_.patcher_label_; bool exchange = (last_instruction != 0 && - (delay_slot_.gpr_outs_mask_ & (1u << rs)) == 0 && - ((delay_slot_.gpr_ins_mask_ | delay_slot_.gpr_outs_mask_) & (1u << rd)) == 0); + (delay_slot_.masks_.gpr_outs_ & (1u << rs)) == 0 && + ((delay_slot_.masks_.gpr_ins_ | delay_slot_.masks_.gpr_outs_) & (1u << rd)) == 0); if (exchange) { // The last instruction cannot be used in a different delay slot, // do not commit the label before it (if any). @@ -1305,67 +1224,67 @@ void MipsAssembler::EmitBcondR6(BranchCondition cond, Register rs, Register rt, } void MipsAssembler::AddS(FRegister fd, FRegister fs, FRegister ft) { - DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x0), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, fd, 0x0)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::SubS(FRegister fd, FRegister fs, FRegister ft) { - DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x1), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, fd, 0x1)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::MulS(FRegister fd, FRegister fs, FRegister ft) { - DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x2), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, fd, 0x2)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::DivS(FRegister fd, FRegister fs, FRegister ft) { - DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x3), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, fd, 0x3)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::AddD(FRegister fd, FRegister fs, FRegister ft) { - DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x0), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, fd, 0x0)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::SubD(FRegister fd, FRegister fs, FRegister ft) { - DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x1), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, fd, 0x1)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::MulD(FRegister fd, FRegister fs, FRegister ft) { - DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x2), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, fd, 0x2)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::DivD(FRegister fd, FRegister fs, FRegister ft) { - DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x3), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, fd, 0x3)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::SqrtS(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x4), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x4)).FprOuts(fd).FprIns(fs); } void MipsAssembler::SqrtD(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x4), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x4)).FprOuts(fd).FprIns(fs); } void MipsAssembler::AbsS(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x5), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x5)).FprOuts(fd).FprIns(fs); } void MipsAssembler::AbsD(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x5), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x5)).FprOuts(fd).FprIns(fs); } void MipsAssembler::MovS(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x6), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x6)).FprOuts(fd).FprIns(fs); } void MipsAssembler::MovD(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x6), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x6)).FprOuts(fd).FprIns(fs); } void MipsAssembler::NegS(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x7), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x7)).FprOuts(fd).FprIns(fs); } void MipsAssembler::NegD(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x7), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x7)).FprOuts(fd).FprIns(fs); } void MipsAssembler::CunS(FRegister fs, FRegister ft) { @@ -1375,7 +1294,8 @@ void MipsAssembler::CunS(FRegister fs, FRegister ft) { void MipsAssembler::CunS(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x31), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x31)) + .CcOuts(cc).FprIns(fs, ft); } void MipsAssembler::CeqS(FRegister fs, FRegister ft) { @@ -1385,7 +1305,8 @@ void MipsAssembler::CeqS(FRegister fs, FRegister ft) { void MipsAssembler::CeqS(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x32), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x32)) + .CcOuts(cc).FprIns(fs, ft); } void MipsAssembler::CueqS(FRegister fs, FRegister ft) { @@ -1395,7 +1316,8 @@ void MipsAssembler::CueqS(FRegister fs, FRegister ft) { void MipsAssembler::CueqS(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x33), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x33)) + .CcOuts(cc).FprIns(fs, ft); } void MipsAssembler::ColtS(FRegister fs, FRegister ft) { @@ -1405,7 +1327,8 @@ void MipsAssembler::ColtS(FRegister fs, FRegister ft) { void MipsAssembler::ColtS(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x34), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x34)) + .CcOuts(cc).FprIns(fs, ft); } void MipsAssembler::CultS(FRegister fs, FRegister ft) { @@ -1415,7 +1338,8 @@ void MipsAssembler::CultS(FRegister fs, FRegister ft) { void MipsAssembler::CultS(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x35), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x35)) + .CcOuts(cc).FprIns(fs, ft); } void MipsAssembler::ColeS(FRegister fs, FRegister ft) { @@ -1425,7 +1349,8 @@ void MipsAssembler::ColeS(FRegister fs, FRegister ft) { void MipsAssembler::ColeS(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x36), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x36)) + .CcOuts(cc).FprIns(fs, ft); } void MipsAssembler::CuleS(FRegister fs, FRegister ft) { @@ -1435,7 +1360,8 @@ void MipsAssembler::CuleS(FRegister fs, FRegister ft) { void MipsAssembler::CuleS(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x37), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, static_cast(cc << 2), 0x37)) + .CcOuts(cc).FprIns(fs, ft); } void MipsAssembler::CunD(FRegister fs, FRegister ft) { @@ -1445,7 +1371,8 @@ void MipsAssembler::CunD(FRegister fs, FRegister ft) { void MipsAssembler::CunD(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x31), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x31)) + .CcOuts(cc).FprIns(fs, ft); } void MipsAssembler::CeqD(FRegister fs, FRegister ft) { @@ -1455,7 +1382,8 @@ void MipsAssembler::CeqD(FRegister fs, FRegister ft) { void MipsAssembler::CeqD(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x32), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x32)) + .CcOuts(cc).FprIns(fs, ft); } void MipsAssembler::CueqD(FRegister fs, FRegister ft) { @@ -1465,7 +1393,8 @@ void MipsAssembler::CueqD(FRegister fs, FRegister ft) { void MipsAssembler::CueqD(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x33), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x33)) + .CcOuts(cc).FprIns(fs, ft); } void MipsAssembler::ColtD(FRegister fs, FRegister ft) { @@ -1475,7 +1404,8 @@ void MipsAssembler::ColtD(FRegister fs, FRegister ft) { void MipsAssembler::ColtD(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x34), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x34)) + .CcOuts(cc).FprIns(fs, ft); } void MipsAssembler::CultD(FRegister fs, FRegister ft) { @@ -1485,7 +1415,8 @@ void MipsAssembler::CultD(FRegister fs, FRegister ft) { void MipsAssembler::CultD(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x35), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x35)) + .CcOuts(cc).FprIns(fs, ft); } void MipsAssembler::ColeD(FRegister fs, FRegister ft) { @@ -1495,7 +1426,8 @@ void MipsAssembler::ColeD(FRegister fs, FRegister ft) { void MipsAssembler::ColeD(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x36), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x36)) + .CcOuts(cc).FprIns(fs, ft); } void MipsAssembler::CuleD(FRegister fs, FRegister ft) { @@ -1505,301 +1437,323 @@ void MipsAssembler::CuleD(FRegister fs, FRegister ft) { void MipsAssembler::CuleD(int cc, FRegister fs, FRegister ft) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x37), cc, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, static_cast(cc << 2), 0x37)) + .CcOuts(cc).FprIns(fs, ft); } void MipsAssembler::CmpUnS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x01), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x14, ft, fs, fd, 0x01)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpEqS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x02), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x14, ft, fs, fd, 0x02)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpUeqS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x03), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x14, ft, fs, fd, 0x03)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpLtS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x04), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x14, ft, fs, fd, 0x04)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpUltS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x05), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x14, ft, fs, fd, 0x05)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpLeS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x06), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x14, ft, fs, fd, 0x06)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpUleS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x07), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x14, ft, fs, fd, 0x07)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpOrS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x11), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x14, ft, fs, fd, 0x11)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpUneS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x12), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x14, ft, fs, fd, 0x12)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpNeS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x13), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x14, ft, fs, fd, 0x13)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpUnD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x01), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x15, ft, fs, fd, 0x01)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpEqD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x02), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x15, ft, fs, fd, 0x02)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpUeqD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x03), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x15, ft, fs, fd, 0x03)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpLtD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x04), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x15, ft, fs, fd, 0x04)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpUltD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x05), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x15, ft, fs, fd, 0x05)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpLeD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x06), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x15, ft, fs, fd, 0x06)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpUleD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x07), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x15, ft, fs, fd, 0x07)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpOrD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x11), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x15, ft, fs, fd, 0x11)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpUneD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x12), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x15, ft, fs, fd, 0x12)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::CmpNeD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x13), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x15, ft, fs, fd, 0x13)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::Movf(Register rd, Register rs, int cc) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrRrrc(EmitR(0, rs, static_cast(cc << 2), rd, 0, 0x01), rd, rs, cc); + DsFsmInstr(EmitR(0, rs, static_cast(cc << 2), rd, 0, 0x01)) + .GprInOuts(rd).GprIns(rs).CcIns(cc); } void MipsAssembler::Movt(Register rd, Register rs, int cc) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrRrrc(EmitR(0, rs, static_cast((cc << 2) | 1), rd, 0, 0x01), rd, rs, cc); + DsFsmInstr(EmitR(0, rs, static_cast((cc << 2) | 1), rd, 0, 0x01)) + .GprInOuts(rd).GprIns(rs).CcIns(cc); } void MipsAssembler::MovfS(FRegister fd, FRegister fs, int cc) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrFffc(EmitFR(0x11, 0x10, static_cast(cc << 2), fs, fd, 0x11), fd, fs, cc); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast(cc << 2), fs, fd, 0x11)) + .FprInOuts(fd).FprIns(fs).CcIns(cc); } void MipsAssembler::MovfD(FRegister fd, FRegister fs, int cc) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrFffc(EmitFR(0x11, 0x11, static_cast(cc << 2), fs, fd, 0x11), fd, fs, cc); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast(cc << 2), fs, fd, 0x11)) + .FprInOuts(fd).FprIns(fs).CcIns(cc); } void MipsAssembler::MovtS(FRegister fd, FRegister fs, int cc) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrFffc(EmitFR(0x11, 0x10, static_cast((cc << 2) | 1), fs, fd, 0x11), - fd, - fs, - cc); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast((cc << 2) | 1), fs, fd, 0x11)) + .FprInOuts(fd).FprIns(fs).CcIns(cc); } void MipsAssembler::MovtD(FRegister fd, FRegister fs, int cc) { CHECK(!IsR6()); CHECK(IsUint<3>(cc)) << cc; - DsFsmInstrFffc(EmitFR(0x11, 0x11, static_cast((cc << 2) | 1), fs, fd, 0x11), - fd, - fs, - cc); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast((cc << 2) | 1), fs, fd, 0x11)) + .FprInOuts(fd).FprIns(fs).CcIns(cc); } void MipsAssembler::MovzS(FRegister fd, FRegister fs, Register rt) { CHECK(!IsR6()); - DsFsmInstrFffr(EmitFR(0x11, 0x10, static_cast(rt), fs, fd, 0x12), fd, fs, rt); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast(rt), fs, fd, 0x12)) + .FprInOuts(fd).FprIns(fs).GprIns(rt); } void MipsAssembler::MovzD(FRegister fd, FRegister fs, Register rt) { CHECK(!IsR6()); - DsFsmInstrFffr(EmitFR(0x11, 0x11, static_cast(rt), fs, fd, 0x12), fd, fs, rt); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast(rt), fs, fd, 0x12)) + .FprInOuts(fd).FprIns(fs).GprIns(rt); } void MipsAssembler::MovnS(FRegister fd, FRegister fs, Register rt) { CHECK(!IsR6()); - DsFsmInstrFffr(EmitFR(0x11, 0x10, static_cast(rt), fs, fd, 0x13), fd, fs, rt); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast(rt), fs, fd, 0x13)) + .FprInOuts(fd).FprIns(fs).GprIns(rt); } void MipsAssembler::MovnD(FRegister fd, FRegister fs, Register rt) { CHECK(!IsR6()); - DsFsmInstrFffr(EmitFR(0x11, 0x11, static_cast(rt), fs, fd, 0x13), fd, fs, rt); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast(rt), fs, fd, 0x13)) + .FprInOuts(fd).FprIns(fs).GprIns(rt); } void MipsAssembler::SelS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFfff(EmitFR(0x11, 0x10, ft, fs, fd, 0x10), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, fd, 0x10)).FprInOuts(fd).FprIns(fs, ft); } void MipsAssembler::SelD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFfff(EmitFR(0x11, 0x11, ft, fs, fd, 0x10), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, fd, 0x10)).FprInOuts(fd).FprIns(fs, ft); } void MipsAssembler::SeleqzS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x14), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, fd, 0x14)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::SeleqzD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x14), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, fd, 0x14)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::SelnezS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x17), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, fd, 0x17)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::SelnezD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x17), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, fd, 0x17)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::ClassS(FRegister fd, FRegister fs) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x1b), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x1b)).FprOuts(fd).FprIns(fs); } void MipsAssembler::ClassD(FRegister fd, FRegister fs) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x1b), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x1b)).FprOuts(fd).FprIns(fs); } void MipsAssembler::MinS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x1c), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, fd, 0x1c)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::MinD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x1c), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, fd, 0x1c)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::MaxS(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x1e), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x10, ft, fs, fd, 0x1e)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::MaxD(FRegister fd, FRegister fs, FRegister ft) { CHECK(IsR6()); - DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x1e), fd, fs, ft); + DsFsmInstr(EmitFR(0x11, 0x11, ft, fs, fd, 0x1e)).FprOuts(fd).FprIns(fs, ft); } void MipsAssembler::TruncLS(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x09), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x09)).FprOuts(fd).FprIns(fs); } void MipsAssembler::TruncLD(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x09), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x09)).FprOuts(fd).FprIns(fs); } void MipsAssembler::TruncWS(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x0D), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x0D)).FprOuts(fd).FprIns(fs); } void MipsAssembler::TruncWD(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x0D), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x0D)).FprOuts(fd).FprIns(fs); } void MipsAssembler::Cvtsw(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x14, static_cast(0), fs, fd, 0x20), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x14, static_cast(0), fs, fd, 0x20)).FprOuts(fd).FprIns(fs); } void MipsAssembler::Cvtdw(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x14, static_cast(0), fs, fd, 0x21), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x14, static_cast(0), fs, fd, 0x21)).FprOuts(fd).FprIns(fs); } void MipsAssembler::Cvtsd(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x20), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0x20)).FprOuts(fd).FprIns(fs); } void MipsAssembler::Cvtds(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x21), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0x21)).FprOuts(fd).FprIns(fs); } void MipsAssembler::Cvtsl(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x15, static_cast(0), fs, fd, 0x20), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x15, static_cast(0), fs, fd, 0x20)).FprOuts(fd).FprIns(fs); } void MipsAssembler::Cvtdl(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x15, static_cast(0), fs, fd, 0x21), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x15, static_cast(0), fs, fd, 0x21)).FprOuts(fd).FprIns(fs); } void MipsAssembler::FloorWS(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0xf), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x10, static_cast(0), fs, fd, 0xf)).FprOuts(fd).FprIns(fs); } void MipsAssembler::FloorWD(FRegister fd, FRegister fs) { - DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0xf), fd, fs, fs); + DsFsmInstr(EmitFR(0x11, 0x11, static_cast(0), fs, fd, 0xf)).FprOuts(fd).FprIns(fs); +} + +FRegister MipsAssembler::GetFpuRegLow(FRegister reg) { + // If FPRs are 32-bit (and get paired to hold 64-bit values), accesses to + // odd-numbered FPRs are reattributed to even-numbered FPRs. This lets us + // use only even-numbered FPRs irrespective of whether we're doing single- + // or double-precision arithmetic. (We don't use odd-numbered 32-bit FPRs + // to hold single-precision values). + return Is32BitFPU() ? static_cast(reg & ~1u) : reg; } void MipsAssembler::Mfc1(Register rt, FRegister fs) { - DsFsmInstrRf(EmitFR(0x11, 0x00, static_cast(rt), fs, static_cast(0), 0x0), - rt, - fs); + DsFsmInstr(EmitFR(0x11, 0x00, static_cast(rt), fs, static_cast(0), 0x0)) + .GprOuts(rt).FprIns(GetFpuRegLow(fs)); } +// Note, the 32 LSBs of a 64-bit value must be loaded into an FPR before the 32 MSBs +// when loading the value as 32-bit halves. void MipsAssembler::Mtc1(Register rt, FRegister fs) { - DsFsmInstrFr(EmitFR(0x11, 0x04, static_cast(rt), fs, static_cast(0), 0x0), - fs, - rt); + uint32_t encoding = + EmitFR(0x11, 0x04, static_cast(rt), fs, static_cast(0), 0x0); + if (Is32BitFPU() && (fs % 2 != 0)) { + // If mtc1 is used to simulate mthc1 by writing to the odd-numbered FPR in + // a pair of 32-bit FPRs, the associated even-numbered FPR is an in/out. + DsFsmInstr(encoding).FprInOuts(GetFpuRegLow(fs)).GprIns(rt); + } else { + // Otherwise (the FPR is 64-bit or even-numbered), the FPR is an out. + DsFsmInstr(encoding).FprOuts(fs).GprIns(rt); + } } void MipsAssembler::Mfhc1(Register rt, FRegister fs) { - DsFsmInstrRf(EmitFR(0x11, 0x03, static_cast(rt), fs, static_cast(0), 0x0), - rt, - fs); + DsFsmInstr(EmitFR(0x11, 0x03, static_cast(rt), fs, static_cast(0), 0x0)) + .GprOuts(rt).FprIns(fs); } +// Note, the 32 LSBs of a 64-bit value must be loaded into an FPR before the 32 MSBs +// when loading the value as 32-bit halves. void MipsAssembler::Mthc1(Register rt, FRegister fs) { - DsFsmInstrFr(EmitFR(0x11, 0x07, static_cast(rt), fs, static_cast(0), 0x0), - fs, - rt); + DsFsmInstr(EmitFR(0x11, 0x07, static_cast(rt), fs, static_cast(0), 0x0)) + .FprInOuts(fs).GprIns(rt); } void MipsAssembler::MoveFromFpuHigh(Register rt, FRegister fs) { @@ -1820,20 +1774,30 @@ void MipsAssembler::MoveToFpuHigh(Register rt, FRegister fs) { } } +// Note, the 32 LSBs of a 64-bit value must be loaded into an FPR before the 32 MSBs +// when loading the value as 32-bit halves. void MipsAssembler::Lwc1(FRegister ft, Register rs, uint16_t imm16) { - DsFsmInstrFr(EmitI(0x31, rs, static_cast(ft), imm16), ft, rs); + uint32_t encoding = EmitI(0x31, rs, static_cast(ft), imm16); + if (Is32BitFPU() && (ft % 2 != 0)) { + // If lwc1 is used to load the odd-numbered FPR in a pair of 32-bit FPRs, + // the associated even-numbered FPR is an in/out. + DsFsmInstr(encoding).FprInOuts(GetFpuRegLow(ft)).GprIns(rs); + } else { + // Otherwise (the FPR is 64-bit or even-numbered), the FPR is an out. + DsFsmInstr(encoding).FprOuts(ft).GprIns(rs); + } } void MipsAssembler::Ldc1(FRegister ft, Register rs, uint16_t imm16) { - DsFsmInstrFr(EmitI(0x35, rs, static_cast(ft), imm16), ft, rs); + DsFsmInstr(EmitI(0x35, rs, static_cast(ft), imm16)).FprOuts(ft).GprIns(rs); } void MipsAssembler::Swc1(FRegister ft, Register rs, uint16_t imm16) { - DsFsmInstrFR(EmitI(0x39, rs, static_cast(ft), imm16), ft, rs); + DsFsmInstr(EmitI(0x39, rs, static_cast(ft), imm16)).FprIns(GetFpuRegLow(ft)).GprIns(rs); } void MipsAssembler::Sdc1(FRegister ft, Register rs, uint16_t imm16) { - DsFsmInstrFR(EmitI(0x3d, rs, static_cast(ft), imm16), ft, rs); + DsFsmInstr(EmitI(0x3d, rs, static_cast(ft), imm16)).FprIns(ft).GprIns(rs); } void MipsAssembler::Break() { @@ -1882,1447 +1846,951 @@ void MipsAssembler::PopAndReturn(Register rd, Register rt) { void MipsAssembler::AndV(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x0, wt, ws, wd, 0x1e), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x0, wt, ws, wd, 0x1e)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::OrV(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x1, wt, ws, wd, 0x1e), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x1, wt, ws, wd, 0x1e)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::NorV(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x2, wt, ws, wd, 0x1e), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x2, wt, ws, wd, 0x1e)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::XorV(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x3, wt, ws, wd, 0x1e), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x3, wt, ws, wd, 0x1e)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::AddvB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x0, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x0, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::AddvH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x1, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x1, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::AddvW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x2, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x2, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::AddvD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x3, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x3, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SubvB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x0, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x0, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SubvH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x1, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x1, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SubvW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x2, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x2, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SubvD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x3, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x3, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::MulvB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x0, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x0, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::MulvH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x1, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x1, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::MulvW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x2, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x2, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::MulvD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x3, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x3, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Div_sB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x0, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x0, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Div_sH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x1, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x1, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Div_sW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x2, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x2, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Div_sD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x3, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x3, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Div_uB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x0, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x0, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Div_uH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x1, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x1, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Div_uW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x2, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x2, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Div_uD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Mod_sB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x6, 0x0, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x6, 0x0, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Mod_sH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x6, 0x1, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x6, 0x1, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Mod_sW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x6, 0x2, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x6, 0x2, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Mod_sD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x6, 0x3, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x6, 0x3, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Mod_uB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x7, 0x0, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x7, 0x0, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Mod_uH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x7, 0x1, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x7, 0x1, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Mod_uW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x7, 0x2, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x7, 0x2, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Mod_uD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x7, 0x3, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x7, 0x3, wt, ws, wd, 0x12)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Add_aB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x0, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x0, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Add_aH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x1, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x1, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Add_aW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x2, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x2, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Add_aD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x3, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x3, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Ave_sB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x0, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x0, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Ave_sH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x1, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x1, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Ave_sW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x2, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x2, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Ave_sD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x3, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x3, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Ave_uB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x0, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x0, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Ave_uH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x1, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x1, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Ave_uW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x2, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x2, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Ave_uD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Aver_sB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x6, 0x0, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x6, 0x0, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Aver_sH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x6, 0x1, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x6, 0x1, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Aver_sW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x6, 0x2, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x6, 0x2, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Aver_sD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x6, 0x3, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x6, 0x3, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Aver_uB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x7, 0x0, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x7, 0x0, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Aver_uH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x7, 0x1, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x7, 0x1, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Aver_uW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x7, 0x2, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x7, 0x2, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Aver_uD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x7, 0x3, wt, ws, wd, 0x10), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x7, 0x3, wt, ws, wd, 0x10)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Max_sB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x0, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x0, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Max_sH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x1, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x1, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Max_sW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x2, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x2, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Max_sD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x3, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x3, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Max_uB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x3, 0x0, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x3, 0x0, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Max_uH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x3, 0x1, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x3, 0x1, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Max_uW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x3, 0x2, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x3, 0x2, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Max_uD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x3, 0x3, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x3, 0x3, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Min_sB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x0, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x0, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Min_sH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x1, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x1, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Min_sW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x2, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x2, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Min_sD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x3, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x3, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Min_uB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x0, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x0, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Min_uH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x1, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x1, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Min_uW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x2, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x2, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Min_uD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x3, wt, ws, wd, 0xe), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x3, wt, ws, wd, 0xe)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::FaddW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x0, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x0, wt, ws, wd, 0x1b)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::FaddD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x1, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x1, wt, ws, wd, 0x1b)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::FsubW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x2, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x2, wt, ws, wd, 0x1b)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::FsubD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x3, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x3, wt, ws, wd, 0x1b)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::FmulW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x0, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x0, wt, ws, wd, 0x1b)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::FmulD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x1, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x1, wt, ws, wd, 0x1b)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::FdivW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x2, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x2, wt, ws, wd, 0x1b)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::FdivD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x3, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x3, wt, ws, wd, 0x1b)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::FmaxW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x7, 0x0, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x7, 0x0, wt, ws, wd, 0x1b)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::FmaxD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x7, 0x1, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x7, 0x1, wt, ws, wd, 0x1b)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::FminW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x6, 0x0, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x6, 0x0, wt, ws, wd, 0x1b)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::FminD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x6, 0x1, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x6, 0x1, wt, ws, wd, 0x1b)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Ffint_sW(VectorRegister wd, VectorRegister ws) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa2RF(0x19e, 0x0, ws, wd, 0x1e), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsa2RF(0x19e, 0x0, ws, wd, 0x1e)).FprOuts(wd).FprIns(ws); } void MipsAssembler::Ffint_sD(VectorRegister wd, VectorRegister ws) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa2RF(0x19e, 0x1, ws, wd, 0x1e), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsa2RF(0x19e, 0x1, ws, wd, 0x1e)).FprOuts(wd).FprIns(ws); } void MipsAssembler::Ftint_sW(VectorRegister wd, VectorRegister ws) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa2RF(0x19c, 0x0, ws, wd, 0x1e), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsa2RF(0x19c, 0x0, ws, wd, 0x1e)).FprOuts(wd).FprIns(ws); } void MipsAssembler::Ftint_sD(VectorRegister wd, VectorRegister ws) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa2RF(0x19c, 0x1, ws, wd, 0x1e), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsa2RF(0x19c, 0x1, ws, wd, 0x1e)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SllB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x0, wt, ws, wd, 0xd), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x0, wt, ws, wd, 0xd)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SllH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x1, wt, ws, wd, 0xd), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x1, wt, ws, wd, 0xd)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SllW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x2, wt, ws, wd, 0xd), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x2, wt, ws, wd, 0xd)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SllD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x0, 0x3, wt, ws, wd, 0xd), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x0, 0x3, wt, ws, wd, 0xd)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SraB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x0, wt, ws, wd, 0xd), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x0, wt, ws, wd, 0xd)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SraH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x1, wt, ws, wd, 0xd), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x1, wt, ws, wd, 0xd)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SraW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x2, wt, ws, wd, 0xd), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x2, wt, ws, wd, 0xd)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SraD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x3, wt, ws, wd, 0xd), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x3, wt, ws, wd, 0xd)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SrlB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x0, wt, ws, wd, 0xd), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x0, wt, ws, wd, 0xd)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SrlH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x1, wt, ws, wd, 0xd), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x1, wt, ws, wd, 0xd)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SrlW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x2, wt, ws, wd, 0xd), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x2, wt, ws, wd, 0xd)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SrlD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x3, wt, ws, wd, 0xd), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x3, wt, ws, wd, 0xd)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::SlliB(VectorRegister wd, VectorRegister ws, int shamt3) { CHECK(HasMsa()); CHECK(IsUint<3>(shamt3)) << shamt3; - DsFsmInstrFff(EmitMsaBIT(0x0, shamt3 | kMsaDfMByteMask, ws, wd, 0x9), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaBIT(0x0, shamt3 | kMsaDfMByteMask, ws, wd, 0x9)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SlliH(VectorRegister wd, VectorRegister ws, int shamt4) { CHECK(HasMsa()); CHECK(IsUint<4>(shamt4)) << shamt4; - DsFsmInstrFff(EmitMsaBIT(0x0, shamt4 | kMsaDfMHalfwordMask, ws, wd, 0x9), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaBIT(0x0, shamt4 | kMsaDfMHalfwordMask, ws, wd, 0x9)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SlliW(VectorRegister wd, VectorRegister ws, int shamt5) { CHECK(HasMsa()); CHECK(IsUint<5>(shamt5)) << shamt5; - DsFsmInstrFff(EmitMsaBIT(0x0, shamt5 | kMsaDfMWordMask, ws, wd, 0x9), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaBIT(0x0, shamt5 | kMsaDfMWordMask, ws, wd, 0x9)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SlliD(VectorRegister wd, VectorRegister ws, int shamt6) { CHECK(HasMsa()); CHECK(IsUint<6>(shamt6)) << shamt6; - DsFsmInstrFff(EmitMsaBIT(0x0, shamt6 | kMsaDfMDoublewordMask, ws, wd, 0x9), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaBIT(0x0, shamt6 | kMsaDfMDoublewordMask, ws, wd, 0x9)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SraiB(VectorRegister wd, VectorRegister ws, int shamt3) { CHECK(HasMsa()); CHECK(IsUint<3>(shamt3)) << shamt3; - DsFsmInstrFff(EmitMsaBIT(0x1, shamt3 | kMsaDfMByteMask, ws, wd, 0x9), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaBIT(0x1, shamt3 | kMsaDfMByteMask, ws, wd, 0x9)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SraiH(VectorRegister wd, VectorRegister ws, int shamt4) { CHECK(HasMsa()); CHECK(IsUint<4>(shamt4)) << shamt4; - DsFsmInstrFff(EmitMsaBIT(0x1, shamt4 | kMsaDfMHalfwordMask, ws, wd, 0x9), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaBIT(0x1, shamt4 | kMsaDfMHalfwordMask, ws, wd, 0x9)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SraiW(VectorRegister wd, VectorRegister ws, int shamt5) { CHECK(HasMsa()); CHECK(IsUint<5>(shamt5)) << shamt5; - DsFsmInstrFff(EmitMsaBIT(0x1, shamt5 | kMsaDfMWordMask, ws, wd, 0x9), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaBIT(0x1, shamt5 | kMsaDfMWordMask, ws, wd, 0x9)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SraiD(VectorRegister wd, VectorRegister ws, int shamt6) { CHECK(HasMsa()); CHECK(IsUint<6>(shamt6)) << shamt6; - DsFsmInstrFff(EmitMsaBIT(0x1, shamt6 | kMsaDfMDoublewordMask, ws, wd, 0x9), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaBIT(0x1, shamt6 | kMsaDfMDoublewordMask, ws, wd, 0x9)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SrliB(VectorRegister wd, VectorRegister ws, int shamt3) { CHECK(HasMsa()); CHECK(IsUint<3>(shamt3)) << shamt3; - DsFsmInstrFff(EmitMsaBIT(0x2, shamt3 | kMsaDfMByteMask, ws, wd, 0x9), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaBIT(0x2, shamt3 | kMsaDfMByteMask, ws, wd, 0x9)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SrliH(VectorRegister wd, VectorRegister ws, int shamt4) { CHECK(HasMsa()); CHECK(IsUint<4>(shamt4)) << shamt4; - DsFsmInstrFff(EmitMsaBIT(0x2, shamt4 | kMsaDfMHalfwordMask, ws, wd, 0x9), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaBIT(0x2, shamt4 | kMsaDfMHalfwordMask, ws, wd, 0x9)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SrliW(VectorRegister wd, VectorRegister ws, int shamt5) { CHECK(HasMsa()); CHECK(IsUint<5>(shamt5)) << shamt5; - DsFsmInstrFff(EmitMsaBIT(0x2, shamt5 | kMsaDfMWordMask, ws, wd, 0x9), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaBIT(0x2, shamt5 | kMsaDfMWordMask, ws, wd, 0x9)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SrliD(VectorRegister wd, VectorRegister ws, int shamt6) { CHECK(HasMsa()); CHECK(IsUint<6>(shamt6)) << shamt6; - DsFsmInstrFff(EmitMsaBIT(0x2, shamt6 | kMsaDfMDoublewordMask, ws, wd, 0x9), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaBIT(0x2, shamt6 | kMsaDfMDoublewordMask, ws, wd, 0x9)).FprOuts(wd).FprIns(ws); } void MipsAssembler::MoveV(VectorRegister wd, VectorRegister ws) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsaBIT(0x1, 0x3e, ws, wd, 0x19), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaBIT(0x1, 0x3e, ws, wd, 0x19)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SplatiB(VectorRegister wd, VectorRegister ws, int n4) { CHECK(HasMsa()); CHECK(IsUint<4>(n4)) << n4; - DsFsmInstrFff(EmitMsaELM(0x1, n4 | kMsaDfNByteMask, ws, wd, 0x19), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaELM(0x1, n4 | kMsaDfNByteMask, ws, wd, 0x19)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SplatiH(VectorRegister wd, VectorRegister ws, int n3) { CHECK(HasMsa()); CHECK(IsUint<3>(n3)) << n3; - DsFsmInstrFff(EmitMsaELM(0x1, n3 | kMsaDfNHalfwordMask, ws, wd, 0x19), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaELM(0x1, n3 | kMsaDfNHalfwordMask, ws, wd, 0x19)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SplatiW(VectorRegister wd, VectorRegister ws, int n2) { CHECK(HasMsa()); CHECK(IsUint<2>(n2)) << n2; - DsFsmInstrFff(EmitMsaELM(0x1, n2 | kMsaDfNWordMask, ws, wd, 0x19), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaELM(0x1, n2 | kMsaDfNWordMask, ws, wd, 0x19)).FprOuts(wd).FprIns(ws); } void MipsAssembler::SplatiD(VectorRegister wd, VectorRegister ws, int n1) { CHECK(HasMsa()); CHECK(IsUint<1>(n1)) << n1; - DsFsmInstrFff(EmitMsaELM(0x1, n1 | kMsaDfNDoublewordMask, ws, wd, 0x19), - static_cast(wd), - static_cast(ws), - static_cast(ws)); + DsFsmInstr(EmitMsaELM(0x1, n1 | kMsaDfNDoublewordMask, ws, wd, 0x19)).FprOuts(wd).FprIns(ws); } void MipsAssembler::Copy_sB(Register rd, VectorRegister ws, int n4) { CHECK(HasMsa()); CHECK(IsUint<4>(n4)) << n4; - DsFsmInstrRf(EmitMsaELM(0x2, n4 | kMsaDfNByteMask, ws, static_cast(rd), 0x19), - rd, - static_cast(ws)); + DsFsmInstr(EmitMsaELM(0x2, n4 | kMsaDfNByteMask, ws, static_cast(rd), 0x19)) + .GprOuts(rd).FprIns(ws); } void MipsAssembler::Copy_sH(Register rd, VectorRegister ws, int n3) { CHECK(HasMsa()); CHECK(IsUint<3>(n3)) << n3; - DsFsmInstrRf(EmitMsaELM(0x2, n3 | kMsaDfNHalfwordMask, ws, static_cast(rd), 0x19), - rd, - static_cast(ws)); + DsFsmInstr(EmitMsaELM(0x2, n3 | kMsaDfNHalfwordMask, ws, static_cast(rd), 0x19)) + .GprOuts(rd).FprIns(ws); } void MipsAssembler::Copy_sW(Register rd, VectorRegister ws, int n2) { CHECK(HasMsa()); CHECK(IsUint<2>(n2)) << n2; - DsFsmInstrRf(EmitMsaELM(0x2, n2 | kMsaDfNWordMask, ws, static_cast(rd), 0x19), - rd, - static_cast(ws)); + DsFsmInstr(EmitMsaELM(0x2, n2 | kMsaDfNWordMask, ws, static_cast(rd), 0x19)) + .GprOuts(rd).FprIns(ws); } void MipsAssembler::Copy_uB(Register rd, VectorRegister ws, int n4) { CHECK(HasMsa()); CHECK(IsUint<4>(n4)) << n4; - DsFsmInstrRf(EmitMsaELM(0x3, n4 | kMsaDfNByteMask, ws, static_cast(rd), 0x19), - rd, - static_cast(ws)); + DsFsmInstr(EmitMsaELM(0x3, n4 | kMsaDfNByteMask, ws, static_cast(rd), 0x19)) + .GprOuts(rd).FprIns(ws); } void MipsAssembler::Copy_uH(Register rd, VectorRegister ws, int n3) { CHECK(HasMsa()); CHECK(IsUint<3>(n3)) << n3; - DsFsmInstrRf(EmitMsaELM(0x3, n3 | kMsaDfNHalfwordMask, ws, static_cast(rd), 0x19), - rd, - static_cast(ws)); + DsFsmInstr(EmitMsaELM(0x3, n3 | kMsaDfNHalfwordMask, ws, static_cast(rd), 0x19)) + .GprOuts(rd).FprIns(ws); } void MipsAssembler::InsertB(VectorRegister wd, Register rs, int n4) { CHECK(HasMsa()); CHECK(IsUint<4>(n4)) << n4; - DsFsmInstrFffr(EmitMsaELM(0x4, n4 | kMsaDfNByteMask, static_cast(rs), wd, 0x19), - static_cast(wd), - static_cast(wd), - rs); + DsFsmInstr(EmitMsaELM(0x4, n4 | kMsaDfNByteMask, static_cast(rs), wd, 0x19)) + .FprInOuts(wd).GprIns(rs); } void MipsAssembler::InsertH(VectorRegister wd, Register rs, int n3) { CHECK(HasMsa()); CHECK(IsUint<3>(n3)) << n3; - DsFsmInstrFffr( - EmitMsaELM(0x4, n3 | kMsaDfNHalfwordMask, static_cast(rs), wd, 0x19), - static_cast(wd), - static_cast(wd), - rs); + DsFsmInstr(EmitMsaELM(0x4, n3 | kMsaDfNHalfwordMask, static_cast(rs), wd, 0x19)) + .FprInOuts(wd).GprIns(rs); } void MipsAssembler::InsertW(VectorRegister wd, Register rs, int n2) { CHECK(HasMsa()); CHECK(IsUint<2>(n2)) << n2; - DsFsmInstrFffr(EmitMsaELM(0x4, n2 | kMsaDfNWordMask, static_cast(rs), wd, 0x19), - static_cast(wd), - static_cast(wd), - rs); + DsFsmInstr(EmitMsaELM(0x4, n2 | kMsaDfNWordMask, static_cast(rs), wd, 0x19)) + .FprInOuts(wd).GprIns(rs); } void MipsAssembler::FillB(VectorRegister wd, Register rs) { CHECK(HasMsa()); - DsFsmInstrFr(EmitMsa2R(0xc0, 0x0, static_cast(rs), wd, 0x1e), - static_cast(wd), - rs); + DsFsmInstr(EmitMsa2R(0xc0, 0x0, static_cast(rs), wd, 0x1e)) + .FprOuts(wd).GprIns(rs); } void MipsAssembler::FillH(VectorRegister wd, Register rs) { CHECK(HasMsa()); - DsFsmInstrFr(EmitMsa2R(0xc0, 0x1, static_cast(rs), wd, 0x1e), - static_cast(wd), - rs); + DsFsmInstr(EmitMsa2R(0xc0, 0x1, static_cast(rs), wd, 0x1e)) + .FprOuts(wd).GprIns(rs); } void MipsAssembler::FillW(VectorRegister wd, Register rs) { CHECK(HasMsa()); - DsFsmInstrFr(EmitMsa2R(0xc0, 0x2, static_cast(rs), wd, 0x1e), - static_cast(wd), - rs); + DsFsmInstr(EmitMsa2R(0xc0, 0x2, static_cast(rs), wd, 0x1e)) + .FprOuts(wd).GprIns(rs); } void MipsAssembler::LdiB(VectorRegister wd, int imm8) { CHECK(HasMsa()); CHECK(IsInt<8>(imm8)) << imm8; - DsFsmInstrFr(EmitMsaI10(0x6, 0x0, imm8 & kMsaS10Mask, wd, 0x7), - static_cast(wd), - ZERO); + DsFsmInstr(EmitMsaI10(0x6, 0x0, imm8 & kMsaS10Mask, wd, 0x7)).FprOuts(wd); } void MipsAssembler::LdiH(VectorRegister wd, int imm10) { CHECK(HasMsa()); CHECK(IsInt<10>(imm10)) << imm10; - DsFsmInstrFr(EmitMsaI10(0x6, 0x1, imm10 & kMsaS10Mask, wd, 0x7), - static_cast(wd), - ZERO); + DsFsmInstr(EmitMsaI10(0x6, 0x1, imm10 & kMsaS10Mask, wd, 0x7)).FprOuts(wd); } void MipsAssembler::LdiW(VectorRegister wd, int imm10) { CHECK(HasMsa()); CHECK(IsInt<10>(imm10)) << imm10; - DsFsmInstrFr(EmitMsaI10(0x6, 0x2, imm10 & kMsaS10Mask, wd, 0x7), - static_cast(wd), - ZERO); + DsFsmInstr(EmitMsaI10(0x6, 0x2, imm10 & kMsaS10Mask, wd, 0x7)).FprOuts(wd); } void MipsAssembler::LdiD(VectorRegister wd, int imm10) { CHECK(HasMsa()); CHECK(IsInt<10>(imm10)) << imm10; - DsFsmInstrFr(EmitMsaI10(0x6, 0x3, imm10 & kMsaS10Mask, wd, 0x7), - static_cast(wd), - ZERO); + DsFsmInstr(EmitMsaI10(0x6, 0x3, imm10 & kMsaS10Mask, wd, 0x7)).FprOuts(wd); } void MipsAssembler::LdB(VectorRegister wd, Register rs, int offset) { CHECK(HasMsa()); CHECK(IsInt<10>(offset)) << offset; - DsFsmInstrFr(EmitMsaMI10(offset & kMsaS10Mask, rs, wd, 0x8, 0x0), - static_cast(wd), - rs); + DsFsmInstr(EmitMsaMI10(offset & kMsaS10Mask, rs, wd, 0x8, 0x0)).FprOuts(wd).GprIns(rs); } void MipsAssembler::LdH(VectorRegister wd, Register rs, int offset) { CHECK(HasMsa()); CHECK(IsInt<11>(offset)) << offset; CHECK_ALIGNED(offset, kMipsHalfwordSize); - DsFsmInstrFr(EmitMsaMI10((offset >> TIMES_2) & kMsaS10Mask, rs, wd, 0x8, 0x1), - static_cast(wd), - rs); + DsFsmInstr(EmitMsaMI10((offset >> TIMES_2) & kMsaS10Mask, rs, wd, 0x8, 0x1)) + .FprOuts(wd).GprIns(rs); } void MipsAssembler::LdW(VectorRegister wd, Register rs, int offset) { CHECK(HasMsa()); CHECK(IsInt<12>(offset)) << offset; CHECK_ALIGNED(offset, kMipsWordSize); - DsFsmInstrFr(EmitMsaMI10((offset >> TIMES_4) & kMsaS10Mask, rs, wd, 0x8, 0x2), - static_cast(wd), - rs); + DsFsmInstr(EmitMsaMI10((offset >> TIMES_4) & kMsaS10Mask, rs, wd, 0x8, 0x2)) + .FprOuts(wd).GprIns(rs); } void MipsAssembler::LdD(VectorRegister wd, Register rs, int offset) { CHECK(HasMsa()); CHECK(IsInt<13>(offset)) << offset; CHECK_ALIGNED(offset, kMipsDoublewordSize); - DsFsmInstrFr(EmitMsaMI10((offset >> TIMES_8) & kMsaS10Mask, rs, wd, 0x8, 0x3), - static_cast(wd), - rs); + DsFsmInstr(EmitMsaMI10((offset >> TIMES_8) & kMsaS10Mask, rs, wd, 0x8, 0x3)) + .FprOuts(wd).GprIns(rs); } void MipsAssembler::StB(VectorRegister wd, Register rs, int offset) { CHECK(HasMsa()); CHECK(IsInt<10>(offset)) << offset; - DsFsmInstrFR(EmitMsaMI10(offset & kMsaS10Mask, rs, wd, 0x9, 0x0), static_cast(wd), rs); + DsFsmInstr(EmitMsaMI10(offset & kMsaS10Mask, rs, wd, 0x9, 0x0)).FprIns(wd).GprIns(rs); } void MipsAssembler::StH(VectorRegister wd, Register rs, int offset) { CHECK(HasMsa()); CHECK(IsInt<11>(offset)) << offset; CHECK_ALIGNED(offset, kMipsHalfwordSize); - DsFsmInstrFR(EmitMsaMI10((offset >> TIMES_2) & kMsaS10Mask, rs, wd, 0x9, 0x1), - static_cast(wd), - rs); + DsFsmInstr(EmitMsaMI10((offset >> TIMES_2) & kMsaS10Mask, rs, wd, 0x9, 0x1)) + .FprIns(wd).GprIns(rs); } void MipsAssembler::StW(VectorRegister wd, Register rs, int offset) { CHECK(HasMsa()); CHECK(IsInt<12>(offset)) << offset; CHECK_ALIGNED(offset, kMipsWordSize); - DsFsmInstrFR(EmitMsaMI10((offset >> TIMES_4) & kMsaS10Mask, rs, wd, 0x9, 0x2), - static_cast(wd), - rs); + DsFsmInstr(EmitMsaMI10((offset >> TIMES_4) & kMsaS10Mask, rs, wd, 0x9, 0x2)) + .FprIns(wd).GprIns(rs); } void MipsAssembler::StD(VectorRegister wd, Register rs, int offset) { CHECK(HasMsa()); CHECK(IsInt<13>(offset)) << offset; CHECK_ALIGNED(offset, kMipsDoublewordSize); - DsFsmInstrFR(EmitMsaMI10((offset >> TIMES_8) & kMsaS10Mask, rs, wd, 0x9, 0x3), - static_cast(wd), - rs); + DsFsmInstr(EmitMsaMI10((offset >> TIMES_8) & kMsaS10Mask, rs, wd, 0x9, 0x3)) + .FprIns(wd).GprIns(rs); } void MipsAssembler::IlvlB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x0, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x0, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvlH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x1, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x1, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvlW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x2, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x2, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvlD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x3, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x3, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvrB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x0, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x0, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvrH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x1, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x1, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvrW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x2, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x2, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvrD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvevB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x6, 0x0, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x6, 0x0, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvevH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x6, 0x1, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x6, 0x1, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvevW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x6, 0x2, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x6, 0x2, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvevD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x6, 0x3, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x6, 0x3, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvodB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x7, 0x0, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x7, 0x0, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvodH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x7, 0x1, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x7, 0x1, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvodW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x7, 0x2, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x7, 0x2, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::IlvodD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x7, 0x3, wt, ws, wd, 0x14), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x7, 0x3, wt, ws, wd, 0x14)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::MaddvB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x0, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x0, wt, ws, wd, 0x12)).FprInOuts(wd).FprIns(ws, wt); } void MipsAssembler::MaddvH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x1, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x1, wt, ws, wd, 0x12)).FprInOuts(wd).FprIns(ws, wt); } void MipsAssembler::MaddvW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x2, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x2, wt, ws, wd, 0x12)).FprInOuts(wd).FprIns(ws, wt); } void MipsAssembler::MaddvD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x1, 0x3, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x1, 0x3, wt, ws, wd, 0x12)).FprInOuts(wd).FprIns(ws, wt); } void MipsAssembler::MsubvB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x0, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x0, wt, ws, wd, 0x12)).FprInOuts(wd).FprIns(ws, wt); } void MipsAssembler::MsubvH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x1, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x1, wt, ws, wd, 0x12)).FprInOuts(wd).FprIns(ws, wt); } void MipsAssembler::MsubvW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x2, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x2, wt, ws, wd, 0x12)).FprInOuts(wd).FprIns(ws, wt); } void MipsAssembler::MsubvD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x3, wt, ws, wd, 0x12), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x3, wt, ws, wd, 0x12)).FprInOuts(wd).FprIns(ws, wt); } void MipsAssembler::Asub_sB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x0, wt, ws, wd, 0x11), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x0, wt, ws, wd, 0x11)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Asub_sH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x1, wt, ws, wd, 0x11), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x1, wt, ws, wd, 0x11)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Asub_sW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x2, wt, ws, wd, 0x11), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x2, wt, ws, wd, 0x11)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Asub_sD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x3, wt, ws, wd, 0x11), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x3, wt, ws, wd, 0x11)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Asub_uB(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x0, wt, ws, wd, 0x11), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x0, wt, ws, wd, 0x11)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Asub_uH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x1, wt, ws, wd, 0x11), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x1, wt, ws, wd, 0x11)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Asub_uW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x2, wt, ws, wd, 0x11), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x2, wt, ws, wd, 0x11)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Asub_uD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x11), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x11)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::FmaddW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x0, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x0, wt, ws, wd, 0x1b)).FprInOuts(wd).FprIns(ws, wt); } void MipsAssembler::FmaddD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x1, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x1, wt, ws, wd, 0x1b)).FprInOuts(wd).FprIns(ws, wt); } void MipsAssembler::FmsubW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x2, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x2, wt, ws, wd, 0x1b)).FprInOuts(wd).FprIns(ws, wt); } void MipsAssembler::FmsubD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x2, 0x3, wt, ws, wd, 0x1b), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x2, 0x3, wt, ws, wd, 0x1b)).FprInOuts(wd).FprIns(ws, wt); } void MipsAssembler::Hadd_sH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x1, wt, ws, wd, 0x15), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x1, wt, ws, wd, 0x15)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Hadd_sW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x2, wt, ws, wd, 0x15), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x2, wt, ws, wd, 0x15)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Hadd_sD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x4, 0x3, wt, ws, wd, 0x15), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x4, 0x3, wt, ws, wd, 0x15)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Hadd_uH(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x1, wt, ws, wd, 0x15), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x1, wt, ws, wd, 0x15)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Hadd_uW(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x2, wt, ws, wd, 0x15), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x2, wt, ws, wd, 0x15)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::Hadd_uD(VectorRegister wd, VectorRegister ws, VectorRegister wt) { CHECK(HasMsa()); - DsFsmInstrFff(EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x15), - static_cast(wd), - static_cast(ws), - static_cast(wt)); + DsFsmInstr(EmitMsa3R(0x5, 0x3, wt, ws, wd, 0x15)).FprOuts(wd).FprIns(ws, wt); } void MipsAssembler::ReplicateFPToVectorRegister(VectorRegister dst, @@ -4144,7 +3612,7 @@ bool MipsAssembler::Branch::CanHaveDelayedInstruction(const DelaySlot& delay_slo case kLongCall: // Instructions depending on or modifying RA should not be moved into delay slots // of branches modifying RA. - return ((delay_slot.gpr_ins_mask_ | delay_slot.gpr_outs_mask_) & (1u << RA)) == 0; + return ((delay_slot.masks_.gpr_ins_ | delay_slot.masks_.gpr_outs_) & (1u << RA)) == 0; // R2 conditional branches. case kCondBranch: @@ -4157,17 +3625,17 @@ bool MipsAssembler::Branch::CanHaveDelayedInstruction(const DelaySlot& delay_slo case kCondGTZ: case kCondEQZ: case kCondNEZ: - return (delay_slot.gpr_outs_mask_ & (1u << lhs_reg_)) == 0; + return (delay_slot.masks_.gpr_outs_ & (1u << lhs_reg_)) == 0; // Branches with two GPR sources. case kCondEQ: case kCondNE: - return (delay_slot.gpr_outs_mask_ & ((1u << lhs_reg_) | (1u << rhs_reg_))) == 0; + return (delay_slot.masks_.gpr_outs_ & ((1u << lhs_reg_) | (1u << rhs_reg_))) == 0; // Branches with one FPU condition code source. case kCondF: case kCondT: - return (delay_slot.cc_outs_mask_ & (1u << lhs_reg_)) == 0; + return (delay_slot.masks_.cc_outs_ & (1u << lhs_reg_)) == 0; default: // We don't support synthetic R2 branches (preceded with slt[u]) at this level @@ -4192,7 +3660,7 @@ bool MipsAssembler::Branch::CanHaveDelayedInstruction(const DelaySlot& delay_slo // Branches with one FPU register source. case kCondF: case kCondT: - return (delay_slot.fpr_outs_mask_ & (1u << lhs_reg_)) == 0; + return (delay_slot.masks_.fpr_outs_ & (1u << lhs_reg_)) == 0; // Others have a forbidden slot instead of a delay slot. default: return false; @@ -4858,8 +4326,8 @@ bool MipsAssembler::CanExchangeWithSlt(Register rs, Register rt) const { // Likewise, if the instruction depends on AT, it can't be exchanged with slt[u] // because slt[u] changes AT. return (delay_slot_.instruction_ != 0 && - (delay_slot_.gpr_outs_mask_ & ((1u << AT) | (1u << rs) | (1u << rt))) == 0 && - (delay_slot_.gpr_ins_mask_ & (1u << AT)) == 0); + (delay_slot_.masks_.gpr_outs_ & ((1u << AT) | (1u << rs) | (1u << rt))) == 0 && + (delay_slot_.masks_.gpr_ins_ & (1u << AT)) == 0); } void MipsAssembler::ExchangeWithSlt(const DelaySlot& forwarded_slot) { diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h index 1c3097ac584ce37746824050dc8dc3cadaba18b1..7de8e2e3665de1862150bb8c6f72bf1dbec5e1f5 100644 --- a/compiler/utils/mips/assembler_mips.h +++ b/compiler/utils/mips/assembler_mips.h @@ -74,6 +74,81 @@ enum FPClassMaskType { kPositiveZero = 0x200, }; +// Instruction description in terms of input and output registers. +// Used for instruction reordering. +struct InOutRegMasks { + InOutRegMasks() + : gpr_outs_(0), gpr_ins_(0), fpr_outs_(0), fpr_ins_(0), cc_outs_(0), cc_ins_(0) {} + + inline InOutRegMasks& GprOuts(Register reg) { + gpr_outs_ |= (1u << reg); + gpr_outs_ &= ~1u; // Ignore register ZERO. + return *this; + } + template + inline InOutRegMasks& GprOuts(T one, Ts... more) { GprOuts(one); GprOuts(more...); return *this; } + + inline InOutRegMasks& GprIns(Register reg) { + gpr_ins_ |= (1u << reg); + gpr_ins_ &= ~1u; // Ignore register ZERO. + return *this; + } + template + inline InOutRegMasks& GprIns(T one, Ts... more) { GprIns(one); GprIns(more...); return *this; } + + inline InOutRegMasks& GprInOuts(Register reg) { GprIns(reg); GprOuts(reg); return *this; } + template + inline InOutRegMasks& GprInOuts(T one, Ts... more) { + GprInOuts(one); + GprInOuts(more...); + return *this; + } + + inline InOutRegMasks& FprOuts(FRegister reg) { fpr_outs_ |= (1u << reg); return *this; } + inline InOutRegMasks& FprOuts(VectorRegister reg) { return FprOuts(static_cast(reg)); } + template + inline InOutRegMasks& FprOuts(T one, Ts... more) { FprOuts(one); FprOuts(more...); return *this; } + + inline InOutRegMasks& FprIns(FRegister reg) { fpr_ins_ |= (1u << reg); return *this; } + inline InOutRegMasks& FprIns(VectorRegister reg) { return FprIns(static_cast(reg)); } + template + inline InOutRegMasks& FprIns(T one, Ts... more) { FprIns(one); FprIns(more...); return *this; } + + inline InOutRegMasks& FprInOuts(FRegister reg) { FprIns(reg); FprOuts(reg); return *this; } + inline InOutRegMasks& FprInOuts(VectorRegister reg) { + return FprInOuts(static_cast(reg)); + } + template + inline InOutRegMasks& FprInOuts(T one, Ts... more) { + FprInOuts(one); + FprInOuts(more...); + return *this; + } + + inline InOutRegMasks& CcOuts(int cc) { cc_outs_ |= (1u << cc); return *this; } + template + inline InOutRegMasks& CcOuts(T one, Ts... more) { CcOuts(one); CcOuts(more...); return *this; } + + inline InOutRegMasks& CcIns(int cc) { cc_ins_ |= (1u << cc); return *this; } + template + inline InOutRegMasks& CcIns(T one, Ts... more) { CcIns(one); CcIns(more...); return *this; } + + // Mask of output GPRs for the instruction. + uint32_t gpr_outs_; + // Mask of input GPRs for the instruction. + uint32_t gpr_ins_; + // Mask of output FPRs for the instruction. + uint32_t fpr_outs_; + // Mask of input FPRs for the instruction. + uint32_t fpr_ins_; + // Mask of output FPU condition code flags for the instruction. + uint32_t cc_outs_; + // Mask of input FPU condition code flags for the instruction. + uint32_t cc_ins_; + + // TODO: add LO and HI. +}; + class MipsLabel : public Label { public: MipsLabel() : prev_branch_id_plus_one_(0) {} @@ -462,6 +537,16 @@ class MipsAssembler FINAL : public Assembler, public JNIMacroAssembler +#include + #include "arch/mips/registers_mips.h" -#include "base/logging.h" #include "base/macros.h" #include "globals.h" diff --git a/compiler/utils/mips64/constants_mips64.h b/compiler/utils/mips64/constants_mips64.h index bc8e40b437d4fca402df5fdfd02b13e7f9b58ff5..310f23c28740d0ae405568b2d20cb808b56cd3c3 100644 --- a/compiler/utils/mips64/constants_mips64.h +++ b/compiler/utils/mips64/constants_mips64.h @@ -19,8 +19,9 @@ #include +#include + #include "arch/mips64/registers_mips64.h" -#include "base/logging.h" #include "base/macros.h" #include "globals.h" diff --git a/compiler/utils/swap_space.cc b/compiler/utils/swap_space.cc index 12d113d4201d412e3062efa0025cbe1eb34e2838..1f9ad4242df8ba1191fdc1f436094962ecec8498 100644 --- a/compiler/utils/swap_space.cc +++ b/compiler/utils/swap_space.cc @@ -22,7 +22,6 @@ #include #include "base/bit_utils.h" -#include "base/logging.h" #include "base/macros.h" #include "base/mutex.h" #include "thread-current-inl.h" diff --git a/compiler/utils/swap_space.h b/compiler/utils/swap_space.h index 08e243b64440f0b89345f03f425ddb399964d36a..76df52710854a0d1cd5d9ee652e8ea3ccc2d3ebe 100644 --- a/compiler/utils/swap_space.h +++ b/compiler/utils/swap_space.h @@ -24,7 +24,8 @@ #include #include -#include "base/logging.h" +#include + #include "base/macros.h" #include "base/mutex.h" @@ -124,7 +125,7 @@ class SwapAllocator { explicit SwapAllocator(SwapSpace* swap_space) : swap_space_(swap_space) {} template - SwapAllocator(const SwapAllocator& other) // NOLINT, implicit + SwapAllocator(const SwapAllocator& other) : swap_space_(other.swap_space_) {} SwapAllocator(const SwapAllocator& other) = default; @@ -160,7 +161,7 @@ class SwapAllocator { explicit SwapAllocator(SwapSpace* swap_space) : swap_space_(swap_space) {} template - SwapAllocator(const SwapAllocator& other) // NOLINT, implicit + SwapAllocator(const SwapAllocator& other) : swap_space_(other.swap_space_) {} SwapAllocator(const SwapAllocator& other) = default; diff --git a/compiler/utils/test_dex_file_builder.h b/compiler/utils/test_dex_file_builder.h index 0da30fe7689712be28611d724351a9d1f20c87ae..441ef8e117089faf6f5ff7a4b4e55e052bf65646 100644 --- a/compiler/utils/test_dex_file_builder.h +++ b/compiler/utils/test_dex_file_builder.h @@ -24,8 +24,9 @@ #include #include +#include + #include "base/bit_utils.h" -#include "base/logging.h" #include "dex_file_loader.h" #include "standard_dex_file.h" diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc index e232addd26f5d7f62a3353384a10b95570dcdda7..36c5c3c0c4613e8c40153c5b6732527fb4716add 100644 --- a/compiler/utils/x86/assembler_x86_test.cc +++ b/compiler/utils/x86/assembler_x86_test.cc @@ -90,7 +90,7 @@ class AssemblerX86Test : public AssemblerTest +#include + #include "arch/x86/registers_x86.h" -#include "base/logging.h" #include "base/macros.h" #include "globals.h" diff --git a/compiler/utils/x86_64/constants_x86_64.h b/compiler/utils/x86_64/constants_x86_64.h index cc508a196b5107b445e3bbea0a0d87b7958e13a4..2af3e7be16a1b83fba6f55d19ae5e339253c260e 100644 --- a/compiler/utils/x86_64/constants_x86_64.h +++ b/compiler/utils/x86_64/constants_x86_64.h @@ -19,8 +19,9 @@ #include +#include + #include "arch/x86_64/registers_x86_64.h" -#include "base/logging.h" #include "base/macros.h" #include "globals.h" diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc index 4709fd0e9e7bc3aa3fc03bcc5afe9d7a1246bf1c..ee1d7c69bcabbf0b4d2dfe01167c3007744484da 100644 --- a/compiler/verifier_deps_test.cc +++ b/compiler/verifier_deps_test.cc @@ -158,11 +158,10 @@ class VerifierDepsTest : public CommonCompilerTest { while (it.HasNextDirectMethod()) { ArtMethod* resolved_method = class_linker_->ResolveMethod( - *primary_dex_file_, it.GetMemberIndex(), dex_cache_handle, class_loader_handle, - nullptr, + /* referrer */ nullptr, it.GetMethodInvokeType(*class_def)); CHECK(resolved_method != nullptr); if (method_name == resolved_method->GetName()) { diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp index 13d2655917ffb4629a209bccacf1742e05fc89aa..6bebf7d2da3086544c52a7663de88c185cbfef1f 100644 --- a/dex2oat/Android.bp +++ b/dex2oat/Android.bp @@ -220,6 +220,7 @@ art_cc_test { "linker/elf_writer_test.cc", "linker/image_test.cc", "linker/image_write_read_test.cc", + "linker/index_bss_mapping_encoder_test.cc", "linker/multi_oat_relative_patcher_test.cc", "linker/oat_writer_test.cc", ], diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 9fa7f697d925d719003efa8a3cb062e5ba7e9f2e..11c903145faffff880dc8fc7c67359d0cc079621 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -58,6 +58,7 @@ #include "compiler_callbacks.h" #include "debug/elf_debug_writer.h" #include "debug/method_debug_info.h" +#include "dexlayout.h" #include "dex/quick_compiler_callbacks.h" #include "dex/verification_results.h" #include "dex2oat_options.h" @@ -345,7 +346,7 @@ NO_RETURN static void Usage(const char* fmt, ...) { CompilerOptions::kDefaultInlineMaxCodeUnits); UsageError(" Default: %d", CompilerOptions::kDefaultInlineMaxCodeUnits); UsageError(""); - UsageError(" --dump-timing: display a breakdown of where time was spent"); + UsageError(" --dump-timings: display a breakdown of where time was spent"); UsageError(""); UsageError(" -g"); UsageError(" --generate-debug-info: Generate debug information for native debugging,"); @@ -537,11 +538,8 @@ class WatchDog { // it's rather easy to hang in unwinding. // LogLine also avoids ART logging lock issues, as it's really only a wrapper around // logcat logging or stderr output. - android::base::LogMessage::LogLine(__FILE__, - __LINE__, - android::base::LogId::DEFAULT, - LogSeverity::FATAL, - message.c_str()); + LogHelper::LogLineLowStack(__FILE__, __LINE__, LogSeverity::FATAL, message.c_str()); + // If we're on the host, try to dump all threads to get a sense of what's going on. This is // restricted to the host as the dump may itself go bad. // TODO: Use a double watchdog timeout, so we can enable this on-device. @@ -628,10 +626,6 @@ class Dex2Oat FINAL { opened_dex_files_maps_(), opened_dex_files_(), no_inline_from_dex_files_(), - dump_stats_(false), - dump_passes_(false), - dump_timing_(false), - dump_slow_timing_(kIsDebugBuild), avoid_storing_invocation_(false), swap_fd_(kInvalidFd), app_image_fd_(kInvalidFd), @@ -1221,9 +1215,6 @@ class Dex2Oat FINAL { } AssignTrueIfExists(args, M::Host, &is_host_); - AssignTrueIfExists(args, M::DumpTiming, &dump_timing_); - AssignTrueIfExists(args, M::DumpPasses, &dump_passes_); - AssignTrueIfExists(args, M::DumpStats, &dump_stats_); AssignTrueIfExists(args, M::AvoidStoringInvocation, &avoid_storing_invocation_); AssignTrueIfExists(args, M::MultiImage, &multi_image_); @@ -1565,7 +1556,7 @@ class Dex2Oat FINAL { // 2) when we have a vdex file, which means it was already verified. const bool verify = !DoDexLayoutOptimizations() && (input_vdex_file_ == nullptr); if (!oat_writers_[i]->WriteAndOpenDexFiles( - kIsVdexEnabled ? vdex_files_[i].get() : oat_files_[i].get(), + vdex_files_[i].get(), rodata_.back(), instruction_set_, instruction_set_features_.get(), @@ -1693,10 +1684,10 @@ class Dex2Oat FINAL { CHECK(class_loader != nullptr); ScopedObjectAccess soa(Thread::Current()); // Unload class loader to free RAM. - jweak weak_class_loader = soa.Env()->vm->AddWeakGlobalRef( + jweak weak_class_loader = soa.Env()->GetVm()->AddWeakGlobalRef( soa.Self(), soa.Decode(class_loader)); - soa.Env()->vm->DeleteGlobalRef(soa.Self(), class_loader); + soa.Env()->GetVm()->DeleteGlobalRef(soa.Self(), class_loader); runtime_->GetHeap()->CollectGarbage(/*clear_soft_references*/ true); ObjPtr decoded_weak = soa.Decode(weak_class_loader); if (decoded_weak != nullptr) { @@ -1726,7 +1717,6 @@ class Dex2Oat FINAL { ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); TimingLogger::ScopedTiming t("dex2oat Compile", timings_); - compiler_phases_timings_.reset(new CumulativeLogger("compilation times")); // Find the dex files we should not inline from. std::vector no_inline_filters; @@ -1787,9 +1777,6 @@ class Dex2Oat FINAL { compiled_classes_.release(), compiled_methods_.release(), thread_count_, - dump_stats_, - dump_passes_, - compiler_phases_timings_.get(), swap_fd_, profile_compilation_info_.get())); driver_->SetDexFilesForOatFile(dex_files_); @@ -2202,12 +2189,10 @@ class Dex2Oat FINAL { } void DumpTiming() { - if (dump_timing_ || (dump_slow_timing_ && timings_->GetTotalNs() > MsToNs(1000))) { + if (compiler_options_->GetDumpTimings() || + (kIsDebugBuild && timings_->GetTotalNs() > MsToNs(1000))) { LOG(INFO) << Dumpable(*timings_); } - if (dump_passes_) { - LOG(INFO) << Dumpable(*driver_->GetTimingsLogger()); - } } bool IsImage() const { @@ -2246,12 +2231,16 @@ class Dex2Oat FINAL { return DoProfileGuidedOptimizations(); } - bool DoEagerUnquickeningOfVdex() const { - // DexLayout can invalidate the vdex metadata, so we need to unquicken - // the vdex file eagerly, before passing it to dexlayout. + bool MayInvalidateVdexMetadata() const { + // DexLayout can invalidate the vdex metadata if changing the class def order is enabled, so + // we need to unquicken the vdex file eagerly, before passing it to dexlayout. return DoDexLayoutOptimizations(); } + bool DoEagerUnquickeningOfVdex() const { + return MayInvalidateVdexMetadata(); + } + bool LoadProfile() { DCHECK(UseProfile()); // TODO(calin): We should be using the runtime arena pool (instead of the @@ -2827,7 +2816,7 @@ class Dex2Oat FINAL { // Dex files we are compiling, does not include the class path dex files. std::vector dex_files_; std::string no_inline_from_string_; - CompactDexLevel compact_dex_level_ = CompactDexLevel::kCompactDexLevelNone; + CompactDexLevel compact_dex_level_ = kDefaultCompactDexLevel; std::vector> elf_writers_; std::vector> oat_writers_; @@ -2842,10 +2831,6 @@ class Dex2Oat FINAL { // Note that this might contain pointers owned by class_loader_context_. std::vector no_inline_from_dex_files_; - bool dump_stats_; - bool dump_passes_; - bool dump_timing_; - bool dump_slow_timing_; bool avoid_storing_invocation_; std::string swap_file_name_; int swap_fd_; @@ -2858,7 +2843,6 @@ class Dex2Oat FINAL { int profile_file_fd_; std::unique_ptr profile_compilation_info_; TimingLogger* timings_; - std::unique_ptr compiler_phases_timings_; std::vector> dex_files_per_oat_file_; std::unordered_map dex_file_oat_index_map_; @@ -2903,7 +2887,7 @@ class ScopedGlobalRef { ~ScopedGlobalRef() { if (obj_ != nullptr) { ScopedObjectAccess soa(Thread::Current()); - soa.Env()->vm->DeleteGlobalRef(soa.Self(), obj_); + soa.Env()->GetVm()->DeleteGlobalRef(soa.Self(), obj_); } } diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc index 708665534a08ea58235337cfbe68858f092bd930..035b395300b620167801b7610f4be930e54a5002 100644 --- a/dex2oat/dex2oat_image_test.cc +++ b/dex2oat/dex2oat_image_test.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include // NOLINT [build/c++11] [5] +#include #include #include #include @@ -22,10 +22,11 @@ #include #include +#include + #include "common_runtime_test.h" #include "base/file_utils.h" -#include "base/logging.h" #include "base/macros.h" #include "base/unix_file/fd_file.h" #include "dex_file-inl.h" diff --git a/dex2oat/dex2oat_options.cc b/dex2oat/dex2oat_options.cc index 2cf070155fa18ccb8d286a6c019d0120577b065c..d9b4ea783505fd15400ae305ce85987ccad675b9 100644 --- a/dex2oat/dex2oat_options.cc +++ b/dex2oat/dex2oat_options.cc @@ -43,7 +43,7 @@ struct CmdlineType : CmdlineTypeParser { // Specify storage for the Dex2oatOptions keys. #define DEX2OAT_OPTIONS_KEY(Type, Name, ...) \ - const Dex2oatArgumentMap::Key Dex2oatArgumentMap::Name {__VA_ARGS__}; // NOLINT [readability/braces] [4] + const Dex2oatArgumentMap::Key Dex2oatArgumentMap::Name {__VA_ARGS__}; #include "dex2oat_options.def" #pragma GCC diagnostic push @@ -220,12 +220,6 @@ static Parser CreateArgumentParser() { .IntoKey(M::Backend) .Define("--host") .IntoKey(M::Host) - .Define("--dump-timing") - .IntoKey(M::DumpTiming) - .Define("--dump-passes") - .IntoKey(M::DumpPasses) - .Define("--dump-stats") - .IntoKey(M::DumpStats) .Define("--avoid-storing-invocation") .IntoKey(M::AvoidStoringInvocation) .Define("--very-large-app-threshold=_") diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index afca26db717e2332769fb1d6bda7f723a417e78d..c8a70c0161b4abb291aa2a7e4c17864115870a3f 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include // NOLINT [build/c++11] [5] +#include #include #include #include @@ -22,11 +22,11 @@ #include #include -#include "android-base/stringprintf.h" +#include +#include #include "common_runtime_test.h" -#include "base/logging.h" #include "base/macros.h" #include "base/mutex-inl.h" #include "bytecode_utils.h" @@ -875,6 +875,8 @@ TEST_F(Dex2oatLayoutTest, TestLayoutAppImage) { } TEST_F(Dex2oatLayoutTest, TestVdexLayout) { + // Disabled until figure out running compact dex + DexLayout causes quickening errors. + TEST_DISABLED_FOR_COMPACT_DEX(); RunTestVDex(); } @@ -941,7 +943,7 @@ class Dex2oatUnquickenTest : public Dex2oatTest { class_it.Next()) { if (class_it.IsAtMethod() && class_it.GetMethodCodeItem() != nullptr) { for (const DexInstructionPcPair& inst : - class_it.GetMethodCodeItem()->Instructions()) { + class_it.GetMethodCodeItem()->Instructions()) { ASSERT_FALSE(inst->IsQuickened()); } } @@ -953,6 +955,8 @@ class Dex2oatUnquickenTest : public Dex2oatTest { }; TEST_F(Dex2oatUnquickenTest, UnquickenMultiDex) { + // Disabled until figure out running compact dex + DexLayout causes quickening errors. + TEST_DISABLED_FOR_COMPACT_DEX(); RunUnquickenMultiDex(); } @@ -991,6 +995,7 @@ TEST_F(Dex2oatWatchdogTest, TestWatchdogOK) { TEST_F(Dex2oatWatchdogTest, TestWatchdogTrigger) { TEST_DISABLED_FOR_MEMORY_TOOL_VALGRIND(); // b/63052624 + TEST_DISABLED_WITHOUT_BAKER_READ_BARRIERS(); // b/63052624 // Check with ten milliseconds. RunTest(false, { "--watchdog-timeout=10" }); } @@ -1244,7 +1249,7 @@ TEST_F(Dex2oatTest, LayoutSections) { ClassDataItemIterator it(*dex, dex->GetClassData(*class_def)); it.SkipAllFields(); std::set code_item_offsets; - for (; it.HasNextDirectMethod() || it.HasNextVirtualMethod(); it.Next()) { + for (; it.HasNextMethod(); it.Next()) { const uint16_t method_idx = it.GetMemberIndex(); const size_t code_item_offset = it.GetMethodCodeItemOffset(); if (code_item_offsets.insert(code_item_offset).second) { @@ -1332,10 +1337,10 @@ TEST_F(Dex2oatTest, LayoutSections) { code_section.parts_[static_cast(LayoutType::kLayoutTypeUnused)]; // All the sections should be non-empty. - EXPECT_GT(section_hot_code.size_, 0u); - EXPECT_GT(section_sometimes_used.size_, 0u); - EXPECT_GT(section_startup_only.size_, 0u); - EXPECT_GT(section_unused.size_, 0u); + EXPECT_GT(section_hot_code.Size(), 0u); + EXPECT_GT(section_sometimes_used.Size(), 0u); + EXPECT_GT(section_startup_only.Size(), 0u); + EXPECT_GT(section_unused.Size(), 0u); // Open the dex file since we need to peek at the code items to verify the layout matches what // we expect. @@ -1356,7 +1361,7 @@ TEST_F(Dex2oatTest, LayoutSections) { // corresponding code item offsets to verify the layout. ClassDataItemIterator it(*dex_file, dex_file->GetClassData(*class_def)); it.SkipAllFields(); - for (; it.HasNextDirectMethod() || it.HasNextVirtualMethod(); it.Next()) { + for (; it.HasNextMethod(); it.Next()) { const size_t method_idx = it.GetMemberIndex(); const size_t code_item_offset = it.GetMethodCodeItemOffset(); const bool is_hot = ContainsElement(hot_methods, method_idx); @@ -1364,25 +1369,25 @@ TEST_F(Dex2oatTest, LayoutSections) { const bool is_post_startup = ContainsElement(post_methods, method_idx); if (is_hot) { // Hot is highest precedence, check that the hot methods are in the hot section. - EXPECT_LT(code_item_offset - section_hot_code.offset_, section_hot_code.size_); + EXPECT_TRUE(section_hot_code.Contains(code_item_offset)); ++hot_count; } else if (is_post_startup) { // Post startup is sometimes used section. - EXPECT_LT(code_item_offset - section_sometimes_used.offset_, section_sometimes_used.size_); + EXPECT_TRUE(section_sometimes_used.Contains(code_item_offset)); ++post_startup_count; } else if (is_startup) { // Startup at this point means not hot or post startup, these must be startup only then. - EXPECT_LT(code_item_offset - section_startup_only.offset_, section_startup_only.size_); + EXPECT_TRUE(section_startup_only.Contains(code_item_offset)); ++startup_count; } else { - if (code_item_offset - section_unused.offset_ < section_unused.size_) { + if (section_unused.Contains(code_item_offset)) { // If no flags are set, the method should be unused ... ++unused_count; } else { // or this method is part of the last code item and the end is 4 byte aligned. ClassDataItemIterator it2(*dex_file, dex_file->GetClassData(*class_def)); it2.SkipAllFields(); - for (; it2.HasNextDirectMethod() || it2.HasNextVirtualMethod(); it2.Next()) { + for (; it2.HasNextMethod(); it2.Next()) { EXPECT_LE(it2.GetMethodCodeItemOffset(), code_item_offset); } uint32_t code_item_size = dex_file->FindCodeItemOffset(*class_def, method_idx); diff --git a/dex2oat/linker/elf_writer_quick.cc b/dex2oat/linker/elf_writer_quick.cc index b139a12fd49a6712c3dd4fd0cb1c1dc99ccda3ee..aa64b7d59e2727acd05474acf0e5ad1e0f9eaa68 100644 --- a/dex2oat/linker/elf_writer_quick.cc +++ b/dex2oat/linker/elf_writer_quick.cc @@ -20,8 +20,9 @@ #include #include +#include + #include "base/casts.h" -#include "base/logging.h" #include "compiled_method.h" #include "debug/elf_debug_writer.h" #include "debug/method_debug_info.h" @@ -66,7 +67,7 @@ class DebugInfoTask : public Task { void Run(Thread*) { result_ = debug::MakeMiniDebugInfo(isa_, instruction_set_features_, - rodata_section_size_, + kPageSize + rodata_section_size_, // .text address. text_section_size_, method_infos_); } @@ -172,6 +173,7 @@ template void ElfWriterQuick::Start() { builder_->Start(); if (compiler_options_->GetGenerateBuildId()) { + builder_->GetBuildId()->AllocateVirtualMemory(builder_->GetBuildId()->GetSize()); builder_->WriteBuildIdSection(); } } @@ -224,9 +226,6 @@ void ElfWriterQuick::EndText(OutputStream* text) { template void ElfWriterQuick::WriteDynamicSection() { - if (bss_size_ != 0u) { - builder_->GetBss()->WriteNoBitsSection(bss_size_); - } if (builder_->GetIsa() == InstructionSet::kMips || builder_->GetIsa() == InstructionSet::kMips64) { builder_->WriteMIPSabiflagsSection(); diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h index d3d42b98bbfb2a094056258ae194b0ddc059596d..85145d3d6437f0855d445a5017f2cd0de2aa6404 100644 --- a/dex2oat/linker/image_test.h +++ b/dex2oat/linker/image_test.h @@ -269,7 +269,7 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, std::unique_ptr cur_opened_dex_files_map; std::vector> cur_opened_dex_files; bool dex_files_ok = oat_writers[i]->WriteAndOpenDexFiles( - kIsVdexEnabled ? vdex_files[i].GetFile() : oat_files[i].GetFile(), + vdex_files[i].GetFile(), rodata.back(), driver->GetInstructionSet(), driver->GetInstructionSetFeatures(), @@ -293,16 +293,7 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, bool image_space_ok = writer->PrepareImageAddressSpace(); ASSERT_TRUE(image_space_ok); - if (kIsVdexEnabled) { - for (size_t i = 0, size = vdex_files.size(); i != size; ++i) { - std::unique_ptr vdex_out = - std::make_unique( - std::make_unique(vdex_files[i].GetFile())); - oat_writers[i]->WriteVerifierDeps(vdex_out.get(), nullptr); - oat_writers[i]->WriteChecksumsAndVdexHeader(vdex_out.get()); - } - } - + DCHECK_EQ(vdex_files.size(), oat_files.size()); for (size_t i = 0, size = oat_files.size(); i != size; ++i) { MultiOatRelativePatcher patcher(driver->GetInstructionSet(), driver->GetInstructionSetFeatures()); @@ -310,6 +301,14 @@ inline void CompilationHelper::Compile(CompilerDriver* driver, ElfWriter* const elf_writer = elf_writers[i].get(); std::vector cur_dex_files(1u, class_path[i]); oat_writer->Initialize(driver, writer.get(), cur_dex_files); + + std::unique_ptr vdex_out = + std::make_unique( + std::make_unique(vdex_files[i].GetFile())); + oat_writer->WriteVerifierDeps(vdex_out.get(), nullptr); + oat_writer->WriteQuickeningInfo(vdex_out.get()); + oat_writer->WriteChecksumsAndVdexHeader(vdex_out.get()); + oat_writer->PrepareLayout(&patcher); size_t rodata_size = oat_writer->GetOatHeader().GetExecutableOffset(); size_t text_size = oat_writer->GetOatSize() - rodata_size; diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc index 68c9f80a441033ebb12040e3f6cf87f6b06e8dc2..738bbf8e9de11394b5f6fa209b26def2429f9cb7 100644 --- a/dex2oat/linker/image_writer.cc +++ b/dex2oat/linker/image_writer.cc @@ -29,7 +29,7 @@ #include "art_method-inl.h" #include "base/callee_save_type.h" #include "base/enums.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/unix_file/fd_file.h" #include "class_linker-inl.h" #include "compiled_method.h" @@ -365,7 +365,7 @@ void ImageWriter::AssignImageOffset(mirror::Object* object, ImageWriter::BinSlot size_t oat_index = GetOatIndex(object); ImageInfo& image_info = GetImageInfo(oat_index); - size_t bin_slot_offset = image_info.bin_slot_offsets_[bin_slot.GetBin()]; + size_t bin_slot_offset = image_info.GetBinSlotOffset(bin_slot.GetBin()); size_t new_offset = bin_slot_offset + bin_slot.GetIndex(); DCHECK_ALIGNED(new_offset, kObjectAlignment); @@ -436,9 +436,10 @@ void ImageWriter::PrepareDexCacheArraySlots() { auto it = dex_file_oat_index_map_.find(dex_file); DCHECK(it != dex_file_oat_index_map_.end()) << dex_file->GetLocation(); ImageInfo& image_info = GetImageInfo(it->second); - image_info.dex_cache_array_starts_.Put(dex_file, image_info.bin_slot_sizes_[kBinDexCacheArray]); + image_info.dex_cache_array_starts_.Put( + dex_file, image_info.GetBinSlotSize(Bin::kDexCacheArray)); DexCacheArraysLayout layout(target_ptr_size_, dex_file); - image_info.bin_slot_sizes_[kBinDexCacheArray] += layout.Size(); + image_info.IncrementBinSlotSize(Bin::kDexCacheArray, layout.Size()); } ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); @@ -494,7 +495,7 @@ void ImageWriter::AddDexCacheArrayRelocation(void* array, DCHECK(!IsInBootImage(array)); size_t oat_index = GetOatIndexForDexCache(dex_cache); native_object_relocations_.emplace(array, - NativeObjectRelocation { oat_index, offset, kNativeObjectRelocationTypeDexCacheArray }); + NativeObjectRelocation { oat_index, offset, NativeObjectRelocationType::kDexCacheArray }); } } @@ -512,7 +513,7 @@ void ImageWriter::AddMethodPointerArray(mirror::PointerArray* arr) { } // kBinArtMethodClean picked arbitrarily, just required to differentiate between ArtFields and // ArtMethods. - pointer_arrays_.emplace(arr, kBinArtMethodClean); + pointer_arrays_.emplace(arr, Bin::kArtMethodClean); } void ImageWriter::AssignImageBinSlot(mirror::Object* object, size_t oat_index) { @@ -528,8 +529,7 @@ void ImageWriter::AssignImageBinSlot(mirror::Object* object, size_t oat_index) { // // This means more pages will stay either clean or shared dirty (with zygote) and // the app will use less of its own (private) memory. - Bin bin = kBinRegular; - size_t current_offset = 0u; + Bin bin = Bin::kRegular; if (kBinObjects) { // @@ -563,7 +563,7 @@ void ImageWriter::AssignImageBinSlot(mirror::Object* object, size_t oat_index) { // so packing them together will not result in a noticeably tighter dirty-to-clean ratio. // if (object->IsClass()) { - bin = kBinClassVerified; + bin = Bin::kClassVerified; mirror::Class* klass = object->AsClass(); // Add non-embedded vtable to the pointer array table if there is one. @@ -584,15 +584,15 @@ void ImageWriter::AssignImageBinSlot(mirror::Object* object, size_t oat_index) { // - classes with dirty static fields. if (dirty_image_objects_ != nullptr && dirty_image_objects_->find(klass->PrettyDescriptor()) != dirty_image_objects_->end()) { - bin = kBinKnownDirty; + bin = Bin::kKnownDirty; } else if (klass->GetStatus() == Class::kStatusInitialized) { - bin = kBinClassInitialized; + bin = Bin::kClassInitialized; // If the class's static fields are all final, put it into a separate bin // since it's very likely it will stay clean. uint32_t num_static_fields = klass->NumStaticFields(); if (num_static_fields == 0) { - bin = kBinClassInitializedFinalStatics; + bin = Bin::kClassInitializedFinalStatics; } else { // Maybe all the statics are final? bool all_final = true; @@ -605,20 +605,20 @@ void ImageWriter::AssignImageBinSlot(mirror::Object* object, size_t oat_index) { } if (all_final) { - bin = kBinClassInitializedFinalStatics; + bin = Bin::kClassInitializedFinalStatics; } } } } else if (object->GetClass()->IsStringClass()) { - bin = kBinString; // Strings are almost always immutable (except for object header). + bin = Bin::kString; // Strings are almost always immutable (except for object header). } else if (object->GetClass() == Runtime::Current()->GetClassLinker()->GetClassRoot(ClassLinker::kJavaLangObject)) { // Instance of java lang object, probably a lock object. This means it will be dirty when we // synchronize on it. - bin = kBinMiscDirty; + bin = Bin::kMiscDirty; } else if (object->IsDexCache()) { // Dex file field becomes dirty when the image is loaded. - bin = kBinMiscDirty; + bin = Bin::kMiscDirty; } // else bin = kBinRegular } @@ -630,14 +630,15 @@ void ImageWriter::AssignImageBinSlot(mirror::Object* object, size_t oat_index) { ImageInfo& image_info = GetImageInfo(oat_index); size_t offset_delta = RoundUp(object_size, kObjectAlignment); // 64-bit alignment - current_offset = image_info.bin_slot_sizes_[bin]; // How many bytes the current bin is at (aligned). + // How many bytes the current bin is at (aligned). + size_t current_offset = image_info.GetBinSlotSize(bin); // Move the current bin size up to accommodate the object we just assigned a bin slot. - image_info.bin_slot_sizes_[bin] += offset_delta; + image_info.IncrementBinSlotSize(bin, offset_delta); BinSlot new_bin_slot(bin, current_offset); SetImageBinSlot(object, new_bin_slot); - ++image_info.bin_slot_count_[bin]; + image_info.IncrementBinSlotCount(bin, 1u); // Grow the image closer to the end by the object we just assigned. image_info.image_end_ += offset_delta; @@ -665,7 +666,7 @@ bool ImageWriter::IsImageBinSlotAssigned(mirror::Object* object) const { BinSlot bin_slot(offset); size_t oat_index = GetOatIndex(object); const ImageInfo& image_info = GetImageInfo(oat_index); - DCHECK_LT(bin_slot.GetIndex(), image_info.bin_slot_sizes_[bin_slot.GetBin()]) + DCHECK_LT(bin_slot.GetIndex(), image_info.GetBinSlotSize(bin_slot.GetBin())) << "bin slot offset should not exceed the size of that bin"; } return true; @@ -682,7 +683,7 @@ ImageWriter::BinSlot ImageWriter::GetImageBinSlot(mirror::Object* object) const BinSlot bin_slot(static_cast(offset)); size_t oat_index = GetOatIndex(object); const ImageInfo& image_info = GetImageInfo(oat_index); - DCHECK_LT(bin_slot.GetIndex(), image_info.bin_slot_sizes_[bin_slot.GetBin()]); + DCHECK_LT(bin_slot.GetIndex(), image_info.GetBinSlotSize(bin_slot.GetBin())); return bin_slot; } @@ -1049,8 +1050,7 @@ void ImageWriter::PruneAndPreloadDexCache(ObjPtr dex_cache, const DexFile::MethodId& method_id = dex_file.GetMethodId(i); if (method_id.class_idx_ != last_class_idx) { last_class_idx = method_id.class_idx_; - last_class = class_linker->LookupResolvedType( - dex_file, last_class_idx, dex_cache, class_loader); + last_class = class_linker->LookupResolvedType(last_class_idx, dex_cache, class_loader); if (last_class != nullptr && !KeepClass(last_class)) { last_class = nullptr; } @@ -1095,8 +1095,7 @@ void ImageWriter::PruneAndPreloadDexCache(ObjPtr dex_cache, const DexFile::FieldId& field_id = dex_file.GetFieldId(i); if (field_id.class_idx_ != last_class_idx) { last_class_idx = field_id.class_idx_; - last_class = class_linker->LookupResolvedType( - dex_file, last_class_idx, dex_cache, class_loader); + last_class = class_linker->LookupResolvedType(last_class_idx, dex_cache, class_loader); if (last_class != nullptr && !KeepClass(last_class)) { last_class = nullptr; } @@ -1129,7 +1128,7 @@ void ImageWriter::PruneAndPreloadDexCache(ObjPtr dex_cache, uint32_t stored_index = pair.index; ObjPtr klass = pair.object.Read(); if (klass == nullptr || i < stored_index) { - klass = class_linker->LookupResolvedType(dex_file, type_idx, dex_cache, class_loader); + klass = class_linker->LookupResolvedType(type_idx, dex_cache, class_loader); if (klass != nullptr) { DCHECK_EQ(dex_cache->GetResolvedType(type_idx), klass); stored_index = i; // For correct clearing below if not keeping the `klass`. @@ -1147,7 +1146,7 @@ void ImageWriter::PruneAndPreloadDexCache(ObjPtr dex_cache, uint32_t stored_index = pair.index; ObjPtr string = pair.object.Read(); if (string == nullptr || i < stored_index) { - string = class_linker->LookupString(dex_file, string_idx, dex_cache); + string = class_linker->LookupString(string_idx, dex_cache); DCHECK(string == nullptr || dex_cache->GetResolvedString(string_idx) == string); } } @@ -1402,12 +1401,12 @@ mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack, auto it = native_object_relocations_.find(cur_fields); CHECK(it == native_object_relocations_.end()) << "Field array " << cur_fields << " already forwarded"; - size_t& offset = image_info.bin_slot_sizes_[kBinArtField]; + size_t offset = image_info.GetBinSlotSize(Bin::kArtField); DCHECK(!IsInBootImage(cur_fields)); native_object_relocations_.emplace( cur_fields, NativeObjectRelocation { - oat_index, offset, kNativeObjectRelocationTypeArtFieldArray + oat_index, offset, NativeObjectRelocationType::kArtFieldArray }); offset += header_size; // Forward individual fields so that we can quickly find where they belong. @@ -1420,9 +1419,14 @@ mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack, DCHECK(!IsInBootImage(field)); native_object_relocations_.emplace( field, - NativeObjectRelocation { oat_index, offset, kNativeObjectRelocationTypeArtField }); + NativeObjectRelocation { oat_index, + offset, + NativeObjectRelocationType::kArtField }); offset += sizeof(ArtField); } + image_info.IncrementBinSlotSize( + Bin::kArtField, header_size + cur_fields->size() * sizeof(ArtField)); + DCHECK_EQ(offset, image_info.GetBinSlotSize(Bin::kArtField)); } } // Visit and assign offsets for methods. @@ -1436,8 +1440,8 @@ mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack, } } NativeObjectRelocationType type = any_dirty - ? kNativeObjectRelocationTypeArtMethodDirty - : kNativeObjectRelocationTypeArtMethodClean; + ? NativeObjectRelocationType::kArtMethodDirty + : NativeObjectRelocationType::kArtMethodClean; Bin bin_type = BinTypeForNativeRelocationType(type); // Forward the entire array at once, but header first. const size_t method_alignment = ArtMethod::Alignment(target_ptr_size_); @@ -1449,15 +1453,15 @@ mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack, auto it = native_object_relocations_.find(array); CHECK(it == native_object_relocations_.end()) << "Method array " << array << " already forwarded"; - size_t& offset = image_info.bin_slot_sizes_[bin_type]; + size_t offset = image_info.GetBinSlotSize(bin_type); DCHECK(!IsInBootImage(array)); native_object_relocations_.emplace(array, NativeObjectRelocation { oat_index, offset, - any_dirty ? kNativeObjectRelocationTypeArtMethodArrayDirty - : kNativeObjectRelocationTypeArtMethodArrayClean }); - offset += header_size; + any_dirty ? NativeObjectRelocationType::kArtMethodArrayDirty + : NativeObjectRelocationType::kArtMethodArrayClean }); + image_info.IncrementBinSlotSize(bin_type, header_size); for (auto& m : as_klass->GetMethods(target_ptr_size_)) { AssignMethodOffset(&m, type, oat_index); } @@ -1476,7 +1480,7 @@ mirror::Object* ImageWriter::TryAssignBinSlot(WorkStack& work_stack, if (imt_method->IsRuntimeMethod() && !IsInBootImage(imt_method) && !NativeRelocationAssigned(imt_method)) { - AssignMethodOffset(imt_method, kNativeObjectRelocationTypeRuntimeMethod, oat_index); + AssignMethodOffset(imt_method, NativeObjectRelocationType::kRuntimeMethod, oat_index); } } } @@ -1526,9 +1530,9 @@ bool ImageWriter::TryAssignImTableOffset(ImTable* imt, size_t oat_index) { imt, NativeObjectRelocation { oat_index, - image_info.bin_slot_sizes_[kBinImTable], - kNativeObjectRelocationTypeIMTable}); - image_info.bin_slot_sizes_[kBinImTable] += size; + image_info.GetBinSlotSize(Bin::kImTable), + NativeObjectRelocationType::kIMTable}); + image_info.IncrementBinSlotSize(Bin::kImTable, size); return true; } @@ -1545,9 +1549,9 @@ void ImageWriter::TryAssignConflictTableOffset(ImtConflictTable* table, size_t o table, NativeObjectRelocation { oat_index, - image_info.bin_slot_sizes_[kBinIMTConflictTable], - kNativeObjectRelocationTypeIMTConflictTable}); - image_info.bin_slot_sizes_[kBinIMTConflictTable] += size; + image_info.GetBinSlotSize(Bin::kIMTConflictTable), + NativeObjectRelocationType::kIMTConflictTable}); + image_info.IncrementBinSlotSize(Bin::kIMTConflictTable, size); } void ImageWriter::AssignMethodOffset(ArtMethod* method, @@ -1560,9 +1564,10 @@ void ImageWriter::AssignMethodOffset(ArtMethod* method, TryAssignConflictTableOffset(method->GetImtConflictTable(target_ptr_size_), oat_index); } ImageInfo& image_info = GetImageInfo(oat_index); - size_t& offset = image_info.bin_slot_sizes_[BinTypeForNativeRelocationType(type)]; + Bin bin_type = BinTypeForNativeRelocationType(type); + size_t offset = image_info.GetBinSlotSize(bin_type); native_object_relocations_.emplace(method, NativeObjectRelocation { oat_index, offset, type }); - offset += ArtMethod::Size(target_ptr_size_); + image_info.IncrementBinSlotSize(bin_type, ArtMethod::Size(target_ptr_size_)); } void ImageWriter::UnbinObjectsIntoOffset(mirror::Object* obj) { @@ -1697,7 +1702,7 @@ void ImageWriter::CalculateNewObjectOffsets() { CHECK(m->IsRuntimeMethod()); DCHECK_EQ(compile_app_image_, IsInBootImage(m)) << "Trampolines should be in boot image"; if (!IsInBootImage(m)) { - AssignMethodOffset(m, kNativeObjectRelocationTypeRuntimeMethod, GetDefaultOatIndex()); + AssignMethodOffset(m, NativeObjectRelocationType::kRuntimeMethod, GetDefaultOatIndex()); } } @@ -1803,18 +1808,18 @@ void ImageWriter::CalculateNewObjectOffsets() { // Calculate bin slot offsets. for (ImageInfo& image_info : image_infos_) { size_t bin_offset = image_objects_offset_begin_; - for (size_t i = 0; i != kBinSize; ++i) { - switch (i) { - case kBinArtMethodClean: - case kBinArtMethodDirty: { + for (size_t i = 0; i != kNumberOfBins; ++i) { + switch (static_cast(i)) { + case Bin::kArtMethodClean: + case Bin::kArtMethodDirty: { bin_offset = RoundUp(bin_offset, method_alignment); break; } - case kBinDexCacheArray: + case Bin::kDexCacheArray: bin_offset = RoundUp(bin_offset, DexCacheArraysLayout::Alignment(target_ptr_size_)); break; - case kBinImTable: - case kBinIMTConflictTable: { + case Bin::kImTable: + case Bin::kIMTConflictTable: { bin_offset = RoundUp(bin_offset, static_cast(target_ptr_size_)); break; } @@ -1827,7 +1832,7 @@ void ImageWriter::CalculateNewObjectOffsets() { } // NOTE: There may be additional padding between the bin slots and the intern table. DCHECK_EQ(image_info.image_end_, - GetBinSizeSum(image_info, kBinMirrorCount) + image_objects_offset_begin_); + image_info.GetBinSizeSum(Bin::kMirrorCount) + image_objects_offset_begin_); } // Calculate image offsets. @@ -1864,7 +1869,7 @@ void ImageWriter::CalculateNewObjectOffsets() { NativeObjectRelocation& relocation = pair.second; Bin bin_type = BinTypeForNativeRelocationType(relocation.type); ImageInfo& image_info = GetImageInfo(relocation.oat_index); - relocation.offset += image_info.bin_slot_offsets_[bin_type]; + relocation.offset += image_info.GetBinSlotOffset(bin_type); } } @@ -1881,33 +1886,32 @@ size_t ImageWriter::ImageInfo::CreateImageSections(ImageSection* out_sections, // Add field section. ImageSection* field_section = &out_sections[ImageHeader::kSectionArtFields]; - *field_section = ImageSection(bin_slot_offsets_[kBinArtField], bin_slot_sizes_[kBinArtField]); - CHECK_EQ(bin_slot_offsets_[kBinArtField], field_section->Offset()); + *field_section = ImageSection(GetBinSlotOffset(Bin::kArtField), GetBinSlotSize(Bin::kArtField)); // Add method section. ImageSection* methods_section = &out_sections[ImageHeader::kSectionArtMethods]; *methods_section = ImageSection( - bin_slot_offsets_[kBinArtMethodClean], - bin_slot_sizes_[kBinArtMethodClean] + bin_slot_sizes_[kBinArtMethodDirty]); + GetBinSlotOffset(Bin::kArtMethodClean), + GetBinSlotSize(Bin::kArtMethodClean) + GetBinSlotSize(Bin::kArtMethodDirty)); // IMT section. ImageSection* imt_section = &out_sections[ImageHeader::kSectionImTables]; - *imt_section = ImageSection(bin_slot_offsets_[kBinImTable], bin_slot_sizes_[kBinImTable]); + *imt_section = ImageSection(GetBinSlotOffset(Bin::kImTable), GetBinSlotSize(Bin::kImTable)); // Conflict tables section. ImageSection* imt_conflict_tables_section = &out_sections[ImageHeader::kSectionIMTConflictTables]; - *imt_conflict_tables_section = ImageSection(bin_slot_offsets_[kBinIMTConflictTable], - bin_slot_sizes_[kBinIMTConflictTable]); + *imt_conflict_tables_section = ImageSection(GetBinSlotOffset(Bin::kIMTConflictTable), + GetBinSlotSize(Bin::kIMTConflictTable)); // Runtime methods section. ImageSection* runtime_methods_section = &out_sections[ImageHeader::kSectionRuntimeMethods]; - *runtime_methods_section = ImageSection(bin_slot_offsets_[kBinRuntimeMethod], - bin_slot_sizes_[kBinRuntimeMethod]); + *runtime_methods_section = ImageSection(GetBinSlotOffset(Bin::kRuntimeMethod), + GetBinSlotSize(Bin::kRuntimeMethod)); // Add dex cache arrays section. ImageSection* dex_cache_arrays_section = &out_sections[ImageHeader::kSectionDexCacheArrays]; - *dex_cache_arrays_section = ImageSection(bin_slot_offsets_[kBinDexCacheArray], - bin_slot_sizes_[kBinDexCacheArray]); + *dex_cache_arrays_section = ImageSection(GetBinSlotOffset(Bin::kDexCacheArray), + GetBinSlotSize(Bin::kDexCacheArray)); // For boot image, round up to the page boundary to separate the interned strings and // class table from the modifiable data. We shall mprotect() these pages read-only when // we load the boot image. This is more than sufficient for the string table alignment, @@ -2060,16 +2064,16 @@ void ImageWriter::CopyAndFixupNativeData(size_t oat_index) { DCHECK_GE(dest, image_info.image_->Begin() + image_info.image_end_); DCHECK(!IsInBootImage(pair.first)); switch (relocation.type) { - case kNativeObjectRelocationTypeArtField: { + case NativeObjectRelocationType::kArtField: { memcpy(dest, pair.first, sizeof(ArtField)); CopyReference( reinterpret_cast(dest)->GetDeclaringClassAddressWithoutBarrier(), reinterpret_cast(pair.first)->GetDeclaringClass().Ptr()); break; } - case kNativeObjectRelocationTypeRuntimeMethod: - case kNativeObjectRelocationTypeArtMethodClean: - case kNativeObjectRelocationTypeArtMethodDirty: { + case NativeObjectRelocationType::kRuntimeMethod: + case NativeObjectRelocationType::kArtMethodClean: + case NativeObjectRelocationType::kArtMethodDirty: { CopyAndFixupMethod(reinterpret_cast(pair.first), reinterpret_cast(dest), image_info); @@ -2077,12 +2081,12 @@ void ImageWriter::CopyAndFixupNativeData(size_t oat_index) { } // For arrays, copy just the header since the elements will get copied by their corresponding // relocations. - case kNativeObjectRelocationTypeArtFieldArray: { + case NativeObjectRelocationType::kArtFieldArray: { memcpy(dest, pair.first, LengthPrefixedArray::ComputeSize(0)); break; } - case kNativeObjectRelocationTypeArtMethodArrayClean: - case kNativeObjectRelocationTypeArtMethodArrayDirty: { + case NativeObjectRelocationType::kArtMethodArrayClean: + case NativeObjectRelocationType::kArtMethodArrayDirty: { size_t size = ArtMethod::Size(target_ptr_size_); size_t alignment = ArtMethod::Alignment(target_ptr_size_); memcpy(dest, pair.first, LengthPrefixedArray::ComputeSize(0, size, alignment)); @@ -2090,16 +2094,16 @@ void ImageWriter::CopyAndFixupNativeData(size_t oat_index) { reinterpret_cast*>(dest)->ClearPadding(size, alignment); break; } - case kNativeObjectRelocationTypeDexCacheArray: + case NativeObjectRelocationType::kDexCacheArray: // Nothing to copy here, everything is done in FixupDexCache(). break; - case kNativeObjectRelocationTypeIMTable: { + case NativeObjectRelocationType::kIMTable: { ImTable* orig_imt = reinterpret_cast(pair.first); ImTable* dest_imt = reinterpret_cast(dest); CopyAndFixupImTable(orig_imt, dest_imt); break; } - case kNativeObjectRelocationTypeIMTConflictTable: { + case NativeObjectRelocationType::kIMTConflictTable: { auto* orig_table = reinterpret_cast(pair.first); CopyAndFixupImtConflictTable( orig_table, @@ -2197,7 +2201,7 @@ void ImageWriter::FixupPointerArray(mirror::Object* dst, << method << " idx=" << i << "/" << num_elements << " with declaring class " << Class::PrettyClass(method->GetDeclaringClass()); } else { - CHECK_EQ(array_type, kBinArtField); + CHECK_EQ(array_type, Bin::kArtField); auto* field = reinterpret_cast(elem); LOG(FATAL) << "No relocation entry for ArtField " << field->PrettyField() << " @ " << field << " idx=" << i << "/" << num_elements << " with declaring class " @@ -2518,8 +2522,8 @@ void ImageWriter::FixupDexCache(mirror::DexCache* orig_dex_cache, copy_dex_cache->SetDexFile(nullptr); } -const uint8_t* ImageWriter::GetOatAddress(OatAddress type) const { - DCHECK_LT(type, kOatAddressCount); +const uint8_t* ImageWriter::GetOatAddress(StubType type) const { + DCHECK_LE(type, StubType::kLast); // If we are compiling an app image, we need to use the stubs of the boot image. if (compile_app_image_) { // Use the current image pointers. @@ -2531,26 +2535,26 @@ const uint8_t* ImageWriter::GetOatAddress(OatAddress type) const { const OatHeader& header = oat_file->GetOatHeader(); switch (type) { // TODO: We could maybe clean this up if we stored them in an array in the oat header. - case kOatAddressQuickGenericJNITrampoline: + case StubType::kQuickGenericJNITrampoline: return static_cast(header.GetQuickGenericJniTrampoline()); - case kOatAddressInterpreterToInterpreterBridge: + case StubType::kInterpreterToInterpreterBridge: return static_cast(header.GetInterpreterToInterpreterBridge()); - case kOatAddressInterpreterToCompiledCodeBridge: + case StubType::kInterpreterToCompiledCodeBridge: return static_cast(header.GetInterpreterToCompiledCodeBridge()); - case kOatAddressJNIDlsymLookup: + case StubType::kJNIDlsymLookup: return static_cast(header.GetJniDlsymLookup()); - case kOatAddressQuickIMTConflictTrampoline: + case StubType::kQuickIMTConflictTrampoline: return static_cast(header.GetQuickImtConflictTrampoline()); - case kOatAddressQuickResolutionTrampoline: + case StubType::kQuickResolutionTrampoline: return static_cast(header.GetQuickResolutionTrampoline()); - case kOatAddressQuickToInterpreterBridge: + case StubType::kQuickToInterpreterBridge: return static_cast(header.GetQuickToInterpreterBridge()); default: UNREACHABLE(); } } const ImageInfo& primary_image_info = GetImageInfo(0); - return GetOatAddressForOffset(primary_image_info.oat_address_offsets_[type], primary_image_info); + return GetOatAddressForOffset(primary_image_info.GetStubOffset(type), primary_image_info); } const uint8_t* ImageWriter::GetQuickCode(ArtMethod* method, @@ -2586,16 +2590,16 @@ const uint8_t* ImageWriter::GetQuickCode(ArtMethod* method, } else if (quick_code == nullptr && method->IsNative() && (!method->IsStatic() || method->GetDeclaringClass()->IsInitialized())) { // Non-static or initialized native method missing compiled code, use generic JNI version. - quick_code = GetOatAddress(kOatAddressQuickGenericJNITrampoline); + quick_code = GetOatAddress(StubType::kQuickGenericJNITrampoline); } else if (quick_code == nullptr && !method->IsNative()) { // We don't have code at all for a non-native method, use the interpreter. - quick_code = GetOatAddress(kOatAddressQuickToInterpreterBridge); + quick_code = GetOatAddress(StubType::kQuickToInterpreterBridge); *quick_is_interpreted = true; } else { CHECK(!method->GetDeclaringClass()->IsInitialized()); // We have code for a static method, but need to go through the resolution stub for class // initialization. - quick_code = GetOatAddress(kOatAddressQuickResolutionTrampoline); + quick_code = GetOatAddress(StubType::kQuickResolutionTrampoline); } if (!IsInBootOatFile(quick_code)) { // DCHECK_GE(quick_code, oat_data_begin_); @@ -2630,11 +2634,11 @@ void ImageWriter::CopyAndFixupMethod(ArtMethod* orig, if (orig_table != nullptr) { // Special IMT conflict method, normal IMT conflict method or unimplemented IMT method. copy->SetEntryPointFromQuickCompiledCodePtrSize( - GetOatAddress(kOatAddressQuickIMTConflictTrampoline), target_ptr_size_); + GetOatAddress(StubType::kQuickIMTConflictTrampoline), target_ptr_size_); copy->SetImtConflictTable(NativeLocationInImage(orig_table), target_ptr_size_); } else if (UNLIKELY(orig == runtime->GetResolutionMethod())) { copy->SetEntryPointFromQuickCompiledCodePtrSize( - GetOatAddress(kOatAddressQuickResolutionTrampoline), target_ptr_size_); + GetOatAddress(StubType::kQuickResolutionTrampoline), target_ptr_size_); } else { bool found_one = false; for (size_t i = 0; i < static_cast(CalleeSaveType::kLastCalleeSaveType); ++i) { @@ -2653,7 +2657,7 @@ void ImageWriter::CopyAndFixupMethod(ArtMethod* orig, // use results in an AbstractMethodError. We use the interpreter to achieve this. if (UNLIKELY(!orig->IsInvokable())) { copy->SetEntryPointFromQuickCompiledCodePtrSize( - GetOatAddress(kOatAddressQuickToInterpreterBridge), target_ptr_size_); + GetOatAddress(StubType::kQuickToInterpreterBridge), target_ptr_size_); } else { bool quick_is_interpreted; const uint8_t* quick_code = GetQuickCode(orig, image_info, &quick_is_interpreted); @@ -2664,17 +2668,17 @@ void ImageWriter::CopyAndFixupMethod(ArtMethod* orig, // The native method's pointer is set to a stub to lookup via dlsym. // Note this is not the code_ pointer, that is handled above. copy->SetEntryPointFromJniPtrSize( - GetOatAddress(kOatAddressJNIDlsymLookup), target_ptr_size_); + GetOatAddress(StubType::kJNIDlsymLookup), target_ptr_size_); } } } } -size_t ImageWriter::GetBinSizeSum(ImageWriter::ImageInfo& image_info, ImageWriter::Bin up_to) const { - DCHECK_LE(up_to, kBinSize); - return std::accumulate(&image_info.bin_slot_sizes_[0], - &image_info.bin_slot_sizes_[up_to], - /*init*/0); +size_t ImageWriter::ImageInfo::GetBinSizeSum(Bin up_to) const { + DCHECK_LE(static_cast(up_to), kNumberOfBins); + return std::accumulate(&bin_slot_sizes_[0], + &bin_slot_sizes_[0] + static_cast(up_to), + /*init*/ static_cast(0)); } ImageWriter::BinSlot::BinSlot(uint32_t lockword) : lockword_(lockword) { @@ -2683,7 +2687,7 @@ ImageWriter::BinSlot::BinSlot(uint32_t lockword) : lockword_(lockword) { static_assert(kBinShift == 27, "wrong number of shift"); static_assert(sizeof(BinSlot) == sizeof(LockWord), "BinSlot/LockWord must have equal sizes"); - DCHECK_LT(GetBin(), kBinSize); + DCHECK_LT(GetBin(), Bin::kMirrorCount); DCHECK_ALIGNED(GetIndex(), kObjectAlignment); } @@ -2702,23 +2706,23 @@ uint32_t ImageWriter::BinSlot::GetIndex() const { ImageWriter::Bin ImageWriter::BinTypeForNativeRelocationType(NativeObjectRelocationType type) { switch (type) { - case kNativeObjectRelocationTypeArtField: - case kNativeObjectRelocationTypeArtFieldArray: - return kBinArtField; - case kNativeObjectRelocationTypeArtMethodClean: - case kNativeObjectRelocationTypeArtMethodArrayClean: - return kBinArtMethodClean; - case kNativeObjectRelocationTypeArtMethodDirty: - case kNativeObjectRelocationTypeArtMethodArrayDirty: - return kBinArtMethodDirty; - case kNativeObjectRelocationTypeDexCacheArray: - return kBinDexCacheArray; - case kNativeObjectRelocationTypeRuntimeMethod: - return kBinRuntimeMethod; - case kNativeObjectRelocationTypeIMTable: - return kBinImTable; - case kNativeObjectRelocationTypeIMTConflictTable: - return kBinIMTConflictTable; + case NativeObjectRelocationType::kArtField: + case NativeObjectRelocationType::kArtFieldArray: + return Bin::kArtField; + case NativeObjectRelocationType::kArtMethodClean: + case NativeObjectRelocationType::kArtMethodArrayClean: + return Bin::kArtMethodClean; + case NativeObjectRelocationType::kArtMethodDirty: + case NativeObjectRelocationType::kArtMethodArrayDirty: + return Bin::kArtMethodDirty; + case NativeObjectRelocationType::kDexCacheArray: + return Bin::kDexCacheArray; + case NativeObjectRelocationType::kRuntimeMethod: + return Bin::kRuntimeMethod; + case NativeObjectRelocationType::kIMTable: + return Bin::kImTable; + case NativeObjectRelocationType::kIMTConflictTable: + return Bin::kIMTConflictTable; } UNREACHABLE(); } @@ -2782,20 +2786,20 @@ void ImageWriter::UpdateOatFileHeader(size_t oat_index, const OatHeader& oat_hea if (oat_index == GetDefaultOatIndex()) { // Primary oat file, read the trampolines. - cur_image_info.oat_address_offsets_[kOatAddressInterpreterToInterpreterBridge] = - oat_header.GetInterpreterToInterpreterBridgeOffset(); - cur_image_info.oat_address_offsets_[kOatAddressInterpreterToCompiledCodeBridge] = - oat_header.GetInterpreterToCompiledCodeBridgeOffset(); - cur_image_info.oat_address_offsets_[kOatAddressJNIDlsymLookup] = - oat_header.GetJniDlsymLookupOffset(); - cur_image_info.oat_address_offsets_[kOatAddressQuickGenericJNITrampoline] = - oat_header.GetQuickGenericJniTrampolineOffset(); - cur_image_info.oat_address_offsets_[kOatAddressQuickIMTConflictTrampoline] = - oat_header.GetQuickImtConflictTrampolineOffset(); - cur_image_info.oat_address_offsets_[kOatAddressQuickResolutionTrampoline] = - oat_header.GetQuickResolutionTrampolineOffset(); - cur_image_info.oat_address_offsets_[kOatAddressQuickToInterpreterBridge] = - oat_header.GetQuickToInterpreterBridgeOffset(); + cur_image_info.SetStubOffset(StubType::kInterpreterToInterpreterBridge, + oat_header.GetInterpreterToInterpreterBridgeOffset()); + cur_image_info.SetStubOffset(StubType::kInterpreterToCompiledCodeBridge, + oat_header.GetInterpreterToCompiledCodeBridgeOffset()); + cur_image_info.SetStubOffset(StubType::kJNIDlsymLookup, + oat_header.GetJniDlsymLookupOffset()); + cur_image_info.SetStubOffset(StubType::kQuickGenericJNITrampoline, + oat_header.GetQuickGenericJniTrampolineOffset()); + cur_image_info.SetStubOffset(StubType::kQuickIMTConflictTrampoline, + oat_header.GetQuickImtConflictTrampolineOffset()); + cur_image_info.SetStubOffset(StubType::kQuickResolutionTrampoline, + oat_header.GetQuickResolutionTrampolineOffset()); + cur_image_info.SetStubOffset(StubType::kQuickToInterpreterBridge, + oat_header.GetQuickToInterpreterBridgeOffset()); } } diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h index 68c7b594fb94b296a198fe465f4af131bda4575a..3aceceb80c288dbf4b1243ecc0f5ec361662f0f9 100644 --- a/dex2oat/linker/image_writer.h +++ b/dex2oat/linker/image_writer.h @@ -161,70 +161,70 @@ class ImageWriter FINAL { // Classify different kinds of bins that objects end up getting packed into during image writing. // Ordered from dirtiest to cleanest (until ArtMethods). - enum Bin { - kBinKnownDirty, // Known dirty objects from --dirty-image-objects list - kBinMiscDirty, // Dex caches, object locks, etc... - kBinClassVerified, // Class verified, but initializers haven't been run + enum class Bin { + kKnownDirty, // Known dirty objects from --dirty-image-objects list + kMiscDirty, // Dex caches, object locks, etc... + kClassVerified, // Class verified, but initializers haven't been run // Unknown mix of clean/dirty: - kBinRegular, - kBinClassInitialized, // Class initializers have been run + kRegular, + kClassInitialized, // Class initializers have been run // All classes get their own bins since their fields often dirty - kBinClassInitializedFinalStatics, // Class initializers have been run, no non-final statics + kClassInitializedFinalStatics, // Class initializers have been run, no non-final statics // Likely-clean: - kBinString, // [String] Almost always immutable (except for obj header). + kString, // [String] Almost always immutable (except for obj header). // Add more bins here if we add more segregation code. // Non mirror fields must be below. // ArtFields should be always clean. - kBinArtField, + kArtField, // If the class is initialized, then the ArtMethods are probably clean. - kBinArtMethodClean, + kArtMethodClean, // ArtMethods may be dirty if the class has native methods or a declaring class that isn't // initialized. - kBinArtMethodDirty, + kArtMethodDirty, // IMT (clean) - kBinImTable, + kImTable, // Conflict tables (clean). - kBinIMTConflictTable, + kIMTConflictTable, // Runtime methods (always clean, do not have a length prefix array). - kBinRuntimeMethod, + kRuntimeMethod, // Dex cache arrays have a special slot for PC-relative addressing. Since they are // huge, and as such their dirtiness is not important for the clean/dirty separation, // we arbitrarily keep them at the end of the native data. - kBinDexCacheArray, // Arrays belonging to dex cache. - kBinSize, + kDexCacheArray, // Arrays belonging to dex cache. + kLast = kDexCacheArray, // Number of bins which are for mirror objects. - kBinMirrorCount = kBinArtField, + kMirrorCount = kArtField, }; friend std::ostream& operator<<(std::ostream& stream, const Bin& bin); - enum NativeObjectRelocationType { - kNativeObjectRelocationTypeArtField, - kNativeObjectRelocationTypeArtFieldArray, - kNativeObjectRelocationTypeArtMethodClean, - kNativeObjectRelocationTypeArtMethodArrayClean, - kNativeObjectRelocationTypeArtMethodDirty, - kNativeObjectRelocationTypeArtMethodArrayDirty, - kNativeObjectRelocationTypeRuntimeMethod, - kNativeObjectRelocationTypeIMTable, - kNativeObjectRelocationTypeIMTConflictTable, - kNativeObjectRelocationTypeDexCacheArray, + enum class NativeObjectRelocationType { + kArtField, + kArtFieldArray, + kArtMethodClean, + kArtMethodArrayClean, + kArtMethodDirty, + kArtMethodArrayDirty, + kRuntimeMethod, + kIMTable, + kIMTConflictTable, + kDexCacheArray, }; friend std::ostream& operator<<(std::ostream& stream, const NativeObjectRelocationType& type); - enum OatAddress { - kOatAddressInterpreterToInterpreterBridge, - kOatAddressInterpreterToCompiledCodeBridge, - kOatAddressJNIDlsymLookup, - kOatAddressQuickGenericJNITrampoline, - kOatAddressQuickIMTConflictTrampoline, - kOatAddressQuickResolutionTrampoline, - kOatAddressQuickToInterpreterBridge, - // Number of elements in the enum. - kOatAddressCount, + enum class StubType { + kInterpreterToInterpreterBridge, + kInterpreterToCompiledCodeBridge, + kJNIDlsymLookup, + kQuickGenericJNITrampoline, + kQuickIMTConflictTrampoline, + kQuickResolutionTrampoline, + kQuickToInterpreterBridge, + kLast = kQuickToInterpreterBridge, }; - friend std::ostream& operator<<(std::ostream& stream, const OatAddress& oat_address); + friend std::ostream& operator<<(std::ostream& stream, const StubType& stub_type); - static constexpr size_t kBinBits = MinimumBitsToStore(kBinMirrorCount - 1); + static constexpr size_t kBinBits = + MinimumBitsToStore(static_cast(Bin::kMirrorCount) - 1); // uint32 = typeof(lockword_) // Subtract read barrier bits since we want these to remain 0, or else it may result in DCHECK // failures due to invalid read barrier bits during object field reads. @@ -232,6 +232,12 @@ class ImageWriter FINAL { // 111000.....0 static const size_t kBinMask = ((static_cast(1) << kBinBits) - 1) << kBinShift; + // Number of bins, including non-mirror bins. + static constexpr size_t kNumberOfBins = static_cast(Bin::kLast) + 1u; + + // Number of stub types. + static constexpr size_t kNumberOfStubTypes = static_cast(StubType::kLast) + 1u; + // We use the lock word to store the bin # and bin index of the object in the image. // // The struct size must be exactly sizeof(LockWord), currently 32-bits, since this will end up @@ -262,6 +268,39 @@ class ImageWriter FINAL { // excluding the bitmap. size_t CreateImageSections(ImageSection* out_sections, bool app_image) const; + size_t GetStubOffset(StubType stub_type) const { + DCHECK_LT(static_cast(stub_type), kNumberOfStubTypes); + return stub_offsets_[static_cast(stub_type)]; + } + + void SetStubOffset(StubType stub_type, size_t offset) { + DCHECK_LT(static_cast(stub_type), kNumberOfStubTypes); + stub_offsets_[static_cast(stub_type)] = offset; + } + + size_t GetBinSlotOffset(Bin bin) const { + DCHECK_LT(static_cast(bin), kNumberOfBins); + return bin_slot_offsets_[static_cast(bin)]; + } + + void IncrementBinSlotSize(Bin bin, size_t size_to_add) { + DCHECK_LT(static_cast(bin), kNumberOfBins); + bin_slot_sizes_[static_cast(bin)] += size_to_add; + } + + size_t GetBinSlotSize(Bin bin) const { + DCHECK_LT(static_cast(bin), kNumberOfBins); + return bin_slot_sizes_[static_cast(bin)]; + } + + void IncrementBinSlotCount(Bin bin, size_t count_to_add) { + DCHECK_LT(static_cast(bin), kNumberOfBins); + bin_slot_count_[static_cast(bin)] += count_to_add; + } + + // Calculate the sum total of the bin slot sizes in [0, up_to). Defaults to all bins. + size_t GetBinSizeSum(Bin up_to) const; + std::unique_ptr image_; // Memory mapped for generating the image. // Target begin of this image. Notes: It is not valid to write here, this is the address @@ -300,12 +339,12 @@ class ImageWriter FINAL { SafeMap dex_cache_array_starts_; // Offset from oat_data_begin_ to the stubs. - uint32_t oat_address_offsets_[kOatAddressCount] = {}; + uint32_t stub_offsets_[kNumberOfStubTypes] = {}; // Bin slot tracking for dirty object packing. - size_t bin_slot_sizes_[kBinSize] = {}; // Number of bytes in a bin. - size_t bin_slot_offsets_[kBinSize] = {}; // Number of bytes in previous bins. - size_t bin_slot_count_[kBinSize] = {}; // Number of objects in a bin. + size_t bin_slot_sizes_[kNumberOfBins] = {}; // Number of bytes in a bin. + size_t bin_slot_offsets_[kNumberOfBins] = {}; // Number of bytes in previous bins. + size_t bin_slot_count_[kNumberOfBins] = {}; // Number of objects in a bin. // Cached size of the intern table for when we allocate memory. size_t intern_table_bytes_ = 0; @@ -367,7 +406,7 @@ class ImageWriter FINAL { } // Returns the address in the boot image if we are compiling the app image. - const uint8_t* GetOatAddress(OatAddress type) const; + const uint8_t* GetOatAddress(StubType type) const; const uint8_t* GetOatAddressForOffset(uint32_t offset, const ImageInfo& image_info) const { // With Quick, code is within the OatFile, as there are all in one @@ -443,9 +482,6 @@ class ImageWriter FINAL { bool* quick_is_interpreted) REQUIRES_SHARED(Locks::mutator_lock_); - // Calculate the sum total of the bin slot sizes in [0, up_to). Defaults to all bins. - size_t GetBinSizeSum(ImageInfo& image_info, Bin up_to = kBinSize) const; - // Return true if a method is likely to be dirtied at runtime. bool WillMethodBeDirty(ArtMethod* m) const REQUIRES_SHARED(Locks::mutator_lock_); @@ -572,9 +608,9 @@ class ImageWriter FINAL { NativeObjectRelocationType type; bool IsArtMethodRelocation() const { - return type == kNativeObjectRelocationTypeArtMethodClean || - type == kNativeObjectRelocationTypeArtMethodDirty || - type == kNativeObjectRelocationTypeRuntimeMethod; + return type == NativeObjectRelocationType::kArtMethodClean || + type == NativeObjectRelocationType::kArtMethodDirty || + type == NativeObjectRelocationType::kRuntimeMethod; } }; std::unordered_map native_object_relocations_; diff --git a/dex2oat/linker/index_bss_mapping_encoder.h b/dex2oat/linker/index_bss_mapping_encoder.h new file mode 100644 index 0000000000000000000000000000000000000000..c6326ed1cfb90dcfc777b68da17da2ab93ffba3e --- /dev/null +++ b/dex2oat/linker/index_bss_mapping_encoder.h @@ -0,0 +1,87 @@ +/* + * 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_DEX2OAT_LINKER_INDEX_BSS_MAPPING_ENCODER_H_ +#define ART_DEX2OAT_LINKER_INDEX_BSS_MAPPING_ENCODER_H_ + +#include + +#include "base/bit_utils.h" +#include "base/bit_vector-inl.h" +#include "index_bss_mapping.h" + +namespace art { +namespace linker { + +// Helper class for encoding compressed IndexBssMapping. +class IndexBssMappingEncoder { + public: + IndexBssMappingEncoder(size_t number_of_indexes, size_t slot_size) + : index_bits_(IndexBssMappingEntry::IndexBits(number_of_indexes)), + slot_size_(slot_size) { + entry_.index_and_mask = static_cast(-1); + entry_.bss_offset = static_cast(-1); + DCHECK_NE(number_of_indexes, 0u); + } + + // Try to merge the next index -> bss_offset mapping into the current entry. + // Return true on success, false on failure. + bool TryMerge(uint32_t index, uint32_t bss_offset) { + DCHECK_LE(MinimumBitsToStore(index), index_bits_); + DCHECK_NE(index, entry_.GetIndex(index_bits_)); + if (entry_.bss_offset + slot_size_ != bss_offset) { + return false; + } + uint32_t diff = index - entry_.GetIndex(index_bits_); + if (diff > 32u - index_bits_) { + return false; + } + uint32_t mask = entry_.GetMask(index_bits_); + if ((mask & ~(static_cast(-1) << diff)) != 0u) { + return false; + } + // Insert the bit indicating the index we've just overwritten + // and shift bits indicating indexes before that. + mask = ((mask << index_bits_) >> diff) | (static_cast(1u) << (32 - diff)); + entry_.index_and_mask = mask | index; + entry_.bss_offset = bss_offset; + return true; + } + + void Reset(uint32_t method_index, uint32_t bss_offset) { + DCHECK_LE(MinimumBitsToStore(method_index), index_bits_); + entry_.index_and_mask = method_index; // Mask bits set to 0. + entry_.bss_offset = bss_offset; + } + + IndexBssMappingEntry GetEntry() { + return entry_; + } + + size_t GetIndexBits() const { + return index_bits_; + } + + private: + const size_t index_bits_; + const size_t slot_size_; + IndexBssMappingEntry entry_; +}; + +} // namespace linker +} // namespace art + +#endif // ART_DEX2OAT_LINKER_INDEX_BSS_MAPPING_ENCODER_H_ diff --git a/dex2oat/linker/index_bss_mapping_encoder_test.cc b/dex2oat/linker/index_bss_mapping_encoder_test.cc new file mode 100644 index 0000000000000000000000000000000000000000..d7ca2a5742aa6dc8f6a43d495744af94e9d26304 --- /dev/null +++ b/dex2oat/linker/index_bss_mapping_encoder_test.cc @@ -0,0 +1,104 @@ +/* + * 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 "index_bss_mapping_encoder.h" + +#include "base/enums.h" +#include "gtest/gtest.h" + +namespace art { +namespace linker { + +TEST(IndexBssMappingEncoder, TryMerge16BitIndex) { + for (PointerSize pointer_size : {PointerSize::k32, PointerSize::k64}) { + size_t raw_pointer_size = static_cast(pointer_size); + IndexBssMappingEncoder encoder(/* number_of_indexes */ 0x10000, raw_pointer_size); + encoder.Reset(1u, 0u); + ASSERT_FALSE(encoder.TryMerge(5u, raw_pointer_size + 1)); // Wrong bss_offset difference. + ASSERT_FALSE(encoder.TryMerge(18u, raw_pointer_size)); // Index out of range. + ASSERT_TRUE(encoder.TryMerge(5u, raw_pointer_size)); + ASSERT_EQ(0u, encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 1u, raw_pointer_size)); + ASSERT_EQ(raw_pointer_size, + encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 5u, raw_pointer_size)); + ASSERT_EQ(IndexBssMappingLookup::npos, + encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 17u, raw_pointer_size)); + ASSERT_FALSE(encoder.TryMerge(17u, 2 * raw_pointer_size + 1)); // Wrong bss_offset difference. + ASSERT_FALSE(encoder.TryMerge(18u, 2 * raw_pointer_size)); // Index out of range. + ASSERT_TRUE(encoder.TryMerge(17u, 2 * raw_pointer_size)); + ASSERT_EQ(0u, encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 1u, raw_pointer_size)); + ASSERT_EQ(raw_pointer_size, + encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 5u, raw_pointer_size)); + ASSERT_EQ(2 * raw_pointer_size, + encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 17u, raw_pointer_size)); + ASSERT_EQ(0x00110000u | 17u, encoder.GetEntry().index_and_mask); + ASSERT_FALSE(encoder.TryMerge(18u, 3 * raw_pointer_size)); // Index out of range. + } +} + +TEST(IndexBssMappingEncoder, TryMerge8BitIndex) { + for (PointerSize pointer_size : {PointerSize::k32, PointerSize::k64}) { + size_t raw_pointer_size = static_cast(pointer_size); + IndexBssMappingEncoder encoder(/* number_of_indexes */ 0x100, raw_pointer_size); + encoder.Reset(1u, 0u); + ASSERT_FALSE(encoder.TryMerge(5u, raw_pointer_size + 1)); // Wrong bss_offset difference. + ASSERT_FALSE(encoder.TryMerge(26u, raw_pointer_size)); // Index out of range. + ASSERT_TRUE(encoder.TryMerge(5u, raw_pointer_size)); + ASSERT_EQ(0u, encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 1u, raw_pointer_size)); + ASSERT_EQ(raw_pointer_size, + encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 5u, raw_pointer_size)); + ASSERT_EQ(IndexBssMappingLookup::npos, + encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 17u, raw_pointer_size)); + ASSERT_FALSE(encoder.TryMerge(25u, 2 * raw_pointer_size + 1)); // Wrong bss_offset difference. + ASSERT_FALSE(encoder.TryMerge(26u, 2 * raw_pointer_size)); // Index out of range. + ASSERT_TRUE(encoder.TryMerge(25u, 2 * raw_pointer_size)); + ASSERT_EQ(0u, encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 1u, raw_pointer_size)); + ASSERT_EQ(raw_pointer_size, + encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 5u, raw_pointer_size)); + ASSERT_EQ(2 * raw_pointer_size, + encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 25u, raw_pointer_size)); + ASSERT_EQ(0x00001100u | 25u, encoder.GetEntry().index_and_mask); + ASSERT_FALSE(encoder.TryMerge(26u, 3 * raw_pointer_size)); // Index out of range. + } +} + +TEST(IndexBssMappingEncoder, TryMerge20BitIndex) { + for (PointerSize pointer_size : {PointerSize::k32, PointerSize::k64}) { + size_t raw_pointer_size = static_cast(pointer_size); + IndexBssMappingEncoder encoder(/* number_of_indexes */ 0x100000, raw_pointer_size); + encoder.Reset(1u, 0u); + ASSERT_FALSE(encoder.TryMerge(5u, raw_pointer_size + 1)); // Wrong bss_offset difference. + ASSERT_FALSE(encoder.TryMerge(14u, raw_pointer_size)); // Index out of range. + ASSERT_TRUE(encoder.TryMerge(5u, raw_pointer_size)); + ASSERT_EQ(0u, encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 1u, raw_pointer_size)); + ASSERT_EQ(raw_pointer_size, + encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 5u, raw_pointer_size)); + ASSERT_EQ(IndexBssMappingLookup::npos, + encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 17u, raw_pointer_size)); + ASSERT_FALSE(encoder.TryMerge(13u, 2 * raw_pointer_size + 1)); // Wrong bss_offset difference. + ASSERT_FALSE(encoder.TryMerge(14u, 2 * raw_pointer_size)); // Index out of range. + ASSERT_TRUE(encoder.TryMerge(13u, 2 * raw_pointer_size)); + ASSERT_EQ(0u, encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 1u, raw_pointer_size)); + ASSERT_EQ(raw_pointer_size, + encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 5u, raw_pointer_size)); + ASSERT_EQ(2 * raw_pointer_size, + encoder.GetEntry().GetBssOffset(encoder.GetIndexBits(), 13u, raw_pointer_size)); + ASSERT_EQ(0x01100000u | 13u, encoder.GetEntry().index_and_mask); + ASSERT_FALSE(encoder.TryMerge(14u, 3 * raw_pointer_size)); // Index out of range. + } +} + +} // namespace linker +} // namespace art diff --git a/dex2oat/linker/multi_oat_relative_patcher.cc b/dex2oat/linker/multi_oat_relative_patcher.cc index 178a78f0bbba3cb18d11de9865295fb8018b9c81..1abaf7dfd1831dbf0d5bbe9e478561248a04bf77 100644 --- a/dex2oat/linker/multi_oat_relative_patcher.cc +++ b/dex2oat/linker/multi_oat_relative_patcher.cc @@ -16,8 +16,9 @@ #include "multi_oat_relative_patcher.h" +#include + #include "base/bit_utils.h" -#include "base/logging.h" #include "globals.h" namespace art { diff --git a/dex2oat/linker/multi_oat_relative_patcher.h b/dex2oat/linker/multi_oat_relative_patcher.h index 6683366467cdc123c8f1561012e54c084b13e192..d97be8d3e3015c76533770dce4aceed6775b4b07 100644 --- a/dex2oat/linker/multi_oat_relative_patcher.h +++ b/dex2oat/linker/multi_oat_relative_patcher.h @@ -26,7 +26,6 @@ namespace art { class CompiledMethod; -class LinkerPatch; class InstructionSetFeatures; namespace linker { diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index d8671d2dd0aa46b7b487f943cecb05fe77468d6b..d261679ac17cd4837e1b6ce32d8d460cdd81e264 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -26,6 +26,7 @@ #include "base/bit_vector-inl.h" #include "base/enums.h" #include "base/file_magic.h" +#include "base/logging.h" // For VLOG #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" @@ -45,8 +46,8 @@ #include "image_writer.h" #include "linker/buffered_output_stream.h" #include "linker/file_output_stream.h" +#include "linker/index_bss_mapping_encoder.h" #include "linker/linker_patch.h" -#include "linker/method_bss_mapping_encoder.h" #include "linker/multi_oat_relative_patcher.h" #include "linker/output_stream.h" #include "mirror/array.h" @@ -274,7 +275,9 @@ class OatWriter::OatDexFile { public: OatDexFile(const char* dex_file_location, DexFileSource source, - CreateTypeLookupTable create_type_lookup_table); + CreateTypeLookupTable create_type_lookup_table, + uint32_t dex_file_location_checksun, + size_t dex_file_size); OatDexFile(OatDexFile&& src) = default; const char* GetLocation() const { @@ -295,29 +298,47 @@ class OatWriter::OatDexFile { // Whether to create the type lookup table. CreateTypeLookupTable create_type_lookup_table_; - // Dex file size. Initialized when writing the dex file. + // Dex file size. Passed in the constructor, but could be + // overwritten by LayoutAndWriteDexFile. size_t dex_file_size_; // Offset of start of OatDexFile from beginning of OatHeader. It is // used to validate file position when writing. size_t offset_; - // Data to write. - uint32_t dex_file_location_size_; - const char* dex_file_location_data_; - uint32_t dex_file_location_checksum_; + ///// Start of data to write to vdex/oat file. + + const uint32_t dex_file_location_size_; + const char* const dex_file_location_data_; + + // The checksum of the dex file. + const uint32_t dex_file_location_checksum_; + + // Offset of the dex file in the vdex file. Set when writing dex files in + // SeekToDexFile. uint32_t dex_file_offset_; - uint32_t class_offsets_offset_; + + // The lookup table offset in the oat file. Set in WriteTypeLookupTables. uint32_t lookup_table_offset_; + + // Class and BSS offsets set in PrepareLayout. + uint32_t class_offsets_offset_; uint32_t method_bss_mapping_offset_; + uint32_t type_bss_mapping_offset_; + uint32_t string_bss_mapping_offset_; + + // Offset of dex sections that will have different runtime madvise states. + // Set in WriteDexLayoutSections. uint32_t dex_sections_layout_offset_; - // Data to write to a separate section. + // Data to write to a separate section. We set the length + // of the vector in OpenDexFiles. dchecked_vector class_offsets_; // Dex section layout info to serialize. DexLayoutSections dex_sections_layout_; + ///// End of data to write to vdex/oat file. private: DISALLOW_COPY_AND_ASSIGN(OatDexFile); }; @@ -396,6 +417,8 @@ OatWriter::OatWriter(bool compiling_boot_image, size_oat_dex_file_dex_layout_sections_(0), size_oat_dex_file_dex_layout_sections_alignment_(0), size_oat_dex_file_method_bss_mapping_offset_(0), + size_oat_dex_file_type_bss_mapping_offset_(0), + size_oat_dex_file_string_bss_mapping_offset_(0), size_oat_lookup_table_alignment_(0), size_oat_lookup_table_(0), size_oat_class_offsets_alignment_(0), @@ -405,12 +428,49 @@ OatWriter::OatWriter(bool compiling_boot_image, size_oat_class_method_bitmaps_(0), size_oat_class_method_offsets_(0), size_method_bss_mappings_(0u), + size_type_bss_mappings_(0u), + size_string_bss_mappings_(0u), relative_patcher_(nullptr), absolute_patch_locations_(), profile_compilation_info_(info), compact_dex_level_(compact_dex_level) { } +static bool ValidateDexFileHeader(const uint8_t* raw_header, const char* location) { + const bool valid_standard_dex_magic = DexFileLoader::IsMagicValid(raw_header); + if (!valid_standard_dex_magic) { + LOG(ERROR) << "Invalid magic number in dex file header. " << " File: " << location; + return false; + } + if (!DexFileLoader::IsVersionAndMagicValid(raw_header)) { + LOG(ERROR) << "Invalid version number in dex file header. " << " File: " << location; + return false; + } + const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_header); + if (header->file_size_ < sizeof(DexFile::Header)) { + LOG(ERROR) << "Dex file header specifies file size insufficient to contain the header." + << " File: " << location; + return false; + } + return true; +} + +static const UnalignedDexFileHeader* GetDexFileHeader(File* file, + uint8_t* raw_header, + const char* location) { + // Read the dex file header and perform minimal verification. + if (!file->ReadFully(raw_header, sizeof(DexFile::Header))) { + PLOG(ERROR) << "Failed to read dex file header. Actual: " + << " File: " << location << " Output: " << file->GetPath(); + return nullptr; + } + if (!ValidateDexFileHeader(raw_header, location)) { + return nullptr; + } + + return AsUnalignedDexFileHeader(raw_header); +} + bool OatWriter::AddDexFileSource(const char* filename, const char* location, CreateTypeLookupTable create_type_lookup_table) { @@ -422,12 +482,20 @@ bool OatWriter::AddDexFileSource(const char* filename, PLOG(ERROR) << "Failed to read magic number from dex file: '" << filename << "'"; return false; } else if (DexFileLoader::IsMagicValid(magic)) { + uint8_t raw_header[sizeof(DexFile::Header)]; + const UnalignedDexFileHeader* header = GetDexFileHeader(&fd, raw_header, location); + if (header == nullptr) { + return false; + } // The file is open for reading, not writing, so it's OK to let the File destructor // close it without checking for explicit Close(), so pass checkUsage = false. raw_dex_files_.emplace_back(new File(fd.Release(), location, /* checkUsage */ false)); - oat_dex_files_.emplace_back(location, - DexFileSource(raw_dex_files_.back().get()), - create_type_lookup_table); + oat_dex_files_.emplace_back(/* OatDexFile */ + location, + DexFileSource(raw_dex_files_.back().get()), + create_type_lookup_table, + header->checksum_, + header->file_size_); } else if (IsZipMagic(magic)) { if (!AddZippedDexFilesSource(std::move(fd), location, create_type_lookup_table)) { return false; @@ -461,9 +529,13 @@ bool OatWriter::AddZippedDexFilesSource(File&& zip_fd, zipped_dex_files_.push_back(std::move(entry)); zipped_dex_file_locations_.push_back(DexFileLoader::GetMultiDexLocation(i, location)); const char* full_location = zipped_dex_file_locations_.back().c_str(); - oat_dex_files_.emplace_back(full_location, - DexFileSource(zipped_dex_files_.back().get()), - create_type_lookup_table); + // We override the checksum from header with the CRC from ZIP entry. + oat_dex_files_.emplace_back(/* OatDexFile */ + full_location, + DexFileSource(zipped_dex_files_.back().get()), + create_type_lookup_table, + zipped_dex_files_.back()->GetCrc32(), + zipped_dex_files_.back()->GetUncompressedLength()); } if (zipped_dex_file_locations_.empty()) { LOG(ERROR) << "No dex files in zip file '" << location << "': " << error_msg; @@ -492,10 +564,13 @@ bool OatWriter::AddVdexDexFilesSource(const VdexFile& vdex_file, // We used `zipped_dex_file_locations_` to keep the strings in memory. zipped_dex_file_locations_.push_back(DexFileLoader::GetMultiDexLocation(i, location)); const char* full_location = zipped_dex_file_locations_.back().c_str(); - oat_dex_files_.emplace_back(full_location, - DexFileSource(current_dex_data), - create_type_lookup_table); - oat_dex_files_.back().dex_file_location_checksum_ = vdex_file.GetLocationChecksum(i); + const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(current_dex_data); + oat_dex_files_.emplace_back(/* OatDexFile */ + full_location, + DexFileSource(current_dex_data), + create_type_lookup_table, + vdex_file.GetLocationChecksum(i), + header->file_size_); } if (vdex_file.GetNextDexFileData(current_dex_data) != nullptr) { @@ -531,8 +606,12 @@ bool OatWriter::AddRawDexFileSource(const ArrayRef& data, return false; } - oat_dex_files_.emplace_back(location, DexFileSource(data.data()), create_type_lookup_table); - oat_dex_files_.back().dex_file_location_checksum_ = location_checksum; + oat_dex_files_.emplace_back(/* OatDexFile */ + location, + DexFileSource(data.data()), + create_type_lookup_table, + location_checksum, + header->file_size_); return true; } @@ -571,10 +650,9 @@ bool OatWriter::WriteAndOpenDexFiles( std::vector> dex_files; // Initialize VDEX and OAT headers. - if (kIsVdexEnabled) { - // Reserve space for Vdex header and checksums. - vdex_size_ = sizeof(VdexFile::Header) + oat_dex_files_.size() * sizeof(VdexFile::VdexChecksum); - } + + // Reserve space for Vdex header and checksums. + vdex_size_ = sizeof(VdexFile::Header) + oat_dex_files_.size() * sizeof(VdexFile::VdexChecksum); oat_size_ = InitOatHeader(instruction_set, instruction_set_features, dchecked_integral_cast(oat_dex_files_.size()), @@ -582,28 +660,12 @@ bool OatWriter::WriteAndOpenDexFiles( ChecksumUpdatingOutputStream checksum_updating_rodata(oat_rodata, oat_header_.get()); - if (kIsVdexEnabled) { - std::unique_ptr vdex_out = - std::make_unique(std::make_unique(vdex_file)); - // Write DEX files into VDEX, mmap and open them. - if (!WriteDexFiles(vdex_out.get(), vdex_file, update_input_vdex) || - !OpenDexFiles(vdex_file, verify, &dex_files_map, &dex_files)) { - return false; - } - } else { - DCHECK(!update_input_vdex); - // Write DEX files into OAT, mmap and open them. - if (!WriteDexFiles(oat_rodata, vdex_file, update_input_vdex) || - !OpenDexFiles(vdex_file, verify, &dex_files_map, &dex_files)) { - return false; - } - - // Do a bulk checksum update for Dex[]. Doing it piece by piece would be - // difficult because we're not using the OutputStream directly. - if (!oat_dex_files_.empty()) { - size_t size = oat_size_ - oat_dex_files_[0].dex_file_offset_; - oat_header_->UpdateChecksum(dex_files_map->Begin(), size); - } + std::unique_ptr vdex_out = + std::make_unique(std::make_unique(vdex_file)); + // Write DEX files into VDEX, mmap and open them. + if (!WriteDexFiles(vdex_out.get(), vdex_file, update_input_vdex) || + !OpenDexFiles(vdex_file, verify, &dex_files_map, &dex_files)) { + return false; } // Write type lookup tables into the oat file. @@ -649,8 +711,8 @@ void OatWriter::PrepareLayout(MultiOatRelativePatcher* relative_patcher) { offset = InitOatClasses(offset); } { - TimingLogger::ScopedTiming split("InitMethodBssMappings", timings_); - offset = InitMethodBssMappings(offset); + TimingLogger::ScopedTiming split("InitIndexBssMappings", timings_); + offset = InitIndexBssMappings(offset); } { TimingLogger::ScopedTiming split("InitOatMaps", timings_); @@ -755,13 +817,12 @@ class OatWriter::OatDexMethodVisitor : public DexMethodVisitor { }; static bool HasCompiledCode(const CompiledMethod* method) { - // The dextodexcompiler puts the quickening info table into the CompiledMethod - // for simplicity. For such methods, we will emit an OatQuickMethodHeader - // only when vdex is disabled. - return method != nullptr && (!method->GetQuickCode().empty() || !kIsVdexEnabled); + return method != nullptr && !method->GetQuickCode().empty(); } static bool HasQuickeningInfo(const CompiledMethod* method) { + // The dextodexcompiler puts the quickening info table into the CompiledMethod + // for simplicity. return method != nullptr && method->GetQuickCode().empty() && !method->GetVmapTable().empty(); } @@ -779,23 +840,22 @@ class OatWriter::InitBssLayoutMethodVisitor : public DexMethodVisitor { for (const LinkerPatch& patch : compiled_method->GetPatches()) { if (patch.GetType() == LinkerPatch::Type::kMethodBssEntry) { MethodReference target_method = patch.TargetMethod(); - auto refs_it = writer_->bss_method_entry_references_.find(target_method.dex_file); - if (refs_it == writer_->bss_method_entry_references_.end()) { - refs_it = writer_->bss_method_entry_references_.Put( - target_method.dex_file, - BitVector(target_method.dex_file->NumMethodIds(), - /* expandable */ false, - Allocator::GetMallocAllocator())); - refs_it->second.ClearAllBits(); - } - refs_it->second.SetBit(target_method.index); + AddBssReference(target_method, + target_method.dex_file->NumMethodIds(), + &writer_->bss_method_entry_references_); writer_->bss_method_entries_.Overwrite(target_method, /* placeholder */ 0u); } else if (patch.GetType() == LinkerPatch::Type::kTypeBssEntry) { - TypeReference ref(patch.TargetTypeDexFile(), patch.TargetTypeIndex()); - writer_->bss_type_entries_.Overwrite(ref, /* placeholder */ 0u); + TypeReference target_type(patch.TargetTypeDexFile(), patch.TargetTypeIndex()); + AddBssReference(target_type, + target_type.dex_file->NumTypeIds(), + &writer_->bss_type_entry_references_); + writer_->bss_type_entries_.Overwrite(target_type, /* placeholder */ 0u); } else if (patch.GetType() == LinkerPatch::Type::kStringBssEntry) { - StringReference ref(patch.TargetStringDexFile(), patch.TargetStringIndex()); - writer_->bss_string_entries_.Overwrite(ref, /* placeholder */ 0u); + StringReference target_string(patch.TargetStringDexFile(), patch.TargetStringIndex()); + AddBssReference(target_string, + target_string.dex_file->NumStringIds(), + &writer_->bss_string_entry_references_); + writer_->bss_string_entries_.Overwrite(target_string, /* placeholder */ 0u); } else if (patch.GetType() == LinkerPatch::Type::kStringInternTable || patch.GetType() == LinkerPatch::Type::kTypeClassTable) { writer_->map_boot_image_tables_to_bss_ = true; @@ -806,6 +866,24 @@ class OatWriter::InitBssLayoutMethodVisitor : public DexMethodVisitor { } return true; } + + private: + void AddBssReference(const DexFileReference& ref, + size_t number_of_indexes, + /*inout*/ SafeMap* references) { + // We currently support inlining of throwing instructions only when they originate in the + // same dex file as the outer method. All .bss references are used by throwing instructions. + DCHECK_EQ(dex_file_, ref.dex_file); + + auto refs_it = references->find(ref.dex_file); + if (refs_it == references->end()) { + refs_it = references->Put( + ref.dex_file, + BitVector(number_of_indexes, /* expandable */ false, Allocator::GetMallocAllocator())); + refs_it->second.ClearAllBits(); + } + refs_it->second.SetBit(ref.index); + } }; class OatWriter::InitOatClassesMethodVisitor : public DexMethodVisitor { @@ -1161,7 +1239,6 @@ class OatWriter::LayoutReserveOffsetCodeMethodVisitor : public OrderedMethodVisi uint16_t method_offsets_index_ = method_data.method_offsets_index; size_t class_def_index = method_data.class_def_index; uint32_t access_flags = method_data.access_flags; - const DexFile::CodeItem* code_item = method_data.code_item; bool has_debug_info = method_data.HasDebugInfo(); size_t debug_info_idx = method_data.debug_info_idx; @@ -1214,23 +1291,17 @@ class OatWriter::LayoutReserveOffsetCodeMethodVisitor : public OrderedMethodVisi // The code offset was 0 when the mapping/vmap table offset was set, so it's set // to 0-offset and we need to adjust it by code_offset. uint32_t code_offset = quick_code_offset - thumb_offset; - if (!compiled_method->GetQuickCode().empty()) { - // If the code is compiled, we write the offset of the stack map relative - // to the code, - if (vmap_table_offset != 0u) { - vmap_table_offset += code_offset; - DCHECK_LT(vmap_table_offset, code_offset); - } - if (method_info_offset != 0u) { - method_info_offset += code_offset; - DCHECK_LT(method_info_offset, code_offset); - } - } else { - CHECK(!kIsVdexEnabled); - // We write the offset of the quickening info relative to the code. + CHECK(!compiled_method->GetQuickCode().empty()); + // If the code is compiled, we write the offset of the stack map relative + // to the code. + if (vmap_table_offset != 0u) { vmap_table_offset += code_offset; DCHECK_LT(vmap_table_offset, code_offset); } + if (method_info_offset != 0u) { + method_info_offset += code_offset; + DCHECK_LT(method_info_offset, code_offset); + } uint32_t frame_size_in_bytes = compiled_method->GetFrameSizeInBytes(); uint32_t core_spill_mask = compiled_method->GetCoreSpillMask(); uint32_t fp_spill_mask = compiled_method->GetFpSpillMask(); @@ -1268,7 +1339,8 @@ class OatWriter::LayoutReserveOffsetCodeMethodVisitor : public OrderedMethodVisi info.class_def_index = class_def_index; info.dex_method_index = method_ref.index; info.access_flags = access_flags; - info.code_item = code_item; + // For intrinsics emitted by codegen, the code has no relation to the original code item. + info.code_item = compiled_method->IsIntrinsic() ? nullptr : method_data.code_item; info.isa = compiled_method->GetInstructionSet(); info.deduped = deduped; info.is_native_debuggable = native_debuggable_; @@ -1327,6 +1399,9 @@ class OatWriter::LayoutReserveOffsetCodeMethodVisitor : public OrderedMethodVisi if (UNLIKELY(lhs->GetPatches().data() != rhs->GetPatches().data())) { return lhs->GetPatches().data() < rhs->GetPatches().data(); } + if (UNLIKELY(lhs->IsIntrinsic() != rhs->IsIntrinsic())) { + return rhs->IsIntrinsic(); + } return false; } }; @@ -1517,11 +1592,10 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { ScopedObjectAccessUnchecked soa(self); StackHandleScope<1> hs(self); method = class_linker_->ResolveMethod( - *dex_file_, it.GetMemberIndex(), hs.NewHandle(dex_cache), ScopedNullHandle(), - nullptr, + /* referrer */ nullptr, invoke_type); if (method == nullptr) { LOG(FATAL_WITHOUT_ABORT) << "Unexpected failure to resolve a method: " @@ -1877,20 +1951,21 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { : class_linker_->FindDexCache(Thread::Current(), *target_dex_file); } - mirror::Class* GetTargetType(const LinkerPatch& patch) REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr GetTargetType(const LinkerPatch& patch) + REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(writer_->HasImage()); ObjPtr dex_cache = GetDexCache(patch.TargetTypeDexFile()); ObjPtr type = - ClassLinker::LookupResolvedType(patch.TargetTypeIndex(), dex_cache, class_loader_); + class_linker_->LookupResolvedType(patch.TargetTypeIndex(), dex_cache, class_loader_); CHECK(type != nullptr); - return type.Ptr(); + return type; } - mirror::String* GetTargetString(const LinkerPatch& patch) REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr GetTargetString(const LinkerPatch& patch) + REQUIRES_SHARED(Locks::mutator_lock_) { ClassLinker* linker = Runtime::Current()->GetClassLinker(); - mirror::String* string = linker->LookupString(*patch.TargetStringDexFile(), - patch.TargetStringIndex(), - GetDexCache(patch.TargetStringDexFile())); + ObjPtr string = + linker->LookupString(patch.TargetStringIndex(), GetDexCache(patch.TargetStringDexFile())); DCHECK(string != nullptr); DCHECK(writer_->HasBootImage() || Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(string)); @@ -1906,13 +1981,14 @@ class OatWriter::WriteCodeMethodVisitor : public OrderedMethodVisitor { return static_cast(reinterpret_cast(method) - oat_data_begin); } - uint32_t GetTargetObjectOffset(mirror::Object* object) REQUIRES_SHARED(Locks::mutator_lock_) { + uint32_t GetTargetObjectOffset(ObjPtr object) + REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(writer_->HasBootImage()); - object = writer_->image_writer_->GetImageAddress(object); + object = writer_->image_writer_->GetImageAddress(object.Ptr()); size_t oat_index = writer_->image_writer_->GetOatIndexForDexFile(dex_file_); uintptr_t oat_data_begin = writer_->image_writer_->GetOatDataBegin(oat_index); // TODO: Clean up offset types. The target offset must be treated as signed. - return static_cast(reinterpret_cast(object) - oat_data_begin); + return static_cast(reinterpret_cast(object.Ptr()) - oat_data_begin); } void PatchObjectAddress(std::vector* code, uint32_t offset, mirror::Object* object) @@ -2126,20 +2202,14 @@ bool OatWriter::VisitDexMethods(DexMethodVisitor* visitor) { ClassDataItemIterator it(*dex_file, class_data); it.SkipAllFields(); size_t class_def_method_index = 0u; - while (it.HasNextDirectMethod()) { + while (it.HasNextMethod()) { if (!visitor->VisitMethod(class_def_method_index, it)) { return false; } ++class_def_method_index; it.Next(); } - while (it.HasNextVirtualMethod()) { - if (UNLIKELY(!visitor->VisitMethod(class_def_method_index, it))) { - return false; - } - ++class_def_method_index; - it.Next(); - } + DCHECK(!it.HasNext()); } } if (UNLIKELY(!visitor->EndClass())) { @@ -2219,38 +2289,101 @@ size_t OatWriter::InitOatMaps(size_t offset) { return offset; } -size_t OatWriter::InitMethodBssMappings(size_t offset) { - size_t number_of_dex_files = 0u; +template +static size_t CalculateNumberOfIndexBssMappingEntries(size_t number_of_indexes, + size_t slot_size, + const BitVector& indexes, + GetBssOffset get_bss_offset) { + IndexBssMappingEncoder encoder(number_of_indexes, slot_size); + size_t number_of_entries = 0u; + bool first_index = true; + for (uint32_t index : indexes.Indexes()) { + uint32_t bss_offset = get_bss_offset(index); + if (first_index || !encoder.TryMerge(index, bss_offset)) { + encoder.Reset(index, bss_offset); + ++number_of_entries; + first_index = false; + } + } + DCHECK_NE(number_of_entries, 0u); + return number_of_entries; +} + +template +static size_t CalculateIndexBssMappingSize(size_t number_of_indexes, + size_t slot_size, + const BitVector& indexes, + GetBssOffset get_bss_offset) { + size_t number_of_entries = CalculateNumberOfIndexBssMappingEntries(number_of_indexes, + slot_size, + indexes, + get_bss_offset); + return IndexBssMapping::ComputeSize(number_of_entries); +} + +size_t OatWriter::InitIndexBssMappings(size_t offset) { + if (bss_method_entry_references_.empty() && + bss_type_entry_references_.empty() && + bss_string_entry_references_.empty()) { + return offset; + } + // If there are any classes, the class offsets allocation aligns the offset + // and we cannot have any index bss mappings without class offsets. + static_assert(alignof(IndexBssMapping) == 4u, "IndexBssMapping alignment check."); + DCHECK_ALIGNED(offset, 4u); + + size_t number_of_method_dex_files = 0u; + size_t number_of_type_dex_files = 0u; + size_t number_of_string_dex_files = 0u; + PointerSize pointer_size = GetInstructionSetPointerSize(oat_header_->GetInstructionSet()); for (size_t i = 0, size = dex_files_->size(); i != size; ++i) { const DexFile* dex_file = (*dex_files_)[i]; - auto it = bss_method_entry_references_.find(dex_file); - if (it != bss_method_entry_references_.end()) { - const BitVector& method_indexes = it->second; - ++number_of_dex_files; - // If there are any classes, the class offsets allocation aligns the offset - // and we cannot have method bss mappings without class offsets. - static_assert(alignof(MethodBssMapping) == 4u, "MethodBssMapping alignment check."); - DCHECK_ALIGNED(offset, 4u); + auto method_it = bss_method_entry_references_.find(dex_file); + if (method_it != bss_method_entry_references_.end()) { + const BitVector& method_indexes = method_it->second; + ++number_of_method_dex_files; oat_dex_files_[i].method_bss_mapping_offset_ = offset; + offset += CalculateIndexBssMappingSize( + dex_file->NumMethodIds(), + static_cast(pointer_size), + method_indexes, + [=](uint32_t index) { + return bss_method_entries_.Get({dex_file, index}); + }); + } - MethodBssMappingEncoder encoder( - GetInstructionSetPointerSize(oat_header_->GetInstructionSet())); - size_t number_of_entries = 0u; - bool first_index = true; - for (uint32_t method_index : method_indexes.Indexes()) { - uint32_t bss_offset = bss_method_entries_.Get(MethodReference(dex_file, method_index)); - if (first_index || !encoder.TryMerge(method_index, bss_offset)) { - encoder.Reset(method_index, bss_offset); - ++number_of_entries; - first_index = false; - } - } - DCHECK_NE(number_of_entries, 0u); - offset += MethodBssMapping::ComputeSize(number_of_entries); + auto type_it = bss_type_entry_references_.find(dex_file); + if (type_it != bss_type_entry_references_.end()) { + const BitVector& type_indexes = type_it->second; + ++number_of_type_dex_files; + oat_dex_files_[i].type_bss_mapping_offset_ = offset; + offset += CalculateIndexBssMappingSize( + dex_file->NumTypeIds(), + sizeof(GcRoot), + type_indexes, + [=](uint32_t index) { + return bss_type_entries_.Get({dex_file, dex::TypeIndex(index)}); + }); + } + + auto string_it = bss_string_entry_references_.find(dex_file); + if (string_it != bss_string_entry_references_.end()) { + const BitVector& string_indexes = string_it->second; + ++number_of_string_dex_files; + oat_dex_files_[i].string_bss_mapping_offset_ = offset; + offset += CalculateIndexBssMappingSize( + dex_file->NumStringIds(), + sizeof(GcRoot), + string_indexes, + [=](uint32_t index) { + return bss_string_entries_.Get({dex_file, dex::StringIndex(index)}); + }); } } - // Check that all dex files targeted by method bss entries are in `*dex_files_`. - CHECK_EQ(number_of_dex_files, bss_method_entry_references_.size()); + // Check that all dex files targeted by bss entries are in `*dex_files_`. + CHECK_EQ(number_of_method_dex_files, bss_method_entry_references_.size()); + CHECK_EQ(number_of_type_dex_files, bss_type_entry_references_.size()); + CHECK_EQ(number_of_string_dex_files, bss_string_entry_references_.size()); return offset; } @@ -2450,7 +2583,7 @@ bool OatWriter::WriteRodata(OutputStream* out) { return false; } - relative_offset = WriteMethodBssMappings(out, file_offset, relative_offset); + relative_offset = WriteIndexBssMappings(out, file_offset, relative_offset); if (relative_offset == 0) { PLOG(ERROR) << "Failed to write method bss mappings to " << out->GetLocation(); return false; @@ -2537,20 +2670,15 @@ class OatWriter::WriteQuickeningInfoMethodVisitor : public DexMethodVisitor { class OatWriter::WriteQuickeningIndicesMethodVisitor { public: WriteQuickeningIndicesMethodVisitor(OutputStream* out, - uint32_t indices_offset, - const SafeMap& offset_map, - std::vector* dex_files_offset) + uint32_t quickening_info_bytes, + const SafeMap& offset_map) : out_(out), - indices_offset_(indices_offset), + quickening_info_bytes_(quickening_info_bytes), written_bytes_(0u), - dex_files_offset_(dex_files_offset), offset_map_(offset_map) {} bool VisitDexMethods(const std::vector& dex_files, const CompilerDriver& driver) { for (const DexFile* dex_file : dex_files) { - // Record the offset for this current dex file. It will be written in the vdex file - // later. - dex_files_offset_->push_back(indices_offset_ + GetNumberOfWrittenBytes()); const size_t class_def_count = dex_file->NumClassDefs(); for (size_t class_def_index = 0; class_def_index != class_def_count; ++class_def_index) { const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); @@ -2561,23 +2689,39 @@ class OatWriter::WriteQuickeningIndicesMethodVisitor { for (ClassDataItemIterator class_it(*dex_file, class_data); class_it.HasNext(); class_it.Next()) { - if (!class_it.IsAtMethod()) { + if (!class_it.IsAtMethod() || class_it.GetMethodCodeItem() == nullptr) { continue; } uint32_t method_idx = class_it.GetMemberIndex(); CompiledMethod* compiled_method = driver.GetCompiledMethod(MethodReference(dex_file, method_idx)); - if (HasQuickeningInfo(compiled_method)) { - uint32_t code_item_offset = class_it.GetMethodCodeItemOffset(); - uint32_t offset = offset_map_.Get(compiled_method->GetVmapTable().data()); - if (!out_->WriteFully(&code_item_offset, sizeof(code_item_offset)) || - !out_->WriteFully(&offset, sizeof(offset))) { + const DexFile::CodeItem* code_item = class_it.GetMethodCodeItem(); + CodeItemDebugInfoAccessor accessor(dex_file, code_item); + const uint32_t existing_debug_info_offset = accessor.DebugInfoOffset(); + // If the existing offset is already out of bounds (and not magic marker 0xFFFFFFFF) + // we will pretend the method has been quickened. + bool existing_offset_out_of_bounds = + (existing_debug_info_offset >= dex_file->Size() && + existing_debug_info_offset != 0xFFFFFFFF); + bool has_quickening_info = HasQuickeningInfo(compiled_method); + if (has_quickening_info || existing_offset_out_of_bounds) { + uint32_t new_debug_info_offset = + dex_file->Size() + quickening_info_bytes_ + written_bytes_; + // Abort if overflow. + CHECK_GE(new_debug_info_offset, dex_file->Size()); + const_cast(code_item)->SetDebugInfoOffset(new_debug_info_offset); + uint32_t quickening_offset = has_quickening_info + ? offset_map_.Get(compiled_method->GetVmapTable().data()) + : VdexFile::kNoQuickeningInfoOffset; + if (!out_->WriteFully(&existing_debug_info_offset, + sizeof(existing_debug_info_offset)) || + !out_->WriteFully(&quickening_offset, sizeof(quickening_offset))) { PLOG(ERROR) << "Failed to write quickening info for " << dex_file->PrettyMethod(method_idx) << " to " << out_->GetLocation(); return false; } - written_bytes_ += sizeof(code_item_offset) + sizeof(offset); + written_bytes_ += sizeof(existing_debug_info_offset) + sizeof(quickening_offset); } } } @@ -2591,25 +2735,16 @@ class OatWriter::WriteQuickeningIndicesMethodVisitor { private: OutputStream* const out_; - const uint32_t indices_offset_; + const uint32_t quickening_info_bytes_; size_t written_bytes_; - std::vector* dex_files_offset_; // Maps quickening map to its offset in the file. const SafeMap& offset_map_; }; bool OatWriter::WriteQuickeningInfo(OutputStream* vdex_out) { - if (!kIsVdexEnabled) { - return true; - } - size_t initial_offset = vdex_size_; size_t start_offset = RoundUp(initial_offset, 4u); - vdex_size_ = start_offset; - vdex_quickening_info_offset_ = vdex_size_; - size_quickening_info_alignment_ = start_offset - initial_offset; - off_t actual_offset = vdex_out->Seek(start_offset, kSeekSet); if (actual_offset != static_cast(start_offset)) { PLOG(ERROR) << "Failed to seek to quickening info section. Actual: " << actual_offset @@ -2627,44 +2762,46 @@ bool OatWriter::WriteQuickeningInfo(OutputStream* vdex_out) { return false; } - WriteQuickeningIndicesMethodVisitor visitor2(vdex_out, - visitor1.GetNumberOfWrittenBytes(), - offset_map, - &dex_files_indices); - if (!visitor2.VisitDexMethods(*dex_files_, *compiler_driver_)) { - PLOG(ERROR) << "Failed to write the vdex quickening info. File: " << vdex_out->GetLocation(); - return false; - } - - DCHECK_EQ(dex_files_->size(), dex_files_indices.size()); - if (!vdex_out->WriteFully( - dex_files_indices.data(), sizeof(dex_files_indices[0]) * dex_files_indices.size())) { - PLOG(ERROR) << "Failed to write the vdex quickening info. File: " << vdex_out->GetLocation(); - return false; - } + if (visitor1.GetNumberOfWrittenBytes() > 0) { + WriteQuickeningIndicesMethodVisitor visitor2(vdex_out, + visitor1.GetNumberOfWrittenBytes(), + offset_map); + if (!visitor2.VisitDexMethods(*dex_files_, *compiler_driver_)) { + PLOG(ERROR) << "Failed to write the vdex quickening info. File: " + << vdex_out->GetLocation(); + return false; + } - if (!vdex_out->Flush()) { - PLOG(ERROR) << "Failed to flush stream after writing quickening info." - << " File: " << vdex_out->GetLocation(); - return false; + if (!vdex_out->Flush()) { + PLOG(ERROR) << "Failed to flush stream after writing quickening info." + << " File: " << vdex_out->GetLocation(); + return false; + } + size_quickening_info_ = visitor1.GetNumberOfWrittenBytes() + + visitor2.GetNumberOfWrittenBytes(); + } else { + // We know we did not quicken. + size_quickening_info_ = 0; } - size_quickening_info_ = visitor1.GetNumberOfWrittenBytes() + - visitor2.GetNumberOfWrittenBytes() + - dex_files_->size() * sizeof(uint32_t); } else { // We know we did not quicken. size_quickening_info_ = 0; } - vdex_size_ += size_quickening_info_; + if (size_quickening_info_ == 0) { + // Nothing was written. Leave `vdex_size_` untouched and unaligned. + vdex_quickening_info_offset_ = initial_offset; + size_quickening_info_alignment_ = 0; + } else { + vdex_size_ = start_offset + size_quickening_info_; + vdex_quickening_info_offset_ = start_offset; + size_quickening_info_alignment_ = start_offset - initial_offset; + } + return true; } bool OatWriter::WriteVerifierDeps(OutputStream* vdex_out, verifier::VerifierDeps* verifier_deps) { - if (!kIsVdexEnabled) { - return true; - } - if (verifier_deps == nullptr) { // Nothing to write. Record the offset, but no need // for alignment. @@ -2779,6 +2916,8 @@ bool OatWriter::WriteCode(OutputStream* out) { DO_STAT(size_oat_dex_file_dex_layout_sections_); DO_STAT(size_oat_dex_file_dex_layout_sections_alignment_); DO_STAT(size_oat_dex_file_method_bss_mapping_offset_); + DO_STAT(size_oat_dex_file_type_bss_mapping_offset_); + DO_STAT(size_oat_dex_file_string_bss_mapping_offset_); DO_STAT(size_oat_lookup_table_alignment_); DO_STAT(size_oat_lookup_table_); DO_STAT(size_oat_class_offsets_alignment_); @@ -2788,6 +2927,8 @@ bool OatWriter::WriteCode(OutputStream* out) { DO_STAT(size_oat_class_method_bitmaps_); DO_STAT(size_oat_class_method_offsets_); DO_STAT(size_method_bss_mappings_); + DO_STAT(size_type_bss_mappings_); + DO_STAT(size_string_bss_mappings_); #undef DO_STAT VLOG(compiler) << "size_total=" << PrettySize(size_total) << " (" << size_total << "B)"; @@ -2927,64 +3068,131 @@ size_t OatWriter::WriteMaps(OutputStream* out, size_t file_offset, size_t relati return relative_offset; } -size_t OatWriter::WriteMethodBssMappings(OutputStream* out, - size_t file_offset, - size_t relative_offset) { + +template +size_t WriteIndexBssMapping(OutputStream* out, + size_t number_of_indexes, + size_t slot_size, + const BitVector& indexes, + GetBssOffset get_bss_offset) { + // Allocate the IndexBssMapping. + size_t number_of_entries = CalculateNumberOfIndexBssMappingEntries( + number_of_indexes, slot_size, indexes, get_bss_offset); + size_t mappings_size = IndexBssMapping::ComputeSize(number_of_entries); + DCHECK_ALIGNED(mappings_size, sizeof(uint32_t)); + std::unique_ptr storage(new uint32_t[mappings_size / sizeof(uint32_t)]); + IndexBssMapping* mappings = new(storage.get()) IndexBssMapping(number_of_entries); + mappings->ClearPadding(); + // Encode the IndexBssMapping. + IndexBssMappingEncoder encoder(number_of_indexes, slot_size); + auto init_it = mappings->begin(); + bool first_index = true; + for (uint32_t index : indexes.Indexes()) { + size_t bss_offset = get_bss_offset(index); + if (first_index) { + first_index = false; + encoder.Reset(index, bss_offset); + } else if (!encoder.TryMerge(index, bss_offset)) { + *init_it = encoder.GetEntry(); + ++init_it; + encoder.Reset(index, bss_offset); + } + } + // Store the last entry. + *init_it = encoder.GetEntry(); + ++init_it; + DCHECK(init_it == mappings->end()); + + if (!out->WriteFully(storage.get(), mappings_size)) { + return 0u; + } + return mappings_size; +} + +size_t OatWriter::WriteIndexBssMappings(OutputStream* out, + size_t file_offset, + size_t relative_offset) { TimingLogger::ScopedTiming split("WriteMethodBssMappings", timings_); + if (bss_method_entry_references_.empty() && + bss_type_entry_references_.empty() && + bss_string_entry_references_.empty()) { + return relative_offset; + } + // If there are any classes, the class offsets allocation aligns the offset + // and we cannot have method bss mappings without class offsets. + static_assert(alignof(IndexBssMapping) == sizeof(uint32_t), + "IndexBssMapping alignment check."); + DCHECK_ALIGNED(relative_offset, sizeof(uint32_t)); + PointerSize pointer_size = GetInstructionSetPointerSize(oat_header_->GetInstructionSet()); for (size_t i = 0, size = dex_files_->size(); i != size; ++i) { const DexFile* dex_file = (*dex_files_)[i]; OatDexFile* oat_dex_file = &oat_dex_files_[i]; - auto it = bss_method_entry_references_.find(dex_file); - if (it != bss_method_entry_references_.end()) { - const BitVector& method_indexes = it->second; - // If there are any classes, the class offsets allocation aligns the offset - // and we cannot have method bss mappings without class offsets. - static_assert(alignof(MethodBssMapping) == sizeof(uint32_t), - "MethodBssMapping alignment check."); - DCHECK_ALIGNED(relative_offset, sizeof(uint32_t)); - - MethodBssMappingEncoder encoder( - GetInstructionSetPointerSize(oat_header_->GetInstructionSet())); - // Allocate a sufficiently large MethodBssMapping. - size_t number_of_method_indexes = method_indexes.NumSetBits(); - DCHECK_NE(number_of_method_indexes, 0u); - size_t max_mappings_size = MethodBssMapping::ComputeSize(number_of_method_indexes); - DCHECK_ALIGNED(max_mappings_size, sizeof(uint32_t)); - std::unique_ptr storage(new uint32_t[max_mappings_size / sizeof(uint32_t)]); - MethodBssMapping* mappings = new(storage.get()) MethodBssMapping(number_of_method_indexes); - mappings->ClearPadding(); - // Encode the MethodBssMapping. - auto init_it = mappings->begin(); - bool first_index = true; - for (uint32_t method_index : method_indexes.Indexes()) { - size_t bss_offset = bss_method_entries_.Get(MethodReference(dex_file, method_index)); - if (first_index) { - first_index = false; - encoder.Reset(method_index, bss_offset); - } else if (!encoder.TryMerge(method_index, bss_offset)) { - *init_it = encoder.GetEntry(); - ++init_it; - encoder.Reset(method_index, bss_offset); - } - } - // Store the last entry and shrink the mapping to the actual size. - *init_it = encoder.GetEntry(); - ++init_it; - DCHECK(init_it <= mappings->end()); - mappings->SetSize(std::distance(mappings->begin(), init_it)); - size_t mappings_size = MethodBssMapping::ComputeSize(mappings->size()); - + auto method_it = bss_method_entry_references_.find(dex_file); + if (method_it != bss_method_entry_references_.end()) { + const BitVector& method_indexes = method_it->second; DCHECK_EQ(relative_offset, oat_dex_file->method_bss_mapping_offset_); DCHECK_OFFSET(); - if (!out->WriteFully(storage.get(), mappings_size)) { + size_t method_mappings_size = WriteIndexBssMapping( + out, + dex_file->NumMethodIds(), + static_cast(pointer_size), + method_indexes, + [=](uint32_t index) { + return bss_method_entries_.Get({dex_file, index}); + }); + if (method_mappings_size == 0u) { return 0u; } - size_method_bss_mappings_ += mappings_size; - relative_offset += mappings_size; + size_method_bss_mappings_ += method_mappings_size; + relative_offset += method_mappings_size; } else { DCHECK_EQ(0u, oat_dex_file->method_bss_mapping_offset_); } + + auto type_it = bss_type_entry_references_.find(dex_file); + if (type_it != bss_type_entry_references_.end()) { + const BitVector& type_indexes = type_it->second; + DCHECK_EQ(relative_offset, oat_dex_file->type_bss_mapping_offset_); + DCHECK_OFFSET(); + size_t type_mappings_size = WriteIndexBssMapping( + out, + dex_file->NumTypeIds(), + sizeof(GcRoot), + type_indexes, + [=](uint32_t index) { + return bss_type_entries_.Get({dex_file, dex::TypeIndex(index)}); + }); + if (type_mappings_size == 0u) { + return 0u; + } + size_type_bss_mappings_ += type_mappings_size; + relative_offset += type_mappings_size; + } else { + DCHECK_EQ(0u, oat_dex_file->type_bss_mapping_offset_); + } + + auto string_it = bss_string_entry_references_.find(dex_file); + if (string_it != bss_string_entry_references_.end()) { + const BitVector& string_indexes = string_it->second; + DCHECK_EQ(relative_offset, oat_dex_file->string_bss_mapping_offset_); + DCHECK_OFFSET(); + size_t string_mappings_size = WriteIndexBssMapping( + out, + dex_file->NumStringIds(), + sizeof(GcRoot), + string_indexes, + [=](uint32_t index) { + return bss_string_entries_.Get({dex_file, dex::StringIndex(index)}); + }); + if (string_mappings_size == 0u) { + return 0u; + } + size_string_bss_mappings_ += string_mappings_size; + relative_offset += string_mappings_size; + } else { + DCHECK_EQ(0u, oat_dex_file->string_bss_mapping_offset_); + } } return relative_offset; } @@ -3082,44 +3290,6 @@ bool OatWriter::RecordOatDataOffset(OutputStream* out) { return true; } -bool OatWriter::ReadDexFileHeader(File* file, OatDexFile* oat_dex_file) { - // Read the dex file header and perform minimal verification. - uint8_t raw_header[sizeof(DexFile::Header)]; - if (!file->ReadFully(&raw_header, sizeof(DexFile::Header))) { - PLOG(ERROR) << "Failed to read dex file header. Actual: " - << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); - return false; - } - if (!ValidateDexFileHeader(raw_header, oat_dex_file->GetLocation())) { - return false; - } - - const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_header); - oat_dex_file->dex_file_size_ = header->file_size_; - oat_dex_file->dex_file_location_checksum_ = header->checksum_; - oat_dex_file->class_offsets_.resize(header->class_defs_size_); - return true; -} - -bool OatWriter::ValidateDexFileHeader(const uint8_t* raw_header, const char* location) { - const bool valid_standard_dex_magic = DexFileLoader::IsMagicValid(raw_header); - if (!valid_standard_dex_magic) { - LOG(ERROR) << "Invalid magic number in dex file header. " << " File: " << location; - return false; - } - if (!DexFileLoader::IsVersionAndMagicValid(raw_header)) { - LOG(ERROR) << "Invalid version number in dex file header. " << " File: " << location; - return false; - } - const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_header); - if (header->file_size_ < sizeof(DexFile::Header)) { - LOG(ERROR) << "Dex file header specifies file size insufficient to contain the header." - << " File: " << location; - return false; - } - return true; -} - bool OatWriter::WriteDexFiles(OutputStream* out, File* file, bool update_input_vdex) { TimingLogger::ScopedTiming split("Write Dex files", timings_); @@ -3152,8 +3322,9 @@ bool OatWriter::WriteDexFile(OutputStream* out, if (!SeekToDexFile(out, file, oat_dex_file)) { return false; } - if (profile_compilation_info_ != nullptr || - compact_dex_level_ != CompactDexLevel::kCompactDexLevelNone) { + // update_input_vdex disables compact dex and layout. + if (!update_input_vdex && (profile_compilation_info_ != nullptr || + compact_dex_level_ != CompactDexLevel::kCompactDexLevelNone)) { CHECK(!update_input_vdex) << "We should never update the input vdex when doing dexlayout"; if (!LayoutAndWriteDexFile(out, oat_dex_file)) { return false; @@ -3176,23 +3347,17 @@ bool OatWriter::WriteDexFile(OutputStream* out, } // Update current size and account for the written data. - if (kIsVdexEnabled) { - DCHECK_EQ(vdex_size_, oat_dex_file->dex_file_offset_); - vdex_size_ += oat_dex_file->dex_file_size_; - } else { - DCHECK(!update_input_vdex); - DCHECK_EQ(oat_size_, oat_dex_file->dex_file_offset_); - oat_size_ += oat_dex_file->dex_file_size_; - } + DCHECK_EQ(vdex_size_, oat_dex_file->dex_file_offset_); + vdex_size_ += oat_dex_file->dex_file_size_; size_dex_file_ += oat_dex_file->dex_file_size_; return true; } bool OatWriter::SeekToDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file) { // Dex files are required to be 4 byte aligned. - size_t initial_offset = kIsVdexEnabled ? vdex_size_ : oat_size_; + size_t initial_offset = vdex_size_; size_t start_offset = RoundUp(initial_offset, 4); - size_t file_offset = kIsVdexEnabled ? start_offset : (oat_data_offset_ + start_offset); + size_t file_offset = start_offset; size_dex_file_alignment_ += start_offset - initial_offset; // Seek to the start of the dex file and flush any pending operations in the stream. @@ -3217,11 +3382,7 @@ bool OatWriter::SeekToDexFile(OutputStream* out, File* file, OatDexFile* oat_dex return false; } - if (kIsVdexEnabled) { - vdex_size_ = start_offset; - } else { - oat_size_ = start_offset; - } + vdex_size_ = start_offset; oat_dex_file->dex_file_offset_ = start_offset; return true; } @@ -3281,15 +3442,19 @@ bool OatWriter::LayoutAndWriteDexFile(OutputStream* out, OatDexFile* oat_dex_fil Options options; options.output_to_memmap_ = true; options.compact_dex_level_ = compact_dex_level_; + options.update_checksum_ = true; DexLayout dex_layout(options, profile_compilation_info_, nullptr); dex_layout.ProcessDexFile(location.c_str(), dex_file.get(), 0); std::unique_ptr mem_map(dex_layout.GetAndReleaseMemMap()); + oat_dex_file->dex_sections_layout_ = dex_layout.GetSections(); + // Dex layout can affect the size of the dex file, so we update here what we have set + // when adding the dex file as a source. + const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(mem_map->Begin()); + oat_dex_file->dex_file_size_ = header->file_size_; if (!WriteDexFile(out, oat_dex_file, mem_map->Begin(), /* update_input_vdex */ false)) { return false; } - oat_dex_file->dex_sections_layout_ = dex_layout.GetSections(); - // Set the checksum of the new oat dex file to be the original file's checksum. - oat_dex_file->dex_file_location_checksum_ = dex_file->GetLocationChecksum(); + CHECK_EQ(oat_dex_file->dex_file_location_checksum_, dex_file->GetLocationChecksum()); return true; } @@ -3297,7 +3462,7 @@ bool OatWriter::WriteDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file, ZipEntry* dex_file) { - size_t start_offset = kIsVdexEnabled ? vdex_size_ : oat_data_offset_ + oat_size_; + size_t start_offset = vdex_size_; DCHECK_EQ(static_cast(start_offset), out->Seek(0, kSeekCurrent)); // Extract the dex file and get the extracted size. @@ -3339,9 +3504,6 @@ bool OatWriter::WriteDexFile(OutputStream* out, << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); return false; } - if (!ReadDexFileHeader(file, oat_dex_file)) { - return false; - } if (extracted_size < oat_dex_file->dex_file_size_) { LOG(ERROR) << "Extracted truncated dex file. Extracted size: " << extracted_size << " file size from header: " << oat_dex_file->dex_file_size_ @@ -3349,9 +3511,6 @@ bool OatWriter::WriteDexFile(OutputStream* out, return false; } - // Override the checksum from header with the CRC from ZIP entry. - oat_dex_file->dex_file_location_checksum_ = dex_file->GetCrc32(); - // Seek both file and stream to the end offset. size_t end_offset = start_offset + oat_dex_file->dex_file_size_; actual_offset = lseek(file->Fd(), end_offset, SEEK_SET); @@ -3390,7 +3549,7 @@ bool OatWriter::WriteDexFile(OutputStream* out, File* file, OatDexFile* oat_dex_file, File* dex_file) { - size_t start_offset = kIsVdexEnabled ? vdex_size_ : oat_data_offset_ + oat_size_; + size_t start_offset = vdex_size_; DCHECK_EQ(static_cast(start_offset), out->Seek(0, kSeekCurrent)); off_t input_offset = lseek(dex_file->Fd(), 0, SEEK_SET); @@ -3400,9 +3559,6 @@ bool OatWriter::WriteDexFile(OutputStream* out, << " File: " << oat_dex_file->GetLocation() << " Output: " << file->GetPath(); return false; } - if (!ReadDexFileHeader(dex_file, oat_dex_file)) { - return false; - } // Copy the input dex file using sendfile(). if (!file->Copy(dex_file, 0, oat_dex_file->dex_file_size_)) { @@ -3464,12 +3620,6 @@ bool OatWriter::WriteDexFile(OutputStream* out, return false; } } - - // Update dex file size and resize class offsets in the OatDexFile. - // Note: For raw data, the checksum is passed directly to AddRawDexFileSource(). - // Note: For vdex, the checksum is copied from the existing vdex file. - oat_dex_file->dex_file_size_ = header->file_size_; - oat_dex_file->class_offsets_.resize(header->class_defs_size_); return true; } @@ -3486,7 +3636,7 @@ bool OatWriter::OpenDexFiles( } size_t map_offset = oat_dex_files_[0].dex_file_offset_; - size_t length = kIsVdexEnabled ? (vdex_size_ - map_offset) : (oat_size_ - map_offset); + size_t length = vdex_size_ - map_offset; std::string error_msg; std::unique_ptr dex_files_map(MemMap::MapFile( @@ -3494,7 +3644,7 @@ bool OatWriter::OpenDexFiles( PROT_READ | PROT_WRITE, MAP_SHARED, file->Fd(), - kIsVdexEnabled ? map_offset : (oat_data_offset_ + map_offset), + map_offset, /* low_4gb */ false, file->GetPath().c_str(), &error_msg)); @@ -3505,29 +3655,22 @@ bool OatWriter::OpenDexFiles( } std::vector> dex_files; for (OatDexFile& oat_dex_file : oat_dex_files_) { - // Make sure no one messed with input files while we were copying data. - // At the very least we need consistent file size and number of class definitions. const uint8_t* raw_dex_file = dex_files_map->Begin() + oat_dex_file.dex_file_offset_ - map_offset; - if (!ValidateDexFileHeader(raw_dex_file, oat_dex_file.GetLocation())) { - // Note: ValidateDexFileHeader() already logged an error message. - LOG(ERROR) << "Failed to verify written dex file header!" + + if (kIsDebugBuild) { + // Sanity check our input files. + // Note that ValidateDexFileHeader() logs error messages. + CHECK(ValidateDexFileHeader(raw_dex_file, oat_dex_file.GetLocation())) + << "Failed to verify written dex file header!" << " Output: " << file->GetPath() << " ~ " << std::hex << map_offset << " ~ " << static_cast(raw_dex_file); - return false; - } - const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_dex_file); - if (header->file_size_ != oat_dex_file.dex_file_size_) { - LOG(ERROR) << "File size mismatch in written dex file header! Expected: " + + const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_dex_file); + CHECK_EQ(header->file_size_, oat_dex_file.dex_file_size_) + << "File size mismatch in written dex file header! Expected: " << oat_dex_file.dex_file_size_ << " Actual: " << header->file_size_ << " Output: " << file->GetPath(); - return false; - } - if (header->class_defs_size_ != oat_dex_file.class_offsets_.size()) { - LOG(ERROR) << "Class defs size mismatch in written dex file header! Expected: " - << oat_dex_file.class_offsets_.size() << " Actual: " << header->class_defs_size_ - << " Output: " << file->GetPath(); - return false; } // Now, open the dex file. @@ -3544,6 +3687,10 @@ bool OatWriter::OpenDexFiles( << " Error: " << error_msg; return false; } + + // Set the class_offsets size now that we have easy access to the DexFile and + // it has been verified in DexFileLoader::Open. + oat_dex_file.class_offsets_.resize(dex_files.back()->GetHeader().class_defs_size_); } *opened_dex_files_map = std::move(dex_files_map); @@ -3698,9 +3845,6 @@ bool OatWriter::WriteDexLayoutSections( } bool OatWriter::WriteChecksumsAndVdexHeader(OutputStream* vdex_out) { - if (!kIsVdexEnabled) { - return true; - } // Write checksums off_t actual_offset = vdex_out->Seek(sizeof(VdexFile::Header), kSeekSet); if (actual_offset != sizeof(VdexFile::Header)) { @@ -3730,6 +3874,7 @@ bool OatWriter::WriteChecksumsAndVdexHeader(OutputStream* vdex_out) { DCHECK_NE(vdex_dex_files_offset_, 0u); DCHECK_NE(vdex_verifier_deps_offset_, 0u); + DCHECK_NE(vdex_quickening_info_offset_, 0u); size_t dex_section_size = vdex_verifier_deps_offset_ - vdex_dex_files_offset_; size_t verifier_deps_section_size = vdex_quickening_info_offset_ - vdex_verifier_deps_offset_; @@ -3784,18 +3929,22 @@ void OatWriter::SetMultiOatRelativePatcherAdjustment() { OatWriter::OatDexFile::OatDexFile(const char* dex_file_location, DexFileSource source, - CreateTypeLookupTable create_type_lookup_table) + CreateTypeLookupTable create_type_lookup_table, + uint32_t dex_file_location_checksum, + size_t dex_file_size) : source_(source), create_type_lookup_table_(create_type_lookup_table), - dex_file_size_(0), + dex_file_size_(dex_file_size), offset_(0), dex_file_location_size_(strlen(dex_file_location)), dex_file_location_data_(dex_file_location), - dex_file_location_checksum_(0u), + dex_file_location_checksum_(dex_file_location_checksum), dex_file_offset_(0u), - class_offsets_offset_(0u), lookup_table_offset_(0u), + class_offsets_offset_(0u), method_bss_mapping_offset_(0u), + type_bss_mapping_offset_(0u), + string_bss_mapping_offset_(0u), dex_sections_layout_offset_(0u), class_offsets_() { } @@ -3808,6 +3957,8 @@ size_t OatWriter::OatDexFile::SizeOf() const { + sizeof(class_offsets_offset_) + sizeof(lookup_table_offset_) + sizeof(method_bss_mapping_offset_) + + sizeof(type_bss_mapping_offset_) + + sizeof(string_bss_mapping_offset_) + sizeof(dex_sections_layout_offset_); } @@ -3863,6 +4014,18 @@ bool OatWriter::OatDexFile::Write(OatWriter* oat_writer, OutputStream* out) cons } oat_writer->size_oat_dex_file_method_bss_mapping_offset_ += sizeof(method_bss_mapping_offset_); + if (!out->WriteFully(&type_bss_mapping_offset_, sizeof(type_bss_mapping_offset_))) { + PLOG(ERROR) << "Failed to write type bss mapping offset to " << out->GetLocation(); + return false; + } + oat_writer->size_oat_dex_file_type_bss_mapping_offset_ += sizeof(type_bss_mapping_offset_); + + if (!out->WriteFully(&string_bss_mapping_offset_, sizeof(string_bss_mapping_offset_))) { + PLOG(ERROR) << "Failed to write string bss mapping offset to " << out->GetLocation(); + return false; + } + oat_writer->size_oat_dex_file_string_bss_mapping_offset_ += sizeof(string_bss_mapping_offset_); + return true; } diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h index 6a82fd1d5906a07e389d2ef848a6e39239c745c1..4055878b55d46169a26e941acc050341037da84a 100644 --- a/dex2oat/linker/oat_writer.h +++ b/dex2oat/linker/oat_writer.h @@ -310,7 +310,7 @@ class OatWriter { size_t InitClassOffsets(size_t offset); size_t InitOatClasses(size_t offset); size_t InitOatMaps(size_t offset); - size_t InitMethodBssMappings(size_t offset); + size_t InitIndexBssMappings(size_t offset); size_t InitOatDexFiles(size_t offset); size_t InitOatCode(size_t offset); size_t InitOatCodeDexFiles(size_t offset); @@ -319,14 +319,12 @@ class OatWriter { size_t WriteClassOffsets(OutputStream* out, size_t file_offset, size_t relative_offset); size_t WriteClasses(OutputStream* out, size_t file_offset, size_t relative_offset); size_t WriteMaps(OutputStream* out, size_t file_offset, size_t relative_offset); - size_t WriteMethodBssMappings(OutputStream* out, size_t file_offset, size_t relative_offset); + size_t WriteIndexBssMappings(OutputStream* out, size_t file_offset, size_t relative_offset); size_t WriteOatDexFiles(OutputStream* out, size_t file_offset, size_t relative_offset); size_t WriteCode(OutputStream* out, size_t file_offset, size_t relative_offset); size_t WriteCodeDexFiles(OutputStream* out, size_t file_offset, size_t relative_offset); bool RecordOatDataOffset(OutputStream* out); - bool ReadDexFileHeader(File* oat_file, OatDexFile* oat_dex_file); - bool ValidateDexFileHeader(const uint8_t* raw_header, const char* location); bool WriteTypeLookupTables(OutputStream* oat_rodata, const std::vector>& opened_dex_files); bool WriteDexLayoutSections(OutputStream* oat_rodata, @@ -403,6 +401,12 @@ class OatWriter { // Map for recording references to ArtMethod entries in .bss. SafeMap bss_method_entry_references_; + // Map for recording references to GcRoot entries in .bss. + SafeMap bss_type_entry_references_; + + // Map for recording references to GcRoot entries in .bss. + SafeMap bss_string_entry_references_; + // Map for allocating ArtMethod entries in .bss. Indexed by MethodReference for the target // method in the dex file with the "method reference value comparator" for deduplication. // The value is the target offset for patching, starting at `bss_start_ + bss_methods_offset_`. @@ -476,6 +480,8 @@ class OatWriter { uint32_t size_oat_dex_file_dex_layout_sections_; uint32_t size_oat_dex_file_dex_layout_sections_alignment_; uint32_t size_oat_dex_file_method_bss_mapping_offset_; + uint32_t size_oat_dex_file_type_bss_mapping_offset_; + uint32_t size_oat_dex_file_string_bss_mapping_offset_; uint32_t size_oat_lookup_table_alignment_; uint32_t size_oat_lookup_table_; uint32_t size_oat_class_offsets_alignment_; @@ -485,6 +491,8 @@ class OatWriter { uint32_t size_oat_class_method_bitmaps_; uint32_t size_oat_class_method_offsets_; uint32_t size_method_bss_mappings_; + uint32_t size_type_bss_mappings_; + uint32_t size_string_bss_mappings_; // The helper for processing relative patches is external so that we can patch across oat files. MultiOatRelativePatcher* relative_patcher_; diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index 1ee2e4efd0ecafe9520b05d53cddeb4385d42fae..e9958b129a29a07463bc04f8d3ce3646f9137bc3 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -45,6 +45,7 @@ #include "oat_writer.h" #include "scoped_thread_state_change-inl.h" #include "utils/test_dex_file_builder.h" +#include "vdex_file.h" namespace art { namespace linker { @@ -102,7 +103,6 @@ class OatTest : public CommonCompilerTest { callbacks_.reset(new QuickCompilerCallbacks(CompilerCallbacks::CallbackMode::kCompileApp)); callbacks_->SetVerificationResults(verification_results_.get()); Runtime::Current()->SetCompilerCallbacks(callbacks_.get()); - timer_.reset(new CumulativeLogger("Compilation times")); compiler_driver_.reset(new CompilerDriver(compiler_options_.get(), verification_results_.get(), compiler_kind, @@ -112,9 +112,6 @@ class OatTest : public CommonCompilerTest { /* compiled_classes */ nullptr, /* compiled_methods */ nullptr, /* thread_count */ 2, - /* dump_stats */ true, - /* dump_passes */ true, - timer_.get(), /* swap_fd */ -1, /* profile_compilation_info */ nullptr)); } @@ -192,7 +189,7 @@ class OatTest : public CommonCompilerTest { OutputStream* oat_rodata = elf_writer->StartRoData(); std::unique_ptr opened_dex_files_map; std::vector> opened_dex_files; - if (!oat_writer.WriteAndOpenDexFiles(kIsVdexEnabled ? vdex_file : oat_file, + if (!oat_writer.WriteAndOpenDexFiles(vdex_file, oat_rodata, compiler_driver_->GetInstructionSet(), compiler_driver_->GetInstructionSetFeatures(), @@ -224,15 +221,16 @@ class OatTest : public CommonCompilerTest { oat_writer.GetBssMethodsOffset(), oat_writer.GetBssRootsOffset()); - if (kIsVdexEnabled) { - std::unique_ptr vdex_out = - std::make_unique(std::make_unique(vdex_file)); - if (!oat_writer.WriteVerifierDeps(vdex_out.get(), nullptr)) { - return false; - } - if (!oat_writer.WriteChecksumsAndVdexHeader(vdex_out.get())) { - return false; - } + std::unique_ptr vdex_out = + std::make_unique(std::make_unique(vdex_file)); + if (!oat_writer.WriteVerifierDeps(vdex_out.get(), nullptr)) { + return false; + } + if (!oat_writer.WriteQuickeningInfo(vdex_out.get())) { + return false; + } + if (!oat_writer.WriteChecksumsAndVdexHeader(vdex_out.get())) { + return false; } if (!oat_writer.WriteRodata(oat_rodata)) { @@ -642,6 +640,11 @@ void OatTest::TestDexFileInput(bool verify, bool low_4gb, bool use_profile) { std::unique_ptr opened_dex_file2 = opened_oat_file->GetOatDexFiles()[1]->OpenDexFile(&error_msg); + ASSERT_EQ(opened_oat_file->GetOatDexFiles()[0]->GetDexFileLocationChecksum(), + dex_file1_data->GetHeader().checksum_); + ASSERT_EQ(opened_oat_file->GetOatDexFiles()[1]->GetDexFileLocationChecksum(), + dex_file2_data->GetHeader().checksum_); + ASSERT_EQ(dex_file1_data->GetHeader().file_size_, opened_dex_file1->GetHeader().file_size_); ASSERT_EQ(0, memcmp(&dex_file1_data->GetHeader(), &opened_dex_file1->GetHeader(), @@ -653,6 +656,13 @@ void OatTest::TestDexFileInput(bool verify, bool low_4gb, bool use_profile) { &opened_dex_file2->GetHeader(), dex_file2_data->GetHeader().file_size_)); ASSERT_EQ(dex_file2_data->GetLocation(), opened_dex_file2->GetLocation()); + + const VdexFile::Header &vdex_header = opened_oat_file->GetVdexFile()->GetHeader(); + ASSERT_EQ(vdex_header.GetQuickeningInfoSize(), 0u); + + int64_t actual_vdex_size = vdex_file.GetFile()->GetLength(); + ASSERT_GE(actual_vdex_size, 0); + ASSERT_EQ((uint64_t) actual_vdex_size, vdex_header.GetComputedFileSize()); } TEST_F(OatTest, DexFileInputCheckOutput) { @@ -720,7 +730,7 @@ void OatTest::TestZipFileInput(bool verify) { key_value_store.Put(OatHeader::kImageLocationKey, "test.art"); { // Test using the AddDexFileSource() interface with the zip file. - std::vector input_filenames { zip_file.GetFilename().c_str() }; // NOLINT [readability/braces] [4] + std::vector input_filenames = { zip_file.GetFilename().c_str() }; ScratchFile oat_file, vdex_file(oat_file, ".vdex"); success = WriteElf(vdex_file.GetFile(), oat_file.GetFile(), input_filenames, @@ -831,7 +841,7 @@ void OatTest::TestZipFileInputWithEmptyDex() { SafeMap key_value_store; key_value_store.Put(OatHeader::kImageLocationKey, "test.art"); - std::vector input_filenames { zip_file.GetFilename().c_str() }; // NOLINT [readability/braces] [4] + std::vector input_filenames = { zip_file.GetFilename().c_str() }; ScratchFile oat_file, vdex_file(oat_file, ".vdex"); std::unique_ptr profile_compilation_info(new ProfileCompilationInfo()); success = WriteElf(vdex_file.GetFile(), oat_file.GetFile(), input_filenames, diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc index 4bfd91fdd9d77eefe986ba4c0890cb9615dbd299..730d4b97a0b28d56429d2b952101b6c79aa86fea 100644 --- a/dexdump/dexdump.cc +++ b/dexdump/dexdump.cc @@ -44,6 +44,7 @@ #include "android-base/stringprintf.h" +#include "code_item_accessors-no_art-inl.h" #include "dex_file-inl.h" #include "dex_file_loader.h" #include "dex_file_types.h" @@ -734,7 +735,7 @@ static void dumpInterface(const DexFile* pDexFile, const DexFile::TypeItem& pTyp * Dumps the catches table associated with the code. */ static void dumpCatches(const DexFile* pDexFile, const DexFile::CodeItem* pCode) { - const u4 triesSize = pCode->tries_size_; + const u4 triesSize = CodeItemDataAccessor(pDexFile, pCode).TriesSize(); // No catch table. if (triesSize == 0) { @@ -949,14 +950,14 @@ static void dumpInstruction(const DexFile* pDexFile, fprintf(gOutFile, "%06x:", codeOffset + 0x10 + insnIdx * 2); // Dump (part of) raw bytes. - const u2* insns = pCode->insns_; + CodeItemInstructionAccessor accessor(pDexFile, pCode); for (u4 i = 0; i < 8; i++) { if (i < insnWidth) { if (i == 7) { fprintf(gOutFile, " ... "); } else { // Print 16-bit value in little-endian order. - const u1* bytePtr = (const u1*) &insns[insnIdx + i]; + const u1* bytePtr = (const u1*) &accessor.Insns()[insnIdx + i]; fprintf(gOutFile, " %02x%02x", bytePtr[0], bytePtr[1]); } } else { @@ -966,7 +967,7 @@ static void dumpInstruction(const DexFile* pDexFile, // Dump pseudo-instruction or opcode. if (pDecInsn->Opcode() == Instruction::NOP) { - const u2 instr = get2LE((const u1*) &insns[insnIdx]); + const u2 instr = get2LE((const u1*) &accessor.Insns()[insnIdx]); if (instr == Instruction::kPackedSwitchSignature) { fprintf(gOutFile, "|%04x: packed-switch-data (%d units)", insnIdx, insnWidth); } else if (instr == Instruction::kSparseSwitchSignature) { @@ -1167,16 +1168,15 @@ static void dumpBytecodes(const DexFile* pDexFile, u4 idx, codeOffset, codeOffset, dot.get(), name, signature.ToString().c_str()); // Iterate over all instructions. - const u2* insns = pCode->insns_; - for (u4 insnIdx = 0; insnIdx < pCode->insns_size_in_code_units_;) { - const Instruction* instruction = Instruction::At(&insns[insnIdx]); + CodeItemDataAccessor accessor(pDexFile, pCode); + for (const DexInstructionPcPair& pair : accessor) { + const Instruction* instruction = &pair.Inst(); const u4 insnWidth = instruction->SizeInCodeUnits(); if (insnWidth == 0) { - fprintf(stderr, "GLITCH: zero-width instruction at idx=0x%04x\n", insnIdx); + fprintf(stderr, "GLITCH: zero-width instruction at idx=0x%04x\n", pair.DexPc()); break; } - dumpInstruction(pDexFile, pCode, codeOffset, insnIdx, insnWidth, instruction); - insnIdx += insnWidth; + dumpInstruction(pDexFile, pCode, codeOffset, pair.DexPc(), insnWidth, instruction); } // for } @@ -1185,11 +1185,13 @@ static void dumpBytecodes(const DexFile* pDexFile, u4 idx, */ static void dumpCode(const DexFile* pDexFile, u4 idx, u4 flags, const DexFile::CodeItem* pCode, u4 codeOffset) { - fprintf(gOutFile, " registers : %d\n", pCode->registers_size_); - fprintf(gOutFile, " ins : %d\n", pCode->ins_size_); - fprintf(gOutFile, " outs : %d\n", pCode->outs_size_); + CodeItemDebugInfoAccessor accessor(pDexFile, pCode, pDexFile->GetDebugInfoOffset(pCode)); + + fprintf(gOutFile, " registers : %d\n", accessor.RegistersSize()); + fprintf(gOutFile, " ins : %d\n", accessor.InsSize()); + fprintf(gOutFile, " outs : %d\n", accessor.OutsSize()); fprintf(gOutFile, " insns size : %d 16-bit code units\n", - pCode->insns_size_in_code_units_); + accessor.InsnsSizeInCodeUnits()); // Bytecode disassembly, if requested. if (gOptions.disassemble) { @@ -1202,9 +1204,9 @@ static void dumpCode(const DexFile* pDexFile, u4 idx, u4 flags, // Positions and locals table in the debug info. bool is_static = (flags & kAccStatic) != 0; fprintf(gOutFile, " positions : \n"); - pDexFile->DecodeDebugPositionInfo(pCode, dumpPositionsCb, nullptr); + pDexFile->DecodeDebugPositionInfo(accessor.DebugInfoOffset(), dumpPositionsCb, nullptr); fprintf(gOutFile, " locals : \n"); - pDexFile->DecodeDebugLocalInfo(pCode, is_static, idx, dumpLocalsCb, nullptr); + accessor.DecodeDebugLocalInfo(is_static, idx, dumpLocalsCb, nullptr); } /* @@ -1391,18 +1393,12 @@ static void dumpCfg(const DexFile* dex_file, int idx) { } ClassDataItemIterator it(*dex_file, class_data); it.SkipAllFields(); - while (it.HasNextDirectMethod()) { + while (it.HasNextMethod()) { dumpCfg(dex_file, it.GetMemberIndex(), it.GetMethodCodeItem()); it.Next(); } - while (it.HasNextVirtualMethod()) { - dumpCfg(dex_file, - it.GetMemberIndex(), - it.GetMethodCodeItem()); - it.Next(); - } } /* diff --git a/dexdump/dexdump_cfg.cc b/dexdump/dexdump_cfg.cc index 28317071dd110398b937dd334bb3d3c353c9d9f6..dd57a117584bbc315d1dedf4e652bd87c00573d7 100644 --- a/dexdump/dexdump_cfg.cc +++ b/dexdump/dexdump_cfg.cc @@ -23,7 +23,9 @@ #include #include #include +#include +#include "code_item_accessors-no_art-inl.h" #include "dex_file-inl.h" #include "dex_instruction-inl.h" @@ -36,17 +38,17 @@ static void dumpMethodCFGImpl(const DexFile* dex_file, os << "digraph {\n"; os << " # /* " << dex_file->PrettyMethod(dex_method_idx, true) << " */\n"; + CodeItemInstructionAccessor accessor(dex_file, code_item); + std::set dex_pc_is_branch_target; { // Go and populate. - const Instruction* inst = Instruction::At(code_item->insns_); - for (uint32_t dex_pc = 0; - dex_pc < code_item->insns_size_in_code_units_; - dex_pc += inst->SizeInCodeUnits(), inst = inst->Next()) { + for (const DexInstructionPcPair& pair : accessor) { + const Instruction* inst = &pair.Inst(); if (inst->IsBranch()) { - dex_pc_is_branch_target.insert(dex_pc + inst->GetTargetOffset()); + dex_pc_is_branch_target.insert(pair.DexPc() + inst->GetTargetOffset()); } else if (inst->IsSwitch()) { - const uint16_t* insns = code_item->insns_ + dex_pc; + const uint16_t* insns = reinterpret_cast(inst); int32_t switch_offset = insns[1] | (static_cast(insns[2]) << 16); const uint16_t* switch_insns = insns + switch_offset; uint32_t switch_count = switch_insns[1]; @@ -62,7 +64,7 @@ static void dumpMethodCFGImpl(const DexFile* dex_file, int32_t offset = static_cast(switch_insns[targets_offset + targ * 2]) | static_cast(switch_insns[targets_offset + targ * 2 + 1] << 16); - dex_pc_is_branch_target.insert(dex_pc + offset); + dex_pc_is_branch_target.insert(pair.DexPc() + offset); } } } @@ -73,12 +75,10 @@ static void dumpMethodCFGImpl(const DexFile* dex_file, std::map dex_pc_to_incl_id; // This has entries for all dex pcs. { - const Instruction* inst = Instruction::At(code_item->insns_); bool first_in_block = true; bool force_new_block = false; - for (uint32_t dex_pc = 0; - dex_pc < code_item->insns_size_in_code_units_; - dex_pc += inst->SizeInCodeUnits(), inst = inst->Next()) { + for (const DexInstructionPcPair& pair : accessor) { + const uint32_t dex_pc = pair.DexPc(); if (dex_pc == 0 || (dex_pc_is_branch_target.find(dex_pc) != dex_pc_is_branch_target.end()) || force_new_block) { @@ -107,7 +107,7 @@ static void dumpMethodCFGImpl(const DexFile* dex_file, // Dump the instruction. Need to escape '"', '<', '>', '{' and '}'. os << "<" << "p" << dex_pc << ">"; os << " 0x" << std::hex << dex_pc << std::dec << ": "; - std::string inst_str = inst->DumpString(dex_file); + std::string inst_str = pair.Inst().DumpString(dex_file); size_t cur_start = 0; // It's OK to start at zero, instruction dumps don't start with chars // we need to escape. while (cur_start != std::string::npos) { @@ -136,7 +136,7 @@ static void dumpMethodCFGImpl(const DexFile* dex_file, // Force a new block for some fall-throughs and some instructions that terminate the "local" // control flow. - force_new_block = inst->IsSwitch() || inst->IsBasicBlockEnd(); + force_new_block = pair.Inst().IsSwitch() || pair.Inst().IsBasicBlockEnd(); } // Close last node. if (dex_pc_to_node_id.size() > 0) { @@ -161,10 +161,9 @@ static void dumpMethodCFGImpl(const DexFile* dex_file, uint32_t last_node_id = std::numeric_limits::max(); uint32_t old_dex_pc = 0; uint32_t block_start_dex_pc = std::numeric_limits::max(); - const Instruction* inst = Instruction::At(code_item->insns_); - for (uint32_t dex_pc = 0; - dex_pc < code_item->insns_size_in_code_units_; - old_dex_pc = dex_pc, dex_pc += inst->SizeInCodeUnits(), inst = inst->Next()) { + for (const DexInstructionPcPair& pair : accessor) { + const Instruction* inst = &pair.Inst(); + const uint32_t dex_pc = pair.DexPc(); { auto it = dex_pc_to_node_id.find(dex_pc); if (it != dex_pc_to_node_id.end()) { @@ -221,7 +220,7 @@ static void dumpMethodCFGImpl(const DexFile* dex_file, } } else if (inst->IsSwitch()) { // TODO: Iterate through all switch targets. - const uint16_t* insns = code_item->insns_ + dex_pc; + const uint16_t* insns = reinterpret_cast(inst); /* make sure the start of the switch is in range */ int32_t switch_offset = insns[1] | (static_cast(insns[2]) << 16); /* offset to switch table is a relative branch-style offset */ @@ -271,6 +270,7 @@ static void dumpMethodCFGImpl(const DexFile* dex_file, // No fall-through. last_node_id = std::numeric_limits::max(); } + old_dex_pc = pair.DexPc(); } // Finish up the last block, if it had common exceptions. if (!exception_targets.empty()) { @@ -292,7 +292,7 @@ static void dumpMethodCFGImpl(const DexFile* dex_file, // TODO // Exception edges. If this is not the first instruction in the block for (uint32_t dex_pc : blocks_with_detailed_exceptions) { - const Instruction* inst = Instruction::At(&code_item->insns_[dex_pc]); + const Instruction* inst = &accessor.InstructionAt(dex_pc); uint32_t this_node_id = dex_pc_to_incl_id.find(dex_pc)->second; while (true) { CatchHandlerIterator catch_it(*code_item, dex_pc); @@ -321,7 +321,7 @@ static void dumpMethodCFGImpl(const DexFile* dex_file, // Loop update. Have a break-out if the next instruction is a branch target and thus in // another block. dex_pc += inst->SizeInCodeUnits(); - if (dex_pc >= code_item->insns_size_in_code_units_) { + if (dex_pc >= accessor.InsnsSizeInCodeUnits()) { break; } if (dex_pc_to_node_id.find(dex_pc) != dex_pc_to_node_id.end()) { @@ -377,7 +377,7 @@ void DumpMethodCFG(const DexFile* dex_file, uint32_t dex_method_idx, std::ostrea it.SkipAllFields(); // Find method, and dump it. - while (it.HasNextDirectMethod() || it.HasNextVirtualMethod()) { + while (it.HasNextMethod()) { uint32_t method_idx = it.GetMemberIndex(); if (method_idx == dex_method_idx) { dumpMethodCFGImpl(dex_file, dex_method_idx, it.GetMethodCodeItem(), os); diff --git a/dexdump/dexdump_main.cc b/dexdump/dexdump_main.cc index 43c3d12de56ebc1839d55880fd7e0d60d8cb85c7..382b551a1a3fd7f735db724a9153112e3bf2f901 100644 --- a/dexdump/dexdump_main.cc +++ b/dexdump/dexdump_main.cc @@ -28,7 +28,9 @@ #include #include -#include "base/logging.h" +#include + +#include // For InitLogging. #include "mem_map.h" #include "runtime.h" diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp index fabe6e785cde1e8219af89608ae462db72f6f58e..a02f75ad00423d88970ea1eb3c787a8122c8dd28 100644 --- a/dexlayout/Android.bp +++ b/dexlayout/Android.bp @@ -45,16 +45,34 @@ art_cc_library { shared_libs: ["libartd"], } -art_cc_binary { - name: "dexlayout", +cc_defaults { + name: "dexlayout-defaults", defaults: ["art_defaults"], host_supported: true, srcs: ["dexlayout_main.cc"], - cflags: ["-Wall"], + shared_libs: [ + "libbase", + ], +} + +art_cc_binary { + name: "dexlayout", + defaults: ["dexlayout-defaults"], shared_libs: [ "libart", "libart-dexlayout", - "libbase", + ], +} + +art_cc_binary { + name: "dexlayoutd", + defaults: [ + "art_debug_defaults", + "dexlayout-defaults", + ], + shared_libs: [ + "libartd", + "libartd-dexlayout", ], } diff --git a/dexlayout/compact_dex_writer.cc b/dexlayout/compact_dex_writer.cc index b089c1d4b3a0918015685cc92b23a79f236475c0..f2d46199a25a22ccb9c1e832f14be9e896e59f2f 100644 --- a/dexlayout/compact_dex_writer.cc +++ b/dexlayout/compact_dex_writer.cc @@ -47,7 +47,7 @@ void CompactDexWriter::WriteHeader() { header.class_defs_off_ = collections.ClassDefsOffset(); header.data_size_ = header_->DataSize(); header.data_off_ = header_->DataOffset(); - Write(reinterpret_cast(&header), sizeof(header), 0u); + UNUSED(Write(reinterpret_cast(&header), sizeof(header), 0u)); } } // namespace art diff --git a/dexlayout/compact_dex_writer.h b/dexlayout/compact_dex_writer.h index 1c77202c9ade3c55dfc4ba8bd912b9e66d933597..e28efab5c191315650c2012a316fbd14013ccf14 100644 --- a/dexlayout/compact_dex_writer.h +++ b/dexlayout/compact_dex_writer.h @@ -25,9 +25,12 @@ namespace art { class CompactDexWriter : public DexWriter { public: - CompactDexWriter(dex_ir::Header* header, MemMap* mem_map, CompactDexLevel compact_dex_level) - : DexWriter(header, mem_map), - compact_dex_level_(compact_dex_level) { } + CompactDexWriter(dex_ir::Header* header, + MemMap* mem_map, + DexLayout* dex_layout, + CompactDexLevel compact_dex_level) + : DexWriter(header, mem_map, dex_layout, /*compute_offsets*/ true), + compact_dex_level_(compact_dex_level) {} protected: void WriteHeader() OVERRIDE; diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc index 3edb0a44f2275bae8ccb33415005b25a820a9931..a163bd96c0b4ff958d72607527a32cba1cfc169e 100644 --- a/dexlayout/dex_ir.cc +++ b/dexlayout/dex_ir.cc @@ -21,6 +21,8 @@ */ #include "dex_ir.h" + +#include "code_item_accessors-inl.h" #include "dex_instruction-inl.h" #include "dex_ir_builder.h" @@ -186,22 +188,28 @@ static bool GetIdsFromByteCode(Collections& collections, return has_id; } -EncodedValue* Collections::ReadEncodedValue(const uint8_t** data) { +EncodedValue* Collections::ReadEncodedValue(const DexFile& dex_file, const uint8_t** data) { const uint8_t encoded_value = *(*data)++; const uint8_t type = encoded_value & 0x1f; EncodedValue* item = new EncodedValue(type); - ReadEncodedValue(data, type, encoded_value >> 5, item); + ReadEncodedValue(dex_file, data, type, encoded_value >> 5, item); return item; } -EncodedValue* Collections::ReadEncodedValue(const uint8_t** data, uint8_t type, uint8_t length) { +EncodedValue* Collections::ReadEncodedValue(const DexFile& dex_file, + const uint8_t** data, + uint8_t type, + uint8_t length) { EncodedValue* item = new EncodedValue(type); - ReadEncodedValue(data, type, length, item); + ReadEncodedValue(dex_file, data, type, length, item); return item; } -void Collections::ReadEncodedValue( - const uint8_t** data, uint8_t type, uint8_t length, EncodedValue* item) { +void Collections::ReadEncodedValue(const DexFile& dex_file, + const uint8_t** data, + uint8_t type, + uint8_t length, + EncodedValue* item) { switch (type) { case DexFile::kDexAnnotationByte: item->SetByte(static_cast(ReadVarWidth(data, length, false))); @@ -271,12 +279,17 @@ void Collections::ReadEncodedValue( } case DexFile::kDexAnnotationArray: { EncodedValueVector* values = new EncodedValueVector(); + const uint32_t offset = *data - dex_file.Begin(); const uint32_t size = DecodeUnsignedLeb128(data); // Decode all elements. for (uint32_t i = 0; i < size; i++) { - values->push_back(std::unique_ptr(ReadEncodedValue(data))); + values->push_back(std::unique_ptr(ReadEncodedValue(dex_file, data))); + } + EncodedArrayItem* array_item = new EncodedArrayItem(values); + if (eagerly_assign_offsets_) { + array_item->SetOffset(offset); } - item->SetEncodedArray(new EncodedArrayItem(values)); + item->SetEncodedArray(array_item); break; } case DexFile::kDexAnnotationAnnotation: { @@ -287,7 +300,7 @@ void Collections::ReadEncodedValue( for (uint32_t i = 0; i < size; i++) { const uint32_t name_index = DecodeUnsignedLeb128(data); elements->push_back(std::unique_ptr( - new AnnotationElement(GetStringId(name_index), ReadEncodedValue(data)))); + new AnnotationElement(GetStringId(name_index), ReadEncodedValue(dex_file, data)))); } item->SetEncodedAnnotation(new EncodedAnnotation(GetTypeId(type_idx), elements)); break; @@ -305,16 +318,16 @@ void Collections::ReadEncodedValue( void Collections::CreateStringId(const DexFile& dex_file, uint32_t i) { const DexFile::StringId& disk_string_id = dex_file.GetStringId(dex::StringIndex(i)); StringData* string_data = new StringData(dex_file.GetStringData(disk_string_id)); - string_datas_.AddItem(string_data, disk_string_id.string_data_off_); + AddItem(string_datas_map_, string_datas_, string_data, disk_string_id.string_data_off_); StringId* string_id = new StringId(string_data); - string_ids_.AddIndexedItem(string_id, StringIdsOffset() + i * StringId::ItemSize(), i); + AddIndexedItem(string_ids_, string_id, StringIdsOffset() + i * StringId::ItemSize(), i); } void Collections::CreateTypeId(const DexFile& dex_file, uint32_t i) { const DexFile::TypeId& disk_type_id = dex_file.GetTypeId(dex::TypeIndex(i)); TypeId* type_id = new TypeId(GetStringId(disk_type_id.descriptor_idx_.index_)); - type_ids_.AddIndexedItem(type_id, TypeIdsOffset() + i * TypeId::ItemSize(), i); + AddIndexedItem(type_ids_, type_id, TypeIdsOffset() + i * TypeId::ItemSize(), i); } void Collections::CreateProtoId(const DexFile& dex_file, uint32_t i) { @@ -325,7 +338,7 @@ void Collections::CreateProtoId(const DexFile& dex_file, uint32_t i) { ProtoId* proto_id = new ProtoId(GetStringId(disk_proto_id.shorty_idx_.index_), GetTypeId(disk_proto_id.return_type_idx_.index_), parameter_type_list); - proto_ids_.AddIndexedItem(proto_id, ProtoIdsOffset() + i * ProtoId::ItemSize(), i); + AddIndexedItem(proto_ids_, proto_id, ProtoIdsOffset() + i * ProtoId::ItemSize(), i); } void Collections::CreateFieldId(const DexFile& dex_file, uint32_t i) { @@ -333,7 +346,7 @@ void Collections::CreateFieldId(const DexFile& dex_file, uint32_t i) { FieldId* field_id = new FieldId(GetTypeId(disk_field_id.class_idx_.index_), GetTypeId(disk_field_id.type_idx_.index_), GetStringId(disk_field_id.name_idx_.index_)); - field_ids_.AddIndexedItem(field_id, FieldIdsOffset() + i * FieldId::ItemSize(), i); + AddIndexedItem(field_ids_, field_id, FieldIdsOffset() + i * FieldId::ItemSize(), i); } void Collections::CreateMethodId(const DexFile& dex_file, uint32_t i) { @@ -341,7 +354,7 @@ void Collections::CreateMethodId(const DexFile& dex_file, uint32_t i) { MethodId* method_id = new MethodId(GetTypeId(disk_method_id.class_idx_.index_), GetProtoId(disk_method_id.proto_idx_), GetStringId(disk_method_id.name_idx_.index_)); - method_ids_.AddIndexedItem(method_id, MethodIdsOffset() + i * MethodId::ItemSize(), i); + AddIndexedItem(method_ids_, method_id, MethodIdsOffset() + i * MethodId::ItemSize(), i); } void Collections::CreateClassDef(const DexFile& dex_file, uint32_t i) { @@ -365,48 +378,48 @@ void Collections::CreateClassDef(const DexFile& dex_file, uint32_t i) { // Static field initializers. const uint8_t* static_data = dex_file.GetEncodedStaticFieldValuesArray(disk_class_def); EncodedArrayItem* static_values = - CreateEncodedArrayItem(static_data, disk_class_def.static_values_off_); + CreateEncodedArrayItem(dex_file, static_data, disk_class_def.static_values_off_); ClassData* class_data = CreateClassData( dex_file, dex_file.GetClassData(disk_class_def), disk_class_def.class_data_off_); ClassDef* class_def = new ClassDef(class_type, access_flags, superclass, interfaces_type_list, source_file, annotations, static_values, class_data); - class_defs_.AddIndexedItem(class_def, ClassDefsOffset() + i * ClassDef::ItemSize(), i); + AddIndexedItem(class_defs_, class_def, ClassDefsOffset() + i * ClassDef::ItemSize(), i); } TypeList* Collections::CreateTypeList(const DexFile::TypeList* dex_type_list, uint32_t offset) { if (dex_type_list == nullptr) { return nullptr; } - auto found_type_list = TypeLists().find(offset); - if (found_type_list != TypeLists().end()) { - return found_type_list->second.get(); - } - TypeIdVector* type_vector = new TypeIdVector(); - uint32_t size = dex_type_list->Size(); - for (uint32_t index = 0; index < size; ++index) { - type_vector->push_back(GetTypeId(dex_type_list->GetTypeItem(index).type_idx_.index_)); + TypeList* type_list = type_lists_map_.GetExistingObject(offset); + if (type_list == nullptr) { + TypeIdVector* type_vector = new TypeIdVector(); + uint32_t size = dex_type_list->Size(); + for (uint32_t index = 0; index < size; ++index) { + type_vector->push_back(GetTypeId(dex_type_list->GetTypeItem(index).type_idx_.index_)); + } + type_list = new TypeList(type_vector); + AddItem(type_lists_map_, type_lists_, type_list, offset); } - TypeList* new_type_list = new TypeList(type_vector); - type_lists_.AddItem(new_type_list, offset); - return new_type_list; + return type_list; } -EncodedArrayItem* Collections::CreateEncodedArrayItem(const uint8_t* static_data, uint32_t offset) { +EncodedArrayItem* Collections::CreateEncodedArrayItem(const DexFile& dex_file, + const uint8_t* static_data, + uint32_t offset) { if (static_data == nullptr) { return nullptr; } - auto found_encoded_array_item = EncodedArrayItems().find(offset); - if (found_encoded_array_item != EncodedArrayItems().end()) { - return found_encoded_array_item->second.get(); - } - uint32_t size = DecodeUnsignedLeb128(&static_data); - EncodedValueVector* values = new EncodedValueVector(); - for (uint32_t i = 0; i < size; ++i) { - values->push_back(std::unique_ptr(ReadEncodedValue(&static_data))); + EncodedArrayItem* encoded_array_item = encoded_array_items_map_.GetExistingObject(offset); + if (encoded_array_item == nullptr) { + uint32_t size = DecodeUnsignedLeb128(&static_data); + EncodedValueVector* values = new EncodedValueVector(); + for (uint32_t i = 0; i < size; ++i) { + values->push_back(std::unique_ptr(ReadEncodedValue(dex_file, &static_data))); + } + // TODO: Calculate the size of the encoded array. + encoded_array_item = new EncodedArrayItem(values); + AddItem(encoded_array_items_map_, encoded_array_items_, encoded_array_item, offset); } - // TODO: Calculate the size of the encoded array. - EncodedArrayItem* encoded_array_item = new EncodedArrayItem(values); - encoded_array_items_.AddItem(encoded_array_item, offset); return encoded_array_item; } @@ -427,19 +440,16 @@ AnnotationItem* Collections::CreateAnnotationItem(const DexFile& dex_file, const DexFile::AnnotationItem* annotation) { const uint8_t* const start_data = reinterpret_cast(annotation); const uint32_t offset = start_data - dex_file.Begin(); - auto found_annotation_item = AnnotationItems().find(offset); - if (found_annotation_item != AnnotationItems().end()) { - return found_annotation_item->second.get(); + AnnotationItem* annotation_item = annotation_items_map_.GetExistingObject(offset); + if (annotation_item == nullptr) { + uint8_t visibility = annotation->visibility_; + const uint8_t* annotation_data = annotation->annotation_; + std::unique_ptr encoded_value( + ReadEncodedValue(dex_file, &annotation_data, DexFile::kDexAnnotationAnnotation, 0)); + annotation_item = new AnnotationItem(visibility, encoded_value->ReleaseEncodedAnnotation()); + annotation_item->SetSize(annotation_data - start_data); + AddItem(annotation_items_map_, annotation_items_, annotation_item, offset); } - uint8_t visibility = annotation->visibility_; - const uint8_t* annotation_data = annotation->annotation_; - std::unique_ptr encoded_value( - ReadEncodedValue(&annotation_data, DexFile::kDexAnnotationAnnotation, 0)); - AnnotationItem* annotation_item = - new AnnotationItem(visibility, encoded_value->ReleaseEncodedAnnotation()); - annotation_item->SetOffset(offset); - annotation_item->SetSize(annotation_data - start_data); - annotation_items_.AddItem(annotation_item, annotation_item->GetOffset()); return annotation_item; } @@ -449,30 +459,30 @@ AnnotationSetItem* Collections::CreateAnnotationSetItem(const DexFile& dex_file, if (disk_annotations_item == nullptr || (disk_annotations_item->size_ == 0 && offset == 0)) { return nullptr; } - auto found_anno_set_item = AnnotationSetItems().find(offset); - if (found_anno_set_item != AnnotationSetItems().end()) { - return found_anno_set_item->second.get(); - } - std::vector* items = new std::vector(); - for (uint32_t i = 0; i < disk_annotations_item->size_; ++i) { - const DexFile::AnnotationItem* annotation = - dex_file.GetAnnotationItem(disk_annotations_item, i); - if (annotation == nullptr) { - continue; + AnnotationSetItem* annotation_set_item = annotation_set_items_map_.GetExistingObject(offset); + if (annotation_set_item == nullptr) { + std::vector* items = new std::vector(); + for (uint32_t i = 0; i < disk_annotations_item->size_; ++i) { + const DexFile::AnnotationItem* annotation = + dex_file.GetAnnotationItem(disk_annotations_item, i); + if (annotation == nullptr) { + continue; + } + AnnotationItem* annotation_item = CreateAnnotationItem(dex_file, annotation); + items->push_back(annotation_item); } - AnnotationItem* annotation_item = CreateAnnotationItem(dex_file, annotation); - items->push_back(annotation_item); + annotation_set_item = new AnnotationSetItem(items); + AddItem(annotation_set_items_map_, annotation_set_items_, annotation_set_item, offset); } - AnnotationSetItem* annotation_set_item = new AnnotationSetItem(items); - annotation_set_items_.AddItem(annotation_set_item, offset); return annotation_set_item; } AnnotationsDirectoryItem* Collections::CreateAnnotationsDirectoryItem(const DexFile& dex_file, const DexFile::AnnotationsDirectoryItem* disk_annotations_item, uint32_t offset) { - auto found_anno_dir_item = AnnotationsDirectoryItems().find(offset); - if (found_anno_dir_item != AnnotationsDirectoryItems().end()) { - return found_anno_dir_item->second.get(); + AnnotationsDirectoryItem* annotations_directory_item = + annotations_directory_items_map_.GetExistingObject(offset); + if (annotations_directory_item != nullptr) { + return annotations_directory_item; } const DexFile::AnnotationSetItem* class_set_item = dex_file.GetClassAnnotationSet(disk_annotations_item); @@ -527,20 +537,19 @@ AnnotationsDirectoryItem* Collections::CreateAnnotationsDirectoryItem(const DexF } } // TODO: Calculate the size of the annotations directory. - AnnotationsDirectoryItem* annotations_directory_item = new AnnotationsDirectoryItem( +annotations_directory_item = new AnnotationsDirectoryItem( class_annotation, field_annotations, method_annotations, parameter_annotations); - annotations_directory_items_.AddItem(annotations_directory_item, offset); + AddItem(annotations_directory_items_map_, + annotations_directory_items_, + annotations_directory_item, + offset); return annotations_directory_item; } ParameterAnnotation* Collections::GenerateParameterAnnotation( const DexFile& dex_file, MethodId* method_id, const DexFile::AnnotationSetRefList* annotation_set_ref_list, uint32_t offset) { - AnnotationSetRefList* set_ref_list = nullptr; - auto found_set_ref_list = AnnotationSetRefLists().find(offset); - if (found_set_ref_list != AnnotationSetRefLists().end()) { - set_ref_list = found_set_ref_list->second.get(); - } + AnnotationSetRefList* set_ref_list = annotation_set_ref_lists_map_.GetExistingObject(offset); if (set_ref_list == nullptr) { std::vector* annotations = new std::vector(); for (uint32_t i = 0; i < annotation_set_ref_list->size_; ++i) { @@ -550,46 +559,47 @@ ParameterAnnotation* Collections::GenerateParameterAnnotation( annotations->push_back(CreateAnnotationSetItem(dex_file, annotation_set_item, set_offset)); } set_ref_list = new AnnotationSetRefList(annotations); - annotation_set_ref_lists_.AddItem(set_ref_list, offset); + AddItem(annotation_set_ref_lists_map_, annotation_set_ref_lists_, set_ref_list, offset); } return new ParameterAnnotation(method_id, set_ref_list); } CodeItem* Collections::CreateCodeItem(const DexFile& dex_file, const DexFile::CodeItem& disk_code_item, uint32_t offset) { - uint16_t registers_size = disk_code_item.registers_size_; - uint16_t ins_size = disk_code_item.ins_size_; - uint16_t outs_size = disk_code_item.outs_size_; - uint32_t tries_size = disk_code_item.tries_size_; + CodeItemDebugInfoAccessor accessor(&dex_file, &disk_code_item); + const uint16_t registers_size = accessor.RegistersSize(); + const uint16_t ins_size = accessor.InsSize(); + const uint16_t outs_size = accessor.OutsSize(); + const uint32_t tries_size = accessor.TriesSize(); // TODO: Calculate the size of the debug info. - const uint8_t* debug_info_stream = dex_file.GetDebugInfoStream(&disk_code_item); + const uint32_t debug_info_offset = accessor.DebugInfoOffset(); + const uint8_t* debug_info_stream = dex_file.GetDebugInfoStream(debug_info_offset); DebugInfoItem* debug_info = nullptr; if (debug_info_stream != nullptr) { - debug_info = debug_info_items_.GetExistingObject(disk_code_item.debug_info_off_); + debug_info = debug_info_items_map_.GetExistingObject(debug_info_offset); if (debug_info == nullptr) { uint32_t debug_info_size = GetDebugInfoStreamSize(debug_info_stream); uint8_t* debug_info_buffer = new uint8_t[debug_info_size]; memcpy(debug_info_buffer, debug_info_stream, debug_info_size); debug_info = new DebugInfoItem(debug_info_size, debug_info_buffer); - debug_info_items_.AddItem(debug_info, disk_code_item.debug_info_off_); + AddItem(debug_info_items_map_, debug_info_items_, debug_info, debug_info_offset); } } - uint32_t insns_size = disk_code_item.insns_size_in_code_units_; + uint32_t insns_size = accessor.InsnsSizeInCodeUnits(); uint16_t* insns = new uint16_t[insns_size]; - memcpy(insns, disk_code_item.insns_, insns_size * sizeof(uint16_t)); + memcpy(insns, accessor.Insns(), insns_size * sizeof(uint16_t)); TryItemVector* tries = nullptr; CatchHandlerVector* handler_list = nullptr; if (tries_size > 0) { tries = new TryItemVector(); handler_list = new CatchHandlerVector(); - for (uint32_t i = 0; i < tries_size; ++i) { - const DexFile::TryItem* disk_try_item = dex_file.GetTryItems(disk_code_item, i); - uint32_t start_addr = disk_try_item->start_addr_; - uint16_t insn_count = disk_try_item->insn_count_; - uint16_t handler_off = disk_try_item->handler_off_; + for (const DexFile::TryItem& disk_try_item : accessor.TryItems()) { + uint32_t start_addr = disk_try_item.start_addr_; + uint16_t insn_count = disk_try_item.insn_count_; + uint16_t handler_off = disk_try_item.handler_off_; const CatchHandler* handlers = nullptr; for (std::unique_ptr& existing_handlers : *handler_list) { if (handler_off == existing_handlers->GetListOffset()) { @@ -600,7 +610,7 @@ CodeItem* Collections::CreateCodeItem(const DexFile& dex_file, if (handlers == nullptr) { bool catch_all = false; TypeAddrPairVector* addr_pairs = new TypeAddrPairVector(); - for (CatchHandlerIterator it(disk_code_item, *disk_try_item); it.HasNext(); it.Next()) { + for (CatchHandlerIterator it(disk_code_item, disk_try_item); it.HasNext(); it.Next()) { const dex::TypeIndex type_index = it.GetHandlerTypeIndex(); const TypeId* type_id = GetTypeIdOrNullPtr(type_index.index_); catch_all |= type_id == nullptr; @@ -614,7 +624,7 @@ CodeItem* Collections::CreateCodeItem(const DexFile& dex_file, tries->push_back(std::unique_ptr(try_item)); } // Manually walk catch handlers list and add any missing handlers unreferenced by try items. - const uint8_t* handlers_base = DexFile::GetCatchHandlerData(disk_code_item, 0); + const uint8_t* handlers_base = accessor.GetCatchHandlerData(); const uint8_t* handlers_data = handlers_base; uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_data); while (handlers_size > handler_list->size()) { @@ -662,24 +672,24 @@ CodeItem* Collections::CreateCodeItem(const DexFile& dex_file, CodeItem* code_item = new CodeItem( registers_size, ins_size, outs_size, debug_info, insns_size, insns, tries, handler_list); code_item->SetSize(size); - code_items_.AddItem(code_item, offset); + AddItem(code_items_map_, code_items_, code_item, offset); // Add "fixup" references to types, strings, methods, and fields. // This is temporary, as we will probably want more detailed parsing of the // instructions here. - std::unique_ptr> type_ids(new std::vector()); - std::unique_ptr> string_ids(new std::vector()); - std::unique_ptr> method_ids(new std::vector()); - std::unique_ptr> field_ids(new std::vector()); + std::vector type_ids; + std::vector string_ids; + std::vector method_ids; + std::vector field_ids; if (GetIdsFromByteCode(*this, code_item, - type_ids.get(), - string_ids.get(), - method_ids.get(), - field_ids.get())) { - CodeFixups* fixups = new CodeFixups(type_ids.release(), - string_ids.release(), - method_ids.release(), - field_ids.release()); + /*out*/ &type_ids, + /*out*/ &string_ids, + /*out*/ &method_ids, + /*out*/ &field_ids)) { + CodeFixups* fixups = new CodeFixups(std::move(type_ids), + std::move(string_ids), + std::move(method_ids), + std::move(field_ids)); code_item->SetCodeFixups(fixups); } @@ -690,7 +700,7 @@ MethodItem* Collections::GenerateMethodItem(const DexFile& dex_file, ClassDataIt MethodId* method_id = GetMethodId(cdii.GetMemberIndex()); uint32_t access_flags = cdii.GetRawMemberAccessFlags(); const DexFile::CodeItem* disk_code_item = cdii.GetMethodCodeItem(); - CodeItem* code_item = code_items_.GetExistingObject(cdii.GetMethodCodeItemOffset()); + CodeItem* code_item = code_items_map_.GetExistingObject(cdii.GetMethodCodeItemOffset()); DebugInfoItem* debug_info = nullptr; if (disk_code_item != nullptr) { if (code_item == nullptr) { @@ -705,7 +715,7 @@ ClassData* Collections::CreateClassData( const DexFile& dex_file, const uint8_t* encoded_data, uint32_t offset) { // Read the fields and methods defined by the class, resolving the circular reference from those // to classes by setting class at the same time. - ClassData* class_data = class_datas_.GetExistingObject(offset); + ClassData* class_data = class_datas_map_.GetExistingObject(offset); if (class_data == nullptr && encoded_data != nullptr) { ClassDataItemIterator cdii(dex_file, encoded_data); // Static fields. @@ -735,7 +745,7 @@ ClassData* Collections::CreateClassData( } class_data = new ClassData(static_fields, instance_fields, direct_methods, virtual_methods); class_data->SetSize(cdii.EndDataPointer() - encoded_data); - class_datas_.AddItem(class_data, offset); + AddItem(class_datas_map_, class_datas_, class_data, offset); } return class_data; } @@ -771,10 +781,10 @@ void Collections::CreateCallSiteId(const DexFile& dex_file, uint32_t i) { const DexFile::CallSiteIdItem& disk_call_site_id = dex_file.GetCallSiteId(i); const uint8_t* disk_call_item_ptr = dex_file.Begin() + disk_call_site_id.data_off_; EncodedArrayItem* call_site_item = - CreateEncodedArrayItem(disk_call_item_ptr, disk_call_site_id.data_off_); + CreateEncodedArrayItem(dex_file, disk_call_item_ptr, disk_call_site_id.data_off_); CallSiteId* call_site_id = new CallSiteId(call_site_item); - call_site_ids_.AddIndexedItem(call_site_id, CallSiteIdsOffset() + i * CallSiteId::ItemSize(), i); + AddIndexedItem(call_site_ids_, call_site_id, CallSiteIdsOffset() + i * CallSiteId::ItemSize(), i); } void Collections::CreateMethodHandleItem(const DexFile& dex_file, uint32_t i) { @@ -796,8 +806,23 @@ void Collections::CreateMethodHandleItem(const DexFile& dex_file, uint32_t i) { field_or_method_id = GetFieldId(index); } MethodHandleItem* method_handle = new MethodHandleItem(type, field_or_method_id); - method_handle_items_.AddIndexedItem( - method_handle, MethodHandleItemsOffset() + i * MethodHandleItem::ItemSize(), i); + AddIndexedItem(method_handle_items_, + method_handle, + MethodHandleItemsOffset() + i * MethodHandleItem::ItemSize(), + i); +} + +void Collections::SortVectorsByMapOrder() { + string_datas_map_.SortVectorByMapOrder(string_datas_); + type_lists_map_.SortVectorByMapOrder(type_lists_); + encoded_array_items_map_.SortVectorByMapOrder(encoded_array_items_); + annotation_items_map_.SortVectorByMapOrder(annotation_items_); + annotation_set_items_map_.SortVectorByMapOrder(annotation_set_items_); + annotation_set_ref_lists_map_.SortVectorByMapOrder(annotation_set_ref_lists_); + annotations_directory_items_map_.SortVectorByMapOrder(annotations_directory_items_); + debug_info_items_map_.SortVectorByMapOrder(debug_info_items_); + code_items_map_.SortVectorByMapOrder(code_items_); + class_datas_map_.SortVectorByMapOrder(class_datas_); } static uint32_t HeaderOffset(const dex_ir::Collections& collections ATTRIBUTE_UNUSED) { diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h index 179d3b96e07a9db8b7570301f113ce93585e5fda..b25e1645ddd1c2c5d9e81057e5c54d9da4dea02a 100644 --- a/dexlayout/dex_ir.h +++ b/dexlayout/dex_ir.h @@ -112,33 +112,55 @@ template class CollectionBase { public: CollectionBase() = default; - uint32_t GetOffset() const { return offset_; } - void SetOffset(uint32_t new_offset) { offset_ = new_offset; } + uint32_t GetOffset() const { + return offset_; + } + void SetOffset(uint32_t new_offset) { + offset_ = new_offset; + } private: - uint32_t offset_ = 0; + // Start out unassigned. + uint32_t offset_ = 0u; DISALLOW_COPY_AND_ASSIGN(CollectionBase); }; template class CollectionVector : public CollectionBase { public: + using Vector = std::vector>; CollectionVector() = default; - void AddIndexedItem(T* object, uint32_t offset, uint32_t index) { - object->SetOffset(offset); - object->SetIndex(index); + uint32_t Size() const { return collection_.size(); } + Vector& Collection() { return collection_; } + + protected: + Vector collection_; + + void AddItem(T* object) { collection_.push_back(std::unique_ptr(object)); } - uint32_t Size() const { return collection_.size(); } - std::vector>& Collection() { return collection_; } private: - std::vector> collection_; - + friend class Collections; DISALLOW_COPY_AND_ASSIGN(CollectionVector); }; +template class IndexedCollectionVector : public CollectionVector { + public: + using Vector = std::vector>; + IndexedCollectionVector() = default; + + private: + void AddIndexedItem(T* object, uint32_t index) { + object->SetIndex(index); + CollectionVector::collection_.push_back(std::unique_ptr(object)); + } + + friend class Collections; + DISALLOW_COPY_AND_ASSIGN(IndexedCollectionVector); +}; + template class CollectionMap : public CollectionBase { public: CollectionMap() = default; @@ -146,21 +168,35 @@ template class CollectionMap : public CollectionBase { // Returns the existing item if it is already inserted, null otherwise. T* GetExistingObject(uint32_t offset) { auto it = collection_.find(offset); - return it != collection_.end() ? it->second.get() : nullptr; + return it != collection_.end() ? it->second : nullptr; } - void AddItem(T* object, uint32_t offset) { - object->SetOffset(offset); - auto it = collection_.emplace(offset, std::unique_ptr(object)); - CHECK(it.second) << "CollectionMap already has an object with offset " << offset << " " - << " and address " << it.first->second.get(); - } uint32_t Size() const { return collection_.size(); } - std::map>& Collection() { return collection_; } + std::map& Collection() { return collection_; } + + // Sort the vector by copying pointers over. + void SortVectorByMapOrder(CollectionVector& vector) { + auto it = collection_.begin(); + CHECK_EQ(vector.Size(), Size()); + for (size_t i = 0; i < Size(); ++i) { + // There are times when the array will temporarily contain the same pointer twice, doing the + // release here sure there is no double free errors. + vector.Collection()[i].release(); + vector.Collection()[i].reset(it->second); + ++it; + } + } private: - std::map> collection_; + std::map collection_; + + void AddItem(T* object, uint32_t offset) { + auto it = collection_.emplace(offset, object); + CHECK(it.second) << "CollectionMap already has an object with offset " << offset << " " + << " and address " << it.first->second; + } + friend class Collections; DISALLOW_COPY_AND_ASSIGN(CollectionMap); }; @@ -168,32 +204,31 @@ class Collections { public: Collections() = default; - std::vector>& StringIds() { return string_ids_.Collection(); } - std::vector>& TypeIds() { return type_ids_.Collection(); } - std::vector>& ProtoIds() { return proto_ids_.Collection(); } - std::vector>& FieldIds() { return field_ids_.Collection(); } - std::vector>& MethodIds() { return method_ids_.Collection(); } - std::vector>& ClassDefs() { return class_defs_.Collection(); } - std::vector>& CallSiteIds() { return call_site_ids_.Collection(); } - std::vector>& MethodHandleItems() + CollectionVector::Vector& StringIds() { return string_ids_.Collection(); } + CollectionVector::Vector& TypeIds() { return type_ids_.Collection(); } + CollectionVector::Vector& ProtoIds() { return proto_ids_.Collection(); } + CollectionVector::Vector& FieldIds() { return field_ids_.Collection(); } + CollectionVector::Vector& MethodIds() { return method_ids_.Collection(); } + CollectionVector::Vector& ClassDefs() { return class_defs_.Collection(); } + CollectionVector::Vector& CallSiteIds() { return call_site_ids_.Collection(); } + CollectionVector::Vector& MethodHandleItems() { return method_handle_items_.Collection(); } - std::map>& StringDatas() - { return string_datas_.Collection(); } - std::map>& TypeLists() { return type_lists_.Collection(); } - std::map>& EncodedArrayItems() + CollectionVector::Vector& StringDatas() { return string_datas_.Collection(); } + CollectionVector::Vector& TypeLists() { return type_lists_.Collection(); } + CollectionVector::Vector& EncodedArrayItems() { return encoded_array_items_.Collection(); } - std::map>& AnnotationItems() + CollectionVector::Vector& AnnotationItems() { return annotation_items_.Collection(); } - std::map>& AnnotationSetItems() + CollectionVector::Vector& AnnotationSetItems() { return annotation_set_items_.Collection(); } - std::map>& AnnotationSetRefLists() + CollectionVector::Vector& AnnotationSetRefLists() { return annotation_set_ref_lists_.Collection(); } - std::map>& AnnotationsDirectoryItems() + CollectionVector::Vector& AnnotationsDirectoryItems() { return annotations_directory_items_.Collection(); } - std::map>& DebugInfoItems() + CollectionVector::Vector& DebugInfoItems() { return debug_info_items_.Collection(); } - std::map>& CodeItems() { return code_items_.Collection(); } - std::map>& ClassDatas() { return class_datas_.Collection(); } + CollectionVector::Vector& CodeItems() { return code_items_.Collection(); } + CollectionVector::Vector& ClassDatas() { return class_datas_.Collection(); } void CreateStringId(const DexFile& dex_file, uint32_t i); void CreateTypeId(const DexFile& dex_file, uint32_t i); @@ -207,7 +242,9 @@ class Collections { void CreateCallSitesAndMethodHandles(const DexFile& dex_file); TypeList* CreateTypeList(const DexFile::TypeList* type_list, uint32_t offset); - EncodedArrayItem* CreateEncodedArrayItem(const uint8_t* static_data, uint32_t offset); + EncodedArrayItem* CreateEncodedArrayItem(const DexFile& dex_file, + const uint8_t* static_data, + uint32_t offset); AnnotationItem* CreateAnnotationItem(const DexFile& dex_file, const DexFile::AnnotationItem* annotation); AnnotationSetItem* CreateAnnotationSetItem(const DexFile& dex_file, @@ -326,37 +363,110 @@ class Collections { uint32_t CodeItemsSize() const { return code_items_.Size(); } uint32_t ClassDatasSize() const { return class_datas_.Size(); } + // Sort the vectors buy map order (same order that was used in the input file). + void SortVectorsByMapOrder(); + + template + void AddItem(CollectionMap& map, + CollectionVector& vector, + Type* item, + uint32_t offset) { + DCHECK(!map.GetExistingObject(offset)); + DCHECK(!item->OffsetAssigned()); + if (eagerly_assign_offsets_) { + item->SetOffset(offset); + } + map.AddItem(item, offset); + vector.AddItem(item); + } + + template + void AddIndexedItem(IndexedCollectionVector& vector, + Type* item, + uint32_t offset, + uint32_t index) { + DCHECK(!item->OffsetAssigned()); + if (eagerly_assign_offsets_) { + item->SetOffset(offset); + } + vector.AddIndexedItem(item, index); + } + + void SetEagerlyAssignOffsets(bool eagerly_assign_offsets) { + eagerly_assign_offsets_ = eagerly_assign_offsets; + } + + void SetLinkData(std::vector&& link_data) { + link_data_ = std::move(link_data); + } + + const std::vector& LinkData() const { + return link_data_; + } + private: - EncodedValue* ReadEncodedValue(const uint8_t** data); - EncodedValue* ReadEncodedValue(const uint8_t** data, uint8_t type, uint8_t length); - void ReadEncodedValue(const uint8_t** data, uint8_t type, uint8_t length, EncodedValue* item); + EncodedValue* ReadEncodedValue(const DexFile& dex_file, const uint8_t** data); + EncodedValue* ReadEncodedValue(const DexFile& dex_file, + const uint8_t** data, + uint8_t type, + uint8_t length); + void ReadEncodedValue(const DexFile& dex_file, + const uint8_t** data, + uint8_t type, + uint8_t length, + EncodedValue* item); ParameterAnnotation* GenerateParameterAnnotation(const DexFile& dex_file, MethodId* method_id, const DexFile::AnnotationSetRefList* annotation_set_ref_list, uint32_t offset); MethodItem* GenerateMethodItem(const DexFile& dex_file, ClassDataItemIterator& cdii); - CollectionVector string_ids_; - CollectionVector type_ids_; - CollectionVector proto_ids_; - CollectionVector field_ids_; - CollectionVector method_ids_; - CollectionVector class_defs_; - CollectionVector call_site_ids_; - CollectionVector method_handle_items_; - - CollectionMap string_datas_; - CollectionMap type_lists_; - CollectionMap encoded_array_items_; - CollectionMap annotation_items_; - CollectionMap annotation_set_items_; - CollectionMap annotation_set_ref_lists_; - CollectionMap annotations_directory_items_; - CollectionMap debug_info_items_; - CollectionMap code_items_; - CollectionMap class_datas_; + // Collection vectors own the IR data. + IndexedCollectionVector string_ids_; + IndexedCollectionVector type_ids_; + IndexedCollectionVector proto_ids_; + IndexedCollectionVector field_ids_; + IndexedCollectionVector method_ids_; + IndexedCollectionVector call_site_ids_; + IndexedCollectionVector method_handle_items_; + IndexedCollectionVector string_datas_; + IndexedCollectionVector type_lists_; + IndexedCollectionVector encoded_array_items_; + IndexedCollectionVector annotation_items_; + IndexedCollectionVector annotation_set_items_; + IndexedCollectionVector annotation_set_ref_lists_; + IndexedCollectionVector annotations_directory_items_; + IndexedCollectionVector class_defs_; + // The order of the vectors controls the layout of the output file by index order, to change the + // layout just sort the vector. Note that you may only change the order of the non indexed vectors + // below. Indexed vectors are accessed by indices in other places, changing the sorting order will + // invalidate the existing indices and is not currently supported. + CollectionVector debug_info_items_; + CollectionVector code_items_; + CollectionVector class_datas_; + + // Note that the maps do not have ownership, the vectors do. + // TODO: These maps should only be required for building the IR and should be put in a separate + // IR builder class. + CollectionMap string_datas_map_; + CollectionMap type_lists_map_; + CollectionMap encoded_array_items_map_; + CollectionMap annotation_items_map_; + CollectionMap annotation_set_items_map_; + CollectionMap annotation_set_ref_lists_map_; + CollectionMap annotations_directory_items_map_; + CollectionMap debug_info_items_map_; + CollectionMap code_items_map_; + CollectionMap class_datas_map_; uint32_t map_list_offset_ = 0; + // Link data. + std::vector link_data_; + + // If we eagerly assign offsets during IR building or later after layout. Must be false if + // changing the layout is enabled. + bool eagerly_assign_offsets_; + DISALLOW_COPY_AND_ASSIGN(Collections); }; @@ -365,15 +475,26 @@ class Item { Item() { } virtual ~Item() { } - uint32_t GetOffset() const { return offset_; } + // Return the assigned offset. + uint32_t GetOffset() const { + CHECK(OffsetAssigned()); + return offset_; + } uint32_t GetSize() const { return size_; } void SetOffset(uint32_t offset) { offset_ = offset; } void SetSize(uint32_t size) { size_ = size; } + bool OffsetAssigned() const { + return offset_ != kOffsetUnassigned; + } protected: Item(uint32_t offset, uint32_t size) : offset_(offset), size_(size) { } - uint32_t offset_ = 0; + // 0 is the dex file header and shouldn't be a valid offset for any part of the dex file. + static constexpr uint32_t kOffsetUnassigned = 0u; + + // Start out unassigned. + uint32_t offset_ = kOffsetUnassigned; uint32_t size_ = 0; }; @@ -892,25 +1013,25 @@ using TryItemVector = std::vector>; class CodeFixups { public: - CodeFixups(std::vector* type_ids, - std::vector* string_ids, - std::vector* method_ids, - std::vector* field_ids) - : type_ids_(type_ids), - string_ids_(string_ids), - method_ids_(method_ids), - field_ids_(field_ids) { } - - std::vector* TypeIds() const { return type_ids_.get(); } - std::vector* StringIds() const { return string_ids_.get(); } - std::vector* MethodIds() const { return method_ids_.get(); } - std::vector* FieldIds() const { return field_ids_.get(); } + CodeFixups(std::vector type_ids, + std::vector string_ids, + std::vector method_ids, + std::vector field_ids) + : type_ids_(std::move(type_ids)), + string_ids_(std::move(string_ids)), + method_ids_(std::move(method_ids)), + field_ids_(std::move(field_ids)) { } + + const std::vector& TypeIds() const { return type_ids_; } + const std::vector& StringIds() const { return string_ids_; } + const std::vector& MethodIds() const { return method_ids_; } + const std::vector& FieldIds() const { return field_ids_; } private: - std::unique_ptr> type_ids_; - std::unique_ptr> string_ids_; - std::unique_ptr> method_ids_; - std::unique_ptr> field_ids_; + std::vector type_ids_; + std::vector string_ids_; + std::vector method_ids_; + std::vector field_ids_; DISALLOW_COPY_AND_ASSIGN(CodeFixups); }; diff --git a/dexlayout/dex_ir_builder.cc b/dexlayout/dex_ir_builder.cc index bd3e1fa718d72d11a25120e4e2b11b2f0ad28a0f..1fd963fe229d26926c6175c28efde812edb114b7 100644 --- a/dexlayout/dex_ir_builder.cc +++ b/dexlayout/dex_ir_builder.cc @@ -26,7 +26,7 @@ namespace dex_ir { static void CheckAndSetRemainingOffsets(const DexFile& dex_file, Collections* collections); -Header* DexIrBuilder(const DexFile& dex_file) { +Header* DexIrBuilder(const DexFile& dex_file, bool eagerly_assign_offsets) { const DexFile::Header& disk_header = dex_file.GetHeader(); Header* header = new Header(disk_header.magic_, disk_header.checksum_, @@ -39,6 +39,7 @@ Header* DexIrBuilder(const DexFile& dex_file) { disk_header.data_size_, disk_header.data_off_); Collections& collections = header->GetCollections(); + collections.SetEagerlyAssignOffsets(eagerly_assign_offsets); // Walk the rest of the header fields. // StringId table. collections.SetStringIdsOffset(disk_header.string_ids_off_); @@ -74,9 +75,16 @@ Header* DexIrBuilder(const DexFile& dex_file) { collections.SetMapListOffset(disk_header.map_off_); // CallSiteIds and MethodHandleItems. collections.CreateCallSitesAndMethodHandles(dex_file); - CheckAndSetRemainingOffsets(dex_file, &collections); + // Sort the vectors by the map order (same order as the file). + collections.SortVectorsByMapOrder(); + + // Load the link data if it exists. + collections.SetLinkData(std::vector( + dex_file.Begin() + dex_file.GetHeader().link_off_, + dex_file.Begin() + dex_file.GetHeader().link_off_ + dex_file.GetHeader().link_size_)); + return header; } diff --git a/dexlayout/dex_ir_builder.h b/dexlayout/dex_ir_builder.h index c53157b5fc2cfe45a45d228a6f067978bae23ce1..4d4b4e8699bb696c2b75afcaaf7b62e995772735 100644 --- a/dexlayout/dex_ir_builder.h +++ b/dexlayout/dex_ir_builder.h @@ -24,7 +24,9 @@ namespace art { namespace dex_ir { -dex_ir::Header* DexIrBuilder(const DexFile& dex_file); +// Eagerly assign offsets assigns offsets based on the original offsets in the input dex file. If +// this not done, dex_ir::Item::GetOffset will abort when reading uninitialized offsets. +dex_ir::Header* DexIrBuilder(const DexFile& dex_file, bool eagerly_assign_offsets); } // namespace dex_ir } // namespace art diff --git a/dexlayout/dex_visualize.cc b/dexlayout/dex_visualize.cc index 4b46341adab8444b211ff64870a3bb0b6fa73775..e4ed69b8d2688d4786b919e2239918fb294e7a12 100644 --- a/dexlayout/dex_visualize.cc +++ b/dexlayout/dex_visualize.cc @@ -188,20 +188,16 @@ class Dumper { DumpAddressRange(code_item, class_index); const dex_ir::CodeFixups* fixups = code_item->GetCodeFixups(); if (fixups != nullptr) { - std::vector* type_ids = fixups->TypeIds(); - for (dex_ir::TypeId* type_id : *type_ids) { + for (dex_ir::TypeId* type_id : fixups->TypeIds()) { DumpTypeId(type_id, class_index); } - std::vector* string_ids = fixups->StringIds(); - for (dex_ir::StringId* string_id : *string_ids) { + for (dex_ir::StringId* string_id : fixups->StringIds()) { DumpStringId(string_id, class_index); } - std::vector* method_ids = fixups->MethodIds(); - for (dex_ir::MethodId* method_id : *method_ids) { + for (dex_ir::MethodId* method_id : fixups->MethodIds()) { DumpMethodId(method_id, class_index); } - std::vector* field_ids = fixups->FieldIds(); - for (dex_ir::FieldId* field_id : *field_ids) { + for (dex_ir::FieldId* field_id : fixups->FieldIds()) { DumpFieldId(field_id, class_index); } } diff --git a/dexlayout/dex_writer.cc b/dexlayout/dex_writer.cc index 4895ab6957913de596706c1c665ba5723bb00a0b..4fd41573911fbf34fe9e8e32bed5ba6611b04bf4 100644 --- a/dexlayout/dex_writer.cc +++ b/dexlayout/dex_writer.cc @@ -18,17 +18,37 @@ #include -#include #include #include "cdex/compact_dex_file.h" #include "compact_dex_writer.h" +#include "dex_file_layout.h" #include "dex_file_types.h" +#include "dexlayout.h" #include "standard_dex_file.h" #include "utf.h" namespace art { +static constexpr uint32_t kDataSectionAlignment = sizeof(uint32_t) * 2; +static constexpr uint32_t kDexSectionWordAlignment = 4; + +static constexpr uint32_t SectionAlignment(DexFile::MapItemType type) { + switch (type) { + case DexFile::kDexTypeClassDataItem: + case DexFile::kDexTypeStringDataItem: + case DexFile::kDexTypeDebugInfoItem: + case DexFile::kDexTypeAnnotationItem: + case DexFile::kDexTypeEncodedArrayItem: + return alignof(uint8_t); + + default: + // All other sections are kDexAlignedSection. + return kDexSectionWordAlignment; + } +} + + size_t EncodeIntValue(int32_t value, uint8_t* buffer) { size_t length = 0; if (value >= 0) { @@ -245,130 +265,213 @@ size_t DexWriter::WriteEncodedMethods(dex_ir::MethodItemVector* methods, size_t return offset - original_offset; } -void DexWriter::WriteStrings() { - uint32_t string_data_off[1]; +// TODO: Refactor this to remove duplicated boiler plate. One way to do this is adding +// function that takes a CollectionVector and uses overloading. +uint32_t DexWriter::WriteStringIds(uint32_t offset, bool reserve_only) { + const uint32_t start = offset; for (std::unique_ptr& string_id : header_->GetCollections().StringIds()) { - string_data_off[0] = string_id->DataItem()->GetOffset(); - Write(string_data_off, string_id->GetSize(), string_id->GetOffset()); + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeStringIdItem)); + if (reserve_only) { + offset += string_id->GetSize(); + } else { + uint32_t string_data_off = string_id->DataItem()->GetOffset(); + offset += Write(&string_data_off, string_id->GetSize(), offset); + } } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetStringIdsOffset(start); + } + return offset - start; +} - for (auto& string_data_pair : header_->GetCollections().StringDatas()) { - std::unique_ptr& string_data = string_data_pair.second; - uint32_t offset = string_data->GetOffset(); +uint32_t DexWriter::WriteStringDatas(uint32_t offset) { + const uint32_t start = offset; + for (std::unique_ptr& string_data : header_->GetCollections().StringDatas()) { + ProcessOffset(&offset, string_data.get()); + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeStringDataItem)); offset += WriteUleb128(CountModifiedUtf8Chars(string_data->Data()), offset); - Write(string_data->Data(), strlen(string_data->Data()), offset); + // Skip null terminator (already zeroed out, no need to write). + offset += Write(string_data->Data(), strlen(string_data->Data()), offset) + 1u; + } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetStringDatasOffset(start); } + return offset - start; } -void DexWriter::WriteTypes() { +uint32_t DexWriter::WriteTypeIds(uint32_t offset) { uint32_t descriptor_idx[1]; + const uint32_t start = offset; for (std::unique_ptr& type_id : header_->GetCollections().TypeIds()) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeTypeIdItem)); + ProcessOffset(&offset, type_id.get()); descriptor_idx[0] = type_id->GetStringId()->GetIndex(); - Write(descriptor_idx, type_id->GetSize(), type_id->GetOffset()); + offset += Write(descriptor_idx, type_id->GetSize(), offset); } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetTypeIdsOffset(start); + } + return offset - start; } -void DexWriter::WriteTypeLists() { +uint32_t DexWriter::WriteTypeLists(uint32_t offset) { uint32_t size[1]; uint16_t list[1]; - for (auto& type_list_pair : header_->GetCollections().TypeLists()) { - std::unique_ptr& type_list = type_list_pair.second; + const uint32_t start = offset; + for (std::unique_ptr& type_list : header_->GetCollections().TypeLists()) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeTypeList)); size[0] = type_list->GetTypeList()->size(); - uint32_t offset = type_list->GetOffset(); + ProcessOffset(&offset, type_list.get()); offset += Write(size, sizeof(uint32_t), offset); for (const dex_ir::TypeId* type_id : *type_list->GetTypeList()) { list[0] = type_id->GetIndex(); offset += Write(list, sizeof(uint16_t), offset); } } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetTypeListsOffset(start); + } + return offset - start; } -void DexWriter::WriteProtos() { +uint32_t DexWriter::WriteProtoIds(uint32_t offset, bool reserve_only) { uint32_t buffer[3]; + const uint32_t start = offset; for (std::unique_ptr& proto_id : header_->GetCollections().ProtoIds()) { - buffer[0] = proto_id->Shorty()->GetIndex(); - buffer[1] = proto_id->ReturnType()->GetIndex(); - buffer[2] = proto_id->Parameters() == nullptr ? 0 : proto_id->Parameters()->GetOffset(); - Write(buffer, proto_id->GetSize(), proto_id->GetOffset()); + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeProtoIdItem)); + ProcessOffset(&offset, proto_id.get()); + if (reserve_only) { + offset += proto_id->GetSize(); + } else { + buffer[0] = proto_id->Shorty()->GetIndex(); + buffer[1] = proto_id->ReturnType()->GetIndex(); + buffer[2] = proto_id->Parameters() == nullptr ? 0 : proto_id->Parameters()->GetOffset(); + offset += Write(buffer, proto_id->GetSize(), offset); + } } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetProtoIdsOffset(start); + } + return offset - start; } -void DexWriter::WriteFields() { +uint32_t DexWriter::WriteFieldIds(uint32_t offset) { uint16_t buffer[4]; + const uint32_t start = offset; for (std::unique_ptr& field_id : header_->GetCollections().FieldIds()) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeFieldIdItem)); + ProcessOffset(&offset, field_id.get()); buffer[0] = field_id->Class()->GetIndex(); buffer[1] = field_id->Type()->GetIndex(); buffer[2] = field_id->Name()->GetIndex(); buffer[3] = field_id->Name()->GetIndex() >> 16; - Write(buffer, field_id->GetSize(), field_id->GetOffset()); + offset += Write(buffer, field_id->GetSize(), offset); + } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetFieldIdsOffset(start); } + return offset - start; } -void DexWriter::WriteMethods() { +uint32_t DexWriter::WriteMethodIds(uint32_t offset) { uint16_t buffer[4]; + const uint32_t start = offset; for (std::unique_ptr& method_id : header_->GetCollections().MethodIds()) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeMethodIdItem)); + ProcessOffset(&offset, method_id.get()); buffer[0] = method_id->Class()->GetIndex(); buffer[1] = method_id->Proto()->GetIndex(); buffer[2] = method_id->Name()->GetIndex(); buffer[3] = method_id->Name()->GetIndex() >> 16; - Write(buffer, method_id->GetSize(), method_id->GetOffset()); + offset += Write(buffer, method_id->GetSize(), offset); + } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetMethodIdsOffset(start); } + return offset - start; } -void DexWriter::WriteEncodedArrays() { - for (auto& encoded_array_pair : header_->GetCollections().EncodedArrayItems()) { - std::unique_ptr& encoded_array = encoded_array_pair.second; - WriteEncodedArray(encoded_array->GetEncodedValues(), encoded_array->GetOffset()); +uint32_t DexWriter::WriteEncodedArrays(uint32_t offset) { + const uint32_t start = offset; + for (std::unique_ptr& encoded_array : + header_->GetCollections().EncodedArrayItems()) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeEncodedArrayItem)); + ProcessOffset(&offset, encoded_array.get()); + offset += WriteEncodedArray(encoded_array->GetEncodedValues(), offset); } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetEncodedArrayItemsOffset(start); + } + return offset - start; } -void DexWriter::WriteAnnotations() { +uint32_t DexWriter::WriteAnnotations(uint32_t offset) { uint8_t visibility[1]; - for (auto& annotation_pair : header_->GetCollections().AnnotationItems()) { - std::unique_ptr& annotation = annotation_pair.second; + const uint32_t start = offset; + for (std::unique_ptr& annotation : + header_->GetCollections().AnnotationItems()) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeAnnotationItem)); visibility[0] = annotation->GetVisibility(); - size_t offset = annotation->GetOffset(); + ProcessOffset(&offset, annotation.get()); offset += Write(visibility, sizeof(uint8_t), offset); - WriteEncodedAnnotation(annotation->GetAnnotation(), offset); + offset += WriteEncodedAnnotation(annotation->GetAnnotation(), offset); + } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetAnnotationItemsOffset(start); } + return offset - start; } -void DexWriter::WriteAnnotationSets() { +uint32_t DexWriter::WriteAnnotationSets(uint32_t offset) { uint32_t size[1]; uint32_t annotation_off[1]; - for (auto& annotation_set_pair : header_->GetCollections().AnnotationSetItems()) { - std::unique_ptr& annotation_set = annotation_set_pair.second; + const uint32_t start = offset; + for (std::unique_ptr& annotation_set : + header_->GetCollections().AnnotationSetItems()) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeAnnotationSetItem)); size[0] = annotation_set->GetItems()->size(); - size_t offset = annotation_set->GetOffset(); + ProcessOffset(&offset, annotation_set.get()); offset += Write(size, sizeof(uint32_t), offset); for (dex_ir::AnnotationItem* annotation : *annotation_set->GetItems()) { annotation_off[0] = annotation->GetOffset(); offset += Write(annotation_off, sizeof(uint32_t), offset); } } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetAnnotationSetItemsOffset(start); + } + return offset - start; } -void DexWriter::WriteAnnotationSetRefs() { +uint32_t DexWriter::WriteAnnotationSetRefs(uint32_t offset) { uint32_t size[1]; uint32_t annotations_off[1]; - for (auto& anno_set_ref_pair : header_->GetCollections().AnnotationSetRefLists()) { - std::unique_ptr& annotation_set_ref = anno_set_ref_pair.second; + const uint32_t start = offset; + for (std::unique_ptr& annotation_set_ref : + header_->GetCollections().AnnotationSetRefLists()) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeAnnotationSetRefList)); size[0] = annotation_set_ref->GetItems()->size(); - size_t offset = annotation_set_ref->GetOffset(); + ProcessOffset(&offset, annotation_set_ref.get()); offset += Write(size, sizeof(uint32_t), offset); for (dex_ir::AnnotationSetItem* annotation_set : *annotation_set_ref->GetItems()) { annotations_off[0] = annotation_set == nullptr ? 0 : annotation_set->GetOffset(); offset += Write(annotations_off, sizeof(uint32_t), offset); } } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetAnnotationSetRefListsOffset(start); + } + return offset - start; } -void DexWriter::WriteAnnotationsDirectories() { +uint32_t DexWriter::WriteAnnotationsDirectories(uint32_t offset) { uint32_t directory_buffer[4]; uint32_t annotation_buffer[2]; - for (auto& annotations_directory_pair : header_->GetCollections().AnnotationsDirectoryItems()) { - std::unique_ptr& annotations_directory = - annotations_directory_pair.second; + const uint32_t start = offset; + for (std::unique_ptr& annotations_directory : + header_->GetCollections().AnnotationsDirectoryItems()) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeAnnotationsDirectoryItem)); + ProcessOffset(&offset, annotations_directory.get()); directory_buffer[0] = annotations_directory->GetClassAnnotation() == nullptr ? 0 : annotations_directory->GetClassAnnotation()->GetOffset(); directory_buffer[1] = annotations_directory->GetFieldAnnotations() == nullptr ? 0 : @@ -377,7 +480,6 @@ void DexWriter::WriteAnnotationsDirectories() { annotations_directory->GetMethodAnnotations()->size(); directory_buffer[3] = annotations_directory->GetParameterAnnotations() == nullptr ? 0 : annotations_directory->GetParameterAnnotations()->size(); - uint32_t offset = annotations_directory->GetOffset(); offset += Write(directory_buffer, 4 * sizeof(uint32_t), offset); if (annotations_directory->GetFieldAnnotations() != nullptr) { for (std::unique_ptr& field : @@ -404,27 +506,55 @@ void DexWriter::WriteAnnotationsDirectories() { } } } -} - -void DexWriter::WriteDebugInfoItems() { - for (auto& debug_info_pair : header_->GetCollections().DebugInfoItems()) { - std::unique_ptr& debug_info = debug_info_pair.second; - Write(debug_info->GetDebugInfo(), debug_info->GetDebugInfoSize(), debug_info->GetOffset()); - } -} - -void DexWriter::WriteCodeItems() { - uint16_t uint16_buffer[4]; - uint32_t uint32_buffer[2]; - for (auto& code_item_pair : header_->GetCollections().CodeItems()) { - std::unique_ptr& code_item = code_item_pair.second; - uint16_buffer[0] = code_item->RegistersSize(); - uint16_buffer[1] = code_item->InsSize(); - uint16_buffer[2] = code_item->OutsSize(); - uint16_buffer[3] = code_item->TriesSize(); - uint32_buffer[0] = code_item->DebugInfo() == nullptr ? 0 : code_item->DebugInfo()->GetOffset(); - uint32_buffer[1] = code_item->InsnsSize(); - size_t offset = code_item->GetOffset(); + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetAnnotationsDirectoryItemsOffset(start); + } + return offset - start; +} + +uint32_t DexWriter::WriteDebugInfoItems(uint32_t offset) { + const uint32_t start = offset; + for (std::unique_ptr& debug_info : + header_->GetCollections().DebugInfoItems()) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeDebugInfoItem)); + ProcessOffset(&offset, debug_info.get()); + offset += Write(debug_info->GetDebugInfo(), debug_info->GetDebugInfoSize(), offset); + } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetDebugInfoItemsOffset(start); + } + return offset - start; +} + +uint32_t DexWriter::WriteCodeItems(uint32_t offset, bool reserve_only) { + DexLayoutSection* code_section = nullptr; + if (!reserve_only && dex_layout_ != nullptr) { + code_section = &dex_layout_->GetSections().sections_[static_cast( + DexLayoutSections::SectionType::kSectionTypeCode)]; + } + uint16_t uint16_buffer[4] = {}; + uint32_t uint32_buffer[2] = {}; + uint32_t start = offset; + for (auto& code_item : header_->GetCollections().CodeItems()) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeCodeItem)); + ProcessOffset(&offset, code_item.get()); + if (!reserve_only) { + uint16_buffer[0] = code_item->RegistersSize(); + uint16_buffer[1] = code_item->InsSize(); + uint16_buffer[2] = code_item->OutsSize(); + uint16_buffer[3] = code_item->TriesSize(); + uint32_buffer[0] = code_item->DebugInfo() == nullptr ? 0 : + code_item->DebugInfo()->GetOffset(); + uint32_buffer[1] = code_item->InsnsSize(); + // Only add the section hotness info once. + if (code_section != nullptr) { + auto it = dex_layout_->LayoutHotnessInfo().code_item_layout_.find(code_item.get()); + if (it != dex_layout_->LayoutHotnessInfo().code_item_layout_.end()) { + code_section->parts_[static_cast(it->second)].CombineSection( + code_item->GetOffset(), code_item->GetOffset() + code_item->GetSize()); + } + } + } offset += Write(uint16_buffer, 4 * sizeof(uint16_t), offset); offset += Write(uint32_buffer, 2 * sizeof(uint32_t), offset); offset += Write(code_item->Insns(), code_item->InsnsSize() * sizeof(uint16_t), offset); @@ -443,7 +573,7 @@ void DexWriter::WriteCodeItems() { offset += Write(insn_count_and_handler_off, 2 * sizeof(uint16_t), offset); } // Leave offset pointing to the end of the try items. - WriteUleb128(code_item->Handlers()->size(), offset); + UNUSED(WriteUleb128(code_item->Handlers()->size(), offset)); for (std::unique_ptr& handlers : *code_item->Handlers()) { size_t list_offset = offset + handlers->GetListOffset(); uint32_t size = handlers->HasCatchAll() ? (handlers->GetHandlers()->size() - 1) * -1 : @@ -457,32 +587,52 @@ void DexWriter::WriteCodeItems() { } } } + // TODO: Clean this up to properly calculate the size instead of assuming it doesn't change. + offset = code_item->GetOffset() + code_item->GetSize(); + } + + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetCodeItemsOffset(start); } + return offset - start; } -void DexWriter::WriteClasses() { +uint32_t DexWriter::WriteClassDefs(uint32_t offset, bool reserve_only) { + const uint32_t start = offset; uint32_t class_def_buffer[8]; for (std::unique_ptr& class_def : header_->GetCollections().ClassDefs()) { - class_def_buffer[0] = class_def->ClassType()->GetIndex(); - class_def_buffer[1] = class_def->GetAccessFlags(); - class_def_buffer[2] = class_def->Superclass() == nullptr ? dex::kDexNoIndex : - class_def->Superclass()->GetIndex(); - class_def_buffer[3] = class_def->InterfacesOffset(); - class_def_buffer[4] = class_def->SourceFile() == nullptr ? dex::kDexNoIndex : - class_def->SourceFile()->GetIndex(); - class_def_buffer[5] = class_def->Annotations() == nullptr ? 0 : - class_def->Annotations()->GetOffset(); - class_def_buffer[6] = class_def->GetClassData() == nullptr ? 0 : - class_def->GetClassData()->GetOffset(); - class_def_buffer[7] = class_def->StaticValues() == nullptr ? 0 : - class_def->StaticValues()->GetOffset(); - size_t offset = class_def->GetOffset(); - Write(class_def_buffer, class_def->GetSize(), offset); - } - - for (auto& class_data_pair : header_->GetCollections().ClassDatas()) { - std::unique_ptr& class_data = class_data_pair.second; - size_t offset = class_data->GetOffset(); + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeClassDefItem)); + if (reserve_only) { + offset += class_def->GetSize(); + } else { + class_def_buffer[0] = class_def->ClassType()->GetIndex(); + class_def_buffer[1] = class_def->GetAccessFlags(); + class_def_buffer[2] = class_def->Superclass() == nullptr ? dex::kDexNoIndex : + class_def->Superclass()->GetIndex(); + class_def_buffer[3] = class_def->InterfacesOffset(); + class_def_buffer[4] = class_def->SourceFile() == nullptr ? dex::kDexNoIndex : + class_def->SourceFile()->GetIndex(); + class_def_buffer[5] = class_def->Annotations() == nullptr ? 0 : + class_def->Annotations()->GetOffset(); + class_def_buffer[6] = class_def->GetClassData() == nullptr ? 0 : + class_def->GetClassData()->GetOffset(); + class_def_buffer[7] = class_def->StaticValues() == nullptr ? 0 : + class_def->StaticValues()->GetOffset(); + offset += Write(class_def_buffer, class_def->GetSize(), offset); + } + } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetClassDefsOffset(start); + } + return offset - start; +} + +uint32_t DexWriter::WriteClassDatas(uint32_t offset) { + const uint32_t start = offset; + for (const std::unique_ptr& class_data : + header_->GetCollections().ClassDatas()) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeClassDataItem)); + ProcessOffset(&offset, class_data.get()); offset += WriteUleb128(class_data->StaticFields()->size(), offset); offset += WriteUleb128(class_data->InstanceFields()->size(), offset); offset += WriteUleb128(class_data->DirectMethods()->size(), offset); @@ -492,139 +642,134 @@ void DexWriter::WriteClasses() { offset += WriteEncodedMethods(class_data->DirectMethods(), offset); offset += WriteEncodedMethods(class_data->VirtualMethods(), offset); } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetClassDatasOffset(start); + } + return offset - start; } -void DexWriter::WriteCallSites() { +uint32_t DexWriter::WriteCallSiteIds(uint32_t offset, bool reserve_only) { + const uint32_t start = offset; uint32_t call_site_off[1]; for (std::unique_ptr& call_site_id : header_->GetCollections().CallSiteIds()) { - call_site_off[0] = call_site_id->CallSiteItem()->GetOffset(); - Write(call_site_off, call_site_id->GetSize(), call_site_id->GetOffset()); + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeCallSiteIdItem)); + if (reserve_only) { + offset += call_site_id->GetSize(); + } else { + call_site_off[0] = call_site_id->CallSiteItem()->GetOffset(); + offset += Write(call_site_off, call_site_id->GetSize(), offset); + } + } + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetCallSiteIdsOffset(start); } + return offset - start; } -void DexWriter::WriteMethodHandles() { +uint32_t DexWriter::WriteMethodHandles(uint32_t offset) { + const uint32_t start = offset; uint16_t method_handle_buff[4]; for (std::unique_ptr& method_handle : header_->GetCollections().MethodHandleItems()) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeMethodHandleItem)); method_handle_buff[0] = static_cast(method_handle->GetMethodHandleType()); method_handle_buff[1] = 0; // unused. method_handle_buff[2] = method_handle->GetFieldOrMethodId()->GetIndex(); method_handle_buff[3] = 0; // unused. - Write(method_handle_buff, method_handle->GetSize(), method_handle->GetOffset()); + offset += Write(method_handle_buff, method_handle->GetSize(), offset); } -} - -struct MapItemContainer { - MapItemContainer(uint32_t type, uint32_t size, uint32_t offset) - : type_(type), size_(size), offset_(offset) { } - - bool operator<(const MapItemContainer& other) const { - return offset_ > other.offset_; - } - - uint32_t type_; - uint32_t size_; - uint32_t offset_; -}; - -void DexWriter::WriteMapItem() { - dex_ir::Collections& collection = header_->GetCollections(); - std::priority_queue queue; - - // Header and index section. - queue.push(MapItemContainer(DexFile::kDexTypeHeaderItem, 1, 0)); - if (collection.StringIdsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeStringIdItem, collection.StringIdsSize(), - collection.StringIdsOffset())); - } - if (collection.TypeIdsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeTypeIdItem, collection.TypeIdsSize(), - collection.TypeIdsOffset())); - } - if (collection.ProtoIdsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeProtoIdItem, collection.ProtoIdsSize(), - collection.ProtoIdsOffset())); - } - if (collection.FieldIdsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeFieldIdItem, collection.FieldIdsSize(), - collection.FieldIdsOffset())); - } - if (collection.MethodIdsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeMethodIdItem, collection.MethodIdsSize(), - collection.MethodIdsOffset())); - } - if (collection.ClassDefsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeClassDefItem, collection.ClassDefsSize(), - collection.ClassDefsOffset())); - } - if (collection.CallSiteIdsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeCallSiteIdItem, collection.CallSiteIdsSize(), - collection.CallSiteIdsOffset())); - } - if (collection.MethodHandleItemsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeMethodHandleItem, - collection.MethodHandleItemsSize(), collection.MethodHandleItemsOffset())); - } - - // Data section. - queue.push(MapItemContainer(DexFile::kDexTypeMapList, 1, collection.MapListOffset())); - if (collection.TypeListsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeTypeList, collection.TypeListsSize(), - collection.TypeListsOffset())); - } - if (collection.AnnotationSetRefListsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeAnnotationSetRefList, - collection.AnnotationSetRefListsSize(), collection.AnnotationSetRefListsOffset())); - } - if (collection.AnnotationSetItemsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeAnnotationSetItem, - collection.AnnotationSetItemsSize(), collection.AnnotationSetItemsOffset())); - } - if (collection.ClassDatasSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeClassDataItem, collection.ClassDatasSize(), - collection.ClassDatasOffset())); - } - if (collection.CodeItemsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeCodeItem, collection.CodeItemsSize(), - collection.CodeItemsOffset())); - } - if (collection.StringDatasSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeStringDataItem, collection.StringDatasSize(), - collection.StringDatasOffset())); - } - if (collection.DebugInfoItemsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeDebugInfoItem, collection.DebugInfoItemsSize(), - collection.DebugInfoItemsOffset())); - } - if (collection.AnnotationItemsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeAnnotationItem, collection.AnnotationItemsSize(), - collection.AnnotationItemsOffset())); - } - if (collection.EncodedArrayItemsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeEncodedArrayItem, - collection.EncodedArrayItemsSize(), collection.EncodedArrayItemsOffset())); - } - if (collection.AnnotationsDirectoryItemsSize() != 0) { - queue.push(MapItemContainer(DexFile::kDexTypeAnnotationsDirectoryItem, - collection.AnnotationsDirectoryItemsSize(), collection.AnnotationsDirectoryItemsOffset())); + if (compute_offsets_ && start != offset) { + header_->GetCollections().SetMethodHandleItemsOffset(start); } + return offset - start; +} - uint32_t offset = collection.MapListOffset(); +uint32_t DexWriter::WriteMapItems(uint32_t offset, MapItemQueue* queue) { + // All the sections should already have been added. uint16_t uint16_buffer[2]; uint32_t uint32_buffer[2]; uint16_buffer[1] = 0; - uint32_buffer[0] = queue.size(); + uint32_buffer[0] = queue->size(); + const uint32_t start = offset; offset += Write(uint32_buffer, sizeof(uint32_t), offset); - while (!queue.empty()) { - const MapItemContainer& map_item = queue.top(); + while (!queue->empty()) { + const MapItem& map_item = queue->top(); uint16_buffer[0] = map_item.type_; uint32_buffer[0] = map_item.size_; uint32_buffer[1] = map_item.offset_; offset += Write(uint16_buffer, 2 * sizeof(uint16_t), offset); offset += Write(uint32_buffer, 2 * sizeof(uint32_t), offset); - queue.pop(); + queue->pop(); } + return offset - start; +} + +uint32_t DexWriter::GenerateAndWriteMapItems(uint32_t offset) { + dex_ir::Collections& collection = header_->GetCollections(); + MapItemQueue queue; + + // Header and index section. + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeHeaderItem, 1, 0)); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeStringIdItem, + collection.StringIdsSize(), + collection.StringIdsOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeTypeIdItem, + collection.TypeIdsSize(), + collection.TypeIdsOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeProtoIdItem, + collection.ProtoIdsSize(), + collection.ProtoIdsOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeFieldIdItem, + collection.FieldIdsSize(), + collection.FieldIdsOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeMethodIdItem, + collection.MethodIdsSize(), + collection.MethodIdsOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeClassDefItem, + collection.ClassDefsSize(), + collection.ClassDefsOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeCallSiteIdItem, + collection.CallSiteIdsSize(), + collection.CallSiteIdsOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeMethodHandleItem, + collection.MethodHandleItemsSize(), + collection.MethodHandleItemsOffset())); + // Data section. + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeMapList, 1, collection.MapListOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeTypeList, + collection.TypeListsSize(), + collection.TypeListsOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeAnnotationSetRefList, + collection.AnnotationSetRefListsSize(), + collection.AnnotationSetRefListsOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeAnnotationSetItem, + collection.AnnotationSetItemsSize(), + collection.AnnotationSetItemsOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeClassDataItem, + collection.ClassDatasSize(), + collection.ClassDatasOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeCodeItem, + collection.CodeItemsSize(), + collection.CodeItemsOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeStringDataItem, + collection.StringDatasSize(), + collection.StringDatasOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeDebugInfoItem, + collection.DebugInfoItemsSize(), + collection.DebugInfoItemsOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeAnnotationItem, + collection.AnnotationItemsSize(), + collection.AnnotationItemsOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeEncodedArrayItem, + collection.EncodedArrayItemsSize(), + collection.EncodedArrayItemsOffset())); + queue.AddIfNotEmpty(MapItem(DexFile::kDexTypeAnnotationsDirectoryItem, + collection.AnnotationsDirectoryItemsSize(), + collection.AnnotationsDirectoryItemsOffset())); + + // Write the map items. + return WriteMapItems(offset, &queue); } void DexWriter::WriteHeader() { @@ -657,38 +802,124 @@ void DexWriter::WriteHeader() { header.data_off_ = header_->DataOffset(); static_assert(sizeof(header) == 0x70, "Size doesn't match dex spec"); - Write(reinterpret_cast(&header), sizeof(header), 0u); + UNUSED(Write(reinterpret_cast(&header), sizeof(header), 0u)); } void DexWriter::WriteMemMap() { - WriteStrings(); - WriteTypes(); - WriteTypeLists(); - WriteProtos(); - WriteFields(); - WriteMethods(); - WriteEncodedArrays(); - WriteAnnotations(); - WriteAnnotationSets(); - WriteAnnotationSetRefs(); - WriteAnnotationsDirectories(); - WriteDebugInfoItems(); - WriteCodeItems(); - WriteClasses(); - WriteCallSites(); - WriteMethodHandles(); - WriteMapItem(); + // Starting offset is right after the header. + uint32_t offset = sizeof(StandardDexFile::Header); + + dex_ir::Collections& collection = header_->GetCollections(); + + // Based on: https://source.android.com/devices/tech/dalvik/dex-format + // Since the offsets may not be calculated already, the writing must be done in the correct order. + const uint32_t string_ids_offset = offset; + offset += WriteStringIds(offset, /*reserve_only*/ true); + offset += WriteTypeIds(offset); + const uint32_t proto_ids_offset = offset; + offset += WriteProtoIds(offset, /*reserve_only*/ true); + offset += WriteFieldIds(offset); + offset += WriteMethodIds(offset); + const uint32_t class_defs_offset = offset; + offset += WriteClassDefs(offset, /*reserve_only*/ true); + const uint32_t call_site_ids_offset = offset; + offset += WriteCallSiteIds(offset, /*reserve_only*/ true); + offset += WriteMethodHandles(offset); + + uint32_t data_offset_ = 0u; + if (compute_offsets_) { + // Data section. + offset = RoundUp(offset, kDataSectionAlignment); + data_offset_ = offset; + } + + // Write code item first to minimize the space required for encoded methods. + // Reserve code item space since we need the debug offsets to actually write them. + const uint32_t code_items_offset = offset; + offset += WriteCodeItems(offset, /*reserve_only*/ true); + // Write debug info section. + offset += WriteDebugInfoItems(offset); + // Actually write code items since debug info offsets are calculated now. + WriteCodeItems(code_items_offset, /*reserve_only*/ false); + + offset += WriteEncodedArrays(offset); + offset += WriteAnnotations(offset); + offset += WriteAnnotationSets(offset); + offset += WriteAnnotationSetRefs(offset); + offset += WriteAnnotationsDirectories(offset); + offset += WriteTypeLists(offset); + offset += WriteClassDatas(offset); + offset += WriteStringDatas(offset); + + // Write delayed id sections that depend on data sections. + WriteStringIds(string_ids_offset, /*reserve_only*/ false); + WriteProtoIds(proto_ids_offset, /*reserve_only*/ false); + WriteClassDefs(class_defs_offset, /*reserve_only*/ false); + WriteCallSiteIds(call_site_ids_offset, /*reserve_only*/ false); + + // Write the map list. + if (compute_offsets_) { + offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeMapList)); + collection.SetMapListOffset(offset); + } else { + offset = collection.MapListOffset(); + } + offset += GenerateAndWriteMapItems(offset); + offset = RoundUp(offset, kDataSectionAlignment); + + // Map items are included in the data section. + if (compute_offsets_) { + header_->SetDataSize(offset - data_offset_); + if (header_->DataSize() != 0) { + // Offset must be zero when the size is zero. + header_->SetDataOffset(data_offset_); + } else { + header_->SetDataOffset(0u); + } + } + + // Write link data if it exists. + const std::vector& link_data = collection.LinkData(); + if (link_data.size() > 0) { + CHECK_EQ(header_->LinkSize(), static_cast(link_data.size())); + if (compute_offsets_) { + header_->SetLinkOffset(offset); + } + offset += Write(&link_data[0], link_data.size(), header_->LinkOffset()); + } + + // Write header last. + if (compute_offsets_) { + header_->SetFileSize(offset); + } WriteHeader(); + + if (dex_layout_->GetOptions().update_checksum_) { + header_->SetChecksum(DexFile::CalculateChecksum(mem_map_->Begin(), offset)); + // Rewrite the header with the calculated checksum. + WriteHeader(); + } } -void DexWriter::Output(dex_ir::Header* header, MemMap* mem_map, CompactDexLevel compact_dex_level) { +void DexWriter::Output(dex_ir::Header* header, + MemMap* mem_map, + DexLayout* dex_layout, + bool compute_offsets, + CompactDexLevel compact_dex_level) { + CHECK(dex_layout != nullptr); std::unique_ptr writer; if (compact_dex_level != CompactDexLevel::kCompactDexLevelNone) { - writer.reset(new CompactDexWriter(header, mem_map, compact_dex_level)); + writer.reset(new CompactDexWriter(header, mem_map, dex_layout, compact_dex_level)); } else { - writer.reset(new DexWriter(header, mem_map)); + writer.reset(new DexWriter(header, mem_map, dex_layout, compute_offsets)); } writer->WriteMemMap(); } +void MapItemQueue::AddIfNotEmpty(const MapItem& item) { + if (item.size_ != 0) { + push(item); + } +} + } // namespace art diff --git a/dexlayout/dex_writer.h b/dexlayout/dex_writer.h index 85d3e7ebf3e4433cbe6f2ce45b58d9dc573630fc..c47898e5338117c5db9b11dc6076f6ac2095c2f6 100644 --- a/dexlayout/dex_writer.h +++ b/dexlayout/dex_writer.h @@ -19,56 +19,119 @@ #ifndef ART_DEXLAYOUT_DEX_WRITER_H_ #define ART_DEXLAYOUT_DEX_WRITER_H_ +#include + #include "base/unix_file/fd_file.h" #include "cdex/compact_dex_level.h" #include "dex_ir.h" #include "mem_map.h" #include "os.h" +#include + namespace art { +class DexLayout; +class DexLayoutHotnessInfo; + +struct MapItem { + // Not using DexFile::MapItemType since compact dex and standard dex file may have different + // sections. + MapItem() = default; + MapItem(uint32_t type, uint32_t size, uint32_t offset) + : type_(type), size_(size), offset_(offset) { } + + // Sort by decreasing order since the priority_queue puts largest elements first. + bool operator>(const MapItem& other) const { + return offset_ > other.offset_; + } + + uint32_t type_ = 0u; + uint32_t size_ = 0u; + uint32_t offset_ = 0u; +}; + +class MapItemQueue : public + std::priority_queue, std::greater> { + public: + void AddIfNotEmpty(const MapItem& item); +}; + class DexWriter { public: - DexWriter(dex_ir::Header* header, MemMap* mem_map) : header_(header), mem_map_(mem_map) {} + DexWriter(dex_ir::Header* header, + MemMap* mem_map, + DexLayout* dex_layout, + bool compute_offsets) + : header_(header), + mem_map_(mem_map), + dex_layout_(dex_layout), + compute_offsets_(compute_offsets) {} - static void Output(dex_ir::Header* header, MemMap* mem_map, CompactDexLevel compact_dex_level); + static void Output(dex_ir::Header* header, + MemMap* mem_map, + DexLayout* dex_layout, + bool compute_offsets, + CompactDexLevel compact_dex_level); virtual ~DexWriter() {} protected: void WriteMemMap(); - size_t Write(const void* buffer, size_t length, size_t offset); - size_t WriteSleb128(uint32_t value, size_t offset); - size_t WriteUleb128(uint32_t value, size_t offset); - size_t WriteEncodedValue(dex_ir::EncodedValue* encoded_value, size_t offset); - size_t WriteEncodedValueHeader(int8_t value_type, size_t value_arg, size_t offset); - size_t WriteEncodedArray(dex_ir::EncodedValueVector* values, size_t offset); - size_t WriteEncodedAnnotation(dex_ir::EncodedAnnotation* annotation, size_t offset); - size_t WriteEncodedFields(dex_ir::FieldItemVector* fields, size_t offset); - size_t WriteEncodedMethods(dex_ir::MethodItemVector* methods, size_t offset); - - void WriteStrings(); - void WriteTypes(); - void WriteTypeLists(); - void WriteProtos(); - void WriteFields(); - void WriteMethods(); - void WriteEncodedArrays(); - void WriteAnnotations(); - void WriteAnnotationSets(); - void WriteAnnotationSetRefs(); - void WriteAnnotationsDirectories(); - void WriteDebugInfoItems(); - void WriteCodeItems(); - void WriteClasses(); - void WriteCallSites(); - void WriteMethodHandles(); - void WriteMapItem(); + size_t Write(const void* buffer, size_t length, size_t offset) WARN_UNUSED; + size_t WriteSleb128(uint32_t value, size_t offset) WARN_UNUSED; + size_t WriteUleb128(uint32_t value, size_t offset) WARN_UNUSED; + size_t WriteEncodedValue(dex_ir::EncodedValue* encoded_value, size_t offset) WARN_UNUSED; + size_t WriteEncodedValueHeader(int8_t value_type, size_t value_arg, size_t offset) WARN_UNUSED; + size_t WriteEncodedArray(dex_ir::EncodedValueVector* values, size_t offset) WARN_UNUSED; + size_t WriteEncodedAnnotation(dex_ir::EncodedAnnotation* annotation, size_t offset) WARN_UNUSED; + size_t WriteEncodedFields(dex_ir::FieldItemVector* fields, size_t offset) WARN_UNUSED; + size_t WriteEncodedMethods(dex_ir::MethodItemVector* methods, size_t offset) WARN_UNUSED; + + // Header and id section virtual void WriteHeader(); + // reserve_only means don't write, only reserve space. This is required since the string data + // offsets must be assigned. + uint32_t WriteStringIds(uint32_t offset, bool reserve_only); + uint32_t WriteTypeIds(uint32_t offset); + uint32_t WriteProtoIds(uint32_t offset, bool reserve_only); + uint32_t WriteFieldIds(uint32_t offset); + uint32_t WriteMethodIds(uint32_t offset); + uint32_t WriteClassDefs(uint32_t offset, bool reserve_only); + uint32_t WriteCallSiteIds(uint32_t offset, bool reserve_only); + + uint32_t WriteEncodedArrays(uint32_t offset); + uint32_t WriteAnnotations(uint32_t offset); + uint32_t WriteAnnotationSets(uint32_t offset); + uint32_t WriteAnnotationSetRefs(uint32_t offset); + uint32_t WriteAnnotationsDirectories(uint32_t offset); + + // Data section. + uint32_t WriteDebugInfoItems(uint32_t offset); + uint32_t WriteCodeItems(uint32_t offset, bool reserve_only); + uint32_t WriteTypeLists(uint32_t offset); + uint32_t WriteStringDatas(uint32_t offset); + uint32_t WriteClassDatas(uint32_t offset); + uint32_t WriteMethodHandles(uint32_t offset); + uint32_t WriteMapItems(uint32_t offset, MapItemQueue* queue); + uint32_t GenerateAndWriteMapItems(uint32_t offset); + + // Process an offset, if compute_offset is set, write into the dex ir item, otherwise read the + // existing offset and use that for writing. + void ProcessOffset(uint32_t* const offset, dex_ir::Item* item) { + if (compute_offsets_) { + item->SetOffset(*offset); + } else { + // Not computing offsets, just use the one in the item. + *offset = item->GetOffset(); + } + } dex_ir::Header* const header_; MemMap* const mem_map_; + DexLayout* const dex_layout_; + bool compute_offsets_; private: DISALLOW_COPY_AND_ASSIGN(DexWriter); diff --git a/dexlayout/dexdiag.cc b/dexlayout/dexdiag.cc index c14cd5f8077fdc25c6449faddcca494f9ab38a89..b2507015e96e09ca7681f9cc4a9bbb6a7ff67e31 100644 --- a/dexlayout/dexdiag.cc +++ b/dexlayout/dexdiag.cc @@ -26,6 +26,7 @@ #include "android-base/stringprintf.h" +#include "base/logging.h" // For InitLogging. #include "base/stringpiece.h" #include "dex_file.h" @@ -289,7 +290,8 @@ static void ProcessOneDexMapping(uint64_t* pagemap, // Build a list of the dex file section types, sorted from highest offset to lowest. std::vector sections; { - std::unique_ptr header(dex_ir::DexIrBuilder(*dex_file)); + std::unique_ptr header(dex_ir::DexIrBuilder(*dex_file, + /*eagerly_assign_offsets*/ true)); sections = dex_ir::GetSortedDexFileSections(header.get(), dex_ir::SortDirection::kSortDescending); } diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc index dd2e809a9269bfd7c489a7d749fbda8a20bc6b87..f87778a65bc15899d7b9a46fa46d5d3e87994a59 100644 --- a/dexlayout/dexlayout.cc +++ b/dexlayout/dexlayout.cc @@ -33,6 +33,7 @@ #include "android-base/stringprintf.h" +#include "base/logging.h" // For VLOG_IS_ON. #include "dex_file-inl.h" #include "dex_file_layout.h" #include "dex_file_loader.h" @@ -52,13 +53,6 @@ namespace art { using android::base::StringPrintf; -// Setting this to false disables class def layout entirely, which is stronger than strictly -// necessary to ensure the partial order w.r.t. class derivation. TODO: Re-enable (b/68317550). -static constexpr bool kChangeClassDefOrder = false; - -static constexpr uint32_t kDataSectionAlignment = sizeof(uint32_t) * 2; -static constexpr uint32_t kDexCodeItemAlignment = 4; - /* * Flags for use with createAccessFlagStr(). */ @@ -1564,7 +1558,7 @@ void DexLayout::DumpDexFile() { } } -std::vector DexLayout::LayoutClassDefsAndClassData(const DexFile* dex_file) { +void DexLayout::LayoutClassDefsAndClassData(const DexFile* dex_file) { std::vector new_class_def_order; for (std::unique_ptr& class_def : header_->GetCollections().ClassDefs()) { dex::TypeIndex type_idx(class_def->ClassType()->GetIndex()); @@ -1578,31 +1572,41 @@ std::vector DexLayout::LayoutClassDefsAndClassData(const Dex new_class_def_order.push_back(class_def.get()); } } - uint32_t class_defs_offset = header_->GetCollections().ClassDefsOffset(); - uint32_t class_data_offset = header_->GetCollections().ClassDatasOffset(); std::unordered_set visited_class_data; - std::vector new_class_data_order; - for (uint32_t i = 0; i < new_class_def_order.size(); ++i) { - dex_ir::ClassDef* class_def = new_class_def_order[i]; - if (kChangeClassDefOrder) { - // This produces dex files that violate the spec since the super class class_def is supposed - // to occur before any subclasses. - class_def->SetIndex(i); - class_def->SetOffset(class_defs_offset); - class_defs_offset += dex_ir::ClassDef::ItemSize(); - } + size_t class_data_index = 0; + dex_ir::CollectionVector::Vector& class_datas = + header_->GetCollections().ClassDatas(); + for (dex_ir::ClassDef* class_def : new_class_def_order) { dex_ir::ClassData* class_data = class_def->GetClassData(); if (class_data != nullptr && visited_class_data.find(class_data) == visited_class_data.end()) { - class_data->SetOffset(class_data_offset); - class_data_offset += class_data->GetSize(); visited_class_data.insert(class_data); - new_class_data_order.push_back(class_data); + // Overwrite the existing vector with the new ordering, note that the sets of objects are + // equivalent, but the order changes. This is why this is not a memory leak. + // TODO: Consider cleaning this up with a shared_ptr. + class_datas[class_data_index].release(); + class_datas[class_data_index].reset(class_data); + ++class_data_index; + } + } + CHECK_EQ(class_data_index, class_datas.size()); + + if (DexLayout::kChangeClassDefOrder) { + // This currently produces dex files that violate the spec since the super class class_def is + // supposed to occur before any subclasses. + dex_ir::CollectionVector::Vector& class_defs = + header_->GetCollections().ClassDefs(); + CHECK_EQ(new_class_def_order.size(), class_defs.size()); + for (size_t i = 0; i < class_defs.size(); ++i) { + // Overwrite the existing vector with the new ordering, note that the sets of objects are + // equivalent, but the order changes. This is why this is not a memory leak. + // TODO: Consider cleaning this up with a shared_ptr. + class_defs[i].release(); + class_defs[i].reset(new_class_def_order[i]); } } - return new_class_data_order; } -int32_t DexLayout::LayoutStringData(const DexFile* dex_file) { +void DexLayout::LayoutStringData(const DexFile* dex_file) { const size_t num_strings = header_->GetCollections().StringIds().size(); std::vector is_shorty(num_strings, false); std::vector from_hot_method(num_strings, false); @@ -1649,11 +1653,11 @@ int32_t DexLayout::LayoutStringData(const DexFile* dex_file) { continue; } // Add const-strings. - for (dex_ir::StringId* id : *fixups->StringIds()) { + for (dex_ir::StringId* id : fixups->StringIds()) { from_hot_method[id->GetIndex()] = true; } // Add field classes, names, and types. - for (dex_ir::FieldId* id : *fixups->FieldIds()) { + for (dex_ir::FieldId* id : fixups->FieldIds()) { // TODO: Only visit field ids from static getters and setters. from_hot_method[id->Class()->GetStringId()->GetIndex()] = true; from_hot_method[id->Name()->GetIndex()] = true; @@ -1661,7 +1665,7 @@ int32_t DexLayout::LayoutStringData(const DexFile* dex_file) { } // For clinits, add referenced method classes, names, and protos. if (is_clinit) { - for (dex_ir::MethodId* id : *fixups->MethodIds()) { + for (dex_ir::MethodId* id : fixups->MethodIds()) { from_hot_method[id->Class()->GetStringId()->GetIndex()] = true; from_hot_method[id->Name()->GetIndex()] = true; is_shorty[id->Proto()->Shorty()->GetIndex()] = true; @@ -1672,23 +1676,9 @@ int32_t DexLayout::LayoutStringData(const DexFile* dex_file) { } // Sort string data by specified order. std::vector string_ids; - size_t min_offset = std::numeric_limits::max(); - size_t max_offset = 0; - size_t hot_bytes = 0; for (auto& string_id : header_->GetCollections().StringIds()) { string_ids.push_back(string_id.get()); - const size_t cur_offset = string_id->DataItem()->GetOffset(); - CHECK_NE(cur_offset, 0u); - min_offset = std::min(min_offset, cur_offset); - dex_ir::StringData* data = string_id->DataItem(); - const size_t element_size = data->GetSize() + 1; // Add one extra for null. - size_t end_offset = cur_offset + element_size; - if (is_shorty[string_id->GetIndex()] || from_hot_method[string_id->GetIndex()]) { - hot_bytes += element_size; - } - max_offset = std::max(max_offset, end_offset); - } - VLOG(compiler) << "Hot string data bytes " << hot_bytes << "/" << max_offset - min_offset; + } std::sort(string_ids.begin(), string_ids.end(), [&is_shorty, &from_hot_method](const dex_ir::StringId* a, @@ -1704,59 +1694,41 @@ int32_t DexLayout::LayoutStringData(const DexFile* dex_file) { if (a_is_shorty != b_is_shorty) { return a_is_shorty < b_is_shorty; } - // Preserve order. - return a->DataItem()->GetOffset() < b->DataItem()->GetOffset(); + // Order by index by default. + return a->GetIndex() < b->GetIndex(); }); - // Now we know what order we want the string data, reorder the offsets. - size_t offset = min_offset; + dex_ir::CollectionVector::Vector& string_datas = + header_->GetCollections().StringDatas(); + // Now we know what order we want the string data, reorder them. + size_t data_index = 0; for (dex_ir::StringId* string_id : string_ids) { - dex_ir::StringData* data = string_id->DataItem(); - data->SetOffset(offset); - offset += data->GetSize() + 1; // Add one extra for null. + string_datas[data_index].release(); + string_datas[data_index].reset(string_id->DataItem()); + ++data_index; } - if (offset > max_offset) { - return offset - max_offset; - // If we expanded the string data section, we need to update the offsets or else we will - // corrupt the next section when writing out. + if (kIsDebugBuild) { + std::unordered_set visited; + for (const std::unique_ptr& data : string_datas) { + visited.insert(data.get()); + } + for (auto& string_id : header_->GetCollections().StringIds()) { + CHECK(visited.find(string_id->DataItem()) != visited.end()); + } } - return 0; + CHECK_EQ(data_index, string_datas.size()); } // Orders code items according to specified class data ordering. -// NOTE: If the section following the code items is byte aligned, the last code item is left in -// place to preserve alignment. Layout needs an overhaul to handle movement of other sections. -int32_t DexLayout::LayoutCodeItems(const DexFile* dex_file, - std::vector new_class_data_order) { - // Do not move code items if class data section precedes code item section. - // ULEB encoding is variable length, causing problems determining the offset of the code items. - // TODO: We should swap the order of these sections in the future to avoid this issue. - uint32_t class_data_offset = header_->GetCollections().ClassDatasOffset(); - uint32_t code_item_offset = header_->GetCollections().CodeItemsOffset(); - if (class_data_offset < code_item_offset) { - return 0; - } - - // Find the last code item so we can leave it in place if the next section is not 4 byte aligned. - dex_ir::CodeItem* last_code_item = nullptr; - std::unordered_set visited_code_items; - bool is_code_item_aligned = IsNextSectionCodeItemAligned(code_item_offset); - if (!is_code_item_aligned) { - for (auto& code_item_pair : header_->GetCollections().CodeItems()) { - std::unique_ptr& code_item = code_item_pair.second; - if (last_code_item == nullptr - || last_code_item->GetOffset() < code_item->GetOffset()) { - last_code_item = code_item.get(); - } - } - } - +void DexLayout::LayoutCodeItems(const DexFile* dex_file) { static constexpr InvokeType invoke_types[] = { kDirect, kVirtual }; - const size_t num_layout_types = static_cast(LayoutType::kLayoutTypeCount); - std::unordered_set code_items[num_layout_types]; + std::unordered_map& code_item_layout = + layout_hotness_info_.code_item_layout_; + + // Assign hotness flags to all code items. for (InvokeType invoke_type : invoke_types) { for (std::unique_ptr& class_def : header_->GetCollections().ClassDefs()) { const bool is_profile_class = @@ -1772,7 +1744,7 @@ int32_t DexLayout::LayoutCodeItems(const DexFile* dex_file, : class_data->VirtualMethods())) { const dex_ir::MethodId *method_id = method->GetMethodId(); dex_ir::CodeItem *code_item = method->GetCodeItem(); - if (code_item == last_code_item || code_item == nullptr) { + if (code_item == nullptr) { continue; } // Separate executed methods (clinits and profiled methods) from unexecuted methods. @@ -1794,194 +1766,61 @@ int32_t DexLayout::LayoutCodeItems(const DexFile* dex_file, } else if (hotness.IsInProfile()) { state = LayoutType::kLayoutTypeSometimesUsed; } - code_items[static_cast(state)].insert(code_item); - } - } - } - - // Removing duplicate CodeItems may expose other issues with downstream - // optimizations such as quickening. But we need to ensure at least the weak - // forms of it currently in use do not break layout optimizations. - std::map original_code_item_offset; - // Total_diff includes diffs generated by clinits, executed, and non-executed methods. - int32_t total_diff = 0; - // The relative placement has no effect on correctness; it is used to ensure - // the layout is deterministic - for (size_t index = 0; index < num_layout_types; ++index) { - const std::unordered_set& code_items_set = code_items[index]; - // diff is reset for each class of code items. - int32_t diff = 0; - const uint32_t start_offset = code_item_offset; - for (dex_ir::ClassData* data : new_class_data_order) { - data->SetOffset(data->GetOffset() + diff); - for (InvokeType invoke_type : invoke_types) { - for (auto &method : *(invoke_type == InvokeType::kDirect - ? data->DirectMethods() - : data->VirtualMethods())) { - dex_ir::CodeItem* code_item = method->GetCodeItem(); - if (code_item != nullptr && - code_items_set.find(code_item) != code_items_set.end()) { - // Compute where the CodeItem was originally laid out. - uint32_t original_offset = code_item->GetOffset(); - auto it = original_code_item_offset.find(code_item); - if (it != original_code_item_offset.end()) { - original_offset = it->second; - } else { - original_code_item_offset[code_item] = code_item->GetOffset(); - // Assign the new offset and move the pointer to allocate space. - code_item->SetOffset(code_item_offset); - code_item_offset += - RoundUp(code_item->GetSize(), kDexCodeItemAlignment); - } - // Update the size of the encoded methods to reflect that the offset difference - // may have changed the ULEB128 length. - diff += - UnsignedLeb128Size(code_item->GetOffset()) - UnsignedLeb128Size(original_offset); - } + auto it = code_item_layout.emplace(code_item, state); + if (!it.second) { + LayoutType& layout_type = it.first->second; + // Already exists, merge the hotness. + layout_type = MergeLayoutType(layout_type, state); } } } - DexLayoutSection& code_section = dex_sections_.sections_[static_cast( - DexLayoutSections::SectionType::kSectionTypeCode)]; - code_section.parts_[index].offset_ = start_offset; - code_section.parts_[index].size_ = code_item_offset - start_offset; - for (size_t i = 0; i < num_layout_types; ++i) { - VLOG(dex) << "Code item layout bucket " << i << " count=" << code_items[i].size() - << " bytes=" << code_section.parts_[i].size_; - } - total_diff += diff; } - // Adjust diff to be 4-byte aligned. - return RoundUp(total_diff, kDexCodeItemAlignment); -} -bool DexLayout::IsNextSectionCodeItemAligned(uint32_t offset) { - dex_ir::Collections& collections = header_->GetCollections(); - std::set section_offsets; - section_offsets.insert(collections.MapListOffset()); - section_offsets.insert(collections.TypeListsOffset()); - section_offsets.insert(collections.AnnotationSetRefListsOffset()); - section_offsets.insert(collections.AnnotationSetItemsOffset()); - section_offsets.insert(collections.ClassDatasOffset()); - section_offsets.insert(collections.CodeItemsOffset()); - section_offsets.insert(collections.StringDatasOffset()); - section_offsets.insert(collections.DebugInfoItemsOffset()); - section_offsets.insert(collections.AnnotationItemsOffset()); - section_offsets.insert(collections.EncodedArrayItemsOffset()); - section_offsets.insert(collections.AnnotationsDirectoryItemsOffset()); - - auto found = section_offsets.find(offset); - if (found != section_offsets.end()) { - found++; - if (found != section_offsets.end()) { - return *found % kDexCodeItemAlignment == 0; + dex_ir::CollectionVector::Vector& code_items = + header_->GetCollections().CodeItems(); + if (VLOG_IS_ON(dex)) { + size_t layout_count[static_cast(LayoutType::kLayoutTypeCount)] = {}; + for (const std::unique_ptr& code_item : code_items) { + auto it = code_item_layout.find(code_item.get()); + DCHECK(it != code_item_layout.end()); + ++layout_count[static_cast(it->second)]; + } + for (size_t i = 0; i < static_cast(LayoutType::kLayoutTypeCount); ++i) { + LOG(INFO) << "Code items in category " << i << " count=" << layout_count[i]; } - } - return false; -} - -// Adjust offsets of every item in the specified section by diff bytes. -template void DexLayout::FixupSection(std::map>& map, - uint32_t diff) { - for (auto& pair : map) { - std::unique_ptr& item = pair.second; - item->SetOffset(item->GetOffset() + diff); - } -} - -// Adjust offsets of all sections with an address after the specified offset by diff bytes. -void DexLayout::FixupSections(uint32_t offset, uint32_t diff) { - dex_ir::Collections& collections = header_->GetCollections(); - uint32_t map_list_offset = collections.MapListOffset(); - if (map_list_offset > offset) { - collections.SetMapListOffset(map_list_offset + diff); - } - - uint32_t type_lists_offset = collections.TypeListsOffset(); - if (type_lists_offset > offset) { - collections.SetTypeListsOffset(type_lists_offset + diff); - FixupSection(collections.TypeLists(), diff); - } - - uint32_t annotation_set_ref_lists_offset = collections.AnnotationSetRefListsOffset(); - if (annotation_set_ref_lists_offset > offset) { - collections.SetAnnotationSetRefListsOffset(annotation_set_ref_lists_offset + diff); - FixupSection(collections.AnnotationSetRefLists(), diff); - } - - uint32_t annotation_set_items_offset = collections.AnnotationSetItemsOffset(); - if (annotation_set_items_offset > offset) { - collections.SetAnnotationSetItemsOffset(annotation_set_items_offset + diff); - FixupSection(collections.AnnotationSetItems(), diff); - } - - uint32_t class_datas_offset = collections.ClassDatasOffset(); - if (class_datas_offset > offset) { - collections.SetClassDatasOffset(class_datas_offset + diff); - FixupSection(collections.ClassDatas(), diff); - } - - uint32_t code_items_offset = collections.CodeItemsOffset(); - if (code_items_offset > offset) { - collections.SetCodeItemsOffset(code_items_offset + diff); - FixupSection(collections.CodeItems(), diff); - } - - uint32_t string_datas_offset = collections.StringDatasOffset(); - if (string_datas_offset > offset) { - collections.SetStringDatasOffset(string_datas_offset + diff); - FixupSection(collections.StringDatas(), diff); - } - - uint32_t debug_info_items_offset = collections.DebugInfoItemsOffset(); - if (debug_info_items_offset > offset) { - collections.SetDebugInfoItemsOffset(debug_info_items_offset + diff); - FixupSection(collections.DebugInfoItems(), diff); - } - - uint32_t annotation_items_offset = collections.AnnotationItemsOffset(); - if (annotation_items_offset > offset) { - collections.SetAnnotationItemsOffset(annotation_items_offset + diff); - FixupSection(collections.AnnotationItems(), diff); - } - - uint32_t encoded_array_items_offset = collections.EncodedArrayItemsOffset(); - if (encoded_array_items_offset > offset) { - collections.SetEncodedArrayItemsOffset(encoded_array_items_offset + diff); - FixupSection(collections.EncodedArrayItems(), diff); } - uint32_t annotations_directory_items_offset = collections.AnnotationsDirectoryItemsOffset(); - if (annotations_directory_items_offset > offset) { - collections.SetAnnotationsDirectoryItemsOffset(annotations_directory_items_offset + diff); - FixupSection(collections.AnnotationsDirectoryItems(), diff); - } + // Sort the code items vector by new layout. The writing process will take care of calculating + // all the offsets. Stable sort to preserve any existing locality that might be there. + std::stable_sort(code_items.begin(), + code_items.end(), + [&](const std::unique_ptr& a, + const std::unique_ptr& b) { + auto it_a = code_item_layout.find(a.get()); + auto it_b = code_item_layout.find(b.get()); + DCHECK(it_a != code_item_layout.end()); + DCHECK(it_b != code_item_layout.end()); + const LayoutType layout_type_a = it_a->second; + const LayoutType layout_type_b = it_b->second; + return layout_type_a < layout_type_b; + }); } void DexLayout::LayoutOutputFile(const DexFile* dex_file) { - const int32_t string_diff = LayoutStringData(dex_file); - // If we expanded the string data section, we need to update the offsets or else we will - // corrupt the next section when writing out. - FixupSections(header_->GetCollections().StringDatasOffset(), string_diff); - // Update file size. - header_->SetFileSize(header_->FileSize() + string_diff); - - std::vector new_class_data_order = LayoutClassDefsAndClassData(dex_file); - const int32_t code_item_diff = LayoutCodeItems(dex_file, new_class_data_order); - // Move sections after ClassData by diff bytes. - FixupSections(header_->GetCollections().ClassDatasOffset(), code_item_diff); - - // Update file and data size. - // The data size must be aligned to kDataSectionAlignment. - const int32_t total_diff = code_item_diff + string_diff; - header_->SetDataSize(RoundUp(header_->DataSize() + total_diff, kDataSectionAlignment)); - header_->SetFileSize(header_->FileSize() + total_diff); + LayoutStringData(dex_file); + LayoutClassDefsAndClassData(dex_file); + LayoutCodeItems(dex_file); } -void DexLayout::OutputDexFile(const DexFile* dex_file) { +void DexLayout::OutputDexFile(const DexFile* dex_file, bool compute_offsets) { const std::string& dex_file_location = dex_file->GetLocation(); std::string error_msg; std::unique_ptr new_file; + // Since we allow dex growth, we need to size the map larger than the original input to be safe. + // Reserve an extra 10% to add some buffer room. Note that this is probably more than + // necessary. + constexpr size_t kReserveFraction = 10; + const size_t max_size = header_->FileSize() + header_->FileSize() / kReserveFraction; if (!options_.output_to_memmap_) { std::string output_location(options_.output_dex_directory_); size_t last_slash = dex_file_location.rfind('/'); @@ -1998,15 +1837,15 @@ void DexLayout::OutputDexFile(const DexFile* dex_file) { LOG(ERROR) << "Could not create dex writer output file: " << output_location; return; } - if (ftruncate(new_file->Fd(), header_->FileSize()) != 0) { + if (ftruncate(new_file->Fd(), max_size) != 0) { LOG(ERROR) << "Could not grow dex writer output file: " << output_location;; new_file->Erase(); return; } - mem_map_.reset(MemMap::MapFile(header_->FileSize(), PROT_READ | PROT_WRITE, MAP_SHARED, + mem_map_.reset(MemMap::MapFile(max_size, PROT_READ | PROT_WRITE, MAP_SHARED, new_file->Fd(), 0, /*low_4gb*/ false, output_location.c_str(), &error_msg)); } else { - mem_map_.reset(MemMap::MapAnonymous("layout dex", nullptr, header_->FileSize(), + mem_map_.reset(MemMap::MapAnonymous("layout dex", nullptr, max_size, PROT_READ | PROT_WRITE, /* low_4gb */ false, /* reuse */ false, &error_msg)); } if (mem_map_ == nullptr) { @@ -2016,8 +1855,14 @@ void DexLayout::OutputDexFile(const DexFile* dex_file) { } return; } - DexWriter::Output(header_, mem_map_.get(), options_.compact_dex_level_); + DexWriter::Output(header_, mem_map_.get(), this, compute_offsets, options_.compact_dex_level_); if (new_file != nullptr) { + // Since we make the memmap larger than needed, shrink the file back down to not leave extra + // padding. + int res = new_file->SetLength(header_->FileSize()); + if (res != 0) { + LOG(ERROR) << "Truncating file resulted in " << res; + } UNUSED(new_file->FlushCloseOrErase()); } } @@ -2028,7 +1873,15 @@ void DexLayout::OutputDexFile(const DexFile* dex_file) { void DexLayout::ProcessDexFile(const char* file_name, const DexFile* dex_file, size_t dex_file_index) { - std::unique_ptr header(dex_ir::DexIrBuilder(*dex_file)); + const bool output = options_.output_dex_directory_ != nullptr || options_.output_to_memmap_; + // Try to avoid eagerly assigning offsets to find bugs since GetOffset will abort if the offset + // is unassigned. + bool eagerly_assign_offsets = false; + if (options_.visualize_pattern_ || options_.show_section_statistics_ || options_.dump_) { + // These options required the offsets for dumping purposes. + eagerly_assign_offsets = true; + } + std::unique_ptr header(dex_ir::DexIrBuilder(*dex_file, eagerly_assign_offsets)); SetHeader(header.get()); if (options_.verbose_) { @@ -2052,13 +1905,17 @@ void DexLayout::ProcessDexFile(const char* file_name, } // In case we are outputting to a file, keep it open so we can verify. - if (options_.output_dex_directory_ != nullptr || options_.output_to_memmap_) { - if (info_ != nullptr) { + if (output) { + // Layout information about what strings and code items are hot. Used by the writing process + // to generate the sections that are stored in the oat file. + bool do_layout = info_ != nullptr; + if (do_layout) { LayoutOutputFile(dex_file); } - OutputDexFile(dex_file); + OutputDexFile(dex_file, do_layout); // Clear header before verifying to reduce peak RAM usage. + const size_t file_size = header_->FileSize(); header.reset(); // Verify the output dex file's structure, only enabled by default for debug builds. @@ -2066,7 +1923,7 @@ void DexLayout::ProcessDexFile(const char* file_name, std::string error_msg; std::string location = "memory mapped file for " + std::string(file_name); std::unique_ptr output_dex_file(DexFileLoader::Open(mem_map_->Begin(), - mem_map_->Size(), + file_size, location, /* checksum */ 0, /*oat_dex_file*/ nullptr, @@ -2076,11 +1933,16 @@ void DexLayout::ProcessDexFile(const char* file_name, CHECK(output_dex_file != nullptr) << "Failed to re-open output file:" << error_msg; // Do IR-level comparison between input and output. This check ignores potential differences - // due to layout, so offsets are not checked. Instead, it checks the data contents of each item. + // due to layout, so offsets are not checked. Instead, it checks the data contents of each + // item. // // Regenerate output IR to catch any bugs that might happen during writing. - std::unique_ptr output_header(dex_ir::DexIrBuilder(*output_dex_file)); - std::unique_ptr orig_header(dex_ir::DexIrBuilder(*dex_file)); + std::unique_ptr output_header( + dex_ir::DexIrBuilder(*output_dex_file, + /*eagerly_assign_offsets*/ true)); + std::unique_ptr orig_header( + dex_ir::DexIrBuilder(*dex_file, + /*eagerly_assign_offsets*/ true)); CHECK(VerifyOutputDexFile(output_header.get(), orig_header.get(), &error_msg)) << error_msg; } } diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h index 2e897739cc778bac3ea2ee4f9d71ba3eb10b9d37..958251e8ac5c012ba6cbb4c28fadbba8ac44ca38 100644 --- a/dexlayout/dexlayout.h +++ b/dexlayout/dexlayout.h @@ -25,6 +25,7 @@ #include #include +#include #include "cdex/compact_dex_level.h" #include "dex_file_layout.h" @@ -62,6 +63,7 @@ class Options { bool verbose_ = false; bool verify_output_ = kIsDebugBuild; bool visualize_pattern_ = false; + bool update_checksum_ = false; CompactDexLevel compact_dex_level_ = CompactDexLevel::kCompactDexLevelNone; OutputFormat output_format_ = kOutputPlain; const char* output_dex_directory_ = nullptr; @@ -69,8 +71,19 @@ class Options { const char* profile_file_name_ = nullptr; }; +// Hotness info +class DexLayoutHotnessInfo { + public: + // Store layout information so that the offset calculation can specify the section sizes. + std::unordered_map code_item_layout_; +}; + class DexLayout { public: + // Setting this to false disables class def layout entirely, which is stronger than strictly + // necessary to ensure the partial order w.r.t. class derivation. TODO: Re-enable (b/68317550). + static constexpr bool kChangeClassDefOrder = false; + DexLayout(Options& options, ProfileCompilationInfo* info, FILE* out_file, @@ -86,10 +99,18 @@ class DexLayout { MemMap* GetAndReleaseMemMap() { return mem_map_.release(); } - const DexLayoutSections& GetSections() const { + DexLayoutSections& GetSections() { return dex_sections_; } + const DexLayoutHotnessInfo& LayoutHotnessInfo() const { + return layout_hotness_info_; + } + + const Options& GetOptions() const { + return options_; + } + private: void DumpAnnotationSetItem(dex_ir::AnnotationSetItem* set_item); void DumpBytecodes(uint32_t idx, const dex_ir::CodeItem* code, uint32_t code_offset); @@ -120,18 +141,14 @@ class DexLayout { void DumpSField(uint32_t idx, uint32_t flags, int i, dex_ir::EncodedValue* init); void DumpDexFile(); - std::vector LayoutClassDefsAndClassData(const DexFile* dex_file); - int32_t LayoutCodeItems(const DexFile* dex_file, - std::vector new_class_data_order); - int32_t LayoutStringData(const DexFile* dex_file); - bool IsNextSectionCodeItemAligned(uint32_t offset); - template void FixupSection(std::map>& map, uint32_t diff); - void FixupSections(uint32_t offset, uint32_t diff); + void LayoutClassDefsAndClassData(const DexFile* dex_file); + void LayoutCodeItems(const DexFile* dex_file); + void LayoutStringData(const DexFile* dex_file); // Creates a new layout for the dex file based on profile info. // Currently reorders ClassDefs, ClassDataItems, and CodeItems. void LayoutOutputFile(const DexFile* dex_file); - void OutputDexFile(const DexFile* dex_file); + void OutputDexFile(const DexFile* dex_file, bool compute_offsets); void DumpCFG(const DexFile* dex_file, int idx); void DumpCFG(const DexFile* dex_file, uint32_t dex_method_idx, const DexFile::CodeItem* code); @@ -142,6 +159,8 @@ class DexLayout { dex_ir::Header* header_; std::unique_ptr mem_map_; DexLayoutSections dex_sections_; + // Layout hotness information is only calculated when dexlayout is enabled. + DexLayoutHotnessInfo layout_hotness_info_; DISALLOW_COPY_AND_ASSIGN(DexLayout); }; diff --git a/dexlayout/dexlayout_main.cc b/dexlayout/dexlayout_main.cc index 17097f172843e474e17b4d050fcb3c757b364244..5bb7196531da30e8f4daadd3a43a34e8692ef570 100644 --- a/dexlayout/dexlayout_main.cc +++ b/dexlayout/dexlayout_main.cc @@ -29,7 +29,9 @@ #include #include -#include "base/logging.h" +#include + +#include "base/logging.h" // For InitLogging. #include "jit/profile_compilation_info.h" #include "mem_map.h" #include "runtime.h" diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc index 08673056d97e6d2cff02dab2c6613541558a90d7..f994fd65332119b71602a0b2e1c8d7da6145b6c2 100644 --- a/dexlayout/dexlayout_test.cc +++ b/dexlayout/dexlayout_test.cc @@ -241,8 +241,8 @@ static void WriteFileBase64(const char* base64, const char* location) { class DexLayoutTest : public CommonRuntimeTest { protected: - virtual void SetUp() { - CommonRuntimeTest::SetUp(); + std::string GetDexLayoutPath() { + return GetTestAndroidRoot() + "/bin/dexlayoutd"; } // Runs FullPlainOutput test. @@ -255,18 +255,16 @@ class DexLayoutTest : public CommonRuntimeTest { ScratchFile dexlayout_output; const std::string& dexlayout_filename = dexlayout_output.GetFilename(); - std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; - EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; for (const std::string &dex_file : GetLibCoreDexFileNames()) { std::vector dexdump_exec_argv = { dexdump, "-d", "-f", "-h", "-l", "plain", "-o", dexdump_filename, dex_file }; - std::vector dexlayout_exec_argv = - { dexlayout, "-d", "-f", "-h", "-l", "plain", "-o", dexlayout_filename, dex_file }; + std::vector dexlayout_args = + { "-d", "-f", "-h", "-l", "plain", "-o", dexlayout_filename, dex_file }; if (!::art::Exec(dexdump_exec_argv, error_msg)) { return false; } - if (!::art::Exec(dexlayout_exec_argv, error_msg)) { + if (!DexLayoutExec(dexlayout_args, error_msg)) { return false; } std::vector diff_exec_argv = @@ -284,13 +282,11 @@ class DexLayoutTest : public CommonRuntimeTest { const std::string& tmp_name = tmp_file.GetFilename(); size_t tmp_last_slash = tmp_name.rfind('/'); std::string tmp_dir = tmp_name.substr(0, tmp_last_slash + 1); - std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; - EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; for (const std::string &dex_file : GetLibCoreDexFileNames()) { - std::vector dexlayout_exec_argv = - { dexlayout, "-w", tmp_dir, "-o", tmp_name, dex_file }; - if (!::art::Exec(dexlayout_exec_argv, error_msg)) { + std::vector dexlayout_args = + { "-w", tmp_dir, "-o", tmp_name, dex_file }; + if (!DexLayoutExec(dexlayout_args, error_msg)) { return false; } size_t dex_file_last_slash = dex_file.rfind('/'); @@ -418,12 +414,9 @@ class DexLayoutTest : public CommonRuntimeTest { // WriteFileBase64(kDexFileLayoutInputProfile, profile_file.c_str()); std::string output_dex = tmp_dir + "classes.dex.new"; - std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; - EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; - - std::vector dexlayout_exec_argv = - { dexlayout, "-v", "-w", tmp_dir, "-o", tmp_name, "-p", profile_file, dex_file }; - if (!::art::Exec(dexlayout_exec_argv, error_msg)) { + std::vector dexlayout_args = + { "-v", "-w", tmp_dir, "-o", tmp_name, "-p", profile_file, dex_file }; + if (!DexLayoutExec(dexlayout_args, error_msg)) { return false; } @@ -466,13 +459,10 @@ class DexLayoutTest : public CommonRuntimeTest { std::string output_dex = tmp_dir + "classes.dex.new"; std::string second_output_dex = tmp_dir + "classes.dex.new.new"; - std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; - EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; - // -v makes sure that the layout did not corrupt the dex file. - std::vector dexlayout_exec_argv = - { dexlayout, "-i", "-v", "-w", tmp_dir, "-o", tmp_name, "-p", profile_file, dex_file }; - if (!::art::Exec(dexlayout_exec_argv, error_msg)) { + std::vector dexlayout_args = + { "-i", "-v", "-w", tmp_dir, "-o", tmp_name, "-p", profile_file, dex_file }; + if (!DexLayoutExec(dexlayout_args, error_msg)) { return false; } @@ -482,9 +472,9 @@ class DexLayoutTest : public CommonRuntimeTest { // -v makes sure that the layout did not corrupt the dex file. // -i since the checksum won't match from the first layout. - std::vector second_dexlayout_exec_argv = - { dexlayout, "-i", "-v", "-w", tmp_dir, "-o", tmp_name, "-p", profile_file, output_dex }; - if (!::art::Exec(second_dexlayout_exec_argv, error_msg)) { + std::vector second_dexlayout_args = + { "-i", "-v", "-w", tmp_dir, "-o", tmp_name, "-p", profile_file, output_dex }; + if (!DexLayoutExec(second_dexlayout_args, error_msg)) { return false; } @@ -516,12 +506,8 @@ class DexLayoutTest : public CommonRuntimeTest { WriteFileBase64(filename, input_dex.c_str()); std::string output_dex = tmp_dir + "classes.dex.new"; - std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; - EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; - - std::vector dexlayout_exec_argv = - { dexlayout, "-w", tmp_dir, "-o", "/dev/null", input_dex }; - if (!::art::Exec(dexlayout_exec_argv, error_msg)) { + std::vector dexlayout_args = { "-w", tmp_dir, "-o", "/dev/null", input_dex }; + if (!DexLayoutExec(dexlayout_args, error_msg)) { return false; } @@ -541,7 +527,7 @@ class DexLayoutTest : public CommonRuntimeTest { bool DexLayoutExec(ScratchFile* dex_file, const char* dex_filename, ScratchFile* profile_file, - std::vector& dexlayout_exec_argv) { + const std::vector& dexlayout_args) { if (dex_filename != nullptr) { WriteBase64ToFile(dex_filename, dex_file->GetFile()); EXPECT_EQ(dex_file->GetFile()->Flush(), 0); @@ -549,14 +535,27 @@ class DexLayoutTest : public CommonRuntimeTest { if (profile_file != nullptr) { CreateProfile(dex_file->GetFilename(), profile_file->GetFilename(), dex_file->GetFilename()); } + std::string error_msg; - const bool result = ::art::Exec(dexlayout_exec_argv, &error_msg); + const bool result = DexLayoutExec(dexlayout_args, &error_msg); if (!result) { LOG(ERROR) << "Error: " << error_msg; return false; } return true; } + + bool DexLayoutExec(const std::vector& dexlayout_args, std::string* error_msg) { + std::vector argv; + + std::string dexlayout = GetDexLayoutPath(); + CHECK(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; + argv.push_back(dexlayout); + + argv.insert(argv.end(), dexlayout_args.begin(), dexlayout_args.end()); + + return ::art::Exec(argv, error_msg); + } }; @@ -614,89 +613,72 @@ TEST_F(DexLayoutTest, UnreferencedEndingCatchHandler) { TEST_F(DexLayoutTest, DuplicateOffset) { ScratchFile temp_dex; - std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; - EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; - std::vector dexlayout_exec_argv = - { dexlayout, "-a", "-i", "-o", "/dev/null", temp_dex.GetFilename() }; + std::vector dexlayout_args = + { "-a", "-i", "-o", "/dev/null", temp_dex.GetFilename() }; ASSERT_TRUE(DexLayoutExec(&temp_dex, kDexFileDuplicateOffset, nullptr /* profile_file */, - dexlayout_exec_argv)); + dexlayout_args)); } TEST_F(DexLayoutTest, NullSetRefListElement) { ScratchFile temp_dex; - std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; - EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; - std::vector dexlayout_exec_argv = - { dexlayout, "-o", "/dev/null", temp_dex.GetFilename() }; + std::vector dexlayout_args = { "-o", "/dev/null", temp_dex.GetFilename() }; ASSERT_TRUE(DexLayoutExec(&temp_dex, kNullSetRefListElementInputDex, nullptr /* profile_file */, - dexlayout_exec_argv)); + dexlayout_args)); } TEST_F(DexLayoutTest, MultiClassData) { ScratchFile temp_dex; ScratchFile temp_profile; - std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; - EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; - std::vector dexlayout_exec_argv = - { dexlayout, "-p", temp_profile.GetFilename(), "-o", "/dev/null", temp_dex.GetFilename() }; + std::vector dexlayout_args = + { "-p", temp_profile.GetFilename(), "-o", "/dev/null", temp_dex.GetFilename() }; ASSERT_TRUE(DexLayoutExec(&temp_dex, kMultiClassDataInputDex, &temp_profile, - dexlayout_exec_argv)); + dexlayout_args)); } TEST_F(DexLayoutTest, UnalignedCodeInfo) { ScratchFile temp_dex; ScratchFile temp_profile; - std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; - EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; - std::vector dexlayout_exec_argv = - { dexlayout, "-p", temp_profile.GetFilename(), "-o", "/dev/null", temp_dex.GetFilename() }; + std::vector dexlayout_args = + { "-p", temp_profile.GetFilename(), "-o", "/dev/null", temp_dex.GetFilename() }; ASSERT_TRUE(DexLayoutExec(&temp_dex, kUnalignedCodeInfoInputDex, &temp_profile, - dexlayout_exec_argv)); + dexlayout_args)); } TEST_F(DexLayoutTest, ClassDataBeforeCode) { ScratchFile temp_dex; ScratchFile temp_profile; - std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; - EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; - std::vector dexlayout_exec_argv = - { dexlayout, "-p", temp_profile.GetFilename(), "-o", "/dev/null", temp_dex.GetFilename() }; + std::vector dexlayout_args = + { "-p", temp_profile.GetFilename(), "-o", "/dev/null", temp_dex.GetFilename() }; ASSERT_TRUE(DexLayoutExec(&temp_dex, kClassDataBeforeCodeInputDex, &temp_profile, - dexlayout_exec_argv)); + dexlayout_args)); } TEST_F(DexLayoutTest, UnknownTypeDebugInfo) { ScratchFile temp_dex; - std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; - EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; - std::vector dexlayout_exec_argv = - { dexlayout, "-o", "/dev/null", temp_dex.GetFilename() }; + std::vector dexlayout_args = { "-o", "/dev/null", temp_dex.GetFilename() }; ASSERT_TRUE(DexLayoutExec(&temp_dex, kUnknownTypeDebugInfoInputDex, nullptr /* profile_file */, - dexlayout_exec_argv)); + dexlayout_args)); } TEST_F(DexLayoutTest, DuplicateCodeItem) { ScratchFile temp_dex; - std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; - EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; - std::vector dexlayout_exec_argv = - { dexlayout, "-o", "/dev/null", temp_dex.GetFilename() }; + std::vector dexlayout_args = { "-o", "/dev/null", temp_dex.GetFilename() }; ASSERT_TRUE(DexLayoutExec(&temp_dex, kDuplicateCodeItemInputDex, nullptr /* profile_file */, - dexlayout_exec_argv)); + dexlayout_args)); } // Test that instructions that go past the end of the code items don't cause crashes. @@ -713,7 +695,7 @@ TEST_F(DexLayoutTest, CodeItemOverrun) { } ClassDataItemIterator it(*dex, data); it.SkipAllFields(); - while (it.HasNextDirectMethod() || it.HasNextVirtualMethod()) { + while (it.HasNextMethod()) { DexFile::CodeItem* item = const_cast(it.GetMethodCodeItem()); if (item != nullptr) { IterationRange instructions = item->Instructions(); @@ -743,14 +725,53 @@ TEST_F(DexLayoutTest, CodeItemOverrun) { CHECK(mutated_successfully) << "Failed to find candidate code item with only one code unit in last instruction."; }); - std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout"; - EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path"; - std::vector dexlayout_exec_argv = - { dexlayout, "-i", "-o", "/dev/null", temp_dex.GetFilename() }; + std::vector dexlayout_args = { "-i", "-o", "/dev/null", temp_dex.GetFilename() }; ASSERT_TRUE(DexLayoutExec(&temp_dex, /*dex_filename*/ nullptr, nullptr /* profile_file */, - dexlayout_exec_argv)); + dexlayout_args)); +} + +// Test that link data is written out (or at least the header is updated). +TEST_F(DexLayoutTest, LinkData) { + TEST_DISABLED_FOR_TARGET(); + ScratchFile temp_dex; + size_t file_size = 0; + MutateDexFile(temp_dex.GetFile(), GetTestDexFileName("ManyMethods"), [&] (DexFile* dex) { + DexFile::Header& header = const_cast(dex->GetHeader()); + header.link_off_ = header.file_size_; + header.link_size_ = 16 * KB; + header.file_size_ += header.link_size_; + file_size = header.file_size_; + }); + TEMP_FAILURE_RETRY(temp_dex.GetFile()->SetLength(file_size)); + + std::string error_msg; + + ScratchFile tmp_file; + const std::string& tmp_name = tmp_file.GetFilename(); + size_t tmp_last_slash = tmp_name.rfind('/'); + std::string tmp_dir = tmp_name.substr(0, tmp_last_slash + 1); + ScratchFile profile_file; + + std::vector dexlayout_args = + { "-i", + "-v", + "-w", tmp_dir, + "-o", tmp_name, + "-p", profile_file.GetFilename(), + temp_dex.GetFilename() + }; + // -v makes sure that the layout did not corrupt the dex file. + ASSERT_TRUE(DexLayoutExec(&temp_dex, + /*dex_filename*/ nullptr, + &profile_file, + dexlayout_args)); + + std::string output_dex = temp_dex.GetFilename() + ".new"; + std::vector rm_exec_argv = + { "/bin/rm", output_dex }; + ASSERT_TRUE(::art::Exec(rm_exec_argv, &error_msg)); } } // namespace art diff --git a/dexlist/Android.bp b/dexlist/Android.bp index 03943bf0f246b2c01addd7b17e6065bd47d21807..8ecff4210e2774e7dc0ff0530fa83aea33958fa6 100644 --- a/dexlist/Android.bp +++ b/dexlist/Android.bp @@ -17,7 +17,7 @@ art_cc_binary { host_supported: true, srcs: ["dexlist.cc"], cflags: ["-Wall", "-Werror"], - shared_libs: ["libart"], + shared_libs: ["libart", "libbase"], } art_cc_test { diff --git a/dexlist/dexlist.cc b/dexlist/dexlist.cc index c8bc132da0e509436abea286a5dd4d6c0918e408..2c910d401854041f68512cc6e334f53ed97c12b8 100644 --- a/dexlist/dexlist.cc +++ b/dexlist/dexlist.cc @@ -26,6 +26,8 @@ #include #include +#include "base/logging.h" // For InitLogging. +#include "code_item_accessors-no_art-inl.h" #include "dex_file-inl.h" #include "dex_file_loader.h" #include "mem_map.h" @@ -98,6 +100,7 @@ static void dumpMethod(const DexFile* pDexFile, if (pCode == nullptr || codeOffset == 0) { return; } + CodeItemDebugInfoAccessor accessor(pDexFile, pCode, pDexFile->GetDebugInfoOffset(pCode)); // Method information. const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(idx); @@ -120,7 +123,7 @@ static void dumpMethod(const DexFile* pDexFile, // Find the first line. int firstLine = -1; - pDexFile->DecodeDebugPositionInfo(pCode, positionsCb, &firstLine); + pDexFile->DecodeDebugPositionInfo(accessor.DebugInfoOffset(), positionsCb, &firstLine); // Method signature. const Signature signature = pDexFile->GetMethodSignature(pMethodId); @@ -128,7 +131,7 @@ static void dumpMethod(const DexFile* pDexFile, // Dump actual method information. fprintf(gOutFile, "0x%08x %d %s %s %s %s %d\n", - insnsOff, pCode->insns_size_in_code_units_ * 2, + insnsOff, accessor.InsnsSizeInCodeUnits() * 2, className.get(), methodName, typeDesc, fileName, firstLine); free(typeDesc); @@ -151,16 +154,8 @@ void dumpClass(const DexFile* pDexFile, u4 idx) { if (pEncodedData != nullptr) { ClassDataItemIterator pClassData(*pDexFile, pEncodedData); pClassData.SkipAllFields(); - // Direct methods. - for (; pClassData.HasNextDirectMethod(); pClassData.Next()) { - dumpMethod(pDexFile, fileName, - pClassData.GetMemberIndex(), - pClassData.GetRawMemberAccessFlags(), - pClassData.GetMethodCodeItem(), - pClassData.GetMethodCodeItemOffset()); - } - // Virtual methods. - for (; pClassData.HasNextVirtualMethod(); pClassData.Next()) { + // Direct and virtual methods. + for (; pClassData.HasNextMethod(); pClassData.Next()) { dumpMethod(pDexFile, fileName, pClassData.GetMemberIndex(), pClassData.GetRawMemberAccessFlags(), diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc index 39c9b9993b263f9c0ede94fa709b2f3e3461a9d1..eead2dcf837fac45711b1566ab5234c382299949 100644 --- a/dexoptanalyzer/dexoptanalyzer.cc +++ b/dexoptanalyzer/dexoptanalyzer.cc @@ -16,9 +16,11 @@ #include +#include "base/logging.h" // For InitLogging. #include "android-base/stringprintf.h" #include "android-base/strings.h" #include "base/file_utils.h" +#include "base/logging.h" // For InitLogging. #include "compiler_filter.h" #include "class_loader_context.h" #include "dex_file.h" diff --git a/dt_fd_forward/Android.bp b/dt_fd_forward/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..1ba2323a153665e5ab5f61f73f02dd0c5ba3ecf9 --- /dev/null +++ b/dt_fd_forward/Android.bp @@ -0,0 +1,66 @@ +// +// 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: "dt_fd_forward-defaults", + host_supported: true, + srcs: ["dt_fd_forward.cc"], + defaults: ["art_defaults"], + + // Note that this tool needs to be built for both 32-bit and 64-bit since it needs to be same + // ISA as what it is attached to. + compile_multilib: "both", + + shared_libs: [ + "libbase", + ], + target: { + android: { + }, + host: { + }, + darwin: { + enabled: false, + }, + }, + header_libs: [ + "javavm_headers", + "dt_fd_forward_export", + ], + multilib: { + lib32: { + suffix: "32", + }, + lib64: { + suffix: "64", + }, + }, +} + +art_cc_library { + name: "libdt_fd_forward", + defaults: ["dt_fd_forward-defaults"], +} + +art_cc_library { + name: "libdt_fd_forwardd", + defaults: [ + "art_debug_defaults", + "dt_fd_forward-defaults", + ], +} diff --git a/dt_fd_forward/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION b/dt_fd_forward/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/dt_fd_forward/NOTICE b/dt_fd_forward/NOTICE new file mode 100644 index 0000000000000000000000000000000000000000..4fd88fa8410f9f9f8234e2208abd3baec3179fc1 --- /dev/null +++ b/dt_fd_forward/NOTICE @@ -0,0 +1,30 @@ +Copyright (C) 2017 The Android Open Source Project +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + +This file implements interfaces from the file jdwpTransport.h. This +implementation is licensed under the same terms as the file +jdwpTransport.h. The copyright and license information for the file +jdwpTransport.h follows. + +Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + +This code is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License version 2 only, as +published by the Free Software Foundation. Oracle designates this +particular file as subject to the "Classpath" exception as provided +by Oracle in the LICENSE file that accompanied this code. + +This code is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +version 2 for more details (a copy is included in the LICENSE file that +accompanied this code). + +You should have received a copy of the GNU General Public License version +2 along with this work; if not, write to the Free Software Foundation, +Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + +Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +or visit www.oracle.com if you need additional information or have any +questions. diff --git a/dt_fd_forward/README.md b/dt_fd_forward/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c7ac8409288445c0000a24f61f25ceb5616a5592 --- /dev/null +++ b/dt_fd_forward/README.md @@ -0,0 +1,32 @@ +# dt_fd_forward + +dt_fd_forward is a jdwpTransport library. It implements the [Java Debug Wire +Protocol Transport Interface +(jdwpTransport)](https://docs.oracle.com/javase/7/docs/technotes/guides/jpda/jdwpTransport.html). +It allows one to handle and proxy JDWP traffic by supplying the implementation +for Attach. This transport requires an address. The address is a single integer +value that is the file-descriptor of an open AF\_UNIX socket. + +When this transport begins listening or attaching it will send the +null-terminated string "dt_fd_forward:START-LISTEN\0" over the given socket. + +When this transport stops listening for connections it will send the +null-terminated string "dt_fd_forward:END-LISTEN\0" over the socket. + +When this transport has successfully received fds from the proxy it sends the +message "dt_fd_forward:ATTACHED\0" over the socket. + +When this transport has closed its copies of the fds it will send the proxy the +message "dt_fd_forward:CLOSING\0" over the socket. + +When this transport accepts or attaches to a connection it will read from the +socket a 1 byte message and 3 file-descriptors. The file descriptors are, in +order, an fd that will be read from to get incoming JDWP packets (read\_fd\_), +an fd that outgoing JDWP packets will be written to (write\_fd\_), and an +_eventfd_ (write\_lock\_fd\_). The eventfd should not have any flags set. Prior +to writing any data to write\_fd\_ the transport will _read_ from the +write\_lock\_fd\_ and after finishing the write it will _write_ to it. This +allows one to safely multiplex data on the write\_fd\_. + +This transport implements no optional capabilities, though this may change in +the future. diff --git a/dt_fd_forward/dt_fd_forward.cc b/dt_fd_forward/dt_fd_forward.cc new file mode 100644 index 0000000000000000000000000000000000000000..cf3088dc609560c67bd18f05dae34c84e43ba313 --- /dev/null +++ b/dt_fd_forward/dt_fd_forward.cc @@ -0,0 +1,761 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jdwpTransport.h. This + * implementation is licensed under the same terms as the file + * jdwpTransport.h. The copyright and license information for the file + * jdwpTransport.h follows. + * + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "dt_fd_forward.h" + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace dt_fd_forward { + +// Helper that puts line-number in error message. +#define DT_IO_ERROR(f) \ + SetLastError(::android::base::StringPrintf("%s:%d - %s: %s", \ + __FILE__, __LINE__, f, strerror(errno))) + +extern const jdwpTransportNativeInterface_ gTransportInterface; + +template static T HostToNetwork(T in); +template static T NetworkToHost(T in); + +template<> int8_t HostToNetwork(int8_t in) { return in; } +template<> int8_t NetworkToHost(int8_t in) { return in; } +template<> int16_t HostToNetwork(int16_t in) { return htons(in); } +template<> int16_t NetworkToHost(int16_t in) { return ntohs(in); } +template<> int32_t HostToNetwork(int32_t in) { return htonl(in); } +template<> int32_t NetworkToHost(int32_t in) { return ntohl(in); } + +FdForwardTransport::FdForwardTransport(jdwpTransportCallback* cb) + : mem_(*cb), + read_fd_(-1), + write_fd_(-1), + wakeup_fd_(eventfd(0, EFD_NONBLOCK)), + listen_fd_(-1), + close_notify_fd_(-1), + state_(TransportState::kClosed), + current_seq_num_(0) {} + +FdForwardTransport::~FdForwardTransport() { } + +bool FdForwardTransport::ChangeState(TransportState old_state, TransportState new_state) { + if (old_state == state_) { + state_ = new_state; + state_cv_.notify_all(); + return true; + } else { + return false; + } +} + +jdwpTransportError FdForwardTransport::PerformAttach(int listen_fd) { + jdwpTransportError err = SetupListen(listen_fd); + if (err != OK) { + return OK; + } + err = Accept(); + StopListening(); + return err; +} + +static void SendListenMessage(const android::base::unique_fd& fd) { + TEMP_FAILURE_RETRY(send(fd, kListenStartMessage, sizeof(kListenStartMessage), MSG_EOR)); +} + +jdwpTransportError FdForwardTransport::SetupListen(int listen_fd) { + std::lock_guard lk(state_mutex_); + if (!ChangeState(TransportState::kClosed, TransportState::kListenSetup)) { + return ERR(ILLEGAL_STATE); + } else { + listen_fd_.reset(dup(listen_fd)); + SendListenMessage(listen_fd_); + CHECK(ChangeState(TransportState::kListenSetup, TransportState::kListening)); + return OK; + } +} + +static void SendListenEndMessage(const android::base::unique_fd& fd) { + TEMP_FAILURE_RETRY(send(fd, kListenEndMessage, sizeof(kListenEndMessage), MSG_EOR)); +} + +jdwpTransportError FdForwardTransport::StopListening() { + std::lock_guard lk(state_mutex_); + if (listen_fd_ != -1) { + SendListenEndMessage(listen_fd_); + } + // Don't close the listen_fd_ since we might need it for later calls to listen. + if (ChangeState(TransportState::kListening, TransportState::kClosed) || + state_ == TransportState::kOpen) { + listen_fd_.reset(); + } + return OK; +} + +// Last error message. +thread_local std::string global_last_error_; + +void FdForwardTransport::SetLastError(const std::string& desc) { + LOG(ERROR) << desc; + global_last_error_ = desc; +} + +IOResult FdForwardTransport::ReadFullyWithoutChecks(void* data, size_t ndata) { + uint8_t* bdata = reinterpret_cast(data); + size_t nbytes = 0; + while (nbytes < ndata) { + int res = TEMP_FAILURE_RETRY(read(read_fd_, bdata + nbytes, ndata - nbytes)); + if (res < 0) { + DT_IO_ERROR("Failed read()"); + return IOResult::kError; + } else if (res == 0) { + return IOResult::kEOF; + } else { + nbytes += res; + } + } + return IOResult::kOk; +} + +IOResult FdForwardTransport::ReadUpToMax(void* data, size_t ndata, /*out*/size_t* read_amount) { + CHECK_GE(read_fd_.get(), 0); + int avail; + int res = ioctl(read_fd_, FIONREAD, &avail); + if (res < 0) { + DT_IO_ERROR("Failed ioctl(read_fd_, FIONREAD, &avail)"); + return IOResult::kError; + } + size_t to_read = std::min(static_cast(avail), ndata); + *read_amount = to_read; + if (*read_amount == 0) { + // Check if the read would cause an EOF. + struct pollfd pollfd = { read_fd_, POLLRDHUP, 0 }; + res = poll(&pollfd, /*nfds*/1, /*timeout*/0); + if (res < 0 || (pollfd.revents & POLLERR) == POLLERR) { + DT_IO_ERROR("Failed poll on read fd."); + return IOResult::kError; + } + return ((pollfd.revents & (POLLRDHUP | POLLHUP)) == 0) ? IOResult::kOk : IOResult::kEOF; + } + + return ReadFullyWithoutChecks(data, to_read); +} + +IOResult FdForwardTransport::ReadFully(void* data, size_t ndata) { + uint64_t seq_num = current_seq_num_; + size_t nbytes = 0; + while (nbytes < ndata) { + size_t read_len; + struct pollfd pollfds[2]; + { + std::lock_guard lk(state_mutex_); + // Operations in this block must not cause an unbounded pause. + if (state_ != TransportState::kOpen || seq_num != current_seq_num_) { + // Async-close occurred! + return IOResult::kInterrupt; + } else { + CHECK_GE(read_fd_.get(), 0); + } + IOResult res = ReadUpToMax(reinterpret_cast(data) + nbytes, + ndata - nbytes, + /*out*/&read_len); + if (res != IOResult::kOk) { + return res; + } else { + nbytes += read_len; + } + + pollfds[0] = { read_fd_, POLLRDHUP | POLLIN, 0 }; + pollfds[1] = { wakeup_fd_, POLLIN, 0 }; + } + if (read_len == 0) { + // No more data. Sleep without locks until more is available. We don't actually check for any + // errors since possible ones are (1) the read_fd_ is closed or wakeup happens which are both + // fine since the wakeup_fd_ or the poll failing will wake us up. + int poll_res = poll(pollfds, 2, -1); + if (poll_res < 0) { + DT_IO_ERROR("Failed to poll!"); + } + // Clear the wakeup_fd regardless. + uint64_t val; + int unused = read(wakeup_fd_, &val, sizeof(val)); + DCHECK(unused == sizeof(val) || errno == EAGAIN); + if (poll_res < 0) { + return IOResult::kError; + } + } + } + return IOResult::kOk; +} + +// A helper that allows us to lock the eventfd 'fd'. +class ScopedEventFdLock { + public: + explicit ScopedEventFdLock(const android::base::unique_fd& fd) : fd_(fd), data_(0) { + TEMP_FAILURE_RETRY(read(fd_, &data_, sizeof(data_))); + } + + ~ScopedEventFdLock() { + TEMP_FAILURE_RETRY(write(fd_, &data_, sizeof(data_))); + } + + private: + const android::base::unique_fd& fd_; + uint64_t data_; +}; + +IOResult FdForwardTransport::WriteFullyWithoutChecks(const void* data, size_t ndata) { + ScopedEventFdLock sefdl(write_lock_fd_); + const uint8_t* bdata = static_cast(data); + size_t nbytes = 0; + while (nbytes < ndata) { + int res = TEMP_FAILURE_RETRY(write(write_fd_, bdata + nbytes, ndata - nbytes)); + if (res < 0) { + DT_IO_ERROR("Failed write()"); + return IOResult::kError; + } else if (res == 0) { + return IOResult::kEOF; + } else { + nbytes += res; + } + } + return IOResult::kOk; +} + +IOResult FdForwardTransport::WriteFully(const void* data, size_t ndata) { + std::lock_guard lk(state_mutex_); + if (state_ != TransportState::kOpen) { + return IOResult::kInterrupt; + } + return WriteFullyWithoutChecks(data, ndata); +} + +static void SendAcceptMessage(int fd) { + TEMP_FAILURE_RETRY(send(fd, kAcceptMessage, sizeof(kAcceptMessage), MSG_EOR)); +} + +IOResult FdForwardTransport::ReceiveFdsFromSocket() { + union { + cmsghdr cm; + uint8_t buffer[CMSG_SPACE(sizeof(FdSet))]; + } msg_union; + // We don't actually care about the data. Only FDs. We need to have an iovec anyway to tell if we + // got the values or not though. + char dummy = '\0'; + iovec iov; + iov.iov_base = &dummy; + iov.iov_len = sizeof(dummy); + + msghdr msg; + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = msg_union.buffer; + msg.msg_controllen = sizeof(msg_union.buffer); + + cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = msg.msg_controllen; + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + memset(reinterpret_cast(CMSG_DATA(cmsg)), -1, FdSet::kDataLength); + + int res = TEMP_FAILURE_RETRY(recvmsg(listen_fd_, &msg, 0)); + if (res <= 0) { + DT_IO_ERROR("Failed to receive fds!"); + return IOResult::kError; + } + FdSet out_fds = FdSet::ReadData(CMSG_DATA(cmsg)); + if (out_fds.read_fd_ < 0 || out_fds.write_fd_ < 0 || out_fds.write_lock_fd_ < 0) { + DT_IO_ERROR("Received fds were invalid!"); + if (out_fds.read_fd_ >= 0) { + close(out_fds.read_fd_); + } + if (out_fds.write_fd_ >= 0) { + close(out_fds.write_fd_); + } + if (out_fds.write_lock_fd_ >= 0) { + close(out_fds.write_lock_fd_); + } + return IOResult::kError; + } + + read_fd_.reset(out_fds.read_fd_); + write_fd_.reset(out_fds.write_fd_); + write_lock_fd_.reset(out_fds.write_lock_fd_); + + // We got the fds. Send ack. + close_notify_fd_.reset(dup(listen_fd_)); + SendAcceptMessage(close_notify_fd_); + + return IOResult::kOk; +} + +// Accept the connection. Note that we match the behavior of other transports which is to just close +// the connection and try again if we get a bad handshake. +jdwpTransportError FdForwardTransport::Accept() { + // TODO Work with timeouts. + while (true) { + std::unique_lock lk(state_mutex_); + while (!ChangeState(TransportState::kListening, TransportState::kOpening)) { + if (state_ == TransportState::kClosed || + state_ == TransportState::kOpen) { + return ERR(ILLEGAL_STATE); + } + state_cv_.wait(lk); + } + + DCHECK_NE(listen_fd_.get(), -1); + if (ReceiveFdsFromSocket() != IOResult::kOk) { + CHECK(ChangeState(TransportState::kOpening, TransportState::kListening)); + return ERR(IO_ERROR); + } + + current_seq_num_++; + + // Moved to the opening state. + char handshake_recv[sizeof(kJdwpHandshake)]; + memset(handshake_recv, 0, sizeof(handshake_recv)); + IOResult res = ReadFullyWithoutChecks(handshake_recv, sizeof(handshake_recv)); + if (res != IOResult::kOk || + strncmp(handshake_recv, kJdwpHandshake, sizeof(kJdwpHandshake)) != 0) { + DT_IO_ERROR("Failed to read handshake"); + CHECK(ChangeState(TransportState::kOpening, TransportState::kListening)); + CloseFdsLocked(); + // Retry. + continue; + } + res = WriteFullyWithoutChecks(kJdwpHandshake, sizeof(kJdwpHandshake)); + if (res != IOResult::kOk) { + DT_IO_ERROR("Failed to write handshake"); + CHECK(ChangeState(TransportState::kOpening, TransportState::kListening)); + CloseFdsLocked(); + // Retry. + continue; + } + break; + } + CHECK(ChangeState(TransportState::kOpening, TransportState::kOpen)); + return OK; +} + +void SendClosingMessage(int fd) { + if (fd >= 0) { + TEMP_FAILURE_RETRY(send(fd, kCloseMessage, sizeof(kCloseMessage), MSG_EOR)); + } +} + +// Actually close the fds associated with this transport. +void FdForwardTransport::CloseFdsLocked() { + // We have a different set of fd's now. Increase the seq number. + current_seq_num_++; + + // All access to these is locked under the state_mutex_ so we are safe to close these. + { + ScopedEventFdLock sefdl(write_lock_fd_); + if (close_notify_fd_ >= 0) { + SendClosingMessage(close_notify_fd_); + } + close_notify_fd_.reset(); + read_fd_.reset(); + write_fd_.reset(); + close_notify_fd_.reset(); + } + write_lock_fd_.reset(); + + // Send a wakeup in case we have any in-progress reads/writes. + uint64_t data = 1; + TEMP_FAILURE_RETRY(write(wakeup_fd_, &data, sizeof(data))); +} + +jdwpTransportError FdForwardTransport::Close() { + std::lock_guard lk(state_mutex_); + jdwpTransportError res = + ChangeState(TransportState::kOpen, TransportState::kClosed) ? OK : ERR(ILLEGAL_STATE); + // Send a wakeup after changing the state even if nothing actually happened. + uint64_t data = 1; + TEMP_FAILURE_RETRY(write(wakeup_fd_, &data, sizeof(data))); + if (res == OK) { + CloseFdsLocked(); + } + return res; +} + +// A helper class to read and parse the JDWP packet. +class PacketReader { + public: + PacketReader(FdForwardTransport* transport, jdwpPacket* pkt) + : transport_(transport), + pkt_(pkt), + is_eof_(false), + is_err_(false) {} + bool ReadFully() { + // Zero out. + memset(pkt_, 0, sizeof(jdwpPacket)); + int32_t len = ReadInt32(); // read len + if (is_err_) { + return false; + } else if (is_eof_) { + return true; + } else if (len < 11) { + transport_->DT_IO_ERROR("Packet with len < 11 received!"); + return false; + } + pkt_->type.cmd.len = len; + pkt_->type.cmd.id = ReadInt32(); + pkt_->type.cmd.flags = ReadByte(); + if (is_err_) { + return false; + } else if (is_eof_) { + return true; + } else if ((pkt_->type.reply.flags & JDWPTRANSPORT_FLAGS_REPLY) == JDWPTRANSPORT_FLAGS_REPLY) { + ReadReplyPacket(); + } else { + ReadCmdPacket(); + } + return !is_err_; + } + + private: + void ReadReplyPacket() { + pkt_->type.reply.errorCode = ReadInt16(); + pkt_->type.reply.data = ReadRemaining(); + } + + void ReadCmdPacket() { + pkt_->type.cmd.cmdSet = ReadByte(); + pkt_->type.cmd.cmd = ReadByte(); + pkt_->type.cmd.data = ReadRemaining(); + } + + template + T HandleResult(IOResult res, T val, T fail) { + switch (res) { + case IOResult::kError: + is_err_ = true; + return fail; + case IOResult::kOk: + return val; + case IOResult::kEOF: + is_eof_ = true; + pkt_->type.cmd.len = 0; + return fail; + case IOResult::kInterrupt: + transport_->DT_IO_ERROR("Failed to read, concurrent close!"); + is_err_ = true; + return fail; + } + } + + jbyte* ReadRemaining() { + if (is_eof_ || is_err_) { + return nullptr; + } + jbyte* out = nullptr; + jint rem = pkt_->type.cmd.len - 11; + CHECK_GE(rem, 0); + if (rem == 0) { + return nullptr; + } else { + out = reinterpret_cast(transport_->Alloc(rem)); + IOResult res = transport_->ReadFully(out, rem); + jbyte* ret = HandleResult(res, out, static_cast(nullptr)); + if (ret != out) { + transport_->Free(out); + } + return ret; + } + } + + jbyte ReadByte() { + if (is_eof_ || is_err_) { + return -1; + } + jbyte out; + IOResult res = transport_->ReadFully(&out, sizeof(out)); + return HandleResult(res, NetworkToHost(out), static_cast(-1)); + } + + jshort ReadInt16() { + if (is_eof_ || is_err_) { + return -1; + } + jshort out; + IOResult res = transport_->ReadFully(&out, sizeof(out)); + return HandleResult(res, NetworkToHost(out), static_cast(-1)); + } + + jint ReadInt32() { + if (is_eof_ || is_err_) { + return -1; + } + jint out; + IOResult res = transport_->ReadFully(&out, sizeof(out)); + return HandleResult(res, NetworkToHost(out), -1); + } + + FdForwardTransport* transport_; + jdwpPacket* pkt_; + bool is_eof_; + bool is_err_; +}; + +jdwpTransportError FdForwardTransport::ReadPacket(jdwpPacket* pkt) { + if (pkt == nullptr) { + return ERR(ILLEGAL_ARGUMENT); + } + PacketReader reader(this, pkt); + if (reader.ReadFully()) { + return OK; + } else { + return ERR(IO_ERROR); + } +} + +// A class that writes a packet to the transport. +class PacketWriter { + public: + PacketWriter(FdForwardTransport* transport, const jdwpPacket* pkt) + : transport_(transport), pkt_(pkt), data_() {} + + bool WriteFully() { + PushInt32(pkt_->type.cmd.len); + PushInt32(pkt_->type.cmd.id); + PushByte(pkt_->type.cmd.flags); + if ((pkt_->type.reply.flags & JDWPTRANSPORT_FLAGS_REPLY) == JDWPTRANSPORT_FLAGS_REPLY) { + PushInt16(pkt_->type.reply.errorCode); + PushData(pkt_->type.reply.data, pkt_->type.reply.len - 11); + } else { + PushByte(pkt_->type.cmd.cmdSet); + PushByte(pkt_->type.cmd.cmd); + PushData(pkt_->type.cmd.data, pkt_->type.cmd.len - 11); + } + IOResult res = transport_->WriteFully(data_.data(), data_.size()); + return res == IOResult::kOk; + } + + private: + void PushInt32(int32_t data) { + data = HostToNetwork(data); + PushData(&data, sizeof(data)); + } + void PushInt16(int16_t data) { + data = HostToNetwork(data); + PushData(&data, sizeof(data)); + } + void PushByte(jbyte data) { + data_.push_back(HostToNetwork(data)); + } + + void PushData(void* d, size_t size) { + uint8_t* bytes = reinterpret_cast(d); + data_.insert(data_.end(), bytes, bytes + size); + } + + FdForwardTransport* transport_; + const jdwpPacket* pkt_; + std::vector data_; +}; + +jdwpTransportError FdForwardTransport::WritePacket(const jdwpPacket* pkt) { + if (pkt == nullptr) { + return ERR(ILLEGAL_ARGUMENT); + } + PacketWriter writer(this, pkt); + if (writer.WriteFully()) { + return OK; + } else { + return ERR(IO_ERROR); + } +} + +jboolean FdForwardTransport::IsOpen() { + return state_ == TransportState::kOpen; +} + +void* FdForwardTransport::Alloc(size_t s) { + return mem_.alloc(s); +} + +void FdForwardTransport::Free(void* data) { + mem_.free(data); +} + +jdwpTransportError FdForwardTransport::GetLastError(/*out*/char** err) { + std::string data = global_last_error_; + *err = reinterpret_cast(Alloc(data.size() + 1)); + strcpy(*err, data.c_str()); + return OK; +} + +static FdForwardTransport* AsFdForward(jdwpTransportEnv* env) { + return reinterpret_cast(env); +} + +static jdwpTransportError ParseAddress(const std::string& addr, + /*out*/int* listen_sock) { + if (!android::base::ParseInt(addr.c_str(), listen_sock) || *listen_sock < 0) { + LOG(ERROR) << "address format is not " << addr; + return ERR(ILLEGAL_ARGUMENT); + } + return OK; +} + +class JdwpTransportFunctions { + public: + static jdwpTransportError GetCapabilities(jdwpTransportEnv* env ATTRIBUTE_UNUSED, + /*out*/ JDWPTransportCapabilities* capabilities_ptr) { + // We don't support any of the optional capabilities (can_timeout_attach, can_timeout_accept, + // can_timeout_handshake) so just return a zeroed capabilities ptr. + // TODO We should maybe support these timeout options. + memset(capabilities_ptr, 0, sizeof(JDWPTransportCapabilities)); + return OK; + } + + // Address is + static jdwpTransportError Attach(jdwpTransportEnv* env, + const char* address, + jlong attach_timeout ATTRIBUTE_UNUSED, + jlong handshake_timeout ATTRIBUTE_UNUSED) { + if (address == nullptr || *address == '\0') { + return ERR(ILLEGAL_ARGUMENT); + } + int listen_fd; + jdwpTransportError err = ParseAddress(address, &listen_fd); + if (err != OK) { + return err; + } + return AsFdForward(env)->PerformAttach(listen_fd); + } + + static jdwpTransportError StartListening(jdwpTransportEnv* env, + const char* address, + /*out*/ char** actual_address) { + if (address == nullptr || *address == '\0') { + return ERR(ILLEGAL_ARGUMENT); + } + int listen_fd; + jdwpTransportError err = ParseAddress(address, &listen_fd); + if (err != OK) { + return err; + } + err = AsFdForward(env)->SetupListen(listen_fd); + if (err != OK) { + return err; + } + if (actual_address != nullptr) { + *actual_address = reinterpret_cast(AsFdForward(env)->Alloc(strlen(address) + 1)); + memcpy(*actual_address, address, strlen(address) + 1); + } + return OK; + } + + static jdwpTransportError StopListening(jdwpTransportEnv* env) { + return AsFdForward(env)->StopListening(); + } + + static jdwpTransportError Accept(jdwpTransportEnv* env, + jlong accept_timeout ATTRIBUTE_UNUSED, + jlong handshake_timeout ATTRIBUTE_UNUSED) { + return AsFdForward(env)->Accept(); + } + + static jboolean IsOpen(jdwpTransportEnv* env) { + return AsFdForward(env)->IsOpen(); + } + + static jdwpTransportError Close(jdwpTransportEnv* env) { + return AsFdForward(env)->Close(); + } + + static jdwpTransportError ReadPacket(jdwpTransportEnv* env, jdwpPacket *pkt) { + return AsFdForward(env)->ReadPacket(pkt); + } + + static jdwpTransportError WritePacket(jdwpTransportEnv* env, const jdwpPacket* pkt) { + return AsFdForward(env)->WritePacket(pkt); + } + + static jdwpTransportError GetLastError(jdwpTransportEnv* env, char** error) { + return AsFdForward(env)->GetLastError(error); + } +}; + +// The actual struct holding all the entrypoints into the jdwpTransport interface. +const jdwpTransportNativeInterface_ gTransportInterface = { + nullptr, // reserved1 + JdwpTransportFunctions::GetCapabilities, + JdwpTransportFunctions::Attach, + JdwpTransportFunctions::StartListening, + JdwpTransportFunctions::StopListening, + JdwpTransportFunctions::Accept, + JdwpTransportFunctions::IsOpen, + JdwpTransportFunctions::Close, + JdwpTransportFunctions::ReadPacket, + JdwpTransportFunctions::WritePacket, + JdwpTransportFunctions::GetLastError, +}; + +extern "C" +JNIEXPORT jint JNICALL jdwpTransport_OnLoad(JavaVM* vm ATTRIBUTE_UNUSED, + jdwpTransportCallback* cb, + jint version, + jdwpTransportEnv** /*out*/env) { + if (version != JDWPTRANSPORT_VERSION_1_0) { + LOG(ERROR) << "unknown version " << version; + return JNI_EVERSION; + } + void* data = cb->alloc(sizeof(FdForwardTransport)); + if (data == nullptr) { + LOG(ERROR) << "Failed to allocate data for transport!"; + return JNI_ENOMEM; + } + FdForwardTransport* transport = + new (data) FdForwardTransport(cb); + transport->functions = &gTransportInterface; + *env = transport; + return JNI_OK; +} + +} // namespace dt_fd_forward diff --git a/dt_fd_forward/dt_fd_forward.h b/dt_fd_forward/dt_fd_forward.h new file mode 100644 index 0000000000000000000000000000000000000000..9303c59acd14ef22ae8916d30d887ef3290b000f --- /dev/null +++ b/dt_fd_forward/dt_fd_forward.h @@ -0,0 +1,154 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jdwpTransport.h. This implementation + * is licensed under the same terms as the file jdwpTransport.h. The + * copyright and license information for the file jdwpTranport.h follows. + * + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ART_DT_FD_FORWARD_DT_FD_FORWARD_H_ +#define ART_DT_FD_FORWARD_DT_FD_FORWARD_H_ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "fd_transport.h" + +namespace dt_fd_forward { + +static constexpr uint8_t kReplyFlag = 0x80; +// Macro and constexpr to make error values less annoying to write. +#define ERR(e) JDWPTRANSPORT_ERROR_ ## e +static constexpr jdwpTransportError OK = ERR(NONE); + +static constexpr const char kJdwpHandshake[14] = { + 'J', 'D', 'W', 'P', '-', 'H', 'a', 'n', 'd', 's', 'h', 'a', 'k', 'e' +}; // "JDWP-Handshake" + +enum class TransportState { + kClosed, // Main state. + kListenSetup, // Transient, wait for the state to change before proceeding. + kListening, // Main state. + kOpening, // Transient, wait for the state to change before proceeding. + kOpen, // Main state. +}; + +enum class IOResult { + kOk, kInterrupt, kError, kEOF, +}; + +class PacketReader; +class PacketWriter; + +// TODO It would be good to get the thread-safety analysis checks working but first we would need to +// use something other than std::mutex which does not have the annotations required. +class FdForwardTransport : public jdwpTransportEnv { + public: + explicit FdForwardTransport(jdwpTransportCallback* cb); + ~FdForwardTransport(); + + jdwpTransportError PerformAttach(int listen_fd); + jdwpTransportError SetupListen(int listen_fd); + jdwpTransportError StopListening(); + + jboolean IsOpen(); + + jdwpTransportError WritePacket(const jdwpPacket* pkt); + jdwpTransportError ReadPacket(jdwpPacket* pkt); + jdwpTransportError Close(); + jdwpTransportError Accept(); + jdwpTransportError GetLastError(/*out*/char** description); + + void* Alloc(size_t data); + void Free(void* data); + + private: + void SetLastError(const std::string& desc); + + bool ChangeState(TransportState old_state, TransportState new_state); // REQUIRES(state_mutex_); + + IOResult ReceiveFdsFromSocket(); + + IOResult WriteFully(const void* data, size_t ndata); // REQUIRES(!state_mutex_); + IOResult WriteFullyWithoutChecks(const void* data, size_t ndata); // REQUIRES(state_mutex_); + IOResult ReadFully(void* data, size_t ndata); // REQUIRES(!state_mutex_); + IOResult ReadUpToMax(void* data, size_t ndata, /*out*/size_t* amount_read); + // REQUIRES(state_mutex_); + IOResult ReadFullyWithoutChecks(void* data, size_t ndata); // REQUIRES(state_mutex_); + + void CloseFdsLocked(); // REQUIRES(state_mutex_) + + // The allocation/deallocation functions. + jdwpTransportCallback mem_; + + // Input from the server; + android::base::unique_fd read_fd_; // GUARDED_BY(state_mutex_); + // Output to the server; + android::base::unique_fd write_fd_; // GUARDED_BY(state_mutex_); + + // an eventfd passed with the write_fd to the transport that we will 'read' from to get a lock on + // the write_fd_. The other side must not hold it for unbounded time. + android::base::unique_fd write_lock_fd_; // GUARDED_BY(state_mutex_); + + // Eventfd we will use to wake-up paused reads for close(). + android::base::unique_fd wakeup_fd_; + + // Socket we will get the read/write fd's from. + android::base::unique_fd listen_fd_; + + // Fd we will write close notification to. This is a dup of listen_fd_. + android::base::unique_fd close_notify_fd_; + + TransportState state_; // GUARDED_BY(state_mutex_); + + std::mutex state_mutex_; + std::condition_variable state_cv_; + + // A counter that we use to make sure we don't do half a read on one and half on another fd. + std::atomic current_seq_num_; + + friend class PacketReader; // For ReadFullyWithInterrupt + friend class PacketWriter; // For WriteFullyWithInterrupt +}; + +} // namespace dt_fd_forward + +#endif // ART_DT_FD_FORWARD_DT_FD_FORWARD_H_ diff --git a/dt_fd_forward/export/Android.bp b/dt_fd_forward/export/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..c3a63212b9fffd9bf66fc6b0f609500c4d2c618f --- /dev/null +++ b/dt_fd_forward/export/Android.bp @@ -0,0 +1,22 @@ +// +// 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. +// + +cc_library_headers { + name: "dt_fd_forward_export", + export_include_dirs: [ "." ], + host_supported: true, + device_supported: true, +} diff --git a/dt_fd_forward/export/MODULE_LICENSE_APACHE2 b/dt_fd_forward/export/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/dt_fd_forward/export/fd_transport.h b/dt_fd_forward/export/fd_transport.h new file mode 100644 index 0000000000000000000000000000000000000000..245f0c22757a4a79747f822da1bd625f630f14fc --- /dev/null +++ b/dt_fd_forward/export/fd_transport.h @@ -0,0 +1,69 @@ +// 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_DT_FD_FORWARD_EXPORT_FD_TRANSPORT_H_ +#define ART_DT_FD_FORWARD_EXPORT_FD_TRANSPORT_H_ + +#include + +namespace dt_fd_forward { + +// The file-descriptors sent over a socket to the dt_fd_forward transport. +struct FdSet { + // A fd that can be read from which provides the JDWP data. + int read_fd_; + + // A fd that can be written to in order to provide JDWP responses and events. + int write_fd_; + + // A eventfd that can be locked to ensure that writes to write_fd_ are atomic. This must be held + // when writing to write_fd_. This allows the proxy to insert packets into the response stream + // without having to parse it. + int write_lock_fd_; + + static constexpr size_t kDataLength = sizeof(int) * 3; + void WriteData(void* buf) { + int* ibuf = reinterpret_cast(buf); + ibuf[0] = read_fd_; + ibuf[1] = write_fd_; + ibuf[2] = write_lock_fd_; + } + + static FdSet ReadData(void* buf) { + int* ibuf = reinterpret_cast(buf); + return FdSet { ibuf[0], ibuf[1], ibuf[2] }; + } +}; + +// This message is sent over the fd associated with the transport when we are listening for fds. +static constexpr char kListenStartMessage[] = "dt_fd_forward:START-LISTEN"; + +// This message is sent over the fd associated with the transport when we stop listening for fds. +static constexpr char kListenEndMessage[] = "dt_fd_forward:END-LISTEN"; + +// This message is sent over the fd associated with the transport when we have accepted a +// connection. This is sent before any handshaking has occurred. It is simply an acknowledgment +// that the FdSet has been received. This will be paired with a single CLOSING message when these +// fds are closed. +static constexpr char kAcceptMessage[] = "dt_fd_forward:ACCEPTED"; + +// This message is sent over the fd associated with the transport when we are closing the fds. This +// can be used by the proxy to send additional data on a dup'd fd. The write_lock_fd_ will be held +// until the other two fds are closed and then it will be released and closed. +static constexpr char kCloseMessage[] = "dt_fd_forward:CLOSING"; + +} // namespace dt_fd_forward + +#endif // ART_DT_FD_FORWARD_EXPORT_FD_TRANSPORT_H_ diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc index f0c915874856579adf1bf179c1a7c55a4128070b..8aa638a0e0c7700382ff3f4b3760a1fefa399671 100644 --- a/imgdiag/imgdiag.cc +++ b/imgdiag/imgdiag.cc @@ -1150,10 +1150,10 @@ class ImgDiagDumper { bool found_boot_map = false; // Find the memory map only for boot.art - for (const backtrace_map_t& map : *tmp_proc_maps) { - if (EndsWith(map.name, GetImageLocationBaseName())) { - if ((map.flags & PROT_WRITE) != 0) { - boot_map_ = map; + for (const backtrace_map_t* map : *tmp_proc_maps) { + if (EndsWith(map->name, GetImageLocationBaseName())) { + if ((map->flags & PROT_WRITE) != 0) { + boot_map_ = *map; found_boot_map = true; break; } @@ -1610,7 +1610,7 @@ class ImgDiagDumper { // BacktraceMap used for finding the memory mapping of the image file. std::unique_ptr proc_maps_; // Boot image mapping. - backtrace_map_t boot_map_{}; // NOLINT + backtrace_map_t boot_map_{}; // The size of the boot image mapping. size_t boot_map_size_; // The contents of /proc//maps. diff --git a/libart_fake/Android.mk b/libart_fake/Android.mk index ed868a5bd5b9617cda50c47ef85b662c283edb8f..96e6a149034cceb7d7228708c0201c5055b3e5e6 100644 --- a/libart_fake/Android.mk +++ b/libart_fake/Android.mk @@ -22,6 +22,7 @@ LOCAL_INSTALLED_MODULE_STEM := libart.so LOCAL_SDK_VERSION := 9 LOCAL_CPP_EXTENSION := .cc LOCAL_SRC_FILES := fake.cc +LOCAL_CFLAGS := -Wall -Werror LOCAL_SHARED_LIBRARIES := liblog ifdef TARGET_2ND_ARCH diff --git a/oatdump/Android.mk b/oatdump/Android.mk index 906404b18cb85f705386b6f2a89154dcf667b6ff..667c37c33dc70d07cc8df4c8504d115bef49e663 100644 --- a/oatdump/Android.mk +++ b/oatdump/Android.mk @@ -41,7 +41,7 @@ endif .PHONY: dump-oat-core-target-$(TARGET_ARCH) ifeq ($(ART_BUILD_TARGET),true) -dump-oat-core-target-$(TARGET_ARCH): $(TARGET_CORE_IMAGE_default_$(ART_PHONY_TEST_TARGET_SUFFIX)) $(OATDUMP) +dump-oat-core-target-$(TARGET_ARCH): $(TARGET_CORE_IMAGE_DEFAULT_$(ART_PHONY_TEST_TARGET_SUFFIX)) $(OATDUMP) $(OATDUMP) --image=$(TARGET_CORE_IMG_LOCATION) \ --output=$(ART_DUMP_OAT_PATH)/core.target.$(TARGET_ARCH).oatdump.txt --instruction-set=$(TARGET_ARCH) @echo Output in $(ART_DUMP_OAT_PATH)/core.target.$(TARGET_ARCH).oatdump.txt @@ -50,7 +50,7 @@ endif ifdef TARGET_2ND_ARCH .PHONY: dump-oat-core-target-$(TARGET_2ND_ARCH) ifeq ($(ART_BUILD_TARGET),true) -dump-oat-core-target-$(TARGET_2ND_ARCH): $(TARGET_CORE_IMAGE_default_$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)) $(OATDUMP) +dump-oat-core-target-$(TARGET_2ND_ARCH): $(TARGET_CORE_IMAGE_DEFAULT_$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)) $(OATDUMP) $(OATDUMP) --image=$(TARGET_CORE_IMG_LOCATION) \ --output=$(ART_DUMP_OAT_PATH)/core.target.$(TARGET_2ND_ARCH).oatdump.txt --instruction-set=$(TARGET_2ND_ARCH) @echo Output in $(ART_DUMP_OAT_PATH)/core.target.$(TARGET_2ND_ARCH).oatdump.txt diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index b20fa902a251caf15f79a272f94070509e9221a8..b2e19a89d6993810ebb41425ec63d232b7d58607 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -32,10 +32,12 @@ #include "arch/instruction_set_features.h" #include "art_field-inl.h" #include "art_method-inl.h" +#include "base/bit_utils_iterator.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "class_linker-inl.h" #include "class_linker.h" +#include "code_item_accessors-inl.h" #include "compiled_method.h" #include "debug/elf_debug_writer.h" #include "debug/method_debug_info.h" @@ -50,6 +52,7 @@ #include "imtable-inl.h" #include "indenter.h" #include "subtype_check.h" +#include "index_bss_mapping.h" #include "interpreter/unstarted_runtime.h" #include "linker/buffered_output_stream.h" #include "linker/elf_builder.h" @@ -143,13 +146,10 @@ class OatSymbolizer FINAL { auto* rodata = builder_->GetRoData(); auto* text = builder_->GetText(); - auto* bss = builder_->GetBss(); const uint8_t* rodata_begin = oat_file_->Begin(); const size_t rodata_size = oat_file_->GetOatHeader().GetExecutableOffset(); - if (no_bits_) { - rodata->WriteNoBitsSection(rodata_size); - } else { + if (!no_bits_) { rodata->Start(); rodata->WriteFully(rodata_begin, rodata_size); rodata->End(); @@ -157,18 +157,12 @@ class OatSymbolizer FINAL { const uint8_t* text_begin = oat_file_->Begin() + rodata_size; const size_t text_size = oat_file_->End() - text_begin; - if (no_bits_) { - text->WriteNoBitsSection(text_size); - } else { + if (!no_bits_) { text->Start(); text->WriteFully(text_begin, text_size); text->End(); } - if (oat_file_->BssSize() != 0) { - bss->WriteNoBitsSection(oat_file_->BssSize()); - } - if (isa == InstructionSet::kMips || isa == InstructionSet::kMips64) { builder_->WriteMIPSabiflagsSection(); } @@ -274,7 +268,7 @@ class OatSymbolizer FINAL { ClassDataItemIterator it(dex_file, class_data); uint32_t class_method_idx = 0; it.SkipAllFields(); - for (; it.HasNextDirectMethod() || it.HasNextVirtualMethod(); it.Next()) { + for (; it.HasNextMethod(); it.Next()) { WalkOatMethod(oat_class.GetOatMethod(class_method_idx++), dex_file, class_def_index, @@ -534,6 +528,29 @@ class OatDumper { } cumulative.Add(data); + + // Dump .bss entries. + DumpBssEntries( + os, + "ArtMethod", + oat_dex_file->GetMethodBssMapping(), + dex_file->NumMethodIds(), + static_cast(GetInstructionSetPointerSize(instruction_set_)), + [=](uint32_t index) { return dex_file->PrettyMethod(index); }); + DumpBssEntries( + os, + "Class", + oat_dex_file->GetTypeBssMapping(), + dex_file->NumTypeIds(), + sizeof(GcRoot), + [=](uint32_t index) { return dex_file->PrettyType(dex::TypeIndex(index)); }); + DumpBssEntries( + os, + "String", + oat_dex_file->GetStringBssMapping(), + dex_file->NumStringIds(), + sizeof(GcRoot), + [=](uint32_t index) { return dex_file->StringDataByIdx(dex::StringIndex(index)); }); } os << "Cumulative dex file data\n"; cumulative.Dump(os); @@ -572,46 +589,36 @@ class OatDumper { } if (options_.export_dex_location_) { - if (kIsVdexEnabled) { - std::string error_msg; - std::string vdex_filename = GetVdexFilename(oat_file_.GetLocation()); - if (!OS::FileExists(vdex_filename.c_str())) { - os << "File " << vdex_filename.c_str() << " does not exist\n"; - return false; - } + std::string error_msg; + std::string vdex_filename = GetVdexFilename(oat_file_.GetLocation()); + if (!OS::FileExists(vdex_filename.c_str())) { + os << "File " << vdex_filename.c_str() << " does not exist\n"; + return false; + } - DexFileUniqV vdex_dex_files; - std::unique_ptr vdex_file = OpenVdexUnquicken(vdex_filename, - &vdex_dex_files, - &error_msg); - if (vdex_file.get() == nullptr) { - os << "Failed to open vdex file: " << error_msg << "\n"; - return false; - } - if (oat_dex_files_.size() != vdex_dex_files.size()) { - os << "Dex files number in Vdex file does not match Dex files number in Oat file: " - << vdex_dex_files.size() << " vs " << oat_dex_files_.size() << '\n'; - return false; - } + DexFileUniqV vdex_dex_files; + std::unique_ptr vdex_file = OpenVdexUnquicken(vdex_filename, + &vdex_dex_files, + &error_msg); + if (vdex_file.get() == nullptr) { + os << "Failed to open vdex file: " << error_msg << "\n"; + return false; + } + if (oat_dex_files_.size() != vdex_dex_files.size()) { + os << "Dex files number in Vdex file does not match Dex files number in Oat file: " + << vdex_dex_files.size() << " vs " << oat_dex_files_.size() << '\n'; + return false; + } - size_t i = 0; - for (const auto& vdex_dex_file : vdex_dex_files) { - const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i]; - CHECK(oat_dex_file != nullptr); - CHECK(vdex_dex_file != nullptr); - if (!ExportDexFile(os, *oat_dex_file, vdex_dex_file.get())) { - success = false; - } - i++; - } - } else { - for (size_t i = 0; i < oat_dex_files_.size(); i++) { - const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i]; - CHECK(oat_dex_file != nullptr); - if (!ExportDexFile(os, *oat_dex_file, /* vdex_dex_file */ nullptr)) { - success = false; - } + size_t i = 0; + for (const auto& vdex_dex_file : vdex_dex_files) { + const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i]; + CHECK(oat_dex_file != nullptr); + CHECK(vdex_dex_file != nullptr); + if (!ExportDexFile(os, *oat_dex_file, vdex_dex_file.get())) { + success = false; } + i++; } } @@ -893,11 +900,7 @@ class OatDumper { ClassDataItemIterator it(*dex_file, class_data); it.SkipAllFields(); uint32_t class_method_index = 0; - while (it.HasNextDirectMethod()) { - AddOffsets(oat_class.GetOatMethod(class_method_index++)); - it.Next(); - } - while (it.HasNextVirtualMethod()) { + while (it.HasNextMethod()) { AddOffsets(oat_class.GetOatMethod(class_method_index++)); it.Next(); } @@ -979,11 +982,7 @@ class OatDumper { } ClassDataItemIterator it(dex_file, class_data); it.SkipAllFields(); - while (it.HasNextDirectMethod()) { - WalkCodeItem(dex_file, it.GetMethodCodeItem()); - it.Next(); - } - while (it.HasNextVirtualMethod()) { + while (it.HasNextMethod()) { WalkCodeItem(dex_file, it.GetMethodCodeItem()); it.Next(); } @@ -994,14 +993,15 @@ class OatDumper { if (code_item == nullptr) { return; } + CodeItemInstructionAccessor instructions(&dex_file, code_item); - const uint16_t* code_ptr = code_item->insns_; // If we inserted a new dex code item pointer, add to total code bytes. + const uint16_t* code_ptr = instructions.Insns(); if (dex_code_item_ptrs_.insert(code_ptr).second) { - dex_code_bytes_ += code_item->insns_size_in_code_units_ * sizeof(code_ptr[0]); + dex_code_bytes_ += instructions.InsnsSizeInCodeUnits() * sizeof(code_ptr[0]); } - for (const DexInstructionPcPair& inst : code_item->Instructions()) { + for (const DexInstructionPcPair& inst : instructions) { switch (inst->Opcode()) { case Instruction::CONST_STRING: { const dex::StringIndex string_index(inst->VRegB_21c()); @@ -1227,20 +1227,7 @@ class OatDumper { ClassDataItemIterator it(dex_file, class_data); it.SkipAllFields(); uint32_t class_method_index = 0; - while (it.HasNextDirectMethod()) { - if (!DumpOatMethod(vios, class_def, class_method_index, oat_class, dex_file, - it.GetMemberIndex(), it.GetMethodCodeItem(), - it.GetRawMemberAccessFlags(), &addr_found)) { - success = false; - } - if (addr_found) { - *stop_analysis = true; - return success; - } - class_method_index++; - it.Next(); - } - while (it.HasNextVirtualMethod()) { + while (it.HasNextMethod()) { if (!DumpOatMethod(vios, class_def, class_method_index, oat_class, dex_file, it.GetMemberIndex(), it.GetMethodCodeItem(), it.GetRawMemberAccessFlags(), &addr_found)) { @@ -1266,11 +1253,16 @@ class OatDumper { bool DumpOatMethod(VariableIndentationOutputStream* vios, const DexFile::ClassDef& class_def, uint32_t class_method_index, - const OatFile::OatClass& oat_class, const DexFile& dex_file, - uint32_t dex_method_idx, const DexFile::CodeItem* code_item, - uint32_t method_access_flags, bool* addr_found) { + const OatFile::OatClass& oat_class, + const DexFile& dex_file, + uint32_t dex_method_idx, + const DexFile::CodeItem* code_item, + uint32_t method_access_flags, + bool* addr_found) { bool success = true; + CodeItemDataAccessor code_item_accessor(&dex_file, code_item); + // TODO: Support regex std::string method_name = dex_file.GetMethodName(dex_file.GetMethodId(dex_method_idx)); if (method_name.find(options_.method_filter_) == std::string::npos) { @@ -1281,7 +1273,9 @@ class OatDumper { vios->Stream() << StringPrintf("%d: %s (dex_method_idx=%d)\n", class_method_index, pretty_method.c_str(), dex_method_idx); - if (options_.list_methods_) return success; + if (options_.list_methods_) { + return success; + } uint32_t oat_method_offsets_offset = oat_class.GetOatMethodOffsetsOffset(class_method_index); const OatMethodOffsets* oat_method_offsets = oat_class.GetOatMethodOffsets(class_method_index); @@ -1302,7 +1296,12 @@ class OatDumper { { vios->Stream() << "DEX CODE:\n"; ScopedIndentation indent2(vios); - DumpDexCode(vios->Stream(), dex_file, code_item); + if (code_item_accessor.HasCodeItem()) { + for (const DexInstructionPcPair& inst : code_item_accessor) { + vios->Stream() << StringPrintf("0x%04x: ", inst.DexPc()) << inst->DumpHexLE(5) + << StringPrintf("\t| %s\n", inst->DumpString(&dex_file).c_str()); + } + } } std::unique_ptr> hs; @@ -1373,7 +1372,7 @@ class OatDumper { vios->Stream() << StringPrintf("(offset=0x%08x)\n", vmap_table_offset); size_t vmap_table_offset_limit = - (kIsVdexEnabled && IsMethodGeneratedByDexToDexCompiler(oat_method, code_item)) + IsMethodGeneratedByDexToDexCompiler(oat_method, code_item_accessor) ? oat_file_.GetVdexFile()->Size() : method_header->GetCode() - oat_file_.Begin(); if (vmap_table_offset >= vmap_table_offset_limit) { @@ -1385,7 +1384,7 @@ class OatDumper { oat_method.GetVmapTableOffsetOffset()); success = false; } else if (options_.dump_vmap_) { - DumpVmapData(vios, oat_method, code_item); + DumpVmapData(vios, oat_method, code_item_accessor); } } { @@ -1406,7 +1405,7 @@ class OatDumper { // after it is dumped, but useful for understanding quick // code, so dumped here. ScopedIndentation indent2(vios); - DumpVregLocations(vios->Stream(), oat_method, code_item); + DumpVregLocations(vios->Stream(), oat_method, code_item_accessor); } { vios->Stream() << "CODE: "; @@ -1448,7 +1447,7 @@ class OatDumper { success = false; if (options_.disassemble_code_) { if (code_size_offset + kPrologueBytes <= oat_file_.Size()) { - DumpCode(vios, oat_method, code_item, true, kPrologueBytes); + DumpCode(vios, oat_method, code_item_accessor, true, kPrologueBytes); } } } else if (code_size > kMaxCodeSize) { @@ -1461,11 +1460,11 @@ class OatDumper { success = false; if (options_.disassemble_code_) { if (code_size_offset + kPrologueBytes <= oat_file_.Size()) { - DumpCode(vios, oat_method, code_item, true, kPrologueBytes); + DumpCode(vios, oat_method, code_item_accessor, true, kPrologueBytes); } } } else if (options_.disassemble_code_) { - DumpCode(vios, oat_method, code_item, !success, 0); + DumpCode(vios, oat_method, code_item_accessor, !success, 0); } } } @@ -1499,18 +1498,18 @@ class OatDumper { // Display data stored at the the vmap offset of an oat method. void DumpVmapData(VariableIndentationOutputStream* vios, const OatFile::OatMethod& oat_method, - const DexFile::CodeItem* code_item) { - if (IsMethodGeneratedByOptimizingCompiler(oat_method, code_item)) { + const CodeItemDataAccessor& code_item_accessor) { + if (IsMethodGeneratedByOptimizingCompiler(oat_method, code_item_accessor)) { // The optimizing compiler outputs its CodeInfo data in the vmap table. const void* raw_code_info = oat_method.GetVmapTable(); if (raw_code_info != nullptr) { CodeInfo code_info(raw_code_info); - DCHECK(code_item != nullptr); + DCHECK(code_item_accessor.HasCodeItem()); ScopedIndentation indent1(vios); MethodInfo method_info = oat_method.GetOatQuickMethodHeader()->GetOptimizedMethodInfo(); - DumpCodeInfo(vios, code_info, oat_method, *code_item, method_info); + DumpCodeInfo(vios, code_info, oat_method, code_item_accessor, method_info); } - } else if (IsMethodGeneratedByDexToDexCompiler(oat_method, code_item)) { + } else if (IsMethodGeneratedByDexToDexCompiler(oat_method, code_item_accessor)) { // We don't encode the size in the table, so just emit that we have quickened // information. ScopedIndentation indent(vios); @@ -1524,11 +1523,11 @@ class OatDumper { void DumpCodeInfo(VariableIndentationOutputStream* vios, const CodeInfo& code_info, const OatFile::OatMethod& oat_method, - const DexFile::CodeItem& code_item, + const CodeItemDataAccessor& code_item_accessor, const MethodInfo& method_info) { code_info.Dump(vios, oat_method.GetCodeOffset(), - code_item.registers_size_, + code_item_accessor.RegistersSize(), options_.dump_code_info_stack_maps_, instruction_set_, method_info); @@ -1539,7 +1538,7 @@ class OatDumper { return static_cast(InstructionSetPointerSize(isa)) + out_num * sizeof(uint32_t); } - static uint32_t GetVRegOffsetFromQuickCode(const DexFile::CodeItem* code_item, + static uint32_t GetVRegOffsetFromQuickCode(const CodeItemDataAccessor& code_item_accessor, uint32_t core_spills, uint32_t fp_spills, size_t frame_size, @@ -1557,8 +1556,8 @@ class OatDumper { int spill_size = POPCOUNT(core_spills) * GetBytesPerGprSpillLocation(isa) + POPCOUNT(fp_spills) * GetBytesPerFprSpillLocation(isa) + sizeof(uint32_t); // Filler. - int num_regs = code_item->registers_size_ - code_item->ins_size_; - int temp_threshold = code_item->registers_size_; + int num_regs = code_item_accessor.RegistersSize() - code_item_accessor.InsSize(); + int temp_threshold = code_item_accessor.RegistersSize(); const int max_num_special_temps = 1; if (reg == temp_threshold) { // The current method pointer corresponds to special location on stack. @@ -1568,7 +1567,7 @@ class OatDumper { * Special temporaries may have custom locations and the logic above deals with that. * However, non-special temporaries are placed relative to the outs. */ - int temps_start = code_item->outs_size_ * sizeof(uint32_t) + int temps_start = code_item_accessor.OutsSize() * sizeof(uint32_t) + static_cast(pointer_size) /* art method */; int relative_offset = (reg - (temp_threshold + max_num_special_temps)) * sizeof(uint32_t); return temps_start + relative_offset; @@ -1583,12 +1582,12 @@ class OatDumper { } void DumpVregLocations(std::ostream& os, const OatFile::OatMethod& oat_method, - const DexFile::CodeItem* code_item) { - if (code_item != nullptr) { - size_t num_locals_ins = code_item->registers_size_; - size_t num_ins = code_item->ins_size_; + const CodeItemDataAccessor& code_item_accessor) { + if (code_item_accessor.HasCodeItem()) { + size_t num_locals_ins = code_item_accessor.RegistersSize(); + size_t num_ins = code_item_accessor.InsSize(); size_t num_locals = num_locals_ins - num_ins; - size_t num_outs = code_item->outs_size_; + size_t num_outs = code_item_accessor.OutsSize(); os << "vr_stack_locations:"; for (size_t reg = 0; reg <= num_locals_ins; reg++) { @@ -1601,7 +1600,7 @@ class OatDumper { os << "\n\tlocals:"; } - uint32_t offset = GetVRegOffsetFromQuickCode(code_item, + uint32_t offset = GetVRegOffsetFromQuickCode(code_item_accessor, oat_method.GetCoreSpillMask(), oat_method.GetFpSpillMask(), oat_method.GetFrameSizeInBytes(), @@ -1623,37 +1622,30 @@ class OatDumper { } } - void DumpDexCode(std::ostream& os, const DexFile& dex_file, const DexFile::CodeItem* code_item) { - if (code_item != nullptr) { - for (const DexInstructionPcPair& inst : code_item->Instructions()) { - os << StringPrintf("0x%04x: ", inst.DexPc()) << inst->DumpHexLE(5) - << StringPrintf("\t| %s\n", inst->DumpString(&dex_file).c_str()); - } - } - } - // Has `oat_method` -- corresponding to the Dex `code_item` -- been compiled by // the optimizing compiler? - static bool IsMethodGeneratedByOptimizingCompiler(const OatFile::OatMethod& oat_method, - const DexFile::CodeItem* code_item) { + static bool IsMethodGeneratedByOptimizingCompiler( + const OatFile::OatMethod& oat_method, + const CodeItemDataAccessor& code_item_accessor) { // If the native GC map is null and the Dex `code_item` is not // null, then this method has been compiled with the optimizing // compiler. return oat_method.GetQuickCode() != nullptr && oat_method.GetVmapTable() != nullptr && - code_item != nullptr; + code_item_accessor.HasCodeItem(); } // Has `oat_method` -- corresponding to the Dex `code_item` -- been compiled by // the dextodex compiler? - static bool IsMethodGeneratedByDexToDexCompiler(const OatFile::OatMethod& oat_method, - const DexFile::CodeItem* code_item) { + static bool IsMethodGeneratedByDexToDexCompiler( + const OatFile::OatMethod& oat_method, + const CodeItemDataAccessor& code_item_accessor) { // If the quick code is null, the Dex `code_item` is not // null, and the vmap table is not null, then this method has been compiled // with the dextodex compiler. return oat_method.GetQuickCode() == nullptr && oat_method.GetVmapTable() != nullptr && - code_item != nullptr; + code_item_accessor.HasCodeItem(); } verifier::MethodVerifier* DumpVerifier(VariableIndentationOutputStream* vios, @@ -1770,7 +1762,8 @@ class OatDumper { }; void DumpCode(VariableIndentationOutputStream* vios, - const OatFile::OatMethod& oat_method, const DexFile::CodeItem* code_item, + const OatFile::OatMethod& oat_method, + const CodeItemDataAccessor& code_item_accessor, bool bad_input, size_t code_size) { const void* quick_code = oat_method.GetQuickCode(); @@ -1780,7 +1773,8 @@ class OatDumper { if (code_size == 0 || quick_code == nullptr) { vios->Stream() << "NO CODE!\n"; return; - } else if (!bad_input && IsMethodGeneratedByOptimizingCompiler(oat_method, code_item)) { + } else if (!bad_input && IsMethodGeneratedByOptimizingCompiler(oat_method, + code_item_accessor)) { // The optimizing compiler outputs its CodeInfo data in the vmap table. StackMapsHelper helper(oat_method.GetVmapTable(), instruction_set_); MethodInfo method_info(oat_method.GetOatQuickMethodHeader()->GetOptimizedMethodInfo()); @@ -1835,7 +1829,8 @@ class OatDumper { kBitsPerByte * location_catalog_bytes); // Dex register bytes. const size_t dex_register_bytes = - helper.GetCodeInfo().GetDexRegisterMapsSize(encoding, code_item->registers_size_); + helper.GetCodeInfo().GetDexRegisterMapsSize(encoding, + code_item_accessor.RegistersSize()); stats_.AddBits( Stats::kByteKindCodeInfoDexRegisterMap, kBitsPerByte * dex_register_bytes); @@ -1874,7 +1869,7 @@ class OatDumper { helper.GetEncoding(), method_info, oat_method.GetCodeOffset(), - code_item->registers_size_, + code_item_accessor.RegistersSize(), instruction_set_); do { helper.Next(); @@ -1892,6 +1887,40 @@ class OatDumper { } } + template + void DumpBssEntries(std::ostream& os, + const char* slot_type, + const IndexBssMapping* mapping, + uint32_t number_of_indexes, + size_t slot_size, + NameGetter name) { + os << ".bss mapping for " << slot_type << ": "; + if (mapping == nullptr) { + os << "empty.\n"; + return; + } + size_t index_bits = IndexBssMappingEntry::IndexBits(number_of_indexes); + size_t num_valid_indexes = 0u; + for (const IndexBssMappingEntry& entry : *mapping) { + num_valid_indexes += 1u + POPCOUNT(entry.GetMask(index_bits)); + } + os << mapping->size() << " entries for " << num_valid_indexes << " valid indexes.\n"; + os << std::hex; + for (const IndexBssMappingEntry& entry : *mapping) { + uint32_t index = entry.GetIndex(index_bits); + uint32_t mask = entry.GetMask(index_bits); + size_t bss_offset = entry.bss_offset - POPCOUNT(mask) * slot_size; + for (uint32_t n : LowToHighBits(mask)) { + size_t current_index = index - (32u - index_bits) + n; + os << " 0x" << bss_offset << ": " << slot_type << ": " << name(current_index) << "\n"; + bss_offset += slot_size; + } + DCHECK_EQ(bss_offset, entry.bss_offset); + os << " 0x" << bss_offset << ": " << slot_type << ": " << name(index) << "\n"; + } + os << std::dec; + } + const OatFile& oat_file_; const std::vector oat_dex_files_; const OatDumperOptions& options_; @@ -2230,7 +2259,7 @@ class ImageDumper { os << StringPrintf("null %s\n", PrettyDescriptor(field->GetTypeDescriptor()).c_str()); } else { // Grab the field type without causing resolution. - ObjPtr field_type = field->LookupType(); + ObjPtr field_type = field->LookupResolvedType(); if (field_type != nullptr) { PrettyObjectValue(os, field_type, value); } else { @@ -2502,8 +2531,8 @@ class ImageDumper { } } } else { - const DexFile::CodeItem* code_item = method->GetCodeItem(); - size_t dex_instruction_bytes = code_item->insns_size_in_code_units_ * 2; + CodeItemDataAccessor code_item_accessor(method); + size_t dex_instruction_bytes = code_item_accessor.InsnsSizeInCodeUnits() * 2; stats_.dex_instruction_bytes += dex_instruction_bytes; bool first_occurrence; diff --git a/openjdkjvm/OpenjdkJvm.cc b/openjdkjvm/OpenjdkJvm.cc index 29ebefddea1e2a86ac38601d838a96a5d61ba0f4..ff839f5126ecd2b42ec38acaeb0c19aed7712233 100644 --- a/openjdkjvm/OpenjdkJvm.cc +++ b/openjdkjvm/OpenjdkJvm.cc @@ -40,9 +40,10 @@ #include #include +#include + #include "../../libcore/ojluni/src/main/native/jvm.h" // TODO(narayan): fix it -#include "base/logging.h" #include "base/macros.h" #include "common_throws.h" #include "gc/heap.h" @@ -386,7 +387,7 @@ JNIEXPORT void JVM_Interrupt(JNIEnv* env, jobject jthread) { JNIEXPORT jboolean JVM_IsInterrupted(JNIEnv* env, jobject jthread, jboolean clearInterrupted) { if (clearInterrupted) { - return static_cast(env)->self->Interrupted() ? JNI_TRUE : JNI_FALSE; + return static_cast(env)->GetSelf()->Interrupted() ? JNI_TRUE : JNI_FALSE; } else { art::ScopedFastNativeObjectAccess soa(env); art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_); diff --git a/openjdkjvmti/Android.bp b/openjdkjvmti/Android.bp index aef4dfc2014ddb184c724b8e382b1d1e374f109c..9ba70681760de50a3e9d8d5ddc22b8a57e16853b 100644 --- a/openjdkjvmti/Android.bp +++ b/openjdkjvmti/Android.bp @@ -34,6 +34,7 @@ cc_defaults { "ti_class.cc", "ti_class_definition.cc", "ti_class_loader.cc", + "ti_ddms.cc", "ti_dump.cc", "ti_extension.cc", "ti_field.cc", diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc index 481492402ba87f40f7d9b770a50e8e57c03dd0bd..aae805569f33664ad3ff6600be8e5570e8982759 100644 --- a/openjdkjvmti/OpenjdkJvmTi.cc +++ b/openjdkjvmti/OpenjdkJvmTi.cc @@ -33,12 +33,14 @@ #include #include +#include + #include #include "jvmti.h" #include "art_jvmti.h" -#include "base/logging.h" +#include "base/logging.h" // For gLogVerbosity. #include "base/mutex.h" #include "events-inl.h" #include "jni_env_ext-inl.h" @@ -1014,14 +1016,21 @@ class JvmtiFunctions { return ERR(NONE); } - std::unique_ptr tmp(new jvmtiEventCallbacks()); - memset(tmp.get(), 0, sizeof(jvmtiEventCallbacks)); + // Lock the event_info_mutex_ while we replace the callbacks. + ArtJvmTiEnv* art_env = ArtJvmTiEnv::AsArtJvmTiEnv(env); + art::WriterMutexLock lk(art::Thread::Current(), art_env->event_info_mutex_); + std::unique_ptr tmp(new ArtJvmtiEventCallbacks()); + // Copy over the extension events. + tmp->CopyExtensionsFrom(art_env->event_callbacks.get()); + // Never overwrite the extension events. size_t copy_size = std::min(sizeof(jvmtiEventCallbacks), static_cast(size_of_callbacks)); copy_size = art::RoundDown(copy_size, sizeof(void*)); + // Copy non-extension events. memcpy(tmp.get(), callbacks, copy_size); - ArtJvmTiEnv::AsArtJvmTiEnv(env)->event_callbacks = std::move(tmp); + // replace the event table. + art_env->event_callbacks = std::move(tmp); return ERR(NONE); } @@ -1077,8 +1086,10 @@ class JvmtiFunctions { jint extension_event_index, jvmtiExtensionEvent callback) { ENSURE_VALID_ENV(env); - // We do not have any extension events, so any call is illegal. - return ExtensionUtil::SetExtensionEventCallback(env, extension_event_index, callback); + return ExtensionUtil::SetExtensionEventCallback(env, + extension_event_index, + callback, + &gEventHandler); } static jvmtiError GetPotentialCapabilities(jvmtiEnv* env, jvmtiCapabilities* capabilities_ptr) { @@ -1428,6 +1439,7 @@ class JvmtiFunctions { art::gLogVerbosity.third_party_jni = val; art::gLogVerbosity.threads = val; art::gLogVerbosity.verifier = val; + // Do not set verifier-debug. art::gLogVerbosity.image = val; // Note: can't switch systrace_lock_logging. That requires changing entrypoints. diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h index 97801e09aad4297d3d0333675bf81488264a8c52..2a8c2e91dfa41fe863a13e6113a0071e00e1e258 100644 --- a/openjdkjvmti/art_jvmti.h +++ b/openjdkjvmti/art_jvmti.h @@ -39,9 +39,10 @@ #include +#include + #include "deopt_manager.h" #include "base/casts.h" -#include "base/logging.h" #include "base/macros.h" #include "base/strlcpy.h" #include "base/mutex.h" @@ -68,7 +69,7 @@ struct ArtJvmTiEnv : public jvmtiEnv { jvmtiCapabilities capabilities; EventMasks event_masks; - std::unique_ptr event_callbacks; + std::unique_ptr event_callbacks; // Tagging is specific to the jvmtiEnv. std::unique_ptr object_tag_table; @@ -94,6 +95,10 @@ struct ArtJvmTiEnv : public jvmtiEnv { static ArtJvmTiEnv* AsArtJvmTiEnv(jvmtiEnv* env) { return art::down_cast(env); } + + // Top level lock. Nothing can be held when we get this except for mutator lock for full + // thread-suspension. + static art::Mutex *gEnvMutex ACQUIRED_AFTER(art::Locks::mutator_lock_); }; // Macro and constexpr to make error values less annoying to write. diff --git a/openjdkjvmti/events-inl.h b/openjdkjvmti/events-inl.h index 7f77f90862459627c34345145d99048b86d5d658..007669b50f7d1615a26bc92ed4c1ccf9a7a5c870 100644 --- a/openjdkjvmti/events-inl.h +++ b/openjdkjvmti/events-inl.h @@ -46,6 +46,45 @@ static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e) { namespace impl { +// Helper for ensuring that the dispatch environment is sane. Events with JNIEnvs need to stash +// pending exceptions since they can cause new ones to be thrown. In accordance with the JVMTI +// specification we allow exceptions originating from events to overwrite the current exception, +// including exceptions originating from earlier events. +class ScopedEventDispatchEnvironment FINAL : public art::ValueObject { + public: + ScopedEventDispatchEnvironment() : env_(nullptr), throw_(nullptr, nullptr) { + DCHECK_EQ(art::Thread::Current()->GetState(), art::ThreadState::kNative); + } + + explicit ScopedEventDispatchEnvironment(JNIEnv* env) + : env_(env), + throw_(env_, env_->ExceptionOccurred()) { + DCHECK_EQ(art::Thread::Current()->GetState(), art::ThreadState::kNative); + // The spec doesn't say how much local data should be there, so we just give 128 which seems + // likely to be enough for most cases. + env_->PushLocalFrame(128); + env_->ExceptionClear(); + } + + ~ScopedEventDispatchEnvironment() { + if (env_ != nullptr) { + if (throw_.get() != nullptr && !env_->ExceptionCheck()) { + // TODO It would be nice to add the overwritten exceptions to the suppressed exceptions list + // of the newest exception. + env_->Throw(throw_.get()); + } + env_->PopLocalFrame(nullptr); + } + DCHECK_EQ(art::Thread::Current()->GetState(), art::ThreadState::kNative); + } + + private: + JNIEnv* env_; + ScopedLocalRef throw_; + + DISALLOW_COPY_AND_ASSIGN(ScopedEventDispatchEnvironment); +}; + // Infrastructure to achieve type safety for event dispatch. #define FORALL_EVENT_TYPES(fn) \ @@ -80,43 +119,85 @@ namespace impl { fn(GarbageCollectionStart, ArtJvmtiEvent::kGarbageCollectionStart) \ fn(GarbageCollectionFinish, ArtJvmtiEvent::kGarbageCollectionFinish) \ fn(ObjectFree, ArtJvmtiEvent::kObjectFree) \ - fn(VMObjectAlloc, ArtJvmtiEvent::kVmObjectAlloc) + fn(VMObjectAlloc, ArtJvmtiEvent::kVmObjectAlloc) \ + fn(DdmPublishChunk, ArtJvmtiEvent::kDdmPublishChunk) template struct EventFnType { }; -#define EVENT_FN_TYPE(name, enum_name) \ -template <> \ -struct EventFnType { \ - using type = decltype(jvmtiEventCallbacks().name); \ +#define EVENT_FN_TYPE(name, enum_name) \ +template <> \ +struct EventFnType { \ + using type = decltype(ArtJvmtiEventCallbacks().name); \ }; FORALL_EVENT_TYPES(EVENT_FN_TYPE) #undef EVENT_FN_TYPE -template -ALWAYS_INLINE inline typename EventFnType::type GetCallback(ArtJvmTiEnv* env); - -#define GET_CALLBACK(name, enum_name) \ -template <> \ -ALWAYS_INLINE inline EventFnType::type GetCallback( \ - ArtJvmTiEnv* env) { \ - if (env->event_callbacks == nullptr) { \ - return nullptr; \ - } \ - return env->event_callbacks->name; \ -} +#define MAKE_EVENT_HANDLER_FUNC(name, enum_name) \ +template<> \ +struct EventHandlerFunc { \ + using EventFnType = typename impl::EventFnType::type; \ + explicit EventHandlerFunc(ArtJvmTiEnv* env) \ + : env_(env), \ + fn_(env_->event_callbacks == nullptr ? nullptr : env_->event_callbacks->name) { } \ + \ + template \ + ALWAYS_INLINE \ + void ExecuteCallback(JNIEnv* jnienv, Args... args) const { \ + if (fn_ != nullptr) { \ + ScopedEventDispatchEnvironment sede(jnienv); \ + DoExecute(jnienv, args...); \ + } \ + } \ + \ + template \ + ALWAYS_INLINE \ + void ExecuteCallback(Args... args) const { \ + if (fn_ != nullptr) { \ + ScopedEventDispatchEnvironment sede; \ + DoExecute(args...); \ + } \ + } \ + \ + private: \ + template \ + ALWAYS_INLINE \ + inline void DoExecute(Args... args) const { \ + static_assert(std::is_same::value, \ + "Unexpected different type of ExecuteCallback"); \ + fn_(env_, args...); \ + } \ + \ + public: \ + ArtJvmTiEnv* env_; \ + EventFnType fn_; \ +}; -FORALL_EVENT_TYPES(GET_CALLBACK) +FORALL_EVENT_TYPES(MAKE_EVENT_HANDLER_FUNC) -#undef GET_CALLBACK +#undef MAKE_EVENT_HANDLER_FUNC #undef FORALL_EVENT_TYPES } // namespace impl +template +inline std::vector> EventHandler::CollectEvents(art::Thread* thread, + Args... args) const { + art::MutexLock mu(thread, envs_lock_); + std::vector> handlers; + for (ArtJvmTiEnv* env : envs) { + if (ShouldDispatch(env, thread, args...)) { + impl::EventHandlerFunc h(env); + handlers.push_back(h); + } + } + return handlers; +} + // C++ does not allow partial template function specialization. The dispatch for our separated // ClassFileLoadHook event types is the same, so use this helper for code deduplication. template @@ -130,29 +211,37 @@ inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread, const unsigned char* class_data, jint* new_class_data_len, unsigned char** new_class_data) const { + art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); static_assert(kEvent == ArtJvmtiEvent::kClassFileLoadHookRetransformable || kEvent == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable, "Unsupported event"); DCHECK(*new_class_data == nullptr); jint current_len = class_data_len; unsigned char* current_class_data = const_cast(class_data); + std::vector> handlers = + CollectEvents(thread, + jnienv, + class_being_redefined, + loader, + name, + protection_domain, + class_data_len, + class_data, + new_class_data_len, + new_class_data); ArtJvmTiEnv* last_env = nullptr; - for (ArtJvmTiEnv* env : envs) { - if (env == nullptr) { - continue; - } + for (const impl::EventHandlerFunc& event : handlers) { jint new_len = 0; unsigned char* new_data = nullptr; - DispatchEventOnEnv(env, - thread, - jnienv, - class_being_redefined, - loader, - name, - protection_domain, - current_len, - static_cast(current_class_data), - &new_len, - &new_data); + ExecuteCallback(event, + jnienv, + class_being_redefined, + loader, + name, + protection_domain, + current_len, + static_cast(current_class_data), + &new_len, + &new_data); if (new_data != nullptr && new_data != current_class_data) { // Destroy the data the last transformer made. We skip this if the previous state was the // initial one since we don't know here which jvmtiEnv allocated it. @@ -161,7 +250,7 @@ inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread, if (last_env != nullptr) { last_env->Deallocate(current_class_data); } - last_env = env; + last_env = event.env_; current_class_data = new_data; current_len = new_len; } @@ -175,70 +264,28 @@ inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread, // Our goal for DispatchEvent: Do not allow implicit type conversion. Types of ...args must match // exactly the argument types of the corresponding Jvmti kEvent function pointer. -template -inline void EventHandler::ExecuteCallback(ArtJvmTiEnv* env, Args... args) { - using FnType = typename impl::EventFnType::type; - FnType callback = impl::GetCallback(env); - if (callback != nullptr) { - (*callback)(env, args...); - } -} - template inline void EventHandler::DispatchEvent(art::Thread* thread, Args... args) const { + art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); static_assert(!std::is_same>>>::value, "Should be calling DispatchEvent with explicit JNIEnv* argument!"); DCHECK(thread == nullptr || !thread->IsExceptionPending()); - for (ArtJvmTiEnv* env : envs) { - if (env != nullptr) { - DispatchEventOnEnv(env, thread, args...); - } + std::vector> events = CollectEvents(thread, args...); + for (auto event : events) { + ExecuteCallback(event, args...); } } -// Helper for ensuring that the dispatch environment is sane. Events with JNIEnvs need to stash -// pending exceptions since they can cause new ones to be thrown. In accordance with the JVMTI -// specification we allow exceptions originating from events to overwrite the current exception, -// including exceptions originating from earlier events. -class ScopedEventDispatchEnvironment FINAL : public art::ValueObject { - public: - explicit ScopedEventDispatchEnvironment(JNIEnv* env) - : env_(env), - thr_(env_, env_->ExceptionOccurred()), - suspend_(art::Thread::Current(), art::kNative) { - // The spec doesn't say how much local data should be there, so we just give 128 which seems - // likely to be enough for most cases. - env_->PushLocalFrame(128); - env_->ExceptionClear(); - UNUSED(suspend_); - } - - ~ScopedEventDispatchEnvironment() { - if (thr_.get() != nullptr && !env_->ExceptionCheck()) { - // TODO It would be nice to add the overwritten exceptions to the suppressed exceptions list - // of the newest exception. - env_->Throw(thr_.get()); - } - env_->PopLocalFrame(nullptr); - } - - private: - JNIEnv* env_; - ScopedLocalRef thr_; - // Not actually unused. The destructor/constructor does important work. - art::ScopedThreadStateChange suspend_; - - DISALLOW_COPY_AND_ASSIGN(ScopedEventDispatchEnvironment); -}; - template inline void EventHandler::DispatchEvent(art::Thread* thread, JNIEnv* jnienv, Args... args) const { - for (ArtJvmTiEnv* env : envs) { - if (env != nullptr) { - DispatchEventOnEnv(env, thread, jnienv, args...); - } + art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); + std::vector> events = CollectEvents(thread, + jnienv, + args...); + for (auto event : events) { + ExecuteCallback(event, jnienv, args...); } } @@ -247,8 +294,9 @@ inline void EventHandler::DispatchEventOnEnv( ArtJvmTiEnv* env, art::Thread* thread, JNIEnv* jnienv, Args... args) const { DCHECK(env != nullptr); if (ShouldDispatch(env, thread, jnienv, args...)) { - ScopedEventDispatchEnvironment sede(jnienv); - ExecuteCallback(env, jnienv, args...); + art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); + impl::EventHandlerFunc func(env); + ExecuteCallback(func, jnienv, args...); } } @@ -259,11 +307,26 @@ inline void EventHandler::DispatchEventOnEnv( typename std::decay_t< std::tuple_element_t<0, std::tuple>>>::value, "Should be calling DispatchEventOnEnv with explicit JNIEnv* argument!"); - if (ShouldDispatch(env, thread, args...)) { - ExecuteCallback(env, args...); + DCHECK(env != nullptr); + if (ShouldDispatch(env, thread, args...)) { + art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); + impl::EventHandlerFunc func(env); + ExecuteCallback(func, args...); } } +template +inline void EventHandler::ExecuteCallback(impl::EventHandlerFunc handler, Args... args) { + handler.ExecuteCallback(args...); +} + +template +inline void EventHandler::ExecuteCallback(impl::EventHandlerFunc handler, + JNIEnv* jnienv, + Args... args) { + handler.ExecuteCallback(jnienv, args...); +} + // Events that need custom logic for if we send the event but are otherwise normal. This includes // the kBreakpoint, kFramePop, kFieldAccess, and kFieldModification events. @@ -346,14 +409,13 @@ inline bool EventHandler::ShouldDispatch( // something. template <> inline void EventHandler::ExecuteCallback( - ArtJvmTiEnv* env, + impl::EventHandlerFunc event, JNIEnv* jnienv, jthread jni_thread, jmethodID jmethod, jboolean is_exception, const art::ShadowFrame* frame ATTRIBUTE_UNUSED) { - ExecuteCallback( - env, jnienv, jni_thread, jmethod, is_exception); + ExecuteCallback(event, jnienv, jni_thread, jmethod, is_exception); } // Need to give a custom specialization for NativeMethodBind since it has to deal with an out @@ -365,20 +427,25 @@ inline void EventHandler::DispatchEvent(art::T jmethodID method, void* cur_method, void** new_method) const { + art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative); + std::vector> events = + CollectEvents(thread, + jnienv, + jni_thread, + method, + cur_method, + new_method); *new_method = cur_method; - for (ArtJvmTiEnv* env : envs) { - if (env != nullptr) { - *new_method = cur_method; - DispatchEventOnEnv(env, - thread, - jnienv, - jni_thread, - method, - cur_method, - new_method); - if (*new_method != nullptr) { - cur_method = *new_method; - } + for (auto event : events) { + *new_method = cur_method; + ExecuteCallback(event, + jnienv, + jni_thread, + method, + cur_method, + new_method); + if (*new_method != nullptr) { + cur_method = *new_method; } } *new_method = cur_method; @@ -438,7 +505,7 @@ inline void EventHandler::DispatchEvent -inline bool EventHandler::ShouldDispatchOnThread(ArtJvmTiEnv* env, art::Thread* thread) { +inline bool EventHandler::ShouldDispatchOnThread(ArtJvmTiEnv* env, art::Thread* thread) const { bool dispatch = env->event_masks.global_event_mask.Test(kEvent); if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(kEvent)) { @@ -460,6 +527,11 @@ inline bool EventHandler::ShouldDispatch(ArtJvmTiEnv* env, } inline void EventHandler::RecalculateGlobalEventMask(ArtJvmtiEvent event) { + art::MutexLock mu(art::Thread::Current(), envs_lock_); + RecalculateGlobalEventMaskLocked(event); +} + +inline void EventHandler::RecalculateGlobalEventMaskLocked(ArtJvmtiEvent event) { bool union_value = false; for (const ArtJvmTiEnv* stored_env : envs) { if (stored_env == nullptr) { diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc index 6a64441a4a8ab819367aea291b4d5074a8948090..330a3de99c5fde9ca7cb913fdd0be7602922218b 100644 --- a/openjdkjvmti/events.cc +++ b/openjdkjvmti/events.cc @@ -36,7 +36,6 @@ #include "art_field-inl.h" #include "art_jvmti.h" #include "art_method-inl.h" -#include "base/logging.h" #include "deopt_manager.h" #include "dex_file_types.h" #include "gc/allocation_listener.h" @@ -60,6 +59,45 @@ namespace openjdkjvmti { +void ArtJvmtiEventCallbacks::CopyExtensionsFrom(const ArtJvmtiEventCallbacks* cb) { + if (art::kIsDebugBuild) { + ArtJvmtiEventCallbacks clean; + DCHECK_EQ(memcmp(&clean, this, sizeof(clean)), 0) + << "CopyExtensionsFrom called with initialized eventsCallbacks!"; + } + if (cb != nullptr) { + memcpy(this, cb, sizeof(*this)); + } else { + memset(this, 0, sizeof(*this)); + } +} + +jvmtiError ArtJvmtiEventCallbacks::Set(jint index, jvmtiExtensionEvent cb) { + switch (index) { + case static_cast(ArtJvmtiEvent::kDdmPublishChunk): + DdmPublishChunk = reinterpret_cast(cb); + return OK; + default: + return ERR(ILLEGAL_ARGUMENT); + } +} + + +bool IsExtensionEvent(jint e) { + return e >= static_cast(ArtJvmtiEvent::kMinEventTypeVal) && + e <= static_cast(ArtJvmtiEvent::kMaxEventTypeVal) && + IsExtensionEvent(static_cast(e)); +} + +bool IsExtensionEvent(ArtJvmtiEvent e) { + switch (e) { + case ArtJvmtiEvent::kDdmPublishChunk: + return true; + default: + return false; + } +} + bool EventMasks::IsEnabledAnywhere(ArtJvmtiEvent event) { return global_event_mask.Test(event) || unioned_thread_event_mask.Test(event); } @@ -100,7 +138,9 @@ EventMask* EventMasks::GetEventMaskOrNull(art::Thread* thread) { } -void EventMasks::EnableEvent(art::Thread* thread, ArtJvmtiEvent event) { +void EventMasks::EnableEvent(ArtJvmTiEnv* env, art::Thread* thread, ArtJvmtiEvent event) { + DCHECK_EQ(&env->event_masks, this); + env->event_info_mutex_.AssertExclusiveHeld(art::Thread::Current()); DCHECK(EventMask::EventIsInRange(event)); GetEventMask(thread).Set(event); if (thread != nullptr) { @@ -108,7 +148,9 @@ void EventMasks::EnableEvent(art::Thread* thread, ArtJvmtiEvent event) { } } -void EventMasks::DisableEvent(art::Thread* thread, ArtJvmtiEvent event) { +void EventMasks::DisableEvent(ArtJvmTiEnv* env, art::Thread* thread, ArtJvmtiEvent event) { + DCHECK_EQ(&env->event_masks, this); + env->event_info_mutex_.AssertExclusiveHeld(art::Thread::Current()); DCHECK(EventMask::EventIsInRange(event)); GetEventMask(thread).Set(event, false); if (thread != nullptr) { @@ -154,25 +196,21 @@ void EventMasks::HandleChangedCapabilities(const jvmtiCapabilities& caps, bool c } void EventHandler::RegisterArtJvmTiEnv(ArtJvmTiEnv* env) { - // Since we never shrink this array we might as well try to fill gaps. - auto it = std::find(envs.begin(), envs.end(), nullptr); - if (it != envs.end()) { - *it = env; - } else { - envs.push_back(env); - } + art::MutexLock mu(art::Thread::Current(), envs_lock_); + envs.push_back(env); } void EventHandler::RemoveArtJvmTiEnv(ArtJvmTiEnv* env) { + art::MutexLock mu(art::Thread::Current(), envs_lock_); // Since we might be currently iterating over the envs list we cannot actually erase elements. // Instead we will simply replace them with 'nullptr' and skip them manually. auto it = std::find(envs.begin(), envs.end(), env); if (it != envs.end()) { - *it = nullptr; + envs.erase(it); for (size_t i = static_cast(ArtJvmtiEvent::kMinEventTypeVal); i <= static_cast(ArtJvmtiEvent::kMaxEventTypeVal); ++i) { - RecalculateGlobalEventMask(static_cast(i)); + RecalculateGlobalEventMaskLocked(static_cast(i)); } } } @@ -213,6 +251,38 @@ static void RunEventCallback(EventHandler* handler, args...); } +static void SetupDdmTracking(art::DdmCallback* listener, bool enable) { + art::ScopedObjectAccess soa(art::Thread::Current()); + if (enable) { + art::Runtime::Current()->GetRuntimeCallbacks()->AddDdmCallback(listener); + } else { + art::Runtime::Current()->GetRuntimeCallbacks()->RemoveDdmCallback(listener); + } +} + +class JvmtiDdmChunkListener : public art::DdmCallback { + public: + explicit JvmtiDdmChunkListener(EventHandler* handler) : handler_(handler) {} + + void DdmPublishChunk(uint32_t type, const art::ArrayRef& data) + OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { + if (handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kDdmPublishChunk)) { + art::Thread* self = art::Thread::Current(); + handler_->DispatchEvent( + self, + static_cast(self->GetJniEnv()), + static_cast(type), + static_cast(data.size()), + reinterpret_cast(data.data())); + } + } + + private: + EventHandler* handler_; + + DISALLOW_COPY_AND_ASSIGN(JvmtiDdmChunkListener); +}; + class JvmtiAllocationListener : public art::gc::AllocationListener { public: explicit JvmtiAllocationListener(EventHandler* handler) : handler_(handler) {} @@ -360,11 +430,11 @@ class JvmtiGcPauseListener : public art::gc::GcPauseListener { finish_enabled_(false) {} void StartPause() OVERRIDE { - handler_->DispatchEvent(nullptr); + handler_->DispatchEvent(art::Thread::Current()); } void EndPause() OVERRIDE { - handler_->DispatchEvent(nullptr); + handler_->DispatchEvent(art::Thread::Current()); } bool IsEnabled() { @@ -832,9 +902,9 @@ static bool EventNeedsFullDeopt(ArtJvmtiEvent event) { } } -static void SetupTraceListener(JvmtiMethodTraceListener* listener, - ArtJvmtiEvent event, - bool enable) { +void EventHandler::SetupTraceListener(JvmtiMethodTraceListener* listener, + ArtJvmtiEvent event, + bool enable) { bool needs_full_deopt = EventNeedsFullDeopt(event); // Make sure we can deopt. { @@ -854,8 +924,21 @@ static void SetupTraceListener(JvmtiMethodTraceListener* listener, } // Add the actual listeners. - art::ScopedThreadStateChange stsc(art::Thread::Current(), art::ThreadState::kNative); uint32_t new_events = GetInstrumentationEventsFor(event); + if (new_events == art::instrumentation::Instrumentation::kDexPcMoved) { + // Need to skip adding the listeners if the event is breakpoint/single-step since those events + // share the same art-instrumentation underlying event. We need to give them their own deopt + // request though so the test waits until here. + DCHECK(event == ArtJvmtiEvent::kBreakpoint || event == ArtJvmtiEvent::kSingleStep); + ArtJvmtiEvent other = event == ArtJvmtiEvent::kBreakpoint ? ArtJvmtiEvent::kSingleStep + : ArtJvmtiEvent::kBreakpoint; + if (IsEventEnabledAnywhere(other)) { + // The event needs to be kept around/is already enabled by the other jvmti event that uses the + // same instrumentation event. + return; + } + } + art::ScopedThreadStateChange stsc(art::Thread::Current(), art::ThreadState::kNative); art::instrumentation::Instrumentation* instr = art::Runtime::Current()->GetInstrumentation(); art::gc::ScopedGCCriticalSection gcs(art::Thread::Current(), art::gc::kGcCauseInstrumentation, @@ -924,6 +1007,9 @@ bool EventHandler::OtherMonitorEventsEnabledAnywhere(ArtJvmtiEvent event) { // Handle special work for the given event type, if necessary. void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) { switch (event) { + case ArtJvmtiEvent::kDdmPublishChunk: + SetupDdmTracking(ddm_listener_.get(), enable); + return; case ArtJvmtiEvent::kVmObjectAlloc: SetupObjectAllocationTracking(alloc_listener_.get(), enable); return; @@ -932,18 +1018,6 @@ void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) { case ArtJvmtiEvent::kGarbageCollectionFinish: SetupGcPauseTracking(gc_pause_listener_.get(), event, enable); return; - - case ArtJvmtiEvent::kBreakpoint: - case ArtJvmtiEvent::kSingleStep: { - ArtJvmtiEvent other = (event == ArtJvmtiEvent::kBreakpoint) ? ArtJvmtiEvent::kSingleStep - : ArtJvmtiEvent::kBreakpoint; - // We only need to do anything if there isn't already a listener installed/held-on by the - // other jvmti event that uses DexPcMoved. - if (!IsEventEnabledAnywhere(other)) { - SetupTraceListener(method_trace_listener_.get(), event, enable); - } - return; - } // FramePop can never be disabled once it's been turned on since we would either need to deal // with dangling pointers or have missed events. // TODO We really need to make this not the case anymore. @@ -960,6 +1034,8 @@ void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) { case ArtJvmtiEvent::kFieldModification: case ArtJvmtiEvent::kException: case ArtJvmtiEvent::kExceptionCatch: + case ArtJvmtiEvent::kBreakpoint: + case ArtJvmtiEvent::kSingleStep: SetupTraceListener(method_trace_listener_.get(), event, enable); return; case ArtJvmtiEvent::kMonitorContendedEnter: @@ -1061,20 +1137,28 @@ jvmtiError EventHandler::SetEvent(ArtJvmTiEnv* env, return ERR(MUST_POSSESS_CAPABILITY); } - bool old_state = global_mask.Test(event); + bool old_state; + bool new_state; - if (mode == JVMTI_ENABLE) { - env->event_masks.EnableEvent(thread, event); - global_mask.Set(event); - } else { - DCHECK_EQ(mode, JVMTI_DISABLE); + { + // Change the event masks atomically. + art::Thread* self = art::Thread::Current(); + art::MutexLock mu(self, envs_lock_); + art::WriterMutexLock mu_env_info(self, env->event_info_mutex_); + old_state = global_mask.Test(event); + if (mode == JVMTI_ENABLE) { + env->event_masks.EnableEvent(env, thread, event); + global_mask.Set(event); + new_state = true; + } else { + DCHECK_EQ(mode, JVMTI_DISABLE); - env->event_masks.DisableEvent(thread, event); - RecalculateGlobalEventMask(event); + env->event_masks.DisableEvent(env, thread, event); + RecalculateGlobalEventMaskLocked(event); + new_state = global_mask.Test(event); + } } - bool new_state = global_mask.Test(event); - // Handle any special work required for the event type. if (new_state != old_state) { HandleEventType(event, mode == JVMTI_ENABLE); @@ -1102,8 +1186,10 @@ void EventHandler::Shutdown() { art::Runtime::Current()->GetInstrumentation()->RemoveListener(method_trace_listener_.get(), ~0); } -EventHandler::EventHandler() { +EventHandler::EventHandler() : envs_lock_("JVMTI Environment List Lock", + art::LockLevel::kTopLockLevel) { alloc_listener_.reset(new JvmtiAllocationListener(this)); + ddm_listener_.reset(new JvmtiDdmChunkListener(this)); gc_pause_listener_.reset(new JvmtiGcPauseListener(this)); method_trace_listener_.reset(new JvmtiMethodTraceListener(this)); monitor_listener_.reset(new JvmtiMonitorListener(this)); diff --git a/openjdkjvmti/events.h b/openjdkjvmti/events.h index aed24e59f3708e9bf3403fde10664f85176dae63..81edb931cdd8e1fa4eed2de95ad411ea14782aa3 100644 --- a/openjdkjvmti/events.h +++ b/openjdkjvmti/events.h @@ -20,7 +20,10 @@ #include #include -#include "base/logging.h" +#include + +#include "base/macros.h" +#include "base/mutex.h" #include "jvmti.h" #include "thread.h" @@ -28,13 +31,14 @@ namespace openjdkjvmti { struct ArtJvmTiEnv; class JvmtiAllocationListener; +class JvmtiDdmChunkListener; class JvmtiGcPauseListener; class JvmtiMethodTraceListener; class JvmtiMonitorListener; // an enum for ArtEvents. This differs from the JVMTI events only in that we distinguish between // retransformation capable and incapable loading -enum class ArtJvmtiEvent { +enum class ArtJvmtiEvent : jint { kMinEventTypeVal = JVMTI_MIN_EVENT_TYPE_VAL, kVmInit = JVMTI_EVENT_VM_INIT, kVmDeath = JVMTI_EVENT_VM_DEATH, @@ -68,9 +72,33 @@ enum class ArtJvmtiEvent { kObjectFree = JVMTI_EVENT_OBJECT_FREE, kVmObjectAlloc = JVMTI_EVENT_VM_OBJECT_ALLOC, kClassFileLoadHookRetransformable = JVMTI_MAX_EVENT_TYPE_VAL + 1, - kMaxEventTypeVal = kClassFileLoadHookRetransformable, + kDdmPublishChunk = JVMTI_MAX_EVENT_TYPE_VAL + 2, + kMaxEventTypeVal = kDdmPublishChunk, +}; + +using ArtJvmtiEventDdmPublishChunk = void (*)(jvmtiEnv *jvmti_env, + JNIEnv* jni_env, + jint data_type, + jint data_len, + const jbyte* data); + +struct ArtJvmtiEventCallbacks : jvmtiEventCallbacks { + ArtJvmtiEventCallbacks() : DdmPublishChunk(nullptr) { + memset(this, 0, sizeof(jvmtiEventCallbacks)); + } + + // Copies extension functions from other callback struct if it exists. There must not have been + // any modifications to this struct when it is called. + void CopyExtensionsFrom(const ArtJvmtiEventCallbacks* cb); + + jvmtiError Set(jint index, jvmtiExtensionEvent cb); + + ArtJvmtiEventDdmPublishChunk DdmPublishChunk; }; +bool IsExtensionEvent(jint e); +bool IsExtensionEvent(ArtJvmtiEvent e); + // Convert a jvmtiEvent into a ArtJvmtiEvent ALWAYS_INLINE static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e); @@ -124,8 +152,16 @@ struct EventMasks { EventMask& GetEventMask(art::Thread* thread); EventMask* GetEventMaskOrNull(art::Thread* thread); - void EnableEvent(art::Thread* thread, ArtJvmtiEvent event); - void DisableEvent(art::Thread* thread, ArtJvmtiEvent event); + // Circular dependencies mean we cannot see the definition of ArtJvmTiEnv so the mutex is simply + // asserted in the function. + // Note that the 'env' passed in must be the same env this EventMasks is associated with. + void EnableEvent(ArtJvmTiEnv* env, art::Thread* thread, ArtJvmtiEvent event); + // REQUIRES(env->event_info_mutex_); + // Circular dependencies mean we cannot see the definition of ArtJvmTiEnv so the mutex is simply + // asserted in the function. + // Note that the 'env' passed in must be the same env this EventMasks is associated with. + void DisableEvent(ArtJvmTiEnv* env, art::Thread* thread, ArtJvmtiEvent event); + // REQUIRES(env->event_info_mutex_); bool IsEnabledAnywhere(ArtJvmtiEvent event); // Make any changes to event masks needed for the given capability changes. If caps_added is true // then caps is all the newly set capabilities of the jvmtiEnv. If it is false then caps is the @@ -133,6 +169,10 @@ struct EventMasks { void HandleChangedCapabilities(const jvmtiCapabilities& caps, bool caps_added); }; +namespace impl { +template struct EventHandlerFunc { }; +} // namespace impl + // Helper class for event handling. class EventHandler { public: @@ -144,10 +184,10 @@ class EventHandler { // Register an env. It is assumed that this happens on env creation, that is, no events are // enabled, yet. - void RegisterArtJvmTiEnv(ArtJvmTiEnv* env); + void RegisterArtJvmTiEnv(ArtJvmTiEnv* env) REQUIRES(!envs_lock_); // Remove an env. - void RemoveArtJvmTiEnv(ArtJvmTiEnv* env); + void RemoveArtJvmTiEnv(ArtJvmTiEnv* env) REQUIRES(!envs_lock_); bool IsEventEnabledAnywhere(ArtJvmtiEvent event) const { if (!EventMask::EventIsInRange(event)) { @@ -159,13 +199,15 @@ class EventHandler { jvmtiError SetEvent(ArtJvmTiEnv* env, art::Thread* thread, ArtJvmtiEvent event, - jvmtiEventMode mode); + jvmtiEventMode mode) + REQUIRES(!envs_lock_); // Dispatch event to all registered environments. Since this one doesn't have a JNIEnv* it doesn't // matter if it has the mutator_lock. template ALWAYS_INLINE - inline void DispatchEvent(art::Thread* thread, Args... args) const; + inline void DispatchEvent(art::Thread* thread, Args... args) const + REQUIRES(!envs_lock_); // Dispatch event to all registered environments stashing exceptions as needed. This works since // JNIEnv* is always the second argument if it is passed to an event. Needed since C++ does not @@ -175,7 +217,8 @@ class EventHandler { // the event to allocate local references. template ALWAYS_INLINE - inline void DispatchEvent(art::Thread* thread, JNIEnv* jnienv, Args... args) const; + inline void DispatchEvent(art::Thread* thread, JNIEnv* jnienv, Args... args) const + REQUIRES(!envs_lock_); // Tell the event handler capabilities were added/lost so it can adjust the sent events.If // caps_added is true then caps is all the newly set capabilities of the jvmtiEnv. If it is false @@ -183,28 +226,50 @@ class EventHandler { ALWAYS_INLINE inline void HandleChangedCapabilities(ArtJvmTiEnv* env, const jvmtiCapabilities& caps, - bool added); + bool added) + REQUIRES(!envs_lock_); // Dispatch event to the given environment, only. template ALWAYS_INLINE - inline void DispatchEventOnEnv( - ArtJvmTiEnv* env, art::Thread* thread, JNIEnv* jnienv, Args... args) const; + inline void DispatchEventOnEnv(ArtJvmTiEnv* env, + art::Thread* thread, + JNIEnv* jnienv, + Args... args) const + REQUIRES(!envs_lock_); // Dispatch event to the given environment, only. template ALWAYS_INLINE - inline void DispatchEventOnEnv(ArtJvmTiEnv* env, art::Thread* thread, Args... args) const; + inline void DispatchEventOnEnv(ArtJvmTiEnv* env, art::Thread* thread, Args... args) const + REQUIRES(!envs_lock_); private: + void SetupTraceListener(JvmtiMethodTraceListener* listener, ArtJvmtiEvent event, bool enable); + + template + ALWAYS_INLINE + inline std::vector> CollectEvents(art::Thread* thread, + Args... args) const + REQUIRES(!envs_lock_); + template ALWAYS_INLINE - static inline bool ShouldDispatchOnThread(ArtJvmTiEnv* env, art::Thread* thread); + inline bool ShouldDispatchOnThread(ArtJvmTiEnv* env, art::Thread* thread) const; template ALWAYS_INLINE - static inline void ExecuteCallback(ArtJvmTiEnv* env, Args... args); + static inline void ExecuteCallback(impl::EventHandlerFunc handler, + JNIEnv* env, + Args... args) + REQUIRES(!envs_lock_); + template + ALWAYS_INLINE + static inline void ExecuteCallback(impl::EventHandlerFunc handler, Args... args) + REQUIRES(!envs_lock_); + + // Public for use to collect dispatches template ALWAYS_INLINE inline bool ShouldDispatch(ArtJvmTiEnv* env, art::Thread* thread, Args... args) const; @@ -216,7 +281,9 @@ class EventHandler { // Recalculates the event mask for the given event. ALWAYS_INLINE - inline void RecalculateGlobalEventMask(ArtJvmtiEvent event); + inline void RecalculateGlobalEventMask(ArtJvmtiEvent event) REQUIRES(!envs_lock_); + ALWAYS_INLINE + inline void RecalculateGlobalEventMaskLocked(ArtJvmtiEvent event) REQUIRES(envs_lock_); template ALWAYS_INLINE inline void DispatchClassFileLoadHookEvent(art::Thread* thread, @@ -228,7 +295,8 @@ class EventHandler { jint class_data_len, const unsigned char* class_data, jint* new_class_data_len, - unsigned char** new_class_data) const; + unsigned char** new_class_data) const + REQUIRES(!envs_lock_); void HandleEventType(ArtJvmtiEvent event, bool enable); void HandleLocalAccessCapabilityAdded(); @@ -236,15 +304,19 @@ class EventHandler { bool OtherMonitorEventsEnabledAnywhere(ArtJvmtiEvent event); - // List of all JvmTiEnv objects that have been created, in their creation order. - // NB Some elements might be null representing envs that have been deleted. They should be skipped - // anytime this list is used. - std::vector envs; + // List of all JvmTiEnv objects that have been created, in their creation order. It is a std::list + // since we mostly access it by iterating over the entire thing, only ever append to the end, and + // need to be able to remove arbitrary elements from it. + std::list envs GUARDED_BY(envs_lock_); + + // Top level lock. Nothing at all should be held when we lock this. + mutable art::Mutex envs_lock_ ACQUIRED_BEFORE(art::Locks::instrument_entrypoints_lock_); // A union of all enabled events, anywhere. EventMask global_mask; std::unique_ptr alloc_listener_; + std::unique_ptr ddm_listener_; std::unique_ptr gc_pause_listener_; std::unique_ptr method_trace_listener_; std::unique_ptr monitor_listener_; diff --git a/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc index c4988695f1d253445ab048ce55424ad4b53b43c5..64dc3a5f02bcdc7e3b1741edd83e5298e90f2e87 100644 --- a/openjdkjvmti/fixed_up_dex_file.cc +++ b/openjdkjvmti/fixed_up_dex_file.cc @@ -60,7 +60,8 @@ static void DoDexUnquicken(const art::DexFile& new_dex_file, const art::DexFile& if (vdex == nullptr) { return; } - vdex->FullyUnquickenDexFile(new_dex_file, original_dex_file); + art::VdexFile::UnquickenDexFile( + new_dex_file, vdex->GetQuickeningInfo(), /* decompile_return_instruction */true); } std::unique_ptr FixedUpDexFile::Create(const art::DexFile& original) { diff --git a/openjdkjvmti/include/CPPLINT.cfg b/openjdkjvmti/include/CPPLINT.cfg new file mode 100644 index 0000000000000000000000000000000000000000..3e717a697fa0b8b35cffba4f5cf2b158c5cad3d7 --- /dev/null +++ b/openjdkjvmti/include/CPPLINT.cfg @@ -0,0 +1,18 @@ +# +# 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. +# + +# External headers, not subject to our lint rules. +exclude_files=^jvmti[.]h$ diff --git a/openjdkjvmti/jvmti_allocator.h b/openjdkjvmti/jvmti_allocator.h index e6cbc8517043665536d3caff1afc6ae23675d48e..bd4c85bd7c2df7e73915aecd375ba75bceed9bc5 100644 --- a/openjdkjvmti/jvmti_allocator.h +++ b/openjdkjvmti/jvmti_allocator.h @@ -32,8 +32,9 @@ #ifndef ART_OPENJDKJVMTI_JVMTI_ALLOCATOR_H_ #define ART_OPENJDKJVMTI_JVMTI_ALLOCATOR_H_ -#include "base/logging.h" -#include "base/macros.h" +#include +#include + #include "jvmti.h" #include "ti_allocator.h" @@ -58,7 +59,7 @@ class JvmtiAllocator { JvmtiAllocator() : env_(nullptr) {} template - JvmtiAllocator(const JvmtiAllocator& other) // NOLINT, implicit + JvmtiAllocator(const JvmtiAllocator& other) : env_(other.env_) {} JvmtiAllocator(const JvmtiAllocator& other) = default; @@ -95,7 +96,7 @@ class JvmtiAllocator { JvmtiAllocator() : env_(nullptr) {} template - JvmtiAllocator(const JvmtiAllocator& other) // NOLINT, implicit + JvmtiAllocator(const JvmtiAllocator& other) : env_(other.env_) {} JvmtiAllocator(const JvmtiAllocator& other) = default; diff --git a/openjdkjvmti/jvmti_weak_table-inl.h b/openjdkjvmti/jvmti_weak_table-inl.h index 5d20946070ebb071c2e3d86feaee4d47173f5545..699004298e5dbfc7da813951f2a5ab0b266a580c 100644 --- a/openjdkjvmti/jvmti_weak_table-inl.h +++ b/openjdkjvmti/jvmti_weak_table-inl.h @@ -36,8 +36,9 @@ #include +#include + #include "art_jvmti.h" -#include "base/logging.h" #include "gc/allocation_listener.h" #include "instrumentation.h" #include "jni_env_ext-inl.h" diff --git a/openjdkjvmti/object_tagging.cc b/openjdkjvmti/object_tagging.cc index 6ba71655773cc0a73249c337d4f5f06aaf0e0d01..ba242ef1e824c82839f988039d316fbcc980d1ae 100644 --- a/openjdkjvmti/object_tagging.cc +++ b/openjdkjvmti/object_tagging.cc @@ -61,7 +61,8 @@ bool ObjectTagTable::DoesHandleNullOnSweep() { return event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kObjectFree); } void ObjectTagTable::HandleNullSweep(jlong tag) { - event_handler_->DispatchEventOnEnv(jvmti_env_, nullptr, tag); + event_handler_->DispatchEventOnEnv( + jvmti_env_, art::Thread::Current(), tag); } } // namespace openjdkjvmti diff --git a/openjdkjvmti/ti_breakpoint.cc b/openjdkjvmti/ti_breakpoint.cc index 8e5b56e9bf637c35ac89bbf67a52a55e3c16448a..7634fa312f0fa12d38754df4c3d36696d11b299a 100644 --- a/openjdkjvmti/ti_breakpoint.cc +++ b/openjdkjvmti/ti_breakpoint.cc @@ -98,7 +98,7 @@ jvmtiError BreakpointUtil::SetBreakpoint(jvmtiEnv* jenv, jmethodID method, jloca art::ScopedObjectAccess soa(art::Thread::Current()); art::ArtMethod* art_method = art::jni::DecodeArtMethod(method)->GetCanonicalMethod(); if (location < 0 || static_cast(location) >= - art_method->GetCodeItem()->insns_size_in_code_units_) { + art_method->DexInstructions().InsnsSizeInCodeUnits()) { return ERR(INVALID_LOCATION); } DeoptManager::Get()->AddMethodBreakpoint(art_method); diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc index 2c5e5f9ccf1fd4710368e092405ad626e14439d6..60ab0a50e6379f084024500e9bd7612160d79ad1 100644 --- a/openjdkjvmti/ti_class.cc +++ b/openjdkjvmti/ti_class.cc @@ -33,7 +33,7 @@ #include "android-base/stringprintf.h" -#include // NOLINT [build/c++11] [5] +#include #include #include "art_jvmti.h" @@ -490,7 +490,7 @@ struct ClassCallback : public art::ClassLoadCallback { // Fix up the local table with a root visitor. RootUpdater local_update(local->input_, local->output_); - t->GetJniEnv()->locals.VisitRoots( + t->GetJniEnv()->VisitJniLocalRoots( &local_update, art::RootInfo(art::kRootJNILocal, t->GetThreadId())); } diff --git a/openjdkjvmti/ti_class_loader.cc b/openjdkjvmti/ti_class_loader.cc index b551b55e185279c4289f3fa928881c3440a2adb3..701ba80fd55097ca403833907ff488cd783f81df 100644 --- a/openjdkjvmti/ti_class_loader.cc +++ b/openjdkjvmti/ti_class_loader.cc @@ -33,11 +33,11 @@ #include -#include "android-base/stringprintf.h" +#include +#include #include "art_field-inl.h" #include "art_jvmti.h" -#include "base/logging.h" #include "dex_file.h" #include "dex_file_types.h" #include "events-inl.h" diff --git a/openjdkjvmti/ti_ddms.cc b/openjdkjvmti/ti_ddms.cc new file mode 100644 index 0000000000000000000000000000000000000000..0b4906d798f4395d6b80b31c419c0d551738e60e --- /dev/null +++ b/openjdkjvmti/ti_ddms.cc @@ -0,0 +1,89 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include + +#include "ti_ddms.h" + +#include "art_jvmti.h" +#include "base/array_ref.h" +#include "debugger.h" +#include "scoped_thread_state_change-inl.h" +#include "thread-inl.h" + +namespace openjdkjvmti { + +jvmtiError DDMSUtil::HandleChunk(jvmtiEnv* env, + jint type_in, + jint length_in, + const jbyte* data_in, + /*out*/jint* type_out, + /*out*/jint* data_length_out, + /*out*/jbyte** data_out) { + if (env == nullptr || type_out == nullptr || data_out == nullptr || data_length_out == nullptr) { + return ERR(NULL_POINTER); + } else if (data_in == nullptr && length_in != 0) { + // Data-in shouldn't be null if we have data. + return ERR(ILLEGAL_ARGUMENT); + } + + *data_length_out = 0; + *data_out = nullptr; + + art::Thread* self = art::Thread::Current(); + art::ScopedThreadStateChange(self, art::ThreadState::kNative); + + art::ArrayRef data_arr(data_in, length_in); + std::vector out_data; + if (!art::Dbg::DdmHandleChunk(self->GetJniEnv(), + type_in, + data_arr, + /*out*/reinterpret_cast(type_out), + /*out*/&out_data)) { + LOG(WARNING) << "Something went wrong with handling the ddm chunk."; + return ERR(INTERNAL); + } else { + jvmtiError error = OK; + if (!out_data.empty()) { + JvmtiUniquePtr ret = AllocJvmtiUniquePtr(env, out_data.size(), &error); + if (error != OK) { + return error; + } + memcpy(ret.get(), out_data.data(), out_data.size()); + *data_out = ret.release(); + *data_length_out = static_cast(out_data.size()); + } + return OK; + } +} + +} // namespace openjdkjvmti diff --git a/openjdkjvmti/ti_ddms.h b/openjdkjvmti/ti_ddms.h new file mode 100644 index 0000000000000000000000000000000000000000..1ea7548607ed3d1b43b1eb3c90c4391403015c8e --- /dev/null +++ b/openjdkjvmti/ti_ddms.h @@ -0,0 +1,53 @@ +/* Copyright (C) 2017 The Android Open Source Project + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This file implements interfaces from the file jvmti.h. This implementation + * is licensed under the same terms as the file jvmti.h. The + * copyright and license information for the file jvmti.h follows. + * + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef ART_OPENJDKJVMTI_TI_DDMS_H_ +#define ART_OPENJDKJVMTI_TI_DDMS_H_ + +#include "jni.h" +#include "jvmti.h" + +namespace openjdkjvmti { + +class DDMSUtil { + public: + static jvmtiError HandleChunk(jvmtiEnv* env, + jint type_in, + jint length_in, + const jbyte* data_in, + /*out*/ jint* type_out, + /*out*/ jint* data_length_out, + /*out*/ jbyte** data_out); +}; + +} // namespace openjdkjvmti + +#endif // ART_OPENJDKJVMTI_TI_DDMS_H_ diff --git a/openjdkjvmti/ti_dump.cc b/openjdkjvmti/ti_dump.cc index 809a5e47bbecd4437a123848a2709493325cb5ca..253580e0e1e56bd3114cf12c911d4958733839b0 100644 --- a/openjdkjvmti/ti_dump.cc +++ b/openjdkjvmti/ti_dump.cc @@ -47,7 +47,7 @@ struct DumpCallback : public art::RuntimeSigQuitCallback { void SigQuit() OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { art::Thread* thread = art::Thread::Current(); art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative); - event_handler->DispatchEvent(nullptr); + event_handler->DispatchEvent(art::Thread::Current()); } EventHandler* event_handler = nullptr; diff --git a/openjdkjvmti/ti_extension.cc b/openjdkjvmti/ti_extension.cc index fbed9640a006b6f598612140619f80500e0d0f3d..79a8cd630421af65ef976e9d7d5925b0d9977223 100644 --- a/openjdkjvmti/ti_extension.cc +++ b/openjdkjvmti/ti_extension.cc @@ -34,8 +34,11 @@ #include "ti_extension.h" #include "art_jvmti.h" +#include "events.h" #include "ti_allocator.h" +#include "ti_ddms.h" #include "ti_heap.h" +#include "thread-inl.h" namespace openjdkjvmti { @@ -51,7 +54,7 @@ struct CParamInfo { JvmtiUniquePtr param_name = CopyString(env, name, err); char* name_ptr = param_name.get(); char_buffers->push_back(std::move(param_name)); - return jvmtiParamInfo { name_ptr, kind, base_type, null_ok }; // NOLINT [whitespace/braces] [4] + return jvmtiParamInfo{ name_ptr, kind, base_type, null_ok }; } }; @@ -143,7 +146,7 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, "com.android.art.heap.get_object_heap_id", "Retrieve the heap id of the the object tagged with the given argument. An " "arbitrary object is chosen if multiple objects exist with the same tag.", - { // NOLINT [whitespace/braces] [4] + { { "tag", JVMTI_KIND_IN, JVMTI_TYPE_JLONG, false}, { "heap_id", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false} }, @@ -156,7 +159,7 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, reinterpret_cast(HeapExtensions::GetHeapName), "com.android.art.heap.get_heap_name", "Retrieve the name of the heap with the given id.", - { // NOLINT [whitespace/braces] [4] + { { "heap_id", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false}, { "heap_name", JVMTI_KIND_ALLOC_BUF, JVMTI_TYPE_CCHAR, false} }, @@ -172,13 +175,13 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, " except for additionally passing the heap id of the current object. The jvmtiHeapCallbacks" " structure is reused, with the callbacks field overloaded to a signature of " "jint (*)(jlong, jlong, jlong*, jint length, void*, jint).", - { // NOLINT [whitespace/braces] [4] + { { "heap_filter", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false}, { "klass", JVMTI_KIND_IN, JVMTI_TYPE_JCLASS, true}, { "callbacks", JVMTI_KIND_IN_PTR, JVMTI_TYPE_CVOID, false}, { "user_data", JVMTI_KIND_IN_PTR, JVMTI_TYPE_CVOID, true} }, - { // NOLINT [whitespace/braces] [4] + { ERR(MUST_POSSESS_CAPABILITY), ERR(INVALID_CLASS), ERR(NULL_POINTER), @@ -194,7 +197,7 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, " 'Allocate' jvmti function. This does not include any memory that has been deallocated" " through the 'Deallocate' function. This number is approximate and might not correspond" " exactly to the sum of the sizes of all not freed allocations.", - { // NOLINT [whitespace/braces] [4] + { { "currently_allocated", JVMTI_KIND_OUT, JVMTI_TYPE_JLONG, false}, }, { ERR(NULL_POINTER) }); @@ -202,6 +205,27 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, return error; } + // DDMS extension + error = add_extension( + reinterpret_cast(DDMSUtil::HandleChunk), + "com.android.art.internal.ddm.process_chunk", + "Handles a single ddms chunk request and returns a response. The reply data is in the ddms" + " chunk format. It returns the processed chunk. This is provided for backwards compatibility" + " reasons only. Agents should avoid making use of this extension when possible and instead" + " use the other JVMTI entrypoints explicitly.", + { + { "type_in", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false }, + { "length_in", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false }, + { "data_in", JVMTI_KIND_IN_BUF, JVMTI_TYPE_JBYTE, true }, + { "type_out", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false }, + { "data_len_out", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false }, + { "data_out", JVMTI_KIND_ALLOC_BUF, JVMTI_TYPE_JBYTE, false } + }, + { ERR(NULL_POINTER), ERR(ILLEGAL_ARGUMENT), ERR(OUT_OF_MEMORY) }); + if (error != ERR(NONE)) { + return error; + } + // Copy into output buffer. *extension_count_ptr = ext_vector.size(); @@ -230,20 +254,133 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, } -jvmtiError ExtensionUtil::GetExtensionEvents(jvmtiEnv* env ATTRIBUTE_UNUSED, +jvmtiError ExtensionUtil::GetExtensionEvents(jvmtiEnv* env, jint* extension_count_ptr, jvmtiExtensionEventInfo** extensions) { - // We don't have any extension events at the moment. - *extension_count_ptr = 0; - *extensions = nullptr; + std::vector ext_vector; + + // Holders for allocated values. + std::vector> char_buffers; + std::vector> param_buffers; + + auto add_extension = [&](ArtJvmtiEvent extension_event_index, + const char* id, + const char* short_description, + const std::vector& params) { + DCHECK(IsExtensionEvent(extension_event_index)); + jvmtiExtensionEventInfo event_info; + jvmtiError error; + + event_info.extension_event_index = static_cast(extension_event_index); + + JvmtiUniquePtr id_ptr = CopyString(env, id, &error); + if (id_ptr == nullptr) { + return error; + } + event_info.id = id_ptr.get(); + char_buffers.push_back(std::move(id_ptr)); + + JvmtiUniquePtr descr = CopyString(env, short_description, &error); + if (descr == nullptr) { + return error; + } + event_info.short_description = descr.get(); + char_buffers.push_back(std::move(descr)); + + event_info.param_count = params.size(); + if (!params.empty()) { + JvmtiUniquePtr params_ptr = + AllocJvmtiUniquePtr(env, params.size(), &error); + if (params_ptr == nullptr) { + return error; + } + event_info.params = params_ptr.get(); + param_buffers.push_back(std::move(params_ptr)); + + for (jint i = 0; i != event_info.param_count; ++i) { + event_info.params[i] = params[i].ToParamInfo(env, &char_buffers, &error); + if (error != OK) { + return error; + } + } + } else { + event_info.params = nullptr; + } + + ext_vector.push_back(event_info); + + return ERR(NONE); + }; + + jvmtiError error; + error = add_extension( + ArtJvmtiEvent::kDdmPublishChunk, + "com.android.art.internal.ddm.publish_chunk", + "Called when there is new ddms information that the agent or other clients can use. The" + " agent is given the 'type' of the ddms chunk and a 'data_size' byte-buffer in 'data'." + " The 'data' pointer is only valid for the duration of the publish_chunk event. The agent" + " is responsible for interpreting the information present in the 'data' buffer. This is" + " provided for backwards-compatibility support only. Agents should prefer to use relevant" + " JVMTI events and functions above listening for this event.", + { + { "jni_env", JVMTI_KIND_IN_PTR, JVMTI_TYPE_JNIENV, false }, + { "type", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false }, + { "data_size", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false }, + { "data", JVMTI_KIND_IN_BUF, JVMTI_TYPE_JBYTE, false }, + }); + if (error != OK) { + return error; + } + + // Copy into output buffer. + + *extension_count_ptr = ext_vector.size(); + JvmtiUniquePtr out_data = + AllocJvmtiUniquePtr(env, ext_vector.size(), &error); + if (out_data == nullptr) { + return error; + } + memcpy(out_data.get(), + ext_vector.data(), + ext_vector.size() * sizeof(jvmtiExtensionEventInfo)); + *extensions = out_data.release(); + + // Release all the buffer holders, we're OK now. + for (auto& holder : char_buffers) { + holder.release(); + } + for (auto& holder : param_buffers) { + holder.release(); + } + return OK; } -jvmtiError ExtensionUtil::SetExtensionEventCallback(jvmtiEnv* env ATTRIBUTE_UNUSED, - jint extension_event_index ATTRIBUTE_UNUSED, - jvmtiExtensionEvent callback ATTRIBUTE_UNUSED) { - // We do not have any extension events, so any call is illegal. - return ERR(ILLEGAL_ARGUMENT); +jvmtiError ExtensionUtil::SetExtensionEventCallback(jvmtiEnv* env, + jint extension_event_index, + jvmtiExtensionEvent callback, + EventHandler* event_handler) { + if (!IsExtensionEvent(extension_event_index)) { + return ERR(ILLEGAL_ARGUMENT); + } + ArtJvmTiEnv* art_env = ArtJvmTiEnv::AsArtJvmTiEnv(env); + jvmtiEventMode mode = callback == nullptr ? JVMTI_DISABLE : JVMTI_ENABLE; + // Lock the event_info_mutex_ while we set the event to make sure it isn't lost by a concurrent + // change to the normal callbacks. + { + art::WriterMutexLock lk(art::Thread::Current(), art_env->event_info_mutex_); + if (art_env->event_callbacks.get() == nullptr) { + art_env->event_callbacks.reset(new ArtJvmtiEventCallbacks()); + } + jvmtiError err = art_env->event_callbacks->Set(extension_event_index, callback); + if (err != OK) { + return err; + } + } + return event_handler->SetEvent(art_env, + /*event_thread*/nullptr, + static_cast(extension_event_index), + mode); } } // namespace openjdkjvmti diff --git a/openjdkjvmti/ti_extension.h b/openjdkjvmti/ti_extension.h index d705ba7f59fbef3b88adb046cd5de85327ca6a95..18133e98e2073bb5d3644d6d38c463dd0a94384d 100644 --- a/openjdkjvmti/ti_extension.h +++ b/openjdkjvmti/ti_extension.h @@ -37,6 +37,8 @@ namespace openjdkjvmti { +class EventHandler; + class ExtensionUtil { public: static jvmtiError GetExtensionFunctions(jvmtiEnv* env, @@ -49,7 +51,8 @@ class ExtensionUtil { static jvmtiError SetExtensionEventCallback(jvmtiEnv* env, jint extension_event_index, - jvmtiExtensionEvent callback); + jvmtiExtensionEvent callback, + EventHandler* event_handler); }; } // namespace openjdkjvmti diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc index cf93bf0fb03d334d65ff18729882c2937e6fc8f2..947ba7911ff28055f1f3dce75a10a74a30365faa 100644 --- a/openjdkjvmti/ti_method.cc +++ b/openjdkjvmti/ti_method.cc @@ -37,6 +37,7 @@ #include "art_method-inl.h" #include "base/enums.h" #include "base/mutex-inl.h" +#include "code_item_accessors-inl.h" #include "dex_file_annotations.h" #include "dex_file_types.h" #include "events-inl.h" @@ -48,6 +49,7 @@ #include "mirror/object_array-inl.h" #include "modifiers.h" #include "nativehelper/scoped_local_ref.h" +#include "oat_file.h" #include "runtime_callbacks.h" #include "scoped_thread_state_change-inl.h" #include "stack.h" @@ -121,19 +123,19 @@ jvmtiError MethodUtil::GetBytecodes(jvmtiEnv* env, } art::ScopedObjectAccess soa(art::Thread::Current()); - const art::DexFile::CodeItem* code_item = art_method->GetCodeItem(); - if (code_item == nullptr) { + art::CodeItemInstructionAccessor accessor(art_method); + if (!accessor.HasCodeItem()) { *size_ptr = 0; *bytecode_ptr = nullptr; return OK; } // 2 bytes per instruction for dex code. - *size_ptr = code_item->insns_size_in_code_units_ * 2; + *size_ptr = accessor.InsnsSizeInCodeUnits() * 2; jvmtiError err = env->Allocate(*size_ptr, bytecode_ptr); if (err != OK) { return err; } - memcpy(*bytecode_ptr, code_item->insns_, *size_ptr); + memcpy(*bytecode_ptr, accessor.Insns(), *size_ptr); return OK; } @@ -166,7 +168,7 @@ jvmtiError MethodUtil::GetArgumentsSize(jvmtiEnv* env ATTRIBUTE_UNUSED, } DCHECK_NE(art_method->GetCodeItemOffset(), 0u); - *size_ptr = art_method->GetCodeItem()->ins_size_; + *size_ptr = art::CodeItemDataAccessor(art_method).InsSize(); return ERR(NONE); } @@ -189,12 +191,17 @@ jvmtiError MethodUtil::GetLocalVariableTable(jvmtiEnv* env, } art::ScopedObjectAccess soa(art::Thread::Current()); - const art::DexFile* dex_file = art_method->GetDexFile(); - const art::DexFile::CodeItem* code_item = art_method->GetCodeItem(); - // TODO code_item == nullptr means that the method is abstract (or native, but we check that + + const art::DexFile* const dex_file = art_method->GetDexFile(); + if (dex_file == nullptr) { + return ERR(ABSENT_INFORMATION); + } + + // TODO HasCodeItem == false means that the method is abstract (or native, but we check that // earlier). We should check what is returned by the RI in this situation since it's not clear // what the appropriate return value is from the spec. - if (dex_file == nullptr || code_item == nullptr) { + art::CodeItemDebugInfoAccessor accessor(art_method); + if (!accessor.HasCodeItem()) { return ERR(ABSENT_INFORMATION); } @@ -259,11 +266,10 @@ jvmtiError MethodUtil::GetLocalVariableTable(jvmtiEnv* env, }; LocalVariableContext context(env); - if (!dex_file->DecodeDebugLocalInfo(code_item, - art_method->IsStatic(), - art_method->GetDexMethodIndex(), - LocalVariableContext::Callback, - &context)) { + if (!accessor.DecodeDebugLocalInfo(art_method->IsStatic(), + art_method->GetDexMethodIndex(), + LocalVariableContext::Callback, + &context)) { // Something went wrong with decoding the debug information. It might as well not be there. return ERR(ABSENT_INFORMATION); } else { @@ -295,7 +301,7 @@ jvmtiError MethodUtil::GetMaxLocals(jvmtiEnv* env ATTRIBUTE_UNUSED, } DCHECK_NE(art_method->GetCodeItemOffset(), 0u); - *max_ptr = art_method->GetCodeItem()->registers_size_; + *max_ptr = art::CodeItemDataAccessor(art_method).RegistersSize(); return ERR(NONE); } @@ -410,7 +416,7 @@ jvmtiError MethodUtil::GetMethodLocation(jvmtiEnv* env ATTRIBUTE_UNUSED, DCHECK_NE(art_method->GetCodeItemOffset(), 0u); *start_location_ptr = 0; - *end_location_ptr = art_method->GetCodeItem()->insns_size_in_code_units_ - 1; + *end_location_ptr = art_method->DexInstructions().InsnsSizeInCodeUnits() - 1; return ERR(NONE); } @@ -459,7 +465,7 @@ jvmtiError MethodUtil::GetLineNumberTable(jvmtiEnv* env, art::ArtMethod* art_method = art::jni::DecodeArtMethod(method); DCHECK(!art_method->IsRuntimeMethod()); - const art::DexFile::CodeItem* code_item; + art::CodeItemDebugInfoAccessor accessor; const art::DexFile* dex_file; { art::ScopedObjectAccess soa(art::Thread::Current()); @@ -474,13 +480,14 @@ jvmtiError MethodUtil::GetLineNumberTable(jvmtiEnv* env, return ERR(NULL_POINTER); } - code_item = art_method->GetCodeItem(); + accessor = art::CodeItemDebugInfoAccessor(art_method); dex_file = art_method->GetDexFile(); - DCHECK(code_item != nullptr) << art_method->PrettyMethod() << " " << dex_file->GetLocation(); + DCHECK(accessor.HasCodeItem()) << art_method->PrettyMethod() << " " << dex_file->GetLocation(); } LineNumberContext context; - bool success = dex_file->DecodeDebugPositionInfo(code_item, CollectLineNumbers, &context); + bool success = dex_file->DecodeDebugPositionInfo( + accessor.DebugInfoOffset(), CollectLineNumbers, &context); if (!success) { return ERR(ABSENT_INFORMATION); } @@ -560,7 +567,7 @@ class CommonLocalVariableClosure : public art::Closure { // TODO It might be useful to fake up support for get at least on proxy frames. result_ = ERR(OPAQUE_FRAME); return; - } else if (method->GetCodeItem()->registers_size_ <= slot_) { + } else if (art::CodeItemDataAccessor(method).RegistersSize() <= slot_) { result_ = ERR(INVALID_SLOT); return; } @@ -608,8 +615,11 @@ class CommonLocalVariableClosure : public art::Closure { /*out*/art::Primitive::Type* type) REQUIRES(art::Locks::mutator_lock_) { const art::DexFile* dex_file = method->GetDexFile(); - const art::DexFile::CodeItem* code_item = method->GetCodeItem(); - if (dex_file == nullptr || code_item == nullptr) { + if (dex_file == nullptr) { + return ERR(OPAQUE_FRAME); + } + art::CodeItemDebugInfoAccessor accessor(method); + if (!accessor.HasCodeItem()) { return ERR(OPAQUE_FRAME); } @@ -648,7 +658,10 @@ class CommonLocalVariableClosure : public art::Closure { }; GetLocalVariableInfoContext context(slot_, dex_pc, descriptor, type); - if (!dex_file->DecodeDebugLocalInfo(code_item, + if (!dex_file->DecodeDebugLocalInfo(accessor.RegistersSize(), + accessor.InsSize(), + accessor.InsnsSizeInCodeUnits(), + accessor.DebugInfoOffset(), method->IsStatic(), method->GetDexMethodIndex(), GetLocalVariableInfoContext::Callback, diff --git a/openjdkjvmti/ti_monitor.cc b/openjdkjvmti/ti_monitor.cc index b31e9f239da9f4b708ef95e5774bf3cf01e7c4fe..7db0566a2efa4a49487f472b26d44e957ae7d93d 100644 --- a/openjdkjvmti/ti_monitor.cc +++ b/openjdkjvmti/ti_monitor.cc @@ -32,9 +32,9 @@ #include "ti_monitor.h" #include -#include // NOLINT [build/c++11] [5] -#include // NOLINT [build/c++11] [5] -#include // NOLINT [build/c++11] [5] +#include +#include +#include #include "art_jvmti.h" #include "monitor.h" diff --git a/openjdkjvmti/ti_phase.cc b/openjdkjvmti/ti_phase.cc index 23df27fbda4c55029c228d44da222cd0d392eded..7157974c13fea9c0cb605271a4231ff553083dc5 100644 --- a/openjdkjvmti/ti_phase.cc +++ b/openjdkjvmti/ti_phase.cc @@ -57,6 +57,7 @@ struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback { } void NextRuntimePhase(RuntimePhase phase) REQUIRES_SHARED(art::Locks::mutator_lock_) OVERRIDE { + art::Thread* self = art::Thread::Current(); switch (phase) { case RuntimePhase::kInitialAgents: PhaseUtil::current_phase_ = JVMTI_PHASE_PRIMORDIAL; @@ -64,8 +65,7 @@ struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback { case RuntimePhase::kStart: { PhaseUtil::current_phase_ = JVMTI_PHASE_START; - art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative); - event_handler->DispatchEvent(nullptr, GetJniEnv()); + event_handler->DispatchEvent(self, GetJniEnv()); } break; case RuntimePhase::kInit: @@ -74,9 +74,7 @@ struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback { PhaseUtil::current_phase_ = JVMTI_PHASE_LIVE; { ScopedLocalRef thread(GetJniEnv(), GetCurrentJThread()); - art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative); - event_handler->DispatchEvent( - nullptr, GetJniEnv(), thread.get()); + event_handler->DispatchEvent(self, GetJniEnv(), thread.get()); } // We need to have these events be ordered to match behavior expected by some real-world // agents. The spec does not really require this but compatibility is a useful property to @@ -86,8 +84,7 @@ struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback { break; case RuntimePhase::kDeath: { - art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative); - event_handler->DispatchEvent(nullptr, GetJniEnv()); + event_handler->DispatchEvent(self, GetJniEnv()); PhaseUtil::current_phase_ = JVMTI_PHASE_DEAD; } // TODO: Block events now. diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc index c4f16f5e2d64589cccc50077212f30c9a5debea0..c18b3546757aced8f1a5f929e98076e9ee2fa404 100644 --- a/openjdkjvmti/ti_redefine.cc +++ b/openjdkjvmti/ti_redefine.cc @@ -33,13 +33,13 @@ #include -#include "android-base/stringprintf.h" +#include +#include #include "art_field-inl.h" #include "art_jvmti.h" #include "art_method-inl.h" #include "base/array_ref.h" -#include "base/logging.h" #include "base/stringpiece.h" #include "class_linker-inl.h" #include "debugger.h" @@ -610,7 +610,7 @@ bool Redefiner::ClassRedefinition::CheckSameMethods() { // Check each of the methods. NB we don't need to specifically check for removals since the 2 dex // files have the same number of methods, which means there must be an equal amount of additions // and removals. - for (; new_iter.HasNextVirtualMethod() || new_iter.HasNextDirectMethod(); new_iter.Next()) { + for (; new_iter.HasNextMethod(); new_iter.Next()) { // Get the data on the method we are searching for const art::DexFile::MethodId& new_method_id = dex_file_->GetMethodId(new_iter.GetMemberIndex()); const char* new_method_name = dex_file_->GetMethodName(new_method_id); @@ -629,8 +629,8 @@ bool Redefiner::ClassRedefinition::CheckSameMethods() { // Since direct methods have different flags than virtual ones (specifically direct methods must // have kAccPrivate or kAccStatic or kAccConstructor flags) we can tell if a method changes from // virtual to direct. - uint32_t new_flags = new_iter.GetMethodAccessFlags() & ~art::kAccPreviouslyWarm; - if (new_flags != (old_method->GetAccessFlags() & (art::kAccValidMethodFlags ^ art::kAccPreviouslyWarm))) { + uint32_t new_flags = new_iter.GetMethodAccessFlags(); + if (new_flags != (old_method->GetAccessFlags() & art::kAccValidMethodFlags)) { RecordFailure(ERR(UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED), StringPrintf("method '%s' (sig: %s) had different access flags", new_method_name, diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc index b43eaa028635d1aba6bbdcf9be7dee71607ba594..f7e3b51d260313c14eb65e3f15b870184920182c 100644 --- a/openjdkjvmti/ti_stack.cc +++ b/openjdkjvmti/ti_stack.cc @@ -44,6 +44,7 @@ #include "base/bit_utils.h" #include "base/enums.h" #include "base/mutex.h" +#include "code_item_accessors-inl.h" #include "dex_file.h" #include "dex_file_annotations.h" #include "dex_file_types.h" @@ -891,7 +892,7 @@ struct MonitorInfoClosure : public art::Closure { visitor.WalkStack(/* include_transitions */ false); // Find any other monitors, including ones acquired in native code. art::RootInfo root_info(art::kRootVMInternal); - target->GetJniEnv()->monitors.VisitRoots(&visitor, root_info); + target->GetJniEnv()->VisitMonitorRoots(&visitor, root_info); err_ = handle_results_(soa_, visitor); } @@ -1044,7 +1045,7 @@ jvmtiError StackUtil::NotifyFramePop(jvmtiEnv* env, jthread thread, jint depth) if (shadow_frame == nullptr) { needs_instrument = true; const size_t frame_id = visitor.GetFrameId(); - const uint16_t num_regs = method->GetCodeItem()->registers_size_; + const uint16_t num_regs = art::CodeItemDataAccessor(method).RegistersSize(); shadow_frame = target->FindOrCreateDebuggerShadowFrame(frame_id, num_regs, method, diff --git a/openjdkjvmti/ti_thread.cc b/openjdkjvmti/ti_thread.cc index 6d075a6b7be8fd9c6f86a33a32ebb7e44f1cc396..555c5a725bf3bcc4d6c527a7123e47aac87ef9fd 100644 --- a/openjdkjvmti/ti_thread.cc +++ b/openjdkjvmti/ti_thread.cc @@ -31,10 +31,11 @@ #include "ti_thread.h" -#include "android-base/strings.h" +#include +#include + #include "art_field-inl.h" #include "art_jvmti.h" -#include "base/logging.h" #include "base/mutex.h" #include "events-inl.h" #include "gc/system_weak.h" @@ -47,6 +48,7 @@ #include "mirror/object-inl.h" #include "mirror/string.h" #include "nativehelper/scoped_local_ref.h" +#include "nativehelper/scoped_utf_chars.h" #include "obj_ptr.h" #include "runtime.h" #include "runtime_callbacks.h" @@ -701,6 +703,7 @@ struct AgentData { JavaVM* java_vm; jvmtiEnv* jvmti_env; jint priority; + std::string name; }; static void* AgentCallback(void* arg) { @@ -708,13 +711,13 @@ static void* AgentCallback(void* arg) { CHECK(data->thread != nullptr); // We already have a peer. So call our special Attach function. - art::Thread* self = art::Thread::Attach("JVMTI Agent thread", true, data->thread); + art::Thread* self = art::Thread::Attach(data->name.c_str(), true, data->thread); 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("JVMTI Agent thread"); + self->SetThreadName(data->name.c_str()); } // Release the peer. @@ -781,6 +784,16 @@ jvmtiError ThreadUtil::RunAgentThread(jvmtiEnv* jvmti_env, data->java_vm = art::Runtime::Current()->GetJavaVM(); data->jvmti_env = jvmti_env; data->priority = priority; + ScopedLocalRef s( + env, + reinterpret_cast( + env->GetObjectField(thread, art::WellKnownClasses::java_lang_Thread_name))); + if (s == nullptr) { + data->name = "JVMTI Agent Thread"; + } else { + ScopedUtfChars name(env, s.get()); + data->name = name.c_str(); + } pthread_t pthread; int pthread_create_result = pthread_create(&pthread, diff --git a/patchoat/Android.bp b/patchoat/Android.bp index d3bc2a754b1829900eda2fa479ab6aebb326d2db..0902823644d239232eb89b6175cebb8573463672 100644 --- a/patchoat/Android.bp +++ b/patchoat/Android.bp @@ -47,3 +47,16 @@ art_cc_binary { "libartd", ], } + +art_cc_test { + name: "art_patchoat_tests", + defaults: [ + "art_gtest_defaults", + ], + srcs: [ + "patchoat_test.cc", + ], + shared_libs: [ + "libartd", + ], +} diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index ae82d72c0fef133d861ef630d73393c21cfaabe0..eb648cba18e8c19c6d95d9636b91aa9985570b5b 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -30,6 +30,7 @@ #include "art_field-inl.h" #include "art_method-inl.h" #include "base/dumpable.h" +#include "base/logging.h" // For InitLogging. #include "base/memory_tool.h" #include "base/scoped_flock.h" #include "base/stringpiece.h" diff --git a/patchoat/patchoat_test.cc b/patchoat/patchoat_test.cc new file mode 100644 index 0000000000000000000000000000000000000000..86e851c72b7dfabde5887e126218c8071987701c --- /dev/null +++ b/patchoat/patchoat_test.cc @@ -0,0 +1,411 @@ +/* + * 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 +#include + +#include +#include + +#include "android-base/stringprintf.h" +#include "android-base/strings.h" + +#include "dexopt_test.h" +#include "runtime.h" + +#include + +namespace art { + +using android::base::StringPrintf; + +class PatchoatTest : public DexoptTest { + public: + static bool ListDirFilesEndingWith( + const std::string& dir, + const std::string& suffix, + std::vector* filenames, + std::string* error_msg) { + DIR* d = opendir(dir.c_str()); + if (d == nullptr) { + *error_msg = "Failed to open directory"; + return false; + } + dirent* e; + struct stat s; + size_t suffix_len = suffix.size(); + while ((e = readdir(d)) != nullptr) { + if ((strcmp(e->d_name, ".") == 0) || (strcmp(e->d_name, "..") == 0)) { + continue; + } + size_t name_len = strlen(e->d_name); + if ((name_len < suffix_len) || (strcmp(&e->d_name[name_len - suffix_len], suffix.c_str()))) { + continue; + } + std::string basename(e->d_name); + std::string filename = dir + "/" + basename; + int stat_result = lstat(filename.c_str(), &s); + if (stat_result != 0) { + *error_msg = + StringPrintf("Failed to stat %s: stat returned %d", filename.c_str(), stat_result); + return false; + } + if (S_ISDIR(s.st_mode)) { + continue; + } + filenames->push_back(basename); + } + closedir(d); + return true; + } + + static void AddRuntimeArg(std::vector& args, const std::string& arg) { + args.push_back("--runtime-arg"); + args.push_back(arg); + } + + bool CompileBootImage(const std::vector& extra_args, + const std::string& image_file_name_prefix, + uint32_t base_addr, + std::string* error_msg) { + Runtime* const runtime = Runtime::Current(); + std::vector argv; + argv.push_back(runtime->GetCompilerExecutable()); + AddRuntimeArg(argv, "-Xms64m"); + AddRuntimeArg(argv, "-Xmx64m"); + std::vector dex_files = GetLibCoreDexFileNames(); + for (const std::string& dex_file : dex_files) { + argv.push_back("--dex-file=" + dex_file); + argv.push_back("--dex-location=" + dex_file); + } + if (runtime->IsJavaDebuggable()) { + argv.push_back("--debuggable"); + } + runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv); + + AddRuntimeArg(argv, "-Xverify:softfail"); + + if (!kIsTargetBuild) { + argv.push_back("--host"); + } + + argv.push_back("--image=" + image_file_name_prefix + ".art"); + argv.push_back("--oat-file=" + image_file_name_prefix + ".oat"); + argv.push_back("--oat-location=" + image_file_name_prefix + ".oat"); + argv.push_back(StringPrintf("--base=0x%" PRIx32, base_addr)); + argv.push_back("--compile-pic"); + argv.push_back("--multi-image"); + argv.push_back("--no-generate-debug-info"); + + std::vector compiler_options = runtime->GetCompilerOptions(); + argv.insert(argv.end(), compiler_options.begin(), compiler_options.end()); + + // We must set --android-root. + const char* android_root = getenv("ANDROID_ROOT"); + CHECK(android_root != nullptr); + argv.push_back("--android-root=" + std::string(android_root)); + argv.insert(argv.end(), extra_args.begin(), extra_args.end()); + + return RunDex2OatOrPatchoat(argv, error_msg); + } + + bool RelocateBootImage(const std::string& input_image_location, + const std::string& output_image_filename, + off_t base_offset_delta, + std::string* error_msg) { + Runtime* const runtime = Runtime::Current(); + std::vector argv; + argv.push_back(runtime->GetPatchoatExecutable()); + argv.push_back("--input-image-location=" + input_image_location); + argv.push_back("--output-image-file=" + output_image_filename); + argv.push_back(StringPrintf("--base-offset-delta=0x%jx", (intmax_t) base_offset_delta)); + argv.push_back(StringPrintf("--instruction-set=%s", GetInstructionSetString(kRuntimeISA))); + + return RunDex2OatOrPatchoat(argv, error_msg); + } + + bool RunDex2OatOrPatchoat(const std::vector& args, std::string* error_msg) { + int link[2]; + + if (pipe(link) == -1) { + return false; + } + + pid_t pid = fork(); + if (pid == -1) { + return false; + } + + if (pid == 0) { + // We need dex2oat to actually log things. + setenv("ANDROID_LOG_TAGS", "*:e", 1); + dup2(link[1], STDERR_FILENO); + close(link[0]); + close(link[1]); + std::vector c_args; + for (const std::string& str : args) { + c_args.push_back(str.c_str()); + } + c_args.push_back(nullptr); + execv(c_args[0], const_cast(c_args.data())); + exit(1); + UNREACHABLE(); + } else { + close(link[1]); + char buffer[128]; + memset(buffer, 0, 128); + ssize_t bytes_read = 0; + + while (TEMP_FAILURE_RETRY(bytes_read = read(link[0], buffer, 128)) > 0) { + *error_msg += std::string(buffer, bytes_read); + } + close(link[0]); + int status = -1; + if (waitpid(pid, &status, 0) != -1) { + return (status == 0); + } + return false; + } + } + + bool CompileBootImageToDir( + const std::string& output_dir, + const std::vector& dex2oat_extra_args, + uint32_t base_addr, + std::string* error_msg) { + return CompileBootImage(dex2oat_extra_args, output_dir + "/boot", base_addr, error_msg); + } + + bool CopyImageChecksumAndSetPatchDelta( + const std::string& src_image_filename, + const std::string& dest_image_filename, + off_t dest_patch_delta, + std::string* error_msg) { + std::unique_ptr src_file(OS::OpenFileForReading(src_image_filename.c_str())); + if (src_file.get() == nullptr) { + *error_msg = StringPrintf("Failed to open source image file %s", src_image_filename.c_str()); + return false; + } + ImageHeader src_header; + if (!src_file->ReadFully(&src_header, sizeof(src_header))) { + *error_msg = StringPrintf("Failed to read source image file %s", src_image_filename.c_str()); + return false; + } + + std::unique_ptr dest_file(OS::OpenFileReadWrite(dest_image_filename.c_str())); + if (dest_file.get() == nullptr) { + *error_msg = + StringPrintf("Failed to open destination image file %s", dest_image_filename.c_str()); + return false; + } + ImageHeader dest_header; + if (!dest_file->ReadFully(&dest_header, sizeof(dest_header))) { + *error_msg = + StringPrintf("Failed to read destination image file %s", dest_image_filename.c_str()); + return false; + } + dest_header.SetOatChecksum(src_header.GetOatChecksum()); + dest_header.SetPatchDelta(dest_patch_delta); + if (!dest_file->ResetOffset()) { + *error_msg = + StringPrintf( + "Failed to seek to start of destination image file %s", dest_image_filename.c_str()); + return false; + } + if (!dest_file->WriteFully(&dest_header, sizeof(dest_header))) { + *error_msg = + StringPrintf("Failed to write to destination image file %s", dest_image_filename.c_str()); + dest_file->Erase(); + return false; + } + if (dest_file->FlushCloseOrErase() != 0) { + *error_msg = + StringPrintf( + "Failed to flush/close destination image file %s", dest_image_filename.c_str()); + return false; + } + + return true; + } + + bool ReadFully( + const std::string& filename, std::vector* contents, std::string* error_msg) { + std::unique_ptr file(OS::OpenFileForReading(filename.c_str())); + if (file.get() == nullptr) { + *error_msg = "Failed to open"; + return false; + } + int64_t size = file->GetLength(); + if (size < 0) { + *error_msg = "Failed to get size"; + return false; + } + contents->resize(size); + if (!file->ReadFully(&(*contents)[0], size)) { + *error_msg = "Failed to read"; + contents->clear(); + return false; + } + return true; + } + + bool BinaryDiff( + const std::string& filename1, const std::string& filename2, std::string* error_msg) { + std::string read_error_msg; + std::vector image1; + if (!ReadFully(filename1, &image1, &read_error_msg)) { + *error_msg = StringPrintf("Failed to read %s: %s", filename1.c_str(), read_error_msg.c_str()); + return true; + } + std::vector image2; + if (!ReadFully(filename2, &image2, &read_error_msg)) { + *error_msg = StringPrintf("Failed to read %s: %s", filename2.c_str(), read_error_msg.c_str()); + return true; + } + if (image1.size() != image2.size()) { + *error_msg = + StringPrintf( + "%s and %s are of different size: %zu vs %zu", + filename1.c_str(), + filename2.c_str(), + image1.size(), + image2.size()); + return true; + } + size_t size = image1.size(); + for (size_t i = 0; i < size; i++) { + if (image1[i] != image2[i]) { + *error_msg = + StringPrintf("%s and %s differ at offset %zu", filename1.c_str(), filename2.c_str(), i); + return true; + } + } + + return false; + } +}; + +TEST_F(PatchoatTest, PatchoatRelocationSameAsDex2oatRelocation) { +#if defined(ART_USE_READ_BARRIER) + // This test checks that relocating a boot image using patchoat produces the same result as + // producing the boot image for that relocated base address using dex2oat. To be precise, these + // two files will have two small differences: the OAT checksum and base address. However, this + // test takes this into account. + + // Compile boot image into a random directory using dex2oat + ScratchFile dex2oat_orig_scratch; + dex2oat_orig_scratch.Unlink(); + std::string dex2oat_orig_dir = dex2oat_orig_scratch.GetFilename(); + ASSERT_EQ(0, mkdir(dex2oat_orig_dir.c_str(), 0700)); + const uint32_t orig_base_addr = 0x60000000; + // Force deterministic output. We want the boot images created by this dex2oat run and the run + // below to differ only in their base address. + std::vector dex2oat_extra_args; + dex2oat_extra_args.push_back("--force-determinism"); + dex2oat_extra_args.push_back("-j1"); // Might not be needed. Causes a 3-5x slowdown. + std::string error_msg; + if (!CompileBootImageToDir(dex2oat_orig_dir, dex2oat_extra_args, orig_base_addr, &error_msg)) { + FAIL() << "CompileBootImage1 failed: " << error_msg; + } + + // Compile a "relocated" boot image into a random directory using dex2oat. This image is relocated + // in the sense that it uses a different base address. + ScratchFile dex2oat_reloc_scratch; + dex2oat_reloc_scratch.Unlink(); + std::string dex2oat_reloc_dir = dex2oat_reloc_scratch.GetFilename(); + ASSERT_EQ(0, mkdir(dex2oat_reloc_dir.c_str(), 0700)); + const uint32_t reloc_base_addr = 0x70000000; + if (!CompileBootImageToDir(dex2oat_reloc_dir, dex2oat_extra_args, reloc_base_addr, &error_msg)) { + FAIL() << "CompileBootImage2 failed: " << error_msg; + } + const off_t base_addr_delta = reloc_base_addr - orig_base_addr; + + // Relocate the original boot image using patchoat. The image is relocated by the same amount + // as the second/relocated image produced by dex2oat. + ScratchFile patchoat_scratch; + patchoat_scratch.Unlink(); + std::string patchoat_dir = patchoat_scratch.GetFilename(); + ASSERT_EQ(0, mkdir(patchoat_dir.c_str(), 0700)); + std::string dex2oat_orig_with_arch_dir = + dex2oat_orig_dir + "/" + GetInstructionSetString(kRuntimeISA); + // The arch-including symlink is needed by patchoat + ASSERT_EQ(0, symlink(dex2oat_orig_dir.c_str(), dex2oat_orig_with_arch_dir.c_str())); + if (!RelocateBootImage( + dex2oat_orig_dir + "/boot.art", + patchoat_dir + "/boot.art", + base_addr_delta, + &error_msg)) { + FAIL() << "RelocateBootImage failed: " << error_msg; + } + + // Assert that patchoat created the same set of .art files as dex2oat + std::vector dex2oat_image_basenames; + std::vector patchoat_image_basenames; + if (!ListDirFilesEndingWith(dex2oat_reloc_dir, ".art", &dex2oat_image_basenames, &error_msg)) { + FAIL() << "Failed to list *.art files in " << dex2oat_reloc_dir << ": " << error_msg; + } + if (!ListDirFilesEndingWith(patchoat_dir, ".art", &patchoat_image_basenames, &error_msg)) { + FAIL() << "Failed to list *.art files in " << patchoat_dir << ": " << error_msg; + } + std::sort(dex2oat_image_basenames.begin(), dex2oat_image_basenames.end()); + std::sort(patchoat_image_basenames.begin(), patchoat_image_basenames.end()); + // .art file names output by patchoat look like tmp@art-data--@boot*.art. To + // compare these with .art file names output by dex2oat we retain only the part of the file name + // after the last @. + std::vector patchoat_image_shortened_basenames(patchoat_image_basenames.size()); + for (size_t i = 0; i < patchoat_image_basenames.size(); i++) { + patchoat_image_shortened_basenames[i] = + patchoat_image_basenames[i].substr(patchoat_image_basenames[i].find_last_of("@") + 1); + } + ASSERT_EQ(dex2oat_image_basenames, patchoat_image_shortened_basenames); + + // Patch up the dex2oat-relocated image files so that it looks as though they were relocated by + // patchoat. patchoat preserves the OAT checksum header field and sets patch delta header field. + for (const std::string& image_basename : dex2oat_image_basenames) { + if (!CopyImageChecksumAndSetPatchDelta( + dex2oat_orig_dir + "/" + image_basename, + dex2oat_reloc_dir + "/" + image_basename, + base_addr_delta, + &error_msg)) { + FAIL() << "Unable to patch up " << image_basename << ": " << error_msg; + } + } + + // Assert that the patchoat-relocated images are identical to the dex2oat-relocated images + for (size_t i = 0; i < dex2oat_image_basenames.size(); i++) { + const std::string& dex2oat_image_basename = dex2oat_image_basenames[i]; + const std::string& dex2oat_image_filename = dex2oat_reloc_dir + "/" + dex2oat_image_basename; + const std::string& patchoat_image_filename = patchoat_dir + "/" + patchoat_image_basenames[i]; + if (BinaryDiff(dex2oat_image_filename, patchoat_image_filename, &error_msg)) { + FAIL() << "patchoat- and dex2oat-relocated variants of " << dex2oat_image_basename + << " differ: " << error_msg; + } + } + + ClearDirectory(dex2oat_orig_dir.c_str(), /*recursive*/ true); + ClearDirectory(dex2oat_reloc_dir.c_str(), /*recursive*/ true); + ClearDirectory(patchoat_dir.c_str(), /*recursive*/ true); + rmdir(dex2oat_orig_dir.c_str()); + rmdir(dex2oat_reloc_dir.c_str()); + rmdir(patchoat_dir.c_str()); +#else + LOG(INFO) << "Skipping PatchoatRelocationSameAsDex2oatRelocation"; + // Force-print to std::cout so it's also outside the logcat. + std::cout << "Skipping PatchoatRelocationSameAsDex2oatRelocation" << std::endl; +#endif +} + +} // namespace art diff --git a/profman/boot_image_profile.cc b/profman/boot_image_profile.cc index 4092f6ed98213c88bc6b6dcdf8814e3714dd5248..48b87c9a6d0c421428f8de99ade8e650cf1ece97 100644 --- a/profman/boot_image_profile.cc +++ b/profman/boot_image_profile.cc @@ -90,9 +90,9 @@ void GenerateBootImageProfile( it.Next(); } it.SkipInstanceFields(); - while (it.HasNextDirectMethod() || it.HasNextVirtualMethod()) { + while (it.HasNextMethod()) { const uint32_t flags = it.GetMethodAccessFlags(); - if ((flags & kAccNative) != 0 || (flags & kAccFastNative) != 0) { + if ((flags & kAccNative) != 0) { // Native method will get dirtied. is_clean = false; break; diff --git a/profman/profman.cc b/profman/profman.cc index 1c506450730be2b4cdbb47ea3404f768e7079eff..0bef205de6c7bea86a218e9f33498b5135c70b3e 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -32,6 +32,7 @@ #include "android-base/strings.h" #include "base/dumpable.h" +#include "base/logging.h" // For InitLogging. #include "base/scoped_flock.h" #include "base/stringpiece.h" #include "base/time_utils.h" @@ -786,7 +787,7 @@ class ProfMan FINAL { method_str = line.substr(method_sep_index + kMethodSep.size()); } - TypeReference class_ref; + TypeReference class_ref(/* dex_file */ nullptr, dex::TypeIndex()); if (!FindClass(dex_files, klass, &class_ref)) { LOG(WARNING) << "Could not find class: " << klass; return false; @@ -810,7 +811,7 @@ class ProfMan FINAL { if (class_data != nullptr) { ClassDataItemIterator it(*dex_file, class_data); it.SkipAllFields(); - while (it.HasNextDirectMethod() || it.HasNextVirtualMethod()) { + while (it.HasNextMethod()) { if (it.GetMethodCodeItemOffset() != 0) { // Add all of the methods that have code to the profile. const uint32_t method_idx = it.GetMemberIndex(); @@ -860,7 +861,8 @@ class ProfMan FINAL { if (!HasSingleInvoke(class_ref, method_index, &dex_pc)) { return false; } - std::vector classes(inline_cache_elems.size()); + std::vector classes(inline_cache_elems.size(), + TypeReference(/* dex_file */ nullptr, dex::TypeIndex())); size_t class_it = 0; for (const std::string& ic_class : inline_cache_elems) { if (!FindClass(dex_files, ic_class, &(classes[class_it++]))) { diff --git a/runtime/Android.bp b/runtime/Android.bp index e032238324da6cefec55fdee754bccd7e4ee21b9..1e5fe16e193187bb419d471ce992f2132911f7d9 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -39,6 +39,7 @@ cc_defaults { "base/hex_dump.cc", "base/logging.cc", "base/mutex.cc", + "base/runtime_debug.cc", "base/safe_copy.cc", "base/scoped_arena_allocator.cc", "base/scoped_flock.cc", @@ -103,6 +104,7 @@ cc_defaults { "gc/verification.cc", "hprof/hprof.cc", "image.cc", + "index_bss_mapping.cc", "indirect_reference_table.cc", "instrumentation.cc", "intern_table.cc", @@ -468,6 +470,7 @@ gensrcs { "instrumentation.h", "indirect_reference_table.h", "invoke_type.h", + "jdwp_provider.h", "jdwp/jdwp.h", "jdwp/jdwp_constants.h", "lock_word.h", @@ -567,6 +570,7 @@ art_cc_test { "class_linker_test.cc", "class_loader_context_test.cc", "class_table_test.cc", + "code_item_accessors_test.cc", "compiler_filter_test.cc", "dex_file_test.cc", "dex_file_verifier_test.cc", @@ -599,6 +603,7 @@ art_cc_test { "intern_table_test.cc", "interpreter/safe_math_test.cc", "interpreter/unstarted_runtime_test.cc", + "jdwp/jdwp_options_test.cc", "java_vm_ext_test.cc", "jit/profile_compilation_info_test.cc", "leb128_test.cc", diff --git a/runtime/CPPLINT.cfg b/runtime/CPPLINT.cfg new file mode 100644 index 0000000000000000000000000000000000000000..6f274994a9ece211c01ba87c758fa8f938a10bf5 --- /dev/null +++ b/runtime/CPPLINT.cfg @@ -0,0 +1,18 @@ +# +# 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. +# + +# External headers, not subject to our lint rules. +exclude_files=^elf[.]h$ diff --git a/runtime/arch/arm/context_arm.h b/runtime/arch/arm/context_arm.h index fa9aa46d4d0e490a7c3e675d370bfce2188fbf03..b9802967fe262aaf7807e6303cc3a63a0942ef4d 100644 --- a/runtime/arch/arm/context_arm.h +++ b/runtime/arch/arm/context_arm.h @@ -17,8 +17,9 @@ #ifndef ART_RUNTIME_ARCH_ARM_CONTEXT_ARM_H_ #define ART_RUNTIME_ARCH_ARM_CONTEXT_ARM_H_ +#include + #include "arch/context.h" -#include "base/logging.h" #include "base/macros.h" #include "registers_arm.h" diff --git a/runtime/arch/arm/fault_handler_arm.cc b/runtime/arch/arm/fault_handler_arm.cc index ef2b34236fafd6040596933977fc3a13755c4af1..315bf957ccaa0de6b1b60143898d7d507759f434 100644 --- a/runtime/arch/arm/fault_handler_arm.cc +++ b/runtime/arch/arm/fault_handler_arm.cc @@ -21,7 +21,7 @@ #include "art_method.h" #include "base/enums.h" #include "base/hex_dump.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/macros.h" #include "globals.h" #include "thread-current-inl.h" diff --git a/runtime/arch/arm/instruction_set_features_arm.cc b/runtime/arch/arm/instruction_set_features_arm.cc index b789fc7481f68676e0d989dde1eba8207a18cf2c..801254fd30ed1bc03fdee991c334fdc88d71947e 100644 --- a/runtime/arch/arm/instruction_set_features_arm.cc +++ b/runtime/arch/arm/instruction_set_features_arm.cc @@ -25,10 +25,9 @@ #include -#include "android-base/stringprintf.h" -#include "android-base/strings.h" - -#include "base/logging.h" +#include +#include +#include #if defined(__arm__) extern "C" bool artCheckForArmSdivInstruction(); diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 6ff8dd60b8ace31aedfdb98717229a511a6ae4bf..6ec9c48b92f4ae881905dbc38fc542feeef68a4c 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -1783,7 +1783,9 @@ ENTRY art_quick_generic_jni_trampoline .cfi_adjust_cfa_offset FRAME_SIZE_SAVE_REFS_AND_ARGS-FRAME_SIZE_SAVE_REFS_ONLY .Lexception_in_native: - ldr sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] + ldr ip, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] + add ip, ip, #-1 // Remove the GenericJNI tag. ADD/SUB writing directly to SP is UNPREDICTABLE. + mov sp, ip .cfi_def_cfa_register sp # This will create a new save-all frame, required by the runtime. DELIVER_PENDING_EXCEPTION diff --git a/runtime/arch/arm/thread_arm.cc b/runtime/arch/arm/thread_arm.cc index ff4f81be0fbd500c3e8878391cd764760b32b7c6..18585c7973a2223720ba1afafaaaf96ed2ff37fc 100644 --- a/runtime/arch/arm/thread_arm.cc +++ b/runtime/arch/arm/thread_arm.cc @@ -16,9 +16,10 @@ #include "thread.h" +#include + #include "asm_support_arm.h" #include "base/enums.h" -#include "base/logging.h" namespace art { diff --git a/runtime/arch/arm64/context_arm64.h b/runtime/arch/arm64/context_arm64.h index 36aded07c48ea77feffc71e1571f2222e56fa3da..e64cfb86eaf1d4ee71e1d2ae590ccc51401fa117 100644 --- a/runtime/arch/arm64/context_arm64.h +++ b/runtime/arch/arm64/context_arm64.h @@ -17,8 +17,9 @@ #ifndef ART_RUNTIME_ARCH_ARM64_CONTEXT_ARM64_H_ #define ART_RUNTIME_ARCH_ARM64_CONTEXT_ARM64_H_ +#include + #include "arch/context.h" -#include "base/logging.h" #include "base/macros.h" #include "registers_arm64.h" diff --git a/runtime/arch/arm64/fault_handler_arm64.cc b/runtime/arch/arm64/fault_handler_arm64.cc index d535c7e3c654ee1d668484f39db2be0839489c5d..d282c8cfc018d5723f3935d817e5d012fa6d588a 100644 --- a/runtime/arch/arm64/fault_handler_arm64.cc +++ b/runtime/arch/arm64/fault_handler_arm64.cc @@ -21,7 +21,7 @@ #include "art_method.h" #include "base/enums.h" #include "base/hex_dump.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/macros.h" #include "globals.h" #include "registers_arm64.h" diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc index 3eabcf9374906548256229ee07c391c62598b084..5d5c69fe264d335678232a83e09223192fb0a47b 100644 --- a/runtime/arch/arm64/instruction_set_features_arm64.cc +++ b/runtime/arch/arm64/instruction_set_features_arm64.cc @@ -19,10 +19,10 @@ #include #include -#include "android-base/stringprintf.h" -#include "android-base/strings.h" +#include +#include +#include -#include "base/logging.h" #include "base/stl_util.h" namespace art { diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 280e5937c61b30a5950fc1ece0637c41a2d50778..47efeb9200cdc3450e0c9f3b1af1ba39f542cc36 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -2299,7 +2299,7 @@ ENTRY art_quick_generic_jni_trampoline .Lexception_in_native: // Move to x1 then sp to please assembler. ldr x1, [xSELF, # THREAD_TOP_QUICK_FRAME_OFFSET] - mov sp, x1 + add sp, x1, #-1 // Remove the GenericJNI tag. .cfi_def_cfa_register sp # This will create a new save-all frame, required by the runtime. DELIVER_PENDING_EXCEPTION diff --git a/runtime/arch/arm64/thread_arm64.cc b/runtime/arch/arm64/thread_arm64.cc index 3483b704eafe465037c6f5f67f41a7a7e4f6d020..19c4a6ac85901b32bddc402c4317107c95856afe 100644 --- a/runtime/arch/arm64/thread_arm64.cc +++ b/runtime/arch/arm64/thread_arm64.cc @@ -16,9 +16,10 @@ #include "thread.h" +#include + #include "asm_support_arm64.h" #include "base/enums.h" -#include "base/logging.h" namespace art { diff --git a/runtime/arch/code_offset.h b/runtime/arch/code_offset.h index ab04b1eaa7a46f02398232cae89d0db2923c8dbd..8e8dde4c4c6f2f161bce748b6d8f952b351e7f24 100644 --- a/runtime/arch/code_offset.h +++ b/runtime/arch/code_offset.h @@ -19,8 +19,10 @@ #include +#include + #include "base/bit_utils.h" -#include "base/logging.h" +#include "base/macros.h" #include "instruction_set.h" namespace art { diff --git a/runtime/arch/instruction_set_features_test.cc b/runtime/arch/instruction_set_features_test.cc index 67e2f358c81ca25c0f421028cba27133f8f48fb6..1e3275cc00374fae3dd677b90490bc55ae5a7e31 100644 --- a/runtime/arch/instruction_set_features_test.cc +++ b/runtime/arch/instruction_set_features_test.cc @@ -19,12 +19,11 @@ #include #ifdef ART_TARGET_ANDROID -#include "android-base/properties.h" +#include #endif -#include "android-base/stringprintf.h" - -#include "base/logging.h" +#include +#include namespace art { diff --git a/runtime/arch/mips/context_mips.h b/runtime/arch/mips/context_mips.h index 7dcff630d18ccfc2824732cdb6ee367b45b713d6..7e073b288a050188e740f5ea93923ae491b374e9 100644 --- a/runtime/arch/mips/context_mips.h +++ b/runtime/arch/mips/context_mips.h @@ -17,8 +17,9 @@ #ifndef ART_RUNTIME_ARCH_MIPS_CONTEXT_MIPS_H_ #define ART_RUNTIME_ARCH_MIPS_CONTEXT_MIPS_H_ +#include + #include "arch/context.h" -#include "base/logging.h" #include "base/macros.h" #include "registers_mips.h" diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc index dca3382664eabbb409d085d3cf981ab3b5cb2be2..209f36705aae2409204a009a1f7dc994c50ee278 100644 --- a/runtime/arch/mips/entrypoints_init_mips.cc +++ b/runtime/arch/mips/entrypoints_init_mips.cc @@ -18,6 +18,7 @@ #include "arch/mips/asm_support_mips.h" #include "atomic.h" +#include "base/logging.h" #include "entrypoints/entrypoint_utils.h" #include "entrypoints/jni/jni_entrypoints.h" #include "entrypoints/math_entrypoints.h" diff --git a/runtime/arch/mips/fault_handler_mips.cc b/runtime/arch/mips/fault_handler_mips.cc index 6dce54e5c5259ea08a0c489eb27eed9e6498d0eb..f82dc08cb25bea19d632e073af6f39136030993d 100644 --- a/runtime/arch/mips/fault_handler_mips.cc +++ b/runtime/arch/mips/fault_handler_mips.cc @@ -20,7 +20,7 @@ #include "art_method.h" #include "base/callee_save_type.h" #include "base/hex_dump.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/macros.h" #include "globals.h" #include "quick_method_frame_info_mips.h" diff --git a/runtime/arch/mips/instruction_set_features_mips.cc b/runtime/arch/mips/instruction_set_features_mips.cc index 6d4145bc98a3e2a698135a0f0e1d3dcb260e9a1b..952ed250d2617859867a954246c02a86bce7d1ab 100644 --- a/runtime/arch/mips/instruction_set_features_mips.cc +++ b/runtime/arch/mips/instruction_set_features_mips.cc @@ -19,10 +19,9 @@ #include #include -#include "android-base/stringprintf.h" -#include "android-base/strings.h" +#include +#include -#include "base/logging.h" #include "base/stl_util.h" namespace art { diff --git a/runtime/arch/mips/instruction_set_features_mips.h b/runtime/arch/mips/instruction_set_features_mips.h index ee539edf3ab79bfc8d12ced0dc4ff761de6adda8..76bc639277f9be01b9b884e479b91adb55d1225b 100644 --- a/runtime/arch/mips/instruction_set_features_mips.h +++ b/runtime/arch/mips/instruction_set_features_mips.h @@ -17,8 +17,9 @@ #ifndef ART_RUNTIME_ARCH_MIPS_INSTRUCTION_SET_FEATURES_MIPS_H_ #define ART_RUNTIME_ARCH_MIPS_INSTRUCTION_SET_FEATURES_MIPS_H_ +#include + #include "arch/instruction_set_features.h" -#include "base/logging.h" #include "base/macros.h" namespace art { diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 489c52c0d2f5fbfeca3d993cc89aa1fa20573747..fc77a641b3c43ea329a6683522388c920e0257e1 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -2283,7 +2283,8 @@ ENTRY art_quick_generic_jni_trampoline nop 2: - lw $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) + lw $t0, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) + addiu $sp, $t0, -1 // Remove the GenericJNI tag. move $gp, $s3 # restore $gp from $s3 # This will create a new save-all frame, required by the runtime. DELIVER_PENDING_EXCEPTION diff --git a/runtime/arch/mips/registers_mips.h b/runtime/arch/mips/registers_mips.h index 57af150b33116323a978b1bae307e57ba6cada09..c7f9a3e74eccc3f8644dfc86da823e3da36c7f9c 100644 --- a/runtime/arch/mips/registers_mips.h +++ b/runtime/arch/mips/registers_mips.h @@ -19,7 +19,8 @@ #include -#include "base/logging.h" +#include + #include "base/macros.h" #include "globals.h" @@ -100,7 +101,8 @@ enum FRegister { F29 = 29, F30 = 30, F31 = 31, - FTMP = F6, // scratch register + FTMP = F6, // scratch register + FTMP2 = F7, // scratch register (in addition to FTMP, reserved for MSA instructions) kNumberOfFRegisters = 32, kNoFRegister = -1, }; diff --git a/runtime/arch/mips/thread_mips.cc b/runtime/arch/mips/thread_mips.cc index 0a9ab7aacd0de9586d98d8d1663868debd803465..0be7a7f4cb624cd5165d66a8de1bf565607a479f 100644 --- a/runtime/arch/mips/thread_mips.cc +++ b/runtime/arch/mips/thread_mips.cc @@ -16,9 +16,10 @@ #include "thread.h" +#include + #include "asm_support_mips.h" #include "base/enums.h" -#include "base/logging.h" namespace art { diff --git a/runtime/arch/mips64/context_mips64.h b/runtime/arch/mips64/context_mips64.h index 89fbf8ffc36106b9dbb96fd4f16a98a8e28d8295..b2a6138471bbb22f943f78f3746c63896bcb6ea2 100644 --- a/runtime/arch/mips64/context_mips64.h +++ b/runtime/arch/mips64/context_mips64.h @@ -17,8 +17,9 @@ #ifndef ART_RUNTIME_ARCH_MIPS64_CONTEXT_MIPS64_H_ #define ART_RUNTIME_ARCH_MIPS64_CONTEXT_MIPS64_H_ +#include + #include "arch/context.h" -#include "base/logging.h" #include "base/macros.h" #include "registers_mips64.h" diff --git a/runtime/arch/mips64/fault_handler_mips64.cc b/runtime/arch/mips64/fault_handler_mips64.cc index bdce5209374c5b6d0dea8ca69aa9c5e794953aaf..ba6fff05adfa452ee7e2e2741eed5cf93624bdb7 100644 --- a/runtime/arch/mips64/fault_handler_mips64.cc +++ b/runtime/arch/mips64/fault_handler_mips64.cc @@ -21,7 +21,7 @@ #include "art_method.h" #include "base/callee_save_type.h" #include "base/hex_dump.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/macros.h" #include "globals.h" #include "quick_method_frame_info_mips64.h" diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index 98ffe6504a8a5800720eb2246f4ded91bdcd5552..3fb83d92320a8b329fda490d6c01b843cac07ecf 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -2158,7 +2158,8 @@ ENTRY art_quick_generic_jni_trampoline dmtc1 $v0, $f0 # place return value to FP return value 1: - ld $sp, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) + ld $t0, THREAD_TOP_QUICK_FRAME_OFFSET(rSELF) + daddiu $sp, $t0, -1 // Remove the GenericJNI tag. # This will create a new save-all frame, required by the runtime. DELIVER_PENDING_EXCEPTION END art_quick_generic_jni_trampoline diff --git a/runtime/arch/mips64/registers_mips64.h b/runtime/arch/mips64/registers_mips64.h index 30de2cc009d721ffb8e4d7d7a62f36299b85ce66..d3a24b6202b0f6218f44288be1478bfd8bda2466 100644 --- a/runtime/arch/mips64/registers_mips64.h +++ b/runtime/arch/mips64/registers_mips64.h @@ -19,7 +19,8 @@ #include -#include "base/logging.h" +#include + #include "base/macros.h" #include "globals.h" @@ -101,7 +102,8 @@ enum FpuRegister { F29 = 29, F30 = 30, F31 = 31, - FTMP = F8, // scratch register + FTMP = F8, // scratch register + FTMP2 = F9, // scratch register (in addition to FTMP, reserved for MSA instructions) kNumberOfFpuRegisters = 32, kNoFpuRegister = -1, }; diff --git a/runtime/arch/mips64/thread_mips64.cc b/runtime/arch/mips64/thread_mips64.cc index 3ce5e50d5745c76e9094639670c3a06add214e2d..c1c390beeba84f1f075760395817c1f116d5aff0 100644 --- a/runtime/arch/mips64/thread_mips64.cc +++ b/runtime/arch/mips64/thread_mips64.cc @@ -16,9 +16,10 @@ #include "thread.h" +#include + #include "asm_support_mips64.h" #include "base/enums.h" -#include "base/logging.h" namespace art { diff --git a/runtime/arch/x86/context_x86.h b/runtime/arch/x86/context_x86.h index 303dfe361c403b615e92e6306df2da4a922ba74e..0ebb22bd6db3cd5f0767e1920284ddf3394b6723 100644 --- a/runtime/arch/x86/context_x86.h +++ b/runtime/arch/x86/context_x86.h @@ -17,8 +17,9 @@ #ifndef ART_RUNTIME_ARCH_X86_CONTEXT_X86_H_ #define ART_RUNTIME_ARCH_X86_CONTEXT_X86_H_ +#include + #include "arch/context.h" -#include "base/logging.h" #include "base/macros.h" #include "registers_x86.h" diff --git a/runtime/arch/x86/fault_handler_x86.cc b/runtime/arch/x86/fault_handler_x86.cc index 527332fe9a53c12153e3f821b602d132c14d6fe0..e6a91247cbef13a0c16681d770193e5e0d685011 100644 --- a/runtime/arch/x86/fault_handler_x86.cc +++ b/runtime/arch/x86/fault_handler_x86.cc @@ -21,7 +21,7 @@ #include "art_method.h" #include "base/enums.h" #include "base/hex_dump.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/macros.h" #include "base/safe_copy.h" #include "globals.h" diff --git a/runtime/arch/x86/instruction_set_features_x86.cc b/runtime/arch/x86/instruction_set_features_x86.cc index ea5a90d8ee0513e52c0d686d72f8ba92d6854113..98462512da326453984c1f99b06b703079cca052 100644 --- a/runtime/arch/x86/instruction_set_features_x86.cc +++ b/runtime/arch/x86/instruction_set_features_x86.cc @@ -19,11 +19,11 @@ #include #include -#include "android-base/stringprintf.h" -#include "android-base/strings.h" +#include +#include +#include #include "arch/x86_64/instruction_set_features_x86_64.h" -#include "base/logging.h" namespace art { diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 25716dc1bbb0ebcfdae15bebb277e0ed1ba8e479..a46ceeba12696de60ef9a992862211212b41c080 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -1969,7 +1969,9 @@ DEFINE_FUNCTION art_quick_generic_jni_trampoline punpckldq %xmm1, %xmm0 ret .Lexception_in_native: - movl %fs:THREAD_TOP_QUICK_FRAME_OFFSET, %esp + pushl %fs:THREAD_TOP_QUICK_FRAME_OFFSET + addl LITERAL(-1), (%esp) // Remove the GenericJNI tag. + movl (%esp), %esp // Do a call to push a new save-all frame required by the runtime. call .Lexception_call .Lexception_call: diff --git a/runtime/arch/x86/registers_x86.h b/runtime/arch/x86/registers_x86.h index 23027ed7d74ec7491c670517118d4278729a9d96..ded3520c76eea9f9c5cac8c7b08690f1a36483a2 100644 --- a/runtime/arch/x86/registers_x86.h +++ b/runtime/arch/x86/registers_x86.h @@ -19,7 +19,8 @@ #include -#include "base/logging.h" +#include + #include "base/macros.h" #include "globals.h" diff --git a/runtime/arch/x86_64/context_x86_64.h b/runtime/arch/x86_64/context_x86_64.h index f8e284598398c83a7b467833f2fc319f33879f93..d242693f81afde1db28a3b8511d0a993c3fd1f39 100644 --- a/runtime/arch/x86_64/context_x86_64.h +++ b/runtime/arch/x86_64/context_x86_64.h @@ -17,8 +17,9 @@ #ifndef ART_RUNTIME_ARCH_X86_64_CONTEXT_X86_64_H_ #define ART_RUNTIME_ARCH_X86_64_CONTEXT_X86_64_H_ +#include + #include "arch/context.h" -#include "base/logging.h" #include "base/macros.h" #include "registers_x86_64.h" diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index 2c3da90f25e946a3075d31ecc178208833dd43cf..463e5a279fb662b4bf2fce2c44da2dcefe7413dc 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -1958,7 +1958,9 @@ DEFINE_FUNCTION art_quick_generic_jni_trampoline movq %rax, %xmm0 ret .Lexception_in_native: - movq %gs:THREAD_TOP_QUICK_FRAME_OFFSET, %rsp + pushq %gs:THREAD_TOP_QUICK_FRAME_OFFSET + addq LITERAL(-1), (%rsp) // Remove the GenericJNI tag. + movq (%rsp), %rsp CFI_DEF_CFA_REGISTER(rsp) // Do a call to push a new save-all frame required by the runtime. call .Lexception_call diff --git a/runtime/arch/x86_64/registers_x86_64.h b/runtime/arch/x86_64/registers_x86_64.h index dda1d5f56906b9998c3b814985c87d835995b11a..4f2243170e75dd70e2716c6e9d860e626e38add4 100644 --- a/runtime/arch/x86_64/registers_x86_64.h +++ b/runtime/arch/x86_64/registers_x86_64.h @@ -19,7 +19,8 @@ #include -#include "base/logging.h" +#include + #include "base/macros.h" #include "globals.h" diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h index 4a328e8d60ec91cf811b0ee64c4bc4d6dcceef08..2b18577ed0836844e7b5c264bc09977c0df1d655 100644 --- a/runtime/art_field-inl.h +++ b/runtime/art_field-inl.h @@ -19,7 +19,8 @@ #include "art_field.h" -#include "base/logging.h" +#include + #include "class_linker.h" #include "dex_file-inl.h" #include "gc/accounting/card_table-inl.h" @@ -299,23 +300,17 @@ inline bool ArtField::IsPrimitiveType() REQUIRES_SHARED(Locks::mutator_lock_) { return GetTypeAsPrimitiveType() != Primitive::kPrimNot; } -inline ObjPtr ArtField::LookupType() { +inline ObjPtr ArtField::LookupResolvedType() { ScopedAssertNoThreadSuspension ants(__FUNCTION__); const uint32_t field_index = GetDexFieldIndex(); ObjPtr declaring_class = GetDeclaringClass(); if (UNLIKELY(declaring_class->IsProxyClass())) { return ProxyFindSystemClass(GetTypeDescriptor()); } - ObjPtr dex_cache = declaring_class->GetDexCache(); - const DexFile* const dex_file = dex_cache->GetDexFile(); - dex::TypeIndex type_idx = dex_file->GetFieldId(field_index).type_idx_; - ObjPtr type = dex_cache->GetResolvedType(type_idx); - if (UNLIKELY(type == nullptr)) { - type = Runtime::Current()->GetClassLinker()->LookupResolvedType( - *dex_file, type_idx, dex_cache, declaring_class->GetClassLoader()); - DCHECK(!Thread::Current()->IsExceptionPending()); - } - return type.Ptr(); + ObjPtr type = Runtime::Current()->GetClassLinker()->LookupResolvedType( + declaring_class->GetDexFile().GetFieldId(field_index).type_idx_, declaring_class); + DCHECK(!Thread::Current()->IsExceptionPending()); + return type; } inline ObjPtr ArtField::ResolveType() { @@ -324,15 +319,9 @@ inline ObjPtr ArtField::ResolveType() { if (UNLIKELY(declaring_class->IsProxyClass())) { return ProxyFindSystemClass(GetTypeDescriptor()); } - auto* dex_cache = declaring_class->GetDexCache(); - const DexFile* const dex_file = dex_cache->GetDexFile(); - dex::TypeIndex type_idx = dex_file->GetFieldId(field_index).type_idx_; - ObjPtr type = dex_cache->GetResolvedType(type_idx); - if (UNLIKELY(type == nullptr)) { - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - type = class_linker->ResolveType(*dex_file, type_idx, declaring_class); - DCHECK_EQ(type == nullptr, Thread::Current()->IsExceptionPending()); - } + ObjPtr type = Runtime::Current()->GetClassLinker()->ResolveType( + declaring_class->GetDexFile().GetFieldId(field_index).type_idx_, declaring_class); + DCHECK_EQ(type == nullptr, Thread::Current()->IsExceptionPending()); return type; } @@ -352,11 +341,10 @@ inline ObjPtr ArtField::GetStringName(Thread* self, bool resolve auto dex_field_index = GetDexFieldIndex(); CHECK_NE(dex_field_index, dex::kDexNoIndex); ObjPtr dex_cache = GetDexCache(); - const auto* dex_file = dex_cache->GetDexFile(); - const auto& field_id = dex_file->GetFieldId(dex_field_index); + const DexFile::FieldId& field_id = dex_cache->GetDexFile()->GetFieldId(dex_field_index); ObjPtr name = dex_cache->GetResolvedString(field_id.name_idx_); if (resolve && name == nullptr) { - name = ResolveGetStringName(self, *dex_file, field_id.name_idx_, dex_cache); + name = ResolveGetStringName(self, field_id.name_idx_, dex_cache); } return name; } diff --git a/runtime/art_field.cc b/runtime/art_field.cc index bc728f4476f5b65d7e83f95877698b14186f89a0..dbba2b0918734eab911a61a5482a77a777adc804 100644 --- a/runtime/art_field.cc +++ b/runtime/art_field.cc @@ -45,17 +45,17 @@ void ArtField::SetOffset(MemberOffset num_bytes) { ObjPtr ArtField::ProxyFindSystemClass(const char* descriptor) { DCHECK(GetDeclaringClass()->IsProxyClass()); - return Runtime::Current()->GetClassLinker()->FindSystemClass(Thread::Current(), descriptor); + ObjPtr klass = Runtime::Current()->GetClassLinker()->LookupClass( + Thread::Current(), descriptor, /* class_loader */ nullptr); + DCHECK(klass != nullptr); + return klass; } ObjPtr ArtField::ResolveGetStringName(Thread* self, - const DexFile& dex_file, dex::StringIndex string_idx, ObjPtr dex_cache) { StackHandleScope<1> hs(self); - return Runtime::Current()->GetClassLinker()->ResolveString(dex_file, - string_idx, - hs.NewHandle(dex_cache)); + return Runtime::Current()->GetClassLinker()->ResolveString(string_idx, hs.NewHandle(dex_cache)); } std::string ArtField::PrettyField(ArtField* f, bool with_type) { diff --git a/runtime/art_field.h b/runtime/art_field.h index 866bf0bc7007f2bc8064582f5be8ec258082b051..8d2f9ff71bcb62a62fe3d1b0a5b21455b09957c7 100644 --- a/runtime/art_field.h +++ b/runtime/art_field.h @@ -205,7 +205,7 @@ class ArtField FINAL { bool IsPrimitiveType() REQUIRES_SHARED(Locks::mutator_lock_); - ObjPtr LookupType() REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr LookupResolvedType() REQUIRES_SHARED(Locks::mutator_lock_); ObjPtr ResolveType() REQUIRES_SHARED(Locks::mutator_lock_); size_t FieldSize() REQUIRES_SHARED(Locks::mutator_lock_); @@ -234,7 +234,6 @@ class ArtField FINAL { ObjPtr ProxyFindSystemClass(const char* descriptor) REQUIRES_SHARED(Locks::mutator_lock_); ObjPtr ResolveGetStringName(Thread* self, - const DexFile& dex_file, dex::StringIndex string_idx, ObjPtr dex_cache) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 12b4d16b37f0bcfd251e7e8d4b7393ef18d423c3..c6c4f4ae4118c9f5e6b002e6636d40f2260bf594 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -21,8 +21,8 @@ #include "art_field.h" #include "base/callee_save_type.h" -#include "base/logging.h" #include "class_linker-inl.h" +#include "code_item_accessors-inl.h" #include "common_throws.h" #include "dex_file-inl.h" #include "dex_file_annotations.h" @@ -94,33 +94,28 @@ inline uint16_t ArtMethod::GetMethodIndexDuringLinking() { return method_index_; } +template inline uint32_t ArtMethod::GetDexMethodIndex() { if (kCheckDeclaringClassState) { - CHECK(IsRuntimeMethod() || GetDeclaringClass()->IsIdxLoaded() || - GetDeclaringClass()->IsErroneous()); + CHECK(IsRuntimeMethod() || + GetDeclaringClass()->IsIdxLoaded() || + GetDeclaringClass()->IsErroneous()); } return GetDexMethodIndexUnchecked(); } inline ObjPtr ArtMethod::LookupResolvedClassFromTypeIndex(dex::TypeIndex type_idx) { ScopedAssertNoThreadSuspension ants(__FUNCTION__); - ObjPtr dex_cache = GetDexCache(); - ObjPtr type = dex_cache->GetResolvedType(type_idx); - if (UNLIKELY(type == nullptr)) { - type = Runtime::Current()->GetClassLinker()->LookupResolvedType( - *dex_cache->GetDexFile(), type_idx, dex_cache, GetClassLoader()); - } - return type.Ptr(); + ObjPtr type = + Runtime::Current()->GetClassLinker()->LookupResolvedType(type_idx, this); + DCHECK(!Thread::Current()->IsExceptionPending()); + return type; } inline ObjPtr ArtMethod::ResolveClassFromTypeIndex(dex::TypeIndex type_idx) { - ObjPtr dex_cache = GetDexCache(); - ObjPtr type = dex_cache->GetResolvedType(type_idx); - if (UNLIKELY(type == nullptr)) { - type = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, this); - CHECK(type != nullptr || Thread::Current()->IsExceptionPending()); - } - return type.Ptr(); + ObjPtr type = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, this); + DCHECK_EQ(type == nullptr, Thread::Current()->IsExceptionPending()); + return type; } inline bool ArtMethod::CheckIncompatibleClassChange(InvokeType type) { @@ -201,7 +196,14 @@ inline const char* ArtMethod::GetShorty() { inline const char* ArtMethod::GetShorty(uint32_t* out_length) { DCHECK(!IsProxyMethod()); const DexFile* dex_file = GetDexFile(); - return dex_file->GetMethodShorty(dex_file->GetMethodId(GetDexMethodIndex()), out_length); + // Don't do a read barrier in the DCHECK() inside GetDexMethodIndex() as GetShorty() + // can be called when the declaring class is about to be unloaded and cannot be added + // to the mark stack (subsequent GC assertion would fail). + // It is safe to avoid the read barrier as the ArtMethod is constructed with a declaring + // Class already satisfying the DCHECK() inside GetDexMethodIndex(), so even if that copy + // of declaring class becomes a from-space object, it shall satisfy the DCHECK(). + return dex_file->GetMethodShorty(dex_file->GetMethodId(GetDexMethodIndex()), + out_length); } inline const Signature ArtMethod::GetSignature() { @@ -296,9 +298,7 @@ inline const DexFile::ClassDef& ArtMethod::GetClassDef() { inline const char* ArtMethod::GetReturnTypeDescriptor() { DCHECK(!IsProxyMethod()); const DexFile* dex_file = GetDexFile(); - const DexFile::MethodId& method_id = dex_file->GetMethodId(GetDexMethodIndex()); - const DexFile::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id); - return dex_file->GetTypeDescriptor(dex_file->GetTypeId(proto_id.return_type_idx_)); + return dex_file->GetTypeDescriptor(dex_file->GetTypeId(GetReturnTypeIndex())); } inline Primitive::Type ArtMethod::GetReturnTypePrimitive() { @@ -318,7 +318,7 @@ inline mirror::ClassLoader* ArtMethod::GetClassLoader() { template inline mirror::DexCache* ArtMethod::GetDexCache() { - if (LIKELY(!IsObsolete())) { + if (LIKELY(!IsObsolete())) { mirror::Class* klass = GetDeclaringClass(); return klass->GetDexCache(); } else { @@ -392,6 +392,7 @@ inline void ArtMethod::SetIntrinsic(uint32_t intrinsic) { bool is_synchronized = IsSynchronized(); bool skip_access_checks = SkipAccessChecks(); bool is_fast_native = IsFastNative(); + bool is_critical_native = IsCriticalNative(); bool is_copied = IsCopied(); bool is_miranda = IsMiranda(); bool is_default = IsDefault(); @@ -404,6 +405,7 @@ inline void ArtMethod::SetIntrinsic(uint32_t intrinsic) { DCHECK_EQ(is_synchronized, IsSynchronized()); DCHECK_EQ(skip_access_checks, SkipAccessChecks()); DCHECK_EQ(is_fast_native, IsFastNative()); + DCHECK_EQ(is_critical_native, IsCriticalNative()); DCHECK_EQ(is_copied, IsCopied()); DCHECK_EQ(is_miranda, IsMiranda()); DCHECK_EQ(is_default, IsDefault()); @@ -457,6 +459,10 @@ inline void ArtMethod::UpdateEntrypoints(const Visitor& visitor, PointerSize poi } } +inline CodeItemInstructionAccessor ArtMethod::DexInstructions() { + return CodeItemInstructionAccessor(this); +} + } // namespace art #endif // ART_RUNTIME_ART_METHOD_INL_H_ diff --git a/runtime/art_method.cc b/runtime/art_method.cc index b5e0f66575f6c9259b78f73cc17e3c7e6d625167..7ddaa7edc3c674abc6330afee8d0f5a86c40f1d8 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -26,7 +26,6 @@ #include "class_linker-inl.h" #include "debugger.h" #include "dex_file-inl.h" -#include "dex_file_annotations.h" #include "dex_instruction.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "gc/accounting/card_table-inl.h" @@ -135,15 +134,14 @@ uint16_t ArtMethod::FindObsoleteDexClassDefIndex() { return dex_file->GetIndexForClassDef(*class_def); } -mirror::String* ArtMethod::GetNameAsString(Thread* self) { +ObjPtr ArtMethod::GetNameAsString(Thread* self) { CHECK(!IsProxyMethod()); StackHandleScope<1> hs(self); Handle dex_cache(hs.NewHandle(GetDexCache())); auto* dex_file = dex_cache->GetDexFile(); uint32_t dex_method_idx = GetDexMethodIndex(); const DexFile::MethodId& method_id = dex_file->GetMethodId(dex_method_idx); - return Runtime::Current()->GetClassLinker()->ResolveString(*dex_file, method_id.name_idx_, - dex_cache); + return Runtime::Current()->GetClassLinker()->ResolveString(method_id.name_idx_, dex_cache); } void ArtMethod::ThrowInvocationTimeError() { @@ -299,9 +297,8 @@ uint32_t ArtMethod::FindCatchBlock(Handle exception_type, } } if (found_dex_pc != dex::kDexNoIndex) { - const Instruction* first_catch_instr = - Instruction::At(&code_item->insns_[found_dex_pc]); - *has_no_move_exception = (first_catch_instr->Opcode() != Instruction::MOVE_EXCEPTION); + const Instruction& first_catch_instr = DexInstructions().InstructionAt(found_dex_pc); + *has_no_move_exception = (first_catch_instr.Opcode() != Instruction::MOVE_EXCEPTION); } // Put the exception back. if (exception != nullptr) { @@ -392,13 +389,9 @@ void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* self->PopManagedStackFragment(fragment); } -const void* ArtMethod::RegisterNative(const void* native_method, bool is_fast) { +const void* ArtMethod::RegisterNative(const void* native_method) { CHECK(IsNative()) << PrettyMethod(); - CHECK(!IsFastNative()) << PrettyMethod(); CHECK(native_method != nullptr) << PrettyMethod(); - if (is_fast) { - AddAccessFlags(kAccFastNative); - } void* new_native_method = nullptr; Runtime::Current()->GetRuntimeCallbacks()->RegisterNativeMethod(this, native_method, @@ -408,7 +401,7 @@ const void* ArtMethod::RegisterNative(const void* native_method, bool is_fast) { } void ArtMethod::UnregisterNative() { - CHECK(IsNative() && !IsFastNative()) << PrettyMethod(); + CHECK(IsNative()) << PrettyMethod(); // restore stub to lookup native pointer via dlsym SetEntryPointFromJni(GetJniDlsymLookupStub()); } @@ -428,33 +421,6 @@ bool ArtMethod::IsPolymorphicSignature() { cls == WellKnownClasses::ToClass(WellKnownClasses::java_lang_invoke_VarHandle)); } -bool ArtMethod::IsAnnotatedWithFastNative() { - return IsAnnotatedWith(WellKnownClasses::dalvik_annotation_optimization_FastNative, - DexFile::kDexVisibilityBuild, - /* lookup_in_resolved_boot_classes */ true); -} - -bool ArtMethod::IsAnnotatedWithCriticalNative() { - return IsAnnotatedWith(WellKnownClasses::dalvik_annotation_optimization_CriticalNative, - DexFile::kDexVisibilityBuild, - /* lookup_in_resolved_boot_classes */ true); -} - -bool ArtMethod::IsAnnotatedWith(jclass klass, - uint32_t visibility, - bool lookup_in_resolved_boot_classes) { - Thread* self = Thread::Current(); - ScopedObjectAccess soa(self); - StackHandleScope<1> shs(self); - - ObjPtr annotation = soa.Decode(klass); - DCHECK(annotation->IsAnnotation()); - Handle annotation_handle(shs.NewHandle(annotation)); - - return annotations::IsMethodAnnotationPresent( - this, annotation_handle, visibility, lookup_in_resolved_boot_classes); -} - static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file, uint16_t class_def_idx, uint32_t method_idx) { @@ -582,8 +548,8 @@ bool ArtMethod::EqualParameters(Handle> param } auto* cl = Runtime::Current()->GetClassLinker(); for (size_t i = 0; i < count; ++i) { - auto type_idx = proto_params->GetTypeItem(i).type_idx_; - auto* type = cl->ResolveType(type_idx, this); + dex::TypeIndex type_idx = proto_params->GetTypeItem(i).type_idx_; + ObjPtr type = cl->ResolveType(type_idx, this); if (type == nullptr) { Thread::Current()->AssertPendingException(); return false; @@ -595,23 +561,14 @@ bool ArtMethod::EqualParameters(Handle> param return true; } -const uint8_t* ArtMethod::GetQuickenedInfo(PointerSize pointer_size) { - if (kIsVdexEnabled) { - const DexFile& dex_file = GetDeclaringClass()->GetDexFile(); - const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); - if (oat_dex_file == nullptr || (oat_dex_file->GetOatFile() == nullptr)) { - return nullptr; - } - return oat_dex_file->GetOatFile()->GetVdexFile()->GetQuickenedInfoOf( - dex_file, GetCodeItemOffset()); - } else { - bool found = false; - OatFile::OatMethod oat_method = FindOatMethodFor(this, pointer_size, &found); - if (!found || (oat_method.GetQuickCode() != nullptr)) { - return nullptr; - } - return oat_method.GetVmapTable(); +const uint8_t* ArtMethod::GetQuickenedInfo() { + const DexFile& dex_file = GetDeclaringClass()->GetDexFile(); + const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); + if (oat_dex_file == nullptr || (oat_dex_file->GetOatFile() == nullptr)) { + return nullptr; } + return oat_dex_file->GetOatFile()->GetVdexFile()->GetQuickenedInfoOf( + dex_file, GetCodeItemOffset()); } const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { @@ -628,11 +585,6 @@ const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { CHECK(existing_entry_point != nullptr) << PrettyMethod() << "@" << this; ClassLinker* class_linker = runtime->GetClassLinker(); - if (class_linker->IsQuickGenericJniStub(existing_entry_point)) { - // The generic JNI does not have any method header. - return nullptr; - } - if (existing_entry_point == GetQuickProxyInvokeHandler()) { DCHECK(IsProxyMethod() && !IsConstructor()); // The proxy entry point does not have any method header. @@ -640,7 +592,8 @@ const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { } // Check whether the current entry point contains this pc. - if (!class_linker->IsQuickResolutionStub(existing_entry_point) && + if (!class_linker->IsQuickGenericJniStub(existing_entry_point) && + !class_linker->IsQuickResolutionStub(existing_entry_point) && !class_linker->IsQuickToInterpreterBridge(existing_entry_point)) { OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromEntryPoint(existing_entry_point); @@ -673,19 +626,13 @@ const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { OatFile::OatMethod oat_method = FindOatMethodFor(this, class_linker->GetImagePointerSize(), &found); if (!found) { - if (class_linker->IsQuickResolutionStub(existing_entry_point)) { - // We are running the generic jni stub, but the entry point of the method has not - // been updated yet. - DCHECK_EQ(pc, 0u) << "Should be a downcall"; - DCHECK(IsNative()); - return nullptr; - } - if (existing_entry_point == GetQuickInstrumentationEntryPoint()) { - // We are running the generic jni stub, but the method is being instrumented. - // NB We would normally expect the pc to be zero but we can have non-zero pc's if - // instrumentation is installed or removed during the call which is using the generic jni - // trampoline. - DCHECK(IsNative()); + if (IsNative()) { + // We are running the GenericJNI stub. The entrypoint may point + // to different entrypoints or to a JIT-compiled JNI stub. + DCHECK(class_linker->IsQuickGenericJniStub(existing_entry_point) || + class_linker->IsQuickResolutionStub(existing_entry_point) || + existing_entry_point == GetQuickInstrumentationEntryPoint() || + (jit != nullptr && jit->GetCodeCache()->ContainsPc(existing_entry_point))); return nullptr; } // Only for unit tests. @@ -743,13 +690,15 @@ void ArtMethod::CopyFrom(ArtMethod* src, PointerSize image_pointer_size) { declaring_class_ = GcRoot(const_cast(src)->GetDeclaringClass()); // If the entry point of the method we are copying from is from JIT code, we just - // put the entry point of the new method to interpreter. We could set the entry point - // to the JIT code, but this would require taking the JIT code cache lock to notify - // it, which we do not want at this level. + // put the entry point of the new method to interpreter or GenericJNI. We could set + // the entry point to the JIT code, but this would require taking the JIT code cache + // lock to notify it, which we do not want at this level. Runtime* runtime = Runtime::Current(); if (runtime->UseJitCompilation()) { if (runtime->GetJit()->GetCodeCache()->ContainsPc(GetEntryPointFromQuickCompiledCode())) { - SetEntryPointFromQuickCompiledCodePtrSize(GetQuickToInterpreterBridge(), image_pointer_size); + SetEntryPointFromQuickCompiledCodePtrSize( + src->IsNative() ? GetQuickGenericJniStub() : GetQuickToInterpreterBridge(), + image_pointer_size); } } // Clear the profiling info for the same reasons as the JIT code. diff --git a/runtime/art_method.h b/runtime/art_method.h index ca2e34e071539d396dec378bd2635c36820b80e1..ab90a10f5a80da9e5916a6f4c96f38e83a59a0fc 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -19,11 +19,17 @@ #include +#include + #include "base/bit_utils.h" #include "base/casts.h" #include "base/enums.h" -#include "base/logging.h" +#include "base/iteration_range.h" +#include "base/macros.h" +#include "base/runtime_debug.h" +#include "code_item_accessors.h" #include "dex_file.h" +#include "dex_instruction_iterator.h" #include "gc_root.h" #include "modifiers.h" #include "obj_ptr.h" @@ -200,9 +206,9 @@ class ArtMethod FINAL { } bool IsMiranda() { - static_assert((kAccMiranda & (kAccIntrinsic | kAccIntrinsicBits)) == 0, - "kAccMiranda conflicts with intrinsic modifier"); - return (GetAccessFlags() & kAccMiranda) != 0; + // The kAccMiranda flag value is used with a different meaning for native methods, + // so we need to check the kAccNative flag as well. + return (GetAccessFlags() & (kAccNative | kAccMiranda)) == kAccMiranda; } // Returns true if invoking this method will not throw an AbstractMethodError or @@ -213,6 +219,7 @@ class ArtMethod FINAL { bool IsCompilable() { if (IsIntrinsic()) { + // kAccCompileDontBother overlaps with kAccIntrinsicBits. return true; } return (GetAccessFlags() & kAccCompileDontBother) == 0; @@ -239,8 +246,9 @@ class ArtMethod FINAL { return (GetAccessFlags() & kAccDefault) != 0; } + template bool IsObsolete() { - return (GetAccessFlags() & kAccObsoleteMethod) != 0; + return (GetAccessFlags() & kAccObsoleteMethod) != 0; } void SetIsObsolete() { @@ -252,11 +260,24 @@ class ArtMethod FINAL { return (GetAccessFlags() & kAccNative) != 0; } + // Checks to see if the method was annotated with @dalvik.annotation.optimization.FastNative. bool IsFastNative() { + // The presence of the annotation is checked by ClassLinker and recorded in access flags. + // The kAccFastNative flag value is used with a different meaning for non-native methods, + // so we need to check the kAccNative flag as well. constexpr uint32_t mask = kAccFastNative | kAccNative; return (GetAccessFlags() & mask) == mask; } + // Checks to see if the method was annotated with @dalvik.annotation.optimization.CriticalNative. + bool IsCriticalNative() { + // The presence of the annotation is checked by ClassLinker and recorded in access flags. + // The kAccCriticalNative flag value is used with a different meaning for non-native methods, + // so we need to check the kAccNative flag as well. + constexpr uint32_t mask = kAccCriticalNative | kAccNative; + return (GetAccessFlags() & mask) == mask; + } + bool IsAbstract() { return (GetAccessFlags() & kAccAbstract) != 0; } @@ -274,10 +295,14 @@ class ArtMethod FINAL { bool IsPolymorphicSignature() REQUIRES_SHARED(Locks::mutator_lock_); bool SkipAccessChecks() { - return (GetAccessFlags() & kAccSkipAccessChecks) != 0; + // The kAccSkipAccessChecks flag value is used with a different meaning for native methods, + // so we need to check the kAccNative flag as well. + return (GetAccessFlags() & (kAccSkipAccessChecks | kAccNative)) == kAccSkipAccessChecks; } void SetSkipAccessChecks() { + // SkipAccessChecks() is applicable only to non-native methods. + DCHECK(!IsNative()); AddAccessFlags(kAccSkipAccessChecks); } @@ -310,14 +335,6 @@ class ArtMethod FINAL { AddAccessFlags(kAccMustCountLocks); } - // Checks to see if the method was annotated with @dalvik.annotation.optimization.FastNative - // -- Independent of kAccFastNative access flags. - bool IsAnnotatedWithFastNative(); - - // Checks to see if the method was annotated with @dalvik.annotation.optimization.CriticalNative - // -- Unrelated to the GC notion of "critical". - bool IsAnnotatedWithCriticalNative(); - // Returns true if this method could be overridden by a default method. bool IsOverridableByDefaultMethod() REQUIRES_SHARED(Locks::mutator_lock_); @@ -364,6 +381,7 @@ class ArtMethod FINAL { ALWAYS_INLINE uint32_t GetDexMethodIndexUnchecked() { return dex_method_index_; } + template ALWAYS_INLINE uint32_t GetDexMethodIndex() REQUIRES_SHARED(Locks::mutator_lock_); void SetDexMethodIndex(uint32_t new_idx) { @@ -417,7 +435,7 @@ class ArtMethod FINAL { // Registers the native method and returns the new entry point. NB The returned entry point might // be different from the native_method argument if some MethodCallback modifies it. - const void* RegisterNative(const void* native_method, bool is_fast) + const void* RegisterNative(const void* native_method) REQUIRES_SHARED(Locks::mutator_lock_) WARN_UNUSED; void UnregisterNative() REQUIRES_SHARED(Locks::mutator_lock_); @@ -448,12 +466,11 @@ class ArtMethod FINAL { } ProfilingInfo* GetProfilingInfo(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_) { - // Don't do a read barrier in the DCHECK, as GetProfilingInfo is called in places - // where the declaring class is treated as a weak reference (accessing it with - // a read barrier would either prevent unloading the class, or crash the runtime if - // the GC wants to unload it). - DCHECK(!IsNative()); - if (UNLIKELY(IsProxyMethod())) { + // Don't do a read barrier in the DCHECK() inside GetAccessFlags() called by IsNative(), + // as GetProfilingInfo is called in places where the declaring class is treated as a weak + // reference (accessing it with a read barrier would either prevent unloading the class, + // or crash the runtime if the GC wants to unload it). + if (UNLIKELY(IsNative()) || UNLIKELY(IsProxyMethod())) { return nullptr; } return reinterpret_cast(GetDataPtrSize(pointer_size)); @@ -562,7 +579,7 @@ class ArtMethod FINAL { ALWAYS_INLINE const char* GetName() REQUIRES_SHARED(Locks::mutator_lock_); - mirror::String* GetNameAsString(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); + ObjPtr GetNameAsString(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); const DexFile::CodeItem* GetCodeItem() REQUIRES_SHARED(Locks::mutator_lock_); @@ -645,7 +662,7 @@ class ArtMethod FINAL { return hotness_count_; } - const uint8_t* GetQuickenedInfo(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); + const uint8_t* GetQuickenedInfo() REQUIRES_SHARED(Locks::mutator_lock_); // Returns the method header for the compiled code containing 'pc'. Note that runtime // methods will return null for this method, as they are not oat based. @@ -700,6 +717,11 @@ class ArtMethod FINAL { "ptr_sized_fields_.entry_point_from_quick_compiled_code_"); } + // Returns the dex instructions of the code item for the art method. Returns an empty array for + // the null code item case. + ALWAYS_INLINE CodeItemInstructionAccessor DexInstructions() + REQUIRES_SHARED(Locks::mutator_lock_); + protected: // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses". // The class we are a part of. @@ -751,11 +773,6 @@ class ArtMethod FINAL { private: uint16_t FindObsoleteDexClassDefIndex() REQUIRES_SHARED(Locks::mutator_lock_); - // If `lookup_in_resolved_boot_classes` is true, look up any of the - // method's annotations' classes in the bootstrap class loader's - // resolved types; otherwise, resolve them as a side effect. - bool IsAnnotatedWith(jclass klass, uint32_t visibility, bool lookup_in_resolved_boot_classes); - static constexpr size_t PtrSizedFieldsOffset(PointerSize pointer_size) { // Round up to pointer size for padding field. Tested in art_method.cc. return RoundUp(offsetof(ArtMethod, hotness_count_) + sizeof(hotness_count_), diff --git a/runtime/atomic.h b/runtime/atomic.h index d8621cc2e63dca17e70e72cd1914d2961c0b93e3..ec3eb6d6090a30296dd798a9efeeb64634e21796 100644 --- a/runtime/atomic.h +++ b/runtime/atomic.h @@ -22,8 +22,9 @@ #include #include +#include + #include "arch/instruction_set.h" -#include "base/logging.h" #include "base/macros.h" namespace art { diff --git a/runtime/barrier.cc b/runtime/barrier.cc index 9bcda35a9dc91ca924a43340b39ff1c13aa8a876..4329a5a2450cd4ae557e4e392c887d3e75d4afdb 100644 --- a/runtime/barrier.cc +++ b/runtime/barrier.cc @@ -16,7 +16,9 @@ #include "barrier.h" -#include "base/logging.h" +#include + +#include "base/aborting.h" #include "base/mutex.h" #include "base/time_utils.h" #include "thread.h" diff --git a/runtime/base/aborting.h b/runtime/base/aborting.h new file mode 100644 index 0000000000000000000000000000000000000000..8906c96ea7227ade5f70764d0c10cb17f60e39e4 --- /dev/null +++ b/runtime/base/aborting.h @@ -0,0 +1,31 @@ +/* + * 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 + * limitations under the License. + */ + +#ifndef ART_RUNTIME_BASE_ABORTING_H_ +#define ART_RUNTIME_BASE_ABORTING_H_ + +#include + +namespace art { + +// 0 if not abort, non-zero if an abort is in progress. Used on fatal exit to prevents recursive +// aborts. Global declaration allows us to disable some error checking to ensure fatal shutdown +// makes forward progress. +extern std::atomic gAborting; + +} // namespace art + +#endif // ART_RUNTIME_BASE_ABORTING_H_ diff --git a/runtime/base/allocator.cc b/runtime/base/allocator.cc index bb006389fae1081730942ecdfdfc141feb9c88fb..2da88c38302e30f49b3bde51a90a0544558a790f 100644 --- a/runtime/base/allocator.cc +++ b/runtime/base/allocator.cc @@ -19,8 +19,9 @@ #include #include +#include + #include "atomic.h" -#include "base/logging.h" namespace art { diff --git a/runtime/base/allocator.h b/runtime/base/allocator.h index fba9308e8e3b13cdaccc19ef4be06fea6de89e4b..99cdb4998464f417e213eb9c8fce9e3108af12ba 100644 --- a/runtime/base/allocator.h +++ b/runtime/base/allocator.h @@ -111,7 +111,7 @@ class TrackingAllocatorImpl : public std::allocator { // Used internally by STL data structures. template - TrackingAllocatorImpl( // NOLINT, implicit + TrackingAllocatorImpl( const TrackingAllocatorImpl& alloc ATTRIBUTE_UNUSED) noexcept {} // Used internally by STL data structures. diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc index 2e35f8ac4f777fc0f31ec1e38be3735a3d613c74..cc413c5ab9bcea120ec02464acd7759d52846421 100644 --- a/runtime/base/arena_allocator.cc +++ b/runtime/base/arena_allocator.cc @@ -23,7 +23,8 @@ #include #include -#include "logging.h" +#include + #include "mem_map.h" #include "mutex.h" #include "systrace.h" diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h index a327cb0a8b1ea660401b82bbc977cd2be5f179aa..9e03658aef80939412ed286e0cd2131abf66e53d 100644 --- a/runtime/base/arena_allocator.h +++ b/runtime/base/arena_allocator.h @@ -20,11 +20,11 @@ #include #include -#include "base/bit_utils.h" -#include "base/dchecked_vector.h" -#include "base/memory_tool.h" +#include "bit_utils.h" +#include "dchecked_vector.h" #include "debug_stack.h" #include "macros.h" +#include "memory_tool.h" #include "mutex.h" namespace art { diff --git a/runtime/base/arena_containers.h b/runtime/base/arena_containers.h index 2e71156ee8c47a5baa2b0295e2b26afb830a06ee..dcdb92b9d89aa654da63297a54614737a5deeab8 100644 --- a/runtime/base/arena_containers.h +++ b/runtime/base/arena_containers.h @@ -143,7 +143,7 @@ class ArenaAllocatorAdapter : private ArenaAllocatorAdapterKind { allocator_(allocator) { } template - ArenaAllocatorAdapter(const ArenaAllocatorAdapter& other) // NOLINT, implicit + ArenaAllocatorAdapter(const ArenaAllocatorAdapter& other) : ArenaAllocatorAdapterKind(other), allocator_(other.allocator_) { } @@ -179,7 +179,7 @@ class ArenaAllocatorAdapter : private ArenaAllocatorAdapterKind { allocator_(allocator) { } template - ArenaAllocatorAdapter(const ArenaAllocatorAdapter& other) // NOLINT, implicit + ArenaAllocatorAdapter(const ArenaAllocatorAdapter& other) : ArenaAllocatorAdapterKind(other), allocator_(other.allocator_) { } diff --git a/runtime/base/arena_object.h b/runtime/base/arena_object.h index ed00babd62acb27221d84a824212f77f9068f695..06884c23d4d3323315eee7a149c06ddbb6dfb715 100644 --- a/runtime/base/arena_object.h +++ b/runtime/base/arena_object.h @@ -17,8 +17,10 @@ #ifndef ART_RUNTIME_BASE_ARENA_OBJECT_H_ #define ART_RUNTIME_BASE_ARENA_OBJECT_H_ -#include "base/arena_allocator.h" -#include "base/logging.h" +#include + +#include "arena_allocator.h" +#include "macros.h" #include "scoped_arena_allocator.h" namespace art { diff --git a/runtime/base/array_ref.h b/runtime/base/array_ref.h index 630a036f3d142edf142af0077d1099787cf199a6..ef86512cf79e96426d9f5226e93b2722b2ad874d 100644 --- a/runtime/base/array_ref.h +++ b/runtime/base/array_ref.h @@ -20,7 +20,7 @@ #include #include -#include "base/logging.h" +#include namespace art { diff --git a/runtime/base/bit_field.h b/runtime/base/bit_field.h index a80ca28d2ec266ebe72f0820aa731357ee03d238..86007d6a3514458bdc0f57a1579b254b614ba061 100644 --- a/runtime/base/bit_field.h +++ b/runtime/base/bit_field.h @@ -17,8 +17,9 @@ #ifndef ART_RUNTIME_BASE_BIT_FIELD_H_ #define ART_RUNTIME_BASE_BIT_FIELD_H_ +#include + #include "globals.h" -#include "logging.h" namespace art { diff --git a/runtime/base/bit_string.h b/runtime/base/bit_string.h index 1cda0210172334c47638fb61e08b8aa5ecbc19e0..bfbe8eaf7198f90f945615ed9a121d86663f5c98 100644 --- a/runtime/base/bit_string.h +++ b/runtime/base/bit_string.h @@ -114,7 +114,7 @@ inline std::ostream& operator<<(std::ostream& os, const BitStringChar& bc) { /** * BitString * - * MSB LSB + * lsb (least significant bit) msb * +------------+------------+------------+-----+------------+ * | | | | | | * | Char0 | Char1 | Char2 | ... | CharN | @@ -131,8 +131,9 @@ inline std::ostream& operator<<(std::ostream& os, const BitStringChar& bc) { * "ABCDE...K" := [A,B,C,D,E, ... K] + [0]*(N-idx(K)) s.t. N >= K. * // Padded with trailing 0s to fit (N+1) bitstring chars. * MaxBitstringLen := N+1 - * StrLen(Bitstring) := MaxBitStringLen - | forall char in CharI..CharN : char == 0 AND Char(I-1) != 0 | - * // Maximum length - the # of consecutive trailing zeroes. + * StrLen(Bitstring) := I s.t. (I == 0 OR Char(I-1) != 0) + * AND forall char in CharI..CharN : char == 0 + * // = Maximum length - the # of consecutive trailing zeroes. * Bitstring[N] := CharN * Bitstring[I..N) := [CharI, CharI+1, ... CharN-1] * @@ -248,7 +249,7 @@ struct BitString { // Does this bitstring contain exactly 0 characters? bool IsEmpty() const { - return (*this) == BitString{}; // NOLINT + return (*this) == BitString{}; } // Remove all BitStringChars starting at end. @@ -278,8 +279,8 @@ struct BitString { private: friend std::ostream& operator<<(std::ostream& os, const BitString& bit_string); - // Data is stored with the "highest" position in the least-significant-bit. - // As positions approach 0, the bits are stored with increasing significance. + // Data is stored with the first character in the least-significant-bit. + // Unused bits are zero. StorageType storage_; }; diff --git a/runtime/base/bit_string_test.cc b/runtime/base/bit_string_test.cc index d5610e7a73d5969493432e37e65d3a3d7af16378..96aa154ef33f8e20d9dcbbcf5b50febc5957cf54 100644 --- a/runtime/base/bit_string_test.cc +++ b/runtime/base/bit_string_test.cc @@ -47,7 +47,7 @@ BitStringChar MakeBitStringChar(size_t val) { BitString MakeBitString(std::initializer_list values = {}) { CHECK_GE(BitString::kCapacity, values.size()); - BitString bs{}; // NOLINT + BitString bs{}; size_t i = 0; for (size_t val : values) { @@ -68,7 +68,7 @@ size_t AsUint(const T& value) { // Make max bitstring, e.g. BitString[4095,7,255] for {12,3,8} template BitString MakeBitStringMax() { - BitString bs{}; // NOLINT + BitString bs{}; for (size_t i = 0; i < kCount; ++i) { bs.SetAt(i, diff --git a/runtime/base/bit_struct.h b/runtime/base/bit_struct.h index 16b555e1c6c6910858600ad09f5ef1fd535f1d7a..b20745941930630a05af618ddcdfa95358d895e9 100644 --- a/runtime/base/bit_struct.h +++ b/runtime/base/bit_struct.h @@ -288,7 +288,7 @@ using BitStructUint = // // See top of file for usage example. #define BITSTRUCT_DEFINE_END(name) \ - }; /* NOLINT [readability/braces] [4] */ \ + }; \ static_assert(art::detail::ValidateBitStructSize(), \ #name "bitsize incorrect: " \ "did you insert extra fields that weren't BitStructX, " \ diff --git a/runtime/base/bit_struct_detail.h b/runtime/base/bit_struct_detail.h index 49d432e69c1c2c246013f041c58fedc8e9a6ab78..912f51c7b098fcfb5007c430eff37fa6cc026f3f 100644 --- a/runtime/base/bit_struct_detail.h +++ b/runtime/base/bit_struct_detail.h @@ -79,7 +79,7 @@ struct HasUnderscoreField { using FalseT = std::integral_constant::type; template - static constexpr auto Test(void*) -> decltype(std::declval()._, TrueT{}); // NOLINT + static constexpr auto Test(void*) -> decltype(std::declval()._, TrueT{}); template static constexpr FalseT Test(...); diff --git a/runtime/base/bit_struct_test.cc b/runtime/base/bit_struct_test.cc index a80d39eb91cd62a92eecc854aa4dea8ab6fc48cf..577682ccceaaabbe58fbcc460767d0b55fb1919e 100644 --- a/runtime/base/bit_struct_test.cc +++ b/runtime/base/bit_struct_test.cc @@ -73,7 +73,7 @@ struct CustomBitStruct { TEST(BitStructs, Custom) { CustomBitStruct expected(0b1111); - BitStructField f{}; // NOLINT + BitStructField f{}; EXPECT_EQ(1u, sizeof(f)); @@ -95,7 +95,7 @@ TEST(BitStructs, TwoCustom) { VALIDATE_BITSTRUCT_SIZE(TestTwoCustom); - TestTwoCustom cst{}; // NOLINT + TestTwoCustom cst{}; // Test the write to most-significant field doesn't clobber least-significant. cst.f4_a = CustomBitStruct(0b0110); @@ -122,7 +122,7 @@ TEST(BitStructs, TwoCustom) { } TEST(BitStructs, Number) { - BitStructNumber bsn{}; // NOLINT + BitStructNumber bsn{}; EXPECT_EQ(2u, sizeof(bsn)); bsn = 0b1111; @@ -154,7 +154,7 @@ TEST(BitStructs, Test1) { EXPECT_EQ(1u, sizeof(u4)); EXPECT_EQ(1u, sizeof(alias_all)); } - TestBitStruct tst{}; // NOLINT + TestBitStruct tst{}; // Check minimal size selection is correct. EXPECT_EQ(1u, sizeof(TestBitStruct)); @@ -229,7 +229,7 @@ BITSTRUCT_DEFINE_END(MixedSizeBitStruct); TEST(BitStructs, Mixed) { EXPECT_EQ(4u, sizeof(MixedSizeBitStruct)); - MixedSizeBitStruct tst{}; // NOLINT + MixedSizeBitStruct tst{}; // Check operator assignment. tst.u3 = 0b111u; @@ -263,11 +263,11 @@ BITSTRUCT_DEFINE_START(TestBitStruct_u8, /* size */ 8) BITSTRUCT_DEFINE_END(TestBitStruct_u8); TEST(BitStructs, FieldAssignment) { - TestBitStruct_u8 all_1s{}; // NOLINT + TestBitStruct_u8 all_1s{}; all_1s.alias_all = 0xffu; { - TestBitStruct_u8 tst{}; // NOLINT + TestBitStruct_u8 tst{}; tst.i3 = all_1s.i3; // Copying a single bitfield does not copy all bitfields. @@ -275,7 +275,7 @@ TEST(BitStructs, FieldAssignment) { } { - TestBitStruct_u8 tst{}; // NOLINT + TestBitStruct_u8 tst{}; tst.u4 = all_1s.u4; // Copying a single bitfield does not copy all bitfields. @@ -291,13 +291,13 @@ BITSTRUCT_DEFINE_START(NestedStruct, /* size */ 64) BITSTRUCT_DEFINE_END(NestedStruct); TEST(BitStructs, NestedFieldAssignment) { - MixedSizeBitStruct mixed_all_1s{}; // NOLINT + MixedSizeBitStruct mixed_all_1s{}; mixed_all_1s.alias_all = 0xFFFFFFFFu; { - NestedStruct xyz{}; // NOLINT + NestedStruct xyz{}; - NestedStruct other{}; // NOLINT + NestedStruct other{}; other.mixed_upper = mixed_all_1s; other.mixed_lower = mixed_all_1s; @@ -307,9 +307,9 @@ TEST(BitStructs, NestedFieldAssignment) { } { - NestedStruct xyz{}; // NOLINT + NestedStruct xyz{}; - NestedStruct other{}; // NOLINT + NestedStruct other{}; other.mixed_upper = mixed_all_1s; other.mixed_lower = mixed_all_1s; diff --git a/runtime/base/bit_utils.h b/runtime/base/bit_utils.h index 5d836545e933e099c47c9cdbe5e81325386dd9e8..34cddbff6a8b5ea0520da34bd2105b37423da33d 100644 --- a/runtime/base/bit_utils.h +++ b/runtime/base/bit_utils.h @@ -20,7 +20,8 @@ #include #include -#include "base/logging.h" +#include + #include "base/stl_util_identity.h" namespace art { diff --git a/runtime/base/bit_utils_iterator.h b/runtime/base/bit_utils_iterator.h index 8514de6b75b44910332e7227add24773076d9d66..2d3d0508cccdafc082a941eb57dac33f7e1cc669 100644 --- a/runtime/base/bit_utils_iterator.h +++ b/runtime/base/bit_utils_iterator.h @@ -21,9 +21,10 @@ #include #include +#include + #include "base/bit_utils.h" #include "base/iteration_range.h" -#include "base/logging.h" #include "base/stl_util.h" namespace art { diff --git a/runtime/base/bit_vector-inl.h b/runtime/base/bit_vector-inl.h index 0e67f77e19d0656d7466aab14551d8a1d6ac771a..e67d4e25eb04f8c2bde4906058676ace3800a7d7 100644 --- a/runtime/base/bit_vector-inl.h +++ b/runtime/base/bit_vector-inl.h @@ -17,9 +17,11 @@ #ifndef ART_RUNTIME_BASE_BIT_VECTOR_INL_H_ #define ART_RUNTIME_BASE_BIT_VECTOR_INL_H_ -#include "base/bit_utils.h" #include "bit_vector.h" -#include "logging.h" + +#include + +#include "base/bit_utils.h" namespace art { diff --git a/runtime/base/bounded_fifo.h b/runtime/base/bounded_fifo.h index 7bcd38202297e1cff3a41a45f394c77c3e4ae6d2..1520770fe6a495abe2e54ef5d2a990bd46fa28d9 100644 --- a/runtime/base/bounded_fifo.h +++ b/runtime/base/bounded_fifo.h @@ -17,8 +17,9 @@ #ifndef ART_RUNTIME_BASE_BOUNDED_FIFO_H_ #define ART_RUNTIME_BASE_BOUNDED_FIFO_H_ +#include + #include "base/bit_utils.h" -#include "base/logging.h" namespace art { diff --git a/runtime/base/casts.h b/runtime/base/casts.h index 0cbabba6df915f7da6be54063c81bf88e552aab7..ac1a10c24ffabf7e7a4f5619aa97bce6c0bad841 100644 --- a/runtime/base/casts.h +++ b/runtime/base/casts.h @@ -24,8 +24,7 @@ #include #include -#include "base/logging.h" -#include "base/macros.h" +#include namespace art { @@ -77,6 +76,14 @@ inline To down_cast(From* f) { // so we only accept pointers return static_cast(f); } +template // use like this: down_cast(foo); +inline To down_cast(From& f) { // so we only accept references + static_assert(std::is_base_of::type>::value, + "down_cast unsafe as To is not a subtype of From"); + + return static_cast(f); +} + template inline Dest bit_cast(const Source& source) { // Compile time assertion: sizeof(Dest) == sizeof(Source) diff --git a/runtime/base/dchecked_vector.h b/runtime/base/dchecked_vector.h index 77f0ea2b7ceceff610b5b74732444dfb672373d2..7236ac301af27c41c787d67f63e12ee96c4e0d68 100644 --- a/runtime/base/dchecked_vector.h +++ b/runtime/base/dchecked_vector.h @@ -21,7 +21,7 @@ #include #include -#include "base/logging.h" +#include namespace art { diff --git a/runtime/base/debug_stack.h b/runtime/base/debug_stack.h index 886065db30927122c50b4bc677e498ee4ff56082..1331e10a0242d9ef77814f628e5d66ae279c14ba 100644 --- a/runtime/base/debug_stack.h +++ b/runtime/base/debug_stack.h @@ -17,8 +17,9 @@ #ifndef ART_RUNTIME_BASE_DEBUG_STACK_H_ #define ART_RUNTIME_BASE_DEBUG_STACK_H_ -#include "base/logging.h" -#include "base/macros.h" +#include +#include + #include "globals.h" namespace art { diff --git a/runtime/base/file_magic.cc b/runtime/base/file_magic.cc index dffb9b43a17cef1e759dc94b4e3a4e913312ea41..ac2e1841637f32b4fb23883ae6ed1c196455ebf7 100644 --- a/runtime/base/file_magic.cc +++ b/runtime/base/file_magic.cc @@ -20,9 +20,9 @@ #include #include -#include "android-base/stringprintf.h" +#include +#include -#include "base/logging.h" #include "base/unix_file/fd_file.h" #include "dex_file.h" diff --git a/runtime/base/file_utils.cc b/runtime/base/file_utils.cc index 323a06519db7df54de6573a618d4f4ef770b08ae..db498600d9c8bf904cbd00e2e14d3f310f3a5ac7 100644 --- a/runtime/base/file_utils.cc +++ b/runtime/base/file_utils.cc @@ -89,7 +89,7 @@ bool ReadFileToString(const std::string& file_name, std::string* result) { } } -bool PrintFileToLog(const std::string& file_name, LogSeverity level) { +bool PrintFileToLog(const std::string& file_name, android::base::LogSeverity level) { File file(file_name, O_RDONLY, false); if (!file.IsOpened()) { return false; diff --git a/runtime/base/file_utils.h b/runtime/base/file_utils.h index 007f3b443d2bb29f1557a30a6e25a2dc77cd2c6f..e4555ad3cb1ddd78a90c8f7633a77535e132670a 100644 --- a/runtime/base/file_utils.h +++ b/runtime/base/file_utils.h @@ -21,13 +21,14 @@ #include +#include + #include "arch/instruction_set.h" -#include "base/logging.h" namespace art { bool ReadFileToString(const std::string& file_name, std::string* result); -bool PrintFileToLog(const std::string& file_name, LogSeverity level); +bool PrintFileToLog(const std::string& file_name, android::base::LogSeverity level); // Find $ANDROID_ROOT, /system, or abort. std::string GetAndroidRoot(); diff --git a/runtime/base/hash_set.h b/runtime/base/hash_set.h index c743342a9854166f66569f936b42807bbffb4492..47e6d93346696d52d7798fc22d6ae957548817e2 100644 --- a/runtime/base/hash_set.h +++ b/runtime/base/hash_set.h @@ -25,8 +25,10 @@ #include #include +#include + #include "bit_utils.h" -#include "logging.h" +#include "macros.h" namespace art { diff --git a/runtime/base/histogram-inl.h b/runtime/base/histogram-inl.h index be2092040dd3f8a97c4da20052620998b82bdda0..3ce0140c846dbc858aee6dc291bd6549ef0465c4 100644 --- a/runtime/base/histogram-inl.h +++ b/runtime/base/histogram-inl.h @@ -24,6 +24,8 @@ #include "histogram.h" +#include + #include "base/bit_utils.h" #include "base/time_utils.h" #include "utils.h" diff --git a/runtime/base/histogram.h b/runtime/base/histogram.h index e0c921e4080c9824f34dfc983123bcbc2dd10d99..7544a9c91847c0b96685a531f80b3743f0ea8a39 100644 --- a/runtime/base/histogram.h +++ b/runtime/base/histogram.h @@ -19,7 +19,7 @@ #include #include -#include "base/logging.h" +#include namespace art { diff --git a/runtime/base/logging.cc b/runtime/base/logging.cc index 4776357fdff04c700526a4491d9801469d262157..90eb74c75cc95a9a47ad33864cfd3c468ee3553b 100644 --- a/runtime/base/logging.cc +++ b/runtime/base/logging.cc @@ -20,7 +20,8 @@ #include #include -#include "base/mutex.h" +#include "aborting.h" +#include "mutex.h" #include "thread-current-inl.h" #include "utils.h" @@ -34,55 +35,6 @@ namespace art { -// We test here that the runtime-debug-checks are actually a no-op constexpr false in release -// builds, as we can't check that in gtests (which are always debug). - -#ifdef NDEBUG -namespace { -DECLARE_RUNTIME_DEBUG_FLAG(kTestForConstexpr); -static_assert(!kTestForConstexpr, "Issue with DECLARE_RUNTIME_DEBUG_FLAG in NDEBUG."); -} -#endif - -// Implementation of runtime debug flags. This should be compile-time optimized away in release -// builds. -namespace { -bool gSlowEnabled = false; // Default for slow flags is "off." - -// Use a function with a static to ensure our vector storage doesn't have initialization order -// issues. -std::vector& GetFlagPtrs() { - static std::vector g_flag_ptrs; - return g_flag_ptrs; -} - -bool RegisterRuntimeDebugFlagImpl(bool* flag_ptr) { - GetFlagPtrs().push_back(flag_ptr); - return gSlowEnabled; -} - -void SetRuntimeDebugFlagsEnabledImpl(bool enabled) { - gSlowEnabled = enabled; - for (bool* flag_ptr : GetFlagPtrs()) { - *flag_ptr = enabled; - } -} - -} // namespace - -bool RegisterRuntimeDebugFlag(bool* flag_ptr) { - if (kIsDebugBuild) { - return RegisterRuntimeDebugFlagImpl(flag_ptr); - } - return false; -} - -void SetRuntimeDebugFlagsEnabled(bool enabled) { - if (kIsDebugBuild) { - SetRuntimeDebugFlagsEnabledImpl(enabled); - } -} - LogVerbosity gLogVerbosity; std::atomic gAborting(0); diff --git a/runtime/base/logging.h b/runtime/base/logging.h index 15f935395e9277e41a933a7ce1406721d61c20aa..c562bdf59f76d264d6eff22e323b86e4bfc640cf 100644 --- a/runtime/base/logging.h +++ b/runtime/base/logging.h @@ -53,6 +53,7 @@ struct LogVerbosity { bool third_party_jni; // Enabled with "-verbose:third-party-jni". bool threads; bool verifier; + bool verifier_debug; // Only works in debug builds. bool image; bool systrace_lock_logging; // Enabled with "-verbose:sys-locks". bool agents; @@ -62,48 +63,6 @@ struct LogVerbosity { // Global log verbosity setting, initialized by InitLogging. extern LogVerbosity gLogVerbosity; -// Runtime debug flags are flags that have a runtime component, that is, their value can be changed. -// This is meant to implement fast vs slow debug builds, in that certain debug flags can be turned -// on and off. To that effect, expose two macros to help implement and globally drive these flags: -// -// In the header, declare a (class) flag like this: -// -// class C { -// DECLARE_RUNTIME_DEBUG_FLAG(kFlag); -// }; -// -// This will declare a flag kFlag that is a constexpr false in release builds, and a static field -// in debug builds. Usage is than uniform as C::kFlag. -// -// In the cc file, define the flag like this: -// -// DEFINE_RUNTIME_DEBUG_FLAG(C, kFlag); -// -// This will define the static storage, as necessary, and register the flag with the runtime -// infrastructure to toggle the value. - -#ifdef NDEBUG -#define DECLARE_RUNTIME_DEBUG_FLAG(x) \ - static constexpr bool x = false; -// Note: the static_assert in the following only works for public flags. Fix this when we cross -// the line at some point. -#define DEFINE_RUNTIME_DEBUG_FLAG(C, x) \ - static_assert(!C::x, "Unexpected enabled flag in release build"); -#else -#define DECLARE_RUNTIME_DEBUG_FLAG(x) \ - static bool x; -#define DEFINE_RUNTIME_DEBUG_FLAG(C, x) \ - bool C::x = RegisterRuntimeDebugFlag(&C::x); -#endif // NDEBUG - -bool RegisterRuntimeDebugFlag(bool* runtime_debug_flag); -void SetRuntimeDebugFlagsEnabled(bool enabled); - -// 0 if not abort, non-zero if an abort is in progress. Used on fatal exit to prevents recursive -// aborts. Global declaration allows us to disable some error checking to ensure fatal shutdown -// makes forward progress. -extern std::atomic gAborting; - // Configure logging based on ANDROID_LOG_TAGS environment variable. // We need to parse a string that looks like // diff --git a/runtime/base/logging_test.cc b/runtime/base/logging_test.cc index d380b9eccc7b330ed5153c713508fd201ecc9c25..404e080b03cb8822b4d3aac06b9db1b5a202e73a 100644 --- a/runtime/base/logging_test.cc +++ b/runtime/base/logging_test.cc @@ -22,6 +22,7 @@ #include "base/bit_utils.h" #include "base/macros.h" #include "common_runtime_test.h" +#include "runtime_debug.h" namespace art { diff --git a/runtime/base/macros.h b/runtime/base/macros.h index 6cd7d60253ba29473e3db4d8eae0daf982b0f631..512e5ce6515a8a74ca76658cb1944c6b5e9d5129 100644 --- a/runtime/base/macros.h +++ b/runtime/base/macros.h @@ -59,6 +59,10 @@ template ART_FRIEND_TEST(test_set_name, individual_test) #define QUOTE(x) #x #define STRINGIFY(x) QUOTE(x) +// Append tokens after evaluating. +#define APPEND_TOKENS_AFTER_EVAL_2(a, b) a ## b +#define APPEND_TOKENS_AFTER_EVAL(a, b) APPEND_TOKENS_AFTER_EVAL_2(a, b) + #ifndef NDEBUG #define ALWAYS_INLINE #else diff --git a/runtime/base/mutex-inl.h b/runtime/base/mutex-inl.h index c9d48ff7f7defa4fa10d40d7879867c023baa49e..587b092ab78469fb7c217a8b2f233d5035475e87 100644 --- a/runtime/base/mutex-inl.h +++ b/runtime/base/mutex-inl.h @@ -80,7 +80,9 @@ static inline void CheckUnattachedThread(LockLevel level) NO_THREAD_SAFETY_ANALY // (see Thread::TransitionFromSuspendedToRunnable). level == kThreadSuspendCountLock || // Avoid recursive death. - level == kAbortLock) << level; + level == kAbortLock || + // Locks at the absolute top of the stack can be locked at any time. + level == kTopLockLevel) << level; } } @@ -92,10 +94,34 @@ inline void BaseMutex::RegisterAsLocked(Thread* self) { if (kDebugLocking) { // Check if a bad Mutex of this level or lower is held. bool bad_mutexes_held = false; + // Specifically allow a kTopLockLevel lock to be gained when the current thread holds the + // mutator_lock_ exclusive. This is because we suspending when holding locks at this level is + // not allowed and if we hold the mutator_lock_ exclusive we must unsuspend stuff eventually + // so there are no deadlocks. + if (level_ == kTopLockLevel && + Locks::mutator_lock_->IsSharedHeld(self) && + !Locks::mutator_lock_->IsExclusiveHeld(self)) { + LOG(ERROR) << "Lock level violation: holding \"" << Locks::mutator_lock_->name_ << "\" " + << "(level " << kMutatorLock << " - " << static_cast(kMutatorLock) + << ") non-exclusive while locking \"" << name_ << "\" " + << "(level " << level_ << " - " << static_cast(level_) << ") a top level" + << "mutex. This is not allowed."; + bad_mutexes_held = true; + } else if (this == Locks::mutator_lock_ && self->GetHeldMutex(kTopLockLevel) != nullptr) { + LOG(ERROR) << "Lock level violation. Locking mutator_lock_ while already having a " + << "kTopLevelLock (" << self->GetHeldMutex(kTopLockLevel)->name_ << "held is " + << "not allowed."; + bad_mutexes_held = true; + } for (int i = level_; i >= 0; --i) { LockLevel lock_level_i = static_cast(i); BaseMutex* held_mutex = self->GetHeldMutex(lock_level_i); - if (UNLIKELY(held_mutex != nullptr) && lock_level_i != kAbortLock) { + if (level_ == kTopLockLevel && + lock_level_i == kMutatorLock && + Locks::mutator_lock_->IsExclusiveHeld(self)) { + // This is checked above. + continue; + } else if (UNLIKELY(held_mutex != nullptr) && lock_level_i != kAbortLock) { LOG(ERROR) << "Lock level violation: holding \"" << held_mutex->name_ << "\" " << "(level " << lock_level_i << " - " << i << ") while locking \"" << name_ << "\" " diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index 877f052006e06cbe9f2ecf684719b56a9862429f..e88ed68ef1654697f925b03e16e94165be74a317 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -102,18 +102,40 @@ static bool ComputeRelativeTimeSpec(timespec* result_ts, const timespec& lhs, co } #endif +// Wait for an amount of time that roughly increases in the argument i. +// Spin for small arguments and yield/sleep for longer ones. +static void BackOff(uint32_t i) { + static constexpr uint32_t kSpinMax = 10; + static constexpr uint32_t kYieldMax = 20; + if (i <= kSpinMax) { + // TODO: Esp. in very latency-sensitive cases, consider replacing this with an explicit + // test-and-test-and-set loop in the caller. Possibly skip entirely on a uniprocessor. + volatile uint32_t x = 0; + const uint32_t spin_count = 10 * i; + for (uint32_t spin = 0; spin < spin_count; ++spin) { + ++x; // Volatile; hence should not be optimized away. + } + // TODO: Consider adding x86 PAUSE and/or ARM YIELD here. + } else if (i <= kYieldMax) { + sched_yield(); + } else { + NanoSleep(1000ull * (i - kYieldMax)); + } +} + class ScopedAllMutexesLock FINAL { public: explicit ScopedAllMutexesLock(const BaseMutex* mutex) : mutex_(mutex) { - while (!gAllMutexData->all_mutexes_guard.CompareExchangeWeakAcquire(0, mutex)) { - NanoSleep(100); + for (uint32_t i = 0; + !gAllMutexData->all_mutexes_guard.CompareExchangeWeakAcquire(0, mutex); + ++i) { + BackOff(i); } } ~ScopedAllMutexesLock() { - while (!gAllMutexData->all_mutexes_guard.CompareExchangeWeakRelease(mutex_, 0)) { - NanoSleep(100); - } + DCHECK_EQ(gAllMutexData->all_mutexes_guard.LoadRelaxed(), mutex_); + gAllMutexData->all_mutexes_guard.StoreRelease(0); } private: @@ -123,17 +145,16 @@ class ScopedAllMutexesLock FINAL { class Locks::ScopedExpectedMutexesOnWeakRefAccessLock FINAL { public: explicit ScopedExpectedMutexesOnWeakRefAccessLock(const BaseMutex* mutex) : mutex_(mutex) { - while (!Locks::expected_mutexes_on_weak_ref_access_guard_.CompareExchangeWeakAcquire(0, - mutex)) { - NanoSleep(100); + for (uint32_t i = 0; + !Locks::expected_mutexes_on_weak_ref_access_guard_.CompareExchangeWeakAcquire(0, mutex); + ++i) { + BackOff(i); } } ~ScopedExpectedMutexesOnWeakRefAccessLock() { - while (!Locks::expected_mutexes_on_weak_ref_access_guard_.CompareExchangeWeakRelease(mutex_, - 0)) { - NanoSleep(100); - } + DCHECK_EQ(Locks::expected_mutexes_on_weak_ref_access_guard_.LoadRelaxed(), mutex_); + Locks::expected_mutexes_on_weak_ref_access_guard_.StoreRelease(0); } private: @@ -584,7 +605,7 @@ ReaderWriterMutex::ReaderWriterMutex(const char* name, LockLevel level) #if ART_USE_FUTEXES , state_(0), num_pending_readers_(0), num_pending_writers_(0) #endif -{ // NOLINT(whitespace/braces) +{ #if !ART_USE_FUTEXES CHECK_MUTEX_CALL(pthread_rwlock_init, (&rwlock_, nullptr)); #endif @@ -938,7 +959,7 @@ void ConditionVariable::WaitHoldingLocks(Thread* self) { } if (self != nullptr) { JNIEnvExt* const env = self->GetJniEnv(); - if (UNLIKELY(env != nullptr && env->runtime_deleted)) { + if (UNLIKELY(env != nullptr && env->IsRuntimeDeleted())) { CHECK(self->IsDaemon()); // If the runtime has been deleted, then we cannot proceed. Just sleep forever. This may // occur for user daemon threads that get a spurious wakeup. This occurs for test 132 with diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index 87c4afe96fc767d32be078188a6d7369d44b6f9c..7077298ca98f309a2d6a4147e23b1dac3c35e891 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -24,8 +24,10 @@ #include #include +#include + #include "atomic.h" -#include "base/logging.h" +#include "base/aborting.h" #include "base/macros.h" #include "globals.h" @@ -122,6 +124,16 @@ enum LockLevel { kInstrumentEntrypointsLock, kZygoteCreationLock, + // The highest valid lock level. Use this if there is code that should only be called with no + // other locks held. Since this is the highest lock level we also allow it to be held even if the + // runtime or current thread is not fully set-up yet (for example during thread attach). Note that + // this lock also has special behavior around the mutator_lock_. Since the mutator_lock_ is not + // really a 'real' lock we allow this to be locked when the mutator_lock_ is held exclusive. + // Furthermore, the mutator_lock_ may not be acquired in any form when a lock of this level is + // held. Since the mutator_lock_ being held strong means that all other threads are suspended this + // will prevent deadlocks while still allowing this lock level to function as a "highest" level. + kTopLockLevel, + kLockLevelCount // Must come last. }; std::ostream& operator<<(std::ostream& os, const LockLevel& rhs); diff --git a/runtime/base/runtime_debug.cc b/runtime/base/runtime_debug.cc new file mode 100644 index 0000000000000000000000000000000000000000..4f8a8ec9c6e80e34ff784d3a9dc18469aae583bb --- /dev/null +++ b/runtime/base/runtime_debug.cc @@ -0,0 +1,74 @@ +/* + * 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 + * limitations under the License. + */ + +#include "runtime_debug.h" + +#include + +#include "globals.h" + +namespace art { + +// We test here that the runtime-debug-checks are actually a no-op constexpr false in release +// builds, as we can't check that in gtests (which are always debug). + +#ifdef NDEBUG +namespace { +DECLARE_RUNTIME_DEBUG_FLAG(kTestForConstexpr); +static_assert(!kTestForConstexpr, "Issue with DECLARE_RUNTIME_DEBUG_FLAG in NDEBUG."); +} +#endif + +// Implementation of runtime debug flags. This should be compile-time optimized away in release +// builds. +namespace { +bool gSlowEnabled = false; // Default for slow flags is "off." + +// Use a function with a static to ensure our vector storage doesn't have initialization order +// issues. +std::vector& GetFlagPtrs() { + static std::vector g_flag_ptrs; + return g_flag_ptrs; +} + +bool RegisterRuntimeDebugFlagImpl(bool* flag_ptr) { + GetFlagPtrs().push_back(flag_ptr); + return gSlowEnabled; +} + +void SetRuntimeDebugFlagsEnabledImpl(bool enabled) { + gSlowEnabled = enabled; + for (bool* flag_ptr : GetFlagPtrs()) { + *flag_ptr = enabled; + } +} + +} // namespace + +bool RegisterRuntimeDebugFlag(bool* flag_ptr) { + if (kIsDebugBuild) { + return RegisterRuntimeDebugFlagImpl(flag_ptr); + } + return false; +} + +void SetRuntimeDebugFlagsEnabled(bool enabled) { + if (kIsDebugBuild) { + SetRuntimeDebugFlagsEnabledImpl(enabled); + } +} + +} // namespace art diff --git a/runtime/base/runtime_debug.h b/runtime/base/runtime_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..89a0361fa7102d490cd9604f026ac66a3473a2a0 --- /dev/null +++ b/runtime/base/runtime_debug.h @@ -0,0 +1,61 @@ +/* + * 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 + * limitations under the License. + */ + +#ifndef ART_RUNTIME_BASE_RUNTIME_DEBUG_H_ +#define ART_RUNTIME_BASE_RUNTIME_DEBUG_H_ + +namespace art { + +// Runtime debug flags are flags that have a runtime component, that is, their value can be changed. +// This is meant to implement fast vs slow debug builds, in that certain debug flags can be turned +// on and off. To that effect, expose two macros to help implement and globally drive these flags: +// +// In the header, declare a (class) flag like this: +// +// class C { +// DECLARE_RUNTIME_DEBUG_FLAG(kFlag); +// }; +// +// This will declare a flag kFlag that is a constexpr false in release builds, and a static field +// in debug builds. Usage is than uniform as C::kFlag. +// +// In the cc file, define the flag like this: +// +// DEFINE_RUNTIME_DEBUG_FLAG(C, kFlag); +// +// This will define the static storage, as necessary, and register the flag with the runtime +// infrastructure to toggle the value. + +#ifdef NDEBUG +#define DECLARE_RUNTIME_DEBUG_FLAG(x) \ + static constexpr bool x = false; +// Note: the static_assert in the following only works for public flags. Fix this when we cross +// the line at some point. +#define DEFINE_RUNTIME_DEBUG_FLAG(C, x) \ + static_assert(!C::x, "Unexpected enabled flag in release build"); +#else +#define DECLARE_RUNTIME_DEBUG_FLAG(x) \ + static bool x; +#define DEFINE_RUNTIME_DEBUG_FLAG(C, x) \ + bool C::x = RegisterRuntimeDebugFlag(&C::x); +#endif // NDEBUG + +bool RegisterRuntimeDebugFlag(bool* runtime_debug_flag); +void SetRuntimeDebugFlagsEnabled(bool enabled); + +} // namespace art + +#endif // ART_RUNTIME_BASE_RUNTIME_DEBUG_H_ diff --git a/runtime/base/scoped_arena_allocator.h b/runtime/base/scoped_arena_allocator.h index 8f50fd443b378a6e6e980809ac9f9e7c3ada7b1c..35e337f0d60b1a85a5fe28cd1a81545742f3217d 100644 --- a/runtime/base/scoped_arena_allocator.h +++ b/runtime/base/scoped_arena_allocator.h @@ -17,10 +17,11 @@ #ifndef ART_RUNTIME_BASE_SCOPED_ARENA_ALLOCATOR_H_ #define ART_RUNTIME_BASE_SCOPED_ARENA_ALLOCATOR_H_ +#include + #include "arena_allocator.h" #include "debug_stack.h" #include "globals.h" -#include "logging.h" #include "macros.h" namespace art { diff --git a/runtime/base/scoped_arena_containers.h b/runtime/base/scoped_arena_containers.h index fccaaeaa42bb87f2755fc10d7de321430a757ef9..756089f4387d9dc9722315ad7acc474040221c38 100644 --- a/runtime/base/scoped_arena_containers.h +++ b/runtime/base/scoped_arena_containers.h @@ -110,7 +110,7 @@ class ScopedArenaAllocatorAdapter arena_stack_(allocator->arena_stack_) { } template - ScopedArenaAllocatorAdapter(const ScopedArenaAllocatorAdapter& other) // NOLINT, implicit + ScopedArenaAllocatorAdapter(const ScopedArenaAllocatorAdapter& other) : DebugStackReference(other), DebugStackIndirectTopRef(other), ArenaAllocatorAdapterKind(other), @@ -153,7 +153,7 @@ class ScopedArenaAllocatorAdapter arena_stack_(allocator->arena_stack_) { } template - ScopedArenaAllocatorAdapter(const ScopedArenaAllocatorAdapter& other) // NOLINT, implicit + ScopedArenaAllocatorAdapter(const ScopedArenaAllocatorAdapter& other) : DebugStackReference(other), DebugStackIndirectTopRef(other), ArenaAllocatorAdapterKind(other), diff --git a/runtime/base/scoped_flock.cc b/runtime/base/scoped_flock.cc index b8df6897e47e7708639c4de1679d1c953f552564..514b97bfb157055f0c36f136d9727f7dbb334615 100644 --- a/runtime/base/scoped_flock.cc +++ b/runtime/base/scoped_flock.cc @@ -19,9 +19,9 @@ #include #include -#include "android-base/stringprintf.h" +#include +#include -#include "base/logging.h" #include "base/unix_file/fd_file.h" namespace art { diff --git a/runtime/base/scoped_flock.h b/runtime/base/scoped_flock.h index 1b933c07f30a2e84869e5ef64db892551d548db5..db6c819c6c444974b5a013620d3e697088d12111 100644 --- a/runtime/base/scoped_flock.h +++ b/runtime/base/scoped_flock.h @@ -20,9 +20,8 @@ #include #include -#include "android-base/unique_fd.h" +#include -#include "base/logging.h" #include "base/macros.h" #include "base/unix_file/fd_file.h" #include "os.h" diff --git a/runtime/base/stl_util.h b/runtime/base/stl_util.h index b27297241df494a04aafee1bfad24250635fa5f3..02f37652cffe371aee8d4495e9579bd865320e2d 100644 --- a/runtime/base/stl_util.h +++ b/runtime/base/stl_util.h @@ -21,7 +21,7 @@ #include #include -#include "base/logging.h" +#include namespace art { diff --git a/runtime/base/stringpiece.cc b/runtime/base/stringpiece.cc index 2570bad85dd82c58381cc6de062506e94e583ada..672431cf9da359c1406b55c7e9fad6cc5a2d1adf 100644 --- a/runtime/base/stringpiece.cc +++ b/runtime/base/stringpiece.cc @@ -19,7 +19,7 @@ #include #include -#include "logging.h" +#include namespace art { diff --git a/runtime/base/systrace.h b/runtime/base/systrace.h index 06db48a576eb6f2a63913b748fdfe5f03b005c98..dc2206e420f3bb708de6313ef26f4f0261c15c9a 100644 --- a/runtime/base/systrace.h +++ b/runtime/base/systrace.h @@ -19,10 +19,12 @@ #define ATRACE_TAG ATRACE_TAG_DALVIK #include -#include +#include #include +#include "android-base/stringprintf.h" + namespace art { class ScopedTrace { @@ -30,6 +32,12 @@ class ScopedTrace { explicit ScopedTrace(const char* name) { ATRACE_BEGIN(name); } + template + explicit ScopedTrace(Fn fn) { + if (ATRACE_ENABLED()) { + ATRACE_BEGIN(fn().c_str()); + } + } explicit ScopedTrace(const std::string& name) : ScopedTrace(name.c_str()) {} @@ -38,6 +46,38 @@ class ScopedTrace { } }; +// Helper for the SCOPED_TRACE macro. Do not use directly. +class ScopedTraceNoStart { + public: + ScopedTraceNoStart() { + } + + ~ScopedTraceNoStart() { + ATRACE_END(); + } + + // Message helper for the macro. Do not use directly. + class ScopedTraceMessageHelper { + public: + ScopedTraceMessageHelper() { + } + ~ScopedTraceMessageHelper() { + ATRACE_BEGIN(buffer_.str().c_str()); + } + + std::ostream& stream() { + return buffer_; + } + + private: + std::ostringstream buffer_; + }; +}; + +#define SCOPED_TRACE \ + ::art::ScopedTraceNoStart trace ## __LINE__; \ + (ATRACE_ENABLED()) && ::art::ScopedTraceNoStart::ScopedTraceMessageHelper().stream() + } // namespace art #endif // ART_RUNTIME_BASE_SYSTRACE_H_ diff --git a/runtime/base/time_utils.h b/runtime/base/time_utils.h index 919937f5ba7c3003dabcffe1d97edef2dbca8f3e..7648a109fa00f0ed889d1889c0fe862dc377a4b7 100644 --- a/runtime/base/time_utils.h +++ b/runtime/base/time_utils.h @@ -76,6 +76,15 @@ static constexpr inline uint64_t MsToNs(uint64_t ms) { return ms * 1000 * 1000; } +// Converts the given number of milliseconds to microseconds +static constexpr inline uint64_t MsToUs(uint64_t ms) { + return ms * 1000; +} + +static constexpr inline uint64_t UsToNs(uint64_t us) { + return us * 1000; +} + #if defined(__APPLE__) #ifndef CLOCK_REALTIME // No clocks to specify on OS/X < 10.12, fake value to pass to routines that require a clock. diff --git a/runtime/base/timing_logger.cc b/runtime/base/timing_logger.cc index b8d6931a830b74b98b1685a650da7a2376da3ffe..23ec3e1aeae640a488a0da1b58d5e89d35104860 100644 --- a/runtime/base/timing_logger.cc +++ b/runtime/base/timing_logger.cc @@ -18,8 +18,9 @@ #include "timing_logger.h" +#include + #include "base/histogram-inl.h" -#include "base/logging.h" #include "base/stl_util.h" #include "base/systrace.h" #include "base/time_utils.h" diff --git a/runtime/base/transform_array_ref.h b/runtime/base/transform_array_ref.h index b432f86d77d59f46c0d6baafe5e5a150ad52bf87..a4e0bc27ed145559d94f7dd3db4c57a48d880827 100644 --- a/runtime/base/transform_array_ref.h +++ b/runtime/base/transform_array_ref.h @@ -72,7 +72,7 @@ class TransformArrayRef { template ::value>::type> - TransformArrayRef(const TransformArrayRef& other) // NOLINT, implicit + TransformArrayRef(const TransformArrayRef& other) : TransformArrayRef(other.base(), other.GetFunction()) { } // Assignment operators. diff --git a/runtime/base/transform_array_ref_test.cc b/runtime/base/transform_array_ref_test.cc index 494dbb29aa3d82ecfa632750f6c98665499094c2..da0340d36b87b2bdf9f73b58582e3986d888e74f 100644 --- a/runtime/base/transform_array_ref_test.cc +++ b/runtime/base/transform_array_ref_test.cc @@ -38,7 +38,7 @@ ATTRIBUTE_UNUSED bool operator==(const ValueHolder& lhs, const ValueHolder& rhs) } // anonymous namespace TEST(TransformArrayRef, ConstRefAdd1) { - auto add1 = [](const ValueHolder& h) { return h.value + 1; }; // NOLINT [readability/braces] + auto add1 = [](const ValueHolder& h) { return h.value + 1; }; std::vector input({ 7, 6, 4, 0 }); std::vector output; @@ -79,7 +79,7 @@ TEST(TransformArrayRef, ConstRefAdd1) { } TEST(TransformArrayRef, NonConstRefSub1) { - auto sub1 = [](ValueHolder& h) { return h.value - 1; }; // NOLINT [readability/braces] + auto sub1 = [](ValueHolder& h) { return h.value - 1; }; std::vector input({ 4, 4, 5, 7, 10 }); std::vector output; diff --git a/runtime/base/transform_iterator.h b/runtime/base/transform_iterator.h index f1a8a52ceb7f29069144d1c21ddf122186116ac5..9c8f822b71e6993983e54f35a992f7ad886569fe 100644 --- a/runtime/base/transform_iterator.h +++ b/runtime/base/transform_iterator.h @@ -62,7 +62,7 @@ class TransformIterator { : data_(base, fn) { } template - TransformIterator(const TransformIterator& other) // NOLINT, implicit + TransformIterator(const TransformIterator& other) : data_(other.base(), other.GetFunction()) { } diff --git a/runtime/base/transform_iterator_test.cc b/runtime/base/transform_iterator_test.cc index a85dda89582e652b69d0ce501e7378ef1dcb5edb..63b6e4f531f3b5b90023cc5fbd11af01230c3d31 100644 --- a/runtime/base/transform_iterator_test.cc +++ b/runtime/base/transform_iterator_test.cc @@ -41,7 +41,7 @@ bool operator==(const ValueHolder& lhs, const ValueHolder& rhs) { } // anonymous namespace TEST(TransformIterator, VectorAdd1) { - auto add1 = [](const ValueHolder& h) { return h.value + 1; }; // NOLINT [readability/braces] + auto add1 = [](const ValueHolder& h) { return h.value + 1; }; std::vector input({ 1, 7, 3, 8 }); std::vector output; @@ -144,7 +144,7 @@ TEST(TransformIterator, VectorAdd1) { } TEST(TransformIterator, ListSub1) { - auto sub1 = [](const ValueHolder& h) { return h.value - 1; }; // NOLINT [readability/braces] + auto sub1 = [](const ValueHolder& h) { return h.value - 1; }; std::list input({ 2, 3, 5, 7, 11 }); std::vector output; @@ -208,7 +208,7 @@ TEST(TransformIterator, ListSub1) { } TEST(TransformIterator, ForwardListSub1) { - auto mul3 = [](const ValueHolder& h) { return h.value * 3; }; // NOLINT [readability/braces] + auto mul3 = [](const ValueHolder& h) { return h.value * 3; }; std::forward_list input({ 1, 1, 2, 3, 5, 8 }); std::vector output; @@ -246,7 +246,7 @@ TEST(TransformIterator, ForwardListSub1) { } TEST(TransformIterator, VectorConstReference) { - auto ref = [](const ValueHolder& h) -> const int& { return h.value; }; // NOLINT [readability/braces] + auto ref = [](const ValueHolder& h) -> const int& { return h.value; }; std::vector input({ 7, 3, 1, 2, 4, 8 }); std::vector output; @@ -339,7 +339,7 @@ TEST(TransformIterator, VectorConstReference) { } TEST(TransformIterator, VectorNonConstReference) { - auto ref = [](ValueHolder& h) -> int& { return h.value; }; // NOLINT [readability/braces] + auto ref = [](ValueHolder& h) -> int& { return h.value; }; std::vector input({ 7, 3, 1, 2, 4, 8 }); std::vector output; @@ -519,7 +519,7 @@ TEST(TransformIterator, VectorConstAndNonConstReference) { } TEST(TransformIterator, TransformRange) { - auto ref = [](ValueHolder& h) -> int& { return h.value; }; // NOLINT [readability/braces] + auto ref = [](ValueHolder& h) -> int& { return h.value; }; std::vector data({ 1, 0, 1, 3, 1, 0 }); for (int& v : MakeTransformRange(data, ref)) { diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc index 792c58172e3fe6fdd53dfefdd6f4416be640feb2..37f239da232014a8a0ce1d12a1e03c3414a94f0c 100644 --- a/runtime/base/unix_file/fd_file.cc +++ b/runtime/base/unix_file/fd_file.cc @@ -23,7 +23,7 @@ #include -#include "base/logging.h" +#include // Includes needed for FdFile::Copy(). #ifdef __linux__ diff --git a/runtime/base/variant_map.h b/runtime/base/variant_map.h index fe332d1e3b5ffff4631d6541f3cab3d22cb5e99f..c480b5162d575c9c35183efc6c7183d4b7feddda 100644 --- a/runtime/base/variant_map.h +++ b/runtime/base/variant_map.h @@ -139,7 +139,7 @@ struct VariantMapKey : detail::VariantMapKeyRaw { // then that is used. Otherwise, the default value for the type TValue{} is returned. TValue CreateDefaultValue() const { if (default_value_ == nullptr) { - return TValue{}; // NOLINT [readability/braces] [4] + return TValue{}; } else { return TValue(*default_value_); } diff --git a/runtime/base/variant_map_test.cc b/runtime/base/variant_map_test.cc index 9dd29baf483cc76650d15aec19d49dc3aefc14a3..4677b6d3b36e2d2cb214d3ceceb0760fb36683db 100644 --- a/runtime/base/variant_map_test.cc +++ b/runtime/base/variant_map_test.cc @@ -107,8 +107,8 @@ TEST(VariantMaps, RuleOfFive) { fmFilled.Set(FruitMap::Orange, 555.0); EXPECT_EQ(size_t(2), fmFilled.Size()); - // Test copy constructor (NOLINT as a reference is suggested, instead) - FruitMap fmEmptyCopy(fmEmpty); // NOLINT + // Test copy constructor + FruitMap fmEmptyCopy(fmEmpty); EXPECT_EQ(size_t(0), fmEmptyCopy.Size()); // Test copy constructor diff --git a/runtime/cdex/compact_dex_file.h b/runtime/cdex/compact_dex_file.h index 8ab9247125a97f2505e526f90771061277dd8178..f17f8cf68a0eab9547780f4c6399cba08f655298 100644 --- a/runtime/cdex/compact_dex_file.h +++ b/runtime/cdex/compact_dex_file.h @@ -24,11 +24,18 @@ namespace art { // CompactDex is a currently ART internal dex file format that aims to reduce storage/RAM usage. class CompactDexFile : public DexFile { public: + static constexpr uint8_t kDexMagic[kDexMagicSize] = { 'c', 'd', 'e', 'x' }; + static constexpr uint8_t kDexMagicVersion[] = {'0', '0', '1', '\0'}; + class Header : public DexFile::Header { // Same for now. }; - static constexpr uint8_t kDexMagic[kDexMagicSize] = { 'c', 'd', 'e', 'x' }; - static constexpr uint8_t kDexMagicVersion[] = {'0', '0', '1', '\0'}; + + struct CodeItem : public DexFile::CodeItem { + private: + // TODO: Insert compact dex specific fields here. + DISALLOW_COPY_AND_ASSIGN(CodeItem); + }; // Write the compact dex specific magic. static void WriteMagic(uint8_t* magic); @@ -44,10 +51,6 @@ class CompactDexFile : public DexFile { static bool IsVersionValid(const uint8_t* magic); virtual bool IsVersionValid() const OVERRIDE; - bool IsCompactDexFile() const OVERRIDE { - return true; - } - private: // Not supported yet. CompactDexFile(const uint8_t* base, @@ -56,7 +59,13 @@ class CompactDexFile : public DexFile { uint32_t location_checksum, const OatDexFile* oat_dex_file, DexFileContainer* container) - : DexFile(base, size, location, location_checksum, oat_dex_file, container) {} + : DexFile(base, + size, + location, + location_checksum, + oat_dex_file, + container, + /*is_compact_dex*/ true) {} friend class DexFile; friend class DexFileLoader; diff --git a/runtime/cdex/compact_dex_level.h b/runtime/cdex/compact_dex_level.h index b824462bf07a0ce1d4f9419705b62c10ea3178b0..5aec00195d8541f35b109dcc1e5b1766f0760fa8 100644 --- a/runtime/cdex/compact_dex_level.h +++ b/runtime/cdex/compact_dex_level.h @@ -17,6 +17,9 @@ #ifndef ART_RUNTIME_CDEX_COMPACT_DEX_LEVEL_H_ #define ART_RUNTIME_CDEX_COMPACT_DEX_LEVEL_H_ +#include + +#include "base/macros.h" #include "dex_file.h" namespace art { @@ -29,6 +32,19 @@ enum class CompactDexLevel { kCompactDexLevelFast, }; +#ifndef ART_DEFAULT_COMPACT_DEX_LEVEL +#error ART_DEFAULT_COMPACT_DEX_LEVEL not specified. +#else +#define ART_DEFAULT_COMPACT_DEX_LEVEL_VALUE_fast CompactDexLevel::kCompactDexLevelFast +#define ART_DEFAULT_COMPACT_DEX_LEVEL_VALUE_none CompactDexLevel::kCompactDexLevelNone + +#define ART_DEFAULT_COMPACT_DEX_LEVEL_DEFAULT APPEND_TOKENS_AFTER_EVAL( \ + ART_DEFAULT_COMPACT_DEX_LEVEL_VALUE_, \ + ART_DEFAULT_COMPACT_DEX_LEVEL) + +static constexpr CompactDexLevel kDefaultCompactDexLevel = ART_DEFAULT_COMPACT_DEX_LEVEL_DEFAULT; +#endif + } // namespace art #endif // ART_RUNTIME_CDEX_COMPACT_DEX_LEVEL_H_ diff --git a/runtime/cha.cc b/runtime/cha.cc index 6c011e8e39d7f20f0f6d1bfc62c208166e466a0c..a53d7e5b250fd877d34fc0198f9ec2a9f6b07479 100644 --- a/runtime/cha.cc +++ b/runtime/cha.cc @@ -17,6 +17,7 @@ #include "cha.h" #include "art_method-inl.h" +#include "base/logging.h" // For VLOG #include "jit/jit.h" #include "jit/jit_code_cache.h" #include "linear_alloc.h" diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc index c3dd702446de5f8b32c7a9aa6889a43e8d8ea8e1..ce4cee827a5df0de859e8c243a07cc54375eea52 100644 --- a/runtime/check_jni.cc +++ b/runtime/check_jni.cc @@ -21,12 +21,14 @@ #include -#include "android-base/stringprintf.h" +#include +#include #include "art_field-inl.h" #include "art_method-inl.h" -#include "base/logging.h" +#include "base/macros.h" #include "base/to_str.h" +#include "base/time_utils.h" #include "class_linker-inl.h" #include "class_linker.h" #include "dex_file-inl.h" @@ -54,24 +56,37 @@ using android::base::StringPrintf; * =========================================================================== */ +// Warn if a JNI critical is held for longer than 16ms. +static constexpr uint64_t kCriticalWarnTimeUs = MsToUs(16); +static_assert(kCriticalWarnTimeUs > 0, "No JNI critical warn time set"); + // Flags passed into ScopedCheck. -#define kFlag_Default 0x0000 +static constexpr uint16_t kFlag_Default = 0x0000; -#define kFlag_CritBad 0x0000 // Calling while in critical is not allowed. -#define kFlag_CritOkay 0x0001 // Calling while in critical is allowed. -#define kFlag_CritGet 0x0002 // This is a critical "get". -#define kFlag_CritRelease 0x0003 // This is a critical "release". -#define kFlag_CritMask 0x0003 // Bit mask to get "crit" value. +// Calling while in critical is not allowed. +static constexpr uint16_t kFlag_CritBad = 0x0000; +// Calling while in critical is allowed. +static constexpr uint16_t kFlag_CritOkay = 0x0001; +// This is a critical "get". +static constexpr uint16_t kFlag_CritGet = 0x0002; +// This is a critical "release". +static constexpr uint16_t kFlag_CritRelease = 0x0003; +// Bit mask to get "crit" value. +static constexpr uint16_t kFlag_CritMask = 0x0003; -#define kFlag_ExcepBad 0x0000 // Raised exceptions are not allowed. -#define kFlag_ExcepOkay 0x0004 // Raised exceptions are allowed. +// Raised exceptions are allowed. +static constexpr uint16_t kFlag_ExcepOkay = 0x0004; -#define kFlag_Release 0x0010 // Are we in a non-critical release function? -#define kFlag_NullableUtf 0x0020 // Are our UTF parameters nullable? +// Are we in a non-critical release function? +static constexpr uint16_t kFlag_Release = 0x0010; +// Are our UTF parameters nullable? +static constexpr uint16_t kFlag_NullableUtf = 0x0020; -#define kFlag_Invocation 0x8000 // Part of the invocation interface (JavaVM*). +// Part of the invocation interface (JavaVM*). +static constexpr uint16_t kFlag_Invocation = 0x0100; -#define kFlag_ForceTrace 0x80000000 // Add this to a JNI function's flags if you want to trace every call. +// Add this to a JNI function's flags if you want to trace every call. +static constexpr uint16_t kFlag_ForceTrace = 0x8000; class VarArgs; /* @@ -248,8 +263,8 @@ class VarArgs { class ScopedCheck { public: - ScopedCheck(int flags, const char* functionName, bool has_method = true) - : function_name_(functionName), flags_(flags), indent_(0), has_method_(has_method) { + ScopedCheck(uint16_t flags, const char* functionName, bool has_method = true) + : function_name_(functionName), indent_(0), flags_(flags), has_method_(has_method) { } ~ScopedCheck() {} @@ -372,7 +387,7 @@ class ScopedCheck { if (f == nullptr) { return false; } - if (c != f->GetDeclaringClass()) { + if (!f->GetDeclaringClass()->IsAssignableFrom(c)) { AbortF("static jfieldID %p not valid for class %s", fid, mirror::Class::PrettyClass(c).c_str()); return false; @@ -709,7 +724,7 @@ class ScopedCheck { return false; } ObjPtr c = o->AsClass(); - if (c != field->GetDeclaringClass()) { + if (!field->GetDeclaringClass()->IsAssignableFrom(c)) { AbortF("attempt to access static field %s with an incompatible class argument of %s: %p", field->PrettyField().c_str(), mirror::Class::PrettyDescriptor(c).c_str(), fid); return false; @@ -1196,7 +1211,7 @@ class ScopedCheck { // this particular instance of JNIEnv. if (env != threadEnv) { // Get the thread owning the JNIEnv that's being used. - Thread* envThread = reinterpret_cast(env)->self; + Thread* envThread = reinterpret_cast(env)->self_; AbortF("thread %s using JNIEnv* from thread %s", ToStr(*self).c_str(), ToStr(*envThread).c_str()); return false; @@ -1208,7 +1223,7 @@ class ScopedCheck { case kFlag_CritOkay: // okay to call this method break; case kFlag_CritBad: // not okay to call - if (threadEnv->critical) { + if (threadEnv->critical_ > 0) { AbortF("thread %s using JNI after critical get", ToStr(*self).c_str()); return false; @@ -1216,15 +1231,25 @@ class ScopedCheck { break; case kFlag_CritGet: // this is a "get" call // Don't check here; we allow nested gets. - threadEnv->critical++; + if (threadEnv->critical_ == 0) { + threadEnv->critical_start_us_ = self->GetCpuMicroTime(); + } + threadEnv->critical_++; break; case kFlag_CritRelease: // this is a "release" call - threadEnv->critical--; - if (threadEnv->critical < 0) { + if (threadEnv->critical_ == 0) { AbortF("thread %s called too many critical releases", ToStr(*self).c_str()); return false; + } else if (threadEnv->critical_ == 1) { + // Leaving the critical region, possibly warn about long critical regions. + uint64_t critical_duration_us = self->GetCpuMicroTime() - threadEnv->critical_start_us_; + if (critical_duration_us > kCriticalWarnTimeUs) { + LOG(WARNING) << "JNI critical lock held for " + << PrettyDuration(UsToNs(critical_duration_us)) << " on " << *self; + } } + threadEnv->critical_--; break; default: LOG(FATAL) << "Bad flags (internal error): " << flags_; @@ -1356,9 +1381,10 @@ class ScopedCheck { // The name of the JNI function being checked. const char* const function_name_; - const int flags_; int indent_; + const uint16_t flags_; + const bool has_method_; DISALLOW_COPY_AND_ASSIGN(ScopedCheck); @@ -2591,11 +2617,11 @@ class CheckJNI { private: static JavaVMExt* GetJavaVMExt(JNIEnv* env) { - return reinterpret_cast(env)->vm; + return reinterpret_cast(env)->GetVm(); } static const JNINativeInterface* baseEnv(JNIEnv* env) { - return reinterpret_cast(env)->unchecked_functions; + return reinterpret_cast(env)->unchecked_functions_; } static jobject NewRef(const char* function_name, JNIEnv* env, jobject obj, IndirectRefKind kind) { diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h index d9e89159f55e7ba9148d23732937924de3cd54ce..c4a613a9422469c5f155cf6456cf5eaac4a1fb20 100644 --- a/runtime/check_reference_map_visitor.h +++ b/runtime/check_reference_map_visitor.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_CHECK_REFERENCE_MAP_VISITOR_H_ #include "art_method-inl.h" +#include "code_item_accessors-inl.h" #include "dex_file_types.h" #include "oat_quick_method_header.h" #include "scoped_thread_state_change-inl.h" @@ -66,14 +67,15 @@ class CheckReferenceMapVisitor : public StackVisitor { CodeInfo code_info = GetCurrentOatQuickMethodHeader()->GetOptimizedCodeInfo(); CodeInfoEncoding encoding = code_info.ExtractEncoding(); StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding); - uint16_t number_of_dex_registers = m->GetCodeItem()->registers_size_; + CodeItemDataAccessor accessor(m); + uint16_t number_of_dex_registers = accessor.RegistersSize(); DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers); uint32_t register_mask = code_info.GetRegisterMaskOf(encoding, stack_map); BitMemoryRegion stack_mask = code_info.GetStackMaskOf(encoding, stack_map); for (int i = 0; i < number_of_references; ++i) { int reg = registers[i]; - CHECK(reg < m->GetCodeItem()->registers_size_); + CHECK_LT(reg, accessor.RegistersSize()); DexRegisterLocation location = dex_register_map.GetDexRegisterLocation( reg, number_of_dex_registers, code_info, encoding); switch (location.GetKind()) { diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index d6f003027b9e23edba9aa3389c8c0c057ac2a7ce..4b317f886fbca74986a5765e42d1c78bc3a86785 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -61,33 +61,94 @@ inline mirror::Class* ClassLinker::FindArrayClass(Thread* self, return array_class.Ptr(); } -inline ObjPtr ClassLinker::LookupResolvedType( - dex::TypeIndex type_idx, - ObjPtr dex_cache, - ObjPtr class_loader) { - ObjPtr type = dex_cache->GetResolvedType(type_idx); - if (type == nullptr) { - type = Runtime::Current()->GetClassLinker()->LookupResolvedType( - *dex_cache->GetDexFile(), type_idx, dex_cache, class_loader); +inline ObjPtr ClassLinker::ResolveType(dex::TypeIndex type_idx, + ObjPtr referrer) { + if (kObjPtrPoisoning) { + StackHandleScope<1> hs(Thread::Current()); + HandleWrapperObjPtr referrer_wrapper = hs.NewHandleWrapper(&referrer); + Thread::Current()->PoisonObjectPointers(); } - return type; + if (kIsDebugBuild) { + Thread::Current()->AssertNoPendingException(); + } + // We do not need the read barrier for getting the DexCache for the initial resolved type + // lookup as both from-space and to-space copies point to the same native resolved types array. + ObjPtr resolved_type = + referrer->GetDexCache()->GetResolvedType(type_idx); + if (resolved_type == nullptr) { + StackHandleScope<2> hs(Thread::Current()); + Handle h_dex_cache(hs.NewHandle(referrer->GetDexCache())); + Handle class_loader(hs.NewHandle(referrer->GetClassLoader())); + resolved_type = DoResolveType(type_idx, h_dex_cache, class_loader); + } + return resolved_type; } -inline mirror::Class* ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtMethod* referrer) { +inline ObjPtr ClassLinker::ResolveType(dex::TypeIndex type_idx, + ArtMethod* referrer) { Thread::PoisonObjectPointersIfDebug(); if (kIsDebugBuild) { Thread::Current()->AssertNoPendingException(); } - ObjPtr resolved_type = referrer->GetDexCache()->GetResolvedType(type_idx); + // We do not need the read barrier for getting the DexCache for the initial resolved type + // lookup as both from-space and to-space copies point to the same native resolved types array. + ObjPtr resolved_type = + referrer->GetDexCache()->GetResolvedType(type_idx); if (UNLIKELY(resolved_type == nullptr)) { StackHandleScope<2> hs(Thread::Current()); - ObjPtr declaring_class = referrer->GetDeclaringClass(); + ObjPtr referring_class = referrer->GetDeclaringClass(); Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); - Handle class_loader(hs.NewHandle(declaring_class->GetClassLoader())); - const DexFile& dex_file = *dex_cache->GetDexFile(); - resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader); + Handle class_loader(hs.NewHandle(referring_class->GetClassLoader())); + resolved_type = DoResolveType(type_idx, dex_cache, class_loader); + } + return resolved_type; +} + +inline ObjPtr ClassLinker::ResolveType(dex::TypeIndex type_idx, + Handle dex_cache, + Handle class_loader) { + DCHECK(dex_cache != nullptr); + Thread::PoisonObjectPointersIfDebug(); + ObjPtr resolved = dex_cache->GetResolvedType(type_idx); + if (resolved == nullptr) { + resolved = DoResolveType(type_idx, dex_cache, class_loader); + } + return resolved; +} + +inline ObjPtr ClassLinker::LookupResolvedType(dex::TypeIndex type_idx, + ObjPtr referrer) { + // We do not need the read barrier for getting the DexCache for the initial resolved type + // lookup as both from-space and to-space copies point to the same native resolved types array. + ObjPtr type = + referrer->GetDexCache()->GetResolvedType(type_idx); + if (type == nullptr) { + type = DoLookupResolvedType(type_idx, referrer->GetDexCache(), referrer->GetClassLoader()); + } + return type; +} + +inline ObjPtr ClassLinker::LookupResolvedType(dex::TypeIndex type_idx, + ArtMethod* referrer) { + // We do not need the read barrier for getting the DexCache for the initial resolved type + // lookup as both from-space and to-space copies point to the same native resolved types array. + ObjPtr type = + referrer->GetDexCache()->GetResolvedType(type_idx); + if (type == nullptr) { + type = DoLookupResolvedType(type_idx, referrer->GetDexCache(), referrer->GetClassLoader()); + } + return type; +} + +inline ObjPtr ClassLinker::LookupResolvedType( + dex::TypeIndex type_idx, + ObjPtr dex_cache, + ObjPtr class_loader) { + ObjPtr type = dex_cache->GetResolvedType(type_idx); + if (type == nullptr) { + type = DoLookupResolvedType(type_idx, dex_cache, class_loader); } - return resolved_type.Ptr(); + return type; } template @@ -147,10 +208,9 @@ inline bool ClassLinker::CheckInvokeClassMismatch(ObjPtr dex_c dex_cache, type, [this, dex_cache, method_idx, class_loader]() REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile& dex_file = *dex_cache->GetDexFile(); - const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx); + const DexFile::MethodId& method_id = dex_cache->GetDexFile()->GetMethodId(method_idx); ObjPtr klass = - LookupResolvedType(dex_file, method_id.class_idx_, dex_cache, class_loader); + LookupResolvedType(method_id.class_idx_, dex_cache, class_loader); DCHECK(klass != nullptr); return klass; }); @@ -186,6 +246,8 @@ inline ArtMethod* ClassLinker::GetResolvedMethod(uint32_t method_idx, ArtMethod* // lookup in the context of the original method from where it steals the code. // However, we delay the GetInterfaceMethodIfProxy() until needed. DCHECK(!referrer->IsProxyMethod() || referrer->IsConstructor()); + // We do not need the read barrier for getting the DexCache for the initial resolved method + // lookup as both from-space and to-space copies point to the same native resolved methods array. ArtMethod* resolved_method = referrer->GetDexCache()->GetResolvedMethod( method_idx, image_pointer_size_); if (resolved_method == nullptr) { @@ -227,6 +289,8 @@ inline ArtMethod* ClassLinker::ResolveMethod(Thread* self, // However, we delay the GetInterfaceMethodIfProxy() until needed. DCHECK(!referrer->IsProxyMethod() || referrer->IsConstructor()); Thread::PoisonObjectPointersIfDebug(); + // We do not need the read barrier for getting the DexCache for the initial resolved method + // lookup as both from-space and to-space copies point to the same native resolved methods array. ArtMethod* resolved_method = referrer->GetDexCache()->GetResolvedMethod( method_idx, image_pointer_size_); DCHECK(resolved_method == nullptr || !resolved_method->IsRuntimeMethod()); @@ -236,9 +300,7 @@ inline ArtMethod* ClassLinker::ResolveMethod(Thread* self, StackHandleScope<2> hs(self); Handle h_dex_cache(hs.NewHandle(referrer->GetDexCache())); Handle h_class_loader(hs.NewHandle(declaring_class->GetClassLoader())); - const DexFile* dex_file = h_dex_cache->GetDexFile(); - resolved_method = ResolveMethod(*dex_file, - method_idx, + resolved_method = ResolveMethod(method_idx, h_dex_cache, h_class_loader, referrer, @@ -279,10 +341,13 @@ inline ArtMethod* ClassLinker::ResolveMethod(Thread* self, inline ArtField* ClassLinker::LookupResolvedField(uint32_t field_idx, ArtMethod* referrer, bool is_static) { - ObjPtr dex_cache = referrer->GetDexCache(); - ArtField* field = dex_cache->GetResolvedField(field_idx, image_pointer_size_); + // We do not need the read barrier for getting the DexCache for the initial resolved field + // lookup as both from-space and to-space copies point to the same native resolved fields array. + ArtField* field = referrer->GetDexCache()->GetResolvedField( + field_idx, image_pointer_size_); if (field == nullptr) { - field = LookupResolvedField(field_idx, dex_cache, referrer->GetClassLoader(), is_static); + ObjPtr class_loader = referrer->GetDeclaringClass()->GetClassLoader(); + field = LookupResolvedField(field_idx, referrer->GetDexCache(), class_loader, is_static); } return field; } @@ -291,15 +356,16 @@ inline ArtField* ClassLinker::ResolveField(uint32_t field_idx, ArtMethod* referrer, bool is_static) { Thread::PoisonObjectPointersIfDebug(); - ObjPtr declaring_class = referrer->GetDeclaringClass(); - ArtField* resolved_field = - referrer->GetDexCache()->GetResolvedField(field_idx, image_pointer_size_); + // We do not need the read barrier for getting the DexCache for the initial resolved field + // lookup as both from-space and to-space copies point to the same native resolved fields array. + ArtField* resolved_field = referrer->GetDexCache()->GetResolvedField( + field_idx, image_pointer_size_); if (UNLIKELY(resolved_field == nullptr)) { StackHandleScope<2> hs(Thread::Current()); + ObjPtr referring_class = referrer->GetDeclaringClass(); Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); - Handle class_loader(hs.NewHandle(declaring_class->GetClassLoader())); - const DexFile& dex_file = *dex_cache->GetDexFile(); - resolved_field = ResolveField(dex_file, field_idx, dex_cache, class_loader, is_static); + Handle class_loader(hs.NewHandle(referring_class->GetClassLoader())); + resolved_field = ResolveField(field_idx, dex_cache, class_loader, is_static); // Note: We cannot check here to see whether we added the field to the cache. The type // might be an erroneous class, which results in it being hidden from us. } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 28caf81e5b209ab93b1a13b4c79cb9ac12676ed8..24fcce49cba0f315f2ea15a7aebe23c0cdfcd568 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -3340,6 +3340,11 @@ void ClassLinker::LoadMethod(const DexFile& dex_file, } } } + if (UNLIKELY((access_flags & kAccNative) != 0u)) { + // Check if the native method is annotated with @FastNative or @CriticalNative. + access_flags |= annotations::GetNativeMethodAnnotationAccessFlags( + dex_file, dst->GetClassDef(), dex_method_idx); + } dst->SetAccessFlags(access_flags); } @@ -3386,7 +3391,7 @@ void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file, // Clean up pass to remove null dex caches. Also check if we need to initialize OatFile .bss. // Null dex caches can occur due to class unloading and we are lazily removing null entries. bool initialize_oat_file_bss = (oat_file != nullptr); - JavaVMExt* const vm = self->GetJniEnv()->vm; + JavaVMExt* const vm = self->GetJniEnv()->GetVm(); for (auto it = dex_caches_.begin(); it != dex_caches_.end(); ) { DexCacheData data = *it; if (self->IsJWeakCleared(data.weak_root)) { @@ -4312,15 +4317,14 @@ void ClassLinker::ResolveClassExceptionHandlerTypes(Handle klass) void ClassLinker::ResolveMethodExceptionHandlerTypes(ArtMethod* method) { // similar to DexVerifier::ScanTryCatchBlocks and dex2oat's ResolveExceptionsForMethod. - const DexFile::CodeItem* code_item = - method->GetDexFile()->GetCodeItem(method->GetCodeItemOffset()); - if (code_item == nullptr) { + CodeItemDataAccessor accessor(method); + if (!accessor.HasCodeItem()) { return; // native or abstract method } - if (code_item->tries_size_ == 0) { + if (accessor.TriesSize() == 0) { return; // nothing to process } - const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(*code_item, 0); + const uint8_t* handlers_ptr = accessor.GetCatchHandlerData(0); uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr); for (uint32_t idx = 0; idx < handlers_size; idx++) { CatchHandlerIterator iterator(handlers_ptr); @@ -4510,7 +4514,7 @@ std::string ClassLinker::GetDescriptorForProxy(ObjPtr proxy_class void ClassLinker::CreateProxyConstructor(Handle klass, ArtMethod* out) { // Create constructor for Proxy that must initialize the method. - CHECK_EQ(GetClassRoot(kJavaLangReflectProxy)->NumDirectMethods(), 23u); + CHECK_EQ(GetClassRoot(kJavaLangReflectProxy)->NumDirectMethods(), 21u); // Find the (InvocationHandler)V method. The exact method offset varies depending // on which front-end compiler was used to build the libcore DEX files. @@ -4813,7 +4817,6 @@ bool ClassLinker::InitializeClass(Thread* self, Handle klass, if (num_static_fields > 0) { const DexFile::ClassDef* dex_class_def = klass->GetClassDef(); CHECK(dex_class_def != nullptr); - const DexFile& dex_file = klass->GetDexFile(); StackHandleScope<3> hs(self); Handle class_loader(hs.NewHandle(klass->GetClassLoader())); Handle dex_cache(hs.NewHandle(klass->GetDexCache())); @@ -4831,11 +4834,11 @@ bool ClassLinker::InitializeClass(Thread* self, Handle klass, } } - annotations::RuntimeEncodedStaticFieldValueIterator value_it(dex_file, - &dex_cache, - &class_loader, + annotations::RuntimeEncodedStaticFieldValueIterator value_it(dex_cache, + class_loader, this, *dex_class_def); + const DexFile& dex_file = *dex_cache->GetDexFile(); const uint8_t* class_data = dex_file.GetClassData(*dex_class_def); ClassDataItemIterator field_it(dex_file, class_data); if (value_it.HasNext()) { @@ -4843,7 +4846,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle klass, CHECK(can_init_statics); for ( ; value_it.HasNext(); value_it.Next(), field_it.Next()) { ArtField* field = ResolveField( - dex_file, field_it.GetMemberIndex(), dex_cache, class_loader, true); + field_it.GetMemberIndex(), dex_cache, class_loader, /* is_static */ true); if (Runtime::Current()->IsActiveTransaction()) { value_it.ReadValueToField(field); } else { @@ -5265,7 +5268,7 @@ void ClassLinker::RegisterClassLoader(ObjPtr class_loader) CHECK(class_loader->GetClassTable() == nullptr); Thread* const self = Thread::Current(); ClassLoaderData data; - data.weak_root = self->GetJniEnv()->vm->AddWeakGlobalRef(self, class_loader); + data.weak_root = self->GetJniEnv()->GetVm()->AddWeakGlobalRef(self, class_loader); // Create and set the class table. data.class_table = new ClassTable; class_loader->SetClassTable(data.class_table); @@ -5462,7 +5465,7 @@ bool ClassLinker::LoadSuperAndInterfaces(Handle klass, const DexF return false; } - ObjPtr super_class = ResolveType(dex_file, super_class_idx, klass.Get()); + ObjPtr super_class = ResolveType(super_class_idx, klass.Get()); if (super_class == nullptr) { DCHECK(Thread::Current()->IsExceptionPending()); return false; @@ -5481,7 +5484,7 @@ bool ClassLinker::LoadSuperAndInterfaces(Handle klass, const DexF if (interfaces != nullptr) { for (size_t i = 0; i < interfaces->Size(); i++) { dex::TypeIndex idx = interfaces->GetTypeItem(i).type_idx_; - ObjPtr interface = ResolveType(dex_file, idx, klass.Get()); + ObjPtr interface = ResolveType(idx, klass.Get()); if (interface == nullptr) { DCHECK(Thread::Current()->IsExceptionPending()); return false; @@ -5518,6 +5521,10 @@ bool ClassLinker::LinkSuperClass(Handle klass) { return false; } // Verify + if (klass->IsInterface() && super != GetClassRoot(kJavaLangObject)) { + ThrowClassFormatError(klass.Get(), "Interfaces must have java.lang.Object as superclass"); + return false; + } if (super->IsFinal()) { ThrowVerifyError(klass.Get(), "Superclass %s of %s is declared final", @@ -7048,6 +7055,7 @@ void ClassLinker::LinkInterfaceMethodsHelper::ReallocMethods() { // verified yet it shouldn't have methods that are skipping access checks. // TODO This is rather arbitrary. We should maybe support classes where only some of its // methods are skip_access_checks. + DCHECK_EQ(new_method.GetAccessFlags() & kAccNative, 0u); constexpr uint32_t kSetFlags = kAccDefault | kAccCopied; constexpr uint32_t kMaskFlags = ~kAccSkipAccessChecks; new_method.SetAccessFlags((new_method.GetAccessFlags() | kSetFlags) & kMaskFlags); @@ -7070,6 +7078,7 @@ void ClassLinker::LinkInterfaceMethodsHelper::ReallocMethods() { // mark this as a default, non-abstract method, since thats what it is. Also clear the // kAccSkipAccessChecks bit since this class hasn't been verified yet it shouldn't have // methods that are skipping access checks. + DCHECK_EQ(new_method.GetAccessFlags() & kAccNative, 0u); constexpr uint32_t kSetFlags = kAccDefault | kAccDefaultConflict | kAccCopied; constexpr uint32_t kMaskFlags = ~(kAccAbstract | kAccSkipAccessChecks); new_method.SetAccessFlags((new_method.GetAccessFlags() | kSetFlags) & kMaskFlags); @@ -7716,32 +7725,32 @@ void ClassLinker::CreateReferenceInstanceOffsets(Handle klass) { klass->SetReferenceInstanceOffsets(reference_offsets); } -mirror::String* ClassLinker::ResolveString(const DexFile& dex_file, - dex::StringIndex string_idx, - Handle dex_cache) { +ObjPtr ClassLinker::ResolveString(dex::StringIndex string_idx, + Handle dex_cache) { DCHECK(dex_cache != nullptr); Thread::PoisonObjectPointersIfDebug(); ObjPtr resolved = dex_cache->GetResolvedString(string_idx); if (resolved != nullptr) { - return resolved.Ptr(); + return resolved; } + const DexFile& dex_file = *dex_cache->GetDexFile(); uint32_t utf16_length; const char* utf8_data = dex_file.StringDataAndUtf16LengthByIdx(string_idx, &utf16_length); ObjPtr string = intern_table_->InternStrong(utf16_length, utf8_data); if (string != nullptr) { dex_cache->SetResolvedString(string_idx, string); } - return string.Ptr(); + return string; } -mirror::String* ClassLinker::LookupString(const DexFile& dex_file, - dex::StringIndex string_idx, - ObjPtr dex_cache) { +ObjPtr ClassLinker::LookupString(dex::StringIndex string_idx, + ObjPtr dex_cache) { DCHECK(dex_cache != nullptr); ObjPtr resolved = dex_cache->GetResolvedString(string_idx); if (resolved != nullptr) { - return resolved.Ptr(); + return resolved; } + const DexFile& dex_file = *dex_cache->GetDexFile(); uint32_t utf16_length; const char* utf8_data = dex_file.StringDataAndUtf16LengthByIdx(string_idx, &utf16_length); ObjPtr string = @@ -7749,87 +7758,168 @@ mirror::String* ClassLinker::LookupString(const DexFile& dex_file, if (string != nullptr) { dex_cache->SetResolvedString(string_idx, string); } - return string.Ptr(); + return string; } -ObjPtr ClassLinker::LookupResolvedType(const DexFile& dex_file, - dex::TypeIndex type_idx, - ObjPtr dex_cache, - ObjPtr class_loader) { - ObjPtr type = dex_cache->GetResolvedType(type_idx); - if (type == nullptr) { - const char* descriptor = dex_file.StringByTypeIdx(type_idx); - DCHECK_NE(*descriptor, '\0') << "descriptor is empty string"; - if (descriptor[1] == '\0') { - // only the descriptors of primitive types should be 1 character long, also avoid class lookup - // for primitive classes that aren't backed by dex files. - type = FindPrimitiveClass(descriptor[0]); +ObjPtr ClassLinker::DoLookupResolvedType(dex::TypeIndex type_idx, + ObjPtr dex_cache, + ObjPtr class_loader) { + const DexFile& dex_file = *dex_cache->GetDexFile(); + const char* descriptor = dex_file.StringByTypeIdx(type_idx); + DCHECK_NE(*descriptor, '\0') << "descriptor is empty string"; + ObjPtr type = nullptr; + if (descriptor[1] == '\0') { + // only the descriptors of primitive types should be 1 character long, also avoid class lookup + // for primitive classes that aren't backed by dex files. + type = FindPrimitiveClass(descriptor[0]); + } else { + Thread* const self = Thread::Current(); + DCHECK(self != nullptr); + const size_t hash = ComputeModifiedUtf8Hash(descriptor); + // Find the class in the loaded classes table. + type = LookupClass(self, descriptor, hash, class_loader.Ptr()); + } + if (type != nullptr) { + if (type->IsResolved()) { + dex_cache->SetResolvedType(type_idx, type); } else { - Thread* const self = Thread::Current(); - DCHECK(self != nullptr); - const size_t hash = ComputeModifiedUtf8Hash(descriptor); - // Find the class in the loaded classes table. - type = LookupClass(self, descriptor, hash, class_loader.Ptr()); - } - if (type != nullptr) { - if (type->IsResolved()) { - dex_cache->SetResolvedType(type_idx, type); - } else { - type = nullptr; - } + type = nullptr; } } - DCHECK(type == nullptr || type->IsResolved()); return type; } -mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, - dex::TypeIndex type_idx, - ObjPtr referrer) { - StackHandleScope<2> hs(Thread::Current()); - Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); - Handle class_loader(hs.NewHandle(referrer->GetClassLoader())); - return ResolveType(dex_file, type_idx, dex_cache, class_loader); +ObjPtr ClassLinker::DoResolveType(dex::TypeIndex type_idx, + Handle dex_cache, + Handle class_loader) { + Thread* self = Thread::Current(); + const char* descriptor = dex_cache->GetDexFile()->StringByTypeIdx(type_idx); + ObjPtr resolved = FindClass(self, descriptor, class_loader); + if (resolved != nullptr) { + // TODO: we used to throw here if resolved's class loader was not the + // boot class loader. This was to permit different classes with the + // same name to be loaded simultaneously by different loaders + dex_cache->SetResolvedType(type_idx, resolved); + } else { + CHECK(self->IsExceptionPending()) + << "Expected pending exception for failed resolution of: " << descriptor; + // Convert a ClassNotFoundException to a NoClassDefFoundError. + StackHandleScope<1> hs(self); + Handle cause(hs.NewHandle(self->GetException())); + if (cause->InstanceOf(GetClassRoot(kJavaLangClassNotFoundException))) { + DCHECK(resolved == nullptr); // No Handle needed to preserve resolved. + self->ClearException(); + ThrowNoClassDefFoundError("Failed resolution of: %s", descriptor); + self->GetException()->SetCause(cause.Get()); + } + } + DCHECK((resolved == nullptr) || resolved->IsResolved()) + << resolved->PrettyDescriptor() << " " << resolved->GetStatus(); + return resolved; } -mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, - dex::TypeIndex type_idx, - Handle dex_cache, - Handle class_loader) { - DCHECK(dex_cache != nullptr); - Thread::PoisonObjectPointersIfDebug(); - ObjPtr resolved = dex_cache->GetResolvedType(type_idx); - if (resolved == nullptr) { - Thread* self = Thread::Current(); - const char* descriptor = dex_file.StringByTypeIdx(type_idx); - resolved = FindClass(self, descriptor, class_loader); - if (resolved != nullptr) { - // TODO: we used to throw here if resolved's class loader was not the - // boot class loader. This was to permit different classes with the - // same name to be loaded simultaneously by different loaders - dex_cache->SetResolvedType(type_idx, resolved); +std::string DescribeSpace(ObjPtr klass) REQUIRES_SHARED(Locks::mutator_lock_) { + std::ostringstream oss; + gc::Heap* heap = Runtime::Current()->GetHeap(); + gc::space::ContinuousSpace* cs = heap->FindContinuousSpaceFromAddress(klass.Ptr()); + if (cs != nullptr) { + if (cs->IsImageSpace()) { + oss << "image;" << cs->GetName() << ";" << cs->AsImageSpace()->GetImageFilename(); } else { - CHECK(self->IsExceptionPending()) - << "Expected pending exception for failed resolution of: " << descriptor; - // Convert a ClassNotFoundException to a NoClassDefFoundError. - StackHandleScope<1> hs(self); - Handle cause(hs.NewHandle(self->GetException())); - if (cause->InstanceOf(GetClassRoot(kJavaLangClassNotFoundException))) { - DCHECK(resolved == nullptr); // No Handle needed to preserve resolved. - self->ClearException(); - ThrowNoClassDefFoundError("Failed resolution of: %s", descriptor); - self->GetException()->SetCause(cause.Get()); + oss << "continuous;" << cs->GetName(); + } + } else { + gc::space::DiscontinuousSpace* ds = + heap->FindDiscontinuousSpaceFromObject(klass, /* fail_ok */ true); + if (ds != nullptr) { + oss << "discontinuous;" << ds->GetName(); + } else { + oss << "invalid"; + } + } + return oss.str(); +} + +std::string DescribeLoaders(ObjPtr loader, const char* class_descriptor) + REQUIRES_SHARED(Locks::mutator_lock_) { + std::ostringstream oss; + uint32_t hash = ComputeModifiedUtf8Hash(class_descriptor); + ObjPtr path_class_loader = + WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_PathClassLoader); + ObjPtr dex_class_loader = + WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DexClassLoader); + ObjPtr delegate_last_class_loader = + WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DelegateLastClassLoader); + + // Print the class loader chain. + bool found_class = false; + const char* loader_separator = ""; + if (loader == nullptr) { + oss << "BootClassLoader"; // This would be unexpected. + } + for (; loader != nullptr; loader = loader->GetParent()) { + oss << loader_separator << loader->GetClass()->PrettyDescriptor(); + loader_separator = ";"; + // If we didn't find the interface yet, try to find it in the current class loader. + if (!found_class) { + ClassTable* table = Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(loader); + ObjPtr klass = + (table != nullptr) ? table->Lookup(class_descriptor, hash) : nullptr; + if (klass != nullptr) { + found_class = true; + oss << "[hit:" << DescribeSpace(klass) << "]"; + } + } + + // For PathClassLoader, DexClassLoader or DelegateLastClassLoader + // also dump the dex file locations. + if (loader->GetClass() == path_class_loader || + loader->GetClass() == dex_class_loader || + loader->GetClass() == delegate_last_class_loader) { + ArtField* const cookie_field = + jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie); + ArtField* const dex_file_field = + jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile); + ObjPtr dex_path_list = + jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)-> + GetObject(loader); + if (dex_path_list != nullptr && dex_file_field != nullptr && cookie_field != nullptr) { + ObjPtr dex_elements_obj = + jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)-> + GetObject(dex_path_list); + if (dex_elements_obj != nullptr) { + ObjPtr> dex_elements = + dex_elements_obj->AsObjectArray(); + oss << "("; + const char* path_separator = ""; + for (int32_t i = 0; i != dex_elements->GetLength(); ++i) { + ObjPtr element = dex_elements->GetWithoutChecks(i); + ObjPtr dex_file = + (element != nullptr) ? dex_file_field->GetObject(element) : nullptr; + ObjPtr long_array = + (dex_file != nullptr) ? cookie_field->GetObject(dex_file)->AsLongArray() : nullptr; + if (long_array != nullptr) { + int32_t long_array_size = long_array->GetLength(); + // First element is the oat file. + for (int32_t j = kDexFileIndexStart; j < long_array_size; ++j) { + const DexFile* cp_dex_file = reinterpret_cast( + static_cast(long_array->GetWithoutChecks(j))); + oss << path_separator << cp_dex_file->GetLocation(); + path_separator = ":"; + } + } + } + oss << ")"; + } } } } - DCHECK((resolved == nullptr) || resolved->IsResolved()) - << resolved->PrettyDescriptor() << " " << resolved->GetStatus(); - return resolved.Ptr(); + + return oss.str(); } template -ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, - uint32_t method_idx, +ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx, Handle dex_cache, Handle class_loader, ArtMethod* referrer, @@ -7847,17 +7937,24 @@ ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex(); return resolved; } + const DexFile& dex_file = *dex_cache->GetDexFile(); const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx); ObjPtr klass = nullptr; if (valid_dex_cache_method) { // We have a valid method from the DexCache but we need to perform ICCE and IAE checks. DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex(); - klass = LookupResolvedType(dex_file, method_id.class_idx_, dex_cache.Get(), class_loader.Get()); - CHECK(klass != nullptr) << resolved->PrettyMethod() << " " << resolved << " " - << resolved->GetAccessFlags(); + klass = LookupResolvedType(method_id.class_idx_, dex_cache.Get(), class_loader.Get()); + if (UNLIKELY(klass == nullptr)) { + const char* descriptor = dex_file.StringByTypeIdx(method_id.class_idx_); + LOG(FATAL) << "Check failed: klass != nullptr Bug: 64759619 Method: " + << resolved->PrettyMethod() << ";" << resolved + << "/0x" << std::hex << resolved->GetAccessFlags() + << " ReferencedClass: " << descriptor + << " ClassLoader: " << DescribeLoaders(class_loader.Get(), descriptor); + } } else { // The method was not in the DexCache, resolve the declaring class. - klass = ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader); + klass = ResolveType(method_id.class_idx_, dex_cache, class_loader); if (klass == nullptr) { DCHECK(Thread::Current()->IsExceptionPending()); return nullptr; @@ -7932,8 +8029,7 @@ ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, } } -ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(const DexFile& dex_file, - uint32_t method_idx, +ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(uint32_t method_idx, Handle dex_cache, Handle class_loader) { ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, image_pointer_size_); @@ -7944,9 +8040,8 @@ ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(const DexFile& dex_file, return resolved; } // Fail, get the declaring class. - const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx); - ObjPtr klass = - ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader); + const DexFile::MethodId& method_id = dex_cache->GetDexFile()->GetMethodId(method_idx); + ObjPtr klass = ResolveType(method_id.class_idx_, dex_cache, class_loader); if (klass == nullptr) { Thread::Current()->AssertPendingException(); return nullptr; @@ -7968,7 +8063,7 @@ ArtField* ClassLinker::LookupResolvedField(uint32_t field_idx, const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx); ObjPtr klass = dex_cache->GetResolvedType(field_id.class_idx_); if (klass == nullptr) { - klass = LookupResolvedType(dex_file, field_id.class_idx_, dex_cache, class_loader); + klass = LookupResolvedType(field_id.class_idx_, dex_cache, class_loader); } if (klass == nullptr) { // The class has not been resolved yet, so the field is also unresolved. @@ -7997,8 +8092,7 @@ ArtField* ClassLinker::LookupResolvedField(uint32_t field_idx, return resolved_field; } -ArtField* ClassLinker::ResolveField(const DexFile& dex_file, - uint32_t field_idx, +ArtField* ClassLinker::ResolveField(uint32_t field_idx, Handle dex_cache, Handle class_loader, bool is_static) { @@ -8008,9 +8102,10 @@ ArtField* ClassLinker::ResolveField(const DexFile& dex_file, if (resolved != nullptr) { return resolved; } + const DexFile& dex_file = *dex_cache->GetDexFile(); const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx); Thread* const self = Thread::Current(); - ObjPtr klass = ResolveType(dex_file, field_id.class_idx_, dex_cache, class_loader); + ObjPtr klass = ResolveType(field_id.class_idx_, dex_cache, class_loader); if (klass == nullptr) { DCHECK(Thread::Current()->IsExceptionPending()); return nullptr; @@ -8039,8 +8134,7 @@ ArtField* ClassLinker::ResolveField(const DexFile& dex_file, return resolved; } -ArtField* ClassLinker::ResolveFieldJLS(const DexFile& dex_file, - uint32_t field_idx, +ArtField* ClassLinker::ResolveFieldJLS(uint32_t field_idx, Handle dex_cache, Handle class_loader) { DCHECK(dex_cache != nullptr); @@ -8049,9 +8143,10 @@ ArtField* ClassLinker::ResolveFieldJLS(const DexFile& dex_file, if (resolved != nullptr) { return resolved; } + const DexFile& dex_file = *dex_cache->GetDexFile(); const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx); Thread* self = Thread::Current(); - ObjPtr klass(ResolveType(dex_file, field_id.class_idx_, dex_cache, class_loader)); + ObjPtr klass = ResolveType(field_id.class_idx_, dex_cache, class_loader); if (klass == nullptr) { DCHECK(Thread::Current()->IsExceptionPending()); return nullptr; @@ -8068,11 +8163,11 @@ ArtField* ClassLinker::ResolveFieldJLS(const DexFile& dex_file, return resolved; } -mirror::MethodType* ClassLinker::ResolveMethodType(Thread* self, - const DexFile& dex_file, - uint32_t proto_idx, - Handle dex_cache, - Handle class_loader) { +ObjPtr ClassLinker::ResolveMethodType( + Thread* self, + uint32_t proto_idx, + Handle dex_cache, + Handle class_loader) { DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); DCHECK(dex_cache != nullptr); @@ -8084,9 +8179,10 @@ mirror::MethodType* ClassLinker::ResolveMethodType(Thread* self, StackHandleScope<4> hs(self); // First resolve the return type. + const DexFile& dex_file = *dex_cache->GetDexFile(); const DexFile::ProtoId& proto_id = dex_file.GetProtoId(proto_idx); Handle return_type(hs.NewHandle( - ResolveType(dex_file, proto_id.return_type_idx_, dex_cache, class_loader))); + ResolveType(proto_id.return_type_idx_, dex_cache, class_loader))); if (return_type == nullptr) { DCHECK(self->IsExceptionPending()); return nullptr; @@ -8112,7 +8208,7 @@ mirror::MethodType* ClassLinker::ResolveMethodType(Thread* self, MutableHandle param_class = hs.NewHandle(nullptr); for (; it.HasNext(); it.Next()) { const dex::TypeIndex type_idx = it.GetTypeIdx(); - param_class.Assign(ResolveType(dex_file, type_idx, dex_cache, class_loader)); + param_class.Assign(ResolveType(type_idx, dex_cache, class_loader)); if (param_class == nullptr) { DCHECK(self->IsExceptionPending()); return nullptr; @@ -8130,14 +8226,13 @@ mirror::MethodType* ClassLinker::ResolveMethodType(Thread* self, return type.Get(); } -mirror::MethodType* ClassLinker::ResolveMethodType(Thread* self, - uint32_t proto_idx, - ArtMethod* referrer) { +ObjPtr ClassLinker::ResolveMethodType(Thread* self, + uint32_t proto_idx, + ArtMethod* referrer) { StackHandleScope<2> hs(self); - const DexFile* dex_file = referrer->GetDexFile(); Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); Handle class_loader(hs.NewHandle(referrer->GetClassLoader())); - return ResolveMethodType(self, *dex_file, proto_idx, dex_cache, class_loader); + return ResolveMethodType(self, proto_idx, dex_cache, class_loader); } mirror::MethodHandle* ClassLinker::ResolveMethodHandleForField( @@ -8315,8 +8410,7 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForMethod( // the invocation type to determine if the method is private. We // then resolve again specifying the intended invocation type to // force the appropriate checks. - target_method = ResolveMethodWithoutInvokeType(*dex_file, - method_handle.field_or_method_idx_, + target_method = ResolveMethodWithoutInvokeType(method_handle.field_or_method_idx_, hs.NewHandle(referrer->GetDexCache()), hs.NewHandle(referrer->GetClassLoader())); if (UNLIKELY(target_method == nullptr)) { @@ -8399,7 +8493,7 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForMethod( DexFileParameterIterator it(*dex_file, target_method->GetPrototype()); while (it.HasNext()) { const dex::TypeIndex type_idx = it.GetTypeIdx(); - mirror::Class* klass = ResolveType(*dex_file, type_idx, dex_cache, class_loader); + ObjPtr klass = ResolveType(type_idx, dex_cache, class_loader); if (nullptr == klass) { DCHECK(self->IsExceptionPending()); return nullptr; @@ -8432,9 +8526,9 @@ mirror::MethodHandle* ClassLinker::ResolveMethodHandleForMethod( return mirror::MethodHandleImpl::Create(self, target, kind, method_type); } -mirror::MethodHandle* ClassLinker::ResolveMethodHandle(Thread* self, - uint32_t method_handle_idx, - ArtMethod* referrer) +ObjPtr ClassLinker::ResolveMethodHandle(Thread* self, + uint32_t method_handle_idx, + ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_) { const DexFile* const dex_file = referrer->GetDexFile(); const DexFile::MethodHandleItem& method_handle = dex_file->GetMethodHandle(method_handle_idx); @@ -8648,10 +8742,10 @@ jobject ClassLinker::CreateWellKnownClassLoader(Thread* self, DCHECK_EQ(h_dex_element_class.Get(), element_file_field->GetDeclaringClass()); ArtField* cookie_field = jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie); - DCHECK_EQ(cookie_field->GetDeclaringClass(), element_file_field->LookupType()); + DCHECK_EQ(cookie_field->GetDeclaringClass(), element_file_field->LookupResolvedType()); ArtField* file_name_field = jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_fileName); - DCHECK_EQ(file_name_field->GetDeclaringClass(), element_file_field->LookupType()); + DCHECK_EQ(file_name_field->GetDeclaringClass(), element_file_field->LookupResolvedType()); // Fill the elements array. int32_t index = 0; @@ -8917,14 +9011,12 @@ mirror::IfTable* ClassLinker::AllocIfTable(Thread* self, size_t ifcount) { // Instantiate ResolveMethod. template ArtMethod* ClassLinker::ResolveMethod( - const DexFile& dex_file, uint32_t method_idx, Handle dex_cache, Handle class_loader, ArtMethod* referrer, InvokeType type); template ArtMethod* ClassLinker::ResolveMethod( - const DexFile& dex_file, uint32_t method_idx, Handle dex_cache, Handle class_loader, diff --git a/runtime/class_linker.h b/runtime/class_linker.h index a4c4f3d9abc272d6cf00b19d3915e9c3169f215e..10562f0890e52cc26e5dcc7dc89755dc54df9bf2 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -243,58 +243,61 @@ class ClassLinker { REQUIRES(!Locks::classlinker_classes_lock_) REQUIRES_SHARED(Locks::mutator_lock_); - // Resolve a String with the given index from the DexFile, storing the - // result in the DexCache. - mirror::String* ResolveString(const DexFile& dex_file, - dex::StringIndex string_idx, - Handle dex_cache) - REQUIRES_SHARED(Locks::mutator_lock_); - - // Find a String with the given index from the DexFile, storing the - // result in the DexCache if found. Return null if not found. - mirror::String* LookupString(const DexFile& dex_file, - dex::StringIndex string_idx, - ObjPtr dex_cache) - REQUIRES_SHARED(Locks::mutator_lock_); - - // Resolve a Type with the given index from the DexFile, storing the - // result in the DexCache. The referrer is used to identify the - // target DexCache and ClassLoader to use for resolution. - mirror::Class* ResolveType(const DexFile& dex_file, - dex::TypeIndex type_idx, - ObjPtr referrer) + // Resolve a String with the given index from the DexFile associated with the given DexCache, + // storing the result in the DexCache. + ObjPtr ResolveString(dex::StringIndex string_idx, + Handle dex_cache) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Find a String with the given index from the DexFile associated with the given DexCache, + // storing the result in the DexCache if found. Return null if not found. + ObjPtr LookupString(dex::StringIndex string_idx, + ObjPtr dex_cache) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Resolve a Type with the given index from the DexFile associated with the given `referrer`, + // storing the result in the DexCache. The `referrer` is used to identify the target DexCache + // and ClassLoader to use for resolution. + ObjPtr ResolveType(dex::TypeIndex type_idx, ObjPtr referrer) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); - // Resolve a Type with the given index from the DexFile, storing the - // result in the DexCache. The referrer is used to identify the - // target DexCache and ClassLoader to use for resolution. - mirror::Class* ResolveType(dex::TypeIndex type_idx, ArtMethod* referrer) + // Resolve a type with the given index from the DexFile associated with the given `referrer`, + // storing the result in the DexCache. The `referrer` is used to identify the target DexCache + // and ClassLoader to use for resolution. + ObjPtr ResolveType(dex::TypeIndex type_idx, ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); - // Look up a resolved type with the given ID from the DexFile. The ClassLoader is used to search - // for the type, since it may be referenced from but not contained within the given DexFile. - ObjPtr LookupResolvedType(const DexFile& dex_file, - dex::TypeIndex type_idx, - ObjPtr dex_cache, - ObjPtr class_loader) + // Resolve a type with the given index from the DexFile associated with the given DexCache + // and ClassLoader, storing the result in DexCache. The ClassLoader is used to search for + // the type, since it may be referenced from but not contained within the DexFile. + ObjPtr ResolveType(dex::TypeIndex type_idx, + Handle dex_cache, + Handle class_loader) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); + + // Look up a resolved type with the given index from the DexFile associated with the given + // `referrer`, storing the result in the DexCache. The `referrer` is used to identify the + // target DexCache and ClassLoader to use for lookup. + ObjPtr LookupResolvedType(dex::TypeIndex type_idx, + ObjPtr referrer) REQUIRES_SHARED(Locks::mutator_lock_); - static ObjPtr LookupResolvedType(dex::TypeIndex type_idx, - ObjPtr dex_cache, - ObjPtr class_loader) + + // Look up a resolved type with the given index from the DexFile associated with the given + // `referrer`, storing the result in the DexCache. The `referrer` is used to identify the + // target DexCache and ClassLoader to use for lookup. + ObjPtr LookupResolvedType(dex::TypeIndex type_idx, ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_); - // Resolve a type with the given ID from the DexFile, storing the - // result in DexCache. The ClassLoader is used to search for the - // type, since it may be referenced from but not contained within - // the given DexFile. - mirror::Class* ResolveType(const DexFile& dex_file, - dex::TypeIndex type_idx, - Handle dex_cache, - Handle class_loader) - REQUIRES_SHARED(Locks::mutator_lock_) - REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); + // Look up a resolved type with the given index from the DexFile associated with the given + // DexCache and ClassLoader. The ClassLoader is used to search for the type, since it may + // be referenced from but not contained within the DexFile. + ObjPtr LookupResolvedType(dex::TypeIndex type_idx, + ObjPtr dex_cache, + ObjPtr class_loader) + REQUIRES_SHARED(Locks::mutator_lock_); // Determine whether a dex cache result should be trusted, or an IncompatibleClassChangeError // check and IllegalAccessError check should be performed even after a hit. @@ -309,14 +312,12 @@ class ClassLinker { ObjPtr class_loader) REQUIRES_SHARED(Locks::mutator_lock_); - // Resolve a method with a given ID from the DexFile, storing the - // result in DexCache. The ClassLinker and ClassLoader are used as - // in ResolveType. What is unique is the method type argument which - // is used to determine if this method is a direct, static, or - // virtual method. + // Resolve a method with a given ID from the DexFile associated with the given DexCache + // and ClassLoader, storing the result in DexCache. The ClassLinker and ClassLoader are + // used as in ResolveType. What is unique is the method type argument which is used to + // determine if this method is a direct, static, or virtual method. template - ArtMethod* ResolveMethod(const DexFile& dex_file, - uint32_t method_idx, + ArtMethod* ResolveMethod(uint32_t method_idx, Handle dex_cache, Handle class_loader, ArtMethod* referrer, @@ -332,8 +333,7 @@ class ClassLinker { ArtMethod* ResolveMethod(Thread* self, uint32_t method_idx, ArtMethod* referrer, InvokeType type) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); - ArtMethod* ResolveMethodWithoutInvokeType(const DexFile& dex_file, - uint32_t method_idx, + ArtMethod* ResolveMethodWithoutInvokeType(uint32_t method_idx, Handle dex_cache, Handle class_loader) REQUIRES_SHARED(Locks::mutator_lock_) @@ -345,47 +345,47 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); - // Resolve a field with a given ID from the DexFile, storing the - // result in DexCache. The ClassLinker and ClassLoader are used as - // in ResolveType. What is unique is the is_static argument which is - // used to determine if we are resolving a static or non-static - // field. - ArtField* ResolveField(const DexFile& dex_file, uint32_t field_idx, + // Resolve a field with a given ID from the DexFile associated with the given DexCache + // and ClassLoader, storing the result in DexCache. The ClassLinker and ClassLoader + // are used as in ResolveType. What is unique is the is_static argument which is used + // to determine if we are resolving a static or non-static field. + ArtField* ResolveField(uint32_t field_idx, Handle dex_cache, - Handle class_loader, bool is_static) + Handle class_loader, + bool is_static) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); - // Resolve a field with a given ID from the DexFile, storing the - // result in DexCache. The ClassLinker and ClassLoader are used as - // in ResolveType. No is_static argument is provided so that Java + // Resolve a field with a given ID from the DexFile associated with the given DexCache + // and ClassLoader, storing the result in DexCache. The ClassLinker and ClassLoader + // are used as in ResolveType. No is_static argument is provided so that Java // field resolution semantics are followed. - ArtField* ResolveFieldJLS(const DexFile& dex_file, - uint32_t field_idx, + ArtField* ResolveFieldJLS(uint32_t field_idx, Handle dex_cache, Handle class_loader) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); - // Resolve a method type with a given ID from the DexFile, storing - // the result in the DexCache. - mirror::MethodType* ResolveMethodType(Thread* self, - const DexFile& dex_file, - uint32_t proto_idx, - Handle dex_cache, - Handle class_loader) + // Resolve a method type with a given ID from the DexFile associated with a given DexCache + // and ClassLoader, storing the result in the DexCache. + ObjPtr ResolveMethodType(Thread* self, + uint32_t proto_idx, + Handle dex_cache, + Handle class_loader) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); - mirror::MethodType* ResolveMethodType(Thread* self, uint32_t proto_idx, ArtMethod* referrer) + ObjPtr ResolveMethodType(Thread* self, + uint32_t proto_idx, + ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_); // Resolve a method handle with a given ID from the DexFile. The // result is not cached in the DexCache as the instance will only be // used once in most circumstances. - mirror::MethodHandle* ResolveMethodHandle(Thread* self, - uint32_t method_handle_idx, - ArtMethod* referrer) + ObjPtr ResolveMethodHandle(Thread* self, + uint32_t method_handle_idx, + ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_); // Returns true on success, false if there's an exception pending. @@ -881,6 +881,19 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_); + // Implementation of LookupResolvedType() called when the type was not found in the dex cache. + ObjPtr DoLookupResolvedType(dex::TypeIndex type_idx, + ObjPtr dex_cache, + ObjPtr class_loader) + REQUIRES_SHARED(Locks::mutator_lock_); + + // Implementation of ResolveType() called when the type was not found in the dex cache. + ObjPtr DoResolveType(dex::TypeIndex type_idx, + Handle dex_cache, + Handle class_loader) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); + // Finds a class by its descriptor, returning NULL if it isn't wasn't loaded // by the given 'class_loader'. Uses the provided hash for the descriptor. mirror::Class* LookupClass(Thread* self, diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 892a85099725311ba63362d9eeb8ef2ae221ca93..246f89e5ccedf8981d8c3b87c71967e91ae55aca 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -954,15 +954,14 @@ TEST_F(ClassLinkerTest, LookupResolvedType) { ObjPtr klass = class_linker_->FindClass(soa.Self(), "LMyClass;", class_loader); dex::TypeIndex type_idx = klass->GetClassDef()->class_idx_; ObjPtr dex_cache = klass->GetDexCache(); - const DexFile& dex_file = klass->GetDexFile(); EXPECT_OBJ_PTR_EQ( - class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache, class_loader.Get()), + class_linker_->LookupResolvedType(type_idx, dex_cache, class_loader.Get()), klass); // Zero out the resolved type and make sure LookupResolvedType still finds it. dex_cache->ClearResolvedType(type_idx); EXPECT_TRUE(dex_cache->GetResolvedType(type_idx) == nullptr); EXPECT_OBJ_PTR_EQ( - class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache, class_loader.Get()), + class_linker_->LookupResolvedType(type_idx, dex_cache, class_loader.Get()), klass); } @@ -983,7 +982,7 @@ TEST_F(ClassLinkerTest, LookupResolvedTypeArray) { dex::TypeIndex array_idx = dex_file.GetIndexForTypeId(*array_id); // Check that the array class wasn't resolved yet. EXPECT_OBJ_PTR_EQ( - class_linker_->LookupResolvedType(dex_file, array_idx, dex_cache.Get(), class_loader.Get()), + class_linker_->LookupResolvedType(array_idx, dex_cache.Get(), class_loader.Get()), ObjPtr(nullptr)); // Resolve the array class we want to test. ObjPtr array_klass @@ -991,13 +990,13 @@ TEST_F(ClassLinkerTest, LookupResolvedTypeArray) { ASSERT_OBJ_PTR_NE(array_klass, ObjPtr(nullptr)); // Test that LookupResolvedType() finds the array class. EXPECT_OBJ_PTR_EQ( - class_linker_->LookupResolvedType(dex_file, array_idx, dex_cache.Get(), class_loader.Get()), + class_linker_->LookupResolvedType(array_idx, dex_cache.Get(), class_loader.Get()), array_klass); // Zero out the resolved type and make sure LookupResolvedType() still finds it. dex_cache->ClearResolvedType(array_idx); EXPECT_TRUE(dex_cache->GetResolvedType(array_idx) == nullptr); EXPECT_OBJ_PTR_EQ( - class_linker_->LookupResolvedType(dex_file, array_idx, dex_cache.Get(), class_loader.Get()), + class_linker_->LookupResolvedType(array_idx, dex_cache.Get(), class_loader.Get()), array_klass); } @@ -1012,15 +1011,14 @@ TEST_F(ClassLinkerTest, LookupResolvedTypeErroneousInit) { ASSERT_OBJ_PTR_NE(klass.Get(), ObjPtr(nullptr)); dex::TypeIndex type_idx = klass->GetClassDef()->class_idx_; Handle dex_cache = hs.NewHandle(klass->GetDexCache()); - const DexFile& dex_file = klass->GetDexFile(); EXPECT_OBJ_PTR_EQ( - class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()), + class_linker_->LookupResolvedType(type_idx, dex_cache.Get(), class_loader.Get()), klass.Get()); // Zero out the resolved type and make sure LookupResolvedType still finds it. dex_cache->ClearResolvedType(type_idx); EXPECT_TRUE(dex_cache->GetResolvedType(type_idx) == nullptr); EXPECT_OBJ_PTR_EQ( - class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()), + class_linker_->LookupResolvedType(type_idx, dex_cache.Get(), class_loader.Get()), klass.Get()); // Force initialization to turn the class erroneous. bool initialized = class_linker_->EnsureInitialized(soa.Self(), @@ -1032,13 +1030,13 @@ TEST_F(ClassLinkerTest, LookupResolvedTypeErroneousInit) { soa.Self()->ClearException(); // Check that the LookupResolvedType() can still find the resolved type. EXPECT_OBJ_PTR_EQ( - class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()), + class_linker_->LookupResolvedType(type_idx, dex_cache.Get(), class_loader.Get()), klass.Get()); // Zero out the resolved type and make sure LookupResolvedType() still finds it. dex_cache->ClearResolvedType(type_idx); EXPECT_TRUE(dex_cache->GetResolvedType(type_idx) == nullptr); EXPECT_OBJ_PTR_EQ( - class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()), + class_linker_->LookupResolvedType(type_idx, dex_cache.Get(), class_loader.Get()), klass.Get()); } @@ -1304,10 +1302,18 @@ TEST_F(ClassLinkerTest, ResolveVerifyAndClinit) { const DexFile::TypeId* type_id = dex_file->FindTypeId("LStaticsFromCode;"); ASSERT_TRUE(type_id != nullptr); dex::TypeIndex type_idx = dex_file->GetIndexForTypeId(*type_id); - mirror::Class* uninit = ResolveVerifyAndClinit(type_idx, clinit, soa.Self(), true, false); + ObjPtr uninit = ResolveVerifyAndClinit(type_idx, + clinit, + soa.Self(), + /* can_run_clinit */ true, + /* verify_access */ false); EXPECT_TRUE(uninit != nullptr); EXPECT_FALSE(uninit->IsInitialized()); - mirror::Class* init = ResolveVerifyAndClinit(type_idx, getS0, soa.Self(), true, false); + ObjPtr init = ResolveVerifyAndClinit(type_idx, + getS0, + soa.Self(), + /* can_run_clinit */ true, + /* verify_access */ false); EXPECT_TRUE(init != nullptr); EXPECT_TRUE(init->IsInitialized()); } @@ -1541,11 +1547,7 @@ TEST_F(ClassLinkerMethodHandlesTest, TestResolveMethodTypes) { // Its RType = Ljava/lang/String; // Its PTypes = { Ljava/lang/String; } Handle method1_type = hs.NewHandle( - class_linker_->ResolveMethodType(soa.Self(), - dex_file, - method1_id.proto_idx_, - dex_cache, - class_loader)); + class_linker_->ResolveMethodType(soa.Self(), method1_id.proto_idx_, dex_cache, class_loader)); // Assert that the method type was resolved successfully. ASSERT_TRUE(method1_type != nullptr); @@ -1559,11 +1561,7 @@ TEST_F(ClassLinkerMethodHandlesTest, TestResolveMethodTypes) { // Resolve the method type again and assert that we get back the same value. Handle method1_type2 = hs.NewHandle( - class_linker_->ResolveMethodType(soa.Self(), - dex_file, - method1_id.proto_idx_, - dex_cache, - class_loader)); + class_linker_->ResolveMethodType(soa.Self(), method1_id.proto_idx_, dex_cache, class_loader)); ASSERT_EQ(method1_type.Get(), method1_type2.Get()); // Resolve the MethodType associated with a different method signature @@ -1576,11 +1574,7 @@ TEST_F(ClassLinkerMethodHandlesTest, TestResolveMethodTypes) { ASSERT_FALSE(method2->IsDirect()); const DexFile::MethodId& method2_id = dex_file.GetMethodId(method2->GetDexMethodIndex()); Handle method2_type = hs.NewHandle( - class_linker_->ResolveMethodType(soa.Self(), - dex_file, - method2_id.proto_idx_, - dex_cache, - class_loader)); + class_linker_->ResolveMethodType(soa.Self(), method2_id.proto_idx_, dex_cache, class_loader)); ASSERT_TRUE(method1_type.Get() != method2_type.Get()); } diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc index 38f59efdf7d3e5ad13ed56fb3d25eb2950f7dd6f..54e75588b932eb22ada95dc74e5a8714153d4280 100644 --- a/runtime/class_loader_context.cc +++ b/runtime/class_loader_context.cc @@ -16,9 +16,6 @@ #include "class_loader_context.h" -#include - -#include "android-base/file.h" #include "art_field-inl.h" #include "base/dchecked_vector.h" #include "base/stl_util.h" @@ -210,19 +207,9 @@ bool ClassLoaderContext::OpenDexFiles(InstructionSet isa, const std::string& cla size_t opened_dex_files_index = info.opened_dex_files.size(); for (const std::string& cp_elem : info.classpath) { // If path is relative, append it to the provided base directory. - std::string raw_location = cp_elem; - if (raw_location[0] != '/' && !classpath_dir.empty()) { - raw_location = classpath_dir + '/' + raw_location; - } - - std::string location; // the real location of the class path element. - - if (!android::base::Realpath(raw_location, &location)) { - // If we can't get the realpath of the location there might be something wrong with the - // classpath (maybe the file was deleted). - // Do not continue in this case and return false. - PLOG(WARNING) << "Could not get the realpath of dex location " << raw_location; - return false; + std::string location = cp_elem; + if (location[0] != '/' && !classpath_dir.empty()) { + location = classpath_dir + (classpath_dir.back() == '/' ? "" : "/") + location; } std::string error_msg; @@ -728,15 +715,20 @@ bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& contex dex_name = info.classpath[k]; expected_dex_name = OatFile::ResolveRelativeEncodedDexLocation( info.classpath[k].c_str(), expected_info.classpath[k]); - } else { + } else if (is_expected_dex_name_absolute) { // The runtime name is relative but the compiled name is absolute. // There is no expected use case that would end up here as dex files are always loaded // with their absolute location. However, be tolerant and do the best effort (in case // there are unexpected new use case...). - DCHECK(is_expected_dex_name_absolute); dex_name = OatFile::ResolveRelativeEncodedDexLocation( expected_info.classpath[k].c_str(), info.classpath[k]); expected_dex_name = expected_info.classpath[k]; + } else { + // Both locations are relative. In this case there's not much we can be sure about + // except that the names are the same. The checksum will ensure that the files are + // are same. This should not happen outside testing and manual invocations. + dex_name = info.classpath[k]; + expected_dex_name = expected_info.classpath[k]; } // Compare the locations. diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc index be6acde4a9ffbbfff7e519f8f416093420a52426..fc3446c3f92c6fdb67008065b73a474578503274 100644 --- a/runtime/class_loader_context_test.cc +++ b/runtime/class_loader_context_test.cc @@ -17,17 +17,13 @@ #include "class_loader_context.h" #include -#include #include "android-base/strings.h" - #include "base/dchecked_vector.h" #include "base/stl_util.h" -#include "class_loader_context.h" #include "class_linker.h" #include "common_runtime_test.h" #include "dex_file.h" -#include "dex2oat_environment_test.h" #include "handle_scope-inl.h" #include "mirror/class.h" #include "mirror/class_loader.h" @@ -84,6 +80,10 @@ class ClassLoaderContextTest : public CommonRuntimeTest { kEndsWith }; + static bool IsAbsoluteLocation(const std::string& location) { + return !location.empty() && location[0] == '/'; + } + void VerifyOpenDexFiles( ClassLoaderContext* context, size_t index, @@ -100,17 +100,22 @@ class ClassLoaderContextTest : public CommonRuntimeTest { info.opened_dex_files[cur_open_dex_index++]; std::unique_ptr& expected_dex_file = (*all_dex_files)[k]; - std::string expected_location = - DexFileLoader::GetBaseLocation(expected_dex_file->GetLocation()); - UniqueCPtr expected_real_location( - realpath(expected_location.c_str(), nullptr)); - ASSERT_TRUE(expected_real_location != nullptr) << expected_location; - expected_location.assign(expected_real_location.get()); - expected_location += DexFileLoader::GetMultiDexSuffix(expected_dex_file->GetLocation()); - - ASSERT_EQ(expected_location, opened_dex_file->GetLocation()); + std::string expected_location = expected_dex_file->GetLocation(); + + const std::string& opened_location = opened_dex_file->GetLocation(); + if (!IsAbsoluteLocation(opened_location)) { + // If the opened location is relative (it was open from a relative path without a + // classpath_dir) it might not match the expected location which is absolute in tests). + // So we compare the endings (the checksum will validate it's actually the same file). + ASSERT_EQ(0, expected_location.compare( + expected_location.length() - opened_location.length(), + opened_location.length(), + opened_location)); + } else { + ASSERT_EQ(expected_location, opened_location); + } ASSERT_EQ(expected_dex_file->GetLocationChecksum(), opened_dex_file->GetLocationChecksum()); - ASSERT_EQ(info.classpath[k], opened_dex_file->GetLocation()); + ASSERT_EQ(info.classpath[k], opened_location); } } @@ -252,6 +257,7 @@ TEST_F(ClassLoaderContextTest, OpenValidDexFiles) { std::string myclass_dex_name = GetTestDexFileName("MyClass"); std::string dex_name = GetTestDexFileName("Main"); + std::unique_ptr context = ClassLoaderContext::Create( "PCL[" + multidex_name + ":" + myclass_dex_name + "];" + @@ -272,42 +278,6 @@ TEST_F(ClassLoaderContextTest, OpenValidDexFiles) { VerifyOpenDexFiles(context.get(), 1, &all_dex_files1); } -class ScratchSymLink { - public: - explicit ScratchSymLink(const std::string& file) { - // Use a temporary scratch file to get a unique name for the link. - ScratchFile scratchFile; - scratch_link_name_ = scratchFile.GetFilename() + ".link.jar"; - CHECK_EQ(0, symlink(file.c_str(), scratch_link_name_.c_str())); - } - - ~ScratchSymLink() { - CHECK_EQ(0, unlink(scratch_link_name_.c_str())); - } - - const std::string& GetFilename() { return scratch_link_name_; } - - private: - std::string scratch_link_name_; -}; - -TEST_F(ClassLoaderContextTest, OpenValidDexFilesSymLink) { - std::string myclass_dex_name = GetTestDexFileName("MyClass"); - // Now replace the dex location with a symlink. - ScratchSymLink link(myclass_dex_name); - - std::unique_ptr context = - ClassLoaderContext::Create("PCL[" + link.GetFilename() + "]"); - - ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, /*classpath_dir*/ "")); - - VerifyContextSize(context.get(), 1); - - std::vector> myclass_dex_files = OpenTestDexFiles("MyClass"); - - VerifyOpenDexFiles(context.get(), 0, &myclass_dex_files); -} - static std::string CreateRelativeString(const std::string& in, const char* cwd) { int cwd_len = strlen(cwd); if (!android::base::StartsWith(in, cwd) || (cwd_len < 1)) { diff --git a/runtime/code_item_accessors-inl.h b/runtime/code_item_accessors-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..f63ea6990fa87c21f0192d489dfb70e231ec68ae --- /dev/null +++ b/runtime/code_item_accessors-inl.h @@ -0,0 +1,49 @@ +/* + * 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_RUNTIME_CODE_ITEM_ACCESSORS_INL_H_ +#define ART_RUNTIME_CODE_ITEM_ACCESSORS_INL_H_ + +#include "code_item_accessors-no_art-inl.h" + +#include "art_method-inl.h" +#include "cdex/compact_dex_file.h" +#include "dex_file-inl.h" +#include "oat_file.h" +#include "standard_dex_file.h" + +namespace art { + +inline CodeItemInstructionAccessor::CodeItemInstructionAccessor(ArtMethod* method) + : CodeItemInstructionAccessor(method->GetDexFile(), method->GetCodeItem()) {} + +inline CodeItemDataAccessor::CodeItemDataAccessor(ArtMethod* method) + : CodeItemDataAccessor(method->GetDexFile(), method->GetCodeItem()) {} + +inline CodeItemDebugInfoAccessor::CodeItemDebugInfoAccessor(ArtMethod* method) + : CodeItemDebugInfoAccessor(method->GetDexFile(), method->GetCodeItem()) {} + +inline CodeItemDebugInfoAccessor::CodeItemDebugInfoAccessor(const DexFile* dex_file, + const DexFile::CodeItem* code_item) { + if (code_item == nullptr) { + return; + } + Init(dex_file, code_item, OatFile::GetDebugInfoOffset(*dex_file, code_item->debug_info_off_)); +} + +} // namespace art + +#endif // ART_RUNTIME_CODE_ITEM_ACCESSORS_INL_H_ diff --git a/runtime/code_item_accessors-no_art-inl.h b/runtime/code_item_accessors-no_art-inl.h new file mode 100644 index 0000000000000000000000000000000000000000..ebdd78bd7450cab6f088c6f2758b0aa921a6b98c --- /dev/null +++ b/runtime/code_item_accessors-no_art-inl.h @@ -0,0 +1,165 @@ +/* + * 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_RUNTIME_CODE_ITEM_ACCESSORS_NO_ART_INL_H_ +#define ART_RUNTIME_CODE_ITEM_ACCESSORS_NO_ART_INL_H_ + +#include "code_item_accessors.h" + +#include "cdex/compact_dex_file.h" +#include "dex_file-inl.h" +#include "standard_dex_file.h" + +// The no ART version is used by binaries that don't include the whole runtime. +namespace art { + +inline void CodeItemInstructionAccessor::Init(const CompactDexFile::CodeItem& code_item) { + insns_size_in_code_units_ = code_item.insns_size_in_code_units_; + insns_ = code_item.insns_; +} + +inline void CodeItemInstructionAccessor::Init(const StandardDexFile::CodeItem& code_item) { + insns_size_in_code_units_ = code_item.insns_size_in_code_units_; + insns_ = code_item.insns_; +} + +inline void CodeItemInstructionAccessor::Init(const DexFile* dex_file, + const DexFile::CodeItem* code_item) { + if (code_item != nullptr) { + DCHECK(dex_file != nullptr); + if (dex_file->IsCompactDexFile()) { + Init(down_cast(*code_item)); + } else { + DCHECK(dex_file->IsStandardDexFile()); + Init(down_cast(*code_item)); + } + } +} + +inline CodeItemInstructionAccessor::CodeItemInstructionAccessor( + const DexFile* dex_file, + const DexFile::CodeItem* code_item) { + Init(dex_file, code_item); +} + +inline DexInstructionIterator CodeItemInstructionAccessor::begin() const { + return DexInstructionIterator(insns_, 0u); +} + +inline DexInstructionIterator CodeItemInstructionAccessor::end() const { + return DexInstructionIterator(insns_, insns_size_in_code_units_); +} + +inline IterationRange CodeItemInstructionAccessor::InstructionsFrom( + uint32_t start_dex_pc) const { + DCHECK_LT(start_dex_pc, InsnsSizeInCodeUnits()); + return { + DexInstructionIterator(insns_, start_dex_pc), + DexInstructionIterator(insns_, insns_size_in_code_units_) }; +} + +inline void CodeItemDataAccessor::Init(const CompactDexFile::CodeItem& code_item) { + CodeItemInstructionAccessor::Init(code_item); + registers_size_ = code_item.registers_size_; + ins_size_ = code_item.ins_size_; + outs_size_ = code_item.outs_size_; + tries_size_ = code_item.tries_size_; +} + +inline void CodeItemDataAccessor::Init(const StandardDexFile::CodeItem& code_item) { + CodeItemInstructionAccessor::Init(code_item); + registers_size_ = code_item.registers_size_; + ins_size_ = code_item.ins_size_; + outs_size_ = code_item.outs_size_; + tries_size_ = code_item.tries_size_; +} + +inline void CodeItemDataAccessor::Init(const DexFile* dex_file, + const DexFile::CodeItem* code_item) { + if (code_item != nullptr) { + DCHECK(dex_file != nullptr); + if (dex_file->IsCompactDexFile()) { + CodeItemDataAccessor::Init(down_cast(*code_item)); + } else { + DCHECK(dex_file->IsStandardDexFile()); + CodeItemDataAccessor::Init(down_cast(*code_item)); + } + } +} + +inline CodeItemDataAccessor::CodeItemDataAccessor(const DexFile* dex_file, + const DexFile::CodeItem* code_item) { + Init(dex_file, code_item); +} + +inline IterationRange CodeItemDataAccessor::TryItems() const { + const DexFile::TryItem* try_items = DexFile::GetTryItems(end(), 0u); + return { + try_items, + try_items + TriesSize() }; +} + +inline const uint8_t* CodeItemDataAccessor::GetCatchHandlerData(size_t offset) const { + return DexFile::GetCatchHandlerData(end(), TriesSize(), offset); +} + +inline const DexFile::TryItem* CodeItemDataAccessor::FindTryItem(uint32_t try_dex_pc) const { + IterationRange try_items(TryItems()); + int32_t index = DexFile::FindTryItem(try_items.begin(), + try_items.end() - try_items.begin(), + try_dex_pc); + return index != -1 ? &try_items.begin()[index] : nullptr; +} + +inline void CodeItemDebugInfoAccessor::Init(const DexFile* dex_file, + const DexFile::CodeItem* code_item, + uint32_t debug_info_offset) { + dex_file_ = dex_file; + debug_info_offset_ = debug_info_offset; + if (dex_file->IsCompactDexFile()) { + Init(down_cast(*code_item)); + } else { + DCHECK(dex_file->IsStandardDexFile()); + Init(down_cast(*code_item)); + } +} + +inline void CodeItemDebugInfoAccessor::Init(const CompactDexFile::CodeItem& code_item) { + CodeItemDataAccessor::Init(code_item); +} + +inline void CodeItemDebugInfoAccessor::Init(const StandardDexFile::CodeItem& code_item) { + CodeItemDataAccessor::Init(code_item); +} + +template +inline bool CodeItemDebugInfoAccessor::DecodeDebugLocalInfo(bool is_static, + uint32_t method_idx, + NewLocalCallback new_local, + void* context) const { + return dex_file_->DecodeDebugLocalInfo(RegistersSize(), + InsSize(), + InsnsSizeInCodeUnits(), + DebugInfoOffset(), + is_static, + method_idx, + new_local, + context); +} + +} // namespace art + +#endif // ART_RUNTIME_CODE_ITEM_ACCESSORS_NO_ART_INL_H_ diff --git a/runtime/code_item_accessors.h b/runtime/code_item_accessors.h new file mode 100644 index 0000000000000000000000000000000000000000..4cbe7a1ba08b37c28c9cd646afef24ee77c34413 --- /dev/null +++ b/runtime/code_item_accessors.h @@ -0,0 +1,170 @@ +/* + * 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. + */ + +// TODO: Dex helpers have ART specific APIs, we may want to refactor these for use in dexdump. + +#ifndef ART_RUNTIME_CODE_ITEM_ACCESSORS_H_ +#define ART_RUNTIME_CODE_ITEM_ACCESSORS_H_ + +#include "base/mutex.h" +#include "cdex/compact_dex_file.h" +#include "dex_file.h" +#include "dex_instruction_iterator.h" +#include "standard_dex_file.h" + +namespace art { + +class ArtMethod; + +// Abstracts accesses to the instruction fields of code items for CompactDexFile and +// StandardDexFile. +class CodeItemInstructionAccessor { + public: + ALWAYS_INLINE CodeItemInstructionAccessor(const DexFile* dex_file, + const DexFile::CodeItem* code_item); + + ALWAYS_INLINE explicit CodeItemInstructionAccessor(ArtMethod* method); + + ALWAYS_INLINE DexInstructionIterator begin() const; + + ALWAYS_INLINE DexInstructionIterator end() const; + + IterationRange InstructionsFrom(uint32_t start_dex_pc) const; + + uint32_t InsnsSizeInCodeUnits() const { + return insns_size_in_code_units_; + } + + const uint16_t* Insns() const { + return insns_; + } + + // Return the instruction for a dex pc. + const Instruction& InstructionAt(uint32_t dex_pc) const { + DCHECK_LT(dex_pc, InsnsSizeInCodeUnits()); + return *Instruction::At(insns_ + dex_pc); + } + + // Return true if the accessor has a code item. + bool HasCodeItem() const { + return Insns() != nullptr; + } + + protected: + CodeItemInstructionAccessor() = default; + + ALWAYS_INLINE void Init(const CompactDexFile::CodeItem& code_item); + ALWAYS_INLINE void Init(const StandardDexFile::CodeItem& code_item); + ALWAYS_INLINE void Init(const DexFile* dex_file, const DexFile::CodeItem* code_item); + + private: + // size of the insns array, in 2 byte code units. 0 if there is no code item. + uint32_t insns_size_in_code_units_ = 0; + + // Pointer to the instructions, null if there is no code item. + const uint16_t* insns_ = 0; +}; + +// Abstracts accesses to code item fields other than debug info for CompactDexFile and +// StandardDexFile. +class CodeItemDataAccessor : public CodeItemInstructionAccessor { + public: + ALWAYS_INLINE CodeItemDataAccessor(const DexFile* dex_file, const DexFile::CodeItem* code_item); + + ALWAYS_INLINE explicit CodeItemDataAccessor(ArtMethod* method); + + uint16_t RegistersSize() const { + return registers_size_; + } + + uint16_t InsSize() const { + return ins_size_; + } + + uint16_t OutsSize() const { + return outs_size_; + } + + uint16_t TriesSize() const { + return tries_size_; + } + + IterationRange TryItems() const; + + const uint8_t* GetCatchHandlerData(size_t offset = 0) const; + + const DexFile::TryItem* FindTryItem(uint32_t try_dex_pc) const; + + protected: + CodeItemDataAccessor() = default; + + ALWAYS_INLINE void Init(const CompactDexFile::CodeItem& code_item); + ALWAYS_INLINE void Init(const StandardDexFile::CodeItem& code_item); + ALWAYS_INLINE void Init(const DexFile* dex_file, const DexFile::CodeItem* code_item); + + private: + // Fields mirrored from the dex/cdex code item. + uint16_t registers_size_; + uint16_t ins_size_; + uint16_t outs_size_; + uint16_t tries_size_; +}; + +// Abstract accesses to code item data including debug info offset. More heavy weight than the other +// helpers. +class CodeItemDebugInfoAccessor : public CodeItemDataAccessor { + public: + CodeItemDebugInfoAccessor() = default; + + // Handles null code items, but not null dex files. + ALWAYS_INLINE CodeItemDebugInfoAccessor(const DexFile* dex_file, + const DexFile::CodeItem* code_item); + + // Initialize with an existing offset. + ALWAYS_INLINE CodeItemDebugInfoAccessor(const DexFile* dex_file, + const DexFile::CodeItem* code_item, + uint32_t debug_info_offset) { + Init(dex_file, code_item, debug_info_offset); + } + + ALWAYS_INLINE void Init(const DexFile* dex_file, + const DexFile::CodeItem* code_item, + uint32_t debug_info_offset); + + ALWAYS_INLINE explicit CodeItemDebugInfoAccessor(ArtMethod* method); + + uint32_t DebugInfoOffset() const { + return debug_info_offset_; + } + + template + bool DecodeDebugLocalInfo(bool is_static, + uint32_t method_idx, + NewLocalCallback new_local, + void* context) const; + + protected: + ALWAYS_INLINE void Init(const CompactDexFile::CodeItem& code_item); + ALWAYS_INLINE void Init(const StandardDexFile::CodeItem& code_item); + + private: + const DexFile* dex_file_ = nullptr; + uint32_t debug_info_offset_ = 0u; +}; + +} // namespace art + +#endif // ART_RUNTIME_CODE_ITEM_ACCESSORS_H_ diff --git a/runtime/code_item_accessors_test.cc b/runtime/code_item_accessors_test.cc new file mode 100644 index 0000000000000000000000000000000000000000..ef5d246a548ffe846b94af9d5a82acd96312e4b6 --- /dev/null +++ b/runtime/code_item_accessors_test.cc @@ -0,0 +1,108 @@ +/* + * 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 "code_item_accessors-inl.h" + +#include + +#include "common_runtime_test.h" +#include "dex_file_loader.h" +#include "mem_map.h" + +namespace art { + +class CodeItemAccessorsTest : public CommonRuntimeTest {}; + +std::unique_ptr CreateFakeDex(bool compact_dex) { + std::string error_msg; + std::unique_ptr map( + MemMap::MapAnonymous(/*name*/ "map", + /*addr*/ nullptr, + /*byte_count*/ kPageSize, + PROT_READ | PROT_WRITE, + /*low_4gb*/ false, + /*reuse*/ false, + &error_msg)); + CHECK(map != nullptr) << error_msg; + if (compact_dex) { + CompactDexFile::WriteMagic(map->Begin()); + CompactDexFile::WriteCurrentVersion(map->Begin()); + } else { + StandardDexFile::WriteMagic(map->Begin()); + StandardDexFile::WriteCurrentVersion(map->Begin()); + } + std::unique_ptr dex( + DexFileLoader::Open("location", + /*location_checksum*/ 123, + std::move(map), + /*verify*/false, + /*verify_checksum*/false, + &error_msg)); + CHECK(dex != nullptr) << error_msg; + return dex; +} + +TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor) { + MemMap::Init(); + std::unique_ptr standard_dex(CreateFakeDex(/*compact_dex*/false)); + ASSERT_TRUE(standard_dex != nullptr); + std::unique_ptr compact_dex(CreateFakeDex(/*compact_dex*/true)); + ASSERT_TRUE(compact_dex != nullptr); + static constexpr uint16_t kRegisterSize = 1; + static constexpr uint16_t kInsSize = 2; + static constexpr uint16_t kOutsSize = 3; + static constexpr uint16_t kTriesSize = 4; + // debug_info_off_ is not accessible from the helpers yet. + static constexpr size_t kInsnsSizeInCodeUnits = 5; + + auto verify_code_item = [&](const DexFile* dex, + const DexFile::CodeItem* item, + const uint16_t* insns) { + CodeItemInstructionAccessor insns_accessor(dex, item); + EXPECT_TRUE(insns_accessor.HasCodeItem()); + ASSERT_EQ(insns_accessor.InsnsSizeInCodeUnits(), kInsnsSizeInCodeUnits); + EXPECT_EQ(insns_accessor.Insns(), insns); + + CodeItemDataAccessor data_accessor(dex, item); + EXPECT_TRUE(data_accessor.HasCodeItem()); + EXPECT_EQ(data_accessor.InsnsSizeInCodeUnits(), kInsnsSizeInCodeUnits); + EXPECT_EQ(data_accessor.Insns(), insns); + EXPECT_EQ(data_accessor.RegistersSize(), kRegisterSize); + EXPECT_EQ(data_accessor.InsSize(), kInsSize); + EXPECT_EQ(data_accessor.OutsSize(), kOutsSize); + EXPECT_EQ(data_accessor.TriesSize(), kTriesSize); + }; + + uint8_t buffer1[sizeof(CompactDexFile::CodeItem) + kInsnsSizeInCodeUnits * sizeof(uint16_t)] = {}; + CompactDexFile::CodeItem* dex_code_item = reinterpret_cast(buffer1); + dex_code_item->registers_size_ = kRegisterSize; + dex_code_item->ins_size_ = kInsSize; + dex_code_item->outs_size_ = kOutsSize; + dex_code_item->tries_size_ = kTriesSize; + dex_code_item->insns_size_in_code_units_ = kInsnsSizeInCodeUnits; + verify_code_item(compact_dex.get(), dex_code_item, dex_code_item->insns_); + + uint8_t buffer2[sizeof(CompactDexFile::CodeItem) + kInsnsSizeInCodeUnits * sizeof(uint16_t)] = {}; + CompactDexFile::CodeItem* cdex_code_item = reinterpret_cast(buffer2); + cdex_code_item->registers_size_ = kRegisterSize; + cdex_code_item->ins_size_ = kInsSize; + cdex_code_item->outs_size_ = kOutsSize; + cdex_code_item->tries_size_ = kTriesSize; + cdex_code_item->insns_size_in_code_units_ = kInsnsSizeInCodeUnits; + verify_code_item(compact_dex.get(), cdex_code_item, cdex_code_item->insns_); +} + +} // namespace art diff --git a/runtime/common_dex_operations.h b/runtime/common_dex_operations.h index 267735fe956a0c9b79539727c0660417750e98b0..89887022e07e788895c1fa39d9230a40e3212400 100644 --- a/runtime/common_dex_operations.h +++ b/runtime/common_dex_operations.h @@ -23,6 +23,7 @@ #include "base/macros.h" #include "base/mutex.h" #include "class_linker.h" +#include "code_item_accessors.h" #include "handle_scope-inl.h" #include "instrumentation.h" #include "interpreter/shadow_frame.h" @@ -52,7 +53,7 @@ namespace interpreter { } // namespace interpreter inline void PerformCall(Thread* self, - const DexFile::CodeItem* code_item, + const CodeItemDataAccessor& accessor, ArtMethod* caller_method, const size_t first_dest_reg, ShadowFrame* callee_frame, @@ -61,13 +62,13 @@ inline void PerformCall(Thread* self, REQUIRES_SHARED(Locks::mutator_lock_) { if (LIKELY(Runtime::Current()->IsStarted())) { if (use_interpreter_entrypoint) { - interpreter::ArtInterpreterToInterpreterBridge(self, code_item, callee_frame, result); + interpreter::ArtInterpreterToInterpreterBridge(self, accessor, callee_frame, result); } else { interpreter::ArtInterpreterToCompiledCodeBridge( self, caller_method, callee_frame, first_dest_reg, result); } } else { - interpreter::UnstartedRuntime::Invoke(self, code_item, callee_frame, result, first_dest_reg); + interpreter::UnstartedRuntime::Invoke(self, accessor, callee_frame, result, first_dest_reg); } } diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index 493468bb3d9387d56d1d765bcb4807f2b512d6e6..6db4d92708e458aa0e237405baca3dfd7326bbc4 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -30,6 +30,7 @@ #include "base/file_utils.h" #include "base/logging.h" #include "base/macros.h" +#include "base/runtime_debug.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" @@ -78,11 +79,11 @@ static const uint8_t kBase64Map[256] = { 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, // NOLINT - 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, // NOLINT + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, - 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, // NOLINT - 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, // NOLINT + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h index 5be8d5b55cb8086d0e923405653849802cf5e613..625884d98dbaa53ec340d3430da1a58127066619 100644 --- a/runtime/common_runtime_test.h +++ b/runtime/common_runtime_test.h @@ -22,8 +22,11 @@ #include +#include + #include "arch/instruction_set.h" #include "base/mutex.h" +#include "cdex/compact_dex_level.h" #include "globals.h" // TODO: Add inl file and avoid including inl. #include "obj_ptr-inl.h" @@ -32,6 +35,9 @@ namespace art { +using LogSeverity = android::base::LogSeverity; +using ScopedLogSeverity = android::base::ScopedLogSeverity; + // OBJ pointer helpers to avoid needing .Decode everywhere. #define EXPECT_OBJ_PTR_EQ(a, b) EXPECT_EQ(MakeObjPtr(a).Ptr(), MakeObjPtr(b).Ptr()); #define ASSERT_OBJ_PTR_EQ(a, b) ASSERT_EQ(MakeObjPtr(a).Ptr(), MakeObjPtr(b).Ptr()); @@ -300,6 +306,11 @@ class CheckJniAbortCatcher { return; \ } +#define TEST_DISABLED_FOR_COMPACT_DEX() \ + if (kDefaultCompactDexLevel != CompactDexLevel::kCompactDexLevelNone) { \ + printf("WARNING: TEST DISABLED FOR COMPACT DEX\n"); \ + return; \ + } } // namespace art #endif // ART_RUNTIME_COMMON_RUNTIME_TEST_H_ diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc index cd52bb6551097d85c57b0f23ca9b398a43a51d67..707885c88d913bf866eea719b86d4619e0e6a05c 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -18,11 +18,11 @@ #include -#include "android-base/stringprintf.h" +#include +#include #include "art_field-inl.h" #include "art_method-inl.h" -#include "base/logging.h" #include "class_linker-inl.h" #include "dex_file-inl.h" #include "dex_instruction-inl.h" @@ -441,7 +441,7 @@ static bool IsValidReadBarrierImplicitCheck(uintptr_t addr) { return addr == monitor_offset; } -static bool IsValidImplicitCheck(uintptr_t addr, ArtMethod* method, const Instruction& instr) +static bool IsValidImplicitCheck(uintptr_t addr, const Instruction& instr) REQUIRES_SHARED(Locks::mutator_lock_) { if (!CanDoImplicitNullCheckOn(addr)) { return false; @@ -483,9 +483,10 @@ static bool IsValidImplicitCheck(uintptr_t addr, ArtMethod* method, const Instru case Instruction::IPUT_BYTE: case Instruction::IPUT_CHAR: case Instruction::IPUT_SHORT: { - ArtField* field = - Runtime::Current()->GetClassLinker()->ResolveField(instr.VRegC_22c(), method, false); - return (addr == 0) || (addr == field->GetOffset().Uint32Value()); + // We might be doing an implicit null check with an offset that doesn't correspond + // to the instruction, for example with two field accesses and the first one being + // eliminated or re-ordered. + return true; } case Instruction::IGET_OBJECT_QUICK: @@ -506,7 +507,10 @@ static bool IsValidImplicitCheck(uintptr_t addr, ArtMethod* method, const Instru case Instruction::IPUT_SHORT_QUICK: case Instruction::IPUT_WIDE_QUICK: case Instruction::IPUT_OBJECT_QUICK: { - return (addr == 0u) || (addr == instr.VRegC_22c()); + // We might be doing an implicit null check with an offset that doesn't correspond + // to the instruction, for example with two field accesses and the first one being + // eliminated or re-ordered. + return true; } case Instruction::AGET_OBJECT: @@ -547,43 +551,43 @@ static bool IsValidImplicitCheck(uintptr_t addr, ArtMethod* method, const Instru void ThrowNullPointerExceptionFromDexPC(bool check_address, uintptr_t addr) { uint32_t throw_dex_pc; ArtMethod* method = Thread::Current()->GetCurrentMethod(&throw_dex_pc); - const DexFile::CodeItem* code = method->GetCodeItem(); - CHECK_LT(throw_dex_pc, code->insns_size_in_code_units_); - const Instruction* instr = Instruction::At(&code->insns_[throw_dex_pc]); - if (check_address && !IsValidImplicitCheck(addr, method, *instr)) { + CodeItemInstructionAccessor accessor(method); + CHECK_LT(throw_dex_pc, accessor.InsnsSizeInCodeUnits()); + const Instruction& instr = accessor.InstructionAt(throw_dex_pc); + if (check_address && !IsValidImplicitCheck(addr, instr)) { const DexFile* dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile(); LOG(FATAL) << "Invalid address for an implicit NullPointerException check: " << "0x" << std::hex << addr << std::dec << ", at " - << instr->DumpString(dex_file) + << instr.DumpString(dex_file) << " in " << method->PrettyMethod(); } - switch (instr->Opcode()) { + switch (instr.Opcode()) { case Instruction::INVOKE_DIRECT: - ThrowNullPointerExceptionForMethodAccess(instr->VRegB_35c(), kDirect); + ThrowNullPointerExceptionForMethodAccess(instr.VRegB_35c(), kDirect); break; case Instruction::INVOKE_DIRECT_RANGE: - ThrowNullPointerExceptionForMethodAccess(instr->VRegB_3rc(), kDirect); + ThrowNullPointerExceptionForMethodAccess(instr.VRegB_3rc(), kDirect); break; case Instruction::INVOKE_VIRTUAL: - ThrowNullPointerExceptionForMethodAccess(instr->VRegB_35c(), kVirtual); + ThrowNullPointerExceptionForMethodAccess(instr.VRegB_35c(), kVirtual); break; case Instruction::INVOKE_VIRTUAL_RANGE: - ThrowNullPointerExceptionForMethodAccess(instr->VRegB_3rc(), kVirtual); + ThrowNullPointerExceptionForMethodAccess(instr.VRegB_3rc(), kVirtual); break; case Instruction::INVOKE_INTERFACE: - ThrowNullPointerExceptionForMethodAccess(instr->VRegB_35c(), kInterface); + ThrowNullPointerExceptionForMethodAccess(instr.VRegB_35c(), kInterface); break; case Instruction::INVOKE_INTERFACE_RANGE: - ThrowNullPointerExceptionForMethodAccess(instr->VRegB_3rc(), kInterface); + ThrowNullPointerExceptionForMethodAccess(instr.VRegB_3rc(), kInterface); break; case Instruction::INVOKE_POLYMORPHIC: - ThrowNullPointerExceptionForMethodAccess(instr->VRegB_45cc(), kVirtual); + ThrowNullPointerExceptionForMethodAccess(instr.VRegB_45cc(), kVirtual); break; case Instruction::INVOKE_POLYMORPHIC_RANGE: - ThrowNullPointerExceptionForMethodAccess(instr->VRegB_4rcc(), kVirtual); + ThrowNullPointerExceptionForMethodAccess(instr.VRegB_4rcc(), kVirtual); break; case Instruction::INVOKE_VIRTUAL_QUICK: case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: { @@ -608,7 +612,7 @@ void ThrowNullPointerExceptionFromDexPC(bool check_address, uintptr_t addr) { case Instruction::IGET_CHAR: case Instruction::IGET_SHORT: { ArtField* field = - Runtime::Current()->GetClassLinker()->ResolveField(instr->VRegC_22c(), method, false); + Runtime::Current()->GetClassLinker()->ResolveField(instr.VRegC_22c(), method, false); ThrowNullPointerExceptionForFieldAccess(field, true /* read */); break; } @@ -640,7 +644,7 @@ void ThrowNullPointerExceptionFromDexPC(bool check_address, uintptr_t addr) { case Instruction::IPUT_CHAR: case Instruction::IPUT_SHORT: { ArtField* field = - Runtime::Current()->GetClassLinker()->ResolveField(instr->VRegC_22c(), method, false); + Runtime::Current()->GetClassLinker()->ResolveField(instr.VRegC_22c(), method, false); ThrowNullPointerExceptionForFieldAccess(field, false /* write */); break; } @@ -703,7 +707,7 @@ void ThrowNullPointerExceptionFromDexPC(bool check_address, uintptr_t addr) { const DexFile* dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile(); LOG(FATAL) << "NullPointerException at an unexpected instruction: " - << instr->DumpString(dex_file) + << instr.DumpString(dex_file) << " in " << method->PrettyMethod(); break; diff --git a/runtime/debugger.cc b/runtime/debugger.cc index c7f245309fea2c00f4b4869d5cb348b535391419..f504d86f7628c7a8b583b6c95406ebd7992c80c5 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -18,6 +18,7 @@ #include +#include #include #include #include @@ -39,6 +40,7 @@ #include "entrypoints/runtime_asm_entrypoints.h" #include "gc/accounting/card_table-inl.h" #include "gc/allocation_record.h" +#include "gc/gc_cause.h" #include "gc/scoped_gc_critical_section.h" #include "gc/space/bump_pointer_space-walk-inl.h" #include "gc/space/large_object_space.h" @@ -57,6 +59,7 @@ #include "mirror/throwable.h" #include "nativehelper/scoped_local_ref.h" #include "nativehelper/scoped_primitive_array.h" +#include "oat_file.h" #include "obj_ptr-inl.h" #include "reflection.h" #include "safe_map.h" @@ -276,11 +279,8 @@ class DebugInstrumentationListener FINAL : public instrumentation::Instrumentati } private: - static bool IsReturn(ArtMethod* method, uint32_t dex_pc) - REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile::CodeItem* code_item = method->GetCodeItem(); - const Instruction* instruction = Instruction::At(&code_item->insns_[dex_pc]); - return instruction->IsReturn(); + static bool IsReturn(ArtMethod* method, uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_) { + return method->DexInstructions().InstructionAt(dex_pc).IsReturn(); } static bool IsListeningToDexPcMoved() REQUIRES_SHARED(Locks::mutator_lock_) { @@ -325,6 +325,8 @@ bool Dbg::gDebuggerActive = false; bool Dbg::gDisposed = false; ObjectRegistry* Dbg::gRegistry = nullptr; DebuggerActiveMethodInspectionCallback Dbg::gDebugActiveCallback; +DebuggerDdmCallback Dbg::gDebugDdmCallback; +InternalDebuggerControlCallback Dbg::gDebuggerControlCallback; // Deoptimization support. std::vector Dbg::deoptimization_requests_; @@ -342,6 +344,17 @@ uint32_t Dbg::instrumentation_events_ = 0; Dbg::DbgThreadLifecycleCallback Dbg::thread_lifecycle_callback_; Dbg::DbgClassLoadCallback Dbg::class_load_callback_; +void DebuggerDdmCallback::DdmPublishChunk(uint32_t type, const ArrayRef& data) { + if (gJdwpState == nullptr) { + VLOG(jdwp) << "Debugger thread not active, ignoring DDM send: " << type; + } else { + iovec vec[1]; + vec[0].iov_base = reinterpret_cast(const_cast(data.data())); + vec[0].iov_len = data.size(); + gJdwpState->DdmSendChunkV(type, vec, 1); + } +} + bool DebuggerActiveMethodInspectionCallback::IsMethodBeingInspected(ArtMethod* m ATTRIBUTE_UNUSED) { return Dbg::IsDebuggerActive(); } @@ -350,6 +363,20 @@ bool DebuggerActiveMethodInspectionCallback::IsMethodSafeToJit(ArtMethod* m) { return !Dbg::MethodHasAnyBreakpoints(m); } +void InternalDebuggerControlCallback::StartDebugger() { + // Release the mutator lock. + ScopedThreadStateChange stsc(art::Thread::Current(), kNative); + Dbg::StartJdwp(); +} + +void InternalDebuggerControlCallback::StopDebugger() { + Dbg::StopJdwp(); +} + +bool InternalDebuggerControlCallback::IsDebuggerConfigured() { + return Dbg::IsJdwpConfigured(); +} + // Breakpoints. static std::vector gBreakpoints GUARDED_BY(Locks::breakpoint_lock_); @@ -531,6 +558,12 @@ void Dbg::StartJdwp() { CHECK(gRegistry == nullptr); gRegistry = new ObjectRegistry; + { + // Setup the Ddm listener + ScopedObjectAccess soa(Thread::Current()); + Runtime::Current()->GetRuntimeCallbacks()->AddDdmCallback(&gDebugDdmCallback); + } + // Init JDWP if the debugger is enabled. This may connect out to a // debugger, passively listen for a debugger, or block waiting for a // debugger. @@ -716,6 +749,7 @@ void Dbg::ConfigureJdwp(const JDWP::JdwpOptions& jdwp_options) { CHECK_NE(jdwp_options.transport, JDWP::kJdwpTransportUnknown); gJdwpOptions = jdwp_options; gJdwpConfigured = true; + Runtime::Current()->GetRuntimeCallbacks()->AddDebuggerControlCallback(&gDebuggerControlCallback); } bool Dbg::IsJdwpConfigured() { @@ -914,7 +948,7 @@ JDWP::JdwpError Dbg::GetContendedMonitor(JDWP::ObjectId thread_id, JDWP::JdwpError Dbg::GetInstanceCounts(const std::vector& class_ids, std::vector* counts) { gc::Heap* heap = Runtime::Current()->GetHeap(); - heap->CollectGarbage(false); + heap->CollectGarbage(false, gc::GcCause::kGcCauseDebugger); VariableSizedHandleScope hs(Thread::Current()); std::vector> classes; counts->clear(); @@ -935,7 +969,7 @@ JDWP::JdwpError Dbg::GetInstances(JDWP::RefTypeId class_id, int32_t max_count, std::vector* instances) { gc::Heap* heap = Runtime::Current()->GetHeap(); // We only want reachable instances, so do a GC. - heap->CollectGarbage(false); + heap->CollectGarbage(false, gc::GcCause::kGcCauseDebugger); JDWP::JdwpError error; ObjPtr c = DecodeClass(class_id, &error); if (c == nullptr) { @@ -943,7 +977,11 @@ JDWP::JdwpError Dbg::GetInstances(JDWP::RefTypeId class_id, int32_t max_count, } VariableSizedHandleScope hs(Thread::Current()); std::vector> raw_instances; - Runtime::Current()->GetHeap()->GetInstances(hs, hs.NewHandle(c), max_count, raw_instances); + Runtime::Current()->GetHeap()->GetInstances(hs, + hs.NewHandle(c), + /* use_is_assignable_from */ false, + max_count, + raw_instances); for (size_t i = 0; i < raw_instances.size(); ++i) { instances->push_back(gRegistry->Add(raw_instances[i].Get())); } @@ -953,7 +991,7 @@ JDWP::JdwpError Dbg::GetInstances(JDWP::RefTypeId class_id, int32_t max_count, JDWP::JdwpError Dbg::GetReferringObjects(JDWP::ObjectId object_id, int32_t max_count, std::vector* referring_objects) { gc::Heap* heap = Runtime::Current()->GetHeap(); - heap->CollectGarbage(false); + heap->CollectGarbage(false, gc::GcCause::kGcCauseDebugger); JDWP::JdwpError error; ObjPtr o = gRegistry->Get(object_id, &error); if (o == nullptr) { @@ -1495,15 +1533,15 @@ static uint32_t MangleAccessFlags(uint32_t accessFlags) { */ static uint16_t MangleSlot(uint16_t slot, ArtMethod* m) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile::CodeItem* code_item = m->GetCodeItem(); - if (code_item == nullptr) { + CodeItemDataAccessor accessor(m); + if (!accessor.HasCodeItem()) { // We should not get here for a method without code (native, proxy or abstract). Log it and // return the slot as is since all registers are arguments. LOG(WARNING) << "Trying to mangle slot for method without code " << m->PrettyMethod(); return slot; } - uint16_t ins_size = code_item->ins_size_; - uint16_t locals_size = code_item->registers_size_ - ins_size; + uint16_t ins_size = accessor.InsSize(); + uint16_t locals_size = accessor.RegistersSize() - ins_size; if (slot >= locals_size) { return slot - locals_size; } else { @@ -1526,8 +1564,8 @@ static size_t GetMethodNumArgRegistersIncludingThis(ArtMethod* method) */ static uint16_t DemangleSlot(uint16_t slot, ArtMethod* m, JDWP::JdwpError* error) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile::CodeItem* code_item = m->GetCodeItem(); - if (code_item == nullptr) { + CodeItemDataAccessor accessor(m); + if (!accessor.HasCodeItem()) { // We should not get here for a method without code (native, proxy or abstract). Log it and // return the slot as is since all registers are arguments. LOG(WARNING) << "Trying to demangle slot for method without code " @@ -1538,9 +1576,9 @@ static uint16_t DemangleSlot(uint16_t slot, ArtMethod* m, JDWP::JdwpError* error return slot; } } else { - if (slot < code_item->registers_size_) { - uint16_t ins_size = code_item->ins_size_; - uint16_t locals_size = code_item->registers_size_ - ins_size; + if (slot < accessor.RegistersSize()) { + uint16_t ins_size = accessor.InsSize(); + uint16_t locals_size = accessor.RegistersSize() - ins_size; *error = JDWP::ERR_NONE; return (slot < ins_size) ? slot + locals_size : slot - ins_size; } @@ -1637,16 +1675,16 @@ void Dbg::OutputLineTable(JDWP::RefTypeId, JDWP::MethodId method_id, JDWP::Expan } }; ArtMethod* m = FromMethodId(method_id); - const DexFile::CodeItem* code_item = m->GetCodeItem(); + CodeItemDebugInfoAccessor accessor(m); uint64_t start, end; - if (code_item == nullptr) { + if (!accessor.HasCodeItem()) { DCHECK(m->IsNative() || m->IsProxyMethod()); start = -1; end = -1; } else { start = 0; // Return the index of the last instruction - end = code_item->insns_size_in_code_units_ - 1; + end = accessor.InsnsSizeInCodeUnits() - 1; } expandBufAdd8BE(pReply, start); @@ -1660,8 +1698,10 @@ void Dbg::OutputLineTable(JDWP::RefTypeId, JDWP::MethodId method_id, JDWP::Expan context.numItems = 0; context.pReply = pReply; - if (code_item != nullptr) { - m->GetDexFile()->DecodeDebugPositionInfo(code_item, DebugCallbackContext::Callback, &context); + if (accessor.HasCodeItem()) { + m->GetDexFile()->DecodeDebugPositionInfo(accessor.DebugInfoOffset(), + DebugCallbackContext::Callback, + &context); } JDWP::Set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems); @@ -1701,6 +1741,7 @@ void Dbg::OutputVariableTable(JDWP::RefTypeId, JDWP::MethodId method_id, bool wi } }; ArtMethod* m = FromMethodId(method_id); + CodeItemDebugInfoAccessor accessor(m); // arg_count considers doubles and longs to take 2 units. // variable_count considers everything to take 1 unit. @@ -1716,11 +1757,15 @@ void Dbg::OutputVariableTable(JDWP::RefTypeId, JDWP::MethodId method_id, bool wi context.variable_count = 0; context.with_generic = with_generic; - const DexFile::CodeItem* code_item = m->GetCodeItem(); - if (code_item != nullptr) { - m->GetDexFile()->DecodeDebugLocalInfo( - code_item, m->IsStatic(), m->GetDexMethodIndex(), DebugCallbackContext::Callback, - &context); + if (accessor.HasCodeItem()) { + m->GetDexFile()->DecodeDebugLocalInfo(accessor.RegistersSize(), + accessor.InsSize(), + accessor.InsnsSizeInCodeUnits(), + accessor.DebugInfoOffset(), + m->IsStatic(), + m->GetDexMethodIndex(), + DebugCallbackContext::Callback, + &context); } JDWP::Set4BE(expandBufGetBuffer(pReply) + variable_count_offset, context.variable_count); @@ -1746,9 +1791,9 @@ JDWP::JdwpError Dbg::GetBytecodes(JDWP::RefTypeId, JDWP::MethodId method_id, if (m == nullptr) { return JDWP::ERR_INVALID_METHODID; } - const DexFile::CodeItem* code_item = m->GetCodeItem(); - size_t byte_count = code_item->insns_size_in_code_units_ * 2; - const uint8_t* begin = reinterpret_cast(code_item->insns_); + CodeItemDataAccessor accessor(m); + size_t byte_count = accessor.InsnsSizeInCodeUnits() * 2; + const uint8_t* begin = reinterpret_cast(accessor.Insns()); const uint8_t* end = begin + byte_count; for (const uint8_t* p = begin; p != end; ++p) { bytecodes->push_back(*p); @@ -2931,9 +2976,8 @@ void Dbg::PostLocationEvent(ArtMethod* m, int dex_pc, mirror::Object* this_objec Handle pending_exception(hs.NewHandle(self->GetException())); self->ClearException(); if (kIsDebugBuild && pending_exception != nullptr) { - const DexFile::CodeItem* code_item = location.method->GetCodeItem(); - const Instruction* instr = Instruction::At(&code_item->insns_[location.dex_pc]); - CHECK_EQ(Instruction::MOVE_EXCEPTION, instr->Opcode()); + const Instruction& instr = location.method->DexInstructions().InstructionAt(location.dex_pc); + CHECK_EQ(Instruction::MOVE_EXCEPTION, instr.Opcode()); } gJdwpState->PostLocationEvent(&location, this_object, event_flags, return_value); @@ -3809,9 +3853,9 @@ JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize // Find the dex_pc values that correspond to the current line, for line-based single-stepping. struct DebugCallbackContext { DebugCallbackContext(SingleStepControl* single_step_control_cb, - int32_t line_number_cb, const DexFile::CodeItem* code_item) + int32_t line_number_cb, uint32_t num_insns_in_code_units) : single_step_control_(single_step_control_cb), line_number_(line_number_cb), - code_item_(code_item), last_pc_valid(false), last_pc(0) { + num_insns_in_code_units_(num_insns_in_code_units), last_pc_valid(false), last_pc(0) { } static bool Callback(void* raw_context, const DexFile::PositionInfo& entry) { @@ -3837,8 +3881,7 @@ JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize ~DebugCallbackContext() { // If the line number was the last in the position table... if (last_pc_valid) { - size_t end = code_item_->insns_size_in_code_units_; - for (uint32_t dex_pc = last_pc; dex_pc < end; ++dex_pc) { + for (uint32_t dex_pc = last_pc; dex_pc < num_insns_in_code_units_; ++dex_pc) { single_step_control_->AddDexPc(dex_pc); } } @@ -3846,7 +3889,7 @@ JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize SingleStepControl* const single_step_control_; const int32_t line_number_; - const DexFile::CodeItem* const code_item_; + const uint32_t num_insns_in_code_units_; bool last_pc_valid; uint32_t last_pc; }; @@ -3865,9 +3908,11 @@ JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId thread_id, JDWP::JdwpStepSize // Note: if the thread is not running Java code (pure native thread), there is no "current" // method on the stack (and no line number either). if (m != nullptr && !m->IsNative()) { - const DexFile::CodeItem* const code_item = m->GetCodeItem(); - DebugCallbackContext context(single_step_control, line_number, code_item); - m->GetDexFile()->DecodeDebugPositionInfo(code_item, DebugCallbackContext::Callback, &context); + CodeItemDebugInfoAccessor accessor(m); + DebugCallbackContext context(single_step_control, line_number, accessor.InsnsSizeInCodeUnits()); + m->GetDexFile()->DecodeDebugPositionInfo(accessor.DebugInfoOffset(), + DebugCallbackContext::Callback, + &context); } // Activate single-step in the thread. @@ -4285,47 +4330,28 @@ void Dbg::FinishInvokeMethod(DebugInvokeReq* pReq) { } } -/* - * "request" contains a full JDWP packet, possibly with multiple chunks. We - * need to process each, accumulate the replies, and ship the whole thing - * back. - * - * Returns "true" if we have a reply. The reply buffer is newly allocated, - * and includes the chunk type/length, followed by the data. - * - * OLD-TODO: we currently assume that the request and reply include a single - * chunk. If this becomes inconvenient we will need to adapt. - */ -bool Dbg::DdmHandlePacket(JDWP::Request* request, uint8_t** pReplyBuf, int* pReplyLen) { - Thread* self = Thread::Current(); - JNIEnv* env = self->GetJniEnv(); - - uint32_t type = request->ReadUnsigned32("type"); - uint32_t length = request->ReadUnsigned32("length"); - - // Create a byte[] corresponding to 'request'. - size_t request_length = request->size(); - ScopedLocalRef dataArray(env, env->NewByteArray(request_length)); +bool Dbg::DdmHandleChunk(JNIEnv* env, + uint32_t type, + const ArrayRef& data, + /*out*/uint32_t* out_type, + /*out*/std::vector* out_data) { + ScopedLocalRef dataArray(env, env->NewByteArray(data.size())); if (dataArray.get() == nullptr) { - LOG(WARNING) << "byte[] allocation failed: " << request_length; + LOG(WARNING) << "byte[] allocation failed: " << data.size(); env->ExceptionClear(); return false; } - env->SetByteArrayRegion(dataArray.get(), 0, request_length, - reinterpret_cast(request->data())); - request->Skip(request_length); - - // Run through and find all chunks. [Currently just find the first.] - ScopedByteArrayRO contents(env, dataArray.get()); - if (length != request_length) { - LOG(WARNING) << StringPrintf("bad chunk found (len=%u pktLen=%zd)", length, request_length); - return false; - } - + env->SetByteArrayRegion(dataArray.get(), + 0, + data.size(), + reinterpret_cast(data.data())); // Call "private static Chunk dispatch(int type, byte[] data, int offset, int length)". - ScopedLocalRef chunk(env, env->CallStaticObjectMethod(WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer, - WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_dispatch, - type, dataArray.get(), 0, length)); + ScopedLocalRef chunk( + env, + env->CallStaticObjectMethod( + WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer, + WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_dispatch, + type, dataArray.get(), 0, data.size())); if (env->ExceptionCheck()) { LOG(INFO) << StringPrintf("Exception thrown by dispatcher for 0x%08x", type); env->ExceptionDescribe(); @@ -4349,30 +4375,83 @@ bool Dbg::DdmHandlePacket(JDWP::Request* request, uint8_t** pReplyBuf, int* pRep * * So we're pretty much stuck with copying data around multiple times. */ - ScopedLocalRef replyData(env, reinterpret_cast(env->GetObjectField(chunk.get(), WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_data))); - jint offset = env->GetIntField(chunk.get(), WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_offset); - length = env->GetIntField(chunk.get(), WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_length); - type = env->GetIntField(chunk.get(), WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_type); + ScopedLocalRef replyData( + env, + reinterpret_cast( + env->GetObjectField( + chunk.get(), WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_data))); + jint offset = env->GetIntField(chunk.get(), + WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_offset); + jint length = env->GetIntField(chunk.get(), + WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_length); + *out_type = env->GetIntField(chunk.get(), + WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_type); + + VLOG(jdwp) << StringPrintf("DDM reply: type=0x%08x data=%p offset=%d length=%d", + type, + replyData.get(), + offset, + length); + out_data->resize(length); + env->GetByteArrayRegion(replyData.get(), + offset, + length, + reinterpret_cast(out_data->data())); - VLOG(jdwp) << StringPrintf("DDM reply: type=0x%08x data=%p offset=%d length=%d", type, replyData.get(), offset, length); - if (length == 0 || replyData.get() == nullptr) { + if (env->ExceptionCheck()) { + LOG(INFO) << StringPrintf("Exception thrown when reading response data from dispatcher 0x%08x", + type); + env->ExceptionDescribe(); + env->ExceptionClear(); return false; } - const int kChunkHdrLen = 8; - uint8_t* reply = new uint8_t[length + kChunkHdrLen]; - if (reply == nullptr) { - LOG(WARNING) << "malloc failed: " << (length + kChunkHdrLen); + return true; +} + +/* + * "request" contains a full JDWP packet, possibly with multiple chunks. We + * need to process each, accumulate the replies, and ship the whole thing + * back. + * + * Returns "true" if we have a reply. The reply buffer is newly allocated, + * and includes the chunk type/length, followed by the data. + * + * OLD-TODO: we currently assume that the request and reply include a single + * chunk. If this becomes inconvenient we will need to adapt. + */ +bool Dbg::DdmHandlePacket(JDWP::Request* request, uint8_t** pReplyBuf, int* pReplyLen) { + Thread* self = Thread::Current(); + JNIEnv* env = self->GetJniEnv(); + + uint32_t type = request->ReadUnsigned32("type"); + uint32_t length = request->ReadUnsigned32("length"); + + // Create a byte[] corresponding to 'request'. + size_t request_length = request->size(); + // Run through and find all chunks. [Currently just find the first.] + if (length != request_length) { + LOG(WARNING) << StringPrintf("bad chunk found (len=%u pktLen=%zd)", length, request_length); return false; } - JDWP::Set4BE(reply + 0, type); - JDWP::Set4BE(reply + 4, length); - env->GetByteArrayRegion(replyData.get(), offset, length, reinterpret_cast(reply + kChunkHdrLen)); - - *pReplyBuf = reply; - *pReplyLen = length + kChunkHdrLen; - VLOG(jdwp) << StringPrintf("dvmHandleDdm returning type=%.4s %p len=%d", reinterpret_cast(reply), reply, length); + ArrayRef data(reinterpret_cast(request->data()), request_length); + std::vector out_data; + uint32_t out_type = 0; + request->Skip(request_length); + if (!DdmHandleChunk(env, type, data, &out_type, &out_data) || out_data.empty()) { + return false; + } + const uint32_t kDdmHeaderSize = 8; + *pReplyLen = out_data.size() + kDdmHeaderSize; + *pReplyBuf = new uint8_t[out_data.size() + kDdmHeaderSize]; + memcpy((*pReplyBuf) + kDdmHeaderSize, out_data.data(), out_data.size()); + JDWP::Set4BE(*pReplyBuf, out_type); + JDWP::Set4BE((*pReplyBuf) + 4, static_cast(out_data.size())); + VLOG(jdwp) + << StringPrintf("dvmHandleDdm returning type=%.4s", reinterpret_cast(*pReplyBuf)) + << "0x" << std::hex << reinterpret_cast(*pReplyBuf) << std::dec + << " len= " << out_data.size(); return true; } @@ -4417,10 +4496,11 @@ void Dbg::DdmSendThreadNotification(Thread* t, uint32_t type) { return; } + RuntimeCallbacks* cb = Runtime::Current()->GetRuntimeCallbacks(); if (type == CHUNK_TYPE("THDE")) { uint8_t buf[4]; JDWP::Set4BE(&buf[0], t->GetThreadId()); - Dbg::DdmSendChunk(CHUNK_TYPE("THDE"), 4, buf); + cb->DdmPublishChunk(CHUNK_TYPE("THDE"), ArrayRef(buf)); } else { CHECK(type == CHUNK_TYPE("THCR") || type == CHUNK_TYPE("THNM")) << type; ScopedObjectAccessUnchecked soa(Thread::Current()); @@ -4439,7 +4519,7 @@ void Dbg::DdmSendThreadNotification(Thread* t, uint32_t type) { JDWP::AppendUtf16BE(bytes, chars, char_count); } CHECK_EQ(bytes.size(), char_count*2 + sizeof(uint32_t)*2); - Dbg::DdmSendChunk(type, bytes); + cb->DdmPublishChunk(type, ArrayRef(bytes)); } } @@ -4482,26 +4562,6 @@ void Dbg::PostThreadDeath(Thread* t) { Dbg::PostThreadStartOrStop(t, CHUNK_TYPE("THDE")); } -void Dbg::DdmSendChunk(uint32_t type, size_t byte_count, const uint8_t* buf) { - CHECK(buf != nullptr); - iovec vec[1]; - vec[0].iov_base = reinterpret_cast(const_cast(buf)); - vec[0].iov_len = byte_count; - Dbg::DdmSendChunkV(type, vec, 1); -} - -void Dbg::DdmSendChunk(uint32_t type, const std::vector& bytes) { - DdmSendChunk(type, bytes.size(), &bytes[0]); -} - -void Dbg::DdmSendChunkV(uint32_t type, const iovec* iov, int iov_count) { - if (gJdwpState == nullptr) { - VLOG(jdwp) << "Debugger thread not active, ignoring DDM send: " << type; - } else { - gJdwpState->DdmSendChunkV(type, iov, iov_count); - } -} - JDWP::JdwpState* Dbg::GetJdwpState() { return gJdwpState; } @@ -4579,7 +4639,8 @@ void Dbg::DdmSendHeapInfo(HpifWhen reason) { JDWP::Append4BE(bytes, heap->GetBytesAllocated()); JDWP::Append4BE(bytes, heap->GetObjectsAllocated()); CHECK_EQ(bytes.size(), 4U + (heap_count * (4 + 8 + 1 + 4 + 4 + 4 + 4))); - Dbg::DdmSendChunk(CHUNK_TYPE("HPIF"), bytes); + Runtime::Current()->GetRuntimeCallbacks()->DdmPublishChunk(CHUNK_TYPE("HPIF"), + ArrayRef(bytes)); } enum HpsgSolidity { @@ -4665,7 +4726,8 @@ class HeapChunkContext { CHECK_LE(pieceLenField_, p_); JDWP::Set4BE(pieceLenField_, totalAllocationUnits_); - Dbg::DdmSendChunk(type_, p_ - &buf_[0], &buf_[0]); + ArrayRef out(&buf_[0], p_ - &buf_[0]); + Runtime::Current()->GetRuntimeCallbacks()->DdmPublishChunk(type_, out); Reset(); } @@ -4847,6 +4909,7 @@ void Dbg::DdmSendHeapSegments(bool native) { if (when == HPSG_WHEN_NEVER) { return; } + RuntimeCallbacks* cb = Runtime::Current()->GetRuntimeCallbacks(); // Figure out what kind of chunks we'll be sending. CHECK(what == HPSG_WHAT_MERGED_OBJECTS || what == HPSG_WHAT_DISTINCT_OBJECTS) << static_cast(what); @@ -4854,7 +4917,8 @@ void Dbg::DdmSendHeapSegments(bool native) { // First, send a heap start chunk. uint8_t heap_id[4]; JDWP::Set4BE(&heap_id[0], 1); // Heap id (bogus; we only have one heap). - Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHST") : CHUNK_TYPE("HPST"), sizeof(heap_id), heap_id); + cb->DdmPublishChunk(native ? CHUNK_TYPE("NHST") : CHUNK_TYPE("HPST"), + ArrayRef(heap_id)); Thread* self = Thread::Current(); Locks::mutator_lock_->AssertSharedHeld(self); @@ -4913,7 +4977,8 @@ void Dbg::DdmSendHeapSegments(bool native) { } // Finally, send a heap end chunk. - Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHEN") : CHUNK_TYPE("HPEN"), sizeof(heap_id), heap_id); + cb->DdmPublishChunk(native ? CHUNK_TYPE("NHEN") : CHUNK_TYPE("HPEN"), + ArrayRef(heap_id)); } void Dbg::SetAllocTrackingEnabled(bool enable) { diff --git a/runtime/debugger.h b/runtime/debugger.h index ec37833f6d4465086afae1380ca1cbf15d1cdda7..74018137a0b43717a21492a02986bb880fe48c96 100644 --- a/runtime/debugger.h +++ b/runtime/debugger.h @@ -27,6 +27,7 @@ #include #include +#include "base/array_ref.h" #include "class_linker.h" #include "gc_root.h" #include "handle.h" @@ -53,11 +54,20 @@ class StackVisitor; class Thread; struct DebuggerActiveMethodInspectionCallback : public MethodInspectionCallback { - bool IsMethodBeingInspected(ArtMethod* m ATTRIBUTE_UNUSED) + bool IsMethodBeingInspected(ArtMethod* method) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); + bool IsMethodSafeToJit(ArtMethod* method) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); +}; + +struct DebuggerDdmCallback : public DdmCallback { + void DdmPublishChunk(uint32_t type, const ArrayRef& data) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); - bool IsMethodSafeToJit(ArtMethod* m) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_); }; +struct InternalDebuggerControlCallback : public DebuggerControlCallback { + void StartDebugger() OVERRIDE; + void StopDebugger() OVERRIDE; + bool IsDebuggerConfigured() OVERRIDE; +}; /* * Invoke-during-breakpoint support. @@ -245,7 +255,8 @@ class Dbg { } // Configures JDWP with parsed command-line options. - static void ConfigureJdwp(const JDWP::JdwpOptions& jdwp_options); + static void ConfigureJdwp(const JDWP::JdwpOptions& jdwp_options) + REQUIRES_SHARED(Locks::mutator_lock_); // Returns true if we had -Xrunjdwp or -agentlib:jdwp= on the command line. static bool IsJdwpConfigured(); @@ -647,15 +658,15 @@ class Dbg { REQUIRES_SHARED(Locks::mutator_lock_); static void DdmSetThreadNotification(bool enable) REQUIRES(!Locks::thread_list_lock_); + static bool DdmHandleChunk( + JNIEnv* env, + uint32_t type, + const ArrayRef& data, + /*out*/uint32_t* out_type, + /*out*/std::vector* out_data); static bool DdmHandlePacket(JDWP::Request* request, uint8_t** pReplyBuf, int* pReplyLen); static void DdmConnected() REQUIRES_SHARED(Locks::mutator_lock_); static void DdmDisconnected() REQUIRES_SHARED(Locks::mutator_lock_); - static void DdmSendChunk(uint32_t type, const std::vector& bytes) - REQUIRES_SHARED(Locks::mutator_lock_); - static void DdmSendChunk(uint32_t type, size_t len, const uint8_t* buf) - REQUIRES_SHARED(Locks::mutator_lock_); - static void DdmSendChunkV(uint32_t type, const iovec* iov, int iov_count) - REQUIRES_SHARED(Locks::mutator_lock_); // Visit breakpoint roots, used to prevent unloading of methods with breakpoints. static void VisitRoots(RootVisitor* visitor) @@ -782,6 +793,8 @@ class Dbg { static bool gDebuggerActive; static DebuggerActiveMethodInspectionCallback gDebugActiveCallback; + static DebuggerDdmCallback gDebugDdmCallback; + static InternalDebuggerControlCallback gDebuggerControlCallback; // Indicates whether we should drop the JDWP connection because the runtime stops or the // debugger called VirtualMachine.Dispose. diff --git a/runtime/dex_file-inl.h b/runtime/dex_file-inl.h index 5dfbd9b6a197f6365a57bba71717c3435addefc3..a6f762120cc0cad19547022fc6f5b85258c8c31e 100644 --- a/runtime/dex_file-inl.h +++ b/runtime/dex_file-inl.h @@ -19,11 +19,12 @@ #include "base/bit_utils.h" #include "base/casts.h" -#include "base/logging.h" #include "base/stringpiece.h" +#include "cdex/compact_dex_file.h" #include "dex_file.h" #include "invoke_type.h" #include "leb128.h" +#include "standard_dex_file.h" namespace art { @@ -133,9 +134,13 @@ inline const char* DexFile::GetShorty(uint32_t proto_idx) const { } inline const DexFile::TryItem* DexFile::GetTryItems(const CodeItem& code_item, uint32_t offset) { - const uint16_t* insns_end_ = &code_item.insns_[code_item.insns_size_in_code_units_]; + return GetTryItems(code_item.Instructions().end(), offset); +} + +inline const DexFile::TryItem* DexFile::GetTryItems(const DexInstructionIterator& code_item_end, + uint32_t offset) { return reinterpret_cast - (RoundUp(reinterpret_cast(insns_end_), 4)) + offset; + (RoundUp(reinterpret_cast(&code_item_end.Inst()), 4)) + offset; } static inline bool DexFileStringEquals(const DexFile* df1, dex::StringIndex sidx1, @@ -379,12 +384,16 @@ bool DexFile::DecodeDebugLocalInfo(const uint8_t* stream, } template -bool DexFile::DecodeDebugLocalInfo(const CodeItem* code_item, +bool DexFile::DecodeDebugLocalInfo(uint32_t registers_size, + uint32_t ins_size, + uint32_t insns_size_in_code_units, + uint32_t debug_info_offset, bool is_static, uint32_t method_idx, NewLocalCallback new_local_callback, void* context) const { - if (code_item == nullptr) { + const uint8_t* const stream = GetDebugInfoStream(debug_info_offset); + if (stream == nullptr) { return false; } std::vector arg_descriptors; @@ -392,15 +401,15 @@ bool DexFile::DecodeDebugLocalInfo(const CodeItem* code_item, for (; it.HasNext(); it.Next()) { arg_descriptors.push_back(it.GetDescriptor()); } - return DecodeDebugLocalInfo(GetDebugInfoStream(code_item), + return DecodeDebugLocalInfo(stream, GetLocation(), GetMethodDeclaringClassDescriptor(GetMethodId(method_idx)), arg_descriptors, this->PrettyMethod(method_idx), is_static, - code_item->registers_size_, - code_item->ins_size_, - code_item->insns_size_in_code_units_, + registers_size, + ins_size, + insns_size_in_code_units, [this](uint32_t idx) { return StringDataByIdx(dex::StringIndex(idx)); }, @@ -481,13 +490,10 @@ bool DexFile::DecodeDebugPositionInfo(const uint8_t* stream, } template -bool DexFile::DecodeDebugPositionInfo(const CodeItem* code_item, +bool DexFile::DecodeDebugPositionInfo(uint32_t debug_info_offset, DexDebugNewPosition position_functor, void* context) const { - if (code_item == nullptr) { - return false; - } - return DecodeDebugPositionInfo(GetDebugInfoStream(code_item), + return DecodeDebugPositionInfo(GetDebugInfoStream(debug_info_offset), [this](uint32_t idx) { return StringDataByIdx(dex::StringIndex(idx)); }, @@ -495,6 +501,16 @@ bool DexFile::DecodeDebugPositionInfo(const CodeItem* code_item, context); } +inline const CompactDexFile* DexFile::AsCompactDexFile() const { + DCHECK(IsCompactDexFile()); + return down_cast(this); +} + +inline const StandardDexFile* DexFile::AsStandardDexFile() const { + DCHECK(IsStandardDexFile()); + return down_cast(this); +} + } // namespace art #endif // ART_RUNTIME_DEX_FILE_INL_H_ diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index 974c7acbb2e5c87acb00b328ca4a7b1da2e0bdec..4aed4026d20bbc04fd300ad8ee615c72689ceeb1 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -29,7 +29,6 @@ #include "android-base/stringprintf.h" #include "base/enums.h" -#include "base/logging.h" #include "base/stl_util.h" #include "dex_file-inl.h" #include "leb128.h" @@ -47,9 +46,13 @@ static_assert(sizeof(dex::TypeIndex) == sizeof(uint16_t), "TypeIndex size is wro static_assert(std::is_trivially_copyable::value, "TypeIndex not trivial"); uint32_t DexFile::CalculateChecksum() const { + return CalculateChecksum(Begin(), Size()); +} + +uint32_t DexFile::CalculateChecksum(const uint8_t* begin, size_t size) { const uint32_t non_sum = OFFSETOF_MEMBER(DexFile::Header, signature_); - const uint8_t* non_sum_ptr = Begin() + non_sum; - return adler32(adler32(0L, Z_NULL, 0), non_sum_ptr, Size() - non_sum); + const uint8_t* non_sum_ptr = begin + non_sum; + return adler32(adler32(0L, Z_NULL, 0), non_sum_ptr, size - non_sum); } int DexFile::GetPermissions() const { @@ -77,7 +80,8 @@ DexFile::DexFile(const uint8_t* base, const std::string& location, uint32_t location_checksum, const OatDexFile* oat_dex_file, - DexFileContainer* container) + DexFileContainer* container, + bool is_compact_dex) : begin_(base), size_(size), location_(location), @@ -94,7 +98,8 @@ DexFile::DexFile(const uint8_t* base, call_site_ids_(nullptr), num_call_site_ids_(0), oat_dex_file_(oat_dex_file), - container_(container) { + container_(container), + is_compact_dex_(is_compact_dex) { CHECK(begin_ != nullptr) << GetLocation(); CHECK_GT(size_, 0U) << GetLocation(); // Check base (=header) alignment. @@ -484,20 +489,18 @@ const Signature DexFile::CreateSignature(const StringPiece& signature) const { return Signature(this, *proto_id); } -int32_t DexFile::FindTryItem(const CodeItem &code_item, uint32_t address) { - // Note: Signed type is important for max and min. - int32_t min = 0; - int32_t max = code_item.tries_size_ - 1; - - while (min <= max) { - int32_t mid = min + ((max - min) / 2); +int32_t DexFile::FindTryItem(const TryItem* try_items, uint32_t tries_size, uint32_t address) { + uint32_t min = 0; + uint32_t max = tries_size; + while (min < max) { + const uint32_t mid = (min + max) / 2; - const art::DexFile::TryItem* ti = GetTryItems(code_item, mid); - uint32_t start = ti->start_addr_; - uint32_t end = start + ti->insn_count_; + const art::DexFile::TryItem& ti = try_items[mid]; + const uint32_t start = ti.start_addr_; + const uint32_t end = start + ti.insn_count_; if (address < start) { - max = mid - 1; + max = mid; } else if (address >= end) { min = mid + 1; } else { // We have a winner! @@ -509,12 +512,8 @@ int32_t DexFile::FindTryItem(const CodeItem &code_item, uint32_t address) { } int32_t DexFile::FindCatchHandlerOffset(const CodeItem &code_item, uint32_t address) { - int32_t try_item = FindTryItem(code_item, address); - if (try_item == -1) { - return -1; - } else { - return DexFile::GetTryItems(code_item, try_item)->handler_off_; - } + int32_t try_item = FindTryItem(GetTryItems(code_item, 0), code_item.tries_size_, address); + return (try_item == -1) ? -1 : DexFile::GetTryItems(code_item, try_item)->handler_off_; } bool DexFile::LineNumForPcCb(void* raw_context, const PositionInfo& entry) { diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 5c9b2585eb111965860709d6fbc42fb3148f45bb..f3a88f766c73ac3a1999fabb2412e106a5a5262c 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -21,8 +21,10 @@ #include #include +#include + #include "base/iteration_range.h" -#include "base/logging.h" +#include "base/macros.h" #include "base/value_object.h" #include "dex_file_types.h" #include "dex_instruction_iterator.h" @@ -32,10 +34,12 @@ namespace art { +class CompactDexFile; enum InvokeType : uint32_t; class MemMap; class OatDexFile; class Signature; +class StandardDexFile; class StringPiece; class ZipArchive; @@ -311,6 +315,12 @@ class DexFile { return *Instruction::At(insns_ + dex_pc); } + // Used when quickening / unquickening. + void SetDebugInfoOffset(uint32_t new_offset) { + debug_info_off_ = new_offset; + } + + private: uint16_t registers_size_; // the number of registers used by this code // (locals + parameters) uint16_t ins_size_; // the number of words of incoming arguments to the method @@ -320,11 +330,25 @@ class DexFile { uint16_t tries_size_; // the number of try_items for this instance. If non-zero, // then these appear as the tries array just after the // insns in this instance. - uint32_t debug_info_off_; // file offset to debug info stream + // Normally holds file offset to debug info stream. In case the method has been quickened + // holds an offset in the Vdex file containing both the actual debug_info_off and the + // quickening info offset. + // Don't use this field directly, use OatFile::GetDebugInfoOffset in general ART code, + // or DexFile::GetDebugInfoOffset in code that are not using a Runtime. + uint32_t debug_info_off_; + uint32_t insns_size_in_code_units_; // size of the insns array, in 2 byte code units uint16_t insns_[1]; // actual array of bytecode. private: + ART_FRIEND_TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor); + friend class CatchHandlerIterator; + friend class CodeItemDataAccessor; + friend class CodeItemDebugInfoAccessor; + friend class CodeItemInstructionAccessor; + friend class DexFile; // TODO: Remove this one when it's cleaned up. + friend class DexFileVerifier; + friend class VdexFile; // TODO: Remove this one when it's cleaned up. DISALLOW_COPY_AND_ASSIGN(CodeItem); }; @@ -696,6 +720,15 @@ class DexFile { return reinterpret_cast(addr); } + uint32_t GetDebugInfoOffset(const CodeItem* code_item) const { + if (code_item == nullptr) { + return 0; + } + CHECK(oat_dex_file_ == nullptr) + << "Should only use GetDebugInfoOffset in a non runtime setup"; + return code_item->debug_info_off_; + } + const char* GetReturnTypeDescriptor(const ProtoId& proto_id) const; // Returns the number of prototype identifiers in the .dex file. @@ -751,27 +784,32 @@ class DexFile { return begin_ + call_site_id.data_off_; } + static const TryItem* GetTryItems(const DexInstructionIterator& code_item_end, uint32_t offset); static const TryItem* GetTryItems(const CodeItem& code_item, uint32_t offset); // Get the base of the encoded data for the given DexCode. - static const uint8_t* GetCatchHandlerData(const CodeItem& code_item, uint32_t offset) { + static const uint8_t* GetCatchHandlerData(const DexInstructionIterator& code_item_end, + uint32_t tries_size, + uint32_t offset) { const uint8_t* handler_data = - reinterpret_cast(GetTryItems(code_item, code_item.tries_size_)); + reinterpret_cast(GetTryItems(code_item_end, tries_size)); return handler_data + offset; } + static const uint8_t* GetCatchHandlerData(const CodeItem& code_item, uint32_t offset) { + return GetCatchHandlerData(code_item.Instructions().end(), code_item.tries_size_, offset); + } // Find which try region is associated with the given address (ie dex pc). Returns -1 if none. - static int32_t FindTryItem(const CodeItem &code_item, uint32_t address); + static int32_t FindTryItem(const TryItem* try_items, uint32_t tries_size, uint32_t address); // Find the handler offset associated with the given address (ie dex pc). Returns -1 if none. static int32_t FindCatchHandlerOffset(const CodeItem &code_item, uint32_t address); // Get the pointer to the start of the debugging data - const uint8_t* GetDebugInfoStream(const CodeItem* code_item) const { + const uint8_t* GetDebugInfoStream(uint32_t debug_info_off) const { // Check that the offset is in bounds. // Note that although the specification says that 0 should be used if there // is no debug information, some applications incorrectly use 0xFFFFFFFF. - const uint32_t debug_info_off = code_item->debug_info_off_; return (debug_info_off == 0 || debug_info_off >= size_) ? nullptr : begin_ + debug_info_off; } @@ -927,7 +965,10 @@ class DexFile { NewLocalCallback new_local, void* context); template - bool DecodeDebugLocalInfo(const CodeItem* code_item, + bool DecodeDebugLocalInfo(uint32_t registers_size, + uint32_t ins_size, + uint32_t insns_size_in_code_units, + uint32_t debug_info_offset, bool is_static, uint32_t method_idx, NewLocalCallback new_local, @@ -940,7 +981,7 @@ class DexFile { DexDebugNewPosition position_functor, void* context); template - bool DecodeDebugPositionInfo(const CodeItem* code_item, + bool DecodeDebugPositionInfo(uint32_t debug_info_offset, DexDebugNewPosition position_functor, void* context) const; @@ -985,6 +1026,7 @@ class DexFile { // Recalculates the checksum of the dex file. Does not use the current value in the header. uint32_t CalculateChecksum() const; + static uint32_t CalculateChecksum(const uint8_t* begin, size_t size); // Returns a human-readable form of the method at an index. std::string PrettyMethod(uint32_t method_idx, bool with_signature = true) const; @@ -993,13 +1035,15 @@ class DexFile { // Returns a human-readable form of the type at an index. std::string PrettyType(dex::TypeIndex type_idx) const; - // Helper functions. - virtual bool IsCompactDexFile() const { - return false; + // Not virtual for performance reasons. + ALWAYS_INLINE bool IsCompactDexFile() const { + return is_compact_dex_; } - virtual bool IsStandardDexFile() const { - return false; + ALWAYS_INLINE bool IsStandardDexFile() const { + return !is_compact_dex_; } + ALWAYS_INLINE const StandardDexFile* AsStandardDexFile() const; + ALWAYS_INLINE const CompactDexFile* AsCompactDexFile() const; protected: DexFile(const uint8_t* base, @@ -1007,7 +1051,8 @@ class DexFile { const std::string& location, uint32_t location_checksum, const OatDexFile* oat_dex_file, - DexFileContainer* container); + DexFileContainer* container, + bool is_compact_dex); // Top-level initializer that calls other Init methods. bool Init(std::string* error_msg); @@ -1073,6 +1118,9 @@ class DexFile { // Manages the underlying memory allocation. std::unique_ptr container_; + // If the dex file is a compact dex file. If false then the dex file is a standard dex file. + const bool is_compact_dex_; + friend class DexFileLoader; friend class DexFileVerifierTest; friend class OatWriter; @@ -1178,6 +1226,11 @@ class ClassDataItemIterator { bool HasNextVirtualMethod() const { return pos_ >= EndOfDirectMethodsPos() && pos_ < EndOfVirtualMethodsPos(); } + bool HasNextMethod() const { + const bool result = pos_ >= EndOfInstanceFieldsPos() && pos_ < EndOfVirtualMethodsPos(); + DCHECK_EQ(result, HasNextDirectMethod() || HasNextVirtualMethod()); + return result; + } void SkipStaticFields() { while (HasNextStaticField()) { Next(); diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc index 845202ff726fb8a6ea8e2058f788ce754420d866..72b18fb420dc23f37b66b85d16a09c06e0545814 100644 --- a/runtime/dex_file_annotations.cc +++ b/runtime/dex_file_annotations.cc @@ -28,6 +28,7 @@ #include "jvalue-inl.h" #include "mirror/field.h" #include "mirror/method.h" +#include "oat_file.h" #include "reflection.h" #include "thread.h" #include "well_known_classes.h" @@ -261,32 +262,38 @@ const uint8_t* SearchEncodedAnnotation(const DexFile& dex_file, return nullptr; } -const DexFile::AnnotationSetItem* FindAnnotationSetForMethod(ArtMethod* method) - REQUIRES_SHARED(Locks::mutator_lock_) { - if (method->IsProxyMethod()) { - return nullptr; - } - const DexFile* dex_file = method->GetDexFile(); +const DexFile::AnnotationSetItem* FindAnnotationSetForMethod(const DexFile& dex_file, + const DexFile::ClassDef& class_def, + uint32_t method_index) { const DexFile::AnnotationsDirectoryItem* annotations_dir = - dex_file->GetAnnotationsDirectory(method->GetClassDef()); + dex_file.GetAnnotationsDirectory(class_def); if (annotations_dir == nullptr) { return nullptr; } const DexFile::MethodAnnotationsItem* method_annotations = - dex_file->GetMethodAnnotations(annotations_dir); + dex_file.GetMethodAnnotations(annotations_dir); if (method_annotations == nullptr) { return nullptr; } - uint32_t method_index = method->GetDexMethodIndex(); uint32_t method_count = annotations_dir->methods_size_; for (uint32_t i = 0; i < method_count; ++i) { if (method_annotations[i].method_idx_ == method_index) { - return dex_file->GetMethodAnnotationSetItem(method_annotations[i]); + return dex_file.GetMethodAnnotationSetItem(method_annotations[i]); } } return nullptr; } +inline const DexFile::AnnotationSetItem* FindAnnotationSetForMethod(ArtMethod* method) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (method->IsProxyMethod()) { + return nullptr; + } + return FindAnnotationSetForMethod(*method->GetDexFile(), + method->GetClassDef(), + method->GetDexMethodIndex()); +} + const DexFile::ParameterAnnotationsItem* FindAnnotationsItemForMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) { const DexFile* dex_file = method->GetDexFile(); @@ -336,8 +343,7 @@ mirror::Object* ProcessEncodedAnnotation(const ClassData& klass, const uint8_t** StackHandleScope<4> hs(self); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Handle annotation_class(hs.NewHandle( - class_linker->ResolveType(klass.GetDexFile(), - dex::TypeIndex(type_index), + class_linker->ResolveType(dex::TypeIndex(type_index), hs.NewHandle(klass.GetDexCache()), hs.NewHandle(klass.GetClassLoader())))); if (annotation_class == nullptr) { @@ -451,7 +457,7 @@ bool ProcessAnnotationValue(const ClassData& klass, } else { StackHandleScope<1> hs(self); element_object = Runtime::Current()->GetClassLinker()->ResolveString( - klass.GetDexFile(), dex::StringIndex(index), hs.NewHandle(klass.GetDexCache())); + dex::StringIndex(index), hs.NewHandle(klass.GetDexCache())); set_object = true; if (element_object == nullptr) { return false; @@ -467,7 +473,6 @@ bool ProcessAnnotationValue(const ClassData& klass, dex::TypeIndex type_index(index); StackHandleScope<2> hs(self); element_object = Runtime::Current()->GetClassLinker()->ResolveType( - klass.GetDexFile(), type_index, hs.NewHandle(klass.GetDexCache()), hs.NewHandle(klass.GetClassLoader())); @@ -494,7 +499,6 @@ bool ProcessAnnotationValue(const ClassData& klass, ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); StackHandleScope<2> hs(self); ArtMethod* method = class_linker->ResolveMethodWithoutInvokeType( - klass.GetDexFile(), index, hs.NewHandle(klass.GetDexCache()), hs.NewHandle(klass.GetClassLoader())); @@ -533,7 +537,6 @@ bool ProcessAnnotationValue(const ClassData& klass, } else { StackHandleScope<2> hs(self); ArtField* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS( - klass.GetDexFile(), index, hs.NewHandle(klass.GetDexCache()), hs.NewHandle(klass.GetClassLoader())); @@ -562,7 +565,6 @@ bool ProcessAnnotationValue(const ClassData& klass, } else { StackHandleScope<3> hs(self); ArtField* enum_field = Runtime::Current()->GetClassLinker()->ResolveField( - klass.GetDexFile(), index, hs.NewHandle(klass.GetDexCache()), hs.NewHandle(klass.GetClassLoader()), @@ -764,8 +766,7 @@ const DexFile::AnnotationItem* GetAnnotationItemFromAnnotationSet( const ClassData& klass, const DexFile::AnnotationSetItem* annotation_set, uint32_t visibility, - Handle annotation_class, - bool lookup_in_resolved_boot_classes = false) + Handle annotation_class) REQUIRES_SHARED(Locks::mutator_lock_) { const DexFile& dex_file = klass.GetDexFile(); for (uint32_t i = 0; i < annotation_set->size_; ++i) { @@ -777,36 +778,18 @@ const DexFile::AnnotationItem* GetAnnotationItemFromAnnotationSet( uint32_t type_index = DecodeUnsignedLeb128(&annotation); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Thread* self = Thread::Current(); - mirror::Class* resolved_class; - if (lookup_in_resolved_boot_classes) { - // Note: We cannot use ClassLinker::LookupResolvedType() because the current DexCache - // may not be registered with the boot class path ClassLoader and we must not pollute - // the DexCache with classes that are not in the associated ClassLoader's ClassTable. - const char* descriptor = dex_file.StringByTypeIdx(dex::TypeIndex(type_index)); - ObjPtr looked_up_class = - class_linker->LookupClass(self, descriptor, /* class_loader */ nullptr); - resolved_class = looked_up_class.Ptr(); - if (resolved_class == nullptr) { - // If `resolved_class` is null, this is fine: just ignore that - // annotation item. We expect this to happen, as we do not - // attempt to resolve the annotation's class in this code path. - continue; - } - } else { - StackHandleScope<2> hs(self); - resolved_class = class_linker->ResolveType( - klass.GetDexFile(), - dex::TypeIndex(type_index), - hs.NewHandle(klass.GetDexCache()), - hs.NewHandle(klass.GetClassLoader())); - if (resolved_class == nullptr) { - std::string temp; - LOG(WARNING) << StringPrintf("Unable to resolve %s annotation class %d", - klass.GetRealClass()->GetDescriptor(&temp), type_index); - CHECK(self->IsExceptionPending()); - self->ClearException(); - continue; - } + StackHandleScope<2> hs(self); + ObjPtr resolved_class = class_linker->ResolveType( + dex::TypeIndex(type_index), + hs.NewHandle(klass.GetDexCache()), + hs.NewHandle(klass.GetClassLoader())); + if (resolved_class == nullptr) { + std::string temp; + LOG(WARNING) << StringPrintf("Unable to resolve %s annotation class %d", + klass.GetRealClass()->GetDescriptor(&temp), type_index); + CHECK(self->IsExceptionPending()); + self->ClearException(); + continue; } if (resolved_class == annotation_class.Get()) { return annotation_item; @@ -822,8 +805,8 @@ mirror::Object* GetAnnotationObjectFromAnnotationSet( uint32_t visibility, Handle annotation_class) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile::AnnotationItem* annotation_item = - GetAnnotationItemFromAnnotationSet(klass, annotation_set, visibility, annotation_class); + const DexFile::AnnotationItem* annotation_item = GetAnnotationItemFromAnnotationSet( + klass, annotation_set, visibility, annotation_class); if (annotation_item == nullptr) { return nullptr; } @@ -1235,21 +1218,78 @@ mirror::ObjectArray* GetSignatureAnnotationForMethod(ArtMethod* bool IsMethodAnnotationPresent(ArtMethod* method, Handle annotation_class, - uint32_t visibility /* = DexFile::kDexVisibilityRuntime */, - bool lookup_in_resolved_boot_classes /* = false */) { + uint32_t visibility /* = DexFile::kDexVisibilityRuntime */) { const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForMethod(method); if (annotation_set == nullptr) { return false; } - const DexFile::AnnotationItem* annotation_item = - GetAnnotationItemFromAnnotationSet(ClassData(method), - annotation_set, - visibility, - annotation_class, - lookup_in_resolved_boot_classes); + const DexFile::AnnotationItem* annotation_item = GetAnnotationItemFromAnnotationSet( + ClassData(method), annotation_set, visibility, annotation_class); return annotation_item != nullptr; } +static void DCheckNativeAnnotation(const char* descriptor, jclass cls) { + if (kIsDebugBuild) { + ScopedObjectAccess soa(Thread::Current()); + ObjPtr klass = soa.Decode(cls); + ClassLinker* linker = Runtime::Current()->GetClassLinker(); + // WellKnownClasses may not be initialized yet, so `klass` may be null. + if (klass != nullptr) { + // Lookup using the boot class path loader should yield the annotation class. + CHECK_EQ(klass, linker->LookupClass(soa.Self(), descriptor, /* class_loader */ nullptr)); + } + } +} + +// Check whether a method from the `dex_file` with the given `annotation_set` +// is annotated with `annotation_descriptor` with build visibility. +static bool IsMethodBuildAnnotationPresent(const DexFile& dex_file, + const DexFile::AnnotationSetItem& annotation_set, + const char* annotation_descriptor, + jclass annotation_class) { + for (uint32_t i = 0; i < annotation_set.size_; ++i) { + const DexFile::AnnotationItem* annotation_item = dex_file.GetAnnotationItem(&annotation_set, i); + if (!IsVisibilityCompatible(annotation_item->visibility_, DexFile::kDexVisibilityBuild)) { + continue; + } + const uint8_t* annotation = annotation_item->annotation_; + uint32_t type_index = DecodeUnsignedLeb128(&annotation); + const char* descriptor = dex_file.StringByTypeIdx(dex::TypeIndex(type_index)); + if (strcmp(descriptor, annotation_descriptor) == 0) { + DCheckNativeAnnotation(descriptor, annotation_class); + return true; + } + } + return false; +} + +uint32_t GetNativeMethodAnnotationAccessFlags(const DexFile& dex_file, + const DexFile::ClassDef& class_def, + uint32_t method_index) { + const DexFile::AnnotationSetItem* annotation_set = + FindAnnotationSetForMethod(dex_file, class_def, method_index); + if (annotation_set == nullptr) { + return 0u; + } + uint32_t access_flags = 0u; + if (IsMethodBuildAnnotationPresent( + dex_file, + *annotation_set, + "Ldalvik/annotation/optimization/FastNative;", + WellKnownClasses::dalvik_annotation_optimization_FastNative)) { + access_flags |= kAccFastNative; + } + if (IsMethodBuildAnnotationPresent( + dex_file, + *annotation_set, + "Ldalvik/annotation/optimization/CriticalNative;", + WellKnownClasses::dalvik_annotation_optimization_CriticalNative)) { + access_flags |= kAccCriticalNative; + } + CHECK_NE(access_flags, kAccFastNative | kAccCriticalNative); + return access_flags; +} + mirror::Object* GetAnnotationForClass(Handle klass, Handle annotation_class) { ClassData data(klass); @@ -1354,7 +1394,6 @@ mirror::Class* GetEnclosingClass(Handle klass) { } StackHandleScope<2> hs(Thread::Current()); ArtMethod* method = Runtime::Current()->GetClassLinker()->ResolveMethodWithoutInvokeType( - data.GetDexFile(), annotation_value.value_.GetI(), hs.NewHandle(data.GetDexCache()), hs.NewHandle(data.GetClassLoader())); @@ -1520,19 +1559,18 @@ int32_t GetLineNumFromPC(const DexFile* dex_file, ArtMethod* method, uint32_t re return -2; } - const DexFile::CodeItem* code_item = dex_file->GetCodeItem(method->GetCodeItemOffset()); - DCHECK(code_item != nullptr) << method->PrettyMethod() << " " << dex_file->GetLocation(); + CodeItemDebugInfoAccessor accessor(method); + DCHECK(accessor.HasCodeItem()) << method->PrettyMethod() << " " << dex_file->GetLocation(); // A method with no line number info should return -1 DexFile::LineNumFromPcContext context(rel_pc, -1); - dex_file->DecodeDebugPositionInfo(code_item, DexFile::LineNumForPcCb, &context); + dex_file->DecodeDebugPositionInfo(accessor.DebugInfoOffset(), DexFile::LineNumForPcCb, &context); return context.line_num_; } template void RuntimeEncodedStaticFieldValueIterator::ReadValueToField(ArtField* field) const { DCHECK(dex_cache_ != nullptr); - DCHECK(class_loader_ != nullptr); switch (type_) { case kBoolean: field->SetBoolean(field->GetDeclaringClass(), jval_.z); break; @@ -1545,17 +1583,15 @@ void RuntimeEncodedStaticFieldValueIterator::ReadValueToField(ArtField* field) c case kDouble: field->SetDouble(field->GetDeclaringClass(), jval_.d); break; case kNull: field->SetObject(field->GetDeclaringClass(), nullptr); break; case kString: { - mirror::String* resolved = linker_->ResolveString(dex_file_, - dex::StringIndex(jval_.i), - *dex_cache_); + ObjPtr resolved = linker_->ResolveString(dex::StringIndex(jval_.i), + dex_cache_); field->SetObject(field->GetDeclaringClass(), resolved); break; } case kType: { - mirror::Class* resolved = linker_->ResolveType(dex_file_, - dex::TypeIndex(jval_.i), - *dex_cache_, - *class_loader_); + ObjPtr resolved = linker_->ResolveType(dex::TypeIndex(jval_.i), + dex_cache_, + class_loader_); field->SetObject(field->GetDeclaringClass(), resolved); break; } diff --git a/runtime/dex_file_annotations.h b/runtime/dex_file_annotations.h index 9dc400d6c24fdf9c33b599f6025bde0b4129b6ae..9ff09291767abe7c6c5facbba6b6b59267f5741c 100644 --- a/runtime/dex_file_annotations.h +++ b/runtime/dex_file_annotations.h @@ -19,18 +19,18 @@ #include "dex_file.h" +#include "handle.h" +#include "mirror/dex_cache.h" #include "mirror/object_array.h" namespace art { namespace mirror { class ClassLoader; -class DexCache; } // namespace mirror class ArtField; class ArtMethod; class ClassLinker; -template class MutableHandle; namespace annotations { @@ -72,9 +72,15 @@ mirror::ObjectArray* GetSignatureAnnotationForMethod(ArtMethod* // side effect. bool IsMethodAnnotationPresent(ArtMethod* method, Handle annotation_class, - uint32_t visibility = DexFile::kDexVisibilityRuntime, - bool lookup_in_resolved_boot_classes = false) + uint32_t visibility = DexFile::kDexVisibilityRuntime) REQUIRES_SHARED(Locks::mutator_lock_); +// Check whether a method from the `dex_file` with the given `method_index` +// is annotated with @dalvik.annotation.optimization.FastNative or +// @dalvik.annotation.optimization.CriticalNative with build visibility. +// If yes, return the associated access flags, i.e. kAccFastNative or kAccCriticalNative. +uint32_t GetNativeMethodAnnotationAccessFlags(const DexFile& dex_file, + const DexFile::ClassDef& class_def, + uint32_t method_index); // Class annotations. mirror::Object* GetAnnotationForClass(Handle klass, @@ -110,13 +116,12 @@ int32_t GetLineNumFromPC(const DexFile* dex_file, ArtMethod* method, uint32_t re class RuntimeEncodedStaticFieldValueIterator : public EncodedStaticFieldValueIterator { public: // A constructor meant to be called from runtime code. - RuntimeEncodedStaticFieldValueIterator(const DexFile& dex_file, - Handle* dex_cache, - Handle* class_loader, + RuntimeEncodedStaticFieldValueIterator(Handle dex_cache, + Handle class_loader, ClassLinker* linker, const DexFile::ClassDef& class_def) REQUIRES_SHARED(Locks::mutator_lock_) - : EncodedStaticFieldValueIterator(dex_file, class_def), + : EncodedStaticFieldValueIterator(*dex_cache->GetDexFile(), class_def), dex_cache_(dex_cache), class_loader_(class_loader), linker_(linker) { @@ -126,9 +131,9 @@ class RuntimeEncodedStaticFieldValueIterator : public EncodedStaticFieldValueIte void ReadValueToField(ArtField* field) const REQUIRES_SHARED(Locks::mutator_lock_); private: - Handle* const dex_cache_; // Dex cache to resolve literal objects. - Handle* const class_loader_; // ClassLoader to resolve types. - ClassLinker* linker_; // Linker to resolve literal objects. + const Handle dex_cache_; // Dex cache to resolve literal objects. + const Handle class_loader_; // ClassLoader to resolve types. + ClassLinker* const linker_; // Linker to resolve literal objects. DISALLOW_IMPLICIT_CONSTRUCTORS(RuntimeEncodedStaticFieldValueIterator); }; diff --git a/runtime/dex_file_layout.cc b/runtime/dex_file_layout.cc index c3fae15b14d80c364bcddbc00c159dc3c135355e..1973440d55d13ab0f278eca4cd9e388c04b9bc7c 100644 --- a/runtime/dex_file_layout.cc +++ b/runtime/dex_file_layout.cc @@ -26,10 +26,10 @@ namespace art { void DexLayoutSection::Subsection::Madvise(const DexFile* dex_file, int advice) const { DCHECK(dex_file != nullptr); - DCHECK_LE(size_, dex_file->Size()); - DCHECK_LE(offset_ + size_, dex_file->Size()); - MadviseLargestPageAlignedRegion(dex_file->Begin() + offset_, - dex_file->Begin() + offset_ + size_, + DCHECK_LT(start_offset_, dex_file->Size()); + DCHECK_LE(end_offset_, dex_file->Size()); + MadviseLargestPageAlignedRegion(dex_file->Begin() + start_offset_, + dex_file->Begin() + end_offset_, advice); } @@ -69,7 +69,7 @@ std::ostream& operator<<(std::ostream& os, const DexLayoutSection& section) { for (size_t i = 0; i < static_cast(LayoutType::kLayoutTypeCount); ++i) { const DexLayoutSection::Subsection& part = section.parts_[i]; os << static_cast(i) << "(" - << part.offset_ << "-" << part.offset_ + part.size_ << ") "; + << part.start_offset_ << "-" << part.end_offset_ << ") "; } return os; } diff --git a/runtime/dex_file_layout.h b/runtime/dex_file_layout.h index 40cc91232e46ff1ab3cba9f38705cd5b357a0e20..9fac5f8458ededebdd88aa187edfd513444f7bb0 100644 --- a/runtime/dex_file_layout.h +++ b/runtime/dex_file_layout.h @@ -17,22 +17,25 @@ #ifndef ART_RUNTIME_DEX_FILE_LAYOUT_H_ #define ART_RUNTIME_DEX_FILE_LAYOUT_H_ +#include #include #include +#include + namespace art { class DexFile; enum class LayoutType : uint8_t { + // Layout of things that are hot (commonly accessed), these should be pinned or madvised will + // need. + kLayoutTypeHot, // Layout of things that are randomly used. These should be advised to random access. // Without layout, this is the default mode when loading a dex file. kLayoutTypeSometimesUsed, // Layout of things that are only used during startup, these can be madvised after launch. kLayoutTypeStartupOnly, - // Layout of things that are hot (commonly accessed), these should be pinned or madvised will - // need. - kLayoutTypeHot, // Layout of things that are needed probably only once (class initializers). These can be // madvised during trim events. kLayoutTypeUsedOnce, @@ -44,6 +47,11 @@ enum class LayoutType : uint8_t { }; std::ostream& operator<<(std::ostream& os, const LayoutType& collector_type); +// Return the "best" layout option if the same item has multiple different layouts. +static inline LayoutType MergeLayoutType(LayoutType a, LayoutType b) { + return std::min(a, b); +} + enum class MadviseState : uint8_t { // Madvise based on a file that was just loaded. kMadviseStateAtLoad, @@ -55,15 +63,35 @@ enum class MadviseState : uint8_t { std::ostream& operator<<(std::ostream& os, const MadviseState& collector_type); // A dex layout section such as code items or strings. Each section is composed of subsections -// that are layed out ajacently to each other such as (hot, unused, startup, etc...). +// that are laid out adjacently to each other such as (hot, unused, startup, etc...). class DexLayoutSection { public: // A subsection is a a continuous range of dex file that is all part of the same layout hint. class Subsection { public: // Use uint32_t to handle 32/64 bit cross compilation. - uint32_t offset_ = 0u; - uint32_t size_ = 0u; + uint32_t start_offset_ = 0u; + uint32_t end_offset_ = 0u; + + bool Contains(uint32_t offset) const { + return start_offset_ <= offset && offset < end_offset_; + } + + bool Size() const { + DCHECK_LE(start_offset_, end_offset_); + return end_offset_ - start_offset_; + } + + void CombineSection(uint32_t start_offset, uint32_t end_offset) { + DCHECK_LT(start_offset, end_offset); + if (start_offset_ == end_offset_) { + start_offset_ = start_offset; + end_offset_ = end_offset; + } else { + start_offset_ = std::min(start_offset_, start_offset); + end_offset_ = std::max(end_offset_, end_offset); + } + } void Madvise(const DexFile* dex_file, int advice) const; }; diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc index 90bc4b8f94a885cfe150c65e5bf84b908dfeb5ec..87eec571f196c94b4de8a9e17d5712d0778fc63c 100644 --- a/runtime/dex_file_test.cc +++ b/runtime/dex_file_test.cc @@ -22,6 +22,7 @@ #include "base/stl_util.h" #include "base/unix_file/fd_file.h" +#include "code_item_accessors-inl.h" #include "common_runtime_test.h" #include "dex_file-inl.h" #include "dex_file_loader.h" @@ -487,52 +488,52 @@ TEST_F(DexFileTest, GetMethodSignature) { "(IDJLjava/lang/Object;)Ljava/lang/Float;", "java.lang.Float GetMethodSignature.m1(int, double, long, java.lang.Object)" }, - { // NOLINT [whitespace/braces] [4] + { "m2", "(ZSC)LGetMethodSignature;", "GetMethodSignature GetMethodSignature.m2(boolean, short, char)" }, - { // NOLINT [whitespace/braces] [4] + { "m3", "()V", "void GetMethodSignature.m3()" }, - { // NOLINT [whitespace/braces] [4] + { "m4", "(I)V", "void GetMethodSignature.m4(int)" }, - { // NOLINT [whitespace/braces] [4] + { "m5", "(II)V", "void GetMethodSignature.m5(int, int)" }, - { // NOLINT [whitespace/braces] [4] + { "m6", "(II[[I)V", "void GetMethodSignature.m6(int, int, int[][])" }, - { // NOLINT [whitespace/braces] [4] + { "m7", "(II[[ILjava/lang/Object;)V", "void GetMethodSignature.m7(int, int, int[][], java.lang.Object)" }, - { // NOLINT [whitespace/braces] [4] + { "m8", "(II[[ILjava/lang/Object;[[Ljava/lang/Object;)V", "void GetMethodSignature.m8(int, int, int[][], java.lang.Object, java.lang.Object[][])" }, - { // NOLINT [whitespace/braces] [4] + { "m9", "()I", "int GetMethodSignature.m9()" }, - { // NOLINT [whitespace/braces] [4] + { "mA", "()[[I", "int[][] GetMethodSignature.mA()" }, - { // NOLINT [whitespace/braces] [4] + { "mB", "()[[Ljava/lang/Object;", "java.lang.Object[][] GetMethodSignature.mB()" @@ -730,7 +731,8 @@ TEST_F(DexFileTest, OpenDexDebugInfoLocalNullType) { kRawDexDebugInfoLocalNullType, tmp.GetFilename().c_str(), 0xf25f2b38U, true); const DexFile::ClassDef& class_def = raw->GetClassDef(0); const DexFile::CodeItem* code_item = raw->GetCodeItem(raw->FindCodeItemOffset(class_def, 1)); - ASSERT_TRUE(raw->DecodeDebugLocalInfo(code_item, true, 1, Callback, nullptr)); + CodeItemDebugInfoAccessor accessor(raw.get(), code_item); + ASSERT_TRUE(accessor.DecodeDebugLocalInfo(true, 1, Callback, nullptr)); } } // namespace art diff --git a/runtime/dex_file_tracking_registrar.cc b/runtime/dex_file_tracking_registrar.cc index 341158671b80e5475ae5a7851e92e9040a1f7a57..c157ddc790cfd9287a711da9f1710035dca3eb04 100644 --- a/runtime/dex_file_tracking_registrar.cc +++ b/runtime/dex_file_tracking_registrar.cc @@ -19,6 +19,8 @@ #include #include +#include + // For dex tracking through poisoning. Note: Requires forcing sanitization. This is the reason for // the ifdefs and early include. #ifdef ART_DEX_FILE_ACCESS_TRACKING @@ -28,7 +30,7 @@ #endif #include "base/memory_tool.h" -#include "base/logging.h" +#include "code_item_accessors-inl.h" #include "dex_file-inl.h" namespace art { @@ -158,7 +160,7 @@ void DexFileTrackingRegistrar::SetAllCodeItemRegistration(bool should_poison) { if (class_data != nullptr) { ClassDataItemIterator cdit(*dex_file_, class_data); cdit.SkipAllFields(); - while (cdit.HasNextDirectMethod()) { + while (cdit.HasNextMethod()) { const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem(); if (code_item != nullptr) { const void* code_item_begin = reinterpret_cast(code_item); @@ -178,14 +180,17 @@ void DexFileTrackingRegistrar::SetAllCodeItemStartRegistration(bool should_poiso if (class_data != nullptr) { ClassDataItemIterator cdit(*dex_file_, class_data); cdit.SkipAllFields(); - while (cdit.HasNextDirectMethod()) { + while (cdit.HasNextMethod()) { const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem(); if (code_item != nullptr) { const void* code_item_begin = reinterpret_cast(code_item); size_t code_item_start = reinterpret_cast(code_item); - size_t code_item_start_end = reinterpret_cast(&code_item->insns_[1]); + CodeItemInstructionAccessor accessor(dex_file_, code_item); + size_t code_item_start_end = reinterpret_cast(accessor.Insns()); size_t code_item_start_size = code_item_start_end - code_item_start; - range_values_.push_back(std::make_tuple(code_item_begin, code_item_start_size, should_poison)); + range_values_.push_back(std::make_tuple(code_item_begin, + code_item_start_size, + should_poison)); } cdit.Next(); } @@ -200,12 +205,13 @@ void DexFileTrackingRegistrar::SetAllInsnsRegistration(bool should_poison) { if (class_data != nullptr) { ClassDataItemIterator cdit(*dex_file_, class_data); cdit.SkipAllFields(); - while (cdit.HasNextDirectMethod()) { + while (cdit.HasNextMethod()) { const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem(); if (code_item != nullptr) { - const void* insns_begin = reinterpret_cast(&code_item->insns_); + CodeItemInstructionAccessor accessor(dex_file_, code_item); + const void* insns_begin = reinterpret_cast(accessor.Insns()); // Member insns_size_in_code_units_ is in 2-byte units - size_t insns_size = code_item->insns_size_in_code_units_ * 2; + size_t insns_size = accessor.InsnsSizeInCodeUnits() * 2; range_values_.push_back(std::make_tuple(insns_begin, insns_size, should_poison)); } cdit.Next(); @@ -221,7 +227,7 @@ void DexFileTrackingRegistrar::SetCodeItemRegistration(const char* class_name, b if (class_data != nullptr) { ClassDataItemIterator cdit(*dex_file_, class_data); cdit.SkipAllFields(); - while (cdit.HasNextDirectMethod()) { + while (cdit.HasNextMethod()) { const DexFile::MethodId& methodid_item = dex_file_->GetMethodId(cdit.GetMemberIndex()); const char * methodid_name = dex_file_->GetMethodName(methodid_item); const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem(); diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc index 50f56c799a8b987aa94019d436bfde61cab42275..edf5650df1638c087b0a397fa01ac378b507a960 100644 --- a/runtime/dex_file_verifier.cc +++ b/runtime/dex_file_verifier.cc @@ -471,7 +471,9 @@ bool DexFileVerifier::CheckMap() { if (IsDataSectionType(item_type)) { uint32_t icount = item->size_; if (UNLIKELY(icount > data_items_left)) { - ErrorStringPrintf("Too many items in data section: %ud", data_item_count + icount); + ErrorStringPrintf("Too many items in data section: %ud item_type %zx", + data_item_count + icount, + static_cast(item_type)); return false; } data_items_left -= icount; @@ -1970,7 +1972,7 @@ dex::TypeIndex DexFileVerifier::FindFirstClassDataDefiner(const uint8_t* ptr, bo return field->class_idx_; } - if (it.HasNextDirectMethod() || it.HasNextVirtualMethod()) { + if (it.HasNextMethod()) { LOAD_METHOD(method, it.GetMemberIndex(), "first_class_data_definer method_id", *success = false; return dex::TypeIndex(DexFile::kDexNoIndex16)) return method->class_idx_; @@ -2566,7 +2568,7 @@ bool DexFileVerifier::CheckInterClassDataItem() { return false; } } - for (; it.HasNextDirectMethod() || it.HasNextVirtualMethod(); it.Next()) { + for (; it.HasNextMethod(); it.Next()) { uint32_t code_off = it.GetMethodCodeItemOffset(); if (code_off != 0 && !CheckOffsetToTypeMap(code_off, DexFile::kDexTypeCodeItem)) { return false; diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc index ee577e7d9a670deef76f83d31f87308075340ef6..d4d912cbfb41f2c7de4f7f0c5a6d56f6ca7e0b62 100644 --- a/runtime/dex_file_verifier_test.cc +++ b/runtime/dex_file_verifier_test.cc @@ -249,7 +249,7 @@ static const uint8_t* FindMethodData(const DexFile* dex_file, it.Next(); } - while (it.HasNextDirectMethod() || it.HasNextVirtualMethod()) { + while (it.HasNextMethod()) { uint32_t method_index = it.GetMemberIndex(); dex::StringIndex name_index = dex_file->GetMethodId(method_index).name_idx_; const DexFile::StringId& string_id = dex_file->GetStringId(name_index); diff --git a/runtime/dex_instruction.cc b/runtime/dex_instruction.cc index e64c0f62c29b59712df520538bc5b106241daba0..6ebe2286e8bb9ce4ee63ed2de498174fe5dd8e14 100644 --- a/runtime/dex_instruction.cc +++ b/runtime/dex_instruction.cc @@ -548,4 +548,14 @@ std::ostream& operator<<(std::ostream& os, const Instruction::Code& code) { return os << Instruction::Name(code); } +uint32_t RangeInstructionOperands::GetOperand(size_t operand_index) const { + DCHECK_LT(operand_index, GetNumberOfOperands()); + return first_operand_ + operand_index; +} + +uint32_t VarArgsInstructionOperands::GetOperand(size_t operand_index) const { + DCHECK_LT(operand_index, GetNumberOfOperands()); + return operands_[operand_index]; +} + } // namespace art diff --git a/runtime/dex_instruction.h b/runtime/dex_instruction.h index 09c78b2428513cab4af3d21bac18b01a4d339704..3ced6920e203abc1f5a79c26968f4c866a141477 100644 --- a/runtime/dex_instruction.h +++ b/runtime/dex_instruction.h @@ -17,7 +17,8 @@ #ifndef ART_RUNTIME_DEX_INSTRUCTION_H_ #define ART_RUNTIME_DEX_INSTRUCTION_H_ -#include "base/logging.h" +#include + #include "base/macros.h" #include "globals.h" @@ -689,6 +690,53 @@ std::ostream& operator<<(std::ostream& os, const Instruction::Format& format); std::ostream& operator<<(std::ostream& os, const Instruction::Flags& flags); std::ostream& operator<<(std::ostream& os, const Instruction::VerifyFlag& vflags); +// Base class for accessing instruction operands. Unifies operand +// access for instructions that have range and varargs forms +// (e.g. invoke-polymoprhic/range and invoke-polymorphic). +class InstructionOperands { + public: + explicit InstructionOperands(size_t num_operands) : num_operands_(num_operands) {} + virtual ~InstructionOperands() {} + virtual uint32_t GetOperand(size_t index) const = 0; + size_t GetNumberOfOperands() const { return num_operands_; } + + private: + size_t num_operands_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(InstructionOperands); +}; + +// Class for accessing operands for instructions with a range format +// (e.g. 3rc and 4rcc). +class RangeInstructionOperands FINAL : public InstructionOperands { + public: + RangeInstructionOperands(uint32_t first_operand, size_t num_operands) + : InstructionOperands(num_operands), first_operand_(first_operand) {} + ~RangeInstructionOperands() {} + uint32_t GetOperand(size_t operand_index) const OVERRIDE; + + private: + const uint32_t first_operand_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(RangeInstructionOperands); +}; + +// Class for accessing operands for instructions with a variable +// number of arguments format (e.g. 35c and 45cc). +class VarArgsInstructionOperands FINAL : public InstructionOperands { + public: + VarArgsInstructionOperands(const uint32_t (&operands)[Instruction::kMaxVarArgRegs], + size_t num_operands) + : InstructionOperands(num_operands), operands_(operands) {} + ~VarArgsInstructionOperands() {} + uint32_t GetOperand(size_t operand_index) const OVERRIDE; + + private: + const uint32_t (&operands_)[Instruction::kMaxVarArgRegs]; + + DISALLOW_IMPLICIT_CONSTRUCTORS(VarArgsInstructionOperands); +}; + } // namespace art #endif // ART_RUNTIME_DEX_INSTRUCTION_H_ diff --git a/runtime/dex_instruction_iterator.h b/runtime/dex_instruction_iterator.h index be583a2533b3ea7efed796b27f9461e72a56ec54..eabe009a0b6c2469f9d937da8ebda95b9ad9dd4a 100644 --- a/runtime/dex_instruction_iterator.h +++ b/runtime/dex_instruction_iterator.h @@ -19,8 +19,10 @@ #include +#include + #include "dex_instruction.h" -#include "base/logging.h" +#include "base/macros.h" namespace art { diff --git a/runtime/dex_to_dex_decompiler.cc b/runtime/dex_to_dex_decompiler.cc index a5ebaded5f37a109384b69f7a8edfca2d71587e3..a4e4fb50c59bf7b35d5ad4158f397322ec586e2f 100644 --- a/runtime/dex_to_dex_decompiler.cc +++ b/runtime/dex_to_dex_decompiler.cc @@ -16,7 +16,9 @@ #include "dex_to_dex_decompiler.h" -#include "base/logging.h" +#include + +#include "base/macros.h" #include "base/mutex.h" #include "bytecode_utils.h" #include "dex_file-inl.h" diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc index 3c8243a6c5d5fe77e33e5b778c04ed90625cc71f..d93d76793f7bc78c8eb037aabc529428bed5ea4e 100644 --- a/runtime/dexopt_test.cc +++ b/runtime/dexopt_test.cc @@ -218,10 +218,11 @@ void DexoptTest::ReserveImageSpace() { std::unique_ptr map(BacktraceMap::Create(getpid(), true)); ASSERT_TRUE(map.get() != nullptr) << "Failed to build process map"; - for (BacktraceMap::const_iterator it = map->begin(); + for (BacktraceMap::iterator it = map->begin(); reservation_start < reservation_end && it != map->end(); ++it) { - ReserveImageSpaceChunk(reservation_start, std::min(it->start, reservation_end)); - reservation_start = std::max(reservation_start, it->end); + const backtrace_map_t* entry = *it; + ReserveImageSpaceChunk(reservation_start, std::min(entry->start, reservation_end)); + reservation_start = std::max(reservation_start, entry->end); } ReserveImageSpaceChunk(reservation_start, reservation_end); } diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc index afe4eeb05923c5f90a8f822ffa77435b17d6b6bc..d057ff3b1a7b25860ba28478ffd6e1208addeced 100644 --- a/runtime/elf_file.cc +++ b/runtime/elf_file.cc @@ -25,7 +25,6 @@ #include "android-base/strings.h" #include "arch/instruction_set.h" -#include "base/logging.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "elf_file_impl.h" diff --git a/runtime/elf_utils.h b/runtime/elf_utils.h index 418d937b120fffa0a11543b56d222baa8029449d..0cac8e8d0263ca078513bcf0c1bc542bb2f59bbb 100644 --- a/runtime/elf_utils.h +++ b/runtime/elf_utils.h @@ -19,11 +19,11 @@ #include +#include + // Explicitly include our own elf.h to avoid Linux and other dependencies. #include "./elf.h" -#include "base/logging.h" - namespace art { // Architecture dependent flags for the ELF header. diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index 82537394279b2695d599a45e9ae9ddc2840082a2..9e5085067c4f6dab9745abad1433a576562f99a4 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -245,7 +245,7 @@ inline mirror::Class* CheckArrayAlloc(dex::TypeIndex type_idx, *slow_path = true; return nullptr; // Failure } - mirror::Class* klass = method->GetDexCache()->GetResolvedType(type_idx); + ObjPtr klass = method->GetDexCache()->GetResolvedType(type_idx); if (UNLIKELY(klass == nullptr)) { // Not in dex cache so try to resolve ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); klass = class_linker->ResolveType(type_idx, method); @@ -264,7 +264,7 @@ inline mirror::Class* CheckArrayAlloc(dex::TypeIndex type_idx, return nullptr; // Failure } } - return klass; + return klass.Ptr(); } // Given the context of a calling Method, use its DexCache to resolve a type to an array Class. If @@ -349,8 +349,7 @@ inline ArtField* FindFieldFromCode(uint32_t field_idx, Handle h_dex_cache(hs.NewHandle(method->GetDexCache())); Handle h_class_loader(hs.NewHandle(method->GetClassLoader())); - resolved_field = class_linker->ResolveFieldJLS(*method->GetDexFile(), - field_idx, + resolved_field = class_linker->ResolveFieldJLS(field_idx, h_dex_cache, h_class_loader); } else { @@ -500,7 +499,8 @@ inline ArtMethod* FindMethodFromCode(uint32_t method_idx, Handle h_referring_class(hs2.NewHandle(referrer->GetDeclaringClass())); const dex::TypeIndex method_type_idx = referrer->GetDexFile()->GetMethodId(method_idx).class_idx_; - mirror::Class* method_reference_class = class_linker->ResolveType(method_type_idx, referrer); + ObjPtr method_reference_class = + class_linker->ResolveType(method_type_idx, referrer); if (UNLIKELY(method_reference_class == nullptr)) { // Bad type idx. CHECK(self->IsExceptionPending()); @@ -682,7 +682,7 @@ inline ArtMethod* FindMethodFast(uint32_t method_idx, } else if (type == kSuper) { // TODO This lookup is rather slow. dex::TypeIndex method_type_idx = dex_cache->GetDexFile()->GetMethodId(method_idx).class_idx_; - ObjPtr method_reference_class = ClassLinker::LookupResolvedType( + ObjPtr method_reference_class = linker->LookupResolvedType( method_type_idx, dex_cache, referrer->GetClassLoader()); if (method_reference_class == nullptr) { // Need to do full type resolution... @@ -711,13 +711,13 @@ inline ArtMethod* FindMethodFast(uint32_t method_idx, } } -inline mirror::Class* ResolveVerifyAndClinit(dex::TypeIndex type_idx, - ArtMethod* referrer, - Thread* self, - bool can_run_clinit, - bool verify_access) { +inline ObjPtr ResolveVerifyAndClinit(dex::TypeIndex type_idx, + ArtMethod* referrer, + Thread* self, + bool can_run_clinit, + bool verify_access) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - mirror::Class* klass = class_linker->ResolveType(type_idx, referrer); + ObjPtr klass = class_linker->ResolveType(type_idx, referrer); if (UNLIKELY(klass == nullptr)) { CHECK(self->IsExceptionPending()); return nullptr; // Failure - Indicate to caller to deliver exception @@ -748,32 +748,31 @@ inline mirror::Class* ResolveVerifyAndClinit(dex::TypeIndex type_idx, return h_class.Get(); } -static inline mirror::String* ResolveString(ClassLinker* class_linker, - dex::StringIndex string_idx, - ArtMethod* referrer) +static inline ObjPtr ResolveString(ClassLinker* class_linker, + dex::StringIndex string_idx, + ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_) { Thread::PoisonObjectPointersIfDebug(); ObjPtr string = referrer->GetDexCache()->GetResolvedString(string_idx); if (UNLIKELY(string == nullptr)) { StackHandleScope<1> hs(Thread::Current()); Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); - const DexFile& dex_file = *dex_cache->GetDexFile(); - string = class_linker->ResolveString(dex_file, string_idx, dex_cache); + string = class_linker->ResolveString(string_idx, dex_cache); } - return string.Ptr(); + return string; } -inline mirror::String* ResolveStringFromCode(ArtMethod* referrer, dex::StringIndex string_idx) { +inline ObjPtr ResolveStringFromCode(ArtMethod* referrer, + dex::StringIndex string_idx) { Thread::PoisonObjectPointersIfDebug(); ObjPtr string = referrer->GetDexCache()->GetResolvedString(string_idx); if (UNLIKELY(string == nullptr)) { StackHandleScope<1> hs(Thread::Current()); Handle dex_cache(hs.NewHandle(referrer->GetDexCache())); - const DexFile& dex_file = *dex_cache->GetDexFile(); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - string = class_linker->ResolveString(dex_file, string_idx, dex_cache); + string = class_linker->ResolveString(string_idx, dex_cache); } - return string.Ptr(); + return string; } inline void UnlockJniSynchronizedMethod(jobject locked, Thread* self) { diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index 2bf4372b1f5821026fb1061eec6bf7a81ea3101e..f3450da3066a5c1156cf6be01e82b42d734f6201 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -245,7 +245,7 @@ ArtMethod* GetCalleeSaveMethodCaller(ArtMethod** sp, CalleeSaveType type, bool d CallerAndOuterMethod GetCalleeSaveMethodCallerAndOuterMethod(Thread* self, CalleeSaveType type) { CallerAndOuterMethod result; ScopedAssertNoThreadSuspension ants(__FUNCTION__); - ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame(); + ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrameKnownNotTagged(); auto outer_caller_and_pc = DoGetCalleeSaveMethodOuterCallerAndPc(sp, type); result.outer_method = outer_caller_and_pc.first; uintptr_t caller_pc = outer_caller_and_pc.second; @@ -256,7 +256,7 @@ CallerAndOuterMethod GetCalleeSaveMethodCallerAndOuterMethod(Thread* self, Calle ArtMethod* GetCalleeSaveOuterMethod(Thread* self, CalleeSaveType type) { ScopedAssertNoThreadSuspension ants(__FUNCTION__); - ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame(); + ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrameKnownNotTagged(); return DoGetCalleeSaveMethodOuterCallerAndPc(sp, type).first; } diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index cda70ea2657592c1947698180a8c1b994b07cf75..830ef84250f938e5c54a3c167c656416b70172e0 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -143,15 +143,16 @@ inline ArtMethod* FindMethodFast(uint32_t method_idx, ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_); -inline mirror::Class* ResolveVerifyAndClinit(dex::TypeIndex type_idx, - ArtMethod* referrer, - Thread* self, - bool can_run_clinit, - bool verify_access) +inline ObjPtr ResolveVerifyAndClinit(dex::TypeIndex type_idx, + ArtMethod* referrer, + Thread* self, + bool can_run_clinit, + bool verify_access) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); -inline mirror::String* ResolveStringFromCode(ArtMethod* referrer, dex::StringIndex string_idx) +inline ObjPtr ResolveStringFromCode(ArtMethod* referrer, + dex::StringIndex string_idx) REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); diff --git a/runtime/entrypoints/jni/jni_entrypoints.cc b/runtime/entrypoints/jni/jni_entrypoints.cc index dd0819ed8ff1b8c21d7dc62e13ec643dc529c99c..780e221129c8901cdc724a5b8cd3b62edc375e6f 100644 --- a/runtime/entrypoints/jni/jni_entrypoints.cc +++ b/runtime/entrypoints/jni/jni_entrypoints.cc @@ -14,8 +14,9 @@ * limitations under the License. */ +#include + #include "art_method-inl.h" -#include "base/logging.h" #include "entrypoints/entrypoint_utils.h" #include "java_vm_ext.h" #include "mirror/object-inl.h" @@ -46,7 +47,7 @@ extern "C" const void* artFindNativeMethod(Thread* self) { return nullptr; } // Register so that future calls don't come here - return method->RegisterNative(native_code, false); + return method->RegisterNative(native_code); } } // namespace art diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h index 8acaa90053f04f2e8a70ac8e7975218605a1eaad..8c908004632d0351dce3093f6c5874ae58669c9b 100644 --- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h +++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h @@ -17,7 +17,7 @@ #ifndef ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_DEFAULT_INIT_ENTRYPOINTS_H_ #define ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_DEFAULT_INIT_ENTRYPOINTS_H_ -#include "base/logging.h" +#include "base/logging.h" // FOR VLOG_IS_ON. #include "entrypoints/jni/jni_entrypoints.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "quick_alloc_entrypoints.h" diff --git a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc index 5f40711753ba4f5dc02c3472c036ca3c3b28883b..c782c9c9492182a7430681400e4d3700b3de2201 100644 --- a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "base/logging.h" +#include "base/logging.h" // For VLOG_IS_ON. #include "base/mutex.h" #include "base/systrace.h" #include "callee_save_frame.h" diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc index 5355267b070e5d0f49fac7e942b75866d3c26895..98378382c582948eb9e5b05161942650fb24450b 100644 --- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc @@ -32,33 +32,103 @@ namespace art { -static inline void BssWriteBarrier(ArtMethod* outer_method) REQUIRES_SHARED(Locks::mutator_lock_) { - // For AOT code, we need a write barrier for the class loader that holds the - // GC roots in the .bss. - const DexFile* dex_file = outer_method->GetDexFile(); - if (dex_file != nullptr && - dex_file->GetOatDexFile() != nullptr && - !dex_file->GetOatDexFile()->GetOatFile()->GetBssGcRoots().empty()) { +static void StoreObjectInBss(ArtMethod* outer_method, + const OatFile* oat_file, + size_t bss_offset, + ObjPtr object) REQUIRES_SHARED(Locks::mutator_lock_) { + // Used for storing Class or String in .bss GC roots. + static_assert(sizeof(GcRoot) == sizeof(GcRoot), "Size check."); + static_assert(sizeof(GcRoot) == sizeof(GcRoot), "Size check."); + DCHECK_NE(bss_offset, IndexBssMappingLookup::npos); + DCHECK_ALIGNED(bss_offset, sizeof(GcRoot)); + if (UNLIKELY(!oat_file->IsExecutable())) { + // There are situations where we execute bytecode tied to an oat file opened + // as non-executable (i.e. the AOT-compiled code cannot be executed) and we + // can JIT that bytecode and get here without the .bss being mmapped. + return; + } + GcRoot* slot = reinterpret_cast*>( + const_cast(oat_file->BssBegin() + bss_offset)); + DCHECK_GE(slot, oat_file->GetBssGcRoots().data()); + DCHECK_LT(slot, oat_file->GetBssGcRoots().data() + oat_file->GetBssGcRoots().size()); + if (slot->IsNull()) { + // This may race with another thread trying to store the very same value but that's OK. + *slot = GcRoot(object); + // We need a write barrier for the class loader that holds the GC roots in the .bss. ObjPtr class_loader = outer_method->GetClassLoader(); + Runtime* runtime = Runtime::Current(); if (kIsDebugBuild) { - ClassTable* class_table = - Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(class_loader); - CHECK(class_table != nullptr && - !class_table->InsertOatFile(dex_file->GetOatDexFile()->GetOatFile())) + ClassTable* class_table = runtime->GetClassLinker()->ClassTableForClassLoader(class_loader); + CHECK(class_table != nullptr && !class_table->InsertOatFile(oat_file)) << "Oat file with .bss GC roots was not registered in class table: " - << dex_file->GetOatDexFile()->GetOatFile()->GetLocation(); + << oat_file->GetLocation(); } if (class_loader != nullptr) { - // Note that we emit the barrier before the compiled code stores the String or Class - // as a GC root. This is OK as there is no suspend point point in between. - Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader); + runtime->GetHeap()->WriteBarrierEveryFieldOf(class_loader); } else { - Runtime::Current()->GetClassLinker()->WriteBarrierForBootOatFileBssRoots( - dex_file->GetOatDexFile()->GetOatFile()); + runtime->GetClassLinker()->WriteBarrierForBootOatFileBssRoots(oat_file); } + } else { + // Each slot serves to store exactly one Class or String. + DCHECK_EQ(object, slot->Read()); } } +static inline void StoreTypeInBss(ArtMethod* outer_method, + dex::TypeIndex type_idx, + ObjPtr resolved_type) + REQUIRES_SHARED(Locks::mutator_lock_) { + const DexFile* dex_file = outer_method->GetDexFile(); + DCHECK(dex_file != nullptr); + const OatDexFile* oat_dex_file = dex_file->GetOatDexFile(); + if (oat_dex_file != nullptr) { + size_t bss_offset = IndexBssMappingLookup::GetBssOffset(oat_dex_file->GetTypeBssMapping(), + type_idx.index_, + dex_file->NumTypeIds(), + sizeof(GcRoot)); + if (bss_offset != IndexBssMappingLookup::npos) { + StoreObjectInBss(outer_method, oat_dex_file->GetOatFile(), bss_offset, resolved_type); + } + } +} + +static inline void StoreStringInBss(ArtMethod* outer_method, + dex::StringIndex string_idx, + ObjPtr resolved_string) + REQUIRES_SHARED(Locks::mutator_lock_) __attribute__((optnone)) { + const DexFile* dex_file = outer_method->GetDexFile(); + DCHECK(dex_file != nullptr); + const OatDexFile* oat_dex_file = dex_file->GetOatDexFile(); + if (oat_dex_file != nullptr) { + size_t bss_offset = IndexBssMappingLookup::GetBssOffset(oat_dex_file->GetStringBssMapping(), + string_idx.index_, + dex_file->NumStringIds(), + sizeof(GcRoot)); + if (bss_offset != IndexBssMappingLookup::npos) { + StoreObjectInBss(outer_method, oat_dex_file->GetOatFile(), bss_offset, resolved_string); + } + } +} + +static ALWAYS_INLINE bool CanReferenceBss(ArtMethod* outer_method, ArtMethod* caller) + REQUIRES_SHARED(Locks::mutator_lock_) { + // .bss references are used only for AOT-compiled code and only when the instruction + // originates from the outer method's dex file and the type or string index is tied to + // that dex file. As we do not want to check if the call is coming from AOT-compiled + // code (that could be expensive), simply check if the caller has the same dex file. + // + // If we've accepted running AOT-compiled code despite the runtime class loader + // resolving the caller to a different dex file, this check shall prevent us from + // filling the .bss slot and we shall keep going through the slow path. This is slow + // but correct; we do not really care that much about performance in this odd case. + // + // JIT can inline throwing instructions across dex files and this check prevents + // looking up the index in the wrong dex file in that case. If the caller and outer + // method have the same dex file, we may or may not find a .bss slot to update; + // if we do, this can still benefit AOT-compiled code executed later. + return outer_method->GetDexFile() == caller->GetDexFile(); +} + extern "C" mirror::Class* artInitializeStaticStorageFromCode(uint32_t type_idx, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { // Called to ensure static storage base is initialized for direct static field reads and writes. @@ -68,43 +138,49 @@ extern "C" mirror::Class* artInitializeStaticStorageFromCode(uint32_t type_idx, auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod( self, CalleeSaveType::kSaveEverythingForClinit); ArtMethod* caller = caller_and_outer.caller; - mirror::Class* result = - ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, true, false); - if (LIKELY(result != nullptr)) { - BssWriteBarrier(caller_and_outer.outer_method); + ObjPtr result = ResolveVerifyAndClinit(dex::TypeIndex(type_idx), + caller, + self, + /* can_run_clinit */ true, + /* verify_access */ false); + if (LIKELY(result != nullptr) && CanReferenceBss(caller_and_outer.outer_method, caller)) { + StoreTypeInBss(caller_and_outer.outer_method, dex::TypeIndex(type_idx), result); } - return result; + return result.Ptr(); } extern "C" mirror::Class* artInitializeTypeFromCode(uint32_t type_idx, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - // Called when method->dex_cache_resolved_types_[] misses. + // Called when the .bss slot was empty or for main-path runtime call. ScopedQuickEntrypointChecks sqec(self); auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod( self, CalleeSaveType::kSaveEverythingForClinit); ArtMethod* caller = caller_and_outer.caller; - mirror::Class* result = - ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, false, false); - if (LIKELY(result != nullptr)) { - BssWriteBarrier(caller_and_outer.outer_method); + ObjPtr result = ResolveVerifyAndClinit(dex::TypeIndex(type_idx), + caller, + self, + /* can_run_clinit */ false, + /* verify_access */ false); + if (LIKELY(result != nullptr) && CanReferenceBss(caller_and_outer.outer_method, caller)) { + StoreTypeInBss(caller_and_outer.outer_method, dex::TypeIndex(type_idx), result); } - return result; + return result.Ptr(); } extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type_idx, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - // Called when caller isn't guaranteed to have access to a type and the dex cache may be - // unpopulated. + // Called when caller isn't guaranteed to have access to a type. ScopedQuickEntrypointChecks sqec(self); auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, CalleeSaveType::kSaveEverything); ArtMethod* caller = caller_and_outer.caller; - mirror::Class* result = - ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, false, true); - if (LIKELY(result != nullptr)) { - BssWriteBarrier(caller_and_outer.outer_method); - } - return result; + ObjPtr result = ResolveVerifyAndClinit(dex::TypeIndex(type_idx), + caller, + self, + /* can_run_clinit */ false, + /* verify_access */ true); + // Do not StoreTypeInBss(); access check entrypoint is never used together with .bss. + return result.Ptr(); } extern "C" mirror::String* artResolveStringFromCode(int32_t string_idx, Thread* self) @@ -113,11 +189,11 @@ extern "C" mirror::String* artResolveStringFromCode(int32_t string_idx, Thread* auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, CalleeSaveType::kSaveEverything); ArtMethod* caller = caller_and_outer.caller; - mirror::String* result = ResolveStringFromCode(caller, dex::StringIndex(string_idx)); - if (LIKELY(result != nullptr)) { - BssWriteBarrier(caller_and_outer.outer_method); + ObjPtr result = ResolveStringFromCode(caller, dex::StringIndex(string_idx)); + if (LIKELY(result != nullptr) && CanReferenceBss(caller_and_outer.outer_method, caller)) { + StoreStringInBss(caller_and_outer.outer_method, dex::StringIndex(string_idx), result); } - return result; + return result.Ptr(); } } // namespace art diff --git a/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc b/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc index d4bc1c76b18ab61f636aa91558980a8756dbe3a4..d22f180c7a81341ea5987cad341538af21524e7a 100644 --- a/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc @@ -28,7 +28,7 @@ extern "C" int artHandleFillArrayDataFromCode(uint32_t payload_offset, mirror::A ArtMethod* method, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { ScopedQuickEntrypointChecks sqec(self); - const uint16_t* const insns = method->GetCodeItem()->insns_; + const uint16_t* const insns = method->DexInstructions().Insns(); const Instruction::ArrayDataPayload* payload = reinterpret_cast(insns + payload_offset); bool success = FillArrayData(array, payload); diff --git a/runtime/entrypoints/quick/quick_jni_entrypoints.cc b/runtime/entrypoints/quick/quick_jni_entrypoints.cc index a8d2a34853eb6b0cd1249e105ee7dee3bdd7d114..3c41a8c3b57d586a2e44d50a83c0706beda8c08c 100644 --- a/runtime/entrypoints/quick/quick_jni_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_jni_entrypoints.cc @@ -14,9 +14,10 @@ * limitations under the License. */ +#include + #include "art_method-inl.h" #include "base/casts.h" -#include "base/logging.h" #include "entrypoints/entrypoint_utils-inl.h" #include "indirect_reference_table.h" #include "mirror/object-inl.h" @@ -28,10 +29,7 @@ namespace art { static_assert(sizeof(IRTSegmentState) == sizeof(uint32_t), "IRTSegmentState size unexpected"); static_assert(std::is_trivial::value, "IRTSegmentState not trivial"); -static bool kEnableAnnotationChecks = RegisterRuntimeDebugFlag(&kEnableAnnotationChecks); - -template -static inline void GoToRunnableFast(Thread* self) NO_THREAD_SAFETY_ANALYSIS; +static inline void GoToRunnableFast(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_); extern void ReadBarrierJni(mirror::CompressedReference* handle_on_stack, Thread* self ATTRIBUTE_UNUSED) { @@ -53,12 +51,12 @@ extern void ReadBarrierJni(mirror::CompressedReference* handle_o extern uint32_t JniMethodFastStart(Thread* self) { JNIEnvExt* env = self->GetJniEnv(); DCHECK(env != nullptr); - uint32_t saved_local_ref_cookie = bit_cast(env->local_ref_cookie); - env->local_ref_cookie = env->locals.GetSegmentState(); + uint32_t saved_local_ref_cookie = bit_cast(env->GetLocalRefCookie()); + env->SetLocalRefCookie(env->GetLocalsSegmentState()); - if (kIsDebugBuild && kEnableAnnotationChecks) { + if (kIsDebugBuild) { ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame(); - CHECK(native_method->IsAnnotatedWithFastNative()) << native_method->PrettyMethod(); + CHECK(native_method->IsFastNative()) << native_method->PrettyMethod(); } return saved_local_ref_cookie; @@ -68,9 +66,12 @@ extern uint32_t JniMethodFastStart(Thread* self) { extern uint32_t JniMethodStart(Thread* self) { JNIEnvExt* env = self->GetJniEnv(); DCHECK(env != nullptr); - uint32_t saved_local_ref_cookie = bit_cast(env->local_ref_cookie); - env->local_ref_cookie = env->locals.GetSegmentState(); + uint32_t saved_local_ref_cookie = bit_cast(env->GetLocalRefCookie()); + env->SetLocalRefCookie(env->GetLocalsSegmentState()); ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame(); + // TODO: Introduce special entrypoint for synchronized @FastNative methods? + // Or ban synchronized @FastNative outright to avoid the extra check here? + DCHECK(!native_method->IsFastNative() || native_method->IsSynchronized()); if (!native_method->IsFastNative()) { // When not fast JNI we transition out of runnable. self->TransitionFromRunnableToSuspended(kNative); @@ -90,25 +91,18 @@ static void GoToRunnable(Thread* self) NO_THREAD_SAFETY_ANALYSIS { if (!is_fast) { self->TransitionFromSuspendedToRunnable(); } else { - GoToRunnableFast(self); + GoToRunnableFast(self); } } -// TODO: NO_THREAD_SAFETY_ANALYSIS due to different control paths depending on fast JNI. -template -ALWAYS_INLINE static inline void GoToRunnableFast(Thread* self) NO_THREAD_SAFETY_ANALYSIS { - if (kIsDebugBuild && kEnableAnnotationChecks) { - // Should only enter here if the method is !Fast JNI or @FastNative. +ALWAYS_INLINE static inline void GoToRunnableFast(Thread* self) { + if (kIsDebugBuild) { + // Should only enter here if the method is @FastNative. ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame(); - - if (kDynamicFast) { - CHECK(native_method->IsFastNative()) << native_method->PrettyMethod(); - } else { - CHECK(native_method->IsAnnotatedWithFastNative()) << native_method->PrettyMethod(); - } + CHECK(native_method->IsFastNative()) << native_method->PrettyMethod(); } - // When we are in "fast" JNI or @FastNative, we are already Runnable. + // When we are in @FastNative, we are already Runnable. // Only do a suspend check on the way out of JNI. if (UNLIKELY(self->TestAllFlags())) { // In fast JNI mode we never transitioned out of runnable. Perform a suspend check if there @@ -121,11 +115,11 @@ ALWAYS_INLINE static inline void GoToRunnableFast(Thread* self) NO_THREAD_SAFETY static void PopLocalReferences(uint32_t saved_local_ref_cookie, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { JNIEnvExt* env = self->GetJniEnv(); - if (UNLIKELY(env->check_jni)) { + if (UNLIKELY(env->IsCheckJniEnabled())) { env->CheckNoHeldMonitors(); } - env->locals.SetSegmentState(env->local_ref_cookie); - env->local_ref_cookie = bit_cast(saved_local_ref_cookie); + env->SetLocalSegmentState(env->GetLocalRefCookie()); + env->SetLocalRefCookie(bit_cast(saved_local_ref_cookie)); self->PopHandleScope(); } @@ -138,7 +132,7 @@ extern void JniMethodEnd(uint32_t saved_local_ref_cookie, Thread* self) { } extern void JniMethodFastEnd(uint32_t saved_local_ref_cookie, Thread* self) { - GoToRunnableFast(self); + GoToRunnableFast(self); PopLocalReferences(saved_local_ref_cookie, self); } @@ -162,7 +156,7 @@ static mirror::Object* JniMethodEndWithReferenceHandleResult(jobject result, } PopLocalReferences(saved_local_ref_cookie, self); // Process result. - if (UNLIKELY(self->GetJniEnv()->check_jni)) { + if (UNLIKELY(self->GetJniEnv()->IsCheckJniEnabled())) { // CheckReferenceResult can resolve types. StackHandleScope<1> hs(self); HandleWrapperObjPtr h_obj(hs.NewHandleWrapper(&o)); @@ -175,7 +169,7 @@ static mirror::Object* JniMethodEndWithReferenceHandleResult(jobject result, extern mirror::Object* JniMethodFastEndWithReference(jobject result, uint32_t saved_local_ref_cookie, Thread* self) { - GoToRunnableFast(self); + GoToRunnableFast(self); return JniMethodEndWithReferenceHandleResult(result, saved_local_ref_cookie, self); } @@ -203,8 +197,8 @@ extern uint64_t GenericJniMethodEnd(Thread* self, HandleScope* handle_scope) // TODO: NO_THREAD_SAFETY_ANALYSIS as GoToRunnable() is NO_THREAD_SAFETY_ANALYSIS NO_THREAD_SAFETY_ANALYSIS { - bool critical_native = called->IsAnnotatedWithCriticalNative(); - bool fast_native = called->IsAnnotatedWithFastNative(); + bool critical_native = called->IsCriticalNative(); + bool fast_native = called->IsFastNative(); bool normal_native = !critical_native && !fast_native; // @Fast and @CriticalNative do not do a state transition. diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index a4a8c349a34bb159977befefc94c036bd279e2fa..a4d14ecf7e7b7ac005b18bb8724a2cc9aaaa788e 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -28,10 +28,11 @@ #include "gc/accounting/card_table-inl.h" #include "imt_conflict_table.h" #include "imtable-inl.h" +#include "index_bss_mapping.h" #include "instrumentation.h" #include "interpreter/interpreter.h" +#include "jit/jit.h" #include "linear_alloc.h" -#include "method_bss_mapping.h" #include "method_handles.h" #include "method_reference.h" #include "mirror/class-inl.h" @@ -783,8 +784,8 @@ extern "C" uint64_t artQuickToInterpreterBridge(ArtMethod* method, Thread* self, DCHECK(!method->IsNative()) << method->PrettyMethod(); uint32_t shorty_len = 0; ArtMethod* non_proxy_method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize); - const DexFile::CodeItem* code_item = non_proxy_method->GetCodeItem(); - DCHECK(code_item != nullptr) << method->PrettyMethod(); + DCHECK(non_proxy_method->GetCodeItem() != nullptr) << method->PrettyMethod(); + CodeItemDataAccessor accessor(non_proxy_method); const char* shorty = non_proxy_method->GetShorty(&shorty_len); JValue result; @@ -794,12 +795,12 @@ extern "C" uint64_t artQuickToInterpreterBridge(ArtMethod* method, Thread* self, } else { const char* old_cause = self->StartAssertNoThreadSuspension( "Building interpreter shadow frame"); - uint16_t num_regs = code_item->registers_size_; + uint16_t num_regs = accessor.RegistersSize(); // No last shadow coming from quick. ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr = CREATE_SHADOW_FRAME(num_regs, /* link */ nullptr, method, /* dex pc */ 0); ShadowFrame* shadow_frame = shadow_frame_unique_ptr.get(); - size_t first_arg_reg = code_item->registers_size_ - code_item->ins_size_; + size_t first_arg_reg = accessor.RegistersSize() - accessor.InsSize(); BuildQuickShadowFrameVisitor shadow_frame_builder(sp, method->IsStatic(), shorty, shorty_len, shadow_frame, first_arg_reg); shadow_frame_builder.VisitArguments(); @@ -822,7 +823,7 @@ extern "C" uint64_t artQuickToInterpreterBridge(ArtMethod* method, Thread* self, } } - result = interpreter::EnterInterpreterFromEntryPoint(self, code_item, shadow_frame); + result = interpreter::EnterInterpreterFromEntryPoint(self, accessor, shadow_frame); } // Pop transition. @@ -1120,10 +1121,9 @@ extern "C" const void* artQuickResolutionTrampoline( // code. if (!found_stack_map || kIsDebugBuild) { uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp); - const DexFile::CodeItem* code; - code = caller->GetCodeItem(); - CHECK_LT(dex_pc, code->insns_size_in_code_units_); - const Instruction& instr = code->InstructionAt(dex_pc); + CodeItemInstructionAccessor accessor(caller); + CHECK_LT(dex_pc, accessor.InsnsSizeInCodeUnits()); + const Instruction& instr = accessor.InstructionAt(dex_pc); Instruction::Code instr_code = instr.Opcode(); bool is_range; switch (instr_code) { @@ -1214,27 +1214,20 @@ extern "C" const void* artQuickResolutionTrampoline( // Update .bss entry in oat file if any. if (called != nullptr && called_method.dex_file->GetOatDexFile() != nullptr) { - const MethodBssMapping* mapping = - called_method.dex_file->GetOatDexFile()->GetMethodBssMapping(); - if (mapping != nullptr) { - auto pp = std::partition_point( - mapping->begin(), - mapping->end(), - [called_method](const MethodBssMappingEntry& entry) { - return entry.method_index < called_method.index; - }); - if (pp != mapping->end() && pp->CoversIndex(called_method.index)) { - size_t bss_offset = pp->GetBssOffset(called_method.index, - static_cast(kRuntimePointerSize)); - DCHECK_ALIGNED(bss_offset, static_cast(kRuntimePointerSize)); - const OatFile* oat_file = called_method.dex_file->GetOatDexFile()->GetOatFile(); - ArtMethod** method_entry = reinterpret_cast(const_cast( - oat_file->BssBegin() + bss_offset)); - DCHECK_GE(method_entry, oat_file->GetBssMethods().data()); - DCHECK_LT(method_entry, - oat_file->GetBssMethods().data() + oat_file->GetBssMethods().size()); - *method_entry = called; - } + size_t bss_offset = IndexBssMappingLookup::GetBssOffset( + called_method.dex_file->GetOatDexFile()->GetMethodBssMapping(), + called_method.index, + called_method.dex_file->NumMethodIds(), + static_cast(kRuntimePointerSize)); + if (bss_offset != IndexBssMappingLookup::npos) { + DCHECK_ALIGNED(bss_offset, static_cast(kRuntimePointerSize)); + const OatFile* oat_file = called_method.dex_file->GetOatDexFile()->GetOatFile(); + ArtMethod** method_entry = reinterpret_cast(const_cast( + oat_file->BssBegin() + bss_offset)); + DCHECK_GE(method_entry, oat_file->GetBssMethods().data()); + DCHECK_LT(method_entry, + oat_file->GetBssMethods().data() + oat_file->GetBssMethods().size()); + *method_entry = called; } } } @@ -1256,17 +1249,8 @@ extern "C" const void* artQuickResolutionTrampoline( } else { DCHECK_EQ(invoke_type, kSuper); CHECK(caller != nullptr) << invoke_type; - StackHandleScope<2> hs(self); - Handle dex_cache( - hs.NewHandle(caller->GetDeclaringClass()->GetDexCache())); - Handle class_loader( - hs.NewHandle(caller->GetDeclaringClass()->GetClassLoader())); - // TODO Maybe put this into a mirror::Class function. ObjPtr ref_class = linker->LookupResolvedType( - *dex_cache->GetDexFile(), - dex_cache->GetDexFile()->GetMethodId(called_method.index).class_idx_, - dex_cache.Get(), - class_loader.Get()); + caller->GetDexFile()->GetMethodId(called_method.index).class_idx_, caller); if (ref_class->IsInterface()) { called = ref_class->FindVirtualMethodForInterfaceSuper(called, kRuntimePointerSize); } else { @@ -2171,32 +2155,19 @@ static void artQuickGenericJniEndJNINonRef(Thread* self, */ extern "C" TwoWordReturn artQuickGenericJniTrampoline(Thread* self, ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_) { + // Note: We cannot walk the stack properly until fixed up below. ArtMethod* called = *sp; DCHECK(called->IsNative()) << called->PrettyMethod(true); - // Fix up a callee-save frame at the bottom of the stack (at `*sp`, - // above the alloca region) while we check for optimization - // annotations, thus allowing stack walking until the completion of - // the JNI frame creation. - // - // Note however that the Generic JNI trampoline does not expect - // exception being thrown at that stage. - *sp = Runtime::Current()->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs); - self->SetTopOfStack(sp); + Runtime* runtime = Runtime::Current(); + jit::Jit* jit = runtime->GetJit(); + if (jit != nullptr) { + jit->AddSamples(self, called, 1u, /*with_backedges*/ false); + } uint32_t shorty_len = 0; const char* shorty = called->GetShorty(&shorty_len); - // Optimization annotations lookup does not try to resolve classes, - // as this may throw an exception, which is not supported by the - // Generic JNI trampoline at this stage; instead, method's - // annotations' classes are looked up in the bootstrap class - // loader's resolved types (which won't trigger an exception). - CHECK(!self->IsExceptionPending()); - bool critical_native = called->IsAnnotatedWithCriticalNative(); - CHECK(!self->IsExceptionPending()); - bool fast_native = called->IsAnnotatedWithFastNative(); - CHECK(!self->IsExceptionPending()); + bool critical_native = called->IsCriticalNative(); + bool fast_native = called->IsFastNative(); bool normal_native = !critical_native && !fast_native; - // Restore the initial ArtMethod pointer at `*sp`. - *sp = called; // Run the visitor and update sp. BuildGenericJniFrameVisitor visitor(self, @@ -2212,8 +2183,8 @@ extern "C" TwoWordReturn artQuickGenericJniTrampoline(Thread* self, ArtMethod** visitor.FinalizeHandleScope(self); } - // Fix up managed-stack things in Thread. - self->SetTopOfStack(sp); + // Fix up managed-stack things in Thread. After this we can walk the stack. + self->SetTopOfStackTagged(sp); self->VerifyStack(); @@ -2333,6 +2304,7 @@ extern "C" uint64_t artQuickGenericJniEndTrampoline(Thread* self, // anything that requires a mutator lock before that would cause problems as GC may have the // exclusive mutator lock and may be moving objects, etc. ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame(); + DCHECK(self->GetManagedStack()->GetTopQuickFrameTag()); uint32_t* sp32 = reinterpret_cast(sp); ArtMethod* called = *sp; uint32_t cookie = *(sp32 - 1); @@ -2477,9 +2449,7 @@ extern "C" TwoWordReturn artInvokeInterfaceTrampoline(ArtMethod* interface_metho // Fetch the dex_method_idx of the target interface method from the caller. uint32_t dex_method_idx; uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp); - const DexFile::CodeItem* code_item = caller_method->GetCodeItem(); - DCHECK_LT(dex_pc, code_item->insns_size_in_code_units_); - const Instruction& instr = code_item->InstructionAt(dex_pc); + const Instruction& instr = caller_method->DexInstructions().InstructionAt(dex_pc); Instruction::Code instr_code = instr.Opcode(); DCHECK(instr_code == Instruction::INVOKE_INTERFACE || instr_code == Instruction::INVOKE_INTERFACE_RANGE) @@ -2598,9 +2568,8 @@ extern "C" uintptr_t artInvokePolymorphic( const Instruction& inst = code->InstructionAt(dex_pc); DCHECK(inst.Opcode() == Instruction::INVOKE_POLYMORPHIC || inst.Opcode() == Instruction::INVOKE_POLYMORPHIC_RANGE); - const DexFile* dex_file = caller_method->GetDexFile(); const uint32_t proto_idx = inst.VRegH(); - const char* shorty = dex_file->GetShorty(proto_idx); + const char* shorty = caller_method->GetDexFile()->GetShorty(proto_idx); const size_t shorty_length = strlen(shorty); static const bool kMethodIsStatic = false; // invoke() and invokeExact() are not static. RememberForGcArgumentVisitor gc_visitor(sp, kMethodIsStatic, shorty, shorty_length, &soa); @@ -2667,28 +2636,24 @@ extern "C" uintptr_t artInvokePolymorphic( // Call DoInvokePolymorphic with |is_range| = true, as shadow frame has argument registers in // consecutive order. - uint32_t unused_args[Instruction::kMaxVarArgRegs] = {}; - uint32_t first_callee_arg = first_arg + 1; - + RangeInstructionOperands operands(first_arg + 1, num_vregs - 1); bool isExact = (jni::EncodeArtMethod(resolved_method) == WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact); bool success = false; if (isExact) { - success = MethodHandleInvokeExact(self, - *shadow_frame, - method_handle, - method_type, - unused_args, - first_callee_arg, - result); + success = MethodHandleInvokeExact(self, + *shadow_frame, + method_handle, + method_type, + &operands, + result); } else { - success = MethodHandleInvoke(self, - *shadow_frame, - method_handle, - method_type, - unused_args, - first_callee_arg, - result); + success = MethodHandleInvoke(self, + *shadow_frame, + method_handle, + method_type, + &operands, + result); } DCHECK(success || self->IsExceptionPending()); diff --git a/runtime/experimental_flags.h b/runtime/experimental_flags.h index b7f40372db3d5fc6bf13a910ebdc0d20c643a702..5f1443814ab446b5ade78eed71809743f9adae7d 100644 --- a/runtime/experimental_flags.h +++ b/runtime/experimental_flags.h @@ -30,10 +30,10 @@ struct ExperimentalFlags { }; constexpr ExperimentalFlags() : value_(0x0000) {} - constexpr ExperimentalFlags(decltype(kNone) t) // NOLINT, implicit + constexpr ExperimentalFlags(decltype(kNone) t) // NOLINT [runtime/explicit] : value_(static_cast(t)) {} - constexpr operator decltype(kNone)() const { // NOLINT, implicit + constexpr operator decltype(kNone)() const { return static_cast(value_); } diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc index 6a4e5b5f01f5a65ef42d239933d92377d464f364..f66836f1471834b01c035830312c036eb40342a1 100644 --- a/runtime/fault_handler.cc +++ b/runtime/fault_handler.cc @@ -21,6 +21,7 @@ #include #include "art_method-inl.h" +#include "base/logging.h" // For VLOG #include "base/safe_copy.h" #include "base/stl_util.h" #include "dex_file_types.h" diff --git a/runtime/gc/accounting/atomic_stack.h b/runtime/gc/accounting/atomic_stack.h index 3d0e8172b6e6087223e1357e2529255d0ae693b9..e5b5694413c5115cf4157dbec67254c0fe7dd569 100644 --- a/runtime/gc/accounting/atomic_stack.h +++ b/runtime/gc/accounting/atomic_stack.h @@ -23,8 +23,9 @@ #include #include +#include + #include "atomic.h" -#include "base/logging.h" #include "base/macros.h" #include "mem_map.h" #include "stack_reference.h" diff --git a/runtime/gc/accounting/bitmap-inl.h b/runtime/gc/accounting/bitmap-inl.h index cd3923abbe40fe07dd4526ed8c89bbbfcf4ba206..ca6b4794de6ce1a3257eb7f3e014a8a666db0983 100644 --- a/runtime/gc/accounting/bitmap-inl.h +++ b/runtime/gc/accounting/bitmap-inl.h @@ -21,9 +21,10 @@ #include +#include + #include "atomic.h" #include "base/bit_utils.h" -#include "base/logging.h" namespace art { namespace gc { diff --git a/runtime/gc/accounting/card_table-inl.h b/runtime/gc/accounting/card_table-inl.h index 6ff53597e46d4fea32ac6ed44d7a81fb941d625b..5f2f2dda4222587d202710e279b3f1fd53b489c0 100644 --- a/runtime/gc/accounting/card_table-inl.h +++ b/runtime/gc/accounting/card_table-inl.h @@ -17,10 +17,12 @@ #ifndef ART_RUNTIME_GC_ACCOUNTING_CARD_TABLE_INL_H_ #define ART_RUNTIME_GC_ACCOUNTING_CARD_TABLE_INL_H_ +#include "card_table.h" + +#include + #include "atomic.h" #include "base/bit_utils.h" -#include "base/logging.h" -#include "card_table.h" #include "mem_map.h" #include "space_bitmap.h" diff --git a/runtime/gc/accounting/card_table.cc b/runtime/gc/accounting/card_table.cc index 01b5896650511133d7244598686fcc5ff7aca14b..934e57a61b35fc440a7d01f1ebcc547ab09d8443 100644 --- a/runtime/gc/accounting/card_table.cc +++ b/runtime/gc/accounting/card_table.cc @@ -18,7 +18,6 @@ #include -#include "base/logging.h" #include "base/systrace.h" #include "card_table-inl.h" #include "gc/heap.h" diff --git a/runtime/gc/accounting/heap_bitmap.h b/runtime/gc/accounting/heap_bitmap.h index 4237e7ee3ffc20fd747b66d65309eb87f2174008..c997f8dbfc679d5981f90cbe15253f817d86836c 100644 --- a/runtime/gc/accounting/heap_bitmap.h +++ b/runtime/gc/accounting/heap_bitmap.h @@ -17,8 +17,11 @@ #ifndef ART_RUNTIME_GC_ACCOUNTING_HEAP_BITMAP_H_ #define ART_RUNTIME_GC_ACCOUNTING_HEAP_BITMAP_H_ +#include + #include "base/allocator.h" -#include "base/logging.h" +#include "base/macros.h" +#include "base/mutex.h" #include "space_bitmap.h" namespace art { diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc index 1b3d0dadaecacf4d13ff71db80a66725d0419a75..0dd05cd6f0b5434ab80af6f0ed607ff54bfded66 100644 --- a/runtime/gc/accounting/mod_union_table.cc +++ b/runtime/gc/accounting/mod_union_table.cc @@ -18,6 +18,7 @@ #include +#include "base/logging.h" // For VLOG #include "base/stl_util.h" #include "bitmap-inl.h" #include "card_table-inl.h" diff --git a/runtime/gc/accounting/space_bitmap-inl.h b/runtime/gc/accounting/space_bitmap-inl.h index b37dd965fc19eb91f498ea86afc37ff82e362429..ba833693f619c958e0b38fbf457be3b5079df6fc 100644 --- a/runtime/gc/accounting/space_bitmap-inl.h +++ b/runtime/gc/accounting/space_bitmap-inl.h @@ -21,9 +21,10 @@ #include +#include + #include "atomic.h" #include "base/bit_utils.h" -#include "base/logging.h" namespace art { namespace gc { diff --git a/runtime/gc/allocation_record.cc b/runtime/gc/allocation_record.cc index 2257b81e098b1994693bdaf5d8288c73eea9d90e..2ee4239e8a49133bc0fe49a5685fbf0777a468e7 100644 --- a/runtime/gc/allocation_record.cc +++ b/runtime/gc/allocation_record.cc @@ -18,6 +18,7 @@ #include "art_method-inl.h" #include "base/enums.h" +#include "base/logging.h" // For VLOG #include "base/stl_util.h" #include "obj_ptr-inl.h" #include "object_callbacks.h" diff --git a/runtime/gc/allocator/dlmalloc.cc b/runtime/gc/allocator/dlmalloc.cc index ef916f87454ef0368c088eba30948bbc150d5d46..65062208d687673adef23d0eada27dcc5c34b9a2 100644 --- a/runtime/gc/allocator/dlmalloc.cc +++ b/runtime/gc/allocator/dlmalloc.cc @@ -16,8 +16,9 @@ #include "dlmalloc.h" +#include + #include "base/bit_utils.h" -#include "base/logging.h" // ART specific morecore implementation defined in space.cc. static void* art_heap_morecore(void* m, intptr_t increment); diff --git a/runtime/gc/allocator/dlmalloc.h b/runtime/gc/allocator/dlmalloc.h index c07da5da4dbf41aac4409091ba664762b6c720a1..29b96ee96c4b84b4ba8ace42013f52f477ee57cd 100644 --- a/runtime/gc/allocator/dlmalloc.h +++ b/runtime/gc/allocator/dlmalloc.h @@ -35,13 +35,6 @@ #include "../../external/dlmalloc/malloc.h" #pragma GCC diagnostic pop -#ifdef ART_TARGET_ANDROID -// Define dlmalloc routines from bionic that cannot be included directly because of redefining -// symbols from the include above. -extern "C" void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*), void* arg); -extern "C" int dlmalloc_trim(size_t); -#endif - // Callback for dlmalloc_inspect_all or mspace_inspect_all that will madvise(2) unused // pages back to the kernel. extern "C" void DlmallocMadviseCallback(void* start, void* end, size_t used_bytes, void* /*arg*/); diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc index b742ac4a7cab3c8b362f0bea1cff06bb99f2dd30..928abe873ebc20bf608316373386a7d6600dd684 100644 --- a/runtime/gc/allocator/rosalloc.cc +++ b/runtime/gc/allocator/rosalloc.cc @@ -23,6 +23,7 @@ #include "android-base/stringprintf.h" +#include "base/logging.h" // For VLOG #include "base/memory_tool.h" #include "base/mutex-inl.h" #include "gc/space/memory_tool_settings.h" diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h index 2c90773b8f8c21c9cf0a5f0efb742da4d4efcd29..6e5cf0ede8c3c82cb37516c67605ce7b8a70ed13 100644 --- a/runtime/gc/allocator/rosalloc.h +++ b/runtime/gc/allocator/rosalloc.h @@ -26,9 +26,10 @@ #include #include +#include + #include "base/allocator.h" #include "base/bit_utils.h" -#include "base/logging.h" #include "base/mutex.h" #include "globals.h" #include "thread.h" diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index 7beff960cc3b8499d4f56f93d64410f3bcb669d1..70685bcbf78f1dae901c12ee1de6af7c217aeb57 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -760,7 +760,8 @@ class ConcurrentCopying::ImmuneSpaceScanObjVisitor { // Done scanning the object, go back to white. bool success = obj->AtomicSetReadBarrierState(ReadBarrier::GrayState(), ReadBarrier::WhiteState()); - CHECK(success); + CHECK(success) + << Runtime::Current()->GetHeap()->GetVerification()->DumpObjectInfo(obj, "failed CAS"); } } else { collector_->ScanImmuneObject(obj); diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc index c5a341fc80faf63ca2a2a95faceea4c9b6cdb2bd..fa34270d957b1586b6ca06fe54514492351b947d 100644 --- a/runtime/gc/collector/garbage_collector.cc +++ b/runtime/gc/collector/garbage_collector.cc @@ -22,7 +22,7 @@ #include "base/dumpable.h" #include "base/histogram-inl.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG_IS_ON. #include "base/mutex-inl.h" #include "base/systrace.h" #include "base/time_utils.h" diff --git a/runtime/gc/collector/immune_spaces.cc b/runtime/gc/collector/immune_spaces.cc index 102405040947a00e5fbe11e2e3f84e07b06c81b0..3b5961899f32c55123c68460247056fba0b5fdb5 100644 --- a/runtime/gc/collector/immune_spaces.cc +++ b/runtime/gc/collector/immune_spaces.cc @@ -19,6 +19,7 @@ #include #include +#include "base/logging.h" // For VLOG. #include "gc/space/space-inl.h" #include "mirror/object.h" #include "oat_file.h" diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc index aef98dee58e448a11354242f36af9a19c0904eb5..34cc129ce8c0c9dff9d30d385c16fc7f24830455 100644 --- a/runtime/gc/collector/mark_compact.cc +++ b/runtime/gc/collector/mark_compact.cc @@ -16,7 +16,9 @@ #include "mark_compact.h" -#include "base/logging.h" +#include + +#include "base/macros.h" #include "base/mutex-inl.h" #include "base/timing_logger.h" #include "gc/accounting/heap_bitmap-inl.h" diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index c6caf4b08eb97ed8810d93bec0e884820d56ab3a..fdfe949265534dd832a9760b1840e2ae2d72216e 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -25,7 +25,7 @@ #include "base/bounded_fifo.h" #include "base/enums.h" #include "base/file_utils.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/macros.h" #include "base/mutex-inl.h" #include "base/systrace.h" diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index 9fb37b61382f2b26aadf7d44604d3d3163aca6b3..3150781a5aa4899dadecf394e7bdd077f59a21aa 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -22,7 +22,7 @@ #include #include -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/macros.h" #include "base/mutex-inl.h" #include "base/timing_logger.h" diff --git a/runtime/gc/gc_cause.cc b/runtime/gc/gc_cause.cc index 871208037a874eeb594235694b896e4354e5e1be..d88fcdcc95706f90c6587e8aafcd839cb6f39467 100644 --- a/runtime/gc/gc_cause.cc +++ b/runtime/gc/gc_cause.cc @@ -15,7 +15,10 @@ */ #include "gc_cause.h" -#include "base/logging.h" + +#include + +#include "base/macros.h" #include "globals.h" #include diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h index 2047646413fcfdf1af47cdc9ffb260bd8ad627dc..52dd104ac80695a9fefe03107070c6b80410e9ce 100644 --- a/runtime/gc/heap-inl.h +++ b/runtime/gc/heap-inl.h @@ -401,8 +401,7 @@ inline bool Heap::IsOutOfMemoryOnAllocation(AllocatorType allocator_type, return true; } // TODO: Grow for allocation is racy, fix it. - VLOG(heap) << "Growing heap from " << PrettySize(max_allowed_footprint_) << " to " - << PrettySize(new_footprint) << " for a " << PrettySize(alloc_size) << " allocation"; + VlogHeapGrowth(max_allowed_footprint_, new_footprint, alloc_size); max_allowed_footprint_ = new_footprint; } } diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 9f6266612a4ae66a2eb834fd192491a87248d8ca..dbaddaf8d4b0e251695f55281e218754ef8acad0 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -30,6 +30,7 @@ #include "base/dumpable.h" #include "base/file_utils.h" #include "base/histogram-inl.h" +#include "base/logging.h" // For VLOG. #include "base/memory_tool.h" #include "base/stl_util.h" #include "base/systrace.h" @@ -799,12 +800,11 @@ void Heap::IncrementDisableThreadFlip(Thread* self) { bool has_waited = false; uint64_t wait_start = NanoTime(); if (thread_flip_running_) { - ATRACE_BEGIN("IncrementDisableThreadFlip"); + ScopedTrace trace("IncrementDisableThreadFlip"); while (thread_flip_running_) { has_waited = true; thread_flip_cond_->Wait(self); } - ATRACE_END(); } ++disable_thread_flip_count_; if (has_waited) { @@ -1299,7 +1299,7 @@ class TrimIndirectReferenceTableClosure : public Closure { explicit TrimIndirectReferenceTableClosure(Barrier* barrier) : barrier_(barrier) { } virtual void Run(Thread* thread) OVERRIDE NO_THREAD_SAFETY_ANALYSIS { - thread->GetJniEnv()->locals.Trim(); + thread->GetJniEnv()->TrimLocals(); // If thread is a running mutator, then act on behalf of the trim thread. // See the code in ThreadList::RunCheckpoint. barrier_->Pass(Thread::Current()); @@ -1796,19 +1796,25 @@ uint64_t Heap::GetBytesAllocatedEver() const { return GetBytesFreedEver() + GetBytesAllocated(); } +// Check whether the given object is an instance of the given class. +static bool MatchesClass(mirror::Object* obj, + Handle h_class, + bool use_is_assignable_from) REQUIRES_SHARED(Locks::mutator_lock_) { + mirror::Class* instance_class = obj->GetClass(); + CHECK(instance_class != nullptr); + ObjPtr klass = h_class.Get(); + if (use_is_assignable_from) { + return klass != nullptr && klass->IsAssignableFrom(instance_class); + } + return instance_class == klass; +} + void Heap::CountInstances(const std::vector>& classes, bool use_is_assignable_from, uint64_t* counts) { auto instance_counter = [&](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) { - mirror::Class* instance_class = obj->GetClass(); - CHECK(instance_class != nullptr); for (size_t i = 0; i < classes.size(); ++i) { - ObjPtr klass = classes[i].Get(); - if (use_is_assignable_from) { - if (klass != nullptr && klass->IsAssignableFrom(instance_class)) { - ++counts[i]; - } - } else if (instance_class == klass) { + if (MatchesClass(obj, classes[i], use_is_assignable_from)) { ++counts[i]; } } @@ -1818,11 +1824,12 @@ void Heap::CountInstances(const std::vector>& classes, void Heap::GetInstances(VariableSizedHandleScope& scope, Handle h_class, + bool use_is_assignable_from, int32_t max_count, std::vector>& instances) { DCHECK_GE(max_count, 0); auto instance_collector = [&](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) { - if (obj->GetClass() == h_class.Get()) { + if (MatchesClass(obj, h_class, use_is_assignable_from)) { if (max_count == 0 || instances.size() < static_cast(max_count)) { instances.push_back(scope.NewHandle(obj)); } @@ -1876,10 +1883,10 @@ void Heap::GetReferringObjects(VariableSizedHandleScope& scope, VisitObjects(referring_objects_finder); } -void Heap::CollectGarbage(bool clear_soft_references) { +void Heap::CollectGarbage(bool clear_soft_references, GcCause cause) { // Even if we waited for a GC we still need to do another GC since weaks allocated during the // last GC will not have necessarily been cleared. - CollectGarbageInternal(gc_plan_.back(), kGcCauseExplicit, clear_soft_references); + CollectGarbageInternal(gc_plan_.back(), cause, clear_soft_references); } bool Heap::SupportHomogeneousSpaceCompactAndCollectorTransitions() const { @@ -4149,5 +4156,10 @@ const Verification* Heap::GetVerification() const { return verification_.get(); } +void Heap::VlogHeapGrowth(size_t max_allowed_footprint, size_t new_footprint, size_t alloc_size) { + VLOG(heap) << "Growing heap from " << PrettySize(max_allowed_footprint) << " to " + << PrettySize(new_footprint) << " for a " << PrettySize(alloc_size) << " allocation"; +} + } // namespace gc } // namespace art diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 4d7424c7efbf17c98b6e4b7c8ef7346b7761bd90..7dcf82f415f33646d561efb391ce84715b3c2c2a 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -22,11 +22,14 @@ #include #include +#include + #include "allocator_type.h" #include "arch/instruction_set.h" #include "atomic.h" -#include "base/logging.h" +#include "base/macros.h" #include "base/mutex.h" +#include "base/runtime_debug.h" #include "base/time_utils.h" #include "gc/collector/gc_type.h" #include "gc/collector/iteration.h" @@ -330,7 +333,7 @@ class Heap { REQUIRES_SHARED(Locks::mutator_lock_); // Initiates an explicit garbage collection. - void CollectGarbage(bool clear_soft_references) + void CollectGarbage(bool clear_soft_references, GcCause cause = kGcCauseExplicit) REQUIRES(!*gc_complete_lock_, !*pending_task_lock_); // Does a concurrent GC, should only be called by the GC daemon thread @@ -346,9 +349,10 @@ class Heap { REQUIRES(!Locks::heap_bitmap_lock_, !*gc_complete_lock_) REQUIRES_SHARED(Locks::mutator_lock_); - // Implements JDWP RT_Instances. + // Implements VMDebug.getInstancesOfClasses and JDWP RT_Instances. void GetInstances(VariableSizedHandleScope& scope, Handle c, + bool use_is_assignable_from, int32_t max_count, std::vector>& instances) REQUIRES(!Locks::heap_bitmap_lock_, !*gc_complete_lock_) @@ -1095,6 +1099,9 @@ class Heap { void TraceHeapSize(size_t heap_size); + // Remove a vlog code from heap-inl.h which is transitively included in half the world. + static void VlogHeapGrowth(size_t max_allowed_footprint, size_t new_footprint, size_t alloc_size); + // All-known continuous spaces, where objects lie within fixed bounds. std::vector continuous_spaces_ GUARDED_BY(Locks::mutator_lock_); diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc index d58d09c79449cd67000f2d8d45e3281df3d5097b..c59642fe4e45ec893d7343399fd987d8b8d84f08 100644 --- a/runtime/gc/reference_processor.cc +++ b/runtime/gc/reference_processor.cc @@ -273,7 +273,7 @@ void ReferenceProcessor::EnqueueClearedReferences(Thread* self) { jobject cleared_references; { ReaderMutexLock mu(self, *Locks::mutator_lock_); - cleared_references = self->GetJniEnv()->vm->AddGlobalRef( + cleared_references = self->GetJniEnv()->GetVm()->AddGlobalRef( self, cleared_references_.GetList()); } if (kAsyncReferenceQueueAdd) { diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc index 576a35c52d532d8738568ca0dddcb95baf8f43fc..a3eef90e3a80bd92b72c6bc0849c4a3fc16b55bb 100644 --- a/runtime/gc/space/dlmalloc_space.cc +++ b/runtime/gc/space/dlmalloc_space.cc @@ -16,6 +16,7 @@ #include "dlmalloc_space-inl.h" +#include "base/logging.h" // For VLOG. #include "base/time_utils.h" #include "gc/accounting/card_table.h" #include "gc/accounting/space_bitmap-inl.h" diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 74813b4dd1565ff72f54dadb86341d14bcab97b9..bcfc68c4a6e3238682d58e1fd4bce98d20e2be6f 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -1587,7 +1587,9 @@ std::unique_ptr ImageSpace::CreateBootImage(const char* image_locati if (!Runtime::Current()->IsImageDex2OatEnabled()) { local_error_msg = "Patching disabled."; } else if (secondary_image) { - local_error_msg = "Cannot patch a secondary image."; + // We really want a working image. Prune and restart. + PruneDalvikCache(image_isa); + _exit(1); } else if (ImageCreationAllowed(is_global_cache, image_isa, &local_error_msg)) { bool patch_success = RelocateImage(image_location, cache_filename.c_str(), image_isa, &local_error_msg); diff --git a/runtime/gc/space/image_space_fs.h b/runtime/gc/space/image_space_fs.h index a0ecb95ac79667b67873488b83be23d5a161408d..6ce81e92097f1b9f5fbdd2d956e5e463df7a69c9 100644 --- a/runtime/gc/space/image_space_fs.h +++ b/runtime/gc/space/image_space_fs.h @@ -23,7 +23,7 @@ #include "android-base/stringprintf.h" #include "base/file_utils.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/macros.h" #include "base/unix_file/fd_file.h" #include "globals.h" diff --git a/runtime/gc/space/image_space_test.cc b/runtime/gc/space/image_space_test.cc index 429abf3311a094e30e2879161510fa5c4f46ee46..fcc47d45fc5f83d132a76e8d3d46cb1ef11ec680 100644 --- a/runtime/gc/space/image_space_test.cc +++ b/runtime/gc/space/image_space_test.cc @@ -139,11 +139,10 @@ TEST_F(ImageSpaceDex2oatTest, Test) { EXPECT_FALSE(Runtime::Current()->GetHeap()->GetBootImageSpaces().empty()); } -// Disabled for b/63622587. -// using ImageSpaceNoDex2oatNoPatchoatTest = ImageSpaceLoadingTest; -// TEST_F(ImageSpaceNoDex2oatNoPatchoatTest, Test) { -// EXPECT_TRUE(Runtime::Current()->GetHeap()->GetBootImageSpaces().empty()); -// } +using ImageSpaceNoDex2oatNoPatchoatTest = ImageSpaceLoadingTest; +TEST_F(ImageSpaceNoDex2oatNoPatchoatTest, Test) { + EXPECT_TRUE(Runtime::Current()->GetHeap()->GetBootImageSpaces().empty()); +} using ImageSpaceNoRelocateNoDex2oatNoPatchoatTest = ImageSpaceLoadingTest; TEST_F(ImageSpaceNoRelocateNoDex2oatNoPatchoatTest, Test) { diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc index 45f4f824485ff56d817f967a4b3f39a5dfa6cd00..d2efb102e95e02fdbe8916822dfb119493ef5524 100644 --- a/runtime/gc/space/large_object_space.cc +++ b/runtime/gc/space/large_object_space.cc @@ -20,7 +20,9 @@ #include -#include "base/logging.h" +#include + +#include "base/macros.h" #include "base/memory_tool.h" #include "base/mutex-inl.h" #include "base/stl_util.h" diff --git a/runtime/gc/space/malloc_space.cc b/runtime/gc/space/malloc_space.cc index dcb783782f256a7c49629e52336e4736a87a5098..17274b508dba691eca05773ee5db749cb27c1781 100644 --- a/runtime/gc/space/malloc_space.cc +++ b/runtime/gc/space/malloc_space.cc @@ -18,6 +18,7 @@ #include "android-base/stringprintf.h" +#include "base/logging.h" // For VLOG #include "gc/accounting/card_table-inl.h" #include "gc/accounting/space_bitmap-inl.h" #include "gc/heap.h" diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc index 5d1f191e4d7e53c693fc887efbdb6ae546b7d292..3a685cb82daa4821c9749086c231b233c97c38d5 100644 --- a/runtime/gc/space/rosalloc_space.cc +++ b/runtime/gc/space/rosalloc_space.cc @@ -17,6 +17,7 @@ #include "rosalloc_space-inl.h" +#include "base/logging.h" // For VLOG. #include "base/time_utils.h" #include "gc/accounting/card_table.h" #include "gc/accounting/space_bitmap-inl.h" diff --git a/runtime/gc/space/space.cc b/runtime/gc/space/space.cc index 74ce273abfad2328299566142761d1f55cb92c8a..2c6afa7eb8d28787dc10bfd1278f50f52a3d6d5c 100644 --- a/runtime/gc/space/space.cc +++ b/runtime/gc/space/space.cc @@ -16,7 +16,9 @@ #include "space.h" -#include "base/logging.h" +#include + +#include "base/macros.h" #include "gc/accounting/heap_bitmap.h" #include "gc/accounting/space_bitmap-inl.h" #include "gc/heap.h" diff --git a/runtime/gc/task_processor.cc b/runtime/gc/task_processor.cc index e9286440545b2b5e9295523300a241ce83ec7302..64e8322a0db1898a582e25297d44bbabf598ede9 100644 --- a/runtime/gc/task_processor.cc +++ b/runtime/gc/task_processor.cc @@ -23,32 +23,37 @@ namespace art { namespace gc { TaskProcessor::TaskProcessor() - : lock_(new Mutex("Task processor lock", kReferenceProcessorLock)), is_running_(false), + : lock_("Task processor lock", kReferenceProcessorLock), + cond_("Task processor condition", lock_), + is_running_(false), running_thread_(nullptr) { - // Piggyback off the reference processor lock level. - cond_.reset(new ConditionVariable("Task processor condition", *lock_)); } TaskProcessor::~TaskProcessor() { - delete lock_; + if (!tasks_.empty()) { + LOG(WARNING) << "TaskProcessor: Finalizing " << tasks_.size() << " unprocessed tasks."; + for (HeapTask* task : tasks_) { + task->Finalize(); + } + } } void TaskProcessor::AddTask(Thread* self, HeapTask* task) { ScopedThreadStateChange tsc(self, kWaitingForTaskProcessor); - MutexLock mu(self, *lock_); + MutexLock mu(self, lock_); tasks_.insert(task); - cond_->Signal(self); + cond_.Signal(self); } HeapTask* TaskProcessor::GetTask(Thread* self) { ScopedThreadStateChange tsc(self, kWaitingForTaskProcessor); - MutexLock mu(self, *lock_); + MutexLock mu(self, lock_); while (true) { if (tasks_.empty()) { if (!is_running_) { return nullptr; } - cond_->Wait(self); // Empty queue, wait until we are signalled. + cond_.Wait(self); // Empty queue, wait until we are signalled. } else { // Non empty queue, look at the top element and see if we are ready to run it. const uint64_t current_time = NanoTime(); @@ -61,18 +66,18 @@ HeapTask* TaskProcessor::GetTask(Thread* self) { return task; } DCHECK_GT(target_time, current_time); - // Wait untl we hit the target run time. + // Wait until we hit the target run time. const uint64_t delta_time = target_time - current_time; const uint64_t ms_delta = NsToMs(delta_time); const uint64_t ns_delta = delta_time - MsToNs(ms_delta); - cond_->TimedWait(self, static_cast(ms_delta), static_cast(ns_delta)); + cond_.TimedWait(self, static_cast(ms_delta), static_cast(ns_delta)); } } UNREACHABLE(); } void TaskProcessor::UpdateTargetRunTime(Thread* self, HeapTask* task, uint64_t new_target_time) { - MutexLock mu(self, *lock_); + MutexLock mu(self, lock_); // Find the task. auto range = tasks_.equal_range(task); for (auto it = range.first; it != range.second; ++it) { @@ -85,7 +90,7 @@ void TaskProcessor::UpdateTargetRunTime(Thread* self, HeapTask* task, uint64_t n // If we became the first task then we may need to signal since we changed the task that we // are sleeping on. if (*tasks_.begin() == task) { - cond_->Signal(self); + cond_.Signal(self); } return; } @@ -94,24 +99,24 @@ void TaskProcessor::UpdateTargetRunTime(Thread* self, HeapTask* task, uint64_t n } bool TaskProcessor::IsRunning() const { - MutexLock mu(Thread::Current(), *lock_); + MutexLock mu(Thread::Current(), lock_); return is_running_; } Thread* TaskProcessor::GetRunningThread() const { - MutexLock mu(Thread::Current(), *lock_); + MutexLock mu(Thread::Current(), lock_); return running_thread_; } void TaskProcessor::Stop(Thread* self) { - MutexLock mu(self, *lock_); + MutexLock mu(self, lock_); is_running_ = false; running_thread_ = nullptr; - cond_->Broadcast(self); + cond_.Broadcast(self); } void TaskProcessor::Start(Thread* self) { - MutexLock mu(self, *lock_); + MutexLock mu(self, lock_); is_running_ = true; running_thread_ = self; } diff --git a/runtime/gc/task_processor.h b/runtime/gc/task_processor.h index e40fa063191b41fc77630a4dc3a7cd99be252c04..f6b560703724ce7d1883522f2facd85cbc0bd4a9 100644 --- a/runtime/gc/task_processor.h +++ b/runtime/gc/task_processor.h @@ -54,17 +54,17 @@ class TaskProcessor { public: TaskProcessor(); virtual ~TaskProcessor(); - void AddTask(Thread* self, HeapTask* task) REQUIRES(!*lock_); - HeapTask* GetTask(Thread* self) REQUIRES(!*lock_); - void Start(Thread* self) REQUIRES(!*lock_); + void AddTask(Thread* self, HeapTask* task) REQUIRES(!lock_); + HeapTask* GetTask(Thread* self) REQUIRES(!lock_); + void Start(Thread* self) REQUIRES(!lock_); // Stop tells the RunAllTasks to finish up the remaining tasks as soon as // possible then return. - void Stop(Thread* self) REQUIRES(!*lock_); - void RunAllTasks(Thread* self) REQUIRES(!*lock_); - bool IsRunning() const REQUIRES(!*lock_); + void Stop(Thread* self) REQUIRES(!lock_); + void RunAllTasks(Thread* self) REQUIRES(!lock_); + bool IsRunning() const REQUIRES(!lock_); void UpdateTargetRunTime(Thread* self, HeapTask* target_time, uint64_t new_target_time) - REQUIRES(!*lock_); - Thread* GetRunningThread() const REQUIRES(!*lock_); + REQUIRES(!lock_); + Thread* GetRunningThread() const REQUIRES(!lock_); private: class CompareByTargetRunTime { @@ -74,9 +74,9 @@ class TaskProcessor { } }; - mutable Mutex* lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + ConditionVariable cond_ GUARDED_BY(lock_); bool is_running_ GUARDED_BY(lock_); - std::unique_ptr cond_ GUARDED_BY(lock_); std::multiset tasks_ GUARDED_BY(lock_); Thread* running_thread_ GUARDED_BY(lock_); diff --git a/runtime/gc/verification.cc b/runtime/gc/verification.cc index 3cd04a61e9aa33f55b11db2e4f18ea5d6afb789d..d99b37762fb16918a8b7853903019a49387c9b9d 100644 --- a/runtime/gc/verification.cc +++ b/runtime/gc/verification.cc @@ -86,7 +86,7 @@ void Verification::LogHeapCorruption(ObjPtr holder, mirror::Object* ref, bool fatal) const { // Lowest priority logging first: - PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT); + PrintFileToLog("/proc/self/maps", android::base::LogSeverity::FATAL_WITHOUT_ABORT); MemMap::DumpMaps(LOG_STREAM(FATAL_WITHOUT_ABORT), true); // Buffer the output in the string stream since it is more important than the stack traces // and we want it to have log priority. The stack traces are printed from Runtime::Abort diff --git a/runtime/globals.h b/runtime/globals.h index 53932fd8cd7b8a48f53905adadf9383dbbfe9369..f14d6e95a67793c5e2df314c9a0422e3fb166b05 100644 --- a/runtime/globals.h +++ b/runtime/globals.h @@ -125,12 +125,6 @@ static constexpr TraceClockSource kDefaultTraceClockSource = TraceClockSource::k static constexpr bool kDefaultMustRelocate = true; -#ifdef ART_ENABLE_VDEX -static constexpr bool kIsVdexEnabled = true; -#else -static constexpr bool kIsVdexEnabled = false; -#endif - // Size of a heap reference. static constexpr size_t kHeapReferenceSize = sizeof(uint32_t); diff --git a/runtime/handle.h b/runtime/handle.h index ccff57549508849e49665baaa16359067b66e325..18e503d1de71ca3187d46a9efa1e42be782b19bf 100644 --- a/runtime/handle.h +++ b/runtime/handle.h @@ -17,8 +17,9 @@ #ifndef ART_RUNTIME_HANDLE_H_ #define ART_RUNTIME_HANDLE_H_ +#include + #include "base/casts.h" -#include "base/logging.h" #include "base/macros.h" #include "base/mutex.h" #include "base/value_object.h" diff --git a/runtime/handle_scope.h b/runtime/handle_scope.h index f248a118e900a6688be6085c2023f6c83c8a5c4f..28a230291d143934c9befaad4b57dc3dba4118f0 100644 --- a/runtime/handle_scope.h +++ b/runtime/handle_scope.h @@ -19,8 +19,9 @@ #include +#include + #include "base/enums.h" -#include "base/logging.h" #include "base/macros.h" #include "base/mutex.h" #include "handle.h" diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc index 6a1a8c72717f4f1f088fbcadf4743795b9fcf187..f4fc85b8e5b52c6b8c784b9d96ffe586d7930fec 100644 --- a/runtime/hprof/hprof.cc +++ b/runtime/hprof/hprof.cc @@ -36,11 +36,13 @@ #include -#include "android-base/stringprintf.h" +#include +#include #include "art_field-inl.h" #include "art_method-inl.h" -#include "base/logging.h" +#include "base/macros.h" +#include "base/mutex.h" #include "base/time_utils.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" diff --git a/runtime/image.cc b/runtime/image.cc index aae572b68d376f1b8da281842cf304f68b4b55bd..8f35d8474c78407e3af6529a91aa072f7a534a4c 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -26,7 +26,7 @@ namespace art { const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' }; -const uint8_t ImageHeader::kImageVersion[] = { '0', '4', 'A', '\0' }; // VarHandle fence intrinsics +const uint8_t ImageHeader::kImageVersion[] = { '0', '5', '1', '\0' }; // @FastNative access flags. ImageHeader::ImageHeader(uint32_t image_begin, uint32_t image_size, diff --git a/runtime/image.h b/runtime/image.h index 3844186a9b1c5963afb0123281eb8b34dbc4abe3..159a308fb3059c692b99ab67a7f91625feac37db 100644 --- a/runtime/image.h +++ b/runtime/image.h @@ -179,6 +179,10 @@ class PACKED(4) ImageHeader { return patch_delta_; } + void SetPatchDelta(off_t patch_delta) { + patch_delta_ = patch_delta; + } + static std::string GetOatLocationFromImageLocation(const std::string& image) { return GetLocationFromImageLocation(image, "oat"); } diff --git a/runtime/indenter.h b/runtime/indenter.h index cc6d4c4e96a24847ee074d4eea34ed1e38f12440..6361dd2092c71fa2e9f28a6dfa62bef5ab121af5 100644 --- a/runtime/indenter.h +++ b/runtime/indenter.h @@ -20,7 +20,8 @@ #include #include -#include "base/logging.h" +#include + #include "base/macros.h" namespace art { @@ -32,7 +33,7 @@ class Indenter : public std::streambuf { public: Indenter(std::streambuf* out, char text, size_t count) : indent_next_(true), out_sbuf_(out), - text_{text, text, text, text, text, text, text, text}, // NOLINT(whitespace/braces) + text_{text, text, text, text, text, text, text, text}, count_(count) {} private: diff --git a/runtime/index_bss_mapping.cc b/runtime/index_bss_mapping.cc new file mode 100644 index 0000000000000000000000000000000000000000..8d9d8cfe63f1252ea13ebfdedec1d1a36bb68fd6 --- /dev/null +++ b/runtime/index_bss_mapping.cc @@ -0,0 +1,72 @@ +/* + * 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 + +#include "index_bss_mapping.h" + +#include "base/bit_utils.h" +#include "base/length_prefixed_array.h" + +namespace art { + +size_t IndexBssMappingEntry::GetBssOffset(size_t index_bits, + uint32_t index, + size_t slot_size) const { + uint32_t diff = GetIndex(index_bits) - index; + if (diff == 0u) { + return bss_offset; + } + size_t mask_bits = 32u - index_bits; + if (diff > mask_bits) { + return IndexBssMappingLookup::npos; + } + // Shift out the index bits and bits for lower indexes. + // Note that `index_bits + (mask_bits - diff) == 32 - diff`. + uint32_t mask_from_index = index_and_mask >> (32u - diff); + if ((mask_from_index & 1u) != 0u) { + return bss_offset - POPCOUNT(mask_from_index) * slot_size; + } else { + return IndexBssMappingLookup::npos; + } +} + +constexpr size_t IndexBssMappingLookup::npos; + +size_t IndexBssMappingLookup::GetBssOffset(const IndexBssMapping* mapping, + uint32_t index, + uint32_t number_of_indexes, + size_t slot_size) { + DCHECK_LT(index, number_of_indexes); + if (mapping == nullptr) { + return npos; + } + size_t index_bits = IndexBssMappingEntry::IndexBits(number_of_indexes); + uint32_t index_mask = IndexBssMappingEntry::IndexMask(index_bits); + auto it = std::partition_point( + mapping->begin(), + mapping->end(), + [=](const struct IndexBssMappingEntry& entry) { + return (entry.index_and_mask & index_mask) < index; + }); + if (it == mapping->end()) { + return npos; + } + const IndexBssMappingEntry& entry = *it; + return entry.GetBssOffset(index_bits, index, slot_size); +} + +} // namespace art diff --git a/runtime/index_bss_mapping.h b/runtime/index_bss_mapping.h new file mode 100644 index 0000000000000000000000000000000000000000..dcbc05c195ff717488b7d88eb4c2629fa8e5b50f --- /dev/null +++ b/runtime/index_bss_mapping.h @@ -0,0 +1,82 @@ +/* + * 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_RUNTIME_INDEX_BSS_MAPPING_H_ +#define ART_RUNTIME_INDEX_BSS_MAPPING_H_ + +#include + +#include "base/bit_utils.h" + +namespace art { + +template class LengthPrefixedArray; + +// IndexBssMappingEntry describes a mapping of one or more indexes to their offsets in the .bss. +// A sorted array of IndexBssMappingEntry is used to describe the mapping of method indexes, +// type indexes or string indexes to offsets of their assigned slots in the .bss. +// +// The highest index and a mask are stored in a single `uint32_t index_and_mask` and the split +// between the index and the mask is provided externally. The "mask" bits specify whether some +// of the previous indexes are mapped to immediately preceding slots. This is permissible only +// if the slots are consecutive and in the same order as indexes. +// +// The .bss offset of the slot associated with the highest index is stored in plain form as +// `bss_offset`. If the mask specifies any smaller indexes being mapped to immediately +// preceding slots, their offsets are calculated using an externally supplied size of the slot. +struct IndexBssMappingEntry { + static size_t IndexBits(uint32_t number_of_indexes) { + DCHECK_NE(number_of_indexes, 0u); + return MinimumBitsToStore(number_of_indexes - 1u); + } + + static uint32_t IndexMask(size_t index_bits) { + DCHECK_LE(index_bits, 32u); + constexpr uint32_t kAllOnes = static_cast(-1); + // Handle `index_bits == 32u` explicitly; shifting uint32_t left by 32 is undefined behavior. + return (index_bits == 32u) ? kAllOnes : ~(kAllOnes << index_bits); + } + + uint32_t GetIndex(size_t index_bits) const { + return index_and_mask & IndexMask(index_bits); + } + + uint32_t GetMask(size_t index_bits) const { + DCHECK_LT(index_bits, 32u); // GetMask() is valid only if there is at least 1 mask bit. + return index_and_mask >> index_bits; + } + + size_t GetBssOffset(size_t index_bits, uint32_t index, size_t slot_size) const; + + uint32_t index_and_mask; + uint32_t bss_offset; +}; + +using IndexBssMapping = LengthPrefixedArray; + +class IndexBssMappingLookup { + public: + static constexpr size_t npos = static_cast(-1); + + static size_t GetBssOffset(const IndexBssMapping* mapping, + uint32_t index, + uint32_t number_of_indexes, + size_t slot_size); +}; + +} // namespace art + +#endif // ART_RUNTIME_INDEX_BSS_MAPPING_H_ diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc index 2c8ec47492e106f074b76050568685e46df7045d..51878312d94c70d45f75be7e6bd9300254db6b0b 100644 --- a/runtime/indirect_reference_table.cc +++ b/runtime/indirect_reference_table.cc @@ -357,7 +357,7 @@ bool IndirectReferenceTable::Remove(IRTSegmentState previous_state, IndirectRef if (self->HandleScopeContains(reinterpret_cast(iref))) { auto* env = self->GetJniEnv(); DCHECK(env != nullptr); - if (env->check_jni) { + if (env->IsCheckJniEnabled()) { ScopedObjectAccess soa(self); LOG(WARNING) << "Attempt to remove non-JNI local reference, dumping thread"; if (kDumpStackOnNonLocalReference) { diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h index 6675099523682fc5979eb4787c0e9e97a344b15c..00184e2ed06436e68c06dd439532ff9cdeb7f68e 100644 --- a/runtime/indirect_reference_table.h +++ b/runtime/indirect_reference_table.h @@ -23,8 +23,10 @@ #include #include +#include + #include "base/bit_utils.h" -#include "base/logging.h" +#include "base/macros.h" #include "base/mutex.h" #include "gc_root.h" #include "obj_ptr.h" diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 49f202182d10be35d8123816aafb716af42a9ee6..a4ee21d4aa2274b1920d715c156f4d9de30c7303 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -1246,18 +1246,17 @@ struct RuntimeMethodShortyVisitor : public StackVisitor { shorty = m->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetShorty()[0]; return false; } - const DexFile::CodeItem* code_item = m->GetCodeItem(); - const Instruction* instr = Instruction::At(&code_item->insns_[GetDexPc()]); - if (instr->IsInvoke()) { + const Instruction& instr = m->DexInstructions().InstructionAt(GetDexPc()); + if (instr.IsInvoke()) { const DexFile* dex_file = m->GetDexFile(); - if (interpreter::IsStringInit(dex_file, instr->VRegB())) { + if (interpreter::IsStringInit(dex_file, instr.VRegB())) { // Invoking string init constructor is turned into invoking // StringFactory.newStringFromChars() which returns a string. shorty = 'L'; return false; } // A regular invoke, use callee's shorty. - uint32_t method_idx = instr->VRegB(); + uint32_t method_idx = instr.VRegB(); shorty = dex_file->GetMethodShorty(method_idx)[0]; } // Stop stack walking since we've seen a Java frame. diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index 01b7d4e4573d05db7fbb5a39c33c4a66959158c4..9b81819da26f3ad4ef37b69d2d171c4ebea798f4 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -240,7 +240,7 @@ static constexpr InterpreterImplKind kInterpreterImplKind = kMterpImplKind; static inline JValue Execute( Thread* self, - const DexFile::CodeItem* code_item, + const CodeItemDataAccessor& accessor, ShadowFrame& shadow_frame, JValue result_register, bool stay_in_interpreter = false) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -254,11 +254,13 @@ static inline JValue Execute( ArtMethod *method = shadow_frame.GetMethod(); if (UNLIKELY(instrumentation->HasMethodEntryListeners())) { - instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), - method, 0); + instrumentation->MethodEnterEvent(self, + shadow_frame.GetThisObject(accessor.InsSize()), + method, + 0); if (UNLIKELY(self->IsExceptionPending())) { instrumentation->MethodUnwindEvent(self, - shadow_frame.GetThisObject(code_item->ins_size_), + shadow_frame.GetThisObject(accessor.InsSize()), method, 0); return JValue(); @@ -277,7 +279,7 @@ static inline JValue Execute( // Calculate the offset of the first input reg. The input registers are in the high regs. // It's ok to access the code item here since JIT code will have been touched by the // interpreter and compiler already. - uint16_t arg_offset = code_item->registers_size_ - code_item->ins_size_; + uint16_t arg_offset = accessor.RegistersSize() - accessor.InsSize(); ArtInterpreterToCompiledCodeBridge(self, nullptr, &shadow_frame, arg_offset, &result); // Push the shadow frame back as the caller will expect it. self->PushShadowFrame(&shadow_frame); @@ -302,27 +304,27 @@ static inline JValue Execute( if (kInterpreterImplKind == kMterpImplKind) { if (transaction_active) { // No Mterp variant - just use the switch interpreter. - return ExecuteSwitchImpl(self, code_item, shadow_frame, result_register, + return ExecuteSwitchImpl(self, accessor, shadow_frame, result_register, false); } else if (UNLIKELY(!Runtime::Current()->IsStarted())) { - return ExecuteSwitchImpl(self, code_item, shadow_frame, result_register, + return ExecuteSwitchImpl(self, accessor, shadow_frame, result_register, false); } else { while (true) { // Mterp does not support all instrumentation/debugging. if (MterpShouldSwitchInterpreters() != 0) { - return ExecuteSwitchImpl(self, code_item, shadow_frame, result_register, + return ExecuteSwitchImpl(self, accessor, shadow_frame, result_register, false); } bool returned = ExecuteMterpImpl(self, - code_item->insns_, + accessor.Insns(), &shadow_frame, &result_register); if (returned) { return result_register; } else { // Mterp didn't like that instruction. Single-step it with the reference interpreter. - result_register = ExecuteSwitchImpl(self, code_item, shadow_frame, + result_register = ExecuteSwitchImpl(self, accessor, shadow_frame, result_register, true); if (shadow_frame.GetDexPC() == dex::kDexNoIndex) { // Single-stepped a return or an exception not handled locally. Return to caller. @@ -334,10 +336,10 @@ static inline JValue Execute( } else { DCHECK_EQ(kInterpreterImplKind, kSwitchImplKind); if (transaction_active) { - return ExecuteSwitchImpl(self, code_item, shadow_frame, result_register, + return ExecuteSwitchImpl(self, accessor, shadow_frame, result_register, false); } else { - return ExecuteSwitchImpl(self, code_item, shadow_frame, result_register, + return ExecuteSwitchImpl(self, accessor, shadow_frame, result_register, false); } } @@ -346,19 +348,19 @@ static inline JValue Execute( if (kInterpreterImplKind == kMterpImplKind) { // No access check variants for Mterp. Just use the switch version. if (transaction_active) { - return ExecuteSwitchImpl(self, code_item, shadow_frame, result_register, + return ExecuteSwitchImpl(self, accessor, shadow_frame, result_register, false); } else { - return ExecuteSwitchImpl(self, code_item, shadow_frame, result_register, + return ExecuteSwitchImpl(self, accessor, shadow_frame, result_register, false); } } else { DCHECK_EQ(kInterpreterImplKind, kSwitchImplKind); if (transaction_active) { - return ExecuteSwitchImpl(self, code_item, shadow_frame, result_register, + return ExecuteSwitchImpl(self, accessor, shadow_frame, result_register, false); } else { - return ExecuteSwitchImpl(self, code_item, shadow_frame, result_register, + return ExecuteSwitchImpl(self, accessor, shadow_frame, result_register, false); } } @@ -387,12 +389,12 @@ void EnterInterpreterFromInvoke(Thread* self, } const char* old_cause = self->StartAssertNoThreadSuspension("EnterInterpreterFromInvoke"); - const DexFile::CodeItem* code_item = method->GetCodeItem(); + CodeItemDataAccessor accessor(method); uint16_t num_regs; uint16_t num_ins; - if (code_item != nullptr) { - num_regs = code_item->registers_size_; - num_ins = code_item->ins_size_; + if (accessor.HasCodeItem()) { + num_regs = accessor.RegistersSize(); + num_ins = accessor.InsSize(); } else if (!method->IsInvokable()) { self->EndAssertNoThreadSuspension(old_cause); method->ThrowInvocationTimeError(); @@ -454,7 +456,7 @@ void EnterInterpreterFromInvoke(Thread* self, } } if (LIKELY(!method->IsNative())) { - JValue r = Execute(self, code_item, *shadow_frame, JValue(), stay_in_interpreter); + JValue r = Execute(self, accessor, *shadow_frame, JValue(), stay_in_interpreter); if (result != nullptr) { *result = r; } @@ -497,7 +499,7 @@ void EnterInterpreterFromDeoptimize(Thread* self, DCHECK(!shadow_frame->GetMethod()->MustCountLocks()); self->SetTopOfShadowStack(shadow_frame); - const DexFile::CodeItem* code_item = shadow_frame->GetMethod()->GetCodeItem(); + CodeItemDataAccessor accessor(shadow_frame->GetMethod()); const uint32_t dex_pc = shadow_frame->GetDexPC(); uint32_t new_dex_pc = dex_pc; if (UNLIKELY(self->IsExceptionPending())) { @@ -510,7 +512,7 @@ void EnterInterpreterFromDeoptimize(Thread* self, self, *shadow_frame, instrumentation) ? shadow_frame->GetDexPC() : dex::kDexNoIndex; } else if (!from_code) { // Deoptimization is not called from code directly. - const Instruction* instr = Instruction::At(&code_item->insns_[dex_pc]); + const Instruction* instr = &accessor.InstructionAt(dex_pc); if (deopt_method_type == DeoptimizationMethodType::kKeepDexPc) { DCHECK(first); // Need to re-execute the dex instruction. @@ -566,7 +568,7 @@ void EnterInterpreterFromDeoptimize(Thread* self, } if (new_dex_pc != dex::kDexNoIndex) { shadow_frame->SetDexPC(new_dex_pc); - value = Execute(self, code_item, *shadow_frame, value); + value = Execute(self, accessor, *shadow_frame, value); } ShadowFrame* old_frame = shadow_frame; shadow_frame = shadow_frame->GetLink(); @@ -580,7 +582,7 @@ void EnterInterpreterFromDeoptimize(Thread* self, ret_val->SetJ(value.GetJ()); } -JValue EnterInterpreterFromEntryPoint(Thread* self, const DexFile::CodeItem* code_item, +JValue EnterInterpreterFromEntryPoint(Thread* self, const CodeItemDataAccessor& accessor, ShadowFrame* shadow_frame) { DCHECK_EQ(self, Thread::Current()); bool implicit_check = !Runtime::Current()->ExplicitStackOverflowChecks(); @@ -593,11 +595,11 @@ JValue EnterInterpreterFromEntryPoint(Thread* self, const DexFile::CodeItem* cod if (jit != nullptr) { jit->NotifyCompiledCodeToInterpreterTransition(self, shadow_frame->GetMethod()); } - return Execute(self, code_item, *shadow_frame, JValue()); + return Execute(self, accessor, *shadow_frame, JValue()); } void ArtInterpreterToInterpreterBridge(Thread* self, - const DexFile::CodeItem* code_item, + const CodeItemDataAccessor& accessor, ShadowFrame* shadow_frame, JValue* result) { bool implicit_check = !Runtime::Current()->ExplicitStackOverflowChecks(); @@ -626,7 +628,7 @@ void ArtInterpreterToInterpreterBridge(Thread* self, } if (LIKELY(!shadow_frame->GetMethod()->IsNative())) { - result->SetJ(Execute(self, code_item, *shadow_frame, JValue()).GetJ()); + result->SetJ(Execute(self, accessor, *shadow_frame, JValue()).GetJ()); } else { // We don't expect to be asked to interpret native code (which is entered via a JNI compiler // generated stub) except during testing and image writing. diff --git a/runtime/interpreter/interpreter.h b/runtime/interpreter/interpreter.h index df8568edcd4e1e6d423b6dc2b67d6981efd53628..f9d7774d5b259260a4b0780a23eba3cc13ee6372 100644 --- a/runtime/interpreter/interpreter.h +++ b/runtime/interpreter/interpreter.h @@ -27,6 +27,7 @@ class Object; } // namespace mirror class ArtMethod; +class CodeItemDataAccessor; union JValue; class ShadowFrame; class Thread; @@ -52,12 +53,15 @@ extern void EnterInterpreterFromDeoptimize(Thread* self, DeoptimizationMethodType method_type) REQUIRES_SHARED(Locks::mutator_lock_); -extern JValue EnterInterpreterFromEntryPoint(Thread* self, const DexFile::CodeItem* code_item, +extern JValue EnterInterpreterFromEntryPoint(Thread* self, + const CodeItemDataAccessor& accessor, ShadowFrame* shadow_frame) REQUIRES_SHARED(Locks::mutator_lock_); -void ArtInterpreterToInterpreterBridge(Thread* self, const DexFile::CodeItem* code_item, - ShadowFrame* shadow_frame, JValue* result) +void ArtInterpreterToInterpreterBridge(Thread* self, + const CodeItemDataAccessor& accessor, + ShadowFrame* shadow_frame, + JValue* result) REQUIRES_SHARED(Locks::mutator_lock_); // One-time sanity check. diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 0a1ae361671f5494a411129a0208d271e424a1b8..deed16b97466b75cf760efed76b1723a6f4ac6ac 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -644,52 +644,47 @@ static bool DoMethodHandleInvokeCommon(Thread* self, // arguments either from a range or an array of arguments depending // on whether the DEX instruction is invoke-polymorphic/range or // invoke-polymorphic. The array here is for the latter. - uint32_t args[Instruction::kMaxVarArgRegs] = {}; if (UNLIKELY(is_range)) { // VRegC is the register holding the method handle. Arguments passed // to the method handle's target do not include the method handle. - uint32_t first_arg = inst->VRegC_4rcc() + 1; - static const bool kIsRange = true; + RangeInstructionOperands operands(inst->VRegC_4rcc() + 1, inst->VRegA_4rcc() - 1); if (invoke_exact) { - return art::MethodHandleInvokeExact(self, - shadow_frame, - method_handle, - callsite_type, - args /* unused */, - first_arg, - result); + return MethodHandleInvokeExact(self, + shadow_frame, + method_handle, + callsite_type, + &operands, + result); } else { - return art::MethodHandleInvoke(self, - shadow_frame, - method_handle, - callsite_type, - args /* unused */, - first_arg, - result); + return MethodHandleInvoke(self, + shadow_frame, + method_handle, + callsite_type, + &operands, + result); } } else { // Get the register arguments for the invoke. + uint32_t args[Instruction::kMaxVarArgRegs] = {}; inst->GetVarArgs(args, inst_data); // Drop the first register which is the method handle performing the invoke. memmove(args, args + 1, sizeof(args[0]) * (Instruction::kMaxVarArgRegs - 1)); args[Instruction::kMaxVarArgRegs - 1] = 0; - static const bool kIsRange = false; + VarArgsInstructionOperands operands(args, inst->VRegA_45cc() - 1); if (invoke_exact) { - return art::MethodHandleInvokeExact(self, - shadow_frame, - method_handle, - callsite_type, - args, - args[0], - result); + return MethodHandleInvokeExact(self, + shadow_frame, + method_handle, + callsite_type, + &operands, + result); } else { - return art::MethodHandleInvoke(self, - shadow_frame, - method_handle, - callsite_type, - args, - args[0], - result); + return MethodHandleInvoke(self, + shadow_frame, + method_handle, + callsite_type, + &operands, + result); } } } @@ -1067,7 +1062,7 @@ static ObjPtr InvokeBootstrapMethod(Thread* self, // The second parameter is the name to lookup. { dex::StringIndex name_idx(static_cast(it.GetJavaValue().i)); - ObjPtr name = class_linker->ResolveString(*dex_file, name_idx, dex_cache); + ObjPtr name = class_linker->ResolveString(name_idx, dex_cache); if (name.IsNull()) { DCHECK(self->IsExceptionPending()); return nullptr; @@ -1078,12 +1073,8 @@ static ObjPtr InvokeBootstrapMethod(Thread* self, // The third parameter is the method type associated with the name. uint32_t method_type_idx = static_cast(it.GetJavaValue().i); - Handle - method_type(hs.NewHandle(class_linker->ResolveMethodType(self, - *dex_file, - method_type_idx, - dex_cache, - class_loader))); + Handle method_type(hs.NewHandle( + class_linker->ResolveMethodType(self, method_type_idx, dex_cache, class_loader))); if (method_type.IsNull()) { DCHECK(self->IsExceptionPending()); return nullptr; @@ -1118,7 +1109,7 @@ static ObjPtr InvokeBootstrapMethod(Thread* self, case EncodedArrayValueIterator::ValueType::kMethodType: { uint32_t idx = static_cast(jvalue.i); ObjPtr ref = - class_linker->ResolveMethodType(self, *dex_file, idx, dex_cache, class_loader); + class_linker->ResolveMethodType(self, idx, dex_cache, class_loader); if (ref.IsNull()) { DCHECK(self->IsExceptionPending()); return nullptr; @@ -1141,7 +1132,7 @@ static ObjPtr InvokeBootstrapMethod(Thread* self, } case EncodedArrayValueIterator::ValueType::kString: { dex::StringIndex idx(static_cast(jvalue.i)); - ObjPtr ref = class_linker->ResolveString(*dex_file, idx, dex_cache); + ObjPtr ref = class_linker->ResolveString(idx, dex_cache); if (ref.IsNull()) { DCHECK(self->IsExceptionPending()); return nullptr; @@ -1152,8 +1143,7 @@ static ObjPtr InvokeBootstrapMethod(Thread* self, } case EncodedArrayValueIterator::ValueType::kType: { dex::TypeIndex idx(static_cast(jvalue.i)); - ObjPtr ref = - class_linker->ResolveType(*dex_file, idx, dex_cache, class_loader); + ObjPtr ref = class_linker->ResolveType(idx, dex_cache, class_loader); if (ref.IsNull()) { DCHECK(self->IsExceptionPending()); return nullptr; @@ -1180,17 +1170,13 @@ static ObjPtr InvokeBootstrapMethod(Thread* self, // Invoke the bootstrap method handle. JValue result; - - // This array of arguments is unused. DoMethodHandleInvokeExact() operates on either a - // an argument array or a range, but always takes an array argument. - uint32_t args_unused[Instruction::kMaxVarArgRegs]; - bool invoke_success = art::MethodHandleInvokeExact(self, - *bootstrap_frame, - bootstrap, - bootstrap_method_type, - args_unused, - 0, - &result); + RangeInstructionOperands operands(0, vreg); + bool invoke_success = MethodHandleInvokeExact(self, + *bootstrap_frame, + bootstrap, + bootstrap_method_type, + &operands, + &result); if (!invoke_success) { DCHECK(self->IsExceptionPending()); return nullptr; @@ -1273,21 +1259,25 @@ bool DoInvokeCustom(Thread* self, Handle target = hs.NewHandle(call_site->GetTarget()); Handle target_method_type = hs.NewHandle(target->GetMethodType()); DCHECK_EQ(static_cast(inst->VRegA()), target_method_type->NumberOfVRegs()); - - uint32_t args[Instruction::kMaxVarArgRegs]; if (is_range) { - args[0] = inst->VRegC_3rc(); + RangeInstructionOperands operands(inst->VRegC_3rc(), inst->VRegA_3rc()); + return MethodHandleInvokeExact(self, + shadow_frame, + target, + target_method_type, + &operands, + result); } else { + uint32_t args[Instruction::kMaxVarArgRegs]; inst->GetVarArgs(args, inst_data); + VarArgsInstructionOperands operands(args, inst->VRegA_35c()); + return MethodHandleInvokeExact(self, + shadow_frame, + target, + target_method_type, + &operands, + result); } - - return art::MethodHandleInvokeExact(self, - shadow_frame, - target, - target_method_type, - args, - args[0], - result); } template @@ -1330,7 +1320,7 @@ static inline bool DoCallCommon(ArtMethod* called_method, } // Compute method information. - const DexFile::CodeItem* code_item = called_method->GetCodeItem(); + CodeItemDataAccessor accessor(called_method); // Number of registers for the callee's call frame. uint16_t num_regs; // Test whether to use the interpreter or compiler entrypoint, and save that result to pass to @@ -1344,7 +1334,7 @@ static inline bool DoCallCommon(ArtMethod* called_method, ClassLinker::ShouldUseInterpreterEntrypoint( called_method, called_method->GetEntryPointFromQuickCompiledCode()); - if (LIKELY(code_item != nullptr)) { + if (LIKELY(accessor.HasCodeItem())) { // When transitioning to compiled code, space only needs to be reserved for the input registers. // The rest of the frame gets discarded. This also prevents accessing the called method's code // item, saving memory by keeping code items of compiled code untouched. @@ -1352,8 +1342,8 @@ static inline bool DoCallCommon(ArtMethod* called_method, DCHECK(!Runtime::Current()->IsAotCompiler()) << "Compiler should use interpreter entrypoint"; num_regs = number_of_inputs; } else { - num_regs = code_item->registers_size_; - DCHECK_EQ(string_init ? number_of_inputs - 1 : number_of_inputs, code_item->ins_size_); + num_regs = accessor.RegistersSize(); + DCHECK_EQ(string_init ? number_of_inputs - 1 : number_of_inputs, accessor.InsSize()); } } else { DCHECK(called_method->IsNative() || called_method->IsProxyMethod()); @@ -1377,7 +1367,7 @@ static inline bool DoCallCommon(ArtMethod* called_method, DCHECK_GT(num_regs, 0u); // As the method is an instance method, there should be at least 1. // The new StringFactory call is static and has one fewer argument. - if (code_item == nullptr) { + if (!accessor.HasCodeItem()) { DCHECK(called_method->IsNative() || called_method->IsProxyMethod()); num_regs--; } // else ... don't need to change num_regs since it comes up from the string_init's code item @@ -1509,7 +1499,7 @@ static inline bool DoCallCommon(ArtMethod* called_method, } PerformCall(self, - code_item, + accessor, shadow_frame.GetMethod(), first_dest_reg, new_shadow_frame, diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index f097bc71b9701da57023bd7ee878621d32e9f59c..269b013caf0812898c73ff68e38aed10b2df605f 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -26,13 +26,14 @@ #include #include -#include "android-base/stringprintf.h" +#include +#include #include "art_field-inl.h" #include "art_method-inl.h" #include "base/enums.h" -#include "base/logging.h" #include "base/macros.h" +#include "base/mutex.h" #include "class_linker-inl.h" #include "common_dex_operations.h" #include "common_throws.h" @@ -206,17 +207,17 @@ static inline bool DoInvoke(Thread* self, } } -static inline mirror::MethodHandle* ResolveMethodHandle(Thread* self, - uint32_t method_handle_index, - ArtMethod* referrer) +static inline ObjPtr ResolveMethodHandle(Thread* self, + uint32_t method_handle_index, + ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); return class_linker->ResolveMethodHandle(self, method_handle_index, referrer); } -static inline mirror::MethodType* ResolveMethodType(Thread* self, - uint32_t method_type_index, - ArtMethod* referrer) +static inline ObjPtr ResolveMethodType(Thread* self, + uint32_t method_type_index, + ArtMethod* referrer) REQUIRES_SHARED(Locks::mutator_lock_) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); return class_linker->ResolveMethodType(self, method_type_index, referrer); @@ -348,9 +349,7 @@ static inline ObjPtr ResolveString(Thread* self, if (UNLIKELY(string_ptr == nullptr)) { StackHandleScope<1> hs(self); Handle dex_cache(hs.NewHandle(method->GetDexCache())); - string_ptr = Runtime::Current()->GetClassLinker()->ResolveString(*dex_cache->GetDexFile(), - string_idx, - dex_cache); + string_ptr = Runtime::Current()->GetClassLinker()->ResolveString(string_idx, dex_cache); } return string_ptr; } diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index 094f08664e0c1d7b58727e66c98051e912122300..81c1e1deeb339e3ad8d3a5f8e782dfc87f865b96 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -67,7 +67,7 @@ namespace interpreter { { \ if (UNLIKELY(instrumentation->HasDexPcListeners()) && \ UNLIKELY(!DoDexPcMoveEvent(self, \ - code_item, \ + accessor, \ shadow_frame, \ dex_pc, \ instrumentation, \ @@ -125,7 +125,7 @@ namespace interpreter { // jvmti-agents while handling breakpoint or single step events. We had to move this into its own // function because it was making ExecuteSwitchImpl have too large a stack. NO_INLINE static bool DoDexPcMoveEvent(Thread* self, - const DexFile::CodeItem* code_item, + const CodeItemDataAccessor& accessor, const ShadowFrame& shadow_frame, uint32_t dex_pc, const instrumentation::Instrumentation* instrumentation, @@ -139,7 +139,7 @@ NO_INLINE static bool DoDexPcMoveEvent(Thread* self, hs.NewHandleWrapper(LIKELY(save_ref == nullptr) ? &null_obj : save_ref->GetGCRoot())); self->ClearException(); instrumentation->DexPcMovedEvent(self, - shadow_frame.GetThisObject(code_item->ins_size_), + shadow_frame.GetThisObject(accessor.InsSize()), shadow_frame.GetMethod(), dex_pc); if (UNLIKELY(self->IsExceptionPending())) { @@ -188,7 +188,7 @@ NO_INLINE static bool SendMethodExitEvents(Thread* self, } template -JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, +JValue ExecuteSwitchImpl(Thread* self, const CodeItemDataAccessor& accessor, ShadowFrame& shadow_frame, JValue result_register, bool interpret_one_instruction) { constexpr bool do_assignability_check = do_access_check; @@ -200,10 +200,10 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, uint32_t dex_pc = shadow_frame.GetDexPC(); const auto* const instrumentation = Runtime::Current()->GetInstrumentation(); - const uint16_t* const insns = code_item->insns_; + ArtMethod* method = shadow_frame.GetMethod(); + const uint16_t* const insns = accessor.Insns(); const Instruction* inst = Instruction::At(insns + dex_pc); uint16_t inst_data; - ArtMethod* method = shadow_frame.GetMethod(); jit::Jit* jit = Runtime::Current()->GetJit(); do { @@ -303,7 +303,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, !SendMethodExitEvents(self, instrumentation, shadow_frame, - shadow_frame.GetThisObject(code_item->ins_size_), + shadow_frame.GetThisObject(accessor.InsSize()), shadow_frame.GetMethod(), inst->GetDexPc(insns), result))) { @@ -325,7 +325,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, !SendMethodExitEvents(self, instrumentation, shadow_frame, - shadow_frame.GetThisObject(code_item->ins_size_), + shadow_frame.GetThisObject(accessor.InsSize()), shadow_frame.GetMethod(), inst->GetDexPc(insns), result))) { @@ -348,7 +348,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, !SendMethodExitEvents(self, instrumentation, shadow_frame, - shadow_frame.GetThisObject(code_item->ins_size_), + shadow_frame.GetThisObject(accessor.InsSize()), shadow_frame.GetMethod(), inst->GetDexPc(insns), result))) { @@ -370,7 +370,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, !SendMethodExitEvents(self, instrumentation, shadow_frame, - shadow_frame.GetThisObject(code_item->ins_size_), + shadow_frame.GetThisObject(accessor.InsSize()), shadow_frame.GetMethod(), inst->GetDexPc(insns), result))) { @@ -412,7 +412,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, !SendMethodExitEvents(self, instrumentation, shadow_frame, - shadow_frame.GetThisObject(code_item->ins_size_), + shadow_frame.GetThisObject(accessor.InsSize()), shadow_frame.GetMethod(), inst->GetDexPc(insns), result))) { @@ -2484,19 +2484,19 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, // Explicit definitions of ExecuteSwitchImpl. template HOT_ATTR -JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, +JValue ExecuteSwitchImpl(Thread* self, const CodeItemDataAccessor& accessor, ShadowFrame& shadow_frame, JValue result_register, bool interpret_one_instruction); template HOT_ATTR -JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, +JValue ExecuteSwitchImpl(Thread* self, const CodeItemDataAccessor& accessor, ShadowFrame& shadow_frame, JValue result_register, bool interpret_one_instruction); template -JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, +JValue ExecuteSwitchImpl(Thread* self, const CodeItemDataAccessor& accessor, ShadowFrame& shadow_frame, JValue result_register, bool interpret_one_instruction); template -JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, +JValue ExecuteSwitchImpl(Thread* self, const CodeItemDataAccessor& accessor, ShadowFrame& shadow_frame, JValue result_register, bool interpret_one_instruction); diff --git a/runtime/interpreter/interpreter_switch_impl.h b/runtime/interpreter/interpreter_switch_impl.h index 267df2e219b772e3fcc4b04b33dbc2aa7a5ff276..aa0f854bb7fe86801b0b669a98c19878a4354b4e 100644 --- a/runtime/interpreter/interpreter_switch_impl.h +++ b/runtime/interpreter/interpreter_switch_impl.h @@ -25,6 +25,7 @@ namespace art { +class CodeItemDataAccessor; class ShadowFrame; class Thread; @@ -32,7 +33,7 @@ namespace interpreter { template JValue ExecuteSwitchImpl(Thread* self, - const DexFile::CodeItem* code_item, + const CodeItemDataAccessor& accessor, ShadowFrame& shadow_frame, JValue result_register, bool interpret_one_instruction) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/interpreter/mterp/arm/entry.S b/runtime/interpreter/mterp/arm/entry.S index de617a90d7f1a2aa7f4fd399b600a964c472902c..df4bcc66f38e38202df327daecbe18b5e943791a 100644 --- a/runtime/interpreter/mterp/arm/entry.S +++ b/runtime/interpreter/mterp/arm/entry.S @@ -23,7 +23,7 @@ /* * On entry: * r0 Thread* self/ - * r1 code_item + * r1 insns_ * r2 ShadowFrame * r3 JValue* result_register * @@ -56,6 +56,7 @@ ENTRY ExecuteMterpImpl VREG_INDEX_TO_ADDR rREFS, r0 @ point to reference array in shadow frame ldr r0, [r2, #SHADOWFRAME_DEX_PC_OFFSET] @ Get starting dex_pc. add rPC, r1, r0, lsl #1 @ Create direct pointer to 1st dex opcode + .cfi_register DPC_PSEUDO_REG, rPC EXPORT_PC /* Starting ibase */ diff --git a/runtime/interpreter/mterp/arm/header.S b/runtime/interpreter/mterp/arm/header.S index 51c2ba4c03f2b7bed07114556519e3b2bdbe7e06..64ab9efa19dc4372da8c2b21c2026c68f46af7e5 100644 --- a/runtime/interpreter/mterp/arm/header.S +++ b/runtime/interpreter/mterp/arm/header.S @@ -85,6 +85,7 @@ unspecified registers or condition codes. * to expand the macros into assembler assignment statements. */ #include "asm_support.h" +#include "interpreter/mterp/cfi_asm_support.h" #define MTERP_PROFILE_BRANCHES 1 #define MTERP_LOGGING 0 diff --git a/runtime/interpreter/mterp/arm64/entry.S b/runtime/interpreter/mterp/arm64/entry.S index f3d40ff6f79b0e22afcce1a251472b83766087b1..8d61210be84e47c2fba76c59fa01657b108a312c 100644 --- a/runtime/interpreter/mterp/arm64/entry.S +++ b/runtime/interpreter/mterp/arm64/entry.S @@ -20,7 +20,7 @@ * Interpreter entry point. * On entry: * x0 Thread* self/ - * x1 code_item + * x1 insns_ * x2 ShadowFrame * x3 JValue* result_register * @@ -46,6 +46,7 @@ ENTRY ExecuteMterpImpl add xREFS, xFP, w0, lsl #2 // point to reference array in shadow frame ldr w0, [x2, #SHADOWFRAME_DEX_PC_OFFSET] // Get starting dex_pc. add xPC, x1, w0, lsl #1 // Create direct pointer to 1st dex opcode + .cfi_register DPC_PSEUDO_REG, xPC EXPORT_PC /* Starting ibase */ diff --git a/runtime/interpreter/mterp/arm64/header.S b/runtime/interpreter/mterp/arm64/header.S index 47f12d2f5de0103040a56f49565c8bd1eee583bf..9261b770d621cbf3cb0a84fe6863163b282a6979 100644 --- a/runtime/interpreter/mterp/arm64/header.S +++ b/runtime/interpreter/mterp/arm64/header.S @@ -87,6 +87,7 @@ codes. * to expand the macros into assembler assignment statements. */ #include "asm_support.h" +#include "interpreter/mterp/cfi_asm_support.h" #define MTERP_PROFILE_BRANCHES 1 #define MTERP_LOGGING 0 diff --git a/runtime/interpreter/mterp/cfi_asm_support.h b/runtime/interpreter/mterp/cfi_asm_support.h new file mode 100644 index 0000000000000000000000000000000000000000..a97e153993b98507553490bba3a867b62a403d24 --- /dev/null +++ b/runtime/interpreter/mterp/cfi_asm_support.h @@ -0,0 +1,31 @@ +/* + * 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_RUNTIME_INTERPRETER_MTERP_CFI_ASM_SUPPORT_H_ +#define ART_RUNTIME_INTERPRETER_MTERP_CFI_ASM_SUPPORT_H_ + +/* + * To keep track of the Dalvik PC, give assign it a magic register number that + * won't be confused with a pysical register. Then, standard .cfi directives + * will track the location of it so that it may be extracted during a stack + * unwind. + * + * The Dalvik PC will be in either a physical registor, or the frame. + * Encoded from the ASCII string " DEX" -> 0x20 0x44 0x45 0x58 + */ +#define DPC_PSEUDO_REG 0x20444558 + +#endif // ART_RUNTIME_INTERPRETER_MTERP_CFI_ASM_SUPPORT_H_ diff --git a/runtime/interpreter/mterp/mips/entry.S b/runtime/interpreter/mterp/mips/entry.S index 03de985cd06797022a84d6668a66ed25a8f0a452..41b5d5650d0f8e0cc16c976480ea4334a450488f 100644 --- a/runtime/interpreter/mterp/mips/entry.S +++ b/runtime/interpreter/mterp/mips/entry.S @@ -32,6 +32,7 @@ */ ExecuteMterpImpl: + .cfi_startproc .set noreorder .cpload t9 .set reorder @@ -53,6 +54,7 @@ ExecuteMterpImpl: EAS2(rREFS, rFP, a0) # point to reference array in shadow frame lw a0, SHADOWFRAME_DEX_PC_OFFSET(a2) # Get starting dex_pc EAS1(rPC, a1, a0) # Create direct pointer to 1st dex opcode + .cfi_register DPC_PSEUDO_REG, rPC EXPORT_PC() diff --git a/runtime/interpreter/mterp/mips/footer.S b/runtime/interpreter/mterp/mips/footer.S index 6e1ba1c882420ec12f31b36d858f3d05981d78d9..1c784ef188c564748f782e32da03481420c748d1 100644 --- a/runtime/interpreter/mterp/mips/footer.S +++ b/runtime/interpreter/mterp/mips/footer.S @@ -284,4 +284,5 @@ MterpProfileActive: STACK_LOAD_FULL() jalr zero, ra + .cfi_endproc .end ExecuteMterpImpl diff --git a/runtime/interpreter/mterp/mips/header.S b/runtime/interpreter/mterp/mips/header.S index e4552ddf3d0bea7e488134f04a4389ecfa25c839..0f7a6f1116dcfb969b12bd918cd835fc28da9654 100644 --- a/runtime/interpreter/mterp/mips/header.S +++ b/runtime/interpreter/mterp/mips/header.S @@ -32,6 +32,7 @@ */ #include "asm_support.h" +#include "interpreter/mterp/cfi_asm_support.h" #if (__mips==32) && (__mips_isa_rev>=2) #define MIPS32REVGE2 /* mips32r2 and greater */ diff --git a/runtime/interpreter/mterp/mips64/entry.S b/runtime/interpreter/mterp/mips64/entry.S index 436b88dbd0fef68f5e77edf9c9bd303cf63b144d..841a81756901654bf94efe032721d39bf73fef54 100644 --- a/runtime/interpreter/mterp/mips64/entry.S +++ b/runtime/interpreter/mterp/mips64/entry.S @@ -73,6 +73,7 @@ ExecuteMterpImpl: dlsa rREFS, v0, rFP, 2 lw v0, SHADOWFRAME_DEX_PC_OFFSET(a2) dlsa rPC, v0, a1, 1 + .cfi_register DPC_PSEUDO_REG, rPC EXPORT_PC /* Starting ibase */ diff --git a/runtime/interpreter/mterp/mips64/header.S b/runtime/interpreter/mterp/mips64/header.S index d1acefd338a844b6b180ef8311cb24e9b0757025..2b550cb53353aa060c626bef5dacdf2cb5eceefb 100644 --- a/runtime/interpreter/mterp/mips64/header.S +++ b/runtime/interpreter/mterp/mips64/header.S @@ -102,6 +102,7 @@ The following registers have fixed assignments: * to expand the macros into assembler assignment statements. */ #include "asm_support.h" +#include "interpreter/mterp/cfi_asm_support.h" /* * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So, diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc index 92dd19ed2f94f5f2c3cc9ad7e39389a19f5d89f6..9c7645af9efde13d9a56732ed348766b6b5ba2dc 100644 --- a/runtime/interpreter/mterp/mterp.cc +++ b/runtime/interpreter/mterp/mterp.cc @@ -151,8 +151,14 @@ extern "C" size_t MterpShouldSwitchInterpreters() Dbg::IsDebuggerActive() || // An async exception has been thrown. We need to go to the switch interpreter. MTerp doesn't // know how to deal with these so we could end up never dealing with it if we are in an - // infinite loop. - UNLIKELY(Thread::Current()->IsAsyncExceptionPending()); + // infinite loop. Since this can be called in a tight loop and getting the current thread + // requires a TLS read we instead first check a short-circuit runtime flag that will only be + // set if something tries to set an async exception. This will make this function faster in + // the common case where no async exception has ever been sent. We don't need to worry about + // synchronization on the runtime flag since it is only set in a checkpoint which will either + // take place on the current thread or act as a synchronization point. + (UNLIKELY(runtime->AreAsyncExceptionsThrown()) && + Thread::Current()->IsAsyncExceptionPending()); } @@ -370,15 +376,15 @@ extern "C" size_t MterpConstClass(uint32_t index, ShadowFrame* shadow_frame, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - mirror::Class* c = ResolveVerifyAndClinit(dex::TypeIndex(index), - shadow_frame->GetMethod(), - self, - false, - false); + ObjPtr c = ResolveVerifyAndClinit(dex::TypeIndex(index), + shadow_frame->GetMethod(), + self, + /* can_run_clinit */ false, + /* verify_access */ false); if (UNLIKELY(c == nullptr)) { return true; } - shadow_frame->SetVRegReference(tgt_vreg, c); + shadow_frame->SetVRegReference(tgt_vreg, c.Ptr()); return false; } @@ -457,17 +463,17 @@ extern "C" size_t MterpNewInstance(ShadowFrame* shadow_frame, Thread* self, uint REQUIRES_SHARED(Locks::mutator_lock_) { const Instruction* inst = Instruction::At(shadow_frame->GetDexPCPtr()); mirror::Object* obj = nullptr; - mirror::Class* c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()), - shadow_frame->GetMethod(), - self, - false, - false); + ObjPtr c = ResolveVerifyAndClinit(dex::TypeIndex(inst->VRegB_21c()), + shadow_frame->GetMethod(), + self, + /* can_run_clinit */ false, + /* verify_access */ false); if (LIKELY(c != nullptr)) { if (UNLIKELY(c->IsStringClass())) { gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator(); obj = mirror::String::AllocEmptyString(self, allocator_type); } else { - obj = AllocObjectFromCode(c, + obj = AllocObjectFromCode(c.Ptr(), self, Runtime::Current()->GetHeap()->GetCurrentAllocator()); } diff --git a/runtime/interpreter/mterp/out/mterp_arm.S b/runtime/interpreter/mterp/out/mterp_arm.S index 69d7edbe8a7f8d7acf4861c35747aa86feeb9df7..f3c1124ec46545a950964baa3b411f933596947a 100644 --- a/runtime/interpreter/mterp/out/mterp_arm.S +++ b/runtime/interpreter/mterp/out/mterp_arm.S @@ -92,6 +92,7 @@ unspecified registers or condition codes. * to expand the macros into assembler assignment statements. */ #include "asm_support.h" +#include "interpreter/mterp/cfi_asm_support.h" #define MTERP_PROFILE_BRANCHES 1 #define MTERP_LOGGING 0 @@ -341,7 +342,7 @@ unspecified registers or condition codes. /* * On entry: * r0 Thread* self/ - * r1 code_item + * r1 insns_ * r2 ShadowFrame * r3 JValue* result_register * @@ -374,6 +375,7 @@ ENTRY ExecuteMterpImpl VREG_INDEX_TO_ADDR rREFS, r0 @ point to reference array in shadow frame ldr r0, [r2, #SHADOWFRAME_DEX_PC_OFFSET] @ Get starting dex_pc. add rPC, r1, r0, lsl #1 @ Create direct pointer to 1st dex opcode + .cfi_register DPC_PSEUDO_REG, rPC EXPORT_PC /* Starting ibase */ diff --git a/runtime/interpreter/mterp/out/mterp_arm64.S b/runtime/interpreter/mterp/out/mterp_arm64.S index 82edab465ef228cfe884a4261d45c9435def8715..347d54f705597afef73e16367db3bc3af25013e7 100644 --- a/runtime/interpreter/mterp/out/mterp_arm64.S +++ b/runtime/interpreter/mterp/out/mterp_arm64.S @@ -94,6 +94,7 @@ codes. * to expand the macros into assembler assignment statements. */ #include "asm_support.h" +#include "interpreter/mterp/cfi_asm_support.h" #define MTERP_PROFILE_BRANCHES 1 #define MTERP_LOGGING 0 @@ -378,7 +379,7 @@ codes. * Interpreter entry point. * On entry: * x0 Thread* self/ - * x1 code_item + * x1 insns_ * x2 ShadowFrame * x3 JValue* result_register * @@ -404,6 +405,7 @@ ENTRY ExecuteMterpImpl add xREFS, xFP, w0, lsl #2 // point to reference array in shadow frame ldr w0, [x2, #SHADOWFRAME_DEX_PC_OFFSET] // Get starting dex_pc. add xPC, x1, w0, lsl #1 // Create direct pointer to 1st dex opcode + .cfi_register DPC_PSEUDO_REG, xPC EXPORT_PC /* Starting ibase */ diff --git a/runtime/interpreter/mterp/out/mterp_mips.S b/runtime/interpreter/mterp/out/mterp_mips.S index 8cc1b191283876c39cac09b89176b1fe004668ea..1687afa58a545a6fe1e2c331874f75fd2bc827de 100644 --- a/runtime/interpreter/mterp/out/mterp_mips.S +++ b/runtime/interpreter/mterp/out/mterp_mips.S @@ -39,6 +39,7 @@ */ #include "asm_support.h" +#include "interpreter/mterp/cfi_asm_support.h" #if (__mips==32) && (__mips_isa_rev>=2) #define MIPS32REVGE2 /* mips32r2 and greater */ @@ -765,6 +766,7 @@ */ ExecuteMterpImpl: + .cfi_startproc .set noreorder .cpload t9 .set reorder @@ -786,6 +788,7 @@ ExecuteMterpImpl: EAS2(rREFS, rFP, a0) # point to reference array in shadow frame lw a0, SHADOWFRAME_DEX_PC_OFFSET(a2) # Get starting dex_pc EAS1(rPC, a1, a0) # Create direct pointer to 1st dex opcode + .cfi_register DPC_PSEUDO_REG, rPC EXPORT_PC() @@ -12842,5 +12845,6 @@ MterpProfileActive: STACK_LOAD_FULL() jalr zero, ra + .cfi_endproc .end ExecuteMterpImpl diff --git a/runtime/interpreter/mterp/out/mterp_mips64.S b/runtime/interpreter/mterp/out/mterp_mips64.S index 139ee2590402cdcf5015495bcaaebf793d0fbef0..559c72bb0c55a525ff93258b46683260fb65de45 100644 --- a/runtime/interpreter/mterp/out/mterp_mips64.S +++ b/runtime/interpreter/mterp/out/mterp_mips64.S @@ -109,6 +109,7 @@ The following registers have fixed assignments: * to expand the macros into assembler assignment statements. */ #include "asm_support.h" +#include "interpreter/mterp/cfi_asm_support.h" /* * Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So, @@ -407,6 +408,7 @@ ExecuteMterpImpl: dlsa rREFS, v0, rFP, 2 lw v0, SHADOWFRAME_DEX_PC_OFFSET(a2) dlsa rPC, v0, a1, 1 + .cfi_register DPC_PSEUDO_REG, rPC EXPORT_PC /* Starting ibase */ diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S index cbab61ebf6a0d4c2394a7ca532ea1e822b5911fb..0613c9d12e845fb7e84b34a21cd70bb7b679e122 100644 --- a/runtime/interpreter/mterp/out/mterp_x86.S +++ b/runtime/interpreter/mterp/out/mterp_x86.S @@ -95,6 +95,7 @@ unspecified registers or condition codes. * to expand the macros into assembler assignment statements. */ #include "asm_support.h" +#include "interpreter/mterp/cfi_asm_support.h" /* * Handle mac compiler specific @@ -342,7 +343,7 @@ unspecified registers or condition codes. /* * On entry: * 0 Thread* self - * 1 code_item + * 1 insns_ * 2 ShadowFrame * 3 JValue* result_register * @@ -379,6 +380,7 @@ SYMBOL(ExecuteMterpImpl): leal (rFP, %eax, 4), rREFS movl SHADOWFRAME_DEX_PC_OFFSET(%edx), %eax lea (%ecx, %eax, 2), rPC + .cfi_register DPC_PSEUDO_REG, rPC EXPORT_PC /* Set up for backwards branches & osr profiling */ diff --git a/runtime/interpreter/mterp/out/mterp_x86_64.S b/runtime/interpreter/mterp/out/mterp_x86_64.S index 83c3e4fb914719ea42bd24aeb9a3e1883838cada..aa91db3b61f182bbde727ca4913116048fc21283 100644 --- a/runtime/interpreter/mterp/out/mterp_x86_64.S +++ b/runtime/interpreter/mterp/out/mterp_x86_64.S @@ -91,6 +91,7 @@ unspecified registers or condition codes. * to expand the macros into assembler assignment statements. */ #include "asm_support.h" +#include "interpreter/mterp/cfi_asm_support.h" /* * Handle mac compiler specific @@ -328,7 +329,7 @@ unspecified registers or condition codes. /* * On entry: * 0 Thread* self - * 1 code_item + * 1 insns_ * 2 ShadowFrame * 3 JValue* result_register * @@ -362,6 +363,7 @@ SYMBOL(ExecuteMterpImpl): leaq (rFP, %rax, 4), rREFS movl SHADOWFRAME_DEX_PC_OFFSET(IN_ARG2), %eax leaq (IN_ARG1, %rax, 2), rPC + .cfi_register DPC_PSEUDO_REG, rPC EXPORT_PC /* Starting ibase */ diff --git a/runtime/interpreter/mterp/x86/entry.S b/runtime/interpreter/mterp/x86/entry.S index 055e834fedbd6a2d5b5d51c1868d40e9a3d52cd2..10ca8366de6e4637997890f68a5d9b713b3690ff 100644 --- a/runtime/interpreter/mterp/x86/entry.S +++ b/runtime/interpreter/mterp/x86/entry.S @@ -24,7 +24,7 @@ /* * On entry: * 0 Thread* self - * 1 code_item + * 1 insns_ * 2 ShadowFrame * 3 JValue* result_register * @@ -61,6 +61,7 @@ SYMBOL(ExecuteMterpImpl): leal (rFP, %eax, 4), rREFS movl SHADOWFRAME_DEX_PC_OFFSET(%edx), %eax lea (%ecx, %eax, 2), rPC + .cfi_register DPC_PSEUDO_REG, rPC EXPORT_PC /* Set up for backwards branches & osr profiling */ diff --git a/runtime/interpreter/mterp/x86/header.S b/runtime/interpreter/mterp/x86/header.S index 370012f324e9a74cc7caee94dc7e186ac79c2f77..0e585e86f0f28bb4a0eeded9f584f0adec2bd0ff 100644 --- a/runtime/interpreter/mterp/x86/header.S +++ b/runtime/interpreter/mterp/x86/header.S @@ -88,6 +88,7 @@ unspecified registers or condition codes. * to expand the macros into assembler assignment statements. */ #include "asm_support.h" +#include "interpreter/mterp/cfi_asm_support.h" /* * Handle mac compiler specific diff --git a/runtime/interpreter/mterp/x86_64/entry.S b/runtime/interpreter/mterp/x86_64/entry.S index 83b845b702c94258262b790cdac574907e01d17e..d85ef7fe24e3c3d127c17ef3c3506ecf1aedb19b 100644 --- a/runtime/interpreter/mterp/x86_64/entry.S +++ b/runtime/interpreter/mterp/x86_64/entry.S @@ -24,7 +24,7 @@ /* * On entry: * 0 Thread* self - * 1 code_item + * 1 insns_ * 2 ShadowFrame * 3 JValue* result_register * @@ -58,6 +58,7 @@ SYMBOL(ExecuteMterpImpl): leaq (rFP, %rax, 4), rREFS movl SHADOWFRAME_DEX_PC_OFFSET(IN_ARG2), %eax leaq (IN_ARG1, %rax, 2), rPC + .cfi_register DPC_PSEUDO_REG, rPC EXPORT_PC /* Starting ibase */ diff --git a/runtime/interpreter/mterp/x86_64/header.S b/runtime/interpreter/mterp/x86_64/header.S index 9d21f3f1a12255735fe0a8c600703f0a284ebba9..a3ef8953caa67a76a075043c8c451041b7a8faee 100644 --- a/runtime/interpreter/mterp/x86_64/header.S +++ b/runtime/interpreter/mterp/x86_64/header.S @@ -84,6 +84,7 @@ unspecified registers or condition codes. * to expand the macros into assembler assignment statements. */ #include "asm_support.h" +#include "interpreter/mterp/cfi_asm_support.h" /* * Handle mac compiler specific diff --git a/runtime/interpreter/shadow_frame.cc b/runtime/interpreter/shadow_frame.cc index ab154cf76769fff94ca4e4308b9e45d505e91b3f..fe7e3e0a9b64d684a754b9cd9be9c59beef89d88 100644 --- a/runtime/interpreter/shadow_frame.cc +++ b/runtime/interpreter/shadow_frame.cc @@ -27,9 +27,9 @@ mirror::Object* ShadowFrame::GetThisObject() const { } else if (m->IsNative()) { return GetVRegReference(0); } else { - const DexFile::CodeItem* code_item = m->GetCodeItem(); - CHECK(code_item != nullptr) << ArtMethod::PrettyMethod(m); - uint16_t reg = code_item->registers_size_ - code_item->ins_size_; + CHECK(m->GetCodeItem() != nullptr) << ArtMethod::PrettyMethod(m); + CodeItemDataAccessor accessor(m); + uint16_t reg = accessor.RegistersSize() - accessor.InsSize(); return GetVRegReference(reg); } } diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 31e798677070bc9ab6363b276133608c511b2d1d..d1436fa9cfe2b833a726bbf074e4502755619308 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -26,12 +26,12 @@ #include #include -#include "android-base/stringprintf.h" +#include +#include #include "art_method-inl.h" #include "base/casts.h" #include "base/enums.h" -#include "base/logging.h" #include "base/macros.h" #include "class_linker.h" #include "common_throws.h" @@ -1910,7 +1910,7 @@ void UnstartedRuntime::Initialize() { tables_initialized_ = true; } -void UnstartedRuntime::Invoke(Thread* self, const DexFile::CodeItem* code_item, +void UnstartedRuntime::Invoke(Thread* self, const CodeItemDataAccessor& accessor, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) { // In a runtime that's not started we intercept certain methods to avoid complicated dependency // problems in core libraries. @@ -1930,7 +1930,7 @@ void UnstartedRuntime::Invoke(Thread* self, const DexFile::CodeItem* code_item, self->PopShadowFrame(); } else { // Not special, continue with regular interpreter execution. - ArtInterpreterToInterpreterBridge(self, code_item, shadow_frame, result); + ArtInterpreterToInterpreterBridge(self, accessor, shadow_frame, result); } } diff --git a/runtime/interpreter/unstarted_runtime.h b/runtime/interpreter/unstarted_runtime.h index bc9ead8360af641abd69c275d3bfa00e99793afd..2e86dea1a9804aa8092cebf7fc00357829cbcb20 100644 --- a/runtime/interpreter/unstarted_runtime.h +++ b/runtime/interpreter/unstarted_runtime.h @@ -25,6 +25,7 @@ namespace art { class ArtMethod; +class CodeItemDataAccessor; class Thread; class ShadowFrame; @@ -48,7 +49,7 @@ class UnstartedRuntime { static void Initialize(); static void Invoke(Thread* self, - const DexFile::CodeItem* code_item, + const CodeItemDataAccessor& accessor, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc index f8b82ed313435881ba91fb3ebf6e4b87a2f35ef1..104fb66ed88dbecf0a4d56b5444b723bcbc586d0 100644 --- a/runtime/java_vm_ext.cc +++ b/runtime/java_vm_ext.cc @@ -337,7 +337,7 @@ class Libraries { } else { VLOG(jni) << "[JNI_OnUnload found for \"" << library->GetPath() << "\"]: Calling..."; JNI_OnUnloadFn jni_on_unload = reinterpret_cast(sym); - jni_on_unload(self->GetJniEnv()->vm, nullptr); + jni_on_unload(self->GetJniEnv()->GetVm(), nullptr); } delete library; } diff --git a/runtime/jdwp/jdwp.h b/runtime/jdwp/jdwp.h index aeeda1e7912030bc686a61199fdbc3ae6787e8a8..b491c3ee5c614c4b129081672b871686fa5ba296 100644 --- a/runtime/jdwp/jdwp.h +++ b/runtime/jdwp/jdwp.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_JDWP_JDWP_H_ #include "atomic.h" +#include "base/logging.h" // For VLOG. #include "base/mutex.h" #include "jdwp/jdwp_bits.h" #include "jdwp/jdwp_constants.h" @@ -97,14 +98,15 @@ bool operator!=(const JdwpLocation& lhs, const JdwpLocation& rhs); * How we talk to the debugger. */ enum JdwpTransportType { - kJdwpTransportUnknown = 0, + kJdwpTransportNone = 0, + kJdwpTransportUnknown, // Unknown tranpsort kJdwpTransportSocket, // transport=dt_socket kJdwpTransportAndroidAdb, // transport=dt_android_adb }; std::ostream& operator<<(std::ostream& os, const JdwpTransportType& rhs); struct JdwpOptions { - JdwpTransportType transport = kJdwpTransportUnknown; + JdwpTransportType transport = kJdwpTransportNone; bool server = false; bool suspend = false; std::string host = ""; @@ -113,6 +115,8 @@ struct JdwpOptions { bool operator==(const JdwpOptions& lhs, const JdwpOptions& rhs); +bool ParseJdwpOptions(const std::string& options, JdwpOptions* jdwp_options); + struct JdwpEvent; class JdwpNetStateBase; struct ModBasket; diff --git a/runtime/jdwp/jdwp_adb.cc b/runtime/jdwp/jdwp_adb.cc index ede4f9edb7c75c311962c8ec87671e29969ac339..d68430f3ac3b095faa93a24e5b368a5b431ff703 100644 --- a/runtime/jdwp/jdwp_adb.cc +++ b/runtime/jdwp/jdwp_adb.cc @@ -22,7 +22,7 @@ #include "android-base/stringprintf.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "jdwp/jdwp_priv.h" #include "thread-current-inl.h" diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc index 41cb64276c992dba12819677e05c96ddbfe6df57..9409b7661f29d78100c16f32846c8c73e72493c9 100644 --- a/runtime/jdwp/jdwp_event.cc +++ b/runtime/jdwp/jdwp_event.cc @@ -25,7 +25,7 @@ #include "art_field-inl.h" #include "art_method-inl.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "debugger.h" #include "jdwp/jdwp_constants.h" #include "jdwp/jdwp_expand_buf.h" diff --git a/runtime/jdwp/jdwp_expand_buf.cc b/runtime/jdwp/jdwp_expand_buf.cc index f0b8c918dc08c9c3645410b41bdf66cd2349b558..4b4ca0e4a30c40a9539ccf59f1b120abfd91d2f7 100644 --- a/runtime/jdwp/jdwp_expand_buf.cc +++ b/runtime/jdwp/jdwp_expand_buf.cc @@ -23,7 +23,8 @@ #include #include -#include "base/logging.h" +#include + #include "jdwp/jdwp.h" #include "jdwp/jdwp_bits.h" diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc index 618332b7ef02c6e419bb3e4eb43e54d2d3236f44..89eef88b88446fb79dec0919e8afffcca9666e12 100644 --- a/runtime/jdwp/jdwp_handler.cc +++ b/runtime/jdwp/jdwp_handler.cc @@ -24,7 +24,7 @@ #include "atomic.h" #include "base/hex_dump.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/macros.h" #include "debugger.h" #include "jdwp/jdwp_constants.h" diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc index e6c60685cc631280e5ee3d448718c8bfb73b88aa..63f5dc8b691465a8ae59c2e84a67574a891beac4 100644 --- a/runtime/jdwp/jdwp_main.cc +++ b/runtime/jdwp/jdwp_main.cc @@ -23,7 +23,7 @@ #include "android-base/stringprintf.h" #include "atomic.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/time_utils.h" #include "debugger.h" #include "jdwp/jdwp_priv.h" @@ -37,6 +37,119 @@ using android::base::StringPrintf; static void* StartJdwpThread(void* arg); + +static bool ParseJdwpOption(const std::string& name, + const std::string& value, + 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 { + jdwp_options->transport = JDWP::kJdwpTransportUnknown; + LOG(ERROR) << "JDWP transport not supported: " << value; + return false; + } + } else if (name == "server") { + if (value == "n") { + jdwp_options->server = false; + } else if (value == "y") { + jdwp_options->server = true; + } else { + LOG(ERROR) << "JDWP option 'server' must be 'y' or 'n'"; + return false; + } + } else if (name == "suspend") { + if (value == "n") { + jdwp_options->suspend = false; + } else if (value == "y") { + jdwp_options->suspend = true; + } else { + LOG(ERROR) << "JDWP option 'suspend' must be 'y' or 'n'"; + return false; + } + } else if (name == "address") { + /* this is either or : */ + 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()) { + LOG(ERROR) << "JDWP address missing port: " << value; + return false; + } + char* end; + uint64_t port = strtoul(port_string.c_str(), &end, 10); + if (*end != '\0' || port > 0xffff) { + LOG(ERROR) << "JDWP address has junk in port field: " << value; + return false; + } + jdwp_options->port = port; + } else if (name == "launch" || name == "onthrow" || name == "oncaught" || name == "timeout") { + /* valid but unsupported */ + LOG(INFO) << "Ignoring JDWP option '" << name << "'='" << value << "'"; + } else { + LOG(INFO) << "Ignoring unrecognized JDWP option '" << name << "'='" << value << "'"; + } + + return true; +} + +bool ParseJdwpOptions(const std::string& options, JdwpOptions* jdwp_options) { + VLOG(jdwp) << "ParseJdwpOptions: " << options; + + if (options == "help") { + LOG(ERROR) << "Example: -XjdwpOptions:transport=dt_socket,address=8000,server=y\n" + << "Example: -Xrunjdwp:transport=dt_socket,address=8000,server=y\n" + << "Example: -Xrunjdwp:transport=dt_socket,address=localhost:6500,server=n\n"; + return false; + } + + const std::string s; + + std::vector pairs; + Split(options, ',', &pairs); + + for (const std::string& jdwp_option : pairs) { + std::string::size_type equals_pos = jdwp_option.find('='); + if (equals_pos == std::string::npos) { + LOG(ERROR) << s << "Can't parse JDWP option '" << jdwp_option << "' in '" << options << "'"; + return false; + } + + bool parse_attempt = ParseJdwpOption(jdwp_option.substr(0, equals_pos), + jdwp_option.substr(equals_pos + 1), + jdwp_options); + if (!parse_attempt) { + // We fail to parse this JDWP option. + return parse_attempt; + } + } + + if (jdwp_options->transport == JDWP::kJdwpTransportUnknown) { + LOG(ERROR) << s << "Must specify JDWP transport: " << options; + return false; + } +#if ART_TARGET_ANDROID + if (jdwp_options->transport == JDWP::kJdwpTransportNone) { + jdwp_options->transport = JDWP::kJdwpTransportAndroidAdb; + LOG(WARNING) << "no JDWP transport specified. Defaulting to dt_android_adb"; + } +#endif + if (!jdwp_options->server && (jdwp_options->host.empty() || jdwp_options->port == 0)) { + LOG(ERROR) << s << "Must specify JDWP host and port when server=n: " << options; + return false; + } + + return true; +} + /* * JdwpNetStateBase class implementation */ diff --git a/runtime/jdwp/jdwp_options_test.cc b/runtime/jdwp/jdwp_options_test.cc new file mode 100644 index 0000000000000000000000000000000000000000..10c52e8cf8d32545573513651d80019256632889 --- /dev/null +++ b/runtime/jdwp/jdwp_options_test.cc @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2013 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 "jdwp.h" + +#include "gtest/gtest.h" + +namespace art { +namespace JDWP { + +TEST(JdwpOptionsTest, Options) { + { + /* + * "Example: -Xrunjdwp:transport=dt_socket,address=8000,server=y\n" + */ + JDWP::JdwpOptions opt = JDWP::JdwpOptions(); + const char *opt_args = "transport=dt_socket,address=8000,server=y"; + + EXPECT_TRUE(ParseJdwpOptions(opt_args, &opt)); + EXPECT_EQ(opt.transport, JdwpTransportType::kJdwpTransportSocket); + EXPECT_EQ(opt.port, 8000u); + EXPECT_EQ(opt.server, true); + EXPECT_EQ(opt.suspend, false); + } + + { + /* + * Example: transport=dt_socket,address=localhost:6500,server=n + */ + JDWP::JdwpOptions opt = JDWP::JdwpOptions(); + const char *opt_args = "transport=dt_socket,address=localhost:6500,server=y"; + + EXPECT_TRUE(ParseJdwpOptions(opt_args, &opt)); + EXPECT_EQ(opt.transport, JdwpTransportType::kJdwpTransportSocket); + EXPECT_EQ(opt.port, 6500u); + EXPECT_EQ(opt.host, "localhost"); + EXPECT_EQ(opt.server, true); + EXPECT_EQ(opt.suspend, false); + } + + { + /* + * Example: transport=dt_android_adb,server=n,suspend=y; + */ + JDWP::JdwpOptions opt = JDWP::JdwpOptions(); + const char *opt_args = "transport=dt_android_adb,server=y"; + + EXPECT_TRUE(ParseJdwpOptions(opt_args, &opt)); + EXPECT_EQ(opt.transport, JdwpTransportType::kJdwpTransportAndroidAdb); + EXPECT_EQ(opt.port, 0xFFFF); + EXPECT_EQ(opt.host, ""); + EXPECT_EQ(opt.server, true); + EXPECT_EQ(opt.suspend, false); + } + + /* + * Test failures + */ + JDWP::JdwpOptions opt = JDWP::JdwpOptions(); + EXPECT_FALSE(ParseJdwpOptions("help", &opt)); + EXPECT_FALSE(ParseJdwpOptions("blabla", &opt)); + EXPECT_FALSE(ParseJdwpOptions("transport=dt_android_adb,server=n", &opt)); +} + +} // namespace JDWP +} // namespace art diff --git a/runtime/jdwp/jdwp_socket.cc b/runtime/jdwp/jdwp_socket.cc index 97662f0727d1fb5af4c9410132f62db2a80c3a5e..673a942517878206e2012d4c0d15390cdacfa40b 100644 --- a/runtime/jdwp/jdwp_socket.cc +++ b/runtime/jdwp/jdwp_socket.cc @@ -28,7 +28,7 @@ #include "android-base/stringprintf.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "jdwp/jdwp_priv.h" namespace art { diff --git a/runtime/jdwp_provider.h b/runtime/jdwp_provider.h new file mode 100644 index 0000000000000000000000000000000000000000..b62e10b4f849a53193e46e1b53174bbd539b5f1c --- /dev/null +++ b/runtime/jdwp_provider.h @@ -0,0 +1,36 @@ +/* + * Copyright 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_RUNTIME_JDWP_PROVIDER_H_ +#define ART_RUNTIME_JDWP_PROVIDER_H_ + +#include + +#include "base/macros.h" +#include "base/logging.h" + +namespace art { + +enum class JdwpProvider { + kNone, + kInternal, + kAdbConnection, +}; + +std::ostream& operator<<(std::ostream& os, const JdwpProvider& rhs); + +} // namespace art +#endif // ART_RUNTIME_JDWP_PROVIDER_H_ diff --git a/runtime/jit/debugger_interface.cc b/runtime/jit/debugger_interface.cc index 135d9b1f51bdfca9334088a12d2e672ae0b4365a..4d1c85a1c2abdac3046062888b47d42568e35053 100644 --- a/runtime/jit/debugger_interface.cc +++ b/runtime/jit/debugger_interface.cc @@ -16,7 +16,8 @@ #include "debugger_interface.h" -#include "base/logging.h" +#include + #include "base/mutex.h" #include "thread-current-inl.h" #include "thread.h" diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 953e195480f0a48077a1ffe3f080ee6de3dafc28..12bf79d7ca898e9a4af3afbd4377ab6737a7df18 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -20,8 +20,9 @@ #include "art_method-inl.h" #include "base/enums.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/memory_tool.h" +#include "base/runtime_debug.h" #include "debugger.h" #include "entrypoints/runtime_asm_entrypoints.h" #include "interpreter/interpreter.h" @@ -466,7 +467,8 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread, // Fetch some data before looking up for an OSR method. We don't want thread // suspension once we hold an OSR method, as the JIT code cache could delete the OSR // method while we are being suspended. - const size_t number_of_vregs = method->GetCodeItem()->registers_size_; + CodeItemDataAccessor accessor(method); + const size_t number_of_vregs = accessor.RegistersSize(); const char* shorty = method->GetShorty(); std::string method_name(VLOG_IS_ON(jit) ? method->PrettyMethod() : ""); void** memory = nullptr; @@ -643,10 +645,14 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_ return; } - if (method->IsClassInitializer() || method->IsNative() || !method->IsCompilable()) { + if (method->IsClassInitializer() || !method->IsCompilable()) { // We do not want to compile such methods. return; } + if (hot_method_threshold_ == 0) { + // Tests might request JIT on first use (compiled synchronously in the interpreter). + return; + } DCHECK(thread_pool_ != nullptr); DCHECK_GT(warm_method_threshold_, 0); DCHECK_GT(hot_method_threshold_, warm_method_threshold_); @@ -659,7 +665,8 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_ count *= priority_thread_weight_; } int32_t new_count = starting_count + count; // int32 here to avoid wrap-around; - if (starting_count < warm_method_threshold_) { + // Note: Native method have no "warm" state or profiling info. + if (LIKELY(!method->IsNative()) && starting_count < warm_method_threshold_) { if ((new_count >= warm_method_threshold_) && (method->GetProfilingInfo(kRuntimePointerSize) == nullptr)) { bool success = ProfilingInfo::Create(self, method, /* retry_allocation */ false); @@ -696,6 +703,7 @@ void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_ // If the samples don't contain any back edge, we don't increment the hotness. return; } + DCHECK(!method->IsNative()); // No back edges reported for native methods. if ((new_count >= osr_method_threshold_) && !code_cache_->IsOsrCompiled(method)) { DCHECK(thread_pool_ != nullptr); thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr)); diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index e1807525ea26049815654ae36c808100974d901d..6f03a688e6c3f6ad8bdc44047cb3a4a19927f2fa 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -21,6 +21,7 @@ #include "arch/context.h" #include "art_method-inl.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" @@ -30,6 +31,7 @@ #include "entrypoints/runtime_asm_entrypoints.h" #include "gc/accounting/bitmap-inl.h" #include "gc/scoped_gc_critical_section.h" +#include "handle.h" #include "intern_table.h" #include "jit/jit.h" #include "jit/profiling_info.h" @@ -38,8 +40,10 @@ #include "oat_file-inl.h" #include "oat_quick_method_header.h" #include "object_callbacks.h" +#include "profile_compilation_info.h" #include "scoped_thread_state_change-inl.h" #include "stack.h" +#include "thread-current-inl.h" #include "thread_list.h" namespace art { @@ -52,6 +56,107 @@ static constexpr int kProtCode = PROT_READ | PROT_EXEC; static constexpr size_t kCodeSizeLogThreshold = 50 * KB; static constexpr size_t kStackMapSizeLogThreshold = 50 * KB; +class JitCodeCache::JniStubKey { + public: + explicit JniStubKey(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) + : shorty_(method->GetShorty()), + is_static_(method->IsStatic()), + is_fast_native_(method->IsFastNative()), + is_critical_native_(method->IsCriticalNative()), + is_synchronized_(method->IsSynchronized()) { + DCHECK(!(is_fast_native_ && is_critical_native_)); + } + + bool operator<(const JniStubKey& rhs) const { + if (is_static_ != rhs.is_static_) { + return rhs.is_static_; + } + if (is_synchronized_ != rhs.is_synchronized_) { + return rhs.is_synchronized_; + } + if (is_fast_native_ != rhs.is_fast_native_) { + return rhs.is_fast_native_; + } + if (is_critical_native_ != rhs.is_critical_native_) { + return rhs.is_critical_native_; + } + return strcmp(shorty_, rhs.shorty_) < 0; + } + + // Update the shorty to point to another method's shorty. Call this function when removing + // the method that references the old shorty from JniCodeData and not removing the entire + // JniCodeData; the old shorty may become a dangling pointer when that method is unloaded. + void UpdateShorty(ArtMethod* method) const REQUIRES_SHARED(Locks::mutator_lock_) { + const char* shorty = method->GetShorty(); + DCHECK_STREQ(shorty_, shorty); + shorty_ = shorty; + } + + private: + // The shorty points to a DexFile data and may need to change + // to point to the same shorty in a different DexFile. + mutable const char* shorty_; + + const bool is_static_; + const bool is_fast_native_; + const bool is_critical_native_; + const bool is_synchronized_; +}; + +class JitCodeCache::JniStubData { + public: + JniStubData() : code_(nullptr), methods_() {} + + void SetCode(const void* code) { + DCHECK(code != nullptr); + code_ = code; + } + + const void* GetCode() const { + return code_; + } + + bool IsCompiled() const { + return GetCode() != nullptr; + } + + void AddMethod(ArtMethod* method) { + if (!ContainsElement(methods_, method)) { + methods_.push_back(method); + } + } + + const std::vector& GetMethods() const { + return methods_; + } + + void RemoveMethodsIn(const LinearAlloc& alloc) { + auto kept_end = std::remove_if( + methods_.begin(), + methods_.end(), + [&alloc](ArtMethod* method) { return alloc.ContainsUnsafe(method); }); + methods_.erase(kept_end, methods_.end()); + } + + bool RemoveMethod(ArtMethod* method) { + auto it = std::find(methods_.begin(), methods_.end(), method); + if (it != methods_.end()) { + methods_.erase(it); + return true; + } else { + return false; + } + } + + void MoveObsoleteMethod(ArtMethod* old_method, ArtMethod* new_method) { + std::replace(methods_.begin(), methods_.end(), old_method, new_method); + } + + private: + const void* code_; + std::vector methods_; +}; + JitCodeCache* JitCodeCache::Create(size_t initial_capacity, size_t max_capacity, bool generate_debug_info, @@ -182,20 +287,44 @@ JitCodeCache::JitCodeCache(MemMap* code_map, << PrettySize(initial_code_capacity); } +JitCodeCache::~JitCodeCache() {} + bool JitCodeCache::ContainsPc(const void* ptr) const { return code_map_->Begin() <= ptr && ptr < code_map_->End(); } bool JitCodeCache::ContainsMethod(ArtMethod* method) { MutexLock mu(Thread::Current(), lock_); - for (auto& it : method_code_map_) { - if (it.second == method) { + if (UNLIKELY(method->IsNative())) { + auto it = jni_stubs_map_.find(JniStubKey(method)); + if (it != jni_stubs_map_.end() && + it->second.IsCompiled() && + ContainsElement(it->second.GetMethods(), method)) { return true; } + } else { + for (const auto& it : method_code_map_) { + if (it.second == method) { + return true; + } + } } return false; } +const void* JitCodeCache::GetJniStubCode(ArtMethod* method) { + DCHECK(method->IsNative()); + MutexLock mu(Thread::Current(), lock_); + auto it = jni_stubs_map_.find(JniStubKey(method)); + if (it != jni_stubs_map_.end()) { + JniStubData& data = it->second; + if (data.IsCompiled() && ContainsElement(data.GetMethods(), method)) { + return data.GetCode(); + } + } + return nullptr; +} + class ScopedCodeCacheWrite : ScopedTrace { public: explicit ScopedCodeCacheWrite(MemMap* code_map, bool only_for_tlb_shootdown = false) @@ -421,7 +550,9 @@ void JitCodeCache::FreeCode(const void* code_ptr) { // Notify native debugger that we are about to remove the code. // It does nothing if we are not using native debugger. DeleteJITCodeEntryForAddress(reinterpret_cast(code_ptr)); - FreeData(GetRootTable(code_ptr)); + if (OatQuickMethodHeader::FromCodePointer(code_ptr)->IsOptimized()) { + FreeData(GetRootTable(code_ptr)); + } // else this is a JNI stub without any data. FreeCode(reinterpret_cast(allocation)); } @@ -458,6 +589,16 @@ void JitCodeCache::RemoveMethodsIn(Thread* self, const LinearAlloc& alloc) { // lead to a deadlock. { ScopedCodeCacheWrite scc(code_map_.get()); + for (auto it = jni_stubs_map_.begin(); it != jni_stubs_map_.end();) { + it->second.RemoveMethodsIn(alloc); + if (it->second.GetMethods().empty()) { + method_headers.insert(OatQuickMethodHeader::FromCodePointer(it->second.GetCode())); + it = jni_stubs_map_.erase(it); + } else { + it->first.UpdateShorty(it->second.GetMethods().front()); + ++it; + } + } for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { if (alloc.ContainsUnsafe(it->second)) { method_headers.insert(OatQuickMethodHeader::FromCodePointer(it->first)); @@ -567,7 +708,8 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, bool has_should_deoptimize_flag, const ArenaSet& cha_single_implementation_list) { - DCHECK(stack_map != nullptr); + DCHECK_NE(stack_map != nullptr, method->IsNative()); + DCHECK(!method->IsNative() || !osr); size_t alignment = GetInstructionSetAlignment(kRuntimeISA); // Ensure the header ends up at expected instruction alignment. size_t header_size = RoundUp(sizeof(OatQuickMethodHeader), alignment); @@ -591,8 +733,8 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, std::copy(code, code + code_size, code_ptr); method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); new (method_header) OatQuickMethodHeader( - code_ptr - stack_map, - code_ptr - method_info, + (stack_map != nullptr) ? code_ptr - stack_map : 0u, + (method_info != nullptr) ? code_ptr - method_info : 0u, frame_size_in_bytes, core_spill_mask, fp_spill_mask, @@ -647,24 +789,40 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, // possible that the compiled code is considered invalidated by some class linking, // but below we still make the compiled code valid for the method. MutexLock mu(self, lock_); - // Fill the root table before updating the entry point. - DCHECK_EQ(FromStackMapToRoots(stack_map), roots_data); - DCHECK_LE(roots_data, stack_map); - FillRootTable(roots_data, roots); - { - // Flush data cache, as compiled code references literals in it. - // We also need a TLB shootdown to act as memory barrier across cores. - ScopedCodeCacheWrite ccw(code_map_.get(), /* only_for_tlb_shootdown */ true); - FlushDataCache(reinterpret_cast(roots_data), - reinterpret_cast(roots_data + data_size)); - } - method_code_map_.Put(code_ptr, method); - if (osr) { - number_of_osr_compilations_++; - osr_code_map_.Put(method, code_ptr); + if (UNLIKELY(method->IsNative())) { + DCHECK(stack_map == nullptr); + DCHECK(roots_data == nullptr); + auto it = jni_stubs_map_.find(JniStubKey(method)); + DCHECK(it != jni_stubs_map_.end()) + << "Entry inserted in NotifyCompilationOf() should be alive."; + JniStubData* data = &it->second; + DCHECK(ContainsElement(data->GetMethods(), method)) + << "Entry inserted in NotifyCompilationOf() should contain this method."; + data->SetCode(code_ptr); + instrumentation::Instrumentation* instrum = Runtime::Current()->GetInstrumentation(); + for (ArtMethod* m : data->GetMethods()) { + instrum->UpdateMethodsCode(m, method_header->GetEntryPoint()); + } } else { - Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( - method, method_header->GetEntryPoint()); + // Fill the root table before updating the entry point. + DCHECK_EQ(FromStackMapToRoots(stack_map), roots_data); + DCHECK_LE(roots_data, stack_map); + FillRootTable(roots_data, roots); + { + // Flush data cache, as compiled code references literals in it. + // We also need a TLB shootdown to act as memory barrier across cores. + ScopedCodeCacheWrite ccw(code_map_.get(), /* only_for_tlb_shootdown */ true); + FlushDataCache(reinterpret_cast(roots_data), + reinterpret_cast(roots_data + data_size)); + } + method_code_map_.Put(code_ptr, method); + if (osr) { + number_of_osr_compilations_++; + osr_code_map_.Put(method, code_ptr); + } else { + Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( + method, method_header->GetEntryPoint()); + } } if (collection_in_progress_) { // We need to update the live bitmap if there is a GC to ensure it sees this new @@ -698,45 +856,18 @@ size_t JitCodeCache::CodeCacheSize() { } bool JitCodeCache::RemoveMethod(ArtMethod* method, bool release_memory) { - MutexLock mu(Thread::Current(), lock_); - if (method->IsNative()) { - return false; - } + // This function is used only for testing and only with non-native methods. + CHECK(!method->IsNative()); - bool in_cache = false; - { - ScopedCodeCacheWrite ccw(code_map_.get()); - for (auto code_iter = method_code_map_.begin(); code_iter != method_code_map_.end();) { - if (code_iter->second == method) { - if (release_memory) { - FreeCode(code_iter->first); - } - code_iter = method_code_map_.erase(code_iter); - in_cache = true; - continue; - } - ++code_iter; - } - } + MutexLock mu(Thread::Current(), lock_); - bool osr = false; - auto code_map = osr_code_map_.find(method); - if (code_map != osr_code_map_.end()) { - osr_code_map_.erase(code_map); - osr = true; - } + bool osr = osr_code_map_.find(method) != osr_code_map_.end(); + bool in_cache = RemoveMethodLocked(method, release_memory); if (!in_cache) { return false; } - ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); - if (info != nullptr) { - auto profile = std::find(profiling_infos_.begin(), profiling_infos_.end(), info); - DCHECK(profile != profiling_infos_.end()); - profiling_infos_.erase(profile); - } - method->SetProfilingInfo(nullptr); method->ClearCounter(); Runtime::Current()->GetInstrumentation()->UpdateMethodsCode( method, GetQuickToInterpreterBridge()); @@ -748,34 +879,58 @@ bool JitCodeCache::RemoveMethod(ArtMethod* method, bool release_memory) { return true; } +bool JitCodeCache::RemoveMethodLocked(ArtMethod* method, bool release_memory) { + if (LIKELY(!method->IsNative())) { + ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); + if (info != nullptr) { + RemoveElement(profiling_infos_, info); + } + method->SetProfilingInfo(nullptr); + } + + bool in_cache = false; + ScopedCodeCacheWrite ccw(code_map_.get()); + if (UNLIKELY(method->IsNative())) { + auto it = jni_stubs_map_.find(JniStubKey(method)); + if (it != jni_stubs_map_.end() && it->second.RemoveMethod(method)) { + in_cache = true; + if (it->second.GetMethods().empty()) { + if (release_memory) { + FreeCode(it->second.GetCode()); + } + jni_stubs_map_.erase(it); + } else { + it->first.UpdateShorty(it->second.GetMethods().front()); + } + } + } else { + for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { + if (it->second == method) { + in_cache = true; + if (release_memory) { + FreeCode(it->first); + } + it = method_code_map_.erase(it); + } else { + ++it; + } + } + + auto osr_it = osr_code_map_.find(method); + if (osr_it != osr_code_map_.end()) { + osr_code_map_.erase(osr_it); + } + } + + return in_cache; +} + // This notifies the code cache that the given method has been redefined and that it should remove // any cached information it has on the method. All threads must be suspended before calling this // method. The compiled code for the method (if there is any) must not be in any threads call stack. void JitCodeCache::NotifyMethodRedefined(ArtMethod* method) { MutexLock mu(Thread::Current(), lock_); - if (method->IsNative()) { - return; - } - ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); - if (info != nullptr) { - auto profile = std::find(profiling_infos_.begin(), profiling_infos_.end(), info); - DCHECK(profile != profiling_infos_.end()); - profiling_infos_.erase(profile); - } - method->SetProfilingInfo(nullptr); - ScopedCodeCacheWrite ccw(code_map_.get()); - for (auto code_iter = method_code_map_.begin(); code_iter != method_code_map_.end();) { - if (code_iter->second == method) { - FreeCode(code_iter->first); - code_iter = method_code_map_.erase(code_iter); - continue; - } - ++code_iter; - } - auto code_map = osr_code_map_.find(method); - if (code_map != osr_code_map_.end()) { - osr_code_map_.erase(code_map); - } + RemoveMethodLocked(method, /* release_memory */ true); } // This invalidates old_method. Once this function returns one can no longer use old_method to @@ -785,11 +940,15 @@ void JitCodeCache::NotifyMethodRedefined(ArtMethod* method) { // shouldn't be used since it is no longer logically in the jit code cache. // TODO We should add DCHECKS that validate that the JIT is paused when this method is entered. void JitCodeCache::MoveObsoleteMethod(ArtMethod* old_method, ArtMethod* new_method) { - // Native methods have no profiling info and need no special handling from the JIT code cache. + MutexLock mu(Thread::Current(), lock_); if (old_method->IsNative()) { + // Update methods in jni_stubs_map_. + for (auto& entry : jni_stubs_map_) { + JniStubData& data = entry.second; + data.MoveObsoleteMethod(old_method, new_method); + } return; } - MutexLock mu(Thread::Current(), lock_); // Update ProfilingInfo to the new one and remove it from the old_method. if (old_method->GetProfilingInfo(kRuntimePointerSize) != nullptr) { DCHECK_EQ(old_method->GetProfilingInfo(kRuntimePointerSize)->GetMethod(), old_method); @@ -931,7 +1090,7 @@ class MarkCodeClosure FINAL : public Closure { // its stack frame, it is not the method owning return_pc_. We just pass null to // LookupMethodHeader: the method is only checked against in debug builds. OatQuickMethodHeader* method_header = - code_cache_->LookupMethodHeader(frame.return_pc_, nullptr); + code_cache_->LookupMethodHeader(frame.return_pc_, /* method */ nullptr); if (method_header != nullptr) { const void* code = method_header->GetCode(); CHECK(code_cache_->GetLiveBitmap()->Test(FromCodeToAllocation(code))); @@ -1084,7 +1243,7 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { const void* entry_point = info->GetMethod()->GetEntryPointFromQuickCompiledCode(); if (ContainsPc(entry_point)) { info->SetSavedEntryPoint(entry_point); - // Don't call Instrumentation::UpdateMethods, as it can check the declaring + // Don't call Instrumentation::UpdateMethodsCode(), as it can check the declaring // class of the method. We may be concurrently running a GC which makes accessing // the class unsafe. We know it is OK to bypass the instrumentation as we've just // checked that the current entry point is JIT compiled code. @@ -1093,6 +1252,25 @@ void JitCodeCache::GarbageCollectCache(Thread* self) { } DCHECK(CheckLiveCompiledCodeHasProfilingInfo()); + + // Change entry points of native methods back to the GenericJNI entrypoint. + for (const auto& entry : jni_stubs_map_) { + const JniStubData& data = entry.second; + if (!data.IsCompiled()) { + continue; + } + // Make sure a single invocation of the GenericJNI trampoline tries to recompile. + uint16_t new_counter = Runtime::Current()->GetJit()->HotMethodThreshold() - 1u; + const OatQuickMethodHeader* method_header = + OatQuickMethodHeader::FromCodePointer(data.GetCode()); + for (ArtMethod* method : data.GetMethods()) { + if (method->GetEntryPointFromQuickCompiledCode() == method_header->GetEntryPoint()) { + // Don't call Instrumentation::UpdateMethodsCode(), same as for normal methods above. + method->SetCounter(new_counter); + method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub()); + } + } + } } live_bitmap_.reset(nullptr); NotifyCollectionDone(self); @@ -1108,13 +1286,22 @@ void JitCodeCache::RemoveUnmarkedCode(Thread* self) { MutexLock mu(self, lock_); ScopedCodeCacheWrite scc(code_map_.get()); // Iterate over all compiled code and remove entries that are not marked. + for (auto it = jni_stubs_map_.begin(); it != jni_stubs_map_.end();) { + JniStubData* data = &it->second; + if (!data->IsCompiled() || GetLiveBitmap()->Test(FromCodeToAllocation(data->GetCode()))) { + ++it; + } else { + method_headers.insert(OatQuickMethodHeader::FromCodePointer(data->GetCode())); + it = jni_stubs_map_.erase(it); + } + } for (auto it = method_code_map_.begin(); it != method_code_map_.end();) { const void* code_ptr = it->first; uintptr_t allocation = FromCodeToAllocation(code_ptr); if (GetLiveBitmap()->Test(allocation)) { ++it; } else { - method_headers.insert(OatQuickMethodHeader::FromCodePointer(it->first)); + method_headers.insert(OatQuickMethodHeader::FromCodePointer(code_ptr)); it = method_code_map_.erase(it); } } @@ -1153,6 +1340,17 @@ void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) { // an entry point is either: // - an osr compiled code, that will be removed if not in a thread call stack. // - discarded compiled code, that will be removed if not in a thread call stack. + for (const auto& entry : jni_stubs_map_) { + const JniStubData& data = entry.second; + const void* code_ptr = data.GetCode(); + const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); + for (ArtMethod* method : data.GetMethods()) { + if (method_header->GetEntryPoint() == method->GetEntryPointFromQuickCompiledCode()) { + GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(code_ptr)); + break; + } + } + } for (const auto& it : method_code_map_) { ArtMethod* method = it.second; const void* code_ptr = it.first; @@ -1232,19 +1430,51 @@ OatQuickMethodHeader* JitCodeCache::LookupMethodHeader(uintptr_t pc, ArtMethod* return nullptr; } - MutexLock mu(Thread::Current(), lock_); - if (method_code_map_.empty()) { - return nullptr; + if (!kIsDebugBuild) { + // Called with null `method` only from MarkCodeClosure::Run() in debug build. + CHECK(method != nullptr); } - auto it = method_code_map_.lower_bound(reinterpret_cast(pc)); - --it; - const void* code_ptr = it->first; - OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); - if (!method_header->Contains(pc)) { - return nullptr; + MutexLock mu(Thread::Current(), lock_); + OatQuickMethodHeader* method_header = nullptr; + ArtMethod* found_method = nullptr; // Only for DCHECK(), not for JNI stubs. + if (method != nullptr && UNLIKELY(method->IsNative())) { + auto it = jni_stubs_map_.find(JniStubKey(method)); + if (it == jni_stubs_map_.end() || !ContainsElement(it->second.GetMethods(), method)) { + return nullptr; + } + const void* code_ptr = it->second.GetCode(); + method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); + if (!method_header->Contains(pc)) { + return nullptr; + } + } else { + auto it = method_code_map_.lower_bound(reinterpret_cast(pc)); + if (it != method_code_map_.begin()) { + --it; + const void* code_ptr = it->first; + if (OatQuickMethodHeader::FromCodePointer(code_ptr)->Contains(pc)) { + method_header = OatQuickMethodHeader::FromCodePointer(code_ptr); + found_method = it->second; + } + } + if (method_header == nullptr && method == nullptr) { + // Scan all compiled JNI stubs as well. This slow search is used only + // for checks in debug build, for release builds the `method` is not null. + for (auto&& entry : jni_stubs_map_) { + const JniStubData& data = entry.second; + if (data.IsCompiled() && + OatQuickMethodHeader::FromCodePointer(data.GetCode())->Contains(pc)) { + method_header = OatQuickMethodHeader::FromCodePointer(data.GetCode()); + } + } + } + if (method_header == nullptr) { + return nullptr; + } } - if (kIsDebugBuild && method != nullptr) { + + if (kIsDebugBuild && method != nullptr && !method->IsNative()) { // When we are walking the stack to redefine classes and creating obsolete methods it is // possible that we might have updated the method_code_map by making this method obsolete in a // previous frame. Therefore we should just check that the non-obsolete version of this method @@ -1253,9 +1483,9 @@ OatQuickMethodHeader* JitCodeCache::LookupMethodHeader(uintptr_t pc, ArtMethod* // occur when we are in the process of allocating and setting up obsolete methods. Otherwise // method and it->second should be identical. (See openjdkjvmti/ti_redefine.cc for more // information.) - DCHECK_EQ(it->second->GetNonObsoleteMethod(), method->GetNonObsoleteMethod()) + DCHECK_EQ(found_method->GetNonObsoleteMethod(), method->GetNonObsoleteMethod()) << ArtMethod::PrettyMethod(method->GetNonObsoleteMethod()) << " " - << ArtMethod::PrettyMethod(it->second->GetNonObsoleteMethod()) << " " + << ArtMethod::PrettyMethod(found_method->GetNonObsoleteMethod()) << " " << std::hex << pc; } return method_header; @@ -1444,21 +1674,51 @@ bool JitCodeCache::NotifyCompilationOf(ArtMethod* method, Thread* self, bool osr return false; } - ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); - if (info == nullptr) { - VLOG(jit) << method->PrettyMethod() << " needs a ProfilingInfo to be compiled"; - // Because the counter is not atomic, there are some rare cases where we may not hit the - // threshold for creating the ProfilingInfo. Reset the counter now to "correct" this. - ClearMethodCounter(method, /*was_warm*/ false); - return false; - } + if (UNLIKELY(method->IsNative())) { + JniStubKey key(method); + auto it = jni_stubs_map_.find(key); + bool new_compilation = false; + if (it == jni_stubs_map_.end()) { + // Create a new entry to mark the stub as being compiled. + it = jni_stubs_map_.Put(key, JniStubData{}); + new_compilation = true; + } + JniStubData* data = &it->second; + data->AddMethod(method); + if (data->IsCompiled()) { + OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(data->GetCode()); + const void* entrypoint = method_header->GetEntryPoint(); + // Update also entrypoints of other methods held by the JniStubData. + // We could simply update the entrypoint of `method` but if the last JIT GC has + // changed these entrypoints to GenericJNI in preparation for a full GC, we may + // as well change them back as this stub shall not be collected anyway and this + // can avoid a few expensive GenericJNI calls. + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); + for (ArtMethod* m : data->GetMethods()) { + instrumentation->UpdateMethodsCode(m, entrypoint); + } + if (collection_in_progress_) { + GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(data->GetCode())); + } + } + return new_compilation; + } else { + ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); + if (info == nullptr) { + VLOG(jit) << method->PrettyMethod() << " needs a ProfilingInfo to be compiled"; + // Because the counter is not atomic, there are some rare cases where we may not hit the + // threshold for creating the ProfilingInfo. Reset the counter now to "correct" this. + ClearMethodCounter(method, /*was_warm*/ false); + return false; + } - if (info->IsMethodBeingCompiled(osr)) { - return false; - } + if (info->IsMethodBeingCompiled(osr)) { + return false; + } - info->SetIsMethodBeingCompiled(true, osr); - return true; + info->SetIsMethodBeingCompiled(true, osr); + return true; + } } ProfilingInfo* JitCodeCache::NotifyCompilerUse(ArtMethod* method, Thread* self) { @@ -1480,10 +1740,23 @@ void JitCodeCache::DoneCompilerUse(ArtMethod* method, Thread* self) { info->DecrementInlineUse(); } -void JitCodeCache::DoneCompiling(ArtMethod* method, Thread* self ATTRIBUTE_UNUSED, bool osr) { - ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); - DCHECK(info->IsMethodBeingCompiled(osr)); - info->SetIsMethodBeingCompiled(false, osr); +void JitCodeCache::DoneCompiling(ArtMethod* method, Thread* self, bool osr) { + DCHECK_EQ(Thread::Current(), self); + MutexLock mu(self, lock_); + if (UNLIKELY(method->IsNative())) { + auto it = jni_stubs_map_.find(JniStubKey(method)); + DCHECK(it != jni_stubs_map_.end()); + JniStubData* data = &it->second; + DCHECK(ContainsElement(data->GetMethods(), method)); + if (UNLIKELY(!data->IsCompiled())) { + // Failed to compile; the JNI compiler never fails, but the cache may be full. + jni_stubs_map_.erase(it); // Remove the entry added in NotifyCompilationOf(). + } // else CommitCodeInternal() updated entrypoints of all methods in the JniStubData. + } else { + ProfilingInfo* info = method->GetProfilingInfo(kRuntimePointerSize); + DCHECK(info->IsMethodBeingCompiled(osr)); + info->SetIsMethodBeingCompiled(false, osr); + } } size_t JitCodeCache::GetMemorySizeOfCodePointer(const void* ptr) { @@ -1493,6 +1766,7 @@ size_t JitCodeCache::GetMemorySizeOfCodePointer(const void* ptr) { void JitCodeCache::InvalidateCompiledCodeFor(ArtMethod* method, const OatQuickMethodHeader* header) { + DCHECK(!method->IsNative()); ProfilingInfo* profiling_info = method->GetProfilingInfo(kRuntimePointerSize); if ((profiling_info != nullptr) && (profiling_info->GetSavedEntryPoint() == header->GetEntryPoint())) { @@ -1548,6 +1822,7 @@ void JitCodeCache::Dump(std::ostream& os) { os << "Current JIT code cache size: " << PrettySize(used_memory_for_code_) << "\n" << "Current JIT data cache size: " << PrettySize(used_memory_for_data_) << "\n" << "Current JIT capacity: " << PrettySize(current_capacity_) << "\n" + << "Current number of JIT JNI stub entries: " << jni_stubs_map_.size() << "\n" << "Current number of JIT code cache entries: " << method_code_map_.size() << "\n" << "Total number of JIT compilations: " << number_of_compilations_ << "\n" << "Total number of JIT compilations for on stack replacement: " diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index 9790e3aa438d998249c2b4c6f8dd394136a206bb..fc011ddb963b1b2dfeefee64e420ed67cadaddd7 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -24,23 +24,46 @@ #include "base/histogram-inl.h" #include "base/macros.h" #include "base/mutex.h" -#include "gc/accounting/bitmap.h" #include "gc_root.h" -#include "jni.h" #include "method_reference.h" -#include "oat_file.h" -#include "profile_compilation_info.h" #include "safe_map.h" -#include "thread_pool.h" namespace art { class ArtMethod; +template class Handle; class LinearAlloc; class InlineCache; class IsMarkedVisitor; +class JitJniStubTestHelper; class OatQuickMethodHeader; +struct ProfileMethodInfo; class ProfilingInfo; +class Thread; + +namespace gc { +namespace accounting { +template class MemoryRangeBitmap; +} // namespace accounting +} // namespace gc + +namespace mirror { +class Class; +class Object; +template class ObjectArray; +} // namespace mirror + +namespace gc { +namespace accounting { +template class MemoryRangeBitmap; +} // namespace accounting +} // namespace gc + +namespace mirror { +class Class; +class Object; +template class ObjectArray; +} // namespace mirror namespace jit { @@ -66,6 +89,7 @@ class JitCodeCache { size_t max_capacity, bool generate_debug_info, std::string* error_msg); + ~JitCodeCache(); // Number of bytes allocated in the code cache. size_t CodeCacheSize() REQUIRES(!lock_); @@ -127,6 +151,9 @@ class JitCodeCache { // Return true if the code cache contains this method. bool ContainsMethod(ArtMethod* method) REQUIRES(!lock_); + // Return the code pointer for a JNI-compiled stub if the method is in the cache, null otherwise. + const void* GetJniStubCode(ArtMethod* method) REQUIRES(!lock_); + // Allocate a region of data that contain `size` bytes, and potentially space // for storing `number_of_roots` roots. Returns null if there is no more room. // Return the number of bytes allocated. @@ -150,11 +177,6 @@ class JitCodeCache { return live_bitmap_.get(); } - // Return whether we should do a full collection given the current state of the cache. - bool ShouldDoFullCollection() - REQUIRES(lock_) - REQUIRES_SHARED(Locks::mutator_lock_); - // Perform a collection on the code cache. void GarbageCollectCache(Thread* self) REQUIRES(!lock_) @@ -210,11 +232,6 @@ class JitCodeCache { uint64_t GetLastUpdateTimeNs() const; - size_t GetCurrentCapacity() REQUIRES(!lock_) { - MutexLock lock(Thread::Current(), lock_); - return current_capacity_; - } - size_t GetMemorySizeOfCodePointer(const void* ptr) REQUIRES(!lock_); void InvalidateCompiledCodeFor(ArtMethod* method, const OatQuickMethodHeader* code) @@ -291,6 +308,12 @@ class JitCodeCache { REQUIRES(!lock_) REQUIRES(!Locks::cha_lock_); + // Removes method from the cache. The caller must ensure that all threads + // are suspended and the method should not be in any thread's stack. + bool RemoveMethodLocked(ArtMethod* method, bool release_memory) + REQUIRES(lock_) + REQUIRES(Locks::mutator_lock_); + // Free in the mspace allocations for `code_ptr`. void FreeCode(const void* code_ptr) REQUIRES(lock_); @@ -310,6 +333,11 @@ class JitCodeCache { // Set the footprint limit of the code cache. void SetFootprintLimit(size_t new_footprint) REQUIRES(lock_); + // Return whether we should do a full collection given the current state of the cache. + bool ShouldDoFullCollection() + REQUIRES(lock_) + REQUIRES_SHARED(Locks::mutator_lock_); + void DoCollection(Thread* self, bool collect_profiling_info) REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); @@ -336,6 +364,9 @@ class JitCodeCache { REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); + class JniStubKey; + class JniStubData; + // Lock for guarding allocations, collections, and the method_code_map_. Mutex lock_; // Condition to wait on during collection. @@ -352,6 +383,8 @@ class JitCodeCache { void* data_mspace_ GUARDED_BY(lock_); // Bitmap for collecting code and data. std::unique_ptr live_bitmap_; + // Holds compiled code associated with the shorty for a JNI stub. + SafeMap jni_stubs_map_ GUARDED_BY(lock_); // Holds compiled code associated to the ArtMethod. SafeMap method_code_map_ GUARDED_BY(lock_); // Holds osr compiled code associated to the ArtMethod. @@ -413,6 +446,7 @@ class JitCodeCache { // Condition to wait on for accessing inline caches. ConditionVariable inline_cache_cond_ GUARDED_BY(lock_); + friend class art::JitJniStubTestHelper; DISALLOW_IMPLICIT_CONSTRUCTORS(JitCodeCache); }; diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc index 805b9c185a9b6def0df014382d44272ccbac0dbc..7754777eb6bece7320147e5240f9aba33d8a045a 100644 --- a/runtime/jit/profile_compilation_info.cc +++ b/runtime/jit/profile_compilation_info.cc @@ -35,6 +35,7 @@ #include "base/arena_allocator.h" #include "base/dumpable.h" #include "base/file_utils.h" +#include "base/logging.h" // For VLOG. #include "base/mutex.h" #include "base/scoped_flock.h" #include "base/stl_util.h" @@ -1034,7 +1035,7 @@ ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLine return kProfileLoadBadData; } const uint8_t* base_ptr = buffer.GetCurrentPtr(); - std::copy_n(base_ptr, bytes, &data->bitmap_storage[0]); + std::copy_n(base_ptr, bytes, data->bitmap_storage.data()); buffer.Advance(bytes); // Read method bitmap. return kProfileLoadSuccess; @@ -1582,7 +1583,11 @@ std::string ProfileCompilationInfo::DumpInfo(const std::vector* for (uint32_t method_idx = 0; method_idx < dex_data->num_method_ids; ++method_idx) { MethodHotness hotness_info(dex_data->GetHotnessInfo(method_idx)); if (startup ? hotness_info.IsStartup() : hotness_info.IsPostStartup()) { - os << method_idx << ", "; + if (dex_file != nullptr) { + os << "\n\t\t" << dex_file->PrettyMethod(method_idx, true); + } else { + os << method_idx << ", "; + } } } if (startup == false) { diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index 01853de4037b2cbeb901a8f786c421d5fcbc9176..ee11cfddb0d3789c0bc6c69ab232f80a24dbeea5 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -25,6 +25,7 @@ #include "art_method-inl.h" #include "base/enums.h" +#include "base/logging.h" // For VLOG. #include "base/scoped_arena_containers.h" #include "base/stl_util.h" #include "base/systrace.h" @@ -357,8 +358,8 @@ static void SampleClassesAndExecutedMethods(pthread_t profiler_pthread, sampled_methods->AddReference(method.GetDexFile(), method.GetDexMethodIndex()); } } else { - CHECK_EQ(method.GetCounter(), 0u) << method.PrettyMethod() - << " access_flags=" << method.GetAccessFlags(); + // We do not record native methods. Once we AOT-compile the app, all native + // methods shall have their thunks compiled. } } } diff --git a/runtime/jit/profiling_info.cc b/runtime/jit/profiling_info.cc index 1344ca05b40ebe458ce65a7aee395ba5ba0dcb24..e54a0179e1f806d4e298abc2df3edb3ecf898543 100644 --- a/runtime/jit/profiling_info.cc +++ b/runtime/jit/profiling_info.cc @@ -44,8 +44,7 @@ bool ProfilingInfo::Create(Thread* self, ArtMethod* method, bool retry_allocatio DCHECK(!method->IsNative()); std::vector entries; - - for (const DexInstructionPcPair& inst : method->GetCodeItem()->Instructions()) { + for (const DexInstructionPcPair& inst : method->DexInstructions()) { switch (inst->Opcode()) { case Instruction::INVOKE_VIRTUAL: case Instruction::INVOKE_VIRTUAL_RANGE: diff --git a/runtime/jni_env_ext-inl.h b/runtime/jni_env_ext-inl.h index d66df081c6694b606437b1d1399ae3a4fcfb7ab0..14f708b18dfc09542934081169b1ac6deadd1919 100644 --- a/runtime/jni_env_ext-inl.h +++ b/runtime/jni_env_ext-inl.h @@ -26,7 +26,7 @@ namespace art { template inline T JNIEnvExt::AddLocalReference(ObjPtr obj) { std::string error_msg; - IndirectRef ref = locals.Add(local_ref_cookie, obj, &error_msg); + IndirectRef ref = locals_.Add(local_ref_cookie_, obj, &error_msg); if (UNLIKELY(ref == nullptr)) { // This is really unexpected if we allow resizing local IRTs... LOG(FATAL) << error_msg; @@ -35,10 +35,10 @@ inline T JNIEnvExt::AddLocalReference(ObjPtr obj) { // TODO: fix this to understand PushLocalFrame, so we can turn it on. if (false) { - if (check_jni) { - size_t entry_count = locals.Capacity(); + if (check_jni_) { + size_t entry_count = locals_.Capacity(); if (entry_count > 16) { - locals.Dump(LOG_STREAM(WARNING) << "Warning: more than 16 JNI local references: " + locals_.Dump(LOG_STREAM(WARNING) << "Warning: more than 16 JNI local references: " << entry_count << " (most recent was a " << mirror::Object::PrettyTypeOf(obj) << ")\n"); // TODO: LOG(FATAL) in a later release? diff --git a/runtime/jni_env_ext.cc b/runtime/jni_env_ext.cc index 8352657f28e65d271388bbcd06c10196920dda8b..efe43ee0e940fdfd5661296922a4f9b0cac2c7ea 100644 --- a/runtime/jni_env_ext.cc +++ b/runtime/jni_env_ext.cc @@ -21,6 +21,7 @@ #include "android-base/stringprintf.h" +#include "base/to_str.h" #include "check_jni.h" #include "indirect_reference_table.h" #include "java_vm_ext.h" @@ -28,6 +29,7 @@ #include "lock_word.h" #include "mirror/object-inl.h" #include "nth_caller_visitor.h" +#include "scoped_thread_state_change.h" #include "thread-current-inl.h" #include "thread_list.h" @@ -40,14 +42,11 @@ static constexpr size_t kMonitorsMax = 4096; // Arbitrary sanity check. const JNINativeInterface* JNIEnvExt::table_override_ = nullptr; -// Checking "locals" requires the mutator lock, but at creation time we're really only interested -// in validity, which isn't changing. To avoid grabbing the mutator lock, factored out and tagged -// with NO_THREAD_SAFETY_ANALYSIS. -static bool CheckLocalsValid(JNIEnvExt* in) NO_THREAD_SAFETY_ANALYSIS { +bool JNIEnvExt::CheckLocalsValid(JNIEnvExt* in) NO_THREAD_SAFETY_ANALYSIS { if (in == nullptr) { return false; } - return in->locals.IsValid(); + return in->locals_.IsValid(); } jint JNIEnvExt::GetEnvHandler(JavaVMExt* vm, /*out*/void** env, jint version) { @@ -73,23 +72,23 @@ JNIEnvExt* JNIEnvExt::Create(Thread* self_in, JavaVMExt* vm_in, std::string* err } JNIEnvExt::JNIEnvExt(Thread* self_in, JavaVMExt* vm_in, std::string* error_msg) - : self(self_in), - vm(vm_in), - local_ref_cookie(kIRTFirstSegment), - locals(kLocalsInitial, kLocal, IndirectReferenceTable::ResizableCapacity::kYes, error_msg), - check_jni(false), - runtime_deleted(false), - critical(0), - monitors("monitors", kMonitorsInitial, kMonitorsMax) { + : self_(self_in), + vm_(vm_in), + local_ref_cookie_(kIRTFirstSegment), + locals_(kLocalsInitial, kLocal, IndirectReferenceTable::ResizableCapacity::kYes, error_msg), + monitors_("monitors", kMonitorsInitial, kMonitorsMax), + critical_(0), + check_jni_(false), + runtime_deleted_(false) { MutexLock mu(Thread::Current(), *Locks::jni_function_table_lock_); - check_jni = vm->IsCheckJniEnabled(); - functions = GetFunctionTable(check_jni); - unchecked_functions = GetJniNativeInterface(); + check_jni_ = vm_in->IsCheckJniEnabled(); + functions = GetFunctionTable(check_jni_); + unchecked_functions_ = GetJniNativeInterface(); } void JNIEnvExt::SetFunctionsToRuntimeShutdownFunctions() { functions = GetRuntimeShutdownNativeInterface(); - runtime_deleted = true; + runtime_deleted_ = true; } JNIEnvExt::~JNIEnvExt() { @@ -100,7 +99,7 @@ jobject JNIEnvExt::NewLocalRef(mirror::Object* obj) { return nullptr; } std::string error_msg; - jobject ref = reinterpret_cast(locals.Add(local_ref_cookie, obj, &error_msg)); + jobject ref = reinterpret_cast(locals_.Add(local_ref_cookie_, obj, &error_msg)); if (UNLIKELY(ref == nullptr)) { // This is really unexpected if we allow resizing local IRTs... LOG(FATAL) << error_msg; @@ -111,12 +110,12 @@ jobject JNIEnvExt::NewLocalRef(mirror::Object* obj) { void JNIEnvExt::DeleteLocalRef(jobject obj) { if (obj != nullptr) { - locals.Remove(local_ref_cookie, reinterpret_cast(obj)); + locals_.Remove(local_ref_cookie_, reinterpret_cast(obj)); } } void JNIEnvExt::SetCheckJniEnabled(bool enabled) { - check_jni = enabled; + check_jni_ = enabled; MutexLock mu(Thread::Current(), *Locks::jni_function_table_lock_); functions = GetFunctionTable(enabled); // Check whether this is a no-op because of override. @@ -126,20 +125,20 @@ void JNIEnvExt::SetCheckJniEnabled(bool enabled) { } void JNIEnvExt::DumpReferenceTables(std::ostream& os) { - locals.Dump(os); - monitors.Dump(os); + locals_.Dump(os); + monitors_.Dump(os); } void JNIEnvExt::PushFrame(int capacity) { - DCHECK_GE(locals.FreeCapacity(), static_cast(capacity)); - stacked_local_ref_cookies.push_back(local_ref_cookie); - local_ref_cookie = locals.GetSegmentState(); + DCHECK_GE(locals_.FreeCapacity(), static_cast(capacity)); + stacked_local_ref_cookies_.push_back(local_ref_cookie_); + local_ref_cookie_ = locals_.GetSegmentState(); } void JNIEnvExt::PopFrame() { - locals.SetSegmentState(local_ref_cookie); - local_ref_cookie = stacked_local_ref_cookies.back(); - stacked_local_ref_cookies.pop_back(); + locals_.SetSegmentState(local_ref_cookie_); + local_ref_cookie_ = stacked_local_ref_cookies_.back(); + stacked_local_ref_cookies_.pop_back(); } // Note: the offset code is brittle, as we can't use OFFSETOF_MEMBER or offsetof easily. Thus, there @@ -188,7 +187,7 @@ static uintptr_t GetJavaCallFrame(Thread* self) REQUIRES_SHARED(Locks::mutator_l } void JNIEnvExt::RecordMonitorEnter(jobject obj) { - locked_objects_.push_back(std::make_pair(GetJavaCallFrame(self), obj)); + locked_objects_.push_back(std::make_pair(GetJavaCallFrame(self_), obj)); } static std::string ComputeMonitorDescription(Thread* self, @@ -230,7 +229,7 @@ static void RemoveMonitors(Thread* self, } void JNIEnvExt::CheckMonitorRelease(jobject obj) { - uintptr_t current_frame = GetJavaCallFrame(self); + uintptr_t current_frame = GetJavaCallFrame(self_); std::pair exact_pair = std::make_pair(current_frame, obj); auto it = std::find(locked_objects_.begin(), locked_objects_.end(), exact_pair); bool will_abort = false; @@ -238,11 +237,11 @@ void JNIEnvExt::CheckMonitorRelease(jobject obj) { locked_objects_.erase(it); } else { // Check whether this monitor was locked in another JNI "session." - ObjPtr mirror_obj = self->DecodeJObject(obj); + ObjPtr mirror_obj = self_->DecodeJObject(obj); for (std::pair& pair : locked_objects_) { - if (self->DecodeJObject(pair.second) == mirror_obj) { - std::string monitor_descr = ComputeMonitorDescription(self, pair.second); - vm->JniAbortF("", + if (self_->DecodeJObject(pair.second) == mirror_obj) { + std::string monitor_descr = ComputeMonitorDescription(self_, pair.second); + vm_->JniAbortF("", "Unlocking monitor that wasn't locked here: %s", monitor_descr.c_str()); will_abort = true; @@ -255,26 +254,26 @@ void JNIEnvExt::CheckMonitorRelease(jobject obj) { // the monitors table, otherwise we may visit local objects in GC during abort (which won't be // valid anymore). if (will_abort) { - RemoveMonitors(self, current_frame, &monitors, &locked_objects_); + RemoveMonitors(self_, current_frame, &monitors_, &locked_objects_); } } void JNIEnvExt::CheckNoHeldMonitors() { - uintptr_t current_frame = GetJavaCallFrame(self); // The locked_objects_ are grouped by their stack frame component, as this enforces structured // locking, and the groups form a stack. So the current frame entries are at the end. Check // whether the vector is empty, and when there are elements, whether the last element belongs // to this call - this signals that there are unlocked monitors. if (!locked_objects_.empty()) { + uintptr_t current_frame = GetJavaCallFrame(self_); std::pair& pair = locked_objects_[locked_objects_.size() - 1]; if (pair.first == current_frame) { - std::string monitor_descr = ComputeMonitorDescription(self, pair.second); - vm->JniAbortF("", + std::string monitor_descr = ComputeMonitorDescription(self_, pair.second); + vm_->JniAbortF("", "Still holding a locked object on JNI end: %s", monitor_descr.c_str()); // When we abort, also make sure that any locks from the current "session" are removed from // the monitors table, otherwise we may visit local objects in GC during abort. - RemoveMonitors(self, current_frame, &monitors, &locked_objects_); + RemoveMonitors(self_, current_frame, &monitors_, &locked_objects_); } else if (kIsDebugBuild) { // Make sure there are really no other entries and our checking worked as expected. for (std::pair& check_pair : locked_objects_) { @@ -282,12 +281,18 @@ void JNIEnvExt::CheckNoHeldMonitors() { } } } + // Ensure critical locks aren't held when returning to Java. + if (critical_ > 0) { + vm_->JniAbortF("", + "Critical lock held when returning to Java on thread %s", + ToStr(*self_).c_str()); + } } static void ThreadResetFunctionTable(Thread* thread, void* arg ATTRIBUTE_UNUSED) REQUIRES(Locks::jni_function_table_lock_) { JNIEnvExt* env = thread->GetJniEnv(); - bool check_jni = env->check_jni; + bool check_jni = env->IsCheckJniEnabled(); env->functions = JNIEnvExt::GetFunctionTable(check_jni); } diff --git a/runtime/jni_env_ext.h b/runtime/jni_env_ext.h index 2f6c5dc92ac1c2e8c49bfa09fc5e47919ce9a3d2..0e8fd0305769a509aca730f1718388f4b88f5764 100644 --- a/runtime/jni_env_ext.h +++ b/runtime/jni_env_ext.h @@ -37,10 +37,15 @@ class Object; // low enough that it forces sanity checks. static constexpr size_t kLocalsInitial = 512; -struct JNIEnvExt : public JNIEnv { +class JNIEnvExt : public JNIEnv { + public: // Creates a new JNIEnvExt. Returns null on error, in which case error_msg // will contain a description of the error. static JNIEnvExt* Create(Thread* self, JavaVMExt* vm, std::string* error_msg); + static Offset SegmentStateOffset(size_t pointer_size); + static Offset LocalRefCookieOffset(size_t pointer_size); + static Offset SelfOffset(size_t pointer_size); + static jint GetEnvHandler(JavaVMExt* vm, /*out*/void** out, jint version); ~JNIEnvExt(); @@ -58,43 +63,44 @@ struct JNIEnvExt : public JNIEnv { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::alloc_tracker_lock_); - static Offset SegmentStateOffset(size_t pointer_size); - static Offset LocalRefCookieOffset(size_t pointer_size); - static Offset SelfOffset(size_t pointer_size); - - static jint GetEnvHandler(JavaVMExt* vm, /*out*/void** out, jint version); + void UpdateLocal(IndirectRef iref, ObjPtr obj) REQUIRES_SHARED(Locks::mutator_lock_) { + locals_.Update(iref, obj); + } jobject NewLocalRef(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_); void DeleteLocalRef(jobject obj) REQUIRES_SHARED(Locks::mutator_lock_); - Thread* const self; - JavaVMExt* const vm; - - // Cookie used when using the local indirect reference table. - IRTSegmentState local_ref_cookie; + void TrimLocals() REQUIRES_SHARED(Locks::mutator_lock_) { + locals_.Trim(); + } + void AssertLocalsEmpty() REQUIRES_SHARED(Locks::mutator_lock_) { + locals_.AssertEmpty(); + } + size_t GetLocalsCapacity() REQUIRES_SHARED(Locks::mutator_lock_) { + return locals_.Capacity(); + } - // JNI local references. - IndirectReferenceTable locals GUARDED_BY(Locks::mutator_lock_); + IRTSegmentState GetLocalRefCookie() const { return local_ref_cookie_; } + void SetLocalRefCookie(IRTSegmentState new_cookie) { local_ref_cookie_ = new_cookie; } - // Stack of cookies corresponding to PushLocalFrame/PopLocalFrame calls. - // TODO: to avoid leaks (and bugs), we need to clear this vector on entry (or return) - // to a native method. - std::vector stacked_local_ref_cookies; + IRTSegmentState GetLocalsSegmentState() const REQUIRES_SHARED(Locks::mutator_lock_) { + return locals_.GetSegmentState(); + } + void SetLocalSegmentState(IRTSegmentState new_state) REQUIRES_SHARED(Locks::mutator_lock_) { + locals_.SetSegmentState(new_state); + } - // Frequently-accessed fields cached from JavaVM. - bool check_jni; + void VisitJniLocalRoots(RootVisitor* visitor, const RootInfo& root_info) + REQUIRES_SHARED(Locks::mutator_lock_) { + locals_.VisitRoots(visitor, root_info); + } - // If we are a JNI env for a daemon thread with a deleted runtime. - bool runtime_deleted; + Thread* GetSelf() const { return self_; } + JavaVMExt* GetVm() const { return vm_; } - // How many nested "critical" JNI calls are we in? - int critical; + bool IsRuntimeDeleted() const { return runtime_deleted_; } + bool IsCheckJniEnabled() const { return check_jni_; } - // Entered JNI monitors, for bulk exit on thread detach. - ReferenceTable monitors; - - // Used by -Xcheck:jni. - const JNINativeInterface* unchecked_functions; // Functions to keep track of monitor lock and unlock operations. Used to ensure proper locking // rules in CheckJNI mode. @@ -108,6 +114,11 @@ struct JNIEnvExt : public JNIEnv { // Check that no monitors are held that have been acquired in this JNI "segment." void CheckNoHeldMonitors() REQUIRES_SHARED(Locks::mutator_lock_); + void VisitMonitorRoots(RootVisitor* visitor, const RootInfo& root_info) + REQUIRES_SHARED(Locks::mutator_lock_) { + monitors_.VisitRoots(visitor, root_info); + } + // Set the functions to the runtime shutdown functions. void SetFunctionsToRuntimeShutdownFunctions(); @@ -124,6 +135,11 @@ struct JNIEnvExt : public JNIEnv { REQUIRES(Locks::jni_function_table_lock_); private: + // Checking "locals" requires the mutator lock, but at creation time we're + // really only interested in validity, which isn't changing. To avoid grabbing + // the mutator lock, factored out and tagged with NO_THREAD_SAFETY_ANALYSIS. + static bool CheckLocalsValid(JNIEnvExt* in) NO_THREAD_SAFETY_ANALYSIS; + // Override of function tables. This applies to both default as well as instrumented (CheckJNI) // function tables. static const JNINativeInterface* table_override_ GUARDED_BY(Locks::jni_function_table_lock_); @@ -133,29 +149,73 @@ struct JNIEnvExt : public JNIEnv { JNIEnvExt(Thread* self, JavaVMExt* vm, std::string* error_msg) REQUIRES(!Locks::jni_function_table_lock_); + // Link to Thread::Current(). + Thread* const self_; + + // The invocation interface JavaVM. + JavaVMExt* const vm_; + + // Cookie used when using the local indirect reference table. + IRTSegmentState local_ref_cookie_; + + // JNI local references. + IndirectReferenceTable locals_ GUARDED_BY(Locks::mutator_lock_); + + // Stack of cookies corresponding to PushLocalFrame/PopLocalFrame calls. + // TODO: to avoid leaks (and bugs), we need to clear this vector on entry (or return) + // to a native method. + std::vector stacked_local_ref_cookies_; + + // Entered JNI monitors, for bulk exit on thread detach. + ReferenceTable monitors_; + + // Used by -Xcheck:jni. + const JNINativeInterface* unchecked_functions_; + // All locked objects, with the (Java caller) stack frame that locked them. Used in CheckJNI // to ensure that only monitors locked in this native frame are being unlocked, and that at // the end all are unlocked. std::vector> locked_objects_; + + // Start time of "critical" JNI calls to ensure that their use doesn't + // excessively block the VM with CheckJNI. + uint64_t critical_start_us_; + + // How many nested "critical" JNI calls are we in? Used by CheckJNI to ensure that criticals are + uint32_t critical_; + + // Frequently-accessed fields cached from JavaVM. + bool check_jni_; + + // If we are a JNI env for a daemon thread with a deleted runtime. + bool runtime_deleted_; + + friend class CheckJNI; + friend class JNI; + friend class ScopedCheck; + friend class ScopedJniEnvLocalRefState; + friend class Thread; + ART_FRIEND_TEST(JniInternalTest, JNIEnvExtOffsets); }; // Used to save and restore the JNIEnvExt state when not going through code created by the JNI // compiler. class ScopedJniEnvLocalRefState { public: - explicit ScopedJniEnvLocalRefState(JNIEnvExt* env) : env_(env) { - saved_local_ref_cookie_ = env->local_ref_cookie; - env->local_ref_cookie = env->locals.GetSegmentState(); + explicit ScopedJniEnvLocalRefState(JNIEnvExt* env) : + env_(env), + saved_local_ref_cookie_(env->local_ref_cookie_) { + env->local_ref_cookie_ = env->locals_.GetSegmentState(); } ~ScopedJniEnvLocalRefState() { - env_->locals.SetSegmentState(env_->local_ref_cookie); - env_->local_ref_cookie = saved_local_ref_cookie_; + env_->locals_.SetSegmentState(env_->local_ref_cookie_); + env_->local_ref_cookie_ = saved_local_ref_cookie_; } private: JNIEnvExt* const env_; - IRTSegmentState saved_local_ref_cookie_; + const IRTSegmentState saved_local_ref_cookie_; DISALLOW_COPY_AND_ASSIGN(ScopedJniEnvLocalRefState); }; diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 5164c85b603fbac6a88649cd0ef567901fe251b1..53ae628a44db79bea8cf1a4da6ada8f0617fbeed 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -28,7 +28,7 @@ #include "atomic.h" #include "base/allocator.h" #include "base/enums.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/mutex.h" #include "base/stl_util.h" #include "class_linker-inl.h" @@ -383,7 +383,7 @@ int ThrowNewException(JNIEnv* env, jclass exception_class, const char* msg, jobj } static JavaVMExt* JavaVmExtFromEnv(JNIEnv* env) { - return reinterpret_cast(env)->vm; + return reinterpret_cast(env)->GetVm(); } #define CHECK_NON_NULL_ARGUMENT(value) \ @@ -545,7 +545,7 @@ class JNI { } static jboolean ExceptionCheck(JNIEnv* env) { - return static_cast(env)->self->IsExceptionPending() ? JNI_TRUE : JNI_FALSE; + return static_cast(env)->self_->IsExceptionPending() ? JNI_TRUE : JNI_FALSE; } static void ExceptionClear(JNIEnv* env) { @@ -623,8 +623,8 @@ class JNI { } static void DeleteGlobalRef(JNIEnv* env, jobject obj) { - JavaVMExt* vm = down_cast(env)->vm; - Thread* self = down_cast(env)->self; + JavaVMExt* vm = down_cast(env)->GetVm(); + Thread* self = down_cast(env)->self_; vm->DeleteGlobalRef(self, obj); } @@ -635,8 +635,8 @@ class JNI { } static void DeleteWeakGlobalRef(JNIEnv* env, jweak obj) { - JavaVMExt* vm = down_cast(env)->vm; - Thread* self = down_cast(env)->self; + JavaVMExt* vm = down_cast(env)->GetVm(); + Thread* self = down_cast(env)->self_; vm->DeleteWeakGlobalRef(self, obj); } @@ -659,7 +659,7 @@ class JNI { // it. b/22119403 ScopedObjectAccess soa(env); auto* ext_env = down_cast(env); - if (!ext_env->locals.Remove(ext_env->local_ref_cookie, obj)) { + if (!ext_env->locals_.Remove(ext_env->local_ref_cookie_, obj)) { // Attempting to delete a local reference that is not in the // topmost local reference frame is a no-op. DeleteLocalRef returns // void and doesn't throw any exceptions, but we should probably @@ -2310,7 +2310,7 @@ class JNI { // first, either as a direct or a virtual method. Then move to // the parent. ArtMethod* m = nullptr; - bool warn_on_going_to_parent = down_cast(env)->vm->IsCheckJniEnabled(); + bool warn_on_going_to_parent = down_cast(env)->GetVm()->IsCheckJniEnabled(); for (ObjPtr current_class = c.Get(); current_class != nullptr; current_class = current_class->GetSuperClass()) { @@ -2364,7 +2364,7 @@ class JNI { // TODO: make this a hard register error in the future. } - const void* final_function_ptr = m->RegisterNative(fnPtr, is_fast); + const void* final_function_ptr = m->RegisterNative(fnPtr); UNUSED(final_function_ptr); } return JNI_OK; @@ -2399,7 +2399,7 @@ class JNI { ObjPtr o = soa.Decode(java_object); o = o->MonitorEnter(soa.Self()); if (soa.Self()->HoldsLock(o)) { - soa.Env()->monitors.Add(o); + soa.Env()->monitors_.Add(o); } if (soa.Self()->IsExceptionPending()) { return JNI_ERR; @@ -2414,7 +2414,7 @@ class JNI { bool remove_mon = soa.Self()->HoldsLock(o); o->MonitorExit(soa.Self()); if (remove_mon) { - soa.Env()->monitors.Remove(o); + soa.Env()->monitors_.Remove(o); } if (soa.Self()->IsExceptionPending()) { return JNI_ERR; @@ -2458,7 +2458,7 @@ class JNI { jobject result = env->NewObject(WellKnownClasses::java_nio_DirectByteBuffer, WellKnownClasses::java_nio_DirectByteBuffer_init, address_arg, capacity_arg); - return static_cast(env)->self->IsExceptionPending() ? nullptr : result; + return static_cast(env)->self_->IsExceptionPending() ? nullptr : result; } static void* GetDirectBufferAddress(JNIEnv* env, jobject java_buffer) { @@ -2504,7 +2504,7 @@ class JNI { } std::string error_msg; - if (!soa.Env()->locals.EnsureFreeCapacity(static_cast(desired_capacity), &error_msg)) { + if (!soa.Env()->locals_.EnsureFreeCapacity(static_cast(desired_capacity), &error_msg)) { std::string caller_error = android::base::StringPrintf("%s: %s", caller, error_msg.c_str()); soa.Self()->ThrowOutOfMemoryError(caller_error.c_str()); return JNI_ERR; diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc index 1ecfe7cb7664aa09b6a982d7cfa8ce88c1a3280e..ad24c94ae9b8ffec169bd78b5e33430a48adeec1 100644 --- a/runtime/jni_internal_test.cc +++ b/runtime/jni_internal_test.cc @@ -867,7 +867,7 @@ TEST_F(JniInternalTest, GetStaticMethodID) { static size_t GetLocalsCapacity(JNIEnv* env) { ScopedObjectAccess soa(Thread::Current()); - return reinterpret_cast(env)->locals.Capacity(); + return reinterpret_cast(env)->GetLocalsCapacity(); } TEST_F(JniInternalTest, FromReflectedField_ToReflectedField) { @@ -1783,66 +1783,115 @@ TEST_F(JniInternalTest, GetObjectArrayElement_SetObjectArrayElement) { EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni)); \ } while (false) +#define TEST_PRIMITIVE_FIELD_FOR_CLASS(cname) \ + do { \ + Thread::Current()->TransitionFromSuspendedToRunnable(); \ + LoadDex("AllFields"); \ + bool started = runtime_->Start(); \ + ASSERT_TRUE(started); \ + jclass c = env_->FindClass(cname); \ + ASSERT_NE(c, nullptr); \ + jobject o = env_->AllocObject(c); \ + ASSERT_NE(o, nullptr); \ + \ + EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Boolean, "sZ", "Z", JNI_TRUE, JNI_FALSE); \ + EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Byte, "sB", "B", 1, 2); \ + EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Char, "sC", "C", 'a', 'b'); \ + EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_DOUBLE_EQ, Double, "sD", "D", 1.0, 2.0); \ + EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_FLOAT_EQ, Float, "sF", "F", 1.0, 2.0); \ + EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Int, "sI", "I", 1, 2); \ + EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Long, "sJ", "J", 1, 2); \ + EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Short, "sS", "S", 1, 2); \ + \ + EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Boolean, "iZ", "Z", JNI_TRUE, JNI_FALSE); \ + EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Byte, "iB", "B", 1, 2); \ + EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Char, "iC", "C", 'a', 'b'); \ + EXPECT_PRIMITIVE_FIELD(EXPECT_DOUBLE_EQ, o, Double, "iD", "D", 1.0, 2.0); \ + EXPECT_PRIMITIVE_FIELD(EXPECT_FLOAT_EQ, o, Float, "iF", "F", 1.0, 2.0); \ + EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Int, "iI", "I", 1, 2); \ + EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Long, "iJ", "J", 1, 2); \ + EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Short, "iS", "S", 1, 2); \ + } while (false) TEST_F(JniInternalTest, GetPrimitiveField_SetPrimitiveField) { - Thread::Current()->TransitionFromSuspendedToRunnable(); - LoadDex("AllFields"); - bool started = runtime_->Start(); - ASSERT_TRUE(started); - - jclass c = env_->FindClass("AllFields"); - ASSERT_NE(c, nullptr); - jobject o = env_->AllocObject(c); - ASSERT_NE(o, nullptr); + TEST_PRIMITIVE_FIELD_FOR_CLASS("AllFields"); +} - EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Boolean, "sZ", "Z", JNI_TRUE, JNI_FALSE); - EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Byte, "sB", "B", 1, 2); - EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Char, "sC", "C", 'a', 'b'); - EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_DOUBLE_EQ, Double, "sD", "D", 1.0, 2.0); - EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_FLOAT_EQ, Float, "sF", "F", 1.0, 2.0); - EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Int, "sI", "I", 1, 2); - EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Long, "sJ", "J", 1, 2); - EXPECT_STATIC_PRIMITIVE_FIELD(EXPECT_EQ, Short, "sS", "S", 1, 2); - - EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Boolean, "iZ", "Z", JNI_TRUE, JNI_FALSE); - EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Byte, "iB", "B", 1, 2); - EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Char, "iC", "C", 'a', 'b'); - EXPECT_PRIMITIVE_FIELD(EXPECT_DOUBLE_EQ, o, Double, "iD", "D", 1.0, 2.0); - EXPECT_PRIMITIVE_FIELD(EXPECT_FLOAT_EQ, o, Float, "iF", "F", 1.0, 2.0); - EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Int, "iI", "I", 1, 2); - EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Long, "iJ", "J", 1, 2); - EXPECT_PRIMITIVE_FIELD(EXPECT_EQ, o, Short, "iS", "S", 1, 2); +TEST_F(JniInternalTest, GetPrimitiveField_SetPrimitiveField_Subclass) { + TEST_PRIMITIVE_FIELD_FOR_CLASS("AllFieldsSub"); } -TEST_F(JniInternalTest, GetObjectField_SetObjectField) { +#define EXPECT_UNRELATED_FIELD_FAILURE(type, field_name, sig, value1) \ + do { \ + jfieldID fid = env_->GetStaticFieldID(c, field_name, sig); \ + EXPECT_NE(fid, nullptr); \ + CheckJniAbortCatcher jni_abort_catcher; \ + env_->Get ## type ## Field(uc, fid); \ + jni_abort_catcher.Check("not valid for an object of class"); \ + env_->Set ## type ## Field(uc, fid, value1); \ + jni_abort_catcher.Check("not valid for an object of class"); \ + } while (false) + +TEST_F(JniInternalTest, GetField_SetField_unrelated) { Thread::Current()->TransitionFromSuspendedToRunnable(); LoadDex("AllFields"); - runtime_->Start(); - + bool started = runtime_->Start(); + ASSERT_TRUE(started); jclass c = env_->FindClass("AllFields"); ASSERT_NE(c, nullptr); - jobject o = env_->AllocObject(c); - ASSERT_NE(o, nullptr); - - jstring s1 = env_->NewStringUTF("hello"); - ASSERT_NE(s1, nullptr); - jstring s2 = env_->NewStringUTF("world"); - ASSERT_NE(s2, nullptr); + jclass uc = env_->FindClass("AllFieldsUnrelated"); + ASSERT_NE(uc, nullptr); + bool old_check_jni = vm_->SetCheckJniEnabled(true); + EXPECT_UNRELATED_FIELD_FAILURE(Boolean, "sZ", "Z", JNI_TRUE); + EXPECT_UNRELATED_FIELD_FAILURE(Byte, "sB", "B", 1); + EXPECT_UNRELATED_FIELD_FAILURE(Char, "sC", "C", 'a'); + EXPECT_UNRELATED_FIELD_FAILURE(Double, "sD", "D", 1.0); + EXPECT_UNRELATED_FIELD_FAILURE(Float, "sF", "F", 1.0); + EXPECT_UNRELATED_FIELD_FAILURE(Int, "sI", "I", 1); + EXPECT_UNRELATED_FIELD_FAILURE(Long, "sJ", "J", 1); + EXPECT_UNRELATED_FIELD_FAILURE(Short, "sS", "S", 1); + EXPECT_UNRELATED_FIELD_FAILURE(Object, "sObject", "Ljava/lang/Object;", c); + EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni)); +} - jfieldID s_fid = env_->GetStaticFieldID(c, "sObject", "Ljava/lang/Object;"); - ASSERT_NE(s_fid, nullptr); - jfieldID i_fid = env_->GetFieldID(c, "iObject", "Ljava/lang/Object;"); - ASSERT_NE(i_fid, nullptr); +#define TEST_OBJECT_FIELD_FOR_CLASS(cname) \ + do { \ + Thread::Current()->TransitionFromSuspendedToRunnable(); \ + LoadDex("AllFields"); \ + runtime_->Start(); \ + \ + jclass c = env_->FindClass(cname); \ + ASSERT_NE(c, nullptr); \ + jobject o = env_->AllocObject(c); \ + ASSERT_NE(o, nullptr); \ + \ + jstring s1 = env_->NewStringUTF("hello"); \ + ASSERT_NE(s1, nullptr); \ + jstring s2 = env_->NewStringUTF("world"); \ + ASSERT_NE(s2, nullptr); \ + \ + jfieldID s_fid = env_->GetStaticFieldID(c, "sObject", "Ljava/lang/Object;"); \ + ASSERT_NE(s_fid, nullptr); \ + jfieldID i_fid = env_->GetFieldID(c, "iObject", "Ljava/lang/Object;"); \ + ASSERT_NE(i_fid, nullptr); \ + \ + env_->SetStaticObjectField(c, s_fid, s1); \ + ASSERT_TRUE(env_->IsSameObject(s1, env_->GetStaticObjectField(c, s_fid))); \ + env_->SetStaticObjectField(c, s_fid, s2); \ + ASSERT_TRUE(env_->IsSameObject(s2, env_->GetStaticObjectField(c, s_fid))); \ + \ + env_->SetObjectField(o, i_fid, s1); \ + ASSERT_TRUE(env_->IsSameObject(s1, env_->GetObjectField(o, i_fid))); \ + env_->SetObjectField(o, i_fid, s2); \ + ASSERT_TRUE(env_->IsSameObject(s2, env_->GetObjectField(o, i_fid))); \ + } while (false) - env_->SetStaticObjectField(c, s_fid, s1); - ASSERT_TRUE(env_->IsSameObject(s1, env_->GetStaticObjectField(c, s_fid))); - env_->SetStaticObjectField(c, s_fid, s2); - ASSERT_TRUE(env_->IsSameObject(s2, env_->GetStaticObjectField(c, s_fid))); +TEST_F(JniInternalTest, GetObjectField_SetObjectField) { + TEST_OBJECT_FIELD_FOR_CLASS("AllFields"); +} - env_->SetObjectField(o, i_fid, s1); - ASSERT_TRUE(env_->IsSameObject(s1, env_->GetObjectField(o, i_fid))); - env_->SetObjectField(o, i_fid, s2); - ASSERT_TRUE(env_->IsSameObject(s2, env_->GetObjectField(o, i_fid))); +TEST_F(JniInternalTest, GetObjectField_SetObjectField_subclass) { + TEST_OBJECT_FIELD_FOR_CLASS("AllFieldsSub"); } TEST_F(JniInternalTest, NewLocalRef_nullptr) { @@ -2351,15 +2400,15 @@ TEST_F(JniInternalTest, IndirectReferenceTableOffsets) { // Test the offset computation of JNIEnvExt offsets. b/26071368. TEST_F(JniInternalTest, JNIEnvExtOffsets) { - EXPECT_EQ(OFFSETOF_MEMBER(JNIEnvExt, local_ref_cookie), + EXPECT_EQ(OFFSETOF_MEMBER(JNIEnvExt, local_ref_cookie_), JNIEnvExt::LocalRefCookieOffset(sizeof(void*)).Uint32Value()); - EXPECT_EQ(OFFSETOF_MEMBER(JNIEnvExt, self), JNIEnvExt::SelfOffset(sizeof(void*)).Uint32Value()); + EXPECT_EQ(OFFSETOF_MEMBER(JNIEnvExt, self_), JNIEnvExt::SelfOffset(sizeof(void*)).Uint32Value()); // segment_state_ is private in the IndirectReferenceTable. So this test isn't as good as we'd // hope it to be. uint32_t segment_state_now = - OFFSETOF_MEMBER(JNIEnvExt, locals) + + OFFSETOF_MEMBER(JNIEnvExt, locals_) + IndirectReferenceTable::SegmentStateOffset(sizeof(void*)).Uint32Value(); uint32_t segment_state_computed = JNIEnvExt::SegmentStateOffset(sizeof(void*)).Uint32Value(); EXPECT_EQ(segment_state_now, segment_state_computed); diff --git a/runtime/leb128.h b/runtime/leb128.h index 31459af3a0492beb993335c75d13037083a329fe..2bfed7f53962c0dbc0e5d704c41fef1385033afb 100644 --- a/runtime/leb128.h +++ b/runtime/leb128.h @@ -19,8 +19,10 @@ #include +#include + #include "base/bit_utils.h" -#include "base/logging.h" +#include "base/macros.h" #include "globals.h" namespace art { diff --git a/runtime/lock_word.h b/runtime/lock_word.h index b9aa0b793b9e97675dc3adda098a38f2b41c1f02..fac1a7597df205b6a8bb580f9d3d83051ffc6792 100644 --- a/runtime/lock_word.h +++ b/runtime/lock_word.h @@ -20,8 +20,9 @@ #include #include +#include + #include "base/bit_utils.h" -#include "base/logging.h" #include "read_barrier.h" namespace art { diff --git a/runtime/managed_stack-inl.h b/runtime/managed_stack-inl.h index 689dd8009a7feb62869511f4cbbde434ab9d5d46..678be8e0982303075e21a799fde4df1a7567cfc8 100644 --- a/runtime/managed_stack-inl.h +++ b/runtime/managed_stack-inl.h @@ -24,7 +24,7 @@ namespace art { inline ShadowFrame* ManagedStack::PushShadowFrame(ShadowFrame* new_top_frame) { - DCHECK(top_quick_frame_ == nullptr); + DCHECK(!HasTopQuickFrame()); ShadowFrame* old_frame = top_shadow_frame_; top_shadow_frame_ = new_top_frame; new_top_frame->SetLink(old_frame); @@ -32,7 +32,7 @@ inline ShadowFrame* ManagedStack::PushShadowFrame(ShadowFrame* new_top_frame) { } inline ShadowFrame* ManagedStack::PopShadowFrame() { - DCHECK(top_quick_frame_ == nullptr); + DCHECK(!HasTopQuickFrame()); CHECK(top_shadow_frame_ != nullptr); ShadowFrame* frame = top_shadow_frame_; top_shadow_frame_ = frame->GetLink(); diff --git a/runtime/managed_stack.h b/runtime/managed_stack.h index 4f1984d55a98a4294047d33373993312a51e8962..d1c230fd8f7ae6ca4b8504d625c59085725cb698 100644 --- a/runtime/managed_stack.h +++ b/runtime/managed_stack.h @@ -21,9 +21,11 @@ #include #include -#include "base/logging.h" +#include + #include "base/macros.h" #include "base/mutex.h" +#include "base/bit_utils.h" namespace art { @@ -42,7 +44,9 @@ template class StackReference; class PACKED(4) ManagedStack { public: ManagedStack() - : top_quick_frame_(nullptr), link_(nullptr), top_shadow_frame_(nullptr) {} + : tagged_top_quick_frame_(TaggedTopQuickFrame::CreateNotTagged(nullptr)), + link_(nullptr), + top_shadow_frame_(nullptr) {} void PushManagedStackFragment(ManagedStack* fragment) { // Copy this top fragment into given fragment. @@ -63,17 +67,36 @@ class PACKED(4) ManagedStack { return link_; } + ArtMethod** GetTopQuickFrameKnownNotTagged() const { + return tagged_top_quick_frame_.GetSpKnownNotTagged(); + } + ArtMethod** GetTopQuickFrame() const { - return top_quick_frame_; + return tagged_top_quick_frame_.GetSp(); + } + + bool GetTopQuickFrameTag() const { + return tagged_top_quick_frame_.GetTag(); + } + + bool HasTopQuickFrame() const { + return tagged_top_quick_frame_.GetTaggedSp() != 0u; } void SetTopQuickFrame(ArtMethod** top) { DCHECK(top_shadow_frame_ == nullptr); - top_quick_frame_ = top; + DCHECK_ALIGNED(top, 4u); + tagged_top_quick_frame_ = TaggedTopQuickFrame::CreateNotTagged(top); + } + + void SetTopQuickFrameTagged(ArtMethod** top) { + DCHECK(top_shadow_frame_ == nullptr); + DCHECK_ALIGNED(top, 4u); + tagged_top_quick_frame_ = TaggedTopQuickFrame::CreateTagged(top); } - static size_t TopQuickFrameOffset() { - return OFFSETOF_MEMBER(ManagedStack, top_quick_frame_); + static size_t TaggedTopQuickFrameOffset() { + return OFFSETOF_MEMBER(ManagedStack, tagged_top_quick_frame_); } ALWAYS_INLINE ShadowFrame* PushShadowFrame(ShadowFrame* new_top_frame); @@ -83,8 +106,12 @@ class PACKED(4) ManagedStack { return top_shadow_frame_; } + bool HasTopShadowFrame() const { + return GetTopShadowFrame() != nullptr; + } + void SetTopShadowFrame(ShadowFrame* top) { - DCHECK(top_quick_frame_ == nullptr); + DCHECK_EQ(tagged_top_quick_frame_.GetTaggedSp(), 0u); top_shadow_frame_ = top; } @@ -97,7 +124,47 @@ class PACKED(4) ManagedStack { bool ShadowFramesContain(StackReference* shadow_frame_entry) const; private: - ArtMethod** top_quick_frame_; + // Encodes the top quick frame (which must be at least 4-byte aligned) + // and a flag that marks the GenericJNI trampoline. + class TaggedTopQuickFrame { + public: + static TaggedTopQuickFrame CreateNotTagged(ArtMethod** sp) { + DCHECK_ALIGNED(sp, 4u); + return TaggedTopQuickFrame(reinterpret_cast(sp)); + } + + static TaggedTopQuickFrame CreateTagged(ArtMethod** sp) { + DCHECK_ALIGNED(sp, 4u); + return TaggedTopQuickFrame(reinterpret_cast(sp) | 1u); + } + + // Get SP known to be not tagged and non-null. + ArtMethod** GetSpKnownNotTagged() const { + DCHECK(!GetTag()); + DCHECK_NE(tagged_sp_, 0u); + return reinterpret_cast(tagged_sp_); + } + + ArtMethod** GetSp() const { + return reinterpret_cast(tagged_sp_ & ~static_cast(1u)); + } + + bool GetTag() const { + return (tagged_sp_ & 1u) != 0u; + } + + uintptr_t GetTaggedSp() const { + return tagged_sp_; + } + + private: + explicit TaggedTopQuickFrame(uintptr_t tagged_sp) : tagged_sp_(tagged_sp) { } + + uintptr_t tagged_sp_; + }; + static_assert(sizeof(TaggedTopQuickFrame) == sizeof(uintptr_t), "TaggedTopQuickFrame size check"); + + TaggedTopQuickFrame tagged_top_quick_frame_; ManagedStack* link_; ShadowFrame* top_shadow_frame_; }; diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc index 7f68d2faa010c6e7826dc2c2800ce4707612e073..8abf8a6003ebaf88010f7b8aac38be19172d656a 100644 --- a/runtime/mem_map.cc +++ b/runtime/mem_map.cc @@ -35,6 +35,7 @@ #include "base/allocator.h" #include "base/bit_utils.h" #include "base/file_utils.h" +#include "base/logging.h" // For VLOG_IS_ON. #include "base/memory_tool.h" #include "globals.h" #include "utils.h" @@ -59,14 +60,15 @@ static Maps* gMaps GUARDED_BY(MemMap::GetMemMapsLock()) = nullptr; static std::ostream& operator<<( std::ostream& os, - std::pair iters) { - for (BacktraceMap::const_iterator it = iters.first; it != iters.second; ++it) { + std::pair iters) { + for (BacktraceMap::iterator it = iters.first; it != iters.second; ++it) { + const backtrace_map_t* entry = *it; os << StringPrintf("0x%08x-0x%08x %c%c%c %s\n", - static_cast(it->start), - static_cast(it->end), - (it->flags & PROT_READ) ? 'r' : '-', - (it->flags & PROT_WRITE) ? 'w' : '-', - (it->flags & PROT_EXEC) ? 'x' : '-', it->name.c_str()); + static_cast(entry->start), + static_cast(entry->end), + (entry->flags & PROT_READ) ? 'r' : '-', + (entry->flags & PROT_WRITE) ? 'w' : '-', + (entry->flags & PROT_EXEC) ? 'x' : '-', entry->name.c_str()); } return os; } @@ -170,9 +172,10 @@ bool MemMap::ContainedWithinExistingMap(uint8_t* ptr, size_t size, std::string* } ScopedBacktraceMapIteratorLock lock(map.get()); - for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) { - if ((begin >= it->start && begin < it->end) // start of new within old - && (end > it->start && end <= it->end)) { // end of new within old + for (BacktraceMap::iterator it = map->begin(); it != map->end(); ++it) { + const backtrace_map_t* entry = *it; + if ((begin >= entry->start && begin < entry->end) // start of new within old + && (end > entry->start && end <= entry->end)) { // end of new within old return true; } } @@ -194,17 +197,18 @@ static bool CheckNonOverlapping(uintptr_t begin, return false; } ScopedBacktraceMapIteratorLock lock(map.get()); - for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) { - if ((begin >= it->start && begin < it->end) // start of new within old - || (end > it->start && end < it->end) // end of new within old - || (begin <= it->start && end > it->end)) { // start/end of new includes all of old + for (BacktraceMap::iterator it = map->begin(); it != map->end(); ++it) { + const backtrace_map_t* entry = *it; + if ((begin >= entry->start && begin < entry->end) // start of new within old + || (end > entry->start && end < entry->end) // end of new within old + || (begin <= entry->start && end > entry->end)) { // start/end of new includes all of old std::ostringstream map_info; map_info << std::make_pair(it, map->end()); *error_msg = StringPrintf("Requested region 0x%08" PRIxPTR "-0x%08" PRIxPTR " overlaps with " "existing map 0x%08" PRIxPTR "-0x%08" PRIxPTR " (%s)\n%s", begin, end, - static_cast(it->start), static_cast(it->end), - it->name.c_str(), + static_cast(entry->start), static_cast(entry->end), + entry->name.c_str(), map_info.str().c_str()); return false; } diff --git a/runtime/mem_map.h b/runtime/mem_map.h index 36a24169d57955b1b4ab1f95e5b0c4c1af2183a4..5603963eacf614c713968bfce5b59bb10cc48361 100644 --- a/runtime/mem_map.h +++ b/runtime/mem_map.h @@ -21,7 +21,7 @@ #include #include -#include // NOLINT [build/c++11] [5] +#include #include #include "android-base/thread_annotations.h" diff --git a/runtime/memory_region.cc b/runtime/memory_region.cc index 13cc5c99bc1c67f0a9468eb0b0658d091324fd1b..862ff736396d5f3250584ff1a41ab1182e544598 100644 --- a/runtime/memory_region.cc +++ b/runtime/memory_region.cc @@ -19,9 +19,6 @@ #include #include -#include "base/logging.h" -#include "globals.h" - namespace art { void MemoryRegion::CopyFrom(size_t offset, const MemoryRegion& from) const { diff --git a/runtime/memory_region.h b/runtime/memory_region.h index 7cf5d49d70c192325044156ec5635f6f209ed85d..23e0aecbda5f83c8b44a23d4526d0c9b8dab61fd 100644 --- a/runtime/memory_region.h +++ b/runtime/memory_region.h @@ -20,10 +20,11 @@ #include #include +#include + #include "arch/instruction_set.h" #include "base/bit_utils.h" #include "base/casts.h" -#include "base/logging.h" #include "base/macros.h" #include "base/value_object.h" #include "globals.h" diff --git a/runtime/method_bss_mapping.h b/runtime/method_bss_mapping.h deleted file mode 100644 index 1476f93e2153b175dc60e77565f85baf2dcae737..0000000000000000000000000000000000000000 --- a/runtime/method_bss_mapping.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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_RUNTIME_METHOD_BSS_MAPPING_H_ -#define ART_RUNTIME_METHOD_BSS_MAPPING_H_ - -#include "base/bit_utils.h" -#include "base/length_prefixed_array.h" - -namespace art { - -// MethodBssMappingEntry describes a mapping of up to 17 method indexes to their offsets -// in the .bss. The highest index and its associated .bss offset are stored in plain form -// as `method_index` and `bss_offset`, respectively, while the additional indexes can be -// stored in compressed form if their associated .bss entries are consecutive and in the -// method index order. Each of the 16 bits of the `index_mask` corresponds to one of the -// previous 16 method indexes and indicates whether there is a .bss entry for that index. -// -struct MethodBssMappingEntry { - bool CoversIndex(uint32_t method_idx) const { - uint32_t diff = method_index - method_idx; - return (diff == 0) || (diff <= 16 && ((index_mask >> (16u - diff)) & 1u) != 0); - } - - uint32_t GetBssOffset(uint32_t method_idx, size_t entry_size) const { - DCHECK(CoversIndex(method_idx)); - uint32_t diff = method_index - method_idx; - if (diff == 0) { - return bss_offset; - } else { - return bss_offset - POPCOUNT(index_mask >> (16u - diff)) * entry_size; - } - } - - uint16_t method_index; - uint16_t index_mask; - uint32_t bss_offset; -}; - -using MethodBssMapping = LengthPrefixedArray; - -} // namespace art - -#endif // ART_RUNTIME_METHOD_BSS_MAPPING_H_ diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc index 5a5d5713a80a154a34580555737d5e3d00d9c2db..88f30a89009dc39da64f8d0163dcc26cac249434 100644 --- a/runtime/method_handles.cc +++ b/runtime/method_handles.cc @@ -299,17 +299,14 @@ bool ConvertJValueCommon( namespace { -template inline void CopyArgumentsFromCallerFrame(const ShadowFrame& caller_frame, ShadowFrame* callee_frame, - const uint32_t (&args)[Instruction::kMaxVarArgRegs], - uint32_t first_arg, - const size_t first_dst_reg, - const size_t num_regs) + const InstructionOperands* const operands, + const size_t first_dst_reg) REQUIRES_SHARED(Locks::mutator_lock_) { - for (size_t i = 0; i < num_regs; ++i) { + for (size_t i = 0; i < operands->GetNumberOfOperands(); ++i) { size_t dst_reg = first_dst_reg + i; - size_t src_reg = is_range ? (first_arg + i) : args[i]; + size_t src_reg = operands->GetOperand(i); // Uint required, so that sign extension does not make this wrong on 64-bit systems uint32_t src_value = caller_frame.GetVReg(src_reg); ObjPtr o = caller_frame.GetVRegReference(src_reg); @@ -324,15 +321,13 @@ inline void CopyArgumentsFromCallerFrame(const ShadowFrame& caller_frame, } } -template inline bool ConvertAndCopyArgumentsFromCallerFrame( Thread* self, Handle callsite_type, Handle callee_type, const ShadowFrame& caller_frame, - const uint32_t (&args)[Instruction::kMaxVarArgRegs], - uint32_t first_arg, - uint32_t first_dst_reg, + uint32_t first_dest_reg, + const InstructionOperands* const operands, ShadowFrame* callee_frame) REQUIRES_SHARED(Locks::mutator_lock_) { ObjPtr> from_types(callsite_type->GetPTypes()); @@ -344,15 +339,14 @@ inline bool ConvertAndCopyArgumentsFromCallerFrame( return false; } - ShadowFrameGetter getter(first_arg, args, caller_frame); - ShadowFrameSetter setter(callee_frame, first_dst_reg); - - return PerformConversions, ShadowFrameSetter>(self, - callsite_type, - callee_type, - &getter, - &setter, - num_method_params); + ShadowFrameGetter getter(operands, caller_frame); + ShadowFrameSetter setter(callee_frame, first_dest_reg); + return PerformConversions(self, + callsite_type, + callee_type, + &getter, + &setter, + num_method_params); } inline bool IsInvoke(const mirror::MethodHandle::Kind handle_kind) { @@ -406,17 +400,15 @@ static inline bool IsCallerTransformer(Handle callsite_type) return false; } -template static inline bool MethodHandleInvokeMethod(ArtMethod* called_method, Handle callsite_type, Handle target_type, Thread* self, ShadowFrame& shadow_frame, - const uint32_t (&args)[Instruction::kMaxVarArgRegs], - uint32_t first_arg, + const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { // Compute method information. - const DexFile::CodeItem* code_item = called_method->GetCodeItem(); + CodeItemDataAccessor accessor(called_method); // Number of registers for the callee's call frame. Note that for non-exact // invokes, we always derive this information from the callee method. We @@ -427,10 +419,10 @@ static inline bool MethodHandleInvokeMethod(ArtMethod* called_method, uint16_t num_regs; size_t num_input_regs; size_t first_dest_reg; - if (LIKELY(code_item != nullptr)) { - num_regs = code_item->registers_size_; - first_dest_reg = num_regs - code_item->ins_size_; - num_input_regs = code_item->ins_size_; + if (LIKELY(accessor.HasCodeItem())) { + num_regs = accessor.RegistersSize(); + first_dest_reg = num_regs - accessor.InsSize(); + num_input_regs = accessor.InsSize(); // Parameter registers go at the end of the shadow frame. DCHECK_NE(first_dest_reg, (size_t)-1); } else { @@ -455,12 +447,10 @@ static inline bool MethodHandleInvokeMethod(ArtMethod* called_method, if (callsite_type->IsExactMatch(target_type.Get())) { // This is an exact invoke, we can take the fast path of just copying all // registers without performing any argument conversions. - CopyArgumentsFromCallerFrame(shadow_frame, - new_shadow_frame, - args, - first_arg, - first_dest_reg, - num_input_regs); + CopyArgumentsFromCallerFrame(shadow_frame, + new_shadow_frame, + operands, + first_dest_reg); } else { // This includes the case where we're entering this invoke-polymorphic // from a transformer method. In that case, the callsite_type will contain @@ -471,7 +461,7 @@ static inline bool MethodHandleInvokeMethod(ArtMethod* called_method, is_caller_transformer = true; // The emulated stack frame is the first and only argument when we're coming // through from a transformer. - size_t first_arg_register = (is_range) ? first_arg : args[0]; + size_t first_arg_register = operands->GetOperand(0); ObjPtr emulated_stack_frame( reinterpret_cast( shadow_frame.GetVRegReference(first_arg_register))); @@ -488,14 +478,13 @@ static inline bool MethodHandleInvokeMethod(ArtMethod* called_method, ThrowWrongMethodTypeException(target_type.Get(), callsite_type.Get()); return false; } - if (!ConvertAndCopyArgumentsFromCallerFrame(self, - callsite_type, - target_type, - shadow_frame, - args, - first_arg, - first_dest_reg, - new_shadow_frame)) { + if (!ConvertAndCopyArgumentsFromCallerFrame(self, + callsite_type, + target_type, + shadow_frame, + first_dest_reg, + operands, + new_shadow_frame)) { DCHECK(self->IsExceptionPending()); result->SetL(0); return false; @@ -507,7 +496,7 @@ static inline bool MethodHandleInvokeMethod(ArtMethod* called_method, bool use_interpreter_entrypoint = ClassLinker::ShouldUseInterpreterEntrypoint( called_method, called_method->GetEntryPointFromQuickCompiledCode()); PerformCall(self, - code_item, + accessor, shadow_frame.GetMethod(), first_dest_reg, new_shadow_frame, @@ -521,7 +510,7 @@ static inline bool MethodHandleInvokeMethod(ArtMethod* called_method, // we need to copy the result back out to the emulated stack frame. if (is_caller_transformer) { StackHandleScope<2> hs(self); - size_t first_callee_register = is_range ? (first_arg) : args[0]; + size_t first_callee_register = operands->GetOperand(0); Handle emulated_stack_frame( hs.NewHandle(reinterpret_cast( shadow_frame.GetVRegReference(first_callee_register)))); @@ -541,15 +530,13 @@ static inline bool MethodHandleInvokeMethod(ArtMethod* called_method, return ConvertReturnValue(callsite_type, target_type, result); } -template static inline bool MethodHandleInvokeTransform(ArtMethod* called_method, Handle callsite_type, Handle callee_type, Thread* self, ShadowFrame& shadow_frame, Handle receiver, - const uint32_t (&args)[Instruction::kMaxVarArgRegs], - uint32_t first_arg, + const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { // This can be fixed to two, because the method we're calling here @@ -563,10 +550,9 @@ static inline bool MethodHandleInvokeTransform(ArtMethod* called_method, // - One for the only method argument (an EmulatedStackFrame). static constexpr size_t kNumRegsForTransform = 2; - const DexFile::CodeItem* code_item = called_method->GetCodeItem(); - DCHECK(code_item != nullptr); - DCHECK_EQ(kNumRegsForTransform, code_item->registers_size_); - DCHECK_EQ(kNumRegsForTransform, code_item->ins_size_); + CodeItemDataAccessor accessor(called_method); + DCHECK_EQ(kNumRegsForTransform, accessor.RegistersSize()); + DCHECK_EQ(kNumRegsForTransform, accessor.InsSize()); ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr = CREATE_SHADOW_FRAME(kNumRegsForTransform, &shadow_frame, called_method, /* dex pc */ 0); @@ -578,16 +564,15 @@ static inline bool MethodHandleInvokeTransform(ArtMethod* called_method, // If we're entering this transformer from another transformer, we can pass // through the handle directly to the callee, instead of having to // instantiate a new stack frame based on the shadow frame. - size_t first_callee_register = is_range ? first_arg : args[0]; + size_t first_callee_register = operands->GetOperand(0); sf.Assign(reinterpret_cast( shadow_frame.GetVRegReference(first_callee_register))); } else { - sf.Assign(mirror::EmulatedStackFrame::CreateFromShadowFrameAndArgs(self, - callsite_type, - callee_type, - shadow_frame, - first_arg, - args)); + sf.Assign(mirror::EmulatedStackFrame::CreateFromShadowFrameAndArgs(self, + callsite_type, + callee_type, + shadow_frame, + operands)); // Something went wrong while creating the emulated stack frame, we should // throw the pending exception. @@ -603,7 +588,7 @@ static inline bool MethodHandleInvokeTransform(ArtMethod* called_method, bool use_interpreter_entrypoint = ClassLinker::ShouldUseInterpreterEntrypoint( called_method, called_method->GetEntryPointFromQuickCompiledCode()); PerformCall(self, - code_item, + accessor, shadow_frame.GetMethod(), 0 /* first destination register */, new_shadow_frame, @@ -699,13 +684,11 @@ ArtMethod* RefineTargetMethod(Thread* self, return target_method; } -template bool DoInvokePolymorphicMethod(Thread* self, ShadowFrame& shadow_frame, Handle method_handle, Handle callsite_type, - const uint32_t (&args)[Instruction::kMaxVarArgRegs], - uint32_t first_arg, + const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { StackHandleScope<1> hs(self); @@ -718,7 +701,7 @@ bool DoInvokePolymorphicMethod(Thread* self, // point because they would have been performed on our behalf at the point // of creation of the method handle. ArtMethod* target_method = method_handle->GetTargetMethod(); - uint32_t receiver_reg = is_range ? first_arg: args[0]; + uint32_t receiver_reg = (operands->GetNumberOfOperands() > 0) ? operands->GetOperand(0) : 0u; ArtMethod* called_method = RefineTargetMethod(self, shadow_frame, handle_kind, @@ -743,24 +726,22 @@ bool DoInvokePolymorphicMethod(Thread* self, Handle callee_type = (handle_kind == mirror::MethodHandle::Kind::kInvokeCallSiteTransform) ? callsite_type : handle_type; - return MethodHandleInvokeTransform(called_method, - callsite_type, - callee_type, - self, - shadow_frame, - method_handle /* receiver */, - args, - first_arg, - result); + return MethodHandleInvokeTransform(called_method, + callsite_type, + callee_type, + self, + shadow_frame, + method_handle /* receiver */, + operands, + result); } else { - return MethodHandleInvokeMethod(called_method, - callsite_type, - handle_type, - self, - shadow_frame, - args, - first_arg, - result); + return MethodHandleInvokeMethod(called_method, + callsite_type, + handle_type, + self, + shadow_frame, + operands, + result); } } @@ -884,23 +865,21 @@ static JValue GetValueFromShadowFrame(const ShadowFrame& shadow_frame, return field_value; } -template +template bool MethodHandleFieldAccess(Thread* self, ShadowFrame& shadow_frame, Handle method_handle, Handle callsite_type, - const uint32_t (&args)[Instruction::kMaxVarArgRegs], - uint32_t first_arg, + const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { StackHandleScope<1> hs(self); Handle handle_type(hs.NewHandle(method_handle->GetMethodType())); const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind(); ArtField* field = method_handle->GetTargetField(); Primitive::Type field_type = field->GetTypeAsPrimitiveType(); - switch (handle_kind) { case mirror::MethodHandle::kInstanceGet: { - size_t obj_reg = is_range ? first_arg : args[0]; + size_t obj_reg = operands->GetOperand(0); ObjPtr obj = shadow_frame.GetVRegReference(obj_reg); MethodHandleFieldGet(self, shadow_frame, obj, field, field_type, result); if (do_conversions && !ConvertReturnValue(callsite_type, handle_type, result)) { @@ -923,8 +902,8 @@ bool MethodHandleFieldAccess(Thread* self, return true; } case mirror::MethodHandle::kInstancePut: { - size_t obj_reg = is_range ? first_arg : args[0]; - size_t value_reg = is_range ? (first_arg + 1) : args[1]; + size_t obj_reg = operands->GetOperand(0); + size_t value_reg = operands->GetOperand(1); const size_t kPTypeIndex = 1; // Use ptypes instead of field type since we may be unboxing a reference for a primitive // field. The field type is incorrect for this case. @@ -948,7 +927,7 @@ bool MethodHandleFieldAccess(Thread* self, DCHECK(self->IsExceptionPending()); return false; } - size_t value_reg = is_range ? first_arg : args[0]; + size_t value_reg = operands->GetOperand(0); const size_t kPTypeIndex = 0; // Use ptypes instead of field type since we may be unboxing a reference for a primitive // field. The field type is incorrect for this case. @@ -971,13 +950,11 @@ bool MethodHandleFieldAccess(Thread* self, } } -template static inline bool MethodHandleInvokeInternal(Thread* self, ShadowFrame& shadow_frame, Handle method_handle, Handle callsite_type, - const uint32_t (&args)[Instruction::kMaxVarArgRegs], - uint32_t first_arg, + const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind(); @@ -989,32 +966,28 @@ static inline bool MethodHandleInvokeInternal(Thread* self, return false; } const bool do_convert = true; - return MethodHandleFieldAccess( + return MethodHandleFieldAccess( self, shadow_frame, method_handle, callsite_type, - args, - first_arg, + operands, result); } - return DoInvokePolymorphicMethod(self, - shadow_frame, - method_handle, - callsite_type, - args, - first_arg, - result); + return DoInvokePolymorphicMethod(self, + shadow_frame, + method_handle, + callsite_type, + operands, + result); } -template static inline bool MethodHandleInvokeExactInternal( Thread* self, ShadowFrame& shadow_frame, Handle method_handle, Handle callsite_type, - const uint32_t (&args)[Instruction::kMaxVarArgRegs], - uint32_t first_arg, + const InstructionOperands* const operands, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { StackHandleScope<1> hs(self); @@ -1027,29 +1000,27 @@ static inline bool MethodHandleInvokeExactInternal( const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind(); if (IsFieldAccess(handle_kind)) { const bool do_convert = false; - return MethodHandleFieldAccess(self, - shadow_frame, - method_handle, - callsite_type, - args, - first_arg, - result); - } - - // Slow-path check. - if (IsInvokeTransform(handle_kind) || IsCallerTransformer(callsite_type)) { - return DoInvokePolymorphicMethod(self, + return MethodHandleFieldAccess(self, shadow_frame, method_handle, callsite_type, - args, - first_arg, + operands, result); } + // Slow-path check. + if (IsInvokeTransform(handle_kind) || IsCallerTransformer(callsite_type)) { + return DoInvokePolymorphicMethod(self, + shadow_frame, + method_handle, + callsite_type, + operands, + result); + } + // On the fast-path. This is equivalent to DoCallPolymoprhic without the conversion paths. ArtMethod* target_method = method_handle->GetTargetMethod(); - uint32_t receiver_reg = is_range ? first_arg : args[0]; + uint32_t receiver_reg = (operands->GetNumberOfOperands() > 0) ? operands->GetOperand(0) : 0u; ArtMethod* called_method = RefineTargetMethod(self, shadow_frame, handle_kind, @@ -1063,14 +1034,14 @@ static inline bool MethodHandleInvokeExactInternal( } // Compute method information. - const DexFile::CodeItem* code_item = called_method->GetCodeItem(); + CodeItemDataAccessor accessor(called_method); uint16_t num_regs; size_t num_input_regs; size_t first_dest_reg; - if (LIKELY(code_item != nullptr)) { - num_regs = code_item->registers_size_; - first_dest_reg = num_regs - code_item->ins_size_; - num_input_regs = code_item->ins_size_; + if (LIKELY(accessor.HasCodeItem())) { + num_regs = accessor.RegistersSize(); + first_dest_reg = num_regs - accessor.InsSize(); + num_input_regs = accessor.InsSize(); // Parameter registers go at the end of the shadow frame. DCHECK_NE(first_dest_reg, (size_t)-1); } else { @@ -1085,18 +1056,16 @@ static inline bool MethodHandleInvokeExactInternal( ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr = CREATE_SHADOW_FRAME(num_regs, &shadow_frame, called_method, /* dex pc */ 0); ShadowFrame* new_shadow_frame = shadow_frame_unique_ptr.get(); - CopyArgumentsFromCallerFrame(shadow_frame, - new_shadow_frame, - args, - first_arg, - first_dest_reg, - num_input_regs); + CopyArgumentsFromCallerFrame(shadow_frame, + new_shadow_frame, + operands, + first_dest_reg); self->EndAssertNoThreadSuspension(old_cause); bool use_interpreter_entrypoint = ClassLinker::ShouldUseInterpreterEntrypoint( called_method, called_method->GetEntryPointFromQuickCompiledCode()); PerformCall(self, - code_item, + accessor, shadow_frame.GetMethod(), first_dest_reg, new_shadow_frame, @@ -1110,43 +1079,37 @@ static inline bool MethodHandleInvokeExactInternal( } // namespace -template -inline bool MethodHandleInvoke(Thread* self, - ShadowFrame& shadow_frame, - Handle method_handle, - Handle callsite_type, - const uint32_t (&args)[Instruction::kMaxVarArgRegs], - uint32_t first_arg, - JValue* result) +bool MethodHandleInvoke(Thread* self, + ShadowFrame& shadow_frame, + Handle method_handle, + Handle callsite_type, + const InstructionOperands* const operands, + JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { if (UNLIKELY(callsite_type->IsExactMatch(method_handle->GetMethodType()))) { // A non-exact invoke that can be invoked exactly. - return MethodHandleInvokeExactInternal(self, - shadow_frame, - method_handle, - callsite_type, - args, - first_arg, - result); + return MethodHandleInvokeExactInternal(self, + shadow_frame, + method_handle, + callsite_type, + operands, + result); } else { - return MethodHandleInvokeInternal(self, - shadow_frame, - method_handle, - callsite_type, - args, - first_arg, - result); + return MethodHandleInvokeInternal(self, + shadow_frame, + method_handle, + callsite_type, + operands, + result); } } -template bool MethodHandleInvokeExact(Thread* self, - ShadowFrame& shadow_frame, - Handle method_handle, - Handle callsite_type, - const uint32_t (&args)[Instruction::kMaxVarArgRegs], - uint32_t first_arg, - JValue* result) + ShadowFrame& shadow_frame, + Handle method_handle, + Handle callsite_type, + const InstructionOperands* const operands, + JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { // We need to check the nominal type of the handle in addition to the // real type. The "nominal" type is present when MethodHandle.asType is @@ -1160,39 +1123,20 @@ bool MethodHandleInvokeExact(Thread* self, } if (LIKELY(!nominal_type->IsExactMatch(method_handle->GetMethodType()))) { // Different nominal type means we have to treat as non-exact. - return MethodHandleInvokeInternal(self, - shadow_frame, - method_handle, - callsite_type, - args, - first_arg, - result); + return MethodHandleInvokeInternal(self, + shadow_frame, + method_handle, + callsite_type, + operands, + result); } } - return MethodHandleInvokeExactInternal(self, - shadow_frame, - method_handle, - callsite_type, - args, - first_arg, - result); + return MethodHandleInvokeExactInternal(self, + shadow_frame, + method_handle, + callsite_type, + operands, + result); } -#define EXPLICIT_DO_METHOD_HANDLE_METHOD(_name, _is_range) \ - template REQUIRES_SHARED(Locks::mutator_lock_) \ - bool MethodHandle##_name<_is_range>( \ - Thread* self, \ - ShadowFrame& shadow_frame, \ - Handle method_handle, \ - Handle callsite_type, \ - const uint32_t (&args)[Instruction::kMaxVarArgRegs], \ - uint32_t first_arg, \ - JValue* result) - -EXPLICIT_DO_METHOD_HANDLE_METHOD(Invoke, true); -EXPLICIT_DO_METHOD_HANDLE_METHOD(Invoke, false); -EXPLICIT_DO_METHOD_HANDLE_METHOD(InvokeExact, true); -EXPLICIT_DO_METHOD_HANDLE_METHOD(InvokeExact, false); -#undef EXPLICIT_DO_METHOD_HANDLE_METHOD - } // namespace art diff --git a/runtime/method_handles.h b/runtime/method_handles.h index 930b8db63ea48462016cfb79339981c86f159f9d..bc74bf23d2758a66977da7dd02b1ec289fdedff5 100644 --- a/runtime/method_handles.h +++ b/runtime/method_handles.h @@ -126,50 +126,40 @@ bool PerformConversions(Thread* self, int32_t num_conversions) REQUIRES_SHARED(Locks::mutator_lock_); // A convenience class that allows for iteration through a list of -// input argument registers |arg| for non-range invokes or a list of -// consecutive registers starting with a given based for range -// invokes. -// -// This is used to iterate over input arguments while performing standard -// argument conversions. -template +// input argument registers. This is used to iterate over input +// arguments while performing standard argument conversions. class ShadowFrameGetter { public: - ShadowFrameGetter(size_t first_src_reg, - const uint32_t (&arg)[Instruction::kMaxVarArgRegs], - const ShadowFrame& shadow_frame) : - first_src_reg_(first_src_reg), - arg_(arg), - shadow_frame_(shadow_frame), - arg_index_(0) { - } + ShadowFrameGetter(const InstructionOperands* const operands, const ShadowFrame& shadow_frame) + : operands_(operands), operand_index_(0), shadow_frame_(shadow_frame) {} ALWAYS_INLINE uint32_t Get() REQUIRES_SHARED(Locks::mutator_lock_) { - const uint32_t next = (is_range ? first_src_reg_ + arg_index_ : arg_[arg_index_]); - ++arg_index_; - - return shadow_frame_.GetVReg(next); + return shadow_frame_.GetVReg(Next()); } ALWAYS_INLINE int64_t GetLong() REQUIRES_SHARED(Locks::mutator_lock_) { - const uint32_t next = (is_range ? first_src_reg_ + arg_index_ : arg_[arg_index_]); - arg_index_ += 2; - - return shadow_frame_.GetVRegLong(next); + return shadow_frame_.GetVRegLong(NextLong()); } ALWAYS_INLINE ObjPtr GetReference() REQUIRES_SHARED(Locks::mutator_lock_) { - const uint32_t next = (is_range ? first_src_reg_ + arg_index_ : arg_[arg_index_]); - ++arg_index_; - - return shadow_frame_.GetVRegReference(next); + return shadow_frame_.GetVRegReference(Next()); } private: - const size_t first_src_reg_; - const uint32_t (&arg_)[Instruction::kMaxVarArgRegs]; + uint32_t Next() { + const uint32_t next = operands_->GetOperand(operand_index_); + operand_index_ += 1; + return next; + } + uint32_t NextLong() { + const uint32_t next = operands_->GetOperand(operand_index_); + operand_index_ += 2; + return next; + } + + const InstructionOperands* const operands_; + size_t operand_index_; // the next register operand to read from frame const ShadowFrame& shadow_frame_; - size_t arg_index_; }; // A convenience class that allows values to be written to a given shadow frame, @@ -201,23 +191,19 @@ class ShadowFrameSetter { size_t arg_index_; }; -template bool MethodHandleInvoke(Thread* self, ShadowFrame& shadow_frame, Handle method_handle, Handle callsite_type, - const uint32_t (&args)[Instruction::kMaxVarArgRegs], - uint32_t first_arg, + const InstructionOperands* const args, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_); -template bool MethodHandleInvokeExact(Thread* self, ShadowFrame& shadow_frame, Handle method_handle, Handle callsite_type, - const uint32_t (&args)[Instruction::kMaxVarArgRegs], - uint32_t first_arg, + const InstructionOperands* const args, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/method_info.h b/runtime/method_info.h index 5a72125be4fdc038889460238ae67ccc3def5161..6485af992dd2911b42d78c493ebc4b79138c9caf 100644 --- a/runtime/method_info.h +++ b/runtime/method_info.h @@ -17,7 +17,9 @@ #ifndef ART_RUNTIME_METHOD_INFO_H_ #define ART_RUNTIME_METHOD_INFO_H_ -#include "base/logging.h" +#include + +#include "base/macros.h" #include "leb128.h" #include "memory_region.h" diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h index 22812454d11d979a0c42bc63d5aa9e0b4395b1d0..636c84c7597e6cdb569531d137bba247de2c5165 100644 --- a/runtime/mirror/array-inl.h +++ b/runtime/mirror/array-inl.h @@ -19,11 +19,11 @@ #include "array.h" -#include "android-base/stringprintf.h" +#include +#include #include "base/bit_utils.h" #include "base/casts.h" -#include "base/logging.h" #include "class.h" #include "gc/heap-inl.h" #include "obj_ptr-inl.h" diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index eb54f7fb1fae98ed377641d0cf97af0101839b43..b4f5d81067921fd8db1e37a99e07cc98b7994d28 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -440,7 +440,6 @@ inline bool Class::ResolvedFieldAccessTest(ObjPtr access_to, // cache. Use LookupResolveType here to search the class table if it is not in the dex cache. // should be no thread suspension due to the class being resolved. ObjPtr dex_access_to = Runtime::Current()->GetClassLinker()->LookupResolvedType( - *dex_cache->GetDexFile(), class_idx, dex_cache, access_to->GetClassLoader()); @@ -477,7 +476,6 @@ inline bool Class::ResolvedMethodAccessTest(ObjPtr access_to, // The referenced class has already been resolved with the method, but may not be in the dex // cache. ObjPtr dex_access_to = Runtime::Current()->GetClassLinker()->LookupResolvedType( - *dex_cache->GetDexFile(), class_idx, dex_cache, access_to->GetClassLoader()); diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 4d810dbce01bdb92479928a6f5f96bf732b1bdd8..e0a341da67b6e9aa3e4b78888a7ce2d4a6c94c60 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -20,6 +20,7 @@ #include "art_field-inl.h" #include "art_method-inl.h" +#include "base/logging.h" // For VLOG. #include "class-inl.h" #include "class_ext.h" #include "class_linker-inl.h" @@ -1034,7 +1035,7 @@ ObjPtr Class::GetDirectInterface(Thread* self, ObjPtr klass, uint3 return interfaces->Get(idx); } else { dex::TypeIndex type_idx = klass->GetDirectInterfaceTypeIdx(idx); - ObjPtr interface = ClassLinker::LookupResolvedType( + ObjPtr interface = Runtime::Current()->GetClassLinker()->LookupResolvedType( type_idx, klass->GetDexCache(), klass->GetClassLoader()); return interface; } @@ -1046,9 +1047,7 @@ ObjPtr Class::ResolveDirectInterface(Thread* self, Handle klass, u DCHECK(!klass->IsArrayClass()); DCHECK(!klass->IsProxyClass()); dex::TypeIndex type_idx = klass->GetDirectInterfaceTypeIdx(idx); - interface = Runtime::Current()->GetClassLinker()->ResolveType(klass->GetDexFile(), - type_idx, - klass.Get()); + interface = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, klass.Get()); CHECK(interface != nullptr || self->IsExceptionPending()); } return interface; @@ -1244,7 +1243,6 @@ ObjPtr Class::GetDeclaredMethodInternal( // still return a synthetic method to handle situations like // escalated visibility. We never return miranda methods that // were synthesized by the runtime. - constexpr uint32_t kSkipModifiers = kAccMiranda | kAccSynthetic; StackHandleScope<3> hs(self); auto h_method_name = hs.NewHandle(name); if (UNLIKELY(h_method_name == nullptr)) { @@ -1264,11 +1262,10 @@ ObjPtr Class::GetDeclaredMethodInternal( } continue; } - auto modifiers = m.GetAccessFlags(); - if ((modifiers & kSkipModifiers) == 0) { - return Method::CreateFromArtMethod(self, &m); - } - if ((modifiers & kAccMiranda) == 0) { + if (!m.IsMiranda()) { + if (!m.IsSynthetic()) { + return Method::CreateFromArtMethod(self, &m); + } result = &m; // Remember as potential result if it's not a miranda method. } } @@ -1291,11 +1288,11 @@ ObjPtr Class::GetDeclaredMethodInternal( } continue; } - if ((modifiers & kSkipModifiers) == 0) { + DCHECK(!m.IsMiranda()); // Direct methods cannot be miranda methods. + if ((modifiers & kAccSynthetic) == 0) { return Method::CreateFromArtMethod(self, &m); } - // Direct methods cannot be miranda methods, so this potential result must be synthetic. - result = &m; + result = &m; // Remember as potential result. } } return result != nullptr diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index bf49f513398e7e1e47412284279292fb59c1429c..c545a9b7d5ddc2ccb3cc22340b6c6869ab568714 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -286,7 +286,7 @@ class MANAGED Class FINAL : public Object { // This does not necessarily mean that access checks are avoidable, // since the class methods might still need to be run with access checks. bool WasVerificationAttempted() REQUIRES_SHARED(Locks::mutator_lock_) { - return (GetAccessFlags() & kAccSkipAccessChecks) != 0; + return (GetAccessFlags() & kAccVerificationAttempted) != 0; } // Mark the class as having gone through a verification attempt. diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h index 8b11c1290dd91b3f39e6a5b16e80fcb5fe230205..8d4d44b6f9ac51f81f63bb37dbdfe2a336ee345d 100644 --- a/runtime/mirror/dex_cache-inl.h +++ b/runtime/mirror/dex_cache-inl.h @@ -19,11 +19,12 @@ #include "dex_cache.h" +#include + #include "art_field.h" #include "art_method.h" #include "base/casts.h" #include "base/enums.h" -#include "base/logging.h" #include "class_linker.h" #include "dex_file.h" #include "gc/heap-inl.h" diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc index 2f63dff3be679f3d41c653bb1699a46a3d15239f..eb4db00ccd7d3508c2d18f56d9cfc0f9907a573a 100644 --- a/runtime/mirror/dex_cache.cc +++ b/runtime/mirror/dex_cache.cc @@ -17,7 +17,6 @@ #include "dex_cache-inl.h" #include "art_method-inl.h" -#include "base/logging.h" #include "class_linker.h" #include "gc/accounting/card_table-inl.h" #include "gc/heap.h" diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h index f75786b521daf27c1063ed102b3d5aa25cd3098e..509db0292e7ea102fac121a2638d91bfcf4b8f26 100644 --- a/runtime/mirror/dex_cache.h +++ b/runtime/mirror/dex_cache.h @@ -19,6 +19,7 @@ #include "array.h" #include "base/bit_utils.h" +#include "base/mutex.h" #include "dex_file_types.h" #include "object.h" #include "object_array.h" diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc index 8198636b3dc4f1df4b7ad7c84a1ee66ad1adcd68..d2bff2c19af6b41910e1125a46ace2365d3a26bf 100644 --- a/runtime/mirror/dex_cache_test.cc +++ b/runtime/mirror/dex_cache_test.cc @@ -150,13 +150,11 @@ TEST_F(DexCacheMethodHandlesTest, TestResolvedMethodTypes) { const DexFile::MethodId& method2_id = dex_file.GetMethodId(method2->GetDexMethodIndex()); Handle method1_type = hs.NewHandle( class_linker_->ResolveMethodType(soa.Self(), - dex_file, method1_id.proto_idx_, dex_cache, class_loader)); Handle method2_type = hs.NewHandle( class_linker_->ResolveMethodType(soa.Self(), - dex_file, method2_id.proto_idx_, dex_cache, class_loader)); diff --git a/runtime/mirror/emulated_stack_frame.cc b/runtime/mirror/emulated_stack_frame.cc index f82bfbfaefb18681c9cc5e244821859fff48aeb6..5757992167ca14264ed82247d2fd4088ec62790b 100644 --- a/runtime/mirror/emulated_stack_frame.cc +++ b/runtime/mirror/emulated_stack_frame.cc @@ -139,14 +139,12 @@ class EmulatedStackFrameAccessor { DISALLOW_COPY_AND_ASSIGN(EmulatedStackFrameAccessor); }; -template mirror::EmulatedStackFrame* EmulatedStackFrame::CreateFromShadowFrameAndArgs( Thread* self, Handle caller_type, Handle callee_type, const ShadowFrame& caller_frame, - const uint32_t first_src_reg, - const uint32_t (&arg)[Instruction::kMaxVarArgRegs]) { + const InstructionOperands* const operands) { StackHandleScope<6> hs(self); // Step 1: We must throw a WrongMethodTypeException if there's a mismatch in the @@ -185,9 +183,9 @@ mirror::EmulatedStackFrame* EmulatedStackFrame::CreateFromShadowFrameAndArgs( } // Step 4 : Perform argument conversions (if required). - ShadowFrameGetter getter(first_src_reg, arg, caller_frame); + ShadowFrameGetter getter(operands, caller_frame); EmulatedStackFrameAccessor setter(references, stack_frame, stack_frame->GetLength()); - if (!PerformConversions, EmulatedStackFrameAccessor>( + if (!PerformConversions( self, caller_type, callee_type, &getter, &setter, num_method_params)) { return nullptr; } @@ -289,21 +287,5 @@ void EmulatedStackFrame::VisitRoots(RootVisitor* visitor) { static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); } -// Explicit CreateFromShadowFrameAndArgs template function declarations. -#define EXPLICIT_CREATE_FROM_SHADOW_FRAME_AND_ARGS_DECL(_is_range) \ - template REQUIRES_SHARED(Locks::mutator_lock_) \ - mirror::EmulatedStackFrame* EmulatedStackFrame::CreateFromShadowFrameAndArgs<_is_range>( \ - Thread* self, \ - Handle caller_type, \ - Handle callee_type, \ - const ShadowFrame& caller_frame, \ - const uint32_t first_src_reg, \ - const uint32_t (&arg)[Instruction::kMaxVarArgRegs]) \ - -EXPLICIT_CREATE_FROM_SHADOW_FRAME_AND_ARGS_DECL(true); -EXPLICIT_CREATE_FROM_SHADOW_FRAME_AND_ARGS_DECL(false); -#undef EXPLICIT_CREATE_FROM_SHADOW_FRAME_AND_ARGS_DECL - - } // namespace mirror } // namespace art diff --git a/runtime/mirror/emulated_stack_frame.h b/runtime/mirror/emulated_stack_frame.h index 76859ef1c8950f588e9dc3340ae0e673481f77ba..b6aa949ec37271e352f3b3a6ef1eedc0f079f892 100644 --- a/runtime/mirror/emulated_stack_frame.h +++ b/runtime/mirror/emulated_stack_frame.h @@ -35,14 +35,12 @@ class MANAGED EmulatedStackFrame : public Object { public: // Creates an emulated stack frame whose type is |frame_type| from // a shadow frame. - template static mirror::EmulatedStackFrame* CreateFromShadowFrameAndArgs( Thread* self, Handle args_type, Handle frame_type, const ShadowFrame& caller_frame, - const uint32_t first_src_reg, - const uint32_t (&arg)[Instruction::kMaxVarArgRegs]) REQUIRES_SHARED(Locks::mutator_lock_); + const InstructionOperands* const operands) REQUIRES_SHARED(Locks::mutator_lock_); // Writes the contents of this emulated stack frame to the |callee_frame| // whose type is |callee_type|, starting at |first_dest_reg|. diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc index 87cc6203096b0e57567fd4f3105a28535f8d85ff..97fb79353063224ab688d921c3e080a984606d0d 100644 --- a/runtime/mirror/object.cc +++ b/runtime/mirror/object.cc @@ -240,7 +240,7 @@ void Object::CheckFieldAssignmentImpl(MemberOffset field_offset, ObjPtr CHECK_NE(field.GetTypeAsPrimitiveType(), Primitive::kPrimNot); // TODO: resolve the field type for moving GC. ObjPtr field_type = - kMovingCollector ? field.LookupType() : field.ResolveType(); + kMovingCollector ? field.LookupResolvedType() : field.ResolveType(); if (field_type != nullptr) { CHECK(field_type->IsAssignableFrom(new_value->GetClass())); } @@ -258,7 +258,7 @@ void Object::CheckFieldAssignmentImpl(MemberOffset field_offset, ObjPtr CHECK_NE(field.GetTypeAsPrimitiveType(), Primitive::kPrimNot); // TODO: resolve the field type for moving GC. ObjPtr field_type = - kMovingCollector ? field.LookupType() : field.ResolveType(); + kMovingCollector ? field.LookupResolvedType() : field.ResolveType(); if (field_type != nullptr) { CHECK(field_type->IsAssignableFrom(new_value->GetClass())); } diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h index 84587c871cfacde15cf7f03b165ae26af7359e56..24c75ec0d8696f80e0eb76d27d6ff71413dafd4f 100644 --- a/runtime/mirror/string-inl.h +++ b/runtime/mirror/string-inl.h @@ -35,7 +35,16 @@ namespace art { namespace mirror { inline uint32_t String::ClassSize(PointerSize pointer_size) { +#ifdef USE_D8_DESUGAR + // Two lambdas in CharSequence: + // lambda$chars$0$CharSequence + // lambda$codePoints$1$CharSequence + // which were virtual functions in standalone desugar, becomes + // direct functions with D8 desugaring. + uint32_t vtable_entries = Object::kVTableLength + 54; +#else uint32_t vtable_entries = Object::kVTableLength + 56; +#endif return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 1, 2, pointer_size); } diff --git a/runtime/modifiers.h b/runtime/modifiers.h index 4b790a0f0374c688a60846ea8f69bdab5187e85b..d7d647b8fd6f17d3cff87ef7e6ce89bd6f7ac41c 100644 --- a/runtime/modifiers.h +++ b/runtime/modifiers.h @@ -49,17 +49,21 @@ static constexpr uint32_t kAccClassIsProxy = 0x00040000; // class (de // declaring class. This flag may only be applied to methods. static constexpr uint32_t kAccObsoleteMethod = 0x00040000; // method (runtime) // Used by a method to denote that its execution does not need to go through slow path interpreter. -static constexpr uint32_t kAccSkipAccessChecks = 0x00080000; // method (dex only) +static constexpr uint32_t kAccSkipAccessChecks = 0x00080000; // method (runtime, not native) // Used by a class to denote that the verifier has attempted to check it at least once. static constexpr uint32_t kAccVerificationAttempted = 0x00080000; // class (runtime) -static constexpr uint32_t kAccFastNative = 0x00080000; // method (dex only) // This is set by the class linker during LinkInterfaceMethods. It is used by a method to represent // that it was copied from its declaring class into another class. All methods marked kAccMiranda // and kAccDefaultConflict will have this bit set. Any kAccDefault method contained in the methods_ // array of a concrete class will also have this bit set. static constexpr uint32_t kAccCopied = 0x00100000; // method (runtime) -static constexpr uint32_t kAccMiranda = 0x00200000; // method (dex only) +static constexpr uint32_t kAccMiranda = 0x00200000; // method (runtime, not native) static constexpr uint32_t kAccDefault = 0x00400000; // method (runtime) +// Native method flags are set when linking the methods based on the presence of the +// @dalvik.annotation.optimization.{Fast,Critical}Native annotations with build visibility. +// Reuse the values of kAccSkipAccessChecks and kAccMiranda which are not used for native methods. +static constexpr uint32_t kAccFastNative = 0x00080000; // method (runtime; native only) +static constexpr uint32_t kAccCriticalNative = 0x00200000; // method (runtime; native only) // Set by the JIT when clearing profiling infos to denote that a method was previously warm. static constexpr uint32_t kAccPreviouslyWarm = 0x00800000; // method (runtime) @@ -106,8 +110,9 @@ static constexpr uint32_t kAccValidFieldFlags = kAccPublic | kAccPrivate | kAccP // Valid (meaningful) bits for a method. static constexpr uint32_t kAccValidMethodFlags = kAccPublic | kAccPrivate | kAccProtected | kAccStatic | kAccFinal | kAccSynchronized | kAccBridge | kAccVarargs | kAccNative | - kAccAbstract | kAccStrict | kAccSynthetic | kAccMiranda | kAccConstructor | - kAccDeclaredSynchronized | kAccPreviouslyWarm; + kAccAbstract | kAccStrict | kAccSynthetic | kAccConstructor | kAccDeclaredSynchronized; +static_assert(((kAccIntrinsic | kAccIntrinsicBits) & kAccValidMethodFlags) == 0, + "Intrinsic bits and valid dex file method access flags must not overlap."); // Valid (meaningful) bits for a class (not interface). // Note 1. These are positive bits. Other bits may have to be zero. diff --git a/runtime/monitor.cc b/runtime/monitor.cc index cdc55bd45c017294fec47e8c84c455c03fb72806..542692fe46707efa182ac14c484ac93c4413fa95 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -21,6 +21,7 @@ #include "android-base/stringprintf.h" #include "art_method-inl.h" +#include "base/logging.h" // For VLOG. #include "base/mutex.h" #include "base/stl_util.h" #include "base/systrace.h" @@ -1288,62 +1289,54 @@ uint32_t Monitor::GetLockOwnerThreadId(mirror::Object* obj) { } } -void Monitor::DescribeWait(std::ostream& os, const Thread* thread) { - // Determine the wait message and object we're waiting or blocked upon. - mirror::Object* pretty_object = nullptr; - const char* wait_message = nullptr; - uint32_t lock_owner = ThreadList::kInvalidThreadId; +ThreadState Monitor::FetchState(const Thread* thread, + /* out */ mirror::Object** monitor_object, + /* out */ uint32_t* lock_owner_tid) { + DCHECK(monitor_object != nullptr); + DCHECK(lock_owner_tid != nullptr); + + *monitor_object = nullptr; + *lock_owner_tid = ThreadList::kInvalidThreadId; + ThreadState state = thread->GetState(); - if (state == kWaiting || state == kTimedWaiting || state == kSleeping) { - wait_message = (state == kSleeping) ? " - sleeping on " : " - waiting on "; - Thread* self = Thread::Current(); - MutexLock mu(self, *thread->GetWaitMutex()); - Monitor* monitor = thread->GetWaitMonitor(); - if (monitor != nullptr) { - pretty_object = monitor->GetObject(); - } - } else if (state == kBlocked || state == kWaitingForLockInflation) { - wait_message = (state == kBlocked) ? " - waiting to lock " - : " - waiting for lock inflation of "; - pretty_object = thread->GetMonitorEnterObject(); - if (pretty_object != nullptr) { - if (kUseReadBarrier && Thread::Current()->GetIsGcMarking()) { - // We may call Thread::Dump() in the middle of the CC thread flip and this thread's stack - // may have not been flipped yet and "pretty_object" may be a from-space (stale) ref, in - // which case the GetLockOwnerThreadId() call below will crash. So explicitly mark/forward - // it here. - pretty_object = ReadBarrier::Mark(pretty_object); + + switch (state) { + case kWaiting: + case kTimedWaiting: + case kSleeping: + { + Thread* self = Thread::Current(); + MutexLock mu(self, *thread->GetWaitMutex()); + Monitor* monitor = thread->GetWaitMonitor(); + if (monitor != nullptr) { + *monitor_object = monitor->GetObject(); } - lock_owner = pretty_object->GetLockOwnerThreadId(); } - } + break; - if (wait_message != nullptr) { - if (pretty_object == nullptr) { - os << wait_message << "an unknown object"; - } else { - if ((pretty_object->GetLockWord(true).GetState() == LockWord::kThinLocked) && - Locks::mutator_lock_->IsExclusiveHeld(Thread::Current())) { - // Getting the identity hashcode here would result in lock inflation and suspension of the - // current thread, which isn't safe if this is the only runnable thread. - os << wait_message << StringPrintf("<@addr=0x%" PRIxPTR "> (a %s)", - reinterpret_cast(pretty_object), - pretty_object->PrettyTypeOf().c_str()); - } else { - // - waiting on <0x6008c468> (a java.lang.Class) - // Call PrettyTypeOf before IdentityHashCode since IdentityHashCode can cause thread - // suspension and move pretty_object. - const std::string pretty_type(pretty_object->PrettyTypeOf()); - os << wait_message << StringPrintf("<0x%08x> (a %s)", pretty_object->IdentityHashCode(), - pretty_type.c_str()); + case kBlocked: + case kWaitingForLockInflation: + { + mirror::Object* lock_object = thread->GetMonitorEnterObject(); + if (lock_object != nullptr) { + if (kUseReadBarrier && Thread::Current()->GetIsGcMarking()) { + // We may call Thread::Dump() in the middle of the CC thread flip and this thread's stack + // may have not been flipped yet and "pretty_object" may be a from-space (stale) ref, in + // which case the GetLockOwnerThreadId() call below will crash. So explicitly mark/forward + // it here. + lock_object = ReadBarrier::Mark(lock_object); + } + *monitor_object = lock_object; + *lock_owner_tid = lock_object->GetLockOwnerThreadId(); } } - // - waiting to lock <0x613f83d8> (a java.lang.Object) held by thread 5 - if (lock_owner != ThreadList::kInvalidThreadId) { - os << " held by thread " << lock_owner; - } - os << "\n"; + break; + + default: + break; } + + return state; } mirror::Object* Monitor::GetContendedMonitor(Thread* thread) { @@ -1384,9 +1377,9 @@ void Monitor::VisitLocks(StackVisitor* stack_visitor, void (*callback)(mirror::O } // Is there any reason to believe there's any synchronization in this method? - const DexFile::CodeItem* code_item = m->GetCodeItem(); - CHECK(code_item != nullptr) << m->PrettyMethod(); - if (code_item->tries_size_ == 0) { + CHECK(m->GetCodeItem() != nullptr) << m->PrettyMethod(); + CodeItemDataAccessor accessor(m); + if (accessor.TriesSize() == 0) { return; // No "tries" implies no synchronization, so no held locks to report. } @@ -1401,26 +1394,37 @@ void Monitor::VisitLocks(StackVisitor* stack_visitor, void (*callback)(mirror::O // Ask the verifier for the dex pcs of all the monitor-enter instructions corresponding to // the locks held in this stack frame. - std::vector monitor_enter_dex_pcs; + std::vector monitor_enter_dex_pcs; verifier::MethodVerifier::FindLocksAtDexPc(m, dex_pc, &monitor_enter_dex_pcs); - for (uint32_t monitor_dex_pc : monitor_enter_dex_pcs) { - // The verifier works in terms of the dex pcs of the monitor-enter instructions. - // We want the registers used by those instructions (so we can read the values out of them). - const Instruction* monitor_enter_instruction = - Instruction::At(&code_item->insns_[monitor_dex_pc]); - - // Quick sanity check. - CHECK_EQ(monitor_enter_instruction->Opcode(), Instruction::MONITOR_ENTER) - << "expected monitor-enter @" << monitor_dex_pc << "; was " - << reinterpret_cast(monitor_enter_instruction); - - uint16_t monitor_register = monitor_enter_instruction->VRegA(); - uint32_t value; - bool success = stack_visitor->GetVReg(m, monitor_register, kReferenceVReg, &value); - CHECK(success) << "Failed to read v" << monitor_register << " of kind " - << kReferenceVReg << " in method " << m->PrettyMethod(); - mirror::Object* o = reinterpret_cast(value); - callback(o, callback_context); + for (verifier::MethodVerifier::DexLockInfo& dex_lock_info : monitor_enter_dex_pcs) { + // As a debug check, check that dex PC corresponds to a monitor-enter. + if (kIsDebugBuild) { + const Instruction& monitor_enter_instruction = accessor.InstructionAt(dex_lock_info.dex_pc); + CHECK_EQ(monitor_enter_instruction.Opcode(), Instruction::MONITOR_ENTER) + << "expected monitor-enter @" << dex_lock_info.dex_pc << "; was " + << reinterpret_cast(&monitor_enter_instruction); + } + + // Iterate through the set of dex registers, as the compiler may not have held all of them + // live. + bool success = false; + for (uint32_t dex_reg : dex_lock_info.dex_registers) { + uint32_t value; + success = stack_visitor->GetVReg(m, dex_reg, kReferenceVReg, &value); + if (success) { + mirror::Object* o = reinterpret_cast(value); + callback(o, callback_context); + break; + } + } + DCHECK(success) << "Failed to find/read reference for monitor-enter at dex pc " + << dex_lock_info.dex_pc + << " in method " + << m->PrettyMethod(); + if (!success) { + LOG(WARNING) << "Had a lock reported for dex pc " << dex_lock_info.dex_pc + << " but was not able to fetch a corresponding object!"; + } } } diff --git a/runtime/monitor.h b/runtime/monitor.h index b4c0e6f471e76bf6af1776176a49569269df7321..f150a8c091ca43d0184c24f9f540280206d01133 100644 --- a/runtime/monitor.h +++ b/runtime/monitor.h @@ -94,7 +94,9 @@ class Monitor { bool interruptShouldThrow, ThreadState why) REQUIRES_SHARED(Locks::mutator_lock_) NO_THREAD_SAFETY_ANALYSIS; - static void DescribeWait(std::ostream& os, const Thread* thread) + static ThreadState FetchState(const Thread* thread, + /* out */ mirror::Object** monitor_object, + /* out */ uint32_t* lock_owner_tid) REQUIRES(!Locks::thread_suspend_count_lock_) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/monitor_objects_stack_visitor.h b/runtime/monitor_objects_stack_visitor.h new file mode 100644 index 0000000000000000000000000000000000000000..5c962c3b26f20757d59928988017e785beec3e0e --- /dev/null +++ b/runtime/monitor_objects_stack_visitor.h @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2014 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_RUNTIME_MONITOR_OBJECTS_STACK_VISITOR_H_ +#define ART_RUNTIME_MONITOR_OBJECTS_STACK_VISITOR_H_ + +#include + +#include "art_method.h" +#include "base/mutex.h" +#include "monitor.h" +#include "stack.h" +#include "thread.h" +#include "thread_state.h" + +namespace art { + +namespace mirror { +class Object; +} + +class Context; + +class MonitorObjectsStackVisitor : public StackVisitor { + public: + MonitorObjectsStackVisitor(Thread* thread_in, + Context* context, + bool check_suspended = true, + bool dump_locks_in = true) + REQUIRES_SHARED(Locks::mutator_lock_) + : StackVisitor(thread_in, + context, + StackVisitor::StackWalkKind::kIncludeInlinedFrames, + check_suspended), + frame_count(0u), + dump_locks(dump_locks_in) {} + + enum class VisitMethodResult { + kContinueMethod, + kSkipMethod, + kEndStackWalk, + }; + + bool VisitFrame() FINAL REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* m = GetMethod(); + if (m->IsRuntimeMethod()) { + return true; + } + + VisitMethodResult vmrEntry = StartMethod(m, frame_count); + switch (vmrEntry) { + case VisitMethodResult::kContinueMethod: + break; + case VisitMethodResult::kSkipMethod: + return true; + case VisitMethodResult::kEndStackWalk: + return false; + } + + if (frame_count == 0) { + // Top frame, check for blocked state. + + mirror::Object* monitor_object; + uint32_t lock_owner_tid; + ThreadState state = Monitor::FetchState(GetThread(), + &monitor_object, + &lock_owner_tid); + switch (state) { + case kWaiting: + case kTimedWaiting: + VisitWaitingObject(monitor_object, state); + break; + case kSleeping: + VisitSleepingObject(monitor_object); + break; + + case kBlocked: + case kWaitingForLockInflation: + VisitBlockedOnObject(monitor_object, state, lock_owner_tid); + break; + + default: + break; + } + } + + if (dump_locks) { + // Visit locks, but do not abort on errors. This could trigger a nested abort. + // Skip visiting locks if dump_locks is false as it would cause a bad_mutexes_held in + // RegTypeCache::RegTypeCache due to thread_list_lock. + Monitor::VisitLocks(this, VisitLockedObject, this, false); + } + + ++frame_count; + + VisitMethodResult vmrExit = EndMethod(m); + switch (vmrExit) { + case VisitMethodResult::kContinueMethod: + case VisitMethodResult::kSkipMethod: + return true; + + case VisitMethodResult::kEndStackWalk: + return false; + } + LOG(FATAL) << "Unreachable"; + UNREACHABLE(); + } + + protected: + virtual VisitMethodResult StartMethod(ArtMethod* m, size_t frame_nr) + REQUIRES_SHARED(Locks::mutator_lock_) = 0; + virtual VisitMethodResult EndMethod(ArtMethod* m) + REQUIRES_SHARED(Locks::mutator_lock_) = 0; + + virtual void VisitWaitingObject(mirror::Object* obj, ThreadState state) + REQUIRES_SHARED(Locks::mutator_lock_) = 0; + virtual void VisitSleepingObject(mirror::Object* obj) + REQUIRES_SHARED(Locks::mutator_lock_) = 0; + virtual void VisitBlockedOnObject(mirror::Object* obj, ThreadState state, uint32_t owner_tid) + REQUIRES_SHARED(Locks::mutator_lock_) = 0; + virtual void VisitLockedObject(mirror::Object* obj) + REQUIRES_SHARED(Locks::mutator_lock_) = 0; + + size_t frame_count; + + private: + static void VisitLockedObject(mirror::Object* o, void* context) + REQUIRES_SHARED(Locks::mutator_lock_) { + MonitorObjectsStackVisitor* self = reinterpret_cast(context); + if (o != nullptr) { + if (kUseReadBarrier && Thread::Current()->GetIsGcMarking()) { + // We may call Thread::Dump() in the middle of the CC thread flip and this thread's stack + // may have not been flipped yet and "o" may be a from-space (stale) ref, in which case the + // IdentityHashCode call below will crash. So explicitly mark/forward it here. + o = ReadBarrier::Mark(o); + } + } + self->VisitLockedObject(o); + } + + const bool dump_locks; +}; + +} // namespace art + +#endif // ART_RUNTIME_MONITOR_OBJECTS_STACK_VISITOR_H_ diff --git a/runtime/monitor_pool.cc b/runtime/monitor_pool.cc index d00f979379d4ae68b1141782695764f15e0a6265..cf5934b6a093a2363092aea041c924a97d0a11c9 100644 --- a/runtime/monitor_pool.cc +++ b/runtime/monitor_pool.cc @@ -16,7 +16,7 @@ #include "monitor_pool.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/mutex-inl.h" #include "monitor.h" #include "thread-current-inl.h" diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index 22355638cdbd0a674bfa1555cdb234a189196763..c0de3749040f63285eddf6457583c464cf94dd42 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -744,6 +744,23 @@ static jobjectArray DexFile_getDexFileOutputPaths(JNIEnv* env, return result; } +static jlong DexFile_getStaticSizeOfDexFile(JNIEnv* env, jclass, jobject cookie) { + const OatFile* oat_file = nullptr; + std::vector dex_files; + if (!ConvertJavaArrayToDexFiles(env, cookie, /*out */ dex_files, /* out */ oat_file)) { + DCHECK(env->ExceptionCheck()); + return 0; + } + + uint64_t file_size = 0; + for (auto& dex_file : dex_files) { + if (dex_file) { + file_size += dex_file->GetHeader().file_size_; + } + } + return static_cast(file_size); +} + static JNINativeMethod gMethods[] = { NATIVE_METHOD(DexFile, closeDexFile, "(Ljava/lang/Object;)Z"), NATIVE_METHOD(DexFile, @@ -779,7 +796,8 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(DexFile, getDexFileStatus, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"), NATIVE_METHOD(DexFile, getDexFileOutputPaths, - "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;") + "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"), + NATIVE_METHOD(DexFile, getStaticSizeOfDexFile, "(Ljava/lang/Object;)J") }; void register_dalvik_system_DexFile(JNIEnv* env) { diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc index 2663bea344562d46c122fada37bb811b6fb31cf3..787646dd2193ca880736ef625b499c5eb99f4953 100644 --- a/runtime/native/dalvik_system_VMDebug.cc +++ b/runtime/native/dalvik_system_VMDebug.cc @@ -157,8 +157,9 @@ static jboolean VMDebug_isDebuggerConnected(JNIEnv*, jclass) { return Dbg::IsDebuggerActive(); } -static jboolean VMDebug_isDebuggingEnabled(JNIEnv*, jclass) { - return Dbg::IsJdwpConfigured(); +static jboolean VMDebug_isDebuggingEnabled(JNIEnv* env, jclass) { + ScopedObjectAccess soa(env); + return Runtime::Current()->GetRuntimeCallbacks()->IsDebuggerConfigured(); } static jlong VMDebug_lastDebuggerActivity(JNIEnv*, jclass) { @@ -319,6 +320,53 @@ static jlongArray VMDebug_countInstancesOfClasses(JNIEnv* env, return soa.AddLocalReference(long_counts); } +static jobjectArray VMDebug_getInstancesOfClasses(JNIEnv* env, + jclass, + jobjectArray javaClasses, + jboolean includeAssignable) { + ScopedObjectAccess soa(env); + StackHandleScope<2> hs(soa.Self()); + Handle> classes = hs.NewHandle( + soa.Decode>(javaClasses)); + if (classes == nullptr) { + return nullptr; + } + + jclass object_array_class = env->FindClass("[Ljava/lang/Object;"); + if (env->ExceptionCheck() == JNI_TRUE) { + return nullptr; + } + CHECK(object_array_class != nullptr); + + size_t num_classes = classes->GetLength(); + jobjectArray result = env->NewObjectArray(num_classes, object_array_class, nullptr); + if (env->ExceptionCheck() == JNI_TRUE) { + return nullptr; + } + + gc::Heap* const heap = Runtime::Current()->GetHeap(); + MutableHandle h_class(hs.NewHandle(nullptr)); + for (size_t i = 0; i < num_classes; ++i) { + h_class.Assign(classes->Get(i)); + + VariableSizedHandleScope hs2(soa.Self()); + std::vector> raw_instances; + heap->GetInstances(hs2, h_class, includeAssignable, /* max_count */ 0, raw_instances); + jobjectArray array = env->NewObjectArray(raw_instances.size(), + WellKnownClasses::java_lang_Object, + nullptr); + if (env->ExceptionCheck() == JNI_TRUE) { + return nullptr; + } + + for (size_t j = 0; j < raw_instances.size(); ++j) { + env->SetObjectArrayElement(array, j, raw_instances[j].ToJObject()); + } + env->SetObjectArrayElement(result, i, array); + } + return result; +} + // We export the VM internal per-heap-space size/alloc/free metrics // for the zygote space, alloc space (application heap), and the large // object space for dumpsys meminfo. The other memory region data such @@ -534,6 +582,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMDebug, dumpReferenceTables, "()V"), NATIVE_METHOD(VMDebug, getAllocCount, "(I)I"), NATIVE_METHOD(VMDebug, getHeapSpaceStats, "([J)V"), + NATIVE_METHOD(VMDebug, getInstancesOfClasses, "([Ljava/lang/Class;Z)[[Ljava/lang/Object;"), NATIVE_METHOD(VMDebug, getInstructionCount, "([I)V"), FAST_NATIVE_METHOD(VMDebug, getLoadedClassCount, "()I"), NATIVE_METHOD(VMDebug, getVmFeatureList, "()[Ljava/lang/String;"), diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 2d1f886896b72ac402caa955e079f75b2f5c670c..6446e1b4f5dcc8a3cb245572787e5782d345e9a4 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -223,7 +223,7 @@ static jboolean VMRuntime_is64Bit(JNIEnv*, jobject) { } static jboolean VMRuntime_isCheckJniEnabled(JNIEnv* env, jobject) { - return down_cast(env)->vm->IsCheckJniEnabled() ? JNI_TRUE : JNI_FALSE; + return down_cast(env)->GetVm()->IsCheckJniEnabled() ? JNI_TRUE : JNI_FALSE; } static void VMRuntime_setTargetSdkVersionNative(JNIEnv*, jobject, jint target_sdk_version) { @@ -375,8 +375,8 @@ static void PreloadDexCachesResolveField(ObjPtr dex_cache, } const DexFile* dex_file = dex_cache->GetDexFile(); const DexFile::FieldId& field_id = dex_file->GetFieldId(field_idx); - ObjPtr klass = - ClassLinker::LookupResolvedType(field_id.class_idx_, dex_cache, nullptr); + ObjPtr klass = Runtime::Current()->GetClassLinker()->LookupResolvedType( + field_id.class_idx_, dex_cache, /* class_loader */ nullptr); if (klass == nullptr) { return; } @@ -401,8 +401,8 @@ static void PreloadDexCachesResolveMethod(ObjPtr dex_cache, ui } const DexFile* dex_file = dex_cache->GetDexFile(); const DexFile::MethodId& method_id = dex_file->GetMethodId(method_idx); - ObjPtr klass = - ClassLinker::LookupResolvedType(method_id.class_idx_, dex_cache, nullptr); + ObjPtr klass = Runtime::Current()->GetClassLinker()->LookupResolvedType( + method_id.class_idx_, dex_cache, /* class_loader */ nullptr); if (klass == nullptr) { return; } diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc index a7bee39a815dcde533d56a52b43c6c687ff4a81e..fd80aaeaf7b57d83845cbf166946284757ae2377 100644 --- a/runtime/native/dalvik_system_ZygoteHooks.cc +++ b/runtime/native/dalvik_system_ZygoteHooks.cc @@ -18,11 +18,14 @@ #include -#include "android-base/stringprintf.h" +#include +#include #include "arch/instruction_set.h" #include "art_method-inl.h" -#include "base/logging.h" +#include "base/macros.h" +#include "base/mutex.h" +#include "base/runtime_debug.h" #include "debugger.h" #include "java_vm_ext.h" #include "jit/jit.h" diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 9359ffc7fd0119697cfeb0259c8019d477a9f875..da5cee1ddcdda13621c6e7c920e5168bc0963c19 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -146,11 +146,11 @@ static jobjectArray Class_getInterfacesInternal(JNIEnv* env, jobject javaThis) { // with kActiveTransaction == false. DCHECK(!Runtime::Current()->IsActiveTransaction()); + ClassLinker* linker = Runtime::Current()->GetClassLinker(); MutableHandle interface(hs.NewHandle(nullptr)); for (uint32_t i = 0; i < num_ifaces; ++i) { const dex::TypeIndex type_idx = iface_list->GetTypeItem(i).type_idx_; - interface.Assign(ClassLinker::LookupResolvedType( - type_idx, klass->GetDexCache(), klass->GetClassLoader())); + interface.Assign(linker->LookupResolvedType(type_idx, klass.Get())); ifaces->SetWithoutChecks(i, interface.Get()); } diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc index a717264bcba70811e9c2bc93d80f7cca908a165a..9a52f7002ba36ad40c492137546acac7bcba50ec 100644 --- a/runtime/native/java_lang_Thread.cc +++ b/runtime/native/java_lang_Thread.cc @@ -37,7 +37,7 @@ static jobject Thread_currentThread(JNIEnv* env, jclass) { } static jboolean Thread_interrupted(JNIEnv* env, jclass) { - return static_cast(env)->self->Interrupted() ? JNI_TRUE : JNI_FALSE; + return static_cast(env)->GetSelf()->Interrupted() ? JNI_TRUE : JNI_FALSE; } static jboolean Thread_isInterrupted(JNIEnv* env, jobject java_thread) { diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc index f8f4b1f0ad09c8d698ae3e2a3830f515c2310fa7..8f8fd71727de080d82733019b89bdc449d051913 100644 --- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc +++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc @@ -16,7 +16,9 @@ #include "org_apache_harmony_dalvik_ddmc_DdmServer.h" -#include "base/logging.h" +#include + +#include "base/array_ref.h" #include "debugger.h" #include "jni_internal.h" #include "native_util.h" @@ -31,7 +33,9 @@ static void DdmServer_nativeSendChunk(JNIEnv* env, jclass, jint type, ScopedFastNativeObjectAccess soa(env); ScopedByteArrayRO data(env, javaData); DCHECK_LE(offset + length, static_cast(data.size())); - Dbg::DdmSendChunk(type, length, reinterpret_cast(&data[offset])); + ArrayRef chunk(reinterpret_cast(&data[offset]), + static_cast(length)); + Runtime::Current()->GetRuntimeCallbacks()->DdmPublishChunk(static_cast(type), chunk); } static JNINativeMethod gMethods[] = { diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc index f5057b013a15ecb9d2b59a7cd9f1501ab6a1ac8f..836637f4f05d8aa8f923edc6a5789f26b04d99f5 100644 --- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc +++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc @@ -16,8 +16,9 @@ #include "org_apache_harmony_dalvik_ddmc_DdmVmInternal.h" +#include + #include "base/file_utils.h" -#include "base/logging.h" #include "base/mutex.h" #include "debugger.h" #include "gc/heap.h" @@ -31,6 +32,10 @@ namespace art { +static Thread* GetSelf(JNIEnv* env) { + return static_cast(env)->GetSelf(); +} + static void DdmVmInternal_enableRecentAllocations(JNIEnv*, jclass, jboolean enable) { Dbg::SetAllocTrackingEnabled(enable); } @@ -50,7 +55,7 @@ static jboolean DdmVmInternal_getRecentAllocationStatus(JNIEnv*, jclass) { */ static jobjectArray DdmVmInternal_getStackTraceById(JNIEnv* env, jclass, jint thin_lock_id) { jobjectArray trace = nullptr; - Thread* const self = Thread::Current(); + Thread* const self = GetSelf(env); if (static_cast(thin_lock_id) == self->GetThreadId()) { // No need to suspend ourself to build stacktrace. ScopedObjectAccess soa(env); @@ -135,7 +140,7 @@ static void ThreadStatsGetterCallback(Thread* t, void* context) { static jbyteArray DdmVmInternal_getThreadStats(JNIEnv* env, jclass) { std::vector bytes; - Thread* self = static_cast(env)->self; + Thread* self = GetSelf(env); { MutexLock mu(self, *Locks::thread_list_lock_); ThreadList* thread_list = Runtime::Current()->GetThreadList(); diff --git a/runtime/native/scoped_fast_native_object_access-inl.h b/runtime/native/scoped_fast_native_object_access-inl.h index b2abc4691a92e42019ea7c2e470a0d72be20c165..20ff76ea278f5637e00f57da43e09501a3afa5d2 100644 --- a/runtime/native/scoped_fast_native_object_access-inl.h +++ b/runtime/native/scoped_fast_native_object_access-inl.h @@ -27,7 +27,7 @@ namespace art { inline ScopedFastNativeObjectAccess::ScopedFastNativeObjectAccess(JNIEnv* env) : ScopedObjectAccessAlreadyRunnable(env) { Locks::mutator_lock_->AssertSharedHeld(Self()); - DCHECK((*Self()->GetManagedStack()->GetTopQuickFrame())->IsAnnotatedWithFastNative()); + DCHECK((*Self()->GetManagedStack()->GetTopQuickFrame())->IsFastNative()); // Don't work with raw objects in non-runnable states. DCHECK_EQ(Self()->GetState(), kRunnable); } diff --git a/runtime/native_bridge_art_interface.cc b/runtime/native_bridge_art_interface.cc index cd8315cdf9a2fe3b603253036f75c9e67773a1c1..10d10912f1c353b403f767146d06a4130faafe17 100644 --- a/runtime/native_bridge_art_interface.cc +++ b/runtime/native_bridge_art_interface.cc @@ -22,7 +22,7 @@ #include "art_method-inl.h" #include "base/enums.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/macros.h" #include "dex_file-inl.h" #include "jni_internal.h" diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc index f166714b79c78bbcb17b4adfe8f3577f1e17652a..ec9455289f07c48b61e717135dd9755a167c84df 100644 --- a/runtime/native_stack_dump.cc +++ b/runtime/native_stack_dump.cc @@ -40,6 +40,7 @@ #include "android-base/stringprintf.h" #include "arch/instruction_set.h" +#include "base/aborting.h" #include "base/file_utils.h" #include "base/memory_tool.h" #include "base/mutex.h" diff --git a/runtime/non_debuggable_classes.cc b/runtime/non_debuggable_classes.cc index 7db199cd060600b81efec2fe65f6c7cd3e553203..8484e2cde7e8d495acc76d7201888912bc1639b2 100644 --- a/runtime/non_debuggable_classes.cc +++ b/runtime/non_debuggable_classes.cc @@ -16,7 +16,6 @@ #include "non_debuggable_classes.h" -#include "base/logging.h" #include "jni_internal.h" #include "mirror/class-inl.h" #include "nativehelper/scoped_local_ref.h" diff --git a/runtime/oat.h b/runtime/oat.h index a3e8eef91a8bb4ad4d63cbdf343d7a9e7ac8d30b..9d2118064da664e3788164cfb76d12b25fca7fcd 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,8 +32,8 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - // Last oat version changed reason: Map boot image InternTable and ClassTable into app .bss. - static constexpr uint8_t kOatVersion[] = { '1', '3', '4', '\0' }; + // Last oat version changed reason: .bss index mapping change. + static constexpr uint8_t kOatVersion[] = { '1', '3', '5', '\0' }; static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 4429adeb81e8948f68bfa93be41543b745aad756..f437db281ddb36e6f1c30b19d74d0d3f7ee66088 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -39,6 +39,7 @@ #include "base/bit_vector.h" #include "base/enums.h" #include "base/file_utils.h" +#include "base/logging.h" // For VLOG_IS_ON. #include "base/stl_util.h" #include "base/systrace.h" #include "base/unix_file/fd_file.h" @@ -193,7 +194,7 @@ OatFileBase* OatFileBase::OpenOatFile(const std::string& vdex_filename, ret->PreLoad(); - if (kIsVdexEnabled && !ret->LoadVdex(vdex_filename, writable, low_4gb, error_msg)) { + if (!ret->LoadVdex(vdex_filename, writable, low_4gb, error_msg)) { return nullptr; } @@ -233,7 +234,7 @@ OatFileBase* OatFileBase::OpenOatFile(int vdex_fd, std::string* error_msg) { std::unique_ptr ret(new kOatFileBaseSubType(oat_location, executable)); - if (kIsVdexEnabled && !ret->LoadVdex(vdex_fd, vdex_location, writable, low_4gb, error_msg)) { + if (!ret->LoadVdex(vdex_fd, vdex_location, writable, low_4gb, error_msg)) { return nullptr; } @@ -314,7 +315,7 @@ bool OatFileBase::ComputeFields(uint8_t* requested_base, if (requested_base != nullptr && begin_ != requested_base) { // Host can fail this check. Do not dump there to avoid polluting the output. if (kIsTargetBuild && (kIsDebugBuild || VLOG_IS_ON(oat))) { - PrintFileToLog("/proc/self/maps", LogSeverity::WARNING); + PrintFileToLog("/proc/self/maps", android::base::LogSeverity::WARNING); } *error_msg = StringPrintf("Failed to find oatdata symbol at expected address: " "oatdata=%p != expected=%p. See process maps in the log.", @@ -404,6 +405,79 @@ static inline bool MapConstantTables(const gc::space::ImageSpace* space, return true; } +static bool ReadIndexBssMapping(OatFile* oat_file, + /*inout*/const uint8_t** oat, + size_t dex_file_index, + const std::string& dex_file_location, + const char* tag, + /*out*/const IndexBssMapping** mapping, + std::string* error_msg) { + uint32_t index_bss_mapping_offset; + if (UNLIKELY(!ReadOatDexFileData(*oat_file, oat, &index_bss_mapping_offset))) { + *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' truncated " + "after %s bss mapping offset", + oat_file->GetLocation().c_str(), + dex_file_index, + dex_file_location.c_str(), + tag); + return false; + } + const bool readable_index_bss_mapping_size = + index_bss_mapping_offset != 0u && + index_bss_mapping_offset <= oat_file->Size() && + IsAligned(index_bss_mapping_offset) && + oat_file->Size() - index_bss_mapping_offset >= IndexBssMapping::ComputeSize(0); + const IndexBssMapping* index_bss_mapping = readable_index_bss_mapping_size + ? reinterpret_cast(oat_file->Begin() + index_bss_mapping_offset) + : nullptr; + if (index_bss_mapping_offset != 0u && + (UNLIKELY(index_bss_mapping == nullptr) || + UNLIKELY(index_bss_mapping->size() == 0u) || + UNLIKELY(oat_file->Size() - index_bss_mapping_offset < + IndexBssMapping::ComputeSize(index_bss_mapping->size())))) { + *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with unaligned or " + " truncated %s bss mapping, offset %u of %zu, length %zu", + oat_file->GetLocation().c_str(), + dex_file_index, + dex_file_location.c_str(), + tag, + index_bss_mapping_offset, + oat_file->Size(), + index_bss_mapping != nullptr ? index_bss_mapping->size() : 0u); + return false; + } + + *mapping = index_bss_mapping; + return true; +} + +static void DCheckIndexToBssMapping(OatFile* oat_file, + uint32_t number_of_indexes, + size_t slot_size, + const IndexBssMapping* index_bss_mapping) { + if (kIsDebugBuild && index_bss_mapping != nullptr) { + size_t index_bits = IndexBssMappingEntry::IndexBits(number_of_indexes); + const IndexBssMappingEntry* prev_entry = nullptr; + for (const IndexBssMappingEntry& entry : *index_bss_mapping) { + CHECK_ALIGNED_PARAM(entry.bss_offset, slot_size); + // When loading a non-executable ElfOatFile, .bss symbols are not even + // looked up, so we cannot verify the offset against BssSize(). + if (oat_file->IsExecutable()) { + CHECK_LT(entry.bss_offset, oat_file->BssSize()); + } + uint32_t mask = entry.GetMask(index_bits); + CHECK_LE(POPCOUNT(mask) * slot_size, entry.bss_offset); + size_t index_mask_span = (mask != 0u) ? 32u - index_bits - CTZ(mask) : 0u; + CHECK_LE(index_mask_span, entry.GetIndex(index_bits)); + if (prev_entry != nullptr) { + CHECK_LT(prev_entry->GetIndex(index_bits), entry.GetIndex(index_bits) - index_mask_span); + } + prev_entry = &entry; + } + CHECK_LT(prev_entry->GetIndex(index_bits), number_of_indexes); + } +} + bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { if (!GetOatHeader().IsValid()) { std::string cause = GetOatHeader().GetValidationErrorMessage(); @@ -658,54 +732,23 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { ? reinterpret_cast(Begin() + dex_layout_sections_offset) : nullptr; - uint32_t method_bss_mapping_offset; - if (UNLIKELY(!ReadOatDexFileData(*this, &oat, &method_bss_mapping_offset))) { - *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' truncated " - "after method bss mapping offset", - GetLocation().c_str(), - i, - dex_file_location.c_str()); - return false; - } - const bool readable_method_bss_mapping_size = - method_bss_mapping_offset != 0u && - method_bss_mapping_offset <= Size() && - IsAligned(method_bss_mapping_offset) && - Size() - method_bss_mapping_offset >= MethodBssMapping::ComputeSize(0); - const MethodBssMapping* method_bss_mapping = readable_method_bss_mapping_size - ? reinterpret_cast(Begin() + method_bss_mapping_offset) - : nullptr; - if (method_bss_mapping_offset != 0u && - (UNLIKELY(method_bss_mapping == nullptr) || - UNLIKELY(method_bss_mapping->size() == 0u) || - UNLIKELY(Size() - method_bss_mapping_offset < - MethodBssMapping::ComputeSize(method_bss_mapping->size())))) { - *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with unaligned or " - " truncated method bss mapping, offset %u of %zu, length %zu", - GetLocation().c_str(), - i, - dex_file_location.c_str(), - method_bss_mapping_offset, - Size(), - method_bss_mapping != nullptr ? method_bss_mapping->size() : 0u); + const IndexBssMapping* method_bss_mapping; + const IndexBssMapping* type_bss_mapping; + const IndexBssMapping* string_bss_mapping; + if (!ReadIndexBssMapping( + this, &oat, i, dex_file_location, "method", &method_bss_mapping, error_msg) || + !ReadIndexBssMapping( + this, &oat, i, dex_file_location, "type", &type_bss_mapping, error_msg) || + !ReadIndexBssMapping( + this, &oat, i, dex_file_location, "string", &string_bss_mapping, error_msg)) { return false; } - if (kIsDebugBuild && method_bss_mapping != nullptr) { - const MethodBssMappingEntry* prev_entry = nullptr; - for (const MethodBssMappingEntry& entry : *method_bss_mapping) { - CHECK_ALIGNED_PARAM(entry.bss_offset, static_cast(pointer_size)); - CHECK_LT(entry.bss_offset, BssSize()); - CHECK_LE(POPCOUNT(entry.index_mask) * static_cast(pointer_size), entry.bss_offset); - size_t index_mask_span = (entry.index_mask != 0u) ? 16u - CTZ(entry.index_mask) : 0u; - CHECK_LE(index_mask_span, entry.method_index); - if (prev_entry != nullptr) { - CHECK_LT(prev_entry->method_index, entry.method_index - index_mask_span); - } - prev_entry = &entry; - } - CHECK_LT(prev_entry->method_index, - reinterpret_cast(dex_file_pointer)->method_ids_size_); - } + DCheckIndexToBssMapping( + this, header->method_ids_size_, static_cast(pointer_size), method_bss_mapping); + DCheckIndexToBssMapping( + this, header->type_ids_size_, sizeof(GcRoot), type_bss_mapping); + DCheckIndexToBssMapping( + this, header->string_ids_size_, sizeof(GcRoot), string_bss_mapping); std::string canonical_location = DexFileLoader::GetDexCanonicalLocation(dex_file_location.c_str()); @@ -718,6 +761,8 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { dex_file_pointer, lookup_table_data, method_bss_mapping, + type_bss_mapping, + string_bss_mapping, class_offsets_pointer, dex_layout_sections); oat_dex_files_storage_.push_back(oat_dex_file); @@ -1024,7 +1069,7 @@ void DlOpenOatFile::PreSetup(const std::string& elf_filename) { dl_iterate_context context0 = { Begin(), &dlopen_mmaps_, 0, 0}; if (dl_iterate_phdr(dl_iterate_context::callback, &context0) == 0) { // OK, give up and print an error. - PrintFileToLog("/proc/self/maps", LogSeverity::WARNING); + PrintFileToLog("/proc/self/maps", android::base::LogSeverity::WARNING); LOG(ERROR) << "File " << elf_filename << " loaded with dlopen but cannot find its mmaps."; } } @@ -1275,7 +1320,7 @@ OatFile* OatFile::Open(const std::string& oat_filename, std::string vdex_filename = GetVdexFilename(oat_filename); // Check that the files even exist, fast-fail. - if (kIsVdexEnabled && !OS::FileExists(vdex_filename.c_str())) { + if (!OS::FileExists(vdex_filename.c_str())) { *error_msg = StringPrintf("File %s does not exist.", vdex_filename.c_str()); return nullptr; } else if (!OS::FileExists(oat_filename.c_str())) { @@ -1427,11 +1472,11 @@ const uint8_t* OatFile::BssEnd() const { } const uint8_t* OatFile::DexBegin() const { - return kIsVdexEnabled ? vdex_->Begin() : Begin(); + return vdex_->Begin(); } const uint8_t* OatFile::DexEnd() const { - return kIsVdexEnabled ? vdex_->End() : End(); + return vdex_->End(); } ArrayRef OatFile::GetBssMethods() const { @@ -1455,6 +1500,21 @@ ArrayRef> OatFile::GetBssGcRoots() const { } } +uint32_t OatFile::GetDebugInfoOffset(const DexFile& dex_file, uint32_t debug_info_off) { + // Note that although the specification says that 0 should be used if there + // is no debug information, some applications incorrectly use 0xFFFFFFFF. + // The following check also handles debug_info_off == 0. + if (debug_info_off < dex_file.Size() || debug_info_off == 0xFFFFFFFF) { + return debug_info_off; + } + const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); + if (oat_dex_file == nullptr || (oat_dex_file->GetOatFile() == nullptr)) { + return debug_info_off; + } + return oat_dex_file->GetOatFile()->GetVdexFile()->GetDebugInfoOffset( + dex_file, debug_info_off); +} + const OatFile::OatDexFile* OatFile::GetOatDexFile(const char* dex_location, const uint32_t* dex_location_checksum, std::string* error_msg) const { @@ -1530,7 +1590,9 @@ OatFile::OatDexFile::OatDexFile(const OatFile* oat_file, uint32_t dex_file_location_checksum, const uint8_t* dex_file_pointer, const uint8_t* lookup_table_data, - const MethodBssMapping* method_bss_mapping_data, + const IndexBssMapping* method_bss_mapping_data, + const IndexBssMapping* type_bss_mapping_data, + const IndexBssMapping* string_bss_mapping_data, const uint32_t* oat_class_offsets_pointer, const DexLayoutSections* dex_layout_sections) : oat_file_(oat_file), @@ -1540,6 +1602,8 @@ OatFile::OatDexFile::OatDexFile(const OatFile* oat_file, dex_file_pointer_(dex_file_pointer), lookup_table_data_(lookup_table_data), method_bss_mapping_(method_bss_mapping_data), + type_bss_mapping_(type_bss_mapping_data), + string_bss_mapping_(string_bss_mapping_data), oat_class_offsets_pointer_(oat_class_offsets_pointer), dex_layout_sections_(dex_layout_sections) { // Initialize TypeLookupTable. diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 7d4e6dfd6b53118db220e0132d0bfe74e78d60eb..1fb17a46f8db0a0fcc849154a37249ec7642941e 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -27,7 +27,7 @@ #include "compiler_filter.h" #include "dex_file.h" #include "dex_file_layout.h" -#include "method_bss_mapping.h" +#include "index_bss_mapping.h" #include "mirror/class.h" #include "oat.h" #include "os.h" @@ -109,11 +109,15 @@ class OatFile { static OatFile* OpenWritable(File* file, const std::string& location, const char* abs_dex_location, std::string* error_msg); - // Opens an oat file from an already opened File. Maps it PROT_READ, MAP_PRIVATE. + // Open an oat file from an already opened File. Maps it PROT_READ, MAP_PRIVATE. static OatFile* OpenReadable(File* file, const std::string& location, const char* abs_dex_location, std::string* error_msg); + // Return the actual debug info offset for an offset that might be actually pointing to + // dequickening info. The returned debug info offset is the one originally in the the dex file. + static uint32_t GetDebugInfoOffset(const DexFile& dex_file, uint32_t debug_info_off); + virtual ~OatFile(); bool IsExecutable() const { @@ -440,10 +444,18 @@ class OatDexFile FINAL { return lookup_table_data_; } - const MethodBssMapping* GetMethodBssMapping() const { + const IndexBssMapping* GetMethodBssMapping() const { return method_bss_mapping_; } + const IndexBssMapping* GetTypeBssMapping() const { + return type_bss_mapping_; + } + + const IndexBssMapping* GetStringBssMapping() const { + return string_bss_mapping_; + } + const uint8_t* GetDexFilePointer() const { return dex_file_pointer_; } @@ -478,7 +490,9 @@ class OatDexFile FINAL { uint32_t dex_file_checksum, const uint8_t* dex_file_pointer, const uint8_t* lookup_table_data, - const MethodBssMapping* method_bss_mapping, + const IndexBssMapping* method_bss_mapping, + const IndexBssMapping* type_bss_mapping, + const IndexBssMapping* string_bss_mapping, const uint32_t* oat_class_offsets_pointer, const DexLayoutSections* dex_layout_sections); @@ -490,7 +504,9 @@ class OatDexFile FINAL { const uint32_t dex_file_location_checksum_ = 0u; const uint8_t* const dex_file_pointer_ = nullptr; const uint8_t* const lookup_table_data_ = nullptr; - const MethodBssMapping* const method_bss_mapping_ = nullptr; + const IndexBssMapping* const method_bss_mapping_ = nullptr; + const IndexBssMapping* const type_bss_mapping_ = nullptr; + const IndexBssMapping* const string_bss_mapping_ = nullptr; const uint32_t* const oat_class_offsets_pointer_ = 0u; mutable std::unique_ptr lookup_table_; const DexLayoutSections* const dex_layout_sections_ = nullptr; diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 378ce2cff2058eb77d2a9777b9bbb1a86d3d7079..8707e736bd619d66f111bed3b534daab780923d7 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -24,7 +24,7 @@ #include "android-base/strings.h" #include "base/file_utils.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/stl_util.h" #include "class_linker.h" #include "compiler_filter.h" @@ -99,28 +99,7 @@ OatFileAssistant::OatFileAssistant(const char* dex_location, << " vdex_fd=" << vdex_fd;; } - // Try to get the realpath for the dex location. - // - // This is OK with respect to dalvik cache naming scheme because we never - // generate oat files starting from symlinks which go into dalvik cache. - // (recall that the oat files in dalvik cache are encoded by replacing '/' - // with '@' in the path). - // The boot image oat files (which are symlinked in dalvik-cache) are not - // loaded via the oat file assistant. - // - // The only case when the dex location may resolve to a different path - // is for secondary dex files (e.g. /data/user/0 symlinks to /data/data and - // the app is free to create its own internal layout). Related to this it is - // worthwhile to mention that installd resolves the secondary dex location - // before calling dex2oat. - UniqueCPtr dex_location_real(realpath(dex_location, nullptr)); - if (dex_location_real != nullptr) { - dex_location_.assign(dex_location_real.get()); - } else { - // If we can't get the realpath of the location there's not much point in trying to move on. - PLOG(ERROR) << "Could not get the realpath of dex_location " << dex_location; - return; - } + dex_location_.assign(dex_location); if (load_executable_ && isa != kRuntimeISA) { LOG(WARNING) << "OatFileAssistant: Load executable specified, " @@ -496,17 +475,10 @@ OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& // Verify the dex checksum. std::string error_msg; - if (kIsVdexEnabled) { - VdexFile* vdex = file.GetVdexFile(); - if (!DexChecksumUpToDate(*vdex, &error_msg)) { - LOG(ERROR) << error_msg; - return kOatDexOutOfDate; - } - } else { - if (!DexChecksumUpToDate(file, &error_msg)) { - LOG(ERROR) << error_msg; - return kOatDexOutOfDate; - } + VdexFile* vdex = file.GetVdexFile(); + if (!DexChecksumUpToDate(*vdex, &error_msg)) { + LOG(ERROR) << error_msg; + return kOatDexOutOfDate; } CompilerFilter::Filter current_compiler_filter = file.GetCompilerFilter(); diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 65d01a4d525aa2b801de35641202934561422b94..a98da0f0294057d7b0c412798b0c2b94317c0e4c 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -351,58 +351,9 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithInvalidOdexVdexFd) { EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); } -// Case: We have a DEX file and up-to-date OAT file for it. We load the dex file -// via a symlink. -// Expect: The status is kNoDexOptNeeded. -TEST_F(OatFileAssistantTest, OatUpToDateSymLink) { - if (IsExecutedAsRoot()) { - // We cannot simulate non writable locations when executed as root: b/38000545. - LOG(ERROR) << "Test skipped because it's running as root"; - return; - } - - std::string real = GetScratchDir() + "/real"; - ASSERT_EQ(0, mkdir(real.c_str(), 0700)); - std::string link = GetScratchDir() + "/link"; - ASSERT_EQ(0, symlink(real.c_str(), link.c_str())); - - std::string dex_location = real + "/OatUpToDate.jar"; - - Copy(GetDexSrc1(), dex_location); - GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); - - // Update the dex location to point to the symlink. - dex_location = link + "/OatUpToDate.jar"; - - // For the use of oat location by making the dex parent not writable. - ScopedNonWritable scoped_non_writable(dex_location); - ASSERT_TRUE(scoped_non_writable.IsSuccessful()); - - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); - - EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, - oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); - EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, - oat_file_assistant.GetDexOptNeeded(CompilerFilter::kQuicken)); - EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, - oat_file_assistant.GetDexOptNeeded(CompilerFilter::kExtract)); - EXPECT_EQ(OatFileAssistant::kDex2OatForFilter, - oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything)); - - EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus()); - EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus()); - EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); -} - // Case: We have a DEX file and up-to-date (ODEX) VDEX file for it, but no // ODEX file. TEST_F(OatFileAssistantTest, VdexUpToDateNoOdex) { - // This test case is only meaningful if vdex is enabled. - if (!kIsVdexEnabled) { - return; - } - std::string dex_location = GetScratchDir() + "/VdexUpToDateNoOdex.jar"; std::string odex_location = GetOdexDir() + "/VdexUpToDateNoOdex.oat"; @@ -446,10 +397,6 @@ TEST_F(OatFileAssistantTest, EmptyVdexOdex) { // Case: We have a DEX file and up-to-date (OAT) VDEX file for it, but no OAT // file. TEST_F(OatFileAssistantTest, VdexUpToDateNoOat) { - // This test case is only meaningful if vdex is enabled. - if (!kIsVdexEnabled) { - return; - } if (IsExecutedAsRoot()) { // We cannot simulate non writable locations when executed as root: b/38000545. LOG(ERROR) << "Test skipped because it's running as root"; @@ -668,11 +615,6 @@ TEST_F(OatFileAssistantTest, OatDexOutOfDate) { // Case: We have a DEX file and an (ODEX) VDEX file out of date with respect // to the dex checksum, but no ODEX file. TEST_F(OatFileAssistantTest, VdexDexOutOfDate) { - // This test case is only meaningful if vdex is enabled. - if (!kIsVdexEnabled) { - return; - } - std::string dex_location = GetScratchDir() + "/VdexDexOutOfDate.jar"; std::string odex_location = GetOdexDir() + "/VdexDexOutOfDate.oat"; @@ -690,11 +632,6 @@ TEST_F(OatFileAssistantTest, VdexDexOutOfDate) { // Case: We have a MultiDEX (ODEX) VDEX file where the non-main multidex entry // is out of date and there is no corresponding ODEX file. TEST_F(OatFileAssistantTest, VdexMultiDexNonMainOutOfDate) { - // This test case is only meaningful if vdex is enabled. - if (!kIsVdexEnabled) { - return; - } - std::string dex_location = GetScratchDir() + "/VdexMultiDexNonMainOutOfDate.jar"; std::string odex_location = GetOdexDir() + "/VdexMultiDexNonMainOutOfDate.odex"; diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index ee35d9cd12a90a22a34973d9cc6ddec9d19b4a9a..91a138a259973bee5d1ce80c230a1d2587132150 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -26,7 +26,7 @@ #include "art_field-inl.h" #include "base/bit_vector-inl.h" #include "base/file_utils.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/stl_util.h" #include "base/systrace.h" #include "class_linker.h" @@ -144,6 +144,9 @@ const OatFile* OatFileManager::GetPrimaryOatFile() const { return nullptr; } +OatFileManager::OatFileManager() + : have_non_pic_oat_file_(false), only_use_system_oat_files_(false) {} + OatFileManager::~OatFileManager() { // Explicitly clear oat_files_ since the OatFile destructor calls back into OatFileManager for // UnRegisterOatFileLocation. @@ -442,8 +445,13 @@ std::vector> OatFileManager::OpenDexFilesFromOat( // if it's in the class path). Note this trades correctness for performance // since the resulting slow down is unacceptable in some cases until b/64530081 // is fixed. + // We still pass the class loader context when the classpath string of the runtime + // is not empty, which is the situation when ART is invoked standalone. + ClassLoaderContext* actual_context = Runtime::Current()->GetClassPathString().empty() + ? nullptr + : context.get(); switch (oat_file_assistant.MakeUpToDate(/*profile_changed*/ false, - /*class_loader_context*/ nullptr, + actual_context, /*out*/ &error_msg)) { case OatFileAssistant::kUpdateFailed: LOG(WARNING) << error_msg; diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h index 12057735eb524e30d054e205ea2081be34ed083e..dd6b7ba2ff8be9d2151325194b14bf34a6672f5e 100644 --- a/runtime/oat_file_manager.h +++ b/runtime/oat_file_manager.h @@ -45,7 +45,7 @@ class OatFile; // pointers returned from functions are always valid. class OatFileManager { public: - OatFileManager() : have_non_pic_oat_file_(false), only_use_system_oat_files_(false) {} + OatFileManager(); ~OatFileManager(); // Add an oat file to the internal accounting, std::aborts if there already exists an oat file diff --git a/runtime/obj_ptr.h b/runtime/obj_ptr.h index 9a66983de78924f2c85ee0228f96c92981c68853..70e767acf6793cf6dd15a99ca22a6c57955754c1 100644 --- a/runtime/obj_ptr.h +++ b/runtime/obj_ptr.h @@ -49,23 +49,22 @@ class ObjPtr { // Note: The following constructors allow implicit conversion. This simplifies code that uses // them, e.g., for parameter passing. However, in general, implicit-conversion constructors - // are discouraged and detected by cpplint and clang-tidy. So mark these constructors - // as NOLINT (without category, as the categories are different). + // are discouraged and detected by clang-tidy. - ALWAYS_INLINE ObjPtr(std::nullptr_t) // NOLINT + ALWAYS_INLINE ObjPtr(std::nullptr_t) REQUIRES_SHARED(Locks::mutator_lock_) : reference_(0u) {} template ::value>::type> - ALWAYS_INLINE ObjPtr(Type* ptr) // NOLINT + ALWAYS_INLINE ObjPtr(Type* ptr) REQUIRES_SHARED(Locks::mutator_lock_) : reference_(Encode(static_cast(ptr))) { } template ::value>::type> - ALWAYS_INLINE ObjPtr(const ObjPtr& other) // NOLINT + ALWAYS_INLINE ObjPtr(const ObjPtr& other) REQUIRES_SHARED(Locks::mutator_lock_) : reference_(kObjPtrPoisoningValidateOnCopy ? Encode(static_cast(other.Ptr())) diff --git a/runtime/os_linux.cc b/runtime/os_linux.cc index a463f700d8e4a158aca204e15fc1d3850f4af02c..1b3e0000da8104ce44a0d83bff53522730524733 100644 --- a/runtime/os_linux.cc +++ b/runtime/os_linux.cc @@ -23,7 +23,8 @@ #include #include -#include "base/logging.h" +#include + #include "base/unix_file/fd_file.h" namespace art { diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index cc09a776b825085db37ce8d19d543eb4b4437252..47309edfd7f1119dd31886903b7dc9060f5f0b3a 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -18,8 +18,10 @@ #include +#include + #include "base/file_utils.h" -#include "base/logging.h" +#include "base/macros.h" #include "base/stringpiece.h" #include "debugger.h" #include "gc/heap.h" @@ -90,8 +92,11 @@ std::unique_ptr ParsedOptions::MakeParser(bool ignore_unrecognize .IntoKey(M::CheckJni) .Define("-Xjniopts:forcecopy") .IntoKey(M::JniOptsForceCopy) - .Define({"-Xrunjdwp:_", "-agentlib:jdwp=_"}) - .WithType() + .Define("-XjdwpProvider:_") + .WithType() + .IntoKey(M::JdwpProvider) + .Define({"-Xrunjdwp:_", "-agentlib:jdwp=_", "-XjdwpOptions:_"}) + .WithType() .IntoKey(M::JdwpOptions) // TODO Re-enable -agentlib: once I have a good way to transform the values. // .Define("-agentlib:_") @@ -548,7 +553,7 @@ bool ParsedOptions::DoParse(const RuntimeOptions& options, // If not low memory mode, semispace otherwise. gc::CollectorType background_collector_type_; - gc::CollectorType collector_type_ = (XGcOption{}).collector_type_; // NOLINT [whitespace/braces] [5] + gc::CollectorType collector_type_ = (XGcOption{}).collector_type_; bool low_memory_mode_ = args.Exists(M::LowMemoryMode); background_collector_type_ = args.GetOrDefault(M::BackgroundGc); diff --git a/runtime/plugin.cc b/runtime/plugin.cc index 6aa078771badf7e2ec1f04024e67adff5b92d0bf..7d86f1d5dc37aed852d56002ac290476b5d7d2a4 100644 --- a/runtime/plugin.cc +++ b/runtime/plugin.cc @@ -20,8 +20,6 @@ #include "android-base/stringprintf.h" -#include "base/logging.h" - namespace art { using android::base::StringPrintf; diff --git a/runtime/plugin.h b/runtime/plugin.h index f077aaf3fb012a7a358e3de7893344cfc77e1de4..909c710a96aee10fbe2208b2385fc3228d97a578 100644 --- a/runtime/plugin.h +++ b/runtime/plugin.h @@ -18,7 +18,8 @@ #define ART_RUNTIME_PLUGIN_H_ #include -#include "base/logging.h" + +#include namespace art { diff --git a/runtime/prebuilt_tools_test.cc b/runtime/prebuilt_tools_test.cc index 6fa9b3424d144753f78c81ac71a5381eb64fb1c2..158d9d6e698626ed12df38e762e7e52f89ffe7ef 100644 --- a/runtime/prebuilt_tools_test.cc +++ b/runtime/prebuilt_tools_test.cc @@ -29,7 +29,7 @@ class PrebuiltToolsTest : public CommonRuntimeTest { }; static void CheckToolsExist(const std::string& tools_dir) { - const char* tools[] { "as", "objcopy", "objdump" }; // NOLINT + const char* tools[] = { "as", "objcopy", "objdump" }; for (const char* tool : tools) { struct stat exec_st; std::string exec_path = tools_dir + tool; @@ -50,7 +50,7 @@ TEST_F(PrebuiltToolsTest, CheckHostTools) { TEST_F(PrebuiltToolsTest, CheckTargetTools) { // Other prebuilts are missing from the build server's repo manifest. - InstructionSet isas[] = { InstructionSet::kThumb2 }; // NOLINT + InstructionSet isas[] = { InstructionSet::kThumb2 }; for (InstructionSet isa : isas) { std::string tools_dir = GetAndroidTargetToolsDir(isa); if (tools_dir.empty()) { diff --git a/runtime/primitive.h b/runtime/primitive.h index a429914d5c5d1210c7e579e0078b24854d109868..5b163d8cbe488493352a3e02387f0a7b363fe02b 100644 --- a/runtime/primitive.h +++ b/runtime/primitive.h @@ -19,7 +19,8 @@ #include -#include "base/logging.h" +#include + #include "base/macros.h" namespace art { diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc index f94923e0650a17eced49ad7594989d64179088c0..06b94c30d2a8f21e0308f241b701d0a6b9a230ac 100644 --- a/runtime/quick_exception_handler.cc +++ b/runtime/quick_exception_handler.cc @@ -19,6 +19,7 @@ #include "arch/context.h" #include "art_method-inl.h" #include "base/enums.h" +#include "base/logging.h" // For VLOG_IS_ON. #include "dex_file_types.h" #include "dex_instruction.h" #include "entrypoints/entrypoint_utils.h" @@ -221,7 +222,8 @@ void QuickExceptionHandler::SetCatchEnvironmentForOptimizedHandler(StackVisitor* self_->DumpStack(LOG_STREAM(INFO) << "Setting catch phis: "); } - const size_t number_of_vregs = handler_method_->GetCodeItem()->registers_size_; + CodeItemDataAccessor accessor(handler_method_); + const size_t number_of_vregs = accessor.RegistersSize(); CodeInfo code_info = handler_method_header_->GetOptimizedCodeInfo(); CodeInfoEncoding encoding = code_info.ExtractEncoding(); @@ -358,7 +360,8 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { const size_t frame_id = GetFrameId(); ShadowFrame* new_frame = GetThread()->FindDebuggerShadowFrame(frame_id); const bool* updated_vregs; - const size_t num_regs = method->GetCodeItem()->registers_size_; + CodeItemDataAccessor accessor(method); + const size_t num_regs = accessor.RegistersSize(); if (new_frame == nullptr) { new_frame = ShadowFrame::CreateDeoptimizedFrame(num_regs, nullptr, method, GetDexPc()); updated_vregs = nullptr; @@ -405,7 +408,8 @@ class DeoptimizeStackVisitor FINAL : public StackVisitor { uintptr_t native_pc_offset = method_header->NativeQuickPcOffset(GetCurrentQuickFramePc()); CodeInfoEncoding encoding = code_info.ExtractEncoding(); StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding); - const size_t number_of_vregs = m->GetCodeItem()->registers_size_; + CodeItemDataAccessor accessor(m); + const size_t number_of_vregs = accessor.RegistersSize(); uint32_t register_mask = code_info.GetRegisterMaskOf(encoding, stack_map); BitMemoryRegion stack_mask = code_info.GetStackMaskOf(encoding, stack_map); DexRegisterMap vreg_map = IsInInlinedFrame() diff --git a/runtime/quick_exception_handler.h b/runtime/quick_exception_handler.h index 12b63c933df83742b9a333a71dab833a25b2eddb..1103dab52ceec5f002e60c1ab2b02510a2a6aed0 100644 --- a/runtime/quick_exception_handler.h +++ b/runtime/quick_exception_handler.h @@ -17,7 +17,8 @@ #ifndef ART_RUNTIME_QUICK_EXCEPTION_HANDLER_H_ #define ART_RUNTIME_QUICK_EXCEPTION_HANDLER_H_ -#include "base/logging.h" +#include + #include "base/macros.h" #include "base/mutex.h" #include "deoptimization_kind.h" diff --git a/runtime/read_barrier.h b/runtime/read_barrier.h index d4b9f4311fe331dd5c2110c96217f110f0e09aff..e8df2ad4ce8c9e6bc77a85615bea6faeb4078405 100644 --- a/runtime/read_barrier.h +++ b/runtime/read_barrier.h @@ -17,9 +17,11 @@ #ifndef ART_RUNTIME_READ_BARRIER_H_ #define ART_RUNTIME_READ_BARRIER_H_ -#include "base/logging.h" +#include + #include "base/macros.h" #include "base/mutex.h" +#include "base/runtime_debug.h" #include "gc_root.h" #include "jni.h" #include "mirror/object_reference.h" diff --git a/runtime/reference_table_test.cc b/runtime/reference_table_test.cc index 720a9d6219bcf76470348221c82f70022ec646b9..1e7fc3ee929a4cb50542e3f1de18124e84017a7d 100644 --- a/runtime/reference_table_test.cc +++ b/runtime/reference_table_test.cc @@ -16,7 +16,7 @@ #include "reference_table.h" -#include // NOLINT [build/c++11] [5] +#include #include "android-base/stringprintf.h" diff --git a/runtime/reflection.cc b/runtime/reflection.cc index 9683cedd4daa97ec014ab78e9ec796d07c33609b..cacbe87f710b6dc7c8594531d60dce0d4980e255 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -447,7 +447,7 @@ static void InvokeWithArgArray(const ScopedObjectAccessAlreadyRunnable& soa, const char* shorty) REQUIRES_SHARED(Locks::mutator_lock_) { uint32_t* args = arg_array->GetArray(); - if (UNLIKELY(soa.Env()->check_jni)) { + if (UNLIKELY(soa.Env()->IsCheckJniEnabled())) { CheckMethodArguments(soa.Vm(), method->GetInterfaceMethodIfProxy(kRuntimePointerSize), args); } method->Invoke(soa.Self(), args, arg_array->GetNumBytes(), result, shorty); @@ -927,14 +927,14 @@ void UpdateReference(Thread* self, jobject obj, ObjPtr result) { IndirectRef ref = reinterpret_cast(obj); IndirectRefKind kind = IndirectReferenceTable::GetIndirectRefKind(ref); if (kind == kLocal) { - self->GetJniEnv()->locals.Update(obj, result); + self->GetJniEnv()->UpdateLocal(obj, result); } else if (kind == kHandleScopeOrInvalid) { LOG(FATAL) << "Unsupported UpdateReference for kind kHandleScopeOrInvalid"; } else if (kind == kGlobal) { - self->GetJniEnv()->vm->UpdateGlobal(self, ref, result); + self->GetJniEnv()->GetVm()->UpdateGlobal(self, ref, result); } else { DCHECK_EQ(kind, kWeakGlobal); - self->GetJniEnv()->vm->UpdateWeakGlobal(self, ref, result); + self->GetJniEnv()->GetVm()->UpdateWeakGlobal(self, ref, result); } } diff --git a/runtime/runtime.cc b/runtime/runtime.cc index f09b6c98253512ab1922ea2838439fda7de96c3f..54aa9e57e6304ad0a9846b9b393c16a5bc132baf 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -57,6 +57,7 @@ #include "asm_support.h" #include "asm_support_check.h" #include "atomic.h" +#include "base/aborting.h" #include "base/arena_allocator.h" #include "base/dumpable.h" #include "base/enums.h" @@ -256,6 +257,7 @@ Runtime::Runtime() force_native_bridge_(false), is_native_bridge_loaded_(false), is_native_debuggable_(false), + async_exceptions_thrown_(false), is_java_debuggable_(false), zygote_max_failed_boots_(0), experimental_flags_(ExperimentalFlags::kNone), @@ -295,6 +297,7 @@ Runtime::~Runtime() { } if (dump_gc_performance_on_shutdown_) { + ScopedLogSeverity sls(LogSeverity::INFO); // This can't be called from the Heap destructor below because it // could call RosAlloc::InspectAll() which needs the thread_list // to be still alive. @@ -352,7 +355,7 @@ Runtime::~Runtime() { } // Make sure our internal threads are dead before we start tearing down things they're using. - Dbg::StopJdwp(); + GetRuntimeCallbacks()->StopDebugger(); delete signal_catcher_; // Make sure all other non-daemon threads have terminated, and all daemon threads are suspended. @@ -804,7 +807,7 @@ bool Runtime::Start() { { ScopedObjectAccess soa(self); - self->GetJniEnv()->locals.AssertEmpty(); + self->GetJniEnv()->AssertLocalsEmpty(); } VLOG(startup) << "Runtime::Start exiting"; @@ -868,8 +871,10 @@ void Runtime::InitNonZygoteOrPostFork( StartSignalCatcher(); // Start the JDWP thread. If the command-line debugger flags specified "suspend=y", - // this will pause the runtime, so we probably want this to come last. - Dbg::StartJdwp(); + // this will pause the runtime (in the internal debugger implementation), so we probably want + // this to come last. + ScopedObjectAccess soa(Thread::Current()); + GetRuntimeCallbacks()->StartDebugger(); } void Runtime::StartSignalCatcher() { @@ -1218,8 +1223,29 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { dump_gc_performance_on_shutdown_ = runtime_options.Exists(Opt::DumpGCPerformanceOnShutdown); - if (runtime_options.Exists(Opt::JdwpOptions)) { - Dbg::ConfigureJdwp(runtime_options.GetOrDefault(Opt::JdwpOptions)); + jdwp_options_ = runtime_options.GetOrDefault(Opt::JdwpOptions); + jdwp_provider_ = runtime_options.GetOrDefault(Opt::JdwpProvider); + switch (jdwp_provider_) { + case JdwpProvider::kNone: { + LOG(WARNING) << "Disabling all JDWP support."; + break; + } + case JdwpProvider::kInternal: { + if (runtime_options.Exists(Opt::JdwpOptions)) { + JDWP::JdwpOptions ops; + if (!JDWP::ParseJdwpOptions(runtime_options.GetOrDefault(Opt::JdwpOptions), &ops)) { + LOG(ERROR) << "failed to parse jdwp options!"; + return false; + } + Dbg::ConfigureJdwp(ops); + } + break; + } + case JdwpProvider::kAdbConnection: { + constexpr const char* plugin_name = kIsDebugBuild ? "libadbconnectiond.so" + : "libadbconnection.so"; + plugins_.push_back(Plugin::Create(plugin_name)); + } } callbacks_->AddThreadLifecycleCallback(Dbg::GetThreadLifecycleCallback()); callbacks_->AddClassLoadCallback(Dbg::GetClassLoadCallback()); @@ -1506,6 +1532,7 @@ static bool EnsureJvmtiPlugin(Runtime* runtime, } // Is the process debuggable? Otherwise, do not attempt to load the plugin. + // TODO Support a crimped jvmti for non-debuggable runtimes. if (!runtime->IsJavaDebuggable()) { *error_msg = "Process is not debuggable."; return false; diff --git a/runtime/runtime.h b/runtime/runtime.h index 6b01cc220fe892b0c345af784b6d2bddda6e72ef..89caac4f3c186315766910caec73dc03eb6c7780 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -35,6 +35,7 @@ #include "experimental_flags.h" #include "gc_root.h" #include "instrumentation.h" +#include "jdwp_provider.h" #include "obj_ptr.h" #include "offsets.h" #include "process_state.h" @@ -586,6 +587,14 @@ class Runtime { is_native_debuggable_ = value; } + bool AreAsyncExceptionsThrown() const { + return async_exceptions_thrown_; + } + + void SetAsyncExceptionsThrown() { + async_exceptions_thrown_ = true; + } + // Returns the build fingerprint, if set. Otherwise an empty string is returned. std::string GetFingerprint() { return fingerprint_; @@ -688,6 +697,14 @@ class Runtime { return madvise_random_access_; } + const std::string& GetJdwpOptions() { + return jdwp_options_; + } + + JdwpProvider GetJdwpProvider() const { + return jdwp_provider_; + } + private: static void InitPlatformSignalHandlers(); @@ -899,6 +916,10 @@ class Runtime { // Whether we are running under native debugger. bool is_native_debuggable_; + // whether or not any async exceptions have ever been thrown. This is used to speed up the + // MterpShouldSwitchInterpreters function. + bool async_exceptions_thrown_; + // Whether Java code needs to be debuggable. bool is_java_debuggable_; @@ -941,6 +962,12 @@ class Runtime { // Whether zygote code is in a section that should not start threads. bool zygote_no_threads_; + // The string containing requested jdwp options + std::string jdwp_options_; + + // The jdwp provider we were configured with. + JdwpProvider jdwp_provider_; + // Saved environment. class EnvSnapshot { public: diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc index 339fe822fd0544ad9593a8c75f0f0d9c0d057ae3..cd3c0b7c88fd882897a693a987539bc0eb88f4ec 100644 --- a/runtime/runtime_callbacks.cc +++ b/runtime/runtime_callbacks.cc @@ -35,6 +35,49 @@ static inline void Remove(T* cb, std::vector* data) { } } +void RuntimeCallbacks::AddDdmCallback(DdmCallback* cb) { + ddm_callbacks_.push_back(cb); +} + +void RuntimeCallbacks::RemoveDdmCallback(DdmCallback* cb) { + Remove(cb, &ddm_callbacks_); +} + +void RuntimeCallbacks::DdmPublishChunk(uint32_t type, const ArrayRef& data) { + for (DdmCallback* cb : ddm_callbacks_) { + cb->DdmPublishChunk(type, data); + } +} + +void RuntimeCallbacks::AddDebuggerControlCallback(DebuggerControlCallback* cb) { + debugger_control_callbacks_.push_back(cb); +} + +void RuntimeCallbacks::RemoveDebuggerControlCallback(DebuggerControlCallback* cb) { + Remove(cb, &debugger_control_callbacks_); +} + +bool RuntimeCallbacks::IsDebuggerConfigured() { + for (DebuggerControlCallback* cb : debugger_control_callbacks_) { + if (cb->IsDebuggerConfigured()) { + return true; + } + } + return false; +} + +void RuntimeCallbacks::StartDebugger() { + for (DebuggerControlCallback* cb : debugger_control_callbacks_) { + cb->StartDebugger(); + } +} + +void RuntimeCallbacks::StopDebugger() { + for (DebuggerControlCallback* cb : debugger_control_callbacks_) { + cb->StopDebugger(); + } +} + void RuntimeCallbacks::AddMethodInspectionCallback(MethodInspectionCallback* cb) { method_inspection_callbacks_.push_back(cb); } diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h index c1ba9643a79e4b880f5253bc3d6b35d51f1f9d59..f405c9fe34e01db97a70b62f6cac911ebb9e0e97 100644 --- a/runtime/runtime_callbacks.h +++ b/runtime/runtime_callbacks.h @@ -19,6 +19,7 @@ #include +#include "base/array_ref.h" #include "base/macros.h" #include "base/mutex.h" #include "dex_file.h" @@ -54,6 +55,26 @@ class ThreadLifecycleCallback; // any state checking (is the listener enabled) in the listener itself. For an example, see // Dbg. +class DdmCallback { + public: + virtual ~DdmCallback() {} + virtual void DdmPublishChunk(uint32_t type, const ArrayRef& data) + REQUIRES_SHARED(Locks::mutator_lock_) = 0; +}; + +class DebuggerControlCallback { + public: + virtual ~DebuggerControlCallback() {} + + // Begin running the debugger. + virtual void StartDebugger() = 0; + // The debugger should begin shutting down since the runtime is ending. This is just advisory + virtual void StopDebugger() = 0; + + // This allows the debugger to tell the runtime if it is configured. + virtual bool IsDebuggerConfigured() = 0; +}; + class RuntimeSigQuitCallback { public: virtual ~RuntimeSigQuitCallback() {} @@ -182,6 +203,24 @@ class RuntimeCallbacks { void RemoveMethodInspectionCallback(MethodInspectionCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_); + // DDMS callbacks + void DdmPublishChunk(uint32_t type, const ArrayRef& data) + REQUIRES_SHARED(Locks::mutator_lock_); + + void AddDdmCallback(DdmCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_); + void RemoveDdmCallback(DdmCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_); + + void StartDebugger() REQUIRES_SHARED(Locks::mutator_lock_); + // NO_THREAD_SAFETY_ANALYSIS since this is only called when we are in the middle of shutting down + // and the mutator_lock_ is no longer acquirable. + void StopDebugger() NO_THREAD_SAFETY_ANALYSIS; + bool IsDebuggerConfigured() REQUIRES_SHARED(Locks::mutator_lock_); + + void AddDebuggerControlCallback(DebuggerControlCallback* cb) + REQUIRES_SHARED(Locks::mutator_lock_); + void RemoveDebuggerControlCallback(DebuggerControlCallback* cb) + REQUIRES_SHARED(Locks::mutator_lock_); + private: std::vector thread_callbacks_ GUARDED_BY(Locks::mutator_lock_); @@ -197,6 +236,10 @@ class RuntimeCallbacks { GUARDED_BY(Locks::mutator_lock_); std::vector method_inspection_callbacks_ GUARDED_BY(Locks::mutator_lock_); + std::vector ddm_callbacks_ + GUARDED_BY(Locks::mutator_lock_); + std::vector debugger_control_callbacks_ + GUARDED_BY(Locks::mutator_lock_); }; } // namespace art diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc index d283c79960e5a91da2f9d2664f694b637efff557..0b69851a55914a4a0fb58538ba23a53c92aa3037 100644 --- a/runtime/runtime_callbacks_test.cc +++ b/runtime/runtime_callbacks_test.cc @@ -22,7 +22,7 @@ #include #include -#include // NOLINT [build/c++11] [5] +#include #include #include "jni.h" diff --git a/runtime/runtime_common.cc b/runtime/runtime_common.cc index eb69d91dadb26d94cd068d3b9e8b17729f8b460e..59af9187f9ba71330588c1270312fdbc029bc0b0 100644 --- a/runtime/runtime_common.cc +++ b/runtime/runtime_common.cc @@ -23,10 +23,12 @@ #include #include -#include "android-base/stringprintf.h" +#include +#include +#include "base/aborting.h" #include "base/file_utils.h" -#include "base/logging.h" +#include "base/logging.h" // For LogHelper, GetCmdLine. #include "base/macros.h" #include "base/mutex.h" #include "native_stack_dump.h" @@ -430,7 +432,7 @@ void HandleUnexpectedSignalCommon(int signal_number, logger(LOG_STREAM(FATAL_WITHOUT_ABORT)); } if (kIsDebugBuild && signal_number == SIGSEGV) { - PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT); + PrintFileToLog("/proc/self/maps", android::base::LogSeverity::FATAL_WITHOUT_ABORT); } Runtime* runtime = Runtime::Current(); diff --git a/runtime/runtime_options.cc b/runtime/runtime_options.cc index b072bb0c375aecd6fbcf2fa80fceb799a3375010..bce0d81cfbb8da6f7369820ce7a2953521b481e1 100644 --- a/runtime/runtime_options.cc +++ b/runtime/runtime_options.cc @@ -30,7 +30,7 @@ namespace art { // Specify storage for the RuntimeOptions keys. -#define RUNTIME_OPTIONS_KEY(Type, Name, ...) const RuntimeArgumentMap::Key RuntimeArgumentMap::Name {__VA_ARGS__}; // NOLINT [readability/braces] [4] +#define RUNTIME_OPTIONS_KEY(Type, Name, ...) const RuntimeArgumentMap::Key RuntimeArgumentMap::Name {__VA_ARGS__}; #include "runtime_options.def" } // namespace art diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def index 2e03562505be85e44296280c245f503ca77b3f2e..4bc824570a3f5276ee21135f9d53ff84ebb7f170 100644 --- a/runtime/runtime_options.def +++ b/runtime/runtime_options.def @@ -43,7 +43,8 @@ RUNTIME_OPTIONS_KEY (std::string, ClassPath) RUNTIME_OPTIONS_KEY (std::string, Image) RUNTIME_OPTIONS_KEY (Unit, CheckJni) RUNTIME_OPTIONS_KEY (Unit, JniOptsForceCopy) -RUNTIME_OPTIONS_KEY (JDWP::JdwpOptions, JdwpOptions) +RUNTIME_OPTIONS_KEY (std::string, JdwpOptions, "") +RUNTIME_OPTIONS_KEY (JdwpProvider, JdwpProvider, JdwpProvider::kInternal) RUNTIME_OPTIONS_KEY (MemoryKiB, MemoryMaximumSize, gc::Heap::kDefaultMaximumSize) // -Xmx RUNTIME_OPTIONS_KEY (MemoryKiB, MemoryInitialSize, gc::Heap::kDefaultInitialSize) // -Xms RUNTIME_OPTIONS_KEY (MemoryKiB, HeapGrowthLimit) // Default is 0 for unlimited diff --git a/runtime/safe_map.h b/runtime/safe_map.h index f29869172ebccbd3b9889ad91a3356b0c30bf713..33e45bdbd8b0f2fdcf8c9378578e9ed6ac29c00b 100644 --- a/runtime/safe_map.h +++ b/runtime/safe_map.h @@ -21,8 +21,9 @@ #include #include +#include + #include "base/allocator.h" -#include "base/logging.h" namespace art { diff --git a/runtime/scoped_thread_state_change-inl.h b/runtime/scoped_thread_state_change-inl.h index aa96871145e96661aa48f51567df7c85b82b2651..f95593209b00ac44d960c169394ddf198bf2ba1d 100644 --- a/runtime/scoped_thread_state_change-inl.h +++ b/runtime/scoped_thread_state_change-inl.h @@ -19,6 +19,8 @@ #include "scoped_thread_state_change.h" +#include + #include "base/casts.h" #include "jni_env_ext-inl.h" #include "obj_ptr-inl.h" @@ -95,12 +97,12 @@ inline bool ScopedObjectAccessAlreadyRunnable::IsRunnable() const { } inline ScopedObjectAccessAlreadyRunnable::ScopedObjectAccessAlreadyRunnable(JNIEnv* env) - : self_(ThreadForEnv(env)), env_(down_cast(env)), vm_(env_->vm) {} + : self_(ThreadForEnv(env)), env_(down_cast(env)), vm_(env_->GetVm()) {} inline ScopedObjectAccessAlreadyRunnable::ScopedObjectAccessAlreadyRunnable(Thread* self) : self_(self), env_(down_cast(self->GetJniEnv())), - vm_(env_ != nullptr ? env_->vm : nullptr) {} + vm_(env_ != nullptr ? env_->GetVm() : nullptr) {} inline ScopedObjectAccessUnchecked::ScopedObjectAccessUnchecked(JNIEnv* env) : ScopedObjectAccessAlreadyRunnable(env), tsc_(Self(), kRunnable) { diff --git a/runtime/scoped_thread_state_change.cc b/runtime/scoped_thread_state_change.cc index 94354fc586ae12953afaaf6900c504fb1824ebcb..6a86cc64111b2664acb74246683a31b0d6303494 100644 --- a/runtime/scoped_thread_state_change.cc +++ b/runtime/scoped_thread_state_change.cc @@ -19,7 +19,6 @@ #include #include "base/casts.h" -#include "base/logging.h" #include "java_vm_ext.h" #include "obj_ptr-inl.h" #include "runtime-inl.h" diff --git a/runtime/scoped_thread_state_change.h b/runtime/scoped_thread_state_change.h index 02b6124118a114fb01b6d80a058436fdedf305a6..0c42c5ae8d7c3c9871b34ad06ebe6d7fa11410db 100644 --- a/runtime/scoped_thread_state_change.h +++ b/runtime/scoped_thread_state_change.h @@ -27,7 +27,7 @@ namespace art { class JavaVMExt; -struct JNIEnvExt; +class JNIEnvExt; template class ObjPtr; class Thread; diff --git a/runtime/signal_catcher.cc b/runtime/signal_catcher.cc index bf5d718113a5db910595e29f6ea37d277f3a1368..d9c4da9b96bbfeaca3e21f784aa29eac19b07619 100644 --- a/runtime/signal_catcher.cc +++ b/runtime/signal_catcher.cc @@ -35,6 +35,7 @@ #include "arch/instruction_set.h" #include "base/file_utils.h" +#include "base/logging.h" // For GetCmdLine. #include "base/time_utils.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" diff --git a/runtime/signal_set.h b/runtime/signal_set.h index 6f888525cb22e2f281ecdcea036f49f51a5b72cd..53613236fa7009493b00e6b18298e42f04dcef0f 100644 --- a/runtime/signal_set.h +++ b/runtime/signal_set.h @@ -19,7 +19,7 @@ #include -#include "base/logging.h" +#include namespace art { diff --git a/runtime/stack.cc b/runtime/stack.cc index ab9fb0d73f89ace6f9e679f0af0d7d0ad61ef8c5..bbea48b4419f7f7e9970475e509424dc322e3df3 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -154,13 +154,13 @@ mirror::Object* StackVisitor::GetThisObject() const { return cur_shadow_frame_->GetVRegReference(0); } } else { - const DexFile::CodeItem* code_item = m->GetCodeItem(); - if (code_item == nullptr) { + CodeItemDataAccessor accessor(m); + if (!accessor.HasCodeItem()) { UNIMPLEMENTED(ERROR) << "Failed to determine this object of abstract or proxy method: " << ArtMethod::PrettyMethod(m); return nullptr; } else { - uint16_t reg = code_item->registers_size_ - code_item->ins_size_; + uint16_t reg = accessor.RegistersSize() - accessor.InsSize(); uint32_t value = 0; bool success = GetVReg(m, reg, kReferenceVReg, &value); // We currently always guarantee the `this` object is live throughout the method. @@ -223,11 +223,11 @@ bool StackVisitor::GetVReg(ArtMethod* m, uint16_t vreg, VRegKind kind, uint32_t* bool StackVisitor::GetVRegFromOptimizedCode(ArtMethod* m, uint16_t vreg, VRegKind kind, uint32_t* val) const { DCHECK_EQ(m, GetMethod()); - const DexFile::CodeItem* code_item = m->GetCodeItem(); - DCHECK(code_item != nullptr) << m->PrettyMethod(); // Can't be null or how would we compile - // its instructions? - uint16_t number_of_dex_registers = code_item->registers_size_; - DCHECK_LT(vreg, code_item->registers_size_); + // Can't be null or how would we compile its instructions? + DCHECK(m->GetCodeItem() != nullptr) << m->PrettyMethod(); + CodeItemDataAccessor accessor(m); + uint16_t number_of_dex_registers = accessor.RegistersSize(); + DCHECK_LT(vreg, number_of_dex_registers); const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader(); CodeInfo code_info = method_header->GetOptimizedCodeInfo(); CodeInfoEncoding encoding = code_info.ExtractEncoding(); @@ -395,8 +395,8 @@ bool StackVisitor::SetVReg(ArtMethod* m, uint16_t vreg, uint32_t new_value, VRegKind kind) { - const DexFile::CodeItem* code_item = m->GetCodeItem(); - if (code_item == nullptr) { + CodeItemDataAccessor accessor(m); + if (!accessor.HasCodeItem()) { return false; } ShadowFrame* shadow_frame = GetCurrentShadowFrame(); @@ -404,7 +404,7 @@ bool StackVisitor::SetVReg(ArtMethod* m, // This is a compiled frame: we must prepare and update a shadow frame that will // be executed by the interpreter after deoptimization of the stack. const size_t frame_id = GetFrameId(); - const uint16_t num_regs = code_item->registers_size_; + const uint16_t num_regs = accessor.RegistersSize(); shadow_frame = thread_->FindOrCreateDebuggerShadowFrame(frame_id, num_regs, m, GetDexPc()); CHECK(shadow_frame != nullptr); // Remember the vreg has been set for debugging and must not be overwritten by the @@ -432,15 +432,15 @@ bool StackVisitor::SetVRegPair(ArtMethod* m, LOG(FATAL) << "Expected long or double: kind_lo=" << kind_lo << ", kind_hi=" << kind_hi; UNREACHABLE(); } - const DexFile::CodeItem* code_item = m->GetCodeItem(); - if (code_item == nullptr) { + CodeItemDataAccessor accessor(m); + if (!accessor.HasCodeItem()) { return false; } ShadowFrame* shadow_frame = GetCurrentShadowFrame(); if (shadow_frame == nullptr) { // This is a compiled frame: we must prepare for deoptimization (see SetVRegFromDebugger). const size_t frame_id = GetFrameId(); - const uint16_t num_regs = code_item->registers_size_; + const uint16_t num_regs = accessor.RegistersSize(); shadow_frame = thread_->FindOrCreateDebuggerShadowFrame(frame_id, num_regs, m, GetDexPc()); CHECK(shadow_frame != nullptr); // Remember the vreg pair has been set for debugging and must not be overwritten by the @@ -735,12 +735,19 @@ QuickMethodFrameInfo StackVisitor::GetCurrentQuickFrameInfo() const { return runtime->GetCalleeSaveMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs); } - // The only remaining case is if the method is native and uses the generic JNI stub. + // The only remaining case is if the method is native and uses the generic JNI stub, + // called either directly or through some (resolution, instrumentation) trampoline. DCHECK(method->IsNative()); - ClassLinker* class_linker = runtime->GetClassLinker(); - const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(method, - kRuntimePointerSize); - DCHECK(class_linker->IsQuickGenericJniStub(entry_point)) << method->PrettyMethod(); + if (kIsDebugBuild) { + ClassLinker* class_linker = runtime->GetClassLinker(); + const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(method, + kRuntimePointerSize); + CHECK(class_linker->IsQuickGenericJniStub(entry_point) || + // The current entrypoint (after filtering out trampolines) may have changed + // from GenericJNI to JIT-compiled stub since we have entered this frame. + (runtime->GetJit() != nullptr && + runtime->GetJit()->GetCodeCache()->ContainsPc(entry_point))) << method->PrettyMethod(); + } // Generic JNI frame. uint32_t handle_refs = GetNumberOfReferenceArgsWithoutReceiver(method) + 1; size_t scope_size = HandleScope::SizeOf(handle_refs); @@ -776,8 +783,48 @@ void StackVisitor::WalkStack(bool include_transitions) { // Can't be both a shadow and a quick fragment. DCHECK(current_fragment->GetTopShadowFrame() == nullptr); ArtMethod* method = *cur_quick_frame_; + DCHECK(method != nullptr); + bool header_retrieved = false; + if (method->IsNative()) { + // We do not have a PC for the first frame, so we cannot simply use + // ArtMethod::GetOatQuickMethodHeader() as we're unable to distinguish there + // between GenericJNI frame and JIT-compiled JNI stub; the entrypoint may have + // changed since the frame was entered. The top quick frame tag indicates + // GenericJNI here, otherwise it's either AOT-compiled or JNI-compiled JNI stub. + if (UNLIKELY(current_fragment->GetTopQuickFrameTag())) { + // The generic JNI does not have any method header. + cur_oat_quick_method_header_ = nullptr; + } else { + const void* existing_entry_point = method->GetEntryPointFromQuickCompiledCode(); + CHECK(existing_entry_point != nullptr); + Runtime* runtime = Runtime::Current(); + ClassLinker* class_linker = runtime->GetClassLinker(); + // Check whether we can quickly get the header from the current entrypoint. + if (!class_linker->IsQuickGenericJniStub(existing_entry_point) && + !class_linker->IsQuickResolutionStub(existing_entry_point) && + existing_entry_point != GetQuickInstrumentationEntryPoint()) { + cur_oat_quick_method_header_ = + OatQuickMethodHeader::FromEntryPoint(existing_entry_point); + } else { + const void* code = method->GetOatMethodQuickCode(class_linker->GetImagePointerSize()); + if (code != nullptr) { + cur_oat_quick_method_header_ = OatQuickMethodHeader::FromEntryPoint(code); + } else { + // This must be a JITted JNI stub frame. + CHECK(runtime->GetJit() != nullptr); + code = runtime->GetJit()->GetCodeCache()->GetJniStubCode(method); + CHECK(code != nullptr) << method->PrettyMethod(); + cur_oat_quick_method_header_ = OatQuickMethodHeader::FromCodePointer(code); + } + } + } + header_retrieved = true; + } while (method != nullptr) { - cur_oat_quick_method_header_ = method->GetOatQuickMethodHeader(cur_quick_frame_pc_); + if (!header_retrieved) { + cur_oat_quick_method_header_ = method->GetOatQuickMethodHeader(cur_quick_frame_pc_); + } + header_retrieved = false; // Force header retrieval in next iteration. SanityCheckFrame(); if ((walk_kind_ == StackWalkKind::kIncludeInlinedFrames) diff --git a/runtime/stack.h b/runtime/stack.h index bd6204f8d29fc6b58dbac67d26caece1ec140b9c..a16930bba05f44a83ec51354c7e9811eff61fabc 100644 --- a/runtime/stack.h +++ b/runtime/stack.h @@ -140,8 +140,7 @@ class StackVisitor { }; template - void WalkStack(bool include_transitions = false) - REQUIRES_SHARED(Locks::mutator_lock_); + void WalkStack(bool include_transitions = false) REQUIRES_SHARED(Locks::mutator_lock_); Thread* GetThread() const { return thread_; diff --git a/runtime/stack_map.h b/runtime/stack_map.h index db776eaa1acbc33c68727d731f97aa231364aa8b..85c734ee4c5edd83f88fceacf4faf15bd66dcc23 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -711,7 +711,11 @@ class StackMapEncoding { total_bit_size_ += MinimumBitsToStore(native_pc_max); dex_pc_bit_offset_ = total_bit_size_; - total_bit_size_ += MinimumBitsToStore(1 /* kNoDexPc */ + dex_pc_max); + // Note: We're not encoding the dex pc if there is none. That's the case + // for an intrinsified native method, such as String.charAt(). + if (dex_pc_max != dex::kDexNoIndex) { + total_bit_size_ += MinimumBitsToStore(1 /* kNoDexPc */ + dex_pc_max); + } // We also need +1 for kNoDexRegisterMap, but since the size is strictly // greater than any offset we might try to encode, we already implicitly have it. diff --git a/runtime/standard_dex_file.cc b/runtime/standard_dex_file.cc index 36bb37afe98df54b5301dd7dc4ab03751f67212d..4c1d3081d86bca42211fe9de3a7f2600b415c146 100644 --- a/runtime/standard_dex_file.cc +++ b/runtime/standard_dex_file.cc @@ -31,6 +31,16 @@ const uint8_t StandardDexFile::kDexMagicVersions[StandardDexFile::kNumDexVersion {'0', '3', '9', '\0'}, }; +void StandardDexFile::WriteMagic(uint8_t* magic) { + std::copy_n(kDexMagic, kDexMagicSize, magic); +} + +void StandardDexFile::WriteCurrentVersion(uint8_t* magic) { + std::copy_n(kDexMagicVersions[StandardDexFile::kDexVersionLen - 1], + kDexVersionLen, + magic + kDexMagicSize); +} + bool StandardDexFile::IsMagicValid(const uint8_t* magic) { return (memcmp(magic, kDexMagic, sizeof(kDexMagic)) == 0); } diff --git a/runtime/standard_dex_file.h b/runtime/standard_dex_file.h index 784ab318217d0d2aabd1d762eaacc62965f38465..5d5359776d8178baec96341c1ea63da1c5a81a4f 100644 --- a/runtime/standard_dex_file.h +++ b/runtime/standard_dex_file.h @@ -32,6 +32,18 @@ class StandardDexFile : public DexFile { // Same for now. }; + struct CodeItem : public DexFile::CodeItem { + private: + // TODO: Insert standard dex specific fields here. + DISALLOW_COPY_AND_ASSIGN(CodeItem); + }; + + // Write the standard dex specific magic. + static void WriteMagic(uint8_t* magic); + + // Write the current version, note that the input is the address of the magic. + static void WriteCurrentVersion(uint8_t* magic); + static const uint8_t kDexMagic[kDexMagicSize]; static constexpr size_t kNumDexVersions = 4; static const uint8_t kDexMagicVersions[kNumDexVersions][kDexVersionLen]; @@ -44,10 +56,6 @@ class StandardDexFile : public DexFile { static bool IsVersionValid(const uint8_t* magic); virtual bool IsVersionValid() const OVERRIDE; - bool IsStandardDexFile() const OVERRIDE { - return true; - } - private: StandardDexFile(const uint8_t* base, size_t size, @@ -55,7 +63,13 @@ class StandardDexFile : public DexFile { uint32_t location_checksum, const OatDexFile* oat_dex_file, DexFileContainer* container) - : DexFile(base, size, location, location_checksum, oat_dex_file, container) {} + : DexFile(base, + size, + location, + location_checksum, + oat_dex_file, + container, + /*is_compact_dex*/ false) {} friend class DexFileLoader; friend class DexFileVerifierTest; diff --git a/runtime/stride_iterator.h b/runtime/stride_iterator.h index 0560c33eee8f403737bf5db78b324e0cd99c632a..511c2c66f279cf0e9a57613cc85bcfd695c20d5d 100644 --- a/runtime/stride_iterator.h +++ b/runtime/stride_iterator.h @@ -19,7 +19,7 @@ #include -#include "base/logging.h" +#include namespace art { diff --git a/runtime/string_reference.h b/runtime/string_reference.h index d0ab4e40d0558c7ea6145e49f15bd2f8af5237c0..24a425371b48506c2e025969824b96d469ee21a0 100644 --- a/runtime/string_reference.h +++ b/runtime/string_reference.h @@ -19,7 +19,8 @@ #include -#include "base/logging.h" +#include + #include "dex_file-inl.h" #include "dex_file_reference.h" #include "dex_file_types.h" diff --git a/runtime/subtype_check.h b/runtime/subtype_check.h index 24f07632ce45789cd6a0f3a186f82b7a6eb1a3db..1556abe67f9f5f09368a9907b796cc624e60cbfd 100644 --- a/runtime/subtype_check.h +++ b/runtime/subtype_check.h @@ -274,7 +274,7 @@ struct SubtypeCheck { // more complicated (e.g. ObjPtr or Depth call) will fail dchecks. // OK. zero-initializing subtype_check_info_ puts us into the kUninitialized state. - SubtypeCheckBits scb_uninitialized = SubtypeCheckBits{}; // NOLINT + SubtypeCheckBits scb_uninitialized = SubtypeCheckBits{}; WriteSubtypeCheckBits(klass, scb_uninitialized); // Do not use "SubtypeCheckInfo" API here since that requires Depth() diff --git a/runtime/subtype_check_info.h b/runtime/subtype_check_info.h index d10d4728ada9d520fe72c2089cca6336cd1a40ff..8320fb35a90d27f5b57b0d5821fd6576fca902fa 100644 --- a/runtime/subtype_check_info.h +++ b/runtime/subtype_check_info.h @@ -90,10 +90,13 @@ namespace art { * * Uninitialized <=> StrLen(PathToRoot) == 0 * Next == 0 + * OF == False * Initialized <=> StrLen(PathToRoot) < Depth - * Next == 0 + * Next == 1 + * OF == False * Assigned <=> StrLen(PathToRoot) == Depth - * Next > 1 + * Next >= 1 + * OF == False * Overflowed <=> OF == True * * Tree Invariants: @@ -188,7 +191,7 @@ struct SubtypeCheckInfo { // Returns a new root SubtypeCheckInfo with a blank PathToRoot. // Post-condition: The return valued has an Assigned state. static SubtypeCheckInfo CreateRoot() { - SubtypeCheckInfo io{}; // NOLINT + SubtypeCheckInfo io{}; io.depth_ = 0u; io.SetNext(io.GetNext() + 1u); @@ -219,8 +222,8 @@ struct SubtypeCheckInfo { // Next must be non-0 to disambiguate it from Uninitialized. child.MaybeInitNext(); - // Always clear the inherited Parent's next Value on the child. - OverwriteNextValueFromParent(/*inout*/&child, BitStringChar{}); // NOLINT + // Always clear the inherited Parent's next Value, i.e. the child's last path entry. + OverwriteNextValueFromParent(/*inout*/&child, BitStringChar{}); // The state is now Initialized | Overflowed. DCHECK_NE(kAssigned, child.GetState()) << child.GetBitString(); @@ -235,7 +238,6 @@ struct SubtypeCheckInfo { // Assign attempt. if (HasNext() && !bitstring_and_of_.overflow_) { - // Do not bother assigning if parent had overflowed. BitStringChar next = GetNext(); if (next != next.MaximumValue()) { // The parent's "next" value is now the child's latest path element. @@ -260,17 +262,16 @@ struct SubtypeCheckInfo { // Get the current state (Uninitialized, Initialized, Assigned, or Overflowed). // See the "SubtypeCheckInfo" documentation above which explains how a state is determined. State GetState() const { - if (GetBitString().IsEmpty()) { - // Empty bitstring (all 0s) -> uninitialized. - DCHECK(!bitstring_and_of_.overflow_); - return kUninitialized; - } - if (bitstring_and_of_.overflow_) { // Overflowed if and only if the OF bit was set. return kOverflowed; } + if (GetBitString().IsEmpty()) { + // Empty bitstring (all 0s) -> uninitialized. + return kUninitialized; + } + // Either Assigned or Initialized. BitString path_to_root = GetPathToRoot(); @@ -387,12 +388,13 @@ struct SubtypeCheckInfo { SetBitStringUnchecked(bs); } + // If there is a next field, set it to 1. void MaybeInitNext() { if (HasNext()) { // Clearing out the "Next" value like this // is often an intermediate operation which temporarily // violates the invariants. Do not do the extra dchecks. - SetNextUnchecked(BitStringChar{}); // NOLINT + SetNextUnchecked(BitStringChar{}); SetNextUnchecked(GetNext()+1u); } } diff --git a/runtime/subtype_check_info_test.cc b/runtime/subtype_check_info_test.cc index bc2e84e37d95220073bf155f70addae11e132a3f..338d75a285fd2e1a2ebe5ea6063e35db7e01b0c4 100644 --- a/runtime/subtype_check_info_test.cc +++ b/runtime/subtype_check_info_test.cc @@ -47,7 +47,7 @@ BitStringChar MakeBitStringChar(size_t val) { BitString MakeBitString(std::initializer_list values = {}) { CHECK_GE(BitString::kCapacity, values.size()); - BitString bs{}; // NOLINT + BitString bs{}; size_t i = 0; for (size_t val : values) { @@ -68,7 +68,7 @@ size_t AsUint(const T& value) { // Make max bistring, e.g. BitString[4095,7,255] for {12,3,8} template BitString MakeBitStringMax() { - BitString bs{}; // NOLINT + BitString bs{}; for (size_t i = 0; i < kCount; ++i) { bs.SetAt(i, @@ -132,7 +132,7 @@ struct SubtypeCheckInfoTest : public ::testing::Test { // Create an SubtypeCheckInfo with the same depth, but with everything else reset. // Returns: SubtypeCheckInfo in the Uninitialized state. static SubtypeCheckInfo CopyCleared(SubtypeCheckInfo sc) { - SubtypeCheckInfo cleared_copy{}; // NOLINT + SubtypeCheckInfo cleared_copy{}; cleared_copy.depth_ = sc.depth_; DCHECK_EQ(SubtypeCheckInfo::kUninitialized, cleared_copy.GetState()); return cleared_copy; @@ -260,7 +260,7 @@ TEST_F(SubtypeCheckInfoTest, EncodedPathToRoot) { SubtypeCheckInfo io = MakeSubtypeCheckInfo(/*path_to_root*/MakeBitStringMax(), - /*next*/BitStringChar{}, // NOLINT + /*next*/BitStringChar{}, /*overflow*/false, /*depth*/BitString::kCapacity); // 0b11111...000 where MSB == 1, and leading 1s = the maximum bitstring representation. @@ -329,7 +329,7 @@ TEST_F(SubtypeCheckInfoTest, CopyCleared) { // CopyCleared is just a thin wrapper around value-init and providing the depth. SubtypeCheckInfo cleared_copy_value = - SubtypeCheckInfo::Create(SubtypeCheckBits{}, /*depth*/1u); // NOLINT + SubtypeCheckInfo::Create(SubtypeCheckBits{}, /*depth*/1u); EXPECT_EQ(SubtypeCheckInfo::kUninitialized, cleared_copy_value.GetState()); EXPECT_EQ(MakeBitString({}), GetPathToRoot(cleared_copy_value)); } diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h index b5a962691bd017a84de93981eceaa62e80d7c0cd..7392dcf6a5859bfd5475fe27b9137f4c7e8967c8 100644 --- a/runtime/thread-inl.h +++ b/runtime/thread-inl.h @@ -19,6 +19,7 @@ #include "thread.h" +#include "base/aborting.h" #include "base/casts.h" #include "base/mutex-inl.h" #include "base/time_utils.h" @@ -33,7 +34,7 @@ namespace art { // Quickly access the current thread from a JNIEnv. static inline Thread* ThreadForEnv(JNIEnv* env) { JNIEnvExt* full_env(down_cast(env)); - return full_env->self; + return full_env->GetSelf(); } inline void Thread::AllowThreadSuspension() { diff --git a/runtime/thread.cc b/runtime/thread.cc index 712eabc8888b8d4132ccda93936c484a4da54c1d..e3a17c2737a4b3395ae1cbeefc98673c09f76c4a 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -70,6 +70,7 @@ #include "mirror/object_array-inl.h" #include "mirror/stack_trace_element.h" #include "monitor.h" +#include "monitor_objects_stack_visitor.h" #include "native_stack_dump.h" #include "nativehelper/scoped_local_ref.h" #include "nativehelper/scoped_utf_chars.h" @@ -620,7 +621,7 @@ void Thread::InstallImplicitProtection() { void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_size, bool is_daemon) { CHECK(java_peer != nullptr); - Thread* self = static_cast(env)->self; + Thread* self = static_cast(env)->GetSelf(); if (VLOG_IS_ON(threads)) { ScopedObjectAccess soa(env); @@ -753,8 +754,8 @@ bool Thread::Init(ThreadList* thread_list, JavaVMExt* java_vm, JNIEnvExt* jni_en tls32_.thin_lock_thread_id = thread_list->AllocThreadId(this); if (jni_env_ext != nullptr) { - DCHECK_EQ(jni_env_ext->vm, java_vm); - DCHECK_EQ(jni_env_ext->self, this); + DCHECK_EQ(jni_env_ext->GetVm(), java_vm); + DCHECK_EQ(jni_env_ext->GetSelf(), this); tlsPtr_.jni_env = jni_env_ext; } else { std::string error_msg; @@ -773,14 +774,16 @@ template Thread* Thread::Attach(const char* thread_name, bool as_daemon, PeerAction peer_action) { Runtime* runtime = Runtime::Current(); if (runtime == nullptr) { - LOG(ERROR) << "Thread attaching to non-existent runtime: " << thread_name; + LOG(ERROR) << "Thread attaching to non-existent runtime: " << + ((thread_name != nullptr) ? thread_name : "(Unnamed)"); return nullptr; } Thread* self; { MutexLock mu(nullptr, *Locks::runtime_shutdown_lock_); if (runtime->IsShuttingDownLocked()) { - LOG(WARNING) << "Thread attaching while runtime is shutting down: " << thread_name; + LOG(WARNING) << "Thread attaching while runtime is shutting down: " << + ((thread_name != nullptr) ? thread_name : "(Unnamed)"); return nullptr; } else { Runtime::Current()->StartThreadBirth(); @@ -850,7 +853,7 @@ Thread* Thread::Attach(const char* thread_name, if (thread_name != nullptr) { self->tlsPtr_.name->assign(thread_name); ::art::SetThreadName(thread_name); - } else if (self->GetJniEnv()->check_jni) { + } else if (self->GetJniEnv()->IsCheckJniEnabled()) { LOG(WARNING) << *Thread::Current() << " attached without supplying a name"; } } @@ -1756,25 +1759,22 @@ void Thread::DumpState(std::ostream& os) const { Thread::DumpState(os, this, GetTid()); } -struct StackDumpVisitor : public StackVisitor { +struct StackDumpVisitor : public MonitorObjectsStackVisitor { StackDumpVisitor(std::ostream& os_in, Thread* thread_in, Context* context, - bool can_allocate_in, + bool can_allocate, bool check_suspended = true, - bool dump_locks_in = true) + bool dump_locks = true) REQUIRES_SHARED(Locks::mutator_lock_) - : StackVisitor(thread_in, - context, - StackVisitor::StackWalkKind::kIncludeInlinedFrames, - check_suspended), + : MonitorObjectsStackVisitor(thread_in, + context, + check_suspended, + can_allocate && dump_locks), os(os_in), - can_allocate(can_allocate_in), last_method(nullptr), last_line_number(0), - repetition_count(0), - frame_count(0), - dump_locks(dump_locks_in) {} + repetition_count(0) {} virtual ~StackDumpVisitor() { if (frame_count == 0) { @@ -1782,13 +1782,12 @@ struct StackDumpVisitor : public StackVisitor { } } - bool VisitFrame() REQUIRES_SHARED(Locks::mutator_lock_) { - ArtMethod* m = GetMethod(); - if (m->IsRuntimeMethod()) { - return true; - } + static constexpr size_t kMaxRepetition = 3u; + + VisitMethodResult StartMethod(ArtMethod* m, size_t frame_nr ATTRIBUTE_UNUSED) + OVERRIDE + REQUIRES_SHARED(Locks::mutator_lock_) { m = m->GetInterfaceMethodIfProxy(kRuntimePointerSize); - const int kMaxRepetition = 3; ObjPtr c = m->GetDeclaringClass(); ObjPtr dex_cache = c->GetDexCache(); int line_number = -1; @@ -1806,67 +1805,97 @@ struct StackDumpVisitor : public StackVisitor { last_line_number = line_number; last_method = m; } - if (repetition_count < kMaxRepetition) { - os << " at " << m->PrettyMethod(false); - if (m->IsNative()) { - os << "(Native method)"; - } else { - const char* source_file(m->GetDeclaringClassSourceFile()); - os << "(" << (source_file != nullptr ? source_file : "unavailable") - << ":" << line_number << ")"; - } - os << "\n"; - if (frame_count == 0) { - Monitor::DescribeWait(os, GetThread()); - } - if (can_allocate && dump_locks) { - // Visit locks, but do not abort on errors. This would trigger a nested abort. - // Skip visiting locks if dump_locks is false as it would cause a bad_mutexes_held in - // RegTypeCache::RegTypeCache due to thread_list_lock. - Monitor::VisitLocks(this, DumpLockedObject, &os, false); - } + + if (repetition_count >= kMaxRepetition) { + // Skip visiting=printing anything. + return VisitMethodResult::kSkipMethod; } - ++frame_count; - return true; + os << " at " << m->PrettyMethod(false); + if (m->IsNative()) { + os << "(Native method)"; + } else { + const char* source_file(m->GetDeclaringClassSourceFile()); + os << "(" << (source_file != nullptr ? source_file : "unavailable") + << ":" << line_number << ")"; + } + os << "\n"; + // Go and visit locks. + return VisitMethodResult::kContinueMethod; + } + + VisitMethodResult EndMethod(ArtMethod* m ATTRIBUTE_UNUSED) OVERRIDE { + return VisitMethodResult::kContinueMethod; } - static void DumpLockedObject(mirror::Object* o, void* context) + void VisitWaitingObject(mirror::Object* obj, ThreadState state ATTRIBUTE_UNUSED) + OVERRIDE + REQUIRES_SHARED(Locks::mutator_lock_) { + PrintObject(obj, " - waiting on ", ThreadList::kInvalidThreadId); + } + void VisitSleepingObject(mirror::Object* obj) + OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { - std::ostream& os = *reinterpret_cast(context); - os << " - locked "; - if (o == nullptr) { - os << "an unknown object"; + PrintObject(obj, " - sleeping on ", ThreadList::kInvalidThreadId); + } + void VisitBlockedOnObject(mirror::Object* obj, + ThreadState state, + uint32_t owner_tid) + OVERRIDE + REQUIRES_SHARED(Locks::mutator_lock_) { + const char* msg; + switch (state) { + case kBlocked: + msg = " - waiting to lock "; + break; + + case kWaitingForLockInflation: + msg = " - waiting for lock inflation of "; + break; + + default: + LOG(FATAL) << "Unreachable"; + UNREACHABLE(); + } + PrintObject(obj, msg, owner_tid); + } + void VisitLockedObject(mirror::Object* obj) + OVERRIDE + REQUIRES_SHARED(Locks::mutator_lock_) { + PrintObject(obj, " - locked ", ThreadList::kInvalidThreadId); + } + + void PrintObject(mirror::Object* obj, + const char* msg, + uint32_t owner_tid) REQUIRES_SHARED(Locks::mutator_lock_) { + if (obj == nullptr) { + os << msg << "an unknown object"; } else { - if (kUseReadBarrier && Thread::Current()->GetIsGcMarking()) { - // We may call Thread::Dump() in the middle of the CC thread flip and this thread's stack - // may have not been flipped yet and "o" may be a from-space (stale) ref, in which case the - // IdentityHashCode call below will crash. So explicitly mark/forward it here. - o = ReadBarrier::Mark(o); - } - if ((o->GetLockWord(false).GetState() == LockWord::kThinLocked) && + if ((obj->GetLockWord(true).GetState() == LockWord::kThinLocked) && Locks::mutator_lock_->IsExclusiveHeld(Thread::Current())) { // Getting the identity hashcode here would result in lock inflation and suspension of the // current thread, which isn't safe if this is the only runnable thread. - os << StringPrintf("<@addr=0x%" PRIxPTR "> (a %s)", reinterpret_cast(o), - o->PrettyTypeOf().c_str()); + os << msg << StringPrintf("<@addr=0x%" PRIxPTR "> (a %s)", + reinterpret_cast(obj), + obj->PrettyTypeOf().c_str()); } else { - // IdentityHashCode can cause thread suspension, which would invalidate o if it moved. So - // we get the pretty type beofre we call IdentityHashCode. - const std::string pretty_type(o->PrettyTypeOf()); - os << StringPrintf("<0x%08x> (a %s)", o->IdentityHashCode(), pretty_type.c_str()); + // - waiting on <0x6008c468> (a java.lang.Class) + // Call PrettyTypeOf before IdentityHashCode since IdentityHashCode can cause thread + // suspension and move pretty_object. + const std::string pretty_type(obj->PrettyTypeOf()); + os << msg << StringPrintf("<0x%08x> (a %s)", obj->IdentityHashCode(), pretty_type.c_str()); } } + if (owner_tid != ThreadList::kInvalidThreadId) { + os << " held by thread " << owner_tid; + } os << "\n"; } std::ostream& os; - const bool can_allocate; ArtMethod* last_method; int last_line_number; - int repetition_count; - int frame_count; - const bool dump_locks; + size_t repetition_count; }; static bool ShouldShowNativeStack(const Thread* thread) @@ -1884,9 +1913,7 @@ static bool ShouldShowNativeStack(const Thread* thread) } // Threads with no managed stack frames should be shown. - const ManagedStack* managed_stack = thread->GetManagedStack(); - if (managed_stack == nullptr || (managed_stack->GetTopQuickFrame() == nullptr && - managed_stack->GetTopShadowFrame() == nullptr)) { + if (!thread->HasManagedStack()) { return true; } @@ -2143,7 +2170,7 @@ void Thread::Destroy() { ScopedObjectAccess soa(self); MonitorExitVisitor visitor(self); // On thread detach, all monitors entered with JNI MonitorEnter are automatically exited. - tlsPtr_.jni_env->monitors.VisitRoots(&visitor, RootInfo(kRootVMInternal)); + tlsPtr_.jni_env->monitors_.VisitRoots(&visitor, RootInfo(kRootVMInternal)); } // Release locally held global references which releasing may require the mutator lock. if (tlsPtr_.jpeer != nullptr) { @@ -2312,7 +2339,7 @@ ObjPtr Thread::DecodeJObject(jobject obj) const { bool expect_null = false; // The "kinds" below are sorted by the frequency we expect to encounter them. if (kind == kLocal) { - IndirectReferenceTable& locals = tlsPtr_.jni_env->locals; + IndirectReferenceTable& locals = tlsPtr_.jni_env->locals_; // Local references do not need a read barrier. result = locals.Get(ref); } else if (kind == kHandleScopeOrInvalid) { @@ -2323,15 +2350,15 @@ ObjPtr Thread::DecodeJObject(jobject obj) const { result = reinterpret_cast*>(obj)->AsMirrorPtr(); VerifyObject(result); } else { - tlsPtr_.jni_env->vm->JniAbortF(nullptr, "use of invalid jobject %p", obj); + tlsPtr_.jni_env->vm_->JniAbortF(nullptr, "use of invalid jobject %p", obj); expect_null = true; result = nullptr; } } else if (kind == kGlobal) { - result = tlsPtr_.jni_env->vm->DecodeGlobal(ref); + result = tlsPtr_.jni_env->vm_->DecodeGlobal(ref); } else { DCHECK_EQ(kind, kWeakGlobal); - result = tlsPtr_.jni_env->vm->DecodeWeakGlobal(const_cast(this), ref); + result = tlsPtr_.jni_env->vm_->DecodeWeakGlobal(const_cast(this), ref); if (Runtime::Current()->IsClearedJniWeakGlobal(result)) { // This is a special case where it's okay to return null. expect_null = true; @@ -2340,7 +2367,7 @@ ObjPtr Thread::DecodeJObject(jobject obj) const { } if (UNLIKELY(!expect_null && result == nullptr)) { - tlsPtr_.jni_env->vm->JniAbortF(nullptr, "use of deleted %s %p", + tlsPtr_.jni_env->vm_->JniAbortF(nullptr, "use of deleted %s %p", ToStr(kind).c_str(), obj); } return result; @@ -2351,7 +2378,7 @@ bool Thread::IsJWeakCleared(jweak obj) const { IndirectRef ref = reinterpret_cast(obj); IndirectRefKind kind = IndirectReferenceTable::GetIndirectRefKind(ref); CHECK_EQ(kind, kWeakGlobal); - return tlsPtr_.jni_env->vm->IsWeakGlobalCleared(const_cast(this), ref); + return tlsPtr_.jni_env->vm_->IsWeakGlobalCleared(const_cast(this), ref); } // Implements java.lang.Thread.interrupted. @@ -2605,6 +2632,61 @@ bool Thread::IsExceptionThrownByCurrentMethod(ObjPtr exceptio return count_visitor.GetDepth() == static_cast(exception->GetStackDepth()); } +static ObjPtr CreateStackTraceElement( + const ScopedObjectAccessAlreadyRunnable& soa, + ArtMethod* method, + uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_) { + int32_t line_number; + StackHandleScope<3> hs(soa.Self()); + auto class_name_object(hs.NewHandle(nullptr)); + auto source_name_object(hs.NewHandle(nullptr)); + if (method->IsProxyMethod()) { + line_number = -1; + class_name_object.Assign(method->GetDeclaringClass()->GetName()); + // source_name_object intentionally left null for proxy methods + } else { + line_number = method->GetLineNumFromDexPC(dex_pc); + // Allocate element, potentially triggering GC + // TODO: reuse class_name_object via Class::name_? + const char* descriptor = method->GetDeclaringClassDescriptor(); + CHECK(descriptor != nullptr); + std::string class_name(PrettyDescriptor(descriptor)); + class_name_object.Assign( + mirror::String::AllocFromModifiedUtf8(soa.Self(), class_name.c_str())); + if (class_name_object == nullptr) { + soa.Self()->AssertPendingOOMException(); + return nullptr; + } + const char* source_file = method->GetDeclaringClassSourceFile(); + if (line_number == -1) { + // Make the line_number field of StackTraceElement hold the dex pc. + // source_name_object is intentionally left null if we failed to map the dex pc to + // a line number (most probably because there is no debug info). See b/30183883. + line_number = dex_pc; + } else { + if (source_file != nullptr) { + source_name_object.Assign(mirror::String::AllocFromModifiedUtf8(soa.Self(), source_file)); + if (source_name_object == nullptr) { + soa.Self()->AssertPendingOOMException(); + return nullptr; + } + } + } + } + const char* method_name = method->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetName(); + CHECK(method_name != nullptr); + Handle method_name_object( + hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), method_name))); + if (method_name_object == nullptr) { + return nullptr; + } + return mirror::StackTraceElement::Alloc(soa.Self(), + class_name_object, + method_name_object, + source_name_object, + line_number); +} + jobjectArray Thread::InternalStackTraceToStackTraceElementArray( const ScopedObjectAccessAlreadyRunnable& soa, jobject internal, @@ -2651,55 +2733,7 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray( ArtMethod* method = method_trace->GetElementPtrSize(i, kRuntimePointerSize); uint32_t dex_pc = method_trace->GetElementPtrSize( i + method_trace->GetLength() / 2, kRuntimePointerSize); - int32_t line_number; - StackHandleScope<3> hs(soa.Self()); - auto class_name_object(hs.NewHandle(nullptr)); - auto source_name_object(hs.NewHandle(nullptr)); - if (method->IsProxyMethod()) { - line_number = -1; - class_name_object.Assign(method->GetDeclaringClass()->GetName()); - // source_name_object intentionally left null for proxy methods - } else { - line_number = method->GetLineNumFromDexPC(dex_pc); - // Allocate element, potentially triggering GC - // TODO: reuse class_name_object via Class::name_? - const char* descriptor = method->GetDeclaringClassDescriptor(); - CHECK(descriptor != nullptr); - std::string class_name(PrettyDescriptor(descriptor)); - class_name_object.Assign( - mirror::String::AllocFromModifiedUtf8(soa.Self(), class_name.c_str())); - if (class_name_object == nullptr) { - soa.Self()->AssertPendingOOMException(); - return nullptr; - } - const char* source_file = method->GetDeclaringClassSourceFile(); - if (line_number == -1) { - // Make the line_number field of StackTraceElement hold the dex pc. - // source_name_object is intentionally left null if we failed to map the dex pc to - // a line number (most probably because there is no debug info). See b/30183883. - line_number = dex_pc; - } else { - if (source_file != nullptr) { - source_name_object.Assign(mirror::String::AllocFromModifiedUtf8(soa.Self(), source_file)); - if (source_name_object == nullptr) { - soa.Self()->AssertPendingOOMException(); - return nullptr; - } - } - } - } - const char* method_name = method->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetName(); - CHECK(method_name != nullptr); - Handle method_name_object( - hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), method_name))); - if (method_name_object == nullptr) { - return nullptr; - } - ObjPtr obj = mirror::StackTraceElement::Alloc(soa.Self(), - class_name_object, - method_name_object, - source_name_object, - line_number); + ObjPtr obj = CreateStackTraceElement(soa, method, dex_pc); if (obj == nullptr) { return nullptr; } @@ -3391,7 +3425,7 @@ class ReferenceMapVisitor : public StackVisitor { const CodeInfoEncoding& _encoding, const StackMap& map, RootVisitor& _visitor) - : number_of_dex_registers(method->GetCodeItem()->registers_size_), + : number_of_dex_registers(CodeItemDataAccessor(method).RegistersSize()), code_info(_code_info), encoding(_encoding), dex_register_map(code_info.GetDexRegisterMapOf(map, @@ -3482,8 +3516,8 @@ void Thread::VisitRoots(RootVisitor* visitor) { RootInfo(kRootNativeStack, thread_id)); } visitor->VisitRootIfNonNull(&tlsPtr_.monitor_enter_object, RootInfo(kRootNativeStack, thread_id)); - tlsPtr_.jni_env->locals.VisitRoots(visitor, RootInfo(kRootJNILocal, thread_id)); - tlsPtr_.jni_env->monitors.VisitRoots(visitor, RootInfo(kRootJNIMonitor, thread_id)); + tlsPtr_.jni_env->VisitJniLocalRoots(visitor, RootInfo(kRootJNILocal, thread_id)); + tlsPtr_.jni_env->VisitMonitorRoots(visitor, RootInfo(kRootJNIMonitor, thread_id)); HandleScopeVisitRoots(visitor, thread_id); if (tlsPtr_.debug_invoke_req != nullptr) { tlsPtr_.debug_invoke_req->VisitRoots(visitor, RootInfo(kRootDebugger, thread_id)); @@ -3702,6 +3736,7 @@ void Thread::DeoptimizeWithDeoptimizationException(JValue* result) { void Thread::SetAsyncException(ObjPtr new_exception) { CHECK(new_exception != nullptr); + Runtime::Current()->SetAsyncExceptionsThrown(); if (kIsDebugBuild) { // Make sure we are in a checkpoint. MutexLock mu(Thread::Current(), *Locks::thread_suspend_count_lock_); diff --git a/runtime/thread.h b/runtime/thread.h index 39be66d5c2fdb726d2331f11de592ebaccc194b8..0f1d7a4a8aac2d6cff326369d4af9990d0f8c0b8 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -86,7 +86,7 @@ class DeoptimizationContextRecord; class DexFile; class FrameIdToShadowFrame; class JavaVMExt; -struct JNIEnvExt; +class JNIEnvExt; class Monitor; class RootVisitor; class ScopedObjectAccessAlreadyRunnable; @@ -474,13 +474,16 @@ class Thread { tlsPtr_.managed_stack.SetTopQuickFrame(top_method); } + void SetTopOfStackTagged(ArtMethod** top_method) { + tlsPtr_.managed_stack.SetTopQuickFrameTagged(top_method); + } + void SetTopOfShadowStack(ShadowFrame* top) { tlsPtr_.managed_stack.SetTopShadowFrame(top); } bool HasManagedStack() const { - return (tlsPtr_.managed_stack.GetTopQuickFrame() != nullptr) || - (tlsPtr_.managed_stack.GetTopShadowFrame() != nullptr); + return tlsPtr_.managed_stack.HasTopQuickFrame() || tlsPtr_.managed_stack.HasTopShadowFrame(); } // If 'msg' is null, no detail message is set. @@ -833,7 +836,7 @@ class Thread { static ThreadOffset TopOfManagedStackOffset() { return ThreadOffsetFromTlsPtr( OFFSETOF_MEMBER(tls_ptr_sized_values, managed_stack) + - ManagedStack::TopQuickFrameOffset()); + ManagedStack::TaggedTopQuickFrameOffset()); } const ManagedStack* GetManagedStack() const { diff --git a/runtime/thread_linux.cc b/runtime/thread_linux.cc index b922d94617c8d3b864143240bc73ff1975e2be2b..9673eee795676a1d54d1ae2d185bb6746452105c 100644 --- a/runtime/thread_linux.cc +++ b/runtime/thread_linux.cc @@ -14,9 +14,11 @@ * limitations under the License. */ +#include "thread.h" + #include -#include "thread.h" +#include "base/logging.h" // For VLOG. #include "utils.h" namespace art { diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 88f1fc69912764ac834b4f5d35ee542048c5e56c..8095ef57c701112a638ed3f8110e762781ba85a4 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -28,6 +28,7 @@ #include "nativehelper/scoped_local_ref.h" #include "nativehelper/scoped_utf_chars.h" +#include "base/aborting.h" #include "base/histogram-inl.h" #include "base/mutex-inl.h" #include "base/systrace.h" @@ -68,8 +69,7 @@ static constexpr useconds_t kThreadSuspendMaxSleepUs = 5000; // Whether we should try to dump the native stack of unattached threads. See commit ed8b723 for // some history. -// Turned off again. b/29248079 -static constexpr bool kDumpUnattachedThreadNativeStackForSigQuit = false; +static constexpr bool kDumpUnattachedThreadNativeStackForSigQuit = true; ThreadList::ThreadList(uint64_t thread_suspend_timeout_ns) : suspend_all_count_(0), @@ -288,12 +288,17 @@ void ThreadList::AssertThreadsAreSuspended(Thread* self, Thread* ignore1, Thread #if HAVE_TIMED_RWLOCK // Attempt to rectify locks so that we dump thread list with required locks before exiting. NO_RETURN static void UnsafeLogFatalForThreadSuspendAllTimeout() { + // Increment gAborting before doing the thread list dump since we don't want any failures from + // AssertThreadSuspensionIsAllowable in cases where thread suspension is not allowed. + // See b/69044468. + ++gAborting; Runtime* runtime = Runtime::Current(); std::ostringstream ss; ss << "Thread suspend timeout\n"; Locks::mutator_lock_->Dump(ss); ss << "\n"; runtime->GetThreadList()->Dump(ss); + --gAborting; LOG(FATAL) << ss.str(); exit(0); } @@ -302,6 +307,8 @@ NO_RETURN static void UnsafeLogFatalForThreadSuspendAllTimeout() { // Unlike suspending all threads where we can wait to acquire the mutator_lock_, suspending an // individual thread requires polling. delay_us is the requested sleep wait. If delay_us is 0 then // we use sched_yield instead of calling usleep. +// Although there is the possibility, here and elsewhere, that usleep could return -1 and +// errno = EINTR, there should be no problem if interrupted, so we do not check. static void ThreadSuspendSleep(useconds_t delay_us) { if (delay_us == 0) { sched_yield(); @@ -357,11 +364,11 @@ size_t ThreadList::RunCheckpoint(Closure* checkpoint_function, Closure* callback // Run the checkpoint on the suspended threads. for (const auto& thread : suspended_count_modified_threads) { if (!thread->IsSuspended()) { - if (ATRACE_ENABLED()) { + ScopedTrace trace([&]() { std::ostringstream oss; thread->ShortDump(oss); - ATRACE_BEGIN((std::string("Waiting for suspension of thread ") + oss.str()).c_str()); - } + return std::string("Waiting for suspension of thread ") + oss.str(); + }); // Busy wait until the thread is suspended. const uint64_t start_time = NanoTime(); do { @@ -370,7 +377,6 @@ size_t ThreadList::RunCheckpoint(Closure* checkpoint_function, Closure* callback const uint64_t total_delay = NanoTime() - start_time; // Shouldn't need to wait for longer than 1000 microseconds. constexpr uint64_t kLongWaitThreshold = MsToNs(1); - ATRACE_END(); if (UNLIKELY(total_delay > kLongWaitThreshold)) { LOG(WARNING) << "Long wait of " << PrettyDuration(total_delay) << " for " << *thread << " suspension!"; diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc index cffaffc0456206fcba29fe1d22682fe7543c3c2c..386cdf006acfb781b26ee4d1fe6c3daf148845c4 100644 --- a/runtime/thread_pool.cc +++ b/runtime/thread_pool.cc @@ -22,11 +22,11 @@ #include -#include "android-base/stringprintf.h" +#include +#include #include "base/bit_utils.h" #include "base/casts.h" -#include "base/logging.h" #include "base/stl_util.h" #include "base/time_utils.h" #include "runtime.h" diff --git a/runtime/ti/agent.cc b/runtime/ti/agent.cc index 3bf169ad408fdca860889d169429b5f2d88d4c72..548752e980f726b509d45cbf7810f29f4e318eb9 100644 --- a/runtime/ti/agent.cc +++ b/runtime/ti/agent.cc @@ -21,6 +21,8 @@ #include "base/strlcpy.h" #include "java_vm_ext.h" #include "runtime.h" +#include "thread-current-inl.h" +#include "scoped_thread_state_change-inl.h" namespace art { namespace ti { @@ -35,6 +37,7 @@ const char* AGENT_ON_UNLOAD_FUNCTION_NAME = "Agent_OnUnload"; Agent::LoadError Agent::DoLoadHelper(bool attaching, /*out*/jint* call_res, /*out*/std::string* error_msg) { + ScopedThreadStateChange stsc(Thread::Current(), ThreadState::kNative); DCHECK(call_res != nullptr); DCHECK(error_msg != nullptr); diff --git a/runtime/trace.cc b/runtime/trace.cc index 4b5a7610a302f47825b59d097f6c3306d6a7395a..a113ab5cc8a6d8415184a8c8121d24255d9d41ca 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -413,42 +413,40 @@ void Trace::StopTracing(bool finish_tracing, bool flush_file) { sampling_pthread_ = 0U; } - { + if (the_trace != nullptr) { + stop_alloc_counting = (the_trace->flags_ & Trace::kTraceCountAllocs) != 0; + if (finish_tracing) { + the_trace->FinishTracing(); + } gc::ScopedGCCriticalSection gcs(self, gc::kGcCauseInstrumentation, gc::kCollectorTypeInstrumentation); ScopedSuspendAll ssa(__FUNCTION__); - if (the_trace != nullptr) { - stop_alloc_counting = (the_trace->flags_ & Trace::kTraceCountAllocs) != 0; - if (finish_tracing) { - the_trace->FinishTracing(); - } - if (the_trace->trace_mode_ == TraceMode::kSampling) { - MutexLock mu(self, *Locks::thread_list_lock_); - runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, nullptr); + if (the_trace->trace_mode_ == TraceMode::kSampling) { + MutexLock mu(self, *Locks::thread_list_lock_); + runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, nullptr); + } else { + runtime->GetInstrumentation()->DisableMethodTracing(kTracerInstrumentationKey); + runtime->GetInstrumentation()->RemoveListener( + the_trace, instrumentation::Instrumentation::kMethodEntered | + instrumentation::Instrumentation::kMethodExited | + instrumentation::Instrumentation::kMethodUnwind); + } + if (the_trace->trace_file_.get() != nullptr) { + // Do not try to erase, so flush and close explicitly. + if (flush_file) { + if (the_trace->trace_file_->Flush() != 0) { + PLOG(WARNING) << "Could not flush trace file."; + } } else { - runtime->GetInstrumentation()->DisableMethodTracing(kTracerInstrumentationKey); - runtime->GetInstrumentation()->RemoveListener( - the_trace, instrumentation::Instrumentation::kMethodEntered | - instrumentation::Instrumentation::kMethodExited | - instrumentation::Instrumentation::kMethodUnwind); + the_trace->trace_file_->MarkUnchecked(); // Do not trigger guard. } - if (the_trace->trace_file_.get() != nullptr) { - // Do not try to erase, so flush and close explicitly. - if (flush_file) { - if (the_trace->trace_file_->Flush() != 0) { - PLOG(WARNING) << "Could not flush trace file."; - } - } else { - the_trace->trace_file_->MarkUnchecked(); // Do not trigger guard. - } - if (the_trace->trace_file_->Close() != 0) { - PLOG(ERROR) << "Could not close trace file."; - } + if (the_trace->trace_file_->Close() != 0) { + PLOG(ERROR) << "Could not close trace file."; } - delete the_trace; } + delete the_trace; } if (stop_alloc_counting) { // Can be racy since SetStatsEnabled is not guarded by any locks. @@ -717,12 +715,12 @@ void Trace::FinishTracing() { FlushBuf(); } else { if (trace_file_.get() == nullptr) { - iovec iov[2]; - iov[0].iov_base = reinterpret_cast(const_cast(header.c_str())); - iov[0].iov_len = header.length(); - iov[1].iov_base = buf_.get(); - iov[1].iov_len = final_offset; - Dbg::DdmSendChunkV(CHUNK_TYPE("MPSE"), iov, 2); + std::vector data; + data.resize(header.length() + final_offset); + memcpy(data.data(), header.c_str(), header.length()); + memcpy(data.data() + header.length(), buf_.get(), final_offset); + Runtime::Current()->GetRuntimeCallbacks()->DdmPublishChunk(CHUNK_TYPE("MPSE"), + ArrayRef(data)); const bool kDumpTraceInfo = false; if (kDumpTraceInfo) { LOG(INFO) << "Trace sent:\n" << header; diff --git a/runtime/transaction.cc b/runtime/transaction.cc index e923aff43905b3cd894eca81fbbf168d9bb4c008..c9766bc9ca3774d33ca759e87669a1e8b0024f72 100644 --- a/runtime/transaction.cc +++ b/runtime/transaction.cc @@ -16,7 +16,8 @@ #include "transaction.h" -#include "base/logging.h" +#include + #include "base/stl_util.h" #include "gc/accounting/card_table-inl.h" #include "gc_root-inl.h" diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc index e52dd0854082713cd14111bc2faadcbc31bd5c5a..304017eadba02b5014c5ac8cb1a2312661a0cd5e 100644 --- a/runtime/transaction_test.cc +++ b/runtime/transaction_test.cc @@ -493,7 +493,7 @@ TEST_F(TransactionTest, ResolveString) { dex::StringIndex string_idx = dex_file->GetIndexForStringId(*string_id); ASSERT_TRUE(string_idx.IsValid()); // String should only get resolved by the initializer. - EXPECT_TRUE(class_linker_->LookupString(*dex_file, string_idx, h_dex_cache.Get()) == nullptr); + EXPECT_TRUE(class_linker_->LookupString(string_idx, h_dex_cache.Get()) == nullptr); EXPECT_TRUE(h_dex_cache->GetResolvedString(string_idx) == nullptr); // Do the transaction, then roll back. Runtime::Current()->EnterTransactionMode(); @@ -502,14 +502,15 @@ TEST_F(TransactionTest, ResolveString) { ASSERT_TRUE(h_klass->IsInitialized()); // Make sure the string got resolved by the transaction. { - mirror::String* s = class_linker_->LookupString(*dex_file, string_idx, h_dex_cache.Get()); + ObjPtr s = + class_linker_->LookupString(string_idx, h_dex_cache.Get()); ASSERT_TRUE(s != nullptr); EXPECT_STREQ(s->ToModifiedUtf8().c_str(), kResolvedString); - EXPECT_EQ(s, h_dex_cache->GetResolvedString(string_idx)); + EXPECT_EQ(s.Ptr(), h_dex_cache->GetResolvedString(string_idx)); } Runtime::Current()->RollbackAndExitTransactionMode(); // Check that the string did not stay resolved. - EXPECT_TRUE(class_linker_->LookupString(*dex_file, string_idx, h_dex_cache.Get()) == nullptr); + EXPECT_TRUE(class_linker_->LookupString(string_idx, h_dex_cache.Get()) == nullptr); EXPECT_TRUE(h_dex_cache->GetResolvedString(string_idx) == nullptr); ASSERT_FALSE(h_klass->IsInitialized()); ASSERT_FALSE(soa.Self()->IsExceptionPending()); diff --git a/runtime/type_reference.h b/runtime/type_reference.h index 70bdc325f0a94b50d0a4a9de8313ba440ffa5425..10a67b1798fee44ef26d0a25a4a87edf2b4bdbe5 100644 --- a/runtime/type_reference.h +++ b/runtime/type_reference.h @@ -19,7 +19,8 @@ #include -#include "base/logging.h" +#include + #include "dex_file_types.h" #include "string_reference.h" @@ -30,7 +31,7 @@ class DexFile; // A type is located by its DexFile and the string_ids_ table index into that DexFile. class TypeReference : public DexFileReference { public: - explicit TypeReference(const DexFile* file = nullptr, dex::TypeIndex index = dex::TypeIndex()) + TypeReference(const DexFile* file, dex::TypeIndex index) : DexFileReference(file, index.index_) {} dex::TypeIndex TypeIndex() const { diff --git a/runtime/utf.cc b/runtime/utf.cc index 7e064826356ec3f5f37d522335a4d0428a9ccec5..93fcb321367c766a40ed2546261e85048c94fd52 100644 --- a/runtime/utf.cc +++ b/runtime/utf.cc @@ -16,7 +16,8 @@ #include "utf.h" -#include "base/logging.h" +#include + #include "mirror/array.h" #include "mirror/object-inl.h" #include "utf-inl.h" diff --git a/runtime/utils.h b/runtime/utils.h index ede32dc57ad473f9168c263a762d7f28f03a80f9..789498ce095b4b1f8d0d8031962369c83ce4be07 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -23,9 +23,10 @@ #include #include +#include + #include "arch/instruction_set.h" #include "base/casts.h" -#include "base/logging.h" #include "base/stringpiece.h" #include "globals.h" #include "primitive.h" diff --git a/runtime/utils/dex_cache_arrays_layout-inl.h b/runtime/utils/dex_cache_arrays_layout-inl.h index 9d4e9fb96c1b20ea71bc9fcaef5489e84e45ae69..855b856187d862c33760c80ab8530238f3a65d4b 100644 --- a/runtime/utils/dex_cache_arrays_layout-inl.h +++ b/runtime/utils/dex_cache_arrays_layout-inl.h @@ -19,8 +19,9 @@ #include "dex_cache_arrays_layout.h" +#include + #include "base/bit_utils.h" -#include "base/logging.h" #include "gc_root.h" #include "globals.h" #include "mirror/dex_cache.h" diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc index 955098d8c2cbf934a0e07a171cffa098f4c42789..fe768a1fd55706b2899f918a31c01eacdde8cf5e 100644 --- a/runtime/vdex_file.cc +++ b/runtime/vdex_file.cc @@ -20,8 +20,9 @@ #include +#include + #include "base/bit_utils.h" -#include "base/logging.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" #include "dex_file.h" @@ -171,56 +172,8 @@ bool VdexFile::OpenAllDexFiles(std::vector>* dex_ return true; } -// Utility class to easily iterate over the quickening data. -class QuickeningInfoIterator { - public: - QuickeningInfoIterator(uint32_t dex_file_index, - uint32_t number_of_dex_files, - const ArrayRef& quickening_info) - : quickening_info_(quickening_info) { - const unaligned_uint32_t* dex_file_indices = reinterpret_cast( - quickening_info.data() + - quickening_info.size() - - number_of_dex_files * sizeof(uint32_t)); - current_code_item_end_ = (dex_file_index == number_of_dex_files - 1) - ? dex_file_indices - : reinterpret_cast( - quickening_info_.data() + dex_file_indices[dex_file_index + 1]); - current_code_item_ptr_ = reinterpret_cast( - quickening_info_.data() + dex_file_indices[dex_file_index]); - } - - bool Done() const { - return current_code_item_ptr_ == current_code_item_end_; - } - - void Advance() { - current_code_item_ptr_ += 2; - } - - uint32_t GetCurrentCodeItemOffset() const { - return current_code_item_ptr_[0]; - } - - const ArrayRef GetCurrentQuickeningInfo() const { - return ArrayRef( - // Add sizeof(uint32_t) to remove the length from the data pointer. - quickening_info_.data() + current_code_item_ptr_[1] + sizeof(uint32_t), - *reinterpret_cast( - quickening_info_.data() + current_code_item_ptr_[1])); - } - - private: - typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t; - const ArrayRef& quickening_info_; - const unaligned_uint32_t* current_code_item_ptr_; - const unaligned_uint32_t* current_code_item_end_; - - DISALLOW_COPY_AND_ASSIGN(QuickeningInfoIterator); -}; - void VdexFile::Unquicken(const std::vector& dex_files, - const ArrayRef& quickening_info, + ArrayRef quickening_info, bool decompile_return_instruction) { if (quickening_info.size() == 0 && !decompile_return_instruction) { // Bail early if there is no quickening info and no need to decompile @@ -228,77 +181,60 @@ void VdexFile::Unquicken(const std::vector& dex_files, return; } - // When we do not decompile RETURN_VOID_NO_BARRIER use the faster - // QuickeningInfoIterator, otherwise use the slower ClassDataItemIterator - if (!decompile_return_instruction) { - for (uint32_t i = 0; i < dex_files.size(); ++i) { - for (QuickeningInfoIterator it(i, dex_files.size(), quickening_info); - !it.Done(); - it.Advance()) { - optimizer::ArtDecompileDEX( - *dex_files[i]->GetCodeItem(it.GetCurrentCodeItemOffset()), - it.GetCurrentQuickeningInfo(), - decompile_return_instruction); - } - } - } else { - for (uint32_t i = 0; i < dex_files.size(); ++i) { - QuickeningInfoIterator quick_it(i, dex_files.size(), quickening_info); - for (uint32_t j = 0; j < dex_files[i]->NumClassDefs(); ++j) { - const DexFile::ClassDef& class_def = dex_files[i]->GetClassDef(j); - const uint8_t* class_data = dex_files[i]->GetClassData(class_def); - if (class_data != nullptr) { - for (ClassDataItemIterator class_it(*dex_files[i], class_data); - class_it.HasNext(); - class_it.Next()) { - if (class_it.IsAtMethod() && class_it.GetMethodCodeItem() != nullptr) { - uint32_t offset = class_it.GetMethodCodeItemOffset(); - if (!quick_it.Done() && offset == quick_it.GetCurrentCodeItemOffset()) { - optimizer::ArtDecompileDEX( - *class_it.GetMethodCodeItem(), - quick_it.GetCurrentQuickeningInfo(), - decompile_return_instruction); - quick_it.Advance(); - } else { - optimizer::ArtDecompileDEX(*class_it.GetMethodCodeItem(), - /* quickened_info */ {}, - decompile_return_instruction); - } - } - } - } - } - DCHECK(quick_it.Done()) << "Failed to use all quickening info"; - } + for (uint32_t i = 0; i < dex_files.size(); ++i) { + UnquickenDexFile(*dex_files[i], quickening_info, decompile_return_instruction); } } -static constexpr uint32_t kNoDexFile = -1; +typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t; -uint32_t VdexFile::GetDexFileIndex(const DexFile& dex_file) const { - uint32_t dex_index = 0; - for (const uint8_t* dex_file_start = GetNextDexFileData(nullptr); - dex_file_start != dex_file.Begin(); - dex_file_start = GetNextDexFileData(dex_file_start)) { - if (dex_file_start == nullptr) { - return kNoDexFile; - } - dex_index++; +static uint32_t GetDebugInfoOffsetInternal(const DexFile& dex_file, + uint32_t offset_in_code_item, + const ArrayRef& quickening_info) { + if (quickening_info.size() == 0) { + // No quickening info: offset is the right one, return it. + return offset_in_code_item; } - return dex_index; + uint32_t quickening_offset = offset_in_code_item - dex_file.Size(); + return *reinterpret_cast(quickening_info.data() + quickening_offset); } -void VdexFile::FullyUnquickenDexFile(const DexFile& target_dex_file, - const DexFile& original_dex_file) const { - uint32_t dex_index = GetDexFileIndex(original_dex_file); - if (dex_index == kNoDexFile) { - return; +static uint32_t GetQuickeningInfoOffsetFrom(const DexFile& dex_file, + uint32_t offset_in_code_item, + const ArrayRef& quickening_info) { + if (offset_in_code_item < dex_file.Size()) { + return VdexFile::kNoQuickeningInfoOffset; + } + if (quickening_info.size() == 0) { + // No quickening info. + return VdexFile::kNoQuickeningInfoOffset; } + uint32_t quickening_offset = offset_in_code_item - dex_file.Size(); - constexpr bool kDecompileReturnInstruction = true; - QuickeningInfoIterator it(dex_index, GetHeader().GetNumberOfDexFiles(), GetQuickeningInfo()); - // Iterate over the class definitions. Even if there is no quickening info, - // we want to unquicken RETURN_VOID_NO_BARRIER instruction. + // Add 2 * sizeof(uint32_t) for the debug info offset and the data offset. + CHECK_LE(quickening_offset + 2 * sizeof(uint32_t), quickening_info.size()); + return *reinterpret_cast( + quickening_info.data() + quickening_offset + sizeof(uint32_t)); +} + +static ArrayRef GetQuickeningInfoAt(const ArrayRef& quickening_info, + uint32_t quickening_offset) { + return (quickening_offset == VdexFile::kNoQuickeningInfoOffset) + ? ArrayRef(nullptr, 0) + : quickening_info.SubArray( + quickening_offset + sizeof(uint32_t), + *reinterpret_cast( + quickening_info.data() + quickening_offset)); +} + +void VdexFile::UnquickenDexFile(const DexFile& target_dex_file, + ArrayRef quickening_info, + bool decompile_return_instruction) { + if (quickening_info.size() == 0 && !decompile_return_instruction) { + // Bail early if there is no quickening info and no need to decompile + // RETURN_VOID_NO_BARRIER instructions to RETURN_VOID instructions. + return; + } for (uint32_t i = 0; i < target_dex_file.NumClassDefs(); ++i) { const DexFile::ClassDef& class_def = target_dex_file.GetClassDef(i); const uint8_t* class_data = target_dex_file.GetClassData(class_def); @@ -307,44 +243,45 @@ void VdexFile::FullyUnquickenDexFile(const DexFile& target_dex_file, class_it.HasNext(); class_it.Next()) { if (class_it.IsAtMethod() && class_it.GetMethodCodeItem() != nullptr) { - uint32_t offset = class_it.GetMethodCodeItemOffset(); - if (!it.Done() && offset == it.GetCurrentCodeItemOffset()) { - optimizer::ArtDecompileDEX( - *class_it.GetMethodCodeItem(), - it.GetCurrentQuickeningInfo(), - kDecompileReturnInstruction); - it.Advance(); - } else { - optimizer::ArtDecompileDEX(*class_it.GetMethodCodeItem(), - ArrayRef(nullptr, 0), - kDecompileReturnInstruction); + const DexFile::CodeItem* code_item = class_it.GetMethodCodeItem(); + uint32_t quickening_offset = GetQuickeningInfoOffsetFrom( + target_dex_file, code_item->debug_info_off_, quickening_info); + if (quickening_offset != VdexFile::kNoQuickeningInfoOffset) { + // If we have quickening data, put back the original debug_info_off. + const_cast(code_item)->SetDebugInfoOffset( + GetDebugInfoOffsetInternal(target_dex_file, + code_item->debug_info_off_, + quickening_info)); } + optimizer::ArtDecompileDEX( + *code_item, + GetQuickeningInfoAt(quickening_info, quickening_offset), + decompile_return_instruction); } } } } } +uint32_t VdexFile::GetDebugInfoOffset(const DexFile& dex_file, uint32_t offset_in_code_item) const { + return GetDebugInfoOffsetInternal(dex_file, offset_in_code_item, GetQuickeningInfo()); +} + const uint8_t* VdexFile::GetQuickenedInfoOf(const DexFile& dex_file, uint32_t code_item_offset) const { - if (GetQuickeningInfo().size() == 0) { - // Bail early if there is no quickening info. - return nullptr; - } + ArrayRef quickening_info = GetQuickeningInfo(); + uint32_t quickening_offset = GetQuickeningInfoOffsetFrom( + dex_file, dex_file.GetCodeItem(code_item_offset)->debug_info_off_, quickening_info); - uint32_t dex_index = GetDexFileIndex(dex_file); - if (dex_index == kNoDexFile) { - return nullptr; - } + return GetQuickeningInfoAt(quickening_info, quickening_offset).data(); +} - for (QuickeningInfoIterator it(dex_index, GetHeader().GetNumberOfDexFiles(), GetQuickeningInfo()); - !it.Done(); - it.Advance()) { - if (code_item_offset == it.GetCurrentCodeItemOffset()) { - return it.GetCurrentQuickeningInfo().data(); - } - } - return nullptr; +bool VdexFile::CanEncodeQuickenedData(const DexFile& dex_file) { + // We are going to use the debug_info_off_ to signal there is + // quickened data, by putting a value greater than dex_file.Size(). So + // make sure we have some room in the offset by checking that we have at least + // half of the range of a uint32_t. + return dex_file.Size() <= (std::numeric_limits::max() >> 1); } } // namespace art diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h index 11f1f527c1ab3abc1a8a15999a9f15415e883ceb..2d9fcab59ce02eec93c1d9eee4532105c442c4fa 100644 --- a/runtime/vdex_file.h +++ b/runtime/vdex_file.h @@ -39,14 +39,14 @@ class DexFile; // DEX[1] the bytecode may have been quickened // ... // DEX[D] +// VerifierDeps +// uint8[D][] verification dependencies // QuickeningInfo -// uint8[] quickening data -// unaligned_uint32_t[2][] table of offsets pair: -// uint32_t[0] contains code_item_offset -// uint32_t[1] contains quickening data offset from the start +// uint8[D][] quickening data +// unaligned_uint32_t[D][2][] table of offsets pair: +// uint32_t[0] contains original CodeItem::debug_info_off_ +// uint32_t[1] contains quickening data offset from the start // of QuickeningInfo -// unalgined_uint32_t[D] start offsets (from the start of QuickeningInfo) in previous -// table for each dex file class VdexFile { public: @@ -68,12 +68,24 @@ class VdexFile { uint32_t GetQuickeningInfoSize() const { return quickening_info_size_; } uint32_t GetNumberOfDexFiles() const { return number_of_dex_files_; } + size_t GetComputedFileSize() const { + return sizeof(Header) + + GetSizeOfChecksumsSection() + + GetDexSize() + + GetVerifierDepsSize() + + GetQuickeningInfoSize(); + } + + size_t GetSizeOfChecksumsSection() const { + return sizeof(VdexChecksum) * GetNumberOfDexFiles(); + } + static constexpr uint8_t kVdexInvalidMagic[] = { 'w', 'd', 'e', 'x' }; private: static constexpr uint8_t kVdexMagic[] = { 'v', 'd', 'e', 'x' }; - // Last update: Use set for unverified_classes_. - static constexpr uint8_t kVdexVersion[] = { '0', '1', '0', '\0' }; + // Last update: Lookup-friendly encoding for quickening info. + static constexpr uint8_t kVdexVersion[] = { '0', '1', '1', '\0' }; uint8_t magic_[4]; uint8_t version_[4]; @@ -149,34 +161,36 @@ class VdexFile { // decompiled to RETURN_VOID instructions using the slower ClassDataItemIterator // instead of the faster QuickeningInfoIterator. static void Unquicken(const std::vector& dex_files, - const ArrayRef& quickening_info, + ArrayRef quickening_info, bool decompile_return_instruction); - // Fully unquicken `target_dex_file` based on quickening info stored - // in this vdex file for `original_dex_file`. - void FullyUnquickenDexFile(const DexFile& target_dex_file, - const DexFile& original_dex_file) const; + // Fully unquicken `target_dex_file` based on `quickening_info`. + static void UnquickenDexFile(const DexFile& target_dex_file, + ArrayRef quickening_info, + bool decompile_return_instruction); // Return the quickening info of the given code item. const uint8_t* GetQuickenedInfoOf(const DexFile& dex_file, uint32_t code_item_offset) const; + uint32_t GetDebugInfoOffset(const DexFile& dex_file, uint32_t offset_in_code_item) const; + + static bool CanEncodeQuickenedData(const DexFile& dex_file); + + static constexpr uint32_t kNoQuickeningInfoOffset = -1; + private: bool HasDexSection() const { return GetHeader().GetDexSize() != 0; } const uint8_t* DexBegin() const { - return Begin() + sizeof(Header) + GetSizeOfChecksumsSection(); + return Begin() + sizeof(Header) + GetHeader().GetSizeOfChecksumsSection(); } const uint8_t* DexEnd() const { return DexBegin() + GetHeader().GetDexSize(); } - size_t GetSizeOfChecksumsSection() const { - return sizeof(VdexChecksum) * GetHeader().GetNumberOfDexFiles(); - } - uint32_t GetDexFileIndex(const DexFile& dex_file) const; std::unique_ptr mmap_; diff --git a/runtime/verifier/method_verifier-inl.h b/runtime/verifier/method_verifier-inl.h index 9bb875c0334c41257b2966c32ad1f368266b1d36..445a6ff7de367739a27b24e3cc7d30426e909d5a 100644 --- a/runtime/verifier/method_verifier-inl.h +++ b/runtime/verifier/method_verifier-inl.h @@ -19,7 +19,8 @@ #include "method_verifier.h" -#include "base/logging.h" +#include + #include "handle_scope-inl.h" #include "mirror/class_loader.h" #include "mirror/dex_cache.h" @@ -27,10 +28,6 @@ namespace art { namespace verifier { -inline const DexFile::CodeItem* MethodVerifier::CodeItem() const { - return code_item_; -} - inline RegisterLine* MethodVerifier::GetRegLine(uint32_t dex_pc) { return reg_table_.GetLine(dex_pc); } diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 121f3cf3642ea420d9aea6867fc026902df5758f..be6915f04b2a5122b586e99512ab05506f2a0d72 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -22,8 +22,9 @@ #include "art_field-inl.h" #include "art_method-inl.h" +#include "base/aborting.h" #include "base/enums.h" -#include "base/logging.h" +#include "base/logging.h" // For VLOG. #include "base/mutex-inl.h" #include "base/stl_util.h" #include "base/systrace.h" @@ -61,8 +62,6 @@ namespace verifier { using android::base::StringPrintf; static constexpr bool kTimeVerifyMethod = !kIsDebugBuild; -static constexpr bool kDebugVerify = false; -// TODO: Add a constant to method_verifier to turn on verbose logging? // On VLOG(verifier), should we dump the whole state when we run into a hard failure? static constexpr bool kDumpRegLinesOnHardFailureIfVLOG = true; @@ -231,7 +230,7 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethods(Thread* self, previous_method_idx = method_idx; InvokeType type = it->GetMethodInvokeType(class_def); ArtMethod* method = linker->ResolveMethod( - *dex_file, method_idx, dex_cache, class_loader, nullptr, type); + method_idx, dex_cache, class_loader, /* referrer */ nullptr, type); if (method == nullptr) { DCHECK(self->IsExceptionPending()); // We couldn't resolve the method, but continue regardless. @@ -284,7 +283,7 @@ FailureKind MethodVerifier::VerifyClass(Thread* self, bool allow_soft_failures, HardFailLogMode log_level, std::string* error) { - ScopedTrace trace(__FUNCTION__); + SCOPED_TRACE << "VerifyClass " << PrettyDescriptor(dex_file->GetClassDescriptor(class_def)); // A class must not be abstract and final. if ((class_def.access_flags_ & (kAccAbstract | kAccFinal)) == (kAccAbstract | kAccFinal)) { @@ -351,13 +350,13 @@ FailureKind MethodVerifier::VerifyClass(Thread* self, } } -static bool IsLargeMethod(const DexFile::CodeItem* const code_item) { - if (code_item == nullptr) { +static bool IsLargeMethod(const CodeItemDataAccessor& accessor) { + if (!accessor.HasCodeItem()) { return false; } - uint16_t registers_size = code_item->registers_size_; - uint32_t insns_size = code_item->insns_size_in_code_units_; + uint16_t registers_size = accessor.RegistersSize(); + uint32_t insns_size = accessor.InsnsSizeInCodeUnits(); return registers_size * insns_size > 4*1024*1024; } @@ -408,6 +407,10 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self, verifier.DumpFailures(VLOG_STREAM(verifier) << "Soft verification failures in " << dex_file->PrettyMethod(method_idx) << "\n"); } + if (VLOG_IS_ON(verifier_debug)) { + std::cout << "\n" << verifier.info_messages_.str(); + verifier.Dump(std::cout); + } result.kind = FailureKind::kSoftFailure; if (method != nullptr && !CanCompilerHandleVerificationFailure(verifier.encountered_failure_types_)) { @@ -481,7 +484,7 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self, callbacks->ClassRejected(ref); } } - if (VLOG_IS_ON(verifier)) { + if (VLOG_IS_ON(verifier) || VLOG_IS_ON(verifier_debug)) { std::cout << "\n" << verifier.info_messages_.str(); verifier.Dump(std::cout); } @@ -491,7 +494,7 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self, if (duration_ns > MsToNs(100)) { LOG(WARNING) << "Verification of " << dex_file->PrettyMethod(method_idx) << " took " << PrettyDuration(duration_ns) - << (IsLargeMethod(code_item) ? " (large method)" : ""); + << (IsLargeMethod(verifier.CodeItem()) ? " (large method)" : ""); } } result.types = verifier.encountered_failure_types_; @@ -564,7 +567,7 @@ MethodVerifier::MethodVerifier(Thread* self, dex_cache_(dex_cache), class_loader_(class_loader), class_def_(class_def), - code_item_(code_item), + code_item_accessor_(dex_file, code_item), declaring_class_(nullptr), interesting_dex_pc_(-1), monitor_enter_dex_pcs_(nullptr), @@ -592,8 +595,10 @@ MethodVerifier::~MethodVerifier() { STLDeleteElements(&failure_messages_); } -void MethodVerifier::FindLocksAtDexPc(ArtMethod* m, uint32_t dex_pc, - std::vector* monitor_enter_dex_pcs) { +void MethodVerifier::FindLocksAtDexPc( + ArtMethod* m, + uint32_t dex_pc, + std::vector* monitor_enter_dex_pcs) { StackHandleScope<2> hs(Thread::Current()); Handle dex_cache(hs.NewHandle(m->GetDexCache())); Handle class_loader(hs.NewHandle(m->GetClassLoader())); @@ -616,29 +621,21 @@ void MethodVerifier::FindLocksAtDexPc(ArtMethod* m, uint32_t dex_pc, verifier.FindLocksAtDexPc(); } -static bool HasMonitorEnterInstructions(const DexFile::CodeItem* const code_item) { - for (const DexInstructionPcPair& inst : code_item->Instructions()) { - if (inst->Opcode() == Instruction::MONITOR_ENTER) { - return true; - } - } - return false; -} - void MethodVerifier::FindLocksAtDexPc() { CHECK(monitor_enter_dex_pcs_ != nullptr); - CHECK(code_item_ != nullptr); // This only makes sense for methods with code. + CHECK(code_item_accessor_.HasCodeItem()); // This only makes sense for methods with code. - // Quick check whether there are any monitor_enter instructions at all. - if (!HasMonitorEnterInstructions(code_item_)) { - return; + // Quick check whether there are any monitor_enter instructions before verifying. + for (const DexInstructionPcPair& inst : code_item_accessor_) { + if (inst->Opcode() == Instruction::MONITOR_ENTER) { + // Strictly speaking, we ought to be able to get away with doing a subset of the full method + // verification. In practice, the phase we want relies on data structures set up by all the + // earlier passes, so we just run the full method verification and bail out early when we've + // got what we wanted. + Verify(); + return; + } } - - // Strictly speaking, we ought to be able to get away with doing a subset of the full method - // verification. In practice, the phase we want relies on data structures set up by all the - // earlier passes, so we just run the full method verification and bail out early when we've - // got what we wanted. - Verify(); } ArtField* MethodVerifier::FindAccessedFieldAtDexPc(ArtMethod* m, uint32_t dex_pc) { @@ -663,7 +660,7 @@ ArtField* MethodVerifier::FindAccessedFieldAtDexPc(ArtMethod* m, uint32_t dex_pc } ArtField* MethodVerifier::FindAccessedFieldAtDexPc(uint32_t dex_pc) { - CHECK(code_item_ != nullptr); // This only makes sense for methods with code. + CHECK(code_item_accessor_.HasCodeItem()); // This only makes sense for methods with code. // Strictly speaking, we ought to be able to get away with doing a subset of the full method // verification. In practice, the phase we want relies on data structures set up by all the @@ -677,7 +674,7 @@ ArtField* MethodVerifier::FindAccessedFieldAtDexPc(uint32_t dex_pc) { if (register_line == nullptr) { return nullptr; } - const Instruction* inst = &code_item_->InstructionAt(dex_pc); + const Instruction* inst = &code_item_accessor_.InstructionAt(dex_pc); return GetQuickFieldAccess(inst, register_line); } @@ -703,7 +700,7 @@ ArtMethod* MethodVerifier::FindInvokedMethodAtDexPc(ArtMethod* m, uint32_t dex_p } ArtMethod* MethodVerifier::FindInvokedMethodAtDexPc(uint32_t dex_pc) { - CHECK(code_item_ != nullptr); // This only makes sense for methods with code. + CHECK(code_item_accessor_.HasCodeItem()); // This only makes sense for methods with code. // Strictly speaking, we ought to be able to get away with doing a subset of the full method // verification. In practice, the phase we want relies on data structures set up by all the @@ -717,7 +714,7 @@ ArtMethod* MethodVerifier::FindInvokedMethodAtDexPc(uint32_t dex_pc) { if (register_line == nullptr) { return nullptr; } - const Instruction* inst = &code_item_->InstructionAt(dex_pc); + const Instruction* inst = &code_item_accessor_.InstructionAt(dex_pc); const bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK); return GetQuickInvokedMethod(inst, register_line, is_range, false); } @@ -769,7 +766,7 @@ bool MethodVerifier::Verify() { } // If there aren't any instructions, make sure that's expected, then exit successfully. - if (code_item_ == nullptr) { + if (!code_item_accessor_.HasCodeItem()) { // Only native or abstract methods may not have code. if ((method_access_flags_ & (kAccNative | kAccAbstract)) == 0) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "zero-length code in concrete non-native method"; @@ -861,17 +858,19 @@ bool MethodVerifier::Verify() { } // Sanity-check the register counts. ins + locals = registers, so make sure that ins <= registers. - if (code_item_->ins_size_ > code_item_->registers_size_) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad register counts (ins=" << code_item_->ins_size_ - << " regs=" << code_item_->registers_size_; + if (code_item_accessor_.InsSize() > code_item_accessor_.RegistersSize()) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad register counts (ins=" + << code_item_accessor_.InsSize() + << " regs=" << code_item_accessor_.RegistersSize(); return false; } // Allocate and initialize an array to hold instruction data. - insn_flags_.reset(allocator_.AllocArray(code_item_->insns_size_in_code_units_)); + insn_flags_.reset(allocator_.AllocArray( + code_item_accessor_.InsnsSizeInCodeUnits())); DCHECK(insn_flags_ != nullptr); std::uninitialized_fill_n(insn_flags_.get(), - code_item_->insns_size_in_code_units_, + code_item_accessor_.InsnsSizeInCodeUnits(), InstructionFlags()); // Run through the instructions and see if the width checks out. bool result = ComputeWidthsAndCountOps(); @@ -923,7 +922,7 @@ std::ostream& MethodVerifier::Fail(VerifyError error) { // Note: this can fail before we touch any instruction, for the signature of a method. So // add a check. if (work_insn_idx_ < dex::kDexNoIndex) { - const Instruction& inst = code_item_->InstructionAt(work_insn_idx_); + const Instruction& inst = code_item_accessor_.InstructionAt(work_insn_idx_); int opcode_flags = Instruction::FlagsOf(inst.Opcode()); if ((opcode_flags & Instruction::kThrow) == 0 && CurrentInsnFlags()->IsInTry()) { @@ -986,15 +985,14 @@ bool MethodVerifier::ComputeWidthsAndCountOps() { size_t new_instance_count = 0; size_t monitor_enter_count = 0; - IterationRange instructions = code_item_->Instructions(); // We can't assume the instruction is well formed, handle the case where calculating the size // goes past the end of the code item. - SafeDexInstructionIterator it(instructions.begin(), instructions.end()); - for ( ; !it.IsErrorState() && it < instructions.end(); ++it) { + SafeDexInstructionIterator it(code_item_accessor_.begin(), code_item_accessor_.end()); + for ( ; !it.IsErrorState() && it < code_item_accessor_.end(); ++it) { // In case the instruction goes past the end of the code item, make sure to not process it. SafeDexInstructionIterator next = it; ++next; - if (next.IsErrorState() || next > instructions.end()) { + if (next.IsErrorState()) { break; } Instruction::Code opcode = it->Opcode(); @@ -1021,8 +1019,8 @@ bool MethodVerifier::ComputeWidthsAndCountOps() { GetInstructionFlags(it.DexPc()).SetIsOpcode(); } - if (it != instructions.end()) { - const size_t insns_size = code_item_->insns_size_in_code_units_; + if (it != code_item_accessor_.end()) { + const size_t insns_size = code_item_accessor_.InsnsSizeInCodeUnits(); Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "code did not end where expected (" << it.DexPc() << " vs. " << insns_size << ")"; return false; @@ -1034,17 +1032,14 @@ bool MethodVerifier::ComputeWidthsAndCountOps() { } bool MethodVerifier::ScanTryCatchBlocks() { - uint32_t tries_size = code_item_->tries_size_; + const uint32_t tries_size = code_item_accessor_.TriesSize(); if (tries_size == 0) { return true; } - uint32_t insns_size = code_item_->insns_size_in_code_units_; - const DexFile::TryItem* tries = DexFile::GetTryItems(*code_item_, 0); - - for (uint32_t idx = 0; idx < tries_size; idx++) { - const DexFile::TryItem* try_item = &tries[idx]; - uint32_t start = try_item->start_addr_; - uint32_t end = start + try_item->insn_count_; + const uint32_t insns_size = code_item_accessor_.InsnsSizeInCodeUnits(); + for (const DexFile::TryItem& try_item : code_item_accessor_.TryItems()) { + const uint32_t start = try_item.start_addr_; + const uint32_t end = start + try_item.insn_count_; if ((start >= end) || (start >= insns_size) || (end > insns_size)) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad exception entry: startAddr=" << start << " endAddr=" << end << " (size=" << insns_size << ")"; @@ -1055,18 +1050,14 @@ bool MethodVerifier::ScanTryCatchBlocks() { << "'try' block starts inside an instruction (" << start << ")"; return false; } - uint32_t dex_pc = start; - const Instruction* inst = Instruction::At(code_item_->insns_ + dex_pc); - while (dex_pc < end) { - GetInstructionFlags(dex_pc).SetInTry(); - size_t insn_size = inst->SizeInCodeUnits(); - dex_pc += insn_size; - inst = inst->RelativeAt(insn_size); + DexInstructionIterator end_it(code_item_accessor_.Insns(), end); + for (DexInstructionIterator it(code_item_accessor_.Insns(), start); it < end_it; ++it) { + GetInstructionFlags(it.DexPc()).SetInTry(); } } // Iterate over each of the handlers to verify target addresses. - const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(*code_item_, 0); - uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr); + const uint8_t* handlers_ptr = code_item_accessor_.GetCatchHandlerData(); + const uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr); ClassLinker* linker = Runtime::Current()->GetClassLinker(); for (uint32_t idx = 0; idx < handlers_size; idx++) { CatchHandlerIterator iterator(handlers_ptr); @@ -1077,7 +1068,7 @@ bool MethodVerifier::ScanTryCatchBlocks() { << "exception handler starts at bad address (" << dex_pc << ")"; return false; } - if (!CheckNotMoveResult(code_item_->insns_, dex_pc)) { + if (!CheckNotMoveResult(code_item_accessor_.Insns(), dex_pc)) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "exception handler begins with move-result* (" << dex_pc << ")"; return false; @@ -1086,9 +1077,8 @@ bool MethodVerifier::ScanTryCatchBlocks() { // Ensure exception types are resolved so that they don't need resolution to be delivered, // unresolved exception types will be ignored by exception delivery if (iterator.GetHandlerTypeIndex().IsValid()) { - mirror::Class* exception_type = linker->ResolveType(*dex_file_, - iterator.GetHandlerTypeIndex(), - dex_cache_, class_loader_); + ObjPtr exception_type = + linker->ResolveType(iterator.GetHandlerTypeIndex(), dex_cache_, class_loader_); if (exception_type == nullptr) { DCHECK(self_->IsExceptionPending()); self_->ClearException(); @@ -1105,7 +1095,7 @@ bool MethodVerifier::VerifyInstructions() { /* Flag the start of the method as a branch target, and a GC point due to stack overflow errors */ GetInstructionFlags(0).SetBranchTarget(); GetInstructionFlags(0).SetCompileTimeInfoPoint(); - for (const DexInstructionPcPair& inst : code_item_->Instructions()) { + for (const DexInstructionPcPair& inst : code_item_accessor_) { const uint32_t dex_pc = inst.DexPc(); if (!VerifyInstruction(&inst.Inst(), dex_pc)) { DCHECK_NE(failures_.size(), 0U); @@ -1259,18 +1249,18 @@ bool MethodVerifier::VerifyInstruction(const Instruction* inst, uint32_t code_of } inline bool MethodVerifier::CheckRegisterIndex(uint32_t idx) { - if (UNLIKELY(idx >= code_item_->registers_size_)) { + if (UNLIKELY(idx >= code_item_accessor_.RegistersSize())) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "register index out of range (" << idx << " >= " - << code_item_->registers_size_ << ")"; + << code_item_accessor_.RegistersSize() << ")"; return false; } return true; } inline bool MethodVerifier::CheckWideRegisterIndex(uint32_t idx) { - if (UNLIKELY(idx + 1 >= code_item_->registers_size_)) { + if (UNLIKELY(idx + 1 >= code_item_accessor_.RegistersSize())) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "wide register index out of range (" << idx - << "+1 >= " << code_item_->registers_size_ << ")"; + << "+1 >= " << code_item_accessor_.RegistersSize() << ")"; return false; } return true; @@ -1387,8 +1377,8 @@ bool MethodVerifier::CheckNewArray(dex::TypeIndex idx) { } bool MethodVerifier::CheckArrayData(uint32_t cur_offset) { - const uint32_t insn_count = code_item_->insns_size_in_code_units_; - const uint16_t* insns = code_item_->insns_ + cur_offset; + const uint32_t insn_count = code_item_accessor_.InsnsSizeInCodeUnits(); + const uint16_t* insns = code_item_accessor_.Insns() + cur_offset; const uint16_t* array_data; int32_t array_data_offset; @@ -1451,10 +1441,9 @@ bool MethodVerifier::CheckBranchTarget(uint32_t cur_offset) { << reinterpret_cast(cur_offset) << " +" << offset; return false; } - const uint32_t insn_count = code_item_->insns_size_in_code_units_; int32_t abs_offset = cur_offset + offset; if (UNLIKELY(abs_offset < 0 || - (uint32_t) abs_offset >= insn_count || + (uint32_t) abs_offset >= code_item_accessor_.InsnsSizeInCodeUnits() || !GetInstructionFlags(abs_offset).IsOpcode())) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid branch target " << offset << " (-> " << reinterpret_cast(abs_offset) << ") at " @@ -1467,7 +1456,7 @@ bool MethodVerifier::CheckBranchTarget(uint32_t cur_offset) { bool MethodVerifier::GetBranchOffset(uint32_t cur_offset, int32_t* pOffset, bool* pConditional, bool* selfOkay) { - const uint16_t* insns = code_item_->insns_ + cur_offset; + const uint16_t* insns = code_item_accessor_.Insns() + cur_offset; *pConditional = false; *selfOkay = false; switch (*insns & 0xff) { @@ -1503,9 +1492,9 @@ bool MethodVerifier::GetBranchOffset(uint32_t cur_offset, int32_t* pOffset, bool } bool MethodVerifier::CheckSwitchTargets(uint32_t cur_offset) { - const uint32_t insn_count = code_item_->insns_size_in_code_units_; + const uint32_t insn_count = code_item_accessor_.InsnsSizeInCodeUnits(); DCHECK_LT(cur_offset, insn_count); - const uint16_t* insns = code_item_->insns_ + cur_offset; + const uint16_t* insns = code_item_accessor_.Insns() + cur_offset; /* make sure the start of the switch is in range */ int32_t switch_offset = insns[1] | (static_cast(insns[2]) << 16); if (UNLIKELY(static_cast(cur_offset) + switch_offset < 0 || @@ -1610,7 +1599,7 @@ bool MethodVerifier::CheckSwitchTargets(uint32_t cur_offset) { } bool MethodVerifier::CheckVarArgRegs(uint32_t vA, uint32_t arg[]) { - uint16_t registers_size = code_item_->registers_size_; + uint16_t registers_size = code_item_accessor_.RegistersSize(); for (uint32_t idx = 0; idx < vA; idx++) { if (UNLIKELY(arg[idx] >= registers_size)) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid reg index (" << arg[idx] @@ -1623,7 +1612,7 @@ bool MethodVerifier::CheckVarArgRegs(uint32_t vA, uint32_t arg[]) { } bool MethodVerifier::CheckVarArgRangeRegs(uint32_t vA, uint32_t vC) { - uint16_t registers_size = code_item_->registers_size_; + uint16_t registers_size = code_item_accessor_.RegistersSize(); // vA/vC are unsigned 8-bit/16-bit quantities for /range instructions, so there's no risk of // integer overflow when adding them here. if (UNLIKELY(vA + vC > registers_size)) { @@ -1635,13 +1624,12 @@ bool MethodVerifier::CheckVarArgRangeRegs(uint32_t vA, uint32_t vC) { } bool MethodVerifier::VerifyCodeFlow() { - uint16_t registers_size = code_item_->registers_size_; - uint32_t insns_size = code_item_->insns_size_in_code_units_; + const uint16_t registers_size = code_item_accessor_.RegistersSize(); /* Create and initialize table holding register status */ reg_table_.Init(kTrackCompilerInterestPoints, insn_flags_.get(), - insns_size, + code_item_accessor_.InsnsSizeInCodeUnits(), registers_size, this); @@ -1681,7 +1669,7 @@ void MethodVerifier::Dump(std::ostream& os) { } void MethodVerifier::Dump(VariableIndentationOutputStream* vios) { - if (code_item_ == nullptr) { + if (!code_item_accessor_.HasCodeItem()) { vios->Stream() << "Native method\n"; return; } @@ -1693,7 +1681,7 @@ void MethodVerifier::Dump(VariableIndentationOutputStream* vios) { vios->Stream() << "Dumping instructions and register lines:\n"; ScopedIndentation indent1(vios); - for (const DexInstructionPcPair& inst : code_item_->Instructions()) { + for (const DexInstructionPcPair& inst : code_item_accessor_) { const size_t dex_pc = inst.DexPc(); RegisterLine* reg_line = reg_table_.GetLine(dex_pc); if (reg_line != nullptr) { @@ -1729,10 +1717,10 @@ bool MethodVerifier::SetTypesFromSignature() { RegisterLine* reg_line = reg_table_.GetLine(0); // Should have been verified earlier. - DCHECK_GE(code_item_->registers_size_, code_item_->ins_size_); + DCHECK_GE(code_item_accessor_.RegistersSize(), code_item_accessor_.InsSize()); - uint32_t arg_start = code_item_->registers_size_ - code_item_->ins_size_; - size_t expected_args = code_item_->ins_size_; /* long/double count as two */ + uint32_t arg_start = code_item_accessor_.RegistersSize() - code_item_accessor_.InsSize(); + size_t expected_args = code_item_accessor_.InsSize(); /* long/double count as two */ // Include the "this" pointer. size_t cur_arg = 0; @@ -1884,8 +1872,8 @@ bool MethodVerifier::SetTypesFromSignature() { } bool MethodVerifier::CodeFlowVerifyMethod() { - const uint16_t* insns = code_item_->insns_; - const uint32_t insns_size = code_item_->insns_size_in_code_units_; + const uint16_t* insns = code_item_accessor_.Insns(); + const uint32_t insns_size = code_item_accessor_.InsnsSizeInCodeUnits(); /* Begin by marking the first instruction as "changed". */ GetInstructionFlags(0).SetChanged(); @@ -1949,7 +1937,7 @@ bool MethodVerifier::CodeFlowVerifyMethod() { GetInstructionFlags(insn_idx).ClearChanged(); } - if (kDebugVerify) { + if (UNLIKELY(VLOG_IS_ON(verifier_debug))) { /* * Scan for dead code. There's nothing "evil" about dead code * (besides the wasted space), but it indicates a flaw somewhere @@ -1960,7 +1948,7 @@ bool MethodVerifier::CodeFlowVerifyMethod() { */ int dead_start = -1; - for (const DexInstructionPcPair& inst : code_item_->Instructions()) { + for (const DexInstructionPcPair& inst : code_item_accessor_) { const uint32_t insn_idx = inst.DexPc(); /* * Switch-statement data doesn't get "visited" by scanner. It @@ -1989,7 +1977,7 @@ bool MethodVerifier::CodeFlowVerifyMethod() { if (dead_start >= 0) { LogVerifyInfo() << "dead code " << reinterpret_cast(dead_start) - << "-" << reinterpret_cast(code_item_->insns_size_in_code_units_ - 1); + << "-" << reinterpret_cast(code_item_accessor_.InsnsSizeInCodeUnits() - 1); } // To dump the state of the verify after a method, do something like: // if (dex_file_->PrettyMethod(dex_method_idx_) == @@ -2052,8 +2040,20 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { // for a thread to be suspended). if (monitor_enter_dex_pcs_ != nullptr && work_insn_idx_ == interesting_dex_pc_) { monitor_enter_dex_pcs_->clear(); // The new work line is more accurate than the previous one. - for (size_t i = 0; i < work_line_->GetMonitorEnterCount(); ++i) { - monitor_enter_dex_pcs_->push_back(work_line_->GetMonitorEnterDexPc(i)); + + std::map depth_to_lock_info; + auto collector = [&](uint32_t dex_reg, uint32_t depth) { + auto insert_pair = depth_to_lock_info.emplace(depth, DexLockInfo(depth)); + auto it = insert_pair.first; + auto set_insert_pair = it->second.dex_registers.insert(dex_reg); + DCHECK(set_insert_pair.second); + }; + work_line_->IterateRegToLockDepths(collector); + for (auto& pair : depth_to_lock_info) { + monitor_enter_dex_pcs_->push_back(pair.second); + // Map depth to dex PC. + (*monitor_enter_dex_pcs_)[monitor_enter_dex_pcs_->size() - 1].dex_pc = + work_line_->GetMonitorEnterDexPc(pair.second.dex_pc); } } @@ -2075,13 +2075,13 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { * * The behavior can be determined from the opcode flags. */ - const uint16_t* insns = code_item_->insns_ + work_insn_idx_; + const uint16_t* insns = code_item_accessor_.Insns() + work_insn_idx_; const Instruction* inst = Instruction::At(insns); int opcode_flags = Instruction::FlagsOf(inst->Opcode()); int32_t branch_target = 0; bool just_set_result = false; - if (kDebugVerify) { + if (UNLIKELY(VLOG_IS_ON(verifier_debug))) { // Generate processing back trace to debug verifier LogVerifyInfo() << "Processing " << inst->DumpString(dex_file_) << "\n" << work_line_->Dump(this) << "\n"; @@ -2239,7 +2239,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-object not expected"; } else { /* return_type is the *expected* return type, not register value */ - DCHECK(!return_type.IsZero()); + DCHECK(!return_type.IsZeroOrNull()); DCHECK(!return_type.IsUninitializedReference()); const uint32_t vregA = inst->VRegA_11x(); const RegType& reg_type = work_line_->GetRegisterType(this, vregA); @@ -2375,7 +2375,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { while (0 != prev_idx && !GetInstructionFlags(prev_idx).IsOpcode()) { prev_idx--; } - const Instruction& prev_inst = code_item_->InstructionAt(prev_idx); + const Instruction& prev_inst = code_item_accessor_.InstructionAt(prev_idx); switch (prev_inst.Opcode()) { case Instruction::MOVE_OBJECT: case Instruction::MOVE_OBJECT_16: @@ -2433,8 +2433,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { const RegType& res_type = ResolveClass(type_idx); if (res_type.IsConflict()) { // If this is a primitive type, fail HARD. - ObjPtr klass = - ClassLinker::LookupResolvedType(type_idx, dex_cache_.Get(), class_loader_.Get()); + ObjPtr klass = Runtime::Current()->GetClassLinker()->LookupResolvedType( + type_idx, dex_cache_.Get(), class_loader_.Get()); if (klass != nullptr && klass->IsPrimitive()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "using primitive type " << dex_file_->StringByTypeIdx(type_idx) << " in instanceof in " @@ -2487,7 +2487,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { case Instruction::ARRAY_LENGTH: { const RegType& res_type = work_line_->GetRegisterType(this, inst->VRegB_12x()); if (res_type.IsReferenceTypes()) { - if (!res_type.IsArrayTypes() && !res_type.IsZero()) { // ie not an array or null + if (!res_type.IsArrayTypes() && !res_type.IsZeroOrNull()) { + // ie not an array or null Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "array-length on non-array " << res_type; } else { work_line_->SetRegisterType(this, @@ -2594,7 +2595,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { /* Similar to the verification done for APUT */ const RegType& array_type = work_line_->GetRegisterType(this, inst->VRegA_31t()); /* array_type can be null if the reg type is Zero */ - if (!array_type.IsZero()) { + if (!array_type.IsZeroOrNull()) { if (!array_type.IsArrayTypes()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data with array type " << array_type; @@ -2634,7 +2635,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { const RegType& reg_type1 = work_line_->GetRegisterType(this, inst->VRegA_22t()); const RegType& reg_type2 = work_line_->GetRegisterType(this, inst->VRegB_22t()); bool mismatch = false; - if (reg_type1.IsZero()) { // zero then integral or reference expected + if (reg_type1.IsZeroOrNull()) { // zero then integral or reference expected mismatch = !reg_type2.IsReferenceTypes() && !reg_type2.IsIntegralTypes(); } else if (reg_type1.IsReferenceTypes()) { // both references? mismatch = !reg_type2.IsReferenceTypes(); @@ -2683,7 +2684,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { break; } - const Instruction& instance_of_inst = code_item_->InstructionAt(instance_of_idx); + const Instruction& instance_of_inst = code_item_accessor_.InstructionAt(instance_of_idx); /* Check for peep-hole pattern of: * ...; @@ -2719,10 +2720,11 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { !cast_type.IsUnresolvedTypes() && !orig_type.IsUnresolvedTypes() && cast_type.HasClass() && // Could be conflict type, make sure it has a class. !cast_type.GetClass()->IsInterface() && - (orig_type.IsZero() || + (orig_type.IsZeroOrNull() || orig_type.IsStrictlyAssignableFrom( cast_type.Merge(orig_type, ®_types_, this), this))) { - RegisterLine* update_line = RegisterLine::Create(code_item_->registers_size_, this); + RegisterLine* update_line = RegisterLine::Create(code_item_accessor_.RegistersSize(), + this); if (inst->Opcode() == Instruction::IF_EQZ) { fallthrough_line.reset(update_line); } else { @@ -2745,7 +2747,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { work_insn_idx_)) { break; } - const Instruction& move_inst = code_item_->InstructionAt(move_idx); + const Instruction& move_inst = code_item_accessor_.InstructionAt(move_idx); switch (move_inst.Opcode()) { case Instruction::MOVE_OBJECT: if (move_inst.VRegA_12x() == instance_of_inst.VRegB_22c()) { @@ -3006,7 +3008,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { break; /* no null refs allowed (?) */ - if (this_type.IsZero()) { + if (this_type.IsZeroOrNull()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unable to initialize null ref"; break; } @@ -3083,7 +3085,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { * interface or Object (see comments in RegType::JoinClass). */ const RegType& this_type = work_line_->GetInvocationThis(this, inst); - if (this_type.IsZero()) { + if (this_type.IsZeroOrNull()) { /* null pointer always passes (and always fails at runtime) */ } else { if (this_type.IsUninitializedTypes()) { @@ -3564,7 +3566,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { return false; } DCHECK_EQ(isConditional, (opcode_flags & Instruction::kContinue) != 0); - if (!CheckNotMoveExceptionOrMoveResult(code_item_->insns_, work_insn_idx_ + branch_target)) { + if (!CheckNotMoveExceptionOrMoveResult(code_item_accessor_.Insns(), + work_insn_idx_ + branch_target)) { return false; } /* update branch target, set "changed" if appropriate */ @@ -3609,8 +3612,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { offset = switch_insns[offset_to_targets + targ * 2] | (static_cast(switch_insns[offset_to_targets + targ * 2 + 1]) << 16); abs_offset = work_insn_idx_ + offset; - DCHECK_LT(abs_offset, code_item_->insns_size_in_code_units_); - if (!CheckNotMoveExceptionOrMoveResult(code_item_->insns_, abs_offset)) { + DCHECK_LT(abs_offset, code_item_accessor_.InsnsSizeInCodeUnits()); + if (!CheckNotMoveExceptionOrMoveResult(code_item_accessor_.Insns(), abs_offset)) { return false; } if (!UpdateRegisters(abs_offset, work_line_.get(), false)) { @@ -3625,7 +3628,9 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { */ if ((opcode_flags & Instruction::kThrow) != 0 && GetInstructionFlags(work_insn_idx_).IsInTry()) { bool has_catch_all_handler = false; - CatchHandlerIterator iterator(*code_item_, work_insn_idx_); + const DexFile::TryItem* try_item = code_item_accessor_.FindTryItem(work_insn_idx_); + CHECK(try_item != nullptr); + CatchHandlerIterator iterator(code_item_accessor_.GetCatchHandlerData(try_item->handler_off_)); // Need the linker to try and resolve the handled class to check if it's Throwable. ClassLinker* linker = Runtime::Current()->GetClassLinker(); @@ -3636,8 +3641,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { has_catch_all_handler = true; } else { // It is also a catch-all if it is java.lang.Throwable. - mirror::Class* klass = linker->ResolveType(*dex_file_, handler_type_idx, dex_cache_, - class_loader_); + ObjPtr klass = + linker->ResolveType(handler_type_idx, dex_cache_, class_loader_); if (klass != nullptr) { if (klass == mirror::Throwable::GetJavaLangThrowable()) { has_catch_all_handler = true; @@ -3682,15 +3687,15 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { * and this change should not be used in those cases. */ if ((opcode_flags & Instruction::kContinue) != 0) { - DCHECK_EQ(&code_item_->InstructionAt(work_insn_idx_), inst); + DCHECK_EQ(&code_item_accessor_.InstructionAt(work_insn_idx_), inst); uint32_t next_insn_idx = work_insn_idx_ + inst->SizeInCodeUnits(); - if (next_insn_idx >= code_item_->insns_size_in_code_units_) { + if (next_insn_idx >= code_item_accessor_.InsnsSizeInCodeUnits()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Execution can walk off end of code area"; return false; } // The only way to get to a move-exception instruction is to get thrown there. Make sure the // next instruction isn't one. - if (!CheckNotMoveException(code_item_->insns_, next_insn_idx)) { + if (!CheckNotMoveException(code_item_accessor_.Insns(), next_insn_idx)) { return false; } if (nullptr != fallthrough_line) { @@ -3699,7 +3704,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } if (GetInstructionFlags(next_insn_idx).IsReturn()) { // For returns we only care about the operand to the return, all other registers are dead. - const Instruction* ret_inst = Instruction::At(code_item_->insns_ + next_insn_idx); + const Instruction* ret_inst = &code_item_accessor_.InstructionAt(next_insn_idx); AdjustReturnLine(this, ret_inst, work_line_.get()); } RegisterLine* next_line = reg_table_.GetLine(next_insn_idx); @@ -3731,14 +3736,14 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { * alone and let the caller sort it out. */ if ((opcode_flags & Instruction::kContinue) != 0) { - DCHECK_EQ(Instruction::At(code_item_->insns_ + work_insn_idx_), inst); + DCHECK_EQ(&code_item_accessor_.InstructionAt(work_insn_idx_), inst); *start_guess = work_insn_idx_ + inst->SizeInCodeUnits(); } else if ((opcode_flags & Instruction::kBranch) != 0) { /* we're still okay if branch_target is zero */ *start_guess = work_insn_idx_ + branch_target; } - DCHECK_LT(*start_guess, code_item_->insns_size_in_code_units_); + DCHECK_LT(*start_guess, code_item_accessor_.InsnsSizeInCodeUnits()); DCHECK(GetInstructionFlags(*start_guess).IsOpcode()); if (have_pending_runtime_throw_failure_) { @@ -3755,16 +3760,16 @@ void MethodVerifier::UninstantiableError(const char* descriptor) { << "non-instantiable klass " << descriptor; } -inline bool MethodVerifier::IsInstantiableOrPrimitive(mirror::Class* klass) { +inline bool MethodVerifier::IsInstantiableOrPrimitive(ObjPtr klass) { return klass->IsInstantiable() || klass->IsPrimitive(); } template const RegType& MethodVerifier::ResolveClass(dex::TypeIndex class_idx) { - mirror::Class* klass = can_load_classes_ - ? Runtime::Current()->GetClassLinker()->ResolveType( - *dex_file_, class_idx, dex_cache_, class_loader_) - : ClassLinker::LookupResolvedType(class_idx, dex_cache_.Get(), class_loader_.Get()).Ptr(); + ClassLinker* linker = Runtime::Current()->GetClassLinker(); + ObjPtr klass = can_load_classes_ + ? linker->ResolveType(class_idx, dex_cache_, class_loader_) + : linker->LookupResolvedType(class_idx, dex_cache_.Get(), class_loader_.Get()); if (can_load_classes_ && klass == nullptr) { DCHECK(self_->IsExceptionPending()); self_->ClearException(); @@ -3777,10 +3782,10 @@ const RegType& MethodVerifier::ResolveClass(dex::TypeIndex class_idx) { UninstantiableError(descriptor); precise = false; } - result = reg_types_.FindClass(klass, precise); + result = reg_types_.FindClass(klass.Ptr(), precise); if (result == nullptr) { const char* descriptor = dex_file_->StringByTypeIdx(class_idx); - result = reg_types_.InsertClass(descriptor, klass, precise); + result = reg_types_.InsertClass(descriptor, klass.Ptr(), precise); } } else { const char* descriptor = dex_file_->StringByTypeIdx(class_idx); @@ -3795,7 +3800,7 @@ const RegType& MethodVerifier::ResolveClass(dex::TypeIndex class_idx) { } // Record result of class resolution attempt. - VerifierDeps::MaybeRecordClassResolution(*dex_file_, class_idx, klass); + VerifierDeps::MaybeRecordClassResolution(*dex_file_, class_idx, klass.Ptr()); // If requested, check if access is allowed. Unresolved types are included in this check, as the // interpreter only tests whether access is allowed when a class is not pre-verified and runs in @@ -3821,8 +3826,8 @@ template const RegType& MethodVerifier::ResolveClasstries_size_ != 0) { - const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(*code_item_, 0); + if (code_item_accessor_.TriesSize() != 0) { + const uint8_t* handlers_ptr = code_item_accessor_.GetCatchHandlerData(); uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr); for (uint32_t i = 0; i < handlers_size; i++) { CatchHandlerIterator iterator(handlers_ptr); @@ -4041,9 +4046,10 @@ ArtMethod* MethodVerifier::VerifyInvocationArgsFromIterator( /* caught by static verifier */ DCHECK(is_range || expected_args <= 5); - if (expected_args > code_item_->outs_size_) { + if (expected_args > code_item_accessor_.OutsSize()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid argument count (" << expected_args - << ") exceeds outsSize (" << code_item_->outs_size_ << ")"; + << ") exceeds outsSize (" + << code_item_accessor_.OutsSize() << ")"; return nullptr; } @@ -4078,7 +4084,7 @@ ArtMethod* MethodVerifier::VerifyInvocationArgsFromIterator( const RegType& adjusted_type = is_init ? GetRegTypeCache()->FromUninitialized(actual_arg_type) : actual_arg_type; - if (method_type != METHOD_INTERFACE && !adjusted_type.IsZero()) { + if (method_type != METHOD_INTERFACE && !adjusted_type.IsZeroOrNull()) { const RegType* res_method_class; // Miranda methods have the declaring interface as their declaring class, not the abstract // class. It would be wrong to use this for the type check (interface type checks are @@ -4451,7 +4457,7 @@ bool MethodVerifier::CheckSignaturePolymorphicMethod(ArtMethod* method) { bool MethodVerifier::CheckSignaturePolymorphicReceiver(const Instruction* inst) { const RegType& this_type = work_line_->GetInvocationThis(this, inst); - if (this_type.IsZero()) { + if (this_type.IsZeroOrNull()) { /* null pointer always passes (and always fails at run time) */ return true; } else if (!this_type.IsNonZeroReferenceTypes()) { @@ -4554,9 +4560,9 @@ ArtMethod* MethodVerifier::VerifyInvokeVirtualQuickArgs(const Instruction* inst, const size_t expected_args = (is_range) ? inst->VRegA_3rc() : inst->VRegA_35c(); /* caught by static verifier */ DCHECK(is_range || expected_args <= 5); - if (expected_args > code_item_->outs_size_) { + if (expected_args > code_item_accessor_.OutsSize()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid argument count (" << expected_args - << ") exceeds outsSize (" << code_item_->outs_size_ << ")"; + << ") exceeds outsSize (" << code_item_accessor_.OutsSize() << ")"; return nullptr; } @@ -4570,7 +4576,7 @@ ArtMethod* MethodVerifier::VerifyInvokeVirtualQuickArgs(const Instruction* inst, Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "'this' arg must be initialized"; return nullptr; } - if (!actual_arg_type.IsZero()) { + if (!actual_arg_type.IsZeroOrNull()) { mirror::Class* klass = res_method->GetDeclaringClass(); std::string temp; const RegType& res_method_class = @@ -4686,13 +4692,20 @@ void MethodVerifier::VerifyAGet(const Instruction* inst, Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Invalid reg type for array index (" << index_type << ")"; } else { const RegType& array_type = work_line_->GetRegisterType(this, inst->VRegB_23x()); - if (array_type.IsZero()) { - have_pending_runtime_throw_failure_ = true; + if (array_type.IsZeroOrNull()) { // Null array class; this code path will fail at runtime. Infer a merge-able type from the - // instruction type. TODO: have a proper notion of bottom here. - if (!is_primitive || insn_type.IsCategory1Types()) { - // Reference or category 1 - work_line_->SetRegisterType(this, inst->VRegA_23x(), reg_types_.Zero()); + // instruction type. + if (!is_primitive) { + work_line_->SetRegisterType(this, inst->VRegA_23x(), reg_types_.Null()); + } else if (insn_type.IsInteger()) { + // Pick a non-zero constant (to distinguish with null) that can fit in any primitive. + // We cannot use 'insn_type' as it could be a float array or an int array. + work_line_->SetRegisterType( + this, inst->VRegA_23x(), DetermineCat1Constant(1, need_precise_constants_)); + } else if (insn_type.IsCategory1Types()) { + // Category 1 + // The 'insn_type' is exactly the type we need. + work_line_->SetRegisterType(this, inst->VRegA_23x(), insn_type); } else { // Category 2 work_line_->SetRegisterTypeWide(this, inst->VRegA_23x(), @@ -4801,7 +4814,7 @@ void MethodVerifier::VerifyAPut(const Instruction* inst, Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Invalid reg type for array index (" << index_type << ")"; } else { const RegType& array_type = work_line_->GetRegisterType(this, inst->VRegB_23x()); - if (array_type.IsZero()) { + if (array_type.IsZeroOrNull()) { // Null array type; this code path will fail at runtime. // Still check that the given value matches the instruction's type. // Note: this is, as usual, complicated by the fact the the instruction isn't fully typed @@ -4870,7 +4883,7 @@ ArtField* MethodVerifier::GetStaticField(int field_idx) { return nullptr; // Can't resolve Class so no more to do here, will do checking at runtime. } ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - ArtField* field = class_linker->ResolveFieldJLS(*dex_file_, field_idx, dex_cache_, class_loader_); + ArtField* field = class_linker->ResolveFieldJLS(field_idx, dex_cache_, class_loader_); // Record result of the field resolution attempt. VerifierDeps::MaybeRecordFieldResolution(*dex_file_, field_idx, field); @@ -4911,7 +4924,7 @@ ArtField* MethodVerifier::GetInstanceField(const RegType& obj_type, int field_id return nullptr; // Can't resolve Class so no more to do here } ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - ArtField* field = class_linker->ResolveFieldJLS(*dex_file_, field_idx, dex_cache_, class_loader_); + ArtField* field = class_linker->ResolveFieldJLS(field_idx, dex_cache_, class_loader_); // Record result of the field resolution attempt. VerifierDeps::MaybeRecordFieldResolution(*dex_file_, field_idx, field); @@ -4923,7 +4936,7 @@ ArtField* MethodVerifier::GetInstanceField(const RegType& obj_type, int field_id DCHECK(self_->IsExceptionPending()); self_->ClearException(); return nullptr; - } else if (obj_type.IsZero()) { + } else if (obj_type.IsZeroOrNull()) { // Cannot infer and check type, however, access will cause null pointer exception. // Fall through into a few last soft failure checks below. } else if (!obj_type.IsReferenceTypes()) { @@ -5035,7 +5048,7 @@ void MethodVerifier::VerifyISFieldAccess(const Instruction* inst, const RegType& } ObjPtr field_type_class = - can_load_classes_ ? field->ResolveType() : field->LookupType(); + can_load_classes_ ? field->ResolveType() : field->LookupResolvedType(); if (field_type_class != nullptr) { field_type = &FromClass(field->GetTypeDescriptor(), field_type_class.Ptr(), @@ -5142,8 +5155,7 @@ void MethodVerifier::VerifyISFieldAccess(const Instruction* inst, const RegType& } } -ArtField* MethodVerifier::GetQuickFieldAccess(const Instruction* inst, - RegisterLine* reg_line) { +ArtField* MethodVerifier::GetQuickFieldAccess(const Instruction* inst, RegisterLine* reg_line) { DCHECK(IsInstructionIGetQuickOrIPutQuick(inst->Opcode())) << inst->Opcode(); const RegType& object_type = reg_line->GetRegisterType(this, inst->VRegB_22c()); if (!object_type.HasClass()) { @@ -5152,10 +5164,11 @@ ArtField* MethodVerifier::GetQuickFieldAccess(const Instruction* inst, } uint32_t field_offset = static_cast(inst->VRegC_22c()); ArtField* const f = ArtField::FindInstanceFieldWithOffset(object_type.GetClass(), field_offset); - DCHECK_EQ(f->GetOffset().Uint32Value(), field_offset); if (f == nullptr) { VLOG(verifier) << "Failed to find instance field at offset '" << field_offset << "' from '" << mirror::Class::PrettyDescriptor(object_type.GetClass()) << "'"; + } else { + DCHECK_EQ(f->GetOffset().Uint32Value(), field_offset); } return f; } @@ -5184,7 +5197,7 @@ void MethodVerifier::VerifyQuickFieldAccess(const Instruction* inst, const RegTy const RegType* field_type; { ObjPtr field_type_class = - can_load_classes_ ? field->ResolveType() : field->LookupType(); + can_load_classes_ ? field->ResolveType() : field->LookupResolvedType(); if (field_type_class != nullptr) { field_type = &FromClass(field->GetTypeDescriptor(), @@ -5330,7 +5343,7 @@ bool MethodVerifier::UpdateRegisters(uint32_t next_insn, RegisterLine* merge_lin // For returns we only care about the operand to the return, all other registers are dead. // Initialize them as conflicts so they don't add to GC and deoptimization information. - const Instruction* ret_inst = Instruction::At(code_item_->insns_ + next_insn); + const Instruction* ret_inst = &code_item_accessor_.InstructionAt(next_insn); AdjustReturnLine(this, ret_inst, target_line); // Directly bail if a hard failure was found. if (have_pending_hard_failure_) { @@ -5339,7 +5352,7 @@ bool MethodVerifier::UpdateRegisters(uint32_t next_insn, RegisterLine* merge_lin } } else { RegisterLineArenaUniquePtr copy; - if (kDebugVerify) { + if (UNLIKELY(VLOG_IS_ON(verifier_debug))) { copy.reset(RegisterLine::Create(target_line->NumRegs(), this)); copy->CopyFromLine(target_line); } @@ -5347,7 +5360,7 @@ bool MethodVerifier::UpdateRegisters(uint32_t next_insn, RegisterLine* merge_lin if (have_pending_hard_failure_) { return false; } - if (kDebugVerify && changed) { + if (UNLIKELY(VLOG_IS_ON(verifier_debug)) && changed) { LogVerifyInfo() << "Merging at [" << reinterpret_cast(work_insn_idx_) << "]" << " to [" << reinterpret_cast(next_insn) << "]: " << "\n" << copy->Dump(this) << " MERGE\n" diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index 1f1d7c1f03dd944d883341dc7f7880ce100a110d..f26f3e2655d4975b292d8024cde624147690f778 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -25,6 +25,7 @@ #include "base/macros.h" #include "base/scoped_arena_containers.h" #include "base/value_object.h" +#include "code_item_accessors.h" #include "dex_file.h" #include "dex_file_types.h" #include "handle.h" @@ -148,10 +149,21 @@ class MethodVerifier { void Dump(std::ostream& os) REQUIRES_SHARED(Locks::mutator_lock_); void Dump(VariableIndentationOutputStream* vios) REQUIRES_SHARED(Locks::mutator_lock_); + // Information structure for a lock held at a certain point in time. + struct DexLockInfo { + // The registers aliasing the lock. + std::set dex_registers; + // The dex PC of the monitor-enter instruction. + uint32_t dex_pc; + + explicit DexLockInfo(uint32_t dex_pc_in) { + dex_pc = dex_pc_in; + } + }; // Fills 'monitor_enter_dex_pcs' with the dex pcs of the monitor-enter instructions corresponding // to the locks held at 'dex_pc' in method 'm'. static void FindLocksAtDexPc(ArtMethod* m, uint32_t dex_pc, - std::vector* monitor_enter_dex_pcs) + std::vector* monitor_enter_dex_pcs) REQUIRES_SHARED(Locks::mutator_lock_); // Returns the accessed field corresponding to the quick instruction's field @@ -186,7 +198,9 @@ class MethodVerifier { REQUIRES_SHARED(Locks::mutator_lock_); // Accessors used by the compiler via CompilerCallback - const DexFile::CodeItem* CodeItem() const; + const CodeItemDataAccessor& CodeItem() const { + return code_item_accessor_; + } RegisterLine* GetRegLine(uint32_t dex_pc); ALWAYS_INLINE const InstructionFlags& GetInstructionFlags(size_t index) const; ALWAYS_INLINE InstructionFlags& GetInstructionFlags(size_t index); @@ -243,7 +257,8 @@ class MethodVerifier { REQUIRES_SHARED(Locks::mutator_lock_); void UninstantiableError(const char* descriptor); - static bool IsInstantiableOrPrimitive(mirror::Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); + static bool IsInstantiableOrPrimitive(ObjPtr klass) + REQUIRES_SHARED(Locks::mutator_lock_); // Is the method being verified a constructor? See the comment on the field. bool IsConstructor() const { @@ -738,7 +753,7 @@ class MethodVerifier { // The class loader for the declaring class of the method. Handle class_loader_ GUARDED_BY(Locks::mutator_lock_); const DexFile::ClassDef& class_def_; // The class def of the declaring class of the method. - const DexFile::CodeItem* const code_item_; // The code item containing the code for the method. + const CodeItemDataAccessor code_item_accessor_; const RegType* declaring_class_; // Lazily computed reg type of the method's declaring class. // Instruction widths and flags, one entry per code unit. // Owned, but not unique_ptr since insn_flags_ are allocated in arenas. @@ -747,7 +762,7 @@ class MethodVerifier { uint32_t interesting_dex_pc_; // The container into which FindLocksAtDexPc should write the registers containing held locks, // null if we're not doing FindLocksAtDexPc. - std::vector* monitor_enter_dex_pcs_; + std::vector* monitor_enter_dex_pcs_; // The types of any error that occurs. std::vector failures_; diff --git a/runtime/verifier/reg_type-inl.h b/runtime/verifier/reg_type-inl.h index 631c6bd7efa8d837d5bfc890884a7aec8aed1f9c..f7197827277a9e7b236a233ccf2afe474517090c 100644 --- a/runtime/verifier/reg_type-inl.h +++ b/runtime/verifier/reg_type-inl.h @@ -29,6 +29,8 @@ namespace art { namespace verifier { inline bool RegType::CanAccess(const RegType& other) const { + DCHECK(IsReferenceTypes()); + DCHECK(!IsNull()); if (Equals(other)) { return true; // Trivial accessibility. } else { @@ -45,9 +47,13 @@ inline bool RegType::CanAccess(const RegType& other) const { } inline bool RegType::CanAccessMember(ObjPtr klass, uint32_t access_flags) const { + DCHECK(IsReferenceTypes()); if ((access_flags & kAccPublic) != 0) { return true; } + if (IsNull()) { + return true; + } if (!IsUnresolvedTypes()) { return GetClass()->CanAccessMember(klass, access_flags); } else { @@ -92,7 +98,7 @@ inline bool RegType::AssignableFrom(const RegType& lhs, LOG(WARNING) << "RegType::AssignableFrom lhs is Conflict!"; return false; case AssignmentType::kReference: - if (rhs.IsZero()) { + if (rhs.IsZeroOrNull()) { return true; // All reference types can be assigned null. } else if (!rhs.IsReferenceTypes()) { return false; // Expect rhs to be a reference type. @@ -119,6 +125,7 @@ inline bool RegType::AssignableFrom(const RegType& lhs, return result; } else { // Unresolved types are only assignable for null and equality. + // Null cannot be the left-hand side. return false; } case AssignmentType::kNotAssignable: @@ -199,6 +206,11 @@ inline const UndefinedType* UndefinedType::GetInstance() { return instance_; } +inline const NullType* NullType::GetInstance() { + DCHECK(instance_ != nullptr); + return instance_; +} + inline void* RegType::operator new(size_t size, ScopedArenaAllocator* allocator) { return allocator->Alloc(size, kArenaAllocMisc); } diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc index 8df2e0f50b685169930400650f22e6943dcae10c..309c374fa8f56a75dff3beb1e3449981da115235 100644 --- a/runtime/verifier/reg_type.cc +++ b/runtime/verifier/reg_type.cc @@ -51,6 +51,7 @@ const LongHiType* LongHiType::instance_ = nullptr; const DoubleLoType* DoubleLoType::instance_ = nullptr; const DoubleHiType* DoubleHiType::instance_ = nullptr; const IntegerType* IntegerType::instance_ = nullptr; +const NullType* NullType::instance_ = nullptr; PrimitiveType::PrimitiveType(mirror::Class* klass, const StringPiece& descriptor, uint16_t cache_id) : RegType(klass, descriptor, cache_id) { @@ -581,6 +582,10 @@ static const RegType& SelectNonConstant(const RegType& a, const RegType& b) { return a.IsConstantTypes() ? b : a; } +static const RegType& SelectNonConstant2(const RegType& a, const RegType& b) { + return a.IsConstantTypes() ? (b.IsZero() ? a : b) : a; +} + const RegType& RegType::Merge(const RegType& incoming_type, RegTypeCache* reg_types, MethodVerifier* verifier) const { @@ -695,8 +700,8 @@ const RegType& RegType::Merge(const RegType& incoming_type, // special. They may only ever be merged with themselves (must be taken care of by the // caller of Merge(), see the DCHECK on entry). So mark any other merge as conflicting here. return conflict; - } else if (IsZero() || incoming_type.IsZero()) { - return SelectNonConstant(*this, incoming_type); // 0 MERGE ref => ref + } else if (IsZeroOrNull() || incoming_type.IsZeroOrNull()) { + return SelectNonConstant2(*this, incoming_type); // 0 MERGE ref => ref } else if (IsJavaLangObject() || incoming_type.IsJavaLangObject()) { return reg_types->JavaLangObject(false); // Object MERGE ref => Object } else if (IsUnresolvedTypes() || incoming_type.IsUnresolvedTypes()) { @@ -965,6 +970,21 @@ bool RegType::CanAssignArray(const RegType& src, return cmp1.CanAssignArray(cmp2, reg_types, class_loader, verifier, soft_error); } +const NullType* NullType::CreateInstance(mirror::Class* klass, + const StringPiece& descriptor, + uint16_t cache_id) { + CHECK(instance_ == nullptr); + instance_ = new NullType(klass, descriptor, cache_id); + return instance_; +} + +void NullType::Destroy() { + if (NullType::instance_ != nullptr) { + delete instance_; + instance_ = nullptr; + } +} + } // namespace verifier } // namespace art diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h index a2085a3f09869638fc458ecbf785a0212deb25cf..9055849ca0bfab17f996d79d64ed260a79ff67b1 100644 --- a/runtime/verifier/reg_type.h +++ b/runtime/verifier/reg_type.h @@ -129,8 +129,12 @@ class RegType { virtual bool IsConstantShort() const { return false; } virtual bool IsOne() const { return false; } virtual bool IsZero() const { return false; } + virtual bool IsNull() const { return false; } bool IsReferenceTypes() const { - return IsNonZeroReferenceTypes() || IsZero(); + return IsNonZeroReferenceTypes() || IsZero() || IsNull(); + } + bool IsZeroOrNull() const { + return IsZero() || IsNull(); } virtual bool IsNonZeroReferenceTypes() const { return false; } bool IsCategory1Types() const { @@ -857,6 +861,46 @@ class ImpreciseConstHiType FINAL : public ConstantType { } }; +// Special "null" type that captures the semantics of null / bottom. +class NullType FINAL : public RegType { + public: + bool IsNull() const OVERRIDE { + return true; + } + + // Get the singleton Null instance. + static const NullType* GetInstance() PURE; + + // Create the singleton instance. + static const NullType* CreateInstance(mirror::Class* klass, + const StringPiece& descriptor, + uint16_t cache_id) + REQUIRES_SHARED(Locks::mutator_lock_); + + static void Destroy(); + + std::string Dump() const OVERRIDE { + return "null"; + } + + AssignmentType GetAssignmentTypeImpl() const OVERRIDE { + return AssignmentType::kReference; + } + + bool IsConstantTypes() const OVERRIDE { + return true; + } + + private: + NullType(mirror::Class* klass, const StringPiece& descriptor, uint16_t cache_id) + REQUIRES_SHARED(Locks::mutator_lock_) + : RegType(klass, descriptor, cache_id) { + CheckConstructorInvariants(this); + } + + static const NullType* instance_; +}; + // Common parent of all uninitialized types. Uninitialized types are created by // "new" dex // instructions and must be passed to a constructor. diff --git a/runtime/verifier/reg_type_cache-inl.h b/runtime/verifier/reg_type_cache-inl.h index 197c97671eefde72c1340e9264adcaedf18dcf6b..61f34afdacf55b72eb24c45142594f0e8c7d89f3 100644 --- a/runtime/verifier/reg_type_cache-inl.h +++ b/runtime/verifier/reg_type_cache-inl.h @@ -81,6 +81,9 @@ inline const UndefinedType& RegTypeCache::Undefined() { inline const ConflictType& RegTypeCache::Conflict() { return *ConflictType::GetInstance(); } +inline const NullType& RegTypeCache::Null() { + return *NullType::GetInstance(); +} inline const ImpreciseConstType& RegTypeCache::ByteConstant() { const ConstantType& result = FromCat1Const(std::numeric_limits::min(), false); diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc index 0029eb90a354ac58d70d1dcc829e7bfaca2ffb62..c68fa0f0d685a39fb75729c89e401c1ce6465701 100644 --- a/runtime/verifier/reg_type_cache.cc +++ b/runtime/verifier/reg_type_cache.cc @@ -16,6 +16,9 @@ #include "reg_type_cache-inl.h" +#include + +#include "base/aborting.h" #include "base/arena_bit_vector.h" #include "base/bit_vector-inl.h" #include "base/casts.h" @@ -51,8 +54,10 @@ ALWAYS_INLINE static inline bool MatchingPrecisionForClass(const RegType* entry, } void RegTypeCache::FillPrimitiveAndSmallConstantTypes() { + // Note: this must have the same order as CreatePrimitiveAndSmallConstantTypes. entries_.push_back(UndefinedType::GetInstance()); entries_.push_back(ConflictType::GetInstance()); + entries_.push_back(NullType::GetInstance()); entries_.push_back(BooleanType::GetInstance()); entries_.push_back(ByteType::GetInstance()); entries_.push_back(ShortType::GetInstance()); @@ -304,6 +309,7 @@ void RegTypeCache::ShutDown() { FloatType::Destroy(); DoubleLoType::Destroy(); DoubleHiType::Destroy(); + NullType::Destroy(); for (int32_t value = kMinSmallConstant; value <= kMaxSmallConstant; ++value) { const PreciseConstType* type = small_precise_constants_[value - kMinSmallConstant]; delete type; @@ -314,33 +320,55 @@ void RegTypeCache::ShutDown() { } } -template -const Type* RegTypeCache::CreatePrimitiveTypeInstance(const std::string& descriptor) { - mirror::Class* klass = nullptr; - // Try loading the class from linker. - if (!descriptor.empty()) { - klass = art::Runtime::Current()->GetClassLinker()->FindSystemClass(Thread::Current(), - descriptor.c_str()); - DCHECK(klass != nullptr); - } - const Type* entry = Type::CreateInstance(klass, descriptor, RegTypeCache::primitive_count_); - RegTypeCache::primitive_count_++; - return entry; -} +// Helper for create_primitive_type_instance lambda. +namespace { +template +struct TypeHelper { + using type = T; + static_assert(std::is_convertible::value, "T must be a RegType"); + + const char* descriptor; + + explicit TypeHelper(const char* d) : descriptor(d) {} +}; +} // namespace void RegTypeCache::CreatePrimitiveAndSmallConstantTypes() { - CreatePrimitiveTypeInstance(""); - CreatePrimitiveTypeInstance(""); - CreatePrimitiveTypeInstance("Z"); - CreatePrimitiveTypeInstance("B"); - CreatePrimitiveTypeInstance("S"); - CreatePrimitiveTypeInstance("C"); - CreatePrimitiveTypeInstance("I"); - CreatePrimitiveTypeInstance("J"); - CreatePrimitiveTypeInstance("J"); - CreatePrimitiveTypeInstance("F"); - CreatePrimitiveTypeInstance("D"); - CreatePrimitiveTypeInstance("D"); + // Note: this must have the same order as FillPrimitiveAndSmallConstantTypes. + + // It is acceptable to pass on the const char* in type to CreateInstance, as all calls below are + // with compile-time constants that will have global lifetime. Use of the lambda ensures this + // code cannot leak to other users. + auto create_primitive_type_instance = [&](auto type) REQUIRES_SHARED(Locks::mutator_lock_) { + using Type = typename decltype(type)::type; + mirror::Class* klass = nullptr; + // Try loading the class from linker. + DCHECK(type.descriptor != nullptr); + if (strlen(type.descriptor) > 0) { + klass = art::Runtime::Current()->GetClassLinker()->FindSystemClass(Thread::Current(), + type.descriptor); + DCHECK(klass != nullptr); + } + const Type* entry = Type::CreateInstance(klass, + type.descriptor, + RegTypeCache::primitive_count_); + RegTypeCache::primitive_count_++; + return entry; + }; + create_primitive_type_instance(TypeHelper("")); + create_primitive_type_instance(TypeHelper("")); + create_primitive_type_instance(TypeHelper("")); + create_primitive_type_instance(TypeHelper("Z")); + create_primitive_type_instance(TypeHelper("B")); + create_primitive_type_instance(TypeHelper("S")); + create_primitive_type_instance(TypeHelper("C")); + create_primitive_type_instance(TypeHelper("I")); + create_primitive_type_instance(TypeHelper("J")); + create_primitive_type_instance(TypeHelper("J")); + create_primitive_type_instance(TypeHelper("F")); + create_primitive_type_instance(TypeHelper("D")); + create_primitive_type_instance(TypeHelper("D")); + for (int32_t value = kMinSmallConstant; value <= kMaxSmallConstant; ++value) { PreciseConstType* type = new PreciseConstType(value, primitive_count_); small_precise_constants_[value - kMinSmallConstant] = type; @@ -396,6 +424,9 @@ const RegType& RegTypeCache::FromUnresolvedMerge(const RegType& left, if (resolved_parts_merged.IsConflict()) { return Conflict(); } + if (resolved_parts_merged.IsJavaLangObject()) { + return resolved_parts_merged; + } bool resolved_merged_is_array = resolved_parts_merged.IsArrayTypes(); if (left_unresolved_is_array || right_unresolved_is_array || resolved_merged_is_array) { diff --git a/runtime/verifier/reg_type_cache.h b/runtime/verifier/reg_type_cache.h index d0907564e248119d60b28d4677b997c7c0398106..52776766bcf9e92aa342fca59ddcef6a229d0f81 100644 --- a/runtime/verifier/reg_type_cache.h +++ b/runtime/verifier/reg_type_cache.h @@ -49,6 +49,7 @@ class IntegerType; class LongHiType; class LongLoType; class MethodVerifier; +class NullType; class PreciseConstType; class PreciseReferenceType; class RegType; @@ -123,6 +124,7 @@ class RegTypeCache { const DoubleHiType& DoubleHi() REQUIRES_SHARED(Locks::mutator_lock_); const UndefinedType& Undefined() REQUIRES_SHARED(Locks::mutator_lock_); const ConflictType& Conflict(); + const NullType& Null(); const PreciseReferenceType& JavaLangClass() REQUIRES_SHARED(Locks::mutator_lock_); const PreciseReferenceType& JavaLangString() REQUIRES_SHARED(Locks::mutator_lock_); @@ -171,9 +173,6 @@ class RegTypeCache { // verifier. StringPiece AddString(const StringPiece& string_piece); - template - static const Type* CreatePrimitiveTypeInstance(const std::string& descriptor) - REQUIRES_SHARED(Locks::mutator_lock_); static void CreatePrimitiveAndSmallConstantTypes() REQUIRES_SHARED(Locks::mutator_lock_); // A quick look up for popular small constants. @@ -183,7 +182,7 @@ class RegTypeCache { kMinSmallConstant + 1]; static constexpr size_t kNumPrimitivesAndSmallConstants = - 12 + (kMaxSmallConstant - kMinSmallConstant + 1); + 13 + (kMaxSmallConstant - kMinSmallConstant + 1); // Have the well known global primitives been created? static bool primitive_initialized_; diff --git a/runtime/verifier/reg_type_test.cc b/runtime/verifier/reg_type_test.cc index 1bc48ed71b7644b0d363f7b95177630b9911661b..15a38f3fd7a5a5116b77f824df9ccbcb834e084c 100644 --- a/runtime/verifier/reg_type_test.cc +++ b/runtime/verifier/reg_type_test.cc @@ -664,6 +664,368 @@ TEST_F(RegTypeTest, MergingDouble) { } } +TEST_F(RegTypeTest, MergeSemiLatticeRef) { + // (Incomplete) semilattice: + // + // Excluded for now: * category-2 types + // * interfaces + // * all of category-1 primitive types, including constants. + // This is to demonstrate/codify the reference side, mostly. + // + // Note: It is not a real semilattice because int = float makes this wonky. :-( + // + // Conflict + // | + // #---------#--------------------------#-----------------------------# + // | | | + // | | Object + // | | | + // int uninit types #---------------#--------#------------------#---------# + // | | | | | | + // | unresolved-merge-types | Object[] char[] byte[] + // | | | | | | | | + // | unresolved-types | #------Number #---------# | | + // | | | | | | | | + // | | #--------Integer Number[] Number[][] | | + // | | | | | | | + // | #---------------#--------#---------#--------#---------# + // | | + // | null + // | | + // #--------------------------#----------------------------# + // | + // 0 + + ArenaStack stack(Runtime::Current()->GetArenaPool()); + ScopedArenaAllocator allocator(&stack); + ScopedObjectAccess soa(Thread::Current()); + + // We cannot allow moving GC. Otherwise we'd have to ensure the reg types are updated (reference + // reg types store a class pointer in a GCRoot, which is normally updated through active verifiers + // being registered with their thread), which is unnecessarily complex. + Runtime::Current()->GetHeap()->IncrementDisableMovingGC(soa.Self()); + + RegTypeCache cache(true, allocator); + + const RegType& conflict = cache.Conflict(); + const RegType& zero = cache.Zero(); + const RegType& null = cache.Null(); + const RegType& int_type = cache.Integer(); + + const RegType& obj = cache.JavaLangObject(false); + const RegType& obj_arr = cache.From(nullptr, "[Ljava/lang/Object;", false); + ASSERT_FALSE(obj_arr.IsUnresolvedReference()); + + const RegType& unresolved_a = cache.From(nullptr, "Ldoes/not/resolve/A;", false); + ASSERT_TRUE(unresolved_a.IsUnresolvedReference()); + const RegType& unresolved_b = cache.From(nullptr, "Ldoes/not/resolve/B;", false); + ASSERT_TRUE(unresolved_b.IsUnresolvedReference()); + const RegType& unresolved_ab = cache.FromUnresolvedMerge(unresolved_a, unresolved_b, nullptr); + ASSERT_TRUE(unresolved_ab.IsUnresolvedMergedReference()); + + const RegType& uninit_this = cache.UninitializedThisArgument(obj); + const RegType& uninit_obj_0 = cache.Uninitialized(obj, 0u); + const RegType& uninit_obj_1 = cache.Uninitialized(obj, 1u); + + const RegType& uninit_unres_this = cache.UninitializedThisArgument(unresolved_a); + const RegType& uninit_unres_a_0 = cache.Uninitialized(unresolved_a, 0); + const RegType& uninit_unres_b_0 = cache.Uninitialized(unresolved_b, 0); + + const RegType& number = cache.From(nullptr, "Ljava/lang/Number;", false); + ASSERT_FALSE(number.IsUnresolvedReference()); + const RegType& integer = cache.From(nullptr, "Ljava/lang/Integer;", false); + ASSERT_FALSE(integer.IsUnresolvedReference()); + + const RegType& uninit_number_0 = cache.Uninitialized(number, 0u); + const RegType& uninit_integer_0 = cache.Uninitialized(integer, 0u); + + const RegType& number_arr = cache.From(nullptr, "[Ljava/lang/Number;", false); + ASSERT_FALSE(number_arr.IsUnresolvedReference()); + const RegType& integer_arr = cache.From(nullptr, "[Ljava/lang/Integer;", false); + ASSERT_FALSE(integer_arr.IsUnresolvedReference()); + + const RegType& number_arr_arr = cache.From(nullptr, "[[Ljava/lang/Number;", false); + ASSERT_FALSE(number_arr_arr.IsUnresolvedReference()); + + const RegType& char_arr = cache.From(nullptr, "[C", false); + ASSERT_FALSE(char_arr.IsUnresolvedReference()); + const RegType& byte_arr = cache.From(nullptr, "[B", false); + ASSERT_FALSE(byte_arr.IsUnresolvedReference()); + + const RegType& unresolved_a_num = cache.FromUnresolvedMerge(unresolved_a, number, nullptr); + ASSERT_TRUE(unresolved_a_num.IsUnresolvedMergedReference()); + const RegType& unresolved_b_num = cache.FromUnresolvedMerge(unresolved_b, number, nullptr); + ASSERT_TRUE(unresolved_b_num.IsUnresolvedMergedReference()); + const RegType& unresolved_ab_num = cache.FromUnresolvedMerge(unresolved_ab, number, nullptr); + ASSERT_TRUE(unresolved_ab_num.IsUnresolvedMergedReference()); + + const RegType& unresolved_a_int = cache.FromUnresolvedMerge(unresolved_a, integer, nullptr); + ASSERT_TRUE(unresolved_a_int.IsUnresolvedMergedReference()); + const RegType& unresolved_b_int = cache.FromUnresolvedMerge(unresolved_b, integer, nullptr); + ASSERT_TRUE(unresolved_b_int.IsUnresolvedMergedReference()); + const RegType& unresolved_ab_int = cache.FromUnresolvedMerge(unresolved_ab, integer, nullptr); + ASSERT_TRUE(unresolved_ab_int.IsUnresolvedMergedReference()); + std::vector uninitialized_types = { + &uninit_this, &uninit_obj_0, &uninit_obj_1, &uninit_number_0, &uninit_integer_0 + }; + std::vector unresolved_types = { + &unresolved_a, + &unresolved_b, + &unresolved_ab, + &unresolved_a_num, + &unresolved_b_num, + &unresolved_ab_num, + &unresolved_a_int, + &unresolved_b_int, + &unresolved_ab_int + }; + std::vector uninit_unresolved_types = { + &uninit_unres_this, &uninit_unres_a_0, &uninit_unres_b_0 + }; + std::vector plain_nonobj_classes = { &number, &integer }; + std::vector plain_nonobj_arr_classes = { + &number_arr, + &number_arr_arr, + &integer_arr, + &char_arr, + }; + // std::vector others = { &conflict, &zero, &null, &obj, &int_type }; + + std::vector all_minus_uninit_conflict; + all_minus_uninit_conflict.insert(all_minus_uninit_conflict.end(), + unresolved_types.begin(), + unresolved_types.end()); + all_minus_uninit_conflict.insert(all_minus_uninit_conflict.end(), + plain_nonobj_classes.begin(), + plain_nonobj_classes.end()); + all_minus_uninit_conflict.insert(all_minus_uninit_conflict.end(), + plain_nonobj_arr_classes.begin(), + plain_nonobj_arr_classes.end()); + all_minus_uninit_conflict.push_back(&zero); + all_minus_uninit_conflict.push_back(&null); + all_minus_uninit_conflict.push_back(&obj); + + std::vector all_minus_uninit; + all_minus_uninit.insert(all_minus_uninit.end(), + all_minus_uninit_conflict.begin(), + all_minus_uninit_conflict.end()); + all_minus_uninit.push_back(&conflict); + + + std::vector all; + all.insert(all.end(), uninitialized_types.begin(), uninitialized_types.end()); + all.insert(all.end(), uninit_unresolved_types.begin(), uninit_unresolved_types.end()); + all.insert(all.end(), all_minus_uninit.begin(), all_minus_uninit.end()); + all.push_back(&int_type); + + auto check = [&](const RegType& in1, const RegType& in2, const RegType& expected_out) + REQUIRES_SHARED(Locks::mutator_lock_) { + const RegType& merge_result = in1.SafeMerge(in2, &cache, nullptr); + EXPECT_EQ(&expected_out, &merge_result) + << in1.Dump() << " x " << in2.Dump() << " = " << merge_result.Dump() + << " != " << expected_out.Dump(); + }; + + // Identity. + { + for (auto r : all) { + check(*r, *r, *r); + } + } + + // Define a covering relation through a list of Edges. We'll then derive LUBs from this and + // create checks for every pair of types. + + struct Edge { + const RegType& from; + const RegType& to; + + Edge(const RegType& from_, const RegType& to_) : from(from_), to(to_) {} + }; + std::vector edges; +#define ADD_EDGE(from, to) edges.emplace_back((from), (to)) + + // To Conflict. + { + for (auto r : uninitialized_types) { + ADD_EDGE(*r, conflict); + } + for (auto r : uninit_unresolved_types) { + ADD_EDGE(*r, conflict); + } + ADD_EDGE(obj, conflict); + ADD_EDGE(int_type, conflict); + } + + ADD_EDGE(zero, null); + + // Unresolved. + { + ADD_EDGE(null, unresolved_a); + ADD_EDGE(null, unresolved_b); + ADD_EDGE(unresolved_a, unresolved_ab); + ADD_EDGE(unresolved_b, unresolved_ab); + + ADD_EDGE(number, unresolved_a_num); + ADD_EDGE(unresolved_a, unresolved_a_num); + ADD_EDGE(number, unresolved_b_num); + ADD_EDGE(unresolved_b, unresolved_b_num); + ADD_EDGE(number, unresolved_ab_num); + ADD_EDGE(unresolved_a_num, unresolved_ab_num); + ADD_EDGE(unresolved_b_num, unresolved_ab_num); + ADD_EDGE(unresolved_ab, unresolved_ab_num); + + ADD_EDGE(integer, unresolved_a_int); + ADD_EDGE(unresolved_a, unresolved_a_int); + ADD_EDGE(integer, unresolved_b_int); + ADD_EDGE(unresolved_b, unresolved_b_int); + ADD_EDGE(integer, unresolved_ab_int); + ADD_EDGE(unresolved_a_int, unresolved_ab_int); + ADD_EDGE(unresolved_b_int, unresolved_ab_int); + ADD_EDGE(unresolved_ab, unresolved_ab_int); + + ADD_EDGE(unresolved_a_int, unresolved_a_num); + ADD_EDGE(unresolved_b_int, unresolved_b_num); + ADD_EDGE(unresolved_ab_int, unresolved_ab_num); + + ADD_EDGE(unresolved_ab_num, obj); + } + + // Classes. + { + ADD_EDGE(null, integer); + ADD_EDGE(integer, number); + ADD_EDGE(number, obj); + } + + // Arrays. + { + ADD_EDGE(integer_arr, number_arr); + ADD_EDGE(number_arr, obj_arr); + ADD_EDGE(obj_arr, obj); + ADD_EDGE(number_arr_arr, obj_arr); + + ADD_EDGE(char_arr, obj); + ADD_EDGE(byte_arr, obj); + + ADD_EDGE(null, integer_arr); + ADD_EDGE(null, number_arr_arr); + ADD_EDGE(null, char_arr); + ADD_EDGE(null, byte_arr); + } + + // Primitive. + { + ADD_EDGE(zero, int_type); + } +#undef ADD_EDGE + + // Create merge triples by using the covering relation established by edges to derive the + // expected merge for any pair of types. + + // Expect merge(in1, in2) == out. + struct MergeExpectation { + const RegType& in1; + const RegType& in2; + const RegType& out; + + MergeExpectation(const RegType& in1_, const RegType& in2_, const RegType& out_) + : in1(in1_), in2(in2_), out(out_) {} + }; + std::vector expectations; + + for (auto r1 : all) { + for (auto r2 : all) { + if (r1 == r2) { + continue; + } + + // Very simple algorithm here that is usually used with adjacency lists. Our graph is + // small, it didn't make sense to have lists per node. Thus, the regular guarantees + // of O(n + |e|) don't apply, but that is acceptable. + // + // To compute r1 lub r2 = merge(r1, r2): + // 1) Generate the reachable set of r1, name it grey. + // 2) Mark all grey reachable nodes of r2 as black. + // 3) Find black nodes with no in-edges from other black nodes. + // 4) If |3)| == 1, that's the lub. + + // Generic BFS of the graph induced by edges, starting at start. new_node will be called + // with any discovered node, in order. + auto bfs = [&](auto new_node, const RegType* start) { + std::unordered_set seen; + std::queue work_list; + work_list.push(start); + while (!work_list.empty()) { + const RegType* cur = work_list.front(); + work_list.pop(); + auto it = seen.find(cur); + if (it != seen.end()) { + continue; + } + seen.insert(cur); + new_node(cur); + + for (const Edge& edge : edges) { + if (&edge.from == cur) { + work_list.push(&edge.to); + } + } + } + }; + + std::unordered_set grey; + auto compute_grey = [&](const RegType* cur) { + grey.insert(cur); // Mark discovered node as grey. + }; + bfs(compute_grey, r1); + + std::set black; + auto compute_black = [&](const RegType* cur) { + // Mark discovered grey node as black. + if (grey.find(cur) != grey.end()) { + black.insert(cur); + } + }; + bfs(compute_black, r2); + + std::set no_in_edge(black); // Copy of black, remove nodes with in-edges. + for (auto r : black) { + for (Edge& e : edges) { + if (&e.from == r) { + no_in_edge.erase(&e.to); // It doesn't matter whether "to" is black or not, just + // attempt to remove it. + } + } + } + + // Helper to print sets when something went wrong. + auto print_set = [](auto& container) REQUIRES_SHARED(Locks::mutator_lock_) { + std::string result; + for (auto r : container) { + result.append(" + "); + result.append(r->Dump()); + } + return result; + }; + ASSERT_EQ(no_in_edge.size(), 1u) << r1->Dump() << " u " << r2->Dump() + << " grey=" << print_set(grey) + << " black=" << print_set(black) + << " no-in-edge=" << print_set(no_in_edge); + expectations.emplace_back(*r1, *r2, **no_in_edge.begin()); + } + } + + // Evaluate merge expectations. The merge is expected to be commutative. + + for (auto& triple : expectations) { + check(triple.in1, triple.in2, triple.out); + check(triple.in2, triple.in1, triple.out); + } + + Runtime::Current()->GetHeap()->DecrementDisableMovingGC(soa.Self()); +} + TEST_F(RegTypeTest, ConstPrecision) { // Tests creating primitive types types. ArenaStack stack(Runtime::Current()->GetArenaPool()); diff --git a/runtime/verifier/register_line-inl.h b/runtime/verifier/register_line-inl.h index a9c9428581ae295565d26deced3de9c9a4fe6768..39d73f54d8257045fffde7f62ede9d0fdfb94089 100644 --- a/runtime/verifier/register_line-inl.h +++ b/runtime/verifier/register_line-inl.h @@ -19,6 +19,7 @@ #include "register_line.h" +#include "base/logging.h" // For VLOG. #include "method_verifier.h" #include "reg_type_cache-inl.h" @@ -192,6 +193,27 @@ inline RegisterLine::RegisterLine(size_t num_regs, MethodVerifier* verifier) SetResultTypeToUnknown(verifier); } +inline void RegisterLine::ClearRegToLockDepth(size_t reg, size_t depth) { + CHECK_LT(depth, 32u); + DCHECK(IsSetLockDepth(reg, depth)); + auto it = reg_to_lock_depths_.find(reg); + DCHECK(it != reg_to_lock_depths_.end()); + uint32_t depths = it->second ^ (1 << depth); + if (depths != 0) { + it->second = depths; + } else { + reg_to_lock_depths_.erase(it); + } + // Need to unlock every register at the same lock depth. These are aliased locks. + uint32_t mask = 1 << depth; + for (auto& pair : reg_to_lock_depths_) { + if ((pair.second & mask) != 0) { + VLOG(verifier) << "Also unlocking " << pair.first; + pair.second ^= mask; + } + } +} + inline void RegisterLineArenaDelete::operator()(RegisterLine* ptr) const { if (ptr != nullptr) { ptr->~RegisterLine(); diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h index 221aa80e43a3a92863b57087123db941a671c30b..82f63b281abc836810954787d00085d766fe8dd3 100644 --- a/runtime/verifier/register_line.h +++ b/runtime/verifier/register_line.h @@ -20,6 +20,8 @@ #include #include +#include + #include "base/scoped_arena_containers.h" #include "safe_map.h" @@ -353,6 +355,23 @@ class RegisterLine { return monitors_[i]; } + // We give access to the lock depth map to avoid an expensive poll loop for FindLocksAtDexPC. + template + void IterateRegToLockDepths(T fn) const { + for (const auto& pair : reg_to_lock_depths_) { + const uint32_t reg = pair.first; + uint32_t depths = pair.second; + uint32_t depth = 0; + while (depths != 0) { + if ((depths & 1) != 0) { + fn(reg, depth); + } + depths >>= 1; + depth++; + } + } + } + private: void CopyRegToLockDepth(size_t dst, size_t src) { auto it = reg_to_lock_depths_.find(src); @@ -384,26 +403,7 @@ class RegisterLine { return true; } - void ClearRegToLockDepth(size_t reg, size_t depth) { - CHECK_LT(depth, 32u); - DCHECK(IsSetLockDepth(reg, depth)); - auto it = reg_to_lock_depths_.find(reg); - DCHECK(it != reg_to_lock_depths_.end()); - uint32_t depths = it->second ^ (1 << depth); - if (depths != 0) { - it->second = depths; - } else { - reg_to_lock_depths_.erase(it); - } - // Need to unlock every register at the same lock depth. These are aliased locks. - uint32_t mask = 1 << depth; - for (auto& pair : reg_to_lock_depths_) { - if ((pair.second & mask) != 0) { - VLOG(verifier) << "Also unlocking " << pair.first; - pair.second ^= mask; - } - } - } + void ClearRegToLockDepth(size_t reg, size_t depth); void ClearAllRegToLockDepths(size_t reg) { reg_to_lock_depths_.erase(reg); diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 9722db96414ab404e648fd618a5423b2847d63a4..5fef7dfd830c64a41292c2f2f39e6e82f934d9b9 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -20,9 +20,9 @@ #include -#include "android-base/stringprintf.h" +#include +#include -#include "base/logging.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "jni_internal.h" #include "mirror/class.h" @@ -269,7 +269,7 @@ uint32_t WellKnownClasses::StringInitToEntryPoint(ArtMethod* string_init) { return kQuick ## entry_point_name; \ } STRING_INIT_LIST(TO_ENTRY_POINT) - #undef TO_STRING_FACTORY + #undef TO_ENTRY_POINT LOG(FATAL) << "Could not find StringFactory method for String."; return 0; } diff --git a/runtime/zip_archive.h b/runtime/zip_archive.h index 821cc5ceb1110be0771818964fd3abe175c5d557..75f8757f6c7b7a869655f7146efe0f9bd6ae5357 100644 --- a/runtime/zip_archive.h +++ b/runtime/zip_archive.h @@ -21,7 +21,8 @@ #include #include -#include "base/logging.h" +#include + #include "base/unix_file/random_access_file.h" #include "globals.h" #include "mem_map.h" diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc index 6800d027f87ad99d795f3fbb673cc18daf633539..b8ab51b6292fd631e46427428fb9770c0dad7581 100644 --- a/sigchainlib/sigchain.cc +++ b/sigchainlib/sigchain.cc @@ -30,7 +30,7 @@ #include #include -#include // NOLINT [build/c++11] [5] +#include #include #include "sigchain.h" diff --git a/simulator/code_simulator_arm64.cc b/simulator/code_simulator_arm64.cc index 939d2e287ff401a7d6dd6d39253a6d74e554dd53..a64bd0bc0b5db22a4625dc7349a32387aa721aa4 100644 --- a/simulator/code_simulator_arm64.cc +++ b/simulator/code_simulator_arm64.cc @@ -16,7 +16,7 @@ #include "code_simulator_arm64.h" -#include "base/logging.h" +#include using namespace vixl::aarch64; // NOLINT(build/namespaces) diff --git a/simulator/code_simulator_container.cc b/simulator/code_simulator_container.cc index a5f05dc8fceccb6a0119fc8cb1527678568f9414..9f52b320f2b9c9d498f4aa3dbf7289a983a14a15 100644 --- a/simulator/code_simulator_container.cc +++ b/simulator/code_simulator_container.cc @@ -18,6 +18,7 @@ #include "code_simulator_container.h" +#include "base/logging.h" // For VLOG. #include "code_simulator.h" #include "globals.h" diff --git a/simulator/code_simulator_container.h b/simulator/code_simulator_container.h index 31a915e4f18fa01a9103d3958e7724ae1647d166..a21971508a060a64be9bb4674d1065ee85aab226 100644 --- a/simulator/code_simulator_container.h +++ b/simulator/code_simulator_container.h @@ -17,8 +17,9 @@ #ifndef ART_SIMULATOR_CODE_SIMULATOR_CONTAINER_H_ #define ART_SIMULATOR_CODE_SIMULATOR_CONTAINER_H_ +#include + #include "arch/instruction_set.h" -#include "base/logging.h" namespace art { diff --git a/test/004-JniTest/expected.txt b/test/004-JniTest/expected.txt index 1d05160883c1060a4f50155d2b952c56c31d1c69..b09b9a22eb8c156e8358629da9b1f1d8705158a3 100644 --- a/test/004-JniTest/expected.txt +++ b/test/004-JniTest/expected.txt @@ -1,4 +1,5 @@ JNI_OnLoad called +ABC.XYZ = 12, GetStaticIntField(DEF.class, 'XYZ') = 12 Super. Super. Subclass. diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc index bc5a0a64e83cd658a7c64490f2a3779ddb64e14b..33a8f5bba26aba628bcc2f847114bfe7cdd13d25 100644 --- a/test/004-JniTest/jni_test.cc +++ b/test/004-JniTest/jni_test.cc @@ -20,8 +20,10 @@ #include #include +#include + #include "art_method-inl.h" -#include "base/logging.h" +#include "base/runtime_debug.h" #include "jni.h" namespace art { @@ -88,6 +90,14 @@ static void testFindClassOnAttachedNativeThread(JNIEnv* env) { CHECK(!env->ExceptionCheck()); } +extern "C" JNIEXPORT jint JNICALL Java_Main_getFieldSubclass(JNIEnv* env, + jclass, + jobject f_obj, + jclass sub) { + jfieldID f = env->FromReflectedField(f_obj); + return env->GetStaticIntField(sub, f); +} + // http://b/10994325 extern "C" JNIEXPORT void JNICALL Java_Main_testFindClassOnAttachedNativeThread(JNIEnv*, jclass) { PthreadHelper(&testFindClassOnAttachedNativeThread); diff --git a/test/004-JniTest/src/Main.java b/test/004-JniTest/src/Main.java index 871107c56b49e95eed5df0a4d737619926aaa17f..f94dcf6c70d96d11a657a8c65849d254041069ca 100644 --- a/test/004-JniTest/src/Main.java +++ b/test/004-JniTest/src/Main.java @@ -18,6 +18,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Field; import java.lang.reflect.Proxy; import java.util.regex.Pattern; @@ -32,6 +33,7 @@ public class Main { throw new RuntimeException("Slow-debug flags unexpectedly off."); } + testFieldSubclass(); testFindClassOnAttachedNativeThread(); testFindFieldOnAttachedNativeThread(); testReflectFieldGetFromAttachedNativeThreadNative(); @@ -65,6 +67,19 @@ public class Main { testDoubleLoad(args[0]); } + static class ABC { public static int XYZ = 12; } + static class DEF extends ABC {} + public static void testFieldSubclass() { + try { + System.out.println("ABC.XYZ = " + ABC.XYZ + ", GetStaticIntField(DEF.class, 'XYZ') = " + + getFieldSubclass(ABC.class.getDeclaredField("XYZ"), DEF.class)); + } catch (Exception e) { + throw new RuntimeException("Failed to test get static field on a subclass", e); + } + } + + public static native int getFieldSubclass(Field f, Class sub); + private static native boolean registerNativesJniTest(); private static native void testCallDefaultMethods(); diff --git a/test/031-class-attributes/jasmin/ClassAttrs$1.j b/test/031-class-attributes/jasmin/ClassAttrs$1.j new file mode 100644 index 0000000000000000000000000000000000000000..ea767efdba4f3e5430511b37a8b8f7708bb90d72 --- /dev/null +++ b/test/031-class-attributes/jasmin/ClassAttrs$1.j @@ -0,0 +1,49 @@ +; 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. + +; (new OtherClass() { int i = 5; }).getClass() + +; ClassAttrs$1.j + +; Generated by ClassFileAnalyzer (Can) +; Analyzer and Disassembler for Java class files +; (Jasmin syntax 2, http://jasmin.sourceforge.net) +; +; ClassFileAnalyzer, version 0.7.0 + +.bytecode 52.0 +.source ClassAttrs.java +.class final ClassAttrs$1 +.super OtherClass +.enclosing method ClassAttrs/main()V +; OpenJDK javac versions <= 8 consider anonymous classes declared side +; static methods to be static (as is this one), whereas OpenJDK 9 javac +; does not. See http://b/62290080 +.inner class static inner ClassAttrs$1 ; + +.field i I + +.method ()V + .limit stack 2 + .limit locals 1 + .line 112 + 0: aload_0 + 1: invokespecial OtherClass/()V + 4: aload_0 + 5: iconst_5 + 6: putfield ClassAttrs$1/i I + 9: return +.end method + + diff --git a/test/031-class-attributes/src/ClassAttrs.java b/test/031-class-attributes/src/ClassAttrs.java index 8489a2c222a23f6e019e9a8d320a5a0ed8c827c1..f55a34c5f2558c6c228bf7efe45cd50752256aa9 100644 --- a/test/031-class-attributes/src/ClassAttrs.java +++ b/test/031-class-attributes/src/ClassAttrs.java @@ -107,9 +107,14 @@ public class ClassAttrs { inner.showMe(); ClassAttrs attrs = new ClassAttrs(); - - /* anonymous, not local, not member */ - printClassAttrs((new OtherClass() { int i = 5; }).getClass()); + try { + /* anonymous, not local, not member */ + printClassAttrs(Class.forName("ClassAttrs$1")); // ClassAttrs$1.j + } catch (ClassNotFoundException e) { + System.out.println("FAILED: " + e); + e.printStackTrace(System.out); + throw new AssertionError(e); + } /* member, not anonymous, not local */ printClassAttrs(MemberClass.class); diff --git a/test/044-proxy/native_proxy.cc b/test/044-proxy/native_proxy.cc index f168719bf515435224e7e8120162015c24c6c2fa..f3178f9c2a89f5027216438b293e4c6427633cae 100644 --- a/test/044-proxy/native_proxy.cc +++ b/test/044-proxy/native_proxy.cc @@ -16,7 +16,7 @@ #include "jni.h" -#include "base/logging.h" +#include namespace art { diff --git a/test/070-nio-buffer/src/Main.java b/test/070-nio-buffer/src/Main.java index a3eeb3fda68090a389ce260217630e0d911f523b..86eb5535949e0d124ce80ed50eb10732fcfd7aca 100644 --- a/test/070-nio-buffer/src/Main.java +++ b/test/070-nio-buffer/src/Main.java @@ -14,6 +14,7 @@ * limitations under the License. */ +import java.nio.Buffer; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -50,9 +51,9 @@ public class Main { 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031 }; - shortBuf.position(0); + ((Buffer) shortBuf).position(0); shortBuf.put(myShorts, 0, 32); // should work - shortBuf.position(0); + ((Buffer) shortBuf).position(0); shortBuf.put(myShorts, 16, 16); // should work shortBuf.put(myShorts, 16, 16); // advance to end @@ -64,7 +65,7 @@ public class Main { } try { - shortBuf.position(0); + ((Buffer) shortBuf).position(0); shortBuf.put(myShorts, 0, 33); // should fail System.out.println("ERROR: out-of-bounds put succeeded\n"); } catch (IndexOutOfBoundsException ioobe) { @@ -72,7 +73,7 @@ public class Main { } try { - shortBuf.position(16); + ((Buffer) shortBuf).position(16); shortBuf.put(myShorts, 0, 17); // should fail System.out.println("ERROR: out-of-bounds put succeeded\n"); } catch (BufferOverflowException boe) { @@ -93,13 +94,13 @@ public class Main { int data[] = new int[25]; //FloatBuffer int1 = direct.asFloatBuffer(); //float data[] = new float[25]; - int1.clear (); - int1.put (data); - int1.position (0); + ((Buffer) int1).clear(); + int1.put(data); + ((Buffer) int1).position(0); - int1.clear (); + ((Buffer) int1).clear(); int1.put (data); - int1.position (0); + ((Buffer) int1).position(0); } /* @@ -119,7 +120,7 @@ public class Main { } static void storeValues(ByteBuffer directBuf) { - directBuf.position(0); + ((Buffer) directBuf).position(0); ShortBuffer shortBuf = directBuf.asShortBuffer(); CharBuffer charBuf = directBuf.asCharBuffer(); IntBuffer intBuf = directBuf.asIntBuffer(); @@ -157,7 +158,7 @@ public class Main { throw new RuntimeException("double get/store failed"); } - directBuf.position(0); + ((Buffer) directBuf).position(0); char[] outBuf = new char[directBuf.limit() * 2]; for (int i = 0; i < directBuf.limit(); i++) { byte b = directBuf.get(); diff --git a/test/071-dexfile-get-static-size/build b/test/071-dexfile-get-static-size/build new file mode 100755 index 0000000000000000000000000000000000000000..0bba66d065e80875df4bd8fa5df1e4ecf90835e2 --- /dev/null +++ b/test/071-dexfile-get-static-size/build @@ -0,0 +1,30 @@ +#!/bin/bash +# +# Copyright 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. + +./default-build "$@" + +# Create and add as resources to the test jar file: +# 1. test1.dex +# 2. test2.dex +# 3. test-jar.jar, containing test1.dex as classes.dex +# 4. multi-jar.jar, containing test1.dex as classes.dex and test2.dex as classes2.dex +mkdir test-jar +cp test1.dex test-jar/classes.dex +cp test2.dex test-jar/classes2.dex +zip -j test-jar.jar test-jar/classes.dex +zip -j multi-jar.jar test-jar/classes.dex test-jar/classes2.dex +jar uf ${TEST_NAME}.jar test1.dex test2.dex test-jar.jar multi-jar.jar + diff --git a/test/071-dexfile-get-static-size/expected.txt b/test/071-dexfile-get-static-size/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..dfb77c3a2f95dab47ce07972aa6e5a9d37d4679f --- /dev/null +++ b/test/071-dexfile-get-static-size/expected.txt @@ -0,0 +1,4 @@ +Size for test1.dex: 1864 +Size for test2.dex: 1264 +Size for test-jar.jar: 1864 +Size for multi-jar.jar: 3128 diff --git a/test/071-dexfile-get-static-size/info.txt b/test/071-dexfile-get-static-size/info.txt new file mode 100644 index 0000000000000000000000000000000000000000..5b528e81b4ec40ab241f3bfdf8bcf7083a5eb018 --- /dev/null +++ b/test/071-dexfile-get-static-size/info.txt @@ -0,0 +1,3 @@ +Test DexFile.getStaticSizeOfDexFile API. + +test1.dex and test2.dex are arbitrary valid dex files. diff --git a/test/071-dexfile-get-static-size/src/Main.java b/test/071-dexfile-get-static-size/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..4bf453801e0ebc2a3bcdd41e7b980f21ca574f1e --- /dev/null +++ b/test/071-dexfile-get-static-size/src/Main.java @@ -0,0 +1,62 @@ +/* + * 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. + */ + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.FileOutputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +public class Main { + private static void extractResource(String resource, String filename) throws Exception { + ClassLoader loader = Main.class.getClassLoader(); + InputStream is = loader.getResourceAsStream(resource); + OutputStream os = new FileOutputStream(filename); + int read; + byte[] buf = new byte[4096]; + while ((read = is.read(buf)) >= 0) { + os.write(buf, 0, read); + } + is.close(); + os.close(); + } + + private static long getDexFileSize(String filename) throws Exception { + ClassLoader loader = Main.class.getClassLoader(); + Class DexFile = loader.loadClass("dalvik.system.DexFile"); + Method DexFile_loadDex = DexFile.getMethod("loadDex", + String.class, + String.class, + Integer.TYPE); + Method DexFile_getStaticSizeOfDexFile = DexFile.getMethod("getStaticSizeOfDexFile"); + Object dexFile = DexFile_loadDex.invoke(null, filename, null, 0); + return (Long) DexFile_getStaticSizeOfDexFile.invoke(dexFile); + } + + private static void test(String resource) throws Exception { + String filename = System.getenv("DEX_LOCATION") + "/" + resource; + extractResource(resource, filename); + long size = getDexFileSize(filename); + System.out.println("Size for " + resource + ": " + size); + } + + public static void main(String[] args) throws Exception { + test("test1.dex"); + test("test2.dex"); + test("test-jar.jar"); + test("multi-jar.jar"); + } +} diff --git a/test/071-dexfile-get-static-size/test1.dex b/test/071-dexfile-get-static-size/test1.dex new file mode 100644 index 0000000000000000000000000000000000000000..84602d03c2c1692d5d5a77f8740732641c38933b Binary files /dev/null and b/test/071-dexfile-get-static-size/test1.dex differ diff --git a/test/071-dexfile-get-static-size/test2.dex b/test/071-dexfile-get-static-size/test2.dex new file mode 100644 index 0000000000000000000000000000000000000000..a07c46ef592d90bf9247720be434540006f25fea Binary files /dev/null and b/test/071-dexfile-get-static-size/test2.dex differ diff --git a/test/085-old-style-inner-class/build b/test/085-old-style-inner-class/build deleted file mode 100644 index 21dc66269d7030e64e33ac10164d5aa6a6577b49..0000000000000000000000000000000000000000 --- a/test/085-old-style-inner-class/build +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2010 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. - -# Stop if something fails. -set -e - -# We compile for a 1.4 target to suppress the use of EnclosingMethod -# attributes. -mkdir classes -${JAVAC} -source 1.4 -target 1.4 -d classes `find src -name '*.java'` - -if [ ${USE_JACK} = "true" ]; then - jar cf classes.jill.jar -C classes . - ${JACK} --import classes.jill.jar --output-dex . -else - # Suppress stderr to keep the inner class warnings out of the expected output. - ${DX} --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes 2>/dev/null -fi - -zip $TEST_NAME.jar classes.dex diff --git a/test/085-old-style-inner-class/jasmin/Main$1.j b/test/085-old-style-inner-class/jasmin/Main$1.j new file mode 100644 index 0000000000000000000000000000000000000000..fde1ddea53a2ef4c1c6f2db779ab528424b61790 --- /dev/null +++ b/test/085-old-style-inner-class/jasmin/Main$1.j @@ -0,0 +1,39 @@ +; 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. + +.source Main.java +.class final Main$1 +.super java/lang/Object +.implements java/lang/Runnable + +; new Runnable() { +; public void run() { } +; }; + +.method ()V + .limit stack 1 + .limit locals 1 + .line 23 + aload_0 + invokespecial java/lang/Object/()V + return +.end method + +.method public run()V + .limit stack 0 + .limit locals 1 + .line 24 + return +.end method + diff --git a/test/085-old-style-inner-class/jasmin/Main$2.j b/test/085-old-style-inner-class/jasmin/Main$2.j new file mode 100644 index 0000000000000000000000000000000000000000..dedbe8682e882582cc0162c1ca134837bef5ade5 --- /dev/null +++ b/test/085-old-style-inner-class/jasmin/Main$2.j @@ -0,0 +1,39 @@ +; 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. + +.source Main.java +.class final Main$2 +.super java/lang/Object +.implements java/lang/Runnable + +; new Runnable() { +; public void run() { } +; }; + +.method ()V + .limit stack 1 + .limit locals 1 + .line 28 + aload_0 + invokespecial java/lang/Object/()V + return +.end method + +.method public run()V + .limit stack 0 + .limit locals 1 + .line 29 + return +.end method + diff --git a/test/085-old-style-inner-class/src/Main.java b/test/085-old-style-inner-class/src/Main.java index c9a5b72dbd91408a5cfce7a5eaa142ed652f31cb..831364cae1f59be07e18622826bbccc311c43d92 100644 --- a/test/085-old-style-inner-class/src/Main.java +++ b/test/085-old-style-inner-class/src/Main.java @@ -20,15 +20,19 @@ import java.lang.reflect.Method; * Test reflection on old-style inner classes. */ public class Main { + /* + // Main$1.j private static Runnable theRunnable = new Runnable() { public void run() { } }; + // Main$1.2 private static Runnable create() { return new Runnable() { public void run() { } }; } + */ private static String nameOf(Class clazz) { return (clazz == null) ? "(null)" : clazz.getName(); @@ -48,8 +52,8 @@ public class Main { nameOf(clazz.getEnclosingMethod())); } - public static void main(String args[]) { - infoFor(theRunnable.getClass()); - infoFor(create().getClass()); + public static void main(String args[]) throws ClassNotFoundException { + infoFor(Class.forName("Main$1")); + infoFor(Class.forName("Main$2")); } } diff --git a/test/099-vmdebug/expected.txt b/test/099-vmdebug/expected.txt index b8d72f66f825b45dbc877f961feda8b3a07b3fc1..f7801de62f1f74b8f9a7f0a2dc4045b21c6727d3 100644 --- a/test/099-vmdebug/expected.txt +++ b/test/099-vmdebug/expected.txt @@ -23,3 +23,9 @@ Instances of null 0 Instances of ClassA assignable 3 Array counts [2, 1, 0] Array counts assignable [3, 1, 0] +ClassD got 3, combined mask: 13 +ClassE got 2, combined mask: 18 +null got 0 +ClassD assignable got 5, combined mask: 31 +ClassE assignable got 2, combined mask: 18 +null assignable got 0 diff --git a/test/099-vmdebug/info.txt b/test/099-vmdebug/info.txt index 7f88086986174c393863a469e851b3fc274017f4..873429e07613d61bfbc7bae52f6a07c44573c977 100644 --- a/test/099-vmdebug/info.txt +++ b/test/099-vmdebug/info.txt @@ -1 +1 @@ -Tests of private dalvik.system.VMDebug support for method tracing. +Tests of dalvik.system.VMDebug APIs. diff --git a/test/099-vmdebug/src/Main.java b/test/099-vmdebug/src/Main.java index 90ad3155cab74823f57fe968d0efabd1540df7d2..e0d829a0d6aca34a184eb0b6ca1df8b229847fa9 100644 --- a/test/099-vmdebug/src/Main.java +++ b/test/099-vmdebug/src/Main.java @@ -33,6 +33,7 @@ public class Main { } testMethodTracing(); testCountInstances(); + testGetInstances(); testRuntimeStat(); testRuntimeStats(); } @@ -249,6 +250,59 @@ public class Main { System.out.println("Array counts assignable " + Arrays.toString(counts)); } + static class ClassD { + public int mask; + + public ClassD(int mask) { + this.mask = mask; + } + } + + static class ClassE extends ClassD { + public ClassE(int mask) { + super(mask); + } + } + + private static void testGetInstances() throws Exception { + ArrayList l = new ArrayList(); + l.add(new ClassD(0x01)); + l.add(new ClassE(0x02)); + l.add(new ClassD(0x04)); + l.add(new ClassD(0x08)); + l.add(new ClassE(0x10)); + Runtime.getRuntime().gc(); + Class[] classes = new Class[] {ClassD.class, ClassE.class, null}; + Object[][] instances = VMDebug.getInstancesOfClasses(classes, false); + + int mask = 0; + for (Object instance : instances[0]) { + mask |= ((ClassD)instance).mask; + } + System.out.println("ClassD got " + instances[0].length + ", combined mask: " + mask); + + mask = 0; + for (Object instance : instances[1]) { + mask |= ((ClassD)instance).mask; + } + System.out.println("ClassE got " + instances[1].length + ", combined mask: " + mask); + System.out.println("null got " + instances[2].length); + + instances = VMDebug.getInstancesOfClasses(classes, true); + mask = 0; + for (Object instance : instances[0]) { + mask |= ((ClassD)instance).mask; + } + System.out.println("ClassD assignable got " + instances[0].length + ", combined mask: " + mask); + + mask = 0; + for (Object instance : instances[1]) { + mask |= ((ClassD)instance).mask; + } + System.out.println("ClassE assignable got " + instances[1].length + ", combined mask: " + mask); + System.out.println("null assignable got " + instances[2].length); + } + private static class VMDebug { private static final Method startMethodTracingMethod; private static final Method stopMethodTracingMethod; @@ -257,6 +311,7 @@ public class Main { private static final Method getRuntimeStatsMethod; private static final Method countInstancesOfClassMethod; private static final Method countInstancesOfClassesMethod; + private static final Method getInstancesOfClassesMethod; static { try { Class c = Class.forName("dalvik.system.VMDebug"); @@ -270,6 +325,8 @@ public class Main { Class.class, Boolean.TYPE); countInstancesOfClassesMethod = c.getDeclaredMethod("countInstancesOfClasses", Class[].class, Boolean.TYPE); + getInstancesOfClassesMethod = c.getDeclaredMethod("getInstancesOfClasses", + Class[].class, Boolean.TYPE); } catch (Exception e) { throw new RuntimeException(e); } @@ -300,5 +357,9 @@ public class Main { return (long[]) countInstancesOfClassesMethod.invoke( null, new Object[]{classes, assignable}); } + public static Object[][] getInstancesOfClasses(Class[] classes, boolean assignable) throws Exception { + return (Object[][]) getInstancesOfClassesMethod.invoke( + null, new Object[]{classes, assignable}); + } } } diff --git a/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc b/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc index 7d40f5773de8b254f799829a97c2f24ad08ad9d4..f01b82553dab3ea5c929c25e05625e0355b6866d 100644 --- a/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc +++ b/test/136-daemon-jni-shutdown/daemon_jni_shutdown.cc @@ -58,7 +58,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_destroyJavaVMAndExit(JNIEnv* env, jc Thread* const self = Thread::Current(); self->SetTopOfStack(nullptr); self->SetTopOfShadowStack(nullptr); - JavaVM* vm = down_cast(env)->vm; + JavaVM* vm = down_cast(env)->GetVm(); vm->DetachCurrentThread(); // Open ourself again to make sure the native library does not get unloaded from // underneath us due to DestroyJavaVM. b/28406866 diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc index 58b33be57356782e6bc8f4d5350d28e490f67ba4..ef758e86e136347cd9dee942e866d58dad853a8f 100644 --- a/test/137-cfi/cfi.cc +++ b/test/137-cfi/cfi.cc @@ -25,11 +25,11 @@ #include "jni.h" +#include +#include #include -#include "android-base/stringprintf.h" #include "base/file_utils.h" -#include "base/logging.h" #include "base/macros.h" #include "gc/heap.h" #include "gc/space/image_space.h" @@ -104,20 +104,6 @@ static void MoreErrorInfo(pid_t pid, bool sig_quit_on_fail) { } #endif -// Currently we have to fall back to our own loader for the boot image when it's compiled PIC -// because its base is zero. Thus in-process unwinding through it won't work. This is a helper -// detecting this. -#if __linux__ -static bool IsPicImage() { - std::vector image_spaces = - Runtime::Current()->GetHeap()->GetBootImageSpaces(); - CHECK(!image_spaces.empty()); // We should be running with an image. - const OatFile* oat_file = image_spaces[0]->GetOatFile(); - CHECK(oat_file != nullptr); // We should have an oat file to go with the image. - return oat_file->IsPic(); -} -#endif - extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess( JNIEnv*, jobject, @@ -125,11 +111,6 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess( jint, jboolean) { #if __linux__ - if (IsPicImage()) { - LOG(INFO) << "Image is pic, in-process unwinding check bypassed."; - return JNI_TRUE; - } - // TODO: What to do on Valgrind? std::unique_ptr bt(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, GetTid())); diff --git a/test/137-cfi/run b/test/137-cfi/run index ebc729bc7435f55919fd37ea6fd5b8ca05755b3f..adea71a07fa4c924565374b22a121e7218609c30 100755 --- a/test/137-cfi/run +++ b/test/137-cfi/run @@ -16,10 +16,15 @@ # Test with full DWARF debugging information. # Check full signatures of methods. +# The option jitthreshold:0 ensures that if we run the test in JIT mode, +# there will be JITed frames on the callstack (it synchronously JITs on first use). ${RUN} "$@" -Xcompiler-option --generate-debug-info \ + --runtime-option -Xjitthreshold:0 \ --args --full-signatures --args --test-local --args --test-remote # Test with minimal compressed debugging information. # Check only method names (parameters are omitted to save space). # Check only remote unwinding since decompression is disabled in local unwinds (b/27391690). -${RUN} "$@" -Xcompiler-option --generate-mini-debug-info --args --test-remote +${RUN} "$@" -Xcompiler-option --generate-mini-debug-info \ + --runtime-option -Xjitthreshold:0 \ + --args --test-remote diff --git a/test/163-app-image-methods/src/Main.java b/test/163-app-image-methods/src/Main.java index a995bb84127f9ec160cd9076426dddc82a996936..c513470b7b18710759a4519e53bc136d0fe53b85 100644 --- a/test/163-app-image-methods/src/Main.java +++ b/test/163-app-image-methods/src/Main.java @@ -22,6 +22,9 @@ public class Main { // Allocate memory for the "AAA.Derived" class name before eating memory. String aaaDerivedName = "AAA.Derived"; System.out.println("Eating all memory."); + // Resolve VMClassLoader before eating all the memory since we can not fail + // initializtaion of boot classpath classes. + Class.forName("java.lang.VMClassLoader"); Object memory = eatAllMemory(); // This test assumes that Derived is not yet resolved. In some configurations diff --git a/test/165-lock-owner-proxy/src/Main.java b/test/165-lock-owner-proxy/src/Main.java index 1b1694d2c15ea89ec9d13a47521fbf92bf10eeb8..fff8e5860686fa001f3e40262d7dba93cfd8a388 100644 --- a/test/165-lock-owner-proxy/src/Main.java +++ b/test/165-lock-owner-proxy/src/Main.java @@ -65,8 +65,13 @@ public class Main { int count = totalOperations; while (count > 0) { synchronized (lockObject) { - inf.foo(10000 - count, 11000 - count, 12000 - count, 13000 - count, - 14000 - count, 15000 - count); + try { + inf.foo(10000 - count, 11000 - count, 12000 - count, 13000 - count, + 14000 - count, 15000 - count); + } catch (OutOfMemoryError e) { + // Ignore errors. This is the test for b/69121347 - see an exception + // instead of native abort. + } } count--; } @@ -96,7 +101,12 @@ public class Main { while (!finish) { // Some random allocations adding up to almost 2M. for (int i = 0; i < 188; i++) { - byte b[] = new byte[i * 100 + 10]; + try { + byte b[] = new byte[i * 100 + 10]; + } catch (OutOfMemoryError e) { + // Ignore. This is just to improve chances that an OOME is thrown during + // proxy invocation. + } } try { Thread.sleep(10); diff --git a/test/166-bad-interface-super/expected.txt b/test/166-bad-interface-super/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..c49f6d255f6863c5f8efb2b3276526223ebb4a1a --- /dev/null +++ b/test/166-bad-interface-super/expected.txt @@ -0,0 +1,2 @@ +Caught java.lang.ClassFormatError when trying to resolve BadSuper1. +Caught java.lang.ClassFormatError when trying to resolve BadSuper2. diff --git a/test/166-bad-interface-super/info.txt b/test/166-bad-interface-super/info.txt new file mode 100644 index 0000000000000000000000000000000000000000..bcba8c0d895a2a97b1932b4a08e4cba5874da588 --- /dev/null +++ b/test/166-bad-interface-super/info.txt @@ -0,0 +1 @@ +Test that linking an interface declaring a superclass other than j.l.Object throws CFE. diff --git a/test/166-bad-interface-super/jasmin/BadSuper1.j b/test/166-bad-interface-super/jasmin/BadSuper1.j new file mode 100644 index 0000000000000000000000000000000000000000..f96564ec73e31b10fd7c74477100949da812fd61 --- /dev/null +++ b/test/166-bad-interface-super/jasmin/BadSuper1.j @@ -0,0 +1,17 @@ +; 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. + +.interface public BadSuper1 +.super BaseInterface + diff --git a/test/166-bad-interface-super/jasmin/BadSuper2.j b/test/166-bad-interface-super/jasmin/BadSuper2.j new file mode 100644 index 0000000000000000000000000000000000000000..584bd2094df3aba76a3cd6f8eabcc40032f6aad8 --- /dev/null +++ b/test/166-bad-interface-super/jasmin/BadSuper2.j @@ -0,0 +1,17 @@ +; 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. + +.interface public BadSuper2 +.super BaseClass + diff --git a/test/166-bad-interface-super/src/BaseClass.java b/test/166-bad-interface-super/src/BaseClass.java new file mode 100644 index 0000000000000000000000000000000000000000..6ea1ad3f3bac46f057bdea104c5938690dbcb811 --- /dev/null +++ b/test/166-bad-interface-super/src/BaseClass.java @@ -0,0 +1,18 @@ +/* + * 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. + */ + +class BaseClass { +} diff --git a/test/166-bad-interface-super/src/BaseInterface.java b/test/166-bad-interface-super/src/BaseInterface.java new file mode 100644 index 0000000000000000000000000000000000000000..7872a43794d58d8593d79a9d93ce2296e768a762 --- /dev/null +++ b/test/166-bad-interface-super/src/BaseInterface.java @@ -0,0 +1,18 @@ +/* + * 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. + */ + +interface BaseInterface { +} diff --git a/test/166-bad-interface-super/src/Main.java b/test/166-bad-interface-super/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..3df257467831aef699e694faa41aa6c7668d84fd --- /dev/null +++ b/test/166-bad-interface-super/src/Main.java @@ -0,0 +1,31 @@ +/* + * 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. + */ + +class Main { + public static void main(String[] args) throws Exception { + tryResolveClassExpectingCFE("BadSuper1"); + tryResolveClassExpectingCFE("BadSuper2"); + } + + public static void tryResolveClassExpectingCFE(String className) throws Exception { + try { + Class.forName(className); + } catch (ClassFormatError e) { + System.out.println( + "Caught " + e.getClass().getName() + " when trying to resolve " + className + "."); + } + } +} diff --git a/test/167-visit-locks/expected.txt b/test/167-visit-locks/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..5157c64c8398208ba0ff96161d080ad46eb2fc20 --- /dev/null +++ b/test/167-visit-locks/expected.txt @@ -0,0 +1,3 @@ +JNI_OnLoad called +First +Second diff --git a/test/167-visit-locks/info.txt b/test/167-visit-locks/info.txt new file mode 100644 index 0000000000000000000000000000000000000000..d849bc31ed4020a9965f6625af2338b1eb168b67 --- /dev/null +++ b/test/167-visit-locks/info.txt @@ -0,0 +1 @@ +Regression test for b/68703210 diff --git a/test/706-jit-skip-compilation/run b/test/167-visit-locks/run similarity index 71% rename from test/706-jit-skip-compilation/run rename to test/167-visit-locks/run index 6c5720a099eb59fb45d5e4d969d80f90296a8957..93654113e6b8345e2a495207cd9d1e9443367c55 100644 --- a/test/706-jit-skip-compilation/run +++ b/test/167-visit-locks/run @@ -1,6 +1,6 @@ #!/bin/bash # -# Copyright (C) 2016 The Android Open Source Project +# 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. @@ -14,6 +14,5 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Run without the app image, otherwise the verification results will be cached -# in the ArtMethod of the image and the test will be skewed. -exec ${RUN} "${@}" --no-app-image +# Use a smaller heap so it's easier to potentially fill up. +exec ${RUN} $@ --runtime-option -Xmx2m diff --git a/test/167-visit-locks/smali/TestSync.smali b/test/167-visit-locks/smali/TestSync.smali new file mode 100644 index 0000000000000000000000000000000000000000..5e68ad70035e3d46765fb83c55da171c02373243 --- /dev/null +++ b/test/167-visit-locks/smali/TestSync.smali @@ -0,0 +1,119 @@ +.class LTestSync; +.super Ljava/lang/Object; +.source "Main.java" + + +# direct methods +.method constructor ()V + .registers 1 + + .prologue + .line 6 + invoke-direct {p0}, Ljava/lang/Object;->()V + + return-void +.end method + +.method public static run()V + # v0-v2 were generated by javac+dx for the original src code, keeping them. + # v10..v19 are for tracking, aliasing and manipulating the first lock. + # v20..v29 are for tracking, aliasing and manipulating the second lock. + .registers 30 + + .prologue + .line 8 + const-string v1, "First" + + .line 9 + const-string v2, "Second" + + move-object v10, v1 + const v1, 0x1 + + .line 10 + monitor-enter v10 + + # Introduce a range of dead copies. + move-object v11, v10 + move-object v12, v10 + move-object v13, v10 + move-object v14, v10 + move-object v15, v10 + move-object/16 v16, v10 + move-object/16 v17, v10 + move-object/16 v18, v10 + + # Introduce a copy that we'll use for unlock. + move-object/16 v19, v10 + + # Clobber the original alias. + const v10, 0x3 + + move-object/16 v20, v2 + const v2, 0x2 + + .line 11 + :try_start_b + monitor-enter v20 + :try_end_c + + # Introduce a range of dead copies. + move-object/16 v21, v20 + move-object/16 v22, v20 + move-object/16 v23, v20 + move-object/16 v24, v20 + move-object/16 v25, v20 + move-object/16 v26, v20 + move-object/16 v27, v20 + + # Introduce another copy that we will hold live. + move-object/16 v28, v20 + + # Clobber the original alias. + const v20, 0x5 + + # Introduce another copy that we'll use for unlock. + move-object/16 v29, v28 + + .catchall {:try_start_b .. :try_end_c} :catchall_15 + + .line 12 + :try_start_c + invoke-static/range { v28 }, LMain;->run(Ljava/lang/Object;)V + + .line 13 + monitor-exit v29 + :try_end_10 + .catchall {:try_start_c .. :try_end_10} :catchall_12 + + .line 14 + :try_start_10 + monitor-exit v19 + :try_end_11 + .catchall {:try_start_10 .. :try_end_11} :catchall_15 + + .line 15 + return-void + + .line 13 + :catchall_12 + move-exception v0 + + :try_start_13 + monitor-exit v29 + :try_end_14 + .catchall {:try_start_13 .. :try_end_14} :catchall_12 + + :try_start_14 + throw v0 + + .line 14 + :catchall_15 + move-exception v0 + + monitor-exit v19 + :try_end_17 + .catchall {:try_start_14 .. :try_end_17} :catchall_15 + + throw v0 +.end method diff --git a/test/167-visit-locks/src/Main.java b/test/167-visit-locks/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..d8da92715aa4eaf17842cbd7d06c009ee163bcb8 --- /dev/null +++ b/test/167-visit-locks/src/Main.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public static void main(String[] args) throws Exception { + System.loadLibrary(args[0]); + + Class.forName("TestSync").getMethod("run").invoke(null); + } + + public static void run(Object o) { + testVisitLocks(); + } + + public static native void testVisitLocks(); +} diff --git a/test/167-visit-locks/visit_locks.cc b/test/167-visit-locks/visit_locks.cc new file mode 100644 index 0000000000000000000000000000000000000000..e79c8806399653d98a58a724402203336620471d --- /dev/null +++ b/test/167-visit-locks/visit_locks.cc @@ -0,0 +1,74 @@ +/* + * 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 "jni.h" + +#include + +#include "android-base/logging.h" + +#include "arch/context.h" +#include "art_method.h" +#include "base/macros.h" +#include "base/mutex.h" +#include "mirror/object-inl.h" +#include "mirror/string.h" +#include "monitor.h" +#include "scoped_thread_state_change-inl.h" +#include "stack.h" +#include "thread-current-inl.h" + +namespace art { + +extern "C" JNIEXPORT void JNICALL Java_Main_testVisitLocks(JNIEnv*, jclass) { + ScopedObjectAccess soa(Thread::Current()); + + class VisitLocks : public StackVisitor { + public: + VisitLocks(Thread* thread, Context* context) + : StackVisitor(thread, context, StackWalkKind::kIncludeInlinedFrames) { + } + + bool VisitFrame() OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* m = GetMethod(); + + // Ignore runtime methods. + if (m == nullptr || m->IsRuntimeMethod()) { + return true; + } + + if (m->PrettyMethod() == "void TestSync.run()") { + // Interesting frame. + Monitor::VisitLocks(this, Callback, nullptr); + return false; + } + + return true; + } + + static void Callback(mirror::Object* obj, void*) REQUIRES_SHARED(Locks::mutator_lock_) { + CHECK(obj != nullptr); + CHECK(obj->IsString()); + std::cerr << obj->AsString()->ToModifiedUtf8() << std::endl; + } + }; + Context* context = Context::Create(); + VisitLocks vl(soa.Self(), context); + vl.WalkStack(); + delete context; +} + +} // namespace art diff --git a/test/1919-vminit-thread-start-timing/vminit.cc b/test/1919-vminit-thread-start-timing/vminit.cc index c492e8bb6217d29f698d4293355a1890bdec22b2..109c61f05cbf19db966277b9f33e694fb5b8468b 100644 --- a/test/1919-vminit-thread-start-timing/vminit.cc +++ b/test/1919-vminit-thread-start-timing/vminit.cc @@ -16,8 +16,8 @@ #include "1919-vminit-thread-start-timing/vminit.h" -#include // NOLINT [build/c++11] [5] -#include // NOLINT [build/c++11] [5] +#include +#include #include #include diff --git a/test/1940-ddms-ext/check b/test/1940-ddms-ext/check new file mode 100755 index 0000000000000000000000000000000000000000..d2c03841fc9b16ef113d2b9e4add2eece239c693 --- /dev/null +++ b/test/1940-ddms-ext/check @@ -0,0 +1,21 @@ +#!/bin/bash +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Need to pull out the describeException ouput since that won't be there on +# device. +sed -e '/\t.*$/d' "$2" | sed -e '/java.lang.ArrayIndexOutOfBoundsException:.*$/d' > "$2.tmp" + +./default-check "$1" "$2.tmp" diff --git a/test/1940-ddms-ext/ddm_ext.cc b/test/1940-ddms-ext/ddm_ext.cc new file mode 100644 index 0000000000000000000000000000000000000000..cc29df9a49d7f45ef5abd72497f63503458896ed --- /dev/null +++ b/test/1940-ddms-ext/ddm_ext.cc @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2013 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 "jvmti.h" + +// Test infrastructure +#include "jvmti_helper.h" +#include "nativehelper/scoped_local_ref.h" +#include "nativehelper/scoped_primitive_array.h" +#include "test_env.h" + +namespace art { +namespace Test1940DdmExt { + +typedef jvmtiError (*DdmHandleChunk)(jvmtiEnv* env, + jint type_in, + jint len_in, + const jbyte* data_in, + jint* type_out, + jint* len_data_out, + jbyte** data_out); + +struct DdmsTrackingData { + DdmHandleChunk send_ddm_chunk; + jclass test_klass; + jmethodID publish_method; +}; + +template +static void Dealloc(T* t) { + jvmti_env->Deallocate(reinterpret_cast(t)); +} + +template +static void Dealloc(T* t, Rest... rs) { + Dealloc(t); + Dealloc(rs...); +} + +extern "C" JNIEXPORT jobject JNICALL Java_art_Test1940_processChunk(JNIEnv* env, + jclass, + jobject chunk) { + DdmsTrackingData* data = nullptr; + if (JvmtiErrorToException( + env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast(&data)))) { + return nullptr; + } + CHECK(chunk != nullptr); + CHECK(data != nullptr); + CHECK(data->send_ddm_chunk != nullptr); + ScopedLocalRef chunk_class(env, env->FindClass("org/apache/harmony/dalvik/ddmc/Chunk")); + if (env->ExceptionCheck()) { + return nullptr; + } + jfieldID type_field_id = env->GetFieldID(chunk_class.get(), "type", "I"); + jfieldID offset_field_id = env->GetFieldID(chunk_class.get(), "offset", "I"); + jfieldID length_field_id = env->GetFieldID(chunk_class.get(), "length", "I"); + jfieldID data_field_id = env->GetFieldID(chunk_class.get(), "data", "[B"); + jint type = env->GetIntField(chunk, type_field_id); + jint off = env->GetIntField(chunk, offset_field_id); + jint len = env->GetIntField(chunk, length_field_id); + ScopedLocalRef chunk_buf( + env, reinterpret_cast(env->GetObjectField(chunk, data_field_id))); + if (env->ExceptionCheck()) { + return nullptr; + } + ScopedByteArrayRO byte_data(env, chunk_buf.get()); + jint out_type; + jint out_size; + jbyte* out_data; + if (JvmtiErrorToException(env, jvmti_env, data->send_ddm_chunk(jvmti_env, + type, + len, + &byte_data[off], + /*out*/&out_type, + /*out*/&out_size, + /*out*/&out_data))) { + return nullptr; + } else { + ScopedLocalRef chunk_data(env, env->NewByteArray(out_size)); + env->SetByteArrayRegion(chunk_data.get(), 0, out_size, out_data); + Dealloc(out_data); + ScopedLocalRef res(env, env->NewObject(chunk_class.get(), + env->GetMethodID(chunk_class.get(), + "", + "(I[BII)V"), + out_type, + chunk_data.get(), + 0, + out_size)); + return res.release(); + } +} + +static void DeallocParams(jvmtiParamInfo* params, jint n_params) { + for (jint i = 0; i < n_params; i++) { + Dealloc(params[i].name); + } +} + +static void JNICALL PublishCB(jvmtiEnv* jvmti, JNIEnv* jnienv, jint type, jint size, jbyte* bytes) { + DdmsTrackingData* data = nullptr; + if (JvmtiErrorToException(jnienv, jvmti, + jvmti->GetEnvironmentLocalStorage(reinterpret_cast(&data)))) { + return; + } + ScopedLocalRef res(jnienv, jnienv->NewByteArray(size)); + jnienv->SetByteArrayRegion(res.get(), 0, size, bytes); + jnienv->CallStaticVoidMethod(data->test_klass, data->publish_method, type, res.get()); +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test1940_initializeTest(JNIEnv* env, + jclass, + jclass method_klass, + jobject publish_method) { + void* old_data = nullptr; + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(&old_data))) { + return; + } else if (old_data != nullptr) { + ScopedLocalRef rt_exception(env, env->FindClass("java/lang/RuntimeException")); + env->ThrowNew(rt_exception.get(), "Environment already has local storage set!"); + return; + } + DdmsTrackingData* data = nullptr; + if (JvmtiErrorToException(env, + jvmti_env, + jvmti_env->Allocate(sizeof(DdmsTrackingData), + reinterpret_cast(&data)))) { + return; + } + memset(data, 0, sizeof(DdmsTrackingData)); + data->test_klass = reinterpret_cast(env->NewGlobalRef(method_klass)); + data->publish_method = env->FromReflectedMethod(publish_method); + if (env->ExceptionCheck()) { + return; + } + // Get the extensions. + jint n_ext = 0; + jvmtiExtensionFunctionInfo* infos = nullptr; + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetExtensionFunctions(&n_ext, &infos))) { + return; + } + for (jint i = 0; i < n_ext; i++) { + jvmtiExtensionFunctionInfo* cur_info = &infos[i]; + if (strcmp("com.android.art.internal.ddm.process_chunk", cur_info->id) == 0) { + data->send_ddm_chunk = reinterpret_cast(cur_info->func); + } + // Cleanup the cur_info + DeallocParams(cur_info->params, cur_info->param_count); + Dealloc(cur_info->id, cur_info->short_description, cur_info->params, cur_info->errors); + } + // Cleanup the array. + Dealloc(infos); + if (data->send_ddm_chunk == nullptr) { + ScopedLocalRef rt_exception(env, env->FindClass("java/lang/RuntimeException")); + env->ThrowNew(rt_exception.get(), "Unable to find memory tracking extensions."); + return; + } + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data))) { + return; + } + + jint event_index = -1; + bool found_event = false; + jvmtiExtensionEventInfo* events = nullptr; + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetExtensionEvents(&n_ext, &events))) { + return; + } + for (jint i = 0; i < n_ext; i++) { + jvmtiExtensionEventInfo* cur_info = &events[i]; + if (strcmp("com.android.art.internal.ddm.publish_chunk", cur_info->id) == 0) { + found_event = true; + event_index = cur_info->extension_event_index; + } + // Cleanup the cur_info + DeallocParams(cur_info->params, cur_info->param_count); + Dealloc(cur_info->id, cur_info->short_description, cur_info->params); + } + // Cleanup the array. + Dealloc(events); + if (!found_event) { + ScopedLocalRef rt_exception(env, env->FindClass("java/lang/RuntimeException")); + env->ThrowNew(rt_exception.get(), "Unable to find ddms extension event."); + return; + } + JvmtiErrorToException(env, + jvmti_env, + jvmti_env->SetExtensionEventCallback( + event_index, reinterpret_cast(PublishCB))); + return; +} + +} // namespace Test1940DdmExt +} // namespace art diff --git a/test/1940-ddms-ext/expected.txt b/test/1940-ddms-ext/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..1a457a01a5cb2e212497f426dc57b99f71b37dee --- /dev/null +++ b/test/1940-ddms-ext/expected.txt @@ -0,0 +1,21 @@ +Sending data [1, 2, 3, 4, 5, 6, 7, 8] +MyDdmHandler: Chunk received: Chunk(Type: 0xDEADBEEF, Len: 8, data: [1, 2, 3, 4, 5, 6, 7, 8]) +MyDdmHandler: Putting value 0x800025 +MyDdmHandler: Chunk returned: Chunk(Type: 0xFADE7357, Len: 8, data: [0, 0, 0, 0, 0, -128, 0, 37]) +JVMTI returned chunk: Chunk(Type: 0xFADE7357, Len: 8, data: [0, 0, 0, 0, 0, -128, 0, 37]) +Sending empty data array +MyDdmHandler: Chunk received: Chunk(Type: 0xDEADBEEF, Len: 0, data: []) +MyDdmHandler: Putting value 0x1 +MyDdmHandler: Chunk returned: Chunk(Type: 0xFADE7357, Len: 8, data: [0, 0, 0, 0, 0, 0, 0, 1]) +JVMTI returned chunk: Chunk(Type: 0xFADE7357, Len: 8, data: [0, 0, 0, 0, 0, 0, 0, 1]) +Sending chunk: Chunk(Type: 0xDEADBEEF, Len: 8, data: [9, 10, 11, 12, 13, 14, 15, 16]) +Chunk published: Chunk(Type: 0xDEADBEEF, Len: 8, data: [9, 10, 11, 12, 13, 14, 15, 16]) +Sending data [1] to chunk handler -1412567295 +MyDdmHandler: Chunk received: Chunk(Type: 0xABCDEF01, Len: 1, data: [1]) +JVMTI returned chunk: Chunk(Type: 0xFADE7357, Len: 0, data: []) +Sending data [1] to chunk handler 305419896 +MyDdmHandler: Chunk received: Chunk(Type: 0x12345678, Len: 1, data: [1]) +Got error: JVMTI_ERROR_INTERNAL +Saw expected thread events. +Expected chunk type published: 1213221190 +Expected chunk type published: 1297109829 diff --git a/test/1940-ddms-ext/info.txt b/test/1940-ddms-ext/info.txt new file mode 100644 index 0000000000000000000000000000000000000000..e1d35ae02677137edd9130d03efc349ba8c3dbbd --- /dev/null +++ b/test/1940-ddms-ext/info.txt @@ -0,0 +1 @@ +Tests the jvmti-extension to get allocated memory snapshot. diff --git a/test/1940-ddms-ext/run b/test/1940-ddms-ext/run new file mode 100755 index 0000000000000000000000000000000000000000..c6e62ae6cd61ce70e69ca620ad612f9b58738a68 --- /dev/null +++ b/test/1940-ddms-ext/run @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +./default-run "$@" --jvmti diff --git a/test/1940-ddms-ext/src-art/art/Test1940.java b/test/1940-ddms-ext/src-art/art/Test1940.java new file mode 100644 index 0000000000000000000000000000000000000000..226fe350bd525a7b492805a8f717335ee764af86 --- /dev/null +++ b/test/1940-ddms-ext/src-art/art/Test1940.java @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art; + +import org.apache.harmony.dalvik.ddmc.*; +import dalvik.system.VMDebug; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.zip.Adler32; +import java.nio.*; + +public class Test1940 { + public static final int DDMS_TYPE_INDEX = 0; + public static final int DDMS_LEN_INDEX = 4; + public static final int DDMS_HEADER_LENGTH = 8; + public static final int MY_DDMS_TYPE = 0xDEADBEEF; + public static final int MY_DDMS_RESPONSE_TYPE = 0xFADE7357; + public static final int MY_EMPTY_DDMS_TYPE = 0xABCDEF01; + public static final int MY_INVALID_DDMS_TYPE = 0x12345678; + + public static final boolean PRINT_ALL_CHUNKS = false; + + public static interface DdmHandler { + public void HandleChunk(int type, byte[] data); + } + + public static final class TestError extends Error { + public TestError(String s) { super(s); } + } + + private static void checkEq(Object a, Object b) { + if (!a.equals(b)) { + throw new TestError("Failure: " + a + " != " + b); + } + } + + private static String printChunk(Chunk k) { + byte[] out = new byte[k.length]; + System.arraycopy(k.data, k.offset, out, 0, k.length); + return String.format("Chunk(Type: 0x%X, Len: %d, data: %s)", + k.type, k.length, Arrays.toString(out)); + } + + private static final class MyDdmHandler extends ChunkHandler { + public void connected() {} + public void disconnected() {} + public Chunk handleChunk(Chunk req) { + System.out.println("MyDdmHandler: Chunk received: " + printChunk(req)); + if (req.type == MY_DDMS_TYPE) { + // For this test we will simply calculate the checksum + ByteBuffer b = ByteBuffer.wrap(new byte[8]); + Adler32 a = new Adler32(); + a.update(req.data, req.offset, req.length); + b.order(ByteOrder.BIG_ENDIAN); + long val = a.getValue(); + b.putLong(val); + System.out.printf("MyDdmHandler: Putting value 0x%X\n", val); + Chunk ret = new Chunk(MY_DDMS_RESPONSE_TYPE, b.array(), 0, 8); + System.out.println("MyDdmHandler: Chunk returned: " + printChunk(ret)); + return ret; + } else if (req.type == MY_EMPTY_DDMS_TYPE) { + return new Chunk(MY_DDMS_RESPONSE_TYPE, new byte[0], 0, 0); + } else if (req.type == MY_INVALID_DDMS_TYPE) { + // This is a very invalid chunk. + return new Chunk(MY_DDMS_RESPONSE_TYPE, new byte[] { 0 }, /*offset*/ 12, /*length*/ 55); + } else { + throw new TestError("Unknown ddm request type: " + req.type); + } + } + } + + public static final ChunkHandler SINGLE_HANDLER = new MyDdmHandler(); + + public static DdmHandler CURRENT_HANDLER; + + public static void HandlePublish(int type, byte[] data) { + if (PRINT_ALL_CHUNKS) { + System.out.println( + "Unknown Chunk published: " + printChunk(new Chunk(type, data, 0, data.length))); + } + CURRENT_HANDLER.HandleChunk(type, data); + } + + // TYPE Thread Create + public static final int TYPE_THCR = 0x54484352; + // Type Thread name + public static final int TYPE_THNM = 0x54484E4D; + // Type Thread death. + public static final int TYPE_THDE = 0x54484445; + // Type Heap info + public static final int TYPE_HPIF = 0x48504946; + // Type Trace Results + public static final int TYPE_MPSE = 0x4D505345; + + public static boolean IsFromThread(Thread t, byte[] data) { + // DDMS always puts the thread-id as the first 4 bytes. + ByteBuffer b = ByteBuffer.wrap(data); + b.order(ByteOrder.BIG_ENDIAN); + return b.getInt() == (int) t.getId(); + } + + public static void run() throws Exception { + CURRENT_HANDLER = (type, data) -> { + System.out.println("Chunk published: " + printChunk(new Chunk(type, data, 0, data.length))); + }; + initializeTest( + Test1940.class, + Test1940.class.getDeclaredMethod("HandlePublish", Integer.TYPE, new byte[0].getClass())); + // Test sending chunk directly. + DdmServer.registerHandler(MY_DDMS_TYPE, SINGLE_HANDLER); + DdmServer.registerHandler(MY_EMPTY_DDMS_TYPE, SINGLE_HANDLER); + DdmServer.registerHandler(MY_INVALID_DDMS_TYPE, SINGLE_HANDLER); + DdmServer.registrationComplete(); + byte[] data = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; + System.out.println("Sending data " + Arrays.toString(data)); + Chunk res = processChunk(data); + System.out.println("JVMTI returned chunk: " + printChunk(res)); + + // Test sending an empty chunk. + System.out.println("Sending empty data array"); + res = processChunk(new byte[0]); + System.out.println("JVMTI returned chunk: " + printChunk(res)); + + // Test sending chunk through DdmServer#sendChunk + Chunk c = new Chunk( + MY_DDMS_TYPE, new byte[] { 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 }, 0, 8); + System.out.println("Sending chunk: " + printChunk(c)); + DdmServer.sendChunk(c); + + // Test getting back an empty chunk. + data = new byte[] { 0x1 }; + System.out.println( + "Sending data " + Arrays.toString(data) + " to chunk handler " + MY_EMPTY_DDMS_TYPE); + res = processChunk(new Chunk(MY_EMPTY_DDMS_TYPE, data, 0, 1)); + System.out.println("JVMTI returned chunk: " + printChunk(res)); + + // Test getting back an invalid chunk. + System.out.println( + "Sending data " + Arrays.toString(data) + " to chunk handler " + MY_INVALID_DDMS_TYPE); + try { + res = processChunk(new Chunk(MY_INVALID_DDMS_TYPE, data, 0, 1)); + System.out.println("JVMTI returned chunk: " + printChunk(res)); + } catch (RuntimeException e) { + System.out.println("Got error: " + e.getMessage()); + } + + // Test thread chunks are sent. + final boolean[] types_seen = new boolean[] { false, false, false }; + CURRENT_HANDLER = (type, cdata) -> { + switch (type) { + case TYPE_THCR: + types_seen[0] = true; + break; + case TYPE_THNM: + types_seen[1] = true; + break; + case TYPE_THDE: + types_seen[2] = true; + break; + default: + // We don't want to print other types. + break; + } + }; + DdmVmInternal.threadNotify(true); + final Thread thr = new Thread(() -> { return; }, "THREAD"); + thr.start(); + thr.join(); + DdmVmInternal.threadNotify(false); + // Make sure we saw at least one of Thread-create, Thread name, & thread death. + if (!types_seen[0] || !types_seen[1] || !types_seen[2]) { + System.out.println("Didn't see expected chunks for thread creation! got: " + + Arrays.toString(types_seen)); + } else { + System.out.println("Saw expected thread events."); + } + + // Test heap chunks are sent. + CURRENT_HANDLER = (type, cdata) -> { + // The actual data is far to noisy for this test as it includes information about global heap + // state. + if (type == TYPE_HPIF) { + System.out.println("Expected chunk type published: " + type); + } + }; + final int HPIF_WHEN_NOW = 1; + if (!DdmVmInternal.heapInfoNotify(HPIF_WHEN_NOW)) { + System.out.println("Unexpected failure for heapInfoNotify!"); + } + + // method Tracing + CURRENT_HANDLER = (type, cdata) -> { + // This chunk includes timing and thread information so we just check the type. + if (type == TYPE_MPSE) { + System.out.println("Expected chunk type published: " + type); + } + }; + VMDebug.startMethodTracingDdms(/*size: default*/0, + /*flags: none*/ 0, + /*sampling*/ false, + /*interval*/ 0); + doNothing(); + doNothing(); + doNothing(); + doNothing(); + VMDebug.stopMethodTracing(); + } + + private static void doNothing() {} + private static Chunk processChunk(byte[] val) { + return processChunk(new Chunk(MY_DDMS_TYPE, val, 0, val.length)); + } + + private static native void initializeTest(Class k, Method m); + private static native Chunk processChunk(Chunk val); +} diff --git a/test/1940-ddms-ext/src/Main.java b/test/1940-ddms-ext/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..1fd4cd3e41488d8fd94c2badee8cd009309fac3a --- /dev/null +++ b/test/1940-ddms-ext/src/Main.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public static void main(String[] args) throws Exception { + art.Test1940.run(); + } +} diff --git a/test/1940-ddms-ext/src/art/Test1940.java b/test/1940-ddms-ext/src/art/Test1940.java new file mode 100644 index 0000000000000000000000000000000000000000..c8dc19ca7f02063245681696fc398a6aa74e3b44 --- /dev/null +++ b/test/1940-ddms-ext/src/art/Test1940.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art; + +public class Test1940 { + public static void run() throws Exception { + throw new RuntimeException("Should not be called. Should use src-art/art/Test1940.java"); + } +} diff --git a/test/1941-dispose-stress/dispose_stress.cc b/test/1941-dispose-stress/dispose_stress.cc new file mode 100644 index 0000000000000000000000000000000000000000..e8fcc775e98d68ac1df716225d91dac2cc9816d1 --- /dev/null +++ b/test/1941-dispose-stress/dispose_stress.cc @@ -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. + */ + +#include + +#include "android-base/logging.h" +#include "jni.h" +#include "scoped_local_ref.h" +#include "scoped_primitive_array.h" + +#include "jvmti.h" + +// Test infrastructure +#include "jvmti_helper.h" +#include "test_env.h" + +namespace art { +namespace Test1941DisposeStress { + +extern "C" JNIEXPORT jlong JNICALL Java_art_Test1941_AllocEnv(JNIEnv* env, jclass) { + JavaVM* vm = nullptr; + if (env->GetJavaVM(&vm) != 0) { + ScopedLocalRef rt_exception(env, env->FindClass("java/lang/RuntimeException")); + env->ThrowNew(rt_exception.get(), "Unable to get JavaVM"); + return -1; + } + jvmtiEnv* new_env = nullptr; + if (vm->GetEnv(reinterpret_cast(&new_env), JVMTI_VERSION_1_0) != 0) { + ScopedLocalRef rt_exception(env, env->FindClass("java/lang/RuntimeException")); + env->ThrowNew(rt_exception.get(), "Unable to create new jvmtiEnv"); + return -1; + } + return static_cast(reinterpret_cast(new_env)); +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test1941_FreeEnv(JNIEnv* env, + jclass, + jlong jvmti_env_ptr) { + JvmtiErrorToException(env, + jvmti_env, + reinterpret_cast(jvmti_env_ptr)->DisposeEnvironment()); +} + +} // namespace Test1941DisposeStress +} // namespace art + diff --git a/test/1941-dispose-stress/expected.txt b/test/1941-dispose-stress/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..ca2eddc7b8e23ac3a8324e4ccdd0b28829f3b690 --- /dev/null +++ b/test/1941-dispose-stress/expected.txt @@ -0,0 +1 @@ +fib(20) is 6765 diff --git a/test/1941-dispose-stress/info.txt b/test/1941-dispose-stress/info.txt new file mode 100644 index 0000000000000000000000000000000000000000..e4a584e46f816dd29a3217b22fd1d7928f8f912a --- /dev/null +++ b/test/1941-dispose-stress/info.txt @@ -0,0 +1,3 @@ +Test basic JVMTI single step functionality. + +Ensures that we can receive single step events from JVMTI. diff --git a/test/913-heaps/build b/test/1941-dispose-stress/run old mode 100644 new mode 100755 similarity index 86% rename from test/913-heaps/build rename to test/1941-dispose-stress/run index 10ffcc537db7c13099a0423b069305bbcafac7ac..51875a7e8653279c610e7928e508b43eaab24fb0 --- a/test/913-heaps/build +++ b/test/1941-dispose-stress/run @@ -14,7 +14,5 @@ # See the License for the specific language governing permissions and # limitations under the License. -# See b/65168732 -export USE_D8=false - -./default-build "$@" +# Ask for stack traces to be dumped to a file rather than to stdout. +./default-run "$@" --jvmti diff --git a/test/1941-dispose-stress/src/Main.java b/test/1941-dispose-stress/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..2fe6b818a0c68d99d5968c0583afd6461ddc2ac1 --- /dev/null +++ b/test/1941-dispose-stress/src/Main.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public static void main(String[] args) throws Exception { + art.Test1941.run(); + } +} diff --git a/test/1941-dispose-stress/src/art/Breakpoint.java b/test/1941-dispose-stress/src/art/Breakpoint.java new file mode 100644 index 0000000000000000000000000000000000000000..bbb89f707fb26f10beeb79c3b57542a26923d45e --- /dev/null +++ b/test/1941-dispose-stress/src/art/Breakpoint.java @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art; + +import java.lang.reflect.Executable; +import java.util.HashSet; +import java.util.Set; +import java.util.Objects; + +public class Breakpoint { + public static class Manager { + public static class BP { + public final Executable method; + public final long location; + + public BP(Executable method) { + this(method, getStartLocation(method)); + } + + public BP(Executable method, long location) { + this.method = method; + this.location = location; + } + + @Override + public boolean equals(Object other) { + return (other instanceof BP) && + method.equals(((BP)other).method) && + location == ((BP)other).location; + } + + @Override + public String toString() { + return method.toString() + " @ " + getLine(); + } + + @Override + public int hashCode() { + return Objects.hash(method, location); + } + + public int getLine() { + try { + LineNumber[] lines = getLineNumberTable(method); + int best = -1; + for (LineNumber l : lines) { + if (l.location > location) { + break; + } else { + best = l.line; + } + } + return best; + } catch (Exception e) { + return -1; + } + } + } + + private Set breaks = new HashSet<>(); + + public void setBreakpoints(BP... bs) { + for (BP b : bs) { + if (breaks.add(b)) { + Breakpoint.setBreakpoint(b.method, b.location); + } + } + } + public void setBreakpoint(Executable method, long location) { + setBreakpoints(new BP(method, location)); + } + + public void clearBreakpoints(BP... bs) { + for (BP b : bs) { + if (breaks.remove(b)) { + Breakpoint.clearBreakpoint(b.method, b.location); + } + } + } + public void clearBreakpoint(Executable method, long location) { + clearBreakpoints(new BP(method, location)); + } + + public void clearAllBreakpoints() { + clearBreakpoints(breaks.toArray(new BP[0])); + } + } + + public static void startBreakpointWatch(Class methodClass, + Executable breakpointReached, + Thread thr) { + startBreakpointWatch(methodClass, breakpointReached, false, thr); + } + + /** + * Enables the trapping of breakpoint events. + * + * If allowRecursive == true then breakpoints will be sent even if one is currently being handled. + */ + public static native void startBreakpointWatch(Class methodClass, + Executable breakpointReached, + boolean allowRecursive, + Thread thr); + public static native void stopBreakpointWatch(Thread thr); + + public static final class LineNumber implements Comparable { + public final long location; + public final int line; + + private LineNumber(long loc, int line) { + this.location = loc; + this.line = line; + } + + public boolean equals(Object other) { + return other instanceof LineNumber && ((LineNumber)other).line == line && + ((LineNumber)other).location == location; + } + + public int compareTo(LineNumber other) { + int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line)); + if (v != 0) { + return v; + } else { + return Long.valueOf(location).compareTo(Long.valueOf(other.location)); + } + } + } + + public static native void setBreakpoint(Executable m, long loc); + public static void setBreakpoint(Executable m, LineNumber l) { + setBreakpoint(m, l.location); + } + + public static native void clearBreakpoint(Executable m, long loc); + public static void clearBreakpoint(Executable m, LineNumber l) { + clearBreakpoint(m, l.location); + } + + private static native Object[] getLineNumberTableNative(Executable m); + public static LineNumber[] getLineNumberTable(Executable m) { + Object[] nativeTable = getLineNumberTableNative(m); + long[] location = (long[])(nativeTable[0]); + int[] lines = (int[])(nativeTable[1]); + if (lines.length != location.length) { + throw new Error("Lines and locations have different lengths!"); + } + LineNumber[] out = new LineNumber[lines.length]; + for (int i = 0; i < lines.length; i++) { + out[i] = new LineNumber(location[i], lines[i]); + } + return out; + } + + public static native long getStartLocation(Executable m); + + public static int locationToLine(Executable m, long location) { + try { + Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m); + int best = -1; + for (Breakpoint.LineNumber l : lines) { + if (l.location > location) { + break; + } else { + best = l.line; + } + } + return best; + } catch (Exception e) { + return -1; + } + } + + public static long lineToLocation(Executable m, int line) throws Exception { + try { + Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m); + for (Breakpoint.LineNumber l : lines) { + if (l.line == line) { + return l.location; + } + } + throw new Exception("Unable to find line " + line + " in " + m); + } catch (Exception e) { + throw new Exception("Unable to get line number info for " + m, e); + } + } +} + diff --git a/test/1941-dispose-stress/src/art/Test1941.java b/test/1941-dispose-stress/src/art/Test1941.java new file mode 100644 index 0000000000000000000000000000000000000000..d5a9de6cab79226d6221df91f2e07ec7684ec876 --- /dev/null +++ b/test/1941-dispose-stress/src/art/Test1941.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art; + +import java.util.Arrays; +import java.lang.reflect.Executable; +import java.lang.reflect.Method; + +public class Test1941 { + public static final boolean PRINT_CNT = false; + public static long CNT = 0; + + // Method with multiple paths we can break on. + public static long fib(long f) { + if (f < 0) { + throw new IllegalArgumentException("Bad argument f < 0: f = " + f); + } else if (f == 0) { + return 0; + } else if (f == 1) { + return 1; + } else { + return fib(f - 1) + fib(f - 2); + } + } + + public static void notifySingleStep(Thread thr, Executable e, long loc) { + // Don't bother actually doing anything. + } + + public static void LoopAllocFreeEnv() { + while (!Thread.interrupted()) { + CNT++; + long env = AllocEnv(); + FreeEnv(env); + } + } + + public static native long AllocEnv(); + public static native void FreeEnv(long env); + + public static void run() throws Exception { + Thread thr = new Thread(Test1941::LoopAllocFreeEnv, "LoopNative"); + thr.start(); + Trace.enableSingleStepTracing(Test1941.class, + Test1941.class.getDeclaredMethod( + "notifySingleStep", Thread.class, Executable.class, Long.TYPE), + null); + + System.out.println("fib(20) is " + fib(20)); + + thr.interrupt(); + thr.join(); + Trace.disableTracing(null); + if (PRINT_CNT) { + System.out.println("Number of envs created/destroyed: " + CNT); + } + } +} diff --git a/test/1941-dispose-stress/src/art/Trace.java b/test/1941-dispose-stress/src/art/Trace.java new file mode 100644 index 0000000000000000000000000000000000000000..8999bb1368a747deefd4c87427c57eb1df547488 --- /dev/null +++ b/test/1941-dispose-stress/src/art/Trace.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package art; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +public class Trace { + public static native void enableTracing(Class methodClass, + Method entryMethod, + Method exitMethod, + Method fieldAccess, + Method fieldModify, + Method singleStep, + Thread thr); + public static native void disableTracing(Thread thr); + + public static void enableFieldTracing(Class methodClass, + Method fieldAccess, + Method fieldModify, + Thread thr) { + enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr); + } + + public static void enableMethodTracing(Class methodClass, + Method entryMethod, + Method exitMethod, + Thread thr) { + enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr); + } + + public static void enableSingleStepTracing(Class methodClass, + Method singleStep, + Thread thr) { + enableTracing(methodClass, null, null, null, null, singleStep, thr); + } + + public static native void watchFieldAccess(Field f); + public static native void watchFieldModification(Field f); + public static native void watchAllFieldAccesses(); + public static native void watchAllFieldModifications(); + + // the names, arguments, and even line numbers of these functions are embedded in the tests so we + // need to add to the bottom and not modify old ones to maintain compat. + public static native void enableTracing2(Class methodClass, + Method entryMethod, + Method exitMethod, + Method fieldAccess, + Method fieldModify, + Method singleStep, + Method ThreadStart, + Method ThreadEnd, + Thread thr); +} diff --git a/test/445-checker-licm/src/Main.java b/test/445-checker-licm/src/Main.java index 00ce3a9f8ead6a290c7df3cd95eaa2b786ab416a..9635e7027875571e0cf684bce87010895568e882 100644 --- a/test/445-checker-licm/src/Main.java +++ b/test/445-checker-licm/src/Main.java @@ -15,6 +15,11 @@ */ public class Main { + static class Dummy { + static int getValue() { + return 1; + } + } /// CHECK-START: int Main.div() licm (before) /// CHECK-DAG: Div loop:{{B\d+}} @@ -107,6 +112,28 @@ public class Main { return result; } + /// CHECK-START: int Main.clinitCheck() licm (before) + /// CHECK-DAG: <> LoadClass loop:<> + /// CHECK-DAG: ClinitCheck [<>] loop:<> + + /// CHECK-START: int Main.clinitCheck() licm (after) + /// CHECK-NOT: LoadClass loop:{{B\d+}} + /// CHECK-NOT: ClinitCheck loop:{{B\d+}} + + /// CHECK-START: int Main.clinitCheck() licm (after) + /// CHECK-DAG: <> LoadClass loop:none + /// CHECK-DAG: ClinitCheck [<>] loop:none + + public static int clinitCheck() { + int i = 0; + int sum = 0; + do { + sum += Dummy.getValue(); + i++; + } while (i < 10); + return sum; + } + /// CHECK-START: int Main.divAndIntrinsic(int[]) licm (before) /// CHECK-DAG: Div loop:{{B\d+}} @@ -213,6 +240,7 @@ public class Main { assertEquals(18900, innerMul()); assertEquals(105, divByA(2, 0)); assertEquals(12, arrayLength(new int[] { 4, 8 })); + assertEquals(10, clinitCheck()); assertEquals(21, divAndIntrinsic(new int[] { 4, -2, 8, -3 })); assertEquals(45, invariantBoundIntrinsic(-10)); assertEquals(30, invariantBodyIntrinsic(2, 3)); diff --git a/test/466-get-live-vreg/get_live_vreg_jni.cc b/test/466-get-live-vreg/get_live_vreg_jni.cc index 6cea673b416f18d6d53df5a3c0428a74388f91ae..24792f1eede335a797d7e9eaa6433c22989be2b8 100644 --- a/test/466-get-live-vreg/get_live_vreg_jni.cc +++ b/test/466-get-live-vreg/get_live_vreg_jni.cc @@ -16,6 +16,7 @@ #include "arch/context.h" #include "art_method-inl.h" +#include "code_item_accessors-inl.h" #include "jni.h" #include "oat_quick_method_header.h" #include "scoped_thread_state_change-inl.h" @@ -41,7 +42,7 @@ class TestVisitor : public StackVisitor { CHECK(GetVReg(m, 0, kIntVReg, &value)); CHECK_EQ(value, 42u); } else if (m_name.compare("$opt$noinline$testIntervalHole") == 0) { - uint32_t number_of_dex_registers = m->GetCodeItem()->registers_size_; + uint32_t number_of_dex_registers = CodeItemDataAccessor(m).RegistersSize(); uint32_t dex_register_of_first_parameter = number_of_dex_registers - 2; found_method_ = true; uint32_t value = 0; diff --git a/test/518-null-array-get/expected.txt b/test/518-null-array-get/expected.txt index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ae5318e53da61807a61265344b43d43df950a5df 100644 --- a/test/518-null-array-get/expected.txt +++ b/test/518-null-array-get/expected.txt @@ -0,0 +1,6 @@ +NullArrayFailInt2Object +NullArrayFailObject2Int +NullArraySuccessInt +NullArraySuccessInt2Float +NullArraySuccessShort +NullArraySuccessRef diff --git a/test/518-null-array-get/info.txt b/test/518-null-array-get/info.txt index 407f590b2b2cbae840fa30b3fb500cfcc497bf79..71e0332e62c2714167ec9a8cb897a8c6395803d6 100644 --- a/test/518-null-array-get/info.txt +++ b/test/518-null-array-get/info.txt @@ -1,3 +1,9 @@ -Regression test for Quick and Optimizing that used -to crash on an aget-object + int-to-byte sequence -(accepted by the verifier in the case the array was null). +Codifies that the verifier should reject type-unsafe +instructions in dead code after aget on null, but pass +type-safe dead code. + +Previously verification stopped after aget on null and +punted the method to the interpreter in an effort to avoid +compiler crashes. As broken code appears very uncommon, +ensure verifier strictness and help the compilers see more +code. diff --git a/test/518-null-array-get/smali/NullArrayFailInt2Object.smali b/test/518-null-array-get/smali/NullArrayFailInt2Object.smali new file mode 100644 index 0000000000000000000000000000000000000000..ca4ed1066076c83e28b25b3751dbdb742d39071b --- /dev/null +++ b/test/518-null-array-get/smali/NullArrayFailInt2Object.smali @@ -0,0 +1,28 @@ +# 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. + +# Check that the result of aget on null cannot be used as a reference. + +.class public LNullArrayFailInt2Object; + +.super Ljava/lang/Object; + +.method public static method()V + .registers 2 + const/4 v0, 0 + const/4 v1, 0 + aget v0, v0, v1 + invoke-virtual { v0 }, Ljava/lang/Object;->toString()Ljava/lang/String; + return-void +.end method diff --git a/test/518-null-array-get/smali/NullArray.smali b/test/518-null-array-get/smali/NullArrayFailObject2Int.smali similarity index 86% rename from test/518-null-array-get/smali/NullArray.smali rename to test/518-null-array-get/smali/NullArrayFailObject2Int.smali index 52abc38473bb8db300c1762aa892e448aebb913a..83823a24e55ffeef6356819f91cf8c58946f78fc 100644 --- a/test/518-null-array-get/smali/NullArray.smali +++ b/test/518-null-array-get/smali/NullArrayFailObject2Int.smali @@ -12,7 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -.class public LNullArray; +# Check that the result of aget-object on null cannot be used as an integral. + +.class public LNullArrayFailObject2Int; .super Ljava/lang/Object; diff --git a/test/518-null-array-get/smali/NullArraySuccessInt.smali b/test/518-null-array-get/smali/NullArraySuccessInt.smali new file mode 100644 index 0000000000000000000000000000000000000000..01cf1c92ab9a4b5662a6ab2908d593ce795e9365 --- /dev/null +++ b/test/518-null-array-get/smali/NullArraySuccessInt.smali @@ -0,0 +1,33 @@ +# 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. + +# Check that the result of aget on null can be used as an int. + +.class public LNullArraySuccessInt; + +.super Ljava/lang/Object; + +.method public static intMethod(I)V + .registers 1 + return-void +.end method + +.method public static method()V + .registers 2 + const/4 v0, 0 + const/4 v1, 0 + aget v0, v0, v1 + invoke-static { v0 }, LNullArraySuccessInt;->intMethod(I)V + return-void +.end method diff --git a/test/518-null-array-get/smali/NullArraySuccessInt2Float.smali b/test/518-null-array-get/smali/NullArraySuccessInt2Float.smali new file mode 100644 index 0000000000000000000000000000000000000000..bd59d5f68e5d213205db971e9106781bc444004a --- /dev/null +++ b/test/518-null-array-get/smali/NullArraySuccessInt2Float.smali @@ -0,0 +1,33 @@ +# 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. + +# Check that the result of aget on null can be used as a float. + +.class public LNullArraySuccessInt2Float; + +.super Ljava/lang/Object; + +.method public static floatMethod(F)V + .registers 1 + return-void +.end method + +.method public static method()V + .registers 2 + const/4 v0, 0 + const/4 v1, 0 + aget v0, v0, v1 + invoke-static { v0 }, LNullArraySuccessInt2Float;->floatMethod(F)V + return-void +.end method diff --git a/test/518-null-array-get/smali/NullArraySuccessRef.smali b/test/518-null-array-get/smali/NullArraySuccessRef.smali new file mode 100644 index 0000000000000000000000000000000000000000..2f512d40891ee63445c1ce81ca3d0cd5af46469d --- /dev/null +++ b/test/518-null-array-get/smali/NullArraySuccessRef.smali @@ -0,0 +1,33 @@ +# 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. + +# Check that the result of aget-object on null can be used as a reference. + +.class public LNullArraySuccessRef; + +.super Ljava/lang/Object; + +.method public voidMethod()V + .registers 1 + return-void +.end method + +.method public static method()V + .registers 2 + const/4 v0, 0 + const/4 v1, 0 + aget-object v0, v0, v1 + invoke-virtual { v0 }, LNullArraySuccessRef;->voidMethod()V + return-void +.end method diff --git a/test/518-null-array-get/smali/NullArraySuccessShort.smali b/test/518-null-array-get/smali/NullArraySuccessShort.smali new file mode 100644 index 0000000000000000000000000000000000000000..d332e51f520a656f3f4386190d0aca220f37a247 --- /dev/null +++ b/test/518-null-array-get/smali/NullArraySuccessShort.smali @@ -0,0 +1,33 @@ +# 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. + +# Check that the result of aget-short on null can be used as a short. + +.class public LNullArraySuccessShort; + +.super Ljava/lang/Object; + +.method public static shortMethod(S)V + .registers 1 + return-void +.end method + +.method public static method()V + .registers 2 + const/4 v0, 0 + const/4 v1, 0 + aget-short v0, v0, v1 + invoke-static { v0 }, LNullArraySuccessShort;->shortMethod(S)V + return-void +.end method diff --git a/test/518-null-array-get/src/Main.java b/test/518-null-array-get/src/Main.java index 66e50aacd7cc41ef6eeaccfbdce808357da4fffd..678aef1f435c9aa2907d01df225c38d0653e3359 100644 --- a/test/518-null-array-get/src/Main.java +++ b/test/518-null-array-get/src/Main.java @@ -22,16 +22,36 @@ public class Main { class InnerClass {} public static void main(String[] args) throws Exception { - Class c = Class.forName("NullArray"); - Method m = c.getMethod("method"); - Object[] arguments = { }; + checkLoad("NullArrayFailInt2Object", true); + checkLoad("NullArrayFailObject2Int", true); + checkLoad("NullArraySuccessInt", false); + checkLoad("NullArraySuccessInt2Float", false); + checkLoad("NullArraySuccessShort", false); + checkLoad("NullArraySuccessRef", false); + } + + private static void checkLoad(String className, boolean expectError) throws Exception { + Class c; try { - m.invoke(null, arguments); - throw new Error("Expected an InvocationTargetException"); - } catch (InvocationTargetException e) { - if (!(e.getCause() instanceof NullPointerException)) { - throw new Error("Expected a NullPointerException"); + c = Class.forName(className); + if (expectError) { + throw new RuntimeException("Expected error for " + className); + } + Method m = c.getMethod("method"); + try { + m.invoke(null); + throw new RuntimeException("Expected an InvocationTargetException"); + } catch (InvocationTargetException e) { + if (!(e.getCause() instanceof NullPointerException)) { + throw new RuntimeException("Expected a NullPointerException"); + } + System.out.println(className); + } + } catch (VerifyError e) { + if (!expectError) { + throw new RuntimeException(e); } + System.out.println(className); } } } diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java index 30d4970df1b3a5f96aa8ab932bbf369e6c1b7d29..f6332b5503f2cba7d367125ef0c089bba41a219a 100644 --- a/test/530-checker-lse/src/Main.java +++ b/test/530-checker-lse/src/Main.java @@ -814,6 +814,23 @@ public class Main { return arr[0] + arr[1] + arr[2] + arr[3]; } + /// CHECK-START: int Main.testNoSideEffects(int[]) load_store_elimination (before) + /// CHECK: ArraySet + /// CHECK: ArraySet + /// CHECK: ArrayGet + + /// CHECK-START: int Main.testNoSideEffects(int[]) load_store_elimination (after) + /// CHECK: ArraySet + /// CHECK: ArraySet + /// CHECK-NOT: ArrayGet + + private static int testNoSideEffects(int[] array) { + array[0] = 101; + int bitCount = Integer.bitCount(0x3456); + array[1] = array[0] + 1; + return array[0] + bitCount; + } + /// CHECK-START: double Main.getCircleArea(double, boolean) load_store_elimination (before) /// CHECK: NewInstance @@ -932,6 +949,74 @@ public class Main { return array[1] + array[i]; } + /// CHECK-START: int Main.testExitMerge(boolean) load_store_elimination (before) + /// CHECK: NewInstance + /// CHECK: InstanceFieldSet + /// CHECK: InstanceFieldGet + /// CHECK: Return + /// CHECK: InstanceFieldSet + /// CHECK: Throw + + /// CHECK-START: int Main.testExitMerge(boolean) load_store_elimination (after) + /// CHECK-NOT: NewInstance + /// CHECK-NOT: InstanceFieldSet + /// CHECK-NOT: InstanceFieldGet + /// CHECK: Return + /// CHECK-NOT: InstanceFieldSet + /// CHECK: Throw + private static int testExitMerge(boolean cond) { + TestClass obj = new TestClass(); + if (cond) { + obj.i = 1; + return obj.i + 1; + } else { + obj.i = 2; + throw new Error(); + } + } + + /// CHECK-START: int Main.testExitMerge2(boolean) load_store_elimination (before) + /// CHECK: NewInstance + /// CHECK: InstanceFieldSet + /// CHECK: InstanceFieldGet + /// CHECK: InstanceFieldSet + /// CHECK: InstanceFieldGet + + /// CHECK-START: int Main.testExitMerge2(boolean) load_store_elimination (after) + /// CHECK-NOT: NewInstance + /// CHECK-NOT: InstanceFieldSet + /// CHECK-NOT: InstanceFieldGet + private static int testExitMerge2(boolean cond) { + TestClass obj = new TestClass(); + int res; + if (cond) { + obj.i = 1; + res = obj.i + 1; + } else { + obj.i = 2; + res = obj.j + 2; + } + return res; + } + + /// CHECK-START: void Main.testStoreSameValue() load_store_elimination (before) + /// CHECK: NewArray + /// CHECK: ArrayGet + /// CHECK: ArraySet + + /// CHECK-START: void Main.testStoreSameValue() load_store_elimination (after) + /// CHECK: NewArray + /// CHECK-NOT: ArrayGet + /// CHECK-NOT: ArraySet + private static void testStoreSameValue() { + Object[] array = new Object[2]; + sArray = array; + Object obj = array[0]; + array[1] = obj; // store the same value as the defaut value. + } + + static Object[] sArray; + static void assertIntEquals(int result, int expected) { if (expected != result) { throw new Error("Expected: " + expected + ", found: " + result); @@ -1021,6 +1106,15 @@ public class Main { assertIntEquals(testStoreStore().i, 41); assertIntEquals(testStoreStore().j, 43); assertIntEquals(testStoreStoreWithDeoptimize(new int[4]), 4); + + assertIntEquals(testExitMerge(true), 2); + assertIntEquals(testExitMerge2(true), 2); + assertIntEquals(testExitMerge2(false), 2); + + int ret = testNoSideEffects(iarray); + assertIntEquals(iarray[0], 101); + assertIntEquals(iarray[1], 102); + assertIntEquals(ret, 108); } static boolean sFlag; diff --git a/test/530-checker-lse3/expected.txt b/test/530-checker-lse3/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/test/530-checker-lse3/info.txt b/test/530-checker-lse3/info.txt new file mode 100644 index 0000000000000000000000000000000000000000..29b4cb82ab9360e64fde656097ae3d3755a0f509 --- /dev/null +++ b/test/530-checker-lse3/info.txt @@ -0,0 +1,4 @@ +Regression test for load store elimination not respecting the loaded type. When +a wider value is stored in a narrower field and then loaded from that field, +LSE needs to replace the value to be stored with a type conversion to the +narrower type. diff --git a/test/530-checker-lse3/smali/StoreLoad.smali b/test/530-checker-lse3/smali/StoreLoad.smali new file mode 100644 index 0000000000000000000000000000000000000000..7fb582c8d1b59bb28ca5ff58b33b15a1ec28d7c4 --- /dev/null +++ b/test/530-checker-lse3/smali/StoreLoad.smali @@ -0,0 +1,62 @@ +# 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. + +.class public LStoreLoad; + +.super Ljava/lang/Object; + +## CHECK-START: int StoreLoad.test(int) load_store_elimination (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: StaticFieldSet [{{l\d+}},<>] field_name:StoreLoad.byteField +## CHECK-DAG: StaticFieldSet [{{l\d+}},<>] field_name:StoreLoad.byteField2 +## CHECK-DAG: <> StaticFieldGet [{{l\d+}}] field_name:StoreLoad.byteField +## CHECK-DAG: <> StaticFieldGet [{{l\d+}}] field_name:StoreLoad.byteField2 +## CHECK-DAG: <> Add [<>,<>] +## CHECK-DAG: Return [<>] + +## CHECK-START: int StoreLoad.test(int) load_store_elimination (after) +## CHECK-NOT: StaticFieldGet + +## CHECK-START: int StoreLoad.test(int) load_store_elimination (after) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: StaticFieldSet [{{l\d+}},<>] field_name:StoreLoad.byteField +## CHECK-DAG: StaticFieldSet [{{l\d+}},<>] field_name:StoreLoad.byteField2 +## CHECK-DAG: <> TypeConversion [<>] +## CHECK-DAG: <> Add [<>,<>] +## CHECK-DAG: Return [<>] +.method public static test(I)I + .registers 2 + sput-byte v1, LStoreLoad;->byteField:B + sput-byte v1, LStoreLoad;->byteField2:B + sget-byte v0, LStoreLoad;->byteField:B + sget-byte v1, LStoreLoad;->byteField2:B + add-int/2addr v0, v1 + return v0 +.end method + +## CHECK-START: int StoreLoad.test2(int) load_store_elimination (before) +## CHECK-DAG: <> ParameterValue +## CHECK-DAG: StaticFieldSet [{{l\d+}},<>] field_name:StoreLoad.byteField +## CHECK-DAG: Return [<>] + +## CHECK-START: int StoreLoad.test2(int) load_store_elimination (after) +## CHECK-NOT: TypeConversion +.method public static test2(I)I + .registers 1 + sput-byte v0, LStoreLoad;->byteField:B + return v0 +.end method + +.field public static byteField:B +.field public static byteField2:B diff --git a/test/530-checker-lse3/src/Main.java b/test/530-checker-lse3/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..caef0b33ccd7dc367b1addc8629067f579cba512 --- /dev/null +++ b/test/530-checker-lse3/src/Main.java @@ -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. + */ + +import java.lang.reflect.Method; +import java.lang.reflect.Field; + +public class Main { + + // Workaround for b/18051191. + class InnerClass {} + + public static void main(String[] args) throws Exception { + Class c = Class.forName("StoreLoad"); + Method m = c.getMethod("test", int.class); + int result = (Integer)m.invoke(null, 0x12345678); + if (result != (0x78 + 0x78)) { + throw new Error("Expected 240, got " + result); + } + m = c.getMethod("test2", int.class); + result = (Integer)m.invoke(null, 0xdeadbeef); + if (result != 0xdeadbeef) { + throw new Error("Expected 0xdeadbeef, got " + result); + } + Field f = c.getDeclaredField("byteField"); + byte b = f.getByte(null); + if (b != (byte)0xef) { + throw new Error("Expected 0xef, got " + b); + } + f = c.getDeclaredField("byteField2"); + b = f.getByte(null); + if (b != (byte)0x78) { + throw new Error("Expected 0xef, got " + b); + } + } +} diff --git a/test/532-checker-nonnull-arrayset/src/Main.java b/test/532-checker-nonnull-arrayset/src/Main.java index 61c9e88e9e5e65c69f9029ccb93ae673a90cb7b7..f6f877c5594abbcc3979d4ddd1c2d92b3ad28481 100644 --- a/test/532-checker-nonnull-arrayset/src/Main.java +++ b/test/532-checker-nonnull-arrayset/src/Main.java @@ -29,9 +29,7 @@ public class Main { /// CHECK-NOT: test /// CHECK: ReturnVoid public static void test() { - Object[] array = new Object[2]; - // Storing to static to avoid some lse optimization. - sArray = array; + Object[] array = sArray; Object nonNull = array[0]; nonNull.getClass(); // Ensure nonNull has an implicit null check. array[1] = nonNull; diff --git a/test/550-checker-multiply-accumulate/src/Main.java b/test/550-checker-multiply-accumulate/src/Main.java index 9e6fd3d96ade20bf810b014e479632f091dc53c5..b76efeac15e89f2fd4ad388636081110e6ec9402 100644 --- a/test/550-checker-multiply-accumulate/src/Main.java +++ b/test/550-checker-multiply-accumulate/src/Main.java @@ -424,31 +424,19 @@ public class Main { return - (left * right); } - /// CHECK-START-ARM64: void Main.SimdMulAdd(int[], int[]) instruction_simplifier$after_bce (before) + /// CHECK-START-{ARM64,MIPS64}: void Main.SimdMulAdd(int[], int[]) instruction_simplifier$after_bce (before) /// CHECK-DAG: Phi loop:<> outer_loop:none /// CHECK-DAG: VecMul loop:<> outer_loop:none /// CHECK-DAG: VecAdd loop:<> outer_loop:none - /// CHECK-START-ARM64: void Main.SimdMulAdd(int[], int[]) instruction_simplifier$after_bce (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.SimdMulAdd(int[], int[]) instruction_simplifier$after_bce (after) /// CHECK-DAG: Phi loop:<> outer_loop:none /// CHECK-DAG: VecMultiplyAccumulate kind:Add loop:<> outer_loop:none - /// CHECK-START-ARM64: void Main.SimdMulAdd(int[], int[]) instruction_simplifier$after_bce (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.SimdMulAdd(int[], int[]) instruction_simplifier$after_bce (after) /// CHECK-NOT: VecMul /// CHECK-NOT: VecAdd - /// CHECK-START-MIPS64: void Main.SimdMulAdd(int[], int[]) instruction_simplifier$after_bce (before) - /// CHECK-DAG: Phi loop:<> outer_loop:none - /// CHECK-DAG: VecMul loop:<> outer_loop:none - /// CHECK-DAG: VecAdd loop:<> outer_loop:none - - /// CHECK-START-MIPS64: void Main.SimdMulAdd(int[], int[]) instruction_simplifier$after_bce (after) - /// CHECK-DAG: Phi loop:<> outer_loop:none - /// CHECK-DAG: VecMultiplyAccumulate kind:Add loop:<> outer_loop:none - - /// CHECK-START-MIPS64: void Main.SimdMulAdd(int[], int[]) instruction_simplifier$after_bce (after) - /// CHECK-NOT: VecMul - /// CHECK-NOT: VecAdd public static void SimdMulAdd(int[] array1, int[] array2) { for (int j = 0; j < 100; j++) { array2[j] += 12345 * array1[j]; @@ -473,31 +461,19 @@ public class Main { } } - /// CHECK-START-ARM64: void Main.SimdMulSub(int[], int[]) instruction_simplifier$after_bce (before) + /// CHECK-START-{ARM64,MIPS64}: void Main.SimdMulSub(int[], int[]) instruction_simplifier$after_bce (before) /// CHECK-DAG: Phi loop:<> outer_loop:none /// CHECK-DAG: VecMul loop:<> outer_loop:none /// CHECK-DAG: VecSub loop:<> outer_loop:none - /// CHECK-START-ARM64: void Main.SimdMulSub(int[], int[]) instruction_simplifier$after_bce (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.SimdMulSub(int[], int[]) instruction_simplifier$after_bce (after) /// CHECK-DAG: Phi loop:<> outer_loop:none /// CHECK-DAG: VecMultiplyAccumulate kind:Sub loop:<> outer_loop:none - /// CHECK-START-ARM64: void Main.SimdMulSub(int[], int[]) instruction_simplifier$after_bce (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.SimdMulSub(int[], int[]) instruction_simplifier$after_bce (after) /// CHECK-NOT: VecMul /// CHECK-NOT: VecSub - /// CHECK-START-MIPS64: void Main.SimdMulSub(int[], int[]) instruction_simplifier$after_bce (before) - /// CHECK-DAG: Phi loop:<> outer_loop:none - /// CHECK-DAG: VecMul loop:<> outer_loop:none - /// CHECK-DAG: VecSub loop:<> outer_loop:none - - /// CHECK-START-MIPS64: void Main.SimdMulSub(int[], int[]) instruction_simplifier$after_bce (after) - /// CHECK-DAG: Phi loop:<> outer_loop:none - /// CHECK-DAG: VecMultiplyAccumulate kind:Sub loop:<> outer_loop:none - - /// CHECK-START-MIPS64: void Main.SimdMulSub(int[], int[]) instruction_simplifier$after_bce (after) - /// CHECK-NOT: VecMul - /// CHECK-NOT: VecSub public static void SimdMulSub(int[] array1, int[] array2) { for (int j = 0; j < 100; j++) { array2[j] -= 12345 * array1[j]; @@ -522,21 +498,14 @@ public class Main { } } - /// CHECK-START-ARM64: void Main.SimdMulMultipleUses(int[], int[]) instruction_simplifier$after_bce (before) + /// CHECK-START-{ARM64,MIPS64}: void Main.SimdMulMultipleUses(int[], int[]) instruction_simplifier$after_bce (before) /// CHECK-DAG: Phi loop:<> outer_loop:none /// CHECK-DAG: VecMul loop:<> outer_loop:none /// CHECK-DAG: VecSub loop:<> outer_loop:none - /// CHECK-START-ARM64: void Main.SimdMulMultipleUses(int[], int[]) instruction_simplifier$after_bce (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.SimdMulMultipleUses(int[], int[]) instruction_simplifier$after_bce (after) /// CHECK-NOT: VecMultiplyAccumulate - /// CHECK-START-MIPS64: void Main.SimdMulMultipleUses(int[], int[]) instruction_simplifier$after_bce (before) - /// CHECK-DAG: Phi loop:<> outer_loop:none - /// CHECK-DAG: VecMul loop:<> outer_loop:none - /// CHECK-DAG: VecSub loop:<> outer_loop:none - - /// CHECK-START-MIPS64: void Main.SimdMulMultipleUses(int[], int[]) instruction_simplifier$after_bce (after) - /// CHECK-NOT: VecMultiplyAccumulate public static void SimdMulMultipleUses(int[] array1, int[] array2) { for (int j = 0; j < 100; j++) { int temp = 12345 * array1[j]; diff --git a/test/552-checker-sharpening/src/Main.java b/test/552-checker-sharpening/src/Main.java index 55873eabf0afc7b6432875a03d62b10c01ae1cca..3173afdfcd8efd47fbe1894d2cbe40d847d45ab6 100644 --- a/test/552-checker-sharpening/src/Main.java +++ b/test/552-checker-sharpening/src/Main.java @@ -44,24 +44,11 @@ public class Main { /// CHECK-START: int Main.testSimple(int) sharpening (before) /// CHECK: InvokeStaticOrDirect method_load_kind:RuntimeCall - /// CHECK-START-ARM: int Main.testSimple(int) sharpening (after) + /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: int Main.testSimple(int) sharpening (after) /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - /// CHECK-START-ARM64: int Main.testSimple(int) sharpening (after) - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - - /// CHECK-START-MIPS: int Main.testSimple(int) sharpening (after) - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - - /// CHECK-START-MIPS64: int Main.testSimple(int) sharpening (after) - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - - /// CHECK-START-X86: int Main.testSimple(int) sharpening (after) + /// CHECK-START-X86: int Main.testSimple(int) pc_relative_fixups_x86 (before) /// CHECK-NOT: X86ComputeBaseMethodAddress - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - - /// CHECK-START-X86_64: int Main.testSimple(int) sharpening (after) - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry /// CHECK-START-X86: int Main.testSimple(int) pc_relative_fixups_x86 (after) /// CHECK: X86ComputeBaseMethodAddress @@ -74,31 +61,14 @@ public class Main { /// CHECK-START: int Main.testDiamond(boolean, int) sharpening (before) /// CHECK: InvokeStaticOrDirect method_load_kind:RuntimeCall + /// CHECK: InvokeStaticOrDirect method_load_kind:RuntimeCall - /// CHECK-START-ARM: int Main.testDiamond(boolean, int) sharpening (after) - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - - /// CHECK-START-ARM64: int Main.testDiamond(boolean, int) sharpening (after) - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - - /// CHECK-START-MIPS: int Main.testDiamond(boolean, int) sharpening (after) - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - - /// CHECK-START-MIPS64: int Main.testDiamond(boolean, int) sharpening (after) + /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: int Main.testDiamond(boolean, int) sharpening (after) /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - /// CHECK-START-X86: int Main.testDiamond(boolean, int) sharpening (after) + /// CHECK-START-X86: int Main.testDiamond(boolean, int) pc_relative_fixups_x86 (before) /// CHECK-NOT: X86ComputeBaseMethodAddress - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - - /// CHECK-START-X86_64: int Main.testDiamond(boolean, int) sharpening (after) - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry - /// CHECK: InvokeStaticOrDirect method_load_kind:BssEntry /// CHECK-START-X86: int Main.testDiamond(boolean, int) pc_relative_fixups_x86 (after) /// CHECK: X86ComputeBaseMethodAddress @@ -169,30 +139,7 @@ public class Main { return x; } - /// CHECK-START: java.lang.String Main.$noinline$getBootImageString() sharpening (before) - /// CHECK: LoadString load_kind:RuntimeCall - - /// CHECK-START-X86: java.lang.String Main.$noinline$getBootImageString() sharpening (after) - // Note: load kind depends on PIC/non-PIC - /// CHECK: LoadString load_kind:{{BootImageAddress|BootImageInternTable}} - - /// CHECK-START-X86_64: java.lang.String Main.$noinline$getBootImageString() sharpening (after) - // Note: load kind depends on PIC/non-PIC - /// CHECK: LoadString load_kind:{{BootImageAddress|BootImageInternTable}} - - /// CHECK-START-ARM: java.lang.String Main.$noinline$getBootImageString() sharpening (after) - // Note: load kind depends on PIC/non-PIC - /// CHECK: LoadString load_kind:{{BootImageAddress|BootImageInternTable}} - - /// CHECK-START-ARM64: java.lang.String Main.$noinline$getBootImageString() sharpening (after) - // Note: load kind depends on PIC/non-PIC - /// CHECK: LoadString load_kind:{{BootImageAddress|BootImageInternTable}} - - /// CHECK-START-MIPS: java.lang.String Main.$noinline$getBootImageString() sharpening (after) - // Note: load kind depends on PIC/non-PIC - /// CHECK: LoadString load_kind:{{BootImageAddress|BootImageInternTable}} - - /// CHECK-START-MIPS64: java.lang.String Main.$noinline$getBootImageString() sharpening (after) + /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: java.lang.String Main.$noinline$getBootImageString() builder (after) // Note: load kind depends on PIC/non-PIC /// CHECK: LoadString load_kind:{{BootImageAddress|BootImageInternTable}} @@ -203,31 +150,16 @@ public class Main { return ""; } - /// CHECK-START: java.lang.String Main.$noinline$getNonBootImageString() sharpening (before) - /// CHECK: LoadString load_kind:RuntimeCall - - /// CHECK-START-X86: java.lang.String Main.$noinline$getNonBootImageString() sharpening (after) + /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: java.lang.String Main.$noinline$getNonBootImageString() builder (after) /// CHECK: LoadString load_kind:BssEntry + /// CHECK-START-X86: java.lang.String Main.$noinline$getNonBootImageString() pc_relative_fixups_x86 (before) + /// CHECK-NOT: X86ComputeBaseMethodAddress + /// CHECK-START-X86: java.lang.String Main.$noinline$getNonBootImageString() pc_relative_fixups_x86 (after) /// CHECK-DAG: X86ComputeBaseMethodAddress /// CHECK-DAG: LoadString load_kind:BssEntry - /// CHECK-START-X86_64: java.lang.String Main.$noinline$getNonBootImageString() sharpening (after) - /// CHECK: LoadString load_kind:BssEntry - - /// CHECK-START-ARM: java.lang.String Main.$noinline$getNonBootImageString() sharpening (after) - /// CHECK: LoadString load_kind:BssEntry - - /// CHECK-START-ARM64: java.lang.String Main.$noinline$getNonBootImageString() sharpening (after) - /// CHECK: LoadString load_kind:BssEntry - - /// CHECK-START-MIPS: java.lang.String Main.$noinline$getNonBootImageString() sharpening (after) - /// CHECK: LoadString load_kind:BssEntry - - /// CHECK-START-MIPS64: java.lang.String Main.$noinline$getNonBootImageString() sharpening (after) - /// CHECK: LoadString load_kind:BssEntry - public static String $noinline$getNonBootImageString() { // Prevent inlining to avoid the string comparison being optimized away. if (doThrow) { throw new Error(); } @@ -235,27 +167,7 @@ public class Main { return "non-boot-image-string"; } - /// CHECK-START-X86: java.lang.Class Main.$noinline$getStringClass() sharpening (after) - // Note: load kind depends on PIC/non-PIC - /// CHECK: LoadClass load_kind:{{BootImageAddress|BootImageClassTable}} class_name:java.lang.String - - /// CHECK-START-X86_64: java.lang.Class Main.$noinline$getStringClass() sharpening (after) - // Note: load kind depends on PIC/non-PIC - /// CHECK: LoadClass load_kind:{{BootImageAddress|BootImageClassTable}} class_name:java.lang.String - - /// CHECK-START-ARM: java.lang.Class Main.$noinline$getStringClass() sharpening (after) - // Note: load kind depends on PIC/non-PIC - /// CHECK: LoadClass load_kind:{{BootImageAddress|BootImageClassTable}} class_name:java.lang.String - - /// CHECK-START-ARM64: java.lang.Class Main.$noinline$getStringClass() sharpening (after) - // Note: load kind depends on PIC/non-PIC - /// CHECK: LoadClass load_kind:{{BootImageAddress|BootImageClassTable}} class_name:java.lang.String - - /// CHECK-START-MIPS: java.lang.Class Main.$noinline$getStringClass() sharpening (after) - // Note: load kind depends on PIC/non-PIC - /// CHECK: LoadClass load_kind:{{BootImageAddress|BootImageClassTable}} class_name:java.lang.String - - /// CHECK-START-MIPS64: java.lang.Class Main.$noinline$getStringClass() sharpening (after) + /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: java.lang.Class Main.$noinline$getStringClass() builder (after) // Note: load kind depends on PIC/non-PIC /// CHECK: LoadClass load_kind:{{BootImageAddress|BootImageClassTable}} class_name:java.lang.String @@ -266,28 +178,16 @@ public class Main { return String.class; } - /// CHECK-START-X86: java.lang.Class Main.$noinline$getOtherClass() sharpening (after) + /// CHECK-START-{ARM,ARM64,MIPS,MIPS64,X86,X86_64}: java.lang.Class Main.$noinline$getOtherClass() builder (after) /// CHECK: LoadClass load_kind:BssEntry class_name:Other + /// CHECK-START-X86: java.lang.Class Main.$noinline$getOtherClass() pc_relative_fixups_x86 (before) + /// CHECK-NOT: X86ComputeBaseMethodAddress + /// CHECK-START-X86: java.lang.Class Main.$noinline$getOtherClass() pc_relative_fixups_x86 (after) /// CHECK-DAG: X86ComputeBaseMethodAddress /// CHECK-DAG: LoadClass load_kind:BssEntry class_name:Other - /// CHECK-START-X86_64: java.lang.Class Main.$noinline$getOtherClass() sharpening (after) - /// CHECK: LoadClass load_kind:BssEntry class_name:Other - - /// CHECK-START-ARM: java.lang.Class Main.$noinline$getOtherClass() sharpening (after) - /// CHECK: LoadClass load_kind:BssEntry class_name:Other - - /// CHECK-START-ARM64: java.lang.Class Main.$noinline$getOtherClass() sharpening (after) - /// CHECK: LoadClass load_kind:BssEntry class_name:Other - - /// CHECK-START-MIPS: java.lang.Class Main.$noinline$getOtherClass() sharpening (after) - /// CHECK: LoadClass load_kind:BssEntry class_name:Other - - /// CHECK-START-MIPS64: java.lang.Class Main.$noinline$getOtherClass() sharpening (after) - /// CHECK: LoadClass load_kind:BssEntry class_name:Other - public static Class $noinline$getOtherClass() { // Prevent inlining to avoid the string comparison being optimized away. if (doThrow) { throw new Error(); } diff --git a/test/566-polymorphic-inlining/polymorphic_inline.cc b/test/566-polymorphic-inlining/polymorphic_inline.cc index b75becffcb905f6a8ac9c1cfb147cdda2aef9934..e2b8aa037fb8df19c719b53ecc3d35a156bda0e6 100644 --- a/test/566-polymorphic-inlining/polymorphic_inline.cc +++ b/test/566-polymorphic-inlining/polymorphic_inline.cc @@ -19,6 +19,7 @@ #include "jit/jit.h" #include "jit/jit_code_cache.h" #include "jit/profiling_info.h" +#include "mirror/class.h" #include "oat_quick_method_header.h" #include "scoped_thread_state_change-inl.h" #include "stack_map.h" diff --git a/test/603-checker-instanceof/src/Main.java b/test/603-checker-instanceof/src/Main.java index ddf4b92fba8dc903fe8f74ee26e16de4f8c87ad8..1487969c037ff97587106fb5be822f1faa58e147 100644 --- a/test/603-checker-instanceof/src/Main.java +++ b/test/603-checker-instanceof/src/Main.java @@ -22,12 +22,17 @@ class ChildClass extends SuperClass { public class Main { - /// CHECK-START: void Main.main(java.lang.String[]) builder (after) + public static void main(String[] args) { + test1(); + test2(); + } + + /// CHECK-START: void Main.test1() builder (after) /// CHECK: BoundType klass:SuperClass can_be_null:false exact:false - /// CHECK-START: void Main.main(java.lang.String[]) builder (after) + /// CHECK-START: void Main.test1() builder (after) /// CHECK-NOT: BoundType klass:SuperClass can_be_null:false exact:true - public static void main(String[] args) { + public static void test1() { Object obj = new ChildClass(); // We need a fixed point iteration to hit the bogus type update @@ -45,4 +50,33 @@ public class Main { } } } + + /// CHECK-START-X86: boolean Main.$noinline$instanceOfString(java.lang.Object) disassembly (after) + /// CHECK: InstanceOf check_kind:exact_check + /// CHECK-NOT: {{.*fs:.*}} + + /// CHECK-START-X86_64: boolean Main.$noinline$instanceOfString(java.lang.Object) disassembly (after) + /// CHECK: InstanceOf check_kind:exact_check + /// CHECK-NOT: {{.*gs:.*}} + + /// CHECK-START-{ARM,ARM64}: boolean Main.$noinline$instanceOfString(java.lang.Object) disassembly (after) + /// CHECK: InstanceOf check_kind:exact_check + // For ARM and ARM64, the marking register (r8 and x20, respectively) can be used in + // non-CC configs for any other purpose, so we'd need a config-specific checker test. + // TODO: Add the checks when we support config-specific tests. + public static boolean $noinline$instanceOfString(Object o) { + // String is a final class, so `instanceof String` should use exact check. + // String is in the boot image, so we should avoid read barriers. The presence + // of the read barrier can be checked in the architecture-specific disassembly. + return o instanceof String; + } + + public static void test2() { + if ($noinline$instanceOfString(new Object())) { + throw new Error(); + } + if (!$noinline$instanceOfString(new String())) { + throw new Error(); + } + } } diff --git a/test/623-checker-loop-regressions/src/Main.java b/test/623-checker-loop-regressions/src/Main.java index 3ef8fe64bb1206b28227df5b6e6b4ab004e95878..29f3817afb5145e2721cb236aab043d101e2d541 100644 --- a/test/623-checker-loop-regressions/src/Main.java +++ b/test/623-checker-loop-regressions/src/Main.java @@ -493,6 +493,95 @@ public class Main { } } + // Avoid bad scheduler-SIMD interaction. + static int doNotMoveSIMD() { + int sum = 0; + for (int j = 0; j <= 8; j++) { + int[] a = new int[17]; // a[i] = 0; + // ConstructorFence ? + for (int i = 0; i < a.length; i++) { + a[i] += 1; // a[i] = 1; + } + for (int i = 0; i < a.length; i++) { + sum += a[i]; // expect a[i] = 1; + } + } + return sum; + } + + // Ensure spilling saves full SIMD values. + private static final int reduction32Values(int[] a, int[] b, int[] c, int[] d) { + int s0 = 0; + int s1 = 0; + int s2 = 0; + int s3 = 0; + int s4 = 0; + int s5 = 0; + int s6 = 0; + int s7 = 0; + int s8 = 0; + int s9 = 0; + int s10 = 0; + int s11 = 0; + int s12 = 0; + int s13 = 0; + int s14 = 0; + int s15 = 0; + int s16 = 0; + int s17 = 0; + int s18 = 0; + int s19 = 0; + int s20 = 0; + int s21 = 0; + int s22 = 0; + int s23 = 0; + int s24 = 0; + int s25 = 0; + int s26 = 0; + int s27 = 0; + int s28 = 0; + int s29 = 0; + int s30 = 0; + int s31 = 0; + for (int i = 1; i < 100; i++) { + s0 += a[i]; + s1 += b[i]; + s2 += c[i]; + s3 += d[i]; + s4 += a[i]; + s5 += b[i]; + s6 += c[i]; + s7 += d[i]; + s8 += a[i]; + s9 += b[i]; + s10 += c[i]; + s11 += d[i]; + s12 += a[i]; + s13 += b[i]; + s14 += c[i]; + s15 += d[i]; + s16 += a[i]; + s17 += b[i]; + s18 += c[i]; + s19 += d[i]; + s20 += a[i]; + s21 += b[i]; + s22 += c[i]; + s23 += d[i]; + s24 += a[i]; + s25 += b[i]; + s26 += c[i]; + s27 += d[i]; + s28 += a[i]; + s29 += b[i]; + s30 += c[i]; + s31 += d[i]; + } + return s0 + s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10 + s11 + s12 + s13 + s14 + s15 + + s16 + s17 + s18 + s19 + s20 + s21 + s22 + s23 + + s24 + s25 + s26 + s27 + s28 + s29 + s30 + s31; + } + public static void main(String[] args) { expectEquals(10, earlyExitFirst(-1)); for (int i = 0; i <= 10; i++) { @@ -655,6 +744,22 @@ public class Main { expectEquals((byte)((short) cx[i] + 1), b1[i]); } + expectEquals(153, doNotMoveSIMD()); + + { + int[] a1 = new int[100]; + int[] a2 = new int[100]; + int[] a3 = new int[100]; + int[] a4 = new int[100]; + for (int i = 0; i < 100; i++) { + a1[i] = i; + a2[i] = 1; + a3[i] = 100 - i; + a4[i] = i % 16; + } + expectEquals(85800, reduction32Values(a1, a2, a3, a4)); + } + System.out.println("passed"); } diff --git a/test/706-jit-skip-compilation/expected.txt b/test/638-checker-inline-cache-intrinsic/expected.txt similarity index 100% rename from test/706-jit-skip-compilation/expected.txt rename to test/638-checker-inline-cache-intrinsic/expected.txt diff --git a/test/638-checker-inline-cache-intrinsic/info.txt b/test/638-checker-inline-cache-intrinsic/info.txt new file mode 100644 index 0000000000000000000000000000000000000000..764577be548f5e17a38e0aaccc04e91c4afa7f86 --- /dev/null +++ b/test/638-checker-inline-cache-intrinsic/info.txt @@ -0,0 +1 @@ +Verify the devirtualization of a method that should be intrinsified. diff --git a/test/638-checker-inline-cache-intrinsic/run b/test/638-checker-inline-cache-intrinsic/run new file mode 100644 index 0000000000000000000000000000000000000000..f43681dd56cf852c2f15d6c03a75930657ea216f --- /dev/null +++ b/test/638-checker-inline-cache-intrinsic/run @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +exec ${RUN} --jit --runtime-option -Xjitthreshold:100 -Xcompiler-option --verbose-methods=inlineMonomorphic,knownReceiverType,stringEquals $@ diff --git a/test/638-checker-inline-cache-intrinsic/src/Main.java b/test/638-checker-inline-cache-intrinsic/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..472cbf68bc9f07ffb328ee868b948a0626d43115 --- /dev/null +++ b/test/638-checker-inline-cache-intrinsic/src/Main.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + + /// CHECK-START: char Main.$noinline$inlineMonomorphic(java.lang.CharSequence) inliner (before) + /// CHECK: InvokeInterface method_name:java.lang.CharSequence.charAt + + /// CHECK-START: char Main.$noinline$inlineMonomorphic(java.lang.CharSequence) inliner (after) + /// CHECK: Deoptimize + /// CHECK: InvokeVirtual method_name:java.lang.String.charAt intrinsic:StringCharAt + + /// CHECK-START: char Main.$noinline$inlineMonomorphic(java.lang.CharSequence) instruction_simplifier$after_inlining (after) + /// CHECK: Deoptimize + /// CHECK-NOT: InvokeInterface + /// CHECK-NOT: InvokeVirtual + + public static char $noinline$inlineMonomorphic(CharSequence cs) { + return cs.charAt(0); + } + + /// CHECK-START: char Main.$noinline$knownReceiverType() inliner (before) + /// CHECK: InvokeInterface method_name:java.lang.CharSequence.charAt + + /// CHECK-START: char Main.$noinline$knownReceiverType() inliner (after) + /// CHECK: InvokeVirtual method_name:java.lang.String.charAt intrinsic:StringCharAt + + /// CHECK-START: char Main.$noinline$knownReceiverType() instruction_simplifier$after_inlining (after) + /// CHECK-NOT: InvokeInterface + /// CHECK-NOT: InvokeVirtual + + public static char $noinline$knownReceiverType() { + CharSequence cs = "abc"; + return cs.charAt(1); + } + + /// CHECK-START: boolean Main.$noinline$stringEquals(java.lang.Object) inliner (before) + /// CHECK: InvokeVirtual method_name:java.lang.Object.equals intrinsic:None + + /// CHECK-START: boolean Main.$noinline$stringEquals(java.lang.Object) inliner (after) + /// CHECK: Deoptimize + /// CHECK: InvokeVirtual method_name:java.lang.Object.equals intrinsic:StringEquals + + /// CHECK-START: boolean Main.$noinline$stringEquals(java.lang.Object) instruction_simplifier$after_inlining (after) + /// CHECK: Deoptimize + /// CHECK: InvokeVirtual method_name:java.lang.Object.equals intrinsic:StringEquals + + public static boolean $noinline$stringEquals(Object obj) { + return obj.equals("def"); + } + + public static void test() { + // Warm up inline cache. + for (int i = 0; i < 45; i++) { + $noinline$inlineMonomorphic(str); + } + for (int i = 0; i < 60; i++) { + $noinline$stringEquals(str); + } + ensureJitCompiled(Main.class, "$noinline$stringEquals"); + ensureJitCompiled(Main.class, "$noinline$inlineMonomorphic"); + ensureJitCompiled(Main.class, "$noinline$knownReceiverType"); + if ($noinline$inlineMonomorphic(str) != 'x') { + throw new Error("Expected x"); + } + if ($noinline$knownReceiverType() != 'b') { + throw new Error("Expected b"); + } + if ($noinline$stringEquals("abc")) { + throw new Error("Expected false"); + } + } + + public static void main(String[] args) { + System.loadLibrary(args[0]); + test(); + } + + static String str = "xyz"; + + private static native void ensureJitCompiled(Class itf, String method_name); +} diff --git a/test/640-checker-boolean-simd/src/Main.java b/test/640-checker-boolean-simd/src/Main.java index 347f916c8d0fe6a5f214902e1af1d07db4cedc8e..7d98e68a5a622bab4e9e4e312a037b2baf33b24c 100644 --- a/test/640-checker-boolean-simd/src/Main.java +++ b/test/640-checker-boolean-simd/src/Main.java @@ -29,17 +29,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.and(boolean) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAnd loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.and(boolean) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAnd loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.and(boolean) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.and(boolean) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecAnd loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -52,17 +42,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.or(boolean) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecOr loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.or(boolean) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecOr loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.or(boolean) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.or(boolean) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecOr loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -75,17 +55,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.xor(boolean) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecXor loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.xor(boolean) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecXor loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.xor(boolean) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.xor(boolean) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecXor loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -98,17 +68,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNot loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNot loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.not() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.not() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecNot loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none diff --git a/test/640-checker-byte-simd/src/Main.java b/test/640-checker-byte-simd/src/Main.java index 5c13fc39268a3d2e13737179e684eb43df64038e..6b691277b00adf6ecac23470dc3f9d464174ea30 100644 --- a/test/640-checker-byte-simd/src/Main.java +++ b/test/640-checker-byte-simd/src/Main.java @@ -29,17 +29,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.add(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAdd loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.add(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAdd loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.add(int) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.add(int) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecAdd loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -52,17 +42,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.sub(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecSub loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.sub(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecSub loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.sub(int) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.sub(int) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecSub loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -75,17 +55,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.mul(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecMul loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.mul(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecMul loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.mul(int) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.mul(int) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecMul loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -111,17 +81,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNeg loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNeg loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.neg() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.neg() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecNeg loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -134,17 +94,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNot loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNot loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.not() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.not() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecNot loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -157,17 +107,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.shl4() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShl loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.shl4() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShl loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.shl4() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.shl4() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecShl loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -180,17 +120,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.sar2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShr loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.sar2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShr loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.sar2() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.sar2() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecShr loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none diff --git a/test/640-checker-char-simd/src/Main.java b/test/640-checker-char-simd/src/Main.java index b3dff1411b44683ba700fc333271b081cc558dee..317a666980d12f9797665c8532f4b5539f136dbe 100644 --- a/test/640-checker-char-simd/src/Main.java +++ b/test/640-checker-char-simd/src/Main.java @@ -29,17 +29,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.add(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAdd loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.add(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAdd loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.add(int) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.add(int) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecAdd loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -52,17 +42,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.sub(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecSub loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.sub(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecSub loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.sub(int) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.sub(int) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecSub loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -75,17 +55,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.mul(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecMul loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.mul(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecMul loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.mul(int) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.mul(int) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecMul loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -99,6 +69,7 @@ public class Main { /// CHECK-DAG: ArraySet loop:<> outer_loop:none // /// CHECK-START: void Main.div(int) loop_optimization (after) + /// CHECK-NOT: VecDiv // // Not supported on any architecture. // @@ -111,17 +82,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNeg loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNeg loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.neg() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.neg() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecNeg loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -134,17 +95,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNot loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNot loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.not() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.not() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecNot loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -157,17 +108,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.shl4() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShl loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.shl4() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShl loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.shl4() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.shl4() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecShl loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -192,17 +133,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.shr2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecUShr loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.shr2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecUShr loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.shr2() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.shr2() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecUShr loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none diff --git a/test/640-checker-double-simd/src/Main.java b/test/640-checker-double-simd/src/Main.java index 5d0899864ab093e1fb0875e9d4004cd4d325c5b7..0f04f734ccd419f2740dadc24460d530e20fbad3 100644 --- a/test/640-checker-double-simd/src/Main.java +++ b/test/640-checker-double-simd/src/Main.java @@ -30,12 +30,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.add(double) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAdd loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.add(double) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.add(double) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecAdd loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -48,12 +43,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.sub(double) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecSub loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.sub(double) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.sub(double) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecSub loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -66,12 +56,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.mul(double) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecMul loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.mul(double) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.mul(double) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecMul loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -84,12 +69,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.div(double) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecDiv loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.div(double) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.div(double) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecDiv loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -102,12 +82,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNeg loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.neg() loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.neg() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecNeg loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -120,12 +95,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.abs() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAbs loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.abs() loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.abs() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecAbs loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -138,11 +108,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.conv(long[]) loop_optimization (after) - /// CHECK-NOT: VecLoad - /// CHECK-NOT: VecStore - // - /// CHECK-START-MIPS64: void Main.conv(long[]) loop_optimization (after) + /// CHECK-START: void Main.conv(long[]) loop_optimization (after) /// CHECK-NOT: VecLoad /// CHECK-NOT: VecStore // diff --git a/test/640-checker-float-simd/src/Main.java b/test/640-checker-float-simd/src/Main.java index c7883f37a3a200029e4aa63551b1389e786ef2bb..d4eef9f763e553f3cd053599ce6e27cfef141bb0 100644 --- a/test/640-checker-float-simd/src/Main.java +++ b/test/640-checker-float-simd/src/Main.java @@ -30,12 +30,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.add(float) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAdd loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.add(float) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.add(float) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecAdd loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -48,12 +43,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.sub(float) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecSub loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.sub(float) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.sub(float) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecSub loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -66,12 +56,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.mul(float) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecMul loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.mul(float) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.mul(float) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecMul loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -84,12 +69,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.div(float) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecDiv loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.div(float) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.div(float) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecDiv loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -102,12 +82,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNeg loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.neg() loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.neg() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecNeg loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -120,12 +95,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-MIPS64: void Main.abs() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAbs loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.abs() loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.abs() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecAbs loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -138,12 +108,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.conv(int[]) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecCnv loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.conv(int[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.conv(int[]) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecCnv loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none diff --git a/test/640-checker-int-simd/src/Main.java b/test/640-checker-int-simd/src/Main.java index aa230bfcaf0eaead3e19e884fe287604242b9aa8..85d8b1b70b0c7dcb322333099d4ba422348255ee 100644 --- a/test/640-checker-int-simd/src/Main.java +++ b/test/640-checker-int-simd/src/Main.java @@ -29,17 +29,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.add(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAdd loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.add(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAdd loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.add(int) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.add(int) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecAdd loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -52,17 +42,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.sub(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecSub loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.sub(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecSub loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.sub(int) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.sub(int) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecSub loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -75,17 +55,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.mul(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecMul loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.mul(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecMul loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.mul(int) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.mul(int) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecMul loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -112,17 +82,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNeg loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNeg loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.neg() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.neg() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecNeg loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -135,17 +95,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNot loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNot loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.not() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.not() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecNot loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -158,17 +108,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.shl4() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShl loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.shl4() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShl loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.shl4() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.shl4() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecShl loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -181,17 +121,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.sar2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShr loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.sar2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShr loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.sar2() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.sar2() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecShr loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -204,17 +134,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.shr2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecUShr loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.shr2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecUShr loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.shr2() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.shr2() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecUShr loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -242,15 +162,7 @@ public class Main { /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.shr32() loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.shr32() loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.shr32() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.shr32() loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none static void shr32() { @@ -271,19 +183,7 @@ public class Main { /// CHECK-DAG: <> UShr [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.shr33() loop_optimization (after) - /// CHECK-DAG: <> IntConstant 1 loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecUShr [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.shr33() loop_optimization (after) - /// CHECK-DAG: <> IntConstant 1 loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecUShr [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.shr33() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.shr33() loop_optimization (after) /// CHECK-DAG: <> IntConstant 1 loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecUShr [<>,<>] loop:<> outer_loop:none @@ -305,19 +205,7 @@ public class Main { /// CHECK-DAG: <> UShr [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.shrMinus254() loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecUShr [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.shrMinus254() loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecUShr [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.shrMinus254() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.shrMinus254() loop_optimization (after) /// CHECK-DAG: <> IntConstant 2 loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecUShr [<>,<>] loop:<> outer_loop:none diff --git a/test/640-checker-long-simd/src/Main.java b/test/640-checker-long-simd/src/Main.java index c754f2a309c21c5daa7b6366a81c36705bf6558f..bb4d0cbd6722c5f72b95460fe08e10002161056d 100644 --- a/test/640-checker-long-simd/src/Main.java +++ b/test/640-checker-long-simd/src/Main.java @@ -29,12 +29,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.add(long) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAdd loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.add(long) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.add(long) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecAdd loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -47,12 +42,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.sub(long) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecSub loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.sub(long) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.sub(long) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecSub loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -65,14 +55,15 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // + // Not directly supported for longs. + // + /// CHECK-START-ARM64: void Main.mul(long) loop_optimization (after) + /// CHECK-NOT: VecMul + // /// CHECK-START-MIPS64: void Main.mul(long) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecMul loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - // Not supported for longs. - /// CHECK-START-ARM64: void Main.mul(long) loop_optimization (after) - /// CHECK-NOT: VecMul static void mul(long x) { for (int i = 0; i < 128; i++) a[i] *= x; @@ -96,12 +87,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNeg loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.neg() loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.neg() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecNeg loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -114,12 +100,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNot loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.not() loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.not() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecNot loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -132,12 +113,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.shl4() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShl loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.shl4() loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.shl4() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecShl loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -150,12 +126,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.sar2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShr loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.sar2() loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.sar2() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecShr loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -168,12 +139,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.shr2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecUShr loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.shr2() loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.shr2() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecUShr loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -201,11 +167,7 @@ public class Main { /// CHECK-DAG: <> ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.shr64() loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.shr64() loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.shr64() loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none static void shr64() { @@ -226,13 +188,7 @@ public class Main { /// CHECK-DAG: <> UShr [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.shr65() loop_optimization (after) - /// CHECK-DAG: <> IntConstant 1 loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecUShr [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.shr65() loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.shr65() loop_optimization (after) /// CHECK-DAG: <> IntConstant 1 loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecUShr [<>,<>] loop:<> outer_loop:none @@ -254,13 +210,7 @@ public class Main { /// CHECK-DAG: <> UShr [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.shrMinus254() loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecUShr [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.shrMinus254() loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.shrMinus254() loop_optimization (after) /// CHECK-DAG: <> IntConstant 2 loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecUShr [<>,<>] loop:<> outer_loop:none diff --git a/test/640-checker-short-simd/src/Main.java b/test/640-checker-short-simd/src/Main.java index e1873978534d20b4add87c74ff1f9043ed99ab7b..2b4ba87b713f959454e30f762f7be037886795b3 100644 --- a/test/640-checker-short-simd/src/Main.java +++ b/test/640-checker-short-simd/src/Main.java @@ -29,17 +29,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.add(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAdd loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.add(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAdd loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.add(int) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.add(int) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecAdd loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -52,17 +42,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.sub(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecSub loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.sub(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecSub loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.sub(int) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.sub(int) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecSub loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -75,17 +55,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.mul(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecMul loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.mul(int) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecMul loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.mul(int) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.mul(int) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecMul loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -99,6 +69,7 @@ public class Main { /// CHECK-DAG: ArraySet loop:<> outer_loop:none // /// CHECK-START: void Main.div(int) loop_optimization (after) + /// CHECK-NOT: VecDiv // // Not supported on any architecture. // @@ -111,17 +82,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNeg loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.neg() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNeg loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.neg() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.neg() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecNeg loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -134,17 +95,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNot loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.not() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecNot loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.not() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.not() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecNot loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -157,17 +108,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.shl4() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShl loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.shl4() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShl loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.shl4() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.shl4() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecShl loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -180,17 +121,7 @@ public class Main { /// CHECK-DAG: ArrayGet loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.sar2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShr loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.sar2() loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecShr loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.sar2() loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.sar2() loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecShr loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none diff --git a/test/642-fp-callees/fp_callees.cc b/test/642-fp-callees/fp_callees.cc index 600f9690ebab2eaf28e64c445a38725b4e0b0a39..17bb55b70df7cb4e670cab00edef020de7b08650 100644 --- a/test/642-fp-callees/fp_callees.cc +++ b/test/642-fp-callees/fp_callees.cc @@ -14,8 +14,9 @@ * limitations under the License. */ +#include + #include "base/casts.h" -#include "base/logging.h" #include "jni.h" namespace art { diff --git a/test/645-checker-abs-simd/src/Main.java b/test/645-checker-abs-simd/src/Main.java index 823908c20e9ead38317902803bf69da90df831b2..d498bda052784c089637261736624b9696949913 100644 --- a/test/645-checker-abs-simd/src/Main.java +++ b/test/645-checker-abs-simd/src/Main.java @@ -28,7 +28,7 @@ public class Main { /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.doitByte(byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitByte(byte[]) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecAbs loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -38,25 +38,6 @@ public class Main { // /// CHECK-EVAL: "<>" != "<>" // - /// CHECK-START-ARM64: void Main.doitByte(byte[]) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAbs loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<> outer_loop:none - /// CHECK-DAG: ArraySet loop:<> outer_loop:none - // - /// CHECK-EVAL: "<>" != "<>" - // - /// CHECK-START-MIPS64: void Main.doitByte(byte[]) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAbs loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<> outer_loop:none - /// CHECK-DAG: ArraySet loop:<> outer_loop:none - // - /// CHECK-EVAL: "<>" != "<>" private static void doitByte(byte[] x) { for (int i = 0; i < x.length; i++) { x[i] = (byte) Math.abs(x[i]); @@ -84,17 +65,7 @@ public class Main { /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.doitShort(short[]) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAbs loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<> outer_loop:none - /// CHECK-DAG: ArraySet loop:<> outer_loop:none - // - /// CHECK-EVAL: "<>" != "<>" - // - /// CHECK-START-ARM64: void Main.doitShort(short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitShort(short[]) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecAbs loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -104,15 +75,6 @@ public class Main { // /// CHECK-EVAL: "<>" != "<>" // - /// CHECK-START-MIPS64: void Main.doitShort(short[]) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAbs loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<> outer_loop:none - /// CHECK-DAG: ArraySet loop:<> outer_loop:none - // - /// CHECK-EVAL: "<>" != "<>" private static void doitShort(short[] x) { for (int i = 0; i < x.length; i++) { x[i] = (short) Math.abs(x[i]); @@ -147,7 +109,7 @@ public class Main { /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.doitInt(int[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitInt(int[]) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecAbs loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -157,25 +119,6 @@ public class Main { // /// CHECK-EVAL: "<>" != "<>" // - /// CHECK-START-ARM64: void Main.doitInt(int[]) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAbs loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<> outer_loop:none - /// CHECK-DAG: ArraySet loop:<> outer_loop:none - // - /// CHECK-EVAL: "<>" != "<>" - // - /// CHECK-START-MIPS64: void Main.doitInt(int[]) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAbs loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsInt loop:<> outer_loop:none - /// CHECK-DAG: ArraySet loop:<> outer_loop:none - // - /// CHECK-EVAL: "<>" != "<>" private static void doitInt(int[] x) { for (int i = 0; i < x.length; i++) { x[i] = Math.abs(x[i]); @@ -188,7 +131,7 @@ public class Main { /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsLong loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.doitLong(long[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.doitLong(long[]) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecAbs loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -198,15 +141,6 @@ public class Main { // /// CHECK-EVAL: "<>" != "<>" // - /// CHECK-START-MIPS64: void Main.doitLong(long[]) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAbs loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsLong loop:<> outer_loop:none - /// CHECK-DAG: ArraySet loop:<> outer_loop:none - // - /// CHECK-EVAL: "<>" != "<>" private static void doitLong(long[] x) { for (int i = 0; i < x.length; i++) { x[i] = Math.abs(x[i]); @@ -219,7 +153,7 @@ public class Main { /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsFloat loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.doitFloat(float[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.doitFloat(float[]) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecAbs loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -229,15 +163,6 @@ public class Main { // /// CHECK-EVAL: "<>" != "<>" // - /// CHECK-START-MIPS64: void Main.doitFloat(float[]) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAbs loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsFloat loop:<> outer_loop:none - /// CHECK-DAG: ArraySet loop:<> outer_loop:none - // - /// CHECK-EVAL: "<>" != "<>" private static void doitFloat(float[] x) { for (int i = 0; i < x.length; i++) { x[i] = Math.abs(x[i]); @@ -250,7 +175,7 @@ public class Main { /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsDouble loop:<> outer_loop:none /// CHECK-DAG: ArraySet loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.doitDouble(double[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.doitDouble(double[]) loop_optimization (after) /// CHECK-DAG: VecLoad loop:<> outer_loop:none /// CHECK-DAG: VecAbs loop:<> outer_loop:none /// CHECK-DAG: VecStore loop:<> outer_loop:none @@ -260,15 +185,6 @@ public class Main { // /// CHECK-EVAL: "<>" != "<>" // - /// CHECK-START-MIPS64: void Main.doitDouble(double[]) loop_optimization (after) - /// CHECK-DAG: VecLoad loop:<> outer_loop:none - /// CHECK-DAG: VecAbs loop:<> outer_loop:none - /// CHECK-DAG: VecStore loop:<> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<> outer_loop:none - /// CHECK-DAG: InvokeStaticOrDirect intrinsic:MathAbsDouble loop:<> outer_loop:none - /// CHECK-DAG: ArraySet loop:<> outer_loop:none - // - /// CHECK-EVAL: "<>" != "<>" private static void doitDouble(double[] x) { for (int i = 0; i < x.length; i++) { x[i] = Math.abs(x[i]); diff --git a/test/646-checker-hadd-alt-byte/src/Main.java b/test/646-checker-hadd-alt-byte/src/Main.java index 41aa40cd6d526c1c8488ce956e9a0c1fa26c1e0b..2ef340ab456320b3c5e85820dd2dde54fa065f71 100644 --- a/test/646-checker-hadd-alt-byte/src/Main.java +++ b/test/646-checker-hadd-alt-byte/src/Main.java @@ -39,19 +39,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:false loop:<> outer_loop:none @@ -86,19 +74,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:false loop:<> outer_loop:none @@ -121,19 +97,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.rounding_halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:true loop:<> outer_loop:none @@ -170,19 +134,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.rounding_halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:true loop:<> outer_loop:none @@ -204,21 +156,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_signed_constant(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 127 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_signed_constant(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 127 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_signed_constant(byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_signed_constant(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 127 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none @@ -252,21 +190,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned_constant(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 255 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned_constant(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 255 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_unsigned_constant(byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_unsigned_constant(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 255 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none diff --git a/test/646-checker-hadd-alt-char/src/Main.java b/test/646-checker-hadd-alt-char/src/Main.java index 8f879c77d09da4f58c9d7892a635174eb3718ce5..2a1382dfde1cf5975a470a663985968e76817675 100644 --- a/test/646-checker-hadd-alt-char/src/Main.java +++ b/test/646-checker-hadd-alt-char/src/Main.java @@ -39,19 +39,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_unsigned(char[], char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_unsigned(char[], char[], char[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none @@ -87,19 +75,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none @@ -125,19 +101,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_unsigned(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.rounding_halving_add_unsigned(char[], char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_unsigned(char[], char[], char[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:true loop:<> outer_loop:none @@ -174,19 +138,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.rounding_halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_also_unsigned(char[], char[], char[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:true loop:<> outer_loop:none @@ -211,21 +163,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned_constant(char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 65535 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned_constant(char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 65535 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_unsigned_constant(char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_unsigned_constant(char[], char[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 65535 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none @@ -259,21 +197,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_also_unsigned_constant(char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 65535 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_also_unsigned_constant(char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 65535 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_also_unsigned_constant(char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_also_unsigned_constant(char[], char[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 65535 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none diff --git a/test/646-checker-hadd-alt-short/src/Main.java b/test/646-checker-hadd-alt-short/src/Main.java index b591081fba67a91a74eb891401029f779ff00c9a..4035b972099ac75726218c125f576e82ef72c1c6 100644 --- a/test/646-checker-hadd-alt-short/src/Main.java +++ b/test/646-checker-hadd-alt-short/src/Main.java @@ -39,19 +39,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_signed(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_signed(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_signed(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_signed(short[], short[], short[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:false loop:<> outer_loop:none @@ -86,19 +74,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_unsigned(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_unsigned(short[], short[], short[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none @@ -121,19 +97,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_signed(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_signed(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.rounding_halving_add_signed(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_signed(short[], short[], short[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:true loop:<> outer_loop:none @@ -170,19 +134,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_unsigned(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.rounding_halving_add_unsigned(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_unsigned(short[], short[], short[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:true loop:<> outer_loop:none @@ -204,21 +156,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_signed_constant(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 32767 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_signed_constant(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 32767 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_signed_constant(short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_signed_constant(short[], short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 32767 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none @@ -252,21 +190,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned_constant(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 65535 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned_constant(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 65535 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_unsigned_constant(short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_unsigned_constant(short[], short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 65535 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none diff --git a/test/646-checker-hadd-byte/src/Main.java b/test/646-checker-hadd-byte/src/Main.java index 4d259c437b1bb6b0a4702e5ddf03d172865c0df8..ca22200a1ab761e48dfe522a45b7845e8cc66ebb 100644 --- a/test/646-checker-hadd-byte/src/Main.java +++ b/test/646-checker-hadd-byte/src/Main.java @@ -36,19 +36,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:false loop:<> outer_loop:none @@ -83,19 +71,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:false loop:<> outer_loop:none @@ -118,19 +94,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.rounding_halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_signed(byte[], byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:true loop:<> outer_loop:none @@ -167,19 +131,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.rounding_halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_unsigned(byte[], byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:true loop:<> outer_loop:none @@ -201,21 +153,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_signed_constant(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 127 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_signed_constant(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 127 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_signed_constant(byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_signed_constant(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 127 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none @@ -249,21 +187,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned_constant(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 255 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned_constant(byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 255 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint8 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_unsigned_constant(byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_unsigned_constant(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 255 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none diff --git a/test/646-checker-hadd-short/src/Main.java b/test/646-checker-hadd-short/src/Main.java index 55bb9586704316f104d56d25347aafdc8fbf203d..85c2fcaf220bad7d782f12d42bd32f536f2d28b0 100644 --- a/test/646-checker-hadd-short/src/Main.java +++ b/test/646-checker-hadd-short/src/Main.java @@ -36,19 +36,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_signed(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_signed(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_signed(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_signed(short[], short[], short[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:false loop:<> outer_loop:none @@ -74,19 +62,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_signed_alt(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_signed_alt(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_signed_alt(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_signed_alt(short[], short[], short[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:false loop:<> outer_loop:none @@ -122,19 +98,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_unsigned(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_unsigned(short[], short[], short[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none @@ -157,19 +121,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_signed(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_signed(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.rounding_halving_add_signed(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_signed(short[], short[], short[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:true loop:<> outer_loop:none @@ -192,19 +144,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_signed_alt(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_signed_alt(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.rounding_halving_add_signed_alt(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_signed_alt(short[], short[], short[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:true loop:<> outer_loop:none @@ -231,19 +171,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_signed_alt2(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_signed_alt2(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.rounding_halving_add_signed_alt2(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_signed_alt2(short[], short[], short[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:true loop:<> outer_loop:none @@ -281,19 +209,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_unsigned(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.rounding_halving_add_unsigned(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_unsigned(short[], short[], short[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:true loop:<> outer_loop:none @@ -330,19 +246,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.rounding_halving_add_unsigned_alt(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.rounding_halving_add_unsigned_alt(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:true loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.rounding_halving_add_unsigned_alt(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.rounding_halving_add_unsigned_alt(short[], short[], short[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:true loop:<> outer_loop:none @@ -365,21 +269,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_signed_constant(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 32767 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_signed_constant(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 32767 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Int16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_signed_constant(short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_signed_constant(short[], short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 32767 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none @@ -413,21 +303,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.halving_add_unsigned_constant(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 65535 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.halving_add_unsigned_constant(short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 65535 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecHalvingAdd [<>,<>] packed_type:Uint16 rounded:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.halving_add_unsigned_constant(short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.halving_add_unsigned_constant(short[], short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 65535 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none diff --git a/test/651-checker-byte-simd-minmax/src/Main.java b/test/651-checker-byte-simd-minmax/src/Main.java index d365689f5d998e2b0567ef46baab6d30ee9b1a1d..45949ae90a0cd62083e4ca969dfbbdd1567079b8 100644 --- a/test/651-checker-byte-simd-minmax/src/Main.java +++ b/test/651-checker-byte-simd-minmax/src/Main.java @@ -27,19 +27,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.doitMin(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Int8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.doitMin(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Int8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.doitMin(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMin(byte[], byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Int8 loop:<> outer_loop:none @@ -70,19 +58,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.doitMinUnsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Uint8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.doitMinUnsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Uint8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.doitMinUnsigned(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMinUnsigned(byte[], byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Uint8 loop:<> outer_loop:none @@ -102,19 +78,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.doitMax(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Int8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.doitMax(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Int8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.doitMax(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMax(byte[], byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Int8 loop:<> outer_loop:none @@ -145,19 +109,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.doitMaxUnsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Uint8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.doitMaxUnsigned(byte[], byte[], byte[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Uint8 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.doitMaxUnsigned(byte[], byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMaxUnsigned(byte[], byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Uint8 loop:<> outer_loop:none @@ -177,7 +129,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.doitMin100(byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.doitMin100(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 100 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none diff --git a/test/651-checker-char-simd-minmax/src/Main.java b/test/651-checker-char-simd-minmax/src/Main.java index 72e8958ad8067cfcc082c6324a071a115117f189..9b056094a33c046e61579fa3e9451829a28e958a 100644 --- a/test/651-checker-char-simd-minmax/src/Main.java +++ b/test/651-checker-char-simd-minmax/src/Main.java @@ -27,19 +27,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.doitMin(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Uint16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.doitMin(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Uint16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.doitMin(char[], char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMin(char[], char[], char[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Uint16 loop:<> outer_loop:none @@ -59,19 +47,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.doitMax(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Uint16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.doitMax(char[], char[], char[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Uint16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.doitMax(char[], char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMax(char[], char[], char[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Uint16 loop:<> outer_loop:none @@ -91,7 +67,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.doitMin100(char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.doitMin100(char[], char[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 100 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none diff --git a/test/651-checker-int-simd-minmax/src/Main.java b/test/651-checker-int-simd-minmax/src/Main.java index 598106e6041ed5e9d77ceac4cef0899d68265d32..66343adaa8473eb136a539822b9498b7b750a12f 100644 --- a/test/651-checker-int-simd-minmax/src/Main.java +++ b/test/651-checker-int-simd-minmax/src/Main.java @@ -26,19 +26,7 @@ public class Main { /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMinIntInt loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.doitMin(int[], int[], int[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] unsigned:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.doitMin(int[], int[], int[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] unsigned:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.doitMin(int[], int[], int[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMin(int[], int[], int[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMin [<>,<>] unsigned:false loop:<> outer_loop:none @@ -57,19 +45,7 @@ public class Main { /// CHECK-DAG: <> InvokeStaticOrDirect [<>,<>] intrinsic:MathMaxIntInt loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.doitMax(int[], int[], int[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMax [<>,<>] unsigned:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.doitMax(int[], int[], int[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMax [<>,<>] unsigned:false loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.doitMax(int[], int[], int[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMax(int[], int[], int[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMax [<>,<>] unsigned:false loop:<> outer_loop:none diff --git a/test/651-checker-short-simd-minmax/src/Main.java b/test/651-checker-short-simd-minmax/src/Main.java index d8c4d1e87e6ece6f64d10454a6002611c70a95ca..5f10adab79b5869e19dbf4ec2ab10a7affea4a66 100644 --- a/test/651-checker-short-simd-minmax/src/Main.java +++ b/test/651-checker-short-simd-minmax/src/Main.java @@ -27,19 +27,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.doitMin(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Int16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.doitMin(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Int16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.doitMin(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMin(short[], short[], short[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Int16 loop:<> outer_loop:none @@ -70,19 +58,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.doitMinUnsigned(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Uint16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.doitMinUnsigned(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Uint16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.doitMinUnsigned(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMinUnsigned(short[], short[], short[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMin [<>,<>] packed_type:Uint16 loop:<> outer_loop:none @@ -102,19 +78,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.doitMax(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Int16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.doitMax(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Int16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.doitMax(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMax(short[], short[], short[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Int16 loop:<> outer_loop:none @@ -145,19 +109,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: void Main.doitMaxUnsigned(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Uint16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: void Main.doitMaxUnsigned(short[], short[], short[]) loop_optimization (after) - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none - /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Uint16 loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},{{i\d+}},<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.doitMaxUnsigned(short[], short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM,ARM64,MIPS64}: void Main.doitMaxUnsigned(short[], short[], short[]) loop_optimization (after) /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none /// CHECK-DAG: <> VecMax [<>,<>] packed_type:Uint16 loop:<> outer_loop:none @@ -177,7 +129,7 @@ public class Main { /// CHECK-DAG: <> TypeConversion [<>] loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.doitMin100(short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.doitMin100(short[], short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 100 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none diff --git a/test/655-jit-clinit/src/Main.java b/test/655-jit-clinit/src/Main.java index 44b315478f75072e35f18757de24d5270f684926..2fb8f2a86e632fc221e691bcce459842d9e0c812 100644 --- a/test/655-jit-clinit/src/Main.java +++ b/test/655-jit-clinit/src/Main.java @@ -23,7 +23,7 @@ public class Main { Foo.hotMethod(); } - public native static boolean isJitCompiled(Class cls, String methodName); + public native static boolean hasJitCompiledEntrypoint(Class cls, String methodName); private native static boolean hasJit(); } @@ -36,7 +36,7 @@ class Foo { static { array = new Object[10000]; - while (!Main.isJitCompiled(Foo.class, "hotMethod")) { + while (!Main.hasJitCompiledEntrypoint(Foo.class, "hotMethod")) { Foo.hotMethod(); try { // Sleep to give a chance for the JIT to compile `hotMethod`. diff --git a/test/656-checker-simd-opt/src/Main.java b/test/656-checker-simd-opt/src/Main.java index 39a126f5d3cbe0da66a6c1078026ccf819379bc0..081e4214224279d01cc902f1368278e55bc2bd2c 100644 --- a/test/656-checker-simd-opt/src/Main.java +++ b/test/656-checker-simd-opt/src/Main.java @@ -102,7 +102,7 @@ public class Main { /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: long Main.longInductionReduction(long[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.longInductionReduction(long[]) loop_optimization (after) /// CHECK-DAG: <> LongConstant 0 loop:none /// CHECK-DAG: <> LongConstant 1 loop:none /// CHECK-DAG: <> LongConstant 2 loop:none @@ -131,7 +131,7 @@ public class Main { /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.intVectorLongInvariant(int[], long[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.intVectorLongInvariant(int[], long[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 1 loop:none /// CHECK-DAG: <> IntConstant 4 loop:none @@ -159,7 +159,7 @@ public class Main { /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.longCanBeDoneWithInt(int[], int[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.longCanBeDoneWithInt(int[], int[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 4 loop:none /// CHECK-DAG: <> LongConstant 1 loop:none diff --git a/test/660-checker-simd-sad-byte/src/Main.java b/test/660-checker-simd-sad-byte/src/Main.java index 72d1c24dbe44c9befc4d0f47c4784a825ae66286..594948b7f9dc70b113b88ce31ffdd8c1d67cf29c 100644 --- a/test/660-checker-simd-sad-byte/src/Main.java +++ b/test/660-checker-simd-sad-byte/src/Main.java @@ -99,7 +99,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadByte2Int(byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadByte2Int(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 16 loop:none /// CHECK-DAG: <> VecSetScalars [<>] loop:none @@ -130,7 +130,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadByte2IntAlt(byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadByte2IntAlt(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 16 loop:none /// CHECK-DAG: <> VecSetScalars [<>] loop:none @@ -163,7 +163,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadByte2IntAlt2(byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadByte2IntAlt2(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 16 loop:none /// CHECK-DAG: <> VecSetScalars [<>] loop:none @@ -201,7 +201,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadByte2Long(byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadByte2Long(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 16 loop:none /// CHECK-DAG: <> LongConstant 0 loop:none @@ -238,7 +238,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadByte2LongAt1(byte[], byte[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadByte2LongAt1(byte[], byte[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 16 loop:none /// CHECK-DAG: <> LongConstant 1 loop:none diff --git a/test/660-checker-simd-sad-char/src/Main.java b/test/660-checker-simd-sad-char/src/Main.java index 2535d49b9cadaac6db9ddfc1daf5fe29661e5de9..ba226149b45595f096677e0a34e26bd50436dd80 100644 --- a/test/660-checker-simd-sad-char/src/Main.java +++ b/test/660-checker-simd-sad-char/src/Main.java @@ -68,7 +68,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadChar2Int(char[], char[]) loop_optimization (after) + /// CHECK-START: int Main.sadChar2Int(char[], char[]) loop_optimization (after) /// CHECK-NOT: VecSADAccumulate private static int sadChar2Int(char[] s1, char[] s2) { int min_length = Math.min(s1.length, s2.length); @@ -91,7 +91,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadChar2IntAlt(char[], char[]) loop_optimization (after) + /// CHECK-START: int Main.sadChar2IntAlt(char[], char[]) loop_optimization (after) /// CHECK-NOT: VecSADAccumulate private static int sadChar2IntAlt(char[] s1, char[] s2) { int min_length = Math.min(s1.length, s2.length); @@ -116,7 +116,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadChar2IntAlt2(char[], char[]) loop_optimization (after) + /// CHECK-START: int Main.sadChar2IntAlt2(char[], char[]) loop_optimization (after) /// CHECK-NOT: VecSADAccumulate private static int sadChar2IntAlt2(char[] s1, char[] s2) { int min_length = Math.min(s1.length, s2.length); @@ -146,7 +146,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadChar2Long(char[], char[]) loop_optimization (after) + /// CHECK-START: long Main.sadChar2Long(char[], char[]) loop_optimization (after) /// CHECK-NOT: VecSADAccumulate private static long sadChar2Long(char[] s1, char[] s2) { int min_length = Math.min(s1.length, s2.length); @@ -174,7 +174,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadChar2LongAt1(char[], char[]) loop_optimization (after) + /// CHECK-START: long Main.sadChar2LongAt1(char[], char[]) loop_optimization (after) /// CHECK-NOT: VecSADAccumulate private static long sadChar2LongAt1(char[] s1, char[] s2) { int min_length = Math.min(s1.length, s2.length); diff --git a/test/660-checker-simd-sad-int/src/Main.java b/test/660-checker-simd-sad-int/src/Main.java index 388bfba0d29a884f4bef1ca8d705321f977e5477..aa8431c1f531aa0abce5718486c9984b19935672 100644 --- a/test/660-checker-simd-sad-int/src/Main.java +++ b/test/660-checker-simd-sad-int/src/Main.java @@ -31,23 +31,14 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: int Main.sadInt2Int(int[], int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 loop:none + /// CHECK-START-{ARM,ARM64,MIPS64}: int Main.sadInt2Int(int[], int[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant {{2|4}} loop:none /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: int Main.sadInt2Int(int[], int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none private static int sadInt2Int(int[] x, int[] y) { int min_length = Math.min(x.length, y.length); int sad = 0; @@ -72,10 +63,7 @@ public class Main { // // No ABS? No SAD! // - /// CHECK-START-ARM: int Main.sadInt2IntAlt(int[], int[]) loop_optimization (after) - /// CHECK-NOT: VecSADAccumulate - // - /// CHECK-START-ARM64: int Main.sadInt2IntAlt(int[], int[]) loop_optimization (after) + /// CHECK-START: int Main.sadInt2IntAlt(int[], int[]) loop_optimization (after) /// CHECK-NOT: VecSADAccumulate private static int sadInt2IntAlt(int[] x, int[] y) { int min_length = Math.min(x.length, y.length); @@ -100,23 +88,14 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM: int Main.sadInt2IntAlt2(int[], int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-ARM64: int Main.sadInt2IntAlt2(int[], int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none + /// CHECK-START-{ARM,ARM64,MIPS64}: int Main.sadInt2IntAlt2(int[], int[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant {{2|4}} loop:none /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none private static int sadInt2IntAlt2(int[] x, int[] y) { int min_length = Math.min(x.length, y.length); int sad = 0; @@ -145,7 +124,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadInt2Long(int[], int[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadInt2Long(int[], int[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 4 loop:none /// CHECK-DAG: <> LongConstant 0 loop:none @@ -182,7 +161,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadInt2LongAt1(int[], int[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadInt2LongAt1(int[], int[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 4 loop:none /// CHECK-DAG: <> LongConstant 1 loop:none diff --git a/test/660-checker-simd-sad-long/src/Main.java b/test/660-checker-simd-sad-long/src/Main.java index 06f62bd03105d046c4177a774c360e36a2becc63..8281812b5bf707fb2fd6c1e990a07ec4f3ab10c2 100644 --- a/test/660-checker-simd-sad-long/src/Main.java +++ b/test/660-checker-simd-sad-long/src/Main.java @@ -32,7 +32,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadLong2Long(long[], long[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadLong2Long(long[], long[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 2 loop:none /// CHECK-DAG: <> LongConstant 0 loop:none @@ -94,7 +94,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadLong2LongAlt2(long[], long[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadLong2LongAlt2(long[], long[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 2 loop:none /// CHECK-DAG: <> LongConstant 0 loop:none @@ -131,7 +131,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadLong2LongAt1(long[], long[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadLong2LongAt1(long[], long[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 2 loop:none /// CHECK-DAG: <> LongConstant 1 loop:none diff --git a/test/660-checker-simd-sad-short/src/Main.java b/test/660-checker-simd-sad-short/src/Main.java index d94308e24d442916b54460de6a31782b61b1767a..16bcabac95ad64536f3d45075986c57c6d7754b2 100644 --- a/test/660-checker-simd-sad-short/src/Main.java +++ b/test/660-checker-simd-sad-short/src/Main.java @@ -66,7 +66,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2Int(short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2Int(short[], short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> VecSetScalars [<>] loop:none @@ -97,7 +97,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntAlt(short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntAlt(short[], short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> VecSetScalars [<>] loop:none @@ -130,7 +130,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntAlt2(short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntAlt2(short[], short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> VecSetScalars [<>] loop:none @@ -168,7 +168,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadShort2Long(short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadShort2Long(short[], short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> LongConstant 0 loop:none @@ -205,7 +205,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadShort2LongAt1(short[], short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadShort2LongAt1(short[], short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> LongConstant 1 loop:none diff --git a/test/660-checker-simd-sad-short2/src/Main.java b/test/660-checker-simd-sad-short2/src/Main.java index 708f3aa14512fedebbc65fc8944983c7d00b1e06..274892d00108d58d0c7149b7847017dc4b41903b 100644 --- a/test/660-checker-simd-sad-short2/src/Main.java +++ b/test/660-checker-simd-sad-short2/src/Main.java @@ -84,7 +84,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadCastedChar2Int(char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadCastedChar2Int(char[], char[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> VecSetScalars [<>] loop:none @@ -134,7 +134,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadCastedChar2IntAlt(char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadCastedChar2IntAlt(char[], char[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> VecSetScalars [<>] loop:none @@ -186,7 +186,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadCastedChar2IntAlt2(char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadCastedChar2IntAlt2(char[], char[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> VecSetScalars [<>] loop:none @@ -243,7 +243,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadCastedChar2Long(char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadCastedChar2Long(char[], char[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> LongConstant 0 loop:none @@ -299,7 +299,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: long Main.sadCastedChar2LongAt1(char[], char[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.sadCastedChar2LongAt1(char[], char[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> LongConstant 1 loop:none diff --git a/test/660-checker-simd-sad-short3/src/Main.java b/test/660-checker-simd-sad-short3/src/Main.java index c8850b41e4df55ba3909616a5f45982c7763d087..5016b658e564bba4106bde79c107351009912a63 100644 --- a/test/660-checker-simd-sad-short3/src/Main.java +++ b/test/660-checker-simd-sad-short3/src/Main.java @@ -33,7 +33,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntParamRight(short[], short) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntParamRight(short[], short) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> ParameterValue loop:none @@ -64,7 +64,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntParamLeft(short[], short) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntParamLeft(short[], short) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> ParameterValue loop:none @@ -95,7 +95,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntConstRight(short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntConstRight(short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> IntConstant 32767 loop:none @@ -126,7 +126,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntConstLeft(short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntConstLeft(short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> IntConstant 32767 loop:none @@ -157,7 +157,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntInvariantRight(short[], int) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntInvariantRight(short[], int) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> TypeConversion [{{i\d+}}] loop:none @@ -189,7 +189,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntInvariantLeft(short[], int) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntInvariantLeft(short[], int) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> TypeConversion [{{i\d+}}] loop:none @@ -199,6 +199,7 @@ public class Main { /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none private static int sadShort2IntInvariantLeft(short[] s, int val) { int sad = 0; short x = (short) (val + 1); @@ -222,7 +223,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntCastedExprRight(short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntCastedExprRight(short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> IntConstant 110 loop:none @@ -233,6 +234,7 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecAdd [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none private static int sadShort2IntCastedExprRight(short[] s) { int sad = 0; for (int i = 0; i < s.length; i++) { @@ -256,7 +258,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: int Main.sadShort2IntCastedExprLeft(short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: int Main.sadShort2IntCastedExprLeft(short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> IntConstant 8 loop:none /// CHECK-DAG: <> IntConstant 110 loop:none @@ -267,6 +269,7 @@ public class Main { /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecAdd [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecSADAccumulate [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none private static int sadShort2IntCastedExprLeft(short[] s) { int sad = 0; for (int i = 0; i < s.length; i++) { diff --git a/test/661-checker-simd-reduc/src/Main.java b/test/661-checker-simd-reduc/src/Main.java index 1add0f102678a4a3db6dac6820a34dd38029a6dd..3a0a0495c4a05bfe68b6008bd92b801864dd4df2 100644 --- a/test/661-checker-simd-reduc/src/Main.java +++ b/test/661-checker-simd-reduc/src/Main.java @@ -62,33 +62,13 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Return [<>] loop:none // - /// CHECK-START-ARM: int Main.reductionInt(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-START-ARM64: int Main.reductionInt(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none + /// CHECK-START-{ARM,ARM64,MIPS64}: int Main.reductionInt(int[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant {{2|4}} loop:none /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-START-MIPS64: int Main.reductionInt(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecReduce [<>] loop:none /// CHECK-DAG: <> VecExtractScalar [<>] loop:none private static int reductionInt(int[] x) { @@ -116,58 +96,19 @@ public class Main { // /// CHECK-EVAL: "<>" != "<>" // - /// CHECK-START-ARM: int Main.reductionIntChain() loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-EVAL: "<>" != "<>" - // - /// CHECK-START-ARM64: int Main.reductionIntChain() loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-EVAL: "<>" != "<>" - // - /// CHECK-START-MIPS64: int Main.reductionIntChain() loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none + /// CHECK-START-{ARM,ARM64,MIPS64}: int Main.reductionIntChain() loop_optimization (after) /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> VecReduce [<>] loop:none /// CHECK-DAG: <> VecExtractScalar [<>] loop:none /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,{{i\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> VecReduce [<>] loop:none /// CHECK-DAG: <> VecExtractScalar [<>] loop:none // @@ -199,38 +140,18 @@ public class Main { // /// CHECK-EVAL: "<>" != "<>" // - /// CHECK-START-ARM: int Main.reductionIntToLoop(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-START-ARM64: int Main.reductionIntToLoop(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-START-MIPS64: int Main.reductionIntToLoop(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none + /// CHECK-START-{ARM,ARM64,MIPS64}: int Main.reductionIntToLoop(int[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant {{2|4}} loop:none /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecReduce [<>] loop:none /// CHECK-DAG: <> VecExtractScalar [<>] loop:none private static int reductionIntToLoop(int[] x) { int r = 0; - for (int i = 0; i < 4; i++) { + for (int i = 0; i < 8; i++) { r += x[i]; } for (int i = r; i < 16; i++) { @@ -250,17 +171,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Return [<>] loop:none // - /// CHECK-START-ARM64: long Main.reductionLong(long[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 loop:none - /// CHECK-DAG: <> VecSetScalars [{{j\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-START-MIPS64: long Main.reductionLong(long[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.reductionLong(long[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 2 loop:none /// CHECK-DAG: <> VecSetScalars [{{j\d+}}] loop:none /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none @@ -312,33 +223,13 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Return [<>] loop:none // - /// CHECK-START-ARM: int Main.reductionIntM1(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-START-ARM64: int Main.reductionIntM1(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-START-MIPS64: int Main.reductionIntM1(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none + /// CHECK-START-{ARM,ARM64,MIPS64}: int Main.reductionIntM1(int[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant {{2|4}} loop:none /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecReduce [<>] loop:none /// CHECK-DAG: <> VecExtractScalar [<>] loop:none private static int reductionIntM1(int[] x) { @@ -360,17 +251,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Return [<>] loop:none // - /// CHECK-START-ARM64: long Main.reductionLongM1(long[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 loop:none - /// CHECK-DAG: <> VecSetScalars [{{j\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecAdd [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-START-MIPS64: long Main.reductionLongM1(long[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.reductionLongM1(long[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 2 loop:none /// CHECK-DAG: <> VecSetScalars [{{j\d+}}] loop:none /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none @@ -421,33 +302,13 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Return [<>] loop:none // - /// CHECK-START-ARM: int Main.reductionMinusInt(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecSub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-START-ARM64: int Main.reductionMinusInt(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none + /// CHECK-START-{ARM,ARM64,MIPS64}: int Main.reductionMinusInt(int[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant {{2|4}} loop:none /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: VecSub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-START-MIPS64: int Main.reductionMinusInt(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none - /// CHECK-DAG: <> VecSetScalars [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecSub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecReduce [<>] loop:none /// CHECK-DAG: <> VecExtractScalar [<>] loop:none private static int reductionMinusInt(int[] x) { @@ -469,17 +330,7 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Return [<>] loop:none // - /// CHECK-START-ARM64: long Main.reductionMinusLong(long[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 loop:none - /// CHECK-DAG: <> VecSetScalars [{{j\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecSub [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-START-MIPS64: long Main.reductionMinusLong(long[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: long Main.reductionMinusLong(long[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 2 loop:none /// CHECK-DAG: <> VecSetScalars [{{j\d+}}] loop:none /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none @@ -531,33 +382,13 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Return [<>] loop:none // - /// CHECK-START-ARM: int Main.reductionMinInt(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 loop:none - /// CHECK-DAG: <> VecReplicateScalar [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecMin [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-START-ARM64: int Main.reductionMinInt(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none - /// CHECK-DAG: <> VecReplicateScalar [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecMin [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-START-MIPS64: int Main.reductionMinInt(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none + /// CHECK-START-{ARM,ARM64,MIPS64}: int Main.reductionMinInt(int[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant {{2|4}} loop:none /// CHECK-DAG: <> VecReplicateScalar [{{i\d+}}] loop:none /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: VecMin [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecReduce [<>] loop:none /// CHECK-DAG: <> VecExtractScalar [<>] loop:none private static int reductionMinInt(int[] x) { @@ -611,33 +442,13 @@ public class Main { /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: Return [<>] loop:none // - /// CHECK-START-ARM: int Main.reductionMaxInt(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 2 loop:none - /// CHECK-DAG: <> VecReplicateScalar [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecMax [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-START-ARM64: int Main.reductionMaxInt(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none - /// CHECK-DAG: <> VecReplicateScalar [{{i\d+}}] loop:none - /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none - /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none - /// CHECK-DAG: VecMax [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: <> VecReduce [<>] loop:none - /// CHECK-DAG: <> VecExtractScalar [<>] loop:none - // - /// CHECK-START-MIPS64: int Main.reductionMaxInt(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 4 loop:none + /// CHECK-START-{ARM,ARM64,MIPS64}: int Main.reductionMaxInt(int[]) loop_optimization (after) + /// CHECK-DAG: <> IntConstant {{2|4}} loop:none /// CHECK-DAG: <> VecReplicateScalar [{{i\d+}}] loop:none /// CHECK-DAG: <> Phi [<>,{{d\d+}}] loop:<> outer_loop:none /// CHECK-DAG: <> VecLoad [{{l\d+}},<>] loop:<> outer_loop:none /// CHECK-DAG: VecMax [<>,<>] loop:<> outer_loop:none - /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none /// CHECK-DAG: <> VecReduce [<>] loop:none /// CHECK-DAG: <> VecExtractScalar [<>] loop:none private static int reductionMaxInt(int[] x) { @@ -743,9 +554,9 @@ public class Main { } // Test various reductions in loops. - int[] x0 = { 0, 0, 0, 0 }; - int[] x1 = { 0, 0, 0, 1 }; - int[] x2 = { 1, 1, 1, 1 }; + int[] x0 = { 0, 0, 0, 0, 0, 0, 0, 0 }; + int[] x1 = { 0, 0, 0, 1, 0, 0, 0, 0 }; + int[] x2 = { 1, 1, 1, 1, 0, 0, 0, 0 }; expectEquals(-74, reductionByte(xb)); expectEquals(-27466, reductionShort(xs)); expectEquals(38070, reductionChar(xc)); @@ -754,7 +565,7 @@ public class Main { expectEquals(120, reductionIntToLoop(x0)); expectEquals(121, reductionIntToLoop(x1)); expectEquals(118, reductionIntToLoop(x2)); - expectEquals(-1205, reductionIntToLoop(xi)); + expectEquals(-1310, reductionIntToLoop(xi)); expectEquals(365750L, reductionLong(xl)); expectEquals(-75, reductionByteM1(xb)); expectEquals(-27467, reductionShortM1(xs)); diff --git a/test/665-checker-simd-zero/src/Main.java b/test/665-checker-simd-zero/src/Main.java index 6cd6d6465acc124a7921cd3c7b971982493c85f2..5c581c4fc7b74debd0e6dbe4391279e92de323db 100644 --- a/test/665-checker-simd-zero/src/Main.java +++ b/test/665-checker-simd-zero/src/Main.java @@ -24,13 +24,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.zeroz(boolean[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.zeroz(boolean[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.zeroz(boolean[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none @@ -46,13 +40,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.zerob(byte[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.zerob(byte[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.zerob(byte[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none @@ -68,13 +56,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.zeroc(char[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.zeroc(char[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.zeroc(char[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none @@ -90,13 +72,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.zeros(short[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.zeros(short[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.zeros(short[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none @@ -112,13 +88,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.zeroi(int[]) loop_optimization (after) - /// CHECK-DAG: <> IntConstant 0 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.zeroi(int[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.zeroi(int[]) loop_optimization (after) /// CHECK-DAG: <> IntConstant 0 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none @@ -134,13 +104,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.zerol(long[]) loop_optimization (after) - /// CHECK-DAG: <> LongConstant 0 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.zerol(long[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.zerol(long[]) loop_optimization (after) /// CHECK-DAG: <> LongConstant 0 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none @@ -156,13 +120,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.zerof(float[]) loop_optimization (after) - /// CHECK-DAG: <> FloatConstant 0 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.zerof(float[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.zerof(float[]) loop_optimization (after) /// CHECK-DAG: <> FloatConstant 0 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none @@ -178,13 +136,7 @@ public class Main { /// CHECK-DAG: <> Phi loop:<> outer_loop:none /// CHECK-DAG: ArraySet [{{l\d+}},<>,<>] loop:<> outer_loop:none // - /// CHECK-START-ARM64: void Main.zerod(double[]) loop_optimization (after) - /// CHECK-DAG: <> DoubleConstant 0 loop:none - /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none - /// CHECK-DAG: <> Phi loop:<> outer_loop:none - /// CHECK-DAG: VecStore [{{l\d+}},<>,<>] loop:<> outer_loop:none - // - /// CHECK-START-MIPS64: void Main.zerod(double[]) loop_optimization (after) + /// CHECK-START-{ARM64,MIPS64}: void Main.zerod(double[]) loop_optimization (after) /// CHECK-DAG: <> DoubleConstant 0 loop:none /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none /// CHECK-DAG: <> Phi loop:<> outer_loop:none diff --git a/test/667-jit-jni-stub/expected.txt b/test/667-jit-jni-stub/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..6a5618ebc67de22d67fae3dfcd84e929be352659 --- /dev/null +++ b/test/667-jit-jni-stub/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/667-jit-jni-stub/info.txt b/test/667-jit-jni-stub/info.txt new file mode 100644 index 0000000000000000000000000000000000000000..6f25c44592c528b719549e172a5cd19f5c6aa88a --- /dev/null +++ b/test/667-jit-jni-stub/info.txt @@ -0,0 +1 @@ +Tests for JITting and collecting JNI stubs. diff --git a/test/667-jit-jni-stub/jit_jni_stub_test.cc b/test/667-jit-jni-stub/jit_jni_stub_test.cc new file mode 100644 index 0000000000000000000000000000000000000000..82e06fc018dc25365e49d09207be32a9f724f98f --- /dev/null +++ b/test/667-jit-jni-stub/jit_jni_stub_test.cc @@ -0,0 +1,63 @@ +/* + * Copyright 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 + +#include "jit/jit.h" +#include "jit/jit_code_cache.h" +#include "mirror/class.h" +#include "mirror/string.h" +#include "runtime.h" +#include "scoped_thread_state_change-inl.h" + +namespace art { + +// Local class declared as a friend of JitCodeCache so that we can access its internals. +class JitJniStubTestHelper { + public: + static bool isNextJitGcFull(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { + CHECK(Runtime::Current()->GetJit() != nullptr); + jit::JitCodeCache* cache = Runtime::Current()->GetJit()->GetCodeCache(); + MutexLock mu(self, cache->lock_); + return cache->ShouldDoFullCollection(); + } +}; + +// Calls through to a static method with signature "()V". +extern "C" JNIEXPORT +void Java_Main_callThrough(JNIEnv* env, jclass, jclass klass, jstring methodName) { + ScopedObjectAccess soa(Thread::Current()); + std::string name = soa.Decode(methodName)->ToModifiedUtf8(); + jmethodID method = env->GetStaticMethodID(klass, name.c_str(), "()V"); + CHECK(method != nullptr) << soa.Decode(klass)->PrettyDescriptor() << "." << name; + env->CallStaticVoidMethod(klass, method); +} + +extern "C" JNIEXPORT +void Java_Main_jitGc(JNIEnv*, jclass) { + CHECK(Runtime::Current()->GetJit() != nullptr); + jit::JitCodeCache* cache = Runtime::Current()->GetJit()->GetCodeCache(); + ScopedObjectAccess soa(Thread::Current()); + cache->GarbageCollectCache(Thread::Current()); +} + +extern "C" JNIEXPORT +jboolean Java_Main_isNextJitGcFull(JNIEnv*, jclass) { + ScopedObjectAccess soa(Thread::Current()); + return JitJniStubTestHelper::isNextJitGcFull(soa.Self()); +} + +} // namespace art diff --git a/test/667-jit-jni-stub/run b/test/667-jit-jni-stub/run new file mode 100755 index 0000000000000000000000000000000000000000..f235c6bc90ec50c60bc817347eb1b6d02a3e08ca --- /dev/null +++ b/test/667-jit-jni-stub/run @@ -0,0 +1,19 @@ +#!/bin/bash +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Disable AOT compilation of JNI stubs. +# Ensure this test is not subject to unexpected code collection. +${RUN} "${@}" --no-prebuild --no-dex2oat --runtime-option -Xjitinitialsize:32M diff --git a/test/667-jit-jni-stub/src/Main.java b/test/667-jit-jni-stub/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..794308d6e102595e941edccc2eb8a50f1ec9be73 --- /dev/null +++ b/test/667-jit-jni-stub/src/Main.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public static void main(String[] args) throws Exception { + System.loadLibrary(args[0]); + if (isAotCompiled(Main.class, "hasJit")) { + throw new Error("This test must be run with --no-prebuild --no-dex2oat!"); + } + if (!hasJit()) { + return; + } + + testCompilationUseAndCollection(); + testMixedFramesOnStack(); + } + + public static void testCompilationUseAndCollection() { + // Test that callThrough() can be JIT-compiled. + assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); + assertFalse(hasJitCompiledCode(Main.class, "callThrough")); + ensureCompiledCallThroughEntrypoint(/* call */ true); + assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + + // Use callThrough() once again now that the method has a JIT-compiled stub. + callThrough(Main.class, "doNothing"); + + // Test that GC with the JIT-compiled stub on the stack does not collect it. + // Also tests stack walk over the JIT-compiled stub. + callThrough(Main.class, "testGcWithCallThroughStubOnStack"); + + // Test that, when marking used methods before a full JIT GC, a single execution + // of the GenericJNI trampoline can save the compiled stub from being collected. + testSingleInvocationTriggersRecompilation(); + + // Test that the JNI compiled stub can actually be collected. + testStubCanBeCollected(); + } + + public static void testGcWithCallThroughStubOnStack() { + // Check that this method was called via JIT-compiled callThrough() stub. + assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); + // This assertion also exercises stack walk over the JIT-compiled callThrough() stub. + assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough")); + + doJitGcsUntilFullJitGcIsScheduled(); + // The callThrough() on the stack above this method is using the compiled stub, + // so the JIT GC should not remove the compiled code. + jitGc(); + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + } + + public static void testSingleInvocationTriggersRecompilation() { + // After scheduling a full JIT GC, single call through the GenericJNI + // trampoline should ensure that the compiled stub is used again. + doJitGcsUntilFullJitGcIsScheduled(); + callThrough(Main.class, "doNothing"); + ensureCompiledCallThroughEntrypoint(/* call */ false); // Wait for the compilation task to run. + assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); + jitGc(); // This JIT GC should not collect the callThrough() stub. + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + } + + public static void testMixedFramesOnStack() { + // Starts without a compiled JNI stub for callThrough(). + assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); + assertFalse(hasJitCompiledCode(Main.class, "callThrough")); + callThrough(Main.class, "testMixedFramesOnStackStage2"); + // We have just returned through the JIT-compiled JNI stub, so it must still + // be compiled (though not necessarily with the entrypoint pointing to it). + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + // Though the callThrough() is on the stack, that frame is using the GenericJNI + // and does not prevent the collection of the JNI stub. + testStubCanBeCollected(); + } + + public static void testMixedFramesOnStackStage2() { + // We cannot assert that callThrough() has no JIT compiled stub as that check + // may race against the compilation task. Just check the caller. + assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough")); + // Now ensure that the JNI stub is compiled and used. + ensureCompiledCallThroughEntrypoint(/* call */ true); + callThrough(Main.class, "testMixedFramesOnStackStage3"); + } + + public static void testMixedFramesOnStackStage3() { + // Check that this method was called via JIT-compiled callThrough() stub. + assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); + // This assertion also exercises stack walk over the JIT-compiled callThrough() stub. + assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough")); + // For a good measure, try a JIT GC. + jitGc(); + } + + public static void testStubCanBeCollected() { + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + doJitGcsUntilFullJitGcIsScheduled(); + assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + jitGc(); // JIT GC without callThrough() on the stack should collect the callThrough() stub. + assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); + assertFalse(hasJitCompiledCode(Main.class, "callThrough")); + } + + public static void doJitGcsUntilFullJitGcIsScheduled() { + // We enter with a compiled stub for callThrough() but we also need the entrypoint to be set. + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + ensureCompiledCallThroughEntrypoint(/* call */ true); + // Perform JIT GC until the next GC is marked to do full collection. + do { + assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); + callThrough(Main.class, "jitGc"); // JIT GC with callThrough() safely on the stack. + } while (!isNextJitGcFull()); + // The JIT GC before the full collection resets entrypoints and waits to see + // if the methods are still in use. + assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough")); + assertTrue(hasJitCompiledCode(Main.class, "callThrough")); + } + + public static void ensureCompiledCallThroughEntrypoint(boolean call) { + int count = 0; + while (!hasJitCompiledEntrypoint(Main.class, "callThrough")) { + // If `call` is true, also exercise the `callThrough()` method to increase hotness. + // Ramp-up the number of calls we do up to 1 << 12. + final int rampUpCutOff = 12; + int limit = call ? 1 << Math.min(count, rampUpCutOff) : 0; + for (int i = 0; i < limit; ++i) { + callThrough(Main.class, "doNothing"); + } + try { + // Sleep to give a chance for the JIT to compile `callThrough` stub. + // After the ramp-up phase, give the JIT even more time to compile. + Thread.sleep(count >= rampUpCutOff ? 200 : 100); + } catch (Exception e) { + // Ignore + } + if (++count == 50) { + throw new Error("TIMEOUT"); + } + }; + } + + public static void assertTrue(boolean value) { + if (!value) { + throw new AssertionError("Expected true!"); + } + } + + public static void assertFalse(boolean value) { + if (value) { + throw new AssertionError("Expected false!"); + } + } + + public static void doNothing() { } + public static void throwError() { throw new Error(); } + + // Note that the callThrough()'s shorty differs from shorties of the other + // native methods used in this test because of the return type `void.` + public native static void callThrough(Class cls, String methodName); + + public native static void jitGc(); + public native static boolean isNextJitGcFull(); + + public native static boolean isAotCompiled(Class cls, String methodName); + public native static boolean hasJitCompiledEntrypoint(Class cls, String methodName); + public native static boolean hasJitCompiledCode(Class cls, String methodName); + private native static boolean hasJit(); +} diff --git a/test/669-checker-break/expected.txt b/test/669-checker-break/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..b0aad4deb5bb3fb6b422e222ec14bf5e4b99babe --- /dev/null +++ b/test/669-checker-break/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/669-checker-break/info.txt b/test/669-checker-break/info.txt new file mode 100644 index 0000000000000000000000000000000000000000..3408b3b238904b18e56b830144738dcfd7b59a5a --- /dev/null +++ b/test/669-checker-break/info.txt @@ -0,0 +1 @@ +Test optimizations of "break" loops. diff --git a/test/669-checker-break/src/Main.java b/test/669-checker-break/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..e59061b1aa5382c294c4b1f143222c9ad5bfb386 --- /dev/null +++ b/test/669-checker-break/src/Main.java @@ -0,0 +1,328 @@ +/* + * 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. + */ + +/** + * Tests for optimizations of break-loops, i.e. loops that break + * out of a while-true loop when the end condition is satisfied. + * In particular, the tests focus on break-loops that can be + * rewritten into regular countable loops (this may improve certain + * loops generated by the Kotlin compiler for inclusive ranges). + */ +public class Main { + + /// CHECK-START: int Main.breakLoop(int[]) induction_var_analysis (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> NullCheck [<>] loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> NotEqual [{{i\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START: int Main.breakLoop(int[]) induction_var_analysis (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> NullCheck [<>] loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> LessThanOrEqual [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-ARM64: int Main.breakLoop(int[]) loop_optimization (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 4 loop:none + /// CHECK-DAG: <> NullCheck [<>] loop:none + /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none + /// CHECK-DAG: VecStore [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + static int breakLoop(int[] a) { + int l = 0; + int u = a.length - 1; + int i = l; + if (l <= u) { + while (true) { + a[i] = 1; + if (i == u) break; + i++; + } + } + return i; + } + + /// CHECK-START: int Main.breakLoopDown(int[]) induction_var_analysis (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant -1 loop:none + /// CHECK-DAG: <> IntConstant 2 loop:none + /// CHECK-DAG: <> NullCheck [<>] loop:none + /// CHECK-DAG: <> Phi [{{i\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> NotEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START: int Main.breakLoopDown(int[]) induction_var_analysis (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant -1 loop:none + /// CHECK-DAG: <> IntConstant 2 loop:none + /// CHECK-DAG: <> NullCheck [<>] loop:none + /// CHECK-DAG: <> Phi [{{i\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: <> GreaterThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + static int breakLoopDown(int[] a) { + int l = 0; + int u = a.length - 1; + int i = u; + if (u >= l) { + while (true) { + a[i] = 2; + if (i == l) break; + i--; + } + } + return i; + } + + /// CHECK-START: int Main.breakLoopSafeConst(int[]) induction_var_analysis (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 3 loop:none + /// CHECK-DAG: <> IntConstant 2147483631 loop:none + /// CHECK-DAG: <> IntConstant 2147483646 loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> NullCheck [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> NotEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START: int Main.breakLoopSafeConst(int[]) induction_var_analysis (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 3 loop:none + /// CHECK-DAG: <> IntConstant 2147483631 loop:none + /// CHECK-DAG: <> IntConstant 2147483646 loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> LessThanOrEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> NullCheck [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START-ARM64: int Main.breakLoopSafeConst(int[]) loop_optimization (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 3 loop:none + /// CHECK-DAG: <> IntConstant 4 loop:none + /// CHECK-DAG: <> VecReplicateScalar [<>] loop:none + /// CHECK-DAG: VecStore [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: Add [<>,<>] loop:<> outer_loop:none + static int breakLoopSafeConst(int[] a) { + int l = Integer.MAX_VALUE - 16; + int u = Integer.MAX_VALUE - 1; + int i = l; + if (l <= u) { // will be removed by simplifier + while (true) { + a[i - l] = 3; + if (i == u) break; + i++; + } + } + return i; + } + + /// CHECK-START: int Main.breakLoopUnsafeConst(int[]) induction_var_analysis (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 4 loop:none + /// CHECK-DAG: <> IntConstant 2147483632 loop:none + /// CHECK-DAG: <> IntConstant 2147483647 loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Sub [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> NullCheck [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> NotEqual [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + // + /// CHECK-START: int Main.breakLoopUnsafeConst(int[]) induction_var_analysis (after) + /// CHECK-DAG: NotEqual + /// CHECK-NOT: LessThanOrEqual + static int breakLoopUnsafeConst(int[] a) { + int l = Integer.MAX_VALUE - 15; + int u = Integer.MAX_VALUE; + int i = l; + if (l <= u) { // will be removed by simplifier + while (true) { + a[i - l] = 4; + if (i == u) break; // rewriting exit not safe! + i++; + } + } + return i; + } + + /// CHECK-START: int Main.breakLoopNastyPhi(int[]) induction_var_analysis (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> IntConstant 5 loop:none + /// CHECK-DAG: <> IntConstant -123 loop:none + /// CHECK-DAG: <> NullCheck [<>] loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: ArraySet [<>,<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> NotEqual [{{i\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:none + /// CHECK-DAG: Return [<>] loop:none + // + /// CHECK-START: int Main.breakLoopNastyPhi(int[]) induction_var_analysis (after) + /// CHECK-DAG: NotEqual + /// CHECK-NOT: LessThanOrEqual + static int breakLoopNastyPhi(int[] a) { + int l = 0; + int u = a.length - 1; + int x = -123; + if (l <= u) { + int i = l; + while (true) { + a[i] = 5; + if (i == u) break; + x = i; + i++; + } + } + return x; // keep another phi live + } + + /// CHECK-START: int Main.breakLoopReduction(int[]) induction_var_analysis (before) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> NullCheck [<>] loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> NotEqual [{{i\d+}},<>] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:none + /// CHECK-DAG: Return [<>] loop:none + // + /// CHECK-START: int Main.breakLoopReduction(int[]) induction_var_analysis (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> IntConstant 1 loop:none + /// CHECK-DAG: <> NullCheck [<>] loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> LessThanOrEqual [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: If [<>] loop:<> outer_loop:none + /// CHECK-DAG: <> BoundsCheck [<>,{{i\d+}}] loop:<> outer_loop:none + /// CHECK-DAG: <> ArrayGet [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Add [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:none + /// CHECK-DAG: Return [<>] loop:none + // + /// CHECK-START-ARM64: int Main.breakLoopReduction(int[]) loop_optimization (after) + /// CHECK-DAG: <> ParameterValue loop:none + /// CHECK-DAG: <> IntConstant 0 loop:none + /// CHECK-DAG: <> VecSetScalars [<>] loop:none + /// CHECK-DAG: <> Phi [<>,<>] loop:<> outer_loop:none + /// CHECK-DAG: <> VecLoad loop:<> outer_loop:none + /// CHECK-DAG: <> VecAdd [<>,<>] loop:<> outer_loop:none + static int breakLoopReduction(int[] a) { + int l = 0; + int u = a.length - 1; + int x = 0; + if (l <= u) { + int i = l; + while (true) { + x += a[i]; + if (i == u) break; + i++; + } + } + return x; + } + + // + // Test driver. + // + + public static void main(String[] args) { + int[] a = new int[100]; + + expectEquals(99, breakLoop(a)); + for (int i = 0; i < a.length; i++) { + expectEquals(1, a[i]); + } + + expectEquals(0, breakLoopDown(a)); + for (int i = 0; i < a.length; i++) { + expectEquals(2, a[i]); + } + + expectEquals(Integer.MAX_VALUE - 1, breakLoopSafeConst(a)); + for (int i = 0; i < a.length; i++) { + int e = i < 16 ? 3 : 2; + expectEquals(e, a[i]); + } + + expectEquals(Integer.MAX_VALUE, breakLoopUnsafeConst(a)); + for (int i = 0; i < a.length; i++) { + int e = i < 16 ? 4 : 2; + expectEquals(e, a[i]); + } + + expectEquals(98, breakLoopNastyPhi(a)); + for (int i = 0; i < a.length; i++) { + expectEquals(5, a[i]); + } + + expectEquals(500, breakLoopReduction(a)); + + System.out.println("passed"); + } + + private static void expectEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } +} diff --git a/test/669-moveable-string-class-equals/expected.txt b/test/669-moveable-string-class-equals/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..6a5618ebc67de22d67fae3dfcd84e929be352659 --- /dev/null +++ b/test/669-moveable-string-class-equals/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/669-moveable-string-class-equals/info.txt b/test/669-moveable-string-class-equals/info.txt new file mode 100644 index 0000000000000000000000000000000000000000..1d3202ef469c8e3ef0bd20b6d41946eda9d92cab --- /dev/null +++ b/test/669-moveable-string-class-equals/info.txt @@ -0,0 +1,2 @@ +Regression test for String.equals() intrinsic instanceof check +when the String.class is moveable. diff --git a/test/669-moveable-string-class-equals/run b/test/669-moveable-string-class-equals/run new file mode 100755 index 0000000000000000000000000000000000000000..7c74d8ca0ea6348dbdeca54cda84d6aaf234c78d --- /dev/null +++ b/test/669-moveable-string-class-equals/run @@ -0,0 +1,19 @@ +#!/bin/bash +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Run without image, so that String.class is moveable. +# Reduce heap size to force more frequent GCs. +${RUN} --no-image --runtime-option -Xmx16m "$@" diff --git a/test/669-moveable-string-class-equals/src/Main.java b/test/669-moveable-string-class-equals/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..d182d51a2567685fbb79252c0dde1246d22b21d6 --- /dev/null +++ b/test/669-moveable-string-class-equals/src/Main.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public static void main(String[] args) { + System.loadLibrary(args[0]); + if (!hasJit()) { + // Make the test pass if not using JIT. + return; + } + if (hasImage()) { + throw new Error("The `run` script should prevent this test from running with an image!"); + } + if (!isClassMoveable(String.class)) { + throw new Error("String.class not moveable despite running without image!"); + } + + // Make sure the Main.test() is JIT-compiled and then call it. + ensureJitCompiled(Main.class, "test"); + test(); + } + + public static void test() { + int length = 5; + + // Hide the type of these strings in an Object array, + // so that we treat them as Object for the String.equals() below. + Object[] array = new Object[length]; + for (int i = 0; i != length; ++i) { + array[i] = "V" + i; + } + + // Continually check string equality between a newly allocated String and an + // already allocated String with the same contents while allocating over 128MiB + // memory (with heap size limited to 16MiB), ensuring we run GC and stress the + // instanceof check in the String.equals() implementation. + for (int count = 0; count != 128 * 1024; ++count) { + for (int i = 0; i != length; ++i) { + allocateAtLeast1KiB(); + assertTrue(("V" + i).equals(array[i])); + } + } + } + + public static void allocateAtLeast1KiB() { + // Give GC more work by allocating Object arrays. + memory[allocationIndex] = new Object[1024 / 4]; + ++allocationIndex; + if (allocationIndex == memory.length) { + allocationIndex = 0; + } + } + + public static void assertTrue(boolean value) { + if (!value) { + throw new Error("Assertion failed!"); + } + } + + private native static boolean hasJit(); + private native static boolean hasImage(); + private native static boolean isClassMoveable(Class cls); + private static native void ensureJitCompiled(Class itf, String method_name); + + // We shall retain some allocated memory and release old allocations + // so that the GC has something to do. + public static Object[] memory = new Object[4096]; + public static int allocationIndex = 0; +} diff --git a/test/671-npe-field-opts/expected.txt b/test/671-npe-field-opts/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/test/671-npe-field-opts/info.txt b/test/671-npe-field-opts/info.txt new file mode 100644 index 0000000000000000000000000000000000000000..f1e584633f52181768022cf6eab5c084461b53c9 --- /dev/null +++ b/test/671-npe-field-opts/info.txt @@ -0,0 +1,3 @@ +Regression test for the compiler, which used to +re-order or remove field access in a way that would confuse the runtime +when validating a NPE. diff --git a/test/671-npe-field-opts/src/Main.java b/test/671-npe-field-opts/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..a5e81ce672d61529be8e5a468bfc45be7354e986 --- /dev/null +++ b/test/671-npe-field-opts/src/Main.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + static Main obj; + // Make 'doCheck' volatile to prevent optimizations + // in $noinline$bar like LICM that could hoist the null check + // out of the loop. + static volatile boolean doCheck = true; + + float floatField; + int intField; + + public static void main(String[] args) { + try { + $noinline$bar(); + throw new Error("Expected NPE"); + } catch (NullPointerException e) { + check(e, 29, 52, "$noinline$bar"); + } + + try { + $noinline$foo(); + throw new Error("Expected NPE"); + } catch (NullPointerException e) { + check(e, 36, 44, "$noinline$foo"); + } + } + + public static float $noinline$foo() { + int v1 = obj.intField; + float v2 = obj.floatField; + return v2; + } + + public static float $noinline$bar() { + float a = 0; + while (doCheck) { + float f = obj.floatField; + int i = obj.intField; + a = (float)i + f; + } + return a; + } + + static void check(NullPointerException npe, int mainLine, int methodLine, String methodName) { + StackTraceElement[] trace = npe.getStackTrace(); + checkElement(trace[0], "Main", methodName, "Main.java", methodLine); + checkElement(trace[1], "Main", "main", "Main.java", mainLine); + } + + static void checkElement(StackTraceElement element, + String declaringClass, String methodName, + String fileName, int lineNumber) { + assertEquals(declaringClass, element.getClassName()); + assertEquals(methodName, element.getMethodName()); + assertEquals(fileName, element.getFileName()); + assertEquals(lineNumber, element.getLineNumber()); + } + + static void assertEquals(Object expected, Object actual) { + if (!expected.equals(actual)) { + String msg = "Expected \"" + expected + "\" but got \"" + actual + "\""; + throw new AssertionError(msg); + } + } + + static void assertEquals(int expected, int actual) { + if (expected != actual) { + throw new AssertionError("Expected " + expected + " got " + actual); + } + } +} diff --git a/test/706-checker-scheduler/src/Main.java b/test/706-checker-scheduler/src/Main.java index d21596d4bc7aa9f9c2babcee7ad8570f4168c224..25e4fad714c982ae2c0bbddebbf361044d99fdc3 100644 --- a/test/706-checker-scheduler/src/Main.java +++ b/test/706-checker-scheduler/src/Main.java @@ -523,7 +523,71 @@ public class Main { return res; } + private static void expectEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + private static final int ARRAY_SIZE = 32; + + // Check that VecReplicateScalar is not reordered. + /// CHECK-START-ARM64: void Main.testVecReplicateScalar() scheduler (before) + /// CHECK: Phi loop:<> outer_loop:none + /// CHECK: NewArray loop:<> outer_loop:none + /// CHECK: VecReplicateScalar loop:<> outer_loop:none + + /// CHECK-START-ARM64: void Main.testVecReplicateScalar() scheduler (after) + /// CHECK: Phi loop:<> outer_loop:none + /// CHECK: NewArray loop:<> outer_loop:none + /// CHECK: VecReplicateScalar loop:<> outer_loop:none + private static void testVecReplicateScalar() { + for (int j = 0; j <= 8; j++) { + int[] a = new int[ARRAY_SIZE]; + for (int i = 0; i < a.length; i++) { + a[i] += 1; + } + for (int i = 0; i < a.length; i++) { + expectEquals(1, a[i]); + } + } + } + + // Check that VecSetScalars, VecReduce, VecExtractScalar are not reordered. + /// CHECK-START-ARM64: void Main.testVecSetScalars() scheduler (before) + /// CHECK: Phi loop:<> outer_loop:none + /// CHECK: NewArray loop:<> outer_loop:none + /// CHECK: VecSetScalars loop:<> outer_loop:none + // + /// CHECK: VecReduce loop:<> outer_loop:none + /// CHECK: VecExtractScalar loop:<> outer_loop:none + /// CHECK: InvokeStaticOrDirect loop:<> outer_loop:none + /// CHECK: InvokeStaticOrDirect loop:<> outer_loop:none + + /// CHECK-START-ARM64: void Main.testVecSetScalars() scheduler (after) + /// CHECK: Phi loop:<> outer_loop:none + /// CHECK: NewArray loop:<> outer_loop:none + /// CHECK: VecSetScalars loop:<> outer_loop:none + // + /// CHECK: VecReduce loop:<> outer_loop:none + /// CHECK: VecExtractScalar loop:<> outer_loop:none + /// CHECK: InvokeStaticOrDirect loop:<> outer_loop:none + /// CHECK: InvokeStaticOrDirect loop:<> outer_loop:none + private static void testVecSetScalars() { + for (int j = 0; j <= 8; j++) { + int[] a = new int[ARRAY_SIZE]; + int s = 5; + for (int i = 0; i < ARRAY_SIZE; i++) { + s+=a[i]; + } + expectEquals(a[0], 0); + expectEquals(s, 5); + } + } + public static void main(String[] args) { + testVecSetScalars(); + testVecReplicateScalar(); if ((arrayAccess() + intDiv(10)) != -35) { System.out.println("FAIL"); } diff --git a/test/706-jit-skip-compilation/info.txt b/test/706-jit-skip-compilation/info.txt deleted file mode 100644 index e9ef86bfb305f2858f68052864abfeec5afc7d5d..0000000000000000000000000000000000000000 --- a/test/706-jit-skip-compilation/info.txt +++ /dev/null @@ -1,4 +0,0 @@ -Regression test for the JIT crashing when compiling a method with invalid -dead dex code. For not compilable methods we don't gather samples and we don't -trigger JIT compilation. However kAccDontBotherCompile is not persisted in the -oat file and so we may end up compiling a method which we shouldn't. diff --git a/test/706-jit-skip-compilation/smali/errclass.smali b/test/706-jit-skip-compilation/smali/errclass.smali deleted file mode 100644 index 410504cb2f76532b552ca185b08f66f2b5500f72..0000000000000000000000000000000000000000 --- a/test/706-jit-skip-compilation/smali/errclass.smali +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (C) 2016 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -.class public LErrClass; - -.super Ljava/lang/Object; - -.method public static errMethod()J - .registers 8 - const/4 v0, 0x0 - const/4 v3, 0x0 - aget v1, v0, v3 # v0 is null, this will alays throw and the invalid code - # below will not be verified. - move v3, v4 - move-wide/from16 v6, v2 # should trigger a verification error if verified as - # v3 is a single register but used as a pair here. - return v6 -.end method - -# Add a field to work around demerger bug b/18051191. -# Failure to verify dex file '...': Offset(552) should be zero when size is zero for field-ids. -.field private a:I diff --git a/test/706-jit-skip-compilation/src/Main.java b/test/706-jit-skip-compilation/src/Main.java deleted file mode 100644 index aa847248d68ccc5453600c9d44ed27f57f81dfde..0000000000000000000000000000000000000000 --- a/test/706-jit-skip-compilation/src/Main.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -public class Main { - public static void main(String[] args) throws Exception { - System.loadLibrary(args[0]); - Class c = Class.forName("ErrClass"); - Method m = c.getMethod("errMethod"); - - // Print the counter before invokes. The golden file expects this to be 0. - int hotnessCounter = getHotnessCounter(c, "errMethod"); - if (hotnessCounter != 0) { - throw new RuntimeException("Unexpected hotnessCounter: " + hotnessCounter); - } - - // Loop enough to make sure the interpreter reports invocations count. - long result = 0; - for (int i = 0; i < 10000; i++) { - try { - result += (Long)m.invoke(null); - hotnessCounter = getHotnessCounter(c, "errMethod"); - if (hotnessCounter != 0) { - throw new RuntimeException( - "Unexpected hotnessCounter: " + hotnessCounter); - } - - } catch (InvocationTargetException e) { - if (!(e.getCause() instanceof NullPointerException)) { - throw e; - } - } - } - - // Not compilable methods should not increase their hotness counter. - if (hotnessCounter != 0) { - throw new RuntimeException("Unexpected hotnessCounter: " + hotnessCounter); - } - } - - public static native int getHotnessCounter(Class cls, String method_name); -} diff --git a/test/711-checker-type-conversion/expected.txt b/test/711-checker-type-conversion/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/test/711-checker-type-conversion/info.txt b/test/711-checker-type-conversion/info.txt new file mode 100644 index 0000000000000000000000000000000000000000..5b63572a76a56ab7e3902267fb9813536e053edc --- /dev/null +++ b/test/711-checker-type-conversion/info.txt @@ -0,0 +1 @@ +Tests for type conversion elimination. diff --git a/test/711-checker-type-conversion/src/Main.java b/test/711-checker-type-conversion/src/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..ae58200b1b3b6814e0f3113b415d122f2b4a248e --- /dev/null +++ b/test/711-checker-type-conversion/src/Main.java @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + + public static void assertByteEquals(byte expected, byte result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void assertShortEquals(short expected, short result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void assertIntEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void assertLongEquals(long expected, long result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void assertCharEquals(char expected, char result) { + if (expected != result) { + // Values are cast to int to display numeric values instead of + // (UTF-16 encoded) characters. + throw new Error("Expected: " + (int)expected + ", found: " + (int)result); + } + } + + /// CHECK-START: byte Main.getByte1() constant_folding (before) + /// CHECK: TypeConversion + /// CHECK: TypeConversion + /// CHECK: Add + /// CHECK: TypeConversion + + /// CHECK-START: byte Main.getByte1() constant_folding (after) + /// CHECK-NOT: TypeConversion + /// CHECK-NOT: Add + + static byte getByte1() { + int i = -2; + int j = -3; + return (byte)((byte)i + (byte)j); + } + + /// CHECK-START: byte Main.getByte2() constant_folding (before) + /// CHECK: TypeConversion + /// CHECK: TypeConversion + /// CHECK: Add + /// CHECK: TypeConversion + + /// CHECK-START: byte Main.getByte2() constant_folding (after) + /// CHECK-NOT: TypeConversion + /// CHECK-NOT: Add + + static byte getByte2() { + int i = -100; + int j = -101; + return (byte)((byte)i + (byte)j); + } + + /// CHECK-START: byte Main.getByte3() constant_folding (before) + /// CHECK: TypeConversion + /// CHECK: TypeConversion + /// CHECK: Add + /// CHECK: TypeConversion + + /// CHECK-START: byte Main.getByte2() constant_folding (after) + /// CHECK-NOT: TypeConversion + /// CHECK-NOT: Add + + static byte getByte3() { + long i = 0xabcdabcdabcdL; + return (byte)((byte)i + (byte)i); + } + + static byte byteVal = -1; + static short shortVal = -1; + static char charVal = 0xffff; + static int intVal = -1; + + static byte[] byteArr = { 0 }; + static short[] shortArr = { 0 }; + static char[] charArr = { 0 }; + static int[] intArr = { 0 }; + + static byte $noinline$getByte() { + return byteVal; + } + + static short $noinline$getShort() { + return shortVal; + } + + static char $noinline$getChar() { + return charVal; + } + + static int $noinline$getInt() { + return intVal; + } + + static boolean sFlag = true; + + /// CHECK-START: void Main.byteToShort() instruction_simplifier$before_codegen (after) + /// CHECK-NOT: TypeConversion + private static void byteToShort() { + shortArr[0] = 0; + if (sFlag) { + shortArr[0] = $noinline$getByte(); + } + } + + /// CHECK-START: void Main.byteToChar() instruction_simplifier$before_codegen (after) + /// CHECK: TypeConversion + private static void byteToChar() { + charArr[0] = 0; + if (sFlag) { + charArr[0] = (char)$noinline$getByte(); + } + } + + /// CHECK-START: void Main.byteToInt() instruction_simplifier$before_codegen (after) + /// CHECK-NOT: TypeConversion + private static void byteToInt() { + intArr[0] = 0; + if (sFlag) { + intArr[0] = $noinline$getByte(); + } + } + + /// CHECK-START: void Main.charToByte() instruction_simplifier$before_codegen (after) + /// CHECK-NOT: TypeConversion + private static void charToByte() { + byteArr[0] = 0; + if (sFlag) { + byteArr[0] = (byte)$noinline$getChar(); + } + } + + /// CHECK-START: void Main.charToShort() instruction_simplifier$before_codegen (after) + /// CHECK-NOT: TypeConversion + private static void charToShort() { + shortArr[0] = 0; + if (sFlag) { + shortArr[0] = (short)$noinline$getChar(); + } + } + + /// CHECK-START: void Main.charToInt() instruction_simplifier$before_codegen (after) + /// CHECK-NOT: TypeConversion + private static void charToInt() { + intArr[0] = 0; + if (sFlag) { + intArr[0] = $noinline$getChar(); + } + } + + /// CHECK-START: void Main.shortToByte() instruction_simplifier$before_codegen (after) + /// CHECK-NOT: TypeConversion + private static void shortToByte() { + byteArr[0] = 0; + if (sFlag) { + byteArr[0] = (byte)$noinline$getShort(); + } + } + + /// CHECK-START: void Main.shortToChar() instruction_simplifier$before_codegen (after) + /// CHECK-NOT: TypeConversion + private static void shortToChar() { + charArr[0] = 0; + if (sFlag) { + charArr[0] = (char)$noinline$getShort(); + } + } + + /// CHECK-START: void Main.shortToInt() instruction_simplifier$before_codegen (after) + /// CHECK-NOT: TypeConversion + private static void shortToInt() { + intArr[0] = 0; + if (sFlag) { + intArr[0] = $noinline$getShort(); + } + } + + /// CHECK-START: void Main.intToByte() instruction_simplifier$before_codegen (after) + /// CHECK-NOT: TypeConversion + private static void intToByte() { + byteArr[0] = 0; + if (sFlag) { + byteArr[0] = (byte)$noinline$getInt(); + } + } + + /// CHECK-START: void Main.intToShort() instruction_simplifier$before_codegen (after) + /// CHECK-NOT: TypeConversion + private static void intToShort() { + shortArr[0] = 0; + if (sFlag) { + shortArr[0] = (short)$noinline$getInt(); + } + } + + /// CHECK-START: void Main.intToChar() instruction_simplifier$before_codegen (after) + /// CHECK-NOT: TypeConversion + private static void intToChar() { + charArr[0] = 0; + if (sFlag) { + charArr[0] = (char)$noinline$getInt(); + } + } + + public static void main(String[] args) { + assertByteEquals(getByte1(), (byte)-5); + assertByteEquals(getByte2(), (byte)(-201)); + assertByteEquals(getByte3(), (byte)(0xcd + 0xcd)); + + byteToShort(); + assertShortEquals(shortArr[0], (short)-1); + byteToChar(); + assertCharEquals(charArr[0], (char)-1); + byteToInt(); + assertIntEquals(intArr[0], -1); + charToByte(); + assertByteEquals(byteArr[0], (byte)-1); + charToShort(); + assertShortEquals(shortArr[0], (short)-1); + charToInt(); + assertIntEquals(intArr[0], 0xffff); + shortToByte(); + assertByteEquals(byteArr[0], (byte)-1); + shortToChar(); + assertCharEquals(charArr[0], (char)-1); + shortToInt(); + assertIntEquals(intArr[0], -1); + intToByte(); + assertByteEquals(byteArr[0], (byte)-1); + intToShort(); + assertShortEquals(shortArr[0], (short)-1); + intToChar(); + assertCharEquals(charArr[0], (char)-1); + } +} diff --git a/test/900-hello-plugin/load_unload.cc b/test/900-hello-plugin/load_unload.cc index 19312b4d71873d936ec919e2bf3da1beac221de7..cab0abf58b57d419acc7b4b3f83d48a0de1d95a9 100644 --- a/test/900-hello-plugin/load_unload.cc +++ b/test/900-hello-plugin/load_unload.cc @@ -17,9 +17,10 @@ #include #include +#include +#include + #include "art_method-inl.h" -#include "base/logging.h" -#include "base/macros.h" #include "java_vm_ext.h" #include "runtime.h" diff --git a/test/901-hello-ti-agent/basics.cc b/test/901-hello-ti-agent/basics.cc index 11b32e46c3122c19a1e05322268c5bfc3a20b49d..472f2b768eb93216332cb72a9d984f5b91a1f16d 100644 --- a/test/901-hello-ti-agent/basics.cc +++ b/test/901-hello-ti-agent/basics.cc @@ -16,7 +16,7 @@ #include "901-hello-ti-agent/basics.h" -#include // NOLINT [build/c++11] [5] +#include #include #include diff --git a/test/904-object-allocation/tracking.cc b/test/904-object-allocation/tracking.cc index 81d1b2cda91c6460f925d8f25407218e023cdd96..9d2592a675459fe753e0a775edb189a48246e926 100644 --- a/test/904-object-allocation/tracking.cc +++ b/test/904-object-allocation/tracking.cc @@ -18,7 +18,7 @@ #include #include -#include // NOLINT [build/c++11] [5] +#include #include #include "android-base/logging.h" diff --git a/test/912-classes/classes.cc b/test/912-classes/classes.cc index a84747289f28ad69c995ac9ef89874fd0ca56df9..1f6954ef99bdea1647847d9e9f967b6f95452310 100644 --- a/test/912-classes/classes.cc +++ b/test/912-classes/classes.cc @@ -16,8 +16,8 @@ #include -#include // NOLINT [build/c++11] [5] -#include // NOLINT [build/c++11] [5] +#include +#include #include #include "android-base/macros.h" diff --git a/test/912-classes/classes_art.cc b/test/912-classes/classes_art.cc index 92a3ba0c97d5942e9d0be166ee871851a4edd29d..de2e456a53358891210a72753b789d0bdaf7b26e 100644 --- a/test/912-classes/classes_art.cc +++ b/test/912-classes/classes_art.cc @@ -16,7 +16,7 @@ #include -#include // NOLINT [build/c++11] [5] +#include #include #include "android-base/macros.h" diff --git a/test/913-heaps/check b/test/913-heaps/check index 835850004ab11481761da7815c62f1ef26098e98..f4892d0a077c23c504c927dd82f90329ac27c5db 100644 --- a/test/913-heaps/check +++ b/test/913-heaps/check @@ -14,9 +14,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Jack has a different set of bytecode offsets/method IDs in the expected.txt +# Jack/D8 has a different set of bytecode offsets/method IDs in the expected.txt + if [[ "$USE_JACK" == true ]]; then patch -p0 expected.txt < expected_jack.diff +elif [[ "$USE_D8" == true ]]; then + patch -p0 expected.txt < expected_d8.diff fi ./default-check "$@" diff --git a/test/913-heaps/expected_d8.diff b/test/913-heaps/expected_d8.diff new file mode 100644 index 0000000000000000000000000000000000000000..83694220a35ffed00890f2457e1c91ad1575341a --- /dev/null +++ b/test/913-heaps/expected_d8.diff @@ -0,0 +1,76 @@ +4,5c4 +< root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 30])--> 1@1000 [size=16, length=-1] +< root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=136, length=-1] +--- +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1] +49c48 +< root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=11,location= 8])--> 1@1000 [size=16, length=-1] +--- +> root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1] +51c50,51 +< root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1] +--- +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=5,location= 21])--> 1@1000 [size=16, length=-1] +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=8,location= 21])--> 1@1000 [size=16, length=-1] +102,103c102 +< root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 30])--> 1@1000 [size=16, length=-1] +< root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=136, length=-1] +--- +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1] +115c114 +< root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=11,location= 8])--> 1@1000 [size=16, length=-1] +--- +> root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1] +117c116,117 +< root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1] +--- +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=5,location= 21])--> 1@1000 [size=16, length=-1] +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=8,location= 21])--> 1@1000 [size=16, length=-1] +162c162 +< root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 30])--> 1@1000 [size=16, length=-1] +--- +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1] +177c177 +< root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=11,location= 8])--> 1@1000 [size=16, length=-1] +--- +> root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1] +179c179,180 +< root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1] +--- +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=5,location= 21])--> 1@1000 [size=16, length=-1] +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=8,location= 21])--> 1@1000 [size=16, length=-1] +201,202c202,203 +< root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 30])--> 1@1000 [size=16, length=-1] +< root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=136, length=-1] +--- +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1] +> root@root --(stack-local[id=1,tag=3000,depth=4,method=runFollowReferences,vreg=3,location= 164])--> 1000@0 [size=123456780050, length=-1] +246c247 +< root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=11,location= 8])--> 1@1000 [size=16, length=-1] +--- +> root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1] +248c249,251 +< root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1] +--- +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=5,location= 21])--> 1@1000 [size=16, length=-1] +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=8,location= 21])--> 1@1000 [size=16, length=-1] +> root@root --(stack-local[id=1,tag=3000,depth=4,method=runFollowReferences,vreg=3,location= 164])--> 1000@0 [size=123456780055, length=-1] +292c295 +< root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=136, length=-1] +--- +> root@root --(stack-local[id=1,tag=3000,depth=4,method=runFollowReferences,vreg=3,location= 181])--> 1000@0 [size=123456780060, length=-1] +319a323 +> root@root --(stack-local[id=1,tag=3000,depth=4,method=runFollowReferences,vreg=3,location= 181])--> 1000@0 [size=123456780065, length=-1] +347c351 +< root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 30])--> 1@1000 [size=16, length=-1] +--- +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1] +366c370 +< root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=11,location= 8])--> 1@1000 [size=16, length=-1] +--- +> root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1] +368c372,373 +< root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1] +--- +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=5,location= 21])--> 1@1000 [size=16, length=-1] +> root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=8,location= 21])--> 1@1000 [size=16, length=-1] diff --git a/test/924-threads/threads.cc b/test/924-threads/threads.cc index 8330179fbfae7f8d1ced938a463c982b75ed29ea..8caff768c1a4ba8bee4f3de723bfbe82de7aea77 100644 --- a/test/924-threads/threads.cc +++ b/test/924-threads/threads.cc @@ -16,7 +16,7 @@ #include -#include // NOLINT [build/c++11] [5] +#include #include #include diff --git a/test/936-search-onload/search_onload.cc b/test/936-search-onload/search_onload.cc index 90d87e0e7bd01394e2b2952d295c9ea6574159e5..23cea83be688f89174fc36f43d98976704de4dde 100644 --- a/test/936-search-onload/search_onload.cc +++ b/test/936-search-onload/search_onload.cc @@ -18,8 +18,9 @@ #include -#include "android-base/stringprintf.h" -#include "base/logging.h" +#include +#include + #include "base/macros.h" #include "jni.h" #include "jvmti.h" diff --git a/test/983-source-transform-verify/source_transform.cc b/test/983-source-transform-verify/source_transform.cc index ca3f88b347269988b45c1eab8fd49bf88b2dece9..3010d7a1a4c4aef20f59278e208d903d1f2a9507 100644 --- a/test/983-source-transform-verify/source_transform.cc +++ b/test/983-source-transform-verify/source_transform.cc @@ -25,7 +25,6 @@ #include "jni.h" #include "jvmti.h" -#include "base/logging.h" #include "base/macros.h" #include "bytecode_utils.h" #include "dex_file.h" diff --git a/test/AllFields/AllFields.java b/test/AllFields/AllFields.java index d5eac8fa2ec91daab1a652e371d77d976c8117e1..24f8ba1a0b1ef9f962c60d30a6b7ab6cda4cb498 100644 --- a/test/AllFields/AllFields.java +++ b/test/AllFields/AllFields.java @@ -14,7 +14,7 @@ * limitations under the License. */ -class AllFields { +public class AllFields { static boolean sZ; static byte sB; static char sC; diff --git a/test/AllFields/AllFieldsSub.java b/test/AllFields/AllFieldsSub.java new file mode 100644 index 0000000000000000000000000000000000000000..d5f933f88d7163ddd497bb128c6be336211cb709 --- /dev/null +++ b/test/AllFields/AllFieldsSub.java @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class AllFieldsSub extends AllFields { } diff --git a/test/AllFields/AllFieldsUnrelated.java b/test/AllFields/AllFieldsUnrelated.java new file mode 100644 index 0000000000000000000000000000000000000000..4db66b18866726babfef3423d794eeabbaa10f73 --- /dev/null +++ b/test/AllFields/AllFieldsUnrelated.java @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class AllFieldsUnrelated { } diff --git a/test/Android.bp b/test/Android.bp index 17ef1141dfbf28d9328ddf71e22758d36454331b..01e424d5e3ab6b052d4cb4c754928af78e0471aa 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -259,11 +259,15 @@ art_cc_defaults { "1932-monitor-events-misc/monitor_misc.cc", "1934-jvmti-signal-thread/signal_threads.cc", "1939-proxy-frames/local_instance.cc", + "1941-dispose-stress/dispose_stress.cc", ], shared_libs: [ "libbase", ], - header_libs: ["libopenjdkjvmti_headers"], + header_libs: [ + "libnativehelper_header_only", + "libopenjdkjvmti_headers", + ], include_dirs: ["art/test/ti-agent"], } @@ -282,6 +286,7 @@ art_cc_defaults { "912-classes/classes_art.cc", "936-search-onload/search_onload.cc", "983-source-transform-verify/source_transform.cc", + "1940-ddms-ext/ddm_ext.cc", ], } @@ -359,8 +364,9 @@ cc_defaults { "141-class-unload/jni_unload.cc", "148-multithread-gc-annotations/gc_coverage.cc", "149-suspend-all-stress/suspend_all.cc", - "203-multi-checkpoint/multi_checkpoint.cc", "154-gc-loop/heap_interface.cc", + "167-visit-locks/visit_locks.cc", + "203-multi-checkpoint/multi_checkpoint.cc", "454-get-vreg/get_vreg_jni.cc", "457-regs/regs_jni.cc", "461-get-reference-vreg/get_reference_vreg_jni.cc", @@ -379,6 +385,7 @@ cc_defaults { "656-annotation-lookup-generic-jni/test.cc", "661-oat-writer-layout/oat_writer_layout.cc", "664-aget-verifier/aget-verifier.cc", + "667-jit-jni-stub/jit_jni_stub_test.cc", "708-jit-cache-churn/jit.cc", ], shared_libs: [ diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 69e4b874f8003344ee0e1a9b8fdd5056ec69f1aa..fe4a327a1f6cff5739a458dc3ca388d69c36d05b 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -27,7 +27,7 @@ TEST_ART_RUN_TEST_DEPENDENCIES := \ # Add d8 dependency, if enabled. ifeq ($(USE_D8),true) TEST_ART_RUN_TEST_DEPENDENCIES += \ - $(HOST_OUT_EXECUTABLES)/d8 + $(HOST_OUT_EXECUTABLES)/d8-compat-dx endif # Convert's a rule name to the form used in variables, e.g. no-relocate to NO_RELOCATE @@ -40,46 +40,22 @@ endef # name-to-var TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_EXECUTABLES) $(TARGET_CORE_IMG_OUTS) # Also need libartagent. -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_ARCH)_libartagent) -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_ARCH)_libartagentd) -ifdef TARGET_2ND_ARCH -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_2ND_ARCH)_libartagent) -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_2ND_ARCH)_libartagentd) -endif +TEST_ART_TARGET_SYNC_DEPS += libartagent-target libartagentd-target # Also need libtiagent. -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_ARCH)_libtiagent) -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_ARCH)_libtiagentd) -ifdef TARGET_2ND_ARCH -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_2ND_ARCH)_libtiagent) -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_2ND_ARCH)_libtiagentd) -endif +TEST_ART_TARGET_SYNC_DEPS += libtiagent-target libtiagentd-target # Also need libtistress. -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_ARCH)_libtistress) -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_ARCH)_libtistressd) -ifdef TARGET_2ND_ARCH -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_2ND_ARCH)_libtistress) -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_2ND_ARCH)_libtistressd) -endif +TEST_ART_TARGET_SYNC_DEPS += libtistress-target libtistressd-target # Also need libarttest. -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_ARCH)_libarttest) -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_ARCH)_libarttestd) -ifdef TARGET_2ND_ARCH -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_2ND_ARCH)_libarttest) -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_2ND_ARCH)_libarttestd) -endif +TEST_ART_TARGET_SYNC_DEPS += libarttest-target libarttestd-target # Also need libnativebridgetest. -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_ARCH)_libnativebridgetest) -ifdef TARGET_2ND_ARCH -TEST_ART_TARGET_SYNC_DEPS += $(OUT_DIR)/$(ART_TEST_LIST_device_$(TARGET_2ND_ARCH)_libnativebridgetest) -endif +TEST_ART_TARGET_SYNC_DEPS += libnativebridgetest-target # Also need libopenjdkjvmti. -TEST_ART_TARGET_SYNC_DEPS += libopenjdkjvmti -TEST_ART_TARGET_SYNC_DEPS += libopenjdkjvmtid +TEST_ART_TARGET_SYNC_DEPS += libopenjdkjvmti-target libopenjdkjvmtid-target TEST_ART_TARGET_SYNC_DEPS += $(TARGET_OUT_JAVA_LIBRARIES)/core-libart-testdex.jar TEST_ART_TARGET_SYNC_DEPS += $(TARGET_OUT_JAVA_LIBRARIES)/core-oj-testdex.jar diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc index 60c7315b6fb43da35c4c56d85c06a7b9cab3898d..1eed80ec9111a2326fb8f0cf3f811bcfd07a3f91 100644 --- a/test/common/runtime_state.cc +++ b/test/common/runtime_state.cc @@ -16,9 +16,11 @@ #include "jni.h" +#include +#include + #include "art_method-inl.h" #include "base/enums.h" -#include "base/logging.h" #include "dex_file-inl.h" #include "instrumentation.h" #include "jit/jit.h" @@ -26,6 +28,7 @@ #include "jit/profiling_info.h" #include "mirror/class-inl.h" #include "nativehelper/ScopedUtfChars.h" +#include "oat_file.h" #include "oat_quick_method_header.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" @@ -151,10 +154,10 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isAotCompiled(JNIEnv* env, return method->GetOatMethodQuickCode(kRuntimePointerSize) != nullptr; } -extern "C" JNIEXPORT jboolean JNICALL Java_Main_isJitCompiled(JNIEnv* env, - jclass, - jclass cls, - jstring method_name) { +extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJitCompiledEntrypoint(JNIEnv* env, + jclass, + jclass cls, + jstring method_name) { jit::Jit* jit = GetJitIfEnabled(); if (jit == nullptr) { return false; @@ -168,6 +171,23 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isJitCompiled(JNIEnv* env, return jit->GetCodeCache()->ContainsPc(method->GetEntryPointFromQuickCompiledCode()); } +extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJitCompiledCode(JNIEnv* env, + jclass, + jclass cls, + jstring method_name) { + jit::Jit* jit = GetJitIfEnabled(); + if (jit == nullptr) { + return false; + } + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + ScopedUtfChars chars(env, method_name); + CHECK(chars.c_str() != nullptr); + ArtMethod* method = soa.Decode(cls)->FindDeclaredDirectMethodByName( + chars.c_str(), kRuntimePointerSize); + return jit->GetCodeCache()->ContainsMethod(method); +} + extern "C" JNIEXPORT void JNICALL Java_Main_ensureJitCompiled(JNIEnv* env, jclass, jclass cls, @@ -267,4 +287,13 @@ extern "C" JNIEXPORT void JNICALL Java_Main_fetchProfiles(JNIEnv*, jclass) { code_cache->GetProfiledMethods(unused_locations, unused_vector); } +extern "C" JNIEXPORT jboolean JNICALL Java_Main_isClassMoveable(JNIEnv*, + jclass, + jclass cls) { + Runtime* runtime = Runtime::Current(); + ScopedObjectAccess soa(Thread::Current()); + ObjPtr klass = soa.Decode(cls); + return runtime->GetHeap()->IsMovableObject(klass); +} + } // namespace art diff --git a/test/common/stack_inspect.cc b/test/common/stack_inspect.cc index 80a278012dc09143573ddb65b1699ac3be880b37..046b1fbcdd61b6b620052ff59e735509c1d42145 100644 --- a/test/common/stack_inspect.cc +++ b/test/common/stack_inspect.cc @@ -16,7 +16,9 @@ #include "jni.h" -#include "base/logging.h" +#include + +#include "base/mutex.h" #include "dex_file-inl.h" #include "jni_internal.h" #include "mirror/class-inl.h" diff --git a/test/etc/default-build b/test/etc/default-build index f14424ecf99d2ef75c99d315f4664e8fcd3b6531..5c8257f210c76e8fc5586510e14f8a1e6f9693a3 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -294,7 +294,7 @@ function make_dex() { local dexer="${DX}" if [ ${USE_D8} = "true" ]; then - dexer="${ANDROID_HOST_OUT}/bin/d8" + dexer="${ANDROID_HOST_OUT}/bin/d8-compat-dx" fi # Make dex file from desugared JAR. diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index 31f43fc5363517eea340d2f7529b93c6c9de91c0..4844d1e7586e896e5b75cbd5b4eab734c48152e7 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -184,6 +184,10 @@ while true; do elif [ "x$1" = "x--prebuild" ]; then PREBUILD="y" shift + elif [ "x$1" = "x--compact-dex-level" ]; then + shift + COMPILE_FLAGS="${COMPILE_FLAGS} --compact-dex-level=$1" + shift elif [ "x$1" = "x--jvmti-redefine-stress" ]; then # APP_IMAGE doesn't really work with jvmti redefine stress USE_JVMTI="y" diff --git a/test/knownfailures.json b/test/knownfailures.json index c6b6574f1b879a4eb9a3c5b77965741bd45bdd6b..dc051d9515421b4aaa30805e6224dcd8de2cb647 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -201,6 +201,12 @@ "time out."], "bug": "http://b/34369284" }, + { + "tests": "1940-ddms-ext", + "description": ["Test expects to be able to start tracing but we cannot", + "do that if tracing is already ongoing."], + "variant": "trace | stream" + }, { "tests": "137-cfi", "description": ["This test unrolls and expects managed frames, but", @@ -231,7 +237,8 @@ "tests": ["604-hot-static-interface", "612-jit-dex-cache", "613-inlining-dex-cache", - "626-set-resolved-string"], + "626-set-resolved-string", + "638-checker-inline-cache-intrinsic"], "variant": "trace | stream", "description": ["These tests expect JIT compilation, which is", "suppressed when tracing."] @@ -642,5 +649,11 @@ "tests": "661-oat-writer-layout", "variant": "interp-ac | interpreter | jit | no-dex2oat | no-prebuild | no-image | trace", "description": ["Test is designed to only check --compiler-filter=speed"] + }, + { + "tests": ["975-iface-private"], + "variant": "cdex-fast", + "description": ["CompactDex doesn't yet work with 975-iface private"] } + ] diff --git a/test/run-test b/test/run-test index fdb2ee47a78d1be5164be2c7df1b772428e641dc..75fe15c919d8120f0e81c0e2aef14b2ec4e27a63 100755 --- a/test/run-test +++ b/test/run-test @@ -225,6 +225,11 @@ while true; do run_args="${run_args} --prebuild" prebuild_mode="yes" shift; + elif [ "x$1" = "x--compact-dex-level" ]; then + option="$1" + shift + run_args="${run_args} $option $1" + shift; elif [ "x$1" = "x--strip-dex" ]; then run_args="${run_args} --strip-dex" shift; @@ -660,6 +665,7 @@ if [ "$usage" = "yes" ]; then echo " -Xcompiler-option Pass an option to the compiler." echo " --build-option Pass an option to the build script." echo " --runtime-option Pass an option to the runtime." + echo " --compact-dex-level Specify a compact dex level to the compiler." echo " --debug Wait for the default debugger to attach." echo " --debug-agent " echo " Wait for the given debugger agent to attach. Currently" diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py index 6d21442045009a57f68bc0420e5c2b800875de85..297ce08bee8c7e8185b1195550462396ebefcdbe 100644 --- a/test/testrunner/target_config.py +++ b/test/testrunner/target_config.py @@ -229,10 +229,13 @@ target_config = { }, 'art-heap-poisoning' : { 'run-test' : ['--interpreter', - '--optimizing'], + '--optimizing', + '--cdex-fast'], 'env' : { 'ART_USE_READ_BARRIER' : 'false', - 'ART_HEAP_POISONING' : 'true' + 'ART_HEAP_POISONING' : 'true', + # Get some extra automated testing coverage for compact dex. + 'ART_DEFAULT_COMPACT_DEX_LEVEL' : 'fast' } }, 'art-preopt' : { @@ -276,7 +279,9 @@ target_config = { 'make' : 'test-art-host-gtest', 'env': { 'ART_DEFAULT_GC_TYPE' : 'SS', - 'ART_USE_READ_BARRIER' : 'false' + 'ART_USE_READ_BARRIER' : 'false', + # Get some extra automated testing coverage for compact dex. + 'ART_DEFAULT_COMPACT_DEX_LEVEL' : 'fast' } }, 'art-gtest-gss-gc': { diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py index e7503827f2aec8a8335518326b54fedfbcd5922a..93998579f314ed505176554456543bef7e007db3 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -141,6 +141,7 @@ def gather_test_info(): VARIANT_TYPE_DICT['debuggable'] = {'ndebuggable', 'debuggable'} VARIANT_TYPE_DICT['gc'] = {'gcstress', 'gcverify', 'cms'} VARIANT_TYPE_DICT['prebuild'] = {'no-prebuild', 'no-dex2oat', 'prebuild'} + VARIANT_TYPE_DICT['cdex_level'] = {'cdex-none', 'cdex-fast'} VARIANT_TYPE_DICT['relocate'] = {'relocate-npatchoat', 'relocate', 'no-relocate'} VARIANT_TYPE_DICT['jni'] = {'jni', 'forcecopy', 'checkjni'} VARIANT_TYPE_DICT['address_sizes'] = {'64', '32'} @@ -183,6 +184,9 @@ def setup_test_env(): if not _user_input_variants['prebuild']: # Default _user_input_variants['prebuild'].add('prebuild') + if not _user_input_variants['cdex_level']: # Default + _user_input_variants['cdex_level'].add('cdex-none') + # By default only run without jvmti if not _user_input_variants['jvmti']: _user_input_variants['jvmti'].add('no-jvmti') @@ -339,10 +343,11 @@ def run_tests(tests): _user_input_variants['relocate'], _user_input_variants['trace'], _user_input_variants['gc'], _user_input_variants['jni'], _user_input_variants['image'], _user_input_variants['pictest'], - _user_input_variants['debuggable'], _user_input_variants['jvmti']) + _user_input_variants['debuggable'], _user_input_variants['jvmti'], + _user_input_variants['cdex_level']) for test, target, run, prebuild, compiler, relocate, trace, gc, \ - jni, image, pictest, debuggable, jvmti in config: + jni, image, pictest, debuggable, jvmti, cdex_level in config: for address_size in _user_input_variants['address_sizes_target'][target]: if stop_testrunner: # When ART_TEST_KEEP_GOING is set to false, then as soon as a test @@ -356,6 +361,7 @@ def run_tests(tests): test_name += target + '-run-test-' test_name += run + '-' test_name += prebuild + '-' + test_name += cdex_level + '-' test_name += compiler + '-' test_name += relocate + '-' test_name += trace + '-' @@ -369,7 +375,7 @@ def run_tests(tests): test_name += address_size variant_set = {target, run, prebuild, compiler, relocate, trace, gc, jni, - image, pictest, debuggable, jvmti, address_size} + image, pictest, debuggable, jvmti, cdex_level, address_size} options_test = options_all @@ -386,6 +392,9 @@ def run_tests(tests): elif prebuild == 'no-dex2oat': options_test += ' --no-prebuild --no-dex2oat' + # Add option and remove the cdex- prefix. + options_test += ' --compact-dex-level ' + cdex_level.replace('cdex-','') + if compiler == 'optimizing': options_test += ' --optimizing' elif compiler == 'regalloc_gc': @@ -806,6 +815,7 @@ def parse_test_name(test_name): regex += '(' + '|'.join(VARIANT_TYPE_DICT['pictest']) + ')-' regex += '(' + '|'.join(VARIANT_TYPE_DICT['debuggable']) + ')-' regex += '(' + '|'.join(VARIANT_TYPE_DICT['jvmti']) + ')-' + regex += '(' + '|'.join(VARIANT_TYPE_DICT['cdex_level']) + ')-' regex += '(' + '|'.join(RUN_TEST_SET) + ')' regex += '(' + '|'.join(VARIANT_TYPE_DICT['address_sizes']) + ')$' match = re.match(regex, test_name) @@ -822,8 +832,9 @@ def parse_test_name(test_name): _user_input_variants['pictest'].add(match.group(10)) _user_input_variants['debuggable'].add(match.group(11)) _user_input_variants['jvmti'].add(match.group(12)) - _user_input_variants['address_sizes'].add(match.group(14)) - return {match.group(13)} + _user_input_variants['cdex_level'].add(match.group(13)) + _user_input_variants['address_sizes'].add(match.group(15)) + return {match.group(14)} raise ValueError(test_name + " is not a valid test") @@ -874,7 +885,7 @@ def parse_option(): global run_all_configs parser = argparse.ArgumentParser(description="Runs all or a subset of the ART test suite.") - parser.add_argument('-t', '--test', dest='test', help='name of the test') + parser.add_argument('-t', '--test', action='append', dest='tests', help='name(s) of the test(s)') parser.add_argument('-j', type=int, dest='n_thread') parser.add_argument('--timeout', default=timeout, type=int, dest='timeout') for variant in TOTAL_VARIANTS_SET: @@ -906,10 +917,12 @@ def parse_option(): options = setup_env_for_build_target(target_config[options['build_target']], parser, options) - test = '' + tests = None env.EXTRA_DISABLED_TESTS.update(set(options['skips'])) - if options['test']: - test = parse_test_name(options['test']) + if options['tests']: + tests = set() + for test_name in options['tests']: + tests |= parse_test_name(test_name) for variant_type in VARIANT_TYPE_DICT: for variant in VARIANT_TYPE_DICT[variant_type]: @@ -935,11 +948,11 @@ def parse_option(): if options['run_all']: run_all_configs = True - return test + return tests def main(): gather_test_info() - user_requested_test = parse_option() + user_requested_tests = parse_option() setup_test_env() if build: build_targets = '' @@ -956,8 +969,8 @@ def main(): build_command += ' dist' if subprocess.call(build_command.split()): sys.exit(1) - if user_requested_test: - test_runner_thread = threading.Thread(target=run_tests, args=(user_requested_test,)) + if user_requested_tests: + test_runner_thread = threading.Thread(target=run_tests, args=(user_requested_tests,)) else: test_runner_thread = threading.Thread(target=run_tests, args=(RUN_TEST_SET,)) test_runner_thread.daemon = True diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc index d85f33a05df3d16d3e3518a337420886dbb35db8..9a7352e479724e0558680cf31800c719de7d2507 100644 --- a/test/ti-agent/common_load.cc +++ b/test/ti-agent/common_load.cc @@ -17,7 +17,8 @@ #include #include -#include "base/logging.h" +#include + #include "base/macros.h" #include "jni_binder.h" diff --git a/test/ti-agent/trace_helper.cc b/test/ti-agent/trace_helper.cc index 8b74c7c089c5027d673fe28a9c3d1e6ee57d9a68..bbc7754a4515b7c88129d7f7f37b8b65af0db764 100644 --- a/test/ti-agent/trace_helper.cc +++ b/test/ti-agent/trace_helper.cc @@ -39,6 +39,18 @@ struct TraceData { bool in_callback; bool access_watch_on_load; bool modify_watch_on_load; + jrawMonitorID trace_mon; + + jclass GetTestClass(jvmtiEnv* jvmti, JNIEnv* env) { + if (JvmtiErrorToException(env, jvmti, jvmti->RawMonitorEnter(trace_mon))) { + return nullptr; + } + jclass out = reinterpret_cast(env->NewLocalRef(test_klass)); + if (JvmtiErrorToException(env, jvmti, jvmti->RawMonitorExit(trace_mon))) { + return nullptr; + } + return out; + } }; static void threadStartCB(jvmtiEnv* jvmti, @@ -49,8 +61,12 @@ static void threadStartCB(jvmtiEnv* jvmti, jvmti->GetEnvironmentLocalStorage(reinterpret_cast(&data)))) { return; } + ScopedLocalRef klass(jnienv, data->GetTestClass(jvmti, jnienv)); + if (klass.get() == nullptr) { + return; + } CHECK(data->thread_start != nullptr); - jnienv->CallStaticVoidMethod(data->test_klass, data->thread_start, thread); + jnienv->CallStaticVoidMethod(klass.get(), data->thread_start, thread); } static void threadEndCB(jvmtiEnv* jvmti, JNIEnv* jnienv, @@ -60,8 +76,12 @@ static void threadEndCB(jvmtiEnv* jvmti, jvmti->GetEnvironmentLocalStorage(reinterpret_cast(&data)))) { return; } + ScopedLocalRef klass(jnienv, data->GetTestClass(jvmti, jnienv)); + if (klass.get() == nullptr) { + return; + } CHECK(data->thread_end != nullptr); - jnienv->CallStaticVoidMethod(data->test_klass, data->thread_end, thread); + jnienv->CallStaticVoidMethod(klass.get(), data->thread_end, thread); } static void singleStepCB(jvmtiEnv* jvmti, @@ -77,10 +97,14 @@ static void singleStepCB(jvmtiEnv* jvmti, if (data->in_callback) { return; } + ScopedLocalRef klass(jnienv, data->GetTestClass(jvmti, jnienv)); + if (klass.get() == nullptr) { + return; + } CHECK(data->single_step != nullptr); data->in_callback = true; jobject method_arg = GetJavaMethod(jvmti, jnienv, method); - jnienv->CallStaticVoidMethod(data->test_klass, + jnienv->CallStaticVoidMethod(klass.get(), data->single_step, thread, method_arg, @@ -106,11 +130,15 @@ static void fieldAccessCB(jvmtiEnv* jvmti, // Don't do callback for either of these to prevent an infinite loop. return; } + ScopedLocalRef klass(jnienv, data->GetTestClass(jvmti, jnienv)); + if (klass.get() == nullptr) { + return; + } CHECK(data->field_access != nullptr); data->in_callback = true; jobject method_arg = GetJavaMethod(jvmti, jnienv, method); jobject field_arg = GetJavaField(jvmti, jnienv, field_klass, field); - jnienv->CallStaticVoidMethod(data->test_klass, + jnienv->CallStaticVoidMethod(klass.get(), data->field_access, method_arg, static_cast(location), @@ -141,6 +169,10 @@ static void fieldModificationCB(jvmtiEnv* jvmti, // Don't do callback recursively to prevent an infinite loop. return; } + ScopedLocalRef klass(jnienv, data->GetTestClass(jvmti, jnienv)); + if (klass.get() == nullptr) { + return; + } CHECK(data->field_modify != nullptr); data->in_callback = true; jobject method_arg = GetJavaMethod(jvmti, jnienv, method); @@ -152,7 +184,7 @@ static void fieldModificationCB(jvmtiEnv* jvmti, jnienv->DeleteLocalRef(field_arg); return; } - jnienv->CallStaticVoidMethod(data->test_klass, + jnienv->CallStaticVoidMethod(klass.get(), data->field_modify, method_arg, static_cast(location), @@ -180,6 +212,10 @@ static void methodExitCB(jvmtiEnv* jvmti, // Don't do callback for either of these to prevent an infinite loop. return; } + ScopedLocalRef klass(jnienv, data->GetTestClass(jvmti, jnienv)); + if (klass.get() == nullptr) { + return; + } CHECK(data->exit_method != nullptr); data->in_callback = true; jobject method_arg = GetJavaMethod(jvmti, jnienv, method); @@ -189,7 +225,7 @@ static void methodExitCB(jvmtiEnv* jvmti, data->in_callback = false; return; } - jnienv->CallStaticVoidMethod(data->test_klass, + jnienv->CallStaticVoidMethod(klass.get(), data->exit_method, method_arg, was_popped_by_exception, @@ -212,12 +248,16 @@ static void methodEntryCB(jvmtiEnv* jvmti, // Don't do callback for either of these to prevent an infinite loop. return; } + ScopedLocalRef klass(jnienv, data->GetTestClass(jvmti, jnienv)); + if (klass.get() == nullptr) { + return; + } data->in_callback = true; jobject method_arg = GetJavaMethod(jvmti, jnienv, method); if (jnienv->ExceptionCheck()) { return; } - jnienv->CallStaticVoidMethod(data->test_klass, data->enter_method, method_arg); + jnienv->CallStaticVoidMethod(klass.get(), data->enter_method, method_arg); jnienv->DeleteLocalRef(method_arg); data->in_callback = false; } @@ -407,6 +447,10 @@ extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing2( return; } memset(data, 0, sizeof(TraceData)); + if (JvmtiErrorToException(env, jvmti_env, + jvmti_env->CreateRawMonitor("Trace monitor", &data->trace_mon))) { + return; + } data->test_klass = reinterpret_cast(env->NewGlobalRef(klass)); data->enter_method = enter != nullptr ? env->FromReflectedMethod(enter) : nullptr; data->exit_method = exit != nullptr ? env->FromReflectedMethod(exit) : nullptr; @@ -537,42 +581,63 @@ extern "C" JNIEXPORT void JNICALL Java_art_Trace_disableTracing( if (data == nullptr || data->test_klass == nullptr) { return; } - env->DeleteGlobalRef(data->test_klass); - if (env->ExceptionCheck()) { - return; - } - // Clear test_klass so we know this isn't being used - data->test_klass = nullptr; + ScopedLocalRef err(env, nullptr); + // First disable all the events. if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_FIELD_ACCESS, thr))) { - return; + env->ExceptionDescribe(); + err.reset(env->ExceptionOccurred()); + env->ExceptionClear(); } if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_FIELD_MODIFICATION, thr))) { - return; + env->ExceptionDescribe(); + err.reset(env->ExceptionOccurred()); + env->ExceptionClear(); } if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_METHOD_ENTRY, thr))) { - return; + env->ExceptionDescribe(); + err.reset(env->ExceptionOccurred()); + env->ExceptionClear(); } if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_METHOD_EXIT, thr))) { - return; + env->ExceptionDescribe(); + err.reset(env->ExceptionOccurred()); + env->ExceptionClear(); } if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_SINGLE_STEP, thr))) { + env->ExceptionDescribe(); + err.reset(env->ExceptionOccurred()); + env->ExceptionClear(); + } + if (JvmtiErrorToException(env, jvmti_env, + jvmti_env->RawMonitorEnter(data->trace_mon))) { + return; + } + // Clear test_klass so we know this isn't being used + env->DeleteGlobalRef(data->test_klass); + data->test_klass = nullptr; + if (JvmtiErrorToException(env, + jvmti_env, + jvmti_env->RawMonitorExit(data->trace_mon))) { return; } + if (err.get() != nullptr) { + env->Throw(err.get()); + } } } // namespace common_trace diff --git a/tools/ahat/Android.mk b/tools/ahat/Android.mk index a9a0492fe9e91b2fa7dad058b96acf5c6edae4b3..34e6a9cd42a516cba509ce440fd3a8d8fe196284 100644 --- a/tools/ahat/Android.mk +++ b/tools/ahat/Android.mk @@ -23,6 +23,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-java-files-under, src/main) LOCAL_JAR_MANIFEST := etc/ahat.mf LOCAL_JAVA_RESOURCE_FILES := $(LOCAL_PATH)/etc/style.css +LOCAL_JAVACFLAGS := -Xdoclint:all/protected LOCAL_IS_HOST_MODULE := true LOCAL_MODULE_TAGS := optional LOCAL_MODULE := ahat diff --git a/tools/ahat/README.txt b/tools/ahat/README.txt index a765b17951411c7cd8484556e7a187c9d45f2bd9..cdfeba45cb00b216229d0fca4dfe78d38e271077 100644 --- a/tools/ahat/README.txt +++ b/tools/ahat/README.txt @@ -30,7 +30,6 @@ TODO: * Show somewhere where to send bugs. * Include a link to /objects in the overview and menu? * Turn on LOCAL_JAVACFLAGS := -Xlint:unchecked -Werror - * Use hex for object ids in URLs? * [low priority] by site allocations won't line up if the stack has been truncated. Is there any way to manually line them up in that case? @@ -54,7 +53,15 @@ Reported Issues: * Request to be able to sort tables by size. Release History: - 1.5 Pending + 1.6 Pending + + 1.5 December 05, 2017 + Distinguish between weakly reachable and unreachable instances. + Allow hex ids to be used for objects in query parameters. + Restore old presentation of sample paths from gc roots. + Fix bug in selection of sample paths from gc root. + Fix bug in proguard deobfuscation of stack frames. + Tighten up and document ahat public API. 1.4 October 03, 2017 Give better error messages on failure to launch ahat. diff --git a/tools/ahat/etc/ahat.mf b/tools/ahat/etc/ahat.mf index 1753406e6e4746b6c06ef0a0c2dc6b931bc2e312..df964838bddf793376d18cae149495faa8ead9a9 100644 --- a/tools/ahat/etc/ahat.mf +++ b/tools/ahat/etc/ahat.mf @@ -1,4 +1,4 @@ Name: ahat/ Implementation-Title: ahat -Implementation-Version: 1.4 +Implementation-Version: 1.5 Main-Class: com.android.ahat.Main diff --git a/tools/ahat/etc/ahat_api.txt b/tools/ahat/etc/ahat_api.txt index 7920adae55700d2b28f1e78b19fcadc000dfd4e0..93fe46bf8b3c717cbdc88d9f675cc1327faa8d5c 100644 --- a/tools/ahat/etc/ahat_api.txt +++ b/tools/ahat/etc/ahat_api.txt @@ -9,7 +9,6 @@ package com.android.ahat { package com.android.ahat.dominators { public class DominatorsComputation { - ctor public DominatorsComputation(); method public static void computeDominators(com.android.ahat.dominators.DominatorsComputation.Node); } @@ -109,7 +108,6 @@ package com.android.ahat.heapdump { } public class Diff { - ctor public Diff(); method public static void snapshots(com.android.ahat.heapdump.AhatSnapshot, com.android.ahat.heapdump.AhatSnapshot); } @@ -159,7 +157,6 @@ package com.android.ahat.heapdump { } public class Parser { - ctor public Parser(); method public static com.android.ahat.heapdump.AhatSnapshot parseHeapDump(java.io.File, com.android.ahat.proguard.ProguardMap) throws com.android.ahat.heapdump.HprofFormatException, java.io.IOException; method public static com.android.ahat.heapdump.AhatSnapshot parseHeapDump(java.nio.ByteBuffer, com.android.ahat.proguard.ProguardMap) throws com.android.ahat.heapdump.HprofFormatException, java.io.IOException; } @@ -210,11 +207,9 @@ package com.android.ahat.heapdump { } public static class Site.ObjectsInfo implements com.android.ahat.heapdump.Diffable { - ctor public Site.ObjectsInfo(com.android.ahat.heapdump.AhatHeap, com.android.ahat.heapdump.AhatClassObj); method public com.android.ahat.heapdump.Site.ObjectsInfo getBaseline(); method public java.lang.String getClassName(); method public boolean isPlaceHolder(); - method public void setBaseline(com.android.ahat.heapdump.Site.ObjectsInfo); field public com.android.ahat.heapdump.AhatClassObj classObj; field public com.android.ahat.heapdump.AhatHeap heap; field public com.android.ahat.heapdump.Size numBytes; @@ -236,6 +231,7 @@ package com.android.ahat.heapdump { ctor public Sort(); method public static java.util.Comparator defaultInstanceCompare(com.android.ahat.heapdump.AhatSnapshot); method public static java.util.Comparator defaultSiteCompare(com.android.ahat.heapdump.AhatSnapshot); + method public static java.util.Comparator withPriority(java.util.Comparator...); field public static final java.util.Comparator FIELD_VALUE_BY_NAME; field public static final java.util.Comparator FIELD_VALUE_BY_TYPE; field public static final java.util.Comparator INSTANCE_BY_TOTAL_RETAINED_SIZE; @@ -246,22 +242,6 @@ package com.android.ahat.heapdump { field public static final java.util.Comparator SIZE_BY_SIZE; } - public static class Sort.InstanceByHeapRetainedSize implements java.util.Comparator { - ctor public Sort.InstanceByHeapRetainedSize(com.android.ahat.heapdump.AhatHeap); - method public int compare(com.android.ahat.heapdump.AhatInstance, com.android.ahat.heapdump.AhatInstance); - } - - public static class Sort.SiteByHeapSize implements java.util.Comparator { - ctor public Sort.SiteByHeapSize(com.android.ahat.heapdump.AhatHeap); - method public int compare(com.android.ahat.heapdump.Site, com.android.ahat.heapdump.Site); - } - - public static class Sort.WithPriority implements java.util.Comparator { - ctor public Sort.WithPriority(java.util.Comparator...); - ctor public Sort.WithPriority(java.util.List>); - method public int compare(T, T); - } - public final class Type extends java.lang.Enum { method public static com.android.ahat.heapdump.Type valueOf(java.lang.String); method public static final com.android.ahat.heapdump.Type[] values(); @@ -285,7 +265,6 @@ package com.android.ahat.heapdump { method public java.lang.Integer asInteger(); method public java.lang.Long asLong(); method public abstract boolean equals(java.lang.Object); - method public com.android.ahat.heapdump.Value getBaseline(); method public static com.android.ahat.heapdump.Value getBaseline(com.android.ahat.heapdump.Value); method public static com.android.ahat.heapdump.Type getType(com.android.ahat.heapdump.Value); method public boolean isAhatInstance(); diff --git a/tools/ahat/etc/test-dump.pro b/tools/ahat/etc/test-dump.pro index 284e4b862125170ad1dcbe3a8ad78c8e1a51d350..296d411cfda7f993f4e4642beab03f6ba73b645f 100644 --- a/tools/ahat/etc/test-dump.pro +++ b/tools/ahat/etc/test-dump.pro @@ -3,3 +3,12 @@ public static void main(java.lang.String[]); } +-keep public class SuperDumpedStuff { + public void allocateObjectAtUnObfSuperSite(); +} + +# Produce useful obfuscated stack traces so we can test useful deobfuscation +# of stack traces. +-renamesourcefileattribute SourceFile +-keepattributes SourceFile,LineNumberTable + diff --git a/tools/ahat/src/main/com/android/ahat/Main.java b/tools/ahat/src/main/com/android/ahat/Main.java index 048573e915afe41f0ed3d445b9f96afef1de5214..04a6012a61de71ff33f3af0ce1a61eb409d11ddc 100644 --- a/tools/ahat/src/main/com/android/ahat/Main.java +++ b/tools/ahat/src/main/com/android/ahat/Main.java @@ -30,6 +30,9 @@ import java.net.InetSocketAddress; import java.text.ParseException; import java.util.concurrent.Executors; +/** + * Contains the main entry point for the ahat heap dump viewer. + */ public class Main { private Main() { } @@ -70,6 +73,14 @@ public class Main { throw new AssertionError("Unreachable"); } + /** + * Main entry for ahat heap dump viewer. + * Launches an http server on localhost for viewing a given heap dump. + * See the ahat README or pass "--help" as one of the arguments to see a + * description of what arguments and options are expected. + * + * @param args the command line arguments + */ public static void main(String[] args) { int port = 7100; for (String arg : args) { diff --git a/tools/ahat/src/main/com/android/ahat/SiteHandler.java b/tools/ahat/src/main/com/android/ahat/SiteHandler.java index 543eaa376ab326fb8fbc96894f6589470fbef856..5093f0d43e35d98ac3eb5ee6ebafca3d644ac897 100644 --- a/tools/ahat/src/main/com/android/ahat/SiteHandler.java +++ b/tools/ahat/src/main/com/android/ahat/SiteHandler.java @@ -88,7 +88,7 @@ class SiteHandler implements AhatHandler { new Column("Class")); List infos = site.getObjectsInfos(); - Comparator compare = new Sort.WithPriority( + Comparator compare = Sort.withPriority( Sort.OBJECTS_INFO_BY_HEAP_NAME, Sort.OBJECTS_INFO_BY_SIZE, Sort.OBJECTS_INFO_BY_CLASS_NAME); diff --git a/tools/ahat/src/main/com/android/ahat/dominators/DominatorsComputation.java b/tools/ahat/src/main/com/android/ahat/dominators/DominatorsComputation.java index 58b7b59f9aa389851152436759ebc2f2e6ced560..d3fea4869ad71831687c279e5e7cc1c663f8cb12 100644 --- a/tools/ahat/src/main/com/android/ahat/dominators/DominatorsComputation.java +++ b/tools/ahat/src/main/com/android/ahat/dominators/DominatorsComputation.java @@ -23,38 +23,72 @@ import java.util.List; import java.util.Queue; /** - * Generic DominatorsComputation. - * - * To use the dominators computation, have your graph nodes implement the - * DominatorsComputation.Node interface, then call - * DominatorsComputation.computeDominators on the single root node. + * Provides a static method for computing the immediate dominators of a + * directed graph. It can be used with any directed graph data structure + * that implements the {@link DominatorsComputation.Node} interface and has + * some root node with no incoming edges. */ public class DominatorsComputation { + private DominatorsComputation() { + } + /** - * Interface for a directed graph to perform the dominators computation on. + * Interface for a directed graph to perform immediate dominators + * computation on. + * The dominators computation can be used with directed graph data + * structures that implement this Node interface. To use the + * dominators computation on your graph, you must make the following + * functionality available to the dominators computation: + *
    + *
  • Efficiently mapping from node to associated internal dominators + * computation state using the + * {@link #setDominatorsComputationState setDominatorsComputationState} and + * {@link #getDominatorsComputationState getDominatorsComputationState} methods. + *
  • Iterating over all outgoing edges of an node using the + * {@link #getReferencesForDominators getReferencesForDominators} method. + *
  • Setting the computed dominator for a node using the + * {@link #setDominator setDominator} method. + *
*/ public interface Node { /** - * Associate the given dominator state with this node. + * Associates the given dominator state with this node. Subsequent calls to + * {@link #getDominatorsComputationState getDominatorsComputationState} on + * this node should return the state given here. At the conclusion of the + * dominators computation, this method will be called for + * each node with state set to null. + * + * @param state the dominator state to associate with this node */ void setDominatorsComputationState(Object state); /** - * Get the most recent dominator state associated with this node using - * setDominatorsComputationState. If setDominatorsComputationState has not - * yet been called, this should return null. + * Returns the dominator state most recently associated with this node + * by a call to {@link #setDominatorsComputationState setDominatorsComputationState}. + * If setDominatorsComputationState has not yet been called + * on this node for this dominators computation, this method should return + * null. + * + * @return the associated dominator state */ Object getDominatorsComputationState(); /** - * Return a collection of nodes referenced from this node, for the - * purposes of computing dominators. + * Returns a collection of nodes referenced from this node, for the + * purposes of computing dominators. This method will be called at most + * once for each node reachable from the root node of the dominators + * computation. + * + * @return an iterable collection of the nodes with an incoming edge from + * this node. */ Iterable getReferencesForDominators(); /** - * Update this node's dominator based on the results of the dominators + * Sets the dominator for this node based on the results of the dominators * computation. + * + * @param dominator the computed immediate dominator of this node */ void setDominator(Node dominator); } @@ -112,8 +146,14 @@ public class DominatorsComputation { } /** - * Compute the dominator tree rooted at the given node. - * There must not be any incoming references to the root node. + * Computes the immediate dominators of all nodes reachable from the root node. + * There must not be any incoming references to the root node. + *

+ * The result of this function is to call the {@link Node#setDominator} + * function on every node reachable from the root node. + * + * @param root the root node of the dominators computation + * @see Node */ public static void computeDominators(Node root) { long id = 0; diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatArrayInstance.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatArrayInstance.java index ccdd6e4df71b4de3e7654257358160020f86f3bf..9c8080267389a7d6b08c0bbc2fedd51fcfb8bbb0 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatArrayInstance.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatArrayInstance.java @@ -21,6 +21,12 @@ import java.util.AbstractList; import java.util.Collections; import java.util.List; +/** + * An array instance from a parsed heap dump. + * It is used for both object and primitive arrays. The class provides methods + * for accessing the length and elements of the array in addition to those + * methods inherited from {@link AhatInstance}. + */ public class AhatArrayInstance extends AhatInstance { // To save space, we store arrays as primitive arrays or AhatInstance arrays // and provide a wrapper over the arrays to expose a list of Values. @@ -186,21 +192,30 @@ public class AhatArrayInstance extends AhatInstance { } /** - * Returns the length of the array. + * Returns the number of elements in the array. + * + * @return number of elements in the array. */ public int getLength() { return mValues.size(); } /** - * Returns the array's values. + * Returns a list of all of the array's elements in order. + * The returned list does not support modification. + * + * @return list of the array's elements. */ public List getValues() { return mValues; } /** - * Returns the object at the given index of this array. + * Returns the value at the given index of this array. + * + * @param index the index of the value to retrieve + * @return the value at the given index + * @throws IndexOutOfBoundsException if the index is out of range */ public Value getValue(int index) { return mValues.get(index); diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassInstance.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassInstance.java index cb9d959508df8ade014f49ae85eaa9c1eeeaa8ac..c82ef20e9bf4d561fda2093708d0e0ea6ce8a82e 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassInstance.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassInstance.java @@ -20,6 +20,15 @@ import java.awt.image.BufferedImage; import java.util.Iterator; import java.util.NoSuchElementException; +/** + * A typical Java object from a parsed heap dump. + * Note that this is used for Java objects that are instances of classes (as + * opposed to arrays), not for class objects themselves. + * See {@link AhatClassObj } for the representation of class objects. + *

+ * This class provides a method for iterating over the instance fields of the + * object in addition to those methods inherited from {@link AhatInstance}. + */ public class AhatClassInstance extends AhatInstance { // Instance fields of the object. These are stored in order of the instance // field descriptors from the class object, starting with this class first, @@ -84,6 +93,10 @@ public class AhatClassInstance extends AhatInstance { /** * Returns the list of class instance fields for this instance. + * Includes values of field inherited from the superclass of this instance. + * The fields are returned in no particular order. + * + * @return Iterable over the instance field values. */ public Iterable getInstanceFields() { return new InstanceFieldIterator(mFields, getClassObj()); @@ -220,7 +233,7 @@ public class AhatClassInstance extends AhatInstance { } - public BufferedImage asBitmap() { + @Override public BufferedImage asBitmap() { BitmapInfo info = getBitmapInfo(); if (info == null) { return null; diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassObj.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassObj.java index 3babf76842e708685af851944189262be68f2ca3..36ada2857c83b7b1f9c32a42836209e84a145cca 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassObj.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatClassObj.java @@ -20,6 +20,13 @@ import java.util.AbstractList; import java.util.Arrays; import java.util.List; +/** + * A class from a parsed heap dump. + * In addition to those methods inherited from {@link AhatInstance}, the class + * provides methods for accessing information about the class object, such as + * the class loader, superclass, static field values and instance field + * descriptors. + */ public class AhatClassObj extends AhatInstance { private String mClassName; private AhatClassObj mSuperClassObj; @@ -56,6 +63,9 @@ public class AhatClassObj extends AhatInstance { /** * Returns the name of the class this is a class object for. + * For example, "java.lang.String". + * + * @return the name of the class */ public String getName() { return mClassName; @@ -63,6 +73,8 @@ public class AhatClassObj extends AhatInstance { /** * Returns the superclass of this class object. + * + * @return the superclass object */ public AhatClassObj getSuperClassObj() { return mSuperClassObj; @@ -70,14 +82,18 @@ public class AhatClassObj extends AhatInstance { /** * Returns the class loader of this class object. + * + * @return the class loader object */ public AhatInstance getClassLoader() { return mClassLoader; } /** - * Returns the size of instances of this object, as reported in the heap - * dump. + * Returns the size of instances of this object. + * The size returned is as reported in the heap dump. + * + * @return the class instance size */ public long getInstanceSize() { return mInstanceSize; @@ -85,6 +101,8 @@ public class AhatClassObj extends AhatInstance { /** * Returns the static field values for this class object. + * + * @return the static field values */ public List getStaticFieldValues() { return Arrays.asList(mStaticFieldValues); @@ -92,6 +110,9 @@ public class AhatClassObj extends AhatInstance { /** * Returns the fields of instances of this class. + * Does not include fields from the super class of this class. + * + * @return the instance fields */ public Field[] getInstanceFields() { return mInstanceFields; diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatHeap.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatHeap.java index b8897a182c83097a237c292a5e32ba3fa322cef6..60c9a0d086c701f07169f8eb9f17d07d5d3dde21 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatHeap.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatHeap.java @@ -16,6 +16,13 @@ package com.android.ahat.heapdump; +/** + * Used to identify and access basic information about a particular + * heap from the heap dump. Standard Java heap dumps have a single heap, + * called the "default" heap. Android heap dumps distinguish among "zygote", + * "image", and "app" heaps. There will be a single instance of AhatHeap for + * each different heap in the heap dump. + */ public class AhatHeap implements Diffable { private String mName; private Size mSize = Size.ZERO; @@ -61,6 +68,9 @@ public class AhatHeap implements Diffable { /** * Returns the name of this heap. + * For example, "default", "app", "image", or "zygote". + * + * @return The name of the heap. */ public String getName() { return mName; @@ -68,6 +78,8 @@ public class AhatHeap implements Diffable { /** * Returns the total number of bytes allocated on this heap. + * + * @return the total number of bytes allocated on this heap. */ public Size getSize() { return mSize; diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java index a9f819f710fcd102994c365a925c6220f48ebf26..67253bf0e7c95ca89eb2acec1d91c5dcc2fa4157 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatInstance.java @@ -26,6 +26,11 @@ import java.util.Deque; import java.util.List; import java.util.Queue; +/** + * A Java instance from a parsed heap dump. It is the base class used for all + * kinds of Java instances, including normal Java objects, class objects, and + * arrays. + */ public abstract class AhatInstance implements Diffable, DominatorsComputation.Node { // The id of this instance from the heap dump. @@ -80,14 +85,20 @@ public abstract class AhatInstance implements Diffable, } /** - * Returns a unique identifier for the instance. + * Returns a unique identifier for this instance. + * + * @return id of the instance */ public long getId() { return mId; } /** - * Returns the shallow number of bytes this object takes up. + * Returns the number of bytes used for this object in the heap. + * The returned size is a shallow size for the object that does not include + * sizes of other objects dominated by this object. + * + * @return the shallow size of the object */ public Size getSize() { return new Size(mClassObj.getInstanceSize() + getExtraJavaSize(), mRegisteredNativeSize); @@ -104,8 +115,13 @@ public abstract class AhatInstance implements Diffable, abstract long getExtraJavaSize(); /** - * Returns the number of bytes belonging to the given heap that this instance - * retains. + * Returns the number of bytes retained by this object in the given heap. + * The returned size includes the shallow size of this object and the size + * of all objects directly or indirectly retained by this object. Only those + * objects allocated on the given heap are included in the reported size. + * + * @param heap the heap to get the retained size for + * @return the retained size of the object */ public Size getRetainedSize(AhatHeap heap) { int index = heap.getIndex(); @@ -116,7 +132,11 @@ public abstract class AhatInstance implements Diffable, } /** - * Returns the total number of bytes this instance retains. + * Returns the total number of bytes retained by this object. The returned + * size includes the shallow size of this object and the size of all objects + * directly or indirectly retained by this object. + * + * @return the total retained size of the object */ public Size getTotalRetainedSize() { Size size = Size.ZERO; @@ -136,7 +156,11 @@ public abstract class AhatInstance implements Diffable, } /** - * Returns true if this object is strongly-reachable. + * Returns true if this object is strongly reachable. An object is strongly + * reachable if there exists a path of (strong) references from some root + * object to this object. + * + * @return true if the object is strongly reachable */ public boolean isStronglyReachable() { return mImmediateDominator != null; @@ -144,14 +168,28 @@ public abstract class AhatInstance implements Diffable, /** * Returns true if this object is reachable only through a - * soft/weak/phantom/finalizer reference. + * soft/weak/phantom/finalizer reference. An object is weakly reachable if + * it is not strongly reachable but there still exists a path of references + * from some root object to this object. Because the object is not strongly + * reachable, any such path must contain a SoftReference, WeakReference, + * PhantomReference, or FinalizerReference somewhere along it. + *

+ * Unlike a strongly reachable object, a weakly reachable object is allowed + * to be garbage collected. + * + * @return true if the object is weakly reachable */ public boolean isWeaklyReachable() { return !isStronglyReachable() && mNextInstanceToGcRoot != null; } /** - * Returns true if this object is completely unreachable. + * Returns true if this object is completely unreachable. An object is + * completely unreachable if there is no path to the object from some root + * object, neither through strong nor soft/weak/phantom/finalizer + * references. + * + * @return true if the object is completely unreachable */ public boolean isUnreachable() { return !isStronglyReachable() && !isWeaklyReachable(); @@ -159,6 +197,8 @@ public abstract class AhatInstance implements Diffable, /** * Returns the heap that this instance is allocated on. + * + * @return heap the instance is allocated on */ public AhatHeap getHeap() { return mHeap; @@ -171,7 +211,10 @@ public abstract class AhatInstance implements Diffable, abstract Iterable getReferences(); /** - * Returns true if this instance is marked as a root instance. + * Returns true if this instance is a GC root. + * + * @return true if this instance is a GC root. + * @see getRootTypes */ public boolean isRoot() { return mRootTypes != 0; @@ -187,6 +230,8 @@ public abstract class AhatInstance implements Diffable, /** * Returns a list of the root types of this object. * Returns null if this object is not a root. + * + * @return list of the objects root types */ public Collection getRootTypes() { if (!isRoot()) { @@ -205,14 +250,17 @@ public abstract class AhatInstance implements Diffable, /** * Returns the immediate dominator of this instance. * Returns null if this is a root instance. + * + * @return the immediate dominator of this instance */ public AhatInstance getImmediateDominator() { return mImmediateDominator; } /** - * Returns a list of those objects immediately dominated by the given - * instance. + * Returns a list of objects immediately dominated by this instance. + * + * @return list of immediately dominated objects */ public List getDominated() { return mDominated; @@ -220,13 +268,17 @@ public abstract class AhatInstance implements Diffable, /** * Returns the site where this instance was allocated. + * + * @return the object's allocation site */ public Site getSite() { return mSite; } /** - * Returns true if the given instance is a class object + * Returns true if this instance is a class object + * + * @return true if this instance is a class object */ public boolean isClassObj() { // Overridden by AhatClassObj. @@ -236,6 +288,8 @@ public abstract class AhatInstance implements Diffable, /** * Returns this as an AhatClassObj if this is an AhatClassObj. * Returns null if this is not an AhatClassObj. + * + * @return this instance as a class object */ public AhatClassObj asClassObj() { // Overridden by AhatClassObj. @@ -243,7 +297,11 @@ public abstract class AhatInstance implements Diffable, } /** - * Returns the class object instance for the class of this object. + * Returns the class object for this instance. + * For example, if this object is an instance of java.lang.String, this + * method returns the AhatClassObj for java.lang.String. + * + * @return the instance's class object */ public AhatClassObj getClassObj() { return mClassObj; @@ -251,6 +309,10 @@ public abstract class AhatInstance implements Diffable, /** * Returns the name of the class this object belongs to. + * For example, if this object is an instance of java.lang.String, returns + * "java.lang.String". + * + * @return the name of this instance's class */ public String getClassName() { AhatClassObj classObj = getClassObj(); @@ -258,7 +320,9 @@ public abstract class AhatInstance implements Diffable, } /** - * Returns true if the given instance is an array instance + * Returns true if the given instance is an array instance. + * + * @return true if the given instance is an array instance */ public boolean isArrayInstance() { // Overridden by AhatArrayInstance. @@ -268,6 +332,8 @@ public abstract class AhatInstance implements Diffable, /** * Returns this as an AhatArrayInstance if this is an AhatArrayInstance. * Returns null if this is not an AhatArrayInstance. + * + * @return this instance as an array instance */ public AhatArrayInstance asArrayInstance() { // Overridden by AhatArrayInstance. @@ -275,7 +341,9 @@ public abstract class AhatInstance implements Diffable, } /** - * Returns true if the given instance is a class instance + * Returns true if this instance is a class instance. + * + * @return true if this instance is a class instance */ public boolean isClassInstance() { return false; @@ -284,15 +352,20 @@ public abstract class AhatInstance implements Diffable, /** * Returns this as an AhatClassInstance if this is an AhatClassInstance. * Returns null if this is not an AhatClassInstance. + * + * @return this instance as a class instance */ public AhatClassInstance asClassInstance() { return null; } /** - * Return the referent associated with this instance. - * This is relevent for instances of java.lang.ref.Reference. - * Returns null if the instance has no referent associated with it. + * Returns the referent associated with this instance. + * This is only relevant for instances of java.lang.ref.Reference or its + * subclasses. Returns null if the instance has no referent associated with + * it. + * + * @return the referent associated with this instance */ public AhatInstance getReferent() { // Overridden by AhatClassInstance. @@ -300,7 +373,9 @@ public abstract class AhatInstance implements Diffable, } /** - * Returns a list of objects with hard references to this object. + * Returns a list of objects with (strong) references to this object. + * + * @return the objects referencing this object */ public List getHardReverseReferences() { if (mHardReverseReferences != null) { @@ -310,7 +385,10 @@ public abstract class AhatInstance implements Diffable, } /** - * Returns a list of objects with soft references to this object. + * Returns a list of objects with soft/weak/phantom/finalizer references to + * this object. + * + * @return the objects weakly referencing this object */ public List getSoftReverseReferences() { if (mSoftReverseReferences != null) { @@ -320,9 +398,12 @@ public abstract class AhatInstance implements Diffable, } /** - * Returns the value of a field of an instance. - * Returns null if the field value is null, the field couldn't be read, or - * there are multiple fields with the same name. + * Returns the value of a field of this instance. Returns null if the field + * value is null, the field couldn't be read, or there are multiple fields + * with the same name. + * + * @param fieldName the name of the field to get the value of + * @return the field value */ public Value getField(String fieldName) { // Overridden by AhatClassInstance. @@ -330,8 +411,13 @@ public abstract class AhatInstance implements Diffable, } /** - * Reads a reference field of this instance. - * Returns null if the field value is null, or if the field couldn't be read. + * Reads a reference field of this instance. Returns null if the field value + * is null, of primitive type, or if the field couldn't be read. There is no + * way using this method to distinguish between a reference field with value + * null and an invalid field. + * + * @param fieldName the name of the reference field to get the value of + * @return the reference field value */ public AhatInstance getRefField(String fieldName) { // Overridden by AhatClassInstance. @@ -339,30 +425,41 @@ public abstract class AhatInstance implements Diffable, } /** - * Assuming inst represents a DexCache object, return the dex location for - * that dex cache. Returns null if the given instance doesn't represent a - * DexCache object or the location could not be found. + * Returns the dex location associated with this object. Only applies to + * instances of dalvik.system.DexCache. If this is an instance of DexCache, + * returns the dex location for that dex cache. Otherwise returns null. * If maxChars is non-negative, the returned location is truncated to * maxChars in length. + * + * @param maxChars the maximum length of the returned string + * @return the dex location associated with this object */ public String getDexCacheLocation(int maxChars) { return null; } /** - * Return the bitmap instance associated with this object, or null if there - * is none. This works for android.graphics.Bitmap instances and their - * underlying Byte[] instances. + * Returns the android.graphics.Bitmap instance associated with this object. + * Instances of android.graphics.Bitmap return themselves. If this is a + * byte[] array containing pixel data for an instance of + * android.graphics.Bitmap, that instance of android.graphics.Bitmap is + * returned. Otherwise null is returned. + * + * @return the bitmap instance associated with this object */ public AhatInstance getAssociatedBitmapInstance() { return null; } /** - * Read the string value from this instance. - * Returns null if this object can't be interpreted as a string. - * The returned string is truncated to maxChars characters. - * If maxChars is negative, the returned string is not truncated. + * Returns the (bounded-length) string associated with this instance. + * Applies to instances of java.lang.String, char[], and in some cases + * byte[]. Returns null if this object cannot be interpreted as a string. + * If maxChars is non-negative, the returned string is truncated to maxChars + * characters in length. + * + * @param maxChars the maximum length of the returned string + * @return the string associated with this instance */ public String asString(int maxChars) { // By default instances can't be interpreted as a string. This method is @@ -372,17 +469,23 @@ public abstract class AhatInstance implements Diffable, } /** - * Reads the string value from an hprof Instance. - * Returns null if the object can't be interpreted as a string. + * Returns the string associated with this instance. Applies to instances of + * java.lang.String, char[], and in some cases byte[]. Returns null if this + * object cannot be interpreted as a string. + * + * @return the string associated with this instance */ public String asString() { return asString(-1); } /** - * Return the bitmap associated with the given instance, if any. + * Returns the bitmap pixel data associated with this instance. * This is relevant for instances of android.graphics.Bitmap and byte[]. - * Returns null if there is no bitmap associated with the given instance. + * Returns null if there is no bitmap pixel data associated with the given + * instance. + * + * @return the bitmap pixel data associated with this image */ public BufferedImage asBitmap() { return null; @@ -402,11 +505,23 @@ public abstract class AhatInstance implements Diffable, } /** - * Returns a sample path from a GC root to this instance. - * This instance is included as the last element of the path with an empty - * field description. + * Returns a sample path from a GC root to this instance. The first element + * of the returned path is a GC root object. This instance is included as + * the last element of the path with an empty field description. + *

+ * If the instance is strongly reachable, a path of string references will + * be returned. If the instance is weakly reachable, the returned path will + * include a soft/weak/phantom/finalizer reference somewhere along it. + * Returns null if this instance is not reachable. + * + * @return sample path from a GC root to this instance + * @see PathElement */ public List getPathFromGcRoot() { + if (isUnreachable()) { + return null; + } + List path = new ArrayList(); AhatInstance dom = this; @@ -434,12 +549,15 @@ public abstract class AhatInstance implements Diffable, return new PathElement(inst.mNextInstanceToGcRoot, inst.mNextInstanceToGcRootField); } - /** Returns a human-readable identifier for this object. + /** + * Returns a human-readable identifier for this object. * For class objects, the string is the class name. * For class instances, the string is the class name followed by '@' and the * hex id of the instance. * For array instances, the string is the array type followed by the size in * square brackets, followed by '@' and the hex id of the instance. + * + * @return human-readable identifier for this object */ @Override public abstract String toString(); diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/AhatSnapshot.java b/tools/ahat/src/main/com/android/ahat/heapdump/AhatSnapshot.java index 59ce5d1c6c4cb751897f575f3e78ce126f5d913c..535db082c1f29c3fd8ebff2344213e7b961e121a 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/AhatSnapshot.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/AhatSnapshot.java @@ -19,6 +19,11 @@ package com.android.ahat.heapdump; import com.android.ahat.dominators.DominatorsComputation; import java.util.List; +/** + * A parsed heap dump. + * It contains methods to access the heaps, allocation sites, roots, classes, + * and instances from the parsed heap dump. + */ public class AhatSnapshot implements Diffable { private final Site mRootSite; @@ -60,16 +65,24 @@ public class AhatSnapshot implements Diffable { } /** - * Returns the instance with given id in this snapshot. + * Returns the instance with the given id in this snapshot. + * Where the id of an instance x is x.getId(). * Returns null if no instance with the given id is found. + * + * @param id the id of the instance to find + * @return the instance with the given id */ public AhatInstance findInstance(long id) { return mInstances.get(id); } /** - * Returns the AhatClassObj with given id in this snapshot. + * Returns the AhatClassObj with the given id in this snapshot. + * Where the id of a class object x is x.getId(). * Returns null if no class object with the given id is found. + * + * @param id the id of the class object to find + * @return the class object with the given id */ public AhatClassObj findClassObj(long id) { AhatInstance inst = findInstance(id); @@ -77,8 +90,12 @@ public class AhatSnapshot implements Diffable { } /** - * Returns the heap with the given name, if any. + * Returns the heap with the given name. + * Where the name of a heap x is x.getName(). * Returns null if no heap with the given name could be found. + * + * @param name the name of the heap to get + * @return the heap with the given name */ public AhatHeap getHeap(String name) { // We expect a small number of heaps (maybe 3 or 4 total), so a linear @@ -93,30 +110,45 @@ public class AhatSnapshot implements Diffable { /** * Returns a list of heaps in the snapshot in canonical order. - * Modifications to the returned list are visible to this AhatSnapshot, - * which is used by diff to insert place holder heaps. + *

+ * Note: modifications to the returned list are visible to this + * AhatSnapshot, which is used by diff to insert place holder heaps. + * + * @return list of heaps */ public List getHeaps() { return mHeaps; } /** - * Returns a collection of instances whose immediate dominator is the - * SENTINEL_ROOT. + * Returns a collection of "rooted" instances. + * An instance is "rooted" if it is a GC root, or if it is retained by more + * than one GC root. These are reachable instances that are not immediately + * dominated by any other instance in the heap. + * + * @return collection of rooted instances */ public List getRooted() { return mSuperRoot.getDominated(); } /** - * Returns the root site for this snapshot. + * Returns the root allocation site for this snapshot. + * + * @return the root allocation site */ public Site getRootSite() { return mRootSite; } - // Get the site associated with the given id. - // Returns the root site if no such site found. + /** + * Returns the site associated with the given id. + * Where the id of a site x is x.getId(). + * Returns the root site if no site with the given id is found. + * + * @param id the id of the site to get + * @return the site with the given id + */ public Site getSite(long id) { Site site = mRootSite.findSite(id); return site == null ? mRootSite : site; @@ -127,8 +159,10 @@ public class AhatSnapshot implements Diffable { } /** - * Returns true if this snapshot has been diffed against another, different + * Returns true if this snapshot has been diffed against a different * snapshot. + * + * @return true if the snapshot has been diffed */ public boolean isDiffed() { return mBaseline != this; diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/Diff.java b/tools/ahat/src/main/com/android/ahat/heapdump/Diff.java index 98c7e58d56cfbc58d242f8fd14d14d46b4c3a7ae..b35b4244ae528a0906bca0e8bcb16a5412ed788f 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/Diff.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/Diff.java @@ -25,9 +25,15 @@ import java.util.List; import java.util.Map; import java.util.Objects; +/** + * Provides a static method to diff two heap dumps. + */ public class Diff { + private Diff() { + } + /** - * Perform a diff between two heap lists. + * Performs a diff between two heap lists. * * Heaps are diffed based on heap name. PlaceHolder heaps will be added to * the given lists as necessary so that every heap in A has a corresponding @@ -312,8 +318,16 @@ public class Diff { } /** - * Perform a diff of the two snapshots, setting each as the baseline for the - * other. + * Performs a diff of two snapshots. + * Each snapshot will be set as the baseline for the other snapshot. + *

+ * The diff algorithm attempts to match instances in snapshot a + * to corresponding instances in snapshot b. The snapshots need + * not come from the same running process, application version, or platform + * version. + * + * @param a one of the snapshots to diff + * @param b the other of the snapshots to diff */ public static void snapshots(AhatSnapshot a, AhatSnapshot b) { a.setBaseline(b); diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/DiffFields.java b/tools/ahat/src/main/com/android/ahat/heapdump/DiffFields.java index e3c671fe2127363bd7785fbbfd81cdb27a1582be..ff07af0028c509524cf65ded61979497a95cf226 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/DiffFields.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/DiffFields.java @@ -22,12 +22,16 @@ import java.util.Comparator; import java.util.List; /** - * This class contains a routine for diffing two collections of static or - * instance fields. + * Provides a routine for diffing two collections of static or instance + * fields. */ public class DiffFields { /** - * Return the result of diffing two collections of field values. + * Returns the result of diffing two collections of field values. + * + * @param current a list of fields in the current heap dump + * @param baseline a list of fields in the baseline heap dump + * @return list of diffed fields */ public static List diff(Iterable current, Iterable baseline) { @@ -85,5 +89,5 @@ public class DiffFields { * by field name and type. */ private static final Comparator FOR_DIFF - = new Sort.WithPriority(Sort.FIELD_VALUE_BY_NAME, Sort.FIELD_VALUE_BY_TYPE); + = Sort.withPriority(Sort.FIELD_VALUE_BY_NAME, Sort.FIELD_VALUE_BY_TYPE); } diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/Diffable.java b/tools/ahat/src/main/com/android/ahat/heapdump/Diffable.java index 53442c857e24b79a3729aff97ea259704875c169..09c8ee6d394c2108098861152b581c849fe944b7 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/Diffable.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/Diffable.java @@ -17,12 +17,19 @@ package com.android.ahat.heapdump; /** - * An interface for objects that have corresponding objects in a baseline heap - * dump. + * An interface for instances/sites/heaps/etc in a heap dump that can be + * related to corresponding instances/sites/heaps/etc in a second heap dump + * when the two heap dumps have been diffed. */ public interface Diffable { /** - * Return the baseline object that corresponds to this one. + * Returns the object in the other heap dump that corresponds to this object. + * When two heap dumps are diffed, diffable objects from the first heap dump + * will be matched to "baseline" objects from the second heap dump, and + * diffable objects from the second heap dump will be matched to "baseline" + * objects from the first heap dump. + * + * @return the matched object from the other heap dump */ T getBaseline(); @@ -32,6 +39,8 @@ public interface Diffable { * baseline heap dump that is not in this heap dump. In that case, we create * a dummy place holder object in this heap dump as an indicator of the * object removed from the baseline heap dump. + * + * @return true if the object is a placeholder */ boolean isPlaceHolder(); } diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/DiffedFieldValue.java b/tools/ahat/src/main/com/android/ahat/heapdump/DiffedFieldValue.java index 3cd273ed98cbcdf2b062091abcb8b51a16aa3c53..8de337ea8c5644f60fa8c29dacbdc50c3efed623 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/DiffedFieldValue.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/DiffedFieldValue.java @@ -18,25 +18,65 @@ package com.android.ahat.heapdump; import java.util.Objects; -/** DiffedFieldValue is used by the DiffedField class to return the result of - * diffing two collections of fields. +/** + * Used by the DiffedField class to return the result of diffing two + * collections of fields. */ public class DiffedFieldValue { + /** + * The name of the field. + */ public final String name; + + /** + * The type of the field. + */ public final Type type; + + /** + * The value of the field in the current heap dump. + */ public final Value current; + + /** + * The value of the field in the baseline heap dump. + */ public final Value baseline; + /** + * Whether the field was added to, deleted from, or matched with a field in + * the baseline heap dump. + */ public final Status status; + /** + * A status enum to indicate whether a field was added to, deleted from, or + * matched with a field in the baseline heap dump. + */ public static enum Status { - ADDED, // The current field has no matching baseline value. - MATCHED, // The current field has a matching baseline value. - DELETED // The baseline field has no matching current value. + /** + * The field exists in the current heap dump but not the baseline. + */ + ADDED, + + /** + * The field exists in both the current and baseline heap dumps. + */ + MATCHED, + + /** + * The field exists in the baseline heap dump but not the current. + */ + DELETED }; /** - * Return a DiffedFieldValue where there is both a current and baseline. + * Constructs a DiffedFieldValue where there are both current and baseline + * fields. + * + * @param current the current field + * @param baseline the baseline field + * @return the constructed DiffedFieldValue */ public static DiffedFieldValue matched(FieldValue current, FieldValue baseline) { return new DiffedFieldValue(current.name, @@ -47,14 +87,20 @@ public class DiffedFieldValue { } /** - * Return a DiffedFieldValue where there is no baseline. + * Constructs a DiffedFieldValue where there is no baseline field. + * + * @param current the current field + * @return the constructed DiffedFieldValue */ public static DiffedFieldValue added(FieldValue current) { return new DiffedFieldValue(current.name, current.type, current.value, null, Status.ADDED); } /** - * Return a DiffedFieldValue where there is no current. + * Constructs a DiffedFieldValue where there is no current field. + * + * @param baseline the baseline field + * @return the constructed DiffedFieldValue */ public static DiffedFieldValue deleted(FieldValue baseline) { return new DiffedFieldValue(baseline.name, baseline.type, null, baseline.value, Status.DELETED); diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/Field.java b/tools/ahat/src/main/com/android/ahat/heapdump/Field.java index dff401796aab65ff7e93f496bac7418921f9b7aa..6494069dcbd890f5f5ccb40ac96ecac96ecbf9e0 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/Field.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/Field.java @@ -16,10 +16,26 @@ package com.android.ahat.heapdump; +/** + * A description of a field from a heap dump. + */ public class Field { + /** + * The name of the field. + */ public final String name; + + /** + * The type of the field. + */ public final Type type; + /** + * Constructs a Field instance. + * + * @param name name of the field + * @param type type of the field + */ public Field(String name, Type type) { this.name = name; this.type = type; diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/FieldValue.java b/tools/ahat/src/main/com/android/ahat/heapdump/FieldValue.java index 20e6da7271ea9f5238a175d31d3b85db9c5206f6..70314da8302c87784961d8889e5029f976f9b21d 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/FieldValue.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/FieldValue.java @@ -16,11 +16,32 @@ package com.android.ahat.heapdump; +/** + * A description and value of a field from a heap dump. + */ public class FieldValue { + /** + * The name of the field. + */ public final String name; + + /** + * The type of the field. + */ public final Type type; + + /** + * The value of the field. + */ public final Value value; + /** + * Constructs an instance of FieldValue. + * + * @param name name of the field + * @param type type of the field + * @param value value of the field + */ public FieldValue(String name, Type type, Value value) { this.name = name; this.type = type; diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/HprofFormatException.java b/tools/ahat/src/main/com/android/ahat/heapdump/HprofFormatException.java index 256a3b46f6fd47416ff8d7302df442a41b3569e5..29ac9b0c5ab973e6bf4f56661715a7135de5b9bc 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/HprofFormatException.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/HprofFormatException.java @@ -16,6 +16,10 @@ package com.android.ahat.heapdump; +/** + * Exception thrown when the heap dump parser detects an improperly formatted + * heap dump file. + */ public class HprofFormatException extends Exception { HprofFormatException(String msg) { super(msg); diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/Parser.java b/tools/ahat/src/main/com/android/ahat/heapdump/Parser.java index d7b1dd78d64251ec162f33ee7363de9ec0c4b392..13be57d41588377c168bc45c8f034a1ea16a2418 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/Parser.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/Parser.java @@ -31,21 +31,43 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +/** + * Provides methods for parsing heap dumps. + */ public class Parser { private static final int ID_SIZE = 4; + private Parser() { + } + /** - * Parse the given heap dump using the given proguard map for deobfuscation. - * We make the following assumptions about valid heap dumps: - * Class serial numbers, stack frames, and stack traces - * individually satisfy the following: - * - all elements are defined before they are referenced. - * - ids are densely packed in some range [a, b] where a is not - * necessarily 0. - * - there are not more than 2^31 elements defined. - * All classes are defined via a LOAD CLASS record before the first heap - * dump segment. - * The ID size used in the heap dump is 4 bytes. + * Parses a heap dump from a File. + *

+ * The heap dump should be a heap dump in the J2SE HPROF format optionally + * with Android extensions and satisfying the following additional + * constraints: + *

    + *
  • + * Class serial numbers, stack frames, and stack traces individually satisfy + * the following: + *
      + *
    • All elements are defined before they are referenced. + *
    • Ids are densely packed in some range [a, b] where a is not necessarily 0. + *
    • There are not more than 2^31 elements defined. + *
    + *
  • All classes are defined via a LOAD CLASS record before the first + * heap dump segment. + *
  • The ID size used in the heap dump is 4 bytes. + *
+ *

+ * The given proguard map will be used to deobfuscate class names, field + * names, and stack traces in the heap dump. + * + * @param hprof the hprof file to parse + * @param map the proguard map for deobfuscation + * @return the parsed heap dump + * @throws IOException if the heap dump could not be read + * @throws HprofFormatException if the heap dump is not properly formatted */ public static AhatSnapshot parseHeapDump(File hprof, ProguardMap map) throws IOException, HprofFormatException { @@ -57,7 +79,33 @@ public class Parser { } /** - * Parse a heap dump from a byte buffer. + * Parses a heap dump from a byte buffer. + *

+ * The heap dump should be a heap dump in the J2SE HPROF format optionally + * with Android extensions and satisfying the following additional + * constraints: + *

    + *
  • + * Class serial numbers, stack frames, and stack traces individually satisfy + * the following: + *
      + *
    • All elements are defined before they are referenced. + *
    • Ids are densely packed in some range [a, b] where a is not necessarily 0. + *
    • There are not more than 2^31 elements defined. + *
    + *
  • All classes are defined via a LOAD CLASS record before the first + * heap dump segment. + *
  • The ID size used in the heap dump is 4 bytes. + *
+ *

+ * The given proguard map will be used to deobfuscate class names, field + * names, and stack traces in the heap dump. + * + * @param hprof the bytes of the hprof file to parse + * @param map the proguard map for deobfuscation + * @return the parsed heap dump + * @throws IOException if the heap dump could not be read + * @throws HprofFormatException if the heap dump is not properly formatted */ public static AhatSnapshot parseHeapDump(ByteBuffer hprof, ProguardMap map) throws IOException, HprofFormatException { @@ -853,7 +901,7 @@ public class Parser { } public long getId() { - return mBuffer.getInt(); + return mBuffer.getInt() & 0xFFFFFFFFL; } public boolean getBool() { diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/PathElement.java b/tools/ahat/src/main/com/android/ahat/heapdump/PathElement.java index 196a24628cde1a749d5bdeb6436771081e8df9e1..5ce0b1edfe5f62e44bdf8a88dde9e5325cc65224 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/PathElement.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/PathElement.java @@ -16,11 +16,51 @@ package com.android.ahat.heapdump; +/** + * A single element along a reference path from a GC root to an instance in + * the heap dump. + *

+ * For example, assuming object A is a root a path to some object X might look + * like: + *

+ *   A.x --> B.y --> C.z --> X
+ * 
+ * + * A path element is a single node of that path, such as B.y. + * @see AhatInstance#getPathFromGcRoot + */ public class PathElement implements Diffable { + /** + * The instance along the reference path that this PathElement is associated + * with. + */ public final AhatInstance instance; + + /** + * A human readable description of which field in instance is + * followed to reach the next element in the path. + * Some examples: + *
    + *
  • "mBlah" for a class instance + *
  • "[4]" for an array instance + *
  • "" for the last element of the path + *
+ */ public final String field; + + /** + * True if instance is a (not necessarily immediate) dominator + * of the final object in the path. + */ public boolean isDominator; + /** + * Constructs a PathElement object. + * isDominator is set to false. + * + * @param instance the path element instance + * @param field the path element field + */ public PathElement(AhatInstance instance, String field) { this.instance = instance; this.field = field; diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/RootType.java b/tools/ahat/src/main/com/android/ahat/heapdump/RootType.java index 734f889af62450101ba23ad98692c42617061a4f..99d85dc9401a9f0c7372825c75d9e8ad3a78466f 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/RootType.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/RootType.java @@ -16,20 +16,80 @@ package com.android.ahat.heapdump; +/** + * Enumeration representing object root types as defined in the binary heap + * dump format specification. + */ public enum RootType { + /** + * There is a JNI Global Reference for the object in question. + */ JNI_GLOBAL (1 << 0), + + /** + * There is a JNI Local Reference for the object in question. + */ JNI_LOCAL (1 << 1), + + /** + * The object in question is a parameter or local variable of a running + * method. + */ JAVA_FRAME (1 << 2), + + /** + * The object in question is a parameter of a running JNI method. + */ NATIVE_STACK (1 << 3), + + /** + * The object is a class object that cannot be unloaded. + */ STICKY_CLASS (1 << 4), + + /** + * The object is referenced from an active thread block. + */ THREAD_BLOCK (1 << 5), + + /** + * The object's monitor is currently in use. + */ MONITOR (1 << 6), + + /** + * The object is a running thread. + */ THREAD (1 << 7), + + /** + * The object is an interned string. + */ INTERNED_STRING (1 << 8), + + /** + * The object is being used by the debugger. + */ DEBUGGER (1 << 9), + + /** + * The object is being used by the VM internally. + */ VM_INTERNAL (1 << 10), + + /** + * The object has no given reason for being considered a root. + */ UNKNOWN (1 << 11), + + /** + * The object's monitor is currently in use from JNI. + */ JNI_MONITOR (1 << 12), + + /** + * The object is waiting to be finalized. + */ FINALIZING (1 << 13); final int mask; diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/Site.java b/tools/ahat/src/main/com/android/ahat/heapdump/Site.java index 4978d5283052b62d6eaaae4244b7e55e96fbfd0a..72c0a4a750cd04ad25a0f526b42b7d5e5c5ff708 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/Site.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/Site.java @@ -24,6 +24,10 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +/** + * Used to collection information about objects allocated at a particular + * allocation site. + */ public class Site implements Diffable { // The site that this site was directly called from. // mParent is null for the root site. @@ -61,18 +65,39 @@ public class Site implements Diffable { private Site mBaseline; + /** + * Summary information about instances allocated at a particular allocation + * site that are instances of a particular class and allocated on a + * particular heap. + */ public static class ObjectsInfo implements Diffable { + /** + * The heap that the summarized objects belong to. + */ public AhatHeap heap; - public AhatClassObj classObj; // May be null. + + /** + * The class of the summarized objects. + */ + public AhatClassObj classObj; // May be null. Not sure why. + + /** + * The number of instances included in the summary. + */ public long numInstances; + + /** + * The sum of the shallow size of each instance included in the summary. + */ public Size numBytes; + private ObjectsInfo baseline; /** - * Construct a new, empty objects info for the given heap and class + * Constructs a new, empty objects info for the given heap and class * combination. */ - public ObjectsInfo(AhatHeap heap, AhatClassObj classObj) { + ObjectsInfo(AhatHeap heap, AhatClassObj classObj) { this.heap = heap; this.classObj = classObj; this.numInstances = 0; @@ -82,12 +107,14 @@ public class Site implements Diffable { /** * Returns the name of the class this ObjectsInfo is associated with. + * + * @return the name of this object info's class */ public String getClassName() { return classObj == null ? "???" : classObj.getName(); } - public void setBaseline(ObjectsInfo baseline) { + void setBaseline(ObjectsInfo baseline) { this.baseline = baseline; } @@ -121,11 +148,11 @@ public class Site implements Diffable { } /** - * Get a child site of this site. - * Returns the site at which the instance was allocated. - * @param frames - The list of frames in the stack trace, starting with the - * inner-most frame. May be null, in which case this site is - * returned. + * Gets a child site of this site. + * @param frames the list of frames in the stack trace, starting with the + * inner-most frame. May be null, in which case this site is + * returned. + * @return the child site */ Site getSite(ProguardMap.Frame[] frames) { return frames == null ? this : getSite(this, frames); @@ -211,22 +238,29 @@ public class Site implements Diffable { return id; } - // Get the size of a site for a specific heap. + /** + * Returns the size of all objects on the given heap allocated at this site. + * Includes objects belonging to heap allocated at this and + * child sites. + * + * @param heap the heap to query the size for + * @return the total shallow size of objects in this site + */ public Size getSize(AhatHeap heap) { return mSizesByHeap[heap.getIndex()]; } /** - * Collect the objects allocated under this site, optionally filtered by + * Collects the objects allocated under this site, optionally filtered by * heap name or class name. Includes objects allocated in children sites. - * @param heapName - The name of the heap the collected objects should - * belong to. This may be null to indicate objects of - * every heap should be collected. - * @param className - The name of the class the collected objects should - * belong to. This may be null to indicate objects of - * every class should be collected. - * @param objects - Out parameter. A collection of objects that all - * collected objects should be added to. + * @param heapName the name of the heap the collected objects should + * belong to. This may be null to indicate objects of + * every heap should be collected. + * @param className the name of the class the collected objects should + * belong to. This may be null to indicate objects of + * every class should be collected. + * @param objects out parameter. A collection of objects that all + * collected objects should be added to. */ public void getObjects(String heapName, String className, Collection objects) { for (AhatInstance inst : mObjects) { @@ -263,11 +297,24 @@ public class Site implements Diffable { return info; } + /** + * Return a summary breakdown of the objects allocated at this site. + * Objects are grouped by class and heap and summarized into a single + * {@link ObjectsInfo}. This method returns all the groups for this + * allocation site. + * + * @return all ObjectInfo summaries for instances allocated at this site + */ public List getObjectsInfos() { return mObjectsInfos; } - // Get the combined size of the site for all heaps. + /** + * Returns the combined size of the site for all heaps. + * Includes all objects allocated at this and child sites. + * + * @return total shallow size of objects in this site + */ public Size getTotalSize() { Size total = Size.ZERO; for (Size size : mSizesByHeap) { @@ -277,39 +324,70 @@ public class Site implements Diffable { } /** - * Return the site this site was called from. + * Returns the site this site was called from. * Returns null for the root site. + * + * @return the site this site was called from */ public Site getParent() { return mParent; } + /** + * Returns the name of the method this allocation site belongs to. + * For example, "equals". + * + * @return the method name of the allocation site + */ public String getMethodName() { return mMethodName; } + /** + * Returns the signature of the method this allocation site belongs to. + * For example, "(Ljava/lang/Object;)Z". + * + * @return the signature of method the allocation site belongs to + */ public String getSignature() { return mSignature; } + /** + * Returns the name of the Java file where this allocation site is found. + * + * @return the file the allocation site belongs to + */ public String getFilename() { return mFilename; } + /** + * Returns the line number of the code in the source file that the + * allocation site refers to. + * + * @return the allocation site line number + */ public int getLineNumber() { return mLineNumber; } /** * Returns the unique id of this site. + * This is an arbitrary unique id computed after processing the heap dump. + * + * @return the site id */ public long getId() { return mId; } /** - * Find the child site with the given id. + * Returns the child site with the given id. * Returns null if no such site was found. + * + * @param id the id of the child site to find + * @return the found child site */ public Site findSite(long id) { if (id == mId) { @@ -341,6 +419,8 @@ public class Site implements Diffable { /** * Returns an unmodifiable list of this site's immediate children. + * + * @return this site's child sites */ public List getChildren() { return Collections.unmodifiableList(mChildren); diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/Size.java b/tools/ahat/src/main/com/android/ahat/heapdump/Size.java index 7c8db900dfca1b6ac31f69e1b75740fa543ee3b8..a4593e195b470e2961b36a77de9691bef2bc6f77 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/Size.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/Size.java @@ -17,47 +17,76 @@ package com.android.ahat.heapdump; /** - * The Size class is used to represent how much space an instance takes up. - * + * Used to represent how much space an instance takes up. * An abstraction is introduced rather than using a long directly in order to * more easily keep track of the different components of the size. For * example, some instances may have associated native, code, or graphics * sizes. - * + *

* Size objects are immutable. */ public class Size { private final long mJavaSize; private final long mRegisteredNativeSize; + /** + * An instance of Size with 0 for all categories. + */ public static Size ZERO = new Size(0, 0); + /** + * Constructs a new instance of Size. + * + * @param javaSize number of bytes in the java category + * @param registeredNativeSize number of bytes in the registeredNativeSize + * category + */ public Size(long javaSize, long registeredNativeSize) { mJavaSize = javaSize; mRegisteredNativeSize = registeredNativeSize; } + /** + * Returns the sum of the size of all categories. + * + * @return the total size + */ public long getSize() { return mJavaSize + mRegisteredNativeSize; } + /** + * Returns the size of the java category. + * + * @return the java category size + */ public long getJavaSize() { return mJavaSize; } + /** + * Returns the size of the registered native category. + * + * @return the registered native category size + */ public long getRegisteredNativeSize() { return mRegisteredNativeSize; } /** - * Returns true if all the fields of this size object are zero. + * Returns true if all categories of this size are zero. + * + * @return true if the size is zero */ public boolean isZero() { return mJavaSize == 0 && mRegisteredNativeSize == 0; } /** - * Return a new Size object that is the sum of this size and the other. + * Returns a new Size object that is the sum of this size and the other. + * + * @param other the size to sum with this size + * @return the new size object */ public Size plus(Size other) { if (isZero()) { @@ -71,8 +100,11 @@ public class Size { } /** - * Return a new Size object that has 'size' more registered native size than - * this Size object. + * Returns a new Size object that has size more registered + * native size than this Size object. + * + * @param size the size to add to the registered native category + * @return the new size object */ public Size plusRegisteredNativeSize(long size) { return new Size(mJavaSize, mRegisteredNativeSize + size); diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/Sort.java b/tools/ahat/src/main/com/android/ahat/heapdump/Sort.java index efe0d6b59b24a4f1d270ec86a060fc8da53579d9..a629b3ce7f909a9c59d3dcbcbf6ab1dc76541280 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/Sort.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/Sort.java @@ -25,14 +25,14 @@ import java.util.List; /** * Provides Comparators and helper functions for sorting Instances, Sites, and * other things. - * + *

* Note: The Comparators defined here impose orderings that are inconsistent * with equals. They should not be used for element lookup or search. They * should only be used for showing elements to the user in different orders. */ public class Sort { /** - * Compare sizes by their total size. + * Compares sizes by their total size. * This sorts sizes from smaller total size to larger total size. */ public static final Comparator SIZE_BY_SIZE = new Comparator() { @@ -43,7 +43,7 @@ public class Sort { }; /** - * Compare instances by their total retained size. + * Compares instances by their total retained size. * Different instances with the same total retained size are considered * equal for the purposes of comparison. * This sorts instances from larger retained size to smaller retained size. @@ -57,12 +57,12 @@ public class Sort { }; /** - * Compare instances by their retained size for a given heap index. + * Compares instances by their retained size for a given heap index. * Different instances with the same total retained size are considered * equal for the purposes of comparison. * This sorts instances from larger retained size to smaller retained size. */ - public static class InstanceByHeapRetainedSize implements Comparator { + private static class InstanceByHeapRetainedSize implements Comparator { private AhatHeap mHeap; public InstanceByHeapRetainedSize(AhatHeap heap) { @@ -76,16 +76,28 @@ public class Sort { } /** - * Compare objects based on a list of comparators, giving priority to the + * Compares objects based on a list of comparators, giving priority to the * earlier comparators in the list. */ - public static class WithPriority implements Comparator { + private static class WithPriority implements Comparator { private List> mComparators; + /** + * Constructs a comparator giving sort priority to earlier comparators in + * the list. + * + * @param comparators the list of comparators to use for sorting + */ public WithPriority(Comparator... comparators) { mComparators = Arrays.asList(comparators); } + /** + * Constructs a comparator giving sort priority to earlier comparators in + * the list. + * + * @param comparators the list of comparators to use for sorting + */ public WithPriority(List> comparators) { mComparators = comparators; } @@ -101,6 +113,27 @@ public class Sort { } } + /** + * Returns a comparator that gives sort priority to earlier comparators in + * the list. + * + * @param the type of object being sorted + * @param comparators the list of comparators to use for sorting + * @return the composite comparator + */ + public static Comparator withPriority(Comparator... comparators) { + return new WithPriority(comparators); + } + + /** + * Returns a comparator that gives a default instance sort for the given + * snapshot. + * Objects are sorted by retained size, with priority given to the "app" + * heap if present. + * + * @param snapshot the snapshot to use the comparator with + * @return the default instance comparator + */ public static Comparator defaultInstanceCompare(AhatSnapshot snapshot) { List> comparators = new ArrayList>(); @@ -116,14 +149,19 @@ public class Sort { } /** - * Compare Sites by the size of objects allocated on a given heap. + * Compares Sites by the size of objects allocated on a given heap. * Different object infos with the same size on the given heap are * considered equal for the purposes of comparison. * This sorts sites from larger size to smaller size. */ - public static class SiteByHeapSize implements Comparator { + private static class SiteByHeapSize implements Comparator { AhatHeap mHeap; + /** + * Constructs a SiteByHeapSize comparator. + * + * @param heap the heap to use when comparing sizes + */ public SiteByHeapSize(AhatHeap heap) { mHeap = heap; } @@ -135,7 +173,7 @@ public class Sort { } /** - * Compare Sites by the total size of objects allocated. + * Compares Sites by the total size of objects allocated. * This sorts sites from larger size to smaller size. */ public static final Comparator SITE_BY_TOTAL_SIZE = new Comparator() { @@ -145,6 +183,14 @@ public class Sort { } }; + /** + * Compares Sites using a default comparison order. + * This sorts sites from larger size to smaller size, giving preference to + * sites with more allocation on the "app" heap, if present. + * + * @param snapshot the snapshot to use the comparator with + * @return the default site comparator + */ public static Comparator defaultSiteCompare(AhatSnapshot snapshot) { List> comparators = new ArrayList>(); @@ -174,7 +220,7 @@ public class Sort { }; /** - * Compare Site.ObjectsInfo by heap name. + * Compares Site.ObjectsInfo by heap name. * Different object infos with the same heap name are considered equal for * the purposes of comparison. */ @@ -187,7 +233,7 @@ public class Sort { }; /** - * Compare Site.ObjectsInfo by class name. + * Compares Site.ObjectsInfo by class name. * Different object infos with the same class name are considered equal for * the purposes of comparison. */ @@ -202,7 +248,7 @@ public class Sort { }; /** - * Compare FieldValue by field name. + * Compares FieldValue by field name. */ public static final Comparator FIELD_VALUE_BY_NAME = new Comparator() { @@ -213,7 +259,7 @@ public class Sort { }; /** - * Compare FieldValue by type name. + * Compares FieldValue by type name. */ public static final Comparator FIELD_VALUE_BY_TYPE = new Comparator() { diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/Type.java b/tools/ahat/src/main/com/android/ahat/heapdump/Type.java index 40249615a2dbf0ad38663666cb13e22f6337d657..ff798645059735bbd33d15c5c3925b98a4c676f9 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/Type.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/Type.java @@ -16,18 +16,63 @@ package com.android.ahat.heapdump; +/** + * Enum corresponding to basic types from the binary heap dump format. + */ public enum Type { + /** + * Type used for any Java object. + */ OBJECT("Object", 4), + + /** + * The primitive boolean type. + */ BOOLEAN("boolean", 1), + + /** + * The primitive char type. + */ CHAR("char", 2), + + /** + * The primitive float type. + */ FLOAT("float", 4), + + /** + * The primitive double type. + */ DOUBLE("double", 8), + + /** + * The primitive byte type. + */ BYTE("byte", 1), + + /** + * The primitive short type. + */ SHORT("short", 2), + + /** + * The primitive int type. + */ INT("int", 4), + + /** + * The primitive long type. + */ LONG("long", 8); + /** + * The name of the type. + */ public final String name; + + /** + * The number of bytes taken up by values of this type in the Java heap. + */ final int size; Type(String name, int size) { diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/Value.java b/tools/ahat/src/main/com/android/ahat/heapdump/Value.java index eea427774bcb550e67466cad562766edd2fd0a4e..b219bf1564d657e962b4ba241ac291387d446cb2 100644 --- a/tools/ahat/src/main/com/android/ahat/heapdump/Value.java +++ b/tools/ahat/src/main/com/android/ahat/heapdump/Value.java @@ -17,48 +17,107 @@ package com.android.ahat.heapdump; /** - * Value represents a field value in a heap dump. The field value is either a - * subclass of AhatInstance or a primitive Java type. + * A Java instance or primitive value from a parsed heap dump. + * Note: To save memory, a null Value is used to represent a null Java + * instance from the heap dump. */ public abstract class Value { + /** + * Constructs a Value for an AhatInstance. + * Note: returns null for null value. + * + * @param value the AhatInstance to make into a value + * @return the constructed value. + */ public static Value pack(AhatInstance value) { return value == null ? null : new InstanceValue(value); } + /** + * Constructs a Value for a boolean. + * + * @param value the boolean to make into a value + * @return the constructed value. + */ public static Value pack(boolean value) { return new BooleanValue(value); } + /** + * Constructs a Value for a char. + * + * @param value the char to make into a value + * @return the constructed value. + */ public static Value pack(char value) { return new CharValue(value); } + /** + * Constructs a Value for a float. + * + * @param value the float to make into a value + * @return the constructed value. + */ public static Value pack(float value) { return new FloatValue(value); } + /** + * Constructs a Value for a double. + * + * @param value the double to make into a value + * @return the constructed value. + */ public static Value pack(double value) { return new DoubleValue(value); } + /** + * Constructs a Value for a byte. + * + * @param value the byte to make into a value + * @return the constructed value. + */ public static Value pack(byte value) { return new ByteValue(value); } + /** + * Constructs a Value for a short. + * + * @param value the short to make into a value + * @return the constructed value. + */ public static Value pack(short value) { return new ShortValue(value); } + /** + * Constructs a Value for a int. + * + * @param value the int to make into a value + * @return the constructed value. + */ public static Value pack(int value) { return new IntValue(value); } + /** + * Constructs a Value for a long. + * + * @param value the long to make into a value + * @return the constructed value. + */ public static Value pack(long value) { return new LongValue(value); } /** - * Return the type of the given value. + * Returns the type of the given value. + * + * @param value the value to get the type of + * @return the value's type */ public static Type getType(Value value) { return value == null ? Type.OBJECT : value.getType(); @@ -70,62 +129,78 @@ public abstract class Value { abstract Type getType(); /** - * Returns true if the Value is an AhatInstance, as opposed to a Java - * primitive value. + * Returns true if the Value is an AhatInstance rather than a primitive + * value. + * + * @return true if the value is an AhatInstance */ public boolean isAhatInstance() { return false; } /** - * Return the Value as an AhatInstance if it is one. + * Returns the Value as an AhatInstance if it is one. * Returns null if the Value represents a Java primitive value. + * + * @return the AhatInstance packed into this value */ public AhatInstance asAhatInstance() { return null; } /** - * Returns true if the Value is an Integer. + * Returns true if the Value is an int. + * + * @return true if the value is an int. */ public boolean isInteger() { return false; } /** - * Return the Value as an Integer if it is one. - * Returns null if the Value does not represent an Integer. + * Returns the Value as an int if it is one. + * Returns null if the Value does not represent an int. + * + * @return the int packed into this value */ public Integer asInteger() { return null; } /** - * Returns true if the Value is an Long. + * Returns true if the Value is an long. + * + * @return true if the value is an long. */ public boolean isLong() { return false; } /** - * Return the Value as an Long if it is one. - * Returns null if the Value does not represent an Long. + * Returns the Value as an long if it is one. + * Returns null if the Value does not represent an long. + * + * @return the long packed into this value */ public Long asLong() { return null; } /** - * Return the Value as a Byte if it is one. - * Returns null if the Value does not represent a Byte. + * Returns the Value as an byte if it is one. + * Returns null if the Value does not represent an byte. + * + * @return the byte packed into this value */ public Byte asByte() { return null; } /** - * Return the Value as a Char if it is one. - * Returns null if the Value does not represent a Char. + * Returns the Value as an char if it is one. + * Returns null if the Value does not represent an char. + * + * @return the char packed into this value */ public Character asChar() { return null; @@ -134,10 +209,18 @@ public abstract class Value { @Override public abstract String toString(); - public Value getBaseline() { + private Value getBaseline() { return this; } + /** + * Returns the baseline of the given value for the purposes of diff. + * This method can be used to handle the case when the Value is null. + * + * @param value the value to get the baseline of + * @return the baseline of the value + * @see Diffable#getBaseline + */ public static Value getBaseline(Value value) { return value == null ? null : value.getBaseline(); } @@ -313,7 +396,6 @@ public abstract class Value { return mInstance.toString(); } - @Override public Value getBaseline() { return InstanceValue.pack(mInstance.getBaseline()); } diff --git a/tools/ahat/src/main/com/android/ahat/proguard/ProguardMap.java b/tools/ahat/src/main/com/android/ahat/proguard/ProguardMap.java index 131bbf3cf6b573a489ed604e4b132e097e6bdb5c..79a737cc188df9b77f02d4463825368e595f7ec1 100644 --- a/tools/ahat/src/main/com/android/ahat/proguard/ProguardMap.java +++ b/tools/ahat/src/main/com/android/ahat/proguard/ProguardMap.java @@ -26,7 +26,10 @@ import java.text.ParseException; import java.util.HashMap; import java.util.Map; -// Class used to deobfuscate classes, fields, and stack frames. +/** + * A representation of a proguard mapping for deobfuscating class names, + * field names, and stack frames. + */ public class ProguardMap { private static final String ARRAY_SYMBOL = "[]"; @@ -88,18 +91,20 @@ public class ProguardMap { String key = obfuscatedMethodName + clearSignature; FrameData frame = mFrames.get(key); if (frame == null) { - return new Frame(obfuscatedMethodName, clearSignature, - obfuscatedFilename, obfuscatedLine); + frame = new FrameData(obfuscatedMethodName, 0); } return new Frame(frame.clearMethodName, clearSignature, - getFileName(clearClassName, frame.clearMethodName), - obfuscatedLine - frame.lineDelta); + getFileName(clearClassName), obfuscatedLine - frame.lineDelta); } } private Map mClassesFromClearName = new HashMap(); private Map mClassesFromObfuscatedName = new HashMap(); + /** + * Information associated with a stack frame that identifies a particular + * line of source code. + */ public static class Frame { Frame(String method, String signature, String filename, int line) { this.method = method; @@ -108,9 +113,28 @@ public class ProguardMap { this.line = line; } + /** + * The name of the method the stack frame belongs to. + * For example, "equals". + */ public final String method; + + /** + * The signature of the method the stack frame belongs to. + * For example, "(Ljava/lang/Object;)Z". + */ public final String signature; + + /** + * The name of the file with containing the line of source that the stack + * frame refers to. + */ public final String filename; + + /** + * The line number of the code in the source file that the stack frame + * refers to. + */ public final int line; } @@ -118,13 +142,44 @@ public class ProguardMap { throw new ParseException(msg, 0); } - // Read in proguard mapping information from the given file. + /** + * Creates a new empty proguard mapping. + * The {@link #readFromFile readFromFile} and + * {@link #readFromReader readFromReader} methods can be used to populate + * the proguard mapping with proguard mapping information. + */ + public ProguardMap() { + } + + /** + * Adds the proguard mapping information in mapFile to this + * proguard mapping. + * The mapFile should be a proguard mapping file generated with + * the -printmapping option when proguard was run. + * + * @param mapFile the name of a file with proguard mapping information + * @throws FileNotFoundException If the mapFile could not be + * found + * @throws IOException If an input exception occurred. + * @throws ParseException If the mapFile is not a properly + * formatted proguard mapping file. + */ public void readFromFile(File mapFile) throws FileNotFoundException, IOException, ParseException { readFromReader(new FileReader(mapFile)); } - // Read in proguard mapping information from the given Reader. + /** + * Adds the proguard mapping information read from mapReader to + * this proguard mapping. + * mapReader should be a Reader of a proguard mapping file + * generated with the -printmapping option when proguard was run. + * + * @param mapReader a Reader for reading the proguard mapping information + * @throws IOException If an input exception occurred. + * @throws ParseException If the mapFile is not a properly + * formatted proguard mapping file. + */ public void readFromReader(Reader mapReader) throws IOException, ParseException { BufferedReader reader = new BufferedReader(mapReader); String line = reader.readLine(); @@ -209,8 +264,15 @@ public class ProguardMap { reader.close(); } - // Returns the deobfuscated version of the given class name. If no - // deobfuscated version is known, the original string is returned. + /** + * Returns the deobfuscated version of the given obfuscated class name. + * If this proguard mapping does not include information about how to + * deobfuscate the obfuscated class name, the obfuscated class name + * is returned. + * + * @param obfuscatedClassName the obfuscated class name to deobfuscate + * @return the deobfuscated class name. + */ public String getClassName(String obfuscatedClassName) { // Class names for arrays may have trailing [] that need to be // stripped before doing the lookup. @@ -226,9 +288,17 @@ public class ProguardMap { return clearBaseName + arraySuffix; } - // Returns the deobfuscated version of the given field name for the given - // (clear) class name. If no deobfuscated version is known, the original - // string is returned. + /** + * Returns the deobfuscated version of the obfuscated field name for the + * given deobfuscated class name. + * If this proguard mapping does not include information about how to + * deobfuscate the obfuscated field name, the obfuscated field name is + * returned. + * + * @param clearClass the deobfuscated name of the class the field belongs to + * @param obfuscatedField the obfuscated field name to deobfuscate + * @return the deobfuscated field name. + */ public String getFieldName(String clearClass, String obfuscatedField) { ClassData classData = mClassesFromClearName.get(clearClass); if (classData == null) { @@ -237,8 +307,21 @@ public class ProguardMap { return classData.getField(obfuscatedField); } - // Returns the deobfuscated frame for the given obfuscated frame and (clear) - // class name. As much of the frame is deobfuscated as can be. + /** + * Returns the deobfuscated version of the obfuscated stack frame + * information for the given deobfuscated class name. + * If this proguard mapping does not include information about how to + * deobfuscate the obfuscated stack frame information, the obfuscated stack + * frame information is returned. + * + * @param clearClassName the deobfuscated name of the class the stack frame's + * method belongs to + * @param obfuscatedMethodName the obfuscated method name to deobfuscate + * @param obfuscatedSignature the obfuscated method signature to deobfuscate + * @param obfuscatedFilename the obfuscated file name to deobfuscate. + * @param obfuscatedLine the obfuscated line number to deobfuscate. + * @return the deobfuscated stack frame information. + */ public Frame getFrame(String clearClassName, String obfuscatedMethodName, String obfuscatedSignature, String obfuscatedFilename, int obfuscatedLine) { String clearSignature = getSignature(obfuscatedSignature); @@ -313,15 +396,10 @@ public class ProguardMap { return builder.toString(); } - // Return a file name for the given clear class name and method. - private static String getFileName(String clearClass, String method) { - int dot = method.lastIndexOf('.'); - if (dot != -1) { - clearClass = method.substring(0, dot); - } - + // Return a file name for the given clear class name. + private static String getFileName(String clearClass) { String filename = clearClass; - dot = filename.lastIndexOf('.'); + int dot = filename.lastIndexOf('.'); if (dot != -1) { filename = filename.substring(dot + 1); } diff --git a/tools/ahat/src/test-dump/DumpedStuff.java b/tools/ahat/src/test-dump/DumpedStuff.java new file mode 100644 index 0000000000000000000000000000000000000000..98ead074927c79a4de0b5125e08967e61f6af828 --- /dev/null +++ b/tools/ahat/src/test-dump/DumpedStuff.java @@ -0,0 +1,160 @@ +/* + * 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. + */ + +import java.lang.ref.PhantomReference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; +import libcore.util.NativeAllocationRegistry; + +// We take a heap dump that includes a single instance of this +// DumpedStuff class. Objects stored as fields in this class can be easily +// found in the hprof dump by searching for the instance of the DumpedStuff +// class and reading the desired field. +public class DumpedStuff extends SuperDumpedStuff { + private void allocateObjectAtKnownSite() { + objectAllocatedAtKnownSite = new Object(); + allocateObjectAtKnownSubSite(); + allocateObjectAtObfSuperSite(); + allocateObjectAtUnObfSuperSite(); + allocateObjectAtOverriddenSite(); + } + + private void allocateObjectAtKnownSubSite() { + objectAllocatedAtKnownSubSite = new Object(); + } + + public void allocateObjectAtOverriddenSite() { + objectAllocatedAtOverriddenSite = new Object(); + } + + DumpedStuff(boolean baseline) { + allocateObjectAtKnownSite(); + + int n = baseline ? 400000 : 1000000; + bigArray = new byte[n]; + for (int i = 0; i < n; i++) { + bigArray[i] = (byte)((i * i) & 0xFF); + } + + // 0x12345, 50000, and 0xABCDABCD are arbitrary values. + NativeAllocationRegistry registry = new NativeAllocationRegistry( + Main.class.getClassLoader(), 0x12345, 50000); + registry.registerNativeAllocation(anObject, 0xABCDABCD); + + { + Object object = new Object(); + aLongStrongPathToSamplePathObject = new Reference(new Reference(new Reference(object))); + aShortWeakPathToSamplePathObject = new WeakReference(new Reference(object)); + } + + addedObject = baseline ? null : new AddedObject(); + removedObject = baseline ? new RemovedObject() : null; + modifiedObject = new ModifiedObject(); + modifiedObject.value = baseline ? 5 : 8; + modifiedObject.modifiedRefField = baseline ? "A1" : "A2"; + modifiedObject.unmodifiedRefField = "B"; + modifiedStaticField = baseline ? "C1" : "C2"; + modifiedArray = baseline ? new int[]{0, 1, 2, 3} : new int[]{3, 1, 2, 0}; + + // Deep matching dominator trees shouldn't smash the stack when we try + // to diff them. Make some deep dominator trees to help test it. + for (int i = 0; i < 10000; i++) { + StackSmasher smasher = new StackSmasher(); + smasher.child = stackSmasher; + stackSmasher = smasher; + + if (!baseline) { + smasher = new StackSmasher(); + smasher.child = stackSmasherAdded; + stackSmasherAdded = smasher; + } + } + + gcPathArray[2].right.left = gcPathArray[2].left.right; + } + + public static class ObjectTree { + public ObjectTree left; + public ObjectTree right; + + public ObjectTree(ObjectTree left, ObjectTree right) { + this.left = left; + this.right = right; + } + } + + public static class AddedObject { + } + + public static class RemovedObject { + } + + public static class UnchangedObject { + } + + public static class ModifiedObject { + public int value; + public String modifiedRefField; + public String unmodifiedRefField; + } + + public static class StackSmasher { + public StackSmasher child; + } + + public static class Reference { + public Object referent; + + public Reference(Object referent) { + this.referent = referent; + } + } + + public String basicString = "hello, world"; + public String nonAscii = "Sigma (Æ©) is not ASCII"; + public String embeddedZero = "embedded\0..."; // Non-ASCII for string compression purposes. + public char[] charArray = "char thing".toCharArray(); + public String nullString = null; + public Object anObject = new Object(); + public Reference aReference = new Reference(anObject); + public ReferenceQueue referenceQueue = new ReferenceQueue(); + public PhantomReference aPhantomReference = new PhantomReference(anObject, referenceQueue); + public WeakReference aWeakReference = new WeakReference(anObject, referenceQueue); + public WeakReference aNullReferentReference = new WeakReference(null, referenceQueue); + public SoftReference aSoftReference = new SoftReference(new Object()); + public byte[] bigArray; + public ObjectTree[] gcPathArray = new ObjectTree[]{null, null, + new ObjectTree( + new ObjectTree(null, new ObjectTree(null, null)), + new ObjectTree(null, null)), + null}; + public Reference aLongStrongPathToSamplePathObject; + public WeakReference aShortWeakPathToSamplePathObject; + public WeakReference aWeakRefToGcRoot = new WeakReference(Main.class); + public SoftReference aWeakChain = new SoftReference(new Reference(new Reference(new Object()))); + public Object[] basicStringRef; + public AddedObject addedObject; + public UnchangedObject unchangedObject = new UnchangedObject(); + public RemovedObject removedObject; + public ModifiedObject modifiedObject; + public StackSmasher stackSmasher; + public StackSmasher stackSmasherAdded; + public static String modifiedStaticField; + public int[] modifiedArray; + public Object objectAllocatedAtKnownSite; + public Object objectAllocatedAtKnownSubSite; +} diff --git a/tools/ahat/src/test-dump/Main.java b/tools/ahat/src/test-dump/Main.java index 079be7da81d739bac3a530740344833a8fb688a5..de3674846bd8022f9f627ea7787a1033a6bee938 100644 --- a/tools/ahat/src/test-dump/Main.java +++ b/tools/ahat/src/test-dump/Main.java @@ -16,11 +16,6 @@ import dalvik.system.VMDebug; import java.io.IOException; -import java.lang.ref.PhantomReference; -import java.lang.ref.ReferenceQueue; -import java.lang.ref.SoftReference; -import java.lang.ref.WeakReference; -import libcore.util.NativeAllocationRegistry; import org.apache.harmony.dalvik.ddmc.DdmVmInternal; /** @@ -31,138 +26,6 @@ public class Main { // collected before we take the heap dump. public static DumpedStuff stuff; - public static class ObjectTree { - public ObjectTree left; - public ObjectTree right; - - public ObjectTree(ObjectTree left, ObjectTree right) { - this.left = left; - this.right = right; - } - } - - public static class AddedObject { - } - - public static class RemovedObject { - } - - public static class UnchangedObject { - } - - public static class ModifiedObject { - public int value; - public String modifiedRefField; - public String unmodifiedRefField; - } - - public static class StackSmasher { - public StackSmasher child; - } - - public static class Reference { - public Object referent; - - public Reference(Object referent) { - this.referent = referent; - } - } - - // We will take a heap dump that includes a single instance of this - // DumpedStuff class. Objects stored as fields in this class can be easily - // found in the hprof dump by searching for the instance of the DumpedStuff - // class and reading the desired field. - public static class DumpedStuff { - public String basicString = "hello, world"; - public String nonAscii = "Sigma (Æ©) is not ASCII"; - public String embeddedZero = "embedded\0..."; // Non-ASCII for string compression purposes. - public char[] charArray = "char thing".toCharArray(); - public String nullString = null; - public Object anObject = new Object(); - public Reference aReference = new Reference(anObject); - public ReferenceQueue referenceQueue = new ReferenceQueue(); - public PhantomReference aPhantomReference = new PhantomReference(anObject, referenceQueue); - public WeakReference aWeakReference = new WeakReference(anObject, referenceQueue); - public WeakReference aNullReferentReference = new WeakReference(null, referenceQueue); - public SoftReference aSoftReference = new SoftReference(new Object()); - public byte[] bigArray; - public ObjectTree[] gcPathArray = new ObjectTree[]{null, null, - new ObjectTree( - new ObjectTree(null, new ObjectTree(null, null)), - new ObjectTree(null, null)), - null}; - public Reference aLongStrongPathToSamplePathObject; - public WeakReference aShortWeakPathToSamplePathObject; - public WeakReference aWeakRefToGcRoot = new WeakReference(Main.class); - public SoftReference aWeakChain = new SoftReference(new Reference(new Reference(new Object()))); - public Object[] basicStringRef; - public AddedObject addedObject; - public UnchangedObject unchangedObject = new UnchangedObject(); - public RemovedObject removedObject; - public ModifiedObject modifiedObject; - public StackSmasher stackSmasher; - public StackSmasher stackSmasherAdded; - public static String modifiedStaticField; - public int[] modifiedArray; - public Object objectAllocatedAtKnownSite1; - public Object objectAllocatedAtKnownSite2; - - private void allocateObjectAtKnownSite1() { - objectAllocatedAtKnownSite1 = new Object(); - allocateObjectAtKnownSite2(); - } - - private void allocateObjectAtKnownSite2() { - objectAllocatedAtKnownSite2 = new Object(); - } - - DumpedStuff(boolean baseline) { - int n = baseline ? 400000 : 1000000; - bigArray = new byte[n]; - for (int i = 0; i < n; i++) { - bigArray[i] = (byte)((i * i) & 0xFF); - } - - // 0x12345, 50000, and 0xABCDABCD are arbitrary values. - NativeAllocationRegistry registry = new NativeAllocationRegistry( - Main.class.getClassLoader(), 0x12345, 50000); - registry.registerNativeAllocation(anObject, 0xABCDABCD); - - { - Object object = new Object(); - aLongStrongPathToSamplePathObject = new Reference(new Reference(new Reference(object))); - aShortWeakPathToSamplePathObject = new WeakReference(new Reference(object)); - } - - addedObject = baseline ? null : new AddedObject(); - removedObject = baseline ? new RemovedObject() : null; - modifiedObject = new ModifiedObject(); - modifiedObject.value = baseline ? 5 : 8; - modifiedObject.modifiedRefField = baseline ? "A1" : "A2"; - modifiedObject.unmodifiedRefField = "B"; - modifiedStaticField = baseline ? "C1" : "C2"; - modifiedArray = baseline ? new int[]{0, 1, 2, 3} : new int[]{3, 1, 2, 0}; - - allocateObjectAtKnownSite1(); - - // Deep matching dominator trees shouldn't smash the stack when we try - // to diff them. Make some deep dominator trees to help test it. - for (int i = 0; i < 10000; i++) { - StackSmasher smasher = new StackSmasher(); - smasher.child = stackSmasher; - stackSmasher = smasher; - - if (!baseline) { - smasher = new StackSmasher(); - smasher.child = stackSmasherAdded; - stackSmasherAdded = smasher; - } - } - - gcPathArray[2].right.left = gcPathArray[2].left.right; - } - } - public static void main(String[] args) throws IOException { if (args.length < 1) { System.err.println("no output file specified"); diff --git a/tools/ahat/src/test-dump/SuperDumpedStuff.java b/tools/ahat/src/test-dump/SuperDumpedStuff.java new file mode 100644 index 0000000000000000000000000000000000000000..5ec62aecb15a9d72960b1688c375bc19ace80550 --- /dev/null +++ b/tools/ahat/src/test-dump/SuperDumpedStuff.java @@ -0,0 +1,36 @@ +/* + * 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. + */ + +// A super class for DumpedStuff to test deobfuscation of methods inherited +// from the super class. +public class SuperDumpedStuff { + + public void allocateObjectAtObfSuperSite() { + objectAllocatedAtObfSuperSite = new Object(); + } + + public void allocateObjectAtUnObfSuperSite() { + objectAllocatedAtUnObfSuperSite = new Object(); + } + + public void allocateObjectAtOverriddenSite() { + objectAllocatedAtOverriddenSite = new Object(); + } + + public Object objectAllocatedAtObfSuperSite; + public Object objectAllocatedAtUnObfSuperSite; + public Object objectAllocatedAtOverriddenSite; +} diff --git a/tools/ahat/src/test/com/android/ahat/ProguardMapTest.java b/tools/ahat/src/test/com/android/ahat/ProguardMapTest.java index ad40f45665dc43fc8cff27d4d33e66916fb24ea8..02976b52853048f34d7b9446c4cf06090166d223 100644 --- a/tools/ahat/src/test/com/android/ahat/ProguardMapTest.java +++ b/tools/ahat/src/test/com/android/ahat/ProguardMapTest.java @@ -45,7 +45,6 @@ public class ProguardMapTest { + " 64:66:class.with.only.Fields methodWithObfRes() -> n\n" + " 80:80:void lineObfuscatedMethod():8:8 -> o\n" + " 90:90:void lineObfuscatedMethod2():9 -> p\n" - + " 120:121:void method.from.a.Superclass.supermethod() -> q\n" ; @Test @@ -160,12 +159,10 @@ public class ProguardMapTest { assertEquals("Methods.java", frame.filename); assertEquals(13, frame.line); - frame = map.getFrame("class.with.Methods", "q", "()V", "SourceFile.java", 120); - // TODO: Should this be "supermethod", instead of - // "method.from.a.Superclass.supermethod"? - assertEquals("method.from.a.Superclass.supermethod", frame.method); - assertEquals("()V", frame.signature); - assertEquals("Superclass.java", frame.filename); - assertEquals(120, frame.line); + // Some methods may not have been obfuscated. We should still be able + // to compute the filename properly. + frame = map.getFrame("class.with.Methods", "unObfuscatedMethodName", + "()V", "SourceFile.java", 0); + assertEquals("Methods.java", frame.filename); } } diff --git a/tools/ahat/src/test/com/android/ahat/SiteTest.java b/tools/ahat/src/test/com/android/ahat/SiteTest.java index dc0fe08297c00e53da3f503ee1dd49862152b39c..0443d7f264d9a940c340708b83b7a77e0ef03588 100644 --- a/tools/ahat/src/test/com/android/ahat/SiteTest.java +++ b/tools/ahat/src/test/com/android/ahat/SiteTest.java @@ -23,6 +23,7 @@ import java.io.IOException; import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertSame; public class SiteTest { @@ -31,36 +32,54 @@ public class SiteTest { TestDump dump = TestDump.getTestDump(); AhatSnapshot snapshot = dump.getAhatSnapshot(); - AhatInstance obj1 = dump.getDumpedAhatInstance("objectAllocatedAtKnownSite1"); - AhatInstance obj2 = dump.getDumpedAhatInstance("objectAllocatedAtKnownSite2"); - Site s2 = obj2.getSite(); - Site s1b = s2.getParent(); - Site s1a = obj1.getSite(); - Site s = s1a.getParent(); + AhatInstance oKnownSite = dump.getDumpedAhatInstance("objectAllocatedAtKnownSite"); + Site sKnownSite = oKnownSite.getSite(); + assertEquals("DumpedStuff.java", sKnownSite.getFilename()); + assertEquals("allocateObjectAtKnownSite", sKnownSite.getMethodName()); + assertEquals(29, sKnownSite.getLineNumber()); + assertSame(sKnownSite, snapshot.getSite(sKnownSite.getId())); - // TODO: The following commented out assertion fails due to a problem with - // proguard deobfuscation. That bug should be fixed. - // assertEquals("Main.java", s.getFilename()); + AhatInstance oKnownSubSite = dump.getDumpedAhatInstance("objectAllocatedAtKnownSubSite"); + Site sKnownSubSite = oKnownSubSite.getSite(); + assertEquals("DumpedStuff.java", sKnownSubSite.getFilename()); + assertEquals("allocateObjectAtKnownSubSite", sKnownSubSite.getMethodName()); + assertEquals(37, sKnownSubSite.getLineNumber()); + assertSame(sKnownSubSite, snapshot.getSite(sKnownSubSite.getId())); - assertEquals("Main.java", s1a.getFilename()); - assertEquals("Main.java", s1b.getFilename()); - assertEquals("Main.java", s2.getFilename()); + Site sKnownSubSiteParent = sKnownSubSite.getParent(); + assertEquals("DumpedStuff.java", sKnownSubSiteParent.getFilename()); + assertEquals("allocateObjectAtKnownSite", sKnownSubSiteParent.getMethodName()); + assertEquals(30, sKnownSubSiteParent.getLineNumber()); + assertSame(sKnownSubSiteParent, snapshot.getSite(sKnownSubSiteParent.getId())); - assertEquals("allocateObjectAtKnownSite1", s1a.getMethodName()); - assertEquals("allocateObjectAtKnownSite1", s1b.getMethodName()); - assertEquals("allocateObjectAtKnownSite2", s2.getMethodName()); + assertNotSame(sKnownSite, sKnownSubSiteParent); + assertSame(sKnownSite.getParent(), sKnownSubSiteParent.getParent()); - // TODO: The following commented out assertion fails due to a problem with - // stack frame line numbers - we don't get different line numbers - // for the different sites, so they are indistiguishable. The - // problem with line numbers should be understood and fixed. - // assertNotSame(s1a, s1b); + Site sKnownSiteParent = sKnownSite.getParent(); + assertEquals("DumpedStuff.java", sKnownSiteParent.getFilename()); + assertEquals("", sKnownSiteParent.getMethodName()); + assertEquals(45, sKnownSiteParent.getLineNumber()); + assertSame(sKnownSiteParent, snapshot.getSite(sKnownSiteParent.getId())); - assertSame(s1a.getParent(), s1b.getParent()); + AhatInstance oObfSuperSite = dump.getDumpedAhatInstance("objectAllocatedAtObfSuperSite"); + Site sObfSuperSite = oObfSuperSite.getSite(); + assertEquals("SuperDumpedStuff.java", sObfSuperSite.getFilename()); + assertEquals("allocateObjectAtObfSuperSite", sObfSuperSite.getMethodName()); + assertEquals(22, sObfSuperSite.getLineNumber()); + assertSame(sObfSuperSite, snapshot.getSite(sObfSuperSite.getId())); - assertSame(s, snapshot.getSite(s.getId())); - assertSame(s1a, snapshot.getSite(s1a.getId())); - assertSame(s1b, snapshot.getSite(s1b.getId())); - assertSame(s2, snapshot.getSite(s2.getId())); + AhatInstance oUnObfSuperSite = dump.getDumpedAhatInstance("objectAllocatedAtUnObfSuperSite"); + Site sUnObfSuperSite = oUnObfSuperSite.getSite(); + assertEquals("SuperDumpedStuff.java", sUnObfSuperSite.getFilename()); + assertEquals("allocateObjectAtUnObfSuperSite", sUnObfSuperSite.getMethodName()); + assertEquals(26, sUnObfSuperSite.getLineNumber()); + assertSame(sUnObfSuperSite, snapshot.getSite(sUnObfSuperSite.getId())); + + AhatInstance oOverriddenSite = dump.getDumpedAhatInstance("objectAllocatedAtOverriddenSite"); + Site sOverriddenSite = oOverriddenSite.getSite(); + assertEquals("DumpedStuff.java", sOverriddenSite.getFilename()); + assertEquals("allocateObjectAtOverriddenSite", sOverriddenSite.getMethodName()); + assertEquals(41, sOverriddenSite.getLineNumber()); + assertSame(sOverriddenSite, snapshot.getSite(sOverriddenSite.getId())); } } diff --git a/tools/breakpoint-logger/breakpoint_logger.cc b/tools/breakpoint-logger/breakpoint_logger.cc index b48a1788e36fb7502b9b7e2f30303e6abf13fb99..2f8b68239b752607a2737f3432f63a0aa29a99c3 100644 --- a/tools/breakpoint-logger/breakpoint_logger.cc +++ b/tools/breakpoint-logger/breakpoint_logger.cc @@ -383,7 +383,7 @@ static jint AgentStart(StartType start, return JNI_ERR; } - jvmtiCapabilities caps {}; // NOLINT [readability/braces] + jvmtiCapabilities caps{}; caps.can_generate_breakpoint_events = JNI_TRUE; caps.can_get_line_numbers = JNI_TRUE; caps.can_get_source_file_name = JNI_TRUE; @@ -394,7 +394,7 @@ static jint AgentStart(StartType start, return JNI_ERR; } - jvmtiEventCallbacks callbacks {}; // NOLINT [readability/braces] + jvmtiEventCallbacks callbacks{}; callbacks.Breakpoint = &BreakpointCB; callbacks.VMInit = &VMInitCB; diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh index 53b509336ee4d49f30b851556b6335966ec0dd13..fd9ad0bb0fbac01a7c8a2c055110805379720cc3 100755 --- a/tools/buildbot-build.sh +++ b/tools/buildbot-build.sh @@ -35,7 +35,7 @@ fi using_jack=$(get_build_var ANDROID_COMPILE_WITH_JACK) java_libraries_dir=${out_dir}/target/common/obj/JAVA_LIBRARIES -common_targets="vogar core-tests apache-harmony-jdwp-tests-hostdex jsr166-tests mockito-target libjdwp" +common_targets="vogar core-tests apache-harmony-jdwp-tests-hostdex jsr166-tests mockito-target" mode="target" j_arg="-j$(nproc)" showcommands= @@ -70,16 +70,23 @@ fi extra_args=SOONG_ALLOW_MISSING_DEPENDENCIES=true if [[ $mode == "host" ]]; then - make_command="make $j_arg $extra_args $showcommands build-art-host-tests $common_targets dx-tests" - make_command+=" ${out_dir}/host/linux-x86/lib/libjavacoretests.so " - make_command+=" ${out_dir}/host/linux-x86/lib64/libjavacoretests.so" - make_command+=" libwrapagentpropertiesd libwrapagentproperties" + make_command="make $j_arg $extra_args $showcommands build-art-host-tests $common_targets" + make_command+=" dx-tests" + mode_suffix="-host" elif [[ $mode == "target" ]]; then make_command="make $j_arg $extra_args $showcommands build-art-target-tests $common_targets" - make_command+=" libjavacrypto libjavacoretests libnetd_client linker toybox toolbox sh" + make_command+=" libjavacrypto-target libnetd_client-target linker toybox toolbox sh" make_command+=" ${out_dir}/host/linux-x86/bin/adb libstdc++ " make_command+=" ${out_dir}/target/product/${TARGET_PRODUCT}/system/etc/public.libraries.txt" + mode_suffix="-target" fi +mode_specific_libraries="libjavacoretests libjdwp libwrapagentproperties libwrapagentpropertiesd" +for LIB in ${mode_specific_libraries} ; do + make_command+=" $LIB${mode_suffix}" +done + + + echo "Executing $make_command" $make_command diff --git a/tools/checker/README b/tools/checker/README index 65f5bd25a508014593c6df6a185a8153baf1dfce..b8dd80376e88e780855850218617b7ece27ec106 100644 --- a/tools/checker/README +++ b/tools/checker/README @@ -76,3 +76,10 @@ Example: /// CHECK-START-ARM64: int MyClass.MyMethod() constant_folding (after) /// CHECK: <> IntConstant {{11|22}} /// CHECK: Return [<>] + +For convenience, several architectures can be specified as set after the +'CHECK-START' keyword. Any listed architecture will match in that case, +thereby avoiding to repeat the check lines if some, but not all architectures +match. An example line looks like: + + /// CHECK-START-{MIPS,ARM,ARM64}: int MyClass.MyMethod() constant_folding (after) diff --git a/tools/checker/checker.py b/tools/checker/checker.py index 2e9faba9fb1127637a0826b6b96a6c572567adb0..65b01a7f150590e0cfe661a43a301fa414b7dfc2 100755 --- a/tools/checker/checker.py +++ b/tools/checker/checker.py @@ -90,7 +90,8 @@ def RunTests(checkPrefix, checkPath, outputFilename, targetArch, debuggableMode) for checkFilename in FindCheckerFiles(checkPath): checkerFile = ParseCheckerStream(os.path.basename(checkFilename), checkPrefix, - open(checkFilename, "r")) + open(checkFilename, "r"), + targetArch) MatchFiles(checkerFile, c1File, targetArch, debuggableMode) diff --git a/tools/checker/file_format/checker/parser.py b/tools/checker/file_format/checker/parser.py index f199a50ebec3f6f3fe0606419315806175ba5bcb..7a5a4c8c26a0921d7b27fcb066a1f64505af8edb 100644 --- a/tools/checker/file_format/checker/parser.py +++ b/tools/checker/file_format/checker/parser.py @@ -44,7 +44,33 @@ def __extractLine(prefix, line, arch = None, debuggable = False): else: return None -def __processLine(line, lineNo, prefix, fileName): +def __preprocessLineForStart(prefix, line, targetArch): + """ This function modifies a CHECK-START-{x,y,z} into a matching + CHECK-START-y line for matching targetArch y. If no matching + architecture is found, CHECK-START-x is returned arbitrarily + to ensure all following check lines are put into a test that + is skipped. Any other line is left unmodified. + """ + if targetArch is not None: + if prefix in line: + # Find { } on the line and assume that defines the set. + s = line.find('{') + e = line.find('}') + if 0 < s and s < e: + archs = line[s+1:e].split(',') + # First verify that every archs is valid. Return the + # full line on failure to prompt error back to user. + for arch in archs: + if not arch in archs_list: + return line + # Now accept matching arch or arbitrarily return first. + if targetArch in archs: + return line[:s] + targetArch + line[e + 1:] + else: + return line[:s] + archs[0] + line[e + 1:] + return line + +def __processLine(line, lineNo, prefix, fileName, targetArch): """ This function is invoked on each line of the check file and returns a triplet which instructs the parser how the line should be handled. If the line is to be included in the current check group, it is returned in the first @@ -56,10 +82,11 @@ def __processLine(line, lineNo, prefix, fileName): return None, None, None # Lines beginning with 'CHECK-START' start a new test case. - # We currently only consider the architecture suffix in "CHECK-START" lines. + # We currently only consider the architecture suffix(es) in "CHECK-START" lines. for debuggable in [True, False]: + sline = __preprocessLineForStart(prefix + "-START", line, targetArch) for arch in [None] + archs_list: - startLine = __extractLine(prefix + "-START", line, arch, debuggable) + startLine = __extractLine(prefix + "-START", sline, arch, debuggable) if startLine is not None: return None, startLine, (arch, debuggable) @@ -164,9 +191,9 @@ def ParseCheckerAssertion(parent, line, variant, lineNo): assertion.addExpression(TestExpression.createPatternFromPlainText(text)) return assertion -def ParseCheckerStream(fileName, prefix, stream): +def ParseCheckerStream(fileName, prefix, stream, targetArch = None): checkerFile = CheckerFile(fileName) - fnProcessLine = lambda line, lineNo: __processLine(line, lineNo, prefix, fileName) + fnProcessLine = lambda line, lineNo: __processLine(line, lineNo, prefix, fileName, targetArch) fnLineOutsideChunk = lambda line, lineNo: \ Logger.fail("Checker line not inside a group", fileName, lineNo) for caseName, caseLines, startLineNo, testData in \ diff --git a/tools/cpplint.py b/tools/cpplint.py deleted file mode 100755 index 308dd8c4798ffd90b97a5907ae1b8f176f66723f..0000000000000000000000000000000000000000 --- a/tools/cpplint.py +++ /dev/null @@ -1,4095 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2009 Google Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -# Here are some issues that I've had people identify in my code during reviews, -# that I think are possible to flag automatically in a lint tool. If these were -# caught by lint, it would save time both for myself and that of my reviewers. -# Most likely, some of these are beyond the scope of the current lint framework, -# but I think it is valuable to retain these wish-list items even if they cannot -# be immediately implemented. -# -# Suggestions -# ----------- -# - Check for no 'explicit' for multi-arg ctor -# - Check for boolean assign RHS in parens -# - Check for ctor initializer-list colon position and spacing -# - Check that if there's a ctor, there should be a dtor -# - Check accessors that return non-pointer member variables are -# declared const -# - Check accessors that return non-const pointer member vars are -# *not* declared const -# - Check for using public includes for testing -# - Check for spaces between brackets in one-line inline method -# - Check for no assert() -# - Check for spaces surrounding operators -# - Check for 0 in pointer context (should be NULL) -# - Check for 0 in char context (should be '\0') -# - Check for camel-case method name conventions for methods -# that are not simple inline getters and setters -# - Do not indent namespace contents -# - Avoid inlining non-trivial constructors in header files -# - Check for old-school (void) cast for call-sites of functions -# ignored return value -# - Check gUnit usage of anonymous namespace -# - Check for class declaration order (typedefs, consts, enums, -# ctor(s?), dtor, friend declarations, methods, member vars) -# - -"""Does google-lint on c++ files. - -The goal of this script is to identify places in the code that *may* -be in non-compliance with google style. It does not attempt to fix -up these problems -- the point is to educate. It does also not -attempt to find all problems, or to ensure that everything it does -find is legitimately a problem. - -In particular, we can get very confused by /* and // inside strings! -We do a small hack, which is to ignore //'s with "'s after them on the -same line, but it is far from perfect (in either direction). -""" - -import codecs -import copy -import getopt -import math # for log -import os -import re -import sre_compile -import string -import sys -import unicodedata - - -_USAGE = """ -Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] - [--counting=total|toplevel|detailed] - [--quiet] - [file] ... - - The style guidelines this tries to follow are those in - http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml - - Every problem is given a confidence score from 1-5, with 5 meaning we are - certain of the problem, and 1 meaning it could be a legitimate construct. - This will miss some errors, and is not a substitute for a code review. - - To suppress false-positive errors of a certain category, add a - 'NOLINT(category)' comment to the line. NOLINT or NOLINT(*) - suppresses errors of all categories on that line. - - The files passed in will be linted; at least one file must be provided. - Linted extensions are .cc, .cpp, and .h. Other file types will be ignored. - - Flags: - - output=vs7 - By default, the output is formatted to ease emacs parsing. Visual Studio - compatible output (vs7) may also be used. Other formats are unsupported. - - verbose=# - Specify a number 0-5 to restrict errors to certain verbosity levels. - - quiet - Don't print anything if no errors are found. - - filter=-x,+y,... - Specify a comma-separated list of category-filters to apply: only - error messages whose category names pass the filters will be printed. - (Category names are printed with the message and look like - "[whitespace/indent]".) Filters are evaluated left to right. - "-FOO" and "FOO" means "do not print categories that start with FOO". - "+FOO" means "do print categories that start with FOO". - - Examples: --filter=-whitespace,+whitespace/braces - --filter=whitespace,runtime/printf,+runtime/printf_format - --filter=-,+build/include_what_you_use - - To see a list of all the categories used in cpplint, pass no arg: - --filter= - - counting=total|toplevel|detailed - The total number of errors found is always printed. If - 'toplevel' is provided, then the count of errors in each of - the top-level categories like 'build' and 'whitespace' will - also be printed. If 'detailed' is provided, then a count - is provided for each category like 'build/class'. - - root=subdir - The root directory used for deriving header guard CPP variable. - By default, the header guard CPP variable is calculated as the relative - path to the directory that contains .git, .hg, or .svn. When this flag - is specified, the relative path is calculated from the specified - directory. If the specified directory does not exist, this flag is - ignored. - - Examples: - Assuing that src/.git exists, the header guard CPP variables for - src/chrome/browser/ui/browser.h are: - - No flag => CHROME_BROWSER_UI_BROWSER_H_ - --root=chrome => BROWSER_UI_BROWSER_H_ - --root=chrome/browser => UI_BROWSER_H_ -""" - -# We categorize each error message we print. Here are the categories. -# We want an explicit list so we can list them all in cpplint --filter=. -# If you add a new error message with a new category, add it to the list -# here! cpplint_unittest.py should tell you if you forget to do this. -# \ used for clearer layout -- pylint: disable-msg=C6013 -_ERROR_CATEGORIES = [ - 'build/class', - 'build/deprecated', - 'build/endif_comment', - 'build/explicit_make_pair', - 'build/forward_decl', - 'build/header_guard', - 'build/include', - 'build/include_alpha', - 'build/include_order', - 'build/include_what_you_use', - 'build/namespaces', - 'build/printf_format', - 'build/storage_class', - 'legal/copyright', - 'readability/alt_tokens', - 'readability/braces', - 'readability/casting', - 'readability/check', - 'readability/constructors', - 'readability/fn_size', - 'readability/function', - 'readability/multiline_comment', - 'readability/multiline_string', - 'readability/namespace', - 'readability/nolint', - 'readability/streams', - 'readability/todo', - 'readability/utf8', - 'runtime/arrays', - 'runtime/casting', - 'runtime/explicit', - 'runtime/int', - 'runtime/init', - 'runtime/invalid_increment', - 'runtime/member_string_references', - 'runtime/memset', - 'runtime/operator', - 'runtime/printf', - 'runtime/printf_format', - 'runtime/references', - 'runtime/rtti', - 'runtime/sizeof', - 'runtime/string', - 'runtime/threadsafe_fn', - 'whitespace/blank_line', - 'whitespace/braces', - 'whitespace/comma', - 'whitespace/comments', - 'whitespace/empty_loop_body', - 'whitespace/end_of_line', - 'whitespace/ending_newline', - 'whitespace/forcolon', - 'whitespace/indent', - 'whitespace/labels', - 'whitespace/line_length', - 'whitespace/newline', - 'whitespace/operators', - 'whitespace/parens', - 'whitespace/semicolon', - 'whitespace/tab', - 'whitespace/todo' - ] - -# The default state of the category filter. This is overrided by the --filter= -# flag. By default all errors are on, so only add here categories that should be -# off by default (i.e., categories that must be enabled by the --filter= flags). -# All entries here should start with a '-' or '+', as in the --filter= flag. -_DEFAULT_FILTERS = ['-build/include_alpha'] - -# We used to check for high-bit characters, but after much discussion we -# decided those were OK, as long as they were in UTF-8 and didn't represent -# hard-coded international strings, which belong in a separate i18n file. - -# Headers that we consider STL headers. -_STL_HEADERS = frozenset([ - 'algobase.h', 'algorithm', 'alloc.h', 'bitset', 'deque', 'exception', - 'function.h', 'functional', 'hash_map', 'hash_map.h', 'hash_set', - 'hash_set.h', 'iterator', 'list', 'list.h', 'map', 'memory', 'new', - 'pair.h', 'pthread_alloc', 'queue', 'set', 'set.h', 'sstream', 'stack', - 'stl_alloc.h', 'stl_relops.h', 'type_traits.h', - 'utility', 'vector', 'vector.h', - ]) - - -# Non-STL C++ system headers. -_CPP_HEADERS = frozenset([ - 'algo.h', 'builtinbuf.h', 'bvector.h', 'cassert', 'cctype', - 'cerrno', 'cfloat', 'ciso646', 'climits', 'clocale', 'cmath', - 'complex', 'complex.h', 'csetjmp', 'csignal', 'cstdarg', 'cstddef', - 'cstdio', 'cstdlib', 'cstring', 'ctime', 'cwchar', 'cwctype', - 'defalloc.h', 'deque.h', 'editbuf.h', 'exception', 'fstream', - 'fstream.h', 'hashtable.h', 'heap.h', 'indstream.h', 'iomanip', - 'iomanip.h', 'ios', 'iosfwd', 'iostream', 'iostream.h', 'istream', - 'istream.h', 'iterator.h', 'limits', 'map.h', 'multimap.h', 'multiset.h', - 'numeric', 'ostream', 'ostream.h', 'parsestream.h', 'pfstream.h', - 'PlotFile.h', 'procbuf.h', 'pthread_alloc.h', 'rope', 'rope.h', - 'ropeimpl.h', 'SFile.h', 'slist', 'slist.h', 'stack.h', 'stdexcept', - 'stdiostream.h', 'streambuf', 'streambuf.h', 'stream.h', 'strfile.h', - 'string', 'strstream', 'strstream.h', 'tempbuf.h', 'tree.h', 'typeinfo', - 'valarray', - ]) - - -# Assertion macros. These are defined in base/logging.h and -# testing/base/gunit.h. Note that the _M versions need to come first -# for substring matching to work. -_CHECK_MACROS = [ - 'DCHECK', 'CHECK', - 'EXPECT_TRUE_M', 'EXPECT_TRUE', - 'ASSERT_TRUE_M', 'ASSERT_TRUE', - 'EXPECT_FALSE_M', 'EXPECT_FALSE', - 'ASSERT_FALSE_M', 'ASSERT_FALSE', - ] - -# Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE -_CHECK_REPLACEMENT = dict([(m, {}) for m in _CHECK_MACROS]) - -for op, replacement in [('==', 'EQ'), ('!=', 'NE'), - ('>=', 'GE'), ('>', 'GT'), - ('<=', 'LE'), ('<', 'LT')]: - _CHECK_REPLACEMENT['DCHECK'][op] = 'DCHECK_%s' % replacement - _CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement - _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement - _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement - _CHECK_REPLACEMENT['EXPECT_TRUE_M'][op] = 'EXPECT_%s_M' % replacement - _CHECK_REPLACEMENT['ASSERT_TRUE_M'][op] = 'ASSERT_%s_M' % replacement - -for op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'), - ('>=', 'LT'), ('>', 'LE'), - ('<=', 'GT'), ('<', 'GE')]: - _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement - _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement - _CHECK_REPLACEMENT['EXPECT_FALSE_M'][op] = 'EXPECT_%s_M' % inv_replacement - _CHECK_REPLACEMENT['ASSERT_FALSE_M'][op] = 'ASSERT_%s_M' % inv_replacement - -# Alternative tokens and their replacements. For full list, see section 2.5 -# Alternative tokens [lex.digraph] in the C++ standard. -# -# Digraphs (such as '%:') are not included here since it's a mess to -# match those on a word boundary. -_ALT_TOKEN_REPLACEMENT = { - 'and': '&&', - 'bitor': '|', - 'or': '||', - 'xor': '^', - 'compl': '~', - 'bitand': '&', - 'and_eq': '&=', - 'or_eq': '|=', - 'xor_eq': '^=', - 'not': '!', - 'not_eq': '!=' - } - -# Compile regular expression that matches all the above keywords. The "[ =()]" -# bit is meant to avoid matching these keywords outside of boolean expressions. -# -# False positives include C-style multi-line comments (http://go/nsiut ) -# and multi-line strings (http://go/beujw ), but those have always been -# troublesome for cpplint. -_ALT_TOKEN_REPLACEMENT_PATTERN = re.compile( - r'[ =()](' + ('|'.join(_ALT_TOKEN_REPLACEMENT.keys())) + r')(?=[ (]|$)') - - -# These constants define types of headers for use with -# _IncludeState.CheckNextIncludeOrder(). -_C_SYS_HEADER = 1 -_CPP_SYS_HEADER = 2 -_LIKELY_MY_HEADER = 3 -_POSSIBLE_MY_HEADER = 4 -_OTHER_HEADER = 5 - -# These constants define the current inline assembly state -_NO_ASM = 0 # Outside of inline assembly block -_INSIDE_ASM = 1 # Inside inline assembly block -_END_ASM = 2 # Last line of inline assembly block -_BLOCK_ASM = 3 # The whole block is an inline assembly block - -# Match start of assembly blocks -_MATCH_ASM = re.compile(r'^\s*(?:asm|_asm|__asm|__asm__)' - r'(?:\s+(volatile|__volatile__))?' - r'\s*[{(]') - - -_regexp_compile_cache = {} - -# Finds occurrences of NOLINT or NOLINT(...). -_RE_SUPPRESSION = re.compile(r'\bNOLINT\b(\([^)]*\))?') - -# {str, set(int)}: a map from error categories to sets of linenumbers -# on which those errors are expected and should be suppressed. -_error_suppressions = {} - -# The root directory used for deriving header guard CPP variable. -# This is set by --root flag. -_root = None - -def ParseNolintSuppressions(filename, raw_line, linenum, error): - """Updates the global list of error-suppressions. - - Parses any NOLINT comments on the current line, updating the global - error_suppressions store. Reports an error if the NOLINT comment - was malformed. - - Args: - filename: str, the name of the input file. - raw_line: str, the line of input text, with comments. - linenum: int, the number of the current line. - error: function, an error handler. - """ - # FIXME(adonovan): "NOLINT(" is misparsed as NOLINT(*). - matched = _RE_SUPPRESSION.search(raw_line) - if matched: - category = matched.group(1) - if category in (None, '(*)'): # => "suppress all" - _error_suppressions.setdefault(None, set()).add(linenum) - else: - if category.startswith('(') and category.endswith(')'): - category = category[1:-1] - if category in _ERROR_CATEGORIES: - _error_suppressions.setdefault(category, set()).add(linenum) - else: - error(filename, linenum, 'readability/nolint', 5, - 'Unknown NOLINT error category: %s' % category) - - -def ResetNolintSuppressions(): - "Resets the set of NOLINT suppressions to empty." - _error_suppressions.clear() - - -def IsErrorSuppressedByNolint(category, linenum): - """Returns true if the specified error category is suppressed on this line. - - Consults the global error_suppressions map populated by - ParseNolintSuppressions/ResetNolintSuppressions. - - Args: - category: str, the category of the error. - linenum: int, the current line number. - Returns: - bool, True iff the error should be suppressed due to a NOLINT comment. - """ - return (linenum in _error_suppressions.get(category, set()) or - linenum in _error_suppressions.get(None, set())) - -def Match(pattern, s): - """Matches the string with the pattern, caching the compiled regexp.""" - # The regexp compilation caching is inlined in both Match and Search for - # performance reasons; factoring it out into a separate function turns out - # to be noticeably expensive. - if not pattern in _regexp_compile_cache: - _regexp_compile_cache[pattern] = sre_compile.compile(pattern) - return _regexp_compile_cache[pattern].match(s) - - -def Search(pattern, s): - """Searches the string for the pattern, caching the compiled regexp.""" - if not pattern in _regexp_compile_cache: - _regexp_compile_cache[pattern] = sre_compile.compile(pattern) - return _regexp_compile_cache[pattern].search(s) - - -class _IncludeState(dict): - """Tracks line numbers for includes, and the order in which includes appear. - - As a dict, an _IncludeState object serves as a mapping between include - filename and line number on which that file was included. - - Call CheckNextIncludeOrder() once for each header in the file, passing - in the type constants defined above. Calls in an illegal order will - raise an _IncludeError with an appropriate error message. - - """ - # self._section will move monotonically through this set. If it ever - # needs to move backwards, CheckNextIncludeOrder will raise an error. - _INITIAL_SECTION = 0 - _MY_H_SECTION = 1 - _C_SECTION = 2 - _CPP_SECTION = 3 - _OTHER_H_SECTION = 4 - - _TYPE_NAMES = { - _C_SYS_HEADER: 'C system header', - _CPP_SYS_HEADER: 'C++ system header', - _LIKELY_MY_HEADER: 'header this file implements', - _POSSIBLE_MY_HEADER: 'header this file may implement', - _OTHER_HEADER: 'other header', - } - _SECTION_NAMES = { - _INITIAL_SECTION: "... nothing. (This can't be an error.)", - _MY_H_SECTION: 'a header this file implements', - _C_SECTION: 'C system header', - _CPP_SECTION: 'C++ system header', - _OTHER_H_SECTION: 'other header', - } - - def __init__(self): - dict.__init__(self) - # The name of the current section. - self._section = self._INITIAL_SECTION - # The path of last found header. - self._last_header = '' - - def CanonicalizeAlphabeticalOrder(self, header_path): - """Returns a path canonicalized for alphabetical comparison. - - - replaces "-" with "_" so they both cmp the same. - - removes '-inl' since we don't require them to be after the main header. - - lowercase everything, just in case. - - Args: - header_path: Path to be canonicalized. - - Returns: - Canonicalized path. - """ - return header_path.replace('-inl.h', '.h').replace('-', '_').lower() - - def IsInAlphabeticalOrder(self, header_path): - """Check if a header is in alphabetical order with the previous header. - - Args: - header_path: Header to be checked. - - Returns: - Returns true if the header is in alphabetical order. - """ - canonical_header = self.CanonicalizeAlphabeticalOrder(header_path) - if self._last_header > canonical_header: - return False - self._last_header = canonical_header - return True - - def CheckNextIncludeOrder(self, header_type): - """Returns a non-empty error message if the next header is out of order. - - This function also updates the internal state to be ready to check - the next include. - - Args: - header_type: One of the _XXX_HEADER constants defined above. - - Returns: - The empty string if the header is in the right order, or an - error message describing what's wrong. - - """ - error_message = ('Found %s after %s' % - (self._TYPE_NAMES[header_type], - self._SECTION_NAMES[self._section])) - - last_section = self._section - - if header_type == _C_SYS_HEADER: - if self._section <= self._C_SECTION: - self._section = self._C_SECTION - else: - self._last_header = '' - return error_message - elif header_type == _CPP_SYS_HEADER: - if self._section <= self._CPP_SECTION: - self._section = self._CPP_SECTION - else: - self._last_header = '' - return error_message - elif header_type == _LIKELY_MY_HEADER: - if self._section <= self._MY_H_SECTION: - self._section = self._MY_H_SECTION - else: - self._section = self._OTHER_H_SECTION - elif header_type == _POSSIBLE_MY_HEADER: - if self._section <= self._MY_H_SECTION: - self._section = self._MY_H_SECTION - else: - # This will always be the fallback because we're not sure - # enough that the header is associated with this file. - self._section = self._OTHER_H_SECTION - else: - assert header_type == _OTHER_HEADER - self._section = self._OTHER_H_SECTION - - if last_section != self._section: - self._last_header = '' - - return '' - - -class _CppLintState(object): - """Maintains module-wide state..""" - - def __init__(self): - self.verbose_level = 1 # global setting. - self.error_count = 0 # global count of reported errors - # filters to apply when emitting error messages - self.filters = _DEFAULT_FILTERS[:] - self.counting = 'total' # In what way are we counting errors? - self.errors_by_category = {} # string to int dict storing error counts - # BEGIN android-added - self.quiet = False # global setting. - # END android-added - - # output format: - # "emacs" - format that emacs can parse (default) - # "vs7" - format that Microsoft Visual Studio 7 can parse - self.output_format = 'emacs' - - def SetOutputFormat(self, output_format): - """Sets the output format for errors.""" - self.output_format = output_format - - # BEGIN android-added - def SetQuiet(self, level): - """Sets the module's quiet setting, and returns the previous setting.""" - last_quiet = self.quiet - self.quiet = level - return last_quiet - # END android-added - - def SetVerboseLevel(self, level): - """Sets the module's verbosity, and returns the previous setting.""" - last_verbose_level = self.verbose_level - self.verbose_level = level - return last_verbose_level - - def SetCountingStyle(self, counting_style): - """Sets the module's counting options.""" - self.counting = counting_style - - def SetFilters(self, filters): - """Sets the error-message filters. - - These filters are applied when deciding whether to emit a given - error message. - - Args: - filters: A string of comma-separated filters (eg "+whitespace/indent"). - Each filter should start with + or -; else we die. - - Raises: - ValueError: The comma-separated filters did not all start with '+' or '-'. - E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter" - """ - # Default filters always have less priority than the flag ones. - self.filters = _DEFAULT_FILTERS[:] - for filt in filters.split(','): - clean_filt = filt.strip() - if clean_filt: - self.filters.append(clean_filt) - for filt in self.filters: - if not (filt.startswith('+') or filt.startswith('-')): - raise ValueError('Every filter in --filters must start with + or -' - ' (%s does not)' % filt) - - def ResetErrorCounts(self): - """Sets the module's error statistic back to zero.""" - self.error_count = 0 - self.errors_by_category = {} - - def IncrementErrorCount(self, category): - """Bumps the module's error statistic.""" - self.error_count += 1 - if self.counting in ('toplevel', 'detailed'): - if self.counting != 'detailed': - category = category.split('/')[0] - if category not in self.errors_by_category: - self.errors_by_category[category] = 0 - self.errors_by_category[category] += 1 - - def PrintErrorCounts(self): - """Print a summary of errors by category, and the total.""" - for category, count in self.errors_by_category.iteritems(): - sys.stderr.write('Category \'%s\' errors found: %d\n' % - (category, count)) - sys.stderr.write('Total errors found: %d\n' % self.error_count) - -_cpplint_state = _CppLintState() - - -def _OutputFormat(): - """Gets the module's output format.""" - return _cpplint_state.output_format - - -def _SetOutputFormat(output_format): - """Sets the module's output format.""" - _cpplint_state.SetOutputFormat(output_format) - - -# BEGIN android-added -def _Quiet(): - """Returns the module's quiet setting.""" - return _cpplint_state.quiet - - -def _SetQuiet(level): - """Sets the module's quiet status, and returns the previous setting.""" - return _cpplint_state.SetQuiet(level) -# END android-added - -def _VerboseLevel(): - """Returns the module's verbosity setting.""" - return _cpplint_state.verbose_level - - -def _SetVerboseLevel(level): - """Sets the module's verbosity, and returns the previous setting.""" - return _cpplint_state.SetVerboseLevel(level) - - -def _SetCountingStyle(level): - """Sets the module's counting options.""" - _cpplint_state.SetCountingStyle(level) - - -def _Filters(): - """Returns the module's list of output filters, as a list.""" - return _cpplint_state.filters - - -def _SetFilters(filters): - """Sets the module's error-message filters. - - These filters are applied when deciding whether to emit a given - error message. - - Args: - filters: A string of comma-separated filters (eg "whitespace/indent"). - Each filter should start with + or -; else we die. - """ - _cpplint_state.SetFilters(filters) - - -class _FunctionState(object): - """Tracks current function name and the number of lines in its body.""" - - _NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc. - _TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER. - - def __init__(self): - self.in_a_function = False - self.lines_in_function = 0 - self.current_function = '' - - def Begin(self, function_name): - """Start analyzing function body. - - Args: - function_name: The name of the function being tracked. - """ - self.in_a_function = True - self.lines_in_function = 0 - self.current_function = function_name - - def Count(self): - """Count line in current function body.""" - if self.in_a_function: - self.lines_in_function += 1 - - def Check(self, error, filename, linenum): - """Report if too many lines in function body. - - Args: - error: The function to call with any errors found. - filename: The name of the current file. - linenum: The number of the line to check. - """ - # BEGIN android-added - if not self.in_a_function: - return - # END android-added - if Match(r'T(EST|est)', self.current_function): - base_trigger = self._TEST_TRIGGER - else: - base_trigger = self._NORMAL_TRIGGER - trigger = base_trigger * 2**_VerboseLevel() - - if self.lines_in_function > trigger: - error_level = int(math.log(self.lines_in_function / base_trigger, 2)) - # 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ... - if error_level > 5: - error_level = 5 - error(filename, linenum, 'readability/fn_size', error_level, - 'Small and focused functions are preferred:' - ' %s has %d non-comment lines' - ' (error triggered by exceeding %d lines).' % ( - self.current_function, self.lines_in_function, trigger)) - - def End(self): - """Stop analyzing function body.""" - self.in_a_function = False - - -class _IncludeError(Exception): - """Indicates a problem with the include order in a file.""" - pass - - -class FileInfo: - """Provides utility functions for filenames. - - FileInfo provides easy access to the components of a file's path - relative to the project root. - """ - - def __init__(self, filename): - self._filename = filename - - def FullName(self): - """Make Windows paths like Unix.""" - return os.path.abspath(self._filename).replace('\\', '/') - - def RepositoryName(self): - """FullName after removing the local path to the repository. - - If we have a real absolute path name here we can try to do something smart: - detecting the root of the checkout and truncating /path/to/checkout from - the name so that we get header guards that don't include things like - "C:\Documents and Settings\..." or "/home/username/..." in them and thus - people on different computers who have checked the source out to different - locations won't see bogus errors. - """ - fullname = self.FullName() - - if os.path.exists(fullname): - project_dir = os.path.dirname(fullname) - - if os.path.exists(os.path.join(project_dir, ".svn")): - # If there's a .svn file in the current directory, we recursively look - # up the directory tree for the top of the SVN checkout - root_dir = project_dir - one_up_dir = os.path.dirname(root_dir) - while os.path.exists(os.path.join(one_up_dir, ".svn")): - root_dir = os.path.dirname(root_dir) - one_up_dir = os.path.dirname(one_up_dir) - - prefix = os.path.commonprefix([root_dir, project_dir]) - return fullname[len(prefix) + 1:] - - # Not SVN <= 1.6? Try to find a git, hg, or svn top level directory by - # searching up from the current path. - root_dir = os.path.dirname(fullname) - while (root_dir != os.path.dirname(root_dir) and - not os.path.exists(os.path.join(root_dir, ".git")) and - not os.path.exists(os.path.join(root_dir, ".hg")) and - not os.path.exists(os.path.join(root_dir, ".svn"))): - root_dir = os.path.dirname(root_dir) - - if (os.path.exists(os.path.join(root_dir, ".git")) or - os.path.exists(os.path.join(root_dir, ".hg")) or - os.path.exists(os.path.join(root_dir, ".svn"))): - prefix = os.path.commonprefix([root_dir, project_dir]) - # BEGIN android-changed - # return fullname[len(prefix) + 1:] - return "art/" + fullname[len(prefix) + 1:] - # END android-changed - - # Don't know what to do; header guard warnings may be wrong... - return fullname - - def Split(self): - """Splits the file into the directory, basename, and extension. - - For 'chrome/browser/browser.cc', Split() would - return ('chrome/browser', 'browser', '.cc') - - Returns: - A tuple of (directory, basename, extension). - """ - - googlename = self.RepositoryName() - project, rest = os.path.split(googlename) - return (project,) + os.path.splitext(rest) - - def BaseName(self): - """File base name - text after the final slash, before the final period.""" - return self.Split()[1] - - def Extension(self): - """File extension - text following the final period.""" - return self.Split()[2] - - def NoExtension(self): - """File has no source file extension.""" - return '/'.join(self.Split()[0:2]) - - def IsSource(self): - """File has a source file extension.""" - return self.Extension()[1:] in ('c', 'cc', 'cpp', 'cxx') - - -def _ShouldPrintError(category, confidence, linenum): - """If confidence >= verbose, category passes filter and is not suppressed.""" - - # There are three ways we might decide not to print an error message: - # a "NOLINT(category)" comment appears in the source, - # the verbosity level isn't high enough, or the filters filter it out. - if IsErrorSuppressedByNolint(category, linenum): - return False - if confidence < _cpplint_state.verbose_level: - return False - - is_filtered = False - for one_filter in _Filters(): - if one_filter.startswith('-'): - if category.startswith(one_filter[1:]): - is_filtered = True - elif one_filter.startswith('+'): - if category.startswith(one_filter[1:]): - is_filtered = False - else: - assert False # should have been checked for in SetFilter. - if is_filtered: - return False - - return True - - -def Error(filename, linenum, category, confidence, message): - """Logs the fact we've found a lint error. - - We log where the error was found, and also our confidence in the error, - that is, how certain we are this is a legitimate style regression, and - not a misidentification or a use that's sometimes justified. - - False positives can be suppressed by the use of - "cpplint(category)" comments on the offending line. These are - parsed into _error_suppressions. - - Args: - filename: The name of the file containing the error. - linenum: The number of the line containing the error. - category: A string used to describe the "category" this bug - falls under: "whitespace", say, or "runtime". Categories - may have a hierarchy separated by slashes: "whitespace/indent". - confidence: A number from 1-5 representing a confidence score for - the error, with 5 meaning that we are certain of the problem, - and 1 meaning that it could be a legitimate construct. - message: The error message. - """ - if _ShouldPrintError(category, confidence, linenum): - _cpplint_state.IncrementErrorCount(category) - if _cpplint_state.output_format == 'vs7': - sys.stderr.write('%s(%s): %s [%s] [%d]\n' % ( - filename, linenum, message, category, confidence)) - elif _cpplint_state.output_format == 'eclipse': - sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % ( - filename, linenum, message, category, confidence)) - else: - sys.stderr.write('%s:%s: %s [%s] [%d]\n' % ( - filename, linenum, message, category, confidence)) - - -# Matches standard C++ escape esequences per 2.13.2.3 of the C++ standard. -_RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile( - r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)') -# Matches strings. Escape codes should already be removed by ESCAPES. -_RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES = re.compile(r'"[^"]*"') -# Matches characters. Escape codes should already be removed by ESCAPES. -_RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES = re.compile(r"'.'") -# Matches multi-line C++ comments. -# This RE is a little bit more complicated than one might expect, because we -# have to take care of space removals tools so we can handle comments inside -# statements better. -# The current rule is: We only clear spaces from both sides when we're at the -# end of the line. Otherwise, we try to remove spaces from the right side, -# if this doesn't work we try on left side but only if there's a non-character -# on the right. -_RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile( - r"""(\s*/\*.*\*/\s*$| - /\*.*\*/\s+| - \s+/\*.*\*/(?=\W)| - /\*.*\*/)""", re.VERBOSE) - - -def IsCppString(line): - """Does line terminate so, that the next symbol is in string constant. - - This function does not consider single-line nor multi-line comments. - - Args: - line: is a partial line of code starting from the 0..n. - - Returns: - True, if next character appended to 'line' is inside a - string constant. - """ - - line = line.replace(r'\\', 'XX') # after this, \\" does not match to \" - return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1 - - -def FindNextMultiLineCommentStart(lines, lineix): - """Find the beginning marker for a multiline comment.""" - while lineix < len(lines): - if lines[lineix].strip().startswith('/*'): - # Only return this marker if the comment goes beyond this line - if lines[lineix].strip().find('*/', 2) < 0: - return lineix - lineix += 1 - return len(lines) - - -def FindNextMultiLineCommentEnd(lines, lineix): - """We are inside a comment, find the end marker.""" - while lineix < len(lines): - if lines[lineix].strip().endswith('*/'): - return lineix - lineix += 1 - return len(lines) - - -def RemoveMultiLineCommentsFromRange(lines, begin, end): - """Clears a range of lines for multi-line comments.""" - # Having // dummy comments makes the lines non-empty, so we will not get - # unnecessary blank line warnings later in the code. - for i in range(begin, end): - lines[i] = '// dummy' - - -def RemoveMultiLineComments(filename, lines, error): - """Removes multiline (c-style) comments from lines.""" - lineix = 0 - while lineix < len(lines): - lineix_begin = FindNextMultiLineCommentStart(lines, lineix) - if lineix_begin >= len(lines): - return - lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin) - if lineix_end >= len(lines): - error(filename, lineix_begin + 1, 'readability/multiline_comment', 5, - 'Could not find end of multi-line comment') - return - RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1) - lineix = lineix_end + 1 - - -def CleanseComments(line): - """Removes //-comments and single-line C-style /* */ comments. - - Args: - line: A line of C++ source. - - Returns: - The line with single-line comments removed. - """ - commentpos = line.find('//') - if commentpos != -1 and not IsCppString(line[:commentpos]): - line = line[:commentpos].rstrip() - # get rid of /* ... */ - return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line) - - -class CleansedLines(object): - """Holds 3 copies of all lines with different preprocessing applied to them. - - 1) elided member contains lines without strings and comments, - 2) lines member contains lines without comments, and - 3) raw_lines member contains all the lines without processing. - All these three members are of , and of the same length. - """ - - def __init__(self, lines): - self.elided = [] - self.lines = [] - self.raw_lines = lines - self.num_lines = len(lines) - for linenum in range(len(lines)): - self.lines.append(CleanseComments(lines[linenum])) - elided = self._CollapseStrings(lines[linenum]) - self.elided.append(CleanseComments(elided)) - - def NumLines(self): - """Returns the number of lines represented.""" - return self.num_lines - - @staticmethod - def _CollapseStrings(elided): - """Collapses strings and chars on a line to simple "" or '' blocks. - - We nix strings first so we're not fooled by text like '"http://"' - - Args: - elided: The line being processed. - - Returns: - The line with collapsed strings. - """ - if not _RE_PATTERN_INCLUDE.match(elided): - # Remove escaped characters first to make quote/single quote collapsing - # basic. Things that look like escaped characters shouldn't occur - # outside of strings and chars. - elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided) - elided = _RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES.sub("''", elided) - elided = _RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES.sub('""', elided) - return elided - - -def FindEndOfExpressionInLine(line, startpos, depth, startchar, endchar): - """Find the position just after the matching endchar. - - Args: - line: a CleansedLines line. - startpos: start searching at this position. - depth: nesting level at startpos. - startchar: expression opening character. - endchar: expression closing character. - - Returns: - Index just after endchar. - """ - for i in xrange(startpos, len(line)): - if line[i] == startchar: - depth += 1 - elif line[i] == endchar: - depth -= 1 - if depth == 0: - return i + 1 - return -1 - - -def CloseExpression(clean_lines, linenum, pos): - """If input points to ( or { or [, finds the position that closes it. - - If lines[linenum][pos] points to a '(' or '{' or '[', finds the - linenum/pos that correspond to the closing of the expression. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - pos: A position on the line. - - Returns: - A tuple (line, linenum, pos) pointer *past* the closing brace, or - (line, len(lines), -1) if we never find a close. Note we ignore - strings and comments when matching; and the line we return is the - 'cleansed' line at linenum. - """ - - line = clean_lines.elided[linenum] - startchar = line[pos] - if startchar not in '({[': - return (line, clean_lines.NumLines(), -1) - if startchar == '(': endchar = ')' - if startchar == '[': endchar = ']' - if startchar == '{': endchar = '}' - - # Check first line - end_pos = FindEndOfExpressionInLine(line, pos, 0, startchar, endchar) - if end_pos > -1: - return (line, linenum, end_pos) - tail = line[pos:] - num_open = tail.count(startchar) - tail.count(endchar) - while linenum < clean_lines.NumLines() - 1: - linenum += 1 - line = clean_lines.elided[linenum] - delta = line.count(startchar) - line.count(endchar) - if num_open + delta <= 0: - return (line, linenum, - FindEndOfExpressionInLine(line, 0, num_open, startchar, endchar)) - num_open += delta - - # Did not find endchar before end of file, give up - return (line, clean_lines.NumLines(), -1) - -def CheckForCopyright(filename, lines, error): - """Logs an error if no Copyright message appears at the top of the file.""" - - # We'll say it should occur by line 10. Don't forget there's a - # dummy line at the front. - for line in xrange(1, min(len(lines), 11)): - if re.search(r'Copyright', lines[line], re.I): break - else: # means no copyright line was found - error(filename, 0, 'legal/copyright', 5, - 'No copyright message found. ' - 'You should have a line: "Copyright [year] "') - - -def GetHeaderGuardCPPVariable(filename): - """Returns the CPP variable that should be used as a header guard. - - Args: - filename: The name of a C++ header file. - - Returns: - The CPP variable that should be used as a header guard in the - named file. - - """ - - # Restores original filename in case that cpplint is invoked from Emacs's - # flymake. - filename = re.sub(r'_flymake\.h$', '.h', filename) - filename = re.sub(r'/\.flymake/([^/]*)$', r'/\1', filename) - - fileinfo = FileInfo(filename) - file_path_from_root = fileinfo.RepositoryName() - if _root: - file_path_from_root = re.sub('^' + _root + os.sep, '', file_path_from_root) - return re.sub(r'[-./\s]', '_', file_path_from_root).upper() + '_' - - -def CheckForHeaderGuard(filename, lines, error): - """Checks that the file contains a header guard. - - Logs an error if no #ifndef header guard is present. For other - headers, checks that the full pathname is used. - - Args: - filename: The name of the C++ header file. - lines: An array of strings, each representing a line of the file. - error: The function to call with any errors found. - """ - - cppvar = GetHeaderGuardCPPVariable(filename) - - ifndef = None - ifndef_linenum = 0 - define = None - endif = None - endif_linenum = 0 - for linenum, line in enumerate(lines): - linesplit = line.split() - if len(linesplit) >= 2: - # find the first occurrence of #ifndef and #define, save arg - if not ifndef and linesplit[0] == '#ifndef': - # set ifndef to the header guard presented on the #ifndef line. - ifndef = linesplit[1] - ifndef_linenum = linenum - if not define and linesplit[0] == '#define': - define = linesplit[1] - # find the last occurrence of #endif, save entire line - if line.startswith('#endif'): - endif = line - endif_linenum = linenum - - if not ifndef: - error(filename, 0, 'build/header_guard', 5, - 'No #ifndef header guard found, suggested CPP variable is: %s' % - cppvar) - return - - if not define: - error(filename, 0, 'build/header_guard', 5, - 'No #define header guard found, suggested CPP variable is: %s' % - cppvar) - return - - # The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__ - # for backward compatibility. - if ifndef != cppvar: - error_level = 0 - if ifndef != cppvar + '_': - error_level = 5 - - ParseNolintSuppressions(filename, lines[ifndef_linenum], ifndef_linenum, - error) - error(filename, ifndef_linenum, 'build/header_guard', error_level, - '#ifndef header guard has wrong style, please use: %s' % cppvar) - - if define != ifndef: - error(filename, 0, 'build/header_guard', 5, - '#ifndef and #define don\'t match, suggested CPP variable is: %s' % - cppvar) - return - - if endif != ('#endif // %s' % cppvar): - error_level = 0 - if endif != ('#endif // %s' % (cppvar + '_')): - error_level = 5 - - ParseNolintSuppressions(filename, lines[endif_linenum], endif_linenum, - error) - error(filename, endif_linenum, 'build/header_guard', error_level, - '#endif line should be "#endif // %s"' % cppvar) - - -def CheckForUnicodeReplacementCharacters(filename, lines, error): - """Logs an error for each line containing Unicode replacement characters. - - These indicate that either the file contained invalid UTF-8 (likely) - or Unicode replacement characters (which it shouldn't). Note that - it's possible for this to throw off line numbering if the invalid - UTF-8 occurred adjacent to a newline. - - Args: - filename: The name of the current file. - lines: An array of strings, each representing a line of the file. - error: The function to call with any errors found. - """ - for linenum, line in enumerate(lines): - if u'\ufffd' in line: - error(filename, linenum, 'readability/utf8', 5, - 'Line contains invalid UTF-8 (or Unicode replacement character).') - - -def CheckForNewlineAtEOF(filename, lines, error): - """Logs an error if there is no newline char at the end of the file. - - Args: - filename: The name of the current file. - lines: An array of strings, each representing a line of the file. - error: The function to call with any errors found. - """ - - # The array lines() was created by adding two newlines to the - # original file (go figure), then splitting on \n. - # To verify that the file ends in \n, we just have to make sure the - # last-but-two element of lines() exists and is empty. - if len(lines) < 3 or lines[-2]: - error(filename, len(lines) - 2, 'whitespace/ending_newline', 5, - 'Could not find a newline character at the end of the file.') - - -def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error): - """Logs an error if we see /* ... */ or "..." that extend past one line. - - /* ... */ comments are legit inside macros, for one line. - Otherwise, we prefer // comments, so it's ok to warn about the - other. Likewise, it's ok for strings to extend across multiple - lines, as long as a line continuation character (backslash) - terminates each line. Although not currently prohibited by the C++ - style guide, it's ugly and unnecessary. We don't do well with either - in this lint program, so we warn about both. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Remove all \\ (escaped backslashes) from the line. They are OK, and the - # second (escaped) slash may trigger later \" detection erroneously. - line = line.replace('\\\\', '') - - if line.count('/*') > line.count('*/'): - error(filename, linenum, 'readability/multiline_comment', 5, - 'Complex multi-line /*...*/-style comment found. ' - 'Lint may give bogus warnings. ' - 'Consider replacing these with //-style comments, ' - 'with #if 0...#endif, ' - 'or with more clearly structured multi-line comments.') - - if (line.count('"') - line.count('\\"')) % 2: - error(filename, linenum, 'readability/multiline_string', 5, - 'Multi-line string ("...") found. This lint script doesn\'t ' - 'do well with such strings, and may give bogus warnings. They\'re ' - 'ugly and unnecessary, and you should use concatenation instead".') - - -threading_list = ( - ('asctime(', 'asctime_r('), - ('ctime(', 'ctime_r('), - ('getgrgid(', 'getgrgid_r('), - ('getgrnam(', 'getgrnam_r('), - ('getlogin(', 'getlogin_r('), - ('getpwnam(', 'getpwnam_r('), - ('getpwuid(', 'getpwuid_r('), - ('gmtime(', 'gmtime_r('), - ('localtime(', 'localtime_r('), - ('rand(', 'rand_r('), - ('readdir(', 'readdir_r('), - ('strtok(', 'strtok_r('), - ('ttyname(', 'ttyname_r('), - ) - - -def CheckPosixThreading(filename, clean_lines, linenum, error): - """Checks for calls to thread-unsafe functions. - - Much code has been originally written without consideration of - multi-threading. Also, engineers are relying on their old experience; - they have learned posix before threading extensions were added. These - tests guide the engineers to use thread-safe functions (when using - posix directly). - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - for single_thread_function, multithread_safe_function in threading_list: - ix = line.find(single_thread_function) - # Comparisons made explicit for clarity -- pylint: disable-msg=C6403 - if ix >= 0 and (ix == 0 or (not line[ix - 1].isalnum() and - line[ix - 1] not in ('_', '.', '>'))): - error(filename, linenum, 'runtime/threadsafe_fn', 2, - 'Consider using ' + multithread_safe_function + - '...) instead of ' + single_thread_function + - '...) for improved thread safety.') - - -# Matches invalid increment: *count++, which moves pointer instead of -# incrementing a value. -_RE_PATTERN_INVALID_INCREMENT = re.compile( - r'^\s*\*\w+(\+\+|--);') - - -def CheckInvalidIncrement(filename, clean_lines, linenum, error): - """Checks for invalid increment *count++. - - For example following function: - void increment_counter(int* count) { - *count++; - } - is invalid, because it effectively does count++, moving pointer, and should - be replaced with ++*count, (*count)++ or *count += 1. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - if _RE_PATTERN_INVALID_INCREMENT.match(line): - error(filename, linenum, 'runtime/invalid_increment', 5, - 'Changing pointer instead of value (or unused value of operator*).') - - -class _BlockInfo(object): - """Stores information about a generic block of code.""" - - def __init__(self, seen_open_brace): - self.seen_open_brace = seen_open_brace - self.open_parentheses = 0 - self.inline_asm = _NO_ASM - - def CheckBegin(self, filename, clean_lines, linenum, error): - """Run checks that applies to text up to the opening brace. - - This is mostly for checking the text after the class identifier - and the "{", usually where the base class is specified. For other - blocks, there isn't much to check, so we always pass. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - pass - - def CheckEnd(self, filename, clean_lines, linenum, error): - """Run checks that applies to text after the closing brace. - - This is mostly used for checking end of namespace comments. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - pass - - -class _ClassInfo(_BlockInfo): - """Stores information about a class.""" - - def __init__(self, name, class_or_struct, clean_lines, linenum): - _BlockInfo.__init__(self, False) - self.name = name - self.starting_linenum = linenum - self.is_derived = False - if class_or_struct == 'struct': - self.access = 'public' - else: - self.access = 'private' - - # Try to find the end of the class. This will be confused by things like: - # class A { - # } *x = { ... - # - # But it's still good enough for CheckSectionSpacing. - self.last_line = 0 - depth = 0 - for i in range(linenum, clean_lines.NumLines()): - line = clean_lines.elided[i] - depth += line.count('{') - line.count('}') - if not depth: - self.last_line = i - break - - def CheckBegin(self, filename, clean_lines, linenum, error): - # Look for a bare ':' - if Search('(^|[^:]):($|[^:])', clean_lines.elided[linenum]): - self.is_derived = True - - -class _NamespaceInfo(_BlockInfo): - """Stores information about a namespace.""" - - def __init__(self, name, linenum): - _BlockInfo.__init__(self, False) - self.name = name or '' - self.starting_linenum = linenum - - def CheckEnd(self, filename, clean_lines, linenum, error): - """Check end of namespace comments.""" - line = clean_lines.raw_lines[linenum] - - # Check how many lines is enclosed in this namespace. Don't issue - # warning for missing namespace comments if there aren't enough - # lines. However, do apply checks if there is already an end of - # namespace comment and it's incorrect. - # - # TODO(unknown): We always want to check end of namespace comments - # if a namespace is large, but sometimes we also want to apply the - # check if a short namespace contained nontrivial things (something - # other than forward declarations). There is currently no logic on - # deciding what these nontrivial things are, so this check is - # triggered by namespace size only, which works most of the time. - if (linenum - self.starting_linenum < 10 - and not Match(r'};*\s*(//|/\*).*\bnamespace\b', line)): - return - - # Look for matching comment at end of namespace. - # - # Note that we accept C style "/* */" comments for terminating - # namespaces, so that code that terminate namespaces inside - # preprocessor macros can be cpplint clean. Example: http://go/nxpiz - # - # We also accept stuff like "// end of namespace ." with the - # period at the end. - # - # Besides these, we don't accept anything else, otherwise we might - # get false negatives when existing comment is a substring of the - # expected namespace. Example: http://go/ldkdc, http://cl/23548205 - if self.name: - # Named namespace - if not Match((r'};*\s*(//|/\*).*\bnamespace\s+' + re.escape(self.name) + - r'[\*/\.\\\s]*$'), - line): - error(filename, linenum, 'readability/namespace', 5, - 'Namespace should be terminated with "// namespace %s"' % - self.name) - else: - # Anonymous namespace - if not Match(r'};*\s*(//|/\*).*\bnamespace[\*/\.\\\s]*$', line): - error(filename, linenum, 'readability/namespace', 5, - 'Namespace should be terminated with "// namespace"') - - -class _PreprocessorInfo(object): - """Stores checkpoints of nesting stacks when #if/#else is seen.""" - - def __init__(self, stack_before_if): - # The entire nesting stack before #if - self.stack_before_if = stack_before_if - - # The entire nesting stack up to #else - self.stack_before_else = [] - - # Whether we have already seen #else or #elif - self.seen_else = False - - -class _NestingState(object): - """Holds states related to parsing braces.""" - - def __init__(self): - # Stack for tracking all braces. An object is pushed whenever we - # see a "{", and popped when we see a "}". Only 3 types of - # objects are possible: - # - _ClassInfo: a class or struct. - # - _NamespaceInfo: a namespace. - # - _BlockInfo: some other type of block. - self.stack = [] - - # Stack of _PreprocessorInfo objects. - self.pp_stack = [] - - def SeenOpenBrace(self): - """Check if we have seen the opening brace for the innermost block. - - Returns: - True if we have seen the opening brace, False if the innermost - block is still expecting an opening brace. - """ - return (not self.stack) or self.stack[-1].seen_open_brace - - def InNamespaceBody(self): - """Check if we are currently one level inside a namespace body. - - Returns: - True if top of the stack is a namespace block, False otherwise. - """ - return self.stack and isinstance(self.stack[-1], _NamespaceInfo) - - def UpdatePreprocessor(self, line): - """Update preprocessor stack. - - We need to handle preprocessors due to classes like this: - #ifdef SWIG - struct ResultDetailsPageElementExtensionPoint { - #else - struct ResultDetailsPageElementExtensionPoint : public Extension { - #endif - (see http://go/qwddn for original example) - - We make the following assumptions (good enough for most files): - - Preprocessor condition evaluates to true from #if up to first - #else/#elif/#endif. - - - Preprocessor condition evaluates to false from #else/#elif up - to #endif. We still perform lint checks on these lines, but - these do not affect nesting stack. - - Args: - line: current line to check. - """ - if Match(r'^\s*#\s*(if|ifdef|ifndef)\b', line): - # Beginning of #if block, save the nesting stack here. The saved - # stack will allow us to restore the parsing state in the #else case. - self.pp_stack.append(_PreprocessorInfo(copy.deepcopy(self.stack))) - elif Match(r'^\s*#\s*(else|elif)\b', line): - # Beginning of #else block - if self.pp_stack: - if not self.pp_stack[-1].seen_else: - # This is the first #else or #elif block. Remember the - # whole nesting stack up to this point. This is what we - # keep after the #endif. - self.pp_stack[-1].seen_else = True - self.pp_stack[-1].stack_before_else = copy.deepcopy(self.stack) - - # Restore the stack to how it was before the #if - self.stack = copy.deepcopy(self.pp_stack[-1].stack_before_if) - else: - # TODO(unknown): unexpected #else, issue warning? - pass - elif Match(r'^\s*#\s*endif\b', line): - # End of #if or #else blocks. - if self.pp_stack: - # If we saw an #else, we will need to restore the nesting - # stack to its former state before the #else, otherwise we - # will just continue from where we left off. - if self.pp_stack[-1].seen_else: - # Here we can just use a shallow copy since we are the last - # reference to it. - self.stack = self.pp_stack[-1].stack_before_else - # Drop the corresponding #if - self.pp_stack.pop() - else: - # TODO(unknown): unexpected #endif, issue warning? - pass - - def Update(self, filename, clean_lines, linenum, error): - """Update nesting state with current line. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Update pp_stack first - self.UpdatePreprocessor(line) - - # Count parentheses. This is to avoid adding struct arguments to - # the nesting stack. - if self.stack: - inner_block = self.stack[-1] - depth_change = line.count('(') - line.count(')') - inner_block.open_parentheses += depth_change - - # Also check if we are starting or ending an inline assembly block. - if inner_block.inline_asm in (_NO_ASM, _END_ASM): - if (depth_change != 0 and - inner_block.open_parentheses == 1 and - _MATCH_ASM.match(line)): - # Enter assembly block - inner_block.inline_asm = _INSIDE_ASM - else: - # Not entering assembly block. If previous line was _END_ASM, - # we will now shift to _NO_ASM state. - inner_block.inline_asm = _NO_ASM - elif (inner_block.inline_asm == _INSIDE_ASM and - inner_block.open_parentheses == 0): - # Exit assembly block - inner_block.inline_asm = _END_ASM - - # Consume namespace declaration at the beginning of the line. Do - # this in a loop so that we catch same line declarations like this: - # namespace proto2 { namespace bridge { class MessageSet; } } - while True: - # Match start of namespace. The "\b\s*" below catches namespace - # declarations even if it weren't followed by a whitespace, this - # is so that we don't confuse our namespace checker. The - # missing spaces will be flagged by CheckSpacing. - namespace_decl_match = Match(r'^\s*namespace\b\s*([:\w]+)?(.*)$', line) - if not namespace_decl_match: - break - - new_namespace = _NamespaceInfo(namespace_decl_match.group(1), linenum) - self.stack.append(new_namespace) - - line = namespace_decl_match.group(2) - if line.find('{') != -1: - new_namespace.seen_open_brace = True - line = line[line.find('{') + 1:] - - # Look for a class declaration in whatever is left of the line - # after parsing namespaces. The regexp accounts for decorated classes - # such as in: - # class LOCKABLE API Object { - # }; - # - # Templates with class arguments may confuse the parser, for example: - # template , - # class Vector = vector > - # class HeapQueue { - # - # Because this parser has no nesting state about templates, by the - # time it saw "class Comparator", it may think that it's a new class. - # Nested templates have a similar problem: - # template < - # typename ExportedType, - # typename TupleType, - # template class ImplTemplate> - # - # To avoid these cases, we ignore classes that are followed by '=' or '>' - class_decl_match = Match( - r'\s*(template\s*<[\w\s<>,:]*>\s*)?' - '(class|struct)\s+([A-Z_]+\s+)*(\w+(?:::\w+)*)' - '(([^=>]|<[^<>]*>)*)$', line) - if (class_decl_match and - (not self.stack or self.stack[-1].open_parentheses == 0)): - self.stack.append(_ClassInfo( - class_decl_match.group(4), class_decl_match.group(2), - clean_lines, linenum)) - line = class_decl_match.group(5) - - # If we have not yet seen the opening brace for the innermost block, - # run checks here. - if not self.SeenOpenBrace(): - self.stack[-1].CheckBegin(filename, clean_lines, linenum, error) - - # Update access control if we are inside a class/struct - if self.stack and isinstance(self.stack[-1], _ClassInfo): - access_match = Match(r'\s*(public|private|protected)\s*:', line) - if access_match: - self.stack[-1].access = access_match.group(1) - - # Consume braces or semicolons from what's left of the line - while True: - # Match first brace, semicolon, or closed parenthesis. - matched = Match(r'^[^{;)}]*([{;)}])(.*)$', line) - if not matched: - break - - token = matched.group(1) - if token == '{': - # If namespace or class hasn't seen a opening brace yet, mark - # namespace/class head as complete. Push a new block onto the - # stack otherwise. - if not self.SeenOpenBrace(): - self.stack[-1].seen_open_brace = True - else: - self.stack.append(_BlockInfo(True)) - if _MATCH_ASM.match(line): - self.stack[-1].inline_asm = _BLOCK_ASM - elif token == ';' or token == ')': - # If we haven't seen an opening brace yet, but we already saw - # a semicolon, this is probably a forward declaration. Pop - # the stack for these. - # - # Similarly, if we haven't seen an opening brace yet, but we - # already saw a closing parenthesis, then these are probably - # function arguments with extra "class" or "struct" keywords. - # Also pop these stack for these. - if not self.SeenOpenBrace(): - self.stack.pop() - else: # token == '}' - # Perform end of block checks and pop the stack. - if self.stack: - self.stack[-1].CheckEnd(filename, clean_lines, linenum, error) - self.stack.pop() - line = matched.group(2) - - def InnermostClass(self): - """Get class info on the top of the stack. - - Returns: - A _ClassInfo object if we are inside a class, or None otherwise. - """ - for i in range(len(self.stack), 0, -1): - classinfo = self.stack[i - 1] - if isinstance(classinfo, _ClassInfo): - return classinfo - return None - - def CheckClassFinished(self, filename, error): - """Checks that all classes have been completely parsed. - - Call this when all lines in a file have been processed. - Args: - filename: The name of the current file. - error: The function to call with any errors found. - """ - # Note: This test can result in false positives if #ifdef constructs - # get in the way of brace matching. See the testBuildClass test in - # cpplint_unittest.py for an example of this. - for obj in self.stack: - if isinstance(obj, _ClassInfo): - error(filename, obj.starting_linenum, 'build/class', 5, - 'Failed to find complete declaration of class %s' % - obj.name) - - -def CheckForNonStandardConstructs(filename, clean_lines, linenum, - nesting_state, error): - """Logs an error if we see certain non-ANSI constructs ignored by gcc-2. - - Complain about several constructs which gcc-2 accepts, but which are - not standard C++. Warning about these in lint is one way to ease the - transition to new compilers. - - put storage class first (e.g. "static const" instead of "const static"). - - "%lld" instead of %qd" in printf-type functions. - - "%1$d" is non-standard in printf-type functions. - - "\%" is an undefined character escape sequence. - - text after #endif is not allowed. - - invalid inner-style forward declaration. - - >? and ?= and )\?=?\s*(\w+|[+-]?\d+)(\.\d*)?', - line): - error(filename, linenum, 'build/deprecated', 3, - '>? and ))?' - # r'\s*const\s*' + type_name + '\s*&\s*\w+\s*;' - error(filename, linenum, 'runtime/member_string_references', 2, - 'const string& members are dangerous. It is much better to use ' - 'alternatives, such as pointers or simple constants.') - - # Everything else in this function operates on class declarations. - # Return early if the top of the nesting stack is not a class, or if - # the class head is not completed yet. - classinfo = nesting_state.InnermostClass() - if not classinfo or not classinfo.seen_open_brace: - return - - # The class may have been declared with namespace or classname qualifiers. - # The constructor and destructor will not have those qualifiers. - base_classname = classinfo.name.split('::')[-1] - - # Look for single-argument constructors that aren't marked explicit. - # Technically a valid construct, but against style. - args = Match(r'\s+(?:inline\s+)?%s\s*\(([^,()]+)\)' - % re.escape(base_classname), - line) - if (args and - args.group(1) != 'void' and - not Match(r'(const\s+)?%s\s*(?:<\w+>\s*)?&' % re.escape(base_classname), - args.group(1).strip())): - error(filename, linenum, 'runtime/explicit', 5, - 'Single-argument constructors should be marked explicit.') - - -def CheckSpacingForFunctionCall(filename, line, linenum, error): - """Checks for the correctness of various spacing around function calls. - - Args: - filename: The name of the current file. - line: The text of the line to check. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - - # Since function calls often occur inside if/for/while/switch - # expressions - which have their own, more liberal conventions - we - # first see if we should be looking inside such an expression for a - # function call, to which we can apply more strict standards. - fncall = line # if there's no control flow construct, look at whole line - for pattern in (r'\bif\s*\((.*)\)\s*{', - r'\bfor\s*\((.*)\)\s*{', - r'\bwhile\s*\((.*)\)\s*[{;]', - r'\bswitch\s*\((.*)\)\s*{'): - match = Search(pattern, line) - if match: - fncall = match.group(1) # look inside the parens for function calls - break - - # Except in if/for/while/switch, there should never be space - # immediately inside parens (eg "f( 3, 4 )"). We make an exception - # for nested parens ( (a+b) + c ). Likewise, there should never be - # a space before a ( when it's a function argument. I assume it's a - # function argument when the char before the whitespace is legal in - # a function name (alnum + _) and we're not starting a macro. Also ignore - # pointers and references to arrays and functions coz they're too tricky: - # we use a very simple way to recognize these: - # " (something)(maybe-something)" or - # " (something)(maybe-something," or - # " (something)[something]" - # Note that we assume the contents of [] to be short enough that - # they'll never need to wrap. - if ( # Ignore control structures. - # BEGIN android-changed - # not Search(r'\b(if|for|while|switch|return|delete)\b', fncall) and - not Search(r'\b(if|for|while|switch|return|delete|new)\b', fncall) and - # END android-changed - # Ignore pointers/references to functions. - not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and - # Ignore pointers/references to arrays. - not Search(r' \([^)]+\)\[[^\]]+\]', fncall)): - if Search(r'\w\s*\(\s(?!\s*\\$)', fncall): # a ( used for a fn call - error(filename, linenum, 'whitespace/parens', 4, - 'Extra space after ( in function call') - elif Search(r'\(\s+(?!(\s*\\)|\()', fncall): - error(filename, linenum, 'whitespace/parens', 2, - 'Extra space after (') - if (Search(r'\w\s+\(', fncall) and - not Search(r'#\s*define|typedef', fncall) and - not Search(r'\w\s+\((\w+::)?\*\w+\)\(', fncall)): - error(filename, linenum, 'whitespace/parens', 4, - 'Extra space before ( in function call') - # If the ) is followed only by a newline or a { + newline, assume it's - # part of a control statement (if/while/etc), and don't complain - if Search(r'[^)]\s+\)\s*[^{\s]', fncall): - # If the closing parenthesis is preceded by only whitespaces, - # try to give a more descriptive error message. - if Search(r'^\s+\)', fncall): - error(filename, linenum, 'whitespace/parens', 2, - 'Closing ) should be moved to the previous line') - else: - error(filename, linenum, 'whitespace/parens', 2, - 'Extra space before )') - - -def IsBlankLine(line): - """Returns true if the given line is blank. - - We consider a line to be blank if the line is empty or consists of - only white spaces. - - Args: - line: A line of a string. - - Returns: - True, if the given line is blank. - """ - return not line or line.isspace() - - -def CheckForFunctionLengths(filename, clean_lines, linenum, - function_state, error): - """Reports for long function bodies. - - For an overview why this is done, see: - http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions - - Uses a simplistic algorithm assuming other style guidelines - (especially spacing) are followed. - Only checks unindented functions, so class members are unchecked. - Trivial bodies are unchecked, so constructors with huge initializer lists - may be missed. - Blank/comment lines are not counted so as to avoid encouraging the removal - of vertical space and comments just to get through a lint check. - NOLINT *on the last line of a function* disables this check. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - function_state: Current function name and lines in body so far. - error: The function to call with any errors found. - """ - lines = clean_lines.lines - line = lines[linenum] - raw = clean_lines.raw_lines - raw_line = raw[linenum] - joined_line = '' - - starting_func = False - regexp = r'(\w(\w|::|\*|\&|\s)*)\(' # decls * & space::name( ... - match_result = Match(regexp, line) - if match_result: - # If the name is all caps and underscores, figure it's a macro and - # ignore it, unless it's TEST or TEST_F. - function_name = match_result.group(1).split()[-1] - if function_name == 'TEST' or function_name == 'TEST_F' or ( - not Match(r'[A-Z_]+$', function_name)): - starting_func = True - - if starting_func: - body_found = False - for start_linenum in xrange(linenum, clean_lines.NumLines()): - start_line = lines[start_linenum] - joined_line += ' ' + start_line.lstrip() - if Search(r'(;|})', start_line): # Declarations and trivial functions - body_found = True - break # ... ignore - elif Search(r'{', start_line): - body_found = True - function = Search(r'((\w|:)*)\(', line).group(1) - if Match(r'TEST', function): # Handle TEST... macros - parameter_regexp = Search(r'(\(.*\))', joined_line) - if parameter_regexp: # Ignore bad syntax - function += parameter_regexp.group(1) - else: - function += '()' - function_state.Begin(function) - break - if not body_found: - # No body for the function (or evidence of a non-function) was found. - error(filename, linenum, 'readability/fn_size', 5, - 'Lint failed to find start of function body.') - elif Match(r'^\}\s*$', line): # function end - function_state.Check(error, filename, linenum) - function_state.End() - elif not Match(r'^\s*$', line): - function_state.Count() # Count non-blank/non-comment lines. - - -_RE_PATTERN_TODO = re.compile(r'^//(\s*)TODO(\(.+?\))?:?(\s|$)?') - - -def CheckComment(comment, filename, linenum, error): - """Checks for common mistakes in TODO comments. - - Args: - comment: The text of the comment from the line in question. - filename: The name of the current file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - match = _RE_PATTERN_TODO.match(comment) - if match: - # One whitespace is correct; zero whitespace is handled elsewhere. - leading_whitespace = match.group(1) - if len(leading_whitespace) > 1: - error(filename, linenum, 'whitespace/todo', 2, - 'Too many spaces before TODO') - - username = match.group(2) - if not username: - error(filename, linenum, 'readability/todo', 2, - 'Missing username in TODO; it should look like ' - '"// TODO(my_username): Stuff."') - - middle_whitespace = match.group(3) - # Comparisons made explicit for correctness -- pylint: disable-msg=C6403 - if middle_whitespace != ' ' and middle_whitespace != '': - error(filename, linenum, 'whitespace/todo', 2, - 'TODO(my_username) should be followed by a space') - -def CheckAccess(filename, clean_lines, linenum, nesting_state, error): - """Checks for improper use of DISALLOW* macros. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - nesting_state: A _NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] # get rid of comments and strings - - matched = Match((r'\s*(DISALLOW_COPY_AND_ASSIGN|' - r'DISALLOW_EVIL_CONSTRUCTORS|' - r'DISALLOW_IMPLICIT_CONSTRUCTORS)'), line) - if not matched: - return - if nesting_state.stack and isinstance(nesting_state.stack[-1], _ClassInfo): - if nesting_state.stack[-1].access != 'private': - error(filename, linenum, 'readability/constructors', 3, - '%s must be in the private: section' % matched.group(1)) - - else: - # Found DISALLOW* macro outside a class declaration, or perhaps it - # was used inside a function when it should have been part of the - # class declaration. We could issue a warning here, but it - # probably resulted in a compiler error already. - pass - - -def FindNextMatchingAngleBracket(clean_lines, linenum, init_suffix): - """Find the corresponding > to close a template. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: Current line number. - init_suffix: Remainder of the current line after the initial <. - - Returns: - True if a matching bracket exists. - """ - line = init_suffix - nesting_stack = ['<'] - while True: - # Find the next operator that can tell us whether < is used as an - # opening bracket or as a less-than operator. We only want to - # warn on the latter case. - # - # We could also check all other operators and terminate the search - # early, e.g. if we got something like this "a(),;\[\]]*([<>(),;\[\]])(.*)$', line) - if match: - # Found an operator, update nesting stack - operator = match.group(1) - line = match.group(2) - - if nesting_stack[-1] == '<': - # Expecting closing angle bracket - if operator in ('<', '(', '['): - nesting_stack.append(operator) - elif operator == '>': - nesting_stack.pop() - if not nesting_stack: - # Found matching angle bracket - return True - elif operator == ',': - # Got a comma after a bracket, this is most likely a template - # argument. We have not seen a closing angle bracket yet, but - # it's probably a few lines later if we look for it, so just - # return early here. - return True - else: - # Got some other operator. - return False - - else: - # Expecting closing parenthesis or closing bracket - if operator in ('<', '(', '['): - nesting_stack.append(operator) - elif operator in (')', ']'): - # We don't bother checking for matching () or []. If we got - # something like (] or [), it would have been a syntax error. - nesting_stack.pop() - - else: - # Scan the next line - linenum += 1 - if linenum >= len(clean_lines.elided): - break - line = clean_lines.elided[linenum] - - # Exhausted all remaining lines and still no matching angle bracket. - # Most likely the input was incomplete, otherwise we should have - # seen a semicolon and returned early. - return True - - -def FindPreviousMatchingAngleBracket(clean_lines, linenum, init_prefix): - """Find the corresponding < that started a template. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: Current line number. - init_prefix: Part of the current line before the initial >. - - Returns: - True if a matching bracket exists. - """ - line = init_prefix - nesting_stack = ['>'] - while True: - # Find the previous operator - match = Search(r'^(.*)([<>(),;\[\]])[^<>(),;\[\]]*$', line) - if match: - # Found an operator, update nesting stack - operator = match.group(2) - line = match.group(1) - - if nesting_stack[-1] == '>': - # Expecting opening angle bracket - if operator in ('>', ')', ']'): - nesting_stack.append(operator) - elif operator == '<': - nesting_stack.pop() - if not nesting_stack: - # Found matching angle bracket - return True - elif operator == ',': - # Got a comma before a bracket, this is most likely a - # template argument. The opening angle bracket is probably - # there if we look for it, so just return early here. - return True - else: - # Got some other operator. - return False - - else: - # Expecting opening parenthesis or opening bracket - if operator in ('>', ')', ']'): - nesting_stack.append(operator) - elif operator in ('(', '['): - nesting_stack.pop() - - else: - # Scan the previous line - linenum -= 1 - if linenum < 0: - break - line = clean_lines.elided[linenum] - - # Exhausted all earlier lines and still no matching angle bracket. - return False - - -def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): - """Checks for the correctness of various spacing issues in the code. - - Things we check for: spaces around operators, spaces after - if/for/while/switch, no spaces around parens in function calls, two - spaces between code and comment, don't start a block with a blank - line, don't end a function with a blank line, don't add a blank line - after public/protected/private, don't have too many blank lines in a row. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - nesting_state: A _NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - - raw = clean_lines.raw_lines - line = raw[linenum] - - # Before nixing comments, check if the line is blank for no good - # reason. This includes the first line after a block is opened, and - # blank lines at the end of a function (ie, right before a line like '}' - # - # Skip all the blank line checks if we are immediately inside a - # namespace body. In other words, don't issue blank line warnings - # for this block: - # namespace { - # - # } - # - # A warning about missing end of namespace comments will be issued instead. - if IsBlankLine(line) and not nesting_state.InNamespaceBody(): - elided = clean_lines.elided - prev_line = elided[linenum - 1] - prevbrace = prev_line.rfind('{') - # TODO(unknown): Don't complain if line before blank line, and line after, - # both start with alnums and are indented the same amount. - # This ignores whitespace at the start of a namespace block - # because those are not usually indented. - if prevbrace != -1 and prev_line[prevbrace:].find('}') == -1: - # OK, we have a blank line at the start of a code block. Before we - # complain, we check if it is an exception to the rule: The previous - # non-empty line has the parameters of a function header that are indented - # 4 spaces (because they did not fit in a 80 column line when placed on - # the same line as the function name). We also check for the case where - # the previous line is indented 6 spaces, which may happen when the - # initializers of a constructor do not fit into a 80 column line. - exception = False - if Match(r' {6}\w', prev_line): # Initializer list? - # We are looking for the opening column of initializer list, which - # should be indented 4 spaces to cause 6 space indentation afterwards. - search_position = linenum-2 - while (search_position >= 0 - and Match(r' {6}\w', elided[search_position])): - search_position -= 1 - exception = (search_position >= 0 - and elided[search_position][:5] == ' :') - else: - # Search for the function arguments or an initializer list. We use a - # simple heuristic here: If the line is indented 4 spaces; and we have a - # closing paren, without the opening paren, followed by an opening brace - # or colon (for initializer lists) we assume that it is the last line of - # a function header. If we have a colon indented 4 spaces, it is an - # initializer list. - exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)', - prev_line) - or Match(r' {4}:', prev_line)) - - if not exception: - error(filename, linenum, 'whitespace/blank_line', 2, - 'Blank line at the start of a code block. Is this needed?') - # Ignore blank lines at the end of a block in a long if-else - # chain, like this: - # if (condition1) { - # // Something followed by a blank line - # - # } else if (condition2) { - # // Something else - # } - if linenum + 1 < clean_lines.NumLines(): - next_line = raw[linenum + 1] - if (next_line - and Match(r'\s*}', next_line) - and next_line.find('} else ') == -1): - error(filename, linenum, 'whitespace/blank_line', 3, - 'Blank line at the end of a code block. Is this needed?') - - matched = Match(r'\s*(public|protected|private):', prev_line) - if matched: - error(filename, linenum, 'whitespace/blank_line', 3, - 'Do not leave a blank line after "%s:"' % matched.group(1)) - - # Next, we complain if there's a comment too near the text - commentpos = line.find('//') - if commentpos != -1: - # Check if the // may be in quotes. If so, ignore it - # Comparisons made explicit for clarity -- pylint: disable-msg=C6403 - if (line.count('"', 0, commentpos) - - line.count('\\"', 0, commentpos)) % 2 == 0: # not in quotes - # Allow one space for new scopes, two spaces otherwise: - if (not Match(r'^\s*{ //', line) and - ((commentpos >= 1 and - line[commentpos-1] not in string.whitespace) or - (commentpos >= 2 and - line[commentpos-2] not in string.whitespace))): - error(filename, linenum, 'whitespace/comments', 2, - 'At least two spaces is best between code and comments') - # There should always be a space between the // and the comment - commentend = commentpos + 2 - if commentend < len(line) and not line[commentend] == ' ': - # but some lines are exceptions -- e.g. if they're big - # comment delimiters like: - # //---------------------------------------------------------- - # or are an empty C++ style Doxygen comment, like: - # /// - # or they begin with multiple slashes followed by a space: - # //////// Header comment - match = (Search(r'[=/-]{4,}\s*$', line[commentend:]) or - Search(r'^/$', line[commentend:]) or - Search(r'^/+ ', line[commentend:])) - if not match: - error(filename, linenum, 'whitespace/comments', 4, - 'Should have a space between // and comment') - CheckComment(line[commentpos:], filename, linenum, error) - - line = clean_lines.elided[linenum] # get rid of comments and strings - - # Don't try to do spacing checks for operator methods - line = re.sub(r'operator(==|!=|<|<<|<=|>=|>>|>)\(', 'operator\(', line) - - # We allow no-spaces around = within an if: "if ( (a=Foo()) == 0 )". - # Otherwise not. Note we only check for non-spaces on *both* sides; - # sometimes people put non-spaces on one side when aligning ='s among - # many lines (not that this is behavior that I approve of...) - if Search(r'[\w.]=[\w.]', line) and not Search(r'\b(if|while) ', line): - error(filename, linenum, 'whitespace/operators', 4, - 'Missing spaces around =') - - # It's ok not to have spaces around binary operators like + - * /, but if - # there's too little whitespace, we get concerned. It's hard to tell, - # though, so we punt on this one for now. TODO. - - # You should always have whitespace around binary operators. - # - # Check <= and >= first to avoid false positives with < and >, then - # check non-include lines for spacing around < and >. - match = Search(r'[^<>=!\s](==|!=|<=|>=)[^<>=!\s]', line) - if match: - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around %s' % match.group(1)) - # We allow no-spaces around << when used like this: 10<<20, but - # not otherwise (particularly, not when used as streams) - match = Search(r'(\S)(?:L|UL|ULL|l|ul|ull)?<<(\S)', line) - if match and not (match.group(1).isdigit() and match.group(2).isdigit()): - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around <<') - elif not Match(r'#.*include', line): - # Avoid false positives on -> - reduced_line = line.replace('->', '') - - # Look for < that is not surrounded by spaces. This is only - # triggered if both sides are missing spaces, even though - # technically should should flag if at least one side is missing a - # space. This is done to avoid some false positives with shifts. - match = Search(r'[^\s<]<([^\s=<].*)', reduced_line) - if (match and - not FindNextMatchingAngleBracket(clean_lines, linenum, match.group(1))): - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around <') - - # Look for > that is not surrounded by spaces. Similar to the - # above, we only trigger if both sides are missing spaces to avoid - # false positives with shifts. - match = Search(r'^(.*[^\s>])>[^\s=>]', reduced_line) - if (match and - not FindPreviousMatchingAngleBracket(clean_lines, linenum, - match.group(1))): - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around >') - - # We allow no-spaces around >> for almost anything. This is because - # C++11 allows ">>" to close nested templates, which accounts for - # most cases when ">>" is not followed by a space. - # - # We still warn on ">>" followed by alpha character, because that is - # likely due to ">>" being used for right shifts, e.g.: - # value >> alpha - # - # When ">>" is used to close templates, the alphanumeric letter that - # follows would be part of an identifier, and there should still be - # a space separating the template type and the identifier. - # type> alpha - match = Search(r'>>[a-zA-Z_]', line) - if match: - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around >>') - - # There shouldn't be space around unary operators - match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line) - if match: - error(filename, linenum, 'whitespace/operators', 4, - 'Extra space for operator %s' % match.group(1)) - - # A pet peeve of mine: no spaces after an if, while, switch, or for - match = Search(r' (if\(|for\(|while\(|switch\()', line) - if match: - error(filename, linenum, 'whitespace/parens', 5, - 'Missing space before ( in %s' % match.group(1)) - - # For if/for/while/switch, the left and right parens should be - # consistent about how many spaces are inside the parens, and - # there should either be zero or one spaces inside the parens. - # We don't want: "if ( foo)" or "if ( foo )". - # Exception: "for ( ; foo; bar)" and "for (foo; bar; )" are allowed. - match = Search(r'\b(if|for|while|switch)\s*' - r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$', - line) - if match: - if len(match.group(2)) != len(match.group(4)): - if not (match.group(3) == ';' and - len(match.group(2)) == 1 + len(match.group(4)) or - not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)): - error(filename, linenum, 'whitespace/parens', 5, - 'Mismatching spaces inside () in %s' % match.group(1)) - if not len(match.group(2)) in [0, 1]: - error(filename, linenum, 'whitespace/parens', 5, - 'Should have zero or one spaces inside ( and ) in %s' % - match.group(1)) - - # You should always have a space after a comma (either as fn arg or operator) - if Search(r',[^\s]', line): - error(filename, linenum, 'whitespace/comma', 3, - 'Missing space after ,') - - # You should always have a space after a semicolon - # except for few corner cases - # TODO(unknown): clarify if 'if (1) { return 1;}' is requires one more - # space after ; - if Search(r';[^\s};\\)/]', line): - error(filename, linenum, 'whitespace/semicolon', 3, - 'Missing space after ;') - - # Next we will look for issues with function calls. - CheckSpacingForFunctionCall(filename, line, linenum, error) - - # Except after an opening paren, or after another opening brace (in case of - # an initializer list, for instance), you should have spaces before your - # braces. And since you should never have braces at the beginning of a line, - # this is an easy test. - if Search(r'[^ ({]{', line): - error(filename, linenum, 'whitespace/braces', 5, - 'Missing space before {') - - # Make sure '} else {' has spaces. - if Search(r'}else', line): - error(filename, linenum, 'whitespace/braces', 5, - 'Missing space before else') - - # You shouldn't have spaces before your brackets, except maybe after - # 'delete []' or 'new char * []'. - if Search(r'\w\s+\[', line) and not Search(r'delete\s+\[', line): - error(filename, linenum, 'whitespace/braces', 5, - 'Extra space before [') - - # You shouldn't have a space before a semicolon at the end of the line. - # There's a special case for "for" since the style guide allows space before - # the semicolon there. - if Search(r':\s*;\s*$', line): - error(filename, linenum, 'whitespace/semicolon', 5, - 'Semicolon defining empty statement. Use {} instead.') - elif Search(r'^\s*;\s*$', line): - error(filename, linenum, 'whitespace/semicolon', 5, - 'Line contains only semicolon. If this should be an empty statement, ' - 'use {} instead.') - elif (Search(r'\s+;\s*$', line) and - not Search(r'\bfor\b', line)): - error(filename, linenum, 'whitespace/semicolon', 5, - 'Extra space before last semicolon. If this should be an empty ' - 'statement, use {} instead.') - - # In range-based for, we wanted spaces before and after the colon, but - # not around "::" tokens that might appear. - if (Search('for *\(.*[^:]:[^: ]', line) or - Search('for *\(.*[^: ]:[^:]', line)): - error(filename, linenum, 'whitespace/forcolon', 2, - 'Missing space around colon in range-based for loop') - - -def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error): - """Checks for additional blank line issues related to sections. - - Currently the only thing checked here is blank line before protected/private. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - class_info: A _ClassInfo objects. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - # Skip checks if the class is small, where small means 25 lines or less. - # 25 lines seems like a good cutoff since that's the usual height of - # terminals, and any class that can't fit in one screen can't really - # be considered "small". - # - # Also skip checks if we are on the first line. This accounts for - # classes that look like - # class Foo { public: ... }; - # - # If we didn't find the end of the class, last_line would be zero, - # and the check will be skipped by the first condition. - if (class_info.last_line - class_info.starting_linenum <= 24 or - linenum <= class_info.starting_linenum): - return - - matched = Match(r'\s*(public|protected|private):', clean_lines.lines[linenum]) - if matched: - # Issue warning if the line before public/protected/private was - # not a blank line, but don't do this if the previous line contains - # "class" or "struct". This can happen two ways: - # - We are at the beginning of the class. - # - We are forward-declaring an inner class that is semantically - # private, but needed to be public for implementation reasons. - # Also ignores cases where the previous line ends with a backslash as can be - # common when defining classes in C macros. - prev_line = clean_lines.lines[linenum - 1] - if (not IsBlankLine(prev_line) and - not Search(r'\b(class|struct)\b', prev_line) and - not Search(r'\\$', prev_line)): - # Try a bit harder to find the beginning of the class. This is to - # account for multi-line base-specifier lists, e.g.: - # class Derived - # : public Base { - end_class_head = class_info.starting_linenum - for i in range(class_info.starting_linenum, linenum): - if Search(r'\{\s*$', clean_lines.lines[i]): - end_class_head = i - break - if end_class_head < linenum - 1: - error(filename, linenum, 'whitespace/blank_line', 3, - '"%s:" should be preceded by a blank line' % matched.group(1)) - - -def GetPreviousNonBlankLine(clean_lines, linenum): - """Return the most recent non-blank line and its line number. - - Args: - clean_lines: A CleansedLines instance containing the file contents. - linenum: The number of the line to check. - - Returns: - A tuple with two elements. The first element is the contents of the last - non-blank line before the current line, or the empty string if this is the - first non-blank line. The second is the line number of that line, or -1 - if this is the first non-blank line. - """ - - prevlinenum = linenum - 1 - while prevlinenum >= 0: - prevline = clean_lines.elided[prevlinenum] - if not IsBlankLine(prevline): # if not a blank line... - return (prevline, prevlinenum) - prevlinenum -= 1 - return ('', -1) - - -def CheckBraces(filename, clean_lines, linenum, error): - """Looks for misplaced braces (e.g. at the end of line). - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - - line = clean_lines.elided[linenum] # get rid of comments and strings - - if Match(r'\s*{\s*$', line): - # We allow an open brace to start a line in the case where someone - # is using braces in a block to explicitly create a new scope, - # which is commonly used to control the lifetime of - # stack-allocated variables. We don't detect this perfectly: we - # just don't complain if the last non-whitespace character on the - # previous non-blank line is ';', ':', '{', or '}', or if the previous - # line starts a preprocessor block. - prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] - if (not Search(r'[;:}{]\s*$', prevline) and - not Match(r'\s*#', prevline)): - error(filename, linenum, 'whitespace/braces', 4, - '{ should almost always be at the end of the previous line') - - # An else clause should be on the same line as the preceding closing brace. - if Match(r'\s*else\s*', line): - prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] - if Match(r'\s*}\s*$', prevline): - error(filename, linenum, 'whitespace/newline', 4, - 'An else should appear on the same line as the preceding }') - - # If braces come on one side of an else, they should be on both. - # However, we have to worry about "else if" that spans multiple lines! - if Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line): - if Search(r'}\s*else if([^{]*)$', line): # could be multi-line if - # find the ( after the if - pos = line.find('else if') - pos = line.find('(', pos) - if pos > 0: - (endline, _, endpos) = CloseExpression(clean_lines, linenum, pos) - if endline[endpos:].find('{') == -1: # must be brace after if - error(filename, linenum, 'readability/braces', 5, - 'If an else has a brace on one side, it should have it on both') - else: # common case: else not followed by a multi-line if - error(filename, linenum, 'readability/braces', 5, - 'If an else has a brace on one side, it should have it on both') - - # Likewise, an else should never have the else clause on the same line - if Search(r'\belse [^\s{]', line) and not Search(r'\belse if\b', line): - error(filename, linenum, 'whitespace/newline', 4, - 'Else clause should never be on same line as else (use 2 lines)') - - # In the same way, a do/while should never be on one line - if Match(r'\s*do [^\s{]', line): - error(filename, linenum, 'whitespace/newline', 4, - 'do/while clauses should not be on a single line') - - # Braces shouldn't be followed by a ; unless they're defining a struct - # or initializing an array. - # We can't tell in general, but we can for some common cases. - prevlinenum = linenum - while True: - (prevline, prevlinenum) = GetPreviousNonBlankLine(clean_lines, prevlinenum) - if Match(r'\s+{.*}\s*;', line) and not prevline.count(';'): - line = prevline + line - else: - break - if (Search(r'{.*}\s*;', line) and - line.count('{') == line.count('}') and - not Search(r'struct|class|enum|\s*=\s*{', line)): - error(filename, linenum, 'readability/braces', 4, - "You don't need a ; after a }") - - -def CheckEmptyLoopBody(filename, clean_lines, linenum, error): - """Loop for empty loop body with only a single semicolon. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - - # Search for loop keywords at the beginning of the line. Because only - # whitespaces are allowed before the keywords, this will also ignore most - # do-while-loops, since those lines should start with closing brace. - line = clean_lines.elided[linenum] - if Match(r'\s*(for|while)\s*\(', line): - # Find the end of the conditional expression - (end_line, end_linenum, end_pos) = CloseExpression( - clean_lines, linenum, line.find('(')) - - # Output warning if what follows the condition expression is a semicolon. - # No warning for all other cases, including whitespace or newline, since we - # have a separate check for semicolons preceded by whitespace. - if end_pos >= 0 and Match(r';', end_line[end_pos:]): - error(filename, end_linenum, 'whitespace/empty_loop_body', 5, - 'Empty loop bodies should use {} or continue') - - -def ReplaceableCheck(operator, macro, line): - """Determine whether a basic CHECK can be replaced with a more specific one. - - For example suggest using CHECK_EQ instead of CHECK(a == b) and - similarly for CHECK_GE, CHECK_GT, CHECK_LE, CHECK_LT, CHECK_NE. - - Args: - operator: The C++ operator used in the CHECK. - macro: The CHECK or EXPECT macro being called. - line: The current source line. - - Returns: - True if the CHECK can be replaced with a more specific one. - """ - - # This matches decimal and hex integers, strings, and chars (in that order). - match_constant = r'([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')' - - # Expression to match two sides of the operator with something that - # looks like a literal, since CHECK(x == iterator) won't compile. - # This means we can't catch all the cases where a more specific - # CHECK is possible, but it's less annoying than dealing with - # extraneous warnings. - match_this = (r'\s*' + macro + r'\((\s*' + - match_constant + r'\s*' + operator + r'[^<>].*|' - r'.*[^<>]' + operator + r'\s*' + match_constant + - r'\s*\))') - - # Don't complain about CHECK(x == NULL) or similar because - # CHECK_EQ(x, NULL) won't compile (requires a cast). - # Also, don't complain about more complex boolean expressions - # involving && or || such as CHECK(a == b || c == d). - return Match(match_this, line) and not Search(r'NULL|&&|\|\|', line) - - -def CheckCheck(filename, clean_lines, linenum, error): - """Checks the use of CHECK and EXPECT macros. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - - # Decide the set of replacement macros that should be suggested - raw_lines = clean_lines.raw_lines - current_macro = '' - for macro in _CHECK_MACROS: - if raw_lines[linenum].find(macro) >= 0: - current_macro = macro - break - if not current_macro: - # Don't waste time here if line doesn't contain 'CHECK' or 'EXPECT' - return - - line = clean_lines.elided[linenum] # get rid of comments and strings - - # Encourage replacing plain CHECKs with CHECK_EQ/CHECK_NE/etc. - for operator in ['==', '!=', '>=', '>', '<=', '<']: - if ReplaceableCheck(operator, current_macro, line): - error(filename, linenum, 'readability/check', 2, - 'Consider using %s instead of %s(a %s b)' % ( - _CHECK_REPLACEMENT[current_macro][operator], - current_macro, operator)) - break - - -def CheckAltTokens(filename, clean_lines, linenum, error): - """Check alternative keywords being used in boolean expressions. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Avoid preprocessor lines - if Match(r'^\s*#', line): - return - - # Last ditch effort to avoid multi-line comments. This will not help - # if the comment started before the current line or ended after the - # current line, but it catches most of the false positives. At least, - # it provides a way to workaround this warning for people who use - # multi-line comments in preprocessor macros. - # - # TODO(unknown): remove this once cpplint has better support for - # multi-line comments. - if line.find('/*') >= 0 or line.find('*/') >= 0: - return - - for match in _ALT_TOKEN_REPLACEMENT_PATTERN.finditer(line): - error(filename, linenum, 'readability/alt_tokens', 2, - 'Use operator %s instead of %s' % ( - _ALT_TOKEN_REPLACEMENT[match.group(1)], match.group(1))) - - -def GetLineWidth(line): - """Determines the width of the line in column positions. - - Args: - line: A string, which may be a Unicode string. - - Returns: - The width of the line in column positions, accounting for Unicode - combining characters and wide characters. - """ - if isinstance(line, unicode): - width = 0 - for uc in unicodedata.normalize('NFC', line): - if unicodedata.east_asian_width(uc) in ('W', 'F'): - width += 2 - elif not unicodedata.combining(uc): - width += 1 - return width - else: - return len(line) - - -def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state, - error): - """Checks rules from the 'C++ style rules' section of cppguide.html. - - Most of these rules are hard to test (naming, comment style), but we - do what we can. In particular we check for 2-space indents, line lengths, - tab usage, spaces inside code, etc. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - file_extension: The extension (without the dot) of the filename. - nesting_state: A _NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - - raw_lines = clean_lines.raw_lines - line = raw_lines[linenum] - - if line.find('\t') != -1: - error(filename, linenum, 'whitespace/tab', 1, - 'Tab found; better to use spaces') - - # One or three blank spaces at the beginning of the line is weird; it's - # hard to reconcile that with 2-space indents. - # NOTE: here are the conditions rob pike used for his tests. Mine aren't - # as sophisticated, but it may be worth becoming so: RLENGTH==initial_spaces - # if(RLENGTH > 20) complain = 0; - # if(match($0, " +(error|private|public|protected):")) complain = 0; - # if(match(prev, "&& *$")) complain = 0; - # if(match(prev, "\\|\\| *$")) complain = 0; - # if(match(prev, "[\",=><] *$")) complain = 0; - # if(match($0, " <<")) complain = 0; - # if(match(prev, " +for \\(")) complain = 0; - # if(prevodd && match(prevprev, " +for \\(")) complain = 0; - initial_spaces = 0 - cleansed_line = clean_lines.elided[linenum] - while initial_spaces < len(line) and line[initial_spaces] == ' ': - initial_spaces += 1 - if line and line[-1].isspace(): - error(filename, linenum, 'whitespace/end_of_line', 4, - 'Line ends in whitespace. Consider deleting these extra spaces.') - # There are certain situations we allow one space, notably for labels - elif ((initial_spaces == 1 or initial_spaces == 3) and - not Match(r'\s*\w+\s*:\s*$', cleansed_line)): - error(filename, linenum, 'whitespace/indent', 3, - 'Weird number of spaces at line-start. ' - 'Are you using a 2-space indent?') - # Labels should always be indented at least one space. - elif not initial_spaces and line[:2] != '//' and Search(r'[^:]:\s*$', - line): - error(filename, linenum, 'whitespace/labels', 4, - 'Labels should always be indented at least one space. ' - 'If this is a member-initializer list in a constructor or ' - 'the base class list in a class definition, the colon should ' - 'be on the following line.') - - - # Check if the line is a header guard. - is_header_guard = False - if file_extension == 'h': - cppvar = GetHeaderGuardCPPVariable(filename) - if (line.startswith('#ifndef %s' % cppvar) or - line.startswith('#define %s' % cppvar) or - line.startswith('#endif // %s' % cppvar)): - is_header_guard = True - # #include lines and header guards can be long, since there's no clean way to - # split them. - # - # URLs can be long too. It's possible to split these, but it makes them - # harder to cut&paste. - # - # The "$Id:...$" comment may also get very long without it being the - # developers fault. - if (not line.startswith('#include') and not is_header_guard and - not Match(r'^\s*//.*http(s?)://\S*$', line) and - not Match(r'^// \$Id:.*#[0-9]+ \$$', line)): - line_width = GetLineWidth(line) - if line_width > 100: - error(filename, linenum, 'whitespace/line_length', 4, - 'Lines should very rarely be longer than 100 characters') - elif line_width > 80: - error(filename, linenum, 'whitespace/line_length', 2, - 'Lines should be <= 80 characters long') - - if (cleansed_line.count(';') > 1 and - # for loops are allowed two ;'s (and may run over two lines). - cleansed_line.find('for') == -1 and - (GetPreviousNonBlankLine(clean_lines, linenum)[0].find('for') == -1 or - GetPreviousNonBlankLine(clean_lines, linenum)[0].find(';') != -1) and - # It's ok to have many commands in a switch case that fits in 1 line - not ((cleansed_line.find('case ') != -1 or - cleansed_line.find('default:') != -1) and - cleansed_line.find('break;') != -1)): - error(filename, linenum, 'whitespace/newline', 0, - 'More than one command on the same line') - - # Some more style checks - CheckBraces(filename, clean_lines, linenum, error) - CheckEmptyLoopBody(filename, clean_lines, linenum, error) - CheckAccess(filename, clean_lines, linenum, nesting_state, error) - CheckSpacing(filename, clean_lines, linenum, nesting_state, error) - CheckCheck(filename, clean_lines, linenum, error) - CheckAltTokens(filename, clean_lines, linenum, error) - classinfo = nesting_state.InnermostClass() - if classinfo: - CheckSectionSpacing(filename, clean_lines, classinfo, linenum, error) - - -_RE_PATTERN_INCLUDE_NEW_STYLE = re.compile(r'#include +"[^/]+\.h"') -_RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$') -# Matches the first component of a filename delimited by -s and _s. That is: -# _RE_FIRST_COMPONENT.match('foo').group(0) == 'foo' -# _RE_FIRST_COMPONENT.match('foo.cc').group(0) == 'foo' -# _RE_FIRST_COMPONENT.match('foo-bar_baz.cc').group(0) == 'foo' -# _RE_FIRST_COMPONENT.match('foo_bar-baz.cc').group(0) == 'foo' -_RE_FIRST_COMPONENT = re.compile(r'^[^-_.]+') - - -def _DropCommonSuffixes(filename): - """Drops common suffixes like _test.cc or -inl.h from filename. - - For example: - >>> _DropCommonSuffixes('foo/foo-inl.h') - 'foo/foo' - >>> _DropCommonSuffixes('foo/bar/foo.cc') - 'foo/bar/foo' - >>> _DropCommonSuffixes('foo/foo_internal.h') - 'foo/foo' - >>> _DropCommonSuffixes('foo/foo_unusualinternal.h') - 'foo/foo_unusualinternal' - - Args: - filename: The input filename. - - Returns: - The filename with the common suffix removed. - """ - for suffix in ('test.cc', 'regtest.cc', 'unittest.cc', - 'inl.h', 'impl.h', 'internal.h'): - if (filename.endswith(suffix) and len(filename) > len(suffix) and - filename[-len(suffix) - 1] in ('-', '_')): - return filename[:-len(suffix) - 1] - return os.path.splitext(filename)[0] - - -def _IsTestFilename(filename): - """Determines if the given filename has a suffix that identifies it as a test. - - Args: - filename: The input filename. - - Returns: - True if 'filename' looks like a test, False otherwise. - """ - if (filename.endswith('_test.cc') or - filename.endswith('_unittest.cc') or - filename.endswith('_regtest.cc')): - return True - else: - return False - - -def _ClassifyInclude(fileinfo, include, is_system): - """Figures out what kind of header 'include' is. - - Args: - fileinfo: The current file cpplint is running over. A FileInfo instance. - include: The path to a #included file. - is_system: True if the #include used <> rather than "". - - Returns: - One of the _XXX_HEADER constants. - - For example: - >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'stdio.h', True) - _C_SYS_HEADER - >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'string', True) - _CPP_SYS_HEADER - >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/foo.h', False) - _LIKELY_MY_HEADER - >>> _ClassifyInclude(FileInfo('foo/foo_unknown_extension.cc'), - ... 'bar/foo_other_ext.h', False) - _POSSIBLE_MY_HEADER - >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/bar.h', False) - _OTHER_HEADER - """ - # This is a list of all standard c++ header files, except - # those already checked for above. - is_stl_h = include in _STL_HEADERS - is_cpp_h = is_stl_h or include in _CPP_HEADERS - - if is_system: - if is_cpp_h: - return _CPP_SYS_HEADER - else: - return _C_SYS_HEADER - - # If the target file and the include we're checking share a - # basename when we drop common extensions, and the include - # lives in . , then it's likely to be owned by the target file. - target_dir, target_base = ( - os.path.split(_DropCommonSuffixes(fileinfo.RepositoryName()))) - include_dir, include_base = os.path.split(_DropCommonSuffixes(include)) - if target_base == include_base and ( - include_dir == target_dir or - include_dir == os.path.normpath(target_dir + '/../public')): - return _LIKELY_MY_HEADER - - # If the target and include share some initial basename - # component, it's possible the target is implementing the - # include, so it's allowed to be first, but we'll never - # complain if it's not there. - target_first_component = _RE_FIRST_COMPONENT.match(target_base) - include_first_component = _RE_FIRST_COMPONENT.match(include_base) - if (target_first_component and include_first_component and - target_first_component.group(0) == - include_first_component.group(0)): - return _POSSIBLE_MY_HEADER - - return _OTHER_HEADER - - - -def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): - """Check rules that are applicable to #include lines. - - Strings on #include lines are NOT removed from elided line, to make - certain tasks easier. However, to prevent false positives, checks - applicable to #include lines in CheckLanguage must be put here. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - include_state: An _IncludeState instance in which the headers are inserted. - error: The function to call with any errors found. - """ - fileinfo = FileInfo(filename) - - line = clean_lines.lines[linenum] - - # "include" should use the new style "foo/bar.h" instead of just "bar.h" - if _RE_PATTERN_INCLUDE_NEW_STYLE.search(line): - error(filename, linenum, 'build/include', 4, - 'Include the directory when naming .h files') - - # we shouldn't include a file more than once. actually, there are a - # handful of instances where doing so is okay, but in general it's - # not. - match = _RE_PATTERN_INCLUDE.search(line) - if match: - include = match.group(2) - is_system = (match.group(1) == '<') - if include in include_state: - error(filename, linenum, 'build/include', 4, - '"%s" already included at %s:%s' % - (include, filename, include_state[include])) - else: - include_state[include] = linenum - - # We want to ensure that headers appear in the right order: - # 1) for foo.cc, foo.h (preferred location) - # 2) c system files - # 3) cpp system files - # 4) for foo.cc, foo.h (deprecated location) - # 5) other google headers - # - # We classify each include statement as one of those 5 types - # using a number of techniques. The include_state object keeps - # track of the highest type seen, and complains if we see a - # lower type after that. - error_message = include_state.CheckNextIncludeOrder( - _ClassifyInclude(fileinfo, include, is_system)) - if error_message: - error(filename, linenum, 'build/include_order', 4, - '%s. Should be: %s.h, c system, c++ system, other.' % - (error_message, fileinfo.BaseName())) - if not include_state.IsInAlphabeticalOrder(include): - error(filename, linenum, 'build/include_alpha', 4, - 'Include "%s" not in alphabetical order' % include) - - # Look for any of the stream classes that are part of standard C++. - match = _RE_PATTERN_INCLUDE.match(line) - if match: - include = match.group(2) - if Match(r'(f|ind|io|i|o|parse|pf|stdio|str|)?stream$', include): - # Many unit tests use cout, so we exempt them. - if not _IsTestFilename(filename): - error(filename, linenum, 'readability/streams', 3, - 'Streams are highly discouraged.') - - -def _GetTextInside(text, start_pattern): - """Retrieves all the text between matching open and close parentheses. - - Given a string of lines and a regular expression string, retrieve all the text - following the expression and between opening punctuation symbols like - (, [, or {, and the matching close-punctuation symbol. This properly nested - occurrences of the punctuations, so for the text like - printf(a(), b(c())); - a call to _GetTextInside(text, r'printf\(') will return 'a(), b(c())'. - start_pattern must match string having an open punctuation symbol at the end. - - Args: - text: The lines to extract text. Its comments and strings must be elided. - It can be single line and can span multiple lines. - start_pattern: The regexp string indicating where to start extracting - the text. - Returns: - The extracted text. - None if either the opening string or ending punctuation could not be found. - """ - # TODO(sugawarayu): Audit cpplint.py to see what places could be profitably - # rewritten to use _GetTextInside (and use inferior regexp matching today). - - # Give opening punctuations to get the matching close-punctuations. - matching_punctuation = {'(': ')', '{': '}', '[': ']'} - closing_punctuation = set(matching_punctuation.itervalues()) - - # Find the position to start extracting text. - match = re.search(start_pattern, text, re.M) - if not match: # start_pattern not found in text. - return None - start_position = match.end(0) - - assert start_position > 0, ( - 'start_pattern must ends with an opening punctuation.') - assert text[start_position - 1] in matching_punctuation, ( - 'start_pattern must ends with an opening punctuation.') - # Stack of closing punctuations we expect to have in text after position. - punctuation_stack = [matching_punctuation[text[start_position - 1]]] - position = start_position - while punctuation_stack and position < len(text): - if text[position] == punctuation_stack[-1]: - punctuation_stack.pop() - elif text[position] in closing_punctuation: - # A closing punctuation without matching opening punctuations. - return None - elif text[position] in matching_punctuation: - punctuation_stack.append(matching_punctuation[text[position]]) - position += 1 - if punctuation_stack: - # Opening punctuations left without matching close-punctuations. - return None - # punctuations match. - return text[start_position:position - 1] - - -def CheckLanguage(filename, clean_lines, linenum, file_extension, include_state, - error): - """Checks rules from the 'C++ language rules' section of cppguide.html. - - Some of these rules are hard to test (function overloading, using - uint32 inappropriately), but we do the best we can. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - file_extension: The extension (without the dot) of the filename. - include_state: An _IncludeState instance in which the headers are inserted. - error: The function to call with any errors found. - """ - # If the line is empty or consists of entirely a comment, no need to - # check it. - line = clean_lines.elided[linenum] - if not line: - return - - match = _RE_PATTERN_INCLUDE.search(line) - if match: - CheckIncludeLine(filename, clean_lines, linenum, include_state, error) - return - - # Create an extended_line, which is the concatenation of the current and - # next lines, for more effective checking of code that may span more than one - # line. - if linenum + 1 < clean_lines.NumLines(): - extended_line = line + clean_lines.elided[linenum + 1] - else: - extended_line = line - - # Make Windows paths like Unix. - fullname = os.path.abspath(filename).replace('\\', '/') - - # TODO(unknown): figure out if they're using default arguments in fn proto. - - # Check for non-const references in functions. This is tricky because & - # is also used to take the address of something. We allow <> for templates, - # (ignoring whatever is between the braces) and : for classes. - # These are complicated re's. They try to capture the following: - # paren (for fn-prototype start), typename, &, varname. For the const - # version, we're willing for const to be before typename or after - # Don't check the implementation on same line. - fnline = line.split('{', 1)[0] - if (len(re.findall(r'\([^()]*\b(?:[\w:]|<[^()]*>)+(\s?&|&\s?)\w+', fnline)) > - len(re.findall(r'\([^()]*\bconst\s+(?:typename\s+)?(?:struct\s+)?' - r'(?:[\w:]|<[^()]*>)+(\s?&|&\s?)\w+', fnline)) + - len(re.findall(r'\([^()]*\b(?:[\w:]|<[^()]*>)+\s+const(\s?&|&\s?)[\w]+', - fnline))): - - # We allow non-const references in a few standard places, like functions - # called "swap()" or iostream operators like "<<" or ">>". We also filter - # out for loops, which lint otherwise mistakenly thinks are functions. - if not Search( - r'(for|swap|Swap|operator[<>][<>])\s*\(\s*' - r'(?:(?:typename\s*)?[\w:]|<.*>)+\s*&', - fnline): - error(filename, linenum, 'runtime/references', 2, - 'Is this a non-const reference? ' - 'If so, make const or use a pointer.') - - # Check to see if they're using an conversion function cast. - # I just try to capture the most common basic types, though there are more. - # Parameterless conversion functions, such as bool(), are allowed as they are - # probably a member operator declaration or default constructor. - match = Search( - r'(\bnew\s+)?\b' # Grab 'new' operator, if it's there - r'(int|float|double|bool|char|int32|uint32|int64|uint64)\([^)]', line) - if match: - # gMock methods are defined using some variant of MOCK_METHODx(name, type) - # where type may be float(), int(string), etc. Without context they are - # virtually indistinguishable from int(x) casts. Likewise, gMock's - # MockCallback takes a template parameter of the form return_type(arg_type), - # which looks much like the cast we're trying to detect. - # BEGIN android-added - # The C++ 2011 std::function class template exhibits a similar issue. - # END android-added - if (match.group(1) is None and # If new operator, then this isn't a cast - not (Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line) or - # BEGIN android-changed - # Match(r'^\s*MockCallback<.*>', line))): - Match(r'^\s*MockCallback<.*>', line) or - Match(r'^\s*std::function<.*>', line))): - # END android-changed - # Try a bit harder to catch gmock lines: the only place where - # something looks like an old-style cast is where we declare the - # return type of the mocked method, and the only time when we - # are missing context is if MOCK_METHOD was split across - # multiple lines (for example http://go/hrfhr ), so we only need - # to check the previous line for MOCK_METHOD. - if (linenum == 0 or - not Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(\S+,\s*$', - clean_lines.elided[linenum - 1])): - error(filename, linenum, 'readability/casting', 4, - 'Using deprecated casting style. ' - 'Use static_cast<%s>(...) instead' % - match.group(2)) - - CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], - 'static_cast', - r'\((int|float|double|bool|char|u?int(16|32|64))\)', error) - - # This doesn't catch all cases. Consider (const char * const)"hello". - # - # (char *) "foo" should always be a const_cast (reinterpret_cast won't - # compile). - if CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], - 'const_cast', r'\((char\s?\*+\s?)\)\s*"', error): - pass - else: - # Check pointer casts for other than string constants - CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], - 'reinterpret_cast', r'\((\w+\s?\*+\s?)\)', error) - - # In addition, we look for people taking the address of a cast. This - # is dangerous -- casts can assign to temporaries, so the pointer doesn't - # point where you think. - if Search( - r'(&\([^)]+\)[\w(])|(&(static|dynamic|reinterpret)_cast\b)', line): - error(filename, linenum, 'runtime/casting', 4, - ('Are you taking an address of a cast? ' - 'This is dangerous: could be a temp var. ' - 'Take the address before doing the cast, rather than after')) - - # Check for people declaring static/global STL strings at the top level. - # This is dangerous because the C++ language does not guarantee that - # globals with constructors are initialized before the first access. - match = Match( - r'((?:|static +)(?:|const +))string +([a-zA-Z0-9_:]+)\b(.*)', - line) - # Make sure it's not a function. - # Function template specialization looks like: "string foo(...". - # Class template definitions look like: "string Foo::Method(...". - if match and not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)?\s*\(([^"]|$)', - match.group(3)): - error(filename, linenum, 'runtime/string', 4, - 'For a static/global string constant, use a C style string instead: ' - '"%schar %s[]".' % - (match.group(1), match.group(2))) - - # Check that we're not using RTTI outside of testing code. - if Search(r'\bdynamic_cast<', line) and not _IsTestFilename(filename): - error(filename, linenum, 'runtime/rtti', 5, - 'Do not use dynamic_cast<>. If you need to cast within a class ' - "hierarchy, use static_cast<> to upcast. Google doesn't support " - 'RTTI.') - - if Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line): - error(filename, linenum, 'runtime/init', 4, - 'You seem to be initializing a member variable with itself.') - - if file_extension == 'h': - # TODO(unknown): check that 1-arg constructors are explicit. - # How to tell it's a constructor? - # (handled in CheckForNonStandardConstructs for now) - # TODO(unknown): check that classes have DISALLOW_EVIL_CONSTRUCTORS - # (level 1 error) - pass - - # Check if people are using the verboten C basic types. The only exception - # we regularly allow is "unsigned short port" for port. - if Search(r'\bshort port\b', line): - if not Search(r'\bunsigned short port\b', line): - error(filename, linenum, 'runtime/int', 4, - 'Use "unsigned short" for ports, not "short"') - else: - match = Search(r'\b(short|long(?! +double)|long long)\b', line) - if match: - error(filename, linenum, 'runtime/int', 4, - 'Use int16/int64/etc, rather than the C type %s' % match.group(1)) - - # When snprintf is used, the second argument shouldn't be a literal. - match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line) - if match and match.group(2) != '0': - # If 2nd arg is zero, snprintf is used to calculate size. - error(filename, linenum, 'runtime/printf', 3, - 'If you can, use sizeof(%s) instead of %s as the 2nd arg ' - 'to snprintf.' % (match.group(1), match.group(2))) - - # Check if some verboten C functions are being used. - if Search(r'\bsprintf\b', line): - error(filename, linenum, 'runtime/printf', 5, - 'Never use sprintf. Use snprintf instead.') - match = Search(r'\b(strcpy|strcat)\b', line) - if match: - error(filename, linenum, 'runtime/printf', 4, - 'Almost always, snprintf is better than %s' % match.group(1)) - - if Search(r'\bsscanf\b', line): - error(filename, linenum, 'runtime/printf', 1, - 'sscanf can be ok, but is slow and can overflow buffers.') - - # Check if some verboten operator overloading is going on - # TODO(unknown): catch out-of-line unary operator&: - # class X {}; - # int operator&(const X& x) { return 42; } // unary operator& - # The trick is it's hard to tell apart from binary operator&: - # class Y { int operator&(const Y& x) { return 23; } }; // binary operator& - if Search(r'\boperator\s*&\s*\(\s*\)', line): - error(filename, linenum, 'runtime/operator', 4, - 'Unary operator& is dangerous. Do not use it.') - - # Check for suspicious usage of "if" like - # } if (a == b) { - if Search(r'\}\s*if\s*\(', line): - error(filename, linenum, 'readability/braces', 4, - 'Did you mean "else if"? If not, start a new line for "if".') - - # Check for potential format string bugs like printf(foo). - # We constrain the pattern not to pick things like DocidForPrintf(foo). - # Not perfect but it can catch printf(foo.c_str()) and printf(foo->c_str()) - # TODO(sugawarayu): Catch the following case. Need to change the calling - # convention of the whole function to process multiple line to handle it. - # printf( - # boy_this_is_a_really_long_variable_that_cannot_fit_on_the_prev_line); - printf_args = _GetTextInside(line, r'(?i)\b(string)?printf\s*\(') - if printf_args: - match = Match(r'([\w.\->()]+)$', printf_args) - if match and match.group(1) != '__VA_ARGS__': - function_name = re.search(r'\b((?:string)?printf)\s*\(', - line, re.I).group(1) - error(filename, linenum, 'runtime/printf', 4, - 'Potential format string bug. Do %s("%%s", %s) instead.' - % (function_name, match.group(1))) - - # Check for potential memset bugs like memset(buf, sizeof(buf), 0). - match = Search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line) - if match and not Match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)): - error(filename, linenum, 'runtime/memset', 4, - 'Did you mean "memset(%s, 0, %s)"?' - % (match.group(1), match.group(2))) - - if Search(r'\busing namespace\b', line): - error(filename, linenum, 'build/namespaces', 5, - 'Do not use namespace using-directives. ' - 'Use using-declarations instead.') - - # Detect variable-length arrays. - match = Match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line) - if (match and match.group(2) != 'return' and match.group(2) != 'delete' and - match.group(3).find(']') == -1): - # Split the size using space and arithmetic operators as delimiters. - # If any of the resulting tokens are not compile time constants then - # report the error. - tokens = re.split(r'\s|\+|\-|\*|\/|<<|>>]', match.group(3)) - is_const = True - skip_next = False - for tok in tokens: - if skip_next: - skip_next = False - continue - - if Search(r'sizeof\(.+\)', tok): continue - if Search(r'arraysize\(\w+\)', tok): continue - - tok = tok.lstrip('(') - tok = tok.rstrip(')') - if not tok: continue - if Match(r'\d+', tok): continue - if Match(r'0[xX][0-9a-fA-F]+', tok): continue - if Match(r'k[A-Z0-9]\w*', tok): continue - if Match(r'(.+::)?k[A-Z0-9]\w*', tok): continue - if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue - # A catch all for tricky sizeof cases, including 'sizeof expression', - # 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)' - # requires skipping the next token because we split on ' ' and '*'. - if tok.startswith('sizeof'): - skip_next = True - continue - is_const = False - break - if not is_const: - error(filename, linenum, 'runtime/arrays', 1, - 'Do not use variable-length arrays. Use an appropriately named ' - "('k' followed by CamelCase) compile-time constant for the size.") - - # If DISALLOW_EVIL_CONSTRUCTORS, DISALLOW_COPY_AND_ASSIGN, or - # DISALLOW_IMPLICIT_CONSTRUCTORS is present, then it should be the last thing - # in the class declaration. - match = Match( - (r'\s*' - r'(DISALLOW_(EVIL_CONSTRUCTORS|COPY_AND_ASSIGN|IMPLICIT_CONSTRUCTORS))' - r'\(.*\);$'), - line) - if match and linenum + 1 < clean_lines.NumLines(): - next_line = clean_lines.elided[linenum + 1] - # We allow some, but not all, declarations of variables to be present - # in the statement that defines the class. The [\w\*,\s]* fragment of - # the regular expression below allows users to declare instances of - # the class or pointers to instances, but not less common types such - # as function pointers or arrays. It's a tradeoff between allowing - # reasonable code and avoiding trying to parse more C++ using regexps. - if not Search(r'^\s*}[\w\*,\s]*;', next_line): - error(filename, linenum, 'readability/constructors', 3, - match.group(1) + ' should be the last thing in the class') - - # Check for use of unnamed namespaces in header files. Registration - # macros are typically OK, so we allow use of "namespace {" on lines - # that end with backslashes. - if (file_extension == 'h' - and Search(r'\bnamespace\s*{', line) - and line[-1] != '\\'): - error(filename, linenum, 'build/namespaces', 4, - 'Do not use unnamed namespaces in header files. See ' - 'http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces' - ' for more information.') - - -def CheckCStyleCast(filename, linenum, line, raw_line, cast_type, pattern, - error): - """Checks for a C-style cast by looking for the pattern. - - This also handles sizeof(type) warnings, due to similarity of content. - - Args: - filename: The name of the current file. - linenum: The number of the line to check. - line: The line of code to check. - raw_line: The raw line of code to check, with comments. - cast_type: The string for the C++ cast to recommend. This is either - reinterpret_cast, static_cast, or const_cast, depending. - pattern: The regular expression used to find C-style casts. - error: The function to call with any errors found. - - Returns: - True if an error was emitted. - False otherwise. - """ - match = Search(pattern, line) - if not match: - return False - - # e.g., sizeof(int) - sizeof_match = Match(r'.*sizeof\s*$', line[0:match.start(1) - 1]) - if sizeof_match: - error(filename, linenum, 'runtime/sizeof', 1, - 'Using sizeof(type). Use sizeof(varname) instead if possible') - return True - - # operator++(int) and operator--(int) - if (line[0:match.start(1) - 1].endswith(' operator++') or - line[0:match.start(1) - 1].endswith(' operator--')): - return False - - remainder = line[match.end(0):] - - # The close paren is for function pointers as arguments to a function. - # eg, void foo(void (*bar)(int)); - # The semicolon check is a more basic function check; also possibly a - # function pointer typedef. - # eg, void foo(int); or void foo(int) const; - # The equals check is for function pointer assignment. - # eg, void *(*foo)(int) = ... - # The > is for MockCallback<...> ... - # - # Right now, this will only catch cases where there's a single argument, and - # it's unnamed. It should probably be expanded to check for multiple - # arguments with some unnamed. - function_match = Match(r'\s*(\)|=|(const)?\s*(;|\{|throw\(\)|>))', remainder) - if function_match: - if (not function_match.group(3) or - function_match.group(3) == ';' or - ('MockCallback<' not in raw_line and - '/*' not in raw_line)): - error(filename, linenum, 'readability/function', 3, - 'All parameters should be named in a function') - return True - - # At this point, all that should be left is actual casts. - error(filename, linenum, 'readability/casting', 4, - 'Using C-style cast. Use %s<%s>(...) instead' % - (cast_type, match.group(1))) - - return True - - -_HEADERS_CONTAINING_TEMPLATES = ( - ('', ('deque',)), - ('', ('unary_function', 'binary_function', - 'plus', 'minus', 'multiplies', 'divides', 'modulus', - 'negate', - 'equal_to', 'not_equal_to', 'greater', 'less', - 'greater_equal', 'less_equal', - 'logical_and', 'logical_or', 'logical_not', - 'unary_negate', 'not1', 'binary_negate', 'not2', - 'bind1st', 'bind2nd', - 'pointer_to_unary_function', - 'pointer_to_binary_function', - 'ptr_fun', - 'mem_fun_t', 'mem_fun', 'mem_fun1_t', 'mem_fun1_ref_t', - 'mem_fun_ref_t', - 'const_mem_fun_t', 'const_mem_fun1_t', - 'const_mem_fun_ref_t', 'const_mem_fun1_ref_t', - 'mem_fun_ref', - )), - ('', ('numeric_limits',)), - ('', ('list',)), - ('', ('map', 'multimap',)), - ('', ('allocator',)), - ('', ('queue', 'priority_queue',)), - ('', ('set', 'multiset',)), - ('', ('stack',)), - ('', ('char_traits', 'basic_string',)), - ('', ('pair',)), - ('', ('vector',)), - - # gcc extensions. - # Note: std::hash is their hash, ::hash is our hash - ('', ('hash_map', 'hash_multimap',)), - ('', ('hash_set', 'hash_multiset',)), - ('', ('slist',)), - ) - -_RE_PATTERN_STRING = re.compile(r'\bstring\b') - -_re_pattern_algorithm_header = [] -for _template in ('copy', 'max', 'min', 'min_element', 'sort', 'swap', - 'transform'): - # Match max(..., ...), max(..., ...), but not foo->max, foo.max or - # type::max(). - _re_pattern_algorithm_header.append( - (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'), - _template, - '')) - -_re_pattern_templates = [] -for _header, _templates in _HEADERS_CONTAINING_TEMPLATES: - for _template in _templates: - _re_pattern_templates.append( - (re.compile(r'(\<|\b)' + _template + r'\s*\<'), - _template + '<>', - _header)) - - -def FilesBelongToSameModule(filename_cc, filename_h): - """Check if these two filenames belong to the same module. - - The concept of a 'module' here is a as follows: - foo.h, foo-inl.h, foo.cc, foo_test.cc and foo_unittest.cc belong to the - same 'module' if they are in the same directory. - some/path/public/xyzzy and some/path/internal/xyzzy are also considered - to belong to the same module here. - - If the filename_cc contains a longer path than the filename_h, for example, - '/absolute/path/to/base/sysinfo.cc', and this file would include - 'base/sysinfo.h', this function also produces the prefix needed to open the - header. This is used by the caller of this function to more robustly open the - header file. We don't have access to the real include paths in this context, - so we need this guesswork here. - - Known bugs: tools/base/bar.cc and base/bar.h belong to the same module - according to this implementation. Because of this, this function gives - some false positives. This should be sufficiently rare in practice. - - Args: - filename_cc: is the path for the .cc file - filename_h: is the path for the header path - - Returns: - Tuple with a bool and a string: - bool: True if filename_cc and filename_h belong to the same module. - string: the additional prefix needed to open the header file. - """ - - if not filename_cc.endswith('.cc'): - return (False, '') - filename_cc = filename_cc[:-len('.cc')] - if filename_cc.endswith('_unittest'): - filename_cc = filename_cc[:-len('_unittest')] - elif filename_cc.endswith('_test'): - filename_cc = filename_cc[:-len('_test')] - filename_cc = filename_cc.replace('/public/', '/') - filename_cc = filename_cc.replace('/internal/', '/') - - if not filename_h.endswith('.h'): - return (False, '') - filename_h = filename_h[:-len('.h')] - if filename_h.endswith('-inl'): - filename_h = filename_h[:-len('-inl')] - filename_h = filename_h.replace('/public/', '/') - filename_h = filename_h.replace('/internal/', '/') - - files_belong_to_same_module = filename_cc.endswith(filename_h) - common_path = '' - if files_belong_to_same_module: - common_path = filename_cc[:-len(filename_h)] - return files_belong_to_same_module, common_path - - -def UpdateIncludeState(filename, include_state, io=codecs): - """Fill up the include_state with new includes found from the file. - - Args: - filename: the name of the header to read. - include_state: an _IncludeState instance in which the headers are inserted. - io: The io factory to use to read the file. Provided for testability. - - Returns: - True if a header was succesfully added. False otherwise. - """ - headerfile = None - try: - headerfile = io.open(filename, 'r', 'utf8', 'replace') - except IOError: - return False - linenum = 0 - for line in headerfile: - linenum += 1 - clean_line = CleanseComments(line) - match = _RE_PATTERN_INCLUDE.search(clean_line) - if match: - include = match.group(2) - # The value formatting is cute, but not really used right now. - # What matters here is that the key is in include_state. - include_state.setdefault(include, '%s:%d' % (filename, linenum)) - return True - - -def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, - io=codecs): - """Reports for missing stl includes. - - This function will output warnings to make sure you are including the headers - necessary for the stl containers and functions that you use. We only give one - reason to include a header. For example, if you use both equal_to<> and - less<> in a .h file, only one (the latter in the file) of these will be - reported as a reason to include the . - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - include_state: An _IncludeState instance. - error: The function to call with any errors found. - io: The IO factory to use to read the header file. Provided for unittest - injection. - """ - required = {} # A map of header name to linenumber and the template entity. - # Example of required: { '': (1219, 'less<>') } - - for linenum in xrange(clean_lines.NumLines()): - line = clean_lines.elided[linenum] - if not line or line[0] == '#': - continue - - # String is special -- it is a non-templatized type in STL. - matched = _RE_PATTERN_STRING.search(line) - if matched: - # Don't warn about strings in non-STL namespaces: - # (We check only the first match per line; good enough.) - prefix = line[:matched.start()] - if prefix.endswith('std::') or not prefix.endswith('::'): - required[''] = (linenum, 'string') - - for pattern, template, header in _re_pattern_algorithm_header: - if pattern.search(line): - required[header] = (linenum, template) - - # The following function is just a speed up, no semantics are changed. - if not '<' in line: # Reduces the cpu time usage by skipping lines. - continue - - for pattern, template, header in _re_pattern_templates: - if pattern.search(line): - required[header] = (linenum, template) - - # The policy is that if you #include something in foo.h you don't need to - # include it again in foo.cc. Here, we will look at possible includes. - # Let's copy the include_state so it is only messed up within this function. - include_state = include_state.copy() - - # Did we find the header for this file (if any) and succesfully load it? - header_found = False - - # Use the absolute path so that matching works properly. - abs_filename = FileInfo(filename).FullName() - - # For Emacs's flymake. - # If cpplint is invoked from Emacs's flymake, a temporary file is generated - # by flymake and that file name might end with '_flymake.cc'. In that case, - # restore original file name here so that the corresponding header file can be - # found. - # e.g. If the file name is 'foo_flymake.cc', we should search for 'foo.h' - # instead of 'foo_flymake.h' - abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename) - - # include_state is modified during iteration, so we iterate over a copy of - # the keys. - header_keys = include_state.keys() - for header in header_keys: - (same_module, common_path) = FilesBelongToSameModule(abs_filename, header) - fullpath = common_path + header - if same_module and UpdateIncludeState(fullpath, include_state, io): - header_found = True - - # If we can't find the header file for a .cc, assume it's because we don't - # know where to look. In that case we'll give up as we're not sure they - # didn't include it in the .h file. - # TODO(unknown): Do a better job of finding .h files so we are confident that - # not having the .h file means there isn't one. - if filename.endswith('.cc') and not header_found: - return - - # All the lines have been processed, report the errors found. - for required_header_unstripped in required: - template = required[required_header_unstripped][1] - if required_header_unstripped.strip('<>"') not in include_state: - error(filename, required[required_header_unstripped][0], - 'build/include_what_you_use', 4, - 'Add #include ' + required_header_unstripped + ' for ' + template) - - -_RE_PATTERN_EXPLICIT_MAKEPAIR = re.compile(r'\bmake_pair\s*<') - - -def CheckMakePairUsesDeduction(filename, clean_lines, linenum, error): - """Check that make_pair's template arguments are deduced. - - G++ 4.6 in C++0x mode fails badly if make_pair's template arguments are - specified explicitly, and such use isn't intended in any case. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - raw = clean_lines.raw_lines - line = raw[linenum] - match = _RE_PATTERN_EXPLICIT_MAKEPAIR.search(line) - if match: - error(filename, linenum, 'build/explicit_make_pair', - 4, # 4 = high confidence - 'For C++11-compatibility, omit template arguments from make_pair' - ' OR use pair directly OR if appropriate, construct a pair directly') - - -def ProcessLine(filename, file_extension, clean_lines, line, - include_state, function_state, nesting_state, error, - extra_check_functions=[]): - """Processes a single line in the file. - - Args: - filename: Filename of the file that is being processed. - file_extension: The extension (dot not included) of the file. - clean_lines: An array of strings, each representing a line of the file, - with comments stripped. - line: Number of line being processed. - include_state: An _IncludeState instance in which the headers are inserted. - function_state: A _FunctionState instance which counts function lines, etc. - nesting_state: A _NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: A callable to which errors are reported, which takes 4 arguments: - filename, line number, error level, and message - extra_check_functions: An array of additional check functions that will be - run on each source line. Each function takes 4 - arguments: filename, clean_lines, line, error - """ - raw_lines = clean_lines.raw_lines - ParseNolintSuppressions(filename, raw_lines[line], line, error) - nesting_state.Update(filename, clean_lines, line, error) - if nesting_state.stack and nesting_state.stack[-1].inline_asm != _NO_ASM: - return - CheckForFunctionLengths(filename, clean_lines, line, function_state, error) - CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error) - CheckStyle(filename, clean_lines, line, file_extension, nesting_state, error) - CheckLanguage(filename, clean_lines, line, file_extension, include_state, - error) - CheckForNonStandardConstructs(filename, clean_lines, line, - nesting_state, error) - CheckPosixThreading(filename, clean_lines, line, error) - CheckInvalidIncrement(filename, clean_lines, line, error) - CheckMakePairUsesDeduction(filename, clean_lines, line, error) - for check_fn in extra_check_functions: - check_fn(filename, clean_lines, line, error) - -def ProcessFileData(filename, file_extension, lines, error, - extra_check_functions=[]): - """Performs lint checks and reports any errors to the given error function. - - Args: - filename: Filename of the file that is being processed. - file_extension: The extension (dot not included) of the file. - lines: An array of strings, each representing a line of the file, with the - last element being empty if the file is terminated with a newline. - error: A callable to which errors are reported, which takes 4 arguments: - filename, line number, error level, and message - extra_check_functions: An array of additional check functions that will be - run on each source line. Each function takes 4 - arguments: filename, clean_lines, line, error - """ - lines = (['// marker so line numbers and indices both start at 1'] + lines + - ['// marker so line numbers end in a known way']) - - include_state = _IncludeState() - function_state = _FunctionState() - nesting_state = _NestingState() - - ResetNolintSuppressions() - - CheckForCopyright(filename, lines, error) - - if file_extension == 'h': - CheckForHeaderGuard(filename, lines, error) - - RemoveMultiLineComments(filename, lines, error) - clean_lines = CleansedLines(lines) - for line in xrange(clean_lines.NumLines()): - ProcessLine(filename, file_extension, clean_lines, line, - include_state, function_state, nesting_state, error, - extra_check_functions) - nesting_state.CheckClassFinished(filename, error) - - CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error) - - # We check here rather than inside ProcessLine so that we see raw - # lines rather than "cleaned" lines. - CheckForUnicodeReplacementCharacters(filename, lines, error) - - CheckForNewlineAtEOF(filename, lines, error) - -def ProcessFile(filename, vlevel, extra_check_functions=[]): - """Does google-lint on a single file. - - Args: - filename: The name of the file to parse. - - vlevel: The level of errors to report. Every error of confidence - >= verbose_level will be reported. 0 is a good default. - - extra_check_functions: An array of additional check functions that will be - run on each source line. Each function takes 4 - arguments: filename, clean_lines, line, error - """ - - _SetVerboseLevel(vlevel) -# BEGIN android-added - old_errors = _cpplint_state.error_count -# END android-added - - try: - # Support the UNIX convention of using "-" for stdin. Note that - # we are not opening the file with universal newline support - # (which codecs doesn't support anyway), so the resulting lines do - # contain trailing '\r' characters if we are reading a file that - # has CRLF endings. - # If after the split a trailing '\r' is present, it is removed - # below. If it is not expected to be present (i.e. os.linesep != - # '\r\n' as in Windows), a warning is issued below if this file - # is processed. - - if filename == '-': - lines = codecs.StreamReaderWriter(sys.stdin, - codecs.getreader('utf8'), - codecs.getwriter('utf8'), - 'replace').read().split('\n') - else: - lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n') - - carriage_return_found = False - # Remove trailing '\r'. - for linenum in range(len(lines)): - if lines[linenum].endswith('\r'): - lines[linenum] = lines[linenum].rstrip('\r') - carriage_return_found = True - - except IOError: - sys.stderr.write( - "Skipping input '%s': Can't open for reading\n" % filename) - return - - # Note, if no dot is found, this will give the entire filename as the ext. - file_extension = filename[filename.rfind('.') + 1:] - - # When reading from stdin, the extension is unknown, so no cpplint tests - # should rely on the extension. - if (filename != '-' and file_extension != 'cc' and file_extension != 'h' - and file_extension != 'cpp'): - sys.stderr.write('Ignoring %s; not a .cc or .h file\n' % filename) - else: - ProcessFileData(filename, file_extension, lines, Error, - extra_check_functions) - if carriage_return_found and os.linesep != '\r\n': - # Use 0 for linenum since outputting only one error for potentially - # several lines. - Error(filename, 0, 'whitespace/newline', 1, - 'One or more unexpected \\r (^M) found;' - 'better to use only a \\n') - -# BEGIN android-changed - # sys.stderr.write('Done processing %s\n' % filename) - if not _cpplint_state.quiet or old_errors != _cpplint_state.error_count: - sys.stderr.write('Done processing %s\n' % filename) -# END android-changed - -def PrintUsage(message): - """Prints a brief usage string and exits, optionally with an error message. - - Args: - message: The optional error message. - """ - sys.stderr.write(_USAGE) - if message: - sys.exit('\nFATAL ERROR: ' + message) - else: - sys.exit(1) - - -def PrintCategories(): - """Prints a list of all the error-categories used by error messages. - - These are the categories used to filter messages via --filter. - """ - sys.stderr.write(''.join(' %s\n' % cat for cat in _ERROR_CATEGORIES)) - sys.exit(0) - - -def ParseArguments(args): - """Parses the command line arguments. - - This may set the output format and verbosity level as side-effects. - - Args: - args: The command line arguments: - - Returns: - The list of filenames to lint. - """ - try: - (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=', - 'stdout', # TODO(enh): added --stdout - # BEGIN android-added - 'quiet', - # END android-added - 'counting=', - 'filter=', - 'root=']) - except getopt.GetoptError: - PrintUsage('Invalid arguments.') - - verbosity = _VerboseLevel() - output_format = _OutputFormat() - output_stream = sys.stderr # TODO(enh): added --stdout - filters = '' - # BEGIN android-added - quiet = _Quiet() - # END android-added - counting_style = '' - - for (opt, val) in opts: - if opt == '--help': - PrintUsage(None) - elif opt == '--stdout': # TODO(enh): added --stdout - output_stream = sys.stdout # TODO(enh): added --stdout - # BEGIN android-added - elif opt == '--quiet': - quiet = True - # END android-added - elif opt == '--output': - if not val in ('emacs', 'vs7', 'eclipse'): - PrintUsage('The only allowed output formats are emacs, vs7 and eclipse.') - output_format = val - elif opt == '--verbose': - verbosity = int(val) - elif opt == '--filter': - filters = val - if not filters: - PrintCategories() - elif opt == '--counting': - if val not in ('total', 'toplevel', 'detailed'): - PrintUsage('Valid counting options are total, toplevel, and detailed') - counting_style = val - elif opt == '--root': - global _root - _root = val - - if not filenames: - PrintUsage('No files were specified.') - - _SetOutputFormat(output_format) - _SetVerboseLevel(verbosity) - _SetFilters(filters) - _SetCountingStyle(counting_style) - # BEGIN android-added - _SetQuiet(quiet) - # END android-added - sys.stderr = output_stream # TODO(enh): added --stdout - - return filenames - - -def main(): - filenames = ParseArguments(sys.argv[1:]) - - # Change stderr to write with replacement characters so we don't die - # if we try to print something containing non-ASCII characters. - sys.stderr = codecs.StreamReaderWriter(sys.stderr, - codecs.getreader('utf8'), - codecs.getwriter('utf8'), - 'replace') - - _cpplint_state.ResetErrorCounts() - for filename in filenames: - ProcessFile(filename, _cpplint_state.verbose_level) - # BEGIN android-changed - # _cpplint_state.PrintErrorCounts() - if not _cpplint_state.quiet or _cpplint_state.error_count > 0: - _cpplint_state.PrintErrorCounts() - # END android-changed - - sys.exit(_cpplint_state.error_count > 0) - - -if __name__ == '__main__': - main() diff --git a/tools/cpplint_presubmit.py b/tools/cpplint_presubmit.py deleted file mode 100755 index b42a6913dc9a21974d83581b72d836fd3f6b8769..0000000000000000000000000000000000000000 --- a/tools/cpplint_presubmit.py +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/python3 -# -# Copyright 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. - -# TODO We should unify this with build/Android.cpplint.mk. - -import os -import pathlib -import subprocess -import sys - -IGNORED_FILES = {"runtime/elf.h", "openjdkjvmti/include/jvmti.h"} - -INTERESTING_SUFFIXES = {".h", ".cc"} - -CPPLINT_FLAGS = [ - '--filter=-whitespace/line_length,-build/include,-readability/function,-readability/streams,-readability/todo,-runtime/references,-runtime/sizeof,-runtime/threadsafe_fn,-runtime/printf', - '--quiet', -] - -def is_interesting(f): - """ - Returns true if this is a file we want to run through cpplint before uploading. False otherwise. - """ - path = pathlib.Path(f) - return f not in IGNORED_FILES and path.suffix in INTERESTING_SUFFIXES and path.exists() - -def get_changed_files(commit): - """ - Gets the files changed in the given commit. - """ - return subprocess.check_output( - ["git", 'diff-tree', '--no-commit-id', '--name-only', '-r', commit], - stderr=subprocess.STDOUT, - universal_newlines=True).split() - -def run_cpplint(files): - """ - Runs cpplint on the given files. - """ - if len(files) == 0: - return - sys.exit(subprocess.call(['tools/cpplint.py'] + CPPLINT_FLAGS + files)) - -def main(): - if 'PREUPLOAD_COMMIT' in os.environ: - commit = os.environ['PREUPLOAD_COMMIT'] - else: - print("WARNING: Not running as a pre-upload hook. Assuming commit to check = 'HEAD'") - commit = "HEAD" - files_to_check = [f for f in get_changed_files(commit) if is_interesting(f)] - run_cpplint(files_to_check) - -if __name__ == '__main__': - main() diff --git a/tools/dt_fds_forward.py b/tools/dt_fds_forward.py new file mode 100755 index 0000000000000000000000000000000000000000..516b7fef965e99df0cd84361c490f60aa9bd3b42 --- /dev/null +++ b/tools/dt_fds_forward.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python3 +# +# Copyright 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. + +""" +A python program that simulates the plugin side of the dt_fd_forward transport for testing. + +This program will invoke a given java language runtime program and send down debugging arguments +that cause it to use the dt_fd_forward transport. This will then create a normal server-port that +debuggers can attach to. +""" + +import argparse +import array +from multiprocessing import Process +import contextlib +import ctypes +import os +import select +import socket +import subprocess +import sys +import time + +LISTEN_START_MESSAGE = b"dt_fd_forward:START-LISTEN\x00" +LISTEN_END_MESSAGE = b"dt_fd_forward:END-LISTEN\x00" +ACCEPTED_MESSAGE = b"dt_fd_forward:ACCEPTED\x00" +CLOSE_MESSAGE = b"dt_fd_forward:CLOSING\x00" + +libc = ctypes.cdll.LoadLibrary("libc.so.6") +def eventfd(init_val, flags): + """ + Creates an eventfd. See 'man 2 eventfd' for more information. + """ + return libc.eventfd(init_val, flags) + +@contextlib.contextmanager +def make_eventfd(init): + """ + Creates an eventfd with given initial value that is closed after the manager finishes. + """ + fd = eventfd(init, 0) + yield fd + os.close(fd) + +@contextlib.contextmanager +def make_sockets(): + """ + Make a (remote,local) socket pair. The remote socket is inheritable by forked processes. They are + both linked together. + """ + (rfd, lfd) = socket.socketpair(socket.AF_UNIX, socket.SOCK_SEQPACKET) + yield (rfd, lfd) + rfd.close() + lfd.close() + +def send_fds(sock, remote_read, remote_write, remote_event): + """ + Send the three fds over the given socket. + """ + sock.sendmsg([b"!"], # We don't actually care what we send here. + [(socket.SOL_SOCKET, # Send over socket. + socket.SCM_RIGHTS, # Payload is file-descriptor array + array.array('i', [remote_read, remote_write, remote_event]))]) + + +def HandleSockets(host, port, local_sock, finish_event): + """ + Handle the IO between the network and the runtime. + + This is similar to what we will do with the plugin that controls the jdwp connection. + + The main difference is it will keep around the connection and event-fd in order to let it send + ddms packets directly. + """ + listening = False + with socket.socket() as sock: + sock.bind((host, port)) + sock.listen() + while True: + sources = [local_sock, finish_event, sock] + print("Starting select on " + str(sources)) + (rf, _, _) = select.select(sources, [], []) + if local_sock in rf: + buf = local_sock.recv(1024) + print("Local_sock has data: " + str(buf)) + if buf == LISTEN_START_MESSAGE: + print("listening on " + str(sock)) + listening = True + elif buf == LISTEN_END_MESSAGE: + print("End listening") + listening = False + elif buf == ACCEPTED_MESSAGE: + print("Fds were accepted.") + elif buf == CLOSE_MESSAGE: + # TODO Dup the fds and send a fake DDMS message like the actual plugin would. + print("Fds were closed") + else: + print("Unknown data received from socket " + str(buf)) + return + elif sock in rf: + (conn, addr) = sock.accept() + with conn: + print("connection accepted from " + str(addr)) + if listening: + with make_eventfd(1) as efd: + print("sending fds ({}, {}, {}) to target.".format(conn.fileno(), conn.fileno(), efd)) + send_fds(local_sock, conn.fileno(), conn.fileno(), efd) + else: + print("Closing fds since we cannot accept them.") + if finish_event in rf: + print("woke up from finish_event") + return + +def StartChildProcess(cmd_pre, cmd_post, jdwp_lib, jdwp_ops, remote_sock, can_be_runtest): + """ + Open the child java-language runtime process. + """ + full_cmd = list(cmd_pre) + os.set_inheritable(remote_sock.fileno(), True) + jdwp_arg = jdwp_lib + "=" + \ + jdwp_ops + "transport=dt_fd_forward,address=" + str(remote_sock.fileno()) + if can_be_runtest and cmd_pre[0].endswith("run-test"): + print("Assuming run-test. Pass --no-run-test if this isn't true") + full_cmd += ["--with-agent", jdwp_arg] + else: + full_cmd.append("-agentpath:" + jdwp_arg) + full_cmd += cmd_post + print("Running " + str(full_cmd)) + # Start the actual process with the fd being passed down. + proc = subprocess.Popen(full_cmd, close_fds=False) + # Get rid of the extra socket. + remote_sock.close() + proc.wait() + +def main(): + parser = argparse.ArgumentParser(description=""" + Runs a socket that forwards to dt_fds. + + Pass '--' to start passing in the program we will pass the debug + options down to. + """) + parser.add_argument("--host", type=str, default="localhost", + help="Host we will listen for traffic on. Defaults to 'localhost'.") + parser.add_argument("--debug-lib", type=str, default="libjdwp.so", + help="jdwp library we pass to -agentpath:. Default is 'libjdwp.so'") + parser.add_argument("--debug-options", type=str, default="server=y,suspend=y,", + help="non-address options we pass to jdwp agent, default is " + + "'server=y,suspend=y,'") + parser.add_argument("--port", type=int, default=12345, + help="port we will expose the traffic on. Defaults to 12345.") + parser.add_argument("--no-run-test", default=False, action="store_true", + help="don't pass in arguments for run-test even if it looks like that is " + + "the program") + parser.add_argument("--pre-end", type=int, default=1, + help="number of 'rest' arguments to put before passing in the debug options") + end_idx = 0 if '--' not in sys.argv else sys.argv.index('--') + if end_idx == 0 and ('--help' in sys.argv or '-h' in sys.argv): + parser.print_help() + return + args = parser.parse_args(sys.argv[:end_idx][1:]) + rest = sys.argv[1 + end_idx:] + + with make_eventfd(0) as wakeup_event: + with make_sockets() as (remote_sock, local_sock): + invoker = Process(target=StartChildProcess, + args=(rest[:args.pre_end], + rest[args.pre_end:], + args.debug_lib, + args.debug_options, + remote_sock, + not args.no_run_test)) + socket_handler = Process(target=HandleSockets, + args=(args.host, args.port, local_sock, wakeup_event)) + socket_handler.start() + invoker.start() + invoker.join() + # Write any 64 bit value to the wakeup_event to make sure that the socket handler will wake + # up and exit. + os.write(wakeup_event, b'\x00\x00\x00\x00\x00\x00\x01\x00') + socket_handler.join() + +if __name__ == '__main__': + main() diff --git a/tools/libjdwp_oj_art_failures.txt b/tools/external_oj_libjdwp_art_failures.txt similarity index 84% rename from tools/libjdwp_oj_art_failures.txt rename to tools/external_oj_libjdwp_art_failures.txt index e0f243ccfbe11cb2e903fe512dc4fd6fa386b315..1178af48526009a964b58b32b29836419caaec2a 100644 --- a/tools/libjdwp_oj_art_failures.txt +++ b/tools/external_oj_libjdwp_art_failures.txt @@ -1,6 +1,9 @@ /* * This file contains expectations for ART's buildbot. The purpose of this file is * to temporarily list failing tests and not break the bots. + * + * This file contains the expectations for the 'libjdwp-aot' and 'libjdwp-jit' + * test groups on the chromium buildbot. */ [ { @@ -61,5 +64,24 @@ "org.apache.harmony.jpda.tests.jdwp.ThreadGroupReference.NameTest#testName001_NullObject", "org.apache.harmony.jpda.tests.jdwp.ThreadGroupReference.ParentTest#testParent_NullObject", "org.apache.harmony.jpda.tests.jdwp.VirtualMachine.CapabilitiesNewTest#testCapabilitiesNew001" ] +}, +{ + description: "Test is flaky", + result: EXEC_FAILED, + bug: 69121056, + name: "org.apache.harmony.jpda.tests.jdwp.ObjectReference.IsCollectedTest#testIsCollected001" +}, +{ + description: "Test crashes", + result: EXEC_FAILED, + bug: 69591477, + name: "org.apache.harmony.jpda.tests.jdwp.VirtualMachine.ExitTest#testExit001" +}, +{ + description: "Test times out on fugu-debug", + result: EXEC_FAILED, + bug: 70459916, + names: [ "org.apache.harmony.jpda.tests.jdwp.VMDebug.VMDebugTest#testVMDebug", + "org.apache.harmony.jpda.tests.jdwp.VMDebug.VMDebugTest002#testVMDebug" ] } ] diff --git a/tools/jfuzz/README.md b/tools/jfuzz/README.md index 10d175b83ddb573d1a0122d245e693974eecddef..eb0e71f53d34fdd32260c9830e459b3af9278db3 100644 --- a/tools/jfuzz/README.md +++ b/tools/jfuzz/README.md @@ -28,6 +28,8 @@ where (higher values yield deeper nested conditionals) -n : defines a fuzzing nest for for/while/do-while loops (higher values yield deeper nested loops) + -t : defines a fuzzing nest for try-catch-finally blocks + (higher values yield deeper nested try-catch-finally blocks) -v : prints version number and exits -h : prints help and exits @@ -48,7 +50,8 @@ How to start JFuzz testing [--report_script=SCRIPT] [--jfuzz_arg=ARG] [--true_divergence] - [--use_dx] + [--dexer=DEXER] + [--debug_info] where @@ -64,7 +67,8 @@ where --report_script : path to script called for each divergence --jfuzz_arg : argument for jfuzz --true_divergence : don't bisect timeout divergences - --use_dx : use dx (rather than jack) + --dexer=DEXER : use either dx, d8, or jack to obtain dex files + --debug_info : include debugging info How to start JFuzz nightly testing ================================== @@ -85,14 +89,16 @@ How to start J/DexFuzz testing (multi-layered) [--num_tests=NUM_TESTS] [--num_inputs=NUM_INPUTS] [--device=DEVICE] - [--use_dx] + [--dexer=DEXER] + [--debug_info] where - --num_tests : number of tests to run (10000 by default) - --num_inputs: number of JFuzz programs to generate - --device : target device serial number (passed to adb -s) - --use_dx : use dx (rather than jack) + --num_tests : number of tests to run (10000 by default) + --num_inputs : number of JFuzz programs to generate + --device : target device serial number (passed to adb -s) + --dexer=DEXER : use either dx, d8, or jack to obtain dex files + --debug_info : include debugging info Background ========== diff --git a/tools/jfuzz/jfuzz.cc b/tools/jfuzz/jfuzz.cc index 7990c6cf8e4425b5b11c080b63a2536108bc4908..60c62752ef4f71b3503435d4e2ece55a1e834606 100644 --- a/tools/jfuzz/jfuzz.cc +++ b/tools/jfuzz/jfuzz.cc @@ -31,8 +31,6 @@ namespace { * Operators. */ -#define EMIT(x) fputs((x)[random0(sizeof(x)/sizeof(const char*))], out_); - static constexpr const char* kIncDecOps[] = { "++", "--" }; static constexpr const char* kIntUnaryOps[] = { "+", "-", "~" }; static constexpr const char* kFpUnaryOps[] = { "+", "-" }; @@ -50,12 +48,22 @@ static constexpr const char* kFpAssignOps[] = { "=", "+=", "-=", "*=", "/=" }; static constexpr const char* kBoolRelOps[] = { "==", "!=" }; static constexpr const char* kRelOps[] = { "==", "!=", ">", ">=", "<", "<=" }; +/* + * Exceptions. + */ +static const char* kExceptionTypes[] = { + "IllegalStateException", + "NullPointerException", + "IllegalArgumentException", + "ArrayIndexOutOfBoundsException" +}; + /* * Version of JFuzz. Increase this each time changes are made to the program * to preserve the property that a given version of JFuzz yields the same * fuzzed program for a deterministic random seed. */ -const char* VERSION = "1.4"; +const char* VERSION = "1.5"; /* * Maximum number of array dimensions, together with corresponding maximum size @@ -64,6 +72,14 @@ const char* VERSION = "1.4"; static const uint32_t kMaxDim = 10; static const uint32_t kMaxDimSize[kMaxDim + 1] = { 0, 1000, 32, 10, 6, 4, 3, 3, 2, 2, 2 }; +/* + * Utility function to return the number of elements in an array. + */ +template +constexpr uint32_t countof(T const (&)[N]) { + return N; +} + /** * A class that generates a random program that compiles correctly. The program * is generated using rules that generate various programming constructs. Each rule @@ -78,7 +94,8 @@ class JFuzz { uint32_t expr_depth, uint32_t stmt_length, uint32_t if_nest, - uint32_t loop_nest) + uint32_t loop_nest, + uint32_t try_nest) : out_(out), fuzz_random_engine_(seed), fuzz_seed_(seed), @@ -86,6 +103,7 @@ class JFuzz { fuzz_stmt_length_(stmt_length), fuzz_if_nest_(if_nest), fuzz_loop_nest_(loop_nest), + fuzz_try_nest_(try_nest), return_type_(randomType()), array_type_(randomType()), array_dim_(random1(kMaxDim)), @@ -97,6 +115,7 @@ class JFuzz { loop_nest_(0), switch_nest_(0), do_nest_(0), + try_nest_(0), boolean_local_(0), int_local_(0), long_local_(0), @@ -168,6 +187,12 @@ class JFuzz { } } + // Emits a random strong selected from an array of operator strings. + template + inline void emitOneOf(const char* const (&ops)[N]) { + fputs(ops[random0(N)], out_); + } + // // Expressions. // @@ -177,9 +202,9 @@ class JFuzz { if (tp == kBoolean) { fputc('!', out_); } else if (isInteger(tp)) { - EMIT(kIntUnaryOps); + emitOneOf(kIntUnaryOps); } else { // isFP(tp) - EMIT(kFpUnaryOps); + emitOneOf(kFpUnaryOps); } } @@ -188,38 +213,38 @@ class JFuzz { if (tp == kBoolean) { // Not applicable, just leave "as is". } else { // isInteger(tp) || isFP(tp) - EMIT(kIncDecOps); + emitOneOf(kIncDecOps); } } // Emit a binary operator (same type in-out). void emitBinaryOp(Type tp) { if (tp == kBoolean) { - EMIT(kBoolBinOps); + emitOneOf(kBoolBinOps); } else if (isInteger(tp)) { - EMIT(kIntBinOps); + emitOneOf(kIntBinOps); } else { // isFP(tp) - EMIT(kFpBinOps); + emitOneOf(kFpBinOps); } } // Emit an assignment operator (same type in-out). void emitAssignmentOp(Type tp) { if (tp == kBoolean) { - EMIT(kBoolAssignOps); + emitOneOf(kBoolAssignOps); } else if (isInteger(tp)) { - EMIT(kIntAssignOps); + emitOneOf(kIntAssignOps); } else { // isFP(tp) - EMIT(kFpAssignOps); + emitOneOf(kFpAssignOps); } } // Emit a relational operator (one type in, boolean out). void emitRelationalOp(Type tp) { if (tp == kBoolean) { - EMIT(kBoolRelOps); + emitOneOf(kBoolRelOps); } else { // isInteger(tp) || isFP(tp) - EMIT(kRelOps); + emitOneOf(kRelOps); } } @@ -808,7 +833,7 @@ class JFuzz { fputs("{\n", out_); indentation_ += 2; emitIndentation(); - fprintf(out_, "int i%u = %d;", loop_nest_, isWhile ? -1 : 0); + fprintf(out_, "int i%u = %d;\n", loop_nest_, isWhile ? -1 : 0); emitIndentation(); if (isWhile) { fprintf(out_, "while (++i%u < ", loop_nest_); @@ -871,6 +896,73 @@ class JFuzz { return mayFollowTrue || mayFollowFalse; } + bool emitTry() { + fputs("try {\n", out_); + indentation_ += 2; + bool mayFollow = emitStatementList(); + indentation_ -= 2; + emitIndentation(); + fputc('}', out_); + return mayFollow; + } + + bool emitCatch() { + uint32_t count = random1(countof(kExceptionTypes)); + bool mayFollow = false; + for (uint32_t i = 0; i < count; ++i) { + fprintf(out_, " catch (%s ex%u_%u) {\n", kExceptionTypes[i], try_nest_, i); + indentation_ += 2; + mayFollow |= emitStatementList(); + indentation_ -= 2; + emitIndentation(); + fputc('}', out_); + } + return mayFollow; + } + + bool emitFinally() { + fputs(" finally {\n", out_); + indentation_ += 2; + bool mayFollow = emitStatementList(); + indentation_ -= 2; + emitIndentation(); + fputc('}', out_); + return mayFollow; + } + + // Emit a try-catch-finally block. + bool emitTryCatchFinally() { + // Apply a hard limit on the number of catch blocks. This is for + // javac which fails if blocks within try-catch-finally are too + // large (much less than you'd expect). + if (try_nest_ > fuzz_try_nest_) { + return emitAssignment(); // fall back + } + + ++try_nest_; // Entering try-catch-finally + + bool mayFollow = emitTry(); + switch (random0(3)) { + case 0: // try..catch + mayFollow |= emitCatch(); + break; + case 1: // try..finally + mayFollow &= emitFinally(); + break; + case 2: // try..catch..finally + // When determining whether code may follow, we observe that a + // finally block always follows after try and catch + // block. Code may only follow if the finally block permits + // and either the try or catch block allows code to follow. + mayFollow = (mayFollow | emitCatch()) & emitFinally(); + break; + } + fputc('\n', out_); + + --try_nest_; // Leaving try-catch-finally + return mayFollow; + } + // Emit a switch statement. bool emitSwitch() { // Continuing if nest becomes less likely as the depth grows. @@ -915,6 +1007,11 @@ class JFuzz { return mayFollow; } + bool emitNopCall() { + fputs("nop();\n", out_); + return true; + } + // Emit an assignment statement. bool emitAssignment() { Type tp = randomType(); @@ -930,16 +1027,18 @@ class JFuzz { // Emit a single statement. Returns true if statements may follow. bool emitStatement() { switch (random1(16)) { // favor assignments - case 1: return emitReturn(false); break; - case 2: return emitContinue(); break; - case 3: return emitBreak(); break; - case 4: return emitScope(); break; - case 5: return emitArrayInit(); break; - case 6: return emitForLoop(); break; - case 7: return emitDoLoop(); break; - case 8: return emitIfStmt(); break; - case 9: return emitSwitch(); break; - default: return emitAssignment(); break; + case 1: return emitReturn(false); break; + case 2: return emitContinue(); break; + case 3: return emitBreak(); break; + case 4: return emitScope(); break; + case 5: return emitArrayInit(); break; + case 6: return emitForLoop(); break; + case 7: return emitDoLoop(); break; + case 8: return emitIfStmt(); break; + case 9: return emitSwitch(); break; + case 10: return emitTryCatchFinally(); break; + case 11: return emitNopCall(); break; + default: return emitAssignment(); break; } } @@ -1109,6 +1208,11 @@ class JFuzz { fputs(" }\n", out_); } + // Emit a static void method. + void emitStaticNopMethod() { + fputs(" public static void nop() {}\n\n", out_); + } + // Emit program header. Emit command line options in the comments. void emitHeader() { fputs("\n/**\n * AOSP JFuzz Tester.\n", out_); @@ -1133,6 +1237,7 @@ class JFuzz { emitArrayDecl(); emitTestConstructor(); emitTestMethod(); + emitStaticNopMethod(); emitMainMethod(); indentation_ -= 2; fputs("}\n\n", out_); @@ -1167,6 +1272,7 @@ class JFuzz { const uint32_t fuzz_stmt_length_; const uint32_t fuzz_if_nest_; const uint32_t fuzz_loop_nest_; + const uint32_t fuzz_try_nest_; // Return and array setup. const Type return_type_; @@ -1182,6 +1288,7 @@ class JFuzz { uint32_t loop_nest_; uint32_t switch_nest_; uint32_t do_nest_; + uint32_t try_nest_; uint32_t boolean_local_; uint32_t int_local_; uint32_t long_local_; @@ -1203,6 +1310,7 @@ int32_t main(int32_t argc, char** argv) { uint32_t stmt_length = 8; uint32_t if_nest = 2; uint32_t loop_nest = 3; + uint32_t try_nest = 2; // Parse options. while (1) { @@ -1226,6 +1334,9 @@ int32_t main(int32_t argc, char** argv) { case 'n': loop_nest = strtoul(optarg, nullptr, 0); break; + case 't': + try_nest = strtoul(optarg, nullptr, 0); + break; case 'v': fprintf(stderr, "jfuzz version %s\n", VERSION); return 0; @@ -1234,7 +1345,7 @@ int32_t main(int32_t argc, char** argv) { fprintf(stderr, "usage: %s [-s seed] " "[-d expr-depth] [-l stmt-length] " - "[-i if-nest] [-n loop-nest] [-v] [-h]\n", + "[-i if-nest] [-n loop-nest] [-t try-nest] [-v] [-h]\n", argv[0]); return 1; } @@ -1244,7 +1355,7 @@ int32_t main(int32_t argc, char** argv) { srand(seed); // Generate fuzzed program. - JFuzz fuzz(stdout, seed, expr_depth, stmt_length, if_nest, loop_nest); + JFuzz fuzz(stdout, seed, expr_depth, stmt_length, if_nest, loop_nest, try_nest); fuzz.emitProgram(); return 0; } diff --git a/tools/jfuzz/run_dex_fuzz_test.py b/tools/jfuzz/run_dex_fuzz_test.py index ca0aec01ccfc914e069e617d625d2352015894d5..a25ef3fca552954d0f4fb6c8ff8dcbc2f1ecffc4 100755 --- a/tools/jfuzz/run_dex_fuzz_test.py +++ b/tools/jfuzz/run_dex_fuzz_test.py @@ -41,14 +41,15 @@ from common.common import RunCommand class DexFuzzTester(object): """Tester that feeds JFuzz programs into DexFuzz testing.""" - def __init__(self, num_tests, num_inputs, device, use_dx): + def __init__(self, num_tests, num_inputs, device, dexer, debug_info): """Constructor for the tester. Args: num_tests: int, number of tests to run num_inputs: int, number of JFuzz programs to generate device: string, target device serial number (or None) - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer + debug_info: boolean, if True include debugging info """ self._num_tests = num_tests self._num_inputs = num_inputs @@ -58,7 +59,8 @@ class DexFuzzTester(object): self._dexfuzz_dir = None self._inputs_dir = None self._dexfuzz_env = None - self._use_dx = use_dx + self._dexer = dexer + self._debug_info = debug_info def __enter__(self): """On entry, enters new temp directory after saving current directory. @@ -109,13 +111,15 @@ class DexFuzzTester(object): Raises: FatalError: error when compilation fails """ - if self._use_dx: - if RunCommand(['javac', 'Test.java'], + if self._dexer == 'dx' or self._dexer == 'd8': + dbg = '-g' if self._debug_info else '-g:none' + if RunCommand(['javac', dbg, 'Test.java'], out=None, err='jerr.txt', timeout=30) != RetCode.SUCCESS: print('Unexpected error while running javac') raise FatalError('Unexpected error while running javac') cfiles = glob('*.class') - if RunCommand(['dx', '--dex', '--output=classes.dex'] + cfiles, + dx = 'dx' if self._dexer == 'dx' else 'd8-compat-dx' + if RunCommand([dx, '--dex', '--output=classes.dex'] + cfiles, out=None, err='dxerr.txt', timeout=30) != RetCode.SUCCESS: print('Unexpected error while running dx') raise FatalError('Unexpected error while running dx') @@ -124,7 +128,8 @@ class DexFuzzTester(object): os.unlink(cfile) os.unlink('jerr.txt') os.unlink('dxerr.txt') - else: + + elif self._dexer == 'jack': jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.', 'Test.java'] if RunCommand(['jack'] + jack_args, out=None, err='jackerr.txt', timeout=30) != RetCode.SUCCESS: @@ -132,6 +137,8 @@ class DexFuzzTester(object): raise FatalError('Unexpected error while running Jack') # Cleanup on success (nothing to see). os.unlink('jackerr.txt') + else: + raise FatalError('Unknown dexer: ' + self._dexer) def GenerateJFuzzPrograms(self): """Generates JFuzz programs. @@ -175,16 +182,21 @@ class DexFuzzTester(object): def main(): # Handle arguments. parser = argparse.ArgumentParser() - parser.add_argument('--num_tests', default=1000, - type=int, help='number of tests to run') - parser.add_argument('--num_inputs', default=10, - type=int, help='number of JFuzz program to generate') - parser.add_argument('--use_dx', default=False, action='store_true', - help='use dx (rather than jack)') + parser.add_argument('--num_tests', default=1000, type=int, + help='number of tests to run (default: 1000)') + parser.add_argument('--num_inputs', default=10, type=int, + help='number of JFuzz program to generate (default: 10)') parser.add_argument('--device', help='target device serial number') + parser.add_argument('--dexer', default='dx', type=str, + help='defines dexer as dx, d8, or jack (default: dx)') + parser.add_argument('--debug_info', default=False, action='store_true', + help='include debugging info') args = parser.parse_args() # Run the DexFuzz tester. - with DexFuzzTester(args.num_tests, args.num_inputs, args.device, args.use_dx) as fuzzer: + with DexFuzzTester(args.num_tests, + args.num_inputs, + args.device, + args.dexer, args.debug_info) as fuzzer: fuzzer.Run() if __name__ == '__main__': diff --git a/tools/jfuzz/run_jfuzz_test.py b/tools/jfuzz/run_jfuzz_test.py index dac1c798177b429e840b6d97f52f0815003aaa3d..34180d993f46e3faa0b214a7ccd19f0edc9e119d 100755 --- a/tools/jfuzz/run_jfuzz_test.py +++ b/tools/jfuzz/run_jfuzz_test.py @@ -43,11 +43,12 @@ from common.common import DeviceTestEnv BISECTABLE_RET_CODES = (RetCode.SUCCESS, RetCode.ERROR, RetCode.TIMEOUT) -def GetExecutionModeRunner(use_dx, device, mode): +def GetExecutionModeRunner(dexer, debug_info, device, mode): """Returns a runner for the given execution mode. Args: - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer + debug_info: boolean, if True include debugging info device: string, target device serial number (or None) mode: string, execution mode Returns: @@ -56,15 +57,15 @@ def GetExecutionModeRunner(use_dx, device, mode): FatalError: error for unknown execution mode """ if mode == 'ri': - return TestRunnerRIOnHost() + return TestRunnerRIOnHost(debug_info) if mode == 'hint': - return TestRunnerArtIntOnHost(use_dx) + return TestRunnerArtIntOnHost(dexer, debug_info) if mode == 'hopt': - return TestRunnerArtOptOnHost(use_dx) + return TestRunnerArtOptOnHost(dexer, debug_info) if mode == 'tint': - return TestRunnerArtIntOnTarget(use_dx, device) + return TestRunnerArtIntOnTarget(dexer, debug_info, device) if mode == 'topt': - return TestRunnerArtOptOnTarget(use_dx, device) + return TestRunnerArtOptOnTarget(dexer, debug_info, device) raise FatalError('Unknown execution mode') @@ -117,33 +118,47 @@ class TestRunner(object): class TestRunnerWithHostCompilation(TestRunner): """Abstract test runner that supports compilation on host.""" - def __init__(self, use_dx): + def __init__(self, dexer, debug_info): """Constructor for the runner with host compilation. Args: - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer + debug_info: boolean, if True include debugging info """ + self._dexer = dexer + self._debug_info = debug_info self._jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.', 'Test.java'] - self._use_dx = use_dx def CompileOnHost(self): - if self._use_dx: - if RunCommand(['javac', 'Test.java'], + if self._dexer == 'dx' or self._dexer == 'd8': + dbg = '-g' if self._debug_info else '-g:none' + if RunCommand(['javac', dbg, 'Test.java'], out=None, err=None, timeout=30) == RetCode.SUCCESS: - retc = RunCommand(['dx', '--dex', '--output=classes.dex'] + glob('*.class'), + dx = 'dx' if self._dexer == 'dx' else 'd8-compat-dx' + retc = RunCommand([dx, '--dex', '--output=classes.dex'] + glob('*.class'), out=None, err='dxerr.txt', timeout=30) else: retc = RetCode.NOTCOMPILED - else: + elif self._dexer == 'jack': retc = RunCommand(['jack'] + self._jack_args, out=None, err='jackerr.txt', timeout=30) + else: + raise FatalError('Unknown dexer: ' + self._dexer) return retc class TestRunnerRIOnHost(TestRunner): """Concrete test runner of the reference implementation on host.""" + def __init__(self, debug_info): + """Constructor for the runner with host compilation. + + Args: + debug_info: boolean, if True include debugging info + """ + self._debug_info = debug_info + @property def description(self): return 'RI on host' @@ -153,7 +168,8 @@ class TestRunnerRIOnHost(TestRunner): return 'RI' def CompileAndRunTest(self): - if RunCommand(['javac', 'Test.java'], + dbg = '-g' if self._debug_info else '-g:none' + if RunCommand(['javac', dbg, 'Test.java'], out=None, err=None, timeout=30) == RetCode.SUCCESS: retc = RunCommand(['java', 'Test'], self.output_file, err=None) else: @@ -167,14 +183,15 @@ class TestRunnerRIOnHost(TestRunner): class TestRunnerArtOnHost(TestRunnerWithHostCompilation): """Abstract test runner of Art on host.""" - def __init__(self, use_dx, extra_args=None): + def __init__(self, dexer, debug_info, extra_args=None): """Constructor for the Art on host tester. Args: - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer + debug_info: boolean, if True include debugging info extra_args: list of strings, extra arguments for dalvikvm """ - super().__init__(use_dx) + super().__init__(dexer, debug_info) self._art_cmd = ['/bin/bash', 'art', '-cp', 'classes.dex'] if extra_args is not None: self._art_cmd += extra_args @@ -191,13 +208,14 @@ class TestRunnerArtOnHost(TestRunnerWithHostCompilation): class TestRunnerArtIntOnHost(TestRunnerArtOnHost): """Concrete test runner of interpreter mode Art on host.""" - def __init__(self, use_dx): + def __init__(self, dexer, debug_info): """Constructor for the Art on host tester (interpreter). Args: - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer + debug_info: boolean, if True include debugging info """ - super().__init__(use_dx, ['-Xint']) + super().__init__(dexer, debug_info, ['-Xint']) @property def description(self): @@ -214,13 +232,14 @@ class TestRunnerArtIntOnHost(TestRunnerArtOnHost): class TestRunnerArtOptOnHost(TestRunnerArtOnHost): """Concrete test runner of optimizing compiler mode Art on host.""" - def __init__(self, use_dx): + def __init__(self, dexer, debug_info): """Constructor for the Art on host tester (optimizing). Args: - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer + debug_info: boolean, if True include debugging info """ - super().__init__(use_dx, None) + super().__init__(dexer, debug_info, None) @property def description(self): @@ -239,15 +258,16 @@ class TestRunnerArtOptOnHost(TestRunnerArtOnHost): class TestRunnerArtOnTarget(TestRunnerWithHostCompilation): """Abstract test runner of Art on target.""" - def __init__(self, use_dx, device, extra_args=None): + def __init__(self, dexer, debug_info, device, extra_args=None): """Constructor for the Art on target tester. Args: - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer + debug_info: boolean, if True include debugging info device: string, target device serial number (or None) extra_args: list of strings, extra arguments for dalvikvm """ - super().__init__(use_dx) + super().__init__(dexer, debug_info) self._test_env = DeviceTestEnv('jfuzz_', specific_device=device) self._dalvik_cmd = ['dalvikvm'] if extra_args is not None: @@ -281,14 +301,15 @@ class TestRunnerArtOnTarget(TestRunnerWithHostCompilation): class TestRunnerArtIntOnTarget(TestRunnerArtOnTarget): """Concrete test runner of interpreter mode Art on target.""" - def __init__(self, use_dx, device): + def __init__(self, dexer, debug_info, device): """Constructor for the Art on target tester (interpreter). Args: - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer + debug_info: boolean, if True include debugging info device: string, target device serial number (or None) """ - super().__init__(use_dx, device, ['-Xint']) + super().__init__(dexer, debug_info, device, ['-Xint']) @property def description(self): @@ -305,14 +326,15 @@ class TestRunnerArtIntOnTarget(TestRunnerArtOnTarget): class TestRunnerArtOptOnTarget(TestRunnerArtOnTarget): """Concrete test runner of optimizing compiler mode Art on target.""" - def __init__(self, use_dx, device): + def __init__(self, dexer, debug_info, device): """Constructor for the Art on target tester (optimizing). Args: - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer + debug_info: boolean, if True include debugging info device: string, target device serial number (or None) """ - super().__init__(use_dx, device, None) + super().__init__(dexer, debug_info, device, None) @property def description(self): @@ -342,7 +364,7 @@ class JFuzzTester(object): """Tester that runs JFuzz many times and report divergences.""" def __init__(self, num_tests, device, mode1, mode2, jfuzz_args, - report_script, true_divergence_only, use_dx): + report_script, true_divergence_only, dexer, debug_info): """Constructor for the tester. Args: @@ -353,16 +375,18 @@ class JFuzzTester(object): jfuzz_args: list of strings, additional arguments for jfuzz report_script: string, path to script called for each divergence true_divergence_only: boolean, if True don't bisect timeout divergences - use_dx: boolean, if True use dx rather than jack + dexer: string, defines dexer + debug_info: boolean, if True include debugging info """ self._num_tests = num_tests self._device = device - self._runner1 = GetExecutionModeRunner(use_dx, device, mode1) - self._runner2 = GetExecutionModeRunner(use_dx, device, mode2) + self._runner1 = GetExecutionModeRunner(dexer, debug_info, device, mode1) + self._runner2 = GetExecutionModeRunner(dexer, debug_info, device, mode2) self._jfuzz_args = jfuzz_args self._report_script = report_script self._true_divergence_only = true_divergence_only - self._use_dx = use_dx + self._dexer = dexer + self._debug_info = debug_info self._save_dir = None self._results_dir = None self._jfuzz_dir = None @@ -405,7 +429,8 @@ class JFuzzTester(object): print('Directory :', self._results_dir) print('Exec-mode1:', self._runner1.description) print('Exec-mode2:', self._runner2.description) - print('Compiler :', 'dx' if self._use_dx else 'jack') + print('Dexer :', self._dexer) + print('Debug-info:', self._debug_info) print() self.ShowStats() for self._test in range(1, self._num_tests + 1): @@ -525,8 +550,8 @@ class JFuzzTester(object): for arg in jfuzz_cmd_str.strip().split(' -')][1:] wrapped_args = ['--jfuzz_arg={0}'.format(opt) for opt in jfuzz_args] repro_cmd_str = (os.path.basename(__file__) + - ' --num_tests=1 ' + - ('--use_dx ' if self._use_dx else '') + + ' --num_tests=1 --dexer=' + self._dexer + + (' --debug_info ' if self._debug_info else ' ') + ' '.join(wrapped_args)) comment = 'jfuzz {0}\nReproduce test:\n{1}\nReproduce divergence:\n{2}\n'.format( jfuzz_ver, jfuzz_cmd_str, repro_cmd_str) @@ -592,29 +617,36 @@ class JFuzzTester(object): def main(): # Handle arguments. parser = argparse.ArgumentParser() - parser.add_argument('--num_tests', default=10000, - type=int, help='number of tests to run') + parser.add_argument('--num_tests', default=10000, type=int, + help='number of tests to run') parser.add_argument('--device', help='target device serial number') parser.add_argument('--mode1', default='ri', help='execution mode 1 (default: ri)') parser.add_argument('--mode2', default='hopt', help='execution mode 2 (default: hopt)') - parser.add_argument('--report_script', help='script called for each' - ' divergence') + parser.add_argument('--report_script', + help='script called for each divergence') parser.add_argument('--jfuzz_arg', default=[], dest='jfuzz_args', - action='append', help='argument for jfuzz') + action='append', + help='argument for jfuzz') parser.add_argument('--true_divergence', default=False, action='store_true', - help='don\'t bisect timeout divergences') - parser.add_argument('--use_dx', default=False, action='store_true', - help='use dx (rather than jack)') + help='do not bisect timeout divergences') + parser.add_argument('--dexer', default='dx', type=str, + help='defines dexer as dx, d8, or jack (default: dx)') + parser.add_argument('--debug_info', default=False, action='store_true', + help='include debugging info') args = parser.parse_args() if args.mode1 == args.mode2: raise FatalError('Identical execution modes given') # Run the JFuzz tester. with JFuzzTester(args.num_tests, - args.device, args.mode1, args.mode2, - args.jfuzz_args, args.report_script, - args.true_divergence, args.use_dx) as fuzzer: + args.device, + args.mode1, args.mode2, + args.jfuzz_args, + args.report_script, + args.true_divergence, + args.dexer, + args.debug_info) as fuzzer: fuzzer.Run() if __name__ == '__main__': diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt index 23a70f7ae71b59e222cc3d8759af175d0da8f165..68aeedde8562c3c35c1d7bd439c22ae9384bd5b2 100644 --- a/tools/libcore_failures.txt +++ b/tools/libcore_failures.txt @@ -33,15 +33,15 @@ 777 all directories on path to socket (on device without su).", result: EXEC_FAILED, modes: [device], - names: ["libcore.io.OsTest#testUnixDomainSockets_in_file_system"] + names: ["libcore.libcore.io.OsTest#testUnixDomainSockets_in_file_system"] }, { description: "TCP_USER_TIMEOUT is not defined on host's tcp.h (glibc-2.15-4.8).", result: EXEC_FAILED, modes: [host], names: ["libcore.android.system.OsConstantsTest#testTcpUserTimeoutIsDefined", - "libcore.io.OsTest#test_socket_tcpUserTimeout_setAndGet", - "libcore.io.OsTest#test_socket_tcpUserTimeout_doesNotWorkOnDatagramSocket"], + "libcore.libcore.io.OsTest#test_socket_tcpUserTimeout_setAndGet", + "libcore.libcore.io.OsTest#test_socket_tcpUserTimeout_doesNotWorkOnDatagramSocket"], bug: 30402085 }, { @@ -113,7 +113,7 @@ { description: "Needs kernel updates on host/device", result: EXEC_FAILED, - names: ["libcore.io.OsTest#test_socketPing"] + names: ["libcore.libcore.io.OsTest#test_socketPing"] }, { description: "Linker issues in chrooted environment", @@ -131,7 +131,7 @@ description: "test_xattr fails on arm64 on the buildbots only: needs investigation", result: EXEC_FAILED, modes: [device], - names: ["libcore.io.OsTest#test_xattr"], + names: ["libcore.libcore.io.OsTest#test_xattr"], bug: 22258911 }, { @@ -143,9 +143,9 @@ { description: "Lack of IPv6 on some buildbot slaves", result: EXEC_FAILED, - names: ["libcore.io.OsTest#test_byteBufferPositions_sendto_recvfrom_af_inet6", - "libcore.io.OsTest#test_sendtoSocketAddress_af_inet6", - "libcore.io.OsTest#test_recvfrom_EmptyPacket"], + names: ["libcore.libcore.io.OsTest#test_byteBufferPositions_sendto_recvfrom_af_inet6", + "libcore.libcore.io.OsTest#test_sendtoSocketAddress_af_inet6", + "libcore.libcore.io.OsTest#test_recvfrom_EmptyPacket"], bug: 25178637 }, { @@ -220,6 +220,6 @@ result: EXEC_FAILED, modes: [device], bug: 69023954, - names: ["libcore.io.OsTest#test_setgroups"] + names: ["libcore.libcore.io.OsTest#test_setgroups"] } ] diff --git a/tools/libcore_gcstress_debug_failures.txt b/tools/libcore_gcstress_debug_failures.txt index d27b8fc06c5182298f61cecec7c1f07b20faf502..0029b0a01a03c49da1be1077e0d19d5626abb0df 100644 --- a/tools/libcore_gcstress_debug_failures.txt +++ b/tools/libcore_gcstress_debug_failures.txt @@ -9,9 +9,9 @@ result: EXEC_FAILED, modes: [device], names: ["jsr166.CompletableFutureTest#testCompleteOnTimeout_completed", - "libcore.icu.TransliteratorTest#testAll", - "libcore.icu.RelativeDateTimeFormatterTest#test_bug25821045", - "libcore.icu.RelativeDateTimeFormatterTest#test_bug25883157", + "libcore.libcore.icu.TransliteratorTest#testAll", + "libcore.libcore.icu.RelativeDateTimeFormatterTest#test_bug25821045", + "libcore.libcore.icu.RelativeDateTimeFormatterTest#test_bug25883157", "libcore.java.lang.ref.ReferenceQueueTest#testRemoveWithDelayedResultAndTimeout", "libcore.java.lang.ref.ReferenceQueueTest#testRemoveWithDelayedResultAndNoTimeout", "libcore.java.util.TimeZoneTest#testSetDefaultDeadlock", diff --git a/tools/libjdwp_art_failures.txt b/tools/prebuilt_libjdwp_art_failures.txt similarity index 84% rename from tools/libjdwp_art_failures.txt rename to tools/prebuilt_libjdwp_art_failures.txt index 646a96adbbbea26e90adeed73b40cd452ee6b3ad..7694a4c7e4505448e60a1f8308f6b354b0e3acdd 100644 --- a/tools/libjdwp_art_failures.txt +++ b/tools/prebuilt_libjdwp_art_failures.txt @@ -1,6 +1,9 @@ /* * This file contains expectations for ART's buildbot. The purpose of this file is * to temporarily list failing tests and not break the bots. + * + * This file contains the expectations for the 'prebuilt-libjdwp-aot' and + * 'prebuilt-libjdwp-jit' test groups on the chromium buildbot. */ [ { @@ -64,6 +67,12 @@ "org.apache.harmony.jpda.tests.jdwp.EventModifiers.InstanceOnlyModifierTest#testMethodExit", "org.apache.harmony.jpda.tests.jdwp.EventModifiers.InstanceOnlyModifierTest#testMethodExitWithReturnValue" ] }, +{ + description: "Tests for VMDebug functionality not implemented in the upstream libjdwp", + result: EXEC_FAILED, + names: [ "org.apache.harmony.jpda.tests.jdwp.VMDebug.VMDebugTest#testVMDebug", + "org.apache.harmony.jpda.tests.jdwp.VMDebug.VMDebugTest002#testVMDebug" ] +}, /* TODO Categorize these failures more. */ { description: "Tests that fail on both ART and RI. These tests are likely incorrect", @@ -91,5 +100,23 @@ "org.apache.harmony.jpda.tests.jdwp.ThreadGroupReference.NameTest#testName001_NullObject", "org.apache.harmony.jpda.tests.jdwp.ThreadGroupReference.ParentTest#testParent_NullObject", "org.apache.harmony.jpda.tests.jdwp.VirtualMachine.CapabilitiesNewTest#testCapabilitiesNew001" ] +}, +{ + description: "Test is flaky", + result: EXEC_FAILED, + bug: 69121056, + name: "org.apache.harmony.jpda.tests.jdwp.ObjectReference.IsCollectedTest#testIsCollected001" +}, +{ + description: "Test for ddms extensions that are not implemented for prebuilt-libjdwp", + result: EXEC_FAILED, + bug: 69169846, + name: "org.apache.harmony.jpda.tests.jdwp.DDM.DDMTest#testChunk001" +}, +{ + description: "Test crashes", + result: EXEC_FAILED, + bug: 69591477, + name: "org.apache.harmony.jpda.tests.jdwp.VirtualMachine.ExitTest#testExit001" } ] diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh index db8c54056dbd0548f38fbc3b596352e6d22b6d66..6a846aee173e81854b3aee94d837ace5b2e65e35 100755 --- a/tools/run-jdwp-tests.sh +++ b/tools/run-jdwp-tests.sh @@ -96,6 +96,14 @@ while true; do # We don't care about jit with the RI use_jit=false shift + elif [[ $1 == --test-timeout-ms ]]; then + # Remove the --test-timeout-ms from the arguments. + args=${args/$1} + shift + jdwp_test_timeout=$1 + # Remove the argument + args=${args/$1} + shift elif [[ $1 == --agent-wrapper ]]; then # Remove the --agent-wrapper from the arguments. args=${args/$1} @@ -284,8 +292,10 @@ fi if [[ $using_jack == "true" ]]; then toolchain_args="--toolchain jack --language JN --jack-arg -g" -else +elif [[ $mode != "ri" ]]; then toolchain_args="--toolchain dx --language CUR" +else + toolchain_args="--toolchain javac --language CUR" fi # Run the tests using vogar. diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh index 51c7348c97f081fb1f010a2fbe7fcf7f66faec72..673eea8cd9f5ddc918b3ebea153511deeb94757a 100755 --- a/tools/run-libcore-tests.sh +++ b/tools/run-libcore-tests.sh @@ -169,4 +169,4 @@ fi # Run the tests using vogar. echo "Running tests for the following test packages:" echo ${working_packages[@]} | tr " " "\n" -vogar --no-stream $vogar_args $expectations $(cparg $DEPS) ${working_packages[@]} +vogar $vogar_args $expectations $(cparg $DEPS) ${working_packages[@]} diff --git a/tools/run-libjdwp-tests.sh b/tools/run-libjdwp-tests.sh index 964bb386efe420fc99f9c4f9df76242dbef1b43c..e116facd98b31c37bb5666fad02d5d822a6234c8 100755 --- a/tools/run-libjdwp-tests.sh +++ b/tools/run-libjdwp-tests.sh @@ -29,11 +29,16 @@ debug="no" has_variant="no" has_mode="no" mode="target" +has_timeout="no" while true; do if [[ $1 == "--debug" ]]; then debug="yes" shift + elif [[ $1 == --test-timeout-ms ]]; then + has_timeout="yes" + shift + shift elif [[ "$1" == "--mode=jvm" ]]; then has_mode="yes" mode="ri" @@ -60,6 +65,12 @@ if [[ "$has_variant" = "no" ]]; then args+=(--variant=X32) fi +if [[ "$has_timeout" = "no" ]]; then + # Double the timeout to 20 seconds + args+=(--test-timeout-ms) + args+=(20000) +fi + # We don't use full paths since it is difficult to determine them for device # tests and not needed due to resolution rules of dlopen. if [[ "$debug" = "yes" ]]; then @@ -68,7 +79,7 @@ else args+=(-Xplugin:libopenjdkjvmti.so) fi -expect_path=$PWD/art/tools/libjdwp_oj_art_failures.txt +expect_path=$PWD/art/tools/external_oj_libjdwp_art_failures.txt function verbose_run() { echo "$@" env "$@" diff --git a/tools/run-prebuilt-libjdwp-tests.sh b/tools/run-prebuilt-libjdwp-tests.sh index 46c2a153a766b2ded73e255b1a06367c80da1578..e7f028ae638ac2720fe4a868e0c9547603533806 100755 --- a/tools/run-prebuilt-libjdwp-tests.sh +++ b/tools/run-prebuilt-libjdwp-tests.sh @@ -96,7 +96,7 @@ if [[ ! -f $plugin ]]; then fi props_path=$PWD/art/tools/libjdwp-compat.props -expect_path=$PWD/art/tools/libjdwp_art_failures.txt +expect_path=$PWD/art/tools/prebuilt_libjdwp_art_failures.txt function verbose_run() { echo "$@" diff --git a/tools/titrace/titrace.cc b/tools/titrace/titrace.cc index e8122807c108b0e5342dfdc6613a720ef97fbd95..981ad56e86f781b273a82275ea51cfec6c3b857b 100644 --- a/tools/titrace/titrace.cc +++ b/tools/titrace/titrace.cc @@ -21,7 +21,7 @@ #include #include #include -#include // NOLINT [build/c++11] [5] +#include // We could probably return a JNI_ERR here but lets crash instead if something fails. #define CHECK_JVMTI_ERROR(jvmti, errnum) \ @@ -195,8 +195,8 @@ struct TraceStatistics { std::unique_ptr instruction_decoder_; - std::atomic single_step_counter_{0u}; // NOLINT [readability/braces] [4] [whitespace/braces] [5] - std::atomic instruction_counter_[256]{}; // NOLINT [whitespace/braces] [5] + std::atomic single_step_counter_{0u}; + std::atomic instruction_counter_[256]{}; // Cache the bytecode to avoid calling into JVMTI repeatedly. // TODO: invalidate if the bytecode was updated? @@ -256,7 +256,7 @@ JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* jvm, TraceStatistics::Initialize(jvmti); } - jvmtiError error{}; // NOLINT [readability/braces] [4] [whitespace/braces] [5] + jvmtiError error{}; // Set capabilities. { diff --git a/tools/wrapagentproperties/wrapagentproperties.cc b/tools/wrapagentproperties/wrapagentproperties.cc index 9eaffbb240685ca08223db155edc9471192fe86c..8b4b062cf5a9d012a0d763659cac6a97e438187a 100644 --- a/tools/wrapagentproperties/wrapagentproperties.cc +++ b/tools/wrapagentproperties/wrapagentproperties.cc @@ -24,7 +24,7 @@ #include #include #include -#include // NOLINT [build/c++11] [5] +#include #include #include #include